diff options
| -rw-r--r-- | fs/overlayfs/Makefile | 2 | ||||
| -rw-r--r-- | fs/overlayfs/overlayfs.h | 23 | ||||
| -rw-r--r-- | fs/overlayfs/ovl_entry.h | 3 | ||||
| -rw-r--r-- | fs/overlayfs/params.c | 389 | ||||
| -rw-r--r-- | fs/overlayfs/super.c | 361 | 
5 files changed, 520 insertions, 258 deletions
| diff --git a/fs/overlayfs/Makefile b/fs/overlayfs/Makefile index 9164c585eb2f..4e173d56b11f 100644 --- a/fs/overlayfs/Makefile +++ b/fs/overlayfs/Makefile @@ -6,4 +6,4 @@  obj-$(CONFIG_OVERLAY_FS) += overlay.o  overlay-objs := super.o namei.o util.o inode.o file.o dir.o readdir.o \ -		copy_up.o export.o +		copy_up.o export.o params.o diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 30227ccc758d..5b6ac03af192 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -368,6 +368,29 @@ static inline bool ovl_open_flags_need_copy_up(int flags)  } +/* params.c */ +#define OVL_MAX_STACK 500 + +struct ovl_fs_context_layer { +	char *name; +	struct path path; +}; + +struct ovl_fs_context { +	struct path upper; +	struct path work; +	size_t capacity; +	size_t nr; /* includes nr_data */ +	size_t nr_data; +	struct ovl_opt_set set; +	struct ovl_fs_context_layer *lower; +}; + +int ovl_parse_param_upperdir(const char *name, struct fs_context *fc, +			     bool workdir); +int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc); +void ovl_parse_param_drop_lowerdir(struct ovl_fs_context *ctx); +  /* util.c */  int ovl_want_write(struct dentry *dentry);  void ovl_drop_write(struct dentry *dentry); diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index b4eb0bd5d0b6..306e1ecdc96d 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -6,7 +6,6 @@   */  struct ovl_config { -	char *lowerdir;  	char *upperdir;  	char *workdir;  	bool default_permissions; @@ -39,6 +38,7 @@ struct ovl_layer {  	int idx;  	/* One fsid per unique underlying sb (upper fsid == 0) */  	int fsid; +	char *name;  };  /* @@ -99,7 +99,6 @@ struct ovl_fs {  	errseq_t errseq;  }; -  /* Number of lower layers, not including data-only layers */  static inline unsigned int ovl_numlowerlayer(struct ovl_fs *ofs)  { diff --git a/fs/overlayfs/params.c b/fs/overlayfs/params.c new file mode 100644 index 000000000000..d17d6b483dd0 --- /dev/null +++ b/fs/overlayfs/params.c @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/fs.h> +#include <linux/namei.h> +#include <linux/fs_context.h> +#include <linux/fs_parser.h> +#include <linux/posix_acl_xattr.h> +#include <linux/xattr.h> +#include "overlayfs.h" + +static ssize_t ovl_parse_param_split_lowerdirs(char *str) +{ +	ssize_t nr_layers = 1, nr_colons = 0; +	char *s, *d; + +	for (s = d = str;; s++, d++) { +		if (*s == '\\') { +			s++; +		} else if (*s == ':') { +			bool next_colon = (*(s + 1) == ':'); + +			nr_colons++; +			if (nr_colons == 2 && next_colon) { +				pr_err("only single ':' or double '::' sequences of unescaped colons in lowerdir mount option allowed.\n"); +				return -EINVAL; +			} +			/* count layers, not colons */ +			if (!next_colon) +				nr_layers++; + +			*d = '\0'; +			continue; +		} + +		*d = *s; +		if (!*s) { +			/* trailing colons */ +			if (nr_colons) { +				pr_err("unescaped trailing colons in lowerdir mount option.\n"); +				return -EINVAL; +			} +			break; +		} +		nr_colons = 0; +	} + +	return nr_layers; +} + +static int ovl_mount_dir_noesc(const char *name, struct path *path) +{ +	int err = -EINVAL; + +	if (!*name) { +		pr_err("empty lowerdir\n"); +		goto out; +	} +	err = kern_path(name, LOOKUP_FOLLOW, path); +	if (err) { +		pr_err("failed to resolve '%s': %i\n", name, err); +		goto out; +	} +	err = -EINVAL; +	if (ovl_dentry_weird(path->dentry)) { +		pr_err("filesystem on '%s' not supported\n", name); +		goto out_put; +	} +	if (!d_is_dir(path->dentry)) { +		pr_err("'%s' not a directory\n", name); +		goto out_put; +	} +	return 0; + +out_put: +	path_put_init(path); +out: +	return err; +} + +static void ovl_unescape(char *s) +{ +	char *d = s; + +	for (;; s++, d++) { +		if (*s == '\\') +			s++; +		*d = *s; +		if (!*s) +			break; +	} +} + +static int ovl_mount_dir(const char *name, struct path *path) +{ +	int err = -ENOMEM; +	char *tmp = kstrdup(name, GFP_KERNEL); + +	if (tmp) { +		ovl_unescape(tmp); +		err = ovl_mount_dir_noesc(tmp, path); + +		if (!err && path->dentry->d_flags & DCACHE_OP_REAL) { +			pr_err("filesystem on '%s' not supported as upperdir\n", +			       tmp); +			path_put_init(path); +			err = -EINVAL; +		} +		kfree(tmp); +	} +	return err; +} + +int ovl_parse_param_upperdir(const char *name, struct fs_context *fc, +			     bool workdir) +{ +	int err; +	struct ovl_fs *ofs = fc->s_fs_info; +	struct ovl_config *config = &ofs->config; +	struct ovl_fs_context *ctx = fc->fs_private; +	struct path path; +	char *dup; + +	err = ovl_mount_dir(name, &path); +	if (err) +		return err; + +	/* +	 * Check whether upper path is read-only here to report failures +	 * early. Don't forget to recheck when the superblock is created +	 * as the mount attributes could change. +	 */ +	if (__mnt_is_readonly(path.mnt)) { +		path_put(&path); +		return -EINVAL; +	} + +	dup = kstrdup(name, GFP_KERNEL); +	if (!dup) { +		path_put(&path); +		return -ENOMEM; +	} + +	if (workdir) { +		kfree(config->workdir); +		config->workdir = dup; +		path_put(&ctx->work); +		ctx->work = path; +	} else { +		kfree(config->upperdir); +		config->upperdir = dup; +		path_put(&ctx->upper); +		ctx->upper = path; +	} +	return 0; +} + +void ovl_parse_param_drop_lowerdir(struct ovl_fs_context *ctx) +{ +	for (size_t nr = 0; nr < ctx->nr; nr++) { +		path_put(&ctx->lower[nr].path); +		kfree(ctx->lower[nr].name); +		ctx->lower[nr].name = NULL; +	} +	ctx->nr = 0; +	ctx->nr_data = 0; +} + +/* + * Parse lowerdir= mount option: + * + * (1) lowerdir=/lower1:/lower2:/lower3::/data1::/data2 + *     Set "/lower1", "/lower2", and "/lower3" as lower layers and + *     "/data1" and "/data2" as data lower layers. Any existing lower + *     layers are replaced. + * (2) lowerdir=:/lower4 + *     Append "/lower4" to current stack of lower layers. This requires + *     that there already is at least one lower layer configured. + * (3) lowerdir=::/lower5 + *     Append data "/lower5" as data lower layer. This requires that + *     there's at least one regular lower layer present. + */ +int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc) +{ +	int err; +	struct ovl_fs_context *ctx = fc->fs_private; +	struct ovl_fs_context_layer *l; +	char *dup = NULL, *dup_iter; +	ssize_t nr_lower = 0, nr = 0, nr_data = 0; +	bool append = false, data_layer = false; + +	/* +	 * Ensure we're backwards compatible with mount(2) +	 * by allowing relative paths. +	 */ + +	/* drop all existing lower layers */ +	if (!*name) { +		ovl_parse_param_drop_lowerdir(ctx); +		return 0; +	} + +	if (strncmp(name, "::", 2) == 0) { +		/* +		 * This is a data layer. +		 * There must be at least one regular lower layer +		 * specified. +		 */ +		if (ctx->nr == 0) { +			pr_err("data lower layers without regular lower layers not allowed"); +			return -EINVAL; +		} + +		/* Skip the leading "::". */ +		name += 2; +		data_layer = true; +		/* +		 * A data layer is automatically an append as there +		 * must've been at least one regular lower layer. +		 */ +		append = true; +	} else if (*name == ':') { +		/* +		 * This is a regular lower layer. +		 * If users want to append a layer enforce that they +		 * have already specified a first layer before. It's +		 * better to be strict. +		 */ +		if (ctx->nr == 0) { +			pr_err("cannot append layer if no previous layer has been specified"); +			return -EINVAL; +		} + +		/* +		 * Once a sequence of data layers has started regular +		 * lower layers are forbidden. +		 */ +		if (ctx->nr_data > 0) { +			pr_err("regular lower layers cannot follow data lower layers"); +			return -EINVAL; +		} + +		/* Skip the leading ":". */ +		name++; +		append = true; +	} + +	dup = kstrdup(name, GFP_KERNEL); +	if (!dup) +		return -ENOMEM; + +	err = -EINVAL; +	nr_lower = ovl_parse_param_split_lowerdirs(dup); +	if (nr_lower < 0) +		goto out_err; + +	if ((nr_lower > OVL_MAX_STACK) || +	    (append && (size_add(ctx->nr, nr_lower) > OVL_MAX_STACK))) { +		pr_err("too many lower directories, limit is %d\n", OVL_MAX_STACK); +		goto out_err; +	} + +	if (!append) +		ovl_parse_param_drop_lowerdir(ctx); + +	/* +	 * (1) append +	 * +	 * We want nr <= nr_lower <= capacity We know nr > 0 and nr <= +	 * capacity. If nr == 0 this wouldn't be append. If nr + +	 * nr_lower is <= capacity then nr <= nr_lower <= capacity +	 * already holds. If nr + nr_lower exceeds capacity, we realloc. +	 * +	 * (2) replace +	 * +	 * Ensure we're backwards compatible with mount(2) which allows +	 * "lowerdir=/a:/b:/c,lowerdir=/d:/e:/f" causing the last +	 * specified lowerdir mount option to win. +	 * +	 * We want nr <= nr_lower <= capacity We know either (i) nr == 0 +	 * or (ii) nr > 0. We also know nr_lower > 0. The capacity +	 * could've been changed multiple times already so we only know +	 * nr <= capacity. If nr + nr_lower > capacity we realloc, +	 * otherwise nr <= nr_lower <= capacity holds already. +	 */ +	nr_lower += ctx->nr; +	if (nr_lower > ctx->capacity) { +		err = -ENOMEM; +		l = krealloc_array(ctx->lower, nr_lower, sizeof(*ctx->lower), +				   GFP_KERNEL_ACCOUNT); +		if (!l) +			goto out_err; + +		ctx->lower = l; +		ctx->capacity = nr_lower; +	} + +	/* +	 *   (3) By (1) and (2) we know nr <= nr_lower <= capacity. +	 *   (4) If ctx->nr == 0 => replace +	 *       We have verified above that the lowerdir mount option +	 *       isn't an append, i.e., the lowerdir mount option +	 *       doesn't start with ":" or "::". +	 * (4.1) The lowerdir mount options only contains regular lower +	 *       layers ":". +	 *       => Nothing to verify. +	 * (4.2) The lowerdir mount options contains regular ":" and +	 *       data "::" layers. +	 *       => We need to verify that data lower layers "::" aren't +	 *          followed by regular ":" lower layers +	 *   (5) If ctx->nr > 0 => append +	 *       We know that there's at least one regular layer +	 *       otherwise we would've failed when parsing the previous +	 *       lowerdir mount option. +	 * (5.1) The lowerdir mount option is a regular layer ":" append +	 *       => We need to verify that no data layers have been +	 *          specified before. +	 * (5.2) The lowerdir mount option is a data layer "::" append +	 *       We know that there's at least one regular layer or +	 *       other data layers. => There's nothing to verify. +	 */ +	dup_iter = dup; +	for (nr = ctx->nr; nr < nr_lower; nr++) { +		l = &ctx->lower[nr]; +		memset(l, 0, sizeof(*l)); + +		err = ovl_mount_dir_noesc(dup_iter, &l->path); +		if (err) +			goto out_put; + +		err = -ENOMEM; +		l->name = kstrdup(dup_iter, GFP_KERNEL_ACCOUNT); +		if (!l->name) +			goto out_put; + +		if (data_layer) +			nr_data++; + +		/* Calling strchr() again would overrun. */ +		if ((nr + 1) == nr_lower) +			break; + +		err = -EINVAL; +		dup_iter = strchr(dup_iter, '\0') + 1; +		if (*dup_iter) { +			/* +			 * This is a regular layer so we require that +			 * there are no data layers. +			 */ +			if ((ctx->nr_data + nr_data) > 0) { +				pr_err("regular lower layers cannot follow data lower layers"); +				goto out_put; +			} + +			data_layer = false; +			continue; +		} + +		/* This is a data lower layer. */ +		data_layer = true; +		dup_iter++; +	} +	ctx->nr = nr_lower; +	ctx->nr_data += nr_data; +	kfree(dup); +	return 0; + +out_put: +	/* +	 * We know nr >= ctx->nr < nr_lower. If we failed somewhere +	 * we want to undo until nr == ctx->nr. This is correct for +	 * both ctx->nr == 0 and ctx->nr > 0. +	 */ +	for (; nr >= ctx->nr; nr--) { +		l = &ctx->lower[nr]; +		kfree(l->name); +		l->name = NULL; +		path_put(&l->path); + +		/* don't overflow */ +		if (nr == 0) +			break; +	} + +out_err: +	kfree(dup); + +	/* Intentionally don't realloc to a smaller size. */ +	return err; +} diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index cc389701dd6d..ed4b35c9d647 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -27,8 +27,6 @@ MODULE_LICENSE("GPL");  struct ovl_dir_cache; -#define OVL_MAX_STACK 500 -  static bool ovl_redirect_dir_def = IS_ENABLED(CONFIG_OVERLAY_FS_REDIRECT_DIR);  module_param_named(redirect_dir, ovl_redirect_dir_def, bool, 0644);  MODULE_PARM_DESC(redirect_dir, @@ -235,6 +233,7 @@ static void ovl_free_fs(struct ovl_fs *ofs)  	for (i = 0; i < ofs->numlayer; i++) {  		iput(ofs->layers[i].trap);  		mounts[i] = ofs->layers[i].mnt; +		kfree(ofs->layers[i].name);  	}  	kern_unmount_array(mounts, ofs->numlayer);  	kfree(ofs->layers); @@ -242,7 +241,6 @@ static void ovl_free_fs(struct ovl_fs *ofs)  		free_anon_bdev(ofs->fs[i].pseudo_dev);  	kfree(ofs->fs); -	kfree(ofs->config.lowerdir);  	kfree(ofs->config.upperdir);  	kfree(ofs->config.workdir);  	if (ofs->creator_cred) @@ -380,8 +378,17 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)  {  	struct super_block *sb = dentry->d_sb;  	struct ovl_fs *ofs = sb->s_fs_info; - -	seq_show_option(m, "lowerdir", ofs->config.lowerdir); +	size_t nr, nr_merged_lower = ofs->numlayer - ofs->numdatalayer; +	const struct ovl_layer *data_layers = &ofs->layers[nr_merged_lower]; + +	/* ofs->layers[0] is the upper layer */ +	seq_printf(m, ",lowerdir=%s", ofs->layers[1].name); +	/* dump regular lower layers */ +	for (nr = 2; nr < nr_merged_lower; nr++) +		seq_printf(m, ":%s", ofs->layers[nr].name); +	/* dump data lower layers */ +	for (nr = 0; nr < ofs->numdatalayer; nr++) +		seq_printf(m, "::%s", data_layers[nr].name);  	if (ofs->config.upperdir) {  		seq_show_option(m, "upperdir", ofs->config.upperdir);  		seq_show_option(m, "workdir", ofs->config.workdir); @@ -464,8 +471,11 @@ static const struct constant_table ovl_parameter_bool[] = {  	{}  }; +#define fsparam_string_empty(NAME, OPT) \ +	__fsparam(fs_param_is_string, NAME, OPT, fs_param_can_be_empty, NULL) +  static const struct fs_parameter_spec ovl_parameter_spec[] = { -	fsparam_string("lowerdir",          Opt_lowerdir), +	fsparam_string_empty("lowerdir",    Opt_lowerdir),  	fsparam_string("upperdir",          Opt_upperdir),  	fsparam_string("workdir",           Opt_workdir),  	fsparam_flag("default_permissions", Opt_default_permissions), @@ -480,10 +490,6 @@ static const struct fs_parameter_spec ovl_parameter_spec[] = {  	{}  }; -struct ovl_fs_context { -	struct ovl_opt_set set; -}; -  static int ovl_parse_param(struct fs_context *fc, struct fs_parameter *param)  {  	int err = 0; @@ -491,7 +497,6 @@ static int ovl_parse_param(struct fs_context *fc, struct fs_parameter *param)  	struct ovl_fs *ofs = fc->s_fs_info;  	struct ovl_config *config = &ofs->config;  	struct ovl_fs_context *ctx = fc->fs_private; -	char *dup;  	int opt;  	/* @@ -508,34 +513,13 @@ static int ovl_parse_param(struct fs_context *fc, struct fs_parameter *param)  	switch (opt) {  	case Opt_lowerdir: -		dup = kstrdup(param->string, GFP_KERNEL); -		if (!dup) { -			err = -ENOMEM; -			break; -		} - -		kfree(config->lowerdir); -		config->lowerdir = dup; +		err = ovl_parse_param_lowerdir(param->string, fc);  		break;  	case Opt_upperdir: -		dup = kstrdup(param->string, GFP_KERNEL); -		if (!dup) { -			err = -ENOMEM; -			break; -		} - -		kfree(config->upperdir); -		config->upperdir = dup; -		break; +		fallthrough;  	case Opt_workdir: -		dup = kstrdup(param->string, GFP_KERNEL); -		if (!dup) { -			err = -ENOMEM; -			break; -		} - -		kfree(config->workdir); -		config->workdir = dup; +		err = ovl_parse_param_upperdir(param->string, fc, +					       (Opt_workdir == opt));  		break;  	case Opt_default_permissions:  		config->default_permissions = true; @@ -587,6 +571,11 @@ static int ovl_fs_params_verify(const struct ovl_fs_context *ctx,  {  	struct ovl_opt_set set = ctx->set; +	if (ctx->nr_data > 0 && !config->metacopy) { +		pr_err("lower data-only dirs require metacopy support.\n"); +		return -EINVAL; +	} +  	/* Workdir/index are useless in non-upper mount */  	if (!config->upperdir) {  		if (config->workdir) { @@ -799,69 +788,6 @@ out_err:  	goto out_unlock;  } -static void ovl_unescape(char *s) -{ -	char *d = s; - -	for (;; s++, d++) { -		if (*s == '\\') -			s++; -		*d = *s; -		if (!*s) -			break; -	} -} - -static int ovl_mount_dir_noesc(const char *name, struct path *path) -{ -	int err = -EINVAL; - -	if (!*name) { -		pr_err("empty lowerdir\n"); -		goto out; -	} -	err = kern_path(name, LOOKUP_FOLLOW, path); -	if (err) { -		pr_err("failed to resolve '%s': %i\n", name, err); -		goto out; -	} -	err = -EINVAL; -	if (ovl_dentry_weird(path->dentry)) { -		pr_err("filesystem on '%s' not supported\n", name); -		goto out_put; -	} -	if (!d_is_dir(path->dentry)) { -		pr_err("'%s' not a directory\n", name); -		goto out_put; -	} -	return 0; - -out_put: -	path_put_init(path); -out: -	return err; -} - -static int ovl_mount_dir(const char *name, struct path *path) -{ -	int err = -ENOMEM; -	char *tmp = kstrdup(name, GFP_KERNEL); - -	if (tmp) { -		ovl_unescape(tmp); -		err = ovl_mount_dir_noesc(tmp, path); - -		if (!err && path->dentry->d_flags & DCACHE_OP_REAL) { -			pr_err("filesystem on '%s' not supported as upperdir\n", -			       tmp); -			path_put_init(path); -			err = -EINVAL; -		} -		kfree(tmp); -	} -	return err; -} -  static int ovl_check_namelen(const struct path *path, struct ovl_fs *ofs,  			     const char *name)  { @@ -882,10 +808,6 @@ static int ovl_lower_dir(const char *name, struct path *path,  	int fh_type;  	int err; -	err = ovl_mount_dir_noesc(name, path); -	if (err) -		return err; -  	err = ovl_check_namelen(path, ofs, name);  	if (err)  		return err; @@ -934,26 +856,6 @@ 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_own_xattr_get(const struct xattr_handler *handler,  			     struct dentry *dentry, struct inode *inode,  			     const char *name, void *buffer, size_t size) @@ -1054,15 +956,12 @@ static int ovl_report_in_use(struct ovl_fs *ofs, const char *name)  }  static int ovl_get_upper(struct super_block *sb, struct ovl_fs *ofs, -			 struct ovl_layer *upper_layer, struct path *upperpath) +			 struct ovl_layer *upper_layer, +			 const struct path *upperpath)  {  	struct vfsmount *upper_mnt;  	int err; -	err = ovl_mount_dir(ofs->config.upperdir, upperpath); -	if (err) -		goto out; -  	/* Upperdir path should not be r/o */  	if (__mnt_is_readonly(upperpath->mnt)) {  		pr_err("upper fs is r/o, try multi-lower layers mount\n"); @@ -1092,6 +991,11 @@ static int ovl_get_upper(struct super_block *sb, struct ovl_fs *ofs,  	upper_layer->idx = 0;  	upper_layer->fsid = 0; +	err = -ENOMEM; +	upper_layer->name = kstrdup(ofs->config.upperdir, GFP_KERNEL); +	if (!upper_layer->name) +		goto out; +  	/*  	 * Inherit SB_NOSEC flag from upperdir.  	 * @@ -1356,46 +1260,37 @@ out:  }  static int ovl_get_workdir(struct super_block *sb, struct ovl_fs *ofs, -			   const struct path *upperpath) +			   const struct path *upperpath, +			   const struct path *workpath)  {  	int err; -	struct path workpath = { }; - -	err = ovl_mount_dir(ofs->config.workdir, &workpath); -	if (err) -		goto out;  	err = -EINVAL; -	if (upperpath->mnt != workpath.mnt) { +	if (upperpath->mnt != workpath->mnt) {  		pr_err("workdir and upperdir must reside under the same mount\n"); -		goto out; +		return err;  	} -	if (!ovl_workdir_ok(workpath.dentry, upperpath->dentry)) { +	if (!ovl_workdir_ok(workpath->dentry, upperpath->dentry)) {  		pr_err("workdir and upperdir must be separate subtrees\n"); -		goto out; +		return err;  	} -	ofs->workbasedir = dget(workpath.dentry); +	ofs->workbasedir = dget(workpath->dentry);  	if (ovl_inuse_trylock(ofs->workbasedir)) {  		ofs->workdir_locked = true;  	} else {  		err = ovl_report_in_use(ofs, "workdir");  		if (err) -			goto out; +			return err;  	}  	err = ovl_setup_trap(sb, ofs->workbasedir, &ofs->workbasedir_trap,  			     "workdir");  	if (err) -		goto out; - -	err = ovl_make_workdir(sb, ofs, &workpath); - -out: -	path_put(&workpath); +		return err; -	return err; +	return ovl_make_workdir(sb, ofs, workpath);  }  static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs, @@ -1559,13 +1454,13 @@ static int ovl_get_data_fsid(struct ovl_fs *ofs)  static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs, -			  struct path *stack, unsigned int numlower, -			  struct ovl_layer *layers) +			  struct ovl_fs_context *ctx, struct ovl_layer *layers)  {  	int err;  	unsigned int i; +	size_t nr_merged_lower; -	ofs->fs = kcalloc(numlower + 2, sizeof(struct ovl_sb), GFP_KERNEL); +	ofs->fs = kcalloc(ctx->nr + 2, sizeof(struct ovl_sb), GFP_KERNEL);  	if (ofs->fs == NULL)  		return -ENOMEM; @@ -1592,13 +1487,15 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,  		ofs->fs[0].is_lower = false;  	} -	for (i = 0; i < numlower; i++) { +	nr_merged_lower = ctx->nr - ctx->nr_data; +	for (i = 0; i < ctx->nr; i++) { +		struct ovl_fs_context_layer *l = &ctx->lower[i];  		struct vfsmount *mnt;  		struct inode *trap;  		int fsid; -		if (i < numlower - ofs->numdatalayer) -			fsid = ovl_get_fsid(ofs, &stack[i]); +		if (i < nr_merged_lower) +			fsid = ovl_get_fsid(ofs, &l->path);  		else  			fsid = ovl_get_data_fsid(ofs);  		if (fsid < 0) @@ -1611,11 +1508,11 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,  		 * the upperdir/workdir is in fact in-use by our  		 * upperdir/workdir.  		 */ -		err = ovl_setup_trap(sb, stack[i].dentry, &trap, "lowerdir"); +		err = ovl_setup_trap(sb, l->path.dentry, &trap, "lowerdir");  		if (err)  			return err; -		if (ovl_is_inuse(stack[i].dentry)) { +		if (ovl_is_inuse(l->path.dentry)) {  			err = ovl_report_in_use(ofs, "lowerdir");  			if (err) {  				iput(trap); @@ -1623,7 +1520,7 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,  			}  		} -		mnt = clone_private_mount(&stack[i]); +		mnt = clone_private_mount(&l->path);  		err = PTR_ERR(mnt);  		if (IS_ERR(mnt)) {  			pr_err("failed to clone lowerpath\n"); @@ -1642,6 +1539,8 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,  		layers[ofs->numlayer].idx = ofs->numlayer;  		layers[ofs->numlayer].fsid = fsid;  		layers[ofs->numlayer].fs = &ofs->fs[fsid]; +		layers[ofs->numlayer].name = l->name; +		l->name = NULL;  		ofs->numlayer++;  		ofs->fs[fsid].is_lower = true;  	} @@ -1682,104 +1581,59 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,  }  static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, -				const char *lower, unsigned int numlower, -				struct ovl_fs *ofs, struct ovl_layer *layers) +					    struct ovl_fs_context *ctx, +					    struct ovl_fs *ofs, +					    struct ovl_layer *layers)  {  	int err; -	struct path *stack = NULL; -	struct ovl_path *lowerstack; -	unsigned int numlowerdata = 0;  	unsigned int i; +	size_t nr_merged_lower;  	struct ovl_entry *oe; +	struct ovl_path *lowerstack; + +	struct ovl_fs_context_layer *l; -	if (!ofs->config.upperdir && numlower == 1) { +	if (!ofs->config.upperdir && ctx->nr == 1) {  		pr_err("at least 2 lowerdir are needed while upperdir nonexistent\n");  		return ERR_PTR(-EINVAL);  	} -	stack = kcalloc(numlower, sizeof(struct path), GFP_KERNEL); -	if (!stack) -		return ERR_PTR(-ENOMEM); +	err = -EINVAL; +	for (i = 0; i < ctx->nr; i++) { +		l = &ctx->lower[i]; -	for (i = 0; i < numlower;) { -		err = ovl_lower_dir(lower, &stack[i], ofs, &sb->s_stack_depth); +		err = ovl_lower_dir(l->name, &l->path, ofs, &sb->s_stack_depth);  		if (err) -			goto out_err; - -		lower = strchr(lower, '\0') + 1; - -		i++; -		if (i == numlower) -			break; - -		err = -EINVAL; -		/* -		 * Empty lower layer path could mean :: separator that indicates -		 * a data-only lower data. -		 * Several data-only layers are allowed, but they all need to be -		 * at the bottom of the stack. -		 */ -		if (*lower) { -			/* normal lower dir */ -			if (numlowerdata) { -				pr_err("lower data-only dirs must be at the bottom of the stack.\n"); -				goto out_err; -			} -		} else { -			/* data-only lower dir */ -			if (!ofs->config.metacopy) { -				pr_err("lower data-only dirs require metacopy support.\n"); -				goto out_err; -			} -			if (i == numlower - 1) { -				pr_err("lowerdir argument must not end with double colon.\n"); -				goto out_err; -			} -			lower++; -			numlower--; -			numlowerdata++; -		} -	} - -	if (numlowerdata) { -		ofs->numdatalayer = numlowerdata; -		pr_info("using the lowest %d of %d lowerdirs as data layers\n", -			numlowerdata, numlower); +			return ERR_PTR(err);  	}  	err = -EINVAL;  	sb->s_stack_depth++;  	if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) {  		pr_err("maximum fs stacking depth exceeded\n"); -		goto out_err; +		return ERR_PTR(err);  	} -	err = ovl_get_layers(sb, ofs, stack, numlower, layers); +	err = ovl_get_layers(sb, ofs, ctx, layers);  	if (err) -		goto out_err; +		return ERR_PTR(err);  	err = -ENOMEM;  	/* Data-only layers are not merged in root directory */ -	oe = ovl_alloc_entry(numlower - numlowerdata); +	nr_merged_lower = ctx->nr - ctx->nr_data; +	oe = ovl_alloc_entry(nr_merged_lower);  	if (!oe) -		goto out_err; +		return ERR_PTR(err);  	lowerstack = ovl_lowerstack(oe); -	for (i = 0; i < numlower - numlowerdata; i++) { -		lowerstack[i].dentry = dget(stack[i].dentry); -		lowerstack[i].layer = &ofs->layers[i+1]; +	for (i = 0; i < nr_merged_lower; i++) { +		l = &ctx->lower[i]; +		lowerstack[i].dentry = dget(l->path.dentry); +		lowerstack[i].layer = &ofs->layers[i + 1];  	} - -out: -	for (i = 0; i < numlower; i++) -		path_put(&stack[i]); -	kfree(stack); +	ofs->numdatalayer = ctx->nr_data;  	return oe; - -out_err: -	oe = ERR_PTR(err); -	goto out;  }  /* @@ -1897,13 +1751,10 @@ static int ovl_fill_super(struct super_block *sb, struct fs_context *fc)  {  	struct ovl_fs *ofs = sb->s_fs_info;  	struct ovl_fs_context *ctx = fc->fs_private; -	struct path upperpath = {};  	struct dentry *root_dentry;  	struct ovl_entry *oe;  	struct ovl_layer *layers;  	struct cred *cred; -	char *splitlower = NULL; -	unsigned int numlower;  	int err;  	err = -EIO; @@ -1922,27 +1773,14 @@ static int ovl_fill_super(struct super_block *sb, struct fs_context *fc)  		goto out_err;  	err = -EINVAL; -	if (!ofs->config.lowerdir) { +	if (ctx->nr == 0) {  		if (!(fc->sb_flags & SB_SILENT))  			pr_err("missing 'lowerdir'\n");  		goto out_err;  	}  	err = -ENOMEM; -	splitlower = kstrdup(ofs->config.lowerdir, GFP_KERNEL); -	if (!splitlower) -		goto out_err; - -	err = -EINVAL; -	numlower = ovl_split_lowerdirs(splitlower); -	if (numlower > OVL_MAX_STACK) { -		pr_err("too many lower directories, limit is %d\n", -		       OVL_MAX_STACK); -		goto out_err; -	} - -	err = -ENOMEM; -	layers = kcalloc(numlower + 1, sizeof(struct ovl_layer), GFP_KERNEL); +	layers = kcalloc(ctx->nr + 1, sizeof(struct ovl_layer), GFP_KERNEL);  	if (!layers)  		goto out_err; @@ -1974,7 +1812,7 @@ static int ovl_fill_super(struct super_block *sb, struct fs_context *fc)  			goto out_err;  		} -		err = ovl_get_upper(sb, ofs, &layers[0], &upperpath); +		err = ovl_get_upper(sb, ofs, &layers[0], &ctx->upper);  		if (err)  			goto out_err; @@ -1988,7 +1826,7 @@ static int ovl_fill_super(struct super_block *sb, struct fs_context *fc)  			}  		} -		err = ovl_get_workdir(sb, ofs, &upperpath); +		err = ovl_get_workdir(sb, ofs, &ctx->upper, &ctx->work);  		if (err)  			goto out_err; @@ -1998,7 +1836,7 @@ static int ovl_fill_super(struct super_block *sb, struct fs_context *fc)  		sb->s_stack_depth = upper_sb->s_stack_depth;  		sb->s_time_gran = upper_sb->s_time_gran;  	} -	oe = ovl_get_lowerstack(sb, splitlower, numlower, ofs, layers); +	oe = ovl_get_lowerstack(sb, ctx, ofs, layers);  	err = PTR_ERR(oe);  	if (IS_ERR(oe))  		goto out_err; @@ -2013,7 +1851,7 @@ static int ovl_fill_super(struct super_block *sb, struct fs_context *fc)  	}  	if (!ovl_force_readonly(ofs) && ofs->config.index) { -		err = ovl_get_indexdir(sb, ofs, oe, &upperpath); +		err = ovl_get_indexdir(sb, ofs, oe, &ctx->upper);  		if (err)  			goto out_free_oe; @@ -2054,13 +1892,10 @@ static int ovl_fill_super(struct super_block *sb, struct fs_context *fc)  	sb->s_iflags |= SB_I_SKIP_SYNC;  	err = -ENOMEM; -	root_dentry = ovl_get_root(sb, upperpath.dentry, oe); +	root_dentry = ovl_get_root(sb, ctx->upper.dentry, oe);  	if (!root_dentry)  		goto out_free_oe; -	path_put(&upperpath); -	kfree(splitlower); -  	sb->s_root = root_dentry;  	return 0; @@ -2080,6 +1915,10 @@ static int ovl_get_tree(struct fs_context *fc)  static inline void ovl_fs_context_free(struct ovl_fs_context *ctx)  { +	ovl_parse_param_drop_lowerdir(ctx); +	path_put(&ctx->upper); +	path_put(&ctx->work); +	kfree(ctx->lower);  	kfree(ctx);  } @@ -2124,11 +1963,18 @@ static int ovl_init_fs_context(struct fs_context *fc)  	if (!ctx)  		return -ENOMEM; +	/* +	 * By default we allocate for three lower layers. It's likely +	 * that it'll cover most users. +	 */ +	ctx->lower = kmalloc_array(3, sizeof(*ctx->lower), GFP_KERNEL_ACCOUNT); +	if (!ctx->lower) +		goto out_err; +	ctx->capacity = 3; +  	ofs = kzalloc(sizeof(struct ovl_fs), GFP_KERNEL); -	if (!ofs) { -		ovl_fs_context_free(ctx); -		return -ENOMEM; -	} +	if (!ofs) +		goto out_err;  	ofs->config.redirect_mode = ovl_redirect_mode_def();  	ofs->config.index	= ovl_index_def; @@ -2141,6 +1987,11 @@ static int ovl_init_fs_context(struct fs_context *fc)  	fc->fs_private		= ctx;  	fc->ops			= &ovl_context_ops;  	return 0; + +out_err: +	ovl_fs_context_free(ctx); +	return -ENOMEM; +  }  static struct file_system_type ovl_fs_type = { | 
