Merge "Add tests for imgdiff." am: 344c8eb453
am: 4fe022c4ed
am: 4e2471d6dd
am: e6c1d578bd
Change-Id: If99c2af21db684d3874649144f0d1ff780474616
This commit is contained in:
commit
6d83a14036
8 changed files with 813 additions and 267 deletions
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
21
applypatch/imgdiff_main.cpp
Normal file
21
applypatch/imgdiff_main.cpp
Normal 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));
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
448
tests/component/imgdiff_test.cpp
Normal file
448
tests/component/imgdiff_test.cpp
Normal 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);
|
||||
}
|
Loading…
Reference in a new issue