Skip to content

Commit 19fd4a9

Browse files
committed
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
2 parents f8bf101 + beae836 commit 19fd4a9

File tree

4 files changed

+98
-69
lines changed

4 files changed

+98
-69
lines changed

Documentation/filesystems/overlayfs.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,18 @@ The specified lower directories will be stacked beginning from the
339339
rightmost one and going left. In the above example lower1 will be the
340340
top, lower2 the middle and lower3 the bottom layer.
341341

342+
Note: directory names containing colons can be provided as lower layer by
343+
escaping the colons with a single backslash. For example:
344+
345+
mount -t overlay overlay -olowerdir=/a\:lower\:\:dir /merged
346+
347+
Since kernel version v6.5, directory names containing colons can also
348+
be provided as lower layer using the fsconfig syscall from new mount api:
349+
350+
fsconfig(fs_fd, FSCONFIG_SET_STRING, "lowerdir", "/a:lower::dir", 0);
351+
352+
In the latter case, colons in lower layer directory names will be escaped
353+
as an octal characters (\072) when displayed in /proc/self/mountinfo.
342354

343355
Metadata only copy up
344356
---------------------

fs/fs_context.c

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -192,17 +192,19 @@ int vfs_parse_fs_string(struct fs_context *fc, const char *key,
192192
EXPORT_SYMBOL(vfs_parse_fs_string);
193193

194194
/**
195-
* generic_parse_monolithic - Parse key[=val][,key[=val]]* mount data
195+
* vfs_parse_monolithic_sep - Parse key[=val][,key[=val]]* mount data
196196
* @fc: The superblock configuration to fill in.
197197
* @data: The data to parse
198+
* @sep: callback for separating next option
198199
*
199-
* Parse a blob of data that's in key[=val][,key[=val]]* form. This can be
200-
* called from the ->monolithic_mount_data() fs_context operation.
200+
* Parse a blob of data that's in key[=val][,key[=val]]* form with a custom
201+
* option separator callback.
201202
*
202203
* Returns 0 on success or the error returned by the ->parse_option() fs_context
203204
* operation on failure.
204205
*/
205-
int generic_parse_monolithic(struct fs_context *fc, void *data)
206+
int vfs_parse_monolithic_sep(struct fs_context *fc, void *data,
207+
char *(*sep)(char **))
206208
{
207209
char *options = data, *key;
208210
int ret = 0;
@@ -214,7 +216,7 @@ int generic_parse_monolithic(struct fs_context *fc, void *data)
214216
if (ret)
215217
return ret;
216218

217-
while ((key = strsep(&options, ",")) != NULL) {
219+
while ((key = sep(&options)) != NULL) {
218220
if (*key) {
219221
size_t v_len = 0;
220222
char *value = strchr(key, '=');
@@ -233,6 +235,28 @@ int generic_parse_monolithic(struct fs_context *fc, void *data)
233235

234236
return ret;
235237
}
238+
EXPORT_SYMBOL(vfs_parse_monolithic_sep);
239+
240+
static char *vfs_parse_comma_sep(char **s)
241+
{
242+
return strsep(s, ",");
243+
}
244+
245+
/**
246+
* generic_parse_monolithic - Parse key[=val][,key[=val]]* mount data
247+
* @fc: The superblock configuration to fill in.
248+
* @data: The data to parse
249+
*
250+
* Parse a blob of data that's in key[=val][,key[=val]]* form. This can be
251+
* called from the ->monolithic_mount_data() fs_context operation.
252+
*
253+
* Returns 0 on success or the error returned by the ->parse_option() fs_context
254+
* operation on failure.
255+
*/
256+
int generic_parse_monolithic(struct fs_context *fc, void *data)
257+
{
258+
return vfs_parse_monolithic_sep(fc, data, vfs_parse_comma_sep);
259+
}
236260
EXPORT_SYMBOL(generic_parse_monolithic);
237261

238262
/**

fs/overlayfs/params.c

Lines changed: 55 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -157,14 +157,43 @@ const struct fs_parameter_spec ovl_parameter_spec[] = {
157157
{}
158158
};
159159

160+
static char *ovl_next_opt(char **s)
161+
{
162+
char *sbegin = *s;
163+
char *p;
164+
165+
if (sbegin == NULL)
166+
return NULL;
167+
168+
for (p = sbegin; *p; p++) {
169+
if (*p == '\\') {
170+
p++;
171+
if (!*p)
172+
break;
173+
} else if (*p == ',') {
174+
*p = '\0';
175+
*s = p + 1;
176+
return sbegin;
177+
}
178+
}
179+
*s = NULL;
180+
return sbegin;
181+
}
182+
183+
static int ovl_parse_monolithic(struct fs_context *fc, void *data)
184+
{
185+
return vfs_parse_monolithic_sep(fc, data, ovl_next_opt);
186+
}
187+
160188
static ssize_t ovl_parse_param_split_lowerdirs(char *str)
161189
{
162190
ssize_t nr_layers = 1, nr_colons = 0;
163191
char *s, *d;
164192

165193
for (s = d = str;; s++, d++) {
166194
if (*s == '\\') {
167-
s++;
195+
/* keep esc chars in split lowerdir */
196+
*d++ = *s++;
168197
} else if (*s == ':') {
169198
bool next_colon = (*(s + 1) == ':');
170199

@@ -239,7 +268,7 @@ static void ovl_unescape(char *s)
239268
}
240269
}
241270

242-
static int ovl_mount_dir(const char *name, struct path *path)
271+
static int ovl_mount_dir(const char *name, struct path *path, bool upper)
243272
{
244273
int err = -ENOMEM;
245274
char *tmp = kstrdup(name, GFP_KERNEL);
@@ -248,7 +277,7 @@ static int ovl_mount_dir(const char *name, struct path *path)
248277
ovl_unescape(tmp);
249278
err = ovl_mount_dir_noesc(tmp, path);
250279

251-
if (!err && path->dentry->d_flags & DCACHE_OP_REAL) {
280+
if (!err && upper && path->dentry->d_flags & DCACHE_OP_REAL) {
252281
pr_err("filesystem on '%s' not supported as upperdir\n",
253282
tmp);
254283
path_put_init(path);
@@ -269,7 +298,7 @@ static int ovl_parse_param_upperdir(const char *name, struct fs_context *fc,
269298
struct path path;
270299
char *dup;
271300

272-
err = ovl_mount_dir(name, &path);
301+
err = ovl_mount_dir(name, &path, true);
273302
if (err)
274303
return err;
275304

@@ -321,12 +350,6 @@ static void ovl_parse_param_drop_lowerdir(struct ovl_fs_context *ctx)
321350
* Set "/lower1", "/lower2", and "/lower3" as lower layers and
322351
* "/data1" and "/data2" as data lower layers. Any existing lower
323352
* layers are replaced.
324-
* (2) lowerdir=:/lower4
325-
* Append "/lower4" to current stack of lower layers. This requires
326-
* that there already is at least one lower layer configured.
327-
* (3) lowerdir=::/lower5
328-
* Append data "/lower5" as data lower layer. This requires that
329-
* there's at least one regular lower layer present.
330353
*/
331354
static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
332355
{
@@ -348,49 +371,9 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
348371
return 0;
349372
}
350373

351-
if (strncmp(name, "::", 2) == 0) {
352-
/*
353-
* This is a data layer.
354-
* There must be at least one regular lower layer
355-
* specified.
356-
*/
357-
if (ctx->nr == 0) {
358-
pr_err("data lower layers without regular lower layers not allowed");
359-
return -EINVAL;
360-
}
361-
362-
/* Skip the leading "::". */
363-
name += 2;
364-
data_layer = true;
365-
/*
366-
* A data layer is automatically an append as there
367-
* must've been at least one regular lower layer.
368-
*/
369-
append = true;
370-
} else if (*name == ':') {
371-
/*
372-
* This is a regular lower layer.
373-
* If users want to append a layer enforce that they
374-
* have already specified a first layer before. It's
375-
* better to be strict.
376-
*/
377-
if (ctx->nr == 0) {
378-
pr_err("cannot append layer if no previous layer has been specified");
379-
return -EINVAL;
380-
}
381-
382-
/*
383-
* Once a sequence of data layers has started regular
384-
* lower layers are forbidden.
385-
*/
386-
if (ctx->nr_data > 0) {
387-
pr_err("regular lower layers cannot follow data lower layers");
388-
return -EINVAL;
389-
}
390-
391-
/* Skip the leading ":". */
392-
name++;
393-
append = true;
374+
if (*name == ':') {
375+
pr_err("cannot append lower layer");
376+
return -EINVAL;
394377
}
395378

396379
dup = kstrdup(name, GFP_KERNEL);
@@ -472,7 +455,7 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
472455
l = &ctx->lower[nr];
473456
memset(l, 0, sizeof(*l));
474457

475-
err = ovl_mount_dir_noesc(dup_iter, &l->path);
458+
err = ovl_mount_dir(dup_iter, &l->path, false);
476459
if (err)
477460
goto out_put;
478461

@@ -682,6 +665,7 @@ static int ovl_reconfigure(struct fs_context *fc)
682665
}
683666

684667
static const struct fs_context_operations ovl_context_ops = {
668+
.parse_monolithic = ovl_parse_monolithic,
685669
.parse_param = ovl_parse_param,
686670
.get_tree = ovl_get_tree,
687671
.reconfigure = ovl_reconfigure,
@@ -950,16 +934,23 @@ int ovl_show_options(struct seq_file *m, struct dentry *dentry)
950934
struct super_block *sb = dentry->d_sb;
951935
struct ovl_fs *ofs = OVL_FS(sb);
952936
size_t nr, nr_merged_lower = ofs->numlayer - ofs->numdatalayer;
953-
char **lowerdatadirs = &ofs->config.lowerdirs[nr_merged_lower];
954-
955-
/* lowerdirs[] starts from offset 1 */
956-
seq_printf(m, ",lowerdir=%s", ofs->config.lowerdirs[1]);
957-
/* dump regular lower layers */
958-
for (nr = 2; nr < nr_merged_lower; nr++)
959-
seq_printf(m, ":%s", ofs->config.lowerdirs[nr]);
960-
/* dump data lower layers */
961-
for (nr = 0; nr < ofs->numdatalayer; nr++)
962-
seq_printf(m, "::%s", lowerdatadirs[nr]);
937+
938+
/*
939+
* lowerdirs[] starts from offset 1, then
940+
* >= 0 regular lower layers prefixed with : and
941+
* >= 0 data-only lower layers prefixed with ::
942+
*
943+
* we need to escase comma and space like seq_show_option() does and
944+
* we also need to escape the colon separator from lowerdir paths.
945+
*/
946+
seq_puts(m, ",lowerdir=");
947+
for (nr = 1; nr < ofs->numlayer; nr++) {
948+
if (nr > 1)
949+
seq_putc(m, ':');
950+
if (nr >= nr_merged_lower)
951+
seq_putc(m, ':');
952+
seq_escape(m, ofs->config.lowerdirs[nr], ":, \t\n\\");
953+
}
963954
if (ofs->config.upperdir) {
964955
seq_show_option(m, "upperdir", ofs->config.upperdir);
965956
seq_show_option(m, "workdir", ofs->config.workdir);

include/linux/fs_context.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ extern struct fs_context *vfs_dup_fs_context(struct fs_context *fc);
136136
extern int vfs_parse_fs_param(struct fs_context *fc, struct fs_parameter *param);
137137
extern int vfs_parse_fs_string(struct fs_context *fc, const char *key,
138138
const char *value, size_t v_size);
139+
int vfs_parse_monolithic_sep(struct fs_context *fc, void *data,
140+
char *(*sep)(char **));
139141
extern int generic_parse_monolithic(struct fs_context *fc, void *data);
140142
extern int vfs_get_tree(struct fs_context *fc);
141143
extern void put_fs_context(struct fs_context *fc);

0 commit comments

Comments
 (0)