[coastguard skipped] Merge sparse cherrypicks from sparse-10316531-L68800000962056696 into tm-qpr3-release.

COASTGUARD_SKIP: Ibe1c289e1b4d3af39fa2423139f134632032c37c
COASTGUARD_SKIP: I57132a55a089c7aae3e17c717ecd9dc1047fede8
COASTGUARD_SKIP: I4afb443b7e527038fb42424543bb1538b01d5ec9

Change-Id: Icb6dcb775e65b7efd0e1a84ffe695b0aa6749105
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 98e0f5a..8c719c8 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -502,18 +502,33 @@
     return boot_devices;
 }
 
-template <typename Pred>
-std::vector<FstabEntry*> GetEntriesByPred(Fstab* fstab, const Pred& pred) {
-    if (fstab == nullptr) {
-        return {};
+FstabEntry BuildDsuUserdataFstabEntry() {
+    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;
+    userdata.fs_mgr_flags.formattable = true;
+    return userdata;
+}
+
+bool 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; });
+    if (iter != fstab->end()) {
+        fstab->erase(iter, fstab->end());
+        return true;
     }
-    std::vector<FstabEntry*> entries;
-    for (auto&& entry : *fstab) {
-        if (pred(entry)) {
-            entries.push_back(&entry);
-        }
-    }
-    return entries;
+    return false;
 }
 
 }  // namespace
