]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
83555251 LP |
2 | |
3 | #include <sys/mount.h> | |
10cdbb83 | 4 | #include <sys/statvfs.h> |
83555251 | 5 | |
c2a986d5 | 6 | #include "alloc-util.h" |
c75370cc | 7 | #include "capability-util.h" |
10cdbb83 LP |
8 | #include "fd-util.h" |
9 | #include "fileio.h" | |
9c653536 | 10 | #include "fs-util.h" |
4e9ef660 | 11 | #include "libmount-util.h" |
f63a2c48 | 12 | #include "missing_magic.h" |
51bb6a10 | 13 | #include "missing_mount.h" |
9c653536 | 14 | #include "mkdir.h" |
83555251 | 15 | #include "mount-util.h" |
61ef3051 | 16 | #include "mountpoint-util.h" |
10cdbb83 LP |
17 | #include "namespace-util.h" |
18 | #include "path-util.h" | |
19 | #include "process-util.h" | |
ea0f3289 | 20 | #include "random-util.h" |
10cdbb83 | 21 | #include "rm-rf.h" |
f63a2c48 | 22 | #include "stat-util.h" |
83555251 | 23 | #include "string-util.h" |
10cdbb83 | 24 | #include "strv.h" |
6d7c4033 | 25 | #include "tests.h" |
10cdbb83 | 26 | #include "tmpfile-util.h" |
83555251 | 27 | |
4f7452a8 | 28 | TEST(mount_option_mangle) { |
f27b437b YW |
29 | char *opts = NULL; |
30 | unsigned long f; | |
31 | ||
32 | assert_se(mount_option_mangle(NULL, MS_RDONLY|MS_NOSUID, &f, &opts) == 0); | |
33 | assert_se(f == (MS_RDONLY|MS_NOSUID)); | |
5152b845 | 34 | ASSERT_NULL(opts); |
f27b437b YW |
35 | |
36 | assert_se(mount_option_mangle("", MS_RDONLY|MS_NOSUID, &f, &opts) == 0); | |
37 | assert_se(f == (MS_RDONLY|MS_NOSUID)); | |
5152b845 | 38 | ASSERT_NULL(opts); |
f27b437b YW |
39 | |
40 | assert_se(mount_option_mangle("ro,nosuid,nodev,noexec", 0, &f, &opts) == 0); | |
41 | assert_se(f == (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC)); | |
5152b845 | 42 | ASSERT_NULL(opts); |
f27b437b | 43 | |
9f563f27 | 44 | assert_se(mount_option_mangle("ro,nosuid,nodev,noexec,mode=0755", 0, &f, &opts) == 0); |
f27b437b | 45 | assert_se(f == (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC)); |
c79e88b3 | 46 | ASSERT_STREQ(opts, "mode=0755"); |
f27b437b YW |
47 | opts = mfree(opts); |
48 | ||
9f563f27 | 49 | assert_se(mount_option_mangle("rw,nosuid,foo,hogehoge,nodev,mode=0755", 0, &f, &opts) == 0); |
f27b437b | 50 | assert_se(f == (MS_NOSUID|MS_NODEV)); |
c79e88b3 | 51 | ASSERT_STREQ(opts, "foo,hogehoge,mode=0755"); |
f27b437b YW |
52 | opts = mfree(opts); |
53 | ||
54 | assert_se(mount_option_mangle("rw,nosuid,nodev,noexec,relatime,net_cls,net_prio", MS_RDONLY, &f, &opts) == 0); | |
55 | assert_se(f == (MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_RELATIME)); | |
c79e88b3 | 56 | ASSERT_STREQ(opts, "net_cls,net_prio"); |
f27b437b YW |
57 | opts = mfree(opts); |
58 | ||
9f563f27 | 59 | assert_se(mount_option_mangle("rw,nosuid,nodev,relatime,size=1630748k,mode=0700,uid=1000,gid=1000", MS_RDONLY, &f, &opts) == 0); |
f27b437b | 60 | assert_se(f == (MS_NOSUID|MS_NODEV|MS_RELATIME)); |
c79e88b3 | 61 | ASSERT_STREQ(opts, "size=1630748k,mode=0700,uid=1000,gid=1000"); |
f27b437b YW |
62 | opts = mfree(opts); |
63 | ||
9f563f27 | 64 | assert_se(mount_option_mangle("size=1630748k,rw,gid=1000,,,nodev,relatime,,mode=0700,nosuid,uid=1000", MS_RDONLY, &f, &opts) == 0); |
f27b437b | 65 | assert_se(f == (MS_NOSUID|MS_NODEV|MS_RELATIME)); |
c79e88b3 | 66 | ASSERT_STREQ(opts, "size=1630748k,gid=1000,mode=0700,uid=1000"); |
f27b437b YW |
67 | opts = mfree(opts); |
68 | ||
9f563f27 | 69 | assert_se(mount_option_mangle("rw,exec,size=8143984k,nr_inodes=2035996,mode=0755", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, &f, &opts) == 0); |
f27b437b | 70 | assert_se(f == (MS_NOSUID|MS_NODEV)); |
c79e88b3 | 71 | ASSERT_STREQ(opts, "size=8143984k,nr_inodes=2035996,mode=0755"); |
f27b437b YW |
72 | opts = mfree(opts); |
73 | ||
74 | assert_se(mount_option_mangle("rw,relatime,fmask=0022,,,dmask=0022", MS_RDONLY, &f, &opts) == 0); | |
75 | assert_se(f == MS_RELATIME); | |
c79e88b3 | 76 | ASSERT_STREQ(opts, "fmask=0022,dmask=0022"); |
f27b437b YW |
77 | opts = mfree(opts); |
78 | ||
79 | assert_se(mount_option_mangle("rw,relatime,fmask=0022,dmask=0022,\"hogehoge", MS_RDONLY, &f, &opts) < 0); | |
9b23679e | 80 | |
9f563f27 | 81 | assert_se(mount_option_mangle("mode=01777,size=10%,nr_inodes=400k,uid=496107520,gid=496107520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\"", 0, &f, &opts) == 0); |
9b23679e | 82 | assert_se(f == 0); |
c79e88b3 | 83 | ASSERT_STREQ(opts, "mode=01777,size=10%,nr_inodes=400k,uid=496107520,gid=496107520,context=\"system_u:object_r:svirt_sandbox_file_t:s0:c0,c1\""); |
9b23679e | 84 | opts = mfree(opts); |
f27b437b YW |
85 | } |
86 | ||
51bb6a10 ZJS |
87 | static void test_mount_flags_to_string_one(unsigned long flags, const char *expected) { |
88 | _cleanup_free_ char *x = NULL; | |
89 | int r; | |
90 | ||
91 | r = mount_flags_to_string(flags, &x); | |
92 | log_info("flags: %#lX → %d/\"%s\"", flags, r, strnull(x)); | |
93 | assert_se(r >= 0); | |
c79e88b3 | 94 | ASSERT_STREQ(x, expected); |
51bb6a10 ZJS |
95 | } |
96 | ||
4f7452a8 | 97 | TEST(mount_flags_to_string) { |
51bb6a10 ZJS |
98 | test_mount_flags_to_string_one(0, "0"); |
99 | test_mount_flags_to_string_one(MS_RDONLY, "MS_RDONLY"); | |
100 | test_mount_flags_to_string_one(MS_NOSUID, "MS_NOSUID"); | |
101 | test_mount_flags_to_string_one(MS_NODEV, "MS_NODEV"); | |
102 | test_mount_flags_to_string_one(MS_NOEXEC, "MS_NOEXEC"); | |
103 | test_mount_flags_to_string_one(MS_SYNCHRONOUS, "MS_SYNCHRONOUS"); | |
104 | test_mount_flags_to_string_one(MS_REMOUNT, "MS_REMOUNT"); | |
105 | test_mount_flags_to_string_one(MS_MANDLOCK, "MS_MANDLOCK"); | |
106 | test_mount_flags_to_string_one(MS_DIRSYNC, "MS_DIRSYNC"); | |
107 | test_mount_flags_to_string_one(MS_NOSYMFOLLOW, "MS_NOSYMFOLLOW"); | |
108 | test_mount_flags_to_string_one(MS_NOATIME, "MS_NOATIME"); | |
109 | test_mount_flags_to_string_one(MS_NODIRATIME, "MS_NODIRATIME"); | |
110 | test_mount_flags_to_string_one(MS_BIND, "MS_BIND"); | |
111 | test_mount_flags_to_string_one(MS_MOVE, "MS_MOVE"); | |
112 | test_mount_flags_to_string_one(MS_REC, "MS_REC"); | |
113 | test_mount_flags_to_string_one(MS_SILENT, "MS_SILENT"); | |
114 | test_mount_flags_to_string_one(MS_POSIXACL, "MS_POSIXACL"); | |
115 | test_mount_flags_to_string_one(MS_UNBINDABLE, "MS_UNBINDABLE"); | |
116 | test_mount_flags_to_string_one(MS_PRIVATE, "MS_PRIVATE"); | |
117 | test_mount_flags_to_string_one(MS_SLAVE, "MS_SLAVE"); | |
118 | test_mount_flags_to_string_one(MS_SHARED, "MS_SHARED"); | |
119 | test_mount_flags_to_string_one(MS_RELATIME, "MS_RELATIME"); | |
120 | test_mount_flags_to_string_one(MS_KERNMOUNT, "MS_KERNMOUNT"); | |
121 | test_mount_flags_to_string_one(MS_I_VERSION, "MS_I_VERSION"); | |
122 | test_mount_flags_to_string_one(MS_STRICTATIME, "MS_STRICTATIME"); | |
123 | test_mount_flags_to_string_one(MS_LAZYTIME, "MS_LAZYTIME"); | |
124 | test_mount_flags_to_string_one(MS_LAZYTIME|MS_STRICTATIME, "MS_STRICTATIME|MS_LAZYTIME"); | |
125 | test_mount_flags_to_string_one(UINT_MAX, | |
126 | "MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_SYNCHRONOUS|MS_REMOUNT|" | |
127 | "MS_MANDLOCK|MS_DIRSYNC|MS_NOSYMFOLLOW|MS_NOATIME|MS_NODIRATIME|" | |
128 | "MS_BIND|MS_MOVE|MS_REC|MS_SILENT|MS_POSIXACL|MS_UNBINDABLE|" | |
129 | "MS_PRIVATE|MS_SLAVE|MS_SHARED|MS_RELATIME|MS_KERNMOUNT|" | |
130 | "MS_I_VERSION|MS_STRICTATIME|MS_LAZYTIME|fc000200"); | |
131 | } | |
132 | ||
4f7452a8 | 133 | TEST(bind_remount_recursive) { |
10cdbb83 LP |
134 | _cleanup_(rm_rf_physical_and_freep) char *tmp = NULL; |
135 | _cleanup_free_ char *subdir = NULL; | |
10cdbb83 | 136 | |
c75370cc LP |
137 | if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0) { |
138 | (void) log_tests_skipped("not running privileged"); | |
10cdbb83 LP |
139 | return; |
140 | } | |
141 | ||
142 | assert_se(mkdtemp_malloc("/tmp/XXXXXX", &tmp) >= 0); | |
143 | subdir = path_join(tmp, "subdir"); | |
144 | assert_se(subdir); | |
145 | assert_se(mkdir(subdir, 0755) >= 0); | |
146 | ||
147 | FOREACH_STRING(p, "/usr", "/sys", "/", tmp) { | |
148 | pid_t pid; | |
149 | ||
150 | pid = fork(); | |
151 | assert_se(pid >= 0); | |
152 | ||
153 | if (pid == 0) { | |
154 | struct statvfs svfs; | |
155 | /* child */ | |
156 | assert_se(detach_mount_namespace() >= 0); | |
157 | ||
158 | /* Check that the subdir is writable (it must be because it's in /tmp) */ | |
159 | assert_se(statvfs(subdir, &svfs) >= 0); | |
160 | assert_se(!FLAGS_SET(svfs.f_flag, ST_RDONLY)); | |
161 | ||
162 | /* Make the subdir a bind mount */ | |
163 | assert_se(mount_nofollow(subdir, subdir, NULL, MS_BIND|MS_REC, NULL) >= 0); | |
164 | ||
165 | /* Ensure it's still writable */ | |
166 | assert_se(statvfs(subdir, &svfs) >= 0); | |
167 | assert_se(!FLAGS_SET(svfs.f_flag, ST_RDONLY)); | |
168 | ||
169 | /* Now mark the path we currently run for read-only */ | |
874052c5 | 170 | assert_se(bind_remount_recursive(p, MS_RDONLY, MS_RDONLY, path_equal(p, "/sys") ? STRV_MAKE("/sys/kernel") : NULL) >= 0); |
10cdbb83 LP |
171 | |
172 | /* Ensure that this worked on the top-level */ | |
173 | assert_se(statvfs(p, &svfs) >= 0); | |
174 | assert_se(FLAGS_SET(svfs.f_flag, ST_RDONLY)); | |
175 | ||
176 | /* And ensure this had an effect on the subdir exactly if we are talking about a path above the subdir */ | |
177 | assert_se(statvfs(subdir, &svfs) >= 0); | |
178 | assert_se(FLAGS_SET(svfs.f_flag, ST_RDONLY) == !!path_startswith(subdir, p)); | |
179 | ||
180 | _exit(EXIT_SUCCESS); | |
181 | } | |
182 | ||
183 | assert_se(wait_for_terminate_and_check("test-remount-rec", pid, WAIT_LOG) == EXIT_SUCCESS); | |
184 | } | |
185 | } | |
186 | ||
4f7452a8 | 187 | TEST(bind_remount_one) { |
67d22a36 LP |
188 | pid_t pid; |
189 | ||
c75370cc LP |
190 | if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0) { |
191 | (void) log_tests_skipped("not running privileged"); | |
67d22a36 LP |
192 | return; |
193 | } | |
194 | ||
195 | pid = fork(); | |
196 | assert_se(pid >= 0); | |
197 | ||
198 | if (pid == 0) { | |
199 | /* child */ | |
200 | ||
201 | _cleanup_fclose_ FILE *proc_self_mountinfo = NULL; | |
202 | ||
203 | assert_se(detach_mount_namespace() >= 0); | |
204 | ||
205 | assert_se(fopen_unlocked("/proc/self/mountinfo", "re", &proc_self_mountinfo) >= 0); | |
206 | ||
207 | assert_se(bind_remount_one_with_mountinfo("/run", MS_RDONLY, MS_RDONLY, proc_self_mountinfo) >= 0); | |
4f5644db | 208 | assert_se(bind_remount_one_with_mountinfo("/run", MS_NOEXEC, MS_RDONLY|MS_NOEXEC, proc_self_mountinfo) >= 0); |
67d22a36 LP |
209 | assert_se(bind_remount_one_with_mountinfo("/proc/idontexist", MS_RDONLY, MS_RDONLY, proc_self_mountinfo) == -ENOENT); |
210 | assert_se(bind_remount_one_with_mountinfo("/proc/self", MS_RDONLY, MS_RDONLY, proc_self_mountinfo) == -EINVAL); | |
211 | assert_se(bind_remount_one_with_mountinfo("/", MS_RDONLY, MS_RDONLY, proc_self_mountinfo) >= 0); | |
212 | ||
213 | _exit(EXIT_SUCCESS); | |
214 | } | |
215 | ||
1927bcdc KN |
216 | assert_se(wait_for_terminate_and_check("test-remount-one-with-mountinfo", pid, WAIT_LOG) == EXIT_SUCCESS); |
217 | ||
218 | pid = fork(); | |
219 | assert_se(pid >= 0); | |
220 | ||
221 | if (pid == 0) { | |
222 | /* child */ | |
223 | ||
224 | assert_se(detach_mount_namespace() >= 0); | |
225 | ||
226 | assert_se(bind_remount_one("/run", MS_RDONLY, MS_RDONLY) >= 0); | |
227 | assert_se(bind_remount_one("/run", MS_NOEXEC, MS_RDONLY|MS_NOEXEC) >= 0); | |
228 | assert_se(bind_remount_one("/proc/idontexist", MS_RDONLY, MS_RDONLY) == -ENOENT); | |
229 | assert_se(bind_remount_one("/proc/self", MS_RDONLY, MS_RDONLY) == -EINVAL); | |
230 | assert_se(bind_remount_one("/", MS_RDONLY, MS_RDONLY) >= 0); | |
231 | ||
232 | _exit(EXIT_SUCCESS); | |
233 | } | |
234 | ||
67d22a36 LP |
235 | assert_se(wait_for_terminate_and_check("test-remount-one", pid, WAIT_LOG) == EXIT_SUCCESS); |
236 | } | |
237 | ||
4f7452a8 | 238 | TEST(make_mount_point_inode) { |
9c653536 ZJS |
239 | _cleanup_(rm_rf_physical_and_freep) char *d = NULL; |
240 | const char *src_file, *src_dir, *dst_file, *dst_dir; | |
241 | struct stat st; | |
242 | ||
9c653536 ZJS |
243 | assert_se(mkdtemp_malloc(NULL, &d) >= 0); |
244 | ||
245 | src_file = strjoina(d, "/src/file"); | |
246 | src_dir = strjoina(d, "/src/dir"); | |
247 | dst_file = strjoina(d, "/dst/file"); | |
248 | dst_dir = strjoina(d, "/dst/dir"); | |
249 | ||
250 | assert_se(mkdir_p(src_dir, 0755) >= 0); | |
251 | assert_se(mkdir_parents(dst_file, 0755) >= 0); | |
252 | assert_se(touch(src_file) >= 0); | |
253 | ||
254 | assert_se(make_mount_point_inode_from_path(src_file, dst_file, 0755) >= 0); | |
255 | assert_se(make_mount_point_inode_from_path(src_dir, dst_dir, 0755) >= 0); | |
256 | ||
257 | assert_se(stat(dst_dir, &st) == 0); | |
258 | assert_se(S_ISDIR(st.st_mode)); | |
259 | assert_se(stat(dst_file, &st) == 0); | |
260 | assert_se(S_ISREG(st.st_mode)); | |
261 | assert_se(!(S_IXUSR & st.st_mode)); | |
262 | assert_se(!(S_IXGRP & st.st_mode)); | |
263 | assert_se(!(S_IXOTH & st.st_mode)); | |
264 | ||
265 | assert_se(unlink(dst_file) == 0); | |
266 | assert_se(rmdir(dst_dir) == 0); | |
267 | ||
268 | assert_se(stat(src_file, &st) == 0); | |
269 | assert_se(make_mount_point_inode_from_stat(&st, dst_file, 0755) >= 0); | |
270 | assert_se(stat(src_dir, &st) == 0); | |
271 | assert_se(make_mount_point_inode_from_stat(&st, dst_dir, 0755) >= 0); | |
272 | ||
273 | assert_se(stat(dst_dir, &st) == 0); | |
274 | assert_se(S_ISDIR(st.st_mode)); | |
275 | assert_se(stat(dst_file, &st) == 0); | |
276 | assert_se(S_ISREG(st.st_mode)); | |
277 | assert_se(!(S_IXUSR & st.st_mode)); | |
278 | assert_se(!(S_IXGRP & st.st_mode)); | |
279 | assert_se(!(S_IXOTH & st.st_mode)); | |
280 | } | |
281 | ||
ea0f3289 LP |
282 | TEST(make_mount_switch_root) { |
283 | _cleanup_(rm_rf_physical_and_freep) char *t = NULL; | |
284 | _cleanup_free_ char *s = NULL; | |
285 | int r; | |
286 | ||
287 | if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0) { | |
288 | (void) log_tests_skipped("not running privileged"); | |
289 | return; | |
290 | } | |
291 | ||
292 | assert_se(mkdtemp_malloc(NULL, &t) >= 0); | |
293 | ||
294 | assert_se(asprintf(&s, "%s/somerandomname%" PRIu64, t, random_u64()) >= 0); | |
295 | assert_se(s); | |
296 | assert_se(touch(s) >= 0); | |
297 | ||
bb59b922 LP |
298 | struct { |
299 | const char *path; | |
300 | bool force_ms_move; | |
301 | } table[] = { | |
302 | { t, false }, | |
303 | { t, true }, | |
304 | { "/", false }, | |
305 | { "/", true }, | |
306 | }; | |
307 | ||
85471164 | 308 | FOREACH_ELEMENT(i, table) { |
4e9ef660 | 309 | r = safe_fork("(switch-root)", |
ea0f3289 LP |
310 | FORK_RESET_SIGNALS | |
311 | FORK_CLOSE_ALL_FDS | | |
e9ccae31 | 312 | FORK_DEATHSIG_SIGTERM | |
ea0f3289 LP |
313 | FORK_WAIT | |
314 | FORK_REOPEN_LOG | | |
315 | FORK_LOG | | |
316 | FORK_NEW_MOUNTNS | | |
317 | FORK_MOUNTNS_SLAVE, | |
318 | NULL); | |
319 | assert_se(r >= 0); | |
320 | ||
321 | if (r == 0) { | |
bb59b922 LP |
322 | assert_se(make_mount_point(i->path) >= 0); |
323 | assert_se(mount_switch_root_full(i->path, /* mount_propagation_flag= */ 0, i->force_ms_move) >= 0); | |
324 | ||
325 | if (!path_equal(i->path, "/")) { | |
326 | assert_se(access(ASSERT_PTR(strrchr(s, '/')), F_OK) >= 0); /* absolute */ | |
327 | assert_se(access(ASSERT_PTR(strrchr(s, '/')) + 1, F_OK) >= 0); /* relative */ | |
328 | assert_se(access(s, F_OK) < 0 && errno == ENOENT); /* doesn't exist in our new environment */ | |
329 | } | |
ea0f3289 LP |
330 | |
331 | _exit(EXIT_SUCCESS); | |
332 | } | |
333 | } | |
334 | } | |
335 | ||
4e9ef660 LP |
336 | TEST(umount_recursive) { |
337 | static const struct { | |
338 | const char *prefix; | |
339 | const char * const keep[3]; | |
340 | } test_table[] = { | |
341 | { | |
342 | .prefix = NULL, | |
343 | .keep = {}, | |
344 | }, | |
345 | { | |
346 | .prefix = "/run", | |
347 | .keep = {}, | |
348 | }, | |
349 | { | |
350 | .prefix = NULL, | |
351 | .keep = { "/dev/shm", NULL }, | |
352 | }, | |
353 | { | |
354 | .prefix = "/dev", | |
355 | .keep = { "/dev/pts", "/dev/shm", NULL }, | |
356 | }, | |
357 | }; | |
358 | ||
359 | int r; | |
360 | ||
85471164 | 361 | FOREACH_ELEMENT(t, test_table) { |
4e9ef660 LP |
362 | |
363 | r = safe_fork("(umount-rec)", | |
364 | FORK_RESET_SIGNALS | | |
365 | FORK_CLOSE_ALL_FDS | | |
e9ccae31 | 366 | FORK_DEATHSIG_SIGTERM | |
4e9ef660 LP |
367 | FORK_WAIT | |
368 | FORK_REOPEN_LOG | | |
369 | FORK_LOG | | |
370 | FORK_NEW_MOUNTNS | | |
371 | FORK_MOUNTNS_SLAVE, | |
372 | NULL); | |
373 | ||
13d84288 ZJS |
374 | if (ERRNO_IS_NEG_PRIVILEGE(r)) |
375 | return (void) log_notice("Skipping umount_recursive() test, lacking privileges"); | |
4e9ef660 LP |
376 | |
377 | assert_se(r >= 0); | |
378 | if (r == 0) { /* child */ | |
379 | _cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL; | |
380 | _cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL; | |
381 | _cleanup_fclose_ FILE *f = NULL; | |
382 | _cleanup_free_ char *k = NULL; | |
383 | ||
384 | /* Open /p/s/m file before we unmount everything (which might include /proc/) */ | |
385 | f = fopen("/proc/self/mountinfo", "re"); | |
386 | if (!f) { | |
9a27ef09 | 387 | log_error_errno(errno, "Failed to open /proc/self/mountinfo: %m"); |
4e9ef660 LP |
388 | _exit(EXIT_FAILURE); |
389 | } | |
390 | ||
391 | assert_se(k = strv_join((char**) t->keep, " ")); | |
392 | log_info("detaching just %s (keep: %s)", strna(t->prefix), strna(empty_to_null(k))); | |
393 | ||
394 | assert_se(umount_recursive_full(t->prefix, MNT_DETACH, (char**) t->keep) >= 0); | |
395 | ||
396 | r = libmount_parse("/proc/self/mountinfo", f, &table, &iter); | |
397 | if (r < 0) { | |
398 | log_error_errno(r, "Failed to parse /proc/self/mountinfo: %m"); | |
399 | _exit(EXIT_FAILURE); | |
400 | } | |
401 | ||
402 | for (;;) { | |
403 | struct libmnt_fs *fs; | |
404 | ||
405 | r = mnt_table_next_fs(table, iter, &fs); | |
406 | if (r == 1) | |
407 | break; | |
408 | if (r < 0) { | |
409 | log_error_errno(r, "Failed to get next entry from /proc/self/mountinfo: %m"); | |
410 | _exit(EXIT_FAILURE); | |
411 | } | |
412 | ||
413 | log_debug("left after complete umount: %s", mnt_fs_get_target(fs)); | |
414 | } | |
415 | ||
416 | _exit(EXIT_SUCCESS); | |
417 | } | |
418 | } | |
419 | } | |
420 | ||
f9ad896e LP |
421 | TEST(fd_make_mount_point) { |
422 | _cleanup_(rm_rf_physical_and_freep) char *t = NULL; | |
423 | _cleanup_free_ char *s = NULL; | |
424 | int r; | |
425 | ||
426 | if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0) { | |
427 | (void) log_tests_skipped("not running privileged"); | |
428 | return; | |
429 | } | |
430 | ||
431 | assert_se(mkdtemp_malloc(NULL, &t) >= 0); | |
432 | ||
433 | assert_se(asprintf(&s, "%s/somerandomname%" PRIu64, t, random_u64()) >= 0); | |
434 | assert_se(s); | |
435 | assert_se(mkdir(s, 0700) >= 0); | |
436 | ||
437 | r = safe_fork("(make_mount-point)", | |
438 | FORK_RESET_SIGNALS | | |
439 | FORK_CLOSE_ALL_FDS | | |
e9ccae31 | 440 | FORK_DEATHSIG_SIGTERM | |
f9ad896e LP |
441 | FORK_WAIT | |
442 | FORK_REOPEN_LOG | | |
443 | FORK_LOG | | |
444 | FORK_NEW_MOUNTNS | | |
445 | FORK_MOUNTNS_SLAVE, | |
446 | NULL); | |
447 | assert_se(r >= 0); | |
448 | ||
449 | if (r == 0) { | |
450 | _cleanup_close_ int fd = -EBADF, fd2 = -EBADF; | |
451 | ||
452 | fd = open(s, O_PATH|O_CLOEXEC); | |
453 | assert_se(fd >= 0); | |
454 | ||
455 | assert_se(fd_is_mount_point(fd, NULL, AT_SYMLINK_FOLLOW) == 0); | |
456 | ||
457 | assert_se(fd_make_mount_point(fd) > 0); | |
458 | ||
459 | /* Reopen the inode so that we end up on the new mount */ | |
460 | fd2 = open(s, O_PATH|O_CLOEXEC); | |
461 | ||
462 | assert_se(fd_is_mount_point(fd2, NULL, AT_SYMLINK_FOLLOW) > 0); | |
463 | ||
464 | assert_se(fd_make_mount_point(fd2) == 0); | |
465 | ||
466 | _exit(EXIT_SUCCESS); | |
467 | } | |
468 | } | |
469 | ||
1b618bf1 LP |
470 | TEST(bind_mount_submounts) { |
471 | _cleanup_(rmdir_and_freep) char *a = NULL, *b = NULL; | |
472 | _cleanup_free_ char *x = NULL; | |
473 | int r; | |
474 | ||
475 | assert_se(mkdtemp_malloc(NULL, &a) >= 0); | |
476 | r = mount_nofollow_verbose(LOG_INFO, "tmpfs", a, "tmpfs", 0, NULL); | |
13d84288 ZJS |
477 | if (ERRNO_IS_NEG_PRIVILEGE(r)) |
478 | return (void) log_tests_skipped("Skipping bind_mount_submounts() test, lacking privileges"); | |
479 | ||
1b618bf1 LP |
480 | assert_se(r >= 0); |
481 | ||
482 | assert_se(x = path_join(a, "foo")); | |
483 | assert_se(touch(x) >= 0); | |
484 | free(x); | |
485 | ||
486 | assert_se(x = path_join(a, "x")); | |
487 | assert_se(mkdir(x, 0755) >= 0); | |
488 | assert_se(mount_nofollow_verbose(LOG_INFO, "tmpfs", x, "tmpfs", 0, NULL) >= 0); | |
489 | free(x); | |
490 | ||
491 | assert_se(x = path_join(a, "x/xx")); | |
492 | assert_se(touch(x) >= 0); | |
493 | free(x); | |
494 | ||
495 | assert_se(x = path_join(a, "y")); | |
496 | assert_se(mkdir(x, 0755) >= 0); | |
497 | assert_se(mount_nofollow_verbose(LOG_INFO, "tmpfs", x, "tmpfs", 0, NULL) >= 0); | |
498 | free(x); | |
499 | ||
500 | assert_se(x = path_join(a, "y/yy")); | |
501 | assert_se(touch(x) >= 0); | |
502 | free(x); | |
503 | ||
504 | assert_se(mkdtemp_malloc(NULL, &b) >= 0); | |
505 | assert_se(mount_nofollow_verbose(LOG_INFO, "tmpfs", b, "tmpfs", 0, NULL) >= 0); | |
506 | ||
507 | assert_se(x = path_join(b, "x")); | |
508 | assert_se(mkdir(x, 0755) >= 0); | |
509 | free(x); | |
510 | ||
511 | assert_se(x = path_join(b, "y")); | |
512 | assert_se(mkdir(x, 0755) >= 0); | |
513 | free(x); | |
514 | ||
515 | assert_se(bind_mount_submounts(a, b) >= 0); | |
516 | ||
517 | assert_se(x = path_join(b, "foo")); | |
518 | assert_se(access(x, F_OK) < 0 && errno == ENOENT); | |
519 | free(x); | |
520 | ||
521 | assert_se(x = path_join(b, "x/xx")); | |
522 | assert_se(access(x, F_OK) >= 0); | |
523 | free(x); | |
524 | ||
525 | assert_se(x = path_join(b, "y/yy")); | |
526 | assert_se(access(x, F_OK) >= 0); | |
527 | free(x); | |
528 | ||
529 | assert_se(x = path_join(b, "x")); | |
b409aacb | 530 | assert_se(path_is_mount_point(x) > 0); |
1b618bf1 LP |
531 | free(x); |
532 | ||
533 | assert_se(x = path_join(b, "y")); | |
b409aacb | 534 | assert_se(path_is_mount_point(x) > 0); |
1b618bf1 LP |
535 | |
536 | assert_se(umount_recursive(a, 0) >= 0); | |
537 | assert_se(umount_recursive(b, 0) >= 0); | |
538 | } | |
539 | ||
2b60ce54 | 540 | DEFINE_TEST_MAIN(LOG_DEBUG); |