]> 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 f9b4a21cfcbd69d402d449bd2d2619b0e631c85b..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>
@@ -25,6 +10,9 @@
 #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"
@@ -34,7 +22,6 @@
 #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 "util.h"
 #include "virt.h"
 
-typedef struct MountPoint {
-        char *path;
-        char *remount_options;
-        unsigned long remount_flags;
-        bool try_remount_ro;
-        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);
@@ -66,54 +47,53 @@ static void mount_point_free(MountPoint **head, MountPoint *m) {
         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, *flags = NULL, *type = NULL, *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 */
-                           "%ms"        /* (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, &flags, &type, &options);
-                if (k != 4) {
-                        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
@@ -129,12 +109,6 @@ static int mount_points_list_get(MountPoint **head) {
                     path_startswith(p, "/proc"))
                         continue;
 
-                m = new0(MountPoint, 1);
-                if (!m)
-                        return -ENOMEM;
-
-                free_and_replace(m->path, p);
-
                 /* 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
@@ -145,79 +119,84 @@ static int mount_points_list_get(MountPoint **head) {
                  * 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. */
-                m->try_remount_ro = detect_container() <= 0 &&
-                                    !fstype_is_network(type) &&
-                                    !fstype_is_api_vfs(type) &&
-                                    !fstype_is_ro(type) &&
-                                    !fstab_test_yes_no_option(options, "ro\0rw\0");
-
-                if (m->try_remount_ro) {
-                        _cleanup_free_ char *unknown_flags = NULL;
+                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 = mount_option_mangle(flags, 0, &m->remount_flags, &unknown_flags);
-                        if (r < 0)
-                                return r;
-                        if (!isempty(unknown_flags))
-                                log_warning("Ignoring unknown mount flags '%s'.", unknown_flags);
+                        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, m->remount_flags, &m->remount_flags, &m->remount_options);
-                        if (r < 0)
-                                return r;
+                        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. */
-                        m->remount_flags = (m->remount_flags|MS_REMOUNT|MS_RDONLY) & ~MS_BIND;
+                        remount_flags = (remount_flags|MS_REMOUNT|MS_RDONLY) & ~MS_BIND;
                 }
 
+                m = new0(MountPoint, 1);
+                if (!m)
+                        return log_oom();
+
+                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);
         }
 
         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();
 
-        (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");
+        r = mnt_table_parse_swaps(t, swaps);
+        if (r < 0)
+                return log_error_errno(r, "Failed to parse %s: %m", swaps);
+
+        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;
 
-                if (endswith(dev, " (deleted)"))
+                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);
+
+                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;
 
@@ -233,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);
@@ -266,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)
@@ -279,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);
@@ -326,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)
@@ -341,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;
@@ -414,7 +387,7 @@ static bool nonunmountable_path(const char *path) {
                 || path_startswith(path, "/run/initramfs");
 }
 
-static int remount_with_timeout(MountPoint *m) {
+static int remount_with_timeout(MountPoint *m, int umount_log_level) {
         pid_t pid;
         int r;
 
@@ -435,7 +408,7 @@ static int remount_with_timeout(MountPoint *m) {
                 /* Start the mount operation here in the child */
                 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);
         }
@@ -445,14 +418,14 @@ static int remount_with_timeout(MountPoint *m) {
                 log_error_errno(r, "Remounting '%s' timed out, issuing SIGKILL to PID " PID_FMT ".", m->path, pid);
                 (void) kill(pid, SIGKILL);
         } else if (r == -EPROTO)
-                log_error_errno(r, "Remounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid);
+                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) {
+static int umount_with_timeout(MountPoint *m, int umount_log_level) {
         pid_t pid;
         int r;
 
@@ -479,7 +452,7 @@ static int umount_with_timeout(MountPoint *m) {
                  * 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);
         }
@@ -489,7 +462,7 @@ static int umount_with_timeout(MountPoint *m) {
                 log_error_errno(r, "Unmounting '%s' timed out, issuing SIGKILL to PID " PID_FMT ".", m->path, pid);
                 (void) kill(pid, SIGKILL);
         } else if (r == -EPROTO)
-                log_error_errno(r, "Unmounting '%s' failed abnormally, child process " PID_FMT " aborted or exited non-zero.", m->path, pid);
+                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);
 
@@ -498,7 +471,7 @@ static int umount_with_timeout(MountPoint *m) {
 
 /* 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;
 
@@ -527,7 +500,7 @@ static int mount_points_list_umount(MountPoint **head, bool *changed) {
                          * 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) < 0) {
+                        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)) {
@@ -544,7 +517,7 @@ static int mount_points_list_umount(MountPoint **head, bool *changed) {
                         continue;
 
                 /* Trying to umount */
-                if (umount_with_timeout(m) < 0)
+                if (umount_with_timeout(m, umount_log_level) < 0)
                         n_failed++;
                 else
                         *changed = true;
@@ -574,7 +547,7 @@ 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;
@@ -604,7 +577,7 @@ static int loopback_points_list_detach(MountPoint **head, bool *changed) {
 
                         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++;
                 }
         }
@@ -612,7 +585,7 @@ 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;
@@ -637,7 +610,7 @@ static int dm_points_list_detach(MountPoint **head, bool *changed) {
                         *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++;
                 }
         }
@@ -645,26 +618,21 @@ 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;
 
@@ -676,7 +644,7 @@ int umount_all(bool *changed) {
         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);
@@ -685,28 +653,23 @@ 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);
 
@@ -714,19 +677,14 @@ int loopback_detach_all(bool *changed) {
 
         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);
 
@@ -734,12 +692,7 @@ int dm_detach_all(bool *changed) {
 
         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);
 }