]> git.pld-linux.org Git - packages/kernel.git/commitdiff
- updated for 2.6.27.
authorPaweł Sikora <pluto@pld-linux.org>
Sun, 19 Oct 2008 17:11:24 +0000 (17:11 +0000)
committercvs2git <feedback@pld-linux.org>
Sun, 24 Jun 2012 12:13:13 +0000 (12:13 +0000)
Changed files:
    kernel-unionfs.patch -> 1.1.2.3

kernel-unionfs.patch

index 8780b17372a994075fc59be9833829fafffab6f8..f467ffe3af1ceefec2311fcbfbb43e9ba4cbf1d1 100644 (file)
@@ -532,10 +532,10 @@ index 0000000..1adde69
 +
 +For more information, see <http://unionfs.filesystems.org/>.
 diff --git a/MAINTAINERS b/MAINTAINERS
-index 56a2f67..26a2176 100644
+index 0a613cb..d811b00 100644
 --- a/MAINTAINERS
 +++ b/MAINTAINERS
-@@ -4093,6 +4093,14 @@ L:      linux-kernel@vger.kernel.org
+@@ -4144,6 +4144,14 @@ L:      linux-kernel@vger.kernel.org
  W:    http://www.kernel.dk
  S:    Maintained
  
@@ -551,10 +551,10 @@ index 56a2f67..26a2176 100644
  P:    Oliver Neukum
  M:    oliver@neukum.name
 diff --git a/fs/Kconfig b/fs/Kconfig
-index 2694648..ee32927 100644
+index abccb5d..3d9310a 100644
 --- a/fs/Kconfig
 +++ b/fs/Kconfig
-@@ -1031,6 +1031,47 @@ config CONFIGFS_FS
+@@ -981,6 +981,47 @@ config CONFIGFS_FS
  
  endmenu
  
@@ -602,7 +602,7 @@ index 2694648..ee32927 100644
  menu "Miscellaneous filesystems"
  
  config ADFS_FS
-@@ -1083,18 +1124,6 @@ config AFFS_FS
+@@ -1033,18 +1074,6 @@ config AFFS_FS
          To compile this file system support as a module, choose M here: the
          module will be called affs.  If unsure, say N.
  
@@ -622,10 +622,10 @@ index 2694648..ee32927 100644
        tristate "Apple Macintosh file system support (EXPERIMENTAL)"
        depends on BLOCK && EXPERIMENTAL
 diff --git a/fs/Makefile b/fs/Makefile
-index 1e7a11b..dadf53b 100644
+index a1482a5..9bf3915 100644
 --- a/fs/Makefile
 +++ b/fs/Makefile
-@@ -85,6 +85,7 @@ obj-$(CONFIG_ISO9660_FS)     += isofs/
+@@ -86,6 +86,7 @@ obj-$(CONFIG_ISO9660_FS)     += isofs/
  obj-$(CONFIG_HFSPLUS_FS)      += hfsplus/ # Before hfs to find wrapped HFS+
  obj-$(CONFIG_HFS_FS)          += hfs/
  obj-$(CONFIG_ECRYPT_FS)               += ecryptfs/
@@ -647,10 +647,10 @@ index 5e59658..4621f89 100644
  out:
        return rc;
 diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c
-index c92cc1c..71fcaea 100644
+index 89209f0..d99a83e 100644
 --- a/fs/ecryptfs/inode.c
 +++ b/fs/ecryptfs/inode.c
-@@ -570,9 +570,9 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+@@ -589,9 +589,9 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                        lower_new_dir_dentry->d_inode, lower_new_dentry);
        if (rc)
                goto out_lock;
@@ -662,7 +662,7 @@ index c92cc1c..71fcaea 100644
  out_lock:
        unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
        dput(lower_new_dentry->d_parent);
-@@ -907,7 +907,7 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
+@@ -913,7 +913,7 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
        rc = notify_change(lower_dentry, ia);
        mutex_unlock(&lower_dentry->d_inode->i_mutex);
  out:
@@ -672,10 +672,10 @@ index c92cc1c..71fcaea 100644
  }
  
 diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c
-index d603631..59daba3 100644
+index 448dfd5..db2db5d 100644
 --- a/fs/ecryptfs/main.c
 +++ b/fs/ecryptfs/main.c
