Unionfs: Grab the unionfs sb private data lock around branch info users
authorErez Zadok <ezk@cs.sunysb.edu>
Thu, 22 Mar 2007 23:32:40 +0000 (19:32 -0400)
committerErez_Zadok <ezk@cs.sunysb.edu>
Mon, 23 Jul 2007 00:50:24 +0000 (20:50 -0400)
Locking/concurrency/race fixes.  Use the unionfs superblock rwsem, and grab
the read lock around every op that uses branch-related information, such as
branch counters.  Grab the write rwsem lock in operations which attempt to
change branch information, such as when adding/deleting branches.  This
will, for example, cause branch-management remount commands (which are
infrequent) to block a bit until all in-progress file operations on open
files are done.

Signed-off-by: Erez Zadok <ezk@cs.sunysb.edu>
[jsipek: whitespace fixes & more locks/unlocks]
Signed-off-by: Josef 'Jeff' Sipek <jsipek@cs.sunysb.edu>
fs/unionfs/commonfops.c
fs/unionfs/copyup.c
fs/unionfs/dirfops.c
fs/unionfs/dirhelper.c
fs/unionfs/file.c
fs/unionfs/inode.c
fs/unionfs/main.c
fs/unionfs/super.c
fs/unionfs/union.h

index 9b6128ccad27b3ee136232dd27ad28bebf117de4..6606cba1f96e8b4547a8bfba21899af98c6333d8 100644 (file)
@@ -170,7 +170,9 @@ static int open_all_files(struct file *file)
 
                dget(hidden_dentry);
                unionfs_mntget(dentry, bindex);
+               unionfs_read_lock(sb);
                branchget(sb, bindex);
+               unionfs_read_unlock(sb);
 
                hidden_file = dentry_open(hidden_dentry,
                                unionfs_lower_mnt_idx(dentry, bindex),
@@ -215,7 +217,9 @@ static int open_highest_file(struct file *file, int willwrite)
 
        dget(hidden_dentry);
        unionfs_mntget(dentry, bstart);
+       unionfs_read_lock(sb);
        branchget(sb, bstart);
+       unionfs_read_unlock(sb);
        hidden_file = dentry_open(hidden_dentry,
                        unionfs_lower_mnt_idx(dentry, bstart), file->f_flags);
        if (IS_ERR(hidden_file)) {
@@ -256,7 +260,9 @@ static int do_delayed_copyup(struct file *file, struct dentry *dentry)
                bend = fbend(file);
                for (bindex = bstart; bindex <= bend; bindex++) {
                        if (unionfs_lower_file_idx(file, bindex)) {
+                               unionfs_read_lock(dentry->d_sb);
                                branchput(dentry->d_sb, bindex);
+                               unionfs_read_unlock(dentry->d_sb);
                                fput(unionfs_lower_file_idx(file, bindex));
                                unionfs_set_lower_file_idx(file, bindex, NULL);
                        }
@@ -387,7 +393,9 @@ static int __open_dir(struct inode *inode, struct file *file)
                /* The branchget goes after the open, because otherwise
                 * we would miss the reference on release.
                 */
+               unionfs_read_lock(inode->i_sb);
                branchget(inode->i_sb, bindex);
+               unionfs_read_unlock(inode->i_sb);
        }
 
        return 0;
@@ -443,7 +451,9 @@ static int __open_file(struct inode *inode, struct file *file)
                return PTR_ERR(hidden_file);
 
        unionfs_set_lower_file(file, hidden_file);
+       unionfs_read_lock(inode->i_sb);
        branchget(inode->i_sb, bstart);
+       unionfs_read_unlock(inode->i_sb);
 
        return 0;
 }
@@ -456,6 +466,7 @@ int unionfs_open(struct inode *inode, struct file *file)
        int bindex = 0, bstart = 0, bend = 0;
        int size;
 
+       unionfs_read_lock(inode->i_sb);
        file->private_data = kzalloc(sizeof(struct unionfs_file_info), GFP_KERNEL);
        if (!UNIONFS_F(file)) {
                err = -ENOMEM;
@@ -481,7 +492,6 @@ int unionfs_open(struct inode *inode, struct file *file)
 
        dentry = file->f_dentry;
        unionfs_lock_dentry(dentry);
-       unionfs_read_lock(inode->i_sb);
 
        bstart = fbstart(file) = dbstart(dentry);
        bend = fbend(file) = dbend(dentry);
@@ -504,14 +514,15 @@ int unionfs_open(struct inode *inode, struct file *file)
                        if (!hidden_file)
                                continue;
 
+                       unionfs_read_lock(file->f_dentry->d_sb);
                        branchput(file->f_dentry->d_sb, bindex);
+                       unionfs_read_unlock(file->f_dentry->d_sb);
                        /* fput calls dput for hidden_dentry */
                        fput(hidden_file);
                }
        }
 
        unionfs_unlock_dentry(dentry);