@@ -576,28 +591,38 @@
 void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot,
                           const std::vector<std::string>& dsu_partitions) {
     static constexpr char kDsuKeysDir[] = "/avb";
+    // Convert userdata
+    // Inherit fstab properties for userdata.
+    FstabEntry userdata;
+    if (FstabEntry* entry = GetEntryForMountPoint(fstab, "/data")) {
+        userdata = *entry;
+        userdata.blk_device = android::gsi::kDsuUserdata;
+        userdata.fs_mgr_flags.logical = true;
+        userdata.fs_mgr_flags.formattable = true;
+        if (!userdata.metadata_key_dir.empty()) {
+            userdata.metadata_key_dir = android::gsi::GetDsuMetadataKeyDir(dsu_slot);
+        }
+    } else {
+        userdata = BuildDsuUserdataFstabEntry();
+    }
+
+    if (EraseFstabEntry(fstab, "/data")) {
+        fstab->emplace_back(userdata);
+    }
+
+    // Convert others
     for (auto&& partition : dsu_partitions) {
         if (!EndsWith(partition, gsi::kDsuPostfix)) {
             continue;
         }
+        // userdata has been handled
+        if (partition == android::gsi::kDsuUserdata) {
+            continue;
+        }
         // scratch is handled by fs_mgr_overlayfs
         if (partition == android::gsi::kDsuScratch) {
             continue;
         }
-        // Convert userdata partition.
-        if (partition == android::gsi::kDsuUserdata) {
-            for (auto&& entry : GetEntriesForMountPoint(fstab, "/data")) {
-                entry->blk_device = android::gsi::kDsuUserdata;
-                entry->fs_mgr_flags.logical = true;
-                entry->fs_mgr_flags.formattable = true;
-                if (!entry->metadata_key_dir.empty()) {
-                    entry->metadata_key_dir = android::gsi::GetDsuMetadataKeyDir(dsu_slot);
-                }
-            }
-            continue;
-        }
-        // Convert RO partitions.
-        //
         // dsu_partition_name = corresponding_partition_name + kDsuPostfix
         // e.g.
         //    system_gsi for system
@@ -605,51 +630,45 @@
         //    vendor_gsi for vendor
         std::string lp_name = partition.substr(0, partition.length() - strlen(gsi::kDsuPostfix));
         std::string mount_point = "/" + lp_name;
-
-        // List of fs_type entries we're lacking, need to synthesis these later.
-        std::vector<std::string> lack_fs_list = {"ext4", "erofs"};
-
-        // Only support early mount (first_stage_mount) partitions.
-        auto pred = [&mount_point](const FstabEntry& entry) {
-            return entry.fs_mgr_flags.first_stage_mount && entry.mount_point == mount_point;
-        };
-
-        // Transform all matching entries and assume they are all adjacent for simplicity.
-        for (auto&& entry : GetEntriesByPred(fstab, pred)) {
-            // .blk_device is replaced with the DSU partition.
-            entry->blk_device = partition;
-            // .avb_keys hints first_stage_mount to load the chained-vbmeta image from partition
-            // footer. See aosp/932779 for more details.
-            entry->avb_keys = kDsuKeysDir;
-            // .logical_partition_name is required to look up AVB Hashtree descriptors.
-            entry->logical_partition_name = lp_name;
-            entry->fs_mgr_flags.logical = true;
-            entry->fs_mgr_flags.slot_select = false;
-            entry->fs_mgr_flags.slot_select_other = false;
-
-            if (auto it = std::find(lack_fs_list.begin(), lack_fs_list.end(), entry->fs_type);
-                it != lack_fs_list.end()) {
-                lack_fs_list.erase(it);
+        std::vector<FstabEntry*> entries = GetEntriesForMountPoint(fstab, mount_point);
+        if (entries.empty()) {
+            FstabEntry entry = {
+                    .blk_device = partition,
+                    // .logical_partition_name is required to look up AVB Hashtree descriptors.
+                    .logical_partition_name = "system",
+                    .mount_point = mount_point,
+                    .fs_type = "ext4",
+                    .flags = MS_RDONLY,
+                    .fs_options = "barrier=1",
+                    .avb_keys = kDsuKeysDir,
+            };
+            entry.fs_mgr_flags.wait = true;
+            entry.fs_mgr_flags.logical = true;
+            entry.fs_mgr_flags.first_stage_mount = true;
+            fstab->emplace_back(entry);
+        } else {
+            // If the corresponding partition exists, transform all its Fstab
+            // by pointing .blk_device to the DSU partition.
+            for (auto&& entry : entries) {
+                entry->blk_device = partition;
+                // AVB keys for DSU should always be under kDsuKeysDir.
+                entry->avb_keys = kDsuKeysDir;
+                entry->fs_mgr_flags.logical = true;
             }
-        }
-
-        if (!lack_fs_list.empty()) {
-            // Insert at the end of the existing mountpoint group, or at the end of fstab.
-            // We assume there is at most one matching mountpoint group, which is the common case.
-            auto it = std::find_if_not(std::find_if(fstab->begin(), fstab->end(), pred),
-                                       fstab->end(), pred);
-            for (const auto& fs_type : lack_fs_list) {
-                it = std::next(fstab->insert(it, {.blk_device = partition,
-                                                  .logical_partition_name = lp_name,
-                                                  .mount_point = mount_point,
-                                                  .fs_type = fs_type,
-                                                  .flags = MS_RDONLY,
-                                                  .avb_keys = kDsuKeysDir,
-                                                  .fs_mgr_flags{
-                                                          .wait = true,
-                                                          .logical = true,
-                                                          .first_stage_mount = true,
-                                                  }}));
+            // Make sure the ext4 is included to support GSI.
+            auto partition_ext4 =
+                    std::find_if(fstab->begin(), fstab->end(), [&](const auto& entry) {
+                        return entry.mount_point == mount_point && entry.fs_type == "ext4";
+                    });
+            if (partition_ext4 == fstab->end()) {
+                auto new_entry = *GetEntryForMountPoint(fstab, mount_point);
+                new_entry.fs_type = "ext4";
+                auto it = std::find_if(fstab->rbegin(), fstab->rend(),
+                                       [&mount_point](const auto& entry) {
+                                           return entry.mount_point == mount_point;
+                                       });
+                auto end_of_mount_point_group = fstab->begin() + std::distance(it, fstab->rend());
+                fstab->insert(end_of_mount_point_group, new_entry);
             }
         }
     }
@@ -838,8 +857,18 @@
 }
 
 std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path) {
-    return GetEntriesByPred(fstab,
-                            [&path](const FstabEntry& entry) { return entry.mount_point == path; });
+    std::vector<FstabEntry*> entries;
+    if (fstab == nullptr) {
+        return entries;
+    }
+
+    for (auto& entry : *fstab) {
+        if (entry.mount_point == path) {
+            entries.emplace_back(&entry);
+        }
+    }
+
+    return entries;
 }
 
 std::set<std::string> GetBootDevices() {
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index e33681c..6c881c0 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -1109,17 +1109,14 @@
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
     std::string fstab_contents = R"fs(
-data   /data        f2fs    noatime     wait,latemount
 system /system      erofs   ro  wait,logical,first_stage_mount
 system /system      ext4    ro  wait,logical,first_stage_mount
 vendor /vendor      ext4    ro  wait,logical,first_stage_mount
+data   /data        f2fs    noatime     wait
 )fs";
 
     ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
-    // If GSI is installed, ReadFstabFromFile() would have called TransformFstabForDsu() implicitly.
-    // In other words, TransformFstabForDsu() would be called two times if running CTS-on-GSI,
-    // which implies TransformFstabForDsu() should be idempotent.
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
     TransformFstabForDsu(&fstab, "dsu", {"system_gsi", "userdata_gsi"});
@@ -1127,23 +1124,21 @@
 
     auto entry = fstab.begin();
 
-    EXPECT_EQ("/data", entry->mount_point);
-    EXPECT_EQ("userdata_gsi", entry->blk_device);
+    EXPECT_EQ("/system", entry->mount_point);
+    EXPECT_EQ("system_gsi", entry->blk_device);
     entry++;
 
     EXPECT_EQ("/system", entry->mount_point);
     EXPECT_EQ("system_gsi", entry->blk_device);
-    EXPECT_EQ("erofs", entry->fs_type);
-    entry++;
-
-    EXPECT_EQ("/system", entry->mount_point);
-    EXPECT_EQ("system_gsi", entry->blk_device);
-    EXPECT_EQ("ext4", entry->fs_type);
     entry++;
 
     EXPECT_EQ("/vendor", entry->mount_point);
     EXPECT_EQ("vendor", entry->blk_device);
     entry++;
+
+    EXPECT_EQ("/data", entry->mount_point);
+    EXPECT_EQ("userdata_gsi", entry->blk_device);
+    entry++;
 }
 
 TEST(fs_mgr, TransformFstabForDsu_synthesisExt4Entry) {
@@ -1152,7 +1147,7 @@
     std::string fstab_contents = R"fs(
 system /system      erofs   ro  wait,logical,first_stage_mount
 vendor /vendor      ext4    ro  wait,logical,first_stage_mount
-data   /data        f2fs    noatime     wait,latemount
+data   /data        f2fs    noatime     wait
 )fs";
 
     ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
@@ -1182,39 +1177,3 @@
     EXPECT_EQ("userdata_gsi", entry->blk_device);
     entry++;
 }
