Merge "Add tests for imgdiff." am: 344c8eb453 am: 4fe022c4ed am: 4e2471d6dd

am: e6c1d578bd

Change-Id: If99c2af21db684d3874649144f0d1ff780474616
This commit is contained in:
Tao Bao 2016-12-28 18:33:13 +00:00 committed by android-build-merger
commit 6d83a14036
8 changed files with 813 additions and 267 deletions

View file

@ -17,7 +17,6 @@ LOCAL_PATH := $(call my-dir)
# libapplypatch (static library)
# ===============================
include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_SRC_FILES := \
applypatch.cpp \
bspatch.cpp \
@ -26,11 +25,11 @@ LOCAL_SRC_FILES := \
utils.cpp
LOCAL_MODULE := libapplypatch
LOCAL_MODULE_TAGS := eng
LOCAL_C_INCLUDES += \
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include \
bootable/recovery
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_STATIC_LIBRARIES += \
LOCAL_STATIC_LIBRARIES := \
libotafault \
libbase \
libcrypto \
@ -42,36 +41,45 @@ include $(BUILD_STATIC_LIBRARY)
# libimgpatch (static library)
# ===============================
include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_SRC_FILES := bspatch.cpp imgpatch.cpp utils.cpp
LOCAL_SRC_FILES := \
bspatch.cpp \
imgpatch.cpp \
utils.cpp
LOCAL_MODULE := libimgpatch
LOCAL_C_INCLUDES += \
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include \
bootable/recovery
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_STATIC_LIBRARIES += libcrypto libbz libz
LOCAL_STATIC_LIBRARIES := \
libcrypto \
libbz \
libz
LOCAL_CFLAGS := -Werror
include $(BUILD_STATIC_LIBRARY)
# libimgpatch (host static library)
# ===============================
include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_SRC_FILES := bspatch.cpp imgpatch.cpp utils.cpp
LOCAL_SRC_FILES := \
bspatch.cpp \
imgpatch.cpp \
utils.cpp
LOCAL_MODULE := libimgpatch
LOCAL_MODULE_HOST_OS := linux
LOCAL_C_INCLUDES += \
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include \
bootable/recovery
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
LOCAL_STATIC_LIBRARIES += libcrypto libbz libz
LOCAL_STATIC_LIBRARIES := \
libcrypto \
libbz \
libz
LOCAL_CFLAGS := -Werror
include $(BUILD_HOST_STATIC_LIBRARY)
# libapplypatch_modes (static library)
# ===============================
include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_SRC_FILES := \
applypatch_modes.cpp
LOCAL_MODULE := libapplypatch_modes
@ -87,7 +95,6 @@ include $(BUILD_STATIC_LIBRARY)
# applypatch (target executable)
# ===============================
include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_SRC_FILES := applypatch_main.cpp
LOCAL_MODULE := applypatch
LOCAL_C_INCLUDES := bootable/recovery
@ -106,18 +113,59 @@ LOCAL_SHARED_LIBRARIES := \
LOCAL_CFLAGS := -Werror
include $(BUILD_EXECUTABLE)
libimgdiff_src_files := \
imgdiff.cpp \
utils.cpp
# libbsdiff is compiled with -D_FILE_OFFSET_BITS=64.
libimgdiff_cflags := \
-Werror \
-D_FILE_OFFSET_BITS=64
libimgdiff_static_libraries := \
libbsdiff \
libz
# libimgdiff (static library)
# ===============================
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
$(libimgdiff_src_files)
LOCAL_MODULE := libimgdiff
LOCAL_CFLAGS := \
$(libimgdiff_cflags)
LOCAL_STATIC_LIBRARIES := \
$(libimgdiff_static_libraries)
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
include $(BUILD_STATIC_LIBRARY)
# libimgdiff (host static library)
# ===============================
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
$(libimgdiff_src_files)
LOCAL_MODULE := libimgdiff
LOCAL_CFLAGS := \
$(libimgdiff_cflags)
LOCAL_STATIC_LIBRARIES := \
$(libimgdiff_static_libraries)
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
include $(BUILD_HOST_STATIC_LIBRARY)
# imgdiff (host static executable)
# ===============================
include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_SRC_FILES := imgdiff.cpp utils.cpp
LOCAL_SRC_FILES := imgdiff_main.cpp
LOCAL_MODULE := imgdiff
LOCAL_STATIC_LIBRARIES += \
libbsdiff \
libbz \
libdivsufsort64 \
libdivsufsort \
libz
LOCAL_CFLAGS := -Werror
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_STATIC_LIBRARIES := \
libimgdiff \
$(libimgdiff_static_libraries) \
libbz \
libdivsufsort \
libdivsufsort64
include $(BUILD_HOST_EXECUTABLE)

