]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
e3478379 | 2 | /*** |
96b2fb93 | 3 | Copyright © 2010 ProFUSION embedded systems |
e3478379 FF |
4 | ***/ |
5 | ||
e3478379 | 6 | #include <fcntl.h> |
e3478379 | 7 | #include <sys/mount.h> |
ca78ad1d | 8 | #include <unistd.h> |
e3478379 | 9 | |
b5efdb8a | 10 | #include "alloc-util.h" |
f461a28d | 11 | #include "chase.h" |
86b9a3e3 | 12 | #include "constants.h" |
20596876 | 13 | #include "dirent-util.h" |
06b5f13d | 14 | #include "errno-util.h" |
3ffd4af2 | 15 | #include "fd-util.h" |
20596876 | 16 | #include "fileio.h" |
06b5f13d | 17 | #include "format-util.h" |
20596876 | 18 | #include "fs-util.h" |
471b48ed | 19 | #include "fstab-util.h" |
fb36b133 | 20 | #include "libmount-util.h" |
8aa304d3 | 21 | #include "log.h" |
6dc68a00 | 22 | #include "mkdir.h" |
e3478379 | 23 | #include "mount-setup.h" |
18c528e9 | 24 | #include "mount-util.h" |
049af8ad | 25 | #include "mountpoint-util.h" |
20596876 | 26 | #include "parse-util.h" |
dccca82b | 27 | #include "process-util.h" |
6dc68a00 | 28 | #include "random-util.h" |
d5641e0d | 29 | #include "signal-util.h" |
06b5f13d DDM |
30 | #include "stat-util.h" |
31 | #include "string-util.h" | |
3ffd4af2 | 32 | #include "umount.h" |
024f268d | 33 | #include "virt.h" |
e3478379 | 34 | |
f5fcacdf | 35 | static void mount_point_free(MountPoint **head, MountPoint *m) { |
12aad1d0 LP |
36 | assert(head); |
37 | assert(m); | |
e3478379 | 38 | |
71fda00f | 39 | LIST_REMOVE(mount_point, *head, m); |
12aad1d0 LP |
40 | |
41 | free(m->path); | |
3bc341be | 42 | free(m->remount_options); |
12aad1d0 | 43 | free(m); |
e3478379 FF |
44 | } |
45 | ||
6fa392bf | 46 | void mount_points_list_free(MountPoint **head) { |
12aad1d0 LP |
47 | assert(head); |
48 | ||
49 | while (*head) | |
50 | mount_point_free(head, *head); | |
e3478379 FF |
51 | } |
52 | ||
93f6cee9 | 53 | int mount_points_list_get(FILE *f, MountPoint **head) { |
13dcfe46 ZJS |
54 | _cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL; |
55 | _cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL; | |
527b7a42 | 56 | int r; |
e3478379 | 57 | |
12aad1d0 LP |
58 | assert(head); |
59 | ||
93f6cee9 | 60 | r = libmount_parse_mountinfo(f, &table, &iter); |
95b862b0 | 61 | if (r < 0) |
93f6cee9 | 62 | return log_error_errno(r, "Failed to parse /proc/self/mountinfo: %m"); |
95b862b0 ZJS |
63 | |
64 | for (;;) { | |
c905aaf6 | 65 | _cleanup_free_ char *options = NULL, *remount_options = NULL; |
95b862b0 | 66 | struct libmnt_fs *fs; |
66c91c3a | 67 | const char *path, *fstype; |
95b862b0 | 68 | unsigned long remount_flags = 0u; |
cef2181c | 69 | bool try_remount_ro, is_api_vfs, is_network; |
9d1b2b22 | 70 | _cleanup_free_ MountPoint *m = NULL; |
e3478379 | 71 | |
13dcfe46 | 72 | r = mnt_table_next_fs(table, iter, &fs); |
c905aaf6 | 73 | if (r == 1) /* EOF */ |
95b862b0 | 74 | break; |
527b7a42 | 75 | if (r < 0) |
93f6cee9 | 76 | return log_error_errno(r, "Failed to get next entry from /proc/self/mountinfo: %m"); |
95b862b0 ZJS |
77 | |
78 | path = mnt_fs_get_target(fs); | |
79 | if (!path) | |
80 | continue; | |
81 | ||
95b862b0 | 82 | fstype = mnt_fs_get_fstype(fs); |
e3478379 | 83 | |
83f3bf4b LP |
84 | /* Combine the generic VFS options with the FS-specific options. Duplicates are not a problem |
85 | * here, because the only options that should come up twice are typically ro/rw, which are | |
86 | * turned into MS_RDONLY or the inversion of it. | |
66c91c3a | 87 | * |
83f3bf4b LP |
88 | * Even if there are duplicates later in mount_option_mangle() they shouldn't hurt anyways as |
89 | * they override each other. */ | |
c2bc710b | 90 | if (!strextend_with_separator(&options, ",", mnt_fs_get_vfs_options(fs))) |
66c91c3a | 91 | return log_oom(); |
c2bc710b | 92 | if (!strextend_with_separator(&options, ",", mnt_fs_get_fs_options(fs))) |
66c91c3a | 93 | return log_oom(); |
94 | ||
83f3bf4b LP |
95 | /* Ignore mount points we can't unmount because they are API or because we are keeping them |
96 | * open (like /dev/console). Also, ignore all mounts below API file systems, since they are | |
97 | * likely virtual too, and hence not worth spending time on. Also, in unprivileged containers | |
98 | * we might lack the rights to unmount these things, hence don't bother. */ | |
9d1b2b22 ZJS |
99 | if (mount_point_is_api(path) || |
100 | mount_point_ignore(path) || | |
0fb08bd5 | 101 | path_below_api_vfs(path)) |
2054a5b8 | 102 | continue; |
2054a5b8 | 103 | |
cef2181c | 104 | is_network = fstype_is_network(fstype); |
f11efcbe LP |
105 | is_api_vfs = fstype_is_api_vfs(fstype); |
106 | ||
83f3bf4b LP |
107 | /* If we are in a container, don't attempt to read-only mount anything as that brings no real |
108 | * benefits, but might confuse the host, as we remount the superblock here, not the bind | |
109 | * mount. | |
1d62d22d | 110 | * |
83f3bf4b LP |
111 | * If the filesystem is a network fs, also skip the remount. It brings no value (we cannot |
112 | * leave a "dirty fs") and could hang if the network is down. Note that umount2() is more | |
113 | * careful and will not hang because of the network being down. */ | |
95b862b0 | 114 | try_remount_ro = detect_container() <= 0 && |
cef2181c | 115 | !is_network && |
f11efcbe | 116 | !is_api_vfs && |
95b862b0 ZJS |
117 | !fstype_is_ro(fstype) && |
118 | !fstab_test_yes_no_option(options, "ro\0rw\0"); | |
3bc341be | 119 | |
95b862b0 | 120 | if (try_remount_ro) { |
83f3bf4b LP |
121 | /* mount(2) states that mount flags and options need to be exactly the same as they |
122 | * were when the filesystem was mounted, except for the desired changes. So we | |
123 | * reconstruct both here and adjust them for the later remount call too. */ | |
3bc341be | 124 | |
95b862b0 ZJS |
125 | r = mnt_fs_get_propagation(fs, &remount_flags); |
126 | if (r < 0) { | |
127 | log_warning_errno(r, "mnt_fs_get_propagation() failed for %s, ignoring: %m", path); | |
128 | continue; | |
129 | } | |
3bc341be | 130 | |
95b862b0 ZJS |
131 | r = mount_option_mangle(options, remount_flags, &remount_flags, &remount_options); |
132 | if (r < 0) { | |
133 | log_warning_errno(r, "mount_option_mangle failed for %s, ignoring: %m", path); | |
134 | continue; | |
135 | } | |
3bc341be JJ |
136 | |
137 | /* MS_BIND is special. If it is provided it will only make the mount-point | |
138 | * read-only. If left out, the super block itself is remounted, which we want. */ | |
95b862b0 | 139 | remount_flags = (remount_flags|MS_REMOUNT|MS_RDONLY) & ~MS_BIND; |
3bc341be | 140 | } |
471b48ed | 141 | |
efc90b98 | 142 | m = new(MountPoint, 1); |
95b862b0 ZJS |
143 | if (!m) |
144 | return log_oom(); | |
145 | ||
6dc68a00 VD |
146 | r = libmount_is_leaf(table, fs); |
147 | if (r < 0) | |
93f6cee9 | 148 | return log_error_errno(r, "Failed to get children mounts for %s from /proc/self/mountinfo: %m", path); |
6dc68a00 VD |
149 | bool leaf = r; |
150 | ||
efc90b98 LP |
151 | *m = (MountPoint) { |
152 | .remount_options = remount_options, | |
153 | .remount_flags = remount_flags, | |
154 | .try_remount_ro = try_remount_ro, | |
f11efcbe LP |
155 | |
156 | /* Unmount sysfs/procfs/… lazily, since syncing doesn't matter there, and it's OK if | |
157 | * something keeps an fd open to it. */ | |
158 | .umount_lazily = is_api_vfs, | |
cef2181c AAF |
159 | |
160 | /* If a mount point is not a leaf, moving it would invalidate our mount table. | |
161 | * If a mount point is on the network and the network is down, it can hang and block | |
162 | * the shutdown. */ | |
163 | .umount_move_if_busy = leaf && !is_network, | |
efc90b98 LP |
164 | }; |
165 | ||
9d1b2b22 ZJS |
166 | m->path = strdup(path); |
167 | if (!m->path) | |
168 | return log_oom(); | |
169 | ||
efc90b98 | 170 | TAKE_PTR(remount_options); |
95b862b0 | 171 | |
9d1b2b22 | 172 | LIST_PREPEND(mount_point, *head, TAKE_PTR(m)); |
e3478379 FF |
173 | } |
174 | ||
c3544e8d | 175 | return 0; |
e3478379 FF |
176 | } |
177 | ||
c826cd3f | 178 | static bool nonunmountable_path(const char *path) { |
798e8117 LP |
179 | assert(path); |
180 | ||
181 | return PATH_IN_SET(path, "/", "/usr") || | |
182 | path_startswith(path, "/run/initramfs"); | |
c826cd3f ZJS |
183 | } |
184 | ||
20596876 | 185 | static void log_umount_blockers(const char *mnt) { |
05768ae3 LP |
186 | _cleanup_free_ char *blockers = NULL; |
187 | int r; | |
188 | ||
20596876 JJ |
189 | _cleanup_closedir_ DIR *dir = opendir("/proc"); |
190 | if (!dir) | |
42ba9974 | 191 | return (void) log_warning_errno(errno, "Failed to open %s: %m", "/proc/"); |
20596876 JJ |
192 | |
193 | FOREACH_DIRENT_ALL(de, dir, break) { | |
194 | if (!IN_SET(de->d_type, DT_DIR, DT_UNKNOWN)) | |
195 | continue; | |
196 | ||
197 | pid_t pid; | |
198 | if (parse_pid(de->d_name, &pid) < 0) | |
199 | continue; | |
200 | ||
05768ae3 LP |
201 | _cleanup_free_ char *fdp = path_join(de->d_name, "fd"); |
202 | if (!fdp) | |
203 | return (void) log_oom(); | |
20596876 | 204 | |
05768ae3 LP |
205 | _cleanup_closedir_ DIR *fd_dir = xopendirat(dirfd(dir), fdp, 0); |
206 | if (!fd_dir) { | |
207 | if (errno != ENOENT) /* process gone by now? */ | |
208 | log_debug_errno(errno, "Failed to open /proc/%s/, ignoring: %m",fdp); | |
20596876 | 209 | continue; |
05768ae3 | 210 | } |
20596876 | 211 | |
05768ae3 | 212 | bool culprit = false; |
20596876 | 213 | FOREACH_DIRENT(fd_de, fd_dir, break) { |
05768ae3 | 214 | _cleanup_free_ char *open_file = NULL; |
20596876 | 215 | |
05768ae3 LP |
216 | r = readlinkat_malloc(dirfd(fd_dir), fd_de->d_name, &open_file); |
217 | if (r < 0) { | |
218 | if (r != -ENOENT) /* fd closed by now */ | |
219 | log_debug_errno(r, "Failed to read link /proc/%s/%s, ignoring: %m", fdp, fd_de->d_name); | |
20596876 | 220 | continue; |
05768ae3 | 221 | } |
20596876 | 222 | |
05768ae3 LP |
223 | if (path_startswith(open_file, mnt)) { |
224 | culprit = true; | |
225 | break; | |
226 | } | |
227 | } | |
20596876 | 228 | |
05768ae3 LP |
229 | if (!culprit) |
230 | continue; | |
20596876 | 231 | |
05768ae3 | 232 | _cleanup_free_ char *comm = NULL; |
d7d74854 | 233 | r = pid_get_comm(pid, &comm); |
05768ae3 LP |
234 | if (r < 0) { |
235 | if (r != -ESRCH) /* process gone by now */ | |
236 | log_debug_errno(r, "Failed to read process name of PID " PID_FMT ": %m", pid); | |
237 | continue; | |
238 | } | |
20596876 | 239 | |
05768ae3 LP |
240 | if (!strextend_with_separator(&blockers, ", ", comm)) |
241 | return (void) log_oom(); | |
20596876 | 242 | |
05768ae3 LP |
243 | if (!strextend(&blockers, "(", de->d_name, ")")) |
244 | return (void) log_oom(); | |
20596876 JJ |
245 | } |
246 | ||
247 | if (blockers) | |
248 | log_warning("Unmounting '%s' blocked by: %s", mnt, blockers); | |
249 | } | |
250 | ||
5125b677 | 251 | static int remount_with_timeout(MountPoint *m, bool last_try) { |
71136404 | 252 | _cleanup_close_pair_ int pfd[2] = EBADF_PAIR; |
b293bb23 | 253 | _cleanup_(sigkill_nowaitp) pid_t pid = 0; |
d5641e0d KW |
254 | int r; |
255 | ||
256 | BLOCK_SIGNALS(SIGCHLD); | |
257 | ||
0494cae0 | 258 | assert(m); |
0494cae0 | 259 | |
b293bb23 LP |
260 | r = pipe2(pfd, O_CLOEXEC|O_NONBLOCK); |
261 | if (r < 0) | |
262 | return r; | |
263 | ||
273d76f4 YW |
264 | /* Due to the possibility of a remount operation hanging, we fork a child process and set a |
265 | * timeout. If the timeout lapses, the assumption is that the particular remount failed. */ | |
911f8f01 YW |
266 | r = safe_fork_full("(sd-remount)", |
267 | NULL, | |
268 | pfd, ELEMENTSOF(pfd), | |
269 | FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_REOPEN_LOG, &pid); | |
4c253ed1 | 270 | if (r < 0) |
b6e1fff1 | 271 | return r; |
4c253ed1 | 272 | if (r == 0) { |
b293bb23 LP |
273 | pfd[0] = safe_close(pfd[0]); |
274 | ||
b85c1905 | 275 | log_info("Remounting '%s' read-only with options '%s'.", m->path, strempty(m->remount_options)); |
d5641e0d KW |
276 | |
277 | /* Start the mount operation here in the child */ | |
3bc341be | 278 | r = mount(NULL, m->path, NULL, m->remount_flags, m->remount_options); |
d5641e0d | 279 | if (r < 0) |
5125b677 JJ |
280 | log_full_errno(last_try ? LOG_ERR : LOG_INFO, |
281 | errno, | |
282 | "Failed to remount '%s' read-only: %m", | |
283 | m->path); | |
d5641e0d | 284 | |
f72a64f3 | 285 | report_errno_and_exit(pfd[1], r); |
d5641e0d KW |
286 | } |
287 | ||
b293bb23 LP |
288 | pfd[1] = safe_close(pfd[1]); |
289 | ||
d5641e0d | 290 | r = wait_for_terminate_with_timeout(pid, DEFAULT_TIMEOUT_USEC); |
b293bb23 | 291 | if (r == -ETIMEDOUT) |
00adeed9 | 292 | log_error_errno(r, "Remounting '%s' timed out, issuing SIGKILL to PID " PID_FMT ".", m->path, pid); |
b293bb23 LP |
293 | else if (r == -EPROTO) { |
294 | /* Try to read error code from child */ | |
295 | if (read(pfd[0], &r, sizeof(r)) == sizeof(r)) | |
296 | log_debug_errno(r, "Remounting '%s' failed abnormally, child process " PID_FMT " failed: %m", m->path, pid); | |
297 | else | |
5afaf407 | 298 | r = log_debug_errno(EPROTO, "Remounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid); |
b293bb23 LP |
299 | TAKE_PID(pid); /* child exited (just not as we expected) hence don't kill anymore */ |
300 | } else if (r < 0) | |
00adeed9 | 301 | log_error_errno(r, "Remounting '%s' failed unexpectedly, couldn't wait for child process " PID_FMT ": %m", m->path, pid); |
d5641e0d KW |
302 | |
303 | return r; | |
304 | } | |
305 | ||
5125b677 | 306 | static int umount_with_timeout(MountPoint *m, bool last_try) { |
71136404 | 307 | _cleanup_close_pair_ int pfd[2] = EBADF_PAIR; |
b293bb23 | 308 | _cleanup_(sigkill_nowaitp) pid_t pid = 0; |
d5641e0d KW |
309 | int r; |
310 | ||
311 | BLOCK_SIGNALS(SIGCHLD); | |
312 | ||
0494cae0 JJ |
313 | assert(m); |
314 | ||
b293bb23 LP |
315 | r = pipe2(pfd, O_CLOEXEC|O_NONBLOCK); |
316 | if (r < 0) | |
317 | return r; | |
318 | ||
273d76f4 YW |
319 | /* Due to the possibility of a umount operation hanging, we fork a child process and set a |
320 | * timeout. If the timeout lapses, the assumption is that the particular umount failed. */ | |
911f8f01 YW |
321 | r = safe_fork_full("(sd-umount)", |
322 | NULL, | |
323 | pfd, ELEMENTSOF(pfd), | |
324 | FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_REOPEN_LOG, &pid); | |
4c253ed1 | 325 | if (r < 0) |
b6e1fff1 | 326 | return r; |
4c253ed1 | 327 | if (r == 0) { |
b293bb23 LP |
328 | pfd[0] = safe_close(pfd[0]); |
329 | ||
d5641e0d KW |
330 | log_info("Unmounting '%s'.", m->path); |
331 | ||
83f3bf4b LP |
332 | /* Start the mount operation here in the child Using MNT_FORCE causes some filesystems |
333 | * (e.g. FUSE and NFS and other network filesystems) to abort any pending requests and return | |
334 | * -EIO rather than blocking indefinitely. If the filesysten is "busy", this may allow | |
335 | * processes to die, thus making the filesystem less busy so the unmount might succeed | |
336 | * (rather than return EBUSY). */ | |
f11efcbe LP |
337 | r = RET_NERRNO(umount2(m->path, |
338 | UMOUNT_NOFOLLOW | /* Don't follow symlinks: this should never happen unless our mount list was wrong */ | |
339 | (m->umount_lazily ? MNT_DETACH : MNT_FORCE))); | |
20596876 JJ |
340 | if (r < 0) { |
341 | log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Failed to unmount %s: %m", m->path); | |
342 | ||
343 | if (r == -EBUSY && last_try) | |
344 | log_umount_blockers(m->path); | |
345 | } | |
d5641e0d | 346 | |
f72a64f3 | 347 | report_errno_and_exit(pfd[1], r); |
d5641e0d KW |
348 | } |
349 | ||
b293bb23 LP |
350 | pfd[1] = safe_close(pfd[1]); |
351 | ||
d5641e0d | 352 | r = wait_for_terminate_with_timeout(pid, DEFAULT_TIMEOUT_USEC); |
b293bb23 | 353 | if (r == -ETIMEDOUT) |
00adeed9 | 354 | log_error_errno(r, "Unmounting '%s' timed out, issuing SIGKILL to PID " PID_FMT ".", m->path, pid); |
b293bb23 LP |
355 | else if (r == -EPROTO) { |
356 | /* Try to read error code from child */ | |
357 | if (read(pfd[0], &r, sizeof(r)) == sizeof(r)) | |
358 | log_debug_errno(r, "Unmounting '%s' failed abnormally, child process " PID_FMT " failed: %m", m->path, pid); | |
359 | else | |
360 | r = log_debug_errno(EPROTO, "Unmounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid); | |
361 | TAKE_PID(pid); /* It died, but abnormally, no purpose in killing */ | |
362 | } else if (r < 0) | |
00adeed9 | 363 | log_error_errno(r, "Unmounting '%s' failed unexpectedly, couldn't wait for child process " PID_FMT ": %m", m->path, pid); |
d5641e0d KW |
364 | |
365 | return r; | |
366 | } | |
367 | ||
49f80dce LP |
368 | /* This includes remounting readonly, which changes the kernel mount options. Therefore the list passed to |
369 | * this function is invalidated, and should not be reused. */ | |
5125b677 | 370 | static int mount_points_list_umount(MountPoint **head, bool *changed, bool last_try) { |
6dc68a00 VD |
371 | int n_failed = 0, r; |
372 | _cleanup_free_ char *resolved_mounts_path = NULL; | |
e3478379 | 373 | |
12aad1d0 | 374 | assert(head); |
0494cae0 | 375 | assert(changed); |
12aad1d0 | 376 | |
116e6d96 | 377 | LIST_FOREACH(mount_point, m, *head) { |
1d62d22d | 378 | if (m->try_remount_ro) { |
49f80dce | 379 | /* We always try to remount directories read-only first, before we go on and umount |
93bd1577 LP |
380 | * them. |
381 | * | |
49f80dce LP |
382 | * Mount points can be stacked. If a mount point is stacked below / or /usr, we |
383 | * cannot umount or remount it directly, since there is no way to refer to the | |
384 | * underlying mount. There's nothing we can do about it for the general case, but we | |
385 | * can do something about it if it is aliased somewhere else via a bind mount. If we | |
386 | * explicitly remount the super block of that alias read-only we hence should be | |
387 | * relatively safe regarding keeping a dirty fs we cannot otherwise see. | |
d5641e0d | 388 | * |
49f80dce LP |
389 | * Since the remount can hang in the instance of remote filesystems, we remount |
390 | * asynchronously and skip the subsequent umount if it fails. */ | |
5125b677 | 391 | if (remount_with_timeout(m, last_try) < 0) { |
8645ffd1 JJ |
392 | /* Remount failed, but try unmounting anyway, |
393 | * unless this is a mount point we want to skip. */ | |
394 | if (nonunmountable_path(m->path)) { | |
c826cd3f | 395 | n_failed++; |
8645ffd1 JJ |
396 | continue; |
397 | } | |
c826cd3f | 398 | } |
93bd1577 LP |
399 | } |
400 | ||
49f80dce LP |
401 | /* Skip / and /usr since we cannot unmount that anyway, since we are running from it. They |
402 | * have already been remounted ro. */ | |
c826cd3f | 403 | if (nonunmountable_path(m->path)) |
e3478379 FF |
404 | continue; |
405 | ||
d5641e0d | 406 | /* Trying to umount */ |
6dc68a00 VD |
407 | r = umount_with_timeout(m, last_try); |
408 | if (r < 0) | |
d5641e0d | 409 | n_failed++; |
0494cae0 JJ |
410 | else |
411 | *changed = true; | |
6dc68a00 VD |
412 | |
413 | /* If a mount is busy, we move it to not keep parent mount points busy. | |
6dc68a00 VD |
414 | * More moving will occur in next iteration with a fresh mount table. |
415 | */ | |
cef2181c | 416 | if (r != -EBUSY || !m->umount_move_if_busy) |
6dc68a00 VD |
417 | continue; |
418 | ||
419 | _cleanup_free_ char *dirname = NULL; | |
420 | ||
421 | r = path_extract_directory(m->path, &dirname); | |
422 | if (r < 0) { | |
423 | n_failed++; | |
424 | log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Cannot find directory for %s: %m", m->path); | |
425 | continue; | |
426 | } | |
427 | ||
428 | /* We need to canonicalize /run/shutdown/mounts. We cannot compare inodes, since /run | |
429 | * might be bind mounted somewhere we want to unmount. And we need to move all mounts in | |
430 | * /run/shutdown/mounts from there. | |
431 | */ | |
432 | if (!resolved_mounts_path) | |
f461a28d | 433 | (void) chase("/run/shutdown/mounts", NULL, 0, &resolved_mounts_path, NULL); |
6dc68a00 VD |
434 | if (!path_equal(dirname, resolved_mounts_path)) { |
435 | char newpath[STRLEN("/run/shutdown/mounts/") + 16 + 1]; | |
436 | ||
437 | xsprintf(newpath, "/run/shutdown/mounts/%016" PRIx64, random_u64()); | |
438 | ||
439 | /* on error of is_dir, assume directory */ | |
440 | if (is_dir(m->path, true) != 0) { | |
441 | r = mkdir_p(newpath, 0000); | |
442 | if (r < 0) { | |
443 | log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Could not create directory %s: %m", newpath); | |
444 | continue; | |
445 | } | |
446 | } else { | |
447 | r = touch_file(newpath, /* parents= */ true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0700); | |
448 | if (r < 0) { | |
449 | log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Could not create file %s: %m", newpath); | |
450 | continue; | |
451 | } | |
452 | } | |
453 | ||
454 | log_info("Moving mount %s to %s.", m->path, newpath); | |
455 | ||
456 | r = RET_NERRNO(mount(m->path, newpath, NULL, MS_MOVE, NULL)); | |
457 | if (r < 0) { | |
458 | n_failed++; | |
459 | log_full_errno(last_try ? LOG_ERR : LOG_INFO, r, "Could not move %s to %s: %m", m->path, newpath); | |
460 | } else | |
461 | *changed = true; | |
462 | } | |
e3478379 FF |
463 | } |
464 | ||
12aad1d0 | 465 | return n_failed; |
e3478379 FF |
466 | } |
467 | ||
5125b677 | 468 | static int umount_all_once(bool *changed, bool last_try) { |
a6dcd229 | 469 | _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head); |
88287615 | 470 | int r; |
e3478379 | 471 | |
0494cae0 JJ |
472 | assert(changed); |
473 | ||
71fda00f | 474 | LIST_HEAD_INIT(mp_list_head); |
6fa392bf | 475 | r = mount_points_list_get(NULL, &mp_list_head); |
e3478379 | 476 | if (r < 0) |
a6dcd229 | 477 | return r; |
116e6d96 | 478 | |
5125b677 | 479 | return mount_points_list_umount(&mp_list_head, changed, last_try); |
116e6d96 AJ |
480 | } |
481 | ||
5125b677 | 482 | int umount_all(bool *changed, bool last_try) { |
116e6d96 AJ |
483 | bool umount_changed; |
484 | int r; | |
485 | ||
0494cae0 JJ |
486 | assert(changed); |
487 | ||
83f3bf4b LP |
488 | /* Retry umount, until nothing can be umounted anymore. Mounts are processed in order, newest |
489 | * first. The retries are needed when an old mount has been moved, to a path inside a newer mount. */ | |
6f7f51f7 HH |
490 | do { |
491 | umount_changed = false; | |
3e085b6c | 492 | |
5125b677 | 493 | r = umount_all_once(&umount_changed, last_try); |
6f7f51f7 HH |
494 | if (umount_changed) |
495 | *changed = true; | |
3e085b6c LP |
496 | } while (umount_changed); |
497 | ||
e3478379 FF |
498 | return r; |
499 | } |