| Author | Tokens | Token Proportion | Commits | Commit Proportion |
|---|---|---|---|---|
| Christian Brauner | 5033 | 99.53% | 5 | 62.50% |
| Miklos Szeredi | 17 | 0.34% | 1 | 12.50% |
| Josef Whiter | 5 | 0.10% | 1 | 12.50% |
| Amir Goldstein | 2 | 0.04% | 1 | 12.50% |
| Total | 5057 | 8 |
// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2026 Christian Brauner <brauner@kernel.org> * * Test for FSMOUNT_NAMESPACE flag. * * Test that fsmount() with FSMOUNT_NAMESPACE creates a new mount * namespace containing the specified mount. */ #define _GNU_SOURCE #include <errno.h> #include <fcntl.h> #include <limits.h> #include <linux/nsfs.h> #include <sched.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> #include <sys/mount.h> #include <sys/stat.h> #include <sys/wait.h> #include <unistd.h> #include "../wrappers.h" #include "../statmount/statmount.h" #include "../utils.h" #include "../../kselftest_harness.h" #ifndef FSMOUNT_NAMESPACE #define FSMOUNT_NAMESPACE 0x00000002 #endif #ifndef FSMOUNT_CLOEXEC #define FSMOUNT_CLOEXEC 0x00000001 #endif #ifndef FSCONFIG_CMD_CREATE #define FSCONFIG_CMD_CREATE 6 #endif static int get_mnt_ns_id(int fd, uint64_t *mnt_ns_id) { if (ioctl(fd, NS_GET_MNTNS_ID, mnt_ns_id) < 0) return -errno; return 0; } static int get_mnt_ns_id_from_path(const char *path, uint64_t *mnt_ns_id) { int fd, ret; fd = open(path, O_RDONLY); if (fd < 0) return -errno; ret = get_mnt_ns_id(fd, mnt_ns_id); close(fd); return ret; } static void log_mount(struct __test_metadata *_metadata, struct statmount *sm) { const char *fs_type = ""; const char *mnt_root = ""; const char *mnt_point = ""; if (sm->mask & STATMOUNT_FS_TYPE) fs_type = sm->str + sm->fs_type; if (sm->mask & STATMOUNT_MNT_ROOT) mnt_root = sm->str + sm->mnt_root; if (sm->mask & STATMOUNT_MNT_POINT) mnt_point = sm->str + sm->mnt_point; TH_LOG(" mnt_id: %llu, parent_id: %llu, fs_type: %s, root: %s, point: %s", (unsigned long long)sm->mnt_id, (unsigned long long)sm->mnt_parent_id, fs_type, mnt_root, mnt_point); } static void dump_mounts(struct __test_metadata *_metadata, uint64_t mnt_ns_id) { uint64_t list[256]; ssize_t nr_mounts; nr_mounts = listmount(LSMT_ROOT, mnt_ns_id, 0, list, 256, 0); if (nr_mounts < 0) { TH_LOG("listmount failed: %s", strerror(errno)); return; } TH_LOG("Mount namespace %llu contains %zd mount(s):", (unsigned long long)mnt_ns_id, nr_mounts); for (ssize_t i = 0; i < nr_mounts; i++) { struct statmount *sm; sm = statmount_alloc(list[i], mnt_ns_id, STATMOUNT_MNT_BASIC | STATMOUNT_FS_TYPE | STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT, 0); if (!sm) { TH_LOG(" [%zd] mnt_id %llu: statmount failed: %s", i, (unsigned long long)list[i], strerror(errno)); continue; } log_mount(_metadata, sm); free(sm); } } static int create_tmpfs_fd(void) { int fs_fd, ret; fs_fd = sys_fsopen("tmpfs", FSOPEN_CLOEXEC); if (fs_fd < 0) return -errno; ret = sys_fsconfig(fs_fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0); if (ret < 0) { close(fs_fd); return -errno; } return fs_fd; } FIXTURE(fsmount_ns) { int fd; int fs_fd; uint64_t current_ns_id; }; FIXTURE_VARIANT(fsmount_ns) { const char *fstype; unsigned int flags; bool expect_success; bool expect_different_ns; int min_mounts; }; FIXTURE_VARIANT_ADD(fsmount_ns, basic_tmpfs) { .fstype = "tmpfs", .flags = FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC, .expect_success = true, .expect_different_ns = true, .min_mounts = 1, }; FIXTURE_VARIANT_ADD(fsmount_ns, cloexec_only) { .fstype = "tmpfs", .flags = FSMOUNT_CLOEXEC, .expect_success = true, .expect_different_ns = false, .min_mounts = 1, }; FIXTURE_VARIANT_ADD(fsmount_ns, namespace_only) { .fstype = "tmpfs", .flags = FSMOUNT_NAMESPACE, .expect_success = true, .expect_different_ns = true, .min_mounts = 1, }; FIXTURE_SETUP(fsmount_ns) { int ret; self->fd = -1; self->fs_fd = -1; /* Check if fsopen syscall is supported */ ret = sys_fsopen("tmpfs", 0); if (ret == -1 && errno == ENOSYS) SKIP(return, "fsopen() syscall not supported"); if (ret >= 0) close(ret); /* Check if statmount/listmount are supported */ ret = statmount(0, 0, 0, 0, NULL, 0, 0); if (ret == -1 && errno == ENOSYS) SKIP(return, "statmount() syscall not supported"); /* Get current mount namespace ID for comparison */ ret = get_mnt_ns_id_from_path("/proc/self/ns/mnt", &self->current_ns_id); if (ret < 0) SKIP(return, "Failed to get current mount namespace ID"); } FIXTURE_TEARDOWN(fsmount_ns) { if (self->fd >= 0) close(self->fd); if (self->fs_fd >= 0) close(self->fs_fd); } TEST_F(fsmount_ns, create_namespace) { uint64_t new_ns_id; uint64_t list[256]; ssize_t nr_mounts; int ret; self->fs_fd = create_tmpfs_fd(); ASSERT_GE(self->fs_fd, 0); self->fd = sys_fsmount(self->fs_fd, variant->flags, 0); if (!variant->expect_success) { ASSERT_LT(self->fd, 0); return; } if (self->fd < 0 && errno == EINVAL) SKIP(return, "FSMOUNT_NAMESPACE not supported"); ASSERT_GE(self->fd, 0); if (variant->expect_different_ns) { /* Verify we can get the namespace ID from the fd */ ret = get_mnt_ns_id(self->fd, &new_ns_id); ASSERT_EQ(ret, 0); /* Verify it's a different namespace */ ASSERT_NE(new_ns_id, self->current_ns_id); /* List mounts in the new namespace */ nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0); ASSERT_GE(nr_mounts, 0) { TH_LOG("%m - listmount failed"); } /* Verify minimum expected mounts */ ASSERT_GE(nr_mounts, variant->min_mounts); TH_LOG("Namespace contains %zd mounts", nr_mounts); } } TEST_F(fsmount_ns, setns_into_namespace) { uint64_t new_ns_id; pid_t pid; int status; int ret; /* Only test with FSMOUNT_NAMESPACE flag */ if (!(variant->flags & FSMOUNT_NAMESPACE)) SKIP(return, "setns test only for FSMOUNT_NAMESPACE case"); self->fs_fd = create_tmpfs_fd(); ASSERT_GE(self->fs_fd, 0); self->fd = sys_fsmount(self->fs_fd, variant->flags, 0); if (self->fd < 0 && errno == EINVAL) SKIP(return, "FSMOUNT_NAMESPACE not supported"); ASSERT_GE(self->fd, 0); /* Get namespace ID and dump all mounts */ ret = get_mnt_ns_id(self->fd, &new_ns_id); ASSERT_EQ(ret, 0); dump_mounts(_metadata, new_ns_id); pid = fork(); ASSERT_GE(pid, 0); if (pid == 0) { /* Child: try to enter the namespace */ if (setns(self->fd, CLONE_NEWNS) < 0) _exit(1); _exit(0); } ASSERT_EQ(waitpid(pid, &status, 0), pid); ASSERT_TRUE(WIFEXITED(status)); ASSERT_EQ(WEXITSTATUS(status), 0); } TEST_F(fsmount_ns, verify_mount_properties) { struct statmount sm; uint64_t new_ns_id; uint64_t list[256]; ssize_t nr_mounts; int ret; /* Only test with basic FSMOUNT_NAMESPACE flags */ if (variant->flags != (FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC)) SKIP(return, "mount properties test only for basic case"); self->fs_fd = create_tmpfs_fd(); ASSERT_GE(self->fs_fd, 0); self->fd = sys_fsmount(self->fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC, 0); if (self->fd < 0 && errno == EINVAL) SKIP(return, "FSMOUNT_NAMESPACE not supported"); ASSERT_GE(self->fd, 0); ret = get_mnt_ns_id(self->fd, &new_ns_id); ASSERT_EQ(ret, 0); nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0); ASSERT_GE(nr_mounts, 1); /* Get info about the root mount */ ret = statmount(list[0], new_ns_id, 0, STATMOUNT_MNT_BASIC, &sm, sizeof(sm), 0); ASSERT_EQ(ret, 0); TH_LOG("Root mount id: %llu, parent: %llu", (unsigned long long)sm.mnt_id, (unsigned long long)sm.mnt_parent_id); } TEST_F(fsmount_ns, verify_tmpfs_type) { struct statmount *sm; uint64_t new_ns_id; uint64_t list[256]; ssize_t nr_mounts; const char *fs_type; int ret; /* Only test with basic FSMOUNT_NAMESPACE flags */ if (variant->flags != (FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC)) SKIP(return, "fs type test only for basic case"); self->fs_fd = create_tmpfs_fd(); ASSERT_GE(self->fs_fd, 0); self->fd = sys_fsmount(self->fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC, 0); if (self->fd < 0 && errno == EINVAL) SKIP(return, "FSMOUNT_NAMESPACE not supported"); ASSERT_GE(self->fd, 0); ret = get_mnt_ns_id(self->fd, &new_ns_id); ASSERT_EQ(ret, 0); nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0); ASSERT_GE(nr_mounts, 1); sm = statmount_alloc(list[0], new_ns_id, STATMOUNT_FS_TYPE, 0); ASSERT_NE(sm, NULL); fs_type = sm->str + sm->fs_type; ASSERT_STREQ(fs_type, "tmpfs"); free(sm); } FIXTURE(fsmount_ns_caps) { bool has_caps; }; FIXTURE_SETUP(fsmount_ns_caps) { int ret; /* Check if fsopen syscall is supported */ ret = sys_fsopen("tmpfs", 0); if (ret == -1 && errno == ENOSYS) SKIP(return, "fsopen() syscall not supported"); if (ret >= 0) close(ret); self->has_caps = (geteuid() == 0); } FIXTURE_TEARDOWN(fsmount_ns_caps) { } TEST_F(fsmount_ns_caps, requires_cap_sys_admin) { pid_t pid; int status; int fs_fd; /* * Prepare the configured filesystem fd as root before forking. * fsopen() requires CAP_SYS_ADMIN in the mount namespace's * user_ns, which won't be available after enter_userns(). */ fs_fd = sys_fsopen("tmpfs", FSOPEN_CLOEXEC); ASSERT_GE(fs_fd, 0); ASSERT_EQ(sys_fsconfig(fs_fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0), 0); pid = fork(); ASSERT_GE(pid, 0); if (pid == 0) { int fd; /* Child: drop privileges using utils.h helper */ if (enter_userns() != 0) _exit(2); /* Drop all caps using utils.h helper */ if (caps_down() == 0) _exit(3); fd = sys_fsmount(fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC, 0); close(fs_fd); if (fd >= 0) { close(fd); /* Should have failed without caps */ _exit(1); } if (errno == EPERM) _exit(0); /* EINVAL means FSMOUNT_NAMESPACE not supported */ if (errno == EINVAL) _exit(6); /* Unexpected error */ _exit(7); } close(fs_fd); ASSERT_EQ(waitpid(pid, &status, 0), pid); ASSERT_TRUE(WIFEXITED(status)); switch (WEXITSTATUS(status)) { case 0: /* Expected: EPERM without caps */ break; case 1: ASSERT_FALSE(true) TH_LOG("FSMOUNT_NAMESPACE succeeded without caps"); break; case 2: SKIP(return, "enter_userns failed"); break; case 3: SKIP(return, "caps_down failed"); break; case 6: SKIP(return, "FSMOUNT_NAMESPACE not supported"); break; default: ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)", WEXITSTATUS(status)); break; } } FIXTURE(fsmount_ns_userns) { int fd; int fs_fd; }; FIXTURE_SETUP(fsmount_ns_userns) { int ret; self->fd = -1; self->fs_fd = -1; /* Check if fsopen syscall is supported */ ret = sys_fsopen("tmpfs", 0); if (ret == -1 && errno == ENOSYS) SKIP(return, "fsopen() syscall not supported"); if (ret >= 0) close(ret); /* Check if statmount/listmount are supported */ ret = statmount(0, 0, 0, 0, NULL, 0, 0); if (ret == -1 && errno == ENOSYS) SKIP(return, "statmount() syscall not supported"); } FIXTURE_TEARDOWN(fsmount_ns_userns) { if (self->fd >= 0) close(self->fd); if (self->fs_fd >= 0) close(self->fs_fd); } TEST_F(fsmount_ns_userns, create_in_userns) { pid_t pid; int status; pid = fork(); ASSERT_GE(pid, 0); if (pid == 0) { uint64_t new_ns_id; uint64_t list[256]; ssize_t nr_mounts; int fs_fd, fd; /* Create new user namespace (also creates mount namespace) */ if (setup_userns() != 0) _exit(2); /* Now we have CAP_SYS_ADMIN in the user namespace */ fs_fd = sys_fsopen("tmpfs", FSOPEN_CLOEXEC); if (fs_fd < 0) _exit(3); if (sys_fsconfig(fs_fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0) { close(fs_fd); _exit(4); } fd = sys_fsmount(fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC, 0); close(fs_fd); if (fd < 0) { if (errno == EINVAL) _exit(6); /* FSMOUNT_NAMESPACE not supported */ _exit(1); } /* Verify we can get the namespace ID */ if (get_mnt_ns_id(fd, &new_ns_id) != 0) _exit(7); /* Verify we can list mounts in the new namespace */ nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0); if (nr_mounts < 0) _exit(8); /* Should have at least 1 mount (the tmpfs) */ if (nr_mounts < 1) _exit(9); close(fd); _exit(0); } ASSERT_EQ(waitpid(pid, &status, 0), pid); ASSERT_TRUE(WIFEXITED(status)); switch (WEXITSTATUS(status)) { case 0: /* Success */ break; case 1: ASSERT_FALSE(true) TH_LOG("fsmount(FSMOUNT_NAMESPACE) failed in userns"); break; case 2: SKIP(return, "setup_userns failed"); break; case 3: SKIP(return, "fsopen failed in userns"); break; case 4: SKIP(return, "fsconfig CMD_CREATE failed in userns"); break; case 6: SKIP(return, "FSMOUNT_NAMESPACE not supported"); break; case 7: ASSERT_FALSE(true) TH_LOG("Failed to get mount namespace ID"); break; case 8: ASSERT_FALSE(true) TH_LOG("listmount failed in new namespace"); break; case 9: ASSERT_FALSE(true) TH_LOG("New namespace has no mounts"); break; default: ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)", WEXITSTATUS(status)); break; } } TEST_F(fsmount_ns_userns, setns_in_userns) { pid_t pid; int status; pid = fork(); ASSERT_GE(pid, 0); if (pid == 0) { uint64_t new_ns_id; int fs_fd, fd; pid_t inner_pid; int inner_status; /* Create new user namespace */ if (setup_userns() != 0) _exit(2); fs_fd = sys_fsopen("tmpfs", FSOPEN_CLOEXEC); if (fs_fd < 0) _exit(3); if (sys_fsconfig(fs_fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0) { close(fs_fd); _exit(4); } fd = sys_fsmount(fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC, 0); close(fs_fd); if (fd < 0) { if (errno == EINVAL) _exit(6); _exit(1); } if (get_mnt_ns_id(fd, &new_ns_id) != 0) _exit(7); /* Fork again to test setns into the new namespace */ inner_pid = fork(); if (inner_pid < 0) _exit(10); if (inner_pid == 0) { /* Inner child: enter the new namespace */ if (setns(fd, CLONE_NEWNS) < 0) _exit(1); _exit(0); } if (waitpid(inner_pid, &inner_status, 0) != inner_pid) _exit(11); if (!WIFEXITED(inner_status) || WEXITSTATUS(inner_status) != 0) _exit(12); close(fd); _exit(0); } ASSERT_EQ(waitpid(pid, &status, 0), pid); ASSERT_TRUE(WIFEXITED(status)); switch (WEXITSTATUS(status)) { case 0: /* Success */ break; case 1: ASSERT_FALSE(true) TH_LOG("fsmount or setns failed in userns"); break; case 2: SKIP(return, "setup_userns failed"); break; case 3: SKIP(return, "fsopen failed in userns"); break; case 4: SKIP(return, "fsconfig CMD_CREATE failed in userns"); break; case 6: SKIP(return, "FSMOUNT_NAMESPACE not supported"); break; case 7: ASSERT_FALSE(true) TH_LOG("Failed to get mount namespace ID"); break; case 10: ASSERT_FALSE(true) TH_LOG("Inner fork failed"); break; case 11: ASSERT_FALSE(true) TH_LOG("Inner waitpid failed"); break; case 12: ASSERT_FALSE(true) TH_LOG("setns into new namespace failed"); break; default: ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)", WEXITSTATUS(status)); break; } } TEST_F(fsmount_ns_userns, umount_fails_einval) { pid_t pid; int status; pid = fork(); ASSERT_GE(pid, 0); if (pid == 0) { uint64_t new_ns_id; uint64_t list[256]; ssize_t nr_mounts; int fs_fd, fd; ssize_t i; /* Create new user namespace */ if (setup_userns() != 0) _exit(2); fs_fd = sys_fsopen("tmpfs", FSOPEN_CLOEXEC); if (fs_fd < 0) _exit(3); if (sys_fsconfig(fs_fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0) { close(fs_fd); _exit(4); } fd = sys_fsmount(fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC, 0); close(fs_fd); if (fd < 0) { if (errno == EINVAL) _exit(6); _exit(1); } if (get_mnt_ns_id(fd, &new_ns_id) != 0) _exit(7); /* Get all mounts in the new namespace */ nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, LISTMOUNT_REVERSE); if (nr_mounts < 0) _exit(13); if (nr_mounts < 1) _exit(14); /* Enter the new namespace */ if (setns(fd, CLONE_NEWNS) < 0) _exit(8); for (i = 0; i < nr_mounts; i++) { struct statmount *sm; const char *mnt_point; sm = statmount_alloc(list[i], new_ns_id, STATMOUNT_MNT_POINT, 0); if (!sm) _exit(15); mnt_point = sm->str + sm->mnt_point; if (umount2(mnt_point, MNT_DETACH) == 0) { free(sm); _exit(9); } if (errno != EINVAL) { /* Wrong error */ free(sm); _exit(10); } free(sm); } close(fd); _exit(0); } ASSERT_EQ(waitpid(pid, &status, 0), pid); ASSERT_TRUE(WIFEXITED(status)); switch (WEXITSTATUS(status)) { case 0: break; case 1: ASSERT_FALSE(true) TH_LOG("fsmount(FSMOUNT_NAMESPACE) failed"); break; case 2: SKIP(return, "setup_userns failed"); break; case 3: SKIP(return, "fsopen failed in userns"); break; case 4: SKIP(return, "fsconfig CMD_CREATE failed in userns"); break; case 6: SKIP(return, "FSMOUNT_NAMESPACE not supported"); break; case 7: ASSERT_FALSE(true) TH_LOG("Failed to get mount namespace ID"); break; case 8: ASSERT_FALSE(true) TH_LOG("setns into new namespace failed"); break; case 9: ASSERT_FALSE(true) TH_LOG("umount succeeded but should have failed with EINVAL"); break; case 10: ASSERT_FALSE(true) TH_LOG("umount failed with wrong error (expected EINVAL)"); break; case 13: ASSERT_FALSE(true) TH_LOG("listmount failed"); break; case 14: ASSERT_FALSE(true) TH_LOG("No mounts in new namespace"); break; case 15: ASSERT_FALSE(true) TH_LOG("statmount_alloc failed"); break; default: ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)", WEXITSTATUS(status)); break; } } TEST_F(fsmount_ns_userns, umount_succeeds) { pid_t pid; int status; pid = fork(); ASSERT_GE(pid, 0); if (pid == 0) { uint64_t new_ns_id; uint64_t list[256]; ssize_t nr_mounts; int fs_fd, fd; ssize_t i; if (unshare(CLONE_NEWNS)) _exit(1); if (sys_mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) != 0) _exit(1); fs_fd = sys_fsopen("tmpfs", FSOPEN_CLOEXEC); if (fs_fd < 0) _exit(3); if (sys_fsconfig(fs_fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0) { close(fs_fd); _exit(4); } fd = sys_fsmount(fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC, 0); close(fs_fd); if (fd < 0) { if (errno == EINVAL) _exit(6); _exit(1); } if (get_mnt_ns_id(fd, &new_ns_id) != 0) _exit(7); /* Get all mounts in the new namespace */ nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, LISTMOUNT_REVERSE); if (nr_mounts < 0) _exit(13); if (nr_mounts < 1) _exit(14); /* Enter the new namespace */ if (setns(fd, CLONE_NEWNS) < 0) _exit(8); for (i = 0; i < nr_mounts; i++) { struct statmount *sm; const char *mnt_point; sm = statmount_alloc(list[i], new_ns_id, STATMOUNT_MNT_POINT, 0); if (!sm) _exit(15); mnt_point = sm->str + sm->mnt_point; if (umount2(mnt_point, MNT_DETACH) != 0) { free(sm); _exit(9); } free(sm); } close(fd); _exit(0); } ASSERT_EQ(waitpid(pid, &status, 0), pid); ASSERT_TRUE(WIFEXITED(status)); switch (WEXITSTATUS(status)) { case 0: break; case 1: ASSERT_FALSE(true) TH_LOG("fsmount(FSMOUNT_NAMESPACE) failed or unshare failed"); break; case 3: SKIP(return, "fsopen failed"); break; case 4: SKIP(return, "fsconfig CMD_CREATE failed"); break; case 6: SKIP(return, "FSMOUNT_NAMESPACE not supported"); break; case 7: ASSERT_FALSE(true) TH_LOG("Failed to get mount namespace ID"); break; case 8: ASSERT_FALSE(true) TH_LOG("setns into new namespace failed"); break; case 9: ASSERT_FALSE(true) TH_LOG("umount failed but should have succeeded"); break; case 13: ASSERT_FALSE(true) TH_LOG("listmount failed"); break; case 14: ASSERT_FALSE(true) TH_LOG("No mounts in new namespace"); break; case 15: ASSERT_FALSE(true) TH_LOG("statmount_alloc failed"); break; default: ASSERT_FALSE(true) TH_LOG("Unexpected error in child (exit %d)", WEXITSTATUS(status)); break; } } FIXTURE(fsmount_ns_mount_attrs) { int fd; int fs_fd; }; FIXTURE_SETUP(fsmount_ns_mount_attrs) { int ret; self->fd = -1; self->fs_fd = -1; /* Check if fsopen syscall is supported */ ret = sys_fsopen("tmpfs", 0); if (ret == -1 && errno == ENOSYS) SKIP(return, "fsopen() syscall not supported"); if (ret >= 0) close(ret); /* Check if statmount/listmount are supported */ ret = statmount(0, 0, 0, 0, NULL, 0, 0); if (ret == -1 && errno == ENOSYS) SKIP(return, "statmount() syscall not supported"); } FIXTURE_TEARDOWN(fsmount_ns_mount_attrs) { if (self->fd >= 0) close(self->fd); if (self->fs_fd >= 0) close(self->fs_fd); } TEST_F(fsmount_ns_mount_attrs, readonly) { struct statmount sm; uint64_t new_ns_id; uint64_t list[256]; ssize_t nr_mounts; int ret; self->fs_fd = create_tmpfs_fd(); ASSERT_GE(self->fs_fd, 0); self->fd = sys_fsmount(self->fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC, MOUNT_ATTR_RDONLY); if (self->fd < 0 && errno == EINVAL) SKIP(return, "FSMOUNT_NAMESPACE not supported"); ASSERT_GE(self->fd, 0); ret = get_mnt_ns_id(self->fd, &new_ns_id); ASSERT_EQ(ret, 0); nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0); ASSERT_GE(nr_mounts, 1); ret = statmount(list[0], new_ns_id, 0, STATMOUNT_MNT_BASIC, &sm, sizeof(sm), 0); ASSERT_EQ(ret, 0); /* Verify the mount is read-only */ ASSERT_TRUE(sm.mnt_attr & MOUNT_ATTR_RDONLY); } TEST_F(fsmount_ns_mount_attrs, noexec) { struct statmount sm; uint64_t new_ns_id; uint64_t list[256]; ssize_t nr_mounts; int ret; self->fs_fd = create_tmpfs_fd(); ASSERT_GE(self->fs_fd, 0); self->fd = sys_fsmount(self->fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC, MOUNT_ATTR_NOEXEC); if (self->fd < 0 && errno == EINVAL) SKIP(return, "FSMOUNT_NAMESPACE not supported"); ASSERT_GE(self->fd, 0); ret = get_mnt_ns_id(self->fd, &new_ns_id); ASSERT_EQ(ret, 0); nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0); ASSERT_GE(nr_mounts, 1); ret = statmount(list[0], new_ns_id, 0, STATMOUNT_MNT_BASIC, &sm, sizeof(sm), 0); ASSERT_EQ(ret, 0); /* Verify the mount is noexec */ ASSERT_TRUE(sm.mnt_attr & MOUNT_ATTR_NOEXEC); } TEST_F(fsmount_ns_mount_attrs, nosuid) { struct statmount sm; uint64_t new_ns_id; uint64_t list[256]; ssize_t nr_mounts; int ret; self->fs_fd = create_tmpfs_fd(); ASSERT_GE(self->fs_fd, 0); self->fd = sys_fsmount(self->fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC, MOUNT_ATTR_NOSUID); if (self->fd < 0 && errno == EINVAL) SKIP(return, "FSMOUNT_NAMESPACE not supported"); ASSERT_GE(self->fd, 0); ret = get_mnt_ns_id(self->fd, &new_ns_id); ASSERT_EQ(ret, 0); nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0); ASSERT_GE(nr_mounts, 1); ret = statmount(list[0], new_ns_id, 0, STATMOUNT_MNT_BASIC, &sm, sizeof(sm), 0); ASSERT_EQ(ret, 0); /* Verify the mount is nosuid */ ASSERT_TRUE(sm.mnt_attr & MOUNT_ATTR_NOSUID); } TEST_F(fsmount_ns_mount_attrs, noatime) { struct statmount sm; uint64_t new_ns_id; uint64_t list[256]; ssize_t nr_mounts; int ret; self->fs_fd = create_tmpfs_fd(); ASSERT_GE(self->fs_fd, 0); self->fd = sys_fsmount(self->fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC, MOUNT_ATTR_NOATIME); if (self->fd < 0 && errno == EINVAL) SKIP(return, "FSMOUNT_NAMESPACE not supported"); ASSERT_GE(self->fd, 0); ret = get_mnt_ns_id(self->fd, &new_ns_id); ASSERT_EQ(ret, 0); nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0); ASSERT_GE(nr_mounts, 1); ret = statmount(list[0], new_ns_id, 0, STATMOUNT_MNT_BASIC, &sm, sizeof(sm), 0); ASSERT_EQ(ret, 0); /* Verify the mount is noatime */ ASSERT_TRUE(sm.mnt_attr & MOUNT_ATTR_NOATIME); } TEST_F(fsmount_ns_mount_attrs, combined) { struct statmount sm; uint64_t new_ns_id; uint64_t list[256]; ssize_t nr_mounts; int ret; self->fs_fd = create_tmpfs_fd(); ASSERT_GE(self->fs_fd, 0); self->fd = sys_fsmount(self->fs_fd, FSMOUNT_NAMESPACE | FSMOUNT_CLOEXEC, MOUNT_ATTR_RDONLY | MOUNT_ATTR_NOEXEC | MOUNT_ATTR_NOSUID | MOUNT_ATTR_NOATIME); if (self->fd < 0 && errno == EINVAL) SKIP(return, "FSMOUNT_NAMESPACE not supported"); ASSERT_GE(self->fd, 0); ret = get_mnt_ns_id(self->fd, &new_ns_id); ASSERT_EQ(ret, 0); nr_mounts = listmount(LSMT_ROOT, new_ns_id, 0, list, 256, 0); ASSERT_GE(nr_mounts, 1); ret = statmount(list[0], new_ns_id, 0, STATMOUNT_MNT_BASIC, &sm, sizeof(sm), 0); ASSERT_EQ(ret, 0); /* Verify all attributes are set */ ASSERT_TRUE(sm.mnt_attr & MOUNT_ATTR_RDONLY); ASSERT_TRUE(sm.mnt_attr & MOUNT_ATTR_NOEXEC); ASSERT_TRUE(sm.mnt_attr & MOUNT_ATTR_NOSUID); ASSERT_TRUE(sm.mnt_attr & MOUNT_ATTR_NOATIME); } TEST_HARNESS_MAIN
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1