Linux Audio

Check our new training course

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