xref: /linux/tools/testing/selftests/filesystems/ustat_test.c (revision 1781f0b3d75caa22376cf7fa0224f9aca696580d)
1*a21db4dcSChristian Brauner // SPDX-License-Identifier: GPL-2.0
2*a21db4dcSChristian Brauner /*
3*a21db4dcSChristian Brauner  * Test ustat(2): looking up superblocks by device number.
4*a21db4dcSChristian Brauner  *
5*a21db4dcSChristian Brauner  * ustat() resolves a device number to a mounted superblock via
6*a21db4dcSChristian Brauner  * user_get_super(). Check that the device number of a mounted tmpfs (an
7*a21db4dcSChristian Brauner  * anonymous device) resolves, that it stops resolving once the filesystem
8*a21db4dcSChristian Brauner  * is unmounted and that bogus device numbers report EINVAL.
9*a21db4dcSChristian Brauner  */
10*a21db4dcSChristian Brauner #define _GNU_SOURCE
11*a21db4dcSChristian Brauner #include <errno.h>
12*a21db4dcSChristian Brauner #include <fcntl.h>
13*a21db4dcSChristian Brauner #include <sched.h>
14*a21db4dcSChristian Brauner #include <stdio.h>
15*a21db4dcSChristian Brauner #include <stdlib.h>
16*a21db4dcSChristian Brauner #include <string.h>
17*a21db4dcSChristian Brauner #include <sys/mount.h>
18*a21db4dcSChristian Brauner #include <sys/stat.h>
19*a21db4dcSChristian Brauner #include <sys/syscall.h>
20*a21db4dcSChristian Brauner #include <unistd.h>
21*a21db4dcSChristian Brauner 
22*a21db4dcSChristian Brauner #include "../kselftest_harness.h"
23*a21db4dcSChristian Brauner 
24*a21db4dcSChristian Brauner /* struct ustat is not exported through UAPI, mirror include/linux/types.h. */
25*a21db4dcSChristian Brauner struct ustat_buf {
26*a21db4dcSChristian Brauner 	int		f_tfree;
27*a21db4dcSChristian Brauner 	unsigned long	f_tinode;
28*a21db4dcSChristian Brauner 	char		f_fname[6];
29*a21db4dcSChristian Brauner 	char		f_fpack[6];
30*a21db4dcSChristian Brauner 	/* slack in case an architecture lays the struct out differently */
31*a21db4dcSChristian Brauner 	char		pad[64];
32*a21db4dcSChristian Brauner };
33*a21db4dcSChristian Brauner 
34*a21db4dcSChristian Brauner #ifdef __NR_ustat
35*a21db4dcSChristian Brauner 
36*a21db4dcSChristian Brauner /*
37*a21db4dcSChristian Brauner  * The kernel decodes @dev with new_decode_dev(), which matches the low 32
38*a21db4dcSChristian Brauner  * bits of the st_dev encoding stat(2) returns for any major below 4096.
39*a21db4dcSChristian Brauner  */
40*a21db4dcSChristian Brauner static int sys_ustat(unsigned int dev, struct ustat_buf *buf)
41*a21db4dcSChristian Brauner {
42*a21db4dcSChristian Brauner 	return syscall(__NR_ustat, dev, buf);
43*a21db4dcSChristian Brauner }
44*a21db4dcSChristian Brauner 
45*a21db4dcSChristian Brauner static int write_string(const char *path, const char *string)
46*a21db4dcSChristian Brauner {
47*a21db4dcSChristian Brauner 	ssize_t len = strlen(string);
48*a21db4dcSChristian Brauner 	int fd;
49*a21db4dcSChristian Brauner 
50*a21db4dcSChristian Brauner 	fd = open(path, O_WRONLY);
51*a21db4dcSChristian Brauner 	if (fd < 0)
52*a21db4dcSChristian Brauner 		return -1;
53*a21db4dcSChristian Brauner 	if (write(fd, string, len) != len) {
54*a21db4dcSChristian Brauner 		close(fd);
55*a21db4dcSChristian Brauner 		return -1;
56*a21db4dcSChristian Brauner 	}
57*a21db4dcSChristian Brauner 	return close(fd);
58*a21db4dcSChristian Brauner }
59*a21db4dcSChristian Brauner 
60*a21db4dcSChristian Brauner /* Enter namespaces in which mounting a tmpfs instance is allowed. */
61*a21db4dcSChristian Brauner static int setup_namespaces(void)
62*a21db4dcSChristian Brauner {
63*a21db4dcSChristian Brauner 	uid_t uid = getuid();
64*a21db4dcSChristian Brauner 	gid_t gid = getgid();
65*a21db4dcSChristian Brauner 	char map[64];
66*a21db4dcSChristian Brauner 
67*a21db4dcSChristian Brauner 	if (unshare(CLONE_NEWNS | (uid ? CLONE_NEWUSER : 0)))
68*a21db4dcSChristian Brauner 		return -1;
69*a21db4dcSChristian Brauner 
70*a21db4dcSChristian Brauner 	if (uid) {
71*a21db4dcSChristian Brauner 		if (write_string("/proc/self/setgroups", "deny"))
72*a21db4dcSChristian Brauner 			return -1;
73*a21db4dcSChristian Brauner 		snprintf(map, sizeof(map), "0 %d 1", uid);
74*a21db4dcSChristian Brauner 		if (write_string("/proc/self/uid_map", map))
75*a21db4dcSChristian Brauner 			return -1;
76*a21db4dcSChristian Brauner 		snprintf(map, sizeof(map), "0 %d 1", gid);
77*a21db4dcSChristian Brauner 		if (write_string("/proc/self/gid_map", map))
78*a21db4dcSChristian Brauner 			return -1;
79*a21db4dcSChristian Brauner 	}
80*a21db4dcSChristian Brauner 
81*a21db4dcSChristian Brauner 	return mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL);
82*a21db4dcSChristian Brauner }
83*a21db4dcSChristian Brauner 
84*a21db4dcSChristian Brauner TEST(resolves_mounted_superblock)
85*a21db4dcSChristian Brauner {
86*a21db4dcSChristian Brauner 	char dir[] = "/tmp/ustat_test.XXXXXX";
87*a21db4dcSChristian Brauner 	struct ustat_buf ub;
88*a21db4dcSChristian Brauner 	struct stat st;
89*a21db4dcSChristian Brauner 
90*a21db4dcSChristian Brauner 	ASSERT_NE(NULL, mkdtemp(dir));
91*a21db4dcSChristian Brauner 
92*a21db4dcSChristian Brauner 	if (setup_namespaces()) {
93*a21db4dcSChristian Brauner 		rmdir(dir);
94*a21db4dcSChristian Brauner 		SKIP(return, "cannot set up namespaces: %s", strerror(errno));
95*a21db4dcSChristian Brauner 	}
96*a21db4dcSChristian Brauner 
97*a21db4dcSChristian Brauner 	ASSERT_EQ(0, mount("ustat_test", dir, "tmpfs", 0, NULL));
98*a21db4dcSChristian Brauner 	ASSERT_EQ(0, stat(dir, &st));
99*a21db4dcSChristian Brauner 
100*a21db4dcSChristian Brauner 	memset(&ub, 0xff, sizeof(ub));
101*a21db4dcSChristian Brauner 	ASSERT_EQ(0, sys_ustat(st.st_dev, &ub))
102*a21db4dcSChristian Brauner 		TH_LOG("ustat(%u): %s", (unsigned int)st.st_dev,
103*a21db4dcSChristian Brauner 		       strerror(errno));
104*a21db4dcSChristian Brauner 
105*a21db4dcSChristian Brauner 	ASSERT_EQ(0, umount(dir));
106*a21db4dcSChristian Brauner 
107*a21db4dcSChristian Brauner 	/* The unmount removed the superblock, the device is gone. */
108*a21db4dcSChristian Brauner 	ASSERT_EQ(-1, sys_ustat(st.st_dev, &ub));
109*a21db4dcSChristian Brauner 	ASSERT_EQ(EINVAL, errno);
110*a21db4dcSChristian Brauner 
111*a21db4dcSChristian Brauner 	rmdir(dir);
112*a21db4dcSChristian Brauner }
113*a21db4dcSChristian Brauner 
114*a21db4dcSChristian Brauner TEST(bogus_device_numbers)
115*a21db4dcSChristian Brauner {
116*a21db4dcSChristian Brauner 	struct ustat_buf ub;
117*a21db4dcSChristian Brauner 
118*a21db4dcSChristian Brauner 	ASSERT_EQ(-1, sys_ustat(0, &ub));
119*a21db4dcSChristian Brauner 	ASSERT_EQ(EINVAL, errno);
120*a21db4dcSChristian Brauner 
121*a21db4dcSChristian Brauner 	/* major 4095, minor 1048575: nothing plausible lives there */
122*a21db4dcSChristian Brauner 	ASSERT_EQ(-1, sys_ustat((0xfffu << 8) | 0xffu | (0xfff00u << 12), &ub));
123*a21db4dcSChristian Brauner 	ASSERT_EQ(EINVAL, errno);
124*a21db4dcSChristian Brauner }
125*a21db4dcSChristian Brauner 
126*a21db4dcSChristian Brauner #else /* !__NR_ustat */
127*a21db4dcSChristian Brauner 
128*a21db4dcSChristian Brauner TEST(unsupported)
129*a21db4dcSChristian Brauner {
130*a21db4dcSChristian Brauner 	SKIP(return, "ustat(2) is not available on this architecture");
131*a21db4dcSChristian Brauner }
132*a21db4dcSChristian Brauner 
133*a21db4dcSChristian Brauner #endif /* __NR_ustat */
134*a21db4dcSChristian Brauner 
135*a21db4dcSChristian Brauner TEST_HARNESS_MAIN
136