diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp index 474d4828c..8e4b5564b 100644 --- a/fs_mgr/libsnapshot/Android.bp +++ b/fs_mgr/libsnapshot/Android.bp @@ -324,6 +324,22 @@ cc_binary { "libstatslog", "libutils", ], + header_libs: [ + "libstorage_literals_headers", + ], + product_variables: { + debuggable: { + cppflags: [ + "-DSNAPSHOTCTL_USERDEBUG_OR_ENG", + ], + shared_libs: [ + "android.hardware.boot@1.0", + "android.hardware.boot@1.1", + "android.hardware.boot-V1-ndk", + "libboot_control_client", + ], + }, + }, } cc_test { diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp index 67189d4be..ad3f83cba 100644 --- a/fs_mgr/libsnapshot/snapshotctl.cpp +++ b/fs_mgr/libsnapshot/snapshotctl.cpp @@ -25,9 +25,27 @@ #include #include +#include +#include +#include +#include +#include #include +#include +#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG +#include +#endif + +using namespace std::chrono_literals; using namespace std::string_literals; +using namespace android::storage_literals; +using android::fs_mgr::CreateLogicalPartitionParams; +using android::fs_mgr::FindPartition; +using android::fs_mgr::GetPartitionSize; +using android::fs_mgr::PartitionOpener; +using android::fs_mgr::ReadMetadata; +using android::fs_mgr::SlotNumberForSlotSuffix; int Usage() { std::cerr << "snapshotctl: Control snapshots.\n" @@ -67,11 +85,136 @@ bool MergeCmdHandler(int /*argc*/, char** argv) { return false; } +#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG +bool CreateTestUpdate(SnapshotManager* sm) { + chromeos_update_engine::DeltaArchiveManifest manifest; + + // We only copy system, to simplify things. + manifest.set_partial_update(true); + + auto dap = manifest.mutable_dynamic_partition_metadata(); + dap->set_snapshot_enabled(true); + dap->set_vabc_enabled(true); + dap->set_vabc_compression_param("none"); + dap->set_cow_version(kCowVersionMajor); + + auto source_slot = fs_mgr_get_slot_suffix(); + auto source_slot_number = SlotNumberForSlotSuffix(source_slot); + auto target_slot = fs_mgr_get_other_slot_suffix(); + auto target_slot_number = SlotNumberForSlotSuffix(target_slot); + auto super_source = fs_mgr_get_super_partition_name(source_slot_number); + + // Get current partition information. + PartitionOpener opener; + auto source_metadata = ReadMetadata(opener, super_source, source_slot_number); + if (!source_metadata) { + std::cerr << "Could not read source partition metadata.\n"; + return false; + } + + auto system_source_name = "system" + source_slot; + auto system_source = FindPartition(*source_metadata.get(), system_source_name); + if (!system_source) { + std::cerr << "Could not find system partition: " << system_source_name << ".\n"; + return false; + } + auto system_source_size = GetPartitionSize(*source_metadata.get(), *system_source); + + // Since we only add copy operations, 64MB should be enough. + auto system_update = manifest.mutable_partitions()->Add(); + system_update->set_partition_name("system"); + system_update->set_estimate_cow_size(64_MiB); + system_update->mutable_new_partition_info()->set_size(system_source_size); + + if (!sm->CreateUpdateSnapshots(manifest)) { + std::cerr << "Could not create update snapshots.\n"; + return false; + } + + // Write the "new" system partition. + auto system_target_name = "system" + target_slot; + auto source_device = "/dev/block/mapper/" + system_source_name; + CreateLogicalPartitionParams clpp = { + .block_device = fs_mgr_get_super_partition_name(target_slot_number), + .metadata_slot = {target_slot_number}, + .partition_name = system_target_name, + .partition_opener = &opener, + .timeout_ms = 10s, + }; + auto writer = sm->OpenSnapshotWriter(clpp, {source_device}); + if (!writer) { + std::cerr << "Could not open snapshot writer.\n"; + return false; + } + if (!writer->Initialize()) { + std::cerr << "Could not initialize snapshot for writing.\n"; + return false; + } + + for (uint64_t block = 0; block < system_source_size / 4096; block++) { + if (!writer->AddCopy(block, block)) { + std::cerr << "Unable to add copy operation for block " << block << ".\n"; + return false; + } + } + if (!writer->Finalize()) { + std::cerr << "Could not finalize COW for " << system_target_name << ".\n"; + return false; + } + writer = nullptr; + + // Finished writing this partition, unmap. + if (!sm->UnmapUpdateSnapshot(system_target_name)) { + std::cerr << "Could not unmap snapshot for " << system_target_name << ".\n"; + return false; + } + + // All snapshots have been written. + if (!sm->FinishedSnapshotWrites(false /* wipe */)) { + std::cerr << "Could not finalize snapshot writes.\n"; + return false; + } + + auto hal = hal::BootControlClient::WaitForService(); + if (!hal) { + std::cerr << "Could not find IBootControl HAL.\n"; + return false; + } + auto cr = hal->SetActiveBootSlot(target_slot_number); + if (!cr.IsOk()) { + std::cerr << "Could not set active boot slot: " << cr.errMsg; + return false; + } + + std::cerr << "It is now safe to reboot your device. If using a physical device, make\n" + << "sure that all physical partitions are flashed to both A and B slots.\n"; + return true; +} + +bool TestOtaHandler(int /* argc */, char** /* argv */) { + auto sm = SnapshotManager::New(); + + if (!sm->BeginUpdate()) { + std::cerr << "Error starting update.\n"; + return false; + } + + if (!CreateTestUpdate(sm.get())) { + sm->CancelUpdate(); + return false; + } + return true; +} +#endif + static std::map> kCmdMap = { // clang-format off {"dump", DumpCmdHandler}, {"merge", MergeCmdHandler}, {"map", MapCmdHandler}, +#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG + {"test-blank-ota", TestOtaHandler}, +#endif {"unmap", UnmapCmdHandler}, // clang-format on };