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