ryantimwilson / rpms / systemd

Forked from rpms/systemd a month ago
Clone
2aacef
From 7b6a09c47f1fee035c4b42840fabf65edce12aa8 Mon Sep 17 00:00:00 2001
2aacef
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
2aacef
Date: Mon, 7 Nov 2022 12:40:20 +0100
2aacef
Subject: [PATCH] pid1: skip cleanup if root is not tmpfs/ramfs
2aacef
2aacef
in_initrd() was really doing two things: checking if we're in the initrd, and
2aacef
also verifying that the initrd is set up correctly. But this second check is
2aacef
complicated, in particular it would return false for overlayfs, even with an
2aacef
upper tmpfs layer. It also doesn't support the use case of having an initial
2aacef
initrd with tmpfs, and then transitioning into an intermediate initrd that is
2aacef
e.g. a DDI, i.e. a filesystem possibly with verity arranged as a disk image.
2aacef
2aacef
We don't need to check if we're in initrd in every program. Instead, concerns
2aacef
are separated:
2aacef
- in_initrd() just does a simple check for /etc/initrd-release.
2aacef
- When doing cleanup, pid1 checks if it's on a tmpfs before starting to wipe
2aacef
  the old root. The only case where we want to remove the old root is when
2aacef
  we're on a plain tempory filesystem. With an overlay, we'd be creating
2aacef
  whiteout files, which is not very useful. (*)
2aacef
2aacef
This should resolve https://bugzilla.redhat.com/show_bug.cgi?id=2137631
2aacef
which is caused by systemd refusing to treat the system as an initrd because
2aacef
overlayfs is used.
2aacef
2aacef
(*) I think the idea of keeping the initrd fs around for shutdown is outdated.
2aacef
We should just have a completely separate exitrd that is unpacked when we want
2aacef
to shut down. This way, we don't waste memory at runtime, and we also don't
2aacef
transition to a potentially older version of systemd. But we don't have support
2aacef
for this yet.
2aacef
2aacef
This replaces 0fef5b0f0bd9ded1ae7bcb3e4e4b2893e36c51a6.
2aacef
2aacef
(cherry picked from commit a940f507fbe1c81d6787dc0b7ce232c39818eec9)
2aacef
2aacef
Related: #2138081
2aacef
---
2aacef
 src/basic/util.c         | 19 ++++++++-----------
2aacef
 src/shared/switch-root.c | 22 ++++++++++++----------
2aacef
 2 files changed, 20 insertions(+), 21 deletions(-)
2aacef
2aacef
diff --git a/src/basic/util.c b/src/basic/util.c
2aacef
index 981f917fab..e6aaa2dc9b 100644
2aacef
--- a/src/basic/util.c
2aacef
+++ b/src/basic/util.c
2aacef
@@ -56,14 +56,8 @@ bool in_initrd(void) {
2aacef
         if (saved_in_initrd >= 0)
2aacef
                 return saved_in_initrd;
2aacef
 
2aacef
-        /* We make two checks here:
2aacef
-         *
2aacef
-         * 1. the flag file /etc/initrd-release must exist
2aacef
-         * 2. the root file system must be a memory file system
2aacef
-         *
2aacef
-         * The second check is extra paranoia, since misdetecting an
2aacef
-         * initrd can have bad consequences due the initrd
2aacef
-         * emptying when transititioning to the main systemd.
2aacef
+        /* If /etc/initrd-release exists, we're in an initrd.
2aacef
+         * This can be overridden by setting SYSTEMD_IN_INITRD=0|1.
2aacef
          */
2aacef
 
2aacef
         r = getenv_bool_secure("SYSTEMD_IN_INITRD");
2aacef
@@ -72,9 +66,12 @@ bool in_initrd(void) {
2aacef
 
2aacef
         if (r >= 0)
2aacef
                 saved_in_initrd = r > 0;
2aacef
-        else
2aacef
-                saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 &&
2aacef
-                                  path_is_temporary_fs("/") > 0;
2aacef
+        else {
2aacef
+                r = access("/etc/initrd-release", F_OK);
2aacef
+                if (r < 0 && errno != ENOENT)
2aacef
+                        log_debug_errno(r, "Failed to check if /etc/initrd-release exists, assuming it does not: %m");
2aacef
+                saved_in_initrd = r >= 0;
2aacef
+        }
2aacef
 
2aacef
         return saved_in_initrd;
2aacef
 }
2aacef
diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c
2aacef
index 1a444841fa..4cad3551a6 100644
2aacef
--- a/src/shared/switch-root.c
2aacef
+++ b/src/shared/switch-root.c
2aacef
@@ -32,7 +32,6 @@ int switch_root(const char *new_root,
2aacef
 
2aacef
         _cleanup_free_ char *resolved_old_root_after = NULL;
2aacef
         _cleanup_close_ int old_root_fd = -1;
2aacef
-        bool old_root_remove;
2aacef
         int r;
2aacef
 
2aacef
         assert(new_root);
2aacef
@@ -42,12 +41,16 @@ int switch_root(const char *new_root,
2aacef
                 return 0;
2aacef
 
2aacef
         /* Check if we shall remove the contents of the old root */
2aacef
-        old_root_remove = in_initrd();
2aacef
-        if (old_root_remove) {
2aacef
-                old_root_fd = open("/", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
2aacef
-                if (old_root_fd < 0)
2aacef
-                        return log_error_errno(errno, "Failed to open root directory: %m");
2aacef
-        }
2aacef
+        old_root_fd = open("/", O_RDONLY | O_CLOEXEC | O_DIRECTORY);
2aacef
+        if (old_root_fd < 0)
2aacef
+                return log_error_errno(errno, "Failed to open root directory: %m");
2aacef
+        r = fd_is_temporary_fs(old_root_fd);
2aacef
+        if (r < 0)
2aacef
+                return log_error_errno(r, "Failed to stat root directory: %m");
2aacef
+        if (r > 0)
2aacef
+                log_debug("Root directory is on tmpfs, will do cleanup later.");
2aacef
+        else
2aacef
+                old_root_fd = safe_close(old_root_fd);
2aacef
 
2aacef
         /* Determine where we shall place the old root after the transition */
2aacef
         r = chase_symlinks(old_root_after, new_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved_old_root_after, NULL);
2aacef
@@ -117,9 +120,8 @@ int switch_root(const char *new_root,
2aacef
                 struct stat rb;
2aacef
 
2aacef
                 if (fstat(old_root_fd, &rb) < 0)
2aacef
-                        log_warning_errno(errno, "Failed to stat old root directory, leaving: %m");
2aacef
-                else
2aacef
-                        (void) rm_rf_children(TAKE_FD(old_root_fd), 0, &rb); /* takes possession of the dir fd, even on failure */
2aacef
+                        return log_error_errno(errno, "Failed to stat old root directory: %m");
2aacef
+                (void) rm_rf_children(TAKE_FD(old_root_fd), 0, &rb); /* takes possession of the dir fd, even on failure */
2aacef
         }
2aacef
 
2aacef
         return 0;