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