In message <h4n8aj$fi...@ger.gmane.org>, "pas...@pabr.org" writes:
> Bart van der Meulen wrote:
> >>  [<c0120b14>] do_page_fault+0x224/0x5c0
> >>  [<c04bc302>] error_code+0x72/0x78
> >>  [<c019f07a>] notify_change+0x2da/0x310
> >>  [<c0188b37>] do_truncate+0x67/0x90
> >>  [<c0188cb5>] do_sys_ftruncate+0x155/0x170
> >>  [<c0188ceb>] sys_ftruncate64+0x1b/0x20
> >>  [<c0105412>] sysenter_past_esp+0x5f/0x85
> 
> This might be related to my unlink+ftruncate bug, which
> is easily reproducible:
> https://bugzilla.filesystems.org/show_bug.cgi?id=633
> 
> > Can somebody give me some pointers on how to debug the problem further?
> 
> strace the application to confirm that it calls unlink
> and then ftruncate on the same file descriptor.
> 
> Pascal

I love easily reproducible oopses. :-)

Please try the patch below, which is also available from here:

        https://bugzilla.fsl.cs.sunysb.edu/attachment.cgi?id=235

The fix adds special handling to unlinked but open inodes, on which the user
tries to setattr.  Special handling is needed because this inode no longer
has a name in the namespace (i.e., no dentry).

Cheers,
Erez.

##############################################################################

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 <e...@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)
 {
_______________________________________________
unionfs mailing list: http://unionfs.filesystems.org/
unionfs@mail.fsl.cs.sunysb.edu
http://www.fsl.cs.sunysb.edu/mailman/listinfo/unionfs

Reply via email to