-@@ -211,7 +211,7 @@ int ecryptfs_interpose(struct dentry *lower_dentry, struct dentry *dentry,
+@@ -197,7 +197,7 @@ int ecryptfs_interpose(struct dentry *lower_dentry, struct dentry *dentry,
                d_add(dentry, inode);
        else
                d_instantiate(dentry, inode);
@@ -685,10 +685,10 @@ index d603631..59daba3 100644
         * other metadata */
        fsstack_copy_inode_size(inode, lower_inode);
 diff --git a/fs/namei.c b/fs/namei.c
-index 01e67dd..b51fde0 100644
+index 4ea63ed..3c8e0d6 100644
 --- a/fs/namei.c
 +++ b/fs/namei.c
-@@ -404,6 +404,7 @@ void release_open_intent(struct nameidata *nd)
+@@ -392,6 +392,7 @@ void release_open_intent(struct nameidata *nd)
        else
                fput(nd->intent.open.file);
  }
@@ -697,10 +697,10 @@ index 01e67dd..b51fde0 100644
  static inline struct dentry *
  do_revalidate(struct dentry *dentry, struct nameidata *nd)
 diff --git a/fs/splice.c b/fs/splice.c
-index aa5f6f6..4282fdf 100644
+index 1bbc6f4..7de91ce 100644
 --- a/fs/splice.c
 +++ b/fs/splice.c
-@@ -878,8 +878,8 @@ EXPORT_SYMBOL(generic_splice_sendpage);
+@@ -887,8 +887,8 @@ EXPORT_SYMBOL(generic_splice_sendpage);
  /*
   * Attempt to initiate a splice from pipe to file.
   */
@@ -711,7 +711,7 @@ index aa5f6f6..4282fdf 100644
  {
        int ret;
  
-@@ -895,13 +895,14 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
+@@ -904,13 +904,14 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
  
        return out->f_op->splice_write(pipe, out, ppos, len, flags);
  }
@@ -729,7 +729,7 @@ index aa5f6f6..4282fdf 100644
  {
        int ret;
  
-@@ -917,6 +918,7 @@ static long do_splice_to(struct file *in, loff_t *ppos,
+@@ -926,6 +927,7 @@ static long do_splice_to(struct file *in, loff_t *ppos,
  
        return in->f_op->splice_read(in, ppos, pipe, len, flags);
  }
@@ -737,7 +737,7 @@ index aa5f6f6..4282fdf 100644
  
  /**
   * splice_direct_to_actor - splices data directly between two non-pipes
-@@ -986,7 +988,7 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd,
+@@ -995,7 +997,7 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd,
                size_t read_len;
                loff_t pos = sd->pos, prev_pos = pos;
  
@@ -746,7 +746,7 @@ index aa5f6f6..4282fdf 100644
                if (unlikely(ret <= 0))
                        goto out_release;
  
-@@ -1045,7 +1047,7 @@ static int direct_splice_actor(struct pipe_inode_info *pipe,
+@@ -1054,7 +1056,7 @@ static int direct_splice_actor(struct pipe_inode_info *pipe,
  {
        struct file *file = sd->u.file;
  
@@ -755,7 +755,7 @@ index aa5f6f6..4282fdf 100644
  }
  
  /**
-@@ -1119,7 +1121,7 @@ static long do_splice(struct file *in, loff_t __user *off_in,
+@@ -1128,7 +1130,7 @@ static long do_splice(struct file *in, loff_t __user *off_in,
                } else
                        off = &out->f_pos;
  
@@ -764,7 +764,7 @@ index aa5f6f6..4282fdf 100644
  
                if (off_out && copy_to_user(off_out, off, sizeof(loff_t)))
                        ret = -EFAULT;
-@@ -1140,7 +1142,7 @@ static long do_splice(struct file *in, loff_t __user *off_in,
+@@ -1149,7 +1151,7 @@ static long do_splice(struct file *in, loff_t __user *off_in,
                } else
                        off = &in->f_pos;
  
@@ -885,11 +885,11 @@ index 67716f6..a66ff6c 100644
  EXPORT_SYMBOL_GPL(fsstack_copy_attr_all);
 diff --git a/fs/unionfs/Makefile b/fs/unionfs/Makefile
 new file mode 100644
-index 0000000..6614204
+index 0000000..fc98b38
 --- /dev/null
 +++ b/fs/unionfs/Makefile
 @@ -0,0 +1,17 @@
-+UNIONFS_VERSION="2.4 (for 2.6.26)"
++UNIONFS_VERSION="2.5 (for 2.6.27-rc6)"
 +
 +EXTRA_CFLAGS += -DUNIONFS_VERSION=\"$(UNIONFS_VERSION)\"
 +
@@ -908,10 +908,10 @@ index 0000000..6614204
 +endif
 diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c
 new file mode 100644
-index 0000000..5861970
+index 0000000..ed3604e
 --- /dev/null
 +++ b/fs/unionfs/commonfops.c
-@@ -0,0 +1,905 @@
+@@ -0,0 +1,879 @@
 +/*
 + * Copyright (c) 2003-2008 Erez Zadok
 + * Copyright (c) 2003-2006 Charles P. Wright
@@ -938,7 +938,7 @@ index 0000000..5861970
 + * stolen from NFS's silly rename
 + */
 +static int copyup_deleted_file(struct file *file, struct dentry *dentry,
-+                             int bstart, int bindex)
++                             struct dentry *parent, int bstart, int bindex)
 +{
 +      static unsigned int counter;
 +      const int i_inosize = sizeof(dentry->d_inode->i_ino) * 2;
@@ -985,8 +985,7 @@ index 0000000..5861970
 +      } while (tmp_dentry->d_inode != NULL);  /* need negative dentry */
 +      dput(tmp_dentry);
 +
-+      err = copyup_named_file(dentry->d_parent->d_inode, file, name, bstart,
-+                              bindex,
++      err = copyup_named_file(parent->d_inode, file, name, bstart, bindex,
 +                              i_size_read(file->f_path.dentry->d_inode));
 +      if (err) {
 +              if (unlikely(err == -EEXIST))
@@ -1095,6 +1094,7 @@ index 0000000..5861970
 +                                  unionfs_lower_mnt_idx(dentry, bindex),
 +                                  file->f_flags);
 +              if (IS_ERR(lower_file)) {
++                      branchput(sb, bindex);
 +                      err = PTR_ERR(lower_file);
 +                      goto out;
 +              } else {
@@ -1112,7 +1112,8 @@ index 0000000..5861970
 +      struct file *lower_file;
 +      struct dentry *lower_dentry;
 +      struct dentry *dentry = file->f_path.dentry;
-+      struct inode *parent_inode = dentry->d_parent->d_inode;
++      struct dentry *parent = dget_parent(dentry);
++      struct inode *parent_inode = parent->d_inode;
 +      struct super_block *sb = dentry->d_sb;
 +
 +      bstart = dbstart(dentry);
@@ -1148,15 +1149,16 @@ index 0000000..5861970
 +
 +      memcpy(&lower_file->f_ra, &file->f_ra, sizeof(struct file_ra_state));
 +out:
++      dput(parent);
 +      return err;
 +}
 +
 +/* perform a delayed copyup of a read-write file on a read-only branch */
-+static int do_delayed_copyup(struct file *file)
++static int do_delayed_copyup(struct file *file, struct dentry *parent)
 +{
 +      int bindex, bstart, bend, err = 0;
 +      struct dentry *dentry = file->f_path.dentry;
-+      struct inode *parent_inode = dentry->d_parent->d_inode;
++      struct inode *parent_inode = parent->d_inode;
 +
 +      bstart = fbstart(file);
 +      bend = fbend(file);
@@ -1170,8 +1172,8 @@ index 0000000..5861970
 +                                        bindex,
 +                                        i_size_read(dentry->d_inode));
 +              else
-+                      err = copyup_deleted_file(file, dentry, bstart,
-+                                                bindex);
++                      err = copyup_deleted_file(file, dentry, parent,
++                                                bstart, bindex);
 +              /* if succeeded, set lower open-file flags and break */
 +              if (!err) {
 +                      struct file *lower_file;
@@ -1207,6 +1209,7 @@ index 0000000..5861970
 + * Expects dentry/parent to be locked already, and revalidated.
 + */
 +static int __unionfs_file_revalidate(struct file *file, struct dentry *dentry,
++                                   struct dentry *parent,
 +                                   struct super_block *sb, int sbgen,
 +                                   int dgen, bool willwrite)
 +{
@@ -1288,7 +1291,7 @@ index 0000000..5861970
 +          is_robranch(dentry)) {
 +              pr_debug("unionfs: do delay copyup of \"%s\"\n",
 +                       dentry->d_name.name);
-+              err = do_delayed_copyup(file);
++              err = do_delayed_copyup(file, parent);
 +              /* regular files have only one open lower file */
 +              if (!err && !S_ISDIR(dentry->d_inode->i_mode))
 +                      fbend(file) = fbstart(file);
@@ -1298,8 +1301,6 @@ index 0000000..5861970
 +      if (err) {
 +              kfree(UNIONFS_F(file)->lower_files);
 +              kfree(UNIONFS_F(file)->saved_branch_ids);
-+      } else {
-+              unionfs_check_file(file);
 +      }
 +      return err;
 +}
@@ -1307,10 +1308,12 @@ index 0000000..5861970
 +/*
 + * Revalidate the struct file
 + * @file: file to revalidate
++ * @parent: parent dentry (locked by caller)
 + * @willwrite: true if caller may cause changes to the file; false otherwise.
 + * Caller must lock/unlock dentry's branch configuration.
 + */
-+int unionfs_file_revalidate(struct file *file, bool willwrite)
++int unionfs_file_revalidate(struct file *file, struct dentry *parent,
++                          bool willwrite)
 +{
 +      struct super_block *sb;
 +      struct dentry *dentry;
@@ -1320,14 +1323,14 @@ index 0000000..5861970
 +      dentry = file->f_path.dentry;
 +      sb = dentry->d_sb;
 +      verify_locked(dentry);
++      verify_locked(parent);
 +
 +      /*
 +       * First revalidate the dentry inside struct file,
 +       * but not unhashed dentries.
 +       */
-+reval_dentry:
 +      if (!d_deleted(dentry) &&
-+          !__unionfs_d_revalidate_chain(dentry, NULL, willwrite)) {
++          !__unionfs_d_revalidate(dentry, parent, willwrite)) {
 +              err = -ESTALE;
 +              goto out;
 +      }
@@ -1335,59 +1338,14 @@ index 0000000..5861970
 +      sbgen = atomic_read(&UNIONFS_SB(sb)->generation);
 +      dgen = atomic_read(&UNIONFS_D(dentry)->generation);
 +
-+      if (unlikely(sbgen > dgen)) {
-+              pr_debug("unionfs: retry dentry %s revalidation\n",
++      if (unlikely(sbgen > dgen)) { /* XXX: should never happen */
++              pr_debug("unionfs: failed to revalidate dentry (%s)\n",
 +                       dentry->d_name.name);
-+              schedule();
-+              goto reval_dentry;
-+      }
-+      BUG_ON(sbgen > dgen);
-+
-+      err = __unionfs_file_revalidate(file, dentry, sb,
-+                                      sbgen, dgen, willwrite);
-+out:
-+      return err;
-+}
-+
-+/* same as unionfs_file_revalidate, but parent dentry must be locked too */
-+int unionfs_file_revalidate_locked(struct file *file, bool willwrite)
-+{
-+      struct super_block *sb;
-+      struct dentry *dentry;
-+      int sbgen, dgen;
-+      int err = 0, valid;
-+
-+      dentry = file->f_path.dentry;
-+      sb = dentry->d_sb;
-+      verify_locked(dentry);
-+      verify_locked(dentry->d_parent);
-+
-+      /* first revalidate (locked) parent, then child */
-+      valid = __unionfs_d_revalidate_chain(dentry->d_parent, NULL, false);
-+      if (unlikely(!valid)) {
-+              err = -ESTALE;  /* same as what real_lookup does */
-+              goto out;
-+      }
-+
-+reval_dentry:
-+      if (!d_deleted(dentry) &&
-+          !__unionfs_d_revalidate_one_locked(dentry, NULL, willwrite)) {
 +              err = -ESTALE;
 +              goto out;
 +      }
 +
-+      sbgen = atomic_read(&UNIONFS_SB(sb)->generation);
-+      dgen = atomic_read(&UNIONFS_D(dentry)->generation);
-+
-+      if (unlikely(sbgen > dgen)) {
-+              pr_debug("unionfs: retry (locked) dentry %s revalidation\n",
-+                       dentry->d_name.name);
-+              schedule();
-+              goto reval_dentry;
-+      }
-+      BUG_ON(sbgen > dgen);
-+
-+      err = __unionfs_file_revalidate(file, dentry, sb,
++      err = __unionfs_file_revalidate(file, dentry, parent, sb,
 +                                      sbgen, dgen, willwrite);
 +out:
 +      return err;
@@ -1430,7 +1388,8 @@ index 0000000..5861970
 +}
 +
 +/* unionfs_open helper function: open a file */
-+static int __open_file(struct inode *inode, struct file *file)
++static int __open_file(struct inode *inode, struct file *file,
++                     struct dentry *parent)
 +{
 +      struct dentry *lower_dentry;
 +      struct file *lower_file;
@@ -1458,9 +1417,8 @@ index 0000000..5861970
 +
 +                      /* copyup the file */
 +                      for (bindex = bstart - 1; bindex >= 0; bindex--) {
-+                              err = copyup_file(
-+                                      file->f_path.dentry->d_parent->d_inode,
-+                                      file, bstart, bindex, size);
++                              err = copyup_file(parent->d_inode, file,
++                                                bstart, bindex, size);
 +                              if (!err)
 +                                      break;
 +                      }
@@ -1499,16 +1457,23 @@ index 0000000..5861970
 +      int err = 0;
 +      struct file *lower_file = NULL;
 +      struct dentry *dentry = file->f_path.dentry;
++      struct dentry *parent;
 +      int bindex = 0, bstart = 0, bend = 0;
 +      int size;
 +      int valid = 0;
 +
 +      unionfs_read_lock(inode->i_sb, UNIONFS_SMUTEX_PARENT);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
-+      if (dentry != dentry->d_parent)
-+              unionfs_lock_dentry(dentry->d_parent, UNIONFS_DMUTEX_PARENT);
 +
-+      valid = __unionfs_d_revalidate_chain(dentry->d_parent, NULL, false);
++      /* don't open unhashed/deleted files */
++      if (d_deleted(dentry)) {
++              err = -ENOENT;
++              goto out_nofree;
++      }
++
++      /* XXX: should I change 'false' below to the 'willwrite' flag? */
++      valid = __unionfs_d_revalidate(dentry, parent, false);
 +      if (unlikely(!valid)) {
 +              err = -ESTALE;
 +              goto out_nofree;
@@ -1548,7 +1513,7 @@ index 0000000..5861970
 +      if (S_ISDIR(inode->i_mode))
 +              err = __open_dir(inode, file);  /* open a dir */
 +      else
-+              err = __open_file(inode, file); /* open a file */
++              err = __open_file(inode, file, parent); /* open a file */
 +
 +      /* freeing the allocated resources, and fput the opened files */
 +      if (err) {
@@ -1576,9 +1541,8 @@ index 0000000..5861970
 +              unionfs_check_file(file);
 +              unionfs_check_inode(inode);
 +      }
-+      if (dentry != dentry->d_parent)
-+              unionfs_unlock_dentry(dentry->d_parent);
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(inode->i_sb);
 +      return err;
 +}
@@ -1595,21 +1559,24 @@ index 0000000..5861970
 +      struct unionfs_inode_info *inodeinfo;
 +      struct super_block *sb = inode->i_sb;
 +      struct dentry *dentry = file->f_path.dentry;
++      struct dentry *parent;
 +      int bindex, bstart, bend;
 +      int fgen, err = 0;
 +
 +      unionfs_read_lock(sb, UNIONFS_SMUTEX_PARENT);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
 +
 +      /*
-+       * Yes, we have to revalidate this file even if it's being released.
-+       * This is important for open-but-unlinked files, as well as mmap
-+       * support.
++       * We try to revalidate, but the VFS ignores return return values
++       * from file->release, so we must always try to succeed here,
++       * including to do the kfree and dput below.  So if revalidation
++       * failed, all we can do is print some message and keep going.
 +       */
-+      err = unionfs_file_revalidate(file, UNIONFS_F(file)->wrote_to_file);
-+      if (unlikely(err))
-+              goto out;
-+      unionfs_check_file(file);
++      err = unionfs_file_revalidate(file, parent,
++                                    UNIONFS_F(file)->wrote_to_file);
++      if (!err)
++              unionfs_check_file(file);
 +      fileinfo = UNIONFS_F(file);
 +      BUG_ON(file->f_path.dentry->d_inode != inode);
 +      inodeinfo = UNIONFS_I(inode);
@@ -1650,8 +1617,8 @@ index 0000000..5861970
 +      }
 +      kfree(fileinfo);
 +
-+out:
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(sb);
 +      return err;
 +}
@@ -1687,8 +1654,8 @@ index 0000000..5861970
 + * We use fd_set and therefore we are limited to the number of the branches
 + * to FD_SETSIZE, which is currently 1024 - plenty for most people
 + */
-+static int unionfs_ioctl_queryfile(struct file *file, unsigned int cmd,
-+                                 unsigned long arg)
++static int unionfs_ioctl_queryfile(struct file *file, struct dentry *parent,
++                                 unsigned int cmd, unsigned long arg)
 +{
 +      int err = 0;
 +      fd_set branchlist;
@@ -1700,7 +1667,7 @@ index 0000000..5861970
 +      dentry = file->f_path.dentry;
 +      orig_bstart = dbstart(dentry);
 +      orig_bend = dbend(dentry);
-+      err = unionfs_partial_lookup(dentry);
++      err = unionfs_partial_lookup(dentry, parent);
 +      if (err)
 +              goto out;
 +      bstart = dbstart(dentry);
@@ -1746,11 +1713,13 @@ index 0000000..5861970
 +{
 +      long err;
 +      struct dentry *dentry = file->f_path.dentry;
++      struct dentry *parent;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
 +
-+      err = unionfs_file_revalidate(file, true);
++      err = unionfs_file_revalidate(file, parent, true);
 +      if (unlikely(err))
 +              goto out;
 +
@@ -1765,7 +1734,7 @@ index 0000000..5861970
 +
 +      case UNIONFS_IOCTL_QUERYFILE:
 +              /* Return list of branches containing the given file */
-+              err = unionfs_ioctl_queryfile(file, cmd, arg);
++              err = unionfs_ioctl_queryfile(file, parent, cmd, arg);
 +              break;
 +
 +      default:
@@ -1777,6 +1746,7 @@ index 0000000..5861970
 +out:
 +      unionfs_check_file(file);
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +      return err;
 +}
@@ -1786,12 +1756,15 @@ index 0000000..5861970
 +      int err = 0;
 +      struct file *lower_file = NULL;
 +      struct dentry *dentry = file->f_path.dentry;
++      struct dentry *parent;
 +      int bindex, bstart, bend;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
 +
-+      err = unionfs_file_revalidate(file, UNIONFS_F(file)->wrote_to_file);
++      err = unionfs_file_revalidate(file, parent,
++                                    UNIONFS_F(file)->wrote_to_file);
 +      if (unlikely(err))
 +              goto out;
 +      unionfs_check_file(file);
@@ -1814,15 +1787,16 @@ index 0000000..5861970
 +      if (!err)
 +              unionfs_check_file(file);
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +      return err;
 +}
 diff --git a/fs/unionfs/copyup.c b/fs/unionfs/copyup.c
 new file mode 100644
-index 0000000..bbd49c8
+index 0000000..ae6ea2b
 --- /dev/null
 +++ b/fs/unionfs/copyup.c
-@@ -0,0 +1,880 @@
+@@ -0,0 +1,879 @@
 +/*
 + * Copyright (c) 2003-2008 Erez Zadok
 + * Copyright (c) 2003-2006 Charles P. Wright
@@ -2009,7 +1983,6 @@ index 0000000..bbd49c8
 +              args.symlink.parent = new_lower_parent_dentry->d_inode;
 +              args.symlink.dentry = new_lower_dentry;
 +              args.symlink.symbuf = symbuf;
-+              args.symlink.mode = old_mode;
 +
 +              run_sioq(__unionfs_symlink, &args);
 +              err = args.err;
@@ -3244,10 +3217,10 @@ index 0000000..db62d22
 +}
 diff --git a/fs/unionfs/dentry.c b/fs/unionfs/dentry.c
 new file mode 100644
-index 0000000..7f0c7f7
+index 0000000..583f4a4
 --- /dev/null
 +++ b/fs/unionfs/dentry.c
-@@ -0,0 +1,570 @@
+@@ -0,0 +1,397 @@
 +/*
 + * Copyright (c) 2003-2008 Erez Zadok
 + * Copyright (c) 2003-2006 Charles P. Wright
@@ -3305,111 +3278,138 @@ index 0000000..7f0c7f7
 +}
 +
 +/*
-+ * Revalidate a single dentry.
-+ * Assume that dentry's info node is locked.
-+ * Assume that parent(s) are all valid already, but
-+ * the child may not yet be valid.
-+ * Returns true if valid, false otherwise.
++ * Purge and invalidate as many data pages of a unionfs inode.  This is
++ * called when the lower inode has changed, and we want to force processes
++ * to re-get the new data.
 + */
-+static bool __unionfs_d_revalidate_one(struct dentry *dentry,
-+                                     struct nameidata *nd)
++static inline void purge_inode_data(struct inode *inode)
++{
++      /* remove all non-private mappings */
++      unmap_mapping_range(inode->i_mapping, 0, 0, 0);
++      /* invalidate as many pages as possible */
++      invalidate_mapping_pages(inode->i_mapping, 0, -1);
++      /*
++       * Don't try to truncate_inode_pages here, because this could lead
++       * to a deadlock between some of address_space ops and dentry
++       * revalidation: the address space op is invoked with a lock on our
++       * own page, and truncate_inode_pages will block on locked pages.
++       */
++}
++
++/*
++ * Revalidate a single file/symlink/special dentry.  Assume that info nodes
++ * of the @dentry and its @parent are locked.  Assume parent is valid,
++ * otherwise return false (and let's hope the VFS will try to re-lookup this
++ * dentry).  Returns true if valid, false otherwise.
++ */
++bool __unionfs_d_revalidate(struct dentry *dentry, struct dentry *parent,
++                          bool willwrite)
 +{
 +      bool valid = true;      /* default is valid */
 +      struct dentry *lower_dentry;
++      struct dentry *result;
 +      int bindex, bstart, bend;
-+      int sbgen, dgen;
++      int sbgen, dgen, pdgen;
 +      int positive = 0;
 +      int interpose_flag;
-+      struct nameidata lowernd; /* TODO: be gentler to the stack */
-+
-+      if (nd)
-+              memcpy(&lowernd, nd, sizeof(struct nameidata));
-+      else
-+              memset(&lowernd, 0, sizeof(struct nameidata));
 +
 +      verify_locked(dentry);
-+      verify_locked(dentry->d_parent);
++      verify_locked(parent);
 +
-+      sbgen = atomic_read(&UNIONFS_SB(dentry->d_sb)->generation);
 +      /* if the dentry is unhashed, do NOT revalidate */
 +      if (d_deleted(dentry))
 +              goto out;
 +
++      dgen = atomic_read(&UNIONFS_D(dentry)->generation);
++
++      if (is_newer_lower(dentry)) {
++              /* root dentry is always valid */
++              if (IS_ROOT(dentry)) {
++                      unionfs_copy_attr_times(dentry->d_inode);
++              } else {
++                      /*
++                       * reset generation number to zero, guaranteed to be
++                       * "old"
++                       */
++                      dgen = 0;
++                      atomic_set(&UNIONFS_D(dentry)->generation, dgen);
++              }
++              if (!willwrite)
++                      purge_inode_data(dentry->d_inode);
++      }
++
++      sbgen = atomic_read(&UNIONFS_SB(dentry->d_sb)->generation);
++
 +      BUG_ON(dbstart(dentry) == -1);
 +      if (dentry->d_inode)
 +              positive = 1;
-+      dgen = atomic_read(&UNIONFS_D(dentry)->generation);
-+      /*
-+       * If we are working on an unconnected dentry, then there is no
-+       * revalidation to be done, because this file does not exist within
-+       * the namespace, and Unionfs operates on the namespace, not data.
-+       */
-+      if (unlikely(sbgen != dgen)) {
-+              struct dentry *result;
-+              int pdgen;
 +
-+              /* The root entry should always be valid */
-+              BUG_ON(IS_ROOT(dentry));
++      /* if our dentry is valid, then validate all lower ones */
++      if (sbgen == dgen)
++              goto validate_lowers;
 +
-+              /* We can't work correctly if our parent isn't valid. */
-+              pdgen = atomic_read(&UNIONFS_D(dentry->d_parent)->generation);
-+              BUG_ON(pdgen != sbgen); /* should never happen here */
++      /* The root entry should always be valid */
++      BUG_ON(IS_ROOT(dentry));
 +
-+              /* Free the pointers for our inodes and this dentry. */
-+              bstart = dbstart(dentry);
-+              bend = dbend(dentry);
++      /* We can't work correctly if our parent isn't valid. */
++      pdgen = atomic_read(&UNIONFS_D(parent)->generation);
 +
-+              /*
-+               * mntput unhashed lower dentries, because those files got
-+               * deleted or rmdir'ed.
-+               */
-+              for (bindex = bstart; bindex <= bend; bindex++) {
-+                      lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
-+                      if (!lower_dentry)
-+                              continue;
-+                      if (!d_deleted(lower_dentry) &&
-+                          !(lower_dentry->d_flags & DCACHE_NFSFS_RENAMED))
-+                          continue;
-+                      unionfs_mntput(dentry, bindex);
-+              }
++      /* Free the pointers for our inodes and this dentry. */
++      path_put_lowers_all(dentry, false);
 +
-+              __dput_lowers(dentry, bstart, bend);
-+              dbstart(dentry) = dbend(dentry) = -1;
++      interpose_flag = INTERPOSE_REVAL_NEG;
++      if (positive) {
++              interpose_flag = INTERPOSE_REVAL;
++              iput_lowers_all(dentry->d_inode, true);
++      }
 +
-+              interpose_flag = INTERPOSE_REVAL_NEG;
-+              if (positive) {
-+                      interpose_flag = INTERPOSE_REVAL;
-+                      iput_lowers_all(dentry->d_inode, true);
-+              }
++      if (realloc_dentry_private_data(dentry) != 0) {
++              valid = false;
++              goto out;
++      }
 +
-+              if (realloc_dentry_private_data(dentry) != 0) {
++      result = unionfs_lookup_full(dentry, parent, interpose_flag);
++      if (result) {
++              if (IS_ERR(result)) {
 +                      valid = false;
 +                      goto out;
 +              }
++              /*
++               * current unionfs_lookup_backend() doesn't return
++               * a valid dentry
++               */
++              dput(dentry);
++              dentry = result;
++      }
 +
-+              result = unionfs_lookup_full(dentry, &lowernd, interpose_flag);
-+              if (result) {
-+                      if (IS_ERR(result)) {
-+                              valid = false;
-+                              goto out;
-+                      }
-+                      /*
-+                       * current unionfs_lookup_backend() doesn't return
-+                       * a valid dentry
-+                       */
-+                      dput(dentry);
-+                      dentry = result;
-+              }
-+
-+              if (unlikely(positive && is_negative_lower(dentry))) {
-+                      make_bad_inode(dentry->d_inode);
-+                      d_drop(dentry);
-+                      valid = false;
-+                      goto out;
-+              }
++      if (unlikely(positive && is_negative_lower(dentry))) {
++              /* call make_bad_inode here ? */
++              d_drop(dentry);
++              valid = false;
 +              goto out;
 +      }
 +
++      /*
++       * if we got here then we have revalidated our dentry and all lower
++       * ones, so we can return safely.
++       */
++      if (!valid)             /* lower dentry revalidation failed */
++              goto out;
++
++      /*
++       * If the parent's gen no.  matches the superblock's gen no., then
++       * we can update our denty's gen no.  If they didn't match, then it
++       * was OK to revalidate this dentry with a stale parent, but we'll
++       * purposely not update our dentry's gen no. (so it can be redone);
++       * and, we'll mark our parent dentry as invalid so it'll force it
++       * (and our dentry) to be revalidated.
++       */
++      if (pdgen == sbgen)
++              atomic_set(&UNIONFS_D(dentry)->generation, sbgen);
++      goto out;
++
++validate_lowers:
++
 +      /* The revalidation must occur across all branches */
 +      bstart = dbstart(dentry);
 +      bend = dbend(dentry);
@@ -3443,7 +3443,7 @@ index 0000000..7f0c7f7
 +               * If we get here, and we copy the meta-data from the lower
 +               * inode to our inode, then it is vital that we have already
 +               * purged all unionfs-level file data.  We do that in the
-+               * caller (__unionfs_d_revalidate_chain) by calling
++               * caller (__unionfs_d_revalidate) by calling
 +               * purge_inode_data.
 +               */
 +              unionfs_copy_attr_all(dentry->d_inode,
@@ -3453,9 +3453,6 @@ index 0000000..7f0c7f7
 +      }
 +
 +out:
-+      if (valid)
-+              atomic_set(&UNIONFS_D(dentry)->generation, sbgen);
-+
 +      return valid;
 +}
 +
@@ -3531,224 +3528,27 @@ index 0000000..7f0c7f7
 +      return false;           /* default: lower is not newer */
 +}
 +
-+/*
-+ * Purge and invalidate as many data pages of a unionfs inode.  This is
-+ * called when the lower inode has changed, and we want to force processes
-+ * to re-get the new data.
-+ */
-+static inline void purge_inode_data(struct inode *inode)
-+{
-+      /* remove all non-private mappings */
-+      unmap_mapping_range(inode->i_mapping, 0, 0, 0);
-+      /* invalidate as many pages as possible */
-+      invalidate_mapping_pages(inode->i_mapping, 0, -1);
-+      /*
-+       * Don't try to truncate_inode_pages here, because this could lead
-+       * to a deadlock between some of address_space ops and dentry
-+       * revalidation: the address space op is invoked with a lock on our
-+       * own page, and truncate_inode_pages will block on locked pages.
-+       */
-+}
-+
-+/*
-+ * Revalidate a single file/symlink/special dentry.  Assume that info nodes
-+ * of the dentry and its parent are locked.  Assume that parent(s) are all
-+ * valid already, but the child may not yet be valid.  Returns true if
-+ * valid, false otherwise.
-+ */
-+bool __unionfs_d_revalidate_one_locked(struct dentry *dentry,
-+                                     struct nameidata *nd,
-+                                     bool willwrite)
-+{
-+      bool valid = false;     /* default is invalid */
-+      int sbgen, dgen, bindex;
-+
-+      verify_locked(dentry);
-+      verify_locked(dentry->d_parent);
-+
-+      sbgen = atomic_read(&UNIONFS_SB(dentry->d_sb)->generation);
-+      dgen = atomic_read(&UNIONFS_D(dentry)->generation);
-+
-+      if (unlikely(is_newer_lower(dentry))) {
-+              /* root dentry special case as aforementioned */
-+              if (IS_ROOT(dentry)) {
-+                      unionfs_copy_attr_times(dentry->d_inode);
-+              } else {
-+                      /*
-+                       * reset generation number to zero, guaranteed to be
-+                       * "old"
-+                       */
-+                      dgen = 0;
-+                      atomic_set(&UNIONFS_D(dentry)->generation, dgen);
-+              }
-+              if (!willwrite)
-+                      purge_inode_data(dentry->d_inode);
-+      }
-+      valid = __unionfs_d_revalidate_one(dentry, nd);
-+
-+      /*
-+       * If __unionfs_d_revalidate_one() succeeded above, then it will
-+       * have incremented the refcnt of the mnt's, but also the branch
-+       * indices of the dentry will have been updated (to take into
-+       * account any branch insertions/deletion.  So the current
-+       * dbstart/dbend match the current, and new, indices of the mnts
-+       * which __unionfs_d_revalidate_one has incremented.  Note: the "if"
-+       * test below does not depend on whether chain_len was 0 or greater.
-+       */
-+      if (!valid || sbgen == dgen)
-+              goto out;
-+      for (bindex = dbstart(dentry); bindex <= dbend(dentry); bindex++)
-+              unionfs_mntput(dentry, bindex);
-+out:
-+      return valid;
-+}
-+
-+/*
-+ * Revalidate a parent chain of dentries, then the actual node.
-+ * Assumes that dentry is locked, but will lock all parents if/when needed.
-+ *
-+ * If 'willwrite' is true, and the lower inode times are not in sync, then
-+ * *don't* purge_inode_data, as it could deadlock if ->write calls us and we
-+ * try to truncate a locked page.  Besides, if unionfs is about to write
-+ * data to a file, then there's the data unionfs is about to write is more
-+ * authoritative than what's below, therefore we can safely overwrite the
-+ * lower inode times and data.
-+ */
-+bool __unionfs_d_revalidate_chain(struct dentry *dentry, struct nameidata *nd,
-+                                bool willwrite)
-+{
-+      bool valid = false;     /* default is invalid */
-+      struct dentry **chain = NULL; /* chain of dentries to reval */
-+      int chain_len = 0;
-+      struct dentry *dtmp;
-+      int sbgen, dgen, i;
-+      int saved_bstart, saved_bend, bindex;
-+
-+      /* find length of chain needed to revalidate */
-+      /* XXX: should I grab some global (dcache?) lock? */
-+      chain_len = 0;
-+      sbgen = atomic_read(&UNIONFS_SB(dentry->d_sb)->generation);
-+      dtmp = dentry->d_parent;
-+      verify_locked(dentry);
-+      if (dentry != dtmp)
-+              unionfs_lock_dentry(dtmp, UNIONFS_DMUTEX_REVAL_PARENT);
-+      dgen = atomic_read(&UNIONFS_D(dtmp)->generation);
-+      /* XXX: should we check if is_newer_lower all the way up? */
-+      if (unlikely(is_newer_lower(dtmp))) {
-+              /*
-+               * Special case: the root dentry's generation number must
-+               * always be valid, but its lower inode times don't have to
-+               * be, so sync up the times only.
-+               */
-+              if (IS_ROOT(dtmp)) {
-+                      unionfs_copy_attr_times(dtmp->d_inode);
-+              } else {
-+                      /*
-+                       * reset generation number to zero, guaranteed to be
-+                       * "old"
-+                       */
-+                      dgen = 0;
-+                      atomic_set(&UNIONFS_D(dtmp)->generation, dgen);
-+              }
-+              purge_inode_data(dtmp->d_inode);
-+      }
-+      if (dentry != dtmp)
-+              unionfs_unlock_dentry(dtmp);
-+      while (sbgen != dgen) {
-+              /* The root entry should always be valid */
-+              BUG_ON(IS_ROOT(dtmp));
-+              chain_len++;
-+              dtmp = dtmp->d_parent;
-+              dgen = atomic_read(&UNIONFS_D(dtmp)->generation);
-+      }
-+      if (chain_len == 0)
-+              goto out_this;  /* shortcut if parents are OK */
-+
-+      /*
-+       * Allocate array of dentries to reval.  We could use linked lists,
-+       * but the number of entries we need to alloc here is often small,
-+       * and short lived, so locality will be better.
-+       */
-+      chain = kzalloc(chain_len * sizeof(struct dentry *), GFP_KERNEL);
-+      if (unlikely(!chain)) {
-+              printk(KERN_CRIT "unionfs: no more memory in %s\n",
-+                     __func__);
-+              goto out;
-+      }
-+
-+      /* grab all dentries in chain, in child to parent order */
-+      dtmp = dentry;
-+      for (i = chain_len-1; i >= 0; i--)
-+              dtmp = chain[i] = dget_parent(dtmp);
-+
-+      /*
-+       * call __unionfs_d_revalidate_one() on each dentry, but in parent
-+       * to child order.
-+       */
-+      for (i = 0; i < chain_len; i++) {
-+              unionfs_lock_dentry(chain[i], UNIONFS_DMUTEX_REVAL_CHILD);
-+              if (chain[i] != chain[i]->d_parent)
-+                      unionfs_lock_dentry(chain[i]->d_parent,
-+                                          UNIONFS_DMUTEX_REVAL_PARENT);
-+              saved_bstart = dbstart(chain[i]);
-+              saved_bend = dbend(chain[i]);
-+              sbgen = atomic_read(&UNIONFS_SB(dentry->d_sb)->generation);
-+              dgen = atomic_read(&UNIONFS_D(chain[i])->generation);
-+
-+              valid = __unionfs_d_revalidate_one(chain[i], nd);
-+              /* XXX: is this the correct mntput condition?! */
-+              if (valid && chain_len > 0 &&
-+                  sbgen != dgen && chain[i]->d_inode &&
-+                  S_ISDIR(chain[i]->d_inode->i_mode)) {
-+                      for (bindex = saved_bstart; bindex <= saved_bend;
-+                           bindex++)
-+                              unionfs_mntput(chain[i], bindex);
-+              }
-+              if (chain[i] != chain[i]->d_parent)
-+                      unionfs_unlock_dentry(chain[i]->d_parent);
-+              unionfs_unlock_dentry(chain[i]);
-+
-+              if (unlikely(!valid))
-+                      goto out_free;
-+      }
-+
-+
-+out_this:
-+      /* finally, lock this dentry and revalidate it */
-+      verify_locked(dentry);  /* verify child is locked */
-+      if (dentry != dentry->d_parent)
-+              unionfs_lock_dentry(dentry->d_parent,
-+                                  UNIONFS_DMUTEX_REVAL_PARENT);
-+      valid = __unionfs_d_revalidate_one_locked(dentry, nd, willwrite);
-+      if (dentry != dentry->d_parent)
-+              unionfs_unlock_dentry(dentry->d_parent);
-+
-+out_free:
-+      /* unlock/dput all dentries in chain and return status */
-+      if (chain_len > 0) {
-+              for (i = 0; i < chain_len; i++)
-+                      dput(chain[i]);
-+              kfree(chain);
-+      }
-+out:
-+      return valid;
-+}
-+
-+static int unionfs_d_revalidate(struct dentry *dentry, struct nameidata *nd)
++static int unionfs_d_revalidate(struct dentry *dentry,
++                              struct nameidata *nd_unused)
 +{
-+      int err;
++      bool valid = true;
++      int err = 1;            /* 1 means valid for the VFS */
++      struct dentry *parent;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
-+
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
-+      err = __unionfs_d_revalidate_chain(dentry, nd, false);
-+      if (likely(err > 0)) { /* true==1: dentry is valid */
++
++      valid = __unionfs_d_revalidate(dentry, parent, false);
++      if (valid) {
 +              unionfs_postcopyup_setmnt(dentry);
 +              unionfs_check_dentry(dentry);
-+              unionfs_check_nd(nd);
++      } else {
++              d_drop(dentry);
++              err = valid;
 +      }
 +      unionfs_unlock_dentry(dentry);
-+
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +
 +      return err;
@@ -3820,10 +3620,10 @@ index 0000000..7f0c7f7
 +};
 diff --git a/fs/unionfs/dirfops.c b/fs/unionfs/dirfops.c
 new file mode 100644
-index 0000000..14ca7d3
+index 0000000..63fb419
 --- /dev/null
 +++ b/fs/unionfs/dirfops.c
-@@ -0,0 +1,292 @@
+@@ -0,0 +1,302 @@
 +/*
 + * Copyright (c) 2003-2008 Erez Zadok
 + * Copyright (c) 2003-2006 Charles P. Wright
@@ -3920,6 +3720,7 @@ index 0000000..14ca7d3
 +      int err = 0;
 +      struct file *lower_file = NULL;
 +      struct dentry *dentry = file->f_path.dentry;
++      struct dentry *parent;
 +      struct inode *inode = NULL;
 +      struct unionfs_getdents_callback buf;
 +      struct unionfs_dir_state *uds;
@@ -3927,9 +3728,10 @@ index 0000000..14ca7d3
 +      loff_t offset;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
 +
-+      err = unionfs_file_revalidate(file, false);
++      err = unionfs_file_revalidate(file, parent, false);
 +      if (unlikely(err))
 +              goto out;
 +
@@ -4013,7 +3815,10 @@ index 0000000..14ca7d3
 +      }
 +
 +out:
++      if (!err)
++              unionfs_check_file(file);
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +      return err;
 +}
@@ -4032,12 +3837,14 @@ index 0000000..14ca7d3
 +{
 +      struct unionfs_dir_state *rdstate;
 +      struct dentry *dentry = file->f_path.dentry;
++      struct dentry *parent;
 +      loff_t err;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
 +
-+      err = unionfs_file_revalidate(file, false);
++      err = unionfs_file_revalidate(file, parent, false);
 +      if (unlikely(err))
 +              goto out;
 +
@@ -4096,7 +3903,10 @@ index 0000000..14ca7d3
 +      }
 +
 +out:
++      if (!err)
++              unionfs_check_file(file);
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +      return err;
 +}
@@ -4118,10 +3928,10 @@ index 0000000..14ca7d3
 +};
 diff --git a/fs/unionfs/dirhelper.c b/fs/unionfs/dirhelper.c
 new file mode 100644
-index 0000000..d936f03
+index 0000000..aa31e91
 --- /dev/null
 +++ b/fs/unionfs/dirhelper.c
-@@ -0,0 +1,157 @@
+@@ -0,0 +1,158 @@
 +/*
 + * Copyright (c) 2003-2008 Erez Zadok
 + * Copyright (c) 2003-2006 Charles P. Wright
@@ -4192,7 +4002,8 @@ index 0000000..d936f03
 +}
 +
 +/* Is a directory logically empty? */
-+int check_empty(struct dentry *dentry, struct unionfs_dir_state **namelist)
++int check_empty(struct dentry *dentry, struct dentry *parent,
++              struct unionfs_dir_state **namelist)
 +{
 +      int err = 0;
 +      struct dentry *lower_dentry = NULL;
@@ -4207,7 +4018,7 @@ index 0000000..d936f03
 +
 +      BUG_ON(!S_ISDIR(dentry->d_inode->i_mode));
 +
-+      err = unionfs_partial_lookup(dentry);
++      err = unionfs_partial_lookup(dentry, parent);
 +      if (err)
 +              goto out;
 +
@@ -4281,10 +4092,10 @@ index 0000000..d936f03
 +}
 diff --git a/fs/unionfs/fanout.h b/fs/unionfs/fanout.h
 new file mode 100644
-index 0000000..4f264de
+index 0000000..69c0921
 --- /dev/null
 +++ b/fs/unionfs/fanout.h
-@@ -0,0 +1,384 @@
+@@ -0,0 +1,407 @@
 +/*
 + * Copyright (c) 2003-2008 Erez Zadok
 + * Copyright (c) 2003-2006 Charles P. Wright
@@ -4558,6 +4369,29 @@ index 0000000..4f264de
 +      mutex_unlock(&UNIONFS_D(d)->lock);
 +}
 +
++static inline struct dentry *unionfs_lock_parent(struct dentry *d,
++                                               unsigned int subclass)
++{
++      struct dentry *p;
++
++      BUG_ON(!d);
++      p = dget_parent(d);
++      if (p != d)
++              mutex_lock_nested(&UNIONFS_D(p)->lock, subclass);
++      return p;
++}
++
++static inline void unionfs_unlock_parent(struct dentry *d, struct dentry *p)
++{
++      BUG_ON(!d);
++      BUG_ON(!p);
++      if (p != d) {
++              BUG_ON(!mutex_is_locked(&UNIONFS_D(p)->lock));
++              mutex_unlock(&UNIONFS_D(p)->lock);
++      }
++      dput(p);
++}
++
 +static inline void verify_locked(struct dentry *d)
 +{
 +      BUG_ON(!d);
@@ -4671,10 +4505,10 @@ index 0000000..4f264de
 +#endif        /* not _FANOUT_H */
 diff --git a/fs/unionfs/file.c b/fs/unionfs/file.c
 new file mode 100644
-index 0000000..965d071
+index 0000000..3cc6a76
 --- /dev/null
 +++ b/fs/unionfs/file.c
-@@ -0,0 +1,341 @@
+@@ -0,0 +1,364 @@
 +/*
 + * Copyright (c) 2003-2008 Erez Zadok
 + * Copyright (c) 2003-2006 Charles P. Wright
@@ -4701,10 +4535,13 @@ index 0000000..965d071
 +      int err;
 +      struct file *lower_file;
 +      struct dentry *dentry = file->f_path.dentry;
++      struct dentry *parent;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
-+      err = unionfs_file_revalidate(file, false);
++
++      err = unionfs_file_revalidate(file, parent, false);
 +      if (unlikely(err))
 +              goto out;
 +
@@ -4719,6 +4556,7 @@ index 0000000..965d071
 +
 +out:
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +      return err;
 +}
@@ -4729,12 +4567,13 @@ index 0000000..965d071
 +      int err = 0;
 +      struct file *lower_file;
 +      struct dentry *dentry = file->f_path.dentry;
++      struct dentry *parent;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
-+      if (dentry != dentry->d_parent)
-+              unionfs_lock_dentry(dentry->d_parent, UNIONFS_DMUTEX_PARENT);
-+      err = unionfs_file_revalidate_locked(file, true);
++
++      err = unionfs_file_revalidate(file, parent, true);
 +      if (unlikely(err))
 +              goto out;
 +
@@ -4751,9 +4590,8 @@ index 0000000..965d071
 +      }
 +
 +out:
-+      if (dentry != dentry->d_parent)
-+              unionfs_unlock_dentry(dentry->d_parent);
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +      return err;
 +}
@@ -4770,14 +4608,16 @@ index 0000000..965d071
 +      bool willwrite;
 +      struct file *lower_file;
 +      struct dentry *dentry = file->f_path.dentry;
++      struct dentry *parent;
 +      struct vm_operations_struct *saved_vm_ops = NULL;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
 +
 +      /* This might be deferred to mmap's writepage */
 +      willwrite = ((vma->vm_flags | VM_SHARED | VM_WRITE) == vma->vm_flags);
-+      err = unionfs_file_revalidate(file, willwrite);
++      err = unionfs_file_revalidate(file, parent, willwrite);
 +      if (unlikely(err))
 +              goto out;
 +      unionfs_check_file(file);
@@ -4834,10 +4674,11 @@ index 0000000..965d071
 +out:
 +      if (!err) {
 +              /* copyup could cause parent dir times to change */
-+              unionfs_copy_attr_times(dentry->d_parent->d_inode);
++              unionfs_copy_attr_times(parent->d_inode);
 +              unionfs_check_file(file);
 +      }
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +      return err;
 +}
@@ -4847,12 +4688,15 @@ index 0000000..965d071
 +      int bindex, bstart, bend;
 +      struct file *lower_file;
 +      struct dentry *lower_dentry;
++      struct dentry *parent;
 +      struct inode *lower_inode, *inode;
 +      int err = -EINVAL;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
-+      err = unionfs_file_revalidate(file, true);
++
++      err = unionfs_file_revalidate(file, parent, true);
 +      if (unlikely(err))
 +              goto out;
 +      unionfs_check_file(file);
@@ -4889,6 +4733,7 @@ index 0000000..965d071
 +      if (!err)
 +              unionfs_check_file(file);
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +      return err;
 +}
@@ -4898,12 +4743,15 @@ index 0000000..965d071
 +      int bindex, bstart, bend;
 +      struct file *lower_file;
 +      struct dentry *dentry = file->f_path.dentry;
++      struct dentry *parent;
 +      struct inode *lower_inode, *inode;
 +      int err = 0;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
-+      err = unionfs_file_revalidate(file, true);
++
++      err = unionfs_file_revalidate(file, parent, true);
 +      if (unlikely(err))
 +              goto out;
 +      unionfs_check_file(file);
@@ -4937,6 +4785,7 @@ index 0000000..965d071
 +      if (!err)
 +              unionfs_check_file(file);
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +      return err;
 +}
@@ -4948,10 +4797,13 @@ index 0000000..965d071
 +      ssize_t err;
 +      struct file *lower_file;
 +      struct dentry *dentry = file->f_path.dentry;
++      struct dentry *parent;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
-+      err = unionfs_file_revalidate(file, false);
++
++      err = unionfs_file_revalidate(file, parent, false);
 +      if (unlikely(err))
 +              goto out;
 +
@@ -4966,6 +4818,7 @@ index 0000000..965d071
 +
 +out:
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +      return err;
 +}
@@ -4977,10 +4830,13 @@ index 0000000..965d071
 +      ssize_t err = 0;
 +      struct file *lower_file;
 +      struct dentry *dentry = file->f_path.dentry;
++      struct dentry *parent;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
-+      err = unionfs_file_revalidate(file, true);
++
++      err = unionfs_file_revalidate(file, parent, true);
 +      if (unlikely(err))
 +              goto out;
 +
@@ -4997,6 +4853,7 @@ index 0000000..965d071
 +
 +out:
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +      return err;
 +}
@@ -5018,10 +4875,10 @@ index 0000000..965d071
 +};
 diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
 new file mode 100644
-index 0000000..a05b412
+index 0000000..800648e
 --- /dev/null
 +++ b/fs/unionfs/inode.c
-@@ -0,0 +1,992 @@
+@@ -0,0 +1,1035 @@
 +/*
 + * Copyright (c) 2003-2008 Erez Zadok
 + * Copyright (c) 2003-2006 Charles P. Wright
@@ -5120,33 +4977,27 @@ index 0000000..a05b412
 +      return lower_dentry;
 +}
 +
-+static int unionfs_create(struct inode *parent, struct dentry *dentry,
-+                        int mode, struct nameidata *nd)
++static int unionfs_create(struct inode *dir, struct dentry *dentry,
++                        int mode, struct nameidata *nd_unused)
 +{
 +      int err = 0;
 +      struct dentry *lower_dentry = NULL;
 +      struct dentry *lower_parent_dentry = NULL;
++      struct dentry *parent;
 +      int valid = 0;
 +      struct nameidata lower_nd;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
-+      unionfs_lock_dentry(dentry->d_parent, UNIONFS_DMUTEX_PARENT);
 +
-+      valid = __unionfs_d_revalidate_chain(dentry->d_parent, nd, false);
++      valid = __unionfs_d_revalidate(dentry, parent, false);
 +      if (unlikely(!valid)) {
 +              err = -ESTALE;  /* same as what real_lookup does */
 +              goto out;
 +      }
 +
-+      valid = __unionfs_d_revalidate_one_locked(dentry, nd, false);
-+      /*
-+       * It's only a bug if this dentry was not negative and couldn't be
-+       * revalidated (shouldn't happen).
-+       */
-+      BUG_ON(!valid && dentry->d_inode);
-+
-+      lower_dentry = find_writeable_branch(parent, dentry);
++      lower_dentry = find_writeable_branch(dir, dentry);
 +      if (IS_ERR(lower_dentry)) {
 +              err = PTR_ERR(lower_dentry);
 +              goto out;
@@ -5166,13 +5017,13 @@ index 0000000..a05b412
 +      release_lower_nd(&lower_nd, err);
 +
 +      if (!err) {
-+              err = PTR_ERR(unionfs_interpose(dentry, parent->i_sb, 0));
++              err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0));
 +              if (!err) {
-+                      unionfs_copy_attr_times(parent);
-+                      fsstack_copy_inode_size(parent,
++                      unionfs_copy_attr_times(dir);
++                      fsstack_copy_inode_size(dir,
 +                                              lower_parent_dentry->d_inode);
 +                      /* update no. of links on parent directory */
-+                      parent->i_nlink = unionfs_get_nlinks(parent);
++                      dir->i_nlink = unionfs_get_nlinks(dir);
 +              }
 +      }
 +
@@ -5181,12 +5032,11 @@ index 0000000..a05b412
 +out:
 +      if (!err) {
 +              unionfs_postcopyup_setmnt(dentry);
-+              unionfs_check_inode(parent);
++              unionfs_check_inode(dir);
 +              unionfs_check_dentry(dentry);
-+              unionfs_check_nd(nd);
 +      }
-+      unionfs_unlock_dentry(dentry->d_parent);
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +      return err;
 +}
