Imported regression suite from CVS
authorJosef 'Jeff' Sipek <jsipek@cs.sunysb.edu>
Fri, 29 Jun 2007 06:54:49 +0000 (02:54 -0400)
committerJosef 'Jeff' Sipek <jsipek@cs.sunysb.edu>
Fri, 29 Jun 2007 06:54:49 +0000 (02:54 -0400)
Signed-off-by: Josef 'Jeff' Sipek <jsipek@cs.sunysb.edu>
50 files changed:
.gitignore [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
brm.conf [new file with mode: 0644]
cleanup [new file with mode: 0755]
default.conf [new file with mode: 0644]
jffs2-empty.img [new file with mode: 0644]
jffs2.conf [new file with mode: 0644]
progs/.cvsignore [new file with mode: 0644]
progs/Makefile [new file with mode: 0644]
progs/bug418.c [new file with mode: 0644]
progs/creat-open.c [new file with mode: 0644]
progs/flock-copyup.c [new file with mode: 0644]
progs/fsync.c [new file with mode: 0644]
progs/mapper.c [new file with mode: 0644]
progs/open-unlink.c [new file with mode: 0644]
progs/queryfile.c [new file with mode: 0644]
progs/rename.c [new file with mode: 0644]
progs/rmdircheckinode.c [new file with mode: 0644]
progs/truncate.c [new file with mode: 0644]
run-all-tests [new file with mode: 0755]
run-tests [new file with mode: 0755]
scaffold [new file with mode: 0644]
showplans [new file with mode: 0755]
t-branchman.sh [new file with mode: 0755]
t-chmod.sh [new file with mode: 0755]
t-creat-open.sh [new file with mode: 0755]
t-create.sh [new file with mode: 0755]
t-flock.sh [new file with mode: 0755]
t-fsync.sh [new file with mode: 0755]
t-incgen.sh [new file with mode: 0755]
t-ioctl.sh [new file with mode: 0755]
t-link-rename.sh [new file with mode: 0755]
t-link.sh [new file with mode: 0755]
t-lookup-opaque.sh [new file with mode: 0755]
t-lookup.sh [new file with mode: 0755]
t-mkdir.sh [new file with mode: 0755]
t-mknod.sh [new file with mode: 0755]
t-mmap.sh [new file with mode: 0755]
t-open-unlink.sh [new file with mode: 0755]
t-open.sh [new file with mode: 0755]
t-readdir.sh [new file with mode: 0755]
t-rename-matrix.sh [new file with mode: 0755]
t-rename-whiteout.sh [new file with mode: 0755]
t-symlink.sh [new file with mode: 0755]
t-truncate-all.sh [new file with mode: 0755]
t-unlink-whiteout.sh [new file with mode: 0755]
testplan [new file with mode: 0644]
thor.conf [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..fe04362
--- /dev/null
@@ -0,0 +1,2 @@
+complete.sh
+start.sh
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..37faf46
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,435 @@
+2007-06-27  Erez Zadok  <ezk@shekel.local>
+
+       * run-tests: support ext4 (needs an ext3 mkfs and currently the f/s
+       is called "extdev").
+       Minor typo fix.
+
+2007-06-17  Erez Zadok  <ezk@cs.sunysb.edu>
+
+       * t-fsync.sh: fix comment at top of test script.
+
+2007-06-10  Erez Zadok  <ezk@cs.sunysb.edu>
+
+       * run-all-tests: export MYFS=jffs2 when running jffs2 tests.
+
+2007-06-05  Erez Zadok  <ezk@cs.sunysb.edu>
+
+       * 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  <ezk@cs.sunysb.edu>
+
+       * *.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  <ezk@cs.sunysb.edu>
+
+       * 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  <ezk@cs.sunysb.edu>
+
+       * README: documentation updates.
+
+       * run-tests: support NFS v2, v3, and v4 mounts.
+
+2007-05-23  Erez Zadok  <ezk@cs.sunysb.edu>
+
+       * *.conf: spell check.
+
+2007-05-23  Josef "Jeff" Sipek  <jsipek@fsl.cs.sunysb.edu>
+
+       * 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  <ezk@cs.sunysb.edu>
+
+       * run-tests: delete temp files created in /tmp once done.
+
+2007-05-23  Josef "Jeff" Sipek  <jsipek@fsl.cs.sunysb.edu>
+
+       * run-tests: Use /dev/.static/dev/loop* as loop devices
+
+       * thor.conf: Disable console echo & update device file names
+
+2007-05-23  Erez Zadok  <ezk@cs.sunysb.edu>
+
+       * 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  <ezk@cs.sunysb.edu>
+
+       * 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  <ezk@cs.sunysb.edu>
+
+       * {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  <ezk@cs.sunysb.edu>
+
+       * t-open-unlink.sh, t-creat-open.sh, t-branchman.sh: minor
+       spelling fixes in comments.
+
+2007-05-05  Erez Zadok  <ezk@cs.sunysb.edu>
+
+       * default.conf: default tests should use the safer /dev/loopN
+       (avoid potential trashing of actual file system devices).
+
+2007-03-17  Josef "Jeff" Sipek  <jsipek@fsl.cs.sunysb.edu>
+
+       * thor.conf: Disable incgen test
+
+2007-03-15  Erez Zadok  <ezk@cs.sunysb.edu>
+
+       * 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  <jsipek@fsl.cs.sunysb.edu>
+
+       * run-tests, default.conf, thor.conf: Allow for highlighting of
+       command output via ANSI color escape codes
+
+2007-03-08  Josef "Jeff" Sipek  <jsipek@fsl.cs.sunysb.edu>
+
+       * thor.conf: re-enable chmod.sh
+
+2007-03-04  Josef "Jeff" Sipek  <jsipek@fsl.cs.sunysb.edu>
+
+       * run-tests: Accept /dev/loop* as a device name, and create a 100MB
+       sparse file for each loop device
+
+2007-03-04  Erez Zadok  <ezk@cs.sunysb.edu>
+
+       * 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  <ezk@cs.sunysb.edu>
+
+       * 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-<NAME>.sh.
+
+2006-11-04  Josef "Jeff" Sipek  <jsipek@fsl.cs.sunysb.edu>
+
+       * rename-501.sh: It just fails all the time...remove it for the time
+       being
+
+2006-05-29  Josef "Jeff" Sipek  <jsipek@fsl.cs.sunysb.edu>
+
+       * rename-501.sh: Updates by Junjiro to test additional cases
+
+2006-05-29  Josef "Jeff" Sipek  <jsipek@fsl.cs.sunysb.edu>
+
+       * rename-matrix.sh: forgot to remove a debug statement
+
+2006-05-29  Josef "Jeff" Sipek  <jsipek@fsl.cs.sunysb.edu>
+
+       * rename-matrix.sh: added checks for the rename matrix (see
+       docs/rename.sh)
+
+2006-03-03  David P. Quigley  <dquigley@fsl.cs.sunysb.edu>
+
+       * 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  <dquigley@fsl.cs.sunysb.edu>
+
+       * 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  <jsipek@fsl.cs.sunysb.edu>
+
+       * branchman.sh: Updated regression test to work with the new nfsro
+       flag
+
+2005-12-29  David P. Quigley  <dquigley@fsl.cs.sunysb.edu>
+
+       * lookup-opaque.sh: Changed script to use -d instead of --home-dir
+
+2005-12-28  Josef "Jeff" Sipek  <jsipek@fsl.cs.sunysb.edu>
+
+       * 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  <jsipek@fsl.cs.sunysb.edu>
+
+       * 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  <cwright@fsl.cs.sunysb.edu>
+
+       * 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  <dquigley@fsl.cs.sunysb.edu>
+
+       * 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  <cwright@fsl.cs.sunysb.edu>
+
+       * branchman.sh: Functionize, and add a query check.
+
+2005-08-29  Charles P. Wright  <cwright@fsl.cs.sunysb.edu>
+
+       * Detect working chattr so we can run tests on reiser.
+
+       * fsync.sh: Run fsync on a file.
+
+2005-08-24  Charles P. Wright  <cwright@fsl.cs.sunysb.edu>
+
+       * scaffold (checkperms): Permissions checking function.
+       * create.sh (BUG383): Test for BUG383.
+
+2005-08-23  Charles P. Wright  <cwright@fsl.cs.sunysb.edu>
+
+       * rename-all.sh: Test for BUG388.
+
+2005-08-18  Charles P. Wright  <cwright@fsl.cs.sunysb.edu>
+
+       * 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  <cwright@fsl.cs.sunysb.edu>
+
+       * 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  <cwright@fsl.cs.sunysb.edu>
+
+       * scaffold: Make sure unmount is executed for each mount.
+
+2005-08-15  Charles P. Wright  <cwright@fsl.cs.sunysb.edu>
+
+       * flock.sh: Test for bug 360.
+
+2005-08-12  Arun M. Krishnakumar  <arunmk@fsl.cs.sunysb.edu>
+
+       * all files : now the files can be used for testing
+       unionfs over NFS as well
+
+2005-08-11  Charles P. Wright  <cwright@fsl.cs.sunysb.edu>
+
+       * branchman.sh: Test to trigger BUG 370.
+
+2005-08-10  Arun M. Krishnakumar  <arunmk@fsl.cs.sunysb.edu>
+
+       * 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 <cwright@fsl.cs.sunysb.edu>
+
+       * branchman.sh: Branch removal using new unionctl remount.
+
+2005-08-04  Charles P. Wright  <cwright@fsl.cs.sunysb.edu>
+
+       * branchman.sh: Test branch removal.
+
+       * truncate-all.sh: Update mount options.
+
+2005-08-03  Charles P. Wright  <cwright@fsl.cs.sunysb.edu>
+
+       * branchman.sh: Test of adding a branch.
+
+2005-08-01  Arun M. Krishnakumar  <arunmk@fsl.cs.sunysb.edu>
+
+       * 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  <cwright@fsl.cs.sunysb.edu>
+
+       * open.sh: Test directory-none-directory open.
+
+       * incgen.sh: Regression test for generation number increment.
+
+2005-07-26  Charles P. Wright  <cwright@fsl.cs.sunysb.edu>
+
+       * unlink-first.sh: No longer needed.
+
+2005-07-22  Charles P. Wright  <cwright@fsl.cs.sunysb.edu>
+
+       * 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  <arunmk@fsl.cs.sunysb.edu>
+
+       * open-unlink.sh : fixed some small issues. this must
+       completely knockout bug #296
+
+2005-07-20  Arun M. Krishnakumar  <arunmk@fsl.cs.sunysb.edu>
+
+       * rmdir-all.sh : updated the test cases to use dir_opaque
+
+2005-07-20  Charles P. Wright  <cwright@fsl.cs.sunysb.edu>
+
+       * 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  <arunmk@fsl.cs.sunysb.edu>
+
+       * mkdir.sh : updated the mkdir to use the dir_opaque
+
+2005-07-19  Charles P. Wright  <cwright@fsl.cs.sunysb.edu>
+
+       * scaffold: Add symlink support.
+       * unlink-whiteout.sh: Add symlink tests (currently broken).
+
+2005-03-02  David P. Quigley  <dquigley@fsl.cs.sunysb.edu>
+
+       * link.sh: Fixed more cases that were not correct due to new unionfs_link
+       code
+
+2005-02-18  David P. Quigley  <dquigley@fsl.cs.sunysb.edu>
+
+       * 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  <cwright@fsl.cs.sunysb.edu>
+
+       * link.sh: Test two directories on the same ro branch.
+
+2004-12-27  Charles P. Wright  <cwright@fsl.cs.sunysb.edu>
+
+       * chmod.sh: Reproduce bug 142.
+
+2004-08-25  Mohammad Nayyer Zubair  <zubair@filer.fsl.cs.sunysb.edu>
+
+       * create.sh: tested unionfs_create()
+
+2004-08-24  Charles P. Wright <cwright@fsl.cs.sunysb.edu>
+
+       * 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 <cwright@fsl.cs.sunysb.edu>
+
+       * rename-whiteout.sh: Test rename-whiteout.
+
+2004-08-18  Mohammad Nayyer Zubair  <zubair@filer.fsl.cs.sunysb.edu>
+
+       * link.sh: script to test all cases of unionfs_link()
+
+2004-08-17  Charles P. Wright <cwright@fsl.cs.sunysb.edu>
+
+       * 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 (file)
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 (file)
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 <http://unionfs.filesystems.org/>
diff --git a/brm.conf b/brm.conf
new file mode 100644 (file)
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 (executable)
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 (file)
index 0000000..0863ebd
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..03d4c4f
--- /dev/null
@@ -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 (file)
index 0000000..40b68a0
--- /dev/null
@@ -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 (file)
index 0000000..298c005
--- /dev/null
@@ -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 (file)
index 0000000..e0355db
--- /dev/null
@@ -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 <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+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 (file)
index 0000000..9f24515
--- /dev/null
@@ -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 <unistd.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <errno.h>
+
+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 (file)
index 0000000..daf9fb4
--- /dev/null
@@ -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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/fcntl.h>
+
+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 (file)
index 0000000..5756e51
--- /dev/null
@@ -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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/fcntl.h>
+
+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 (file)
index 0000000..c18e157
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 1997-2007 Erez Zadok <ezk@cs.stonybrook.edu>
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#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 (file)
index 0000000..c8b1388
--- /dev/null
@@ -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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+
+#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 (file)
index 0000000..ac09a1a
--- /dev/null
@@ -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 <sys/select.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+
+#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 (file)
index 0000000..5639f89
--- /dev/null
@@ -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 <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+
+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 (file)
index 0000000..6b28d99
--- /dev/null
@@ -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 <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+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 (file)
index 0000000..ff543db
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+
+
+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 (executable)
index 0000000..e06371d
--- /dev/null
@@ -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 (executable)
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 (file)
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 (executable)
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 (executable)
index 0000000..ea3ce33
--- /dev/null
@@ -0,0 +1,173 @@
+#!/bin/sh
+
+# TEST: Branches: b0
+# TEST:  add b1 before b0
+# TEST:  add b1 after b0
+
+source scaffold
+
+function files {
+cat <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR1
+d $LOWER_DIR2
+d $LOWER_DIR3
+d $LOWER_DIR/b4
+FILES
+}
+
+function beforefiles {
+cat <<FILES
+f $LOWER_DIR0/b0-only
+f $LOWER_DIR0/a
+f $LOWER_DIR1/a
+f $LOWER_DIR2/a
+f $LOWER_DIR3/a
+f $LOWER_DIR/b4/a
+FILES
+}
+
+function afterfiles {
+       beforefiles
+}
+
+function add_before {
+       ( files ; beforefiles) | create_hierarchy
+       mount_union "" $LOWER_DIR0
+
+       checktype $MOUNTPOINT/a 'f'
+       echo "$LOWER_DIR0/a" | diff $MOUNTPOINT/a -
+       unionctl /mnt/unionfs --add --before $LOWER_DIR0 $LOWER_DIR1
+       echo "$LOWER_DIR1/a" | diff $MOUNTPOINT/a -
+
+       unmount_union
+       ( files ; afterfiles )  | check_hierarchy $TOP_LOWER_DIR
+}
+
+function add_after {
+       ( files ; beforefiles) | create_hierarchy
+       mount_union "" $LOWER_DIR0
+
+       checktype $MOUNTPOINT/a 'f'
+       echo "$LOWER_DIR0/a" | diff $MOUNTPOINT/a -
+       unionctl /mnt/unionfs --add --after $LOWER_DIR0 $LOWER_DIR1
+       echo "$LOWER_DIR0/a" | diff $MOUNTPOINT/a -
+
+       unmount_union
+       ( files ; afterfiles )  | check_hierarchy $TOP_LOWER_DIR
+}
+
+function add_multiple {
+       ( files ; beforefiles) | create_hierarchy
+       mount_union "" $LOWER_DIR0
+
+       checktype $MOUNTPOINT/a 'f'
+       echo "$LOWER_DIR0/a" | diff $MOUNTPOINT/a -
+       for ((i = 1; i <= 4; i++)) ; do
+               unionctl /mnt/unionfs --add $LOWER_DIR/b$i
+               echo "$LOWER_DIR/b$i/a" | diff $MOUNTPOINT/a -
+       done
+
+       unmount_union
+       ( files ; afterfiles )  | check_hierarchy $TOP_LOWER_DIR
+}
+
+function remove {
+       ( files ; beforefiles) | create_hierarchy
+
+       mount_union "" $LOWER_DIR1 $LOWER_DIR0
+
+       echo "$LOWER_DIR1/a" | diff $MOUNTPOINT/a -
+       unionctl /mnt/unionfs --remove $LOWER_DIR1
+       echo "$LOWER_DIR0/a" | diff $MOUNTPOINT/a -
+
+       unmount_union
+
+       ( files ; afterfiles )  | check_hierarchy $TOP_LOWER_DIR
+}
+
+function remove_multiple {
+       ( files ; beforefiles) | create_hierarchy
+
+       mount_union "" $LOWER_DIR0
+
+       echo "$LOWER_DIR0/a" | diff $MOUNTPOINT/a -
+       for ((i = 1; i <= 4; i++)) ; do
+               unionctl /mnt/unionfs --add $LOWER_DIR/b$i
+               echo "$LOWER_DIR/b$i/a" | diff $MOUNTPOINT/a -
+       done
+
+       for ((i = 1; i < 4; i++)) ; do
+               unionctl /mnt/unionfs --remove $LOWER_DIR/b$i
+               echo "$LOWER_DIR/b4/a" | diff $MOUNTPOINT/a -
+       done
+       unionctl /mnt/unionfs --remove $LOWER_DIR/b4
+       echo "$LOWER_DIR0/a" | diff $MOUNTPOINT/a -
+
+       unmount_union
+       ( files ; afterfiles )  | check_hierarchy $TOP_LOWER_DIR
+}
+
+function BUG370 {
+       ( files ; beforefiles) | create_hierarchy
+       #Why was this done?
+       mount_union "" $LOWER_DIR0=rw $LOWER_DIR0=ro
+
+       #None of this will work since we don't allow duplicates
+       unionctl $MOUNTPOINT --mode $LOWER_DIR0 ro
+       unionctl $MOUNTPOINT --mode $LOWER_DIR0 rw
+       unionctl $MOUNTPOINT --add --mode rw $LOWER_DIR1
+       unionctl $MOUNTPOINT --mode $LOWER_DIR0 ro
+       unionctl $MOUNTPOINT --add --mode rw $LOWER_DIR1
+       unionctl $MOUNTPOINT --remove $LOWER_DIR1
+       unionctl $MOUNTPOINT --mode $LOWER_DIR0 ro
+       unionctl $MOUNTPOINT --add --mode rw $LOWER_DIR1
+
+       unmount_union
+       ( files ; afterfiles )  | check_hierarchy $TOP_LOWER_DIR
+}
+
+function query {
+       ( files ; beforefiles) | create_hierarchy
+       mount_union "" $LOWER_DIR/b*
+
+cat <<TMP >/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 >/tmp/$$
+$MOUNTPOINT/b0-only    $LOWER_DIR0 (rw-)
+TMP
+       unionctl $MOUNTPOINT/b0-only --query | diff - /tmp/$$
+cat <<TMP >/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 (executable)
index 0000000..de7a7cb
--- /dev/null
@@ -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 <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+f $LOWER_DIR0/a
+f $LOWER_DIR0/b
+d $LOWER_DIR0/c
+d $LOWER_DIR0/d
+
+d $LOWER_DIR1
+f $LOWER_DIR1/a
+d $LOWER_DIR1/c
+f $LOWER_DIR1/e
+d $LOWER_DIR1/f
+
+d $LOWER_DIR2
+d $LOWER_DIR3
+
+FILES
+}
+
+# initial set of files
+function afterfiles_ro {
+cat <<FILES
+f $LOWER_DIR0/e
+d $LOWER_DIR0/f
+FILES
+}
+
+function do_chmod {
+       TARGET=$1
+
+       chmod 700 $TARGET || return $?
+
+       if [ `find $TARGET -printf '%m'` != "700" ] ; then
+               echo "Permissions for $TARGET are not 700" 1>&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 (executable)
index 0000000..614a9e3
--- /dev/null
@@ -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 <<FILES
+d $LOWER_DIR0
+d $LOWER_DIR0/b
+FILES
+}
+
+function afterfiles {
+cat <<FILES
+d $LOWER_DIR0
+d $LOWER_DIR0/b
+f $LOWER_DIR0/b/creat-open
+FILES
+}
+
+( files ) | create_hierarchy
+
+EXPECTED_SIZE=`ls -l progs/creat-open | awk '{print $5}'`
+cp progs/creat-open $LOWER_DIR0/b/
+
+mount_union "" $LOWER_DIR0/b
+
+RET=0
+$MOUNTPOINT/creat-open || RET="$?"
+
+if [ $RET -ne 26 ]; then
+       echo "creat(2) returned $RET, expected 26 (ETXTBSY)"
+fi
+
+ACTUAL_SIZE=`ls -l $MOUNTPOINT/creat-open | awk '{print $5}'`
+if [ $EXPECTED_SIZE -ne $ACTUAL_SIZE ]; then
+       echo "File size: $ACTUAL_SIZE, expected $EXPECTED_SIZE"
+fi
+
+unmount_union
+
+( afterfiles ) | check_hierarchy $LOWER_DIR0
+
+complete_test
diff --git a/t-create.sh b/t-create.sh
new file mode 100755 (executable)
index 0000000..f28e743
--- /dev/null
@@ -0,0 +1,163 @@
+#!/bin/sh
+
+# TEST: Branches b0,b1,b2 and b0,b1=ro,b2=ro
+# TEST: mkdir A
+# TEST:  Where A is in the same branch
+# TEST:  Where A already exists as a whiteout on the same branch
+# TEST:  Where A already exists as a whiteout on the same branch and there are
+#        pre-existing entries to the right
+#
+# TEST:  Where A is on a RO branch
+# TEST:  Where A exists as a whiteout on a RO branch
+# TEST:  Where A already exists as a whiteout on the same branch and there are
+#        pre-existing entries to the right
+
+
+source scaffold
+
+# initial directories
+function directories {
+cat <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR0/d1
+d $LOWER_DIR0/d1/d2
+d $LOWER_DIR0/d6
+d $LOWER_DIR1
+d $LOWER_DIR1/d5
+d $LOWER_DIR1/d1
+d $LOWER_DIR1/d1/x
+d $LOWER_DIR1/d1/d2
+d $LOWER_DIR1/d1/d2/d3
+d $LOWER_DIR2
+d $LOWER_DIR2/d5
+d $LOWER_DIR2/d1
+d $LOWER_DIR2/d1/d2
+d $LOWER_DIR2/d1/d2/d3
+f $LOWER_DIR2/d1/d2/d3/a
+f $LOWER_DIR2/d1/d2/d3/b
+f $LOWER_DIR2/d1/d2/d3/c
+d $LOWER_DIR2/d1/d2/d3/d4
+d $LOWER_DIR3
+
+FILES
+}
+
+# initial set of files
+function beforefiles {
+cat <<FILES
+
+f $LOWER_DIR0/d1/.wh.x
+
+f $LOWER_DIR2/d1/d2/d3/d4/.wh.d
+
+FILES
+}
+
+function beforefiles_383 {
+cat <<FILES
+f $LOWER_DIR1/y
+FILES
+}
+function afterfiles_383 {
+cat <<FILES
+f $LOWER_DIR0/y
+f $LOWER_DIR1/y
+FILES
+}
+
+
+function afterfiles_rw {
+cat <<FILES
+f $LOWER_DIR0/y
+
+f $LOWER_DIR0/d1/x
+
+f $LOWER_DIR2/d1/d2/d3/d4/d
+
+FILES
+}
+
+
+function afterfiles_ro {
+cat <<FILES
+f $LOWER_DIR0/y
+
+f $LOWER_DIR0/d1/x
+
+d $LOWER_DIR0/d1/d2/d3
+d $LOWER_DIR0/d1/d2/d3/d4
+f $LOWER_DIR0/d1/d2/d3/d4/d
+f $LOWER_DIR2/d1/d2/d3/d4/.wh.d
+
+FILES
+}
+
+function rw {
+       ( directories ; beforefiles) | create_hierarchy
+
+       mount_union "" $LOWER_DIR0 $LOWER_DIR1 $LOWER_DIR2
+
+       touch $MOUNTPOINT/y
+       checktype $MOUNTPOINT/y 'f'
+       touch $MOUNTPOINT/d1/x
+       checktype $MOUNTPOINT/d1/x 'f'
+       touch $MOUNTPOINT/d1/d2/d3/d4/d
+       checktype $MOUNTPOINT/d1/d2/d3/d4/d 'f'
+
+       unmount_union
+       ( directories ; afterfiles_rw )  | check_hierarchy $TOP_LOWER_DIR
+}
+
+
+function ro {
+       ( directories ; beforefiles) | create_hierarchy
+
+       mount_union "" $LOWER_DIR0 $LOWER_DIR1=ro $LOWER_DIR2=ro
+
+       touch $MOUNTPOINT/y
+       checktype $MOUNTPOINT/y 'f'
+       touch $MOUNTPOINT/d1/x
+       checktype $MOUNTPOINT/d1/x 'f'
+       touch $MOUNTPOINT/d1/d2/d3/d4/d
+       checktype $MOUNTPOINT/d1/d2/d3/d4/d 'f'
+
+       unmount_union
+       ( directories ; afterfiles_ro )  | check_hierarchy $TOP_LOWER_DIR
+}
+
+function BUG383 {
+       ( directories ; beforefiles_383 ) | create_hierarchy
+
+       mount_union "" $LOWER_DIR0 $LOWER_DIR1=ro $LOWER_DIR2=ro
+
+       local SM=`umask`
+       umask 022
+       checktype $MOUNTPOINT/y 'f'
+       checkperms $MOUNTPOINT/y 644
+       chmod 421 $MOUNTPOINT/y
+       checkperms $MOUNTPOINT/y 421
+       rm -f $MOUNTPOINT/y
+       checktype $MOUNTPOINT/y '-'
+       umask 026
+       touch $MOUNTPOINT/y
+       checktype $MOUNTPOINT/y 'f'
+       checkperms $MOUNTPOINT/y 640
+
+       umask $SM
+
+       unmount_union
+       ( directories ; afterfiles_383 )  | check_hierarchy $TOP_LOWER_DIR
+}
+
+if [ -z "$FXNS" ] ; then
+       FXNS="rw ro BUG383"
+fi
+
+for x in $FXNS
+do
+       $x
+       echo -n "[$x] "
+done
+
+complete_test
diff --git a/t-flock.sh b/t-flock.sh
new file mode 100755 (executable)
index 0000000..6125145
--- /dev/null
@@ -0,0 +1,52 @@
+#!/bin/sh
+# TEST: Branches: b0,b1=ro and b0,b1
+# TEST: flock(F_WRLCK) a file on b1
+
+source scaffold
+
+function files {
+cat <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR1
+f $LOWER_DIR1/a
+FILES
+}
+
+function afterfiles_ro {
+cat <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+f $LOWER_DIR0/a
+d $LOWER_DIR1
+f $LOWER_DIR1/a
+d $LOWER_DIR2
+d $LOWER_DIR3
+FILES
+}
+
+function afterfiles_rw {
+cat <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR1
+d $LOWER_DIR2
+d $LOWER_DIR3
+f $LOWER_DIR1/a
+FILES
+}
+
+
+( files ) | create_hierarchy
+mount_union "" $LOWER_DIR0 $LOWER_DIR1=ro
+./progs/flock-copyup $MOUNTPOINT/a
+unmount_union
+( afterfiles_ro ) | check_hierarchy $TOP_LOWER_DIR
+
+( files ) | create_hierarchy
+mount_union "" $LOWER_DIR0 $LOWER_DIR1
+./progs/flock-copyup $MOUNTPOINT/a
+unmount_union
+( afterfiles_rw ) | check_hierarchy $TOP_LOWER_DIR
+
+complete_test
diff --git a/t-fsync.sh b/t-fsync.sh
new file mode 100755 (executable)
index 0000000..c8a5615
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/sh
+# TEST: Branches: b0,b1=ro and b0,b1
+# TEST: open file 'a', write, then fsync the file on b0
+
+source scaffold
+
+function files {
+cat <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR1
+d $LOWER_DIR2
+d $LOWER_DIR3
+f $LOWER_DIR0/a
+FILES
+}
+
+function rw {
+       ( files ) | create_hierarchy
+       mount_union "" $LOWER_DIR0 $LOWER_DIR1
+       ./progs/fsync $MOUNTPOINT/a
+       unmount_union
+       ( files ) | check_hierarchy $TOP_LOWER_DIR
+}
+
+if [ -z "$FXNS" ] ; then
+       FXNS="rw"
+fi
+
+for x in $FXNS
+do
+       $x
+       echo -n "[$x] "
+done
+
+complete_test
diff --git a/t-incgen.sh b/t-incgen.sh
new file mode 100755 (executable)
index 0000000..0af38fc
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+# TEST: Branches: b0
+# TEST:  Increment the generation number 10 times.
+
+source scaffold
+
+function files {
+cat <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR1
+d $LOWER_DIR2
+d $LOWER_DIR3
+FILES
+}
+
+
+( files ) | create_hierarchy
+mount_union "" $LOWER_DIR0
+
+for ((i = 2; i < 12; i++))
+do
+       echo "New generation $i" >/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 (executable)
index 0000000..5ea5df4
--- /dev/null
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+# TEST: Branches: b0,b1=ro,b2=ro
+# TEST: run queryfile ioctl
+source scaffold
+
+function files {
+cat <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR1
+d $LOWER_DIR2
+d $LOWER_DIR3
+FILES
+}
+
+function beforefiles {
+cat <<FILES
+d $LOWER_DIR0/D
+d $LOWER_DIR2/D
+
+f $LOWER_DIR0/.wh.a
+f $LOWER_DIR1/a
+f $LOWER_DIR2/a
+
+f $LOWER_DIR0/b
+f $LOWER_DIR1/c
+f $LOWER_DIR2/b
+FILES
+}
+
+function afterfiles {
+       beforefiles
+}
+
+# prepare expected output file
+
+function expected_output {
+cat <<FILES
+/mnt/unionfs/D /n/lower/b0 (rw-)
+/mnt/unionfs/D /n/lower/b2 (r--)
+/mnt/unionfs/b /n/lower/b0 (rw-)
+/mnt/unionfs/b /n/lower/b2 (r--)
+/mnt/unionfs/c /n/lower/b1 (r--)
+Unable to retrieve list of branches for /mnt/unionfs/a: No such file or directory
+FILES
+}
+
+( files ; beforefiles) | create_hierarchy
+mount_union "" $LOWER_DIR0 $LOWER_DIR1=ro $LOWER_DIR2=ro
+expected_output > /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 (executable)
index 0000000..54e1950
--- /dev/null
@@ -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 <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR1
+d $LOWER_DIR2
+d $LOWER_DIR3
+f $LOWER_DIR2/abc
+FILES
+}
+
+function afterfiles_ro {
+cat <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+l $LOWER_DIR0/abc
+f $LOWER_DIR0/abc_
+d $LOWER_DIR1
+d $LOWER_DIR2
+f $LOWER_DIR2/abc
+d $LOWER_DIR3
+FILES
+}
+
+( files ) | create_hierarchy
+mount_union "" $LOWER_DIR0 $LOWER_DIR1=ro $LOWER_DIR2=ro
+
+cd /mnt/unionfs/ > /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 (executable)
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 <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR0/d1
+d $LOWER_DIR0/d6
+d $LOWER_DIR1
+d $LOWER_DIR1/d5
+d $LOWER_DIR1/d1
+d $LOWER_DIR1/d1/d2
+d $LOWER_DIR1/d1/d2/d3
+d $LOWER_DIR1/d1/d2/d3/d4
+d $LOWER_DIR1/d7
+d $LOWER_DIR1/d8
+d $LOWER_DIR2
+d $LOWER_DIR3
+
+FILES
+}
+
+# initial set of files
+function beforefiles {
+cat <<FILES
+f $LOWER_DIR0/a
+
+f $LOWER_DIR0/b
+
+f $LOWER_DIR0/c
+
+f $LOWER_DIR1/d5/d
+
+f $LOWER_DIR0/d1/f
+f $LOWER_DIR0/d1/.wh.g
+
+f $LOWER_DIR0/d1/.wh.h
+f $LOWER_DIR1/d1/i
+
+f $LOWER_DIR0/d6/x
+
+f $LOWER_DIR1/d7/j
+
+FILES
+}
+
+function beforefiles_391 {
+cat <<FILES
+d $LOWER_DIR1/d
+f $LOWER_DIR1/d/a
+FILES
+}
+
+function afterfiles_391 {
+beforefiles_391
+cat <<FILES
+d $LOWER_DIR0/d
+f $LOWER_DIR0/d/a
+f $LOWER_DIR0/d/b
+FILES
+}
+
+
+function afterfiles_rw {
+cat <<FILES
+f $LOWER_DIR0/a
+f $LOWER_DIR0/k
+
+f $LOWER_DIR0/b
+f $LOWER_DIR0/d1/l
+
+f $LOWER_DIR0/c
+f $LOWER_DIR0/d1/d2/m
+
+f $LOWER_DIR1/d5/d
+f $LOWER_DIR1/n
+
+f $LOWER_DIR0/d1/f
+f $LOWER_DIR0/d1/g
+
+f $LOWER_DIR1/d1/h
+f $LOWER_DIR1/d1/i
+
+f $LOWER_DIR0/d6/x
+d $LOWER_DIR0/d1/d2
+d $LOWER_DIR0/d1/d2/d3
+d $LOWER_DIR0/d1/d2/d3/d4
+f $LOWER_DIR0/d1/d2/d3/d4/j
+
+f $LOWER_DIR1/d7/j
+f $LOWER_DIR1/d8/k
+
+FILES
+}
+
+function afterfiles_onelink {
+beforefiles
+cat <<FILES
+f $LOWER_DIR0/k
+FILES
+}
+
+function afterfiles_ro {
+cat <<FILES
+f $LOWER_DIR0/a
+f $LOWER_DIR0/k
+
+f $LOWER_DIR0/b
+f $LOWER_DIR0/d1/l
+
+f $LOWER_DIR0/c
+f $LOWER_DIR0/d1/d2/m
+
+f $LOWER_DIR1/d5/d
+f $LOWER_DIR0/d5/d
+d $LOWER_DIR0/d5
+f $LOWER_DIR0/n
+
+f $LOWER_DIR0/d1/f
+f $LOWER_DIR0/d1/g
+
+f $LOWER_DIR0/d1/h
+f $LOWER_DIR0/d1/i
+f $LOWER_DIR1/d1/i
+
+f $LOWER_DIR0/d6/x
+d $LOWER_DIR0/d1/d2
+d $LOWER_DIR0/d1/d2/d3
+d $LOWER_DIR0/d1/d2/d3/d4
+f $LOWER_DIR0/d1/d2/d3/d4/j
+
+d $LOWER_DIR0/d7
+d $LOWER_DIR0/d8
+f $LOWER_DIR1/d7/j
+f $LOWER_DIR0/d7/j
+f $LOWER_DIR0/d8/k
+
+FILES
+}
+
+function rw {
+       ( directories ; beforefiles) | create_hierarchy
+
+       mount_union "" $LOWER_DIR0 $LOWER_DIR1
+
+
+       link $MOUNTPOINT/a $MOUNTPOINT/k
+       link $MOUNTPOINT/b $MOUNTPOINT/d1/l
+       link $MOUNTPOINT/c $MOUNTPOINT/d1/d2/m
+       link $MOUNTPOINT/d5/d $MOUNTPOINT/n
+       link $MOUNTPOINT/d1/f $MOUNTPOINT/d1/g
+       link $MOUNTPOINT/d1/i $MOUNTPOINT/d1/h
+       link $MOUNTPOINT/d6/x $MOUNTPOINT/d1/d2/d3/d4/j
+       link $MOUNTPOINT/d7/j $MOUNTPOINT/d8/k
+
+       unmount_union
+       ( directories ; afterfiles_rw )  | check_hierarchy $TOP_LOWER_DIR
+}
+
+function onelink {
+       ( directories ; beforefiles) | create_hierarchy
+
+       mount_union "" $LOWER_DIR0 $LOWER_DIR1
+
+       link $MOUNTPOINT/a $MOUNTPOINT/k
+
+       unmount_union
+       ( directories ; afterfiles_onelink )  | check_hierarchy $TOP_LOWER_DIR
+}
+
+function ro {
+       ( directories ; beforefiles) | create_hierarchy
+
+       mount_union "" $LOWER_DIR0 $LOWER_DIR1=ro
+
+       link $MOUNTPOINT/a $MOUNTPOINT/k
+       link $MOUNTPOINT/b $MOUNTPOINT/d1/l
+       link $MOUNTPOINT/c $MOUNTPOINT/d1/d2/m
+       link $MOUNTPOINT/d5/d $MOUNTPOINT/n  ## source is on EROFS
+       link $MOUNTPOINT/d1/f $MOUNTPOINT/d1/g
+       link $MOUNTPOINT/d1/i $MOUNTPOINT/d1/h  ## source is on EROFS
+       link $MOUNTPOINT/d6/x $MOUNTPOINT/d1/d2/d3/d4/j
+       link $MOUNTPOINT/d7/j $MOUNTPOINT/d8/k
+
+       unmount_union
+       ( directories ; afterfiles_ro )  | check_hierarchy $TOP_LOWER_DIR
+}
+
+function BUG391 {
+       ( directories ; beforefiles_391) | create_hierarchy
+
+       mount_union "" $LOWER_DIR0 $LOWER_DIR1=ro
+
+       link $MOUNTPOINT/d/a $MOUNTPOINT/d/b
+       checktype $MOUNTPOINT/d 'd'
+       checktype $MOUNTPOINT/d/a 'f'
+       checktype $MOUNTPOINT/d/b 'f'
+
+       unmount_union
+       ( directories ; afterfiles_391)  | check_hierarchy $TOP_LOWER_DIR
+}
+
+if [ -z "$FXNS" ] ; then
+       FXNS="rw ro onelink BUG391"
+fi
+
+for x in $FXNS
+do
+       $x
+       echo -n "[$x] "
+done
+
+complete_test
diff --git a/t-lookup-opaque.sh b/t-lookup-opaque.sh
new file mode 100755 (executable)
index 0000000..6769980
--- /dev/null
@@ -0,0 +1,71 @@
+#!/bin/sh
+
+# TEST: lookup(F), where F is:
+# TEST:        File-Directory-File
+# TEST:                Directory-File-Directory
+# TEST:                Whiteout-Directory
+
+source scaffold
+
+function branch_files {
+cat <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR0/dir
+f $LOWER_DIR0/dir/ro
+d $LOWER_DIR1
+d $LOWER_DIR1/dir
+f $LOWER_DIR1/dir/.wh.__dir_opaque
+f $LOWER_DIR1/dir/rw
+d $LOWER_DIR2
+d $LOWER_DIR3
+FILES
+}
+function cleanup {
+       unmount_union
+       userdel -f -r $UNPRIV_USER
+}
+
+branch_files | create_hierarchy
+
+chmod 777 $LOWER_DIR0
+chmod 777 $LOWER_DIR1
+chmod 700 $LOWER_DIR0/dir
+chmod 700 $LOWER_DIR1/dir
+chmod 644 $LOWER_DIR0/dir/ro
+chmod 644 $LOWER_DIR1/dir/.wh.__dir_opaque
+chmod 644 $LOWER_DIR1/dir/rw
+
+PRIV_USER="root"
+UNPRIV_USER="unionfs_test"
+
+if [ `cat /etc/passwd | grep $UNPRIV_USER | wc -l` -ne 0 ]; then
+       echo "User $UNPRIV_USER exists!"
+       exit 1
+fi
+
+useradd -d /tmp/$UNPRIV_USER -g nobody -m $UNPRIV_USER
+
+chown $UNPRIV_USER $LOWER_DIR0
+chown $UNPRIV_USER $LOWER_DIR1
+chown $PRIV_USER $LOWER_DIR0/dir
+chown $PRIV_USER $LOWER_DIR1/dir
+chown $UNPRIV_USER $LOWER_DIR0/dir/ro
+chown $UNPRIV_USER $LOWER_DIR1/dir/.wh.__dir_opaque
+chown $UNPRIV_USER $LOWER_DIR1/dir/rw
+
+mount_union "" $LOWER_DIR1=rw $LOWER_DIR0=ro
+
+su $UNPRIV_USER -c "ls -ald $MOUNTPOINT/dir > /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 (executable)
index 0000000..7e779ba
--- /dev/null
@@ -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 <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR1
+d $LOWER_DIR2
+d $LOWER_DIR3
+
+f $LOWER_DIR0/a
+d $LOWER_DIR1/a
+f $LOWER_DIR2/a
+
+d $LOWER_DIR0/b
+f $LOWER_DIR1/b
+d $LOWER_DIR2/b
+
+f $LOWER_DIR1/.wh.c
+d $LOWER_DIR2/c
+f $LOWER_DIR2/c/d
+FILES
+}
+
+files | create_hierarchy
+
+mount_union "" $TOP_LOWER_DIR/b?
+
+checktype $MOUNTPOINT/a 'f'
+checktype $MOUNTPOINT/b 'd'
+checktype $MOUNTPOINT/c '-'
+
+
+
+
+unmount_union
+
+files | check_hierarchy $TOP_LOWER_DIR
+
+complete_test
diff --git a/t-mkdir.sh b/t-mkdir.sh
new file mode 100755 (executable)
index 0000000..f00dfcc
--- /dev/null
@@ -0,0 +1,118 @@
+#!/bin/sh
+
+source scaffold
+
+# initial directories
+function directories {
+cat <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR0/d1
+d $LOWER_DIR0/d1/d2
+d $LOWER_DIR0/d6
+d $LOWER_DIR1
+d $LOWER_DIR1/d5
+d $LOWER_DIR1/d1
+d $LOWER_DIR1/d1/d2
+d $LOWER_DIR1/d1/d2/d3
+f $LOWER_DIR1/d1/d2/d3/a
+f $LOWER_DIR1/d1/d2/d3/b
+f $LOWER_DIR1/d1/d2/d3/c
+d $LOWER_DIR1/d1/d2/d3/d4
+d $LOWER_DIR2
+d $LOWER_DIR2/d5
+d $LOWER_DIR2/d1
+d $LOWER_DIR2/d1/d2
+d $LOWER_DIR2/d1/d2/d3
+f $LOWER_DIR2/d1/d2/d3/d
+f $LOWER_DIR2/d1/d2/d3/e
+f $LOWER_DIR2/d1/d2/d3/f
+d $LOWER_DIR2/d1/d2/d3/d4
+d $LOWER_DIR3
+
+FILES
+}
+
+# initial set of files
+function beforefiles {
+cat <<FILES
+
+f $LOWER_DIR0/d1/.wh.x
+
+f $LOWER_DIR0/d1/d2/.wh.d3
+
+FILES
+}
+
+
+function afterfiles_rw {
+cat <<FILES
+d $LOWER_DIR0/y
+
+d $LOWER_DIR0/d1/x
+
+d $LOWER_DIR0/d1/d2/d3
+
+f $LOWER_DIR0/d1/d2/d3/.wh.__dir_opaque
+f $LOWER_DIR0/d1/x/.wh.__dir_opaque
+f $LOWER_DIR0/y/.wh.__dir_opaque
+
+FILES
+}
+
+
+
+function afterfiles_ro {
+cat <<FILES
+d $LOWER_DIR0/y
+
+d $LOWER_DIR0/d1/x
+
+d $LOWER_DIR0/d1/d2/d3
+f $LOWER_DIR0/d1/d2/d3/.wh.__dir_opaque
+f $LOWER_DIR0/d1/x/.wh.__dir_opaque
+f $LOWER_DIR0/y/.wh.__dir_opaque
+
+FILES
+}
+
+
+
+
+##### simple tests
+( directories ; beforefiles) | create_hierarchy
+
+mount_union "" $LOWER_DIR0 $LOWER_DIR1 $LOWER_DIR2
+
+
+mkdir $MOUNTPOINT/y
+checktype $MOUNTPOINT/y 'd'
+mkdir $MOUNTPOINT/d1/x
+checktype $MOUNTPOINT/d1/x 'd'
+mkdir $MOUNTPOINT/d1/d2/d3
+checktype $MOUNTPOINT/d1/d2/d3 'd'
+checktype $MOUNTPOINT/d1/d2/d3/d4 '-'
+
+unmount_union
+( directories ; afterfiles_rw )  | check_hierarchy $TOP_LOWER_DIR
+
+
+
+
+#### simple tests
+( directories ; beforefiles) | create_hierarchy
+
+mount_union "" $LOWER_DIR0 $LOWER_DIR1=ro $LOWER_DIR2=ro
+
+mkdir $MOUNTPOINT/y
+checktype $MOUNTPOINT/y 'd'
+mkdir $MOUNTPOINT/d1/x
+checktype $MOUNTPOINT/d1/x 'd'
+mkdir $MOUNTPOINT/d1/d2/d3
+checktype $MOUNTPOINT/d1/d2/d3 'd'
+checktype $MOUNTPOINT/d1/d2/d3/d4 '-'
+
+unmount_union
+( directories ; afterfiles_ro )  | check_hierarchy $TOP_LOWER_DIR
+
+complete_test
diff --git a/t-mknod.sh b/t-mknod.sh
new file mode 100755 (executable)
index 0000000..40a6a56
--- /dev/null
@@ -0,0 +1,99 @@
+#!/bin/sh
+
+# TEST: Branches b0,b1 and b0,b1=ro
+# TEST: mknod A
+# TEST:  Where A is in the same branch
+# TEST:  Where A already exists as a whiteout on the same branch
+# TEST:  Where A exists as a whiteout on a RO branch
+
+source scaffold
+
+# initial directories
+function directories {
+cat <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR0/d1
+d $LOWER_DIR0/d6
+d $LOWER_DIR1
+d $LOWER_DIR1/d5
+d $LOWER_DIR1/d1
+d $LOWER_DIR1/d1/d2
+d $LOWER_DIR1/d1/d2/d3
+d $LOWER_DIR1/d1/d2/d3/d4
+d $LOWER_DIR2
+d $LOWER_DIR3
+
+FILES
+}
+
+# initial set of files
+function beforefiles {
+cat <<FILES
+f $LOWER_DIR1/d1/d2/d3/d4/.wh.c
+FILES
+}
+
+
+function afterfiles_rw {
+cat <<FILES
+b $LOWER_DIR0/a
+
+c $LOWER_DIR1/d5/b
+
+b $LOWER_DIR1/d1/d2/d3/d4/c
+
+FILES
+}
+
+
+
+function afterfiles_ro {
+cat <<FILES
+b $LOWER_DIR0/a
+
+d $LOWER_DIR0/d5
+c $LOWER_DIR0/d5/b
+
+f $LOWER_DIR1/d1/d2/d3/d4/.wh.c
+d $LOWER_DIR0/d1/d2
+d $LOWER_DIR0/d1/d2/d3
+d $LOWER_DIR0/d1/d2/d3/d4
+b $LOWER_DIR0/d1/d2/d3/d4/c
+FILES
+}
+
+
+
+
+( directories ; beforefiles) | create_hierarchy
+
+mount_union "" $LOWER_DIR0 $LOWER_DIR1
+
+mknod $MOUNTPOINT/a    b 200 0
+checktype "$MOUNTPOINT/a" 'b'
+mknod $MOUNTPOINT/d5/b  c  200 0
+checktype "$MOUNTPOINT/d5/b" 'c'
+mknod $MOUNTPOINT/d1/d2/d3/d4/c  b  200 0
+checktype "$MOUNTPOINT/d1/d2/d3/d4/c" 'b'
+
+
+unmount_union
+( directories ; afterfiles_rw )  | check_hierarchy $TOP_LOWER_DIR
+
+
+( directories ; beforefiles) | create_hierarchy
+
+mount_union "" $LOWER_DIR0 $LOWER_DIR1=ro
+
+mknod $MOUNTPOINT/a   b  200 0
+checktype "$MOUNTPOINT/a" 'b'
+mknod $MOUNTPOINT/d5/b  c   200 0
+checktype "$MOUNTPOINT/d5/b" 'c'
+mknod $MOUNTPOINT/d1/d2/d3/d4/c   b  200 0
+checktype "$MOUNTPOINT/d1/d2/d3/d4/c" 'b'
+
+unmount_union
+( directories ; afterfiles_ro )  | check_hierarchy $TOP_LOWER_DIR
+
+complete_test
diff --git a/t-mmap.sh b/t-mmap.sh
new file mode 100755 (executable)
index 0000000..5c21409
--- /dev/null
+++ b/t-mmap.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+# TEST: Branches: b0,b1=ro and b0,b1
+# TEST: open(a), mmap) for reading, read, unmap, close
+# TEST: open(b), mmap for writing/copyup, read, rewrite, unmap, close
+
+source scaffold
+
+function files {
+cat <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR1
+f $LOWER_DIR1/a
+f $LOWER_DIR1/b
+d $LOWER_DIR2
+d $LOWER_DIR3
+FILES
+}
+
+function afterfiles_ro {
+cat <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR1
+f $LOWER_DIR1/a
+f $LOWER_DIR1/b
+d $LOWER_DIR2
+d $LOWER_DIR3
+FILES
+}
+
+function afterfiles_rw {
+cat <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+f $LOWER_DIR0/b
+d $LOWER_DIR1
+f $LOWER_DIR1/a
+f $LOWER_DIR1/b
+d $LOWER_DIR2
+d $LOWER_DIR3
+FILES
+}
+
+# read-only mmap test
+function test_ro {
+    ( files ) | create_hierarchy
+    mount_union "" $LOWER_DIR0 $LOWER_DIR1=ro
+    ./progs/mapper -r $MOUNTPOINT/a > /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 (executable)
index 0000000..b120cc5
--- /dev/null
@@ -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 <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR1
+f $LOWER_DIR1/a
+d $LOWER_DIR2
+d $LOWER_DIR3
+FILES
+}
+
+function afterfiles_ro {
+cat <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+f $LOWER_DIR0/.wh.a
+d $LOWER_DIR1
+f $LOWER_DIR1/a
+d $LOWER_DIR2
+d $LOWER_DIR3
+FILES
+}
+
+function afterfiles_rw {
+cat <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR1
+d $LOWER_DIR2
+d $LOWER_DIR3
+FILES
+}
+
+
+( files ) | create_hierarchy
+mount_union "" $LOWER_DIR0 $LOWER_DIR1=ro
+./progs/open-unlink $MOUNTPOINT/a
+unmount_union
+( afterfiles_ro ) | check_hierarchy $TOP_LOWER_DIR
+
+complete_test
diff --git a/t-open.sh b/t-open.sh
new file mode 100755 (executable)
index 0000000..e9db34d
--- /dev/null
+++ b/t-open.sh
@@ -0,0 +1,103 @@
+#!/bin/sh
+
+# TEST: Branches: b0,b1=ro,b2=ro
+# TEST: open(D) where D is
+# TEST:  a directory in b0
+# TEST:  nonexistent in b1
+# TEST:  a directory in b0
+
+source scaffold
+
+function files {
+cat <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR1
+d $LOWER_DIR2
+d $LOWER_DIR3
+FILES
+}
+
+function beforefiles {
+cat <<FILES
+d $LOWER_DIR0/a
+d $LOWER_DIR2/a
+
+f $LOWER_DIR0/b
+f $LOWER_DIR1/c
+f $LOWER_DIR2/d
+
+f $LOWER_DIR0/e
+f $LOWER_DIR1/e
+f $LOWER_DIR2/e
+FILES
+}
+
+function afterfiles {
+       beforefiles
+}
+
+function beforefiles_copyup {
+cat <<FILES
+d $LOWER_DIR1/d1
+d $LOWER_DIR1/d1/d2
+f $LOWER_DIR1/d1/d2/a
+FILES
+}
+
+function afterfiles_copyup {
+cat <<FILES
+d $LOWER_DIR0/d1
+d $LOWER_DIR0/d1/d2
+f $LOWER_DIR0/d1/d2/a
+d $LOWER_DIR1/d1
+d $LOWER_DIR1/d1/d2
+f $LOWER_DIR1/d1/d2/a
+FILES
+}
+
+function ro {
+       ( files ; beforefiles) | create_hierarchy
+
+       mount_union "" $LOWER_DIR0 $LOWER_DIR1=ro $LOWER_DIR2=ro
+
+       checktype $MOUNTPOINT/a 'd'
+       /bin/ls $MOUNTPOINT/a >/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 (executable)
index 0000000..e021d63
--- /dev/null
@@ -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 <<FILES0
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR1
+d $LOWER_DIR2
+d $LOWER_DIR3
+FILES0
+}
+
+# This works for 1-92 failing if > 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 (executable)
index 0000000..ab1ef00
--- /dev/null
@@ -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 <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+f $LOWER_DIR0/s.file
+d $LOWER_DIR0/s.dir
+d $LOWER_DIR0/s.child
+f $LOWER_DIR0/s.child/foo
+d $LOWER_DIR0/s.wh
+f $LOWER_DIR0/s.wh/.wh.foo
+f $LOWER_DIR0/d.file
+d $LOWER_DIR0/d.dir
+d $LOWER_DIR0/d.child
+f $LOWER_DIR0/d.child/foo
+d $LOWER_DIR0/d.wh
+f $LOWER_DIR0/d.wh/.wh.foo
+d $LOWER_DIR1
+d $LOWER_DIR2
+d $LOWER_DIR3
+FILES
+}
+
+function renamestr {
+       EXPR1=`echo $1 | sed -e 's/\//\\\\\//g'`
+       EXPR2=`echo $2 | sed -e 's/\//\\\\\//g'`
+
+       echo "s/^\([a-zA-Z]\) $EXPR1/\1 $EXPR2/"
+}
+
+# file->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 (executable)
index 0000000..19cfc63
--- /dev/null
@@ -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 <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR1
+d $LOWER_DIR2
+d $LOWER_DIR3
+FILES
+}
+
+function beforefiles {
+cat <<FILES
+s $LOWER_DIR2/rA.S
+
+s $LOWER_DIR1/rB.S
+s $LOWER_DIR2/rB.S
+
+s $LOWER_DIR0/rC.S
+s $LOWER_DIR1/rC.S
+
+s $LOWER_DIR0/rD.S
+s $LOWER_DIR1/rD.S
+f $LOWER_DIR2/rD.D
+
+s $LOWER_DIR1/rE.S
+s $LOWER_DIR2/rE.S
+f $LOWER_DIR2/rE.D
+
+s $LOWER_DIR2/rF.S
+f $LOWER_DIR1/rF.D
+f $LOWER_DIR2/rF.D
+
+s $LOWER_DIR2/rG.S
+f $LOWER_DIR0/rG.D
+f $LOWER_DIR2/rG.D
+
+s $LOWER_DIR0/rH.S
+s $LOWER_DIR1/rH.S
+s $LOWER_DIR2/rH.S
+f $LOWER_DIR0/rH.D
+f $LOWER_DIR1/rH.D
+f $LOWER_DIR2/rH.D
+
+fs $LOWER_DIR0/rI.S
+fsi $LOWER_DIR1/rI.S
+fs $LOWER_DIR2/rI.S
+f $LOWER_DIR0/rI.D
+f $LOWER_DIR1/rI.D
+f $LOWER_DIR2/rI.D
+FILES
+}
+
+function afterfiles_rw {
+cat <<FILES
+f $LOWER_DIR2/rA.D
+
+f $LOWER_DIR1/rB.D
+f $LOWER_DIR1/.wh.rB.S
+f $LOWER_DIR2/rB.S
+
+f $LOWER_DIR0/.wh.rC.S
+f $LOWER_DIR0/rC.D
+f $LOWER_DIR1/rC.S
+
+f $LOWER_DIR0/.wh.rD.S
+f $LOWER_DIR0/rD.D
+f $LOWER_DIR1/rD.S
+f $LOWER_DIR2/rD.D
+
+f $LOWER_DIR1/rE.D
+f $LOWER_DIR1/.wh.rE.S
+f $LOWER_DIR2/rE.S
+f $LOWER_DIR2/rE.D
+
+f $LOWER_DIR2/rF.D
+
+f $LOWER_DIR2/rG.D
+
+f $LOWER_DIR0/.wh.rH.S
+f $LOWER_DIR1/rH.S
+f $LOWER_DIR2/rH.S
+f $LOWER_DIR0/rH.D
+f $LOWER_DIR1/rH.D
+f $LOWER_DIR2/rH.D
+
+f $LOWER_DIR0/.wh.rI.S
+f $LOWER_DIR1/rI.S
+f $LOWER_DIR2/rI.S
+f $LOWER_DIR0/rI.D
+f $LOWER_DIR1/rI.D
+f $LOWER_DIR2/rI.D
+FILES
+}
+
+function afterfiles_ro {
+cat <<FILES
+f $LOWER_DIR0/.wh.rA.S
+f $LOWER_DIR0/rA.D
+f $LOWER_DIR2/rA.S
+
+f $LOWER_DIR0/.wh.rB.S
+f $LOWER_DIR0/rB.D
+f $LOWER_DIR1/rB.S
+f $LOWER_DIR2/rB.S
+
+f $LOWER_DIR0/rC.D
+f $LOWER_DIR0/.wh.rC.S
+f $LOWER_DIR1/rC.S
+
+f $LOWER_DIR0/rD.D
+f $LOWER_DIR0/.wh.rD.S
+f $LOWER_DIR1/rD.S
+f $LOWER_DIR2/rD.D
+
+f $LOWER_DIR0/rE.D
+f $LOWER_DIR0/.wh.rE.S
+f $LOWER_DIR1/rE.S
+f $LOWER_DIR2/rE.S
+f $LOWER_DIR2/rE.D
+
+f $LOWER_DIR0/.wh.rF.S
+f $LOWER_DIR0/rF.D
+f $LOWER_DIR2/rF.S
+f $LOWER_DIR1/rF.D
+f $LOWER_DIR2/rF.D
+
+f $LOWER_DIR0/.wh.rG.S
+f $LOWER_DIR2/rG.S
+f $LOWER_DIR0/rG.D
+f $LOWER_DIR2/rG.D
+
+f $LOWER_DIR0/.wh.rH.S
+f $LOWER_DIR1/rH.S
+f $LOWER_DIR2/rH.S
+f $LOWER_DIR0/rH.D
+f $LOWER_DIR1/rH.D
+f $LOWER_DIR2/rH.D
+
+f $LOWER_DIR0/.wh.rI.S
+f $LOWER_DIR1/rI.S
+f $LOWER_DIR2/rI.S
+f $LOWER_DIR0/rI.D
+f $LOWER_DIR1/rI.D
+f $LOWER_DIR2/rI.D
+FILES
+}
+
+function beforefiles_fail {
+cat <<FILES
+fi $LOWER_DIR2/rJ.S
+FILES
+}
+
+
+function afterfiles_fail {
+cat <<FILES
+f $LOWER_DIR2/rJ.S
+FILES
+}
+
+
+for STATE in rw ro
+do
+       ( files ; beforefiles) | create_hierarchy
+       mount_union "" $LOWER_DIR0 $LOWER_DIR1=$STATE $LOWER_DIR2=$STATE
+       for X in A B C D E F G H I
+       do
+               mv -f "$MOUNTPOINT/r$X.S" "$MOUNTPOINT/r$X.D"
+               checktype "$MOUNTPOINT/r$X.S" '-'
+               checktype "$MOUNTPOINT/r$X.D" 'f'
+               echo "Source file." | diff "$MOUNTPOINT/r$X.D" -
+       done
+       unmount_union
+
+       ( files ; afterfiles_$STATE )  | check_hierarchy $TOP_LOWER_DIR
+
+       echo -n "[$STATE] "
+
+done
+
+# XXX: should we check if all lower branches have chattr?
+if havechattr $LOWER_DIR0 ; then
+       ( files ; beforefiles_fail) | create_hierarchy
+       mount_union "" $LOWER_DIR0 $LOWER_DIR1 $LOWER_DIR2
+       for X in J
+       do
+               shouldfail mv -f "$MOUNTPOINT/r$X.S" "$MOUNTPOINT/r$X.D"
+               checktype "$MOUNTPOINT/r$X.S" 'f'
+               checktype "$MOUNTPOINT/r$X.D" '-'
+       done
+       unmount_union
+       ( files ; afterfiles_fail )  | check_hierarchy $TOP_LOWER_DIR
+fi
+
+complete_test
diff --git a/t-symlink.sh b/t-symlink.sh
new file mode 100755 (executable)
index 0000000..effe7da
--- /dev/null
@@ -0,0 +1,122 @@
+#!/bin/sh
+
+# TEST: Branches b0,b1 and b0,b1=ro
+# TEST: symlink(A, B)
+# TEST:  Where A and B are in the same branch
+# TEST:  Where A and B are in different branches
+# TEST:  Where B already exists as a whiteout on the same branch
+# TEST:  Where B exists as a whiteout on a RO branch
+
+
+
+source scaffold
+
+# initial directories
+function directories {
+cat <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR0/d1
+d $LOWER_DIR0/d6
+d $LOWER_DIR1
+d $LOWER_DIR1/d5
+d $LOWER_DIR1/d1
+d $LOWER_DIR1/d1/d2
+d $LOWER_DIR1/d1/d2/d3
+d $LOWER_DIR1/d1/d2/d3/d4
+d $LOWER_DIR2
+d $LOWER_DIR3
+
+FILES
+}
+
+# initial set of files
+function beforefiles {
+cat <<FILES
+f $LOWER_DIR0/a
+
+f $LOWER_DIR0/b
+
+f $LOWER_DIR1/d1/d2/d3/d4/.wh.c
+
+FILES
+}
+
+
+function afterfiles_rw {
+cat <<FILES
+f $LOWER_DIR0/a
+l $LOWER_DIR0/d
+
+f $LOWER_DIR0/b
+l $LOWER_DIR1/d5/e
+
+l $LOWER_DIR1/d1/d2/d3/d4/c
+
+FILES
+}
+
+
+
+function afterfiles_ro {
+cat <<FILES
+
+f $LOWER_DIR0/a
+l $LOWER_DIR0/d
+
+f $LOWER_DIR0/b
+d $LOWER_DIR0/d5
+l $LOWER_DIR0/d5/e
+
+f $LOWER_DIR1/d1/d2/d3/d4/.wh.c
+d $LOWER_DIR0/d1/d2
+d $LOWER_DIR0/d1/d2/d3
+d $LOWER_DIR0/d1/d2/d3/d4
+l $LOWER_DIR0/d1/d2/d3/d4/c
+
+FILES
+}
+
+
+
+
+##### simple tests
+( directories ; beforefiles) | create_hierarchy
+
+mount_union "" $LOWER_DIR0 $LOWER_DIR1
+
+function do_link {
+       SOURCE=$1
+       DEST=$2
+
+       ln --symbolic $SOURCE $DEST || return $?
+
+       if [ `readlink $DEST` != "$SOURCE" ] ; then
+               echo "readlink('$DEST') does not match '$SOURCE'" 1>&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 (executable)
index 0000000..e09399c
--- /dev/null
@@ -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 <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR1
+d $LOWER_DIR2
+d $LOWER_DIR2/d1
+d $LOWER_DIR2/d1/d2
+d $LOWER_DIR2/d1/d2/d3
+d $LOWER_DIR2/d1/d2/d3/d4
+d $LOWER_DIR3
+FILES
+}
+
+
+(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 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 (executable)
index 0000000..d12ea2b
--- /dev/null
@@ -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 <<FILES
+d $TOP_LOWER_DIR
+d $LOWER_DIR0
+d $LOWER_DIR1
+d $LOWER_DIR0/d1
+d $LOWER_DIR0/d1/d2
+d $LOWER_DIR1/d1
+d $LOWER_DIR1/d1/d2
+d $LOWER_DIR2
+d $LOWER_DIR3
+FILES
+}
+
+function beforefiles {
+cat <<FILES
+f $LOWER_DIR0/a
+f $LOWER_DIR1/a
+
+f $LOWER_DIR1/b
+
+f $LOWER_DIR0/c
+f $LOWER_DIR1/c
+
+f $LOWER_DIR0/d
+
+f $LOWER_DIR1/d1/d2/e
+
+l $LOWER_DIR1/g
+FILES
+}
+
+function afterfiles_ro {
+cat <<FILES
+f $LOWER_DIR0/.wh.a
+f $LOWER_DIR1/a
+
+f $LOWER_DIR0/.wh.b
+f $LOWER_DIR1/b
+
+f $LOWER_DIR0/.wh.c
+f $LOWER_DIR1/c
+
+f $LOWER_DIR0/.wh.d
+
+f $LOWER_DIR0/d1/d2/.wh.e
+f $LOWER_DIR1/d1/d2/e
+
+f $LOWER_DIR0/.wh.g
+l $LOWER_DIR1/g
+FILES
+}
+
+function afterfiles_rw {
+cat <<FILES
+f $LOWER_DIR0/.wh.a
+f $LOWER_DIR1/a
+
+f $LOWER_DIR1/.wh.b
+
+f $LOWER_DIR0/.wh.c
+f $LOWER_DIR1/c
+
+f $LOWER_DIR0/.wh.d
+
+f $LOWER_DIR1/d1/d2/.wh.e
+
+f $LOWER_DIR1/.wh.g
+FILES
+}
+
+function afterfiles_createback_ro {
+cat <<FILES
+f $LOWER_DIR0/a
+f $LOWER_DIR1/a
+
+f $LOWER_DIR0/b
+f $LOWER_DIR1/b
+
+f $LOWER_DIR0/c
+f $LOWER_DIR1/c
+
+f $LOWER_DIR0/d
+
+f $LOWER_DIR0/d1/d2/e
+f $LOWER_DIR1/d1/d2/e
+
+f $LOWER_DIR0/g
+l $LOWER_DIR1/g
+FILES
+}
+
+function afterfiles_createback_rw {
+cat <<FILES
+f $LOWER_DIR0/a
+f $LOWER_DIR1/a
+
+f $LOWER_DIR0/b
+f $LOWER_DIR1/.wh.b
+
+f $LOWER_DIR0/c
+f $LOWER_DIR1/c
+
+f $LOWER_DIR0/d
+
+f $LOWER_DIR0/d1/d2/e
+f $LOWER_DIR1/d1/d2/.wh.e
+
+f $LOWER_DIR0/g
+f $LOWER_DIR1/.wh.g
+FILES
+}
+
+
+function test1
+{
+##### simple test
+( files ; beforefiles) | create_hierarchy
+
+mount_union "" $LOWER_DIR0 $LOWER_DIR1=ro
+
+unlink $MOUNTPOINT/a
+unlink $MOUNTPOINT/b
+unlink $MOUNTPOINT/c
+unlink $MOUNTPOINT/d
+unlink $MOUNTPOINT/d1/d2/e
+unlink $MOUNTPOINT/g
+
+# making sure things are gone
+checktype $MOUNTPOINT/a '-'
+checktype $MOUNTPOINT/b '-'
+checktype $MOUNTPOINT/c '-'
+checktype $MOUNTPOINT/d '-'
+checktype $MOUNTPOINT/d1/d2/e '-'
+checktype $MOUNTPOINT/g '-'
+
+unmount_union
+( files ; afterfiles_ro )  | check_hierarchy $TOP_LOWER_DIR
+
+echo -n "[ro] "
+}
+
+function test2
+{
+##### now unlink files and then create them before unmount
+( files ; beforefiles) | create_hierarchy
+
+mount_union "" $LOWER_DIR0 $LOWER_DIR1=ro
+
+unlink $MOUNTPOINT/a
+unlink $MOUNTPOINT/b
+unlink $MOUNTPOINT/c
+unlink $MOUNTPOINT/d
+unlink $MOUNTPOINT/d1/d2/e
+unlink $MOUNTPOINT/g
+# making sure things are gone
+checktype $MOUNTPOINT/a '-'
+checktype $MOUNTPOINT/b '-'
+checktype $MOUNTPOINT/c '-'
+checktype $MOUNTPOINT/d '-'
+checktype $MOUNTPOINT/d1/d2/e '-'
+checktype $MOUNTPOINT/g '-'
+
+# create back files
+touch $MOUNTPOINT/a
+touch $MOUNTPOINT/b
+touch $MOUNTPOINT/c
+touch $MOUNTPOINT/d
+touch $MOUNTPOINT/d1/d2/e
+touch $MOUNTPOINT/g
+# making sure things are created back
+checktype $MOUNTPOINT/a 'f'
+checktype $MOUNTPOINT/b 'f'
+checktype $MOUNTPOINT/c 'f'
+checktype $MOUNTPOINT/d 'f'
+checktype $MOUNTPOINT/d1/d2/e 'f'
+checktype $MOUNTPOINT/g 'f'
+
+unmount_union
+( files ; afterfiles_createback_ro )  | check_hierarchy $TOP_LOWER_DIR
+
+echo -n "[ro createback] "
+}
+
+function test3
+{
+##### now unlink files and then create them after unmount
+( files ; beforefiles) | create_hierarchy
+
+mount_union "" $LOWER_DIR0 $LOWER_DIR1=ro
+
+unlink $MOUNTPOINT/a
+unlink $MOUNTPOINT/b
+unlink $MOUNTPOINT/c
+unlink $MOUNTPOINT/d
+unlink $MOUNTPOINT/d1/d2/e
+unlink $MOUNTPOINT/g
+# making sure things are gone
+checktype $MOUNTPOINT/a '-'
+checktype $MOUNTPOINT/b '-'
+checktype $MOUNTPOINT/c '-'
+checktype $MOUNTPOINT/d '-'
+checktype $MOUNTPOINT/d1/d2/e '-'
+checktype $MOUNTPOINT/g '-'
+
+unmount_union
+( files ; afterfiles_ro )  | check_hierarchy $TOP_LOWER_DIR
+echo -n "[ro] "
+}
+
+function test4
+{
+mount_union "" $LOWER_DIR0 $LOWER_DIR1=ro
+
+# create back files
+touch $MOUNTPOINT/a
+touch $MOUNTPOINT/b
+touch $MOUNTPOINT/c
+touch $MOUNTPOINT/d
+touch $MOUNTPOINT/d1/d2/e
+touch $MOUNTPOINT/g
+# making sure things are created back
+checktype $MOUNTPOINT/a 'f'
+checktype $MOUNTPOINT/b 'f'
+checktype $MOUNTPOINT/c 'f'
+checktype $MOUNTPOINT/d 'f'
+checktype $MOUNTPOINT/d1/d2/e 'f'
+checktype $MOUNTPOINT/g 'f'
+
+unmount_union
+( files ; afterfiles_createback_ro)  | check_hierarchy $TOP_LOWER_DIR
+
+echo -n "[ro createback 2] "
+}
+
+function test5
+{
+##### simple test with rw branches
+( files ; beforefiles) | create_hierarchy
+
+mount_union "" $LOWER_DIR0 $LOWER_DIR1
+
+unlink $MOUNTPOINT/a
+unlink $MOUNTPOINT/b
+unlink $MOUNTPOINT/c
+unlink $MOUNTPOINT/d
+unlink $MOUNTPOINT/d1/d2/e
+unlink $MOUNTPOINT/g
+# making sure things are gone
+checktype $MOUNTPOINT/a '-'
+checktype $MOUNTPOINT/b '-'
+checktype $MOUNTPOINT/c '-'
+checktype $MOUNTPOINT/d '-'
+checktype $MOUNTPOINT/d1/d2/e '-'
+checktype $MOUNTPOINT/g '-'
+
+unmount_union
+
+( files ; afterfiles_rw)  | check_hierarchy $TOP_LOWER_DIR
+
+echo -n "[rw] "
+}
+
+function test6
+{
+##### now unlink files and then create them before unmount
+( files ; beforefiles) | create_hierarchy
+
+mount_union "" $LOWER_DIR0 $LOWER_DIR1
+
+unlink $MOUNTPOINT/a
+unlink $MOUNTPOINT/b
+unlink $MOUNTPOINT/c
+unlink $MOUNTPOINT/d
+unlink $MOUNTPOINT/d1/d2/e
+unlink $MOUNTPOINT/g
+# making sure things are gone
+checktype $MOUNTPOINT/a '-'
+checktype $MOUNTPOINT/b '-'
+checktype $MOUNTPOINT/c '-'
+checktype $MOUNTPOINT/d '-'
+checktype $MOUNTPOINT/d1/d2/e '-'
+checktype $MOUNTPOINT/g '-'
+
+# create back files
+touch $MOUNTPOINT/a
+touch $MOUNTPOINT/b
+touch $MOUNTPOINT/c
+touch $MOUNTPOINT/d
+touch $MOUNTPOINT/d1/d2/e
+touch $MOUNTPOINT/g
+# making sure things are created back
+checktype $MOUNTPOINT/a 'f'
+checktype $MOUNTPOINT/b 'f'
+checktype $MOUNTPOINT/c 'f'
+checktype $MOUNTPOINT/d 'f'
+checktype $MOUNTPOINT/d1/d2/e 'f'
+checktype $MOUNTPOINT/g 'f'
+
+unmount_union
+( files ; afterfiles_createback_rw )  | check_hierarchy $TOP_LOWER_DIR
+
+echo -n "[rw createback] "
+}
+
+function test7
+{
+##### now unlink files and then create them after unmount
+( files ; beforefiles) | create_hierarchy
+
+mount_union "" $LOWER_DIR0 $LOWER_DIR1
+
+unlink $MOUNTPOINT/a
+unlink $MOUNTPOINT/b
+unlink $MOUNTPOINT/c
+unlink $MOUNTPOINT/d
+unlink $MOUNTPOINT/d1/d2/e
+unlink $MOUNTPOINT/g
+# making sure things are gone
+checktype $MOUNTPOINT/a '-'
+checktype $MOUNTPOINT/b '-'
+checktype $MOUNTPOINT/c '-'
+checktype $MOUNTPOINT/d '-'
+checktype $MOUNTPOINT/d1/d2/e '-'
+checktype $MOUNTPOINT/g '-'
+
+unmount_union
+( files ; afterfiles_rw )  | check_hierarchy $TOP_LOWER_DIR
+
+mount_union "" $LOWER_DIR0 $LOWER_DIR1
+
+# create back files
+touch $MOUNTPOINT/a
+touch $MOUNTPOINT/b
+touch $MOUNTPOINT/c
+touch $MOUNTPOINT/d
+touch $MOUNTPOINT/d1/d2/e
+touch $MOUNTPOINT/g
+# making sure things are created back
+checktype $MOUNTPOINT/a 'f'
+checktype $MOUNTPOINT/b 'f'
+checktype $MOUNTPOINT/c 'f'
+checktype $MOUNTPOINT/d 'f'
+checktype  $MOUNTPOINT/d1/d2/e 'f'
+checktype $MOUNTPOINT/g 'f'
+
+unmount_union
+( files ; afterfiles_createback_rw)  | check_hierarchy $TOP_LOWER_DIR
+echo -n "[rw createback 2] "
+}
+
+test1
+test2
+test3
+test4
+test5
+test6
+test7
+
+complete_test
diff --git a/testplan b/testplan
new file mode 100644 (file)
index 0000000..495fdf7
--- /dev/null
+++ b/testplan
@@ -0,0 +1,93 @@
+Scripts:
+
+1. lookup.sh:
+   Test unionfs_lookup() function thoroughly. Create a directory tree structure, test lookup
+   and then verify the underlying tree structure.
+
+4. unlink-delete-whiteout-ro.sh:
+   test unlink_whiteout with mix of ro and rw branches
+
+5. unlink-delete-whiteout-rw.sh:
+   test unlink_whiteout with just rw branches
+
+
+6  rmdir-all-ro.sh:
+   test rmdir_all with mix of rw and ro branches
+
+7. rmdir-all-rw.sh:
+   test rmdir_all with just rw branches
+
+8. rmdir-delete-whiteout-ro.sh:
+   test rmdir_whiteout with mix of ro and rw branches
+
+9. rmdir-delete-whiteout-rw.sh:
+   test rmdir_whiteout with just rw branches
+
+
+
+10. create-ro.sh:
+    test create with mix of rw and ro branches
+
+11. create-rw.sh:
+    test create with just rw branches
+
+
+
+12. mkdir-ro.sh:
+    test create with mix of rw and ro branches
+
+13. mkdir-rw.sh:
+    test create with just rw branches
+
+
+16. rename-delete-whiteout-ro.sh:
+    test rename_whiteout with mix of ro and rw branches
+
+17. rename-delete-whiteout-rw.sh:
+    test rename_whiteout with just rw branches
+
+# TEST: symlink
+#
+
+# 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 B is on b0 and A is on b1
+# 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: 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: utimes(F)
+# TEST: truncate(F, x)
+# 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
+
+# TEST: Branches b0,b1 and b0,b1=ro
+# TEST: mknod(A)
+# TEST:  A does not exist
+# TEST:  A exists as a file
+# TEST:  A exists as a whiteout in b0
+# TEST:  A exists as a whiteout in b1
+# TEST:  A exists as a whiteout in b0, and a file in b1
+# TEST: Branches b0,b1=ro
+# TEST:  B exists in b1
+# TEST:  mknod(B/A)
+# TEST:   B does not exist in b0
diff --git a/thor.conf b/thor.conf
new file mode 100644 (file)
index 0000000..49e7fc6
--- /dev/null
+++ b/thor.conf
@@ -0,0 +1,95 @@
+# 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-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/.static/dev/loop0
+DEV1=/dev/.static/dev/loop1
+DEV2=/dev/.static/dev/loop2
+DEV3=/dev/.static/dev/loop3
+# 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
+ECHODEV=
+
+# 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"