--- /dev/null
+complete.sh
+start.sh
--- /dev/null
+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'
+
--- /dev/null
+#
+# 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:
--- /dev/null
+ 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/>
--- /dev/null
+# 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"
--- /dev/null
+#!/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
--- /dev/null
+# 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"
--- /dev/null
+# 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"
--- /dev/null
+creat-open
+open-unlink
+flock-copyup
+fsync
+truncate
+bug418
+rmdircheckinode
+rename
+mapper
+queryfile
--- /dev/null
+#
+# 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 *~
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+#!/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 $?
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
+
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
+
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+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
--- /dev/null
+# 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"