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