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 <[email protected]>
Signed-off-by: Andrei Vagin <[email protected]>
Signed-off-by: Bhavik Sachdev <[email protected]>
---
 .../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


Reply via email to