-       unionfs_read_unlock(inode->i_sb);
 
 out:
        if (err) {
@@ -520,6 +531,7 @@ int unionfs_open(struct inode *inode, struct file *file)
                kfree(UNIONFS_F(file));
        }
 out_nofree:
+       unionfs_read_unlock(inode->i_sb);
        return err;
 }
 
@@ -532,6 +544,7 @@ int unionfs_file_release(struct inode *inode, struct file *file)
        int bindex, bstart, bend;
        int fgen;
 
+       unionfs_read_lock(inode->i_sb);
        /* fput all the hidden files */
        fgen = atomic_read(&fileinfo->generation);
        bstart = fbstart(file);
@@ -565,6 +578,7 @@ int unionfs_file_release(struct inode *inode, struct file *file)
                fileinfo->rdstate = NULL;
        }
        kfree(fileinfo);
+       unionfs_read_unlock(inode->i_sb);
        return 0;
 }
 
@@ -593,6 +607,7 @@ static long do_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        }
 
 out:
+       unionfs_read_unlock(file->f_dentry->d_sb);
        return err;
 }
 
@@ -600,6 +615,7 @@ long unionfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
        long err;
 
+       unionfs_read_lock(file->f_dentry->d_sb);
        if ((err = unionfs_file_revalidate(file, 1)))
                goto out;
 
@@ -635,8 +651,11 @@ int unionfs_flush(struct file *file, fl_owner_t id)
        struct dentry *dentry = file->f_dentry;
        int bindex, bstart, bend;
 
+       unionfs_read_lock(file->f_dentry->d_sb);
+
        if ((err = unionfs_file_revalidate(file, 1)))
                goto out;
+
        if (!atomic_dec_and_test(&UNIONFS_I(dentry->d_inode)->totalopens))
                goto out;
 
@@ -664,6 +683,6 @@ int unionfs_flush(struct file *file, fl_owner_t id)
 out_lock:
        unionfs_unlock_dentry(dentry);
 out:
+       unionfs_read_unlock(file->f_dentry->d_sb);
        return err;
 }
