1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * F_SET_RW_HINT is refused on an immutable inode. Nothing is ever written 4 * to it and it may be shared with everybody, like a namespace file or the 5 * root of an empty mount namespace. 6 */ 7 #define _GNU_SOURCE 8 #include <errno.h> 9 #include <fcntl.h> 10 #include <sched.h> 11 #include <stdint.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <unistd.h> 16 #include <sys/ioctl.h> 17 #include <sys/stat.h> 18 #include <sys/wait.h> 19 20 #include "../kselftest_harness.h" 21 22 /* <linux/fs.h> and <linux/fcntl.h> don't mix with the libc headers */ 23 #ifndef FS_IOC_GETFLAGS 24 #define FS_IOC_GETFLAGS _IOR('f', 1, long) 25 #define FS_IOC_SETFLAGS _IOW('f', 2, long) 26 #endif 27 #ifndef FS_IMMUTABLE_FL 28 #define FS_IMMUTABLE_FL 0x00000010 29 #endif 30 #ifndef F_LINUX_SPECIFIC_BASE 31 #define F_LINUX_SPECIFIC_BASE 1024 32 #endif 33 #ifndef F_GET_RW_HINT 34 #define F_GET_RW_HINT (F_LINUX_SPECIFIC_BASE + 11) 35 #define F_SET_RW_HINT (F_LINUX_SPECIFIC_BASE + 12) 36 #endif 37 #ifndef RWH_WRITE_LIFE_SHORT 38 #define RWH_WRITE_LIFE_SHORT 2 39 #endif 40 #ifndef UNSHARE_EMPTY_MNTNS 41 #define UNSHARE_EMPTY_MNTNS 0x00100000 42 #endif 43 44 static int set_hint(int fd, uint64_t hint) 45 { 46 return fcntl(fd, F_SET_RW_HINT, &hint); 47 } 48 49 static long get_hint(int fd) 50 { 51 uint64_t hint; 52 53 if (fcntl(fd, F_GET_RW_HINT, &hint)) 54 return -1; 55 return hint; 56 } 57 58 TEST(immutable_file) 59 { 60 char path[] = "/tmp/rw_hint.XXXXXX"; 61 int fd, flags; 62 63 if (geteuid()) 64 SKIP(return, "test requires root"); 65 66 fd = mkstemp(path); 67 ASSERT_GE(fd, 0); 68 unlink(path); 69 ASSERT_EQ(set_hint(fd, RWH_WRITE_LIFE_SHORT), 0); 70 EXPECT_EQ(get_hint(fd), RWH_WRITE_LIFE_SHORT); 71 72 if (ioctl(fd, FS_IOC_GETFLAGS, &flags)) { 73 close(fd); 74 SKIP(return, "no file attributes on this filesystem"); 75 } 76 flags |= FS_IMMUTABLE_FL; 77 ASSERT_EQ(ioctl(fd, FS_IOC_SETFLAGS, &flags), 0); 78 EXPECT_EQ(set_hint(fd, RWH_WRITE_LIFE_SHORT), -1); 79 EXPECT_EQ(errno, EPERM); 80 flags &= ~FS_IMMUTABLE_FL; 81 ASSERT_EQ(ioctl(fd, FS_IOC_SETFLAGS, &flags), 0); 82 EXPECT_EQ(set_hint(fd, RWH_WRITE_LIFE_SHORT), 0); 83 close(fd); 84 } 85 86 TEST(namespace_file) 87 { 88 int fd; 89 90 if (geteuid()) 91 SKIP(return, "test requires root"); 92 93 fd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC); 94 ASSERT_GE(fd, 0); 95 EXPECT_EQ(set_hint(fd, RWH_WRITE_LIFE_SHORT), -1); 96 EXPECT_EQ(errno, EPERM); 97 close(fd); 98 } 99 100 TEST(empty_mntns_root) 101 { 102 int status; 103 pid_t pid; 104 105 if (geteuid()) 106 SKIP(return, "test requires root"); 107 108 pid = fork(); 109 ASSERT_GE(pid, 0); 110 if (pid == 0) { 111 int fd; 112 113 if (unshare(UNSHARE_EMPTY_MNTNS)) 114 _exit(errno == EINVAL ? 100 : 1); 115 fd = open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC); 116 if (fd < 0) 117 _exit(2); 118 if (set_hint(fd, RWH_WRITE_LIFE_SHORT) == 0) 119 _exit(3); 120 _exit(errno == EPERM ? 0 : 4); 121 } 122 ASSERT_EQ(waitpid(pid, &status, 0), pid); 123 ASSERT_TRUE(WIFEXITED(status)); 124 if (WEXITSTATUS(status) == 100) 125 SKIP(return, "UNSHARE_EMPTY_MNTNS not supported"); 126 EXPECT_EQ(WEXITSTATUS(status), 0); 127 } 128 129 TEST_HARNESS_MAIN 130