commit d463f7066f3d3c1aaef33d7b4a67b1506cb9289a
Author: Erez Zadok <[email protected]>
Date: Tue Sep 22 17:46:00 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 <[email protected]>
diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index 946846f..07a2c17 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -838,7 +838,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))
@@ -932,7 +932,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;
@@ -979,14 +979,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)
{
_______________________________________________
unionfs-cvs mailing list: http://unionfs.filesystems.org/
[email protected]
http://www.fsl.cs.sunysb.edu/mailman/listinfo/unionfs-cvs