commit b736a6881f199d38364624cb1283dce5ce70144c
Author: Yiannis Pericleous <[EMAIL PROTECTED]>
Date:   Sat May 19 16:41:10 2007 -0400

    Verify and maintain fanout invariants.
    
    This somewhat long patch calls various invariant-checking (debugging)
    functions in all places where the fanout invariants should hold.  The three
    invariant-checking functions, __unionfs_check_{inode,dentry,file}, perform
    exhaustive sanity checking on the fan-out of various Unionfs objects.  We
    check that no lower objects exist outside the start/end branch range; that
    all objects within are non-NULL (with some allowed exceptions); that for
    every lower file there's a lower dentry+inode; that the start/end ranges
    match for all corresponding lower objects; that open files/symlinks have
    only one lower objects, but directories can have several; and more.
    
    The rest of this patch actually fixes many places where these invariants did
    not hold, which could lead to bugs or corruptions under heavy loads,
    multi-threaded workloads, dynamic branch-management, and mmap operations.
    Most of the bugs related to actions involving copyups and whiteouts.  With
    these fixes, the entire Unionfs regression suite passes without a single
    invariant violated.
    
    Conflicts:
    
        fs/unionfs/commonfops.c
        fs/unionfs/file.c
        fs/unionfs/inode.c
        fs/unionfs/unlink.c

diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c
index 
a306f1e0a157a4ea84336be2025e3a3d5f8221f5..919378ad9d3a56c3129f008995a629acc8fe3997
 100644
--- a/fs/unionfs/commonfops.c
+++ b/fs/unionfs/commonfops.c
@@ -78,11 +78,18 @@ static int copyup_deleted_file(struct fi
 
        /* bring it to the same state as an unlinked file */
        hidden_dentry = unionfs_lower_dentry_idx(dentry, dbstart(dentry));
+       if (!unionfs_lower_inode_idx(dentry->d_inode, bindex)) {
+               atomic_inc(&hidden_dentry->d_inode->i_count);
+               unionfs_set_lower_inode_idx(dentry->d_inode, bindex,
+                                           hidden_dentry->d_inode);
+       }
        hidden_dir_dentry = lock_parent(hidden_dentry);
        err = vfs_unlink(hidden_dir_dentry->d_inode, hidden_dentry);
        unlock_dir(hidden_dir_dentry);
 
 out:
+       if (!err)
+               unionfs_check_dentry(dentry);
        return err;
 }
 
@@ -322,6 +329,8 @@ static int do_delayed_copyup(struct file
 
        BUG_ON(!S_ISREG(file->f_dentry->d_inode->i_mode));
 
+       unionfs_check_file(file);
+       unionfs_check_dentry(dentry);
        for (bindex = bstart - 1; bindex >= 0; bindex--) {
                if (!d_deleted(file->f_dentry))
                        err = copyup_file(parent_inode, file, bstart,
@@ -343,9 +352,25 @@ static int do_delayed_copyup(struct file
                                fput(unionfs_lower_file_idx(file, bindex));
                                unionfs_set_lower_file_idx(file, bindex, NULL);
                        }
+                       if (unionfs_lower_mnt_idx(dentry, bindex)) {
+                               unionfs_mntput(dentry, bindex);
+                               unionfs_set_lower_mnt_idx(dentry, bindex, NULL);
+                       }
+                       if (unionfs_lower_dentry_idx(dentry, bindex)) {
+                               BUG_ON(!dentry->d_inode);
+                               iput(unionfs_lower_inode_idx(dentry->d_inode, 
bindex));
+                               unionfs_set_lower_inode_idx(dentry->d_inode, 
bindex, NULL);
+                               dput(unionfs_lower_dentry_idx(dentry, bindex));
+                               unionfs_set_lower_dentry_idx(dentry, bindex, 
NULL);
+                       }
                }
-               fbend(file) = bend;
+               /* for reg file, we only open it "once" */
+               fbend(file) = fbstart(file);
+               set_dbend(dentry, dbstart(dentry));
+               ibend(dentry->d_inode) = ibstart(dentry->d_inode);
        }
+       unionfs_check_file(file);
+       unionfs_check_dentry(dentry);
        return err;
 }
 
@@ -439,6 +464,8 @@ out:
                kfree(UNIONFS_F(file)->saved_branch_ids);
        }
 out_nofree:
+       if (!err)
+               unionfs_check_file(file);
        unionfs_unlock_dentry(dentry);
        return err;
 }
@@ -633,6 +660,8 @@ out:
        }
 out_nofree:
        unionfs_read_unlock(inode->i_sb);
+       unionfs_check_inode(inode);
+       unionfs_check_file(file);
        return err;
 }
 
