bugfix: fixed interpose to check if d_splice_alias returns a dentry
authorErez_Zadok <ezk@cs.sunysb.edu>
Sat, 19 May 2007 22:22:53 +0000 (18:22 -0400)
committerErez Zadok <ezk@cs.sunysb.edu>
Tue, 11 Nov 2014 02:39:47 +0000 (21:39 -0500)
Also in unionfs_interpose, ensure that this new dentry's private data is
correct.  unionfs_interpose now returns a dentry if d_splice_alias has
returned a dentry.  (This is a reimplementation of the same fix from the ODF
code base.)

fs/unionfs/inode.c
fs/unionfs/lookup.c
fs/unionfs/main.c
fs/unionfs/union.h

index d0fc43e150546bdc22c6e36f6f657976aae6c4a3..5f5dccca5a8f58701f136681a0a637fb89c105c9 100644 (file)
@@ -162,7 +162,12 @@ static int unionfs_create(struct inode *parent, struct dentry *dentry,
                                                     wh_dentry);
                        wh_dentry = NULL;
 
-                       err = unionfs_interpose(dentry, parent->i_sb, 0);
+                       /*
+                        * Only INTERPOSE_LOOKUP can return a value other
+                        * than 0 on err.
+                        */
+                       err = PTR_ERR(unionfs_interpose(dentry,
+                                                       parent->i_sb, 0));
                        goto out;
                }
        }
