From 13b5225d6278af15e84ebd1889f04cfe81b47787 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Mon, 9 Sep 2024 17:49:33 +0200
Subject: [PATCH] shutdown: teach sync_with_progress() to optionally sync a
 specific fd only

This is preparation for reusing the logic for syncing DM and other
devices with a timeout applied.

Conflict:context adaption.
Reference:https://github.com/systemd/systemd/pull/34330/commits/13b5225d6278af15e84ebd1889f04cfe81b47787

---
 src/shared/async.c      | 22 ++++++++++++++++++++++
 src/shared/async.h      |  1 +
 src/shutdown/shutdown.c | 31 ++++++++++++++++++++++---------
 src/shutdown/shutdown.h |  4 ++++
 4 files changed, 49 insertions(+), 9 deletions(-)
 create mode 100644 src/shutdown/shutdown.h

diff --git a/src/shared/async.c b/src/shared/async.c
index bbb8b810..bd043c84 100644
--- a/src/shared/async.c
+++ b/src/shared/async.c
@@ -34,6 +34,28 @@ int asynchronous_sync(pid_t *ret_pid) {
         return 0;
 }
 
+int asynchronous_fsync(int fd, pid_t *ret_pid) {
+        int r;
+
+        assert(fd >= 0);
+        /* Same as asynchronous_sync() above, but calls fsync() on a specific fd */
+
+        r = safe_fork_full("(sd-fsync)",
+                           /* stdio_fds= */ NULL,
+                           /* except_fds= */ &fd,
+                           /* n_except_fds= */ 1,
+                           FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|(ret_pid ? 0 : FORK_DETACH), ret_pid);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                /* Child process */
+                fsync(fd);
+                _exit(EXIT_SUCCESS);
+        }
+
+        return 0;
+}
+
 /* We encode the fd to close in the userdata pointer as an unsigned value. The highest bit indicates whether
  * we need to fork again */
 #define NEED_DOUBLE_FORK (1U << (sizeof(unsigned) * 8 - 1))
diff --git a/src/shared/async.h b/src/shared/async.h
index 96148f90..2f5bbd51 100644
--- a/src/shared/async.h
+++ b/src/shared/async.h
@@ -20,6 +20,7 @@
  * for avoiding threads. */
 
 int asynchronous_sync(pid_t *ret_pid);
+int asynchronous_fsync(int fd, pid_t *ret_pid);
 int asynchronous_close(int fd);
 int asynchronous_rm_rf(const char *p, RemoveFlags flags);
 
diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c
index 03e6e70f..e6c9e0f8 100644
--- a/src/shutdown/shutdown.c
+++ b/src/shutdown/shutdown.c
@@ -40,6 +40,7 @@
 #include "process-util.h"
 #include "reboot-util.h"
 #include "rlimit-util.h"
+#include "shutdown.h"
 #include "signal-util.h"
 #include "string-util.h"
 #include "switch-root.h"
@@ -223,8 +224,10 @@ static int sync_making_progress(unsigned long long *prev_dirty) {
         return r;
 }
 
-static int sync_with_progress(void) {
+int sync_with_progress(int fd) {
         unsigned long long dirty = ULLONG_MAX;
+        _cleanup_free_ char *path = NULL;
+        const char *what;
         pid_t pid;
         int r;
 
@@ -233,11 +236,20 @@ static int sync_with_progress(void) {
         /* Due to the possibility of the sync operation hanging, we fork a child process and monitor
          * the progress. If the timeout lapses, the assumption is that the particular sync stalled. */
 
-        r = asynchronous_sync(&pid);
-        if (r < 0)
-                return log_error_errno(r, "Failed to fork sync(): %m");
+        if (fd >= 0) {
+                r = asynchronous_fsync(fd, &pid);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to fork fsync(): %m");
+
+                (void) fd_get_path(fd, &path);
+        } else {
+                r = asynchronous_sync(&pid);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to fork sync(): %m");
+        }
 
-        log_info("Syncing filesystems and block devices.");
+        what = path ?: "filesystems and block devices";
+        log_info("Syncing %s.", what);
 
         /* Start monitoring the sync operation. If more than
          * SYNC_PROGRESS_ATTEMPTS lapse without progress being made,
@@ -248,7 +260,7 @@ static int sync_with_progress(void) {
                         /* Sync finished without error (sync() call itself does not return an error code) */
                         return 0;
                 if (r != -ETIMEDOUT)
-                        return log_error_errno(r, "Failed to sync filesystems and block devices: %m");
+                        return log_error_errno(r, "Failed to sync %s: %m", what);
 
                 /* Reset the check counter if we made some progress */
                 if (sync_making_progress(&dirty) > 0)
@@ -258,7 +270,8 @@ static int sync_with_progress(void) {
         /* Only reached in the event of a timeout. We should issue a kill to the stray process. */
         (void) kill(pid, SIGKILL);
         return log_error_errno(SYNTHETIC_ERRNO(ETIMEDOUT),
-                               "Syncing filesystems and block devices - timed out, issuing SIGKILL to PID "PID_FMT".",
+                               "Syncing %s - timed out, issuing SIGKILL to PID "PID_FMT".",
+                               what,
                                pid);
 }
 
@@ -432,7 +445,7 @@ int main(int argc, char *argv[]) {
          * desperately trying to sync IO to disk within their timeout. Do not remove this sync, data corruption will
          * result. */
         if (!in_container)
-                (void) sync_with_progress();
+                (void) sync_with_progress(-EBADF);
 
         disable_coredumps();
         disable_binfmt();
@@ -600,7 +613,7 @@ int main(int argc, char *argv[]) {
          * which might have caused IO, hence let's do it once more. Do not remove this sync, data corruption
          * will result. */
         if (!in_container)
-                (void) sync_with_progress();
+                (void) sync_with_progress(-EBADF);
 
         /* This is primarily useful when running systemd in a VM, as it provides the user running the VM with
          * a mechanism to pick up systemd's exit status in the VM. */
diff --git a/src/shutdown/shutdown.h b/src/shutdown/shutdown.h
new file mode 100644
index 00000000..99aaec69
--- /dev/null
+++ b/src/shutdown/shutdown.h
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+int sync_with_progress(int fd);
-- 
2.43.0