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 33 : static void mount_point_free(MountPoint **head, MountPoint *m) {
40 33 : assert(head);
41 33 : assert(m);
42 :
43 33 : LIST_REMOVE(mount_point, *head, m);
44 :
45 33 : free(m->path);
46 33 : free(m->remount_options);
47 33 : free(m);
48 33 : }
49 :
50 6 : void mount_points_list_free(MountPoint **head) {
51 6 : assert(head);
52 :
53 39 : while (*head)
54 33 : mount_point_free(head, *head);
55 6 : }
56 :
57 4 : int mount_points_list_get(const char *mountinfo, MountPoint **head) {
58 4 : _cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL;
59 4 : _cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL;
60 : int r;
61 :
62 4 : assert(head);
63 :
64 4 : r = libmount_parse(mountinfo, NULL, &table, &iter);
65 4 : if (r < 0)
66 0 : return log_error_errno(r, "Failed to parse %s: %m", mountinfo);
67 :
68 88 : for (;;) {
69 : struct libmnt_fs *fs;
70 : const char *path, *fstype;
71 92 : _cleanup_free_ char *options = NULL;
72 92 : unsigned long remount_flags = 0u;
73 92 : _cleanup_free_ char *remount_options = NULL;
74 : bool try_remount_ro;
75 92 : _cleanup_free_ MountPoint *m = NULL;
76 :
77 92 : r = mnt_table_next_fs(table, iter, &fs);
78 92 : if (r == 1)
79 4 : break;
80 88 : if (r < 0)
81 0 : return log_error_errno(r, "Failed to get next entry from %s: %m", mountinfo);
82 :
83 88 : path = mnt_fs_get_target(fs);
84 88 : if (!path)
85 0 : continue;
86 :
87 88 : 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 88 : if (!strextend_with_separator(&options, ",",
98 : mnt_fs_get_vfs_options(fs),
99 : NULL))
100 0 : return log_oom();
101 88 : 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 132 : if (mount_point_is_api(path) ||
114 87 : mount_point_ignore(path) ||
115 43 : PATH_STARTSWITH_SET(path, "/dev", "/sys", "/proc"))
116 58 : 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 30 : try_remount_ro = detect_container() <= 0 &&
129 30 : !fstype_is_network(fstype) &&
130 30 : !fstype_is_api_vfs(fstype) &&
131 76 : !fstype_is_ro(fstype) &&
132 16 : !fstab_test_yes_no_option(options, "ro\0rw\0");
133 :
134 30 : 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 16 : r = mnt_fs_get_propagation(fs, &remount_flags);
141 16 : 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 16 : r = mount_option_mangle(options, remount_flags, &remount_flags, &remount_options);
147 16 : 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 16 : remount_flags = (remount_flags|MS_REMOUNT|MS_RDONLY) & ~MS_BIND;
155 : }
156 :
157 30 : m = new0(MountPoint, 1);
158 30 : if (!m)
159 0 : return log_oom();
160 :
161 30 : m->path = strdup(path);
162 30 : if (!m->path)
163 0 : return log_oom();
164 :
165 30 : m->remount_options = TAKE_PTR(remount_options);
166 30 : m->remount_flags = remount_flags;
167 30 : m->try_remount_ro = try_remount_ro;
168 :
169 30 : LIST_PREPEND(mount_point, *head, TAKE_PTR(m));
170 : }
171 :
172 4 : return 0;
173 : }
174 :
175 2 : int swap_list_get(const char *swaps, MountPoint **head) {
176 2 : _cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL;
177 2 : _cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL;
178 : int r;
179 :
180 2 : assert(head);
181 :
182 2 : t = mnt_new_table();
183 2 : i = mnt_new_iter(MNT_ITER_FORWARD);
184 2 : if (!t || !i)
185 0 : return log_oom();
186 :
187 2 : r = mnt_table_parse_swaps(t, swaps);
188 2 : if (r < 0)
189 0 : return log_error_errno(r, "Failed to parse %s: %m", swaps);
190 :
191 3 : for (;;) {
192 : struct libmnt_fs *fs;
193 5 : _cleanup_free_ MountPoint *swap = NULL;
194 : const char *source;
195 :
196 5 : r = mnt_table_next_fs(t, i, &fs);
197 5 : if (r == 1)
198 2 : break;
199 3 : if (r < 0)
200 0 : return log_error_errno(r, "Failed to get next entry from %s: %m", swaps);
201 :
202 3 : source = mnt_fs_get_source(fs);
203 3 : if (!source)
204 0 : continue;
205 :
206 3 : swap = new0(MountPoint, 1);
207 3 : if (!swap)
208 0 : return -ENOMEM;
209 :
210 3 : swap->path = strdup(source);
211 3 : if (!swap->path)
212 0 : return -ENOMEM;
213 :
214 3 : LIST_PREPEND(mount_point, *head, TAKE_PTR(swap));
215 : }
216 :
217 2 : 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 : }
|