diff options
Diffstat (limited to 'recovery.cpp')
-rw-r--r-- | recovery.cpp | 237 |
1 files changed, 58 insertions, 179 deletions
diff --git a/recovery.cpp b/recovery.cpp index 5fc673ec2..97ca0a504 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -18,11 +18,9 @@ #include <ctype.h> #include <errno.h> -#include <fcntl.h> #include <getopt.h> #include <inttypes.h> #include <limits.h> -#include <linux/fs.h> #include <linux/input.h> #include <stdio.h> #include <stdlib.h> @@ -30,8 +28,8 @@ #include <sys/types.h> #include <unistd.h> -#include <algorithm> #include <functional> +#include <iterator> #include <memory> #include <string> #include <vector> @@ -42,19 +40,20 @@ #include <android-base/properties.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> -#include <android-base/unique_fd.h> -#include <bootloader_message/bootloader_message.h> #include <cutils/properties.h> /* for property_list */ +#include <fs_mgr/roots.h> #include <healthhalutils/HealthHalUtils.h> #include <ziparchive/zip_archive.h> +#include "bootloader_message/bootloader_message.h" #include "common.h" #include "fsck_unshare_blocks.h" #include "install/adb_install.h" -#include "install/fuse_sdcard_install.h" +#include "install/fuse_install.h" #include "install/install.h" #include "install/package.h" #include "install/wipe_data.h" +#include "install/wipe_device.h" #include "otautil/error_code.h" #include "otautil/logging.h" #include "otautil/paths.h" @@ -85,6 +84,8 @@ const char* reason = nullptr; * * The arguments which may be supplied in the recovery.command file: * --update_package=path - verify install an OTA package file + * --install_with_fuse - install the update package with FUSE. This allows installation of large + * packages on LP32 builds. Since the mmap will otherwise fail due to out of memory. * --wipe_data - erase user data (and cache), then reboot * --prompt_and_wipe_data - prompt the user that data is corrupt, with their consent erase user * data (and cache), then reboot @@ -115,12 +116,12 @@ const char* reason = nullptr; * 3. main system reboots into recovery * 4. get_args() writes BCB with "boot-recovery" and "--update_package=..." * -- after this, rebooting will attempt to reinstall the update -- - * 5. install_package() attempts to install the update + * 5. InstallPackage() attempts to install the update * NOTE: the package install must itself be restartable from any point * 6. finish_recovery() erases BCB * -- after this, rebooting will (try to) restart the main system -- * 7. ** if install failed ** - * 7a. prompt_and_wait() shows an error icon and waits for the user + * 7a. PromptAndWait() shows an error icon and waits for the user * 7b. the user reboots (pulling the battery, etc) into the main system */ @@ -221,165 +222,6 @@ static InstallResult prompt_and_wipe_data(Device* device) { } } -// Secure-wipe a given partition. It uses BLKSECDISCARD, if supported. Otherwise, it goes with -// BLKDISCARD (if device supports BLKDISCARDZEROES) or BLKZEROOUT. -static bool secure_wipe_partition(const std::string& partition) { - android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(partition.c_str(), O_WRONLY))); - if (fd == -1) { - PLOG(ERROR) << "Failed to open \"" << partition << "\""; - return false; - } - - uint64_t range[2] = { 0, 0 }; - if (ioctl(fd, BLKGETSIZE64, &range[1]) == -1 || range[1] == 0) { - PLOG(ERROR) << "Failed to get partition size"; - return false; - } - LOG(INFO) << "Secure-wiping \"" << partition << "\" from " << range[0] << " to " << range[1]; - - LOG(INFO) << " Trying BLKSECDISCARD..."; - if (ioctl(fd, BLKSECDISCARD, &range) == -1) { - PLOG(WARNING) << " Failed"; - - // Use BLKDISCARD if it zeroes out blocks, otherwise use BLKZEROOUT. - unsigned int zeroes; - if (ioctl(fd, BLKDISCARDZEROES, &zeroes) == 0 && zeroes != 0) { - LOG(INFO) << " Trying BLKDISCARD..."; - if (ioctl(fd, BLKDISCARD, &range) == -1) { - PLOG(ERROR) << " Failed"; - return false; - } - } else { - LOG(INFO) << " Trying BLKZEROOUT..."; - if (ioctl(fd, BLKZEROOUT, &range) == -1) { - PLOG(ERROR) << " Failed"; - return false; - } - } - } - - LOG(INFO) << " Done"; - return true; -} - -static std::unique_ptr<Package> ReadWipePackage(size_t wipe_package_size) { - if (wipe_package_size == 0) { - LOG(ERROR) << "wipe_package_size is zero"; - return nullptr; - } - - std::string wipe_package; - std::string err_str; - if (!read_wipe_package(&wipe_package, wipe_package_size, &err_str)) { - PLOG(ERROR) << "Failed to read wipe package" << err_str; - return nullptr; - } - - return Package::CreateMemoryPackage( - std::vector<uint8_t>(wipe_package.begin(), wipe_package.end()), nullptr); -} - -// Checks if the wipe package matches expectation. If the check passes, reads the list of -// partitions to wipe from the package. Checks include -// 1. verify the package. -// 2. check metadata (ota-type, pre-device and serial number if having one). -static bool CheckWipePackage(Package* wipe_package) { - if (!verify_package(wipe_package, ui)) { - LOG(ERROR) << "Failed to verify package"; - return false; - } - - ZipArchiveHandle zip = wipe_package->GetZipArchiveHandle(); - if (!zip) { - LOG(ERROR) << "Failed to get ZipArchiveHandle"; - return false; - } - - std::map<std::string, std::string> metadata; - if (!ReadMetadataFromPackage(zip, &metadata)) { - LOG(ERROR) << "Failed to parse metadata in the zip file"; - return false; - } - - return CheckPackageMetadata(metadata, OtaType::BRICK) == 0; -} - -std::vector<std::string> GetWipePartitionList(Package* wipe_package) { - ZipArchiveHandle zip = wipe_package->GetZipArchiveHandle(); - if (!zip) { - LOG(ERROR) << "Failed to get ZipArchiveHandle"; - return {}; - } - - static constexpr const char* RECOVERY_WIPE_ENTRY_NAME = "recovery.wipe"; - - std::string partition_list_content; - ZipString path(RECOVERY_WIPE_ENTRY_NAME); - ZipEntry entry; - if (FindEntry(zip, path, &entry) == 0) { - uint32_t length = entry.uncompressed_length; - partition_list_content = std::string(length, '\0'); - if (auto err = ExtractToMemory( - zip, &entry, reinterpret_cast<uint8_t*>(partition_list_content.data()), length); - err != 0) { - LOG(ERROR) << "Failed to extract " << RECOVERY_WIPE_ENTRY_NAME << ": " - << ErrorCodeString(err); - return {}; - } - } else { - LOG(INFO) << "Failed to find " << RECOVERY_WIPE_ENTRY_NAME - << ", falling back to use the partition list on device."; - - static constexpr const char* RECOVERY_WIPE_ON_DEVICE = "/etc/recovery.wipe"; - if (!android::base::ReadFileToString(RECOVERY_WIPE_ON_DEVICE, &partition_list_content)) { - PLOG(ERROR) << "failed to read \"" << RECOVERY_WIPE_ON_DEVICE << "\""; - return {}; - } - } - - std::vector<std::string> result; - std::vector<std::string> lines = android::base::Split(partition_list_content, "\n"); - for (const std::string& line : lines) { - std::string partition = android::base::Trim(line); - // Ignore '#' comment or empty lines. - if (android::base::StartsWith(partition, "#") || partition.empty()) { - continue; - } - result.push_back(line); - } - - return result; -} - -// Wipes the current A/B device, with a secure wipe of all the partitions in RECOVERY_WIPE. -static bool wipe_ab_device(size_t wipe_package_size) { - ui->SetBackground(RecoveryUI::ERASING); - ui->SetProgressType(RecoveryUI::INDETERMINATE); - - auto wipe_package = ReadWipePackage(wipe_package_size); - if (!wipe_package) { - LOG(ERROR) << "Failed to open wipe package"; - return false; - } - - if (!CheckWipePackage(wipe_package.get())) { - LOG(ERROR) << "Failed to verify wipe package"; - return false; - } - - auto partition_list = GetWipePartitionList(wipe_package.get()); - if (partition_list.empty()) { - LOG(ERROR) << "Empty wipe ab partition list"; - return false; - } - - for (const auto& partition : partition_list) { - // Proceed anyway even if it fails to wipe some partition. - secure_wipe_partition(partition); - } - return true; -} - static void choose_recovery_file(Device* device) { std::vector<std::string> entries; if (has_cache) { @@ -473,14 +315,18 @@ static void run_graphics_test() { ui->ShowText(true); } -// Returns REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION means to take the default, -// which is to reboot or shutdown depending on if the --shutdown_after flag was passed to recovery. -static Device::BuiltinAction prompt_and_wait(Device* device, int status) { +// Shows the recovery UI and waits for user input. Returns one of the device builtin actions, such +// as REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION means to take the default, which +// is to reboot or shutdown depending on if the --shutdown_after flag was passed to recovery. +static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status) { for (;;) { finish_recovery(); switch (status) { case INSTALL_SUCCESS: case INSTALL_NONE: + case INSTALL_SKIPPED: + case INSTALL_RETRY: + case INSTALL_KEY_INTERRUPTED: ui->SetBackground(RecoveryUI::NO_COMMAND); break; @@ -488,6 +334,12 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { case INSTALL_CORRUPT: ui->SetBackground(RecoveryUI::ERROR); break; + + case INSTALL_REBOOT: + // All the reboots should have been handled prior to entering PromptAndWait() or immediately + // after installing a package. + LOG(FATAL) << "Invalid status code of INSTALL_REBOOT"; + break; } ui->SetProgressType(RecoveryUI::EMPTY); @@ -506,6 +358,8 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { : device->InvokeMenuItem(chosen_item); switch (chosen_action) { + case Device::REBOOT_FROM_FASTBOOT: // Can not happen + case Device::SHUTDOWN_FROM_FASTBOOT: // Can not happen case Device::NO_ACTION: break; @@ -556,7 +410,7 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { status = ApplyFromAdb(device, false /* rescue_mode */, &reboot_action); } else { adb = false; - status = ApplyFromSdcard(device, ui); + status = ApplyFromSdcard(device); } ui->Print("\nInstall from %s completed with status %d.\n", adb ? "ADB" : "SD card", status); @@ -588,8 +442,7 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { break; } case Device::MOUNT_SYSTEM: - // the system partition is mounted at /mnt/system - if (ensure_path_mounted_at(get_system_root(), "/mnt/system") != -1) { + if (ensure_path_mounted_at(android::fs_mgr::GetSystemRoot(), "/mnt/system") != -1) { ui->Print("Mounted /system.\n"); } break; @@ -726,6 +579,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri static constexpr struct option OPTIONS[] = { { "fastboot", no_argument, nullptr, 0 }, { "fsck_unshare_blocks", no_argument, nullptr, 0 }, + { "install_with_fuse", no_argument, nullptr, 0 }, { "just_exit", no_argument, nullptr, 'x' }, { "locale", required_argument, nullptr, 0 }, { "prompt_and_wipe_data", no_argument, nullptr, 0 }, @@ -746,6 +600,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri }; const char* update_package = nullptr; + bool install_with_fuse = false; // memory map the update package by default. bool should_wipe_data = false; bool should_prompt_and_wipe_data = false; bool should_wipe_cache = false; @@ -781,6 +636,8 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri std::string option = OPTIONS[option_index].name; if (option == "fsck_unshare_blocks") { fsck_unshare_blocks = true; + } else if (option == "install_with_fuse") { + install_with_fuse = true; } else if (option == "locale" || option == "fastboot") { // Handled in recovery_main.cpp } else if (option == "prompt_and_wipe_data") { @@ -851,7 +708,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri ui->Print("Supported API: %d\n", kRecoveryApiVersion); - int status = INSTALL_SUCCESS; + InstallResult status = INSTALL_SUCCESS; // next_action indicates the next target to reboot into upon finishing the install. It could be // overridden to a different reboot target per user request. Device::BuiltinAction next_action = shutdown_after ? Device::SHUTDOWN : Device::REBOOT; @@ -881,7 +738,29 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri set_retry_bootloader_message(retry_count + 1, args); } - status = install_package(update_package, should_wipe_cache, true, retry_count, ui); + if (update_package[0] == '@') { + ensure_path_mounted(update_package + 1); + } else { + ensure_path_mounted(update_package); + } + + if (install_with_fuse) { + LOG(INFO) << "Installing package " << update_package << " with fuse"; + status = InstallWithFuseFromPath(update_package, ui); + } else if (auto memory_package = Package::CreateMemoryPackage( + update_package, + std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1)); + memory_package != nullptr) { + status = InstallPackage(memory_package.get(), update_package, should_wipe_cache, + retry_count, ui); + } else { + // We may fail to memory map the package on 32 bit builds for packages with 2GiB+ size. + // In such cases, we will try to install the package with fuse. This is not the default + // installation method because it introduces a layer of indirection from the kernel space. + LOG(WARNING) << "Failed to memory map package " << update_package + << "; falling back to install with fuse"; + status = InstallWithFuseFromPath(update_package, ui); + } if (status != INSTALL_SUCCESS) { ui->Print("Installation aborted.\n"); @@ -895,8 +774,8 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri // Print retry count on screen. ui->Print("Retry attempt %d\n", retry_count); - // Reboot and retry the update - if (!reboot("reboot,recovery")) { + // Reboot back into recovery to retry the update. + if (!Reboot("recovery")) { ui->Print("Reboot failed\n"); } else { while (true) { @@ -934,7 +813,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri status = INSTALL_ERROR; } } else if (should_wipe_ab) { - if (!wipe_ab_device(wipe_package_size)) { + if (!WipeAbDevice(device, wipe_package_size)) { status = INSTALL_ERROR; } } else if (sideload) { @@ -989,7 +868,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri // for 5s followed by an automatic reboot. if (status != INSTALL_REBOOT) { if (status == INSTALL_NONE || ui->IsTextVisible()) { - Device::BuiltinAction temp = prompt_and_wait(device, status); + auto temp = PromptAndWait(device, status); if (temp != Device::NO_ACTION) { next_action = temp; } |