-
index 4ccef817bc40f0cafab38a6954b2b8cb85193b80..411553a9a968eaf06f1c95b81ae3f796e342b9db 100644 (file)
@@ -207,7 +207,9 @@ static int __copyup_reg_data(struct dentry *dentry,
 
        /* open old file */
        unionfs_mntget(dentry, old_bindex);
+       unionfs_read_lock(sb);
        branchget(sb, old_bindex);
+       unionfs_read_unlock(sb);
        input_file = dentry_open(old_hidden_dentry,
                                unionfs_lower_mnt_idx(dentry, old_bindex),
                                O_RDONLY | O_LARGEFILE);
@@ -224,7 +226,9 @@ static int __copyup_reg_data(struct dentry *dentry,
        /* open new file */
        dget(new_hidden_dentry);
        unionfs_mntget(dentry, new_bindex);
+       unionfs_read_lock(sb);
        branchget(sb, new_bindex);
+       unionfs_read_unlock(sb);
        output_file = dentry_open(new_hidden_dentry,
                                unionfs_lower_mnt_idx(dentry, new_bindex),
                                O_WRONLY | O_LARGEFILE);
@@ -295,13 +299,17 @@ static int __copyup_reg_data(struct dentry *dentry,
        fput(output_file);
 
 out_close_in2:
+       unionfs_read_lock(sb);
        branchput(sb, new_bindex);
+       unionfs_read_unlock(sb);
 
 out_close_in:
        fput(input_file);
 
 out:
+       unionfs_read_lock(sb);
        branchput(sb, old_bindex);
+       unionfs_read_unlock(sb);
 
        return err;
 }
@@ -350,8 +358,6 @@ static int copyup_named_dentry(struct inode *dir, struct dentry *dentry,
 
        sb = dir->i_sb;
 
-       unionfs_read_lock(sb);
-
        if ((err = is_robranch_super(sb, new_bindex)))
                goto out;
 
@@ -443,7 +449,9 @@ static int copyup_named_dentry(struct inode *dir, struct dentry *dentry,
                /* need to close the file */
 
                fput(*copyup_file);
+               unionfs_read_lock(sb);
                branchput(sb, new_bindex);
+               unionfs_read_unlock(sb);
        }
 
        /*
@@ -469,8 +477,6 @@ static int copyup_named_dentry(struct inode *dir, struct dentry *dentry,
        kfree(symbuf);
 
 out:
-       unionfs_read_unlock(sb);
-
        return err;
 }
 
index 8f568c77334a15208380c1139092315f888afdfe..6ff32a07393054e37a972764312467e34b8a878c 100644 (file)
@@ -94,6 +94,7 @@ static int unionfs_readdir(struct file *file, void *dirent, filldir_t filldir)
        int bend;
        loff_t offset;
 
+       unionfs_read_lock(file->f_dentry->d_sb);
        if ((err = unionfs_file_revalidate(file, 0)))
                goto out;
 
@@ -175,6 +176,7 @@ static int unionfs_readdir(struct file *file, void *dirent, filldir_t filldir)
                file->f_pos = rdstate2offset(uds);
 
 out:
+       unionfs_read_unlock(file->f_dentry->d_sb);
        return err;
 }
 
@@ -192,6 +194,7 @@ static loff_t unionfs_dir_llseek(struct file *file, loff_t offset, int origin)
        struct unionfs_dir_state *rdstate;
        loff_t err;
 
+       unionfs_read_lock(file->f_dentry->d_sb);
        if ((err = unionfs_file_revalidate(file, 0)))
                goto out;
 
@@ -245,6 +248,7 @@ static loff_t unionfs_dir_llseek(struct file *file, loff_t offset, int origin)
        }
 
 out:
+       unionfs_read_unlock(file->f_dentry->d_sb);
        return err;
 }
 
index bb5f7bc97f3ce7b0009cc1c24ae2f32242b555c7..bd15eb4f5ebe777ff283bf09ef1f6efead82fbb2 100644 (file)
@@ -226,14 +226,18 @@ int check_empty(struct dentry *dentry, struct unionfs_dir_state **namelist)
 
                dget(hidden_dentry);
                unionfs_mntget(dentry, bindex);
+               unionfs_read_lock(sb);
                branchget(sb, bindex);
+               unionfs_read_unlock(sb);
                hidden_file =
                    dentry_open(hidden_dentry, unionfs_lower_mnt_idx(dentry, bindex),
                                O_RDONLY);
                if (IS_ERR(hidden_file)) {
                        err = PTR_ERR(hidden_file);
                        dput(hidden_dentry);
+                       unionfs_read_lock(sb);
                        branchput(sb, bindex);
+                       unionfs_read_unlock(sb);
                        goto out;
                }
 
@@ -248,7 +252,9 @@ int check_empty(struct dentry *dentry, struct unionfs_dir_state **namelist)
 
                /* fput calls dput for hidden_dentry */
                fput(hidden_file);
+               unionfs_read_lock(sb);
                branchput(sb, bindex);
+               unionfs_read_unlock(sb);
 
                if (err < 0)
                        goto out;
index 9ce092d7f2ffaf8b52244fb8475705ebf13de350..84d6bab8b02812db637519ed50553f6b24035fc5 100644 (file)
@@ -27,6 +27,7 @@ static loff_t unionfs_llseek(struct file *file, loff_t offset, int origin)
        loff_t err;
        struct file *hidden_file = NULL;
 
+       unionfs_read_lock(file->f_dentry->d_sb);
        if ((err = unionfs_file_revalidate(file, 0)))
                goto out;
 
@@ -48,6 +49,7 @@ static loff_t unionfs_llseek(struct file *file, loff_t offset, int origin)
                file->f_version++;
        }
 out:
+       unionfs_read_unlock(file->f_dentry->d_sb);
        return err;
 }
 
@@ -58,6 +60,7 @@ static ssize_t unionfs_read(struct file * file, char __user * buf,
        loff_t pos = *ppos;
        int err;
 
+       unionfs_read_lock(file->f_dentry->d_sb);
        if ((err = unionfs_file_revalidate(file, 0)))
                goto out;
 
@@ -70,6 +73,7 @@ static ssize_t unionfs_read(struct file * file, char __user * buf,
        *ppos = pos;
 
 out:
+       unionfs_read_unlock(file->f_dentry->d_sb);
        return err;
 }
 
@@ -123,12 +127,14 @@ static ssize_t unionfs_write(struct file * file, const char __user * buf,
 {
        int err = 0;
 
+       unionfs_read_lock(file->f_dentry->d_sb);
        if ((err = unionfs_file_revalidate(file, 1)))
                goto out;
 
        err = __unionfs_write(file, buf, count, ppos);
 
 out:
+       unionfs_read_unlock(file->f_dentry->d_sb);
        return err;
 }
 
@@ -143,6 +149,7 @@ static unsigned int unionfs_poll(struct file *file, poll_table * wait)
        unsigned int mask = DEFAULT_POLLMASK;
        struct file *hidden_file = NULL;
 
+       unionfs_read_lock(file->f_dentry->d_sb);
        if (unionfs_file_revalidate(file, 0)) {
                /* We should pretend an error happend. */
                mask = POLLERR | POLLIN | POLLOUT;
@@ -157,6 +164,7 @@ static unsigned int unionfs_poll(struct file *file, poll_table * wait)
        mask = hidden_file->f_op->poll(hidden_file, wait);
 
 out:
+       unionfs_read_unlock(file->f_dentry->d_sb);
        return mask;
 }
 
@@ -184,6 +192,7 @@ static int unionfs_mmap(struct file *file, struct vm_area_struct *vma)
        int err = 0;
        int willwrite;
 
+       unionfs_read_lock(file->f_dentry->d_sb);
        /* This might could be deferred to mmap's writepage. */
        willwrite = ((vma->vm_flags | VM_SHARED | VM_WRITE) == vma->vm_flags);
        if ((err = unionfs_file_revalidate(file, willwrite)))
@@ -192,6 +201,7 @@ static int unionfs_mmap(struct file *file, struct vm_area_struct *vma)
        err = __do_mmap(file, vma);
 
 out:
+       unionfs_read_unlock(file->f_dentry->d_sb);
        return err;
 }
 
@@ -200,6 +210,7 @@ static int unionfs_fsync(struct file *file, struct dentry *dentry, int datasync)
        int err;
        struct file *hidden_file = NULL;
 
+       unionfs_read_lock(file->f_dentry->d_sb);
        if ((err = unionfs_file_revalidate(file, 1)))
                goto out;
 
@@ -215,6 +226,7 @@ static int unionfs_fsync(struct file *file, struct dentry *dentry, int datasync)
        mutex_unlock(&hidden_file->f_dentry->d_inode->i_mutex);
 
 out:
+       unionfs_read_unlock(file->f_dentry->d_sb);
        return err;
 }
 
@@ -223,6 +235,7 @@ static int unionfs_fasync(int fd, struct file *file, int flag)
        int err = 0;
        struct file *hidden_file = NULL;
 
+       unionfs_read_lock(file->f_dentry->d_sb);
        if ((err = unionfs_file_revalidate(file, 1)))
                goto out;
 
@@ -232,6 +245,7 @@ static int unionfs_fasync(int fd, struct file *file, int flag)
                err = hidden_file->f_op->fasync(fd, hidden_file, flag);
 
 out:
+       unionfs_read_unlock(file->f_dentry->d_sb);
        return err;
 }
 
