ovl: make upperdir optional
authorMiklos Szeredi <mszeredi@suse.cz>
Fri, 12 Dec 2014 23:59:51 +0000 (00:59 +0100)
committerMiklos Szeredi <mszeredi@suse.cz>
Fri, 12 Dec 2014 23:59:51 +0000 (00:59 +0100)
Make "upperdir=" mount option optional.  If "upperdir=" is not given, then
the "workdir=" option is also optional (and ignored if given).

Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
fs/overlayfs/super.c

index 592370ff453a188999c597421ed459bc752bf410..35bb0adf10cfe8e041ca7fb8d7358a392e5b952c 100644 (file)
@@ -516,8 +516,10 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
        struct ovl_fs *ufs = sb->s_fs_info;
 
        seq_printf(m, ",lowerdir=%s", ufs->config.lowerdir);
-       seq_printf(m, ",upperdir=%s", ufs->config.upperdir);
-       seq_printf(m, ",workdir=%s", ufs->config.workdir);
+       if (ufs->config.upperdir) {
+               seq_printf(m, ",upperdir=%s", ufs->config.upperdir);
+               seq_printf(m, ",workdir=%s", ufs->config.workdir);
+       }
        return 0;
 }
 
@@ -768,8 +770,8 @@ static bool ovl_workdir_ok(struct dentry *workdir, struct dentry *upperdir)
 static int ovl_fill_super(struct super_block *sb, void *data, int silent)
 {
        struct path lowerpath;
-       struct path upperpath;
-       struct path workpath;
+       struct path upperpath = { NULL, NULL };
+       struct path workpath = { NULL, NULL };
        struct dentry *root_dentry;
        struct ovl_entry *oe;
        struct ovl_fs *ufs;
@@ -786,31 +788,38 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
        if (err)
                goto out_free_config;
 
-       /* FIXME: workdir is not needed for a R/O mount */
        err = -EINVAL;
-       if (!ufs->config.upperdir || !ufs->config.lowerdir ||
-           !ufs->config.workdir) {
-               pr_err("overlayfs: missing upperdir or lowerdir or workdir\n");
+       if (!ufs->config.lowerdir) {
+               pr_err("overlayfs: missing 'lowerdir'\n");
                goto out_free_config;
        }
 
-       err = ovl_mount_dir(ufs->config.upperdir, &upperpath);
-       if (err)
-               goto out_free_config;
+       sb->s_stack_depth = 0;
+       if (ufs->config.upperdir) {
+               /* FIXME: workdir is not needed for a R/O mount */
+               if (!ufs->config.workdir) {
+                       pr_err("overlayfs: missing 'workdir'\n");
+                       goto out_free_config;
+               }
 
-       err = ovl_mount_dir(ufs->config.workdir, &workpath);
-       if (err)
-               goto out_put_upperpath;
+               err = ovl_mount_dir(ufs->config.upperdir, &upperpath);
+               if (err)
+                       goto out_free_config;
 
-       if (upperpath.mnt != workpath.mnt) {
-               pr_err("overlayfs: workdir and upperdir must reside under the same mount\n");
-               goto out_put_workpath;
-       }
-       if (!ovl_workdir_ok(workpath.dentry, upperpath.dentry)) {
-               pr_err("overlayfs: workdir and upperdir must be separate subtrees\n");
-               goto out_put_workpath;
+               err = ovl_mount_dir(ufs->config.workdir, &workpath);
+               if (err)
+                       goto out_put_upperpath;
+
+               if (upperpath.mnt != workpath.mnt) {
+                       pr_err("overlayfs: workdir and upperdir must reside under the same mount\n");
+                       goto out_put_workpath;
+               }
+               if (!ovl_workdir_ok(workpath.dentry, upperpath.dentry)) {
+                       pr_err("overlayfs: workdir and upperdir must be separate subtrees\n");
+                       goto out_put_workpath;
+               }
+               sb->s_stack_depth = upperpath.mnt->mnt_sb->s_stack_depth;
        }
-       sb->s_stack_depth = upperpath.mnt->mnt_sb->s_stack_depth;
 
        err = ovl_lower_dir(ufs->config.lowerdir, &lowerpath,
                            &ufs->lower_namelen, &sb->s_stack_depth);
@@ -824,19 +833,21 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
                goto out_put_lowerpath;
        }
 
-       ufs->upper_mnt = clone_private_mount(&upperpath);
-       err = PTR_ERR(ufs->upper_mnt);
-       if (IS_ERR(ufs->upper_mnt)) {
-               pr_err("overlayfs: failed to clone upperpath\n");
-               goto out_put_lowerpath;
-       }
+       if (ufs->config.upperdir) {
+               ufs->upper_mnt = clone_private_mount(&upperpath);
+               err = PTR_ERR(ufs->upper_mnt);
+               if (IS_ERR(ufs->upper_mnt)) {
+                       pr_err("overlayfs: failed to clone upperpath\n");
+                       goto out_put_lowerpath;
+               }
 
-       ufs->workdir = ovl_workdir_create(ufs->upper_mnt, workpath.dentry);
-       err = PTR_ERR(ufs->workdir);
-       if (IS_ERR(ufs->workdir)) {
-               pr_err("overlayfs: failed to create directory %s/%s\n",
-                      ufs->config.workdir, OVL_WORKDIR_NAME);
-               goto out_put_upper_mnt;
+               ufs->workdir = ovl_workdir_create(ufs->upper_mnt, workpath.dentry);
+               err = PTR_ERR(ufs->workdir);
+               if (IS_ERR(ufs->workdir)) {
+                       pr_err("overlayfs: failed to create directory %s/%s\n",
+                              ufs->config.workdir, OVL_WORKDIR_NAME);
+                       goto out_put_upper_mnt;
+               }
        }
 
        ufs->lower_mnt = kcalloc(1, sizeof(struct vfsmount *), GFP_KERNEL);
@@ -858,8 +869,8 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
        ufs->lower_mnt[0] = mnt;
        ufs->numlower = 1;
 
-       /* If the upper fs is r/o, we mark overlayfs r/o too */
-       if (ufs->upper_mnt->mnt_sb->s_flags & MS_RDONLY)
+       /* If the upper fs is r/o or nonexistent, we mark overlayfs r/o too */
+       if (!ufs->upper_mnt || (ufs->upper_mnt->mnt_sb->s_flags & MS_RDONLY))
                sb->s_flags |= MS_RDONLY;
 
        sb->s_d_op = &ovl_dentry_operations;