M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions
+67
View File
@@ -0,0 +1,67 @@
# Copyright (C) 2008 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.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := applypatch.c bspatch.c freecache.c imgpatch.c utils.c
LOCAL_MODULE := libapplypatch
LOCAL_MODULE_TAGS := eng
LOCAL_C_INCLUDES += external/bzip2 external/zlib bootable/recovery
LOCAL_STATIC_LIBRARIES += libmtdutils libmincrypt libbz libz
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := main.c
LOCAL_MODULE := applypatch
LOCAL_C_INCLUDES += bootable/recovery
LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz libminelf
ifeq ($(TARGET_FOTA_UPDATE_LIB),)
LOCAL_STATIC_LIBRARIES += libduadummy
else
LOCAL_STATIC_LIBRARIES += $(TARGET_FOTA_UPDATE_LIB)
endif
LOCAL_SHARED_LIBRARIES += libz libcutils libstdc++ libc
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := main.c
LOCAL_MODULE := applypatch_static
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_TAGS := eng
LOCAL_C_INCLUDES += bootable/recovery
LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz libminelf
ifeq ($(TARGET_FOTA_UPDATE_LIB),)
LOCAL_STATIC_LIBRARIES += libduadummy
else
LOCAL_STATIC_LIBRARIES += $(TARGET_FOTA_UPDATE_LIB)
endif
LOCAL_STATIC_LIBRARIES += libz libcutils libstdc++ libc
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := imgdiff.c utils.c bsdiff.c
LOCAL_MODULE := imgdiff
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_TAGS := eng
LOCAL_C_INCLUDES += external/zlib external/bzip2
LOCAL_STATIC_LIBRARIES += libz libbz
include $(BUILD_HOST_EXECUTABLE)
+78
View File
@@ -0,0 +1,78 @@
AM_CFLAGS := -Wundef \
-Wstrict-prototypes \
-Wno-trigraphs \
-g -O0 \
-fno-inline \
-fno-short-enums \
-fpic
AM_CPPFLAGS := \
-I. \
-I$(WORKSPACE)/system/core/include
AM_LDFLAGS := -shared -version-info 1:0:0
c_sources := applypatch.c \
bspatch.c \
bsdiff.c \
imgdiff.c \
imgpatch.c \
freecache.c \
utils.c
h_sources := applypatch.h
library_includedir := $(pkgincludedir)
library_include_HEADERS := $(h_sources)
requiredlibs := -lm \
-lpthread \
-lz \
-lbz2 \
-lmincrypt \
-lcutils \
../duadummy/libduadummy.la \
../minelf/libminelf.la \
../mtdutils/libmtdutils.la \
../edify/libedify.la \
../minzip/libminzip.la
lib_LTLIBRARIES := libapplypatch.la
if USE_GLIB
libapplypatch_la_SOURCES := $(c_sources) $(h_sources)
libapplypatch_la_CFLAGS := $(AM_CFLAGS) -DUSE_GLIB @GLIB_CFLAGS@
libapplypatch_la_CPPFLAGS := $(AM_CPPFLAGS) -DUSE_GLIB @GLIB_CFLAGS@
libapplypatch_la_LIBADD := $(requiredlibs) @GLIB_LIBS@
libapplypatch_la_LDFLAGS := -all-static
else
libapplypatch_la_SOURCES := $(c_sources) $(h_sources)
libapplypatch_la_CFLAGS := $(AM_CFLAGS)
libapplypatch_la_CPPFLAGS := $(AM_CPPFLAGS)
libapplypatch_la_LIBADD := $(requiredlibs)
libapplypatch_la_LDFLAGS := -all-static
endif
applypatch_c_sources := applypatch.c \
bspatch.c \
bsdiff.c \
imgpatch.c \
freecache.c \
utils.c \
main.c
applypatch_h_sources := applypatch.h utils.h
bin_PROGRAMS := applypatch
applypatch_SOURCES := $(applypatch_c_sources) $(applypatch_h_sources)
if USE_GLIB
applypatch_CFLAGS := $(AM_CFLAGS) -DUSE_GLIB @GLIB_CFLAGS@
applypatch_CPPFLAGS := $(AM_CPPFLAGS) -DUSE_GLIB @GLIB_CFLAGS@
applypatch_LDADD := $(requiredlibs) @GLIB_LIBS@
applypatch_LDFLAGS := -all-static
else
applypatch_CFLAGS := $(AM_CFLAGS)
applypatch_CPPFLAGS := $(AM_CPPFLAGS)
applypatch_LDADD := $(requiredlibs)
applypatch_LDFLAGS := -all-static
endif
+915
View File
@@ -0,0 +1,915 @@
/*
* Copyright (C) 2008 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 <errno.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include "mincrypt/sha.h"
#include "applypatch.h"
#include "mtdutils/mtdutils.h"
#include "edify/expr.h"
int SaveFileContents(const char* filename, FileContents file);
static int LoadPartitionContents(const char* filename, FileContents* file);
int ParseSha1(const char* str, uint8_t* digest);
static ssize_t FileSink(unsigned char* data, ssize_t len, void* token);
static int mtd_partitions_scanned = 0;
// Read a file into memory; optionally (retouch_flag == RETOUCH_DO_MASK) mask
// the retouched entries back to their original value (such that SHA-1 checks
// don't fail due to randomization); store the file contents and associated
// metadata in *file.
//
// Return 0 on success.
int LoadFileContents(const char* filename, FileContents* file,
int retouch_flag) {
file->data = NULL;
// A special 'filename' beginning with "MTD:" or "EMMC:" means to
// load the contents of a partition.
if (strncmp(filename, "MTD:", 4) == 0 ||
strncmp(filename, "EMMC:", 5) == 0) {
return LoadPartitionContents(filename, file);
}
if (stat(filename, &file->st) != 0) {
printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
return -1;
}
file->size = file->st.st_size;
file->data = malloc(file->size);
FILE* f = fopen(filename, "rb");
if (f == NULL) {
printf("failed to open \"%s\": %s\n", filename, strerror(errno));
free(file->data);
file->data = NULL;
return -1;
}
ssize_t bytes_read = fread(file->data, 1, file->size, f);
if (bytes_read != file->size) {
printf("short read of \"%s\" (%ld bytes of %ld)\n",
filename, (long)bytes_read, (long)file->size);
free(file->data);
file->data = NULL;
return -1;
}
fclose(f);
// apply_patch[_check] functions are blind to randomization. Randomization
// is taken care of in [Undo]RetouchBinariesFn. If there is a mismatch
// within a file, this means the file is assumed "corrupt" for simplicity.
if (retouch_flag) {
int32_t desired_offset = 0;
if (retouch_mask_data(file->data, file->size,
&desired_offset, NULL) != RETOUCH_DATA_MATCHED) {
printf("error trying to mask retouch entries\n");
free(file->data);
file->data = NULL;
return -1;
}
}
SHA(file->data, file->size, file->sha1);
return 0;
}
static size_t* size_array;
// comparison function for qsort()ing an int array of indexes into
// size_array[].
static int compare_size_indices(const void* a, const void* b) {
int aa = *(int*)a;
int bb = *(int*)b;
if (size_array[aa] < size_array[bb]) {
return -1;
} else if (size_array[aa] > size_array[bb]) {
return 1;
} else {
return 0;
}
}
void FreeFileContents(FileContents* file) {
if (file) free(file->data);
free(file);
}
// Load the contents of an MTD or 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
// 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.
//
// This complexity is needed because if an OTA installation is
// interrupted, the partition might contain either the source or the
// target data, which might be of different lengths. We need to know
// the length in order to read from a partition (there is no
// "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) {
char* copy = strdup(filename);
const char* magic = strtok(copy, ":");
enum PartitionType type;
if (strcmp(magic, "MTD") == 0) {
type = MTD;
} else if (strcmp(magic, "EMMC") == 0) {
type = EMMC;
} else {
printf("LoadPartitionContents called with bad filename (%s)\n",
filename);
return -1;
}
const char* partition = strtok(NULL, ":");
int i;
int colons = 0;
for (i = 0; filename[i] != '\0'; ++i) {
if (filename[i] == ':') {
++colons;
}
}
if (colons < 3 || colons%2 == 0) {
printf("LoadPartitionContents called with bad filename (%s)\n",
filename);
}
int pairs = (colons-1)/2; // # of (size,sha1) pairs in filename
int* index = malloc(pairs * sizeof(int));
size_t* size = malloc(pairs * sizeof(size_t));
char** sha1sum = malloc(pairs * sizeof(char*));
for (i = 0; i < pairs; ++i) {
const char* size_str = strtok(NULL, ":");
size[i] = strtol(size_str, NULL, 10);
if (size[i] == 0) {
printf("LoadPartitionContents called with bad size (%s)\n", filename);
return -1;
}
sha1sum[i] = strtok(NULL, ":");
index[i] = i;
}
// sort the index[] array so it indexes the pairs in order of
// increasing size.
size_array = size;
qsort(index, pairs, sizeof(int), compare_size_indices);
MtdReadContext* ctx = NULL;
FILE* dev = NULL;
switch (type) {
case MTD:
if (!mtd_partitions_scanned) {
mtd_scan_partitions();
mtd_partitions_scanned = 1;
}
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 = fopen(partition, "rb");
if (dev == NULL) {
printf("failed to open emmc partition \"%s\": %s\n",
partition, strerror(errno));
return -1;
}
}
SHA_CTX sha_ctx;
SHA_init(&sha_ctx);
uint8_t parsed_sha[SHA_DIGEST_SIZE];
// allocate enough memory to hold the largest size.
file->data = malloc(size[index[pairs-1]]);
char* p = (char*)file->data;
file->size = 0; // # bytes read so far
for (i = 0; i < pairs; ++i) {
// Read enough additional bytes to get us up to the next size
// (again, we're trying the possibilities in order of increasing
// size).
size_t next = size[index[i]] - file->size;
size_t read = 0;
if (next > 0) {
switch (type) {
case MTD:
read = mtd_read_data(ctx, p, next);
break;
case EMMC:
read = fread(p, 1, next, dev);
break;
}
if (next != read) {
printf("short read (%d bytes of %d) for partition \"%s\"\n",
read, next, partition);
free(file->data);
file->data = NULL;
return -1;
}
SHA_update(&sha_ctx, p, read);
file->size += read;
}
// Duplicate the SHA context and finalize the duplicate so we can
// check it against this pair's expected hash.
SHA_CTX temp_ctx;
memcpy(&temp_ctx, &sha_ctx, sizeof(SHA_CTX));
const uint8_t* sha_so_far = SHA_final(&temp_ctx);
if (ParseSha1(sha1sum[index[i]], parsed_sha) != 0) {
printf("failed to parse sha1 %s in %s\n",
sha1sum[index[i]], filename);
free(file->data);
file->data = NULL;
return -1;
}
if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_SIZE) == 0) {
// we have a match. stop reading the partition; we'll return
// the data we've read so far.
printf("partition read matched size %d sha %s\n",
size[index[i]], sha1sum[index[i]]);
break;
}
p += read;
}
switch (type) {
case MTD:
mtd_read_close(ctx);
break;
case EMMC:
fclose(dev);
break;
}
if (i == pairs) {
// Ran off the end of the list of (size,sha1) pairs without
// finding a match.
printf("contents of partition \"%s\" didn't match %s\n",
partition, filename);
free(file->data);
file->data = NULL;
return -1;
}
const uint8_t* sha_final = SHA_final(&sha_ctx);
for (i = 0; i < SHA_DIGEST_SIZE; ++i) {
file->sha1[i] = sha_final[i];
}
// Fake some stat() info.
file->st.st_mode = 0644;
file->st.st_uid = 0;
file->st.st_gid = 0;
free(copy);
free(index);
free(size);
free(sha1sum);
return 0;
}
// Save the contents of the given FileContents object under the given
// filename. Return 0 on success.
int SaveFileContents(const char* filename, FileContents file) {
int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC);
if (fd < 0) {
printf("failed to open \"%s\" for write: %s\n",
filename, strerror(errno));
return -1;
}
ssize_t bytes_written = FileSink(file.data, file.size, &fd);
if (bytes_written != file.size) {
printf("short write of \"%s\" (%ld bytes of %ld) (%s)\n",
filename, (long)bytes_written, (long)file.size,
strerror(errno));
close(fd);
return -1;
}
fsync(fd);
close(fd);
if (chmod(filename, file.st.st_mode) != 0) {
printf("chmod of \"%s\" failed: %s\n", filename, strerror(errno));
return -1;
}
if (chown(filename, file.st.st_uid, file.st.st_gid) != 0) {
printf("chown of \"%s\" failed: %s\n", filename, strerror(errno));
return -1;
}
return 0;
}
// Write a memory buffer to 'target' partition, a string of the form
// "MTD:<partition>[:...]" or "EMMC:<partition_device>:". Return 0 on
// success.
int WriteToPartition(unsigned char* data, size_t len,
const char* target) {
char* copy = strdup(target);
const char* magic = strtok(copy, ":");
enum PartitionType type;
if (strcmp(magic, "MTD") == 0) {
type = MTD;
} else if (strcmp(magic, "EMMC") == 0) {
type = EMMC;
} else {
printf("WriteToPartition called with bad target (%s)\n", target);
return -1;
}
const char* partition = strtok(NULL, ":");
if (partition == NULL) {
printf("bad partition target name \"%s\"\n", target);
return -1;
}
switch (type) {
case MTD:
if (!mtd_partitions_scanned) {
mtd_scan_partitions();
mtd_partitions_scanned = 1;
}
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 written = mtd_write_data(ctx, (char*)data, len);
if (written != len) {
printf("only wrote %d of %d bytes to MTD %s\n",
written, len, partition);
mtd_write_close(ctx);
return -1;
}
if (mtd_erase_blocks(ctx, -1) < 0) {
printf("error finishing mtd write of %s\n", partition);
mtd_write_close(ctx);
return -1;
}
if (mtd_write_close(ctx)) {
printf("error closing mtd write of %s\n", partition);
return -1;
}
break;
case EMMC:
;
FILE* f = fopen(partition, "wb");
if (fwrite(data, 1, len, f) != len) {
printf("short write writing to %s (%s)\n",
partition, strerror(errno));
return -1;
}
if (fclose(f) != 0) {
printf("error closing %s (%s)\n", partition, strerror(errno));
return -1;
}
break;
}
free(copy);
return 0;
}
// Take a string 'str' of 40 hex digits and parse it into the 20
// byte array 'digest'. 'str' may contain only the digest or be of
// the form "<digest>:<anything>". Return 0 on success, -1 on any
// error.
int ParseSha1(const char* str, uint8_t* digest) {
int i;
const char* ps = str;
uint8_t* pd = digest;
for (i = 0; i < SHA_DIGEST_SIZE * 2; ++i, ++ps) {
int digit;
if (*ps >= '0' && *ps <= '9') {
digit = *ps - '0';
} else if (*ps >= 'a' && *ps <= 'f') {
digit = *ps - 'a' + 10;
} else if (*ps >= 'A' && *ps <= 'F') {
digit = *ps - 'A' + 10;
} else {
return -1;
}
if (i % 2 == 0) {
*pd = digit << 4;
} else {
*pd |= digit;
++pd;
}
}
if (*ps != '\0') return -1;
return 0;
}
// Search an array of sha1 strings for one matching the given sha1.
// Return the index of the match on success, or -1 if no match is
// found.
int FindMatchingPatch(uint8_t* sha1, char** const patch_sha1_str,
int num_patches) {
int i;
uint8_t patch_sha1[SHA_DIGEST_SIZE];
for (i = 0; i < num_patches; ++i) {
if (ParseSha1(patch_sha1_str[i], patch_sha1) == 0 &&
memcmp(patch_sha1, sha1, SHA_DIGEST_SIZE) == 0) {
return i;
}
}
return -1;
}
// Returns 0 if the contents of the file (argv[2]) or the cached file
// match any of the sha1's on the command line (argv[3:]). Returns
// nonzero otherwise.
int applypatch_check(const char* filename,
int num_patches, char** const patch_sha1_str) {
FileContents file;
file.data = NULL;
// It's okay to specify no sha1s; the check will pass if the
// LoadFileContents is successful. (Useful for reading
// partitions, where the filename encodes the sha1s; no need to
// check them twice.)
if (LoadFileContents(filename, &file, RETOUCH_DO_MASK) != 0 ||
(num_patches > 0 &&
FindMatchingPatch(file.sha1, patch_sha1_str, num_patches) < 0)) {
printf("file \"%s\" doesn't have any of expected "
"sha1 sums; checking cache\n", filename);
free(file.data);
// If the source file is missing or corrupted, it might be because
// we were killed in the middle of patching it. A copy of it
// should have been made in CACHE_TEMP_SOURCE. If that file
// exists and matches the sha1 we're looking for, the check still
// passes.
if (LoadFileContents(CACHE_TEMP_SOURCE, &file, RETOUCH_DO_MASK) != 0) {
printf("failed to load cache file\n");
return 1;
}
if (FindMatchingPatch(file.sha1, patch_sha1_str, num_patches) < 0) {
printf("cache bits don't match any sha1 for \"%s\"\n", filename);
free(file.data);
return 1;
}
}
free(file.data);
return 0;
}
int ShowLicenses() {
ShowBSDiffLicense();
return 0;
}
ssize_t FileSink(unsigned char* data, ssize_t len, void* token) {
int fd = *(int *)token;
ssize_t done = 0;
ssize_t wrote;
while (done < (ssize_t) len) {
wrote = write(fd, data+done, len-done);
if (wrote <= 0) {
printf("error writing %d bytes: %s\n", (int)(len-done), strerror(errno));
return done;
}
done += wrote;
}
return done;
}
typedef struct {
unsigned char* buffer;
ssize_t size;
ssize_t pos;
} MemorySinkInfo;
ssize_t MemorySink(unsigned char* data, ssize_t len, void* token) {
MemorySinkInfo* msi = (MemorySinkInfo*)token;
if (msi->size - msi->pos < len) {
return -1;
}
memcpy(msi->buffer + msi->pos, data, len);
msi->pos += len;
return len;
}
// Return the amount of free space (in bytes) on the filesystem
// containing filename. filename must exist. Return -1 on error.
size_t FreeSpaceForFile(const char* filename) {
struct statfs sf;
if (statfs(filename, &sf) != 0) {
printf("failed to statfs %s: %s\n", filename, strerror(errno));
return -1;
}
return sf.f_bsize * sf.f_bfree;
}
int CacheSizeCheck(size_t bytes) {
if (MakeFreeSpaceOnCache(bytes) < 0) {
printf("unable to make %ld bytes available on /cache\n", (long)bytes);
return 1;
} else {
return 0;
}
}
// This function applies binary patches to files in a way that is safe
// (the original file is not touched until we have the desired
// replacement for it) and idempotent (it's okay to run this program
// multiple times).
//
// - if the sha1 hash of <target_filename> is <target_sha1_string>,
// does nothing and exits successfully.
//
// - otherwise, if the sha1 hash of <source_filename> is one of the
// entries in <patch_sha1_str>, the corresponding patch from
// <patch_data> (which must be a VAL_BLOB) is applied to produce a
// new file (the type of patch is automatically detected from the
// blob daat). If that new file has sha1 hash <target_sha1_str>,
// moves it to replace <target_filename>, and exits successfully.
// Note that if <source_filename> and <target_filename> are not the
// same, <source_filename> is NOT deleted on success.
// <target_filename> may be the string "-" to mean "the same as
// source_filename".
//
// - otherwise, or if any error is encountered, exits with non-zero
// status.
//
// <source_filename> may refer to a partition to read the source data.
// See the comments for the LoadPartition Contents() function above
// for the format of such a filename.
int applypatch(const char* source_filename,
const char* target_filename,
const char* target_sha1_str,
size_t target_size,
int num_patches,
char** const patch_sha1_str,
Value** patch_data) {
printf("\napplying patch to %s\n", source_filename);
if (target_filename[0] == '-' &&
target_filename[1] == '\0') {
target_filename = source_filename;
}
uint8_t target_sha1[SHA_DIGEST_SIZE];
if (ParseSha1(target_sha1_str, target_sha1) != 0) {
printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str);
return 1;
}
FileContents copy_file;
FileContents source_file;
const Value* source_patch_value = NULL;
const Value* copy_patch_value = NULL;
int made_copy = 0;
// We try to load the target file into the source_file object.
if (LoadFileContents(target_filename, &source_file,
RETOUCH_DO_MASK) == 0) {
if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) == 0) {
// The early-exit case: the patch was already applied, this file
// has the desired hash, nothing for us to do.
printf("\"%s\" is already target; no patch needed\n",
target_filename);
return 0;
}
}
if (source_file.data == NULL ||
(target_filename != source_filename &&
strcmp(target_filename, source_filename) != 0)) {
// Need to load the source file: either we failed to load the
// target file, or we did but it's different from the source file.
free(source_file.data);
LoadFileContents(source_filename, &source_file,
RETOUCH_DO_MASK);
}
if (source_file.data != NULL) {
int to_use = FindMatchingPatch(source_file.sha1,
patch_sha1_str, num_patches);
if (to_use >= 0) {
source_patch_value = patch_data[to_use];
}
}
if (source_patch_value == NULL) {
free(source_file.data);
printf("source file is bad; trying copy\n");
if (LoadFileContents(CACHE_TEMP_SOURCE, &copy_file,
RETOUCH_DO_MASK) < 0) {
// fail.
printf("failed to read copy file\n");
return 1;
}
int to_use = FindMatchingPatch(copy_file.sha1,
patch_sha1_str, num_patches);
if (to_use >= 0) {
copy_patch_value = patch_data[to_use];
}
if (copy_patch_value == NULL) {
// fail.
printf("copy file doesn't match source SHA-1s either\n");
return 1;
}
}
int retry = 1;
SHA_CTX ctx;
int output;
MemorySinkInfo msi;
FileContents* source_to_use;
char* outname;
// assume that target_filename (eg "/system/app/Foo.apk") is located
// on the same filesystem as its top-level directory ("/system").
// We need something that exists for calling statfs().
char target_fs[strlen(target_filename)+1];
char* slash = strchr(target_filename+1, '/');
if (slash != NULL) {
int count = slash - target_filename;
strncpy(target_fs, target_filename, count);
target_fs[count] = '\0';
} else {
strcpy(target_fs, target_filename);
}
do {
// Is there enough room in the target filesystem to hold the patched
// file?
if (strncmp(target_filename, "MTD:", 4) == 0 ||
strncmp(target_filename, "EMMC:", 5) == 0) {
// If the target is a partition, we're actually going to
// write the output to /tmp and then copy it to the
// partition. statfs() always returns 0 blocks free for
// /tmp, so instead we'll just assume that /tmp has enough
// space to hold the file.
// We still write the original source to cache, in case
// the partition write is interrupted.
if (MakeFreeSpaceOnCache(source_file.size) < 0) {
printf("not enough free space on /cache\n");
return 1;
}
if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
printf("failed to back up source file\n");
return 1;
}
made_copy = 1;
retry = 0;
} else {
int enough_space = 0;
if (retry > 0) {
size_t free_space = FreeSpaceForFile(target_fs);
enough_space =
(free_space > (256 << 10)) && // 256k (two-block) minimum
(free_space > (target_size * 3 / 2)); // 50% margin of error
printf("target %ld bytes; free space %ld bytes; retry %d; enough %d\n",
(long)target_size, (long)free_space, retry, enough_space);
}
if (!enough_space) {
retry = 0;
}
if (!enough_space && source_patch_value != NULL) {
// Using the original source, but not enough free space. First
// 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) {
// 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.
printf("not enough free space for target but source "
"is partition\n");
return 1;
}
if (MakeFreeSpaceOnCache(source_file.size) < 0) {
printf("not enough free space on /cache\n");
return 1;
}
if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
printf("failed to back up source file\n");
return 1;
}
made_copy = 1;
unlink(source_filename);
size_t free_space = FreeSpaceForFile(target_fs);
printf("(now %ld bytes free for target)\n", (long)free_space);
}
}
const Value* patch;
if (source_patch_value != NULL) {
source_to_use = &source_file;
patch = source_patch_value;
} else {
source_to_use = &copy_file;
patch = copy_patch_value;
}
if (patch->type != VAL_BLOB) {
printf("patch is not a blob\n");
return 1;
}
SinkFn sink = NULL;
void* token = NULL;
output = -1;
outname = NULL;
if (strncmp(target_filename, "MTD:", 4) == 0 ||
strncmp(target_filename, "EMMC:", 5) == 0) {
// We store the decoded output in memory.
msi.buffer = malloc(target_size);
if (msi.buffer == NULL) {
printf("failed to alloc %ld bytes for output\n",
(long)target_size);
return 1;
}
msi.pos = 0;
msi.size = target_size;
sink = MemorySink;
token = &msi;
} else {
// We write the decoded output to "<tgt-file>.patch".
outname = (char*)malloc(strlen(target_filename) + 10);
strcpy(outname, target_filename);
strcat(outname, ".patch");
output = open(outname, O_WRONLY | O_CREAT | O_TRUNC);
if (output < 0) {
printf("failed to open output file %s: %s\n",
outname, strerror(errno));
return 1;
}
sink = FileSink;
token = &output;
}
char* header = patch->data;
ssize_t header_bytes_read = patch->size;
SHA_init(&ctx);
int result;
if (header_bytes_read >= 8 &&
memcmp(header, "BSDIFF40", 8) == 0) {
result = ApplyBSDiffPatch(source_to_use->data, source_to_use->size,
patch, 0, sink, token, &ctx);
} else if (header_bytes_read >= 8 &&
memcmp(header, "IPDIFF10", 8) == 0) {
result = ApplyIPDiffPatch(source_to_use->data, source_to_use->size,
patch, 0, sink, token, &ctx);
} else if (header_bytes_read >= 8 &&
memcmp(header, "IMGDIFF2", 8) == 0) {
result = ApplyImagePatch(source_to_use->data, source_to_use->size,
patch, sink, token, &ctx);
} else {
printf("Unknown patch file format\n");
return 1;
}
if (output >= 0) {
fsync(output);
close(output);
}
if (result != 0) {
if (retry == 0) {
printf("applying patch failed\n");
return result != 0;
} else {
printf("applying patch failed; retrying\n");
}
if (outname != NULL) {
unlink(outname);
}
} else {
// succeeded; no need to retry
break;
}
} while (retry-- > 0);
const uint8_t* current_target_sha1 = SHA_final(&ctx);
if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_SIZE) != 0) {
printf("patch did not produce expected sha1\n");
return 1;
}
if (output < 0) {
// Copy the temp file to the partition.
if (WriteToPartition(msi.buffer, msi.pos, target_filename) != 0) {
printf("write of patched data to %s failed\n", target_filename);
return 1;
}
free(msi.buffer);
} else {
// Give the .patch file the same owner, group, and mode of the
// original source file.
if (chmod(outname, source_to_use->st.st_mode) != 0) {
printf("chmod of \"%s\" failed: %s\n", outname, strerror(errno));
return 1;
}
if (chown(outname, source_to_use->st.st_uid,
source_to_use->st.st_gid) != 0) {
printf("chown of \"%s\" failed: %s\n", outname, strerror(errno));
return 1;
}
// Finally, rename the .patch file to replace the target file.
if (rename(outname, target_filename) != 0) {
printf("rename of .patch to \"%s\" failed: %s\n",
target_filename, strerror(errno));
return 1;
}
}
// If this run of applypatch created the copy, and we're here, we
// can delete it.
if (made_copy) unlink(CACHE_TEMP_SOURCE);
// Success!
return 0;
}
+95
View File
@@ -0,0 +1,95 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _APPLYPATCH_H
#define _APPLYPATCH_H
#include <sys/stat.h>
#include "mincrypt/sha.h"
#include "minelf/Retouch.h"
#include "edify/expr.h"
typedef struct _Patch {
uint8_t sha1[SHA_DIGEST_SIZE];
const char* patch_filename;
} Patch;
typedef struct _FileContents {
uint8_t sha1[SHA_DIGEST_SIZE];
unsigned char* data;
ssize_t size;
struct stat st;
} FileContents;
// When there isn't enough room on the target filesystem to hold the
// patched version of the file, we copy the original here and delete
// it to free up space. If the expected source file doesn't exist, or
// is corrupted, we look to see if this file contains the bits we want
// and use it as the source instead.
#define CACHE_TEMP_SOURCE "/cache/saved.file"
typedef ssize_t (*SinkFn)(unsigned char*, ssize_t, void*);
// applypatch.c
int ShowLicenses();
size_t FreeSpaceForFile(const char* filename);
int CacheSizeCheck(size_t bytes);
int ParseSha1(const char* str, uint8_t* digest);
int applypatch(const char* source_filename,
const char* target_filename,
const char* target_sha1_str,
size_t target_size,
int num_patches,
char** const patch_sha1_str,
Value** patch_data);
int applypatch_check(const char* filename,
int num_patches,
char** const patch_sha1_str);
int LoadFileContents(const char* filename, FileContents* file,
int retouch_flag);
int SaveFileContents(const char* filename, FileContents file);
void FreeFileContents(FileContents* file);
int FindMatchingPatch(uint8_t* sha1, char** const patch_sha1_str,
int num_patches);
// bsdiff.c
void ShowBSDiffLicense();
int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
const Value* patch, ssize_t patch_offset,
SinkFn sink, void* token, SHA_CTX* ctx);
int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
const Value* patch, ssize_t patch_offset,
unsigned char** new_data, ssize_t* new_size);
// imgpatch.c
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
const Value* patch,
SinkFn sink, void* token, SHA_CTX* ctx);
// delta update
int ApplyIPDiffPatch(const unsigned char* old_data, ssize_t old_size,
const Value* patch, ssize_t patch_offset,
SinkFn sink, void* token, SHA_CTX* ctx);
int ipth_UpdateOneFile(const unsigned char* old_buf, ssize_t old_len,
const Value* patch, ssize_t patch_offset,
unsigned char** new_buf, ssize_t* new_len);
// freecache.c
int MakeFreeSpaceOnCache(size_t bytes_needed);
#endif
+350
View File
@@ -0,0 +1,350 @@
#!/bin/bash
#
# A test suite for applypatch. Run in a client where you have done
# envsetup, choosecombo, etc.
#
# DO NOT RUN THIS ON A DEVICE YOU CARE ABOUT. It will mess up your
# system partition.
#
#
# TODO: find some way to get this run regularly along with the rest of
# the tests.
EMULATOR_PORT=5580
DATA_DIR=$ANDROID_BUILD_TOP/bootable/recovery/applypatch/testdata
# This must be the filename that applypatch uses for its copies.
CACHE_TEMP_SOURCE=/cache/saved.file
# Put all binaries and files here. We use /cache because it's a
# temporary filesystem in the emulator; it's created fresh each time
# the emulator starts.
WORK_DIR=/system
# partition that WORK_DIR is located on, without the leading slash
WORK_FS=system
# set to 0 to use a device instead
USE_EMULATOR=1
# ------------------------
tmpdir=$(mktemp -d)
if [ "$USE_EMULATOR" == 1 ]; then
emulator -wipe-data -noaudio -no-window -port $EMULATOR_PORT &
pid_emulator=$!
ADB="adb -s emulator-$EMULATOR_PORT "
else
ADB="adb -d "
fi
echo "waiting to connect to device"
$ADB wait-for-device
echo "device is available"
$ADB remount
# free up enough space on the system partition for the test to run.
$ADB shell rm -r /system/media
# run a command on the device; exit with the exit status of the device
# command.
run_command() {
$ADB shell "$@" \; echo \$? | awk '{if (b) {print a}; a=$0; b=1} END {exit a}'
}
testname() {
echo
echo "$1"...
testname="$1"
}
fail() {
echo
echo FAIL: $testname
echo
[ "$open_pid" == "" ] || kill $open_pid
[ "$pid_emulator" == "" ] || kill $pid_emulator
exit 1
}
sha1() {
sha1sum $1 | awk '{print $1}'
}
free_space() {
run_command df | awk "/$1/ {print gensub(/K/, \"\", \"g\", \$6)}"
}
cleanup() {
# not necessary if we're about to kill the emulator, but nice for
# running on real devices or already-running emulators.
testname "removing test files"
run_command rm $WORK_DIR/bloat.dat
run_command rm $WORK_DIR/old.file
run_command rm $WORK_DIR/foo
run_command rm $WORK_DIR/patch.bsdiff
run_command rm $WORK_DIR/applypatch
run_command rm $CACHE_TEMP_SOURCE
run_command rm /cache/bloat*.dat
[ "$pid_emulator" == "" ] || kill $pid_emulator
if [ $# == 0 ]; then
rm -rf $tmpdir
fi
}
cleanup leave_tmp
$ADB push $ANDROID_PRODUCT_OUT/system/bin/applypatch $WORK_DIR/applypatch
BAD1_SHA1=$(printf "%040x" $RANDOM)
BAD2_SHA1=$(printf "%040x" $RANDOM)
OLD_SHA1=$(sha1 $DATA_DIR/old.file)
NEW_SHA1=$(sha1 $DATA_DIR/new.file)
NEW_SIZE=$(stat -c %s $DATA_DIR/new.file)
# --------------- basic execution ----------------------
testname "usage message"
run_command $WORK_DIR/applypatch && fail
testname "display license"
run_command $WORK_DIR/applypatch -l | grep -q -i copyright || fail
# --------------- check mode ----------------------
$ADB push $DATA_DIR/old.file $WORK_DIR
testname "check mode single"
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $OLD_SHA1 || fail
testname "check mode multiple"
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD1_SHA1 $OLD_SHA1 $BAD2_SHA1|| fail
testname "check mode failure"
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 && fail
$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE
# put some junk in the old file
run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail
testname "check mode cache (corrupted) single"
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $OLD_SHA1 || fail
testname "check mode cache (corrupted) multiple"
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD1_SHA1 $OLD_SHA1 $BAD2_SHA1|| fail
testname "check mode cache (corrupted) failure"
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 && fail
# remove the old file entirely
run_command rm $WORK_DIR/old.file
testname "check mode cache (missing) single"
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $OLD_SHA1 || fail
testname "check mode cache (missing) multiple"
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD1_SHA1 $OLD_SHA1 $BAD2_SHA1|| fail
testname "check mode cache (missing) failure"
run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 && fail
# --------------- apply patch ----------------------
$ADB push $DATA_DIR/old.file $WORK_DIR
$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR
echo hello > $tmpdir/foo
$ADB push $tmpdir/foo $WORK_DIR
# Check that the partition has enough space to apply the patch without
# copying. If it doesn't, we'll be testing the low-space condition
# when we intend to test the not-low-space condition.
testname "apply patches (with enough space)"
free_kb=$(free_space $WORK_FS)
echo "${free_kb}kb free on /$WORK_FS."
if (( free_kb * 1024 < NEW_SIZE * 3 / 2 )); then
echo "Not enough space on /$WORK_FS to patch test file."
echo
echo "This doesn't mean that applypatch is necessarily broken;"
echo "just that /$WORK_FS doesn't have enough free space to"
echo "properly run this test."
exit 1
fi
testname "apply bsdiff patch"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
$ADB pull $WORK_DIR/old.file $tmpdir/patched
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
testname "reapply bsdiff patch"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
$ADB pull $WORK_DIR/old.file $tmpdir/patched
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
# --------------- apply patch in new location ----------------------
$ADB push $DATA_DIR/old.file $WORK_DIR
$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR
# Check that the partition has enough space to apply the patch without
# copying. If it doesn't, we'll be testing the low-space condition
# when we intend to test the not-low-space condition.
testname "apply patch to new location (with enough space)"
free_kb=$(free_space $WORK_FS)
echo "${free_kb}kb free on /$WORK_FS."
if (( free_kb * 1024 < NEW_SIZE * 3 / 2 )); then
echo "Not enough space on /$WORK_FS to patch test file."
echo
echo "This doesn't mean that applypatch is necessarily broken;"
echo "just that /$WORK_FS doesn't have enough free space to"
echo "properly run this test."
exit 1
fi
run_command rm $WORK_DIR/new.file
run_command rm $CACHE_TEMP_SOURCE
testname "apply bsdiff patch to new location"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
$ADB pull $WORK_DIR/new.file $tmpdir/patched
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
testname "reapply bsdiff patch to new location"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
$ADB pull $WORK_DIR/new.file $tmpdir/patched
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE
# put some junk in the old file
run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail
testname "apply bsdiff patch to new location with corrupted source"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $OLD_SHA1:$WORK_DIR/patch.bsdiff $BAD1_SHA1:$WORK_DIR/foo || fail
$ADB pull $WORK_DIR/new.file $tmpdir/patched
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
# put some junk in the cache copy, too
run_command dd if=/dev/urandom of=$CACHE_TEMP_SOURCE count=100 bs=1024 || fail
run_command rm $WORK_DIR/new.file
testname "apply bsdiff patch to new location with corrupted source and copy (no new file)"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $OLD_SHA1:$WORK_DIR/patch.bsdiff $BAD1_SHA1:$WORK_DIR/foo && fail
# put some junk in the new file
run_command dd if=/dev/urandom of=$WORK_DIR/new.file count=100 bs=1024 || fail
testname "apply bsdiff patch to new location with corrupted source and copy (bad new file)"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $OLD_SHA1:$WORK_DIR/patch.bsdiff $BAD1_SHA1:$WORK_DIR/foo && fail
# --------------- apply patch with low space on /system ----------------------
$ADB push $DATA_DIR/old.file $WORK_DIR
$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR
free_kb=$(free_space $WORK_FS)
echo "${free_kb}kb free on /$WORK_FS; we'll soon fix that."
echo run_command dd if=/dev/zero of=$WORK_DIR/bloat.dat count=$((free_kb-512)) bs=1024 || fail
run_command dd if=/dev/zero of=$WORK_DIR/bloat.dat count=$((free_kb-512)) bs=1024 || fail
free_kb=$(free_space $WORK_FS)
echo "${free_kb}kb free on /$WORK_FS now."
testname "apply bsdiff patch with low space"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
$ADB pull $WORK_DIR/old.file $tmpdir/patched
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
testname "reapply bsdiff patch with low space"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
$ADB pull $WORK_DIR/old.file $tmpdir/patched
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
# --------------- apply patch with low space on /system and /cache ----------------------
$ADB push $DATA_DIR/old.file $WORK_DIR
$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR
free_kb=$(free_space $WORK_FS)
echo "${free_kb}kb free on /$WORK_FS"
run_command mkdir /cache/subdir
run_command 'echo > /cache/subdir/a.file'
run_command 'echo > /cache/a.file'
run_command mkdir /cache/recovery /cache/recovery/otatest
run_command 'echo > /cache/recovery/otatest/b.file'
run_command "echo > $CACHE_TEMP_SOURCE"
free_kb=$(free_space cache)
echo "${free_kb}kb free on /cache; we'll soon fix that."
run_command dd if=/dev/zero of=/cache/bloat_small.dat count=128 bs=1024 || fail
run_command dd if=/dev/zero of=/cache/bloat_large.dat count=$((free_kb-640)) bs=1024 || fail
free_kb=$(free_space cache)
echo "${free_kb}kb free on /cache now."
testname "apply bsdiff patch with low space, full cache, can't delete enough"
$ADB shell 'cat >> /cache/bloat_large.dat' & open_pid=$!
echo "open_pid is $open_pid"
# size check should fail even though it deletes some stuff
run_command $WORK_DIR/applypatch -s $NEW_SIZE && fail
run_command ls /cache/bloat_small.dat && fail # was deleted
run_command ls /cache/a.file && fail # was deleted
run_command ls /cache/recovery/otatest/b.file && fail # was deleted
run_command ls /cache/bloat_large.dat || fail # wasn't deleted because it was open
run_command ls /cache/subdir/a.file || fail # wasn't deleted because it's in a subdir
run_command ls $CACHE_TEMP_SOURCE || fail # wasn't deleted because it's the source file copy
# should fail; not enough files can be deleted
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff && fail
run_command ls /cache/bloat_large.dat || fail # wasn't deleted because it was open
run_command ls /cache/subdir/a.file || fail # wasn't deleted because it's in a subdir
run_command ls $CACHE_TEMP_SOURCE || fail # wasn't deleted because it's the source file copy
kill $open_pid # /cache/bloat_large.dat is no longer open
testname "apply bsdiff patch with low space, full cache, can delete enough"
# should succeed after deleting /cache/bloat_large.dat
run_command $WORK_DIR/applypatch -s $NEW_SIZE || fail
run_command ls /cache/bloat_large.dat && fail # was deleted
run_command ls /cache/subdir/a.file || fail # still wasn't deleted because it's in a subdir
run_command ls $CACHE_TEMP_SOURCE || fail # wasn't deleted because it's the source file copy
# should succeed
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
$ADB pull $WORK_DIR/old.file $tmpdir/patched
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
run_command ls /cache/subdir/a.file || fail # still wasn't deleted because it's in a subdir
run_command ls $CACHE_TEMP_SOURCE && fail # was deleted because patching overwrote it, then deleted it
# --------------- apply patch from cache ----------------------
$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE
# put some junk in the old file
run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail
testname "apply bsdiff patch from cache (corrupted source) with low space"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
$ADB pull $WORK_DIR/old.file $tmpdir/patched
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE
# remove the old file entirely
run_command rm $WORK_DIR/old.file
testname "apply bsdiff patch from cache (missing source) with low space"
run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
$ADB pull $WORK_DIR/old.file $tmpdir/patched
diff -q $DATA_DIR/new.file $tmpdir/patched || fail
# --------------- cleanup ----------------------
cleanup
echo
echo PASS
echo
+410
View File
@@ -0,0 +1,410 @@
/*
* 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 *old,off_t oldsize,u_char *new,off_t newsize)
{
off_t i;
for(i=0;(i<oldsize)&&(i<newsize);i++)
if(old[i]!=new[i]) break;
return i;
}
static off_t search(off_t *I,u_char *old,off_t oldsize,
u_char *new,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],new,newsize);
y=matchlen(old+I[en],oldsize-I[en],new,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],new,MIN(oldsize-I[x],newsize))<0) {
return search(I,old,oldsize,new,newsize,x,en,pos);
} else {
return search(I,old,oldsize,new,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, new, newsize are arguments; we don't load this
// data from files. old and new 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* new, 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 = malloc((oldsize+1) * sizeof(off_t));
V = malloc((oldsize+1) * sizeof(off_t));
qsufsort(*IP, V, old, oldsize);
free(V);
}
I = *IP;
if(((db=malloc(newsize+1))==NULL) ||
((eb=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,new+scan,newsize-scan,
0,oldsize,&pos);
for(;scsc<scan+len;scsc++)
if((scsc+lastoffset<oldsize) &&
(old[scsc+lastoffset] == new[scsc]))
oldscore++;
if(((len==oldscore) && (len!=0)) ||
(len>oldscore+8)) break;
if((scan+lastoffset<oldsize) &&
(old[scan+lastoffset] == new[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]==new[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]==new[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(new[lastscan+lenf-overlap+i]==
old[lastpos+lenf-overlap+i]) s++;
if(new[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]=new[lastscan+i]-old[lastpos+i];
for(i=0;i<(scan-lenb)-(lastscan+lenf);i++)
eb[eblen+i]=new[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;
}
+252
View File
@@ -0,0 +1,252 @@
/*
* Copyright (C) 2008 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 a nearly line-for-line copy of bspatch.c from the
// bsdiff-4.3 distribution; the primary differences being how the
// input and output data are read and the error handling. Running
// applypatch with the -l option will display the bsdiff license
// notice.
#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <bzlib.h>
#include "mincrypt/sha.h"
#include "applypatch.h"
void ShowBSDiffLicense() {
puts("The bsdiff library used herein is:\n"
"\n"
"Copyright 2003-2005 Colin Percival\n"
"All rights reserved\n"
"\n"
"Redistribution and use in source and binary forms, with or without\n"
"modification, are permitted providing that the following conditions\n"
"are met:\n"
"1. Redistributions of source code must retain the above copyright\n"
" notice, this list of conditions and the following disclaimer.\n"
"2. Redistributions in binary form must reproduce the above copyright\n"
" notice, this list of conditions and the following disclaimer in the\n"
" documentation and/or other materials provided with the distribution.\n"
"\n"
"THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
"IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"
"WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
"ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n"
"DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n"
"DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n"
"OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n"
"HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n"
"STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\n"
"IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n"
"POSSIBILITY OF SUCH DAMAGE.\n"
"\n------------------\n\n"
"This program uses Julian R Seward's \"libbzip2\" library, available\n"
"from http://www.bzip.org/.\n"
);
}
static off_t offtin(u_char *buf)
{
off_t y;
y=buf[7]&0x7F;
y=y*256;y+=buf[6];
y=y*256;y+=buf[5];
y=y*256;y+=buf[4];
y=y*256;y+=buf[3];
y=y*256;y+=buf[2];
y=y*256;y+=buf[1];
y=y*256;y+=buf[0];
if(buf[7]&0x80) y=-y;
return y;
}
int FillBuffer(unsigned char* buffer, int size, bz_stream* stream) {
stream->next_out = (char*)buffer;
stream->avail_out = size;
while (stream->avail_out > 0) {
int bzerr = BZ2_bzDecompress(stream);
if (bzerr != BZ_OK && bzerr != BZ_STREAM_END) {
printf("bz error %d decompressing\n", bzerr);
return -1;
}
if (stream->avail_out > 0) {
printf("need %d more bytes\n", stream->avail_out);
}
}
return 0;
}
int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
const Value* patch, ssize_t patch_offset,
SinkFn sink, void* token, SHA_CTX* ctx) {
unsigned char* new_data;
ssize_t new_size;
if (ApplyBSDiffPatchMem(old_data, old_size, patch, patch_offset,
&new_data, &new_size) != 0) {
return -1;
}
if (sink(new_data, new_size, token) < new_size) {
printf("short write of output: %d (%s)\n", errno, strerror(errno));
return 1;
}
if (ctx) {
SHA_update(ctx, new_data, new_size);
}
free(new_data);
return 0;
}
int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
const Value* patch, ssize_t patch_offset,
unsigned char** new_data, ssize_t* new_size) {
// Patch data format:
// 0 8 "BSDIFF40"
// 8 8 X
// 16 8 Y
// 24 8 sizeof(newfile)
// 32 X bzip2(control block)
// 32+X Y bzip2(diff block)
// 32+X+Y ??? bzip2(extra block)
// with control block a set of triples (x,y,z) meaning "add x bytes
// from oldfile to x bytes from the diff block; copy y bytes from the
// extra block; seek forwards in oldfile by z bytes".
unsigned char* header = (unsigned char*) patch->data + patch_offset;
if (memcmp(header, "BSDIFF40", 8) != 0) {
printf("corrupt bsdiff patch file header (magic number)\n");
return 1;
}
ssize_t ctrl_len, data_len;
ctrl_len = offtin(header+8);
data_len = offtin(header+16);
*new_size = offtin(header+24);
if (ctrl_len < 0 || data_len < 0 || *new_size < 0) {
printf("corrupt patch file header (data lengths)\n");
return 1;
}
int bzerr;
bz_stream cstream;
cstream.next_in = patch->data + patch_offset + 32;
cstream.avail_in = ctrl_len;
cstream.bzalloc = NULL;
cstream.bzfree = NULL;
cstream.opaque = NULL;
if ((bzerr = BZ2_bzDecompressInit(&cstream, 0, 0)) != BZ_OK) {
printf("failed to bzinit control stream (%d)\n", bzerr);
}
bz_stream dstream;
dstream.next_in = patch->data + patch_offset + 32 + ctrl_len;
dstream.avail_in = data_len;
dstream.bzalloc = NULL;
dstream.bzfree = NULL;
dstream.opaque = NULL;
if ((bzerr = BZ2_bzDecompressInit(&dstream, 0, 0)) != BZ_OK) {
printf("failed to bzinit diff stream (%d)\n", bzerr);
}
bz_stream estream;
estream.next_in = patch->data + patch_offset + 32 + ctrl_len + data_len;
estream.avail_in = patch->size - (patch_offset + 32 + ctrl_len + data_len);
estream.bzalloc = NULL;
estream.bzfree = NULL;
estream.opaque = NULL;
if ((bzerr = BZ2_bzDecompressInit(&estream, 0, 0)) != BZ_OK) {
printf("failed to bzinit extra stream (%d)\n", bzerr);
}
*new_data = malloc(*new_size);
if (*new_data == NULL) {
printf("failed to allocate %ld bytes of memory for output file\n",
(long)*new_size);
return 1;
}
off_t oldpos = 0, newpos = 0;
off_t ctrl[3];
off_t len_read;
int i;
unsigned char buf[24];
while (newpos < *new_size) {
// Read control data
if (FillBuffer(buf, 24, &cstream) != 0) {
printf("error while reading control stream\n");
return 1;
}
ctrl[0] = offtin(buf);
ctrl[1] = offtin(buf+8);
ctrl[2] = offtin(buf+16);
// Sanity check
if (newpos + ctrl[0] > *new_size) {
printf("corrupt patch (new file overrun)\n");
return 1;
}
// Read diff string
if (FillBuffer(*new_data + newpos, ctrl[0], &dstream) != 0) {
printf("error while reading diff stream\n");
return 1;
}
// Add old data to diff string
for (i = 0; i < ctrl[0]; ++i) {
if ((oldpos+i >= 0) && (oldpos+i < old_size)) {
(*new_data)[newpos+i] += old_data[oldpos+i];
}
}
// Adjust pointers
newpos += ctrl[0];
oldpos += ctrl[0];
// Sanity check
if (newpos + ctrl[1] > *new_size) {
printf("corrupt patch (new file overrun)\n");
return 1;
}
// Read extra string
if (FillBuffer(*new_data + newpos, ctrl[1], &estream) != 0) {
printf("error while reading extra stream\n");
return 1;
}
// Adjust pointers
newpos += ctrl[1];
oldpos += ctrl[2];
}
BZ2_bzDecompressEnd(&cstream);
BZ2_bzDecompressEnd(&dstream);
BZ2_bzDecompressEnd(&estream);
return 0;
}
+172
View File
@@ -0,0 +1,172 @@
#include <errno.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <unistd.h>
#include <dirent.h>
#include <ctype.h>
#include "applypatch.h"
static int EliminateOpenFiles(char** files, int file_count) {
DIR* d;
struct dirent* de;
d = opendir("/proc");
if (d == NULL) {
printf("error opening /proc: %s\n", strerror(errno));
return -1;
}
while ((de = readdir(d)) != 0) {
int i;
for (i = 0; de->d_name[i] != '\0' && isdigit(de->d_name[i]); ++i);
if (de->d_name[i]) continue;
// de->d_name[i] is numeric
char path[FILENAME_MAX];
strcpy(path, "/proc/");
strcat(path, de->d_name);
strcat(path, "/fd/");
DIR* fdd;
struct dirent* fdde;
fdd = opendir(path);
if (fdd == NULL) {
printf("error opening %s: %s\n", path, strerror(errno));
continue;
}
while ((fdde = readdir(fdd)) != 0) {
char fd_path[FILENAME_MAX];
char link[FILENAME_MAX];
strcpy(fd_path, path);
strcat(fd_path, fdde->d_name);
int count;
count = readlink(fd_path, link, sizeof(link)-1);
if (count >= 0) {
link[count] = '\0';
// This is inefficient, but it should only matter if there are
// lots of files in /cache, and lots of them are open (neither
// of which should be true, especially in recovery).
if (strncmp(link, "/cache/", 7) == 0) {
int j;
for (j = 0; j < file_count; ++j) {
if (files[j] && strcmp(files[j], link) == 0) {
printf("%s is open by %s\n", link, de->d_name);
free(files[j]);
files[j] = NULL;
}
}
}
}
}
closedir(fdd);
}
closedir(d);
return 0;
}
int FindExpendableFiles(char*** names, int* entries) {
DIR* d;
struct dirent* de;
int size = 32;
*entries = 0;
*names = malloc(size * sizeof(char*));
char path[FILENAME_MAX];
// We're allowed to delete unopened regular files in any of these
// directories.
const char* dirs[2] = {"/cache", "/cache/recovery/otatest"};
unsigned int i;
for (i = 0; i < sizeof(dirs)/sizeof(dirs[0]); ++i) {
d = opendir(dirs[i]);
if (d == NULL) {
printf("error opening %s: %s\n", dirs[i], strerror(errno));
continue;
}
// Look for regular files in the directory (not in any subdirectories).
while ((de = readdir(d)) != 0) {
strcpy(path, dirs[i]);
strcat(path, "/");
strcat(path, de->d_name);
// We can't delete CACHE_TEMP_SOURCE; if it's there we might have
// restarted during installation and could be depending on it to
// be there.
if (strcmp(path, CACHE_TEMP_SOURCE) == 0) continue;
struct stat st;
if (stat(path, &st) == 0 && S_ISREG(st.st_mode)) {
if (*entries >= size) {
size *= 2;
*names = realloc(*names, size * sizeof(char*));
}
(*names)[(*entries)++] = strdup(path);
}
}
closedir(d);
}
printf("%d regular files in deletable directories\n", *entries);
if (EliminateOpenFiles(*names, *entries) < 0) {
return -1;
}
return 0;
}
int MakeFreeSpaceOnCache(size_t bytes_needed) {
size_t free_now = FreeSpaceForFile("/cache");
printf("%ld bytes free on /cache (%ld needed)\n",
(long)free_now, (long)bytes_needed);
if (free_now >= bytes_needed) {
return 0;
}
char** names;
int entries;
if (FindExpendableFiles(&names, &entries) < 0) {
return -1;
}
if (entries == 0) {
// nothing we can delete to free up space!
printf("no files can be deleted to free space on /cache\n");
return -1;
}
// We could try to be smarter about which files to delete: the
// biggest ones? the smallest ones that will free up enough space?
// the oldest? the newest?
//
// Instead, we'll be dumb.
int i;
for (i = 0; i < entries && free_now < bytes_needed; ++i) {
if (names[i]) {
unlink(names[i]);
free_now = FreeSpaceForFile("/cache");
printf("deleted %s; now %ld bytes free\n", names[i], (long)free_now);
free(names[i]);
}
}
for (; i < entries; ++i) {
free(names[i]);
}
free(names);
return (free_now >= bytes_needed) ? 0 : -1;
}
File diff suppressed because it is too large Load Diff
+30
View File
@@ -0,0 +1,30 @@
/*
* 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.
*/
// 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
// 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
// The gzip footer size really is fixed.
#define GZIP_FOOTER_LEN 8
+118
View File
@@ -0,0 +1,118 @@
#!/bin/bash
#
# A script for testing imgdiff/applypatch. It takes two full OTA
# packages as arguments. It generates (on the host) patches for all
# the zip/jar/apk files they have in common, as well as boot and
# recovery images. It then applies the patches on the device (or
# emulator) and checks that the resulting file is correct.
EMULATOR_PORT=5580
# set to 0 to use a device instead
USE_EMULATOR=0
# where on the device to do all the patching.
WORK_DIR=/data/local/tmp
START_OTA_PACKAGE=$1
END_OTA_PACKAGE=$2
# ------------------------
tmpdir=$(mktemp -d)
if [ "$USE_EMULATOR" == 1 ]; then
emulator -wipe-data -noaudio -no-window -port $EMULATOR_PORT &
pid_emulator=$!
ADB="adb -s emulator-$EMULATOR_PORT "
else
ADB="adb -d "
fi
echo "waiting to connect to device"
$ADB wait-for-device
# run a command on the device; exit with the exit status of the device
# command.
run_command() {
$ADB shell "$@" \; echo \$? | awk '{if (b) {print a}; a=$0; b=1} END {exit a}'
}
testname() {
echo
echo "$1"...
testname="$1"
}
fail() {
echo
echo FAIL: $testname
echo
[ "$open_pid" == "" ] || kill $open_pid
[ "$pid_emulator" == "" ] || kill $pid_emulator
exit 1
}
sha1() {
sha1sum $1 | awk '{print $1}'
}
size() {
stat -c %s $1 | tr -d '\n'
}
cleanup() {
# not necessary if we're about to kill the emulator, but nice for
# running on real devices or already-running emulators.
testname "removing test files"
run_command rm $WORK_DIR/applypatch
run_command rm $WORK_DIR/source
run_command rm $WORK_DIR/target
run_command rm $WORK_DIR/patch
[ "$pid_emulator" == "" ] || kill $pid_emulator
rm -rf $tmpdir
}
$ADB push $ANDROID_PRODUCT_OUT/system/bin/applypatch $WORK_DIR/applypatch
patch_and_apply() {
local fn=$1
shift
unzip -p $START_OTA_PACKAGE $fn > $tmpdir/source
unzip -p $END_OTA_PACKAGE $fn > $tmpdir/target
imgdiff "$@" $tmpdir/source $tmpdir/target $tmpdir/patch
bsdiff $tmpdir/source $tmpdir/target $tmpdir/patch.bs
echo "patch for $fn is $(size $tmpdir/patch) [of $(size $tmpdir/target)] ($(size $tmpdir/patch.bs) with bsdiff)"
echo "$fn $(size $tmpdir/patch) of $(size $tmpdir/target) bsdiff $(size $tmpdir/patch.bs)" >> /tmp/stats.txt
$ADB push $tmpdir/source $WORK_DIR/source || fail "source push failed"
run_command rm /data/local/tmp/target
$ADB push $tmpdir/patch $WORK_DIR/patch || fail "patch push failed"
run_command /data/local/tmp/applypatch /data/local/tmp/source \
/data/local/tmp/target $(sha1 $tmpdir/target) $(size $tmpdir/target) \
$(sha1 $tmpdir/source):/data/local/tmp/patch \
|| fail "applypatch of $fn failed"
$ADB pull /data/local/tmp/target $tmpdir/result
diff -q $tmpdir/target $tmpdir/result || fail "patch output not correct!"
}
# --------------- basic execution ----------------------
for i in $((zipinfo -1 $START_OTA_PACKAGE; zipinfo -1 $END_OTA_PACKAGE) | \
sort | uniq -d | egrep -e '[.](apk|jar|zip)$'); do
patch_and_apply $i -z
done
patch_and_apply boot.img
patch_and_apply system/recovery.img
# --------------- cleanup ----------------------
cleanup
echo
echo PASS
echo
+254
View File
@@ -0,0 +1,254 @@
/*
* 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.
*/
// See imgdiff.c in this directory for a description of the patch file
// format.
#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include "zlib.h"
#include "mincrypt/sha.h"
#include "applypatch.h"
#include "imgdiff.h"
#include "utils.h"
int CheckPatchHeader(const Value* patch, ssize_t patch_offset)
{
unsigned char* header = (unsigned char*) patch->data + patch_offset;
if (memcmp(header, "BSDIFF40", 8) == 0) {
return 0;
}
// Use delta update mechanism
if (memcmp(header, "IPDIFF10", 8) == 0) {
return 1;
}
fprintf(stderr, "corrupt patch file header (magic number)\n");
return -1;
}
/*
* Apply the patch given in 'patch_filename' to the source data given
* by (old_data, old_size). Write the patched output to the 'output'
* 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) {
ssize_t pos = 12;
char* header = patch->data;
if (patch->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.)
if (memcmp(header, "IMGDIFF2", 8) != 0) {
printf("corrupt patch file header (magic number)\n");
return -1;
}
int num_chunks = Read4(header+8);
int i;
for (i = 0; i < num_chunks; ++i) {
// each chunk's header record starts with 4 bytes.
if (pos + 4 > patch->size) {
printf("failed to read chunk %d record\n", i);
return -1;
}
int type = Read4(patch->data + pos);
pos += 4;
if (type == CHUNK_NORMAL) {
char* normal_header = patch->data + pos;
pos += 24;
if (pos > patch->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);
int patch_type = CheckPatchHeader(patch, patch_offset);
if (patch_type == 0)
ApplyBSDiffPatch(old_data + src_start, src_len,
patch, patch_offset, sink, token, ctx);
else if (patch_type == 1)
ApplyIPDiffPatch(old_data + src_start, src_len,
patch, patch_offset, sink, token, ctx);
} else if (type == CHUNK_RAW) {
char* raw_header = patch->data + pos;
pos += 4;
if (pos > patch->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->size) {
printf("failed to read chunk %d raw data\n", i);
return -1;
}
SHA_update(ctx, patch->data + pos, data_len);
if (sink((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.
char* deflate_header = patch->data + pos;
pos += 60;
if (pos > patch->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);
// Decompress the source data; the chunk header tells us exactly
// how big we expect it to be when decompressed.
unsigned char* expanded_source = malloc(expanded_len);
if (expanded_source == NULL) {
printf("failed to allocate %d bytes for expanded_source\n",
expanded_len);
return -1;
}
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;
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.
if (strm.avail_out != 0) {
printf("source inflation short by %d bytes\n", strm.avail_out);
return -1;
}
inflateEnd(&strm);
// Next, apply the bsdiff patch (in memory) to the uncompressed
// data.
unsigned char* uncompressed_target_data;
ssize_t uncompressed_target_size;
int patch_type = CheckPatchHeader(patch, patch_offset);
if (patch_type == 0) {
if (ApplyBSDiffPatchMem(expanded_source, expanded_len,
patch, patch_offset,
&uncompressed_target_data,
&uncompressed_target_size) != 0) {
return -1;
}
} else /*patch_type == 1*/ {
if (ipth_UpdateOneFile(expanded_source, expanded_len,
patch, patch_offset,
&uncompressed_target_data,
&uncompressed_target_size) != 0) {
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.
unsigned char* temp_data = expanded_source;
ssize_t temp_size = expanded_len;
if (temp_size < 32768) {
// ... unless the buffer is too small, in which case we'll
// allocate a fresh one.
free(temp_data);
temp_data = malloc(32768);
temp_size = 32768;
}
// now the deflate stream
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = uncompressed_target_size;
strm.next_in = uncompressed_target_data;
ret = deflateInit2(&strm, level, method, windowBits, memLevel, strategy);
do {
strm.avail_out = temp_size;
strm.next_out = temp_data;
ret = deflate(&strm, Z_FINISH);
ssize_t have = temp_size - strm.avail_out;
if (sink(temp_data, have, token) != have) {
printf("failed to write %ld compressed bytes to output\n",
(long)have);
return -1;
}
SHA_update(ctx, temp_data, have);
} while (ret != Z_STREAM_END);
deflateEnd(&strm);
free(temp_data);
free(uncompressed_target_data);
} else {
printf("patch chunk %d is unknown type %d\n", i, type);
return -1;
}
}
return 0;
}
+195
View File
@@ -0,0 +1,195 @@
/*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "applypatch.h"
#include "edify/expr.h"
#include "mincrypt/sha.h"
int CheckMode(int argc, char** argv) {
if (argc < 3) {
return 2;
}
return applypatch_check(argv[2], argc-3, argv+3);
}
int SpaceMode(int argc, char** argv) {
if (argc != 3) {
return 2;
}
char* endptr;
size_t bytes = strtol(argv[2], &endptr, 10);
if (bytes == 0 && endptr == argv[2]) {
printf("can't parse \"%s\" as byte count\n\n", argv[2]);
return 1;
}
return CacheSizeCheck(bytes);
}
// Parse arguments (which should be of the form "<sha1>" or
// "<sha1>:<filename>" into the new parallel arrays *sha1s and
// *patches (loading file contents into the patches). Returns 0 on
// success.
static int ParsePatchArgs(int argc, char** argv,
char*** sha1s, Value*** patches, int* num_patches) {
*num_patches = argc;
*sha1s = malloc(*num_patches * sizeof(char*));
*patches = malloc(*num_patches * sizeof(Value*));
memset(*patches, 0, *num_patches * sizeof(Value*));
uint8_t digest[SHA_DIGEST_SIZE];
int i;
for (i = 0; i < *num_patches; ++i) {
char* colon = strchr(argv[i], ':');
if (colon != NULL) {
*colon = '\0';
++colon;
}
if (ParseSha1(argv[i], digest) != 0) {
printf("failed to parse sha1 \"%s\"\n", argv[i]);
return -1;
}
(*sha1s)[i] = argv[i];
if (colon == NULL) {
(*patches)[i] = NULL;
} else {
FileContents fc;
if (LoadFileContents(colon, &fc, RETOUCH_DONT_MASK) != 0) {
goto abort;
}
(*patches)[i] = malloc(sizeof(Value));
(*patches)[i]->type = VAL_BLOB;
(*patches)[i]->size = fc.size;
(*patches)[i]->data = (char*)fc.data;
}
}
return 0;
abort:
for (i = 0; i < *num_patches; ++i) {
Value* p = (*patches)[i];
if (p != NULL) {
free(p->data);
free(p);
}
}
free(*sha1s);
free(*patches);
return -1;
}
int PatchMode(int argc, char** argv) {
if (argc < 6) {
return 2;
}
char* endptr;
size_t target_size = strtol(argv[4], &endptr, 10);
if (target_size == 0 && endptr == argv[4]) {
printf("can't parse \"%s\" as byte count\n\n", argv[4]);
return 1;
}
char** sha1s;
Value** patches;
int num_patches;
if (ParsePatchArgs(argc-5, argv+5, &sha1s, &patches, &num_patches) != 0) {
printf("failed to parse patch args\n");
return 1;
}
int result = applypatch(argv[1], argv[2], argv[3], target_size,
num_patches, sha1s, patches);
int i;
for (i = 0; i < num_patches; ++i) {
Value* p = patches[i];
if (p != NULL) {
free(p->data);
free(p);
}
}
free(sha1s);
free(patches);
return result;
}
// This program applies binary patches to files in a way that is safe
// (the original file is not touched until we have the desired
// replacement for it) and idempotent (it's okay to run this program
// multiple times).
//
// - if the sha1 hash of <tgt-file> is <tgt-sha1>, does nothing and exits
// successfully.
//
// - otherwise, if the sha1 hash of <src-file> is <src-sha1>, applies the
// bsdiff <patch> to <src-file> to produce a new file (the type of patch
// is automatically detected from the file header). If that new
// file has sha1 hash <tgt-sha1>, moves it to replace <tgt-file>, and
// exits successfully. Note that if <src-file> and <tgt-file> are
// not the same, <src-file> is NOT deleted on success. <tgt-file>
// may be the string "-" to mean "the same as src-file".
//
// - 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
// to read the source data. See the comments for the
// LoadMTDContents() function above for the format of such a filename.
int main(int argc, char** argv) {
if (argc < 2) {
usage:
printf(
"usage: %s <src-file> <tgt-file> <tgt-sha1> <tgt-size> "
"[<src-sha1>:<patch> ...]\n"
" or %s -c <file> [<sha1> ...]\n"
" or %s -s <bytes>\n"
" 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",
argv[0], argv[0], argv[0], argv[0]);
return 2;
}
int result;
if (strncmp(argv[1], "-l", 3) == 0) {
result = ShowLicenses();
} else if (strncmp(argv[1], "-c", 3) == 0) {
result = CheckMode(argc, argv);
} else if (strncmp(argv[1], "-s", 3) == 0) {
result = SpaceMode(argc, argv);
} else {
result = PatchMode(argc, argv);
}
if (result == 2) {
goto usage;
}
return result;
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
+65
View File
@@ -0,0 +1,65 @@
/*
* 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.
*/
#include <stdio.h>
#include "utils.h"
/** Write a 4-byte value to f in little-endian order. */
void Write4(int value, FILE* f) {
fputc(value & 0xff, f);
fputc((value >> 8) & 0xff, f);
fputc((value >> 16) & 0xff, f);
fputc((value >> 24) & 0xff, f);
}
/** Write an 8-byte value to f in little-endian order. */
void Write8(long long value, FILE* f) {
fputc(value & 0xff, f);
fputc((value >> 8) & 0xff, f);
fputc((value >> 16) & 0xff, f);
fputc((value >> 24) & 0xff, f);
fputc((value >> 32) & 0xff, f);
fputc((value >> 40) & 0xff, f);
fputc((value >> 48) & 0xff, f);
fputc((value >> 56) & 0xff, f);
}
int Read2(void* pv) {
unsigned char* p = pv;
return (int)(((unsigned int)p[1] << 8) |
(unsigned int)p[0]);
}
int Read4(void* pv) {
unsigned char* p = pv;
return (int)(((unsigned int)p[3] << 24) |
((unsigned int)p[2] << 16) |
((unsigned int)p[1] << 8) |
(unsigned int)p[0]);
}
long long Read8(void* pv) {
unsigned char* p = 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]);
}
+30
View File
@@ -0,0 +1,30 @@
/*
* 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.
*/
#ifndef _BUILD_TOOLS_APPLYPATCH_UTILS_H
#define _BUILD_TOOLS_APPLYPATCH_UTILS_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);
int Read2(void* p);
int Read4(void* p);
long long Read8(void* p);
#endif // _BUILD_TOOLS_APPLYPATCH_UTILS_H