Copy snapuserd to first_stage_ramdisk

Certain binaries, such as snapuserd, are only available under
/system/bin. To make them accessible by first stage init, we copy
/system/bin/snapuserd to /first_stage_ramdisk/system/bin/snapuserd .

Test: th
Bug: 219841787
Change-Id: I913425a82905c745a05ac32d488f08506dc264ff
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 3cd0252..52e3995 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -27,6 +27,7 @@
 #include <sys/utsname.h>
 #include <unistd.h>
 
+#include <chrono>
 #include <filesystem>
 #include <string>
 #include <vector>
@@ -107,6 +108,39 @@
            cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
 }
 
+static void Copy(const char* src, const char* dst) {
+    if (link(src, dst) == 0) {
+        LOG(INFO) << "hard linking " << src << " to " << dst << " succeeded";
+        return;
+    }
+    PLOG(FATAL) << "hard linking " << src << " to " << dst << " failed, falling back to copy.";
+}
+
+// Move e2fsck before switching root, so that it is available at the same path
+// after switching root.
+void PrepareSwitchRoot() {
+    constexpr const char* src = "/system/bin/snapuserd";
+    constexpr const char* dst = "/first_stage_ramdisk/system/bin/snapuserd";
+
+    if (access(dst, X_OK) == 0) {
+        LOG(INFO) << dst << " already exists and it can be executed";
+        return;
+    }
+
+    if (access(src, F_OK) != 0) {
+        PLOG(INFO) << "Not moving " << src << " because it cannot be accessed";
+        return;
+    }
+
+    auto dst_dir = android::base::Dirname(dst);
+    std::error_code ec;
+    if (access(dst_dir.c_str(), F_OK) != 0) {
+        if (!fs::create_directories(dst_dir, ec)) {
+            LOG(FATAL) << "Cannot create " << dst_dir << ": " << ec.message();
+        }
+    }
+    Copy(src, dst);
+}
 }  // namespace
 
 std::string GetModuleLoadList(bool recovery, const std::string& dir_path) {
@@ -304,12 +338,11 @@
                   << module_elapse_time.count() << " ms";
     }
 
-
     bool created_devices = false;
     if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {
         if (!IsRecoveryMode()) {
             created_devices = DoCreateDevices();
-            if (!created_devices){
+            if (!created_devices) {
                 LOG(ERROR) << "Failed to create device nodes early";
             }
         }
@@ -352,10 +385,11 @@
 
     if (ForceNormalBoot(cmdline, bootconfig)) {
         mkdir("/first_stage_ramdisk", 0755);
+        PrepareSwitchRoot();
         // SwitchRoot() must be called with a mount point as the target, so we bind mount the
         // target directory to itself here.
         if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
-            LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
+            PLOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
         }
         SwitchRoot("/first_stage_ramdisk");
     }
diff --git a/init/switch_root.cpp b/init/switch_root.cpp
index 575b67f..86fad80 100644
--- a/init/switch_root.cpp
+++ b/init/switch_root.cpp
@@ -78,7 +78,8 @@
         auto new_mount_path = new_root + mount_path;
         mkdir(new_mount_path.c_str(), 0755);
         if (mount(mount_path.c_str(), new_mount_path.c_str(), nullptr, MS_MOVE, nullptr) != 0) {
-            PLOG(FATAL) << "Unable to move mount at '" << mount_path << "'";
+            PLOG(FATAL) << "Unable to move mount at '" << mount_path << "' to "
+                        << "'" << new_mount_path << "'";
         }
     }