Loading fs_mgr/fs_mgr_fstab.cpp +64 −43 Original line number Diff line number Diff line Loading @@ -539,6 +539,20 @@ bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) { return false; } template <typename Pred> std::vector<FstabEntry*> GetEntriesByPred(Fstab* fstab, const Pred& pred) { if (fstab == nullptr) { return {}; } std::vector<FstabEntry*> entries; for (auto&& entry : *fstab) { if (pred(entry)) { entries.push_back(&entry); } } return entries; } } // namespace bool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out) { Loading Loading @@ -614,11 +628,7 @@ void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot, userdata = BuildDsuUserdataFstabEntry(); } if (EraseFstabEntry(fstab, "/data")) { fstab->emplace_back(userdata); } // Convert others // Convert RO partitions. for (auto&& partition : dsu_partitions) { if (!EndsWith(partition, gsi::kDsuPostfix)) { continue; Loading @@ -638,48 +648,59 @@ void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot, // vendor_gsi for vendor std::string lp_name = partition.substr(0, partition.length() - strlen(gsi::kDsuPostfix)); std::string mount_point = "/" + lp_name; 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, // 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; }; 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) { // 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 for DSU should always be under kDsuKeysDir. // .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); } } // 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); 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, }})); } } } // Always append userdata last for stable ordering. if (EraseFstabEntry(fstab, "/data")) { fstab->emplace_back(userdata); } } void EnableMandatoryFlags(Fstab* fstab) { Loading fs_mgr/tests/fs_mgr_test.cpp +43 −2 Original line number Diff line number Diff line Loading @@ -1109,14 +1109,17 @@ TEST(fs_mgr, TransformFstabForDsu) { 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"}); Loading @@ -1126,10 +1129,12 @@ data /data f2fs noatime wait 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); Loading @@ -1147,7 +1152,7 @@ TEST(fs_mgr, TransformFstabForDsu_synthesisExt4Entry) { 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 data /data f2fs noatime wait,latemount )fs"; ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path)); Loading Loading @@ -1177,3 +1182,39 @@ data /data f2fs noatime wait 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("/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++; EXPECT_EQ("/data", entry->mount_point); EXPECT_EQ("userdata_gsi", entry->blk_device); entry++; } Loading
fs_mgr/fs_mgr_fstab.cpp +64 −43 Original line number Diff line number Diff line Loading @@ -539,6 +539,20 @@ bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) { return false; } template <typename Pred> std::vector<FstabEntry*> GetEntriesByPred(Fstab* fstab, const Pred& pred) { if (fstab == nullptr) { return {}; } std::vector<FstabEntry*> entries; for (auto&& entry : *fstab) { if (pred(entry)) { entries.push_back(&entry); } } return entries; } } // namespace bool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out) { Loading Loading @@ -614,11 +628,7 @@ void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot, userdata = BuildDsuUserdataFstabEntry(); } if (EraseFstabEntry(fstab, "/data")) { fstab->emplace_back(userdata); } // Convert others // Convert RO partitions. for (auto&& partition : dsu_partitions) { if (!EndsWith(partition, gsi::kDsuPostfix)) { continue; Loading @@ -638,48 +648,59 @@ void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot, // vendor_gsi for vendor std::string lp_name = partition.substr(0, partition.length() - strlen(gsi::kDsuPostfix)); std::string mount_point = "/" + lp_name; 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, // 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; }; 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) { // 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 for DSU should always be under kDsuKeysDir. // .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); } } // 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); 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, }})); } } } // Always append userdata last for stable ordering. if (EraseFstabEntry(fstab, "/data")) { fstab->emplace_back(userdata); } } void EnableMandatoryFlags(Fstab* fstab) { Loading
fs_mgr/tests/fs_mgr_test.cpp +43 −2 Original line number Diff line number Diff line Loading @@ -1109,14 +1109,17 @@ TEST(fs_mgr, TransformFstabForDsu) { 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"}); Loading @@ -1126,10 +1129,12 @@ data /data f2fs noatime wait 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); Loading @@ -1147,7 +1152,7 @@ TEST(fs_mgr, TransformFstabForDsu_synthesisExt4Entry) { 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 data /data f2fs noatime wait,latemount )fs"; ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path)); Loading Loading @@ -1177,3 +1182,39 @@ data /data f2fs noatime wait 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("/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++; EXPECT_EQ("/data", entry->mount_point); EXPECT_EQ("userdata_gsi", entry->blk_device); entry++; }