From: Josef 'Jeff' Sipek Date: Fri, 29 Jun 2007 06:54:49 +0000 (-0400) Subject: Imported regression suite from CVS X-Git-Url: https://git.fsl.cs.stonybrook.edu/?a=commitdiff_plain;h=26d02773b24b4e17582d0d74a7b1c4a420f02274;p=unionfs-regression.git Imported regression suite from CVS Signed-off-by: Josef 'Jeff' Sipek --- 26d02773b24b4e17582d0d74a7b1c4a420f02274 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fe04362 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +complete.sh +start.sh diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..37faf46 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,435 @@ +2007-06-27 Erez Zadok + + * run-tests: support ext4 (needs an ext3 mkfs and currently the f/s + is called "extdev"). + Minor typo fix. + +2007-06-17 Erez Zadok + + * t-fsync.sh: fix comment at top of test script. + +2007-06-10 Erez Zadok + + * run-all-tests: export MYFS=jffs2 when running jffs2 tests. + +2007-06-05 Erez Zadok + + * t-mmap.sh: skip mmap read/write test for jffs2 (doesn't support + writable mappings). + + * run-tests: export name of leftmost file system so some tests can + be run conditionally. + + * run-tests: show lower file system being used. + + * run-all-tests: simple wrapper script to run all regression tests + on all known working lower file systems. + + * default.conf (MYFS): allow overriding the default ext2 lower + file system to something else. + + * README: document how to override default lower file system (env + var $MYFS) + +2007-05-30 Erez Zadok + + * *.conf (ALL_TESTS): support new ioctl test. + + * progs/Makefile (BINS): build new ioctl test. + + * t-ioctl.sh: test script for queryfile ioctl. + + * progs/queryfile.c: utility program to invoke the QUERYFILE + unionfs ioctl (borrowed from unionfs-utils). + + * progs/*.c: copyright updates. + +2007-05-28 Erez Zadok + + * run-tests: reiserfs v3 mkfs command does not honor the -q flag + completely. It prints out copyright and credits text, some on + stdout and some on stderr. So ignore all stderr/stdout output from + mkfs.reiserfs. + + * run-tests: When using loop devices and nfsv3, sometimes some of + the lower branches cannot be unmounted with an EBUSY. But if you + wait a little longer for pdflush to run, or run /bin/sync + manually, then you can mount those lower branches. This is + probably some sort of a race between the loop device driver and + NFSv3. We can work around it if we mount all nfs partitions with + "-o sync", or run sync by hand here. + + * t-truncate-all.sh: bugfix, discovered when trying NFS for the + lower branches. Check if branch 2 supports chatter, not top-level + directory. + + * run-tests: don't print annoying delay messages more than once. + +2007-05-25 Erez Zadok + + * README: documentation updates. + + * run-tests: support NFS v2, v3, and v4 mounts. + +2007-05-23 Erez Zadok + + * *.conf: spell check. + +2007-05-23 Josef "Jeff" Sipek + + * progs/mapper.c: Use %p to print pointers, not %x (%p is _always_ + the right size - 32 or 64 bits; %x expects an int, and not all + architectures have sizeof(int)==sizeof(void*)) + +2007-05-23 Erez Zadok + + * run-tests: delete temp files created in /tmp once done. + +2007-05-23 Josef "Jeff" Sipek + + * run-tests: Use /dev/.static/dev/loop* as loop devices + + * thor.conf: Disable console echo & update device file names + +2007-05-23 Erez Zadok + + * run-tests: allow tests to be skipped if their name contains the + string "OFF". Makes it easier to skip tests that succeeded or + failed. + + * jffs2.conf: sample jffs2 test configuration. + + * jffs2-empty.img: dummy canned jffs2 image, made with mkfs.jffs2. + + * run-tests (setup_lower, do_unmount): support jffs2. + + * run-tests (wait4): support fractional delay time, including + sub-second (e.g., "sleep 0.5"). Also delay during setup time too + (needed to work around a race b/t the devfs and mtdblock drivers). + +2007-05-22 Erez Zadok + + * nfs.conf: an all-NFS sample configuration. + + * run-tests: minor cleanup. + Don't need to see stderr from 'dd'. + Unmount the /n/lower/bN mount point not the device itself. + Colorize the name of the TEST before it runs. + Support NFS branches. + +2007-05-19 Erez Zadok + + * {default,thor}.conf (ALL_TESTS): include mmap test. + + * progs/Makefile: invoke mmap test. + + * t-mmap.sh: new script to execute mmap test. + + * progs/mapper.c: simple new program to test mmap reading and + writing with copyup. + +2007-05-17 Erez Zadok + + * t-open-unlink.sh, t-creat-open.sh, t-branchman.sh: minor + spelling fixes in comments. + +2007-05-05 Erez Zadok + + * default.conf: default tests should use the safer /dev/loopN + (avoid potential trashing of actual file system devices). + +2007-03-17 Josef "Jeff" Sipek + + * thor.conf: Disable incgen test + +2007-03-15 Erez Zadok + + * brm.conf: a configuration for the branch-management branch. + Turns off incgen because it doesn't make sense here, and the + branchman test because branch-management tests need to be + rethought and tested concurrently (and these two test break the + suite anyway.) + +2007-03-08 Josef "Jeff" Sipek + + * run-tests, default.conf, thor.conf: Allow for highlighting of + command output via ANSI color escape codes + +2007-03-08 Josef "Jeff" Sipek + + * thor.conf: re-enable chmod.sh + +2007-03-04 Josef "Jeff" Sipek + + * run-tests: Accept /dev/loop* as a device name, and create a 100MB + sparse file for each loop device + +2007-03-04 Erez Zadok + + * run-tests (CONF): accept configuration file in short form "foo" + or long form "foo.conf". + + * t-chmod.sh: fixes and cleanups. + +2007-03-03 Erez Zadok + + * Makefile: pass $MFLAGS. Separate rule to build test progs + vs. running tests (must run "make check" to invoke tests now). + + * README, progs/*: cleanup, copyrights, and URLs. + + * t-unlink-whiteout.sh: wrap each test in a function so they can + be run individually with a simple script change. + + * scaffold: delete also all hidden files (esp. whiteouts). + + * README: update README. + + * default.conf: default configuration file for run-tests script. + Defines devices to format, file system types, list of tests to run + (overridden by $MYTESTS from environment), and two additional + optional features: (1) sleep delay before critical actions which + may oops; and (2) echo command to /dev/console or desired device, + which helps to correlate the suite's actions with many debugging + printk's. + + * run-tests: new driver script to run tests while re-formatting + partitions before each test, unmounting the after each test, and + unloading unionfs module. + + * scaffold, t-*.sh: major overhaul to support a separate partition + and file system per branch. + + * *.sh: rename all test scripts as t-.sh. + +2006-11-04 Josef "Jeff" Sipek + + * rename-501.sh: It just fails all the time...remove it for the time + being + +2006-05-29 Josef "Jeff" Sipek + + * rename-501.sh: Updates by Junjiro to test additional cases + +2006-05-29 Josef "Jeff" Sipek + + * rename-matrix.sh: forgot to remove a debug statement + +2006-05-29 Josef "Jeff" Sipek + + * rename-matrix.sh: added checks for the rename matrix (see + docs/rename.sh) + +2006-03-03 David P. Quigley + + * unlink-all.sh, rename-all.sh, rmdir-all.sh: added check for + DELETE_ALL env variable being set to perform checks. + +2006-02-20 David P. Quigley + + * unlink-all.sh: added delete=all as the first param of the + mount_union function call to account for it no longer being the + default behavior. + * rename-all.sh: added delete=all as the first param of the + mount_union function call to account for it no longer being the + default behavior. + +2006-01-20 Josef "Jeff" Sipek + + * branchman.sh: Updated regression test to work with the new nfsro + flag + +2005-12-29 David P. Quigley + + * lookup-opaque.sh: Changed script to use -d instead of --home-dir + +2005-12-28 Josef "Jeff" Sipek + + * lookup-opaque.sh: New test for situations where a dir is set as + unreadable, ie. the mode is 0700 or something, unionfs cannot detect + it is opaque or not + +2005-12-27 Josef "Jeff" Sipek + + * creat-open.sh, progs/creat-open.c: New test for situations + where you try to creat(2) an executing file + +2005-09-27 Charles P. Wright + + * unlink-all.sh: New test for situations where you have opaque + whiteouts to the right of a file. + + * rmdir-all.sh: Test for BUG420, but it doesn't fail as + described in the email. + +2005-09-14 David P. Quigley + + * scaffold: touch will return 0 on an immutable file so the + test in havechattr will be done with echo now + +2005-09-01 Charles P. Wright + + * branchman.sh: Functionize, and add a query check. + +2005-08-29 Charles P. Wright + + * Detect working chattr so we can run tests on reiser. + + * fsync.sh: Run fsync on a file. + +2005-08-24 Charles P. Wright + + * scaffold (checkperms): Permissions checking function. + * create.sh (BUG383): Test for BUG383. + +2005-08-23 Charles P. Wright + + * rename-all.sh: Test for BUG388. + +2005-08-18 Charles P. Wright + + * unlink-all.sh: Add BUG 319 regression test, because that fix + introduced a reference count leak elsewhere. This should make sure + the subsequent fix doesn't unfix 319. + +2005-08-17 Charles P. Wright + + * link.sh, unlink-all.sh: Make it easier to run a subset of tests. + * flock.sh: Use complete_test. + +2005-08-16 Charles P. Wright + + * scaffold: Make sure unmount is executed for each mount. + +2005-08-15 Charles P. Wright + + * flock.sh: Test for bug 360. + +2005-08-12 Arun M. Krishnakumar + + * all files : now the files can be used for testing + unionfs over NFS as well + +2005-08-11 Charles P. Wright + + * branchman.sh: Test to trigger BUG 370. + +2005-08-10 Arun M. Krishnakumar + + * all files : introduced a variable to set the lower-level + directory. introduced a variable that will later help + test NFS in future (this does not test it completely now). + +2005-08-10 Charles P. Wright + + * branchman.sh: Branch removal using new unionctl remount. + +2005-08-04 Charles P. Wright + + * branchman.sh: Test branch removal. + + * truncate-all.sh: Update mount options. + +2005-08-03 Charles P. Wright + + * branchman.sh: Test of adding a branch. + +2005-08-01 Arun M. Krishnakumar + + * link-rename.sh: Added a test case to check bugs #345 + and #351. This checks for copyups across ro branches. + +2005-08-01 Charles P. Wright + + * open.sh: Test directory-none-directory open. + + * incgen.sh: Regression test for generation number increment. + +2005-07-26 Charles P. Wright + + * unlink-first.sh: No longer needed. + +2005-07-22 Charles P. Wright + + * scaffold: Behave slightly better when chattr -i'ing the tree. + * Makefile: Die on a failed test. + * progs/open-unlink.c: Fix a comment typo. + +2005-07-22 Arun M. Krishnakumar + + * open-unlink.sh : fixed some small issues. this must + completely knockout bug #296 + +2005-07-20 Arun M. Krishnakumar + + * rmdir-all.sh : updated the test cases to use dir_opaque + +2005-07-20 Charles P. Wright + + * open-unlink.sh: A shell script that calls a program that does nasty + a combination of open, unlink, and copyup after unlink. (Works on Ext2, + but currently provokes bad Unionfs behavior.) + + * scaffold: Remove checkfile_after_remove and checkfile_after_create + as checktype does the same thing. + * unlink-all.sh: Check type of files after removal. + * unlink-whiteout.sh: Update results of tests and test rw symlink. + +2005-07-19 Arun M. Krishnakumar + + * mkdir.sh : updated the mkdir to use the dir_opaque + +2005-07-19 Charles P. Wright + + * scaffold: Add symlink support. + * unlink-whiteout.sh: Add symlink tests (currently broken). + +2005-03-02 David P. Quigley + + * link.sh: Fixed more cases that were not correct due to new unionfs_link + code + +2005-02-18 David P. Quigley + + * link.sh: Some cases were no longer valid due to the changes to unionfs_link. + The test cases have been fixed to reflect this and new ones have been added. + +2005-02-08 Charles P. Wright + + * link.sh: Test two directories on the same ro branch. + +2004-12-27 Charles P. Wright + + * chmod.sh: Reproduce bug 142. + +2004-08-25 Mohammad Nayyer Zubair + + * create.sh: tested unionfs_create() + +2004-08-24 Charles P. Wright + + * symlink.sh: readlink to make sure that it worked, and check the hierarchy + + * *.sh: Use set -e, so we don't need to do || exit $? + * scaffold: Support checking devices. + * mknod.sh: Make sure the devices are created and check the results! + + * Clean up the output for some of the scripts. + * truncate-all.sh: Embed truncate.c into shell script and compile it. + +2004-08-19 Charles P. Wright + + * rename-whiteout.sh: Test rename-whiteout. + +2004-08-18 Mohammad Nayyer Zubair + + * link.sh: script to test all cases of unionfs_link() + +2004-08-17 Charles P. Wright + + * testplan: symlink, link, truncate, times, and mknod were left out + A detailed mknod plan example is included + * unlink.sh: Test recursive whiteout creation and an error in the + middle + * scaffold: Create immutable files with 'i' instead of 'f' + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8883e6b --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ +# +# Copyright (c) 2003-2007 Erez Zadok +# Copyright (c) 2003-2006 Charles P. Wright +# Copyright (c) 2005-2007 Josef 'Jeff' Sipek +# Copyright (c) 2005-2006 Junjiro Okajima +# Copyright (c) 2005 Arun M. Krishnakumar +# Copyright (c) 2004-2006 David P. Quigley +# Copyright (c) 2003-2004 Mohammad Nayyer Zubair +# Copyright (c) 2003 Puja Gupta +# Copyright (c) 2003 Harikesavan Krishnan +# Copyright (c) 2003-2007 Stony Brook University +# Copyright (c) 2003-2007 The Research Foundation of State University of New York* +# +# For specific licensing information, see the COPYING file distributed with +# this package. +# +# This Copyright notice must be kept intact and distributed with all sources. + +progs: FRC + ${MAKE} ${MFLAGS} -C progs all + +check: FRC + ./run-tests default + +FRC: diff --git a/README b/README new file mode 100644 index 0000000..c32edc3 --- /dev/null +++ b/README @@ -0,0 +1,85 @@ + Unionfs 2.0 Regression Test Suite README + **************************************** + +Local File Systems Tests (tested with ext2, ext3, ext4, xfs, and reiserfs) +-------------------------------------------------------------------- + +The current regression suite requires that you define four separate +local-disk devices, which WILL BE FORMATTED(!) by the regression suite +driver script. The four partitions will be formatted and mounted under +/n/lower/b0, /n/lower/b1, /n/lower/b2, and /n/lower/b3. You can define the +partition's names and desired file system types to format as them with, in a +special configuration file. + +You can also specify the device pathname as /dev/loop0, /dev/loop1, etc. In +that case, the "run-tests" script will automatically create a small file +system and format it as desired. We recommend you use loop devices, as it's +much easier to create and destroy file systems using loop devices. + +To run all the tests, execute + +# ./run-tests default + +which will read the configuration file default.conf. This configuration +file defines the tests to run, devices to format and their respective file +systems, and three optional features: + +1. A delay to sleep before each critical action which may reveal a bug. + +2. A logfile or console device name to append the name of the command to be + executed to. This is useful of you have a lot of debugging printk's from + inside the kernel, so you can tell which printk's below to which action. + +3. you can change the default file system used as the lower file system from + ext2 to anything else, for example as follows: + +# MYFS=nfs3 ./run-tests default + +It's recommend that you don't change default.conf. Instead, copy it to +something like "mytests.conf" and then run + +# ./run-tests mytests + +While running the default set of tests, you can override the set of tests to +run, and execute just one or two as follows: + +# MYTESTS="lookup" ./run-tests mytests + +or + +# MYTESTS="mkdir mknod symlink" ./run-tests mytests + +The tests normally produce no output except "OK", "[rw]", and "[ro]" or +other status within square brackets. If you see any other output, then +there are problems. The two most common type of outputs are error messages +from commands, or output from diff, which indicates that Unionfs did some +other lower-level operation than was expected. The diff lines will contain +a file type (e.g., "f" for files or "d" for directories), and then a +lower-level name that shouldn't appear, or does appear. + + +############################################################################## +NFS Tests +--------- +The procedure is similar to the above, except that the you can specify +the file system name as follows + +FS0=nfs # defaults to NFSv3 +FS1=nfs2 # NFSv2 +FS2=nfs3 # NFSv3 +FS3=nfs4 # NFSv4 + +For NFS tests, run-tests will format a loop device based file system with +ext2, and export it to localhost. + +############################################################################## +JFFS2 Tests +----------- + +If you specify the file system type as "jffs2", then "run-tests" will use a +pre-built small jffs2 blank image, and mount it over the loop device in +question. To get jffs2 to work, your kernel needs not just the JFFS2 file +system, but also MTD device support. + +############################################################################## +For more information, see diff --git a/brm.conf b/brm.conf new file mode 100644 index 0000000..1b1d2bf --- /dev/null +++ b/brm.conf @@ -0,0 +1,93 @@ +# default Unionfs 2.0 regression configuration file + +# names of all possible tests +# Note: you can give full name of test (t-chmod.sh) or short (chmod) +ALL_TESTS=" + t-chmod.sh + t-creat-open.sh + t-create.sh + t-flock.sh + t-fsync.sh + t-ioctl.sh + t-link-rename.sh + t-link.sh + t-lookup-opaque.sh + t-lookup.sh + t-mkdir.sh + t-mknod.sh + t-open-unlink.sh + t-open.sh + t-readdir.sh + t-rename-matrix.sh + t-rename-whiteout.sh + t-symlink.sh + t-truncate-all.sh + t-unlink-whiteout.sh +" + +# The branchman and incgen tests are "broken" and need to be rewritten to +# support the new remount-style -ezk. +BROKEN_TESTS=" + t-branchman.sh + t-incgen.sh +" +# names of tests to run (change as you like) +# Will take $MYTESTS list of tests from the environment +TESTS2RUN=${MYTESTS:-$ALL_TESTS} + +# name of four devices to use +DEV0=/dev/loop1 +DEV1=/dev/loop2 +DEV2=/dev/loop3 +DEV3=/dev/loop4 +# Name of file systems to format your device. Supported file systems +# include: ext2, ext3, xfs, reiserfs, nfs, nfs2, nfs3, nfs4, and jffs2. +FS0=ext2 +FS1=ext2 +FS2=ext2 +FS3=ext2 + +# delay between each test (in seconds, optional) +DELAY=1 + +# Echo the command being executed to a file/device (optional) This is useful +# when unionfs printk's some debugging output which may go to a log file, +# console, or syslog. With this you can show command in your logs before it +# runs. +#ECHODEV=/var/log/all +ECHODEV=/dev/console + +# ANSI color codes, concatenated by ';' +# +# 00 for normal display (or just 0) +# 01 for bold on (or just 1) +# 02 faint (or just 2) +# 03 standout (or just 3) +# 04 underline (or just 4) +# 05 blink on (or just 5) +# 07 reverse video on (or just 7) +# 08 nondisplayed (invisible) (or just 8) +# 22 normal +# 23 no-standout +# 24 no-underline +# 25 no-blink +# 27 no-reverse +# 30 black foreground +# 31 red foreground +# 32 green foreground +# 33 yellow foreground +# 34 blue foreground +# 35 magenta foreground +# 36 cyan foreground +# 37 white foreground +# 39 default foreground +# 40 black background +# 41 red background +# 42 green background +# 43 yellow background +# 44 blue background +# 45 magenta background +# 46 cyan background +# 47 white background +# 49 default background +OUTPUT_COLOR="1;32" diff --git a/cleanup b/cleanup new file mode 100755 index 0000000..5716ace --- /dev/null +++ b/cleanup @@ -0,0 +1,15 @@ +#!/bin/bash + +set -x + +umount /mnt/unionfs + +umount /n/lower/b0 +umount /n/lower/b1 +umount /n/lower/b2 +umount /n/lower/b3 + +losetup -d /dev/.static/dev/loop0 +losetup -d /dev/.static/dev/loop1 +losetup -d /dev/.static/dev/loop2 +losetup -d /dev/.static/dev/loop3 diff --git a/default.conf b/default.conf new file mode 100644 index 0000000..0863ebd --- /dev/null +++ b/default.conf @@ -0,0 +1,96 @@ +# default Unionfs 2.0 regression configuration file +# uses ext2 by default, which you can override using a $MYFS env var + +# names of all possible tests +# Note: you can give full name of test (t-chmod.sh) or short (chmod) +ALL_TESTS=" + t-chmod.sh + t-creat-open.sh + t-create.sh + t-flock.sh + t-fsync.sh + t-ioctl.sh + t-link-rename.sh + t-link.sh + t-lookup-opaque.sh + t-lookup.sh + t-mkdir.sh + t-mknod.sh + t-mmap.sh + t-open-unlink.sh + t-open.sh + t-readdir.sh + t-rename-matrix.sh + t-rename-whiteout.sh + t-symlink.sh + t-truncate-all.sh + t-unlink-whiteout.sh +" + +# The branch-management test is "broken" and needs to be rewritten to +# support the new remount-style -ezk. +BROKEN_TESTS=" + t-branchman.sh + t-incgen.sh +" +# names of tests to run (change as you like) +# Will take $MYTESTS list of tests from the environment +TESTS2RUN=${MYTESTS:-$ALL_TESTS} + +# name of four devices to use +DEV0=/dev/loop0 +DEV1=/dev/loop1 +DEV2=/dev/loop2 +DEV3=/dev/loop3 +# Name of file systems to format your device. Supported file systems +# include: ext2, ext3, xfs, reiserfs, nfs, nfs2, nfs3, nfs4, and jffs2. +myfs=${MYFS:-ext2} +FS0=$myfs +FS1=$myfs +FS2=$myfs +FS3=$myfs + +# delay between each test (in seconds or fractions thereof, optional) +DELAY=0.5 + +# Echo the command being executed to a file/device (optional) This is useful +# when unionfs printk's some debugging output which may go to a log file, +# console, or syslog. With this you can show command in your logs before it +# runs. +#ECHODEV=/var/log/all +ECHODEV=/dev/console + +# ANSI color codes, concatenated by ';' +# +# 00 for normal display (or just 0) +# 01 for bold on (or just 1) +# 02 faint (or just 2) +# 03 standout (or just 3) +# 04 underline (or just 4) +# 05 blink on (or just 5) +# 07 reverse video on (or just 7) +# 08 nondisplayed (invisible) (or just 8) +# 22 normal +# 23 no-standout +# 24 no-underline +# 25 no-blink +# 27 no-reverse +# 30 black foreground +# 31 red foreground +# 32 green foreground +# 33 yellow foreground +# 34 blue foreground +# 35 magenta foreground +# 36 cyan foreground +# 37 white foreground +# 39 default foreground +# 40 black background +# 41 red background +# 42 green background +# 43 yellow background +# 44 blue background +# 45 magenta background +# 46 cyan background +# 47 white background +# 49 default background +OUTPUT_COLOR="1;32" diff --git a/jffs2-empty.img b/jffs2-empty.img new file mode 100644 index 0000000..b4b8856 Binary files /dev/null and b/jffs2-empty.img differ diff --git a/jffs2.conf b/jffs2.conf new file mode 100644 index 0000000..03d4c4f --- /dev/null +++ b/jffs2.conf @@ -0,0 +1,96 @@ +# default Unionfs 2.0 regression configuration file +# uses ext2 by default, which you can override using a $MYFS env var + +# names of all possible tests +# Note: you can give full name of test (t-chmod.sh) or short (chmod) +ALL_TESTS=" + t-chmod.sh + t-creat-open.sh + t-create.sh + t-flock.sh + t-fsync.sh + t-ioctl.sh + t-link-rename.sh + t-link.sh + t-lookup-opaque.sh + t-lookup.sh + t-mkdir.sh + t-mknod.sh + t-mmap.sh + t-open-unlink.sh + t-open.sh + t-readdir.sh + t-rename-matrix.sh + t-rename-whiteout.sh + t-symlink.sh + t-truncate-all.sh + t-unlink-whiteout.sh +" + +# The branch-management test is "broken" and needs to be rewritten to +# support the new remount-style -ezk. +BROKEN_TESTS=" + t-branchman.sh + t-incgen.sh +" +# names of tests to run (change as you like) +# Will take $MYTESTS list of tests from the environment +TESTS2RUN=${MYTESTS:-$ALL_TESTS} + +# name of four devices to use +DEV0=/dev/loop0 +DEV1=/dev/loop1 +DEV2=/dev/loop2 +DEV3=/dev/loop3 +# Name of file systems to format your device. Supported file systems +# include: ext2, ext3, xfs, reiserfs, nfs, nfs2, nfs3, nfs4, and jffs2. +myfs=${MYFS:-ext2} +FS0=jffs2 +FS1=$myfs +FS2=$myfs +FS3=$myfs + +# delay between each test (in seconds or fractions thereof, optional) +DELAY=0.5 + +# Echo the command being executed to a file/device (optional) This is useful +# when unionfs printk's some debugging output which may go to a log file, +# console, or syslog. With this you can show command in your logs before it +# runs. +#ECHODEV=/var/log/all +ECHODEV=/dev/console + +# ANSI color codes, concatenated by ';' +# +# 00 for normal display (or just 0) +# 01 for bold on (or just 1) +# 02 faint (or just 2) +# 03 standout (or just 3) +# 04 underline (or just 4) +# 05 blink on (or just 5) +# 07 reverse video on (or just 7) +# 08 nondisplayed (invisible) (or just 8) +# 22 normal +# 23 no-standout +# 24 no-underline +# 25 no-blink +# 27 no-reverse +# 30 black foreground +# 31 red foreground +# 32 green foreground +# 33 yellow foreground +# 34 blue foreground +# 35 magenta foreground +# 36 cyan foreground +# 37 white foreground +# 39 default foreground +# 40 black background +# 41 red background +# 42 green background +# 43 yellow background +# 44 blue background +# 45 magenta background +# 46 cyan background +# 47 white background +# 49 default background +OUTPUT_COLOR="1;32" diff --git a/progs/.cvsignore b/progs/.cvsignore new file mode 100644 index 0000000..40b68a0 --- /dev/null +++ b/progs/.cvsignore @@ -0,0 +1,10 @@ +creat-open +open-unlink +flock-copyup +fsync +truncate +bug418 +rmdircheckinode +rename +mapper +queryfile diff --git a/progs/Makefile b/progs/Makefile new file mode 100644 index 0000000..298c005 --- /dev/null +++ b/progs/Makefile @@ -0,0 +1,31 @@ +# +# Copyright (c) 2003-2007 Erez Zadok +# Copyright (c) 2003-2006 Charles P. Wright +# Copyright (c) 2005-2007 Josef 'Jeff' Sipek +# Copyright (c) 2005-2006 Junjiro Okajima +# Copyright (c) 2005 Arun M. Krishnakumar +# Copyright (c) 2004-2006 David P. Quigley +# Copyright (c) 2003-2004 Mohammad Nayyer Zubair +# Copyright (c) 2003 Puja Gupta +# Copyright (c) 2003 Harikesavan Krishnan +# Copyright (c) 2003-2007 Stony Brook University +# Copyright (c) 2003-2007 The Research Foundation of SUNY +# +# For specific licensing information, see the COPYING file distributed with +# this package. +# +# This Copyright notice must be kept intact and distributed with all sources. + +CFLAGS=-g -Wall -Werror # -lefence +MOUNTPOINT=. +BINS=open-unlink flock-copyup fsync truncate bug418 rmdircheckinode \ + creat-open rename mapper queryfile + +all: $(BINS) + +check: all + echo "I am the very model of a Modern major general." > $(MOUNTPOINT)/a + ./open-unlink $(MOUNTPOINT)/a + +clean: + rm -f $(BINS) core *~ diff --git a/progs/bug418.c b/progs/bug418.c new file mode 100644 index 0000000..e0355db --- /dev/null +++ b/progs/bug418.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2003-2007 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2007 Stony Brook University + * Copyright (c) 2003-2007 The Research Foundation of SUNY + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ + +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + int fd; + char string[] = "XXXXXXXXXXXXXXX"; + + if (argc != 2) { + fprintf(stderr, "%s filename\n", argv[0]); + exit(1); + } + + fd = open(argv[1], O_RDWR); + if (fd < 0) { + perror("open"); + exit(1); + } + + sleep(7); + + if (write(fd, string, strlen(string)) != strlen(string)) { + perror("write"); + exit(1); + } + + exit(0); +} diff --git a/progs/creat-open.c b/progs/creat-open.c new file mode 100644 index 0000000..9f24515 --- /dev/null +++ b/progs/creat-open.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2003-2007 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2007 Stony Brook University + * Copyright (c) 2003-2007 The Research Foundation of SUNY + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + return (creat(argv[0], 0755) == -1 ? errno : 0); +} diff --git a/progs/flock-copyup.c b/progs/flock-copyup.c new file mode 100644 index 0000000..daf9fb4 --- /dev/null +++ b/progs/flock-copyup.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2003-2007 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2007 Stony Brook University + * Copyright (c) 2003-2007 The Research Foundation of SUNY + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ + +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + int fd; + struct flock lock; + + if (argc != 2) { + fprintf(stderr, "%s file\n", argv[0]); + exit(1); + } + + fd = open(argv[1], O_RDWR); + if (fd == -1) { + perror("open"); + exit(1); + } + + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + + if (fcntl(fd, F_SETLK, &lock) == -1) { + perror("fcntl"); + exit(1); + } + + exit(0); +} diff --git a/progs/fsync.c b/progs/fsync.c new file mode 100644 index 0000000..5756e51 --- /dev/null +++ b/progs/fsync.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2003-2007 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2007 Stony Brook University + * Copyright (c) 2003-2007 The Research Foundation of SUNY + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ + +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + const char *s = "I am the very model of a modern major general.\n"; + int fd; + + if (argc != 2) { + fprintf(stderr, "%s file\n", argv[0]); + exit(1); + } + + fd = open(argv[1], O_RDWR); + if (fd == -1) { + perror("open"); + exit(1); + } + + if (write(fd, s, strlen(s)) != strlen(s)) { + perror("write"); + exit(1); + } + + if (fsync(fd) != 0) { + perror("fsync"); + exit(1); + } + + exit(0); +} diff --git a/progs/mapper.c b/progs/mapper.c new file mode 100644 index 0000000..c18e157 --- /dev/null +++ b/progs/mapper.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 1997-2007 Erez Zadok + * Copyright (c) 2001-2007 Stony Brook University + * Copyright (c) 2003-2007 The Research Foundation of SUNY + * + * For specific licensing information, see the COPYING file distributed with + * this package, or get one from + * ftp://ftp.filesystems.org/pub/fistgen/COPYING. + * + * This Copyright notice must be kept intact and distributed with all + * fistgen sources INCLUDING sources generated by fistgen. + * + * + * File: mapper.c + * + * Usage: mapper -r file + * Usage: mapper -w file + * -r will open file for reading only + * -w will open file for reading anf writing, and will change the file + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BASE 0 +#define SIZE 10 + +#ifndef MAP_FAILED +#define MAP_FAILED ((void *)-1) +#endif + +void usage(void) +{ + fprintf(stderr, "Usage: mapper -r filename\n"); + fprintf(stderr, " mapper -w filename\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + int fd; + caddr_t pa; + int rw_flag = -1; /* 0=read, 1=read+write */ + char *filename, byte; + + /* check input args */ + if (argc != 3) + usage(); + if (strcmp(argv[1], "-w") == 0) + rw_flag = 1; + else if (strcmp(argv[1], "-r") == 0) + rw_flag = 0; + else + usage(); + + filename = argv[2]; + + /* open input file */ + if (rw_flag) + fd = open(filename, O_RDWR); + else + fd = open(filename, O_RDONLY); + if (fd < 0) { + perror(filename); + exit(1); + } + printf("opened file %s\n", filename); + + /* mmap the input file */ + pa = mmap((caddr_t) 0, + SIZE, + (rw_flag ? (PROT_READ | PROT_WRITE) : PROT_READ), + MAP_SHARED, + fd, + BASE); + if (pa == MAP_FAILED) { + perror("mmap"); + exit(1); + } + printf("file is mapped with address %p\n", pa); + + /* now read the first byte of that file */ + byte = pa[0]; + printf("byte 0 is decimal %03d (\'%c\')\n", + byte, isprint(byte) ? byte : '.'); + + /* for a read-write test, modify the first byte */ + if (rw_flag) + pa[0] += 1; + + /* msync the file */ + if (msync(pa, SIZE, MS_SYNC) < 0) { + perror("msync"); + exit(1); + } + printf("file is msynchronized\n"); + + /* unmap the file */ + if (munmap(pa, SIZE) < 0) { + perror("munmap"); + exit(1); + } + printf("file is unmapped\n"); + + close(fd); + printf("closed file %s\n", filename); + + /* + * Now, if we need to do a read-write test, then we reopen the file and + * check if the file's content (first bytes) is what we expect it to be. + */ + if (!rw_flag) + goto out; + /* re-open file (readonly now) */ + fd = open(filename, O_RDONLY); + if (fd < 0) { + perror(filename); + exit(1); + } + printf("re-opened file %s\n", filename); + + /* re-mmap the input file */ + pa = mmap((caddr_t) 0, + SIZE, + PROT_READ, + MAP_SHARED, + fd, + BASE); + if (pa == MAP_FAILED) { + perror("mmap"); + exit(1); + } + printf("file is re-mapped with address %p\n", pa); + /* check if bytes match */ + if (pa[0] != (byte+1)) { + fprintf(stderr, + "bytes mismatched in re-mapped file, old=%03d new=%03d!\n", + byte+1, pa[0]); + exit(1); + } + /* unmap the file */ + if (munmap(pa, SIZE) < 0) { + perror("munmap"); + exit(1); + } + printf("file is unmapped again\n"); + + close(fd); + printf("closed file %s again\n", filename); + +out: + exit(0); +} diff --git a/progs/open-unlink.c b/progs/open-unlink.c new file mode 100644 index 0000000..c8b1388 --- /dev/null +++ b/progs/open-unlink.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2003-2007 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2007 Stony Brook University + * Copyright (c) 2003-2007 The Research Foundation of SUNY + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ + +/* This is a testcase for BUG 299. */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define ERROR(cond, s) if (cond) { \ + fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, s, strerror(errno)); \ + assert(0); \ + exit(1); \ +} + +int main(int argc, char *argv[]) { + int fd, fd2; + int ret; + char *filename; + char bufsize; + char *buf; + char *buf2; + struct stat st; + char *judygarland = "Somewhere over the rainbow way up high, there's a land that I heard of Once in a lullaby."; + char *judygarland2 = "Somewhere over the rainbow skies are blue, and the dreams that you dare to dream really do come true."; + int l; + + if (argc != 2) { + fprintf(stderr, "%s file\n", argv[0]); + exit(1); + } + + filename = argv[1]; + + fd = open(filename, O_RDWR); + ERROR(fd == -1, "open"); + ERROR(fstat(fd, &st) == -1, "fstat"); + ERROR(st.st_size <= 0, "Original file too small"); + bufsize = st.st_size; + ERROR((buf = malloc(bufsize + 1)) == NULL, "malloc(buf)"); + ERROR((buf2 = malloc(bufsize + 1)) == NULL, "malloc(buf2)"); + ERROR(read(fd, buf, bufsize + 1) != bufsize, "Original read"); + + ERROR(unlink(filename) != 0, "Unlink"); + + /* This fails if we can't read after the unlink. */ + ERROR(lseek(fd, 0, SEEK_SET) != 0, "Second read seek"); + ERROR(read(fd, buf2, bufsize + 1) != bufsize, "Second read"); + ERROR(memcmp(buf, buf2, bufsize) != 0, "Second bad data"); + + fd2 = open(filename, O_RDWR|O_CREAT|O_EXCL, 0644); + ERROR(fd2 == -1, "Second open"); + + /* This fails if we can't read after recreation. */ + ERROR(lseek(fd, 0, SEEK_SET) != 0, "Third read seek"); + ERROR(read(fd, buf2, bufsize + 1) != bufsize, "Third read"); + ERROR(memcmp(buf, buf2, bufsize) != 0, "Third bad data"); + + l = strlen(judygarland); + ERROR(write(fd2, judygarland, l) != l, "Rewrite."); + ERROR(close(fd2) != 0, "close of new fd"); + + /* This fails if we can't read after rewrite. */ + ERROR(lseek(fd, 0, SEEK_SET) != 0, "Fourth read"); + ERROR(read(fd, buf2, bufsize + 1) != bufsize, "Fourth read"); + ERROR(memcmp(buf, buf2, bufsize) != 0, "Fourth bad data"); + + /* This fails if we can't copyup. */ + ERROR(fstat(fd, &st) == -1, "fstat"); + ERROR(st.st_size != bufsize, "File size changed."); + + l = strlen(judygarland2); + if (bufsize < l) { + free(buf); + free(buf2); + buf = strdup(judygarland2); + ERROR(buf == NULL, "Reallocate buf."); + bufsize = strlen(buf); + buf2 = malloc(bufsize); + ERROR(buf2 == NULL, "Reallocate buf2."); + } else { + memcpy(buf, judygarland2, l); + } + + /* Rewrite of the file using our own fd (provoking copyup). */ + ERROR(lseek(fd, 0, SEEK_SET) != 0, "Second rewrite seek."); + ERROR(write(fd, judygarland2, l) != l, "Second rewrite"); + ERROR(fstat(fd, &st) == -1, "fstat"); + ERROR(st.st_size != bufsize, "File size not bufsize."); + + /* This fails if we our rewrite didn't work. */ + ERROR(lseek(fd, 0, SEEK_SET) != 0, "Fifth read seek"); + ERROR((ret = read(fd, buf2, bufsize + 1)) != bufsize, "Fifth read"); + ERROR(memcmp(buf, buf2, bufsize) != 0, "Fifth bad data"); + + ERROR(close(fd) != 0, "close of original fd"); + + /* Now to make sure we didn't clobber the first verse. */ + fd2 = open(filename, O_RDWR); + ERROR(fd2 == -1, "Third open"); + l = strlen(judygarland); + if (bufsize < l + 1) { + free(buf2); + bufsize = l; + buf2 = malloc(bufsize + 1); + ERROR(buf2 == NULL, "Reallocate buf2."); + } + ERROR(read(fd, buf2, l + 1) != l, "Sixth read"); + ERROR(memcmp(judygarland, buf2, l) != 0, "Sixth bad data"); + ERROR(close(fd2) != 0, "Second close of new fd"); + + unlink(filename); + + exit(0); +} diff --git a/progs/queryfile.c b/progs/queryfile.c new file mode 100644 index 0000000..ac09a1a --- /dev/null +++ b/progs/queryfile.c @@ -0,0 +1,369 @@ +/* + * Copyright (c) 2003-2007 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef Sipek + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2005-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2007 Stony Brook University + * Copyright (c) 2003-2007 The Research Foundation of SUNY + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAY_READ 4 +#define MAY_WRITE 2 +#define MAY_NFSRO 16 + +# define UNIONFS_IOCTL_QUERYFILE _IOR(0x15, 15, int) + +/* Branch information */ +struct unionfs_branch { + char *path; + int perms; +}; + +static char **branches; +static int *branchperms; + +int parse_rw(char *p) { + if (strcmp(p, "ro") == 0) + return MAY_READ; + else if (strcmp(p, "nfsro") == 0) + return MAY_READ | MAY_NFSRO; + else if (strcmp(p, "rw") == 0) + return MAY_READ | MAY_WRITE; + else + return 0; +} + +char **parse_options(char *options) +{ + char **ret = NULL; + int i = 0; + + char *p; + char *q; + char *r; + char *s, *t, *u; + + p = options; + do { + q = strchr(p, ','); + if (q) { + *q++ = '\0'; + } + if (!strncmp(p, "dirs=", strlen("dirs="))) { + r = p + strlen("dirs="); + do { + s = strchr(r, ':'); + if (s) { + *s++ = '\0'; + } + + i++; + ret = realloc(ret, sizeof(char *) * (i + 1)); + if (!ret) { + perror("realloc()"); + return NULL; + } + branchperms = + realloc(branchperms, sizeof(int) * i); + if (!branchperms) { + perror("realloc()"); + return NULL; + } + + t = strchr(r, '='); + u = t + 1; + if (!t || !u || !*u) + goto err; + *t = 0; + branchperms[i - 1] = parse_rw(u); + if (!branchperms[i - 1]) { + err: + fprintf(stderr, "cannot parse '%s'\n", + r); + return NULL; + } + ret[i - 1] = strdup(r); + ret[i] = NULL; + + r = s; + } + while (r); + } + p = q; + } + while (p); + + branches = ret; + return ret; +} + +/* + * This function will take a patch and check it against /proc/mounts to find + * its mount point. If uniononly is set then it will make sure its a unionfs + * mount point. This function assumes the both options and actual_path are + * valid and not null; + */ +int find_union(const char *path, char **options, char **actual_path, + int uniononly) +{ + FILE *f = NULL; + char *s = NULL; + char *s2 = NULL; + char *p; + char *q; + int candidate = 0; + int mallocsize = 1024; /* Just a reasonable starting value. */ + + retry: + if (*options) { + free(*options); + *options = NULL; + } + + if (*actual_path) { + free(*actual_path); + *actual_path = NULL; + } + if (f) { + fclose(f); + f = NULL; + } + s2 = realloc(s, mallocsize); + if (!s2) { + fprintf(stderr, "realloc(%d): %s\n", mallocsize, + strerror(errno)); + goto out; + } + s = s2; + + f = fopen("/proc/mounts", "r"); + if (!f) { + fprintf(stderr, "fopen(/proc/mounts): %s\n", strerror(errno)); + goto out; + } + while (fgets(s, mallocsize, f)) { + int testcan; + + /* If we don't have enough information, we should remalloc it. */ + if (strlen(s) == (mallocsize - 1)) { + mallocsize *= 2; + goto retry; + } + + p = strchr(s, ' '); + if (!p) + continue; + p++; + + q = strchr(p, ' '); + if (!q) + continue; + *q++ = '\0'; + + testcan = strlen(p); + if (testcan <= candidate) { + continue; + } + + if (!strncmp(path, p, testcan)) { + if (*actual_path) { + free(*actual_path); + } + *actual_path = strdup(p); + if (!*actual_path) { + fprintf(stderr, "strdup: %s\n", + strerror(errno)); + goto out; + } + p = strchr(q, ' '); + if (!p) + continue; + *p++ = '\0'; + if (uniononly) { + if (strcmp(q, "unionfs")) { + candidate = 0; + continue; + } + } + candidate = testcan; + + q = strrchr(p, ' '); + if (!q) + continue; + *q = '\0'; + q = strrchr(p, ' '); + if (!q) + continue; + *q = '\0'; + + if (*options) { + free(*options); + } + *options = strdup(p); + if (!*options) { + fprintf(stderr, "strdup: %s\n", + strerror(errno)); + goto out; + } + } + } + + out: + if (s) + free(s); + if (f) + fclose(f); + + if (*options) { + return 0; + } + + errno = -ENOENT; + return -1; +} + +int load_branches(const char *union_path) +{ + int ret; + char *options = NULL, *actual_path = NULL; + + ret = find_union(union_path, &options, &actual_path, 1); + if (ret) { + errno = EINVAL; + goto out; + } + + branches = parse_options(options); + if (branches <= 0) { + fprintf(stderr, "Could not parse options from /proc/mounts!\n"); + ret = -1; + goto out; + } + + out: + if (options) + free(options); + + if (actual_path) + free(actual_path); + + return ret; +} + +/* + * Resolves the real path of a relative path + */ +int get_real_path(const char *path, char *resolv_path) +{ + struct stat st; + + if (realpath(path, resolv_path) == NULL) { + return -1; + } + + if (strcmp(resolv_path, "/") && (resolv_path[strlen(resolv_path) - 1] == '/')) { + resolv_path[strlen(resolv_path) - 1] = '\0'; + } + + if (stat(resolv_path, &st) == -1) { + perror("stat()"); + return -1; + } + + return 0; +} + +int unionfs_query(const char *file_path, struct unionfs_branch **ufs_branches) +{ + int i; + int fd; + int ret; + int len; + fd_set branchlist; + char resolv_path[PATH_MAX]; + + if ( get_real_path(file_path, resolv_path) ) + return -1; + + if ( load_branches(resolv_path) ) + return -1; + + if ((fd = open(file_path, O_RDONLY)) < 0) { + fprintf(stderr, + "Unable to open file %s : %s", + file_path, strerror(errno)); + return -1; + } + + len = ioctl(fd, UNIONFS_IOCTL_QUERYFILE, &branchlist); + if (len < 0) { + fprintf(stderr, + "Unable to retrieve list of branches for file %s : %s\n", + file_path, strerror(errno)); + return -1; + } + + ret = 0; + *ufs_branches = malloc(sizeof(struct unionfs_branch)); + if (!(*ufs_branches)) { + errno = ENOMEM; + return -1; + } + + + for (i = 0; i <= len; i++) { + if (FD_ISSET(i, &branchlist)) { + *ufs_branches = realloc(*ufs_branches, + sizeof(struct unionfs_branch)*(ret+1)); + (*ufs_branches)[ret].path = malloc(strlen(branches[ret]+1)); + strcpy((*ufs_branches)[ret].path, branches[i]); + (*ufs_branches)[ret].perms = branchperms[i]; + ret++; + } + + } + return ret; +} + +int main(int argc, char *argv[]) +{ + struct unionfs_branch *branches; + int ret, i; + + ret = unionfs_query(argv[1], &branches); + if (ret < 0) { + fprintf(stderr, "Unable to retrieve list of branches for %s: %s\n", + argv[1], strerror(errno)); + exit(EXIT_FAILURE); + } + for (i = 0; i < ret; i++) { + char r, w, n; + r = (branches[i].perms & MAY_READ) ? 'r' : '-'; + w = (branches[i].perms & MAY_WRITE) ? 'w' : '-'; + n = (branches[i].perms & MAY_NFSRO) ? 'n' : '-'; + printf("%s\t%s (%c%c%c)\n", argv[1], + branches[i].path, r, w, n); + } + exit(EXIT_SUCCESS); +} diff --git a/progs/rename.c b/progs/rename.c new file mode 100644 index 0000000..5639f89 --- /dev/null +++ b/progs/rename.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2003-2007 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2007 Stony Brook University + * Copyright (c) 2003-2007 The Research Foundation of SUNY + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + int e; + + if (argc != 3) { + printf("Usage:\n%s source dest\n", argv[0]); + exit(0); + } + + e = rename(argv[1], argv[2]); + + if (e == -1) { + e = errno; + perror("rename"); + } else { + e = 0; + } + + return e; +} + diff --git a/progs/rmdircheckinode.c b/progs/rmdircheckinode.c new file mode 100644 index 0000000..6b28d99 --- /dev/null +++ b/progs/rmdircheckinode.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2003-2007 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2007 Stony Brook University + * Copyright (c) 2003-2007 The Research Foundation of SUNY + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + struct stat before, after; + + if (argc != 2) { + fprintf(stderr, "%s filename\n", argv[0]); + exit(1); + } + + if (stat(argv[1], &before)) + perror("First Stat"); + if (rmdir(argv[1])) + if (errno != ENOTEMPTY) + perror("rmdir"); + if (stat(argv[1], &after)) + perror("Second stat"); + if (before.st_ino != after.st_ino) + exit(1); + exit(0); +} diff --git a/progs/truncate.c b/progs/truncate.c new file mode 100644 index 0000000..ff543db --- /dev/null +++ b/progs/truncate.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2003-2007 Erez Zadok + * Copyright (c) 2003-2006 Charles P. Wright + * Copyright (c) 2005-2007 Josef 'Jeff' Sipek + * Copyright (c) 2005-2006 Junjiro Okajima + * Copyright (c) 2005 Arun M. Krishnakumar + * Copyright (c) 2004-2006 David P. Quigley + * Copyright (c) 2003-2004 Mohammad Nayyer Zubair + * Copyright (c) 2003 Puja Gupta + * Copyright (c) 2003 Harikesavan Krishnan + * Copyright (c) 2003-2007 Stony Brook University + * Copyright (c) 2003-2007 The Research Foundation of SUNY + * + * For specific licensing information, see the COPYING file distributed with + * this package. + * + * This Copyright notice must be kept intact and distributed with all sources. + */ + +#include +#include +#include +#include +#include + + +void usage(const char *argv) { + fprintf(stderr, "%s -f file size\n", argv); + exit(1); +} + +int main(int argc, char *argv[]) { + off_t size; + char *end; + + if (argc < 3 || argc > 4) { + usage(argv[0]); + } + if (argc == 4) { + int fd; + if (strcmp(argv[1], "-f")) { + usage(argv[0]); + fprintf(stderr, "%s -f file size\n", argv[0]); + exit(1); + } + fd = open(argv[2], O_RDWR); + if (fd == -1) { + perror("open"); + exit(1); + } + size = strtoul(argv[3], &end, 0); + if (*end) { + usage(argv[0]); + } + + if (ftruncate(fd, size) == -1) { + perror("ftruncate"); + exit(1); + } + close(fd); + } else { + size = strtoul(argv[2], &end, 0); + if (*end) { + usage(argv[0]); + } + if (truncate(argv[1], size) == -1) { + perror("truncate"); + exit(1); + } + } + exit(0); +} diff --git a/run-all-tests b/run-all-tests new file mode 100755 index 0000000..e06371d --- /dev/null +++ b/run-all-tests @@ -0,0 +1,14 @@ +#!/bin/sh +# run all unionfs regression tests + +# support for nfs4 isn't ready yet +LIST="ext2 ext3 xfs reiserfs nfs2 nfs3" +for fs in $LIST +do + echo "Running $fs tests..." + sleep 1 + MYFS=$fs ./run-tests default || exit $? +done + +# jffs2 is a special test, as jffs2 is used only as the leftmost branch +MYFS="jffs2" ./run-tests jffs2 || exit $? diff --git a/run-tests b/run-tests new file mode 100755 index 0000000..bf69144 --- /dev/null +++ b/run-tests @@ -0,0 +1,238 @@ +#!/bin/bash -norc +# run from within regression-2.0/ +# usage: ./run-tests confname +# e.g., ./run-tests default +# +# Note: force user to type configuration file name, because that file contains +# commands that *format* the two tests file systems!!! + +# check usage +if test -z "$1" +then + echo "Usage: $0 confname" + echo "e.g.: $0 default" + exit 1 +fi + +# check for arguments +CONF="$1" +test -f $CONF || CONF="$1.conf" +if test -f "$CONF" +then + source "$CONF" +else + echo "Cannot find configuration file \"$CONF\"" + exit 1 +fi +if test -z "$TESTS2RUN" +then + echo "No tests defined" + exit 1 +fi +for i in 0 1 2 3 ; do + dev=$(eval echo \$DEV$i) + if test -z "$dev" ; then + echo "No DEV$i defined" + exit 1 + fi + fs=$(eval echo \$FS$i) + if test -z "$fs" ; then + echo "No FS$i defined" + exit 1 + fi +done + +# pretty sleep command +function wait4 +{ + sleep $delay +} +# run command verbosely plus optional delay +function runcmd +{ + test -n "$ECHODEV" && echo "CMD: $@" >> $ECHODEV + echo "CMD: $@" + test -n "$delay" && wait4 $delay + + test -n "$OUTPUT_COLOR" && echo -e -n "\033[${OUTPUT_COLOR}m" + $@ + ret=$? + test -n "$OUTPUT_COLOR" && echo -e -n "\033[m" + + test $ret -ne 0 && exit $ret +} +# setup lower file systems (WARNING: will *format* all lower file systems!!!) +function setup_lower +{ + # unset delay here, because we don't need to delay for these setup commands + for i in 0 1 2 3 ; do + dev=$(eval echo \$DEV$i) + fs=$(eval echo \$FS$i) + + case "$dev" in + /dev/loop*|/dev/.static/dev/loop*) + case "$fs" in + jffs2 ) + runcmd cp jffs2-empty.img /tmp/fs.$$.$i + ;; + * ) + runcmd dd if=/dev/zero of=/tmp/fs.$$.$i bs=1024k count=1 seek=100 2> /dev/null + ;; + esac + runcmd losetup $dev /tmp/fs.$$.$i + ;; + *) + ;; + esac + + case "$fs" in + nfs* ) + # figure out the NFS version, if any (default = NFSv3) + vers=`echo $fs | tr -dc [0-9]` + case "$vers" in + 2 | 3 ) + versopt="-o nfsvers=$vers" + fs="nfs" + ;; + 4 ) + versopt="" + ;; + * ) + versopt="" + fs="nfs" + ;; + esac + basefs=ext2 # the base disk based f/s to export to NFS + runcmd mkfs -t $basefs -q $dev + runcmd mkdir -p /n/export/b$i + runcmd mount -t $basefs $dev /n/export/b$i + # nfsv4 needs an extra export command for the namespace root + if test "$fs" = "nfs4" ; then + runcmd exportfs -o no_root_squash,rw,insecure,fsid=0 localhost:/ + fi + runcmd exportfs -o no_root_squash,rw localhost:/n/export/b$i + runcmd mkdir -p /n/lower/b$i + runcmd mount -t $fs $versopt localhost:/n/export/b$i /n/lower/b$i + ;; + jffs2 ) + runcmd modprobe mtdblock + runcmd modprobe block2mtd block2mtd=$dev,128ki + runcmd mount -t $fs /dev/mtdblock$i /n/lower/b$i + ;; + reiserfs ) + # reiserfs v3 mkfs command does not honor the -q flag + # completely. It prints out copyright and credits text, + # some on stdout and some on stderr. So ignore all + # stderr/stdout output from mkfs.reiserfs. + runcmd mkfs -t $fs -q $dev > /dev/null 2>&1 + runcmd mkdir -p /n/lower/b$i + runcmd mount -t $fs $dev /n/lower/b$i + ;; + ext4 ) + # ext4 is in development. Use ext3 f/s format for now. + runcmd mkfs -t ext3 -q $dev + runcmd mkdir -p /n/lower/b$i + runcmd mount -t ext4dev $dev /n/lower/b$i + ;; + * ) + runcmd mkfs -t $fs -q $dev + runcmd mkdir -p /n/lower/b$i + runcmd mount -t $fs $dev /n/lower/b$i + ;; + esac + + done +} + +function do_umount +{ + branch=$1 + dev=$2 + fs=$3 + + # first, unmount main branch + runcmd umount /n/lower/b$branch + + case "$fs" in + nfs* ) + runcmd exportfs -u localhost:/n/export/b$branch + runcmd umount /n/export/b$branch + # nfsv4: extra unexport command + test "$fs" = "nfs4" && \ + runcmd exportfs -u localhost:/ + ;; + jffs2 ) + runcmd rmmod block2mtd + runcmd rmmod mtdblock + ;; + * ) + ;; + esac + case "$dev" in + /dev/loop*|/dev/.static/dev/loop*) + runcmd losetup -d $dev + runcmd rm -f /tmp/fs.$$.$branch + ;; + *) + ;; + esac +} + +export PATH=../../unionfs-utils:$PATH + +LINEBRK="----------------------------------------------------------------------" + +for t in ${TESTS2RUN} +do + test -n "$ECHODEV" && echo $LINEBRK >> $ECHODEV + echo $LINEBRK + # skip tests? + case $t in + *OFF* ) + test -n "$OUTPUT_COLOR" && echo -e -n "\033[${OUTPUT_COLOR}m" + echo -n "SKIPPING TEST: $t ..." + test -n "$OUTPUT_COLOR" && echo -e -n "\033[m" + echo + exit 0 + ;; + esac + # find test to run + test -f $t || t="t-$t.sh" + if test -f $t + then + test -n "$OUTPUT_COLOR" && echo -e -n "\033[${OUTPUT_COLOR}m" + echo "TEST: $t (FS=${MYFS:-default})" + test -n "$OUTPUT_COLOR" && echo -e -n "\033[m" + else + echo "no such test script $t" + exit 1 + fi + + delay=$DELAY + echo "Sleeping for "$delay" seconds between each command." + setup_lower + + # export the type of leftmost file system, as some tests need to know (jffs2) + export FS0 + + # run actual test and abort on error (test script tries to umount unionfs) + runcmd bash $t + + # When using loop devices and nfsv3, sometimes some of the lower branches + # cannot be unmounted with an EBUSY. But if you wait a little longer for + # pdflush to run, or run /bin/sync manually, then you can mount those + # lower branches. This is probably some sort of a race between the loop + # device driver and NFSv3. We can work around it if we mount all nfs + # partitions with "-o sync", or run sync by hand here. + runcmd sync + + # try to unmount lower file systems (check if we leak anything) + for i in 0 1 2 3 ; do + do_umount $i $(eval echo \$DEV$i) $(eval echo \$FS$i) + done + # unload unionfs module (check if we leak anything) + runcmd rmmod unionfs +done + +# all's well +exit 0 diff --git a/scaffold b/scaffold new file mode 100644 index 0000000..7a24dbd --- /dev/null +++ b/scaffold @@ -0,0 +1,274 @@ +#!/bin/sh +#vim:filetype=sh +#vim:shiftwidth=8 + +set -e +echo -n "$0: " + +TOP_LOWER_DIR=/n/lower + +test -z "$MOUNTPOINT" && MOUNTPOINT=/mnt/unionfs +test -z "$LOWER_DIR0" && LOWER_DIR0=$TOP_LOWER_DIR/b0 +test -z "$LOWER_DIR1" && LOWER_DIR1=$TOP_LOWER_DIR/b1 +test -z "$LOWER_DIR2" && LOWER_DIR2=$TOP_LOWER_DIR/b2 +test -z "$LOWER_DIR3" && LOWER_DIR3=$TOP_LOWER_DIR/b3 +test -z "$NFS" && NFS= +test -z "$DEBUG" && DEBUG=0 + +MOUNTS=0 + +function roloopify { + local DIR=$1 + if [ ! -d "$DIR" ] ; then + echo "Can not find $DIR" 1>&2 + return 1 + fi + mkisofs -quiet -o /tmp/$$.iso -JR $DIR + mount -o loop,ro /tmp/$$.iso $DIR +# rm -f /tmp/$$.iso + echo "Inspect" + read N + return 0 +} + +function havechattr { + local FILE=tmp.chattr.check + if [ ! -d $1 ] ; then + return 1 + fi + touch $1/$FILE + if ! chattr +i $1/$FILE >/dev/null 2>&1 ; then + rm -f $1/$FILE + return 1 + fi + if (echo "Hello World" > $1/$FILE) >/dev/null 2>&1 ; then + chattr -i $1/$FILE >/dev/null 2>&1 + rm -f $1/$FILE + return 1 + fi + chattr -i $1/$FILE + rm -f $1/$FILE + return 0 +} + +function create_hierarchy { +# XXX: what's this for?! +# touch $LOWER_DIR0/f + + havechattr $LOWER_DIR0 && chattr -R -i $LOWER_DIR0 + havechattr $LOWER_DIR1 && chattr -R -i $LOWER_DIR1 + havechattr $LOWER_DIR2 && chattr -R -i $LOWER_DIR2 + havechattr $LOWER_DIR3 && chattr -R -i $LOWER_DIR3 + + # delete all files, dirs, and hidden files below mount points + # (we cannot delete the mounted mountpoints themselves (EBUSY) + rm -fr $TOP_LOWER_DIR/b[0-3]/[a-zA-Z0-9_]* + rm -fr $TOP_LOWER_DIR/b[0-3]/.[a-zA-Z0-9_]* + + while read LINE + do + if [ "$LINE" = "" ] ; then + continue + fi + TYPE=`echo $LINE | cut -d' ' -f 1` + NAME=`echo $LINE | cut -d' ' -f 2` + + unset DIR FILE IMMUTABLE SOURCE SYMLINK + + ( echo $TYPE | grep -q d ) && DIR=1 + ( echo $TYPE | grep -q f ) && FILE=1 + ( echo $TYPE | grep -q i ) && IMMUTABLE=1 + ( echo $TYPE | grep -q s ) && SOURCE=1 + ( echo $TYPE | grep -q l ) && SYMLINK=1 + + if [ ! -z "$SOURCE" ] ; then + if [ ! -z "$DIR" ] ; then + echo "BAD TYPE (rename sources cannot be directories): $TYPE" 1>&2 + exit 1 + fi + FILE=1 + fi + + if [ ! -z "$IMMUTABLE" ] ; then + if [ -z "$DIR" -a -z "$SYMLINK" ] ; then + FILE=1 + fi + fi + + if [ ! -z "$DIR" -a ! -z "$FILE" ] ; then + echo "BAD TYPE (both a file and directory) : $TYPE" 1>&2 + exit 1 + fi + if [ ! -z "$DIR" -a ! -z "$SYMLINK" ] ; then + echo "BAD TYPE (both a symbolic link and directory) : $TYPE" 1>&2 + exit 1 + fi + if [ ! -z "$FILE" -a ! -z "$SYMLINK" ] ; then + echo "BAD TYPE (both a symbolic link and a file) : $TYPE" 1>&2 + exit 1 + fi + + + if [ ! -z "$DIR" -a ! -z "$SOURCE" ] ; then + echo "Directories can not be sources: $TYPE" 1>&2 + exit 1 + fi + + if [ ! -z "$SYMLINK" -a ! -z "$SOURCE" ] ; then + echo "Symbolic links can not be sources: $TYPE" 1>&2 + exit 1 + fi + + if [ ! -z "$DIR" ] ; then + mkdir -p $NAME + elif [ ! -z "$FILE" ] ; then + mkdir -p `dirname $NAME` || exit $? + if [ ! -z "$SOURCE" ] ; then + echo "Source file." > $NAME || exit $? + else + echo $NAME > $NAME || exit $? + fi + elif [ ! -z "$SYMLINK" ] ; then + ln -s "linktext:$NAME" $NAME + else + echo "What type am i: $TYPE" 1>&2 + exit $? + fi + + if [ ! -z "$IMMUTABLE" ] ; then + chattr +i $NAME || exit $? + fi + done +} + +function check_hierarchy { + ( find $1 -type d -printf 'd %p\n' ; find $1 -type f -printf 'f %p\n' ; find $1 -type b -printf 'b %p\n' ; find $1 -type c -printf 'c %p\n' ; find $1 -type l -printf 'l %p\n') | sort > /tmp/check-$$ + grep -v '^$' | sort | diff -u - /tmp/check-$$ + ERR=$? + rm -f /tmp/check-$$ + return $ERR +} + +function mount_union { + if [ "$MOUNTS" -gt 0 ] ; then + echo "There is already an outstanding mount!" 1>&2 + exit 1 + fi + + if [ -z "$1" ] ; then + OPTION="dirs=" + else + OPTION="$1,dirs=" + fi + + shift + + while [ "$#" -gt 0 ] + do + OPTION="$OPTION$1" + if [ "$#" -ne "1" ] ; then + OPTION="$OPTION"":" + fi + shift + done + + mount -t unionfs -o $OPTION none $MOUNTPOINT + + MOUNTS=$((MOUNTS + 1)) + + return $? +} + +function unmount_union { + umount $MOUNTPOINT + ERR=$? + if [ "$?" -eq "0" ] ; then + MOUNTS=$((MOUNTS - 1)) + else + echo "Could not unmount $MOUNTPOINT" 1>&2 + exit 1 + fi + return $ERR +} + +function checktype { + local F=$1 + local CHECK=$2 + + local ERR= + if [ "$CHECK" = "-" ] ; then + [ ! -e "$F" ] || ERR=$? + elif [ "$CHECK" = "f" ] ; then + [ -f "$F" ] || ERR=$? + elif [ "$CHECK" = "d" ] ; then + [ -d "$F" ] || ERR=$? + elif [ "$CHECK" = "b" ] ; then + [ -b "$F" ] || ERR=$? + elif [ "$CHECK" = "c" ] ; then + [ -c "$F" ] || ERR=$? + elif [ "$CHECK" = "l" ] ; then + [ -l "$F" ] || ERR=$? + else + echo "Unknown check '$CHECK'" 1>&2 + /bin/false + fi + local SERR=$? + if [ -z "$ERR" ] ; then + ERR=$SERR + fi + + if [ "$ERR" != "0" ] ; then + echo "$F doesn't match '$CHECK'" 1>&2 + ls -ld $F 1>&2 + fi + + return $ERR +} + +function checkperms { + local F=$1 + local CHECK=$2 + + local PERMS=`find "$1" -printf %m` + + if [ "$PERMS" != "$CHECK" ] ; then + echo "$F permissions $PERMS doesn't match '$CHECK'" 1>&2 + ls -ld $F 1>&2 + return 1 + fi + + return 0 +} + +function shouldfail { + if $* 2>/tmp/saveout-$$ ; then + echo "UNWANTED SUCCESS: " $* + cat /tmp/saveout-$$ + return 1 + fi + rm -f /tmp/saveout-$$ + return 0 +} + +function complete_test { + if [ "$MOUNTS" -gt 0 ] ; then + echo "There is a leftover mount!" 1>&2 + exit 1 + fi + if [ -f complete.sh ] ; then + COMPLETE=1 + source complete.sh + fi + echo "OK" + exit 0 +} + +function start_test { + if [ -f start.sh ] ; then + START=1 + source start.sh + START= + fi +} + +start_test diff --git a/showplans b/showplans new file mode 100755 index 0000000..a9ad6bf --- /dev/null +++ b/showplans @@ -0,0 +1,17 @@ +#!/bin/sh + +if [ "$#" -gt 0 ] ; then + FILES=$* +else + FILES=*.sh +fi + +for X in $FILES +do + if grep -q '^# TEST: ' $X ; then + echo "$X:" + grep '^# TEST: ' $X | sed -e 's/^\# TEST: //'g + echo + echo + fi +done diff --git a/t-branchman.sh b/t-branchman.sh new file mode 100755 index 0000000..ea3ce33 --- /dev/null +++ b/t-branchman.sh @@ -0,0 +1,173 @@ +#!/bin/sh + +# TEST: Branches: b0 +# TEST: add b1 before b0 +# TEST: add b1 after b0 + +source scaffold + +function files { +cat </tmp/$$ +$MOUNTPOINT $LOWER_DIR0 (rw-) +$MOUNTPOINT $LOWER_DIR1 (rw-) +$MOUNTPOINT $LOWER_DIR2 (rw-) +$MOUNTPOINT $LOWER_DIR3 (rw-) +$MOUNTPOINT $LOWER_DIR/b4 (rw-) +TMP + unionctl $MOUNTPOINT --query | diff - /tmp/$$ +cat </tmp/$$ +$MOUNTPOINT/b0-only $LOWER_DIR0 (rw-) +TMP + unionctl $MOUNTPOINT/b0-only --query | diff - /tmp/$$ +cat </tmp/$$ +$MOUNTPOINT/a $LOWER_DIR0 (rw-) +$MOUNTPOINT/a $LOWER_DIR1 (rw-) +$MOUNTPOINT/a $LOWER_DIR2 (rw-) +$MOUNTPOINT/a $LOWER_DIR3 (rw-) +$MOUNTPOINT/a $LOWER_DIR/b4 (rw-) +TMP + unionctl $MOUNTPOINT/a --query | diff - /tmp/$$ + rm -f /tmp/$$ + + unmount_union + ( files ; afterfiles ) | check_hierarchy $TOP_LOWER_DIR +} + + +if [ -z "$FXNS" ] ; then + #FXNS="add_before add_after add_multiple remove remove_multiple BUG370 query" + FXNS="query" +fi + +for x in $FXNS +do + $x + echo -n "[$x] " +done + +complete_test diff --git a/t-chmod.sh b/t-chmod.sh new file mode 100755 index 0000000..de7a7cb --- /dev/null +++ b/t-chmod.sh @@ -0,0 +1,99 @@ +#!/bin/sh + +# TEST: Branches b0,b1 and b0,b1=ro +# TEST: chmod(A, 700) +# TEST: Where A is in b0, b1, and both as a file/directory + + +source scaffold + +# initial directories +function beforefiles { +cat <&2 + return 1 + fi + + chmod 644 $TARGET || return $? + + if [ `find $TARGET -printf '%m'` != "644" ] ; then + echo "Permissions for $TARGET are not 644" 1>&2 + return 1 + fi + + return 0 +} + +function test1 { +# The read-write tests +( beforefiles ) | create_hierarchy + +mount_union "" $LOWER_DIR0 $LOWER_DIR1 + +do_chmod $MOUNTPOINT/a +do_chmod $MOUNTPOINT/b +do_chmod $MOUNTPOINT/c +do_chmod $MOUNTPOINT/d +do_chmod $MOUNTPOINT/e +do_chmod $MOUNTPOINT/f + +unmount_union + +( beforefiles ) | check_hierarchy $TOP_LOWER_DIR +echo -n "[rw] " +} + +function test2 { +# The readonly tests +( beforefiles ) | create_hierarchy +mount_union "" $LOWER_DIR0 $LOWER_DIR1=ro + +do_chmod $MOUNTPOINT/a +do_chmod $MOUNTPOINT/b +do_chmod $MOUNTPOINT/c +do_chmod $MOUNTPOINT/d +do_chmod $MOUNTPOINT/e +do_chmod $MOUNTPOINT/f + +unmount_union +( beforefiles ; afterfiles_ro ) | check_hierarchy $TOP_LOWER_DIR +echo -n "[ro] " +} + +test1 +test2 + +complete_test diff --git a/t-creat-open.sh b/t-creat-open.sh new file mode 100755 index 0000000..614a9e3 --- /dev/null +++ b/t-creat-open.sh @@ -0,0 +1,47 @@ +#!/bin/sh +# TEST: Branches: b0 +# copy creat-open to the lower file-system (before mount) +# TEST: run creat-open +# TEST: creat-open should be non-zero length + +source scaffold + +function files { +cat </tmp/$$ + uniondbg -g $MOUNTPOINT | diff /tmp/$$ - + rm /tmp/$$ +done + +unmount_union +( files ) | check_hierarchy $TOP_LOWER_DIR + +complete_test diff --git a/t-ioctl.sh b/t-ioctl.sh new file mode 100755 index 0000000..5ea5df4 --- /dev/null +++ b/t-ioctl.sh @@ -0,0 +1,69 @@ +#!/bin/sh + +# TEST: Branches: b0,b1=ro,b2=ro +# TEST: run queryfile ioctl +source scaffold + +function files { +cat < /tmp/.expected.$$ + +( +./progs/queryfile $MOUNTPOINT/D +./progs/queryfile $MOUNTPOINT/b +./progs/queryfile $MOUNTPOINT/c +./progs/queryfile $MOUNTPOINT/a +) > /tmp/.saveout.$$ 2>&1 + +unmount_union +# diff expected vs. actual out and exit on any change +diff -b /tmp/.expected.$$ /tmp/.saveout.$$ +result=$? +rm -f /tmp/.expected.$$ /tmp/.saveout.$$ +test $result != 0 && exit $result + +( files ; afterfiles ) | check_hierarchy $TOP_LOWER_DIR +complete_test + diff --git a/t-link-rename.sh b/t-link-rename.sh new file mode 100755 index 0000000..54e1950 --- /dev/null +++ b/t-link-rename.sh @@ -0,0 +1,44 @@ +#!/bin/sh +# TEST: Branches: b0,b1=ro,b2=ro +# This checks some rename and copyup issues where the +# copyup crosses a ro branch + +source scaffold + +function files { +cat < /dev/null +ln -s abc abc3 +mv abc abc_ +mv abc3 abc +cd - > /dev/null + +unmount_union +( afterfiles_ro ) | check_hierarchy $TOP_LOWER_DIR + +complete_test diff --git a/t-link.sh b/t-link.sh new file mode 100755 index 0000000..6914e12 --- /dev/null +++ b/t-link.sh @@ -0,0 +1,227 @@ +#!/bin/sh + +# TEST: Branches b0,b1 and b0,b1=ro +# TEST: link(A, B) +# TEST: Where A and B are in the same directory on b0/b1 +# TEST: Where A and B are in different directories on b0/b1 +# TEST: Where A is on b0 and B is on b1 +# TEST: Where A is on b1 and B is on b0 +# TEST: Where B already exists as a whiteout on the same branch +# TEST: Where B already exists as a whiteout on a higher priority branch +# TEST: Where A exists in b0 and B exists in b1 in a different directory (should create +# same directory structure in b0) + +source scaffold + +# initial directories +function directories { +cat < /dev/null" || (cleanup && echo "FAILED" && exit 1) +shouldfail su $UNPRIV_USER -c "ls -al $MOUNTPOINT/dir" +ls -ald $MOUNTPOINT/dir > /dev/null +ls -al $MOUNTPOINT/dir > /dev/null +su $UNPRIV_USER -c "ls -ald $MOUNTPOINT/dir > /dev/null" || (cleanup && echo "FAILED" && exit 1) +shouldfail su $UNPRIV_USER -c "ls -al $MOUNTPOINT/dir" + +# unmount, remove user, ... +cleanup + +branch_files | check_hierarchy $TOP_LOWER_DIR + +complete_test diff --git a/t-lookup.sh b/t-lookup.sh new file mode 100755 index 0000000..7e779ba --- /dev/null +++ b/t-lookup.sh @@ -0,0 +1,47 @@ +#!/bin/sh + +# TEST: lookup(F), where F is: +# TEST: File-Directory-File +# TEST: Directory-File-Directory +# TEST: Whiteout-Directory + +source scaffold + +function files { +cat < /dev/null + unmount_union + echo -n "[ro] " + ( afterfiles_ro ) | check_hierarchy $TOP_LOWER_DIR +} + +# read-write mmap test +function test_rw { + ( files ) | create_hierarchy + mount_union "" $LOWER_DIR0 $LOWER_DIR1=ro + ./progs/mapper -w $MOUNTPOINT/b > /dev/null + echo -n "[rw] " + unmount_union + ( afterfiles_rw ) | check_hierarchy $TOP_LOWER_DIR +} + +test_ro +# skip on jffs2 (doesn't support writeable mappings) +if test "$FS0" != "jffs2" ; then + test_rw +fi + +complete_test diff --git a/t-open-unlink.sh b/t-open-unlink.sh new file mode 100755 index 0000000..b120cc5 --- /dev/null +++ b/t-open-unlink.sh @@ -0,0 +1,48 @@ +#!/bin/sh +# TEST: Branches: b0,b1=ro and b0,b1 +# TEST: open(a), unlink(a), recreate(a), unlink(a) +# TEST: at each point verify the contents of file 'a' using the original fd + +source scaffold + +function files { +cat </dev/null + + echo "$LOWER_DIR0/b" | diff /mnt/unionfs/b - + echo "$LOWER_DIR1/c" | diff /mnt/unionfs/c - + echo "$LOWER_DIR2/d" | diff /mnt/unionfs/d - + + echo "$LOWER_DIR0/e" | diff /mnt/unionfs/e - + + unmount_union + + ( files ; afterfiles ) | check_hierarchy $TOP_LOWER_DIR +} + +function copyup { + ( files ; beforefiles_copyup) | create_hierarchy + + mount_union "" $LOWER_DIR0 $LOWER_DIR1=ro + + checktype $MOUNTPOINT/d1/d2/a 'f' + echo "$LOWER_DIR1/d1/d2/a" | diff /mnt/unionfs/d1/d2/a - + echo "New data" > /mnt/unionfs/d1/d2/a + echo "New data" | diff /mnt/unionfs/d1/d2/a - + + unmount_union + + ( files ; afterfiles_copyup ) | check_hierarchy $TOP_LOWER_DIR +} + +if [ -z "$FXNS" ] ; then + FXNS="ro copyup" +fi + +for x in $FXNS +do + $x + echo -n "[$x] " +done + +complete_test diff --git a/t-readdir.sh b/t-readdir.sh new file mode 100755 index 0000000..e021d63 --- /dev/null +++ b/t-readdir.sh @@ -0,0 +1,59 @@ +#!/bin/sh + +# readdir.sh: Author: Tom Young, twyun@twyoung.com, Date: 10/22/05 +# Test for many files and directories at multiple levels. + +# TEST: Branches: b0,b1 +# TEST: Create many files in b0 and b1 then see if all appear in the unionfs. + +source scaffold + +function files { +cat < 92. +if [ -z $1 ]; then + NGEN=100 +else + NGEN=$1 +fi + +# Generate N files +function filesx { + N=$(($NGEN)) + while [ $N -gt 0 ]; do + echo f $LOWER_DIR0/aljlelhkagekhakjdhfkjhakdhfaekcj_$N + N=$(($N-1)) + done +} + +# Generate N files +function filesy { + N=$(($NGEN)) + while [ $N -gt 0 ]; do + echo f $LOWER_DIR1/aljlelhkagekhakjdhfkjhakdhfaekcj_$N + N=$(($N-1)) + done +} + +( files; filesx; filesy ) | create_hierarchy +mount_union "" $LOWER_DIR0 $LOWER_DIR1 +FILES=`ls /mnt/unionfs/* |wc -l` +if [ $FILES != $NGEN ] ; then + echo "There is a discrepancy in the number of files." + echo -n "Unionfs: $FILES" + echo -n "Expected: $NGEN" + exit 1 +fi +( files; filesx; filesy ) | check_hierarchy $TOP_LOWER_DIR + +unmount_union + +complete_test diff --git a/t-rename-matrix.sh b/t-rename-matrix.sh new file mode 100755 index 0000000..ab1ef00 --- /dev/null +++ b/t-rename-matrix.sh @@ -0,0 +1,257 @@ +#!/bin/sh + +# This file contains the most basic version of the rename matrix (see +# docs/rename.txt). It creates all the files/directories on the left most +# branch, and therefore never tests copyup. It is however useful to see the +# most basic operation succeed before diving into more complex scenarios + +source scaffold + +function beforefiles { +cat <none +function file2none { + ./progs/rename $MOUNTPOINT/s.file $MOUNTPOINT/d.none + return $? +} +function afterfiles_file2none { + beforefiles | sed -e "`renamestr $LOWER_DIR0/s.file $LOWER_DIR0/d.none`" +} + +# file->file +function file2file { + ./progs/rename $MOUNTPOINT/s.file $MOUNTPOINT/d.file + return $? +} +function afterfiles_file2file { + beforefiles | grep -v "$LOWER_DIR0/s.file" +} + +# file->dir +function file2dir { + shouldfail ./progs/rename $MOUNTPOINT/s.file $MOUNTPOINT/d.dir + return $? +} +function afterfiles_file2dir { + beforefiles +} + +# file->child +function file2child { + shouldfail ./progs/rename $MOUNTPOINT/s.file $MOUNTPOINT/d.child + return $? +} +function afterfiles_file2child { + beforefiles +} + +# file->wh +function file2wh { + shouldfail ./progs/rename $MOUNTPOINT/s.file $MOUNTPOINT/d.wh + return $? +} +function afterfiles_file2wh { + beforefiles +} + + +# dir->none +function dir2none { + ./progs/rename $MOUNTPOINT/s.dir $MOUNTPOINT/d.none + return $? +} +function afterfiles_dir2none { + beforefiles | sed -e "`renamestr $LOWER_DIR0/s.dir $LOWER_DIR0/d.none`" + echo "f $LOWER_DIR0/d.none/.wh.__dir_opaque" +} + +# dir->file +function dir2file { + shouldfail ./progs/rename $MOUNTPOINT/s.dir $MOUNTPOINT/d.file + return $? +} +function afterfiles_dir2file { + beforefiles +} + +# dir->dir +function dir2dir { + ./progs/rename $MOUNTPOINT/s.dir $MOUNTPOINT/d.dir + return $? +} +function afterfiles_dir2dir { + beforefiles | grep -v "$LOWER_DIR0/s.dir" + echo "f $LOWER_DIR0/d.dir/.wh.__dir_opaque" +} + +# dir->child +function dir2child { + shouldfail ./progs/rename $MOUNTPOINT/s.dir $MOUNTPOINT/d.child + return $? +} +function afterfiles_dir2child { + beforefiles +} + +# dir->wh +function dir2wh { + ./progs/rename $MOUNTPOINT/s.dir $MOUNTPOINT/d.wh + return $? +} +function afterfiles_dir2wh { + beforefiles | grep -v "$LOWER_DIR0/s.dir" | grep -v "$LOWER_DIR0/d.wh/.wh.foo" + echo "f $LOWER_DIR0/d.wh/.wh.__dir_opaque" +} + + +# child->none +function child2none { + ./progs/rename $MOUNTPOINT/s.child $MOUNTPOINT/d.none + return $? +} +function afterfiles_child2none { + beforefiles | grep -v "$LOWER_DIR0/s.child" + echo "d $LOWER_DIR0/d.none" + echo "f $LOWER_DIR0/d.none/foo" + echo "f $LOWER_DIR0/d.none/.wh.__dir_opaque" +} + +# child->file +function child2file { + shouldfail ./progs/rename $MOUNTPOINT/s.child $MOUNTPOINT/d.file + return $? +} +function afterfiles_child2file { + beforefiles +} + +# child->dir +function child2dir { + ./progs/rename $MOUNTPOINT/s.child $MOUNTPOINT/d.dir + return $? +} +function afterfiles_child2dir { + beforefiles | grep -v "$LOWER_DIR0/s.child" + echo "f $LOWER_DIR0/d.dir/foo" + echo "f $LOWER_DIR0/d.dir/.wh.__dir_opaque" +} + +# child->child +function child2child { + shouldfail ./progs/rename $MOUNTPOINT/s.child $MOUNTPOINT/d.child + return $? +} +function afterfiles_child2child { + beforefiles +} + +# child->wh +function child2wh { + ./progs/rename $MOUNTPOINT/s.child $MOUNTPOINT/d.wh + return $? +} +function afterfiles_child2wh { + beforefiles | grep -v "$LOWER_DIR0/s.child" | grep -v "$LOWER_DIR0/d.wh/.wh.foo" + echo "f $LOWER_DIR0/d.wh/foo" + echo "f $LOWER_DIR0/d.wh/.wh.__dir_opaque" +} + + +# wh->none +function wh2none { + ./progs/rename $MOUNTPOINT/s.wh $MOUNTPOINT/d.none + return $? +} +function afterfiles_wh2none { + beforefiles | sed -e 's/s\.wh/d\.none/' + echo "f $LOWER_DIR0/d.none/.wh.__dir_opaque" +} + +# wh->file +function wh2file { + shouldfail ./progs/rename $MOUNTPOINT/s.wh $MOUNTPOINT/d.file + return $? +} +function afterfiles_wh2file { + beforefiles +} + +# wh->dir +function wh2dir { + ./progs/rename $MOUNTPOINT/s.wh $MOUNTPOINT/d.dir + return $? +} +function afterfiles_wh2dir { + beforefiles | grep -v "$LOWER_DIR0/s.wh" + echo "f $LOWER_DIR0/d.dir/.wh.foo" + echo "f $LOWER_DIR0/d.dir/.wh.__dir_opaque" +} + +# wh->child +function wh2child { + shouldfail ./progs/rename $MOUNTPOINT/s.wh $MOUNTPOINT/d.child + return $? +} +function afterfiles_wh2child { + beforefiles +} + +# wh->wh +function wh2wh { + ./progs/rename $MOUNTPOINT/s.wh $MOUNTPOINT/d.wh + return $? +} +function afterfiles_wh2wh { + beforefiles | grep -v "$LOWER_DIR0/s.wh" + echo "f $LOWER_DIR0/d.wh/.wh.__dir_opaque" +} + +SRC="file dir child wh" +DST="none file dir child wh" + +for s in $SRC +do + for d in $DST + do + beforefiles | create_hierarchy + mount_union "" $LOWER_DIR0 + + ${s}2${d} + + afterfiles_${s}2${d} | check_hierarchy $TOP_LOWER_DIR + unmount_union + + echo -n "[${s} ${d}] " + done +done + + +complete_test + diff --git a/t-rename-whiteout.sh b/t-rename-whiteout.sh new file mode 100755 index 0000000..19cfc63 --- /dev/null +++ b/t-rename-whiteout.sh @@ -0,0 +1,235 @@ +#!/bin/sh + +# TEST: Branches: b0,b1,b2 and b0,b1=ro,b2=ro +# TEST: rename(S, D) where S and D are in the following configurations +# TEST: +--------+----------+ +# TEST: |b0|b1|b2| filename | +# TEST: |--|--|--|----------| +# TEST: | | | S| rA | +# TEST: | | | | | +# TEST: |--|--|--|----------| +# TEST: | | S| S| rB | +# TEST: | | | | | +# TEST: |--|--|--|----------| +# TEST: | S| S| | rC | +# TEST: | | | | | +# TEST: |--|--|--|----------| +# TEST: | S| S| | rD | +# TEST: | | | D| | +# TEST: |--|--|--|----------| +# TEST: | | S| S| rE | +# TEST: | | | D| | +# TEST: |--|--|--|----------| +# TEST: | | | S| rF | +# TEST: | | D| D| | +# TEST: |--|--|--|----------| +# TEST: | | | S| rG | +# TEST: | D| | D| | +# TEST: |--|--|--|----------| +# TEST: | S| S| S| rH | +# TEST: | D| D| D| | +# TEST: +--------+----------+ +# TEST: | S|iS| S| rI | +# TEST: | D| D| D| | +# TEST: +--------+----------+ +# TEST: | | |iS| rJ | +# TEST: | | | | | +# TEST: +--------+----------+ + +source scaffold + +function files { +cat <&2 + return 1 + fi + + return 0 +} + +do_link $MOUNTPOINT/a $MOUNTPOINT/d +do_link $MOUNTPOINT/b $MOUNTPOINT/d5/e +do_link $MOUNTPOINT/a $MOUNTPOINT/d1/d2/d3/d4/c + +unmount_union +( directories ; afterfiles_rw ) | check_hierarchy $TOP_LOWER_DIR + +( directories ; beforefiles) | create_hierarchy + +mount_union "" $LOWER_DIR0 $LOWER_DIR1=ro + + +ln --symbolic $MOUNTPOINT/a $MOUNTPOINT/d +ln --symbolic $MOUNTPOINT/b $MOUNTPOINT/d5/e +ln --symbolic $MOUNTPOINT/a $MOUNTPOINT/d1/d2/d3/d4/c + +unmount_union +( directories ; afterfiles_ro ) | check_hierarchy $TOP_LOWER_DIR + + +complete_test diff --git a/t-truncate-all.sh b/t-truncate-all.sh new file mode 100755 index 0000000..e09399c --- /dev/null +++ b/t-truncate-all.sh @@ -0,0 +1,118 @@ +#!/bin/sh + +# TEST: Branches b0,b1 and b0,b1=ro +# TEST: truncate(F) +# TEST: F on b1 to zero +# TEST: F on b1 to a non-zero size less than the original +# TEST: F on b1 to a larger size than the original +# TEST: F on b0 and b1 to zero +# TEST: WHERE x = 0, 0 < x < size(f), and size(f) < x +# TEST: Using the following branch configurations +# TEST: Branches b0,b1 and b0,b1=ro +# TEST: F on b0 +# TEST: F on b1 +# TEST: F on b2 +# TEST: F on b0,b1 +# TEST: Branches b0,b1,b2 +# TEST: F on b0,b1,b2 +# TEST: F on b0,b1(immutable),b2 +# TEST: Branches b0,b1=ro,b2 +# TEST: F on b0,b1,b2 + +source scaffold + +# initial directories +function directories { +cat </dev/null +dd if=/dev/zero of=$LOWER_DIR0/b bs=4000 count=2 2>/dev/null +dd if=/dev/zero of=$LOWER_DIR0/c bs=4000 count=2 2>/dev/null + +dd if=/dev/zero of=$LOWER_DIR0/d bs=4000 count=2 2>/dev/null +dd if=/dev/zero of=$LOWER_DIR1/d bs=4000 count=2 2>/dev/null +dd if=/dev/zero of=$LOWER_DIR2/d bs=4000 count=2 2>/dev/null + +dd if=/dev/zero of=$LOWER_DIR2/d1/d2/d3/d4/e bs=4000 count=2 2>/dev/null + +dd if=/dev/zero of=$LOWER_DIR2/d1/d2/d3/d4/f bs=4000 count=2 2>/dev/null + +if havechattr $LOWER_DIR2 ; then + CHATTR=1 + chattr +i $LOWER_DIR2/d1/d2/d3/d4/f +fi + +# mount unionfs +mount_union "" $LOWER_DIR0 $LOWER_DIR1 $LOWER_DIR2 + +./progs/truncate -f $MOUNTPOINT/a 0 + +./progs/truncate -f $MOUNTPOINT/b 5000 + +./progs/truncate -f $MOUNTPOINT/c 10000 + +./progs/truncate -f $MOUNTPOINT/d 10000 + +./progs/truncate -f $MOUNTPOINT/d1/d2/d3/d4/e 10000 + +if [ ! -z "$CHATTR" ] ; then + shouldfail ./truncate -f $MOUNTPOINT/d1/d2/d3/d4/f 10000 +fi + +unmount_union + + + +#### do same tests with mix of ro branches + +(directories) | create_hierarchy + +dd if=/dev/zero of=$LOWER_DIR0/a bs=4000 count=2 2>/dev/null +dd if=/dev/zero of=$LOWER_DIR0/b bs=4000 count=2 2>/dev/null +dd if=/dev/zero of=$LOWER_DIR0/c bs=4000 count=2 2>/dev/null + +dd if=/dev/zero of=$LOWER_DIR0/d bs=4000 count=2 2>/dev/null +dd if=/dev/zero of=$LOWER_DIR1/d bs=4000 count=2 2>/dev/null +dd if=/dev/zero of=$LOWER_DIR2/d bs=4000 count=2 2>/dev/null + +dd if=/dev/zero of=$LOWER_DIR2/d1/d2/d3/d4/e bs=4000 count=2 2>/dev/null + +dd if=/dev/zero of=$LOWER_DIR2/d1/d2/d3/d4/f bs=4000 count=2 2>/dev/null +if [ ! -z "$CHATTR" ] ; then + chattr +i $LOWER_DIR2/d1/d2/d3/d4/f +fi + + +mount_union "" $LOWER_DIR0 $LOWER_DIR1=ro $LOWER_DIR2=ro + +./progs/truncate -f $MOUNTPOINT/a 0 + +./progs/truncate -f $MOUNTPOINT/b 5000 + +./progs/truncate -f $MOUNTPOINT/c 10000 + +./progs/truncate -f $MOUNTPOINT/d 10000 + +./progs/truncate -f $MOUNTPOINT/d1/d2/d3/d4/e 10000 + +if [ ! -z "$CHATTR" ] ; then + shouldfail ./progs/truncate -f $MOUNTPOINT/d1/d2/d3/d4/f 10000 +fi + +unmount_union + +complete_test diff --git a/t-unlink-whiteout.sh b/t-unlink-whiteout.sh new file mode 100755 index 0000000..d12ea2b --- /dev/null +++ b/t-unlink-whiteout.sh @@ -0,0 +1,377 @@ +#!/bin/sh + +# TEST: Branches: b0,b1=ro and b0,b1 +# unlink_whiteout(F) where F is a file in b0 or b1 or both +# F is in b0, b1 +# F is in b0 +# F is in b1 +# +# TEST: Branches: b0,b1=ro, and b0,b1 +# g is a symlink in b1 +# +# TEST: after unlinking create same files again +# 1. create them before unmount +# 2. create them after unmount and then mount + +source scaffold + +function files { +cat <