@@ -5196,27 +5046,20 @@ index 0000000..a05b412
 + * do NOT want to call __unionfs_d_revalidate_chain because by definition,
 + * we don't have a valid dentry here yet.
 + */
-+static struct dentry *unionfs_lookup(struct inode *parent,
++static struct dentry *unionfs_lookup(struct inode *dir,
 +                                   struct dentry *dentry,
-+                                   struct nameidata *nd)
++                                   struct nameidata *nd_unused)
 +{
-+      struct path path_save = {NULL, NULL};
-+      struct dentry *ret;
++      struct dentry *ret, *parent;
 +      int err = 0;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
-+      if (dentry != dentry->d_parent)
-+              unionfs_lock_dentry(dentry->d_parent, UNIONFS_DMUTEX_ROOT);
-+
-+      /* save the dentry & vfsmnt from namei */
-+      if (nd) {
-+              path_save.dentry = nd->path.dentry;
-+              path_save.mnt = nd->path.mnt;
-+      }
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +
 +      /*
-+       * unionfs_lookup_backend returns a locked dentry upon success,
-+       * so we'll have to unlock it below.
++       * As long as we lock/dget the parent, then can skip validating the
++       * parent now; we may have to rebuild this dentry on the next
++       * ->d_revalidate, however.
 +       */
 +
 +      /* allocate dentry private data.  We free it in ->d_release */
@@ -5225,13 +5068,9 @@ index 0000000..a05b412
 +              ret = ERR_PTR(err);
 +              goto out;
 +      }
