xref: /linux/tools/testing/selftests/filesystems/statmount/statmount_test.c (revision bf5802238dc181b1f7375d358af1d01cd72d1c11)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 
3 #define _GNU_SOURCE
4 
5 #include <assert.h>
6 #include <stdint.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/mount.h>
14 #include <linux/stat.h>
15 #include <asm/unistd.h>
16 
17 #include "../../kselftest.h"
18 
19 static const char *const known_fs[] = {
20 	"9p", "adfs", "affs", "afs", "aio", "anon_inodefs", "apparmorfs",
21 	"autofs", "bcachefs", "bdev", "befs", "bfs", "binder", "binfmt_misc",
22 	"bpf", "btrfs", "btrfs_test_fs", "ceph", "cgroup", "cgroup2", "cifs",
23 	"coda", "configfs", "cpuset", "cramfs", "cxl", "dax", "debugfs",
24 	"devpts", "devtmpfs", "dmabuf", "drm", "ecryptfs", "efivarfs", "efs",
25 	"erofs", "exfat", "ext2", "ext3", "ext4", "f2fs", "functionfs",
26 	"fuse", "fuseblk", "fusectl", "gadgetfs", "gfs2", "gfs2meta", "hfs",
27 	"hfsplus", "hostfs", "hpfs", "hugetlbfs", "ibmasmfs", "iomem",
28 	"ipathfs", "iso9660", "jffs2", "jfs", "minix", "mqueue", "msdos",
29 	"nfs", "nfs4", "nfsd", "nilfs2", "nsfs", "ntfs", "ntfs3", "ocfs2",
30 	"ocfs2_dlmfs", "ocxlflash", "omfs", "openpromfs", "overlay", "pipefs",
31 	"proc", "pstore", "pvfs2", "qnx4", "qnx6", "ramfs", "reiserfs",
32 	"resctrl", "romfs", "rootfs", "rpc_pipefs", "s390_hypfs", "secretmem",
33 	"securityfs", "selinuxfs", "smackfs", "smb3", "sockfs", "spufs",
34 	"squashfs", "sysfs", "sysv", "tmpfs", "tracefs", "ubifs", "udf",
35 	"ufs", "v7", "vboxsf", "vfat", "virtiofs", "vxfs", "xenfs", "xfs",
36 	"zonefs", NULL };
37 
38 static int statmount(uint64_t mnt_id, uint64_t mask, struct statmount *buf,
39 		     size_t bufsize, unsigned int flags)
40 {
41 	struct mnt_id_req req = {
42 		.size = MNT_ID_REQ_SIZE_VER0,
43 		.mnt_id = mnt_id,
44 		.param = mask,
45 	};
46 
47 	return syscall(__NR_statmount, &req, buf, bufsize, flags);
48 }
49 
50 static struct statmount *statmount_alloc(uint64_t mnt_id, uint64_t mask, unsigned int flags)
51 {
52 	size_t bufsize = 1 << 15;
53 	struct statmount *buf = NULL, *tmp = alloca(bufsize);
54 	int tofree = 0;
55 	int ret;
56 
57 	for (;;) {
58 		ret = statmount(mnt_id, mask, tmp, bufsize, flags);
59 		if (ret != -1)
60 			break;
61 		if (tofree)
62 			free(tmp);
63 		if (errno != EOVERFLOW)
64 			return NULL;
65 		bufsize <<= 1;
66 		tofree = 1;
67 		tmp = malloc(bufsize);
68 		if (!tmp)
69 			return NULL;
70 	}
71 	buf = malloc(tmp->size);
72 	if (buf)
73 		memcpy(buf, tmp, tmp->size);
74 	if (tofree)
75 		free(tmp);
76 
77 	return buf;
78 }
79 
80 static void write_file(const char *path, const char *val)
81 {
82 	int fd = open(path, O_WRONLY);
83 	size_t len = strlen(val);
84 	int ret;
85 
86 	if (fd == -1)
87 		ksft_exit_fail_msg("opening %s for write: %s\n", path, strerror(errno));
88 
89 	ret = write(fd, val, len);
90 	if (ret == -1)
91 		ksft_exit_fail_msg("writing to %s: %s\n", path, strerror(errno));
92 	if (ret != len)
93 		ksft_exit_fail_msg("short write to %s\n", path);
94 
95 	ret = close(fd);
96 	if (ret == -1)
97 		ksft_exit_fail_msg("closing %s\n", path);
98 }
99 
100 static uint64_t get_mnt_id(const char *name, const char *path, uint64_t mask)
101 {
102 	struct statx sx;
103 	int ret;
104 
105 	ret = statx(AT_FDCWD, path, 0, mask, &sx);
106 	if (ret == -1)
107 		ksft_exit_fail_msg("retrieving %s mount ID for %s: %s\n",
108 				   mask & STATX_MNT_ID_UNIQUE ? "unique" : "old",
109 				   name, strerror(errno));
110 	if (!(sx.stx_mask & mask))
111 		ksft_exit_fail_msg("no %s mount ID available for %s\n",
112 				   mask & STATX_MNT_ID_UNIQUE ? "unique" : "old",
113 				   name);
114 
115 	return sx.stx_mnt_id;
116 }
117 
118 
119 static char root_mntpoint[] = "/tmp/statmount_test_root.XXXXXX";
120 static int orig_root;
121 static uint64_t root_id, parent_id;
122 static uint32_t old_root_id, old_parent_id;
123 
124 
125 static void cleanup_namespace(void)
126 {
127 	fchdir(orig_root);
128 	chroot(".");
129 	umount2(root_mntpoint, MNT_DETACH);
130 	rmdir(root_mntpoint);
131 }
132 
133 static void setup_namespace(void)
134 {
135 	int ret;
136 	char buf[32];
137 	uid_t uid = getuid();
138 	gid_t gid = getgid();
139 
140 	ret = unshare(CLONE_NEWNS|CLONE_NEWUSER);
141 	if (ret == -1)
142 		ksft_exit_fail_msg("unsharing mountns and userns: %s\n",
143 				   strerror(errno));
144 
145 	sprintf(buf, "0 %d 1", uid);
146 	write_file("/proc/self/uid_map", buf);
147 	write_file("/proc/self/setgroups", "deny");
148 	sprintf(buf, "0 %d 1", gid);
149 	write_file("/proc/self/gid_map", buf);
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 
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 
210 static ssize_t listmount(uint64_t mnt_id, uint64_t last_mnt_id,
211 			 uint64_t list[], size_t num, unsigned int flags)
212 {
213 	struct mnt_id_req req = {
214 		.size = MNT_ID_REQ_SIZE_VER0,
215 		.mnt_id = mnt_id,
216 		.param = last_mnt_id,
217 	};
218 
219 	return syscall(__NR_listmount, &req, list, num, flags);
220 }
221 
222 static void test_listmount_empty_root(void)
223 {
224 	ssize_t res;
225 	const unsigned int size = 32;
226 	uint64_t list[size];
227 
228 	res = listmount(LSMT_ROOT, 0, list, size, 0);
229 	if (res == -1) {
230 		ksft_test_result_fail("listmount: %s\n", strerror(errno));
231 		return;
232 	}
233 	if (res != 1) {
234 		ksft_test_result_fail("listmount result is %zi != 1\n", res);
235 		return;
236 	}
237 
238 	if (list[0] != root_id) {
239 		ksft_test_result_fail("listmount ID doesn't match 0x%llx != 0x%llx\n",
240 				      (unsigned long long) list[0],
241 				      (unsigned long long) root_id);
242 		return;
243 	}
244 
245 	ksft_test_result_pass("listmount empty root\n");
246 }
247 
248 static void test_statmount_zero_mask(void)
249 {
250 	struct statmount sm;
251 	int ret;
252 
253 	ret = statmount(root_id, 0, &sm, sizeof(sm), 0);
254 	if (ret == -1) {
255 		ksft_test_result_fail("statmount zero mask: %s\n",
256 				      strerror(errno));
257 		return;
258 	}
259 	if (sm.size != sizeof(sm)) {
260 		ksft_test_result_fail("unexpected size: %u != %u\n",
261 				      sm.size, (uint32_t) sizeof(sm));
262 		return;
263 	}
264 	if (sm.mask != 0) {
265 		ksft_test_result_fail("unexpected mask: 0x%llx != 0x0\n",
266 				      (unsigned long long) sm.mask);
267 		return;
268 	}
269 
270 	ksft_test_result_pass("statmount zero mask\n");
271 }
272 
273 static void test_statmount_mnt_basic(void)
274 {
275 	struct statmount sm;
276 	int ret;
277 	uint64_t mask = STATMOUNT_MNT_BASIC;
278 
279 	ret = statmount(root_id, mask, &sm, sizeof(sm), 0);
280 	if (ret == -1) {
281 		ksft_test_result_fail("statmount mnt basic: %s\n",
282 				      strerror(errno));
283 		return;
284 	}
285 	if (sm.size != sizeof(sm)) {
286 		ksft_test_result_fail("unexpected size: %u != %u\n",
287 				      sm.size, (uint32_t) sizeof(sm));
288 		return;
289 	}
290 	if (sm.mask != mask) {
291 		ksft_test_result_skip("statmount mnt basic unavailable\n");
292 		return;
293 	}
294 
295 	if (sm.mnt_id != root_id) {
296 		ksft_test_result_fail("unexpected root ID: 0x%llx != 0x%llx\n",
297 				      (unsigned long long) sm.mnt_id,
298 				      (unsigned long long) root_id);
299 		return;
300 	}
301 
302 	if (sm.mnt_id_old != old_root_id) {
303 		ksft_test_result_fail("unexpected old root ID: %u != %u\n",
304 				      sm.mnt_id_old, old_root_id);
305 		return;
306 	}
307 
308 	if (sm.mnt_parent_id != parent_id) {
309 		ksft_test_result_fail("unexpected parent ID: 0x%llx != 0x%llx\n",
310 				      (unsigned long long) sm.mnt_parent_id,
311 				      (unsigned long long) parent_id);
312 		return;
313 	}
314 
315 	if (sm.mnt_parent_id_old != old_parent_id) {
316 		ksft_test_result_fail("unexpected old parent ID: %u != %u\n",
317 				      sm.mnt_parent_id_old, old_parent_id);
318 		return;
319 	}
320 
321 	if (sm.mnt_propagation != MS_PRIVATE) {
322 		ksft_test_result_fail("unexpected propagation: 0x%llx\n",
323 				      (unsigned long long) sm.mnt_propagation);
324 		return;
325 	}
326 
327 	ksft_test_result_pass("statmount mnt basic\n");
328 }
329 
330 
331 static void test_statmount_sb_basic(void)
332 {
333 	struct statmount sm;
334 	int ret;
335 	uint64_t mask = STATMOUNT_SB_BASIC;
336 	struct statx sx;
337 	struct statfs sf;
338 
339 	ret = statmount(root_id, mask, &sm, sizeof(sm), 0);
340 	if (ret == -1) {
341 		ksft_test_result_fail("statmount sb basic: %s\n",
342 				      strerror(errno));
343 		return;
344 	}
345 	if (sm.size != sizeof(sm)) {
346 		ksft_test_result_fail("unexpected size: %u != %u\n",
347 				      sm.size, (uint32_t) sizeof(sm));
348 		return;
349 	}
350 	if (sm.mask != mask) {
351 		ksft_test_result_skip("statmount sb basic unavailable\n");
352 		return;
353 	}
354 
355 	ret = statx(AT_FDCWD, "/", 0, 0, &sx);
356 	if (ret == -1) {
357 		ksft_test_result_fail("stat root failed: %s\n",
358 				      strerror(errno));
359 		return;
360 	}
361 
362 	if (sm.sb_dev_major != sx.stx_dev_major ||
363 	    sm.sb_dev_minor != sx.stx_dev_minor) {
364 		ksft_test_result_fail("unexpected sb dev %u:%u != %u:%u\n",
365 				      sm.sb_dev_major, sm.sb_dev_minor,
366 				      sx.stx_dev_major, sx.stx_dev_minor);
367 		return;
368 	}
369 
370 	ret = statfs("/", &sf);
371 	if (ret == -1) {
372 		ksft_test_result_fail("statfs root failed: %s\n",
373 				      strerror(errno));
374 		return;
375 	}
376 
377 	if (sm.sb_magic != sf.f_type) {
378 		ksft_test_result_fail("unexpected sb magic: 0x%llx != 0x%lx\n",
379 				      (unsigned long long) sm.sb_magic,
380 				      sf.f_type);
381 		return;
382 	}
383 
384 	ksft_test_result_pass("statmount sb basic\n");
385 }
386 
387 static void test_statmount_mnt_point(void)
388 {
389 	struct statmount *sm;
390 
391 	sm = statmount_alloc(root_id, STATMOUNT_MNT_POINT, 0);
392 	if (!sm) {
393 		ksft_test_result_fail("statmount mount point: %s\n",
394 				      strerror(errno));
395 		return;
396 	}
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, STATMOUNT_MNT_ROOT, 0);
418 	if (!sm) {
419 		ksft_test_result_fail("statmount mount root: %s\n",
420 				      strerror(errno));
421 		return;
422 	}
423 	mnt_root = sm->str + sm->mnt_root;
424 	last_root = strrchr(mnt_root, '/');
425 	if (last_root)
426 		last_root++;
427 	else
428 		last_root = mnt_root;
429 
430 	if (strcmp(last_dir, last_root) != 0) {
431 		ksft_test_result_fail("unexpected mount root last component: '%s' != '%s'\n",
432 				      last_root, last_dir);
433 		goto out;
434 	}
435 	ksft_test_result_pass("statmount mount root\n");
436 out:
437 	free(sm);
438 }
439 
440 static void test_statmount_fs_type(void)
441 {
442 	struct statmount *sm;
443 	const char *fs_type;
444 	const char *const *s;
445 
446 	sm = statmount_alloc(root_id, STATMOUNT_FS_TYPE, 0);
447 	if (!sm) {
448 		ksft_test_result_fail("statmount fs type: %s\n",
449 				      strerror(errno));
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 
464 static void test_statmount_string(uint64_t mask, size_t off, const char *name)
465 {
466 	struct statmount *sm;
467 	size_t len, shortsize, exactsize;
468 	uint32_t start, i;
469 	int ret;
470 
471 	sm = statmount_alloc(root_id, mask, 0);
472 	if (!sm) {
473 		ksft_test_result_fail("statmount %s: %s\n", name,
474 				      strerror(errno));
475 		goto out;
476 	}
477 	if (sm->size < sizeof(*sm)) {
478 		ksft_test_result_fail("unexpected size: %u < %u\n",
479 				      sm->size, (uint32_t) sizeof(*sm));
480 		goto out;
481 	}
482 	if (sm->mask != mask) {
483 		ksft_test_result_skip("statmount %s unavailable\n", name);
484 		goto out;
485 	}
486 	len = sm->size - sizeof(*sm);
487 	start = ((uint32_t *) sm)[off];
488 
489 	for (i = start;; i++) {
490 		if (i >= len) {
491 			ksft_test_result_fail("string out of bounds\n");
492 			goto out;
493 		}
494 		if (!sm->str[i])
495 			break;
496 	}
497 	exactsize = sm->size;
498 	shortsize = sizeof(*sm) + i;
499 
500 	ret = statmount(root_id, mask, sm, exactsize, 0);
501 	if (ret == -1) {
502 		ksft_test_result_fail("statmount exact size: %s\n",
503 				      strerror(errno));
504 		goto out;
505 	}
506 	errno = 0;
507 	ret = statmount(root_id, mask, sm, shortsize, 0);
508 	if (ret != -1 || errno != EOVERFLOW) {
509 		ksft_test_result_fail("should have failed with EOVERFLOW: %s\n",
510 				      strerror(errno));
511 		goto out;
512 	}
513 
514 	ksft_test_result_pass("statmount string %s\n", name);
515 out:
516 	free(sm);
517 }
518 
519 static void test_listmount_tree(void)
520 {
521 	ssize_t res;
522 	const unsigned int log2_num = 4;
523 	const unsigned int step = 3;
524 	const unsigned int size = (1 << log2_num) + step + 1;
525 	size_t num, expect = 1 << log2_num;
526 	uint64_t list[size];
527 	uint64_t list2[size];
528 	size_t i;
529 
530 
531 	res = setup_mount_tree(log2_num);
532 	if (res == -1)
533 		return;
534 
535 	num = res = listmount(LSMT_ROOT, 0, list, size, 0);
536 	if (res == -1) {
537 		ksft_test_result_fail("listmount: %s\n", strerror(errno));
538 		return;
539 	}
540 	if (num != expect) {
541 		ksft_test_result_fail("listmount result is %zi != %zi\n",
542 				      res, expect);
543 		return;
544 	}
545 
546 	for (i = 0; i < size - step;) {
547 		res = listmount(LSMT_ROOT, i ? list2[i - 1] : 0, list2 + i, step, 0);
548 		if (res == -1)
549 			ksft_test_result_fail("short listmount: %s\n",
550 					      strerror(errno));
551 		i += res;
552 		if (res < step)
553 			break;
554 	}
555 	if (i != num) {
556 		ksft_test_result_fail("different number of entries: %zu != %zu\n",
557 				      i, num);
558 		return;
559 	}
560 	for (i = 0; i < num; i++) {
561 		if (list2[i] != list[i]) {
562 			ksft_test_result_fail("different value for entry %zu: 0x%llx != 0x%llx\n",
563 					      i,
564 					      (unsigned long long) list2[i],
565 					      (unsigned long long) list[i]);
566 		}
567 	}
568 
569 	ksft_test_result_pass("listmount tree\n");
570 }
571 
572 #define str_off(memb) (offsetof(struct statmount, memb) / sizeof(uint32_t))
573 
574 int main(void)
575 {
576 	int ret;
577 	uint64_t all_mask = STATMOUNT_SB_BASIC | STATMOUNT_MNT_BASIC |
578 		STATMOUNT_PROPAGATE_FROM | STATMOUNT_MNT_ROOT |
579 		STATMOUNT_MNT_POINT | STATMOUNT_FS_TYPE;
580 
581 	ksft_print_header();
582 
583 	ret = statmount(0, 0, NULL, 0, 0);
584 	assert(ret == -1);
585 	if (errno == ENOSYS)
586 		ksft_exit_skip("statmount() syscall not supported\n");
587 
588 	setup_namespace();
589 
590 	ksft_set_plan(14);
591 	test_listmount_empty_root();
592 	test_statmount_zero_mask();
593 	test_statmount_mnt_basic();
594 	test_statmount_sb_basic();
595 	test_statmount_mnt_root();
596 	test_statmount_mnt_point();
597 	test_statmount_fs_type();
598 	test_statmount_string(STATMOUNT_MNT_ROOT, str_off(mnt_root), "mount root");
599 	test_statmount_string(STATMOUNT_MNT_POINT, str_off(mnt_point), "mount point");
600 	test_statmount_string(STATMOUNT_FS_TYPE, str_off(fs_type), "fs type");
601 	test_statmount_string(all_mask, str_off(mnt_root), "mount root & all");
602 	test_statmount_string(all_mask, str_off(mnt_point), "mount point & all");
603 	test_statmount_string(all_mask, str_off(fs_type), "fs type & all");
604 
605 	test_listmount_tree();
606 
607 
608 	if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
609 		ksft_exit_fail();
610 	else
611 		ksft_exit_pass();
612 }
613