]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
ovl: support multiple lower layers
authorMiklos Szeredi <mszeredi@suse.cz>
Fri, 12 Dec 2014 23:59:52 +0000 (00:59 +0100)
committerMiklos Szeredi <mszeredi@suse.cz>
Fri, 12 Dec 2014 23:59:52 +0000 (00:59 +0100)
Allow "lowerdir=" option to contain multiple lower directories separated by
a colon (e.g. "lowerdir=/bin:/usr/bin").  Colon characters in filenames can
be escaped with a backslash.

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

index a27c950ece61b0d312fbba2a3757fab71c5b3616..b37092886dcc32079036fdb5c096d0a334746aa4 100644 (file)
@@ -159,6 +159,18 @@ overlay filesystem (though an operation on the name of the file such as
 rename or unlink will of course be noticed and handled).
 
 
 rename or unlink will of course be noticed and handled).
 
 
+Multiple lower layers
+---------------------
+
+Multiple lower layers can now be given using the the colon (":") as a
+separator character between the directory names.  For example:
+
+  mount -t overlay overlay -olowerdir=/lower1:/lower2:/lower3 /merged
+
+As the example shows, "upperdir=" and "workdir=" may be omitted.  In that case
+the overlay will be read-only.
+
+
 Non-standard behavior
 ---------------------
 
 Non-standard behavior
 ---------------------
 
index 35bb0adf10cfe8e041ca7fb8d7358a392e5b952c..5c495a17a5a3e2dff1ca52782f54a5e7bec646cd 100644 (file)
@@ -60,6 +60,8 @@ struct ovl_entry {
        struct path lowerstack[];
 };
 
        struct path lowerstack[];
 };
 
+#define OVL_MAX_STACK 500
+
 const char *ovl_opaque_xattr = "trusted.overlay.opaque";
 
 static struct dentry *__ovl_dentry_lower(struct ovl_entry *oe)
 const char *ovl_opaque_xattr = "trusted.overlay.opaque";
 
 static struct dentry *__ovl_dentry_lower(struct ovl_entry *oe)
@@ -692,8 +694,12 @@ static bool ovl_is_allowed_fs_type(struct dentry *root)
 
 static int ovl_mount_dir_noesc(const char *name, struct path *path)
 {
 
 static int ovl_mount_dir_noesc(const char *name, struct path *path)
 {
-       int err;
+       int err = -EINVAL;
 
 
+       if (!*name) {
+               pr_err("overlayfs: empty lowerdir\n");
+               goto out;
+       }
        err = kern_path(name, LOOKUP_FOLLOW, path);
        if (err) {
                pr_err("overlayfs: failed to resolve '%s': %i\n", name, err);
        err = kern_path(name, LOOKUP_FOLLOW, path);
        if (err) {
                pr_err("overlayfs: failed to resolve '%s': %i\n", name, err);
@@ -735,7 +741,7 @@ static int ovl_lower_dir(const char *name, struct path *path, long *namelen,
        int err;
        struct kstatfs statfs;
 
        int err;
        struct kstatfs statfs;
 
-       err = ovl_mount_dir(name, path);
+       err = ovl_mount_dir_noesc(name, path);
        if (err)
                goto out;
 
        if (err)
                goto out;
 
@@ -767,15 +773,38 @@ static bool ovl_workdir_ok(struct dentry *workdir, struct dentry *upperdir)
        return ok;
 }
 
        return ok;
 }
 
+static unsigned int ovl_split_lowerdirs(char *str)
+{
+       unsigned int ctr = 1;
+       char *s, *d;
+
+       for (s = d = str;; s++, d++) {
+               if (*s == '\\') {
+                       s++;
+               } else if (*s == ':') {
+                       *d = '\0';
+                       ctr++;
+                       continue;
+               }
+               *d = *s;
+               if (!*s)
+                       break;
+       }
+       return ctr;
+}
+
 static int ovl_fill_super(struct super_block *sb, void *data, int silent)
 {
 static int ovl_fill_super(struct super_block *sb, void *data, int silent)
 {
-       struct path lowerpath;
        struct path upperpath = { NULL, NULL };
        struct path workpath = { NULL, NULL };
        struct dentry *root_dentry;
        struct ovl_entry *oe;
        struct ovl_fs *ufs;
        struct path upperpath = { NULL, NULL };
        struct path workpath = { NULL, NULL };
        struct dentry *root_dentry;
        struct ovl_entry *oe;
        struct ovl_fs *ufs;
-       struct vfsmount *mnt;
+       struct path *stack = NULL;
+       char *lowertmp;
+       char *lower;
+       unsigned int numlower;
+       unsigned int stacklen = 0;
        unsigned int i;
        int err;
 
        unsigned int i;
        int err;
 
@@ -820,12 +849,30 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
                }
                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);
-       if (err)
+       err = -ENOMEM;
+       lowertmp = kstrdup(ufs->config.lowerdir, GFP_KERNEL);
+       if (!lowertmp)
                goto out_put_workpath;
 
                goto out_put_workpath;
 
+       err = -EINVAL;
+       stacklen = ovl_split_lowerdirs(lowertmp);
+       if (stacklen > OVL_MAX_STACK)
+               goto out_free_lowertmp;
+
+       stack = kcalloc(stacklen, sizeof(struct path), GFP_KERNEL);
+       if (!stack)
+               goto out_free_lowertmp;
+
+       lower = lowertmp;
+       for (numlower = 0; numlower < stacklen; numlower++) {
+               err = ovl_lower_dir(lower, &stack[numlower],
+                                   &ufs->lower_namelen, &sb->s_stack_depth);
+               if (err)
+                       goto out_put_lowerpath;
+
+               lower = strchr(lower, '\0') + 1;
+       }
+
        err = -EINVAL;
        sb->s_stack_depth++;
        if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) {
        err = -EINVAL;
        sb->s_stack_depth++;
        if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) {
@@ -850,24 +897,25 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
                }
        }
 
                }
        }
 
