Replace use of overlayfs' override_creds with overlay_remounter

This new solution does not require the use of non-upstreamed
override_creds patches.

Test: system/core/fs_mgr/tests/adb-remount-test.sh
Bug: 388912628
Change-Id: I2bc86a7171c014f88786c6ab97c8788ad024b264
diff --git a/init/Android.bp b/init/Android.bp
index ed19b4b..dbdf80b 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -136,6 +136,8 @@
                 "-DWORLD_WRITABLE_KMSG=1",
                 "-UDUMP_ON_UMOUNT_FAILURE",
                 "-DDUMP_ON_UMOUNT_FAILURE=1",
+                "-UALLOW_REMOUNT_OVERLAYS",
+                "-DALLOW_REMOUNT_OVERLAYS=1",
             ],
         },
         eng: {
@@ -263,7 +265,10 @@
     name: "init",
     required: [
         "init_second_stage",
-    ],
+    ] + select(product_variable("debuggable"), {
+        true: ["overlay_remounter"],
+        false: [],
+    }),
 }
 
 cc_defaults {
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index e06a645..6bb0ad7 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -402,7 +402,7 @@
 
     // /second_stage_resources is used to preserve files from first to second
     // stage init
-    CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+    CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOSUID | MS_NODEV,
                     "mode=0755,uid=0,gid=0"));
 
     if (IsMicrodroid() && android::virtualization::IsOpenDiceChangesFlagEnabled()) {
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 6316b4d..8bdf5b6 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -69,6 +69,7 @@
 #include <android/avf_cc_flags.h>
 #include <fs_avb/fs_avb.h>
 #include <fs_mgr.h>
+#include <fs_mgr_overlayfs.h>
 #include <genfslabelsversion.h>
 #include <libgsi/libgsi.h>
 #include <libsnapshot/snapshot.h>
@@ -77,6 +78,7 @@
 #include "block_dev_initializer.h"
 #include "debug_ramdisk.h"
 #include "reboot_utils.h"
+#include "second_stage_resources.h"
 #include "snapuserd_transition.h"
 #include "util.h"
 
@@ -698,6 +700,65 @@
     }
 }
 
+#ifdef ALLOW_REMOUNT_OVERLAYS
+void SetupOverlays() {
+    if (android::fs_mgr::use_override_creds) return;
+
+    bool has_overlays = false;
+    std::string contents;
+    auto result = android::base::ReadFileToString("/proc/mounts", &contents, true);
+
+    auto lines = android::base::Split(contents, "\n");
+    for (auto const& line : lines)
+        if (android::base::StartsWith(line, "overlay")) {
+            has_overlays = true;
+            break;
+        }
+
+    if (!has_overlays) return;
+
+    // After adb remount, we mount all r/o volumes with overlayfs to allow writing.
+    // However, since overlayfs performs its file operations in the context of the
+    // mounting process, this will not work as is - init is in the kernel domain in
+    // first stage, which has very limited permissions.
+
+    // In order to fix this, we need to unmount remount all these volumes from a process
+    // with sufficient privileges to be able to perform these operations. The
+    // overlay_remounter domain has those privileges on debuggable devices.
+    // We will call overlay_remounter which will do the unmounts/mounts.
+    // But for that to work, the volumes must not be busy, so we need to copy
+    // overlay_remounter from system to a ramdisk and run it from there.
+
+    const char* kOverlayRemounter = "overlay_remounter";
+    auto or_src = std::filesystem::path("/system/xbin/") / kOverlayRemounter;
+    auto or_dest = std::filesystem::path(kSecondStageRes) / kOverlayRemounter;
+    std::error_code ec;
+    std::filesystem::copy(or_src, or_dest, ec);
+    if (ec) {
+        LOG(FATAL) << "Failed to copy " << or_src << " to " << or_dest << " " << ec.message();
+    }
+
+    if (selinux_android_restorecon(or_dest.c_str(), 0) == -1) {
+        PLOG(FATAL) << "restorecon of " << or_dest << " failed";
+    }
+    auto dest = unique_fd(open(or_dest.c_str(), O_RDONLY | O_CLOEXEC));
+    if (dest.get() == -1) {
+        PLOG(FATAL) << "Failed to reopen " << or_dest;
+    }
+    if (unlink(or_dest.c_str()) == -1) {
+        PLOG(FATAL) << "Failed to unlink " << or_dest;
+    }
+    const char* args[] = {or_dest.c_str(), nullptr};
+    fexecve(dest.get(), const_cast<char**>(args), nullptr);
+
+    // execv() only returns if an error happened, in which case we
+    // panic and never return from this function.
+    PLOG(FATAL) << "execv(\"" << or_dest << "\") failed";
+}
+#else
+void SetupOverlays() {}
+#endif
+
 int SetupSelinux(char** argv) {
     SetStdioToDevNull(argv);
     InitKernelLogging(argv);
@@ -738,6 +799,10 @@
 
     setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);
 
+    // SetupOverlays does not return if overlays exist, instead it execs overlay_remounter
+    // which then execs second stage init
+    SetupOverlays();
+
     const char* path = "/system/bin/init";
     const char* args[] = {path, "second_stage", nullptr};
     execv(path, const_cast<char**>(args));