Add tests for STATMOUNT_BY_FD flag, which adds support for passing a
file descriptors to statmount(). The fd can also be on a "unmounted"
mount (mount unmounted with MNT_DETACH), we also include tests for that.
Co-developed-by: Andrei Vagin <avagin@gmail.com>
Signed-off-by: Andrei Vagin <avagin@gmail.com>
Signed-off-by: Bhavik Sachdev <b.sachdev1904@gmail.com>
---
.../filesystems/statmount/statmount.h | 15 +-
.../filesystems/statmount/statmount_test.c | 261 +++++++++++++++++-
.../filesystems/statmount/statmount_test_ns.c | 101 ++++++-
3 files changed, 354 insertions(+), 23 deletions(-)
diff --git a/tools/testing/selftests/filesystems/statmount/statmount.h b/tools/testing/selftests/filesystems/statmount/statmount.h
index 99e5ad082fb1..e1cba4bfd8d9 100644
--- a/tools/testing/selftests/filesystems/statmount/statmount.h
+++ b/tools/testing/selftests/filesystems/statmount/statmount.h
@@ -43,19 +43,24 @@
#endif
#endif
-static inline int statmount(uint64_t mnt_id, uint64_t mnt_ns_id, uint64_t mask,
- struct statmount *buf, size_t bufsize,
+static inline int statmount(uint64_t mnt_id, uint64_t mnt_ns_id, uint32_t fd,
+ uint64_t mask, struct statmount *buf, size_t bufsize,
unsigned int flags)
{
struct mnt_id_req req = {
.size = MNT_ID_REQ_SIZE_VER0,
- .mnt_id = mnt_id,
.param = mask,
};
- if (mnt_ns_id) {
+ if (flags & STATMOUNT_BY_FD) {
req.size = MNT_ID_REQ_SIZE_VER1;
- req.mnt_ns_id = mnt_ns_id;
+ req.mnt_fd = fd;
+ } else {
+ req.mnt_id = mnt_id;
+ if (mnt_ns_id) {
+ req.size = MNT_ID_REQ_SIZE_VER1;
+ req.mnt_ns_id = mnt_ns_id;
+ }
}
return syscall(__NR_statmount, &req, buf, bufsize, flags);
diff --git a/tools/testing/selftests/filesystems/statmount/statmount_test.c b/tools/testing/selftests/filesystems/statmount/statmount_test.c
index f048042e53e9..4790a349806e 100644
--- a/tools/testing/selftests/filesystems/statmount/statmount_test.c
+++ b/tools/testing/selftests/filesystems/statmount/statmount_test.c
@@ -33,15 +33,24 @@ static const char *const known_fs[] = {
"sysv", "tmpfs", "tracefs", "ubifs", "udf", "ufs", "v7", "vboxsf",
"vfat", "virtiofs", "vxfs", "xenfs", "xfs", "zonefs", NULL };
-static struct statmount *statmount_alloc(uint64_t mnt_id, uint64_t mask, unsigned int flags)
+static struct statmount *statmount_alloc(uint64_t mnt_id, int fd, uint64_t mask, unsigned int flags)
{
size_t bufsize = 1 << 15;
- struct statmount *buf = NULL, *tmp = alloca(bufsize);
+ struct statmount *buf = NULL, *tmp = NULL;
int tofree = 0;
int ret;
+ if (flags & STATMOUNT_BY_FD && fd < 0)
+ return NULL;
+
+ tmp = alloca(bufsize);
+
for (;;) {
- ret = statmount(mnt_id, 0, mask, tmp, bufsize, flags);
+ if (flags & STATMOUNT_BY_FD)
+ ret = statmount(0, 0, (uint32_t) fd, mask, tmp, bufsize, flags);
+ else
+ ret = statmount(mnt_id, 0, 0, mask, tmp, bufsize, flags);
+
if (ret != -1)
break;
if (tofree)
@@ -237,7 +246,7 @@ static void test_statmount_zero_mask(void)
struct statmount sm;
int ret;
- ret = statmount(root_id, 0, 0, &sm, sizeof(sm), 0);
+ ret = statmount(root_id, 0, 0, 0, &sm, sizeof(sm), 0);
if (ret == -1) {
ksft_test_result_fail("statmount zero mask: %s\n",
strerror(errno));
@@ -263,7 +272,7 @@ static void test_statmount_mnt_basic(void)
int ret;
uint64_t mask = STATMOUNT_MNT_BASIC;
- ret = statmount(root_id, 0, mask, &sm, sizeof(sm), 0);
+ ret = statmount(root_id, 0, 0, mask, &sm, sizeof(sm), 0);
if (ret == -1) {
ksft_test_result_fail("statmount mnt basic: %s\n",
strerror(errno));
@@ -323,7 +332,7 @@ static void test_statmount_sb_basic(void)
struct statx sx;
struct statfs sf;
- ret = statmount(root_id, 0, mask, &sm, sizeof(sm), 0);
+ ret = statmount(root_id, 0, 0, mask, &sm, sizeof(sm), 0);
if (ret == -1) {
ksft_test_result_fail("statmount sb basic: %s\n",
strerror(errno));
@@ -375,7 +384,7 @@ static void test_statmount_mnt_point(void)
{
struct statmount *sm;
- sm = statmount_alloc(root_id, STATMOUNT_MNT_POINT, 0);
+ sm = statmount_alloc(root_id, 0, STATMOUNT_MNT_POINT, 0);
if (!sm) {
ksft_test_result_fail("statmount mount point: %s\n",
strerror(errno));
@@ -405,7 +414,7 @@ static void test_statmount_mnt_root(void)
assert(last_dir);
last_dir++;
- sm = statmount_alloc(root_id, STATMOUNT_MNT_ROOT, 0);
+ sm = statmount_alloc(root_id, 0, STATMOUNT_MNT_ROOT, 0);
if (!sm) {
ksft_test_result_fail("statmount mount root: %s\n",
strerror(errno));
@@ -438,7 +447,7 @@ static void test_statmount_fs_type(void)
const char *fs_type;
const char *const *s;
- sm = statmount_alloc(root_id, STATMOUNT_FS_TYPE, 0);
+ sm = statmount_alloc(root_id, 0, STATMOUNT_FS_TYPE, 0);
if (!sm) {
ksft_test_result_fail("statmount fs type: %s\n",
strerror(errno));
@@ -467,7 +476,7 @@ static void test_statmount_mnt_opts(void)
char *line = NULL;
size_t len = 0;
- sm = statmount_alloc(root_id, STATMOUNT_MNT_BASIC | STATMOUNT_MNT_OPTS,
+ sm = statmount_alloc(root_id, 0, STATMOUNT_MNT_BASIC | STATMOUNT_MNT_OPTS,
0);
if (!sm) {
ksft_test_result_fail("statmount mnt opts: %s\n",
@@ -557,7 +566,7 @@ static void test_statmount_string(uint64_t mask, size_t off, const char *name)
uint32_t start, i;
int ret;
- sm = statmount_alloc(root_id, mask, 0);
+ sm = statmount_alloc(root_id, 0, mask, 0);
if (!sm) {
ksft_test_result_fail("statmount %s: %s\n", name,
strerror(errno));
@@ -586,14 +595,14 @@ static void test_statmount_string(uint64_t mask, size_t off, const char *name)
exactsize = sm->size;
shortsize = sizeof(*sm) + i;
- ret = statmount(root_id, 0, mask, sm, exactsize, 0);
+ ret = statmount(root_id, 0, 0, mask, sm, exactsize, 0);
if (ret == -1) {
ksft_test_result_fail("statmount exact size: %s\n",
strerror(errno));
goto out;
}
errno = 0;
- ret = statmount(root_id, 0, mask, sm, shortsize, 0);
+ ret = statmount(root_id, 0, 0, mask, sm, shortsize, 0);
if (ret != -1 || errno != EOVERFLOW) {
ksft_test_result_fail("should have failed with EOVERFLOW: %s\n",
strerror(errno));
@@ -658,6 +667,226 @@ static void test_listmount_tree(void)
ksft_test_result_pass("listmount tree\n");
}
+static void test_statmount_by_fd(void)
+{
+ struct statmount *sm = NULL;
+ char tmpdir[] = "/statmount.fd.XXXXXX";
+ const char root[] = "/test";
+ char subdir[PATH_MAX], tmproot[PATH_MAX];
+ int fd;
+
+ if (!mkdtemp(tmpdir)) {
+ ksft_perror("mkdtemp");
+ return;
+ }
+
+ if (mount("statmount.test", tmpdir, "tmpfs", 0, NULL)) {
+ ksft_perror("mount");
+ rmdir(tmpdir);
+ return;
+ }
+
+ snprintf(subdir, PATH_MAX, "%s%s", tmpdir, root);
+ snprintf(tmproot, PATH_MAX, "%s/%s", tmpdir, "chroot");
+
+ if (mkdir(subdir, 0755)) {
+ ksft_perror("mkdir");
+ goto err_tmpdir;
+ }
+
+ if (mount(subdir, subdir, NULL, MS_BIND, 0)) {
+ ksft_perror("mount");
+ goto err_subdir;
+ }
+
+ if (mkdir(tmproot, 0755)) {
+ ksft_perror("mkdir");
+ goto err_subdir;
+ }
+
+ fd = open(subdir, O_PATH);
+ if (fd < 0) {
+ ksft_perror("open");
+ goto err_tmproot;
+ }
+
+ if (chroot(tmproot)) {
+ ksft_perror("chroot");
+ goto err_fd;
+ }
+
+ sm = statmount_alloc(0, fd, STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT, STATMOUNT_BY_FD);
+ if (!sm) {
+ ksft_test_result_fail("statmount by fd failed: %s\n", strerror(errno));
+ goto err_chroot;
+ }
+
+ if (sm->size < sizeof(*sm)) {
+ ksft_test_result_fail("unexpected size: %u < %u\n",
+ sm->size, (uint32_t) sizeof(*sm));
+ goto err_chroot;
+ }
+
+ if (sm->mask & STATMOUNT_MNT_POINT) {
+ ksft_test_result_fail("STATMOUNT_MNT_POINT unexpectedly set in statmount\n");
+ goto err_chroot;
+ }
+
+ if (!(sm->mask & STATMOUNT_MNT_ROOT)) {
+ ksft_test_result_fail("STATMOUNT_MNT_ROOT not set in statmount\n");
+ goto err_chroot;
+ }
+
+ if (strcmp(root, sm->str + sm->mnt_root) != 0) {
+ ksft_test_result_fail("statmount returned incorrect mnt_root,"
+ "statmount mnt_root: %s != %s\n",
+ sm->str + sm->mnt_root, root);
+ goto err_chroot;
+ }
+
+ if (chroot(".")) {
+ ksft_perror("chroot");
+ goto out;
+ }
+
+ free(sm);
+ sm = statmount_alloc(0, fd, STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT, STATMOUNT_BY_FD);
+ if (!sm) {
+ ksft_test_result_fail("statmount by fd failed: %s\n", strerror(errno));
+ goto err_fd;
+ }
+
+ if (sm->size < sizeof(*sm)) {
+ ksft_test_result_fail("unexpected size: %u < %u\n",
+ sm->size, (uint32_t) sizeof(*sm));
+ goto out;
+ }
+
+ if (!(sm->mask & STATMOUNT_MNT_POINT)) {
+ ksft_test_result_fail("STATMOUNT_MNT_POINT not set in statmount\n");
+ goto out;
+ }
+
+ if (!(sm->mask & STATMOUNT_MNT_ROOT)) {
+ ksft_test_result_fail("STATMOUNT_MNT_ROOT not set in statmount\n");
+ goto out;
+ }
+
+ if (strcmp(subdir, sm->str + sm->mnt_point) != 0) {
+ ksft_test_result_fail("statmount returned incorrect mnt_point,"
+ "statmount mnt_point: %s != %s\n", sm->str + sm->mnt_point, subdir);
+ goto out;
+ }
+
+ if (strcmp(root, sm->str + sm->mnt_root) != 0) {
+ ksft_test_result_fail("statmount returned incorrect mnt_root,"
+ "statmount mnt_root: %s != %s\n", sm->str + sm->mnt_root, root);
+ goto out;
+ }
+
+ ksft_test_result_pass("statmount by fd\n");
+ goto out;
+err_chroot:
+ chroot(".");
+out:
+ free(sm);
+err_fd:
+ close(fd);
+err_tmproot:
+ rmdir(tmproot);
+err_subdir:
+ umount2(subdir, MNT_DETACH);
+ rmdir(subdir);
+err_tmpdir:
+ umount2(tmpdir, MNT_DETACH);
+ rmdir(tmpdir);
+}
+
+static void test_statmount_by_fd_unmounted(void)
+{
+ const char root[] = "/test.unmounted";
+ char tmpdir[] = "/statmount.fd.XXXXXX";
+ char subdir[PATH_MAX];
+ int fd;
+ struct statmount *sm = NULL;
+
+ if (!mkdtemp(tmpdir)) {
+ ksft_perror("mkdtemp");
+ return;
+ }
+
+ if (mount("statmount.test", tmpdir, "tmpfs", 0, NULL)) {
+ ksft_perror("mount");
+ rmdir(tmpdir);
+ return;
+ }
+
+ snprintf(subdir, PATH_MAX, "%s%s", tmpdir, root);
+
+ if (mkdir(subdir, 0755)) {
+ ksft_perror("mkdir");
+ goto err_tmpdir;
+ }
+
+ if (mount(subdir, subdir, 0, MS_BIND, NULL)) {
+ ksft_perror("mount");
+ goto err_subdir;
+ }
+
+ fd = open(subdir, O_PATH);
+ if (fd < 0) {
+ ksft_perror("open");
+ goto err_subdir;
+ }
+
+ if (umount2(tmpdir, MNT_DETACH)) {
+ ksft_perror("umount2");
+ goto err_fd;
+ }
+
+ sm = statmount_alloc(0, fd, STATMOUNT_MNT_POINT | STATMOUNT_MNT_ROOT, STATMOUNT_BY_FD);
+ if (!sm) {
+ ksft_test_result_fail("statmount by fd unmounted: %s\n",
+ strerror(errno));
+ goto err_sm;
+ }
+
+ if (sm->size < sizeof(*sm)) {
+ ksft_test_result_fail("unexpected size: %u < %u\n",
+ sm->size, (uint32_t) sizeof(*sm));
+ goto err_sm;
+ }
+
+ if (sm->mask & STATMOUNT_MNT_POINT) {
+ ksft_test_result_fail("STATMOUNT_MNT_POINT unexpectedly set in mask\n");
+ goto err_sm;
+ }
+
+ if (!(sm->mask & STATMOUNT_MNT_ROOT)) {
+ ksft_test_result_fail("STATMOUNT_MNT_ROOT not set in mask\n");
+ goto err_sm;
+ }
+
+ if (strcmp(sm->str + sm->mnt_root, root) != 0) {
+ ksft_test_result_fail("statmount returned incorrect mnt_root,"
+ "statmount mnt_root: %s != %s\n",
+ sm->str + sm->mnt_root, root);
+ goto err_sm;
+ }
+
+ ksft_test_result_pass("statmount by fd on unmounted mount\n");
+err_sm:
+ free(sm);
+err_fd:
+ close(fd);
+err_subdir:
+ umount2(subdir, MNT_DETACH);
+ rmdir(subdir);
+err_tmpdir:
+ umount2(tmpdir, MNT_DETACH);
+ rmdir(tmpdir);
+}
+
#define str_off(memb) (offsetof(struct statmount, memb) / sizeof(uint32_t))
int main(void)
@@ -669,14 +898,14 @@ int main(void)
ksft_print_header();
- ret = statmount(0, 0, 0, NULL, 0, 0);
+ ret = statmount(0, 0, 0, 0, NULL, 0, 0);
assert(ret == -1);
if (errno == ENOSYS)
ksft_exit_skip("statmount() syscall not supported\n");
setup_namespace();
- ksft_set_plan(15);
+ ksft_set_plan(17);
test_listmount_empty_root();
test_statmount_zero_mask();
test_statmount_mnt_basic();
@@ -693,6 +922,8 @@ int main(void)
test_statmount_string(all_mask, str_off(fs_type), "fs type & all");
test_listmount_tree();
+ test_statmount_by_fd_unmounted();
+ test_statmount_by_fd();
if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
diff --git a/tools/testing/selftests/filesystems/statmount/statmount_test_ns.c b/tools/testing/selftests/filesystems/statmount/statmount_test_ns.c
index 605a3fa16bf7..6449b50dde0c 100644
--- a/tools/testing/selftests/filesystems/statmount/statmount_test_ns.c
+++ b/tools/testing/selftests/filesystems/statmount/statmount_test_ns.c
@@ -102,7 +102,7 @@ static int _test_statmount_mnt_ns_id(void)
if (!root_id)
return NSID_ERROR;
- ret = statmount(root_id, 0, STATMOUNT_MNT_NS_ID, &sm, sizeof(sm), 0);
+ ret = statmount(root_id, 0, 0, STATMOUNT_MNT_NS_ID, &sm, sizeof(sm), 0);
if (ret == -1) {
ksft_print_msg("statmount mnt ns id: %s\n", strerror(errno));
return NSID_ERROR;
@@ -128,6 +128,98 @@ static int _test_statmount_mnt_ns_id(void)
return NSID_PASS;
}
+static int _test_statmount_mnt_ns_id_by_fd(void)
+{
+ struct statmount sm;
+ uint64_t mnt_ns_id;
+ int ret, fd, mounted = 1, status = NSID_ERROR;
+ char mnt[] = "/statmount.fd.XXXXXX";
+
+ ret = get_mnt_ns_id("/proc/self/ns/mnt", &mnt_ns_id);
+ if (ret != NSID_PASS)
+ return ret;
+
+ if (!mkdtemp(mnt)) {
+ ksft_print_msg("statmount by fd mnt ns id mkdtemp: %s\n", strerror(errno));
+ return NSID_ERROR;
+ }
+
+ if (mount(mnt, mnt, NULL, MS_BIND, 0)) {
+ ksft_print_msg("statmount by fd mnt ns id mount: %s\n", strerror(errno));
+ status = NSID_ERROR;
+ goto err;
+ }
+
+ fd = open(mnt, O_PATH);
+ if (fd < 0) {
+ ksft_print_msg("statmount by fd mnt ns id open: %s\n", strerror(errno));
+ goto err;
+ }
+
+ ret = statmount(0, 0, fd, STATMOUNT_MNT_NS_ID, &sm, sizeof(sm), STATMOUNT_BY_FD);
+ if (ret == -1) {
+ ksft_print_msg("statmount mnt ns id statmount: %s\n", strerror(errno));
+ status = NSID_ERROR;
+ goto out;
+ }
+
+ if (sm.size != sizeof(sm)) {
+ ksft_print_msg("unexpected size: %u != %u\n", sm.size,
+ (uint32_t)sizeof(sm));
+ status = NSID_FAIL;
+ goto out;
+ }
+ if (sm.mask != STATMOUNT_MNT_NS_ID) {
+ ksft_print_msg("statmount mnt ns id unavailable\n");
+ status = NSID_SKIP;
+ goto out;
+ }
+
+ if (sm.mnt_ns_id != mnt_ns_id) {
+ ksft_print_msg("unexpected mnt ns ID: 0x%llx != 0x%llx\n",
+ (unsigned long long)sm.mnt_ns_id,
+ (unsigned long long)mnt_ns_id);
+ status = NSID_FAIL;
+ goto out;
+ }
+
+ mounted = 0;
+ if (umount2(mnt, MNT_DETACH)) {
+ ksft_print_msg("statmount by fd mnt ns id umount2: %s\n", strerror(errno));
+ goto out;
+ }
+
+ ret = statmount(0, 0, fd, STATMOUNT_MNT_NS_ID, &sm, sizeof(sm), STATMOUNT_BY_FD);
+ if (ret == -1) {
+ ksft_print_msg("statmount mnt ns id statmount: %s\n", strerror(errno));
+ status = NSID_ERROR;
+ goto out;
+ }
+
+ if (sm.size != sizeof(sm)) {
+ ksft_print_msg("unexpected size: %u != %u\n", sm.size,
+ (uint32_t)sizeof(sm));
+ status = NSID_FAIL;
+ goto out;
+ }
+
+ if (sm.mask == STATMOUNT_MNT_NS_ID) {
+ ksft_print_msg("unexpected STATMOUNT_MNT_NS_ID in mask\n");
+ status = NSID_FAIL;
+ goto out;
+ }
+
+ status = NSID_PASS;
+out:
+ close(fd);
+ if (mounted)
+ umount2(mnt, MNT_DETACH);
+err:
+ rmdir(mnt);
+ return status;
+}
+
+
static void test_statmount_mnt_ns_id(void)
{
pid_t pid;
@@ -148,6 +240,9 @@ static void test_statmount_mnt_ns_id(void)
if (ret != NSID_PASS)
exit(ret);
ret = _test_statmount_mnt_ns_id();
+ if (ret != NSID_PASS)
+ exit(ret);
+ ret = _test_statmount_mnt_ns_id_by_fd();
exit(ret);
}
@@ -179,7 +274,7 @@ static int validate_external_listmount(pid_t pid, uint64_t child_nr_mounts)
for (int i = 0; i < nr_mounts; i++) {
struct statmount sm;
- ret = statmount(list[i], mnt_ns_id, STATMOUNT_MNT_NS_ID, &sm,
+ ret = statmount(list[i], mnt_ns_id, 0, STATMOUNT_MNT_NS_ID, &sm,
sizeof(sm), 0);
if (ret < 0) {
ksft_print_msg("statmount mnt ns id: %s\n", strerror(errno));
@@ -275,7 +370,7 @@ int main(void)
int ret;
ksft_print_header();
- ret = statmount(0, 0, 0, NULL, 0, 0);
+ ret = statmount(0, 0, 0, 0, NULL, 0, 0);
assert(ret == -1);
if (errno == ENOSYS)
ksft_exit_skip("statmount() syscall not supported\n");
--
2.52.0
© 2016 - 2025 Red Hat, Inc.