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