#include <errno.h>
#include <stdio.h>
-#include <mntent.h>
#include <sys/epoll.h>
#include <signal.h>
#include <libmount.h>
#include "manager.h"
#include "unit.h"
#include "mount.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
#include "log.h"
#include "sd-messages.h"
#include "strv.h"
#include "unit-name.h"
#include "dbus-mount.h"
#include "special.h"
-#include "bus-common-errors.h"
#include "exit-status.h"
-#include "def.h"
+#include "fstab-util.h"
+
+#define RETRY_UMOUNT_MAX 32
DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_table*, mnt_free_table);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_iter*, mnt_free_iter);
static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
static bool mount_needs_network(const char *options, const char *fstype) {
- if (mount_test_option(options, "_netdev"))
+ if (fstab_test_option(options, "_netdev\0"))
return true;
if (fstype && fstype_is_network(fstype))
static bool mount_is_bind(const MountParameters *p) {
assert(p);
- if (mount_test_option(p->options, "bind"))
- return true;
-
- if (p->fstype && streq(p->fstype, "bind"))
+ if (fstab_test_option(p->options, "bind\0" "rbind\0"))
return true;
- if (mount_test_option(p->options, "rbind"))
- return true;
-
- if (p->fstype && streq(p->fstype, "rbind"))
+ if (p->fstype && STR_IN_SET(p->fstype, "bind", "rbind"))
return true;
return false;
static bool mount_is_auto(const MountParameters *p) {
assert(p);
- return !mount_test_option(p->options, "noauto");
+ return !fstab_test_option(p->options, "noauto\0");
}
static bool needs_quota(const MountParameters *p) {
if (mount_is_bind(p))
return false;
- return mount_test_option(p->options, "usrquota") ||
- mount_test_option(p->options, "grpquota") ||
- mount_test_option(p->options, "quota") ||
- mount_test_option(p->options, "usrjquota") ||
- mount_test_option(p->options, "grpjquota");
+ return fstab_test_option(p->options,
+ "usrquota\0" "grpquota\0" "quota\0" "usrjquota\0" "grpjquota\0");
}
static void mount_init(Unit *u) {
assert(m);
- p = get_mount_parameters_fragment(m);
+ p = get_mount_parameters(m);
if (!p)
return 0;
return false;
p = get_mount_parameters(m);
- if (p && mount_test_option(p->options, "x-initrd.mount") &&
+ if (p && fstab_test_option(p->options, "x-initrd.mount\0") &&
!in_initrd())
return false;
if (UNIT(m)->manager->running_as != SYSTEMD_SYSTEM)
return 0;
- p = get_mount_parameters(m);
-
- if (!p)
+ /* We do not add any default dependencies to / and /usr, since
+ * they are guaranteed to stay mounted the whole time, since
+ * our system is on it. Also, don't bother with anything
+ * mounted below virtual file systems, it's also going to be
+ * virtual, and hence not worth the effort. */
+ if (path_equal(m->where, "/") ||
+ path_equal(m->where, "/usr") ||
+ path_startswith(m->where, "/proc") ||
+ path_startswith(m->where, "/sys") ||
+ path_startswith(m->where, "/dev"))
return 0;
- if (path_equal(m->where, "/") ||
- path_equal(m->where, "/usr"))
+ p = get_mount_parameters(m);
+ if (!p)
return 0;
if (mount_is_network(p)) {
m->reload_result = MOUNT_SUCCESS;
}
-static int mount_coldplug(Unit *u) {
+static int mount_coldplug(Unit *u, Hashmap *deferred_work) {
Mount *m = MOUNT(u);
MountState new_state = MOUNT_DEAD;
int r;
assert(c);
assert(_pid);
- unit_realize_cgroup(UNIT(m));
+ (void) unit_realize_cgroup(UNIT(m));
+ if (m->reset_cpu_usage) {
+ (void) unit_reset_cpu_usage(UNIT(m));
+ m->reset_cpu_usage = false;
+ }
r = unit_setup_exec_runtime(UNIT(m));
if (r < 0)
assert(m);
+ /* Start counting our attempts */
+ if (!IN_SET(m->state,
+ MOUNT_UNMOUNTING,
+ MOUNT_UNMOUNTING_SIGTERM,
+ MOUNT_UNMOUNTING_SIGKILL))
+ m->n_retry_umount = 0;
+
m->control_command_id = MOUNT_EXEC_UNMOUNT;
m->control_command = m->exec_command + MOUNT_EXEC_UNMOUNT;
- if ((r = exec_command_set(
- m->control_command,
- "/bin/umount",
- "-n",
- m->where,
- NULL)) < 0)
+ r = exec_command_set(m->control_command, "/bin/umount", m->where, NULL);
+ if (r >= 0 && UNIT(m)->manager->running_as == SYSTEMD_SYSTEM)
+ r = exec_command_append(m->control_command, "-n", NULL);
+ if (r < 0)
goto fail;
mount_unwatch_control_pid(m);
- if ((r = mount_spawn(m, m->control_command, &m->control_pid)) < 0)
+ r = mount_spawn(m, m->control_command, &m->control_pid);
+ if (r < 0)
goto fail;
mount_set_state(m, MOUNT_UNMOUNTING);
if (r < 0)
goto fail;
- if (m->from_fragment)
- r = exec_command_set(
- m->control_command,
- "/bin/mount",
- m->sloppy_options ? "-ns" : "-n",
- m->parameters_fragment.what,
- m->where,
- "-t", m->parameters_fragment.fstype ? m->parameters_fragment.fstype : "auto",
- m->parameters_fragment.options ? "-o" : NULL, m->parameters_fragment.options,
- NULL);
- else
+ if (m->from_fragment) {
+ _cleanup_free_ char *opts = NULL;
+
+ r = fstab_filter_options(m->parameters_fragment.options,
+ "nofail\0" "noauto\0" "auto\0", NULL, NULL, &opts);
+ if (r < 0)
+ goto fail;
+
+ r = exec_command_set(m->control_command, "/bin/mount",
+ m->parameters_fragment.what, m->where, NULL);
+ if (r >= 0 && UNIT(m)->manager->running_as == SYSTEMD_SYSTEM)
+ r = exec_command_append(m->control_command, "-n", NULL);
+ if (r >= 0 && m->sloppy_options)
+ r = exec_command_append(m->control_command, "-s", NULL);
+ if (r >= 0 && m->parameters_fragment.fstype)
+ r = exec_command_append(m->control_command, "-t", m->parameters_fragment.fstype, NULL);
+ if (r >= 0 && !isempty(opts))
+ r = exec_command_append(m->control_command, "-o", opts, NULL);
+ } else
r = -ENOENT;
if (r < 0)
const char *o;
if (m->parameters_fragment.options)
- o = strappenda("remount,", m->parameters_fragment.options);
+ o = strjoina("remount,", m->parameters_fragment.options);
else
o = "remount";
- r = exec_command_set(
- m->control_command,
- "/bin/mount",
- m->sloppy_options ? "-ns" : "-n",
- m->parameters_fragment.what,
- m->where,
- "-t", m->parameters_fragment.fstype ? m->parameters_fragment.fstype : "auto",
- "-o", o,
- NULL);
+ r = exec_command_set(m->control_command, "/bin/mount",
+ m->parameters_fragment.what, m->where,
+ "-o", o, NULL);
+ if (r >= 0 && UNIT(m)->manager->running_as == SYSTEMD_SYSTEM)
+ r = exec_command_append(m->control_command, "-n", NULL);
+ if (r >= 0 && m->sloppy_options)
+ r = exec_command_append(m->control_command, "-s", NULL);
+ if (r >= 0 && m->parameters_fragment.fstype)
+ r = exec_command_append(m->control_command, "-t", m->parameters_fragment.fstype, NULL);
} else
r = -ENOENT;
m->result = MOUNT_SUCCESS;
m->reload_result = MOUNT_SUCCESS;
+ m->reset_cpu_usage = true;
mount_enter_mounting(m);
- return 0;
+ return 1;
}
static int mount_stop(Unit *u) {
m->state == MOUNT_REMOUNTING_SIGKILL);
mount_enter_unmounting(m);
- return 0;
+ return 1;
}
static int mount_reload(Unit *u) {
case MOUNT_UNMOUNTING_SIGKILL:
case MOUNT_UNMOUNTING_SIGTERM:
- if (f == MOUNT_SUCCESS)
- mount_enter_dead(m, f);
- else if (m->from_proc_self_mountinfo)
+ if (f == MOUNT_SUCCESS) {
+
+ if (m->from_proc_self_mountinfo) {
+
+ /* Still a mount point? If so, let's
+ * try again. Most likely there were
+ * multiple mount points stacked on
+ * top of each other. Note that due to
+ * the io event priority logic we can
+ * be sure the new mountinfo is loaded
+ * before we process the SIGCHLD for
+ * the mount command. */
+
+ if (m->n_retry_umount < RETRY_UMOUNT_MAX) {
+ log_unit_debug(u->id, "%s: mount still present, trying again.", u->id);
+ m->n_retry_umount++;
+ mount_enter_unmounting(m);
+ } else {
+ log_unit_debug(u->id, "%s: mount still present after %u attempts to unmount, giving up.", u->id, m->n_retry_umount);
+ mount_enter_mounted(m, f);
+ }
+ } else
+ mount_enter_dead(m, f);
+
+ } else if (m->from_proc_self_mountinfo)
mount_enter_mounted(m, f);
else
mount_enter_dead(m, f);
return 0;
}
-static int mount_add_one(
+static int mount_setup_unit(
Manager *m,
const char *what,
const char *where,
u = unit_new(m, sizeof(Mount));
if (!u)
- return -ENOMEM;
+ return log_oom();
r = unit_add_name(u, e);
if (r < 0)
goto fail;
}
-
if (m->running_as == SYSTEMD_SYSTEM) {
const char* target;
return 0;
fail:
+ log_warning_errno(r, "Failed to set up mount unit: %m");
+
if (delete && u)
unit_free(u);
}
static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) {
- _cleanup_(mnt_free_tablep) struct libmnt_table *tb = NULL;
- _cleanup_(mnt_free_iterp) struct libmnt_iter *itr = NULL;
- struct libmnt_fs *fs;
+ _cleanup_(mnt_free_tablep) struct libmnt_table *t = NULL;
+ _cleanup_(mnt_free_iterp) struct libmnt_iter *i = NULL;
int r = 0;
assert(m);
- tb = mnt_new_table();
- itr = mnt_new_iter(MNT_ITER_FORWARD);
- if (!tb || !itr)
+ t = mnt_new_table();
+ if (!t)
return log_oom();
- r = mnt_table_parse_mtab(tb, NULL);
+ i = mnt_new_iter(MNT_ITER_FORWARD);
+ if (!i)
+ return log_oom();
+
+ r = mnt_table_parse_mtab(t, NULL);
if (r < 0)
- return r;
+ return log_error_errno(r, "Failed to parse /proc/self/mountinfo: %m");
r = 0;
for (;;) {
const char *device, *path, *options, *fstype;
_cleanup_free_ const char *d = NULL, *p = NULL;
+ struct libmnt_fs *fs;
int k;
- k = mnt_table_next_fs(tb, itr, &fs);
+ k = mnt_table_next_fs(t, i, &fs);
if (k == 1)
break;
- else if (k < 0)
- return log_error_errno(k, "Failed to get next entry from /etc/fstab: %m");
+ if (k < 0)
+ return log_error_errno(k, "Failed to get next entry from /proc/self/mountinfo: %m");
device = mnt_fs_get_source(fs);
path = mnt_fs_get_target(fs);
fstype = mnt_fs_get_fstype(fs);
d = cunescape(device);
+ if (!d)
+ return log_oom();
+
p = cunescape(path);
- if (!d || !p)
+ if (!p)
return log_oom();
- k = mount_add_one(m, d, p, options, fstype, set_flags);
+ (void) device_found_node(m, d, true, DEVICE_FOUND_MOUNT, set_flags);
+
+ k = mount_setup_unit(m, d, p, options, fstype, set_flags);
if (r == 0 && k < 0)
r = k;
}
* internal behaviour of libmount here. */
for (;;) {
- uint8_t buffer[INOTIFY_EVENT_MAX] _alignas_(struct inotify_event);
+ union inotify_event_buffer buffer;
struct inotify_event *e;
ssize_t l;
- l = read(fd, buffer, sizeof(buffer));
+ l = read(fd, &buffer, sizeof(buffer));
if (l < 0) {
if (errno == EAGAIN || errno == EINTR)
break;
r = mount_load_proc_self_mountinfo(m, true);
if (r < 0) {
- log_error_errno(r, "Failed to reread /proc/self/mountinfo: %m");
-
/* Reset flags, just in case, for later calls */
LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) {
Mount *mount = MOUNT(u);
break;
}
+ if (mount->parameters_proc_self_mountinfo.what)
+ (void) device_found_node(m, mount->parameters_proc_self_mountinfo.what, false, DEVICE_FOUND_MOUNT, true);
+
+
} else if (mount->just_mounted || mount->just_changed) {
/* New or changed mount entry */