init: Add support for GSI installations in first-stage mount.

Bug: 121209697
Test: gsi boots
Change-Id: I69db0f8e999da366e46728b1008602f543cd79f6
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index d9f2837..974e13e 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -59,6 +59,7 @@
         "libfs_avb",
         "libfstab",
         "libdm",
+        "libgsi",
     ],
     export_static_lib_headers: [
         "libfs_avb",
@@ -105,5 +106,8 @@
         },
     },
     export_include_dirs: ["include_fstab"],
-    header_libs: ["libbase_headers"],
+    header_libs: [
+        "libbase_headers",
+        "libgsi_headers",
+    ],
 }
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 53b47be..2576035 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -30,6 +30,7 @@
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <libgsi/libgsi.h>
 
 #include "fs_mgr_priv.h"
 
@@ -638,6 +639,35 @@
     return boot_devices;
 }
 
+static void EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
+    auto iter = std::remove_if(fstab->begin(), fstab->end(),
+                               [&](const auto& entry) { return entry.mount_point == mount_point; });
+    fstab->erase(iter, fstab->end());
+}
+
+static void TransformFstabForGsi(Fstab* fstab) {
+    EraseFstabEntry(fstab, "/system");
+    EraseFstabEntry(fstab, "/data");
+
+    fstab->emplace_back(BuildGsiSystemFstabEntry());
+
+    constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
+
+    FstabEntry userdata = {
+            .blk_device = "userdata_gsi",
+            .mount_point = "/data",
+            .fs_type = "ext4",
+            .flags = kFlags,
+            .reserved_size = 128 * 1024 * 1024,
+    };
+    userdata.fs_mgr_flags.wait = true;
+    userdata.fs_mgr_flags.check = true;
+    userdata.fs_mgr_flags.logical = true;
+    userdata.fs_mgr_flags.quota = true;
+    userdata.fs_mgr_flags.late_mount = true;
+    fstab->emplace_back(userdata);
+}
+
 bool ReadFstabFromFile(const std::string& path, Fstab* fstab) {
     auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
     if (!fstab_file) {
@@ -645,10 +675,15 @@
         return false;
     }
 
-    if (!fs_mgr_read_fstab_file(fstab_file.get(), path == "/proc/mounts", fstab)) {
+    bool is_proc_mounts = path == "/proc/mounts";
+
+    if (!fs_mgr_read_fstab_file(fstab_file.get(), is_proc_mounts, fstab)) {
         LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << path << "'";
         return false;
     }
+    if (!is_proc_mounts && !access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
+        TransformFstabForGsi(fstab);
+    }
 
     return true;
 }
@@ -1023,3 +1058,17 @@
 int fs_mgr_is_checkpoint_blk(const struct fstab_rec* fstab) {
     return fstab->fs_mgr_flags & MF_CHECKPOINT_BLK;
 }
+
+FstabEntry BuildGsiSystemFstabEntry() {
+    FstabEntry system = {
+            .blk_device = "system_gsi",
+            .mount_point = "/system",
+            .fs_type = "ext4",
+            .flags = MS_RDONLY,
+            .fs_options = "barrier=1",
+    };
+    system.fs_mgr_flags.wait = true;
+    system.fs_mgr_flags.logical = true;
+    system.fs_mgr_flags.first_stage_mount = true;
+    return system;
+}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 6364ca9..0f89467 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -48,6 +48,7 @@
 #include <fs_mgr_overlayfs.h>
 #include <fstab/fstab.h>
 #include <libdm/dm.h>
+#include <libgsi/libgsi.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
 
@@ -802,8 +803,9 @@
 bool fs_mgr_overlayfs_invalid() {
     if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return true;
 
-    // in recovery or fastbootd mode, not allowed!
+    // in recovery, fastbootd, or gsi mode, not allowed!
     if (fs_mgr_access("/system/bin/recovery")) return true;
+    if (android::gsi::IsGsiRunning()) return true;
 
     return false;
 }
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 0997254..6643c0d 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -187,3 +187,6 @@
 FstabEntry FstabRecToFstabEntry(const fstab_rec* fstab_rec);
 Fstab LegacyFstabToFstab(const struct fstab* legacy_fstab);
 fstab* FstabToLegacyFstab(const Fstab& fstab);
+
+// Helper method to build a GSI fstab entry for mounting /system.
+FstabEntry BuildGsiSystemFstabEntry();
diff --git a/init/Android.bp b/init/Android.bp
index c920dc2..3369fb9 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -77,6 +77,7 @@
         "libext4_utils",
         "libfs_mgr",
         "libfscrypt",
+        "libgsi",
         "libhidl-gen-utils",
         "libkeyutils",
         "liblog",
diff --git a/init/Android.mk b/init/Android.mk
index bdd0301..69c63e1 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -91,6 +91,7 @@
     libz \
     libselinux \
     libcap \
+    libgsi \
 
 LOCAL_SANITIZE := signed-integer-overflow
 # First stage init is weird: it may start without stdout/stderr, and no /proc.
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 4a66e46..0139317 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -54,6 +54,7 @@
 #include <fs_mgr.h>
 #include <fscrypt/fscrypt.h>
 #include <fscrypt/fscrypt_init_extensions.h>
+#include <libgsi/libgsi.h>
 #include <selinux/android.h>
 #include <selinux/label.h>
 #include <selinux/selinux.h>
@@ -520,6 +521,9 @@
         return Success();
     } else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
         /* Setup a wipe via recovery, and reboot into recovery */
