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