-+      ret = unionfs_lookup_full(dentry, nd, INTERPOSE_LOOKUP);
 +
-+      /* restore the dentry & vfsmnt in namei */
-+      if (nd) {
-+              nd->path.dentry = path_save.dentry;
-+              nd->path.mnt = path_save.mnt;
-+      }
++      ret = unionfs_lookup_full(dentry, parent, INTERPOSE_LOOKUP);
++
 +      if (!IS_ERR(ret)) {
 +              if (ret)
 +                      dentry = ret;
@@ -5241,22 +5080,16 @@ index 0000000..a05b412
 +                      unionfs_postcopyup_release(dentry);
 +              }
 +              unionfs_copy_attr_times(dentry->d_inode);
-+              /* parent times may have changed */
-+              unionfs_copy_attr_times(dentry->d_parent->d_inode);
 +      }
 +
-+      unionfs_check_inode(parent);
-+      if (!IS_ERR(ret)) {
++      unionfs_check_inode(dir);
++      if (!IS_ERR(ret))
 +              unionfs_check_dentry(dentry);
-+              unionfs_check_nd(nd);
-+      }
-+      unionfs_unlock_dentry(dentry);
++      unionfs_check_dentry(parent);
++      unionfs_unlock_dentry(dentry); /* locked in new_dentry_private data */
 +
 +out:
-+      if (dentry != dentry->d_parent) {
-+              unionfs_check_dentry(dentry->d_parent);
-+              unionfs_unlock_dentry(dentry->d_parent);
-+      }
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +
 +      return ret;
@@ -5269,19 +5102,27 @@ index 0000000..a05b412
 +      struct dentry *lower_old_dentry = NULL;
 +      struct dentry *lower_new_dentry = NULL;
 +      struct dentry *lower_dir_dentry = NULL;
