Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0-or-later
2
3#define _GNU_SOURCE
4
5#include <assert.h>
6#include <stddef.h>
7#include <sched.h>
8#include <fcntl.h>
9#include <sys/param.h>
10#include <sys/mount.h>
11#include <sys/stat.h>
12#include <sys/statfs.h>
13#include <linux/stat.h>
14
15#include "statmount.h"
16#include "kselftest.h"
17
18static const char *const known_fs[] = {
19 "9p", "adfs", "affs", "afs", "aio", "anon_inodefs", "apparmorfs",
20 "autofs", "bcachefs", "bdev", "befs", "bfs", "binder", "binfmt_misc",
21 "bpf", "btrfs", "btrfs_test_fs", "ceph", "cgroup", "cgroup2", "cifs",
22 "coda", "configfs", "cpuset", "cramfs", "cxl", "dax", "debugfs",
23 "devpts", "devtmpfs", "dmabuf", "drm", "ecryptfs", "efivarfs", "efs",
24 "erofs", "exfat", "ext2", "ext3", "ext4", "f2fs", "functionfs",
25 "fuse", "fuseblk", "fusectl", "gadgetfs", "gfs2", "gfs2meta", "hfs",
26 "hfsplus", "hostfs", "hpfs", "hugetlbfs", "ibmasmfs", "iomem",
27 "ipathfs", "iso9660", "jffs2", "jfs", "minix", "mqueue", "msdos",
28 "nfs", "nfs4", "nfsd", "nilfs2", "nsfs", "ntfs", "ntfs3", "ocfs2",
29 "ocfs2_dlmfs", "omfs", "openpromfs", "overlay", "pipefs", "proc",
30 "pstore", "pvfs2", "qnx4", "qnx6", "ramfs", "resctrl", "romfs",
31 "rootfs", "rpc_pipefs", "s390_hypfs", "secretmem", "securityfs",
32 "selinuxfs", "smackfs", "smb3", "sockfs", "spufs", "squashfs", "sysfs",
33 "sysv", "tmpfs", "tracefs", "ubifs", "udf", "ufs", "v7", "vboxsf",
34 "vfat", "virtiofs", "vxfs", "xenfs", "xfs", "zonefs", NULL };
35
36static void write_file(const char *path, const char *val)
37{
38 int fd = open(path, O_WRONLY);
39 size_t len = strlen(val);
40 int ret;
41
42 if (fd == -1)
43 ksft_exit_fail_msg("opening %s for write: %s\n", path, strerror(errno));
44
45 ret = write(fd, val, len);
46 if (ret == -1)
47 ksft_exit_fail_msg("writing to %s: %s\n", path, strerror(errno));
48 if (ret != len)
49 ksft_exit_fail_msg("short write to %s\n", path);
50
51 ret = close(fd);
52 if (ret == -1)
53 ksft_exit_fail_msg("closing %s\n", path);
54}
55
56static uint64_t get_mnt_id(const char *name, const char *path, uint64_t mask)
57{
58 struct statx sx;
59 int ret;
60
61 ret = statx(AT_FDCWD, path, 0, mask, &sx);
62 if (ret == -1)
63 ksft_exit_fail_msg("retrieving %s mount ID for %s: %s\n",
64 mask & STATX_MNT_ID_UNIQUE ? "unique" : "old",
65 name, strerror(errno));
66 if (!(sx.stx_mask & mask))
67 ksft_exit_fail_msg("no %s mount ID available for %s\n",
68 mask & STATX_MNT_ID_UNIQUE ? "unique" : "old",
69 name);
70
71 return sx.stx_mnt_id;
72}
73
74
75static char root_mntpoint[] = "/tmp/statmount_test_root.XXXXXX";
76static int orig_root;
77static uint64_t root_id, parent_id;
78static uint32_t old_root_id, old_parent_id;
79static FILE *f_mountinfo;
80
81static void cleanup_namespace(void)
82{
83 int ret;
84
85 ret = fchdir(orig_root);
86 if (ret == -1)
87 ksft_perror("fchdir to original root");
88
89 ret = chroot(".");
90 if (ret == -1)
91 ksft_perror("chroot to original root");
92
93 umount2(root_mntpoint, MNT_DETACH);
94 rmdir(root_mntpoint);
95}
96
97static void setup_namespace(void)
98{
99 int ret;
100 char buf[32];
101 uid_t uid = getuid();
102 gid_t gid = getgid();
103
104 ret = unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWPID);
105 if (ret == -1)
106 ksft_exit_fail_msg("unsharing mountns and userns: %s\n",
107 strerror(errno));
108
109 sprintf(buf, "0 %d 1", uid);
110 write_file("/proc/self/uid_map", buf);
111 write_file("/proc/self/setgroups", "deny");
112 sprintf(buf, "0 %d 1", gid);
113 write_file("/proc/self/gid_map", buf);
114
115 f_mountinfo = fopen("/proc/self/mountinfo", "re");
116 if (!f_mountinfo)
117 ksft_exit_fail_msg("failed to open mountinfo: %s\n",
118 strerror(errno));
119
120 ret = mount("", "/", NULL, MS_REC|MS_PRIVATE, NULL);
121 if (ret == -1)
122 ksft_exit_fail_msg("making mount tree private: %s\n",
123 strerror(errno));
124
125 if (!mkdtemp(root_mntpoint))
126 ksft_exit_fail_msg("creating temporary directory %s: %s\n",
127 root_mntpoint, strerror(errno));
128
129 old_parent_id = get_mnt_id("parent", root_mntpoint, STATX_MNT_ID);
130 parent_id = get_mnt_id("parent", root_mntpoint, STATX_MNT_ID_UNIQUE);
131
132 orig_root = open("/", O_PATH);
133 if (orig_root == -1)
134 ksft_exit_fail_msg("opening root directory: %s",
135 strerror(errno));
136
137 atexit(cleanup_namespace);
138
139 ret = mount(root_mntpoint, root_mntpoint, NULL, MS_BIND, NULL);
140 if (ret == -1)
141 ksft_exit_fail_msg("mounting temp root %s: %s\n",
142 root_mntpoint, strerror(errno));
143
144 ret = chroot(root_mntpoint);
145 if (ret == -1)
146 ksft_exit_fail_msg("chroot to temp root %s: %s\n",
147 root_mntpoint, strerror(errno));
148
149 ret = chdir("/");
150 if (ret == -1)
151 ksft_exit_fail_msg("chdir to root: %s\n", strerror(errno));
152
153 old_root_id = get_mnt_id("root", "/", STATX_MNT_ID);
154 root_id = get_mnt_id("root", "/", STATX_MNT_ID_UNIQUE);
155}
156
157static int setup_mount_tree(int log2_num)
158{
159 int ret, i;
160
161 ret = mount("", "/", NULL, MS_REC|MS_SHARED, NULL);
162 if (ret == -1) {
163 ksft_test_result_fail("making mount tree shared: %s\n",
164 strerror(errno));
165 return -1;
166 }
167
168 for (i = 0; i < log2_num; i++) {
169 ret = mount("/", "/", NULL, MS_BIND, NULL);
170 if (ret == -1) {
171 ksft_test_result_fail("mounting submount %s: %s\n",
172 root_mntpoint, strerror(errno));
173 return -1;
174 }
175 }
176 return 0;
177}
178
179static void test_listmount_empty_root(void)
180{
181 ssize_t res;
182 const unsigned int size = 32;
183 uint64_t list[size];
184
185 res = listmount(LSMT_ROOT, 0, 0, list, size, 0);
186 if (res == -1) {
187 ksft_test_result_fail("listmount: %s\n", strerror(errno));
188 return;
189 }
190 if (res != 1) {
191 ksft_test_result_fail("listmount result is %zi != 1\n", res);
192 return;
193 }
194
195 if (list[0] != root_id) {
196 ksft_test_result_fail("listmount ID doesn't match 0x%llx != 0x%llx\n",
197 (unsigned long long) list[0],
198 (unsigned long long) root_id);
199 return;
200 }
201
202 ksft_test_result_pass("listmount empty root\n");
203}
204
205static void test_statmount_zero_mask(void)
206{
207 struct statmount sm;
208 int ret;
209
210 ret = statmount(root_id, 0, 0, 0, &sm, sizeof(sm), 0);
211 if (ret == -1) {
212 ksft_test_result_fail("statmount zero mask: %s\n",
213 strerror(errno));
214 return;
215 }
216 if (sm.size != sizeof(sm)) {
217 ksft_test_result_fail("unexpected size: %u != %u\n",
218 sm.size, (uint32_t) sizeof(sm));
219 return;
220 }
221 if (sm.mask != 0) {
222 ksft_test_result_fail("unexpected mask: 0x%llx != 0x0\n",
223 (unsigned long long) sm.mask);
224 return;
225 }
226
227 ksft_test_result_pass("statmount zero mask\n");
228}
229
230static void test_statmount_mnt_basic(void)
231{
232 struct statmount sm;
233 int ret;
234 uint64_t mask = STATMOUNT_MNT_BASIC;
235
236 ret = statmount(root_id, 0, 0, mask, &sm, sizeof(sm), 0);
237 if (ret == -1) {
238 ksft_test_result_fail("statmount mnt basic: %s\n",
239 strerror(errno));
240 return;
241 }
242 if (sm.size != sizeof(sm)) {
243 ksft_test_result_fail("unexpected size: %u != %u\n",
244 sm.size, (uint32_t) sizeof(sm));
245 return;
246 }
247 if (sm.mask != mask) {
248 ksft_test_result_skip("statmount mnt basic unavailable\n");
249 return;
250 }
251
252 if (sm.mnt_id != root_id) {
253 ksft_test_result_fail("unexpected root ID: 0x%llx != 0x%llx\n",
254 (unsigned long long) sm.mnt_id,
255 (unsigned long long) root_id);
256 return;
257 }
258
259 if (sm.mnt_id_old != old_root_id) {
260 ksft_test_result_fail("unexpected old root ID: %u != %u\n",
261 sm.mnt_id_old, old_root_id);
262 return;
263 }
264
265 if (sm.mnt_parent_id != parent_id) {
266 ksft_test_result_fail("unexpected parent ID: 0x%llx != 0x%llx\n",
267 (unsigned long long) sm.mnt_parent_id,
268 (unsigned long long) parent_id);
269 return;
270 }
271
272 if (sm.mnt_parent_id_old != old_parent_id) {
273 ksft_test_result_fail("unexpected old parent ID: %u != %u\n",
274 sm.mnt_parent_id_old, old_parent_id);
275 return;
276 }
277
278 if (sm.mnt_propagation != MS_PRIVATE) {
279 ksft_test_result_fail("unexpected propagation: 0x%llx\n",
280 (unsigned long long) sm.mnt_propagation);
281 return;
282 }
283
284 ksft_test_result_pass("statmount mnt basic\n");
285}
286
287
288static void test_statmount_sb_basic(void)
289{
290 struct statmount sm;
291 int ret;
292 uint64_t mask = STATMOUNT_SB_BASIC;
293 struct statx sx;
294 struct statfs sf;
295
296 ret = statmount(root_id, 0, 0, mask, &sm, sizeof(sm), 0);
297 if (ret == -1) {
298 ksft_test_result_fail("statmount sb basic: %s\n",
299 strerror(errno));
300 return;
301 }
302 if (sm.size != sizeof(sm)) {
303 ksft_test_result_fail("unexpected size: %u != %u\n",
304 sm.size, (uint32_t) sizeof(sm));
305 return;
306 }
307 if (sm.mask != mask) {
308 ksft_test_result_skip("statmount sb basic unavailable\n");
309 return;
310 }
311
312 ret = statx(AT_FDCWD, "/", 0, 0, &sx);
313 if (ret == -1) {
314 ksft_test_result_fail("stat root failed: %s\n",
315 strerror(errno));
316 return;
317 }
318
319 if (sm.sb_dev_major != sx.stx_dev_major ||
320 sm.sb_dev_minor != sx.stx_dev_minor) {
321 ksft_test_result_fail("unexpected sb dev %u:%u != %u:%u\n",
322 sm.sb_dev_major, sm.sb_dev_minor,
323 sx.stx_dev_major, sx.stx_dev_minor);
324 return;
325 }
326
327 ret = statfs("/", &sf);
328 if (ret == -1) {
329 ksft_test_result_fail("statfs root failed: %s\n",
330 strerror(errno));
331 return;
332 }
333
334 if (sm.sb_magic != sf.f_type) {
335 ksft_test_result_fail("unexpected sb magic: 0x%llx != 0x%lx\n",
336 (unsigned long long) sm.sb_magic,
337 sf.f_type);
338 return;
339 }
340
341 ksft_test_result_pass("statmount sb basic\n");
342}
343
344static void test_statmount_mnt_point(void)
345{
346 struct statmount *sm;
347
348 sm = statmount_alloc(root_id, 0, STATMOUNT_MNT_POINT, 0);
349 if (!sm) {
350 ksft_test_result_fail("statmount mount point: %s\n",
351 strerror(errno));
352 return;
353 }
354
355 if (!(sm->mask & STATMOUNT_MNT_POINT)) {
356 ksft_test_result_fail("missing STATMOUNT_MNT_POINT in mask\n");
357 return;
358 }
359 if (strcmp(sm->str + sm->mnt_point, "/") != 0) {
360 ksft_test_result_fail("unexpected mount point: '%s' != '/'\n",
361 sm->str + sm->mnt_point);
362 goto out;
363 }
364 ksft_test_result_pass("statmount mount point\n");
365out:
366 free(sm);
367}
368
369static void test_statmount_mnt_root(void)
370{
371 struct statmount *sm;
372 const char *mnt_root, *last_dir, *last_root;
373
374 last_dir = strrchr(root_mntpoint, '/');
375 assert(last_dir);
376 last_dir++;
377
378 sm = statmount_alloc(root_id, 0, STATMOUNT_MNT_ROOT, 0);
379 if (!sm) {
380 ksft_test_result_fail("statmount mount root: %s\n",
381 strerror(errno));
382 return;
383 }
384 if (!(sm->mask & STATMOUNT_MNT_ROOT)) {
385 ksft_test_result_fail("missing STATMOUNT_MNT_ROOT in mask\n");
386 return;
387 }
388 mnt_root = sm->str + sm->mnt_root;
389 last_root = strrchr(mnt_root, '/');
390 if (last_root)
391 last_root++;
392 else
393 last_root = mnt_root;
394
395 if (strcmp(last_dir, last_root) != 0) {
396 ksft_test_result_fail("unexpected mount root last component: '%s' != '%s'\n",
397 last_root, last_dir);
398 goto out;
399 }
400 ksft_test_result_pass("statmount mount root\n");
401out:
402 free(sm);
403}
404
405static void test_statmount_fs_type(void)
406{
407 struct statmount *sm;
408 const char *fs_type;
409 const char *const *s;
410
411 sm = statmount_alloc(root_id, 0, STATMOUNT_FS_TYPE, 0);
412 if (!sm) {
413 ksft_test_result_fail("statmount fs type: %s\n",
414 strerror(errno));
415 return;
416 }
417 if (!(sm->mask & STATMOUNT_FS_TYPE)) {
418 ksft_test_result_fail("missing STATMOUNT_FS_TYPE in mask\n");
419 return;
420 }
421 fs_type = sm->str + sm->fs_type;
422 for (s = known_fs; s != NULL; s++) {
423 if (strcmp(fs_type, *s) == 0)
424 break;
425 }
426 if (!s)
427 ksft_print_msg("unknown filesystem type: %s\n", fs_type);
428
429 ksft_test_result_pass("statmount fs type\n");
430 free(sm);
431}
432
433static void test_statmount_mnt_opts(void)
434{
435 struct statmount *sm;
436 const char *statmount_opts;
437 char *line = NULL;
438 size_t len = 0;
439
440 sm = statmount_alloc(root_id, 0, STATMOUNT_MNT_BASIC | STATMOUNT_MNT_OPTS,
441 0);
442 if (!sm) {
443 ksft_test_result_fail("statmount mnt opts: %s\n",
444 strerror(errno));
445 return;
446 }
447
448 if (!(sm->mask & STATMOUNT_MNT_BASIC)) {
449 ksft_test_result_fail("missing STATMOUNT_MNT_BASIC in mask\n");
450 return;
451 }
452
453 while (getline(&line, &len, f_mountinfo) != -1) {
454 int i;
455 char *p, *p2;
456 unsigned int old_mnt_id;
457
458 old_mnt_id = atoi(line);
459 if (old_mnt_id != sm->mnt_id_old)
460 continue;
461
462 for (p = line, i = 0; p && i < 5; i++)
463 p = strchr(p + 1, ' ');
464 if (!p)
465 continue;
466
467 p2 = strchr(p + 1, ' ');
468 if (!p2)
469 continue;
470 *p2 = '\0';
471 p = strchr(p2 + 1, '-');
472 if (!p)
473 continue;
474 for (p++, i = 0; p && i < 2; i++)
475 p = strchr(p + 1, ' ');
476 if (!p)
477 continue;
478 p++;
479
480 /* skip generic superblock options */
481 if (strncmp(p, "ro", 2) == 0)
482 p += 2;
483 else if (strncmp(p, "rw", 2) == 0)
484 p += 2;
485 if (*p == ',')
486 p++;
487 if (strncmp(p, "sync", 4) == 0)
488 p += 4;
489 if (*p == ',')
490 p++;
491 if (strncmp(p, "dirsync", 7) == 0)
492 p += 7;
493 if (*p == ',')
494 p++;
495 if (strncmp(p, "lazytime", 8) == 0)
496 p += 8;
497 if (*p == ',')
498 p++;
499 p2 = strrchr(p, '\n');
500 if (p2)
501 *p2 = '\0';
502
503 if (sm->mask & STATMOUNT_MNT_OPTS)
504 statmount_opts = sm->str + sm->mnt_opts;
505 else
506 statmount_opts = "";
507 if (strcmp(statmount_opts, p) != 0)
508 ksft_test_result_fail(
509 "unexpected mount options: '%s' != '%s'\n",
510 statmount_opts, p);
511 else
512 ksft_test_result_pass("statmount mount options\n");
513 free(sm);
514 free(line);
515 return;
516 }
517
518 ksft_test_result_fail("didnt't find mount entry\n");
519 free(sm);
520 free(line);
521}
522
523static void test_statmount_string(uint64_t mask, size_t off, const char *name)
524{
525 struct statmount *sm;
526 size_t len, shortsize, exactsize;
527 uint32_t start, i;
528 int ret;
529
530 sm = statmount_alloc(root_id, 0, mask, 0);
531 if (!sm) {
532 ksft_test_result_fail("statmount %s: %s\n", name,
533 strerror(errno));
534 goto out;
535 }
536 if (sm->size < sizeof(*sm)) {
537 ksft_test_result_fail("unexpected size: %u < %u\n",
538 sm->size, (uint32_t) sizeof(*sm));
539 goto out;
540 }
541 if (sm->mask != mask) {
542 ksft_test_result_skip("statmount %s unavailable\n", name);
543 goto out;
544 }
545 len = sm->size - sizeof(*sm);
546 start = ((uint32_t *) sm)[off];
547
548 for (i = start;; i++) {
549 if (i >= len) {
550 ksft_test_result_fail("string out of bounds\n");
551 goto out;
552 }
553 if (!sm->str[i])
554 break;
555 }
556 exactsize = sm->size;
557 shortsize = sizeof(*sm) + i;
558
559 ret = statmount(root_id, 0, 0, mask, sm, exactsize, 0);
560 if (ret == -1) {
561 ksft_test_result_fail("statmount exact size: %s\n",
562 strerror(errno));
563 goto out;
564 }
565 errno = 0;
566 ret = statmount(root_id, 0, 0, mask, sm, shortsize, 0);
567 if (ret != -1 || errno != EOVERFLOW) {
568 ksft_test_result_fail("should have failed with EOVERFLOW: %s\n",
569 strerror(errno));
570 goto out;
571 }
572
573 ksft_test_result_pass("statmount string %s\n", name);
574out:
575 free(sm);
576}
577
578static void test_listmount_tree(void)
579{
580 ssize_t res;
581 const unsigned int log2_num = 4;
582 const unsigned int step = 3;
583 const unsigned int size = (1 << log2_num) + step + 1;
584 size_t num, expect = 1 << log2_num;
585 uint64_t list[size];
586 uint64_t list2[size];
587 size_t i;
588
589
590 res = setup_mount_tree(log2_num);
591 if (res == -1)
592 return;
593
594 num = res = listmount(LSMT_ROOT, 0, 0, list, size, 0);
595 if (res == -1) {
596 ksft_test_result_fail("listmount: %s\n", strerror(errno));
597 return;
598 }
599 if (num != expect) {
600 ksft_test_result_fail("listmount result is %zi != %zi\n",
601 res, expect);
602 return;
603 }
604
605 for (i = 0; i < size - step;) {
606 res = listmount(LSMT_ROOT, 0, i ? list2[i - 1] : 0, list2 + i, step, 0);
607 if (res == -1)
608 ksft_test_result_fail("short listmount: %s\n",
609 strerror(errno));
610 i += res;
611 if (res < step)
612 break;
613 }
614 if (i != num) {
615 ksft_test_result_fail("different number of entries: %zu != %zu\n",
616 i, num);
617 return;
618 }
619 for (i = 0; i < num; i++) {
620 if (list2[i] != list[i]) {
621 ksft_test_result_fail("different value for entry %zu: 0x%llx != 0x%llx\n",
622 i,
623 (unsigned long long) list2[i],
624 (unsigned long long) list[i]);
625 }
626 }
627
628 ksft_test_result_pass("listmount tree\n");
629}
630
631static void test_statmount_by_fd(void)
632{
633 struct statmount *sm = NULL;
634 char tmpdir[] = "/statmount.fd.XXXXXX";
635 const char root[] = "/test";
636 char subdir[PATH_MAX], tmproot[PATH_MAX];
637 int fd;
638
639 if (!mkdtemp(tmpdir)) {
640 ksft_perror("mkdtemp");
641 return;
642 }
643
644 if (mount("statmount.test", tmpdir, "tmpfs", 0, NULL)) {
645 ksft_perror("mount");
646 rmdir(tmpdir);
647 return;
648 }
649
650 snprintf(subdir, PATH_MAX, "%s%s", tmpdir, root);
651 snprintf(tmproot, PATH_MAX, "%s/%s", tmpdir, "chroot");
652
653 if (mkdir(subdir, 0755)) {
654 ksft_perror("mkdir");
655 goto err_tmpdir;
656 }
657
658 if (mount(subdir, subdir, NULL, MS_BIND, 0)) {
659 ksft_perror("mount");
660 goto err_subdir;
661 }
662
663 if (mkdir(tmproot, 0755)) {
664 ksft_perror("mkdir");
665 goto err_subdir;
666 }
667
668 fd = open(subdir, O_PATH);
669 if (fd < 0) {
670 ksft_perror("open");
671 goto err_tmproot;
672 }
673
674 if (chroot(tmproot)) {
675 ksft_perror("chroot");
676 goto err_fd;
677 }
678
679 sm = statmount_alloc_by_fd(fd, STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT);
680 if (!sm) {
681 ksft_test_result_fail("statmount by fd failed: %s\n", strerror(errno));
682 goto err_chroot;
683 }
684
685 if (sm->size < sizeof(*sm)) {
686 ksft_test_result_fail("unexpected size: %u < %u\n",
687 sm->size, (uint32_t) sizeof(*sm));
688 goto err_chroot;
689 }
690
691 if (sm->mask & STATMOUNT_MNT_POINT) {
692 ksft_test_result_fail("STATMOUNT_MNT_POINT unexpectedly set in statmount\n");
693 goto err_chroot;
694 }
695
696 if (!(sm->mask & STATMOUNT_MNT_ROOT)) {
697 ksft_test_result_fail("STATMOUNT_MNT_ROOT not set in statmount\n");
698 goto err_chroot;
699 }
700
701 if (strcmp(root, sm->str + sm->mnt_root) != 0) {
702 ksft_test_result_fail("statmount returned incorrect mnt_root,"
703 "statmount mnt_root: %s != %s\n",
704 sm->str + sm->mnt_root, root);
705 goto err_chroot;
706 }
707
708 if (chroot(".")) {
709 ksft_perror("chroot");
710 goto out;
711 }
712
713 free(sm);
714 sm = statmount_alloc_by_fd(fd, STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT);
715 if (!sm) {
716 ksft_test_result_fail("statmount by fd failed: %s\n", strerror(errno));
717 goto err_fd;
718 }
719
720 if (sm->size < sizeof(*sm)) {
721 ksft_test_result_fail("unexpected size: %u < %u\n",
722 sm->size, (uint32_t) sizeof(*sm));
723 goto out;
724 }
725
726 if (!(sm->mask & STATMOUNT_MNT_POINT)) {
727 ksft_test_result_fail("STATMOUNT_MNT_POINT not set in statmount\n");
728 goto out;
729 }
730
731 if (!(sm->mask & STATMOUNT_MNT_ROOT)) {
732 ksft_test_result_fail("STATMOUNT_MNT_ROOT not set in statmount\n");
733 goto out;
734 }
735
736 if (strcmp(subdir, sm->str + sm->mnt_point) != 0) {
737 ksft_test_result_fail("statmount returned incorrect mnt_point,"
738 "statmount mnt_point: %s != %s\n", sm->str + sm->mnt_point, subdir);
739 goto out;
740 }
741
742 if (strcmp(root, sm->str + sm->mnt_root) != 0) {
743 ksft_test_result_fail("statmount returned incorrect mnt_root,"
744 "statmount mnt_root: %s != %s\n", sm->str + sm->mnt_root, root);
745 goto out;
746 }
747
748 ksft_test_result_pass("statmount by fd\n");
749 goto out;
750err_chroot:
751 chroot(".");
752out:
753 free(sm);
754err_fd:
755 close(fd);
756err_tmproot:
757 rmdir(tmproot);
758err_subdir:
759 umount2(subdir, MNT_DETACH);
760 rmdir(subdir);
761err_tmpdir:
762 umount2(tmpdir, MNT_DETACH);
763 rmdir(tmpdir);
764}
765
766static void test_statmount_by_fd_unmounted(void)
767{
768 const char root[] = "/test.unmounted";
769 char tmpdir[] = "/statmount.fd.XXXXXX";
770 char subdir[PATH_MAX];
771 int fd;
772 struct statmount *sm = NULL;
773
774 if (!mkdtemp(tmpdir)) {
775 ksft_perror("mkdtemp");
776 return;
777 }
778
779 if (mount("statmount.test", tmpdir, "tmpfs", 0, NULL)) {
780 ksft_perror("mount");
781 rmdir(tmpdir);
782 return;
783 }
784
785 snprintf(subdir, PATH_MAX, "%s%s", tmpdir, root);
786
787 if (mkdir(subdir, 0755)) {
788 ksft_perror("mkdir");
789 goto err_tmpdir;
790 }
791
792 if (mount(subdir, subdir, 0, MS_BIND, NULL)) {
793 ksft_perror("mount");
794 goto err_subdir;
795 }
796
797 fd = open(subdir, O_PATH);
798 if (fd < 0) {
799 ksft_perror("open");
800 goto err_subdir;
801 }
802
803 if (umount2(tmpdir, MNT_DETACH)) {
804 ksft_perror("umount2");
805 goto err_fd;
806 }
807
808 sm = statmount_alloc_by_fd(fd, STATMOUNT_MNT_POINT | STATMOUNT_MNT_ROOT);
809 if (!sm) {
810 ksft_test_result_fail("statmount by fd unmounted: %s\n",
811 strerror(errno));
812 goto err_sm;
813 }
814
815 if (sm->size < sizeof(*sm)) {
816 ksft_test_result_fail("unexpected size: %u < %u\n",
817 sm->size, (uint32_t) sizeof(*sm));
818 goto err_sm;
819 }
820
821 if (sm->mask & STATMOUNT_MNT_POINT) {
822 ksft_test_result_fail("STATMOUNT_MNT_POINT unexpectedly set in mask\n");
823 goto err_sm;
824 }
825
826 if (!(sm->mask & STATMOUNT_MNT_ROOT)) {
827 ksft_test_result_fail("STATMOUNT_MNT_ROOT not set in mask\n");
828 goto err_sm;
829 }
830
831 if (strcmp(sm->str + sm->mnt_root, root) != 0) {
832 ksft_test_result_fail("statmount returned incorrect mnt_root,"
833 "statmount mnt_root: %s != %s\n",
834 sm->str + sm->mnt_root, root);
835 goto err_sm;
836 }
837
838 ksft_test_result_pass("statmount by fd on unmounted mount\n");
839err_sm:
840 free(sm);
841err_fd:
842 close(fd);
843err_subdir:
844 umount2(subdir, MNT_DETACH);
845 rmdir(subdir);
846err_tmpdir:
847 umount2(tmpdir, MNT_DETACH);
848 rmdir(tmpdir);
849}
850
851#define str_off(memb) (offsetof(struct statmount, memb) / sizeof(uint32_t))
852
853int main(void)
854{
855 int ret;
856 uint64_t all_mask = STATMOUNT_SB_BASIC | STATMOUNT_MNT_BASIC |
857 STATMOUNT_PROPAGATE_FROM | STATMOUNT_MNT_ROOT |
858 STATMOUNT_MNT_POINT | STATMOUNT_FS_TYPE | STATMOUNT_MNT_NS_ID;
859
860 ksft_print_header();
861
862 ret = statmount(0, 0, 0, 0, NULL, 0, 0);
863 assert(ret == -1);
864 if (errno == ENOSYS)
865 ksft_exit_skip("statmount() syscall not supported\n");
866
867 setup_namespace();
868
869 ksft_set_plan(17);
870 test_listmount_empty_root();
871 test_statmount_zero_mask();
872 test_statmount_mnt_basic();
873 test_statmount_sb_basic();
874 test_statmount_mnt_root();
875 test_statmount_mnt_point();
876 test_statmount_fs_type();
877 test_statmount_mnt_opts();
878 test_statmount_string(STATMOUNT_MNT_ROOT, str_off(mnt_root), "mount root");
879 test_statmount_string(STATMOUNT_MNT_POINT, str_off(mnt_point), "mount point");
880 test_statmount_string(STATMOUNT_FS_TYPE, str_off(fs_type), "fs type");
881 test_statmount_string(all_mask, str_off(mnt_root), "mount root & all");
882 test_statmount_string(all_mask, str_off(mnt_point), "mount point & all");
883 test_statmount_string(all_mask, str_off(fs_type), "fs type & all");
884
885 test_listmount_tree();
886 test_statmount_by_fd_unmounted();
887 test_statmount_by_fd();
888
889
890 if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
891 ksft_exit_fail();
892 else
893 ksft_exit_pass();
894}