GIT: unionfs2-2.6.27.y: Unionfs: handle an open-unlink-ftruncate sequence

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


commit 82bdc707b3c33ccd0d9fbaac9d121dd399b55421
Author: Erez Zadok <ezk at cs.sunysb.edu>
Date:   Tue Sep 22 17:46:01 2009 -0400

    Unionfs: handle an open-unlink-ftruncate sequence
    
    If someone calls open(), then unlink(), then ftruncate() on a file (rare,
    but possible), then it's possible for unionfs to get an unlinked inode which
    doesn't have an inode->i_sb and its inode->i_ino is zero.  Don't oops in
    that case.
    
    Signed-off-by: Erez Zadok <ezk at cs.sunysb.edu>

diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index 7e86273..bd5a3b3 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -835,7 +835,7 @@ static int unionfs_permission(struct inode *inode, int mask)
 		if (err && err != -EACCES && err != EPERM && bindex > 0) {
 			umode_t mode = lower_inode->i_mode;
 			if ((is_robranch_super(inode->i_sb, bindex) ||
-			     IS_RDONLY(lower_inode)) &&
+			     __is_rdonly(lower_inode)) &&
 			    (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
 				err = 0;
 			if (IS_COPYUP_ERR(err))
@@ -930,7 +930,7 @@ static int unionfs_setattr(struct dentry *dentry, struct iattr *ia)
 
 	/* copyup if the file is on a read only branch */
 	if (is_robranch_super(dentry->d_sb, bstart)
-	    || IS_RDONLY(lower_inode)) {
+	    || __is_rdonly(lower_inode)) {
 		/* check if we have a branch to copy up to */
 		if (bstart <= 0) {
 			err = -EACCES;
@@ -977,14 +977,20 @@ static int unionfs_setattr(struct dentry *dentry, struct iattr *ia)
 	}
 
 	/* notify the (possibly copied-up) lower inode */
-	mutex_lock(&lower_inode->i_mutex);
+	/*
+	 * Note: we use lower_dentry->d_inode, because lower_inode may be
+	 * unlinked (no inode->i_sb and i_ino==0.  This happens if someone
+	 * tries to open(), unlink(), then ftruncate() a file.
+	 */
+	mutex_lock(&lower_dentry->d_inode->i_mutex);
 	err = notify_change(lower_dentry, ia);
-	mutex_unlock(&lower_inode->i_mutex);
+	mutex_unlock(&lower_dentry->d_inode->i_mutex);
 	if (err)
 		goto out;
 
 	/* get attributes from the first lower inode */
-	unionfs_copy_attr_all(inode, lower_inode);
+	if (ibstart(inode) >= 0)
+		unionfs_copy_attr_all(inode, lower_inode);
 	/*
 	 * unionfs_copy_attr_all will copy the lower times to our inode if
 	 * the lower ones are newer (useful for cache coherency).  However,
diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h
index 1b2b86f..9663506 100644
--- a/fs/unionfs/union.h
+++ b/fs/unionfs/union.h
@@ -485,6 +485,15 @@ static inline int set_branchperms(struct super_block *sb, int index, int perms)
 	return perms;
 }
 
+/* check if readonly lower inode, but possibly unlinked (no inode->i_sb) */
+static inline int __is_rdonly(const struct inode *inode)
+{
+	/* if unlinked, can't be readonly (?) */
+	if (!inode->i_sb)
+		return 0;
+	return IS_RDONLY(inode);
+
+}
 /* Is this file on a read-only branch? */
 static inline int is_robranch_super(const struct super_block *sb, int index)
 {


More information about the unionfs-cvs mailing list