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