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