xref: /linux/tools/testing/selftests/filesystems/umount_propagation/shrink_submounts_test.c (revision 8e8cd9a4c6dc006e9bd51b8ad0079fb4576b48d3)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * A synchronous umount first unmounts the shrinkable submounts of the
4  * victim that aren't busy. Every one of them has to be checked right
5  * before it is unmounted: unmounting one can slide a busy mount to where
6  * the propagated copy of the next one is looked up.
7  */
8 #define _GNU_SOURCE
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <sched.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <sys/fanotify.h>
17 #include <sys/mount.h>
18 #include <sys/stat.h>
19 #include <sys/statfs.h>
20 #include <sys/vfs.h>
21 #include <linux/magic.h>
22 
23 #include "../../kselftest_harness.h"
24 
25 #ifndef FAN_REPORT_MNT
26 #define FAN_REPORT_MNT		0x00004000
27 #endif
28 #ifndef FAN_MARK_MNTNS
29 #define FAN_MARK_MNTNS		0x00000110
30 #endif
31 #ifndef FAN_MNT_ATTACH
32 #define FAN_MNT_ATTACH		0x01000000
33 #endif
34 #ifndef FAN_MNT_DETACH
35 #define FAN_MNT_DETACH		0x02000000
36 #endif
37 
38 #define DIR_LEN 64
39 #define PATH_LEN 128
40 
FIXTURE(shrink_submounts)41 FIXTURE(shrink_submounts) {
42 	char base[DIR_LEN];
43 	char automount[PATH_LEN];
44 	bool mounted;
45 	int fan;
46 };
47 
48 /*
49  * Shrinkable mounts come from an automount. The tracefs mount below debugfs
50  * is one and bind mounts inherit the flag.
51  */
FIXTURE_SETUP(shrink_submounts)52 FIXTURE_SETUP(shrink_submounts)
53 {
54 	struct stat st;
55 	char p[PATH_LEN];
56 
57 	self->mounted = false;
58 	self->fan = -1;
59 
60 	if (geteuid() != 0)
61 		SKIP(return, "test requires CAP_SYS_ADMIN");
62 
63 	ASSERT_EQ(unshare(CLONE_NEWNS), 0);
64 	ASSERT_EQ(mount("", "/", NULL, MS_REC | MS_PRIVATE, NULL), 0);
65 
66 	snprintf(self->base, sizeof(self->base), "/tmp/shrink_submounts.XXXXXX");
67 	ASSERT_NE(mkdtemp(self->base), NULL);
68 	ASSERT_EQ(mount("tmpfs", self->base, "tmpfs", 0, NULL), 0);
69 	self->mounted = true;
70 	ASSERT_EQ(mount(NULL, self->base, NULL, MS_PRIVATE, NULL), 0);
71 
72 	snprintf(p, sizeof(p), "%s/dbg", self->base);
73 	ASSERT_EQ(mkdir(p, 0755), 0);
74 	if (mount("debugfs", p, "debugfs", 0, NULL)) {
75 		umount2(self->base, MNT_DETACH);
76 		rmdir(self->base);
77 		SKIP(return, "test requires debugfs");
78 	}
79 	snprintf(self->automount, sizeof(self->automount), "%s/dbg/tracing",
80 		 self->base);
81 	snprintf(p, sizeof(p), "%s/dbg/tracing/.", self->base);
82 	if (stat(p, &st)) {
83 		umount2(self->base, MNT_DETACH);
84 		rmdir(self->base);
85 		SKIP(return, "test requires the tracefs automount");
86 	}
87 }
88 
FIXTURE_TEARDOWN(shrink_submounts)89 FIXTURE_TEARDOWN(shrink_submounts)
90 {
91 	if (self->fan >= 0)
92 		close(self->fan);
93 	chdir("/");
94 	if (self->mounted)
95 		umount2(self->base, MNT_DETACH);
96 	rmdir(self->base);
97 }
98 
mounted_tmpfs(const char * path)99 static bool mounted_tmpfs(const char *path)
100 {
101 	struct statfs st;
102 
103 	return !statfs(path, &st) && st.f_type == TMPFS_MAGIC;
104 }
105 
106 /*
107  * P is a shared bind mount of the automount, P1 a slave of P that is shared
108  * in turn and P2 its peer. B, another bind mount of the automount, goes on
109  * P/options and propagates copies Bc1 and Bc2 onto P1/options and
110  * P2/options. R, a tmpfs and our working directory, sits on top of Bc2 which
111  * is made private first. P, with B on it, is moved to P1/instances.
112  *
113  * A synchronous umount of P1 unmounts the shrinkable submounts Bc1 and B
114  * first. Unmounting Bc1 takes Bc2 along and slides R to P2/options where
115  * the propagated copy of B is looked up next. R is busy, so B has to stay
116  * and the umount fails with EBUSY.
117  */
TEST_F(shrink_submounts,busy_mount_moved_into_reach)118 TEST_F(shrink_submounts, busy_mount_moved_into_reach)
119 {
120 	char p[PATH_LEN], p1[PATH_LEN], p2[PATH_LEN], r[PATH_LEN], cwd[PATH_LEN];
121 	int nsfd;
122 
123 	snprintf(p, sizeof(p), "%s/p", self->base);
124 	snprintf(p1, sizeof(p1), "%s/p1", self->base);
125 	snprintf(p2, sizeof(p2), "%s/p2", self->base);
126 	ASSERT_EQ(mkdir(p, 0755), 0);
127 	ASSERT_EQ(mkdir(p1, 0755), 0);
128 	ASSERT_EQ(mkdir(p2, 0755), 0);
129 
130 	/* watch the mount namespace so that the detached mounts get queued */
131 	self->fan = fanotify_init(FAN_REPORT_MNT, O_RDONLY);
132 	if (self->fan >= 0) {
133 		nsfd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
134 		ASSERT_GE(nsfd, 0);
135 		EXPECT_EQ(fanotify_mark(self->fan, FAN_MARK_ADD | FAN_MARK_MNTNS,
136 					FAN_MNT_ATTACH | FAN_MNT_DETACH, nsfd, NULL), 0);
137 		close(nsfd);
138 	}
139 
140 	ASSERT_EQ(mount(self->automount, p, NULL, MS_BIND, NULL), 0);
141 	ASSERT_EQ(mount(NULL, p, NULL, MS_SHARED, NULL), 0);
142 	ASSERT_EQ(mount(p, p1, NULL, MS_BIND, NULL), 0);
143 	ASSERT_EQ(mount(NULL, p1, NULL, MS_SLAVE, NULL), 0);
144 	ASSERT_EQ(mount(NULL, p1, NULL, MS_SHARED, NULL), 0);
145 	ASSERT_EQ(mount(p1, p2, NULL, MS_BIND, NULL), 0);
146 
147 	/* B on P/options, copies on P1/options and P2/options */
148 	snprintf(r, sizeof(r), "%s/p/options", self->base);
149 	ASSERT_EQ(mount(self->automount, r, NULL, MS_BIND, NULL), 0);
150 
151 	/* R on top of Bc2 */
152 	snprintf(r, sizeof(r), "%s/p2/options", self->base);
153 	ASSERT_EQ(mount(NULL, r, NULL, MS_PRIVATE, NULL), 0);
154 	ASSERT_EQ(mount("R", r, "tmpfs", 0, NULL), 0);
155 	ASSERT_EQ(chdir(r), 0);
156 
157 	/* P, with B on it, below the victim */
158 	snprintf(cwd, sizeof(cwd), "%s/p1/instances", self->base);
159 	ASSERT_EQ(mount(p, cwd, NULL, MS_MOVE, NULL), 0);
160 
161 	ASSERT_TRUE(mounted_tmpfs(r));
162 	ASSERT_EQ(umount2(p1, 0), -1);
163 	EXPECT_EQ(errno, EBUSY);
164 
165 	/* R is still mounted and still our working directory */
166 	EXPECT_TRUE(mounted_tmpfs(r));
167 	ASSERT_NE(getcwd(cwd, sizeof(cwd)), NULL);
168 	EXPECT_STREQ(cwd, r);
169 }
170 
171 /*
172  * T is shared and Q, a slave of T, has T moved into it, so Q receives
173  * propagation from its own child. M, another bind mount of the automount, is
174  * on T/options and that is the dentry T sits on in Q. Unmounting M makes T
175  * the propagated victim at that dentry in Q and takes T along. The shrink
176  * walk of V has just unmounted M and continues in the children of T.
177  */
TEST_F(shrink_submounts,parent_goes_with_child)178 TEST_F(shrink_submounts, parent_goes_with_child)
179 {
180 	char v[PATH_LEN], t[PATH_LEN], q[PATH_LEN], p[PATH_LEN];
181 	struct stat before, after;
182 
183 	snprintf(v, sizeof(v), "%s/v", self->base);
184 	snprintf(t, sizeof(t), "%s/v/t", self->base);
185 	snprintf(q, sizeof(q), "%s/v/q", self->base);
186 	ASSERT_EQ(mkdir(v, 0755), 0);
187 	ASSERT_EQ(mount("V", v, "tmpfs", 0, NULL), 0);
188 	ASSERT_EQ(mount(NULL, v, NULL, MS_PRIVATE, NULL), 0);
189 	ASSERT_EQ(stat(v, &before), 0);
190 	ASSERT_EQ(mkdir(t, 0755), 0);
191 	ASSERT_EQ(mkdir(q, 0755), 0);
192 
193 	/* T shared, M on T/options before anything receives from T */
194 	ASSERT_EQ(mount(self->automount, t, NULL, MS_BIND, NULL), 0);
195 	ASSERT_EQ(mount(NULL, t, NULL, MS_SHARED, NULL), 0);
196 	snprintf(p, sizeof(p), "%s/v/t/options", self->base);
197 	ASSERT_EQ(mount(self->automount, p, NULL, MS_BIND, NULL), 0);
198 
199 	/* Q, a slave of T, and T moved into Q at the dentry M sits on */
200 	ASSERT_EQ(mount(t, q, NULL, MS_BIND, NULL), 0);
201 	ASSERT_EQ(mount(NULL, q, NULL, MS_SLAVE, NULL), 0);
202 	snprintf(p, sizeof(p), "%s/v/q/options", self->base);
203 	ASSERT_EQ(mount(t, p, NULL, MS_MOVE, NULL), 0);
204 
205 	/* M, T and then Q go, V is empty and can be unmounted */
206 	ASSERT_EQ(umount2(v, 0), 0);
207 	ASSERT_EQ(stat(v, &after), 0);
208 	EXPECT_NE(before.st_dev, after.st_dev);
209 }
210 
211 TEST_HARNESS_MAIN
212