From 1e1225614ca1106116dcad9fb37aaeb6106408ab Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 24 Aug 2023 23:41:05 +0900 Subject: [PATCH] core/credential,mount: re-read /proc/self/mountinfo before invoking umount command When a unit has credentials, stopping the service unmounts the credentials directory. On shutdown, stopping the service and the corresponding mount unit may be done mostly simultaneously, and if we invoke umount command soon after umount() being called on stopping the service, the mount unit will fail. This makes Mount.invalidated_state flag set when umount() is called for a path, and re-read /proc/self/mouninfo before invoking umount command if the flag is set. Fixes #25527. Replaces #26959. --- src/core/exec-credential.c | 11 +++++++---- src/core/exec-credential.h | 2 +- src/core/mount.c | 32 ++++++++++++++++++++++++++++++++ src/core/mount.h | 4 ++++ src/core/unit.c | 2 +- 5 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/core/exec-credential.c b/src/core/exec-credential.c index e69c4a9fa6f..2a72f1c3961 100644 --- a/src/core/exec-credential.c +++ b/src/core/exec-credential.c @@ -12,6 +12,7 @@ #include "label-util.h" #include "mkdir-label.h" #include "mount-util.h" +#include "mount.h" #include "mountpoint-util.h" #include "process-util.h" #include "random-util.h" @@ -138,19 +139,21 @@ int unit_add_default_credential_dependencies(Unit *u, const ExecContext *c) { return unit_add_dependency_by_name(u, UNIT_AFTER, m, /* add_reference= */ true, UNIT_DEPENDENCY_FILE); } -int exec_context_destroy_credentials(const ExecContext *c, const char *runtime_prefix, const char *unit) { +int exec_context_destroy_credentials(Unit *u) { _cleanup_free_ char *p = NULL; int r; - assert(c); + assert(u); - r = get_credential_directory(runtime_prefix, unit, &p); + r = get_credential_directory(u->manager->prefix[EXEC_DIRECTORY_RUNTIME], u->id, &p); if (r <= 0) return r; /* This is either a tmpfs/ramfs of its own, or a plain directory. Either way, let's first try to * unmount it, and afterwards remove the mount point */ - (void) umount2(p, MNT_DETACH|UMOUNT_NOFOLLOW); + if (umount2(p, MNT_DETACH|UMOUNT_NOFOLLOW) >= 0) + (void) mount_invalidate_state_by_path(u->manager, p); + (void) rm_rf(p, REMOVE_ROOT|REMOVE_CHMOD); return 0; diff --git a/src/core/exec-credential.h b/src/core/exec-credential.h index 9e6f6656217..6f836fbd0b8 100644 --- a/src/core/exec-credential.h +++ b/src/core/exec-credential.h @@ -45,7 +45,7 @@ int exec_context_get_credential_directory( int unit_add_default_credential_dependencies(Unit *u, const ExecContext *c); -int exec_context_destroy_credentials(const ExecContext *c, const char *runtime_root, const char *unit); +int exec_context_destroy_credentials(Unit *u); int exec_setup_credentials( const ExecContext *context, const ExecParameters *params, diff --git a/src/core/mount.c b/src/core/mount.c index f93bad7074b..931075a1ee7 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -1284,6 +1284,11 @@ static int mount_stop(Unit *u) { assert(m); + /* When we directly call umount() for a path, then the state of the corresponding mount unit may be + * outdated. Let's re-read mountinfo now and update the state. */ + if (m->invalidated_state) + (void) mount_process_proc_self_mountinfo(u->manager); + switch (m->state) { case MOUNT_UNMOUNTING: @@ -1318,6 +1323,11 @@ static int mount_stop(Unit *u) { mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, MOUNT_SUCCESS); return 0; + case MOUNT_DEAD: + case MOUNT_FAILED: + /* The mount has just been unmounted by somebody else. */ + return 0; + default: assert_not_reached(); } @@ -2067,6 +2077,8 @@ static int mount_process_proc_self_mountinfo(Manager *m) { LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) { Mount *mount = MOUNT(u); + mount->invalidated_state = false; + if (!mount_is_mounted(mount)) { /* A mount point is not around right now. It might be gone, or might never have @@ -2160,6 +2172,26 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, return mount_process_proc_self_mountinfo(m); } +int mount_invalidate_state_by_path(Manager *manager, const char *path) { + _cleanup_free_ char *name = NULL; + Unit *u; + int r; + + assert(manager); + assert(path); + + r = unit_name_from_path(path, ".mount", &name); + if (r < 0) + return log_debug_errno(r, "Failed to generate unit name from path \"%s\", ignoring: %m", path); + + u = manager_get_unit(manager, name); + if (!u) + return -ENOENT; + + MOUNT(u)->invalidated_state = true; + return 0; +} + static void mount_reset_failed(Unit *u) { Mount *m = MOUNT(u); diff --git a/src/core/mount.h b/src/core/mount.h index d6d6d335a45..25606747196 100644 --- a/src/core/mount.h +++ b/src/core/mount.h @@ -49,6 +49,8 @@ struct Mount { MountParameters parameters_proc_self_mountinfo; MountParameters parameters_fragment; + bool invalidated_state:1; /* Set when the 'state' of the mount unit may be outdated, and we need to + * re-read /proc/self/mountinfo. */ bool from_proc_self_mountinfo:1; bool from_fragment:1; @@ -92,6 +94,8 @@ extern const UnitVTable mount_vtable; void mount_fd_event(Manager *m, int events); +int mount_invalidate_state_by_path(Manager *manager, const char *path); + const char* mount_exec_command_to_string(MountExecCommand i) _const_; MountExecCommand mount_exec_command_from_string(const char *s) _pure_; diff --git a/src/core/unit.c b/src/core/unit.c index 56af1253adb..34cbde690b8 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -5994,7 +5994,7 @@ void unit_destroy_runtime_data(Unit *u, const ExecContext *context) { if (context->runtime_directory_preserve_mode == EXEC_PRESERVE_NO) exec_context_destroy_runtime_directory(context, u->manager->prefix[EXEC_DIRECTORY_RUNTIME]); - exec_context_destroy_credentials(context, u->manager->prefix[EXEC_DIRECTORY_RUNTIME], u->id); + exec_context_destroy_credentials(u); exec_context_destroy_mount_ns_dir(u); }