1 // SPDX-License-Identifier: GPL-2.0-or-later
10 #include <sys/mount.h>
12 #include <sys/statfs.h>
13 #include <linux/mount.h>
14 #include <linux/stat.h>
15 #include <asm/unistd.h>
17 #include "../../kselftest.h"
19 static const char *const known_fs[] = {
20 "9p", "adfs", "affs", "afs", "aio", "anon_inodefs", "apparmorfs",
21 "autofs", "bcachefs", "bdev", "befs", "bfs", "binder", "binfmt_misc",
22 "bpf", "btrfs", "btrfs_test_fs", "ceph", "cgroup", "cgroup2", "cifs",
23 "coda", "configfs", "cpuset", "cramfs", "cxl", "dax", "debugfs",
24 "devpts", "devtmpfs", "dmabuf", "drm", "ecryptfs", "efivarfs", "efs",
25 "erofs", "exfat", "ext2", "ext3", "ext4", "f2fs", "functionfs",
26 "fuse", "fuseblk", "fusectl", "gadgetfs", "gfs2", "gfs2meta", "hfs",
27 "hfsplus", "hostfs", "hpfs", "hugetlbfs", "ibmasmfs", "iomem",
28 "ipathfs", "iso9660", "jffs2", "jfs", "minix", "mqueue", "msdos",
29 "nfs", "nfs4", "nfsd", "nilfs2", "nsfs", "ntfs", "ntfs3", "ocfs2",
30 "ocfs2_dlmfs", "ocxlflash", "omfs", "openpromfs", "overlay", "pipefs",
31 "proc", "pstore", "pvfs2", "qnx4", "qnx6", "ramfs", "reiserfs",
32 "resctrl", "romfs", "rootfs", "rpc_pipefs", "s390_hypfs", "secretmem",
33 "securityfs", "selinuxfs", "smackfs", "smb3", "sockfs", "spufs",
34 "squashfs", "sysfs", "sysv", "tmpfs", "tracefs", "ubifs", "udf",
35 "ufs", "v7", "vboxsf", "vfat", "virtiofs", "vxfs", "xenfs", "xfs",
38 static int statmount(uint64_t mnt_id, uint64_t mask, struct statmount *buf,
39 size_t bufsize, unsigned int flags)
41 struct mnt_id_req req = {
42 .size = MNT_ID_REQ_SIZE_VER0,
47 return syscall(__NR_statmount, &req, buf, bufsize, flags);
50 static struct statmount *statmount_alloc(uint64_t mnt_id, uint64_t mask, unsigned int flags)
52 size_t bufsize = 1 << 15;
53 struct statmount *buf = NULL, *tmp = alloca(bufsize);
58 ret = statmount(mnt_id, mask, tmp, bufsize, flags);
63 if (errno != EOVERFLOW)
67 tmp = malloc(bufsize);
71 buf = malloc(tmp->size);
73 memcpy(buf, tmp, tmp->size);
80 static void write_file(const char *path, const char *val)
82 int fd = open(path, O_WRONLY);
83 size_t len = strlen(val);
87 ksft_exit_fail_msg("opening %s for write: %s\n", path, strerror(errno));
89 ret = write(fd, val, len);
91 ksft_exit_fail_msg("writing to %s: %s\n", path, strerror(errno));
93 ksft_exit_fail_msg("short write to %s\n", path);
97 ksft_exit_fail_msg("closing %s\n", path);
100 static uint64_t get_mnt_id(const char *name, const char *path, uint64_t mask)
105 ret = statx(AT_FDCWD, path, 0, mask, &sx);
107 ksft_exit_fail_msg("retrieving %s mount ID for %s: %s\n",
108 mask & STATX_MNT_ID_UNIQUE ? "unique" : "old",
109 name, strerror(errno));
110 if (!(sx.stx_mask & mask))
111 ksft_exit_fail_msg("no %s mount ID available for %s\n",
112 mask & STATX_MNT_ID_UNIQUE ? "unique" : "old",
115 return sx.stx_mnt_id;
119 static char root_mntpoint[] = "/tmp/statmount_test_root.XXXXXX";
120 static int orig_root;
121 static uint64_t root_id, parent_id;
122 static uint32_t old_root_id, old_parent_id;
125 static void cleanup_namespace(void)
129 umount2(root_mntpoint, MNT_DETACH);
130 rmdir(root_mntpoint);
133 static void setup_namespace(void)
137 uid_t uid = getuid();
138 gid_t gid = getgid();
140 ret = unshare(CLONE_NEWNS|CLONE_NEWUSER);
142 ksft_exit_fail_msg("unsharing mountns and userns: %s\n",
145 sprintf(buf, "0 %d 1", uid);
146 write_file("/proc/self/uid_map", buf);
147 write_file("/proc/self/setgroups", "deny");
148 sprintf(buf, "0 %d 1", gid);
149 write_file("/proc/self/gid_map", buf);
151 ret = mount("", "/", NULL, MS_REC|MS_PRIVATE, NULL);
153 ksft_exit_fail_msg("making mount tree private: %s\n",
156 if (!mkdtemp(root_mntpoint))
157 ksft_exit_fail_msg("creating temporary directory %s: %s\n",
158 root_mntpoint, strerror(errno));
160 old_parent_id = get_mnt_id("parent", root_mntpoint, STATX_MNT_ID);
161 parent_id = get_mnt_id("parent", root_mntpoint, STATX_MNT_ID_UNIQUE);
163 orig_root = open("/", O_PATH);
165 ksft_exit_fail_msg("opening root directory: %s",
168 atexit(cleanup_namespace);
170 ret = mount(root_mntpoint, root_mntpoint, NULL, MS_BIND, NULL);
172 ksft_exit_fail_msg("mounting temp root %s: %s\n",
173 root_mntpoint, strerror(errno));
175 ret = chroot(root_mntpoint);
177 ksft_exit_fail_msg("chroot to temp root %s: %s\n",
178 root_mntpoint, strerror(errno));
182 ksft_exit_fail_msg("chdir to root: %s\n", strerror(errno));
184 old_root_id = get_mnt_id("root", "/", STATX_MNT_ID);
185 root_id = get_mnt_id("root", "/", STATX_MNT_ID_UNIQUE);
188 static int setup_mount_tree(int log2_num)
192 ret = mount("", "/", NULL, MS_REC|MS_SHARED, NULL);
194 ksft_test_result_fail("making mount tree shared: %s\n",
199 for (i = 0; i < log2_num; i++) {
200 ret = mount("/", "/", NULL, MS_BIND, NULL);
202 ksft_test_result_fail("mounting submount %s: %s\n",
203 root_mntpoint, strerror(errno));
210 static ssize_t listmount(uint64_t mnt_id, uint64_t last_mnt_id,
211 uint64_t list[], size_t num, unsigned int flags)
213 struct mnt_id_req req = {
214 .size = MNT_ID_REQ_SIZE_VER0,
216 .param = last_mnt_id,
219 return syscall(__NR_listmount, &req, list, num, flags);
222 static void test_listmount_empty_root(void)
225 const unsigned int size = 32;
228 res = listmount(LSMT_ROOT, 0, list, size, 0);
230 ksft_test_result_fail("listmount: %s\n", strerror(errno));
234 ksft_test_result_fail("listmount result is %zi != 1\n", res);
238 if (list[0] != root_id) {
239 ksft_test_result_fail("listmount ID doesn't match 0x%llx != 0x%llx\n",
240 (unsigned long long) list[0],
241 (unsigned long long) root_id);
245 ksft_test_result_pass("listmount empty root\n");
248 static void test_statmount_zero_mask(void)
253 ret = statmount(root_id, 0, &sm, sizeof(sm), 0);
255 ksft_test_result_fail("statmount zero mask: %s\n",
259 if (sm.size != sizeof(sm)) {
260 ksft_test_result_fail("unexpected size: %u != %u\n",
261 sm.size, (uint32_t) sizeof(sm));
265 ksft_test_result_fail("unexpected mask: 0x%llx != 0x0\n",
266 (unsigned long long) sm.mask);
270 ksft_test_result_pass("statmount zero mask\n");
273 static void test_statmount_mnt_basic(void)
277 uint64_t mask = STATMOUNT_MNT_BASIC;
279 ret = statmount(root_id, mask, &sm, sizeof(sm), 0);
281 ksft_test_result_fail("statmount mnt basic: %s\n",
285 if (sm.size != sizeof(sm)) {
286 ksft_test_result_fail("unexpected size: %u != %u\n",
287 sm.size, (uint32_t) sizeof(sm));
290 if (sm.mask != mask) {
291 ksft_test_result_skip("statmount mnt basic unavailable\n");
295 if (sm.mnt_id != root_id) {
296 ksft_test_result_fail("unexpected root ID: 0x%llx != 0x%llx\n",
297 (unsigned long long) sm.mnt_id,
298 (unsigned long long) root_id);
302 if (sm.mnt_id_old != old_root_id) {
303 ksft_test_result_fail("unexpected old root ID: %u != %u\n",
304 sm.mnt_id_old, old_root_id);
308 if (sm.mnt_parent_id != parent_id) {
309 ksft_test_result_fail("unexpected parent ID: 0x%llx != 0x%llx\n",
310 (unsigned long long) sm.mnt_parent_id,
311 (unsigned long long) parent_id);
315 if (sm.mnt_parent_id_old != old_parent_id) {
316 ksft_test_result_fail("unexpected old parent ID: %u != %u\n",
317 sm.mnt_parent_id_old, old_parent_id);
321 if (sm.mnt_propagation != MS_PRIVATE) {
322 ksft_test_result_fail("unexpected propagation: 0x%llx\n",
323 (unsigned long long) sm.mnt_propagation);
327 ksft_test_result_pass("statmount mnt basic\n");
331 static void test_statmount_sb_basic(void)
335 uint64_t mask = STATMOUNT_SB_BASIC;
339 ret = statmount(root_id, mask, &sm, sizeof(sm), 0);
341 ksft_test_result_fail("statmount sb basic: %s\n",
345 if (sm.size != sizeof(sm)) {
346 ksft_test_result_fail("unexpected size: %u != %u\n",
347 sm.size, (uint32_t) sizeof(sm));
350 if (sm.mask != mask) {
351 ksft_test_result_skip("statmount sb basic unavailable\n");
355 ret = statx(AT_FDCWD, "/", 0, 0, &sx);
357 ksft_test_result_fail("stat root failed: %s\n",
362 if (sm.sb_dev_major != sx.stx_dev_major ||
363 sm.sb_dev_minor != sx.stx_dev_minor) {
364 ksft_test_result_fail("unexpected sb dev %u:%u != %u:%u\n",
365 sm.sb_dev_major, sm.sb_dev_minor,
366 sx.stx_dev_major, sx.stx_dev_minor);
370 ret = statfs("/", &sf);
372 ksft_test_result_fail("statfs root failed: %s\n",
377 if (sm.sb_magic != sf.f_type) {
378 ksft_test_result_fail("unexpected sb magic: 0x%llx != 0x%lx\n",
379 (unsigned long long) sm.sb_magic,
384 ksft_test_result_pass("statmount sb basic\n");
387 static void test_statmount_mnt_point(void)
389 struct statmount *sm;
391 sm = statmount_alloc(root_id, STATMOUNT_MNT_POINT, 0);
393 ksft_test_result_fail("statmount mount point: %s\n",
398 if (strcmp(sm->str + sm->mnt_point, "/") != 0) {
399 ksft_test_result_fail("unexpected mount point: '%s' != '/'\n",
400 sm->str + sm->mnt_point);
403 ksft_test_result_pass("statmount mount point\n");
408 static void test_statmount_mnt_root(void)
410 struct statmount *sm;
411 const char *mnt_root, *last_dir, *last_root;
413 last_dir = strrchr(root_mntpoint, '/');
417 sm = statmount_alloc(root_id, STATMOUNT_MNT_ROOT, 0);
419 ksft_test_result_fail("statmount mount root: %s\n",
423 mnt_root = sm->str + sm->mnt_root;
424 last_root = strrchr(mnt_root, '/');
428 last_root = mnt_root;
430 if (strcmp(last_dir, last_root) != 0) {
431 ksft_test_result_fail("unexpected mount root last component: '%s' != '%s'\n",
432 last_root, last_dir);
435 ksft_test_result_pass("statmount mount root\n");
440 static void test_statmount_fs_type(void)
442 struct statmount *sm;
444 const char *const *s;
446 sm = statmount_alloc(root_id, STATMOUNT_FS_TYPE, 0);
448 ksft_test_result_fail("statmount fs type: %s\n",
452 fs_type = sm->str + sm->fs_type;
453 for (s = known_fs; s != NULL; s++) {
454 if (strcmp(fs_type, *s) == 0)
458 ksft_print_msg("unknown filesystem type: %s\n", fs_type);
460 ksft_test_result_pass("statmount fs type\n");
464 static void test_statmount_string(uint64_t mask, size_t off, const char *name)
466 struct statmount *sm;
467 size_t len, shortsize, exactsize;
471 sm = statmount_alloc(root_id, mask, 0);
473 ksft_test_result_fail("statmount %s: %s\n", name,
477 if (sm->size < sizeof(*sm)) {
478 ksft_test_result_fail("unexpected size: %u < %u\n",
479 sm->size, (uint32_t) sizeof(*sm));
482 if (sm->mask != mask) {
483 ksft_test_result_skip("statmount %s unavailable\n", name);
486 len = sm->size - sizeof(*sm);
487 start = ((uint32_t *) sm)[off];
489 for (i = start;; i++) {
491 ksft_test_result_fail("string out of bounds\n");
497 exactsize = sm->size;
498 shortsize = sizeof(*sm) + i;
500 ret = statmount(root_id, mask, sm, exactsize, 0);
502 ksft_test_result_fail("statmount exact size: %s\n",
507 ret = statmount(root_id, mask, sm, shortsize, 0);
508 if (ret != -1 || errno != EOVERFLOW) {
509 ksft_test_result_fail("should have failed with EOVERFLOW: %s\n",
514 ksft_test_result_pass("statmount string %s\n", name);
519 static void test_listmount_tree(void)
522 const unsigned int log2_num = 4;
523 const unsigned int step = 3;
524 const unsigned int size = (1 << log2_num) + step + 1;
525 size_t num, expect = 1 << log2_num;
527 uint64_t list2[size];
531 res = setup_mount_tree(log2_num);
535 num = res = listmount(LSMT_ROOT, 0, list, size, 0);
537 ksft_test_result_fail("listmount: %s\n", strerror(errno));
541 ksft_test_result_fail("listmount result is %zi != %zi\n",
546 for (i = 0; i < size - step;) {
547 res = listmount(LSMT_ROOT, i ? list2[i - 1] : 0, list2 + i, step, 0);
549 ksft_test_result_fail("short listmount: %s\n",
556 ksft_test_result_fail("different number of entries: %zu != %zu\n",
560 for (i = 0; i < num; i++) {
561 if (list2[i] != list[i]) {
562 ksft_test_result_fail("different value for entry %zu: 0x%llx != 0x%llx\n",
564 (unsigned long long) list2[i],
565 (unsigned long long) list[i]);
569 ksft_test_result_pass("listmount tree\n");
572 #define str_off(memb) (offsetof(struct statmount, memb) / sizeof(uint32_t))
577 uint64_t all_mask = STATMOUNT_SB_BASIC | STATMOUNT_MNT_BASIC |
578 STATMOUNT_PROPAGATE_FROM | STATMOUNT_MNT_ROOT |
579 STATMOUNT_MNT_POINT | STATMOUNT_FS_TYPE;
583 ret = statmount(0, 0, NULL, 0, 0);
586 ksft_exit_skip("statmount() syscall not supported\n");
591 test_listmount_empty_root();
592 test_statmount_zero_mask();
593 test_statmount_mnt_basic();
594 test_statmount_sb_basic();
595 test_statmount_mnt_root();
596 test_statmount_mnt_point();
597 test_statmount_fs_type();
598 test_statmount_string(STATMOUNT_MNT_ROOT, str_off(mnt_root), "mount root");
599 test_statmount_string(STATMOUNT_MNT_POINT, str_off(mnt_point), "mount point");
600 test_statmount_string(STATMOUNT_FS_TYPE, str_off(fs_type), "fs type");
601 test_statmount_string(all_mask, str_off(mnt_root), "mount root & all");
602 test_statmount_string(all_mask, str_off(mnt_point), "mount point & all");
603 test_statmount_string(all_mask, str_off(fs_type), "fs type & all");
605 test_listmount_tree();
608 if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)