Commit 19fd4a91 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'ovl-fixes-6.6-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs

Pull overlayfs fixes from Amir Goldstein:

 - Various fixes for regressions due to conversion to new mount
   api in v6.5

 - Disable a new mount option syntax (append lowerdir) that was
   added in v6.5 because we plan to add a different lowerdir
   append syntax in v6.7

* tag 'ovl-fixes-6.6-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs:
  ovl: temporarily disable appending lowedirs
  ovl: fix regression in showing lowerdir mount option
  ovl: fix regression in parsing of mount options with escaped comma
  fs: factor out vfs_parse_monolithic_sep() helper
parents f8bf101b beae836e
...@@ -339,6 +339,18 @@ The specified lower directories will be stacked beginning from the ...@@ -339,6 +339,18 @@ The specified lower directories will be stacked beginning from the
rightmost one and going left. In the above example lower1 will be the rightmost one and going left. In the above example lower1 will be the
top, lower2 the middle and lower3 the bottom layer. top, lower2 the middle and lower3 the bottom layer.
Note: directory names containing colons can be provided as lower layer by
escaping the colons with a single backslash. For example:
mount -t overlay overlay -olowerdir=/a\:lower\:\:dir /merged
Since kernel version v6.5, directory names containing colons can also
be provided as lower layer using the fsconfig syscall from new mount api:
fsconfig(fs_fd, FSCONFIG_SET_STRING, "lowerdir", "/a:lower::dir", 0);
In the latter case, colons in lower layer directory names will be escaped
as an octal characters (\072) when displayed in /proc/self/mountinfo.
Metadata only copy up Metadata only copy up
--------------------- ---------------------
......
...@@ -192,17 +192,19 @@ int vfs_parse_fs_string(struct fs_context *fc, const char *key, ...@@ -192,17 +192,19 @@ int vfs_parse_fs_string(struct fs_context *fc, const char *key,
EXPORT_SYMBOL(vfs_parse_fs_string); EXPORT_SYMBOL(vfs_parse_fs_string);
/** /**
* generic_parse_monolithic - Parse key[=val][,key[=val]]* mount data * vfs_parse_monolithic_sep - Parse key[=val][,key[=val]]* mount data
* @fc: The superblock configuration to fill in. * @fc: The superblock configuration to fill in.
* @data: The data to parse * @data: The data to parse
* @sep: callback for separating next option
* *
* Parse a blob of data that's in key[=val][,key[=val]]* form. This can be * Parse a blob of data that's in key[=val][,key[=val]]* form with a custom
* called from the ->monolithic_mount_data() fs_context operation. * option separator callback.
* *
* Returns 0 on success or the error returned by the ->parse_option() fs_context * Returns 0 on success or the error returned by the ->parse_option() fs_context
* operation on failure. * operation on failure.
*/ */
int generic_parse_monolithic(struct fs_context *fc, void *data) int vfs_parse_monolithic_sep(struct fs_context *fc, void *data,
char *(*sep)(char **))
{ {
char *options = data, *key; char *options = data, *key;
int ret = 0; int ret = 0;
...@@ -214,7 +216,7 @@ int generic_parse_monolithic(struct fs_context *fc, void *data) ...@@ -214,7 +216,7 @@ int generic_parse_monolithic(struct fs_context *fc, void *data)
if (ret) if (ret)
return ret; return ret;
while ((key = strsep(&options, ",")) != NULL) { while ((key = sep(&options)) != NULL) {
if (*key) { if (*key) {
size_t v_len = 0; size_t v_len = 0;
char *value = strchr(key, '='); char *value = strchr(key, '=');
...@@ -233,6 +235,28 @@ int generic_parse_monolithic(struct fs_context *fc, void *data) ...@@ -233,6 +235,28 @@ int generic_parse_monolithic(struct fs_context *fc, void *data)
return ret; return ret;
} }
EXPORT_SYMBOL(vfs_parse_monolithic_sep);
static char *vfs_parse_comma_sep(char **s)
{
return strsep(s, ",");
}
/**
* generic_parse_monolithic - Parse key[=val][,key[=val]]* mount data
* @fc: The superblock configuration to fill in.
* @data: The data to parse
*
* Parse a blob of data that's in key[=val][,key[=val]]* form. This can be
* called from the ->monolithic_mount_data() fs_context operation.
*
* Returns 0 on success or the error returned by the ->parse_option() fs_context
* operation on failure.
*/
int generic_parse_monolithic(struct fs_context *fc, void *data)
{
return vfs_parse_monolithic_sep(fc, data, vfs_parse_comma_sep);
}
EXPORT_SYMBOL(generic_parse_monolithic); EXPORT_SYMBOL(generic_parse_monolithic);
/** /**
......
...@@ -157,6 +157,34 @@ const struct fs_parameter_spec ovl_parameter_spec[] = { ...@@ -157,6 +157,34 @@ const struct fs_parameter_spec ovl_parameter_spec[] = {
{} {}
}; };
static char *ovl_next_opt(char **s)
{
char *sbegin = *s;
char *p;
if (sbegin == NULL)
return NULL;
for (p = sbegin; *p; p++) {
if (*p == '\\') {
p++;
if (!*p)
break;
} else if (*p == ',') {
*p = '\0';
*s = p + 1;
return sbegin;
}
}
*s = NULL;
return sbegin;
}
static int ovl_parse_monolithic(struct fs_context *fc, void *data)
{
return vfs_parse_monolithic_sep(fc, data, ovl_next_opt);
}
static ssize_t ovl_parse_param_split_lowerdirs(char *str) static ssize_t ovl_parse_param_split_lowerdirs(char *str)
{ {
ssize_t nr_layers = 1, nr_colons = 0; ssize_t nr_layers = 1, nr_colons = 0;
...@@ -164,7 +192,8 @@ static ssize_t ovl_parse_param_split_lowerdirs(char *str) ...@@ -164,7 +192,8 @@ static ssize_t ovl_parse_param_split_lowerdirs(char *str)
for (s = d = str;; s++, d++) { for (s = d = str;; s++, d++) {
if (*s == '\\') { if (*s == '\\') {
s++; /* keep esc chars in split lowerdir */
*d++ = *s++;
} else if (*s == ':') { } else if (*s == ':') {
bool next_colon = (*(s + 1) == ':'); bool next_colon = (*(s + 1) == ':');
...@@ -239,7 +268,7 @@ static void ovl_unescape(char *s) ...@@ -239,7 +268,7 @@ static void ovl_unescape(char *s)
} }
} }
static int ovl_mount_dir(const char *name, struct path *path) static int ovl_mount_dir(const char *name, struct path *path, bool upper)
{ {
int err = -ENOMEM; int err = -ENOMEM;
char *tmp = kstrdup(name, GFP_KERNEL); char *tmp = kstrdup(name, GFP_KERNEL);
...@@ -248,7 +277,7 @@ static int ovl_mount_dir(const char *name, struct path *path) ...@@ -248,7 +277,7 @@ static int ovl_mount_dir(const char *name, struct path *path)
ovl_unescape(tmp); ovl_unescape(tmp);
err = ovl_mount_dir_noesc(tmp, path); err = ovl_mount_dir_noesc(tmp, path);
if (!err && path->dentry->d_flags & DCACHE_OP_REAL) { if (!err && upper && path->dentry->d_flags & DCACHE_OP_REAL) {
pr_err("filesystem on '%s' not supported as upperdir\n", pr_err("filesystem on '%s' not supported as upperdir\n",
tmp); tmp);
path_put_init(path); path_put_init(path);
...@@ -269,7 +298,7 @@ static int ovl_parse_param_upperdir(const char *name, struct fs_context *fc, ...@@ -269,7 +298,7 @@ static int ovl_parse_param_upperdir(const char *name, struct fs_context *fc,
struct path path; struct path path;
char *dup; char *dup;
err = ovl_mount_dir(name, &path); err = ovl_mount_dir(name, &path, true);
if (err) if (err)
return err; return err;
...@@ -321,12 +350,6 @@ static void ovl_parse_param_drop_lowerdir(struct ovl_fs_context *ctx) ...@@ -321,12 +350,6 @@ static void ovl_parse_param_drop_lowerdir(struct ovl_fs_context *ctx)
* Set "/lower1", "/lower2", and "/lower3" as lower layers and * Set "/lower1", "/lower2", and "/lower3" as lower layers and
* "/data1" and "/data2" as data lower layers. Any existing lower * "/data1" and "/data2" as data lower layers. Any existing lower
* layers are replaced. * 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.
*/ */
static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc) static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
{ {
...@@ -348,51 +371,11 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc) ...@@ -348,51 +371,11 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
return 0; return 0;
} }
if (strncmp(name, "::", 2) == 0) { if (*name == ':') {
/* pr_err("cannot append lower layer");
* 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; 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); dup = kstrdup(name, GFP_KERNEL);
if (!dup) if (!dup)
return -ENOMEM; return -ENOMEM;
...@@ -472,7 +455,7 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc) ...@@ -472,7 +455,7 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
l = &ctx->lower[nr]; l = &ctx->lower[nr];
memset(l, 0, sizeof(*l)); memset(l, 0, sizeof(*l));
err = ovl_mount_dir_noesc(dup_iter, &l->path); err = ovl_mount_dir(dup_iter, &l->path, false);
if (err) if (err)
goto out_put; goto out_put;
...@@ -682,6 +665,7 @@ static int ovl_reconfigure(struct fs_context *fc) ...@@ -682,6 +665,7 @@ static int ovl_reconfigure(struct fs_context *fc)
} }
static const struct fs_context_operations ovl_context_ops = { static const struct fs_context_operations ovl_context_ops = {
.parse_monolithic = ovl_parse_monolithic,
.parse_param = ovl_parse_param, .parse_param = ovl_parse_param,
.get_tree = ovl_get_tree, .get_tree = ovl_get_tree,
.reconfigure = ovl_reconfigure, .reconfigure = ovl_reconfigure,
...@@ -950,16 +934,23 @@ int ovl_show_options(struct seq_file *m, struct dentry *dentry) ...@@ -950,16 +934,23 @@ int ovl_show_options(struct seq_file *m, struct dentry *dentry)
struct super_block *sb = dentry->d_sb; struct super_block *sb = dentry->d_sb;
struct ovl_fs *ofs = OVL_FS(sb); struct ovl_fs *ofs = OVL_FS(sb);
size_t nr, nr_merged_lower = ofs->numlayer - ofs->numdatalayer; size_t nr, nr_merged_lower = ofs->numlayer - ofs->numdatalayer;
char **lowerdatadirs = &ofs->config.lowerdirs[nr_merged_lower];
/*
/* lowerdirs[] starts from offset 1 */ * lowerdirs[] starts from offset 1, then
seq_printf(m, ",lowerdir=%s", ofs->config.lowerdirs[1]); * >= 0 regular lower layers prefixed with : and
/* dump regular lower layers */ * >= 0 data-only lower layers prefixed with ::
for (nr = 2; nr < nr_merged_lower; nr++) *
seq_printf(m, ":%s", ofs->config.lowerdirs[nr]); * we need to escase comma and space like seq_show_option() does and
/* dump data lower layers */ * we also need to escape the colon separator from lowerdir paths.
for (nr = 0; nr < ofs->numdatalayer; nr++) */
seq_printf(m, "::%s", lowerdatadirs[nr]); seq_puts(m, ",lowerdir=");
for (nr = 1; nr < ofs->numlayer; nr++) {
if (nr > 1)
seq_putc(m, ':');
if (nr >= nr_merged_lower)
seq_putc(m, ':');
seq_escape(m, ofs->config.lowerdirs[nr], ":, \t\n\\");
}
if (ofs->config.upperdir) { if (ofs->config.upperdir) {
seq_show_option(m, "upperdir", ofs->config.upperdir); seq_show_option(m, "upperdir", ofs->config.upperdir);
seq_show_option(m, "workdir", ofs->config.workdir); seq_show_option(m, "workdir", ofs->config.workdir);
......
...@@ -136,6 +136,8 @@ extern struct fs_context *vfs_dup_fs_context(struct fs_context *fc); ...@@ -136,6 +136,8 @@ extern struct fs_context *vfs_dup_fs_context(struct fs_context *fc);
extern int vfs_parse_fs_param(struct fs_context *fc, struct fs_parameter *param); extern int vfs_parse_fs_param(struct fs_context *fc, struct fs_parameter *param);
extern int vfs_parse_fs_string(struct fs_context *fc, const char *key, extern int vfs_parse_fs_string(struct fs_context *fc, const char *key,
const char *value, size_t v_size); const char *value, size_t v_size);
int vfs_parse_monolithic_sep(struct fs_context *fc, void *data,
char *(*sep)(char **));
extern int generic_parse_monolithic(struct fs_context *fc, void *data); extern int generic_parse_monolithic(struct fs_context *fc, void *data);
extern int vfs_get_tree(struct fs_context *fc); extern int vfs_get_tree(struct fs_context *fc);
extern void put_fs_context(struct fs_context *fc); extern void put_fs_context(struct fs_context *fc);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment