diff options
Diffstat (limited to 'install')
-rw-r--r-- | install/Android.bp | 4 | ||||
-rw-r--r-- | install/adb_install.cpp | 18 | ||||
-rw-r--r-- | install/fuse_install.cpp (renamed from install/fuse_sdcard_install.cpp) | 84 | ||||
-rw-r--r-- | install/include/install/adb_install.h | 11 | ||||
-rw-r--r-- | install/include/install/fuse_install.h | 30 | ||||
-rw-r--r-- | install/include/install/install.h | 22 | ||||
-rw-r--r-- | install/include/install/package.h | 9 | ||||
-rw-r--r-- | install/include/install/wipe_device.h (renamed from install/include/install/fuse_sdcard_install.h) | 11 | ||||
-rw-r--r-- | install/include/private/setup_commands.h | 8 | ||||
-rw-r--r-- | install/install.cpp | 166 | ||||
-rw-r--r-- | install/package.cpp | 18 | ||||
-rw-r--r-- | install/verifier.cpp | 10 | ||||
-rw-r--r-- | install/wipe_device.cpp | 197 |
13 files changed, 431 insertions, 157 deletions
diff --git a/install/Android.bp b/install/Android.bp index ea893a075..89cc3f23e 100644 --- a/install/Android.bp +++ b/install/Android.bp @@ -47,7 +47,6 @@ cc_defaults { // external dependencies "libvintf_recovery", "libvintf", - "libfstab", ], } @@ -62,11 +61,12 @@ cc_library_static { srcs: [ "adb_install.cpp", "asn1_decoder.cpp", - "fuse_sdcard_install.cpp", + "fuse_install.cpp", "install.cpp", "package.cpp", "verifier.cpp", "wipe_data.cpp", + "wipe_device.cpp", ], shared_libs: [ diff --git a/install/adb_install.cpp b/install/adb_install.cpp index 4dd1f1b09..ed664429a 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -90,7 +90,7 @@ static bool WriteStatusToFd(MinadbdCommandStatus status, int fd) { // Installs the package from FUSE. Returns the installation result and whether it should continue // waiting for new commands. -static auto AdbInstallPackageHandler(RecoveryUI* ui, int* result) { +static auto AdbInstallPackageHandler(RecoveryUI* ui, InstallResult* result) { // How long (in seconds) we wait for the package path to be ready. It doesn't need to be too long // because the minadbd service has already issued an install command. FUSE_SIDELOAD_HOST_PATHNAME // will start to exist once the host connects and starts serving a package. Poll for its @@ -110,7 +110,11 @@ static auto AdbInstallPackageHandler(RecoveryUI* ui, int* result) { break; } } - *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, ui); + + auto package = + Package::CreateFilePackage(FUSE_SIDELOAD_HOST_PATHNAME, + std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1)); + *result = InstallPackage(package.get(), FUSE_SIDELOAD_HOST_PATHNAME, false, 0, ui); break; } @@ -120,7 +124,7 @@ static auto AdbInstallPackageHandler(RecoveryUI* ui, int* result) { return std::make_pair(*result == INSTALL_SUCCESS, should_continue); } -static auto AdbRebootHandler(MinadbdCommand command, int* result, +static auto AdbRebootHandler(MinadbdCommand command, InstallResult* result, Device::BuiltinAction* reboot_action) { // Use Device::REBOOT_{FASTBOOT,RECOVERY,RESCUE}, instead of the ones with ENTER_. This allows // rebooting back into fastboot/recovery/rescue mode through bootloader, which may use a newly @@ -331,7 +335,7 @@ static void CreateMinadbdServiceAndExecuteCommands( signal(SIGPIPE, SIG_DFL); } -int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action) { +InstallResult ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action) { // Save the usb state to restore after the sideload operation. std::string usb_state = android::base::GetProperty("sys.usb.state", "none"); // Clean up state and stop adbd. @@ -342,7 +346,7 @@ int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot RecoveryUI* ui = device->GetUI(); - int install_result = INSTALL_ERROR; + InstallResult install_result = INSTALL_ERROR; std::map<MinadbdCommand, CommandFunction> command_map{ { MinadbdCommand::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) }, { MinadbdCommand::kRebootAndroid, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootAndroid, @@ -363,11 +367,13 @@ int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot "\n\nNow send the package you want to apply\n" "to the device with \"adb sideload <filename>\"...\n"); } else { - ui->Print("\n\nWaiting for rescue commands...\n"); command_map.emplace(MinadbdCommand::kWipeData, [&device]() { bool result = WipeData(device, false); return std::make_pair(result, true); }); + command_map.emplace(MinadbdCommand::kNoOp, []() { return std::make_pair(true, true); }); + + ui->Print("\n\nWaiting for rescue commands...\n"); } CreateMinadbdServiceAndExecuteCommands(ui, command_map, rescue_mode); diff --git a/install/fuse_sdcard_install.cpp b/install/fuse_install.cpp index 1aa8768e7..8a7a278e0 100644 --- a/install/fuse_sdcard_install.cpp +++ b/install/fuse_install.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "install/fuse_sdcard_install.h" +#include "install/fuse_install.h" #include <dirent.h> #include <signal.h> @@ -27,6 +27,7 @@ #include <algorithm> #include <functional> #include <memory> +#include <string> #include <vector> #include <android-base/logging.h> @@ -74,7 +75,8 @@ static std::string BrowseDirectory(const std::string& path, Device* device, Reco // Skip "." and ".." entries. if (name == "." || name == "..") continue; dirs.push_back(name + "/"); - } else if (de->d_type == DT_REG && android::base::EndsWithIgnoreCase(name, ".zip")) { + } else if (de->d_type == DT_REG && (android::base::EndsWithIgnoreCase(name, ".zip") || + android::base::EndsWithIgnoreCase(name, ".map"))) { entries.push_back(name); } } @@ -119,49 +121,44 @@ static std::string BrowseDirectory(const std::string& path, Device* device, Reco // Unreachable. } -static bool StartSdcardFuse(const std::string& path) { - auto file_data_reader = std::make_unique<FuseFileDataProvider>(path, 65536); - - if (!file_data_reader->Valid()) { +static bool StartInstallPackageFuse(std::string_view path) { + if (path.empty()) { return false; } - // The installation process expects to find the sdcard unmounted. Unmount it with MNT_DETACH so - // that our open file continues to work but new references see it as unmounted. - umount2("/sdcard", MNT_DETACH); + constexpr auto FUSE_BLOCK_SIZE = 65536; + bool is_block_map = android::base::ConsumePrefix(&path, "@"); + auto fuse_data_provider = + is_block_map ? FuseBlockDataProvider::CreateFromBlockMap(std::string(path), FUSE_BLOCK_SIZE) + : FuseFileDataProvider::CreateFromFile(std::string(path), FUSE_BLOCK_SIZE); - return run_fuse_sideload(std::move(file_data_reader)) == 0; -} - -int ApplyFromSdcard(Device* device, RecoveryUI* ui) { - if (ensure_path_mounted(SDCARD_ROOT) != 0) { - LOG(ERROR) << "\n-- Couldn't mount " << SDCARD_ROOT << ".\n"; - return INSTALL_ERROR; + if (!fuse_data_provider || !fuse_data_provider->Valid()) { + LOG(ERROR) << "Failed to create fuse data provider."; + return false; } - std::string path = BrowseDirectory(SDCARD_ROOT, device, ui); - if (path.empty()) { - LOG(ERROR) << "\n-- No package file selected.\n"; - ensure_path_unmounted(SDCARD_ROOT); - return INSTALL_ERROR; + if (android::base::StartsWith(path, SDCARD_ROOT)) { + // The installation process expects to find the sdcard unmounted. Unmount it with MNT_DETACH so + // that our open file continues to work but new references see it as unmounted. + umount2(SDCARD_ROOT, MNT_DETACH); } - ui->Print("\n-- Install %s ...\n", path.c_str()); - SetSdcardUpdateBootloaderMessage(); + return run_fuse_sideload(std::move(fuse_data_provider)) == 0; +} +InstallResult InstallWithFuseFromPath(std::string_view path, RecoveryUI* ui) { // We used to use fuse in a thread as opposed to a process. Since accessing // through fuse involves going from kernel to userspace to kernel, it leads // to deadlock when a page fault occurs. (Bug: 26313124) pid_t child; if ((child = fork()) == 0) { - bool status = StartSdcardFuse(path); + bool status = StartInstallPackageFuse(path); _exit(status ? EXIT_SUCCESS : EXIT_FAILURE); } - // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the fuse in child - // process is ready. - int result = INSTALL_ERROR; + // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the fuse in child process is ready. + InstallResult result = INSTALL_ERROR; int status; bool waited = false; for (int i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) { @@ -183,8 +180,11 @@ int ApplyFromSdcard(Device* device, RecoveryUI* ui) { break; } } - - result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0 /*retry_count*/, ui); + auto package = + Package::CreateFilePackage(FUSE_SIDELOAD_HOST_PATHNAME, + std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1)); + result = + InstallPackage(package.get(), FUSE_SIDELOAD_HOST_PATHNAME, false, 0 /* retry_count */, ui); break; } @@ -201,6 +201,32 @@ int ApplyFromSdcard(Device* device, RecoveryUI* ui) { LOG(ERROR) << "Error exit from the fuse process: " << WEXITSTATUS(status); } + return result; +} + +InstallResult ApplyFromSdcard(Device* device) { + auto ui = device->GetUI(); + if (ensure_path_mounted(SDCARD_ROOT) != 0) { + LOG(ERROR) << "\n-- Couldn't mount " << SDCARD_ROOT << ".\n"; + return INSTALL_ERROR; + } + + std::string path = BrowseDirectory(SDCARD_ROOT, device, ui); + if (path.empty()) { + LOG(ERROR) << "\n-- No package file selected.\n"; + ensure_path_unmounted(SDCARD_ROOT); + return INSTALL_ERROR; + } + + // Hint the install function to read from a block map file. + if (android::base::EndsWithIgnoreCase(path, ".map")) { + path = "@" + path; + } + + ui->Print("\n-- Install %s ...\n", path.c_str()); + SetSdcardUpdateBootloaderMessage(); + + auto result = InstallWithFuseFromPath(path, ui); ensure_path_unmounted(SDCARD_ROOT); return result; } diff --git a/install/include/install/adb_install.h b/install/include/install/adb_install.h index 3a0a81747..880022361 100644 --- a/install/include/install/adb_install.h +++ b/install/include/install/adb_install.h @@ -16,9 +16,10 @@ #pragma once -#include <recovery_ui/device.h> +#include "install/install.h" +#include "recovery_ui/device.h" -// Applies a package via `adb sideload` or `adb rescue`. Returns the install result (in `enum -// InstallResult`). When a reboot has been requested, INSTALL_REBOOT will be the return value, with -// the reboot target set in reboot_action. -int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action); +// Applies a package via `adb sideload` or `adb rescue`. Returns the install result. When a reboot +// has been requested, INSTALL_REBOOT will be the return value, with the reboot target set in +// reboot_action. +InstallResult ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action); diff --git a/install/include/install/fuse_install.h b/install/include/install/fuse_install.h new file mode 100644 index 000000000..63b116aeb --- /dev/null +++ b/install/include/install/fuse_install.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 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 <string_view> + +#include "install/install.h" +#include "recovery_ui/device.h" +#include "recovery_ui/ui.h" + +// Starts FUSE with the package from |path| as the data source. And installs the package from +// |FUSE_SIDELOAD_HOST_PATHNAME|. The |path| can point to the location of a package zip file or a +// block map file with the prefix '@'; e.g. /sdcard/package.zip, @/cache/recovery/block.map. +InstallResult InstallWithFuseFromPath(std::string_view path, RecoveryUI* ui); + +InstallResult ApplyFromSdcard(Device* device); diff --git a/install/include/install/install.h b/install/include/install/install.h index c0a8f1f4c..b4b3a9149 100644 --- a/install/include/install/install.h +++ b/install/include/install/install.h @@ -44,11 +44,12 @@ enum class OtaType { BRICK, }; -// Installs the given update package. This function should also wipe the cache partition after a -// successful installation if |should_wipe_cache| is true or an updater command asks to wipe the -// cache. -int install_package(const std::string& package, bool should_wipe_cache, bool needs_mount, - int retry_count, RecoveryUI* ui); +// Installs the given update package. The package_id is a string provided by the caller (e.g. the +// package path) to identify the package and log to last_install. This function should also wipe the +// cache partition after a successful installation if |should_wipe_cache| is true or an updater +// command asks to wipe the cache. +InstallResult InstallPackage(Package* package, const std::string_view package_id, + bool should_wipe_cache, int retry_count, RecoveryUI* ui); // Verifies the package by ota keys. Returns true if the package is verified successfully, // otherwise returns false. @@ -58,14 +59,11 @@ bool verify_package(Package* package, RecoveryUI* ui); // result to |metadata|. Return true if succeed, otherwise return false. bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map<std::string, std::string>* metadata); -// Reads the "recovery.wipe" entry in the zip archive returns a list of partitions to wipe. -std::vector<std::string> GetWipePartitionList(Package* wipe_package); - // Verifies the compatibility info in a Treble-compatible package. Returns true directly if the // entry doesn't exist. bool verify_package_compatibility(ZipArchiveHandle package_zip); -// Checks if the the metadata in the OTA package has expected values. Returns 0 on success. -// Mandatory checks: ota-type, pre-device and serial number(if presents) -// AB OTA specific checks: pre-build version, fingerprint, timestamp. -int CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type); +// Checks if the metadata in the OTA package has expected values. Mandatory checks: ota-type, +// pre-device and serial number (if presents). A/B OTA specific checks: pre-build version, +// fingerprint, timestamp. +bool CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type); diff --git a/install/include/install/package.h b/install/include/install/package.h index cd44d10be..0b4233238 100644 --- a/install/include/install/package.h +++ b/install/include/install/package.h @@ -28,6 +28,11 @@ #include "verifier.h" +enum class PackageType { + kMemory, + kFile, +}; + // This class serves as a wrapper for an OTA update package. It aims to provide the common // interface for both packages loaded in memory and packages read from fd. class Package : public VerifierInterface { @@ -41,6 +46,10 @@ class Package : public VerifierInterface { virtual ~Package() = default; + virtual PackageType GetType() const = 0; + + virtual std::string GetPath() const = 0; + // Opens the package as a zip file and returns the ZipArchiveHandle. virtual ZipArchiveHandle GetZipArchiveHandle() = 0; diff --git a/install/include/install/fuse_sdcard_install.h b/install/include/install/wipe_device.h index d9214ca3b..c60b99997 100644 --- a/install/include/install/fuse_sdcard_install.h +++ b/install/include/install/wipe_device.h @@ -16,7 +16,14 @@ #pragma once +#include <string> +#include <vector> + +#include "install/package.h" #include "recovery_ui/device.h" -#include "recovery_ui/ui.h" -int ApplyFromSdcard(Device* device, RecoveryUI* ui); +// Wipes the current A/B device, with a secure wipe of all the partitions in RECOVERY_WIPE. +bool WipeAbDevice(Device* device, size_t wipe_package_size); + +// Reads the "recovery.wipe" entry in the zip archive returns a list of partitions to wipe. +std::vector<std::string> GetWipePartitionList(Package* wipe_package); diff --git a/install/include/private/setup_commands.h b/install/include/private/setup_commands.h index 7fdc741d6..dcff76112 100644 --- a/install/include/private/setup_commands.h +++ b/install/include/private/setup_commands.h @@ -27,13 +27,13 @@ // |zip| located at |package|. Stores the command line that should be called into |cmd|. The // |status_fd| is the file descriptor the child process should use to report back the progress of // the update. -int SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int retry_count, - int status_fd, std::vector<std::string>* cmd); +bool SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int retry_count, + int status_fd, std::vector<std::string>* cmd); // Sets up the commands for an A/B update. Extracts the needed entries from the open zip archive // |zip| located at |package|. Stores the command line that should be called into |cmd|. The // |status_fd| is the file descriptor the child process should use to report back the progress of // the update. Note that since this applies to the sideloading flow only, it takes one less // parameter |retry_count| than the non-A/B version. -int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd, - std::vector<std::string>* cmd); +bool SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd, + std::vector<std::string>* cmd); diff --git a/install/install.cpp b/install/install.cpp index e2d470096..9d67b0105 100644 --- a/install/install.cpp +++ b/install/install.cpp @@ -60,7 +60,8 @@ using namespace std::chrono_literals; static constexpr int kRecoveryApiVersion = 3; -// Assert the version defined in code and in Android.mk are consistent. +// We define RECOVERY_API_VERSION in Android.mk, which will be picked up by build system and packed +// into target_files.zip. Assert the version defined in code and in Android.mk are consistent. static_assert(kRecoveryApiVersion == RECOVERY_API_VERSION, "Mismatching recovery API versions."); // Default allocation of progress bar segments to operations @@ -73,9 +74,8 @@ bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map<std::string, std::st CHECK(metadata != nullptr); static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata"; - ZipString path(METADATA_PATH); ZipEntry entry; - if (FindEntry(zip, path, &entry) != 0) { + if (FindEntry(zip, METADATA_PATH, &entry) != 0) { LOG(ERROR) << "Failed to find " << METADATA_PATH; return false; } @@ -139,14 +139,14 @@ static void ReadSourceTargetBuild(const std::map<std::string, std::string>& meta // Checks the build version, fingerprint and timestamp in the metadata of the A/B package. // Downgrading is not allowed unless explicitly enabled in the package and only for // incremental packages. -static int CheckAbSpecificMetadata(const std::map<std::string, std::string>& metadata) { +static bool CheckAbSpecificMetadata(const std::map<std::string, std::string>& metadata) { // Incremental updates should match the current build. auto device_pre_build = android::base::GetProperty("ro.build.version.incremental", ""); auto pkg_pre_build = get_value(metadata, "pre-build-incremental"); if (!pkg_pre_build.empty() && pkg_pre_build != device_pre_build) { LOG(ERROR) << "Package is for source build " << pkg_pre_build << " but expected " << device_pre_build; - return INSTALL_ERROR; + return false; } auto device_fingerprint = android::base::GetProperty("ro.build.fingerprint", ""); @@ -154,7 +154,7 @@ static int CheckAbSpecificMetadata(const std::map<std::string, std::string>& met if (!pkg_pre_build_fingerprint.empty() && pkg_pre_build_fingerprint != device_fingerprint) { LOG(ERROR) << "Package is for source build " << pkg_pre_build_fingerprint << " but expected " << device_fingerprint; - return INSTALL_ERROR; + return false; } // Check for downgrade version. @@ -172,36 +172,36 @@ static int CheckAbSpecificMetadata(const std::map<std::string, std::string>& met "newer than timestamp " << build_timestamp << " but package has timestamp " << pkg_post_timestamp << " and downgrade not allowed."; - return INSTALL_ERROR; + return false; } if (pkg_pre_build_fingerprint.empty()) { LOG(ERROR) << "Downgrade package must have a pre-build version set, not allowed."; - return INSTALL_ERROR; + return false; } } - return 0; + return true; } -int CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type) { +bool CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type) { auto package_ota_type = get_value(metadata, "ota-type"); auto expected_ota_type = OtaTypeToString(ota_type); if (ota_type != OtaType::AB && ota_type != OtaType::BRICK) { LOG(INFO) << "Skip package metadata check for ota type " << expected_ota_type; - return 0; + return true; } if (package_ota_type != expected_ota_type) { LOG(ERROR) << "Unexpected ota package type, expects " << expected_ota_type << ", actual " << package_ota_type; - return INSTALL_ERROR; + return false; } auto device = android::base::GetProperty("ro.product.device", ""); auto pkg_device = get_value(metadata, "pre-device"); if (pkg_device != device || pkg_device.empty()) { LOG(ERROR) << "Package is for product " << pkg_device << " but expected " << device; - return INSTALL_ERROR; + return false; } // We allow the package to not have any serialno; and we also allow it to carry multiple serial @@ -218,7 +218,7 @@ int CheckPackageMetadata(const std::map<std::string, std::string>& metadata, Ota } if (!serial_number_match) { LOG(ERROR) << "Package is for serial " << pkg_serial_no; - return INSTALL_ERROR; + return false; } } @@ -226,21 +226,20 @@ int CheckPackageMetadata(const std::map<std::string, std::string>& metadata, Ota return CheckAbSpecificMetadata(metadata); } - return 0; + return true; } -int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd, - std::vector<std::string>* cmd) { +bool SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd, + std::vector<std::string>* cmd) { CHECK(cmd != nullptr); // For A/B updates we extract the payload properties to a buffer and obtain the RAW payload offset // in the zip file. static constexpr const char* AB_OTA_PAYLOAD_PROPERTIES = "payload_properties.txt"; - ZipString property_name(AB_OTA_PAYLOAD_PROPERTIES); ZipEntry properties_entry; - if (FindEntry(zip, property_name, &properties_entry) != 0) { + if (FindEntry(zip, AB_OTA_PAYLOAD_PROPERTIES, &properties_entry) != 0) { LOG(ERROR) << "Failed to find " << AB_OTA_PAYLOAD_PROPERTIES; - return INSTALL_CORRUPT; + return false; } uint32_t properties_entry_length = properties_entry.uncompressed_length; std::vector<uint8_t> payload_properties(properties_entry_length); @@ -248,15 +247,14 @@ int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int ExtractToMemory(zip, &properties_entry, payload_properties.data(), properties_entry_length); if (err != 0) { LOG(ERROR) << "Failed to extract " << AB_OTA_PAYLOAD_PROPERTIES << ": " << ErrorCodeString(err); - return INSTALL_CORRUPT; + return false; } static constexpr const char* AB_OTA_PAYLOAD = "payload.bin"; - ZipString payload_name(AB_OTA_PAYLOAD); ZipEntry payload_entry; - if (FindEntry(zip, payload_name, &payload_entry) != 0) { + if (FindEntry(zip, AB_OTA_PAYLOAD, &payload_entry) != 0) { LOG(ERROR) << "Failed to find " << AB_OTA_PAYLOAD; - return INSTALL_CORRUPT; + return false; } long payload_offset = payload_entry.offset; *cmd = { @@ -266,20 +264,19 @@ int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int "--headers=" + std::string(payload_properties.begin(), payload_properties.end()), android::base::StringPrintf("--status_fd=%d", status_fd), }; - return 0; + return true; } -int SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int retry_count, - int status_fd, std::vector<std::string>* cmd) { +bool SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int retry_count, + int status_fd, std::vector<std::string>* cmd) { CHECK(cmd != nullptr); // In non-A/B updates we extract the update binary from the package. static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary"; - ZipString binary_name(UPDATE_BINARY_NAME); ZipEntry binary_entry; - if (FindEntry(zip, binary_name, &binary_entry) != 0) { + if (FindEntry(zip, UPDATE_BINARY_NAME, &binary_entry) != 0) { LOG(ERROR) << "Failed to find update binary " << UPDATE_BINARY_NAME; - return INSTALL_CORRUPT; + return false; } const std::string binary_path = Paths::Get().temporary_update_binary(); @@ -288,13 +285,12 @@ int SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, i open(binary_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0755)); if (fd == -1) { PLOG(ERROR) << "Failed to create " << binary_path; - return INSTALL_ERROR; + return false; } - int32_t error = ExtractEntryToFile(zip, &binary_entry, fd); - if (error != 0) { + if (auto error = ExtractEntryToFile(zip, &binary_entry, fd); error != 0) { LOG(ERROR) << "Failed to extract " << UPDATE_BINARY_NAME << ": " << ErrorCodeString(error); - return INSTALL_ERROR; + return false; } // When executing the update binary contained in the package, the arguments passed are: @@ -311,7 +307,7 @@ int SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, i if (retry_count > 0) { cmd->push_back("retry"); } - return 0; + return true; } static void log_max_temperature(int* max_temperature, const std::atomic<bool>& logger_finished) { @@ -325,21 +321,25 @@ static void log_max_temperature(int* max_temperature, const std::atomic<bool>& l } // If the package contains an update binary, extract it and run it. -static int try_update_binary(const std::string& package, ZipArchiveHandle zip, bool* wipe_cache, - std::vector<std::string>* log_buffer, int retry_count, - int* max_temperature, RecoveryUI* ui) { +static InstallResult TryUpdateBinary(Package* package, bool* wipe_cache, + std::vector<std::string>* log_buffer, int retry_count, + int* max_temperature, RecoveryUI* ui) { std::map<std::string, std::string> metadata; + auto zip = package->GetZipArchiveHandle(); if (!ReadMetadataFromPackage(zip, &metadata)) { LOG(ERROR) << "Failed to parse metadata in the zip file"; return INSTALL_CORRUPT; } bool is_ab = android::base::GetBoolProperty("ro.build.ab_update", false); - // Verifies against the metadata in the package first. - if (int check_status = is_ab ? CheckPackageMetadata(metadata, OtaType::AB) : 0; - check_status != 0) { + if (is_ab) { + CHECK(package->GetType() == PackageType::kFile); + } + + // Verify against the metadata in the package first. + if (is_ab && !CheckPackageMetadata(metadata, OtaType::AB)) { log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure)); - return check_status; + return INSTALL_ERROR; } ReadSourceTargetBuild(metadata, log_buffer); @@ -385,13 +385,15 @@ static int try_update_binary(const std::string& package, ZipArchiveHandle zip, b // updater requests logging the string (e.g. cause of the failure). // + std::string package_path = package->GetPath(); + std::vector<std::string> args; - if (int update_status = - is_ab ? SetUpAbUpdateCommands(package, zip, pipe_write.get(), &args) - : SetUpNonAbUpdateCommands(package, zip, retry_count, pipe_write.get(), &args); - update_status != 0) { + if (auto setup_result = + is_ab ? SetUpAbUpdateCommands(package_path, zip, pipe_write.get(), &args) + : SetUpNonAbUpdateCommands(package_path, zip, retry_count, pipe_write.get(), &args); + !setup_result) { log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure)); - return update_status; + return INSTALL_CORRUPT; } pid_t pid = fork(); @@ -490,11 +492,11 @@ static int try_update_binary(const std::string& package, ZipArchiveHandle zip, b } if (WIFEXITED(status)) { if (WEXITSTATUS(status) != EXIT_SUCCESS) { - LOG(ERROR) << "Error in " << package << " (status " << WEXITSTATUS(status) << ")"; + LOG(ERROR) << "Error in " << package_path << " (status " << WEXITSTATUS(status) << ")"; return INSTALL_ERROR; } } else if (WIFSIGNALED(status)) { - LOG(ERROR) << "Error in " << package << " (killed by signal " << WTERMSIG(status) << ")"; + LOG(ERROR) << "Error in " << package_path << " (killed by signal " << WTERMSIG(status) << ")"; return INSTALL_ERROR; } else { LOG(FATAL) << "Invalid status code " << status; @@ -503,16 +505,15 @@ static int try_update_binary(const std::string& package, ZipArchiveHandle zip, b return INSTALL_SUCCESS; } -// Verifes the compatibility info in a Treble-compatible package. Returns true directly if the +// Verifies the compatibility info in a Treble-compatible package. Returns true directly if the // entry doesn't exist. Note that the compatibility info is packed in a zip file inside the OTA // package. bool verify_package_compatibility(ZipArchiveHandle package_zip) { LOG(INFO) << "Verifying package compatibility..."; static constexpr const char* COMPATIBILITY_ZIP_ENTRY = "compatibility.zip"; - ZipString compatibility_entry_name(COMPATIBILITY_ZIP_ENTRY); ZipEntry compatibility_entry; - if (FindEntry(package_zip, compatibility_entry_name, &compatibility_entry) != 0) { + if (FindEntry(package_zip, COMPATIBILITY_ZIP_ENTRY, &compatibility_entry) != 0) { LOG(INFO) << "Package doesn't contain " << COMPATIBILITY_ZIP_ENTRY << " entry"; return true; } @@ -536,7 +537,7 @@ bool verify_package_compatibility(ZipArchiveHandle package_zip) { // Iterate all the entries inside COMPATIBILITY_ZIP_ENTRY and read the contents. void* cookie; - ret = StartIteration(zip_handle, &cookie, nullptr, nullptr); + ret = StartIteration(zip_handle, &cookie); if (ret != 0) { LOG(ERROR) << "Failed to start iterating zip entries: " << ErrorCodeString(ret); CloseArchive(zip_handle); @@ -546,13 +547,13 @@ bool verify_package_compatibility(ZipArchiveHandle package_zip) { std::vector<std::string> compatibility_info; ZipEntry info_entry; - ZipString info_name; + std::string_view info_name; while (Next(cookie, &info_entry, &info_name) == 0) { std::string content(info_entry.uncompressed_length, '\0'); int32_t ret = ExtractToMemory(zip_handle, &info_entry, reinterpret_cast<uint8_t*>(&content[0]), info_entry.uncompressed_length); if (ret != 0) { - LOG(ERROR) << "Failed to read " << info_name.name << ": " << ErrorCodeString(ret); + LOG(ERROR) << "Failed to read " << info_name << ": " << ErrorCodeString(ret); CloseArchive(zip_handle); return false; } @@ -571,36 +572,16 @@ bool verify_package_compatibility(ZipArchiveHandle package_zip) { return false; } -static int really_install_package(const std::string& path, bool* wipe_cache, bool needs_mount, - std::vector<std::string>* log_buffer, int retry_count, - int* max_temperature, RecoveryUI* ui) { +static InstallResult VerifyAndInstallPackage(Package* package, bool* wipe_cache, + std::vector<std::string>* log_buffer, int retry_count, + int* max_temperature, RecoveryUI* ui) { ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); - ui->Print("Finding update package...\n"); // Give verification half the progress bar... ui->SetProgressType(RecoveryUI::DETERMINATE); ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME); - LOG(INFO) << "Update location: " << path; - - // Map the update package into memory. - ui->Print("Opening update package...\n"); - - if (needs_mount) { - if (path[0] == '@') { - ensure_path_mounted(path.substr(1)); - } else { - ensure_path_mounted(path); - } - } - - auto package = Package::CreateMemoryPackage( - path, std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1)); - if (!package) { - log_buffer->push_back(android::base::StringPrintf("error: %d", kMapFileFailure)); - return INSTALL_CORRUPT; - } // Verify package. - if (!verify_package(package.get(), ui)) { + if (!verify_package(package, ui)) { log_buffer->push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure)); return INSTALL_CORRUPT; } @@ -624,32 +605,37 @@ static int really_install_package(const std::string& path, bool* wipe_cache, boo ui->Print("Retry attempt: %d\n", retry_count); } ui->SetEnableReboot(false); - int result = - try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature, ui); + auto result = TryUpdateBinary(package, wipe_cache, log_buffer, retry_count, max_temperature, ui); ui->SetEnableReboot(true); ui->Print("\n"); return result; } -int install_package(const std::string& path, bool should_wipe_cache, bool needs_mount, - int retry_count, RecoveryUI* ui) { - CHECK(!path.empty()); - +InstallResult InstallPackage(Package* package, const std::string_view package_id, + bool should_wipe_cache, int retry_count, RecoveryUI* ui) { auto start = std::chrono::system_clock::now(); int start_temperature = GetMaxValueFromThermalZone(); int max_temperature = start_temperature; - int result; + InstallResult result; std::vector<std::string> log_buffer; - if (setup_install_mounts() != 0) { + + ui->Print("Supported API: %d\n", kRecoveryApiVersion); + + ui->Print("Finding update package...\n"); + LOG(INFO) << "Update package id: " << package_id; + if (!package) { + log_buffer.push_back(android::base::StringPrintf("error: %d", kMapFileFailure)); + result = INSTALL_CORRUPT; + } else if (setup_install_mounts() != 0) { LOG(ERROR) << "failed to set up expected mounts for install; aborting"; result = INSTALL_ERROR; } else { bool updater_wipe_cache = false; - result = really_install_package(path, &updater_wipe_cache, needs_mount, &log_buffer, - retry_count, &max_temperature, ui); + result = VerifyAndInstallPackage(package, &updater_wipe_cache, &log_buffer, retry_count, + &max_temperature, ui); should_wipe_cache = should_wipe_cache || updater_wipe_cache; } @@ -677,7 +663,7 @@ int install_package(const std::string& path, bool should_wipe_cache, bool needs_ // The first two lines need to be the package name and install result. std::vector<std::string> log_header = { - path, + std::string(package_id), result == INSTALL_SUCCESS ? "1" : "0", "time_total: " + std::to_string(time_total), "retry: " + std::to_string(retry_count), diff --git a/install/package.cpp b/install/package.cpp index 4402f4855..86fc0647d 100644 --- a/install/package.cpp +++ b/install/package.cpp @@ -40,12 +40,20 @@ class MemoryPackage : public Package { ~MemoryPackage() override; + PackageType GetType() const override { + return PackageType::kMemory; + } + // Memory maps the package file if necessary. Initializes the start address and size of the // package. uint64_t GetPackageSize() const override { return package_size_; } + std::string GetPath() const override { + return path_; + } + bool ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) override; ZipArchiveHandle GetZipArchiveHandle() override; @@ -82,10 +90,18 @@ class FilePackage : public Package { ~FilePackage() override; + PackageType GetType() const override { + return PackageType::kFile; + } + uint64_t GetPackageSize() const override { return package_size_; } + std::string GetPath() const override { + return path_; + } + bool ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) override; ZipArchiveHandle GetZipArchiveHandle() override; @@ -253,7 +269,7 @@ ZipArchiveHandle FilePackage::GetZipArchiveHandle() { return zip_handle_; } - if (auto err = OpenArchiveFd(fd_.get(), path_.c_str(), &zip_handle_); err != 0) { + if (auto err = OpenArchiveFd(fd_.get(), path_.c_str(), &zip_handle_, false); err != 0) { LOG(ERROR) << "Can't open package" << path_ << " : " << ErrorCodeString(err); return nullptr; } diff --git a/install/verifier.cpp b/install/verifier.cpp index 6ba1d77c3..ab750442d 100644 --- a/install/verifier.cpp +++ b/install/verifier.cpp @@ -311,8 +311,7 @@ int verify_file(VerifierInterface* package, const std::vector<Certificate>& keys static std::vector<Certificate> IterateZipEntriesAndSearchForKeys(const ZipArchiveHandle& handle) { void* cookie; - ZipString suffix("x509.pem"); - int32_t iter_status = StartIteration(handle, &cookie, nullptr, &suffix); + int32_t iter_status = StartIteration(handle, &cookie, "", "x509.pem"); if (iter_status != 0) { LOG(ERROR) << "Failed to iterate over entries in the certificate zipfile: " << ErrorCodeString(iter_status); @@ -321,22 +320,21 @@ static std::vector<Certificate> IterateZipEntriesAndSearchForKeys(const ZipArchi std::vector<Certificate> result; - ZipString name; + std::string_view name; ZipEntry entry; while ((iter_status = Next(cookie, &entry, &name)) == 0) { std::vector<uint8_t> pem_content(entry.uncompressed_length); if (int32_t extract_status = ExtractToMemory(handle, &entry, pem_content.data(), pem_content.size()); extract_status != 0) { - LOG(ERROR) << "Failed to extract " << std::string(name.name, name.name + name.name_length); + LOG(ERROR) << "Failed to extract " << name; return {}; } Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); // Aborts the parsing if we fail to load one of the key file. if (!LoadCertificateFromBuffer(pem_content, &cert)) { - LOG(ERROR) << "Failed to load keys from " - << std::string(name.name, name.name + name.name_length); + LOG(ERROR) << "Failed to load keys from " << name; return {}; } diff --git a/install/wipe_device.cpp b/install/wipe_device.cpp new file mode 100644 index 000000000..89d5d31a3 --- /dev/null +++ b/install/wipe_device.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2019 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 "install/wipe_device.h" + +#include <errno.h> +#include <fcntl.h> +#include <linux/fs.h> +#include <stdint.h> +#include <sys/ioctl.h> + +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/strings.h> +#include <android-base/unique_fd.h> +#include <ziparchive/zip_archive.h> + +#include "bootloader_message/bootloader_message.h" +#include "install/install.h" +#include "install/package.h" +#include "recovery_ui/device.h" +#include "recovery_ui/ui.h" + +std::vector<std::string> GetWipePartitionList(Package* wipe_package) { + ZipArchiveHandle zip = wipe_package->GetZipArchiveHandle(); + if (!zip) { + LOG(ERROR) << "Failed to get ZipArchiveHandle"; + return {}; + } + + constexpr char RECOVERY_WIPE_ENTRY_NAME[] = "recovery.wipe"; + + std::string partition_list_content; + ZipEntry entry; + if (FindEntry(zip, RECOVERY_WIPE_ENTRY_NAME, &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."; + + constexpr 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; + auto lines = android::base::Split(partition_list_content, "\n"); + for (const auto& line : lines) { + auto partition = android::base::Trim(line); + // Ignore '#' comment or empty lines. + if (android::base::StartsWith(partition, "#") || partition.empty()) { + continue; + } + result.push_back(line); + } + + return result; +} + +// Secure-wipes a given partition. It uses BLKSECDISCARD, if supported. Otherwise, it goes with +// BLKDISCARD (if device supports BLKDISCARDZEROES) or BLKZEROOUT. +static bool SecureWipePartition(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; + if (std::string err_str; !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, RecoveryUI* ui) { + 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); +} + +bool WipeAbDevice(Device* device, size_t wipe_package_size) { + auto ui = device->GetUI(); + 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(), ui)) { + 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. + SecureWipePartition(partition); + } + return true; +} |