[
  [
    "Documentation/filesystems/00-INDEX", 
    "Documentation/filesystems/00-INDEX", 
    [
      "diff --git a/Documentation/filesystems/00-INDEX b/Documentation/filesystems/00-INDEX", 
      "index 59db1bc..40816d4 100644", 
      "--- Documentation/filesystems/00-INDEX", 
      "+++ Documentation/filesystems/00-INDEX", 
      "@@ -86,6 +86,8 @@ udf.txt", 
      "- info and mount options for the UDF filesystem.", 
      "ufs.txt", 
      "- info on the ufs filesystem.", 
      "+unionfs/", 
      "+\t- info on the unionfs filesystem", 
      "vfat.txt", 
      "- info on using the VFAT filesystem used in Windows NT and Windows 95", 
      "vfs.txt"
    ]
  ], 
  [
    "dev/null", 
    "b/Documentation/filesystems/unionfs/00-INDEX", 
    [
      "diff --git a/Documentation/filesystems/unionfs/00-INDEX b/Documentation/filesystems/unionfs/00-INDEX", 
      "new file mode 100644", 
      "index 0000000..96fdf67", 
      "--- dev/null", 
      "+++ b/Documentation/filesystems/unionfs/00-INDEX", 
      "@@ -0,0 +1,10 @@", 
      "+00-INDEX", 
      "+\t- this file.", 
      "+concepts.txt", 
      "+\t- A brief introduction of concepts.", 
      "+issues.txt", 
      "+\t- A summary of known issues with unionfs.", 
      "+rename.txt", 
      "+\t- Information regarding rename operations.", 
      "+usage.txt", 
      "+\t- Usage information and examples."
    ]
  ], 
  [
    "dev/null", 
    "b/Documentation/filesystems/unionfs/concepts.txt", 
    [
      "diff --git a/Documentation/filesystems/unionfs/concepts.txt b/Documentation/filesystems/unionfs/concepts.txt", 
      "new file mode 100644", 
      "index 0000000..b853788", 
      "--- dev/null", 
      "+++ b/Documentation/filesystems/unionfs/concepts.txt", 
      "@@ -0,0 +1,287 @@", 
      "+Unionfs 2.x CONCEPTS:", 
      "+=====================", 
      "+", 
      "+This file describes the concepts needed by a namespace unification file", 
      "+system.", 
      "+", 
      "+", 
      "+Branch Priority:", 
      "+================", 
      "+", 
      "+Each branch is assigned a unique priority - starting from 0 (highest", 
      "+priority).  No two branches can have the same priority.", 
      "+", 
      "+", 
      "+Branch Mode:", 
      "+============", 
      "+", 
      "+Each branch is assigned a mode - read-write or read-only. This allows", 
      "+directories on media mounted read-write to be used in a read-only manner.", 
      "+", 
      "+", 
      "+Whiteouts:", 
      "+==========", 
      "+", 
      "+A whiteout removes a file name from the namespace. Whiteouts are needed when", 
      "+one attempts to remove a file on a read-only branch.", 
      "+", 
      "+Suppose we have a two-branch union, where branch 0 is read-write and branch", 
      "+1 is read-only. And a file 'foo' on branch 1:", 
      "+", 
      "+./b0/", 
      "+./b1/", 
      "+./b1/foo", 
      "+", 
      "+The unified view would simply be:", 
      "+", 
      "+./union/", 
      "+./union/foo", 
      "+", 
      "+Since 'foo' is stored on a read-only branch, it cannot be removed. A", 
      "+whiteout is used to remove the name 'foo' from the unified namespace. Again,", 
      "+since branch 1 is read-only, the whiteout cannot be created there. So, we", 
      "+try on a higher priority (lower numerically) branch and create the whiteout", 
      "+there.", 
      "+", 
      "+./b0/", 
      "+./b0/.wh.foo", 
      "+./b1/", 
      "+./b1/foo", 
      "+", 
      "+Later, when Unionfs traverses branches (due to lookup or readdir), it", 
      "+eliminate 'foo' from the namespace (as well as the whiteout itself.)", 
      "+", 
      "+", 
      "+Opaque Directories:", 
      "+===================", 
      "+", 
      "+Assume we have a unionfs mount comprising of two branches.  Branch 0 is", 
      "+empty; branch 1 has the directory /a and file /a/f.  Let's say we mount a", 
      "+union of branch 0 as read-write and branch 1 as read-only.  Now, let's say", 
      "+we try to perform the following operation in the union:", 
      "+", 
      "+\trm -fr a", 
      "+", 
      "+Because branch 1 is not writable, we cannot physically remove the file /a/f", 
      "+or the directory /a.  So instead, we will create a whiteout in branch 0", 
      "+named /.wh.a, masking out the name \"a\" from branch 1.  Next, let's say we", 
      "+try to create a directory named \"a\" as follows:", 
      "+", 
      "+\tmkdir a", 
      "+", 
      "+Because we have a whiteout for \"a\" already, Unionfs behaves as if \"a\"", 
      "+doesn't exist, and thus will delete the whiteout and replace it with an", 
      "+actual directory named \"a\".", 
      "+", 
      "+The problem now is that if you try to \"ls\" in the union, Unionfs will", 
      "+perform is normal directory name unification, for *all* directories named", 
      "+\"a\" in all branches.  This will cause the file /a/f from branch 1 to", 
      "+re-appear in the union's namespace, which violates Unix semantics.", 
      "+", 
      "+To avoid this problem, we have a different form of whiteouts for", 
      "+directories, called \"opaque directories\" (same as BSD Union Mount does).", 
      "+Whenever we replace a whiteout with a directory, that directory is marked as", 
      "+opaque.  In Unionfs 2.x, it means that we create a file named", 
      "+/a/.wh.__dir_opaque in branch 0, after having created directory /a there.", 
      "+When unionfs notices that a directory is opaque, it stops all namespace", 
      "+operations (including merging readdir contents) at that opaque directory.", 
      "+This prevents re-exposing names from masked out directories.", 
      "+", 
      "+", 
      "+Duplicate Elimination:", 
      "+======================", 
      "+", 
      "+It is possible for files on different branches to have the same name.", 
      "+Unionfs then has to select which instance of the file to show to the user.", 
      "+Given the fact that each branch has a priority associated with it, the", 
      "+simplest solution is to take the instance from the highest priority", 
      "+(numerically lowest value) and \"hide\" the others.", 
      "+", 
      "+", 
      "+Unlinking:", 
      "+=========", 
      "+", 
      "+Unlink operation on non-directory instances is optimized to remove the", 
      "+maximum possible objects in case multiple underlying branches have the same", 
      "+file name.  The unlink operation will first try to delete file instances", 
      "+from highest priority branch and then move further to delete from remaining", 
      "+branches in order of their decreasing priority.  Consider a case (F..D..F),", 
      "+where F is a file and D is a directory of the same name; here, some", 
      "+intermediate branch could have an empty directory instance with the same", 
      "+name, so this operation also tries to delete this directory instance and", 
      "+proceed further to delete from next possible lower priority branch.  The", 
      "+unionfs unlink operation will smoothly delete the files with same name from", 
      "+all possible underlying branches.  In case if some error occurs, it creates", 
      "+whiteout in highest priority branch that will hide file instance in rest of", 
      "+the branches.  An error could occur either if an unlink operations in any of", 
      "+the underlying branch failed or if a branch has no write permission.", 
      "+", 
      "+This unlinking policy is known as \"delete all\" and it has the benefit of", 
      "+overall reducing the number of inodes used by duplicate files, and further", 
      "+reducing the total number of inodes consumed by whiteouts.  The cost is of", 
      "+extra processing, but testing shows this extra processing is well worth the", 
      "+savings.", 
      "+", 
      "+", 
      "+Copyup:", 
      "+=======", 
      "+", 
      "+When a change is made to the contents of a file's data or meta-data, they", 
      "+have to be stored somewhere.  The best way is to create a copy of the", 
      "+original file on a branch that is writable, and then redirect the write", 
      "+though to this copy.  The copy must be made on a higher priority branch so", 
      "+that lookup and readdir return this newer \"version\" of the file rather than", 
      "+the original (see duplicate elimination).", 
      "+", 
      "+An entire unionfs mount can be read-only or read-write.  If it's read-only,", 
      "+then none of the branches will be written to, even if some of the branches", 
      "+are physically writeable.  If the unionfs mount is read-write, then the", 
      "+leftmost (highest priority) branch must be writeable (for copyup to take", 
      "+place); the remaining branches can be any mix of read-write and read-only.", 
      "+", 
      "+In a writeable mount, unionfs will create new files/dir in the leftmost", 
      "+branch.  If one tries to modify a file in a read-only branch/media, unionfs", 
      "+will copyup the file to the leftmost branch and modify it there.  If you try", 
      "+to modify a file from a writeable branch which is not the leftmost branch,", 
      "+then unionfs will modify it in that branch; this is useful if you, say,", 
      "+unify differnet packages (e.g., apache, sendmail, ftpd, etc.) and you want", 
      "+changes to specific package files to remain logically in the directory where", 
      "+they came from.", 
      "+", 
      "+Cache Coherency:", 
      "+================", 
      "+", 
      "+Unionfs users often want to be able to modify files and directories directly", 
      "+on the lower branches, and have those changes be visible at the Unionfs", 
      "+level.  This means that data (e.g., pages) and meta-data (dentries, inodes,", 
      "+open files, etc.) have to be synchronized between the upper and lower", 
      "+layers.  In other words, the newest changes from a layer below have to be", 
      "+propagated to the Unionfs layer above.  If the two layers are not in sync, a", 
      "+cache incoherency ensues, which could lead to application failures and even", 
      "+oopses.  The Linux kernel, however, has a rather limited set of mechanisms", 
      "+to ensure this inter-layer cache coherency---so Unionfs has to do most of", 
      "+the hard work on its own.", 
      "+", 
      "+Maintaining Invariants:", 
      "+", 
      "+The way Unionfs ensures cache coherency is as follows.  At each entry point", 
      "+to a Unionfs file system method, we call a utility function to validate the", 
      "+primary objects of this method.  Generally, we call unionfs_file_revalidate", 
      "+on open files, and __unionfs_d_revalidate_chain on dentries (which also", 
      "+validates inodes).  These utility functions check to see whether the upper", 
      "+Unionfs object is in sync with any of the lower objects that it represents.", 
      "+The checks we perform include whether the Unionfs superblock has a newer", 
      "+generation number, or if any of the lower objects mtime's or ctime's are", 
      "+newer.  (Note: generation numbers change when branch-management commands are", 
      "+issued, so in a way, maintaining cache coherency is also very important for", 
      "+branch-management.)  If indeed we determine that any Unionfs object is no", 
      "+longer in sync with its lower counterparts, then we rebuild that object", 
      "+similarly to how we do so for branch-management.", 
      "+", 
      "+While rebuilding Unionfs's objects, we also purge any page mappings and", 
      "+truncate inode pages (see fs/unionfs/dentry.c:purge_inode_data).  This is to", 
      "+ensure that Unionfs will re-get the newer data from the lower branches.  We", 
      "+perform this purging only if the Unionfs operation in question is a reading", 
      "+operation; if Unionfs is performing a data writing operation (e.g., ->write,", 
      "+->commit_write, etc.) then we do NOT flush the lower mappings/pages: this is", 
      "+because (1) a self-deadlock could occur and (2) the upper Unionfs pages are", 
      "+considered more authoritative anyway, as they are newer and will overwrite", 
      "+any lower pages.", 
      "+", 
      "+Unionfs maintains the following important invariant regarding mtime's,", 
      "+ctime's, and atime's: the upper inode object's times are the max() of all of", 
      "+the lower ones.  For non-directory objects, there's only one object below,", 
      "+so the mapping is simple; for directory objects, there could me multiple", 
      "+lower objects and we have to sync up with the newest one of all the lower", 
      "+ones.  This invariant is important to maintain, especially for directories", 
      "+(besides, we need this to be POSIX compliant).  A union could comprise", 
      "+multiple writable branches, each of which could change.  If we don't reflect", 
      "+the newest possible mtime/ctime, some applications could fail.  For example,", 
      "+NFSv2/v3 exports check for newer directory mtimes on the server to determine", 
      "+if the client-side attribute cache should be purged.", 
      "+", 
      "+To maintain these important invariants, of course, Unionfs carefully", 
      "+synchronizes upper and lower times in various places.  For example, if we", 
      "+copy-up a file to a top-level branch, the parent directory where the file", 
      "+was copied up to will now have a new mtime: so after a successful copy-up,", 
      "+we sync up with the new top-level branch's parent directory mtime.", 
      "+", 
      "+Implementation:", 
      "+", 
      "+This cache-coherency implementation is efficient because it defers any", 
      "+synchronizing between the upper and lower layers until absolutely needed.", 
      "+Consider the example a common situation where users perform a lot of lower", 
      "+changes, such as untarring a whole package.  While these take place,", 
      "+typically the user doesn't access the files via Unionfs; only after the", 
      "+lower changes are done, does the user try to access the lower files.  With", 
      "+our cache-coherency implementation, the entirety of the changes to the lower", 
      "+branches will not result in a single CPU cycle spent at the Unionfs level", 
      "+until the user invokes a system call that goes through Unionfs.", 
      "+", 
      "+We have considered two alternate cache-coherency designs.  (1) Using the", 
      "+dentry/inode notify functionality to register interest in finding out about", 
      "+any lower changes.  This is a somewhat limited and also a heavy-handed", 
      "+approach which could result in many notifications to the Unionfs layer upon", 
      "+each small change at the lower layer (imagine a file being modified multiple", 
      "+times in rapid succession).  (2) Rewriting the VFS to support explicit", 
      "+callbacks from lower objects to upper objects.  We began exploring such an", 
      "+implementation, but found it to be very complicated--it would have resulted", 
      "+in massive VFS/MM changes which are unlikely to be accepted by the LKML", 
      "+community.  We therefore believe that our current cache-coherency design and", 
      "+implementation represent the best approach at this time.", 
      "+", 
      "+Limitations:", 
      "+", 
      "+Our implementation works in that as long as a user process will have caused", 
      "+Unionfs to be called, directly or indirectly, even to just do", 
      "+->d_revalidate; then we will have purged the current Unionfs data and the", 
      "+process will see the new data.  For example, a process that continually", 
      "+re-reads the same file's data will see the NEW data as soon as the lower", 
      "+file had changed, upon the next read(2) syscall (even if the file is still", 
      "+open!)  However, this doesn't work when the process re-reads the open file's", 
      "+data via mmap(2) (unless the user unmaps/closes the file and remaps/reopens", 
      "+it).  Once we respond to ->readpage(s), then the kernel maps the page into", 
      "+the process's address space and there doesn't appear to be a way to force", 
      "+the kernel to invalidate those pages/mappings, and force the process to", 
      "+re-issue ->readpage.  If there's a way to invalidate active mappings and", 
      "+force a ->readpage, let us know please (invalidate_inode_pages2 doesn't do", 
      "+the trick).", 
      "+", 
      "+Our current Unionfs code has to perform many file-revalidation calls.  It", 
      "+would be really nice if the VFS would export an optional file system hook", 
      "+->file_revalidate (similarly to dentry->d_revalidate) that will be called", 
      "+before each VFS op that has a \"struct file\" in it.", 
      "+", 
      "+Certain file systems have micro-second granularity (or better) for inode", 
      "+times, and asynchronous actions could cause those times to change with some", 
      "+small delay.  In such cases, Unionfs may see a changed inode time that only", 
      "+differs by a tiny fraction of a second: such a change may be a false", 
      "+positive indication that the lower object has changed, whereas if unionfs", 
      "+waits a little longer, that false indication will not be seen.  (These false", 
      "+positives are harmless, because they would at most cause unionfs to", 
      "+re-validate an object that may need no revalidation, and print a debugging", 
      "+message that clutters the console/logs.)  Therefore, to minimize the chances", 
      "+of these situations, we delay the detection of changed times by a small", 
      "+factor of a few seconds, called UNIONFS_MIN_CC_TIME (which defaults to 3", 
      "+seconds, as does NFS).  This means that we will detect the change, only a", 
      "+couple of seconds later, if indeed the time change persists in the lower", 
      "+file object.  This delayed detection has an added performance benefit: we", 
      "+reduce the number of times that unionfs has to revalidate objects, in case", 
      "+there's a lot of concurrent activity on both the upper and lower objects,", 
      "+for the same file(s).  Lastly, this delayed time attribute detection is", 
      "+similar to how NFS clients operate (e.g., acregmin).", 
      "+", 
      "+Finally, there is no way currently in Linux to prevent lower directories", 
      "+from being moved around (i.e., topology changes); there's no way to prevent", 
      "+modifications to directory sub-trees of whole file systems which are mounted", 
      "+read-write.  It is therefore possible for in-flight operations in unionfs to", 
      "+take place, while a lower directory is being moved around.  Therefore, if", 
      "+you try to, say, create a new file in a directory through unionfs, while the", 
      "+directory is being moved around directly, then the new file may get created", 
      "+in the new location where that directory was moved to.  This is a somewhat", 
      "+similar behaviour in NFS: an NFS client could be creating a new file while", 
      "+th NFS server is moving th directory around; the file will get successfully", 
      "+created in the new location.  (The one exception in unionfs is that if the", 
      "+branch is marked read-only by unionfs, then a copyup will take place.)", 
      "+", 
      "+For more information, see <http://unionfs.filesystems.org/>."
    ]
  ], 
  [
    "dev/null", 
    "b/Documentation/filesystems/unionfs/issues.txt", 
    [
      "diff --git a/Documentation/filesystems/unionfs/issues.txt b/Documentation/filesystems/unionfs/issues.txt", 
      "new file mode 100644", 
      "index 0000000..f4b7e7e", 
      "--- dev/null", 
      "+++ b/Documentation/filesystems/unionfs/issues.txt", 
      "@@ -0,0 +1,28 @@", 
      "+KNOWN Unionfs 2.x ISSUES:", 
      "+=========================", 
      "+", 
      "+1. Unionfs should not use lookup_one_len() on the underlying f/s as it", 
      "+   confuses NFSv4.  Currently, unionfs_lookup() passes lookup intents to the", 
      "+   lower file-system, this eliminates part of the problem.  The remaining", 
      "+   calls to lookup_one_len may need to be changed to pass an intent.  We are", 
      "+   currently introducing VFS changes to fs/namei.c's do_path_lookup() to", 
      "+   allow proper file lookup and opening in stackable file systems.", 
      "+", 
      "+2. Lockdep (a debugging feature) isn't aware of stacking, and so it", 
      "+   incorrectly complains about locking problems.  The problem boils down to", 
      "+   this: Lockdep considers all objects of a certain type to be in the same", 
      "+   class, for example, all inodes.  Lockdep doesn't like to see a lock held", 
      "+   on two inodes within the same task, and warns that it could lead to a", 
      "+   deadlock.  However, stackable file systems do precisely that: they lock", 
      "+   an upper object, and then a lower object, in a strict order to avoid", 
      "+   locking problems; in addition, Unionfs, as a fan-out file system, may", 
      "+   have to lock several lower inodes.  We are currently looking into Lockdep", 
      "+   to see how to make it aware of stackable file systems.  For now, we", 
      "+   temporarily disable lockdep when calling vfs methods on lower objects,", 
      "+   but only for those places where lockdep complained.  While this solution", 
      "+   may seem unclean, it is not without precedent: other places in the kernel", 
      "+   also do similar temporary disabling, of course after carefully having", 
      "+   checked that it is the right thing to do.  Anyway, you get any warnings", 
      "+   from Lockdep, please report them to the Unionfs maintainers.", 
      "+", 
      "+For more information, see <http://unionfs.filesystems.org/>."
    ]
  ], 
  [
    "dev/null", 
    "b/Documentation/filesystems/unionfs/rename.txt", 
    [
      "diff --git a/Documentation/filesystems/unionfs/rename.txt b/Documentation/filesystems/unionfs/rename.txt", 
      "new file mode 100644", 
      "index 0000000..e20bb82", 
      "--- dev/null", 
      "+++ b/Documentation/filesystems/unionfs/rename.txt", 
      "@@ -0,0 +1,31 @@", 
      "+Rename is a complex beast. The following table shows which rename(2) operations", 
      "+should succeed and which should fail.", 
      "+", 
      "+o: success", 
      "+E: error (either unionfs or vfs)", 
      "+X: EXDEV", 
      "+", 
      "+none = file does not exist", 
      "+file = file is a file", 
      "+dir  = file is a empty directory", 
      "+child= file is a non-empty directory", 
      "+wh   = file is a directory containing only whiteouts; this makes it logically", 
      "+\t\tempty", 
      "+", 
      "+                      none    file    dir     child   wh", 
      "+file                  o       o       E       E       E", 
      "+dir                   o       E       o       E       o", 
      "+child                 X       E       X       E       X", 
      "+wh                    o       E       o       E       o", 
      "+", 
      "+", 
      "+Renaming directories:", 
      "+=====================", 
      "+", 
      "+Whenever a empty (either physically or logically) directory is being renamed,", 
      "+the following sequence of events should take place:", 
      "+", 
      "+1) Remove whiteouts from both source and destination directory", 
      "+2) Rename source to destination", 
      "+3) Make destination opaque to prevent anything under it from showing up", 
      "+"
    ]
  ], 
  [
    "dev/null", 
    "b/Documentation/filesystems/unionfs/usage.txt", 
    [
      "diff --git a/Documentation/filesystems/unionfs/usage.txt b/Documentation/filesystems/unionfs/usage.txt", 
      "new file mode 100644", 
      "index 0000000..1adde69", 
      "--- dev/null", 
      "+++ b/Documentation/filesystems/unionfs/usage.txt", 
      "@@ -0,0 +1,134 @@", 
      "+Unionfs is a stackable unification file system, which can appear to merge", 
      "+the contents of several directories (branches), while keeping their physical", 
      "+content separate.  Unionfs is useful for unified source tree management,", 
      "+merged contents of split CD-ROM, merged separate software package", 
      "+directories, data grids, and more.  Unionfs allows any mix of read-only and", 
      "+read-write branches, as well as insertion and deletion of branches anywhere", 
      "+in the fan-out.  To maintain Unix semantics, Unionfs handles elimination of", 
      "+duplicates, partial-error conditions, and more.", 
      "+", 
      "+GENERAL SYNTAX", 
      "+==============", 
      "+", 
      "+# mount -t unionfs -o <OPTIONS>,<BRANCH-OPTIONS> none MOUNTPOINT", 
      "+", 
      "+OPTIONS can be any legal combination of:", 
      "+", 
      "+- ro\t\t# mount file system read-only", 
      "+- rw\t\t# mount file system read-write", 
      "+- remount\t# remount the file system (see Branch Management below)", 
      "+- incgen\t# increment generation no. (see Cache Consistency below)", 
      "+", 
      "+BRANCH-OPTIONS can be either (1) a list of branches given to the \"dirs=\"", 
      "+option, or (2) a list of individual branch manipulation commands, combined", 
      "+with the \"remount\" option, and is further described in the \"Branch", 
      "+Management\" section below.", 
      "+", 
      "+The syntax for the \"dirs=\" mount option is:", 
      "+", 
      "+\tdirs=branch[=ro|=rw][:...]", 
      "+", 
      "+The \"dirs=\" option takes a colon-delimited list of directories to compose", 
      "+the union, with an optional branch mode for each of those directories.", 
      "+Directories that come earlier (specified first, on the left) in the list", 
      "+have a higher precedence than those which come later.  Additionally,", 
      "+read-only or read-write permissions of the branch can be specified by", 
      "+appending =ro or =rw (default) to each directory.  See the Copyup section in", 
      "+concepts.txt, for a description of Unionfs's behavior when mixing read-only", 
      "+and read-write branches and mounts.", 
      "+", 
      "+Syntax:", 
      "+", 
      "+\tdirs=/branch1[=ro|=rw]:/branch2[=ro|=rw]:...:/branchN[=ro|=rw]", 
      "+", 
      "+Example:", 
      "+", 
      "+\tdirs=/writable_branch=rw:/read-only_branch=ro", 
      "+", 
      "+", 
      "+BRANCH MANAGEMENT", 
      "+=================", 
      "+", 
      "+Once you mount your union for the first time, using the \"dirs=\" option, you", 
      "+can then change the union's overall mode or reconfigure the branches, using", 
      "+the remount option, as follows.", 
      "+", 
      "+To downgrade a union from read-write to read-only:", 
      "+", 
      "+# mount -t unionfs -o remount,ro none MOUNTPOINT", 
      "+", 
      "+To upgrade a union from read-only to read-write:", 
      "+", 
      "+# mount -t unionfs -o remount,rw none MOUNTPOINT", 
      "+", 
      "+To delete a branch /foo, regardless where it is in the current union:", 
      "+", 
      "+# mount -t unionfs -o remount,del=/foo none MOUNTPOINT", 
      "+", 
      "+To insert (add) a branch /foo before /bar:", 
      "+", 
      "+# mount -t unionfs -o remount,add=/bar:/foo none MOUNTPOINT", 
      "+", 
      "+To insert (add) a branch /foo (with the \"rw\" mode flag) before /bar:", 
      "+", 
      "+# mount -t unionfs -o remount,add=/bar:/foo=rw none MOUNTPOINT", 
      "+", 
      "+To insert (add) a branch /foo (in \"rw\" mode) at the very beginning (i.e., a", 
      "+new highest-priority branch), you can use the above syntax, or use a short", 
      "+hand version as follows:", 
      "+", 
      "+# mount -t unionfs -o remount,add=/foo none MOUNTPOINT", 
      "+", 
      "+To append a branch to the very end (new lowest-priority branch):", 
      "+", 
      "+# mount -t unionfs -o remount,add=:/foo none MOUNTPOINT", 
      "+", 
      "+To append a branch to the very end (new lowest-priority branch), in", 
      "+read-only mode:", 
      "+", 
      "+# mount -t unionfs -o remount,add=:/foo=ro none MOUNTPOINT", 
      "+", 
      "+Finally, to change the mode of one existing branch, say /foo, from read-only", 
      "+to read-write, and change /bar from read-write to read-only:", 
      "+", 
      "+# mount -t unionfs -o remount,mode=/foo=rw,mode=/bar=ro none MOUNTPOINT", 
      "+", 
      "+Note: in Unionfs 2.x, you cannot set the leftmost branch to readonly because", 
      "+then Unionfs won't have any writable place for copyups to take place.", 
      "+Moreover, the VFS can get confused when it tries to modify something in a", 
      "+file system mounted read-write, but isn't permitted to write to it.", 
      "+Instead, you should set the whole union as readonly, as described above.", 
      "+If, however, you must set the leftmost branch as readonly, perhaps so you", 
      "+can get a snapshot of it at a point in time, then you should insert a new", 
      "+writable top-level branch, and mark the one you want as readonly.  This can", 
      "+be accomplished as follows, assuming that /foo is your current leftmost", 
      "+branch:", 
      "+", 
      "+# mount -t tmpfs -o size=NNN /new", 
      "+# mount -t unionfs -o remount,add=/new,mode=/foo=ro none MOUNTPOINT", 
      "+<do what you want safely in /foo>", 
      "+# mount -t unionfs -o remount,del=/new,mode=/foo=rw none MOUNTPOINT", 
      "+<check if there's anything in /new you want to preserve>", 
      "+# umount /new", 
      "+", 
      "+CACHE CONSISTENCY", 
      "+=================", 
      "+", 
      "+If you modify any file on any of the lower branches directly, while there is", 
      "+a Unionfs 2.x mounted above any of those branches, you should tell Unionfs", 
      "+to purge its caches and re-get the objects.  To do that, you have to", 
      "+increment the generation number of the superblock using the following", 
      "+command:", 
      "+", 
      "+# mount -t unionfs -o remount,incgen none MOUNTPOINT", 
      "+", 
      "+Note that the older way of incrementing the generation number using an", 
      "+ioctl, is no longer supported in Unionfs 2.0 and newer.  Ioctls in general", 
      "+are not encouraged.  Plus, an ioctl is per-file concept, whereas the", 
      "+generation number is a per-file-system concept.  Worse, such an ioctl", 
      "+requires an open file, which then has to be invalidated by the very nature", 
      "+of the generation number increase (read: the old generation increase ioctl", 
      "+was pretty racy).", 
      "+", 
      "+", 
      "+For more information, see <http://unionfs.filesystems.org/>."
    ]
  ], 
  [
    "MAINTAINERS", 
    "MAINTAINERS", 
    [
      "diff --git a/MAINTAINERS b/MAINTAINERS", 
      "index 9a91d9e..87e1e63 100644", 
      "--- MAINTAINERS", 
      "+++ MAINTAINERS", 
      "@@ -3740,6 +3740,14 @@ L:\tlinux-kernel@vger.kernel.org", 
      "W:\thttp://www.kernel.dk", 
      "S:\tMaintained", 
      "", 
      "+UNIONFS", 
      "+P:\tErez Zadok", 
      "+M:\tezk@cs.sunysb.edu", 
      "+L:\tunionfs@filesystems.org", 
      "+W:\thttp://unionfs.filesystems.org", 
      "+T:\tgit git.kernel.org/pub/scm/linux/kernel/git/ezk/unionfs.git", 
      "+S:\tMaintained", 
      "+", 
      "USB ACM DRIVER", 
      "P:\tOliver Neukum", 
      "M:\toliver@neukum.name"
    ]
  ], 
  [
    "fs/Kconfig", 
    "fs/Kconfig", 
    [
      "diff --git a/fs/Kconfig b/fs/Kconfig", 
      "index f9eed6d..9afb8df 100644", 
      "--- fs/Kconfig", 
      "+++ fs/Kconfig", 
      "@@ -1027,6 +1027,47 @@ config CONFIGFS_FS", 
      "", 
      "endmenu", 
      "", 
      "+menu \"Layered filesystems\"", 
      "+", 
      "+config ECRYPT_FS", 
      "+\ttristate \"eCrypt filesystem layer support (EXPERIMENTAL)\"", 
      "+\tdepends on EXPERIMENTAL && KEYS && CRYPTO && NET", 
      "+\thelp", 
      "+\t  Encrypted filesystem that operates on the VFS layer.  See", 
      "+\t  <file:Documentation/ecryptfs.txt> to learn more about", 
      "+\t  eCryptfs.  Userspace components are required and can be", 
      "+\t  obtained from <http://ecryptfs.sf.net>.", 
      "+", 
      "+\t  To compile this file system support as a module, choose M here: the", 
      "+\t  module will be called ecryptfs.", 
      "+", 
      "+config UNION_FS", 
      "+\ttristate \"Union file system (EXPERIMENTAL)\"", 
      "+\tdepends on EXPERIMENTAL", 
      "+\thelp", 
      "+\t  Unionfs is a stackable unification file system, which appears to", 
      "+\t  merge the contents of several directories (branches), while keeping", 
      "+\t  their physical content separate.", 
      "+", 
      "+\t  See <http://unionfs.filesystems.org> for details", 
      "+", 
      "+config UNION_FS_XATTR", 
      "+\tbool \"Unionfs extended attributes\"", 
      "+\tdepends on UNION_FS", 
      "+\thelp", 
      "+\t  Extended attributes are name:value pairs associated with inodes by", 
      "+\t  the kernel or by users (see the attr(5) manual page).", 
      "+", 
      "+\t  If unsure, say N.", 
      "+", 
      "+config UNION_FS_DEBUG", 
      "+\tbool \"Debug Unionfs\"", 
      "+\tdepends on UNION_FS", 
      "+\thelp", 
      "+\t  If you say Y here, you can turn on debugging output from Unionfs.", 
      "+", 
      "+endmenu", 
      "+", 
      "menu \"Miscellaneous filesystems\"", 
      "", 
      "config ADFS_FS", 
      "@@ -1079,18 +1120,6 @@ config AFFS_FS", 
      "To compile this file system support as a module, choose M here: the", 
      "module will be called affs.  If unsure, say N.", 
      "", 
      "-config ECRYPT_FS", 
      "-\ttristate \"eCrypt filesystem layer support (EXPERIMENTAL)\"", 
      "-\tdepends on EXPERIMENTAL && KEYS && CRYPTO && NET", 
      "-\thelp", 
      "-\t  Encrypted filesystem that operates on the VFS layer.  See", 
      "-\t  <file:Documentation/ecryptfs.txt> to learn more about", 
      "-\t  eCryptfs.  Userspace components are required and can be", 
      "-\t  obtained from <http://ecryptfs.sf.net>.", 
      "-", 
      "-\t  To compile this file system support as a module, choose M here: the", 
      "-\t  module will be called ecryptfs.", 
      "-", 
      "config HFS_FS", 
      "tristate \"Apple Macintosh file system support (EXPERIMENTAL)\"", 
      "depends on BLOCK && EXPERIMENTAL"
    ]
  ], 
  [
    "fs/Makefile", 
    "fs/Makefile", 
    [
      "diff --git a/fs/Makefile b/fs/Makefile", 
      "index 720c29d..f1301f3 100644", 
      "--- fs/Makefile", 
      "+++ fs/Makefile", 
      "@@ -84,6 +84,7 @@ obj-$(CONFIG_ISO9660_FS)\t+= isofs/", 
      "obj-$(CONFIG_HFSPLUS_FS)\t+= hfsplus/ # Before hfs to find wrapped HFS+", 
      "obj-$(CONFIG_HFS_FS)\t\t+= hfs/", 
      "obj-$(CONFIG_ECRYPT_FS)\t\t+= ecryptfs/", 
      "+obj-$(CONFIG_UNION_FS)\t\t+= unionfs/", 
      "obj-$(CONFIG_VXFS_FS)\t\t+= freevxfs/", 
      "obj-$(CONFIG_NFS_FS)\t\t+= nfs/", 
      "obj-$(CONFIG_EXPORTFS)\t\t+= exportfs/"
    ]
  ], 
  [
    "fs/ecryptfs/dentry.c", 
    "fs/ecryptfs/dentry.c", 
    [
      "diff --git a/fs/ecryptfs/dentry.c b/fs/ecryptfs/dentry.c", 
      "index cb20b96..a8c1686 100644", 
      "--- fs/ecryptfs/dentry.c", 
      "+++ fs/ecryptfs/dentry.c", 
      "@@ -62,7 +62,7 @@ static int ecryptfs_d_revalidate(struct dentry *dentry, struct nameidata *nd)", 
      "struct inode *lower_inode =", 
      "ecryptfs_inode_to_lower(dentry->d_inode);", 
      "", 
      "-\t\tfsstack_copy_attr_all(dentry->d_inode, lower_inode, NULL);", 
      "+\t\tfsstack_copy_attr_all(dentry->d_inode, lower_inode);", 
      "}", 
      "out:", 
      "return rc;"
    ]
  ], 
  [
    "fs/ecryptfs/inode.c", 
    "fs/ecryptfs/inode.c", 
    [
      "diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c", 
      "index 131954b..fc4c6cb 100644", 
      "--- fs/ecryptfs/inode.c", 
      "+++ fs/ecryptfs/inode.c", 
      "@@ -601,9 +601,9 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,", 
      "lower_new_dir_dentry->d_inode, lower_new_dentry);", 
      "if (rc)", 
      "goto out_lock;", 
      "-\tfsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode, NULL);", 
      "+\tfsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode);", 
      "if (new_dir != old_dir)", 
      "-\t\tfsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode, NULL);", 
      "+\t\tfsstack_copy_attr_all(old_dir, lower_old_dir_dentry->d_inode);", 
      "out_lock:", 
      "unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);", 
      "dput(lower_new_dentry->d_parent);", 
      "@@ -961,7 +961,7 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)", 
      "}", 
      "rc = notify_change(lower_dentry, ia);", 
      "out:", 
      "-\tfsstack_copy_attr_all(inode, lower_inode, NULL);", 
      "+\tfsstack_copy_attr_all(inode, lower_inode);", 
      "return rc;", 
      "}", 
      ""
    ]
  ], 
  [
    "fs/ecryptfs/main.c", 
    "fs/ecryptfs/main.c", 
    [
      "diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c", 
      "index a984972..cb349a4 100644", 
      "--- fs/ecryptfs/main.c", 
      "+++ fs/ecryptfs/main.c", 
      "@@ -151,7 +151,7 @@ int ecryptfs_interpose(struct dentry *lower_dentry, struct dentry *dentry,", 
      "d_add(dentry, inode);", 
      "else", 
      "d_instantiate(dentry, inode);", 
      "-\tfsstack_copy_attr_all(inode, lower_inode, NULL);", 
      "+\tfsstack_copy_attr_all(inode, lower_inode);", 
      "/* This size will be overwritten for real files w/ headers and", 
      "* other metadata */", 
      "fsstack_copy_inode_size(inode, lower_inode);"
    ]
  ], 
  [
    "fs/namei.c", 
    "fs/namei.c", 
    [
      "diff --git a/fs/namei.c b/fs/namei.c", 
      "index 314afe6..c15ce85 100644", 
      "--- fs/namei.c", 
      "+++ fs/namei.c", 
      "@@ -374,6 +374,7 @@ void release_open_intent(struct nameidata *nd)", 
      "else", 
      "fput(nd->intent.open.file);", 
      "}", 
      "+EXPORT_SYMBOL_GPL(release_open_intent);", 
      "", 
      "static inline struct dentry *", 
      "do_revalidate(struct dentry *dentry, struct nameidata *nd)"
    ]
  ], 
  [
    "fs/splice.c", 
    "fs/splice.c", 
    [
      "diff --git a/fs/splice.c b/fs/splice.c", 
      "index 1a9c0e6..f931be4 100644", 
      "--- fs/splice.c", 
      "+++ fs/splice.c", 
      "@@ -943,8 +943,8 @@ EXPORT_SYMBOL(generic_splice_sendpage);", 
      "/*", 
      "* Attempt to initiate a splice from pipe to file.", 
      "*/", 
      "-static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,", 
      "-\t\t\t   loff_t *ppos, size_t len, unsigned int flags)", 
      "+long vfs_splice_from(struct pipe_inode_info *pipe, struct file *out,", 
      "+\t\t     loff_t *ppos, size_t len, unsigned int flags)", 
      "{", 
      "int ret;", 
      "", 
      "@@ -964,13 +964,14 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,", 
      "", 
      "return out->f_op->splice_write(pipe, out, ppos, len, flags);", 
      "}", 
      "+EXPORT_SYMBOL_GPL(vfs_splice_from);", 
      "", 
      "/*", 
      "* Attempt to initiate a splice from a file to a pipe.", 
      "*/", 
      "-static long do_splice_to(struct file *in, loff_t *ppos,", 
      "-\t\t\t struct pipe_inode_info *pipe, size_t len,", 
      "-\t\t\t unsigned int flags)", 
      "+long vfs_splice_to(struct file *in, loff_t *ppos,", 
      "+\t\t   struct pipe_inode_info *pipe, size_t len,", 
      "+\t\t   unsigned int flags)", 
      "{", 
      "int ret;", 
      "", 
      "@@ -990,6 +991,7 @@ static long do_splice_to(struct file *in, loff_t *ppos,", 
      "", 
      "return in->f_op->splice_read(in, ppos, pipe, len, flags);", 
      "}", 
      "+EXPORT_SYMBOL_GPL(vfs_splice_to);", 
      "", 
      "/**", 
      "* splice_direct_to_actor - splices data directly between two non-pipes", 
      "@@ -1059,7 +1061,7 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd,", 
      "size_t read_len;", 
      "loff_t pos = sd->pos;", 
      "", 
      "-\t\tret = do_splice_to(in, &pos, pipe, len, flags);", 
      "+\t\tret = vfs_splice_to(in, &pos, pipe, len, flags);", 
      "if (unlikely(ret <= 0))", 
      "goto out_release;", 
      "", 
      "@@ -1117,7 +1119,7 @@ static int direct_splice_actor(struct pipe_inode_info *pipe,", 
      "{", 
      "struct file *file = sd->u.file;", 
      "", 
      "-\treturn do_splice_from(pipe, file, &sd->pos, sd->total_len, sd->flags);", 
      "+\treturn vfs_splice_from(pipe, file, &sd->pos, sd->total_len, sd->flags);", 
      "}", 
      "", 
      "/**", 
      "@@ -1191,7 +1193,7 @@ static long do_splice(struct file *in, loff_t __user *off_in,", 
      "} else", 
      "off = &out->f_pos;", 
      "", 
      "-\t\tret = do_splice_from(pipe, out, off, len, flags);", 
      "+\t\tret = vfs_splice_from(pipe, out, off, len, flags);", 
      "", 
      "if (off_out && copy_to_user(off_out, off, sizeof(loff_t)))", 
      "ret = -EFAULT;", 
      "@@ -1212,7 +1214,7 @@ static long do_splice(struct file *in, loff_t __user *off_in,", 
      "} else", 
      "off = &in->f_pos;", 
      "", 
      "-\t\tret = do_splice_to(in, off, pipe, len, flags);", 
      "+\t\tret = vfs_splice_to(in, off, pipe, len, flags);", 
      "", 
      "if (off_in && copy_to_user(off_in, off, sizeof(loff_t)))", 
      "ret = -EFAULT;"
    ]
  ], 
  [
    "fs/stack.c", 
    "fs/stack.c", 
    [
      "diff --git a/fs/stack.c b/fs/stack.c", 
      "index 67716f6..cc1443d 100644", 
      "--- fs/stack.c", 
      "+++ fs/stack.c", 
      "@@ -1,24 +1,82 @@", 
      "+/*", 
      "+ * Copyright (c) 2006-2009 Erez Zadok", 
      "+ * Copyright (c) 2006-2007 Josef 'Jeff' Sipek", 
      "+ * Copyright (c) 2006-2009 Stony Brook University", 
      "+ * Copyright (c) 2006-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "#include <linux/module.h>", 
      "#include <linux/fs.h>", 
      "#include <linux/fs_stack.h>", 
      "", 
      "-/* does _NOT_ require i_mutex to be held.", 
      "+/*", 
      "+ * does _NOT_ require i_mutex to be held.", 
      "*", 
      "* This function cannot be inlined since i_size_{read,write} is rather", 
      "* heavy-weight on 32-bit systems", 
      "*/", 
      "-void fsstack_copy_inode_size(struct inode *dst, const struct inode *src)", 
      "+void fsstack_copy_inode_size(struct inode *dst, struct inode *src)", 
      "{", 
      "-\ti_size_write(dst, i_size_read((struct inode *)src));", 
      "-\tdst->i_blocks = src->i_blocks;", 
      "+\tloff_t i_size;", 
      "+\tblkcnt_t i_blocks;", 
      "+", 
      "+\t/*", 
      "+\t * i_size_read() includes its own seqlocking and protection from", 
      "+\t * preemption (see include/linux/fs.h): we need nothing extra for", 
      "+\t * that here, and prefer to avoid nesting locks than attempt to", 
      "+\t * keep i_size and i_blocks in synch together.", 
      "+\t */", 
      "+\ti_size = i_size_read(src);", 
      "+", 
      "+\t/*", 
      "+\t * But if CONFIG_LSF (on 32-bit), we ought to make an effort to keep", 
      "+\t * the two halves of i_blocks in synch despite SMP or PREEMPT - though", 
      "+\t * stat's generic_fillattr() doesn't bother, and we won't be applying", 
      "+\t * quotas (where i_blocks does become important) at the upper level.", 
      "+\t *", 
      "+\t * We don't actually know what locking is used at the lower level; but", 
      "+\t * if it's a filesystem that supports quotas, it will be using i_lock", 
      "+\t * as in inode_add_bytes().  tmpfs uses other locking, and its 32-bit", 
      "+\t * is (just) able to exceed 2TB i_size with the aid of holes; but its", 
      "+\t * i_blocks cannot carry into the upper long without almost 2TB swap -", 
      "+\t * let's ignore that case.", 
      "+\t */", 
      "+\tif (sizeof(i_blocks) > sizeof(long))", 
      "+\t\tspin_lock(&src->i_lock);", 
      "+\ti_blocks = src->i_blocks;", 
      "+\tif (sizeof(i_blocks) > sizeof(long))", 
      "+\t\tspin_unlock(&src->i_lock);", 
      "+", 
      "+\t/*", 
      "+\t * If CONFIG_SMP on 32-bit, it's vital for fsstack_copy_inode_size()", 
      "+\t * to hold some lock around i_size_write(), otherwise i_size_read()", 
      "+\t * may spin forever (see include/linux/fs.h).  We don't necessarily", 
      "+\t * hold i_mutex when this is called, so take i_lock for that case.", 
      "+\t *", 
      "+\t * And if CONFIG_LSF (on 32-bit), continue our effort to keep the", 
      "+\t * two halves of i_blocks in synch despite SMP or PREEMPT: use i_lock", 
      "+\t * for that case too, and do both at once by combining the tests.", 
      "+\t *", 
      "+\t * There is none of this locking overhead in the 64-bit case.", 
      "+\t */", 
      "+\tif (sizeof(i_size) > sizeof(long) || sizeof(i_blocks) > sizeof(long))", 
      "+\t\tspin_lock(&dst->i_lock);", 
      "+\ti_size_write(dst, i_size);", 
      "+\tdst->i_blocks = i_blocks;", 
      "+\tif (sizeof(i_size) > sizeof(long) || sizeof(i_blocks) > sizeof(long))", 
      "+\t\tspin_unlock(&dst->i_lock);", 
      "}", 
      "EXPORT_SYMBOL_GPL(fsstack_copy_inode_size);", 
      "", 
      "-/* copy all attributes; get_nlinks is optional way to override the i_nlink", 
      "+/*", 
      "+ * copy all attributes; get_nlinks is optional way to override the i_nlink", 
      "* copying", 
      "*/", 
      "-void fsstack_copy_attr_all(struct inode *dest, const struct inode *src,", 
      "-\t\t\t\tint (*get_nlinks)(struct inode *))", 
      "+void fsstack_copy_attr_all(struct inode *dest, const struct inode *src)", 
      "{", 
      "dest->i_mode = src->i_mode;", 
      "dest->i_uid = src->i_uid;", 
      "@@ -29,14 +87,6 @@ void fsstack_copy_attr_all(struct inode *dest, const struct inode *src,", 
      "dest->i_ctime = src->i_ctime;", 
      "dest->i_blkbits = src->i_blkbits;", 
      "dest->i_flags = src->i_flags;", 
      "-", 
      "-\t/*", 
      "-\t * Update the nlinks AFTER updating the above fields, because the", 
      "-\t * get_links callback may depend on them.", 
      "-\t */", 
      "-\tif (!get_nlinks)", 
      "-\t\tdest->i_nlink = src->i_nlink;", 
      "-\telse", 
      "-\t\tdest->i_nlink = (*get_nlinks)(dest);", 
      "+\tdest->i_nlink = src->i_nlink;", 
      "}", 
      "EXPORT_SYMBOL_GPL(fsstack_copy_attr_all);"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/Makefile", 
    [
      "diff --git a/fs/unionfs/Makefile b/fs/unionfs/Makefile", 
      "new file mode 100644", 
      "index 0000000..1d71ee7", 
      "--- dev/null", 
      "+++ b/fs/unionfs/Makefile", 
      "@@ -0,0 +1,17 @@", 
      "+UNIONFS_VERSION=\"2.5.1 (for 2.6.23.17)\"", 
      "+", 
      "+EXTRA_CFLAGS += -DUNIONFS_VERSION=\\\"$(UNIONFS_VERSION)\\\"", 
      "+", 
      "+obj-$(CONFIG_UNION_FS) += unionfs.o", 
      "+", 
      "+unionfs-y := subr.o dentry.o file.o inode.o main.o super.o \\", 
      "+\trdstate.o copyup.o dirhelper.o rename.o unlink.o \\", 
      "+\tlookup.o commonfops.o dirfops.o sioq.o mmap.o whiteout.o", 
      "+", 
      "+unionfs-$(CONFIG_UNION_FS_XATTR) += xattr.o", 
      "+", 
      "+unionfs-$(CONFIG_UNION_FS_DEBUG) += debug.o", 
      "+", 
      "+ifeq ($(CONFIG_UNION_FS_DEBUG),y)", 
      "+EXTRA_CFLAGS += -DDEBUG", 
      "+endif"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/commonfops.c", 
    [
      "diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c", 
      "new file mode 100644", 
      "index 0000000..63ee085", 
      "--- dev/null", 
      "+++ b/fs/unionfs/commonfops.c", 
      "@@ -0,0 +1,879 @@", 
      "+/*", 
      "+ * Copyright (c) 2003-2009 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-2009 Stony Brook University", 
      "+ * Copyright (c) 2003-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#include \"union.h\"", 
      "+", 
      "+/*", 
      "+ * 1) Copyup the file", 
      "+ * 2) Rename the file to '.unionfs<original inode#><counter>' - obviously", 
      "+ * stolen from NFS's silly rename", 
      "+ */", 
      "+static int copyup_deleted_file(struct file *file, struct dentry *dentry,", 
      "+\t\t\t       struct dentry *parent, int bstart, int bindex)", 
      "+{", 
      "+\tstatic unsigned int counter;", 
      "+\tconst int i_inosize = sizeof(dentry->d_inode->i_ino) * 2;", 
      "+\tconst int countersize = sizeof(counter) * 2;", 
      "+\tconst int nlen = sizeof(\".unionfs\") + i_inosize + countersize - 1;", 
      "+\tchar name[nlen + 1];", 
      "+\tint err;", 
      "+\tstruct dentry *tmp_dentry = NULL;", 
      "+\tstruct dentry *lower_dentry;", 
      "+\tstruct dentry *lower_dir_dentry = NULL;", 
      "+", 
      "+\tlower_dentry = unionfs_lower_dentry_idx(dentry, bstart);", 
      "+", 
      "+\tsprintf(name, \".unionfs%*.*lx\",", 
      "+\t\ti_inosize, i_inosize, lower_dentry->d_inode->i_ino);", 
      "+", 
      "+\t/*", 
      "+\t * Loop, looking for an unused temp name to copyup to.", 
      "+\t *", 
      "+\t * It's somewhat silly that we look for a free temp tmp name in the", 
      "+\t * source branch (bstart) instead of the dest branch (bindex), where", 
      "+\t * the final name will be created.  We _will_ catch it if somehow", 
      "+\t * the name exists in the dest branch, but it'd be nice to catch it", 
      "+\t * sooner than later.", 
      "+\t */", 
      "+retry:", 
      "+\ttmp_dentry = NULL;", 
      "+\tdo {", 
      "+\t\tchar *suffix = name + nlen - countersize;", 
      "+", 
      "+\t\tdput(tmp_dentry);", 
      "+\t\tcounter++;", 
      "+\t\tsprintf(suffix, \"%*.*x\", countersize, countersize, counter);", 
      "+", 
      "+\t\tpr_debug(\"unionfs: trying to rename %s to %s\\n\",", 
      "+\t\t\t dentry->d_name.name, name);", 
      "+", 
      "+\t\ttmp_dentry = lookup_one_len(name, lower_dentry->d_parent,", 
      "+\t\t\t\t\t    nlen);", 
      "+\t\tif (IS_ERR(tmp_dentry)) {", 
      "+\t\t\terr = PTR_ERR(tmp_dentry);", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+\t} while (tmp_dentry->d_inode != NULL);\t/* need negative dentry */", 
      "+\tdput(tmp_dentry);", 
      "+", 
      "+\terr = copyup_named_file(parent->d_inode, file, name, bstart, bindex,", 
      "+\t\t\t\ti_size_read(file->f_path.dentry->d_inode));", 
      "+\tif (err) {", 
      "+\t\tif (unlikely(err == -EEXIST))", 
      "+\t\t\tgoto retry;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/* bring it to the same state as an unlinked file */", 
      "+\tlower_dentry = unionfs_lower_dentry_idx(dentry, dbstart(dentry));", 
      "+\tif (!unionfs_lower_inode_idx(dentry->d_inode, bindex)) {", 
      "+\t\tatomic_inc(&lower_dentry->d_inode->i_count);", 
      "+\t\tunionfs_set_lower_inode_idx(dentry->d_inode, bindex,", 
      "+\t\t\t\t\t    lower_dentry->d_inode);", 
      "+\t}", 
      "+\tlower_dir_dentry = lock_parent(lower_dentry);", 
      "+\terr = vfs_unlink(lower_dir_dentry->d_inode, lower_dentry);", 
      "+\tunlock_dir(lower_dir_dentry);", 
      "+", 
      "+out:", 
      "+\tif (!err)", 
      "+\t\tunionfs_check_dentry(dentry);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * put all references held by upper struct file and free lower file pointer", 
      "+ * array", 
      "+ */", 
      "+static void cleanup_file(struct file *file)", 
      "+{", 
      "+\tint bindex, bstart, bend;", 
      "+\tstruct file **lower_files;", 
      "+\tstruct file *lower_file;", 
      "+\tstruct super_block *sb = file->f_path.dentry->d_sb;", 
      "+", 
      "+\tlower_files = UNIONFS_F(file)->lower_files;", 
      "+\tbstart = fbstart(file);", 
      "+\tbend = fbend(file);", 
      "+", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tint i;\t/* holds (possibly) updated branch index */", 
      "+\t\tint old_bid;", 
      "+", 
      "+\t\tlower_file = unionfs_lower_file_idx(file, bindex);", 
      "+\t\tif (!lower_file)", 
      "+\t\t\tcontinue;", 
      "+", 
      "+\t\t/*", 
      "+\t\t * Find new index of matching branch with an open", 
      "+\t\t * file, since branches could have been added or", 
      "+\t\t * deleted causing the one with open files to shift.", 
      "+\t\t */", 
      "+\t\told_bid = UNIONFS_F(file)->saved_branch_ids[bindex];", 
      "+\t\ti = branch_id_to_idx(sb, old_bid);", 
      "+\t\tif (unlikely(i < 0)) {", 
      "+\t\t\tprintk(KERN_ERR \"unionfs: no superblock for \"", 
      "+\t\t\t       \"file %p\\n\", file);", 
      "+\t\t\tcontinue;", 
      "+\t\t}", 
      "+", 
      "+\t\t/* decrement count of open files */", 
      "+\t\tbranchput(sb, i);", 
      "+\t\t/*", 
      "+\t\t * fput will perform an mntput for us on the correct branch.", 
      "+\t\t * Although we're using the file's old branch configuration,", 
      "+\t\t * bindex, which is the old index, correctly points to the", 
      "+\t\t * right branch in the file's branch list.  In other words,", 
      "+\t\t * we're going to mntput the correct branch even if branches", 
      "+\t\t * have been added/removed.", 
      "+\t\t */", 
      "+\t\tfput(lower_file);", 
      "+\t\tUNIONFS_F(file)->lower_files[bindex] = NULL;", 
      "+\t\tUNIONFS_F(file)->saved_branch_ids[bindex] = -1;", 
      "+\t}", 
      "+", 
      "+\tUNIONFS_F(file)->lower_files = NULL;", 
      "+\tkfree(lower_files);", 
      "+\tkfree(UNIONFS_F(file)->saved_branch_ids);", 
      "+\t/* set to NULL because caller needs to know if to kfree on error */", 
      "+\tUNIONFS_F(file)->saved_branch_ids = NULL;", 
      "+}", 
      "+", 
      "+/* open all lower files for a given file */", 
      "+static int open_all_files(struct file *file)", 
      "+{", 
      "+\tint bindex, bstart, bend, err = 0;", 
      "+\tstruct file *lower_file;", 
      "+\tstruct dentry *lower_dentry;", 
      "+\tstruct dentry *dentry = file->f_path.dentry;", 
      "+\tstruct super_block *sb = dentry->d_sb;", 
      "+", 
      "+\tbstart = dbstart(dentry);", 
      "+\tbend = dbend(dentry);", 
      "+", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tlower_dentry = unionfs_lower_dentry_idx(dentry, bindex);", 
      "+\t\tif (!lower_dentry)", 
      "+\t\t\tcontinue;", 
      "+", 
      "+\t\tdget(lower_dentry);", 
      "+\t\tunionfs_mntget(dentry, bindex);", 
      "+\t\tbranchget(sb, bindex);", 
      "+", 
      "+\t\tlower_file =", 
      "+\t\t\tdentry_open(lower_dentry,", 
      "+\t\t\t\t    unionfs_lower_mnt_idx(dentry, bindex),", 
      "+\t\t\t\t    file->f_flags);", 
      "+\t\tif (IS_ERR(lower_file)) {", 
      "+\t\t\tbranchput(sb, bindex);", 
      "+\t\t\terr = PTR_ERR(lower_file);", 
      "+\t\t\tgoto out;", 
      "+\t\t} else {", 
      "+\t\t\tunionfs_set_lower_file_idx(file, bindex, lower_file);", 
      "+\t\t}", 
      "+\t}", 
      "+out:", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/* open the highest priority file for a given upper file */", 
      "+static int open_highest_file(struct file *file, bool willwrite)", 
      "+{", 
      "+\tint bindex, bstart, bend, err = 0;", 
      "+\tstruct file *lower_file;", 
      "+\tstruct dentry *lower_dentry;", 
      "+\tstruct dentry *dentry = file->f_path.dentry;", 
      "+\tstruct dentry *parent = dget_parent(dentry);", 
      "+\tstruct inode *parent_inode = parent->d_inode;", 
      "+\tstruct super_block *sb = dentry->d_sb;", 
      "+", 
      "+\tbstart = dbstart(dentry);", 
      "+\tbend = dbend(dentry);", 
      "+", 
      "+\tlower_dentry = unionfs_lower_dentry(dentry);", 
      "+\tif (willwrite && IS_WRITE_FLAG(file->f_flags) && is_robranch(dentry)) {", 
      "+\t\tfor (bindex = bstart - 1; bindex >= 0; bindex--) {", 
      "+\t\t\terr = copyup_file(parent_inode, file, bstart, bindex,", 
      "+\t\t\t\t\t  i_size_read(dentry->d_inode));", 
      "+\t\t\tif (!err)", 
      "+\t\t\t\tbreak;", 
      "+\t\t}", 
      "+\t\tatomic_set(&UNIONFS_F(file)->generation,", 
      "+\t\t\t   atomic_read(&UNIONFS_I(dentry->d_inode)->", 
      "+\t\t\t\t       generation));", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tdget(lower_dentry);", 
      "+\tunionfs_mntget(dentry, bstart);", 
      "+\tlower_file = dentry_open(lower_dentry,", 
      "+\t\t\t\t unionfs_lower_mnt_idx(dentry, bstart),", 
      "+\t\t\t\t file->f_flags);", 
      "+\tif (IS_ERR(lower_file)) {", 
      "+\t\terr = PTR_ERR(lower_file);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tbranchget(sb, bstart);", 
      "+\tunionfs_set_lower_file(file, lower_file);", 
      "+\t/* Fix up the position. */", 
      "+\tlower_file->f_pos = file->f_pos;", 
      "+", 
      "+\tmemcpy(&lower_file->f_ra, &file->f_ra, sizeof(struct file_ra_state));", 
      "+out:", 
      "+\tdput(parent);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/* perform a delayed copyup of a read-write file on a read-only branch */", 
      "+static int do_delayed_copyup(struct file *file, struct dentry *parent)", 
      "+{", 
      "+\tint bindex, bstart, bend, err = 0;", 
      "+\tstruct dentry *dentry = file->f_path.dentry;", 
      "+\tstruct inode *parent_inode = parent->d_inode;", 
      "+", 
      "+\tbstart = fbstart(file);", 
      "+\tbend = fbend(file);", 
      "+", 
      "+\tBUG_ON(!S_ISREG(dentry->d_inode->i_mode));", 
      "+", 
      "+\tunionfs_check_file(file);", 
      "+\tfor (bindex = bstart - 1; bindex >= 0; bindex--) {", 
      "+\t\tif (!d_deleted(dentry))", 
      "+\t\t\terr = copyup_file(parent_inode, file, bstart,", 
      "+\t\t\t\t\t  bindex,", 
      "+\t\t\t\t\t  i_size_read(dentry->d_inode));", 
      "+\t\telse", 
      "+\t\t\terr = copyup_deleted_file(file, dentry, parent,", 
      "+\t\t\t\t\t\t  bstart, bindex);", 
      "+\t\t/* if succeeded, set lower open-file flags and break */", 
      "+\t\tif (!err) {", 
      "+\t\t\tstruct file *lower_file;", 
      "+\t\t\tlower_file = unionfs_lower_file_idx(file, bindex);", 
      "+\t\t\tlower_file->f_flags = file->f_flags;", 
      "+\t\t\tbreak;", 
      "+\t\t}", 
      "+\t}", 
      "+\tif (err || (bstart <= fbstart(file)))", 
      "+\t\tgoto out;", 
      "+\tbend = fbend(file);", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tif (unionfs_lower_file_idx(file, bindex)) {", 
      "+\t\t\tbranchput(dentry->d_sb, bindex);", 
      "+\t\t\tfput(unionfs_lower_file_idx(file, bindex));", 
      "+\t\t\tunionfs_set_lower_file_idx(file, bindex, NULL);", 
      "+\t\t}", 
      "+\t}", 
      "+\tpath_put_lowers(dentry, bstart, bend, false);", 
      "+\tiput_lowers(dentry->d_inode, bstart, bend, false);", 
      "+\t/* for reg file, we only open it \"once\" */", 
      "+\tfbend(file) = fbstart(file);", 
      "+\tdbend(dentry) = dbstart(dentry);", 
      "+\tibend(dentry->d_inode) = ibstart(dentry->d_inode);", 
      "+", 
      "+out:", 
      "+\tunionfs_check_file(file);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Helper function for unionfs_file_revalidate/locked.", 
      "+ * Expects dentry/parent to be locked already, and revalidated.", 
      "+ */", 
      "+static int __unionfs_file_revalidate(struct file *file, struct dentry *dentry,", 
      "+\t\t\t\t     struct dentry *parent,", 
      "+\t\t\t\t     struct super_block *sb, int sbgen,", 
      "+\t\t\t\t     int dgen, bool willwrite)", 
      "+{", 
      "+\tint fgen;", 
      "+\tint bstart, bend, orig_brid;", 
      "+\tint size;", 
      "+\tint err = 0;", 
      "+", 
      "+\tfgen = atomic_read(&UNIONFS_F(file)->generation);", 
      "+", 
      "+\t/*", 
      "+\t * There are two cases we are interested in.  The first is if the", 
      "+\t * generation is lower than the super-block.  The second is if", 
      "+\t * someone has copied up this file from underneath us, we also need", 
      "+\t * to refresh things.", 
      "+\t */", 
      "+\tif (d_deleted(dentry) ||", 
      "+\t    (sbgen <= fgen &&", 
      "+\t     dbstart(dentry) == fbstart(file) &&", 
      "+\t     unionfs_lower_file(file)))", 
      "+\t\tgoto out_may_copyup;", 
      "+", 
      "+\t/* save orig branch ID */", 
      "+\torig_brid = UNIONFS_F(file)->saved_branch_ids[fbstart(file)];", 
      "+", 
      "+\t/* First we throw out the existing files. */", 
      "+\tcleanup_file(file);", 
      "+", 
      "+\t/* Now we reopen the file(s) as in unionfs_open. */", 
      "+\tbstart = fbstart(file) = dbstart(dentry);", 
      "+\tbend = fbend(file) = dbend(dentry);", 
      "+", 
      "+\tsize = sizeof(struct file *) * sbmax(sb);", 
      "+\tUNIONFS_F(file)->lower_files = kzalloc(size, GFP_KERNEL);", 
      "+\tif (unlikely(!UNIONFS_F(file)->lower_files)) {", 
      "+\t\terr = -ENOMEM;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tsize = sizeof(int) * sbmax(sb);", 
      "+\tUNIONFS_F(file)->saved_branch_ids = kzalloc(size, GFP_KERNEL);", 
      "+\tif (unlikely(!UNIONFS_F(file)->saved_branch_ids)) {", 
      "+\t\terr = -ENOMEM;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tif (S_ISDIR(dentry->d_inode->i_mode)) {", 
      "+\t\t/* We need to open all the files. */", 
      "+\t\terr = open_all_files(file);", 
      "+\t\tif (err)", 
      "+\t\t\tgoto out;", 
      "+\t} else {", 
      "+\t\tint new_brid;", 
      "+\t\t/* We only open the highest priority branch. */", 
      "+\t\terr = open_highest_file(file, willwrite);", 
      "+\t\tif (err)", 
      "+\t\t\tgoto out;", 
      "+\t\tnew_brid = UNIONFS_F(file)->saved_branch_ids[fbstart(file)];", 
      "+\t\tif (unlikely(new_brid != orig_brid && sbgen > fgen)) {", 
      "+\t\t\t/*", 
      "+\t\t\t * If we re-opened the file on a different branch", 
      "+\t\t\t * than the original one, and this was due to a new", 
      "+\t\t\t * branch inserted, then update the mnt counts of", 
      "+\t\t\t * the old and new branches accordingly.", 
      "+\t\t\t */", 
      "+\t\t\tunionfs_mntget(dentry, bstart);", 
      "+\t\t\tunionfs_mntput(sb->s_root,", 
      "+\t\t\t\t       branch_id_to_idx(sb, orig_brid));", 
      "+\t\t}", 
      "+\t\t/* regular files have only one open lower file */", 
      "+\t\tfbend(file) = fbstart(file);", 
      "+\t}", 
      "+\tatomic_set(&UNIONFS_F(file)->generation,", 
      "+\t\t   atomic_read(&UNIONFS_I(dentry->d_inode)->generation));", 
      "+", 
      "+out_may_copyup:", 
      "+\t/* Copyup on the first write to a file on a readonly branch. */", 
      "+\tif (willwrite && IS_WRITE_FLAG(file->f_flags) &&", 
      "+\t    !IS_WRITE_FLAG(unionfs_lower_file(file)->f_flags) &&", 
      "+\t    is_robranch(dentry)) {", 
      "+\t\tpr_debug(\"unionfs: do delay copyup of \\\"%s\\\"\\n\",", 
      "+\t\t\t dentry->d_name.name);", 
      "+\t\terr = do_delayed_copyup(file, parent);", 
      "+\t\t/* regular files have only one open lower file */", 
      "+\t\tif (!err && !S_ISDIR(dentry->d_inode->i_mode))", 
      "+\t\t\tfbend(file) = fbstart(file);", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\tif (err) {", 
      "+\t\tkfree(UNIONFS_F(file)->lower_files);", 
      "+\t\tkfree(UNIONFS_F(file)->saved_branch_ids);", 
      "+\t}", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Revalidate the struct file", 
      "+ * @file: file to revalidate", 
      "+ * @parent: parent dentry (locked by caller)", 
      "+ * @willwrite: true if caller may cause changes to the file; false otherwise.", 
      "+ * Caller must lock/unlock dentry's branch configuration.", 
      "+ */", 
      "+int unionfs_file_revalidate(struct file *file, struct dentry *parent,", 
      "+\t\t\t    bool willwrite)", 
      "+{", 
      "+\tstruct super_block *sb;", 
      "+\tstruct dentry *dentry;", 
      "+\tint sbgen, dgen;", 
      "+\tint err = 0;", 
      "+", 
      "+\tdentry = file->f_path.dentry;", 
      "+\tsb = dentry->d_sb;", 
      "+\tverify_locked(dentry);", 
      "+\tverify_locked(parent);", 
      "+", 
      "+\t/*", 
      "+\t * First revalidate the dentry inside struct file,", 
      "+\t * but not unhashed dentries.", 
      "+\t */", 
      "+\tif (!d_deleted(dentry) &&", 
      "+\t    !__unionfs_d_revalidate(dentry, parent, willwrite)) {", 
      "+\t\terr = -ESTALE;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tsbgen = atomic_read(&UNIONFS_SB(sb)->generation);", 
      "+\tdgen = atomic_read(&UNIONFS_D(dentry)->generation);", 
      "+", 
      "+\tif (unlikely(sbgen > dgen)) { /* XXX: should never happen */", 
      "+\t\tpr_debug(\"unionfs: failed to revalidate dentry (%s)\\n\",", 
      "+\t\t\t dentry->d_name.name);", 
      "+\t\terr = -ESTALE;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\terr = __unionfs_file_revalidate(file, dentry, parent, sb,", 
      "+\t\t\t\t\tsbgen, dgen, willwrite);", 
      "+out:", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/* unionfs_open helper function: open a directory */", 
      "+static int __open_dir(struct inode *inode, struct file *file)", 
      "+{", 
      "+\tstruct dentry *lower_dentry;", 
      "+\tstruct file *lower_file;", 
      "+\tint bindex, bstart, bend;", 
      "+\tstruct vfsmount *mnt;", 
      "+", 
      "+\tbstart = fbstart(file) = dbstart(file->f_path.dentry);", 
      "+\tbend = fbend(file) = dbend(file->f_path.dentry);", 
      "+", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tlower_dentry =", 
      "+\t\t\tunionfs_lower_dentry_idx(file->f_path.dentry, bindex);", 
      "+\t\tif (!lower_dentry)", 
      "+\t\t\tcontinue;", 
      "+", 
      "+\t\tdget(lower_dentry);", 
      "+\t\tunionfs_mntget(file->f_path.dentry, bindex);", 
      "+\t\tmnt = unionfs_lower_mnt_idx(file->f_path.dentry, bindex);", 
      "+\t\tlower_file = dentry_open(lower_dentry, mnt, file->f_flags);", 
      "+\t\tif (IS_ERR(lower_file))", 
      "+\t\t\treturn PTR_ERR(lower_file);", 
      "+", 
      "+\t\tunionfs_set_lower_file_idx(file, bindex, lower_file);", 
      "+", 
      "+\t\t/*", 
      "+\t\t * The branchget goes after the open, because otherwise", 
      "+\t\t * we would miss the reference on release.", 
      "+\t\t */", 
      "+\t\tbranchget(inode->i_sb, bindex);", 
      "+\t}", 
      "+", 
      "+\treturn 0;", 
      "+}", 
      "+", 
      "+/* unionfs_open helper function: open a file */", 
      "+static int __open_file(struct inode *inode, struct file *file,", 
      "+\t\t       struct dentry *parent)", 
      "+{", 
      "+\tstruct dentry *lower_dentry;", 
      "+\tstruct file *lower_file;", 
      "+\tint lower_flags;", 
      "+\tint bindex, bstart, bend;", 
      "+", 
      "+\tlower_dentry = unionfs_lower_dentry(file->f_path.dentry);", 
      "+\tlower_flags = file->f_flags;", 
      "+", 
      "+\tbstart = fbstart(file) = dbstart(file->f_path.dentry);", 
      "+\tbend = fbend(file) = dbend(file->f_path.dentry);", 
      "+", 
      "+\t/*", 
      "+\t * check for the permission for lower file.  If the error is", 
      "+\t * COPYUP_ERR, copyup the file.", 
      "+\t */", 
      "+\tif (lower_dentry->d_inode && is_robranch(file->f_path.dentry)) {", 
      "+\t\t/*", 
      "+\t\t * if the open will change the file, copy it up otherwise", 
      "+\t\t * defer it.", 
      "+\t\t */", 
      "+\t\tif (lower_flags & O_TRUNC) {", 
      "+\t\t\tint size = 0;", 
      "+\t\t\tint err = -EROFS;", 
      "+", 
      "+\t\t\t/* copyup the file */", 
      "+\t\t\tfor (bindex = bstart - 1; bindex >= 0; bindex--) {", 
      "+\t\t\t\terr = copyup_file(parent->d_inode, file,", 
      "+\t\t\t\t\t\t  bstart, bindex, size);", 
      "+\t\t\t\tif (!err)", 
      "+\t\t\t\t\tbreak;", 
      "+\t\t\t}", 
      "+\t\t\treturn err;", 
      "+\t\t} else {", 
      "+\t\t\t/*", 
      "+\t\t\t * turn off writeable flags, to force delayed copyup", 
      "+\t\t\t * by caller.", 
      "+\t\t\t */", 
      "+\t\t\tlower_flags &= ~(OPEN_WRITE_FLAGS);", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\tdget(lower_dentry);", 
      "+", 
      "+\t/*", 
      "+\t * dentry_open will decrement mnt refcnt if err.", 
      "+\t * otherwise fput() will do an mntput() for us upon file close.", 
      "+\t */", 
      "+\tunionfs_mntget(file->f_path.dentry, bstart);", 
      "+\tlower_file =", 
      "+\t\tdentry_open(lower_dentry,", 
      "+\t\t\t    unionfs_lower_mnt_idx(file->f_path.dentry, bstart),", 
      "+\t\t\t    lower_flags);", 
      "+\tif (IS_ERR(lower_file))", 
      "+\t\treturn PTR_ERR(lower_file);", 
      "+", 
      "+\tunionfs_set_lower_file(file, lower_file);", 
      "+\tbranchget(inode->i_sb, bstart);", 
      "+", 
      "+\treturn 0;", 
      "+}", 
      "+", 
      "+int unionfs_open(struct inode *inode, struct file *file)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct file *lower_file = NULL;", 
      "+\tstruct dentry *dentry = file->f_path.dentry;", 
      "+\tstruct dentry *parent;", 
      "+\tint bindex = 0, bstart = 0, bend = 0;", 
      "+\tint size;", 
      "+\tint valid = 0;", 
      "+", 
      "+\tunionfs_read_lock(inode->i_sb, UNIONFS_SMUTEX_PARENT);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\t/* don't open unhashed/deleted files */", 
      "+\tif (d_deleted(dentry)) {", 
      "+\t\terr = -ENOENT;", 
      "+\t\tgoto out_nofree;", 
      "+\t}", 
      "+", 
      "+\t/* XXX: should I change 'false' below to the 'willwrite' flag? */", 
      "+\tvalid = __unionfs_d_revalidate(dentry, parent, false);", 
      "+\tif (unlikely(!valid)) {", 
      "+\t\terr = -ESTALE;", 
      "+\t\tgoto out_nofree;", 
      "+\t}", 
      "+", 
      "+\tfile->private_data =", 
      "+\t\tkzalloc(sizeof(struct unionfs_file_info), GFP_KERNEL);", 
      "+\tif (unlikely(!UNIONFS_F(file))) {", 
      "+\t\terr = -ENOMEM;", 
      "+\t\tgoto out_nofree;", 
      "+\t}", 
      "+\tfbstart(file) = -1;", 
      "+\tfbend(file) = -1;", 
      "+\tatomic_set(&UNIONFS_F(file)->generation,", 
      "+\t\t   atomic_read(&UNIONFS_I(inode)->generation));", 
      "+", 
      "+\tsize = sizeof(struct file *) * sbmax(inode->i_sb);", 
      "+\tUNIONFS_F(file)->lower_files = kzalloc(size, GFP_KERNEL);", 
      "+\tif (unlikely(!UNIONFS_F(file)->lower_files)) {", 
      "+\t\terr = -ENOMEM;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tsize = sizeof(int) * sbmax(inode->i_sb);", 
      "+\tUNIONFS_F(file)->saved_branch_ids = kzalloc(size, GFP_KERNEL);", 
      "+\tif (unlikely(!UNIONFS_F(file)->saved_branch_ids)) {", 
      "+\t\terr = -ENOMEM;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tbstart = fbstart(file) = dbstart(dentry);", 
      "+\tbend = fbend(file) = dbend(dentry);", 
      "+", 
      "+\t/*", 
      "+\t * open all directories and make the unionfs file struct point to", 
      "+\t * these lower file structs", 
      "+\t */", 
      "+\tif (S_ISDIR(inode->i_mode))", 
      "+\t\terr = __open_dir(inode, file);\t/* open a dir */", 
      "+\telse", 
      "+\t\terr = __open_file(inode, file, parent);\t/* open a file */", 
      "+", 
      "+\t/* freeing the allocated resources, and fput the opened files */", 
      "+\tif (err) {", 
      "+\t\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\t\tlower_file = unionfs_lower_file_idx(file, bindex);", 
      "+\t\t\tif (!lower_file)", 
      "+\t\t\t\tcontinue;", 
      "+", 
      "+\t\t\tbranchput(dentry->d_sb, bindex);", 
      "+\t\t\t/* fput calls dput for lower_dentry */", 
      "+\t\t\tfput(lower_file);", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\tif (err) {", 
      "+\t\tkfree(UNIONFS_F(file)->lower_files);", 
      "+\t\tkfree(UNIONFS_F(file)->saved_branch_ids);", 
      "+\t\tkfree(UNIONFS_F(file));", 
      "+\t}", 
      "+out_nofree:", 
      "+\tif (!err) {", 
      "+\t\tunionfs_postcopyup_setmnt(dentry);", 
      "+\t\tunionfs_copy_attr_times(inode);", 
      "+\t\tunionfs_check_file(file);", 
      "+\t\tunionfs_check_inode(inode);", 
      "+\t}", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(inode->i_sb);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * release all lower object references & free the file info structure", 
      "+ *", 
      "+ * No need to grab sb info's rwsem.", 
      "+ */", 
      "+int unionfs_file_release(struct inode *inode, struct file *file)", 
      "+{", 
      "+\tstruct file *lower_file = NULL;", 
      "+\tstruct unionfs_file_info *fileinfo;", 
      "+\tstruct unionfs_inode_info *inodeinfo;", 
      "+\tstruct super_block *sb = inode->i_sb;", 
      "+\tstruct dentry *dentry = file->f_path.dentry;", 
      "+\tstruct dentry *parent;", 
      "+\tint bindex, bstart, bend;", 
      "+\tint fgen, err = 0;", 
      "+", 
      "+\tunionfs_read_lock(sb, UNIONFS_SMUTEX_PARENT);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\t/*", 
      "+\t * We try to revalidate, but the VFS ignores return return values", 
      "+\t * from file->release, so we must always try to succeed here,", 
      "+\t * including to do the kfree and dput below.  So if revalidation", 
      "+\t * failed, all we can do is print some message and keep going.", 
      "+\t */", 
      "+\terr = unionfs_file_revalidate(file, parent,", 
      "+\t\t\t\t      UNIONFS_F(file)->wrote_to_file);", 
      "+\tif (!err)", 
      "+\t\tunionfs_check_file(file);", 
      "+\tfileinfo = UNIONFS_F(file);", 
      "+\tBUG_ON(file->f_path.dentry->d_inode != inode);", 
      "+\tinodeinfo = UNIONFS_I(inode);", 
      "+", 
      "+\t/* fput all the lower files */", 
      "+\tfgen = atomic_read(&fileinfo->generation);", 
      "+\tbstart = fbstart(file);", 
      "+\tbend = fbend(file);", 
      "+", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tlower_file = unionfs_lower_file_idx(file, bindex);", 
      "+", 
      "+\t\tif (lower_file) {", 
      "+\t\t\tunionfs_set_lower_file_idx(file, bindex, NULL);", 
      "+\t\t\tfput(lower_file);", 
      "+\t\t\tbranchput(sb, bindex);", 
      "+\t\t}", 
      "+", 
      "+\t\t/* if there are no more refs to the dentry, dput it */", 
      "+\t\tif (d_deleted(dentry)) {", 
      "+\t\t\tdput(unionfs_lower_dentry_idx(dentry, bindex));", 
      "+\t\t\tunionfs_set_lower_dentry_idx(dentry, bindex, NULL);", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\tkfree(fileinfo->lower_files);", 
      "+\tkfree(fileinfo->saved_branch_ids);", 
      "+", 
      "+\tif (fileinfo->rdstate) {", 
      "+\t\tfileinfo->rdstate->access = jiffies;", 
      "+\t\tspin_lock(&inodeinfo->rdlock);", 
      "+\t\tinodeinfo->rdcount++;", 
      "+\t\tlist_add_tail(&fileinfo->rdstate->cache,", 
      "+\t\t\t      &inodeinfo->readdircache);", 
      "+\t\tmark_inode_dirty(inode);", 
      "+\t\tspin_unlock(&inodeinfo->rdlock);", 
      "+\t\tfileinfo->rdstate = NULL;", 
      "+\t}", 
      "+\tkfree(fileinfo);", 
      "+", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(sb);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/* pass the ioctl to the lower fs */", 
      "+static long do_ioctl(struct file *file, unsigned int cmd, unsigned long arg)", 
      "+{", 
      "+\tstruct file *lower_file;", 
      "+\tint err;", 
      "+", 
      "+\tlower_file = unionfs_lower_file(file);", 
      "+", 
      "+\terr = -ENOTTY;", 
      "+\tif (!lower_file || !lower_file->f_op)", 
      "+\t\tgoto out;", 
      "+\tif (lower_file->f_op->unlocked_ioctl) {", 
      "+\t\terr = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg);", 
      "+\t} else if (lower_file->f_op->ioctl) {", 
      "+\t\tlock_kernel();", 
      "+\t\terr = lower_file->f_op->ioctl(", 
      "+\t\t\tlower_file->f_path.dentry->d_inode,", 
      "+\t\t\tlower_file, cmd, arg);", 
      "+\t\tunlock_kernel();", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * return to user-space the branch indices containing the file in question", 
      "+ *", 
      "+ * We use fd_set and therefore we are limited to the number of the branches", 
      "+ * to FD_SETSIZE, which is currently 1024 - plenty for most people", 
      "+ */", 
      "+static int unionfs_ioctl_queryfile(struct file *file, struct dentry *parent,", 
      "+\t\t\t\t   unsigned int cmd, unsigned long arg)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tfd_set branchlist;", 
      "+\tint bstart = 0, bend = 0, bindex = 0;", 
      "+\tint orig_bstart, orig_bend;", 
      "+\tstruct dentry *dentry, *lower_dentry;", 
      "+\tstruct vfsmount *mnt;", 
      "+", 
      "+\tdentry = file->f_path.dentry;", 
      "+\torig_bstart = dbstart(dentry);", 
      "+\torig_bend = dbend(dentry);", 
      "+\terr = unionfs_partial_lookup(dentry, parent);", 
      "+\tif (err)", 
      "+\t\tgoto out;", 
      "+\tbstart = dbstart(dentry);", 
      "+\tbend = dbend(dentry);", 
      "+", 
      "+\tFD_ZERO(&branchlist);", 
      "+", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tlower_dentry = unionfs_lower_dentry_idx(dentry, bindex);", 
      "+\t\tif (!lower_dentry)", 
      "+\t\t\tcontinue;", 
      "+\t\tif (likely(lower_dentry->d_inode))", 
      "+\t\t\tFD_SET(bindex, &branchlist);", 
      "+\t\t/* purge any lower objects after partial_lookup */", 
      "+\t\tif (bindex < orig_bstart || bindex > orig_bend) {", 
      "+\t\t\tdput(lower_dentry);", 
      "+\t\t\tunionfs_set_lower_dentry_idx(dentry, bindex, NULL);", 
      "+\t\t\tiput(unionfs_lower_inode_idx(dentry->d_inode, bindex));", 
      "+\t\t\tunionfs_set_lower_inode_idx(dentry->d_inode, bindex,", 
      "+\t\t\t\t\t\t    NULL);", 
      "+\t\t\tmnt = unionfs_lower_mnt_idx(dentry, bindex);", 
      "+\t\t\tif (!mnt)", 
      "+\t\t\t\tcontinue;", 
      "+\t\t\tunionfs_mntput(dentry, bindex);", 
      "+\t\t\tunionfs_set_lower_mnt_idx(dentry, bindex, NULL);", 
      "+\t\t}", 
      "+\t}", 
      "+\t/* restore original dentry's offsets */", 
      "+\tdbstart(dentry) = orig_bstart;", 
      "+\tdbend(dentry) = orig_bend;", 
      "+\tibstart(dentry->d_inode) = orig_bstart;", 
      "+\tibend(dentry->d_inode) = orig_bend;", 
      "+", 
      "+\terr = copy_to_user((void __user *)arg, &branchlist, sizeof(fd_set));", 
      "+\tif (unlikely(err))", 
      "+\t\terr = -EFAULT;", 
      "+", 
      "+out:", 
      "+\treturn err < 0 ? err : bend;", 
      "+}", 
      "+", 
      "+long unionfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)", 
      "+{", 
      "+\tlong err;", 
      "+\tstruct dentry *dentry = file->f_path.dentry;", 
      "+\tstruct dentry *parent;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\terr = unionfs_file_revalidate(file, parent, true);", 
      "+\tif (unlikely(err))", 
      "+\t\tgoto out;", 
      "+", 
      "+\t/* check if asked for local commands */", 
      "+\tswitch (cmd) {", 
      "+\tcase UNIONFS_IOCTL_INCGEN:", 
      "+\t\t/* Increment the superblock generation count */", 
      "+\t\tpr_info(\"unionfs: incgen ioctl deprecated; \"", 
      "+\t\t\t\"use \\\"-o remount,incgen\\\"\\n\");", 
      "+\t\terr = -ENOSYS;", 
      "+\t\tbreak;", 
      "+", 
      "+\tcase UNIONFS_IOCTL_QUERYFILE:", 
      "+\t\t/* Return list of branches containing the given file */", 
      "+\t\terr = unionfs_ioctl_queryfile(file, parent, cmd, arg);", 
      "+\t\tbreak;", 
      "+", 
      "+\tdefault:", 
      "+\t\t/* pass the ioctl down */", 
      "+\t\terr = do_ioctl(file, cmd, arg);", 
      "+\t\tbreak;", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\tunionfs_check_file(file);", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+int unionfs_flush(struct file *file, fl_owner_t id)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct file *lower_file = NULL;", 
      "+\tstruct dentry *dentry = file->f_path.dentry;", 
      "+\tstruct dentry *parent;", 
      "+\tint bindex, bstart, bend;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\terr = unionfs_file_revalidate(file, parent,", 
      "+\t\t\t\t      UNIONFS_F(file)->wrote_to_file);", 
      "+\tif (unlikely(err))", 
      "+\t\tgoto out;", 
      "+\tunionfs_check_file(file);", 
      "+", 
      "+\tbstart = fbstart(file);", 
      "+\tbend = fbend(file);", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tlower_file = unionfs_lower_file_idx(file, bindex);", 
      "+", 
      "+\t\tif (lower_file && lower_file->f_op &&", 
      "+\t\t    lower_file->f_op->flush) {", 
      "+\t\t\terr = lower_file->f_op->flush(lower_file, id);", 
      "+\t\t\tif (err)", 
      "+\t\t\t\tgoto out;", 
      "+\t\t}", 
      "+", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\tif (!err)", 
      "+\t\tunionfs_check_file(file);", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+\treturn err;", 
      "+}"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/copyup.c", 
    [
      "diff --git a/fs/unionfs/copyup.c b/fs/unionfs/copyup.c", 
      "new file mode 100644", 
      "index 0000000..064e5f4", 
      "--- dev/null", 
      "+++ b/fs/unionfs/copyup.c", 
      "@@ -0,0 +1,889 @@", 
      "+/*", 
      "+ * Copyright (c) 2003-2009 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-2009 Stony Brook University", 
      "+ * Copyright (c) 2003-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#include \"union.h\"", 
      "+", 
      "+/*", 
      "+ * For detailed explanation of copyup see:", 
      "+ * Documentation/filesystems/unionfs/concepts.txt", 
      "+ */", 
      "+", 
      "+#ifdef CONFIG_UNION_FS_XATTR", 
      "+/* copyup all extended attrs for a given dentry */", 
      "+static int copyup_xattrs(struct dentry *old_lower_dentry,", 
      "+\t\t\t struct dentry *new_lower_dentry)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tssize_t list_size = -1;", 
      "+\tchar *name_list = NULL;", 
      "+\tchar *attr_value = NULL;", 
      "+\tchar *name_list_buf = NULL;", 
      "+", 
      "+\t/* query the actual size of the xattr list */", 
      "+\tlist_size = vfs_listxattr(old_lower_dentry, NULL, 0);", 
      "+\tif (list_size <= 0) {", 
      "+\t\terr = list_size;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/* allocate space for the actual list */", 
      "+\tname_list = unionfs_xattr_alloc(list_size + 1, XATTR_LIST_MAX);", 
      "+\tif (unlikely(!name_list || IS_ERR(name_list))) {", 
      "+\t\terr = PTR_ERR(name_list);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tname_list_buf = name_list; /* save for kfree at end */", 
      "+", 
      "+\t/* now get the actual xattr list of the source file */", 
      "+\tlist_size = vfs_listxattr(old_lower_dentry, name_list, list_size);", 
      "+\tif (list_size <= 0) {", 
      "+\t\terr = list_size;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/* allocate space to hold each xattr's value */", 
      "+\tattr_value = unionfs_xattr_alloc(XATTR_SIZE_MAX, XATTR_SIZE_MAX);", 
      "+\tif (unlikely(!attr_value || IS_ERR(attr_value))) {", 
      "+\t\terr = PTR_ERR(name_list);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/* in a loop, get and set each xattr from src to dst file */", 
      "+\twhile (*name_list) {", 
      "+\t\tssize_t size;", 
      "+", 
      "+\t\t/* Lock here since vfs_getxattr doesn't lock for us */", 
      "+\t\tmutex_lock(&old_lower_dentry->d_inode->i_mutex);", 
      "+\t\tsize = vfs_getxattr(old_lower_dentry, name_list,", 
      "+\t\t\t\t    attr_value, XATTR_SIZE_MAX);", 
      "+\t\tmutex_unlock(&old_lower_dentry->d_inode->i_mutex);", 
      "+\t\tif (size < 0) {", 
      "+\t\t\terr = size;", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+\t\tif (size > XATTR_SIZE_MAX) {", 
      "+\t\t\terr = -E2BIG;", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+\t\t/* Don't lock here since vfs_setxattr does it for us. */", 
      "+\t\terr = vfs_setxattr(new_lower_dentry, name_list, attr_value,", 
      "+\t\t\t\t   size, 0);", 
      "+\t\t/*", 
      "+\t\t * Selinux depends on \"security.*\" xattrs, so to maintain", 
      "+\t\t * the security of copied-up files, if Selinux is active,", 
      "+\t\t * then we must copy these xattrs as well.  So we need to", 
      "+\t\t * temporarily get FOWNER privileges.", 
      "+\t\t * XXX: move entire copyup code to SIOQ.", 
      "+\t\t */", 
      "+\t\tif (err == -EPERM && !capable(CAP_FOWNER)) {", 
      "+\t\t\tcap_raise(current->cap_effective, CAP_FOWNER);", 
      "+\t\t\terr = vfs_setxattr(new_lower_dentry, name_list,", 
      "+\t\t\t\t\t   attr_value, size, 0);", 
      "+\t\t\tcap_lower(current->cap_effective, CAP_FOWNER);", 
      "+\t\t}", 
      "+\t\tif (err < 0)", 
      "+\t\t\tgoto out;", 
      "+\t\tname_list += strlen(name_list) + 1;", 
      "+\t}", 
      "+out:", 
      "+\tunionfs_xattr_kfree(name_list_buf);", 
      "+\tunionfs_xattr_kfree(attr_value);", 
      "+\t/* Ignore if xattr isn't supported */", 
      "+\tif (err == -ENOTSUPP || err == -EOPNOTSUPP)", 
      "+\t\terr = 0;", 
      "+\treturn err;", 
      "+}", 
      "+#endif /* CONFIG_UNION_FS_XATTR */", 
      "+", 
      "+/*", 
      "+ * Determine the mode based on the copyup flags, and the existing dentry.", 
      "+ *", 
      "+ * Handle file systems which may not support certain options.  For example", 
      "+ * jffs2 doesn't allow one to chmod a symlink.  So we ignore such harmless", 
      "+ * errors, rather than propagating them up, which results in copyup errors", 
      "+ * and errors returned back to users.", 
      "+ */", 
      "+static int copyup_permissions(struct super_block *sb,", 
      "+\t\t\t      struct dentry *old_lower_dentry,", 
      "+\t\t\t      struct dentry *new_lower_dentry)", 
      "+{", 
      "+\tstruct inode *i = old_lower_dentry->d_inode;", 
      "+\tstruct iattr newattrs;", 
      "+\tint err;", 
      "+", 
      "+\tnewattrs.ia_atime = i->i_atime;", 
      "+\tnewattrs.ia_mtime = i->i_mtime;", 
      "+\tnewattrs.ia_ctime = i->i_ctime;", 
      "+\tnewattrs.ia_gid = i->i_gid;", 
      "+\tnewattrs.ia_uid = i->i_uid;", 
      "+\tnewattrs.ia_valid = ATTR_CTIME | ATTR_ATIME | ATTR_MTIME |", 
      "+\t\tATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_FORCE |", 
      "+\t\tATTR_GID | ATTR_UID;", 
      "+\tmutex_lock(&new_lower_dentry->d_inode->i_mutex);", 
      "+\terr = notify_change(new_lower_dentry, &newattrs);", 
      "+\tif (err)", 
      "+\t\tgoto out;", 
      "+", 
      "+\t/* now try to change the mode and ignore EOPNOTSUPP on symlinks */", 
      "+\tnewattrs.ia_mode = i->i_mode;", 
      "+\tnewattrs.ia_valid = ATTR_MODE | ATTR_FORCE;", 
      "+\terr = notify_change(new_lower_dentry, &newattrs);", 
      "+\tif (err == -EOPNOTSUPP &&", 
      "+\t    S_ISLNK(new_lower_dentry->d_inode->i_mode)) {", 
      "+\t\tprintk(KERN_WARNING", 
      "+\t\t       \"unionfs: changing \\\"%s\\\" symlink mode unsupported\\n\",", 
      "+\t\t       new_lower_dentry->d_name.name);", 
      "+\t\terr = 0;", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\tmutex_unlock(&new_lower_dentry->d_inode->i_mutex);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * create the new device/file/directory - use copyup_permission to copyup", 
      "+ * times, and mode", 
      "+ *", 
      "+ * if the object being copied up is a regular file, the file is only created,", 
      "+ * the contents have to be copied up separately", 
      "+ */", 
      "+static int __copyup_ndentry(struct dentry *old_lower_dentry,", 
      "+\t\t\t    struct dentry *new_lower_dentry,", 
      "+\t\t\t    struct dentry *new_lower_parent_dentry,", 
      "+\t\t\t    char *symbuf)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tumode_t old_mode = old_lower_dentry->d_inode->i_mode;", 
      "+\tstruct sioq_args args;", 
      "+", 
      "+\tif (S_ISDIR(old_mode)) {", 
      "+\t\targs.mkdir.parent = new_lower_parent_dentry->d_inode;", 
      "+\t\targs.mkdir.dentry = new_lower_dentry;", 
      "+\t\targs.mkdir.mode = old_mode;", 
      "+", 
      "+\t\trun_sioq(__unionfs_mkdir, &args);", 
      "+\t\terr = args.err;", 
      "+\t} else if (S_ISLNK(old_mode)) {", 
      "+\t\targs.symlink.parent = new_lower_parent_dentry->d_inode;", 
      "+\t\targs.symlink.dentry = new_lower_dentry;", 
      "+\t\targs.symlink.symbuf = symbuf;", 
      "+\t\targs.symlink.mode = old_mode;", 
      "+", 
      "+\t\trun_sioq(__unionfs_symlink, &args);", 
      "+\t\terr = args.err;", 
      "+\t} else if (S_ISBLK(old_mode) || S_ISCHR(old_mode) ||", 
      "+\t\t   S_ISFIFO(old_mode) || S_ISSOCK(old_mode)) {", 
      "+\t\targs.mknod.parent = new_lower_parent_dentry->d_inode;", 
      "+\t\targs.mknod.dentry = new_lower_dentry;", 
      "+\t\targs.mknod.mode = old_mode;", 
      "+\t\targs.mknod.dev = old_lower_dentry->d_inode->i_rdev;", 
      "+", 
      "+\t\trun_sioq(__unionfs_mknod, &args);", 
      "+\t\terr = args.err;", 
      "+\t} else if (S_ISREG(old_mode)) {", 
      "+\t\tstruct nameidata nd;", 
      "+\t\terr = init_lower_nd(&nd, LOOKUP_CREATE);", 
      "+\t\tif (unlikely(err < 0))", 
      "+\t\t\tgoto out;", 
      "+\t\targs.create.nd = &nd;", 
      "+\t\targs.create.parent = new_lower_parent_dentry->d_inode;", 
      "+\t\targs.create.dentry = new_lower_dentry;", 
      "+\t\targs.create.mode = old_mode;", 
      "+", 
      "+\t\trun_sioq(__unionfs_create, &args);", 
      "+\t\terr = args.err;", 
      "+\t\trelease_lower_nd(&nd, err);", 
      "+\t} else {", 
      "+\t\tprintk(KERN_CRIT \"unionfs: unknown inode type %d\\n\",", 
      "+\t\t       old_mode);", 
      "+\t\tBUG();", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+static int __copyup_reg_data(struct dentry *dentry,", 
      "+\t\t\t     struct dentry *new_lower_dentry, int new_bindex,", 
      "+\t\t\t     struct dentry *old_lower_dentry, int old_bindex,", 
      "+\t\t\t     struct file **copyup_file, loff_t len)", 
      "+{", 
      "+\tstruct super_block *sb = dentry->d_sb;", 
      "+\tstruct file *input_file;", 
      "+\tstruct file *output_file;", 
      "+\tstruct vfsmount *output_mnt;", 
      "+\tmm_segment_t old_fs;", 
      "+\tchar *buf = NULL;", 
      "+\tssize_t read_bytes, write_bytes;", 
      "+\tloff_t size;", 
      "+\tint err = 0;", 
      "+", 
      "+\t/* open old file */", 
      "+\tunionfs_mntget(dentry, old_bindex);", 
      "+\tbranchget(sb, old_bindex);", 
      "+\t/* dentry_open calls dput and mntput if it returns an error */", 
      "+\tinput_file = dentry_open(old_lower_dentry,", 
      "+\t\t\t\t unionfs_lower_mnt_idx(dentry, old_bindex),", 
      "+\t\t\t\t O_RDONLY | O_LARGEFILE);", 
      "+\tif (IS_ERR(input_file)) {", 
      "+\t\tdput(old_lower_dentry);", 
      "+\t\terr = PTR_ERR(input_file);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tif (unlikely(!input_file->f_op || !input_file->f_op->read)) {", 
      "+\t\terr = -EINVAL;", 
      "+\t\tgoto out_close_in;", 
      "+\t}", 
      "+", 
      "+\t/* open new file */", 
      "+\tdget(new_lower_dentry);", 
      "+\toutput_mnt = unionfs_mntget(sb->s_root, new_bindex);", 
      "+\tbranchget(sb, new_bindex);", 
      "+\toutput_file = dentry_open(new_lower_dentry, output_mnt,", 
      "+\t\t\t\t  O_RDWR | O_LARGEFILE);", 
      "+\tif (IS_ERR(output_file)) {", 
      "+\t\terr = PTR_ERR(output_file);", 
      "+\t\tgoto out_close_in2;", 
      "+\t}", 
      "+\tif (unlikely(!output_file->f_op || !output_file->f_op->write)) {", 
      "+\t\terr = -EINVAL;", 
      "+\t\tgoto out_close_out;", 
      "+\t}", 
      "+", 
      "+\t/* allocating a buffer */", 
      "+\tbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);", 
      "+\tif (unlikely(!buf)) {", 
      "+\t\terr = -ENOMEM;", 
      "+\t\tgoto out_close_out;", 
      "+\t}", 
      "+", 
      "+\tinput_file->f_pos = 0;", 
      "+\toutput_file->f_pos = 0;", 
      "+", 
      "+\told_fs = get_fs();", 
      "+\tset_fs(KERNEL_DS);", 
      "+", 
      "+\tsize = len;", 
      "+\terr = 0;", 
      "+\tdo {", 
      "+\t\tif (len >= PAGE_SIZE)", 
      "+\t\t\tsize = PAGE_SIZE;", 
      "+\t\telse if ((len < PAGE_SIZE) && (len > 0))", 
      "+\t\t\tsize = len;", 
      "+", 
      "+\t\tlen -= PAGE_SIZE;", 
      "+", 
      "+\t\tread_bytes =", 
      "+\t\t\tinput_file->f_op->read(input_file,", 
      "+\t\t\t\t\t       (char __user *)buf, size,", 
      "+\t\t\t\t\t       &input_file->f_pos);", 
      "+\t\tif (read_bytes <= 0) {", 
      "+\t\t\terr = read_bytes;", 
      "+\t\t\tbreak;", 
      "+\t\t}", 
      "+", 
      "+\t\t/* see Documentation/filesystems/unionfs/issues.txt */", 
      "+\t\tlockdep_off();", 
      "+\t\twrite_bytes =", 
      "+\t\t\toutput_file->f_op->write(output_file,", 
      "+\t\t\t\t\t\t (char __user *)buf,", 
      "+\t\t\t\t\t\t read_bytes,", 
      "+\t\t\t\t\t\t &output_file->f_pos);", 
      "+\t\tlockdep_on();", 
      "+\t\tif ((write_bytes < 0) || (write_bytes < read_bytes)) {", 
      "+\t\t\terr = write_bytes;", 
      "+\t\t\tbreak;", 
      "+\t\t}", 
      "+\t} while ((read_bytes > 0) && (len > 0));", 
      "+", 
      "+\tset_fs(old_fs);", 
      "+", 
      "+\tkfree(buf);", 
      "+", 
      "+\tif (!err)", 
      "+\t\terr = output_file->f_op->fsync(output_file,", 
      "+\t\t\t\t\t       new_lower_dentry, 0);", 
      "+", 
      "+\tif (err)", 
      "+\t\tgoto out_close_out;", 
      "+", 
      "+\tif (copyup_file) {", 
      "+\t\t*copyup_file = output_file;", 
      "+\t\tgoto out_close_in;", 
      "+\t}", 
      "+", 
      "+out_close_out:", 
      "+\tfput(output_file);", 
      "+", 
      "+out_close_in2:", 
      "+\tbranchput(sb, new_bindex);", 
      "+", 
      "+out_close_in:", 
      "+\tfput(input_file);", 
      "+", 
      "+out:", 
      "+\tbranchput(sb, old_bindex);", 
      "+", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * dput the lower references for old and new dentry & clear a lower dentry", 
      "+ * pointer", 
      "+ */", 
      "+static void __clear(struct dentry *dentry, struct dentry *old_lower_dentry,", 
      "+\t\t    int old_bstart, int old_bend,", 
      "+\t\t    struct dentry *new_lower_dentry, int new_bindex)", 
      "+{", 
      "+\t/* get rid of the lower dentry and all its traces */", 
      "+\tunionfs_set_lower_dentry_idx(dentry, new_bindex, NULL);", 
      "+\tdbstart(dentry) = old_bstart;", 
      "+\tdbend(dentry) = old_bend;", 
      "+", 
      "+\tdput(new_lower_dentry);", 
      "+\tdput(old_lower_dentry);", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Copy up a dentry to a file of specified name.", 
      "+ *", 
      "+ * @dir: used to pull the ->i_sb to access other branches", 
      "+ * @dentry: the non-negative dentry whose lower_inode we should copy", 
      "+ * @bstart: the branch of the lower_inode to copy from", 
      "+ * @new_bindex: the branch to create the new file in", 
      "+ * @name: the name of the file to create", 
      "+ * @namelen: length of @name", 
      "+ * @copyup_file: the \"struct file\" to return (optional)", 
      "+ * @len: how many bytes to copy-up?", 
      "+ */", 
      "+int copyup_dentry(struct inode *dir, struct dentry *dentry, int bstart,", 
      "+\t\t  int new_bindex, const char *name, int namelen,", 
      "+\t\t  struct file **copyup_file, loff_t len)", 
      "+{", 
      "+\tstruct dentry *new_lower_dentry;", 
      "+\tstruct dentry *old_lower_dentry = NULL;", 
      "+\tstruct super_block *sb;", 
      "+\tint err = 0;", 
      "+\tint old_bindex;", 
      "+\tint old_bstart;", 
      "+\tint old_bend;", 
      "+\tstruct dentry *new_lower_parent_dentry = NULL;", 
      "+\tmm_segment_t oldfs;", 
      "+\tchar *symbuf = NULL;", 
      "+", 
      "+\tverify_locked(dentry);", 
      "+", 
      "+\told_bindex = bstart;", 
      "+\told_bstart = dbstart(dentry);", 
      "+\told_bend = dbend(dentry);", 
      "+", 
      "+\tBUG_ON(new_bindex < 0);", 
      "+\tBUG_ON(new_bindex >= old_bindex);", 
      "+", 
      "+\tsb = dir->i_sb;", 
      "+", 
      "+\terr = is_robranch_super(sb, new_bindex);", 
      "+\tif (err)", 
      "+\t\tgoto out;", 
      "+", 
      "+\t/* Create the directory structure above this dentry. */", 
      "+\tnew_lower_dentry = create_parents(dir, dentry, name, new_bindex);", 
      "+\tif (IS_ERR(new_lower_dentry)) {", 
      "+\t\terr = PTR_ERR(new_lower_dentry);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\told_lower_dentry = unionfs_lower_dentry_idx(dentry, old_bindex);", 
      "+\t/* we conditionally dput this old_lower_dentry at end of function */", 
      "+\tdget(old_lower_dentry);", 
      "+", 
      "+\t/* For symlinks, we must read the link before we lock the directory. */", 
      "+\tif (S_ISLNK(old_lower_dentry->d_inode->i_mode)) {", 
      "+", 
      "+\t\tsymbuf = kmalloc(PATH_MAX, GFP_KERNEL);", 
      "+\t\tif (unlikely(!symbuf)) {", 
      "+\t\t\t__clear(dentry, old_lower_dentry,", 
      "+\t\t\t\told_bstart, old_bend,", 
      "+\t\t\t\tnew_lower_dentry, new_bindex);", 
      "+\t\t\terr = -ENOMEM;", 
      "+\t\t\tgoto out_free;", 
      "+\t\t}", 
      "+", 
      "+\t\toldfs = get_fs();", 
      "+\t\tset_fs(KERNEL_DS);", 
      "+\t\terr = old_lower_dentry->d_inode->i_op->readlink(", 
      "+\t\t\told_lower_dentry,", 
      "+\t\t\t(char __user *)symbuf,", 
      "+\t\t\tPATH_MAX);", 
      "+\t\tset_fs(oldfs);", 
      "+\t\tif (err < 0) {", 
      "+\t\t\t__clear(dentry, old_lower_dentry,", 
      "+\t\t\t\told_bstart, old_bend,", 
      "+\t\t\t\tnew_lower_dentry, new_bindex);", 
      "+\t\t\tgoto out_free;", 
      "+\t\t}", 
      "+\t\tsymbuf[err] = '\\0';", 
      "+\t}", 
      "+", 
      "+\t/* Now we lock the parent, and create the object in the new branch. */", 
      "+\tnew_lower_parent_dentry = lock_parent(new_lower_dentry);", 
      "+", 
      "+\t/* create the new inode */", 
      "+\terr = __copyup_ndentry(old_lower_dentry, new_lower_dentry,", 
      "+\t\t\t       new_lower_parent_dentry, symbuf);", 
      "+", 
      "+\tif (err) {", 
      "+\t\t__clear(dentry, old_lower_dentry,", 
      "+\t\t\told_bstart, old_bend,", 
      "+\t\t\tnew_lower_dentry, new_bindex);", 
      "+\t\tgoto out_unlock;", 
      "+\t}", 
      "+", 
      "+\t/* We actually copyup the file here. */", 
      "+\tif (S_ISREG(old_lower_dentry->d_inode->i_mode))", 
      "+\t\terr = __copyup_reg_data(dentry, new_lower_dentry, new_bindex,", 
      "+\t\t\t\t\told_lower_dentry, old_bindex,", 
      "+\t\t\t\t\tcopyup_file, len);", 
      "+\tif (err)", 
      "+\t\tgoto out_unlink;", 
      "+", 
      "+\t/* Set permissions. */", 
      "+\terr = copyup_permissions(sb, old_lower_dentry, new_lower_dentry);", 
      "+\tif (err)", 
      "+\t\tgoto out_unlink;", 
      "+", 
      "+#ifdef CONFIG_UNION_FS_XATTR", 
      "+\t/* Selinux uses extended attributes for permissions. */", 
      "+\terr = copyup_xattrs(old_lower_dentry, new_lower_dentry);", 
      "+\tif (err)", 
      "+\t\tgoto out_unlink;", 
      "+#endif /* CONFIG_UNION_FS_XATTR */", 
      "+", 
      "+\t/* do not allow files getting deleted to be re-interposed */", 
      "+\tif (!d_deleted(dentry))", 
      "+\t\tunionfs_reinterpose(dentry);", 
      "+", 
      "+\tgoto out_unlock;", 
      "+", 
      "+out_unlink:", 
      "+\t/*", 
      "+\t * copyup failed, because we possibly ran out of space or", 
      "+\t * quota, or something else happened so let's unlink; we don't", 
      "+\t * really care about the return value of vfs_unlink", 
      "+\t */", 
      "+\tvfs_unlink(new_lower_parent_dentry->d_inode, new_lower_dentry);", 
      "+", 
      "+\tif (copyup_file) {", 
      "+\t\t/* need to close the file */", 
      "+", 
      "+\t\tfput(*copyup_file);", 
      "+\t\tbranchput(sb, new_bindex);", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * TODO: should we reset the error to something like -EIO?", 
      "+\t *", 
      "+\t * If we don't reset, the user may get some nonsensical errors, but", 
      "+\t * on the other hand, if we reset to EIO, we guarantee that the user", 
      "+\t * will get a \"confusing\" error message.", 
      "+\t */", 
      "+", 
      "+out_unlock:", 
      "+\tunlock_dir(new_lower_parent_dentry);", 
      "+", 
      "+out_free:", 
      "+\t/*", 
      "+\t * If old_lower_dentry was not a file, then we need to dput it.  If", 
      "+\t * it was a file, then it was already dput indirectly by other", 
      "+\t * functions we call above which operate on regular files.", 
      "+\t */", 
      "+\tif (old_lower_dentry && old_lower_dentry->d_inode &&", 
      "+\t    !S_ISREG(old_lower_dentry->d_inode->i_mode))", 
      "+\t\tdput(old_lower_dentry);", 
      "+\tkfree(symbuf);", 
      "+", 
      "+\tif (err) {", 
      "+\t\t/*", 
      "+\t\t * if directory creation succeeded, but inode copyup failed,", 
      "+\t\t * then purge new dentries.", 
      "+\t\t */", 
      "+\t\tif (dbstart(dentry) < old_bstart &&", 
      "+\t\t    ibstart(dentry->d_inode) > dbstart(dentry))", 
      "+\t\t\t__clear(dentry, NULL, old_bstart, old_bend,", 
      "+\t\t\t\tunionfs_lower_dentry(dentry), dbstart(dentry));", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tif (!S_ISDIR(dentry->d_inode->i_mode)) {", 
      "+\t\tunionfs_postcopyup_release(dentry);", 
      "+\t\tif (!unionfs_lower_inode(dentry->d_inode)) {", 
      "+\t\t\t/*", 
      "+\t\t\t * If we got here, then we copied up to an", 
      "+\t\t\t * unlinked-open file, whose name is .unionfsXXXXX.", 
      "+\t\t\t */", 
      "+\t\t\tstruct inode *inode = new_lower_dentry->d_inode;", 
      "+\t\t\tatomic_inc(&inode->i_count);", 
      "+\t\t\tunionfs_set_lower_inode_idx(dentry->d_inode,", 
      "+\t\t\t\t\t\t    ibstart(dentry->d_inode),", 
      "+\t\t\t\t\t\t    inode);", 
      "+\t\t}", 
      "+\t}", 
      "+\tunionfs_postcopyup_setmnt(dentry);", 
      "+\t/* sync inode times from copied-up inode to our inode */", 
      "+\tunionfs_copy_attr_times(dentry->d_inode);", 
      "+\tunionfs_check_inode(dir);", 
      "+\tunionfs_check_dentry(dentry);", 
      "+out:", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * This function creates a copy of a file represented by 'file' which", 
      "+ * currently resides in branch 'bstart' to branch 'new_bindex.'  The copy", 
      "+ * will be named \"name\".", 
      "+ */", 
      "+int copyup_named_file(struct inode *dir, struct file *file, char *name,", 
      "+\t\t      int bstart, int new_bindex, loff_t len)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct file *output_file = NULL;", 
      "+", 
      "+\terr = copyup_dentry(dir, file->f_path.dentry, bstart, new_bindex,", 
      "+\t\t\t    name, strlen(name), &output_file, len);", 
      "+\tif (!err) {", 
      "+\t\tfbstart(file) = new_bindex;", 
      "+\t\tunionfs_set_lower_file_idx(file, new_bindex, output_file);", 
      "+\t}", 
      "+", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * This function creates a copy of a file represented by 'file' which", 
      "+ * currently resides in branch 'bstart' to branch 'new_bindex'.", 
      "+ */", 
      "+int copyup_file(struct inode *dir, struct file *file, int bstart,", 
      "+\t\tint new_bindex, loff_t len)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct file *output_file = NULL;", 
      "+\tstruct dentry *dentry = file->f_path.dentry;", 
      "+", 
      "+\terr = copyup_dentry(dir, dentry, bstart, new_bindex,", 
      "+\t\t\t    dentry->d_name.name, dentry->d_name.len,", 
      "+\t\t\t    &output_file, len);", 
      "+\tif (!err) {", 
      "+\t\tfbstart(file) = new_bindex;", 
      "+\t\tunionfs_set_lower_file_idx(file, new_bindex, output_file);", 
      "+\t}", 
      "+", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/* purge a dentry's lower-branch states (dput/mntput, etc.) */", 
      "+static void __cleanup_dentry(struct dentry *dentry, int bindex,", 
      "+\t\t\t     int old_bstart, int old_bend)", 
      "+{", 
      "+\tint loop_start;", 
      "+\tint loop_end;", 
      "+\tint new_bstart = -1;", 
      "+\tint new_bend = -1;", 
      "+\tint i;", 
      "+", 
      "+\tloop_start = min(old_bstart, bindex);", 
      "+\tloop_end = max(old_bend, bindex);", 
      "+", 
      "+\t/*", 
      "+\t * This loop sets the bstart and bend for the new dentry by", 
      "+\t * traversing from left to right.  It also dputs all negative", 
      "+\t * dentries except bindex", 
      "+\t */", 
      "+\tfor (i = loop_start; i <= loop_end; i++) {", 
      "+\t\tif (!unionfs_lower_dentry_idx(dentry, i))", 
      "+\t\t\tcontinue;", 
      "+", 
      "+\t\tif (i == bindex) {", 
      "+\t\t\tnew_bend = i;", 
      "+\t\t\tif (new_bstart < 0)", 
      "+\t\t\t\tnew_bstart = i;", 
      "+\t\t\tcontinue;", 
      "+\t\t}", 
      "+", 
      "+\t\tif (!unionfs_lower_dentry_idx(dentry, i)->d_inode) {", 
      "+\t\t\tdput(unionfs_lower_dentry_idx(dentry, i));", 
      "+\t\t\tunionfs_set_lower_dentry_idx(dentry, i, NULL);", 
      "+", 
      "+\t\t\tunionfs_mntput(dentry, i);", 
      "+\t\t\tunionfs_set_lower_mnt_idx(dentry, i, NULL);", 
      "+\t\t} else {", 
      "+\t\t\tif (new_bstart < 0)", 
      "+\t\t\t\tnew_bstart = i;", 
      "+\t\t\tnew_bend = i;", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\tif (new_bstart < 0)", 
      "+\t\tnew_bstart = bindex;", 
      "+\tif (new_bend < 0)", 
      "+\t\tnew_bend = bindex;", 
      "+\tdbstart(dentry) = new_bstart;", 
      "+\tdbend(dentry) = new_bend;", 
      "+", 
      "+}", 
      "+", 
      "+/* set lower inode ptr and update bstart & bend if necessary */", 
      "+static void __set_inode(struct dentry *upper, struct dentry *lower,", 
      "+\t\t\tint bindex)", 
      "+{", 
      "+\tunionfs_set_lower_inode_idx(upper->d_inode, bindex,", 
      "+\t\t\t\t    igrab(lower->d_inode));", 
      "+\tif (likely(ibstart(upper->d_inode) > bindex))", 
      "+\t\tibstart(upper->d_inode) = bindex;", 
      "+\tif (likely(ibend(upper->d_inode) < bindex))", 
      "+\t\tibend(upper->d_inode) = bindex;", 
      "+", 
      "+}", 
      "+", 
      "+/* set lower dentry ptr and update bstart & bend if necessary */", 
      "+static void __set_dentry(struct dentry *upper, struct dentry *lower,", 
      "+\t\t\t int bindex)", 
      "+{", 
      "+\tunionfs_set_lower_dentry_idx(upper, bindex, lower);", 
      "+\tif (likely(dbstart(upper) > bindex))", 
      "+\t\tdbstart(upper) = bindex;", 
      "+\tif (likely(dbend(upper) < bindex))", 
      "+\t\tdbend(upper) = bindex;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * This function replicates the directory structure up-to given dentry", 
      "+ * in the bindex branch.", 
      "+ */", 
      "+struct dentry *create_parents(struct inode *dir, struct dentry *dentry,", 
      "+\t\t\t      const char *name, int bindex)", 
      "+{", 
      "+\tint err;", 
      "+\tstruct dentry *child_dentry;", 
      "+\tstruct dentry *parent_dentry;", 
      "+\tstruct dentry *lower_parent_dentry = NULL;", 
      "+\tstruct dentry *lower_dentry = NULL;", 
      "+\tconst char *childname;", 
      "+\tunsigned int childnamelen;", 
      "+\tint nr_dentry;", 
      "+\tint count = 0;", 
      "+\tint old_bstart;", 
      "+\tint old_bend;", 
      "+\tstruct dentry **path = NULL;", 
      "+\tstruct super_block *sb;", 
      "+", 
      "+\tverify_locked(dentry);", 
      "+", 
      "+\terr = is_robranch_super(dir->i_sb, bindex);", 
      "+\tif (err) {", 
      "+\t\tlower_dentry = ERR_PTR(err);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\told_bstart = dbstart(dentry);", 
      "+\told_bend = dbend(dentry);", 
      "+", 
      "+\tlower_dentry = ERR_PTR(-ENOMEM);", 
      "+", 
      "+\t/* There is no sense allocating any less than the minimum. */", 
      "+\tnr_dentry = 1;", 
      "+\tpath = kmalloc(nr_dentry * sizeof(struct dentry *), GFP_KERNEL);", 
      "+\tif (unlikely(!path))", 
      "+\t\tgoto out;", 
      "+", 
      "+\t/* assume the negative dentry of unionfs as the parent dentry */", 
      "+\tparent_dentry = dentry;", 
      "+", 
      "+\t/*", 
      "+\t * This loop finds the first parent that exists in the given branch.", 
      "+\t * We start building the directory structure from there.  At the end", 
      "+\t * of the loop, the following should hold:", 
      "+\t *  - child_dentry is the first nonexistent child", 
      "+\t *  - parent_dentry is the first existent parent", 
      "+\t *  - path[0] is the = deepest child", 
      "+\t *  - path[count] is the first child to create", 
      "+\t */", 
      "+\tdo {", 
      "+\t\tchild_dentry = parent_dentry;", 
      "+", 
      "+\t\t/* find the parent directory dentry in unionfs */", 
      "+\t\tparent_dentry = dget_parent(child_dentry);", 
      "+", 
      "+\t\t/* find out the lower_parent_dentry in the given branch */", 
      "+\t\tlower_parent_dentry =", 
      "+\t\t\tunionfs_lower_dentry_idx(parent_dentry, bindex);", 
      "+", 
      "+\t\t/* grow path table */", 
      "+\t\tif (count == nr_dentry) {", 
      "+\t\t\tvoid *p;", 
      "+", 
      "+\t\t\tnr_dentry *= 2;", 
      "+\t\t\tp = krealloc(path, nr_dentry * sizeof(struct dentry *),", 
      "+\t\t\t\t     GFP_KERNEL);", 
      "+\t\t\tif (unlikely(!p)) {", 
      "+\t\t\t\tlower_dentry = ERR_PTR(-ENOMEM);", 
      "+\t\t\t\tgoto out;", 
      "+\t\t\t}", 
      "+\t\t\tpath = p;", 
      "+\t\t}", 
      "+", 
      "+\t\t/* store the child dentry */", 
      "+\t\tpath[count++] = child_dentry;", 
      "+\t} while (!lower_parent_dentry);", 
      "+\tcount--;", 
      "+", 
      "+\tsb = dentry->d_sb;", 
      "+", 
      "+\t/*", 
      "+\t * This code goes between the begin/end labels and basically", 
      "+\t * emulates a while(child_dentry != dentry), only cleaner and", 
      "+\t * shorter than what would be a much longer while loop.", 
      "+\t */", 
      "+begin:", 
      "+\t/* get lower parent dir in the current branch */", 
      "+\tlower_parent_dentry = unionfs_lower_dentry_idx(parent_dentry, bindex);", 
      "+\tdput(parent_dentry);", 
      "+", 
      "+\t/* init the values to lookup */", 
      "+\tchildname = child_dentry->d_name.name;", 
      "+\tchildnamelen = child_dentry->d_name.len;", 
      "+", 
      "+\tif (child_dentry != dentry) {", 
      "+\t\t/* lookup child in the underlying file system */", 
      "+\t\tlower_dentry = lookup_one_len(childname, lower_parent_dentry,", 
      "+\t\t\t\t\t      childnamelen);", 
      "+\t\tif (IS_ERR(lower_dentry))", 
      "+\t\t\tgoto out;", 
      "+\t} else {", 
      "+\t\t/*", 
      "+\t\t * Is the name a whiteout of the child name ?  lookup the", 
      "+\t\t * whiteout child in the underlying file system", 
      "+\t\t */", 
      "+\t\tlower_dentry = lookup_one_len(name, lower_parent_dentry,", 
      "+\t\t\t\t\t      strlen(name));", 
      "+\t\tif (IS_ERR(lower_dentry))", 
      "+\t\t\tgoto out;", 
      "+", 
      "+\t\t/* Replace the current dentry (if any) with the new one */", 
      "+\t\tdput(unionfs_lower_dentry_idx(dentry, bindex));", 
      "+\t\tunionfs_set_lower_dentry_idx(dentry, bindex,", 
      "+\t\t\t\t\t     lower_dentry);", 
      "+", 
      "+\t\t__cleanup_dentry(dentry, bindex, old_bstart, old_bend);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tif (lower_dentry->d_inode) {", 
      "+\t\t/*", 
      "+\t\t * since this already exists we dput to avoid", 
      "+\t\t * multiple references on the same dentry", 
      "+\t\t */", 
      "+\t\tdput(lower_dentry);", 
      "+\t} else {", 
      "+\t\tstruct sioq_args args;", 
      "+", 
      "+\t\t/* it's a negative dentry, create a new dir */", 
      "+\t\tlower_parent_dentry = lock_parent(lower_dentry);", 
      "+", 
      "+\t\targs.mkdir.parent = lower_parent_dentry->d_inode;", 
      "+\t\targs.mkdir.dentry = lower_dentry;", 
      "+\t\targs.mkdir.mode = child_dentry->d_inode->i_mode;", 
      "+", 
      "+\t\trun_sioq(__unionfs_mkdir, &args);", 
      "+\t\terr = args.err;", 
      "+", 
      "+\t\tif (!err)", 
      "+\t\t\terr = copyup_permissions(dir->i_sb, child_dentry,", 
      "+\t\t\t\t\t\t lower_dentry);", 
      "+\t\tunlock_dir(lower_parent_dentry);", 
      "+\t\tif (err) {", 
      "+\t\t\tdput(lower_dentry);", 
      "+\t\t\tlower_dentry = ERR_PTR(err);", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+", 
      "+\t}", 
      "+", 
      "+\t__set_inode(child_dentry, lower_dentry, bindex);", 
      "+\t__set_dentry(child_dentry, lower_dentry, bindex);", 
      "+\t/*", 
      "+\t * update times of this dentry, but also the parent, because if", 
      "+\t * we changed, the parent may have changed too.", 
      "+\t */", 
      "+\tfsstack_copy_attr_times(parent_dentry->d_inode,", 
      "+\t\t\t\tlower_parent_dentry->d_inode);", 
      "+\tunionfs_copy_attr_times(child_dentry->d_inode);", 
      "+", 
      "+\tparent_dentry = child_dentry;", 
      "+\tchild_dentry = path[--count];", 
      "+\tgoto begin;", 
      "+out:", 
      "+\t/* cleanup any leftover locks from the do/while loop above */", 
      "+\tif (IS_ERR(lower_dentry))", 
      "+\t\twhile (count)", 
      "+\t\t\tdput(path[count--]);", 
      "+\tkfree(path);", 
      "+\treturn lower_dentry;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Post-copyup helper to ensure we have valid mnts: set lower mnt of", 
      "+ * dentry+parents to the first parent node that has an mnt.", 
      "+ */", 
      "+void unionfs_postcopyup_setmnt(struct dentry *dentry)", 
      "+{", 
      "+\tstruct dentry *parent, *hasone;", 
      "+\tint bindex = dbstart(dentry);", 
      "+", 
      "+\tif (unionfs_lower_mnt_idx(dentry, bindex))", 
      "+\t\treturn;", 
      "+\thasone = dentry->d_parent;", 
      "+\t/* this loop should stop at root dentry */", 
      "+\twhile (!unionfs_lower_mnt_idx(hasone, bindex))", 
      "+\t\thasone = hasone->d_parent;", 
      "+\tparent = dentry;", 
      "+\twhile (!unionfs_lower_mnt_idx(parent, bindex)) {", 
      "+\t\tunionfs_set_lower_mnt_idx(parent, bindex,", 
      "+\t\t\t\t\t  unionfs_mntget(hasone, bindex));", 
      "+\t\tparent = parent->d_parent;", 
      "+\t}", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Post-copyup helper to release all non-directory source objects of a", 
      "+ * copied-up file.  Regular files should have only one lower object.", 
      "+ */", 
      "+void unionfs_postcopyup_release(struct dentry *dentry)", 
      "+{", 
      "+\tint bstart, bend;", 
      "+", 
      "+\tBUG_ON(S_ISDIR(dentry->d_inode->i_mode));", 
      "+\tbstart = dbstart(dentry);", 
      "+\tbend = dbend(dentry);", 
      "+", 
      "+\tpath_put_lowers(dentry, bstart + 1, bend, false);", 
      "+\tiput_lowers(dentry->d_inode, bstart + 1, bend, false);", 
      "+", 
      "+\tdbend(dentry) = bstart;", 
      "+\tibend(dentry->d_inode) = ibstart(dentry->d_inode) = bstart;", 
      "+}"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/debug.c", 
    [
      "diff --git a/fs/unionfs/debug.c b/fs/unionfs/debug.c", 
      "new file mode 100644", 
      "index 0000000..db1ae1e", 
      "--- dev/null", 
      "+++ b/fs/unionfs/debug.c", 
      "@@ -0,0 +1,537 @@", 
      "+/*", 
      "+ * Copyright (c) 2003-2009 Erez Zadok", 
      "+ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek", 
      "+ * Copyright (c) 2003-2009 Stony Brook University", 
      "+ * Copyright (c) 2003-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#include \"union.h\"", 
      "+", 
      "+/*", 
      "+ * Helper debugging functions for maintainers (and for users to report back", 
      "+ * useful information back to maintainers)", 
      "+ */", 
      "+", 
      "+#ifndef KERN_CONT", 
      "+# define KERN_CONT\t\"\"", 
      "+#endif /* not KERN_CONT */", 
      "+", 
      "+/* it's always useful to know what part of the code called us */", 
      "+#define PRINT_CALLER(fname, fxn, line)\t\t\t\t\t\\", 
      "+\tdo {\t\t\t\t\t\t\t\t\\", 
      "+\t\tif (!printed_caller) {\t\t\t\t\t\\", 
      "+\t\t\tpr_debug(\"PC:%s:%s:%d\\n\", (fname), (fxn), (line)); \\", 
      "+\t\t\tprinted_caller = 1;\t\t\t\t\\", 
      "+\t\t}\t\t\t\t\t\t\t\\", 
      "+\t} while (0)", 
      "+", 
      "+/*", 
      "+ * __unionfs_check_{inode,dentry,file} perform exhaustive sanity checking on", 
      "+ * the fan-out of various Unionfs objects.  We check that no lower objects", 
      "+ * exist  outside the start/end branch range; that all objects within are", 
      "+ * non-NULL (with some allowed exceptions); that for every lower file", 
      "+ * there's a lower dentry+inode; that the start/end ranges match for all", 
      "+ * corresponding lower objects; that open files/symlinks have only one lower", 
      "+ * objects, but directories can have several; and more.", 
      "+ */", 
      "+void __unionfs_check_inode(const struct inode *inode,", 
      "+\t\t\t   const char *fname, const char *fxn, int line)", 
      "+{", 
      "+\tint bindex;", 
      "+\tint istart, iend;", 
      "+\tstruct inode *lower_inode;", 
      "+\tstruct super_block *sb;", 
      "+\tint printed_caller = 0;", 
      "+\tvoid *poison_ptr;", 
      "+", 
      "+\t/* for inodes now */", 
      "+\tBUG_ON(!inode);", 
      "+\tsb = inode->i_sb;", 
      "+\tistart = ibstart(inode);", 
      "+\tiend = ibend(inode);", 
      "+\t/* don't check inode if no lower branches */", 
      "+\tif (istart < 0 && iend < 0)", 
      "+\t\treturn;", 
      "+\tif (unlikely(istart > iend)) {", 
      "+\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\tpr_debug(\" Ci0: inode=%p istart/end=%d:%d\\n\",", 
      "+\t\t\t inode, istart, iend);", 
      "+\t}", 
      "+\tif (unlikely((istart == -1 && iend != -1) ||", 
      "+\t\t     (istart != -1 && iend == -1))) {", 
      "+\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\tpr_debug(\" Ci1: inode=%p istart/end=%d:%d\\n\",", 
      "+\t\t\t inode, istart, iend);", 
      "+\t}", 
      "+\tif (!S_ISDIR(inode->i_mode)) {", 
      "+\t\tif (unlikely(iend != istart)) {", 
      "+\t\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\t\tpr_debug(\" Ci2: inode=%p istart=%d iend=%d\\n\",", 
      "+\t\t\t\t inode, istart, iend);", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\tfor (bindex = sbstart(sb); bindex < sbmax(sb); bindex++) {", 
      "+\t\tif (unlikely(!UNIONFS_I(inode))) {", 
      "+\t\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\t\tpr_debug(\" Ci3: no inode_info %p\\n\", inode);", 
      "+\t\t\treturn;", 
      "+\t\t}", 
      "+\t\tif (unlikely(!UNIONFS_I(inode)->lower_inodes)) {", 
      "+\t\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\t\tpr_debug(\" Ci4: no lower_inodes %p\\n\", inode);", 
      "+\t\t\treturn;", 
      "+\t\t}", 
      "+\t\tlower_inode = unionfs_lower_inode_idx(inode, bindex);", 
      "+\t\tif (lower_inode) {", 
      "+\t\t\tmemset(&poison_ptr, POISON_INUSE, sizeof(void *));", 
      "+\t\t\tif (unlikely(bindex < istart || bindex > iend)) {", 
      "+\t\t\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\t\t\tpr_debug(\" Ci5: inode/linode=%p:%p bindex=%d \"", 
      "+\t\t\t\t\t \"istart/end=%d:%d\\n\", inode,", 
      "+\t\t\t\t\t lower_inode, bindex, istart, iend);", 
      "+\t\t\t} else if (unlikely(lower_inode == poison_ptr)) {", 
      "+\t\t\t\t/* freed inode! */", 
      "+\t\t\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\t\t\tpr_debug(\" Ci6: inode/linode=%p:%p bindex=%d \"", 
      "+\t\t\t\t\t \"istart/end=%d:%d\\n\", inode,", 
      "+\t\t\t\t\t lower_inode, bindex, istart, iend);", 
      "+\t\t\t}", 
      "+\t\t\tcontinue;", 
      "+\t\t}", 
      "+\t\t/* if we get here, then lower_inode == NULL */", 
      "+\t\tif (bindex < istart || bindex > iend)", 
      "+\t\t\tcontinue;", 
      "+\t\t/*", 
      "+\t\t * directories can have NULL lower inodes in b/t start/end,", 
      "+\t\t * but NOT if at the start/end range.", 
      "+\t\t */", 
      "+\t\tif (unlikely(S_ISDIR(inode->i_mode) &&", 
      "+\t\t\t     bindex > istart && bindex < iend))", 
      "+\t\t\tcontinue;", 
      "+\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\tpr_debug(\" Ci7: inode/linode=%p:%p \"", 
      "+\t\t\t \"bindex=%d istart/end=%d:%d\\n\",", 
      "+\t\t\t inode, lower_inode, bindex, istart, iend);", 
      "+\t}", 
      "+}", 
      "+", 
      "+void __unionfs_check_dentry(const struct dentry *dentry,", 
      "+\t\t\t    const char *fname, const char *fxn, int line)", 
      "+{", 
      "+\tint bindex;", 
      "+\tint dstart, dend, istart, iend;", 
      "+\tstruct dentry *lower_dentry;", 
      "+\tstruct inode *inode, *lower_inode;", 
      "+\tstruct super_block *sb;", 
      "+\tstruct vfsmount *lower_mnt;", 
      "+\tint printed_caller = 0;", 
      "+\tvoid *poison_ptr;", 
      "+", 
      "+\tBUG_ON(!dentry);", 
      "+\tsb = dentry->d_sb;", 
      "+\tinode = dentry->d_inode;", 
      "+\tdstart = dbstart(dentry);", 
      "+\tdend = dbend(dentry);", 
      "+\t/* don't check dentry/mnt if no lower branches */", 
      "+\tif (dstart < 0 && dend < 0)", 
      "+\t\tgoto check_inode;", 
      "+\tBUG_ON(dstart > dend);", 
      "+", 
      "+\tif (unlikely((dstart == -1 && dend != -1) ||", 
      "+\t\t     (dstart != -1 && dend == -1))) {", 
      "+\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\tpr_debug(\" CD0: dentry=%p dstart/end=%d:%d\\n\",", 
      "+\t\t\t dentry, dstart, dend);", 
      "+\t}", 
      "+\t/*", 
      "+\t * check for NULL dentries inside the start/end range, or", 
      "+\t * non-NULL dentries outside the start/end range.", 
      "+\t */", 
      "+\tfor (bindex = sbstart(sb); bindex < sbmax(sb); bindex++) {", 
      "+\t\tlower_dentry = unionfs_lower_dentry_idx(dentry, bindex);", 
      "+\t\tif (lower_dentry) {", 
      "+\t\t\tif (unlikely(bindex < dstart || bindex > dend)) {", 
      "+\t\t\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\t\t\tpr_debug(\" CD1: dentry/lower=%p:%p(%p) \"", 
      "+\t\t\t\t\t \"bindex=%d dstart/end=%d:%d\\n\",", 
      "+\t\t\t\t\t dentry, lower_dentry,", 
      "+\t\t\t\t\t (lower_dentry ? lower_dentry->d_inode :", 
      "+\t\t\t\t\t  (void *) -1L),", 
      "+\t\t\t\t\t bindex, dstart, dend);", 
      "+\t\t\t}", 
      "+\t\t} else {\t/* lower_dentry == NULL */", 
      "+\t\t\tif (bindex < dstart || bindex > dend)", 
      "+\t\t\t\tcontinue;", 
      "+\t\t\t/*", 
      "+\t\t\t * Directories can have NULL lower inodes in b/t", 
      "+\t\t\t * start/end, but NOT if at the start/end range.", 
      "+\t\t\t * Ignore this rule, however, if this is a NULL", 
      "+\t\t\t * dentry or a deleted dentry.", 
      "+\t\t\t */", 
      "+\t\t\tif (unlikely(!d_deleted((struct dentry *) dentry) &&", 
      "+\t\t\t\t     inode &&", 
      "+\t\t\t\t     !(inode && S_ISDIR(inode->i_mode) &&", 
      "+\t\t\t\t       bindex > dstart && bindex < dend))) {", 
      "+\t\t\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\t\t\tpr_debug(\" CD2: dentry/lower=%p:%p(%p) \"", 
      "+\t\t\t\t\t \"bindex=%d dstart/end=%d:%d\\n\",", 
      "+\t\t\t\t\t dentry, lower_dentry,", 
      "+\t\t\t\t\t (lower_dentry ?", 
      "+\t\t\t\t\t  lower_dentry->d_inode :", 
      "+\t\t\t\t\t  (void *) -1L),", 
      "+\t\t\t\t\t bindex, dstart, dend);", 
      "+\t\t\t}", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\t/* check for vfsmounts same as for dentries */", 
      "+\tfor (bindex = sbstart(sb); bindex < sbmax(sb); bindex++) {", 
      "+\t\tlower_mnt = unionfs_lower_mnt_idx(dentry, bindex);", 
      "+\t\tif (lower_mnt) {", 
      "+\t\t\tif (unlikely(bindex < dstart || bindex > dend)) {", 
      "+\t\t\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\t\t\tpr_debug(\" CM0: dentry/lmnt=%p:%p bindex=%d \"", 
      "+\t\t\t\t\t \"dstart/end=%d:%d\\n\", dentry,", 
      "+\t\t\t\t\t lower_mnt, bindex, dstart, dend);", 
      "+\t\t\t}", 
      "+\t\t} else {\t/* lower_mnt == NULL */", 
      "+\t\t\tif (bindex < dstart || bindex > dend)", 
      "+\t\t\t\tcontinue;", 
      "+\t\t\t/*", 
      "+\t\t\t * Directories can have NULL lower inodes in b/t", 
      "+\t\t\t * start/end, but NOT if at the start/end range.", 
      "+\t\t\t * Ignore this rule, however, if this is a NULL", 
      "+\t\t\t * dentry.", 
      "+\t\t\t */", 
      "+\t\t\tif (unlikely(inode &&", 
      "+\t\t\t\t     !(inode && S_ISDIR(inode->i_mode) &&", 
      "+\t\t\t\t       bindex > dstart && bindex < dend))) {", 
      "+\t\t\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\t\t\tpr_debug(\" CM1: dentry/lmnt=%p:%p \"", 
      "+\t\t\t\t\t \"bindex=%d dstart/end=%d:%d\\n\",", 
      "+\t\t\t\t\t dentry, lower_mnt, bindex,", 
      "+\t\t\t\t\t dstart, dend);", 
      "+\t\t\t}", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+check_inode:", 
      "+\t/* for inodes now */", 
      "+\tif (!inode)", 
      "+\t\treturn;", 
      "+\tistart = ibstart(inode);", 
      "+\tiend = ibend(inode);", 
      "+\t/* don't check inode if no lower branches */", 
      "+\tif (istart < 0 && iend < 0)", 
      "+\t\treturn;", 
      "+\tBUG_ON(istart > iend);", 
      "+\tif (unlikely((istart == -1 && iend != -1) ||", 
      "+\t\t     (istart != -1 && iend == -1))) {", 
      "+\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\tpr_debug(\" CI0: dentry/inode=%p:%p istart/end=%d:%d\\n\",", 
      "+\t\t\t dentry, inode, istart, iend);", 
      "+\t}", 
      "+\tif (unlikely(istart != dstart)) {", 
      "+\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\tpr_debug(\" CI1: dentry/inode=%p:%p istart=%d dstart=%d\\n\",", 
      "+\t\t\t dentry, inode, istart, dstart);", 
      "+\t}", 
      "+\tif (unlikely(iend != dend)) {", 
      "+\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\tpr_debug(\" CI2: dentry/inode=%p:%p iend=%d dend=%d\\n\",", 
      "+\t\t\t dentry, inode, iend, dend);", 
      "+\t}", 
      "+", 
      "+\tif (!S_ISDIR(inode->i_mode)) {", 
      "+\t\tif (unlikely(dend != dstart)) {", 
      "+\t\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\t\tpr_debug(\" CI3: dentry/inode=%p:%p dstart=%d dend=%d\\n\",", 
      "+\t\t\t\t dentry, inode, dstart, dend);", 
      "+\t\t}", 
      "+\t\tif (unlikely(iend != istart)) {", 
      "+\t\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\t\tpr_debug(\" CI4: dentry/inode=%p:%p istart=%d iend=%d\\n\",", 
      "+\t\t\t\t dentry, inode, istart, iend);", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\tfor (bindex = sbstart(sb); bindex < sbmax(sb); bindex++) {", 
      "+\t\tlower_inode = unionfs_lower_inode_idx(inode, bindex);", 
      "+\t\tif (lower_inode) {", 
      "+\t\t\tmemset(&poison_ptr, POISON_INUSE, sizeof(void *));", 
      "+\t\t\tif (unlikely(bindex < istart || bindex > iend)) {", 
      "+\t\t\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\t\t\tpr_debug(\" CI5: dentry/linode=%p:%p bindex=%d \"", 
      "+\t\t\t\t\t \"istart/end=%d:%d\\n\", dentry,", 
      "+\t\t\t\t\t lower_inode, bindex, istart, iend);", 
      "+\t\t\t} else if (unlikely(lower_inode == poison_ptr)) {", 
      "+\t\t\t\t/* freed inode! */", 
      "+\t\t\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\t\t\tpr_debug(\" CI6: dentry/linode=%p:%p bindex=%d \"", 
      "+\t\t\t\t\t \"istart/end=%d:%d\\n\", dentry,", 
      "+\t\t\t\t\t lower_inode, bindex, istart, iend);", 
      "+\t\t\t}", 
      "+\t\t\tcontinue;", 
      "+\t\t}", 
      "+\t\t/* if we get here, then lower_inode == NULL */", 
      "+\t\tif (bindex < istart || bindex > iend)", 
      "+\t\t\tcontinue;", 
      "+\t\t/*", 
      "+\t\t * directories can have NULL lower inodes in b/t start/end,", 
      "+\t\t * but NOT if at the start/end range.", 
      "+\t\t */", 
      "+\t\tif (unlikely(S_ISDIR(inode->i_mode) &&", 
      "+\t\t\t     bindex > istart && bindex < iend))", 
      "+\t\t\tcontinue;", 
      "+\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\tpr_debug(\" CI7: dentry/linode=%p:%p \"", 
      "+\t\t\t \"bindex=%d istart/end=%d:%d\\n\",", 
      "+\t\t\t dentry, lower_inode, bindex, istart, iend);", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * If it's a directory, then intermediate objects b/t start/end can", 
      "+\t * be NULL.  But, check that all three are NULL: lower dentry, mnt,", 
      "+\t * and inode.", 
      "+\t */", 
      "+\tif (dstart >= 0 && dend >= 0 && S_ISDIR(inode->i_mode))", 
      "+\t\tfor (bindex = dstart+1; bindex < dend; bindex++) {", 
      "+\t\t\tlower_inode = unionfs_lower_inode_idx(inode, bindex);", 
      "+\t\t\tlower_dentry = unionfs_lower_dentry_idx(dentry,", 
      "+\t\t\t\t\t\t\t\tbindex);", 
      "+\t\t\tlower_mnt = unionfs_lower_mnt_idx(dentry, bindex);", 
      "+\t\t\tif (unlikely(!((lower_inode && lower_dentry &&", 
      "+\t\t\t\t\tlower_mnt) ||", 
      "+\t\t\t\t       (!lower_inode &&", 
      "+\t\t\t\t\t!lower_dentry && !lower_mnt)))) {", 
      "+\t\t\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\t\t\tpr_debug(\" Cx: lmnt/ldentry/linode=%p:%p:%p \"", 
      "+\t\t\t\t\t \"bindex=%d dstart/end=%d:%d\\n\",", 
      "+\t\t\t\t\t lower_mnt, lower_dentry, lower_inode,", 
      "+\t\t\t\t\t bindex, dstart, dend);", 
      "+\t\t\t}", 
      "+\t\t}", 
      "+\t/* check if lower inode is newer than upper one (it shouldn't) */", 
      "+\tif (unlikely(is_newer_lower(dentry) && !is_negative_lower(dentry))) {", 
      "+\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\tfor (bindex = ibstart(inode); bindex <= ibend(inode);", 
      "+\t\t     bindex++) {", 
      "+\t\t\tlower_inode = unionfs_lower_inode_idx(inode, bindex);", 
      "+\t\t\tif (unlikely(!lower_inode))", 
      "+\t\t\t\tcontinue;", 
      "+\t\t\tpr_debug(\" CI8: bindex=%d mtime/lmtime=%lu.%lu/%lu.%lu \"", 
      "+\t\t\t\t \"ctime/lctime=%lu.%lu/%lu.%lu\\n\",", 
      "+\t\t\t\t bindex,", 
      "+\t\t\t\t inode->i_mtime.tv_sec,", 
      "+\t\t\t\t inode->i_mtime.tv_nsec,", 
      "+\t\t\t\t lower_inode->i_mtime.tv_sec,", 
      "+\t\t\t\t lower_inode->i_mtime.tv_nsec,", 
      "+\t\t\t\t inode->i_ctime.tv_sec,", 
      "+\t\t\t\t inode->i_ctime.tv_nsec,", 
      "+\t\t\t\t lower_inode->i_ctime.tv_sec,", 
      "+\t\t\t\t lower_inode->i_ctime.tv_nsec);", 
      "+\t\t}", 
      "+\t}", 
      "+}", 
      "+", 
      "+void __unionfs_check_file(const struct file *file,", 
      "+\t\t\t  const char *fname, const char *fxn, int line)", 
      "+{", 
      "+\tint bindex;", 
      "+\tint dstart, dend, fstart, fend;", 
      "+\tstruct dentry *dentry;", 
      "+\tstruct file *lower_file;", 
      "+\tstruct inode *inode;", 
      "+\tstruct super_block *sb;", 
      "+\tint printed_caller = 0;", 
      "+", 
      "+\tBUG_ON(!file);", 
      "+\tdentry = file->f_path.dentry;", 
      "+\tsb = dentry->d_sb;", 
      "+\tdstart = dbstart(dentry);", 
      "+\tdend = dbend(dentry);", 
      "+\tBUG_ON(dstart > dend);", 
      "+\tfstart = fbstart(file);", 
      "+\tfend = fbend(file);", 
      "+\tBUG_ON(fstart > fend);", 
      "+", 
      "+\tif (unlikely((fstart == -1 && fend != -1) ||", 
      "+\t\t     (fstart != -1 && fend == -1))) {", 
      "+\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\tpr_debug(\" CF0: file/dentry=%p:%p fstart/end=%d:%d\\n\",", 
      "+\t\t\t file, dentry, fstart, fend);", 
      "+\t}", 
      "+\tif (unlikely(fstart != dstart)) {", 
      "+\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\tpr_debug(\" CF1: file/dentry=%p:%p fstart=%d dstart=%d\\n\",", 
      "+\t\t\t file, dentry, fstart, dstart);", 
      "+\t}", 
      "+\tif (unlikely(fend != dend)) {", 
      "+\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\tpr_debug(\" CF2: file/dentry=%p:%p fend=%d dend=%d\\n\",", 
      "+\t\t\t file, dentry, fend, dend);", 
      "+\t}", 
      "+\tinode = dentry->d_inode;", 
      "+\tif (!S_ISDIR(inode->i_mode)) {", 
      "+\t\tif (unlikely(fend != fstart)) {", 
      "+\t\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\t\tpr_debug(\" CF3: file/inode=%p:%p fstart=%d fend=%d\\n\",", 
      "+\t\t\t\t file, inode, fstart, fend);", 
      "+\t\t}", 
      "+\t\tif (unlikely(dend != dstart)) {", 
      "+\t\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\t\tpr_debug(\" CF4: file/dentry=%p:%p dstart=%d dend=%d\\n\",", 
      "+\t\t\t\t file, dentry, dstart, dend);", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * check for NULL dentries inside the start/end range, or", 
      "+\t * non-NULL dentries outside the start/end range.", 
      "+\t */", 
      "+\tfor (bindex = sbstart(sb); bindex < sbmax(sb); bindex++) {", 
      "+\t\tlower_file = unionfs_lower_file_idx(file, bindex);", 
      "+\t\tif (lower_file) {", 
      "+\t\t\tif (unlikely(bindex < fstart || bindex > fend)) {", 
      "+\t\t\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\t\t\tpr_debug(\" CF5: file/lower=%p:%p bindex=%d \"", 
      "+\t\t\t\t\t \"fstart/end=%d:%d\\n\", file,", 
      "+\t\t\t\t\t lower_file, bindex, fstart, fend);", 
      "+\t\t\t}", 
      "+\t\t} else {\t/* lower_file == NULL */", 
      "+\t\t\tif (bindex >= fstart && bindex <= fend) {", 
      "+\t\t\t\t/*", 
      "+\t\t\t\t * directories can have NULL lower inodes in", 
      "+\t\t\t\t * b/t start/end, but NOT if at the", 
      "+\t\t\t\t * start/end range.", 
      "+\t\t\t\t */", 
      "+\t\t\t\tif (unlikely(!(S_ISDIR(inode->i_mode) &&", 
      "+\t\t\t\t\t       bindex > fstart &&", 
      "+\t\t\t\t\t       bindex < fend))) {", 
      "+\t\t\t\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\t\t\t\tpr_debug(\" CF6: file/lower=%p:%p \"", 
      "+\t\t\t\t\t\t \"bindex=%d fstart/end=%d:%d\\n\",", 
      "+\t\t\t\t\t\t file, lower_file, bindex,", 
      "+\t\t\t\t\t\t fstart, fend);", 
      "+\t\t\t\t}", 
      "+\t\t\t}", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\t__unionfs_check_dentry(dentry, fname, fxn, line);", 
      "+}", 
      "+", 
      "+void __unionfs_check_nd(const struct nameidata *nd,", 
      "+\t\t\tconst char *fname, const char *fxn, int line)", 
      "+{", 
      "+\tstruct file *file;", 
      "+\tint printed_caller = 0;", 
      "+", 
      "+\tif (unlikely(!nd))", 
      "+\t\treturn;", 
      "+\tif (nd->flags & LOOKUP_OPEN) {", 
      "+\t\tfile = nd->intent.open.file;", 
      "+\t\tif (unlikely(file->f_path.dentry &&", 
      "+\t\t\t     strcmp(file->f_path.dentry->d_sb->s_type->name,", 
      "+\t\t\t\t    UNIONFS_NAME))) {", 
      "+\t\t\tPRINT_CALLER(fname, fxn, line);", 
      "+\t\t\tpr_debug(\" CND1: lower_file of type %s\\n\",", 
      "+\t\t\t\t file->f_path.dentry->d_sb->s_type->name);", 
      "+\t\t\tBUG();", 
      "+\t\t}", 
      "+\t}", 
      "+}", 
      "+", 
      "+/* useful to track vfsmount leaks that could cause EBUSY on unmount */", 
      "+void __show_branch_counts(const struct super_block *sb,", 
      "+\t\t\t  const char *file, const char *fxn, int line)", 
      "+{", 
      "+\tint i;", 
      "+\tstruct vfsmount *mnt;", 
      "+", 
      "+\tpr_debug(\"BC:\");", 
      "+\tfor (i = 0; i < sbmax(sb); i++) {", 
      "+\t\tif (likely(sb->s_root))", 
      "+\t\t\tmnt = UNIONFS_D(sb->s_root)->lower_paths[i].mnt;", 
      "+\t\telse", 
      "+\t\t\tmnt = NULL;", 
      "+\t\tprintk(KERN_CONT \"%d:\",", 
      "+\t\t       (mnt ? atomic_read(&mnt->mnt_count) : -99));", 
      "+\t}", 
      "+\tprintk(KERN_CONT \"%s:%s:%d\\n\", file, fxn, line);", 
      "+}", 
      "+", 
      "+void __show_inode_times(const struct inode *inode,", 
      "+\t\t\tconst char *file, const char *fxn, int line)", 
      "+{", 
      "+\tstruct inode *lower_inode;", 
      "+\tint bindex;", 
      "+", 
      "+\tfor (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) {", 
      "+\t\tlower_inode = unionfs_lower_inode_idx(inode, bindex);", 
      "+\t\tif (unlikely(!lower_inode))", 
      "+\t\t\tcontinue;", 
      "+\t\tpr_debug(\"IT(%lu:%d): %s:%s:%d \"", 
      "+\t\t\t \"um=%lu/%lu lm=%lu/%lu uc=%lu/%lu lc=%lu/%lu\\n\",", 
      "+\t\t\t inode->i_ino, bindex,", 
      "+\t\t\t file, fxn, line,", 
      "+\t\t\t inode->i_mtime.tv_sec, inode->i_mtime.tv_nsec,", 
      "+\t\t\t lower_inode->i_mtime.tv_sec,", 
      "+\t\t\t lower_inode->i_mtime.tv_nsec,", 
      "+\t\t\t inode->i_ctime.tv_sec, inode->i_ctime.tv_nsec,", 
      "+\t\t\t lower_inode->i_ctime.tv_sec,", 
      "+\t\t\t lower_inode->i_ctime.tv_nsec);", 
      "+\t}", 
      "+}", 
      "+", 
      "+void __show_dinode_times(const struct dentry *dentry,", 
      "+\t\t\tconst char *file, const char *fxn, int line)", 
      "+{", 
      "+\tstruct inode *inode = dentry->d_inode;", 
      "+\tstruct inode *lower_inode;", 
      "+\tint bindex;", 
      "+", 
      "+\tfor (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) {", 
      "+\t\tlower_inode = unionfs_lower_inode_idx(inode, bindex);", 
      "+\t\tif (!lower_inode)", 
      "+\t\t\tcontinue;", 
      "+\t\tpr_debug(\"DT(%s:%lu:%d): %s:%s:%d \"", 
      "+\t\t\t \"um=%lu/%lu lm=%lu/%lu uc=%lu/%lu lc=%lu/%lu\\n\",", 
      "+\t\t\t dentry->d_name.name, inode->i_ino, bindex,", 
      "+\t\t\t file, fxn, line,", 
      "+\t\t\t inode->i_mtime.tv_sec, inode->i_mtime.tv_nsec,", 
      "+\t\t\t lower_inode->i_mtime.tv_sec,", 
      "+\t\t\t lower_inode->i_mtime.tv_nsec,", 
      "+\t\t\t inode->i_ctime.tv_sec, inode->i_ctime.tv_nsec,", 
      "+\t\t\t lower_inode->i_ctime.tv_sec,", 
      "+\t\t\t lower_inode->i_ctime.tv_nsec);", 
      "+\t}", 
      "+}", 
      "+", 
      "+void __show_inode_counts(const struct inode *inode,", 
      "+\t\t\tconst char *file, const char *fxn, int line)", 
      "+{", 
      "+\tstruct inode *lower_inode;", 
      "+\tint bindex;", 
      "+", 
      "+\tif (unlikely(!inode)) {", 
      "+\t\tpr_debug(\"SiC: Null inode\\n\");", 
      "+\t\treturn;", 
      "+\t}", 
      "+\tfor (bindex = sbstart(inode->i_sb); bindex <= sbend(inode->i_sb);", 
      "+\t     bindex++) {", 
      "+\t\tlower_inode = unionfs_lower_inode_idx(inode, bindex);", 
      "+\t\tif (unlikely(!lower_inode))", 
      "+\t\t\tcontinue;", 
      "+\t\tpr_debug(\"SIC(%lu:%d:%d): lc=%d %s:%s:%d\\n\",", 
      "+\t\t\t inode->i_ino, bindex,", 
      "+\t\t\t atomic_read(&(inode)->i_count),", 
      "+\t\t\t atomic_read(&(lower_inode)->i_count),", 
      "+\t\t\t file, fxn, line);", 
      "+\t}", 
      "+}"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/dentry.c", 
    [
      "diff --git a/fs/unionfs/dentry.c b/fs/unionfs/dentry.c", 
      "new file mode 100644", 
      "index 0000000..85b5d3c", 
      "--- dev/null", 
      "+++ b/fs/unionfs/dentry.c", 
      "@@ -0,0 +1,397 @@", 
      "+/*", 
      "+ * Copyright (c) 2003-2009 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-2009 Stony Brook University", 
      "+ * Copyright (c) 2003-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#include \"union.h\"", 
      "+", 
      "+bool is_negative_lower(const struct dentry *dentry)", 
      "+{", 
      "+\tint bindex;", 
      "+\tstruct dentry *lower_dentry;", 
      "+", 
      "+\tBUG_ON(!dentry);", 
      "+\t/* cache coherency: check if file was deleted on lower branch */", 
      "+\tif (dbstart(dentry) < 0)", 
      "+\t\treturn true;", 
      "+\tfor (bindex = dbstart(dentry); bindex <= dbend(dentry); bindex++) {", 
      "+\t\tlower_dentry = unionfs_lower_dentry_idx(dentry, bindex);", 
      "+\t\t/* unhashed (i.e., unlinked) lower dentries don't count */", 
      "+\t\tif (lower_dentry && lower_dentry->d_inode &&", 
      "+\t\t    !d_deleted(lower_dentry) &&", 
      "+\t\t    !(lower_dentry->d_flags & DCACHE_NFSFS_RENAMED))", 
      "+\t\t\treturn false;", 
      "+\t}", 
      "+\treturn true;", 
      "+}", 
      "+", 
      "+static inline void __dput_lowers(struct dentry *dentry, int start, int end)", 
      "+{", 
      "+\tstruct dentry *lower_dentry;", 
      "+\tint bindex;", 
      "+", 
      "+\tif (start < 0)", 
      "+\t\treturn;", 
      "+\tfor (bindex = start; bindex <= end; bindex++) {", 
      "+\t\tlower_dentry = unionfs_lower_dentry_idx(dentry, bindex);", 
      "+\t\tif (!lower_dentry)", 
      "+\t\t\tcontinue;", 
      "+\t\tunionfs_set_lower_dentry_idx(dentry, bindex, NULL);", 
      "+\t\tdput(lower_dentry);", 
      "+\t}", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Purge and invalidate as many data pages of a unionfs inode.  This is", 
      "+ * called when the lower inode has changed, and we want to force processes", 
      "+ * to re-get the new data.", 
      "+ */", 
      "+static inline void purge_inode_data(struct inode *inode)", 
      "+{", 
      "+\t/* remove all non-private mappings */", 
      "+\tunmap_mapping_range(inode->i_mapping, 0, 0, 0);", 
      "+\t/* invalidate as many pages as possible */", 
      "+\tinvalidate_mapping_pages(inode->i_mapping, 0, -1);", 
      "+\t/*", 
      "+\t * Don't try to truncate_inode_pages here, because this could lead", 
      "+\t * to a deadlock between some of address_space ops and dentry", 
      "+\t * revalidation: the address space op is invoked with a lock on our", 
      "+\t * own page, and truncate_inode_pages will block on locked pages.", 
      "+\t */", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Revalidate a single file/symlink/special dentry.  Assume that info nodes", 
      "+ * of the @dentry and its @parent are locked.  Assume parent is valid,", 
      "+ * otherwise return false (and let's hope the VFS will try to re-lookup this", 
      "+ * dentry).  Returns true if valid, false otherwise.", 
      "+ */", 
      "+bool __unionfs_d_revalidate(struct dentry *dentry, struct dentry *parent,", 
      "+\t\t\t    bool willwrite)", 
      "+{", 
      "+\tbool valid = true;\t/* default is valid */", 
      "+\tstruct dentry *lower_dentry;", 
      "+\tstruct dentry *result;", 
      "+\tint bindex, bstart, bend;", 
      "+\tint sbgen, dgen, pdgen;", 
      "+\tint positive = 0;", 
      "+\tint interpose_flag;", 
      "+", 
      "+\tverify_locked(dentry);", 
      "+\tverify_locked(parent);", 
      "+", 
      "+\t/* if the dentry is unhashed, do NOT revalidate */", 
      "+\tif (d_deleted(dentry))", 
      "+\t\tgoto out;", 
      "+", 
      "+\tdgen = atomic_read(&UNIONFS_D(dentry)->generation);", 
      "+", 
      "+\tif (is_newer_lower(dentry)) {", 
      "+\t\t/* root dentry is always valid */", 
      "+\t\tif (IS_ROOT(dentry)) {", 
      "+\t\t\tunionfs_copy_attr_times(dentry->d_inode);", 
      "+\t\t} else {", 
      "+\t\t\t/*", 
      "+\t\t\t * reset generation number to zero, guaranteed to be", 
      "+\t\t\t * \"old\"", 
      "+\t\t\t */", 
      "+\t\t\tdgen = 0;", 
      "+\t\t\tatomic_set(&UNIONFS_D(dentry)->generation, dgen);", 
      "+\t\t}", 
      "+\t\tif (!willwrite)", 
      "+\t\t\tpurge_inode_data(dentry->d_inode);", 
      "+\t}", 
      "+", 
      "+\tsbgen = atomic_read(&UNIONFS_SB(dentry->d_sb)->generation);", 
      "+", 
      "+\tBUG_ON(dbstart(dentry) == -1);", 
      "+\tif (dentry->d_inode)", 
      "+\t\tpositive = 1;", 
      "+", 
      "+\t/* if our dentry is valid, then validate all lower ones */", 
      "+\tif (sbgen == dgen)", 
      "+\t\tgoto validate_lowers;", 
      "+", 
      "+\t/* The root entry should always be valid */", 
      "+\tBUG_ON(IS_ROOT(dentry));", 
      "+", 
      "+\t/* We can't work correctly if our parent isn't valid. */", 
      "+\tpdgen = atomic_read(&UNIONFS_D(parent)->generation);", 
      "+", 
      "+\t/* Free the pointers for our inodes and this dentry. */", 
      "+\tpath_put_lowers_all(dentry, false);", 
      "+", 
      "+\tinterpose_flag = INTERPOSE_REVAL_NEG;", 
      "+\tif (positive) {", 
      "+\t\tinterpose_flag = INTERPOSE_REVAL;", 
      "+\t\tiput_lowers_all(dentry->d_inode, true);", 
      "+\t}", 
      "+", 
      "+\tif (realloc_dentry_private_data(dentry) != 0) {", 
      "+\t\tvalid = false;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tresult = unionfs_lookup_full(dentry, parent, interpose_flag);", 
      "+\tif (result) {", 
      "+\t\tif (IS_ERR(result)) {", 
      "+\t\t\tvalid = false;", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+\t\t/*", 
      "+\t\t * current unionfs_lookup_backend() doesn't return", 
      "+\t\t * a valid dentry", 
      "+\t\t */", 
      "+\t\tdput(dentry);", 
      "+\t\tdentry = result;", 
      "+\t}", 
      "+", 
      "+\tif (unlikely(positive && is_negative_lower(dentry))) {", 
      "+\t\t/* call make_bad_inode here ? */", 
      "+\t\td_drop(dentry);", 
      "+\t\tvalid = false;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * if we got here then we have revalidated our dentry and all lower", 
      "+\t * ones, so we can return safely.", 
      "+\t */", 
      "+\tif (!valid)\t\t/* lower dentry revalidation failed */", 
      "+\t\tgoto out;", 
      "+", 
      "+\t/*", 
      "+\t * If the parent's gen no.  matches the superblock's gen no., then", 
      "+\t * we can update our denty's gen no.  If they didn't match, then it", 
      "+\t * was OK to revalidate this dentry with a stale parent, but we'll", 
      "+\t * purposely not update our dentry's gen no. (so it can be redone);", 
      "+\t * and, we'll mark our parent dentry as invalid so it'll force it", 
      "+\t * (and our dentry) to be revalidated.", 
      "+\t */", 
      "+\tif (pdgen == sbgen)", 
      "+\t\tatomic_set(&UNIONFS_D(dentry)->generation, sbgen);", 
      "+\tgoto out;", 
      "+", 
      "+validate_lowers:", 
      "+", 
      "+\t/* The revalidation must occur across all branches */", 
      "+\tbstart = dbstart(dentry);", 
      "+\tbend = dbend(dentry);", 
      "+\tBUG_ON(bstart == -1);", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tlower_dentry = unionfs_lower_dentry_idx(dentry, bindex);", 
      "+\t\tif (!lower_dentry || !lower_dentry->d_op", 
      "+\t\t    || !lower_dentry->d_op->d_revalidate)", 
      "+\t\t\tcontinue;", 
      "+\t\t/*", 
      "+\t\t * Don't pass nameidata to lower file system, because we", 
      "+\t\t * don't want an arbitrary lower file being opened or", 
      "+\t\t * returned to us: it may be useless to us because of the", 
      "+\t\t * fanout nature of unionfs (cf. file/directory open-file", 
      "+\t\t * invariants).  We will open lower files as and when needed", 
      "+\t\t * later on.", 
      "+\t\t */", 
      "+\t\tif (!lower_dentry->d_op->d_revalidate(lower_dentry, NULL))", 
      "+\t\t\tvalid = false;", 
      "+\t}", 
      "+", 
      "+\tif (!dentry->d_inode ||", 
      "+\t    ibstart(dentry->d_inode) < 0 ||", 
      "+\t    ibend(dentry->d_inode) < 0) {", 
      "+\t\tvalid = false;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tif (valid) {", 
      "+\t\t/*", 
      "+\t\t * If we get here, and we copy the meta-data from the lower", 
      "+\t\t * inode to our inode, then it is vital that we have already", 
      "+\t\t * purged all unionfs-level file data.  We do that in the", 
      "+\t\t * caller (__unionfs_d_revalidate) by calling", 
      "+\t\t * purge_inode_data.", 
      "+\t\t */", 
      "+\t\tunionfs_copy_attr_all(dentry->d_inode,", 
      "+\t\t\t\t      unionfs_lower_inode(dentry->d_inode));", 
      "+\t\tfsstack_copy_inode_size(dentry->d_inode,", 
      "+\t\t\t\t\tunionfs_lower_inode(dentry->d_inode));", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\treturn valid;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Determine if the lower inode objects have changed from below the unionfs", 
      "+ * inode.  Return true if changed, false otherwise.", 
      "+ *", 
      "+ * We check if the mtime or ctime have changed.  However, the inode times", 
      "+ * can be changed by anyone without much protection, including", 
      "+ * asynchronously.  This can sometimes cause unionfs to find that the lower", 
      "+ * file system doesn't change its inode times quick enough, resulting in a", 
      "+ * false positive indication (which is harmless, it just makes unionfs do", 
      "+ * extra work in re-validating the objects).  To minimize the chances of", 
      "+ * these situations, we still consider such small time changes valid, but we", 
      "+ * don't print debugging messages unless the time changes are greater than", 
      "+ * UNIONFS_MIN_CC_TIME (which defaults to 3 seconds, as with NFS's acregmin)", 
      "+ * because significant changes are more likely due to users manually", 
      "+ * touching lower files.", 
      "+ */", 
      "+bool is_newer_lower(const struct dentry *dentry)", 
      "+{", 
      "+\tint bindex;", 
      "+\tstruct inode *inode;", 
      "+\tstruct inode *lower_inode;", 
      "+", 
      "+\t/* ignore if we're called on semi-initialized dentries/inodes */", 
      "+\tif (!dentry || !UNIONFS_D(dentry))", 
      "+\t\treturn false;", 
      "+\tinode = dentry->d_inode;", 
      "+\tif (!inode || !UNIONFS_I(inode)->lower_inodes ||", 
      "+\t    ibstart(inode) < 0 || ibend(inode) < 0)", 
      "+\t\treturn false;", 
      "+", 
      "+\tfor (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) {", 
      "+\t\tlower_inode = unionfs_lower_inode_idx(inode, bindex);", 
      "+\t\tif (!lower_inode)", 
      "+\t\t\tcontinue;", 
      "+", 
      "+\t\t/* check if mtime/ctime have changed */", 
      "+\t\tif (unlikely(timespec_compare(&inode->i_mtime,", 
      "+\t\t\t\t\t      &lower_inode->i_mtime) < 0)) {", 
      "+\t\t\tif ((lower_inode->i_mtime.tv_sec -", 
      "+\t\t\t     inode->i_mtime.tv_sec) > UNIONFS_MIN_CC_TIME) {", 
      "+\t\t\t\tpr_info(\"unionfs: new lower inode mtime \"", 
      "+\t\t\t\t\t\"(bindex=%d, name=%s)\\n\", bindex,", 
      "+\t\t\t\t\tdentry->d_name.name);", 
      "+\t\t\t\tshow_dinode_times(dentry);", 
      "+\t\t\t}", 
      "+\t\t\treturn true;", 
      "+\t\t}", 
      "+\t\tif (unlikely(timespec_compare(&inode->i_ctime,", 
      "+\t\t\t\t\t      &lower_inode->i_ctime) < 0)) {", 
      "+\t\t\tif ((lower_inode->i_ctime.tv_sec -", 
      "+\t\t\t     inode->i_ctime.tv_sec) > UNIONFS_MIN_CC_TIME) {", 
      "+\t\t\t\tpr_info(\"unionfs: new lower inode ctime \"", 
      "+\t\t\t\t\t\"(bindex=%d, name=%s)\\n\", bindex,", 
      "+\t\t\t\t\tdentry->d_name.name);", 
      "+\t\t\t\tshow_dinode_times(dentry);", 
      "+\t\t\t}", 
      "+\t\t\treturn true;", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * Last check: if this is a positive dentry, but somehow all lower", 
      "+\t * dentries are negative or unhashed, then this dentry needs to be", 
      "+\t * revalidated, because someone probably deleted the objects from", 
      "+\t * the lower branches directly.", 
      "+\t */", 
      "+\tif (is_negative_lower(dentry))", 
      "+\t\treturn true;", 
      "+", 
      "+\treturn false;\t\t/* default: lower is not newer */", 
      "+}", 
      "+", 
      "+static int unionfs_d_revalidate(struct dentry *dentry,", 
      "+\t\t\t\tstruct nameidata *nd_unused)", 
      "+{", 
      "+\tbool valid = true;", 
      "+\tint err = 1;\t\t/* 1 means valid for the VFS */", 
      "+\tstruct dentry *parent;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\tvalid = __unionfs_d_revalidate(dentry, parent, false);", 
      "+\tif (valid) {", 
      "+\t\tunionfs_postcopyup_setmnt(dentry);", 
      "+\t\tunionfs_check_dentry(dentry);", 
      "+\t} else {", 
      "+\t\td_drop(dentry);", 
      "+\t\terr = valid;", 
      "+\t}", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+static void unionfs_d_release(struct dentry *dentry)", 
      "+{", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);", 
      "+\tif (unlikely(!UNIONFS_D(dentry)))", 
      "+\t\tgoto out;\t/* skip if no lower branches */", 
      "+\t/* must lock our branch configuration here */", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\tunionfs_check_dentry(dentry);", 
      "+\t/* this could be a negative dentry, so check first */", 
      "+\tif (dbstart(dentry) < 0) {", 
      "+\t\tunionfs_unlock_dentry(dentry);", 
      "+\t\tgoto out;\t/* due to a (normal) failed lookup */", 
      "+\t}", 
      "+", 
      "+\t/* Release all the lower dentries */", 
      "+\tpath_put_lowers_all(dentry, true);", 
      "+", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+", 
      "+out:", 
      "+\tfree_dentry_private_data(dentry);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+\treturn;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Called when we're removing the last reference to our dentry.  So we", 
      "+ * should drop all lower references too.", 
      "+ */", 
      "+static void unionfs_d_iput(struct dentry *dentry, struct inode *inode)", 
      "+{", 
      "+\tint rc;", 
      "+", 
      "+\tBUG_ON(!dentry);", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\tif (!UNIONFS_D(dentry) || dbstart(dentry) < 0)", 
      "+\t\tgoto drop_lower_inodes;", 
      "+\tpath_put_lowers_all(dentry, false);", 
      "+", 
      "+drop_lower_inodes:", 
      "+\trc = atomic_read(&inode->i_count);", 
      "+\tif (rc == 1 && inode->i_nlink == 1 && ibstart(inode) >= 0) {", 
      "+\t\t/* see Documentation/filesystems/unionfs/issues.txt */", 
      "+\t\tlockdep_off();", 
      "+\t\tiput(unionfs_lower_inode(inode));", 
      "+\t\tlockdep_on();", 
      "+\t\tunionfs_set_lower_inode(inode, NULL);", 
      "+\t\t/* XXX: may need to set start/end to -1? */", 
      "+\t}", 
      "+", 
      "+\tiput(inode);", 
      "+", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+}", 
      "+", 
      "+struct dentry_operations unionfs_dops = {", 
      "+\t.d_revalidate\t= unionfs_d_revalidate,", 
      "+\t.d_release\t= unionfs_d_release,", 
      "+\t.d_iput\t\t= unionfs_d_iput,", 
      "+};"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/dirfops.c", 
    [
      "diff --git a/fs/unionfs/dirfops.c b/fs/unionfs/dirfops.c", 
      "new file mode 100644", 
      "index 0000000..eccb9ae", 
      "--- dev/null", 
      "+++ b/fs/unionfs/dirfops.c", 
      "@@ -0,0 +1,302 @@", 
      "+/*", 
      "+ * Copyright (c) 2003-2009 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-2009 Stony Brook University", 
      "+ * Copyright (c) 2003-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#include \"union.h\"", 
      "+", 
      "+/* Make sure our rdstate is playing by the rules. */", 
      "+static void verify_rdstate_offset(struct unionfs_dir_state *rdstate)", 
      "+{", 
      "+\tBUG_ON(rdstate->offset >= DIREOF);", 
      "+\tBUG_ON(rdstate->cookie >= MAXRDCOOKIE);", 
      "+}", 
      "+", 
      "+struct unionfs_getdents_callback {", 
      "+\tstruct unionfs_dir_state *rdstate;", 
      "+\tvoid *dirent;", 
      "+\tint entries_written;", 
      "+\tint filldir_called;", 
      "+\tint filldir_error;", 
      "+\tfilldir_t filldir;", 
      "+\tstruct super_block *sb;", 
      "+};", 
      "+", 
      "+/* based on generic filldir in fs/readir.c */", 
      "+static int unionfs_filldir(void *dirent, const char *oname, int namelen,", 
      "+\t\t\t   loff_t offset, u64 ino, unsigned int d_type)", 
      "+{", 
      "+\tstruct unionfs_getdents_callback *buf = dirent;", 
      "+\tstruct filldir_node *found = NULL;", 
      "+\tint err = 0;", 
      "+\tint is_whiteout;", 
      "+\tchar *name = (char *) oname;", 
      "+", 
      "+\tbuf->filldir_called++;", 
      "+", 
      "+\tis_whiteout = is_whiteout_name(&name, &namelen);", 
      "+", 
      "+\tfound = find_filldir_node(buf->rdstate, name, namelen, is_whiteout);", 
      "+", 
      "+\tif (found) {", 
      "+\t\t/*", 
      "+\t\t * If we had non-whiteout entry in dir cache, then mark it", 
      "+\t\t * as a whiteout and but leave it in the dir cache.", 
      "+\t\t */", 
      "+\t\tif (is_whiteout && !found->whiteout)", 
      "+\t\t\tfound->whiteout = is_whiteout;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/* if 'name' isn't a whiteout, filldir it. */", 
      "+\tif (!is_whiteout) {", 
      "+\t\toff_t pos = rdstate2offset(buf->rdstate);", 
      "+\t\tu64 unionfs_ino = ino;", 
      "+", 
      "+\t\terr = buf->filldir(buf->dirent, name, namelen, pos,", 
      "+\t\t\t\t   unionfs_ino, d_type);", 
      "+\t\tbuf->rdstate->offset++;", 
      "+\t\tverify_rdstate_offset(buf->rdstate);", 
      "+\t}", 
      "+\t/*", 
      "+\t * If we did fill it, stuff it in our hash, otherwise return an", 
      "+\t * error.", 
      "+\t */", 
      "+\tif (err) {", 
      "+\t\tbuf->filldir_error = err;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tbuf->entries_written++;", 
      "+\terr = add_filldir_node(buf->rdstate, name, namelen,", 
      "+\t\t\t       buf->rdstate->bindex, is_whiteout);", 
      "+\tif (err)", 
      "+\t\tbuf->filldir_error = err;", 
      "+", 
      "+out:", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+static int unionfs_readdir(struct file *file, void *dirent, filldir_t filldir)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct file *lower_file = NULL;", 
      "+\tstruct dentry *dentry = file->f_path.dentry;", 
      "+\tstruct dentry *parent;", 
      "+\tstruct inode *inode = NULL;", 
      "+\tstruct unionfs_getdents_callback buf;", 
      "+\tstruct unionfs_dir_state *uds;", 
      "+\tint bend;", 
      "+\tloff_t offset;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\terr = unionfs_file_revalidate(file, parent, false);", 
      "+\tif (unlikely(err))", 
      "+\t\tgoto out;", 
      "+", 
      "+\tinode = dentry->d_inode;", 
      "+", 
      "+\tuds = UNIONFS_F(file)->rdstate;", 
      "+\tif (!uds) {", 
      "+\t\tif (file->f_pos == DIREOF) {", 
      "+\t\t\tgoto out;", 
      "+\t\t} else if (file->f_pos > 0) {", 
      "+\t\t\tuds = find_rdstate(inode, file->f_pos);", 
      "+\t\t\tif (unlikely(!uds)) {", 
      "+\t\t\t\terr = -ESTALE;", 
      "+\t\t\t\tgoto out;", 
      "+\t\t\t}", 
      "+\t\t\tUNIONFS_F(file)->rdstate = uds;", 
      "+\t\t} else {", 
      "+\t\t\tinit_rdstate(file);", 
      "+\t\t\tuds = UNIONFS_F(file)->rdstate;", 
      "+\t\t}", 
      "+\t}", 
      "+\tbend = fbend(file);", 
      "+", 
      "+\twhile (uds->bindex <= bend) {", 
      "+\t\tlower_file = unionfs_lower_file_idx(file, uds->bindex);", 
      "+\t\tif (!lower_file) {", 
      "+\t\t\tuds->bindex++;", 
      "+\t\t\tuds->dirpos = 0;", 
      "+\t\t\tcontinue;", 
      "+\t\t}", 
      "+", 
      "+\t\t/* prepare callback buffer */", 
      "+\t\tbuf.filldir_called = 0;", 
      "+\t\tbuf.filldir_error = 0;", 
      "+\t\tbuf.entries_written = 0;", 
      "+\t\tbuf.dirent = dirent;", 
      "+\t\tbuf.filldir = filldir;", 
      "+\t\tbuf.rdstate = uds;", 
      "+\t\tbuf.sb = inode->i_sb;", 
      "+", 
      "+\t\t/* Read starting from where we last left off. */", 
      "+\t\toffset = vfs_llseek(lower_file, uds->dirpos, SEEK_SET);", 
      "+\t\tif (offset < 0) {", 
      "+\t\t\terr = offset;", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+\t\terr = vfs_readdir(lower_file, unionfs_filldir, &buf);", 
      "+", 
      "+\t\t/* Save the position for when we continue. */", 
      "+\t\toffset = vfs_llseek(lower_file, 0, SEEK_CUR);", 
      "+\t\tif (offset < 0) {", 
      "+\t\t\terr = offset;", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+\t\tuds->dirpos = offset;", 
      "+", 
      "+\t\t/* Copy the atime. */", 
      "+\t\tfsstack_copy_attr_atime(inode,", 
      "+\t\t\t\t\tlower_file->f_path.dentry->d_inode);", 
      "+", 
      "+\t\tif (err < 0)", 
      "+\t\t\tgoto out;", 
      "+", 
      "+\t\tif (buf.filldir_error)", 
      "+\t\t\tbreak;", 
      "+", 
      "+\t\tif (!buf.entries_written) {", 
      "+\t\t\tuds->bindex++;", 
      "+\t\t\tuds->dirpos = 0;", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\tif (!buf.filldir_error && uds->bindex >= bend) {", 
      "+\t\t/* Save the number of hash entries for next time. */", 
      "+\t\tUNIONFS_I(inode)->hashsize = uds->hashentries;", 
      "+\t\tfree_rdstate(uds);", 
      "+\t\tUNIONFS_F(file)->rdstate = NULL;", 
      "+\t\tfile->f_pos = DIREOF;", 
      "+\t} else {", 
      "+\t\tfile->f_pos = rdstate2offset(uds);", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\tif (!err)", 
      "+\t\tunionfs_check_file(file);", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * This is not meant to be a generic repositioning function.  If you do", 
      "+ * things that aren't supported, then we return EINVAL.", 
      "+ *", 
      "+ * What is allowed:", 
      "+ *  (1) seeking to the same position that you are currently at", 
      "+ *\tThis really has no effect, but returns where you are.", 
      "+ *  (2) seeking to the beginning of the file", 
      "+ *\tThis throws out all state, and lets you begin again.", 
      "+ */", 
      "+static loff_t unionfs_dir_llseek(struct file *file, loff_t offset, int origin)", 
      "+{", 
      "+\tstruct unionfs_dir_state *rdstate;", 
      "+\tstruct dentry *dentry = file->f_path.dentry;", 
      "+\tstruct dentry *parent;", 
      "+\tloff_t err;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\terr = unionfs_file_revalidate(file, parent, false);", 
      "+\tif (unlikely(err))", 
      "+\t\tgoto out;", 
      "+", 
      "+\trdstate = UNIONFS_F(file)->rdstate;", 
      "+", 
      "+\t/*", 
      "+\t * we let users seek to their current position, but not anywhere", 
      "+\t * else.", 
      "+\t */", 
      "+\tif (!offset) {", 
      "+\t\tswitch (origin) {", 
      "+\t\tcase SEEK_SET:", 
      "+\t\t\tif (rdstate) {", 
      "+\t\t\t\tfree_rdstate(rdstate);", 
      "+\t\t\t\tUNIONFS_F(file)->rdstate = NULL;", 
      "+\t\t\t}", 
      "+\t\t\tinit_rdstate(file);", 
      "+\t\t\terr = 0;", 
      "+\t\t\tbreak;", 
      "+\t\tcase SEEK_CUR:", 
      "+\t\t\terr = file->f_pos;", 
      "+\t\t\tbreak;", 
      "+\t\tcase SEEK_END:", 
      "+\t\t\t/* Unsupported, because we would break everything.  */", 
      "+\t\t\terr = -EINVAL;", 
      "+\t\t\tbreak;", 
      "+\t\t}", 
      "+\t} else {", 
      "+\t\tswitch (origin) {", 
      "+\t\tcase SEEK_SET:", 
      "+\t\t\tif (rdstate) {", 
      "+\t\t\t\tif (offset == rdstate2offset(rdstate))", 
      "+\t\t\t\t\terr = offset;", 
      "+\t\t\t\telse if (file->f_pos == DIREOF)", 
      "+\t\t\t\t\terr = DIREOF;", 
      "+\t\t\t\telse", 
      "+\t\t\t\t\terr = -EINVAL;", 
      "+\t\t\t} else {", 
      "+\t\t\t\tstruct inode *inode;", 
      "+\t\t\t\tinode = dentry->d_inode;", 
      "+\t\t\t\trdstate = find_rdstate(inode, offset);", 
      "+\t\t\t\tif (rdstate) {", 
      "+\t\t\t\t\tUNIONFS_F(file)->rdstate = rdstate;", 
      "+\t\t\t\t\terr = rdstate->offset;", 
      "+\t\t\t\t} else {", 
      "+\t\t\t\t\terr = -EINVAL;", 
      "+\t\t\t\t}", 
      "+\t\t\t}", 
      "+\t\t\tbreak;", 
      "+\t\tcase SEEK_CUR:", 
      "+\t\tcase SEEK_END:", 
      "+\t\t\t/* Unsupported, because we would break everything.  */", 
      "+\t\t\terr = -EINVAL;", 
      "+\t\t\tbreak;", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\tif (!err)", 
      "+\t\tunionfs_check_file(file);", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Trimmed directory options, we shouldn't pass everything down since", 
      "+ * we don't want to operate on partial directories.", 
      "+ */", 
      "+struct file_operations unionfs_dir_fops = {", 
      "+\t.llseek\t\t= unionfs_dir_llseek,", 
      "+\t.read\t\t= generic_read_dir,", 
      "+\t.readdir\t= unionfs_readdir,", 
      "+\t.unlocked_ioctl\t= unionfs_ioctl,", 
      "+\t.open\t\t= unionfs_open,", 
      "+\t.release\t= unionfs_file_release,", 
      "+\t.flush\t\t= unionfs_flush,", 
      "+\t.fsync\t\t= unionfs_fsync,", 
      "+\t.fasync\t\t= unionfs_fasync,", 
      "+};"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/dirhelper.c", 
    [
      "diff --git a/fs/unionfs/dirhelper.c b/fs/unionfs/dirhelper.c", 
      "new file mode 100644", 
      "index 0000000..b2c645e", 
      "--- dev/null", 
      "+++ b/fs/unionfs/dirhelper.c", 
      "@@ -0,0 +1,158 @@", 
      "+/*", 
      "+ * Copyright (c) 2003-2009 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-2009 Stony Brook University", 
      "+ * Copyright (c) 2003-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#include \"union.h\"", 
      "+", 
      "+#define RD_NONE 0", 
      "+#define RD_CHECK_EMPTY 1", 
      "+/* The callback structure for check_empty. */", 
      "+struct unionfs_rdutil_callback {", 
      "+\tint err;", 
      "+\tint filldir_called;", 
      "+\tstruct unionfs_dir_state *rdstate;", 
      "+\tint mode;", 
      "+};", 
      "+", 
      "+/* This filldir function makes sure only whiteouts exist within a directory. */", 
      "+static int readdir_util_callback(void *dirent, const char *oname, int namelen,", 
      "+\t\t\t\t loff_t offset, u64 ino, unsigned int d_type)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct unionfs_rdutil_callback *buf = dirent;", 
      "+\tint is_whiteout;", 
      "+\tstruct filldir_node *found;", 
      "+\tchar *name = (char *) oname;", 
      "+", 
      "+\tbuf->filldir_called = 1;", 
      "+", 
      "+\tif (name[0] == '.' && (namelen == 1 ||", 
      "+\t\t\t       (name[1] == '.' && namelen == 2)))", 
      "+\t\tgoto out;", 
      "+", 
      "+\tis_whiteout = is_whiteout_name(&name, &namelen);", 
      "+", 
      "+\tfound = find_filldir_node(buf->rdstate, name, namelen, is_whiteout);", 
      "+\t/* If it was found in the table there was a previous whiteout. */", 
      "+\tif (found)", 
      "+\t\tgoto out;", 
      "+", 
      "+\t/*", 
      "+\t * if it wasn't found and isn't a whiteout, the directory isn't", 
      "+\t * empty.", 
      "+\t */", 
      "+\terr = -ENOTEMPTY;", 
      "+\tif ((buf->mode == RD_CHECK_EMPTY) && !is_whiteout)", 
      "+\t\tgoto out;", 
      "+", 
      "+\terr = add_filldir_node(buf->rdstate, name, namelen,", 
      "+\t\t\t       buf->rdstate->bindex, is_whiteout);", 
      "+", 
      "+out:", 
      "+\tbuf->err = err;", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/* Is a directory logically empty? */", 
      "+int check_empty(struct dentry *dentry, struct dentry *parent,", 
      "+\t\tstruct unionfs_dir_state **namelist)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct dentry *lower_dentry = NULL;", 
      "+\tstruct vfsmount *mnt;", 
      "+\tstruct super_block *sb;", 
      "+\tstruct file *lower_file;", 
      "+\tstruct unionfs_rdutil_callback *buf = NULL;", 
      "+\tint bindex, bstart, bend, bopaque;", 
      "+", 
      "+\tsb = dentry->d_sb;", 
      "+", 
      "+", 
      "+\tBUG_ON(!S_ISDIR(dentry->d_inode->i_mode));", 
      "+", 
      "+\terr = unionfs_partial_lookup(dentry, parent);", 
      "+\tif (err)", 
      "+\t\tgoto out;", 
      "+", 
      "+\tbstart = dbstart(dentry);", 
      "+\tbend = dbend(dentry);", 
      "+\tbopaque = dbopaque(dentry);", 
      "+\tif (0 <= bopaque && bopaque < bend)", 
      "+\t\tbend = bopaque;", 
      "+", 
      "+\tbuf = kmalloc(sizeof(struct unionfs_rdutil_callback), GFP_KERNEL);", 
      "+\tif (unlikely(!buf)) {", 
      "+\t\terr = -ENOMEM;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tbuf->err = 0;", 
      "+\tbuf->mode = RD_CHECK_EMPTY;", 
      "+\tbuf->rdstate = alloc_rdstate(dentry->d_inode, bstart);", 
      "+\tif (unlikely(!buf->rdstate)) {", 
      "+\t\terr = -ENOMEM;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/* Process the lower directories with rdutil_callback as a filldir. */", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tlower_dentry = unionfs_lower_dentry_idx(dentry, bindex);", 
      "+\t\tif (!lower_dentry)", 
      "+\t\t\tcontinue;", 
      "+\t\tif (!lower_dentry->d_inode)", 
      "+\t\t\tcontinue;", 
      "+\t\tif (!S_ISDIR(lower_dentry->d_inode->i_mode))", 
      "+\t\t\tcontinue;", 
      "+", 
      "+\t\tdget(lower_dentry);", 
      "+\t\tmnt = unionfs_mntget(dentry, bindex);", 
      "+\t\tbranchget(sb, bindex);", 
      "+\t\tlower_file = dentry_open(lower_dentry, mnt, O_RDONLY);", 
      "+\t\tif (IS_ERR(lower_file)) {", 
      "+\t\t\terr = PTR_ERR(lower_file);", 
      "+\t\t\tbranchput(sb, bindex);", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+", 
      "+\t\tdo {", 
      "+\t\t\tbuf->filldir_called = 0;", 
      "+\t\t\tbuf->rdstate->bindex = bindex;", 
      "+\t\t\terr = vfs_readdir(lower_file,", 
      "+\t\t\t\t\t  readdir_util_callback, buf);", 
      "+\t\t\tif (buf->err)", 
      "+\t\t\t\terr = buf->err;", 
      "+\t\t} while ((err >= 0) && buf->filldir_called);", 
      "+", 
      "+\t\t/* fput calls dput for lower_dentry */", 
      "+\t\tfput(lower_file);", 
      "+\t\tbranchput(sb, bindex);", 
      "+", 
      "+\t\tif (err < 0)", 
      "+\t\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\tif (buf) {", 
      "+\t\tif (namelist && !err)", 
      "+\t\t\t*namelist = buf->rdstate;", 
      "+\t\telse if (buf->rdstate)", 
      "+\t\t\tfree_rdstate(buf->rdstate);", 
      "+\t\tkfree(buf);", 
      "+\t}", 
      "+", 
      "+", 
      "+\treturn err;", 
      "+}"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/fanout.h", 
    [
      "diff --git a/fs/unionfs/fanout.h b/fs/unionfs/fanout.h", 
      "new file mode 100644", 
      "index 0000000..04ffa85", 
      "--- dev/null", 
      "+++ b/fs/unionfs/fanout.h", 
      "@@ -0,0 +1,407 @@", 
      "+/*", 
      "+ * Copyright (c) 2003-2009 Erez Zadok", 
      "+ * Copyright (c) 2003-2006 Charles P. Wright", 
      "+ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek", 
      "+ * 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-2009 Stony Brook University", 
      "+ * Copyright (c) 2003-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#ifndef _FANOUT_H_", 
      "+#define _FANOUT_H_", 
      "+", 
      "+/*", 
      "+ * Inode to private data", 
      "+ *", 
      "+ * Since we use containers and the struct inode is _inside_ the", 
      "+ * unionfs_inode_info structure, UNIONFS_I will always (given a non-NULL", 
      "+ * inode pointer), return a valid non-NULL pointer.", 
      "+ */", 
      "+static inline struct unionfs_inode_info *UNIONFS_I(const struct inode *inode)", 
      "+{", 
      "+\treturn container_of(inode, struct unionfs_inode_info, vfs_inode);", 
      "+}", 
      "+", 
      "+#define ibstart(ino) (UNIONFS_I(ino)->bstart)", 
      "+#define ibend(ino) (UNIONFS_I(ino)->bend)", 
      "+", 
      "+/* Dentry to private data */", 
      "+#define UNIONFS_D(dent) ((struct unionfs_dentry_info *)(dent)->d_fsdata)", 
      "+#define dbstart(dent) (UNIONFS_D(dent)->bstart)", 
      "+#define dbend(dent) (UNIONFS_D(dent)->bend)", 
      "+#define dbopaque(dent) (UNIONFS_D(dent)->bopaque)", 
      "+", 
      "+/* Superblock to private data */", 
      "+#define UNIONFS_SB(super) ((struct unionfs_sb_info *)(super)->s_fs_info)", 
      "+#define sbstart(sb) 0", 
      "+#define sbend(sb) (UNIONFS_SB(sb)->bend)", 
      "+#define sbmax(sb) (UNIONFS_SB(sb)->bend + 1)", 
      "+#define sbhbid(sb) (UNIONFS_SB(sb)->high_branch_id)", 
      "+", 
      "+/* File to private Data */", 
      "+#define UNIONFS_F(file) ((struct unionfs_file_info *)((file)->private_data))", 
      "+#define fbstart(file) (UNIONFS_F(file)->bstart)", 
      "+#define fbend(file) (UNIONFS_F(file)->bend)", 
      "+", 
      "+/* macros to manipulate branch IDs in stored in our superblock */", 
      "+static inline int branch_id(struct super_block *sb, int index)", 
      "+{", 
      "+\tBUG_ON(!sb || index < 0);", 
      "+\treturn UNIONFS_SB(sb)->data[index].branch_id;", 
      "+}", 
      "+", 
      "+static inline void set_branch_id(struct super_block *sb, int index, int val)", 
      "+{", 
      "+\tBUG_ON(!sb || index < 0);", 
      "+\tUNIONFS_SB(sb)->data[index].branch_id = val;", 
      "+}", 
      "+", 
      "+static inline void new_branch_id(struct super_block *sb, int index)", 
      "+{", 
      "+\tBUG_ON(!sb || index < 0);", 
      "+\tset_branch_id(sb, index, ++UNIONFS_SB(sb)->high_branch_id);", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Find new index of matching branch with an existing superblock of a known", 
      "+ * (possibly old) id.  This is needed because branches could have been", 
      "+ * added/deleted causing the branches of any open files to shift.", 
      "+ *", 
      "+ * @sb: the new superblock which may have new/different branch IDs", 
      "+ * @id: the old/existing id we're looking for", 
      "+ * Returns index of newly found branch (0 or greater), -1 otherwise.", 
      "+ */", 
      "+static inline int branch_id_to_idx(struct super_block *sb, int id)", 
      "+{", 
      "+\tint i;", 
      "+\tfor (i = 0; i < sbmax(sb); i++) {", 
      "+\t\tif (branch_id(sb, i) == id)", 
      "+\t\t\treturn i;", 
      "+\t}", 
      "+\t/* in the non-ODF code, this should really never happen */", 
      "+\tprintk(KERN_WARNING \"unionfs: cannot find branch with id %d\\n\", id);", 
      "+\treturn -1;", 
      "+}", 
      "+", 
      "+/* File to lower file. */", 
      "+static inline struct file *unionfs_lower_file(const struct file *f)", 
      "+{", 
      "+\tBUG_ON(!f);", 
      "+\treturn UNIONFS_F(f)->lower_files[fbstart(f)];", 
      "+}", 
      "+", 
      "+static inline struct file *unionfs_lower_file_idx(const struct file *f,", 
      "+\t\t\t\t\t\t  int index)", 
      "+{", 
      "+\tBUG_ON(!f || index < 0);", 
      "+\treturn UNIONFS_F(f)->lower_files[index];", 
      "+}", 
      "+", 
      "+static inline void unionfs_set_lower_file_idx(struct file *f, int index,", 
      "+\t\t\t\t\t      struct file *val)", 
      "+{", 
      "+\tBUG_ON(!f || index < 0);", 
      "+\tUNIONFS_F(f)->lower_files[index] = val;", 
      "+\t/* save branch ID (may be redundant?) */", 
      "+\tUNIONFS_F(f)->saved_branch_ids[index] =", 
      "+\t\tbranch_id((f)->f_path.dentry->d_sb, index);", 
      "+}", 
      "+", 
      "+static inline void unionfs_set_lower_file(struct file *f, struct file *val)", 
      "+{", 
      "+\tBUG_ON(!f);", 
      "+\tunionfs_set_lower_file_idx((f), fbstart(f), (val));", 
      "+}", 
      "+", 
      "+/* Inode to lower inode. */", 
      "+static inline struct inode *unionfs_lower_inode(const struct inode *i)", 
      "+{", 
      "+\tBUG_ON(!i);", 
      "+\treturn UNIONFS_I(i)->lower_inodes[ibstart(i)];", 
      "+}", 
      "+", 
      "+static inline struct inode *unionfs_lower_inode_idx(const struct inode *i,", 
      "+\t\t\t\t\t\t    int index)", 
      "+{", 
      "+\tBUG_ON(!i || index < 0);", 
      "+\treturn UNIONFS_I(i)->lower_inodes[index];", 
      "+}", 
      "+", 
      "+static inline void unionfs_set_lower_inode_idx(struct inode *i, int index,", 
      "+\t\t\t\t\t       struct inode *val)", 
      "+{", 
      "+\tBUG_ON(!i || index < 0);", 
      "+\tUNIONFS_I(i)->lower_inodes[index] = val;", 
      "+}", 
      "+", 
      "+static inline void unionfs_set_lower_inode(struct inode *i, struct inode *val)", 
      "+{", 
      "+\tBUG_ON(!i);", 
      "+\tUNIONFS_I(i)->lower_inodes[ibstart(i)] = val;", 
      "+}", 
      "+", 
      "+/* Superblock to lower superblock. */", 
      "+static inline struct super_block *unionfs_lower_super(", 
      "+\t\t\t\t\tconst struct super_block *sb)", 
      "+{", 
      "+\tBUG_ON(!sb);", 
      "+\treturn UNIONFS_SB(sb)->data[sbstart(sb)].sb;", 
      "+}", 
      "+", 
      "+static inline struct super_block *unionfs_lower_super_idx(", 
      "+\t\t\t\t\tconst struct super_block *sb,", 
      "+\t\t\t\t\tint index)", 
      "+{", 
      "+\tBUG_ON(!sb || index < 0);", 
      "+\treturn UNIONFS_SB(sb)->data[index].sb;", 
      "+}", 
      "+", 
      "+static inline void unionfs_set_lower_super_idx(struct super_block *sb,", 
      "+\t\t\t\t\t       int index,", 
      "+\t\t\t\t\t       struct super_block *val)", 
      "+{", 
      "+\tBUG_ON(!sb || index < 0);", 
      "+\tUNIONFS_SB(sb)->data[index].sb = val;", 
      "+}", 
      "+", 
      "+static inline void unionfs_set_lower_super(struct super_block *sb,", 
      "+\t\t\t\t\t   struct super_block *val)", 
      "+{", 
      "+\tBUG_ON(!sb);", 
      "+\tUNIONFS_SB(sb)->data[sbstart(sb)].sb = val;", 
      "+}", 
      "+", 
      "+/* Branch count macros. */", 
      "+static inline int branch_count(const struct super_block *sb, int index)", 
      "+{", 
      "+\tBUG_ON(!sb || index < 0);", 
      "+\treturn atomic_read(&UNIONFS_SB(sb)->data[index].open_files);", 
      "+}", 
      "+", 
      "+static inline void set_branch_count(struct super_block *sb, int index, int val)", 
      "+{", 
      "+\tBUG_ON(!sb || index < 0);", 
      "+\tatomic_set(&UNIONFS_SB(sb)->data[index].open_files, val);", 
      "+}", 
      "+", 
      "+static inline void branchget(struct super_block *sb, int index)", 
      "+{", 
      "+\tBUG_ON(!sb || index < 0);", 
      "+\tatomic_inc(&UNIONFS_SB(sb)->data[index].open_files);", 
      "+}", 
      "+", 
      "+static inline void branchput(struct super_block *sb, int index)", 
      "+{", 
      "+\tBUG_ON(!sb || index < 0);", 
      "+\tatomic_dec(&UNIONFS_SB(sb)->data[index].open_files);", 
      "+}", 
      "+", 
      "+/* Dentry macros */", 
      "+static inline void unionfs_set_lower_dentry_idx(struct dentry *dent, int index,", 
      "+\t\t\t\t\t\tstruct dentry *val)", 
      "+{", 
      "+\tBUG_ON(!dent || index < 0);", 
      "+\tUNIONFS_D(dent)->lower_paths[index].dentry = val;", 
      "+}", 
      "+", 
      "+static inline struct dentry *unionfs_lower_dentry_idx(", 
      "+\t\t\t\tconst struct dentry *dent,", 
      "+\t\t\t\tint index)", 
      "+{", 
      "+\tBUG_ON(!dent || index < 0);", 
      "+\treturn UNIONFS_D(dent)->lower_paths[index].dentry;", 
      "+}", 
      "+", 
      "+static inline struct dentry *unionfs_lower_dentry(const struct dentry *dent)", 
      "+{", 
      "+\tBUG_ON(!dent);", 
      "+\treturn unionfs_lower_dentry_idx(dent, dbstart(dent));", 
      "+}", 
      "+", 
      "+static inline void unionfs_set_lower_mnt_idx(struct dentry *dent, int index,", 
      "+\t\t\t\t\t     struct vfsmount *mnt)", 
      "+{", 
      "+\tBUG_ON(!dent || index < 0);", 
      "+\tUNIONFS_D(dent)->lower_paths[index].mnt = mnt;", 
      "+}", 
      "+", 
      "+static inline struct vfsmount *unionfs_lower_mnt_idx(", 
      "+\t\t\t\t\tconst struct dentry *dent,", 
      "+\t\t\t\t\tint index)", 
      "+{", 
      "+\tBUG_ON(!dent || index < 0);", 
      "+\treturn UNIONFS_D(dent)->lower_paths[index].mnt;", 
      "+}", 
      "+", 
      "+static inline struct vfsmount *unionfs_lower_mnt(const struct dentry *dent)", 
      "+{", 
      "+\tBUG_ON(!dent);", 
      "+\treturn unionfs_lower_mnt_idx(dent, dbstart(dent));", 
      "+}", 
      "+", 
      "+/* Macros for locking a dentry. */", 
      "+enum unionfs_dentry_lock_class {", 
      "+\tUNIONFS_DMUTEX_NORMAL,", 
      "+\tUNIONFS_DMUTEX_ROOT,", 
      "+\tUNIONFS_DMUTEX_PARENT,", 
      "+\tUNIONFS_DMUTEX_CHILD,", 
      "+\tUNIONFS_DMUTEX_WHITEOUT,", 
      "+\tUNIONFS_DMUTEX_REVAL_PARENT, /* for file/dentry revalidate */", 
      "+\tUNIONFS_DMUTEX_REVAL_CHILD,   /* for file/dentry revalidate */", 
      "+};", 
      "+", 
      "+static inline void unionfs_lock_dentry(struct dentry *d,", 
      "+\t\t\t\t       unsigned int subclass)", 
      "+{", 
      "+\tBUG_ON(!d);", 
      "+\tmutex_lock_nested(&UNIONFS_D(d)->lock, subclass);", 
      "+}", 
      "+", 
      "+static inline void unionfs_unlock_dentry(struct dentry *d)", 
      "+{", 
      "+\tBUG_ON(!d);", 
      "+\tmutex_unlock(&UNIONFS_D(d)->lock);", 
      "+}", 
      "+", 
      "+static inline struct dentry *unionfs_lock_parent(struct dentry *d,", 
      "+\t\t\t\t\t\t unsigned int subclass)", 
      "+{", 
      "+\tstruct dentry *p;", 
      "+", 
      "+\tBUG_ON(!d);", 
      "+\tp = dget_parent(d);", 
      "+\tif (p != d)", 
      "+\t\tmutex_lock_nested(&UNIONFS_D(p)->lock, subclass);", 
      "+\treturn p;", 
      "+}", 
      "+", 
      "+static inline void unionfs_unlock_parent(struct dentry *d, struct dentry *p)", 
      "+{", 
      "+\tBUG_ON(!d);", 
      "+\tBUG_ON(!p);", 
      "+\tif (p != d) {", 
      "+\t\tBUG_ON(!mutex_is_locked(&UNIONFS_D(p)->lock));", 
      "+\t\tmutex_unlock(&UNIONFS_D(p)->lock);", 
      "+\t}", 
      "+\tdput(p);", 
      "+}", 
      "+", 
      "+static inline void verify_locked(struct dentry *d)", 
      "+{", 
      "+\tBUG_ON(!d);", 
      "+\tBUG_ON(!mutex_is_locked(&UNIONFS_D(d)->lock));", 
      "+}", 
      "+", 
      "+/* macros to put lower objects */", 
      "+", 
      "+/*", 
      "+ * iput lower inodes of an unionfs dentry, from bstart to bend.  If", 
      "+ * @free_lower is true, then also kfree the memory used to hold the lower", 
      "+ * object pointers.", 
      "+ */", 
      "+static inline void iput_lowers(struct inode *inode,", 
      "+\t\t\t       int bstart, int bend, bool free_lower)", 
      "+{", 
      "+\tstruct inode *lower_inode;", 
      "+\tint bindex;", 
      "+", 
      "+\tBUG_ON(!inode);", 
      "+\tBUG_ON(!UNIONFS_I(inode));", 
      "+\tBUG_ON(bstart < 0);", 
      "+", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tlower_inode = unionfs_lower_inode_idx(inode, bindex);", 
      "+\t\tif (lower_inode) {", 
      "+\t\t\tunionfs_set_lower_inode_idx(inode, bindex, NULL);", 
      "+\t\t\t/* see Documentation/filesystems/unionfs/issues.txt */", 
      "+\t\t\tlockdep_off();", 
      "+\t\t\tiput(lower_inode);", 
      "+\t\t\tlockdep_on();", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\tif (free_lower) {", 
      "+\t\tkfree(UNIONFS_I(inode)->lower_inodes);", 
      "+\t\tUNIONFS_I(inode)->lower_inodes = NULL;", 
      "+\t}", 
      "+}", 
      "+", 
      "+/* iput all lower inodes, and reset start/end branch indices to -1 */", 
      "+static inline void iput_lowers_all(struct inode *inode, bool free_lower)", 
      "+{", 
      "+\tint bstart, bend;", 
      "+", 
      "+\tBUG_ON(!inode);", 
      "+\tBUG_ON(!UNIONFS_I(inode));", 
      "+\tbstart = ibstart(inode);", 
      "+\tbend = ibend(inode);", 
      "+\tBUG_ON(bstart < 0);", 
      "+", 
      "+\tiput_lowers(inode, bstart, bend, free_lower);", 
      "+\tibstart(inode) = ibend(inode) = -1;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * dput/mntput all lower dentries and vfsmounts of an unionfs dentry, from", 
      "+ * bstart to bend.  If @free_lower is true, then also kfree the memory used", 
      "+ * to hold the lower object pointers.", 
      "+ *", 
      "+ * XXX: implement using path_put VFS macros", 
      "+ */", 
      "+static inline void path_put_lowers(struct dentry *dentry,", 
      "+\t\t\t\t   int bstart, int bend, bool free_lower)", 
      "+{", 
      "+\tstruct dentry *lower_dentry;", 
      "+\tstruct vfsmount *lower_mnt;", 
      "+\tint bindex;", 
      "+", 
      "+\tBUG_ON(!dentry);", 
      "+\tBUG_ON(!UNIONFS_D(dentry));", 
      "+\tBUG_ON(bstart < 0);", 
      "+", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tlower_dentry = unionfs_lower_dentry_idx(dentry, bindex);", 
      "+\t\tif (lower_dentry) {", 
      "+\t\t\tunionfs_set_lower_dentry_idx(dentry, bindex, NULL);", 
      "+\t\t\tdput(lower_dentry);", 
      "+\t\t}", 
      "+\t\tlower_mnt = unionfs_lower_mnt_idx(dentry, bindex);", 
      "+\t\tif (lower_mnt) {", 
      "+\t\t\tunionfs_set_lower_mnt_idx(dentry, bindex, NULL);", 
      "+\t\t\tmntput(lower_mnt);", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\tif (free_lower) {", 
      "+\t\tkfree(UNIONFS_D(dentry)->lower_paths);", 
      "+\t\tUNIONFS_D(dentry)->lower_paths = NULL;", 
      "+\t}", 
      "+}", 
      "+", 
      "+/*", 
      "+ * dput/mntput all lower dentries and vfsmounts, and reset start/end branch", 
      "+ * indices to -1.", 
      "+ */", 
      "+static inline void path_put_lowers_all(struct dentry *dentry, bool free_lower)", 
      "+{", 
      "+\tint bstart, bend;", 
      "+", 
      "+\tBUG_ON(!dentry);", 
      "+\tBUG_ON(!UNIONFS_D(dentry));", 
      "+\tbstart = dbstart(dentry);", 
      "+\tbend = dbend(dentry);", 
      "+\tBUG_ON(bstart < 0);", 
      "+", 
      "+\tpath_put_lowers(dentry, bstart, bend, free_lower);", 
      "+\tdbstart(dentry) = dbend(dentry) = -1;", 
      "+}", 
      "+", 
      "+#endif\t/* not _FANOUT_H */"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/file.c", 
    [
      "diff --git a/fs/unionfs/file.c b/fs/unionfs/file.c", 
      "new file mode 100644", 
      "index 0000000..af6f8a7", 
      "--- dev/null", 
      "+++ b/fs/unionfs/file.c", 
      "@@ -0,0 +1,364 @@", 
      "+/*", 
      "+ * Copyright (c) 2003-2009 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-2009 Stony Brook University", 
      "+ * Copyright (c) 2003-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#include \"union.h\"", 
      "+", 
      "+static ssize_t unionfs_read(struct file *file, char __user *buf,", 
      "+\t\t\t    size_t count, loff_t *ppos)", 
      "+{", 
      "+\tint err;", 
      "+\tstruct file *lower_file;", 
      "+\tstruct dentry *dentry = file->f_path.dentry;", 
      "+\tstruct dentry *parent;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\terr = unionfs_file_revalidate(file, parent, false);", 
      "+\tif (unlikely(err))", 
      "+\t\tgoto out;", 
      "+", 
      "+\tlower_file = unionfs_lower_file(file);", 
      "+\terr = vfs_read(lower_file, buf, count, ppos);", 
      "+\t/* update our inode atime upon a successful lower read */", 
      "+\tif (err >= 0) {", 
      "+\t\tfsstack_copy_attr_atime(dentry->d_inode,", 
      "+\t\t\t\t\tlower_file->f_path.dentry->d_inode);", 
      "+\t\tunionfs_check_file(file);", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+static ssize_t unionfs_write(struct file *file, const char __user *buf,", 
      "+\t\t\t     size_t count, loff_t *ppos)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct file *lower_file;", 
      "+\tstruct dentry *dentry = file->f_path.dentry;", 
      "+\tstruct dentry *parent;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\terr = unionfs_file_revalidate(file, parent, true);", 
      "+\tif (unlikely(err))", 
      "+\t\tgoto out;", 
      "+", 
      "+\tlower_file = unionfs_lower_file(file);", 
      "+\terr = vfs_write(lower_file, buf, count, ppos);", 
      "+\t/* update our inode times+sizes upon a successful lower write */", 
      "+\tif (err >= 0) {", 
      "+\t\tfsstack_copy_inode_size(dentry->d_inode,", 
      "+\t\t\t\t\tlower_file->f_path.dentry->d_inode);", 
      "+\t\tfsstack_copy_attr_times(dentry->d_inode,", 
      "+\t\t\t\t\tlower_file->f_path.dentry->d_inode);", 
      "+\t\tUNIONFS_F(file)->wrote_to_file = true; /* for delayed copyup */", 
      "+\t\tunionfs_check_file(file);", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+static int unionfs_file_readdir(struct file *file, void *dirent,", 
      "+\t\t\t\tfilldir_t filldir)", 
      "+{", 
      "+\treturn -ENOTDIR;", 
      "+}", 
      "+", 
      "+static int unionfs_mmap(struct file *file, struct vm_area_struct *vma)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tbool willwrite;", 
      "+\tstruct file *lower_file;", 
      "+\tstruct dentry *dentry = file->f_path.dentry;", 
      "+\tstruct dentry *parent;", 
      "+\tstruct vm_operations_struct *saved_vm_ops = NULL;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\t/* This might be deferred to mmap's writepage */", 
      "+\twillwrite = ((vma->vm_flags | VM_SHARED | VM_WRITE) == vma->vm_flags);", 
      "+\terr = unionfs_file_revalidate(file, parent, willwrite);", 
      "+\tif (unlikely(err))", 
      "+\t\tgoto out;", 
      "+\tunionfs_check_file(file);", 
      "+", 
      "+\t/*", 
      "+\t * File systems which do not implement ->writepage may use", 
      "+\t * generic_file_readonly_mmap as their ->mmap op.  If you call", 
      "+\t * generic_file_readonly_mmap with VM_WRITE, you'd get an -EINVAL.", 
      "+\t * But we cannot call the lower ->mmap op, so we can't tell that", 
      "+\t * writeable mappings won't work.  Therefore, our only choice is to", 
      "+\t * check if the lower file system supports the ->writepage, and if", 
      "+\t * not, return EINVAL (the same error that", 
      "+\t * generic_file_readonly_mmap returns in that case).", 
      "+\t */", 
      "+\tlower_file = unionfs_lower_file(file);", 
      "+\tif (willwrite && !lower_file->f_mapping->a_ops->writepage) {", 
      "+\t\terr = -EINVAL;", 
      "+\t\tprintk(KERN_ERR \"unionfs: branch %d file system does not \"", 
      "+\t\t       \"support writeable mmap\\n\", fbstart(file));", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * find and save lower vm_ops.", 
      "+\t *", 
      "+\t * XXX: the VFS should have a cleaner way of finding the lower vm_ops", 
      "+\t */", 
      "+\tif (!UNIONFS_F(file)->lower_vm_ops) {", 
      "+\t\terr = lower_file->f_op->mmap(lower_file, vma);", 
      "+\t\tif (err) {", 
      "+\t\t\tprintk(KERN_ERR \"unionfs: lower mmap failed %d\\n\", err);", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+\t\tsaved_vm_ops = vma->vm_ops;", 
      "+\t\terr = do_munmap(current->mm, vma->vm_start,", 
      "+\t\t\t\tvma->vm_end - vma->vm_start);", 
      "+\t\tif (err) {", 
      "+\t\t\tprintk(KERN_ERR \"unionfs: do_munmap failed %d\\n\", err);", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\tfile->f_mapping->a_ops = &unionfs_dummy_aops;", 
      "+\terr = generic_file_mmap(file, vma);", 
      "+\tfile->f_mapping->a_ops = &unionfs_aops;", 
      "+\tif (err) {", 
      "+\t\tprintk(KERN_ERR \"unionfs: generic_file_mmap failed %d\\n\", err);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tvma->vm_ops = &unionfs_vm_ops;", 
      "+\tif (!UNIONFS_F(file)->lower_vm_ops)", 
      "+\t\tUNIONFS_F(file)->lower_vm_ops = saved_vm_ops;", 
      "+", 
      "+out:", 
      "+\tif (!err) {", 
      "+\t\t/* copyup could cause parent dir times to change */", 
      "+\t\tunionfs_copy_attr_times(parent->d_inode);", 
      "+\t\tunionfs_check_file(file);", 
      "+\t}", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+int unionfs_fsync(struct file *file, struct dentry *dentry, int datasync)", 
      "+{", 
      "+\tint bindex, bstart, bend;", 
      "+\tstruct file *lower_file;", 
      "+\tstruct dentry *lower_dentry;", 
      "+\tstruct dentry *parent;", 
      "+\tstruct inode *lower_inode, *inode;", 
      "+\tint err = -EINVAL;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\terr = unionfs_file_revalidate(file, parent, true);", 
      "+\tif (unlikely(err))", 
      "+\t\tgoto out;", 
      "+\tunionfs_check_file(file);", 
      "+", 
      "+\tbstart = fbstart(file);", 
      "+\tbend = fbend(file);", 
      "+\tif (bstart < 0 || bend < 0)", 
      "+\t\tgoto out;", 
      "+", 
      "+\tinode = dentry->d_inode;", 
      "+\tif (unlikely(!inode)) {", 
      "+\t\tprintk(KERN_ERR", 
      "+\t\t       \"unionfs: null lower inode in unionfs_fsync\\n\");", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tlower_inode = unionfs_lower_inode_idx(inode, bindex);", 
      "+\t\tif (!lower_inode || !lower_inode->i_fop->fsync)", 
      "+\t\t\tcontinue;", 
      "+\t\tlower_file = unionfs_lower_file_idx(file, bindex);", 
      "+\t\tlower_dentry = unionfs_lower_dentry_idx(dentry, bindex);", 
      "+\t\tmutex_lock(&lower_inode->i_mutex);", 
      "+\t\terr = lower_inode->i_fop->fsync(lower_file,", 
      "+\t\t\t\t\t\tlower_dentry,", 
      "+\t\t\t\t\t\tdatasync);", 
      "+\t\tif (!err && bindex == bstart)", 
      "+\t\t\tfsstack_copy_attr_times(inode, lower_inode);", 
      "+\t\tmutex_unlock(&lower_inode->i_mutex);", 
      "+\t\tif (err)", 
      "+\t\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\tif (!err)", 
      "+\t\tunionfs_check_file(file);", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+int unionfs_fasync(int fd, struct file *file, int flag)", 
      "+{", 
      "+\tint bindex, bstart, bend;", 
      "+\tstruct file *lower_file;", 
      "+\tstruct dentry *dentry = file->f_path.dentry;", 
      "+\tstruct dentry *parent;", 
      "+\tstruct inode *lower_inode, *inode;", 
      "+\tint err = 0;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\terr = unionfs_file_revalidate(file, parent, true);", 
      "+\tif (unlikely(err))", 
      "+\t\tgoto out;", 
      "+\tunionfs_check_file(file);", 
      "+", 
      "+\tbstart = fbstart(file);", 
      "+\tbend = fbend(file);", 
      "+\tif (bstart < 0 || bend < 0)", 
      "+\t\tgoto out;", 
      "+", 
      "+\tinode = dentry->d_inode;", 
      "+\tif (unlikely(!inode)) {", 
      "+\t\tprintk(KERN_ERR", 
      "+\t\t       \"unionfs: null lower inode in unionfs_fasync\\n\");", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tlower_inode = unionfs_lower_inode_idx(inode, bindex);", 
      "+\t\tif (!lower_inode || !lower_inode->i_fop->fasync)", 
      "+\t\t\tcontinue;", 
      "+\t\tlower_file = unionfs_lower_file_idx(file, bindex);", 
      "+\t\tmutex_lock(&lower_inode->i_mutex);", 
      "+\t\terr = lower_inode->i_fop->fasync(fd, lower_file, flag);", 
      "+\t\tif (!err && bindex == bstart)", 
      "+\t\t\tfsstack_copy_attr_times(inode, lower_inode);", 
      "+\t\tmutex_unlock(&lower_inode->i_mutex);", 
      "+\t\tif (err)", 
      "+\t\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\tif (!err)", 
      "+\t\tunionfs_check_file(file);", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+static ssize_t unionfs_splice_read(struct file *file, loff_t *ppos,", 
      "+\t\t\t\t   struct pipe_inode_info *pipe, size_t len,", 
      "+\t\t\t\t   unsigned int flags)", 
      "+{", 
      "+\tssize_t err;", 
      "+\tstruct file *lower_file;", 
      "+\tstruct dentry *dentry = file->f_path.dentry;", 
      "+\tstruct dentry *parent;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\terr = unionfs_file_revalidate(file, parent, false);", 
      "+\tif (unlikely(err))", 
      "+\t\tgoto out;", 
      "+", 
      "+\tlower_file = unionfs_lower_file(file);", 
      "+\terr = vfs_splice_to(lower_file, ppos, pipe, len, flags);", 
      "+\t/* update our inode atime upon a successful lower splice-read */", 
      "+\tif (err >= 0) {", 
      "+\t\tfsstack_copy_attr_atime(dentry->d_inode,", 
      "+\t\t\t\t\tlower_file->f_path.dentry->d_inode);", 
      "+\t\tunionfs_check_file(file);", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+static ssize_t unionfs_splice_write(struct pipe_inode_info *pipe,", 
      "+\t\t\t\t    struct file *file, loff_t *ppos,", 
      "+\t\t\t\t    size_t len, unsigned int flags)", 
      "+{", 
      "+\tssize_t err = 0;", 
      "+\tstruct file *lower_file;", 
      "+\tstruct dentry *dentry = file->f_path.dentry;", 
      "+\tstruct dentry *parent;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\terr = unionfs_file_revalidate(file, parent, true);", 
      "+\tif (unlikely(err))", 
      "+\t\tgoto out;", 
      "+", 
      "+\tlower_file = unionfs_lower_file(file);", 
      "+\terr = vfs_splice_from(pipe, lower_file, ppos, len, flags);", 
      "+\t/* update our inode times+sizes upon a successful lower write */", 
      "+\tif (err >= 0) {", 
      "+\t\tfsstack_copy_inode_size(dentry->d_inode,", 
      "+\t\t\t\t\tlower_file->f_path.dentry->d_inode);", 
      "+\t\tfsstack_copy_attr_times(dentry->d_inode,", 
      "+\t\t\t\t\tlower_file->f_path.dentry->d_inode);", 
      "+\t\tunionfs_check_file(file);", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+struct file_operations unionfs_main_fops = {", 
      "+\t.llseek\t\t= generic_file_llseek,", 
      "+\t.read\t\t= unionfs_read,", 
      "+\t.write\t\t= unionfs_write,", 
      "+\t.readdir\t= unionfs_file_readdir,", 
      "+\t.unlocked_ioctl\t= unionfs_ioctl,", 
      "+\t.mmap\t\t= unionfs_mmap,", 
      "+\t.open\t\t= unionfs_open,", 
      "+\t.flush\t\t= unionfs_flush,", 
      "+\t.release\t= unionfs_file_release,", 
      "+\t.fsync\t\t= unionfs_fsync,", 
      "+\t.fasync\t\t= unionfs_fasync,", 
      "+\t.splice_read\t= unionfs_splice_read,", 
      "+\t.splice_write\t= unionfs_splice_write,", 
      "+};"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/inode.c", 
    [
      "diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c", 
      "new file mode 100644", 
      "index 0000000..68e6985", 
      "--- dev/null", 
      "+++ b/fs/unionfs/inode.c", 
      "@@ -0,0 +1,1030 @@", 
      "+/*", 
      "+ * Copyright (c) 2003-2009 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-2009 Stony Brook University", 
      "+ * Copyright (c) 2003-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#include \"union.h\"", 
      "+", 
      "+/*", 
      "+ * Find a writeable branch to create new object in.  Checks all writeble", 
      "+ * branches of the parent inode, from istart to iend order; if none are", 
      "+ * suitable, also tries branch 0 (which may require a copyup).", 
      "+ *", 
      "+ * Return a lower_dentry we can use to create object in, or ERR_PTR.", 
      "+ */", 
      "+static struct dentry *find_writeable_branch(struct inode *parent,", 
      "+\t\t\t\t\t    struct dentry *dentry)", 
      "+{", 
      "+\tint err = -EINVAL;", 
      "+\tint bindex, istart, iend;", 
      "+\tstruct dentry *lower_dentry = NULL;", 
      "+", 
      "+\tistart = ibstart(parent);", 
      "+\tiend = ibend(parent);", 
      "+\tif (istart < 0)", 
      "+\t\tgoto out;", 
      "+", 
      "+begin:", 
      "+\tfor (bindex = istart; bindex <= iend; bindex++) {", 
      "+\t\t/* skip non-writeable branches */", 
      "+\t\terr = is_robranch_super(dentry->d_sb, bindex);", 
      "+\t\tif (err) {", 
      "+\t\t\terr = -EROFS;", 
      "+\t\t\tcontinue;", 
      "+\t\t}", 
      "+\t\tlower_dentry = unionfs_lower_dentry_idx(dentry, bindex);", 
      "+\t\tif (!lower_dentry)", 
      "+\t\t\tcontinue;", 
      "+\t\t/*", 
      "+\t\t * check for whiteouts in writeable branch, and remove them", 
      "+\t\t * if necessary.", 
      "+\t\t */", 
      "+\t\terr = check_unlink_whiteout(dentry, lower_dentry, bindex);", 
      "+\t\tif (err > 0)\t/* ignore if whiteout found and removed */", 
      "+\t\t\terr = 0;", 
      "+\t\tif (err)", 
      "+\t\t\tcontinue;", 
      "+\t\t/* if get here, we can write to the branch */", 
      "+\t\tbreak;", 
      "+\t}", 
      "+\t/*", 
      "+\t * If istart wasn't already branch 0, and we got any error, then try", 
      "+\t * branch 0 (which may require copyup)", 
      "+\t */", 
      "+\tif (err && istart > 0) {", 
      "+\t\tistart = iend = 0;", 
      "+\t\tgoto begin;", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * If we tried even branch 0, and still got an error, abort.  But if", 
      "+\t * the error was an EROFS, then we should try to copyup.", 
      "+\t */", 
      "+\tif (err && err != -EROFS)", 
      "+\t\tgoto out;", 
      "+", 
      "+\t/*", 
      "+\t * If we get here, then check if copyup needed.  If lower_dentry is", 
      "+\t * NULL, create the entire dentry directory structure in branch 0.", 
      "+\t */", 
      "+\tif (!lower_dentry) {", 
      "+\t\tbindex = 0;", 
      "+\t\tlower_dentry = create_parents(parent, dentry,", 
      "+\t\t\t\t\t      dentry->d_name.name, bindex);", 
      "+\t\tif (IS_ERR(lower_dentry)) {", 
      "+\t\t\terr = PTR_ERR(lower_dentry);", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+\t}", 
      "+\terr = 0;\t\t/* all's well */", 
      "+out:", 
      "+\tif (err)", 
      "+\t\treturn ERR_PTR(err);", 
      "+\treturn lower_dentry;", 
      "+}", 
      "+", 
      "+static int unionfs_create(struct inode *dir, struct dentry *dentry,", 
      "+\t\t\t  int mode, struct nameidata *nd_unused)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct dentry *lower_dentry = NULL;", 
      "+\tstruct dentry *lower_parent_dentry = NULL;", 
      "+\tstruct dentry *parent;", 
      "+\tint valid = 0;", 
      "+\tstruct nameidata lower_nd;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\tvalid = __unionfs_d_revalidate(dentry, parent, false);", 
      "+\tif (unlikely(!valid)) {", 
      "+\t\terr = -ESTALE;\t/* same as what real_lookup does */", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tlower_dentry = find_writeable_branch(dir, dentry);", 
      "+\tif (IS_ERR(lower_dentry)) {", 
      "+\t\terr = PTR_ERR(lower_dentry);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tlower_parent_dentry = lock_parent(lower_dentry);", 
      "+\tif (IS_ERR(lower_parent_dentry)) {", 
      "+\t\terr = PTR_ERR(lower_parent_dentry);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\terr = init_lower_nd(&lower_nd, LOOKUP_CREATE);", 
      "+\tif (unlikely(err < 0))", 
      "+\t\tgoto out;", 
      "+\terr = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode,", 
      "+\t\t\t &lower_nd);", 
      "+\trelease_lower_nd(&lower_nd, err);", 
      "+", 
      "+\tif (!err) {", 
      "+\t\terr = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0));", 
      "+\t\tif (!err) {", 
      "+\t\t\tunionfs_copy_attr_times(dir);", 
      "+\t\t\tfsstack_copy_inode_size(dir,", 
      "+\t\t\t\t\t\tlower_parent_dentry->d_inode);", 
      "+\t\t\t/* update no. of links on parent directory */", 
      "+\t\t\tdir->i_nlink = unionfs_get_nlinks(dir);", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\tunlock_dir(lower_parent_dentry);", 
      "+", 
      "+out:", 
      "+\tif (!err) {", 
      "+\t\tunionfs_postcopyup_setmnt(dentry);", 
      "+\t\tunionfs_check_inode(dir);", 
      "+\t\tunionfs_check_dentry(dentry);", 
      "+\t}", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * unionfs_lookup is the only special function which takes a dentry, yet we", 
      "+ * do NOT want to call __unionfs_d_revalidate_chain because by definition,", 
      "+ * we don't have a valid dentry here yet.", 
      "+ */", 
      "+static struct dentry *unionfs_lookup(struct inode *dir,", 
      "+\t\t\t\t     struct dentry *dentry,", 
      "+\t\t\t\t     struct nameidata *nd_unused)", 
      "+{", 
      "+\tstruct dentry *ret, *parent;", 
      "+\tint err = 0;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+", 
      "+\t/*", 
      "+\t * As long as we lock/dget the parent, then can skip validating the", 
      "+\t * parent now; we may have to rebuild this dentry on the next", 
      "+\t * ->d_revalidate, however.", 
      "+\t */", 
      "+", 
      "+\t/* allocate dentry private data.  We free it in ->d_release */", 
      "+\terr = new_dentry_private_data(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+\tif (unlikely(err)) {", 
      "+\t\tret = ERR_PTR(err);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tret = unionfs_lookup_full(dentry, parent, INTERPOSE_LOOKUP);", 
      "+", 
      "+\tif (!IS_ERR(ret)) {", 
      "+\t\tif (ret)", 
      "+\t\t\tdentry = ret;", 
      "+\t\t/* lookup_full can return multiple positive dentries */", 
      "+\t\tif (dentry->d_inode && !S_ISDIR(dentry->d_inode->i_mode)) {", 
      "+\t\t\tBUG_ON(dbstart(dentry) < 0);", 
      "+\t\t\tunionfs_postcopyup_release(dentry);", 
      "+\t\t}", 
      "+\t\tunionfs_copy_attr_times(dentry->d_inode);", 
      "+\t}", 
      "+", 
      "+\tunionfs_check_inode(dir);", 
      "+\tif (!IS_ERR(ret))", 
      "+\t\tunionfs_check_dentry(dentry);", 
      "+\tunionfs_check_dentry(parent);", 
      "+\tunionfs_unlock_dentry(dentry); /* locked in new_dentry_private data */", 
      "+", 
      "+out:", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+", 
      "+\treturn ret;", 
      "+}", 
      "+", 
      "+static int unionfs_link(struct dentry *old_dentry, struct inode *dir,", 
      "+\t\t\tstruct dentry *new_dentry)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct dentry *lower_old_dentry = NULL;", 
      "+\tstruct dentry *lower_new_dentry = NULL;", 
      "+\tstruct dentry *lower_dir_dentry = NULL;", 
      "+\tstruct dentry *old_parent, *new_parent;", 
      "+\tchar *name = NULL;", 
      "+\tbool valid;", 
      "+", 
      "+\tunionfs_read_lock(old_dentry->d_sb, UNIONFS_SMUTEX_CHILD);", 
      "+\told_parent = dget_parent(old_dentry);", 
      "+\tnew_parent = dget_parent(new_dentry);", 
      "+\tunionfs_double_lock_parents(old_parent, new_parent);", 
      "+\tunionfs_double_lock_dentry(old_dentry, new_dentry);", 
      "+", 
      "+\tvalid = __unionfs_d_revalidate(old_dentry, old_parent, false);", 
      "+\tif (unlikely(!valid)) {", 
      "+\t\terr = -ESTALE;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tif (new_dentry->d_inode) {", 
      "+\t\tvalid = __unionfs_d_revalidate(new_dentry, new_parent, false);", 
      "+\t\tif (unlikely(!valid)) {", 
      "+\t\t\terr = -ESTALE;", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\tlower_new_dentry = unionfs_lower_dentry(new_dentry);", 
      "+", 
      "+\t/* check for a whiteout in new dentry branch, and delete it */", 
      "+\terr = check_unlink_whiteout(new_dentry, lower_new_dentry,", 
      "+\t\t\t\t    dbstart(new_dentry));", 
      "+\tif (err > 0) {\t       /* whiteout found and removed successfully */", 
      "+\t\tlower_dir_dentry = dget_parent(lower_new_dentry);", 
      "+\t\tfsstack_copy_attr_times(dir, lower_dir_dentry->d_inode);", 
      "+\t\tdput(lower_dir_dentry);", 
      "+\t\tdir->i_nlink = unionfs_get_nlinks(dir);", 
      "+\t\terr = 0;", 
      "+\t}", 
      "+\tif (err)", 
      "+\t\tgoto out;", 
      "+", 
      "+\t/* check if parent hierachy is needed, then link in same branch */", 
      "+\tif (dbstart(old_dentry) != dbstart(new_dentry)) {", 
      "+\t\tlower_new_dentry = create_parents(dir, new_dentry,", 
      "+\t\t\t\t\t\t  new_dentry->d_name.name,", 
      "+\t\t\t\t\t\t  dbstart(old_dentry));", 
      "+\t\terr = PTR_ERR(lower_new_dentry);", 
      "+\t\tif (IS_COPYUP_ERR(err))", 
      "+\t\t\tgoto docopyup;", 
      "+\t\tif (!lower_new_dentry || IS_ERR(lower_new_dentry))", 
      "+\t\t\tgoto out;", 
      "+\t}", 
      "+\tlower_new_dentry = unionfs_lower_dentry(new_dentry);", 
      "+\tlower_old_dentry = unionfs_lower_dentry(old_dentry);", 
      "+", 
      "+\tBUG_ON(dbstart(old_dentry) != dbstart(new_dentry));", 
      "+\tlower_dir_dentry = lock_parent(lower_new_dentry);", 
      "+\terr = is_robranch(old_dentry);", 
      "+\tif (!err) {", 
      "+\t\t/* see Documentation/filesystems/unionfs/issues.txt */", 
      "+\t\tlockdep_off();", 
      "+\t\terr = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode,", 
      "+\t\t\t       lower_new_dentry);", 
      "+\t\tlockdep_on();", 
      "+\t}", 
      "+\tunlock_dir(lower_dir_dentry);", 
      "+", 
      "+docopyup:", 
      "+\tif (IS_COPYUP_ERR(err)) {", 
      "+\t\tint old_bstart = dbstart(old_dentry);", 
      "+\t\tint bindex;", 
      "+", 
      "+\t\tfor (bindex = old_bstart - 1; bindex >= 0; bindex--) {", 
      "+\t\t\terr = copyup_dentry(old_parent->d_inode,", 
      "+\t\t\t\t\t    old_dentry, old_bstart,", 
      "+\t\t\t\t\t    bindex, old_dentry->d_name.name,", 
      "+\t\t\t\t\t    old_dentry->d_name.len, NULL,", 
      "+\t\t\t\t\t    i_size_read(old_dentry->d_inode));", 
      "+\t\t\tif (err)", 
      "+\t\t\t\tcontinue;", 
      "+\t\t\tlower_new_dentry =", 
      "+\t\t\t\tcreate_parents(dir, new_dentry,", 
      "+\t\t\t\t\t       new_dentry->d_name.name,", 
      "+\t\t\t\t\t       bindex);", 
      "+\t\t\tlower_old_dentry = unionfs_lower_dentry(old_dentry);", 
      "+\t\t\tlower_dir_dentry = lock_parent(lower_new_dentry);", 
      "+\t\t\t/* see Documentation/filesystems/unionfs/issues.txt */", 
      "+\t\t\tlockdep_off();", 
      "+\t\t\t/* do vfs_link */", 
      "+\t\t\terr = vfs_link(lower_old_dentry,", 
      "+\t\t\t\t       lower_dir_dentry->d_inode,", 
      "+\t\t\t\t       lower_new_dentry);", 
      "+\t\t\tlockdep_on();", 
      "+\t\t\tunlock_dir(lower_dir_dentry);", 
      "+\t\t\tgoto check_link;", 
      "+\t\t}", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+check_link:", 
      "+\tif (err || !lower_new_dentry->d_inode)", 
      "+\t\tgoto out;", 
      "+", 
      "+\t/* Its a hard link, so use the same inode */", 
      "+\tnew_dentry->d_inode = igrab(old_dentry->d_inode);", 
      "+\td_add(new_dentry, new_dentry->d_inode);", 
      "+\tunionfs_copy_attr_all(dir, lower_new_dentry->d_parent->d_inode);", 
      "+\tfsstack_copy_inode_size(dir, lower_new_dentry->d_parent->d_inode);", 
      "+", 
      "+\t/* propagate number of hard-links */", 
      "+\told_dentry->d_inode->i_nlink = unionfs_get_nlinks(old_dentry->d_inode);", 
      "+\t/* new dentry's ctime may have changed due to hard-link counts */", 
      "+\tunionfs_copy_attr_times(new_dentry->d_inode);", 
      "+", 
      "+out:", 
      "+\tif (!new_dentry->d_inode)", 
      "+\t\td_drop(new_dentry);", 
      "+", 
      "+\tkfree(name);", 
      "+\tif (!err)", 
      "+\t\tunionfs_postcopyup_setmnt(new_dentry);", 
      "+", 
      "+\tunionfs_check_inode(dir);", 
      "+\tunionfs_check_dentry(new_dentry);", 
      "+\tunionfs_check_dentry(old_dentry);", 
      "+", 
      "+\tunionfs_double_unlock_dentry(old_dentry, new_dentry);", 
      "+\tunionfs_double_unlock_parents(old_parent, new_parent);", 
      "+\tdput(new_parent);", 
      "+\tdput(old_parent);", 
      "+\tunionfs_read_unlock(old_dentry->d_sb);", 
      "+", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+static int unionfs_symlink(struct inode *dir, struct dentry *dentry,", 
      "+\t\t\t   const char *symname)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct dentry *lower_dentry = NULL;", 
      "+\tstruct dentry *wh_dentry = NULL;", 
      "+\tstruct dentry *lower_parent_dentry = NULL;", 
      "+\tstruct dentry *parent;", 
      "+\tchar *name = NULL;", 
      "+\tint valid = 0;", 
      "+\tumode_t mode;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\tvalid = __unionfs_d_revalidate(dentry, parent, false);", 
      "+\tif (unlikely(!valid)) {", 
      "+\t\terr = -ESTALE;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * It's only a bug if this dentry was not negative and couldn't be", 
      "+\t * revalidated (shouldn't happen).", 
      "+\t */", 
      "+\tBUG_ON(!valid && dentry->d_inode);", 
      "+", 
      "+\tlower_dentry = find_writeable_branch(dir, dentry);", 
      "+\tif (IS_ERR(lower_dentry)) {", 
      "+\t\terr = PTR_ERR(lower_dentry);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tlower_parent_dentry = lock_parent(lower_dentry);", 
      "+\tif (IS_ERR(lower_parent_dentry)) {", 
      "+\t\terr = PTR_ERR(lower_parent_dentry);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tmode = S_IALLUGO;", 
      "+\terr = vfs_symlink(lower_parent_dentry->d_inode, lower_dentry,", 
      "+\t\t\t  symname, mode);", 
      "+\tif (!err) {", 
      "+\t\terr = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0));", 
      "+\t\tif (!err) {", 
      "+\t\t\tunionfs_copy_attr_times(dir);", 
      "+\t\t\tfsstack_copy_inode_size(dir,", 
      "+\t\t\t\t\t\tlower_parent_dentry->d_inode);", 
      "+\t\t\t/* update no. of links on parent directory */", 
      "+\t\t\tdir->i_nlink = unionfs_get_nlinks(dir);", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\tunlock_dir(lower_parent_dentry);", 
      "+", 
      "+out:", 
      "+\tdput(wh_dentry);", 
      "+\tkfree(name);", 
      "+", 
      "+\tif (!err) {", 
      "+\t\tunionfs_postcopyup_setmnt(dentry);", 
      "+\t\tunionfs_check_inode(dir);", 
      "+\t\tunionfs_check_dentry(dentry);", 
      "+\t}", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+static int unionfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct dentry *lower_dentry = NULL;", 
      "+\tstruct dentry *lower_parent_dentry = NULL;", 
      "+\tstruct dentry *parent;", 
      "+\tint bindex = 0, bstart;", 
      "+\tchar *name = NULL;", 
      "+\tint valid;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\tvalid = __unionfs_d_revalidate(dentry, parent, false);", 
      "+\tif (unlikely(!valid)) {", 
      "+\t\terr = -ESTALE;\t/* same as what real_lookup does */", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tbstart = dbstart(dentry);", 
      "+", 
      "+\tlower_dentry = unionfs_lower_dentry(dentry);", 
      "+", 
      "+\t/* check for a whiteout in new dentry branch, and delete it */", 
      "+\terr = check_unlink_whiteout(dentry, lower_dentry, bstart);", 
      "+\tif (err > 0)\t       /* whiteout found and removed successfully */", 
      "+\t\terr = 0;", 
      "+\tif (err) {", 
      "+\t\t/* exit if the error returned was NOT -EROFS */", 
      "+\t\tif (!IS_COPYUP_ERR(err))", 
      "+\t\t\tgoto out;", 
      "+\t\tbstart--;", 
      "+\t}", 
      "+", 
      "+\t/* check if copyup's needed, and mkdir */", 
      "+\tfor (bindex = bstart; bindex >= 0; bindex--) {", 
      "+\t\tint i;", 
      "+\t\tint bend = dbend(dentry);", 
      "+", 
      "+\t\tif (is_robranch_super(dentry->d_sb, bindex))", 
      "+\t\t\tcontinue;", 
      "+", 
      "+\t\tlower_dentry = unionfs_lower_dentry_idx(dentry, bindex);", 
      "+\t\tif (!lower_dentry) {", 
      "+\t\t\tlower_dentry = create_parents(dir, dentry,", 
      "+\t\t\t\t\t\t      dentry->d_name.name,", 
      "+\t\t\t\t\t\t      bindex);", 
      "+\t\t\tif (!lower_dentry || IS_ERR(lower_dentry)) {", 
      "+\t\t\t\tprintk(KERN_ERR \"unionfs: lower dentry \"", 
      "+\t\t\t\t       \" NULL for bindex = %d\\n\", bindex);", 
      "+\t\t\t\tcontinue;", 
      "+\t\t\t}", 
      "+\t\t}", 
      "+", 
      "+\t\tlower_parent_dentry = lock_parent(lower_dentry);", 
      "+", 
      "+\t\tif (IS_ERR(lower_parent_dentry)) {", 
      "+\t\t\terr = PTR_ERR(lower_parent_dentry);", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+", 
      "+\t\terr = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry,", 
      "+\t\t\t\tmode);", 
      "+", 
      "+\t\tunlock_dir(lower_parent_dentry);", 
      "+", 
      "+\t\t/* did the mkdir succeed? */", 
      "+\t\tif (err)", 
      "+\t\t\tbreak;", 
      "+", 
      "+\t\tfor (i = bindex + 1; i <= bend; i++) {", 
      "+\t\t\t/* XXX: use path_put_lowers? */", 
      "+\t\t\tif (unionfs_lower_dentry_idx(dentry, i)) {", 
      "+\t\t\t\tdput(unionfs_lower_dentry_idx(dentry, i));", 
      "+\t\t\t\tunionfs_set_lower_dentry_idx(dentry, i, NULL);", 
      "+\t\t\t}", 
      "+\t\t}", 
      "+\t\tdbend(dentry) = bindex;", 
      "+", 
      "+\t\t/*", 
      "+\t\t * Only INTERPOSE_LOOKUP can return a value other than 0 on", 
      "+\t\t * err.", 
      "+\t\t */", 
      "+\t\terr = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0));", 
      "+\t\tif (!err) {", 
      "+\t\t\tunionfs_copy_attr_times(dir);", 
      "+\t\t\tfsstack_copy_inode_size(dir,", 
      "+\t\t\t\t\t\tlower_parent_dentry->d_inode);", 
      "+", 
      "+\t\t\t/* update number of links on parent directory */", 
      "+\t\t\tdir->i_nlink = unionfs_get_nlinks(dir);", 
      "+\t\t}", 
      "+", 
      "+\t\terr = make_dir_opaque(dentry, dbstart(dentry));", 
      "+\t\tif (err) {", 
      "+\t\t\tprintk(KERN_ERR \"unionfs: mkdir: error creating \"", 
      "+\t\t\t       \".wh.__dir_opaque: %d\\n\", err);", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+", 
      "+\t\t/* we are done! */", 
      "+\t\tbreak;", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\tif (!dentry->d_inode)", 
      "+\t\td_drop(dentry);", 
      "+", 
      "+\tkfree(name);", 
      "+", 
      "+\tif (!err) {", 
      "+\t\tunionfs_copy_attr_times(dentry->d_inode);", 
      "+\t\tunionfs_postcopyup_setmnt(dentry);", 
      "+\t}", 
      "+\tunionfs_check_inode(dir);", 
      "+\tunionfs_check_dentry(dentry);", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+static int unionfs_mknod(struct inode *dir, struct dentry *dentry, int mode,", 
      "+\t\t\t dev_t dev)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct dentry *lower_dentry = NULL;", 
      "+\tstruct dentry *wh_dentry = NULL;", 
      "+\tstruct dentry *lower_parent_dentry = NULL;", 
      "+\tstruct dentry *parent;", 
      "+\tchar *name = NULL;", 
      "+\tint valid = 0;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\tvalid = __unionfs_d_revalidate(dentry, parent, false);", 
      "+\tif (unlikely(!valid)) {", 
      "+\t\terr = -ESTALE;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * It's only a bug if this dentry was not negative and couldn't be", 
      "+\t * revalidated (shouldn't happen).", 
      "+\t */", 
      "+\tBUG_ON(!valid && dentry->d_inode);", 
      "+", 
      "+\tlower_dentry = find_writeable_branch(dir, dentry);", 
      "+\tif (IS_ERR(lower_dentry)) {", 
      "+\t\terr = PTR_ERR(lower_dentry);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tlower_parent_dentry = lock_parent(lower_dentry);", 
      "+\tif (IS_ERR(lower_parent_dentry)) {", 
      "+\t\terr = PTR_ERR(lower_parent_dentry);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\terr = vfs_mknod(lower_parent_dentry->d_inode, lower_dentry, mode, dev);", 
      "+\tif (!err) {", 
      "+\t\terr = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0));", 
      "+\t\tif (!err) {", 
      "+\t\t\tunionfs_copy_attr_times(dir);", 
      "+\t\t\tfsstack_copy_inode_size(dir,", 
      "+\t\t\t\t\t\tlower_parent_dentry->d_inode);", 
      "+\t\t\t/* update no. of links on parent directory */", 
      "+\t\t\tdir->i_nlink = unionfs_get_nlinks(dir);", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\tunlock_dir(lower_parent_dentry);", 
      "+", 
      "+out:", 
      "+\tdput(wh_dentry);", 
      "+\tkfree(name);", 
      "+", 
      "+\tif (!err) {", 
      "+\t\tunionfs_postcopyup_setmnt(dentry);", 
      "+\t\tunionfs_check_inode(dir);", 
      "+\t\tunionfs_check_dentry(dentry);", 
      "+\t}", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/* requires sb, dentry, and parent to already be locked */", 
      "+static int __unionfs_readlink(struct dentry *dentry, char __user *buf,", 
      "+\t\t\t      int bufsiz)", 
      "+{", 
      "+\tint err;", 
      "+\tstruct dentry *lower_dentry;", 
      "+", 
      "+\tlower_dentry = unionfs_lower_dentry(dentry);", 
      "+", 
      "+\tif (!lower_dentry->d_inode->i_op ||", 
      "+\t    !lower_dentry->d_inode->i_op->readlink) {", 
      "+\t\terr = -EINVAL;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\terr = lower_dentry->d_inode->i_op->readlink(lower_dentry,", 
      "+\t\t\t\t\t\t    buf, bufsiz);", 
      "+\tif (err >= 0)", 
      "+\t\tfsstack_copy_attr_atime(dentry->d_inode,", 
      "+\t\t\t\t\tlower_dentry->d_inode);", 
      "+", 
      "+out:", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+static int unionfs_readlink(struct dentry *dentry, char __user *buf,", 
      "+\t\t\t    int bufsiz)", 
      "+{", 
      "+\tint err;", 
      "+\tstruct dentry *parent;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\tif (unlikely(!__unionfs_d_revalidate(dentry, parent, false))) {", 
      "+\t\terr = -ESTALE;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\terr = __unionfs_readlink(dentry, buf, bufsiz);", 
      "+", 
      "+out:", 
      "+\tunionfs_check_dentry(dentry);", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+static void *unionfs_follow_link(struct dentry *dentry, struct nameidata *nd)", 
      "+{", 
      "+\tchar *buf;", 
      "+\tint len = PAGE_SIZE, err;", 
      "+\tmm_segment_t old_fs;", 
      "+\tstruct dentry *parent;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\t/* This is freed by the put_link method assuming a successful call. */", 
      "+\tbuf = kmalloc(len, GFP_KERNEL);", 
      "+\tif (unlikely(!buf)) {", 
      "+\t\terr = -ENOMEM;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/* read the symlink, and then we will follow it */", 
      "+\told_fs = get_fs();", 
      "+\tset_fs(KERNEL_DS);", 
      "+\terr = __unionfs_readlink(dentry, buf, len);", 
      "+\tset_fs(old_fs);", 
      "+\tif (err < 0) {", 
      "+\t\tkfree(buf);", 
      "+\t\tbuf = NULL;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tbuf[err] = 0;", 
      "+\tnd_set_link(nd, buf);", 
      "+\terr = 0;", 
      "+", 
      "+out:", 
      "+\tif (err >= 0) {", 
      "+\t\tunionfs_check_nd(nd);", 
      "+\t\tunionfs_check_dentry(dentry);", 
      "+\t}", 
      "+", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+", 
      "+\treturn ERR_PTR(err);", 
      "+}", 
      "+", 
      "+/* this @nd *IS* still used */", 
      "+static void unionfs_put_link(struct dentry *dentry, struct nameidata *nd,", 
      "+\t\t\t     void *cookie)", 
      "+{", 
      "+\tstruct dentry *parent;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\tif (unlikely(!__unionfs_d_revalidate(dentry, parent, false)))", 
      "+\t\tprintk(KERN_ERR", 
      "+\t\t       \"unionfs: put_link failed to revalidate dentry\\n\");", 
      "+", 
      "+\tunionfs_check_dentry(dentry);", 
      "+\tunionfs_check_nd(nd);", 
      "+\tkfree(nd_get_link(nd));", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+}", 
      "+", 
      "+/*", 
      "+ * This is a variant of fs/namei.c:permission() or inode_permission() which", 
      "+ * skips over EROFS tests (because we perform copyup on EROFS).", 
      "+ */", 
      "+static int __inode_permission(struct inode *inode, int mask,", 
      "+\t\t\t      struct nameidata *nd)", 
      "+{", 
      "+\tint retval;", 
      "+", 
      "+\t/* nobody gets write access to an immutable file */", 
      "+\tif ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))", 
      "+\t\treturn -EACCES;", 
      "+", 
      "+\t/* Ordinary permission routines do not understand MAY_APPEND. */", 
      "+\tif (inode->i_op && inode->i_op->permission) {", 
      "+\t\tretval = inode->i_op->permission(inode, mask, nd);", 
      "+\t\tif (!retval) {", 
      "+\t\t\t/*", 
      "+\t\t\t * Exec permission on a regular file is denied if none", 
      "+\t\t\t * of the execute bits are set.", 
      "+\t\t\t *", 
      "+\t\t\t * This check should be done by the ->permission()", 
      "+\t\t\t * method.", 
      "+\t\t\t */", 
      "+\t\t\tif ((mask & MAY_EXEC) && S_ISREG(inode->i_mode) &&", 
      "+\t\t\t    !(inode->i_mode & S_IXUGO))", 
      "+\t\t\t\treturn -EACCES;", 
      "+\t\t}", 
      "+\t} else {", 
      "+\t\tretval = generic_permission(inode, mask, NULL);", 
      "+\t}", 
      "+\tif (retval)", 
      "+\t\treturn retval;", 
      "+", 
      "+\treturn security_inode_permission(inode,", 
      "+\t\t\t mask & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND),", 
      "+\t\t\t nd);", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Don't grab the superblock read-lock in unionfs_permission, which prevents", 
      "+ * a deadlock with the branch-management \"add branch\" code (which grabbed", 
      "+ * the write lock).  It is safe to not grab the read lock here, because even", 
      "+ * with branch management taking place, there is no chance that", 
      "+ * unionfs_permission, or anything it calls, will use stale branch", 
      "+ * information.", 
      "+ */", 
      "+static int unionfs_permission(struct inode *inode, int mask,", 
      "+\t\t\t      struct nameidata *nd)", 
      "+{", 
      "+\tstruct inode *lower_inode = NULL;", 
      "+\tint err = 0;", 
      "+\tint bindex, bstart, bend;", 
      "+\tconst int is_file = !S_ISDIR(inode->i_mode);", 
      "+\tconst int write_mask = (mask & MAY_WRITE) && !(mask & MAY_READ);", 
      "+\tstruct inode *inode_grabbed = igrab(inode);", 
      "+", 
      "+\tif (nd)", 
      "+\t\tunionfs_lock_dentry(nd->dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\tif (!UNIONFS_I(inode)->lower_inodes) {", 
      "+\t\tif (is_file)\t/* dirs can be unlinked but chdir'ed to */", 
      "+\t\t\terr = -ESTALE;\t/* force revalidate */", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tbstart = ibstart(inode);", 
      "+\tbend = ibend(inode);", 
      "+\tif (unlikely(bstart < 0 || bend < 0)) {", 
      "+\t\t/*", 
      "+\t\t * With branch-management, we can get a stale inode here.", 
      "+\t\t * If so, we return ESTALE back to link_path_walk, which", 
      "+\t\t * would discard the dcache entry and re-lookup the", 
      "+\t\t * dentry+inode.  This should be equivalent to issuing", 
      "+\t\t * __unionfs_d_revalidate_chain on nd.dentry here.", 
      "+\t\t */", 
      "+\t\tif (is_file)\t/* dirs can be unlinked but chdir'ed to */", 
      "+\t\t\terr = -ESTALE;\t/* force revalidate */", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tlower_inode = unionfs_lower_inode_idx(inode, bindex);", 
      "+\t\tif (!lower_inode)", 
      "+\t\t\tcontinue;", 
      "+", 
      "+\t\t/*", 
      "+\t\t * check the condition for D-F-D underlying files/directories,", 
      "+\t\t * we don't have to check for files, if we are checking for", 
      "+\t\t * directories.", 
      "+\t\t */", 
      "+\t\tif (!is_file && !S_ISDIR(lower_inode->i_mode))", 
      "+\t\t\tcontinue;", 
      "+", 
      "+\t\t/*", 
      "+\t\t * We check basic permissions, but we ignore any conditions", 
      "+\t\t * such as readonly file systems or branches marked as", 
      "+\t\t * readonly, because those conditions should lead to a", 
      "+\t\t * copyup taking place later on.  However, if user never had", 
      "+\t\t * access to the file, then no copyup could ever take place.", 
      "+\t\t */", 
      "+\t\terr = __inode_permission(lower_inode, mask, nd);", 
      "+\t\tif (err && err != -EACCES && err != EPERM && bindex > 0) {", 
      "+\t\t\tumode_t mode = lower_inode->i_mode;", 
      "+\t\t\tif ((is_robranch_super(inode->i_sb, bindex) ||", 
      "+\t\t\t     IS_RDONLY(lower_inode)) &&", 
      "+\t\t\t    (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))", 
      "+\t\t\t\terr = 0;", 
      "+\t\t\tif (IS_COPYUP_ERR(err))", 
      "+\t\t\t\terr = 0;", 
      "+\t\t}", 
      "+", 
      "+\t\t/*", 
      "+\t\t * The permissions are an intersection of the overall directory", 
      "+\t\t * permissions, so we fail if one fails.", 
      "+\t\t */", 
      "+\t\tif (err)", 
      "+\t\t\tgoto out;", 
      "+", 
      "+\t\t/* only the leftmost file matters. */", 
      "+\t\tif (is_file || write_mask) {", 
      "+\t\t\tif (is_file && write_mask) {", 
      "+\t\t\t\terr = get_write_access(lower_inode);", 
      "+\t\t\t\tif (!err)", 
      "+\t\t\t\t\tput_write_access(lower_inode);", 
      "+\t\t\t}", 
      "+\t\t\tbreak;", 
      "+\t\t}", 
      "+\t}", 
      "+\t/* sync times which may have changed (asynchronously) below */", 
      "+\tunionfs_copy_attr_times(inode);", 
      "+", 
      "+out:", 
      "+\tunionfs_check_inode(inode);", 
      "+\tunionfs_check_nd(nd);", 
      "+\tif (nd)", 
      "+\t\tunionfs_unlock_dentry(nd->dentry);", 
      "+\tiput(inode_grabbed);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+static int unionfs_setattr(struct dentry *dentry, struct iattr *ia)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct dentry *lower_dentry;", 
      "+\tstruct dentry *parent;", 
      "+\tstruct inode *inode;", 
      "+\tstruct inode *lower_inode;", 
      "+\tint bstart, bend, bindex;", 
      "+\tloff_t size;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\tif (unlikely(!__unionfs_d_revalidate(dentry, parent, false))) {", 
      "+\t\terr = -ESTALE;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tbstart = dbstart(dentry);", 
      "+\tbend = dbend(dentry);", 
      "+\tinode = dentry->d_inode;", 
      "+", 
      "+\tlower_dentry = unionfs_lower_dentry(dentry);", 
      "+\tif (!lower_dentry) { /* should never happen after above revalidate */", 
      "+\t\terr = -EINVAL;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tlower_inode = unionfs_lower_inode(inode);", 
      "+", 
      "+\t/* check if user has permission to change lower inode */", 
      "+\terr = inode_change_ok(lower_inode, ia);", 
      "+\tif (err)", 
      "+\t\tgoto out;", 
      "+", 
      "+\t/* copyup if the file is on a read only branch */", 
      "+\tif (is_robranch_super(dentry->d_sb, bstart)", 
      "+\t    || IS_RDONLY(lower_inode)) {", 
      "+\t\t/* check if we have a branch to copy up to */", 
      "+\t\tif (bstart <= 0) {", 
      "+\t\t\terr = -EACCES;", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+", 
      "+\t\tif (ia->ia_valid & ATTR_SIZE)", 
      "+\t\t\tsize = ia->ia_size;", 
      "+\t\telse", 
      "+\t\t\tsize = i_size_read(inode);", 
      "+\t\t/* copyup to next available branch */", 
      "+\t\tfor (bindex = bstart - 1; bindex >= 0; bindex--) {", 
      "+\t\t\terr = copyup_dentry(parent->d_inode,", 
      "+\t\t\t\t\t    dentry, bstart, bindex,", 
      "+\t\t\t\t\t    dentry->d_name.name,", 
      "+\t\t\t\t\t    dentry->d_name.len,", 
      "+\t\t\t\t\t    NULL, size);", 
      "+\t\t\tif (!err)", 
      "+\t\t\t\tbreak;", 
      "+\t\t}", 
      "+\t\tif (err)", 
      "+\t\t\tgoto out;", 
      "+\t\t/* get updated lower_dentry/inode after copyup */", 
      "+\t\tlower_dentry = unionfs_lower_dentry(dentry);", 
      "+\t\tlower_inode = unionfs_lower_inode(inode);", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * If shrinking, first truncate upper level to cancel writing dirty", 
      "+\t * pages beyond the new eof; and also if its' maxbytes is more", 
      "+\t * limiting (fail with -EFBIG before making any change to the lower", 
      "+\t * level).  There is no need to vmtruncate the upper level", 
      "+\t * afterwards in the other cases: we fsstack_copy_inode_size from", 
      "+\t * the lower level.", 
      "+\t */", 
      "+\tif (ia->ia_valid & ATTR_SIZE) {", 
      "+\t\tsize = i_size_read(inode);", 
      "+\t\tif (ia->ia_size < size || (ia->ia_size > size &&", 
      "+\t\t    inode->i_sb->s_maxbytes < lower_inode->i_sb->s_maxbytes)) {", 
      "+\t\t\terr = vmtruncate(inode, ia->ia_size);", 
      "+\t\t\tif (err)", 
      "+\t\t\t\tgoto out;", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\t/* notify the (possibly copied-up) lower inode */", 
      "+\tmutex_lock(&lower_inode->i_mutex);", 
      "+\terr = notify_change(lower_dentry, ia);", 
      "+\tmutex_unlock(&lower_inode->i_mutex);", 
      "+\tif (err)", 
      "+\t\tgoto out;", 
      "+", 
      "+\t/* get attributes from the first lower inode */", 
      "+\tunionfs_copy_attr_all(inode, lower_inode);", 
      "+\t/*", 
      "+\t * unionfs_copy_attr_all will copy the lower times to our inode if", 
      "+\t * the lower ones are newer (useful for cache coherency).  However,", 
      "+\t * ->setattr is the only place in which we may have to copy the", 
      "+\t * lower inode times absolutely, to support utimes(2).", 
      "+\t */", 
      "+\tif (ia->ia_valid & ATTR_MTIME_SET)", 
      "+\t\tinode->i_mtime = lower_inode->i_mtime;", 
      "+\tif (ia->ia_valid & ATTR_CTIME)", 
      "+\t\tinode->i_ctime = lower_inode->i_ctime;", 
      "+\tif (ia->ia_valid & ATTR_ATIME_SET)", 
      "+\t\tinode->i_atime = lower_inode->i_atime;", 
      "+\tfsstack_copy_inode_size(inode, lower_inode);", 
      "+", 
      "+out:", 
      "+\tif (!err)", 
      "+\t\tunionfs_check_dentry(dentry);", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+struct inode_operations unionfs_symlink_iops = {", 
      "+\t.readlink\t= unionfs_readlink,", 
      "+\t.permission\t= unionfs_permission,", 
      "+\t.follow_link\t= unionfs_follow_link,", 
      "+\t.setattr\t= unionfs_setattr,", 
      "+\t.put_link\t= unionfs_put_link,", 
      "+};", 
      "+", 
      "+struct inode_operations unionfs_dir_iops = {", 
      "+\t.create\t\t= unionfs_create,", 
      "+\t.lookup\t\t= unionfs_lookup,", 
      "+\t.link\t\t= unionfs_link,", 
      "+\t.unlink\t\t= unionfs_unlink,", 
      "+\t.symlink\t= unionfs_symlink,", 
      "+\t.mkdir\t\t= unionfs_mkdir,", 
      "+\t.rmdir\t\t= unionfs_rmdir,", 
      "+\t.mknod\t\t= unionfs_mknod,", 
      "+\t.rename\t\t= unionfs_rename,", 
      "+\t.permission\t= unionfs_permission,", 
      "+\t.setattr\t= unionfs_setattr,", 
      "+#ifdef CONFIG_UNION_FS_XATTR", 
      "+\t.setxattr\t= unionfs_setxattr,", 
      "+\t.getxattr\t= unionfs_getxattr,", 
      "+\t.removexattr\t= unionfs_removexattr,", 
      "+\t.listxattr\t= unionfs_listxattr,", 
      "+#endif /* CONFIG_UNION_FS_XATTR */", 
      "+};", 
      "+", 
      "+struct inode_operations unionfs_main_iops = {", 
      "+\t.permission\t= unionfs_permission,", 
      "+\t.setattr\t= unionfs_setattr,", 
      "+#ifdef CONFIG_UNION_FS_XATTR", 
      "+\t.setxattr\t= unionfs_setxattr,", 
      "+\t.getxattr\t= unionfs_getxattr,", 
      "+\t.removexattr\t= unionfs_removexattr,", 
      "+\t.listxattr\t= unionfs_listxattr,", 
      "+#endif /* CONFIG_UNION_FS_XATTR */", 
      "+};"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/lookup.c", 
    [
      "diff --git a/fs/unionfs/lookup.c b/fs/unionfs/lookup.c", 
      "new file mode 100644", 
      "index 0000000..3363f67", 
      "--- dev/null", 
      "+++ b/fs/unionfs/lookup.c", 
      "@@ -0,0 +1,572 @@", 
      "+/*", 
      "+ * Copyright (c) 2003-2009 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-2009 Stony Brook University", 
      "+ * Copyright (c) 2003-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#include \"union.h\"", 
      "+", 
      "+/*", 
      "+ * Lookup one path component @name relative to a <base,mnt> path pair.", 
      "+ * Behaves nearly the same as lookup_one_len (i.e., return negative dentry", 
      "+ * on ENOENT), but uses the @mnt passed, so it can cross bind mounts and", 
      "+ * other lower mounts properly.  If @new_mnt is non-null, will fill in the", 
      "+ * new mnt there.  Caller is responsible to dput/mntput/path_put returned", 
      "+ * @dentry and @new_mnt.", 
      "+ */", 
      "+struct dentry *__lookup_one(struct dentry *base, struct vfsmount *mnt,", 
      "+\t\t\t    const char *name, struct vfsmount **new_mnt)", 
      "+{", 
      "+\tstruct dentry *dentry = NULL;", 
      "+\tstruct nameidata lower_nd;", 
      "+\tint err;", 
      "+", 
      "+\t/* we use flags=0 to get basic lookup */", 
      "+\terr = vfs_path_lookup(base, mnt, name, 0, &lower_nd);", 
      "+", 
      "+\tswitch (err) {", 
      "+\tcase 0: /* no error */", 
      "+\t\tdentry = lower_nd.dentry;", 
      "+\t\tif (new_mnt)", 
      "+\t\t\t*new_mnt = lower_nd.mnt; /* rc already inc'ed */", 
      "+\t\tbreak;", 
      "+\tcase -ENOENT:", 
      "+\t\t /*", 
      "+\t\t  * We don't consider ENOENT an error, and we want to return", 
      "+\t\t  * a negative dentry (ala lookup_one_len).  As we know", 
      "+\t\t  * there was no inode for this name before (-ENOENT), then", 
      "+\t\t  * it's safe to call lookup_one_len (which doesn't take a", 
      "+\t\t  * vfsmount).", 
      "+\t\t  */", 
      "+\t\tdentry = lookup_one_len(name, base, strlen(name));", 
      "+\t\tif (new_mnt)", 
      "+\t\t\t*new_mnt = mntget(lower_nd.mnt);", 
      "+\t\tbreak;", 
      "+\tdefault: /* all other real errors */", 
      "+\t\tdentry = ERR_PTR(err);", 
      "+\t\tbreak;", 
      "+\t}", 
      "+", 
      "+\treturn dentry;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * This is a utility function that fills in a unionfs dentry.", 
      "+ * Caller must lock this dentry with unionfs_lock_dentry.", 
      "+ *", 
      "+ * Returns: 0 (ok), or -ERRNO if an error occurred.", 
      "+ * XXX: get rid of _partial_lookup and make callers call _lookup_full directly", 
      "+ */", 
      "+int unionfs_partial_lookup(struct dentry *dentry, struct dentry *parent)", 
      "+{", 
      "+\tstruct dentry *tmp;", 
      "+\tint err = -ENOSYS;", 
      "+", 
      "+\ttmp = unionfs_lookup_full(dentry, parent, INTERPOSE_PARTIAL);", 
      "+", 
      "+\tif (!tmp) {", 
      "+\t\terr = 0;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tif (IS_ERR(tmp)) {", 
      "+\t\terr = PTR_ERR(tmp);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\t/* XXX: need to change the interface */", 
      "+\tBUG_ON(tmp != dentry);", 
      "+out:", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/* The dentry cache is just so we have properly sized dentries. */", 
      "+static struct kmem_cache *unionfs_dentry_cachep;", 
      "+int unionfs_init_dentry_cache(void)", 
      "+{", 
      "+\tunionfs_dentry_cachep =", 
      "+\t\tkmem_cache_create(\"unionfs_dentry\",", 
      "+\t\t\t\t  sizeof(struct unionfs_dentry_info),", 
      "+\t\t\t\t  0, SLAB_RECLAIM_ACCOUNT, NULL);", 
      "+", 
      "+\treturn (unionfs_dentry_cachep ? 0 : -ENOMEM);", 
      "+}", 
      "+", 
      "+void unionfs_destroy_dentry_cache(void)", 
      "+{", 
      "+\tif (unionfs_dentry_cachep)", 
      "+\t\tkmem_cache_destroy(unionfs_dentry_cachep);", 
      "+}", 
      "+", 
      "+void free_dentry_private_data(struct dentry *dentry)", 
      "+{", 
      "+\tif (!dentry || !dentry->d_fsdata)", 
      "+\t\treturn;", 
      "+\tkfree(UNIONFS_D(dentry)->lower_paths);", 
      "+\tUNIONFS_D(dentry)->lower_paths = NULL;", 
      "+\tkmem_cache_free(unionfs_dentry_cachep, dentry->d_fsdata);", 
      "+\tdentry->d_fsdata = NULL;", 
      "+}", 
      "+", 
      "+static inline int __realloc_dentry_private_data(struct dentry *dentry)", 
      "+{", 
      "+\tstruct unionfs_dentry_info *info = UNIONFS_D(dentry);", 
      "+\tvoid *p;", 
      "+\tint size;", 
      "+", 
      "+\tBUG_ON(!info);", 
      "+", 
      "+\tsize = sizeof(struct path) * sbmax(dentry->d_sb);", 
      "+\tp = krealloc(info->lower_paths, size, GFP_ATOMIC);", 
      "+\tif (unlikely(!p))", 
      "+\t\treturn -ENOMEM;", 
      "+", 
      "+\tinfo->lower_paths = p;", 
      "+", 
      "+\tinfo->bstart = -1;", 
      "+\tinfo->bend = -1;", 
      "+\tinfo->bopaque = -1;", 
      "+\tinfo->bcount = sbmax(dentry->d_sb);", 
      "+\tatomic_set(&info->generation,", 
      "+\t\t\tatomic_read(&UNIONFS_SB(dentry->d_sb)->generation));", 
      "+", 
      "+\tmemset(info->lower_paths, 0, size);", 
      "+", 
      "+\treturn 0;", 
      "+}", 
      "+", 
      "+/* UNIONFS_D(dentry)->lock must be locked */", 
      "+int realloc_dentry_private_data(struct dentry *dentry)", 
      "+{", 
      "+\tif (!__realloc_dentry_private_data(dentry))", 
      "+\t\treturn 0;", 
      "+", 
      "+\tkfree(UNIONFS_D(dentry)->lower_paths);", 
      "+\tfree_dentry_private_data(dentry);", 
      "+\treturn -ENOMEM;", 
      "+}", 
      "+", 
      "+/* allocate new dentry private data */", 
      "+int new_dentry_private_data(struct dentry *dentry, int subclass)", 
      "+{", 
      "+\tstruct unionfs_dentry_info *info = UNIONFS_D(dentry);", 
      "+", 
      "+\tBUG_ON(info);", 
      "+", 
      "+\tinfo = kmem_cache_alloc(unionfs_dentry_cachep, GFP_ATOMIC);", 
      "+\tif (unlikely(!info))", 
      "+\t\treturn -ENOMEM;", 
      "+", 
      "+\tmutex_init(&info->lock);", 
      "+\tmutex_lock_nested(&info->lock, subclass);", 
      "+", 
      "+\tinfo->lower_paths = NULL;", 
      "+", 
      "+\tdentry->d_fsdata = info;", 
      "+", 
      "+\tif (!__realloc_dentry_private_data(dentry))", 
      "+\t\treturn 0;", 
      "+", 
      "+\tmutex_unlock(&info->lock);", 
      "+\tfree_dentry_private_data(dentry);", 
      "+\treturn -ENOMEM;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * scan through the lower dentry objects, and set bstart to reflect the", 
      "+ * starting branch", 
      "+ */", 
      "+void update_bstart(struct dentry *dentry)", 
      "+{", 
      "+\tint bindex;", 
      "+\tint bstart = dbstart(dentry);", 
      "+\tint bend = dbend(dentry);", 
      "+\tstruct dentry *lower_dentry;", 
      "+", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tlower_dentry = unionfs_lower_dentry_idx(dentry, bindex);", 
      "+\t\tif (!lower_dentry)", 
      "+\t\t\tcontinue;", 
      "+\t\tif (lower_dentry->d_inode) {", 
      "+\t\t\tdbstart(dentry) = bindex;", 
      "+\t\t\tbreak;", 
      "+\t\t}", 
      "+\t\tdput(lower_dentry);", 
      "+\t\tunionfs_set_lower_dentry_idx(dentry, bindex, NULL);", 
      "+\t}", 
      "+}", 
      "+", 
      "+", 
      "+/*", 
      "+ * Initialize a nameidata structure (the intent part) we can pass to a lower", 
      "+ * file system.  Returns 0 on success or -error (only -ENOMEM possible).", 
      "+ * Inside that nd structure, this function may also return an allocated", 
      "+ * struct file (for open intents).  The caller, when done with this nd, must", 
      "+ * kfree the intent file (using release_lower_nd).", 
      "+ *", 
      "+ * XXX: this code, and the callers of this code, should be redone using", 
      "+ * vfs_path_lookup() when (1) the nameidata structure is refactored into a", 
      "+ * separate intent-structure, and (2) open_namei() is broken into a VFS-only", 
      "+ * function and a method that other file systems can call.", 
      "+ */", 
      "+int init_lower_nd(struct nameidata *nd, unsigned int flags)", 
      "+{", 
      "+\tint err = 0;", 
      "+#ifdef ALLOC_LOWER_ND_FILE", 
      "+\t/*", 
      "+\t * XXX: one day we may need to have the lower return an open file", 
      "+\t * for us.  It is not needed in 2.6.23-rc1 for nfs2/nfs3, but may", 
      "+\t * very well be needed for nfs4.", 
      "+\t */", 
      "+\tstruct file *file;", 
      "+#endif /* ALLOC_LOWER_ND_FILE */", 
      "+", 
      "+\tmemset(nd, 0, sizeof(struct nameidata));", 
      "+\tif (!flags)", 
      "+\t\treturn err;", 
      "+", 
      "+\tswitch (flags) {", 
      "+\tcase LOOKUP_CREATE:", 
      "+\t\tnd->intent.open.flags |= O_CREAT;", 
      "+\t\t/* fall through: shared code for create/open cases */", 
      "+\tcase LOOKUP_OPEN:", 
      "+\t\tnd->flags = flags;", 
      "+\t\tnd->intent.open.flags |= (FMODE_READ | FMODE_WRITE);", 
      "+#ifdef ALLOC_LOWER_ND_FILE", 
      "+\t\tfile = kzalloc(sizeof(struct file), GFP_KERNEL);", 
      "+\t\tif (unlikely(!file)) {", 
      "+\t\t\terr = -ENOMEM;", 
      "+\t\t\tbreak; /* exit switch statement and thus return */", 
      "+\t\t}", 
      "+\t\tnd->intent.open.file = file;", 
      "+#endif /* ALLOC_LOWER_ND_FILE */", 
      "+\t\tbreak;", 
      "+\tcase LOOKUP_ACCESS:", 
      "+\t\tnd->flags = flags;", 
      "+\t\tbreak;", 
      "+\tdefault:", 
      "+\t\t/*", 
      "+\t\t * We should never get here, for now.", 
      "+\t\t * We can add new cases here later on.", 
      "+\t\t */", 
      "+\t\tpr_debug(\"unionfs: unknown nameidata flag 0x%x\\n\", flags);", 
      "+\t\tBUG();", 
      "+\t\tbreak;", 
      "+\t}", 
      "+", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+void release_lower_nd(struct nameidata *nd, int err)", 
      "+{", 
      "+\tif (!nd->intent.open.file)", 
      "+\t\treturn;", 
      "+\telse if (!err)", 
      "+\t\trelease_open_intent(nd);", 
      "+#ifdef ALLOC_LOWER_ND_FILE", 
      "+\tkfree(nd->intent.open.file);", 
      "+#endif /* ALLOC_LOWER_ND_FILE */", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Main (and complex) driver function for Unionfs's lookup", 
      "+ *", 
      "+ * Returns: NULL (ok), ERR_PTR if an error occurred, or a non-null non-error", 
      "+ * PTR if d_splice returned a different dentry.", 
      "+ *", 
      "+ * If lookupmode is INTERPOSE_PARTIAL/REVAL/REVAL_NEG, the passed dentry's", 
      "+ * inode info must be locked.  If lookupmode is INTERPOSE_LOOKUP (i.e., a", 
      "+ * newly looked-up dentry), then unionfs_lookup_backend will return a locked", 
      "+ * dentry's info, which the caller must unlock.", 
      "+ */", 
      "+struct dentry *unionfs_lookup_full(struct dentry *dentry,", 
      "+\t\t\t\t   struct dentry *parent, int lookupmode)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct dentry *lower_dentry = NULL;", 
      "+\tstruct vfsmount *lower_mnt;", 
      "+\tstruct vfsmount *lower_dir_mnt;", 
      "+\tstruct dentry *wh_lower_dentry = NULL;", 
      "+\tstruct dentry *lower_dir_dentry = NULL;", 
      "+\tstruct dentry *d_interposed = NULL;", 
      "+\tint bindex, bstart, bend, bopaque;", 
      "+\tint opaque, num_positive = 0;", 
      "+\tconst char *name;", 
      "+\tint namelen;", 
      "+\tint pos_start, pos_end;", 
      "+", 
      "+\t/*", 
      "+\t * We should already have a lock on this dentry in the case of a", 
      "+\t * partial lookup, or a revalidation.  Otherwise it is returned from", 
      "+\t * new_dentry_private_data already locked.", 
      "+\t */", 
      "+\tverify_locked(dentry);", 
      "+\tverify_locked(parent);", 
      "+", 
      "+\t/* must initialize dentry operations */", 
      "+\tdentry->d_op = &unionfs_dops;", 
      "+", 
      "+\t/* We never partial lookup the root directory. */", 
      "+\tif (IS_ROOT(dentry))", 
      "+\t\tgoto out;", 
      "+", 
      "+\tname = dentry->d_name.name;", 
      "+\tnamelen = dentry->d_name.len;", 
      "+", 
      "+\t/* No dentries should get created for possible whiteout names. */", 
      "+\tif (!is_validname(name)) {", 
      "+\t\terr = -EPERM;", 
      "+\t\tgoto out_free;", 
      "+\t}", 
      "+", 
      "+\t/* Now start the actual lookup procedure. */", 
      "+\tbstart = dbstart(parent);", 
      "+\tbend = dbend(parent);", 
      "+\tbopaque = dbopaque(parent);", 
      "+\tBUG_ON(bstart < 0);", 
      "+", 
      "+\t/* adjust bend to bopaque if needed */", 
      "+\tif ((bopaque >= 0) && (bopaque < bend))", 
      "+\t\tbend = bopaque;", 
      "+", 
      "+\t/* lookup all possible dentries */", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+", 
      "+\t\tlower_dentry = unionfs_lower_dentry_idx(dentry, bindex);", 
      "+\t\tlower_mnt = unionfs_lower_mnt_idx(dentry, bindex);", 
      "+", 
      "+\t\t/* skip if we already have a positive lower dentry */", 
      "+\t\tif (lower_dentry) {", 
      "+\t\t\tif (dbstart(dentry) < 0)", 
      "+\t\t\t\tdbstart(dentry) = bindex;", 
      "+\t\t\tif (bindex > dbend(dentry))", 
      "+\t\t\t\tdbend(dentry) = bindex;", 
      "+\t\t\tif (lower_dentry->d_inode)", 
      "+\t\t\t\tnum_positive++;", 
      "+\t\t\tcontinue;", 
      "+\t\t}", 
      "+", 
      "+\t\tlower_dir_dentry =", 
      "+\t\t\tunionfs_lower_dentry_idx(parent, bindex);", 
      "+\t\t/* if the lower dentry's parent does not exist, skip this */", 
      "+\t\tif (!lower_dir_dentry || !lower_dir_dentry->d_inode)", 
      "+\t\t\tcontinue;", 
      "+", 
      "+\t\t/* also skip it if the parent isn't a directory. */", 
      "+\t\tif (!S_ISDIR(lower_dir_dentry->d_inode->i_mode))", 
      "+\t\t\tcontinue; /* XXX: should be BUG_ON */", 
      "+", 
      "+\t\t/* check for whiteouts: stop lookup if found */", 
      "+\t\twh_lower_dentry = lookup_whiteout(name, lower_dir_dentry);", 
      "+\t\tif (IS_ERR(wh_lower_dentry)) {", 
      "+\t\t\terr = PTR_ERR(wh_lower_dentry);", 
      "+\t\t\tgoto out_free;", 
      "+\t\t}", 
      "+\t\tif (wh_lower_dentry->d_inode) {", 
      "+\t\t\tdbend(dentry) = dbopaque(dentry) = bindex;", 
      "+\t\t\tif (dbstart(dentry) < 0)", 
      "+\t\t\t\tdbstart(dentry) = bindex;", 
      "+\t\t\tdput(wh_lower_dentry);", 
      "+\t\t\tbreak;", 
      "+\t\t}", 
      "+\t\tdput(wh_lower_dentry);", 
      "+", 
      "+\t\t/* Now do regular lookup; lookup @name */", 
      "+\t\tlower_dir_mnt = unionfs_lower_mnt_idx(parent, bindex);", 
      "+\t\tlower_mnt = NULL; /* XXX: needed? */", 
      "+", 
      "+\t\tlower_dentry = __lookup_one(lower_dir_dentry, lower_dir_mnt,", 
      "+\t\t\t\t\t    name, &lower_mnt);", 
      "+", 
      "+\t\tif (IS_ERR(lower_dentry)) {", 
      "+\t\t\terr = PTR_ERR(lower_dentry);", 
      "+\t\t\tgoto out_free;", 
      "+\t\t}", 
      "+\t\tunionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry);", 
      "+\t\tif (!lower_mnt)", 
      "+\t\t\tlower_mnt = unionfs_mntget(dentry->d_sb->s_root,", 
      "+\t\t\t\t\t\t   bindex);", 
      "+\t\tunionfs_set_lower_mnt_idx(dentry, bindex, lower_mnt);", 
      "+", 
      "+\t\t/* adjust dbstart/end */", 
      "+\t\tif (dbstart(dentry) < 0)", 
      "+\t\t\tdbstart(dentry) = bindex;", 
      "+\t\tif (bindex > dbend(dentry))", 
      "+\t\t\tdbend(dentry) = bindex;", 
      "+\t\t/*", 
      "+\t\t * We always store the lower dentries above, and update", 
      "+\t\t * dbstart/dbend, even if the whole unionfs dentry is", 
      "+\t\t * negative (i.e., no lower inodes).", 
      "+\t\t */", 
      "+\t\tif (!lower_dentry->d_inode)", 
      "+\t\t\tcontinue;", 
      "+\t\tnum_positive++;", 
      "+", 
      "+\t\t/*", 
      "+\t\t * check if we just found an opaque directory, if so, stop", 
      "+\t\t * lookups here.", 
      "+\t\t */", 
      "+\t\tif (!S_ISDIR(lower_dentry->d_inode->i_mode))", 
      "+\t\t\tcontinue;", 
      "+\t\topaque = is_opaque_dir(dentry, bindex);", 
      "+\t\tif (opaque < 0) {", 
      "+\t\t\terr = opaque;", 
      "+\t\t\tgoto out_free;", 
      "+\t\t} else if (opaque) {", 
      "+\t\t\tdbend(dentry) = dbopaque(dentry) = bindex;", 
      "+\t\t\tbreak;", 
      "+\t\t}", 
      "+\t\tdbend(dentry) = bindex;", 
      "+", 
      "+\t\t/* update parent directory's atime with the bindex */", 
      "+\t\tfsstack_copy_attr_atime(parent->d_inode,", 
      "+\t\t\t\t\tlower_dir_dentry->d_inode);", 
      "+\t}", 
      "+", 
      "+\t/* sanity checks, then decide if to process a negative dentry */", 
      "+\tBUG_ON(dbstart(dentry) < 0 && dbend(dentry) >= 0);", 
      "+\tBUG_ON(dbstart(dentry) >= 0 && dbend(dentry) < 0);", 
      "+", 
      "+\tif (num_positive > 0)", 
      "+\t\tgoto out_positive;", 
      "+", 
      "+\t/*** handle NEGATIVE dentries ***/", 
      "+", 
      "+\t/*", 
      "+\t * If negative, keep only first lower negative dentry, to save on", 
      "+\t * memory.", 
      "+\t */", 
      "+\tif (dbstart(dentry) < dbend(dentry)) {", 
      "+\t\tpath_put_lowers(dentry, dbstart(dentry) + 1,", 
      "+\t\t\t\tdbend(dentry), false);", 
      "+\t\tdbend(dentry) = dbstart(dentry);", 
      "+\t}", 
      "+\tif (lookupmode == INTERPOSE_PARTIAL)", 
      "+\t\tgoto out;", 
      "+\tif (lookupmode == INTERPOSE_LOOKUP) {", 
      "+\t\t/*", 
      "+\t\t * If all we found was a whiteout in the first available", 
      "+\t\t * branch, then create a negative dentry for a possibly new", 
      "+\t\t * file to be created.", 
      "+\t\t */", 
      "+\t\tif (dbopaque(dentry) < 0)", 
      "+\t\t\tgoto out;", 
      "+\t\t/* XXX: need to get mnt here */", 
      "+\t\tbindex = dbstart(dentry);", 
      "+\t\tif (unionfs_lower_dentry_idx(dentry, bindex))", 
      "+\t\t\tgoto out;", 
      "+\t\tlower_dir_dentry =", 
      "+\t\t\tunionfs_lower_dentry_idx(parent, bindex);", 
      "+\t\tif (!lower_dir_dentry || !lower_dir_dentry->d_inode)", 
      "+\t\t\tgoto out;", 
      "+\t\tif (!S_ISDIR(lower_dir_dentry->d_inode->i_mode))", 
      "+\t\t\tgoto out; /* XXX: should be BUG_ON */", 
      "+\t\t/* XXX: do we need to cross bind mounts here? */", 
      "+\t\tlower_dentry = lookup_one_len(name, lower_dir_dentry, namelen);", 
      "+\t\tif (IS_ERR(lower_dentry)) {", 
      "+\t\t\terr = PTR_ERR(lower_dentry);", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+\t\t/* XXX: need to mntget/mntput as needed too! */", 
      "+\t\tunionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry);", 
      "+\t\t/* XXX: wrong mnt for crossing bind mounts! */", 
      "+\t\tlower_mnt = unionfs_mntget(dentry->d_sb->s_root, bindex);", 
      "+\t\tunionfs_set_lower_mnt_idx(dentry, bindex, lower_mnt);", 
      "+", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/* if we're revalidating a positive dentry, don't make it negative */", 
      "+\tif (lookupmode != INTERPOSE_REVAL)", 
      "+\t\td_add(dentry, NULL);", 
      "+", 
      "+\tgoto out;", 
      "+", 
      "+out_positive:", 
      "+\t/*** handle POSITIVE dentries ***/", 
      "+", 
      "+\t/*", 
      "+\t * This unionfs dentry is positive (at least one lower inode", 
      "+\t * exists), so scan entire dentry from beginning to end, and remove", 
      "+\t * any negative lower dentries, if any.  Then, update dbstart/dbend", 
      "+\t * to reflect the start/end of positive dentries.", 
      "+\t */", 
      "+\tpos_start = pos_end = -1;", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tlower_dentry = unionfs_lower_dentry_idx(dentry,", 
      "+\t\t\t\t\t\t\tbindex);", 
      "+\t\tif (lower_dentry && lower_dentry->d_inode) {", 
      "+\t\t\tif (pos_start < 0)", 
      "+\t\t\t\tpos_start = bindex;", 
      "+\t\t\tif (bindex > pos_end)", 
      "+\t\t\t\tpos_end = bindex;", 
      "+\t\t\tcontinue;", 
      "+\t\t}", 
      "+\t\tpath_put_lowers(dentry, bindex, bindex, false);", 
      "+\t}", 
      "+\tif (pos_start >= 0)", 
      "+\t\tdbstart(dentry) = pos_start;", 
      "+\tif (pos_end >= 0)", 
      "+\t\tdbend(dentry) = pos_end;", 
      "+", 
      "+\t/* Partial lookups need to re-interpose, or throw away older negs. */", 
      "+\tif (lookupmode == INTERPOSE_PARTIAL) {", 
      "+\t\tif (dentry->d_inode) {", 
      "+\t\t\tunionfs_reinterpose(dentry);", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+", 
      "+\t\t/*", 
      "+\t\t * This dentry was positive, so it is as if we had a", 
      "+\t\t * negative revalidation.", 
      "+\t\t */", 
      "+\t\tlookupmode = INTERPOSE_REVAL_NEG;", 
      "+\t\tupdate_bstart(dentry);", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * Interpose can return a dentry if d_splice returned a different", 
      "+\t * dentry.", 
      "+\t */", 
      "+\td_interposed = unionfs_interpose(dentry, dentry->d_sb, lookupmode);", 
      "+\tif (IS_ERR(d_interposed))", 
      "+\t\terr = PTR_ERR(d_interposed);", 
      "+\telse if (d_interposed)", 
      "+\t\tdentry = d_interposed;", 
      "+", 
      "+\tif (!err)", 
      "+\t\tgoto out;", 
      "+\td_drop(dentry);", 
      "+", 
      "+out_free:", 
      "+\t/* should dput/mntput all the underlying dentries on error condition */", 
      "+\tif (dbstart(dentry) >= 0)", 
      "+\t\tpath_put_lowers_all(dentry, false);", 
      "+\t/* free lower_paths unconditionally */", 
      "+\tkfree(UNIONFS_D(dentry)->lower_paths);", 
      "+\tUNIONFS_D(dentry)->lower_paths = NULL;", 
      "+", 
      "+out:", 
      "+\tif (dentry && UNIONFS_D(dentry)) {", 
      "+\t\tBUG_ON(dbstart(dentry) < 0 && dbend(dentry) >= 0);", 
      "+\t\tBUG_ON(dbstart(dentry) >= 0 && dbend(dentry) < 0);", 
      "+\t}", 
      "+\tif (d_interposed && UNIONFS_D(d_interposed)) {", 
      "+\t\tBUG_ON(dbstart(d_interposed) < 0 && dbend(d_interposed) >= 0);", 
      "+\t\tBUG_ON(dbstart(d_interposed) >= 0 && dbend(d_interposed) < 0);", 
      "+\t}", 
      "+", 
      "+\tif (!err && d_interposed)", 
      "+\t\treturn d_interposed;", 
      "+\treturn ERR_PTR(err);", 
      "+}"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/main.c", 
    [
      "diff --git a/fs/unionfs/main.c b/fs/unionfs/main.c", 
      "new file mode 100644", 
      "index 0000000..e9ae963", 
      "--- dev/null", 
      "+++ b/fs/unionfs/main.c", 
      "@@ -0,0 +1,778 @@", 
      "+/*", 
      "+ * Copyright (c) 2003-2009 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-2009 Stony Brook University", 
      "+ * Copyright (c) 2003-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#include \"union.h\"", 
      "+#include <linux/module.h>", 
      "+#include <linux/moduleparam.h>", 
      "+", 
      "+static void unionfs_fill_inode(struct dentry *dentry,", 
      "+\t\t\t       struct inode *inode)", 
      "+{", 
      "+\tstruct inode *lower_inode;", 
      "+\tstruct dentry *lower_dentry;", 
      "+\tint bindex, bstart, bend;", 
      "+", 
      "+\tbstart = dbstart(dentry);", 
      "+\tbend = dbend(dentry);", 
      "+", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tlower_dentry = unionfs_lower_dentry_idx(dentry, bindex);", 
      "+\t\tif (!lower_dentry) {", 
      "+\t\t\tunionfs_set_lower_inode_idx(inode, bindex, NULL);", 
      "+\t\t\tcontinue;", 
      "+\t\t}", 
      "+", 
      "+\t\t/* Initialize the lower inode to the new lower inode. */", 
      "+\t\tif (!lower_dentry->d_inode)", 
      "+\t\t\tcontinue;", 
      "+", 
      "+\t\tunionfs_set_lower_inode_idx(inode, bindex,", 
      "+\t\t\t\t\t    igrab(lower_dentry->d_inode));", 
      "+\t}", 
      "+", 
      "+\tibstart(inode) = dbstart(dentry);", 
      "+\tibend(inode) = dbend(dentry);", 
      "+", 
      "+\t/* Use attributes from the first branch. */", 
      "+\tlower_inode = unionfs_lower_inode(inode);", 
      "+", 
      "+\t/* Use different set of inode ops for symlinks & directories */", 
      "+\tif (S_ISLNK(lower_inode->i_mode))", 
      "+\t\tinode->i_op = &unionfs_symlink_iops;", 
      "+\telse if (S_ISDIR(lower_inode->i_mode))", 
      "+\t\tinode->i_op = &unionfs_dir_iops;", 
      "+", 
      "+\t/* Use different set of file ops for directories */", 
      "+\tif (S_ISDIR(lower_inode->i_mode))", 
      "+\t\tinode->i_fop = &unionfs_dir_fops;", 
      "+", 
      "+\t/* properly initialize special inodes */", 
      "+\tif (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) ||", 
      "+\t    S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode))", 
      "+\t\tinit_special_inode(inode, lower_inode->i_mode,", 
      "+\t\t\t\t   lower_inode->i_rdev);", 
      "+", 
      "+\t/* all well, copy inode attributes */", 
      "+\tunionfs_copy_attr_all(inode, lower_inode);", 
      "+\tfsstack_copy_inode_size(inode, lower_inode);", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Connect a unionfs inode dentry/inode with several lower ones.  This is", 
      "+ * the classic stackable file system \"vnode interposition\" action.", 
      "+ *", 
      "+ * @sb: unionfs's super_block", 
      "+ */", 
      "+struct dentry *unionfs_interpose(struct dentry *dentry, struct super_block *sb,", 
      "+\t\t\t\t int flag)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct inode *inode;", 
      "+\tint need_fill_inode = 1;", 
      "+\tstruct dentry *spliced = NULL;", 
      "+", 
      "+\tverify_locked(dentry);", 
      "+", 
      "+\t/*", 
      "+\t * We allocate our new inode below, by calling iget.", 
      "+\t * iget will call our read_inode which will initialize some", 
      "+\t * of the new inode's fields", 
      "+\t */", 
      "+", 
      "+\t/*", 
      "+\t * On revalidate we've already got our own inode and just need", 
      "+\t * to fix it up.", 
      "+\t */", 
      "+\tif (flag == INTERPOSE_REVAL) {", 
      "+\t\tinode = dentry->d_inode;", 
      "+\t\tUNIONFS_I(inode)->bstart = -1;", 
      "+\t\tUNIONFS_I(inode)->bend = -1;", 
      "+\t\tatomic_set(&UNIONFS_I(inode)->generation,", 
      "+\t\t\t   atomic_read(&UNIONFS_SB(sb)->generation));", 
      "+", 
      "+\t\tUNIONFS_I(inode)->lower_inodes =", 
      "+\t\t\tkcalloc(sbmax(sb), sizeof(struct inode *), GFP_KERNEL);", 
      "+\t\tif (unlikely(!UNIONFS_I(inode)->lower_inodes)) {", 
      "+\t\t\terr = -ENOMEM;", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+\t} else {", 
      "+\t\t/* get unique inode number for unionfs */", 
      "+\t\tinode = iget(sb, iunique(sb, UNIONFS_ROOT_INO));", 
      "+\t\tif (!inode) {", 
      "+\t\t\terr = -EACCES;", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+\t\tif (atomic_read(&inode->i_count) > 1)", 
      "+\t\t\tgoto skip;", 
      "+\t}", 
      "+", 
      "+\tneed_fill_inode = 0;", 
      "+\tunionfs_fill_inode(dentry, inode);", 
      "+", 
      "+skip:", 
      "+\t/* only (our) lookup wants to do a d_add */", 
      "+\tswitch (flag) {", 
      "+\tcase INTERPOSE_DEFAULT:", 
      "+\t\t/* for operations which create new inodes */", 
      "+\t\td_add(dentry, inode);", 
      "+\t\tbreak;", 
      "+\tcase INTERPOSE_REVAL_NEG:", 
      "+\t\td_instantiate(dentry, inode);", 
      "+\t\tbreak;", 
      "+\tcase INTERPOSE_LOOKUP:", 
      "+\t\tspliced = d_splice_alias(inode, dentry);", 
      "+\t\tif (spliced && spliced != dentry) {", 
      "+\t\t\t/*", 
      "+\t\t\t * d_splice can return a dentry if it was", 
      "+\t\t\t * disconnected and had to be moved.  We must ensure", 
      "+\t\t\t * that the private data of the new dentry is", 
      "+\t\t\t * correct and that the inode info was filled", 
      "+\t\t\t * properly.  Finally we must return this new", 
      "+\t\t\t * dentry.", 
      "+\t\t\t */", 
      "+\t\t\tspliced->d_op = &unionfs_dops;", 
      "+\t\t\tspliced->d_fsdata = dentry->d_fsdata;", 
      "+\t\t\tdentry->d_fsdata = NULL;", 
      "+\t\t\tdentry = spliced;", 
      "+\t\t\tif (need_fill_inode) {", 
      "+\t\t\t\tneed_fill_inode = 0;", 
      "+\t\t\t\tunionfs_fill_inode(dentry, inode);", 
      "+\t\t\t}", 
      "+\t\t\tgoto out_spliced;", 
      "+\t\t} else if (!spliced) {", 
      "+\t\t\tif (need_fill_inode) {", 
      "+\t\t\t\tneed_fill_inode = 0;", 
      "+\t\t\t\tunionfs_fill_inode(dentry, inode);", 
      "+\t\t\t\tgoto out_spliced;", 
      "+\t\t\t}", 
      "+\t\t}", 
      "+\t\tbreak;", 
      "+\tcase INTERPOSE_REVAL:", 
      "+\t\t/* Do nothing. */", 
      "+\t\tbreak;", 
      "+\tdefault:", 
      "+\t\tprintk(KERN_CRIT \"unionfs: invalid interpose flag passed!\\n\");", 
      "+\t\tBUG();", 
      "+\t}", 
      "+\tgoto out;", 
      "+", 
      "+out_spliced:", 
      "+\tif (!err)", 
      "+\t\treturn spliced;", 
      "+out:", 
      "+\treturn ERR_PTR(err);", 
      "+}", 
      "+", 
      "+/* like interpose above, but for an already existing dentry */", 
      "+void unionfs_reinterpose(struct dentry *dentry)", 
      "+{", 
      "+\tstruct dentry *lower_dentry;", 
      "+\tstruct inode *inode;", 
      "+\tint bindex, bstart, bend;", 
      "+", 
      "+\tverify_locked(dentry);", 
      "+", 
      "+\t/* This is pre-allocated inode */", 
      "+\tinode = dentry->d_inode;", 
      "+", 
      "+\tbstart = dbstart(dentry);", 
      "+\tbend = dbend(dentry);", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tlower_dentry = unionfs_lower_dentry_idx(dentry, bindex);", 
      "+\t\tif (!lower_dentry)", 
      "+\t\t\tcontinue;", 
      "+", 
      "+\t\tif (!lower_dentry->d_inode)", 
      "+\t\t\tcontinue;", 
      "+\t\tif (unionfs_lower_inode_idx(inode, bindex))", 
      "+\t\t\tcontinue;", 
      "+\t\tunionfs_set_lower_inode_idx(inode, bindex,", 
      "+\t\t\t\t\t    igrab(lower_dentry->d_inode));", 
      "+\t}", 
      "+\tibstart(inode) = dbstart(dentry);", 
      "+\tibend(inode) = dbend(dentry);", 
      "+}", 
      "+", 
      "+/*", 
      "+ * make sure the branch we just looked up (nd) makes sense:", 
      "+ *", 
      "+ * 1) we're not trying to stack unionfs on top of unionfs", 
      "+ * 2) it exists", 
      "+ * 3) is a directory", 
      "+ */", 
      "+int check_branch(struct nameidata *nd)", 
      "+{", 
      "+\t/* XXX: remove in ODF code -- stacking unions allowed there */", 
      "+\tif (!strcmp(nd->dentry->d_sb->s_type->name, UNIONFS_NAME))", 
      "+\t\treturn -EINVAL;", 
      "+\tif (!nd->dentry->d_inode)", 
      "+\t\treturn -ENOENT;", 
      "+\tif (!S_ISDIR(nd->dentry->d_inode->i_mode))", 
      "+\t\treturn -ENOTDIR;", 
      "+\treturn 0;", 
      "+}", 
      "+", 
      "+/* checks if two lower_dentries have overlapping branches */", 
      "+static int is_branch_overlap(struct dentry *dent1, struct dentry *dent2)", 
      "+{", 
      "+\tstruct dentry *dent = NULL;", 
      "+", 
      "+\tdent = dent1;", 
      "+\twhile ((dent != dent2) && (dent->d_parent != dent))", 
      "+\t\tdent = dent->d_parent;", 
      "+", 
      "+\tif (dent == dent2)", 
      "+\t\treturn 1;", 
      "+", 
      "+\tdent = dent2;", 
      "+\twhile ((dent != dent1) && (dent->d_parent != dent))", 
      "+\t\tdent = dent->d_parent;", 
      "+", 
      "+\treturn (dent == dent1);", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Parse \"ro\" or \"rw\" options, but default to \"rw\" if no mode options was", 
      "+ * specified.  Fill the mode bits in @perms.  If encounter an unknown", 
      "+ * string, return -EINVAL.  Otherwise return 0.", 
      "+ */", 
      "+int parse_branch_mode(const char *name, int *perms)", 
      "+{", 
      "+\tif (!name || !strcmp(name, \"rw\")) {", 
      "+\t\t*perms = MAY_READ | MAY_WRITE;", 
      "+\t\treturn 0;", 
      "+\t}", 
      "+\tif (!strcmp(name, \"ro\")) {", 
      "+\t\t*perms = MAY_READ;", 
      "+\t\treturn 0;", 
      "+\t}", 
      "+\treturn -EINVAL;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * parse the dirs= mount argument", 
      "+ *", 
      "+ * We don't need to lock the superblock private data's rwsem, as we get", 
      "+ * called only by unionfs_read_super - it is still a long time before anyone", 
      "+ * can even get a reference to us.", 
      "+ */", 
      "+static int parse_dirs_option(struct super_block *sb, struct unionfs_dentry_info", 
      "+\t\t\t     *lower_root_info, char *options)", 
      "+{", 
      "+\tstruct nameidata nd;", 
      "+\tchar *name;", 
      "+\tint err = 0;", 
      "+\tint branches = 1;", 
      "+\tint bindex = 0;", 
      "+\tint i = 0;", 
      "+\tint j = 0;", 
      "+\tstruct dentry *dent1;", 
      "+\tstruct dentry *dent2;", 
      "+", 
      "+\tif (options[0] == '\\0') {", 
      "+\t\tprintk(KERN_ERR \"unionfs: no branches specified\\n\");", 
      "+\t\terr = -EINVAL;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * Each colon means we have a separator, this is really just a rough", 
      "+\t * guess, since strsep will handle empty fields for us.", 
      "+\t */", 
      "+\tfor (i = 0; options[i]; i++)", 
      "+\t\tif (options[i] == ':')", 
      "+\t\t\tbranches++;", 
      "+", 
      "+\t/* allocate space for underlying pointers to lower dentry */", 
      "+\tUNIONFS_SB(sb)->data =", 
      "+\t\tkcalloc(branches, sizeof(struct unionfs_data), GFP_KERNEL);", 
      "+\tif (unlikely(!UNIONFS_SB(sb)->data)) {", 
      "+\t\terr = -ENOMEM;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tlower_root_info->lower_paths =", 
      "+\t\tkcalloc(branches, sizeof(struct path), GFP_KERNEL);", 
      "+\tif (unlikely(!lower_root_info->lower_paths)) {", 
      "+\t\terr = -ENOMEM;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/* now parsing a string such as \"b1:b2=rw:b3=ro:b4\" */", 
      "+\tbranches = 0;", 
      "+\twhile ((name = strsep(&options, \":\")) != NULL) {", 
      "+\t\tint perms;", 
      "+\t\tchar *mode = strchr(name, '=');", 
      "+", 
      "+\t\tif (!name)", 
      "+\t\t\tcontinue;", 
      "+\t\tif (!*name) {\t/* bad use of ':' (extra colons) */", 
      "+\t\t\terr = -EINVAL;", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+", 
      "+\t\tbranches++;", 
      "+", 
      "+\t\t/* strip off '=' if any */", 
      "+\t\tif (mode)", 
      "+\t\t\t*mode++ = '\\0';", 
      "+", 
      "+\t\terr = parse_branch_mode(mode, &perms);", 
      "+\t\tif (err) {", 
      "+\t\t\tprintk(KERN_ERR \"unionfs: invalid mode \\\"%s\\\" for \"", 
      "+\t\t\t       \"branch %d\\n\", mode, bindex);", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+\t\t/* ensure that leftmost branch is writeable */", 
      "+\t\tif (!bindex && !(perms & MAY_WRITE)) {", 
      "+\t\t\tprintk(KERN_ERR \"unionfs: leftmost branch cannot be \"", 
      "+\t\t\t       \"read-only (use \\\"-o ro\\\" to create a \"", 
      "+\t\t\t       \"read-only union)\\n\");", 
      "+\t\t\terr = -EINVAL;", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+", 
      "+\t\terr = path_lookup(name, LOOKUP_FOLLOW, &nd);", 
      "+\t\tif (err) {", 
      "+\t\t\tprintk(KERN_ERR \"unionfs: error accessing \"", 
      "+\t\t\t       \"lower directory '%s' (error %d)\\n\",", 
      "+\t\t\t       name, err);", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+", 
      "+\t\terr = check_branch(&nd);", 
      "+\t\tif (err) {", 
      "+\t\t\tprintk(KERN_ERR \"unionfs: lower directory \"", 
      "+\t\t\t       \"'%s' is not a valid branch\\n\", name);", 
      "+\t\t\tpath_release(&nd);", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+", 
      "+\t\tlower_root_info->lower_paths[bindex].dentry = nd.dentry;", 
      "+\t\tlower_root_info->lower_paths[bindex].mnt = nd.mnt;", 
      "+", 
      "+\t\tset_branchperms(sb, bindex, perms);", 
      "+\t\tset_branch_count(sb, bindex, 0);", 
      "+\t\tnew_branch_id(sb, bindex);", 
      "+", 
      "+\t\tif (lower_root_info->bstart < 0)", 
      "+\t\t\tlower_root_info->bstart = bindex;", 
      "+\t\tlower_root_info->bend = bindex;", 
      "+\t\tbindex++;", 
      "+\t}", 
      "+", 
      "+\tif (branches == 0) {", 
      "+\t\tprintk(KERN_ERR \"unionfs: no branches specified\\n\");", 
      "+\t\terr = -EINVAL;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tBUG_ON(branches != (lower_root_info->bend + 1));", 
      "+", 
      "+\t/*", 
      "+\t * Ensure that no overlaps exist in the branches.", 
      "+\t *", 
      "+\t * This test is required because the Linux kernel has no support", 
      "+\t * currently for ensuring coherency between stackable layers and", 
      "+\t * branches.  If we were to allow overlapping branches, it would be", 
      "+\t * possible, for example, to delete a file via one branch, which", 
      "+\t * would not be reflected in another branch.  Such incoherency could", 
      "+\t * lead to inconsistencies and even kernel oopses.  Rather than", 
      "+\t * implement hacks to work around some of these cache-coherency", 
      "+\t * problems, we prevent branch overlapping, for now.  A complete", 
      "+\t * solution will involve proper kernel/VFS support for cache", 
      "+\t * coherency, at which time we could safely remove this", 
      "+\t * branch-overlapping test.", 
      "+\t */", 
      "+\tfor (i = 0; i < branches; i++) {", 
      "+\t\tdent1 = lower_root_info->lower_paths[i].dentry;", 
      "+\t\tfor (j = i + 1; j < branches; j++) {", 
      "+\t\t\tdent2 = lower_root_info->lower_paths[j].dentry;", 
      "+\t\t\tif (is_branch_overlap(dent1, dent2)) {", 
      "+\t\t\t\tprintk(KERN_ERR \"unionfs: branches %d and \"", 
      "+\t\t\t\t       \"%d overlap\\n\", i, j);", 
      "+\t\t\t\terr = -EINVAL;", 
      "+\t\t\t\tgoto out;", 
      "+\t\t\t}", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\tif (err) {", 
      "+\t\tfor (i = 0; i < branches; i++)", 
      "+\t\t\tif (lower_root_info->lower_paths[i].dentry) {", 
      "+\t\t\t\tdput(lower_root_info->lower_paths[i].dentry);", 
      "+\t\t\t\t/* initialize: can't use unionfs_mntput here */", 
      "+\t\t\t\tmntput(lower_root_info->lower_paths[i].mnt);", 
      "+\t\t\t}", 
      "+", 
      "+\t\tkfree(lower_root_info->lower_paths);", 
      "+\t\tkfree(UNIONFS_SB(sb)->data);", 
      "+", 
      "+\t\t/*", 
      "+\t\t * MUST clear the pointers to prevent potential double free if", 
      "+\t\t * the caller dies later on", 
      "+\t\t */", 
      "+\t\tlower_root_info->lower_paths = NULL;", 
      "+\t\tUNIONFS_SB(sb)->data = NULL;", 
      "+\t}", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Parse mount options.  See the manual page for usage instructions.", 
      "+ *", 
      "+ * Returns the dentry object of the lower-level (lower) directory;", 
      "+ * We want to mount our stackable file system on top of that lower directory.", 
      "+ */", 
      "+static struct unionfs_dentry_info *unionfs_parse_options(", 
      "+\t\t\t\t\t struct super_block *sb,", 
      "+\t\t\t\t\t char *options)", 
      "+{", 
      "+\tstruct unionfs_dentry_info *lower_root_info;", 
      "+\tchar *optname;", 
      "+\tint err = 0;", 
      "+\tint bindex;", 
      "+\tint dirsfound = 0;", 
      "+", 
      "+\t/* allocate private data area */", 
      "+\terr = -ENOMEM;", 
      "+\tlower_root_info =", 
      "+\t\tkzalloc(sizeof(struct unionfs_dentry_info), GFP_KERNEL);", 
      "+\tif (unlikely(!lower_root_info))", 
      "+\t\tgoto out_error;", 
      "+\tlower_root_info->bstart = -1;", 
      "+\tlower_root_info->bend = -1;", 
      "+\tlower_root_info->bopaque = -1;", 
      "+", 
      "+\twhile ((optname = strsep(&options, \",\")) != NULL) {", 
      "+\t\tchar *optarg;", 
      "+", 
      "+\t\tif (!optname || !*optname)", 
      "+\t\t\tcontinue;", 
      "+", 
      "+\t\toptarg = strchr(optname, '=');", 
      "+\t\tif (optarg)", 
      "+\t\t\t*optarg++ = '\\0';", 
      "+", 
      "+\t\t/*", 
      "+\t\t * All of our options take an argument now. Insert ones that", 
      "+\t\t * don't, above this check.", 
      "+\t\t */", 
      "+\t\tif (!optarg) {", 
      "+\t\t\tprintk(KERN_ERR \"unionfs: %s requires an argument\\n\",", 
      "+\t\t\t       optname);", 
      "+\t\t\terr = -EINVAL;", 
      "+\t\t\tgoto out_error;", 
      "+\t\t}", 
      "+", 
      "+\t\tif (!strcmp(\"dirs\", optname)) {", 
      "+\t\t\tif (++dirsfound > 1) {", 
      "+\t\t\t\tprintk(KERN_ERR", 
      "+\t\t\t\t       \"unionfs: multiple dirs specified\\n\");", 
      "+\t\t\t\terr = -EINVAL;", 
      "+\t\t\t\tgoto out_error;", 
      "+\t\t\t}", 
      "+\t\t\terr = parse_dirs_option(sb, lower_root_info, optarg);", 
      "+\t\t\tif (err)", 
      "+\t\t\t\tgoto out_error;", 
      "+\t\t\tcontinue;", 
      "+\t\t}", 
      "+", 
      "+\t\terr = -EINVAL;", 
      "+\t\tprintk(KERN_ERR", 
      "+\t\t       \"unionfs: unrecognized option '%s'\\n\", optname);", 
      "+\t\tgoto out_error;", 
      "+\t}", 
      "+\tif (dirsfound != 1) {", 
      "+\t\tprintk(KERN_ERR \"unionfs: dirs option required\\n\");", 
      "+\t\terr = -EINVAL;", 
      "+\t\tgoto out_error;", 
      "+\t}", 
      "+\tgoto out;", 
      "+", 
      "+out_error:", 
      "+\tif (lower_root_info && lower_root_info->lower_paths) {", 
      "+\t\tfor (bindex = lower_root_info->bstart;", 
      "+\t\t     bindex >= 0 && bindex <= lower_root_info->bend;", 
      "+\t\t     bindex++) {", 
      "+\t\t\tstruct dentry *d;", 
      "+\t\t\tstruct vfsmount *m;", 
      "+", 
      "+\t\t\td = lower_root_info->lower_paths[bindex].dentry;", 
      "+\t\t\tm = lower_root_info->lower_paths[bindex].mnt;", 
      "+", 
      "+\t\t\tdput(d);", 
      "+\t\t\t/* initializing: can't use unionfs_mntput here */", 
      "+\t\t\tmntput(m);", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\tkfree(lower_root_info->lower_paths);", 
      "+\tkfree(lower_root_info);", 
      "+", 
      "+\tkfree(UNIONFS_SB(sb)->data);", 
      "+\tUNIONFS_SB(sb)->data = NULL;", 
      "+", 
      "+\tlower_root_info = ERR_PTR(err);", 
      "+out:", 
      "+\treturn lower_root_info;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * our custom d_alloc_root work-alike", 
      "+ *", 
      "+ * we can't use d_alloc_root if we want to use our own interpose function", 
      "+ * unchanged, so we simply call our own \"fake\" d_alloc_root", 
      "+ */", 
      "+static struct dentry *unionfs_d_alloc_root(struct super_block *sb)", 
      "+{", 
      "+\tstruct dentry *ret = NULL;", 
      "+", 
      "+\tif (sb) {", 
      "+\t\tstatic const struct qstr name = {", 
      "+\t\t\t.name = \"/\",", 
      "+\t\t\t.len = 1", 
      "+\t\t};", 
      "+", 
      "+\t\tret = d_alloc(NULL, &name);", 
      "+\t\tif (likely(ret)) {", 
      "+\t\t\tret->d_op = &unionfs_dops;", 
      "+\t\t\tret->d_sb = sb;", 
      "+\t\t\tret->d_parent = ret;", 
      "+\t\t}", 
      "+\t}", 
      "+\treturn ret;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * There is no need to lock the unionfs_super_info's rwsem as there is no", 
      "+ * way anyone can have a reference to the superblock at this point in time.", 
      "+ */", 
      "+static int unionfs_read_super(struct super_block *sb, void *raw_data,", 
      "+\t\t\t      int silent)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct unionfs_dentry_info *lower_root_info = NULL;", 
      "+\tint bindex, bstart, bend;", 
      "+", 
      "+\tif (!raw_data) {", 
      "+\t\tprintk(KERN_ERR", 
      "+\t\t       \"unionfs: read_super: missing data argument\\n\");", 
      "+\t\terr = -EINVAL;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/* Allocate superblock private data */", 
      "+\tsb->s_fs_info = kzalloc(sizeof(struct unionfs_sb_info), GFP_KERNEL);", 
      "+\tif (unlikely(!UNIONFS_SB(sb))) {", 
      "+\t\tprintk(KERN_CRIT \"unionfs: read_super: out of memory\\n\");", 
      "+\t\terr = -ENOMEM;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tUNIONFS_SB(sb)->bend = -1;", 
      "+\tatomic_set(&UNIONFS_SB(sb)->generation, 1);", 
      "+\tinit_rwsem(&UNIONFS_SB(sb)->rwsem);", 
      "+\tUNIONFS_SB(sb)->high_branch_id = -1; /* -1 == invalid branch ID */", 
      "+", 
      "+\tlower_root_info = unionfs_parse_options(sb, raw_data);", 
      "+\tif (IS_ERR(lower_root_info)) {", 
      "+\t\tprintk(KERN_ERR", 
      "+\t\t       \"unionfs: read_super: error while parsing options \"", 
      "+\t\t       \"(err = %ld)\\n\", PTR_ERR(lower_root_info));", 
      "+\t\terr = PTR_ERR(lower_root_info);", 
      "+\t\tlower_root_info = NULL;", 
      "+\t\tgoto out_free;", 
      "+\t}", 
      "+\tif (lower_root_info->bstart == -1) {", 
      "+\t\terr = -ENOENT;", 
      "+\t\tgoto out_free;", 
      "+\t}", 
      "+", 
      "+\t/* set the lower superblock field of upper superblock */", 
      "+\tbstart = lower_root_info->bstart;", 
      "+\tBUG_ON(bstart != 0);", 
      "+\tsbend(sb) = bend = lower_root_info->bend;", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tstruct dentry *d = lower_root_info->lower_paths[bindex].dentry;", 
      "+\t\tatomic_inc(&d->d_sb->s_active);", 
      "+\t\tunionfs_set_lower_super_idx(sb, bindex, d->d_sb);", 
      "+\t}", 
      "+", 
      "+\t/* max Bytes is the maximum bytes from highest priority branch */", 
      "+\tsb->s_maxbytes = unionfs_lower_super_idx(sb, 0)->s_maxbytes;", 
      "+", 
      "+\t/*", 
      "+\t * Our c/m/atime granularity is 1 ns because we may stack on file", 
      "+\t * systems whose granularity is as good.  This is important for our", 
      "+\t * time-based cache coherency.", 
      "+\t */", 
      "+\tsb->s_time_gran = 1;", 
      "+", 
      "+\tsb->s_op = &unionfs_sops;", 
      "+", 
      "+\t/* See comment next to the definition of unionfs_d_alloc_root */", 
      "+\tsb->s_root = unionfs_d_alloc_root(sb);", 
      "+\tif (unlikely(!sb->s_root)) {", 
      "+\t\terr = -ENOMEM;", 
      "+\t\tgoto out_dput;", 
      "+\t}", 
      "+", 
      "+\t/* link the upper and lower dentries */", 
      "+\tsb->s_root->d_fsdata = NULL;", 
      "+\terr = new_dentry_private_data(sb->s_root, UNIONFS_DMUTEX_ROOT);", 
      "+\tif (unlikely(err))", 
      "+\t\tgoto out_freedpd;", 
      "+", 
      "+\t/* Set the lower dentries for s_root */", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tstruct dentry *d;", 
      "+\t\tstruct vfsmount *m;", 
      "+", 
      "+\t\td = lower_root_info->lower_paths[bindex].dentry;", 
      "+\t\tm = lower_root_info->lower_paths[bindex].mnt;", 
      "+", 
      "+\t\tunionfs_set_lower_dentry_idx(sb->s_root, bindex, d);", 
      "+\t\tunionfs_set_lower_mnt_idx(sb->s_root, bindex, m);", 
      "+\t}", 
      "+\tdbstart(sb->s_root) = bstart;", 
      "+\tdbend(sb->s_root) = bend;", 
      "+", 
      "+\t/* Set the generation number to one, since this is for the mount. */", 
      "+\tatomic_set(&UNIONFS_D(sb->s_root)->generation, 1);", 
      "+", 
      "+\t/*", 
      "+\t * Call interpose to create the upper level inode.  Only", 
      "+\t * INTERPOSE_LOOKUP can return a value other than 0 on err.", 
      "+\t */", 
      "+\terr = PTR_ERR(unionfs_interpose(sb->s_root, sb, 0));", 
      "+\tunionfs_unlock_dentry(sb->s_root);", 
      "+\tif (!err)", 
      "+\t\tgoto out;", 
      "+\t/* else fall through */", 
      "+", 
      "+out_freedpd:", 
      "+\tif (UNIONFS_D(sb->s_root)) {", 
      "+\t\tkfree(UNIONFS_D(sb->s_root)->lower_paths);", 
      "+\t\tfree_dentry_private_data(sb->s_root);", 
      "+\t}", 
      "+\tdput(sb->s_root);", 
      "+", 
      "+out_dput:", 
      "+\tif (lower_root_info && !IS_ERR(lower_root_info)) {", 
      "+\t\tfor (bindex = lower_root_info->bstart;", 
      "+\t\t     bindex <= lower_root_info->bend; bindex++) {", 
      "+\t\t\tstruct dentry *d;", 
      "+\t\t\tstruct vfsmount *m;", 
      "+", 
      "+\t\t\td = lower_root_info->lower_paths[bindex].dentry;", 
      "+\t\t\tm = lower_root_info->lower_paths[bindex].mnt;", 
      "+", 
      "+\t\t\tdput(d);", 
      "+\t\t\t/* initializing: can't use unionfs_mntput here */", 
      "+\t\t\tmntput(m);", 
      "+\t\t\t/* drop refs we took earlier */", 
      "+\t\t\tatomic_dec(&d->d_sb->s_active);", 
      "+\t\t}", 
      "+\t\tkfree(lower_root_info->lower_paths);", 
      "+\t\tkfree(lower_root_info);", 
      "+\t\tlower_root_info = NULL;", 
      "+\t}", 
      "+", 
      "+out_free:", 
      "+\tkfree(UNIONFS_SB(sb)->data);", 
      "+\tkfree(UNIONFS_SB(sb));", 
      "+\tsb->s_fs_info = NULL;", 
      "+", 
      "+out:", 
      "+\tif (lower_root_info && !IS_ERR(lower_root_info)) {", 
      "+\t\tkfree(lower_root_info->lower_paths);", 
      "+\t\tkfree(lower_root_info);", 
      "+\t}", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+static int unionfs_get_sb(struct file_system_type *fs_type,", 
      "+\t\t\t  int flags, const char *dev_name,", 
      "+\t\t\t  void *raw_data, struct vfsmount *mnt)", 
      "+{", 
      "+\tint err;", 
      "+\terr = get_sb_nodev(fs_type, flags, raw_data, unionfs_read_super, mnt);", 
      "+\tif (!err)", 
      "+\t\tUNIONFS_SB(mnt->mnt_sb)->dev_name =", 
      "+\t\t\tkstrdup(dev_name, GFP_KERNEL);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+static struct file_system_type unionfs_fs_type = {", 
      "+\t.owner\t\t= THIS_MODULE,", 
      "+\t.name\t\t= UNIONFS_NAME,", 
      "+\t.get_sb\t\t= unionfs_get_sb,", 
      "+\t.kill_sb\t= generic_shutdown_super,", 
      "+\t.fs_flags\t= FS_REVAL_DOT,", 
      "+};", 
      "+", 
      "+static int __init init_unionfs_fs(void)", 
      "+{", 
      "+\tint err;", 
      "+", 
      "+\tpr_info(\"Registering unionfs \" UNIONFS_VERSION \"\\n\");", 
      "+", 
      "+\terr = unionfs_init_filldir_cache();", 
      "+\tif (unlikely(err))", 
      "+\t\tgoto out;", 
      "+\terr = unionfs_init_inode_cache();", 
      "+\tif (unlikely(err))", 
      "+\t\tgoto out;", 
      "+\terr = unionfs_init_dentry_cache();", 
      "+\tif (unlikely(err))", 
      "+\t\tgoto out;", 
      "+\terr = init_sioq();", 
      "+\tif (unlikely(err))", 
      "+\t\tgoto out;", 
      "+\terr = register_filesystem(&unionfs_fs_type);", 
      "+out:", 
      "+\tif (unlikely(err)) {", 
      "+\t\tstop_sioq();", 
      "+\t\tunionfs_destroy_filldir_cache();", 
      "+\t\tunionfs_destroy_inode_cache();", 
      "+\t\tunionfs_destroy_dentry_cache();", 
      "+\t}", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+static void __exit exit_unionfs_fs(void)", 
      "+{", 
      "+\tstop_sioq();", 
      "+\tunionfs_destroy_filldir_cache();", 
      "+\tunionfs_destroy_inode_cache();", 
      "+\tunionfs_destroy_dentry_cache();", 
      "+\tunregister_filesystem(&unionfs_fs_type);", 
      "+\tpr_info(\"Completed unionfs module unload\\n\");", 
      "+}", 
      "+", 
      "+MODULE_AUTHOR(\"Erez Zadok, Filesystems and Storage Lab, Stony Brook University\"", 
      "+\t      \" (http://www.fsl.cs.sunysb.edu)\");", 
      "+MODULE_DESCRIPTION(\"Unionfs \" UNIONFS_VERSION", 
      "+\t\t   \" (http://unionfs.filesystems.org)\");", 
      "+MODULE_LICENSE(\"GPL\");", 
      "+", 
      "+module_init(init_unionfs_fs);", 
      "+module_exit(exit_unionfs_fs);"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/mmap.c", 
    [
      "diff --git a/fs/unionfs/mmap.c b/fs/unionfs/mmap.c", 
      "new file mode 100644", 
      "index 0000000..18b05d5", 
      "--- dev/null", 
      "+++ b/fs/unionfs/mmap.c", 
      "@@ -0,0 +1,89 @@", 
      "+/*", 
      "+ * Copyright (c) 2003-2009 Erez Zadok", 
      "+ * Copyright (c) 2003-2006 Charles P. Wright", 
      "+ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek", 
      "+ * Copyright (c) 2005-2006 Junjiro Okajima", 
      "+ * Copyright (c) 2006      Shaya Potter", 
      "+ * 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-2009 Stony Brook University", 
      "+ * Copyright (c) 2003-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#include \"union.h\"", 
      "+", 
      "+", 
      "+/*", 
      "+ * XXX: we need a dummy readpage handler because generic_file_mmap (which we", 
      "+ * use in unionfs_mmap) checks for the existence of", 
      "+ * mapping->a_ops->readpage, else it returns -ENOEXEC.  The VFS will need to", 
      "+ * be fixed to allow a file system to define vm_ops->fault without any", 
      "+ * address_space_ops whatsoever.", 
      "+ *", 
      "+ * Otherwise, we don't want to use our readpage method at all.", 
      "+ */", 
      "+static int unionfs_readpage(struct file *file, struct page *page)", 
      "+{", 
      "+\tBUG();", 
      "+\treturn -EINVAL;", 
      "+}", 
      "+", 
      "+static int unionfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf)", 
      "+{", 
      "+\tint err;", 
      "+\tstruct file *file, *lower_file;", 
      "+\tstruct vm_operations_struct *lower_vm_ops;", 
      "+\tstruct vm_area_struct lower_vma;", 
      "+", 
      "+\tBUG_ON(!vma);", 
      "+\tmemcpy(&lower_vma, vma, sizeof(struct vm_area_struct));", 
      "+\tfile = lower_vma.vm_file;", 
      "+\tlower_vm_ops = UNIONFS_F(file)->lower_vm_ops;", 
      "+\tBUG_ON(!lower_vm_ops);", 
      "+", 
      "+\tlower_file = unionfs_lower_file(file);", 
      "+\tBUG_ON(!lower_file);", 
      "+\t/*", 
      "+\t * XXX: vm_ops->fault may be called in parallel.  Because we have to", 
      "+\t * resort to temporarily changing the vma->vm_file to point to the", 
      "+\t * lower file, a concurrent invocation of unionfs_fault could see a", 
      "+\t * different value.  In this workaround, we keep a different copy of", 
      "+\t * the vma structure in our stack, so we never expose a different", 
      "+\t * value of the vma->vm_file called to us, even temporarily.  A", 
      "+\t * better fix would be to change the calling semantics of ->fault to", 
      "+\t * take an explicit file pointer.", 
      "+\t */", 
      "+\tlower_vma.vm_file = lower_file;", 
      "+\terr = lower_vm_ops->fault(&lower_vma, vmf);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * XXX: the default address_space_ops for unionfs is empty.  We cannot set", 
      "+ * our inode->i_mapping->a_ops to NULL because too many code paths expect", 
      "+ * the a_ops vector to be non-NULL.", 
      "+ */", 
      "+struct address_space_operations unionfs_aops = {", 
      "+\t/* empty on purpose */", 
      "+};", 
      "+", 
      "+/*", 
      "+ * XXX: we need a second, dummy address_space_ops vector, to be used", 
      "+ * temporarily during unionfs_mmap, because the latter calls", 
      "+ * generic_file_mmap, which checks if ->readpage exists, else returns", 
      "+ * -ENOEXEC.", 
      "+ */", 
      "+struct address_space_operations unionfs_dummy_aops = {", 
      "+\t.readpage\t= unionfs_readpage,", 
      "+};", 
      "+", 
      "+struct vm_operations_struct unionfs_vm_ops = {", 
      "+\t.fault\t\t= unionfs_fault,", 
      "+};"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/rdstate.c", 
    [
      "diff --git a/fs/unionfs/rdstate.c b/fs/unionfs/rdstate.c", 
      "new file mode 100644", 
      "index 0000000..485464b", 
      "--- dev/null", 
      "+++ b/fs/unionfs/rdstate.c", 
      "@@ -0,0 +1,285 @@", 
      "+/*", 
      "+ * Copyright (c) 2003-2009 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-2009 Stony Brook University", 
      "+ * Copyright (c) 2003-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#include \"union.h\"", 
      "+", 
      "+/* This file contains the routines for maintaining readdir state. */", 
      "+", 
      "+/*", 
      "+ * There are two structures here, rdstate which is a hash table", 
      "+ * of the second structure which is a filldir_node.", 
      "+ */", 
      "+", 
      "+/*", 
      "+ * This is a struct kmem_cache for filldir nodes, because we allocate a lot", 
      "+ * of them and they shouldn't waste memory.  If the node has a small name", 
      "+ * (as defined by the dentry structure), then we use an inline name to", 
      "+ * preserve kmalloc space.", 
      "+ */", 
      "+static struct kmem_cache *unionfs_filldir_cachep;", 
      "+", 
      "+int unionfs_init_filldir_cache(void)", 
      "+{", 
      "+\tunionfs_filldir_cachep =", 
      "+\t\tkmem_cache_create(\"unionfs_filldir\",", 
      "+\t\t\t\t  sizeof(struct filldir_node), 0,", 
      "+\t\t\t\t  SLAB_RECLAIM_ACCOUNT, NULL);", 
      "+", 
      "+\treturn (unionfs_filldir_cachep ? 0 : -ENOMEM);", 
      "+}", 
      "+", 
      "+void unionfs_destroy_filldir_cache(void)", 
      "+{", 
      "+\tif (unionfs_filldir_cachep)", 
      "+\t\tkmem_cache_destroy(unionfs_filldir_cachep);", 
      "+}", 
      "+", 
      "+/*", 
      "+ * This is a tuning parameter that tells us roughly how big to make the", 
      "+ * hash table in directory entries per page.  This isn't perfect, but", 
      "+ * at least we get a hash table size that shouldn't be too overloaded.", 
      "+ * The following averages are based on my home directory.", 
      "+ * 14.44693\tOverall", 
      "+ * 12.29\tSingle Page Directories", 
      "+ * 117.93\tMulti-page directories", 
      "+ */", 
      "+#define DENTPAGE 4096", 
      "+#define DENTPERONEPAGE 12", 
      "+#define DENTPERPAGE 118", 
      "+#define MINHASHSIZE 1", 
      "+static int guesstimate_hash_size(struct inode *inode)", 
      "+{", 
      "+\tstruct inode *lower_inode;", 
      "+\tint bindex;", 
      "+\tint hashsize = MINHASHSIZE;", 
      "+", 
      "+\tif (UNIONFS_I(inode)->hashsize > 0)", 
      "+\t\treturn UNIONFS_I(inode)->hashsize;", 
      "+", 
      "+\tfor (bindex = ibstart(inode); bindex <= ibend(inode); bindex++) {", 
      "+\t\tlower_inode = unionfs_lower_inode_idx(inode, bindex);", 
      "+\t\tif (!lower_inode)", 
      "+\t\t\tcontinue;", 
      "+", 
      "+\t\tif (i_size_read(lower_inode) == DENTPAGE)", 
      "+\t\t\thashsize += DENTPERONEPAGE;", 
      "+\t\telse", 
      "+\t\t\thashsize += (i_size_read(lower_inode) / DENTPAGE) *", 
      "+\t\t\t\tDENTPERPAGE;", 
      "+\t}", 
      "+", 
      "+\treturn hashsize;", 
      "+}", 
      "+", 
      "+int init_rdstate(struct file *file)", 
      "+{", 
      "+\tBUG_ON(sizeof(loff_t) !=", 
      "+\t       (sizeof(unsigned int) + sizeof(unsigned int)));", 
      "+\tBUG_ON(UNIONFS_F(file)->rdstate != NULL);", 
      "+", 
      "+\tUNIONFS_F(file)->rdstate = alloc_rdstate(file->f_path.dentry->d_inode,", 
      "+\t\t\t\t\t\t fbstart(file));", 
      "+", 
      "+\treturn (UNIONFS_F(file)->rdstate ? 0 : -ENOMEM);", 
      "+}", 
      "+", 
      "+struct unionfs_dir_state *find_rdstate(struct inode *inode, loff_t fpos)", 
      "+{", 
      "+\tstruct unionfs_dir_state *rdstate = NULL;", 
      "+\tstruct list_head *pos;", 
      "+", 
      "+\tspin_lock(&UNIONFS_I(inode)->rdlock);", 
      "+\tlist_for_each(pos, &UNIONFS_I(inode)->readdircache) {", 
      "+\t\tstruct unionfs_dir_state *r =", 
      "+\t\t\tlist_entry(pos, struct unionfs_dir_state, cache);", 
      "+\t\tif (fpos == rdstate2offset(r)) {", 
      "+\t\t\tUNIONFS_I(inode)->rdcount--;", 
      "+\t\t\tlist_del(&r->cache);", 
      "+\t\t\trdstate = r;", 
      "+\t\t\tbreak;", 
      "+\t\t}", 
      "+\t}", 
      "+\tspin_unlock(&UNIONFS_I(inode)->rdlock);", 
      "+\treturn rdstate;", 
      "+}", 
      "+", 
      "+struct unionfs_dir_state *alloc_rdstate(struct inode *inode, int bindex)", 
      "+{", 
      "+\tint i = 0;", 
      "+\tint hashsize;", 
      "+\tunsigned long mallocsize = sizeof(struct unionfs_dir_state);", 
      "+\tstruct unionfs_dir_state *rdstate;", 
      "+", 
      "+\thashsize = guesstimate_hash_size(inode);", 
      "+\tmallocsize += hashsize * sizeof(struct list_head);", 
      "+\tmallocsize = __roundup_pow_of_two(mallocsize);", 
      "+", 
      "+\t/* This should give us about 500 entries anyway. */", 
      "+\tif (mallocsize > PAGE_SIZE)", 
      "+\t\tmallocsize = PAGE_SIZE;", 
      "+", 
      "+\thashsize = (mallocsize - sizeof(struct unionfs_dir_state)) /", 
      "+\t\tsizeof(struct list_head);", 
      "+", 
      "+\trdstate = kmalloc(mallocsize, GFP_KERNEL);", 
      "+\tif (unlikely(!rdstate))", 
      "+\t\treturn NULL;", 
      "+", 
      "+\tspin_lock(&UNIONFS_I(inode)->rdlock);", 
      "+\tif (UNIONFS_I(inode)->cookie >= (MAXRDCOOKIE - 1))", 
      "+\t\tUNIONFS_I(inode)->cookie = 1;", 
      "+\telse", 
      "+\t\tUNIONFS_I(inode)->cookie++;", 
      "+", 
      "+\trdstate->cookie = UNIONFS_I(inode)->cookie;", 
      "+\tspin_unlock(&UNIONFS_I(inode)->rdlock);", 
      "+\trdstate->offset = 1;", 
      "+\trdstate->access = jiffies;", 
      "+\trdstate->bindex = bindex;", 
      "+\trdstate->dirpos = 0;", 
      "+\trdstate->hashentries = 0;", 
      "+\trdstate->size = hashsize;", 
      "+\tfor (i = 0; i < rdstate->size; i++)", 
      "+\t\tINIT_LIST_HEAD(&rdstate->list[i]);", 
      "+", 
      "+\treturn rdstate;", 
      "+}", 
      "+", 
      "+static void free_filldir_node(struct filldir_node *node)", 
      "+{", 
      "+\tif (node->namelen >= DNAME_INLINE_LEN_MIN)", 
      "+\t\tkfree(node->name);", 
      "+\tkmem_cache_free(unionfs_filldir_cachep, node);", 
      "+}", 
      "+", 
      "+void free_rdstate(struct unionfs_dir_state *state)", 
      "+{", 
      "+\tstruct filldir_node *tmp;", 
      "+\tint i;", 
      "+", 
      "+\tfor (i = 0; i < state->size; i++) {", 
      "+\t\tstruct list_head *head = &(state->list[i]);", 
      "+\t\tstruct list_head *pos, *n;", 
      "+", 
      "+\t\t/* traverse the list and deallocate space */", 
      "+\t\tlist_for_each_safe(pos, n, head) {", 
      "+\t\t\ttmp = list_entry(pos, struct filldir_node, file_list);", 
      "+\t\t\tlist_del(&tmp->file_list);", 
      "+\t\t\tfree_filldir_node(tmp);", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\tkfree(state);", 
      "+}", 
      "+", 
      "+struct filldir_node *find_filldir_node(struct unionfs_dir_state *rdstate,", 
      "+\t\t\t\t       const char *name, int namelen,", 
      "+\t\t\t\t       int is_whiteout)", 
      "+{", 
      "+\tint index;", 
      "+\tunsigned int hash;", 
      "+\tstruct list_head *head;", 
      "+\tstruct list_head *pos;", 
      "+\tstruct filldir_node *cursor = NULL;", 
      "+\tint found = 0;", 
      "+", 
      "+\tBUG_ON(namelen <= 0);", 
      "+", 
      "+\thash = full_name_hash(name, namelen);", 
      "+\tindex = hash % rdstate->size;", 
      "+", 
      "+\thead = &(rdstate->list[index]);", 
      "+\tlist_for_each(pos, head) {", 
      "+\t\tcursor = list_entry(pos, struct filldir_node, file_list);", 
      "+", 
      "+\t\tif (cursor->namelen == namelen && cursor->hash == hash &&", 
      "+\t\t    !strncmp(cursor->name, name, namelen)) {", 
      "+\t\t\t/*", 
      "+\t\t\t * a duplicate exists, and hence no need to create", 
      "+\t\t\t * entry to the list", 
      "+\t\t\t */", 
      "+\t\t\tfound = 1;", 
      "+", 
      "+\t\t\t/*", 
      "+\t\t\t * if a duplicate is found in this branch, and is", 
      "+\t\t\t * not due to the caller looking for an entry to", 
      "+\t\t\t * whiteout, then the file system may be corrupted.", 
      "+\t\t\t */", 
      "+\t\t\tif (unlikely(!is_whiteout &&", 
      "+\t\t\t\t     cursor->bindex == rdstate->bindex))", 
      "+\t\t\t\tprintk(KERN_ERR \"unionfs: filldir: possible \"", 
      "+\t\t\t\t       \"I/O error: a file is duplicated \"", 
      "+\t\t\t\t       \"in the same branch %d: %s\\n\",", 
      "+\t\t\t\t       rdstate->bindex, cursor->name);", 
      "+\t\t\tbreak;", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\tif (!found)", 
      "+\t\tcursor = NULL;", 
      "+", 
      "+\treturn cursor;", 
      "+}", 
      "+", 
      "+int add_filldir_node(struct unionfs_dir_state *rdstate, const char *name,", 
      "+\t\t     int namelen, int bindex, int whiteout)", 
      "+{", 
      "+\tstruct filldir_node *new;", 
      "+\tunsigned int hash;", 
      "+\tint index;", 
      "+\tint err = 0;", 
      "+\tstruct list_head *head;", 
      "+", 
      "+\tBUG_ON(namelen <= 0);", 
      "+", 
      "+\thash = full_name_hash(name, namelen);", 
      "+\tindex = hash % rdstate->size;", 
      "+\thead = &(rdstate->list[index]);", 
      "+", 
      "+\tnew = kmem_cache_alloc(unionfs_filldir_cachep, GFP_KERNEL);", 
      "+\tif (unlikely(!new)) {", 
      "+\t\terr = -ENOMEM;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tINIT_LIST_HEAD(&new->file_list);", 
      "+\tnew->namelen = namelen;", 
      "+\tnew->hash = hash;", 
      "+\tnew->bindex = bindex;", 
      "+\tnew->whiteout = whiteout;", 
      "+", 
      "+\tif (namelen < DNAME_INLINE_LEN_MIN) {", 
      "+\t\tnew->name = new->iname;", 
      "+\t} else {", 
      "+\t\tnew->name = kmalloc(namelen + 1, GFP_KERNEL);", 
      "+\t\tif (unlikely(!new->name)) {", 
      "+\t\t\tkmem_cache_free(unionfs_filldir_cachep, new);", 
      "+\t\t\tnew = NULL;", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\tmemcpy(new->name, name, namelen);", 
      "+\tnew->name[namelen] = '\\0';", 
      "+", 
      "+\trdstate->hashentries++;", 
      "+", 
      "+\tlist_add(&(new->file_list), head);", 
      "+out:", 
      "+\treturn err;", 
      "+}"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/rename.c", 
    [
      "diff --git a/fs/unionfs/rename.c b/fs/unionfs/rename.c", 
      "new file mode 100644", 
      "index 0000000..ed13260", 
      "--- dev/null", 
      "+++ b/fs/unionfs/rename.c", 
      "@@ -0,0 +1,520 @@", 
      "+/*", 
      "+ * Copyright (c) 2003-2009 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-2009 Stony Brook University", 
      "+ * Copyright (c) 2003-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#include \"union.h\"", 
      "+", 
      "+/*", 
      "+ * This is a helper function for rename, used when rename ends up with hosed", 
      "+ * over dentries and we need to revert.", 
      "+ */", 
      "+static int unionfs_refresh_lower_dentry(struct dentry *dentry,", 
      "+\t\t\t\t\tstruct dentry *parent, int bindex)", 
      "+{", 
      "+\tstruct dentry *lower_dentry;", 
      "+\tstruct dentry *lower_parent;", 
      "+\tint err = 0;", 
      "+", 
      "+\tverify_locked(dentry);", 
      "+", 
      "+\tlower_parent = unionfs_lower_dentry_idx(parent, bindex);", 
      "+", 
      "+\tBUG_ON(!S_ISDIR(lower_parent->d_inode->i_mode));", 
      "+", 
      "+\tlower_dentry = lookup_one_len(dentry->d_name.name, lower_parent,", 
      "+\t\t\t\t      dentry->d_name.len);", 
      "+\tif (IS_ERR(lower_dentry)) {", 
      "+\t\terr = PTR_ERR(lower_dentry);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tdput(unionfs_lower_dentry_idx(dentry, bindex));", 
      "+\tiput(unionfs_lower_inode_idx(dentry->d_inode, bindex));", 
      "+\tunionfs_set_lower_inode_idx(dentry->d_inode, bindex, NULL);", 
      "+", 
      "+\tif (!lower_dentry->d_inode) {", 
      "+\t\tdput(lower_dentry);", 
      "+\t\tunionfs_set_lower_dentry_idx(dentry, bindex, NULL);", 
      "+\t} else {", 
      "+\t\tunionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry);", 
      "+\t\tunionfs_set_lower_inode_idx(dentry->d_inode, bindex,", 
      "+\t\t\t\t\t    igrab(lower_dentry->d_inode));", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+static int __unionfs_rename(struct inode *old_dir, struct dentry *old_dentry,", 
      "+\t\t\t    struct dentry *old_parent,", 
      "+\t\t\t    struct inode *new_dir, struct dentry *new_dentry,", 
      "+\t\t\t    struct dentry *new_parent,", 
      "+\t\t\t    int bindex)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct dentry *lower_old_dentry;", 
      "+\tstruct dentry *lower_new_dentry;", 
      "+\tstruct dentry *lower_old_dir_dentry;", 
      "+\tstruct dentry *lower_new_dir_dentry;", 
      "+\tstruct dentry *trap;", 
      "+", 
      "+\tlower_new_dentry = unionfs_lower_dentry_idx(new_dentry, bindex);", 
      "+\tlower_old_dentry = unionfs_lower_dentry_idx(old_dentry, bindex);", 
      "+", 
      "+\tif (!lower_new_dentry) {", 
      "+\t\tlower_new_dentry =", 
      "+\t\t\tcreate_parents(new_parent->d_inode,", 
      "+\t\t\t\t       new_dentry, new_dentry->d_name.name,", 
      "+\t\t\t\t       bindex);", 
      "+\t\tif (IS_ERR(lower_new_dentry)) {", 
      "+\t\t\terr = PTR_ERR(lower_new_dentry);", 
      "+\t\t\tif (IS_COPYUP_ERR(err))", 
      "+\t\t\t\tgoto out;", 
      "+\t\t\tprintk(KERN_ERR \"unionfs: error creating directory \"", 
      "+\t\t\t       \"tree for rename, bindex=%d err=%d\\n\",", 
      "+\t\t\t       bindex, err);", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\t/* check for and remove whiteout, if any */", 
      "+\terr = check_unlink_whiteout(new_dentry, lower_new_dentry, bindex);", 
      "+\tif (err > 0) /* ignore if whiteout found and successfully removed */", 
      "+\t\terr = 0;", 
      "+\tif (err)", 
      "+\t\tgoto out;", 
      "+", 
      "+\t/* check of old_dentry branch is writable */", 
      "+\terr = is_robranch_super(old_dentry->d_sb, bindex);", 
      "+\tif (err)", 
      "+\t\tgoto out;", 
      "+", 
      "+\tdget(lower_old_dentry);", 
      "+\tdget(lower_new_dentry);", 
      "+\tlower_old_dir_dentry = dget_parent(lower_old_dentry);", 
      "+\tlower_new_dir_dentry = dget_parent(lower_new_dentry);", 
      "+", 
      "+\t/* see Documentation/filesystems/unionfs/issues.txt */", 
      "+\tlockdep_off();", 
      "+\ttrap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);", 
      "+\t/* source should not be ancenstor of target */", 
      "+\tif (trap == lower_old_dentry) {", 
      "+\t\terr = -EINVAL;", 
      "+\t\tgoto out_err_unlock;", 
      "+\t}", 
      "+\t/* target should not be ancenstor of source */", 
      "+\tif (trap == lower_new_dentry) {", 
      "+\t\terr = -ENOTEMPTY;", 
      "+\t\tgoto out_err_unlock;", 
      "+\t}", 
      "+\terr = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry,", 
      "+\t\t\t lower_new_dir_dentry->d_inode, lower_new_dentry);", 
      "+out_err_unlock:", 
      "+\tif (!err) {", 
      "+\t\t/* update parent dir times */", 
      "+\t\tfsstack_copy_attr_times(old_dir, lower_old_dir_dentry->d_inode);", 
      "+\t\tfsstack_copy_attr_times(new_dir, lower_new_dir_dentry->d_inode);", 
      "+\t}", 
      "+\tunlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);", 
      "+\tlockdep_on();", 
      "+", 
      "+\tdput(lower_old_dir_dentry);", 
      "+\tdput(lower_new_dir_dentry);", 
      "+\tdput(lower_old_dentry);", 
      "+\tdput(lower_new_dentry);", 
      "+", 
      "+out:", 
      "+\tif (!err) {", 
      "+\t\t/* Fixup the new_dentry. */", 
      "+\t\tif (bindex < dbstart(new_dentry))", 
      "+\t\t\tdbstart(new_dentry) = bindex;", 
      "+\t\telse if (bindex > dbend(new_dentry))", 
      "+\t\t\tdbend(new_dentry) = bindex;", 
      "+\t}", 
      "+", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Main rename code.  This is sufficiently complex, that it's documented in", 
      "+ * Documentation/filesystems/unionfs/rename.txt.  This routine calls", 
      "+ * __unionfs_rename() above to perform some of the work.", 
      "+ */", 
      "+static int do_unionfs_rename(struct inode *old_dir,", 
      "+\t\t\t     struct dentry *old_dentry,", 
      "+\t\t\t     struct dentry *old_parent,", 
      "+\t\t\t     struct inode *new_dir,", 
      "+\t\t\t     struct dentry *new_dentry,", 
      "+\t\t\t     struct dentry *new_parent)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tint bindex;", 
      "+\tint old_bstart, old_bend;", 
      "+\tint new_bstart, new_bend;", 
      "+\tint do_copyup = -1;", 
      "+\tint local_err = 0;", 
      "+\tint eio = 0;", 
      "+\tint revert = 0;", 
      "+", 
      "+\told_bstart = dbstart(old_dentry);", 
      "+\told_bend = dbend(old_dentry);", 
      "+", 
      "+\tnew_bstart = dbstart(new_dentry);", 
      "+\tnew_bend = dbend(new_dentry);", 
      "+", 
      "+\t/* Rename source to destination. */", 
      "+\terr = __unionfs_rename(old_dir, old_dentry, old_parent,", 
      "+\t\t\t       new_dir, new_dentry, new_parent,", 
      "+\t\t\t       old_bstart);", 
      "+\tif (err) {", 
      "+\t\tif (!IS_COPYUP_ERR(err))", 
      "+\t\t\tgoto out;", 
      "+\t\tdo_copyup = old_bstart - 1;", 
      "+\t} else {", 
      "+\t\trevert = 1;", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * Unlink all instances of destination that exist to the left of", 
      "+\t * bstart of source. On error, revert back, goto out.", 
      "+\t */", 
      "+\tfor (bindex = old_bstart - 1; bindex >= new_bstart; bindex--) {", 
      "+\t\tstruct dentry *unlink_dentry;", 
      "+\t\tstruct dentry *unlink_dir_dentry;", 
      "+", 
      "+\t\tBUG_ON(bindex < 0);", 
      "+\t\tunlink_dentry = unionfs_lower_dentry_idx(new_dentry, bindex);", 
      "+\t\tif (!unlink_dentry)", 
      "+\t\t\tcontinue;", 
      "+", 
      "+\t\tunlink_dir_dentry = lock_parent(unlink_dentry);", 
      "+\t\terr = is_robranch_super(old_dir->i_sb, bindex);", 
      "+\t\tif (!err)", 
      "+\t\t\terr = vfs_unlink(unlink_dir_dentry->d_inode,", 
      "+\t\t\t\t\t unlink_dentry);", 
      "+", 
      "+\t\tfsstack_copy_attr_times(new_parent->d_inode,", 
      "+\t\t\t\t\tunlink_dir_dentry->d_inode);", 
      "+\t\t/* propagate number of hard-links */", 
      "+\t\tnew_parent->d_inode->i_nlink =", 
      "+\t\t\tunionfs_get_nlinks(new_parent->d_inode);", 
      "+", 
      "+\t\tunlock_dir(unlink_dir_dentry);", 
      "+\t\tif (!err) {", 
      "+\t\t\tif (bindex != new_bstart) {", 
      "+\t\t\t\tdput(unlink_dentry);", 
      "+\t\t\t\tunionfs_set_lower_dentry_idx(new_dentry,", 
      "+\t\t\t\t\t\t\t     bindex, NULL);", 
      "+\t\t\t}", 
      "+\t\t} else if (IS_COPYUP_ERR(err)) {", 
      "+\t\t\tdo_copyup = bindex - 1;", 
      "+\t\t} else if (revert) {", 
      "+\t\t\tgoto revert;", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\tif (do_copyup != -1) {", 
      "+\t\tfor (bindex = do_copyup; bindex >= 0; bindex--) {", 
      "+\t\t\t/*", 
      "+\t\t\t * copyup the file into some left directory, so that", 
      "+\t\t\t * you can rename it", 
      "+\t\t\t */", 
      "+\t\t\terr = copyup_dentry(old_parent->d_inode,", 
      "+\t\t\t\t\t    old_dentry, old_bstart, bindex,", 
      "+\t\t\t\t\t    old_dentry->d_name.name,", 
      "+\t\t\t\t\t    old_dentry->d_name.len, NULL,", 
      "+\t\t\t\t\t    i_size_read(old_dentry->d_inode));", 
      "+\t\t\t/* if copyup failed, try next branch to the left */", 
      "+\t\t\tif (err)", 
      "+\t\t\t\tcontinue;", 
      "+\t\t\t/*", 
      "+\t\t\t * create whiteout before calling __unionfs_rename", 
      "+\t\t\t * because the latter will change the old_dentry's", 
      "+\t\t\t * lower name and parent dir, resulting in the", 
      "+\t\t\t * whiteout getting created in the wrong dir.", 
      "+\t\t\t */", 
      "+\t\t\terr = create_whiteout(old_dentry, bindex);", 
      "+\t\t\tif (err) {", 
      "+\t\t\t\tprintk(KERN_ERR \"unionfs: can't create a \"", 
      "+\t\t\t\t       \"whiteout for %s in rename (err=%d)\\n\",", 
      "+\t\t\t\t       old_dentry->d_name.name, err);", 
      "+\t\t\t\tcontinue;", 
      "+\t\t\t}", 
      "+\t\t\terr = __unionfs_rename(old_dir, old_dentry, old_parent,", 
      "+\t\t\t\t\t       new_dir, new_dentry, new_parent,", 
      "+\t\t\t\t\t       bindex);", 
      "+\t\t\tbreak;", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\t/* make it opaque */", 
      "+\tif (S_ISDIR(old_dentry->d_inode->i_mode)) {", 
      "+\t\terr = make_dir_opaque(old_dentry, dbstart(old_dentry));", 
      "+\t\tif (err)", 
      "+\t\t\tgoto revert;", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * Create whiteout for source, only if:", 
      "+\t * (1) There is more than one underlying instance of source.", 
      "+\t * (We did a copy_up is taken care of above).", 
      "+\t */", 
      "+\tif ((old_bstart != old_bend) && (do_copyup == -1)) {", 
      "+\t\terr = create_whiteout(old_dentry, old_bstart);", 
      "+\t\tif (err) {", 
      "+\t\t\t/* can't fix anything now, so we exit with -EIO */", 
      "+\t\t\tprintk(KERN_ERR \"unionfs: can't create a whiteout for \"", 
      "+\t\t\t       \"%s in rename!\\n\", old_dentry->d_name.name);", 
      "+\t\t\terr = -EIO;", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\treturn err;", 
      "+", 
      "+revert:", 
      "+\t/* Do revert here. */", 
      "+\tlocal_err = unionfs_refresh_lower_dentry(new_dentry, new_parent,", 
      "+\t\t\t\t\t\t old_bstart);", 
      "+\tif (local_err) {", 
      "+\t\tprintk(KERN_ERR \"unionfs: revert failed in rename: \"", 
      "+\t\t       \"the new refresh failed\\n\");", 
      "+\t\teio = -EIO;", 
      "+\t}", 
      "+", 
      "+\tlocal_err = unionfs_refresh_lower_dentry(old_dentry, old_parent,", 
      "+\t\t\t\t\t\t old_bstart);", 
      "+\tif (local_err) {", 
      "+\t\tprintk(KERN_ERR \"unionfs: revert failed in rename: \"", 
      "+\t\t       \"the old refresh failed\\n\");", 
      "+\t\teio = -EIO;", 
      "+\t\tgoto revert_out;", 
      "+\t}", 
      "+", 
      "+\tif (!unionfs_lower_dentry_idx(new_dentry, bindex) ||", 
      "+\t    !unionfs_lower_dentry_idx(new_dentry, bindex)->d_inode) {", 
      "+\t\tprintk(KERN_ERR \"unionfs: revert failed in rename: \"", 
      "+\t\t       \"the object disappeared from under us!\\n\");", 
      "+\t\teio = -EIO;", 
      "+\t\tgoto revert_out;", 
      "+\t}", 
      "+", 
      "+\tif (unionfs_lower_dentry_idx(old_dentry, bindex) &&", 
      "+\t    unionfs_lower_dentry_idx(old_dentry, bindex)->d_inode) {", 
      "+\t\tprintk(KERN_ERR \"unionfs: revert failed in rename: \"", 
      "+\t\t       \"the object was created underneath us!\\n\");", 
      "+\t\teio = -EIO;", 
      "+\t\tgoto revert_out;", 
      "+\t}", 
      "+", 
      "+\tlocal_err = __unionfs_rename(new_dir, new_dentry, new_parent,", 
      "+\t\t\t\t     old_dir, old_dentry, old_parent,", 
      "+\t\t\t\t     old_bstart);", 
      "+", 
      "+\t/* If we can't fix it, then we cop-out with -EIO. */", 
      "+\tif (local_err) {", 
      "+\t\tprintk(KERN_ERR \"unionfs: revert failed in rename!\\n\");", 
      "+\t\teio = -EIO;", 
      "+\t}", 
      "+", 
      "+\tlocal_err = unionfs_refresh_lower_dentry(new_dentry, new_parent,", 
      "+\t\t\t\t\t\t bindex);", 
      "+\tif (local_err)", 
      "+\t\teio = -EIO;", 
      "+\tlocal_err = unionfs_refresh_lower_dentry(old_dentry, old_parent,", 
      "+\t\t\t\t\t\t bindex);", 
      "+\tif (local_err)", 
      "+\t\teio = -EIO;", 
      "+", 
      "+revert_out:", 
      "+\tif (eio)", 
      "+\t\terr = eio;", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * We can't copyup a directory, because it may involve huge numbers of", 
      "+ * children, etc.  Doing that in the kernel would be bad, so instead we", 
      "+ * return EXDEV to the user-space utility that caused this, and let the", 
      "+ * user-space recurse and ask us to copy up each file separately.", 
      "+ */", 
      "+static int may_rename_dir(struct dentry *dentry, struct dentry *parent)", 
      "+{", 
      "+\tint err, bstart;", 
      "+", 
      "+\terr = check_empty(dentry, parent, NULL);", 
      "+\tif (err == -ENOTEMPTY) {", 
      "+\t\tif (is_robranch(dentry))", 
      "+\t\t\treturn -EXDEV;", 
      "+\t} else if (err) {", 
      "+\t\treturn err;", 
      "+\t}", 
      "+", 
      "+\tbstart = dbstart(dentry);", 
      "+\tif (dbend(dentry) == bstart || dbopaque(dentry) == bstart)", 
      "+\t\treturn 0;", 
      "+", 
      "+\tdbstart(dentry) = bstart + 1;", 
      "+\terr = check_empty(dentry, parent, NULL);", 
      "+\tdbstart(dentry) = bstart;", 
      "+\tif (err == -ENOTEMPTY)", 
      "+\t\terr = -EXDEV;", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * The locking rules in unionfs_rename are complex.  We could use a simpler", 
      "+ * superblock-level name-space lock for renames and copy-ups.", 
      "+ */", 
      "+int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry,", 
      "+\t\t   struct inode *new_dir, struct dentry *new_dentry)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct dentry *wh_dentry;", 
      "+\tstruct dentry *old_parent, *new_parent;", 
      "+\tint valid = true;", 
      "+", 
      "+\tunionfs_read_lock(old_dentry->d_sb, UNIONFS_SMUTEX_CHILD);", 
      "+\told_parent = dget_parent(old_dentry);", 
      "+\tnew_parent = dget_parent(new_dentry);", 
      "+\t/* un/lock parent dentries only if they differ from old/new_dentry */", 
      "+\tif (old_parent != old_dentry &&", 
      "+\t    old_parent != new_dentry)", 
      "+\t\tunionfs_lock_dentry(old_parent, UNIONFS_DMUTEX_REVAL_PARENT);", 
      "+\tif (new_parent != old_dentry &&", 
      "+\t    new_parent != new_dentry &&", 
      "+\t    new_parent != old_parent)", 
      "+\t\tunionfs_lock_dentry(new_parent, UNIONFS_DMUTEX_REVAL_CHILD);", 
      "+\tunionfs_double_lock_dentry(old_dentry, new_dentry);", 
      "+", 
      "+\tvalid = __unionfs_d_revalidate(old_dentry, old_parent, false);", 
      "+\tif (!valid) {", 
      "+\t\terr = -ESTALE;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tif (!d_deleted(new_dentry) && new_dentry->d_inode) {", 
      "+\t\tvalid = __unionfs_d_revalidate(new_dentry, new_parent, false);", 
      "+\t\tif (!valid) {", 
      "+\t\t\terr = -ESTALE;", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\tif (!S_ISDIR(old_dentry->d_inode->i_mode))", 
      "+\t\terr = unionfs_partial_lookup(old_dentry, old_parent);", 
      "+\telse", 
      "+\t\terr = may_rename_dir(old_dentry, old_parent);", 
      "+", 
      "+\tif (err)", 
      "+\t\tgoto out;", 
      "+", 
      "+\terr = unionfs_partial_lookup(new_dentry, new_parent);", 
      "+\tif (err)", 
      "+\t\tgoto out;", 
      "+", 
      "+\t/*", 
      "+\t * if new_dentry is already lower because of whiteout,", 
      "+\t * simply override it even if the whited-out dir is not empty.", 
      "+\t */", 
      "+\twh_dentry = find_first_whiteout(new_dentry);", 
      "+\tif (!IS_ERR(wh_dentry)) {", 
      "+\t\tdput(wh_dentry);", 
      "+\t} else if (new_dentry->d_inode) {", 
      "+\t\tif (S_ISDIR(old_dentry->d_inode->i_mode) !=", 
      "+\t\t    S_ISDIR(new_dentry->d_inode->i_mode)) {", 
      "+\t\t\terr = S_ISDIR(old_dentry->d_inode->i_mode) ?", 
      "+\t\t\t\t-ENOTDIR : -EISDIR;", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+", 
      "+\t\tif (S_ISDIR(new_dentry->d_inode->i_mode)) {", 
      "+\t\t\tstruct unionfs_dir_state *namelist = NULL;", 
      "+\t\t\t/* check if this unionfs directory is empty or not */", 
      "+\t\t\terr = check_empty(new_dentry, new_parent, &namelist);", 
      "+\t\t\tif (err)", 
      "+\t\t\t\tgoto out;", 
      "+", 
      "+\t\t\tif (!is_robranch(new_dentry))", 
      "+\t\t\t\terr = delete_whiteouts(new_dentry,", 
      "+\t\t\t\t\t\t       dbstart(new_dentry),", 
      "+\t\t\t\t\t\t       namelist);", 
      "+", 
      "+\t\t\tfree_rdstate(namelist);", 
      "+", 
      "+\t\t\tif (err)", 
      "+\t\t\t\tgoto out;", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\terr = do_unionfs_rename(old_dir, old_dentry, old_parent,", 
      "+\t\t\t\tnew_dir, new_dentry, new_parent);", 
      "+\tif (err)", 
      "+\t\tgoto out;", 
      "+", 
      "+\t/*", 
      "+\t * force re-lookup since the dir on ro branch is not renamed, and", 
      "+\t * lower dentries still indicate the un-renamed ones.", 
      "+\t */", 
      "+\tif (S_ISDIR(old_dentry->d_inode->i_mode))", 
      "+\t\tatomic_dec(&UNIONFS_D(old_dentry)->generation);", 
      "+\telse", 
      "+\t\tunionfs_postcopyup_release(old_dentry);", 
      "+\tif (new_dentry->d_inode && !S_ISDIR(new_dentry->d_inode->i_mode)) {", 
      "+\t\tunionfs_postcopyup_release(new_dentry);", 
      "+\t\tunionfs_postcopyup_setmnt(new_dentry);", 
      "+\t\tif (!unionfs_lower_inode(new_dentry->d_inode)) {", 
      "+\t\t\t/*", 
      "+\t\t\t * If we get here, it means that no copyup was", 
      "+\t\t\t * needed, and that a file by the old name already", 
      "+\t\t\t * existing on the destination branch; that file got", 
      "+\t\t\t * renamed earlier in this function, so all we need", 
      "+\t\t\t * to do here is set the lower inode.", 
      "+\t\t\t */", 
      "+\t\t\tstruct inode *inode;", 
      "+\t\t\tinode = unionfs_lower_inode(old_dentry->d_inode);", 
      "+\t\t\tigrab(inode);", 
      "+\t\t\tunionfs_set_lower_inode_idx(new_dentry->d_inode,", 
      "+\t\t\t\t\t\t    dbstart(new_dentry),", 
      "+\t\t\t\t\t\t    inode);", 
      "+\t\t}", 
      "+\t}", 
      "+\t/* if all of this renaming succeeded, update our times */", 
      "+\tunionfs_copy_attr_times(old_dentry->d_inode);", 
      "+\tunionfs_copy_attr_times(new_dentry->d_inode);", 
      "+\tunionfs_check_inode(old_dir);", 
      "+\tunionfs_check_inode(new_dir);", 
      "+\tunionfs_check_dentry(old_dentry);", 
      "+\tunionfs_check_dentry(new_dentry);", 
      "+", 
      "+out:", 
      "+\tif (err)\t\t/* clear the new_dentry stuff created */", 
      "+\t\td_drop(new_dentry);", 
      "+", 
      "+\tunionfs_double_unlock_dentry(old_dentry, new_dentry);", 
      "+\tif (new_parent != old_dentry &&", 
      "+\t    new_parent != new_dentry &&", 
      "+\t    new_parent != old_parent)", 
      "+\t\tunionfs_unlock_dentry(new_parent);", 
      "+\tif (old_parent != old_dentry &&", 
      "+\t    old_parent != new_dentry)", 
      "+\t\tunionfs_unlock_dentry(old_parent);", 
      "+\tdput(new_parent);", 
      "+\tdput(old_parent);", 
      "+\tunionfs_read_unlock(old_dentry->d_sb);", 
      "+", 
      "+\treturn err;", 
      "+}"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/sioq.c", 
    [
      "diff --git a/fs/unionfs/sioq.c b/fs/unionfs/sioq.c", 
      "new file mode 100644", 
      "index 0000000..ba3bf63", 
      "--- dev/null", 
      "+++ b/fs/unionfs/sioq.c", 
      "@@ -0,0 +1,101 @@", 
      "+/*", 
      "+ * Copyright (c) 2006-2009 Erez Zadok", 
      "+ * Copyright (c) 2006      Charles P. Wright", 
      "+ * Copyright (c) 2006-2007 Josef 'Jeff' Sipek", 
      "+ * Copyright (c) 2006      Junjiro Okajima", 
      "+ * Copyright (c) 2006      David P. Quigley", 
      "+ * Copyright (c) 2006-2009 Stony Brook University", 
      "+ * Copyright (c) 2006-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#include \"union.h\"", 
      "+", 
      "+/*", 
      "+ * Super-user IO work Queue - sometimes we need to perform actions which", 
      "+ * would fail due to the unix permissions on the parent directory (e.g.,", 
      "+ * rmdir a directory which appears empty, but in reality contains", 
      "+ * whiteouts).", 
      "+ */", 
      "+", 
      "+static struct workqueue_struct *superio_workqueue;", 
      "+", 
      "+int __init init_sioq(void)", 
      "+{", 
      "+\tint err;", 
      "+", 
      "+\tsuperio_workqueue = create_workqueue(\"unionfs_siod\");", 
      "+\tif (!IS_ERR(superio_workqueue))", 
      "+\t\treturn 0;", 
      "+", 
      "+\terr = PTR_ERR(superio_workqueue);", 
      "+\tprintk(KERN_ERR \"unionfs: create_workqueue failed %d\\n\", err);", 
      "+\tsuperio_workqueue = NULL;", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+void stop_sioq(void)", 
      "+{", 
      "+\tif (superio_workqueue)", 
      "+\t\tdestroy_workqueue(superio_workqueue);", 
      "+}", 
      "+", 
      "+void run_sioq(work_func_t func, struct sioq_args *args)", 
      "+{", 
      "+\tINIT_WORK(&args->work, func);", 
      "+", 
      "+\tinit_completion(&args->comp);", 
      "+\twhile (!queue_work(superio_workqueue, &args->work)) {", 
      "+\t\t/* TODO: do accounting if needed */", 
      "+\t\tschedule();", 
      "+\t}", 
      "+\twait_for_completion(&args->comp);", 
      "+}", 
      "+", 
      "+void __unionfs_create(struct work_struct *work)", 
      "+{", 
      "+\tstruct sioq_args *args = container_of(work, struct sioq_args, work);", 
      "+\tstruct create_args *c = &args->create;", 
      "+", 
      "+\targs->err = vfs_create(c->parent, c->dentry, c->mode, c->nd);", 
      "+\tcomplete(&args->comp);", 
      "+}", 
      "+", 
      "+void __unionfs_mkdir(struct work_struct *work)", 
      "+{", 
      "+\tstruct sioq_args *args = container_of(work, struct sioq_args, work);", 
      "+\tstruct mkdir_args *m = &args->mkdir;", 
      "+", 
      "+\targs->err = vfs_mkdir(m->parent, m->dentry, m->mode);", 
      "+\tcomplete(&args->comp);", 
      "+}", 
      "+", 
      "+void __unionfs_mknod(struct work_struct *work)", 
      "+{", 
      "+\tstruct sioq_args *args = container_of(work, struct sioq_args, work);", 
      "+\tstruct mknod_args *m = &args->mknod;", 
      "+", 
      "+\targs->err = vfs_mknod(m->parent, m->dentry, m->mode, m->dev);", 
      "+\tcomplete(&args->comp);", 
      "+}", 
      "+", 
      "+void __unionfs_symlink(struct work_struct *work)", 
      "+{", 
      "+\tstruct sioq_args *args = container_of(work, struct sioq_args, work);", 
      "+\tstruct symlink_args *s = &args->symlink;", 
      "+", 
      "+\targs->err = vfs_symlink(s->parent, s->dentry, s->symbuf, s->mode);", 
      "+\tcomplete(&args->comp);", 
      "+}", 
      "+", 
      "+void __unionfs_unlink(struct work_struct *work)", 
      "+{", 
      "+\tstruct sioq_args *args = container_of(work, struct sioq_args, work);", 
      "+\tstruct unlink_args *u = &args->unlink;", 
      "+", 
      "+\targs->err = vfs_unlink(u->parent, u->dentry);", 
      "+\tcomplete(&args->comp);", 
      "+}"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/sioq.h", 
    [
      "diff --git a/fs/unionfs/sioq.h b/fs/unionfs/sioq.h", 
      "new file mode 100644", 
      "index 0000000..4f4ba53", 
      "--- dev/null", 
      "+++ b/fs/unionfs/sioq.h", 
      "@@ -0,0 +1,92 @@", 
      "+/*", 
      "+ * Copyright (c) 2006-2009 Erez Zadok", 
      "+ * Copyright (c) 2006      Charles P. Wright", 
      "+ * Copyright (c) 2006-2007 Josef 'Jeff' Sipek", 
      "+ * Copyright (c) 2006      Junjiro Okajima", 
      "+ * Copyright (c) 2006      David P. Quigley", 
      "+ * Copyright (c) 2006-2009 Stony Brook University", 
      "+ * Copyright (c) 2006-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#ifndef _SIOQ_H", 
      "+#define _SIOQ_H", 
      "+", 
      "+struct deletewh_args {", 
      "+\tstruct unionfs_dir_state *namelist;", 
      "+\tstruct dentry *dentry;", 
      "+\tint bindex;", 
      "+};", 
      "+", 
      "+struct is_opaque_args {", 
      "+\tstruct dentry *dentry;", 
      "+};", 
      "+", 
      "+struct create_args {", 
      "+\tstruct inode *parent;", 
      "+\tstruct dentry *dentry;", 
      "+\tumode_t mode;", 
      "+\tstruct nameidata *nd;", 
      "+};", 
      "+", 
      "+struct mkdir_args {", 
      "+\tstruct inode *parent;", 
      "+\tstruct dentry *dentry;", 
      "+\tumode_t mode;", 
      "+};", 
      "+", 
      "+struct mknod_args {", 
      "+\tstruct inode *parent;", 
      "+\tstruct dentry *dentry;", 
      "+\tumode_t mode;", 
      "+\tdev_t dev;", 
      "+};", 
      "+", 
      "+struct symlink_args {", 
      "+\tstruct inode *parent;", 
      "+\tstruct dentry *dentry;", 
      "+\tchar *symbuf;", 
      "+\tumode_t mode;", 
      "+};", 
      "+", 
      "+struct unlink_args {", 
      "+\tstruct inode *parent;", 
      "+\tstruct dentry *dentry;", 
      "+};", 
      "+", 
      "+", 
      "+struct sioq_args {", 
      "+\tstruct completion comp;", 
      "+\tstruct work_struct work;", 
      "+\tint err;", 
      "+\tvoid *ret;", 
      "+", 
      "+\tunion {", 
      "+\t\tstruct deletewh_args deletewh;", 
      "+\t\tstruct is_opaque_args is_opaque;", 
      "+\t\tstruct create_args create;", 
      "+\t\tstruct mkdir_args mkdir;", 
      "+\t\tstruct mknod_args mknod;", 
      "+\t\tstruct symlink_args symlink;", 
      "+\t\tstruct unlink_args unlink;", 
      "+\t};", 
      "+};", 
      "+", 
      "+/* Extern definitions for SIOQ functions */", 
      "+extern int __init init_sioq(void);", 
      "+extern void stop_sioq(void);", 
      "+extern void run_sioq(work_func_t func, struct sioq_args *args);", 
      "+", 
      "+/* Extern definitions for our privilege escalation helpers */", 
      "+extern void __unionfs_create(struct work_struct *work);", 
      "+extern void __unionfs_mkdir(struct work_struct *work);", 
      "+extern void __unionfs_mknod(struct work_struct *work);", 
      "+extern void __unionfs_symlink(struct work_struct *work);", 
      "+extern void __unionfs_unlink(struct work_struct *work);", 
      "+extern void __delete_whiteouts(struct work_struct *work);", 
      "+extern void __is_opaque_dir(struct work_struct *work);", 
      "+", 
      "+#endif /* not _SIOQ_H */"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/subr.c", 
    [
      "diff --git a/fs/unionfs/subr.c b/fs/unionfs/subr.c", 
      "new file mode 100644", 
      "index 0000000..018b4fd", 
      "--- dev/null", 
      "+++ b/fs/unionfs/subr.c", 
      "@@ -0,0 +1,95 @@", 
      "+/*", 
      "+ * Copyright (c) 2003-2009 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-2009 Stony Brook University", 
      "+ * Copyright (c) 2003-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#include \"union.h\"", 
      "+", 
      "+/*", 
      "+ * returns the right n_link value based on the inode type", 
      "+ */", 
      "+int unionfs_get_nlinks(const struct inode *inode)", 
      "+{", 
      "+\t/* don't bother to do all the work since we're unlinked */", 
      "+\tif (inode->i_nlink == 0)", 
      "+\t\treturn 0;", 
      "+", 
      "+\tif (!S_ISDIR(inode->i_mode))", 
      "+\t\treturn unionfs_lower_inode(inode)->i_nlink;", 
      "+", 
      "+\t/*", 
      "+\t * For directories, we return 1. The only place that could cares", 
      "+\t * about links is readdir, and there's d_type there so even that", 
      "+\t * doesn't matter.", 
      "+\t */", 
      "+\treturn 1;", 
      "+}", 
      "+", 
      "+/* copy a/m/ctime from the lower branch with the newest times */", 
      "+void unionfs_copy_attr_times(struct inode *upper)", 
      "+{", 
      "+\tint bindex;", 
      "+\tstruct inode *lower;", 
      "+", 
      "+\tif (!upper)", 
      "+\t\treturn;", 
      "+\tif (ibstart(upper) < 0) {", 
      "+#ifdef CONFIG_UNION_FS_DEBUG", 
      "+\t\tWARN_ON(ibstart(upper) < 0);", 
      "+#endif /* CONFIG_UNION_FS_DEBUG */", 
      "+\t\treturn;", 
      "+\t}", 
      "+\tfor (bindex = ibstart(upper); bindex <= ibend(upper); bindex++) {", 
      "+\t\tlower = unionfs_lower_inode_idx(upper, bindex);", 
      "+\t\tif (!lower)", 
      "+\t\t\tcontinue; /* not all lower dir objects may exist */", 
      "+\t\tif (unlikely(timespec_compare(&upper->i_mtime,", 
      "+\t\t\t\t\t      &lower->i_mtime) < 0))", 
      "+\t\t\tupper->i_mtime = lower->i_mtime;", 
      "+\t\tif (unlikely(timespec_compare(&upper->i_ctime,", 
      "+\t\t\t\t\t      &lower->i_ctime) < 0))", 
      "+\t\t\tupper->i_ctime = lower->i_ctime;", 
      "+\t\tif (unlikely(timespec_compare(&upper->i_atime,", 
      "+\t\t\t\t\t      &lower->i_atime) < 0))", 
      "+\t\t\tupper->i_atime = lower->i_atime;", 
      "+\t}", 
      "+}", 
      "+", 
      "+/*", 
      "+ * A unionfs/fanout version of fsstack_copy_attr_all.  Uses a", 
      "+ * unionfs_get_nlinks to properly calcluate the number of links to a file.", 
      "+ * Also, copies the max() of all a/m/ctimes for all lower inodes (which is", 
      "+ * important if the lower inode is a directory type)", 
      "+ */", 
      "+void unionfs_copy_attr_all(struct inode *dest,", 
      "+\t\t\t   const struct inode *src)", 
      "+{", 
      "+\tdest->i_mode = src->i_mode;", 
      "+\tdest->i_uid = src->i_uid;", 
      "+\tdest->i_gid = src->i_gid;", 
      "+\tdest->i_rdev = src->i_rdev;", 
      "+", 
      "+\tunionfs_copy_attr_times(dest);", 
      "+", 
      "+\tdest->i_blkbits = src->i_blkbits;", 
      "+\tdest->i_flags = src->i_flags;", 
      "+", 
      "+\t/*", 
      "+\t * Update the nlinks AFTER updating the above fields, because the", 
      "+\t * get_links callback may depend on them.", 
      "+\t */", 
      "+\tdest->i_nlink = unionfs_get_nlinks(dest);", 
      "+}"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/super.c", 
    [
      "diff --git a/fs/unionfs/super.c b/fs/unionfs/super.c", 
      "new file mode 100644", 
      "index 0000000..314d4f2", 
      "--- dev/null", 
      "+++ b/fs/unionfs/super.c", 
      "@@ -0,0 +1,1048 @@", 
      "+/*", 
      "+ * Copyright (c) 2003-2009 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-2009 Stony Brook University", 
      "+ * Copyright (c) 2003-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#include \"union.h\"", 
      "+", 
      "+/*", 
      "+ * The inode cache is used with alloc_inode for both our inode info and the", 
      "+ * vfs inode.", 
      "+ */", 
      "+static struct kmem_cache *unionfs_inode_cachep;", 
      "+", 
      "+static void unionfs_read_inode(struct inode *inode)", 
      "+{", 
      "+\tint size;", 
      "+\tstruct unionfs_inode_info *info = UNIONFS_I(inode);", 
      "+", 
      "+\tmemset(info, 0, offsetof(struct unionfs_inode_info, vfs_inode));", 
      "+\tinfo->bstart = -1;", 
      "+\tinfo->bend = -1;", 
      "+\tatomic_set(&info->generation,", 
      "+\t\t   atomic_read(&UNIONFS_SB(inode->i_sb)->generation));", 
      "+\tspin_lock_init(&info->rdlock);", 
      "+\tinfo->rdcount = 1;", 
      "+\tinfo->hashsize = -1;", 
      "+\tINIT_LIST_HEAD(&info->readdircache);", 
      "+", 
      "+\tsize = sbmax(inode->i_sb) * sizeof(struct inode *);", 
      "+\tinfo->lower_inodes = kzalloc(size, GFP_KERNEL);", 
      "+\tif (unlikely(!info->lower_inodes)) {", 
      "+\t\tprintk(KERN_CRIT \"unionfs: no kernel memory when allocating \"", 
      "+\t\t       \"lower-pointer array!\\n\");", 
      "+\t\tBUG();", 
      "+\t}", 
      "+", 
      "+\tinode->i_version++;", 
      "+\tinode->i_op = &unionfs_main_iops;", 
      "+\tinode->i_fop = &unionfs_main_fops;", 
      "+", 
      "+\tinode->i_mapping->a_ops = &unionfs_aops;", 
      "+", 
      "+\t/*", 
      "+\t * reset times so unionfs_copy_attr_all can keep out time invariants", 
      "+\t * right (upper inode time being the max of all lower ones).", 
      "+\t */", 
      "+\tinode->i_atime.tv_sec = inode->i_atime.tv_nsec = 0;", 
      "+\tinode->i_mtime.tv_sec = inode->i_mtime.tv_nsec = 0;", 
      "+\tinode->i_ctime.tv_sec = inode->i_ctime.tv_nsec = 0;", 
      "+", 
      "+}", 
      "+", 
      "+/*", 
      "+ * we now define delete_inode, because there are two VFS paths that may", 
      "+ * destroy an inode: one of them calls clear inode before doing everything", 
      "+ * else that's needed, and the other is fine.  This way we truncate the inode", 
      "+ * size (and its pages) and then clear our own inode, which will do an iput", 
      "+ * on our and the lower inode.", 
      "+ *", 
      "+ * No need to lock sb info's rwsem.", 
      "+ */", 
      "+static void unionfs_delete_inode(struct inode *inode)", 
      "+{", 
      "+#if BITS_PER_LONG == 32 && defined(CONFIG_SMP)", 
      "+\tspin_lock(&inode->i_lock);", 
      "+#endif", 
      "+\ti_size_write(inode, 0);\t/* every f/s seems to do that */", 
      "+#if BITS_PER_LONG == 32 && defined(CONFIG_SMP)", 
      "+\tspin_unlock(&inode->i_lock);", 
      "+#endif", 
      "+", 
      "+\tif (inode->i_data.nrpages)", 
      "+\t\ttruncate_inode_pages(&inode->i_data, 0);", 
      "+", 
      "+\tclear_inode(inode);", 
      "+}", 
      "+", 
      "+/*", 
      "+ * final actions when unmounting a file system", 
      "+ *", 
      "+ * No need to lock rwsem.", 
      "+ */", 
      "+static void unionfs_put_super(struct super_block *sb)", 
      "+{", 
      "+\tint bindex, bstart, bend;", 
      "+\tstruct unionfs_sb_info *spd;", 
      "+\tint leaks = 0;", 
      "+", 
      "+\tspd = UNIONFS_SB(sb);", 
      "+\tif (!spd)", 
      "+\t\treturn;", 
      "+", 
      "+\tbstart = sbstart(sb);", 
      "+\tbend = sbend(sb);", 
      "+", 
      "+\t/* Make sure we have no leaks of branchget/branchput. */", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++)", 
      "+\t\tif (unlikely(branch_count(sb, bindex) != 0)) {", 
      "+\t\t\tprintk(KERN_CRIT", 
      "+\t\t\t       \"unionfs: branch %d has %d references left!\\n\",", 
      "+\t\t\t       bindex, branch_count(sb, bindex));", 
      "+\t\t\tleaks = 1;", 
      "+\t\t}", 
      "+\tWARN_ON(leaks != 0);", 
      "+", 
      "+\t/* decrement lower super references */", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tstruct super_block *s;", 
      "+\t\ts = unionfs_lower_super_idx(sb, bindex);", 
      "+\t\tunionfs_set_lower_super_idx(sb, bindex, NULL);", 
      "+\t\tatomic_dec(&s->s_active);", 
      "+\t}", 
      "+", 
      "+\tkfree(spd->dev_name);", 
      "+\tkfree(spd->data);", 
      "+\tkfree(spd);", 
      "+\tsb->s_fs_info = NULL;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Since people use this to answer the \"How big of a file can I write?\"", 
      "+ * question, we report the size of the highest priority branch as the size of", 
      "+ * the union.", 
      "+ */", 
      "+static int unionfs_statfs(struct dentry *dentry, struct kstatfs *buf)", 
      "+{", 
      "+\tint err\t= 0;", 
      "+\tstruct super_block *sb;", 
      "+\tstruct dentry *lower_dentry;", 
      "+\tstruct dentry *parent;", 
      "+\tbool valid;", 
      "+", 
      "+\tsb = dentry->d_sb;", 
      "+", 
      "+\tunionfs_read_lock(sb, UNIONFS_SMUTEX_CHILD);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\tvalid = __unionfs_d_revalidate(dentry, parent, false);", 
      "+\tif (unlikely(!valid)) {", 
      "+\t\terr = -ESTALE;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tunionfs_check_dentry(dentry);", 
      "+", 
      "+\tlower_dentry = unionfs_lower_dentry(sb->s_root);", 
      "+\terr = vfs_statfs(lower_dentry, buf);", 
      "+", 
      "+\t/* set return buf to our f/s to avoid confusing user-level utils */", 
      "+\tbuf->f_type = UNIONFS_SUPER_MAGIC;", 
      "+\t/*", 
      "+\t * Our maximum file name can is shorter by a few bytes because every", 
      "+\t * file name could potentially be whited-out.", 
      "+\t *", 
      "+\t * XXX: this restriction goes away with ODF.", 
      "+\t */", 
      "+\tunionfs_set_max_namelen(&buf->f_namelen);", 
      "+", 
      "+\t/*", 
      "+\t * reset two fields to avoid confusing user-land.", 
      "+\t * XXX: is this still necessary?", 
      "+\t */", 
      "+\tmemset(&buf->f_fsid, 0, sizeof(__kernel_fsid_t));", 
      "+\tmemset(&buf->f_spare, 0, sizeof(buf->f_spare));", 
      "+", 
      "+out:", 
      "+\tunionfs_check_dentry(dentry);", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(sb);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/* handle mode changing during remount */", 
      "+static noinline_for_stack int do_remount_mode_option(", 
      "+\t\t\t\t\tchar *optarg,", 
      "+\t\t\t\t\tint cur_branches,", 
      "+\t\t\t\t\tstruct unionfs_data *new_data,", 
      "+\t\t\t\t\tstruct path *new_lower_paths)", 
      "+{", 
      "+\tint err = -EINVAL;", 
      "+\tint perms, idx;", 
      "+\tchar *modename = strchr(optarg, '=');", 
      "+\tstruct nameidata nd;", 
      "+", 
      "+\t/* by now, optarg contains the branch name */", 
      "+\tif (!*optarg) {", 
      "+\t\tprintk(KERN_ERR", 
      "+\t\t       \"unionfs: no branch specified for mode change\\n\");", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tif (!modename) {", 
      "+\t\tprintk(KERN_ERR \"unionfs: branch \\\"%s\\\" requires a mode\\n\",", 
      "+\t\t       optarg);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\t*modename++ = '\\0';", 
      "+\terr = parse_branch_mode(modename, &perms);", 
      "+\tif (err) {", 
      "+\t\tprintk(KERN_ERR \"unionfs: invalid mode \\\"%s\\\" for \\\"%s\\\"\\n\",", 
      "+\t\t       modename, optarg);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * Find matching branch index.  For now, this assumes that nothing", 
      "+\t * has been mounted on top of this Unionfs stack.  Once we have /odf", 
      "+\t * and cache-coherency resolved, we'll address the branch-path", 
      "+\t * uniqueness.", 
      "+\t */", 
      "+\terr = path_lookup(optarg, LOOKUP_FOLLOW, &nd);", 
      "+\tif (err) {", 
      "+\t\tprintk(KERN_ERR \"unionfs: error accessing \"", 
      "+\t\t       \"lower directory \\\"%s\\\" (error %d)\\n\",", 
      "+\t\t       optarg, err);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tfor (idx = 0; idx < cur_branches; idx++)", 
      "+\t\tif (nd.mnt == new_lower_paths[idx].mnt &&", 
      "+\t\t    nd.dentry == new_lower_paths[idx].dentry)", 
      "+\t\t\tbreak;", 
      "+\tpath_release(&nd);\t/* no longer needed */", 
      "+\tif (idx == cur_branches) {", 
      "+\t\terr = -ENOENT;\t/* err may have been reset above */", 
      "+\t\tprintk(KERN_ERR \"unionfs: branch \\\"%s\\\" \"", 
      "+\t\t       \"not found\\n\", optarg);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\t/* check/change mode for existing branch */", 
      "+\t/* we don't warn if perms==branchperms */", 
      "+\tnew_data[idx].branchperms = perms;", 
      "+\terr = 0;", 
      "+out:", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/* handle branch deletion during remount */", 
      "+static noinline_for_stack int do_remount_del_option(", 
      "+\t\t\t\t\tchar *optarg, int cur_branches,", 
      "+\t\t\t\t\tstruct unionfs_data *new_data,", 
      "+\t\t\t\t\tstruct path *new_lower_paths)", 
      "+{", 
      "+\tint err = -EINVAL;", 
      "+\tint idx;", 
      "+\tstruct nameidata nd;", 
      "+", 
      "+\t/* optarg contains the branch name to delete */", 
      "+", 
      "+\t/*", 
      "+\t * Find matching branch index.  For now, this assumes that nothing", 
      "+\t * has been mounted on top of this Unionfs stack.  Once we have /odf", 
      "+\t * and cache-coherency resolved, we'll address the branch-path", 
      "+\t * uniqueness.", 
      "+\t */", 
      "+\terr = path_lookup(optarg, LOOKUP_FOLLOW, &nd);", 
      "+\tif (err) {", 
      "+\t\tprintk(KERN_ERR \"unionfs: error accessing \"", 
      "+\t\t       \"lower directory \\\"%s\\\" (error %d)\\n\",", 
      "+\t\t       optarg, err);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tfor (idx = 0; idx < cur_branches; idx++)", 
      "+\t\tif (nd.mnt == new_lower_paths[idx].mnt &&", 
      "+\t\t    nd.dentry == new_lower_paths[idx].dentry)", 
      "+\t\t\tbreak;", 
      "+\tpath_release(&nd);\t/* no longer needed */", 
      "+\tif (idx == cur_branches) {", 
      "+\t\tprintk(KERN_ERR \"unionfs: branch \\\"%s\\\" \"", 
      "+\t\t       \"not found\\n\", optarg);", 
      "+\t\terr = -ENOENT;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\t/* check if there are any open files on the branch to be deleted */", 
      "+\tif (atomic_read(&new_data[idx].open_files) > 0) {", 
      "+\t\terr = -EBUSY;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * Now we have to delete the branch.  First, release any handles it", 
      "+\t * has.  Then, move the remaining array indexes past \"idx\" in", 
      "+\t * new_data and new_lower_paths one to the left.  Finally, adjust", 
      "+\t * cur_branches.", 
      "+\t */", 
      "+\tpathput(&new_lower_paths[idx]);", 
      "+", 
      "+\tif (idx < cur_branches - 1) {", 
      "+\t\t/* if idx==cur_branches-1, we delete last branch: easy */", 
      "+\t\tmemmove(&new_data[idx], &new_data[idx+1],", 
      "+\t\t\t(cur_branches - 1 - idx) *", 
      "+\t\t\tsizeof(struct unionfs_data));", 
      "+\t\tmemmove(&new_lower_paths[idx], &new_lower_paths[idx+1],", 
      "+\t\t\t(cur_branches - 1 - idx) * sizeof(struct path));", 
      "+\t}", 
      "+", 
      "+\terr = 0;", 
      "+out:", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/* handle branch insertion during remount */", 
      "+static noinline_for_stack int do_remount_add_option(", 
      "+\t\t\t\t\tchar *optarg, int cur_branches,", 
      "+\t\t\t\t\tstruct unionfs_data *new_data,", 
      "+\t\t\t\t\tstruct path *new_lower_paths,", 
      "+\t\t\t\t\tint *high_branch_id)", 
      "+{", 
      "+\tint err = -EINVAL;", 
      "+\tint perms;", 
      "+\tint idx = 0;\t\t/* default: insert at beginning */", 
      "+\tchar *new_branch , *modename = NULL;", 
      "+\tstruct nameidata nd;", 
      "+", 
      "+\t/*", 
      "+\t * optarg can be of several forms:", 
      "+\t *", 
      "+\t * /bar:/foo\t\tinsert /foo before /bar", 
      "+\t * /bar:/foo=ro\t\tinsert /foo in ro mode before /bar", 
      "+\t * /foo\t\t\tinsert /foo in the beginning (prepend)", 
      "+\t * :/foo\t\tinsert /foo at the end (append)", 
      "+\t */", 
      "+\tif (*optarg == ':') {\t/* append? */", 
      "+\t\tnew_branch = optarg + 1; /* skip ':' */", 
      "+\t\tidx = cur_branches;", 
      "+\t\tgoto found_insertion_point;", 
      "+\t}", 
      "+\tnew_branch = strchr(optarg, ':');", 
      "+\tif (!new_branch) {\t/* prepend? */", 
      "+\t\tnew_branch = optarg;", 
      "+\t\tgoto found_insertion_point;", 
      "+\t}", 
      "+\t*new_branch++ = '\\0';\t/* holds path+mode of new branch */", 
      "+", 
      "+\t/*", 
      "+\t * Find matching branch index.  For now, this assumes that nothing", 
      "+\t * has been mounted on top of this Unionfs stack.  Once we have /odf", 
      "+\t * and cache-coherency resolved, we'll address the branch-path", 
      "+\t * uniqueness.", 
      "+\t */", 
      "+\terr = path_lookup(optarg, LOOKUP_FOLLOW, &nd);", 
      "+\tif (err) {", 
      "+\t\tprintk(KERN_ERR \"unionfs: error accessing \"", 
      "+\t\t       \"lower directory \\\"%s\\\" (error %d)\\n\",", 
      "+\t\t       optarg, err);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tfor (idx = 0; idx < cur_branches; idx++)", 
      "+\t\tif (nd.mnt == new_lower_paths[idx].mnt &&", 
      "+\t\t    nd.dentry == new_lower_paths[idx].dentry)", 
      "+\t\t\tbreak;", 
      "+\tpath_release(&nd);\t/* no longer needed */", 
      "+\tif (idx == cur_branches) {", 
      "+\t\tprintk(KERN_ERR \"unionfs: branch \\\"%s\\\" \"", 
      "+\t\t       \"not found\\n\", optarg);", 
      "+\t\terr = -ENOENT;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * At this point idx will hold the index where the new branch should", 
      "+\t * be inserted before.", 
      "+\t */", 
      "+found_insertion_point:", 
      "+\t/* find the mode for the new branch */", 
      "+\tif (new_branch)", 
      "+\t\tmodename = strchr(new_branch, '=');", 
      "+\tif (modename)", 
      "+\t\t*modename++ = '\\0';", 
      "+\tif (!new_branch || !*new_branch) {", 
      "+\t\tprintk(KERN_ERR \"unionfs: null new branch\\n\");", 
      "+\t\terr = -EINVAL;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\terr = parse_branch_mode(modename, &perms);", 
      "+\tif (err) {", 
      "+\t\tprintk(KERN_ERR \"unionfs: invalid mode \\\"%s\\\" for \"", 
      "+\t\t       \"branch \\\"%s\\\"\\n\", modename, new_branch);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\terr = path_lookup(new_branch, LOOKUP_FOLLOW, &nd);", 
      "+\tif (err) {", 
      "+\t\tprintk(KERN_ERR \"unionfs: error accessing \"", 
      "+\t\t       \"lower directory \\\"%s\\\" (error %d)\\n\",", 
      "+\t\t       new_branch, err);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\t/*", 
      "+\t * It's probably safe to check_mode the new branch to insert.  Note:", 
      "+\t * we don't allow inserting branches which are unionfs's by", 
      "+\t * themselves (check_branch returns EINVAL in that case).  This is", 
      "+\t * because this code base doesn't support stacking unionfs: the ODF", 
      "+\t * code base supports that correctly.", 
      "+\t */", 
      "+\terr = check_branch(&nd);", 
      "+\tif (err) {", 
      "+\t\tprintk(KERN_ERR \"unionfs: lower directory \"", 
      "+\t\t       \"\\\"%s\\\" is not a valid branch\\n\", optarg);", 
      "+\t\tpath_release(&nd);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * Now we have to insert the new branch.  But first, move the bits", 
      "+\t * to make space for the new branch, if needed.  Finally, adjust", 
      "+\t * cur_branches.", 
      "+\t * We don't release nd here; it's kept until umount/remount.", 
      "+\t */", 
      "+\tif (idx < cur_branches) {", 
      "+\t\t/* if idx==cur_branches, we append: easy */", 
      "+\t\tmemmove(&new_data[idx+1], &new_data[idx],", 
      "+\t\t\t(cur_branches - idx) * sizeof(struct unionfs_data));", 
      "+\t\tmemmove(&new_lower_paths[idx+1], &new_lower_paths[idx],", 
      "+\t\t\t(cur_branches - idx) * sizeof(struct path));", 
      "+\t}", 
      "+\tnew_lower_paths[idx].dentry = nd.dentry;", 
      "+\tnew_lower_paths[idx].mnt = nd.mnt;", 
      "+", 
      "+\tnew_data[idx].sb = nd.dentry->d_sb;", 
      "+\tatomic_set(&new_data[idx].open_files, 0);", 
      "+\tnew_data[idx].branchperms = perms;", 
      "+\tnew_data[idx].branch_id = ++*high_branch_id; /* assign new branch ID */", 
      "+", 
      "+\terr = 0;", 
      "+out:", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+", 
      "+/*", 
      "+ * Support branch management options on remount.", 
      "+ *", 
      "+ * See Documentation/filesystems/unionfs/ for details.", 
      "+ *", 
      "+ * @flags: numeric mount options", 
      "+ * @options: mount options string", 
      "+ *", 
      "+ * This function can rearrange a mounted union dynamically, adding and", 
      "+ * removing branches, including changing branch modes.  Clearly this has to", 
      "+ * be done safely and atomically.  Luckily, the VFS already calls this", 
      "+ * function with lock_super(sb) and lock_kernel() held, preventing", 
      "+ * concurrent mixing of new mounts, remounts, and unmounts.  Moreover,", 
      "+ * do_remount_sb(), our caller function, already called shrink_dcache_sb(sb)", 
      "+ * to purge dentries/inodes from our superblock, and also called", 
      "+ * fsync_super(sb) to purge any dirty pages.  So we're good.", 
      "+ *", 
      "+ * XXX: however, our remount code may also need to invalidate mapped pages", 
      "+ * so as to force them to be re-gotten from the (newly reconfigured) lower", 
      "+ * branches.  This has to wait for proper mmap and cache coherency support", 
      "+ * in the VFS.", 
      "+ *", 
      "+ */", 
      "+static int unionfs_remount_fs(struct super_block *sb, int *flags,", 
      "+\t\t\t      char *options)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tint i;", 
      "+\tchar *optionstmp, *tmp_to_free;\t/* kstrdup'ed of \"options\" */", 
      "+\tchar *optname;", 
      "+\tint cur_branches = 0;\t/* no. of current branches */", 
      "+\tint new_branches = 0;\t/* no. of branches actually left in the end */", 
      "+\tint add_branches;\t/* est. no. of branches to add */", 
      "+\tint del_branches;\t/* est. no. of branches to del */", 
      "+\tint max_branches;\t/* max possible no. of branches */", 
      "+\tstruct unionfs_data *new_data = NULL, *tmp_data = NULL;", 
      "+\tstruct path *new_lower_paths = NULL, *tmp_lower_paths = NULL;", 
      "+\tstruct inode **new_lower_inodes = NULL;", 
      "+\tint new_high_branch_id;\t/* new high branch ID */", 
      "+\tint size;\t\t/* memory allocation size, temp var */", 
      "+\tint old_ibstart, old_ibend;", 
      "+", 
      "+\tunionfs_write_lock(sb);", 
      "+", 
      "+\t/*", 
      "+\t * The VFS will take care of \"ro\" and \"rw\" flags, and we can safely", 
      "+\t * ignore MS_SILENT, but anything else left over is an error.  So we", 
      "+\t * need to check if any other flags may have been passed (none are", 
      "+\t * allowed/supported as of now).", 
      "+\t */", 
      "+\tif ((*flags & ~(MS_RDONLY | MS_SILENT)) != 0) {", 
      "+\t\tprintk(KERN_ERR", 
      "+\t\t       \"unionfs: remount flags 0x%x unsupported\\n\", *flags);", 
      "+\t\terr = -EINVAL;", 
      "+\t\tgoto out_error;", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * If 'options' is NULL, it's probably because the user just changed", 
      "+\t * the union to a \"ro\" or \"rw\" and the VFS took care of it.  So", 
      "+\t * nothing to do and we're done.", 
      "+\t */", 
      "+\tif (!options || options[0] == '\\0')", 
      "+\t\tgoto out_error;", 
      "+", 
      "+\t/*", 
      "+\t * Find out how many branches we will have in the end, counting", 
      "+\t * \"add\" and \"del\" commands.  Copy the \"options\" string because", 
      "+\t * strsep modifies the string and we need it later.", 
      "+\t */", 
      "+\ttmp_to_free = kstrdup(options, GFP_KERNEL);", 
      "+\toptionstmp = tmp_to_free;", 
      "+\tif (unlikely(!optionstmp)) {", 
      "+\t\terr = -ENOMEM;", 
      "+\t\tgoto out_free;", 
      "+\t}", 
      "+\tcur_branches = sbmax(sb); /* current no. branches */", 
      "+\tnew_branches = sbmax(sb);", 
      "+\tdel_branches = 0;", 
      "+\tadd_branches = 0;", 
      "+\tnew_high_branch_id = sbhbid(sb); /* save current high_branch_id */", 
      "+\twhile ((optname = strsep(&optionstmp, \",\")) != NULL) {", 
      "+\t\tchar *optarg;", 
      "+", 
      "+\t\tif (!optname || !*optname)", 
      "+\t\t\tcontinue;", 
      "+", 
      "+\t\toptarg = strchr(optname, '=');", 
      "+\t\tif (optarg)", 
      "+\t\t\t*optarg++ = '\\0';", 
      "+", 
      "+\t\tif (!strcmp(\"add\", optname))", 
      "+\t\t\tadd_branches++;", 
      "+\t\telse if (!strcmp(\"del\", optname))", 
      "+\t\t\tdel_branches++;", 
      "+\t}", 
      "+\tkfree(tmp_to_free);", 
      "+\t/* after all changes, will we have at least one branch left? */", 
      "+\tif ((new_branches + add_branches - del_branches) < 1) {", 
      "+\t\tprintk(KERN_ERR", 
      "+\t\t       \"unionfs: no branches left after remount\\n\");", 
      "+\t\terr = -EINVAL;", 
      "+\t\tgoto out_free;", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * Since we haven't actually parsed all the add/del options, nor", 
      "+\t * have we checked them for errors, we don't know for sure how many", 
      "+\t * branches we will have after all changes have taken place.  In", 
      "+\t * fact, the total number of branches left could be less than what", 
      "+\t * we have now.  So we need to allocate space for a temporary", 
      "+\t * placeholder that is at least as large as the maximum number of", 
      "+\t * branches we *could* have, which is the current number plus all", 
      "+\t * the additions.  Once we're done with these temp placeholders, we", 
      "+\t * may have to re-allocate the final size, copy over from the temp,", 
      "+\t * and then free the temps (done near the end of this function).", 
      "+\t */", 
      "+\tmax_branches = cur_branches + add_branches;", 
      "+\t/* allocate space for new pointers to lower dentry */", 
      "+\ttmp_data = kcalloc(max_branches,", 
      "+\t\t\t   sizeof(struct unionfs_data), GFP_KERNEL);", 
      "+\tif (unlikely(!tmp_data)) {", 
      "+\t\terr = -ENOMEM;", 
      "+\t\tgoto out_free;", 
      "+\t}", 
      "+\t/* allocate space for new pointers to lower paths */", 
      "+\ttmp_lower_paths = kcalloc(max_branches,", 
      "+\t\t\t\t  sizeof(struct path), GFP_KERNEL);", 
      "+\tif (unlikely(!tmp_lower_paths)) {", 
      "+\t\terr = -ENOMEM;", 
      "+\t\tgoto out_free;", 
      "+\t}", 
      "+\t/* copy current info into new placeholders, incrementing refcnts */", 
      "+\tmemcpy(tmp_data, UNIONFS_SB(sb)->data,", 
      "+\t       cur_branches * sizeof(struct unionfs_data));", 
      "+\tmemcpy(tmp_lower_paths, UNIONFS_D(sb->s_root)->lower_paths,", 
      "+\t       cur_branches * sizeof(struct path));", 
      "+\tfor (i = 0; i < cur_branches; i++)", 
      "+\t\tpathget(&tmp_lower_paths[i]); /* drop refs at end of fxn */", 
      "+", 
      "+\t/*******************************************************************", 
      "+\t * For each branch command, do path_lookup on the requested branch,", 
      "+\t * and apply the change to a temp branch list.  To handle errors, we", 
      "+\t * already dup'ed the old arrays (above), and increased the refcnts", 
      "+\t * on various f/s objects.  So now we can do all the path_lookups", 
      "+\t * and branch-management commands on the new arrays.  If it fail mid", 
      "+\t * way, we free the tmp arrays and *put all objects.  If we succeed,", 
      "+\t * then we free old arrays and *put its objects, and then replace", 
      "+\t * the arrays with the new tmp list (we may have to re-allocate the", 
      "+\t * memory because the temp lists could have been larger than what we", 
      "+\t * actually needed).", 
      "+\t *******************************************************************/", 
      "+", 
      "+\twhile ((optname = strsep(&options, \",\")) != NULL) {", 
      "+\t\tchar *optarg;", 
      "+", 
      "+\t\tif (!optname || !*optname)", 
      "+\t\t\tcontinue;", 
      "+\t\t/*", 
      "+\t\t * At this stage optname holds a comma-delimited option, but", 
      "+\t\t * without the commas.  Next, we need to break the string on", 
      "+\t\t * the '=' symbol to separate CMD=ARG, where ARG itself can", 
      "+\t\t * be KEY=VAL.  For example, in mode=/foo=rw, CMD is \"mode\",", 
      "+\t\t * KEY is \"/foo\", and VAL is \"rw\".", 
      "+\t\t */", 
      "+\t\toptarg = strchr(optname, '=');", 
      "+\t\tif (optarg)", 
      "+\t\t\t*optarg++ = '\\0';", 
      "+\t\t/* incgen remount option (instead of old ioctl) */", 
      "+\t\tif (!strcmp(\"incgen\", optname)) {", 
      "+\t\t\terr = 0;", 
      "+\t\t\tgoto out_no_change;", 
      "+\t\t}", 
      "+", 
      "+\t\t/*", 
      "+\t\t * All of our options take an argument now.  (Insert ones", 
      "+\t\t * that don't above this check.)  So at this stage optname", 
      "+\t\t * contains the CMD part and optarg contains the ARG part.", 
      "+\t\t */", 
      "+\t\tif (!optarg || !*optarg) {", 
      "+\t\t\tprintk(KERN_ERR \"unionfs: all remount options require \"", 
      "+\t\t\t       \"an argument (%s)\\n\", optname);", 
      "+\t\t\terr = -EINVAL;", 
      "+\t\t\tgoto out_release;", 
      "+\t\t}", 
      "+", 
      "+\t\tif (!strcmp(\"add\", optname)) {", 
      "+\t\t\terr = do_remount_add_option(optarg, new_branches,", 
      "+\t\t\t\t\t\t    tmp_data,", 
      "+\t\t\t\t\t\t    tmp_lower_paths,", 
      "+\t\t\t\t\t\t    &new_high_branch_id);", 
      "+\t\t\tif (err)", 
      "+\t\t\t\tgoto out_release;", 
      "+\t\t\tnew_branches++;", 
      "+\t\t\tif (new_branches > UNIONFS_MAX_BRANCHES) {", 
      "+\t\t\t\tprintk(KERN_ERR \"unionfs: command exceeds \"", 
      "+\t\t\t\t       \"%d branches\\n\", UNIONFS_MAX_BRANCHES);", 
      "+\t\t\t\terr = -E2BIG;", 
      "+\t\t\t\tgoto out_release;", 
      "+\t\t\t}", 
      "+\t\t\tcontinue;", 
      "+\t\t}", 
      "+\t\tif (!strcmp(\"del\", optname)) {", 
      "+\t\t\terr = do_remount_del_option(optarg, new_branches,", 
      "+\t\t\t\t\t\t    tmp_data,", 
      "+\t\t\t\t\t\t    tmp_lower_paths);", 
      "+\t\t\tif (err)", 
      "+\t\t\t\tgoto out_release;", 
      "+\t\t\tnew_branches--;", 
      "+\t\t\tcontinue;", 
      "+\t\t}", 
      "+\t\tif (!strcmp(\"mode\", optname)) {", 
      "+\t\t\terr = do_remount_mode_option(optarg, new_branches,", 
      "+\t\t\t\t\t\t     tmp_data,", 
      "+\t\t\t\t\t\t     tmp_lower_paths);", 
      "+\t\t\tif (err)", 
      "+\t\t\t\tgoto out_release;", 
      "+\t\t\tcontinue;", 
      "+\t\t}", 
      "+", 
      "+\t\t/*", 
      "+\t\t * When you use \"mount -o remount,ro\", mount(8) will", 
      "+\t\t * reportedly pass the original dirs= string from", 
      "+\t\t * /proc/mounts.  So for now, we have to ignore dirs= and", 
      "+\t\t * not consider it an error, unless we want to allow users", 
      "+\t\t * to pass dirs= in remount.  Note that to allow the VFS to", 
      "+\t\t * actually process the ro/rw remount options, we have to", 
      "+\t\t * return 0 from this function.", 
      "+\t\t */", 
      "+\t\tif (!strcmp(\"dirs\", optname)) {", 
      "+\t\t\tprintk(KERN_WARNING", 
      "+\t\t\t       \"unionfs: remount ignoring option \\\"%s\\\"\\n\",", 
      "+\t\t\t       optname);", 
      "+\t\t\tcontinue;", 
      "+\t\t}", 
      "+", 
      "+\t\terr = -EINVAL;", 
      "+\t\tprintk(KERN_ERR", 
      "+\t\t       \"unionfs: unrecognized option \\\"%s\\\"\\n\", optname);", 
      "+\t\tgoto out_release;", 
      "+\t}", 
      "+", 
      "+out_no_change:", 
      "+", 
      "+\t/******************************************************************", 
      "+\t * WE'RE ALMOST DONE: check if leftmost branch might be read-only,", 
      "+\t * see if we need to allocate a small-sized new vector, copy the", 
      "+\t * vectors to their correct place, release the refcnt of the older", 
      "+\t * ones, and return.  Also handle invalidating any pages that will", 
      "+\t * have to be re-read.", 
      "+\t *******************************************************************/", 
      "+", 
      "+\tif (!(tmp_data[0].branchperms & MAY_WRITE)) {", 
      "+\t\tprintk(KERN_ERR \"unionfs: leftmost branch cannot be read-only \"", 
      "+\t\t       \"(use \\\"remount,ro\\\" to create a read-only union)\\n\");", 
      "+\t\terr = -EINVAL;", 
      "+\t\tgoto out_release;", 
      "+\t}", 
      "+", 
      "+\t/* (re)allocate space for new pointers to lower dentry */", 
      "+\tsize = new_branches * sizeof(struct unionfs_data);", 
      "+\tnew_data = krealloc(tmp_data, size, GFP_KERNEL);", 
      "+\tif (unlikely(!new_data)) {", 
      "+\t\terr = -ENOMEM;", 
      "+\t\tgoto out_release;", 
      "+\t}", 
      "+", 
      "+\t/* allocate space for new pointers to lower paths */", 
      "+\tsize = new_branches * sizeof(struct path);", 
      "+\tnew_lower_paths = krealloc(tmp_lower_paths, size, GFP_KERNEL);", 
      "+\tif (unlikely(!new_lower_paths)) {", 
      "+\t\terr = -ENOMEM;", 
      "+\t\tgoto out_release;", 
      "+\t}", 
      "+", 
      "+\t/* allocate space for new pointers to lower inodes */", 
      "+\tnew_lower_inodes = kcalloc(new_branches,", 
      "+\t\t\t\t   sizeof(struct inode *), GFP_KERNEL);", 
      "+\tif (unlikely(!new_lower_inodes)) {", 
      "+\t\terr = -ENOMEM;", 
      "+\t\tgoto out_release;", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * OK, just before we actually put the new set of branches in place,", 
      "+\t * we need to ensure that our own f/s has no dirty objects left.", 
      "+\t * Luckily, do_remount_sb() already calls shrink_dcache_sb(sb) and", 
      "+\t * fsync_super(sb), taking care of dentries, inodes, and dirty", 
      "+\t * pages.  So all that's left is for us to invalidate any leftover", 
      "+\t * (non-dirty) pages to ensure that they will be re-read from the", 
      "+\t * new lower branches (and to support mmap).", 
      "+\t */", 
      "+", 
      "+\t/*", 
      "+\t * Once we finish the remounting successfully, our superblock", 
      "+\t * generation number will have increased.  This will be detected by", 
      "+\t * our dentry-revalidation code upon subsequent f/s operations", 
      "+\t * through unionfs.  The revalidation code will rebuild the union of", 
      "+\t * lower inodes for a given unionfs inode and invalidate any pages", 
      "+\t * of such \"stale\" inodes (by calling our purge_inode_data", 
      "+\t * function).  This revalidation will happen lazily and", 
      "+\t * incrementally, as users perform operations on cached inodes.  We", 
      "+\t * would like to encourage this revalidation to happen sooner if", 
      "+\t * possible, so we like to try to invalidate as many other pages in", 
      "+\t * our superblock as we can.  We used to call drop_pagecache_sb() or", 
      "+\t * a variant thereof, but either method was racy (drop_caches alone", 
      "+\t * is known to be racy).  So now we let the revalidation happen on a", 
      "+\t * per file basis in ->d_revalidate.", 
      "+\t */", 
      "+", 
      "+\t/* grab new lower super references; release old ones */", 
      "+\tfor (i = 0; i < new_branches; i++)", 
      "+\t\tatomic_inc(&new_data[i].sb->s_active);", 
      "+\tfor (i = 0; i < sbmax(sb); i++)", 
      "+\t\tatomic_dec(&UNIONFS_SB(sb)->data[i].sb->s_active);", 
      "+", 
      "+\t/* copy new vectors into their correct place */", 
      "+\ttmp_data = UNIONFS_SB(sb)->data;", 
      "+\tUNIONFS_SB(sb)->data = new_data;", 
      "+\tnew_data = NULL;\t/* so don't free good pointers below */", 
      "+\ttmp_lower_paths = UNIONFS_D(sb->s_root)->lower_paths;", 
      "+\tUNIONFS_D(sb->s_root)->lower_paths = new_lower_paths;", 
      "+\tnew_lower_paths = NULL;\t/* so don't free good pointers below */", 
      "+", 
      "+\t/* update our unionfs_sb_info and root dentry index of last branch */", 
      "+\ti = sbmax(sb);\t\t/* save no. of branches to release at end */", 
      "+\tsbend(sb) = new_branches - 1;", 
      "+\tdbend(sb->s_root) = new_branches - 1;", 
      "+\told_ibstart = ibstart(sb->s_root->d_inode);", 
      "+\told_ibend = ibend(sb->s_root->d_inode);", 
      "+\tibend(sb->s_root->d_inode) = new_branches - 1;", 
      "+\tUNIONFS_D(sb->s_root)->bcount = new_branches;", 
      "+\tnew_branches = i; /* no. of branches to release below */", 
      "+", 
      "+\t/*", 
      "+\t * Update lower inodes: 3 steps", 
      "+\t * 1. grab ref on all new lower inodes", 
      "+\t */", 
      "+\tfor (i = dbstart(sb->s_root); i <= dbend(sb->s_root); i++) {", 
      "+\t\tstruct dentry *lower_dentry =", 
      "+\t\t\tunionfs_lower_dentry_idx(sb->s_root, i);", 
      "+\t\tigrab(lower_dentry->d_inode);", 
      "+\t\tnew_lower_inodes[i] = lower_dentry->d_inode;", 
      "+\t}", 
      "+\t/* 2. release reference on all older lower inodes */", 
      "+\tiput_lowers(sb->s_root->d_inode, old_ibstart, old_ibend, true);", 
      "+\t/* 3. update root dentry's inode to new lower_inodes array */", 
      "+\tUNIONFS_I(sb->s_root->d_inode)->lower_inodes = new_lower_inodes;", 
      "+\tnew_lower_inodes = NULL;", 
      "+", 
      "+\t/* maxbytes may have changed */", 
      "+\tsb->s_maxbytes = unionfs_lower_super_idx(sb, 0)->s_maxbytes;", 
      "+\t/* update high branch ID */", 
      "+\tsbhbid(sb) = new_high_branch_id;", 
      "+", 
      "+\t/* update our sb->generation for revalidating objects */", 
      "+\ti = atomic_inc_return(&UNIONFS_SB(sb)->generation);", 
      "+\tatomic_set(&UNIONFS_D(sb->s_root)->generation, i);", 
      "+\tatomic_set(&UNIONFS_I(sb->s_root->d_inode)->generation, i);", 
      "+\tif (!(*flags & MS_SILENT))", 
      "+\t\tpr_info(\"unionfs: %s: new generation number %d\\n\",", 
      "+\t\t\tUNIONFS_SB(sb)->dev_name, i);", 
      "+\t/* finally, update the root dentry's times */", 
      "+\tunionfs_copy_attr_times(sb->s_root->d_inode);", 
      "+\terr = 0;\t\t/* reset to success */", 
      "+", 
      "+\t/*", 
      "+\t * The code above falls through to the next label, and releases the", 
      "+\t * refcnts of the older ones (stored in tmp_*): if we fell through", 
      "+\t * here, it means success.  However, if we jump directly to this", 
      "+\t * label from any error above, then an error occurred after we", 
      "+\t * grabbed various refcnts, and so we have to release the", 
      "+\t * temporarily constructed structures.", 
      "+\t */", 
      "+out_release:", 
      "+\t/* no need to cleanup/release anything in tmp_data */", 
      "+\tif (tmp_lower_paths)", 
      "+\t\tfor (i = 0; i < new_branches; i++)", 
      "+\t\t\tpathput(&tmp_lower_paths[i]);", 
      "+out_free:", 
      "+\tkfree(tmp_lower_paths);", 
      "+\tkfree(tmp_data);", 
      "+\tkfree(new_lower_paths);", 
      "+\tkfree(new_data);", 
      "+\tkfree(new_lower_inodes);", 
      "+out_error:", 
      "+\tunionfs_check_dentry(sb->s_root);", 
      "+\tunionfs_write_unlock(sb);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Called by iput() when the inode reference count reached zero", 
      "+ * and the inode is not hashed anywhere.  Used to clear anything", 
      "+ * that needs to be, before the inode is completely destroyed and put", 
      "+ * on the inode free list.", 
      "+ *", 
      "+ * No need to lock sb info's rwsem.", 
      "+ */", 
      "+static void unionfs_clear_inode(struct inode *inode)", 
      "+{", 
      "+\tint bindex, bstart, bend;", 
      "+\tstruct inode *lower_inode;", 
      "+\tstruct list_head *pos, *n;", 
      "+\tstruct unionfs_dir_state *rdstate;", 
      "+", 
      "+\tlist_for_each_safe(pos, n, &UNIONFS_I(inode)->readdircache) {", 
      "+\t\trdstate = list_entry(pos, struct unionfs_dir_state, cache);", 
      "+\t\tlist_del(&rdstate->cache);", 
      "+\t\tfree_rdstate(rdstate);", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * Decrement a reference to a lower_inode, which was incremented", 
      "+\t * by our read_inode when it was created initially.", 
      "+\t */", 
      "+\tbstart = ibstart(inode);", 
      "+\tbend = ibend(inode);", 
      "+\tif (bstart >= 0) {", 
      "+\t\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\t\tlower_inode = unionfs_lower_inode_idx(inode, bindex);", 
      "+\t\t\tif (!lower_inode)", 
      "+\t\t\t\tcontinue;", 
      "+\t\t\tunionfs_set_lower_inode_idx(inode, bindex, NULL);", 
      "+\t\t\t/* see Documentation/filesystems/unionfs/issues.txt */", 
      "+\t\t\tlockdep_off();", 
      "+\t\t\tiput(lower_inode);", 
      "+\t\t\tlockdep_on();", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\tkfree(UNIONFS_I(inode)->lower_inodes);", 
      "+\tUNIONFS_I(inode)->lower_inodes = NULL;", 
      "+}", 
      "+", 
      "+static struct inode *unionfs_alloc_inode(struct super_block *sb)", 
      "+{", 
      "+\tstruct unionfs_inode_info *i;", 
      "+", 
      "+\ti = kmem_cache_alloc(unionfs_inode_cachep, GFP_KERNEL);", 
      "+\tif (unlikely(!i))", 
      "+\t\treturn NULL;", 
      "+", 
      "+\t/* memset everything up to the inode to 0 */", 
      "+\tmemset(i, 0, offsetof(struct unionfs_inode_info, vfs_inode));", 
      "+", 
      "+\ti->vfs_inode.i_version = 1;", 
      "+\treturn &i->vfs_inode;", 
      "+}", 
      "+", 
      "+static void unionfs_destroy_inode(struct inode *inode)", 
      "+{", 
      "+\tkmem_cache_free(unionfs_inode_cachep, UNIONFS_I(inode));", 
      "+}", 
      "+", 
      "+/* unionfs inode cache constructor */", 
      "+static void init_once(void *v, struct kmem_cache *cachep, unsigned long flags)", 
      "+{", 
      "+\tstruct unionfs_inode_info *i = v;", 
      "+", 
      "+\tinode_init_once(&i->vfs_inode);", 
      "+}", 
      "+", 
      "+int unionfs_init_inode_cache(void)", 
      "+{", 
      "+\tint err = 0;", 
      "+", 
      "+\tunionfs_inode_cachep =", 
      "+\t\tkmem_cache_create(\"unionfs_inode_cache\",", 
      "+\t\t\t\t  sizeof(struct unionfs_inode_info), 0,", 
      "+\t\t\t\t  SLAB_RECLAIM_ACCOUNT, init_once);", 
      "+\tif (unlikely(!unionfs_inode_cachep))", 
      "+\t\terr = -ENOMEM;", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/* unionfs inode cache destructor */", 
      "+void unionfs_destroy_inode_cache(void)", 
      "+{", 
      "+\tif (unionfs_inode_cachep)", 
      "+\t\tkmem_cache_destroy(unionfs_inode_cachep);", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Called when we have a dirty inode, right here we only throw out", 
      "+ * parts of our readdir list that are too old.", 
      "+ *", 
      "+ * No need to grab sb info's rwsem.", 
      "+ */", 
      "+static int unionfs_write_inode(struct inode *inode, int sync)", 
      "+{", 
      "+\tstruct list_head *pos, *n;", 
      "+\tstruct unionfs_dir_state *rdstate;", 
      "+", 
      "+\tspin_lock(&UNIONFS_I(inode)->rdlock);", 
      "+\tlist_for_each_safe(pos, n, &UNIONFS_I(inode)->readdircache) {", 
      "+\t\trdstate = list_entry(pos, struct unionfs_dir_state, cache);", 
      "+\t\t/* We keep this list in LRU order. */", 
      "+\t\tif ((rdstate->access + RDCACHE_JIFFIES) > jiffies)", 
      "+\t\t\tbreak;", 
      "+\t\tUNIONFS_I(inode)->rdcount--;", 
      "+\t\tlist_del(&rdstate->cache);", 
      "+\t\tfree_rdstate(rdstate);", 
      "+\t}", 
      "+\tspin_unlock(&UNIONFS_I(inode)->rdlock);", 
      "+", 
      "+\treturn 0;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Used only in nfs, to kill any pending RPC tasks, so that subsequent", 
      "+ * code can actually succeed and won't leave tasks that need handling.", 
      "+ */", 
      "+static void unionfs_umount_begin(struct vfsmount *mnt, int flags)", 
      "+{", 
      "+\tstruct super_block *sb, *lower_sb;", 
      "+\tstruct vfsmount *lower_mnt;", 
      "+\tint bindex, bstart, bend;", 
      "+", 
      "+\tif (!(flags & MNT_FORCE))", 
      "+\t\t/*", 
      "+\t\t * we are not being MNT_FORCE'd, therefore we should emulate", 
      "+\t\t * old behavior", 
      "+\t\t */", 
      "+\t\treturn;", 
      "+", 
      "+\tsb = mnt->mnt_sb;", 
      "+", 
      "+\tunionfs_read_lock(sb, UNIONFS_SMUTEX_CHILD);", 
      "+", 
      "+\tbstart = sbstart(sb);", 
      "+\tbend = sbend(sb);", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tlower_mnt = unionfs_lower_mnt_idx(sb->s_root, bindex);", 
      "+\t\tlower_sb = unionfs_lower_super_idx(sb, bindex);", 
      "+", 
      "+\t\tif (lower_mnt && lower_sb && lower_sb->s_op &&", 
      "+\t\t    lower_sb->s_op->umount_begin)", 
      "+\t\t\tlower_sb->s_op->umount_begin(lower_mnt, flags);", 
      "+\t}", 
      "+", 
      "+\tunionfs_read_unlock(sb);", 
      "+}", 
      "+", 
      "+static int unionfs_show_options(struct seq_file *m, struct vfsmount *mnt)", 
      "+{", 
      "+\tstruct super_block *sb = mnt->mnt_sb;", 
      "+\tint ret = 0;", 
      "+\tchar *tmp_page;", 
      "+\tchar *path;", 
      "+\tint bindex, bstart, bend;", 
      "+\tint perms;", 
      "+", 
      "+\tunionfs_read_lock(sb, UNIONFS_SMUTEX_CHILD);", 
      "+", 
      "+\tunionfs_lock_dentry(sb->s_root, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\ttmp_page = (char *) __get_free_page(GFP_KERNEL);", 
      "+\tif (unlikely(!tmp_page)) {", 
      "+\t\tret = -ENOMEM;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tbstart = sbstart(sb);", 
      "+\tbend = sbend(sb);", 
      "+", 
      "+\tseq_printf(m, \",dirs=\");", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tpath = d_path(unionfs_lower_dentry_idx(sb->s_root, bindex),", 
      "+\t\t\t      unionfs_lower_mnt_idx(sb->s_root, bindex),", 
      "+\t\t\t      tmp_page, PAGE_SIZE);", 
      "+\t\tif (IS_ERR(path)) {", 
      "+\t\t\tret = PTR_ERR(path);", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+", 
      "+\t\tperms = branchperms(sb, bindex);", 
      "+", 
      "+\t\tseq_printf(m, \"%s=%s\", path,", 
      "+\t\t\t   perms & MAY_WRITE ? \"rw\" : \"ro\");", 
      "+\t\tif (bindex != bend)", 
      "+\t\t\tseq_printf(m, \":\");", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\tfree_page((unsigned long) tmp_page);", 
      "+", 
      "+\tunionfs_unlock_dentry(sb->s_root);", 
      "+", 
      "+\tunionfs_read_unlock(sb);", 
      "+", 
      "+\treturn ret;", 
      "+}", 
      "+", 
      "+struct super_operations unionfs_sops = {", 
      "+\t.read_inode\t= unionfs_read_inode,", 
      "+\t.delete_inode\t= unionfs_delete_inode,", 
      "+\t.put_super\t= unionfs_put_super,", 
      "+\t.statfs\t\t= unionfs_statfs,", 
      "+\t.remount_fs\t= unionfs_remount_fs,", 
      "+\t.clear_inode\t= unionfs_clear_inode,", 
      "+\t.umount_begin\t= unionfs_umount_begin,", 
      "+\t.show_options\t= unionfs_show_options,", 
      "+\t.write_inode\t= unionfs_write_inode,", 
      "+\t.alloc_inode\t= unionfs_alloc_inode,", 
      "+\t.destroy_inode\t= unionfs_destroy_inode,", 
      "+};"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/union.h", 
    [
      "diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h", 
      "new file mode 100644", 
      "index 0000000..0bca4a6", 
      "--- dev/null", 
      "+++ b/fs/unionfs/union.h", 
      "@@ -0,0 +1,653 @@", 
      "+/*", 
      "+ * Copyright (c) 2003-2009 Erez Zadok", 
      "+ * Copyright (c) 2003-2006 Charles P. Wright", 
      "+ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek", 
      "+ * 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-2009 Stony Brook University", 
      "+ * Copyright (c) 2003-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#ifndef _UNION_H_", 
      "+#define _UNION_H_", 
      "+", 
      "+#include <linux/dcache.h>", 
      "+#include <linux/file.h>", 
      "+#include <linux/list.h>", 
      "+#include <linux/fs.h>", 
      "+#include <linux/mm.h>", 
      "+#include <linux/module.h>", 
      "+#include <linux/mount.h>", 
      "+#include <linux/namei.h>", 
      "+#include <linux/page-flags.h>", 
      "+#include <linux/pagemap.h>", 
      "+#include <linux/poll.h>", 
      "+#include <linux/security.h>", 
      "+#include <linux/seq_file.h>", 
      "+#include <linux/slab.h>", 
      "+#include <linux/spinlock.h>", 
      "+#include <linux/smp_lock.h>", 
      "+#include <linux/statfs.h>", 
      "+#include <linux/string.h>", 
      "+#include <linux/vmalloc.h>", 
      "+#include <linux/writeback.h>", 
      "+#include <linux/buffer_head.h>", 
      "+#include <linux/xattr.h>", 
      "+#include <linux/fs_stack.h>", 
      "+#include <linux/magic.h>", 
      "+#include <linux/log2.h>", 
      "+#include <linux/poison.h>", 
      "+#include <linux/mman.h>", 
      "+#include <linux/backing-dev.h>", 
      "+#include <linux/splice.h>", 
      "+", 
      "+#include <asm/system.h>", 
      "+", 
      "+#include <linux/union_fs.h>", 
      "+", 
      "+/* the file system name */", 
      "+#define UNIONFS_NAME \"unionfs\"", 
      "+", 
      "+/* unionfs root inode number */", 
      "+#define UNIONFS_ROOT_INO     1", 
      "+", 
      "+/* number of times we try to get a unique temporary file name */", 
      "+#define GET_TMPNAM_MAX_RETRY\t5", 
      "+", 
      "+/* maximum number of branches we support, to avoid memory blowup */", 
      "+#define UNIONFS_MAX_BRANCHES\t128", 
      "+", 
      "+/* minimum time (seconds) required for time-based cache-coherency */", 
      "+#define UNIONFS_MIN_CC_TIME\t3", 
      "+", 
      "+/* Operations vectors defined in specific files. */", 
      "+extern struct file_operations unionfs_main_fops;", 
      "+extern struct file_operations unionfs_dir_fops;", 
      "+extern struct inode_operations unionfs_main_iops;", 
      "+extern struct inode_operations unionfs_dir_iops;", 
      "+extern struct inode_operations unionfs_symlink_iops;", 
      "+extern struct super_operations unionfs_sops;", 
      "+extern struct dentry_operations unionfs_dops;", 
      "+extern struct address_space_operations unionfs_aops, unionfs_dummy_aops;", 
      "+extern struct vm_operations_struct unionfs_vm_ops;", 
      "+", 
      "+/* How long should an entry be allowed to persist */", 
      "+#define RDCACHE_JIFFIES\t(5*HZ)", 
      "+", 
      "+/* compatibility with Real-Time patches */", 
      "+#ifdef CONFIG_PREEMPT_RT", 
      "+# define unionfs_rw_semaphore\tcompat_rw_semaphore", 
      "+#else /* not CONFIG_PREEMPT_RT */", 
      "+# define unionfs_rw_semaphore\trw_semaphore", 
      "+#endif /* not CONFIG_PREEMPT_RT */", 
      "+", 
      "+#ifndef noinline_for_stack", 
      "+# define noinline_for_stack noinline", 
      "+#endif /* not noinline_for_stack */", 
      "+", 
      "+/* file private data. */", 
      "+struct unionfs_file_info {", 
      "+\tint bstart;", 
      "+\tint bend;", 
      "+\tatomic_t generation;", 
      "+", 
      "+\tstruct unionfs_dir_state *rdstate;", 
      "+\tstruct file **lower_files;", 
      "+\tint *saved_branch_ids; /* IDs of branches when file was opened */", 
      "+\tstruct vm_operations_struct *lower_vm_ops;", 
      "+\tbool wrote_to_file;\t/* for delayed copyup */", 
      "+};", 
      "+", 
      "+/* unionfs inode data in memory */", 
      "+struct unionfs_inode_info {", 
      "+\tint bstart;", 
      "+\tint bend;", 
      "+\tatomic_t generation;", 
      "+\t/* Stuff for readdir over NFS. */", 
      "+\tspinlock_t rdlock;", 
      "+\tstruct list_head readdircache;", 
      "+\tint rdcount;", 
      "+\tint hashsize;", 
      "+\tint cookie;", 
      "+", 
      "+\t/* The lower inodes */", 
      "+\tstruct inode **lower_inodes;", 
      "+", 
      "+\tstruct inode vfs_inode;", 
      "+};", 
      "+", 
      "+/* unionfs dentry data in memory */", 
      "+struct unionfs_dentry_info {", 
      "+\t/*", 
      "+\t * The semaphore is used to lock the dentry as soon as we get into a", 
      "+\t * unionfs function from the VFS.  Our lock ordering is that children", 
      "+\t * go before their parents.", 
      "+\t */", 
      "+\tstruct mutex lock;", 
      "+\tint bstart;", 
      "+\tint bend;", 
      "+\tint bopaque;", 
      "+\tint bcount;", 
      "+\tatomic_t generation;", 
      "+\tstruct path *lower_paths;", 
      "+};", 
      "+", 
      "+/* These are the pointers to our various objects. */", 
      "+struct unionfs_data {", 
      "+\tstruct super_block *sb;\t/* lower super_block */", 
      "+\tatomic_t open_files;\t/* number of open files on branch */", 
      "+\tint branchperms;", 
      "+\tint branch_id;\t\t/* unique branch ID at re/mount time */", 
      "+};", 
      "+", 
      "+/* unionfs super-block data in memory */", 
      "+struct unionfs_sb_info {", 
      "+\tint bend;", 
      "+", 
      "+\tatomic_t generation;", 
      "+", 
      "+\t/*", 
      "+\t * This rwsem is used to make sure that a branch management", 
      "+\t * operation...", 
      "+\t *   1) will not begin before all currently in-flight operations", 
      "+\t *      complete.", 
      "+\t *   2) any new operations do not execute until the currently", 
      "+\t *      running branch management operation completes.", 
      "+\t *", 
      "+\t * The write_lock_owner records the PID of the task which grabbed", 
      "+\t * the rw_sem for writing.  If the same task also tries to grab the", 
      "+\t * read lock, we allow it.  This prevents a self-deadlock when", 
      "+\t * branch-management is used on a pivot_root'ed union, because we", 
      "+\t * have to ->lookup paths which belong to the same union.", 
      "+\t */", 
      "+\tstruct unionfs_rw_semaphore rwsem;", 
      "+\tpid_t write_lock_owner;\t/* PID of rw_sem owner (write lock) */", 
      "+\tint high_branch_id;\t/* last unique branch ID given */", 
      "+\tchar *dev_name;\t\t/* to identify different unions in pr_debug */", 
      "+\tstruct unionfs_data *data;", 
      "+};", 
      "+", 
      "+/*", 
      "+ * structure for making the linked list of entries by readdir on left branch", 
      "+ * to compare with entries on right branch", 
      "+ */", 
      "+struct filldir_node {", 
      "+\tstruct list_head file_list;\t/* list for directory entries */", 
      "+\tchar *name;\t\t/* name entry */", 
      "+\tint hash;\t\t/* name hash */", 
      "+\tint namelen;\t\t/* name len since name is not 0 terminated */", 
      "+", 
      "+\t/*", 
      "+\t * we can check for duplicate whiteouts and files in the same branch", 
      "+\t * in order to return -EIO.", 
      "+\t */", 
      "+\tint bindex;", 
      "+", 
      "+\t/* is this a whiteout entry? */", 
      "+\tint whiteout;", 
      "+", 
      "+\t/* Inline name, so we don't need to separately kmalloc small ones */", 
      "+\tchar iname[DNAME_INLINE_LEN_MIN];", 
      "+};", 
      "+", 
      "+/* Directory hash table. */", 
      "+struct unionfs_dir_state {", 
      "+\tunsigned int cookie;\t/* the cookie, based off of rdversion */", 
      "+\tunsigned int offset;\t/* The entry we have returned. */", 
      "+\tint bindex;", 
      "+\tloff_t dirpos;\t\t/* offset within the lower level directory */", 
      "+\tint size;\t\t/* How big is the hash table? */", 
      "+\tint hashentries;\t/* How many entries have been inserted? */", 
      "+\tunsigned long access;", 
      "+", 
      "+\t/* This cache list is used when the inode keeps us around. */", 
      "+\tstruct list_head cache;", 
      "+\tstruct list_head list[0];", 
      "+};", 
      "+", 
      "+/* externs needed for fanout.h or sioq.h */", 
      "+extern int unionfs_get_nlinks(const struct inode *inode);", 
      "+extern void unionfs_copy_attr_times(struct inode *upper);", 
      "+extern void unionfs_copy_attr_all(struct inode *dest, const struct inode *src);", 
      "+", 
      "+/* include miscellaneous macros */", 
      "+#include \"fanout.h\"", 
      "+#include \"sioq.h\"", 
      "+", 
      "+/* externs for cache creation/deletion routines */", 
      "+extern void unionfs_destroy_filldir_cache(void);", 
      "+extern int unionfs_init_filldir_cache(void);", 
      "+extern int unionfs_init_inode_cache(void);", 
      "+extern void unionfs_destroy_inode_cache(void);", 
      "+extern int unionfs_init_dentry_cache(void);", 
      "+extern void unionfs_destroy_dentry_cache(void);", 
      "+", 
      "+/* Initialize and free readdir-specific  state. */", 
      "+extern int init_rdstate(struct file *file);", 
      "+extern struct unionfs_dir_state *alloc_rdstate(struct inode *inode,", 
      "+\t\t\t\t\t       int bindex);", 
      "+extern struct unionfs_dir_state *find_rdstate(struct inode *inode,", 
      "+\t\t\t\t\t      loff_t fpos);", 
      "+extern void free_rdstate(struct unionfs_dir_state *state);", 
      "+extern int add_filldir_node(struct unionfs_dir_state *rdstate,", 
      "+\t\t\t    const char *name, int namelen, int bindex,", 
      "+\t\t\t    int whiteout);", 
      "+extern struct filldir_node *find_filldir_node(struct unionfs_dir_state *rdstate,", 
      "+\t\t\t\t\t      const char *name, int namelen,", 
      "+\t\t\t\t\t      int is_whiteout);", 
      "+", 
      "+extern struct dentry **alloc_new_dentries(int objs);", 
      "+extern struct unionfs_data *alloc_new_data(int objs);", 
      "+", 
      "+/* We can only use 32-bits of offset for rdstate --- blech! */", 
      "+#define DIREOF (0xfffff)", 
      "+#define RDOFFBITS 20\t\t/* This is the number of bits in DIREOF. */", 
      "+#define MAXRDCOOKIE (0xfff)", 
      "+/* Turn an rdstate into an offset. */", 
      "+static inline off_t rdstate2offset(struct unionfs_dir_state *buf)", 
      "+{", 
      "+\toff_t tmp;", 
      "+", 
      "+\ttmp = ((buf->cookie & MAXRDCOOKIE) << RDOFFBITS)", 
      "+\t\t| (buf->offset & DIREOF);", 
      "+\treturn tmp;", 
      "+}", 
      "+", 
      "+/* Macros for locking a super_block. */", 
      "+enum unionfs_super_lock_class {", 
      "+\tUNIONFS_SMUTEX_NORMAL,", 
      "+\tUNIONFS_SMUTEX_PARENT,\t/* when locking on behalf of file */", 
      "+\tUNIONFS_SMUTEX_CHILD,\t/* when locking on behalf of dentry */", 
      "+};", 
      "+static inline void unionfs_read_lock(struct super_block *sb, int subclass)", 
      "+{", 
      "+\tif (UNIONFS_SB(sb)->write_lock_owner &&", 
      "+\t    UNIONFS_SB(sb)->write_lock_owner == current->pid)", 
      "+\t\treturn;", 
      "+\tdown_read_nested(&UNIONFS_SB(sb)->rwsem, subclass);", 
      "+}", 
      "+static inline void unionfs_read_unlock(struct super_block *sb)", 
      "+{", 
      "+\tif (UNIONFS_SB(sb)->write_lock_owner &&", 
      "+\t    UNIONFS_SB(sb)->write_lock_owner == current->pid)", 
      "+\t\treturn;", 
      "+\tup_read(&UNIONFS_SB(sb)->rwsem);", 
      "+}", 
      "+static inline void unionfs_write_lock(struct super_block *sb)", 
      "+{", 
      "+\tdown_write(&UNIONFS_SB(sb)->rwsem);", 
      "+\tUNIONFS_SB(sb)->write_lock_owner = current->pid;", 
      "+}", 
      "+static inline void unionfs_write_unlock(struct super_block *sb)", 
      "+{", 
      "+\tup_write(&UNIONFS_SB(sb)->rwsem);", 
      "+\tUNIONFS_SB(sb)->write_lock_owner = 0;", 
      "+}", 
      "+", 
      "+static inline void unionfs_double_lock_dentry(struct dentry *d1,", 
      "+\t\t\t\t\t      struct dentry *d2)", 
      "+{", 
      "+\tBUG_ON(d1 == d2);", 
      "+\tif (d1 < d2) {", 
      "+\t\tunionfs_lock_dentry(d1, UNIONFS_DMUTEX_PARENT);", 
      "+\t\tunionfs_lock_dentry(d2, UNIONFS_DMUTEX_CHILD);", 
      "+\t} else {", 
      "+\t\tunionfs_lock_dentry(d2, UNIONFS_DMUTEX_PARENT);", 
      "+\t\tunionfs_lock_dentry(d1, UNIONFS_DMUTEX_CHILD);", 
      "+\t}", 
      "+}", 
      "+", 
      "+static inline void unionfs_double_unlock_dentry(struct dentry *d1,", 
      "+\t\t\t\t\t\tstruct dentry *d2)", 
      "+{", 
      "+\tBUG_ON(d1 == d2);", 
      "+\tif (d1 < d2) { /* unlock in reverse order than double_lock_dentry */", 
      "+\t\tunionfs_unlock_dentry(d1);", 
      "+\t\tunionfs_unlock_dentry(d2);", 
      "+\t} else {", 
      "+\t\tunionfs_unlock_dentry(d2);", 
      "+\t\tunionfs_unlock_dentry(d1);", 
      "+\t}", 
      "+}", 
      "+", 
      "+static inline void unionfs_double_lock_parents(struct dentry *p1,", 
      "+\t\t\t\t\t       struct dentry *p2)", 
      "+{", 
      "+\tif (p1 == p2) {", 
      "+\t\tunionfs_lock_dentry(p1, UNIONFS_DMUTEX_REVAL_PARENT);", 
      "+\t\treturn;", 
      "+\t}", 
      "+\tif (p1 < p2) {", 
      "+\t\tunionfs_lock_dentry(p1, UNIONFS_DMUTEX_REVAL_PARENT);", 
      "+\t\tunionfs_lock_dentry(p2, UNIONFS_DMUTEX_REVAL_CHILD);", 
      "+\t} else {", 
      "+\t\tunionfs_lock_dentry(p2, UNIONFS_DMUTEX_REVAL_PARENT);", 
      "+\t\tunionfs_lock_dentry(p1, UNIONFS_DMUTEX_REVAL_CHILD);", 
      "+\t}", 
      "+}", 
      "+", 
      "+static inline void unionfs_double_unlock_parents(struct dentry *p1,", 
      "+\t\t\t\t\t\t struct dentry *p2)", 
      "+{", 
      "+\tif (p1 == p2) {", 
      "+\t\tunionfs_unlock_dentry(p1);", 
      "+\t\treturn;", 
      "+\t}", 
      "+\tif (p1 < p2) { /* unlock in reverse order of double_lock_parents */", 
      "+\t\tunionfs_unlock_dentry(p1);", 
      "+\t\tunionfs_unlock_dentry(p2);", 
      "+\t} else {", 
      "+\t\tunionfs_unlock_dentry(p2);", 
      "+\t\tunionfs_unlock_dentry(p1);", 
      "+\t}", 
      "+}", 
      "+", 
      "+extern int new_dentry_private_data(struct dentry *dentry, int subclass);", 
      "+extern int realloc_dentry_private_data(struct dentry *dentry);", 
      "+extern void free_dentry_private_data(struct dentry *dentry);", 
      "+extern void update_bstart(struct dentry *dentry);", 
      "+extern int init_lower_nd(struct nameidata *nd, unsigned int flags);", 
      "+extern void release_lower_nd(struct nameidata *nd, int err);", 
      "+", 
      "+/*", 
      "+ * EXTERNALS:", 
      "+ */", 
      "+", 
      "+/* replicates the directory structure up to given dentry in given branch */", 
      "+extern struct dentry *create_parents(struct inode *dir, struct dentry *dentry,", 
      "+\t\t\t\t     const char *name, int bindex);", 
      "+", 
      "+/* partial lookup */", 
      "+extern int unionfs_partial_lookup(struct dentry *dentry,", 
      "+\t\t\t\t  struct dentry *parent);", 
      "+extern struct dentry *unionfs_lookup_full(struct dentry *dentry,", 
      "+\t\t\t\t\t  struct dentry *parent,", 
      "+\t\t\t\t\t  int lookupmode);", 
      "+", 
      "+/* copies a file from dbstart to newbindex branch */", 
      "+extern int copyup_file(struct inode *dir, struct file *file, int bstart,", 
      "+\t\t       int newbindex, loff_t size);", 
      "+extern int copyup_named_file(struct inode *dir, struct file *file,", 
      "+\t\t\t     char *name, int bstart, int new_bindex,", 
      "+\t\t\t     loff_t len);", 
      "+/* copies a dentry from dbstart to newbindex branch */", 
      "+extern int copyup_dentry(struct inode *dir, struct dentry *dentry,", 
      "+\t\t\t int bstart, int new_bindex, const char *name,", 
      "+\t\t\t int namelen, struct file **copyup_file, loff_t len);", 
      "+/* helper functions for post-copyup actions */", 
      "+extern void unionfs_postcopyup_setmnt(struct dentry *dentry);", 
      "+extern void unionfs_postcopyup_release(struct dentry *dentry);", 
      "+", 
      "+/* Is this directory empty: 0 if it is empty, -ENOTEMPTY if not. */", 
      "+extern int check_empty(struct dentry *dentry, struct dentry *parent,", 
      "+\t\t       struct unionfs_dir_state **namelist);", 
      "+/* whiteout and opaque directory helpers */", 
      "+extern char *alloc_whname(const char *name, int len);", 
      "+extern bool is_whiteout_name(char **namep, int *namelenp);", 
      "+extern bool is_validname(const char *name);", 
      "+extern struct dentry *lookup_whiteout(const char *name,", 
      "+\t\t\t\t      struct dentry *lower_parent);", 
      "+extern struct dentry *find_first_whiteout(struct dentry *dentry);", 
      "+extern int unlink_whiteout(struct dentry *wh_dentry);", 
      "+extern int check_unlink_whiteout(struct dentry *dentry,", 
      "+\t\t\t\t struct dentry *lower_dentry, int bindex);", 
      "+extern int create_whiteout(struct dentry *dentry, int start);", 
      "+extern int delete_whiteouts(struct dentry *dentry, int bindex,", 
      "+\t\t\t    struct unionfs_dir_state *namelist);", 
      "+extern int is_opaque_dir(struct dentry *dentry, int bindex);", 
      "+extern int make_dir_opaque(struct dentry *dir, int bindex);", 
      "+extern void unionfs_set_max_namelen(long *namelen);", 
      "+", 
      "+extern void unionfs_reinterpose(struct dentry *this_dentry);", 
      "+extern struct super_block *unionfs_duplicate_super(struct super_block *sb);", 
      "+", 
      "+/* Locking functions. */", 
      "+extern int unionfs_setlk(struct file *file, int cmd, struct file_lock *fl);", 
      "+extern int unionfs_getlk(struct file *file, struct file_lock *fl);", 
      "+", 
      "+/* Common file operations. */", 
      "+extern int unionfs_file_revalidate(struct file *file, struct dentry *parent,", 
      "+\t\t\t\t   bool willwrite);", 
      "+extern int unionfs_open(struct inode *inode, struct file *file);", 
      "+extern int unionfs_file_release(struct inode *inode, struct file *file);", 
      "+extern int unionfs_flush(struct file *file, fl_owner_t id);", 
      "+extern long unionfs_ioctl(struct file *file, unsigned int cmd,", 
      "+\t\t\t  unsigned long arg);", 
      "+extern int unionfs_fsync(struct file *file, struct dentry *dentry,", 
      "+\t\t\t int datasync);", 
      "+extern int unionfs_fasync(int fd, struct file *file, int flag);", 
      "+", 
      "+/* Inode operations */", 
      "+extern int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry,", 
      "+\t\t\t  struct inode *new_dir, struct dentry *new_dentry);", 
      "+extern int unionfs_unlink(struct inode *dir, struct dentry *dentry);", 
      "+extern int unionfs_rmdir(struct inode *dir, struct dentry *dentry);", 
      "+", 
      "+extern bool __unionfs_d_revalidate(struct dentry *dentry,", 
      "+\t\t\t\t   struct dentry *parent, bool willwrite);", 
      "+extern bool is_negative_lower(const struct dentry *dentry);", 
      "+extern bool is_newer_lower(const struct dentry *dentry);", 
      "+extern void purge_sb_data(struct super_block *sb);", 
      "+", 
      "+/* The values for unionfs_interpose's flag. */", 
      "+#define INTERPOSE_DEFAULT\t0", 
      "+#define INTERPOSE_LOOKUP\t1", 
      "+#define INTERPOSE_REVAL\t\t2", 
      "+#define INTERPOSE_REVAL_NEG\t3", 
      "+#define INTERPOSE_PARTIAL\t4", 
      "+", 
      "+extern struct dentry *unionfs_interpose(struct dentry *this_dentry,", 
      "+\t\t\t\t\tstruct super_block *sb, int flag);", 
      "+", 
      "+#ifdef CONFIG_UNION_FS_XATTR", 
      "+/* Extended attribute functions. */", 
      "+extern void *unionfs_xattr_alloc(size_t size, size_t limit);", 
      "+static inline void unionfs_xattr_kfree(const void *p)", 
      "+{", 
      "+\tkfree(p);", 
      "+}", 
      "+extern ssize_t unionfs_getxattr(struct dentry *dentry, const char *name,", 
      "+\t\t\t\tvoid *value, size_t size);", 
      "+extern int unionfs_removexattr(struct dentry *dentry, const char *name);", 
      "+extern ssize_t unionfs_listxattr(struct dentry *dentry, char *list,", 
      "+\t\t\t\t size_t size);", 
      "+extern int unionfs_setxattr(struct dentry *dentry, const char *name,", 
      "+\t\t\t    const void *value, size_t size, int flags);", 
      "+#endif /* CONFIG_UNION_FS_XATTR */", 
      "+", 
      "+/* The root directory is unhashed, but isn't deleted. */", 
      "+static inline int d_deleted(struct dentry *d)", 
      "+{", 
      "+\treturn d_unhashed(d) && (d != d->d_sb->s_root);", 
      "+}", 
      "+", 
      "+/* unionfs_permission, check if we should bypass error to facilitate copyup */", 
      "+#define IS_COPYUP_ERR(err) ((err) == -EROFS)", 
      "+", 
      "+/* unionfs_open, check if we need to copyup the file */", 
      "+#define OPEN_WRITE_FLAGS (O_WRONLY | O_RDWR | O_APPEND)", 
      "+#define IS_WRITE_FLAG(flag) ((flag) & OPEN_WRITE_FLAGS)", 
      "+", 
      "+static inline int branchperms(const struct super_block *sb, int index)", 
      "+{", 
      "+\tBUG_ON(index < 0);", 
      "+\treturn UNIONFS_SB(sb)->data[index].branchperms;", 
      "+}", 
      "+", 
      "+static inline int set_branchperms(struct super_block *sb, int index, int perms)", 
      "+{", 
      "+\tBUG_ON(index < 0);", 
      "+\tUNIONFS_SB(sb)->data[index].branchperms = perms;", 
      "+\treturn perms;", 
      "+}", 
      "+", 
      "+/* Is this file on a read-only branch? */", 
      "+static inline int is_robranch_super(const struct super_block *sb, int index)", 
      "+{", 
      "+\tint ret;", 
      "+", 
      "+\tret = (!(branchperms(sb, index) & MAY_WRITE)) ? -EROFS : 0;", 
      "+\treturn ret;", 
      "+}", 
      "+", 
      "+/* Is this file on a read-only branch? */", 
      "+static inline int is_robranch_idx(const struct dentry *dentry, int index)", 
      "+{", 
      "+\tstruct super_block *lower_sb;", 
      "+", 
      "+\tBUG_ON(index < 0);", 
      "+", 
      "+\tif (!(branchperms(dentry->d_sb, index) & MAY_WRITE))", 
      "+\t\treturn -EROFS;", 
      "+", 
      "+\tlower_sb = unionfs_lower_super_idx(dentry->d_sb, index);", 
      "+\tBUG_ON(lower_sb == NULL);", 
      "+\t/*", 
      "+\t * test sb flags directly, not IS_RDONLY(lower_inode) because the", 
      "+\t * lower_dentry could be a negative.", 
      "+\t */", 
      "+\tif (lower_sb->s_flags & MS_RDONLY)", 
      "+\t\treturn -EROFS;", 
      "+", 
      "+\treturn 0;", 
      "+}", 
      "+", 
      "+static inline int is_robranch(const struct dentry *dentry)", 
      "+{", 
      "+\tint index;", 
      "+", 
      "+\tindex = UNIONFS_D(dentry)->bstart;", 
      "+\tBUG_ON(index < 0);", 
      "+", 
      "+\treturn is_robranch_idx(dentry, index);", 
      "+}", 
      "+", 
      "+/*", 
      "+ * EXTERNALS:", 
      "+ */", 
      "+extern int check_branch(struct nameidata *nd);", 
      "+extern int parse_branch_mode(const char *name, int *perms);", 
      "+", 
      "+/* locking helpers */", 
      "+static inline struct dentry *lock_parent(struct dentry *dentry)", 
      "+{", 
      "+\tstruct dentry *dir = dget_parent(dentry);", 
      "+\tmutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);", 
      "+\treturn dir;", 
      "+}", 
      "+static inline struct dentry *lock_parent_wh(struct dentry *dentry)", 
      "+{", 
      "+\tstruct dentry *dir = dget_parent(dentry);", 
      "+", 
      "+\tmutex_lock_nested(&dir->d_inode->i_mutex, UNIONFS_DMUTEX_WHITEOUT);", 
      "+\treturn dir;", 
      "+}", 
      "+", 
      "+static inline void unlock_dir(struct dentry *dir)", 
      "+{", 
      "+\tmutex_unlock(&dir->d_inode->i_mutex);", 
      "+\tdput(dir);", 
      "+}", 
      "+", 
      "+static inline struct vfsmount *unionfs_mntget(struct dentry *dentry,", 
      "+\t\t\t\t\t      int bindex)", 
      "+{", 
      "+\tstruct vfsmount *mnt;", 
      "+", 
      "+\tBUG_ON(!dentry || bindex < 0);", 
      "+", 
      "+\tmnt = mntget(unionfs_lower_mnt_idx(dentry, bindex));", 
      "+#ifdef CONFIG_UNION_FS_DEBUG", 
      "+\tif (!mnt)", 
      "+\t\tpr_debug(\"unionfs: mntget: mnt=%p bindex=%d\\n\",", 
      "+\t\t\t mnt, bindex);", 
      "+#endif /* CONFIG_UNION_FS_DEBUG */", 
      "+", 
      "+\treturn mnt;", 
      "+}", 
      "+", 
      "+static inline void unionfs_mntput(struct dentry *dentry, int bindex)", 
      "+{", 
      "+\tstruct vfsmount *mnt;", 
      "+", 
      "+\tif (!dentry && bindex < 0)", 
      "+\t\treturn;", 
      "+\tBUG_ON(!dentry || bindex < 0);", 
      "+", 
      "+\tmnt = unionfs_lower_mnt_idx(dentry, bindex);", 
      "+#ifdef CONFIG_UNION_FS_DEBUG", 
      "+\t/*", 
      "+\t * Directories can have NULL lower objects in between start/end, but", 
      "+\t * NOT if at the start/end range.  We cannot verify that this dentry", 
      "+\t * is a type=DIR, because it may already be a negative dentry.  But", 
      "+\t * if dbstart is greater than dbend, we know that this couldn't have", 
      "+\t * been a regular file: it had to have been a directory.", 
      "+\t */", 
      "+\tif (!mnt && !(bindex > dbstart(dentry) && bindex < dbend(dentry)))", 
      "+\t\tpr_debug(\"unionfs: mntput: mnt=%p bindex=%d\\n\", mnt, bindex);", 
      "+#endif /* CONFIG_UNION_FS_DEBUG */", 
      "+\tmntput(mnt);", 
      "+}", 
      "+", 
      "+#ifdef CONFIG_UNION_FS_DEBUG", 
      "+", 
      "+/* useful for tracking code reachability */", 
      "+#define UDBG pr_debug(\"DBG:%s:%s:%d\\n\", __FILE__, __func__, __LINE__)", 
      "+", 
      "+#define unionfs_check_inode(i)\t__unionfs_check_inode((i),\t\\", 
      "+\t__FILE__, __func__, __LINE__)", 
      "+#define unionfs_check_dentry(d)\t__unionfs_check_dentry((d),\t\\", 
      "+\t__FILE__, __func__, __LINE__)", 
      "+#define unionfs_check_file(f)\t__unionfs_check_file((f),\t\\", 
      "+\t__FILE__, __func__, __LINE__)", 
      "+#define unionfs_check_nd(n)\t__unionfs_check_nd((n),\t\t\\", 
      "+\t__FILE__, __func__, __LINE__)", 
      "+#define show_branch_counts(sb)\t__show_branch_counts((sb),\t\\", 
      "+\t__FILE__, __func__, __LINE__)", 
      "+#define show_inode_times(i)\t__show_inode_times((i),\t\t\\", 
      "+\t__FILE__, __func__, __LINE__)", 
      "+#define show_dinode_times(d)\t__show_dinode_times((d),\t\\", 
      "+\t__FILE__, __func__, __LINE__)", 
      "+#define show_inode_counts(i)\t__show_inode_counts((i),\t\\", 
      "+\t__FILE__, __func__, __LINE__)", 
      "+", 
      "+extern void __unionfs_check_inode(const struct inode *inode, const char *fname,", 
      "+\t\t\t\t  const char *fxn, int line);", 
      "+extern void __unionfs_check_dentry(const struct dentry *dentry,", 
      "+\t\t\t\t   const char *fname, const char *fxn,", 
      "+\t\t\t\t   int line);", 
      "+extern void __unionfs_check_file(const struct file *file,", 
      "+\t\t\t\t const char *fname, const char *fxn, int line);", 
      "+extern void __unionfs_check_nd(const struct nameidata *nd,", 
      "+\t\t\t       const char *fname, const char *fxn, int line);", 
      "+extern void __show_branch_counts(const struct super_block *sb,", 
      "+\t\t\t\t const char *file, const char *fxn, int line);", 
      "+extern void __show_inode_times(const struct inode *inode,", 
      "+\t\t\t       const char *file, const char *fxn, int line);", 
      "+extern void __show_dinode_times(const struct dentry *dentry,", 
      "+\t\t\t\tconst char *file, const char *fxn, int line);", 
      "+extern void __show_inode_counts(const struct inode *inode,", 
      "+\t\t\t\tconst char *file, const char *fxn, int line);", 
      "+", 
      "+#else /* not CONFIG_UNION_FS_DEBUG */", 
      "+", 
      "+/* we leave useful hooks for these check functions throughout the code */", 
      "+#define unionfs_check_inode(i)\t\tdo { } while (0)", 
      "+#define unionfs_check_dentry(d)\t\tdo { } while (0)", 
      "+#define unionfs_check_file(f)\t\tdo { } while (0)", 
      "+#define unionfs_check_nd(n)\t\tdo { } while (0)", 
      "+#define show_branch_counts(sb)\t\tdo { } while (0)", 
      "+#define show_inode_times(i)\t\tdo { } while (0)", 
      "+#define show_dinode_times(d)\t\tdo { } while (0)", 
      "+#define show_inode_counts(i)\t\tdo { } while (0)", 
      "+", 
      "+#endif /* not CONFIG_UNION_FS_DEBUG */", 
      "+", 
      "+#endif\t/* not _UNION_H_ */"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/unlink.c", 
    [
      "diff --git a/fs/unionfs/unlink.c b/fs/unionfs/unlink.c", 
      "new file mode 100644", 
      "index 0000000..b6d8e10", 
      "--- dev/null", 
      "+++ b/fs/unionfs/unlink.c", 
      "@@ -0,0 +1,282 @@", 
      "+/*", 
      "+ * Copyright (c) 2003-2009 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-2009 Stony Brook University", 
      "+ * Copyright (c) 2003-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#include \"union.h\"", 
      "+", 
      "+/*", 
      "+ * Helper function for Unionfs's unlink operation.", 
      "+ *", 
      "+ * The main goal of this function is to optimize the unlinking of non-dir", 
      "+ * objects in unionfs by deleting all possible lower inode objects from the", 
      "+ * underlying branches having same dentry name as the non-dir dentry on", 
      "+ * which this unlink operation is called.  This way we delete as many lower", 
      "+ * inodes as possible, and save space.  Whiteouts need to be created in", 
      "+ * branch0 only if unlinking fails on any of the lower branch other than", 
      "+ * branch0, or if a lower branch is marked read-only.", 
      "+ *", 
      "+ * Also, while unlinking a file, if we encounter any dir type entry in any", 
      "+ * intermediate branch, then we remove the directory by calling vfs_rmdir.", 
      "+ * The following special cases are also handled:", 
      "+", 
      "+ * (1) If an error occurs in branch0 during vfs_unlink, then we return", 
      "+ *     appropriate error.", 
      "+ *", 
      "+ * (2) If we get an error during unlink in any of other lower branch other", 
      "+ *     than branch0, then we create a whiteout in branch0.", 
      "+ *", 
      "+ * (3) If a whiteout already exists in any intermediate branch, we delete", 
      "+ *     all possible inodes only up to that branch (this is an \"opaqueness\"", 
      "+ *     as as per Documentation/filesystems/unionfs/concepts.txt).", 
      "+ *", 
      "+ */", 
      "+static int unionfs_unlink_whiteout(struct inode *dir, struct dentry *dentry,", 
      "+\t\t\t\t   struct dentry *parent)", 
      "+{", 
      "+\tstruct dentry *lower_dentry;", 
      "+\tstruct dentry *lower_dir_dentry;", 
      "+\tint bindex;", 
      "+\tint err = 0;", 
      "+", 
      "+\terr = unionfs_partial_lookup(dentry, parent);", 
      "+\tif (err)", 
      "+\t\tgoto out;", 
      "+", 
      "+\t/* trying to unlink all possible valid instances */", 
      "+\tfor (bindex = dbstart(dentry); bindex <= dbend(dentry); bindex++) {", 
      "+\t\tlower_dentry = unionfs_lower_dentry_idx(dentry, bindex);", 
      "+\t\tif (!lower_dentry || !lower_dentry->d_inode)", 
      "+\t\t\tcontinue;", 
      "+", 
      "+\t\tlower_dir_dentry = lock_parent(lower_dentry);", 
      "+", 
      "+\t\t/* avoid destroying the lower inode if the object is in use */", 
      "+\t\tdget(lower_dentry);", 
      "+\t\terr = is_robranch_super(dentry->d_sb, bindex);", 
      "+\t\tif (!err) {", 
      "+\t\t\t/* see Documentation/filesystems/unionfs/issues.txt */", 
      "+\t\t\tlockdep_off();", 
      "+\t\t\tif (!S_ISDIR(lower_dentry->d_inode->i_mode))", 
      "+\t\t\t\terr = vfs_unlink(lower_dir_dentry->d_inode,", 
      "+\t\t\t\t\t\t\t\tlower_dentry);", 
      "+\t\t\telse", 
      "+\t\t\t\terr = vfs_rmdir(lower_dir_dentry->d_inode,", 
      "+\t\t\t\t\t\t\t\tlower_dentry);", 
      "+\t\t\tlockdep_on();", 
      "+\t\t}", 
      "+", 
      "+\t\t/* if lower object deletion succeeds, update inode's times */", 
      "+\t\tif (!err)", 
      "+\t\t\tunionfs_copy_attr_times(dentry->d_inode);", 
      "+\t\tdput(lower_dentry);", 
      "+\t\tfsstack_copy_attr_times(dir, lower_dir_dentry->d_inode);", 
      "+\t\tunlock_dir(lower_dir_dentry);", 
      "+", 
      "+\t\tif (err)", 
      "+\t\t\tbreak;", 
      "+\t}", 
      "+", 
      "+\t/*", 
      "+\t * Create the whiteout in branch 0 (highest priority) only if (a)", 
      "+\t * there was an error in any intermediate branch other than branch 0", 
      "+\t * due to failure of vfs_unlink/vfs_rmdir or (b) a branch marked or", 
      "+\t * mounted read-only.", 
      "+\t */", 
      "+\tif (err) {", 
      "+\t\tif ((bindex == 0) ||", 
      "+\t\t    ((bindex == dbstart(dentry)) &&", 
      "+\t\t     (!IS_COPYUP_ERR(err))))", 
      "+\t\t\tgoto out;", 
      "+\t\telse {", 
      "+\t\t\tif (!IS_COPYUP_ERR(err))", 
      "+\t\t\t\tpr_debug(\"unionfs: lower object deletion \"", 
      "+\t\t\t\t\t     \"failed in branch:%d\\n\", bindex);", 
      "+\t\t\terr = create_whiteout(dentry, sbstart(dentry->d_sb));", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\tif (!err)", 
      "+\t\tinode_dec_link_count(dentry->d_inode);", 
      "+", 
      "+\t/* We don't want to leave negative leftover dentries for revalidate. */", 
      "+\tif (!err && (dbopaque(dentry) != -1))", 
      "+\t\tupdate_bstart(dentry);", 
      "+", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+int unionfs_unlink(struct inode *dir, struct dentry *dentry)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct inode *inode = dentry->d_inode;", 
      "+\tstruct dentry *parent;", 
      "+\tint valid;", 
      "+", 
      "+\tBUG_ON(S_ISDIR(inode->i_mode));", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\tvalid = __unionfs_d_revalidate(dentry, parent, false);", 
      "+\tif (unlikely(!valid)) {", 
      "+\t\terr = -ESTALE;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tunionfs_check_dentry(dentry);", 
      "+", 
      "+\terr = unionfs_unlink_whiteout(dir, dentry, parent);", 
      "+\t/* call d_drop so the system \"forgets\" about us */", 
      "+\tif (!err) {", 
      "+\t\tunionfs_postcopyup_release(dentry);", 
      "+\t\tunionfs_postcopyup_setmnt(parent);", 
      "+\t\tif (inode->i_nlink == 0) /* drop lower inodes */", 
      "+\t\t\tiput_lowers_all(inode, false);", 
      "+\t\td_drop(dentry);", 
      "+\t\t/*", 
      "+\t\t * if unlink/whiteout succeeded, parent dir mtime has", 
      "+\t\t * changed", 
      "+\t\t */", 
      "+\t\tunionfs_copy_attr_times(dir);", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\tif (!err) {", 
      "+\t\tunionfs_check_dentry(dentry);", 
      "+\t\tunionfs_check_inode(dir);", 
      "+\t}", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+static int unionfs_rmdir_first(struct inode *dir, struct dentry *dentry,", 
      "+\t\t\t       struct unionfs_dir_state *namelist)", 
      "+{", 
      "+\tint err;", 
      "+\tstruct dentry *lower_dentry;", 
      "+\tstruct dentry *lower_dir_dentry = NULL;", 
      "+", 
      "+\t/* Here we need to remove whiteout entries. */", 
      "+\terr = delete_whiteouts(dentry, dbstart(dentry), namelist);", 
      "+\tif (err)", 
      "+\t\tgoto out;", 
      "+", 
      "+\tlower_dentry = unionfs_lower_dentry(dentry);", 
      "+", 
      "+\tlower_dir_dentry = lock_parent(lower_dentry);", 
      "+", 
      "+\t/* avoid destroying the lower inode if the file is in use */", 
      "+\tdget(lower_dentry);", 
      "+\terr = is_robranch(dentry);", 
      "+\tif (!err) {", 
      "+\t\t/* see Documentation/filesystems/unionfs/issues.txt */", 
      "+\t\tlockdep_off();", 
      "+\t\terr = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry);", 
      "+\t\tlockdep_on();", 
      "+\t}", 
      "+\tdput(lower_dentry);", 
      "+", 
      "+\tfsstack_copy_attr_times(dir, lower_dir_dentry->d_inode);", 
      "+\t/* propagate number of hard-links */", 
      "+\tdentry->d_inode->i_nlink = unionfs_get_nlinks(dentry->d_inode);", 
      "+", 
      "+out:", 
      "+\tif (lower_dir_dentry)", 
      "+\t\tunlock_dir(lower_dir_dentry);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+int unionfs_rmdir(struct inode *dir, struct dentry *dentry)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct unionfs_dir_state *namelist = NULL;", 
      "+\tstruct dentry *parent;", 
      "+\tint dstart, dend;", 
      "+\tbool valid;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\tvalid = __unionfs_d_revalidate(dentry, parent, false);", 
      "+\tif (unlikely(!valid)) {", 
      "+\t\terr = -ESTALE;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+\tunionfs_check_dentry(dentry);", 
      "+", 
      "+\t/* check if this unionfs directory is empty or not */", 
      "+\terr = check_empty(dentry, parent, &namelist);", 
      "+\tif (err)", 
      "+\t\tgoto out;", 
      "+", 
      "+\terr = unionfs_rmdir_first(dir, dentry, namelist);", 
      "+\tdstart = dbstart(dentry);", 
      "+\tdend = dbend(dentry);", 
      "+\t/*", 
      "+\t * We create a whiteout for the directory if there was an error to", 
      "+\t * rmdir the first directory entry in the union.  Otherwise, we", 
      "+\t * create a whiteout only if there is no chance that a lower", 
      "+\t * priority branch might also have the same named directory.  IOW,", 
      "+\t * if there is not another same-named directory at a lower priority", 
      "+\t * branch, then we don't need to create a whiteout for it.", 
      "+\t */", 
      "+\tif (!err) {", 
      "+\t\tif (dstart < dend)", 
      "+\t\t\terr = create_whiteout(dentry, dstart);", 
      "+\t} else {", 
      "+\t\tint new_err;", 
      "+", 
      "+\t\tif (dstart == 0)", 
      "+\t\t\tgoto out;", 
      "+", 
      "+\t\t/* exit if the error returned was NOT -EROFS */", 
      "+\t\tif (!IS_COPYUP_ERR(err))", 
      "+\t\t\tgoto out;", 
      "+", 
      "+\t\tnew_err = create_whiteout(dentry, dstart - 1);", 
      "+\t\tif (new_err != -EEXIST)", 
      "+\t\t\terr = new_err;", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\t/*", 
      "+\t * Drop references to lower dentry/inode so storage space for them", 
      "+\t * can be reclaimed.  Then, call d_drop so the system \"forgets\"", 
      "+\t * about us.", 
      "+\t */", 
      "+\tif (!err) {", 
      "+\t\tiput_lowers_all(dentry->d_inode, false);", 
      "+\t\tdput(unionfs_lower_dentry_idx(dentry, dstart));", 
      "+\t\tunionfs_set_lower_dentry_idx(dentry, dstart, NULL);", 
      "+\t\td_drop(dentry);", 
      "+\t\t/* update our lower vfsmnts, in case a copyup took place */", 
      "+\t\tunionfs_postcopyup_setmnt(dentry);", 
      "+\t\tunionfs_check_dentry(dentry);", 
      "+\t\tunionfs_check_inode(dir);", 
      "+\t}", 
      "+", 
      "+\tif (namelist)", 
      "+\t\tfree_rdstate(namelist);", 
      "+", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+\treturn err;", 
      "+}"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/whiteout.c", 
    [
      "diff --git a/fs/unionfs/whiteout.c b/fs/unionfs/whiteout.c", 
      "new file mode 100644", 
      "index 0000000..ae5c5e3", 
      "--- dev/null", 
      "+++ b/fs/unionfs/whiteout.c", 
      "@@ -0,0 +1,577 @@", 
      "+/*", 
      "+ * Copyright (c) 2003-2009 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-2009 Stony Brook University", 
      "+ * Copyright (c) 2003-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#include \"union.h\"", 
      "+", 
      "+/*", 
      "+ * whiteout and opaque directory helpers", 
      "+ */", 
      "+", 
      "+/* What do we use for whiteouts. */", 
      "+#define UNIONFS_WHPFX \".wh.\"", 
      "+#define UNIONFS_WHLEN 4", 
      "+/*", 
      "+ * If a directory contains this file, then it is opaque.  We start with the", 
      "+ * .wh. flag so that it is blocked by lookup.", 
      "+ */", 
      "+#define UNIONFS_DIR_OPAQUE_NAME \"__dir_opaque\"", 
      "+#define UNIONFS_DIR_OPAQUE UNIONFS_WHPFX UNIONFS_DIR_OPAQUE_NAME", 
      "+", 
      "+/* construct whiteout filename */", 
      "+char *alloc_whname(const char *name, int len)", 
      "+{", 
      "+\tchar *buf;", 
      "+", 
      "+\tbuf = kmalloc(len + UNIONFS_WHLEN + 1, GFP_KERNEL);", 
      "+\tif (unlikely(!buf))", 
      "+\t\treturn ERR_PTR(-ENOMEM);", 
      "+", 
      "+\tstrcpy(buf, UNIONFS_WHPFX);", 
      "+\tstrlcat(buf, name, len + UNIONFS_WHLEN + 1);", 
      "+", 
      "+\treturn buf;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * XXX: this can be inline or CPP macro, but is here to keep all whiteout", 
      "+ * code in one place.", 
      "+ */", 
      "+void unionfs_set_max_namelen(long *namelen)", 
      "+{", 
      "+\t*namelen -= UNIONFS_WHLEN;", 
      "+}", 
      "+", 
      "+/* check if @namep is a whiteout, update @namep and @namelenp accordingly */", 
      "+bool is_whiteout_name(char **namep, int *namelenp)", 
      "+{", 
      "+\tif (*namelenp > UNIONFS_WHLEN &&", 
      "+\t    !strncmp(*namep, UNIONFS_WHPFX, UNIONFS_WHLEN)) {", 
      "+\t\t*namep += UNIONFS_WHLEN;", 
      "+\t\t*namelenp -= UNIONFS_WHLEN;", 
      "+\t\treturn true;", 
      "+\t}", 
      "+\treturn false;", 
      "+}", 
      "+", 
      "+/* is the filename valid == !(whiteout for a file or opaque dir marker) */", 
      "+bool is_validname(const char *name)", 
      "+{", 
      "+\tif (!strncmp(name, UNIONFS_WHPFX, UNIONFS_WHLEN))", 
      "+\t\treturn false;", 
      "+\tif (!strncmp(name, UNIONFS_DIR_OPAQUE_NAME,", 
      "+\t\t     sizeof(UNIONFS_DIR_OPAQUE_NAME) - 1))", 
      "+\t\treturn false;", 
      "+\treturn true;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Look for a whiteout @name in @lower_parent directory.  If error, return", 
      "+ * ERR_PTR.  Caller must dput() the returned dentry if not an error.", 
      "+ *", 
      "+ * XXX: some callers can reuse the whname allocated buffer to avoid repeated", 
      "+ * free then re-malloc calls.  Need to provide a different API for those", 
      "+ * callers.", 
      "+ */", 
      "+struct dentry *lookup_whiteout(const char *name, struct dentry *lower_parent)", 
      "+{", 
      "+\tchar *whname = NULL;", 
      "+\tint err = 0, namelen;", 
      "+\tstruct dentry *wh_dentry = NULL;", 
      "+", 
      "+\tnamelen = strlen(name);", 
      "+\twhname = alloc_whname(name, namelen);", 
      "+\tif (unlikely(IS_ERR(whname))) {", 
      "+\t\terr = PTR_ERR(whname);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/* check if whiteout exists in this branch: lookup .wh.foo */", 
      "+\twh_dentry = lookup_one_len(whname, lower_parent, strlen(whname));", 
      "+\tif (IS_ERR(wh_dentry)) {", 
      "+\t\terr = PTR_ERR(wh_dentry);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/* check if negative dentry (ENOENT) */", 
      "+\tif (!wh_dentry->d_inode)", 
      "+\t\tgoto out;", 
      "+", 
      "+\t/* whiteout found: check if valid type */", 
      "+\tif (!S_ISREG(wh_dentry->d_inode->i_mode)) {", 
      "+\t\tprintk(KERN_ERR \"unionfs: invalid whiteout %s entry type %d\\n\",", 
      "+\t\t       whname, wh_dentry->d_inode->i_mode);", 
      "+\t\tdput(wh_dentry);", 
      "+\t\terr = -EIO;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\tkfree(whname);", 
      "+\tif (err)", 
      "+\t\twh_dentry = ERR_PTR(err);", 
      "+\treturn wh_dentry;", 
      "+}", 
      "+", 
      "+/* find and return first whiteout in parent directory, else ENOENT */", 
      "+struct dentry *find_first_whiteout(struct dentry *dentry)", 
      "+{", 
      "+\tint bindex, bstart, bend;", 
      "+\tstruct dentry *parent, *lower_parent, *wh_dentry;", 
      "+", 
      "+\tparent = dget_parent(dentry);", 
      "+", 
      "+\tbstart = dbstart(parent);", 
      "+\tbend = dbend(parent);", 
      "+\twh_dentry = ERR_PTR(-ENOENT);", 
      "+", 
      "+\tfor (bindex = bstart; bindex <= bend; bindex++) {", 
      "+\t\tlower_parent = unionfs_lower_dentry_idx(parent, bindex);", 
      "+\t\tif (!lower_parent)", 
      "+\t\t\tcontinue;", 
      "+\t\twh_dentry = lookup_whiteout(dentry->d_name.name, lower_parent);", 
      "+\t\tif (IS_ERR(wh_dentry))", 
      "+\t\t\tcontinue;", 
      "+\t\tif (wh_dentry->d_inode)", 
      "+\t\t\tbreak;", 
      "+\t\tdput(wh_dentry);", 
      "+\t\twh_dentry = ERR_PTR(-ENOENT);", 
      "+\t}", 
      "+", 
      "+\tdput(parent);", 
      "+", 
      "+\treturn wh_dentry;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Unlink a whiteout dentry.  Returns 0 or -errno.  Caller must hold and", 
      "+ * release dentry reference.", 
      "+ */", 
      "+int unlink_whiteout(struct dentry *wh_dentry)", 
      "+{", 
      "+\tint err;", 
      "+\tstruct dentry *lower_dir_dentry;", 
      "+", 
      "+\t/* dget and lock parent dentry */", 
      "+\tlower_dir_dentry = lock_parent_wh(wh_dentry);", 
      "+", 
      "+\t/* see Documentation/filesystems/unionfs/issues.txt */", 
      "+\tlockdep_off();", 
      "+\terr = vfs_unlink(lower_dir_dentry->d_inode, wh_dentry);", 
      "+\tlockdep_on();", 
      "+\tunlock_dir(lower_dir_dentry);", 
      "+", 
      "+\t/*", 
      "+\t * Whiteouts are special files and should be deleted no matter what", 
      "+\t * (as if they never existed), in order to allow this create", 
      "+\t * operation to succeed.  This is especially important in sticky", 
      "+\t * directories: a whiteout may have been created by one user, but", 
      "+\t * the newly created file may be created by another user.", 
      "+\t * Therefore, in order to maintain Unix semantics, if the vfs_unlink", 
      "+\t * above failed, then we have to try to directly unlink the", 
      "+\t * whiteout.  Note: in the ODF version of unionfs, whiteout are", 
      "+\t * handled much more cleanly.", 
      "+\t */", 
      "+\tif (err == -EPERM) {", 
      "+\t\tstruct inode *inode = lower_dir_dentry->d_inode;", 
      "+\t\terr = inode->i_op->unlink(inode, wh_dentry);", 
      "+\t}", 
      "+\tif (err)", 
      "+\t\tprintk(KERN_ERR \"unionfs: could not unlink whiteout %s, \"", 
      "+\t\t       \"err = %d\\n\", wh_dentry->d_name.name, err);", 
      "+", 
      "+\treturn err;", 
      "+", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Helper function when creating new objects (create, symlink, mknod, etc.).", 
      "+ * Checks to see if there's a whiteout in @lower_dentry's parent directory,", 
      "+ * whose name is taken from @dentry.  Then tries to remove that whiteout, if", 
      "+ * found.  If <dentry,bindex> is a branch marked readonly, return -EROFS.", 
      "+ * If it finds both a regular file and a whiteout, return -EIO (this should", 
      "+ * never happen).", 
      "+ *", 
      "+ * Return 0 if no whiteout was found.  Return 1 if one was found and", 
      "+ * successfully removed.  Therefore a value >= 0 tells the caller that", 
      "+ * @lower_dentry belongs to a good branch to create the new object in).", 
      "+ * Return -ERRNO if an error occurred during whiteout lookup or in trying to", 
      "+ * unlink the whiteout.", 
      "+ */", 
      "+int check_unlink_whiteout(struct dentry *dentry, struct dentry *lower_dentry,", 
      "+\t\t\t  int bindex)", 
      "+{", 
      "+\tint err;", 
      "+\tstruct dentry *wh_dentry = NULL;", 
      "+\tstruct dentry *lower_dir_dentry = NULL;", 
      "+", 
      "+\t/* look for whiteout dentry first */", 
      "+\tlower_dir_dentry = dget_parent(lower_dentry);", 
      "+\twh_dentry = lookup_whiteout(dentry->d_name.name, lower_dir_dentry);", 
      "+\tdput(lower_dir_dentry);", 
      "+\tif (IS_ERR(wh_dentry)) {", 
      "+\t\terr = PTR_ERR(wh_dentry);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tif (!wh_dentry->d_inode) { /* no whiteout exists*/", 
      "+\t\terr = 0;", 
      "+\t\tgoto out_dput;", 
      "+\t}", 
      "+", 
      "+\t/* check if regular file and whiteout were both found */", 
      "+\tif (unlikely(lower_dentry->d_inode)) {", 
      "+\t\terr = -EIO;", 
      "+\t\tprintk(KERN_ERR \"unionfs: found both whiteout and regular \"", 
      "+\t\t       \"file in directory %s (branch %d)\\n\",", 
      "+\t\t       lower_dir_dentry->d_name.name, bindex);", 
      "+\t\tgoto out_dput;", 
      "+\t}", 
      "+", 
      "+\t/* check if branch is writeable */", 
      "+\terr = is_robranch_super(dentry->d_sb, bindex);", 
      "+\tif (err)", 
      "+\t\tgoto out_dput;", 
      "+", 
      "+\t/* .wh.foo has been found, so let's unlink it */", 
      "+\terr = unlink_whiteout(wh_dentry);", 
      "+\tif (!err)", 
      "+\t\terr = 1; /* a whiteout was found and successfully removed */", 
      "+out_dput:", 
      "+\tdput(wh_dentry);", 
      "+out:", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Pass an unionfs dentry and an index.  It will try to create a whiteout", 
      "+ * for the filename in dentry, and will try in branch 'index'.  On error,", 
      "+ * it will proceed to a branch to the left.", 
      "+ */", 
      "+int create_whiteout(struct dentry *dentry, int start)", 
      "+{", 
      "+\tint bstart, bend, bindex;", 
      "+\tstruct dentry *lower_dir_dentry;", 
      "+\tstruct dentry *lower_dentry;", 
      "+\tstruct dentry *lower_wh_dentry;", 
      "+\tstruct nameidata nd;", 
      "+\tchar *name = NULL;", 
      "+\tint err = -EINVAL;", 
      "+", 
      "+\tverify_locked(dentry);", 
      "+", 
      "+\tbstart = dbstart(dentry);", 
      "+\tbend = dbend(dentry);", 
      "+", 
      "+\t/* create dentry's whiteout equivalent */", 
      "+\tname = alloc_whname(dentry->d_name.name, dentry->d_name.len);", 
      "+\tif (unlikely(IS_ERR(name))) {", 
      "+\t\terr = PTR_ERR(name);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tfor (bindex = start; bindex >= 0; bindex--) {", 
      "+\t\tlower_dentry = unionfs_lower_dentry_idx(dentry, bindex);", 
      "+", 
      "+\t\tif (!lower_dentry) {", 
      "+\t\t\t/*", 
      "+\t\t\t * if lower dentry is not present, create the", 
      "+\t\t\t * entire lower dentry directory structure and go", 
      "+\t\t\t * ahead.  Since we want to just create whiteout, we", 
      "+\t\t\t * only want the parent dentry, and hence get rid of", 
      "+\t\t\t * this dentry.", 
      "+\t\t\t */", 
      "+\t\t\tlower_dentry = create_parents(dentry->d_inode,", 
      "+\t\t\t\t\t\t      dentry,", 
      "+\t\t\t\t\t\t      dentry->d_name.name,", 
      "+\t\t\t\t\t\t      bindex);", 
      "+\t\t\tif (!lower_dentry || IS_ERR(lower_dentry)) {", 
      "+\t\t\t\tint ret = PTR_ERR(lower_dentry);", 
      "+\t\t\t\tif (!IS_COPYUP_ERR(ret))", 
      "+\t\t\t\t\tprintk(KERN_ERR", 
      "+\t\t\t\t\t       \"unionfs: create_parents for \"", 
      "+\t\t\t\t\t       \"whiteout failed: bindex=%d \"", 
      "+\t\t\t\t\t       \"err=%d\\n\", bindex, ret);", 
      "+\t\t\t\tcontinue;", 
      "+\t\t\t}", 
      "+\t\t}", 
      "+", 
      "+\t\tlower_wh_dentry =", 
      "+\t\t\tlookup_one_len(name, lower_dentry->d_parent,", 
      "+\t\t\t\t       dentry->d_name.len + UNIONFS_WHLEN);", 
      "+\t\tif (IS_ERR(lower_wh_dentry))", 
      "+\t\t\tcontinue;", 
      "+", 
      "+\t\t/*", 
      "+\t\t * The whiteout already exists. This used to be impossible,", 
      "+\t\t * but now is possible because of opaqueness.", 
      "+\t\t */", 
      "+\t\tif (lower_wh_dentry->d_inode) {", 
      "+\t\t\tdput(lower_wh_dentry);", 
      "+\t\t\terr = 0;", 
      "+\t\t\tgoto out;", 
      "+\t\t}", 
      "+", 
      "+\t\terr = init_lower_nd(&nd, LOOKUP_CREATE);", 
      "+\t\tif (unlikely(err < 0))", 
      "+\t\t\tgoto out;", 
      "+\t\tlower_dir_dentry = lock_parent_wh(lower_wh_dentry);", 
      "+\t\terr = is_robranch_super(dentry->d_sb, bindex);", 
      "+\t\tif (!err)", 
      "+\t\t\terr = vfs_create(lower_dir_dentry->d_inode,", 
      "+\t\t\t\t\t lower_wh_dentry,", 
      "+\t\t\t\t\t ~current->fs->umask & S_IRUGO,", 
      "+\t\t\t\t\t &nd);", 
      "+\t\tunlock_dir(lower_dir_dentry);", 
      "+\t\tdput(lower_wh_dentry);", 
      "+\t\trelease_lower_nd(&nd, err);", 
      "+", 
      "+\t\tif (!err || !IS_COPYUP_ERR(err))", 
      "+\t\t\tbreak;", 
      "+\t}", 
      "+", 
      "+\t/* set dbopaque so that lookup will not proceed after this branch */", 
      "+\tif (!err)", 
      "+\t\tdbopaque(dentry) = bindex;", 
      "+", 
      "+out:", 
      "+\tkfree(name);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * Delete all of the whiteouts in a given directory for rmdir.", 
      "+ *", 
      "+ * lower directory inode should be locked", 
      "+ */", 
      "+static int do_delete_whiteouts(struct dentry *dentry, int bindex,", 
      "+\t\t\t       struct unionfs_dir_state *namelist)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct dentry *lower_dir_dentry = NULL;", 
      "+\tstruct dentry *lower_dentry;", 
      "+\tchar *name = NULL, *p;", 
      "+\tstruct inode *lower_dir;", 
      "+\tint i;", 
      "+\tstruct list_head *pos;", 
      "+\tstruct filldir_node *cursor;", 
      "+", 
      "+\t/* Find out lower parent dentry */", 
      "+\tlower_dir_dentry = unionfs_lower_dentry_idx(dentry, bindex);", 
      "+\tBUG_ON(!S_ISDIR(lower_dir_dentry->d_inode->i_mode));", 
      "+\tlower_dir = lower_dir_dentry->d_inode;", 
      "+\tBUG_ON(!S_ISDIR(lower_dir->i_mode));", 
      "+", 
      "+\terr = -ENOMEM;", 
      "+\tname = __getname();", 
      "+\tif (unlikely(!name))", 
      "+\t\tgoto out;", 
      "+\tstrcpy(name, UNIONFS_WHPFX);", 
      "+\tp = name + UNIONFS_WHLEN;", 
      "+", 
      "+\terr = 0;", 
      "+\tfor (i = 0; !err && i < namelist->size; i++) {", 
      "+\t\tlist_for_each(pos, &namelist->list[i]) {", 
      "+\t\t\tcursor =", 
      "+\t\t\t\tlist_entry(pos, struct filldir_node,", 
      "+\t\t\t\t\t   file_list);", 
      "+\t\t\t/* Only operate on whiteouts in this branch. */", 
      "+\t\t\tif (cursor->bindex != bindex)", 
      "+\t\t\t\tcontinue;", 
      "+\t\t\tif (!cursor->whiteout)", 
      "+\t\t\t\tcontinue;", 
      "+", 
      "+\t\t\tstrlcpy(p, cursor->name, PATH_MAX - UNIONFS_WHLEN);", 
      "+\t\t\tlower_dentry =", 
      "+\t\t\t\tlookup_one_len(name, lower_dir_dentry,", 
      "+\t\t\t\t\t       cursor->namelen +", 
      "+\t\t\t\t\t       UNIONFS_WHLEN);", 
      "+\t\t\tif (IS_ERR(lower_dentry)) {", 
      "+\t\t\t\terr = PTR_ERR(lower_dentry);", 
      "+\t\t\t\tbreak;", 
      "+\t\t\t}", 
      "+\t\t\tif (lower_dentry->d_inode)", 
      "+\t\t\t\terr = vfs_unlink(lower_dir, lower_dentry);", 
      "+\t\t\tdput(lower_dentry);", 
      "+\t\t\tif (err)", 
      "+\t\t\t\tbreak;", 
      "+\t\t}", 
      "+\t}", 
      "+", 
      "+\t__putname(name);", 
      "+", 
      "+\t/* After all of the removals, we should copy the attributes once. */", 
      "+\tfsstack_copy_attr_times(dentry->d_inode, lower_dir_dentry->d_inode);", 
      "+", 
      "+out:", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+", 
      "+void __delete_whiteouts(struct work_struct *work)", 
      "+{", 
      "+\tstruct sioq_args *args = container_of(work, struct sioq_args, work);", 
      "+\tstruct deletewh_args *d = &args->deletewh;", 
      "+", 
      "+\targs->err = do_delete_whiteouts(d->dentry, d->bindex, d->namelist);", 
      "+\tcomplete(&args->comp);", 
      "+}", 
      "+", 
      "+/* delete whiteouts in a dir (for rmdir operation) using sioq if necessary */", 
      "+int delete_whiteouts(struct dentry *dentry, int bindex,", 
      "+\t\t     struct unionfs_dir_state *namelist)", 
      "+{", 
      "+\tint err;", 
      "+\tstruct super_block *sb;", 
      "+\tstruct dentry *lower_dir_dentry;", 
      "+\tstruct inode *lower_dir;", 
      "+\tstruct sioq_args args;", 
      "+", 
      "+\tsb = dentry->d_sb;", 
      "+", 
      "+\tBUG_ON(!S_ISDIR(dentry->d_inode->i_mode));", 
      "+\tBUG_ON(bindex < dbstart(dentry));", 
      "+\tBUG_ON(bindex > dbend(dentry));", 
      "+\terr = is_robranch_super(sb, bindex);", 
      "+\tif (err)", 
      "+\t\tgoto out;", 
      "+", 
      "+\tlower_dir_dentry = unionfs_lower_dentry_idx(dentry, bindex);", 
      "+\tBUG_ON(!S_ISDIR(lower_dir_dentry->d_inode->i_mode));", 
      "+\tlower_dir = lower_dir_dentry->d_inode;", 
      "+\tBUG_ON(!S_ISDIR(lower_dir->i_mode));", 
      "+", 
      "+\tif (!permission(lower_dir, MAY_WRITE | MAY_EXEC, NULL)) {", 
      "+\t\terr = do_delete_whiteouts(dentry, bindex, namelist);", 
      "+\t} else {", 
      "+\t\targs.deletewh.namelist = namelist;", 
      "+\t\targs.deletewh.dentry = dentry;", 
      "+\t\targs.deletewh.bindex = bindex;", 
      "+\t\trun_sioq(__delete_whiteouts, &args);", 
      "+\t\terr = args.err;", 
      "+\t}", 
      "+", 
      "+out:", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/****************************************************************************", 
      "+ * Opaque directory helpers                                                 *", 
      "+ ****************************************************************************/", 
      "+", 
      "+/*", 
      "+ * is_opaque_dir: returns 0 if it is NOT an opaque dir, 1 if it is, and", 
      "+ * -errno if an error occurred trying to figure this out.", 
      "+ */", 
      "+int is_opaque_dir(struct dentry *dentry, int bindex)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct dentry *lower_dentry;", 
      "+\tstruct dentry *wh_lower_dentry;", 
      "+\tstruct inode *lower_inode;", 
      "+\tstruct sioq_args args;", 
      "+", 
      "+\tlower_dentry = unionfs_lower_dentry_idx(dentry, bindex);", 
      "+\tlower_inode = lower_dentry->d_inode;", 
      "+", 
      "+\tBUG_ON(!S_ISDIR(lower_inode->i_mode));", 
      "+", 
      "+\tmutex_lock(&lower_inode->i_mutex);", 
      "+", 
      "+\tif (!permission(lower_inode, MAY_EXEC, NULL)) {", 
      "+\t\twh_lower_dentry =", 
      "+\t\t\tlookup_one_len(UNIONFS_DIR_OPAQUE, lower_dentry,", 
      "+\t\t\t\t       sizeof(UNIONFS_DIR_OPAQUE) - 1);", 
      "+\t} else {", 
      "+\t\targs.is_opaque.dentry = lower_dentry;", 
      "+\t\trun_sioq(__is_opaque_dir, &args);", 
      "+\t\twh_lower_dentry = args.ret;", 
      "+\t}", 
      "+", 
      "+\tmutex_unlock(&lower_inode->i_mutex);", 
      "+", 
      "+\tif (IS_ERR(wh_lower_dentry)) {", 
      "+\t\terr = PTR_ERR(wh_lower_dentry);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\t/* This is an opaque dir iff wh_lower_dentry is positive */", 
      "+\terr = !!wh_lower_dentry->d_inode;", 
      "+", 
      "+\tdput(wh_lower_dentry);", 
      "+out:", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+void __is_opaque_dir(struct work_struct *work)", 
      "+{", 
      "+\tstruct sioq_args *args = container_of(work, struct sioq_args, work);", 
      "+", 
      "+\targs->ret = lookup_one_len(UNIONFS_DIR_OPAQUE, args->is_opaque.dentry,", 
      "+\t\t\t\t   sizeof(UNIONFS_DIR_OPAQUE) - 1);", 
      "+\tcomplete(&args->comp);", 
      "+}", 
      "+", 
      "+int make_dir_opaque(struct dentry *dentry, int bindex)", 
      "+{", 
      "+\tint err = 0;", 
      "+\tstruct dentry *lower_dentry, *diropq;", 
      "+\tstruct inode *lower_dir;", 
      "+\tstruct nameidata nd;", 
      "+\tkernel_cap_t orig_cap;", 
      "+", 
      "+\t/*", 
      "+\t * Opaque directory whiteout markers are special files (like regular", 
      "+\t * whiteouts), and should appear to the users as if they don't", 
      "+\t * exist.  They should be created/deleted regardless of directory", 
      "+\t * search/create permissions, but only for the duration of this", 
      "+\t * creation of the .wh.__dir_opaque: file.  Note, this does not", 
      "+\t * circumvent normal ->permission).", 
      "+\t */", 
      "+\torig_cap = current->cap_effective;", 
      "+\tcap_raise(current->cap_effective, CAP_DAC_READ_SEARCH);", 
      "+\tcap_raise(current->cap_effective, CAP_DAC_OVERRIDE);", 
      "+", 
      "+\tlower_dentry = unionfs_lower_dentry_idx(dentry, bindex);", 
      "+\tlower_dir = lower_dentry->d_inode;", 
      "+\tBUG_ON(!S_ISDIR(dentry->d_inode->i_mode) ||", 
      "+\t       !S_ISDIR(lower_dir->i_mode));", 
      "+", 
      "+\tmutex_lock(&lower_dir->i_mutex);", 
      "+\tdiropq = lookup_one_len(UNIONFS_DIR_OPAQUE, lower_dentry,", 
      "+\t\t\t\tsizeof(UNIONFS_DIR_OPAQUE) - 1);", 
      "+\tif (IS_ERR(diropq)) {", 
      "+\t\terr = PTR_ERR(diropq);", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\terr = init_lower_nd(&nd, LOOKUP_CREATE);", 
      "+\tif (unlikely(err < 0))", 
      "+\t\tgoto out;", 
      "+\tif (!diropq->d_inode)", 
      "+\t\terr = vfs_create(lower_dir, diropq, S_IRUGO, &nd);", 
      "+\tif (!err)", 
      "+\t\tdbopaque(dentry) = bindex;", 
      "+\trelease_lower_nd(&nd, err);", 
      "+", 
      "+\tdput(diropq);", 
      "+", 
      "+out:", 
      "+\tmutex_unlock(&lower_dir->i_mutex);", 
      "+\tcurrent->cap_effective = orig_cap;", 
      "+\treturn err;", 
      "+}"
    ]
  ], 
  [
    "dev/null", 
    "b/fs/unionfs/xattr.c", 
    [
      "diff --git a/fs/unionfs/xattr.c b/fs/unionfs/xattr.c", 
      "new file mode 100644", 
      "index 0000000..af72cca", 
      "--- dev/null", 
      "+++ b/fs/unionfs/xattr.c", 
      "@@ -0,0 +1,173 @@", 
      "+/*", 
      "+ * Copyright (c) 2003-2009 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-2009 Stony Brook University", 
      "+ * Copyright (c) 2003-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#include \"union.h\"", 
      "+", 
      "+/* This is lifted from fs/xattr.c */", 
      "+void *unionfs_xattr_alloc(size_t size, size_t limit)", 
      "+{", 
      "+\tvoid *ptr;", 
      "+", 
      "+\tif (size > limit)", 
      "+\t\treturn ERR_PTR(-E2BIG);", 
      "+", 
      "+\tif (!size)\t\t/* size request, no buffer is needed */", 
      "+\t\treturn NULL;", 
      "+", 
      "+\tptr = kmalloc(size, GFP_KERNEL);", 
      "+\tif (unlikely(!ptr))", 
      "+\t\treturn ERR_PTR(-ENOMEM);", 
      "+\treturn ptr;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * BKL held by caller.", 
      "+ * dentry->d_inode->i_mutex locked", 
      "+ */", 
      "+ssize_t unionfs_getxattr(struct dentry *dentry, const char *name, void *value,", 
      "+\t\t\t size_t size)", 
      "+{", 
      "+\tstruct dentry *lower_dentry = NULL;", 
      "+\tstruct dentry *parent;", 
      "+\tint err = -EOPNOTSUPP;", 
      "+\tbool valid;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\tvalid = __unionfs_d_revalidate(dentry, parent, false);", 
      "+\tif (unlikely(!valid)) {", 
      "+\t\terr = -ESTALE;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tlower_dentry = unionfs_lower_dentry(dentry);", 
      "+", 
      "+\terr = vfs_getxattr(lower_dentry, (char *) name, value, size);", 
      "+", 
      "+out:", 
      "+\tunionfs_check_dentry(dentry);", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * BKL held by caller.", 
      "+ * dentry->d_inode->i_mutex locked", 
      "+ */", 
      "+int unionfs_setxattr(struct dentry *dentry, const char *name,", 
      "+\t\t     const void *value, size_t size, int flags)", 
      "+{", 
      "+\tstruct dentry *lower_dentry = NULL;", 
      "+\tstruct dentry *parent;", 
      "+\tint err = -EOPNOTSUPP;", 
      "+\tbool valid;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\tvalid = __unionfs_d_revalidate(dentry, parent, false);", 
      "+\tif (unlikely(!valid)) {", 
      "+\t\terr = -ESTALE;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tlower_dentry = unionfs_lower_dentry(dentry);", 
      "+", 
      "+\terr = vfs_setxattr(lower_dentry, (char *) name, (void *) value,", 
      "+\t\t\t   size, flags);", 
      "+", 
      "+out:", 
      "+\tunionfs_check_dentry(dentry);", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * BKL held by caller.", 
      "+ * dentry->d_inode->i_mutex locked", 
      "+ */", 
      "+int unionfs_removexattr(struct dentry *dentry, const char *name)", 
      "+{", 
      "+\tstruct dentry *lower_dentry = NULL;", 
      "+\tstruct dentry *parent;", 
      "+\tint err = -EOPNOTSUPP;", 
      "+\tbool valid;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\tvalid = __unionfs_d_revalidate(dentry, parent, false);", 
      "+\tif (unlikely(!valid)) {", 
      "+\t\terr = -ESTALE;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tlower_dentry = unionfs_lower_dentry(dentry);", 
      "+", 
      "+\terr = vfs_removexattr(lower_dentry, (char *) name);", 
      "+", 
      "+out:", 
      "+\tunionfs_check_dentry(dentry);", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+\treturn err;", 
      "+}", 
      "+", 
      "+/*", 
      "+ * BKL held by caller.", 
      "+ * dentry->d_inode->i_mutex locked", 
      "+ */", 
      "+ssize_t unionfs_listxattr(struct dentry *dentry, char *list, size_t size)", 
      "+{", 
      "+\tstruct dentry *lower_dentry = NULL;", 
      "+\tstruct dentry *parent;", 
      "+\tint err = -EOPNOTSUPP;", 
      "+\tchar *encoded_list = NULL;", 
      "+\tbool valid;", 
      "+", 
      "+\tunionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);", 
      "+\tparent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);", 
      "+\tunionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);", 
      "+", 
      "+\tvalid = __unionfs_d_revalidate(dentry, parent, false);", 
      "+\tif (unlikely(!valid)) {", 
      "+\t\terr = -ESTALE;", 
      "+\t\tgoto out;", 
      "+\t}", 
      "+", 
      "+\tlower_dentry = unionfs_lower_dentry(dentry);", 
      "+", 
      "+\tencoded_list = list;", 
      "+\terr = vfs_listxattr(lower_dentry, encoded_list, size);", 
      "+", 
      "+out:", 
      "+\tunionfs_check_dentry(dentry);", 
      "+\tunionfs_unlock_dentry(dentry);", 
      "+\tunionfs_unlock_parent(dentry, parent);", 
      "+\tunionfs_read_unlock(dentry->d_sb);", 
      "+\treturn err;", 
      "+}"
    ]
  ], 
  [
    "include/linux/fs_stack.h", 
    "include/linux/fs_stack.h", 
    [
      "diff --git a/include/linux/fs_stack.h b/include/linux/fs_stack.h", 
      "index bb516ce..64f1ced 100644", 
      "--- include/linux/fs_stack.h", 
      "+++ include/linux/fs_stack.h", 
      "@@ -1,17 +1,27 @@", 
      "+/*", 
      "+ * Copyright (c) 2006-2009 Erez Zadok", 
      "+ * Copyright (c) 2006-2007 Josef 'Jeff' Sipek", 
      "+ * Copyright (c) 2006-2009 Stony Brook University", 
      "+ * Copyright (c) 2006-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "#ifndef _LINUX_FS_STACK_H", 
      "#define _LINUX_FS_STACK_H", 
      "", 
      "-/* This file defines generic functions used primarily by stackable", 
      "+/*", 
      "+ * This file defines generic functions used primarily by stackable", 
      "* filesystems; none of these functions require i_mutex to be held.", 
      "*/", 
      "", 
      "#include <linux/fs.h>", 
      "", 
      "/* externs for fs/stack.c */", 
      "-extern void fsstack_copy_attr_all(struct inode *dest, const struct inode *src,", 
      "-\t\t\t\tint (*get_nlinks)(struct inode *));", 
      "-", 
      "-extern void fsstack_copy_inode_size(struct inode *dst, const struct inode *src);", 
      "+extern void fsstack_copy_attr_all(struct inode *dest, const struct inode *src);", 
      "+extern void fsstack_copy_inode_size(struct inode *dst, struct inode *src);", 
      "", 
      "/* inlines */", 
      "static inline void fsstack_copy_attr_atime(struct inode *dest,"
    ]
  ], 
  [
    "include/linux/magic.h", 
    "include/linux/magic.h", 
    [
      "diff --git a/include/linux/magic.h b/include/linux/magic.h", 
      "index 36cc20d..8dac50b 100644", 
      "--- include/linux/magic.h", 
      "+++ include/linux/magic.h", 
      "@@ -35,6 +35,8 @@", 
      "#define REISER2FS_SUPER_MAGIC_STRING\t\"ReIsEr2Fs\"", 
      "#define REISER2FS_JR_SUPER_MAGIC_STRING\t\"ReIsEr3Fs\"", 
      "", 
      "+#define UNIONFS_SUPER_MAGIC 0xf15f083d", 
      "+", 
      "#define SMB_SUPER_MAGIC\t\t0x517B", 
      "#define USBDEVICE_SUPER_MAGIC\t0x9fa2", 
      ""
    ]
  ], 
  [
    "include/linux/namei.h", 
    "include/linux/namei.h", 
    [
      "diff --git a/include/linux/namei.h b/include/linux/namei.h", 
      "index 6c38efb..e9477b5 100644", 
      "--- include/linux/namei.h", 
      "+++ include/linux/namei.h", 
      "@@ -3,6 +3,7 @@", 
      "", 
      "#include <linux/dcache.h>", 
      "#include <linux/linkage.h>", 
      "+#include <linux/mount.h>", 
      "", 
      "struct vfsmount;", 
      "", 
      "@@ -100,4 +101,16 @@ static inline char *nd_get_link(struct nameidata *nd)", 
      "return nd->saved_names[nd->depth];", 
      "}", 
      "", 
      "+static inline void pathget(struct path *path)", 
      "+{", 
      "+\tmntget(path->mnt);", 
      "+\tdget(path->dentry);", 
      "+}", 
      "+", 
      "+static inline void pathput(struct path *path)", 
      "+{", 
      "+\tdput(path->dentry);", 
      "+\tmntput(path->mnt);", 
      "+}", 
      "+", 
      "#endif /* _LINUX_NAMEI_H */"
    ]
  ], 
  [
    "include/linux/splice.h", 
    "include/linux/splice.h", 
    [
      "diff --git a/include/linux/splice.h b/include/linux/splice.h", 
      "index 33e447f..616d473 100644", 
      "--- include/linux/splice.h", 
      "+++ include/linux/splice.h", 
      "@@ -69,5 +69,10 @@ extern ssize_t splice_to_pipe(struct pipe_inode_info *,", 
      "struct splice_pipe_desc *);", 
      "extern ssize_t splice_direct_to_actor(struct file *, struct splice_desc *,", 
      "splice_direct_actor *);", 
      "+extern long vfs_splice_from(struct pipe_inode_info *pipe, struct file *out,", 
      "+\t\t\t    loff_t *ppos, size_t len, unsigned int flags);", 
      "+extern long vfs_splice_to(struct file *in, loff_t *ppos,", 
      "+\t\t\t  struct pipe_inode_info *pipe, size_t len,", 
      "+\t\t\t  unsigned int flags);", 
      "", 
      "#endif"
    ]
  ], 
  [
    "dev/null", 
    "b/include/linux/union_fs.h", 
    [
      "diff --git a/include/linux/union_fs.h b/include/linux/union_fs.h", 
      "new file mode 100644", 
      "index 0000000..c84d97e", 
      "--- dev/null", 
      "+++ b/include/linux/union_fs.h", 
      "@@ -0,0 +1,22 @@", 
      "+/*", 
      "+ * Copyright (c) 2003-2009 Erez Zadok", 
      "+ * Copyright (c) 2005-2007 Josef 'Jeff' Sipek", 
      "+ * Copyright (c) 2003-2009 Stony Brook University", 
      "+ * Copyright (c) 2003-2009 The Research Foundation of SUNY", 
      "+ *", 
      "+ * This program is free software; you can redistribute it and/or modify", 
      "+ * it under the terms of the GNU General Public License version 2 as", 
      "+ * published by the Free Software Foundation.", 
      "+ */", 
      "+", 
      "+#ifndef _LINUX_UNION_FS_H", 
      "+#define _LINUX_UNION_FS_H", 
      "+", 
      "+/*", 
      "+ * DEFINITIONS FOR USER AND KERNEL CODE:", 
      "+ */", 
      "+# define UNIONFS_IOCTL_INCGEN\t\t_IOR(0x15, 11, int)", 
      "+# define UNIONFS_IOCTL_QUERYFILE\t_IOR(0x15, 15, int)", 
      "+", 
      "+#endif /* _LINUX_UNIONFS_H */", 
      "+"
    ]
  ]
]