++      struct dentry *old_parent, *new_parent;
 +      char *name = NULL;
++      bool valid;
 +
 +      unionfs_read_lock(old_dentry->d_sb, UNIONFS_SMUTEX_CHILD);
-+      unionfs_double_lock_dentry(new_dentry, old_dentry);
++      old_parent = dget_parent(old_dentry);
++      new_parent = dget_parent(new_dentry);
++      unionfs_double_lock_parents(old_parent, new_parent);
++      unionfs_double_lock_dentry(old_dentry, new_dentry);
 +
-+      if (unlikely(!__unionfs_d_revalidate_chain(old_dentry, NULL, false))) {
++      valid = __unionfs_d_revalidate(old_dentry, old_parent, false);
++      if (unlikely(!valid)) {
 +              err = -ESTALE;
 +              goto out;
 +      }
-+      if (unlikely(new_dentry->d_inode &&
-+                   !__unionfs_d_revalidate_chain(new_dentry, NULL, false))) {
-+              err = -ESTALE;
-+              goto out;
++      if (new_dentry->d_inode) {
++              valid = __unionfs_d_revalidate(new_dentry, new_parent, false);
++              if (unlikely(!valid)) {
++                      err = -ESTALE;
++                      goto out;
++              }
 +      }
 +
 +      lower_new_dentry = unionfs_lower_dentry(new_dentry);
@@ -5331,7 +5172,7 @@ index 0000000..a05b412
 +              int bindex;
 +
 +              for (bindex = old_bstart - 1; bindex >= 0; bindex--) {
-+                      err = copyup_dentry(old_dentry->d_parent->d_inode,
++                      err = copyup_dentry(old_parent->d_inode,
 +                                          old_dentry, old_bstart,
 +                                          bindex, old_dentry->d_name.name,
 +                                          old_dentry->d_name.len, NULL,
@@ -5384,38 +5225,36 @@ index 0000000..a05b412
 +      unionfs_check_dentry(new_dentry);
 +      unionfs_check_dentry(old_dentry);
 +
-+      unionfs_unlock_dentry(new_dentry);
-+      unionfs_unlock_dentry(old_dentry);
++      unionfs_double_unlock_dentry(old_dentry, new_dentry);
++      unionfs_double_unlock_parents(old_parent, new_parent);
++      dput(new_parent);
++      dput(old_parent);
 +      unionfs_read_unlock(old_dentry->d_sb);
 +
 +      return err;
 +}
 +
-+static int unionfs_symlink(struct inode *parent, struct dentry *dentry,
++static int unionfs_symlink(struct inode *dir, struct dentry *dentry,
 +                         const char *symname)
 +{
 +      int err = 0;
 +      struct dentry *lower_dentry = NULL;
 +      struct dentry *wh_dentry = NULL;
 +      struct dentry *lower_parent_dentry = NULL;
++      struct dentry *parent;
 +      char *name = NULL;
 +      int valid = 0;
 +      umode_t mode;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
-+      unionfs_lock_dentry(dentry->d_parent, UNIONFS_DMUTEX_PARENT);
 +
-+      valid = __unionfs_d_revalidate_chain(dentry->d_parent, NULL, false);
++      valid = __unionfs_d_revalidate(dentry, parent, false);
 +      if (unlikely(!valid)) {
 +              err = -ESTALE;
 +              goto out;
 +      }
-+      if (unlikely(dentry->d_inode &&
-+                   !__unionfs_d_revalidate_one_locked(dentry, NULL, false))) {
-+              err = -ESTALE;
-+              goto out;
-+      }
 +
 +      /*
 +       * It's only a bug if this dentry was not negative and couldn't be
@@ -5423,7 +5262,7 @@ index 0000000..a05b412
 +       */
 +      BUG_ON(!valid && dentry->d_inode);
 +
-+      lower_dentry = find_writeable_branch(parent, dentry);
++      lower_dentry = find_writeable_branch(dir, dentry);
 +      if (IS_ERR(lower_dentry)) {
 +              err = PTR_ERR(lower_dentry);
 +              goto out;
@@ -5436,16 +5275,15 @@ index 0000000..a05b412
 +      }
 +
 +      mode = S_IALLUGO;
-+      err = vfs_symlink(lower_parent_dentry->d_inode, lower_dentry,
-+                        symname, mode);
++      err = vfs_symlink(lower_parent_dentry->d_inode, lower_dentry, symname);
 +      if (!err) {
-+              err = PTR_ERR(unionfs_interpose(dentry, parent->i_sb, 0));
++              err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0));
 +              if (!err) {
-+                      unionfs_copy_attr_times(parent);
-+                      fsstack_copy_inode_size(parent,
++                      unionfs_copy_attr_times(dir);
++                      fsstack_copy_inode_size(dir,
 +                                              lower_parent_dentry->d_inode);
 +                      /* update no. of links on parent directory */
-+                      parent->i_nlink = unionfs_get_nlinks(parent);
++                      dir->i_nlink = unionfs_get_nlinks(dir);
 +              }
 +      }
 +
@@ -5457,38 +5295,34 @@ index 0000000..a05b412
 +
 +      if (!err) {
 +              unionfs_postcopyup_setmnt(dentry);
-+              unionfs_check_inode(parent);
++              unionfs_check_inode(dir);
 +              unionfs_check_dentry(dentry);
 +      }
-+      unionfs_unlock_dentry(dentry->d_parent);
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +      return err;
 +}
 +
-+static int unionfs_mkdir(struct inode *parent, struct dentry *dentry, int mode)
++static int unionfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 +{
 +      int err = 0;
 +      struct dentry *lower_dentry = NULL;
 +      struct dentry *lower_parent_dentry = NULL;
++      struct dentry *parent;
 +      int bindex = 0, bstart;
 +      char *name = NULL;
 +      int valid;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
-+      unionfs_lock_dentry(dentry->d_parent, UNIONFS_DMUTEX_PARENT);
 +
-+      valid = __unionfs_d_revalidate_chain(dentry->d_parent, NULL, false);
++      valid = __unionfs_d_revalidate(dentry, parent, false);
 +      if (unlikely(!valid)) {
 +              err = -ESTALE;  /* same as what real_lookup does */
 +              goto out;
 +      }
-+      if (unlikely(dentry->d_inode &&
-+                   !__unionfs_d_revalidate_one_locked(dentry, NULL, false))) {
-+              err = -ESTALE;
-+              goto out;
-+      }
 +
 +      bstart = dbstart(dentry);
 +
@@ -5515,7 +5349,7 @@ index 0000000..a05b412
 +
 +              lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
 +              if (!lower_dentry) {
-+                      lower_dentry = create_parents(parent, dentry,
++                      lower_dentry = create_parents(dir, dentry,
 +                                                    dentry->d_name.name,
 +                                                    bindex);
 +                      if (!lower_dentry || IS_ERR(lower_dentry)) {
@@ -5541,7 +5375,8 @@ index 0000000..a05b412
 +              if (err)
 +                      break;
 +
-+              for (i = bindex + 1; i < bend; i++) {
++              for (i = bindex + 1; i <= bend; i++) {
++                      /* XXX: use path_put_lowers? */
 +                      if (unionfs_lower_dentry_idx(dentry, i)) {
 +                              dput(unionfs_lower_dentry_idx(dentry, i));
 +                              unionfs_set_lower_dentry_idx(dentry, i, NULL);
@@ -5553,14 +5388,14 @@ index 0000000..a05b412
 +               * Only INTERPOSE_LOOKUP can return a value other than 0 on
 +               * err.
 +               */
-+              err = PTR_ERR(unionfs_interpose(dentry, parent->i_sb, 0));
++              err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0));
 +              if (!err) {
-+                      unionfs_copy_attr_times(parent);
-+                      fsstack_copy_inode_size(parent,
++                      unionfs_copy_attr_times(dir);
++                      fsstack_copy_inode_size(dir,
 +                                              lower_parent_dentry->d_inode);
 +
 +                      /* update number of links on parent directory */
-+                      parent->i_nlink = unionfs_get_nlinks(parent);
++                      dir->i_nlink = unionfs_get_nlinks(dir);
 +              }
 +
 +              err = make_dir_opaque(dentry, dbstart(dentry));
@@ -5584,39 +5419,35 @@ index 0000000..a05b412
 +              unionfs_copy_attr_times(dentry->d_inode);
 +              unionfs_postcopyup_setmnt(dentry);
 +      }
-+      unionfs_check_inode(parent);
++      unionfs_check_inode(dir);
 +      unionfs_check_dentry(dentry);
-+      unionfs_unlock_dentry(dentry->d_parent);
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +
 +      return err;
 +}
 +
-+static int unionfs_mknod(struct inode *parent, struct dentry *dentry, int mode,
++static int unionfs_mknod(struct inode *dir, struct dentry *dentry, int mode,
 +                       dev_t dev)
 +{
 +      int err = 0;
 +      struct dentry *lower_dentry = NULL;
 +      struct dentry *wh_dentry = NULL;
 +      struct dentry *lower_parent_dentry = NULL;
++      struct dentry *parent;
 +      char *name = NULL;
 +      int valid = 0;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
-+      unionfs_lock_dentry(dentry->d_parent, UNIONFS_DMUTEX_PARENT);
 +
-+      valid = __unionfs_d_revalidate_chain(dentry->d_parent, NULL, false);
++      valid = __unionfs_d_revalidate(dentry, parent, false);
 +      if (unlikely(!valid)) {
 +              err = -ESTALE;
 +              goto out;
 +      }
-+      if (unlikely(dentry->d_inode &&
-+                   !__unionfs_d_revalidate_one_locked(dentry, NULL, false))) {
-+              err = -ESTALE;
-+              goto out;
-+      }
 +
 +      /*
 +       * It's only a bug if this dentry was not negative and couldn't be
@@ -5624,7 +5455,7 @@ index 0000000..a05b412
 +       */
 +      BUG_ON(!valid && dentry->d_inode);
 +
-+      lower_dentry = find_writeable_branch(parent, dentry);
++      lower_dentry = find_writeable_branch(dir, dentry);
 +      if (IS_ERR(lower_dentry)) {
 +              err = PTR_ERR(lower_dentry);
 +              goto out;
@@ -5638,13 +5469,13 @@ index 0000000..a05b412
 +
 +      err = vfs_mknod(lower_parent_dentry->d_inode, lower_dentry, mode, dev);
 +      if (!err) {
-+              err = PTR_ERR(unionfs_interpose(dentry, parent->i_sb, 0));
++              err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0));
 +              if (!err) {
-+                      unionfs_copy_attr_times(parent);
-+                      fsstack_copy_inode_size(parent,
++                      unionfs_copy_attr_times(dir);
++                      fsstack_copy_inode_size(dir,
 +                                              lower_parent_dentry->d_inode);
 +                      /* update no. of links on parent directory */
-+                      parent->i_nlink = unionfs_get_nlinks(parent);
++                      dir->i_nlink = unionfs_get_nlinks(dir);
 +              }
 +      }
 +
@@ -5656,29 +5487,22 @@ index 0000000..a05b412
 +
 +      if (!err) {
 +              unionfs_postcopyup_setmnt(dentry);
-+              unionfs_check_inode(parent);
++              unionfs_check_inode(dir);
 +              unionfs_check_dentry(dentry);
 +      }
-+      unionfs_unlock_dentry(dentry->d_parent);
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +      return err;
 +}
 +
-+static int unionfs_readlink(struct dentry *dentry, char __user *buf,
-+                          int bufsiz)
++/* requires sb, dentry, and parent to already be locked */
++static int __unionfs_readlink(struct dentry *dentry, char __user *buf,
++                            int bufsiz)
 +{
 +      int err;
 +      struct dentry *lower_dentry;
 +
-+      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
-+      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
-+
-+      if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
-+              err = -ESTALE;
-+              goto out;
-+      }
-+
 +      lower_dentry = unionfs_lower_dentry(dentry);
 +
 +      if (!lower_dentry->d_inode->i_op ||
@@ -5689,34 +5513,50 @@ index 0000000..a05b412
 +
 +      err = lower_dentry->d_inode->i_op->readlink(lower_dentry,
 +                                                  buf, bufsiz);
-+      if (err > 0)
++      if (err >= 0)
 +              fsstack_copy_attr_atime(dentry->d_inode,
 +                                      lower_dentry->d_inode);
 +
 +out:
++      return err;
++}
++
++static int unionfs_readlink(struct dentry *dentry, char __user *buf,
++                          int bufsiz)
++{
++      int err;
++      struct dentry *parent;
++
++      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
++      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
++
++      if (unlikely(!__unionfs_d_revalidate(dentry, parent, false))) {
++              err = -ESTALE;
++              goto out;
++      }
++
++      err = __unionfs_readlink(dentry, buf, bufsiz);
++
++out:
 +      unionfs_check_dentry(dentry);
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +
 +      return err;
 +}
 +
-+/*
-+ * unionfs_follow_link takes a dentry, but it is simple.  It only needs to
-+ * allocate some memory and then call our ->readlink method.  Our
-+ * unionfs_readlink *does* lock our dentry and revalidate the dentry.
-+ * Therefore, we do not have to lock our dentry here, to prevent a deadlock;
-+ * nor do we need to revalidate it either.  It is safe to not lock our
-+ * dentry here, nor revalidate it, because unionfs_follow_link does not do
-+ * anything (prior to calling ->readlink) which could become inconsistent
-+ * due to branch management.  We also don't need to lock our super because
-+ * this function isn't affected by branch-management.
-+ */
 +static void *unionfs_follow_link(struct dentry *dentry, struct nameidata *nd)
 +{
 +      char *buf;
 +      int len = PAGE_SIZE, err;
 +      mm_segment_t old_fs;
++      struct dentry *parent;
++
++      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
++      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
 +
 +      /* This is freed by the put_link method assuming a successful call. */
 +      buf = kmalloc(len, GFP_KERNEL);
@@ -5728,7 +5568,7 @@ index 0000000..a05b412
 +      /* read the symlink, and then we will follow it */
 +      old_fs = get_fs();
 +      set_fs(KERNEL_DS);
-+      err = dentry->d_inode->i_op->readlink(dentry, (char __user *)buf, len);
++      err = __unionfs_readlink(dentry, buf, len);
 +      set_fs(old_fs);
 +      if (err < 0) {
 +              kfree(buf);
@@ -5740,23 +5580,29 @@ index 0000000..a05b412
 +      err = 0;
 +
 +out:
-+      if (!err) {
-+              unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
++      if (err >= 0) {
++              unionfs_check_nd(nd);
 +              unionfs_check_dentry(dentry);
-+              unionfs_unlock_dentry(dentry);
 +      }
-+      unionfs_check_nd(nd);
++
++      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
++      unionfs_read_unlock(dentry->d_sb);
++
 +      return ERR_PTR(err);
 +}
 +
-+/* FIXME: We may not have to lock here */
++/* this @nd *IS* still used */
 +static void unionfs_put_link(struct dentry *dentry, struct nameidata *nd,
 +                           void *cookie)
 +{
-+      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
++      struct dentry *parent;
 +
++      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
-+      if (unlikely(!__unionfs_d_revalidate_chain(dentry, nd, false)))
++
++      if (unlikely(!__unionfs_d_revalidate(dentry, parent, false)))
 +              printk(KERN_ERR
 +                     "unionfs: put_link failed to revalidate dentry\n");
 +
@@ -5764,10 +5610,48 @@ index 0000000..a05b412
 +      unionfs_check_nd(nd);
 +      kfree(nd_get_link(nd));
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +}
 +
 +/*
++ * This is a variant of fs/namei.c:permission() or inode_permission() which
++ * skips over EROFS tests (because we perform copyup on EROFS).
++ */
++static int __inode_permission(struct inode *inode, int mask)
++{
++      int retval;
++
++      /* nobody gets write access to an immutable file */
++      if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
++              return -EACCES;
++
++      /* Ordinary permission routines do not understand MAY_APPEND. */
++      if (inode->i_op && inode->i_op->permission) {
++              retval = inode->i_op->permission(inode, mask);
++              if (!retval) {
++                      /*
++                       * Exec permission on a regular file is denied if none
++                       * of the execute bits are set.
++                       *
++                       * This check should be done by the ->permission()
++                       * method.
++                       */
++                      if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode) &&
++                          !(inode->i_mode & S_IXUGO))
++                              return -EACCES;
++              }
++      } else {
++              retval = generic_permission(inode, mask, NULL);
++      }
++      if (retval)
++              return retval;
++
++      return security_inode_permission(inode,
++                      mask & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND));
++}
++
++/*
 + * Don't grab the superblock read-lock in unionfs_permission, which prevents
 + * a deadlock with the branch-management "add branch" code (which grabbed
 + * the write lock).  It is safe to not grab the read lock here, because even
@@ -5775,17 +5659,18 @@ index 0000000..a05b412
 + * unionfs_permission, or anything it calls, will use stale branch
 + * information.
 + */
-+static int unionfs_permission(struct inode *inode, int mask,
-+                            struct nameidata *nd)
++static int unionfs_permission(struct inode *inode, int mask)
 +{
 +      struct inode *lower_inode = NULL;
 +      int err = 0;
 +      int bindex, bstart, bend;
 +      const int is_file = !S_ISDIR(inode->i_mode);
 +      const int write_mask = (mask & MAY_WRITE) && !(mask & MAY_READ);
++      struct inode *inode_grabbed = igrab(inode);
++      struct dentry *dentry = d_find_alias(inode);
 +
-+      if (nd)
-+              unionfs_lock_dentry(nd->path.dentry, UNIONFS_DMUTEX_CHILD);
++      if (dentry)
++              unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
 +
 +      if (!UNIONFS_I(inode)->lower_inodes) {
 +              if (is_file)    /* dirs can be unlinked but chdir'ed to */
@@ -5824,12 +5709,14 @@ index 0000000..a05b412
 +               * We check basic permissions, but we ignore any conditions
 +               * such as readonly file systems or branches marked as
 +               * readonly, because those conditions should lead to a
-+               * copyup taking place later on.
++               * copyup taking place later on.  However, if user never had
++               * access to the file, then no copyup could ever take place.
 +               */
-+              err = permission(lower_inode, mask, nd);
-+              if (err && bindex > 0) {
++              err = __inode_permission(lower_inode, mask);
++              if (err && err != -EACCES && err != EPERM && bindex > 0) {
 +                      umode_t mode = lower_inode->i_mode;
-+                      if (is_robranch_super(inode->i_sb, bindex) &&
++                      if ((is_robranch_super(inode->i_sb, bindex) ||
++                           IS_RDONLY(lower_inode)) &&
 +                          (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
 +                              err = 0;
 +                      if (IS_COPYUP_ERR(err))
@@ -5858,9 +5745,11 @@ index 0000000..a05b412
 +
 +out:
 +      unionfs_check_inode(inode);
-+      unionfs_check_nd(nd);
-+      if (nd)
-+              unionfs_unlock_dentry(nd->path.dentry);
++      if (dentry) {
++              unionfs_unlock_dentry(dentry);
++              dput(dentry);
++      }
++      iput(inode_grabbed);
 +      return err;
 +}
 +
@@ -5868,15 +5757,17 @@ index 0000000..a05b412
 +{
 +      int err = 0;
 +      struct dentry *lower_dentry;
++      struct dentry *parent;
 +      struct inode *inode;
 +      struct inode *lower_inode;
 +      int bstart, bend, bindex;
 +      loff_t size;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
 +
-+      if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
++      if (unlikely(!__unionfs_d_revalidate(dentry, parent, false))) {
 +              err = -ESTALE;
 +              goto out;
 +      }
@@ -5893,11 +5784,20 @@ index 0000000..a05b412
 +              ia->ia_valid &= ~ATTR_MODE;
 +
 +      lower_dentry = unionfs_lower_dentry(dentry);
-+      BUG_ON(!lower_dentry);  /* should never happen after above revalidate */
++      if (!lower_dentry) { /* should never happen after above revalidate */
++              err = -EINVAL;
++              goto out;
++      }
++      lower_inode = unionfs_lower_inode(inode);
++
++      /* check if user has permission to change lower inode */
++      err = inode_change_ok(lower_inode, ia);
++      if (err)
++              goto out;
 +
 +      /* copyup if the file is on a read only branch */
 +      if (is_robranch_super(dentry->d_sb, bstart)
-+          || IS_RDONLY(lower_dentry->d_inode)) {
++          || IS_RDONLY(lower_inode)) {
 +              /* check if we have a branch to copy up to */
 +              if (bstart <= 0) {
 +                      err = -EACCES;
@@ -5910,7 +5810,7 @@ index 0000000..a05b412
 +                      size = i_size_read(inode);
 +              /* copyup to next available branch */
 +              for (bindex = bstart - 1; bindex >= 0; bindex--) {
-+                      err = copyup_dentry(dentry->d_parent->d_inode,
++                      err = copyup_dentry(parent->d_inode,
 +                                          dentry, bstart, bindex,
 +                                          dentry->d_name.name,
 +                                          dentry->d_name.len,
@@ -5920,12 +5820,11 @@ index 0000000..a05b412
 +              }
 +              if (err)
 +                      goto out;
-+              /* get updated lower_dentry after copyup */
++              /* get updated lower_dentry/inode after copyup */
 +              lower_dentry = unionfs_lower_dentry(dentry);
++              lower_inode = unionfs_lower_inode(inode);
 +      }
 +
-+      lower_inode = unionfs_lower_inode(inode);
-+
 +      /*
 +       * If shrinking, first truncate upper level to cancel writing dirty
 +       * pages beyond the new eof; and also if its' maxbytes is more
@@ -5945,9 +5844,9 @@ index 0000000..a05b412
 +      }
 +
 +      /* notify the (possibly copied-up) lower inode */
-+      mutex_lock(&lower_dentry->d_inode->i_mutex);
++      mutex_lock(&lower_inode->i_mutex);
 +      err = notify_change(lower_dentry, ia);
-+      mutex_unlock(&lower_dentry->d_inode->i_mutex);
++      mutex_unlock(&lower_inode->i_mutex);
 +      if (err)
 +              goto out;
 +
@@ -5971,6 +5870,7 @@ index 0000000..a05b412
 +      if (!err)
 +              unionfs_check_dentry(dentry);
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +
 +      return err;
@@ -6016,10 +5916,10 @@ index 0000000..a05b412
 +};
 diff --git a/fs/unionfs/lookup.c b/fs/unionfs/lookup.c
 new file mode 100644
-index 0000000..0ae7f3a
+index 0000000..6877b8c
 --- /dev/null
 +++ b/fs/unionfs/lookup.c
-@@ -0,0 +1,573 @@
+@@ -0,0 +1,569 @@
 +/*
 + * Copyright (c) 2003-2008 Erez Zadok
 + * Copyright (c) 2003-2006 Charles P. Wright
@@ -6091,13 +5991,12 @@ index 0000000..0ae7f3a
 + * Returns: 0 (ok), or -ERRNO if an error occurred.
 + * XXX: get rid of _partial_lookup and make callers call _lookup_full directly
 + */
-+int unionfs_partial_lookup(struct dentry *dentry)
++int unionfs_partial_lookup(struct dentry *dentry, struct dentry *parent)
 +{
 +      struct dentry *tmp;
-+      struct nameidata nd = { .flags = 0 };
 +      int err = -ENOSYS;
 +
-+      tmp = unionfs_lookup_full(dentry, &nd, INTERPOSE_PARTIAL);
++      tmp = unionfs_lookup_full(dentry, parent, INTERPOSE_PARTIAL);
 +
 +      if (!tmp) {
 +              err = 0;
@@ -6274,9 +6173,6 @@ index 0000000..0ae7f3a
 +              nd->intent.open.file = file;
 +#endif /* ALLOC_LOWER_ND_FILE */
 +              break;
-+      case LOOKUP_ACCESS:
-+              nd->flags = flags;
-+              break;
 +      default:
 +              /*
 +               * We should never get here, for now.
@@ -6313,7 +6209,7 @@ index 0000000..0ae7f3a
 + * dentry's info, which the caller must unlock.
 + */
 +struct dentry *unionfs_lookup_full(struct dentry *dentry,
-+                                 struct nameidata *nd_unused, int lookupmode)
++                                 struct dentry *parent, int lookupmode)
 +{
 +      int err = 0;
 +      struct dentry *lower_dentry = NULL;
@@ -6321,7 +6217,6 @@ index 0000000..0ae7f3a
 +      struct vfsmount *lower_dir_mnt;
 +      struct dentry *wh_lower_dentry = NULL;
 +      struct dentry *lower_dir_dentry = NULL;
-+      struct dentry *parent_dentry = NULL;
 +      struct dentry *d_interposed = NULL;
 +      int bindex, bstart, bend, bopaque;
 +      int opaque, num_positive = 0;
@@ -6335,6 +6230,7 @@ index 0000000..0ae7f3a
 +       * new_dentry_private_data already locked.
 +       */
 +      verify_locked(dentry);
++      verify_locked(parent);
 +
 +      /* must initialize dentry operations */
 +      dentry->d_op = &unionfs_dops;
@@ -6342,7 +6238,6 @@ index 0000000..0ae7f3a
 +      /* We never partial lookup the root directory. */
 +      if (IS_ROOT(dentry))
 +              goto out;
-+      parent_dentry = dget_parent(dentry);
 +
 +      name = dentry->d_name.name;
 +      namelen = dentry->d_name.len;
@@ -6354,9 +6249,9 @@ index 0000000..0ae7f3a
 +      }
 +
 +      /* Now start the actual lookup procedure. */
-+      bstart = dbstart(parent_dentry);
-+      bend = dbend(parent_dentry);
-+      bopaque = dbopaque(parent_dentry);
++      bstart = dbstart(parent);
++      bend = dbend(parent);
++      bopaque = dbopaque(parent);
 +      BUG_ON(bstart < 0);
 +
 +      /* adjust bend to bopaque if needed */
@@ -6381,7 +6276,7 @@ index 0000000..0ae7f3a
 +              }
 +
 +              lower_dir_dentry =
-+                      unionfs_lower_dentry_idx(parent_dentry, bindex);
++                      unionfs_lower_dentry_idx(parent, bindex);
 +              /* if the lower dentry's parent does not exist, skip this */
 +              if (!lower_dir_dentry || !lower_dir_dentry->d_inode)
 +                      continue;
@@ -6406,7 +6301,7 @@ index 0000000..0ae7f3a
 +              dput(wh_lower_dentry);
 +
 +              /* Now do regular lookup; lookup @name */
-+              lower_dir_mnt = unionfs_lower_mnt_idx(parent_dentry, bindex);
++              lower_dir_mnt = unionfs_lower_mnt_idx(parent, bindex);
 +              lower_mnt = NULL; /* XXX: needed? */
 +
 +              lower_dentry = __lookup_one(lower_dir_dentry, lower_dir_mnt,
@@ -6417,7 +6312,9 @@ index 0000000..0ae7f3a
 +                      goto out_free;
 +              }
 +              unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry);
-+              BUG_ON(!lower_mnt);
++              if (!lower_mnt)
++                      lower_mnt = unionfs_mntget(dentry->d_sb->s_root,
++                                                 bindex);
 +              unionfs_set_lower_mnt_idx(dentry, bindex, lower_mnt);
 +
 +              /* adjust dbstart/end */
@@ -6451,7 +6348,7 @@ index 0000000..0ae7f3a
 +              dbend(dentry) = bindex;
 +
 +              /* update parent directory's atime with the bindex */
-+              fsstack_copy_attr_atime(parent_dentry->d_inode,
++              fsstack_copy_attr_atime(parent->d_inode,
 +                                      lower_dir_dentry->d_inode);
 +      }
 +
@@ -6488,7 +6385,7 @@ index 0000000..0ae7f3a
 +              if (unionfs_lower_dentry_idx(dentry, bindex))
 +                      goto out;
 +              lower_dir_dentry =
-+                      unionfs_lower_dentry_idx(parent_dentry, bindex);
++                      unionfs_lower_dentry_idx(parent, bindex);
 +              if (!lower_dir_dentry || !lower_dir_dentry->d_inode)
 +                      goto out;
 +              if (!S_ISDIR(lower_dir_dentry->d_inode->i_mode))
@@ -6588,7 +6485,6 @@ index 0000000..0ae7f3a
 +              BUG_ON(dbstart(d_interposed) >= 0 && dbend(d_interposed) < 0);
 +      }
 +
-+      dput(parent_dentry);
 +      if (!err && d_interposed)
 +              return d_interposed;
 +      return ERR_PTR(err);
@@ -7764,10 +7660,10 @@ index 0000000..06d5374
 +}
 diff --git a/fs/unionfs/rename.c b/fs/unionfs/rename.c
 new file mode 100644
-index 0000000..da7d589
+index 0000000..800d9ee
 --- /dev/null
 +++ b/fs/unionfs/rename.c
-@@ -0,0 +1,478 @@
+@@ -0,0 +1,515 @@
 +/*
 + * Copyright (c) 2003-2008 Erez Zadok
 + * Copyright (c) 2003-2006 Charles P. Wright
@@ -7792,7 +7688,8 @@ index 0000000..da7d589
 + * This is a helper function for rename, used when rename ends up with hosed
 + * over dentries and we need to revert.
 + */
-+static int unionfs_refresh_lower_dentry(struct dentry *dentry, int bindex)
++static int unionfs_refresh_lower_dentry(struct dentry *dentry,
++                                      struct dentry *parent, int bindex)
 +{
 +      struct dentry *lower_dentry;
 +      struct dentry *lower_parent;
@@ -7800,9 +7697,7 @@ index 0000000..da7d589
 +
 +      verify_locked(dentry);
 +
-+      unionfs_lock_dentry(dentry->d_parent, UNIONFS_DMUTEX_CHILD);
-+      lower_parent = unionfs_lower_dentry_idx(dentry->d_parent, bindex);
-+      unionfs_unlock_dentry(dentry->d_parent);
++      lower_parent = unionfs_lower_dentry_idx(parent, bindex);
 +
 +      BUG_ON(!S_ISDIR(lower_parent->d_inode->i_mode));
 +
@@ -7831,7 +7726,9 @@ index 0000000..da7d589
 +}
 +
 +static int __unionfs_rename(struct inode *old_dir, struct dentry *old_dentry,
++                          struct dentry *old_parent,
 +                          struct inode *new_dir, struct dentry *new_dentry,
++                          struct dentry *new_parent,
 +                          int bindex)
 +{
 +      int err = 0;
@@ -7846,7 +7743,7 @@ index 0000000..da7d589
 +
 +      if (!lower_new_dentry) {
 +              lower_new_dentry =
-+                      create_parents(new_dentry->d_parent->d_inode,
++                      create_parents(new_parent->d_inode,
 +                                     new_dentry, new_dentry->d_name.name,
 +                                     bindex);
 +              if (IS_ERR(lower_new_dentry)) {
@@ -7925,15 +7822,16 @@ index 0000000..da7d589
 + */
 +static int do_unionfs_rename(struct inode *old_dir,
 +                           struct dentry *old_dentry,
++                           struct dentry *old_parent,
 +                           struct inode *new_dir,
-+                           struct dentry *new_dentry)
++                           struct dentry *new_dentry,
++                           struct dentry *new_parent)
 +{
 +      int err = 0;
 +      int bindex, bwh_old;
 +      int old_bstart, old_bend;
 +      int new_bstart, new_bend;
 +      int do_copyup = -1;
-+      struct dentry *parent_dentry;
 +      int local_err = 0;
 +      int eio = 0;
 +      int revert = 0;
@@ -7941,13 +7839,13 @@ index 0000000..da7d589
 +      old_bstart = dbstart(old_dentry);
 +      bwh_old = old_bstart;
 +      old_bend = dbend(old_dentry);
-+      parent_dentry = old_dentry->d_parent;
 +
 +      new_bstart = dbstart(new_dentry);
 +      new_bend = dbend(new_dentry);
 +
 +      /* Rename source to destination. */
-+      err = __unionfs_rename(old_dir, old_dentry, new_dir, new_dentry,
++      err = __unionfs_rename(old_dir, old_dentry, old_parent,
++                             new_dir, new_dentry, new_parent,
 +                             old_bstart);
 +      if (err) {
 +              if (!IS_COPYUP_ERR(err))
@@ -7976,11 +7874,11 @@ index 0000000..da7d589
 +                      err = vfs_unlink(unlink_dir_dentry->d_inode,
 +                                       unlink_dentry);
 +
-+              fsstack_copy_attr_times(new_dentry->d_parent->d_inode,
++              fsstack_copy_attr_times(new_parent->d_inode,
 +                                      unlink_dir_dentry->d_inode);
 +              /* propagate number of hard-links */
-+              new_dentry->d_parent->d_inode->i_nlink =
-+                      unionfs_get_nlinks(new_dentry->d_parent->d_inode);
++              new_parent->d_inode->i_nlink =
++                      unionfs_get_nlinks(new_parent->d_inode);
 +
 +              unlock_dir(unlink_dir_dentry);
 +              if (!err) {
@@ -8002,7 +7900,7 @@ index 0000000..da7d589
 +                       * copyup the file into some left directory, so that
 +                       * you can rename it
 +                       */
-+                      err = copyup_dentry(old_dentry->d_parent->d_inode,
++                      err = copyup_dentry(old_parent->d_inode,
 +                                          old_dentry, old_bstart, bindex,
 +                                          old_dentry->d_name.name,
 +                                          old_dentry->d_name.len, NULL,
@@ -8011,8 +7909,8 @@ index 0000000..da7d589
 +                      if (err)
 +                              continue;
 +                      bwh_old = bindex;
-+                      err = __unionfs_rename(old_dir, old_dentry,
-+                                             new_dir, new_dentry,
++                      err = __unionfs_rename(old_dir, old_dentry, old_parent,
++                                             new_dir, new_dentry, new_parent,
 +                                             bindex);
 +                      break;
 +              }
@@ -8051,14 +7949,16 @@ index 0000000..da7d589
 +
 +revert:
 +      /* Do revert here. */
-+      local_err = unionfs_refresh_lower_dentry(new_dentry, old_bstart);
++      local_err = unionfs_refresh_lower_dentry(new_dentry, new_parent,
++                                               old_bstart);
 +      if (local_err) {
 +              printk(KERN_ERR "unionfs: revert failed in rename: "
 +                     "the new refresh failed\n");
 +              eio = -EIO;
 +      }
 +
-+      local_err = unionfs_refresh_lower_dentry(old_dentry, old_bstart);
++      local_err = unionfs_refresh_lower_dentry(old_dentry, old_parent,
++                                               old_bstart);
 +      if (local_err) {
 +              printk(KERN_ERR "unionfs: revert failed in rename: "
 +                     "the old refresh failed\n");
@@ -8082,8 +7982,9 @@ index 0000000..da7d589
 +              goto revert_out;
 +      }
 +
-+      local_err = __unionfs_rename(new_dir, new_dentry,
-+                                   old_dir, old_dentry, old_bstart);
++      local_err = __unionfs_rename(new_dir, new_dentry, new_parent,
++                                   old_dir, old_dentry, old_parent,
++                                   old_bstart);
 +
 +      /* If we can't fix it, then we cop-out with -EIO. */
 +      if (local_err) {
@@ -8091,10 +7992,12 @@ index 0000000..da7d589
 +              eio = -EIO;
 +      }
 +
-+      local_err = unionfs_refresh_lower_dentry(new_dentry, bindex);
++      local_err = unionfs_refresh_lower_dentry(new_dentry, new_parent,
++                                               bindex);
 +      if (local_err)
 +              eio = -EIO;
-+      local_err = unionfs_refresh_lower_dentry(old_dentry, bindex);
++      local_err = unionfs_refresh_lower_dentry(old_dentry, old_parent,
++                                               bindex);
 +      if (local_err)
 +              eio = -EIO;
 +
@@ -8110,11 +8013,11 @@ index 0000000..da7d589
 + * return EXDEV to the user-space utility that caused this, and let the
 + * user-space recurse and ask us to copy up each file separately.
 + */
-+static int may_rename_dir(struct dentry *dentry)
++static int may_rename_dir(struct dentry *dentry, struct dentry *parent)
 +{
 +      int err, bstart;
 +
-+      err = check_empty(dentry, NULL);
++      err = check_empty(dentry, parent, NULL);
 +      if (err == -ENOTEMPTY) {
 +              if (is_robranch(dentry))
 +                      return -EXDEV;
@@ -8127,41 +8030,60 @@ index 0000000..da7d589
 +              return 0;
 +
 +      dbstart(dentry) = bstart + 1;
-+      err = check_empty(dentry, NULL);
++      err = check_empty(dentry, parent, NULL);
 +      dbstart(dentry) = bstart;
 +      if (err == -ENOTEMPTY)
 +              err = -EXDEV;
 +      return err;
 +}
 +
++/*
++ * The locking rules in unionfs_rename are complex.  We could use a simpler
++ * superblock-level name-space lock for renames and copy-ups.
++ */
 +int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 +                 struct inode *new_dir, struct dentry *new_dentry)
 +{
 +      int err = 0;
 +      struct dentry *wh_dentry;
++      struct dentry *old_parent, *new_parent;
++      int valid = true;
 +
 +      unionfs_read_lock(old_dentry->d_sb, UNIONFS_SMUTEX_CHILD);
++      old_parent = dget_parent(old_dentry);
++      new_parent = dget_parent(new_dentry);
++      /* un/lock parent dentries only if they differ from old/new_dentry */
++      if (old_parent != old_dentry &&
++          old_parent != new_dentry)
++              unionfs_lock_dentry(old_parent, UNIONFS_DMUTEX_REVAL_PARENT);
++      if (new_parent != old_dentry &&
++          new_parent != new_dentry &&
++          new_parent != old_parent)
++              unionfs_lock_dentry(new_parent, UNIONFS_DMUTEX_REVAL_CHILD);
 +      unionfs_double_lock_dentry(old_dentry, new_dentry);
 +
-+      if (unlikely(!__unionfs_d_revalidate_chain(old_dentry, NULL, false))) {
++      valid = __unionfs_d_revalidate(old_dentry, old_parent, false);
++      if (!valid) {
 +              err = -ESTALE;
 +              goto out;
 +      }
-+      if (unlikely(!d_deleted(new_dentry) && new_dentry->d_inode &&
-+                   !__unionfs_d_revalidate_chain(new_dentry, NULL, false))) {
-+              err = -ESTALE;
-+              goto out;
++      if (!d_deleted(new_dentry) && new_dentry->d_inode) {
++              valid = __unionfs_d_revalidate(new_dentry, new_parent, false);
++              if (!valid) {
++                      err = -ESTALE;
++                      goto out;
++              }
 +      }
 +
 +      if (!S_ISDIR(old_dentry->d_inode->i_mode))
-+              err = unionfs_partial_lookup(old_dentry);
++              err = unionfs_partial_lookup(old_dentry, old_parent);
 +      else
-+              err = may_rename_dir(old_dentry);
++              err = may_rename_dir(old_dentry, old_parent);
 +
 +      if (err)
 +              goto out;
 +
-+      err = unionfs_partial_lookup(new_dentry);
++      err = unionfs_partial_lookup(new_dentry, new_parent);
 +      if (err)
 +              goto out;
 +
@@ -8183,7 +8105,7 @@ index 0000000..da7d589
 +              if (S_ISDIR(new_dentry->d_inode->i_mode)) {
 +                      struct unionfs_dir_state *namelist = NULL;
 +                      /* check if this unionfs directory is empty or not */
-+                      err = check_empty(new_dentry, &namelist);
++                      err = check_empty(new_dentry, new_parent, &namelist);
 +                      if (err)
 +                              goto out;
 +
@@ -8199,7 +8121,8 @@ index 0000000..da7d589
 +              }
 +      }
 +
-+      err = do_unionfs_rename(old_dir, old_dentry, new_dir, new_dentry);
++      err = do_unionfs_rename(old_dir, old_dentry, old_parent,
++                              new_dir, new_dentry, new_parent);
 +      if (err)
 +              goto out;
 +
@@ -8241,14 +8164,24 @@ index 0000000..da7d589
 +out:
 +      if (err)                /* clear the new_dentry stuff created */
 +              d_drop(new_dentry);
-+      unionfs_unlock_dentry(new_dentry);
-+      unionfs_unlock_dentry(old_dentry);
++
++      unionfs_double_unlock_dentry(old_dentry, new_dentry);
++      if (new_parent != old_dentry &&
++          new_parent != new_dentry &&
++          new_parent != old_parent)
++              unionfs_unlock_dentry(new_parent);
++      if (old_parent != old_dentry &&
++          old_parent != new_dentry)
++              unionfs_unlock_dentry(old_parent);
++      dput(new_parent);
++      dput(old_parent);
 +      unionfs_read_unlock(old_dentry->d_sb);
++
 +      return err;
 +}
 diff --git a/fs/unionfs/sioq.c b/fs/unionfs/sioq.c
 new file mode 100644
-index 0000000..e6f15a0
+index 0000000..dd45e39
 --- /dev/null
 +++ b/fs/unionfs/sioq.c
 @@ -0,0 +1,101 @@
@@ -8341,7 +8274,7 @@ index 0000000..e6f15a0
 +      struct sioq_args *args = container_of(work, struct sioq_args, work);
 +      struct symlink_args *s = &args->symlink;
 +
-+      args->err = vfs_symlink(s->parent, s->dentry, s->symbuf, s->mode);
++      args->err = vfs_symlink(s->parent, s->dentry, s->symbuf);
 +      complete(&args->comp);
 +}
 +
@@ -8355,10 +8288,10 @@ index 0000000..e6f15a0
 +}
 diff --git a/fs/unionfs/sioq.h b/fs/unionfs/sioq.h
 new file mode 100644
-index 0000000..e072bf7
+index 0000000..679a0df
 --- /dev/null
 +++ b/fs/unionfs/sioq.h
-@@ -0,0 +1,92 @@
+@@ -0,0 +1,91 @@
 +/*
 + * Copyright (c) 2006-2008 Erez Zadok
 + * Copyright (c) 2006      Charles P. Wright
@@ -8410,7 +8343,6 @@ index 0000000..e072bf7
 +      struct inode *parent;
 +      struct dentry *dentry;
 +      char *symbuf;
-+      umode_t mode;
 +};
 +
 +struct unlink_args {
@@ -8554,10 +8486,10 @@ index 0000000..8747d20
 +}
 diff --git a/fs/unionfs/super.c b/fs/unionfs/super.c
 new file mode 100644
-index 0000000..1f4b3f4
+index 0000000..8115079
 --- /dev/null
 +++ b/fs/unionfs/super.c
-@@ -0,0 +1,1042 @@
+@@ -0,0 +1,1047 @@
 +/*
 + * Copyright (c) 2003-2008 Erez Zadok
 + * Copyright (c) 2003-2006 Charles P. Wright
@@ -8684,7 +8616,7 @@ index 0000000..1f4b3f4
 +                             bindex, branch_count(sb, bindex));
 +                      leaks = 1;
 +              }
-+      BUG_ON(leaks != 0);
++      WARN_ON(leaks != 0);
 +
 +      /* decrement lower super references */
 +      for (bindex = bstart; bindex <= bend; bindex++) {
@@ -8710,13 +8642,17 @@ index 0000000..1f4b3f4
 +      int err = 0;
 +      struct super_block *sb;
 +      struct dentry *lower_dentry;
++      struct dentry *parent;
++      bool valid;
 +
 +      sb = dentry->d_sb;
 +
 +      unionfs_read_lock(sb, UNIONFS_SMUTEX_CHILD);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
 +
-+      if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
++      valid = __unionfs_d_revalidate(dentry, parent, false);
++      if (unlikely(!valid)) {
 +              err = -ESTALE;
 +              goto out;
 +      }
@@ -8745,6 +8681,7 @@ index 0000000..1f4b3f4
 +out:
 +      unionfs_check_dentry(dentry);
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(sb);
 +      return err;
 +}
@@ -9460,7 +9397,7 @@ index 0000000..1f4b3f4
 +}
 +
 +/* unionfs inode cache constructor */
-+static void init_once(struct kmem_cache *cachep, void *obj)
++static void init_once(void *obj)
 +{
 +      struct unionfs_inode_info *i = obj;
 +
@@ -9602,10 +9539,10 @@ index 0000000..1f4b3f4
 +};
 diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h
 new file mode 100644
-index 0000000..1b9c3f7
+index 0000000..00e6dec
 --- /dev/null
 +++ b/fs/unionfs/union.h
-@@ -0,0 +1,607 @@
+@@ -0,0 +1,650 @@
 +/*
 + * Copyright (c) 2003-2008 Erez Zadok
 + * Copyright (c) 2003-2006 Charles P. Wright
@@ -9900,11 +9837,56 @@ index 0000000..1b9c3f7
 +{
 +      BUG_ON(d1 == d2);
 +      if (d1 < d2) {
-+              unionfs_lock_dentry(d2, UNIONFS_DMUTEX_CHILD);
 +              unionfs_lock_dentry(d1, UNIONFS_DMUTEX_PARENT);
++              unionfs_lock_dentry(d2, UNIONFS_DMUTEX_CHILD);
 +      } else {
-+              unionfs_lock_dentry(d1, UNIONFS_DMUTEX_CHILD);
 +              unionfs_lock_dentry(d2, UNIONFS_DMUTEX_PARENT);
++              unionfs_lock_dentry(d1, UNIONFS_DMUTEX_CHILD);
++      }
++}
++
++static inline void unionfs_double_unlock_dentry(struct dentry *d1,
++                                              struct dentry *d2)
++{
++      BUG_ON(d1 == d2);
++      if (d1 < d2) { /* unlock in reverse order than double_lock_dentry */
++              unionfs_unlock_dentry(d1);
++              unionfs_unlock_dentry(d2);
++      } else {
++              unionfs_unlock_dentry(d2);
++              unionfs_unlock_dentry(d1);
++      }
++}
++
++static inline void unionfs_double_lock_parents(struct dentry *p1,
++                                             struct dentry *p2)
++{
++      if (p1 == p2) {
++              unionfs_lock_dentry(p1, UNIONFS_DMUTEX_REVAL_PARENT);
++              return;
++      }
++      if (p1 < p2) {
++              unionfs_lock_dentry(p1, UNIONFS_DMUTEX_REVAL_PARENT);
++              unionfs_lock_dentry(p2, UNIONFS_DMUTEX_REVAL_CHILD);
++      } else {
++              unionfs_lock_dentry(p2, UNIONFS_DMUTEX_REVAL_PARENT);
++              unionfs_lock_dentry(p1, UNIONFS_DMUTEX_REVAL_CHILD);
++      }
++}
++
++static inline void unionfs_double_unlock_parents(struct dentry *p1,
++                                               struct dentry *p2)
++{
++      if (p1 == p2) {
++              unionfs_unlock_dentry(p1);
++              return;
++      }
++      if (p1 < p2) { /* unlock in reverse order of double_lock_parents */
++              unionfs_unlock_dentry(p1);
++              unionfs_unlock_dentry(p2);
++      } else {
++              unionfs_unlock_dentry(p2);
++              unionfs_unlock_dentry(p1);
 +      }
 +}
 +
@@ -9924,9 +9906,10 @@ index 0000000..1b9c3f7
 +                                   const char *name, int bindex);
 +
 +/* partial lookup */
-+extern int unionfs_partial_lookup(struct dentry *dentry);
++extern int unionfs_partial_lookup(struct dentry *dentry,
++                                struct dentry *parent);
 +extern struct dentry *unionfs_lookup_full(struct dentry *dentry,
-+                                        struct nameidata *nd_unused,
++                                        struct dentry *parent,
 +                                        int lookupmode);
 +
 +/* copies a file from dbstart to newbindex branch */
@@ -9944,7 +9927,7 @@ index 0000000..1b9c3f7
 +extern void unionfs_postcopyup_release(struct dentry *dentry);
 +
 +/* Is this directory empty: 0 if it is empty, -ENOTEMPTY if not. */
-+extern int check_empty(struct dentry *dentry,
++extern int check_empty(struct dentry *dentry, struct dentry *parent,
 +                     struct unionfs_dir_state **namelist);
 +/* whiteout and opaque directory helpers */
 +extern char *alloc_whname(const char *name, int len);
@@ -9971,8 +9954,8 @@ index 0000000..1b9c3f7
 +extern int unionfs_getlk(struct file *file, struct file_lock *fl);
 +
 +/* Common file operations. */
-+extern int unionfs_file_revalidate(struct file *file, bool willwrite);
-+extern int unionfs_file_revalidate_locked(struct file *file, bool willwrite);
++extern int unionfs_file_revalidate(struct file *file, struct dentry *parent,
++                                 bool willwrite);
 +extern int unionfs_open(struct inode *inode, struct file *file);
 +extern int unionfs_file_release(struct inode *inode, struct file *file);
 +extern int unionfs_flush(struct file *file, fl_owner_t id);
@@ -9989,11 +9972,8 @@ index 0000000..1b9c3f7
 +extern int unionfs_unlink(struct inode *dir, struct dentry *dentry);
 +extern int unionfs_rmdir(struct inode *dir, struct dentry *dentry);
 +
-+extern bool __unionfs_d_revalidate_one_locked(struct dentry *dentry,
-+                                            struct nameidata *nd,
-+                                            bool willwrite);
-+extern bool __unionfs_d_revalidate_chain(struct dentry *dentry,
-+                                       struct nameidata *nd, bool willwrite);
++extern bool __unionfs_d_revalidate(struct dentry *dentry,
++                                 struct dentry *parent, bool willwrite);
 +extern bool is_negative_lower(const struct dentry *dentry);
 +extern bool is_newer_lower(const struct dentry *dentry);
 +extern void purge_sb_data(struct super_block *sb);
@@ -10215,10 +10195,10 @@ index 0000000..1b9c3f7
 +#endif        /* not _UNION_H_ */
 diff --git a/fs/unionfs/unlink.c b/fs/unionfs/unlink.c
 new file mode 100644
-index 0000000..623f68d
+index 0000000..6634c4b
 --- /dev/null
 +++ b/fs/unionfs/unlink.c
-@@ -0,0 +1,277 @@
+@@ -0,0 +1,282 @@
 +/*
 + * Copyright (c) 2003-2008 Erez Zadok
 + * Copyright (c) 2003-2006 Charles P. Wright
@@ -10265,14 +10245,15 @@ index 0000000..623f68d
 + *     as as per Documentation/filesystems/unionfs/concepts.txt).
 + *
 + */
-+static int unionfs_unlink_whiteout(struct inode *dir, struct dentry *dentry)
++static int unionfs_unlink_whiteout(struct inode *dir, struct dentry *dentry,
++                                 struct dentry *parent)
 +{
 +      struct dentry *lower_dentry;
 +      struct dentry *lower_dir_dentry;
 +      int bindex;
 +      int err = 0;
 +
-+      err = unionfs_partial_lookup(dentry);
++      err = unionfs_partial_lookup(dentry, parent);
 +      if (err)
 +              goto out;
 +
@@ -10344,29 +10325,26 @@ index 0000000..623f68d
 +{
 +      int err = 0;
 +      struct inode *inode = dentry->d_inode;
++      struct dentry *parent;
 +      int valid;
 +
 +      BUG_ON(S_ISDIR(inode->i_mode));
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
-+      unionfs_lock_dentry(dentry->d_parent, UNIONFS_DMUTEX_PARENT);
 +
-+      valid = __unionfs_d_revalidate_chain(dentry->d_parent, NULL, false);
-+      if (unlikely(!valid)) {
-+              err = -ESTALE;
-+              goto out;
-+      }
-+      valid = __unionfs_d_revalidate_one_locked(dentry, NULL, false);
++      valid = __unionfs_d_revalidate(dentry, parent, false);
 +      if (unlikely(!valid)) {
 +              err = -ESTALE;
 +              goto out;
 +      }
 +      unionfs_check_dentry(dentry);
 +
-+      err = unionfs_unlink_whiteout(dir, dentry);
++      err = unionfs_unlink_whiteout(dir, dentry, parent);
 +      /* call d_drop so the system "forgets" about us */
 +      if (!err) {
 +              unionfs_postcopyup_release(dentry);
++              unionfs_postcopyup_setmnt(parent);
 +              if (inode->i_nlink == 0) /* drop lower inodes */
 +                      iput_lowers_all(inode, false);
 +              d_drop(dentry);
@@ -10382,8 +10360,8 @@ index 0000000..623f68d
 +              unionfs_check_dentry(dentry);
 +              unionfs_check_inode(dir);
 +      }
-+      unionfs_unlock_dentry(dentry->d_parent);
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +      return err;
 +}
@@ -10429,19 +10407,23 @@ index 0000000..623f68d
 +{
 +      int err = 0;
 +      struct unionfs_dir_state *namelist = NULL;
++      struct dentry *parent;
 +      int dstart, dend;
++      bool valid;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
 +
-+      if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
++      valid = __unionfs_d_revalidate(dentry, parent, false);
++      if (unlikely(!valid)) {
 +              err = -ESTALE;
 +              goto out;
 +      }
 +      unionfs_check_dentry(dentry);
 +
 +      /* check if this unionfs directory is empty or not */
-+      err = check_empty(dentry, &namelist);
++      err = check_empty(dentry, parent, &namelist);
 +      if (err)
 +              goto out;
 +
@@ -10487,18 +10469,21 @@ index 0000000..623f68d
 +              d_drop(dentry);
 +              /* update our lower vfsmnts, in case a copyup took place */
 +              unionfs_postcopyup_setmnt(dentry);
++              unionfs_check_dentry(dentry);
++              unionfs_check_inode(dir);
 +      }
 +
 +      if (namelist)
 +              free_rdstate(namelist);
 +
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +      return err;
 +}
 diff --git a/fs/unionfs/whiteout.c b/fs/unionfs/whiteout.c
 new file mode 100644
-index 0000000..94b5241
+index 0000000..0934ac8
 --- /dev/null
 +++ b/fs/unionfs/whiteout.c
 @@ -0,0 +1,577 @@
@@ -10638,7 +10623,7 @@ index 0000000..94b5241
 +      struct dentry *parent, *lower_parent, *wh_dentry;
 +
 +      parent = dget_parent(dentry);
-+      unionfs_lock_dentry(parent, UNIONFS_DMUTEX_WHITEOUT);
++
 +      bstart = dbstart(parent);
 +      bend = dbend(parent);
 +      wh_dentry = ERR_PTR(-ENOENT);
@@ -10655,7 +10640,7 @@ index 0000000..94b5241
 +              dput(wh_dentry);
 +              wh_dentry = ERR_PTR(-ENOENT);
 +      }
-+      unionfs_unlock_dentry(parent);
++
 +      dput(parent);
 +
 +      return wh_dentry;
@@ -10742,7 +10727,7 @@ index 0000000..94b5241
 +              err = -EIO;
 +              printk(KERN_ERR "unionfs: found both whiteout and regular "
 +                     "file in directory %s (branch %d)\n",
-+                     lower_dentry->d_parent->d_name.name, bindex);
++                     lower_dir_dentry->d_name.name, bindex);
 +              goto out_dput;
 +      }
 +
@@ -10959,7 +10944,7 @@ index 0000000..94b5241
 +      lower_dir = lower_dir_dentry->d_inode;
 +      BUG_ON(!S_ISDIR(lower_dir->i_mode));
 +
-+      if (!permission(lower_dir, MAY_WRITE | MAY_EXEC, NULL)) {
++      if (!inode_permission(lower_dir, MAY_WRITE | MAY_EXEC)) {
 +              err = do_delete_whiteouts(dentry, bindex, namelist);
 +      } else {
 +              args.deletewh.namelist = namelist;
@@ -10996,7 +10981,7 @@ index 0000000..94b5241
 +
 +      mutex_lock(&lower_inode->i_mutex);
 +
-+      if (!permission(lower_inode, MAY_EXEC, NULL)) {
++      if (!inode_permission(lower_inode, MAY_EXEC)) {
 +              wh_lower_dentry =
 +                      lookup_one_len(UNIONFS_DIR_OPAQUE, lower_dentry,
 +                                     sizeof(UNIONFS_DIR_OPAQUE) - 1);
@@ -11081,10 +11066,10 @@ index 0000000..94b5241
 +}
 diff --git a/fs/unionfs/xattr.c b/fs/unionfs/xattr.c
 new file mode 100644
-index 0000000..93a8fce
+index 0000000..e2215c1
 --- /dev/null
 +++ b/fs/unionfs/xattr.c
-@@ -0,0 +1,153 @@
+@@ -0,0 +1,173 @@
 +/*
 + * Copyright (c) 2003-2008 Erez Zadok
 + * Copyright (c) 2003-2006 Charles P. Wright
@@ -11130,12 +11115,16 @@ index 0000000..93a8fce
 +                       size_t size)
 +{
 +      struct dentry *lower_dentry = NULL;
++      struct dentry *parent;
 +      int err = -EOPNOTSUPP;
++      bool valid;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
 +
-+      if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
++      valid = __unionfs_d_revalidate(dentry, parent, false);
++      if (unlikely(!valid)) {
 +              err = -ESTALE;
 +              goto out;
 +      }
@@ -11147,6 +11136,7 @@ index 0000000..93a8fce
 +out:
 +      unionfs_check_dentry(dentry);
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +      return err;
 +}
@@ -11159,12 +11149,16 @@ index 0000000..93a8fce
 +                   const void *value, size_t size, int flags)
 +{
 +      struct dentry *lower_dentry = NULL;
++      struct dentry *parent;
 +      int err = -EOPNOTSUPP;
++      bool valid;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
 +
-+      if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
++      valid = __unionfs_d_revalidate(dentry, parent, false);
++      if (unlikely(!valid)) {
 +              err = -ESTALE;
 +              goto out;
 +      }
@@ -11177,6 +11171,7 @@ index 0000000..93a8fce
 +out:
 +      unionfs_check_dentry(dentry);
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +      return err;
 +}
@@ -11188,12 +11183,16 @@ index 0000000..93a8fce
 +int unionfs_removexattr(struct dentry *dentry, const char *name)
 +{
 +      struct dentry *lower_dentry = NULL;
++      struct dentry *parent;
 +      int err = -EOPNOTSUPP;
++      bool valid;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
 +
-+      if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
++      valid = __unionfs_d_revalidate(dentry, parent, false);
++      if (unlikely(!valid)) {
 +              err = -ESTALE;
 +              goto out;
 +      }
@@ -11205,6 +11204,7 @@ index 0000000..93a8fce
 +out:
 +      unionfs_check_dentry(dentry);
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +      return err;
 +}
@@ -11216,13 +11216,17 @@ index 0000000..93a8fce
 +ssize_t unionfs_listxattr(struct dentry *dentry, char *list, size_t size)
 +{
 +      struct dentry *lower_dentry = NULL;
++      struct dentry *parent;
 +      int err = -EOPNOTSUPP;
 +      char *encoded_list = NULL;
++      bool valid;
 +
 +      unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
++      parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT);
 +      unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
 +
-+      if (unlikely(!__unionfs_d_revalidate_chain(dentry, NULL, false))) {
++      valid = __unionfs_d_revalidate(dentry, parent, false);
++      if (unlikely(!valid)) {
 +              err = -ESTALE;
 +              goto out;
 +      }
@@ -11235,6 +11239,7 @@ index 0000000..93a8fce
 +out:
 +      unionfs_check_dentry(dentry);
 +      unionfs_unlock_dentry(dentry);
++      unionfs_unlock_parent(dentry, parent);
 +      unionfs_read_unlock(dentry->d_sb);
 +      return err;
 +}
This page took 0.246934 seconds and 4 git commands to generate.