xref: /linux/tools/testing/selftests/filesystems/statmount/statmount_test.c (revision e2683c8868d03382da7e1ce8453b543a043066d1)
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 
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 
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 
81 static 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 
97 static 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 
157 static 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 
179 static 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 
205 static 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 
230 static 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 
288 static 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 
344 static 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");
365 out:
366 	free(sm);
367 }
368 
369 static 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");
401 out:
402 	free(sm);
403 }
404 
405 static 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 
433 static 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 
523 static 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);
574 out:
575 	free(sm);
576 }
577 
578 static 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 
631 static 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;
750 err_chroot:
751 	chroot(".");
752 out:
753 	free(sm);
754 err_fd:
755 	close(fd);
756 err_tmproot:
757 	rmdir(tmproot);
758 err_subdir:
759 	umount2(subdir, MNT_DETACH);
760 	rmdir(subdir);
761 err_tmpdir:
762 	umount2(tmpdir, MNT_DETACH);
763 	rmdir(tmpdir);
764 }
765 
766 static 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");
839 err_sm:
840 	free(sm);
841 err_fd:
842 	close(fd);
843 err_subdir:
844 	umount2(subdir, MNT_DETACH);
845 	rmdir(subdir);
846 err_tmpdir:
847 	umount2(tmpdir, MNT_DETACH);
848 	rmdir(tmpdir);
849 }
850 
851 #define str_off(memb) (offsetof(struct statmount, memb) / sizeof(uint32_t))
852 
853 int 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 }
895