xref: /linux/tools/testing/selftests/filesystems/statmount/statmount_test.c (revision 85cdaca6970028bf6f544c355c90035586836ddf)
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 	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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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