diff options
-rw-r--r-- | applypatch/applypatch.cpp | 45 | ||||
-rw-r--r-- | applypatch/bspatch.cpp | 14 | ||||
-rw-r--r-- | applypatch/imgpatch.cpp | 26 | ||||
-rw-r--r-- | applypatch/include/applypatch/applypatch.h | 19 | ||||
-rw-r--r-- | applypatch/include/applypatch/imgpatch.h | 9 | ||||
-rw-r--r-- | edify/parser.yy | 3 | ||||
-rw-r--r-- | install.cpp | 52 | ||||
-rw-r--r-- | otautil/Android.mk | 12 | ||||
-rw-r--r-- | otautil/ThermalUtil.cpp | 80 | ||||
-rw-r--r-- | otautil/ThermalUtil.h | 24 | ||||
-rw-r--r-- | tests/component/imgdiff_test.cpp | 80 | ||||
-rw-r--r-- | tests/component/uncrypt_test.cpp | 185 | ||||
-rw-r--r-- | updater/blockimg.cpp | 511 |
13 files changed, 574 insertions, 486 deletions
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index 7be3fdbde..51bf3932a 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -27,6 +27,7 @@ #include <sys/types.h> #include <unistd.h> +#include <functional> #include <memory> #include <string> #include <utility> @@ -42,7 +43,7 @@ #include "print_sha1.h" static int LoadPartitionContents(const std::string& filename, FileContents* file); -static ssize_t FileSink(const unsigned char* data, ssize_t len, void* token); +static size_t FileSink(const unsigned char* data, size_t len, int fd); static int GenerateTarget(const FileContents& source_file, const std::unique_ptr<Value>& patch, const std::string& target_filename, const uint8_t target_sha1[SHA_DIGEST_LENGTH], const Value* bonus_data); @@ -194,8 +195,8 @@ int SaveFileContents(const char* filename, const FileContents* file) { return -1; } - ssize_t bytes_written = FileSink(file->data.data(), file->data.size(), &fd); - if (bytes_written != static_cast<ssize_t>(file->data.size())) { + size_t bytes_written = FileSink(file->data.data(), file->data.size(), fd); + if (bytes_written != file->data.size()) { printf("short write of \"%s\" (%zd bytes of %zu): %s\n", filename, bytes_written, file->data.size(), strerror(errno)); return -1; @@ -433,25 +434,17 @@ int ShowLicenses() { return 0; } -ssize_t FileSink(const unsigned char* data, ssize_t len, void* token) { - int fd = *static_cast<int*>(token); - ssize_t done = 0; - ssize_t wrote; - while (done < len) { - wrote = TEMP_FAILURE_RETRY(ota_write(fd, data+done, len-done)); - if (wrote == -1) { - printf("error writing %zd bytes: %s\n", (len-done), strerror(errno)); - return done; - } - done += wrote; +static size_t FileSink(const unsigned char* data, size_t len, int fd) { + size_t done = 0; + while (done < len) { + ssize_t wrote = TEMP_FAILURE_RETRY(ota_write(fd, data + done, len - done)); + if (wrote == -1) { + printf("error writing %zd bytes: %s\n", (len - done), strerror(errno)); + return done; } - return done; -} - -ssize_t MemorySink(const unsigned char* data, ssize_t len, void* token) { - std::string* s = static_cast<std::string*>(token); - s->append(reinterpret_cast<const char*>(data), len); - return len; + done += wrote; + } + return done; } // Return the amount of free space (in bytes) on the filesystem @@ -647,9 +640,11 @@ static int GenerateTarget(const FileContents& source_file, const std::unique_ptr } // We store the decoded output in memory. - SinkFn sink = MemorySink; std::string memory_sink_str; // Don't need to reserve space. - void* token = &memory_sink_str; + SinkFn sink = [&memory_sink_str](const unsigned char* data, size_t len) { + memory_sink_str.append(reinterpret_cast<const char*>(data), len); + return len; + }; SHA_CTX ctx; SHA1_Init(&ctx); @@ -657,10 +652,10 @@ static int GenerateTarget(const FileContents& source_file, const std::unique_ptr int result; if (use_bsdiff) { result = ApplyBSDiffPatch(source_file.data.data(), source_file.data.size(), patch.get(), 0, - sink, token, &ctx); + sink, &ctx); } else { result = ApplyImagePatch(source_file.data.data(), source_file.data.size(), patch.get(), sink, - token, &ctx, bonus_data); + &ctx, bonus_data); } if (result != 0) { diff --git a/applypatch/bspatch.cpp b/applypatch/bspatch.cpp index 9920c2be1..f75a2c680 100644 --- a/applypatch/bspatch.cpp +++ b/applypatch/bspatch.cpp @@ -24,9 +24,9 @@ #include <sys/types.h> #include <bspatch.h> +#include <openssl/sha.h> #include "applypatch/applypatch.h" -#include "openssl/sha.h" void ShowBSDiffLicense() { puts("The bsdiff library used herein is:\n" @@ -60,10 +60,10 @@ void ShowBSDiffLicense() { ); } -int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size, const Value* patch, - ssize_t patch_offset, SinkFn sink, void* token, SHA_CTX* ctx) { - auto sha_sink = [&](const uint8_t* data, size_t len) { - len = sink(data, len, token); +int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value* patch, + size_t patch_offset, SinkFn sink, SHA_CTX* ctx) { + auto sha_sink = [&sink, &ctx](const uint8_t* data, size_t len) { + len = sink(data, len); if (ctx) SHA1_Update(ctx, data, len); return len; }; @@ -72,8 +72,8 @@ int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size, const Valu patch->data.size(), sha_sink); } -int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, const Value* patch, - ssize_t patch_offset, std::vector<unsigned char>* new_data) { +int ApplyBSDiffPatchMem(const unsigned char* old_data, size_t old_size, const Value* patch, + size_t patch_offset, std::vector<unsigned char>* new_data) { auto vector_sink = [new_data](const uint8_t* data, size_t len) { new_data->insert(new_data->end(), data, data + len); return len; diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp index adcc61fd6..7d8b7361c 100644 --- a/applypatch/imgpatch.cpp +++ b/applypatch/imgpatch.cpp @@ -43,12 +43,11 @@ static inline int32_t Read4(const void *address) { return android::base::get_unaligned<int32_t>(address); } -int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, - const unsigned char* patch_data, ssize_t patch_size, - SinkFn sink, void* token) { +int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsigned char* patch_data, + size_t patch_size, SinkFn sink) { Value patch(VAL_BLOB, std::string(reinterpret_cast<const char*>(patch_data), patch_size)); - return ApplyImagePatch(old_data, old_size, &patch, sink, token, nullptr, nullptr); + return ApplyImagePatch(old_data, old_size, &patch, sink, nullptr, nullptr); } /* @@ -57,8 +56,8 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, * file, and update the SHA context with the output data as well. * Return 0 on success. */ -int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value* patch, - SinkFn sink, void* token, SHA_CTX* ctx, const Value* bonus_data) { +int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* patch, SinkFn sink, + SHA_CTX* ctx, const Value* bonus_data) { if (patch->data.size() < 12) { printf("patch too short to contain header\n"); return -1; @@ -97,11 +96,11 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value size_t src_len = static_cast<size_t>(Read8(normal_header + 8)); size_t patch_offset = static_cast<size_t>(Read8(normal_header + 16)); - if (src_start + src_len > static_cast<size_t>(old_size)) { + if (src_start + src_len > old_size) { printf("source data too short\n"); return -1; } - ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink, token, ctx); + ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink, ctx); } else if (type == CHUNK_RAW) { const char* raw_header = &patch->data[pos]; pos += 4; @@ -110,15 +109,14 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value return -1; } - ssize_t data_len = Read4(raw_header); + size_t data_len = static_cast<size_t>(Read4(raw_header)); if (pos + data_len > patch->data.size()) { printf("failed to read chunk %d raw data\n", i); return -1; } if (ctx) SHA1_Update(ctx, &patch->data[pos], data_len); - if (sink(reinterpret_cast<const unsigned char*>(&patch->data[pos]), data_len, token) != - data_len) { + if (sink(reinterpret_cast<const unsigned char*>(&patch->data[pos]), data_len) != data_len) { printf("failed to write chunk %d raw data\n", i); return -1; } @@ -143,7 +141,7 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value int memLevel = Read4(deflate_header + 52); int strategy = Read4(deflate_header + 56); - if (src_start + src_len > static_cast<size_t>(old_size)) { + if (src_start + src_len > old_size) { printf("source data too short\n"); return -1; } @@ -240,9 +238,9 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value strm.avail_out = temp_data.size(); strm.next_out = temp_data.data(); ret = deflate(&strm, Z_FINISH); - ssize_t have = temp_data.size() - strm.avail_out; + size_t have = temp_data.size() - strm.avail_out; - if (sink(temp_data.data(), have, token) != have) { + if (sink(temp_data.data(), have) != have) { printf("failed to write %zd compressed bytes to output\n", have); return -1; } diff --git a/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h index 4489decb6..da55432d5 100644 --- a/applypatch/include/applypatch/applypatch.h +++ b/applypatch/include/applypatch/applypatch.h @@ -20,6 +20,7 @@ #include <stdint.h> #include <sys/stat.h> +#include <functional> #include <memory> #include <string> #include <vector> @@ -41,7 +42,7 @@ struct FileContents { // and use it as the source instead. #define CACHE_TEMP_SOURCE "/cache/saved.file" -typedef ssize_t (*SinkFn)(const unsigned char*, ssize_t, void*); +using SinkFn = std::function<size_t(const unsigned char*, size_t)>; // applypatch.cpp int ShowLicenses(); @@ -66,18 +67,14 @@ int SaveFileContents(const char* filename, const FileContents* file); // bspatch.cpp void ShowBSDiffLicense(); -int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size, - const Value* patch, ssize_t patch_offset, - SinkFn sink, void* token, SHA_CTX* ctx); -int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, - const Value* patch, ssize_t patch_offset, - std::vector<unsigned char>* new_data); +int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value* patch, + size_t patch_offset, SinkFn sink, SHA_CTX* ctx); +int ApplyBSDiffPatchMem(const unsigned char* old_data, size_t old_size, const Value* patch, + size_t patch_offset, std::vector<unsigned char>* new_data); // imgpatch.cpp -int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, - const Value* patch, - SinkFn sink, void* token, SHA_CTX* ctx, - const Value* bonus_data); +int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* patch, SinkFn sink, + SHA_CTX* ctx, const Value* bonus_data); // freecache.cpp int MakeFreeSpaceOnCache(size_t bytes_needed); diff --git a/applypatch/include/applypatch/imgpatch.h b/applypatch/include/applypatch/imgpatch.h index 6549f79f0..07c66094f 100644 --- a/applypatch/include/applypatch/imgpatch.h +++ b/applypatch/include/applypatch/imgpatch.h @@ -19,10 +19,11 @@ #include <sys/types.h> -using SinkFn = ssize_t (*)(const unsigned char*, ssize_t, void*); +#include <functional> -int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, - const unsigned char* patch_data, ssize_t patch_size, - SinkFn sink, void* token); +using SinkFn = std::function<size_t(const unsigned char*, size_t)>; + +int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsigned char* patch_data, + size_t patch_size, SinkFn sink); #endif // _APPLYPATCH_IMGPATCH_H diff --git a/edify/parser.yy b/edify/parser.yy index 97205fe3b..b1685eb1f 100644 --- a/edify/parser.yy +++ b/edify/parser.yy @@ -23,6 +23,8 @@ #include <string> #include <vector> +#include <android-base/macros.h> + #include "expr.h" #include "yydefs.h" #include "parser.h" @@ -121,6 +123,7 @@ arglist: /* empty */ { $$->emplace_back($1); } | arglist ',' expr { + UNUSED($1); $$->push_back(std::unique_ptr<Expr>($3)); } ; diff --git a/install.cpp b/install.cpp index 7cef44a37..0a2fa3ca4 100644 --- a/install.cpp +++ b/install.cpp @@ -26,11 +26,15 @@ #include <sys/wait.h> #include <unistd.h> +#include <algorithm> #include <chrono> +#include <condition_variable> #include <functional> #include <limits> #include <map> +#include <mutex> #include <string> +#include <thread> #include <vector> #include <android-base/file.h> @@ -46,10 +50,13 @@ #include "error_code.h" #include "minui/minui.h" #include "otautil/SysUtil.h" +#include "otautil/ThermalUtil.h" #include "roots.h" #include "ui.h" #include "verifier.h" +using namespace std::chrono_literals; + #define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary" static constexpr const char* AB_OTA_PAYLOAD_PROPERTIES = "payload_properties.txt"; static constexpr const char* AB_OTA_PAYLOAD = "payload.bin"; @@ -63,6 +70,8 @@ static constexpr float VERIFICATION_PROGRESS_FRACTION = 0.25; static constexpr float DEFAULT_FILES_PROGRESS_FRACTION = 0.4; static constexpr float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1; +static std::condition_variable finish_log_temperature; + // This function parses and returns the build.version.incremental static int parse_build_number(const std::string& str) { size_t pos = str.find('='); @@ -299,9 +308,19 @@ update_binary_command(const char* path, ZipArchiveHandle zip, int retry_count, } #endif // !AB_OTA_UPDATER +static void log_max_temperature(int* max_temperature) { + CHECK(max_temperature != nullptr); + std::mutex mtx; + std::unique_lock<std::mutex> lck(mtx); + while (finish_log_temperature.wait_for(lck, 20s) == std::cv_status::timeout) { + *max_temperature = std::max(*max_temperature, GetMaxValueFromThermalZone()); + } +} + // If the package contains an update binary, extract it and run it. static int try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_cache, - std::vector<std::string>& log_buffer, int retry_count) { + std::vector<std::string>& log_buffer, int retry_count, + int* max_temperature) { read_source_target_build(zip, log_buffer); int pipefd[2]; @@ -392,6 +411,8 @@ static int try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_ } close(pipefd[1]); + std::thread temperature_logger(log_max_temperature, max_temperature); + *wipe_cache = false; bool retry_update = false; @@ -453,6 +474,10 @@ static int try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_ int status; waitpid(pid, &status, 0); + + finish_log_temperature.notify_one(); + temperature_logger.join(); + if (retry_update) { return INSTALL_RETRY; } @@ -466,7 +491,7 @@ static int try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_ static int really_install_package(const char *path, bool* wipe_cache, bool needs_mount, - std::vector<std::string>& log_buffer, int retry_count) + std::vector<std::string>& log_buffer, int retry_count, int* max_temperature) { ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); ui->Print("Finding update package...\n"); @@ -517,7 +542,7 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount, ui->Print("Retry attempt: %d\n", retry_count); } ui->SetEnableReboot(false); - int result = try_update_binary(path, zip, wipe_cache, log_buffer, retry_count); + int result = try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature); ui->SetEnableReboot(true); ui->Print("\n"); @@ -533,13 +558,17 @@ install_package(const char* path, bool* wipe_cache, const char* install_file, modified_flash = true; auto start = std::chrono::system_clock::now(); + int start_temperature = GetMaxValueFromThermalZone(); + int max_temperature = start_temperature; + int result; std::vector<std::string> log_buffer; if (setup_install_mounts() != 0) { LOG(ERROR) << "failed to set up expected mounts for install; aborting"; result = INSTALL_ERROR; } else { - result = really_install_package(path, wipe_cache, needs_mount, log_buffer, retry_count); + result = really_install_package(path, wipe_cache, needs_mount, log_buffer, retry_count, + &max_temperature); } // Measure the time spent to apply OTA update in seconds. @@ -570,8 +599,21 @@ install_package(const char* path, bool* wipe_cache, const char* install_file, "time_total: " + std::to_string(time_total), "retry: " + std::to_string(retry_count), }; + + int end_temperature = GetMaxValueFromThermalZone(); + max_temperature = std::max(end_temperature, max_temperature); + if (start_temperature > 0) { + log_buffer.push_back("temperature_start: " + std::to_string(start_temperature)); + } + if (end_temperature > 0) { + log_buffer.push_back("temperature_end: " + std::to_string(end_temperature)); + } + if (max_temperature > 0) { + log_buffer.push_back("temperature_max: " + std::to_string(max_temperature)); + } + std::string log_content = android::base::Join(log_header, "\n") + "\n" + - android::base::Join(log_buffer, "\n"); + android::base::Join(log_buffer, "\n") + "\n"; if (!android::base::WriteStringToFile(log_content, install_file)) { PLOG(ERROR) << "failed to write " << install_file; } diff --git a/otautil/Android.mk b/otautil/Android.mk index e602f19ee..f7ca9a9ee 100644 --- a/otautil/Android.mk +++ b/otautil/Android.mk @@ -18,12 +18,16 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ SysUtil.cpp \ DirUtil.cpp \ - ZipUtil.cpp + ZipUtil.cpp \ + ThermalUtil.cpp -LOCAL_STATIC_LIBRARIES := libselinux libbase +LOCAL_STATIC_LIBRARIES := \ + libselinux \ + libbase LOCAL_MODULE := libotautil - -LOCAL_CFLAGS += -Werror -Wall +LOCAL_CFLAGS := \ + -Werror \ + -Wall include $(BUILD_STATIC_LIBRARY) diff --git a/otautil/ThermalUtil.cpp b/otautil/ThermalUtil.cpp new file mode 100644 index 000000000..13d36432a --- /dev/null +++ b/otautil/ThermalUtil.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2017 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 "ThermalUtil.h" + +#include <dirent.h> +#include <stdio.h> + +#include <algorithm> +#include <string> +#include <vector> + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/parseint.h> +#include <android-base/strings.h> + +static constexpr auto THERMAL_PREFIX = "/sys/class/thermal/"; + +static int thermal_filter(const dirent* de) { + if (android::base::StartsWith(de->d_name, "thermal_zone")) { + return 1; + } + return 0; +} + +static std::vector<std::string> InitThermalPaths() { + dirent** namelist; + int n = scandir(THERMAL_PREFIX, &namelist, thermal_filter, alphasort); + if (n == -1) { + PLOG(ERROR) << "Failed to scandir " << THERMAL_PREFIX; + return {}; + } + if (n == 0) { + LOG(ERROR) << "Failed to find CPU thermal info in " << THERMAL_PREFIX; + return {}; + } + + std::vector<std::string> thermal_paths; + while (n--) { + thermal_paths.push_back(THERMAL_PREFIX + std::string(namelist[n]->d_name) + "/temp"); + free(namelist[n]); + } + free(namelist); + return thermal_paths; +} + +int GetMaxValueFromThermalZone() { + static std::vector<std::string> thermal_paths = InitThermalPaths(); + int max_temperature = -1; + for (const auto& path : thermal_paths) { + std::string content; + if (!android::base::ReadFileToString(path, &content)) { + PLOG(WARNING) << "Failed to read " << path; + continue; + } + + int temperature; + if (!android::base::ParseInt(android::base::Trim(content), &temperature)) { + LOG(WARNING) << "Failed to parse integer in " << content; + continue; + } + max_temperature = std::max(temperature, max_temperature); + } + LOG(INFO) << "current maximum temperature: " << max_temperature; + return max_temperature; +}
\ No newline at end of file diff --git a/otautil/ThermalUtil.h b/otautil/ThermalUtil.h new file mode 100644 index 000000000..43ab55940 --- /dev/null +++ b/otautil/ThermalUtil.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 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. + */ + +#ifndef OTAUTIL_THERMALUTIL_H +#define OTAUTIL_THERMALUTIL_H + +// We can find the temperature reported by all sensors in /sys/class/thermal/thermal_zone*/temp. +// Their values are in millidegree Celsius; and we will log the maximum one. +int GetMaxValueFromThermalZone(); + +#endif // OTAUTIL_THERMALUTIL_H diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index 2f648501c..7d00a3d53 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <stdio.h> + #include <string> #include <vector> @@ -27,12 +29,6 @@ using android::base::get_unaligned; -static ssize_t MemorySink(const unsigned char* data, ssize_t len, void* token) { - std::string* s = static_cast<std::string*>(token); - s->append(reinterpret_cast<const char*>(data), len); - return len; -} - // Sanity check for the given imgdiff patch header. static void verify_patch_header(const std::string& patch, size_t* num_normal, size_t* num_raw, size_t* num_deflate) { @@ -79,6 +75,18 @@ static void verify_patch_header(const std::string& patch, size_t* num_normal, si if (num_deflate != nullptr) *num_deflate = deflate; } +static void verify_patched_image(const std::string& src, const std::string& patch, + const std::string& tgt) { + std::string patched; + ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(), + reinterpret_cast<const unsigned char*>(patch.data()), patch.size(), + [&patched](const unsigned char* data, size_t len) { + patched.append(reinterpret_cast<const char*>(data), len); + return len; + })); + ASSERT_EQ(tgt, patched); +} + TEST(ImgdiffTest, invalid_args) { // Insufficient inputs. ASSERT_EQ(2, imgdiff(1, (const char* []){ "imgdiff" })); @@ -124,11 +132,7 @@ TEST(ImgdiffTest, image_mode_smoke) { ASSERT_EQ(0U, num_deflate); ASSERT_EQ(1U, num_raw); - std::string patched; - ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(), - reinterpret_cast<const unsigned char*>(patch.data()), patch.size(), - MemorySink, &patched)); - ASSERT_EQ(tgt, patched); + verify_patched_image(src, patch, tgt); } TEST(ImgdiffTest, zip_mode_smoke_store) { @@ -177,11 +181,7 @@ TEST(ImgdiffTest, zip_mode_smoke_store) { ASSERT_EQ(0U, num_deflate); ASSERT_EQ(1U, num_raw); - std::string patched; - ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(), - reinterpret_cast<const unsigned char*>(patch.data()), patch.size(), - MemorySink, &patched)); - ASSERT_EQ(tgt, patched); + verify_patched_image(src, patch, tgt); } TEST(ImgdiffTest, zip_mode_smoke_compressed) { @@ -230,11 +230,7 @@ TEST(ImgdiffTest, zip_mode_smoke_compressed) { ASSERT_EQ(1U, num_deflate); ASSERT_EQ(2U, num_raw); - std::string patched; - ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(), - reinterpret_cast<const unsigned char*>(patch.data()), patch.size(), - MemorySink, &patched)); - ASSERT_EQ(tgt, patched); + verify_patched_image(src, patch, tgt); } TEST(ImgdiffTest, zip_mode_smoke_trailer_zeros) { @@ -286,11 +282,7 @@ TEST(ImgdiffTest, zip_mode_smoke_trailer_zeros) { ASSERT_EQ(1U, num_deflate); ASSERT_EQ(2U, num_raw); - std::string patched; - ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(), - reinterpret_cast<const unsigned char*>(patch.data()), patch.size(), - MemorySink, &patched)); - ASSERT_EQ(tgt, patched); + verify_patched_image(src, patch, tgt); } TEST(ImgdiffTest, image_mode_simple) { @@ -333,11 +325,7 @@ TEST(ImgdiffTest, image_mode_simple) { ASSERT_EQ(1U, num_deflate); ASSERT_EQ(2U, num_raw); - std::string patched; - ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(), - reinterpret_cast<const unsigned char*>(patch.data()), patch.size(), - MemorySink, &patched)); - ASSERT_EQ(tgt, patched); + verify_patched_image(src, patch, tgt); } TEST(ImgdiffTest, image_mode_different_num_chunks) { @@ -413,11 +401,7 @@ TEST(ImgdiffTest, image_mode_merge_chunks) { ASSERT_EQ(1U, num_deflate); ASSERT_EQ(2U, num_raw); - std::string patched; - ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(), - reinterpret_cast<const unsigned char*>(patch.data()), patch.size(), - MemorySink, &patched)); - ASSERT_EQ(tgt, patched); + verify_patched_image(src, patch, tgt); } TEST(ImgdiffTest, image_mode_spurious_magic) { @@ -454,11 +438,7 @@ TEST(ImgdiffTest, image_mode_spurious_magic) { ASSERT_EQ(0U, num_deflate); ASSERT_EQ(1U, num_raw); - std::string patched; - ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(), - reinterpret_cast<const unsigned char*>(patch.data()), patch.size(), - MemorySink, &patched)); - ASSERT_EQ(tgt, patched); + verify_patched_image(src, patch, tgt); } TEST(ImgdiffTest, image_mode_short_input1) { @@ -494,11 +474,7 @@ TEST(ImgdiffTest, image_mode_short_input1) { ASSERT_EQ(0U, num_deflate); ASSERT_EQ(1U, num_raw); - std::string patched; - ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(), - reinterpret_cast<const unsigned char*>(patch.data()), patch.size(), - MemorySink, &patched)); - ASSERT_EQ(tgt, patched); + verify_patched_image(src, patch, tgt); } TEST(ImgdiffTest, image_mode_short_input2) { @@ -534,11 +510,7 @@ TEST(ImgdiffTest, image_mode_short_input2) { ASSERT_EQ(0U, num_deflate); ASSERT_EQ(1U, num_raw); - std::string patched; - ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(), - reinterpret_cast<const unsigned char*>(patch.data()), patch.size(), - MemorySink, &patched)); - ASSERT_EQ(tgt, patched); + verify_patched_image(src, patch, tgt); } TEST(ImgdiffTest, image_mode_single_entry_long) { @@ -577,9 +549,5 @@ TEST(ImgdiffTest, image_mode_single_entry_long) { ASSERT_EQ(0U, num_deflate); ASSERT_EQ(0U, num_raw); - std::string patched; - ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(), - reinterpret_cast<const unsigned char*>(patch.data()), patch.size(), - MemorySink, &patched)); - ASSERT_EQ(tgt, patched); + verify_patched_image(src, patch, tgt); } diff --git a/tests/component/uncrypt_test.cpp b/tests/component/uncrypt_test.cpp index 4f2b8164f..5e057e129 100644 --- a/tests/component/uncrypt_test.cpp +++ b/tests/component/uncrypt_test.cpp @@ -25,12 +25,15 @@ #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/properties.h> +#include <android-base/test_utils.h> #include <android-base/unique_fd.h> #include <bootloader_message/bootloader_message.h> #include <gtest/gtest.h> #include "common/component_test_util.h" +using namespace std::string_literals; + static const std::string UNCRYPT_SOCKET = "/dev/socket/uncrypt"; static const std::string INIT_SVC_SETUP_BCB = "init.svc.setup-bcb"; static const std::string INIT_SVC_CLEAR_BCB = "init.svc.clear-bcb"; @@ -65,128 +68,104 @@ class UncryptTest : public ::testing::Test { has_misc = parse_misc(); } - bool has_misc; -}; - -TEST_F(UncryptTest, setup_bcb) { - if (!has_misc) { - GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; - return; - } - - // Trigger the setup-bcb service. - ASSERT_TRUE(android::base::SetProperty("ctl.start", "setup-bcb")); - - // Test tends to be flaky if proceeding immediately ("Transport endpoint is not connected"). - sleep(1); - - struct sockaddr_un un = {}; - un.sun_family = AF_UNIX; - strlcpy(un.sun_path, UNCRYPT_SOCKET.c_str(), sizeof(un.sun_path)); - - int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); - ASSERT_NE(-1, sockfd); - - // Connect to the uncrypt socket. - bool success = false; - for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) { - if (connect(sockfd, reinterpret_cast<struct sockaddr*>(&un), sizeof(struct sockaddr_un)) != 0) { - success = true; - break; + void SetupOrClearBcb(bool isSetup, const std::string& message, + const std::string& message_in_bcb) const { + if (!has_misc) { + GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; + return; } - sleep(1); - } - ASSERT_TRUE(success); - - // Send out the BCB message. - std::string message = "--update_message=abc value"; - std::string message_in_bcb = "recovery\n--update_message=abc value\n"; - int length = static_cast<int>(message.size()); - int length_out = htonl(length); - ASSERT_TRUE(android::base::WriteFully(sockfd, &length_out, sizeof(int))) - << "Failed to write length: " << strerror(errno); - ASSERT_TRUE(android::base::WriteFully(sockfd, message.data(), length)) - << "Failed to write message: " << strerror(errno); - - // Check the status code from uncrypt. - int status; - ASSERT_TRUE(android::base::ReadFully(sockfd, &status, sizeof(int))); - ASSERT_EQ(100U, ntohl(status)); - // Ack having received the status code. - int code = 0; - ASSERT_TRUE(android::base::WriteFully(sockfd, &code, sizeof(int))); + // Trigger the setup-bcb service. + ASSERT_TRUE(android::base::SetProperty("ctl.start", isSetup ? "setup-bcb" : "clear-bcb")); - ASSERT_EQ(0, close(sockfd)); + // Test tends to be flaky if proceeding immediately ("Transport endpoint is not connected"). + sleep(1); - ASSERT_TRUE(android::base::SetProperty("ctl.stop", "setup-bcb")); + sockaddr_un un = {}; + un.sun_family = AF_UNIX; + strlcpy(un.sun_path, UNCRYPT_SOCKET.c_str(), sizeof(un.sun_path)); - // Verify the message by reading from BCB directly. - bootloader_message boot; - std::string err; - ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err; + int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + ASSERT_NE(-1, sockfd); - ASSERT_EQ("boot-recovery", std::string(boot.command)); - ASSERT_EQ(message_in_bcb, std::string(boot.recovery)); + // Connect to the uncrypt socket. + bool success = false; + for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) { + if (connect(sockfd, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un)) != 0) { + success = true; + break; + } + sleep(1); + } + ASSERT_TRUE(success); + + if (isSetup) { + // Send out the BCB message. + int length = static_cast<int>(message.size()); + int length_out = htonl(length); + ASSERT_TRUE(android::base::WriteFully(sockfd, &length_out, sizeof(int))) + << "Failed to write length: " << strerror(errno); + ASSERT_TRUE(android::base::WriteFully(sockfd, message.data(), length)) + << "Failed to write message: " << strerror(errno); + } - // The rest of the boot.recovery message should be zero'd out. - ASSERT_LE(message_in_bcb.size(), sizeof(boot.recovery)); - size_t left = sizeof(boot.recovery) - message_in_bcb.size(); - ASSERT_EQ(std::string(left, '\0'), std::string(&boot.recovery[message_in_bcb.size()], left)); + // Check the status code from uncrypt. + int status; + ASSERT_TRUE(android::base::ReadFully(sockfd, &status, sizeof(int))); + ASSERT_EQ(100U, ntohl(status)); - // Clear the BCB. - ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err; -} + // Ack having received the status code. + int code = 0; + ASSERT_TRUE(android::base::WriteFully(sockfd, &code, sizeof(int))); -TEST_F(UncryptTest, clear_bcb) { - if (!has_misc) { - GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; - return; - } + ASSERT_EQ(0, close(sockfd)); - // Trigger the clear-bcb service. - ASSERT_TRUE(android::base::SetProperty("ctl.start", "clear-bcb")); + ASSERT_TRUE(android::base::SetProperty("ctl.stop", isSetup ? "setup-bcb" : "clear-bcb")); - // Test tends to be flaky if proceeding immediately ("Transport endpoint is not connected"). - sleep(1); + // Verify the message by reading from BCB directly. + bootloader_message boot; + std::string err; + ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err; - struct sockaddr_un un = {}; - un.sun_family = AF_UNIX; - strlcpy(un.sun_path, UNCRYPT_SOCKET.c_str(), sizeof(un.sun_path)); + if (isSetup) { + ASSERT_EQ("boot-recovery", std::string(boot.command)); + ASSERT_EQ(message_in_bcb, std::string(boot.recovery)); - int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); - ASSERT_NE(-1, sockfd); + // The rest of the boot.recovery message should be zero'd out. + ASSERT_LE(message_in_bcb.size(), sizeof(boot.recovery)); + size_t left = sizeof(boot.recovery) - message_in_bcb.size(); + ASSERT_EQ(std::string(left, '\0'), std::string(&boot.recovery[message_in_bcb.size()], left)); - // Connect to the uncrypt socket. - bool success = false; - for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) { - if (connect(sockfd, reinterpret_cast<struct sockaddr*>(&un), sizeof(struct sockaddr_un)) != 0) { - success = true; - break; + // Clear the BCB. + ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err; + } else { + // All the bytes should be cleared. + ASSERT_EQ(std::string(sizeof(boot), '\0'), + std::string(reinterpret_cast<const char*>(&boot), sizeof(boot))); } - sleep(1); } - ASSERT_TRUE(success); - // Check the status code from uncrypt. - int status; - ASSERT_TRUE(android::base::ReadFully(sockfd, &status, sizeof(int))); - ASSERT_EQ(100U, ntohl(status)); - - // Ack having received the status code. - int code = 0; - ASSERT_TRUE(android::base::WriteFully(sockfd, &code, sizeof(int))); + bool has_misc; +}; - ASSERT_EQ(0, close(sockfd)); +TEST_F(UncryptTest, setup_bcb) { + std::string message = "--update_message=abc value"; + std::string message_in_bcb = "recovery\n--update_message=abc value\n"; + SetupOrClearBcb(true, message, message_in_bcb); +} - ASSERT_TRUE(android::base::SetProperty("ctl.stop", "clear-bcb")); +TEST_F(UncryptTest, clear_bcb) { + SetupOrClearBcb(false, "", ""); +} - // Verify the content by reading from BCB directly. - bootloader_message boot; - std::string err; - ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err; +TEST_F(UncryptTest, setup_bcb_wipe_ab) { + TemporaryFile wipe_package; + ASSERT_TRUE(android::base::WriteStringToFile(std::string(345, 'a'), wipe_package.path)); - // All the bytes should be cleared. - ASSERT_EQ(std::string(sizeof(boot), '\0'), - std::string(reinterpret_cast<const char*>(&boot), sizeof(boot))); + // It's expected to store a wipe package in /misc, with the package size passed to recovery. + std::string message = + "--wipe_ab\n--wipe_package="s + wipe_package.path + "\n--reason=wipePackage"s; + std::string message_in_bcb = + "recovery\n--wipe_ab\n--wipe_package_size=345\n--reason=wipePackage\n"; + SetupOrClearBcb(true, message, message_in_bcb); } diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index c614ccc47..8c0f885a1 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -240,57 +240,54 @@ struct RangeSinkState { size_t p_remain; }; -static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) { - RangeSinkState* rss = reinterpret_cast<RangeSinkState*>(token); - - if (rss->p_remain == 0) { - LOG(ERROR) << "range sink write overrun"; - return 0; - } - - ssize_t written = 0; - while (size > 0) { - size_t write_now = size; - - if (rss->p_remain < write_now) { - write_now = rss->p_remain; - } +static size_t RangeSinkWrite(const uint8_t* data, size_t size, RangeSinkState* rss) { + if (rss->p_remain == 0) { + LOG(ERROR) << "range sink write overrun"; + return 0; + } - if (write_all(rss->fd, data, write_now) == -1) { - break; - } + size_t written = 0; + while (size > 0) { + size_t write_now = size; - data += write_now; - size -= write_now; + if (rss->p_remain < write_now) { + write_now = rss->p_remain; + } - rss->p_remain -= write_now; - written += write_now; + if (write_all(rss->fd, data, write_now) == -1) { + break; + } - if (rss->p_remain == 0) { - // move to the next block - ++rss->p_block; - if (rss->p_block < rss->tgt.count) { - rss->p_remain = (rss->tgt.pos[rss->p_block * 2 + 1] - - rss->tgt.pos[rss->p_block * 2]) * BLOCKSIZE; + data += write_now; + size -= write_now; - off64_t offset = static_cast<off64_t>(rss->tgt.pos[rss->p_block*2]) * BLOCKSIZE; - if (!discard_blocks(rss->fd, offset, rss->p_remain)) { - break; - } + rss->p_remain -= write_now; + written += write_now; - if (!check_lseek(rss->fd, offset, SEEK_SET)) { - break; - } + if (rss->p_remain == 0) { + // Move to the next block. + ++rss->p_block; + if (rss->p_block < rss->tgt.count) { + rss->p_remain = + (rss->tgt.pos[rss->p_block * 2 + 1] - rss->tgt.pos[rss->p_block * 2]) * BLOCKSIZE; + + off64_t offset = static_cast<off64_t>(rss->tgt.pos[rss->p_block * 2]) * BLOCKSIZE; + if (!discard_blocks(rss->fd, offset, rss->p_remain)) { + break; + } - } else { - // we can't write any more; return how many bytes have - // been written so far. - break; - } + if (!check_lseek(rss->fd, offset, SEEK_SET)) { + break; } + + } else { + // We can't write any more; return how many bytes have been written so far. + break; + } } + } - return written; + return written; } // All of the data for all the 'new' transfers is contained in one @@ -338,7 +335,7 @@ static bool receive_new_data(const uint8_t* data, size_t size, void* cookie) { // At this point nti->rss is set, and we own it. The main // thread is waiting for it to disappear from nti. - ssize_t written = RangeSinkWrite(data, size, nti->rss); + size_t written = RangeSinkWrite(data, size, nti->rss); data += written; size -= written; @@ -696,7 +693,7 @@ static int LoadStash(CommandParameters& params, const std::string& id, bool veri } static int WriteStash(const std::string& base, const std::string& id, int blocks, - std::vector<uint8_t>& buffer, bool checkspace, bool *exists) { + std::vector<uint8_t>& buffer, bool checkspace, bool* exists) { if (base.empty()) { return -1; } @@ -883,96 +880,81 @@ static void MoveRange(std::vector<uint8_t>& dest, const RangeSet& locs, } } -// Do a source/target load for move/bsdiff/imgdiff in version 2. -// We expect to parse the remainder of the parameter tokens as one of: -// -// <tgt_range> <src_block_count> <src_range> -// (loads data from source image only) -// -// <tgt_range> <src_block_count> - <[stash_id:stash_range] ...> -// (loads data from stashes only) -// -// <tgt_range> <src_block_count> <src_range> <src_loc> <[stash_id:stash_range] ...> -// (loads data from both source image and stashes) -// -// On return, params.buffer is filled with the loaded source data (rearranged and combined with -// stashed data as necessary). buffer may be reallocated if needed to accommodate the source data. -// *tgt is the target RangeSet. Any stashes required are loaded using LoadStash. - -static int LoadSrcTgtVersion2(CommandParameters& params, RangeSet& tgt, size_t& src_blocks, - bool* overlap) { +/** + * We expect to parse the remainder of the parameter tokens as one of: + * + * <src_block_count> <src_range> + * (loads data from source image only) + * + * <src_block_count> - <[stash_id:stash_range] ...> + * (loads data from stashes only) + * + * <src_block_count> <src_range> <src_loc> <[stash_id:stash_range] ...> + * (loads data from both source image and stashes) + * + * On return, params.buffer is filled with the loaded source data (rearranged and combined with + * stashed data as necessary). buffer may be reallocated if needed to accommodate the source data. + * tgt is the target RangeSet for detecting overlaps. Any stashes required are loaded using + * LoadStash. + */ +static int LoadSourceBlocks(CommandParameters& params, const RangeSet& tgt, size_t* src_blocks, + bool* overlap) { + CHECK(src_blocks != nullptr); + CHECK(overlap != nullptr); + + // <src_block_count> + const std::string& token = params.tokens[params.cpos++]; + if (!android::base::ParseUint(token, src_blocks)) { + LOG(ERROR) << "invalid src_block_count \"" << token << "\""; + return -1; + } - // At least it needs to provide three parameters: <tgt_range>, - // <src_block_count> and "-"/<src_range>. - if (params.cpos + 2 >= params.tokens.size()) { - LOG(ERROR) << "invalid parameters"; - return -1; - } + allocate(*src_blocks * BLOCKSIZE, params.buffer); - // <tgt_range> - tgt = parse_range(params.tokens[params.cpos++]); + // "-" or <src_range> [<src_loc>] + if (params.tokens[params.cpos] == "-") { + // no source ranges, only stashes + params.cpos++; + } else { + RangeSet src = parse_range(params.tokens[params.cpos++]); + *overlap = range_overlaps(src, tgt); - // <src_block_count> - const std::string& token = params.tokens[params.cpos++]; - if (!android::base::ParseUint(token.c_str(), &src_blocks)) { - LOG(ERROR) << "invalid src_block_count \"" << token << "\""; - return -1; + if (ReadBlocks(src, params.buffer, params.fd) == -1) { + return -1; } - allocate(src_blocks * BLOCKSIZE, params.buffer); - - // "-" or <src_range> [<src_loc>] - if (params.tokens[params.cpos] == "-") { - // no source ranges, only stashes - params.cpos++; - } else { - RangeSet src = parse_range(params.tokens[params.cpos++]); - int res = ReadBlocks(src, params.buffer, params.fd); - - if (overlap) { - *overlap = range_overlaps(src, tgt); - } - - if (res == -1) { - return -1; - } - - if (params.cpos >= params.tokens.size()) { - // no stashes, only source range - return 0; - } - - RangeSet locs = parse_range(params.tokens[params.cpos++]); - MoveRange(params.buffer, locs, params.buffer); + if (params.cpos >= params.tokens.size()) { + // no stashes, only source range + return 0; } - // <[stash_id:stash_range]> - while (params.cpos < params.tokens.size()) { - // Each word is a an index into the stash table, a colon, and - // then a rangeset describing where in the source block that - // stashed data should go. - std::vector<std::string> tokens = android::base::Split(params.tokens[params.cpos++], ":"); - if (tokens.size() != 2) { - LOG(ERROR) << "invalid parameter"; - return -1; - } - - std::vector<uint8_t> stash; - int res = LoadStash(params, tokens[0], false, nullptr, stash, true); - - if (res == -1) { - // These source blocks will fail verification if used later, but we - // will let the caller decide if this is a fatal failure - LOG(ERROR) << "failed to load stash " << tokens[0]; - continue; - } + RangeSet locs = parse_range(params.tokens[params.cpos++]); + MoveRange(params.buffer, locs, params.buffer); + } - RangeSet locs = parse_range(tokens[1]); + // <[stash_id:stash_range]> + while (params.cpos < params.tokens.size()) { + // Each word is a an index into the stash table, a colon, and then a RangeSet describing where + // in the source block that stashed data should go. + std::vector<std::string> tokens = android::base::Split(params.tokens[params.cpos++], ":"); + if (tokens.size() != 2) { + LOG(ERROR) << "invalid parameter"; + return -1; + } - MoveRange(params.buffer, locs, stash); + std::vector<uint8_t> stash; + if (LoadStash(params, tokens[0], false, nullptr, stash, true) == -1) { + // These source blocks will fail verification if used later, but we + // will let the caller decide if this is a fatal failure + LOG(ERROR) << "failed to load stash " << tokens[0]; + continue; } - return 0; + RangeSet locs = parse_range(tokens[1]); + MoveRange(params.buffer, locs, stash); + } + + return 0; } /** @@ -989,9 +971,8 @@ static int LoadSrcTgtVersion2(CommandParameters& params, RangeSet& tgt, size_t& * <tgt_range> <src_block_count> <src_range> <src_loc> <[stash_id:stash_range] ...> * (loads data from both source image and stashes) * - * Parameters are the same as for LoadSrcTgtVersion2, except for 'onehash', which tells the function - * whether to expect separate source and targe block hashes, or if they are both the same and only - * one hash should be expected, and 'isunresumable', which receives a non-zero value if block + * 'onehash' tells whether to expect separate source and targe block hashes, or if they are both the + * same and only one hash should be expected. params.isunresumable will be set to true if block * verification fails in a way that the update cannot be resumed anymore. * * If the function is unable to load the necessary blocks or their contents don't match the hashes, @@ -1002,87 +983,100 @@ static int LoadSrcTgtVersion2(CommandParameters& params, RangeSet& tgt, size_t& * * If the return value is 0, source blocks have expected content and the command can be performed. */ -static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t& src_blocks, - bool onehash, bool& overlap) { - if (params.cpos >= params.tokens.size()) { - LOG(ERROR) << "missing source hash"; - return -1; - } +static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t* src_blocks, + bool onehash, bool* overlap) { + CHECK(src_blocks != nullptr); + CHECK(overlap != nullptr); - std::string srchash = params.tokens[params.cpos++]; - std::string tgthash; + if (params.cpos >= params.tokens.size()) { + LOG(ERROR) << "missing source hash"; + return -1; + } - if (onehash) { - tgthash = srchash; - } else { - if (params.cpos >= params.tokens.size()) { - LOG(ERROR) << "missing target hash"; - return -1; - } - tgthash = params.tokens[params.cpos++]; - } + std::string srchash = params.tokens[params.cpos++]; + std::string tgthash; - if (LoadSrcTgtVersion2(params, tgt, src_blocks, &overlap) == -1) { - return -1; + if (onehash) { + tgthash = srchash; + } else { + if (params.cpos >= params.tokens.size()) { + LOG(ERROR) << "missing target hash"; + return -1; } + tgthash = params.tokens[params.cpos++]; + } - std::vector<uint8_t> tgtbuffer(tgt.size * BLOCKSIZE); + // At least it needs to provide three parameters: <tgt_range>, <src_block_count> and + // "-"/<src_range>. + if (params.cpos + 2 >= params.tokens.size()) { + LOG(ERROR) << "invalid parameters"; + return -1; + } - if (ReadBlocks(tgt, tgtbuffer, params.fd) == -1) { - return -1; - } + // <tgt_range> + tgt = parse_range(params.tokens[params.cpos++]); - if (VerifyBlocks(tgthash, tgtbuffer, tgt.size, false) == 0) { - // Target blocks already have expected content, command should be skipped. - return 1; - } + std::vector<uint8_t> tgtbuffer(tgt.size * BLOCKSIZE); + if (ReadBlocks(tgt, tgtbuffer, params.fd) == -1) { + return -1; + } - if (VerifyBlocks(srchash, params.buffer, src_blocks, true) == 0) { - // If source and target blocks overlap, stash the source blocks so we can - // resume from possible write errors. In verify mode, we can skip stashing - // because the source blocks won't be overwritten. - if (overlap && params.canwrite) { - LOG(INFO) << "stashing " << src_blocks << " overlapping blocks to " << srchash; + // Return now if target blocks already have expected content. + if (VerifyBlocks(tgthash, tgtbuffer, tgt.size, false) == 0) { + return 1; + } - bool stash_exists = false; - if (WriteStash(params.stashbase, srchash, src_blocks, params.buffer, true, - &stash_exists) != 0) { - LOG(ERROR) << "failed to stash overlapping source blocks"; - return -1; - } + // Load source blocks. + if (LoadSourceBlocks(params, tgt, src_blocks, overlap) == -1) { + return -1; + } - params.stashed += src_blocks; - // Can be deleted when the write has completed. - if (!stash_exists) { - params.freestash = srchash; - } - } + if (VerifyBlocks(srchash, params.buffer, *src_blocks, true) == 0) { + // If source and target blocks overlap, stash the source blocks so we can + // resume from possible write errors. In verify mode, we can skip stashing + // because the source blocks won't be overwritten. + if (*overlap && params.canwrite) { + LOG(INFO) << "stashing " << *src_blocks << " overlapping blocks to " << srchash; + + bool stash_exists = false; + if (WriteStash(params.stashbase, srchash, *src_blocks, params.buffer, true, + &stash_exists) != 0) { + LOG(ERROR) << "failed to stash overlapping source blocks"; + return -1; + } - // Source blocks have expected content, command can proceed. - return 0; + params.stashed += *src_blocks; + // Can be deleted when the write has completed. + if (!stash_exists) { + params.freestash = srchash; + } } - if (overlap && LoadStash(params, srchash, true, nullptr, params.buffer, true) == 0) { - // Overlapping source blocks were previously stashed, command can proceed. - // We are recovering from an interrupted command, so we don't know if the - // stash can safely be deleted after this command. - return 0; - } + // Source blocks have expected content, command can proceed. + return 0; + } - // Valid source data not available, update cannot be resumed. - LOG(ERROR) << "partition has unexpected contents"; - PrintHashForCorruptedSourceBlocks(params, params.buffer); + if (*overlap && LoadStash(params, srchash, true, nullptr, params.buffer, true) == 0) { + // Overlapping source blocks were previously stashed, command can proceed. We are recovering + // from an interrupted command, so we don't know if the stash can safely be deleted after this + // command. + return 0; + } - params.isunresumable = true; + // Valid source data not available, update cannot be resumed. + LOG(ERROR) << "partition has unexpected contents"; + PrintHashForCorruptedSourceBlocks(params, params.buffer); - return -1; + params.isunresumable = true; + + return -1; } static int PerformCommandMove(CommandParameters& params) { size_t blocks = 0; bool overlap = false; RangeSet tgt; - int status = LoadSrcTgtVersion3(params, tgt, blocks, true, overlap); + int status = LoadSrcTgtVersion3(params, tgt, &blocks, true, &overlap); if (status == -1) { LOG(ERROR) << "failed to read blocks for move"; @@ -1262,92 +1256,95 @@ static int PerformCommandNew(CommandParameters& params) { } static int PerformCommandDiff(CommandParameters& params) { + // <offset> <length> + if (params.cpos + 1 >= params.tokens.size()) { + LOG(ERROR) << "missing patch offset or length for " << params.cmdname; + return -1; + } - // <offset> <length> - if (params.cpos + 1 >= params.tokens.size()) { - LOG(ERROR) << "missing patch offset or length for " << params.cmdname; - return -1; - } + size_t offset; + if (!android::base::ParseUint(params.tokens[params.cpos++], &offset)) { + LOG(ERROR) << "invalid patch offset"; + return -1; + } - size_t offset; - if (!android::base::ParseUint(params.tokens[params.cpos++].c_str(), &offset)) { - LOG(ERROR) << "invalid patch offset"; - return -1; - } + size_t len; + if (!android::base::ParseUint(params.tokens[params.cpos++], &len)) { + LOG(ERROR) << "invalid patch len"; + return -1; + } - size_t len; - if (!android::base::ParseUint(params.tokens[params.cpos++].c_str(), &len)) { - LOG(ERROR) << "invalid patch len"; - return -1; - } + RangeSet tgt; + size_t blocks = 0; + bool overlap = false; + int status = LoadSrcTgtVersion3(params, tgt, &blocks, false, &overlap); - RangeSet tgt; - size_t blocks = 0; - bool overlap = false; - int status = LoadSrcTgtVersion3(params, tgt, blocks, false, overlap); + if (status == -1) { + LOG(ERROR) << "failed to read blocks for diff"; + return -1; + } - if (status == -1) { - LOG(ERROR) << "failed to read blocks for diff"; - return -1; - } + if (status == 0) { + params.foundwrites = true; + } else if (params.foundwrites) { + LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]"; + } + if (params.canwrite) { if (status == 0) { - params.foundwrites = true; - } else if (params.foundwrites) { - LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]"; - } - - if (params.canwrite) { - if (status == 0) { - LOG(INFO) << "patching " << blocks << " blocks to " << tgt.size; - Value patch_value(VAL_BLOB, - std::string(reinterpret_cast<const char*>(params.patch_start + offset), len)); - RangeSinkState rss(tgt); - rss.fd = params.fd; - rss.p_block = 0; - rss.p_remain = (tgt.pos[1] - tgt.pos[0]) * BLOCKSIZE; - - off64_t offset = static_cast<off64_t>(tgt.pos[0]) * BLOCKSIZE; - if (!discard_blocks(params.fd, offset, rss.p_remain)) { - return -1; - } - - if (!check_lseek(params.fd, offset, SEEK_SET)) { - return -1; - } + LOG(INFO) << "patching " << blocks << " blocks to " << tgt.size; + Value patch_value( + VAL_BLOB, std::string(reinterpret_cast<const char*>(params.patch_start + offset), len)); + RangeSinkState rss(tgt); + rss.fd = params.fd; + rss.p_block = 0; + rss.p_remain = (tgt.pos[1] - tgt.pos[0]) * BLOCKSIZE; + + off64_t offset = static_cast<off64_t>(tgt.pos[0]) * BLOCKSIZE; + if (!discard_blocks(params.fd, offset, rss.p_remain)) { + return -1; + } - if (params.cmdname[0] == 'i') { // imgdiff - if (ApplyImagePatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value, - &RangeSinkWrite, &rss, nullptr, nullptr) != 0) { - LOG(ERROR) << "Failed to apply image patch."; - return -1; - } - } else { - if (ApplyBSDiffPatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value, - 0, &RangeSinkWrite, &rss, nullptr) != 0) { - LOG(ERROR) << "Failed to apply bsdiff patch."; - return -1; - } - } + if (!check_lseek(params.fd, offset, SEEK_SET)) { + return -1; + } - // We expect the output of the patcher to fill the tgt ranges exactly. - if (rss.p_block != tgt.count || rss.p_remain != 0) { - LOG(ERROR) << "range sink underrun?"; - } - } else { - LOG(INFO) << "skipping " << blocks << " blocks already patched to " << tgt.size - << " [" << params.cmdline << "]"; + if (params.cmdname[0] == 'i') { // imgdiff + if (ApplyImagePatch( + params.buffer.data(), blocks * BLOCKSIZE, &patch_value, + std::bind(&RangeSinkWrite, std::placeholders::_1, std::placeholders::_2, &rss), + nullptr, nullptr) != 0) { + LOG(ERROR) << "Failed to apply image patch."; + return -1; } - } + } else { + if (ApplyBSDiffPatch( + params.buffer.data(), blocks * BLOCKSIZE, &patch_value, 0, + std::bind(&RangeSinkWrite, std::placeholders::_1, std::placeholders::_2, &rss), + nullptr) != 0) { + LOG(ERROR) << "Failed to apply bsdiff patch."; + return -1; + } + } - if (!params.freestash.empty()) { - FreeStash(params.stashbase, params.freestash); - params.freestash.clear(); + // We expect the output of the patcher to fill the tgt ranges exactly. + if (rss.p_block != tgt.count || rss.p_remain != 0) { + LOG(ERROR) << "range sink underrun?"; + } + } else { + LOG(INFO) << "skipping " << blocks << " blocks already patched to " << tgt.size << " [" + << params.cmdline << "]"; } + } - params.written += tgt.size; + if (!params.freestash.empty()) { + FreeStash(params.stashbase, params.freestash); + params.freestash.clear(); + } - return 0; + params.written += tgt.size; + + return 0; } static int PerformCommandErase(CommandParameters& params) { @@ -1871,7 +1868,7 @@ Value* BlockImageRecoverFn(const char* name, State* state, LOG(INFO) << filename->data << " image corrupted, attempting to recover..."; // When opened with O_RDWR, libfec rewrites corrupted blocks when they are read - fec::io fh(filename->data.c_str(), O_RDWR); + fec::io fh(filename->data, O_RDWR); if (!fh) { ErrorAbort(state, kLibfecFailure, "fec_open \"%s\" failed: %s", filename->data.c_str(), |