Hi,

in private mail with Erez, Erez also came up with the same solution, except
that he only changes the first case, which is enough (tested it):

===================================================================
--- unionfs-2.5.8_for_2.6.32.28/fs/unionfs/inode.c
+++ unionfs-2.5.8_for_2.6.32.28/fs/unionfs/inode.c
@@ -927,7 +927,11 @@
                err = -EINVAL;
                goto out;
        }
-       lower_inode = unionfs_lower_inode(inode);
+       /*
+        * Get the lower inode directly from lower dentry, in case ibstart
+        * is -1 (which happens when the file is open but unlinked.
+        */
+       lower_inode = lower_dentry->d_inode;

        /* check if user has permission to change lower inode */
        err = inode_change_ok(lower_inode, ia);

One remaining code branch in unionfs_setattr() still exhibits a further issue,
with a very similar test case (open() is changed to not create the file):

  #include <unistd.h>
  #include <fcntl.h>
  int main(int argc, char *argv[]) {
    int fd = open(argv[1],O_RDWR|O_APPEND);
    unlink(argv[1]);
    ftruncate(fd, 0);
  }

When the file, on which open+unlink+ftruncate runs, is found on a read-only
branch, a copyup needs to be done, which is taken care of by this code in
unionfs_setattr():

        /* copyup if the file is on a read only branch */
        if (is_robranch_super(dentry->d_sb, bstart)
            || __is_rdonly(lower_inode)) {
                /* check if we have a branch to copy up to */
                if (bstart <= 0) {
                        err = -EACCES;
                        goto out;
                }

                if (ia->ia_valid & ATTR_SIZE)
                        size = ia->ia_size;
                else
                        size = i_size_read(inode);
                /* copyup to next available branch */
                for (bindex = bstart - 1; bindex >= 0; bindex--) {
                        err = copyup_dentry(parent->d_inode,
                                            dentry, bstart, bindex,
                                            dentry->d_name.name,
                                            dentry->d_name.len,
                                            NULL, size);
                        if (!err)
                                break;
                }
                if (err)
                        goto out;
                /* get updated lower_dentry/inode after copyup */
                lower_dentry = unionfs_lower_dentry(dentry);
                lower_inode = unionfs_lower_inode(inode);
        }

Note that, during the unlink() system call in the test case, unionfs created a
whiteout entry in the a writeable branch above the read-only branch to indicate
that the file on the read-only branch is marked deleted inside unionfs.

After the call to copyup_dentry() in the loop above succeeded, there is now a
new file in the same branch as the the whiteout, which is contradicting the
whiteout and unionfs_setattr().

copyup_dentry() also creates a new lower_dentry which is retrieved by this line:

                /* get updated lower_dentry/inode after copyup */
                lower_dentry = unionfs_lower_dentry(dentry);

This is then used to call notify_change() for the setattr:

        mutex_lock(&lower_dentry->d_inode->i_mutex);
        err = notify_change(lower_dentry, &lower_ia);
        mutex_unlock(&lower_dentry->d_inode->i_mutex);

But if the dentry still exists (as it does now) after the return from setattr,
unionfs_lookup() will find this dentry and ignore the whiteout, showing the
file e.g. in ls -l, but with no file attributes because the stat() will not
work, likely due to the whiteout, so you see a file with ??? as perms etc,
and you cannot open, remove or replace the looked-up file.

Adding an call to "dput(lower_dentry);" for this specific case before the
call to unionfs_setfattr() causes unionfs_lookup to find the whiteout and
the file is not shown in ls after the call to ftruncate(). If possible, it
would likely be best to have the copyup_dentry take care of this but I do
not know if this is possible. For now, I can only present this finding.

Best Regards,
Bernhard

PS: There is a remaining open bugzilla entry for this crash:
https://bugzilla.filesystems.org/show_bug.cgi?id=637

PS: If somebody is only reading the archive, here are further links to
discussions on this issue (see the launchpad entries, they are 3 years old):
http://www.mail-archive.com/[email protected]/msg03061.html
http://www.mail-archive.com/[email protected]/msg03114.html
_______________________________________________
unionfs mailing list: http://unionfs.filesystems.org/
[email protected]
http://www.fsl.cs.sunysb.edu/mailman/listinfo/unionfs

Reply via email to