index 1b2e8a8ab7beffdcf5690f95a99f95e60ab78c8d..6dfc16f29d0eb82cf54d19453dbe07e4c0f44999 100644 (file)
@@ -789,9 +789,13 @@ static int inode_permission(struct inode *inode, int mask, struct nameidata *nd,
                retval = inode->i_op->permission(inode, submask, nd);
                if ((retval == -EACCES) && (submask & MAY_WRITE) &&
                    (!strcmp("nfs", (inode)->i_sb->s_type->name)) &&
-                   (nd) && (nd->mnt) && (nd->mnt->mnt_sb) &&
-                   (branchperms(nd->mnt->mnt_sb, bindex) & MAY_NFSRO)) {
-                       retval = generic_permission(inode, submask, NULL);
+                   (nd) && (nd->mnt) && (nd->mnt->mnt_sb)) {
+                       int perms;
+                       unionfs_read_lock(nd->mnt->mnt_sb);
+                       perms = branchperms(nd->mnt->mnt_sb, bindex);
+                       unionfs_read_unlock(nd->mnt->mnt_sb);
+                       if (perms & MAY_NFSRO)
+                               retval = generic_permission(inode, submask, NULL);
                }
        } else
                retval = generic_permission(inode, submask, NULL);
index b80b5540b3e40dd0fcd9d4c4e31bab71c819999a..4fffafa4153fc16b6514598a288252aa659ca9da 100644 (file)
@@ -556,11 +556,15 @@ static int unionfs_read_super(struct super_block *sb, void *raw_data,
 
                d = hidden_root_info->lower_paths[bindex].dentry;
 
+               unionfs_write_lock(sb);
                unionfs_set_lower_super_idx(sb, bindex, d->d_sb);
+               unionfs_write_unlock(sb);
        }
 
        /* Unionfs: Max Bytes is the maximum bytes from highest priority branch */