@@ -640,19 +669,21 @@ out_nofree:
 int unionfs_file_release(struct inode *inode, struct file *file)
 {
        struct file *hidden_file = NULL;
-       struct unionfs_file_info *fileinfo;
-       struct unionfs_inode_info *inodeinfo;
+       struct unionfs_file_info *fileinfo = UNIONFS_F(file);
+       struct unionfs_inode_info *inodeinfo = UNIONFS_I(inode);
        struct super_block *sb = inode->i_sb;
        int bindex, bstart, bend;
        int fgen, err = 0;
 
+       unionfs_check_file(file);
        unionfs_read_lock(sb);
+       /*
+        * Yes, we have to revalidate this file even if it's being released.
+        * This is important for open-but-unlinked files, as well as mmap
+        * support.
+        */
        if ((err = unionfs_file_revalidate(file, 1)))
                goto out;
-       fileinfo = UNIONFS_F(file);
-       BUG_ON(file->f_dentry->d_inode != inode);
-       inodeinfo = UNIONFS_I(inode);
-
        /* fput all the hidden files */
        fgen = atomic_read(&fileinfo->generation);
        bstart = fbstart(file);
@@ -776,6 +807,7 @@ long unionfs_ioctl(struct file *file, un
        }
 
 out:
+       unionfs_check_file(file);
        return err;
 }
 
@@ -790,6 +822,7 @@ int unionfs_flush(struct file *file, fl_
 
        if ((err = unionfs_file_revalidate(file, 1)))
                goto out;
+       unionfs_check_file(file);
 
        if (!atomic_dec_and_test(&UNIONFS_I(dentry->d_inode)->totalopens))
                goto out;
@@ -821,5 +854,6 @@ out_lock:
        unionfs_unlock_dentry(dentry);
 out:
        unionfs_read_unlock(file->f_dentry->d_sb);
+       unionfs_check_file(file);
        return err;
 }
diff --git a/fs/unionfs/copyup.c b/fs/unionfs/copyup.c
index 
02aa62aac7606ceb0ebb0be6c81b5327f874ed7e..e93a799aa32fee554743bcdde7c1593649886f99
 100644
--- a/fs/unionfs/copyup.c
+++ b/fs/unionfs/copyup.c
@@ -489,6 +489,25 @@ out_free:
                dput(old_hidden_dentry);
        kfree(symbuf);
 
+       if (err)
+               goto out;
+       if (!S_ISDIR(dentry->d_inode->i_mode)) {
+               unionfs_purge_extras(dentry);
+               if (!unionfs_lower_inode(dentry->d_inode)) {
+                       /*
+                        * If we got here, then we copied up to an
+                        * unlinked-open file, whose name is .unionfsXXXXX.
+                        */
+                       struct inode *inode = new_hidden_dentry->d_inode;
+                       atomic_inc(&inode->i_count);
+                       unionfs_set_lower_inode_idx(dentry->d_inode,
+                                                   ibstart(dentry->d_inode),
+                                                   inode);
+               }
+       }
+       unionfs_inherit_mnt(dentry);
+       unionfs_check_inode(dir);
+       unionfs_check_dentry(dentry);
 out:
        return err;
 }
diff --git a/fs/unionfs/dentry.c b/fs/unionfs/dentry.c
index 
d1ee7921112f9b0c741fab5bb35a3c6bf715e2c7..1653267c1d5a5fa94861e633237f1954bcfecae8
 100644
