]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core/credential,mount: re-read /proc/self/mountinfo before invoking umount command 28957/head
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 24 Aug 2023 14:41:05 +0000 (23:41 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 6 Sep 2023 16:05:13 +0000 (01:05 +0900)
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
src/core/exec-credential.h
src/core/mount.c
src/core/mount.h
src/core/unit.c

index e69c4a9fa6fe60e1945f768151fa8f6d99b24449..2a72f1c39615198146afdd97ea3647a458883ea0 100644 (file)
@@ -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;
index 9e6f6656217210156b8f6a8357dcb7ec049960c3..6f836fbd0b835cccbdaf38f63ac6bf35d17c3553 100644 (file)
@@ -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,
index f93bad7074b590369dcc64f1764ba96a43e11e6d..931075a1ee797d69f449a95421fe363d2079ca17 100644 (file)
@@ -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);
 
index d6d6d335a45d4879b898fb02428d0b801ebce631..25606747196056a377a09de567e50655e4ab12ae 100644 (file)
@@ -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_;
 
index 56af1253adb194e665056e4a1584683aa1f35b30..34cbde690b810258bd63b262785e6937b7ef6f3b 100644 (file)
@@ -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);
 }