diff --git a/fastboot/Android.bp b/fastboot/Android.bp index 02a887ec8..a757d56e5 100644 --- a/fastboot/Android.bp +++ b/fastboot/Android.bp @@ -137,12 +137,14 @@ cc_binary { "libhidlbase", "liblog", "liblp", + "libprotobuf-cpp-lite", "libsparse", "libutils", ], static_libs: [ "libhealthhalutils", + "libsnapshot_nobinder", ], header_libs: [ diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp index dfd569062..905578c4c 100644 --- a/fastboot/device/commands.cpp +++ b/fastboot/device/commands.cpp @@ -19,6 +19,8 @@ #include <sys/socket.h> #include <sys/un.h> +#include <unordered_set> + #include <android-base/logging.h> #include <android-base/parseint.h> #include <android-base/properties.h> @@ -33,6 +35,7 @@ #include <libgsi/libgsi.h> #include <liblp/builder.h> #include <liblp/liblp.h> +#include <libsnapshot/snapshot.h> #include <uuid/uuid.h> #include "constants.h" @@ -48,6 +51,7 @@ using ::android::hardware::boot::V1_0::Slot; using ::android::hardware::boot::V1_1::MergeStatus; using ::android::hardware::fastboot::V1_0::Result; using ::android::hardware::fastboot::V1_0::Status; +using android::snapshot::SnapshotManager; using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl; struct VariableHandlers { @@ -57,6 +61,24 @@ struct VariableHandlers { std::function<std::vector<std::vector<std::string>>(FastbootDevice*)> get_all_args; }; +static bool IsSnapshotUpdateInProgress(FastbootDevice* device) { + auto hal = device->boot1_1(); + if (!hal) { + return false; + } + auto merge_status = hal->getSnapshotMergeStatus(); + return merge_status == MergeStatus::SNAPSHOTTED || merge_status == MergeStatus::MERGING; +} + +static bool IsProtectedPartitionDuringMerge(FastbootDevice* device, const std::string& name) { + static const std::unordered_set<std::string> ProtectedPartitionsDuringMerge = { + "userdata", "metadata", "misc"}; + if (ProtectedPartitionsDuringMerge.count(name) == 0) { + return false; + } + return IsSnapshotUpdateInProgress(device); +} + static void GetAllVars(FastbootDevice* device, const std::string& name, const VariableHandlers& handlers) { if (!handlers.get_all_args) { @@ -142,8 +164,14 @@ bool EraseHandler(FastbootDevice* device, const std::vector<std::string>& args) return device->WriteStatus(FastbootResult::FAIL, "Erase is not allowed on locked devices"); } + const auto& partition_name = args[1]; + if (IsProtectedPartitionDuringMerge(device, partition_name)) { + auto message = "Cannot erase " + partition_name + " while a snapshot update is in progress"; + return device->WriteFail(message); + } + PartitionHandle handle; - if (!OpenPartition(device, args[1], &handle)) { + if (!OpenPartition(device, partition_name, &handle)) { return device->WriteStatus(FastbootResult::FAIL, "Partition doesn't exist"); } if (wipe_block_device(handle.fd(), get_block_device_size(handle.fd())) == 0) { @@ -208,9 +236,9 @@ bool SetActiveHandler(FastbootDevice* device, const std::vector<std::string>& ar "set_active command is not allowed on locked devices"); } - // Slot suffix needs to be between 'a' and 'z'. Slot slot; if (!GetSlotNumber(args[1], &slot)) { + // Slot suffix needs to be between 'a' and 'z'. return device->WriteStatus(FastbootResult::FAIL, "Bad slot suffix"); } @@ -223,6 +251,32 @@ bool SetActiveHandler(FastbootDevice* device, const std::vector<std::string>& ar if (slot >= boot_control_hal->getNumberSlots()) { return device->WriteStatus(FastbootResult::FAIL, "Slot out of range"); } + + // If the slot is not changing, do nothing. + if (slot == boot_control_hal->getCurrentSlot()) { + return device->WriteOkay(""); + } + + // Check how to handle the current snapshot state. + if (auto hal11 = device->boot1_1()) { + auto merge_status = hal11->getSnapshotMergeStatus(); + if (merge_status == MergeStatus::MERGING) { + return device->WriteFail("Cannot change slots while a snapshot update is in progress"); + } + // Note: we allow the slot change if the state is SNAPSHOTTED. First- + // stage init does not have access to the HAL, and uses the slot number + // and /metadata OTA state to determine whether a slot change occurred. + // Booting into the old slot would erase the OTA, and switching A->B->A + // would simply resume it if no boots occur in between. Re-flashing + // partitions implicitly cancels the OTA, so leaving the state as-is is + // safe. + if (merge_status == MergeStatus::SNAPSHOTTED) { + device->WriteInfo( + "Changing the active slot with a snapshot applied may cancel the" + " update."); + } + } + CommandResult ret; auto cb = [&ret](CommandResult result) { ret = result; }; auto result = boot_control_hal->setActiveBootSlot(slot, cb); @@ -466,6 +520,11 @@ bool FlashHandler(FastbootDevice* device, const std::vector<std::string>& args) } const auto& partition_name = args[1]; + if (IsProtectedPartitionDuringMerge(device, partition_name)) { + auto message = "Cannot flash " + partition_name + " while a snapshot update is in progress"; + return device->WriteFail(message); + } + if (LogicalPartitionExists(device, partition_name)) { CancelPartitionSnapshot(device, partition_name); } @@ -555,12 +614,9 @@ bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args) { bool SnapshotUpdateHandler(FastbootDevice* device, const std::vector<std::string>& args) { // Note that we use the HAL rather than mounting /metadata, since we want // our results to match the bootloader. - auto hal = device->boot_control_hal(); + auto hal = device->boot1_1(); if (!hal) return device->WriteFail("Not supported"); - android::sp<IBootControl1_1> hal11 = IBootControl1_1::castFrom(hal); - if (!hal11) return device->WriteFail("Not supported"); - // If no arguments, return the same thing as a getvar. Note that we get the // HAL first so we can return "not supported" before we return the less // specific error message below. @@ -573,18 +629,34 @@ bool SnapshotUpdateHandler(FastbootDevice* device, const std::vector<std::string return device->WriteOkay(""); } - if (args.size() != 2 || args[1] != "cancel") { + MergeStatus status = hal->getSnapshotMergeStatus(); + + if (args.size() != 2) { return device->WriteFail("Invalid arguments"); } + if (args[1] == "cancel") { + switch (status) { + case MergeStatus::SNAPSHOTTED: + case MergeStatus::MERGING: + hal->setSnapshotMergeStatus(MergeStatus::CANCELLED); + break; + default: + break; + } + } else if (args[1] == "merge") { + if (status != MergeStatus::MERGING) { + return device->WriteFail("No snapshot merge is in progress"); + } - MergeStatus status = hal11->getSnapshotMergeStatus(); - switch (status) { - case MergeStatus::SNAPSHOTTED: - case MergeStatus::MERGING: - hal11->setSnapshotMergeStatus(MergeStatus::CANCELLED); - break; - default: - break; + auto sm = SnapshotManager::NewForFirstStageMount(); + if (!sm) { + return device->WriteFail("Unable to create SnapshotManager"); + } + if (!sm->HandleImminentDataWipe()) { + return device->WriteFail("Unable to finish snapshot merge"); + } + } else { + return device->WriteFail("Invalid parameter to snapshot-update"); } return device->WriteStatus(FastbootResult::OKAY, "Success"); } diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp index d3c2bdaeb..31fc35937 100644 --- a/fastboot/device/fastboot_device.cpp +++ b/fastboot/device/fastboot_device.cpp @@ -60,7 +60,11 @@ FastbootDevice::FastbootDevice() boot_control_hal_(IBootControl::getService()), health_hal_(get_health_service()), fastboot_hal_(IFastboot::getService()), - active_slot_("") {} + active_slot_("") { + if (boot_control_hal_) { + boot1_1_ = android::hardware::boot::V1_1::IBootControl::castFrom(boot_control_hal_); + } +} FastbootDevice::~FastbootDevice() { CloseDevice(); diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h index 091aadfd3..bbe817244 100644 --- a/fastboot/device/fastboot_device.h +++ b/fastboot/device/fastboot_device.h @@ -23,6 +23,7 @@ #include <vector> #include <android/hardware/boot/1.0/IBootControl.h> +#include <android/hardware/boot/1.1/IBootControl.h> #include <android/hardware/fastboot/1.0/IFastboot.h> #include <android/hardware/health/2.0/IHealth.h> @@ -51,6 +52,7 @@ class FastbootDevice { android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal() { return boot_control_hal_; } + android::sp<android::hardware::boot::V1_1::IBootControl> boot1_1() { return boot1_1_; } android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal() { return fastboot_hal_; } @@ -63,6 +65,7 @@ class FastbootDevice { std::unique_ptr<Transport> transport_; android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal_; + android::sp<android::hardware::boot::V1_1::IBootControl> boot1_1_; android::sp<android::hardware::health::V2_0::IHealth> health_hal_; android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal_; std::vector<char> download_data_; diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp index 6e613d647..91fc84c47 100644 --- a/fastboot/device/variables.cpp +++ b/fastboot/device/variables.cpp @@ -432,19 +432,13 @@ bool GetSnapshotUpdateStatus(FastbootDevice* device, const std::vector<std::stri std::string* message) { // Note that we use the HAL rather than mounting /metadata, since we want // our results to match the bootloader. - auto hal = device->boot_control_hal(); + auto hal = device->boot1_1(); if (!hal) { *message = "not supported"; return false; } - android::sp<IBootControl1_1> hal11 = IBootControl1_1::castFrom(hal); - if (!hal11) { - *message = "not supported"; - return false; - } - - MergeStatus status = hal11->getSnapshotMergeStatus(); + MergeStatus status = hal->getSnapshotMergeStatus(); switch (status) { case MergeStatus::SNAPSHOTTED: *message = "snapshotted"; diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp index 7ce7c7c0e..cbd42b17e 100644 --- a/fastboot/fastboot.cpp +++ b/fastboot/fastboot.cpp @@ -399,6 +399,9 @@ static int show_help() { " snapshot-update cancel On devices that support snapshot-based updates, cancel\n" " an in-progress update. This may make the device\n" " unbootable until it is reflashed.\n" + " snapshot-update merge On devices that support snapshot-based updates, finish\n" + " an in-progress update if it is in the \"merging\"\n" + " phase.\n" "\n" "boot image:\n" " boot KERNEL [RAMDISK [SECOND]]\n" @@ -2089,8 +2092,8 @@ int FastBootTool::Main(int argc, char* argv[]) { if (!args.empty()) { arg = next_arg(&args); } - if (!arg.empty() && arg != "cancel") { - syntax_error("expected: snapshot-update [cancel]"); + if (!arg.empty() && (arg != "cancel" && arg != "merge")) { + syntax_error("expected: snapshot-update [cancel|merge]"); } fb->SnapshotUpdateCommand(arg); } else { diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp index 6a5ad206a..8d534ea32 100644 --- a/fastboot/fastboot_driver.cpp +++ b/fastboot/fastboot_driver.cpp @@ -124,8 +124,11 @@ RetCode FastBootDriver::SetActive(const std::string& slot, std::string* response RetCode FastBootDriver::SnapshotUpdateCommand(const std::string& command, std::string* response, std::vector<std::string>* info) { + prolog_(StringPrintf("Snapshot %s", command.c_str())); std::string raw = FB_CMD_SNAPSHOT_UPDATE ":" + command; - return RawCommand(raw, response, info); + auto result = RawCommand(raw, response, info); + epilog_(result); + return result; } RetCode FastBootDriver::FlashPartition(const std::string& partition, diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h index 8f247098f..7411e5a01 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h @@ -224,7 +224,8 @@ class SnapshotManager final { bool CreateLogicalAndSnapshotPartitions(const std::string& super_device); // This method should be called preceding any wipe or flash of metadata or - // userdata. It is only valid in recovery. + // userdata. It is only valid in recovery or fastbootd, and it ensures that + // a merge has been completed. // // When userdata will be wiped or flashed, it is necessary to clean up any // snapshot state. If a merge is in progress, the merge must be finished. diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp index 1de700861..008ece722 100644 --- a/fs_mgr/libsnapshot/snapshot.cpp +++ b/fs_mgr/libsnapshot/snapshot.cpp @@ -2185,6 +2185,15 @@ bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callba return true; } + // Check this early, so we don't accidentally start trying to populate + // the state file in recovery. Note we don't call GetUpdateState since + // we want errors in acquiring the lock to be propagated, instead of + // returning UpdateState::None. + auto state_file = GetStateFilePath(); + if (access(state_file.c_str(), F_OK) != 0 && errno == ENOENT) { + return true; + } + auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix()); auto super_path = device_->GetSuperDevice(slot_number); if (!CreateLogicalAndSnapshotPartitions(super_path)) {