--- a/fs/unionfs/dentry.c
+++ b/fs/unionfs/dentry.c
@@ -289,9 +289,11 @@ static int unionfs_d_revalidate(struct d
 {
        int err;
 
+       unionfs_check_dentry(dentry);
        unionfs_lock_dentry(dentry);
        err = __unionfs_d_revalidate_chain(dentry, nd);
        unionfs_unlock_dentry(dentry);
+       unionfs_check_dentry(dentry);
 
        return err;
 }
@@ -304,6 +306,7 @@ static void unionfs_d_release(struct den
 {
        int bindex, bstart, bend;
 
+       unionfs_check_dentry(dentry);
        /* this could be a negative dentry, so check first */
        if (!UNIONFS_D(dentry)) {
                printk(KERN_DEBUG "unionfs: dentry without private data: %.*s",
diff --git a/fs/unionfs/file.c b/fs/unionfs/file.c
index 
9f61ded8cc4dd4c69539e10d1c82c89b8894ac45..5e34f6e2024625d47dd0c7eb539f8fc99edd98dd
 100644
--- a/fs/unionfs/file.c
+++ b/fs/unionfs/file.c
@@ -39,6 +39,7 @@ static ssize_t unionfs_read(struct file 
 
 out:
        unionfs_read_unlock(file->f_dentry->d_sb);
+       unionfs_check_file(file);
        return err;
 }
 
@@ -56,6 +57,9 @@ static ssize_t unionfs_aio_read(struct k
                touch_atime(unionfs_lower_mnt(iocb->ki_filp->f_path.dentry),
                            unionfs_lower_dentry(iocb->ki_filp->f_path.dentry));
 
+out:
+       unionfs_read_unlock(file->f_dentry->d_sb);
+       unionfs_check_file(file);
        return err;
 }
 static ssize_t unionfs_write(struct file * file, const char __user * buf,
@@ -71,6 +75,7 @@ static ssize_t unionfs_write(struct file
 
 out:
        unionfs_read_unlock(file->f_dentry->d_sb);
+       unionfs_check_file(file);
        return err;
 }
 
@@ -97,6 +102,7 @@ static int unionfs_mmap(struct file *fil
 
 out:
        unionfs_read_unlock(file->f_dentry->d_sb);
+       unionfs_check_file(file);
        return err;
 }
 
diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index 
8f9d38bb6971b4715889a6097abb8678e1e5944a..4e33e74eabf8716a6b443168cf76410a086fb5ed
 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -111,8 +111,14 @@ static int unionfs_create(struct inode *
 
 
 out:
+       if (!err)
+               unionfs_inherit_mnt(dentry);
        unionfs_unlock_dentry(dentry);
        unionfs_read_unlock(dentry->d_sb);
+
+       unionfs_check_inode(parent);
+       unionfs_check_dentry(dentry->d_parent);
+       unionfs_check_dentry(dentry);
        return err;
 }
 
@@ -137,7 +143,11 @@ static struct dentry *unionfs_lookup(str
                nd->dentry = path_save.dentry;
                nd->mnt = path_save.mnt;
        }
+       if (!IS_ERR(ret))
+               unionfs_inherit_mnt(dentry);
 
+       unionfs_check_inode(parent);
+       unionfs_check_dentry(dentry);
        return ret;
 }
 
@@ -253,9 +263,15 @@ out:
        if (!new_dentry->d_inode)
                d_drop(new_dentry);
 
+       if (!err)
+               unionfs_inherit_mnt(new_dentry);
+
        unionfs_unlock_dentry(new_dentry);
        unionfs_unlock_dentry(old_dentry);
 
+       unionfs_check_inode(dir);
+       unionfs_check_dentry(new_dentry);
+       unionfs_check_dentry(old_dentry);
        return err;
 }
 
@@ -334,7 +350,12 @@ static int unionfs_symlink(struct inode 
                d_drop(dentry);
 
 out:
+       if (!err)
+               unionfs_inherit_mnt(dentry);
        unionfs_unlock_dentry(dentry);
+
+       unionfs_check_inode(dir);
+       unionfs_check_dentry(dentry);
        return err;
 }
 
@@ -428,6 +449,8 @@ out:
        kfree(name);
 
        unionfs_unlock_dentry(dentry);
+       unionfs_check_inode(parent);
+       unionfs_check_dentry(dentry);
        return err;
 }
 
@@ -500,7 +523,12 @@ out:
        if (!dentry->d_inode)
                d_drop(dentry);
 
+       if (!err)
+               unionfs_inherit_mnt(dentry);
        unionfs_unlock_dentry(dentry);
+
+       unionfs_check_inode(dir);
+       unionfs_check_dentry(dentry);
        return err;
 }
 
@@ -529,6 +557,7 @@ static int unionfs_readlink(struct dentr
 
 out:
        unionfs_unlock_dentry(dentry);
+       unionfs_check_dentry(dentry);
        return err;
 }
 
@@ -563,12 +592,14 @@ static void *unionfs_follow_link(struct 
        err = 0;
 
 out:
+       unionfs_check_dentry(dentry);
        return ERR_PTR(err);
 }
 
 static void unionfs_put_link(struct dentry *dentry, struct nameidata *nd,
                             void *cookie)
 {
+       unionfs_check_dentry(dentry);
        kfree(nd_get_link(nd));
 }
 
@@ -692,6 +723,7 @@ static int unionfs_permission(struct ino
 
 out:
        unionfs_read_unlock(inode->i_sb);
+       unionfs_check_inode(inode);
        return err;
 }
 
@@ -766,9 +798,9 @@ static int unionfs_setattr(struct dentry
        hidden_inode = unionfs_lower_inode(dentry->d_inode);
        fsstack_copy_attr_all(inode, hidden_inode, unionfs_get_nlinks);
        fsstack_copy_inode_size(inode, hidden_inode);
-
 out:
        unionfs_unlock_dentry(dentry);
+       unionfs_check_dentry(dentry);
        return err;
 }
 
diff --git a/fs/unionfs/rename.c b/fs/unionfs/rename.c
index 
62731ca385cb5ca64d40623113ca57000836c277..4e106b65756e11107686a7e64b6dd5d2485e9d4c
 100644
--- a/fs/unionfs/rename.c
+++ b/fs/unionfs/rename.c
@@ -383,13 +383,42 @@ out:
        if (err)
                /* clear the new_dentry stuff created */
                d_drop(new_dentry);
-       else
+       else {
                /*
                 * force re-lookup since the dir on ro branch is not renamed,
                 * and hidden dentries still indicate the un-renamed ones.
                 */
                if (S_ISDIR(old_dentry->d_inode->i_mode))
                        atomic_dec(&UNIONFS_D(old_dentry)->generation);
+               else
+                       unionfs_purge_extras(old_dentry);
+               if (new_dentry->d_inode &&
+                   !S_ISDIR(new_dentry->d_inode->i_mode)) {
+                       unionfs_purge_extras(new_dentry);
+                       unionfs_inherit_mnt(new_dentry);
+                       if (!unionfs_lower_inode(new_dentry->d_inode)) {
+                               /*
+                                * If we get here, it means that no copyup
+                                * was needed, and that a file by the old
+                                * name already existing on the destination
+                                * branch; that file got renamed earlier in
+                                * this function, so all we need to do here
+                                * is set the lower inode.
+                                */
+                               struct inode *inode;
+                               inode = 
unionfs_lower_inode(old_dentry->d_inode);
+                               atomic_inc(&inode->i_count);
+                               unionfs_set_lower_inode_idx(
+                                       new_dentry->d_inode,
+                                       dbstart(new_dentry), inode);
+                       }
+
+               }
+               unionfs_check_inode(old_dir);
+               unionfs_check_inode(new_dir);
+               unionfs_check_dentry(old_dentry);
+               unionfs_check_dentry(new_dentry);
+       }
 
        unionfs_unlock_dentry(new_dentry);
        unionfs_unlock_dentry(old_dentry);
diff --git a/fs/unionfs/unlink.c b/fs/unionfs/unlink.c
index 
29987254ca49625185b42c53ab1af57c0257ebca..7a4ec0822f2a4ff2f4fa1fca25d9ea26f66bb2ee
 100644
--- a/fs/unionfs/unlink.c
+++ b/fs/unionfs/unlink.c
@@ -98,6 +98,7 @@ int unionfs_unlink(struct inode *dir, st
 
        BUG_ON(!is_valid_dentry(dentry));
 
+       unionfs_check_dentry(dentry);
        unionfs_lock_dentry(dentry);
 
        err = unionfs_do_unlink(dir, dentry);
@@ -110,8 +111,14 @@ int unionfs_unlink(struct inode *dir, st
                 */
                /* FIXME: what if this fails? */
                err = odf_remove(dentry, ODF_RMV_NOTWH);
+       }
+       if (!err) {
+               if (!S_ISDIR(dentry->d_inode->i_mode))
+                       unionfs_purge_extras(dentry);
+               unionfs_check_dentry(dentry);
                d_drop(dentry);
        }
+
        unionfs_unlock_dentry(dentry);
        return err;
 }
@@ -192,6 +199,7 @@ int unionfs_rmdir(struct inode *dir, str
 
        BUG_ON(!is_valid_dentry(dentry));
 
+       unionfs_check_dentry(dentry);
        unionfs_lock_dentry(dentry);
 
        /* check if this unionfs directory is empty or not */
diff --git a/fs/unionfs/xattr.c b/fs/unionfs/xattr.c
index 
12d618bd8f498dc2e632a3891edad2f6cb833768..4cacead60c61a7d7053832068b231b5b386fdb9a
 100644
--- a/fs/unionfs/xattr.c
+++ b/fs/unionfs/xattr.c
@@ -66,6 +66,7 @@ ssize_t unionfs_getxattr(struct dentry *
        err = vfs_getxattr(hidden_dentry, (char*) name, value, size);
 
        unionfs_unlock_dentry(dentry);
+       unionfs_check_dentry(dentry);
        return err;
 }
 
@@ -88,6 +89,7 @@ int unionfs_setxattr(struct dentry *dent
                           size, flags);
 
        unionfs_unlock_dentry(dentry);
+       unionfs_check_dentry(dentry);
        return err;
 }
 
@@ -108,6 +110,7 @@ int unionfs_removexattr(struct dentry *d
        err = vfs_removexattr(hidden_dentry, (char*) name);
 
        unionfs_unlock_dentry(dentry);
+       unionfs_check_dentry(dentry);
        return err;
 }
 
@@ -131,5 +134,6 @@ ssize_t unionfs_listxattr(struct dentry 
        err = vfs_listxattr(hidden_dentry, encoded_list, size);
 
        unionfs_unlock_dentry(dentry);
+       unionfs_check_dentry(dentry);
        return err;
 }
_______________________________________________
unionfs-cvs mailing list: http://unionfs.filesystems.org/
[email protected]
http://www.fsl.cs.sunysb.edu/mailman/listinfo/unionfs-cvs

Reply via email to