From 29a211c19402c4c972a3afed9f302ef893bff0e9 Mon Sep 17 00:00:00 2001 From: Daniel Zheng Date: Fri, 10 Mar 2023 07:23:49 +0000 Subject: [PATCH] Changing flashall to parse fastboot-info.txt Test: tested on Raven Change-Id: I9f26bf6025a0a6318f84be929091f4e82bd87556 --- fastboot/fastboot.cpp | 292 +++++++++++++++++++++++++++++++++++++----- fastboot/fastboot.h | 4 +- fastboot/task.cpp | 88 ++++++++++++- fastboot/task.h | 24 +++- 4 files changed, 375 insertions(+), 33 deletions(-) diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp index d7d2091bb..6b0df523e 100644 --- a/fastboot/fastboot.cpp +++ b/fastboot/fastboot.cpp @@ -1284,7 +1284,7 @@ static void flash_buf(const std::string& partition, struct fastboot_buffer* buf, } } -static std::string get_current_slot() { +std::string get_current_slot() { std::string current_slot; if (fb->GetVar("current-slot", ¤t_slot) != fastboot::SUCCESS) return ""; if (current_slot[0] == '_') current_slot.erase(0, 1); @@ -1560,7 +1560,7 @@ static void CancelSnapshotIfNeeded() { } } -std::string GetPartitionName(const ImageEntry& entry, std::string& current_slot) { +std::string GetPartitionName(const ImageEntry& entry, const std::string& current_slot) { auto slot = entry.second; if (slot.empty()) { slot = current_slot; @@ -1574,6 +1574,216 @@ std::string GetPartitionName(const ImageEntry& entry, std::string& current_slot) return entry.first->part_name + "_" + slot; } +std::unique_ptr ParseFlashCommand(const FlashingPlan* fp, + const std::vector& parts) { + bool apply_vbmeta = false; + std::string slot = fp->slot_override; + std::string partition; + std::string img_name; + for (auto& part : parts) { + if (part == "--apply-vbmeta") { + apply_vbmeta = true; + } else if (part == "--slot-other") { + slot = fp->secondary_slot; + } else if (partition.empty()) { + partition = part; + } else if (img_name.empty()) { + img_name = part; + } else { + LOG(ERROR) << "unknown argument" << part + << " in fastboot-info.txt. parts: " << android::base::Join(parts, " "); + return nullptr; + } + } + if (partition.empty()) { + LOG(ERROR) << "partition name not found when parsing fastboot-info.txt. parts: " + << android::base::Join(parts, " "); + return nullptr; + } + if (img_name.empty()) { + img_name = partition + ".img"; + } + return std::make_unique(slot, partition, img_name, apply_vbmeta); +} + +std::unique_ptr ParseRebootCommand(const FlashingPlan* fp, + const std::vector& parts) { + if (parts.empty()) return std::make_unique(fp); + if (parts.size() > 1) { + LOG(ERROR) << "unknown arguments in reboot {target} in fastboot-info.txt: " + << android::base::Join(parts, " "); + return nullptr; + } + return std::make_unique(fp, parts[0]); +} + +std::unique_ptr ParseWipeCommand(const FlashingPlan* fp, + const std::vector& parts) { + if (parts.size() != 1) { + LOG(ERROR) << "unknown arguments in erase {partition} in fastboot-info.txt: " + << android::base::Join(parts, " "); + return nullptr; + } + return std::make_unique(fp, parts[0]); +} + +std::unique_ptr ParseFastbootInfoLine(const FlashingPlan* fp, + const std::vector& command) { + if (command.size() == 0) { + return nullptr; + } + std::unique_ptr task; + + if (command[0] == "flash") { + task = ParseFlashCommand(fp, std::vector{command.begin() + 1, command.end()}); + } else if (command[0] == "reboot") { + task = ParseRebootCommand(fp, std::vector{command.begin() + 1, command.end()}); + } else if (command[0] == "update-super" && command.size() == 1) { + task = std::make_unique(fp); + } else if (command[0] == "erase" && command.size() == 2) { + task = ParseWipeCommand(fp, std::vector{command.begin() + 1, command.end()}); + } + if (!task) { + LOG(ERROR) << "unknown command parsing fastboot-info.txt line: " + << android::base::Join(command, " "); + } + return task; +} + +void AddResizeTasks(const FlashingPlan* fp, std::vector>* tasks) { + // expands "resize-partitions" into individual commands : resize {os_partition_1}, resize + // {os_partition_2}, etc. + std::vector> resize_tasks; + std::optional loc; + for (size_t i = 0; i < tasks->size(); i++) { + if (auto flash_task = tasks->at(i)->AsFlashTask()) { + if (should_flash_in_userspace(flash_task->GetPartitionAndSlot())) { + if (!loc) { + loc = i; + } + resize_tasks.emplace_back(std::make_unique( + fp, flash_task->GetPartition(), "0", fp->slot_override)); + } + } + } + // if no logical partitions (although should never happen since system will always need to be + // flashed) + if (!loc) { + return; + } + tasks->insert(tasks->begin() + loc.value(), std::make_move_iterator(resize_tasks.begin()), + std::make_move_iterator(resize_tasks.end())); + return; +} + +static bool IsNumber(const std::string& s) { + bool period = false; + for (size_t i = 0; i < s.length(); i++) { + if (!isdigit(s[i])) { + if (!period && s[i] == '.' && i != 0 && i != s.length() - 1) { + period = true; + } else { + return false; + } + } + } + return true; +} + +static bool IsIgnore(const std::vector& command) { + if (command[0][0] == '#') { + return true; + } + return false; +} + +bool CheckFastbootInfoRequirements(const std::vector& command) { + if (command.size() != 2) { + LOG(ERROR) << "unknown characters in version info in fastboot-info.txt -> " + << android::base::Join(command, " "); + return false; + } + if (command[0] != "version") { + LOG(ERROR) << "unknown characters in version info in fastboot-info.txt -> " + << android::base::Join(command, " "); + return false; + } + + if (!IsNumber(command[1])) { + LOG(ERROR) << "version number contains non-numeric values in fastboot-info.txt -> " + << android::base::Join(command, " "); + return false; + } + + LOG(VERBOSE) << "Checking 'fastboot-info.txt version'"; + if (command[1] < PLATFORM_TOOLS_VERSION) { + return true; + } + LOG(ERROR) << "fasboot-info.txt version: " << command[1] + << " not compatible with host tool version --> " << PLATFORM_TOOLS_VERSION; + return false; +} + +std::vector> ParseFastbootInfo(const FlashingPlan* fp, + const std::vector& file) { + std::vector> tasks; + // Get os_partitions that need to be resized + for (auto& text : file) { + std::vector command = android::base::Tokenize(text, " "); + if (IsIgnore(command)) { + continue; + } + if (command.size() > 1 && command[0] == "version") { + if (!CheckFastbootInfoRequirements(command)) { + return {}; + } + continue; + } else if (command.size() >= 2 && command[0] == "if-wipe") { + if (!fp->wants_wipe) { + continue; + } + command.erase(command.begin()); + } + auto task = ParseFastbootInfoLine(fp, command); + if (!task) { + LOG(ERROR) << "Error when parsing fastboot-info.txt, falling back on Hardcoded list: " + << text; + return {}; + } + tasks.emplace_back(std::move(task)); + } + if (auto flash_super_task = FlashSuperLayoutTask::InitializeFromTasks(fp, tasks)) { + auto it = tasks.begin(); + for (size_t i = 0; i < tasks.size(); i++) { + if (auto flash_task = tasks[i]->AsFlashTask()) { + if (should_flash_in_userspace(flash_task->GetPartitionAndSlot())) { + break; + } + } + if (auto wipe_task = tasks[i]->AsWipeTask()) { + break; + } + it++; + } + tasks.insert(it, std::move(flash_super_task)); + } else { + AddResizeTasks(fp, &tasks); + } + return tasks; +} + +std::vector> ParseFastbootInfo(const FlashingPlan* fp, std::ifstream& fs) { + if (!fs || fs.eof()) return {}; + + std::string text; + std::vector file; + // Get os_partitions that need to be resized + while (std::getline(fs, text)) { + file.emplace_back(text); + } + return ParseFastbootInfo(fp, file); +} + class FlashAllTool { public: FlashAllTool(FlashingPlan* fp); @@ -1586,6 +1796,7 @@ class FlashAllTool { void CollectImages(); void FlashImages(const std::vector>& images); void FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf); + void HardcodedFlash(); std::vector boot_images_; std::vector os_images_; @@ -1611,38 +1822,23 @@ void FlashAllTool::Flash() { CancelSnapshotIfNeeded(); - // First flash boot partitions. We allow this to happen either in userspace - // or in bootloader fastboot. - FlashImages(boot_images_); - - std::vector> tasks; - - if (auto flash_super_task = FlashSuperLayoutTask::Initialize(fp_, os_images_)) { - tasks.emplace_back(std::move(flash_super_task)); - } else { - // Sync the super partition. This will reboot to userspace fastboot if needed. - tasks.emplace_back(std::make_unique(fp_)); - // Resize any logical partition to 0, so each partition is reset to 0 - // extents, and will achieve more optimal allocation. - for (const auto& [image, slot] : os_images_) { - // Retrofit devices have two super partitions, named super_a and super_b. - // On these devices, secondary slots must be flashed as physical - // partitions (otherwise they would not mount on first boot). To enforce - // this, we delete any logical partitions for the "other" slot. - if (is_retrofit_device()) { - std::string partition_name = image->part_name + "_"s + slot; - if (image->IsSecondary() && is_logical(partition_name)) { - fp_->fb->DeletePartition(partition_name); - } - tasks.emplace_back(std::make_unique(fp_, partition_name)); - } - tasks.emplace_back(std::make_unique(fp_, image->part_name, "0", slot)); - } + std::string path = find_item_given_name("fastboot-info.txt"); + std::ifstream stream(path); + std::vector> tasks = ParseFastbootInfo(fp_, stream); + if (tasks.empty()) { + LOG(VERBOSE) << "Flashing from hardcoded images. fastboot-info.txt is empty or does not " + "exist"; + HardcodedFlash(); + return; } + LOG(VERBOSE) << "Flashing from fastboot-info.txt"; for (auto& task : tasks) { task->Run(); } - FlashImages(os_images_); + if (fp_->wants_wipe) { + // avoid adding duplicate wipe tasks in fastboot main code. + fp_->wants_wipe = false; + } } void FlashAllTool::CheckRequirements() { @@ -1693,6 +1889,42 @@ void FlashAllTool::CollectImages() { } } +void FlashAllTool::HardcodedFlash() { + CollectImages(); + // First flash boot partitions. We allow this to happen either in userspace + // or in bootloader fastboot. + FlashImages(boot_images_); + + std::vector> tasks; + + if (auto flash_super_task = FlashSuperLayoutTask::Initialize(fp_, os_images_)) { + tasks.emplace_back(std::move(flash_super_task)); + } else { + // Sync the super partition. This will reboot to userspace fastboot if needed. + tasks.emplace_back(std::make_unique(fp_)); + // Resize any logical partition to 0, so each partition is reset to 0 + // extents, and will achieve more optimal allocation. + for (const auto& [image, slot] : os_images_) { + // Retrofit devices have two super partitions, named super_a and super_b. + // On these devices, secondary slots must be flashed as physical + // partitions (otherwise they would not mount on first boot). To enforce + // this, we delete any logical partitions for the "other" slot. + if (is_retrofit_device()) { + std::string partition_name = image->part_name + "_"s + slot; + if (image->IsSecondary() && should_flash_in_userspace(partition_name)) { + fp_->fb->DeletePartition(partition_name); + } + tasks.emplace_back(std::make_unique(fp_, partition_name)); + } + tasks.emplace_back(std::make_unique(fp_, image->part_name, "0", slot)); + } + } + for (auto& i : tasks) { + i->Run(); + } + FlashImages(os_images_); +} + void FlashAllTool::FlashImages(const std::vector>& images) { for (const auto& [image, slot] : images) { fastboot_buffer buf; diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h index 6462a4fed..7c44bd86b 100644 --- a/fastboot/fastboot.h +++ b/fastboot/fastboot.h @@ -83,6 +83,7 @@ struct FlashingPlan { std::string slot_override; std::string current_slot; std::string secondary_slot; + fastboot::FastBootDriver* fb; }; @@ -94,6 +95,7 @@ void do_for_partitions(const std::string& part, const std::string& slot, std::string find_item(const std::string& item); void reboot_to_userspace_fastboot(); void syntax_error(const char* fmt, ...); +std::string get_current_slot(); struct NetworkSerial { Socket::Protocol protocol; @@ -103,7 +105,7 @@ struct NetworkSerial { Result ParseNetworkSerial(const std::string& serial); bool supports_AB(); -std::string GetPartitionName(const ImageEntry& entry, std::string& current_slot_); +std::string GetPartitionName(const ImageEntry& entry, const std::string& current_slot_); void flash_partition_files(const std::string& partition, const std::vector& files); int64_t get_sparse_limit(int64_t size); std::vector resparse_file(sparse_file* s, int64_t max_size); diff --git a/fastboot/task.cpp b/fastboot/task.cpp index d43fd8084..054c1edba 100644 --- a/fastboot/task.cpp +++ b/fastboot/task.cpp @@ -23,6 +23,7 @@ #include "fastboot.h" #include "filesystem.h" #include "super_flash_helper.h" +#include "util.h" using namespace std::string_literals; FlashTask::FlashTask(const std::string& _slot, const std::string& _pname, const std::string& _fname, @@ -45,6 +46,20 @@ void FlashTask::Run() { do_for_partitions(pname_, slot_, flash, true); } +std::string FlashTask::GetPartitionAndSlot() { + auto slot = slot_; + if (slot.empty()) { + slot = get_current_slot(); + } + if (slot.empty()) { + return pname_; + } + if (slot == "all") { + LOG(FATAL) << "Cannot retrieve a singular name when using all slots"; + } + return pname_ + "_" + slot; +} + RebootTask::RebootTask(const FlashingPlan* fp) : fp_(fp){}; RebootTask::RebootTask(const FlashingPlan* fp, const std::string& reboot_target) : reboot_target_(reboot_target), fp_(fp){}; @@ -93,7 +108,7 @@ void FlashSuperLayoutTask::Run() { } std::unique_ptr FlashSuperLayoutTask::Initialize( - FlashingPlan* fp, std::vector& os_images) { + const FlashingPlan* fp, std::vector& os_images) { if (!supports_AB()) { LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device"; return nullptr; @@ -156,6 +171,77 @@ std::unique_ptr FlashSuperLayoutTask::Initialize( partition_size); } +std::unique_ptr FlashSuperLayoutTask::InitializeFromTasks( + const FlashingPlan* fp, std::vector>& tasks) { + if (!supports_AB()) { + LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device"; + return nullptr; + } + if (fp->slot_override == "all") { + LOG(VERBOSE) << "Cannot optimize flashing super for all slots"; + return nullptr; + } + + // Does this device use dynamic partitions at all? + unique_fd fd = fp->source->OpenFile("super_empty.img"); + + if (fd < 0) { + LOG(VERBOSE) << "could not open super_empty.img"; + return nullptr; + } + + std::string super_name; + // Try to find whether there is a super partition. + if (fp->fb->GetVar("super-partition-name", &super_name) != fastboot::SUCCESS) { + super_name = "super"; + } + uint64_t partition_size; + std::string partition_size_str; + if (fp->fb->GetVar("partition-size:" + super_name, &partition_size_str) != fastboot::SUCCESS) { + LOG(VERBOSE) << "Cannot optimize super flashing: could not determine super partition"; + return nullptr; + } + partition_size_str = fb_fix_numeric_var(partition_size_str); + if (!android::base::ParseUint(partition_size_str, &partition_size)) { + LOG(VERBOSE) << "Could not parse " << super_name << " size: " << partition_size_str; + return nullptr; + } + + std::unique_ptr helper = std::make_unique(*fp->source); + if (!helper->Open(fd)) { + return nullptr; + } + + for (const auto& task : tasks) { + if (auto flash_task = task->AsFlashTask()) { + if (should_flash_in_userspace(flash_task->GetPartitionAndSlot())) { + auto partition = flash_task->GetPartitionAndSlot(); + if (!helper->AddPartition(partition, flash_task->GetImageName(), false)) { + return nullptr; + } + } + } + } + + auto s = helper->GetSparseLayout(); + if (!s) return nullptr; + // Remove images that we already flashed, just in case we have non-dynamic OS images. + auto remove_if_callback = [&](const auto& task) -> bool { + if (auto flash_task = task->AsFlashTask()) { + return helper->WillFlash(flash_task->GetPartitionAndSlot()); + } else if (auto update_super_task = task->AsUpdateSuperTask()) { + return true; + } else if (auto reboot_task = task->AsRebootTask()) { + return true; + } + return false; + }; + tasks.erase(std::remove_if(tasks.begin(), tasks.end(), remove_if_callback), tasks.end()); + + return std::make_unique(super_name, std::move(helper), std::move(s), + partition_size); +} + UpdateSuperTask::UpdateSuperTask(const FlashingPlan* fp) : fp_(fp) {} void UpdateSuperTask::Run() { diff --git a/fastboot/task.h b/fastboot/task.h index 801a0f6bf..34e3e92aa 100644 --- a/fastboot/task.h +++ b/fastboot/task.h @@ -26,10 +26,20 @@ struct FlashingPlan; struct Image; using ImageEntry = std::pair; +class FlashTask; +class RebootTask; +class UpdateSuperTask; +class WipeTask; + class Task { public: Task() = default; virtual void Run() = 0; + virtual FlashTask* AsFlashTask() { return nullptr; } + virtual RebootTask* AsRebootTask() { return nullptr; } + virtual UpdateSuperTask* AsUpdateSuperTask() { return nullptr; } + virtual WipeTask* AsWipeTask() { return nullptr; } + virtual ~Task() = default; }; @@ -37,7 +47,12 @@ class FlashTask : public Task { public: FlashTask(const std::string& slot, const std::string& pname, const std::string& fname, const bool apply_vbmeta); + virtual FlashTask* AsFlashTask() override { return this; } + std::string GetPartition() { return pname_; } + std::string GetImageName() { return fname_; } + std::string GetPartitionAndSlot(); + std::string GetSlot() { return slot_; } void Run() override; private: @@ -51,6 +66,7 @@ class RebootTask : public Task { public: RebootTask(const FlashingPlan* fp); RebootTask(const FlashingPlan* fp, const std::string& reboot_target); + virtual RebootTask* AsRebootTask() override { return this; } void Run() override; private: @@ -62,8 +78,10 @@ class FlashSuperLayoutTask : public Task { public: FlashSuperLayoutTask(const std::string& super_name, std::unique_ptr helper, SparsePtr sparse_layout, uint64_t super_size); - static std::unique_ptr Initialize(FlashingPlan* fp, + static std::unique_ptr Initialize(const FlashingPlan* fp, std::vector& os_images); + static std::unique_ptr InitializeFromTasks( + const FlashingPlan* fp, std::vector>& tasks); using ImageEntry = std::pair; void Run() override; @@ -77,6 +95,8 @@ class FlashSuperLayoutTask : public Task { class UpdateSuperTask : public Task { public: UpdateSuperTask(const FlashingPlan* fp); + virtual UpdateSuperTask* AsUpdateSuperTask() override { return this; } + void Run() override; private: @@ -109,6 +129,8 @@ class DeleteTask : public Task { class WipeTask : public Task { public: WipeTask(const FlashingPlan* fp, const std::string& pname); + virtual WipeTask* AsWipeTask() override { return this; } + void Run() override; private: