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: Miklos Szeredi <mszeredi@suse.cz>
This commit is contained in:
Miklos Szeredi 2014-12-13 00:59:52 +01:00
parent 53a08cb9b8
commit a78d9f0d5d
2 changed files with 95 additions and 27 deletions

View 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
--------------------- ---------------------

View 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"; 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)
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,
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)
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,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 = -ENOMEM;
err = ovl_lower_dir(ufs->config.lowerdir, &lowerpath, lowertmp = kstrdup(ufs->config.lowerdir, GFP_KERNEL);
&ufs->lower_namelen, &sb->s_stack_depth); if (!lowertmp)
if (err)
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; err = -EINVAL;
sb->s_stack_depth++; sb->s_stack_depth++;
if (sb->s_stack_depth > FILESYSTEM_MAX_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) 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); if (IS_ERR(mnt)) {
err = PTR_ERR(mnt); pr_err("overlayfs: failed to clone lowerpath\n");
if (IS_ERR(mnt)) { goto out_put_lower_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[ufs->numlower] = mnt;
ufs->numlower++;
} }
/*
* 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;
/* 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)
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)
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 @@ out_put_workdir:
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: