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