fastbootd: Disallow certain operations during snapshot updates.
When a snapshot is applied or is merging, requests to erase or flash userdata, metadata, or misc must be protected. In addition, the set_active command must be restricted when a merge is in progress. In addition, introduce a "snapshot-update merge" command for assisting with erase requests when a merge is in progress. As in recovery, this will force a merge to complete. Bug: 139154945 Test: apply update fastboot erase userdata fastboot erase metadata fastboot erase misc fastboot set_active Change-Id: I152446464335c62c39ffb4cc6366be9de19eac30
This commit is contained in:
parent
b58df7dac3
commit
220ddb1f0f
9 changed files with 119 additions and 28 deletions
|
@ -137,12 +137,14 @@ cc_binary {
|
|||
"libhidlbase",
|
||||
"liblog",
|
||||
"liblp",
|
||||
"libprotobuf-cpp-lite",
|
||||
"libsparse",
|
||||
"libutils",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libhealthhalutils",
|
||||
"libsnapshot_nobinder",
|
||||
],
|
||||
|
||||
header_libs: [
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)) {
|
||||
|
|
Loading…
Reference in a new issue