kernel: add a patch for union mounts in 2.6.31
authorFelix Fietkau <nbd@openwrt.org>
Fri, 25 Sep 2009 20:58:37 +0000 (20:58 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Fri, 25 Sep 2009 20:58:37 +0000 (20:58 +0000)
SVN-Revision: 17726

target/linux/generic-2.6/config-2.6.31
target/linux/generic-2.6/patches-2.6.31/230-union_mounts.patch [new file with mode: 0644]
target/linux/generic-2.6/patches-2.6.31/231-union_mounts_bind_fix.patch [new file with mode: 0644]

index f262b6e..c4566c0 100644 (file)
@@ -2067,6 +2067,7 @@ CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 # CONFIG_UIO is not set
 # CONFIG_ULTRIX_PARTITION is not set
 # CONFIG_UNEVICTABLE_LRU is not set
+# CONFIG_UNION_MOUNT is not set
 CONFIG_UNIX98_PTYS=y
 # CONFIG_UNIXWARE_DISKLABEL is not set
 CONFIG_UNIX=y
diff --git a/target/linux/generic-2.6/patches-2.6.31/230-union_mounts.patch b/target/linux/generic-2.6/patches-2.6.31/230-union_mounts.patch
new file mode 100644 (file)
index 0000000..d1b6b93
--- /dev/null
@@ -0,0 +1,5203 @@
+--- /dev/null
++++ b/Documentation/filesystems/union-mounts.txt
+@@ -0,0 +1,187 @@
++VFS based Union Mounts
++----------------------
++
++ 1. What are "Union Mounts"
++ 2. The Union Stack
++ 3. Whiteouts, Opaque Directories, and Fallthrus
++ 4. Copy-up
++ 5. Directory Reading
++ 6. Known Problems
++ 7. References
++
++-------------------------------------------------------------------------------
++
++1. What are "Union Mounts"
++==========================
++
++Please note: this is NOT about UnionFS and it is NOT derived work!
++
++Traditionally the mount operation is opaque, which means that the content of
++the mount point, the directory where the file system is mounted on, is hidden
++by the content of the mounted file system's root directory until the file
++system is unmounted again. Unlike the traditional UNIX mount mechanism, that
++hides the contents of the mount point, a union mount presents a view as if
++both filesystems are merged together. Although only the topmost layer of the
++mount stack can be altered, it appears as if transparent file system mounts
++allow any file to be created, modified or deleted.
++
++Most people know the concepts and features of union mounts from other
++operating systems like Sun's Translucent Filesystem, Plan9 or BSD. For an
++in-depth review of union mounts and other unioning file systems, see:
++
++http://lwn.net/Articles/324291/
++http://lwn.net/Articles/325369/
++http://lwn.net/Articles/327738/
++
++Here are the key features of this implementation:
++- completely VFS based
++- does not change the namespace stacking
++- directory listings have duplicate entries removed in the kernel
++- writable unions: only the topmost file system layer may be writable
++- writable unions: new whiteout filetype handled inside the kernel
++
++-------------------------------------------------------------------------------
++
++2. The Union Stack
++==================
++
++The mounted file systems are organized in the "file system hierarchy" (tree of
++vfsmount structures), which keeps track about the stacking of file systems
++upon each other. The per-directory view on the file system hierarchy is called
++"mount stack" and reflects the order of file systems, which are mounted on a
++specific directory.
++
++Union mounts present a single unified view of the contents of two or more file
++systems as if they are merged together. Since the information which file
++system objects are part of a unified view is not directly available from the
++file system hierarchy there is a need for a new structure. The file system
++objects, which are part of a unified view are ordered in a so-called "union
++stack". Only directories can be part of a unified view.
++
++The link between two layers of the union stack is maintained using the
++union_mount structure (#include <linux/union.h>):
++
++struct union_mount {
++       atomic_t u_count;               /* reference count */
++       struct mutex u_mutex;
++       struct list_head u_unions;      /* list head for d_unions */
++       struct hlist_node u_hash;       /* list head for searching */
++       struct hlist_node u_rhash;      /* list head for reverse searching */
++
++       struct path u_this;             /* this is me */
++       struct path u_next;             /* this is what I overlay */
++};
++
++The union_mount structure holds a reference (dget,mntget) to the next lower
++layer of the union stack. Since a dentry can be part of multiple unions
++(e.g. with bind mounts) they are tied together via the d_unions field of the
++dentry structure.
++
++All union_mount structures are cached in two hash tables, one for lookups of
++the next lower layer of the union stack and one for reverse lookups of the
++next upper layer of the union stack. The reverse lookup is necessary to
++resolve CWD relative path lookups. For calculation of the hash value, the
++(dentry,vfsmount) pair is used. The u_this field is used for the hash table
++which is used in forward lookups and the u_next field for the reverse lookups.
++
++During every new mount (or mount propagation), a new union_mount structure is
++allocated. A reference to the mountpoint's vfsmount and dentry is taken and
++stored in the u_next field.  In almost the same manner an union_mount
++structure is created during the first time lookup of a directory within a
++union mount point. In this case the lookup proceeds to all lower layers of the
++union. Therefore the complete union stack is constructed during lookups.
++
++The union_mount structures of a dentry are destroyed when the dentry itself is
++destroyed. Therefore the dentry cache is indirectly driving the union_mount
++cache like this is done for inodes too. Please note that lower layer
++union_mount structures are kept in memory until the topmost dentry is
++destroyed.
++
++-------------------------------------------------------------------------------
++
++3. Whiteouts, Opaque Directories, and Fallthrus
++===========================================================
++
++The whiteout filetype isn't new. It has been there for quite some time now
++but Linux's VFS hasn't used it yet. With the availability of union mount code
++inside the VFS the whiteout filetype is getting important to support writable
++union mounts. For read-only union mounts, support for whiteouts or
++copy-on-open is not necessary.
++
++The whiteout filetype has the same function as negative dentries: they
++describe a filename which isn't there. The creation of whiteouts needs
++lowlevel filesystem support. At the time of writing this, there is whiteout
++support for tmpfs, ext2 and ext3 available. The VFS is extended to make the
++whiteout handling transparent to all its users. The whiteouts are not
++visible to user-space.
++
++What happens when we create a directory that was previously whited-out? We
++don't want the directory entries from underlying filesystems to suddenly appear
++in the newly created directory.  So we mark the directory opaque (the file
++system must support storage of the opaque flag).
++
++Fallthrus are directory entries that override the opaque flag on a directory
++for that specific directory entry name (the lookup "falls through" to the next
++layer of the union mount).  Fallthrus are mainly useful for implementing
++readdir().
++
++-------------------------------------------------------------------------------
++
++4. Copy-up
++===========
++
++Any write to an object on any layer other than the topmost triggers a copy-up
++of the object to the topmost file system. For regular files, the copy-up
++happens when it is opened in writable mode.
++
++Directories are copied up on open, regardless of intent to write, to simplify
++copy-up of any object located below it in the namespace. Otherwise we have to
++walk the entire pathname to create intermediate directories whenever we do a
++copy-up. This is the same approach as BSD union mounts and uses a negigible
++amount of disk space.  Note that the actual directory entries themselves are
++not copied-up from the lower levels until (a) the directory is written to, or
++(b) the first readdir() of the directory (more on that later).
++
++Rename across different levels of the union is implemented as a copy-up
++operation for regular files. Rename of directories simply returns EXDEV, the
++same as if we tried to rename across different mounts. Most applications have
++to handle this case anyway. Some applications do not expect EXDEV on
++rename operations within the same directory, but these applications will also
++be broken with bind mounts.
++
++-------------------------------------------------------------------------------
++
++5. Directory Reading
++====================
++
++readdir() is somewhat difficult to implement in a unioning file system. We must
++eliminate duplicates, apply whiteouts, and start up readdir() where we left
++off, given a single f_pos value. Our solution is to copy up all the directory
++entries to the topmost directory the first time readdir() is called on a
++directory. During this copy-up, we skip duplicates and entries covered by
++whiteouts, and then create fallthru entries for each remaining visible dentry.
++Then we mark the whole directory opaque. From then on, we just use the topmost
++file system's normal readdir() operation.
++
++-------------------------------------------------------------------------------
++
++6. Known Problems
++=================
++
++- copyup() for other filetypes that reg and dir (e.g. for chown() on devices)
++- symlinks are untested
++
++-------------------------------------------------------------------------------
++
++7. References
++=============
++
++[1] http://marc.info/?l=linux-fsdevel&m=96035682927821&w=2
++[2] http://marc.info/?l=linux-fsdevel&m=117681527820133&w=2
++[3] http://marc.info/?l=linux-fsdevel&m=117913503200362&w=2
++[4] http://marc.info/?l=linux-fsdevel&m=118231827024394&w=2
++
++Authors:
++Jan Blunck <jblunck@suse.de>
++Bharata B Rao <bharata@linux.vnet.ibm.com>
++Valerie Aurora <vaurora@redhat.com>
+--- a/fs/autofs4/autofs_i.h
++++ b/fs/autofs4/autofs_i.h
+@@ -130,6 +130,7 @@
+       int reghost_enabled;
+       int needs_reghost;
+       struct super_block *sb;
++      struct vfsmount *mnt;
+       struct mutex wq_mutex;
+       spinlock_t fs_lock;
+       struct autofs_wait_queue *queues; /* Wait queue pointer */
+--- a/fs/autofs4/init.c
++++ b/fs/autofs4/init.c
+@@ -17,7 +17,16 @@
+ static int autofs_get_sb(struct file_system_type *fs_type,
+       int flags, const char *dev_name, void *data, struct vfsmount *mnt)
+ {
+-      return get_sb_nodev(fs_type, flags, data, autofs4_fill_super, mnt);
++      struct autofs_sb_info *sbi;
++      int ret;
++
++      ret = get_sb_nodev(fs_type, flags, data, autofs4_fill_super, mnt);
++      if (ret)
++              return ret;
++
++      sbi = autofs4_sbi(mnt->mnt_sb);
++      sbi->mnt = mnt;
++      return 0;
+ }
+ static struct file_system_type autofs_fs_type = {
+--- a/fs/autofs4/root.c
++++ b/fs/autofs4/root.c
+@@ -179,6 +179,12 @@
+       DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d",
+               dentry, dentry->d_name.len, dentry->d_name.name, oz_mode,
+               nd->flags);
++
++      dput(nd->path.dentry);
++      mntput(nd->path.mnt);
++      nd->path.mnt = mntget(sbi->mnt);
++      nd->path.dentry = dget(dentry);
++
+       /*
+        * For an expire of a covered direct or offset mount we need
+        * to break out of follow_down() at the autofs mount trigger
+--- a/fs/compat.c
++++ b/fs/compat.c
+@@ -847,6 +847,9 @@
+       struct compat_old_linux_dirent __user *dirent;
+       compat_ulong_t d_ino;
++      if (d_type == DT_WHT)
++              return 0;
++
+       if (buf->result)
+               return -EINVAL;
+       d_ino = ino;
+@@ -918,6 +921,9 @@
+       compat_ulong_t d_ino;
+       int reclen = ALIGN(NAME_OFFSET(dirent) + namlen + 2, sizeof(compat_long_t));
++      if (d_type == DT_WHT)
++              return 0;
++
+       buf->error = -EINVAL;   /* only used if we fail.. */
+       if (reclen > buf->count)
+               return -EINVAL;
+@@ -1007,6 +1013,9 @@
+       int reclen = ALIGN(jj + namlen + 1, sizeof(u64));
+       u64 off;
++      if (d_type == DT_WHT)
++              return 0;
++
+       buf->error = -EINVAL;   /* only used if we fail.. */
+       if (reclen > buf->count)
+               return -EINVAL;
+--- a/fs/dcache.c
++++ b/fs/dcache.c
+@@ -18,6 +18,7 @@
+ #include <linux/string.h>
+ #include <linux/mm.h>
+ #include <linux/fs.h>
++#include <linux/union.h>
+ #include <linux/fsnotify.h>
+ #include <linux/slab.h>
+ #include <linux/init.h>
+@@ -157,14 +158,19 @@
+ }
+ /**
+- * d_kill - kill dentry and return parent
++ * __d_kill - kill dentry and return parent
+  * @dentry: dentry to kill
++ * @list: kill list
++ * @greedy: return parent instead of putting it on the kill list
+  *
+  * The dentry must already be unhashed and removed from the LRU.
+  *
+- * If this is the root of the dentry tree, return NULL.
++ * If this is the root of the dentry tree, return NULL. If greedy is zero, we
++ * put the parent of this dentry on the kill list instead. The callers must
++ * make sure that __d_kill_final() is called on all dentries on the kill list.
+  */
+-static struct dentry *d_kill(struct dentry *dentry)
++static struct dentry *__d_kill(struct dentry *dentry, struct list_head *list,
++                             int greedy)
+       __releases(dentry->d_lock)
+       __releases(dcache_lock)
+ {
+@@ -172,13 +178,78 @@
+       list_del(&dentry->d_u.d_child);
+       dentry_stat.nr_dentry--;        /* For d_free, below */
+-      /*drops the locks, at that point nobody can reach this dentry */
++
++      /*
++       * If we are not greedy we just put this on a list for later processing
++       * (follow up to parent, releasing of inode and freeing dentry memory).
++       */
++      if (!greedy) {
++              list_del_init(&dentry->d_alias);
++              /* at this point nobody can reach this dentry */
++              list_add(&dentry->d_lru, list);
++              spin_unlock(&dentry->d_lock);
++              spin_unlock(&dcache_lock);
++              __shrink_d_unions(dentry, list);
++              return NULL;
++      }
++
++      /* drops the locks, at that point nobody can reach this dentry */
+       dentry_iput(dentry);
++      /* If the dentry was in an union delete them */
++      __shrink_d_unions(dentry, list);
++      if (IS_ROOT(dentry))
++              parent = NULL;
++      else
++              parent = dentry->d_parent;
++      d_free(dentry);
++      return parent;
++}
++
++void __dput(struct dentry *, struct list_head *, int);
++
++static void __d_kill_final(struct dentry *dentry, struct list_head *list)
++{
++      struct dentry *parent;
++      struct inode *inode = dentry->d_inode;
++
++      if (inode) {
++              dentry->d_inode = NULL;
++              if (!inode->i_nlink)
++                      fsnotify_inoderemove(inode);
++              if (dentry->d_op && dentry->d_op->d_iput)
++                      dentry->d_op->d_iput(dentry, inode);
++              else
++                      iput(inode);
++      }
++
+       if (IS_ROOT(dentry))
+               parent = NULL;
+       else
+               parent = dentry->d_parent;
+       d_free(dentry);
++      __dput(parent, list, 1);
++}
++
++/**
++ * d_kill - kill dentry and return parent
++ * @dentry: dentry to kill
++ *
++ * The dentry must already be unhashed and removed from the LRU.
++ *
++ * If this is the root of the dentry tree, return NULL.
++ */
++static struct dentry *d_kill(struct dentry *dentry)
++{
++      LIST_HEAD(mortuary);
++      struct dentry *parent;
++
++      parent = __d_kill(dentry, &mortuary, 1);
++      while (!list_empty(&mortuary)) {
++              dentry = list_entry(mortuary.next, struct dentry, d_lru);
++              list_del(&dentry->d_lru);
++              __d_kill_final(dentry, &mortuary);
++      }
++
+       return parent;
+ }
+@@ -199,19 +270,24 @@
+  * Real recursion would eat up our stack space.
+  */
+-/*
+- * dput - release a dentry
+- * @dentry: dentry to release 
++/**
++ * __dput - release a dentry
++ * @dentry: dentry to release
++ * @list: kill list argument for __d_kill()
++ * @greedy: greedy argument for __d_kill()
+  *
+  * Release a dentry. This will drop the usage count and if appropriate
+  * call the dentry unlink method as well as removing it from the queues and
+  * releasing its resources. If the parent dentries were scheduled for release
+- * they too may now get deleted.
++ * they too may now get deleted if @greedy is not zero. Otherwise parent is
++ * added to the kill list. The callers must make sure that __d_kill_final() is
++ * called on all dentries on the kill list.
++ *
++ * You probably want to use dput() instead.
+  *
+  * no dcache lock, please.
+  */
+-
+-void dput(struct dentry *dentry)
++void __dput(struct dentry *dentry, struct list_head *list, int greedy)
+ {
+       if (!dentry)
+               return;
+@@ -252,12 +328,35 @@
+ kill_it:
+       /* if dentry was on the d_lru list delete it from there */
+       dentry_lru_del(dentry);
+-      dentry = d_kill(dentry);
++      dentry = __d_kill(dentry, list, greedy);
+       if (dentry)
+               goto repeat;
+ }
+ /**
++ * dput - release a dentry
++ * @dentry: dentry to release
++ *
++ * Release a dentry. This will drop the usage count and if appropriate
++ * call the dentry unlink method as well as removing it from the queues and
++ * releasing its resources. If the parent dentries were scheduled for release
++ * they too may now get deleted.
++ *
++ * no dcache lock, please.
++ */
++void dput(struct dentry *dentry)
++{
++      LIST_HEAD(mortuary);
++
++      __dput(dentry, &mortuary, 1);
++      while (!list_empty(&mortuary)) {
++              dentry = list_entry(mortuary.next, struct dentry, d_lru);
++              list_del(&dentry->d_lru);
++              __d_kill_final(dentry, &mortuary);
++      }
++}
++
++/**
+  * d_invalidate - invalidate a dentry
+  * @dentry: dentry to invalidate
+  *
+@@ -689,6 +788,7 @@
+                                       iput(inode);
+                       }
++                      shrink_d_unions(dentry);
+                       d_free(dentry);
+                       /* finished when we fall off the top of the tree,
+@@ -951,6 +1051,10 @@
+       INIT_LIST_HEAD(&dentry->d_lru);
+       INIT_LIST_HEAD(&dentry->d_subdirs);
+       INIT_LIST_HEAD(&dentry->d_alias);
++#ifdef CONFIG_UNION_MOUNT
++      INIT_LIST_HEAD(&dentry->d_unions);
++      dentry->d_unionized = 0;
++#endif
+       if (parent) {
+               dentry->d_parent = dget(parent);
+@@ -981,8 +1085,10 @@
+ /* the caller must hold dcache_lock */
+ static void __d_instantiate(struct dentry *dentry, struct inode *inode)
+ {
+-      if (inode)
++      if (inode) {
++              dentry->d_flags &= ~(DCACHE_WHITEOUT|DCACHE_FALLTHRU);
+               list_add(&dentry->d_alias, &inode->i_dentry);
++      }
+       dentry->d_inode = inode;
+       fsnotify_d_instantiate(dentry, inode);
+ }
+@@ -1513,7 +1619,9 @@
+       spin_lock(&dentry->d_lock);
+       isdir = S_ISDIR(dentry->d_inode->i_mode);
+       if (atomic_read(&dentry->d_count) == 1) {
++              __d_drop_unions(dentry);
+               dentry_iput(dentry);
++              shrink_d_unions(dentry);
+               fsnotify_nameremove(dentry, isdir);
+               return;
+       }
+@@ -1524,14 +1632,14 @@
+       spin_unlock(&dentry->d_lock);
+       spin_unlock(&dcache_lock);
++      shrink_d_unions(dentry);
+       fsnotify_nameremove(dentry, isdir);
+ }
+ static void __d_rehash(struct dentry * entry, struct hlist_head *list)
+ {
+-
+-      entry->d_flags &= ~DCACHE_UNHASHED;
+-      hlist_add_head_rcu(&entry->d_hash, list);
++      entry->d_flags &= ~DCACHE_UNHASHED;
++      hlist_add_head_rcu(&entry->d_hash, list);
+ }
+ static void _d_rehash(struct dentry * entry)
+@@ -1550,6 +1658,7 @@
+ {
+       spin_lock(&dcache_lock);
+       spin_lock(&entry->d_lock);
++      BUG_ON(!d_unhashed(entry));
+       _d_rehash(entry);
+       spin_unlock(&entry->d_lock);
+       spin_unlock(&dcache_lock);
+@@ -2182,7 +2291,9 @@
+               struct list_head *tmp = next;
+               struct dentry *dentry = list_entry(tmp, struct dentry, d_u.d_child);
+               next = tmp->next;
+-              if (d_unhashed(dentry)||!dentry->d_inode)
++              if (d_unhashed(dentry)||(!dentry->d_inode &&
++                                       !d_is_whiteout(dentry) &&
++                                       !d_is_fallthru(dentry)))
+                       continue;
+               if (!list_empty(&dentry->d_subdirs)) {
+                       this_parent = dentry;
+--- a/fs/ext2/dir.c
++++ b/fs/ext2/dir.c
+@@ -219,7 +219,8 @@
+ {
+       if (len != de->name_len)
+               return 0;
+-      if (!de->inode)
++      if (!de->inode && ((de->file_type != EXT2_FT_WHT) &&
++                         (de->file_type != EXT2_FT_FALLTHRU)))
+               return 0;
+       return !memcmp(name, de->name, len);
+ }
+@@ -255,6 +256,8 @@
+       [EXT2_FT_FIFO]          = DT_FIFO,
+       [EXT2_FT_SOCK]          = DT_SOCK,
+       [EXT2_FT_SYMLINK]       = DT_LNK,
++      [EXT2_FT_WHT]           = DT_WHT,
++      [EXT2_FT_FALLTHRU]      = DT_UNKNOWN,
+ };
+ #define S_SHIFT 12
+@@ -341,6 +344,18 @@
+                                       ext2_put_page(page);
+                                       return 0;
+                               }
++                      } else if (de->file_type == EXT2_FT_FALLTHRU) {
++                              int over;
++                              unsigned char d_type = DT_UNKNOWN;
++
++                              offset = (char *)de - kaddr;
++                              over = filldir(dirent, de->name, de->name_len,
++                                              (n<<PAGE_CACHE_SHIFT) | offset,
++                                              123, d_type);
++                              if (over) {
++                                      ext2_put_page(page);
++                                      return 0;
++                              }
+                       }
+                       filp->f_pos += ext2_rec_len_from_disk(de->rec_len);
+               }
+@@ -448,6 +463,30 @@
+       return res;
+ }
++/* Special version for filetype based whiteout support */
++ino_t ext2_inode_by_dentry(struct inode *dir, struct dentry *dentry)
++{
++      ino_t res = 0;
++      struct ext2_dir_entry_2 *de;
++      struct page *page;
++
++      de = ext2_find_entry (dir, &dentry->d_name, &page);
++      if (de) {
++              res = le32_to_cpu(de->inode);
++              if (!res && de->file_type == EXT2_FT_WHT) {
++                      spin_lock(&dentry->d_lock);
++                      dentry->d_flags |= DCACHE_WHITEOUT;
++                      spin_unlock(&dentry->d_lock);
++              } else if(!res && de->file_type == EXT2_FT_FALLTHRU) {
++                      spin_lock(&dentry->d_lock);
++                      dentry->d_flags |= DCACHE_FALLTHRU;
++                      spin_unlock(&dentry->d_lock);
++              }
++              ext2_put_page(page);
++      }
++      return res;
++}
++
+ /* Releases the page */
+ void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de,
+                  struct page *page, struct inode *inode, int update_times)
+@@ -472,9 +511,10 @@
+ }
+ /*
+- *    Parent is locked.
++ * Find or append a given dentry to the parent directory
+  */
+-int ext2_add_link (struct dentry *dentry, struct inode *inode)
++static ext2_dirent * ext2_append_entry(struct dentry * dentry,
++                                     struct page ** page)
+ {
+       struct inode *dir = dentry->d_parent->d_inode;
+       const char *name = dentry->d_name.name;
+@@ -482,13 +522,10 @@
+       unsigned chunk_size = ext2_chunk_size(dir);
+       unsigned reclen = EXT2_DIR_REC_LEN(namelen);
+       unsigned short rec_len, name_len;
+-      struct page *page = NULL;
+-      ext2_dirent * de;
++      ext2_dirent * de = NULL;
+       unsigned long npages = dir_pages(dir);
+       unsigned long n;
+       char *kaddr;
+-      loff_t pos;
+-      int err;
+       /*
+        * We take care of directory expansion in the same loop.
+@@ -498,55 +535,97 @@
+       for (n = 0; n <= npages; n++) {
+               char *dir_end;
+-              page = ext2_get_page(dir, n, 0);
+-              err = PTR_ERR(page);
+-              if (IS_ERR(page))
++              *page = ext2_get_page(dir, n, 0);
++              de = ERR_PTR(PTR_ERR(*page));
++              if (IS_ERR(*page))
+                       goto out;
+-              lock_page(page);
+-              kaddr = page_address(page);
++              lock_page(*page);
++              kaddr = page_address(*page);
+               dir_end = kaddr + ext2_last_byte(dir, n);
+               de = (ext2_dirent *)kaddr;
+               kaddr += PAGE_CACHE_SIZE - reclen;
+               while ((char *)de <= kaddr) {
+                       if ((char *)de == dir_end) {
+                               /* We hit i_size */
+-                              name_len = 0;
+-                              rec_len = chunk_size;
++                              de->name_len = 0;
+                               de->rec_len = ext2_rec_len_to_disk(chunk_size);
+                               de->inode = 0;
++                              de->file_type = 0;
+                               goto got_it;
+                       }
+                       if (de->rec_len == 0) {
+                               ext2_error(dir->i_sb, __func__,
+                                       "zero-length directory entry");
+-                              err = -EIO;
++                              de = ERR_PTR(-EIO);
+                               goto out_unlock;
+                       }
+-                      err = -EEXIST;
+                       if (ext2_match (namelen, name, de))
+-                              goto out_unlock;
++                              goto got_it;
+                       name_len = EXT2_DIR_REC_LEN(de->name_len);
+                       rec_len = ext2_rec_len_from_disk(de->rec_len);
+-                      if (!de->inode && rec_len >= reclen)
++                      if (!de->inode && (de->file_type != EXT2_FT_WHT) &&
++                          (de->file_type != EXT2_FT_FALLTHRU) &&
++                          (rec_len >= reclen))
+                               goto got_it;
+                       if (rec_len >= name_len + reclen)
+                               goto got_it;
+                       de = (ext2_dirent *) ((char *) de + rec_len);
+               }
+-              unlock_page(page);
+-              ext2_put_page(page);
++              unlock_page(*page);
++              ext2_put_page(*page);
+       }
++
+       BUG();
+-      return -EINVAL;
+ got_it:
++      return de;
++      /* OFFSET_CACHE */
++out_unlock:
++      unlock_page(*page);
++      ext2_put_page(*page);
++out:
++      return de;
++}
++
++/*
++ *    Parent is locked.
++ */
++int ext2_add_link (struct dentry *dentry, struct inode *inode)
++{
++      struct inode *dir = dentry->d_parent->d_inode;
++      const char *name = dentry->d_name.name;
++      int namelen = dentry->d_name.len;
++      unsigned short rec_len, name_len;
++      ext2_dirent * de;
++      struct page *page;
++      loff_t pos;
++      int err;
++
++      de = ext2_append_entry(dentry, &page);
++      if (IS_ERR(de))
++              return PTR_ERR(de);
++
++      err = -EEXIST;
++      if (ext2_match (namelen, name, de)) {
++              if ((de->file_type == EXT2_FT_WHT) ||
++                  (de->file_type == EXT2_FT_FALLTHRU))
++                      goto got_it;
++              goto out_unlock;
++      }
++
++got_it:
++      name_len = EXT2_DIR_REC_LEN(de->name_len);
++      rec_len = ext2_rec_len_from_disk(de->rec_len);
++
+       pos = page_offset(page) +
+               (char*)de - (char*)page_address(page);
+       err = __ext2_write_begin(NULL, page->mapping, pos, rec_len, 0,
+                                                       &page, NULL);
+       if (err)
+               goto out_unlock;
+-      if (de->inode) {
++      if (de->inode || (((de->file_type == EXT2_FT_WHT) ||
++                         (de->file_type == EXT2_FT_FALLTHRU)) &&
++                        !ext2_match (namelen, name, de))) {
+               ext2_dirent *de1 = (ext2_dirent *) ((char *) de + name_len);
+               de1->rec_len = ext2_rec_len_to_disk(rec_len - name_len);
+               de->rec_len = ext2_rec_len_to_disk(name_len);
+@@ -563,7 +642,60 @@
+       /* OFFSET_CACHE */
+ out_put:
+       ext2_put_page(page);
+-out:
++      return err;
++out_unlock:
++      unlock_page(page);
++      goto out_put;
++}
++
++/*
++ * Create a fallthru entry.
++ */
++int ext2_fallthru_entry (struct inode *dir, struct dentry *dentry)
++{
++      const char *name = dentry->d_name.name;
++      int namelen = dentry->d_name.len;
++      unsigned short rec_len, name_len;
++      ext2_dirent * de;
++      struct page *page;
++      loff_t pos;
++      int err;
++
++      de = ext2_append_entry(dentry, &page);
++      if (IS_ERR(de))
++              return PTR_ERR(de);
++
++      err = -EEXIST;
++      if (ext2_match (namelen, name, de))
++              goto out_unlock;
++
++      name_len = EXT2_DIR_REC_LEN(de->name_len);
++      rec_len = ext2_rec_len_from_disk(de->rec_len);
++
++      pos = page_offset(page) +
++              (char*)de - (char*)page_address(page);
++      err = __ext2_write_begin(NULL, page->mapping, pos, rec_len, 0,
++                                                      &page, NULL);
++      if (err)
++              goto out_unlock;
++      if (de->inode || (de->file_type == EXT2_FT_WHT) ||
++          (de->file_type == EXT2_FT_FALLTHRU)) {
++              ext2_dirent *de1 = (ext2_dirent *) ((char *) de + name_len);
++              de1->rec_len = ext2_rec_len_to_disk(rec_len - name_len);
++              de->rec_len = ext2_rec_len_to_disk(name_len);
++              de = de1;
++      }
++      de->name_len = namelen;
++      memcpy(de->name, name, namelen);
++      de->inode = 0;
++      de->file_type = EXT2_FT_FALLTHRU;
++      err = ext2_commit_chunk(page, pos, rec_len);
++      dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
++      EXT2_I(dir)->i_flags &= ~EXT2_BTREE_FL;
++      mark_inode_dirty(dir);
++      /* OFFSET_CACHE */
++out_put:
++      ext2_put_page(page);
+       return err;
+ out_unlock:
+       unlock_page(page);
+@@ -616,6 +748,70 @@
+       return err;
+ }
++int ext2_whiteout_entry (struct inode * dir, struct dentry * dentry,
++                       struct ext2_dir_entry_2 * de, struct page * page)
++{
++      const char *name = dentry->d_name.name;
++      int namelen = dentry->d_name.len;
++      unsigned short rec_len, name_len;
++      loff_t pos;
++      int err;
++
++      if (!de) {
++              de = ext2_append_entry(dentry, &page);
++              BUG_ON(!de);
++      }
++
++      err = -EEXIST;
++      if (ext2_match (namelen, name, de) &&
++          (de->file_type == EXT2_FT_WHT)) {
++              ext2_error(dir->i_sb, __func__,
++                         "entry is already a whiteout in directory #%lu",
++                         dir->i_ino);
++              goto out_unlock;
++      }
++
++      name_len = EXT2_DIR_REC_LEN(de->name_len);
++      rec_len = ext2_rec_len_from_disk(de->rec_len);
++
++      pos = page_offset(page) +
++              (char*)de - (char*)page_address(page);
++      err = __ext2_write_begin(NULL, page->mapping, pos, rec_len, 0,
++                                                      &page, NULL);
++      if (err)
++              goto out_unlock;
++      /*
++       * We whiteout an existing entry. Do what ext2_delete_entry() would do,
++       * except that we don't need to merge with the previous entry since
++       * we are going to reuse it.
++       */
++      if (ext2_match (namelen, name, de))
++              de->inode = 0;
++      if (de->inode || (((de->file_type == EXT2_FT_WHT) ||
++                         (de->file_type == EXT2_FT_FALLTHRU)) &&
++                        !ext2_match (namelen, name, de))) {
++              ext2_dirent *de1 = (ext2_dirent *) ((char *) de + name_len);
++              de1->rec_len = ext2_rec_len_to_disk(rec_len - name_len);
++              de->rec_len = ext2_rec_len_to_disk(name_len);
++              de = de1;
++      }
++      de->name_len = namelen;
++      memcpy(de->name, name, namelen);
++      de->inode = 0;
++      de->file_type = EXT2_FT_WHT;
++      err = ext2_commit_chunk(page, pos, rec_len);
++      dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
++      EXT2_I(dir)->i_flags &= ~EXT2_BTREE_FL;
++      mark_inode_dirty(dir);
++      /* OFFSET_CACHE */
++out_put:
++      ext2_put_page(page);
++      return err;
++out_unlock:
++      unlock_page(page);
++      goto out_put;
++}
++
+ /*
+  * Set the first fragment of directory.
+  */
+--- a/fs/ext2/ext2.h
++++ b/fs/ext2/ext2.h
+@@ -102,9 +102,13 @@
+ /* dir.c */
+ extern int ext2_add_link (struct dentry *, struct inode *);
+ extern ino_t ext2_inode_by_name(struct inode *, struct qstr *);
++extern ino_t ext2_inode_by_dentry(struct inode *, struct dentry *);
+ extern int ext2_make_empty(struct inode *, struct inode *);
+ extern struct ext2_dir_entry_2 * ext2_find_entry (struct inode *,struct qstr *, struct page **);
+ extern int ext2_delete_entry (struct ext2_dir_entry_2 *, struct page *);
++extern int ext2_whiteout_entry (struct inode *, struct dentry *,
++                              struct ext2_dir_entry_2 *, struct page *);
++extern int ext2_fallthru_entry (struct inode *, struct dentry *);
+ extern int ext2_empty_dir (struct inode *);
+ extern struct ext2_dir_entry_2 * ext2_dotdot (struct inode *, struct page **);
+ extern void ext2_set_link(struct inode *, struct ext2_dir_entry_2 *, struct page *, struct inode *, int);
+--- a/fs/ext2/inode.c
++++ b/fs/ext2/inode.c
+@@ -1176,7 +1176,8 @@
+ {
+       unsigned int flags = EXT2_I(inode)->i_flags;
+-      inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC);
++      inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC|
++                          S_OPAQUE);
+       if (flags & EXT2_SYNC_FL)
+               inode->i_flags |= S_SYNC;
+       if (flags & EXT2_APPEND_FL)
+@@ -1187,6 +1188,8 @@
+               inode->i_flags |= S_NOATIME;
+       if (flags & EXT2_DIRSYNC_FL)
+               inode->i_flags |= S_DIRSYNC;
++      if (flags & EXT2_OPAQUE_FL)
++              inode->i_flags |= S_OPAQUE;
+ }
+ /* Propagate flags from i_flags to EXT2_I(inode)->i_flags */
+@@ -1194,8 +1197,8 @@
+ {
+       unsigned int flags = ei->vfs_inode.i_flags;
+-      ei->i_flags &= ~(EXT2_SYNC_FL|EXT2_APPEND_FL|
+-                      EXT2_IMMUTABLE_FL|EXT2_NOATIME_FL|EXT2_DIRSYNC_FL);
++      ei->i_flags &= ~(EXT2_SYNC_FL|EXT2_APPEND_FL|EXT2_IMMUTABLE_FL|
++                       EXT2_NOATIME_FL|EXT2_DIRSYNC_FL|EXT2_OPAQUE_FL);
+       if (flags & S_SYNC)
+               ei->i_flags |= EXT2_SYNC_FL;
+       if (flags & S_APPEND)
+@@ -1206,6 +1209,8 @@
+               ei->i_flags |= EXT2_NOATIME_FL;
+       if (flags & S_DIRSYNC)
+               ei->i_flags |= EXT2_DIRSYNC_FL;
++      if (flags & S_OPAQUE)
++              ei->i_flags |= EXT2_OPAQUE_FL;
+ }
+ struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
+--- a/fs/ext2/namei.c
++++ b/fs/ext2/namei.c
+@@ -54,15 +54,16 @@
+  * Methods themselves.
+  */
+-static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd)
++static struct dentry *ext2_lookup(struct inode * dir, struct dentry *dentry,
++                                struct nameidata *nd)
+ {
+       struct inode * inode;
+       ino_t ino;
+-      
++
+       if (dentry->d_name.len > EXT2_NAME_LEN)
+               return ERR_PTR(-ENAMETOOLONG);
+-      ino = ext2_inode_by_name(dir, &dentry->d_name);
++      ino = ext2_inode_by_dentry(dir, dentry);
+       inode = NULL;
+       if (ino) {
+               inode = ext2_iget(dir->i_sb, ino);
+@@ -230,6 +231,10 @@
+       else
+               inode->i_mapping->a_ops = &ext2_aops;
++      /* if we call mkdir on a whiteout create an opaque directory */
++      if (dentry->d_flags & DCACHE_WHITEOUT)
++              inode->i_flags |= S_OPAQUE;
++
+       inode_inc_link_count(inode);
+       err = ext2_make_empty(inode, dir);
+@@ -293,6 +298,78 @@
+       return err;
+ }
++/*
++ * Create a whiteout for the dentry
++ */
++static int ext2_whiteout(struct inode *dir, struct dentry *dentry,
++                       struct dentry *new_dentry)
++{
++      struct inode * inode = dentry->d_inode;
++      struct ext2_dir_entry_2 * de = NULL;
++      struct page * page;
++      int err = -ENOTEMPTY;
++
++      if (!EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb,
++                                     EXT2_FEATURE_INCOMPAT_FILETYPE)) {
++              ext2_error (dir->i_sb, "ext2_whiteout",
++                          "can't set whiteout filetype");
++              err = -EPERM;
++              goto out;
++      }
++
++      if (inode) {
++              if (S_ISDIR(inode->i_mode) && !ext2_empty_dir(inode))
++                      goto out;
++
++              err = -ENOENT;
++              de = ext2_find_entry (dir, &dentry->d_name, &page);
++              if (!de)
++                      goto out;
++              lock_page(page);
++      }
++
++      err = ext2_whiteout_entry (dir, dentry, de, page);
++      if (err)
++              goto out;
++
++      spin_lock(&new_dentry->d_lock);
++      new_dentry->d_flags &= ~DCACHE_FALLTHRU;
++      new_dentry->d_flags |= DCACHE_WHITEOUT;
++      spin_unlock(&new_dentry->d_lock);
++      d_add(new_dentry, NULL);
++
++      if (inode) {
++              inode->i_ctime = dir->i_ctime;
++              inode_dec_link_count(inode);
++              if (S_ISDIR(inode->i_mode)) {
++                      inode->i_size = 0;
++                      inode_dec_link_count(inode);
++                      inode_dec_link_count(dir);
++              }
++      }
++      err = 0;
++out:
++      return err;
++}
++
++/*
++ * Create a fallthru entry.
++ */
++static int ext2_fallthru (struct inode *dir, struct dentry *dentry)
++{
++      int err;
++
++      err = ext2_fallthru_entry(dir, dentry);
++      if (err)
++              return err;
++
++      d_instantiate(dentry, NULL);
++      spin_lock(&dentry->d_lock);
++      dentry->d_flags |= DCACHE_FALLTHRU;
++      spin_unlock(&dentry->d_lock);
++      return 0;
++}
++
+ static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry,
+       struct inode * new_dir, struct dentry * new_dentry )
+ {
+@@ -392,6 +469,8 @@
+       .mkdir          = ext2_mkdir,
+       .rmdir          = ext2_rmdir,
+       .mknod          = ext2_mknod,
++      .whiteout       = ext2_whiteout,
++      .fallthru       = ext2_fallthru,
+       .rename         = ext2_rename,
+ #ifdef CONFIG_EXT2_FS_XATTR
+       .setxattr       = generic_setxattr,
+--- a/fs/ext2/super.c
++++ b/fs/ext2/super.c
+@@ -1062,6 +1062,13 @@
+       if (EXT2_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_HAS_JOURNAL))
+               ext2_warning(sb, __func__,
+                       "mounting ext3 filesystem as ext2");
++
++      /*
++       * Whiteouts (and fallthrus) require explicit whiteout support.
++       */
++      if (EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_WHITEOUT))
++              sb->s_flags |= MS_WHITEOUT;
++
+       ext2_setup_super (sb, es, sb->s_flags & MS_RDONLY);
+       return 0;
+--- a/fs/Kconfig
++++ b/fs/Kconfig
+@@ -58,6 +58,14 @@
+ source "fs/quota/Kconfig"
++config UNION_MOUNT
++       bool "Union mount support (EXPERIMENTAL)"
++       depends on EXPERIMENTAL
++       ---help---
++         If you say Y here, you will be able to mount file systems as
++         union mount stacks. This is a VFS based implementation and
++         should work with all file systems. If unsure, say N.
++
+ source "fs/autofs/Kconfig"
+ source "fs/autofs4/Kconfig"
+ source "fs/fuse/Kconfig"
+--- a/fs/libfs.c
++++ b/fs/libfs.c
+@@ -133,6 +133,7 @@
+       struct dentry *cursor = filp->private_data;
+       struct list_head *p, *q = &cursor->d_u.d_child;
+       ino_t ino;
++      int d_type;
+       int i = filp->f_pos;
+       switch (i) {
+@@ -158,14 +159,25 @@
+                       for (p=q->next; p != &dentry->d_subdirs; p=p->next) {
+                               struct dentry *next;
+                               next = list_entry(p, struct dentry, d_u.d_child);
+-                              if (d_unhashed(next) || !next->d_inode)
++                              if (d_unhashed(next) || (!next->d_inode && !d_is_fallthru(next)))
+                                       continue;
++                              if (d_is_fallthru(next)) {
++                                      /* XXX Make up things we can
++                                       * only get out of the inode.
++                                       * Should probably really do a
++                                       * lookup instead. */
++                                      ino = 100; /* XXX Made up number of no significance */
++                                      d_type = DT_UNKNOWN;
++                              } else {
++                                      ino = next->d_inode->i_ino;
++                                      d_type = dt_type(next->d_inode);
++                              }
++
+                               spin_unlock(&dcache_lock);
+                               if (filldir(dirent, next->d_name.name, 
+                                           next->d_name.len, filp->f_pos, 
+-                                          next->d_inode->i_ino, 
+-                                          dt_type(next->d_inode)) < 0)
++                                          ino, d_type) < 0)
+                                       return 0;
+                               spin_lock(&dcache_lock);
+                               /* next is still alive */
+--- a/fs/Makefile
++++ b/fs/Makefile
+@@ -52,6 +52,7 @@
+ obj-$(CONFIG_GENERIC_ACL)     += generic_acl.o
+ obj-y                         += quota/
++obj-$(CONFIG_UNION_MOUNT)     += union.o
+ obj-$(CONFIG_PROC_FS)         += proc/
+ obj-y                         += partitions/
+--- a/fs/namei.c
++++ b/fs/namei.c
+@@ -33,6 +33,7 @@
+ #include <linux/fcntl.h>
+ #include <linux/device_cgroup.h>
+ #include <linux/fs_struct.h>
++#include <linux/union.h>
+ #include <asm/uaccess.h>
+ #define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
+@@ -229,16 +230,17 @@
+ }
+ /**
+- * inode_permission  -  check for access rights to a given inode
++ * __inode_permission  -  check for access rights to a given inode
+  * @inode:    inode to check permission on
+  * @mask:     right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
++ * @rofs:     check for read-only fs
+  *
+  * Used to check for read/write/execute permissions on an inode.
+  * We use "fsuid" for this, letting us set arbitrary permissions
+  * for filesystem access without changing the "normal" uids which
+  * are used for other things.
+  */
+-int inode_permission(struct inode *inode, int mask)
++int __inode_permission(struct inode *inode, int mask, int rofs)
+ {
+       int retval;
+@@ -248,7 +250,7 @@
+               /*
+                * Nobody gets write access to a read-only fs.
+                */
+-              if (IS_RDONLY(inode) &&
++              if ((rofs & IS_RDONLY(inode)) &&
+                   (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
+                       return -EROFS;
+@@ -276,6 +278,18 @@
+ }
+ /**
++ * inode_permission  -  check for access rights to a given inode
++ * @inode:    inode to check permission on
++ * @mask:     right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
++ *
++ * This version pays attention to the MS_RDONLY flag on the fs.
++ */
++int inode_permission(struct inode *inode, int mask)
++{
++      return __inode_permission(inode, mask, 1);
++}
++
++/**
+  * file_permission  -  check for additional access rights to a given file
+  * @file:     file to check access rights for
+  * @mask:     right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
+@@ -404,15 +418,10 @@
+  * Internal lookup() using the new generic dcache.
+  * SMP-safe
+  */
+-static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name, struct nameidata *nd)
++static struct dentry *cache_lookup(struct dentry *parent, struct qstr *name,
++                                 struct nameidata *nd)
+ {
+-      struct dentry * dentry = __d_lookup(parent, name);
+-
+-      /* lockess __d_lookup may fail due to concurrent d_move() 
+-       * in some unrelated directory, so try with d_lookup
+-       */
+-      if (!dentry)
+-              dentry = d_lookup(parent, name);
++      struct dentry *dentry = d_lookup(parent, name);
+       if (dentry && dentry->d_op && dentry->d_op->d_revalidate)
+               dentry = do_revalidate(dentry, nd);
+@@ -421,6 +430,208 @@
+ }
+ /*
++ * Theory of operation for opaque, whiteout, and fallthru:
++ *
++ * whiteout: Unconditionally stop lookup here - ENOENT
++ *
++ * opaque: Don't lookup in directories lower in the union stack
++ *
++ * fallthru: While looking up an entry, ignore the opaque flag for the
++ * current directory only.
++ *
++ * A union stack is a linked list of directory dentries which appear
++ * in the same place in the namespace.  When constructing the union
++ * stack, we include directories below opaque directories so that we
++ * can properly handle fallthrus.  All non-fallthru lookups have to
++ * check for the opaque flag on the parent directory and obey it.
++ *
++ * In general, the code pattern is to lookup the the topmost entry
++ * first (either the first visible non-negative dentry or a negative
++ * dentry in the topmost layer of the union), then build the union
++ * stack for the newly looked-up entry (if it is a directory).
++ */
++
++/**
++ * __cache_lookup_topmost - lookup the topmost (non-)negative dentry
++ *
++ * @nd - parent's nameidata
++ * @name - pathname part to lookup
++ * @path - found dentry for pathname part
++ *
++ * This is used for union mount lookups from dcache. The first non-negative
++ * dentry is searched on all layers of the union stack. Otherwise the topmost
++ * negative dentry is returned.
++ */
++static int __cache_lookup_topmost(struct nameidata *nd, struct qstr *name,
++                                struct path *path)
++{
++      struct dentry *dentry;
++
++      dentry = d_lookup(nd->path.dentry, name);
++      if (dentry && dentry->d_op && dentry->d_op->d_revalidate)
++              dentry = do_revalidate(dentry, nd);
++
++      /*
++       * Remember the topmost negative dentry in case we don't find anything
++       */
++      path->dentry = dentry;
++      path->mnt = dentry ? nd->path.mnt : NULL;
++
++      if (!dentry || (dentry->d_inode || d_is_whiteout(dentry)))
++              return !dentry;
++
++      /* Keep going through opaque directories if we found a fallthru */
++      if (IS_OPAQUE(nd->path.dentry->d_inode) && !d_is_fallthru(dentry))
++              return !dentry;
++
++      /* look for the first non-negative or whiteout dentry */
++
++      while (follow_union_down(&nd->path)) {
++              dentry = d_hash_and_lookup(nd->path.dentry, name);
++
++              /*
++               * If parts of the union stack are not in the dcache we need
++               * to do a real lookup
++               */
++              if (!dentry)
++                      goto out_dput;
++
++              /*
++               * If parts of the union don't survive the revalidation we
++               * need to do a real lookup
++               */
++              if (dentry->d_op && dentry->d_op->d_revalidate) {
++                      dentry = do_revalidate(dentry, nd);
++                      if (!dentry)
++                              goto out_dput;
++              }
++
++              if (dentry->d_inode || d_is_whiteout(dentry))
++                      goto out_dput;
++
++              /* Stop the lookup on opaque parent and non-fallthru child */
++              if (IS_OPAQUE(nd->path.dentry->d_inode) && !d_is_fallthru(dentry))
++                      goto out_dput;
++
++              dput(dentry);
++      }
++
++      return !dentry;
++
++out_dput:
++      dput(path->dentry);
++      path->dentry = dentry;
++      path->mnt = dentry ? mntget(nd->path.mnt) : NULL;
++      return !dentry;
++}
++
++/**
++ * __cache_lookup_build_union - build the union stack for this part,
++ * cached version
++ *
++ * This is called after you have the topmost dentry in @path.
++ */
++static int __cache_lookup_build_union(struct nameidata *nd, struct qstr *name,
++                                    struct path *path)
++{
++      struct path last = *path;
++      struct dentry *dentry;
++
++      while (follow_union_down(&nd->path)) {
++              dentry = d_hash_and_lookup(nd->path.dentry, name);
++              if (!dentry)
++                      return 1;
++
++              if (dentry->d_op && dentry->d_op->d_revalidate) {
++                      dentry = do_revalidate(dentry, nd);
++                      if (!dentry)
++                              return 1;
++              }
++
++              if (d_is_whiteout(dentry)) {
++                      dput(dentry);
++                      break;
++              }
++
++              if (!dentry->d_inode) {
++                      dput(dentry);
++                      continue;
++              }
++
++              /* only directories can be part of a union stack */
++              if (!S_ISDIR(dentry->d_inode->i_mode)) {
++                      dput(dentry);
++                      break;
++              }
++
++              /* Add the newly discovered dir to the union stack */
++              append_to_union(last.mnt, last.dentry, nd->path.mnt, dentry);
++
++              if (last.dentry != path->dentry)
++                      path_put(&last);
++              last.dentry = dentry;
++              last.mnt = mntget(nd->path.mnt);
++      }
++
++      if (last.dentry != path->dentry)
++              path_put(&last);
++
++      return 0;
++}
++
++/**
++ * cache_lookup_union - lookup a single pathname part from dcache
++ *
++ * This is a union mount capable version of what d_lookup() & revalidate()
++ * would do. This function returns a valid (union) dentry on success.
++ *
++ * Remember: On failure it means that parts of the union aren't cached. You
++ * should call real_lookup() afterwards to find the proper (union) dentry.
++ */
++static int cache_lookup_union(struct nameidata *nd, struct qstr *name,
++                            struct path *path)
++{
++      int res ;
++
++      if (!IS_MNT_UNION(nd->path.mnt)) {
++              path->dentry = cache_lookup(nd->path.dentry, name, nd);
++              path->mnt = path->dentry ? nd->path.mnt : NULL;
++              res = path->dentry ? 0 : 1;
++      } else {
++              struct path safe = {
++                      .dentry = nd->path.dentry,
++                      .mnt = nd->path.mnt
++              };
++
++              path_get(&safe);
++              res = __cache_lookup_topmost(nd, name, path);
++              if (res)
++                      goto out;
++
++              /* only directories can be part of a union stack */
++              if (!path->dentry->d_inode ||
++                  !S_ISDIR(path->dentry->d_inode->i_mode))
++                      goto out;
++
++              /* Build the union stack for this part */
++              res = __cache_lookup_build_union(nd, name, path);
++              if (res) {
++                      dput(path->dentry);
++                      if (path->mnt != safe.mnt)
++                              mntput(path->mnt);
++                      goto out;
++              }
++
++out:
++              path_put(&nd->path);
++              nd->path.dentry = safe.dentry;
++              nd->path.mnt = safe.mnt;
++      }
++
++      return res;
++}
++
++/*
+  * Short-cut version of permission(), for calling by
+  * path_walk(), when dcache lock is held.  Combines parts
+  * of permission() and generic_permission(), and tests ONLY for
+@@ -467,10 +678,11 @@
+  * make sure that nobody added the entry to the dcache in the meantime..
+  * SMP-safe
+  */
+-static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, struct nameidata *nd)
++static int real_lookup(struct nameidata *nd, struct qstr *name,
++                     struct path *path)
+ {
+-      struct dentry * result;
+-      struct inode *dir = parent->d_inode;
++      struct inode *dir = nd->path.dentry->d_inode;
++      int res = 0;
+       mutex_lock(&dir->i_mutex);
+       /*
+@@ -487,27 +699,36 @@
+        *
+        * so doing d_lookup() (with seqlock), instead of lockfree __d_lookup
+        */
+-      result = d_lookup(parent, name);
+-      if (!result) {
++      path->dentry = d_lookup(nd->path.dentry, name);
++      path->mnt = nd->path.mnt;
++      if (!path->dentry) {
+               struct dentry *dentry;
+               /* Don't create child dentry for a dead directory. */
+-              result = ERR_PTR(-ENOENT);
+-              if (IS_DEADDIR(dir))
++              if (IS_DEADDIR(dir)) {
++                      res = -ENOENT;
+                       goto out_unlock;
++              }
+-              dentry = d_alloc(parent, name);
+-              result = ERR_PTR(-ENOMEM);
++              dentry = d_alloc(nd->path.dentry, name);
+               if (dentry) {
+-                      result = dir->i_op->lookup(dir, dentry, nd);
+-                      if (result)
++                      path->dentry = dir->i_op->lookup(dir, dentry, nd);
++                      if (path->dentry) {
+                               dput(dentry);
+-                      else
+-                              result = dentry;
++                              if (IS_ERR(path->dentry)) {
++                                      res = PTR_ERR(path->dentry);
++                                      path->dentry = NULL;
++                                      path->mnt = NULL;
++                              }
++                      } else
++                              path->dentry = dentry;
++              } else {
++                      res = -ENOMEM;
++                      path->mnt = NULL;
+               }
+ out_unlock:
+               mutex_unlock(&dir->i_mutex);
+-              return result;
++              return res;
+       }
+       /*
+@@ -515,12 +736,170 @@
+        * we waited on the semaphore. Need to revalidate.
+        */
+       mutex_unlock(&dir->i_mutex);
+-      if (result->d_op && result->d_op->d_revalidate) {
+-              result = do_revalidate(result, nd);
+-              if (!result)
+-                      result = ERR_PTR(-ENOENT);
++      if (path->dentry->d_op && path->dentry->d_op->d_revalidate) {
++              path->dentry = do_revalidate(path->dentry, nd);
++              if (!path->dentry) {
++                      res = -ENOENT;
++                      path->mnt = NULL;
++              }
++              if (IS_ERR(path->dentry)) {
++                      res = PTR_ERR(path->dentry);
++                      path->dentry = NULL;
++                      path->mnt = NULL;
++              }
+       }
+-      return result;
++
++      return res;
++}
++
++/**
++ * __real_lookup_topmost - lookup topmost dentry, non-cached version
++ *
++ * If we reach a dentry with restricted access, we just stop the lookup
++ * because we shouldn't see through that dentry. Same thing for dentry
++ * type mismatch and whiteouts.
++ *
++ * FIXME:
++ * - handle union stacks in use
++ * - handle union stacks mounted upon union stacks
++ * - avoid unnecessary allocations of union locks
++ */
++static int __real_lookup_topmost(struct nameidata *nd, struct qstr *name,
++                               struct path *path)
++{
++      struct path next;
++      int err;
++
++      err = real_lookup(nd, name, path);
++      if (err)
++              return err;
++
++      if (path->dentry->d_inode || d_is_whiteout(path->dentry))
++              return 0;
++
++      if (IS_OPAQUE(nd->path.dentry->d_inode) && !d_is_fallthru(path->dentry))
++              return 0;
++
++      while (follow_union_down(&nd->path)) {
++              name->hash = full_name_hash(name->name, name->len);
++              if (nd->path.dentry->d_op && nd->path.dentry->d_op->d_hash) {
++                      err = nd->path.dentry->d_op->d_hash(nd->path.dentry,
++                                                          name);
++                      if (err < 0)
++                              goto out;
++              }
++
++              err = real_lookup(nd, name, &next);
++              if (err)
++                      goto out;
++
++              if (next.dentry->d_inode || d_is_whiteout(next.dentry)) {
++                      dput(path->dentry);
++                      mntget(next.mnt);
++                      *path = next;
++                      goto out;
++              }
++
++              if (IS_OPAQUE(nd->path.dentry->d_inode) && !d_is_fallthru(next.dentry))
++                      goto out;
++
++              dput(next.dentry);
++      }
++out:
++      if (err)
++              dput(path->dentry);
++      return err;
++}
++
++/**
++ * __real_lookup_build_union: build the union stack for this pathname
++ * part, non-cached version
++ *
++ * Called when not all parts of the union stack are in cache
++ */
++
++static int __real_lookup_build_union(struct nameidata *nd, struct qstr *name,
++                                   struct path *path)
++{
++      struct path last = *path;
++      struct path next;
++      int err = 0;
++
++      while (follow_union_down(&nd->path)) {
++              /* We need to recompute the hash for lower layer lookups */
++              name->hash = full_name_hash(name->name, name->len);
++              if (nd->path.dentry->d_op && nd->path.dentry->d_op->d_hash) {
++                      err = nd->path.dentry->d_op->d_hash(nd->path.dentry,
++                                                          name);
++                      if (err < 0)
++                              goto out;
++              }
++
++              err = real_lookup(nd, name, &next);
++              if (err)
++                      goto out;
++
++              if (d_is_whiteout(next.dentry)) {
++                      dput(next.dentry);
++                      break;
++              }
++
++              if (!next.dentry->d_inode) {
++                      dput(next.dentry);
++                      continue;
++              }
++
++              /* only directories can be part of a union stack */
++              if (!S_ISDIR(next.dentry->d_inode->i_mode)) {
++                      dput(next.dentry);
++                      break;
++              }
++
++              /* now we know we found something "real" */
++              append_to_union(last.mnt, last.dentry, next.mnt, next.dentry);
++
++              if (last.dentry != path->dentry)
++                      path_put(&last);
++              last.dentry = next.dentry;
++              last.mnt = mntget(next.mnt);
++      }
++
++      if (last.dentry != path->dentry)
++              path_put(&last);
++out:
++      return err;
++}
++
++static int real_lookup_union(struct nameidata *nd, struct qstr *name,
++                           struct path *path)
++{
++      struct path safe = { .dentry = nd->path.dentry, .mnt = nd->path.mnt };
++      int res ;
++
++      path_get(&safe);
++      res = __real_lookup_topmost(nd, name, path);
++      if (res)
++              goto out;
++
++      /* only directories can be part of a union stack */
++      if (!path->dentry->d_inode ||
++          !S_ISDIR(path->dentry->d_inode->i_mode))
++              goto out;
++
++      /* Build the union stack for this part */
++      res = __real_lookup_build_union(nd, name, path);
++      if (res) {
++              dput(path->dentry);
++              if (path->mnt != safe.mnt)
++                      mntput(path->mnt);
++              goto out;
++      }
++
++out:
++      path_put(&nd->path);
++      nd->path.dentry = safe.dentry;
++      nd->path.mnt = safe.mnt;
++      return res;
+ }
+ /*
+@@ -623,11 +1002,8 @@
+       touch_atime(path->mnt, dentry);
+       nd_set_link(nd, NULL);
+-      if (path->mnt != nd->path.mnt) {
+-              path_to_nameidata(path, nd);
+-              dget(dentry);
+-      }
+-      mntget(path->mnt);
++      if (path->mnt == nd->path.mnt)
++              mntget(nd->path.mnt);
+       cookie = dentry->d_inode->i_op->follow_link(dentry, nd);
+       error = PTR_ERR(cookie);
+       if (!IS_ERR(cookie)) {
+@@ -715,7 +1091,7 @@
+       return res;
+ }
+-static void follow_mount(struct path *path)
++void follow_mount(struct path *path)
+ {
+       while (d_mountpoint(path->dentry)) {
+               struct vfsmount *mounted = lookup_mnt(path);
+@@ -780,6 +1156,7 @@
+               nd->path.mnt = parent;
+       }
+       follow_mount(&nd->path);
++      follow_union_mount(&nd->path);
+ }
+ /*
+@@ -790,35 +1167,55 @@
+ static int do_lookup(struct nameidata *nd, struct qstr *name,
+                    struct path *path)
+ {
+-      struct vfsmount *mnt = nd->path.mnt;
+-      struct dentry *dentry = __d_lookup(nd->path.dentry, name);
++      int err;
++
++      if (IS_MNT_UNION(nd->path.mnt))
++              goto need_union_lookup;
+-      if (!dentry)
++      path->dentry = __d_lookup(nd->path.dentry, name);
++      path->mnt = nd->path.mnt;
++      if (!path->dentry)
+               goto need_lookup;
+-      if (dentry->d_op && dentry->d_op->d_revalidate)
++      if (path->dentry->d_op && path->dentry->d_op->d_revalidate)
+               goto need_revalidate;
++
+ done:
+-      path->mnt = mnt;
+-      path->dentry = dentry;
+-      __follow_mount(path);
++      if (nd->path.mnt != path->mnt) {
++              nd->um_flags |= LAST_LOWLEVEL;
++              follow_mount(path);
++      } else
++              __follow_mount(path);
++      follow_union_mount(path);
+       return 0;
+ need_lookup:
+-      dentry = real_lookup(nd->path.dentry, name, nd);
+-      if (IS_ERR(dentry))
++      err = real_lookup(nd, name, path);
++      if (err)
++              goto fail;
++      goto done;
++
++need_union_lookup:
++      err = cache_lookup_union(nd, name, path);
++      if (!err && path->dentry)
++              goto done;
++
++      err = real_lookup_union(nd, name, path);
++      if (err)
+               goto fail;
+       goto done;
+ need_revalidate:
+-      dentry = do_revalidate(dentry, nd);
+-      if (!dentry)
++      path->dentry = do_revalidate(path->dentry, nd);
++      if (!path->dentry)
+               goto need_lookup;
+-      if (IS_ERR(dentry))
++      if (IS_ERR(path->dentry)) {
++              err = PTR_ERR(path->dentry);
+               goto fail;
++      }
+       goto done;
+ fail:
+-      return PTR_ERR(dentry);
++      return err;
+ }
+ /*
+@@ -845,6 +1242,8 @@
+       if (nd->depth)
+               lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE);
++      follow_union_mount(&nd->path);
++
+       /* At this point we know we have a real path component. */
+       for(;;) {
+               unsigned long hash;
+@@ -913,6 +1312,44 @@
+               if (err)
+                       break;
++              /*
++               * We want to create this element on the top level
++               * file system in two cases:
++               *
++               * - We are specifically told to - LOOKUP_TOPMOST.
++               * - This is a directory, and it does not yet exist on
++               *   the top level.  Various tricks only work if
++               *   directories always exist on the top level.
++               *
++               * In either case, only create this element on the top
++               * level if the last element is located on the lower
++               * level.  If the last element is located on the top
++               * level, then every single element in the path
++               * already exists on the top level.
++               *
++               * Note that we can assume that the parent is on the
++               * top level since we always create the directory on
++               * the top level.
++               */
++
++              if ((nd->um_flags & LAST_LOWLEVEL) &&
++                  ((next.dentry->d_inode &&
++                    S_ISDIR(next.dentry->d_inode->i_mode) &&
++                    (nd->path.mnt != next.mnt)) ||
++                   (nd->flags & LOOKUP_TOPMOST))) {
++                      struct dentry *dentry;
++
++                      dentry = union_create_topmost(nd, &this, &next);
++                      if (IS_ERR(dentry)) {
++                              err = PTR_ERR(dentry);
++                              goto out_dput;
++                      }
++                      path_put_conditional(&next, nd);
++                      next.mnt = nd->path.mnt;
++                      next.dentry = dentry;
++                      nd->um_flags &= ~LAST_LOWLEVEL;
++              }
++
+               err = -ENOENT;
+               inode = next.dentry->d_inode;
+               if (!inode)
+@@ -962,6 +1399,25 @@
+               err = do_lookup(nd, &this, &next);
+               if (err)
+                       break;
++
++              if ((nd->um_flags & LAST_LOWLEVEL) &&
++                  ((next.dentry->d_inode &&
++                    S_ISDIR(next.dentry->d_inode->i_mode) &&
++                    (nd->path.mnt != next.mnt)) ||
++                   (nd->flags & LOOKUP_TOPMOST))) {
++                      struct dentry *dentry;
++
++                      dentry = union_create_topmost(nd, &this, &next);
++                      if (IS_ERR(dentry)) {
++                              err = PTR_ERR(dentry);
++                              goto out_dput;
++                      }
++                      path_put_conditional(&next, nd);
++                      next.mnt = nd->path.mnt;
++                      next.dentry = dentry;
++                      nd->um_flags &= ~LAST_LOWLEVEL;
++              }
++
+               inode = next.dentry->d_inode;
+               if ((lookup_flags & LOOKUP_FOLLOW)
+                   && inode && inode->i_op->follow_link) {
+@@ -1029,6 +1485,7 @@
+       nd->last_type = LAST_ROOT; /* if there are only slashes... */
+       nd->flags = flags;
++      nd->um_flags = 0;
+       nd->depth = 0;
+       nd->root.mnt = NULL;
+@@ -1172,61 +1629,437 @@
+ }
+ static struct dentry *__lookup_hash(struct qstr *name,
+-              struct dentry *base, struct nameidata *nd)
++                                  struct dentry *base, struct nameidata *nd)
++{
++      struct dentry *dentry;
++      struct inode *inode;
++      int err;
++
++      inode = base->d_inode;
++
++      /*
++       * See if the low-level filesystem might want
++       * to use its own hash..
++       */
++      if (base->d_op && base->d_op->d_hash) {
++              err = base->d_op->d_hash(base, name);
++              dentry = ERR_PTR(err);
++              if (err < 0)
++                      goto out;
++      }
++
++      dentry = cache_lookup(base, name, nd);
++      if (!dentry) {
++              struct dentry *new;
++
++              /* Don't create child dentry for a dead directory. */
++              dentry = ERR_PTR(-ENOENT);
++              if (IS_DEADDIR(inode))
++                      goto out;
++
++              new = d_alloc(base, name);
++              dentry = ERR_PTR(-ENOMEM);
++              if (!new)
++                      goto out;
++              dentry = inode->i_op->lookup(inode, new, nd);
++              if (!dentry)
++                      dentry = new;
++              else
++                      dput(new);
++      }
++out:
++      return dentry;
++}
++
++/*
++ * Restricted form of lookup. Doesn't follow links, single-component only,
++ * needs parent already locked. Doesn't follow mounts.
++ * SMP-safe.
++ */
++static int lookup_hash(struct nameidata *nd, struct qstr *name,
++                     struct path *path)
++{
++      int err;
++
++      err = inode_permission(nd->path.dentry->d_inode, MAY_EXEC);
++      if (err)
++              return err;
++      path->mnt = nd->path.mnt;
++      path->dentry =  __lookup_hash(name, nd->path.dentry, nd);
++      if (IS_ERR(path->dentry)) {
++              err = PTR_ERR(path->dentry);
++              path->dentry = NULL;
++              path->mnt = NULL;
++      }
++      return err;
++}
++
++static int __hash_lookup_topmost(struct nameidata *nd, struct qstr *name,
++                               struct path *path)
++{
++      struct path next;
++      int err;
++
++      err = lookup_hash(nd, name, path);
++      if (err)
++              return err;
++
++      if (path->dentry->d_inode || d_is_whiteout(path->dentry))
++              return 0;
++
++      if (IS_OPAQUE(nd->path.dentry->d_inode) && !d_is_fallthru(path->dentry))
++              return 0;
++
++      while (follow_union_down(&nd->path)) {
++              name->hash = full_name_hash(name->name, name->len);
++              if (nd->path.dentry->d_op && nd->path.dentry->d_op->d_hash) {
++                      err = nd->path.dentry->d_op->d_hash(nd->path.dentry,
++                                                          name);
++                      if (err < 0)
++                              goto out;
++              }
++
++              mutex_lock(&nd->path.dentry->d_inode->i_mutex);
++              err = lookup_hash(nd, name, &next);
++              mutex_unlock(&nd->path.dentry->d_inode->i_mutex);
++              if (err)
++                      goto out;
++
++              if (next.dentry->d_inode || d_is_whiteout(next.dentry)) {
++                      dput(path->dentry);
++                      mntget(next.mnt);
++                      *path = next;
++                      goto out;
++              }
++
++              if (IS_OPAQUE(nd->path.dentry->d_inode) && !d_is_fallthru(next.dentry))
++                      goto out;
++
++              dput(next.dentry);
++      }
++out:
++      if (err)
++              dput(path->dentry);
++      return err;
++}
++
++static int __hash_lookup_build_union(struct nameidata *nd, struct qstr *name,
++                                   struct path *path)
++{
++      struct path last = *path;
++      struct path next;
++      int err = 0;
++
++      while (follow_union_down(&nd->path)) {
++              /* We need to recompute the hash for lower layer lookups */
++              name->hash = full_name_hash(name->name, name->len);
++              if (nd->path.dentry->d_op && nd->path.dentry->d_op->d_hash) {
++                      err = nd->path.dentry->d_op->d_hash(nd->path.dentry,
++                                                          name);
++                      if (err < 0)
++                              goto out;
++              }
++
++              mutex_lock(&nd->path.dentry->d_inode->i_mutex);
++              err = lookup_hash(nd, name, &next);
++              mutex_unlock(&nd->path.dentry->d_inode->i_mutex);
++              if (err)
++                      goto out;
++
++              if (d_is_whiteout(next.dentry)) {
++                      dput(next.dentry);
++                      break;
++              }
++
++              if (!next.dentry->d_inode) {
++                      dput(next.dentry);
++                      continue;
++              }
++
++              /* only directories can be part of a union stack */
++              if (!S_ISDIR(next.dentry->d_inode->i_mode)) {
++                      dput(next.dentry);
++                      break;
++              }
++
++              /* now we know we found something "real" */
++              append_to_union(last.mnt, last.dentry, next.mnt, next.dentry);
++
++              if (last.dentry != path->dentry)
++                      path_put(&last);
++              last.dentry = next.dentry;
++              last.mnt = mntget(next.mnt);
++      }
++
++      if (last.dentry != path->dentry)
++              path_put(&last);
++out:
++      return err;
++}
++
++int hash_lookup_union(struct nameidata *nd, struct qstr *name,
++                           struct path *path)
++{
++      struct path safe = { .dentry = nd->path.dentry, .mnt = nd->path.mnt };
++      int res ;
++
++      path_get(&safe);
++      res = __hash_lookup_topmost(nd, name, path);
++      if (res)
++              goto out;
++
++      /* only directories can be part of a union stack */
++      if (!path->dentry->d_inode ||
++          !S_ISDIR(path->dentry->d_inode->i_mode))
++              goto out;
++
++      /* Build the union stack for this part */
++      res = __hash_lookup_build_union(nd, name, path);
++      if (res) {
++              dput(path->dentry);
++              if (path->mnt != safe.mnt)
++                      mntput(path->mnt);
++              goto out;
++      }
++
++out:
++      path_put(&nd->path);
++      nd->path.dentry = safe.dentry;
++      nd->path.mnt = safe.mnt;
++      return res;
++}
++
++/**
++ * do_union_hash_lookup() - walk down the union stack and lookup_hash()
++ * @nd: nameidata of parent to lookup from
++ * @name: pathname component to lookup
++ * @path: path to store result of lookup in
++ *
++ * Walk down the union stack and search for single pathname component name. It
++ * is assumed that the caller already did a lookup_hash() in the topmost parent
++ * that gave negative lookup result. Therefore this does call lookup_hash() in
++ * every lower layer (!) of the union stack. If a directory is found the union
++ * stack for that is assembled as well.
++ *
++ * Note:
++ * The caller needs to take care of holding a valid reference to the topmost
++ * parent.
++ * On error we leave @path untouched as well as when we don't find anything.
++ */
++static int do_union_hash_lookup(struct nameidata *nd, struct qstr *name,
++                              struct path *path)
++{
++      struct path next;
++      int err = 0;
++
++      while (follow_union_down(&nd->path)) {
++              /* rehash because of d_op->d_hash() by the previous layer */
++              name->hash = full_name_hash(name->name, name->len);
++
++              mutex_lock(&nd->path.dentry->d_inode->i_mutex);
++              err = lookup_hash(nd, name, &next);
++              mutex_unlock(&nd->path.dentry->d_inode->i_mutex);
++
++              if (err)
++                      break;
++
++              if (next.dentry->d_inode) {
++                      mntget(next.mnt);
++                      if (!S_ISDIR(next.dentry->d_inode->i_mode)) {
++                              *path = next;
++                              break;
++                      }
++                      err = __hash_lookup_build_union(nd, name, &next);
++                      if (err)
++                              path_put(&next);
++                      else
++                              *path = next;
++                      break;
++              }
++
++              path_put_conditional(&next, nd);
++
++              if ((IS_OPAQUE(nd->path.dentry->d_inode) &&
++                   !d_is_fallthru(next.dentry)) ||
++                  d_is_whiteout(next.dentry))
++                      break;
++      }
++
++      return err;
++}
++
++/**
++ * _hash_lookup_union() - lookup single pathname component
++ * @nd: nameidata of parent to lookup from
++ * @name: pathname component to lookup
++ * @path: path to store result of lookup in
++ *
++ * Returns the topmost parent locked and the target dentry found in the union
++ * or the topmost negative target dentry otherwise.
++ *
++ * Note:
++ * Returns topmost parent locked even on error.
++ */
++static int _hash_lookup_union(struct nameidata *nd, struct qstr *name,
++                            struct path *path)
++{
++      struct path parent = nd->path;
++      struct path topmost;
++      int err;
++
++      mutex_lock(&nd->path.dentry->d_inode->i_mutex);
++      err = lookup_hash(nd, name, path);
++      if (err)
++              return err;
++
++      /* return if we found something and it isn't a directory we are done */
++      if (path->dentry->d_inode && !S_ISDIR(path->dentry->d_inode->i_mode))
++              return 0;
++
++      /* stop lookup if the parent directory is marked opaque */
++      if ((IS_OPAQUE(nd->path.dentry->d_inode) &&
++           !d_is_fallthru(path->dentry)) ||
++          d_is_whiteout(path->dentry))
++              return 0;
++
++      if (!strcmp(path->mnt->mnt_sb->s_type->name, "proc") ||
++          !strcmp(path->mnt->mnt_sb->s_type->name, "sysfs"))
++              return 0;
++
++      mutex_unlock(&nd->path.dentry->d_inode->i_mutex);
++
++      /*
++       * safe a reference to the topmost parent for walking the union stack
++       */
++      path_get(&parent);
++      topmost = *path;
++
++      if (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode)) {
++              err = __hash_lookup_build_union(nd, name, path);
++              if (err)
++                      goto err_lock_parent;
++              goto out_lock_and_revalidate_parent;
++      }
++
++      err = do_union_hash_lookup(nd, name, path);
++      if (err)
++              goto err_lock_parent;
++
++out_lock_and_revalidate_parent:
++      /* seems that we haven't found anything, so return the topmost */
++      path_to_nameidata(&parent, nd);
++      mutex_lock(&nd->path.dentry->d_inode->i_mutex);
++
++      if (topmost.dentry == path->dentry) {
++              spin_lock(&path->dentry->d_lock);
++              if (nd->path.dentry != path->dentry->d_parent) {
++                      spin_unlock(&path->dentry->d_lock);
++                      dput(path->dentry);
++                      name->hash = full_name_hash(name->name, name->len);
++                      err = lookup_hash(nd, name, path);
++                      if (err)
++                              return err;
++                      /* FIXME: What if we find a directory here ... */
++                      return err;
++              }
++              spin_unlock(&path->dentry->d_lock);
++      } else
++              dput(topmost.dentry);
++
++      return 0;
++
++err_lock_parent:
++      path_to_nameidata(&parent, nd);
++      path_put_conditional(path, nd);
++      mutex_lock(&nd->path.dentry->d_inode->i_mutex);
++      return err;
++}
++
++/**
++ * lookup_rename_source() - lookup the source used by rename
++ *
++ * This is a special version of _hash_lookup_union() which becomes necessary
++ * for finding the source of a rename on union mounts.
++ *
++ * See comment for _hash_lookup_union() above.
++ */
++static int lookup_rename_source(struct nameidata *oldnd,
++                              struct nameidata *newnd,
++                              struct dentry **trap, struct qstr *name,
++                              struct path *old)
+ {
+-      struct dentry *dentry;
+-      struct inode *inode;
++      struct path parent = oldnd->path;
++      struct path topmost;
+       int err;
+-      inode = base->d_inode;
++      err = lookup_hash(oldnd, name, old);
++      if (err)
++              return err;
++
++      /* return if we found something and it isn't a directory we are done */
++      if (old->dentry->d_inode && !S_ISDIR(old->dentry->d_inode->i_mode))
++              return 0;
++
++      /* stop lookup if the parent directory is marked opaque */
++      if ((IS_OPAQUE(oldnd->path.dentry->d_inode) &&
++           !d_is_fallthru(old->dentry)) ||
++          d_is_whiteout(old->dentry))
++              return 0;
++
++      if (!strcmp(old->mnt->mnt_sb->s_type->name, "proc") ||
++          !strcmp(old->mnt->mnt_sb->s_type->name, "sysfs"))
++              return 0;
++
++      unlock_rename(oldnd->path.dentry, newnd->path.dentry);
+       /*
+-       * See if the low-level filesystem might want
+-       * to use its own hash..
++       * safe a reference to the topmost parent for walking the union stack
+        */
+-      if (base->d_op && base->d_op->d_hash) {
+-              err = base->d_op->d_hash(base, name);
+-              dentry = ERR_PTR(err);
+-              if (err < 0)
+-                      goto out;
++      path_get(&parent);
++      topmost = *old;
++
++      if (old->dentry->d_inode && S_ISDIR(old->dentry->d_inode->i_mode)) {
++              err = __hash_lookup_build_union(oldnd, name, old);
++              if (err)
++                      goto err_lock;
++              goto out_lock_and_revalidate_parent;
+       }
+-      dentry = cached_lookup(base, name, nd);
+-      if (!dentry) {
+-              struct dentry *new;
++      err = do_union_hash_lookup(oldnd, name, old);
++      if (err)
++              goto err_lock;
+-              /* Don't create child dentry for a dead directory. */
+-              dentry = ERR_PTR(-ENOENT);
+-              if (IS_DEADDIR(inode))
+-                      goto out;
++out_lock_and_revalidate_parent:
++      path_to_nameidata(&parent, oldnd);
++      *trap = lock_rename(oldnd->path.dentry, newnd->path.dentry);
+-              new = d_alloc(base, name);
+-              dentry = ERR_PTR(-ENOMEM);
+-              if (!new)
+-                      goto out;
+-              dentry = inode->i_op->lookup(inode, new, nd);
+-              if (!dentry)
+-                      dentry = new;
+-              else
+-                      dput(new);
+-      }
+-out:
+-      return dentry;
+-}
++      /*
++       * If we return the topmost dentry we have to make sure that it has not
++       * been moved away while we gave up the topmost parents i_mutex lock.
++       */
++      if (topmost.dentry == old->dentry) {
++              spin_lock(&old->dentry->d_lock);
++              if (oldnd->path.dentry != old->dentry->d_parent) {
++                      spin_unlock(&old->dentry->d_lock);
++                      dput(old->dentry);
++                      name->hash = full_name_hash(name->name, name->len);
++                      err = lookup_hash(oldnd, name, old);
++                      if (err)
++                              return err;
++                      /* FIXME: What if we find a directory here ... */
++                      return err;
++              }
++              spin_unlock(&old->dentry->d_lock);
++      } else
++              dput(topmost.dentry);
+-/*
+- * Restricted form of lookup. Doesn't follow links, single-component only,
+- * needs parent already locked. Doesn't follow mounts.
+- * SMP-safe.
+- */
+-static struct dentry *lookup_hash(struct nameidata *nd)
+-{
+-      int err;
++      return 0;
+-      err = inode_permission(nd->path.dentry->d_inode, MAY_EXEC);
+-      if (err)
+-              return ERR_PTR(err);
+-      return __lookup_hash(&nd->last, nd->path.dentry, nd);
++err_lock:
++      path_to_nameidata(&parent, oldnd);
++      path_put_conditional(old, oldnd);
++      *trap = lock_rename(oldnd->path.dentry, newnd->path.dentry);
++      return err;
+ }
+ static int __lookup_one_len(const char *name, struct qstr *this,
+@@ -1502,8 +2335,9 @@
+       return error;
+ }
+-int may_open(struct path *path, int acc_mode, int flag)
++int may_open(struct nameidata *nd, int acc_mode, int flag)
+ {
++      struct path *path = &nd->path;
+       struct dentry *dentry = path->dentry;
+       struct inode *inode = dentry->d_inode;
+       int error;
+@@ -1529,7 +2363,7 @@
+               break;
+       }
+-      error = inode_permission(inode, acc_mode);
++      error = union_permission(path, acc_mode);
+       if (error)
+               return error;
+@@ -1575,6 +2409,9 @@
+               if (!error)
+                       error = security_path_truncate(path, 0,
+                                              ATTR_MTIME|ATTR_CTIME|ATTR_OPEN);
++              /* XXX don't copy up file data */
++              if (is_unionized(path->dentry, path->mnt))
++                      error = union_copyup(nd, flag /* XXX not used */);
+               if (!error) {
+                       vfs_dq_init(inode);
+@@ -1621,7 +2458,7 @@
+       if (error)
+               return error;
+       /* Don't check for write permission, don't truncate */
+-      return may_open(&nd->path, 0, flag & ~O_TRUNC);
++      return may_open(nd, 0, flag & ~O_TRUNC);
+ }
+ /*
+@@ -1736,12 +2573,10 @@
+       if (flag & O_EXCL)
+               nd.flags |= LOOKUP_EXCL;
+       mutex_lock(&dir->d_inode->i_mutex);
+-      path.dentry = lookup_hash(&nd);
+-      path.mnt = nd.path.mnt;
++      error = hash_lookup_union(&nd, &nd.last, &path);
+ do_last:
+-      error = PTR_ERR(path.dentry);
+-      if (IS_ERR(path.dentry)) {
++      if (error) {
+               mutex_unlock(&dir->d_inode->i_mutex);
+               goto exit;
+       }
+@@ -1801,10 +2636,23 @@
+       if (path.dentry->d_inode->i_op->follow_link)
+               goto do_link;
+-      path_to_nameidata(&path, &nd);
+       error = -EISDIR;
+       if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))
+-              goto exit;
++              goto exit_dput;
++
++      /*
++       * If this file is on a lower layer of the union stack, copy it to the
++       * topmost layer before opening it
++       */
++      if (path.dentry->d_inode &&
++          (path.dentry->d_parent != dir) &&
++          S_ISREG(path.dentry->d_inode->i_mode)) {
++              error = __union_copyup(&path, &nd, &path);
++              if (error)
++                      goto exit_dput;
++      }
++
++      path_to_nameidata(&path, &nd);
+ ok:
+       /*
+        * Consider:
+@@ -1822,12 +2670,18 @@
+               if (error)
+                       goto exit;
+       }
+-      error = may_open(&nd.path, acc_mode, flag);
++      error = may_open(&nd, acc_mode, flag);
+       if (error) {
+               if (will_write)
+                       mnt_drop_write(nd.path.mnt);
+               goto exit;
+       }
++      /* Okay, all permissions go, now copy up */
++      if (!(flag & O_CREAT) && (flag & FMODE_WRITE)) {
++              error = union_copyup(&nd, flag /* XXX not used */);
++              if (error)
++                      goto exit;
++      }
+       filp = nameidata_to_filp(&nd, open_flag);
+       if (IS_ERR(filp))
+               ima_counts_put(&nd.path,
+@@ -1902,8 +2756,7 @@
+       }
+       dir = nd.path.dentry;
+       mutex_lock(&dir->d_inode->i_mutex);
+-      path.dentry = lookup_hash(&nd);
+-      path.mnt = nd.path.mnt;
++      error = hash_lookup_union(&nd, &nd.last, &path);
+       __putname(nd.last.name);
+       goto do_last;
+ }
+@@ -1937,7 +2790,8 @@
+  */
+ struct dentry *lookup_create(struct nameidata *nd, int is_dir)
+ {
+-      struct dentry *dentry = ERR_PTR(-EEXIST);
++      struct path path = { .dentry = ERR_PTR(-EEXIST) } ;
++      int err;
+       mutex_lock_nested(&nd->path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
+       /*
+@@ -1953,11 +2807,13 @@
+       /*
+        * Do the final lookup.
+        */
+-      dentry = lookup_hash(nd);
+-      if (IS_ERR(dentry))
++      err = hash_lookup_union(nd, &nd->last, &path);
++      if (err) {
++              path.dentry = ERR_PTR(err);
+               goto fail;
++      }
+-      if (dentry->d_inode)
++      if (path.dentry->d_inode)
+               goto eexist;
+       /*
+        * Special case - lookup gave negative, but... we had foo/bar/
+@@ -1966,15 +2822,17 @@
+        * been asking for (non-existent) directory. -ENOENT for you.
+        */
+       if (unlikely(!is_dir && nd->last.name[nd->last.len])) {
+-              dput(dentry);
+-              dentry = ERR_PTR(-ENOENT);
++              path_put_conditional(&path, nd);
++              path.dentry = ERR_PTR(-ENOENT);
+       }
+-      return dentry;
++      if (nd->path.mnt != path.mnt)
++              mntput(path.mnt);
++      return path.dentry;
+ eexist:
+-      dput(dentry);
+-      dentry = ERR_PTR(-EEXIST);
++      path_put_conditional(&path, nd);
++      path.dentry = ERR_PTR(-EEXIST);
+ fail:
+-      return dentry;
++      return path.dentry;
+ }
+ EXPORT_SYMBOL_GPL(lookup_create);
+@@ -2086,6 +2944,7 @@
+ int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+ {
+       int error = may_create(dir, dentry);
++      int opaque = 0;
+       if (error)
+               return error;
+@@ -2099,9 +2958,18 @@
+               return error;
+       vfs_dq_init(dir);
++
++      if (d_is_whiteout(dentry))
++              opaque = 1;
++
+       error = dir->i_op->mkdir(dir, dentry, mode);
+-      if (!error)
++      if (!error) {
+               fsnotify_mkdir(dir, dentry);
++              if (opaque) {
++                      dentry->d_inode->i_flags |= S_OPAQUE;
++                      mark_inode_dirty(dentry->d_inode);
++              }
++      }
+       return error;
+ }
+@@ -2147,6 +3015,212 @@
+       return sys_mkdirat(AT_FDCWD, pathname, mode);
+ }
++
++/* Checks on the victim for whiteout */
++static inline int may_whiteout(struct inode *dir, struct dentry *victim,
++                             int isdir)
++{
++      int err;
++
++      /* from may_create() */
++      if (IS_DEADDIR(dir))
++              return -ENOENT;
++      err = inode_permission(dir, MAY_WRITE | MAY_EXEC);
++      if (err)
++              return err;
++
++      /* from may_delete() */
++      if (IS_APPEND(dir))
++              return -EPERM;
++      if (!victim->d_inode)
++              return 0;
++      if (check_sticky(dir, victim->d_inode) ||
++          IS_APPEND(victim->d_inode) ||
++          IS_IMMUTABLE(victim->d_inode))
++              return -EPERM;
++      if (isdir) {
++              if (!S_ISDIR(victim->d_inode->i_mode))
++                      return -ENOTDIR;
++              if (IS_ROOT(victim))
++                      return -EBUSY;
++      } else if (S_ISDIR(victim->d_inode->i_mode))
++              return -EISDIR;
++      if (victim->d_flags & DCACHE_NFSFS_RENAMED)
++              return -EBUSY;
++      return 0;
++}
++
++/**
++ * vfs_whiteout: creates a white-out for the given directory entry
++ * @dir: parent inode
++ * @dentry: directory entry to white-out
++ *
++ * Simply white-out a given directory entry. This functionality is usually used
++ * in the sense of unlink. Therefore the given dentry can still be in-use and
++ * contains an in-use inode. The filesystem has to do what unlink or rmdir
++ * would in that case. Since the dentry still might be in-use we have to
++ * provide a fresh unhashed dentry that whiteout can fill the new inode into.
++ * In that case the given dentry is dropped and the fresh dentry containing the
++ * whiteout is rehashed instead. If the given dentry is unused, the whiteout
++ * inode is instantiated into it instead.
++ *
++ * After this returns with success, don't make any assumptions about the inode.
++ * Just dput() it dentry.
++ */
++static int vfs_whiteout(struct inode *dir, struct dentry *dentry, int isdir)
++{
++      int err;
++      struct inode *old_inode = dentry->d_inode;
++      struct dentry *parent, *whiteout;
++
++      err = may_whiteout(dir, dentry, isdir);
++      if (err)
++              return err;
++
++      BUG_ON(dentry->d_parent->d_inode != dir);
++
++      if (!dir->i_op || !dir->i_op->whiteout)
++              return -EOPNOTSUPP;
++
++      if (old_inode) {
++              vfs_dq_init(dir);
++
++              mutex_lock(&old_inode->i_mutex);
++              if (isdir)
++                      dentry_unhash(dentry);
++              if (d_mountpoint(dentry))
++                      err = -EBUSY;
++              else {
++                      if (isdir)
++                              err = security_inode_rmdir(dir, dentry);
++                      else
++                              err = security_inode_unlink(dir, dentry);
++              }
++      }
++
++      parent = dget_parent(dentry);
++      whiteout = d_alloc_name(parent, dentry->d_name.name);
++
++      if (!err)
++              err = dir->i_op->whiteout(dir, dentry, whiteout);
++
++      if (old_inode) {
++              mutex_unlock(&old_inode->i_mutex);
++              if (!err) {
++                      fsnotify_link_count(old_inode);
++                      d_delete(dentry);
++              }
++              if (isdir)
++                      dput(dentry);
++      }
++
++      dput(whiteout);
++      dput(parent);
++      return err;
++}
++
++int path_whiteout(struct path *dir_path, struct dentry *dentry, int isdir)
++{
++      int error = mnt_want_write(dir_path->mnt);
++
++      if (!error) {
++              error = vfs_whiteout(dir_path->dentry->d_inode, dentry, isdir);
++              mnt_drop_write(dir_path->mnt);
++      }
++
++      return error;
++}
++EXPORT_SYMBOL(path_whiteout);
++
++/*
++ * This is abusing readdir to check if a union directory is logically empty.
++ * Al Viro barfed when he saw this, but Val said: "Well, at this point I'm
++ * aiming for working, pretty can come later"
++ */
++static int filldir_is_empty(void *__buf, const char *name, int namlen,
++                          loff_t offset, u64 ino, unsigned int d_type)
++{
++      int *is_empty = (int *)__buf;
++
++      switch (namlen) {
++      case 2:
++              if (name[1] != '.')
++                      break;
++      case 1:
++              if (name[0] != '.')
++                      break;
++              return 0;
++      }
++
++      if (d_type == DT_WHT)
++              return 0;
++
++      (*is_empty) = 0;
++      return 0;
++}
++
++static int directory_is_empty(struct dentry *dentry, struct vfsmount *mnt)
++{
++      struct file *file;
++      int err;
++      int is_empty = 1;
++
++      BUG_ON(!S_ISDIR(dentry->d_inode->i_mode));
++
++      /* references for the file pointer */
++      dget(dentry);
++      mntget(mnt);
++
++      file = dentry_open(dentry, mnt, O_RDONLY, current_cred());
++      if (IS_ERR(file))
++              return 0;
++
++      err = vfs_readdir(file, filldir_is_empty, &is_empty);
++
++      fput(file);
++      return is_empty;
++}
++
++static int do_whiteout(struct nameidata *nd, struct path *path, int isdir)
++{
++      struct path safe = { .dentry = dget(nd->path.dentry),
++                           .mnt = mntget(nd->path.mnt) };
++      struct dentry *dentry = path->dentry;
++      int err;
++
++      err = may_whiteout(nd->path.dentry->d_inode, dentry, isdir);
++      if (err)
++              goto out;
++
++      err = -ENOENT;
++      if (!dentry->d_inode)
++              goto out;
++
++      err = -ENOTEMPTY;
++      if (isdir && !directory_is_empty(path->dentry, path->mnt))
++              goto out;
++
++      if (nd->path.dentry != dentry->d_parent) {
++              dentry = __lookup_hash(&path->dentry->d_name, nd->path.dentry,
++                                     nd);
++              err = PTR_ERR(dentry);
++              if (IS_ERR(dentry))
++                      goto out;
++
++              dput(path->dentry);
++              if (path->mnt != safe.mnt)
++                      mntput(path->mnt);
++              path->mnt = nd->path.mnt;
++              path->dentry = dentry;
++      }
++
++      err = vfs_whiteout(nd->path.dentry->d_inode, dentry, isdir);
++
++out:
++      path_put(&safe);
++      return err;
++}
++
+ /*
+  * We try to drop the dentry early: we should have
+  * a usage count of 2 if we're the only user of this
+@@ -2211,7 +3285,7 @@
+ {
+       int error = 0;
+       char * name;
+-      struct dentry *dentry;
++      struct path path;
+       struct nameidata nd;
+       error = user_path_parent(dfd, pathname, &nd, &name);
+@@ -2233,21 +3307,24 @@
+       nd.flags &= ~LOOKUP_PARENT;
+       mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
+-      dentry = lookup_hash(&nd);
+-      error = PTR_ERR(dentry);
+-      if (IS_ERR(dentry))
++      error = hash_lookup_union(&nd, &nd.last, &path);
++      if (error)
+               goto exit2;
++      if (is_unionized(nd.path.dentry, nd.path.mnt)) {
++              error = do_whiteout(&nd, &path, 1);
++              goto exit3;
++      }
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
+               goto exit3;
+-      error = security_path_rmdir(&nd.path, dentry);
++      error = security_path_rmdir(&nd.path, path.dentry);
+       if (error)
+               goto exit4;
+-      error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
++      error = vfs_rmdir(nd.path.dentry->d_inode, path.dentry);
+ exit4:
+       mnt_drop_write(nd.path.mnt);
+ exit3:
+-      dput(dentry);
++      path_put_conditional(&path, &nd);
+ exit2:
+       mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+ exit1:
+@@ -2302,7 +3379,7 @@
+ {
+       int error;
+       char *name;
+-      struct dentry *dentry;
++      struct path path;
+       struct nameidata nd;
+       struct inode *inode = NULL;
+@@ -2317,26 +3394,29 @@
+       nd.flags &= ~LOOKUP_PARENT;
+       mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
+-      dentry = lookup_hash(&nd);
+-      error = PTR_ERR(dentry);
+-      if (!IS_ERR(dentry)) {
++      error = hash_lookup_union(&nd, &nd.last, &path);
++      if (!error) {
+               /* Why not before? Because we want correct error value */
+               if (nd.last.name[nd.last.len])
+                       goto slashes;
+-              inode = dentry->d_inode;
++              inode = path.dentry->d_inode;
+               if (inode)
+                       atomic_inc(&inode->i_count);
++              if (is_unionized(nd.path.dentry, nd.path.mnt)) {
++                      error = do_whiteout(&nd, &path, 0);
++                      goto exit2;
++              }
+               error = mnt_want_write(nd.path.mnt);
+               if (error)
+                       goto exit2;
+-              error = security_path_unlink(&nd.path, dentry);
++              error = security_path_unlink(&nd.path, path.dentry);
+               if (error)
+                       goto exit3;
+-              error = vfs_unlink(nd.path.dentry->d_inode, dentry);
++              error = vfs_unlink(nd.path.dentry->d_inode, path.dentry);
+ exit3:
+               mnt_drop_write(nd.path.mnt);
+       exit2:
+-              dput(dentry);
++              path_put_conditional(&path, &nd);
+       }
+       mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
+       if (inode)
+@@ -2347,8 +3427,8 @@
+       return error;
+ slashes:
+-      error = !dentry->d_inode ? -ENOENT :
+-              S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
++      error = !path.dentry->d_inode ? -ENOENT :
++              S_ISDIR(path.dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
+       goto exit2;
+ }
+@@ -2684,11 +3764,96 @@
+       return error;
+ }
++static int vfs_rename_union(struct nameidata *oldnd, struct path *old,
++                          struct nameidata *newnd, struct path *new)
++{
++      struct inode *old_dir = oldnd->path.dentry->d_inode;
++      struct inode *new_dir = newnd->path.dentry->d_inode;
++      struct qstr old_name;
++      char *name;
++      struct dentry *dentry;
++      int error;
++
++      if (old->dentry->d_inode == new->dentry->d_inode)
++              return 0;
++      error = may_whiteout(old_dir, old->dentry, 0);
++      if (error)
++              return error;
++      if (!old_dir->i_op || !old_dir->i_op->whiteout)
++              return -EPERM;
++
++      if (!new->dentry->d_inode)
++              error = may_create(new_dir, new->dentry);
++      else
++              error = may_delete(new_dir, new->dentry, 0);
++      if (error)
++              return error;
++
++      vfs_dq_init(old_dir);
++      vfs_dq_init(new_dir);
++
++      error = -EBUSY;
++      if (d_mountpoint(old->dentry) || d_mountpoint(new->dentry))
++              return error;
++
++      error = -ENOMEM;
++      name = kmalloc(old->dentry->d_name.len, GFP_KERNEL);
++      if (!name)
++              return error;
++      strncpy(name, old->dentry->d_name.name, old->dentry->d_name.len);
++      name[old->dentry->d_name.len] = 0;
++      old_name.len = old->dentry->d_name.len;
++      old_name.hash = old->dentry->d_name.hash;
++      old_name.name = name;
++
++      /* possibly delete the existing new file */
++      if ((newnd->path.dentry == new->dentry->d_parent) &&
++          new->dentry->d_inode) {
++              /* FIXME: inode may be truncated while we hold a lock */
++              error = vfs_unlink(new_dir, new->dentry);
++              if (error)
++                      goto freename;
++
++              dentry = __lookup_hash(&new->dentry->d_name,
++                                     newnd->path.dentry, newnd);
++              if (IS_ERR(dentry))
++                      goto freename;
++
++              dput(new->dentry);
++              new->dentry = dentry;
++      }
++
++      /* copyup to the new file */
++      error = __union_copyup(old, newnd, new);
++      if (error)
++              goto freename;
++
++      /* whiteout the old file */
++      dentry = __lookup_hash(&old_name, oldnd->path.dentry, oldnd);
++      error = PTR_ERR(dentry);
++      if (IS_ERR(dentry))
++              goto freename;
++      error = vfs_whiteout(old_dir, dentry, 0);
++      dput(dentry);
++
++      /* FIXME: This is acutally unlink() && create() ... */
++/*
++  if (!error) {
++  const char *new_name = old_dentry->d_name.name;
++  fsnotify_move(old_dir, new_dir, old_name.name, new_name, 0,
++  new_dentry->d_inode, old_dentry->d_inode);
++  }
++*/
++freename:
++      kfree(old_name.name);
++      return error;
++}
++
+ SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
+               int, newdfd, const char __user *, newname)
+ {
+       struct dentry *old_dir, *new_dir;
+-      struct dentry *old_dentry, *new_dentry;
++      struct path old, new;
+       struct dentry *trap;
+       struct nameidata oldnd, newnd;
+       char *from;
+@@ -2722,16 +3887,28 @@
+       trap = lock_rename(new_dir, old_dir);
+-      old_dentry = lookup_hash(&oldnd);
+-      error = PTR_ERR(old_dentry);
+-      if (IS_ERR(old_dentry))
++      /*
++       * For union mounts we need to call a giant lookup_rename_source()
++       * instead.
++       * First lock_rename() and look on the topmost fs like you would do in
++       * the normal rename, if you find something which is not a directory,
++       * go ahead and lookup target and do normal rename.
++       * If you find a negative dentry, unlock_rename() and continue as
++       * _hash_lookup_union() would do without locking the topmost parent
++       * at the end. After that do lock_rename() of the source parent and the
++       * target parent and do a copyup with additional whiteout creation at
++       * the end.
++       */
++//    error = hash_lookup_union(&oldnd, &oldnd.last, &old);
++      error = lookup_rename_source(&oldnd, &newnd, &trap, &oldnd.last, &old);
++      if (error)
+               goto exit3;
+       /* source must exist */
+       error = -ENOENT;
+-      if (!old_dentry->d_inode)
++      if (!old.dentry->d_inode)
+               goto exit4;
+       /* unless the source is a directory trailing slashes give -ENOTDIR */
+-      if (!S_ISDIR(old_dentry->d_inode->i_mode)) {
++      if (!S_ISDIR(old.dentry->d_inode->i_mode)) {
+               error = -ENOTDIR;
+               if (oldnd.last.name[oldnd.last.len])
+                       goto exit4;
+@@ -2740,32 +3917,44 @@
+       }
+       /* source should not be ancestor of target */
+       error = -EINVAL;
+-      if (old_dentry == trap)
++      if (old.dentry == trap)
+               goto exit4;
+-      new_dentry = lookup_hash(&newnd);
+-      error = PTR_ERR(new_dentry);
+-      if (IS_ERR(new_dentry))
++      /* target is always on topmost fs, even with unions */
++      error = lookup_hash(&newnd, &newnd.last, &new);
++      if (error)
+               goto exit4;
+       /* target should not be an ancestor of source */
+       error = -ENOTEMPTY;
+-      if (new_dentry == trap)
++      if (new.dentry == trap)
++              goto exit5;
++      /* renaming of directories on unions is done by the user-space */
++      error = -EXDEV;
++      if (is_unionized(oldnd.path.dentry, oldnd.path.mnt) &&
++          S_ISDIR(old.dentry->d_inode->i_mode))
+               goto exit5;
++//    if (is_unionized(newnd.path.dentry, newnd.path.mnt))
++//            goto exit5;
+       error = mnt_want_write(oldnd.path.mnt);
+       if (error)
+               goto exit5;
+-      error = security_path_rename(&oldnd.path, old_dentry,
+-                                   &newnd.path, new_dentry);
++      error = security_path_rename(&oldnd.path, old.dentry,
++                                   &newnd.path, new.dentry);
+       if (error)
+               goto exit6;
+-      error = vfs_rename(old_dir->d_inode, old_dentry,
+-                                 new_dir->d_inode, new_dentry);
++      if (is_unionized(oldnd.path.dentry, oldnd.path.mnt) &&
++          (old.dentry->d_parent != oldnd.path.dentry)) {
++              error = vfs_rename_union(&oldnd, &old, &newnd, &new);
++              goto exit6;
++      }
++      error = vfs_rename(old_dir->d_inode, old.dentry,
++                                 new_dir->d_inode, new.dentry);
+ exit6:
+       mnt_drop_write(oldnd.path.mnt);
+ exit5:
+-      dput(new_dentry);
++      path_put_conditional(&new, &newnd);
+ exit4:
+-      dput(old_dentry);
++      path_put_conditional(&old, &oldnd);
+ exit3:
+       unlock_rename(new_dir, old_dir);
+ exit2:
+--- a/fs/namespace.c
++++ b/fs/namespace.c
+@@ -29,6 +29,7 @@
+ #include <linux/log2.h>
+ #include <linux/idr.h>
+ #include <linux/fs_struct.h>
++#include <linux/union.h>
+ #include <asm/uaccess.h>
+ #include <asm/unistd.h>
+ #include "pnode.h"
+@@ -150,6 +151,9 @@
+               INIT_LIST_HEAD(&mnt->mnt_share);
+               INIT_LIST_HEAD(&mnt->mnt_slave_list);
+               INIT_LIST_HEAD(&mnt->mnt_slave);
++#ifdef CONFIG_UNION_MOUNT
++              INIT_LIST_HEAD(&mnt->mnt_unions);
++#endif
+ #ifdef CONFIG_SMP
+               mnt->mnt_writers = alloc_percpu(int);
+               if (!mnt->mnt_writers)
+@@ -469,6 +473,7 @@
+ static void detach_mnt(struct vfsmount *mnt, struct path *old_path)
+ {
++      detach_mnt_union(mnt);
+       old_path->dentry = mnt->mnt_mountpoint;
+       old_path->mnt = mnt->mnt_parent;
+       mnt->mnt_parent = mnt;
+@@ -492,6 +497,7 @@
+       list_add_tail(&mnt->mnt_hash, mount_hashtable +
+                       hash(path->mnt, path->dentry));
+       list_add_tail(&mnt->mnt_child, &path->mnt->mnt_mounts);
++      attach_mnt_union(mnt, path->mnt, path->dentry);
+ }
+ /*
+@@ -514,6 +520,7 @@
+       list_add_tail(&mnt->mnt_hash, mount_hashtable +
+                               hash(parent, mnt->mnt_mountpoint));
+       list_add_tail(&mnt->mnt_child, &parent->mnt_mounts);
++      attach_mnt_union(mnt, mnt->mnt_parent, mnt->mnt_mountpoint);
+       touch_mnt_namespace(n);
+ }
+@@ -770,6 +777,7 @@
+               { MNT_NODIRATIME, ",nodiratime" },
+               { MNT_RELATIME, ",relatime" },
+               { MNT_STRICTATIME, ",strictatime" },
++              { MNT_UNION, ",union" },
+               { 0, NULL }
+       };
+       const struct proc_fs_info *fs_infop;
+@@ -984,6 +992,7 @@
+                       struct dentry *dentry;
+                       struct vfsmount *m;
+                       spin_lock(&vfsmount_lock);
++                      detach_mnt_union(mnt);
+                       dentry = mnt->mnt_mountpoint;
+                       m = mnt->mnt_parent;
+                       mnt->mnt_mountpoint = mnt->mnt_root;
+@@ -1102,6 +1111,11 @@
+       spin_unlock(&vfsmount_lock);
+       if (retval)
+               security_sb_umount_busy(mnt);
++      /* If this was a union mount, we are no longer a read-only
++       * user on the underlying mount */
++      if (mnt->mnt_flags & MNT_UNION)
++              mnt->mnt_parent->mnt_sb->s_readonly_users--;
++
+       up_write(&namespace_sem);
+       release_mounts(&umount_list);
+       return retval;
+@@ -1426,6 +1440,10 @@
+       if (path->dentry != path->mnt->mnt_root)
+               return -EINVAL;
++      /* Don't change the type of union mounts */
++      if (IS_MNT_UNION(path->mnt))
++              return -EINVAL;
++
+       down_write(&namespace_sem);
+       if (type == MS_SHARED) {
+               err = invent_group_ids(mnt, recurse);
+@@ -1444,10 +1462,65 @@
+ }
+ /*
++ * Mount-time check of upper and lower layer file systems to see if we
++ * can union mount one on the other.
++ *
++ * Union mounts must follow these rules:
++ *
++ * - The lower layer must be read-only.  This avoids lots of nasty
++ *   unsolvable races where file system structures disappear suddenly.
++ *   XXX - Checking the vfsmnt for read-only is a temporary hack; the
++ *   file system could be mounted read-write elsewhere.  We need to
++ *   enforce read-only at the superblock level (patches coming).
++ *
++ * - The upper layer must be writable.  This isn't an absolute
++ *   requirement; right now we need it to make readdir() work since we
++ *   copy up directory entries to the top level.  A possible
++ *   workaround is to mount a tmpfs file system transparently over the
++ *   top.
++ *
++ * - The upper layer must support whiteouts and fallthrus (if it is
++ *   writeable).
++ *
++ * - The lower layer must not also be a union mount.  This is just to
++ *   make life simpler for now, there is no inherent limitation on the
++ *   number of layers.
++ *
++ * XXX - Check other mount flags for incompatibilities - I'm sure
++ * there are some.
++ */
++
++static int
++check_union_mnt(struct path *mntpnt, struct vfsmount *top_mnt, int mnt_flags)
++{
++      struct vfsmount *lower_mnt = mntpnt->mnt;
++
++      /* Is this even a union mount? */
++      if (!(mnt_flags & MNT_UNION))
++              return 0;
++
++      /* Lower layer must be read-only and not a union mount */
++      if (!(lower_mnt->mnt_sb->s_flags & MS_RDONLY) ||
++          (lower_mnt->mnt_flags & MNT_UNION))
++              return -EBUSY;
++
++      /* Upper layer must be writable */
++      if (mnt_flags & MNT_READONLY)
++              return -EROFS;
++
++      /* Upper layer must support whiteouts and fallthrus */
++      if (!(top_mnt->mnt_sb->s_flags & MS_WHITEOUT))
++              return -EINVAL;
++
++      /* All good! */
++      return 0;
++}
++
++/*
+  * do loopback mount.
+  */
+-static int do_loopback(struct path *path, char *old_name,
+-                              int recurse)
++static int do_loopback(struct path *path, char *old_name, int recurse,
++                     int mnt_flags)
+ {
+       struct path old_path;
+       struct vfsmount *mnt = NULL;
+@@ -1477,6 +1550,13 @@
+       if (!mnt)
+               goto out;
++      err = check_union_mnt(&old_path, mnt, mnt_flags);
++      if (err)
++              goto out;
++
++      if (mnt_flags & MNT_UNION)
++              mnt->mnt_flags |= MNT_UNION;
++
+       err = graft_tree(mnt, path);
+       if (err) {
+               LIST_HEAD(umount_list);
+@@ -1486,6 +1566,10 @@
+               release_mounts(&umount_list);
+       }
++      /* If this is a union mount, add ourselves to the readonly users */
++      if (mnt_flags & MNT_UNION)
++              mnt->mnt_parent->mnt_sb->s_readonly_users++;
++
+ out:
+       up_write(&namespace_sem);
+       path_put(&old_path);
+@@ -1570,6 +1654,13 @@
+       if (err)
+               return err;
++      /* moving to or from a union mount is not supported */
++      err = -EINVAL;
++      if (IS_MNT_UNION(path->mnt))
++              goto exit;
++      if (IS_MNT_UNION(old_path.mnt))
++              goto exit;
++
+       down_write(&namespace_sem);
+       while (d_mountpoint(path->dentry) &&
+              follow_down(path))
+@@ -1627,6 +1718,7 @@
+       up_write(&namespace_sem);
+       if (!err)
+               path_put(&parent_path);
++exit:
+       path_put(&old_path);
+       return err;
+ }
+@@ -1684,10 +1776,18 @@
+       if (S_ISLNK(newmnt->mnt_root->d_inode->i_mode))
+               goto unlock;
++      err = check_union_mnt(path, newmnt, mnt_flags);
++      if (err)
++              goto unlock;
++
+       newmnt->mnt_flags = mnt_flags;
+       if ((err = graft_tree(newmnt, path)))
+               goto unlock;
++      /* If this is a union mount, add ourselves to the readonly users */
++      if (mnt_flags & MNT_UNION)
++              newmnt->mnt_parent->mnt_sb->s_readonly_users++;
++
+       if (fslist) /* add to the specified expiration list */
+               list_add_tail(&newmnt->mnt_expire, fslist);
+@@ -1925,10 +2025,12 @@
+               mnt_flags &= ~(MNT_RELATIME | MNT_NOATIME);
+       if (flags & MS_RDONLY)
+               mnt_flags |= MNT_READONLY;
++      if (flags & MS_UNION)
++              mnt_flags |= MNT_UNION;
+       flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE |
+                  MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |
+-                 MS_STRICTATIME);
++                 MS_STRICTATIME | MS_UNION);
+       /* ... and get the mountpoint */
+       retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);
+@@ -1944,7 +2046,8 @@
+               retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags,
+                                   data_page);
+       else if (flags & MS_BIND)
+-              retval = do_loopback(&path, dev_name, flags & MS_REC);
++              retval = do_loopback(&path, dev_name, flags & MS_REC,
++                                   mnt_flags);
+       else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
+               retval = do_change_type(&path, flags);
+       else if (flags & MS_MOVE)
+@@ -2179,6 +2282,8 @@
+       if (d_unlinked(old.dentry))
+               goto out2;
+       error = -EBUSY;
++      follow_union_down(&new);
++      follow_union_down(&root);
+       if (new.mnt == root.mnt ||
+           old.mnt == root.mnt)
+               goto out2; /* loop, on the same file system  */
+--- a/fs/nfsctl.c
++++ b/fs/nfsctl.c
+@@ -38,10 +38,10 @@
+               return ERR_PTR(error);
+       if (flags == O_RDWR)
+-              error = may_open(&nd.path, MAY_READ|MAY_WRITE,
+-                                         FMODE_READ|FMODE_WRITE);
++              error = may_open(&nd, MAY_READ|MAY_WRITE,
++                               FMODE_READ|FMODE_WRITE);
+       else
+-              error = may_open(&nd.path, MAY_WRITE, FMODE_WRITE);
++              error = may_open(&nd, MAY_WRITE, FMODE_WRITE);
+       if (!error)
+               return dentry_open(nd.path.dentry, nd.path.mnt, flags,
+--- a/fs/nfsd/nfs3xdr.c
++++ b/fs/nfsd/nfs3xdr.c
+@@ -884,6 +884,11 @@
+       int             elen;           /* estimated entry length in words */
+       int             num_entry_words = 0;    /* actual number of words */
++      if (d_type == DT_WHT) {
++              cd->common.err = nfs_ok;
++              return 0;
++      }
++
+       if (cd->offset) {
+               u64 offset64 = offset;
+--- a/fs/nfsd/nfs4xdr.c
++++ b/fs/nfsd/nfs4xdr.c
+@@ -2263,7 +2263,7 @@
+       __be32 nfserr = nfserr_toosmall;
+       /* In nfsv4, "." and ".." never make it onto the wire.. */
+-      if (name && isdotent(name, namlen)) {
++      if (d_type == DT_WHT || (name && isdotent(name, namlen))) {
+               cd->common.err = nfs_ok;
+               return 0;
+       }
+--- a/fs/nfsd/nfsxdr.c
++++ b/fs/nfsd/nfsxdr.c
+@@ -513,6 +513,10 @@
+                       namlen, name, offset, ino);
+        */
++      if (d_type == DT_WHT) {
++              cd->common.err = nfs_ok;
++              return 0;
++      }
+       if (offset > ~((u32) 0)) {
+               cd->common.err = nfserr_fbig;
+               return -EINVAL;
+--- a/fs/open.c
++++ b/fs/open.c
+@@ -30,6 +30,7 @@
+ #include <linux/audit.h>
+ #include <linux/falloc.h>
+ #include <linux/fs_struct.h>
++#include <linux/union.h>
+ int vfs_statfs(struct dentry *dentry, struct kstatfs *buf)
+ {
+@@ -222,69 +223,69 @@
+       return err;
+ }
+-static long do_sys_truncate(const char __user *pathname, loff_t length)
++static int __do_ftruncate(struct file *file, unsigned long length, int small)
+ {
+-      struct path path;
+-      struct inode *inode;
++      struct inode * inode;
++      struct dentry *dentry;
+       int error;
+       error = -EINVAL;
+-      if (length < 0) /* sorry, but loff_t says... */
++      if (length < 0)
+               goto out;
++      /* explicitly opened as large or we are on 64-bit box */
++      if (file->f_flags & O_LARGEFILE)
++              small = 0;
+-      error = user_path(pathname, &path);
+-      if (error)
++      dentry = file->f_path.dentry;
++      inode = dentry->d_inode;
++      error = -EINVAL;
++      if (!S_ISREG(inode->i_mode) || !(file->f_mode & FMODE_WRITE))
+               goto out;
+-      inode = path.dentry->d_inode;
+-
+-      /* For directories it's -EISDIR, for other non-regulars - -EINVAL */
+-      error = -EISDIR;
+-      if (S_ISDIR(inode->i_mode))
+-              goto dput_and_out;
+       error = -EINVAL;
+-      if (!S_ISREG(inode->i_mode))
+-              goto dput_and_out;
+-
+-      error = mnt_want_write(path.mnt);
+-      if (error)
+-              goto dput_and_out;
++      /* Cannot ftruncate over 2^31 bytes without large file support */
++      if (small && length > MAX_NON_LFS)
+-      error = inode_permission(inode, MAY_WRITE);
+-      if (error)
+-              goto mnt_drop_write_and_out;
++              goto out;
+       error = -EPERM;
+       if (IS_APPEND(inode))
+-              goto mnt_drop_write_and_out;
++              goto out;
+-      error = get_write_access(inode);
+-      if (error)
+-              goto mnt_drop_write_and_out;
++      error = locks_verify_truncate(inode, file, length);
++      if (!error)
++              error = security_path_truncate(&file->f_path, length,
++                                             ATTR_MTIME|ATTR_CTIME);
++      if (!error)
++              /* Already copied up for union, opened with write */
++              error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, file);
++out:
++      return error;
++}
+-      /*
+-       * Make sure that there are no leases.  get_write_access() protects
+-       * against the truncate racing with a lease-granting setlease().
+-       */
+-      error = break_lease(inode, FMODE_WRITE);
+-      if (error)
+-              goto put_write_and_out;
++static long do_sys_truncate(const char __user *pathname, loff_t length)
++{
++      struct file *file;
++      char *tmp;
++      int error;
+-      error = locks_verify_truncate(inode, NULL, length);
+-      if (!error)
+-              error = security_path_truncate(&path, length, 0);
+-      if (!error) {
+-              vfs_dq_init(inode);
+-              error = do_truncate(path.dentry, length, 0, NULL);
+-      }
++      error = -EINVAL;
++      if (length < 0) /* sorry, but loff_t says... */
++              return error;
+-put_write_and_out:
+-      put_write_access(inode);
+-mnt_drop_write_and_out:
+-      mnt_drop_write(path.mnt);
+-dput_and_out:
+-      path_put(&path);
+-out:
++      tmp = getname(pathname);
++      if (IS_ERR(tmp))
++              return PTR_ERR(tmp);
++
++      file = filp_open(tmp, O_RDWR | O_LARGEFILE, 0);
++      putname(tmp);
++
++      if (IS_ERR(file))
++              return PTR_ERR(file);
++
++      error = __do_ftruncate(file, length, 0);
++
++      fput(file);
+       return error;
+ }
+@@ -296,45 +297,16 @@
+ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
+ {
+-      struct inode * inode;
+-      struct dentry *dentry;
+       struct file * file;
+       int error;
+-      error = -EINVAL;
+-      if (length < 0)
+-              goto out;
+       error = -EBADF;
+       file = fget(fd);
+       if (!file)
+               goto out;
+-      /* explicitly opened as large or we are on 64-bit box */
+-      if (file->f_flags & O_LARGEFILE)
+-              small = 0;
+-
+-      dentry = file->f_path.dentry;
+-      inode = dentry->d_inode;
+-      error = -EINVAL;
+-      if (!S_ISREG(inode->i_mode) || !(file->f_mode & FMODE_WRITE))
+-              goto out_putf;
+-
+-      error = -EINVAL;
+-      /* Cannot ftruncate over 2^31 bytes without large file support */
+-      if (small && length > MAX_NON_LFS)
+-              goto out_putf;
++      error = __do_ftruncate(file, length, small);
+-      error = -EPERM;
+-      if (IS_APPEND(inode))
+-              goto out_putf;
+-
+-      error = locks_verify_truncate(inode, file, length);
+-      if (!error)
+-              error = security_path_truncate(&file->f_path, length,
+-                                             ATTR_MTIME|ATTR_CTIME);
+-      if (!error)
+-              error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, file);
+-out_putf:
+       fput(file);
+ out:
+       return error;
+@@ -493,7 +465,8 @@
+                       goto out_path_release;
+       }
+-      res = inode_permission(inode, mode | MAY_ACCESS);
++      res = union_permission(&path, mode | MAY_ACCESS);
++
+       /* SuS v2 requires we report a read only fs too */
+       if (res || !(mode & S_IWOTH) || special_file(inode->i_mode))
+               goto out_path_release;
+@@ -507,7 +480,8 @@
+        * inherently racy and know that the fs may change
+        * state before we even see this result.
+        */
+-      if (__mnt_is_readonly(path.mnt))
++      if ((!is_unionized(path.dentry, path.mnt) &&
++           (__mnt_is_readonly(path.mnt))))
+               res = -EROFS;
+ out_path_release:
+@@ -553,20 +527,19 @@
+       error = -EBADF;
+       file = fget(fd);
+       if (!file)
+-              goto out;
++              return error;
+       inode = file->f_path.dentry->d_inode;
+       error = -ENOTDIR;
+       if (!S_ISDIR(inode->i_mode))
+-              goto out_putf;
++              goto out;
+       error = inode_permission(inode, MAY_EXEC | MAY_ACCESS);
+       if (!error)
+               set_fs_pwd(current->fs, &file->f_path);
+-out_putf:
+-      fput(file);
+ out:
++      fput(file);
+       return error;
+ }
+--- a/fs/readdir.c
++++ b/fs/readdir.c
+@@ -16,6 +16,7 @@
+ #include <linux/security.h>
+ #include <linux/syscalls.h>
+ #include <linux/unistd.h>
++#include <linux/union.h>
+ #include <asm/uaccess.h>
+@@ -36,9 +37,24 @@
+       res = -ENOENT;
+       if (!IS_DEADDIR(inode)) {
++              /*
++               * XXX Think harder about locking for
++               * union_copyup_dir.  Currently we lock the topmost
++               * directory and hold that lock while sequentially
++               * acquiring and dropping locks for the directories
++               * below this one in the union stack.
++               */
++              if (is_unionized(file->f_path.dentry, file->f_path.mnt) &&
++                  !IS_OPAQUE(inode)) {
++                      res = union_copyup_dir(&file->f_path);
++                      if (res)
++                              goto out_unlock;
++              }
++
+               res = file->f_op->readdir(file, buf, filler);
+               file_accessed(file);
+       }
++out_unlock:
+       mutex_unlock(&inode->i_mutex);
+ out:
+       return res;
+@@ -77,6 +93,9 @@
+       struct old_linux_dirent __user * dirent;
+       unsigned long d_ino;
++      if (d_type == DT_WHT)
++              return 0;
++
+       if (buf->result)
+               return -EINVAL;
+       d_ino = ino;
+@@ -154,6 +173,9 @@
+       unsigned long d_ino;
+       int reclen = ALIGN(NAME_OFFSET(dirent) + namlen + 2, sizeof(long));
++      if (d_type == DT_WHT)
++              return 0;
++
+       buf->error = -EINVAL;   /* only used if we fail.. */
+       if (reclen > buf->count)
+               return -EINVAL;
+@@ -239,6 +261,9 @@
+       struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf;
+       int reclen = ALIGN(NAME_OFFSET(dirent) + namlen + 1, sizeof(u64));
++      if (d_type == DT_WHT)
++              return 0;
++
+       buf->error = -EINVAL;   /* only used if we fail.. */
+       if (reclen > buf->count)
+               return -EINVAL;
+--- a/fs/super.c
++++ b/fs/super.c
+@@ -553,6 +553,15 @@
+       }
+       remount_rw = !(flags & MS_RDONLY) && (sb->s_flags & MS_RDONLY);
++      /* If we are remounting read/write, make sure that none of the
++         users require read-only for correct operation (such as
++         union mounts). */
++      if (remount_rw && sb->s_readonly_users) {
++              printk(KERN_INFO "%s: In use by %d read-only user(s)\n",
++                     sb->s_id, sb->s_readonly_users);
++              return -EROFS;
++      }
++
+       if (sb->s_op->remount_fs) {
+               retval = sb->s_op->remount_fs(sb, &flags, data);
+               if (retval)
+@@ -889,6 +898,11 @@
+       if (error)
+               goto out_sb;
++      error = -EROFS;
++      if (!(flags & MS_RDONLY) &&
++          (mnt->mnt_sb->s_readonly_users))
++              goto out_sb;
++
+       mnt->mnt_mountpoint = mnt->mnt_root;
+       mnt->mnt_parent = mnt;
+       up_write(&mnt->mnt_sb->s_umount);
+--- /dev/null
++++ b/fs/union.c
+@@ -0,0 +1,981 @@
++/*
++ * VFS based union mount for Linux
++ *
++ * Copyright (C) 2004-2007 IBM Corporation, IBM Deutschland Entwicklung GmbH.
++ * Copyright (C) 2007-2009 Novell Inc.
++ *
++ *   Author(s): Jan Blunck (j.blunck@tu-harburg.de)
++ *              Valerie Aurora <vaurora@redhat.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the Free
++ * Software Foundation; either version 2 of the License, or (at your option)
++ * any later version.
++ */
++
++#include <linux/bootmem.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/hash.h>
++#include <linux/fs.h>
++#include <linux/mount.h>
++#include <linux/fs_struct.h>
++#include <linux/union.h>
++#include <linux/namei.h>
++#include <linux/file.h>
++#include <linux/mm.h>
++#include <linux/quotaops.h>
++#include <linux/dnotify.h>
++#include <linux/security.h>
++#include <linux/pipe_fs_i.h>
++#include <linux/splice.h>
++
++/*
++ * This is borrowed from fs/inode.c. The hashtable for lookups. Somebody
++ * should try to make this good - I've just made it work.
++ */
++static unsigned int union_hash_mask __read_mostly;
++static unsigned int union_hash_shift __read_mostly;
++static struct hlist_head *union_hashtable __read_mostly;
++static unsigned int union_rhash_mask __read_mostly;
++static unsigned int union_rhash_shift __read_mostly;
++static struct hlist_head *union_rhashtable __read_mostly;
++
++/*
++ * Locking Rules:
++ * - dcache_lock (for union_rlookup() only)
++ * - union_lock
++ */
++DEFINE_SPINLOCK(union_lock);
++
++static struct kmem_cache *union_cache __read_mostly;
++
++static unsigned long hash(struct dentry *dentry, struct vfsmount *mnt)
++{
++      unsigned long tmp;
++
++      tmp = ((unsigned long)mnt * (unsigned long)dentry) ^
++              (GOLDEN_RATIO_PRIME + (unsigned long)mnt) / L1_CACHE_BYTES;
++      tmp = tmp ^ ((tmp ^ GOLDEN_RATIO_PRIME) >> union_hash_shift);
++      return tmp & union_hash_mask;
++}
++
++static __initdata unsigned long union_hash_entries;
++
++static int __init set_union_hash_entries(char *str)
++{
++      if (!str)
++              return 0;
++      union_hash_entries = simple_strtoul(str, &str, 0);
++      return 1;
++}
++
++__setup("union_hash_entries=", set_union_hash_entries);
++
++static int __init init_union(void)
++{
++      int loop;
++
++      union_cache = KMEM_CACHE(union_mount, SLAB_PANIC | SLAB_MEM_SPREAD);
++      union_hashtable = alloc_large_system_hash("Union-cache",
++                                                sizeof(struct hlist_head),
++                                                union_hash_entries,
++                                                14,
++                                                0,
++                                                &union_hash_shift,
++                                                &union_hash_mask,
++                                                0);
++
++      for (loop = 0; loop < (1 << union_hash_shift); loop++)
++              INIT_HLIST_HEAD(&union_hashtable[loop]);
++
++
++      union_rhashtable = alloc_large_system_hash("rUnion-cache",
++                                                sizeof(struct hlist_head),
++                                                union_hash_entries,
++                                                14,
++                                                0,
++                                                &union_rhash_shift,
++                                                &union_rhash_mask,
++                                                0);
++
++      for (loop = 0; loop < (1 << union_rhash_shift); loop++)
++              INIT_HLIST_HEAD(&union_rhashtable[loop]);
++
++      return 0;
++}
++
++fs_initcall(init_union);
++
++struct union_mount *union_alloc(struct dentry *this, struct vfsmount *this_mnt,
++                              struct dentry *next, struct vfsmount *next_mnt)
++{
++      struct union_mount *um;
++
++      BUG_ON(!S_ISDIR(this->d_inode->i_mode));
++      BUG_ON(!S_ISDIR(next->d_inode->i_mode));
++
++      um = kmem_cache_alloc(union_cache, GFP_ATOMIC);
++      if (!um)
++              return NULL;
++
++      atomic_set(&um->u_count, 1);
++      INIT_LIST_HEAD(&um->u_unions);
++      INIT_LIST_HEAD(&um->u_list);
++      INIT_HLIST_NODE(&um->u_hash);
++      INIT_HLIST_NODE(&um->u_rhash);
++
++      um->u_this.mnt = this_mnt;
++      um->u_this.dentry = this;
++      um->u_next.mnt = mntget(next_mnt);
++      um->u_next.dentry = dget(next);
++
++      return um;
++}
++
++struct union_mount *union_get(struct union_mount *um)
++{
++      BUG_ON(!atomic_read(&um->u_count));
++      atomic_inc(&um->u_count);
++      return um;
++}
++
++static int __union_put(struct union_mount *um)
++{
++      if (!atomic_dec_and_test(&um->u_count))
++              return 0;
++
++      BUG_ON(!hlist_unhashed(&um->u_hash));
++      BUG_ON(!hlist_unhashed(&um->u_rhash));
++
++      kmem_cache_free(union_cache, um);
++      return 1;
++}
++
++void union_put(struct union_mount *um)
++{
++      struct path tmp = um->u_next;
++
++      if (__union_put(um))
++              path_put(&tmp);
++}
++
++static void __union_hash(struct union_mount *um)
++{
++      hlist_add_head(&um->u_hash, union_hashtable +
++                     hash(um->u_this.dentry, um->u_this.mnt));
++      hlist_add_head(&um->u_rhash, union_rhashtable +
++                     hash(um->u_next.dentry, um->u_next.mnt));
++}
++
++static void __union_unhash(struct union_mount *um)
++{
++      hlist_del_init(&um->u_hash);
++      hlist_del_init(&um->u_rhash);
++}
++
++struct union_mount *union_lookup(struct dentry *dentry, struct vfsmount *mnt)
++{
++      struct hlist_head *head = union_hashtable + hash(dentry, mnt);
++      struct hlist_node *node;
++      struct union_mount *um;
++
++      hlist_for_each_entry(um, node, head, u_hash) {
++              if ((um->u_this.dentry == dentry) &&
++                  (um->u_this.mnt == mnt))
++                      return um;
++      }
++
++      return NULL;
++}
++
++struct union_mount *union_rlookup(struct dentry *dentry, struct vfsmount *mnt)
++{
++      struct hlist_head *head = union_rhashtable + hash(dentry, mnt);
++      struct hlist_node *node;
++      struct union_mount *um;
++
++      hlist_for_each_entry(um, node, head, u_rhash) {
++              if ((um->u_next.dentry == dentry) &&
++                  (um->u_next.mnt == mnt))
++                      return um;
++      }
++
++      return NULL;
++}
++
++/*
++ * is_unionized - check if a dentry lives on a union mounted file system
++ *
++ * This tests if a dentry is living on an union mounted file system by walking
++ * the file system hierarchy.
++ */
++int is_unionized(struct dentry *dentry, struct vfsmount *mnt)
++{
++      struct path this = { .mnt = mntget(mnt),
++                           .dentry = dget(dentry) };
++      struct vfsmount *tmp;
++
++      do {
++              /* check if there is an union mounted on top of us */
++              spin_lock(&vfsmount_lock);
++              list_for_each_entry(tmp, &this.mnt->mnt_mounts, mnt_child) {
++                      if (!(tmp->mnt_flags & MNT_UNION))
++                              continue;
++                      /* Isn't this a bug? */
++                      if (this.dentry->d_sb != tmp->mnt_mountpoint->d_sb)
++                              continue;
++                      if (is_subdir(this.dentry, tmp->mnt_mountpoint)) {
++                              spin_unlock(&vfsmount_lock);
++                              path_put(&this);
++                              return 1;
++                      }
++              }
++              spin_unlock(&vfsmount_lock);
++
++              /* check our mountpoint next */
++              tmp = mntget(this.mnt->mnt_parent);
++              dput(this.dentry);
++              this.dentry = dget(this.mnt->mnt_mountpoint);
++              mntput(this.mnt);
++              this.mnt = tmp;
++      } while (this.mnt != this.mnt->mnt_parent);
++
++      path_put(&this);
++      return 0;
++}
++
++int append_to_union(struct vfsmount *mnt, struct dentry *dentry,
++                  struct vfsmount *dest_mnt, struct dentry *dest_dentry)
++{
++      struct union_mount *this, *um;
++
++      BUG_ON(!IS_MNT_UNION(mnt));
++
++      this = union_alloc(dentry, mnt, dest_dentry, dest_mnt);
++      if (!this)
++              return -ENOMEM;
++
++      spin_lock(&union_lock);
++      um = union_lookup(dentry, mnt);
++      if (um) {
++              BUG_ON((um->u_next.dentry != dest_dentry) ||
++                     (um->u_next.mnt != dest_mnt));
++              spin_unlock(&union_lock);
++              union_put(this);
++              return 0;
++      }
++      list_add(&this->u_list, &mnt->mnt_unions);
++      list_add(&this->u_unions, &dentry->d_unions);
++      dest_dentry->d_unionized++;
++      __union_hash(this);
++      spin_unlock(&union_lock);
++      return 0;
++}
++
++/*
++ * follow_union_down - follow the union stack one layer down
++ *
++ * This is called to traverse the union stack from one layer to the next
++ * overlayed one. follow_union_down() is called by various lookup functions
++ * that are aware of union mounts.
++ *
++ * Returns non-zero if followed to the next layer, zero otherwise.
++ */
++int follow_union_down(struct path *path)
++{
++      struct union_mount *um;
++
++      if (!IS_MNT_UNION(path->mnt))
++              return 0;
++
++      spin_lock(&union_lock);
++      um = union_lookup(path->dentry, path->mnt);
++      spin_unlock(&union_lock);
++      if (um) {
++              path_get(&um->u_next);
++              dput(path->dentry);
++              path->dentry = um->u_next.dentry;
++              mntput(path->mnt);
++              path->mnt = um->u_next.mnt;
++              return 1;
++      }
++      return 0;
++}
++
++/*
++ * follow_union_mount - follow the union stack to the topmost layer
++ *
++ * This is called to traverse the union stack to the topmost layer. This is
++ * necessary for following parent pointers in an union mount.
++ *
++ * Returns none zero if followed to the topmost layer, zero otherwise.
++ */
++int follow_union_mount(struct path *path)
++{
++      struct union_mount *um;
++      int res = 0;
++
++      while (IS_UNION(path->dentry)) {
++              spin_lock(&dcache_lock);
++              spin_lock(&union_lock);
++              um = union_rlookup(path->dentry, path->mnt);
++              if (um)
++                      path_get(&um->u_this);
++              spin_unlock(&union_lock);
++              spin_unlock(&dcache_lock);
++
++              /*
++               * Q: Aaargh, how do I validate the topmost dentry pointer?
++               * A: Eeeeasy! We took the dcache_lock and union_lock. Since
++               *    this protects from any dput'ng going on, we know that the
++               *    dentry is valid since the union is unhashed under
++               *    dcache_lock too.
++               */
++              if (!um)
++                      break;
++              dput(path->dentry);
++              path->dentry = um->u_this.dentry;
++              mntput(path->mnt);
++              path->mnt = um->u_this.mnt;
++              res = 1;
++      }
++
++      return res;
++}
++
++/*
++ * Union mount copyup support
++ */
++
++extern int hash_lookup_union(struct nameidata *, struct qstr *, struct path *);
++extern void follow_mount(struct path *);
++
++/*
++ * union_relookup_topmost - lookup and create the topmost path to dentry
++ * @nd: pointer to nameidata
++ * @flags: lookup flags
++ */
++static int union_relookup_topmost(struct nameidata *nd, int flags)
++{
++      int err;
++      char *kbuf, *name;
++      struct nameidata this;
++
++      kbuf = (char *)__get_free_page(GFP_KERNEL);
++      if (!kbuf)
++              return -ENOMEM;
++
++      name = d_path(&nd->path, kbuf, PAGE_SIZE);
++      err = PTR_ERR(name);
++      if (IS_ERR(name))
++              goto free_page;
++
++      err = path_lookup(name, flags|LOOKUP_CREATE|LOOKUP_TOPMOST, &this);
++      if (err)
++              goto free_page;
++
++      path_put(&nd->path);
++      nd->path.dentry = this.path.dentry;
++      nd->path.mnt = this.path.mnt;
++
++      /*
++       * the nd->flags should be unchanged
++       */
++      BUG_ON(this.um_flags & LAST_LOWLEVEL);
++      nd->um_flags &= ~LAST_LOWLEVEL;
++ free_page:
++      free_page((unsigned long)kbuf);
++      return err;
++}
++
++static void __update_fs_pwd(struct path *path, struct dentry *dentry,
++                          struct vfsmount *mnt)
++{
++      struct path old = { NULL, NULL };
++
++      write_lock(&current->fs->lock);
++      if (current->fs->pwd.dentry == path->dentry) {
++              old = current->fs->pwd;
++              path_get(&current->fs->pwd);
++      }
++      write_unlock(&current->fs->lock);
++
++      if (old.dentry)
++              path_put(&old);
++
++      return;
++}
++
++/**
++ * union_permission  -  check for access rights to a given inode
++ * @inode:    inode to check permission on
++ * @mask:     right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
++ *
++ * In a union mount, the top layer is always read-write and the bottom
++ * is always read-only.  Ignore the read-only flag on the lower fs.
++ *
++ * Only need for certain activities, like checking to see if write
++ * access is ok.
++ */
++
++int union_permission(struct path *path, int mask)
++{
++      struct inode *inode = path->dentry->d_inode;
++
++      if (!is_unionized(path->dentry, path->mnt))
++              return inode_permission(inode, mask);
++
++      /* Tell __inode_permission to ignore MS_RDONLY */
++      return __inode_permission(inode, mask, 0);
++}
++
++/*
++ * union_create_topmost - create the topmost path component
++ * @nd: pointer to nameidata of the base directory
++ * @name: pointer to file name
++ * @path: pointer to path of the overlaid file
++ *
++ * This is called by __link_path_walk() to create the directories on a path
++ * when it is called with LOOKUP_TOPMOST.
++ */
++struct dentry *union_create_topmost(struct nameidata *nd, struct qstr *name,
++                                  struct path *path)
++{
++      struct dentry *dentry, *parent = nd->path.dentry;
++      int res, mode = path->dentry->d_inode->i_mode;
++
++      if (parent->d_sb == path->dentry->d_sb)
++              return ERR_PTR(-EEXIST);
++
++      mutex_lock(&parent->d_inode->i_mutex);
++      dentry = lookup_one_len(name->name, nd->path.dentry, name->len);
++      if (IS_ERR(dentry))
++              goto out_unlock;
++
++      switch (mode & S_IFMT) {
++      case S_IFREG:
++              /*
++               * FIXME: Does this make any sense in this case?
++               * Special case - lookup gave negative, but... we had foo/bar/
++               * From the vfs_mknod() POV we just have a negative dentry -
++               * all is fine. Let's be bastards - you had / on the end,you've
++               * been asking for (non-existent) directory. -ENOENT for you.
++               */
++              if (name->name[name->len] && !dentry->d_inode) {
++                      dput(dentry);
++                      dentry = ERR_PTR(-ENOENT);
++                      goto out_unlock;
++              }
++
++              res = vfs_create(parent->d_inode, dentry, mode, nd);
++              if (res) {
++                      dput(dentry);
++                      dentry = ERR_PTR(res);
++                      goto out_unlock;
++              }
++              break;
++      case S_IFDIR:
++              res = vfs_mkdir(parent->d_inode, dentry, mode);
++              if (res) {
++                      dput(dentry);
++                      dentry = ERR_PTR(res);
++                      goto out_unlock;
++              }
++
++              res = append_to_union(nd->path.mnt, dentry, path->mnt,
++                                    path->dentry);
++              if (res) {
++                      dput(dentry);
++                      dentry = ERR_PTR(res);
++                      goto out_unlock;
++              }
++              break;
++      default:
++              dput(dentry);
++              dentry = ERR_PTR(-EINVAL);
++              goto out_unlock;
++      }
++
++      /* FIXME: Really necessary ??? */
++/*    __update_fs_pwd(path, dentry, nd->path.mnt); */
++
++ out_unlock:
++      mutex_unlock(&parent->d_inode->i_mutex);
++      return dentry;
++}
++
++static int union_copy_file(struct dentry *old_dentry, struct vfsmount *old_mnt,
++                         struct dentry *new_dentry, struct vfsmount *new_mnt)
++{
++      int ret;
++      size_t size;
++      loff_t offset;
++      struct file *old_file, *new_file;
++      const struct cred *cred = current_cred();
++
++      dget(old_dentry);
++      mntget(old_mnt);
++      old_file = dentry_open(old_dentry, old_mnt, O_RDONLY, cred);
++      if (IS_ERR(old_file))
++              return PTR_ERR(old_file);
++
++      dget(new_dentry);
++      mntget(new_mnt);
++      new_file = dentry_open(new_dentry, new_mnt, O_WRONLY, cred);
++      ret = PTR_ERR(new_file);
++      if (IS_ERR(new_file))
++              goto fput_old;
++
++      /* XXX be smart by using a length param, which indicates max
++       * data we'll want (e.g., we are about to truncate to 0 or 10
++       * bytes or something */
++      size = i_size_read(old_file->f_path.dentry->d_inode);
++      if (((size_t)size != size) || ((ssize_t)size != size)) {
++              ret = -EFBIG;
++              goto fput_new;
++      }
++
++      offset = 0;
++      ret = do_splice_direct(old_file, &offset, new_file, size,
++                             SPLICE_F_MOVE);
++      if (ret >= 0)
++              ret = 0;
++ fput_new:
++      fput(new_file);
++ fput_old:
++      fput(old_file);
++      return ret;
++}
++
++/**
++ * __union_copyup - copy a file to the topmost directory
++ * @old: pointer to path of the old file name
++ * @new_nd: pointer to nameidata of the topmost directory
++ * @new: pointer to path of the new file name
++ *
++ * The topmost directory @new_nd must already be locked. Creates the topmost
++ * file if it doesn't exist yet.
++ */
++int __union_copyup(struct path *old, struct nameidata *new_nd,
++                 struct path *new)
++{
++      struct dentry *dentry;
++      int error;
++
++      /* Maybe this should be -EINVAL */
++      if (S_ISDIR(old->dentry->d_inode->i_mode))
++              return -EISDIR;
++
++      if (new_nd->path.dentry != new->dentry->d_parent) {
++              mutex_lock(&new_nd->path.dentry->d_inode->i_mutex);
++              dentry = lookup_one_len(new->dentry->d_name.name,
++                                      new_nd->path.dentry,
++                                      new->dentry->d_name.len);
++              mutex_unlock(&new_nd->path.dentry->d_inode->i_mutex);
++              if (IS_ERR(dentry))
++                      return PTR_ERR(dentry);
++              error = -EEXIST;
++              if (dentry->d_inode)
++                      goto out_dput;
++      } else
++              dentry = dget(new->dentry);
++
++      if (!dentry->d_inode) {
++              error = vfs_create(new_nd->path.dentry->d_inode, dentry,
++                                 old->dentry->d_inode->i_mode, new_nd);
++              if (error)
++                      goto out_dput;
++      }
++
++      BUG_ON(!S_ISREG(old->dentry->d_inode->i_mode));
++      error = union_copy_file(old->dentry, old->mnt, dentry,
++                              new_nd->path.mnt);
++      if (error) {
++              /* FIXME: are there return value we should not
++               * BUG() on ? */
++              BUG_ON(vfs_unlink(new_nd->path.dentry->d_inode,
++                                dentry));
++              goto out_dput;
++      }
++
++      dput(new->dentry);
++      new->dentry = dentry;
++      if (new->mnt != new_nd->path.mnt)
++              mntput(new->mnt);
++      new->mnt = new_nd->path.mnt;
++      return error;
++
++out_dput:
++      dput(dentry);
++      return error;
++}
++
++/*
++ * union_copyup - copy a file to the topmost layer of the union stack
++ * @nd: nameidata pointer to the file
++ * @flags: flags given to open_namei
++ */
++int union_copyup(struct nameidata *nd, int flags /* XXX not used */)
++{
++      struct qstr this;
++      char *name;
++      struct dentry *dir;
++      struct path path;
++      int err;
++
++      if (!is_unionized(nd->path.dentry, nd->path.mnt))
++              return 0;
++      if (!S_ISREG(nd->path.dentry->d_inode->i_mode))
++              return 0;
++
++      /* safe the name for hash_lookup_union() */
++      this.len = nd->path.dentry->d_name.len;
++      this.hash = nd->path.dentry->d_name.hash;
++      name = kmalloc(this.len + 1, GFP_KERNEL);
++      if (!name)
++              return -ENOMEM;
++      this.name = name;
++      memcpy(name, nd->path.dentry->d_name.name, nd->path.dentry->d_name.len);
++      name[this.len] = 0;
++
++      err = union_relookup_topmost(nd, nd->flags|LOOKUP_PARENT);
++      if (err) {
++              kfree(name);
++              return err;
++      }
++      nd->flags &= ~LOOKUP_PARENT;
++
++      dir = nd->path.dentry;
++      mutex_lock(&dir->d_inode->i_mutex);
++      err = hash_lookup_union(nd, &this, &path);
++      mutex_unlock(&dir->d_inode->i_mutex);
++      kfree(name);
++      if (err)
++              return err;
++
++      err = -ENOENT;
++      if (!path.dentry->d_inode)
++              goto exit_dput;
++
++      /* Necessary?! I guess not ... */
++      follow_mount(&path);
++
++      err = -ENOENT;
++      if (!path.dentry->d_inode)
++              goto exit_dput;
++
++      err = -EISDIR;
++      if (!S_ISREG(path.dentry->d_inode->i_mode))
++              goto exit_dput;
++
++      if (path.dentry->d_parent != nd->path.dentry) {
++              err = __union_copyup(&path, nd, &path);
++              if (err)
++                      goto exit_dput;
++      }
++
++      dput(nd->path.dentry);
++      if (nd->path.mnt != path.mnt)
++              mntput(nd->path.mnt);
++      nd->path = path;
++      return 0;
++
++exit_dput:
++      dput(path.dentry);
++      if (path.mnt != nd->path.mnt)
++              mntput(path.mnt);
++      return err;
++}
++
++/*
++ * This must be called when unhashing a dentry. This is called with dcache_lock
++ * and unhashes all unions this dentry is in.
++ */
++void __d_drop_unions(struct dentry *dentry)
++{
++      struct union_mount *this, *next;
++
++      spin_lock(&union_lock);
++      list_for_each_entry_safe(this, next, &dentry->d_unions, u_unions)
++              __union_unhash(this);
++      spin_unlock(&union_lock);
++}
++EXPORT_SYMBOL_GPL(__d_drop_unions);
++
++/*
++ * This must be called after __d_drop_unions() without holding any locks.
++ * Note: The dentry might still be reachable via a lookup but at that time it
++ * already a negative dentry. Otherwise it would be unhashed. The union_mount
++ * structure itself is still reachable through mnt->mnt_unions (which we
++ * protect against with union_lock).
++ */
++void shrink_d_unions(struct dentry *dentry)
++{
++      struct union_mount *this, *next;
++
++repeat:
++      spin_lock(&union_lock);
++      list_for_each_entry_safe(this, next, &dentry->d_unions, u_unions) {
++              BUG_ON(!hlist_unhashed(&this->u_hash));
++              BUG_ON(!hlist_unhashed(&this->u_rhash));
++              list_del(&this->u_list);
++              list_del(&this->u_unions);
++              this->u_next.dentry->d_unionized--;
++              spin_unlock(&union_lock);
++              union_put(this);
++              goto repeat;
++      }
++      spin_unlock(&union_lock);
++}
++
++extern void __dput(struct dentry *, struct list_head *, int);
++
++/*
++ * This is the special variant for use in dput() only.
++ */
++void __shrink_d_unions(struct dentry *dentry, struct list_head *list)
++{
++      struct union_mount *this, *next;
++
++      BUG_ON(!d_unhashed(dentry));
++
++repeat:
++      spin_lock(&union_lock);
++      list_for_each_entry_safe(this, next, &dentry->d_unions, u_unions) {
++              struct dentry *n_dentry = this->u_next.dentry;
++              struct vfsmount *n_mnt = this->u_next.mnt;
++
++              BUG_ON(!hlist_unhashed(&this->u_hash));
++              BUG_ON(!hlist_unhashed(&this->u_rhash));
++              list_del(&this->u_list);
++              list_del(&this->u_unions);
++              this->u_next.dentry->d_unionized--;
++              spin_unlock(&union_lock);
++              if (__union_put(this)) {
++                      __dput(n_dentry, list, 0);
++                      mntput(n_mnt);
++              }
++              goto repeat;
++      }
++      spin_unlock(&union_lock);
++}
++
++/*
++ * Remove all union_mounts structures belonging to this vfsmount from the
++ * union lookup hashtable and so on ...
++ */
++void shrink_mnt_unions(struct vfsmount *mnt)
++{
++      struct union_mount *this, *next;
++
++repeat:
++      spin_lock(&union_lock);
++      list_for_each_entry_safe(this, next, &mnt->mnt_unions, u_list) {
++              if (this->u_this.dentry == mnt->mnt_root)
++                      continue;
++              __union_unhash(this);
++              list_del(&this->u_list);
++              list_del(&this->u_unions);
++              this->u_next.dentry->d_unionized--;
++              spin_unlock(&union_lock);
++              union_put(this);
++              goto repeat;
++      }
++      spin_unlock(&union_lock);
++}
++
++int attach_mnt_union(struct vfsmount *mnt, struct vfsmount *dest_mnt,
++                   struct dentry *dest_dentry)
++{
++      if (!IS_MNT_UNION(mnt))
++              return 0;
++
++      return append_to_union(mnt, mnt->mnt_root, dest_mnt, dest_dentry);
++}
++
++void detach_mnt_union(struct vfsmount *mnt)
++{
++      struct union_mount *um;
++
++      if (!IS_MNT_UNION(mnt))
++              return;
++
++      shrink_mnt_unions(mnt);
++
++      spin_lock(&union_lock);
++      um = union_lookup(mnt->mnt_root, mnt);
++      __union_unhash(um);
++      list_del(&um->u_list);
++      list_del(&um->u_unions);
++      um->u_next.dentry->d_unionized--;
++      spin_unlock(&union_lock);
++      union_put(um);
++      return;
++}
++
++/**
++ * union_copyup_dir_one - copy up a single directory entry
++ *
++ * Individual directory entry copyup function for union_copyup_dir.
++ * We get the entries from higher level layers first.
++ */
++
++static int union_copyup_dir_one(void *buf, const char *name, int namlen,
++                              loff_t offset, u64 ino, unsigned int d_type)
++{
++      struct dentry *topmost_dentry = (struct dentry *) buf;
++      struct dentry *dentry;
++      int err = 0;
++
++      switch (namlen) {
++      case 2:
++              if (name[1] != '.')
++                      break;
++      case 1:
++              if (name[0] != '.')
++                      break;
++              return 0;
++      }
++
++      /* Lookup this entry in the topmost directory */
++      dentry = lookup_one_len(name, topmost_dentry, namlen);
++
++      if (IS_ERR(dentry)) {
++              printk(KERN_INFO "error looking up %s\n", dentry->d_name.name);
++              goto out;
++      }
++
++      /*
++       * If the entry already exists, one of the following is true:
++       * it was already copied up (due to an earlier lookup), an
++       * entry with the same name already exists on the topmost file
++       * system, it is a whiteout, or it is a fallthru.  In each
++       * case, the top level entry masks any entries from lower file
++       * systems, so don't copy up this entry.
++       */
++      if (dentry->d_inode || d_is_whiteout(dentry) ||
++          d_is_fallthru(dentry)) {
++              printk(KERN_INFO "skipping copy of %s\n", dentry->d_name.name);
++              goto out_dput;
++      }
++
++      /*
++       * If the entry doesn't exist, create a fallthru entry in the
++       * topmost file system.  All possible directory types are
++       * used, so each file system must implement its own way of
++       * storing a fallthru entry.
++       */
++      printk(KERN_INFO "creating fallthru for %s\n", dentry->d_name.name);
++      err = topmost_dentry->d_inode->i_op->fallthru(topmost_dentry->d_inode,
++                                                    dentry);
++      /* FIXME */
++      BUG_ON(err);
++      /*
++       * At this point, we have a negative dentry marked as fallthru
++       * in the cache.  We could potentially lookup the entry lower
++       * level file system and turn this into a positive dentry
++       * right now, but it is not clear that would be a performance
++       * win and adds more opportunities to fail.
++       */
++out_dput:
++      dput(dentry);
++out:
++      return 0;
++}
++
++/**
++ * union_copyup_dir - copy up low-level directory entries to topmost dir
++ *
++ * readdir() is difficult to support on union file systems for two
++ * reasons: We must eliminate duplicates and apply whiteouts, and we
++ * must return something in f_pos that lets us restart in the same
++ * place when we return.  Our solution is to, on first readdir() of
++ * the directory, copy up all visible entries from the low-level file
++ * systems and mark the entries that refer to low-level file system
++ * objects as "fallthru" entries.
++ */
++
++int union_copyup_dir(struct path *topmost_path)
++{
++      struct dentry *topmost_dentry = topmost_path->dentry;
++      struct path path = *topmost_path;
++      int res = 0;
++
++      /*
++       * Skip opaque dirs.
++       */
++      if (IS_OPAQUE(topmost_dentry->d_inode))
++              return 0;
++
++      /*
++       * Mark this dir opaque to show that we have already copied up
++       * the lower entries.  Only fallthru entries pass through to
++       * the underlying file system.
++       *
++       * XXX Deal with the lower file system changing.  This could
++       * be through running a tool over the top level file system to
++       * make directories transparent again, or we could check the
++       * mtime of the underlying directory.
++       */
++
++      topmost_dentry->d_inode->i_flags |= S_OPAQUE;
++      mark_inode_dirty(topmost_dentry->d_inode);
++
++      /*
++       * Loop through each dir on each level copying up the entries
++       * to the topmost.
++       */
++
++      /* Don't drop the caller's reference to the topmost path */
++      path_get(&path);
++      while (follow_union_down(&path)) {
++              struct file * ftmp;
++              struct inode * inode;
++
++              /* XXX Permit fallthrus on lower-level? Would need to
++               * pass in opaque flag to union_copyup_dir_one() and
++               * only copy up fallthru entries there.  We allow
++               * fallthrus in lower level opaque directories on
++               * lookup, so for consistency we should do one or the
++               * other in both places. */
++              if (IS_OPAQUE(path.dentry->d_inode))
++                      break;
++
++              /* dentry_open() doesn't get a path reference itself */
++              path_get(&path);
++              ftmp = dentry_open(path.dentry, path.mnt,
++                                 O_RDONLY | O_DIRECTORY | O_NOATIME,
++                                 current_cred());
++              if (IS_ERR(ftmp)) {
++                      printk (KERN_ERR "unable to open dir %s for "
++                              "directory copyup: %ld\n",
++                              path.dentry->d_name.name, PTR_ERR(ftmp));
++                      continue;
++              }
++
++              inode = path.dentry->d_inode;
++              mutex_lock(&inode->i_mutex);
++
++              res = -ENOENT;
++              if (IS_DEADDIR(inode))
++                      goto out_fput;
++              /*
++               * Read the whole directory, calling our directory
++               * entry copyup function on each entry.  Pass in the
++               * topmost dentry as our private data so we can create
++               * new entries in the topmost directory.
++               */
++              res = ftmp->f_op->readdir(ftmp, topmost_dentry,
++                                        union_copyup_dir_one);
++out_fput:
++              mutex_unlock(&inode->i_mutex);
++              fput(ftmp);
++
++              if (res)
++                      break;
++      }
++      path_put(&path);
++      return res;
++}
+--- a/include/linux/dcache.h
++++ b/include/linux/dcache.h
+@@ -101,6 +101,15 @@
+       struct dentry *d_parent;        /* parent directory */
+       struct qstr d_name;
++#ifdef CONFIG_UNION_MOUNT
++      /*
++       * The following fields are used by the VFS based union mount
++       * implementation. Both are protected by union_lock!
++       */
++      struct list_head d_unions;      /* list of union_mount's */
++      unsigned int d_unionized;       /* unions referencing this dentry */
++#endif
++
+       struct list_head d_lru;         /* LRU list */
+       /*
+        * d_child and d_rcu can share memory
+@@ -186,6 +195,9 @@
+ #define DCACHE_FSNOTIFY_PARENT_WATCHED        0x0080 /* Parent inode is watched by some fsnotify listener */
++#define DCACHE_WHITEOUT               0x0100  /* This negative dentry is a whiteout */
++#define DCACHE_FALLTHRU               0x0200  /* Keep looking in the file system below */
++
+ extern spinlock_t dcache_lock;
+ extern seqlock_t rename_lock;
+@@ -205,12 +217,20 @@
+  * __d_drop requires dentry->d_lock.
+  */
++#ifdef CONFIG_UNION_MOUNT
++extern void __d_drop_unions(struct dentry *);
++#endif
++
+ static inline void __d_drop(struct dentry *dentry)
+ {
+       if (!(dentry->d_flags & DCACHE_UNHASHED)) {
+               dentry->d_flags |= DCACHE_UNHASHED;
+               hlist_del_rcu(&dentry->d_hash);
+       }
++#ifdef CONFIG_UNION_MOUNT
++      /* remove dentry from the union hashtable */
++      __d_drop_unions(dentry);
++#endif
+ }
+ static inline void d_drop(struct dentry *dentry)
+@@ -358,6 +378,16 @@
+       return d_unhashed(dentry) && !IS_ROOT(dentry);
+ }
++static inline int d_is_whiteout(struct dentry *dentry)
++{
++      return (dentry->d_flags & DCACHE_WHITEOUT);
++}
++
++static inline int d_is_fallthru(struct dentry *dentry)
++{
++      return (dentry->d_flags & DCACHE_FALLTHRU);
++}
++
+ static inline struct dentry *dget_parent(struct dentry *dentry)
+ {
+       struct dentry *ret;
+--- a/include/linux/ext2_fs.h
++++ b/include/linux/ext2_fs.h
+@@ -189,6 +189,7 @@
+ #define EXT2_NOTAIL_FL                        FS_NOTAIL_FL    /* file tail should not be merged */
+ #define EXT2_DIRSYNC_FL                       FS_DIRSYNC_FL   /* dirsync behaviour (directories only) */
+ #define EXT2_TOPDIR_FL                        FS_TOPDIR_FL    /* Top of directory hierarchies*/
++#define EXT2_OPAQUE_FL                        0x00040000
+ #define EXT2_RESERVED_FL              FS_RESERVED_FL  /* reserved for ext2 lib */
+ #define EXT2_FL_USER_VISIBLE          FS_FL_USER_VISIBLE      /* User visible flags */
+@@ -503,10 +504,12 @@
+ #define EXT3_FEATURE_INCOMPAT_RECOVER         0x0004
+ #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV     0x0008
+ #define EXT2_FEATURE_INCOMPAT_META_BG         0x0010
++#define EXT2_FEATURE_INCOMPAT_WHITEOUT                0x0020
+ #define EXT2_FEATURE_INCOMPAT_ANY             0xffffffff
+ #define EXT2_FEATURE_COMPAT_SUPP      EXT2_FEATURE_COMPAT_EXT_ATTR
+ #define EXT2_FEATURE_INCOMPAT_SUPP    (EXT2_FEATURE_INCOMPAT_FILETYPE| \
++                                       EXT2_FEATURE_INCOMPAT_WHITEOUT| \
+                                        EXT2_FEATURE_INCOMPAT_META_BG)
+ #define EXT2_FEATURE_RO_COMPAT_SUPP   (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+                                        EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
+@@ -573,6 +576,8 @@
+       EXT2_FT_FIFO,
+       EXT2_FT_SOCK,
+       EXT2_FT_SYMLINK,
++      EXT2_FT_WHT,
++      EXT2_FT_FALLTHRU,
+       EXT2_FT_MAX
+ };
+--- a/include/linux/fs.h
++++ b/include/linux/fs.h
+@@ -188,6 +188,7 @@
+ #define MS_REMOUNT    32      /* Alter flags of a mounted FS */
+ #define MS_MANDLOCK   64      /* Allow mandatory locks on an FS */
+ #define MS_DIRSYNC    128     /* Directory modifications are synchronous */
++#define MS_UNION      256
+ #define MS_NOATIME    1024    /* Do not update access times. */
+ #define MS_NODIRATIME 2048    /* Do not update directory access times */
+ #define MS_BIND               4096
+@@ -205,6 +206,7 @@
+ #define MS_KERNMOUNT  (1<<22) /* this is a kern_mount call */
+ #define MS_I_VERSION  (1<<23) /* Update inode I_version field */
+ #define MS_STRICTATIME        (1<<24) /* Always perform atime updates */
++#define MS_WHITEOUT   (1<<26) /* fs does support white-out filetype */
+ #define MS_ACTIVE     (1<<30)
+ #define MS_NOUSER     (1<<31)
+@@ -231,6 +233,7 @@
+ #define S_NOCMTIME    128     /* Do not update file c/mtime */
+ #define S_SWAPFILE    256     /* Do not truncate: swapon got its bmaps */
+ #define S_PRIVATE     512     /* Inode is fs-internal */
++#define S_OPAQUE      1024    /* Directory is opaque */
+ /*
+  * Note that nosuid etc flags are inode-specific: setting some file-system
+@@ -266,6 +269,8 @@
+ #define IS_SWAPFILE(inode)    ((inode)->i_flags & S_SWAPFILE)
+ #define IS_PRIVATE(inode)     ((inode)->i_flags & S_PRIVATE)
++#define IS_OPAQUE(inode)      ((inode)->i_flags & S_OPAQUE)
++
+ /* the read-only stuff doesn't really belong here, but any other place is
+    probably as bad and I don't want to create yet another include file. */
+@@ -1379,6 +1384,11 @@
+        * generic_show_options()
+        */
+       char *s_options;
++
++      /*
++       * Users who require read-only access - e.g., union mounts
++       */
++      int s_readonly_users;
+ };
+ extern struct timespec current_fs_time(struct super_block *sb);
+@@ -1521,6 +1531,8 @@
+       int (*mkdir) (struct inode *,struct dentry *,int);
+       int (*rmdir) (struct inode *,struct dentry *);
+       int (*mknod) (struct inode *,struct dentry *,int,dev_t);
++      int (*whiteout) (struct inode *, struct dentry *, struct dentry *);
++      int (*fallthru) (struct inode *, struct dentry *);
+       int (*rename) (struct inode *, struct dentry *,
+                       struct inode *, struct dentry *);
+       int (*readlink) (struct dentry *, char __user *,int);
+@@ -2094,6 +2106,7 @@
+ extern sector_t bmap(struct inode *, sector_t);
+ #endif
+ extern int notify_change(struct dentry *, struct iattr *);
++extern int __inode_permission(struct inode *inode, int mask, int rofs);
+ extern int inode_permission(struct inode *, int);
+ extern int generic_permission(struct inode *, int,
+               int (*check_acl)(struct inode *, int));
+@@ -2121,7 +2134,7 @@
+ extern struct file *do_filp_open(int dfd, const char *pathname,
+               int open_flag, int mode, int acc_mode);
+-extern int may_open(struct path *, int, int);
++extern int may_open(struct nameidata *, int, int);
+ extern int kernel_read(struct file *, loff_t, char *, unsigned long);
+ extern struct file * open_exec(const char *);
+--- a/include/linux/mount.h
++++ b/include/linux/mount.h
+@@ -35,6 +35,7 @@
+ #define MNT_SHARED    0x1000  /* if the vfsmount is a shared mount */
+ #define MNT_UNBINDABLE        0x2000  /* if the vfsmount is a unbindable mount */
+ #define MNT_PNODE_MASK        0x3000  /* propagation flag mask */
++#define MNT_UNION     0x4000  /* if the vfsmount is a union mount */
+ struct vfsmount {
+       struct list_head mnt_hash;
+@@ -53,6 +54,9 @@
+       struct list_head mnt_slave_list;/* list of slave mounts */
+       struct list_head mnt_slave;     /* slave list entry */
+       struct vfsmount *mnt_master;    /* slave is on master->mnt_slave_list */
++#ifdef CONFIG_UNION_MOUNT
++      struct list_head mnt_unions;    /* list of union_mount structures */
++#endif
+       struct mnt_namespace *mnt_ns;   /* containing namespace */
+       int mnt_id;                     /* mount identifier */
+       int mnt_group_id;               /* peer group identifier */
+--- a/include/linux/namei.h
++++ b/include/linux/namei.h
+@@ -20,6 +20,7 @@
+       struct qstr     last;
+       struct path     root;
+       unsigned int    flags;
++      unsigned int    um_flags;
+       int             last_type;
+       unsigned        depth;
+       char *saved_names[MAX_NESTED_LINKS + 1];
+@@ -35,6 +36,9 @@
+  */
+ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
++#define LAST_UNION             0x01
++#define LAST_LOWLEVEL          0x02
++
+ /*
+  * The bitmask for a lookup event:
+  *  - follow links at the end
+@@ -49,6 +53,8 @@
+ #define LOOKUP_CONTINUE                4
+ #define LOOKUP_PARENT         16
+ #define LOOKUP_REVAL          64
++#define LOOKUP_TOPMOST               128
++
+ /*
+  * Intent data
+  */
+--- /dev/null
++++ b/include/linux/union.h
+@@ -0,0 +1,84 @@
++/*
++ * VFS based union mount for Linux
++ *
++ * Copyright (C) 2004-2007 IBM Corporation, IBM Deutschland Entwicklung GmbH.
++ * Copyright (C) 2007 Novell Inc.
++ *   Author(s): Jan Blunck (j.blunck@tu-harburg.de)
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the Free
++ * Software Foundation; either version 2 of the License, or (at your option)
++ * any later version.
++ *
++ */
++#ifndef __LINUX_UNION_H
++#define __LINUX_UNION_H
++#ifdef __KERNEL__
++
++#include <linux/list.h>
++#include <asm/atomic.h>
++
++struct dentry;
++struct vfsmount;
++
++#ifdef CONFIG_UNION_MOUNT
++
++/*
++ * The new union mount structure.
++ */
++struct union_mount {
++      atomic_t u_count;               /* reference count */
++      struct mutex u_mutex;
++      struct list_head u_unions;      /* list head for d_unions */
++      struct list_head u_list;        /* list head for mnt_unions */
++      struct hlist_node u_hash;       /* list head for seaching */
++      struct hlist_node u_rhash;      /* list head for reverse seaching */
++
++      struct path u_this;             /* this is me */
++      struct path u_next;             /* this is what I overlay */
++};
++
++#define IS_UNION(dentry)      (!list_empty(&(dentry)->d_unions) || \
++                               (dentry)->d_unionized)
++#define IS_MNT_UNION(mnt)     ((mnt)->mnt_flags & MNT_UNION)
++
++extern int is_unionized(struct dentry *, struct vfsmount *);
++extern int append_to_union(struct vfsmount *, struct dentry *,
++                         struct vfsmount *, struct dentry *);
++extern int follow_union_down(struct path *);
++extern int follow_union_mount(struct path *);
++extern void __d_drop_unions(struct dentry *);
++extern void shrink_d_unions(struct dentry *);
++extern void __shrink_d_unions(struct dentry *, struct list_head *);
++extern int attach_mnt_union(struct vfsmount *, struct vfsmount *,
++                          struct dentry *);
++extern void detach_mnt_union(struct vfsmount *);
++extern struct dentry *union_create_topmost(struct nameidata *, struct qstr *,
++                                         struct path *);
++extern int __union_copyup(struct path *, struct nameidata *, struct path *);
++extern int union_copyup(struct nameidata *, int);
++extern int union_copyup_dir(struct path *path);
++extern int union_permission(struct path *, int);
++
++#else /* CONFIG_UNION_MOUNT */
++
++#define IS_UNION(x)                   (0)
++#define IS_MNT_UNION(x)                       (0)
++#define is_unionized(x, y)            (0)
++#define append_to_union(x1, y1, x2, y2)       ({ BUG(); (0); })
++#define follow_union_down(x, y)               ({ (0); })
++#define follow_union_mount(x, y)      ({ (0); })
++#define __d_drop_unions(x)            do { } while (0)
++#define shrink_d_unions(x)            do { } while (0)
++#define __shrink_d_unions(x,y)                do { } while (0)
++#define attach_mnt_union(x, y, z)     do { } while (0)
++#define detach_mnt_union(x)           do { } while (0)
++#define union_create_topmost(x, y, z) ({ BUG(); (NULL); })
++#define __union_copyup(x, y, z)               ({ BUG(); (0); })
++#define union_copyup(x, y)            ({ (0); })
++#define union_copyup_dir(x)           ({ BUG(); (0); })
++#define union_permission(x, y)                inode_permission(x->dentry->d_inode, y)
++
++#endif        /* CONFIG_UNION_MOUNT */
++#endif        /* __KERNEL__ */
++#endif        /* __LINUX_UNION_H */
+--- a/mm/shmem.c
++++ b/mm/shmem.c
+@@ -1794,6 +1794,118 @@
+       return 0;
+ }
++static int shmem_rmdir(struct inode *dir, struct dentry *dentry);
++static int shmem_unlink(struct inode *dir, struct dentry *dentry);
++
++/*
++ * Create a dentry to signify a whiteout.
++ */
++static int shmem_whiteout(struct inode *dir, struct dentry *old_dentry,
++                        struct dentry *new_dentry)
++{
++      struct shmem_sb_info *sbinfo = SHMEM_SB(dir->i_sb);
++      struct dentry *dentry;
++
++      if (!(dir->i_sb->s_flags & MS_WHITEOUT))
++              return -EPERM;
++
++      /* This gives us a proper initialized negative dentry */
++      dentry = simple_lookup(dir, new_dentry, NULL);
++      if (dentry && IS_ERR(dentry))
++              return PTR_ERR(dentry);
++
++      /*
++       * No ordinary (disk based) filesystem counts whiteouts as inodes;
++       * but each new link needs a new dentry, pinning lowmem, and
++       * tmpfs dentries cannot be pruned until they are unlinked.
++       */
++      if (sbinfo->max_inodes) {
++              spin_lock(&sbinfo->stat_lock);
++              if (!sbinfo->free_inodes) {
++                      spin_unlock(&sbinfo->stat_lock);
++                      return -ENOSPC;
++              }
++              sbinfo->free_inodes--;
++              spin_unlock(&sbinfo->stat_lock);
++      }
++
++      if (old_dentry->d_inode || d_is_fallthru(old_dentry)) {
++              if (old_dentry->d_inode && S_ISDIR(old_dentry->d_inode->i_mode))
++                      shmem_rmdir(dir, old_dentry);
++              else
++                      shmem_unlink(dir, old_dentry);
++      }
++
++      dir->i_size += BOGO_DIRENT_SIZE;
++      dir->i_ctime = dir->i_mtime = CURRENT_TIME;
++      /* Extra pinning count for the created dentry */
++      dget(new_dentry);
++      spin_lock(&new_dentry->d_lock);
++      new_dentry->d_flags |= DCACHE_WHITEOUT;
++      spin_unlock(&new_dentry->d_lock);
++      return 0;
++}
++
++static void shmem_d_instantiate(struct inode *dir, struct dentry *dentry,
++                              struct inode *inode);
++
++/*
++ * Create a dentry to signify a fallthru.  A fallthru lets us read the
++ * low-level dentries into the dcache once on the first readdir() and
++ * then
++ */
++static int shmem_fallthru(struct inode *dir, struct dentry *dentry)
++{
++      struct shmem_sb_info *sbinfo = SHMEM_SB(dir->i_sb);
++
++      /* FIXME: this is stupid */
++      if (!(dir->i_sb->s_flags & MS_WHITEOUT))
++              return -EPERM;
++
++      if (dentry->d_inode || d_is_fallthru(dentry) || d_is_whiteout(dentry))
++              return -EEXIST;
++
++      /*
++       * Each new link needs a new dentry, pinning lowmem, and tmpfs
++       * dentries cannot be pruned until they are unlinked.
++       */
++      if (sbinfo->max_inodes) {
++              spin_lock(&sbinfo->stat_lock);
++              if (!sbinfo->free_inodes) {
++                      spin_unlock(&sbinfo->stat_lock);
++                      return -ENOSPC;
++              }
++              sbinfo->free_inodes--;
++              spin_unlock(&sbinfo->stat_lock);
++      }
++
++      shmem_d_instantiate(dir, dentry, NULL);
++      dir->i_ctime = dir->i_mtime = CURRENT_TIME;
++
++      spin_lock(&dentry->d_lock);
++      dentry->d_flags |= DCACHE_FALLTHRU;
++      spin_unlock(&dentry->d_lock);
++      return 0;
++}
++
++static void shmem_d_instantiate(struct inode *dir, struct dentry *dentry,
++                              struct inode *inode)
++{
++      if (d_is_whiteout(dentry)) {
++              /* Re-using an existing whiteout */
++              shmem_free_inode(dir->i_sb);
++              if (S_ISDIR(inode->i_mode))
++                      inode->i_mode |= S_OPAQUE;
++      } else if (d_is_fallthru(dentry)) {
++              shmem_free_inode(dir->i_sb);
++      } else {
++              /* New dentry */
++              dir->i_size += BOGO_DIRENT_SIZE;
++              dget(dentry); /* Extra count - pin the dentry in core */
++      }
++      /* Will clear DCACHE_WHITEOUT and DCACHE_FALLTHRU flags */
++      d_instantiate(dentry, inode);
++}
+ /*
+  * File creation. Allocate an inode, and we're done..
+  */
+@@ -1818,15 +1930,16 @@
+                       iput(inode);
+                       return error;
+               }
++
+               if (dir->i_mode & S_ISGID) {
+                       inode->i_gid = dir->i_gid;
+                       if (S_ISDIR(mode))
+                               inode->i_mode |= S_ISGID;
+               }
+-              dir->i_size += BOGO_DIRENT_SIZE;
++
++              shmem_d_instantiate(dir, dentry, inode);
++
+               dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+-              d_instantiate(dentry, inode);
+-              dget(dentry); /* Extra count - pin the dentry in core */
+       }
+       return error;
+ }
+@@ -1864,12 +1977,11 @@
+       if (ret)
+               goto out;
+-      dir->i_size += BOGO_DIRENT_SIZE;
++      shmem_d_instantiate(dir, dentry, inode);
++
+       inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+       inc_nlink(inode);
+       atomic_inc(&inode->i_count);    /* New dentry reference */
+-      dget(dentry);           /* Extra pinning count for the created dentry */
+-      d_instantiate(dentry, inode);
+ out:
+       return ret;
+ }
+@@ -1878,21 +1990,63 @@
+ {
+       struct inode *inode = dentry->d_inode;
+-      if (inode->i_nlink > 1 && !S_ISDIR(inode->i_mode))
+-              shmem_free_inode(inode->i_sb);
++      if (d_is_whiteout(dentry) || d_is_fallthru(dentry) ||
++          (inode->i_nlink > 1 && !S_ISDIR(inode->i_mode)))
++              shmem_free_inode(dir->i_sb);
++      if (inode) {
++              inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
++              drop_nlink(inode);
++      }
+       dir->i_size -= BOGO_DIRENT_SIZE;
+-      inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+-      drop_nlink(inode);
+       dput(dentry);   /* Undo the count from "create" - this does all the work */
+       return 0;
+ }
++static void shmem_dir_unlink_whiteouts(struct inode *dir, struct dentry *dentry)
++{
++      if (!dentry->d_inode)
++              return;
++
++      /* Remove whiteouts from logical empty directory */
++      if (S_ISDIR(dentry->d_inode->i_mode) &&
++          dentry->d_inode->i_sb->s_flags & MS_WHITEOUT) {
++              struct dentry *child, *next;
++              LIST_HEAD(list);
++
++              spin_lock(&dcache_lock);
++              list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) {
++                      spin_lock(&child->d_lock);
++                      /* Unlink fallthrus too */
++                      if (d_is_whiteout(child) || d_is_fallthru(child)) {
++                              __d_drop(child);
++                              if (!list_empty(&child->d_lru)) {
++                                      list_del(&child->d_lru);
++                                      dentry_stat.nr_unused--;
++                              }
++                              list_add(&child->d_lru, &list);
++                      }
++                      spin_unlock(&child->d_lock);
++              }
++              spin_unlock(&dcache_lock);
++
++              list_for_each_entry_safe(child, next, &list, d_lru) {
++                      spin_lock(&child->d_lock);
++                      list_del_init(&child->d_lru);
++                      spin_unlock(&child->d_lock);
++
++                      shmem_unlink(dentry->d_inode, child);
++              }
++      }
++}
++
+ static int shmem_rmdir(struct inode *dir, struct dentry *dentry)
+ {
+       if (!simple_empty(dentry))
+               return -ENOTEMPTY;
++      /* Remove whiteouts from logical empty directory */
++      shmem_dir_unlink_whiteouts(dir, dentry);
+       drop_nlink(dentry->d_inode);
+       drop_nlink(dir);
+       return shmem_unlink(dir, dentry);
+@@ -1901,7 +2055,7 @@
+ /*
+  * The VFS layer already does all the dentry stuff for rename,
+  * we just have to decrement the usage count for the target if
+- * it exists so that the VFS layer correctly free's it when it
++ * it exists so that the VFS layer correctly frees it when it
+  * gets overwritten.
+  */
+ static int shmem_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry)
+@@ -1912,7 +2066,12 @@
+       if (!simple_empty(new_dentry))
+               return -ENOTEMPTY;
++      if (d_is_whiteout(new_dentry))
++              shmem_unlink(new_dir, new_dentry);
++
+       if (new_dentry->d_inode) {
++              /* Remove whiteouts from logical empty directory */
++              shmem_dir_unlink_whiteouts(new_dir, new_dentry);
+               (void) shmem_unlink(new_dir, new_dentry);
+               if (they_are_dirs)
+                       drop_nlink(old_dir);
+@@ -1977,12 +2136,12 @@
+               set_page_dirty(page);
+               page_cache_release(page);
+       }
++
++      shmem_d_instantiate(dir, dentry, inode);
++
+       if (dir->i_mode & S_ISGID)
+               inode->i_gid = dir->i_gid;
+-      dir->i_size += BOGO_DIRENT_SIZE;
+       dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+-      d_instantiate(dentry, inode);
+-      dget(dentry);
+       return 0;
+ }
+@@ -2363,6 +2522,12 @@
+       if (!root)
+               goto failed_iput;
+       sb->s_root = root;
++
++#ifdef CONFIG_TMPFS
++      if (!(sb->s_flags & MS_NOUSER))
++              sb->s_flags |= MS_WHITEOUT;
++#endif
++
+       return 0;
+ failed_iput:
+@@ -2462,6 +2627,8 @@
+       .rmdir          = shmem_rmdir,
+       .mknod          = shmem_mknod,
+       .rename         = shmem_rename,
++      .whiteout       = shmem_whiteout,
++      .fallthru       = shmem_fallthru,
+ #endif
+ #ifdef CONFIG_TMPFS_POSIX_ACL
+       .setattr        = shmem_notify_change,
diff --git a/target/linux/generic-2.6/patches-2.6.31/231-union_mounts_bind_fix.patch b/target/linux/generic-2.6/patches-2.6.31/231-union_mounts_bind_fix.patch
new file mode 100644 (file)
index 0000000..9a7cae6
--- /dev/null
@@ -0,0 +1,11 @@
+--- a/fs/namespace.c
++++ b/fs/namespace.c
+@@ -1550,7 +1550,7 @@
+       if (!mnt)
+               goto out;
+-      err = check_union_mnt(&old_path, mnt, mnt_flags);
++      err = check_union_mnt(path, mnt, mnt_flags);
+       if (err)
+               goto out;