]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/umount.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / core / umount.c
index 591dac71f0364b97aecf62d6437f829859f5fb59..cd195e12af6fd14d419837ea5e1a5f08d37230c5 100644 (file)
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
 #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;
@@ -76,7 +79,7 @@ static int mount_points_list_get(MountPoint **head) {
                 return -errno;
 
         for (i = 1;; i++) {
-                _cleanup_free_ char *path = NULL, *options = NULL;
+                _cleanup_free_ char *path = NULL, *options = NULL, *type = NULL;
                 char *p = NULL;
                 MountPoint *m;
                 int k;
@@ -90,12 +93,12 @@ static int mount_points_list_get(MountPoint **head) {
                            "%*s"        /* (6) mount flags */
                            "%*[^-]"     /* (7) optional fields */
                            "- "         /* (8) separator */
-                           "%*s "       /* (9) file system type */
+                           "%ms "       /* (9) file system type */
                            "%*s"        /* (10) mount source */
                            "%ms"        /* (11) mount options */
                            "%*[^\n]",   /* some rubbish at the end */
-                           &path, &options);
-                if (k != 2) {
+                           &path, &type, &options);
+                if (k != 3) {
                         if (k == EOF)
                                 break;
 
@@ -132,6 +135,8 @@ static int mount_points_list_get(MountPoint **head) {
                 m->path = p;
                 m->options = options;
                 options = NULL;
+                m->type = type;
+                type = NULL;
 
                 LIST_PREPEND(mount_point, *head, m);
         }
@@ -371,25 +376,38 @@ static int delete_dm(dev_t devnum) {
 
 static bool nonunmountable_path(const char *path) {
         return path_equal(path, "/")
-#ifndef HAVE_SPLIT_USR
+#if ! HAVE_SPLIT_USR
                 || path_equal(path, "/usr")
 #endif
                 || path_startswith(path, "/run/initramfs");
 }
 
+/* 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, bool log_error) {
-        MountPoint *m, *n;
+        MountPoint *m;
         int n_failed = 0;
 
         assert(head);
 
-        LIST_FOREACH_SAFE(mount_point, m, n, *head) {
+        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 (detect_container() <= 0)  {
+                   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
@@ -429,15 +447,17 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e
                 if (nonunmountable_path(m->path))
                         continue;
 
-                /* Trying to umount. We don't force here since we rely
-                 * on busy NFS and FUSE file systems to return EBUSY
-                 * until we closed everything on top of them. */
+                /* Trying to umount. Using MNT_FORCE causes some
+                 * filesystems (e.g. FUSE and NFS and other network
+                 * filesystems) to abort any pending requests and
+                 * return -EIO rather than blocking indefinitely.
+                 * If the filesysten is "busy", this may allow processes
+                 * to die, thus making the filesystem less busy so
+                 * the unmount might succeed (rather then return EBUSY).*/
                 log_info("Unmounting %s.", m->path);
-                if (umount2(m->path, 0) == 0) {
+                if (umount2(m->path, MNT_FORCE) == 0) {
                         if (changed)
                                 *changed = true;
-
-                        mount_point_free(head, m);
                 } else {
                         if (log_error)
                                 log_warning_errno(errno, "Could not unmount %s: %m", m->path);
@@ -509,22 +529,22 @@ static int loopback_points_list_detach(MountPoint **head, bool *changed) {
 
 static int dm_points_list_detach(MountPoint **head, bool *changed) {
         MountPoint *m, *n;
-        int n_failed = 0, k;
-        struct stat root_st;
+        int n_failed = 0, r;
+        dev_t rootdev;
 
         assert(head);
 
-        k = lstat("/", &root_st);
+        r = get_block_device("/", &rootdev);
+        if (r <= 0)
+                rootdev = 0;
 
         LIST_FOREACH_SAFE(mount_point, m, n, *head) {
-                int r;
 
-                if (k >= 0 &&
-                    major(root_st.st_dev) != 0 &&
-                    root_st.st_dev == m->devnum) {
-                        n_failed++;
-                        continue;
-                }
+                if (major(rootdev) != 0)
+                        if (rootdev == m->devnum) {
+                                n_failed ++;
+                                continue;
+                        }
 
                 log_info("Detaching DM %u:%u.", major(m->devnum), minor(m->devnum));
                 r = delete_dm(m->devnum);
@@ -542,9 +562,8 @@ static int dm_points_list_detach(MountPoint **head, bool *changed) {
         return n_failed;
 }
 
-int umount_all(bool *changed) {
+static int umount_all_once(bool *changed, bool log_error) {
         int r;
-        bool umount_changed;
         LIST_HEAD(MountPoint, mp_list_head);
 
         LIST_HEAD_INIT(mp_list_head);
@@ -552,21 +571,31 @@ int umount_all(bool *changed) {
         if (r < 0)
                 goto end;
 
+        r = mount_points_list_umount(&mp_list_head, changed, log_error);
+
+  end:
+        mount_points_list_free(&mp_list_head);
+
+        return r;
+}
+
+int umount_all(bool *changed) {
+        bool umount_changed;
+        int r;
+
         /* retry umount, until nothing can be umounted anymore */
         do {
                 umount_changed = false;
 
-                mount_points_list_umount(&mp_list_head, &umount_changed, false);
+                umount_all_once(&umount_changed, false);
                 if (umount_changed)
                         *changed = true;
-
         } while (umount_changed);
 
         /* umount one more time with logging enabled */
-        r = mount_points_list_umount(&mp_list_head, &umount_changed, true);
-
-  end:
-        mount_points_list_free(&mp_list_head);
+        r = umount_all_once(&umount_changed, true);
+        if (umount_changed)
+                *changed = true;
 
         return r;
 }