overlayfs fixes for 6.6-rc6

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE9zuTYTs0RXF+Ke33EVvVyTe/1WoFAmUrsD8ACgkQEVvVyTe/
 1WpCmg//XfJm6TdeFFuoEoYezytHXnEHnIM+kHMOqmtY1AoaW4884dvevKOCBsUF
 BXXfnIsJc1GmKBDCOxaWFXTRN9iLHTLww8dZzJFX1vDYsqo96t7TbbssyOuOy9vp
 K4uYX/pZzuggyBCUZvz/UdjZPEAwPVmsU/uXe/gkHQLS+wpOH0e7hOB7p91I0iZx
 0JdleyoDyw2AtqWLscGrTYqozW+lNMl0smADWaGfLcn29rRkR10mhq5heYa+4b2C
 TiG29rpIDgXyhz9w+Tq4f3oxJDrE1qlQGX7G8tJjISS9UDHxrZqFun0+nr15Y8Ge
 3YstSaMlarAx+UGtuEX80QcYHlYDAWnCKwkRAD8wBtKLeO0pG6BSmCCVi34CAoN+
 NBy5KQeoDV96vqoIc8lDmXWOgOkzOogJzzspOWT3H7jmonTjkYL/rYGuV1V6lpuk
 Ngopc7HxjiZeGb9Zchr1KOT6xJzjYm74/Ph8I9ECPAamgO6UxlOcsBF2uc7OZIlL
 Jzv5Hl+ITiXzxa/KQonx2nEtdO8INf5wxHjy4nlnnY0dcibGCA1wi/3/8/vqJBIf
 tuRoQJ/eHruTf23dwPRFwo4JoOViSv3Up9GFyEodllBA6DSAHK96low8eeEZAw9e
 MeyWy45dewgH1jvx3bb8Sd29CVxUXvMEl33hYngu09/cv7XulMw=
 =9LTH
 -----END PGP SIGNATURE-----

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
This commit is contained in:
Linus Torvalds 2023-10-15 08:55:51 -07:00
commit 19fd4a91dd
4 changed files with 97 additions and 68 deletions

View File

@ -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
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
---------------------

View File

@ -192,17 +192,19 @@ int vfs_parse_fs_string(struct fs_context *fc, const char *key,
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.
* @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
* called from the ->monolithic_mount_data() fs_context operation.
* Parse a blob of data that's in key[=val][,key[=val]]* form with a custom
* option separator callback.
*
* 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)
int vfs_parse_monolithic_sep(struct fs_context *fc, void *data,
char *(*sep)(char **))
{
char *options = data, *key;
int ret = 0;
@ -214,7 +216,7 @@ int generic_parse_monolithic(struct fs_context *fc, void *data)
if (ret)
return ret;
while ((key = strsep(&options, ",")) != NULL) {
while ((key = sep(&options)) != NULL) {
if (*key) {
size_t v_len = 0;
char *value = strchr(key, '=');
@ -233,6 +235,28 @@ int generic_parse_monolithic(struct fs_context *fc, void *data)
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);
/**

View File

@ -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)
{
ssize_t nr_layers = 1, nr_colons = 0;
@ -164,7 +192,8 @@ static ssize_t ovl_parse_param_split_lowerdirs(char *str)
for (s = d = str;; s++, d++) {
if (*s == '\\') {
s++;
/* keep esc chars in split lowerdir */
*d++ = *s++;
} else if (*s == ':') {
bool next_colon = (*(s + 1) == ':');
@ -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;
char *tmp = kstrdup(name, GFP_KERNEL);
@ -248,7 +277,7 @@ static int ovl_mount_dir(const char *name, struct path *path)
ovl_unescape(tmp);
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",
tmp);
path_put_init(path);
@ -269,7 +298,7 @@ static int ovl_parse_param_upperdir(const char *name, struct fs_context *fc,
struct path path;
char *dup;
err = ovl_mount_dir(name, &path);
err = ovl_mount_dir(name, &path, true);
if (err)
return err;
@ -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
* "/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.
*/
static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
{
@ -348,49 +371,9 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
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;
if (*name == ':') {
pr_err("cannot append lower layer");
return -EINVAL;
}
dup = kstrdup(name, GFP_KERNEL);
@ -472,7 +455,7 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
l = &ctx->lower[nr];
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)
goto out_put;
@ -682,6 +665,7 @@ static int ovl_reconfigure(struct fs_context *fc)
}
static const struct fs_context_operations ovl_context_ops = {
.parse_monolithic = ovl_parse_monolithic,
.parse_param = ovl_parse_param,
.get_tree = ovl_get_tree,
.reconfigure = ovl_reconfigure,
@ -950,16 +934,23 @@ int ovl_show_options(struct seq_file *m, struct dentry *dentry)
struct super_block *sb = dentry->d_sb;
struct ovl_fs *ofs = OVL_FS(sb);
size_t nr, nr_merged_lower = ofs->numlayer - ofs->numdatalayer;
char **lowerdatadirs = &ofs->config.lowerdirs[nr_merged_lower];
/* lowerdirs[] starts from offset 1 */
seq_printf(m, ",lowerdir=%s", ofs->config.lowerdirs[1]);
/* dump regular lower layers */
for (nr = 2; nr < nr_merged_lower; nr++)
seq_printf(m, ":%s", ofs->config.lowerdirs[nr]);
/* dump data lower layers */
for (nr = 0; nr < ofs->numdatalayer; nr++)
seq_printf(m, "::%s", lowerdatadirs[nr]);
/*
* lowerdirs[] starts from offset 1, then
* >= 0 regular lower layers prefixed with : and
* >= 0 data-only lower layers prefixed with ::
*
* we need to escase comma and space like seq_show_option() does and
* we also need to escape the colon separator from lowerdir paths.
*/
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) {
seq_show_option(m, "upperdir", ofs->config.upperdir);
seq_show_option(m, "workdir", ofs->config.workdir);

View File

@ -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_string(struct fs_context *fc, const char *key,
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 vfs_get_tree(struct fs_context *fc);
extern void put_fs_context(struct fs_context *fc);