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:
commit
19fd4a91dd
|
@ -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
|
||||
---------------------
|
||||
|
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue