xref: /linux/tools/testing/selftests/filesystems/empty_mntns/internal_sb_reconfigure_test.c (revision 8e8cd9a4c6dc006e9bd51b8ad0079fb4576b48d3)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * The root of an empty mount namespace is a nullfs mount. Its superblock is
4  * kernel-internal and shared by every mount namespace. It can't be
5  * reconfigured, neither through fspick() nor through mount(MS_REMOUNT) nor
6  * through umount() of the root which remounts it read-only.
7  */
8 #define _GNU_SOURCE
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <sched.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <sys/mount.h>
15 #include <sys/statfs.h>
16 #include <sys/statvfs.h>
17 #include <sys/syscall.h>
18 #include <sys/vfs.h>
19 #include <sys/wait.h>
20 #include <unistd.h>
21 
22 #include "../utils.h"
23 #include "../wrappers.h"
24 #include "empty_mntns.h"
25 #include "kselftest_harness.h"
26 
27 #ifndef __NR_fspick
28 #define __NR_fspick 433
29 #endif
30 
sys_fspick(int dfd,const char * path,unsigned int flags)31 static int sys_fspick(int dfd, const char *path, unsigned int flags)
32 {
33 	return syscall(__NR_fspick, dfd, path, flags);
34 }
35 
36 /* Child exit codes. */
37 enum {
38 	CHILD_OK,
39 	CHILD_USERNS,		/* could not create the user namespace */
40 	CHILD_UNSHARE,		/* could not create the empty mount namespace */
41 	CHILD_FSPICK,		/* fspick() of the root was not refused with EINVAL */
42 	CHILD_REMOUNT,		/* mount(MS_REMOUNT) was not refused with EINVAL */
43 	CHILD_UMOUNT,		/* umount() of the root succeeded */
44 	CHILD_STATFS,		/* statfs() of the root failed */
45 	CHILD_RDONLY,		/* the root ended up read-only */
46 };
47 
empty_mntns_child(void)48 static int empty_mntns_child(void)
49 {
50 	struct statfs st;
51 
52 	if (enter_userns())
53 		return CHILD_USERNS;
54 	if (unshare(UNSHARE_EMPTY_MNTNS))
55 		return CHILD_UNSHARE;
56 
57 	if (sys_fspick(AT_FDCWD, "/", 0) >= 0 || errno != EINVAL)
58 		return CHILD_FSPICK;
59 	if (!mount(NULL, "/", NULL, MS_REMOUNT | MS_RDONLY, NULL) ||
60 	    errno != EINVAL)
61 		return CHILD_REMOUNT;
62 	if (!umount2("/", 0))
63 		return CHILD_UMOUNT;
64 	if (statfs("/", &st))
65 		return CHILD_STATFS;
66 	if (st.f_flags & ST_RDONLY)
67 		return CHILD_RDONLY;
68 	return CHILD_OK;
69 }
70 
FIXTURE(internal_sb_reconfigure)71 FIXTURE(internal_sb_reconfigure) {};
72 
FIXTURE_SETUP(internal_sb_reconfigure)73 FIXTURE_SETUP(internal_sb_reconfigure)
74 {
75 	pid_t pid;
76 	int status;
77 
78 	pid = fork();
79 	ASSERT_GE(pid, 0);
80 	if (pid == 0) {
81 		if (enter_userns())
82 			_exit(1);
83 		if (unshare(UNSHARE_EMPTY_MNTNS))
84 			_exit(1);
85 		_exit(0);
86 	}
87 	ASSERT_EQ(waitpid(pid, &status, 0), pid);
88 	if (!WIFEXITED(status) || WEXITSTATUS(status))
89 		SKIP(return, "UNSHARE_EMPTY_MNTNS not supported");
90 }
91 
FIXTURE_TEARDOWN(internal_sb_reconfigure)92 FIXTURE_TEARDOWN(internal_sb_reconfigure) {}
93 
TEST_F(internal_sb_reconfigure,nullfs_root)94 TEST_F(internal_sb_reconfigure, nullfs_root)
95 {
96 	pid_t pid;
97 	int status;
98 
99 	pid = fork();
100 	ASSERT_GE(pid, 0);
101 	if (pid == 0)
102 		_exit(empty_mntns_child());
103 	ASSERT_EQ(waitpid(pid, &status, 0), pid);
104 	ASSERT_TRUE(WIFEXITED(status));
105 	ASSERT_EQ(WEXITSTATUS(status), CHILD_OK);
106 }
107 
108 TEST_HARNESS_MAIN
109