diff options
Diffstat (limited to 'update_verifier')
-rw-r--r-- | update_verifier/update_verifier.cpp | 174 | ||||
-rw-r--r-- | update_verifier/update_verifier_main.cpp | 6 |
2 files changed, 116 insertions, 64 deletions
diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index d3a5185b8..faebbede0 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -45,6 +45,7 @@ #include <unistd.h> #include <algorithm> +#include <future> #include <string> #include <vector> @@ -123,11 +124,6 @@ static bool read_blocks(const std::string& partition, const std::string& range_s LOG(ERROR) << "Failed to find dm block device for " << partition; return false; } - android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY))); - if (fd.get() == -1) { - PLOG(ERROR) << "Error reading " << dm_block_device << " for partition " << partition; - return false; - } // For block range string, first integer 'count' equals 2 * total number of valid ranges, // followed by 'count' number comma separated integers. Every two integers reprensent a @@ -142,73 +138,110 @@ static bool read_blocks(const std::string& partition, const std::string& range_s return false; } - size_t blk_count = 0; - for (size_t i = 1; i < ranges.size(); i += 2) { - unsigned int range_start, range_end; - bool parse_status = android::base::ParseUint(ranges[i], &range_start); - parse_status = parse_status && android::base::ParseUint(ranges[i + 1], &range_end); - if (!parse_status || range_start >= range_end) { - LOG(ERROR) << "Invalid range pair " << ranges[i] << ", " << ranges[i + 1]; - return false; - } + std::vector<std::future<bool>> threads; + size_t thread_num = std::thread::hardware_concurrency() ?: 4; + thread_num = std::min(thread_num, range_count / 2); + size_t group_range_count = range_count / thread_num; - static constexpr size_t BLOCKSIZE = 4096; - if (lseek64(fd.get(), static_cast<off64_t>(range_start) * BLOCKSIZE, SEEK_SET) == -1) { - PLOG(ERROR) << "lseek to " << range_start << " failed"; - return false; - } - - size_t remain = (range_end - range_start) * BLOCKSIZE; - while (remain > 0) { - size_t to_read = std::min(remain, 1024 * BLOCKSIZE); - std::vector<uint8_t> buf(to_read); - if (!android::base::ReadFully(fd.get(), buf.data(), to_read)) { - PLOG(ERROR) << "Failed to read blocks " << range_start << " to " << range_end; + for (size_t t = 0; t < thread_num; t++) { + auto thread_func = [t, group_range_count, &dm_block_device, &ranges, &partition]() { + size_t blk_count = 0; + static constexpr size_t kBlockSize = 4096; + std::vector<uint8_t> buf(1024 * kBlockSize); + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY))); + if (fd.get() == -1) { + PLOG(ERROR) << "Error reading " << dm_block_device << " for partition " << partition; return false; } - remain -= to_read; - } - blk_count += (range_end - range_start); + + for (size_t i = 1 + group_range_count * t; i < group_range_count * (t + 1) + 1; i += 2) { + unsigned int range_start, range_end; + bool parse_status = android::base::ParseUint(ranges[i], &range_start); + parse_status = parse_status && android::base::ParseUint(ranges[i + 1], &range_end); + if (!parse_status || range_start >= range_end) { + LOG(ERROR) << "Invalid range pair " << ranges[i] << ", " << ranges[i + 1]; + return false; + } + + if (lseek64(fd.get(), static_cast<off64_t>(range_start) * kBlockSize, SEEK_SET) == -1) { + PLOG(ERROR) << "lseek to " << range_start << " failed"; + return false; + } + + size_t remain = (range_end - range_start) * kBlockSize; + while (remain > 0) { + size_t to_read = std::min(remain, 1024 * kBlockSize); + if (!android::base::ReadFully(fd.get(), buf.data(), to_read)) { + PLOG(ERROR) << "Failed to read blocks " << range_start << " to " << range_end; + return false; + } + remain -= to_read; + } + blk_count += (range_end - range_start); + } + LOG(INFO) << "Finished reading " << blk_count << " blocks on " << dm_block_device; + return true; + }; + + threads.emplace_back(std::async(std::launch::async, thread_func)); } - LOG(INFO) << "Finished reading " << blk_count << " blocks on " << dm_block_device; - return true; + bool ret = true; + for (auto& t : threads) { + ret = t.get() && ret; + } + LOG(INFO) << "Finished reading blocks on " << dm_block_device << " with " << thread_num + << " threads."; + return ret; } +// Returns true to indicate a passing verification (or the error should be ignored); Otherwise +// returns false on fatal errors, where we should reject the current boot and trigger a fallback. +// Note that update_verifier should be backward compatible to not reject care_map.txt from old +// releases, which could otherwise fail to boot into the new release. For example, we've changed +// the care_map format between N and O. An O update_verifier would fail to work with N +// care_map.txt. This could be a result of sideloading an O OTA while the device having a pending N +// update. bool verify_image(const std::string& care_map_name) { - android::base::unique_fd care_map_fd(TEMP_FAILURE_RETRY(open(care_map_name.c_str(), O_RDONLY))); - // If the device is flashed before the current boot, it may not have care_map.txt - // in /data/ota_package. To allow the device to continue booting in this situation, - // we should print a warning and skip the block verification. - if (care_map_fd.get() == -1) { - PLOG(WARNING) << "Failed to open " << care_map_name; - return true; - } - // Care map file has four lines (two lines if vendor partition is not present): - // First line has the block partition name (system/vendor). - // Second line holds all ranges of blocks to verify. - // The next two lines have the same format but for vendor partition. - std::string file_content; - if (!android::base::ReadFdToString(care_map_fd.get(), &file_content)) { - LOG(ERROR) << "Error reading care map contents to string."; - return false; - } + android::base::unique_fd care_map_fd(TEMP_FAILURE_RETRY(open(care_map_name.c_str(), O_RDONLY))); + // If the device is flashed before the current boot, it may not have care_map.txt + // in /data/ota_package. To allow the device to continue booting in this situation, + // we should print a warning and skip the block verification. + if (care_map_fd.get() == -1) { + PLOG(WARNING) << "Failed to open " << care_map_name; + return true; + } + // Care map file has four lines (two lines if vendor partition is not present): + // First line has the block partition name (system/vendor). + // Second line holds all ranges of blocks to verify. + // The next two lines have the same format but for vendor partition. + std::string file_content; + if (!android::base::ReadFdToString(care_map_fd.get(), &file_content)) { + LOG(ERROR) << "Error reading care map contents to string."; + return false; + } - std::vector<std::string> lines; - lines = android::base::Split(android::base::Trim(file_content), "\n"); - if (lines.size() != 2 && lines.size() != 4) { - LOG(ERROR) << "Invalid lines in care_map: found " << lines.size() - << " lines, expecting 2 or 4 lines."; - return false; - } + std::vector<std::string> lines; + lines = android::base::Split(android::base::Trim(file_content), "\n"); + if (lines.size() != 2 && lines.size() != 4) { + LOG(ERROR) << "Invalid lines in care_map: found " << lines.size() + << " lines, expecting 2 or 4 lines."; + return false; + } - for (size_t i = 0; i < lines.size(); i += 2) { - if (!read_blocks(lines[i], lines[i+1])) { - return false; - } + for (size_t i = 0; i < lines.size(); i += 2) { + // We're seeing an N care_map.txt. Skip the verification since it's not compatible with O + // update_verifier (the last few metadata blocks can't be read via device mapper). + if (android::base::StartsWith(lines[i], "/dev/block/")) { + LOG(WARNING) << "Found legacy care_map.txt; skipped."; + return true; + } + if (!read_blocks(lines[i], lines[i+1])) { + return false; } + } - return true; + return true; } static int reboot_device() { @@ -239,23 +272,36 @@ int update_verifier(int argc, char** argv) { // The current slot has not booted successfully. #if defined(PRODUCT_SUPPORTS_VERITY) || defined(BOARD_AVB_ENABLE) + bool skip_verification = false; std::string verity_mode = android::base::GetProperty("ro.boot.veritymode", ""); if (verity_mode.empty()) { + // With AVB it's possible to disable verification entirely and + // in this case ro.boot.veritymode is empty. +#if defined(BOARD_AVB_ENABLE) + LOG(WARNING) << "verification has been disabled; marking without verification."; + skip_verification = true; +#else LOG(ERROR) << "Failed to get dm-verity mode."; return reboot_device(); +#endif } else if (android::base::EqualsIgnoreCase(verity_mode, "eio")) { // We shouldn't see verity in EIO mode if the current slot hasn't booted successfully before. // Continue the verification until we fail to read some blocks. LOG(WARNING) << "Found dm-verity in EIO mode."; + } else if (android::base::EqualsIgnoreCase(verity_mode, "disabled")) { + LOG(WARNING) << "dm-verity in disabled mode; marking without verification."; + skip_verification = true; } else if (verity_mode != "enforcing") { LOG(ERROR) << "Unexpected dm-verity mode : " << verity_mode << ", expecting enforcing."; return reboot_device(); } - static constexpr auto CARE_MAP_FILE = "/data/ota_package/care_map.txt"; - if (!verify_image(CARE_MAP_FILE)) { - LOG(ERROR) << "Failed to verify all blocks in care map file."; - return reboot_device(); + if (!skip_verification) { + static constexpr auto CARE_MAP_FILE = "/data/ota_package/care_map.txt"; + if (!verify_image(CARE_MAP_FILE)) { + LOG(ERROR) << "Failed to verify all blocks in care map file."; + return reboot_device(); + } } #else LOG(WARNING) << "dm-verity not enabled; marking without verification."; diff --git a/update_verifier/update_verifier_main.cpp b/update_verifier/update_verifier_main.cpp index 46e8bbb59..9dd5a0cc4 100644 --- a/update_verifier/update_verifier_main.cpp +++ b/update_verifier/update_verifier_main.cpp @@ -16,8 +16,14 @@ // See the comments in update_verifier.cpp. +#include <android-base/logging.h> + #include "update_verifier/update_verifier.h" int main(int argc, char** argv) { + // Set up update_verifier logging to be written to kmsg; because we may not have Logd during + // boot time. + android::base::InitLogging(argv, &android::base::KernelLogger); + return update_verifier(argc, argv); } |