]> git.proxmox.com Git - systemd.git/blame - src/core/umount.c
New upstream version 236
[systemd.git] / src / core / umount.c
CommitLineData
52ad194e 1/* SPDX-License-Identifier: LGPL-2.1+ */
663996b3
MS
2/***
3 This file is part of systemd.
4
5 Copyright 2010 ProFUSION embedded systems
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
21#include <errno.h>
22#include <fcntl.h>
db2df898 23#include <linux/loop.h>
663996b3
MS
24#include <string.h>
25#include <sys/mount.h>
26#include <sys/swap.h>
663996b3 27
db2df898
MP
28#include "libudev.h"
29
30#include "alloc-util.h"
52ad194e 31#include "def.h"
db2df898
MP
32#include "escape.h"
33#include "fd-util.h"
34#include "fstab-util.h"
81c58355 35#include "linux-3.13/dm-ioctl.h"
663996b3
MS
36#include "list.h"
37#include "mount-setup.h"
663996b3 38#include "path-util.h"
52ad194e 39#include "signal-util.h"
db2df898
MP
40#include "string-util.h"
41#include "udev-util.h"
42#include "umount.h"
f5e65279 43#include "mount-util.h"
663996b3
MS
44#include "util.h"
45#include "virt.h"
46
47typedef struct MountPoint {
48 char *path;
db2df898 49 char *options;
f5e65279 50 char *type;
663996b3 51 dev_t devnum;
60f067b4 52 LIST_FIELDS(struct MountPoint, mount_point);
663996b3
MS
53} MountPoint;
54
55static void mount_point_free(MountPoint **head, MountPoint *m) {
56 assert(head);
57 assert(m);
58
60f067b4 59 LIST_REMOVE(mount_point, *head, m);
663996b3
MS
60
61 free(m->path);
62 free(m);
63}
64
65static void mount_points_list_free(MountPoint **head) {
66 assert(head);
67
68 while (*head)
69 mount_point_free(head, *head);
70}
71
72static int mount_points_list_get(MountPoint **head) {
60f067b4 73 _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
663996b3 74 unsigned int i;
e3bff60a 75 int r;
663996b3
MS
76
77 assert(head);
78
60f067b4
JS
79 proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
80 if (!proc_self_mountinfo)
663996b3
MS
81 return -errno;
82
83 for (i = 1;; i++) {
f5e65279 84 _cleanup_free_ char *path = NULL, *options = NULL, *type = NULL;
60f067b4 85 char *p = NULL;
663996b3 86 MountPoint *m;
60f067b4 87 int k;
663996b3 88
60f067b4
JS
89 k = fscanf(proc_self_mountinfo,
90 "%*s " /* (1) mount id */
91 "%*s " /* (2) parent id */
92 "%*s " /* (3) major:minor */
93 "%*s " /* (4) root */
94 "%ms " /* (5) mount point */
db2df898 95 "%*s" /* (6) mount flags */
60f067b4
JS
96 "%*[^-]" /* (7) optional fields */
97 "- " /* (8) separator */
f5e65279 98 "%ms " /* (9) file system type */
60f067b4 99 "%*s" /* (10) mount source */
db2df898 100 "%ms" /* (11) mount options */
60f067b4 101 "%*[^\n]", /* some rubbish at the end */
f5e65279
MB
102 &path, &type, &options);
103 if (k != 3) {
663996b3
MS
104 if (k == EOF)
105 break;
106
107 log_warning("Failed to parse /proc/self/mountinfo:%u.", i);
663996b3
MS
108 continue;
109 }
110
e3bff60a
MP
111 r = cunescape(path, UNESCAPE_RELAX, &p);
112 if (r < 0)
113 return r;
663996b3
MS
114
115 /* Ignore mount points we can't unmount because they
116 * are API or because we are keeping them open (like
e735f4d4
MP
117 * /dev/console). Also, ignore all mounts below API
118 * file systems, since they are likely virtual too,
119 * and hence not worth spending time on. Also, in
120 * unprivileged containers we might lack the rights to
121 * unmount these things, hence don't bother. */
663996b3
MS
122 if (mount_point_is_api(p) ||
123 mount_point_ignore(p) ||
e735f4d4
MP
124 path_startswith(p, "/dev") ||
125 path_startswith(p, "/sys") ||
126 path_startswith(p, "/proc")) {
663996b3
MS
127 free(p);
128 continue;
129 }
130
60f067b4
JS
131 m = new0(MountPoint, 1);
132 if (!m) {
663996b3 133 free(p);
60f067b4 134 return -ENOMEM;
663996b3
MS
135 }
136
137 m->path = p;
db2df898
MP
138 m->options = options;
139 options = NULL;
f5e65279
MB
140 m->type = type;
141 type = NULL;
db2df898 142
60f067b4 143 LIST_PREPEND(mount_point, *head, m);
663996b3
MS
144 }
145
60f067b4 146 return 0;
663996b3
MS
147}
148
149static int swap_list_get(MountPoint **head) {
e842803a 150 _cleanup_fclose_ FILE *proc_swaps = NULL;
663996b3 151 unsigned int i;
e3bff60a 152 int r;
663996b3
MS
153
154 assert(head);
155
e3bff60a
MP
156 proc_swaps = fopen("/proc/swaps", "re");
157 if (!proc_swaps)
663996b3
MS
158 return (errno == ENOENT) ? 0 : -errno;
159
160 (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");
161
162 for (i = 2;; i++) {
163 MountPoint *swap;
52ad194e 164 _cleanup_free_ char *dev = NULL, *d = NULL;
663996b3
MS
165 int k;
166
e3bff60a
MP
167 k = fscanf(proc_swaps,
168 "%ms " /* device/file */
169 "%*s " /* type of swap */
170 "%*s " /* swap size */
171 "%*s " /* used */
172 "%*s\n", /* priority */
173 &dev);
663996b3 174
e3bff60a 175 if (k != 1) {
663996b3
MS
176 if (k == EOF)
177 break;
178
179 log_warning("Failed to parse /proc/swaps:%u.", i);
663996b3
MS
180 continue;
181 }
182
52ad194e 183 if (endswith(dev, " (deleted)"))
663996b3 184 continue;
663996b3 185
e3bff60a 186 r = cunescape(dev, UNESCAPE_RELAX, &d);
e3bff60a
MP
187 if (r < 0)
188 return r;
663996b3 189
e3bff60a 190 swap = new0(MountPoint, 1);
52ad194e 191 if (!swap)
e842803a 192 return -ENOMEM;
663996b3 193
52ad194e 194 free_and_replace(swap->path, d);
60f067b4 195 LIST_PREPEND(mount_point, *head, swap);
663996b3
MS
196 }
197
e842803a 198 return 0;
663996b3
MS
199}
200
201static int loopback_list_get(MountPoint **head) {
60f067b4 202 _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
663996b3 203 struct udev_list_entry *item = NULL, *first = NULL;
60f067b4
JS
204 _cleanup_udev_unref_ struct udev *udev = NULL;
205 int r;
663996b3
MS
206
207 assert(head);
208
60f067b4
JS
209 udev = udev_new();
210 if (!udev)
211 return -ENOMEM;
663996b3 212
60f067b4
JS
213 e = udev_enumerate_new(udev);
214 if (!e)
215 return -ENOMEM;
663996b3 216
60f067b4
JS
217 r = udev_enumerate_add_match_subsystem(e, "block");
218 if (r < 0)
219 return r;
663996b3 220
60f067b4
JS
221 r = udev_enumerate_add_match_sysname(e, "loop*");
222 if (r < 0)
223 return r;
224
225 r = udev_enumerate_add_match_sysattr(e, "loop/backing_file", NULL);
226 if (r < 0)
227 return r;
228
229 r = udev_enumerate_scan_devices(e);
230 if (r < 0)
231 return r;
663996b3
MS
232
233 first = udev_enumerate_get_list_entry(e);
234 udev_list_entry_foreach(item, first) {
235 MountPoint *lb;
60f067b4 236 _cleanup_udev_device_unref_ struct udev_device *d;
663996b3
MS
237 char *loop;
238 const char *dn;
239
60f067b4
JS
240 d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
241 if (!d)
242 return -ENOMEM;
663996b3 243
60f067b4
JS
244 dn = udev_device_get_devnode(d);
245 if (!dn)
663996b3 246 continue;
663996b3
MS
247
248 loop = strdup(dn);
60f067b4
JS
249 if (!loop)
250 return -ENOMEM;
663996b3 251
60f067b4
JS
252 lb = new0(MountPoint, 1);
253 if (!lb) {
663996b3 254 free(loop);
60f067b4 255 return -ENOMEM;
663996b3
MS
256 }
257
258 lb->path = loop;
60f067b4 259 LIST_PREPEND(mount_point, *head, lb);
663996b3
MS
260 }
261
60f067b4 262 return 0;
663996b3
MS
263}
264
265static int dm_list_get(MountPoint **head) {
60f067b4 266 _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
663996b3 267 struct udev_list_entry *item = NULL, *first = NULL;
60f067b4
JS
268 _cleanup_udev_unref_ struct udev *udev = NULL;
269 int r;
663996b3
MS
270
271 assert(head);
272
60f067b4
JS
273 udev = udev_new();
274 if (!udev)
275 return -ENOMEM;
663996b3 276
60f067b4
JS
277 e = udev_enumerate_new(udev);
278 if (!e)
279 return -ENOMEM;
663996b3 280
60f067b4
JS
281 r = udev_enumerate_add_match_subsystem(e, "block");
282 if (r < 0)
283 return r;
663996b3 284
60f067b4
JS
285 r = udev_enumerate_add_match_sysname(e, "dm-*");
286 if (r < 0)
287 return r;
663996b3 288
60f067b4
JS
289 r = udev_enumerate_scan_devices(e);
290 if (r < 0)
291 return r;
663996b3 292
60f067b4 293 first = udev_enumerate_get_list_entry(e);
663996b3
MS
294 udev_list_entry_foreach(item, first) {
295 MountPoint *m;
60f067b4 296 _cleanup_udev_device_unref_ struct udev_device *d;
663996b3
MS
297 dev_t devnum;
298 char *node;
299 const char *dn;
300
60f067b4
JS
301 d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
302 if (!d)
303 return -ENOMEM;
663996b3
MS
304
305 devnum = udev_device_get_devnum(d);
306 dn = udev_device_get_devnode(d);
60f067b4 307 if (major(devnum) == 0 || !dn)
663996b3 308 continue;
663996b3
MS
309
310 node = strdup(dn);
60f067b4
JS
311 if (!node)
312 return -ENOMEM;
663996b3 313
60f067b4
JS
314 m = new(MountPoint, 1);
315 if (!m) {
663996b3 316 free(node);
60f067b4 317 return -ENOMEM;
663996b3
MS
318 }
319
320 m->path = node;
321 m->devnum = devnum;
60f067b4 322 LIST_PREPEND(mount_point, *head, m);
663996b3
MS
323 }
324
60f067b4 325 return 0;
663996b3
MS
326}
327
328static int delete_loopback(const char *device) {
60f067b4
JS
329 _cleanup_close_ int fd = -1;
330 int r;
663996b3 331
60f067b4
JS
332 fd = open(device, O_RDONLY|O_CLOEXEC);
333 if (fd < 0)
663996b3
MS
334 return errno == ENOENT ? 0 : -errno;
335
336 r = ioctl(fd, LOOP_CLR_FD, 0);
663996b3
MS
337 if (r >= 0)
338 return 1;
339
340 /* ENXIO: not bound, so no error */
341 if (errno == ENXIO)
342 return 0;
343
344 return -errno;
345}
346
347static int delete_dm(dev_t devnum) {
2897b343 348
663996b3 349 struct dm_ioctl dm = {
2897b343
MP
350 .version = {
351 DM_VERSION_MAJOR,
352 DM_VERSION_MINOR,
353 DM_VERSION_PATCHLEVEL
354 },
663996b3
MS
355 .data_size = sizeof(dm),
356 .dev = devnum,
357 };
358
2897b343
MP
359 _cleanup_close_ int fd = -1;
360
663996b3
MS
361 assert(major(devnum) != 0);
362
363 fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC);
364 if (fd < 0)
365 return -errno;
366
2897b343
MP
367 if (ioctl(fd, DM_DEV_REMOVE, &dm) < 0)
368 return -errno;
369
370 return 0;
663996b3
MS
371}
372
81c58355
MB
373static bool nonunmountable_path(const char *path) {
374 return path_equal(path, "/")
f5e65279 375#if ! HAVE_SPLIT_USR
81c58355
MB
376 || path_equal(path, "/usr")
377#endif
378 || path_startswith(path, "/run/initramfs");
379}
380
52ad194e
MB
381static int remount_with_timeout(MountPoint *m, char *options, int *n_failed) {
382 pid_t pid;
383 int r;
384
385 BLOCK_SIGNALS(SIGCHLD);
386
387 /* Due to the possiblity of a remount operation hanging, we
388 * fork a child process and set a timeout. If the timeout
389 * lapses, the assumption is that that particular remount
390 * failed. */
391 pid = fork();
392 if (pid < 0)
393 return log_error_errno(errno, "Failed to fork: %m");
394
395 if (pid == 0) {
396 log_info("Remounting '%s' read-only in with options '%s'.", m->path, options);
397
398 /* Start the mount operation here in the child */
399 r = mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, options);
400 if (r < 0)
401 log_error_errno(errno, "Failed to remount '%s' read-only: %m", m->path);
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) {
408 log_error_errno(errno, "Remounting '%s' - timed out, issuing SIGKILL to PID "PID_FMT".", m->path, pid);
409 (void) kill(pid, SIGKILL);
410 } else if (r < 0)
411 log_error_errno(r, "Failed to wait for process: %m");
412
413 return r;
414}
415
416static int umount_with_timeout(MountPoint *m, bool *changed) {
417 pid_t pid;
418 int r;
419
420 BLOCK_SIGNALS(SIGCHLD);
421
422 /* Due to the possiblity of a umount operation hanging, we
423 * fork a child process and set a timeout. If the timeout
424 * lapses, the assumption is that that particular umount
425 * failed. */
426 pid = fork();
427 if (pid < 0)
428 return log_error_errno(errno, "Failed to fork: %m");
429
430 if (pid == 0) {
431 log_info("Unmounting '%s'.", m->path);
432
433 /* Start the mount operation here in the child Using MNT_FORCE
434 * causes some filesystems (e.g. FUSE and NFS and other network
435 * filesystems) to abort any pending requests and return -EIO
436 * rather than blocking indefinitely. If the filesysten is
437 * "busy", this may allow processes to die, thus making the
438 * filesystem less busy so the unmount might succeed (rather
439 * then return EBUSY).*/
440 r = umount2(m->path, MNT_FORCE);
441 if (r < 0)
442 log_error_errno(errno, "Failed to unmount %s: %m", m->path);
443
444 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
445 }
446
447 r = wait_for_terminate_with_timeout(pid, DEFAULT_TIMEOUT_USEC);
448 if (r == -ETIMEDOUT) {
449 log_error_errno(errno, "Unmounting '%s' - timed out, issuing SIGKILL to PID "PID_FMT".", m->path, pid);
450 (void) kill(pid, SIGKILL);
451 } else if (r < 0)
452 log_error_errno(r, "Failed to wait for process: %m");
453
454 return r;
455}
456
457/* This includes remounting readonly, which changes the kernel mount options.
458 * Therefore the list passed to this function is invalidated, and should not be reused. */
459
460static int mount_points_list_umount(MountPoint **head, bool *changed) {
461 MountPoint *m;
663996b3
MS
462 int n_failed = 0;
463
464 assert(head);
465
52ad194e
MB
466 LIST_FOREACH(mount_point, m, *head) {
467 bool mount_is_readonly;
468
469 mount_is_readonly = fstab_test_yes_no_option(m->options, "ro\0rw\0");
663996b3
MS
470
471 /* If we are in a container, don't attempt to
472 read-only mount anything as that brings no real
473 benefits, but might confuse the host, as we remount
f5e65279
MB
474 the superblock here, not the bind mount.
475 If the filesystem is a network fs, also skip the
476 remount. It brings no value (we cannot leave
52ad194e
MB
477 a "dirty fs") and could hang if the network is down.
478 Note that umount2() is more careful and will not
479 hang because of the network being down. */
f5e65279 480 if (detect_container() <= 0 &&
52ad194e
MB
481 !fstype_is_network(m->type) &&
482 !mount_is_readonly) {
db2df898
MP
483 _cleanup_free_ char *options = NULL;
484 /* MS_REMOUNT requires that the data parameter
485 * should be the same from the original mount
486 * except for the desired changes. Since we want
487 * to remount read-only, we should filter out
488 * rw (and ro too, because it confuses the kernel) */
489 (void) fstab_filter_options(m->options, "rw\0ro\0", NULL, NULL, &options);
490
663996b3
MS
491 /* We always try to remount directories
492 * read-only first, before we go on and umount
493 * them.
494 *
495 * Mount points can be stacked. If a mount
496 * point is stacked below / or /usr, we
497 * cannot umount or remount it directly,
498 * since there is no way to refer to the
499 * underlying mount. There's nothing we can do
500 * about it for the general case, but we can
501 * do something about it if it is aliased
502 * somehwere else via a bind mount. If we
503 * explicitly remount the super block of that
504 * alias read-only we hence should be
81c58355 505 * relatively safe regarding keeping dirty an fs
52ad194e
MB
506 * we cannot otherwise see.
507 *
508 * Since the remount can hang in the instance of
509 * remote filesystems, we remount asynchronously
510 * and skip the subsequent umount if it fails */
511 if (remount_with_timeout(m, options, &n_failed) < 0) {
81c58355
MB
512 if (nonunmountable_path(m->path))
513 n_failed++;
52ad194e 514 continue;
81c58355 515 }
663996b3
MS
516 }
517
518 /* Skip / and /usr since we cannot unmount that
519 * anyway, since we are running from it. They have
520 * already been remounted ro. */
81c58355 521 if (nonunmountable_path(m->path))
663996b3
MS
522 continue;
523
52ad194e
MB
524 /* Trying to umount */
525 if (umount_with_timeout(m, changed) < 0)
526 n_failed++;
527 else {
663996b3
MS
528 if (changed)
529 *changed = true;
663996b3
MS
530 }
531 }
532
533 return n_failed;
534}
535
536static int swap_points_list_off(MountPoint **head, bool *changed) {
537 MountPoint *m, *n;
538 int n_failed = 0;
539
540 assert(head);
541
542 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
543 log_info("Deactivating swap %s.", m->path);
544 if (swapoff(m->path) == 0) {
545 if (changed)
546 *changed = true;
547
548 mount_point_free(head, m);
549 } else {
f47781d8 550 log_warning_errno(errno, "Could not deactivate swap %s: %m", m->path);
663996b3
MS
551 n_failed++;
552 }
553 }
554
555 return n_failed;
556}
557
558static int loopback_points_list_detach(MountPoint **head, bool *changed) {
559 MountPoint *m, *n;
560 int n_failed = 0, k;
561 struct stat root_st;
562
563 assert(head);
564
565 k = lstat("/", &root_st);
566
567 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
568 int r;
569 struct stat loopback_st;
570
571 if (k >= 0 &&
572 major(root_st.st_dev) != 0 &&
573 lstat(m->path, &loopback_st) >= 0 &&
574 root_st.st_dev == loopback_st.st_rdev) {
aa27b158 575 n_failed++;
663996b3
MS
576 continue;
577 }
578
579 log_info("Detaching loopback %s.", m->path);
580 r = delete_loopback(m->path);
581 if (r >= 0) {
582 if (r > 0 && changed)
583 *changed = true;
584
585 mount_point_free(head, m);
586 } else {
f47781d8 587 log_warning_errno(errno, "Could not detach loopback %s: %m", m->path);
663996b3
MS
588 n_failed++;
589 }
590 }
591
592 return n_failed;
593}
594
595static int dm_points_list_detach(MountPoint **head, bool *changed) {
596 MountPoint *m, *n;
f5e65279
MB
597 int n_failed = 0, r;
598 dev_t rootdev;
663996b3
MS
599
600 assert(head);
601
f5e65279
MB
602 r = get_block_device("/", &rootdev);
603 if (r <= 0)
604 rootdev = 0;
663996b3
MS
605
606 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
663996b3 607
f5e65279
MB
608 if (major(rootdev) != 0)
609 if (rootdev == m->devnum) {
610 n_failed ++;
611 continue;
612 }
663996b3
MS
613
614 log_info("Detaching DM %u:%u.", major(m->devnum), minor(m->devnum));
615 r = delete_dm(m->devnum);
616 if (r >= 0) {
617 if (changed)
618 *changed = true;
619
620 mount_point_free(head, m);
621 } else {
f47781d8 622 log_warning_errno(errno, "Could not detach DM %s: %m", m->path);
663996b3
MS
623 n_failed++;
624 }
625 }
626
627 return n_failed;
628}
629
52ad194e 630static int umount_all_once(bool *changed) {
663996b3 631 int r;
663996b3
MS
632 LIST_HEAD(MountPoint, mp_list_head);
633
60f067b4 634 LIST_HEAD_INIT(mp_list_head);
663996b3
MS
635 r = mount_points_list_get(&mp_list_head);
636 if (r < 0)
637 goto end;
638
52ad194e
MB
639 r = mount_points_list_umount(&mp_list_head, changed);
640
641 end:
642 mount_points_list_free(&mp_list_head);
643
644 return r;
645}
646
647int umount_all(bool *changed) {
648 bool umount_changed;
649 int r;
650
651 /* Retry umount, until nothing can be umounted anymore. Mounts are
652 * processed in order, newest first. The retries are needed when
653 * an old mount has been moved, to a path inside a newer mount. */
663996b3
MS
654 do {
655 umount_changed = false;
656
52ad194e 657 r = umount_all_once(&umount_changed);
663996b3
MS
658 if (umount_changed)
659 *changed = true;
663996b3
MS
660 } while (umount_changed);
661
663996b3
MS
662 return r;
663}
664
665int swapoff_all(bool *changed) {
666 int r;
667 LIST_HEAD(MountPoint, swap_list_head);
668
60f067b4 669 LIST_HEAD_INIT(swap_list_head);
663996b3
MS
670
671 r = swap_list_get(&swap_list_head);
672 if (r < 0)
673 goto end;
674
675 r = swap_points_list_off(&swap_list_head, changed);
676
677 end:
678 mount_points_list_free(&swap_list_head);
679
680 return r;
681}
682
683int loopback_detach_all(bool *changed) {
684 int r;
685 LIST_HEAD(MountPoint, loopback_list_head);
686
60f067b4 687 LIST_HEAD_INIT(loopback_list_head);
663996b3
MS
688
689 r = loopback_list_get(&loopback_list_head);
690 if (r < 0)
691 goto end;
692
693 r = loopback_points_list_detach(&loopback_list_head, changed);
694
695 end:
696 mount_points_list_free(&loopback_list_head);
697
698 return r;
699}
700
701int dm_detach_all(bool *changed) {
702 int r;
703 LIST_HEAD(MountPoint, dm_list_head);
704
60f067b4 705 LIST_HEAD_INIT(dm_list_head);
663996b3
MS
706
707 r = dm_list_get(&dm_list_head);
708 if (r < 0)
709 goto end;
710
711 r = dm_points_list_detach(&dm_list_head, changed);
712
713 end:
714 mount_points_list_free(&dm_list_head);
715
716 return r;
717}