]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/umount.c
tree-wide: beautify remaining copyright statements
[thirdparty/systemd.git] / src / core / umount.c
index 7f8ddb99ee47deec577c9f9f374ea24cc5d985dc..241fe6fc625ab357e727a1213a4c0a7a169d5bca 100644 (file)
@@ -1,21 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 /***
-  This file is part of systemd.
-
-  Copyright 2010 ProFUSION embedded systems
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+  Copyright © 2010 ProFUSION embedded systems
 ***/
 
 #include <errno.h>
 #include <sys/mount.h>
 #include <sys/swap.h>
 
+/* This needs to be after sys/mount.h :( */
+#include <libmount.h>
+
 #include "libudev.h"
 
 #include "alloc-util.h"
+#include "blockdev-util.h"
 #include "def.h"
 #include "escape.h"
 #include "fd-util.h"
 #include "fstab-util.h"
 #include "linux-3.13/dm-ioctl.h"
-#include "list.h"
 #include "mount-setup.h"
+#include "mount-util.h"
 #include "path-util.h"
+#include "process-util.h"
 #include "signal-util.h"
 #include "string-util.h"
 #include "udev-util.h"
 #include "umount.h"
-#include "mount-util.h"
 #include "util.h"
 #include "virt.h"
 
-typedef struct MountPoint {
-        char *path;
-        char *options;
-        char *type;
-        dev_t devnum;
-        LIST_FIELDS(struct MountPoint, mount_point);
-} MountPoint;
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_table*, mnt_free_table);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_iter*, mnt_free_iter);
 
 static void mount_point_free(MountPoint **head, MountPoint *m) {
         assert(head);
@@ -59,58 +43,57 @@ static void mount_point_free(MountPoint **head, MountPoint *m) {
         LIST_REMOVE(mount_point, *head, m);
 
         free(m->path);
+        free(m->remount_options);
         free(m);
 }
 
-static void mount_points_list_free(MountPoint **head) {
+void mount_points_list_free(MountPoint **head) {
         assert(head);
 
         while (*head)
                 mount_point_free(head, *head);
 }
 
-static int mount_points_list_get(MountPoint **head) {
-        _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
-        unsigned int i;
+int mount_points_list_get(const char *mountinfo, MountPoint **head) {
+        _cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL;
+        _cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL;
         int r;
 
         assert(head);
 
-        proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
-        if (!proc_self_mountinfo)
-                return -errno;
+        t = mnt_new_table();
+        i = mnt_new_iter(MNT_ITER_FORWARD);
+        if (!t || !i)
+                return log_oom();
 
-        for (i = 1;; i++) {
-                _cleanup_free_ char *path = NULL, *options = NULL, *type = NULL;
-                char *p = NULL;
+        r = mnt_table_parse_mtab(t, mountinfo);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse %s: %m", mountinfo);
+
+        for (;;) {
+                struct libmnt_fs *fs;
+                const char *path, *options, *fstype;
+                _cleanup_free_ char *p = NULL;
+                unsigned long remount_flags = 0u;
+                _cleanup_free_ char *remount_options = NULL;
+                bool try_remount_ro;
                 MountPoint *m;
-                int k;
-
-                k = fscanf(proc_self_mountinfo,
-                           "%*s "       /* (1) mount id */
-                           "%*s "       /* (2) parent id */
-                           "%*s "       /* (3) major:minor */
-                           "%*s "       /* (4) root */
-                           "%ms "       /* (5) mount point */
-                           "%*s"        /* (6) mount flags */
-                           "%*[^-]"     /* (7) optional fields */
-                           "- "         /* (8) separator */
-                           "%ms "       /* (9) file system type */
-                           "%*s"        /* (10) mount source */
-                           "%ms"        /* (11) mount options */
-                           "%*[^\n]",   /* some rubbish at the end */
-                           &path, &type, &options);
-                if (k != 3) {
-                        if (k == EOF)
-                                break;
-
-                        log_warning("Failed to parse /proc/self/mountinfo:%u.", i);
-                        continue;
-                }
 
-                r = cunescape(path, UNESCAPE_RELAX, &p);
+                r = mnt_table_next_fs(t, i, &fs);
+                if (r == 1)
+                        break;
                 if (r < 0)
-                        return r;
+                        return log_error_errno(r, "Failed to get next entry from %s: %m", mountinfo);
+
+                path = mnt_fs_get_target(fs);
+                if (!path)
+                        continue;
+
+                if (cunescape(path, UNESCAPE_RELAX, &p) < 0)
+                        return log_oom();
+
+                options = mnt_fs_get_options(fs);
+                fstype = mnt_fs_get_fstype(fs);
 
                 /* Ignore mount points we can't unmount because they
                  * are API or because we are keeping them open (like
@@ -123,22 +106,56 @@ static int mount_points_list_get(MountPoint **head) {
                     mount_point_ignore(p) ||
                     path_startswith(p, "/dev") ||
                     path_startswith(p, "/sys") ||
-                    path_startswith(p, "/proc")) {
-                        free(p);
+                    path_startswith(p, "/proc"))
                         continue;
+
+                /* If we are in a container, don't attempt to
+                 * read-only mount anything as that brings no real
+                 * benefits, but might confuse the host, as we remount
+                 * the superblock here, not the bind mount.
+                 *
+                 * If the filesystem is a network fs, also skip the
+                 * remount. It brings no value (we cannot leave
+                 * a "dirty fs") and could hang if the network is down.
+                 * Note that umount2() is more careful and will not
+                 * hang because of the network being down. */
+                try_remount_ro = detect_container() <= 0 &&
+                                 !fstype_is_network(fstype) &&
+                                 !fstype_is_api_vfs(fstype) &&
+                                 !fstype_is_ro(fstype) &&
+                                 !fstab_test_yes_no_option(options, "ro\0rw\0");
+
+                if (try_remount_ro) {
+                        /* mount(2) states that mount flags and options need to be exactly the same
+                         * as they were when the filesystem was mounted, except for the desired
+                         * changes. So we reconstruct both here and adjust them for the later
+                         * remount call too. */
+
+                        r = mnt_fs_get_propagation(fs, &remount_flags);
+                        if (r < 0) {
+                                log_warning_errno(r, "mnt_fs_get_propagation() failed for %s, ignoring: %m", path);
+                                continue;
+                        }
+
+                        r = mount_option_mangle(options, remount_flags, &remount_flags, &remount_options);
+                        if (r < 0) {
+                                log_warning_errno(r, "mount_option_mangle failed for %s, ignoring: %m", path);
+                                continue;
+                        }
+
+                        /* MS_BIND is special. If it is provided it will only make the mount-point
+                         * read-only. If left out, the super block itself is remounted, which we want. */
+                        remount_flags = (remount_flags|MS_REMOUNT|MS_RDONLY) & ~MS_BIND;
                 }
 
                 m = new0(MountPoint, 1);
-                if (!m) {
-                        free(p);
-                        return -ENOMEM;
-                }
+                if (!m)
+                        return log_oom();
 
-                m->path = p;
-                m->options = options;
-                options = NULL;
-                m->type = type;
-                type = NULL;
+                free_and_replace(m->path, p);
+                free_and_replace(m->remount_options, remount_options);
+                m->remount_flags = remount_flags;
+                m->try_remount_ro = try_remount_ro;
 
                 LIST_PREPEND(mount_point, *head, m);
         }
@@ -146,44 +163,40 @@ static int mount_points_list_get(MountPoint **head) {
         return 0;
 }
 
-static int swap_list_get(MountPoint **head) {
-        _cleanup_fclose_ FILE *proc_swaps = NULL;
-        unsigned int i;
+int swap_list_get(const char *swaps, MountPoint **head) {
+        _cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL;
+        _cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL;
         int r;
 
         assert(head);
 
-        proc_swaps = fopen("/proc/swaps", "re");
-        if (!proc_swaps)
-                return (errno == ENOENT) ? 0 : -errno;
+        t = mnt_new_table();
+        i = mnt_new_iter(MNT_ITER_FORWARD);
+        if (!t || !i)
+                return log_oom();
+
+        r = mnt_table_parse_swaps(t, swaps);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse %s: %m", swaps);
 
-        (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");
+        for (;;) {
+                struct libmnt_fs *fs;
 
-        for (i = 2;; i++) {
                 MountPoint *swap;
-                _cleanup_free_ char *dev = NULL, *d = NULL;
-                int k;
-
-                k = fscanf(proc_swaps,
-                           "%ms " /* device/file */
-                           "%*s " /* type of swap */
-                           "%*s " /* swap size */
-                           "%*s " /* used */
-                           "%*s\n", /* priority */
-                           &dev);
-
-                if (k != 1) {
-                        if (k == EOF)
-                                break;
-
-                        log_warning("Failed to parse /proc/swaps:%u.", i);
-                        continue;
-                }
+                const char *source;
+                _cleanup_free_ char *d = NULL;
+
+                r = mnt_table_next_fs(t, i, &fs);
+                if (r == 1)
+                        break;
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get next entry from %s: %m", swaps);
 
-                if (endswith(dev, " (deleted)"))
+                source = mnt_fs_get_source(fs);
+                if (!source)
                         continue;
 
-                r = cunescape(dev, UNESCAPE_RELAX, &d);
+                r = cunescape(source, UNESCAPE_RELAX, &d);
                 if (r < 0)
                         return r;
 
@@ -199,9 +212,9 @@ static int swap_list_get(MountPoint **head) {
 }
 
 static int loopback_list_get(MountPoint **head) {
-        _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
+        _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
         struct udev_list_entry *item = NULL, *first = NULL;
-        _cleanup_udev_unref_ struct udev *udev = NULL;
+        _cleanup_(udev_unrefp) struct udev *udev = NULL;
         int r;
 
         assert(head);
@@ -232,10 +245,9 @@ static int loopback_list_get(MountPoint **head) {
 
         first = udev_enumerate_get_list_entry(e);
         udev_list_entry_foreach(item, first) {
-                MountPoint *lb;
-                _cleanup_udev_device_unref_ struct udev_device *d;
-                char *loop;
+                _cleanup_(udev_device_unrefp) struct udev_device *d;
                 const char *dn;
+                _cleanup_free_ MountPoint *lb = NULL;
 
                 d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
                 if (!d)
@@ -245,27 +257,25 @@ static int loopback_list_get(MountPoint **head) {
                 if (!dn)
                         continue;
 
-                loop = strdup(dn);
-                if (!loop)
-                        return -ENOMEM;
-
                 lb = new0(MountPoint, 1);
-                if (!lb) {
-                        free(loop);
+                if (!lb)
                         return -ENOMEM;
-                }
 
-                lb->path = loop;
+                r = free_and_strdup(&lb->path, dn);
+                if (r < 0)
+                        return r;
+
                 LIST_PREPEND(mount_point, *head, lb);
+                lb = NULL;
         }
 
         return 0;
 }
 
 static int dm_list_get(MountPoint **head) {
-        _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
+        _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
         struct udev_list_entry *item = NULL, *first = NULL;
-        _cleanup_udev_unref_ struct udev *udev = NULL;
+        _cleanup_(udev_unrefp) struct udev *udev = NULL;
         int r;
 
         assert(head);
@@ -292,11 +302,10 @@ static int dm_list_get(MountPoint **head) {
 
         first = udev_enumerate_get_list_entry(e);
         udev_list_entry_foreach(item, first) {
-                MountPoint *m;
-                _cleanup_udev_device_unref_ struct udev_device *d;
+                _cleanup_(udev_device_unrefp) struct udev_device *d;
                 dev_t devnum;
-                char *node;
                 const char *dn;
+                _cleanup_free_ MountPoint *m = NULL;
 
                 d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
                 if (!d)
@@ -307,19 +316,17 @@ static int dm_list_get(MountPoint **head) {
                 if (major(devnum) == 0 || !dn)
                         continue;
 
-                node = strdup(dn);
-                if (!node)
-                        return -ENOMEM;
-
-                m = new(MountPoint, 1);
-                if (!m) {
-                        free(node);
+                m = new0(MountPoint, 1);
+                if (!m)
                         return -ENOMEM;
-                }
 
-                m->path = node;
                 m->devnum = devnum;
+                r = free_and_strdup(&m->path, dn);
+                if (r < 0)
+                        return r;
+
                 LIST_PREPEND(mount_point, *head, m);
+                m = NULL;
         }
 
         return 0;
@@ -329,6 +336,8 @@ static int delete_loopback(const char *device) {
         _cleanup_close_ int fd = -1;
         int r;
 
+        assert(device);
+
         fd = open(device, O_RDONLY|O_CLOEXEC);
         if (fd < 0)
                 return errno == ENOENT ? 0 : -errno;
@@ -378,56 +387,60 @@ static bool nonunmountable_path(const char *path) {
                 || path_startswith(path, "/run/initramfs");
 }
 
-static int remount_with_timeout(MountPoint *m, char *options, int *n_failed) {
+static int remount_with_timeout(MountPoint *m, int umount_log_level) {
         pid_t pid;
         int r;
 
         BLOCK_SIGNALS(SIGCHLD);
 
+        assert(m);
+
         /* Due to the possiblity of a remount operation hanging, we
          * fork a child process and set a timeout. If the timeout
          * lapses, the assumption is that that particular remount
          * failed. */
-        pid = fork();
-        if (pid < 0)
-                return log_error_errno(errno, "Failed to fork: %m");
-
-        if (pid == 0) {
-                log_info("Remounting '%s' read-only in with options '%s'.", m->path, options);
+        r = safe_fork("(sd-remount)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_REOPEN_LOG, &pid);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                log_info("Remounting '%s' read-only in with options '%s'.", m->path, m->remount_options);
 
                 /* Start the mount operation here in the child */
-                r = mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, options);
+                r = mount(NULL, m->path, NULL, m->remount_flags, m->remount_options);
                 if (r < 0)
-                        log_error_errno(errno, "Failed to remount '%s' read-only: %m", m->path);
+                        log_full_errno(umount_log_level, errno, "Failed to remount '%s' read-only: %m", m->path);
 
                 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
         }
 
         r = wait_for_terminate_with_timeout(pid, DEFAULT_TIMEOUT_USEC);
         if (r == -ETIMEDOUT) {
-                log_error_errno(errno, "Remounting '%s' - timed out, issuing SIGKILL to PID "PID_FMT".", m->path, pid);
+                log_error_errno(r, "Remounting '%s' timed out, issuing SIGKILL to PID " PID_FMT ".", m->path, pid);
                 (void) kill(pid, SIGKILL);
-        } else if (r < 0)
-                log_error_errno(r, "Failed to wait for process: %m");
+        } else if (r == -EPROTO)
+                log_debug_errno(r, "Remounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid);
+        else if (r < 0)
+                log_error_errno(r, "Remounting '%s' failed unexpectedly, couldn't wait for child process " PID_FMT ": %m", m->path, pid);
 
         return r;
 }
 
-static int umount_with_timeout(MountPoint *m, bool *changed) {
+static int umount_with_timeout(MountPoint *m, int umount_log_level) {
         pid_t pid;
         int r;
 
         BLOCK_SIGNALS(SIGCHLD);
 
+        assert(m);
+
         /* Due to the possiblity of a umount operation hanging, we
          * fork a child process and set a timeout. If the timeout
          * lapses, the assumption is that that particular umount
          * failed. */
-        pid = fork();
-        if (pid < 0)
-                return log_error_errno(errno, "Failed to fork: %m");
-
-        if (pid == 0) {
+        r = safe_fork("(sd-umount)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_REOPEN_LOG, &pid);
+        if (r < 0)
+                return r;
+        if (r == 0) {
                 log_info("Unmounting '%s'.", m->path);
 
                 /* Start the mount operation here in the child Using MNT_FORCE
@@ -439,55 +452,34 @@ static int umount_with_timeout(MountPoint *m, bool *changed) {
                  * then return EBUSY).*/
                 r = umount2(m->path, MNT_FORCE);
                 if (r < 0)
-                        log_error_errno(errno, "Failed to unmount %s: %m", m->path);
+                        log_full_errno(umount_log_level, errno, "Failed to unmount %s: %m", m->path);
 
                 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
         }
 
         r = wait_for_terminate_with_timeout(pid, DEFAULT_TIMEOUT_USEC);
         if (r == -ETIMEDOUT) {
-                log_error_errno(errno, "Unmounting '%s' - timed out, issuing SIGKILL to PID "PID_FMT".", m->path, pid);
+                log_error_errno(r, "Unmounting '%s' timed out, issuing SIGKILL to PID " PID_FMT ".", m->path, pid);
                 (void) kill(pid, SIGKILL);
-        } else if (r < 0)
-                log_error_errno(r, "Failed to wait for process: %m");
+        } else if (r == -EPROTO)
+                log_debug_errno(r, "Unmounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid);
+        else if (r < 0)
+                log_error_errno(r, "Unmounting '%s' failed unexpectedly, couldn't wait for child process " PID_FMT ": %m", m->path, pid);
 
         return r;
 }
 
 /* This includes remounting readonly, which changes the kernel mount options.
  * Therefore the list passed to this function is invalidated, and should not be reused. */
-
-static int mount_points_list_umount(MountPoint **head, bool *changed) {
+static int mount_points_list_umount(MountPoint **head, bool *changed, int umount_log_level) {
         MountPoint *m;
         int n_failed = 0;
 
         assert(head);
+        assert(changed);
 
         LIST_FOREACH(mount_point, m, *head) {
-                bool mount_is_readonly;
-
-                mount_is_readonly = fstab_test_yes_no_option(m->options, "ro\0rw\0");
-
-                /* If we are in a container, don't attempt to
-                   read-only mount anything as that brings no real
-                   benefits, but might confuse the host, as we remount
-                   the superblock here, not the bind mount.
-                   If the filesystem is a network fs, also skip the
-                   remount.  It brings no value (we cannot leave
-                   a "dirty fs") and could hang if the network is down.
-                   Note that umount2() is more careful and will not
-                   hang because of the network being down. */
-                if (detect_container() <= 0 &&
-                    !fstype_is_network(m->type) &&
-                    !mount_is_readonly) {
-                        _cleanup_free_ char *options = NULL;
-                        /* MS_REMOUNT requires that the data parameter
-                         * should be the same from the original mount
-                         * except for the desired changes. Since we want
-                         * to remount read-only, we should filter out
-                         * rw (and ro too, because it confuses the kernel) */
-                        (void) fstab_filter_options(m->options, "rw\0ro\0", NULL, NULL, &options);
-
+                if (m->try_remount_ro) {
                         /* We always try to remount directories
                          * read-only first, before we go on and umount
                          * them.
@@ -502,16 +494,19 @@ static int mount_points_list_umount(MountPoint **head, bool *changed) {
                          * somehwere else via a bind mount. If we
                          * explicitly remount the super block of that
                          * alias read-only we hence should be
-                         * relatively safe regarding keeping dirty an fs
+                         * relatively safe regarding keeping a dirty fs
                          * we cannot otherwise see.
                          *
                          * Since the remount can hang in the instance of
                          * remote filesystems, we remount asynchronously
-                         * and skip the subsequent umount if it fails */
-                        if (remount_with_timeout(m, options, &n_failed) < 0) {
-                                if (nonunmountable_path(m->path))
+                         * and skip the subsequent umount if it fails. */
+                        if (remount_with_timeout(m, umount_log_level) < 0) {
+                                /* Remount failed, but try unmounting anyway,
+                                 * unless this is a mount point we want to skip. */
+                                if (nonunmountable_path(m->path)) {
                                         n_failed++;
-                                continue;
+                                        continue;
+                                }
                         }
                 }
 
@@ -522,12 +517,10 @@ static int mount_points_list_umount(MountPoint **head, bool *changed) {
                         continue;
 
                 /* Trying to umount */
-                if (umount_with_timeout(m, changed) < 0)
+                if (umount_with_timeout(m, umount_log_level) < 0)
                         n_failed++;
-                else {
-                        if (changed)
-                                *changed = true;
-                }
+                else
+                        *changed = true;
         }
 
         return n_failed;
@@ -538,13 +531,12 @@ static int swap_points_list_off(MountPoint **head, bool *changed) {
         int n_failed = 0;
 
         assert(head);
+        assert(changed);
 
         LIST_FOREACH_SAFE(mount_point, m, n, *head) {
                 log_info("Deactivating swap %s.", m->path);
                 if (swapoff(m->path) == 0) {
-                        if (changed)
-                                *changed = true;
-
+                        *changed = true;
                         mount_point_free(head, m);
                 } else {
                         log_warning_errno(errno, "Could not deactivate swap %s: %m", m->path);
@@ -555,12 +547,13 @@ static int swap_points_list_off(MountPoint **head, bool *changed) {
         return n_failed;
 }
 
-static int loopback_points_list_detach(MountPoint **head, bool *changed) {
+static int loopback_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) {
         MountPoint *m, *n;
         int n_failed = 0, k;
         struct stat root_st;
 
         assert(head);
+        assert(changed);
 
         k = lstat("/", &root_st);
 
@@ -579,12 +572,12 @@ static int loopback_points_list_detach(MountPoint **head, bool *changed) {
                 log_info("Detaching loopback %s.", m->path);
                 r = delete_loopback(m->path);
                 if (r >= 0) {
-                        if (r > 0 && changed)
+                        if (r > 0)
                                 *changed = true;
 
                         mount_point_free(head, m);
                 } else {
-                        log_warning_errno(errno, "Could not detach loopback %s: %m", m->path);
+                        log_full_errno(umount_log_level, errno, "Could not detach loopback %s: %m", m->path);
                         n_failed++;
                 }
         }
@@ -592,12 +585,13 @@ static int loopback_points_list_detach(MountPoint **head, bool *changed) {
         return n_failed;
 }
 
-static int dm_points_list_detach(MountPoint **head, bool *changed) {
+static int dm_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) {
         MountPoint *m, *n;
         int n_failed = 0, r;
         dev_t rootdev;
 
         assert(head);
+        assert(changed);
 
         r = get_block_device("/", &rootdev);
         if (r <= 0)
@@ -605,21 +599,18 @@ static int dm_points_list_detach(MountPoint **head, bool *changed) {
 
         LIST_FOREACH_SAFE(mount_point, m, n, *head) {
 
-                if (major(rootdev) != 0)
-                        if (rootdev == m->devnum) {
-                                n_failed ++;
-                                continue;
-                        }
+                if (major(rootdev) != 0 && rootdev == m->devnum) {
+                        n_failed ++;
+                        continue;
+                }
 
                 log_info("Detaching DM %u:%u.", major(m->devnum), minor(m->devnum));
                 r = delete_dm(m->devnum);
                 if (r >= 0) {
-                        if (changed)
-                                *changed = true;
-
+                        *changed = true;
                         mount_point_free(head, m);
                 } else {
-                        log_warning_errno(errno, "Could not detach DM %s: %m", m->path);
+                        log_full_errno(umount_log_level, errno, "Could not detach DM %s: %m", m->path);
                         n_failed++;
                 }
         }
@@ -627,34 +618,33 @@ static int dm_points_list_detach(MountPoint **head, bool *changed) {
         return n_failed;
 }
 
-static int umount_all_once(bool *changed) {
+static int umount_all_once(bool *changed, int umount_log_level) {
         int r;
-        LIST_HEAD(MountPoint, mp_list_head);
+        _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head);
+
+        assert(changed);
 
         LIST_HEAD_INIT(mp_list_head);
-        r = mount_points_list_get(&mp_list_head);
+        r = mount_points_list_get(NULL, &mp_list_head);
         if (r < 0)
-                goto end;
-
-        r = mount_points_list_umount(&mp_list_head, changed);
-
-  end:
-        mount_points_list_free(&mp_list_head);
+                return r;
 
-        return r;
+        return mount_points_list_umount(&mp_list_head, changed, umount_log_level);
 }
 
-int umount_all(bool *changed) {
+int umount_all(bool *changed, int umount_log_level) {
         bool umount_changed;
         int r;
 
+        assert(changed);
+
         /* Retry umount, until nothing can be umounted anymore. Mounts are
          * processed in order, newest first. The retries are needed when
          * an old mount has been moved, to a path inside a newer mount. */
         do {
                 umount_changed = false;
 
-                r = umount_all_once(&umount_changed);
+                r = umount_all_once(&umount_changed, umount_log_level);
                 if (umount_changed)
                         *changed = true;
         } while (umount_changed);
@@ -663,55 +653,46 @@ int umount_all(bool *changed) {
 }
 
 int swapoff_all(bool *changed) {
+        _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, swap_list_head);
         int r;
-        LIST_HEAD(MountPoint, swap_list_head);
+
+        assert(changed);
 
         LIST_HEAD_INIT(swap_list_head);
 
-        r = swap_list_get(&swap_list_head);
+        r = swap_list_get(NULL, &swap_list_head);
         if (r < 0)
-                goto end;
-
-        r = swap_points_list_off(&swap_list_head, changed);
-
-  end:
-        mount_points_list_free(&swap_list_head);
+                return r;
 
-        return r;
+        return swap_points_list_off(&swap_list_head, changed);
 }
 
-int loopback_detach_all(bool *changed) {
+int loopback_detach_all(bool *changed, int umount_log_level) {
+        _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, loopback_list_head);
         int r;
-        LIST_HEAD(MountPoint, loopback_list_head);
+
+        assert(changed);
 
         LIST_HEAD_INIT(loopback_list_head);
 
         r = loopback_list_get(&loopback_list_head);
         if (r < 0)
-                goto end;
-
-        r = loopback_points_list_detach(&loopback_list_head, changed);
-
-  end:
-        mount_points_list_free(&loopback_list_head);
+                return r;
 
-        return r;
+        return loopback_points_list_detach(&loopback_list_head, changed, umount_log_level);
 }
 
-int dm_detach_all(bool *changed) {
+int dm_detach_all(bool *changed, int umount_log_level) {
+        _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, dm_list_head);
         int r;
-        LIST_HEAD(MountPoint, dm_list_head);
+
+        assert(changed);
 
         LIST_HEAD_INIT(dm_list_head);
 
         r = dm_list_get(&dm_list_head);
         if (r < 0)
-                goto end;
-
-        r = dm_points_list_detach(&dm_list_head, changed);
-
-  end:
-        mount_points_list_free(&dm_list_head);
+                return r;
 
-        return r;
+        return dm_points_list_detach(&dm_list_head, changed, umount_log_level);
 }