forked from openkylin/platform_build
Merge "relocate applypatch; check patch integrity"
This commit is contained in:
commit
b2ee0fd5c0
|
@ -1,59 +0,0 @@
|
||||||
# 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.
|
|
||||||
|
|
||||||
ifneq ($(TARGET_SIMULATOR),true)
|
|
||||||
|
|
||||||
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_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
|
|
||||||
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_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
|
|
||||||
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)
|
|
||||||
|
|
||||||
endif # !TARGET_SIMULATOR
|
|
|
@ -1,895 +0,0 @@
|
||||||
/*
|
|
||||||
* 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"
|
|
||||||
|
|
||||||
int SaveFileContents(const char* filename, FileContents file);
|
|
||||||
int LoadMTDContents(const char* filename, FileContents* file);
|
|
||||||
int ParseSha1(const char* str, uint8_t* digest);
|
|
||||||
size_t FileSink(unsigned char* data, size_t len, void* token);
|
|
||||||
|
|
||||||
static int mtd_partitions_scanned = 0;
|
|
||||||
|
|
||||||
// Read a file into memory; store it and its associated metadata in
|
|
||||||
// *file. Return 0 on success.
|
|
||||||
int LoadFileContents(const char* filename, FileContents* file) {
|
|
||||||
file->data = NULL;
|
|
||||||
|
|
||||||
// A special 'filename' beginning with "MTD:" means to load the
|
|
||||||
// contents of an MTD partition.
|
|
||||||
if (strncmp(filename, "MTD:", 4) == 0) {
|
|
||||||
return LoadMTDContents(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t bytes_read = fread(file->data, 1, file->size, f);
|
|
||||||
if (bytes_read != file->size) {
|
|
||||||
printf("short read of \"%s\" (%d bytes of %d)\n",
|
|
||||||
filename, bytes_read, file->size);
|
|
||||||
free(file->data);
|
|
||||||
file->data = NULL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the contents of an MTD partition into the provided
|
|
||||||
// FileContents. filename should be a string of the form
|
|
||||||
// "MTD:<partition_name>:<size_1>:<sha1_1>:<size_2>:<sha1_2>:...".
|
|
||||||
// The smallest size_n bytes for which that prefix of the mtd 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 MTD (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.
|
|
||||||
int LoadMTDContents(const char* filename, FileContents* file) {
|
|
||||||
char* copy = strdup(filename);
|
|
||||||
const char* magic = strtok(copy, ":");
|
|
||||||
if (strcmp(magic, "MTD") != 0) {
|
|
||||||
printf("LoadMTDContents 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("LoadMTDContents 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("LoadMTDContents 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);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
MtdReadContext* ctx = mtd_read_partition(mtd);
|
|
||||||
if (ctx == NULL) {
|
|
||||||
printf("failed to initialize read of mtd partition \"%s\"\n",
|
|
||||||
partition);
|
|
||||||
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) {
|
|
||||||
read = mtd_read_data(ctx, p, next);
|
|
||||||
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("mtd read matched size %d sha %s\n",
|
|
||||||
size[index[i]], sha1sum[index[i]]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
p += read;
|
|
||||||
}
|
|
||||||
|
|
||||||
mtd_read_close(ctx);
|
|
||||||
|
|
||||||
if (i == pairs) {
|
|
||||||
// Ran off the end of the list of (size,sha1) pairs without
|
|
||||||
// finding a match.
|
|
||||||
printf("contents of MTD 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t bytes_written = FileSink(file.data, file.size, &fd);
|
|
||||||
if (bytes_written != file.size) {
|
|
||||||
printf("short write of \"%s\" (%d bytes of %d) (%s)\n",
|
|
||||||
filename, bytes_written, 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_mtd partition, a string of the form
|
|
||||||
// "MTD:<partition>[:...]". Return 0 on success.
|
|
||||||
int WriteToMTDPartition(unsigned char* data, size_t len,
|
|
||||||
const char* target_mtd) {
|
|
||||||
char* partition = strchr(target_mtd, ':');
|
|
||||||
if (partition == NULL) {
|
|
||||||
printf("bad MTD target name \"%s\"\n", target_mtd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
++partition;
|
|
||||||
// Trim off anything after a colon, eg "MTD:boot:blah:blah:blah...".
|
|
||||||
// We want just the partition name "boot".
|
|
||||||
partition = strdup(partition);
|
|
||||||
char* end = strchr(partition, ':');
|
|
||||||
if (end != NULL)
|
|
||||||
*end = '\0';
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(partition);
|
|
||||||
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' && *ps != ':') return -1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse arguments (which should be of the form "<sha1>" or
|
|
||||||
// "<sha1>:<filename>" into the array *patches, returning the number
|
|
||||||
// of Patch objects in *num_patches. Return 0 on success.
|
|
||||||
int ParseShaArgs(int argc, char** argv, Patch** patches, int* num_patches) {
|
|
||||||
*num_patches = argc;
|
|
||||||
*patches = malloc(*num_patches * sizeof(Patch));
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < *num_patches; ++i) {
|
|
||||||
if (ParseSha1(argv[i], (*patches)[i].sha1) != 0) {
|
|
||||||
printf("failed to parse sha1 \"%s\"\n", argv[i]);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (argv[i][SHA_DIGEST_SIZE*2] == '\0') {
|
|
||||||
(*patches)[i].patch_filename = NULL;
|
|
||||||
} else if (argv[i][SHA_DIGEST_SIZE*2] == ':') {
|
|
||||||
(*patches)[i].patch_filename = argv[i] + (SHA_DIGEST_SIZE*2+1);
|
|
||||||
} else {
|
|
||||||
printf("failed to parse filename \"%s\"\n", argv[i]);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search an array of Patch objects for one matching the given sha1.
|
|
||||||
// Return the Patch object on success, or NULL if no match is found.
|
|
||||||
const Patch* FindMatchingPatch(uint8_t* sha1, Patch* patches, int num_patches) {
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < num_patches; ++i) {
|
|
||||||
if (memcmp(patches[i].sha1, sha1, SHA_DIGEST_SIZE) == 0) {
|
|
||||||
return patches+i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 CheckMode(int argc, char** argv) {
|
|
||||||
if (argc < 3) {
|
|
||||||
printf("no filename given\n");
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
int num_patches;
|
|
||||||
Patch* patches;
|
|
||||||
if (ParseShaArgs(argc-3, argv+3, &patches, &num_patches) != 0) { return 1; }
|
|
||||||
|
|
||||||
FileContents file;
|
|
||||||
file.data = NULL;
|
|
||||||
|
|
||||||
// It's okay to specify no sha1s; the check will pass if the
|
|
||||||
// LoadFileContents is successful. (Useful for reading MTD
|
|
||||||
// partitions, where the filename encodes the sha1s; no need to
|
|
||||||
// check them twice.)
|
|
||||||
if (LoadFileContents(argv[2], &file) != 0 ||
|
|
||||||
(num_patches > 0 &&
|
|
||||||
FindMatchingPatch(file.sha1, patches, num_patches) == NULL)) {
|
|
||||||
printf("file \"%s\" doesn't have any of expected "
|
|
||||||
"sha1 sums; checking cache\n", argv[2]);
|
|
||||||
|
|
||||||
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) != 0) {
|
|
||||||
printf("failed to load cache file\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FindMatchingPatch(file.sha1, patches, num_patches) == NULL) {
|
|
||||||
printf("cache bits don't match any sha1 for \"%s\"\n",
|
|
||||||
argv[2]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(file.data);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ShowLicenses() {
|
|
||||||
ShowBSDiffLicense();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t FileSink(unsigned char* data, size_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;
|
|
||||||
}
|
|
||||||
printf("wrote %d bytes to output\n", (int)done);
|
|
||||||
return done;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
unsigned char* buffer;
|
|
||||||
size_t size;
|
|
||||||
size_t pos;
|
|
||||||
} MemorySinkInfo;
|
|
||||||
|
|
||||||
size_t MemorySink(unsigned char* data, size_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// As you might guess from the arguments, this function used to be
|
|
||||||
// main(); it was split out this way so applypatch could be built as a
|
|
||||||
// static library and linked into other executables as well. In the
|
|
||||||
// future only the library form will exist; we will not need to build
|
|
||||||
// this as a standalone executable.
|
|
||||||
//
|
|
||||||
// The arguments to this function are just the command-line of the
|
|
||||||
// standalone executable:
|
|
||||||
//
|
|
||||||
// <src-file> <tgt-file> <tgt-sha1> <tgt-size> [<src-sha1>:<patch> ...]
|
|
||||||
// to apply a patch. Returns 0 on success, 1 on failure.
|
|
||||||
//
|
|
||||||
// "-c" <file> [<sha1> ...]
|
|
||||||
// to check a file's contents against zero or more sha1s. Returns
|
|
||||||
// 0 if it matches any of them, 1 if it doesn't.
|
|
||||||
//
|
|
||||||
// "-s" <bytes>
|
|
||||||
// returns 0 if enough free space is available on /cache; 1 if it
|
|
||||||
// does not.
|
|
||||||
//
|
|
||||||
// "-l"
|
|
||||||
// shows open-source license information and returns 0.
|
|
||||||
//
|
|
||||||
// This function returns 2 if the arguments are not understood (in the
|
|
||||||
// standalone executable, this causes the usage message to be
|
|
||||||
// printed).
|
|
||||||
//
|
|
||||||
// TODO: make the interface more sensible for use as a library.
|
|
||||||
|
|
||||||
int applypatch(int argc, char** argv) {
|
|
||||||
if (argc < 2) {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strncmp(argv[1], "-l", 3) == 0) {
|
|
||||||
return ShowLicenses();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strncmp(argv[1], "-c", 3) == 0) {
|
|
||||||
return CheckMode(argc, argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strncmp(argv[1], "-s", 3) == 0) {
|
|
||||||
if (argc != 3) {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
size_t bytes = strtol(argv[2], NULL, 10);
|
|
||||||
if (MakeFreeSpaceOnCache(bytes) < 0) {
|
|
||||||
printf("unable to make %ld bytes available on /cache\n", (long)bytes);
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t target_sha1[SHA_DIGEST_SIZE];
|
|
||||||
|
|
||||||
const char* source_filename = argv[1];
|
|
||||||
const char* target_filename = argv[2];
|
|
||||||
if (target_filename[0] == '-' &&
|
|
||||||
target_filename[1] == '\0') {
|
|
||||||
target_filename = source_filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\napplying patch to %s\n", source_filename);
|
|
||||||
|
|
||||||
if (ParseSha1(argv[3], target_sha1) != 0) {
|
|
||||||
printf("failed to parse tgt-sha1 \"%s\"\n", argv[3]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long target_size = strtoul(argv[4], NULL, 0);
|
|
||||||
|
|
||||||
int num_patches;
|
|
||||||
Patch* patches;
|
|
||||||
if (ParseShaArgs(argc-5, argv+5, &patches, &num_patches) < 0) { return 1; }
|
|
||||||
|
|
||||||
FileContents copy_file;
|
|
||||||
FileContents source_file;
|
|
||||||
const char* source_patch_filename = NULL;
|
|
||||||
const char* copy_patch_filename = NULL;
|
|
||||||
int made_copy = 0;
|
|
||||||
|
|
||||||
// We try to load the target file into the source_file object.
|
|
||||||
if (LoadFileContents(target_filename, &source_file) == 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (source_file.data != NULL) {
|
|
||||||
const Patch* to_use =
|
|
||||||
FindMatchingPatch(source_file.sha1, patches, num_patches);
|
|
||||||
if (to_use != NULL) {
|
|
||||||
source_patch_filename = to_use->patch_filename;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (source_patch_filename == NULL) {
|
|
||||||
free(source_file.data);
|
|
||||||
printf("source file is bad; trying copy\n");
|
|
||||||
|
|
||||||
if (LoadFileContents(CACHE_TEMP_SOURCE, ©_file) < 0) {
|
|
||||||
// fail.
|
|
||||||
printf("failed to read copy file\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Patch* to_use =
|
|
||||||
FindMatchingPatch(copy_file.sha1, patches, num_patches);
|
|
||||||
if (to_use != NULL) {
|
|
||||||
copy_patch_filename = to_use->patch_filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (copy_patch_filename == 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) {
|
|
||||||
// If the target is an MTD 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 MTD
|
|
||||||
// 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);
|
|
||||||
int enough_space =
|
|
||||||
(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_filename != 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) {
|
|
||||||
// It's impossible to free space on the target filesystem by
|
|
||||||
// deleting the source if the source is an MTD 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 MTD\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 char* patch_filename;
|
|
||||||
if (source_patch_filename != NULL) {
|
|
||||||
source_to_use = &source_file;
|
|
||||||
patch_filename = source_patch_filename;
|
|
||||||
} else {
|
|
||||||
source_to_use = ©_file;
|
|
||||||
patch_filename = copy_patch_filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
SinkFn sink = NULL;
|
|
||||||
void* token = NULL;
|
|
||||||
output = -1;
|
|
||||||
outname = NULL;
|
|
||||||
if (strncmp(target_filename, "MTD:", 4) == 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MAX_HEADER_LENGTH 8
|
|
||||||
unsigned char header[MAX_HEADER_LENGTH];
|
|
||||||
FILE* patchf = fopen(patch_filename, "rb");
|
|
||||||
if (patchf == NULL) {
|
|
||||||
printf("failed to open patch file %s: %s\n",
|
|
||||||
patch_filename, strerror(errno));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
int header_bytes_read = fread(header, 1, MAX_HEADER_LENGTH, patchf);
|
|
||||||
fclose(patchf);
|
|
||||||
|
|
||||||
SHA_init(&ctx);
|
|
||||||
|
|
||||||
int result;
|
|
||||||
|
|
||||||
if (header_bytes_read >= 4 &&
|
|
||||||
header[0] == 0xd6 && header[1] == 0xc3 &&
|
|
||||||
header[2] == 0xc4 && header[3] == 0) {
|
|
||||||
// xdelta3 patches begin "VCD" (with the high bits set) followed
|
|
||||||
// by a zero byte (the version number).
|
|
||||||
printf("error: xdelta3 patches no longer supported\n");
|
|
||||||
return 1;
|
|
||||||
} else if (header_bytes_read >= 8 &&
|
|
||||||
memcmp(header, "BSDIFF40", 8) == 0) {
|
|
||||||
result = ApplyBSDiffPatch(source_to_use->data, source_to_use->size,
|
|
||||||
patch_filename, 0, sink, token, &ctx);
|
|
||||||
} else if (header_bytes_read >= 8 &&
|
|
||||||
memcmp(header, "IMGDIFF", 7) == 0 &&
|
|
||||||
(header[7] == '1' || header[7] == '2')) {
|
|
||||||
result = ApplyImagePatch(source_to_use->data, source_to_use->size,
|
|
||||||
patch_filename, 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 MTD partition.
|
|
||||||
if (WriteToMTDPartition(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;
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
/*
|
|
||||||
* 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"
|
|
||||||
|
|
||||||
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;
|
|
||||||
size_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 size_t (*SinkFn)(unsigned char*, size_t, void*);
|
|
||||||
|
|
||||||
// applypatch.c
|
|
||||||
size_t FreeSpaceForFile(const char* filename);
|
|
||||||
int applypatch(int argc, char** argv);
|
|
||||||
|
|
||||||
// bsdiff.c
|
|
||||||
void ShowBSDiffLicense();
|
|
||||||
int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
|
|
||||||
const char* patch_filename, ssize_t offset,
|
|
||||||
SinkFn sink, void* token, SHA_CTX* ctx);
|
|
||||||
int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
|
|
||||||
const char* patch_filename, 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 char* patch_filename,
|
|
||||||
SinkFn sink, void* token, SHA_CTX* ctx);
|
|
||||||
|
|
||||||
// freecache.c
|
|
||||||
int MakeFreeSpaceOnCache(size_t bytes_needed);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,345 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# A test suite for applypatch. Run in a client where you have done
|
|
||||||
# envsetup, choosecombo, etc.
|
|
||||||
#
|
|
||||||
# DO NOT RUN THIS ON A DEVICE YOU CARE ABOUT. It will mess up your
|
|
||||||
# system partition.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# TODO: find some way to get this run regularly along with the rest of
|
|
||||||
# the tests.
|
|
||||||
|
|
||||||
EMULATOR_PORT=5580
|
|
||||||
DATA_DIR=$ANDROID_BUILD_TOP/build/tools/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/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
|
|
||||||
|
|
||||||
rm -rf $tmpdir
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup
|
|
||||||
|
|
||||||
$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
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
|
@ -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 *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;
|
|
||||||
}
|
|
|
@ -1,252 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
|
|
||||||
const char* patch_filename, 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_filename, patch_offset,
|
|
||||||
&new_data, &new_size) != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sink(new_data, new_size, token) < new_size) {
|
|
||||||
fprintf(stderr, "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 char* patch_filename, ssize_t patch_offset,
|
|
||||||
unsigned char** new_data, ssize_t* new_size) {
|
|
||||||
|
|
||||||
FILE* f;
|
|
||||||
if ((f = fopen(patch_filename, "rb")) == NULL) {
|
|
||||||
fprintf(stderr, "failed to open patch file\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// File 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".
|
|
||||||
|
|
||||||
fseek(f, patch_offset, SEEK_SET);
|
|
||||||
|
|
||||||
unsigned char header[32];
|
|
||||||
if (fread(header, 1, 32, f) < 32) {
|
|
||||||
fprintf(stderr, "failed to read patch file header\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (memcmp(header, "BSDIFF40", 8) != 0) {
|
|
||||||
fprintf(stderr, "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) {
|
|
||||||
fprintf(stderr, "corrupt patch file header (data lengths)\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
int bzerr;
|
|
||||||
|
|
||||||
#define OPEN_AT(f, bzf, offset) \
|
|
||||||
FILE* f; \
|
|
||||||
BZFILE* bzf; \
|
|
||||||
if ((f = fopen(patch_filename, "rb")) == NULL) { \
|
|
||||||
fprintf(stderr, "failed to open patch file\n"); \
|
|
||||||
return 1; \
|
|
||||||
} \
|
|
||||||
if (fseeko(f, offset+patch_offset, SEEK_SET)) { \
|
|
||||||
fprintf(stderr, "failed to seek in patch file\n"); \
|
|
||||||
return 1; \
|
|
||||||
} \
|
|
||||||
if ((bzf = BZ2_bzReadOpen(&bzerr, f, 0, 0, NULL, 0)) == NULL) { \
|
|
||||||
fprintf(stderr, "failed to bzReadOpen in patch file (%d)\n", bzerr); \
|
|
||||||
return 1; \
|
|
||||||
}
|
|
||||||
|
|
||||||
OPEN_AT(cpf, cpfbz2, 32);
|
|
||||||
OPEN_AT(dpf, dpfbz2, 32+ctrl_len);
|
|
||||||
OPEN_AT(epf, epfbz2, 32+ctrl_len+data_len);
|
|
||||||
|
|
||||||
#undef OPEN_AT
|
|
||||||
|
|
||||||
*new_data = malloc(*new_size);
|
|
||||||
if (*new_data == NULL) {
|
|
||||||
fprintf(stderr, "failed to allocate %d bytes of memory for output file\n",
|
|
||||||
(int)*new_size);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
off_t oldpos = 0, newpos = 0;
|
|
||||||
off_t ctrl[3];
|
|
||||||
off_t len_read;
|
|
||||||
int i;
|
|
||||||
unsigned char buf[8];
|
|
||||||
while (newpos < *new_size) {
|
|
||||||
// Read control data
|
|
||||||
for (i = 0; i < 3; ++i) {
|
|
||||||
len_read = BZ2_bzRead(&bzerr, cpfbz2, buf, 8);
|
|
||||||
if (len_read < 8 || !(bzerr == BZ_OK || bzerr == BZ_STREAM_END)) {
|
|
||||||
fprintf(stderr, "corrupt patch (read control)\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
ctrl[i] = offtin(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanity check
|
|
||||||
if (newpos + ctrl[0] > *new_size) {
|
|
||||||
fprintf(stderr, "corrupt patch (new file overrun)\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read diff string
|
|
||||||
len_read = BZ2_bzRead(&bzerr, dpfbz2, *new_data + newpos, ctrl[0]);
|
|
||||||
if (len_read < ctrl[0] || !(bzerr == BZ_OK || bzerr == BZ_STREAM_END)) {
|
|
||||||
fprintf(stderr, "corrupt patch (read diff)\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) {
|
|
||||||
fprintf(stderr, "corrupt patch (new file overrun)\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read extra string
|
|
||||||
len_read = BZ2_bzRead(&bzerr, epfbz2, *new_data + newpos, ctrl[1]);
|
|
||||||
if (len_read < ctrl[1] || !(bzerr == BZ_OK || bzerr == BZ_STREAM_END)) {
|
|
||||||
fprintf(stderr, "corrupt patch (read extra)\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adjust pointers
|
|
||||||
newpos += ctrl[1];
|
|
||||||
oldpos += ctrl[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
BZ2_bzReadClose(&bzerr, cpfbz2);
|
|
||||||
BZ2_bzReadClose(&bzerr, dpfbz2);
|
|
||||||
BZ2_bzReadClose(&bzerr, epfbz2);
|
|
||||||
fclose(cpf);
|
|
||||||
fclose(dpf);
|
|
||||||
fclose(epf);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,172 +0,0 @@
|
||||||
#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
|
@ -1,30 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 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
|
|
|
@ -1,118 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# A script for testing imgdiff/applypatch. It takes two full OTA
|
|
||||||
# packages as arguments. It generates (on the host) patches for all
|
|
||||||
# the zip/jar/apk files they have in common, as well as boot and
|
|
||||||
# recovery images. It then applies the patches on the device (or
|
|
||||||
# emulator) and checks that the resulting file is correct.
|
|
||||||
|
|
||||||
EMULATOR_PORT=5580
|
|
||||||
|
|
||||||
# set to 0 to use a device instead
|
|
||||||
USE_EMULATOR=0
|
|
||||||
|
|
||||||
# where on the device to do all the patching.
|
|
||||||
WORK_DIR=/data/local/tmp
|
|
||||||
|
|
||||||
START_OTA_PACKAGE=$1
|
|
||||||
END_OTA_PACKAGE=$2
|
|
||||||
|
|
||||||
# ------------------------
|
|
||||||
|
|
||||||
tmpdir=$(mktemp -d)
|
|
||||||
|
|
||||||
if [ "$USE_EMULATOR" == 1 ]; then
|
|
||||||
emulator -wipe-data -noaudio -no-window -port $EMULATOR_PORT &
|
|
||||||
pid_emulator=$!
|
|
||||||
ADB="adb -s emulator-$EMULATOR_PORT "
|
|
||||||
else
|
|
||||||
ADB="adb -d "
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "waiting to connect to device"
|
|
||||||
$ADB wait-for-device
|
|
||||||
|
|
||||||
# run a command on the device; exit with the exit status of the device
|
|
||||||
# command.
|
|
||||||
run_command() {
|
|
||||||
$ADB shell "$@" \; echo \$? | awk '{if (b) {print a}; a=$0; b=1} END {exit a}'
|
|
||||||
}
|
|
||||||
|
|
||||||
testname() {
|
|
||||||
echo
|
|
||||||
echo "$1"...
|
|
||||||
testname="$1"
|
|
||||||
}
|
|
||||||
|
|
||||||
fail() {
|
|
||||||
echo
|
|
||||||
echo FAIL: $testname
|
|
||||||
echo
|
|
||||||
[ "$open_pid" == "" ] || kill $open_pid
|
|
||||||
[ "$pid_emulator" == "" ] || kill $pid_emulator
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
sha1() {
|
|
||||||
sha1sum $1 | awk '{print $1}'
|
|
||||||
}
|
|
||||||
|
|
||||||
size() {
|
|
||||||
stat -c %s $1 | tr -d '\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup() {
|
|
||||||
# not necessary if we're about to kill the emulator, but nice for
|
|
||||||
# running on real devices or already-running emulators.
|
|
||||||
testname "removing test files"
|
|
||||||
run_command rm $WORK_DIR/applypatch
|
|
||||||
run_command rm $WORK_DIR/source
|
|
||||||
run_command rm $WORK_DIR/target
|
|
||||||
run_command rm $WORK_DIR/patch
|
|
||||||
|
|
||||||
[ "$pid_emulator" == "" ] || kill $pid_emulator
|
|
||||||
|
|
||||||
rm -rf $tmpdir
|
|
||||||
}
|
|
||||||
|
|
||||||
$ADB push $ANDROID_PRODUCT_OUT/system/bin/applypatch $WORK_DIR/applypatch
|
|
||||||
|
|
||||||
patch_and_apply() {
|
|
||||||
local fn=$1
|
|
||||||
shift
|
|
||||||
|
|
||||||
unzip -p $START_OTA_PACKAGE $fn > $tmpdir/source
|
|
||||||
unzip -p $END_OTA_PACKAGE $fn > $tmpdir/target
|
|
||||||
imgdiff "$@" $tmpdir/source $tmpdir/target $tmpdir/patch
|
|
||||||
bsdiff $tmpdir/source $tmpdir/target $tmpdir/patch.bs
|
|
||||||
echo "patch for $fn is $(size $tmpdir/patch) [of $(size $tmpdir/target)] ($(size $tmpdir/patch.bs) with bsdiff)"
|
|
||||||
echo "$fn $(size $tmpdir/patch) of $(size $tmpdir/target) bsdiff $(size $tmpdir/patch.bs)" >> /tmp/stats.txt
|
|
||||||
$ADB push $tmpdir/source $WORK_DIR/source || fail "source push failed"
|
|
||||||
run_command rm /data/local/tmp/target
|
|
||||||
$ADB push $tmpdir/patch $WORK_DIR/patch || fail "patch push failed"
|
|
||||||
run_command /data/local/tmp/applypatch /data/local/tmp/source \
|
|
||||||
/data/local/tmp/target $(sha1 $tmpdir/target) $(size $tmpdir/target) \
|
|
||||||
$(sha1 $tmpdir/source):/data/local/tmp/patch \
|
|
||||||
|| fail "applypatch of $fn failed"
|
|
||||||
$ADB pull /data/local/tmp/target $tmpdir/result
|
|
||||||
diff -q $tmpdir/target $tmpdir/result || fail "patch output not correct!"
|
|
||||||
}
|
|
||||||
|
|
||||||
# --------------- basic execution ----------------------
|
|
||||||
|
|
||||||
for i in $((zipinfo -1 $START_OTA_PACKAGE; zipinfo -1 $END_OTA_PACKAGE) | \
|
|
||||||
sort | uniq -d | egrep -e '[.](apk|jar|zip)$'); do
|
|
||||||
patch_and_apply $i -z
|
|
||||||
done
|
|
||||||
patch_and_apply boot.img
|
|
||||||
patch_and_apply system/recovery.img
|
|
||||||
|
|
||||||
|
|
||||||
# --------------- cleanup ----------------------
|
|
||||||
|
|
||||||
cleanup
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo PASS
|
|
||||||
echo
|
|
||||||
|
|
|
@ -1,364 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 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"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 char* patch_filename,
|
|
||||||
SinkFn sink, void* token, SHA_CTX* ctx) {
|
|
||||||
FILE* f;
|
|
||||||
if ((f = fopen(patch_filename, "rb")) == NULL) {
|
|
||||||
printf("failed to open patch file\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char header[12];
|
|
||||||
if (fread(header, 1, 12, f) != 12) {
|
|
||||||
printf("failed to read patch file header\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// IMGDIFF1 uses CHUNK_NORMAL and CHUNK_GZIP.
|
|
||||||
// IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW.
|
|
||||||
if (memcmp(header, "IMGDIFF", 7) != 0 ||
|
|
||||||
(header[7] != '1' && header[7] != '2')) {
|
|
||||||
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.
|
|
||||||
unsigned char chunk[4];
|
|
||||||
if (fread(chunk, 1, 4, f) != 4) {
|
|
||||||
printf("failed to read chunk %d record\n", i);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int type = Read4(chunk);
|
|
||||||
|
|
||||||
if (type == CHUNK_NORMAL) {
|
|
||||||
unsigned char normal_header[24];
|
|
||||||
if (fread(normal_header, 1, 24, f) != 24) {
|
|
||||||
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);
|
|
||||||
|
|
||||||
printf("CHUNK %d: normal patch offset %d\n", i, patch_offset);
|
|
||||||
|
|
||||||
ApplyBSDiffPatch(old_data + src_start, src_len,
|
|
||||||
patch_filename, patch_offset,
|
|
||||||
sink, token, ctx);
|
|
||||||
} else if (type == CHUNK_GZIP) {
|
|
||||||
// This branch is basically a duplicate of the CHUNK_DEFLATE
|
|
||||||
// branch, with a bit of extra processing for the gzip header
|
|
||||||
// and footer. I've avoided factoring the common code out since
|
|
||||||
// this branch will just be deleted when we drop support for
|
|
||||||
// IMGDIFF1.
|
|
||||||
|
|
||||||
// gzip chunks have an additional 64 + gzip_header_len + 8 bytes
|
|
||||||
// in their chunk header.
|
|
||||||
unsigned char* gzip = malloc(64);
|
|
||||||
if (fread(gzip, 1, 64, f) != 64) {
|
|
||||||
printf("failed to read chunk %d initial gzip header data\n",
|
|
||||||
i);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
size_t gzip_header_len = Read4(gzip+60);
|
|
||||||
gzip = realloc(gzip, 64 + gzip_header_len + 8);
|
|
||||||
if (fread(gzip+64, 1, gzip_header_len+8, f) != gzip_header_len+8) {
|
|
||||||
printf("failed to read chunk %d remaining gzip header data\n",
|
|
||||||
i);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t src_start = Read8(gzip);
|
|
||||||
size_t src_len = Read8(gzip+8);
|
|
||||||
size_t patch_offset = Read8(gzip+16);
|
|
||||||
|
|
||||||
size_t expanded_len = Read8(gzip+24);
|
|
||||||
size_t target_len = Read8(gzip+32);
|
|
||||||
int gz_level = Read4(gzip+40);
|
|
||||||
int gz_method = Read4(gzip+44);
|
|
||||||
int gz_windowBits = Read4(gzip+48);
|
|
||||||
int gz_memLevel = Read4(gzip+52);
|
|
||||||
int gz_strategy = Read4(gzip+56);
|
|
||||||
|
|
||||||
printf("CHUNK %d: gzip patch offset %d\n", i, patch_offset);
|
|
||||||
|
|
||||||
// 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 - (gzip_header_len + 8);
|
|
||||||
strm.next_in = (unsigned char*)(old_data + src_start + gzip_header_len);
|
|
||||||
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;
|
|
||||||
if (ApplyBSDiffPatchMem(expanded_source, expanded_len,
|
|
||||||
patch_filename, patch_offset,
|
|
||||||
&uncompressed_target_data,
|
|
||||||
&uncompressed_target_size) != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now compress the target data and append it to the output.
|
|
||||||
|
|
||||||
// start with the gzip header.
|
|
||||||
sink(gzip+64, gzip_header_len, token);
|
|
||||||
SHA_update(ctx, gzip+64, gzip_header_len);
|
|
||||||
|
|
||||||
// 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, gz_level, gz_method, gz_windowBits,
|
|
||||||
gz_memLevel, gz_strategy);
|
|
||||||
do {
|
|
||||||
strm.avail_out = temp_size;
|
|
||||||
strm.next_out = temp_data;
|
|
||||||
ret = deflate(&strm, Z_FINISH);
|
|
||||||
size_t have = temp_size - strm.avail_out;
|
|
||||||
|
|
||||||
if (sink(temp_data, have, token) != have) {
|
|
||||||
printf("failed to write %d compressed bytes to output\n",
|
|
||||||
have);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
SHA_update(ctx, temp_data, have);
|
|
||||||
} while (ret != Z_STREAM_END);
|
|
||||||
deflateEnd(&strm);
|
|
||||||
|
|
||||||
// lastly, the gzip footer.
|
|
||||||
sink(gzip+64+gzip_header_len, 8, token);
|
|
||||||
SHA_update(ctx, gzip+64+gzip_header_len, 8);
|
|
||||||
|
|
||||||
free(temp_data);
|
|
||||||
free(uncompressed_target_data);
|
|
||||||
free(gzip);
|
|
||||||
} else if (type == CHUNK_RAW) {
|
|
||||||
unsigned char raw_header[4];
|
|
||||||
if (fread(raw_header, 1, 4, f) != 4) {
|
|
||||||
printf("failed to read chunk %d raw header data\n", i);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t data_len = Read4(raw_header);
|
|
||||||
|
|
||||||
printf("CHUNK %d: raw data %d\n", i, data_len);
|
|
||||||
|
|
||||||
unsigned char* temp = malloc(data_len);
|
|
||||||
if (fread(temp, 1, data_len, f) != data_len) {
|
|
||||||
printf("failed to read chunk %d raw data\n", i);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
SHA_update(ctx, temp, data_len);
|
|
||||||
if (sink(temp, data_len, token) != data_len) {
|
|
||||||
printf("failed to write chunk %d raw data\n", i);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else if (type == CHUNK_DEFLATE) {
|
|
||||||
// deflate chunks have an additional 60 bytes in their chunk header.
|
|
||||||
unsigned char deflate_header[60];
|
|
||||||
if (fread(deflate_header, 1, 60, f) != 60) {
|
|
||||||
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);
|
|
||||||
|
|
||||||
printf("CHUNK %d: deflate patch offset %d\n", i, patch_offset);
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
if (ApplyBSDiffPatchMem(expanded_source, expanded_len,
|
|
||||||
patch_filename, 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);
|
|
||||||
size_t have = temp_size - strm.avail_out;
|
|
||||||
|
|
||||||
if (sink(temp_data, have, token) != have) {
|
|
||||||
printf("failed to write %d compressed bytes to output\n",
|
|
||||||
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;
|
|
||||||
}
|
|
|
@ -1,60 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
extern int applypatch(int argc, char** argv);
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
int result = applypatch(argc, argv);
|
|
||||||
if (result == 2) {
|
|
||||||
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 result;
|
|
||||||
}
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,62 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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(unsigned char* p) {
|
|
||||||
return (int)(((unsigned int)p[1] << 8) |
|
|
||||||
(unsigned int)p[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Read4(unsigned char* p) {
|
|
||||||
return (int)(((unsigned int)p[3] << 24) |
|
|
||||||
((unsigned int)p[2] << 16) |
|
|
||||||
((unsigned int)p[1] << 8) |
|
|
||||||
(unsigned int)p[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
long long Read8(unsigned char* p) {
|
|
||||||
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]);
|
|
||||||
}
|
|
|
@ -1,30 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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(unsigned char* p);
|
|
||||||
int Read4(unsigned char* p);
|
|
||||||
long long Read8(unsigned char* p);
|
|
||||||
|
|
||||||
#endif // _BUILD_TOOLS_APPLYPATCH_UTILS_H
|
|
|
@ -113,7 +113,7 @@ class EdifyGenerator(object):
|
||||||
def PatchCheck(self, filename, *sha1):
|
def PatchCheck(self, filename, *sha1):
|
||||||
"""Check that the given file (or MTD reference) has one of the
|
"""Check that the given file (or MTD reference) has one of the
|
||||||
given *sha1 hashes."""
|
given *sha1 hashes."""
|
||||||
self.script.append('assert(apply_patch_check("%s"' % (filename,) +
|
self.script.append('assert(sha1_check(read_file("%s")' % (filename,) +
|
||||||
"".join([', "%s"' % (i,) for i in sha1]) +
|
"".join([', "%s"' % (i,) for i in sha1]) +
|
||||||
'));')
|
'));')
|
||||||
|
|
||||||
|
|
|
@ -627,7 +627,7 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
||||||
verbatim_targets.append((tf.name, tf.size))
|
verbatim_targets.append((tf.name, tf.size))
|
||||||
else:
|
else:
|
||||||
common.ZipWriteStr(output_zip, "patch/" + tf.name + ".p", d)
|
common.ZipWriteStr(output_zip, "patch/" + tf.name + ".p", d)
|
||||||
patch_list.append((tf.name, tf, sf, tf.size))
|
patch_list.append((tf.name, tf, sf, tf.size, sha.sha(d).hexdigest()))
|
||||||
largest_source_size = max(largest_source_size, sf.size)
|
largest_source_size = max(largest_source_size, sf.size)
|
||||||
|
|
||||||
source_fp = GetBuildProp("ro.build.fingerprint", source_zip)
|
source_fp = GetBuildProp("ro.build.fingerprint", source_zip)
|
||||||
|
@ -669,7 +669,7 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
||||||
total_verify_size += source_boot.size
|
total_verify_size += source_boot.size
|
||||||
so_far = 0
|
so_far = 0
|
||||||
|
|
||||||
for fn, tf, sf, size in patch_list:
|
for fn, tf, sf, size, patch_sha in patch_list:
|
||||||
script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
|
script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
|
||||||
so_far += sf.size
|
so_far += sf.size
|
||||||
script.SetProgress(so_far / total_verify_size)
|
script.SetProgress(so_far / total_verify_size)
|
||||||
|
@ -693,6 +693,9 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
||||||
script.Print("Unpacking patches...")
|
script.Print("Unpacking patches...")
|
||||||
script.UnpackPackageDir("patch", "/tmp/patchtmp")
|
script.UnpackPackageDir("patch", "/tmp/patchtmp")
|
||||||
|
|
||||||
|
for fn, tf, sf, size, patch_sha in patch_list:
|
||||||
|
script.PatchCheck("/tmp/patchtmp/" + tf.name + ".p", patch_sha)
|
||||||
|
|
||||||
device_specific.IncrementalOTA_VerifyEnd()
|
device_specific.IncrementalOTA_VerifyEnd()
|
||||||
|
|
||||||
script.Comment("---- start making changes here ----")
|
script.Comment("---- start making changes here ----")
|
||||||
|
@ -714,7 +717,7 @@ def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
|
||||||
so_far = 0
|
so_far = 0
|
||||||
|
|
||||||
script.Print("Patching system files...")
|
script.Print("Patching system files...")
|
||||||
for fn, tf, sf, size in patch_list:
|
for fn, tf, sf, size, _ in patch_list:
|
||||||
script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1,
|
script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1,
|
||||||
sf.sha1, "/tmp/patchtmp/"+fn+".p")
|
sf.sha1, "/tmp/patchtmp/"+fn+".p")
|
||||||
so_far += tf.size
|
so_far += tf.size
|
||||||
|
|
Loading…
Reference in New Issue