mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
vfs-6.13.ovl
-----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCZzcXJQAKCRCRxhvAZXjc opmaAQDuEpUXi3PSNwgXGRYAzeBpsonoNrBtzSRTlaza7KZCiAEAsFUSbQJNGdkN TqC9KvI1EiX2hVvWmVBVttBNH6CY+wY= =4le5 -----END PGP SIGNATURE----- Merge tag 'vfs-6.13.ovl' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs Pull overlayfs updates from Christian Brauner: "Make overlayfs support specifying layers through file descriptors. Currently overlayfs only allows specifying layers through path names. This is inconvenient for users that want to assemble an overlayfs mount purely based on file descriptors: 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); There's also a large set of new overlayfs selftests to test new features and some older properties" * tag 'vfs-6.13.ovl' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: selftests: add test for specifying 500 lower layers 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
This commit is contained in:
commit
a29835c9d0
@ -440,6 +440,23 @@ For example::
|
|||||||
fsconfig(fs_fd, FSCONFIG_SET_STRING, "datadir+", "/do2", 0);
|
fsconfig(fs_fd, FSCONFIG_SET_STRING, "datadir+", "/do2", 0);
|
||||||
|
|
||||||
|
|
||||||
|
Specifying layers via file descriptors
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
Since kernel v6.13, overlayfs supports specifying layers via file descriptors in
|
||||||
|
addition to specifying them as paths. This feature is available for the
|
||||||
|
"datadir+", "lowerdir+", "upperdir", and "workdir+" mount options with the
|
||||||
|
fsconfig syscall from the new mount api::
|
||||||
|
|
||||||
|
fsconfig(fs_fd, FSCONFIG_SET_FD, "lowerdir+", NULL, fd_lower1);
|
||||||
|
fsconfig(fs_fd, FSCONFIG_SET_FD, "lowerdir+", NULL, fd_lower2);
|
||||||
|
fsconfig(fs_fd, FSCONFIG_SET_FD, "lowerdir+", NULL, fd_lower3);
|
||||||
|
fsconfig(fs_fd, FSCONFIG_SET_FD, "datadir+", NULL, fd_data1);
|
||||||
|
fsconfig(fs_fd, FSCONFIG_SET_FD, "datadir+", NULL, fd_data2);
|
||||||
|
fsconfig(fs_fd, FSCONFIG_SET_FD, "workdir", NULL, fd_work);
|
||||||
|
fsconfig(fs_fd, FSCONFIG_SET_FD, "upperdir", NULL, fd_upper);
|
||||||
|
|
||||||
|
|
||||||
fs-verity support
|
fs-verity support
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
@ -309,6 +309,26 @@ int fs_param_is_fd(struct p_log *log, const struct fs_parameter_spec *p,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(fs_param_is_fd);
|
EXPORT_SYMBOL(fs_param_is_fd);
|
||||||
|
|
||||||
|
int fs_param_is_file_or_string(struct p_log *log,
|
||||||
|
const struct fs_parameter_spec *p,
|
||||||
|
struct fs_parameter *param,
|
||||||
|
struct fs_parse_result *result)
|
||||||
|
{
|
||||||
|
switch (param->type) {
|
||||||
|
case fs_value_is_string:
|
||||||
|
return fs_param_is_string(log, p, param, result);
|
||||||
|
case fs_value_is_file:
|
||||||
|
result->uint_32 = param->dirfd;
|
||||||
|
if (result->uint_32 <= INT_MAX)
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return fs_param_bad_value(log, param);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(fs_param_is_file_or_string);
|
||||||
|
|
||||||
int fs_param_is_uid(struct p_log *log, const struct fs_parameter_spec *p,
|
int fs_param_is_uid(struct p_log *log, const struct fs_parameter_spec *p,
|
||||||
struct fs_parameter *param, struct fs_parse_result *result)
|
struct fs_parameter *param, struct fs_parse_result *result)
|
||||||
{
|
{
|
||||||
|
@ -141,10 +141,10 @@ static int ovl_verity_mode_def(void)
|
|||||||
|
|
||||||
const struct fs_parameter_spec ovl_parameter_spec[] = {
|
const struct fs_parameter_spec ovl_parameter_spec[] = {
|
||||||
fsparam_string_empty("lowerdir", Opt_lowerdir),
|
fsparam_string_empty("lowerdir", Opt_lowerdir),
|
||||||
fsparam_string("lowerdir+", Opt_lowerdir_add),
|
fsparam_file_or_string("lowerdir+", Opt_lowerdir_add),
|
||||||
fsparam_string("datadir+", Opt_datadir_add),
|
fsparam_file_or_string("datadir+", Opt_datadir_add),
|
||||||
fsparam_string("upperdir", Opt_upperdir),
|
fsparam_file_or_string("upperdir", Opt_upperdir),
|
||||||
fsparam_string("workdir", Opt_workdir),
|
fsparam_file_or_string("workdir", Opt_workdir),
|
||||||
fsparam_flag("default_permissions", Opt_default_permissions),
|
fsparam_flag("default_permissions", Opt_default_permissions),
|
||||||
fsparam_enum("redirect_dir", Opt_redirect_dir, ovl_parameter_redirect_dir),
|
fsparam_enum("redirect_dir", Opt_redirect_dir, ovl_parameter_redirect_dir),
|
||||||
fsparam_enum("index", Opt_index, ovl_parameter_bool),
|
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,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ovl_parse_layer(struct fs_context *fc, const char *layer_name, enum ovl_opt layer)
|
static inline bool is_upper_layer(enum ovl_opt layer)
|
||||||
|
{
|
||||||
|
return layer == Opt_upperdir || layer == Opt_workdir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle non-file descriptor-based layer options that require path lookup. */
|
||||||
|
static inline int ovl_kern_path(const char *layer_name, struct path *layer_path,
|
||||||
|
enum ovl_opt layer)
|
||||||
{
|
{
|
||||||
char *name = kstrdup(layer_name, GFP_KERNEL);
|
|
||||||
bool upper = (layer == Opt_upperdir || layer == Opt_workdir);
|
|
||||||
struct path path;
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
switch (layer) {
|
||||||
|
case Opt_upperdir:
|
||||||
|
fallthrough;
|
||||||
|
case Opt_workdir:
|
||||||
|
fallthrough;
|
||||||
|
case Opt_lowerdir:
|
||||||
|
err = ovl_mount_dir(layer_name, layer_path);
|
||||||
|
break;
|
||||||
|
case Opt_lowerdir_add:
|
||||||
|
fallthrough;
|
||||||
|
case Opt_datadir_add:
|
||||||
|
err = ovl_mount_dir_noesc(layer_name, layer_path);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
WARN_ON_ONCE(true);
|
||||||
|
err = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ovl_do_parse_layer(struct fs_context *fc, const char *layer_name,
|
||||||
|
struct path *layer_path, enum ovl_opt layer)
|
||||||
|
{
|
||||||
|
char *name __free(kfree) = kstrdup(layer_name, GFP_KERNEL);
|
||||||
|
bool upper;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
if (!name)
|
if (!name)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (upper || layer == Opt_lowerdir)
|
upper = is_upper_layer(layer);
|
||||||
err = ovl_mount_dir(name, &path);
|
err = ovl_mount_dir_check(fc, layer_path, layer, name, upper);
|
||||||
else
|
|
||||||
err = ovl_mount_dir_noesc(name, &path);
|
|
||||||
if (err)
|
if (err)
|
||||||
goto out_free;
|
return err;
|
||||||
|
|
||||||
err = ovl_mount_dir_check(fc, &path, layer, name, upper);
|
|
||||||
if (err)
|
|
||||||
goto out_put;
|
|
||||||
|
|
||||||
if (!upper) {
|
if (!upper) {
|
||||||
err = ovl_ctx_realloc_lower(fc);
|
err = ovl_ctx_realloc_lower(fc);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_put;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Store the user provided path string in ctx to show in mountinfo */
|
/* Store the user provided path string in ctx to show in mountinfo */
|
||||||
ovl_add_layer(fc, layer, &path, &name);
|
ovl_add_layer(fc, layer, layer_path, &name);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ovl_parse_layer(struct fs_context *fc, struct fs_parameter *param,
|
||||||
|
enum ovl_opt layer)
|
||||||
|
{
|
||||||
|
struct path layer_path __free(path_put) = {};
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
switch (param->type) {
|
||||||
|
case fs_value_is_string:
|
||||||
|
err = ovl_kern_path(param->string, &layer_path, layer);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
err = ovl_do_parse_layer(fc, param->string, &layer_path, layer);
|
||||||
|
break;
|
||||||
|
case fs_value_is_file: {
|
||||||
|
char *buf __free(kfree);
|
||||||
|
char *layer_name;
|
||||||
|
|
||||||
|
buf = kmalloc(PATH_MAX, GFP_KERNEL_ACCOUNT);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
layer_path = param->file->f_path;
|
||||||
|
path_get(&layer_path);
|
||||||
|
|
||||||
|
layer_name = d_path(&layer_path, buf, PATH_MAX);
|
||||||
|
if (IS_ERR(layer_name))
|
||||||
|
return PTR_ERR(layer_name);
|
||||||
|
|
||||||
|
err = ovl_do_parse_layer(fc, layer_name, &layer_path, layer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
WARN_ON_ONCE(true);
|
||||||
|
err = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
out_put:
|
|
||||||
path_put(&path);
|
|
||||||
out_free:
|
|
||||||
kfree(name);
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -474,7 +534,13 @@ static int ovl_parse_param_lowerdir(const char *name, struct fs_context *fc)
|
|||||||
|
|
||||||
iter = dup;
|
iter = dup;
|
||||||
for (nr = 0; nr < nr_lower; nr++) {
|
for (nr = 0; nr < nr_lower; nr++) {
|
||||||
err = ovl_parse_layer(fc, iter, Opt_lowerdir);
|
struct path path __free(path_put) = {};
|
||||||
|
|
||||||
|
err = ovl_kern_path(iter, &path, Opt_lowerdir);
|
||||||
|
if (err)
|
||||||
|
goto out_err;
|
||||||
|
|
||||||
|
err = ovl_do_parse_layer(fc, iter, &path, Opt_lowerdir);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
|
|
||||||
@ -555,7 +621,7 @@ static int ovl_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
|||||||
case Opt_datadir_add:
|
case Opt_datadir_add:
|
||||||
case Opt_upperdir:
|
case Opt_upperdir:
|
||||||
case Opt_workdir:
|
case Opt_workdir:
|
||||||
err = ovl_parse_layer(fc, param->string, opt);
|
err = ovl_parse_layer(fc, param, opt);
|
||||||
break;
|
break;
|
||||||
case Opt_default_permissions:
|
case Opt_default_permissions:
|
||||||
config->default_permissions = true;
|
config->default_permissions = true;
|
||||||
|
@ -28,7 +28,8 @@ typedef int fs_param_type(struct p_log *,
|
|||||||
*/
|
*/
|
||||||
fs_param_type fs_param_is_bool, fs_param_is_u32, fs_param_is_s32, fs_param_is_u64,
|
fs_param_type fs_param_is_bool, fs_param_is_u32, fs_param_is_s32, fs_param_is_u64,
|
||||||
fs_param_is_enum, fs_param_is_string, fs_param_is_blob, fs_param_is_blockdev,
|
fs_param_is_enum, fs_param_is_string, fs_param_is_blob, fs_param_is_blockdev,
|
||||||
fs_param_is_path, fs_param_is_fd, fs_param_is_uid, fs_param_is_gid;
|
fs_param_is_path, fs_param_is_fd, fs_param_is_uid, fs_param_is_gid,
|
||||||
|
fs_param_is_file_or_string;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Specification of the type of value a parameter wants.
|
* Specification of the type of value a parameter wants.
|
||||||
@ -133,6 +134,8 @@ static inline bool fs_validate_description(const char *name,
|
|||||||
#define fsparam_bdev(NAME, OPT) __fsparam(fs_param_is_blockdev, NAME, OPT, 0, NULL)
|
#define fsparam_bdev(NAME, OPT) __fsparam(fs_param_is_blockdev, NAME, OPT, 0, NULL)
|
||||||
#define fsparam_path(NAME, OPT) __fsparam(fs_param_is_path, NAME, OPT, 0, NULL)
|
#define fsparam_path(NAME, OPT) __fsparam(fs_param_is_path, NAME, OPT, 0, NULL)
|
||||||
#define fsparam_fd(NAME, OPT) __fsparam(fs_param_is_fd, NAME, OPT, 0, NULL)
|
#define fsparam_fd(NAME, OPT) __fsparam(fs_param_is_fd, NAME, OPT, 0, NULL)
|
||||||
|
#define fsparam_file_or_string(NAME, OPT) \
|
||||||
|
__fsparam(fs_param_is_file_or_string, NAME, OPT, 0, NULL)
|
||||||
#define fsparam_uid(NAME, OPT) __fsparam(fs_param_is_uid, NAME, OPT, 0, NULL)
|
#define fsparam_uid(NAME, OPT) __fsparam(fs_param_is_uid, NAME, OPT, 0, NULL)
|
||||||
#define fsparam_gid(NAME, OPT) __fsparam(fs_param_is_gid, NAME, OPT, 0, NULL)
|
#define fsparam_gid(NAME, OPT) __fsparam(fs_param_is_gid, NAME, OPT, 0, NULL)
|
||||||
|
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0-only
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
dev_in_maps
|
dev_in_maps
|
||||||
|
set_layers_via_fds
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
TEST_GEN_PROGS := dev_in_maps
|
TEST_GEN_PROGS := dev_in_maps set_layers_via_fds
|
||||||
|
|
||||||
CFLAGS := -Wall -Werror
|
CFLAGS := -Wall -Werror
|
||||||
|
|
||||||
|
@ -17,32 +17,7 @@
|
|||||||
|
|
||||||
#include "../../kselftest.h"
|
#include "../../kselftest.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "wrappers.h"
|
||||||
static int sys_fsopen(const char *fsname, unsigned int flags)
|
|
||||||
{
|
|
||||||
return syscall(__NR_fsopen, fsname, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sys_fsconfig(int fd, unsigned int cmd, const char *key, const char *value, int aux)
|
|
||||||
{
|
|
||||||
return syscall(__NR_fsconfig, fd, cmd, key, value, aux);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sys_fsmount(int fd, unsigned int flags, unsigned int attr_flags)
|
|
||||||
{
|
|
||||||
return syscall(__NR_fsmount, fd, flags, attr_flags);
|
|
||||||
}
|
|
||||||
static int sys_mount(const char *src, const char *tgt, const char *fst,
|
|
||||||
unsigned long flags, const void *data)
|
|
||||||
{
|
|
||||||
return syscall(__NR_mount, src, tgt, fst, flags, data);
|
|
||||||
}
|
|
||||||
static int sys_move_mount(int from_dfd, const char *from_pathname,
|
|
||||||
int to_dfd, const char *to_pathname,
|
|
||||||
unsigned int flags)
|
|
||||||
{
|
|
||||||
return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd, to_pathname, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
static long get_file_dev_and_inode(void *addr, struct statx *stx)
|
static long get_file_dev_and_inode(void *addr, struct statx *stx)
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,217 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#define __SANE_USERSPACE_TYPES__ // Use ll64
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "../../kselftest_harness.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "wrappers.h"
|
||||||
|
|
||||||
|
FIXTURE(set_layers_via_fds) {
|
||||||
|
};
|
||||||
|
|
||||||
|
FIXTURE_SETUP(set_layers_via_fds)
|
||||||
|
{
|
||||||
|
ASSERT_EQ(mkdir("/set_layers_via_fds", 0755), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
FIXTURE_TEARDOWN(set_layers_via_fds)
|
||||||
|
{
|
||||||
|
umount2("/set_layers_via_fds", 0);
|
||||||
|
ASSERT_EQ(rmdir("/set_layers_via_fds"), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(set_layers_via_fds, set_layers_via_fds)
|
||||||
|
{
|
||||||
|
int fd_context, fd_tmpfs, fd_overlay;
|
||||||
|
int layer_fds[] = { [0 ... 8] = -EBADF };
|
||||||
|
bool layers_found[] = { [0 ... 8] = false };
|
||||||
|
size_t len = 0;
|
||||||
|
char *line = NULL;
|
||||||
|
FILE *f_mountinfo;
|
||||||
|
|
||||||
|
ASSERT_EQ(unshare(CLONE_NEWNS), 0);
|
||||||
|
ASSERT_EQ(sys_mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL), 0);
|
||||||
|
|
||||||
|
fd_context = sys_fsopen("tmpfs", 0);
|
||||||
|
ASSERT_GE(fd_context, 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_CMD_CREATE, NULL, NULL, 0), 0);
|
||||||
|
fd_tmpfs = sys_fsmount(fd_context, 0, 0);
|
||||||
|
ASSERT_GE(fd_tmpfs, 0);
|
||||||
|
ASSERT_EQ(close(fd_context), 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(mkdirat(fd_tmpfs, "w", 0755), 0);
|
||||||
|
ASSERT_EQ(mkdirat(fd_tmpfs, "u", 0755), 0);
|
||||||
|
ASSERT_EQ(mkdirat(fd_tmpfs, "l1", 0755), 0);
|
||||||
|
ASSERT_EQ(mkdirat(fd_tmpfs, "l2", 0755), 0);
|
||||||
|
ASSERT_EQ(mkdirat(fd_tmpfs, "l3", 0755), 0);
|
||||||
|
ASSERT_EQ(mkdirat(fd_tmpfs, "l4", 0755), 0);
|
||||||
|
ASSERT_EQ(mkdirat(fd_tmpfs, "d1", 0755), 0);
|
||||||
|
ASSERT_EQ(mkdirat(fd_tmpfs, "d2", 0755), 0);
|
||||||
|
ASSERT_EQ(mkdirat(fd_tmpfs, "d3", 0755), 0);
|
||||||
|
|
||||||
|
layer_fds[0] = openat(fd_tmpfs, "w", O_DIRECTORY);
|
||||||
|
ASSERT_GE(layer_fds[0], 0);
|
||||||
|
|
||||||
|
layer_fds[1] = openat(fd_tmpfs, "u", O_DIRECTORY);
|
||||||
|
ASSERT_GE(layer_fds[1], 0);
|
||||||
|
|
||||||
|
layer_fds[2] = openat(fd_tmpfs, "l1", O_DIRECTORY);
|
||||||
|
ASSERT_GE(layer_fds[2], 0);
|
||||||
|
|
||||||
|
layer_fds[3] = openat(fd_tmpfs, "l2", O_DIRECTORY);
|
||||||
|
ASSERT_GE(layer_fds[3], 0);
|
||||||
|
|
||||||
|
layer_fds[4] = openat(fd_tmpfs, "l3", O_DIRECTORY);
|
||||||
|
ASSERT_GE(layer_fds[4], 0);
|
||||||
|
|
||||||
|
layer_fds[5] = openat(fd_tmpfs, "l4", O_DIRECTORY);
|
||||||
|
ASSERT_GE(layer_fds[5], 0);
|
||||||
|
|
||||||
|
layer_fds[6] = openat(fd_tmpfs, "d1", O_DIRECTORY);
|
||||||
|
ASSERT_GE(layer_fds[6], 0);
|
||||||
|
|
||||||
|
layer_fds[7] = openat(fd_tmpfs, "d2", O_DIRECTORY);
|
||||||
|
ASSERT_GE(layer_fds[7], 0);
|
||||||
|
|
||||||
|
layer_fds[8] = openat(fd_tmpfs, "d3", O_DIRECTORY);
|
||||||
|
ASSERT_GE(layer_fds[8], 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(sys_move_mount(fd_tmpfs, "", -EBADF, "/tmp", MOVE_MOUNT_F_EMPTY_PATH), 0);
|
||||||
|
ASSERT_EQ(close(fd_tmpfs), 0);
|
||||||
|
|
||||||
|
fd_context = sys_fsopen("overlay", 0);
|
||||||
|
ASSERT_GE(fd_context, 0);
|
||||||
|
|
||||||
|
ASSERT_NE(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "lowerdir", NULL, layer_fds[2]), 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "workdir", NULL, layer_fds[0]), 0);
|
||||||
|
ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "upperdir", NULL, layer_fds[1]), 0);
|
||||||
|
ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "lowerdir+", NULL, layer_fds[2]), 0);
|
||||||
|
ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "lowerdir+", NULL, layer_fds[3]), 0);
|
||||||
|
ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "lowerdir+", NULL, layer_fds[4]), 0);
|
||||||
|
ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "lowerdir+", NULL, layer_fds[5]), 0);
|
||||||
|
ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "datadir+", NULL, layer_fds[6]), 0);
|
||||||
|
ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "datadir+", NULL, layer_fds[7]), 0);
|
||||||
|
ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "datadir+", NULL, layer_fds[8]), 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_STRING, "metacopy", "on", 0), 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_CMD_CREATE, NULL, NULL, 0), 0);
|
||||||
|
|
||||||
|
fd_overlay = sys_fsmount(fd_context, 0, 0);
|
||||||
|
ASSERT_GE(fd_overlay, 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(sys_move_mount(fd_overlay, "", -EBADF, "/set_layers_via_fds", MOVE_MOUNT_F_EMPTY_PATH), 0);
|
||||||
|
|
||||||
|
f_mountinfo = fopen("/proc/self/mountinfo", "r");
|
||||||
|
ASSERT_NE(f_mountinfo, NULL);
|
||||||
|
|
||||||
|
while (getline(&line, &len, f_mountinfo) != -1) {
|
||||||
|
char *haystack = line;
|
||||||
|
|
||||||
|
if (strstr(haystack, "workdir=/tmp/w"))
|
||||||
|
layers_found[0] = true;
|
||||||
|
if (strstr(haystack, "upperdir=/tmp/u"))
|
||||||
|
layers_found[1] = true;
|
||||||
|
if (strstr(haystack, "lowerdir+=/tmp/l1"))
|
||||||
|
layers_found[2] = true;
|
||||||
|
if (strstr(haystack, "lowerdir+=/tmp/l2"))
|
||||||
|
layers_found[3] = true;
|
||||||
|
if (strstr(haystack, "lowerdir+=/tmp/l3"))
|
||||||
|
layers_found[4] = true;
|
||||||
|
if (strstr(haystack, "lowerdir+=/tmp/l4"))
|
||||||
|
layers_found[5] = true;
|
||||||
|
if (strstr(haystack, "datadir+=/tmp/d1"))
|
||||||
|
layers_found[6] = true;
|
||||||
|
if (strstr(haystack, "datadir+=/tmp/d2"))
|
||||||
|
layers_found[7] = true;
|
||||||
|
if (strstr(haystack, "datadir+=/tmp/d3"))
|
||||||
|
layers_found[8] = true;
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
|
||||||
|
for (int i = 0; i < ARRAY_SIZE(layer_fds); i++) {
|
||||||
|
ASSERT_EQ(layers_found[i], true);
|
||||||
|
ASSERT_EQ(close(layer_fds[i]), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(close(fd_context), 0);
|
||||||
|
ASSERT_EQ(close(fd_overlay), 0);
|
||||||
|
ASSERT_EQ(fclose(f_mountinfo), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(set_layers_via_fds, set_500_layers_via_fds)
|
||||||
|
{
|
||||||
|
int fd_context, fd_tmpfs, fd_overlay, fd_work, fd_upper, fd_lower;
|
||||||
|
int layer_fds[500] = { [0 ... 499] = -EBADF };
|
||||||
|
|
||||||
|
ASSERT_EQ(unshare(CLONE_NEWNS), 0);
|
||||||
|
ASSERT_EQ(sys_mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL), 0);
|
||||||
|
|
||||||
|
fd_context = sys_fsopen("tmpfs", 0);
|
||||||
|
ASSERT_GE(fd_context, 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_CMD_CREATE, NULL, NULL, 0), 0);
|
||||||
|
fd_tmpfs = sys_fsmount(fd_context, 0, 0);
|
||||||
|
ASSERT_GE(fd_tmpfs, 0);
|
||||||
|
ASSERT_EQ(close(fd_context), 0);
|
||||||
|
|
||||||
|
for (int i = 0; i < ARRAY_SIZE(layer_fds); i++) {
|
||||||
|
char path[100];
|
||||||
|
|
||||||
|
sprintf(path, "l%d", i);
|
||||||
|
ASSERT_EQ(mkdirat(fd_tmpfs, path, 0755), 0);
|
||||||
|
layer_fds[i] = openat(fd_tmpfs, path, O_DIRECTORY);
|
||||||
|
ASSERT_GE(layer_fds[i], 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(mkdirat(fd_tmpfs, "w", 0755), 0);
|
||||||
|
fd_work = openat(fd_tmpfs, "w", O_DIRECTORY);
|
||||||
|
ASSERT_GE(fd_work, 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(mkdirat(fd_tmpfs, "u", 0755), 0);
|
||||||
|
fd_upper = openat(fd_tmpfs, "u", O_DIRECTORY);
|
||||||
|
ASSERT_GE(fd_upper, 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(mkdirat(fd_tmpfs, "l501", 0755), 0);
|
||||||
|
fd_lower = openat(fd_tmpfs, "l501", O_DIRECTORY);
|
||||||
|
ASSERT_GE(fd_lower, 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(sys_move_mount(fd_tmpfs, "", -EBADF, "/tmp", MOVE_MOUNT_F_EMPTY_PATH), 0);
|
||||||
|
ASSERT_EQ(close(fd_tmpfs), 0);
|
||||||
|
|
||||||
|
fd_context = sys_fsopen("overlay", 0);
|
||||||
|
ASSERT_GE(fd_context, 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "workdir", NULL, fd_work), 0);
|
||||||
|
ASSERT_EQ(close(fd_work), 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "upperdir", NULL, fd_upper), 0);
|
||||||
|
ASSERT_EQ(close(fd_upper), 0);
|
||||||
|
|
||||||
|
for (int i = 0; i < ARRAY_SIZE(layer_fds); i++) {
|
||||||
|
ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "lowerdir+", NULL, layer_fds[i]), 0);
|
||||||
|
ASSERT_EQ(close(layer_fds[i]), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_NE(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "lowerdir+", NULL, fd_lower), 0);
|
||||||
|
ASSERT_EQ(close(fd_lower), 0);
|
||||||
|
|
||||||
|
ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_CMD_CREATE, NULL, NULL, 0), 0);
|
||||||
|
|
||||||
|
fd_overlay = sys_fsmount(fd_context, 0, 0);
|
||||||
|
ASSERT_GE(fd_overlay, 0);
|
||||||
|
ASSERT_EQ(close(fd_context), 0);
|
||||||
|
ASSERT_EQ(close(fd_overlay), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_HARNESS_MAIN
|
47
tools/testing/selftests/filesystems/overlayfs/wrappers.h
Normal file
47
tools/testing/selftests/filesystems/overlayfs/wrappers.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
//
|
||||||
|
#ifndef __SELFTEST_OVERLAYFS_WRAPPERS_H__
|
||||||
|
#define __SELFTEST_OVERLAYFS_WRAPPERS_H__
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/mount.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
|
||||||
|
static inline int sys_fsopen(const char *fsname, unsigned int flags)
|
||||||
|
{
|
||||||
|
return syscall(__NR_fsopen, fsname, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int sys_fsconfig(int fd, unsigned int cmd, const char *key,
|
||||||
|
const char *value, int aux)
|
||||||
|
{
|
||||||
|
return syscall(__NR_fsconfig, fd, cmd, key, value, aux);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int sys_fsmount(int fd, unsigned int flags,
|
||||||
|
unsigned int attr_flags)
|
||||||
|
{
|
||||||
|
return syscall(__NR_fsmount, fd, flags, attr_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int sys_mount(const char *src, const char *tgt, const char *fst,
|
||||||
|
unsigned long flags, const void *data)
|
||||||
|
{
|
||||||
|
return syscall(__NR_mount, src, tgt, fst, flags, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef MOVE_MOUNT_F_EMPTY_PATH
|
||||||
|
#define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline int sys_move_mount(int from_dfd, const char *from_pathname,
|
||||||
|
int to_dfd, const char *to_pathname,
|
||||||
|
unsigned int flags)
|
||||||
|
{
|
||||||
|
return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd,
|
||||||
|
to_pathname, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user