+        if (android::gsi::IsGsiRunning()) {
+            return Error() << "cannot wipe within GSI";
+        }
         PLOG(ERROR) << "fs_mgr_mount_all suggested recovery, so wiping data via recovery.";
         const std::vector<std::string> options = {"--wipe_data", "--reason=fs_mgr_mount_all" };
         return reboot_into_recovery(options);
@@ -1022,7 +1026,8 @@
     }
     service->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
         if (siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) {
-            if (fscrypt_is_native()) {
+            // TODO (b/122850122): support this in gsi
+            if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
                 LOG(ERROR) << "Rebooting into recovery, reason: " << reboot_reason;
                 if (auto result = reboot_into_recovery(
                             {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 71fe401..affa39e 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -34,6 +34,7 @@
 #include <fs_mgr.h>
 #include <fs_mgr_dm_linear.h>
 #include <fs_mgr_overlayfs.h>
+#include <libgsi/libgsi.h>
 #include <liblp/liblp.h>
 
 #include "devices.h"
@@ -79,6 +80,7 @@
     bool IsDmLinearEnabled();
     bool GetDmLinearMetadataDevice();
     bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);
+    void UseGsiIfPresent();
 
     ListenerAction UeventCallback(const Uevent& uevent);
 
@@ -207,6 +209,8 @@
     }
 
     required_devices_partition_names_.emplace(super_partition_name_);
+    // When booting from live GSI images, userdata is the super device.
+    required_devices_partition_names_.emplace("userdata");
     return true;
 }
 
@@ -410,6 +414,16 @@
 // this case, we mount system first then pivot to it.  From that point on,
 // we are effectively identical to a system-as-root device.
 bool FirstStageMount::TrySwitchSystemAsRoot() {
+    auto metadata_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
+        return entry.mount_point == "/metadata";
+    });
+    if (metadata_partition != fstab_.end()) {
+        if (MountPartition(&(*metadata_partition))) {
+            fstab_.erase(metadata_partition);
+            UseGsiIfPresent();
+        }
+    }
+
     auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
         return entry.mount_point == "/system";
     });
@@ -513,6 +527,40 @@
     return true;
 }
 
+void FirstStageMount::UseGsiIfPresent() {
+    std::string metadata_file, error;
+
+    if (!android::gsi::CanBootIntoGsi(&metadata_file, &error)) {
+        LOG(INFO) << "GSI " << error << ", proceeding with normal boot";
+        return;
+    }
+
+    auto metadata = android::fs_mgr::ReadFromImageFile(metadata_file.c_str());
+    if (!metadata) {
+        LOG(ERROR) << "GSI partition layout could not be read";
+        return;
+    }
+
+    if (!android::fs_mgr::CreateLogicalPartitions(*metadata.get(), "/dev/block/by-name/userdata")) {
+        LOG(ERROR) << "GSI partition layout could not be instantiated";
+        return;
+    }
+
+    if (!android::gsi::MarkSystemAsGsi()) {
+        PLOG(ERROR) << "GSI indicator file could not be written";
+        return;
+    }
+
+    // Replace the existing system fstab entry.
+    auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
+        return entry.mount_point == "/system";
+    });
+    if (system_partition != fstab_.end()) {
+        fstab_.erase(system_partition);
+    }
+    fstab_.emplace_back(BuildGsiSystemFstabEntry());
+}
+
 bool FirstStageMountVBootV1::GetDmVerityDevices() {
     std::string verity_loc_device;
     need_dm_verity_ = false;