+       unionfs_read_lock(sb);
        sb->s_maxbytes = unionfs_lower_super_idx(sb, 0)->s_maxbytes;
+       unionfs_read_unlock(sb);
 
        sb->s_op = &unionfs_sops;
 
index 7f0d174581993c0fceabe277321fa66ec5badb36..5d1190887924cfd7184b6e4a6c877dc29b7704ab 100644 (file)
@@ -131,7 +131,9 @@ static int unionfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 
        sb = dentry->d_sb;
 
+       unionfs_read_lock(sb);
        hidden_sb = unionfs_lower_super_idx(sb, sbstart(sb));
+       unionfs_read_unlock(sb);
        err = vfs_statfs(hidden_sb->s_root, buf);
 
        buf->f_type = UNIONFS_SUPER_MAGIC;
@@ -884,7 +886,9 @@ static void unionfs_umount_begin(struct vfsmount *mnt, int flags)
        bend = sbend(sb);
        for (bindex = bstart; bindex <= bend; bindex++) {
                hidden_mnt = unionfs_lower_mnt_idx(sb->s_root, bindex);
+               unionfs_read_lock(sb);
                hidden_sb = unionfs_lower_super_idx(sb, bindex);
+               unionfs_read_unlock(sb);
 
                if (hidden_mnt && hidden_sb && hidden_sb->s_op &&
                    hidden_sb->s_op->umount_begin)
@@ -922,7 +926,9 @@ static int unionfs_show_options(struct seq_file *m, struct vfsmount *mnt)
                        goto out;
                }
 
+               unionfs_read_lock(sb);
                perms = branchperms(sb, bindex);
+               unionfs_read_unlock(sb);
 
                seq_printf(m, "%s=%s", path,
                           perms & MAY_WRITE ? "rw" : "ro");
index 66ffe88d29674177b4f4d7ba093c8b28cd7cb135..faaa87e7640b5a3489670abee3a5f6c3570072ec 100644 (file)
@@ -301,7 +301,6 @@ extern int unionfs_rename(struct inode *old_dir, struct dentry *old_dentry,
 int unionfs_unlink(struct inode *dir, struct dentry *dentry);
 int unionfs_rmdir(struct inode *dir, struct dentry *dentry);
 
-int __unionfs_d_revalidate(struct dentry *dentry, struct nameidata *nd);
 int __unionfs_d_revalidate_chain(struct dentry *dentry, struct nameidata *nd);
 
 /* The values for unionfs_interpose's flag. */
@@ -368,7 +367,11 @@ static inline int set_branchperms(struct super_block *sb, int index, int perms)
 /* Is this file on a read-only branch? */
 static inline int is_robranch_super(const struct super_block *sb, int index)
 {
-       return (!(branchperms(sb, index) & MAY_WRITE)) ? -EROFS : 0;
+       int ret;
+       unionfs_read_lock(sb);
+       ret = (!(branchperms(sb, index) & MAY_WRITE)) ? -EROFS : 0;
+       unionfs_read_unlock(sb);
+       return ret;
 }
 
 /* Is this file on a read-only branch? */
@@ -378,10 +381,11 @@ static inline int is_robranch_idx(const struct dentry *dentry, int index)
 
        BUG_ON(index < 0);
 
+       unionfs_read_lock(dentry->d_sb);
        if ((!(branchperms(dentry->d_sb, index) & MAY_WRITE)) ||
            IS_RDONLY(unionfs_lower_dentry_idx(dentry, index)->d_inode))
                err = -EROFS;
-
+       unionfs_read_unlock(dentry->d_sb);
        return err;
 }