Loading fs_mgr/libsnapshot/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ cc_defaults { header_libs: [ "libchrome", "libfiemap_headers", "libstorage_literals_headers", "libupdate_engine_headers", ], export_static_lib_headers: [ Loading fs_mgr/libsnapshot/partition_cow_creator.cpp +19 −2 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <android-base/logging.h> #include <android/snapshot/snapshot.pb.h> #include <storage_literals/storage_literals.h> #include "dm_snapshot_internals.h" #include "utility.h" Loading @@ -34,6 +35,8 @@ using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>; namespace android { namespace snapshot { using namespace android::storage_literals; // Intersect two linear extents. If no intersection, return an extent with length 0. static std::unique_ptr<Extent> Intersect(Extent* target_extent, Extent* existing_extent) { // Convert target_extent and existing_extent to linear extents. Zero extents Loading Loading @@ -138,6 +141,17 @@ void WriteExtent(DmSnapCowSizeCalculator* sc, const chromeos_update_engine::Exte } uint64_t PartitionCowCreator::GetCowSize() { if (compression_enabled) { if (update == nullptr || !update->has_estimate_cow_size()) { LOG(ERROR) << "Update manifest does not include a COW size"; return 0; } // Add an extra 2MB of wiggle room for any minor differences in labels/metadata // that might come up. return update->estimate_cow_size() + 2_MiB; } // WARNING: The origin partition should be READ-ONLY const uint64_t logical_block_size = current_metadata->logical_block_size(); const unsigned int sectors_per_block = logical_block_size / kSectorSize; Loading @@ -149,9 +163,9 @@ uint64_t PartitionCowCreator::GetCowSize() { WriteExtent(&sc, de, sectors_per_block); } if (operations == nullptr) return sc.cow_size_bytes(); if (update == nullptr) return sc.cow_size_bytes(); for (const auto& iop : *operations) { for (const auto& iop : update->operations()) { const InstallOperation* written_op = &iop; InstallOperation buf; // Do not allocate space for extents that are going to be skipped Loading Loading @@ -213,6 +227,9 @@ std::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() { LOG(INFO) << "Remaining free space for COW: " << free_region_length << " bytes"; auto cow_size = GetCowSize(); if (!cow_size) { return {}; } // Compute the COW partition size. uint64_t cow_partition_size = std::min(cow_size, free_region_length); Loading fs_mgr/libsnapshot/partition_cow_creator.h +5 −2 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ struct PartitionCowCreator { using MetadataBuilder = android::fs_mgr::MetadataBuilder; using Partition = android::fs_mgr::Partition; using InstallOperation = chromeos_update_engine::InstallOperation; using PartitionUpdate = chromeos_update_engine::PartitionUpdate; template <typename T> using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>; Loading @@ -50,11 +51,13 @@ struct PartitionCowCreator { MetadataBuilder* current_metadata = nullptr; // The suffix of the current slot. std::string current_suffix; // List of operations to be applied on the partition. const RepeatedPtrField<InstallOperation>* operations = nullptr; // Partition information from the OTA manifest. const PartitionUpdate* update = nullptr; // Extra extents that are going to be invalidated during the update // process. std::vector<ChromeOSExtent> extra_extents = {}; // True if compression is enabled. bool compression_enabled = false; struct Return { SnapshotStatus snapshot_status; Loading fs_mgr/libsnapshot/partition_cow_creator_test.cpp +57 −3 Original line number Diff line number Diff line Loading @@ -145,13 +145,15 @@ TEST_F(PartitionCowCreatorTest, CowSize) { auto cow_device_size = [](const std::vector<InstallOperation>& iopv, MetadataBuilder* builder_a, MetadataBuilder* builder_b, Partition* system_b) { RepeatedInstallOperationPtr riop(iopv.begin(), iopv.end()); PartitionUpdate update; *update.mutable_operations() = RepeatedInstallOperationPtr(iopv.begin(), iopv.end()); PartitionCowCreator creator{.target_metadata = builder_b, .target_suffix = "_b", .target_partition = system_b, .current_metadata = builder_a, .current_suffix = "_a", .operations = &riop}; .update = &update}; auto ret = creator.Run(); Loading Loading @@ -218,7 +220,7 @@ TEST_F(PartitionCowCreatorTest, Zero) { .target_partition = system_b, .current_metadata = builder_a.get(), .current_suffix = "_a", .operations = nullptr}; .update = nullptr}; auto ret = creator.Run(); Loading @@ -228,6 +230,58 @@ TEST_F(PartitionCowCreatorTest, Zero) { ASSERT_EQ(0u, ret->snapshot_status.cow_partition_size()); } TEST_F(PartitionCowCreatorTest, CompressionEnabled) { constexpr uint64_t super_size = 1_MiB; auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2); ASSERT_NE(builder_a, nullptr); auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2); ASSERT_NE(builder_b, nullptr); auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY); ASSERT_NE(system_b, nullptr); ASSERT_TRUE(builder_b->ResizePartition(system_b, 128_KiB)); PartitionUpdate update; update.set_estimate_cow_size(256_KiB); PartitionCowCreator creator{.target_metadata = builder_b.get(), .target_suffix = "_b", .target_partition = system_b, .current_metadata = builder_a.get(), .current_suffix = "_a", .compression_enabled = true, .update = &update}; auto ret = creator.Run(); ASSERT_TRUE(ret.has_value()); ASSERT_EQ(ret->snapshot_status.cow_file_size(), 1458176); } TEST_F(PartitionCowCreatorTest, CompressionWithNoManifest) { constexpr uint64_t super_size = 1_MiB; auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2); ASSERT_NE(builder_a, nullptr); auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2); ASSERT_NE(builder_b, nullptr); auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY); ASSERT_NE(system_b, nullptr); ASSERT_TRUE(builder_b->ResizePartition(system_b, 128_KiB)); PartitionUpdate update; PartitionCowCreator creator{.target_metadata = builder_b.get(), .target_suffix = "_b", .target_partition = system_b, .current_metadata = builder_a.get(), .current_suffix = "_a", .compression_enabled = true, .update = nullptr}; auto ret = creator.Run(); ASSERT_FALSE(ret.has_value()); } TEST(DmSnapshotInternals, CowSizeCalculator) { SKIP_IF_NON_VIRTUAL_AB(); Loading fs_mgr/libsnapshot/snapshot.cpp +10 −13 Original line number Diff line number Diff line Loading @@ -75,7 +75,7 @@ using android::hardware::boot::V1_1::MergeStatus; using chromeos_update_engine::DeltaArchiveManifest; using chromeos_update_engine::Extent; using chromeos_update_engine::FileDescriptor; using chromeos_update_engine::InstallOperation; using chromeos_update_engine::PartitionUpdate; template <typename T> using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>; using std::chrono::duration_cast; Loading Loading @@ -116,10 +116,6 @@ SnapshotManager::SnapshotManager(IDeviceInfo* device) : device_(device) { metadata_dir_ = device_->GetMetadataDir(); } static inline bool IsCompressionEnabled() { return android::base::GetBoolProperty("ro.virtual_ab.compression.enabled", false); } static std::string GetCowName(const std::string& snapshot_name) { return snapshot_name + "-cow"; } Loading Loading @@ -2527,8 +2523,9 @@ Return SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manife .target_partition = nullptr, .current_metadata = current_metadata.get(), .current_suffix = current_suffix, .operations = nullptr, .update = nullptr, .extra_extents = {}, .compression_enabled = IsCompressionEnabled(), }; auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices, Loading Loading @@ -2572,12 +2569,11 @@ Return SnapshotManager::CreateUpdateSnapshotsInternal( return Return::Error(); } std::map<std::string, const RepeatedPtrField<InstallOperation>*> install_operation_map; std::map<std::string, const PartitionUpdate*> partition_map; std::map<std::string, std::vector<Extent>> extra_extents_map; for (const auto& partition_update : manifest.partitions()) { auto suffixed_name = partition_update.partition_name() + target_suffix; auto&& [it, inserted] = install_operation_map.emplace(suffixed_name, &partition_update.operations()); auto&& [it, inserted] = partition_map.emplace(suffixed_name, &partition_update); if (!inserted) { LOG(ERROR) << "Duplicated partition " << partition_update.partition_name() << " in update manifest."; Loading @@ -2595,10 +2591,10 @@ Return SnapshotManager::CreateUpdateSnapshotsInternal( for (auto* target_partition : ListPartitionsWithSuffix(target_metadata, target_suffix)) { cow_creator->target_partition = target_partition; cow_creator->operations = nullptr; auto operations_it = install_operation_map.find(target_partition->name()); if (operations_it != install_operation_map.end()) { cow_creator->operations = operations_it->second; cow_creator->update = nullptr; auto iter = partition_map.find(target_partition->name()); if (iter != partition_map.end()) { cow_creator->update = iter->second; } else { LOG(INFO) << target_partition->name() << " isn't included in the payload, skipping the cow creation."; Loading @@ -2614,6 +2610,7 @@ Return SnapshotManager::CreateUpdateSnapshotsInternal( // Compute the device sizes for the partition. auto cow_creator_ret = cow_creator->Run(); if (!cow_creator_ret.has_value()) { LOG(ERROR) << "PartitionCowCreator returned no value for " << target_partition->name(); return Return::Error(); } Loading Loading
fs_mgr/libsnapshot/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ cc_defaults { header_libs: [ "libchrome", "libfiemap_headers", "libstorage_literals_headers", "libupdate_engine_headers", ], export_static_lib_headers: [ Loading
fs_mgr/libsnapshot/partition_cow_creator.cpp +19 −2 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <android-base/logging.h> #include <android/snapshot/snapshot.pb.h> #include <storage_literals/storage_literals.h> #include "dm_snapshot_internals.h" #include "utility.h" Loading @@ -34,6 +35,8 @@ using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>; namespace android { namespace snapshot { using namespace android::storage_literals; // Intersect two linear extents. If no intersection, return an extent with length 0. static std::unique_ptr<Extent> Intersect(Extent* target_extent, Extent* existing_extent) { // Convert target_extent and existing_extent to linear extents. Zero extents Loading Loading @@ -138,6 +141,17 @@ void WriteExtent(DmSnapCowSizeCalculator* sc, const chromeos_update_engine::Exte } uint64_t PartitionCowCreator::GetCowSize() { if (compression_enabled) { if (update == nullptr || !update->has_estimate_cow_size()) { LOG(ERROR) << "Update manifest does not include a COW size"; return 0; } // Add an extra 2MB of wiggle room for any minor differences in labels/metadata // that might come up. return update->estimate_cow_size() + 2_MiB; } // WARNING: The origin partition should be READ-ONLY const uint64_t logical_block_size = current_metadata->logical_block_size(); const unsigned int sectors_per_block = logical_block_size / kSectorSize; Loading @@ -149,9 +163,9 @@ uint64_t PartitionCowCreator::GetCowSize() { WriteExtent(&sc, de, sectors_per_block); } if (operations == nullptr) return sc.cow_size_bytes(); if (update == nullptr) return sc.cow_size_bytes(); for (const auto& iop : *operations) { for (const auto& iop : update->operations()) { const InstallOperation* written_op = &iop; InstallOperation buf; // Do not allocate space for extents that are going to be skipped Loading Loading @@ -213,6 +227,9 @@ std::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() { LOG(INFO) << "Remaining free space for COW: " << free_region_length << " bytes"; auto cow_size = GetCowSize(); if (!cow_size) { return {}; } // Compute the COW partition size. uint64_t cow_partition_size = std::min(cow_size, free_region_length); Loading
fs_mgr/libsnapshot/partition_cow_creator.h +5 −2 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ struct PartitionCowCreator { using MetadataBuilder = android::fs_mgr::MetadataBuilder; using Partition = android::fs_mgr::Partition; using InstallOperation = chromeos_update_engine::InstallOperation; using PartitionUpdate = chromeos_update_engine::PartitionUpdate; template <typename T> using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>; Loading @@ -50,11 +51,13 @@ struct PartitionCowCreator { MetadataBuilder* current_metadata = nullptr; // The suffix of the current slot. std::string current_suffix; // List of operations to be applied on the partition. const RepeatedPtrField<InstallOperation>* operations = nullptr; // Partition information from the OTA manifest. const PartitionUpdate* update = nullptr; // Extra extents that are going to be invalidated during the update // process. std::vector<ChromeOSExtent> extra_extents = {}; // True if compression is enabled. bool compression_enabled = false; struct Return { SnapshotStatus snapshot_status; Loading
fs_mgr/libsnapshot/partition_cow_creator_test.cpp +57 −3 Original line number Diff line number Diff line Loading @@ -145,13 +145,15 @@ TEST_F(PartitionCowCreatorTest, CowSize) { auto cow_device_size = [](const std::vector<InstallOperation>& iopv, MetadataBuilder* builder_a, MetadataBuilder* builder_b, Partition* system_b) { RepeatedInstallOperationPtr riop(iopv.begin(), iopv.end()); PartitionUpdate update; *update.mutable_operations() = RepeatedInstallOperationPtr(iopv.begin(), iopv.end()); PartitionCowCreator creator{.target_metadata = builder_b, .target_suffix = "_b", .target_partition = system_b, .current_metadata = builder_a, .current_suffix = "_a", .operations = &riop}; .update = &update}; auto ret = creator.Run(); Loading Loading @@ -218,7 +220,7 @@ TEST_F(PartitionCowCreatorTest, Zero) { .target_partition = system_b, .current_metadata = builder_a.get(), .current_suffix = "_a", .operations = nullptr}; .update = nullptr}; auto ret = creator.Run(); Loading @@ -228,6 +230,58 @@ TEST_F(PartitionCowCreatorTest, Zero) { ASSERT_EQ(0u, ret->snapshot_status.cow_partition_size()); } TEST_F(PartitionCowCreatorTest, CompressionEnabled) { constexpr uint64_t super_size = 1_MiB; auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2); ASSERT_NE(builder_a, nullptr); auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2); ASSERT_NE(builder_b, nullptr); auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY); ASSERT_NE(system_b, nullptr); ASSERT_TRUE(builder_b->ResizePartition(system_b, 128_KiB)); PartitionUpdate update; update.set_estimate_cow_size(256_KiB); PartitionCowCreator creator{.target_metadata = builder_b.get(), .target_suffix = "_b", .target_partition = system_b, .current_metadata = builder_a.get(), .current_suffix = "_a", .compression_enabled = true, .update = &update}; auto ret = creator.Run(); ASSERT_TRUE(ret.has_value()); ASSERT_EQ(ret->snapshot_status.cow_file_size(), 1458176); } TEST_F(PartitionCowCreatorTest, CompressionWithNoManifest) { constexpr uint64_t super_size = 1_MiB; auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2); ASSERT_NE(builder_a, nullptr); auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2); ASSERT_NE(builder_b, nullptr); auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY); ASSERT_NE(system_b, nullptr); ASSERT_TRUE(builder_b->ResizePartition(system_b, 128_KiB)); PartitionUpdate update; PartitionCowCreator creator{.target_metadata = builder_b.get(), .target_suffix = "_b", .target_partition = system_b, .current_metadata = builder_a.get(), .current_suffix = "_a", .compression_enabled = true, .update = nullptr}; auto ret = creator.Run(); ASSERT_FALSE(ret.has_value()); } TEST(DmSnapshotInternals, CowSizeCalculator) { SKIP_IF_NON_VIRTUAL_AB(); Loading
fs_mgr/libsnapshot/snapshot.cpp +10 −13 Original line number Diff line number Diff line Loading @@ -75,7 +75,7 @@ using android::hardware::boot::V1_1::MergeStatus; using chromeos_update_engine::DeltaArchiveManifest; using chromeos_update_engine::Extent; using chromeos_update_engine::FileDescriptor; using chromeos_update_engine::InstallOperation; using chromeos_update_engine::PartitionUpdate; template <typename T> using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>; using std::chrono::duration_cast; Loading Loading @@ -116,10 +116,6 @@ SnapshotManager::SnapshotManager(IDeviceInfo* device) : device_(device) { metadata_dir_ = device_->GetMetadataDir(); } static inline bool IsCompressionEnabled() { return android::base::GetBoolProperty("ro.virtual_ab.compression.enabled", false); } static std::string GetCowName(const std::string& snapshot_name) { return snapshot_name + "-cow"; } Loading Loading @@ -2527,8 +2523,9 @@ Return SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manife .target_partition = nullptr, .current_metadata = current_metadata.get(), .current_suffix = current_suffix, .operations = nullptr, .update = nullptr, .extra_extents = {}, .compression_enabled = IsCompressionEnabled(), }; auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices, Loading Loading @@ -2572,12 +2569,11 @@ Return SnapshotManager::CreateUpdateSnapshotsInternal( return Return::Error(); } std::map<std::string, const RepeatedPtrField<InstallOperation>*> install_operation_map; std::map<std::string, const PartitionUpdate*> partition_map; std::map<std::string, std::vector<Extent>> extra_extents_map; for (const auto& partition_update : manifest.partitions()) { auto suffixed_name = partition_update.partition_name() + target_suffix; auto&& [it, inserted] = install_operation_map.emplace(suffixed_name, &partition_update.operations()); auto&& [it, inserted] = partition_map.emplace(suffixed_name, &partition_update); if (!inserted) { LOG(ERROR) << "Duplicated partition " << partition_update.partition_name() << " in update manifest."; Loading @@ -2595,10 +2591,10 @@ Return SnapshotManager::CreateUpdateSnapshotsInternal( for (auto* target_partition : ListPartitionsWithSuffix(target_metadata, target_suffix)) { cow_creator->target_partition = target_partition; cow_creator->operations = nullptr; auto operations_it = install_operation_map.find(target_partition->name()); if (operations_it != install_operation_map.end()) { cow_creator->operations = operations_it->second; cow_creator->update = nullptr; auto iter = partition_map.find(target_partition->name()); if (iter != partition_map.end()) { cow_creator->update = iter->second; } else { LOG(INFO) << target_partition->name() << " isn't included in the payload, skipping the cow creation."; Loading @@ -2614,6 +2610,7 @@ Return SnapshotManager::CreateUpdateSnapshotsInternal( // Compute the device sizes for the partition. auto cow_creator_ret = cow_creator->Run(); if (!cow_creator_ret.has_value()) { LOG(ERROR) << "PartitionCowCreator returned no value for " << target_partition->name(); return Return::Error(); } Loading