-       ufs->lower_mnt = kcalloc(1, sizeof(struct vfsmount *), GFP_KERNEL);
+       ufs->lower_mnt = kcalloc(numlower, sizeof(struct vfsmount *), GFP_KERNEL);
        if (ufs->lower_mnt == NULL)
                goto out_put_workdir;
        if (ufs->lower_mnt == NULL)
                goto out_put_workdir;
+       for (i = 0; i < numlower; i++) {
+               struct vfsmount *mnt = clone_private_mount(&stack[i]);
 
 
-       mnt = clone_private_mount(&lowerpath);
-       err = PTR_ERR(mnt);
-       if (IS_ERR(mnt)) {
-               pr_err("overlayfs: failed to clone lowerpath\n");
-               goto out_put_lower_mnt;
-       }
-       /*
-        * Make lower_mnt R/O.  That way fchmod/fchown on lower file
-        * will fail instead of modifying lower fs.
-        */
-       mnt->mnt_flags |= MNT_READONLY;
+               if (IS_ERR(mnt)) {
+                       pr_err("overlayfs: failed to clone lowerpath\n");
+                       goto out_put_lower_mnt;
+               }
+               /*
+                * Make lower_mnt R/O.  That way fchmod/fchown on lower file
+                * will fail instead of modifying lower fs.
+                */
+               mnt->mnt_flags |= MNT_READONLY;
 
 
-       ufs->lower_mnt[0] = mnt;
-       ufs->numlower = 1;
+               ufs->lower_mnt[ufs->numlower] = mnt;
+               ufs->numlower++;
+       }
 
        /* 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))
 
        /* 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))
@@ -876,7 +924,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
        sb->s_d_op = &ovl_dentry_operations;
 
        err = -ENOMEM;
        sb->s_d_op = &ovl_dentry_operations;
 
        err = -ENOMEM;
-       oe = ovl_alloc_entry(1);
+       oe = ovl_alloc_entry(numlower);
        if (!oe)
                goto out_put_lower_mnt;
 
        if (!oe)
                goto out_put_lower_mnt;
 
@@ -885,12 +933,16 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
                goto out_free_oe;
 
        mntput(upperpath.mnt);
                goto out_free_oe;
 
        mntput(upperpath.mnt);
-       mntput(lowerpath.mnt);
+       for (i = 0; i < numlower; i++)
+               mntput(stack[i].mnt);
        path_put(&workpath);
        path_put(&workpath);
+       kfree(lowertmp);
 
        oe->__upperdentry = upperpath.dentry;
 
        oe->__upperdentry = upperpath.dentry;
-       oe->lowerstack[0].dentry = lowerpath.dentry;
-       oe->lowerstack[0].mnt = ufs->lower_mnt[0];
+       for (i = 0; i < numlower; i++) {
+               oe->lowerstack[i].dentry = stack[i].dentry;
+               oe->lowerstack[i].mnt = ufs->lower_mnt[i];
+       }
 
        root_dentry->d_fsdata = oe;
 
 
        root_dentry->d_fsdata = oe;
 
@@ -912,7 +964,11 @@ out_put_workdir:
 out_put_upper_mnt:
        mntput(ufs->upper_mnt);
 out_put_lowerpath:
 out_put_upper_mnt:
        mntput(ufs->upper_mnt);
 out_put_lowerpath:
-       path_put(&lowerpath);
+       for (i = 0; i < numlower; i++)
+               path_put(&stack[i]);
+       kfree(stack);
+out_free_lowertmp:
+       kfree(lowertmp);
 out_put_workpath:
        path_put(&workpath);
 out_put_upperpath:
 out_put_workpath:
        path_put(&workpath);
 out_put_upperpath: