Skip to content

Commit 58439f6

Browse files
committed
Merge patch series "ovl: file descriptors based layer setup"
Christian Brauner <[email protected]> says: Currently overlayfs only allows specifying layers through path names. This is inconvenient for users such as systemd that want to assemble an overlayfs mount purely based on file descriptors. When porting overlayfs to the new mount api I already mentioned this. This enables user to specify both: fsconfig(fd_overlay, FSCONFIG_SET_FD, "upperdir+", NULL, fd_upper); fsconfig(fd_overlay, FSCONFIG_SET_FD, "workdir+", NULL, fd_work); fsconfig(fd_overlay, FSCONFIG_SET_FD, "lowerdir+", NULL, fd_lower1); fsconfig(fd_overlay, FSCONFIG_SET_FD, "lowerdir+", NULL, fd_lower2); in addition to: fsconfig(fd_overlay, FSCONFIG_SET_STRING, "upperdir+", "/upper", 0); fsconfig(fd_overlay, FSCONFIG_SET_STRING, "workdir+", "/work", 0); fsconfig(fd_overlay, FSCONFIG_SET_STRING, "lowerdir+", "/lower1", 0); fsconfig(fd_overlay, FSCONFIG_SET_STRING, "lowerdir+", "/lower2", 0); The selftest contain an example for this. * patches from https://lore.kernel.org/r/[email protected]: selftests: add overlayfs fd mounting selftests selftests: use shared header Documentation,ovl: document new file descriptor based layers ovl: specify layers via file descriptors fs: add helper to use mount option as path or fd Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Christian Brauner <[email protected]>
2 parents 8e929cb + af91991 commit 58439f6

File tree

9 files changed

+334
-53
lines changed

9 files changed

+334
-53
lines changed

Documentation/filesystems/overlayfs.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,23 @@ For example::
440440
fsconfig(fs_fd, FSCONFIG_SET_STRING, "datadir+", "/do2", 0);
441441

442442

443+
Specifying layers via file descriptors
444+
--------------------------------------
445+
446+
Since kernel v6.13, overlayfs supports specifying layers via file descriptors in
447+
addition to specifying them as paths. This feature is available for the
448+
"datadir+", "lowerdir+", "upperdir", and "workdir+" mount options with the
449+
fsconfig syscall from the new mount api::
450+
451+
fsconfig(fs_fd, FSCONFIG_SET_FD, "lowerdir+", NULL, fd_lower1);
452+
fsconfig(fs_fd, FSCONFIG_SET_FD, "lowerdir+", NULL, fd_lower2);
453+
fsconfig(fs_fd, FSCONFIG_SET_FD, "lowerdir+", NULL, fd_lower3);
454+
fsconfig(fs_fd, FSCONFIG_SET_FD, "datadir+", NULL, fd_data1);
455+
fsconfig(fs_fd, FSCONFIG_SET_FD, "datadir+", NULL, fd_data2);
456+
fsconfig(fs_fd, FSCONFIG_SET_FD, "workdir", NULL, fd_work);
457+
fsconfig(fs_fd, FSCONFIG_SET_FD, "upperdir", NULL, fd_upper);
458+
459+
443460
fs-verity support
444461
-----------------
445462

fs/fs_parser.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,26 @@ int fs_param_is_fd(struct p_log *log, const struct fs_parameter_spec *p,
308308
}
309309
EXPORT_SYMBOL(fs_param_is_fd);
310310

311+
int fs_param_is_file_or_string(struct p_log *log,
312+
const struct fs_parameter_spec *p,
313+
struct fs_parameter *param,
314+
struct fs_parse_result *result)
315+
{
316+
switch (param->type) {
317+
case fs_value_is_string:
318+
return fs_param_is_string(log, p, param, result);
319+
case fs_value_is_file:
320+
result->uint_32 = param->dirfd;
321+
if (result->uint_32 <= INT_MAX)
322+
return 0;
323+
break;
324+
default:
325+
break;
326+
}
327+
return fs_param_bad_value(log, param);
328+
}
329+
EXPORT_SYMBOL(fs_param_is_file_or_string);
330+
311331
int fs_param_is_uid(struct p_log *log, const struct fs_parameter_spec *p,
312332
struct fs_parameter *param, struct fs_parse_result *result)
313333
{

fs/overlayfs/params.c

Lines changed: 91 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,10 @@ static int ovl_verity_mode_def(void)
141141

142142
const struct fs_parameter_spec ovl_parameter_spec[] = {
143143
fsparam_string_empty("lowerdir", Opt_lowerdir),
144-
fsparam_string("lowerdir+", Opt_lowerdir_add),
145-
fsparam_string("datadir+", Opt_datadir_add),
146-
fsparam_string("upperdir", Opt_upperdir),
147-
fsparam_string("workdir", Opt_workdir),
144+
fsparam_file_or_string("lowerdir+", Opt_lowerdir_add),
145+
fsparam_file_or_string("datadir+", Opt_datadir_add),
146+
fsparam_file_or_string("upperdir", Opt_upperdir),
147+
fsparam_file_or_string("workdir", Opt_workdir),
148148
fsparam_flag("default_permissions", Opt_default_permissions),
149149
fsparam_enum("redirect_dir", Opt_redirect_dir, ovl_parameter_redirect_dir),
150150
fsparam_enum("index", Opt_index, ovl_parameter_bool),
@@ -367,40 +367,100 @@ static void ovl_add_layer(struct fs_context *fc, enum ovl_opt layer,
367367
}
368368
}
369369

370-
static int ovl_parse_layer(struct fs_context *fc, const char *layer_name, enum ovl_opt layer)
370+
static inline bool is_upper_layer(enum ovl_opt layer)
371+
{
372+
return layer == Opt_upperdir || layer == Opt_workdir;
373+
}
374+
375+
/* Handle non-file descriptor-based layer options that require path lookup. */
376+
static inline int ovl_kern_path(const char *layer_name, struct path *layer_path,
377+
enum ovl_opt layer)
371378
{
372-
char *name = kstrdup(layer_name, GFP_KERNEL);
373-
bool upper = (layer == Opt_upperdir || layer == Opt_workdir);
374-
struct path path;
375379
int err;
376380

381+
switch (layer) {
382+
case Opt_upperdir:
383+
fallthrough;
384+
case Opt_workdir:
385+
fallthrough;
386+
case Opt_lowerdir:
387+
err = ovl_mount_dir(layer_name, layer_path);
388+
break;
389+
case Opt_lowerdir_add:
390+
fallthrough;
391+
case Opt_datadir_add:
392+
err = ovl_mount_dir_noesc(layer_name, layer_path);
393+
break;
394+
default:
395+
WARN_ON_ONCE(true);
396+
err = -EINVAL;
397+
}
398+
399+
return err;
400+
}
401+
402+
static int ovl_do_parse_layer(struct fs_context *fc, const char *layer_name,
403+
struct path *layer_path, enum ovl_opt layer)
404+
{
405+
char *name __free(kfree) = kstrdup(layer_name, GFP_KERNEL);
406+
bool upper;
407+
int err = 0;
408+
377409
if (!name)
378410
return -ENOMEM;
379411

380-
if (upper || layer == Opt_lowerdir)
381-
err = ovl_mount_dir(name, &path);
382-
else
383-
err = ovl_mount_dir_noesc(name, &path);
412+
upper = is_upper_layer(layer);
413+
err = ovl_mount_dir_check(fc, layer_path, layer, name, upper);
384414
if (err)
385-
goto out_free;
386-
387-
err = ovl_mount_dir_check(fc, &path, layer, name, upper);
388-
if (err)
389-
goto out_put;
415+
return err;
390416

391417
if (!upper) {
392418
err = ovl_ctx_realloc_lower(fc);
393419
if (err)
394-
goto out_put;
420+
return err;
395421
}
396422

397423
/* Store the user provided path string in ctx to show in mountinfo */
398-
ovl_add_layer(fc, layer, &path, &name);
424+
ovl_add_layer(fc, layer, layer_path, &name);
425+
return err;
426+
}
427+
428+
static int ovl_parse_layer(struct fs_context *fc, struct fs_parameter *param,
429+
enum ovl_opt layer)
430+
{
431+
struct path layer_path __free(path_put) = {};
432+
int err = 0;
433+
434+
switch (param->type) {
435+
case fs_value_is_string:
436+
err = ovl_kern_path(param->string, &layer_path, layer);
437+
if (err)
438+
return err;
439+
err = ovl_do_parse_layer(fc, param->string, &layer_path, layer);
440+
break;
441+
case fs_value_is_file: {
442+
char *buf __free(kfree);
443+
char *layer_name;
444+
445+
buf = kmalloc(PATH_MAX, GFP_KERNEL_ACCOUNT);
446+
if (!buf)
447+
return -ENOMEM;
448+
449+
layer_path = param->file->f_path;
450+
path_get(&layer_path);
451+
452+
layer_name = d_path(&layer_path, buf, PATH_MAX);
453+
if (IS_ERR(layer_name))
454+
return PTR_ERR(layer_name);
455+
456+
err = ovl_do_parse_layer(fc, layer_name, &layer_path, layer);
457+
break;
458+
}
459+
default:
460+
WARN_ON_ONCE(true);
461+
err = -EINVAL;
462+
}
399463

400-
out_put:
401-
path_put(&path);
402-
out_free:
403-
kfree(name);
404464
return err;
405465
}
406466

@@ -474,7 +534,13 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
474534

475535
iter = dup;
476536
for (nr = 0; nr < nr_lower; nr++) {
477-
err = ovl_parse_layer(fc, iter, Opt_lowerdir);
537+
struct path path __free(path_put) = {};
538+
539+
err = ovl_kern_path(iter, &path, Opt_lowerdir);
540+
if (err)
541+
goto out_err;
542+
543+
err = ovl_do_parse_layer(fc, iter, &path, Opt_lowerdir);
478544
if (err)
479545
goto out_err;
480546

@@ -555,7 +621,7 @@ static int ovl_parse_param(struct fs_context *fc, struct fs_parameter *param)
555621
case Opt_datadir_add:
556622
case Opt_upperdir:
557623
case Opt_workdir:
558-
err = ovl_parse_layer(fc, param->string, opt);
624+
err = ovl_parse_layer(fc, param, opt);
559625
break;
560626
case Opt_default_permissions:
561627
config->default_permissions = true;

include/linux/fs_parser.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ typedef int fs_param_type(struct p_log *,
2828
*/
2929
fs_param_type fs_param_is_bool, fs_param_is_u32, fs_param_is_s32, fs_param_is_u64,
3030
fs_param_is_enum, fs_param_is_string, fs_param_is_blob, fs_param_is_blockdev,
31-
fs_param_is_path, fs_param_is_fd, fs_param_is_uid, fs_param_is_gid;
31+
fs_param_is_path, fs_param_is_fd, fs_param_is_uid, fs_param_is_gid,
32+
fs_param_is_file_or_string;
3233

3334
/*
3435
* Specification of the type of value a parameter wants.
@@ -133,6 +134,8 @@ static inline bool fs_validate_description(const char *name,
133134
#define fsparam_bdev(NAME, OPT) __fsparam(fs_param_is_blockdev, NAME, OPT, 0, NULL)
134135
#define fsparam_path(NAME, OPT) __fsparam(fs_param_is_path, NAME, OPT, 0, NULL)
135136
#define fsparam_fd(NAME, OPT) __fsparam(fs_param_is_fd, NAME, OPT, 0, NULL)
137+
#define fsparam_file_or_string(NAME, OPT) \
138+
__fsparam(fs_param_is_file_or_string, NAME, OPT, 0, NULL)
136139
#define fsparam_uid(NAME, OPT) __fsparam(fs_param_is_uid, NAME, OPT, 0, NULL)
137140
#define fsparam_gid(NAME, OPT) __fsparam(fs_param_is_gid, NAME, OPT, 0, NULL)
138141

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
# SPDX-License-Identifier: GPL-2.0-only
22
dev_in_maps
3+
set_layers_via_fds

tools/testing/selftests/filesystems/overlayfs/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# SPDX-License-Identifier: GPL-2.0
22

3-
TEST_GEN_PROGS := dev_in_maps
3+
TEST_GEN_PROGS := dev_in_maps set_layers_via_fds
44

55
CFLAGS := -Wall -Werror
66

tools/testing/selftests/filesystems/overlayfs/dev_in_maps.c

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,32 +17,7 @@
1717

1818
#include "../../kselftest.h"
1919
#include "log.h"
20-
21-
static int sys_fsopen(const char *fsname, unsigned int flags)
22-
{
23-
return syscall(__NR_fsopen, fsname, flags);
24-
}
25-
26-
static int sys_fsconfig(int fd, unsigned int cmd, const char *key, const char *value, int aux)
27-
{
28-
return syscall(__NR_fsconfig, fd, cmd, key, value, aux);
29-
}
30-
31-
static int sys_fsmount(int fd, unsigned int flags, unsigned int attr_flags)
32-
{
33-
return syscall(__NR_fsmount, fd, flags, attr_flags);
34-
}
35-
static int sys_mount(const char *src, const char *tgt, const char *fst,
36-
unsigned long flags, const void *data)
37-
{
38-
return syscall(__NR_mount, src, tgt, fst, flags, data);
39-
}
40-
static int sys_move_mount(int from_dfd, const char *from_pathname,
41-
int to_dfd, const char *to_pathname,
42-
unsigned int flags)
43-
{
44-
return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd, to_pathname, flags);
45-
}
20+
#include "wrappers.h"
4621

4722
static long get_file_dev_and_inode(void *addr, struct statx *stx)
4823
{

0 commit comments

Comments
 (0)