From f7eb760fe76cb66c5d5341b03d6a66cc1f06d795 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 27 Mar 2017 15:12:48 -0700 Subject: applypatch: Change the ssize_t length parameters to size_t. Mostly for applypatch family APIs like ApplyBSDiffPatch() and ApplyImagePatch(). Changing to size_t doesn't indicate they would necessarily work with very large size_t (e.g. > ssize_t), just similar to write(2). But otherwise accepting negative length doesn't make much sense. Also change the return type of SinkFn from ssize_t to size_t. Callers tell a successful sink by comparing the number of written bytes against the desired value. Negative return values like -1 are not needed. This also makes it consistent with bsdiff::bspatch interface. Test: recovery_component_test Test: Apply an incremental with the new updater. Change-Id: I7ff1615203a5c9854134f75d019e266f4ea6e714 --- applypatch/applypatch.cpp | 37 +++++++++++++++--------------- applypatch/bspatch.cpp | 8 +++---- applypatch/imgpatch.cpp | 17 +++++++------- applypatch/include/applypatch/applypatch.h | 18 ++++++--------- applypatch/include/applypatch/imgpatch.h | 7 +++--- 5 files changed, 40 insertions(+), 47 deletions(-) (limited to 'applypatch') diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index 7be3fdbde..06b8e3120 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -42,7 +42,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, void* token); static int GenerateTarget(const FileContents& source_file, const std::unique_ptr& patch, const std::string& target_filename, const uint8_t target_sha1[SHA_DIGEST_LENGTH], const Value* bonus_data); @@ -194,8 +194,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(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 +433,24 @@ int ShowLicenses() { return 0; } -ssize_t FileSink(const unsigned char* data, ssize_t len, void* token) { - int fd = *static_cast(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, void* token) { + int fd = *static_cast(token); + 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; + done += wrote; + } + return done; } -ssize_t MemorySink(const unsigned char* data, ssize_t len, void* token) { - std::string* s = static_cast(token); - s->append(reinterpret_cast(data), len); - return len; +size_t MemorySink(const unsigned char* data, size_t len, void* token) { + std::string* s = static_cast(token); + s->append(reinterpret_cast(data), len); + return len; } // Return the amount of free space (in bytes) on the filesystem diff --git a/applypatch/bspatch.cpp b/applypatch/bspatch.cpp index 9920c2be1..8ef7a068d 100644 --- a/applypatch/bspatch.cpp +++ b/applypatch/bspatch.cpp @@ -60,8 +60,8 @@ 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 ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value* patch, + size_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); if (ctx) SHA1_Update(ctx, data, 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* new_data) { +int ApplyBSDiffPatchMem(const unsigned char* old_data, size_t old_size, const Value* patch, + size_t patch_offset, std::vector* 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..7a88ffbbc 100644 --- a/applypatch/imgpatch.cpp +++ b/applypatch/imgpatch.cpp @@ -43,9 +43,8 @@ static inline int32_t Read4(const void *address) { return android::base::get_unaligned(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, void* token) { Value patch(VAL_BLOB, std::string(reinterpret_cast(patch_data), patch_size)); return ApplyImagePatch(old_data, old_size, &patch, sink, token, 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, + void* token, SHA_CTX* ctx, const Value* bonus_data) { if (patch->data.size() < 12) { printf("patch too short to contain header\n"); return -1; @@ -97,7 +96,7 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value size_t src_len = static_cast(Read8(normal_header + 8)); size_t patch_offset = static_cast(Read8(normal_header + 16)); - if (src_start + src_len > static_cast(old_size)) { + if (src_start + src_len > old_size) { printf("source data too short\n"); return -1; } @@ -110,7 +109,7 @@ 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(Read4(raw_header)); if (pos + data_len > patch->data.size()) { printf("failed to read chunk %d raw data\n", i); @@ -143,7 +142,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(old_size)) { + if (src_start + src_len > old_size) { printf("source data too short\n"); return -1; } @@ -240,7 +239,7 @@ 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) { printf("failed to write %zd compressed bytes to output\n", have); diff --git a/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h index 4489decb6..52fb58287 100644 --- a/applypatch/include/applypatch/applypatch.h +++ b/applypatch/include/applypatch/applypatch.h @@ -41,7 +41,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 = size_t (*)(const unsigned char*, size_t, void*); // applypatch.cpp int ShowLicenses(); @@ -66,18 +66,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* new_data); +int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value* patch, + size_t patch_offset, SinkFn sink, void* token, SHA_CTX* ctx); +int ApplyBSDiffPatchMem(const unsigned char* old_data, size_t old_size, const Value* patch, + size_t patch_offset, std::vector* 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, + void* token, 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..16e4c4f57 100644 --- a/applypatch/include/applypatch/imgpatch.h +++ b/applypatch/include/applypatch/imgpatch.h @@ -19,10 +19,9 @@ #include -using SinkFn = ssize_t (*)(const unsigned char*, ssize_t, void*); +using SinkFn = size_t (*)(const unsigned char*, size_t, void*); -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, void* token); #endif // _APPLYPATCH_IMGPATCH_H -- cgit v1.2.3 From c0e1c46a707370952ea1ddeb71b176d04fe71bb9 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 1 Feb 2017 10:20:10 -0800 Subject: applypatch: Let Apply{BSDiff,Image}Patch accept std::function. Test: mmma bootable/recovery system/update_engine Test: recovery_component_test Change-Id: I93c2caa87bf94a53509bb37f98f2c02bcadb6f5c --- applypatch/applypatch.cpp | 24 ++++++++++-------------- applypatch/bspatch.cpp | 8 ++++---- applypatch/imgpatch.cpp | 13 ++++++------- applypatch/include/applypatch/applypatch.h | 7 ++++--- applypatch/include/applypatch/imgpatch.h | 6 ++++-- 5 files changed, 28 insertions(+), 30 deletions(-) (limited to 'applypatch') diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index 06b8e3120..51bf3932a 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -42,7 +43,7 @@ #include "print_sha1.h" static int LoadPartitionContents(const std::string& filename, FileContents* file); -static size_t FileSink(const unsigned char* data, size_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& patch, const std::string& target_filename, const uint8_t target_sha1[SHA_DIGEST_LENGTH], const Value* bonus_data); @@ -194,7 +195,7 @@ int SaveFileContents(const char* filename, const FileContents* file) { return -1; } - size_t bytes_written = FileSink(file->data.data(), file->data.size(), &fd); + 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)); @@ -433,8 +434,7 @@ int ShowLicenses() { return 0; } -static size_t FileSink(const unsigned char* data, size_t len, void* token) { - int fd = *static_cast(token); +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)); @@ -447,12 +447,6 @@ static size_t FileSink(const unsigned char* data, size_t len, void* token) { return done; } -size_t MemorySink(const unsigned char* data, size_t len, void* token) { - std::string* s = static_cast(token); - s->append(reinterpret_cast(data), len); - return len; -} - // Return the amount of free space (in bytes) on the filesystem // containing filename. filename must exist. Return -1 on error. size_t FreeSpaceForFile(const char* filename) { @@ -646,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(data), len); + return len; + }; SHA_CTX ctx; SHA1_Init(&ctx); @@ -656,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 8ef7a068d..f75a2c680 100644 --- a/applypatch/bspatch.cpp +++ b/applypatch/bspatch.cpp @@ -24,9 +24,9 @@ #include #include +#include #include "applypatch/applypatch.h" -#include "openssl/sha.h" void ShowBSDiffLicense() { puts("The bsdiff library used herein is:\n" @@ -61,9 +61,9 @@ void ShowBSDiffLicense() { } int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value* patch, - size_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); + 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; }; diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp index 7a88ffbbc..7d8b7361c 100644 --- a/applypatch/imgpatch.cpp +++ b/applypatch/imgpatch.cpp @@ -44,10 +44,10 @@ static inline int32_t Read4(const void *address) { } int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsigned char* patch_data, - size_t patch_size, SinkFn sink, void* token) { + size_t patch_size, SinkFn sink) { Value patch(VAL_BLOB, std::string(reinterpret_cast(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,7 +57,7 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsign * Return 0 on success. */ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* patch, SinkFn sink, - void* token, SHA_CTX* ctx, const Value* bonus_data) { + SHA_CTX* ctx, const Value* bonus_data) { if (patch->data.size() < 12) { printf("patch too short to contain header\n"); return -1; @@ -100,7 +100,7 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* 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; @@ -116,8 +116,7 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* return -1; } if (ctx) SHA1_Update(ctx, &patch->data[pos], data_len); - if (sink(reinterpret_cast(&patch->data[pos]), data_len, token) != - data_len) { + if (sink(reinterpret_cast(&patch->data[pos]), data_len) != data_len) { printf("failed to write chunk %d raw data\n", i); return -1; } @@ -241,7 +240,7 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* ret = deflate(&strm, Z_FINISH); 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 52fb58287..da55432d5 100644 --- a/applypatch/include/applypatch/applypatch.h +++ b/applypatch/include/applypatch/applypatch.h @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -41,7 +42,7 @@ struct FileContents { // and use it as the source instead. #define CACHE_TEMP_SOURCE "/cache/saved.file" -using SinkFn = size_t (*)(const unsigned char*, size_t, void*); +using SinkFn = std::function; // applypatch.cpp int ShowLicenses(); @@ -67,13 +68,13 @@ int SaveFileContents(const char* filename, const FileContents* file); // bspatch.cpp void ShowBSDiffLicense(); int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value* patch, - size_t patch_offset, SinkFn sink, void* token, SHA_CTX* ctx); + 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* new_data); // imgpatch.cpp int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* patch, SinkFn sink, - void* token, SHA_CTX* ctx, const Value* bonus_data); + 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 16e4c4f57..07c66094f 100644 --- a/applypatch/include/applypatch/imgpatch.h +++ b/applypatch/include/applypatch/imgpatch.h @@ -19,9 +19,11 @@ #include -using SinkFn = size_t (*)(const unsigned char*, size_t, void*); +#include + +using SinkFn = std::function; int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsigned char* patch_data, - size_t patch_size, SinkFn sink, void* token); + size_t patch_size, SinkFn sink); #endif // _APPLYPATCH_IMGPATCH_H -- cgit v1.2.3 From c3ef089dfac921ef6ca04d5341a4035e8c4feb51 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 4 Oct 2016 18:03:57 -0700 Subject: applypatch: Remove duplicate test files. CL in commit b8a693bbc73808924f4be8c4d47bbc4da0647e3a has ported the tests to gtest-based recovery_component_tests. Test: N/A Change-Id: I485fa11413df68a11416d4a9f91f090f302bcfc0 --- applypatch/applypatch.sh | 350 --------------------------------------- applypatch/imgdiff_test.sh | 118 ------------- applypatch/testdata/new.file | Bin 1388877 -> 0 bytes applypatch/testdata/old.file | Bin 1348051 -> 0 bytes applypatch/testdata/patch.bsdiff | Bin 57476 -> 0 bytes 5 files changed, 468 deletions(-) delete mode 100755 applypatch/applypatch.sh delete mode 100755 applypatch/imgdiff_test.sh delete mode 100644 applypatch/testdata/new.file delete mode 100644 applypatch/testdata/old.file delete mode 100644 applypatch/testdata/patch.bsdiff (limited to 'applypatch') diff --git a/applypatch/applypatch.sh b/applypatch/applypatch.sh deleted file mode 100755 index 8ea68a1a9..000000000 --- a/applypatch/applypatch.sh +++ /dev/null @@ -1,350 +0,0 @@ -#!/bin/bash -# -# A test suite for applypatch. Run in a client where you have done -# envsetup, choosecombo, etc. -# -# DO NOT RUN THIS ON A DEVICE YOU CARE ABOUT. It will mess up your -# system partition. -# -# -# TODO: find some way to get this run regularly along with the rest of -# the tests. - -EMULATOR_PORT=5580 -DATA_DIR=$ANDROID_BUILD_TOP/bootable/recovery/applypatch/testdata - -# This must be the filename that applypatch uses for its copies. -CACHE_TEMP_SOURCE=/cache/saved.file - -# Put all binaries and files here. We use /cache because it's a -# temporary filesystem in the emulator; it's created fresh each time -# the emulator starts. -WORK_DIR=/system - -# partition that WORK_DIR is located on, without the leading slash -WORK_FS=system - -# set to 0 to use a device instead -USE_EMULATOR=1 - -# ------------------------ - -tmpdir=$(mktemp -d) - -if [ "$USE_EMULATOR" == 1 ]; then - emulator -wipe-data -noaudio -no-window -port $EMULATOR_PORT & - pid_emulator=$! - ADB="adb -s emulator-$EMULATOR_PORT " -else - ADB="adb -d " -fi - -echo "waiting to connect to device" -$ADB wait-for-device -echo "device is available" -$ADB remount -# free up enough space on the system partition for the test to run. -$ADB shell rm -r /system/media - -# run a command on the device; exit with the exit status of the device -# command. -run_command() { - $ADB shell "$@" \; echo \$? | awk '{if (b) {print a}; a=$0; b=1} END {exit a}' -} - -testname() { - echo - echo "$1"... - testname="$1" -} - -fail() { - echo - echo FAIL: $testname - echo - [ "$open_pid" == "" ] || kill $open_pid - [ "$pid_emulator" == "" ] || kill $pid_emulator - exit 1 -} - -sha1() { - sha1sum $1 | awk '{print $1}' -} - -free_space() { - run_command df | awk "/$1/ {print gensub(/K/, \"\", \"g\", \$6)}" -} - -cleanup() { - # not necessary if we're about to kill the emulator, but nice for - # running on real devices or already-running emulators. - testname "removing test files" - run_command rm $WORK_DIR/bloat.dat - run_command rm $WORK_DIR/old.file - run_command rm $WORK_DIR/foo - run_command rm $WORK_DIR/patch.bsdiff - run_command rm $WORK_DIR/applypatch - run_command rm $CACHE_TEMP_SOURCE - run_command rm /cache/bloat*.dat - - [ "$pid_emulator" == "" ] || kill $pid_emulator - - if [ $# == 0 ]; then - rm -rf $tmpdir - fi -} - -cleanup leave_tmp - -$ADB push $ANDROID_PRODUCT_OUT/system/bin/applypatch $WORK_DIR/applypatch - -BAD1_SHA1=$(printf "%040x" $RANDOM) -BAD2_SHA1=$(printf "%040x" $RANDOM) -OLD_SHA1=$(sha1 $DATA_DIR/old.file) -NEW_SHA1=$(sha1 $DATA_DIR/new.file) -NEW_SIZE=$(stat -c %s $DATA_DIR/new.file) - -# --------------- basic execution ---------------------- - -testname "usage message" -run_command $WORK_DIR/applypatch && fail - -testname "display license" -run_command $WORK_DIR/applypatch -l | grep -q -i copyright || fail - - -# --------------- check mode ---------------------- - -$ADB push $DATA_DIR/old.file $WORK_DIR - -testname "check mode single" -run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $OLD_SHA1 || fail - -testname "check mode multiple" -run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD1_SHA1 $OLD_SHA1 $BAD2_SHA1|| fail - -testname "check mode failure" -run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 && fail - -$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE -# put some junk in the old file -run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail - -testname "check mode cache (corrupted) single" -run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $OLD_SHA1 || fail - -testname "check mode cache (corrupted) multiple" -run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD1_SHA1 $OLD_SHA1 $BAD2_SHA1|| fail - -testname "check mode cache (corrupted) failure" -run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 && fail - -# remove the old file entirely -run_command rm $WORK_DIR/old.file - -testname "check mode cache (missing) single" -run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $OLD_SHA1 || fail - -testname "check mode cache (missing) multiple" -run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD1_SHA1 $OLD_SHA1 $BAD2_SHA1|| fail - -testname "check mode cache (missing) failure" -run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 && fail - - -# --------------- apply patch ---------------------- - -$ADB push $DATA_DIR/old.file $WORK_DIR -$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR -echo hello > $tmpdir/foo -$ADB push $tmpdir/foo $WORK_DIR - -# Check that the partition has enough space to apply the patch without -# copying. If it doesn't, we'll be testing the low-space condition -# when we intend to test the not-low-space condition. -testname "apply patches (with enough space)" -free_kb=$(free_space $WORK_FS) -echo "${free_kb}kb free on /$WORK_FS." -if (( free_kb * 1024 < NEW_SIZE * 3 / 2 )); then - echo "Not enough space on /$WORK_FS to patch test file." - echo - echo "This doesn't mean that applypatch is necessarily broken;" - echo "just that /$WORK_FS doesn't have enough free space to" - echo "properly run this test." - exit 1 -fi - -testname "apply bsdiff patch" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail -$ADB pull $WORK_DIR/old.file $tmpdir/patched -diff -q $DATA_DIR/new.file $tmpdir/patched || fail - -testname "reapply bsdiff patch" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail -$ADB pull $WORK_DIR/old.file $tmpdir/patched -diff -q $DATA_DIR/new.file $tmpdir/patched || fail - - -# --------------- apply patch in new location ---------------------- - -$ADB push $DATA_DIR/old.file $WORK_DIR -$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR - -# Check that the partition has enough space to apply the patch without -# copying. If it doesn't, we'll be testing the low-space condition -# when we intend to test the not-low-space condition. -testname "apply patch to new location (with enough space)" -free_kb=$(free_space $WORK_FS) -echo "${free_kb}kb free on /$WORK_FS." -if (( free_kb * 1024 < NEW_SIZE * 3 / 2 )); then - echo "Not enough space on /$WORK_FS to patch test file." - echo - echo "This doesn't mean that applypatch is necessarily broken;" - echo "just that /$WORK_FS doesn't have enough free space to" - echo "properly run this test." - exit 1 -fi - -run_command rm $WORK_DIR/new.file -run_command rm $CACHE_TEMP_SOURCE - -testname "apply bsdiff patch to new location" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail -$ADB pull $WORK_DIR/new.file $tmpdir/patched -diff -q $DATA_DIR/new.file $tmpdir/patched || fail - -testname "reapply bsdiff patch to new location" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail -$ADB pull $WORK_DIR/new.file $tmpdir/patched -diff -q $DATA_DIR/new.file $tmpdir/patched || fail - -$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE -# put some junk in the old file -run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail - -testname "apply bsdiff patch to new location with corrupted source" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $OLD_SHA1:$WORK_DIR/patch.bsdiff $BAD1_SHA1:$WORK_DIR/foo || fail -$ADB pull $WORK_DIR/new.file $tmpdir/patched -diff -q $DATA_DIR/new.file $tmpdir/patched || fail - -# put some junk in the cache copy, too -run_command dd if=/dev/urandom of=$CACHE_TEMP_SOURCE count=100 bs=1024 || fail - -run_command rm $WORK_DIR/new.file -testname "apply bsdiff patch to new location with corrupted source and copy (no new file)" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $OLD_SHA1:$WORK_DIR/patch.bsdiff $BAD1_SHA1:$WORK_DIR/foo && fail - -# put some junk in the new file -run_command dd if=/dev/urandom of=$WORK_DIR/new.file count=100 bs=1024 || fail - -testname "apply bsdiff patch to new location with corrupted source and copy (bad new file)" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $OLD_SHA1:$WORK_DIR/patch.bsdiff $BAD1_SHA1:$WORK_DIR/foo && fail - -# --------------- apply patch with low space on /system ---------------------- - -$ADB push $DATA_DIR/old.file $WORK_DIR -$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR - -free_kb=$(free_space $WORK_FS) -echo "${free_kb}kb free on /$WORK_FS; we'll soon fix that." -echo run_command dd if=/dev/zero of=$WORK_DIR/bloat.dat count=$((free_kb-512)) bs=1024 || fail -run_command dd if=/dev/zero of=$WORK_DIR/bloat.dat count=$((free_kb-512)) bs=1024 || fail -free_kb=$(free_space $WORK_FS) -echo "${free_kb}kb free on /$WORK_FS now." - -testname "apply bsdiff patch with low space" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail -$ADB pull $WORK_DIR/old.file $tmpdir/patched -diff -q $DATA_DIR/new.file $tmpdir/patched || fail - -testname "reapply bsdiff patch with low space" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail -$ADB pull $WORK_DIR/old.file $tmpdir/patched -diff -q $DATA_DIR/new.file $tmpdir/patched || fail - -# --------------- apply patch with low space on /system and /cache ---------------------- - -$ADB push $DATA_DIR/old.file $WORK_DIR -$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR - -free_kb=$(free_space $WORK_FS) -echo "${free_kb}kb free on /$WORK_FS" - -run_command mkdir /cache/subdir -run_command 'echo > /cache/subdir/a.file' -run_command 'echo > /cache/a.file' -run_command mkdir /cache/recovery /cache/recovery/otatest -run_command 'echo > /cache/recovery/otatest/b.file' -run_command "echo > $CACHE_TEMP_SOURCE" -free_kb=$(free_space cache) -echo "${free_kb}kb free on /cache; we'll soon fix that." -run_command dd if=/dev/zero of=/cache/bloat_small.dat count=128 bs=1024 || fail -run_command dd if=/dev/zero of=/cache/bloat_large.dat count=$((free_kb-640)) bs=1024 || fail -free_kb=$(free_space cache) -echo "${free_kb}kb free on /cache now." - -testname "apply bsdiff patch with low space, full cache, can't delete enough" -$ADB shell 'cat >> /cache/bloat_large.dat' & open_pid=$! -echo "open_pid is $open_pid" - -# size check should fail even though it deletes some stuff -run_command $WORK_DIR/applypatch -s $NEW_SIZE && fail -run_command ls /cache/bloat_small.dat && fail # was deleted -run_command ls /cache/a.file && fail # was deleted -run_command ls /cache/recovery/otatest/b.file && fail # was deleted -run_command ls /cache/bloat_large.dat || fail # wasn't deleted because it was open -run_command ls /cache/subdir/a.file || fail # wasn't deleted because it's in a subdir -run_command ls $CACHE_TEMP_SOURCE || fail # wasn't deleted because it's the source file copy - -# should fail; not enough files can be deleted -run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff && fail -run_command ls /cache/bloat_large.dat || fail # wasn't deleted because it was open -run_command ls /cache/subdir/a.file || fail # wasn't deleted because it's in a subdir -run_command ls $CACHE_TEMP_SOURCE || fail # wasn't deleted because it's the source file copy - -kill $open_pid # /cache/bloat_large.dat is no longer open - -testname "apply bsdiff patch with low space, full cache, can delete enough" - -# should succeed after deleting /cache/bloat_large.dat -run_command $WORK_DIR/applypatch -s $NEW_SIZE || fail -run_command ls /cache/bloat_large.dat && fail # was deleted -run_command ls /cache/subdir/a.file || fail # still wasn't deleted because it's in a subdir -run_command ls $CACHE_TEMP_SOURCE || fail # wasn't deleted because it's the source file copy - -# should succeed -run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail -$ADB pull $WORK_DIR/old.file $tmpdir/patched -diff -q $DATA_DIR/new.file $tmpdir/patched || fail -run_command ls /cache/subdir/a.file || fail # still wasn't deleted because it's in a subdir -run_command ls $CACHE_TEMP_SOURCE && fail # was deleted because patching overwrote it, then deleted it - -# --------------- apply patch from cache ---------------------- - -$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE -# put some junk in the old file -run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail - -testname "apply bsdiff patch from cache (corrupted source) with low space" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail -$ADB pull $WORK_DIR/old.file $tmpdir/patched -diff -q $DATA_DIR/new.file $tmpdir/patched || fail - -$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE -# remove the old file entirely -run_command rm $WORK_DIR/old.file - -testname "apply bsdiff patch from cache (missing source) with low space" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail -$ADB pull $WORK_DIR/old.file $tmpdir/patched -diff -q $DATA_DIR/new.file $tmpdir/patched || fail - - -# --------------- cleanup ---------------------- - -cleanup - -echo -echo PASS -echo - diff --git a/applypatch/imgdiff_test.sh b/applypatch/imgdiff_test.sh deleted file mode 100755 index dcdb922b4..000000000 --- a/applypatch/imgdiff_test.sh +++ /dev/null @@ -1,118 +0,0 @@ -#!/bin/bash -# -# A script for testing imgdiff/applypatch. It takes two full OTA -# packages as arguments. It generates (on the host) patches for all -# the zip/jar/apk files they have in common, as well as boot and -# recovery images. It then applies the patches on the device (or -# emulator) and checks that the resulting file is correct. - -EMULATOR_PORT=5580 - -# set to 0 to use a device instead -USE_EMULATOR=0 - -# where on the device to do all the patching. -WORK_DIR=/data/local/tmp - -START_OTA_PACKAGE=$1 -END_OTA_PACKAGE=$2 - -# ------------------------ - -tmpdir=$(mktemp -d) - -if [ "$USE_EMULATOR" == 1 ]; then - emulator -wipe-data -noaudio -no-window -port $EMULATOR_PORT & - pid_emulator=$! - ADB="adb -s emulator-$EMULATOR_PORT " -else - ADB="adb -d " -fi - -echo "waiting to connect to device" -$ADB wait-for-device - -# run a command on the device; exit with the exit status of the device -# command. -run_command() { - $ADB shell "$@" \; echo \$? | awk '{if (b) {print a}; a=$0; b=1} END {exit a}' -} - -testname() { - echo - echo "$1"... - testname="$1" -} - -fail() { - echo - echo FAIL: $testname - echo - [ "$open_pid" == "" ] || kill $open_pid - [ "$pid_emulator" == "" ] || kill $pid_emulator - exit 1 -} - -sha1() { - sha1sum $1 | awk '{print $1}' -} - -size() { - stat -c %s $1 | tr -d '\n' -} - -cleanup() { - # not necessary if we're about to kill the emulator, but nice for - # running on real devices or already-running emulators. - testname "removing test files" - run_command rm $WORK_DIR/applypatch - run_command rm $WORK_DIR/source - run_command rm $WORK_DIR/target - run_command rm $WORK_DIR/patch - - [ "$pid_emulator" == "" ] || kill $pid_emulator - - rm -rf $tmpdir -} - -$ADB push $ANDROID_PRODUCT_OUT/system/bin/applypatch $WORK_DIR/applypatch - -patch_and_apply() { - local fn=$1 - shift - - unzip -p $START_OTA_PACKAGE $fn > $tmpdir/source - unzip -p $END_OTA_PACKAGE $fn > $tmpdir/target - imgdiff "$@" $tmpdir/source $tmpdir/target $tmpdir/patch - bsdiff $tmpdir/source $tmpdir/target $tmpdir/patch.bs - echo "patch for $fn is $(size $tmpdir/patch) [of $(size $tmpdir/target)] ($(size $tmpdir/patch.bs) with bsdiff)" - echo "$fn $(size $tmpdir/patch) of $(size $tmpdir/target) bsdiff $(size $tmpdir/patch.bs)" >> /tmp/stats.txt - $ADB push $tmpdir/source $WORK_DIR/source || fail "source push failed" - run_command rm /data/local/tmp/target - $ADB push $tmpdir/patch $WORK_DIR/patch || fail "patch push failed" - run_command /data/local/tmp/applypatch /data/local/tmp/source \ - /data/local/tmp/target $(sha1 $tmpdir/target) $(size $tmpdir/target) \ - $(sha1 $tmpdir/source):/data/local/tmp/patch \ - || fail "applypatch of $fn failed" - $ADB pull /data/local/tmp/target $tmpdir/result - diff -q $tmpdir/target $tmpdir/result || fail "patch output not correct!" -} - -# --------------- basic execution ---------------------- - -for i in $((zipinfo -1 $START_OTA_PACKAGE; zipinfo -1 $END_OTA_PACKAGE) | \ - sort | uniq -d | egrep -e '[.](apk|jar|zip)$'); do - patch_and_apply $i -z -done -patch_and_apply boot.img -patch_and_apply system/recovery.img - - -# --------------- cleanup ---------------------- - -cleanup - -echo -echo PASS -echo - diff --git a/applypatch/testdata/new.file b/applypatch/testdata/new.file deleted file mode 100644 index cdeb8fd50..000000000 Binary files a/applypatch/testdata/new.file and /dev/null differ diff --git a/applypatch/testdata/old.file b/applypatch/testdata/old.file deleted file mode 100644 index 166c8732e..000000000 Binary files a/applypatch/testdata/old.file and /dev/null differ diff --git a/applypatch/testdata/patch.bsdiff b/applypatch/testdata/patch.bsdiff deleted file mode 100644 index b78d38573..000000000 Binary files a/applypatch/testdata/patch.bsdiff and /dev/null differ -- cgit v1.2.3 From 391bb7bb924a1b0a7990a8a7e2875ee94387e73d Mon Sep 17 00:00:00 2001 From: Jinguang Dong Date: Wed, 26 Apr 2017 10:40:45 +0800 Subject: applypatch: Add determine the return value of ApplyDiffPatch and capture the error flow. Construct ota package which is bsdiff exception scene ,then do simulation test, native code can not capture exception scenes. Test: recovery_component_test Test: Apply an bsdiff exception scene ota package. Change-Id: Icd9f6eac78739bd35c74b9fcaaf8154335d680a5 --- applypatch/imgpatch.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'applypatch') diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp index 7d8b7361c..3d905f7b4 100644 --- a/applypatch/imgpatch.cpp +++ b/applypatch/imgpatch.cpp @@ -100,7 +100,10 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* printf("source data too short\n"); return -1; } - ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink, ctx); + if (ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink, ctx) != 0) { + printf("Failed to apply bsdiff patch.\n"); + return -1; + } } else if (type == CHUNK_RAW) { const char* raw_header = &patch->data[pos]; pos += 4; -- cgit v1.2.3 From 8ab28082b77767cab144c3eeaee358598d8e3007 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 25 Apr 2017 21:13:21 -0700 Subject: applypatch: Remove the obsolete support for "applypatch -s". The SpaceMode (applypatch -s) was used in amend script (cupcake) only, which has been removed since commit 9ce2ebf5d300eba5f6086583b0941ef68a3e4b42 (platform/build). The later (and current) edify script uses apply_patch_space(). Note that other modes (PatchMode, CheckMode) of applypatch executable are mostly used by install-recovery.sh script. Test: No active user of "applypatch -s". Test: recovery_component_test Change-Id: I1d689b7fedd3884077e88ed1d6c22f7a2198859d --- applypatch/applypatch_modes.cpp | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) (limited to 'applypatch') diff --git a/applypatch/applypatch_modes.cpp b/applypatch/applypatch_modes.cpp index 7b191a801..aa32d57ef 100644 --- a/applypatch/applypatch_modes.cpp +++ b/applypatch/applypatch_modes.cpp @@ -44,19 +44,6 @@ static int CheckMode(int argc, const char** argv) { return applypatch_check(argv[2], sha1); } -static int SpaceMode(int argc, const char** argv) { - if (argc != 3) { - return 2; - } - - size_t bytes; - if (!android::base::ParseUint(argv[2], &bytes) || bytes == 0) { - printf("can't parse \"%s\" as byte count\n\n", argv[2]); - return 1; - } - return CacheSizeCheck(bytes); -} - // Parse arguments (which should be of the form ":" into the // new parallel arrays *sha1s and *files. Returns true on success. static bool ParsePatchArgs(int argc, const char** argv, std::vector* sha1s, @@ -175,13 +162,12 @@ int applypatch_modes(int argc, const char** argv) { "usage: %s [-b ] " "[: ...]\n" " or %s -c [ ...]\n" - " or %s -s \n" " or %s -l\n" "\n" "Filenames may be of the form\n" " EMMC::::::...\n" "to specify reading from or writing to an EMMC partition.\n\n", - argv[0], argv[0], argv[0], argv[0]); + argv[0], argv[0], argv[0]); return 2; } @@ -191,8 +177,6 @@ int applypatch_modes(int argc, const char** argv) { result = ShowLicenses(); } else if (strncmp(argv[1], "-c", 3) == 0) { result = CheckMode(argc, argv); - } else if (strncmp(argv[1], "-s", 3) == 0) { - result = SpaceMode(argc, argv); } else { result = PatchMode(argc, argv); } -- cgit v1.2.3 From ce5fa5e5384508655c804519d428a402bb3df1d9 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Mon, 15 May 2017 12:32:33 -0700 Subject: Print SHA1 of the patch if bsdiff fails with data error This will help us to identify the patch corruption. Meanwhile fix a wrong size parameter passed to bspatch. (patch->data.size() into patch->data.size() - patch_offset). Also remove the only usage of "ApplyBSDiffPatchMem()" and inline its Sink function for simplicity. Bug: 37855643 Test: Prints SHA1 for corrupted patch in imgdiff_test. Change-Id: Ibf2db8c08b0ded1409bb7c91a3547a6bf99c601d --- applypatch/bspatch.cpp | 38 +++++++++++++++++++----------- applypatch/imgpatch.cpp | 14 ++++++----- applypatch/include/applypatch/applypatch.h | 2 -- 3 files changed, 32 insertions(+), 22 deletions(-) (limited to 'applypatch') diff --git a/applypatch/bspatch.cpp b/applypatch/bspatch.cpp index f75a2c680..65ee614ef 100644 --- a/applypatch/bspatch.cpp +++ b/applypatch/bspatch.cpp @@ -23,10 +23,14 @@ #include #include +#include + +#include #include #include #include "applypatch/applypatch.h" +#include "print_sha1.h" void ShowBSDiffLicense() { puts("The bsdiff library used herein is:\n" @@ -67,18 +71,24 @@ int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value if (ctx) SHA1_Update(ctx, data, len); return len; }; - return bsdiff::bspatch(old_data, old_size, - reinterpret_cast(&patch->data[patch_offset]), - patch->data.size(), sha_sink); -} -int ApplyBSDiffPatchMem(const unsigned char* old_data, size_t old_size, const Value* patch, - size_t patch_offset, std::vector* 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; - }; - return bsdiff::bspatch(old_data, old_size, - reinterpret_cast(&patch->data[patch_offset]), - patch->data.size(), vector_sink); -} + CHECK(patch != nullptr); + CHECK_LE(patch_offset, patch->data.size()); + + int result = bsdiff::bspatch(old_data, old_size, + reinterpret_cast(&patch->data[patch_offset]), + patch->data.size() - patch_offset, sha_sink); + if (result != 0) { + LOG(ERROR) << "bspatch failed, result: " << result; + // print SHA1 of the patch in the case of a data error. + if (result == 2) { + uint8_t digest[SHA_DIGEST_LENGTH]; + SHA1(reinterpret_cast(patch->data.data() + patch_offset), + patch->data.size() - patch_offset, digest); + std::string patch_sha1 = print_sha1(digest); + LOG(ERROR) << "Patch may be corrupted, offset: " << patch_offset << ", SHA1: " + << patch_sha1; + } + } + return result; +} \ No newline at end of file diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp index 3d905f7b4..702a624ae 100644 --- a/applypatch/imgpatch.cpp +++ b/applypatch/imgpatch.cpp @@ -200,12 +200,14 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* } // Next, apply the bsdiff patch (in memory) to the uncompressed data. - std::vector uncompressed_target_data; - // TODO(senj): Remove the only usage of ApplyBSDiffPatchMem here, - // replace it with ApplyBSDiffPatch with a custom sink function that - // wraps the given sink function to stream output to save memory. - if (ApplyBSDiffPatchMem(expanded_source.data(), expanded_len, patch, patch_offset, - &uncompressed_target_data) != 0) { + std::vector uncompressed_target_data; + // TODO: replace the custom sink function passed into ApplyBSDiffPatch so that it wraps the + // given sink function to stream output to save memory. + if (ApplyBSDiffPatch(expanded_source.data(), expanded_len, patch, patch_offset, + [&uncompressed_target_data](const uint8_t* data, size_t len) { + uncompressed_target_data.insert(uncompressed_target_data.end(), data, data + len); + return len; + }, nullptr) != 0) { return -1; } if (uncompressed_target_data.size() != target_len) { diff --git a/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h index da55432d5..581360ef1 100644 --- a/applypatch/include/applypatch/applypatch.h +++ b/applypatch/include/applypatch/applypatch.h @@ -69,8 +69,6 @@ int SaveFileContents(const char* filename, const FileContents* file); void ShowBSDiffLicense(); 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* new_data); // imgpatch.cpp int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* patch, SinkFn sink, -- cgit v1.2.3 From a897f95bd5426aff62c7bb95170985b90123a468 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 17 May 2017 22:41:55 -0700 Subject: Implement a custom deflate sink function for bspatch This new sink function works as a wrapper of the old sink. It deflates the available patch data on the fly. Therefore, we don't need to store the full uncompressed patch data in memory. Test: recovery_component_test && apply an incremental update on angler Change-Id: I2274ec50a1607089abcc9d0954a2a748f28c3122 --- applypatch/imgpatch.cpp | 144 +++++++++++++++++++++++++++++------------------- 1 file changed, 86 insertions(+), 58 deletions(-) (limited to 'applypatch') diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp index 702a624ae..df75f98d4 100644 --- a/applypatch/imgpatch.cpp +++ b/applypatch/imgpatch.cpp @@ -26,12 +26,14 @@ #include #include +#include #include #include +#include +#include #include #include -#include #include #include @@ -43,6 +45,86 @@ static inline int32_t Read4(const void *address) { return android::base::get_unaligned(address); } +// This function is a wrapper of ApplyBSDiffPatch(). It has a custom sink function to deflate the +// patched data and stream the deflated data to output. +static bool ApplyBSDiffPatchAndStreamOutput(const uint8_t* src_data, size_t src_len, + const Value* patch, size_t patch_offset, + const char* deflate_header, SinkFn sink, SHA_CTX* ctx) { + size_t expected_target_length = static_cast(Read8(deflate_header + 32)); + int level = Read4(deflate_header + 40); + int method = Read4(deflate_header + 44); + int window_bits = Read4(deflate_header + 48); + int mem_level = Read4(deflate_header + 52); + int strategy = Read4(deflate_header + 56); + + std::unique_ptr strm(new z_stream(), deflateEnd); + strm->zalloc = Z_NULL; + strm->zfree = Z_NULL; + strm->opaque = Z_NULL; + strm->avail_in = 0; + strm->next_in = nullptr; + int ret = deflateInit2(strm.get(), level, method, window_bits, mem_level, strategy); + if (ret != Z_OK) { + LOG(ERROR) << "Failed to init uncompressed data deflation: " << ret; + return false; + } + + // Define a custom sink wrapper that feeds to bspatch. It deflates the available patch data on + // the fly and outputs the compressed data to the given sink. + size_t actual_target_length = 0; + size_t total_written = 0; + static constexpr size_t buffer_size = 32768; + auto compression_sink = [&](const uint8_t* data, size_t len) -> size_t { + // The input patch length for an update never exceeds INT_MAX. + strm->avail_in = len; + strm->next_in = data; + do { + std::vector buffer(buffer_size); + strm->avail_out = buffer_size; + strm->next_out = buffer.data(); + if (actual_target_length + len < expected_target_length) { + ret = deflate(strm.get(), Z_NO_FLUSH); + } else { + ret = deflate(strm.get(), Z_FINISH); + } + if (ret != Z_OK && ret != Z_STREAM_END) { + LOG(ERROR) << "Failed to deflate stream: " << ret; + // zero length indicates an error in the sink function of bspatch(). + return 0; + } + + size_t have = buffer_size - strm->avail_out; + total_written += have; + if (sink(buffer.data(), have) != have) { + LOG(ERROR) << "Failed to write " << have << " compressed bytes to output."; + return 0; + } + if (ctx) SHA1_Update(ctx, buffer.data(), have); + } while ((strm->avail_in != 0 || strm->avail_out == 0) && ret != Z_STREAM_END); + + actual_target_length += len; + return len; + }; + + if (ApplyBSDiffPatch(src_data, src_len, patch, patch_offset, compression_sink, nullptr) != 0) { + return false; + } + + if (ret != Z_STREAM_END) { + LOG(ERROR) << "ret is expected to be Z_STREAM_END, but it's " << ret; + return false; + } + + if (expected_target_length != actual_target_length) { + LOG(ERROR) << "target length is expected to be " << expected_target_length << ", but it's " + << actual_target_length; + return false; + } + LOG(DEBUG) << "bspatch writes " << total_written << " bytes in total to streaming output."; + + return true; +} + 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(patch_data), patch_size)); @@ -137,12 +219,6 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* size_t src_len = static_cast(Read8(deflate_header + 8)); size_t patch_offset = static_cast(Read8(deflate_header + 16)); size_t expanded_len = static_cast(Read8(deflate_header + 24)); - size_t target_len = static_cast(Read8(deflate_header + 32)); - int level = Read4(deflate_header + 40); - int method = Read4(deflate_header + 44); - int windowBits = Read4(deflate_header + 48); - int memLevel = Read4(deflate_header + 52); - int strategy = Read4(deflate_header + 56); if (src_start + src_len > old_size) { printf("source data too short\n"); @@ -199,60 +275,12 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* } } - // Next, apply the bsdiff patch (in memory) to the uncompressed data. - std::vector uncompressed_target_data; - // TODO: replace the custom sink function passed into ApplyBSDiffPatch so that it wraps the - // given sink function to stream output to save memory. - if (ApplyBSDiffPatch(expanded_source.data(), expanded_len, patch, patch_offset, - [&uncompressed_target_data](const uint8_t* data, size_t len) { - uncompressed_target_data.insert(uncompressed_target_data.end(), data, data + len); - return len; - }, nullptr) != 0) { - return -1; - } - if (uncompressed_target_data.size() != target_len) { - printf("expected target len to be %zu, but it's %zu\n", target_len, - uncompressed_target_data.size()); + if (!ApplyBSDiffPatchAndStreamOutput(expanded_source.data(), expanded_len, patch, + patch_offset, deflate_header, sink, ctx)) { + LOG(ERROR) << "Fail to apply streaming bspatch."; return -1; } - // Now compress the target data and append it to the output. - - // we're done with the expanded_source data buffer, so we'll - // reuse that memory to receive the output of deflate. - if (expanded_source.size() < 32768U) { - expanded_source.resize(32768U); - } - - { - std::vector& temp_data = expanded_source; - - // now the deflate stream - z_stream strm; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = uncompressed_target_data.size(); - strm.next_in = uncompressed_target_data.data(); - int ret = deflateInit2(&strm, level, method, windowBits, memLevel, strategy); - if (ret != Z_OK) { - printf("failed to init uncompressed data deflation: %d\n", ret); - return -1; - } - do { - strm.avail_out = temp_data.size(); - strm.next_out = temp_data.data(); - ret = deflate(&strm, Z_FINISH); - size_t have = temp_data.size() - strm.avail_out; - - if (sink(temp_data.data(), have) != have) { - printf("failed to write %zd compressed bytes to output\n", have); - return -1; - } - if (ctx) SHA1_Update(ctx, temp_data.data(), have); - } while (ret != Z_STREAM_END); - deflateEnd(&strm); - } } else { printf("patch chunk %d is unknown type %d\n", i, type); return -1; -- cgit v1.2.3 From 14ebc1e5ae6968424eb242f3b0330f82e475a1e4 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 5 Jul 2017 12:04:07 -0700 Subject: Fix a rare failure for imgdiff when random data equals gzip header In a rare case, a random chunk will pass both the gzip header check and the inflation process; but fail the uncompressed length check in the footer. This leads to a imgdiff failure. So, we should treat this chunk as 'normal' instead of 'inflated' while generating the patch. Bug: 63334984 Test: imgdiff generates patch successfully on previous failing images. Change-Id: Ice84f22d3653bce9756bda91e70528c0d2f264a0 --- applypatch/imgdiff.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'applypatch') diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index 41d73ab98..fc240644f 100644 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -693,6 +693,20 @@ static bool ReadImage(const char* filename, std::vector* chunks, continue; } + // The footer contains the size of the uncompressed data. Double-check to make sure that it + // matches the size of the data we got when we actually did the decompression. + size_t footer_index = pos + raw_data_len + GZIP_FOOTER_LEN - 4; + if (sz - footer_index < 4) { + printf("Warning: invalid footer position; treating as a nomal chunk\n"); + continue; + } + size_t footer_size = get_unaligned(img->data() + footer_index); + if (footer_size != uncompressed_len) { + printf("Warning: footer size %zu != decompressed size %zu; treating as a nomal chunk\n", + footer_size, uncompressed_len); + continue; + } + ImageChunk body(CHUNK_DEFLATE, pos, img, raw_data_len); uncompressed_data.resize(uncompressed_len); body.SetUncompressedData(std::move(uncompressed_data)); @@ -704,17 +718,6 @@ static bool ReadImage(const char* filename, std::vector* chunks, chunks->emplace_back(CHUNK_NORMAL, pos, img, GZIP_FOOTER_LEN); pos += GZIP_FOOTER_LEN; - - // The footer (that we just skipped over) contains the size of - // the uncompressed data. Double-check to make sure that it - // matches the size of the data we got when we actually did - // the decompression. - size_t footer_size = get_unaligned(img->data() + pos - 4); - if (footer_size != body.DataLengthForPatch()) { - printf("Error: footer size %zu != decompressed size %zu\n", footer_size, - body.GetRawDataLength()); - return false; - } } else { // Use a normal chunk to take all the contents until the next gzip chunk (or EOF); we expect // the number of chunks to be small (5 for typical boot and recovery images). -- cgit v1.2.3