From 12211d163e93c655f56ce8023f9ef05597d777d2 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 24 Jul 2018 15:21:20 -0700 Subject: [PATCH 1/2] fastbootd: Enable erase and flash commands for physical partitions. Bug: 78793464 Test: adb reboot fastboot && fastboot flashall Change-Id: Ibe802c36f6efe20111a2315616ef34d3a027950f --- fastboot/Android.bp | 1 + fastboot/device/commands.cpp | 34 +++++++++- fastboot/device/commands.h | 2 + fastboot/device/fastboot_device.cpp | 3 + fastboot/device/fastboot_device.h | 5 +- fastboot/device/flashing.cpp | 102 ++++++++++++++++++++++++++++ fastboot/device/flashing.h | 24 +++++++ fastboot/device/utility.cpp | 37 ++++++++++ fastboot/device/utility.h | 24 +++++++ fastboot/device/variables.cpp | 15 ++++ fastboot/device/variables.h | 1 + 11 files changed, 241 insertions(+), 7 deletions(-) create mode 100644 fastboot/device/flashing.cpp create mode 100644 fastboot/device/flashing.h diff --git a/fastboot/Android.bp b/fastboot/Android.bp index e9bb1d7af..19f639063 100644 --- a/fastboot/Android.bp +++ b/fastboot/Android.bp @@ -94,6 +94,7 @@ cc_binary { srcs: [ "device/commands.cpp", "device/fastboot_device.cpp", + "device/flashing.cpp", "device/main.cpp", "device/usb_client.cpp", "device/utility.cpp", diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp index 0e4a68b20..7eaefe6de 100644 --- a/fastboot/device/commands.cpp +++ b/fastboot/device/commands.cpp @@ -26,9 +26,11 @@ #include #include #include +#include #include "constants.h" #include "fastboot_device.h" +#include "flashing.h" #include "utility.h" using ::android::hardware::hidl_string; @@ -51,7 +53,8 @@ bool GetVarHandler(FastbootDevice* device, const std::vector& args) {FB_VAR_SLOT_COUNT, GetSlotCount}, {FB_VAR_HAS_SLOT, GetHasSlot}, {FB_VAR_SLOT_SUCCESSFUL, GetSlotSuccessful}, - {FB_VAR_SLOT_UNBOOTABLE, GetSlotUnbootable}}; + {FB_VAR_SLOT_UNBOOTABLE, GetSlotUnbootable}, + {FB_VAR_PARTITION_SIZE, GetPartitionSize}}; // args[0] is command name, args[1] is variable. auto found_variable = kVariableMap.find(args[1]); @@ -63,6 +66,20 @@ bool GetVarHandler(FastbootDevice* device, const std::vector& args) return found_variable->second(device, getvar_args); } +bool EraseHandler(FastbootDevice* device, const std::vector& args) { + if (args.size() < 2) { + return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments"); + } + PartitionHandle handle; + if (!OpenPartition(device, args[1], &handle)) { + return device->WriteStatus(FastbootResult::FAIL, "Partition doesn't exist"); + } + if (wipe_block_device(handle.fd(), get_block_device_size(handle.fd())) == 0) { + return device->WriteStatus(FastbootResult::OKAY, "Erasing succeeded"); + } + return device->WriteStatus(FastbootResult::FAIL, "Erasing failed"); +} + bool DownloadHandler(FastbootDevice* device, const std::vector& args) { if (args.size() < 2) { return device->WriteStatus(FastbootResult::FAIL, "size argument unspecified"); @@ -72,12 +89,12 @@ bool DownloadHandler(FastbootDevice* device, const std::vector& arg if (!android::base::ParseUint("0x" + args[1], &size, UINT_MAX)) { return device->WriteStatus(FastbootResult::FAIL, "Invalid size"); } - device->get_download_data().resize(size); + device->download_data().resize(size); if (!device->WriteStatus(FastbootResult::DATA, android::base::StringPrintf("%08x", size))) { return false; } - if (device->HandleData(true, &device->get_download_data())) { + if (device->HandleData(true, &device->download_data())) { return device->WriteStatus(FastbootResult::OKAY, ""); } @@ -85,6 +102,17 @@ bool DownloadHandler(FastbootDevice* device, const std::vector& arg return device->WriteStatus(FastbootResult::FAIL, "Couldn't download data"); } +bool FlashHandler(FastbootDevice* device, const std::vector& args) { + if (args.size() < 2) { + return device->WriteStatus(FastbootResult::FAIL, "Invalid arguments"); + } + int ret = Flash(device, args[1]); + if (ret < 0) { + return device->WriteStatus(FastbootResult::FAIL, strerror(-ret)); + } + return device->WriteStatus(FastbootResult::OKAY, "Flashing succeeded"); +} + bool SetActiveHandler(FastbootDevice* device, const std::vector& args) { if (args.size() < 2) { return device->WriteStatus(FastbootResult::FAIL, "Missing slot argument"); diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h index 8785b917a..830eb554e 100644 --- a/fastboot/device/commands.h +++ b/fastboot/device/commands.h @@ -39,3 +39,5 @@ bool RebootBootloaderHandler(FastbootDevice* device, const std::vector& args); bool RebootRecoveryHandler(FastbootDevice* device, const std::vector& args); bool GetVarHandler(FastbootDevice* device, const std::vector& args); +bool EraseHandler(FastbootDevice* device, const std::vector& args); +bool FlashHandler(FastbootDevice* device, const std::vector& args); diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp index a225bf80d..b94fbb0b1 100644 --- a/fastboot/device/fastboot_device.cpp +++ b/fastboot/device/fastboot_device.cpp @@ -23,6 +23,7 @@ #include #include "constants.h" +#include "flashing.h" #include "usb_client.h" using ::android::hardware::hidl_string; @@ -40,6 +41,8 @@ FastbootDevice::FastbootDevice() {FB_CMD_REBOOT_BOOTLOADER, RebootBootloaderHandler}, {FB_CMD_REBOOT_FASTBOOT, RebootFastbootHandler}, {FB_CMD_REBOOT_RECOVERY, RebootRecoveryHandler}, + {FB_CMD_ERASE, EraseHandler}, + {FB_CMD_FLASH, FlashHandler}, }), transport_(std::make_unique()), boot_control_hal_(IBootControl::getService()) {} diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h index 75171206f..addc2eff7 100644 --- a/fastboot/device/fastboot_device.h +++ b/fastboot/device/fastboot_device.h @@ -43,9 +43,7 @@ class FastbootDevice { bool WriteOkay(const std::string& message); bool WriteFail(const std::string& message); - std::vector& get_download_data() { return download_data_; } - void set_upload_data(const std::vector& data) { upload_data_ = data; } - void set_upload_data(std::vector&& data) { upload_data_ = std::move(data); } + std::vector& download_data() { return download_data_; } Transport* get_transport() { return transport_.get(); } android::sp boot_control_hal() { return boot_control_hal_; @@ -57,5 +55,4 @@ class FastbootDevice { std::unique_ptr transport_; android::sp boot_control_hal_; std::vector download_data_; - std::vector upload_data_; }; diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp new file mode 100644 index 000000000..d3dd82c92 --- /dev/null +++ b/fastboot/device/flashing.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "flashing.h" + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "fastboot_device.h" +#include "utility.h" + +namespace { + +constexpr uint32_t SPARSE_HEADER_MAGIC = 0xed26ff3a; + +} // namespace + +int FlashRawDataChunk(int fd, const char* data, size_t len) { + size_t ret = 0; + while (ret < len) { + int this_len = std::min(static_cast(1048576UL * 8), len - ret); + int this_ret = write(fd, data, this_len); + if (this_ret < 0) { + PLOG(ERROR) << "Failed to flash data of len " << len; + return -1; + } + data += this_ret; + ret += this_ret; + } + return 0; +} + +int FlashRawData(int fd, const std::vector& downloaded_data) { + int ret = FlashRawDataChunk(fd, downloaded_data.data(), downloaded_data.size()); + if (ret < 0) { + return -errno; + } + return ret; +} + +int WriteCallback(void* priv, const void* data, size_t len) { + int fd = reinterpret_cast(priv); + if (!data) { + return lseek64(fd, len, SEEK_CUR) >= 0 ? 0 : -errno; + } + return FlashRawDataChunk(fd, reinterpret_cast(data), len); +} + +int FlashSparseData(int fd, std::vector& downloaded_data) { + struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(), true, false); + if (!file) { + return -ENOENT; + } + return sparse_file_callback(file, false, false, WriteCallback, reinterpret_cast(fd)); +} + +int FlashBlockDevice(int fd, std::vector& downloaded_data) { + lseek64(fd, 0, SEEK_SET); + if (downloaded_data.size() >= sizeof(SPARSE_HEADER_MAGIC) && + *reinterpret_cast(downloaded_data.data()) == SPARSE_HEADER_MAGIC) { + return FlashSparseData(fd, downloaded_data); + } else { + return FlashRawData(fd, downloaded_data); + } +} + +int Flash(FastbootDevice* device, const std::string& partition_name) { + PartitionHandle handle; + if (!OpenPartition(device, partition_name, &handle)) { + return -ENOENT; + } + + std::vector data = std::move(device->download_data()); + if (data.size() == 0) { + return -EINVAL; + } else if (data.size() > get_block_device_size(handle.fd())) { + return -EOVERFLOW; + } + return FlashBlockDevice(handle.fd(), data); +} diff --git a/fastboot/device/flashing.h b/fastboot/device/flashing.h new file mode 100644 index 000000000..206a4076e --- /dev/null +++ b/fastboot/device/flashing.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +class FastbootDevice; + +int Flash(FastbootDevice* device, const std::string& partition_name); diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp index c8d2b3eb1..73cf1bff1 100644 --- a/fastboot/device/utility.cpp +++ b/fastboot/device/utility.cpp @@ -16,8 +16,45 @@ #include "utility.h" +#include + +#include "fastboot_device.h" + +using android::base::unique_fd; using android::hardware::boot::V1_0::Slot; +static bool OpenPhysicalPartition(const std::string& name, PartitionHandle* handle) { + std::optional path = FindPhysicalPartition(name); + if (!path) { + return false; + } + *handle = PartitionHandle(*path); + return true; +} + +bool OpenPartition(FastbootDevice* /* device */, const std::string& name, PartitionHandle* handle) { + if (!OpenPhysicalPartition(name, handle)) { + LOG(ERROR) << "No such partition: " << name; + return false; + } + + unique_fd fd(TEMP_FAILURE_RETRY(open(handle->path().c_str(), O_WRONLY | O_EXCL))); + if (fd < 0) { + PLOG(ERROR) << "Failed to open block device: " << handle->path(); + return false; + } + handle->set_fd(std::move(fd)); + return true; +} + +std::optional FindPhysicalPartition(const std::string& name) { + std::string path = "/dev/block/by-name/" + name; + if (access(path.c_str(), R_OK | W_OK) < 0) { + return {}; + } + return path; +} + bool GetSlotNumber(const std::string& slot, Slot* number) { if (slot.size() != 1) { return false; diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h index 867d693ba..26f486b7b 100644 --- a/fastboot/device/utility.h +++ b/fastboot/device/utility.h @@ -15,8 +15,32 @@ */ #pragma once +#include #include +#include #include +// Logical partitions are only mapped to a block device as needed, and +// immediately unmapped when no longer needed. In order to enforce this we +// require accessing partitions through a Handle abstraction, which may perform +// additional operations after closing its file descriptor. +class PartitionHandle { + public: + PartitionHandle() {} + explicit PartitionHandle(const std::string& path) : path_(path) {} + const std::string& path() const { return path_; } + int fd() const { return fd_.get(); } + void set_fd(android::base::unique_fd&& fd) { fd_ = std::move(fd); } + + private: + std::string path_; + android::base::unique_fd fd_; +}; + +class FastbootDevice; + +std::optional FindPhysicalPartition(const std::string& name); +bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle); + bool GetSlotNumber(const std::string& slot, android::hardware::boot::V1_0::Slot* number); diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp index 33f7f74bd..8f66fea76 100644 --- a/fastboot/device/variables.cpp +++ b/fastboot/device/variables.cpp @@ -16,6 +16,8 @@ #include "variables.h" +#include + #include #include #include @@ -24,6 +26,7 @@ #include #include "fastboot_device.h" +#include "flashing.h" #include "utility.h" using ::android::hardware::boot::V1_0::BoolResult; @@ -125,3 +128,15 @@ bool GetHasSlot(FastbootDevice* device, const std::vector& args) { std::string result = (args[0] == "userdata" ? "no" : "yes"); return device->WriteOkay(result); } + +bool GetPartitionSize(FastbootDevice* device, const std::vector& args) { + if (args.size() < 1) { + return device->WriteFail("Missing argument"); + } + PartitionHandle handle; + if (!OpenPartition(device, args[0], &handle)) { + return device->WriteFail("Could not open partition"); + } + uint64_t size = get_block_device_size(handle.fd()); + return device->WriteOkay(android::base::StringPrintf("%" PRIX64, size)); +} diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h index 45c6dc9cb..88947e0ce 100644 --- a/fastboot/device/variables.h +++ b/fastboot/device/variables.h @@ -34,3 +34,4 @@ bool GetSlotUnbootable(FastbootDevice* device, const std::vector& a bool GetMaxDownloadSize(FastbootDevice* device, const std::vector& args); bool GetUnlocked(FastbootDevice* device, const std::vector& args); bool GetHasSlot(FastbootDevice* device, const std::vector& args); +bool GetPartitionSize(FastbootDevice* device, const std::vector& args); From 88ef0b1f256be1958a9c6c8989165400f1181340 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Thu, 9 Aug 2018 10:40:00 -0700 Subject: [PATCH 2/2] fastbootd: Add support for flashing logical partitions. When flashing logical partitions, we read the "super" partition metadata corresponding to the current slot. We then temporarily create a device-mapper device for that partition, and immediately destroy the device after all operations are complete. We do not mount partitions ahead of time, or keep them mounted, because a fastboot operation may change the layout of the logical partition table (or change which slot is current). Bug: 78793464 Test: fastboot flash a logical partition under "super" Change-Id: Id2a61d3592decabeebfd283c4fd6e6cbe576a18c --- fastboot/Android.bp | 1 + fastboot/device/flashing.cpp | 1 - fastboot/device/utility.cpp | 62 +++++++++++++++++++++++++++++++++-- fastboot/device/utility.h | 14 ++++++++ fastboot/device/variables.cpp | 8 +++++ 5 files changed, 83 insertions(+), 3 deletions(-) diff --git a/fastboot/Android.bp b/fastboot/Android.bp index 19f639063..9995052e4 100644 --- a/fastboot/Android.bp +++ b/fastboot/Android.bp @@ -114,6 +114,7 @@ cc_binary { "libhidltransport", "libhwbinder", "liblog", + "liblp", "libsparse", "libutils", ], diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp index d3dd82c92..b5ed1702c 100644 --- a/fastboot/device/flashing.cpp +++ b/fastboot/device/flashing.cpp @@ -25,7 +25,6 @@ #include #include #include -#include #include #include "fastboot_device.h" diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp index 73cf1bff1..ec84576ce 100644 --- a/fastboot/device/utility.cpp +++ b/fastboot/device/utility.cpp @@ -17,9 +17,12 @@ #include "utility.h" #include +#include +#include #include "fastboot_device.h" +using namespace android::fs_mgr; using android::base::unique_fd; using android::hardware::boot::V1_0::Slot; @@ -32,8 +35,31 @@ static bool OpenPhysicalPartition(const std::string& name, PartitionHandle* hand return true; } -bool OpenPartition(FastbootDevice* /* device */, const std::string& name, PartitionHandle* handle) { - if (!OpenPhysicalPartition(name, handle)) { +static bool OpenLogicalPartition(const std::string& name, const std::string& slot, + PartitionHandle* handle) { + std::optional path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME); + if (!path) { + return false; + } + uint32_t slot_number = SlotNumberForSlotSuffix(slot); + std::string dm_path; + if (!CreateLogicalPartition(path->c_str(), slot_number, name, true, &dm_path)) { + LOG(ERROR) << "Could not map partition: " << name; + return false; + } + auto closer = [name]() -> void { DestroyLogicalPartition(name); }; + *handle = PartitionHandle(dm_path, std::move(closer)); + return true; +} + +bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle) { + // We prioritize logical partitions over physical ones, and do this + // consistently for other partition operations (like getvar:partition-size). + if (LogicalPartitionExists(name, device->GetCurrentSlot())) { + if (!OpenLogicalPartition(name, device->GetCurrentSlot(), handle)) { + return false; + } + } else if (!OpenPhysicalPartition(name, handle)) { LOG(ERROR) << "No such partition: " << name; return false; } @@ -55,6 +81,38 @@ std::optional FindPhysicalPartition(const std::string& name) { return path; } +static const LpMetadataPartition* FindLogicalPartition(const LpMetadata& metadata, + const std::string& name) { + for (const auto& partition : metadata.partitions) { + if (GetPartitionName(partition) == name) { + return &partition; + } + } + return nullptr; +} + +bool LogicalPartitionExists(const std::string& name, const std::string& slot_suffix, + bool* is_zero_length) { + auto path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME); + if (!path) { + return false; + } + + uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix); + std::unique_ptr metadata = ReadMetadata(path->c_str(), slot_number); + if (!metadata) { + return false; + } + const LpMetadataPartition* partition = FindLogicalPartition(*metadata.get(), name); + if (!partition) { + return false; + } + if (is_zero_length) { + *is_zero_length = (partition->num_extents == 0); + } + return true; +} + bool GetSlotNumber(const std::string& slot, Slot* number) { if (slot.size() != 1) { return false; diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h index 26f486b7b..0931fc313 100644 --- a/fastboot/device/utility.h +++ b/fastboot/device/utility.h @@ -29,6 +29,17 @@ class PartitionHandle { public: PartitionHandle() {} explicit PartitionHandle(const std::string& path) : path_(path) {} + PartitionHandle(const std::string& path, std::function&& closer) + : path_(path), closer_(std::move(closer)) {} + PartitionHandle(PartitionHandle&& other) = default; + PartitionHandle& operator=(PartitionHandle&& other) = default; + ~PartitionHandle() { + if (closer_) { + // Make sure the device is closed first. + fd_ = {}; + closer_(); + } + } const std::string& path() const { return path_; } int fd() const { return fd_.get(); } void set_fd(android::base::unique_fd&& fd) { fd_ = std::move(fd); } @@ -36,11 +47,14 @@ class PartitionHandle { private: std::string path_; android::base::unique_fd fd_; + std::function closer_; }; class FastbootDevice; std::optional FindPhysicalPartition(const std::string& name); +bool LogicalPartitionExists(const std::string& name, const std::string& slot_suffix, + bool* is_zero_length = nullptr); bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle); bool GetSlotNumber(const std::string& slot, android::hardware::boot::V1_0::Slot* number); diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp index 8f66fea76..b51b98535 100644 --- a/fastboot/device/variables.cpp +++ b/fastboot/device/variables.cpp @@ -133,6 +133,14 @@ bool GetPartitionSize(FastbootDevice* device, const std::vector& ar if (args.size() < 1) { return device->WriteFail("Missing argument"); } + // Zero-length partitions cannot be created through device-mapper, so we + // special case them here. + bool is_zero_length; + if (LogicalPartitionExists(args[0], device->GetCurrentSlot(), &is_zero_length) && + is_zero_length) { + return device->WriteOkay("0"); + } + // Otherwise, open the partition as normal. PartitionHandle handle; if (!OpenPartition(device, args[0], &handle)) { return device->WriteFail("Could not open partition");