Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : : /***
3 : : Copyright © 2010 ProFUSION embedded systems
4 : : ***/
5 : :
6 : : #include <errno.h>
7 : : #include <fcntl.h>
8 : : #include <linux/dm-ioctl.h>
9 : : #include <linux/loop.h>
10 : : #include <string.h>
11 : : #include <sys/mount.h>
12 : : #include <sys/swap.h>
13 : : #include <sys/stat.h>
14 : : #include <sys/types.h>
15 : : #include <unistd.h>
16 : :
17 : : #include "sd-device.h"
18 : :
19 : : #include "alloc-util.h"
20 : : #include "blockdev-util.h"
21 : : #include "def.h"
22 : : #include "device-util.h"
23 : : #include "escape.h"
24 : : #include "fd-util.h"
25 : : #include "fstab-util.h"
26 : : #include "libmount-util.h"
27 : : #include "mount-setup.h"
28 : : #include "mount-util.h"
29 : : #include "mountpoint-util.h"
30 : : #include "path-util.h"
31 : : #include "process-util.h"
32 : : #include "signal-util.h"
33 : : #include "string-util.h"
34 : : #include "strv.h"
35 : : #include "umount.h"
36 : : #include "util.h"
37 : : #include "virt.h"
38 : :
39 : 132 : static void mount_point_free(MountPoint **head, MountPoint *m) {
40 [ - + ]: 132 : assert(head);
41 [ - + ]: 132 : assert(m);
42 : :
43 [ - + + + : 132 : LIST_REMOVE(mount_point, *head, m);
- + - + ]
44 : :
45 : 132 : free(m->path);
46 : 132 : free(m->remount_options);
47 : 132 : free(m);
48 : 132 : }
49 : :
50 : 24 : void mount_points_list_free(MountPoint **head) {
51 [ - + ]: 24 : assert(head);
52 : :
53 [ + + ]: 156 : while (*head)
54 : 132 : mount_point_free(head, *head);
55 : 24 : }
56 : :
57 : 16 : int mount_points_list_get(const char *mountinfo, MountPoint **head) {
58 : 16 : _cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL;
59 : 16 : _cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL;
60 : : int r;
61 : :
62 [ - + ]: 16 : assert(head);
63 : :
64 : 16 : r = libmount_parse(mountinfo, NULL, &table, &iter);
65 [ - + ]: 16 : if (r < 0)
66 [ # # ]: 0 : return log_error_errno(r, "Failed to parse %s: %m", mountinfo);
67 : :
68 : 352 : for (;;) {
69 : : struct libmnt_fs *fs;
70 : : const char *path, *fstype;
71 [ + + - + ]: 368 : _cleanup_free_ char *options = NULL;
72 : 368 : unsigned long remount_flags = 0u;
73 [ + + - + ]: 368 : _cleanup_free_ char *remount_options = NULL;
74 : : bool try_remount_ro;
75 [ + + - + ]: 368 : _cleanup_free_ MountPoint *m = NULL;
76 : :
77 : 368 : r = mnt_table_next_fs(table, iter, &fs);
78 [ + + ]: 368 : if (r == 1)
79 : 16 : break;
80 [ - + ]: 352 : if (r < 0)
81 [ # # ]: 0 : return log_error_errno(r, "Failed to get next entry from %s: %m", mountinfo);
82 : :
83 : 352 : path = mnt_fs_get_target(fs);
84 [ - + ]: 352 : if (!path)
85 : 0 : continue;
86 : :
87 : 352 : fstype = mnt_fs_get_fstype(fs);
88 : :
89 : : /* Combine the generic VFS options with the FS-specific
90 : : * options. Duplicates are not a problem here, because the only
91 : : * options that should come up twice are typically ro/rw, which
92 : : * are turned into MS_RDONLY or the inversion of it.
93 : : *
94 : : * Even if there are duplicates later in mount_option_mangle()
95 : : * they shouldn't hurt anyways as they override each other.
96 : : */
97 [ - + ]: 352 : if (!strextend_with_separator(&options, ",",
98 : : mnt_fs_get_vfs_options(fs),
99 : : NULL))
100 : 0 : return log_oom();
101 [ - + ]: 352 : if (!strextend_with_separator(&options, ",",
102 : : mnt_fs_get_fs_options(fs),
103 : : NULL))
104 : 0 : return log_oom();
105 : :
106 : : /* Ignore mount points we can't unmount because they
107 : : * are API or because we are keeping them open (like
108 : : * /dev/console). Also, ignore all mounts below API
109 : : * file systems, since they are likely virtual too,
110 : : * and hence not worth spending time on. Also, in
111 : : * unprivileged containers we might lack the rights to
112 : : * unmount these things, hence don't bother. */
113 [ + + + + ]: 528 : if (mount_point_is_api(path) ||
114 [ + + ]: 348 : mount_point_ignore(path) ||
115 : 172 : PATH_STARTSWITH_SET(path, "/dev", "/sys", "/proc"))
116 : 232 : continue;
117 : :
118 : : /* If we are in a container, don't attempt to
119 : : * read-only mount anything as that brings no real
120 : : * benefits, but might confuse the host, as we remount
121 : : * the superblock here, not the bind mount.
122 : : *
123 : : * If the filesystem is a network fs, also skip the
124 : : * remount. It brings no value (we cannot leave
125 : : * a "dirty fs") and could hang if the network is down.
126 : : * Note that umount2() is more careful and will not
127 : : * hang because of the network being down. */
128 : 120 : try_remount_ro = detect_container() <= 0 &&
129 [ + - ]: 120 : !fstype_is_network(fstype) &&
130 [ + + ]: 120 : !fstype_is_api_vfs(fstype) &&
131 [ + - + - ]: 304 : !fstype_is_ro(fstype) &&
132 [ + - ]: 64 : !fstab_test_yes_no_option(options, "ro\0rw\0");
133 : :
134 [ + + ]: 120 : if (try_remount_ro) {
135 : : /* mount(2) states that mount flags and options need to be exactly the same
136 : : * as they were when the filesystem was mounted, except for the desired
137 : : * changes. So we reconstruct both here and adjust them for the later
138 : : * remount call too. */
139 : :
140 : 64 : r = mnt_fs_get_propagation(fs, &remount_flags);
141 [ - + ]: 64 : if (r < 0) {
142 [ # # ]: 0 : log_warning_errno(r, "mnt_fs_get_propagation() failed for %s, ignoring: %m", path);
143 : 0 : continue;
144 : : }
145 : :
146 : 64 : r = mount_option_mangle(options, remount_flags, &remount_flags, &remount_options);
147 [ - + ]: 64 : if (r < 0) {
148 [ # # ]: 0 : log_warning_errno(r, "mount_option_mangle failed for %s, ignoring: %m", path);
149 : 0 : continue;
150 : : }
151 : :
152 : : /* MS_BIND is special. If it is provided it will only make the mount-point
153 : : * read-only. If left out, the super block itself is remounted, which we want. */
154 : 64 : remount_flags = (remount_flags|MS_REMOUNT|MS_RDONLY) & ~MS_BIND;
155 : : }
156 : :
157 : 120 : m = new0(MountPoint, 1);
158 [ - + ]: 120 : if (!m)
159 : 0 : return log_oom();
160 : :
161 : 120 : m->path = strdup(path);
162 [ - + ]: 120 : if (!m->path)
163 : 0 : return log_oom();
164 : :
165 : 120 : m->remount_options = TAKE_PTR(remount_options);
166 : 120 : m->remount_flags = remount_flags;
167 : 120 : m->try_remount_ro = try_remount_ro;
168 : :
169 [ - + + + ]: 120 : LIST_PREPEND(mount_point, *head, TAKE_PTR(m));
170 : : }
171 : :
172 : 16 : return 0;
173 : : }
174 : :
175 : 8 : int swap_list_get(const char *swaps, MountPoint **head) {
176 : 8 : _cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL;
177 : 8 : _cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL;
178 : : int r;
179 : :
180 [ - + ]: 8 : assert(head);
181 : :
182 : 8 : t = mnt_new_table();
183 : 8 : i = mnt_new_iter(MNT_ITER_FORWARD);
184 [ + - - + ]: 8 : if (!t || !i)
185 : 0 : return log_oom();
186 : :
187 : 8 : r = mnt_table_parse_swaps(t, swaps);
188 [ - + ]: 8 : if (r < 0)
189 [ # # ]: 0 : return log_error_errno(r, "Failed to parse %s: %m", swaps);
190 : :
191 : 12 : for (;;) {
192 : : struct libmnt_fs *fs;
193 [ + + - - ]: 20 : _cleanup_free_ MountPoint *swap = NULL;
194 : : const char *source;
195 : :
196 : 20 : r = mnt_table_next_fs(t, i, &fs);
197 [ + + ]: 20 : if (r == 1)
198 : 8 : break;
199 [ - + ]: 12 : if (r < 0)
200 [ # # ]: 0 : return log_error_errno(r, "Failed to get next entry from %s: %m", swaps);
201 : :
202 : 12 : source = mnt_fs_get_source(fs);
203 [ - + ]: 12 : if (!source)
204 : 0 : continue;
205 : :
206 : 12 : swap = new0(MountPoint, 1);
207 [ - + ]: 12 : if (!swap)
208 : 0 : return -ENOMEM;
209 : :
210 : 12 : swap->path = strdup(source);
211 [ - + ]: 12 : if (!swap->path)
212 : 0 : return -ENOMEM;
213 : :
214 [ - + + + ]: 12 : LIST_PREPEND(mount_point, *head, TAKE_PTR(swap));
215 : : }
216 : :
217 : 8 : return 0;
218 : : }
219 : :
220 : 0 : static int loopback_list_get(MountPoint **head) {
221 : 0 : _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
222 : : sd_device *d;
223 : : int r;
224 : :
225 [ # # ]: 0 : assert(head);
226 : :
227 : 0 : r = sd_device_enumerator_new(&e);
228 [ # # ]: 0 : if (r < 0)
229 : 0 : return r;
230 : :
231 : 0 : r = sd_device_enumerator_allow_uninitialized(e);
232 [ # # ]: 0 : if (r < 0)
233 : 0 : return r;
234 : :
235 : 0 : r = sd_device_enumerator_add_match_subsystem(e, "block", true);
236 [ # # ]: 0 : if (r < 0)
237 : 0 : return r;
238 : :
239 : 0 : r = sd_device_enumerator_add_match_sysname(e, "loop*");
240 [ # # ]: 0 : if (r < 0)
241 : 0 : return r;
242 : :
243 : 0 : r = sd_device_enumerator_add_match_sysattr(e, "loop/backing_file", NULL, true);
244 [ # # ]: 0 : if (r < 0)
245 : 0 : return r;
246 : :
247 [ # # ]: 0 : FOREACH_DEVICE(e, d) {
248 [ # # # ]: 0 : _cleanup_free_ char *p = NULL;
249 : : const char *dn;
250 : : MountPoint *lb;
251 : :
252 [ # # ]: 0 : if (sd_device_get_devname(d, &dn) < 0)
253 : 0 : continue;
254 : :
255 : 0 : p = strdup(dn);
256 [ # # ]: 0 : if (!p)
257 : 0 : return -ENOMEM;
258 : :
259 : 0 : lb = new(MountPoint, 1);
260 [ # # ]: 0 : if (!lb)
261 : 0 : return -ENOMEM;
262 : :
263 : 0 : *lb = (MountPoint) {
264 : 0 : .path = TAKE_PTR(p),
265 : : };
266 : :
267 [ # # # # ]: 0 : LIST_PREPEND(mount_point, *head, lb);
268 : : }
269 : :
270 : 0 : return 0;
271 : : }
272 : :
273 : 0 : static int dm_list_get(MountPoint **head) {
274 : 0 : _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
275 : : sd_device *d;
276 : : int r;
277 : :
278 [ # # ]: 0 : assert(head);
279 : :
280 : 0 : r = sd_device_enumerator_new(&e);
281 [ # # ]: 0 : if (r < 0)
282 : 0 : return r;
283 : :
284 : 0 : r = sd_device_enumerator_allow_uninitialized(e);
285 [ # # ]: 0 : if (r < 0)
286 : 0 : return r;
287 : :
288 : 0 : r = sd_device_enumerator_add_match_subsystem(e, "block", true);
289 [ # # ]: 0 : if (r < 0)
290 : 0 : return r;
291 : :
292 : 0 : r = sd_device_enumerator_add_match_sysname(e, "dm-*");
293 [ # # ]: 0 : if (r < 0)
294 : 0 : return r;
295 : :
296 [ # # ]: 0 : FOREACH_DEVICE(e, d) {
297 [ # # # ]: 0 : _cleanup_free_ char *p = NULL;
298 : : const char *dn;
299 : : MountPoint *m;
300 : : dev_t devnum;
301 : :
302 [ # # # # ]: 0 : if (sd_device_get_devnum(d, &devnum) < 0 ||
303 : 0 : sd_device_get_devname(d, &dn) < 0)
304 : 0 : continue;
305 : :
306 : 0 : p = strdup(dn);
307 [ # # ]: 0 : if (!p)
308 : 0 : return -ENOMEM;
309 : :
310 : 0 : m = new(MountPoint, 1);
311 [ # # ]: 0 : if (!m)
312 : 0 : return -ENOMEM;
313 : :
314 : 0 : *m = (MountPoint) {
315 : 0 : .path = TAKE_PTR(p),
316 : : .devnum = devnum,
317 : : };
318 : :
319 [ # # # # ]: 0 : LIST_PREPEND(mount_point, *head, m);
320 : : }
321 : :
322 : 0 : return 0;
323 : : }
324 : :
325 : 0 : static int delete_loopback(const char *device) {
326 : 0 : _cleanup_close_ int fd = -1;
327 : : int r;
328 : :
329 [ # # ]: 0 : assert(device);
330 : :
331 : 0 : fd = open(device, O_RDONLY|O_CLOEXEC);
332 [ # # ]: 0 : if (fd < 0)
333 [ # # ]: 0 : return errno == ENOENT ? 0 : -errno;
334 : :
335 : 0 : r = ioctl(fd, LOOP_CLR_FD, 0);
336 [ # # ]: 0 : if (r >= 0)
337 : 0 : return 1;
338 : :
339 : : /* ENXIO: not bound, so no error */
340 [ # # ]: 0 : if (errno == ENXIO)
341 : 0 : return 0;
342 : :
343 : 0 : return -errno;
344 : : }
345 : :
346 : 0 : static int delete_dm(dev_t devnum) {
347 : :
348 : 0 : struct dm_ioctl dm = {
349 : : .version = {
350 : : DM_VERSION_MAJOR,
351 : : DM_VERSION_MINOR,
352 : : DM_VERSION_PATCHLEVEL
353 : : },
354 : : .data_size = sizeof(dm),
355 : : .dev = devnum,
356 : : };
357 : :
358 : 0 : _cleanup_close_ int fd = -1;
359 : :
360 [ # # ]: 0 : assert(major(devnum) != 0);
361 : :
362 : 0 : fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC);
363 [ # # ]: 0 : if (fd < 0)
364 : 0 : return -errno;
365 : :
366 [ # # ]: 0 : if (ioctl(fd, DM_DEV_REMOVE, &dm) < 0)
367 : 0 : return -errno;
368 : :
369 : 0 : return 0;
370 : : }
371 : :
372 : 0 : static bool nonunmountable_path(const char *path) {
373 : 0 : return path_equal(path, "/")
374 : : #if ! HAVE_SPLIT_USR
375 [ # # ]: 0 : || path_equal(path, "/usr")
376 : : #endif
377 [ # # # # ]: 0 : || path_startswith(path, "/run/initramfs");
378 : : }
379 : :
380 : 0 : static int remount_with_timeout(MountPoint *m, int umount_log_level) {
381 : : pid_t pid;
382 : : int r;
383 : :
384 [ # # ]: 0 : BLOCK_SIGNALS(SIGCHLD);
385 : :
386 [ # # ]: 0 : assert(m);
387 : :
388 : : /* Due to the possibility of a remount operation hanging, we
389 : : * fork a child process and set a timeout. If the timeout
390 : : * lapses, the assumption is that that particular remount
391 : : * failed. */
392 : 0 : r = safe_fork("(sd-remount)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_REOPEN_LOG, &pid);
393 [ # # ]: 0 : if (r < 0)
394 : 0 : return r;
395 [ # # ]: 0 : if (r == 0) {
396 [ # # ]: 0 : log_info("Remounting '%s' read-only in with options '%s'.", m->path, m->remount_options);
397 : :
398 : : /* Start the mount operation here in the child */
399 : 0 : r = mount(NULL, m->path, NULL, m->remount_flags, m->remount_options);
400 [ # # ]: 0 : if (r < 0)
401 [ # # ]: 0 : log_full_errno(umount_log_level, errno, "Failed to remount '%s' read-only: %m", m->path);
402 : :
403 : 0 : _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
404 : : }
405 : :
406 : 0 : r = wait_for_terminate_with_timeout(pid, DEFAULT_TIMEOUT_USEC);
407 [ # # ]: 0 : if (r == -ETIMEDOUT) {
408 [ # # ]: 0 : log_error_errno(r, "Remounting '%s' timed out, issuing SIGKILL to PID " PID_FMT ".", m->path, pid);
409 : 0 : (void) kill(pid, SIGKILL);
410 [ # # ]: 0 : } else if (r == -EPROTO)
411 [ # # ]: 0 : log_debug_errno(r, "Remounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid);
412 [ # # ]: 0 : else if (r < 0)
413 [ # # ]: 0 : log_error_errno(r, "Remounting '%s' failed unexpectedly, couldn't wait for child process " PID_FMT ": %m", m->path, pid);
414 : :
415 : 0 : return r;
416 : : }
417 : :
418 : 0 : static int umount_with_timeout(MountPoint *m, int umount_log_level) {
419 : : pid_t pid;
420 : : int r;
421 : :
422 [ # # ]: 0 : BLOCK_SIGNALS(SIGCHLD);
423 : :
424 [ # # ]: 0 : assert(m);
425 : :
426 : : /* Due to the possibility of a umount operation hanging, we
427 : : * fork a child process and set a timeout. If the timeout
428 : : * lapses, the assumption is that that particular umount
429 : : * failed. */
430 : 0 : r = safe_fork("(sd-umount)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_REOPEN_LOG, &pid);
431 [ # # ]: 0 : if (r < 0)
432 : 0 : return r;
433 [ # # ]: 0 : if (r == 0) {
434 [ # # ]: 0 : log_info("Unmounting '%s'.", m->path);
435 : :
436 : : /* Start the mount operation here in the child Using MNT_FORCE
437 : : * causes some filesystems (e.g. FUSE and NFS and other network
438 : : * filesystems) to abort any pending requests and return -EIO
439 : : * rather than blocking indefinitely. If the filesysten is
440 : : * "busy", this may allow processes to die, thus making the
441 : : * filesystem less busy so the unmount might succeed (rather
442 : : * then return EBUSY).*/
443 : 0 : r = umount2(m->path, MNT_FORCE);
444 [ # # ]: 0 : if (r < 0)
445 [ # # ]: 0 : log_full_errno(umount_log_level, errno, "Failed to unmount %s: %m", m->path);
446 : :
447 : 0 : _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
448 : : }
449 : :
450 : 0 : r = wait_for_terminate_with_timeout(pid, DEFAULT_TIMEOUT_USEC);
451 [ # # ]: 0 : if (r == -ETIMEDOUT) {
452 [ # # ]: 0 : log_error_errno(r, "Unmounting '%s' timed out, issuing SIGKILL to PID " PID_FMT ".", m->path, pid);
453 : 0 : (void) kill(pid, SIGKILL);
454 [ # # ]: 0 : } else if (r == -EPROTO)
455 [ # # ]: 0 : log_debug_errno(r, "Unmounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid);
456 [ # # ]: 0 : else if (r < 0)
457 [ # # ]: 0 : log_error_errno(r, "Unmounting '%s' failed unexpectedly, couldn't wait for child process " PID_FMT ": %m", m->path, pid);
458 : :
459 : 0 : return r;
460 : : }
461 : :
462 : : /* This includes remounting readonly, which changes the kernel mount options.
463 : : * Therefore the list passed to this function is invalidated, and should not be reused. */
464 : 0 : static int mount_points_list_umount(MountPoint **head, bool *changed, int umount_log_level) {
465 : : MountPoint *m;
466 : 0 : int n_failed = 0;
467 : :
468 [ # # ]: 0 : assert(head);
469 [ # # ]: 0 : assert(changed);
470 : :
471 [ # # ]: 0 : LIST_FOREACH(mount_point, m, *head) {
472 [ # # ]: 0 : if (m->try_remount_ro) {
473 : : /* We always try to remount directories
474 : : * read-only first, before we go on and umount
475 : : * them.
476 : : *
477 : : * Mount points can be stacked. If a mount
478 : : * point is stacked below / or /usr, we
479 : : * cannot umount or remount it directly,
480 : : * since there is no way to refer to the
481 : : * underlying mount. There's nothing we can do
482 : : * about it for the general case, but we can
483 : : * do something about it if it is aliased
484 : : * somewhere else via a bind mount. If we
485 : : * explicitly remount the super block of that
486 : : * alias read-only we hence should be
487 : : * relatively safe regarding keeping a dirty fs
488 : : * we cannot otherwise see.
489 : : *
490 : : * Since the remount can hang in the instance of
491 : : * remote filesystems, we remount asynchronously
492 : : * and skip the subsequent umount if it fails. */
493 [ # # ]: 0 : if (remount_with_timeout(m, umount_log_level) < 0) {
494 : : /* Remount failed, but try unmounting anyway,
495 : : * unless this is a mount point we want to skip. */
496 [ # # ]: 0 : if (nonunmountable_path(m->path)) {
497 : 0 : n_failed++;
498 : 0 : continue;
499 : : }
500 : : }
501 : : }
502 : :
503 : : /* Skip / and /usr since we cannot unmount that
504 : : * anyway, since we are running from it. They have
505 : : * already been remounted ro. */
506 [ # # ]: 0 : if (nonunmountable_path(m->path))
507 : 0 : continue;
508 : :
509 : : /* Trying to umount */
510 [ # # ]: 0 : if (umount_with_timeout(m, umount_log_level) < 0)
511 : 0 : n_failed++;
512 : : else
513 : 0 : *changed = true;
514 : : }
515 : :
516 : 0 : return n_failed;
517 : : }
518 : :
519 : 0 : static int swap_points_list_off(MountPoint **head, bool *changed) {
520 : : MountPoint *m, *n;
521 : 0 : int n_failed = 0;
522 : :
523 [ # # ]: 0 : assert(head);
524 [ # # ]: 0 : assert(changed);
525 : :
526 [ # # ]: 0 : LIST_FOREACH_SAFE(mount_point, m, n, *head) {
527 [ # # ]: 0 : log_info("Deactivating swap %s.", m->path);
528 [ # # ]: 0 : if (swapoff(m->path) == 0) {
529 : 0 : *changed = true;
530 : 0 : mount_point_free(head, m);
531 : : } else {
532 [ # # ]: 0 : log_warning_errno(errno, "Could not deactivate swap %s: %m", m->path);
533 : 0 : n_failed++;
534 : : }
535 : : }
536 : :
537 : 0 : return n_failed;
538 : : }
539 : :
540 : 0 : static int loopback_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) {
541 : : MountPoint *m, *n;
542 : 0 : int n_failed = 0, k;
543 : : struct stat root_st;
544 : :
545 [ # # ]: 0 : assert(head);
546 [ # # ]: 0 : assert(changed);
547 : :
548 : 0 : k = lstat("/", &root_st);
549 : :
550 [ # # ]: 0 : LIST_FOREACH_SAFE(mount_point, m, n, *head) {
551 : : int r;
552 : : struct stat loopback_st;
553 : :
554 [ # # ]: 0 : if (k >= 0 &&
555 [ # # # # ]: 0 : major(root_st.st_dev) != 0 &&
556 : 0 : lstat(m->path, &loopback_st) >= 0 &&
557 [ # # ]: 0 : root_st.st_dev == loopback_st.st_rdev) {
558 : 0 : n_failed++;
559 : 0 : continue;
560 : : }
561 : :
562 [ # # ]: 0 : log_info("Detaching loopback %s.", m->path);
563 : 0 : r = delete_loopback(m->path);
564 [ # # ]: 0 : if (r >= 0) {
565 [ # # ]: 0 : if (r > 0)
566 : 0 : *changed = true;
567 : :
568 : 0 : mount_point_free(head, m);
569 : : } else {
570 [ # # ]: 0 : log_full_errno(umount_log_level, errno, "Could not detach loopback %s: %m", m->path);
571 : 0 : n_failed++;
572 : : }
573 : : }
574 : :
575 : 0 : return n_failed;
576 : : }
577 : :
578 : 0 : static int dm_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) {
579 : : MountPoint *m, *n;
580 : 0 : int n_failed = 0, r;
581 : : dev_t rootdev;
582 : :
583 [ # # ]: 0 : assert(head);
584 [ # # ]: 0 : assert(changed);
585 : :
586 : 0 : r = get_block_device("/", &rootdev);
587 [ # # ]: 0 : if (r <= 0)
588 : 0 : rootdev = 0;
589 : :
590 [ # # ]: 0 : LIST_FOREACH_SAFE(mount_point, m, n, *head) {
591 : :
592 [ # # # # ]: 0 : if (major(rootdev) != 0 && rootdev == m->devnum) {
593 : 0 : n_failed ++;
594 : 0 : continue;
595 : : }
596 : :
597 [ # # ]: 0 : log_info("Detaching DM %u:%u.", major(m->devnum), minor(m->devnum));
598 : 0 : r = delete_dm(m->devnum);
599 [ # # ]: 0 : if (r >= 0) {
600 : 0 : *changed = true;
601 : 0 : mount_point_free(head, m);
602 : : } else {
603 [ # # ]: 0 : log_full_errno(umount_log_level, errno, "Could not detach DM %s: %m", m->path);
604 : 0 : n_failed++;
605 : : }
606 : : }
607 : :
608 : 0 : return n_failed;
609 : : }
610 : :
611 : 0 : static int umount_all_once(bool *changed, int umount_log_level) {
612 : : int r;
613 : 0 : _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head);
614 : :
615 [ # # ]: 0 : assert(changed);
616 : :
617 : 0 : LIST_HEAD_INIT(mp_list_head);
618 : 0 : r = mount_points_list_get(NULL, &mp_list_head);
619 [ # # ]: 0 : if (r < 0)
620 : 0 : return r;
621 : :
622 : 0 : return mount_points_list_umount(&mp_list_head, changed, umount_log_level);
623 : : }
624 : :
625 : 0 : int umount_all(bool *changed, int umount_log_level) {
626 : : bool umount_changed;
627 : : int r;
628 : :
629 [ # # ]: 0 : assert(changed);
630 : :
631 : : /* Retry umount, until nothing can be umounted anymore. Mounts are
632 : : * processed in order, newest first. The retries are needed when
633 : : * an old mount has been moved, to a path inside a newer mount. */
634 : : do {
635 : 0 : umount_changed = false;
636 : :
637 : 0 : r = umount_all_once(&umount_changed, umount_log_level);
638 [ # # ]: 0 : if (umount_changed)
639 : 0 : *changed = true;
640 [ # # ]: 0 : } while (umount_changed);
641 : :
642 : 0 : return r;
643 : : }
644 : :
645 : 0 : int swapoff_all(bool *changed) {
646 : 0 : _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, swap_list_head);
647 : : int r;
648 : :
649 [ # # ]: 0 : assert(changed);
650 : :
651 : 0 : LIST_HEAD_INIT(swap_list_head);
652 : :
653 : 0 : r = swap_list_get(NULL, &swap_list_head);
654 [ # # ]: 0 : if (r < 0)
655 : 0 : return r;
656 : :
657 : 0 : return swap_points_list_off(&swap_list_head, changed);
658 : : }
659 : :
660 : 0 : int loopback_detach_all(bool *changed, int umount_log_level) {
661 : 0 : _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, loopback_list_head);
662 : : int r;
663 : :
664 [ # # ]: 0 : assert(changed);
665 : :
666 : 0 : LIST_HEAD_INIT(loopback_list_head);
667 : :
668 : 0 : r = loopback_list_get(&loopback_list_head);
669 [ # # ]: 0 : if (r < 0)
670 : 0 : return r;
671 : :
672 : 0 : return loopback_points_list_detach(&loopback_list_head, changed, umount_log_level);
673 : : }
674 : :
675 : 0 : int dm_detach_all(bool *changed, int umount_log_level) {
676 : 0 : _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, dm_list_head);
677 : : int r;
678 : :
679 [ # # ]: 0 : assert(changed);
680 : :
681 : 0 : LIST_HEAD_INIT(dm_list_head);
682 : :
683 : 0 : r = dm_list_get(&dm_list_head);
684 [ # # ]: 0 : if (r < 0)
685 : 0 : return r;
686 : :
687 : 0 : return dm_points_list_detach(&dm_list_head, changed, umount_log_level);
688 : : }
|