]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shutdown/umount.c
network: ignore requested ipv6 routing policy rule when ipv6 is disabled by sysctl
[thirdparty/systemd.git] / src / shutdown / umount.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
e3478379 2/***
96b2fb93 3 Copyright © 2010 ProFUSION embedded systems
e3478379
FF
4***/
5
6#include <errno.h>
7#include <fcntl.h>
01234e1f 8#include <linux/dm-ioctl.h>
4f5dd394 9#include <linux/loop.h>
e3478379
FF
10#include <string.h>
11#include <sys/mount.h>
12#include <sys/swap.h>
ca78ad1d
ZJS
13#include <sys/stat.h>
14#include <sys/types.h>
15#include <unistd.h>
e3478379 16
6bcf00ed 17#include "sd-device.h"
b4bbcaa9 18
b5efdb8a 19#include "alloc-util.h"
18c528e9 20#include "blockdev-util.h"
d5641e0d 21#include "def.h"
8437c059 22#include "device-util.h"
07630cea 23#include "escape.h"
3ffd4af2 24#include "fd-util.h"
471b48ed 25#include "fstab-util.h"
fb36b133 26#include "libmount-util.h"
e3478379 27#include "mount-setup.h"
18c528e9 28#include "mount-util.h"
049af8ad 29#include "mountpoint-util.h"
9eb977db 30#include "path-util.h"
dccca82b 31#include "process-util.h"
d5641e0d 32#include "signal-util.h"
07630cea 33#include "string-util.h"
da9fc98d 34#include "strv.h"
3ffd4af2 35#include "umount.h"
e3478379 36#include "util.h"
024f268d 37#include "virt.h"
e3478379 38
12aad1d0
LP
39static void mount_point_free(MountPoint **head, MountPoint *m) {
40 assert(head);
41 assert(m);
e3478379 42
71fda00f 43 LIST_REMOVE(mount_point, *head, m);
12aad1d0
LP
44
45 free(m->path);
3bc341be 46 free(m->remount_options);
12aad1d0 47 free(m);
e3478379
FF
48}
49
6fa392bf 50void mount_points_list_free(MountPoint **head) {
12aad1d0
LP
51 assert(head);
52
53 while (*head)
54 mount_point_free(head, *head);
e3478379
FF
55}
56
6fa392bf 57int mount_points_list_get(const char *mountinfo, MountPoint **head) {
13dcfe46
ZJS
58 _cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL;
59 _cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL;
527b7a42 60 int r;
e3478379 61
12aad1d0
LP
62 assert(head);
63
e2857b3d 64 r = libmount_parse(mountinfo, NULL, &table, &iter);
95b862b0 65 if (r < 0)
6fa392bf 66 return log_error_errno(r, "Failed to parse %s: %m", mountinfo);
95b862b0
ZJS
67
68 for (;;) {
69 struct libmnt_fs *fs;
66c91c3a 70 const char *path, *fstype;
71 _cleanup_free_ char *options = NULL;
95b862b0
ZJS
72 unsigned long remount_flags = 0u;
73 _cleanup_free_ char *remount_options = NULL;
74 bool try_remount_ro;
9d1b2b22 75 _cleanup_free_ MountPoint *m = NULL;
e3478379 76
13dcfe46 77 r = mnt_table_next_fs(table, iter, &fs);
95b862b0
ZJS
78 if (r == 1)
79 break;
527b7a42 80 if (r < 0)
6fa392bf 81 return log_error_errno(r, "Failed to get next entry from %s: %m", mountinfo);
95b862b0
ZJS
82
83 path = mnt_fs_get_target(fs);
84 if (!path)
85 continue;
86
95b862b0 87 fstype = mnt_fs_get_fstype(fs);
e3478379 88
66c91c3a 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
9d1b2b22 92 * are turned into MS_RDONLY or the inversion of it.
66c91c3a 93 *
94 * Even if there are duplicates later in mount_option_mangle()
9d1b2b22 95 * they shouldn't hurt anyways as they override each other.
66c91c3a 96 */
97 if (!strextend_with_separator(&options, ",",
98 mnt_fs_get_vfs_options(fs),
99 NULL))
100 return log_oom();
101 if (!strextend_with_separator(&options, ",",
102 mnt_fs_get_fs_options(fs),
103 NULL))
104 return log_oom();
105
46108b3b
LP
106 /* Ignore mount points we can't unmount because they
107 * are API or because we are keeping them open (like
874d3404
LP
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. */
9d1b2b22
ZJS
113 if (mount_point_is_api(path) ||
114 mount_point_ignore(path) ||
115 PATH_STARTSWITH_SET(path, "/dev", "/sys", "/proc"))
2054a5b8 116 continue;
2054a5b8 117
1d62d22d
JJ
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. */
95b862b0
ZJS
128 try_remount_ro = detect_container() <= 0 &&
129 !fstype_is_network(fstype) &&
130 !fstype_is_api_vfs(fstype) &&
131 !fstype_is_ro(fstype) &&
132 !fstab_test_yes_no_option(options, "ro\0rw\0");
3bc341be 133
95b862b0 134 if (try_remount_ro) {
3bc341be
JJ
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
95b862b0
ZJS
140 r = mnt_fs_get_propagation(fs, &remount_flags);
141 if (r < 0) {
142 log_warning_errno(r, "mnt_fs_get_propagation() failed for %s, ignoring: %m", path);
143 continue;
144 }
3bc341be 145
95b862b0
ZJS
146 r = mount_option_mangle(options, remount_flags, &remount_flags, &remount_options);
147 if (r < 0) {
148 log_warning_errno(r, "mount_option_mangle failed for %s, ignoring: %m", path);
149 continue;
150 }
3bc341be
JJ
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. */
95b862b0 154 remount_flags = (remount_flags|MS_REMOUNT|MS_RDONLY) & ~MS_BIND;
3bc341be 155 }
471b48ed 156
95b862b0
ZJS
157 m = new0(MountPoint, 1);
158 if (!m)
159 return log_oom();
160
9d1b2b22
ZJS
161 m->path = strdup(path);
162 if (!m->path)
163 return log_oom();
164
165 m->remount_options = TAKE_PTR(remount_options);
95b862b0
ZJS
166 m->remount_flags = remount_flags;
167 m->try_remount_ro = try_remount_ro;
168
9d1b2b22 169 LIST_PREPEND(mount_point, *head, TAKE_PTR(m));
e3478379
FF
170 }
171
c3544e8d 172 return 0;
e3478379
FF
173}
174
1fd8edb5 175int swap_list_get(const char *swaps, MountPoint **head) {
71ae04c4
ZJS
176 _cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL;
177 _cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL;
527b7a42 178 int r;
e3478379 179
12aad1d0
LP
180 assert(head);
181
71ae04c4
ZJS
182 t = mnt_new_table();
183 i = mnt_new_iter(MNT_ITER_FORWARD);
184 if (!t || !i)
185 return log_oom();
e3478379 186
71ae04c4
ZJS
187 r = mnt_table_parse_swaps(t, swaps);
188 if (r < 0)
189 return log_error_errno(r, "Failed to parse %s: %m", swaps);
190
191 for (;;) {
192 struct libmnt_fs *fs;
9d1b2b22 193 _cleanup_free_ MountPoint *swap = NULL;
71ae04c4 194 const char *source;
71ae04c4
ZJS
195
196 r = mnt_table_next_fs(t, i, &fs);
197 if (r == 1)
198 break;
199 if (r < 0)
200 return log_error_errno(r, "Failed to get next entry from %s: %m", swaps);
e3478379 201
71ae04c4
ZJS
202 source = mnt_fs_get_source(fs);
203 if (!source)
e3478379 204 continue;
e3478379 205
527b7a42 206 swap = new0(MountPoint, 1);
595c66a3 207 if (!swap)
e1d75803 208 return -ENOMEM;
e3478379 209
9d1b2b22
ZJS
210 swap->path = strdup(source);
211 if (!swap->path)
212 return -ENOMEM;
213
214 LIST_PREPEND(mount_point, *head, TAKE_PTR(swap));
e3478379
FF
215 }
216
e1d75803 217 return 0;
e3478379
FF
218}
219
12aad1d0 220static int loopback_list_get(MountPoint **head) {
6bcf00ed
YW
221 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
222 sd_device *d;
06acf2d4 223 int r;
e3478379 224
12aad1d0
LP
225 assert(head);
226
6bcf00ed
YW
227 r = sd_device_enumerator_new(&e);
228 if (r < 0)
229 return r;
e3478379 230
6bcf00ed
YW
231 r = sd_device_enumerator_allow_uninitialized(e);
232 if (r < 0)
233 return r;
e3478379 234
6bcf00ed 235 r = sd_device_enumerator_add_match_subsystem(e, "block", true);
06acf2d4
LP
236 if (r < 0)
237 return r;
238
6bcf00ed 239 r = sd_device_enumerator_add_match_sysname(e, "loop*");
06acf2d4
LP
240 if (r < 0)
241 return r;
242
6bcf00ed 243 r = sd_device_enumerator_add_match_sysattr(e, "loop/backing_file", NULL, true);
06acf2d4
LP
244 if (r < 0)
245 return r;
e3478379 246
8437c059 247 FOREACH_DEVICE(e, d) {
209c6f03 248 _cleanup_free_ char *p = NULL;
6bcf00ed 249 const char *dn;
209c6f03 250 MountPoint *lb;
e3478379 251
6bcf00ed 252 if (sd_device_get_devname(d, &dn) < 0)
2d9a3397 253 continue;
b854a7e7 254
209c6f03
YW
255 p = strdup(dn);
256 if (!p)
257 return -ENOMEM;
258
259 lb = new(MountPoint, 1);
a6dcd229 260 if (!lb)
1ca208fb 261 return -ENOMEM;
e3478379 262
209c6f03
YW
263 *lb = (MountPoint) {
264 .path = TAKE_PTR(p),
265 };
a6dcd229 266
71fda00f 267 LIST_PREPEND(mount_point, *head, lb);
e3478379
FF
268 }
269
1ca208fb 270 return 0;
e3478379
FF
271}
272
12aad1d0 273static int dm_list_get(MountPoint **head) {
6bcf00ed
YW
274 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
275 sd_device *d;
06acf2d4 276 int r;
d48141ba 277
12aad1d0
LP
278 assert(head);
279
6bcf00ed
YW
280 r = sd_device_enumerator_new(&e);
281 if (r < 0)
282 return r;
d48141ba 283
6bcf00ed
YW
284 r = sd_device_enumerator_allow_uninitialized(e);
285 if (r < 0)
286 return r;
d48141ba 287
6bcf00ed 288 r = sd_device_enumerator_add_match_subsystem(e, "block", true);
06acf2d4
LP
289 if (r < 0)
290 return r;
d48141ba 291
6bcf00ed 292 r = sd_device_enumerator_add_match_sysname(e, "dm-*");
06acf2d4
LP
293 if (r < 0)
294 return r;
d48141ba 295
8437c059 296 FOREACH_DEVICE(e, d) {
209c6f03 297 _cleanup_free_ char *p = NULL;
6bcf00ed 298 const char *dn;
209c6f03 299 MountPoint *m;
6bcf00ed 300 dev_t devnum;
d48141ba 301
209c6f03
YW
302 if (sd_device_get_devnum(d, &devnum) < 0 ||
303 sd_device_get_devname(d, &dn) < 0)
6bcf00ed 304 continue;
d48141ba 305
209c6f03
YW
306 p = strdup(dn);
307 if (!p)
308 return -ENOMEM;
d48141ba 309
209c6f03 310 m = new(MountPoint, 1);
a6dcd229 311 if (!m)
1ca208fb 312 return -ENOMEM;
d48141ba 313
209c6f03
YW
314 *m = (MountPoint) {
315 .path = TAKE_PTR(p),
316 .devnum = devnum,
317 };
a6dcd229 318
71fda00f 319 LIST_PREPEND(mount_point, *head, m);
d48141ba
LP
320 }
321
1ca208fb 322 return 0;
d48141ba
LP
323}
324
e3478379 325static int delete_loopback(const char *device) {
03e334a1
LP
326 _cleanup_close_ int fd = -1;
327 int r;
e3478379 328
0494cae0
JJ
329 assert(device);
330
03e334a1
LP
331 fd = open(device, O_RDONLY|O_CLOEXEC);
332 if (fd < 0)
c4f8bd1a 333 return errno == ENOENT ? 0 : -errno;
e3478379 334
ce726252 335 r = ioctl(fd, LOOP_CLR_FD, 0);
12aad1d0
LP
336 if (r >= 0)
337 return 1;
338
ce726252 339 /* ENXIO: not bound, so no error */
12aad1d0
LP
340 if (errno == ENXIO)
341 return 0;
342
343 return -errno;
e3478379
FF
344}
345
2d9a3397 346static int delete_dm(dev_t devnum) {
cf139e60 347
b92bea5d 348 struct dm_ioctl dm = {
cf139e60
LP
349 .version = {
350 DM_VERSION_MAJOR,
351 DM_VERSION_MINOR,
352 DM_VERSION_PATCHLEVEL
353 },
b92bea5d
ZJS
354 .data_size = sizeof(dm),
355 .dev = devnum,
356 };
d48141ba 357
cf139e60
LP
358 _cleanup_close_ int fd = -1;
359
2d9a3397 360 assert(major(devnum) != 0);
d48141ba 361
e62d8c39
ZJS
362 fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC);
363 if (fd < 0)
d48141ba
LP
364 return -errno;
365
cf139e60
LP
366 if (ioctl(fd, DM_DEV_REMOVE, &dm) < 0)
367 return -errno;
368
369 return 0;
d48141ba
LP
370}
371
c826cd3f
ZJS
372static bool nonunmountable_path(const char *path) {
373 return path_equal(path, "/")
349cc4a5 374#if ! HAVE_SPLIT_USR
c826cd3f
ZJS
375 || path_equal(path, "/usr")
376#endif
377 || path_startswith(path, "/run/initramfs");
378}
379
456b2199 380static int remount_with_timeout(MountPoint *m, int umount_log_level) {
d5641e0d
KW
381 pid_t pid;
382 int r;
383
384 BLOCK_SIGNALS(SIGCHLD);
385
0494cae0 386 assert(m);
0494cae0 387
5238e957 388 /* Due to the possibility of a remount operation hanging, we
d5641e0d
KW
389 * fork a child process and set a timeout. If the timeout
390 * lapses, the assumption is that that particular remount
391 * failed. */
0b1f3c76 392 r = safe_fork("(sd-remount)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_REOPEN_LOG, &pid);
4c253ed1 393 if (r < 0)
b6e1fff1 394 return r;
4c253ed1 395 if (r == 0) {
3bc341be 396 log_info("Remounting '%s' read-only in with options '%s'.", m->path, m->remount_options);
d5641e0d
KW
397
398 /* Start the mount operation here in the child */
3bc341be 399 r = mount(NULL, m->path, NULL, m->remount_flags, m->remount_options);
d5641e0d 400 if (r < 0)
456b2199 401 log_full_errno(umount_log_level, errno, "Failed to remount '%s' read-only: %m", m->path);
d5641e0d
KW
402
403 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
404 }
405
406 r = wait_for_terminate_with_timeout(pid, DEFAULT_TIMEOUT_USEC);
407 if (r == -ETIMEDOUT) {
00adeed9 408 log_error_errno(r, "Remounting '%s' timed out, issuing SIGKILL to PID " PID_FMT ".", m->path, pid);
d5641e0d 409 (void) kill(pid, SIGKILL);
00adeed9 410 } else if (r == -EPROTO)
456b2199 411 log_debug_errno(r, "Remounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid);
00adeed9
LP
412 else if (r < 0)
413 log_error_errno(r, "Remounting '%s' failed unexpectedly, couldn't wait for child process " PID_FMT ": %m", m->path, pid);
d5641e0d
KW
414
415 return r;
416}
417
456b2199 418static int umount_with_timeout(MountPoint *m, int umount_log_level) {
d5641e0d
KW
419 pid_t pid;
420 int r;
421
422 BLOCK_SIGNALS(SIGCHLD);
423
0494cae0
JJ
424 assert(m);
425
5238e957 426 /* Due to the possibility of a umount operation hanging, we
d5641e0d
KW
427 * fork a child process and set a timeout. If the timeout
428 * lapses, the assumption is that that particular umount
429 * failed. */
0b1f3c76 430 r = safe_fork("(sd-umount)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_REOPEN_LOG, &pid);
4c253ed1 431 if (r < 0)
b6e1fff1 432 return r;
4c253ed1 433 if (r == 0) {
d5641e0d
KW
434 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 r = umount2(m->path, MNT_FORCE);
444 if (r < 0)
456b2199 445 log_full_errno(umount_log_level, errno, "Failed to unmount %s: %m", m->path);
d5641e0d
KW
446
447 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
448 }
449
450 r = wait_for_terminate_with_timeout(pid, DEFAULT_TIMEOUT_USEC);
451 if (r == -ETIMEDOUT) {
00adeed9 452 log_error_errno(r, "Unmounting '%s' timed out, issuing SIGKILL to PID " PID_FMT ".", m->path, pid);
d5641e0d 453 (void) kill(pid, SIGKILL);
00adeed9 454 } else if (r == -EPROTO)
456b2199 455 log_debug_errno(r, "Unmounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid);
00adeed9
LP
456 else if (r < 0)
457 log_error_errno(r, "Unmounting '%s' failed unexpectedly, couldn't wait for child process " PID_FMT ": %m", m->path, pid);
d5641e0d
KW
458
459 return r;
460}
461
116e6d96
AJ
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. */
456b2199 464static int mount_points_list_umount(MountPoint **head, bool *changed, int umount_log_level) {
116e6d96 465 MountPoint *m;
12aad1d0 466 int n_failed = 0;
e3478379 467
12aad1d0 468 assert(head);
0494cae0 469 assert(changed);
12aad1d0 470
116e6d96 471 LIST_FOREACH(mount_point, m, *head) {
1d62d22d 472 if (m->try_remount_ro) {
93bd1577
LP
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
ab06eef8 479 * cannot umount or remount it directly,
93bd1577
LP
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
5238e957 484 * somewhere else via a bind mount. If we
93bd1577
LP
485 * explicitly remount the super block of that
486 * alias read-only we hence should be
8645ffd1 487 * relatively safe regarding keeping a dirty fs
d5641e0d
KW
488 * we cannot otherwise see.
489 *
490 * Since the remount can hang in the instance of
491 * remote filesystems, we remount asynchronously
8645ffd1 492 * and skip the subsequent umount if it fails. */
456b2199 493 if (remount_with_timeout(m, umount_log_level) < 0) {
8645ffd1
JJ
494 /* Remount failed, but try unmounting anyway,
495 * unless this is a mount point we want to skip. */
496 if (nonunmountable_path(m->path)) {
c826cd3f 497 n_failed++;
8645ffd1
JJ
498 continue;
499 }
c826cd3f 500 }
93bd1577
LP
501 }
502
503 /* Skip / and /usr since we cannot unmount that
5a6f9d23
HG
504 * anyway, since we are running from it. They have
505 * already been remounted ro. */
c826cd3f 506 if (nonunmountable_path(m->path))
e3478379
FF
507 continue;
508
d5641e0d 509 /* Trying to umount */
456b2199 510 if (umount_with_timeout(m, umount_log_level) < 0)
d5641e0d 511 n_failed++;
0494cae0
JJ
512 else
513 *changed = true;
e3478379
FF
514 }
515
12aad1d0 516 return n_failed;
e3478379
FF
517}
518
12aad1d0
LP
519static int swap_points_list_off(MountPoint **head, bool *changed) {
520 MountPoint *m, *n;
521 int n_failed = 0;
522
523 assert(head);
0494cae0 524 assert(changed);
e3478379 525
12aad1d0 526 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
735e0712 527 log_info("Deactivating swap %s.", m->path);
12aad1d0 528 if (swapoff(m->path) == 0) {
0494cae0 529 *changed = true;
12aad1d0
LP
530 mount_point_free(head, m);
531 } else {
56f64d95 532 log_warning_errno(errno, "Could not deactivate swap %s: %m", m->path);
12aad1d0 533 n_failed++;
e3478379
FF
534 }
535 }
536
12aad1d0 537 return n_failed;
e3478379
FF
538}
539
456b2199 540static int loopback_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) {
12aad1d0 541 MountPoint *m, *n;
7fc942b2
LP
542 int n_failed = 0, k;
543 struct stat root_st;
12aad1d0
LP
544
545 assert(head);
0494cae0 546 assert(changed);
12aad1d0 547
7fc942b2
LP
548 k = lstat("/", &root_st);
549
12aad1d0
LP
550 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
551 int r;
7fc942b2
LP
552 struct stat loopback_st;
553
554 if (k >= 0 &&
555 major(root_st.st_dev) != 0 &&
556 lstat(m->path, &loopback_st) >= 0 &&
557 root_st.st_dev == loopback_st.st_rdev) {
313cefa1 558 n_failed++;
7fc942b2
LP
559 continue;
560 }
e3478379 561
735e0712 562 log_info("Detaching loopback %s.", m->path);
bce93b7a
MS
563 r = delete_loopback(m->path);
564 if (r >= 0) {
0494cae0 565 if (r > 0)
12aad1d0
LP
566 *changed = true;
567
568 mount_point_free(head, m);
569 } else {
456b2199 570 log_full_errno(umount_log_level, errno, "Could not detach loopback %s: %m", m->path);
12aad1d0 571 n_failed++;
e3478379
FF
572 }
573 }
574
12aad1d0 575 return n_failed;
e3478379
FF
576}
577
456b2199 578static int dm_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) {
12aad1d0 579 MountPoint *m, *n;
33e8d8af
FB
580 int n_failed = 0, r;
581 dev_t rootdev;
12aad1d0
LP
582
583 assert(head);
0494cae0 584 assert(changed);
12aad1d0 585
33e8d8af
FB
586 r = get_block_device("/", &rootdev);
587 if (r <= 0)
588 rootdev = 0;
7fc942b2 589
12aad1d0 590 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
12aad1d0 591
0494cae0
JJ
592 if (major(rootdev) != 0 && rootdev == m->devnum) {
593 n_failed ++;
594 continue;
595 }
7fc942b2 596
735e0712 597 log_info("Detaching DM %u:%u.", major(m->devnum), minor(m->devnum));
bce93b7a
MS
598 r = delete_dm(m->devnum);
599 if (r >= 0) {
0494cae0 600 *changed = true;
12aad1d0
LP
601 mount_point_free(head, m);
602 } else {
456b2199 603 log_full_errno(umount_log_level, errno, "Could not detach DM %s: %m", m->path);
12aad1d0 604 n_failed++;
d48141ba
LP
605 }
606 }
607
12aad1d0 608 return n_failed;
d48141ba
LP
609}
610
456b2199 611static int umount_all_once(bool *changed, int umount_log_level) {
e3478379 612 int r;
a6dcd229 613 _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head);
e3478379 614
0494cae0
JJ
615 assert(changed);
616
71fda00f 617 LIST_HEAD_INIT(mp_list_head);
6fa392bf 618 r = mount_points_list_get(NULL, &mp_list_head);
e3478379 619 if (r < 0)
a6dcd229 620 return r;
116e6d96 621
a6dcd229 622 return mount_points_list_umount(&mp_list_head, changed, umount_log_level);
116e6d96
AJ
623}
624
456b2199 625int umount_all(bool *changed, int umount_log_level) {
116e6d96
AJ
626 bool umount_changed;
627 int r;
628
0494cae0
JJ
629 assert(changed);
630
d5641e0d
KW
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. */
6f7f51f7
HH
634 do {
635 umount_changed = false;
3e085b6c 636
456b2199 637 r = umount_all_once(&umount_changed, umount_log_level);
6f7f51f7
HH
638 if (umount_changed)
639 *changed = true;
3e085b6c
LP
640 } while (umount_changed);
641
e3478379
FF
642 return r;
643}
644
12aad1d0 645int swapoff_all(bool *changed) {
a6dcd229 646 _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, swap_list_head);
e3478379 647 int r;
e3478379 648
0494cae0
JJ
649 assert(changed);
650
71fda00f 651 LIST_HEAD_INIT(swap_list_head);
e3478379 652
1fd8edb5 653 r = swap_list_get(NULL, &swap_list_head);
e3478379 654 if (r < 0)
a6dcd229 655 return r;
e3478379 656
a6dcd229 657 return swap_points_list_off(&swap_list_head, changed);
e3478379
FF
658}
659
456b2199 660int loopback_detach_all(bool *changed, int umount_log_level) {
a6dcd229 661 _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, loopback_list_head);
e3478379 662 int r;
e3478379 663
0494cae0
JJ
664 assert(changed);
665
71fda00f 666 LIST_HEAD_INIT(loopback_list_head);
e3478379
FF
667
668 r = loopback_list_get(&loopback_list_head);
669 if (r < 0)
a6dcd229 670 return r;
e3478379 671
a6dcd229 672 return loopback_points_list_detach(&loopback_list_head, changed, umount_log_level);
e3478379 673}
d48141ba 674
456b2199 675int dm_detach_all(bool *changed, int umount_log_level) {
a6dcd229 676 _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, dm_list_head);
d48141ba 677 int r;
d48141ba 678
0494cae0
JJ
679 assert(changed);
680
71fda00f 681 LIST_HEAD_INIT(dm_list_head);
d48141ba
LP
682
683 r = dm_list_get(&dm_list_head);
684 if (r < 0)
a6dcd229 685 return r;
d48141ba 686
a6dcd229 687 return dm_points_list_detach(&dm_list_head, changed, umount_log_level);
d48141ba 688}