-
-TEST(fs_mgr, TransformFstabForDsu_synthesisAllMissingEntries) {
-    TemporaryFile tf;
-    ASSERT_TRUE(tf.fd != -1);
-    std::string fstab_contents = R"fs(
-data   /data        f2fs    noatime     wait,latemount
-vendor /vendor      ext4    ro  wait,logical,first_stage_mount
-)fs";
-
-    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
-
-    Fstab fstab;
-    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
-    TransformFstabForDsu(&fstab, "dsu", {"system_gsi", "userdata_gsi"});
-    ASSERT_EQ(4U, fstab.size());
-
-    auto entry = fstab.begin();
-
-    EXPECT_EQ("/data", entry->mount_point);
-    EXPECT_EQ("userdata_gsi", entry->blk_device);
-    entry++;
-
-    EXPECT_EQ("/vendor", entry->mount_point);
-    EXPECT_EQ("vendor", entry->blk_device);
-    entry++;
-
-    EXPECT_EQ("/system", entry->mount_point);
-    EXPECT_EQ("system_gsi", entry->blk_device);
-    EXPECT_EQ("ext4", entry->fs_type);
-    entry++;
-
-    EXPECT_EQ("/system", entry->mount_point);
-    EXPECT_EQ("system_gsi", entry->blk_device);
-    EXPECT_EQ("erofs", entry->fs_type);
-    entry++;
-}