summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2014-12-13 00:59:52 +0100
committerMiklos Szeredi <mszeredi@suse.cz>2014-12-13 00:59:52 +0100
commita78d9f0d5d5ca9054703376c7c23c901807ddd87 (patch)
tree956a4d1e7517b9ead6f6f101dbfc1f344bec87fd
parent53a08cb9b8bccfe58f1228c7c27baf34a83da78b (diff)
downloadlinux-a78d9f0d5d5ca9054703376c7c23c901807ddd87.tar.gz
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>
-rw-r--r--Documentation/filesystems/overlayfs.txt12
-rw-r--r--fs/overlayfs/super.c110
2 files changed, 95 insertions, 27 deletions
diff --git a/Documentation/filesystems/overlayfs.txt b/Documentation/filesystems/overlayfs.txt
index a27c950..b370928 100644
--- a/Documentation/filesystems/overlayfs.txt
+++ b/Documentation/filesystems/overlayfs.txt
@@ -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).
+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
---------------------
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 35bb0ad..5c495a1 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -60,6 +60,8 @@ struct ovl_entry {
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)
@@ -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)
{
- 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);
@@ -735,7 +741,7 @@ static int ovl_lower_dir(const char *name, struct path *path, long *namelen,
int err;
struct kstatfs statfs;
- err = ovl_mount_dir(name, path);
+ err = ovl_mount_dir_noesc(name, path);
if (err)
goto out;
@@ -767,15 +773,38 @@ static bool ovl_workdir_ok(struct dentry *workdir, struct dentry *upperdir)
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)
{
- 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 vfsmount *mnt;
+ struct path *stack = NULL;
+ char *lowertmp;
+ char *lower;
+ unsigned int numlower;
+ unsigned int stacklen = 0;
unsigned int i;
int err;
@@ -820,13 +849,31 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
}
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;
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) {
pr_err("overlayfs: maximum fs stacking depth exceeded\n");
@@ -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;
+ 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))
@@ -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;
- oe = ovl_alloc_entry(1);
+ oe = ovl_alloc_entry(numlower);
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);
- mntput(lowerpath.mnt);
+ for (i = 0; i < numlower; i++)
+ mntput(stack[i].mnt);
path_put(&workpath);
+ kfree(lowertmp);
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;
@@ -912,7 +964,11 @@ out_put_workdir:
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: