+/* 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;
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;
"%*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;
m->path = p;
m->options = options;
options = NULL;
+ m->type = type;
+ type = NULL;
LIST_PREPEND(mount_point, *head, m);
}
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
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);
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);
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);
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;
}