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", "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
statmount_alloc(uint64_t mnt_id,int fd,uint64_t mask,unsigned int flags)36 static struct statmount *statmount_alloc(uint64_t mnt_id, int fd, uint64_t mask, unsigned int flags)
37 {
38 size_t bufsize = 1 << 15;
39 struct statmount *buf = NULL, *tmp = NULL;
40 int tofree = 0;
41 int ret;
42
43 if (flags & STATMOUNT_BY_FD && fd < 0)
44 return NULL;
45
46 tmp = alloca(bufsize);
47
48 for (;;) {
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
54 if (ret != -1)
55 break;
56 if (tofree)
57 free(tmp);
58 if (errno != EOVERFLOW)
59 return NULL;
60 bufsize <<= 1;
61 tofree = 1;
62 tmp = malloc(bufsize);
63 if (!tmp)
64 return NULL;
65 }
66 buf = malloc(tmp->size);
67 if (buf)
68 memcpy(buf, tmp, tmp->size);
69 if (tofree)
70 free(tmp);
71
72 return buf;
73 }
74
write_file(const char * path,const char * val)75 static void write_file(const char *path, const char *val)
76 {
77 int fd = open(path, O_WRONLY);
78 size_t len = strlen(val);
79 int ret;
80
81 if (fd == -1)
82 ksft_exit_fail_msg("opening %s for write: %s\n", path, strerror(errno));
83
84 ret = write(fd, val, len);
85 if (ret == -1)
86 ksft_exit_fail_msg("writing to %s: %s\n", path, strerror(errno));
87 if (ret != len)
88 ksft_exit_fail_msg("short write to %s\n", path);
89
90 ret = close(fd);
91 if (ret == -1)
92 ksft_exit_fail_msg("closing %s\n", path);
93 }
94
get_mnt_id(const char * name,const char * path,uint64_t mask)95 static uint64_t get_mnt_id(const char *name, const char *path, uint64_t mask)
96 {
97 struct statx sx;
98 int ret;
99
100 ret = statx(AT_FDCWD, path, 0, mask, &sx);
101 if (ret == -1)
102 ksft_exit_fail_msg("retrieving %s mount ID for %s: %s\n",
103 mask & STATX_MNT_ID_UNIQUE ? "unique" : "old",
104 name, strerror(errno));
105 if (!(sx.stx_mask & mask))
106 ksft_exit_fail_msg("no %s mount ID available for %s\n",
107 mask & STATX_MNT_ID_UNIQUE ? "unique" : "old",
108 name);
109
110 return sx.stx_mnt_id;
111 }
112
113
114 static char root_mntpoint[] = "/tmp/statmount_test_root.XXXXXX";
115 static int orig_root;
116 static uint64_t root_id, parent_id;
117 static uint32_t old_root_id, old_parent_id;
118 static FILE *f_mountinfo;
119
cleanup_namespace(void)120 static void cleanup_namespace(void)
121 {
122 int ret;
123
124 ret = fchdir(orig_root);
125 if (ret == -1)
126 ksft_perror("fchdir to original root");
127
128 ret = chroot(".");
129 if (ret == -1)
130 ksft_perror("chroot to original root");
131
132 umount2(root_mntpoint, MNT_DETACH);
133 rmdir(root_mntpoint);
134 }
135
setup_namespace(void)136 static void setup_namespace(void)
137 {
138 int ret;
139 char buf[32];
140 uid_t uid = getuid();
141 gid_t gid = getgid();
142
143 ret = unshare(CLONE_NEWNS|CLONE_NEWUSER|CLONE_NEWPID);
144 if (ret == -1)
145 ksft_exit_fail_msg("unsharing mountns and userns: %s\n",
146 strerror(errno));
147
148 sprintf(buf, "0 %d 1", uid);
149 write_file("/proc/self/uid_map", buf);
150 write_file("/proc/self/setgroups", "deny");
151 sprintf(buf, "0 %d 1", gid);
152 write_file("/proc/self/gid_map", buf);
153
154 f_mountinfo = fopen("/proc/self/mountinfo", "re");
155 if (!f_mountinfo)
156 ksft_exit_fail_msg("failed to open mountinfo: %s\n",
157 strerror(errno));
158
159 ret = mount("", "/", NULL, MS_REC|MS_PRIVATE, NULL);
160 if (ret == -1)
161 ksft_exit_fail_msg("making mount tree private: %s\n",
162 strerror(errno));
163
164 if (!mkdtemp(root_mntpoint))
165 ksft_exit_fail_msg("creating temporary directory %s: %s\n",
166 root_mntpoint, strerror(errno));
167
168 old_parent_id = get_mnt_id("parent", root_mntpoint, STATX_MNT_ID);
169 parent_id = get_mnt_id("parent", root_mntpoint, STATX_MNT_ID_UNIQUE);
170
171 orig_root = open("/", O_PATH);
172 if (orig_root == -1)
173 ksft_exit_fail_msg("opening root directory: %s",
174 strerror(errno));
175
176 atexit(cleanup_namespace);
177
178 ret = mount(root_mntpoint, root_mntpoint, NULL, MS_BIND, NULL);
179 if (ret == -1)
180 ksft_exit_fail_msg("mounting temp root %s: %s\n",
181 root_mntpoint, strerror(errno));
182
183 ret = chroot(root_mntpoint);
184 if (ret == -1)
185 ksft_exit_fail_msg("chroot to temp root %s: %s\n",
186 root_mntpoint, strerror(errno));
187
188 ret = chdir("/");
189 if (ret == -1)
190 ksft_exit_fail_msg("chdir to root: %s\n", strerror(errno));
191
192 old_root_id = get_mnt_id("root", "/", STATX_MNT_ID);
193 root_id = get_mnt_id("root", "/", STATX_MNT_ID_UNIQUE);
194 }
195
setup_mount_tree(int log2_num)196 static int setup_mount_tree(int log2_num)
197 {
198 int ret, i;
199
200 ret = mount("", "/", NULL, MS_REC|MS_SHARED, NULL);
201 if (ret == -1) {
202 ksft_test_result_fail("making mount tree shared: %s\n",
203 strerror(errno));
204 return -1;
205 }
206
207 for (i = 0; i < log2_num; i++) {
208 ret = mount("/", "/", NULL, MS_BIND, NULL);
209 if (ret == -1) {
210 ksft_test_result_fail("mounting submount %s: %s\n",
211 root_mntpoint, strerror(errno));
212 return -1;
213 }
214 }
215 return 0;
216 }
217
test_listmount_empty_root(void)218 static void test_listmount_empty_root(void)
219 {
220 ssize_t res;
221 const unsigned int size = 32;
222 uint64_t list[size];
223
224 res = listmount(LSMT_ROOT, 0, 0, list, size, 0);
225 if (res == -1) {
226 ksft_test_result_fail("listmount: %s\n", strerror(errno));
227 return;
228 }
229 if (res != 1) {
230 ksft_test_result_fail("listmount result is %zi != 1\n", res);
231 return;
232 }
233
234 if (list[0] != root_id) {
235 ksft_test_result_fail("listmount ID doesn't match 0x%llx != 0x%llx\n",
236 (unsigned long long) list[0],
237 (unsigned long long) root_id);
238 return;
239 }
240
241 ksft_test_result_pass("listmount empty root\n");
242 }
243
test_statmount_zero_mask(void)244 static void test_statmount_zero_mask(void)
245 {
246 struct statmount sm;
247 int ret;
248
249 ret = statmount(root_id, 0, 0, 0, &sm, sizeof(sm), 0);
250 if (ret == -1) {
251 ksft_test_result_fail("statmount zero mask: %s\n",
252 strerror(errno));
253 return;
254 }
255 if (sm.size != sizeof(sm)) {
256 ksft_test_result_fail("unexpected size: %u != %u\n",
257 sm.size, (uint32_t) sizeof(sm));
258 return;
259 }
260 if (sm.mask != 0) {
261 ksft_test_result_fail("unexpected mask: 0x%llx != 0x0\n",
262 (unsigned long long) sm.mask);
263 return;
264 }
265
266 ksft_test_result_pass("statmount zero mask\n");
267 }
268
test_statmount_mnt_basic(void)269 static void test_statmount_mnt_basic(void)
270 {
271 struct statmount sm;
272 int ret;
273 uint64_t mask = STATMOUNT_MNT_BASIC;
274
275 ret = statmount(root_id, 0, 0, mask, &sm, sizeof(sm), 0);
276 if (ret == -1) {
277 ksft_test_result_fail("statmount mnt basic: %s\n",
278 strerror(errno));
279 return;
280 }
281 if (sm.size != sizeof(sm)) {
282 ksft_test_result_fail("unexpected size: %u != %u\n",
283 sm.size, (uint32_t) sizeof(sm));
284 return;
285 }
286 if (sm.mask != mask) {
287 ksft_test_result_skip("statmount mnt basic unavailable\n");
288 return;
289 }
290
291 if (sm.mnt_id != root_id) {
292 ksft_test_result_fail("unexpected root ID: 0x%llx != 0x%llx\n",
293 (unsigned long long) sm.mnt_id,
294 (unsigned long long) root_id);
295 return;
296 }
297
298 if (sm.mnt_id_old != old_root_id) {
299 ksft_test_result_fail("unexpected old root ID: %u != %u\n",
300 sm.mnt_id_old, old_root_id);
301 return;
302 }
303
304 if (sm.mnt_parent_id != parent_id) {
305 ksft_test_result_fail("unexpected parent ID: 0x%llx != 0x%llx\n",
306 (unsigned long long) sm.mnt_parent_id,
307 (unsigned long long) parent_id);
308 return;
309 }
310
311 if (sm.mnt_parent_id_old != old_parent_id) {
312 ksft_test_result_fail("unexpected old parent ID: %u != %u\n",
313 sm.mnt_parent_id_old, old_parent_id);
314 return;
315 }
316
317 if (sm.mnt_propagation != MS_PRIVATE) {
318 ksft_test_result_fail("unexpected propagation: 0x%llx\n",
319 (unsigned long long) sm.mnt_propagation);
320 return;
321 }
322
323 ksft_test_result_pass("statmount mnt basic\n");
324 }
325
326
test_statmount_sb_basic(void)327 static void test_statmount_sb_basic(void)
328 {
329 struct statmount sm;
330 int ret;
331 uint64_t mask = STATMOUNT_SB_BASIC;
332 struct statx sx;
333 struct statfs sf;
334
335 ret = statmount(root_id, 0, 0, mask, &sm, sizeof(sm), 0);
336 if (ret == -1) {
337 ksft_test_result_fail("statmount sb basic: %s\n",
338 strerror(errno));
339 return;
340 }
341 if (sm.size != sizeof(sm)) {
342 ksft_test_result_fail("unexpected size: %u != %u\n",
343 sm.size, (uint32_t) sizeof(sm));
344 return;
345 }
346 if (sm.mask != mask) {
347 ksft_test_result_skip("statmount sb basic unavailable\n");
348 return;
349 }
350
351 ret = statx(AT_FDCWD, "/", 0, 0, &sx);
352 if (ret == -1) {
353 ksft_test_result_fail("stat root failed: %s\n",
354 strerror(errno));
355 return;
356 }
357
358 if (sm.sb_dev_major != sx.stx_dev_major ||
359 sm.sb_dev_minor != sx.stx_dev_minor) {
360 ksft_test_result_fail("unexpected sb dev %u:%u != %u:%u\n",
361 sm.sb_dev_major, sm.sb_dev_minor,
362 sx.stx_dev_major, sx.stx_dev_minor);
363 return;
364 }
365
366 ret = statfs("/", &sf);
367 if (ret == -1) {
368 ksft_test_result_fail("statfs root failed: %s\n",
369 strerror(errno));
370 return;
371 }
372
373 if (sm.sb_magic != sf.f_type) {
374 ksft_test_result_fail("unexpected sb magic: 0x%llx != 0x%lx\n",
375 (unsigned long long) sm.sb_magic,
376 sf.f_type);
377 return;
378 }
379
380 ksft_test_result_pass("statmount sb basic\n");
381 }
382
test_statmount_mnt_point(void)383 static void test_statmount_mnt_point(void)
384 {
385 struct statmount *sm;
386
387 sm = statmount_alloc(root_id, 0, STATMOUNT_MNT_POINT, 0);
388 if (!sm) {
389 ksft_test_result_fail("statmount mount point: %s\n",
390 strerror(errno));
391 return;
392 }
393
394 if (!(sm->mask & STATMOUNT_MNT_POINT)) {
395 ksft_test_result_fail("missing STATMOUNT_MNT_POINT in mask\n");
396 return;
397 }
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);
401 goto out;
402 }
403 ksft_test_result_pass("statmount mount point\n");
404 out:
405 free(sm);
406 }
407
test_statmount_mnt_root(void)408 static void test_statmount_mnt_root(void)
409 {
410 struct statmount *sm;
411 const char *mnt_root, *last_dir, *last_root;
412
413 last_dir = strrchr(root_mntpoint, '/');
414 assert(last_dir);
415 last_dir++;
416
417 sm = statmount_alloc(root_id, 0, STATMOUNT_MNT_ROOT, 0);
418 if (!sm) {
419 ksft_test_result_fail("statmount mount root: %s\n",
420 strerror(errno));
421 return;
422 }
423 if (!(sm->mask & STATMOUNT_MNT_ROOT)) {
424 ksft_test_result_fail("missing STATMOUNT_MNT_ROOT in mask\n");
425 return;
426 }
427 mnt_root = sm->str + sm->mnt_root;
428 last_root = strrchr(mnt_root, '/');
429 if (last_root)
430 last_root++;
431 else
432 last_root = mnt_root;
433
434 if (strcmp(last_dir, last_root) != 0) {
435 ksft_test_result_fail("unexpected mount root last component: '%s' != '%s'\n",
436 last_root, last_dir);
437 goto out;
438 }
439 ksft_test_result_pass("statmount mount root\n");
440 out:
441 free(sm);
442 }
443
test_statmount_fs_type(void)444 static void test_statmount_fs_type(void)
445 {
446 struct statmount *sm;
447 const char *fs_type;
448 const char *const *s;
449
450 sm = statmount_alloc(root_id, 0, STATMOUNT_FS_TYPE, 0);
451 if (!sm) {
452 ksft_test_result_fail("statmount fs type: %s\n",
453 strerror(errno));
454 return;
455 }
456 if (!(sm->mask & STATMOUNT_FS_TYPE)) {
457 ksft_test_result_fail("missing STATMOUNT_FS_TYPE in mask\n");
458 return;
459 }
460 fs_type = sm->str + sm->fs_type;
461 for (s = known_fs; s != NULL; s++) {
462 if (strcmp(fs_type, *s) == 0)
463 break;
464 }
465 if (!s)
466 ksft_print_msg("unknown filesystem type: %s\n", fs_type);
467
468 ksft_test_result_pass("statmount fs type\n");
469 free(sm);
470 }
471
test_statmount_mnt_opts(void)472 static void test_statmount_mnt_opts(void)
473 {
474 struct statmount *sm;
475 const char *statmount_opts;
476 char *line = NULL;
477 size_t len = 0;
478
479 sm = statmount_alloc(root_id, 0, STATMOUNT_MNT_BASIC | STATMOUNT_MNT_OPTS,
480 0);
481 if (!sm) {
482 ksft_test_result_fail("statmount mnt opts: %s\n",
483 strerror(errno));
484 return;
485 }
486
487 if (!(sm->mask & STATMOUNT_MNT_BASIC)) {
488 ksft_test_result_fail("missing STATMOUNT_MNT_BASIC in mask\n");
489 return;
490 }
491
492 while (getline(&line, &len, f_mountinfo) != -1) {
493 int i;
494 char *p, *p2;
495 unsigned int old_mnt_id;
496
497 old_mnt_id = atoi(line);
498 if (old_mnt_id != sm->mnt_id_old)
499 continue;
500
501 for (p = line, i = 0; p && i < 5; i++)
502 p = strchr(p + 1, ' ');
503 if (!p)
504 continue;
505
506 p2 = strchr(p + 1, ' ');
507 if (!p2)
508 continue;
509 *p2 = '\0';
510 p = strchr(p2 + 1, '-');
511 if (!p)
512 continue;
513 for (p++, i = 0; p && i < 2; i++)
514 p = strchr(p + 1, ' ');
515 if (!p)
516 continue;
517 p++;
518
519 /* skip generic superblock options */
520 if (strncmp(p, "ro", 2) == 0)
521 p += 2;
522 else if (strncmp(p, "rw", 2) == 0)
523 p += 2;
524 if (*p == ',')
525 p++;
526 if (strncmp(p, "sync", 4) == 0)
527 p += 4;
528 if (*p == ',')
529 p++;
530 if (strncmp(p, "dirsync", 7) == 0)
531 p += 7;
532 if (*p == ',')
533 p++;
534 if (strncmp(p, "lazytime", 8) == 0)
535 p += 8;
536 if (*p == ',')
537 p++;
538 p2 = strrchr(p, '\n');
539 if (p2)
540 *p2 = '\0';
541
542 if (sm->mask & STATMOUNT_MNT_OPTS)
543 statmount_opts = sm->str + sm->mnt_opts;
544 else
545 statmount_opts = "";
546 if (strcmp(statmount_opts, p) != 0)
547 ksft_test_result_fail(
548 "unexpected mount options: '%s' != '%s'\n",
549 statmount_opts, p);
550 else
551 ksft_test_result_pass("statmount mount options\n");
552 free(sm);
553 free(line);
554 return;
555 }
556
557 ksft_test_result_fail("didnt't find mount entry\n");
558 free(sm);
559 free(line);
560 }
561
test_statmount_string(uint64_t mask,size_t off,const char * name)562 static void test_statmount_string(uint64_t mask, size_t off, const char *name)
563 {
564 struct statmount *sm;
565 size_t len, shortsize, exactsize;
566 uint32_t start, i;
567 int ret;
568
569 sm = statmount_alloc(root_id, 0, mask, 0);
570 if (!sm) {
571 ksft_test_result_fail("statmount %s: %s\n", name,
572 strerror(errno));
573 goto out;
574 }
575 if (sm->size < sizeof(*sm)) {
576 ksft_test_result_fail("unexpected size: %u < %u\n",
577 sm->size, (uint32_t) sizeof(*sm));
578 goto out;
579 }
580 if (sm->mask != mask) {
581 ksft_test_result_skip("statmount %s unavailable\n", name);
582 goto out;
583 }
584 len = sm->size - sizeof(*sm);
585 start = ((uint32_t *) sm)[off];
586
587 for (i = start;; i++) {
588 if (i >= len) {
589 ksft_test_result_fail("string out of bounds\n");
590 goto out;
591 }
592 if (!sm->str[i])
593 break;
594 }
595 exactsize = sm->size;
596 shortsize = sizeof(*sm) + i;
597
598 ret = statmount(root_id, 0, 0, mask, sm, exactsize, 0);
599 if (ret == -1) {
600 ksft_test_result_fail("statmount exact size: %s\n",
601 strerror(errno));
602 goto out;
603 }
604 errno = 0;
605 ret = statmount(root_id, 0, 0, mask, sm, shortsize, 0);
606 if (ret != -1 || errno != EOVERFLOW) {
607 ksft_test_result_fail("should have failed with EOVERFLOW: %s\n",
608 strerror(errno));
609 goto out;
610 }
611
612 ksft_test_result_pass("statmount string %s\n", name);
613 out:
614 free(sm);
615 }
616
test_listmount_tree(void)617 static void test_listmount_tree(void)
618 {
619 ssize_t res;
620 const unsigned int log2_num = 4;
621 const unsigned int step = 3;
622 const unsigned int size = (1 << log2_num) + step + 1;
623 size_t num, expect = 1 << log2_num;
624 uint64_t list[size];
625 uint64_t list2[size];
626 size_t i;
627
628
629 res = setup_mount_tree(log2_num);
630 if (res == -1)
631 return;
632
633 num = res = listmount(LSMT_ROOT, 0, 0, list, size, 0);
634 if (res == -1) {
635 ksft_test_result_fail("listmount: %s\n", strerror(errno));
636 return;
637 }
638 if (num != expect) {
639 ksft_test_result_fail("listmount result is %zi != %zi\n",
640 res, expect);
641 return;
642 }
643
644 for (i = 0; i < size - step;) {
645 res = listmount(LSMT_ROOT, 0, i ? list2[i - 1] : 0, list2 + i, step, 0);
646 if (res == -1)
647 ksft_test_result_fail("short listmount: %s\n",
648 strerror(errno));
649 i += res;
650 if (res < step)
651 break;
652 }
653 if (i != num) {
654 ksft_test_result_fail("different number of entries: %zu != %zu\n",
655 i, num);
656 return;
657 }
658 for (i = 0; i < num; i++) {
659 if (list2[i] != list[i]) {
660 ksft_test_result_fail("different value for entry %zu: 0x%llx != 0x%llx\n",
661 i,
662 (unsigned long long) list2[i],
663 (unsigned long long) list[i]);
664 }
665 }
666
667 ksft_test_result_pass("listmount tree\n");
668 }
669
test_statmount_by_fd(void)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
test_statmount_by_fd_unmounted(void)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
890 #define str_off(memb) (offsetof(struct statmount, memb) / sizeof(uint32_t))
891
main(void)892 int main(void)
893 {
894 int ret;
895 uint64_t all_mask = STATMOUNT_SB_BASIC | STATMOUNT_MNT_BASIC |
896 STATMOUNT_PROPAGATE_FROM | STATMOUNT_MNT_ROOT |
897 STATMOUNT_MNT_POINT | STATMOUNT_FS_TYPE | STATMOUNT_MNT_NS_ID;
898
899 ksft_print_header();
900
901 ret = statmount(0, 0, 0, 0, NULL, 0, 0);
902 assert(ret == -1);
903 if (errno == ENOSYS)
904 ksft_exit_skip("statmount() syscall not supported\n");
905
906 setup_namespace();
907
908 ksft_set_plan(17);
909 test_listmount_empty_root();
910 test_statmount_zero_mask();
911 test_statmount_mnt_basic();
912 test_statmount_sb_basic();
913 test_statmount_mnt_root();
914 test_statmount_mnt_point();
915 test_statmount_fs_type();
916 test_statmount_mnt_opts();
917 test_statmount_string(STATMOUNT_MNT_ROOT, str_off(mnt_root), "mount root");
918 test_statmount_string(STATMOUNT_MNT_POINT, str_off(mnt_point), "mount point");
919 test_statmount_string(STATMOUNT_FS_TYPE, str_off(fs_type), "fs type");
920 test_statmount_string(all_mask, str_off(mnt_root), "mount root & all");
921 test_statmount_string(all_mask, str_off(mnt_point), "mount point & all");
922 test_statmount_string(all_mask, str_off(fs_type), "fs type & all");
923
924 test_listmount_tree();
925 test_statmount_by_fd_unmounted();
926 test_statmount_by_fd();
927
928
929 if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
930 ksft_exit_fail();
931 else
932 ksft_exit_pass();
933 }
934