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