Skip to content

Commit 0c82fdb

Browse files
bsach64brauner
authored andcommitted
selftests: statmount: tests for STATMOUNT_BY_FD
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> Link: https://patch.msgid.link/20251129091455.757724-4-b.sachdev1904@gmail.com Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent 0e50322 commit 0c82fdb

File tree

3 files changed

+354
-23
lines changed

3 files changed

+354
-23
lines changed

tools/testing/selftests/filesystems/statmount/statmount.h

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,24 @@
4343
#endif
4444
#endif
4545

46-
static inline int statmount(uint64_t mnt_id, uint64_t mnt_ns_id, uint64_t mask,
47-
struct statmount *buf, size_t bufsize,
46+
static inline int statmount(uint64_t mnt_id, uint64_t mnt_ns_id, uint32_t fd,
47+
uint64_t mask, struct statmount *buf, size_t bufsize,
4848
unsigned int flags)
4949
{
5050
struct mnt_id_req req = {
5151
.size = MNT_ID_REQ_SIZE_VER0,
52-
.mnt_id = mnt_id,
5352
.param = mask,
5453
};
5554

56-
if (mnt_ns_id) {
55+
if (flags & STATMOUNT_BY_FD) {
5756
req.size = MNT_ID_REQ_SIZE_VER1;
58-
req.mnt_ns_id = mnt_ns_id;
57+
req.mnt_fd = fd;
58+
} else {
59+
req.mnt_id = mnt_id;
60+
if (mnt_ns_id) {
61+
req.size = MNT_ID_REQ_SIZE_VER1;
62+
req.mnt_ns_id = mnt_ns_id;
63+
}
5964
}
6065

6166
return syscall(__NR_statmount, &req, buf, bufsize, flags);

tools/testing/selftests/filesystems/statmount/statmount_test.c

Lines changed: 246 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,24 @@ static const char *const known_fs[] = {
3333
"sysv", "tmpfs", "tracefs", "ubifs", "udf", "ufs", "v7", "vboxsf",
3434
"vfat", "virtiofs", "vxfs", "xenfs", "xfs", "zonefs", NULL };
3535

36-
static struct statmount *statmount_alloc(uint64_t mnt_id, uint64_t mask, unsigned int flags)
36+
static struct statmount *statmount_alloc(uint64_t mnt_id, int fd, uint64_t mask, unsigned int flags)
3737
{
3838
size_t bufsize = 1 << 15;
39-
struct statmount *buf = NULL, *tmp = alloca(bufsize);
39+
struct statmount *buf = NULL, *tmp = NULL;
4040
int tofree = 0;
4141
int ret;
4242

43+
if (flags & STATMOUNT_BY_FD && fd < 0)
44+
return NULL;
45+
46+
tmp = alloca(bufsize);
47+
4348
for (;;) {
44-
ret = statmount(mnt_id, 0, mask, tmp, bufsize, flags);
49+
if (flags & STATMOUNT_BY_FD)
50+
ret = statmount(0, 0, (uint32_t) fd, mask, tmp, bufsize, flags);
51+
else
52+
ret = statmount(mnt_id, 0, 0, mask, tmp, bufsize, flags);
53+
4554
if (ret != -1)
4655
break;
4756
if (tofree)
@@ -237,7 +246,7 @@ static void test_statmount_zero_mask(void)
237246
struct statmount sm;
238247
int ret;
239248

240-
ret = statmount(root_id, 0, 0, &sm, sizeof(sm), 0);
249+
ret = statmount(root_id, 0, 0, 0, &sm, sizeof(sm), 0);
241250
if (ret == -1) {
242251
ksft_test_result_fail("statmount zero mask: %s\n",
243252
strerror(errno));
@@ -263,7 +272,7 @@ static void test_statmount_mnt_basic(void)
263272
int ret;
264273
uint64_t mask = STATMOUNT_MNT_BASIC;
265274

266-
ret = statmount(root_id, 0, mask, &sm, sizeof(sm), 0);
275+
ret = statmount(root_id, 0, 0, mask, &sm, sizeof(sm), 0);
267276
if (ret == -1) {
268277
ksft_test_result_fail("statmount mnt basic: %s\n",
269278
strerror(errno));
@@ -323,7 +332,7 @@ static void test_statmount_sb_basic(void)
323332
struct statx sx;
324333
struct statfs sf;
325334

326-
ret = statmount(root_id, 0, mask, &sm, sizeof(sm), 0);
335+
ret = statmount(root_id, 0, 0, mask, &sm, sizeof(sm), 0);
327336
if (ret == -1) {
328337
ksft_test_result_fail("statmount sb basic: %s\n",
329338
strerror(errno));
@@ -375,7 +384,7 @@ static void test_statmount_mnt_point(void)
375384
{
376385
struct statmount *sm;
377386

378-
sm = statmount_alloc(root_id, STATMOUNT_MNT_POINT, 0);
387+
sm = statmount_alloc(root_id, 0, STATMOUNT_MNT_POINT, 0);
379388
if (!sm) {
380389
ksft_test_result_fail("statmount mount point: %s\n",
381390
strerror(errno));
@@ -405,7 +414,7 @@ static void test_statmount_mnt_root(void)
405414
assert(last_dir);
406415
last_dir++;
407416

408-
sm = statmount_alloc(root_id, STATMOUNT_MNT_ROOT, 0);
417+
sm = statmount_alloc(root_id, 0, STATMOUNT_MNT_ROOT, 0);
409418
if (!sm) {
410419
ksft_test_result_fail("statmount mount root: %s\n",
411420
strerror(errno));
@@ -438,7 +447,7 @@ static void test_statmount_fs_type(void)
438447
const char *fs_type;
439448
const char *const *s;
440449

441-
sm = statmount_alloc(root_id, STATMOUNT_FS_TYPE, 0);
450+
sm = statmount_alloc(root_id, 0, STATMOUNT_FS_TYPE, 0);
442451
if (!sm) {
443452
ksft_test_result_fail("statmount fs type: %s\n",
444453
strerror(errno));
@@ -467,7 +476,7 @@ static void test_statmount_mnt_opts(void)
467476
char *line = NULL;
468477
size_t len = 0;
469478

470-
sm = statmount_alloc(root_id, STATMOUNT_MNT_BASIC | STATMOUNT_MNT_OPTS,
479+
sm = statmount_alloc(root_id, 0, STATMOUNT_MNT_BASIC | STATMOUNT_MNT_OPTS,
471480
0);
472481
if (!sm) {
473482
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)
557566
uint32_t start, i;
558567
int ret;
559568

560-
sm = statmount_alloc(root_id, mask, 0);
569+
sm = statmount_alloc(root_id, 0, mask, 0);
561570
if (!sm) {
562571
ksft_test_result_fail("statmount %s: %s\n", name,
563572
strerror(errno));
@@ -586,14 +595,14 @@ static void test_statmount_string(uint64_t mask, size_t off, const char *name)
586595
exactsize = sm->size;
587596
shortsize = sizeof(*sm) + i;
588597

589-
ret = statmount(root_id, 0, mask, sm, exactsize, 0);
598+
ret = statmount(root_id, 0, 0, mask, sm, exactsize, 0);
590599
if (ret == -1) {
591600
ksft_test_result_fail("statmount exact size: %s\n",
592601
strerror(errno));
593602
goto out;
594603
}
595604
errno = 0;
596-
ret = statmount(root_id, 0, mask, sm, shortsize, 0);
605+
ret = statmount(root_id, 0, 0, mask, sm, shortsize, 0);
597606
if (ret != -1 || errno != EOVERFLOW) {
598607
ksft_test_result_fail("should have failed with EOVERFLOW: %s\n",
599608
strerror(errno));
@@ -658,6 +667,226 @@ static void test_listmount_tree(void)
658667
ksft_test_result_pass("listmount tree\n");
659668
}
660669

670+
static void test_statmount_by_fd(void)
671+
{
672+
struct statmount *sm = NULL;
673+
char tmpdir[] = "/statmount.fd.XXXXXX";
674+
const char root[] = "/test";
675+
char subdir[PATH_MAX], tmproot[PATH_MAX];
676+
int fd;
677+
678+
if (!mkdtemp(tmpdir)) {
679+
ksft_perror("mkdtemp");
680+
return;
681+
}
682+
683+
if (mount("statmount.test", tmpdir, "tmpfs", 0, NULL)) {
684+
ksft_perror("mount");
685+
rmdir(tmpdir);
686+
return;
687+
}
688+
689+
snprintf(subdir, PATH_MAX, "%s%s", tmpdir, root);
690+
snprintf(tmproot, PATH_MAX, "%s/%s", tmpdir, "chroot");
691+
692+
if (mkdir(subdir, 0755)) {
693+
ksft_perror("mkdir");
694+
goto err_tmpdir;
695+
}
696+
697+
if (mount(subdir, subdir, NULL, MS_BIND, 0)) {
698+
ksft_perror("mount");
699+
goto err_subdir;
700+
}
701+
702+
if (mkdir(tmproot, 0755)) {
703+
ksft_perror("mkdir");
704+
goto err_subdir;
705+
}
706+
707+
fd = open(subdir, O_PATH);
708+
if (fd < 0) {
709+
ksft_perror("open");
710+
goto err_tmproot;
711+
}
712+
713+
if (chroot(tmproot)) {
714+
ksft_perror("chroot");
715+
goto err_fd;
716+
}
717+
718+
sm = statmount_alloc(0, fd, STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT, STATMOUNT_BY_FD);
719+
if (!sm) {
720+
ksft_test_result_fail("statmount by fd failed: %s\n", strerror(errno));
721+
goto err_chroot;
722+
}
723+
724+
if (sm->size < sizeof(*sm)) {
725+
ksft_test_result_fail("unexpected size: %u < %u\n",
726+
sm->size, (uint32_t) sizeof(*sm));
727+
goto err_chroot;
728+
}
729+
730+
if (sm->mask & STATMOUNT_MNT_POINT) {
731+
ksft_test_result_fail("STATMOUNT_MNT_POINT unexpectedly set in statmount\n");
732+
goto err_chroot;
733+
}
734+
735+
if (!(sm->mask & STATMOUNT_MNT_ROOT)) {
736+
ksft_test_result_fail("STATMOUNT_MNT_ROOT not set in statmount\n");
737+
goto err_chroot;
738+
}
739+
740+
if (strcmp(root, sm->str + sm->mnt_root) != 0) {
741+
ksft_test_result_fail("statmount returned incorrect mnt_root,"
742+
"statmount mnt_root: %s != %s\n",
743+
sm->str + sm->mnt_root, root);
744+
goto err_chroot;
745+
}
746+
747+
if (chroot(".")) {
748+
ksft_perror("chroot");
749+
goto out;
750+
}
751+
752+
free(sm);
753+
sm = statmount_alloc(0, fd, STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT, STATMOUNT_BY_FD);
754+
if (!sm) {
755+
ksft_test_result_fail("statmount by fd failed: %s\n", strerror(errno));
756+
goto err_fd;
757+
}
758+
759+
if (sm->size < sizeof(*sm)) {
760+
ksft_test_result_fail("unexpected size: %u < %u\n",
761+
sm->size, (uint32_t) sizeof(*sm));
762+
goto out;
763+
}
764+
765+
if (!(sm->mask & STATMOUNT_MNT_POINT)) {
766+
ksft_test_result_fail("STATMOUNT_MNT_POINT not set in statmount\n");
767+
goto out;
768+
}
769+
770+
if (!(sm->mask & STATMOUNT_MNT_ROOT)) {
771+
ksft_test_result_fail("STATMOUNT_MNT_ROOT not set in statmount\n");
772+
goto out;
773+
}
774+
775+
if (strcmp(subdir, sm->str + sm->mnt_point) != 0) {
776+
ksft_test_result_fail("statmount returned incorrect mnt_point,"
777+
"statmount mnt_point: %s != %s\n", sm->str + sm->mnt_point, subdir);
778+
goto out;
779+
}
780+
781+
if (strcmp(root, sm->str + sm->mnt_root) != 0) {
782+
ksft_test_result_fail("statmount returned incorrect mnt_root,"
783+
"statmount mnt_root: %s != %s\n", sm->str + sm->mnt_root, root);
784+
goto out;
785+
}
786+
787+
ksft_test_result_pass("statmount by fd\n");
788+
goto out;
789+
err_chroot:
790+
chroot(".");
791+
out:
792+
free(sm);
793+
err_fd:
794+
close(fd);
795+
err_tmproot:
796+
rmdir(tmproot);
797+
err_subdir:
798+
umount2(subdir, MNT_DETACH);
799+
rmdir(subdir);
800+
err_tmpdir:
801+
umount2(tmpdir, MNT_DETACH);
802+
rmdir(tmpdir);
803+
}
804+
805+
static void test_statmount_by_fd_unmounted(void)
806+
{
807+
const char root[] = "/test.unmounted";
808+
char tmpdir[] = "/statmount.fd.XXXXXX";
809+
char subdir[PATH_MAX];
810+
int fd;
811+
struct statmount *sm = NULL;
812+
813+
if (!mkdtemp(tmpdir)) {
814+
ksft_perror("mkdtemp");
815+
return;
816+
}
817+
818+
if (mount("statmount.test", tmpdir, "tmpfs", 0, NULL)) {
819+
ksft_perror("mount");
820+
rmdir(tmpdir);
821+
return;
822+
}
823+
824+
snprintf(subdir, PATH_MAX, "%s%s", tmpdir, root);
825+
826+
if (mkdir(subdir, 0755)) {
827+
ksft_perror("mkdir");
828+
goto err_tmpdir;
829+
}
830+
831+
if (mount(subdir, subdir, 0, MS_BIND, NULL)) {
832+
ksft_perror("mount");
833+
goto err_subdir;
834+
}
835+
836+
fd = open(subdir, O_PATH);
837+
if (fd < 0) {
838+
ksft_perror("open");
839+
goto err_subdir;
840+
}
841+
842+
if (umount2(tmpdir, MNT_DETACH)) {
843+
ksft_perror("umount2");
844+
goto err_fd;
845+
}
846+
847+
sm = statmount_alloc(0, fd, STATMOUNT_MNT_POINT | STATMOUNT_MNT_ROOT, STATMOUNT_BY_FD);
848+
if (!sm) {
849+
ksft_test_result_fail("statmount by fd unmounted: %s\n",
850+
strerror(errno));
851+
goto err_sm;
852+
}
853+
854+
if (sm->size < sizeof(*sm)) {
855+
ksft_test_result_fail("unexpected size: %u < %u\n",
856+
sm->size, (uint32_t) sizeof(*sm));
857+
goto err_sm;
858+
}
859+
860+
if (sm->mask & STATMOUNT_MNT_POINT) {
861+
ksft_test_result_fail("STATMOUNT_MNT_POINT unexpectedly set in mask\n");
862+
goto err_sm;
863+
}
864+
865+
if (!(sm->mask & STATMOUNT_MNT_ROOT)) {
866+
ksft_test_result_fail("STATMOUNT_MNT_ROOT not set in mask\n");
867+
goto err_sm;
868+
}
869+
870+
if (strcmp(sm->str + sm->mnt_root, root) != 0) {
871+
ksft_test_result_fail("statmount returned incorrect mnt_root,"
872+
"statmount mnt_root: %s != %s\n",
873+
sm->str + sm->mnt_root, root);
874+
goto err_sm;
875+
}
876+
877+
ksft_test_result_pass("statmount by fd on unmounted mount\n");
878+
err_sm:
879+
free(sm);
880+
err_fd:
881+
close(fd);
882+
err_subdir:
883+
umount2(subdir, MNT_DETACH);
884+
rmdir(subdir);
885+
err_tmpdir:
886+
umount2(tmpdir, MNT_DETACH);
887+
rmdir(tmpdir);
888+
}
889+
661890
#define str_off(memb) (offsetof(struct statmount, memb) / sizeof(uint32_t))
662891

663892
int main(void)
@@ -669,14 +898,14 @@ int main(void)
669898

670899
ksft_print_header();
671900

672-
ret = statmount(0, 0, 0, NULL, 0, 0);
901+
ret = statmount(0, 0, 0, 0, NULL, 0, 0);
673902
assert(ret == -1);
674903
if (errno == ENOSYS)
675904
ksft_exit_skip("statmount() syscall not supported\n");
676905

677906
setup_namespace();
678907

679-
ksft_set_plan(15);
908+
ksft_set_plan(17);
680909
test_listmount_empty_root();
681910
test_statmount_zero_mask();
682911
test_statmount_mnt_basic();
@@ -693,6 +922,8 @@ int main(void)
693922
test_statmount_string(all_mask, str_off(fs_type), "fs type & all");
694923

695924
test_listmount_tree();
925+
test_statmount_by_fd_unmounted();
926+
test_statmount_by_fd();
696927

697928

698929
if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)

0 commit comments

Comments
 (0)