X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fcore%2Fmount.c;h=8e4a3769446a497ba9bfd5d72054e2bc6d6c09cc;hb=c43b2132f37264600cc26e07c8d85dfdd6c969f0;hp=61bc3e339220ebff36023d1147f77ab6f29a4b98;hpb=90598531b0bb79626ae5471f7c7c9e82954050c9;p=elogind.git diff --git a/src/core/mount.c b/src/core/mount.c index 61bc3e339..8e4a37694 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -21,7 +21,6 @@ #include #include -#include #include #include #include @@ -30,8 +29,6 @@ #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" @@ -41,9 +38,13 @@ #include "unit-name.h" #include "dbus-mount.h" #include "special.h" -#include "bus-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 const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = { [MOUNT_DEAD] = UNIT_INACTIVE, @@ -65,7 +66,7 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user 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)) @@ -74,37 +75,31 @@ static bool mount_needs_network(const char *options, const char *fstype) { return false; } -static bool mount_is_network(MountParameters *p) { +static bool mount_is_network(const MountParameters *p) { assert(p); return mount_needs_network(p->options, p->fstype); } -static bool mount_is_bind(MountParameters *p) { +static bool mount_is_bind(const MountParameters *p) { assert(p); - if (mount_test_option(p->options, "bind")) + if (fstab_test_option(p->options, "bind\0" "rbind\0")) return true; - if (p->fstype && streq(p->fstype, "bind")) - 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(MountParameters *p) { +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(MountParameters *p) { +static bool needs_quota(const MountParameters *p) { assert(p); if (mount_is_network(p)) @@ -113,11 +108,8 @@ static bool needs_quota(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) { @@ -303,7 +295,7 @@ static int mount_add_device_links(Mount *m) { assert(m); - p = get_mount_parameters_fragment(m); + p = get_mount_parameters(m); if (!p) return 0; @@ -364,7 +356,7 @@ static bool should_umount(Mount *m) { 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; @@ -381,13 +373,20 @@ static int mount_add_default_dependencies(Mount *m) { 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)) { @@ -864,20 +863,26 @@ static void mount_enter_unmounting(Mount *m) { 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); @@ -913,17 +918,25 @@ static void mount_enter_mounting(Mount *m) { 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) @@ -958,19 +971,19 @@ static void mount_enter_remounting(Mount *m) { 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; @@ -1019,7 +1032,7 @@ static int mount_start(Unit *u) { m->reload_result = MOUNT_SUCCESS; mount_enter_mounting(m); - return 0; + return 1; } static int mount_stop(Unit *u) { @@ -1043,7 +1056,7 @@ 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) { @@ -1236,9 +1249,31 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { 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); @@ -1351,7 +1386,7 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user return 0; } -static int mount_add_one( +static int mount_setup_unit( Manager *m, const char *what, const char *where, @@ -1394,7 +1429,7 @@ static int mount_add_one( u = unit_new(m, sizeof(Mount)); if (!u) - return -ENOMEM; + return log_oom(); r = unit_add_name(u, e); if (r < 0) @@ -1412,7 +1447,6 @@ static int mount_add_one( goto fail; } - if (m->running_as == SYSTEMD_SYSTEM) { const char* target; @@ -1508,48 +1542,45 @@ static int mount_add_one( return 0; fail: + log_warning_errno(r, "Failed to set up mount unit: %m"); + if (delete && u) unit_free(u); return r; } -static inline void mnt_free_table_p(struct libmnt_table **tb) { - mnt_free_table(*tb); -} - -static inline void mnt_free_iter_p(struct libmnt_iter **itr) { - mnt_free_iter(*itr); -} - static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { - _cleanup_(mnt_free_table_p) struct libmnt_table *tb = NULL; - _cleanup_(mnt_free_iter_p) 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(); + + i = mnt_new_iter(MNT_ITER_FORWARD); + if (!i) return log_oom(); - r = mnt_table_parse_mtab(tb, NULL); + 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); @@ -1557,11 +1588,16 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { 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; } @@ -1621,14 +1657,18 @@ static int mount_enumerate(Manager *m) { if (m->utab_inotify_fd < 0) { m->utab_inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); - if (m->utab_inotify_fd < 0) - goto fail_with_errno; + if (m->utab_inotify_fd < 0) { + r = -errno; + goto fail; + } (void) mkdir_p_label("/run/mount", 0755); r = inotify_add_watch(m->utab_inotify_fd, "/run/mount", IN_MOVED_TO); - if (r < 0) - goto fail_with_errno; + if (r < 0) { + r = -errno; + goto fail; + } r = sd_event_add_io(m->event, &m->mount_utab_event_source, m->utab_inotify_fd, EPOLLIN, mount_dispatch_io, m); if (r < 0) @@ -1645,8 +1685,6 @@ static int mount_enumerate(Manager *m) { return 0; -fail_with_errno: - r = -errno; fail: mount_shutdown(m); return r; @@ -1662,35 +1700,47 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, /* The manager calls this for every fd event happening on the * /proc/self/mountinfo file, which informs us about mounting - * table changes - * This may also be called for /run/mount events */ + * table changes, and for /run/mount events which we watch + * for mount options. */ if (fd == m->utab_inotify_fd) { - char inotify_buffer[sizeof(struct inotify_event) + NAME_MAX + 1]; - struct inotify_event *event; - char *p; - int rescan = 0; - - while ((r = read(fd, inotify_buffer, sizeof(inotify_buffer))) > 0) { - for (p = inotify_buffer; p < inotify_buffer + r; ) { - event = (struct inotify_event *) p; - /* only care about changes to utab, but we have - * to monitor the directory to reliably get - * notifications about when utab is replaced - * using rename(2) */ - if (strcmp(event->name, "utab") == 0) - rescan = 1; - p += sizeof(struct inotify_event) + event->len; + bool rescan = false; + + /* FIXME: We *really* need to replace this with + * libmount's own API for this, we should not hardcode + * internal behaviour of libmount here. */ + + for (;;) { + union inotify_event_buffer buffer; + struct inotify_event *e; + ssize_t l; + + l = read(fd, &buffer, sizeof(buffer)); + if (l < 0) { + if (errno == EAGAIN || errno == EINTR) + break; + + log_error_errno(errno, "Failed to read utab inotify: %m"); + break; + } + + FOREACH_INOTIFY_EVENT(e, buffer, l) { + /* Only care about changes to utab, + * but we have to monitor the + * directory to reliably get + * notifications about when utab is + * replaced using rename(2) */ + if ((e->mask & IN_Q_OVERFLOW) || streq(e->name, "utab")) + rescan = true; } } + if (!rescan) return 0; } 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); @@ -1723,6 +1773,10 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, 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 */