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