Commit a78d9f0d authored by Miklos Szeredi's avatar Miklos Szeredi

ovl: support multiple lower layers

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: default avatarMiklos Szeredi <mszeredi@suse.cz>
parent 53a08cb9
...@@ -159,6 +159,18 @@ overlay filesystem (though an operation on the name of the file such as ...@@ -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
--------------------- ---------------------
......
...@@ -60,6 +60,8 @@ struct ovl_entry { ...@@ -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"; const char *ovl_opaque_xattr = "trusted.overlay.opaque";
static struct dentry *__ovl_dentry_lower(struct ovl_entry *oe) static struct dentry *__ovl_dentry_lower(struct ovl_entry *oe)
...@@ -692,8 +694,12 @@ static bool ovl_is_allowed_fs_type(struct dentry *root) ...@@ -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); err = kern_path(name, LOOKUP_FOLLOW, path);
if (err) { if (err) {
pr_err("overlayfs: failed to resolve '%s': %i\n", name, 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, ...@@ -735,7 +741,7 @@ static int ovl_lower_dir(const char *name, struct path *path, long *namelen,
int err; int err;
struct kstatfs statfs; struct kstatfs statfs;
err = ovl_mount_dir(name, path); err = ovl_mount_dir_noesc(name, path);
if (err) if (err)
goto out; goto out;
...@@ -767,15 +773,38 @@ static bool ovl_workdir_ok(struct dentry *workdir, struct dentry *upperdir) ...@@ -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 upperpath = { NULL, NULL };
struct path workpath = { NULL, NULL }; struct path workpath = { NULL, NULL };
struct dentry *root_dentry; struct dentry *root_dentry;
struct ovl_entry *oe; struct ovl_entry *oe;
struct ovl_fs *ufs; 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; unsigned int i;
int err; int err;
...@@ -820,11 +849,29 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -820,11 +849,29 @@ 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 = -ENOMEM;
lowertmp = kstrdup(ufs->config.lowerdir, GFP_KERNEL);
if (!lowertmp)
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;
err = ovl_lower_dir(ufs->config.lowerdir, &lowerpath, lower = lowertmp;
for (numlower = 0; numlower < stacklen; numlower++) {
err = ovl_lower_dir(lower, &stack[numlower],
&ufs->lower_namelen, &sb->s_stack_depth); &ufs->lower_namelen, &sb->s_stack_depth);
if (err) if (err)
goto out_put_workpath; goto out_put_lowerpath;
lower = strchr(lower, '\0') + 1;
}
err = -EINVAL; err = -EINVAL;
sb->s_stack_depth++; sb->s_stack_depth++;
...@@ -850,12 +897,12 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -850,12 +897,12 @@ 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) if (ufs->lower_mnt == NULL)
goto out_put_workdir; 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)) { if (IS_ERR(mnt)) {
pr_err("overlayfs: failed to clone lowerpath\n"); pr_err("overlayfs: failed to clone lowerpath\n");
goto out_put_lower_mnt; goto out_put_lower_mnt;
...@@ -866,8 +913,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -866,8 +913,9 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
*/ */
mnt->mnt_flags |= MNT_READONLY; mnt->mnt_flags |= MNT_READONLY;
ufs->lower_mnt[0] = mnt; ufs->lower_mnt[ufs->numlower] = mnt;
ufs->numlower = 1; ufs->numlower++;
}
/* If the upper fs is r/o or nonexistent, we mark overlayfs r/o too */ /* 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 (!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) ...@@ -876,7 +924,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
sb->s_d_op = &ovl_dentry_operations; sb->s_d_op = &ovl_dentry_operations;
err = -ENOMEM; err = -ENOMEM;
oe = ovl_alloc_entry(1); oe = ovl_alloc_entry(numlower);
if (!oe) if (!oe)
goto out_put_lower_mnt; goto out_put_lower_mnt;
...@@ -885,12 +933,16 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -885,12 +933,16 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
goto out_free_oe; goto out_free_oe;
mntput(upperpath.mnt); 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; for (i = 0; i < numlower; i++) {
oe->lowerstack[0].mnt = ufs->lower_mnt[0]; 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 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ...@@ -912,7 +964,11 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
out_put_upper_mnt: out_put_upper_mnt:
mntput(ufs->upper_mnt); mntput(ufs->upper_mnt);
out_put_lowerpath: 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: out_put_workpath:
path_put(&workpath); path_put(&workpath);
out_put_upperpath: out_put_upperpath:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment