ovl: get exclusive ownership on upper/work dirs
authorAmir Goldstein <amir73il@gmail.com>
Wed, 21 Jun 2017 12:28:33 +0000 (15:28 +0300)
committerMiklos Szeredi <mszeredi@redhat.com>
Tue, 4 Jul 2017 20:03:17 +0000 (22:03 +0200)
Bad things can happen if several concurrent overlay mounts try to
use the same upperdir/workdir path.

Try to get the 'inuse' advisory lock on upperdir and workdir.
Fail mount if another overlay mount instance or another user
holds the 'inuse' lock on these directories.

Note that this provides no protection for concurrent overlay
mount that use overlapping (i.e. descendant) upper/work dirs.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/overlayfs/ovl_entry.h
fs/overlayfs/super.c

index 6a90a48c358970637bf74bb3519dbf9386445ff7..5b5a3211642425aa25bc753c075c7c68755614e3 100644 (file)
@@ -21,6 +21,9 @@ struct ovl_fs {
        struct vfsmount *upper_mnt;
        unsigned numlower;
        struct vfsmount **lower_mnt;
+       /* workbasedir is the path at workdir= mount option */
+       struct dentry *workbasedir;
+       /* workdir is the 'work' directory under workbasedir */
        struct dentry *workdir;
        long namelen;
        /* pathnames of lower and upper dirs, for show_options */
index e0a51ea773eca3361509cc727c0e2c1332876d48..b3163772702198d7436a8fe1245853e9802b3423 100644 (file)
@@ -204,6 +204,10 @@ static void ovl_put_super(struct super_block *sb)
        unsigned i;
 
        dput(ufs->workdir);
+       ovl_inuse_unlock(ufs->workbasedir);
+       dput(ufs->workbasedir);
+       if (ufs->upper_mnt)
+               ovl_inuse_unlock(ufs->upper_mnt->mnt_root);
        mntput(ufs->upper_mnt);
        for (i = 0; i < ufs->numlower; i++)
                mntput(ufs->lower_mnt[i]);
@@ -821,9 +825,15 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
                if (err)
                        goto out_put_upperpath;
 
+               err = -EBUSY;
+               if (!ovl_inuse_trylock(upperpath.dentry)) {
+                       pr_err("overlayfs: upperdir is in-use by another mount\n");
+                       goto out_put_upperpath;
+               }
+
                err = ovl_mount_dir(ufs->config.workdir, &workpath);
                if (err)
-                       goto out_put_upperpath;
+                       goto out_unlock_upperdentry;
 
                err = -EINVAL;
                if (upperpath.mnt != workpath.mnt) {
@@ -834,12 +844,20 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
                        pr_err("overlayfs: workdir and upperdir must be separate subtrees\n");
                        goto out_put_workpath;
                }
+
+               err = -EBUSY;
+               if (!ovl_inuse_trylock(workpath.dentry)) {
+                       pr_err("overlayfs: workdir is in-use by another mount\n");
+                       goto out_put_workpath;
+               }
+
+               ufs->workbasedir = workpath.dentry;
                sb->s_stack_depth = upperpath.mnt->mnt_sb->s_stack_depth;
        }
        err = -ENOMEM;
        lowertmp = kstrdup(ufs->config.lowerdir, GFP_KERNEL);
        if (!lowertmp)
-               goto out_put_workpath;
+               goto out_unlock_workdentry;
 
        err = -EINVAL;
        stacklen = ovl_split_lowerdirs(lowertmp);
@@ -882,6 +900,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
                        pr_err("overlayfs: failed to clone upperpath\n");
                        goto out_put_lowerpath;
                }
+
                /* Don't inherit atime flags */
                ufs->upper_mnt->mnt_flags &= ~(MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME);
 
@@ -1004,7 +1023,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
        mntput(upperpath.mnt);
        for (i = 0; i < numlower; i++)
                mntput(stack[i].mnt);
-       path_put(&workpath);
+       mntput(workpath.mnt);
        kfree(lowertmp);
 
        if (upperpath.dentry) {
@@ -1043,8 +1062,12 @@ out_put_lowerpath:
        kfree(stack);
 out_free_lowertmp:
        kfree(lowertmp);
+out_unlock_workdentry:
+       ovl_inuse_unlock(workpath.dentry);
 out_put_workpath:
        path_put(&workpath);
+out_unlock_upperdentry:
+       ovl_inuse_unlock(upperpath.dentry);
 out_put_upperpath:
        path_put(&upperpath);
 out_free_config: