diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp index 331b0c805..293ff00e8 100644 --- a/fastboot/fastboot.cpp +++ b/fastboot/fastboot.cpp @@ -574,6 +574,7 @@ static int unzip_to_file(ZipArchiveHandle zip, const char* entry_name) { ZipEntry zip_entry; if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) { fprintf(stderr, "archive does not contain '%s'\n", entry_name); + errno = ENOENT; return -1; } @@ -1033,14 +1034,6 @@ static void do_flash(const char* pname, const char* fname) { flash_buf(pname, &buf); } -static void do_update_signature(ZipArchiveHandle zip, const char* filename) { - int64_t sz; - void* data = unzip_to_memory(zip, filename, &sz); - if (data == nullptr) return; - fb_queue_download("signature", data, sz); - fb_queue_command("signature", "installing signature"); -} - // Sets slot_override as the active slot. If slot_override is blank, // set current slot as active instead. This clears slot-unbootable. static void set_active(const std::string& slot_override) { @@ -1080,96 +1073,6 @@ static bool if_partition_exists(const std::string& partition, const std::string& return fb_getvar("partition-size:" + partition_name, &partition_size); } -static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary) { - queue_info_dump(); - - fb_queue_query_save("product", cur_product, sizeof(cur_product)); - - ZipArchiveHandle zip; - int error = OpenArchive(filename, &zip); - if (error != 0) { - die("failed to open zip file '%s': %s", filename, ErrorCodeString(error)); - } - - int64_t sz; - void* data = unzip_to_memory(zip, "android-info.txt", &sz); - if (data == nullptr) { - die("update package '%s' has no android-info.txt", filename); - } - - check_requirements(reinterpret_cast(data), sz); - - std::string secondary; - if (!skip_secondary) { - if (slot_override != "") { - secondary = get_other_slot(slot_override); - } else { - secondary = get_other_slot(); - } - if (secondary == "") { - if (supports_AB()) { - fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n"); - } - skip_secondary = true; - } - } - for (size_t i = 0; i < arraysize(images); ++i) { - const char* slot = slot_override.c_str(); - if (images[i].IsSecondary()) { - if (!skip_secondary) { - slot = secondary.c_str(); - } else { - continue; - } - } - - int fd = unzip_to_file(zip, images[i].img_name); - if (fd == -1) { - if (images[i].optional_if_no_image) { - continue; // An optional file is missing, so ignore it. - } - die("non-optional file %s missing", images[i].img_name); - } - - fastboot_buffer buf; - if (!load_buf_fd(fd, &buf)) { - die("cannot load %s from flash: %s", images[i].img_name, strerror(errno)); - } - - auto update = [&](const std::string& partition) { - do_update_signature(zip, images[i].sig_name); - flash_buf(partition.c_str(), &buf); - /* not closing the fd here since the sparse code keeps the fd around - * but hasn't mmaped data yet. The temporary file will get cleaned up when the - * program exits. - */ - }; - do_for_partitions(images[i].part_name, slot, update, false); - } - - if (slot_override == "all") { - set_active("a"); - } else { - set_active(slot_override); - } - - CloseArchive(zip); -} - -static void do_send_signature(const std::string& fn) { - std::size_t extension_loc = fn.find(".img"); - if (extension_loc == std::string::npos) return; - - std::string fs_sig = fn.substr(0, extension_loc) + ".sig"; - - int64_t sz; - void* data = load_file(fs_sig.c_str(), &sz); - if (data == nullptr) return; - - fb_queue_download("signature", data, sz); - fb_queue_command("signature", "installing signature"); -} - static bool is_logical(const std::string& partition) { std::string value; return fb_getvar("is-logical:" + partition, &value) && value == "yes"; @@ -1186,114 +1089,58 @@ static void reboot_to_userspace_fastboot() { fb_reinit(open_device()); } -static void update_super_partition(bool force_wipe) { - if (!if_partition_exists("super", "")) { - return; - } - std::string image = find_item_given_name("super_empty.img"); - if (access(image.c_str(), R_OK) < 0) { - return; - } +class ImageSource { + public: + virtual void* ReadFile(const std::string& name, int64_t* size) const = 0; + virtual int OpenFile(const std::string& name) const = 0; +}; - if (!is_userspace_fastboot()) { - reboot_to_userspace_fastboot(); - } +class FlashAllTool { + public: + FlashAllTool(const ImageSource& source, const std::string& slot_override, bool skip_secondary, bool wipe); - int fd = open(image.c_str(), O_RDONLY); - if (fd < 0) { - die("could not open '%s': %s", image.c_str(), strerror(errno)); - } - fb_queue_download_fd("super", fd, get_file_size(fd)); + void Flash(); - std::string command = "update-super:super"; - if (force_wipe) { - command += ":wipe"; - } - fb_queue_command(command, "Updating super partition"); + private: + void CheckRequirements(); + void DetermineSecondarySlot(); + void CollectImages(); + void FlashImages(const std::vector>& images); + void FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf); + void UpdateSuperPartition(); - // We need these commands to have finished before proceeding, since - // otherwise "getvar is-logical" may not return a correct answer below. - fb_execute_queue(); + const ImageSource& source_; + std::string slot_override_; + bool skip_secondary_; + bool wipe_; + std::string secondary_slot_; + std::vector> boot_images_; + std::vector> os_images_; +}; + +FlashAllTool::FlashAllTool(const ImageSource& source, const std::string& slot_override, bool skip_secondary, bool wipe) + : source_(source), + slot_override_(slot_override), + skip_secondary_(skip_secondary), + wipe_(wipe) +{ } -static void flash_images(const std::vector>& images) { - // Flash each partition in the list if it has a corresponding image. - for (const auto& [image, slot] : images) { - auto fname = find_item_given_name(image->img_name); - fastboot_buffer buf; - if (!load_buf(fname.c_str(), &buf)) { - if (image->optional_if_no_image) continue; - die("could not load '%s': %s", image->img_name, strerror(errno)); - } - auto flashall = [&](const std::string &partition) { - do_send_signature(fname.c_str()); - if (is_logical(partition)) { - fb_queue_resize_partition(partition, std::to_string(buf.image_size)); - } - flash_buf(partition.c_str(), &buf); - }; - do_for_partitions(image->part_name, slot, flashall, false); - } -} - -static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe) { - std::string fname; - queue_info_dump(); - - fb_queue_query_save("product", cur_product, sizeof(cur_product)); - - fname = find_item_given_name("android-info.txt"); - if (fname.empty()) die("cannot find android-info.txt"); - - int64_t sz; - void* data = load_file(fname.c_str(), &sz); - if (data == nullptr) die("could not load android-info.txt: %s", strerror(errno)); - - check_requirements(reinterpret_cast(data), sz); - - std::string secondary; - if (!skip_secondary) { - if (slot_override != "") { - secondary = get_other_slot(slot_override); - } else { - secondary = get_other_slot(); - } - if (secondary == "") { - if (supports_AB()) { - fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n"); - } - skip_secondary = true; - } - } - - // List of partitions to flash and their slots. - std::vector> boot_images; - std::vector> os_images; - for (size_t i = 0; i < arraysize(images); i++) { - const char* slot = NULL; - if (images[i].IsSecondary()) { - if (!skip_secondary) slot = secondary.c_str(); - } else { - slot = slot_override.c_str(); - } - if (!slot) continue; - if (images[i].type == ImageType::BootCritical) { - boot_images.emplace_back(&images[i], slot); - } else if (images[i].type == ImageType::Normal) { - os_images.emplace_back(&images[i], slot); - } - } +void FlashAllTool::Flash() { + CheckRequirements(); + DetermineSecondarySlot(); + CollectImages(); // First flash boot partitions. We allow this to happen either in userspace // or in bootloader fastboot. - flash_images(boot_images); + FlashImages(boot_images_); // Sync the super partition. This will reboot to userspace fastboot if needed. - update_super_partition(wipe); + UpdateSuperPartition(); // 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) { + for (const auto& [image, slot] : os_images_) { auto resize_partition = [](const std::string& partition) -> void { if (is_logical(partition)) { fb_queue_resize_partition(partition, "0"); @@ -1303,15 +1150,178 @@ static void do_flashall(const std::string& slot_override, bool skip_secondary, b } // Flash OS images, resizing logical partitions as needed. - flash_images(os_images); + FlashImages(os_images_); - if (slot_override == "all") { + if (slot_override_ == "all") { set_active("a"); } else { - set_active(slot_override); + set_active(slot_override_); } } +void FlashAllTool::CheckRequirements() { + int64_t sz; + void* data = source_.ReadFile("android-info.txt", &sz); + if (data == nullptr) { + die("could not read android-info.txt"); + } + check_requirements(reinterpret_cast(data), sz); +} + +void FlashAllTool::DetermineSecondarySlot() { + if (skip_secondary_) { + return; + } + if (slot_override_ != "") { + secondary_slot_ = get_other_slot(slot_override_); + } else { + secondary_slot_ = get_other_slot(); + } + if (secondary_slot_ == "") { + if (supports_AB()) { + fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n"); + } + skip_secondary_ = true; + } +} + +void FlashAllTool::CollectImages() { + for (size_t i = 0; i < arraysize(images); ++i) { + std::string slot = slot_override_; + if (images[i].IsSecondary()) { + if (skip_secondary_) { + continue; + } + slot = secondary_slot_; + } + if (images[i].type == ImageType::BootCritical) { + boot_images_.emplace_back(&images[i], slot); + } else if (images[i].type == ImageType::Normal) { + os_images_.emplace_back(&images[i], slot); + } + } +} + +void FlashAllTool::FlashImages(const std::vector>& images) { + for (const auto& [image, slot] : images) { + fastboot_buffer buf; + int fd = source_.OpenFile(image->img_name); + if (fd < 0 || !load_buf_fd(fd, &buf)) { + if (image->optional_if_no_image) { + continue; + } + die("could not load '%s': %s", image->img_name, strerror(errno)); + } + FlashImage(*image, slot, &buf); + } +} + +void FlashAllTool::FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf) { + auto flash = [&, this](const std::string& partition_name) { + int64_t sz; + void* data = source_.ReadFile(image.sig_name, &sz); + if (data) { + fb_queue_download("signature", data, sz); + fb_queue_command("signature", "installing signature"); + } + + if (is_logical(partition_name)) { + fb_queue_resize_partition(partition_name, std::to_string(buf->image_size)); + } + flash_buf(partition_name.c_str(), buf); + }; + do_for_partitions(image.part_name, slot, flash, false); +} + +void FlashAllTool::UpdateSuperPartition() { + if (!if_partition_exists("super", "")) { + return; + } + + int fd = source_.OpenFile("super_empty.img"); + if (fd < 0) { + return; + } + if (!is_userspace_fastboot()) { + reboot_to_userspace_fastboot(); + } + fb_queue_download_fd("super", fd, get_file_size(fd)); + + std::string command = "update-super:super"; + if (wipe_) { + command += ":wipe"; + } + fb_queue_command(command, "Updating super partition"); + + // We need these commands to have finished before proceeding, since + // otherwise "getvar is-logical" may not return a correct answer below. + fb_execute_queue(); +} + +class ZipImageSource final : public ImageSource { + public: + explicit ZipImageSource(ZipArchiveHandle zip) : zip_(zip) {} + void* ReadFile(const std::string& name, int64_t* size) const override; + int OpenFile(const std::string& name) const override; + + private: + ZipArchiveHandle zip_; +}; + +void* ZipImageSource::ReadFile(const std::string& name, int64_t* size) const { + return unzip_to_memory(zip_, name.c_str(), size); +} + +int ZipImageSource::OpenFile(const std::string& name) const { + return unzip_to_file(zip_, name.c_str()); +} + +static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary) { + queue_info_dump(); + + fb_queue_query_save("product", cur_product, sizeof(cur_product)); + + ZipArchiveHandle zip; + int error = OpenArchive(filename, &zip); + if (error != 0) { + die("failed to open zip file '%s': %s", filename, ErrorCodeString(error)); + } + + FlashAllTool tool(ZipImageSource(zip), slot_override, skip_secondary, false); + tool.Flash(); + + CloseArchive(zip); +} + +class LocalImageSource final : public ImageSource { + public: + void* ReadFile(const std::string& name, int64_t* size) const override; + int OpenFile(const std::string& name) const override; +}; + +void* LocalImageSource::ReadFile(const std::string& name, int64_t* size) const { + auto path = find_item_given_name(name); + if (path.empty()) { + return nullptr; + } + return load_file(path.c_str(), size); +} + +int LocalImageSource::OpenFile(const std::string& name) const { + auto path = find_item_given_name(name); + return open(path.c_str(), O_RDONLY); +} + +static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe) { + std::string fname; + queue_info_dump(); + + fb_queue_query_save("product", cur_product, sizeof(cur_product)); + + FlashAllTool tool(LocalImageSource(), slot_override, skip_secondary, wipe); + tool.Flash(); +} + static std::string next_arg(std::vector* args) { if (args->empty()) syntax_error("expected argument"); std::string result = args->front();