--- /dev/null
+// 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;
+}
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,
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);
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)
{
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);
{}
};
+#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),
{}
};
-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;
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;
/*
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;
{
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) {
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)
{
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;
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)
}
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");
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.
*
}
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,
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;
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)
* 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);
}
}
- 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");
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;
}
}
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;
}
/*
{
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;
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;
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;
}
}
- err = ovl_get_workdir(sb, ofs, &upperpath);
+ err = ovl_get_workdir(sb, ofs, &ctx->upper, &ctx->work);
if (err)
goto out_err;
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;
}
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;
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;
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);
}
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;
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 = {