diff options
Diffstat (limited to 'applypatch')
-rw-r--r-- | applypatch/Android.mk | 71 | ||||
-rw-r--r-- | applypatch/Makefile | 33 | ||||
-rw-r--r-- | applypatch/applypatch.cpp | 316 | ||||
-rw-r--r-- | applypatch/bsdiff.cpp | 410 | ||||
-rw-r--r-- | applypatch/bspatch.cpp | 3 | ||||
-rw-r--r-- | applypatch/freecache.cpp | 2 | ||||
-rw-r--r-- | applypatch/imgdiff.cpp | 41 | ||||
-rw-r--r-- | applypatch/imgpatch.cpp | 12 | ||||
-rw-r--r-- | applypatch/include/applypatch/applypatch.h (renamed from applypatch/applypatch.h) | 1 | ||||
-rw-r--r-- | applypatch/libimgpatch.pc | 6 | ||||
-rw-r--r-- | applypatch/main.cpp | 10 | ||||
-rw-r--r-- | applypatch/utils.cpp | 20 | ||||
-rw-r--r-- | applypatch/utils.h | 5 |
13 files changed, 235 insertions, 695 deletions
diff --git a/applypatch/Android.mk b/applypatch/Android.mk index 887a570db..48efe340e 100644 --- a/applypatch/Android.mk +++ b/applypatch/Android.mk @@ -14,61 +14,86 @@ LOCAL_PATH := $(call my-dir) +# libapplypatch (static library) +# =============================== include $(CLEAR_VARS) - LOCAL_CLANG := true -LOCAL_SRC_FILES := applypatch.cpp bspatch.cpp freecache.cpp imgpatch.cpp utils.cpp +LOCAL_SRC_FILES := \ + applypatch.cpp \ + bspatch.cpp \ + freecache.cpp \ + imgpatch.cpp \ + utils.cpp LOCAL_MODULE := libapplypatch LOCAL_MODULE_TAGS := eng -LOCAL_C_INCLUDES += bootable/recovery -LOCAL_STATIC_LIBRARIES += libbase libotafault libmtdutils libcrypto_static libbz libz - +LOCAL_C_INCLUDES += \ + $(LOCAL_PATH)/include \ + bootable/recovery +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_STATIC_LIBRARIES += \ + libotafault \ + libbase \ + libcrypto_static \ + libbz \ + libz include $(BUILD_STATIC_LIBRARY) +# libimgpatch (static library) +# =============================== include $(CLEAR_VARS) - LOCAL_CLANG := true LOCAL_SRC_FILES := bspatch.cpp imgpatch.cpp utils.cpp LOCAL_MODULE := libimgpatch -LOCAL_C_INCLUDES += bootable/recovery +LOCAL_C_INCLUDES += \ + $(LOCAL_PATH)/include \ + bootable/recovery LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES += libcrypto_static libbz libz - include $(BUILD_STATIC_LIBRARY) -ifeq ($(HOST_OS),linux) +# libimgpatch (host static library) +# =============================== include $(CLEAR_VARS) - LOCAL_CLANG := true LOCAL_SRC_FILES := bspatch.cpp imgpatch.cpp utils.cpp LOCAL_MODULE := libimgpatch -LOCAL_C_INCLUDES += bootable/recovery +LOCAL_MODULE_HOST_OS := linux +LOCAL_C_INCLUDES += \ + $(LOCAL_PATH)/include \ + bootable/recovery LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES += libcrypto_static libbz libz - include $(BUILD_HOST_STATIC_LIBRARY) -endif # HOST_OS == linux +# applypatch (executable) +# =============================== include $(CLEAR_VARS) - LOCAL_CLANG := true LOCAL_SRC_FILES := main.cpp LOCAL_MODULE := applypatch LOCAL_C_INCLUDES += bootable/recovery -LOCAL_STATIC_LIBRARIES += libapplypatch libbase libotafault libmtdutils libcrypto_static libbz \ - libedify \ - +LOCAL_STATIC_LIBRARIES += \ + libapplypatch \ + libbase \ + libedify \ + libotafault \ + libminzip \ + libcrypto_static \ + libbz LOCAL_SHARED_LIBRARIES += libz libcutils libc - include $(BUILD_EXECUTABLE) +# imgdiff (host static executable) +# =============================== include $(CLEAR_VARS) - LOCAL_CLANG := true -LOCAL_SRC_FILES := imgdiff.cpp utils.cpp bsdiff.cpp +LOCAL_SRC_FILES := imgdiff.cpp utils.cpp LOCAL_MODULE := imgdiff +LOCAL_STATIC_LIBRARIES += \ + libbsdiff \ + libbz \ + libdivsufsort64 \ + libdivsufsort \ + libz LOCAL_FORCE_STATIC_EXECUTABLE := true -LOCAL_C_INCLUDES += external/zlib external/bzip2 -LOCAL_STATIC_LIBRARIES += libz libbz - include $(BUILD_HOST_EXECUTABLE) diff --git a/applypatch/Makefile b/applypatch/Makefile new file mode 100644 index 000000000..fb4984303 --- /dev/null +++ b/applypatch/Makefile @@ -0,0 +1,33 @@ +# 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. + +# This file is for building imgdiff in Chrome OS. + +CPPFLAGS += -iquote.. -Iinclude +CXXFLAGS += -std=c++11 -O3 -Wall -Werror +LDLIBS += -lbz2 -lz + +.PHONY: all clean + +all: imgdiff libimgpatch.a + +clean: + rm -f *.o imgdiff libimgpatch.a + +imgdiff: imgdiff.o bsdiff.o utils.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDLIBS) -o $@ $^ + +libimgpatch.a utils.o: CXXFLAGS += -fPIC +libimgpatch.a: imgpatch.o bspatch.o utils.o + ${AR} rcs $@ $^ diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index 7985fc0c6..02a3c6e41 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -31,8 +31,7 @@ #include <android-base/strings.h> #include "openssl/sha.h" -#include "applypatch.h" -#include "mtdutils/mtdutils.h" +#include "applypatch/applypatch.h" #include "edify/expr.h" #include "ota_io.h" #include "print_sha1.h" @@ -49,17 +48,14 @@ static int GenerateTarget(FileContents* source_file, size_t target_size, const Value* bonus_data); -static bool mtd_partitions_scanned = false; - // Read a file into memory; store the file contents and associated // metadata in *file. // // Return 0 on success. int LoadFileContents(const char* filename, FileContents* file) { - // A special 'filename' beginning with "MTD:" or "EMMC:" means to + // A special 'filename' beginning with "EMMC:" means to // load the contents of a partition. - if (strncmp(filename, "MTD:", 4) == 0 || - strncmp(filename, "EMMC:", 5) == 0) { + if (strncmp(filename, "EMMC:", 5) == 0) { return LoadPartitionContents(filename, file); } @@ -77,7 +73,7 @@ int LoadFileContents(const char* filename, FileContents* file) { size_t bytes_read = ota_fread(data.data(), 1, data.size(), f); if (bytes_read != data.size()) { - printf("short read of \"%s\" (%zu bytes of %zd)\n", filename, bytes_read, data.size()); + printf("short read of \"%s\" (%zu bytes of %zu)\n", filename, bytes_read, data.size()); ota_fclose(f); return -1; } @@ -87,10 +83,9 @@ int LoadFileContents(const char* filename, FileContents* file) { return 0; } -// Load the contents of an MTD or EMMC partition into the provided +// Load the contents of an EMMC partition into the provided // FileContents. filename should be a string of the form -// "MTD:<partition_name>:<size_1>:<sha1_1>:<size_2>:<sha1_2>:..." (or -// "EMMC:<partition_device>:..."). The smallest size_n bytes for +// "EMMC:<partition_device>:...". The smallest size_n bytes for // which that prefix of the partition contents has the corresponding // sha1 hash will be loaded. It is acceptable for a size value to be // repeated with different sha1s. Will return 0 on success. @@ -102,8 +97,6 @@ int LoadFileContents(const char* filename, FileContents* file) { // "end-of-file" marker), so the caller must specify the possible // lengths and the hash of the data, and we'll do the load expecting // to find one of those hashes. -enum PartitionType { MTD, EMMC }; - static int LoadPartitionContents(const char* filename, FileContents* file) { std::string copy(filename); std::vector<std::string> pieces = android::base::Split(copy, ":"); @@ -112,12 +105,7 @@ static int LoadPartitionContents(const char* filename, FileContents* file) { return -1; } - enum PartitionType type; - if (pieces[0] == "MTD") { - type = MTD; - } else if (pieces[0] == "EMMC") { - type = EMMC; - } else { + if (pieces[0] != "EMMC") { printf("LoadPartitionContents called with bad filename (%s)\n", filename); return -1; } @@ -145,36 +133,10 @@ static int LoadPartitionContents(const char* filename, FileContents* file) { } ); - MtdReadContext* ctx = NULL; - FILE* dev = NULL; - - switch (type) { - case MTD: { - if (!mtd_partitions_scanned) { - mtd_scan_partitions(); - mtd_partitions_scanned = true; - } - - const MtdPartition* mtd = mtd_find_partition_by_name(partition); - if (mtd == NULL) { - printf("mtd partition \"%s\" not found (loading %s)\n", partition, filename); - return -1; - } - - ctx = mtd_read_partition(mtd); - if (ctx == NULL) { - printf("failed to initialize read of mtd partition \"%s\"\n", partition); - return -1; - } - break; - } - - case EMMC: - dev = ota_fopen(partition, "rb"); - if (dev == NULL) { - printf("failed to open emmc partition \"%s\": %s\n", partition, strerror(errno)); - return -1; - } + FILE* dev = ota_fopen(partition, "rb"); + if (dev == NULL) { + printf("failed to open emmc partition \"%s\": %s\n", partition, strerror(errno)); + return -1; } SHA_CTX sha_ctx; @@ -192,16 +154,7 @@ static int LoadPartitionContents(const char* filename, FileContents* file) { // we're trying the possibilities in order of increasing size). size_t next = size[index[i]] - data_size; if (next > 0) { - size_t read = 0; - switch (type) { - case MTD: - read = mtd_read_data(ctx, p, next); - break; - - case EMMC: - read = ota_fread(p, 1, next, dev); - break; - } + size_t read = ota_fread(p, 1, next, dev); if (next != read) { printf("short read (%zu bytes of %zu) for partition \"%s\"\n", read, next, partition); @@ -234,16 +187,7 @@ static int LoadPartitionContents(const char* filename, FileContents* file) { } } - switch (type) { - case MTD: - mtd_read_close(ctx); - break; - - case EMMC: - ota_fclose(dev); - break; - } - + ota_fclose(dev); if (!found) { // Ran off the end of the list of (size,sha1) pairs without finding a match. @@ -302,7 +246,7 @@ int SaveFileContents(const char* filename, const FileContents* file) { } // Write a memory buffer to 'target' partition, a string of the form -// "MTD:<partition>[:...]" or "EMMC:<partition_device>[:...]". The target name +// "EMMC:<partition_device>[:...]". The target name // might contain multiple colons, but WriteToPartition() only uses the first // two and ignores the rest. Return 0 on success. int WriteToPartition(const unsigned char* data, size_t len, const char* target) { @@ -314,165 +258,117 @@ int WriteToPartition(const unsigned char* data, size_t len, const char* target) return -1; } - enum PartitionType type; - if (pieces[0] == "MTD") { - type = MTD; - } else if (pieces[0] == "EMMC") { - type = EMMC; - } else { + if (pieces[0] != "EMMC") { printf("WriteToPartition called with bad target (%s)\n", target); return -1; } const char* partition = pieces[1].c_str(); - switch (type) { - case MTD: { - if (!mtd_partitions_scanned) { - mtd_scan_partitions(); - mtd_partitions_scanned = true; - } - - const MtdPartition* mtd = mtd_find_partition_by_name(partition); - if (mtd == NULL) { - printf("mtd partition \"%s\" not found for writing\n", partition); - return -1; - } - - MtdWriteContext* ctx = mtd_write_partition(mtd); - if (ctx == NULL) { - printf("failed to init mtd partition \"%s\" for writing\n", partition); - return -1; - } + size_t start = 0; + bool success = false; + int fd = ota_open(partition, O_RDWR | O_SYNC); + if (fd < 0) { + printf("failed to open %s: %s\n", partition, strerror(errno)); + return -1; + } - size_t written = mtd_write_data(ctx, reinterpret_cast<const char*>(data), len); - if (written != len) { - printf("only wrote %zu of %zu bytes to MTD %s\n", written, len, partition); - mtd_write_close(ctx); - return -1; - } + for (size_t attempt = 0; attempt < 2; ++attempt) { + if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1) { + printf("failed seek on %s: %s\n", partition, strerror(errno)); + return -1; + } + while (start < len) { + size_t to_write = len - start; + if (to_write > 1<<20) to_write = 1<<20; - if (mtd_erase_blocks(ctx, -1) < 0) { - printf("error finishing mtd write of %s\n", partition); - mtd_write_close(ctx); + ssize_t written = TEMP_FAILURE_RETRY(ota_write(fd, data+start, to_write)); + if (written == -1) { + printf("failed write writing to %s: %s\n", partition, strerror(errno)); return -1; } + start += written; + } + if (ota_fsync(fd) != 0) { + printf("failed to sync to %s (%s)\n", partition, strerror(errno)); + return -1; + } + if (ota_close(fd) != 0) { + printf("failed to close %s (%s)\n", partition, strerror(errno)); + return -1; + } + fd = ota_open(partition, O_RDONLY); + if (fd < 0) { + printf("failed to reopen %s for verify (%s)\n", partition, strerror(errno)); + return -1; + } - if (mtd_write_close(ctx)) { - printf("error closing mtd write of %s\n", partition); - return -1; - } - break; + // Drop caches so our subsequent verification read + // won't just be reading the cache. + sync(); + int dc = ota_open("/proc/sys/vm/drop_caches", O_WRONLY); + if (TEMP_FAILURE_RETRY(ota_write(dc, "3\n", 2)) == -1) { + printf("write to /proc/sys/vm/drop_caches failed: %s\n", strerror(errno)); + } else { + printf(" caches dropped\n"); } + ota_close(dc); + sleep(1); - case EMMC: { - size_t start = 0; - bool success = false; - int fd = ota_open(partition, O_RDWR | O_SYNC); - if (fd < 0) { - printf("failed to open %s: %s\n", partition, strerror(errno)); - return -1; + // verify + if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) { + printf("failed to seek back to beginning of %s: %s\n", + partition, strerror(errno)); + return -1; + } + unsigned char buffer[4096]; + start = len; + for (size_t p = 0; p < len; p += sizeof(buffer)) { + size_t to_read = len - p; + if (to_read > sizeof(buffer)) { + to_read = sizeof(buffer); } - for (size_t attempt = 0; attempt < 2; ++attempt) { - if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1) { - printf("failed seek on %s: %s\n", partition, strerror(errno)); + size_t so_far = 0; + while (so_far < to_read) { + ssize_t read_count = + TEMP_FAILURE_RETRY(ota_read(fd, buffer+so_far, to_read-so_far)); + if (read_count == -1) { + printf("verify read error %s at %zu: %s\n", + partition, p, strerror(errno)); return -1; } - while (start < len) { - size_t to_write = len - start; - if (to_write > 1<<20) to_write = 1<<20; - - ssize_t written = TEMP_FAILURE_RETRY(ota_write(fd, data+start, to_write)); - if (written == -1) { - printf("failed write writing to %s: %s\n", partition, strerror(errno)); - return -1; - } - start += written; - } - if (ota_fsync(fd) != 0) { - printf("failed to sync to %s (%s)\n", partition, strerror(errno)); - return -1; - } - if (ota_close(fd) != 0) { - printf("failed to close %s (%s)\n", partition, strerror(errno)); - return -1; - } - fd = ota_open(partition, O_RDONLY); - if (fd < 0) { - printf("failed to reopen %s for verify (%s)\n", partition, strerror(errno)); - return -1; - } - - // Drop caches so our subsequent verification read - // won't just be reading the cache. - sync(); - int dc = ota_open("/proc/sys/vm/drop_caches", O_WRONLY); - if (TEMP_FAILURE_RETRY(ota_write(dc, "3\n", 2)) == -1) { - printf("write to /proc/sys/vm/drop_caches failed: %s\n", strerror(errno)); - } else { - printf(" caches dropped\n"); - } - ota_close(dc); - sleep(1); - - // verify - if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) { - printf("failed to seek back to beginning of %s: %s\n", - partition, strerror(errno)); - return -1; - } - unsigned char buffer[4096]; - start = len; - for (size_t p = 0; p < len; p += sizeof(buffer)) { - size_t to_read = len - p; - if (to_read > sizeof(buffer)) { - to_read = sizeof(buffer); - } - - size_t so_far = 0; - while (so_far < to_read) { - ssize_t read_count = - TEMP_FAILURE_RETRY(ota_read(fd, buffer+so_far, to_read-so_far)); - if (read_count == -1) { - printf("verify read error %s at %zu: %s\n", - partition, p, strerror(errno)); - return -1; - } - if (static_cast<size_t>(read_count) < to_read) { - printf("short verify read %s at %zu: %zd %zu %s\n", - partition, p, read_count, to_read, strerror(errno)); - } - so_far += read_count; - } - - if (memcmp(buffer, data+p, to_read) != 0) { - printf("verification failed starting at %zu\n", p); - start = p; - break; - } - } - - if (start == len) { - printf("verification read succeeded (attempt %zu)\n", attempt+1); - success = true; - break; + if (static_cast<size_t>(read_count) < to_read) { + printf("short verify read %s at %zu: %zd %zu %s\n", + partition, p, read_count, to_read, strerror(errno)); } + so_far += read_count; } - if (!success) { - printf("failed to verify after all attempts\n"); - return -1; + if (memcmp(buffer, data+p, to_read) != 0) { + printf("verification failed starting at %zu\n", p); + start = p; + break; } + } - if (ota_close(fd) != 0) { - printf("error closing %s (%s)\n", partition, strerror(errno)); - return -1; - } - sync(); + if (start == len) { + printf("verification read succeeded (attempt %zu)\n", attempt+1); + success = true; break; } } + if (!success) { + printf("failed to verify after all attempts\n"); + return -1; + } + + if (ota_close(fd) != 0) { + printf("error closing %s (%s)\n", partition, strerror(errno)); + return -1; + } + sync(); + return 0; } @@ -596,7 +492,7 @@ size_t FreeSpaceForFile(const char* filename) { int CacheSizeCheck(size_t bytes) { if (MakeFreeSpaceOnCache(bytes) < 0) { - printf("unable to make %ld bytes available on /cache\n", (long)bytes); + printf("unable to make %zu bytes available on /cache\n", bytes); return 1; } else { return 0; @@ -729,7 +625,7 @@ int applypatch_flash(const char* source_filename, const char* target_filename, std::string target_str(target_filename); std::vector<std::string> pieces = android::base::Split(target_str, ":"); - if (pieces.size() != 2 || (pieces[0] != "MTD" && pieces[0] != "EMMC")) { + if (pieces.size() != 2 || pieces[0] != "EMMC") { printf("invalid target name \"%s\"", target_filename); return 1; } @@ -778,8 +674,7 @@ static int GenerateTarget(FileContents* source_file, FileContents* source_to_use; int made_copy = 0; - bool target_is_partition = (strncmp(target_filename, "MTD:", 4) == 0 || - strncmp(target_filename, "EMMC:", 5) == 0); + bool target_is_partition = (strncmp(target_filename, "EMMC:", 5) == 0); const std::string tmp_target_filename = std::string(target_filename) + ".patch"; // assume that target_filename (eg "/system/app/Foo.apk") is located @@ -860,8 +755,7 @@ static int GenerateTarget(FileContents* source_file, // copy the source file to cache, then delete it from the original // location. - if (strncmp(source_filename, "MTD:", 4) == 0 || - strncmp(source_filename, "EMMC:", 5) == 0) { + if (strncmp(source_filename, "EMMC:", 5) == 0) { // It's impossible to free space on the target filesystem by // deleting the source if the source is a partition. If // we're ever in a state where we need to do this, fail. @@ -897,7 +791,7 @@ static int GenerateTarget(FileContents* source_file, } else { // We write the decoded output to "<tgt-file>.patch". output_fd = ota_open(tmp_target_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, - S_IRUSR | S_IWUSR); + S_IRUSR | S_IWUSR); if (output_fd < 0) { printf("failed to open output file %s: %s\n", tmp_target_filename.c_str(), strerror(errno)); diff --git a/applypatch/bsdiff.cpp b/applypatch/bsdiff.cpp deleted file mode 100644 index 55dbe5cf1..000000000 --- a/applypatch/bsdiff.cpp +++ /dev/null @@ -1,410 +0,0 @@ -/* - * Copyright (C) 2009 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. - */ - -/* - * Most of this code comes from bsdiff.c from the bsdiff-4.3 - * distribution, which is: - */ - -/*- - * Copyright 2003-2005 Colin Percival - * All rights reserved - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted providing that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include <sys/types.h> - -#include <bzlib.h> -#include <err.h> -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#define MIN(x,y) (((x)<(y)) ? (x) : (y)) - -static void split(off_t *I,off_t *V,off_t start,off_t len,off_t h) -{ - off_t i,j,k,x,tmp,jj,kk; - - if(len<16) { - for(k=start;k<start+len;k+=j) { - j=1;x=V[I[k]+h]; - for(i=1;k+i<start+len;i++) { - if(V[I[k+i]+h]<x) { - x=V[I[k+i]+h]; - j=0; - }; - if(V[I[k+i]+h]==x) { - tmp=I[k+j];I[k+j]=I[k+i];I[k+i]=tmp; - j++; - }; - }; - for(i=0;i<j;i++) V[I[k+i]]=k+j-1; - if(j==1) I[k]=-1; - }; - return; - }; - - x=V[I[start+len/2]+h]; - jj=0;kk=0; - for(i=start;i<start+len;i++) { - if(V[I[i]+h]<x) jj++; - if(V[I[i]+h]==x) kk++; - }; - jj+=start;kk+=jj; - - i=start;j=0;k=0; - while(i<jj) { - if(V[I[i]+h]<x) { - i++; - } else if(V[I[i]+h]==x) { - tmp=I[i];I[i]=I[jj+j];I[jj+j]=tmp; - j++; - } else { - tmp=I[i];I[i]=I[kk+k];I[kk+k]=tmp; - k++; - }; - }; - - while(jj+j<kk) { - if(V[I[jj+j]+h]==x) { - j++; - } else { - tmp=I[jj+j];I[jj+j]=I[kk+k];I[kk+k]=tmp; - k++; - }; - }; - - if(jj>start) split(I,V,start,jj-start,h); - - for(i=0;i<kk-jj;i++) V[I[jj+i]]=kk-1; - if(jj==kk-1) I[jj]=-1; - - if(start+len>kk) split(I,V,kk,start+len-kk,h); -} - -static void qsufsort(off_t *I,off_t *V,u_char *old,off_t oldsize) -{ - off_t buckets[256]; - off_t i,h,len; - - for(i=0;i<256;i++) buckets[i]=0; - for(i=0;i<oldsize;i++) buckets[old[i]]++; - for(i=1;i<256;i++) buckets[i]+=buckets[i-1]; - for(i=255;i>0;i--) buckets[i]=buckets[i-1]; - buckets[0]=0; - - for(i=0;i<oldsize;i++) I[++buckets[old[i]]]=i; - I[0]=oldsize; - for(i=0;i<oldsize;i++) V[i]=buckets[old[i]]; - V[oldsize]=0; - for(i=1;i<256;i++) if(buckets[i]==buckets[i-1]+1) I[buckets[i]]=-1; - I[0]=-1; - - for(h=1;I[0]!=-(oldsize+1);h+=h) { - len=0; - for(i=0;i<oldsize+1;) { - if(I[i]<0) { - len-=I[i]; - i-=I[i]; - } else { - if(len) I[i-len]=-len; - len=V[I[i]]+1-i; - split(I,V,i,len,h); - i+=len; - len=0; - }; - }; - if(len) I[i-len]=-len; - }; - - for(i=0;i<oldsize+1;i++) I[V[i]]=i; -} - -static off_t matchlen(u_char *olddata,off_t oldsize,u_char *newdata,off_t newsize) -{ - off_t i; - - for(i=0;(i<oldsize)&&(i<newsize);i++) - if(olddata[i]!=newdata[i]) break; - - return i; -} - -static off_t search(off_t *I,u_char *old,off_t oldsize, - u_char *newdata,off_t newsize,off_t st,off_t en,off_t *pos) -{ - off_t x,y; - - if(en-st<2) { - x=matchlen(old+I[st],oldsize-I[st],newdata,newsize); - y=matchlen(old+I[en],oldsize-I[en],newdata,newsize); - - if(x>y) { - *pos=I[st]; - return x; - } else { - *pos=I[en]; - return y; - } - }; - - x=st+(en-st)/2; - if(memcmp(old+I[x],newdata,MIN(oldsize-I[x],newsize))<0) { - return search(I,old,oldsize,newdata,newsize,x,en,pos); - } else { - return search(I,old,oldsize,newdata,newsize,st,x,pos); - }; -} - -static void offtout(off_t x,u_char *buf) -{ - off_t y; - - if(x<0) y=-x; else y=x; - - buf[0]=y%256;y-=buf[0]; - y=y/256;buf[1]=y%256;y-=buf[1]; - y=y/256;buf[2]=y%256;y-=buf[2]; - y=y/256;buf[3]=y%256;y-=buf[3]; - y=y/256;buf[4]=y%256;y-=buf[4]; - y=y/256;buf[5]=y%256;y-=buf[5]; - y=y/256;buf[6]=y%256;y-=buf[6]; - y=y/256;buf[7]=y%256; - - if(x<0) buf[7]|=0x80; -} - -// This is main() from bsdiff.c, with the following changes: -// -// - old, oldsize, newdata, newsize are arguments; we don't load this -// data from files. old and newdata are owned by the caller; we -// don't free them at the end. -// -// - the "I" block of memory is owned by the caller, who passes a -// pointer to *I, which can be NULL. This way if we call -// bsdiff() multiple times with the same 'old' data, we only do -// the qsufsort() step the first time. -// -int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* newdata, off_t newsize, - const char* patch_filename) -{ - int fd; - off_t *I; - off_t scan,pos,len; - off_t lastscan,lastpos,lastoffset; - off_t oldscore,scsc; - off_t s,Sf,lenf,Sb,lenb; - off_t overlap,Ss,lens; - off_t i; - off_t dblen,eblen; - u_char *db,*eb; - u_char buf[8]; - u_char header[32]; - FILE * pf; - BZFILE * pfbz2; - int bz2err; - - if (*IP == NULL) { - off_t* V; - *IP = reinterpret_cast<off_t*>(malloc((oldsize+1) * sizeof(off_t))); - V = reinterpret_cast<off_t*>(malloc((oldsize+1) * sizeof(off_t))); - qsufsort(*IP, V, old, oldsize); - free(V); - } - I = *IP; - - if(((db=reinterpret_cast<u_char*>(malloc(newsize+1)))==NULL) || - ((eb=reinterpret_cast<u_char*>(malloc(newsize+1)))==NULL)) err(1,NULL); - dblen=0; - eblen=0; - - /* Create the patch file */ - if ((pf = fopen(patch_filename, "w")) == NULL) - err(1, "%s", patch_filename); - - /* Header is - 0 8 "BSDIFF40" - 8 8 length of bzip2ed ctrl block - 16 8 length of bzip2ed diff block - 24 8 length of new file */ - /* File is - 0 32 Header - 32 ?? Bzip2ed ctrl block - ?? ?? Bzip2ed diff block - ?? ?? Bzip2ed extra block */ - memcpy(header,"BSDIFF40",8); - offtout(0, header + 8); - offtout(0, header + 16); - offtout(newsize, header + 24); - if (fwrite(header, 32, 1, pf) != 1) - err(1, "fwrite(%s)", patch_filename); - - /* Compute the differences, writing ctrl as we go */ - if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL) - errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err); - scan=0;len=0; - lastscan=0;lastpos=0;lastoffset=0; - while(scan<newsize) { - oldscore=0; - - for(scsc=scan+=len;scan<newsize;scan++) { - len=search(I,old,oldsize,newdata+scan,newsize-scan, - 0,oldsize,&pos); - - for(;scsc<scan+len;scsc++) - if((scsc+lastoffset<oldsize) && - (old[scsc+lastoffset] == newdata[scsc])) - oldscore++; - - if(((len==oldscore) && (len!=0)) || - (len>oldscore+8)) break; - - if((scan+lastoffset<oldsize) && - (old[scan+lastoffset] == newdata[scan])) - oldscore--; - }; - - if((len!=oldscore) || (scan==newsize)) { - s=0;Sf=0;lenf=0; - for(i=0;(lastscan+i<scan)&&(lastpos+i<oldsize);) { - if(old[lastpos+i]==newdata[lastscan+i]) s++; - i++; - if(s*2-i>Sf*2-lenf) { Sf=s; lenf=i; }; - }; - - lenb=0; - if(scan<newsize) { - s=0;Sb=0; - for(i=1;(scan>=lastscan+i)&&(pos>=i);i++) { - if(old[pos-i]==newdata[scan-i]) s++; - if(s*2-i>Sb*2-lenb) { Sb=s; lenb=i; }; - }; - }; - - if(lastscan+lenf>scan-lenb) { - overlap=(lastscan+lenf)-(scan-lenb); - s=0;Ss=0;lens=0; - for(i=0;i<overlap;i++) { - if(newdata[lastscan+lenf-overlap+i]== - old[lastpos+lenf-overlap+i]) s++; - if(newdata[scan-lenb+i]== - old[pos-lenb+i]) s--; - if(s>Ss) { Ss=s; lens=i+1; }; - }; - - lenf+=lens-overlap; - lenb-=lens; - }; - - for(i=0;i<lenf;i++) - db[dblen+i]=newdata[lastscan+i]-old[lastpos+i]; - for(i=0;i<(scan-lenb)-(lastscan+lenf);i++) - eb[eblen+i]=newdata[lastscan+lenf+i]; - - dblen+=lenf; - eblen+=(scan-lenb)-(lastscan+lenf); - - offtout(lenf,buf); - BZ2_bzWrite(&bz2err, pfbz2, buf, 8); - if (bz2err != BZ_OK) - errx(1, "BZ2_bzWrite, bz2err = %d", bz2err); - - offtout((scan-lenb)-(lastscan+lenf),buf); - BZ2_bzWrite(&bz2err, pfbz2, buf, 8); - if (bz2err != BZ_OK) - errx(1, "BZ2_bzWrite, bz2err = %d", bz2err); - - offtout((pos-lenb)-(lastpos+lenf),buf); - BZ2_bzWrite(&bz2err, pfbz2, buf, 8); - if (bz2err != BZ_OK) - errx(1, "BZ2_bzWrite, bz2err = %d", bz2err); - - lastscan=scan-lenb; - lastpos=pos-lenb; - lastoffset=pos-scan; - }; - }; - BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL); - if (bz2err != BZ_OK) - errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err); - - /* Compute size of compressed ctrl data */ - if ((len = ftello(pf)) == -1) - err(1, "ftello"); - offtout(len-32, header + 8); - - /* Write compressed diff data */ - if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL) - errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err); - BZ2_bzWrite(&bz2err, pfbz2, db, dblen); - if (bz2err != BZ_OK) - errx(1, "BZ2_bzWrite, bz2err = %d", bz2err); - BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL); - if (bz2err != BZ_OK) - errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err); - - /* Compute size of compressed diff data */ - if ((newsize = ftello(pf)) == -1) - err(1, "ftello"); - offtout(newsize - len, header + 16); - - /* Write compressed extra data */ - if ((pfbz2 = BZ2_bzWriteOpen(&bz2err, pf, 9, 0, 0)) == NULL) - errx(1, "BZ2_bzWriteOpen, bz2err = %d", bz2err); - BZ2_bzWrite(&bz2err, pfbz2, eb, eblen); - if (bz2err != BZ_OK) - errx(1, "BZ2_bzWrite, bz2err = %d", bz2err); - BZ2_bzWriteClose(&bz2err, pfbz2, 0, NULL, NULL); - if (bz2err != BZ_OK) - errx(1, "BZ2_bzWriteClose, bz2err = %d", bz2err); - - /* Seek to the beginning, write the header, and close the file */ - if (fseeko(pf, 0, SEEK_SET)) - err(1, "fseeko"); - if (fwrite(header, 32, 1, pf) != 1) - err(1, "fwrite(%s)", patch_filename); - if (fclose(pf)) - err(1, "fclose"); - - /* Free the memory we used */ - free(db); - free(eb); - - return 0; -} diff --git a/applypatch/bspatch.cpp b/applypatch/bspatch.cpp index ebb55f1d1..a4945da28 100644 --- a/applypatch/bspatch.cpp +++ b/applypatch/bspatch.cpp @@ -30,7 +30,7 @@ #include <bzlib.h> #include "openssl/sha.h" -#include "applypatch.h" +#include "applypatch/applypatch.h" void ShowBSDiffLicense() { puts("The bsdiff library used herein is:\n" @@ -182,7 +182,6 @@ int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, off_t oldpos = 0, newpos = 0; off_t ctrl[3]; - off_t len_read; int i; unsigned char buf[24]; while (newpos < new_size) { diff --git a/applypatch/freecache.cpp b/applypatch/freecache.cpp index c84f42797..331cae265 100644 --- a/applypatch/freecache.cpp +++ b/applypatch/freecache.cpp @@ -32,7 +32,7 @@ #include <android-base/parseint.h> #include <android-base/stringprintf.h> -#include "applypatch.h" +#include "applypatch/applypatch.h" static int EliminateOpenFiles(std::set<std::string>* files) { std::unique_ptr<DIR, decltype(&closedir)> d(opendir("/proc"), closedir); diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index f22502e38..7c5bb866d 100644 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -130,6 +130,8 @@ #include <unistd.h> #include <sys/types.h> +#include <bsdiff.h> + #include "zlib.h" #include "imgdiff.h" #include "utils.h" @@ -144,8 +146,6 @@ typedef struct { size_t source_start; size_t source_len; - off_t* I; // used by bsdiff - // --- for CHUNK_DEFLATE chunks only: --- // original (compressed) deflate data @@ -179,10 +179,6 @@ static int fileentry_compare(const void* a, const void* b) { } } -// from bsdiff.c -int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* newdata, off_t newsize, - const char* patch_filename); - unsigned char* ReadZip(const char* filename, int* num_chunks, ImageChunk** chunks, int include_pseudo_chunk) { @@ -296,7 +292,6 @@ unsigned char* ReadZip(const char* filename, curr->len = st.st_size; curr->data = img; curr->filename = NULL; - curr->I = NULL; ++curr; ++*num_chunks; } @@ -311,7 +306,6 @@ unsigned char* ReadZip(const char* filename, curr->deflate_len = temp_entries[nextentry].deflate_len; curr->deflate_data = img + pos; curr->filename = temp_entries[nextentry].filename; - curr->I = NULL; curr->len = temp_entries[nextentry].uncomp_len; curr->data = reinterpret_cast<unsigned char*>(malloc(curr->len)); @@ -356,7 +350,6 @@ unsigned char* ReadZip(const char* filename, } curr->data = img + pos; curr->filename = NULL; - curr->I = NULL; pos += curr->len; ++*num_chunks; @@ -407,7 +400,6 @@ unsigned char* ReadImage(const char* filename, while (pos < sz) { unsigned char* p = img+pos; - bool processed_deflate = false; if (sz - pos >= 4 && p[0] == 0x1f && p[1] == 0x8b && p[2] == 0x08 && // deflate compression @@ -425,7 +417,6 @@ unsigned char* ReadImage(const char* filename, curr->type = CHUNK_NORMAL; curr->len = GZIP_HEADER_LEN; curr->data = p; - curr->I = NULL; pos += curr->len; p += curr->len; @@ -433,7 +424,6 @@ unsigned char* ReadImage(const char* filename, curr->type = CHUNK_DEFLATE; curr->filename = NULL; - curr->I = NULL; // We must decompress this chunk in order to discover where it // ends, and so we can put the uncompressed data and its length @@ -461,28 +451,27 @@ unsigned char* ReadImage(const char* filename, strm.next_out = curr->data + curr->len; ret = inflate(&strm, Z_NO_FLUSH); if (ret < 0) { - if (!processed_deflate) { - // This is the first chunk, assume that it's just a spurious - // gzip header instead of a real one. - break; - } - printf("Error: inflate failed [%s] at file offset [%zu]\n" - "imgdiff only supports gzip kernel compression," - " did you try CONFIG_KERNEL_LZO?\n", + printf("Warning: inflate failed [%s] at offset [%zu]," + " treating as a normal chunk\n", strm.msg, chunk_offset); - free(img); - return NULL; + break; } curr->len = allocated - strm.avail_out; if (strm.avail_out == 0) { allocated *= 2; curr->data = reinterpret_cast<unsigned char*>(realloc(curr->data, allocated)); } - processed_deflate = true; } while (ret != Z_STREAM_END); curr->deflate_len = sz - strm.avail_in - pos; inflateEnd(&strm); + + if (ret < 0) { + free(curr->data); + *num_chunks -= 2; + continue; + } + pos += curr->deflate_len; p += curr->deflate_len; ++curr; @@ -493,7 +482,6 @@ unsigned char* ReadImage(const char* filename, curr->start = pos; curr->len = GZIP_FOOTER_LEN; curr->data = img+pos; - curr->I = NULL; pos += curr->len; p += curr->len; @@ -517,7 +505,6 @@ unsigned char* ReadImage(const char* filename, *chunks = reinterpret_cast<ImageChunk*>(realloc(*chunks, *num_chunks * sizeof(ImageChunk))); ImageChunk* curr = *chunks + (*num_chunks-1); curr->start = pos; - curr->I = NULL; // 'pos' is not the offset of the start of a gzip chunk, so scan // forward until we find a gzip header. @@ -598,7 +585,6 @@ int ReconstructDeflateChunk(ImageChunk* chunk) { return -1; } - size_t p = 0; unsigned char* out = reinterpret_cast<unsigned char*>(malloc(BUFFER_SIZE)); // We only check two combinations of encoder parameters: level 6 @@ -645,7 +631,7 @@ unsigned char* MakePatch(ImageChunk* src, ImageChunk* tgt, size_t* size) { close(fd); // temporary file is created and we don't need its file // descriptor - int r = bsdiff(src->data, src->len, &(src->I), tgt->data, tgt->len, ptemp); + int r = bsdiff::bsdiff(src->data, src->len, tgt->data, tgt->len, ptemp); if (r != 0) { printf("bsdiff() failed: %d\n", r); return NULL; @@ -844,7 +830,6 @@ int main(int argc, char** argv) { } if (argc != 4) { - usage: printf("usage: %s [-z] [-b <bonus-file>] <src-img> <tgt-img> <patch-file>\n", argv[0]); return 2; diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp index d175d6385..0c06d6b1e 100644 --- a/applypatch/imgpatch.cpp +++ b/applypatch/imgpatch.cpp @@ -28,7 +28,7 @@ #include "zlib.h" #include "openssl/sha.h" -#include "applypatch.h" +#include "applypatch/applypatch.h" #include "imgdiff.h" #include "utils.h" @@ -130,6 +130,7 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, 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); @@ -200,6 +201,11 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, &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. @@ -231,8 +237,8 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, ssize_t have = temp_data.size() - strm.avail_out; if (sink(temp_data.data(), have, token) != have) { - printf("failed to write %ld compressed bytes to output\n", - (long)have); + printf("failed to write %zd compressed bytes to output\n", + have); return -1; } if (ctx) SHA1_Update(ctx, temp_data.data(), have); diff --git a/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h index f392c5534..9ee39d293 100644 --- a/applypatch/applypatch.h +++ b/applypatch/include/applypatch/applypatch.h @@ -17,6 +17,7 @@ #ifndef _APPLYPATCH_H #define _APPLYPATCH_H +#include <stdint.h> #include <sys/stat.h> #include <vector> diff --git a/applypatch/libimgpatch.pc b/applypatch/libimgpatch.pc new file mode 100644 index 000000000..e5002934f --- /dev/null +++ b/applypatch/libimgpatch.pc @@ -0,0 +1,6 @@ +# This file is for libimgpatch in Chrome OS. + +Name: libimgpatch +Description: Apply imgdiff patch +Version: 0.0.1 +Libs: -limgpatch -lbz2 -lz diff --git a/applypatch/main.cpp b/applypatch/main.cpp index 9013760c4..1968ae48f 100644 --- a/applypatch/main.cpp +++ b/applypatch/main.cpp @@ -22,7 +22,7 @@ #include <memory> #include <vector> -#include "applypatch.h" +#include "applypatch/applypatch.h" #include "edify/expr.h" #include "openssl/sha.h" @@ -160,9 +160,9 @@ static int PatchMode(int argc, char** argv) { // - otherwise, or if any error is encountered, exits with non-zero // status. // -// <src-file> (or <file> in check mode) may refer to an MTD partition +// <src-file> (or <file> in check mode) may refer to an EMMC partition // to read the source data. See the comments for the -// LoadMTDContents() function above for the format of such a filename. +// LoadPartitionContents() function for the format of such a filename. int main(int argc, char** argv) { if (argc < 2) { @@ -175,8 +175,8 @@ int main(int argc, char** argv) { " or %s -l\n" "\n" "Filenames may be of the form\n" - " MTD:<partition>:<len_1>:<sha1_1>:<len_2>:<sha1_2>:...\n" - "to specify reading from or writing to an MTD partition.\n\n", + " EMMC:<partition>:<len_1>:<sha1_1>:<len_2>:<sha1_2>:...\n" + "to specify reading from or writing to an EMMC partition.\n\n", argv[0], argv[0], argv[0], argv[0]); return 2; } diff --git a/applypatch/utils.cpp b/applypatch/utils.cpp index 4a80be75f..fef250f01 100644 --- a/applypatch/utils.cpp +++ b/applypatch/utils.cpp @@ -27,7 +27,7 @@ void Write4(int value, FILE* f) { } /** Write an 8-byte value to f in little-endian order. */ -void Write8(long long value, FILE* f) { +void Write8(int64_t value, FILE* f) { fputc(value & 0xff, f); fputc((value >> 8) & 0xff, f); fputc((value >> 16) & 0xff, f); @@ -52,14 +52,14 @@ int Read4(void* pv) { (unsigned int)p[0]); } -long long Read8(void* pv) { +int64_t Read8(void* pv) { unsigned char* p = reinterpret_cast<unsigned char*>(pv); - return (long long)(((unsigned long long)p[7] << 56) | - ((unsigned long long)p[6] << 48) | - ((unsigned long long)p[5] << 40) | - ((unsigned long long)p[4] << 32) | - ((unsigned long long)p[3] << 24) | - ((unsigned long long)p[2] << 16) | - ((unsigned long long)p[1] << 8) | - (unsigned long long)p[0]); + return (int64_t)(((uint64_t)p[7] << 56) | + ((uint64_t)p[6] << 48) | + ((uint64_t)p[5] << 40) | + ((uint64_t)p[4] << 32) | + ((uint64_t)p[3] << 24) | + ((uint64_t)p[2] << 16) | + ((uint64_t)p[1] << 8) | + (uint64_t)p[0]); } diff --git a/applypatch/utils.h b/applypatch/utils.h index bc97f1720..1c34edd97 100644 --- a/applypatch/utils.h +++ b/applypatch/utils.h @@ -17,14 +17,15 @@ #ifndef _BUILD_TOOLS_APPLYPATCH_UTILS_H #define _BUILD_TOOLS_APPLYPATCH_UTILS_H +#include <inttypes.h> #include <stdio.h> // Read and write little-endian values of various sizes. void Write4(int value, FILE* f); -void Write8(long long value, FILE* f); +void Write8(int64_t value, FILE* f); int Read2(void* p); int Read4(void* p); -long long Read8(void* p); +int64_t Read8(void* p); #endif // _BUILD_TOOLS_APPLYPATCH_UTILS_H |