View file

@ -121,19 +121,19 @@
* information that is stored on the system partition.
*/
#include "applypatch/imgdiff.h"
#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
#include <bsdiff.h>
#include <zlib.h>
#include "zlib.h"
#include "imgdiff.h"
#include "utils.h"
typedef struct {
@ -374,8 +374,7 @@ unsigned char* ReadZip(const char* filename,
* return value when done with all the chunks. Returns NULL on
* failure.
*/
unsigned char* ReadImage(const char* filename,
int* num_chunks, ImageChunk** chunks) {
unsigned char* ReadImage(const char* filename, int* num_chunks, ImageChunk** chunks) {
struct stat st;
if (stat(filename, &st) != 0) {
printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
@ -403,7 +402,7 @@ unsigned char* ReadImage(const char* filename,
*chunks = NULL;
while (pos < sz) {
unsigned char* p = img+pos;
unsigned char* p = img + pos;
if (sz - pos >= 4 &&
p[0] == 0x1f && p[1] == 0x8b &&
@ -413,8 +412,7 @@ unsigned char* ReadImage(const char* filename,
size_t chunk_offset = pos;
*num_chunks += 3;
*chunks = static_cast<ImageChunk*>(realloc(*chunks,
*num_chunks * sizeof(ImageChunk)));
*chunks = static_cast<ImageChunk*>(realloc(*chunks, *num_chunks * sizeof(ImageChunk)));
ImageChunk* curr = *chunks + (*num_chunks-3);
// create a normal chunk for the header.
@ -502,8 +500,7 @@ unsigned char* ReadImage(const char* filename,
// the decompression.
size_t footer_size = Read4(p-4);
if (footer_size != curr[-2].len) {
printf("Error: footer size %zu != decompressed size %zu\n",
footer_size, curr[-2].len);
printf("Error: footer size %zu != decompressed size %zu\n", footer_size, curr[-2].len);
free(img);
return NULL;
}
@ -637,7 +634,11 @@ unsigned char* MakePatch(ImageChunk* src, ImageChunk* tgt, size_t* size) {
}
}
#if defined(__ANDROID__)
char ptemp[] = "/data/local/tmp/imgdiff-patch-XXXXXX";
#else
char ptemp[] = "/tmp/imgdiff-patch-XXXXXX";
#endif
int fd = mkstemp(ptemp);
if (fd == -1) {
@ -793,10 +794,8 @@ void MergeAdjacentNormalChunks(ImageChunk* chunks, int* num_chunks) {
*num_chunks = out;
}
ImageChunk* FindChunkByName(const char* name,
ImageChunk* chunks, int num_chunks) {
int i;
for (i = 0; i < num_chunks; ++i) {
ImageChunk* FindChunkByName(const char* name, ImageChunk* chunks, int num_chunks) {
for (int i = 0; i < num_chunks; ++i) {
if (chunks[i].type == CHUNK_DEFLATE && chunks[i].filename &&
strcmp(name, chunks[i].filename) == 0) {
return chunks+i;
@ -812,11 +811,11 @@ void DumpChunks(ImageChunk* chunks, int num_chunks) {
}
}
int main(int argc, char** argv) {
int zip_mode = 0;
int imgdiff(int argc, const char** argv) {
bool zip_mode = false;
if (argc >= 2 && strcmp(argv[1], "-z") == 0) {
zip_mode = 1;
zip_mode = true;
--argc;
++argv;
}
@ -880,12 +879,10 @@ int main(int argc, char** argv) {
// Verify that the source and target images have the same chunk
// structure (ie, the same sequence of deflate and normal chunks).
if (!zip_mode) {
// Merge the gzip header and footer in with any adjacent
// normal chunks.
MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks);
MergeAdjacentNormalChunks(src_chunks, &num_src_chunks);
}
// Merge the gzip header and footer in with any adjacent
// normal chunks.
MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks);
MergeAdjacentNormalChunks(src_chunks, &num_src_chunks);
if (num_src_chunks != num_tgt_chunks) {
printf("source and target don't have same number of chunks!\n");
@ -897,8 +894,7 @@ int main(int argc, char** argv) {
}
for (i = 0; i < num_src_chunks; ++i) {
if (src_chunks[i].type != tgt_chunks[i].type) {
printf("source and target don't have same chunk "
"structure! (chunk %d)\n", i);
printf("source and target don't have same chunk structure! (chunk %d)\n", i);
printf("source chunks:\n");
DumpChunks(src_chunks, num_src_chunks);
printf("target chunks:\n");
@ -983,8 +979,7 @@ int main(int argc, char** argv) {
if (zip_mode) {
ImageChunk* src;
if (tgt_chunks[i].type == CHUNK_DEFLATE &&
(src = FindChunkByName(tgt_chunks[i].filename, src_chunks,
num_src_chunks))) {
(src = FindChunkByName(tgt_chunks[i].filename, src_chunks, num_src_chunks))) {
patch_data[i] = MakePatch(src, tgt_chunks+i, patch_size+i);
} else {
patch_data[i] = MakePatch(src_chunks, tgt_chunks+i, patch_size+i);
@ -1000,8 +995,7 @@ int main(int argc, char** argv) {
patch_data[i] = MakePatch(src_chunks+i, tgt_chunks+i, patch_size+i);
}
printf("patch %3d is %zu bytes (of %zu)\n",
i, patch_size[i], tgt_chunks[i].source_len);
printf("patch %3d is %zu bytes (of %zu)\n", i, patch_size[i], tgt_chunks[i].source_len);
}
// Figure out how big the imgdiff file header is going to be, so

View file

@ -0,0 +1,21 @@
/*
* Copyright (C) 2016 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 "applypatch/imgdiff.h"
int main(int argc, char** argv) {
return imgdiff(argc, const_cast<const char**>(argv));
}

View file

@ -14,32 +14,34 @@
* limitations under the License.
*/
// See imgdiff.c in this directory for a description of the patch file
// See imgdiff.cpp in this directory for a description of the patch file
// format.
#include <applypatch/imgpatch.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/cdefs.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <string>
#include <vector>
#include "zlib.h"
#include "openssl/sha.h"
#include "applypatch/applypatch.h"
#include "imgdiff.h"
#include <applypatch/applypatch.h>
#include <applypatch/imgdiff.h>
#include <openssl/sha.h>
#include <zlib.h>
#include "utils.h"
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
const unsigned char* patch_data, ssize_t patch_size,
SinkFn sink, void* token) {
Value patch(VAL_BLOB, std::string(
reinterpret_cast<const char*>(patch_data), patch_size));
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, token, nullptr, nullptr);
}
/*
@ -48,208 +50,201 @@ 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) {
if (patch->data.size() < 12) {
printf("patch too short to contain header\n");
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) {
if (patch->data.size() < 12) {
printf("patch too short to contain header\n");
return -1;
}
// IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW.
// (IMGDIFF1, which is no longer supported, used CHUNK_NORMAL and
// CHUNK_GZIP.)
size_t pos = 12;
const char* header = &patch->data[0];
if (memcmp(header, "IMGDIFF2", 8) != 0) {
printf("corrupt patch file header (magic number)\n");
return -1;
}
int num_chunks = Read4(header + 8);
for (int i = 0; i < num_chunks; ++i) {
// each chunk's header record starts with 4 bytes.
if (pos + 4 > patch->data.size()) {
printf("failed to read chunk %d record\n", i);
return -1;
}
int type = Read4(&patch->data[pos]);
pos += 4;
if (type == CHUNK_NORMAL) {
const char* normal_header = &patch->data[pos];
pos += 24;
if (pos > patch->data.size()) {
printf("failed to read chunk %d normal header data\n", i);
return -1;
}
}
// IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW.
// (IMGDIFF1, which is no longer supported, used CHUNK_NORMAL and
// CHUNK_GZIP.)
size_t pos = 12;
const char* header = &patch->data[0];
if (memcmp(header, "IMGDIFF2", 8) != 0) {
printf("corrupt patch file header (magic number)\n");
size_t src_start = Read8(normal_header);
size_t src_len = Read8(normal_header + 8);
size_t patch_offset = Read8(normal_header + 16);
if (src_start + src_len > static_cast<size_t>(old_size)) {
printf("source data too short\n");
return -1;
}
}
ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink, token, ctx);
} else if (type == CHUNK_RAW) {
const char* raw_header = &patch->data[pos];
pos += 4;
if (pos > patch->data.size()) {
printf("failed to read chunk %d raw header data\n", i);
return -1;
}
int num_chunks = Read4(header+8);
ssize_t data_len = Read4(raw_header);
for (int i = 0; i < num_chunks; ++i) {
// each chunk's header record starts with 4 bytes.
if (pos + 4 > patch->data.size()) {
printf("failed to read chunk %d record\n", i);
return -1;
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) {
printf("failed to write chunk %d raw data\n", i);
return -1;
}
pos += data_len;
} else if (type == CHUNK_DEFLATE) {
// deflate chunks have an additional 60 bytes in their chunk header.
const char* deflate_header = &patch->data[pos];
pos += 60;
if (pos > patch->data.size()) {
printf("failed to read chunk %d deflate header data\n", i);
return -1;
}
size_t src_start = Read8(deflate_header);
size_t src_len = Read8(deflate_header + 8);
size_t patch_offset = Read8(deflate_header + 16);
size_t expanded_len = Read8(deflate_header + 24);
size_t target_len = 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 > static_cast<size_t>(old_size)) {
printf("source data too short\n");
return -1;
}
// Decompress the source data; the chunk header tells us exactly
// how big we expect it to be when decompressed.
// Note: expanded_len will include the bonus data size if
// the patch was constructed with bonus data. The
// deflation will come up 'bonus_size' bytes short; these
// must be appended from the bonus_data value.
size_t bonus_size = (i == 1 && bonus_data != NULL) ? bonus_data->data.size() : 0;
std::vector<unsigned char> expanded_source(expanded_len);
// inflate() doesn't like strm.next_out being a nullptr even with
// avail_out being zero (Z_STREAM_ERROR).
if (expanded_len != 0) {
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = src_len;
strm.next_in = const_cast<unsigned char*>(old_data + src_start);
strm.avail_out = expanded_len;
strm.next_out = expanded_source.data();
int ret = inflateInit2(&strm, -15);
if (ret != Z_OK) {
printf("failed to init source inflation: %d\n", ret);
return -1;
}
int type = Read4(&patch->data[pos]);
pos += 4;
if (type == CHUNK_NORMAL) {
const char* normal_header = &patch->data[pos];
pos += 24;
if (pos > patch->data.size()) {
printf("failed to read chunk %d normal header data\n", i);
return -1;
}
size_t src_start = Read8(normal_header);
size_t src_len = Read8(normal_header+8);
size_t patch_offset = Read8(normal_header+16);
if (src_start + src_len > static_cast<size_t>(old_size)) {
printf("source data too short\n");
return -1;
}
ApplyBSDiffPatch(old_data + src_start, src_len,
patch, patch_offset, sink, token, ctx);
} else if (type == CHUNK_RAW) {
const char* raw_header = &patch->data[pos];
pos += 4;
if (pos > patch->data.size()) {
printf("failed to read chunk %d raw header data\n", i);
return -1;
}
ssize_t data_len = 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) {
printf("failed to write chunk %d raw data\n", i);
return -1;
}
pos += data_len;
} else if (type == CHUNK_DEFLATE) {
// deflate chunks have an additional 60 bytes in their chunk header.
const char* deflate_header = &patch->data[pos];
pos += 60;
if (pos > patch->data.size()) {
printf("failed to read chunk %d deflate header data\n", i);
return -1;
}
size_t src_start = Read8(deflate_header);
size_t src_len = Read8(deflate_header+8);
size_t patch_offset = Read8(deflate_header+16);
size_t expanded_len = Read8(deflate_header+24);
size_t target_len = 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 > static_cast<size_t>(old_size)) {
printf("source data too short\n");
return -1;
}
// Decompress the source data; the chunk header tells us exactly
// how big we expect it to be when decompressed.
// Note: expanded_len will include the bonus data size if
// the patch was constructed with bonus data. The
// deflation will come up 'bonus_size' bytes short; these
// must be appended from the bonus_data value.
size_t bonus_size = (i == 1 && bonus_data != NULL) ? bonus_data->data.size() : 0;
std::vector<unsigned char> expanded_source(expanded_len);
// inflate() doesn't like strm.next_out being a nullptr even with
// avail_out being zero (Z_STREAM_ERROR).
if (expanded_len != 0) {
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = src_len;
strm.next_in = (unsigned char*)(old_data + src_start);
strm.avail_out = expanded_len;
strm.next_out = expanded_source.data();
int ret;
ret = inflateInit2(&strm, -15);
if (ret != Z_OK) {
printf("failed to init source inflation: %d\n", ret);
return -1;
}
// Because we've provided enough room to accommodate the output
// data, we expect one call to inflate() to suffice.
ret = inflate(&strm, Z_SYNC_FLUSH);
if (ret != Z_STREAM_END) {
printf("source inflation returned %d\n", ret);
return -1;
}
// We should have filled the output buffer exactly, except
// for the bonus_size.
if (strm.avail_out != bonus_size) {
printf("source inflation short by %zu bytes\n", strm.avail_out-bonus_size);
return -1;
}
inflateEnd(&strm);
if (bonus_size) {
memcpy(expanded_source.data() + (expanded_len - bonus_size),
&bonus_data->data[0], bonus_size);
}
}
// Next, apply the bsdiff patch (in memory) to the uncompressed
// data.
std::vector<unsigned char> uncompressed_target_data;
if (ApplyBSDiffPatchMem(expanded_source.data(), expanded_len,
patch, patch_offset,
&uncompressed_target_data) != 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());
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<unsigned char>& 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);
ssize_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);
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;
// Because we've provided enough room to accommodate the output
// data, we expect one call to inflate() to suffice.
ret = inflate(&strm, Z_SYNC_FLUSH);
if (ret != Z_STREAM_END) {
printf("source inflation returned %d\n", ret);
return -1;
}
}
// We should have filled the output buffer exactly, except
// for the bonus_size.
if (strm.avail_out != bonus_size) {
printf("source inflation short by %zu bytes\n", strm.avail_out - bonus_size);
return -1;
}
inflateEnd(&strm);
return 0;
if (bonus_size) {
memcpy(expanded_source.data() + (expanded_len - bonus_size), &bonus_data->data[0],
bonus_size);
}
}
// Next, apply the bsdiff patch (in memory) to the uncompressed data.
std::vector<unsigned char> uncompressed_target_data;
if (ApplyBSDiffPatchMem(expanded_source.data(), expanded_len, patch, patch_offset,
&uncompressed_target_data) != 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());
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<unsigned char>& 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);
ssize_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);
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;
}
}
return 0;
}

View file

@ -14,17 +14,26 @@
* limitations under the License.
*/
#ifndef _APPLYPATCH_IMGDIFF_H
#define _APPLYPATCH_IMGDIFF_H
#include <stddef.h>
// Image patch chunk types
#define CHUNK_NORMAL 0
#define CHUNK_GZIP 1 // version 1 only
#define CHUNK_DEFLATE 2 // version 2 only
#define CHUNK_RAW 3 // version 2 only
#define CHUNK_NORMAL 0
#define CHUNK_GZIP 1 // version 1 only
#define CHUNK_DEFLATE 2 // version 2 only
#define CHUNK_RAW 3 // version 2 only
// The gzip header size is actually variable, but we currently don't
// support gzipped data with any of the optional fields, so for now it
// will always be ten bytes. See RFC 1952 for the definition of the
// gzip format.
#define GZIP_HEADER_LEN 10
static constexpr size_t GZIP_HEADER_LEN = 10;
// The gzip footer size really is fixed.
#define GZIP_FOOTER_LEN 8
static constexpr size_t GZIP_FOOTER_LEN = 8;
int imgdiff(int argc, const char** argv);
#endif // _APPLYPATCH_IMGDIFF_H

View file

@ -14,13 +14,15 @@
* limitations under the License.
*/
#ifndef _IMGPATCH_H
#define _IMGPATCH_H
#ifndef _APPLYPATCH_IMGPATCH_H
#define _APPLYPATCH_IMGPATCH_H
typedef ssize_t (*SinkFn)(const unsigned char*, ssize_t, void*);
#include <sys/types.h>
using SinkFn = ssize_t (*)(const unsigned char*, ssize_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);
#endif //_IMGPATCH_H
#endif // _APPLYPATCH_IMGPATCH_H

View file

@ -65,6 +65,7 @@ LOCAL_SRC_FILES := \
component/applypatch_test.cpp \
component/bootloader_message_test.cpp \
component/edify_test.cpp \
component/imgdiff_test.cpp \
component/uncrypt_test.cpp \
component/updater_test.cpp \
component/verifier_test.cpp
@ -83,6 +84,9 @@ LOCAL_STATIC_LIBRARIES := \
libapplypatch_modes \
libapplypatch \
libedify \
libimgdiff \
libimgpatch \
libbsdiff \
libotafault \
libupdater \
libbootloader_message \
@ -90,6 +94,8 @@ LOCAL_STATIC_LIBRARIES := \
libminui \
libotautil \
libmounts \
libdivsufsort \
libdivsufsort64 \
libfs_mgr \
liblog \
libselinux \
@ -130,3 +136,26 @@ LOCAL_GENERATED_SOURCES += $(GEN)
LOCAL_PICKUP_FILES := $(testdata_continuous_zip_prefix)
include $(BUILD_NATIVE_TEST)
# Host tests
include $(CLEAR_VARS)
LOCAL_CFLAGS := -Werror
LOCAL_MODULE := recovery_host_test
LOCAL_MODULE_HOST_OS := linux
LOCAL_C_INCLUDES := bootable/recovery
LOCAL_SRC_FILES := \
component/imgdiff_test.cpp
LOCAL_STATIC_LIBRARIES := \
libimgdiff \
libimgpatch \
libbsdiff \
libziparchive \
libbase \
libcrypto \
libbz \
libdivsufsort64 \
libdivsufsort \
libz
LOCAL_SHARED_LIBRARIES := \
liblog
include $(BUILD_HOST_NATIVE_TEST)

View file

@ -0,0 +1,448 @@
/*
* Copyright (C) 2016 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 <string>
#include <vector>
#include <android-base/file.h>
#include <android-base/test_utils.h>
#include <applypatch/imgdiff.h>
#include <applypatch/imgpatch.h>
#include <gtest/gtest.h>
#include <ziparchive/zip_writer.h>
#include "applypatch/utils.h"
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) {
const size_t size = patch.size();
const char* data = patch.data();
ASSERT_GE(size, 12U);
ASSERT_EQ("IMGDIFF2", std::string(data, 8));
const int num_chunks = Read4(data + 8);
ASSERT_GE(num_chunks, 0);
size_t normal = 0;
size_t raw = 0;
size_t deflate = 0;
size_t pos = 12;
for (int i = 0; i < num_chunks; ++i) {
ASSERT_LE(pos + 4, size);
int type = Read4(data + pos);
pos += 4;
if (type == CHUNK_NORMAL) {
pos += 24;
ASSERT_LE(pos, size);
normal++;
} else if (type == CHUNK_RAW) {
ASSERT_LE(pos + 4, size);
ssize_t data_len = Read4(data + pos);
ASSERT_GT(data_len, 0);
pos += 4 + data_len;
ASSERT_LE(pos, size);
raw++;
} else if (type == CHUNK_DEFLATE) {
pos += 60;
ASSERT_LE(pos, size);
deflate++;
} else {
FAIL() << "Invalid patch type: " << type;
}
}
if (num_normal != nullptr) *num_normal = normal;
if (num_raw != nullptr) *num_raw = raw;
if (num_deflate != nullptr) *num_deflate = deflate;
}
TEST(ImgdiffTest, invalid_args) {
// Insufficient inputs.
ASSERT_EQ(2, imgdiff(1, (const char* []){ "imgdiff" }));
ASSERT_EQ(2, imgdiff(2, (const char* []){ "imgdiff", "-z" }));
ASSERT_EQ(2, imgdiff(2, (const char* []){ "imgdiff", "-b" }));
ASSERT_EQ(2, imgdiff(3, (const char* []){ "imgdiff", "-z", "-b" }));
// Failed to read bonus file.
ASSERT_EQ(1, imgdiff(3, (const char* []){ "imgdiff", "-b", "doesntexist" }));
// Failed to read input files.
ASSERT_EQ(1, imgdiff(4, (const char* []){ "imgdiff", "doesntexist", "doesntexist", "output" }));
ASSERT_EQ(
1, imgdiff(5, (const char* []){ "imgdiff", "-z", "doesntexist", "doesntexist", "output" }));
}
TEST(ImgdiffTest, image_mode_smoke) {
// Random bytes.
const std::string src("abcdefg");
TemporaryFile src_file;
ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
const std::string tgt("abcdefgxyz");
TemporaryFile tgt_file;
ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
TemporaryFile patch_file;
std::vector<const char*> args = {
"imgdiff", src_file.path, tgt_file.path, patch_file.path,
};
ASSERT_EQ(0, imgdiff(args.size(), args.data()));
// Verify.
std::string patch;
ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
// Expect one CHUNK_RAW entry.
size_t num_normal;
size_t num_raw;
size_t num_deflate;
verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
ASSERT_EQ(0U, num_normal);
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);
}
TEST(ImgdiffTest, zip_mode_smoke_store) {
// Construct src and tgt zip files.
TemporaryFile src_file;
FILE* src_file_ptr = fdopen(src_file.fd, "wb");
ZipWriter src_writer(src_file_ptr);
ASSERT_EQ(0, src_writer.StartEntry("file1.txt", 0)); // Store mode.
const std::string src_content("abcdefg");
ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
ASSERT_EQ(0, src_writer.FinishEntry());
ASSERT_EQ(0, src_writer.Finish());
ASSERT_EQ(0, fclose(src_file_ptr));
TemporaryFile tgt_file;
FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb");
ZipWriter tgt_writer(tgt_file_ptr);
ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", 0)); // Store mode.
const std::string tgt_content("abcdefgxyz");
ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
ASSERT_EQ(0, tgt_writer.FinishEntry());
ASSERT_EQ(0, tgt_writer.Finish());
ASSERT_EQ(0, fclose(tgt_file_ptr));
// Compute patch.
TemporaryFile patch_file;
std::vector<const char*> args = {
"imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
};
ASSERT_EQ(0, imgdiff(args.size(), args.data()));
// Verify.
std::string tgt;
ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
std::string src;
ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
std::string patch;
ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
// Expect one CHUNK_RAW entry.
size_t num_normal;
size_t num_raw;
size_t num_deflate;
verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
ASSERT_EQ(0U, num_normal);
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);
}
TEST(ImgdiffTest, zip_mode_smoke_compressed) {
// Construct src and tgt zip files.
TemporaryFile src_file;
FILE* src_file_ptr = fdopen(src_file.fd, "wb");
ZipWriter src_writer(src_file_ptr);
ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress));
const std::string src_content("abcdefg");
ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size()));
ASSERT_EQ(0, src_writer.FinishEntry());
ASSERT_EQ(0, src_writer.Finish());
ASSERT_EQ(0, fclose(src_file_ptr));
TemporaryFile tgt_file;
FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb");
ZipWriter tgt_writer(tgt_file_ptr);
ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress));
const std::string tgt_content("abcdefgxyz");
ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size()));
ASSERT_EQ(0, tgt_writer.FinishEntry());
ASSERT_EQ(0, tgt_writer.Finish());
ASSERT_EQ(0, fclose(tgt_file_ptr));
// Compute patch.
TemporaryFile patch_file;
std::vector<const char*> args = {
"imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path,
};
ASSERT_EQ(0, imgdiff(args.size(), args.data()));
// Verify.
std::string tgt;
ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
std::string src;
ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src));
std::string patch;
ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
// Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
size_t num_normal;
size_t num_raw;
size_t num_deflate;
verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
ASSERT_EQ(0U, num_normal);
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);
}
TEST(ImgdiffTest, image_mode_simple) {
// src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd).
const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
'\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac',
'\x02', '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03',
'\x00', '\x00', '\x00' };
const std::string src(src_data.cbegin(), src_data.cend());
TemporaryFile src_file;
ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
// tgt: "abcdefgxyz" + gzipped "xxyyzz".
const std::vector<char> tgt_data = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b',
'\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac',
'\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\x00', '\x00', '\x00'
};
const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
TemporaryFile tgt_file;
ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
TemporaryFile patch_file;
std::vector<const char*> args = {
"imgdiff", src_file.path, tgt_file.path, patch_file.path,
};
ASSERT_EQ(0, imgdiff(args.size(), args.data()));
// Verify.
std::string patch;
ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
// Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
size_t num_normal;
size_t num_raw;
size_t num_deflate;
verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
ASSERT_EQ(0U, num_normal);
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);
}
TEST(ImgdiffTest, image_mode_different_num_chunks) {
// src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd) + gzipped "test".
const std::vector<char> src_data = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\x1f', '\x8b', '\x08',
'\x00', '\xc4', '\x1e', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac', '\x02',
'\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03', '\x00', '\x00', '\x00', '\x1f', '\x8b',
'\x08', '\x00', '\xb2', '\x3a', '\x53', '\x58', '\x00', '\x03', '\x2b', '\x49', '\x2d',
'\x2e', '\x01', '\x00', '\x0c', '\x7e', '\x7f', '\xd8', '\x04', '\x00', '\x00', '\x00'
};
const std::string src(src_data.cbegin(), src_data.cend());
TemporaryFile src_file;
ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
// tgt: "abcdefgxyz" + gzipped "xxyyzz".
const std::vector<char> tgt_data = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b',
'\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac',
'\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\x00', '\x00', '\x00'
};
const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
TemporaryFile tgt_file;
ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
TemporaryFile patch_file;
std::vector<const char*> args = {
"imgdiff", src_file.path, tgt_file.path, patch_file.path,
};
ASSERT_EQ(1, imgdiff(args.size(), args.data()));
}
TEST(ImgdiffTest, image_mode_merge_chunks) {
// src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd).
const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
'\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac',
'\x02', '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03',
'\x00', '\x00', '\x00' };
const std::string src(src_data.cbegin(), src_data.cend());
TemporaryFile src_file;
ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
// tgt: gzipped "xyz" + "abcdefgh".
const std::vector<char> tgt_data = {
'\x1f', '\x8b', '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8',
'\xa8', '\xac', '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\x00',
'\x00', '\x00', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z'
};
const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
TemporaryFile tgt_file;
ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
// Since a gzipped entry will become CHUNK_RAW (header) + CHUNK_DEFLATE (data) +
// CHUNK_RAW (footer), they both should contain the same chunk types after merging.
TemporaryFile patch_file;
std::vector<const char*> args = {
"imgdiff", src_file.path, tgt_file.path, patch_file.path,
};
ASSERT_EQ(0, imgdiff(args.size(), args.data()));
// Verify.
std::string patch;
ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
// Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer).
size_t num_normal;
size_t num_raw;
size_t num_deflate;
verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
ASSERT_EQ(0U, num_normal);
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);
}
TEST(ImgdiffTest, image_mode_spurious_magic) {
// src: "abcdefgh" + '0x1f8b0b00' + some bytes.
const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
'\x53', '\x58', 't', 'e', 's', 't' };
const std::string src(src_data.cbegin(), src_data.cend());
TemporaryFile src_file;
ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
// tgt: "abcdefgxyz".
const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
TemporaryFile tgt_file;
ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
TemporaryFile patch_file;
std::vector<const char*> args = {
"imgdiff", src_file.path, tgt_file.path, patch_file.path,
};
ASSERT_EQ(0, imgdiff(args.size(), args.data()));
// Verify.
std::string patch;
ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
// Expect one CHUNK_RAW (header) entry.
size_t num_normal;
size_t num_raw;
size_t num_deflate;
verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
ASSERT_EQ(0U, num_normal);
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);
}
TEST(ImgdiffTest, image_mode_single_entry_long) {
// src: "abcdefgh" + '0x1f8b0b00' + some bytes.
const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e',
'\x53', '\x58', 't', 'e', 's', 't' };
const std::string src(src_data.cbegin(), src_data.cend());
TemporaryFile src_file;
ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path));
// tgt: "abcdefgxyz" + 200 bytes.
std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' };
tgt_data.resize(tgt_data.size() + 200);
const std::string tgt(tgt_data.cbegin(), tgt_data.cend());
TemporaryFile tgt_file;
ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path));
TemporaryFile patch_file;
std::vector<const char*> args = {
"imgdiff", src_file.path, tgt_file.path, patch_file.path,
};
ASSERT_EQ(0, imgdiff(args.size(), args.data()));
// Verify.
std::string patch;
ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch));
// Expect one CHUNK_NORMAL entry, since it's exceeding the 160-byte limit for RAW.
size_t num_normal;
size_t num_raw;
size_t num_deflate;
verify_patch_header(patch, &num_normal, &num_raw, &num_deflate);
ASSERT_EQ(1U, num_normal);
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);
}