@@ -204,7 +209,12 @@ static int unionfs_create(struct inode *parent, struct dentry *dentry,
                        if (!IS_COPYUP_ERR(err))
                                break;
                } else {
-                       err = unionfs_interpose(dentry, parent->i_sb, 0);
+                       /*
+                        * Only INTERPOSE_LOOKUP can return a value other
+                        * than 0 on err.
+                        */
+                       err = PTR_ERR(unionfs_interpose(dentry,
+                                                       parent->i_sb, 0));
                        if (!err) {
                                fsstack_copy_attr_times(parent,
                                                        hidden_parent_dentry->
@@ -519,7 +529,12 @@ static int unionfs_symlink(struct inode *dir, struct dentry *dentry,
                        if (!IS_COPYUP_ERR(err))
                                break;
                } else {
-                       err = unionfs_interpose(dentry, dir->i_sb, 0);
+                       /*
+                        * Only INTERPOSE_LOOKUP can return a value other
+                        * than 0 on err.
+                        */
+                       err = PTR_ERR(unionfs_interpose(dentry,
+                                                       dir->i_sb, 0));
                        if (!err) {
                                fsstack_copy_attr_times(dir,
                                                        hidden_dir_dentry->
@@ -654,7 +669,11 @@ static int unionfs_mkdir(struct inode *parent, struct dentry *dentry, int mode)
                }
                set_dbend(dentry, bindex);
 
-               err = unionfs_interpose(dentry, parent->i_sb, 0);
+               /*
+                * Only INTERPOSE_LOOKUP can return a value other than 0 on
+                * err.
+                */
+               err = PTR_ERR(unionfs_interpose(dentry, parent->i_sb, 0));
                if (!err) {
                        fsstack_copy_attr_times(parent,
                                                hidden_parent_dentry->d_inode);
@@ -776,7 +795,11 @@ static int unionfs_mknod(struct inode *dir, struct dentry *dentry, int mode,
                        break;
                }
 
-               err = unionfs_interpose(dentry, dir->i_sb, 0);
+               /*
+                * Only INTERPOSE_LOOKUP can return a value other than 0 on
+                * err.
+                */
+               err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0));
                if (!err) {
                        fsstack_copy_attr_times(dir,
                                                hidden_parent_dentry->d_inode);
index 253fe4a41ce11460c6b5eb6513b178072559bffc..ffdc5535a2b7cdc529913fa974fcd8024b046933 100644 (file)
@@ -79,6 +79,7 @@ struct dentry *unionfs_lookup_backend(struct dentry *dentry,
        struct dentry *wh_hidden_dentry = NULL;
        struct dentry *hidden_dir_dentry = NULL;
        struct dentry *parent_dentry = NULL;
+       struct dentry *d_interposed = NULL;
        int bindex, bstart, bend, bopaque;
        int dentry_count = 0;   /* Number of positive dentries. */
        int first_dentry_offset = -1; /* -1 is uninitialized */
@@ -366,7 +367,16 @@ out_positive:
                bend = dbend(dentry);
        }
 
-       err = unionfs_interpose(dentry, dentry->d_sb, lookupmode);
+       /*
+        * Interpose can return a dentry if d_splice returned a different
+        * dentry.
+        */
+       d_interposed = unionfs_interpose(dentry, dentry->d_sb, lookupmode);
+       if (IS_ERR(d_interposed))
+               err = PTR_ERR(d_interposed);
+       else if (d_interposed)
+               dentry = d_interposed;
+
        if (err)
                goto out_drop;
 
@@ -402,6 +412,8 @@ out:
        dput(parent_dentry);
        if (locked_child || (err && allocated_new_info))
                unionfs_unlock_dentry(dentry);
+       if (!err && d_interposed)
+               return d_interposed;
        return ERR_PTR(err);
 }
 
index ffcd04074d57682280d311d56d0cd6acecd0183b..f5bec5b47a9bc33195c4a18ab9e4245f21622df6 100644 (file)
@@ -26,7 +26,8 @@
  *
  * @sb: unionfs's super_block
  */
-int unionfs_interpose(struct dentry *dentry, struct super_block *sb, int flag)
+struct dentry *unionfs_interpose(struct dentry *dentry, struct super_block *sb,
+                                int flag)
 {
        struct inode *hidden_inode;
        struct dentry *hidden_dentry;
@@ -34,6 +35,8 @@ int unionfs_interpose(struct dentry *dentry, struct super_block *sb, int flag)
        struct inode *inode;
        int is_negative_dentry = 1;
        int bindex, bstart, bend;
+       int skipped = 1;
+       struct dentry *spliced = NULL;
 
        verify_locked(dentry);
 
@@ -80,11 +83,12 @@ int unionfs_interpose(struct dentry *dentry, struct super_block *sb, int flag)
                        err = -EACCES;
                        goto out;
                }
-
                if (atomic_read(&inode->i_count) > 1)
                        goto skip;
        }
 
+fill_i_info:
+       skipped = 0;
        for (bindex = bstart; bindex <= bend; bindex++) {
                hidden_dentry = unionfs_lower_dentry_idx(dentry, bindex);
                if (!hidden_dentry) {
@@ -132,6 +136,8 @@ int unionfs_interpose(struct dentry *dentry, struct super_block *sb, int flag)
        fsstack_copy_attr_all(inode, hidden_inode, unionfs_get_nlinks);
        fsstack_copy_inode_size(inode, hidden_inode);
 
+       if (spliced)
+               goto out_spliced;
 skip:
        /* only (our) lookup wants to do a d_add */
        switch (flag) {
@@ -140,7 +146,25 @@ skip:
                d_instantiate(dentry, inode);
                break;
        case INTERPOSE_LOOKUP:
-               err = PTR_ERR(d_splice_alias(inode, dentry));
+               spliced = d_splice_alias(inode, dentry);
+               if (IS_ERR(spliced))
+                       err = PTR_ERR(spliced);
+
+               /*
+                * d_splice can return a dentry if it was disconnected and
+                * had to be moved.  We must ensure that the private data of
+                * the new dentry is correct and that the inode info was
+                * filled properly.  Finally we must return this new dentry.
+                */
+               else if (spliced && spliced != dentry) {
+                       spliced->d_op = &unionfs_dops;
+                       spliced->d_fsdata = dentry->d_fsdata;
+                       dentry->d_fsdata = NULL;
+                       dentry = spliced;
+                       if (skipped)
+                               goto fill_i_info;
+                       goto out_spliced;
+               }
                break;
        case INTERPOSE_REVAL:
                /* Do nothing. */
@@ -149,9 +173,13 @@ skip:
                printk(KERN_ERR "unionfs: invalid interpose flag passed!");
                BUG();
        }
+       goto out;
 
+out_spliced:
+       if (!err)
+               return spliced;
 out:
-       return err;
+       return ERR_PTR(err);
 }
 
 /* like interpose above, but for an already existing dentry */
@@ -625,8 +653,11 @@ static int unionfs_read_super(struct super_block *sb, void *raw_data,
        /* Set the generation number to one, since this is for the mount. */
        atomic_set(&UNIONFS_D(sb->s_root)->generation, 1);
 
-       /* call interpose to create the upper level inode */
-       err = unionfs_interpose(sb->s_root, sb, 0);
+       /*
+        * Call interpose to create the upper level inode.  Only
+        * INTERPOSE_LOOKUP can return a value other than 0 on err.
+        */
+       err = PTR_ERR(unionfs_interpose(sb->s_root, sb, 0));
        unionfs_unlock_dentry(sb->s_root);
        if (!err)
                goto out;
index b3282892daf2d7deddaa4b2aa935c671a2f5be94..d4e004fb1e96118b85dbc7d77e0f3c405a23b906 100644 (file)
@@ -318,8 +318,8 @@ extern int __unionfs_d_revalidate_chain(struct dentry *dentry,
 #define INTERPOSE_REVAL_NEG    3
 #define INTERPOSE_PARTIAL      4
 
-extern int unionfs_interpose(struct dentry *this_dentry,
-                            struct super_block *sb, int flag);
+extern struct dentry *unionfs_interpose(struct dentry *this_dentry,
+                                       struct super_block *sb, int flag);
 
 #ifdef CONFIG_UNION_FS_XATTR
 /* Extended attribute functions. */