GIT: unionfs2-2.6.27.y: bugfix: fixed interpose to check if d_splice_alias returns a dentry

Erez Zadok ezk at fsl.cs.sunysb.edu
Thu Aug 12 23:16:18 EDT 2010


commit e76bc1a3aee681e3575125ccf4f9b93459b96bce
Author: Erez_Zadok <ezk at cs.sunysb.edu>
Date:   Sat May 19 18:22:53 2007 -0400

    bugfix: fixed interpose to check if d_splice_alias returns a dentry
    
    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.)

diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index d0fc43e..5f5dccc 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -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);
diff --git a/fs/unionfs/lookup.c b/fs/unionfs/lookup.c
index 253fe4a..ffdc553 100644
--- a/fs/unionfs/lookup.c
+++ b/fs/unionfs/lookup.c
@@ -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);
 }
 
diff --git a/fs/unionfs/main.c b/fs/unionfs/main.c
index ffcd040..f5bec5b 100644
--- a/fs/unionfs/main.c
+++ b/fs/unionfs/main.c
@@ -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;
diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h
index b328289..d4e004f 100644
--- a/fs/unionfs/union.h
+++ b/fs/unionfs/union.h
@@ -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. */


More information about the unionfs-cvs mailing list