On Mon, Oct 30, 2023 at 2:04 PM Amir Goldstein <amir73il@xxxxxxxxx> wrote: > > Add new mount options lowerdir+ and datadir+ that can be used to add > layers to lower layers stack one by one. > > Unlike the legacy lowerdir mount option, special characters (i.e. colons > and cammas) are not unescaped with these new mount options. > > The new mount options can be repeated to compose a large stack of lower > layers, but they may not be mixed with the lagacy lowerdir mount option, > because for displaying lower layers in mountinfo, we do not want to mix > escaped with unescaped lower layers path syntax. > > Similar to data-only layer rules with the lowerdir mount option, the > datadir+ option must follow at least one lowerdir+ option and the > lowerdir+ option must not follow the datadir+ option. > > If the legacy lowerdir mount option follows lowerdir+ and datadir+ > mount options, it overrides them. Sepcifically, calling: > > fsconfig(FSCONFIG_SET_STRING, "lowerdir", "", 0); > > can be used to reset previously setup lower layers. > > Suggested-by: Miklos Szeredi <miklos@xxxxxxxxxx> > Link: https://lore.kernel.org/r/CAJfpegt7VC94KkRtb1dfHG8+4OzwPBLYqhtc8=QFUxpFJE+=RQ@xxxxxxxxxxxxxx/ > Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx> > --- > fs/overlayfs/params.c | 78 +++++++++++++++++++++++++++++++++++++++++-- > 1 file changed, 76 insertions(+), 2 deletions(-) > > diff --git a/fs/overlayfs/params.c b/fs/overlayfs/params.c > index 9a9238eac730..1c390e93d060 100644 > --- a/fs/overlayfs/params.c > +++ b/fs/overlayfs/params.c > @@ -45,6 +45,8 @@ MODULE_PARM_DESC(metacopy, > > enum ovl_opt { > Opt_lowerdir, > + Opt_lowerdir_add, > + Opt_datadir_add, > Opt_upperdir, > Opt_workdir, > Opt_default_permissions, > @@ -140,8 +142,11 @@ static int ovl_verity_mode_def(void) > #define fsparam_string_empty(NAME, OPT) \ > __fsparam(fs_param_is_string, NAME, OPT, fs_param_can_be_empty, NULL) > > + > const struct fs_parameter_spec ovl_parameter_spec[] = { > fsparam_string_empty("lowerdir", Opt_lowerdir), > + fsparam_string("lowerdir+", Opt_lowerdir_add), > + fsparam_string("datadir+", Opt_datadir_add), > fsparam_string("upperdir", Opt_upperdir), > fsparam_string("workdir", Opt_workdir), > fsparam_flag("default_permissions", Opt_default_permissions), > @@ -273,6 +278,8 @@ static int ovl_mount_dir(const char *name, struct path *path) > static int ovl_mount_dir_check(struct fs_context *fc, const struct path *path, > enum ovl_opt layer, const char *name, bool upper) > { > + struct ovl_fs_context *ctx = fc->fs_private; > + > if (ovl_dentry_weird(path->dentry)) > return invalfc(fc, "filesystem on %s not supported", name); > > @@ -289,16 +296,44 @@ static int ovl_mount_dir_check(struct fs_context *fc, const struct path *path, > return invalfc(fc, "filesystem not supported as %s", name); > if (__mnt_is_readonly(path->mnt)) > return invalfc(fc, "%s is read-only", name); > + } else { > + if (ctx->lowerdir_all) > + return invalfc(fc, "%s cannot follow lowerdir mount option", name); > + if (ctx->nr_data && layer == Opt_lowerdir_add) > + return invalfc(fc, "regular lower layers cannot follow data layers"); > + if (ctx->nr == OVL_MAX_STACK) > + return invalfc(fc, "too many lower directories, limit is %d", > + OVL_MAX_STACK); > } > return 0; > } > > +static int ovl_ctx_realloc_lower(struct fs_context *fc) > +{ > + struct ovl_fs_context *ctx = fc->fs_private; > + struct ovl_fs_context_layer *l; > + size_t nr; > + > + if (ctx->nr < ctx->capacity) > + return 0; > + > + nr = min(max(4096 / sizeof(*l), ctx->capacity * 2), (size_t) OVL_MAX_STACK); > + l = krealloc_array(ctx->lower, nr, sizeof(*l), GFP_KERNEL_ACCOUNT); > + if (!l) > + return -ENOMEM; > + > + ctx->lower = l; > + ctx->capacity = nr; > + return 0; > +} > + > static void ovl_add_layer(struct fs_context *fc, enum ovl_opt layer, > struct path *path, char **pname) > { > struct ovl_fs *ofs = fc->s_fs_info; > struct ovl_config *config = &ofs->config; > struct ovl_fs_context *ctx = fc->fs_private; > + struct ovl_fs_context_layer *l; > > switch (layer) { > case Opt_workdir: > @@ -309,6 +344,16 @@ static void ovl_add_layer(struct fs_context *fc, enum ovl_opt layer, > swap(config->upperdir, *pname); > swap(ctx->upper, *path); > break; > + case Opt_datadir_add: > + ctx->nr_data++; > + fallthrough; > + case Opt_lowerdir_add: > + WARN_ON(ctx->nr >= ctx->capacity); > + l = &ctx->lower[ctx->nr++]; > + memset(l, 0, sizeof(*l)); > + swap(l->name, *pname); > + swap(l->path, *path); > + break; > default: > WARN_ON(1); > } > @@ -324,7 +369,10 @@ static int ovl_parse_layer(struct fs_context *fc, struct fs_parameter *param, > if (!name) > return -ENOMEM; > > - err = ovl_mount_dir(name, &path); > + if (upper) > + err = ovl_mount_dir(name, &path); > + else > + err = ovl_mount_dir_noesc(name, &path); > if (err) > goto out_free; > > @@ -332,6 +380,12 @@ static int ovl_parse_layer(struct fs_context *fc, struct fs_parameter *param, > if (err) > goto out_put; > > + if (!upper) { > + err = ovl_ctx_realloc_lower(fc); > + if (err) > + goto out_put; > + } > + > /* Store the user provided path string in ctx to show in mountinfo */ > ovl_add_layer(fc, layer, &path, &name); > > @@ -514,6 +568,10 @@ static int ovl_parse_param(struct fs_context *fc, struct fs_parameter *param) > case Opt_lowerdir: > err = ovl_parse_param_lowerdir(param->string, fc); > break; > + case Opt_lowerdir_add: > + case Opt_datadir_add: > + err = ovl_parse_layer(fc, param, opt, false); > + break; > case Opt_upperdir: > case Opt_workdir: > err = ovl_parse_layer(fc, param, opt, true); Sorry, ovl_parse_layer() doesn't need bool upper arg. Your POC didn't have it. I removed that and pushed to overlayfs-next. Thanks, Amir.