summaryrefslogtreecommitdiffstats
path: root/applypatch
diff options
context:
space:
mode:
Diffstat (limited to 'applypatch')
-rw-r--r--applypatch/Android.mk2
-rw-r--r--applypatch/applypatch.c1359
-rw-r--r--applypatch/applypatch.h22
-rwxr-xr-xapplypatch/applypatch.sh11
-rw-r--r--applypatch/bspatch.c372
-rw-r--r--applypatch/imgpatch.c485
-rw-r--r--applypatch/main.c147
-rw-r--r--applypatch/utils.c37
-rw-r--r--applypatch/utils.h6
9 files changed, 1185 insertions, 1256 deletions
diff --git a/applypatch/Android.mk b/applypatch/Android.mk
index d20d6c8f1..bb024f669 100644
--- a/applypatch/Android.mk
+++ b/applypatch/Android.mk
@@ -29,6 +29,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES := main.c
LOCAL_MODULE := applypatch
+LOCAL_C_INCLUDES += bootable/recovery
LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
LOCAL_SHARED_LIBRARIES += libz libcutils libstdc++ libc
@@ -40,6 +41,7 @@ LOCAL_SRC_FILES := main.c
LOCAL_MODULE := applypatch_static
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_TAGS := eng
+LOCAL_C_INCLUDES += bootable/recovery
LOCAL_STATIC_LIBRARIES += libapplypatch libmtdutils libmincrypt libbz
LOCAL_STATIC_LIBRARIES += libz libcutils libstdc++ libc
diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c
index daf372907..99d366165 100644
--- a/applypatch/applypatch.c
+++ b/applypatch/applypatch.c
@@ -28,6 +28,7 @@
#include "mincrypt/sha.h"
#include "applypatch.h"
#include "mtdutils/mtdutils.h"
+#include "edify/expr.h"
int SaveFileContents(const char* filename, FileContents file);
int LoadMTDContents(const char* filename, FileContents* file);
@@ -39,57 +40,57 @@ 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;
+ 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);
- }
+ // 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;
- }
+ 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->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;
- }
+ 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);
+ ssize_t bytes_read = fread(file->data, 1, file->size, f);
+ if (bytes_read != file->size) {
+ printf("short read of \"%s\" (%ld bytes of %ld)\n",
+ filename, (long)bytes_read, (long)file->size);
+ free(file->data);
+ file->data = NULL;
+ return -1;
+ }
+ fclose(f);
- SHA(file->data, file->size, file->sha1);
- return 0;
+ 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;
- }
+ int aa = *(int*)a;
+ int bb = *(int*)b;
+ if (size_array[aa] < size_array[bb]) {
+ return -1;
+ } else if (size_array[aa] > size_array[bb]) {
+ return 1;
+ } else {
+ return 0;
+ }
}
void FreeFileContents(FileContents* file) {
@@ -113,239 +114,240 @@ void FreeFileContents(FileContents* file) {
// 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;
+ 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;
- }
- SHA_update(&sha_ctx, p, read);
- file->size += read;
}
+ const char* partition = strtok(NULL, ":");
- // 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);
+ 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);
+ }
- 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;
+ 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;
}
- 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;
+ // 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;
}
- p += read;
- }
+ 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;
+ }
- mtd_read_close(ctx);
+ MtdReadContext* ctx = mtd_read_partition(mtd);
+ if (ctx == NULL) {
+ printf("failed to initialize read of mtd partition \"%s\"\n",
+ partition);
+ return -1;
+ }
- 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;
- }
+ 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;
+ }
- const uint8_t* sha_final = SHA_final(&sha_ctx);
- for (i = 0; i < SHA_DIGEST_SIZE; ++i) {
- file->sha1[i] = sha_final[i];
- }
+ 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;
+ // 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);
+ free(copy);
+ free(index);
+ free(size);
+ free(sha1sum);
- return 0;
+ 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;
- }
+ 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));
+ ssize_t bytes_written = FileSink(file.data, file.size, &fd);
+ if (bytes_written != file.size) {
+ printf("short write of \"%s\" (%ld bytes of %ld) (%s)\n",
+ filename, (long)bytes_written, (long)file.size,
+ strerror(errno));
+ close(fd);
+ return -1;
+ }
+ fsync(fd);
close(fd);
- 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;
- }
+ 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;
+ 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;
- }
+ 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;
+ }
- MtdWriteContext* ctx = mtd_write_partition(mtd);
- if (ctx == NULL) {
- printf("failed to init mtd partition \"%s\" for writing\n",
- partition);
- return -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;
+ }
- 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;
- }
+ MtdWriteContext* ctx = mtd_write_partition(mtd);
+ if (ctx == NULL) {
+ printf("failed to init mtd partition \"%s\" for writing\n",
+ partition);
+ return -1;
+ }
- if (mtd_erase_blocks(ctx, -1) < 0) {
- printf("error finishing mtd write of %s\n", partition);
- mtd_write_close(ctx);
- 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_write_close(ctx)) {
- printf("error closing mtd write of %s\n", partition);
- return -1;
- }
+ if (mtd_erase_blocks(ctx, -1) < 0) {
+ printf("error finishing mtd write of %s\n", partition);
+ mtd_write_close(ctx);
+ return -1;
+ }
- free(partition);
- return 0;
+ if (mtd_write_close(ctx)) {
+ printf("error closing mtd write of %s\n", partition);
+ return -1;
+ }
+
+ free(partition);
+ return 0;
}
@@ -354,547 +356,460 @@ int WriteToMTDPartition(unsigned char* data, size_t len,
// 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;
+ 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;
+ }
}
- }
-
- return 0;
+ if (*ps != '\0') 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;
+// Search an array of sha1 strings for one matching the given sha1.
+// Return the index of the match on success, or -1 if no match is
+// found.
+int FindMatchingPatch(uint8_t* sha1, char** const patch_sha1_str,
+ int num_patches) {
+ int i;
+ uint8_t patch_sha1[SHA_DIGEST_SIZE];
+ for (i = 0; i < num_patches; ++i) {
+ if (ParseSha1(patch_sha1_str[i], patch_sha1) == 0 &&
+ memcmp(patch_sha1, sha1, SHA_DIGEST_SIZE) == 0) {
+ return i;
+ }
}
- }
- return NULL;
+ return -1;
}
// Returns 0 if the contents of the file (argv[2]) or the cached file
// match any of the sha1's on the command line (argv[3:]). Returns
// nonzero otherwise.
-int 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;
- }
+int applypatch_check(const char* filename,
+ int num_patches, char** const patch_sha1_str) {
+ FileContents file;
+ file.data = NULL;
+
+ // It's okay to specify no sha1s; the check will pass if the
+ // LoadFileContents is successful. (Useful for reading MTD
+ // partitions, where the filename encodes the sha1s; no need to
+ // check them twice.)
+ if (LoadFileContents(filename, &file) != 0 ||
+ (num_patches > 0 &&
+ FindMatchingPatch(file.sha1, patch_sha1_str, num_patches) < 0)) {
+ printf("file \"%s\" doesn't have any of expected "
+ "sha1 sums; checking cache\n", filename);
+
+ free(file.data);
+
+ // If the source file is missing or corrupted, it might be because
+ // we were killed in the middle of patching it. A copy of it
+ // should have been made in CACHE_TEMP_SOURCE. If that file
+ // exists and matches the sha1 we're looking for, the check still
+ // passes.
+
+ if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 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;
+ if (FindMatchingPatch(file.sha1, patch_sha1_str, num_patches) < 0) {
+ printf("cache bits don't match any sha1 for \"%s\"\n", filename);
+ free(file.data);
+ return 1;
+ }
}
- }
- free(file.data);
- return 0;
+ free(file.data);
+ return 0;
}
int ShowLicenses() {
- ShowBSDiffLicense();
- return 0;
+ ShowBSDiffLicense();
+ return 0;
}
ssize_t FileSink(unsigned char* data, ssize_t len, void* token) {
- int fd = *(int *)token;
- ssize_t done = 0;
- ssize_t wrote;
- while (done < (ssize_t) len) {
- wrote = write(fd, data+done, len-done);
- if (wrote <= 0) {
- printf("error writing %d bytes: %s\n", (int)(len-done), strerror(errno));
- return done;
- }
- done += wrote;
- }
- printf("wrote %d bytes to output\n", (int)done);
- return done;
+ int fd = *(int *)token;
+ ssize_t done = 0;
+ ssize_t wrote;
+ while (done < (ssize_t) len) {
+ wrote = write(fd, data+done, len-done);
+ if (wrote <= 0) {
+ printf("error writing %d bytes: %s\n", (int)(len-done), strerror(errno));
+ return done;
+ }
+ done += wrote;
+ }
+ return done;
}
typedef struct {
- unsigned char* buffer;
- ssize_t size;
- ssize_t pos;
+ unsigned char* buffer;
+ ssize_t size;
+ ssize_t pos;
} MemorySinkInfo;
ssize_t MemorySink(unsigned char* data, ssize_t len, void* token) {
- MemorySinkInfo* msi = (MemorySinkInfo*)token;
- if (msi->size - msi->pos < len) {
- return -1;
- }
- memcpy(msi->buffer + msi->pos, data, len);
- msi->pos += len;
- return len;
+ 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;
+ struct statfs sf;
+ if (statfs(filename, &sf) != 0) {
+ printf("failed to statfs %s: %s\n", filename, strerror(errno));
+ return -1;
+ }
+ return sf.f_bsize * sf.f_bfree;
+}
+
+int CacheSizeCheck(size_t bytes) {
+ if (MakeFreeSpaceOnCache(bytes) < 0) {
+ printf("unable to make %ld bytes available on /cache\n", (long)bytes);
+ return 1;
+ } else {
+ return 0;
+ }
}
-// This program applies binary patches to files in a way that is safe
+
+// This function applies binary patches to files in a way that is safe
// (the original file is not touched until we have the desired
// replacement for it) and idempotent (it's okay to run this program
// multiple times).
//
-// - if the sha1 hash of <tgt-file> is <tgt-sha1>, does nothing and exits
-// successfully.
+// - if the sha1 hash of <target_filename> is <target_sha1_string>,
+// 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, if the sha1 hash of <source_filename> is one of the
+// entries in <patch_sha1_str>, the corresponding patch from
+// <patch_data> (which must be a VAL_BLOB) is applied to produce a
+// new file (the type of patch is automatically detected from the
+// blob daat). If that new file has sha1 hash <target_sha1_str>,
+// moves it to replace <target_filename>, and exits successfully.
+// Note that if <source_filename> and <target_filename> are not the
+// same, <source_filename> is NOT deleted on success.
+// <target_filename> may be the string "-" to mean "the same as
+// source_filename".
//
// - otherwise, or if any error is encountered, exits with non-zero
// status.
//
-// <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;
- }
+// <source_filename> 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 applypatch(const char* source_filename,
+ const char* target_filename,
+ const char* target_sha1_str,
+ size_t target_size,
+ int num_patches,
+ char** const patch_sha1_str,
+ Value** patch_data) {
+ printf("\napplying patch to %s\n", source_filename);
+
+ if (target_filename[0] == '-' &&
+ target_filename[1] == '\0') {
+ target_filename = source_filename;
+ }
- if (strncmp(argv[1], "-l", 3) == 0) {
- return ShowLicenses();
- }
+ uint8_t target_sha1[SHA_DIGEST_SIZE];
+ if (ParseSha1(target_sha1_str, target_sha1) != 0) {
+ printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str);
+ return 1;
+ }
- if (strncmp(argv[1], "-c", 3) == 0) {
- return CheckMode(argc, argv);
- }
+ FileContents copy_file;
+ FileContents source_file;
+ const Value* source_patch_value = NULL;
+ const Value* copy_patch_value = NULL;
+ int made_copy = 0;
+
+ // We try to load the target file into the source_file object.
+ if (LoadFileContents(target_filename, &source_file) == 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 (strncmp(argv[1], "-s", 3) == 0) {
- if (argc != 3) {
- return 2;
+ 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);
}
- 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, &copy_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 (source_file.data != NULL) {
+ int to_use = FindMatchingPatch(source_file.sha1,
+ patch_sha1_str, num_patches);
+ if (to_use >= 0) {
+ source_patch_value = patch_data[to_use];
}
+ }
- if (MakeFreeSpaceOnCache(source_file.size) < 0) {
- printf("not enough free space on /cache\n");
- return 1;
+ if (source_patch_value == NULL) {
+ free(source_file.data);
+ printf("source file is bad; trying copy\n");
+
+ if (LoadFileContents(CACHE_TEMP_SOURCE, &copy_file) < 0) {
+ // fail.
+ printf("failed to read copy file\n");
+ return 1;
}
- if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
- printf("failed to back up source file\n");
- return 1;
+ int to_use = FindMatchingPatch(copy_file.sha1,
+ patch_sha1_str, num_patches);
+ if (to_use > 0) {
+ copy_patch_value = patch_data[to_use];
}
- made_copy = 1;
- unlink(source_filename);
- size_t free_space = FreeSpaceForFile(target_fs);
- printf("(now %ld bytes free for target)\n", (long)free_space);
- }
+ if (copy_patch_value == NULL) {
+ // fail.
+ printf("copy file doesn't match source SHA-1s either\n");
+ return 1;
+ }
}
- const char* patch_filename;
- if (source_patch_filename != NULL) {
- source_to_use = &source_file;
- patch_filename = source_patch_filename;
- } else {
- source_to_use = &copy_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);
+ 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 {
- printf("Unknown patch file format\n");
- return 1;
+ strcpy(target_fs, target_filename);
}
- if (output >= 0) {
- fsync(output);
- close(output);
+ 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_value != NULL) {
+ // Using the original source, but not enough free space. First
+ // copy the source file to cache, then delete it from the original
+ // location.
+
+ if (strncmp(source_filename, "MTD:", 4) == 0) {
+ // 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 Value* patch;
+ if (source_patch_value != NULL) {
+ source_to_use = &source_file;
+ patch = source_patch_value;
+ } else {
+ source_to_use = &copy_file;
+ patch = copy_patch_value;
+ }
+
+ if (patch->type != VAL_BLOB) {
+ printf("patch is not a blob\n");
+ return 1;
+ }
+
+ SinkFn sink = NULL;
+ void* token = NULL;
+ output = -1;
+ outname = NULL;
+ if (strncmp(target_filename, "MTD:", 4) == 0) {
+ // We store the decoded output in memory.
+ msi.buffer = malloc(target_size);
+ if (msi.buffer == NULL) {
+ printf("failed to alloc %ld bytes for output\n",
+ (long)target_size);
+ return 1;
+ }
+ msi.pos = 0;
+ msi.size = target_size;
+ sink = MemorySink;
+ token = &msi;
+ } else {
+ // We write the decoded output to "<tgt-file>.patch".
+ outname = (char*)malloc(strlen(target_filename) + 10);
+ strcpy(outname, target_filename);
+ strcat(outname, ".patch");
+
+ output = open(outname, O_WRONLY | O_CREAT | O_TRUNC);
+ if (output < 0) {
+ printf("failed to open output file %s: %s\n",
+ outname, strerror(errno));
+ return 1;
+ }
+ sink = FileSink;
+ token = &output;
+ }
+
+ char* header = patch->data;
+ ssize_t header_bytes_read = patch->size;
+
+ SHA_init(&ctx);
+
+ int result;
+
+ if (header_bytes_read >= 8 &&
+ memcmp(header, "BSDIFF40", 8) == 0) {
+ result = ApplyBSDiffPatch(source_to_use->data, source_to_use->size,
+ patch, 0, sink, token, &ctx);
+ } else if (header_bytes_read >= 8 &&
+ memcmp(header, "IMGDIFF2", 8) == 0) {
+ result = ApplyImagePatch(source_to_use->data, source_to_use->size,
+ patch, sink, token, &ctx);
+ } else {
+ printf("Unknown patch file format\n");
+ return 1;
+ }
+
+ if (output >= 0) {
+ fsync(output);
+ close(output);
+ }
+
+ if (result != 0) {
+ if (retry == 0) {
+ printf("applying patch failed\n");
+ return result != 0;
+ } else {
+ printf("applying patch failed; retrying\n");
+ }
+ if (outname != NULL) {
+ unlink(outname);
+ }
+ } else {
+ // succeeded; no need to retry
+ break;
+ }
+ } while (retry-- > 0);
+
+ const uint8_t* current_target_sha1 = SHA_final(&ctx);
+ if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_SIZE) != 0) {
+ printf("patch did not produce expected sha1\n");
+ return 1;
}
- if (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);
- }
+ 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 {
- // 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;
+ // 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;
}
diff --git a/applypatch/applypatch.h b/applypatch/applypatch.h
index 3cb802165..10c01259a 100644
--- a/applypatch/applypatch.h
+++ b/applypatch/applypatch.h
@@ -19,6 +19,7 @@
#include <sys/stat.h>
#include "mincrypt/sha.h"
+#include "edify/expr.h"
typedef struct _Patch {
uint8_t sha1[SHA_DIGEST_SIZE];
@@ -42,8 +43,21 @@ typedef struct _FileContents {
typedef ssize_t (*SinkFn)(unsigned char*, ssize_t, void*);
// applypatch.c
+int ShowLicenses();
size_t FreeSpaceForFile(const char* filename);
-int applypatch(int argc, char** argv);
+int CacheSizeCheck(size_t bytes);
+int ParseSha1(const char* str, uint8_t* digest);
+
+int applypatch(const char* source_filename,
+ const char* target_filename,
+ const char* target_sha1_str,
+ size_t target_size,
+ int num_patches,
+ char** const patch_sha1_str,
+ Value** patch_data);
+int applypatch_check(const char* filename,
+ int num_patches,
+ char** const patch_sha1_str);
// Read a file into memory; store it and its associated metadata in
// *file. Return 0 on success.
@@ -53,15 +67,15 @@ void FreeFileContents(FileContents* file);
// bsdiff.c
void ShowBSDiffLicense();
int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
- const char* patch_filename, ssize_t offset,
+ const Value* patch, ssize_t patch_offset,
SinkFn sink, void* token, SHA_CTX* ctx);
int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
- const char* patch_filename, ssize_t patch_offset,
+ const Value* patch, ssize_t patch_offset,
unsigned char** new_data, ssize_t* new_size);
// imgpatch.c
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
- const char* patch_filename,
+ const Value* patch,
SinkFn sink, void* token, SHA_CTX* ctx);
// freecache.c
diff --git a/applypatch/applypatch.sh b/applypatch/applypatch.sh
index 88f3025ff..8ea68a1a9 100755
--- a/applypatch/applypatch.sh
+++ b/applypatch/applypatch.sh
@@ -11,7 +11,7 @@
# the tests.
EMULATOR_PORT=5580
-DATA_DIR=$ANDROID_BUILD_TOP/build/tools/applypatch/testdata
+DATA_DIR=$ANDROID_BUILD_TOP/bootable/recovery/applypatch/testdata
# This must be the filename that applypatch uses for its copies.
CACHE_TEMP_SOURCE=/cache/saved.file
@@ -81,6 +81,7 @@ cleanup() {
testname "removing test files"
run_command rm $WORK_DIR/bloat.dat
run_command rm $WORK_DIR/old.file
+ run_command rm $WORK_DIR/foo
run_command rm $WORK_DIR/patch.bsdiff
run_command rm $WORK_DIR/applypatch
run_command rm $CACHE_TEMP_SOURCE
@@ -88,10 +89,12 @@ cleanup() {
[ "$pid_emulator" == "" ] || kill $pid_emulator
- rm -rf $tmpdir
+ if [ $# == 0 ]; then
+ rm -rf $tmpdir
+ fi
}
-cleanup
+cleanup leave_tmp
$ADB push $ANDROID_PRODUCT_OUT/system/bin/applypatch $WORK_DIR/applypatch
@@ -153,6 +156,8 @@ run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 &&
$ADB push $DATA_DIR/old.file $WORK_DIR
$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR
+echo hello > $tmpdir/foo
+$ADB push $tmpdir/foo $WORK_DIR
# Check that the partition has enough space to apply the patch without
# copying. If it doesn't, we'll be testing the low-space condition
diff --git a/applypatch/bspatch.c b/applypatch/bspatch.c
index d5cd6174d..2e80f81d0 100644
--- a/applypatch/bspatch.c
+++ b/applypatch/bspatch.c
@@ -32,221 +32,221 @@
#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"
- );
+ 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;
+ 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];
+ 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;
+ if(buf[7]&0x80) y=-y;
- return y;
+ return y;
}
+int FillBuffer(unsigned char* buffer, int size, bz_stream* stream) {
+ stream->next_out = (char*)buffer;
+ stream->avail_out = size;
+ while (stream->avail_out > 0) {
+ int bzerr = BZ2_bzDecompress(stream);
+ if (bzerr != BZ_OK && bzerr != BZ_STREAM_END) {
+ printf("bz error %d decompressing\n", bzerr);
+ return -1;
+ }
+ if (stream->avail_out > 0) {
+ printf("need %d more bytes\n", stream->avail_out);
+ }
+ }
+ return 0;
+}
int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
- const char* patch_filename, ssize_t patch_offset,
+ const Value* patch, ssize_t patch_offset,
SinkFn sink, void* token, SHA_CTX* ctx) {
- unsigned char* new_data;
- ssize_t new_size;
- if (ApplyBSDiffPatchMem(old_data, old_size, patch_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;
+ unsigned char* new_data;
+ ssize_t new_size;
+ if (ApplyBSDiffPatchMem(old_data, old_size, patch, patch_offset,
+ &new_data, &new_size) != 0) {
+ return -1;
+ }
+
+ if (sink(new_data, new_size, token) < new_size) {
+ printf("short write of output: %d (%s)\n", errno, strerror(errno));
+ return 1;
+ }
+ if (ctx) {
+ SHA_update(ctx, new_data, new_size);
+ }
+ free(new_data);
+
+ return 0;
}
int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
- const char* patch_filename, ssize_t patch_offset,
+ const Value* patch, 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");
+ // Patch data format:
+ // 0 8 "BSDIFF40"
+ // 8 8 X
+ // 16 8 Y
+ // 24 8 sizeof(newfile)
+ // 32 X bzip2(control block)
+ // 32+X Y bzip2(diff block)
+ // 32+X+Y ??? bzip2(extra block)
+ // with control block a set of triples (x,y,z) meaning "add x bytes
+ // from oldfile to x bytes from the diff block; copy y bytes from the
+ // extra block; seek forwards in oldfile by z bytes".
+
+ unsigned char* header = (unsigned char*) patch->data + patch_offset;
+ if (memcmp(header, "BSDIFF40", 8) != 0) {
+ printf("corrupt bsdiff patch file header (magic number)\n");
return 1;
- }
- ctrl[i] = offtin(buf);
}
- // Sanity check
- if (newpos + ctrl[0] > *new_size) {
- fprintf(stderr, "corrupt patch (new file overrun)\n");
- return 1;
- }
+ ssize_t ctrl_len, data_len;
+ ctrl_len = offtin(header+8);
+ data_len = offtin(header+16);
+ *new_size = offtin(header+24);
- // 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;
+ if (ctrl_len < 0 || data_len < 0 || *new_size < 0) {
+ printf("corrupt patch file header (data lengths)\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];
- }
- }
+ int bzerr;
- // Adjust pointers
- newpos += ctrl[0];
- oldpos += ctrl[0];
+ bz_stream cstream;
+ cstream.next_in = patch->data + patch_offset + 32;
+ cstream.avail_in = ctrl_len;
+ cstream.bzalloc = NULL;
+ cstream.bzfree = NULL;
+ cstream.opaque = NULL;
+ if ((bzerr = BZ2_bzDecompressInit(&cstream, 0, 0)) != BZ_OK) {
+ printf("failed to bzinit control stream (%d)\n", bzerr);
+ }
- // Sanity check
- if (newpos + ctrl[1] > *new_size) {
- fprintf(stderr, "corrupt patch (new file overrun)\n");
- return 1;
+ bz_stream dstream;
+ dstream.next_in = patch->data + patch_offset + 32 + ctrl_len;
+ dstream.avail_in = data_len;
+ dstream.bzalloc = NULL;
+ dstream.bzfree = NULL;
+ dstream.opaque = NULL;
+ if ((bzerr = BZ2_bzDecompressInit(&dstream, 0, 0)) != BZ_OK) {
+ printf("failed to bzinit diff stream (%d)\n", bzerr);
}
- // 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;
+ bz_stream estream;
+ estream.next_in = patch->data + patch_offset + 32 + ctrl_len + data_len;
+ estream.avail_in = patch->size - (patch_offset + 32 + ctrl_len + data_len);
+ estream.bzalloc = NULL;
+ estream.bzfree = NULL;
+ estream.opaque = NULL;
+ if ((bzerr = BZ2_bzDecompressInit(&estream, 0, 0)) != BZ_OK) {
+ printf("failed to bzinit extra stream (%d)\n", bzerr);
}
- // Adjust pointers
- newpos += ctrl[1];
- oldpos += ctrl[2];
- }
+ *new_data = malloc(*new_size);
+ if (*new_data == NULL) {
+ printf("failed to allocate %ld bytes of memory for output file\n",
+ (long)*new_size);
+ return 1;
+ }
- BZ2_bzReadClose(&bzerr, cpfbz2);
- BZ2_bzReadClose(&bzerr, dpfbz2);
- BZ2_bzReadClose(&bzerr, epfbz2);
- fclose(cpf);
- fclose(dpf);
- fclose(epf);
+ off_t oldpos = 0, newpos = 0;
+ off_t ctrl[3];
+ off_t len_read;
+ int i;
+ unsigned char buf[24];
+ while (newpos < *new_size) {
+ // Read control data
+ if (FillBuffer(buf, 24, &cstream) != 0) {
+ printf("error while reading control stream\n");
+ return 1;
+ }
+ ctrl[0] = offtin(buf);
+ ctrl[1] = offtin(buf+8);
+ ctrl[2] = offtin(buf+16);
+
+ // Sanity check
+ if (newpos + ctrl[0] > *new_size) {
+ printf("corrupt patch (new file overrun)\n");
+ return 1;
+ }
+
+ // Read diff string
+ if (FillBuffer(*new_data + newpos, ctrl[0], &dstream) != 0) {
+ printf("error while reading diff stream\n");
+ return 1;
+ }
+
+ // Add old data to diff string
+ for (i = 0; i < ctrl[0]; ++i) {
+ if ((oldpos+i >= 0) && (oldpos+i < old_size)) {
+ (*new_data)[newpos+i] += old_data[oldpos+i];
+ }
+ }
+
+ // Adjust pointers
+ newpos += ctrl[0];
+ oldpos += ctrl[0];
+
+ // Sanity check
+ if (newpos + ctrl[1] > *new_size) {
+ printf("corrupt patch (new file overrun)\n");
+ return 1;
+ }
+
+ // Read extra string
+ if (FillBuffer(*new_data + newpos, ctrl[1], &estream) != 0) {
+ printf("error while reading extra stream\n");
+ return 1;
+ }
+
+ // Adjust pointers
+ newpos += ctrl[1];
+ oldpos += ctrl[2];
+ }
- return 0;
+ BZ2_bzDecompressEnd(&cstream);
+ BZ2_bzDecompressEnd(&dstream);
+ BZ2_bzDecompressEnd(&estream);
+ return 0;
}
diff --git a/applypatch/imgpatch.c b/applypatch/imgpatch.c
index 53228174f..e3ee80ac0 100644
--- a/applypatch/imgpatch.c
+++ b/applypatch/imgpatch.c
@@ -36,329 +36,184 @@
* Return 0 on success.
*/
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
- const char* patch_filename,
+ const Value* patch,
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);
+ ssize_t pos = 12;
+ char* header = patch->data;
+ if (patch->size < 12) {
+ printf("patch too short to contain header\n");
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) {
+ // IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW.
+ // (IMGDIFF1, which is no longer supported, used CHUNK_NORMAL and
+ // CHUNK_GZIP.)
+ if (memcmp(header, "IMGDIFF2", 8) != 0) {
+ printf("corrupt patch file header (magic number)\n");
return -1;
- }
-
- // 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;
+ int num_chunks = Read4(header+8);
- if (sink(temp_data, have, token) != have) {
- printf("failed to write %d compressed bytes to output\n",
- have);
- return -1;
+ int i;
+ for (i = 0; i < num_chunks; ++i) {
+ // each chunk's header record starts with 4 bytes.
+ if (pos + 4 > patch->size) {
+ printf("failed to read chunk %d record\n", i);
+ return -1;
}
- 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;
+ int type = Read4(patch->data + pos);
+ pos += 4;
+
+ if (type == CHUNK_NORMAL) {
+ char* normal_header = patch->data + pos;
+ pos += 24;
+ if (pos > patch->size) {
+ printf("failed to read chunk %d normal header data\n", i);
+ return -1;
+ }
+
+ size_t src_start = Read8(normal_header);
+ size_t src_len = Read8(normal_header+8);
+ size_t patch_offset = Read8(normal_header+16);
+
+ ApplyBSDiffPatch(old_data + src_start, src_len,
+ patch, patch_offset, sink, token, ctx);
+ } else if (type == CHUNK_RAW) {
+ char* raw_header = patch->data + pos;
+ pos += 4;
+ if (pos > patch->size) {
+ printf("failed to read chunk %d raw header data\n", i);
+ return -1;
+ }
+
+ ssize_t data_len = Read4(raw_header);
+
+ if (pos + data_len > patch->size) {
+ printf("failed to read chunk %d raw data\n", i);
+ return -1;
+ }
+ SHA_update(ctx, patch->data + pos, data_len);
+ if (sink((unsigned char*)patch->data + pos,
+ data_len, token) != data_len) {
+ printf("failed to write chunk %d raw data\n", i);
+ return -1;
+ }
+ pos += data_len;
+ } else if (type == CHUNK_DEFLATE) {
+ // deflate chunks have an additional 60 bytes in their chunk header.
+ char* deflate_header = patch->data + pos;
+ pos += 60;
+ if (pos > patch->size) {
+ printf("failed to read chunk %d deflate header data\n", i);
+ return -1;
+ }
+
+ size_t src_start = Read8(deflate_header);
+ size_t src_len = Read8(deflate_header+8);
+ size_t patch_offset = Read8(deflate_header+16);
+ size_t expanded_len = Read8(deflate_header+24);
+ size_t target_len = Read8(deflate_header+32);
+ int level = Read4(deflate_header+40);
+ int method = Read4(deflate_header+44);
+ int windowBits = Read4(deflate_header+48);
+ int memLevel = Read4(deflate_header+52);
+ int strategy = Read4(deflate_header+56);
+
+ // Decompress the source data; the chunk header tells us exactly
+ // how big we expect it to be when decompressed.
+
+ unsigned char* expanded_source = malloc(expanded_len);
+ if (expanded_source == NULL) {
+ printf("failed to allocate %d bytes for expanded_source\n",
+ expanded_len);
+ return -1;
+ }
+
+ z_stream strm;
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = src_len;
+ strm.next_in = (unsigned char*)(old_data + src_start);
+ strm.avail_out = expanded_len;
+ strm.next_out = expanded_source;
+
+ int ret;
+ ret = inflateInit2(&strm, -15);
+ if (ret != Z_OK) {
+ printf("failed to init source inflation: %d\n", ret);
+ return -1;
+ }
+
+ // Because we've provided enough room to accommodate the output
+ // data, we expect one call to inflate() to suffice.
+ ret = inflate(&strm, Z_SYNC_FLUSH);
+ if (ret != Z_STREAM_END) {
+ printf("source inflation returned %d\n", ret);
+ return -1;
+ }
+ // We should have filled the output buffer exactly.
+ if (strm.avail_out != 0) {
+ printf("source inflation short by %d bytes\n", strm.avail_out);
+ return -1;
+ }
+ inflateEnd(&strm);
+
+ // Next, apply the bsdiff patch (in memory) to the uncompressed
+ // data.
+ unsigned char* uncompressed_target_data;
+ ssize_t uncompressed_target_size;
+ if (ApplyBSDiffPatchMem(expanded_source, expanded_len,
+ patch, patch_offset,
+ &uncompressed_target_data,
+ &uncompressed_target_size) != 0) {
+ return -1;
+ }
+
+ // Now compress the target data and append it to the output.
+
+ // we're done with the expanded_source data buffer, so we'll
+ // reuse that memory to receive the output of deflate.
+ unsigned char* temp_data = expanded_source;
+ ssize_t temp_size = expanded_len;
+ if (temp_size < 32768) {
+ // ... unless the buffer is too small, in which case we'll
+ // allocate a fresh one.
+ free(temp_data);
+ temp_data = malloc(32768);
+ temp_size = 32768;
+ }
+
+ // now the deflate stream
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = uncompressed_target_size;
+ strm.next_in = uncompressed_target_data;
+ ret = deflateInit2(&strm, level, method, windowBits, memLevel, strategy);
+ do {
+ strm.avail_out = temp_size;
+ strm.next_out = temp_data;
+ ret = deflate(&strm, Z_FINISH);
+ ssize_t have = temp_size - strm.avail_out;
+
+ if (sink(temp_data, have, token) != have) {
+ printf("failed to write %ld compressed bytes to output\n",
+ (long)have);
+ return -1;
+ }
+ SHA_update(ctx, temp_data, have);
+ } while (ret != Z_STREAM_END);
+ deflateEnd(&strm);
+
+ free(temp_data);
+ free(uncompressed_target_data);
+ } else {
+ printf("patch chunk %d is unknown type %d\n", i, type);
+ return -1;
}
- 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;
+ return 0;
}
diff --git a/applypatch/main.c b/applypatch/main.c
index e08f5c1eb..3917f86ed 100644
--- a/applypatch/main.c
+++ b/applypatch/main.c
@@ -15,8 +15,126 @@
*/
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
-extern int applypatch(int argc, char** argv);
+#include "applypatch.h"
+#include "edify/expr.h"
+#include "mincrypt/sha.h"
+
+int CheckMode(int argc, char** argv) {
+ if (argc < 3) {
+ return 2;
+ }
+ return applypatch_check(argv[2], argc-3, argv+3);
+}
+
+int SpaceMode(int argc, char** argv) {
+ if (argc != 3) {
+ return 2;
+ }
+ char* endptr;
+ size_t bytes = strtol(argv[2], &endptr, 10);
+ if (bytes == 0 && endptr == argv[2]) {
+ printf("can't parse \"%s\" as byte count\n\n", argv[2]);
+ return 1;
+ }
+ return CacheSizeCheck(bytes);
+}
+
+// Parse arguments (which should be of the form "<sha1>" or
+// "<sha1>:<filename>" into the new parallel arrays *sha1s and
+// *patches (loading file contents into the patches). Returns 0 on
+// success.
+static int ParsePatchArgs(int argc, char** argv,
+ char*** sha1s, Value*** patches, int* num_patches) {
+ *num_patches = argc;
+ *sha1s = malloc(*num_patches * sizeof(char*));
+ *patches = malloc(*num_patches * sizeof(Value*));
+ memset(*patches, 0, *num_patches * sizeof(Value*));
+
+ uint8_t digest[SHA_DIGEST_SIZE];
+
+ int i;
+ for (i = 0; i < *num_patches; ++i) {
+ char* colon = strchr(argv[i], ':');
+ if (colon != NULL) {
+ *colon = '\0';
+ ++colon;
+ }
+
+ if (ParseSha1(argv[i], digest) != 0) {
+ printf("failed to parse sha1 \"%s\"\n", argv[i]);
+ return -1;
+ }
+
+ (*sha1s)[i] = argv[i];
+ if (colon == NULL) {
+ (*patches)[i] = NULL;
+ } else {
+ FileContents fc;
+ if (LoadFileContents(colon, &fc) != 0) {
+ goto abort;
+ }
+ (*patches)[i] = malloc(sizeof(Value));
+ (*patches)[i]->type = VAL_BLOB;
+ (*patches)[i]->size = fc.size;
+ (*patches)[i]->data = (char*)fc.data;
+ }
+ }
+
+ return 0;
+
+ abort:
+ for (i = 0; i < *num_patches; ++i) {
+ Value* p = (*patches)[i];
+ if (p != NULL) {
+ free(p->data);
+ free(p);
+ }
+ }
+ free(*sha1s);
+ free(*patches);
+ return -1;
+}
+
+int PatchMode(int argc, char** argv) {
+ if (argc < 6) {
+ return 2;
+ }
+
+ char* endptr;
+ size_t target_size = strtol(argv[4], &endptr, 10);
+ if (target_size == 0 && endptr == argv[4]) {
+ printf("can't parse \"%s\" as byte count\n\n", argv[4]);
+ return 1;
+ }
+
+ char** sha1s;
+ Value** patches;
+ int num_patches;
+ if (ParsePatchArgs(argc-5, argv+5, &sha1s, &patches, &num_patches) != 0) {
+ printf("failed to parse patch args\n");
+ return 1;
+ }
+
+ int result = applypatch(argv[1], argv[2], argv[3], target_size,
+ num_patches, sha1s, patches);
+
+ int i;
+ for (i = 0; i < num_patches; ++i) {
+ Value* p = patches[i];
+ if (p != NULL) {
+ free(p->data);
+ free(p);
+ }
+ }
+ free(sha1s);
+ free(patches);
+
+ return result;
+}
// This program applies binary patches to files in a way that is safe
// (the original file is not touched until we have the desired
@@ -42,9 +160,9 @@ extern int applypatch(int argc, char** argv);
// 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(
+ if (argc < 2) {
+ usage:
+ printf(
"usage: %s <src-file> <tgt-file> <tgt-sha1> <tgt-size> "
"[<src-sha1>:<patch> ...]\n"
" or %s -c <file> [<sha1> ...]\n"
@@ -55,6 +173,23 @@ int main(int argc, char** argv) {
" 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;
+ return 2;
+ }
+
+ int result;
+
+ if (strncmp(argv[1], "-l", 3) == 0) {
+ result = ShowLicenses();
+ } else if (strncmp(argv[1], "-c", 3) == 0) {
+ result = CheckMode(argc, argv);
+ } else if (strncmp(argv[1], "-s", 3) == 0) {
+ result = SpaceMode(argc, argv);
+ } else {
+ result = PatchMode(argc, argv);
+ }
+
+ if (result == 2) {
+ goto usage;
+ }
+ return result;
}
diff --git a/applypatch/utils.c b/applypatch/utils.c
index 912229bcf..41ff676dc 100644
--- a/applypatch/utils.c
+++ b/applypatch/utils.c
@@ -38,25 +38,28 @@ void Write8(long long value, FILE* f) {
fputc((value >> 56) & 0xff, f);
}
-int Read2(unsigned char* p) {
- return (int)(((unsigned int)p[1] << 8) |
- (unsigned int)p[0]);
+int Read2(void* pv) {
+ unsigned char* p = pv;
+ 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]);
+int Read4(void* pv) {
+ unsigned char* p = pv;
+ return (int)(((unsigned int)p[3] << 24) |
+ ((unsigned int)p[2] << 16) |
+ ((unsigned int)p[1] << 8) |
+ (unsigned int)p[0]);
}
-long long Read8(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]);
+long long Read8(void* pv) {
+ unsigned char* p = pv;
+ return (long long)(((unsigned long long)p[7] << 56) |
+ ((unsigned long long)p[6] << 48) |
+ ((unsigned long long)p[5] << 40) |
+ ((unsigned long long)p[4] << 32) |
+ ((unsigned long long)p[3] << 24) |
+ ((unsigned long long)p[2] << 16) |
+ ((unsigned long long)p[1] << 8) |
+ (unsigned long long)p[0]);
}
diff --git a/applypatch/utils.h b/applypatch/utils.h
index d6d6f1d3e..bc97f1720 100644
--- a/applypatch/utils.h
+++ b/applypatch/utils.h
@@ -23,8 +23,8 @@
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);
+int Read2(void* p);
+int Read4(void* p);
+long long Read8(void* p);
#endif // _BUILD_TOOLS_APPLYPATCH_UTILS_H