brintos

brintos / linux-shallow public Read only

0
0
Text · 16.4 KiB · c773334 Raw
684 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2 3#define _GNU_SOURCE4 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", "reiserfs",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	else416		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		else523			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	else682		ksft_exit_pass();683}684