diff options
Diffstat (limited to 'applypatch')
-rw-r--r-- | applypatch/applypatch.c | 147 | ||||
-rw-r--r-- | applypatch/applypatch.h | 8 | ||||
-rw-r--r-- | applypatch/imgdiff.c | 57 | ||||
-rw-r--r-- | applypatch/imgpatch.c | 21 | ||||
-rw-r--r-- | applypatch/main.c | 23 |
5 files changed, 221 insertions, 35 deletions
diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index 488fd8c6f..0dcdce0b1 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.c @@ -39,7 +39,8 @@ static int GenerateTarget(FileContents* source_file, const char* source_filename, const char* target_filename, const uint8_t target_sha1[SHA_DIGEST_SIZE], - size_t target_size); + size_t target_size, + const Value* bonus_data); static int mtd_partitions_scanned = 0; @@ -420,18 +421,111 @@ int WriteToPartition(unsigned char* data, size_t len, break; case EMMC: - ; - FILE* f = fopen(partition, "wb"); - if (fwrite(data, 1, len, f) != len) { - printf("short write writing to %s (%s)\n", - partition, strerror(errno)); + { + size_t start = 0; + int success = 0; + int fd = open(partition, O_RDWR | O_SYNC); + if (fd < 0) { + printf("failed to open %s: %s\n", partition, strerror(errno)); + return -1; + } + int attempt; + + for (attempt = 0; attempt < 10; ++attempt) { + size_t next_sync = start + (1<<20); + printf("raw O_SYNC write %s attempt %d start at %d\n", partition, attempt+1, start); + lseek(fd, start, SEEK_SET); + while (start < len) { + size_t to_write = len - start; + if (to_write > 4096) to_write = 4096; + + ssize_t written = write(fd, data+start, to_write); + if (written < 0) { + if (errno == EINTR) { + written = 0; + } else { + printf("failed write writing to %s (%s)\n", + partition, strerror(errno)); + return -1; + } + } + start += written; + if (start >= next_sync) { + fsync(fd); + next_sync = start + (1<<20); + } + } + fsync(fd); + + // drop caches so our subsequent verification read + // won't just be reading the cache. + sync(); + int dc = open("/proc/sys/vm/drop_caches", O_WRONLY); + write(dc, "3\n", 2); + close(dc); + sleep(1); + printf(" caches dropped\n"); + + // verify + lseek(fd, 0, SEEK_SET); + unsigned char buffer[4096]; + start = len; + size_t p; + for (p = 0; p < len; p += sizeof(buffer)) { + size_t to_read = len - p; + if (to_read > sizeof(buffer)) to_read = sizeof(buffer); + + size_t so_far = 0; + while (so_far < to_read) { + ssize_t read_count = read(fd, buffer+so_far, to_read-so_far); + if (read_count < 0) { + if (errno == EINTR) { + read_count = 0; + } else { + printf("verify read error %s at %d: %s\n", + partition, p, strerror(errno)); + return -1; + } + } + if ((size_t)read_count < to_read) { + printf("short verify read %s at %d: %d %d %s\n", + partition, p, read_count, to_read, strerror(errno)); + } + so_far += read_count; + } + + if (memcmp(buffer, data+p, to_read)) { + printf("verification failed starting at %d\n", p); + start = p; + break; + } + } + + if (start == len) { + printf("verification read succeeded (attempt %d)\n", attempt+1); + success = true; + break; + } + + sleep(2); + } + + if (!success) { + printf("failed to verify after all attempts\n"); return -1; } - if (fclose(f) != 0) { + + if (close(fd) != 0) { printf("error closing %s (%s)\n", partition, strerror(errno)); return -1; } + // hack: sync and sleep after closing in hopes of getting + // the data actually onto flash. + printf("sleeping after close\n"); + sync(); + sleep(5); break; + } } free(copy); @@ -472,7 +566,7 @@ int ParseSha1(const char* str, uint8_t* digest) { // 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, const char** patch_sha1_str, +int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str, int num_patches) { int i; uint8_t patch_sha1[SHA_DIGEST_SIZE]; @@ -584,6 +678,14 @@ int CacheSizeCheck(size_t bytes) { } } +static void print_short_sha1(const uint8_t sha1[SHA_DIGEST_SIZE]) { + int i; + const char* hex = "0123456789abcdef"; + for (i = 0; i < 4; ++i) { + putchar(hex[(sha1[i]>>4) & 0xf]); + putchar(hex[sha1[i] & 0xf]); + } +} // This function applies binary patches to files in a way that is safe // (the original file is not touched until we have the desired @@ -617,8 +719,9 @@ int applypatch(const char* source_filename, size_t target_size, int num_patches, char** const patch_sha1_str, - Value** patch_data) { - printf("\napplying patch to %s\n", source_filename); + Value** patch_data, + Value* bonus_data) { + printf("patch %s: ", source_filename); if (target_filename[0] == '-' && target_filename[1] == '\0') { @@ -644,8 +747,9 @@ int applypatch(const char* source_filename, 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); + printf("already "); + print_short_sha1(target_sha1); + putchar('\n'); free(source_file.data); return 0; } @@ -699,7 +803,7 @@ int applypatch(const char* source_filename, int result = GenerateTarget(&source_file, source_patch_value, ©_file, copy_patch_value, source_filename, target_filename, - target_sha1, target_size); + target_sha1, target_size, bonus_data); free(source_file.data); free(copy_file.data); @@ -713,7 +817,8 @@ static int GenerateTarget(FileContents* source_file, const char* source_filename, const char* target_filename, const uint8_t target_sha1[SHA_DIGEST_SIZE], - size_t target_size) { + size_t target_size, + const Value* bonus_data) { int retry = 1; SHA_CTX ctx; int output; @@ -766,8 +871,10 @@ static int GenerateTarget(FileContents* source_file, enough_space = (free_space > (256 << 10)) && // 256k (two-block) minimum (free_space > (target_size * 3 / 2)); // 50% margin of error - printf("target %ld bytes; free space %ld bytes; retry %d; enough %d\n", - (long)target_size, (long)free_space, retry, enough_space); + if (!enough_space) { + 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) { @@ -802,7 +909,7 @@ static int GenerateTarget(FileContents* source_file, unlink(source_filename); size_t free_space = FreeSpaceForFile(target_fs); - printf("(now %ld bytes free for target)\n", (long)free_space); + printf("(now %ld bytes free for target) ", (long)free_space); } } @@ -867,7 +974,7 @@ static int GenerateTarget(FileContents* source_file, } 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); + patch, sink, token, &ctx, bonus_data); } else { printf("Unknown patch file format\n"); return 1; @@ -898,6 +1005,10 @@ static int GenerateTarget(FileContents* source_file, if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_SIZE) != 0) { printf("patch did not produce expected sha1\n"); return 1; + } else { + printf("now "); + print_short_sha1(target_sha1); + putchar('\n'); } if (output < 0) { diff --git a/applypatch/applypatch.h b/applypatch/applypatch.h index fb58843ba..f1f13a100 100644 --- a/applypatch/applypatch.h +++ b/applypatch/applypatch.h @@ -55,7 +55,8 @@ int applypatch(const char* source_filename, size_t target_size, int num_patches, char** const patch_sha1_str, - Value** patch_data); + Value** patch_data, + Value* bonus_data); int applypatch_check(const char* filename, int num_patches, char** const patch_sha1_str); @@ -64,7 +65,7 @@ int LoadFileContents(const char* filename, FileContents* file, int retouch_flag); int SaveFileContents(const char* filename, const FileContents* file); void FreeFileContents(FileContents* file); -int FindMatchingPatch(uint8_t* sha1, const char** patch_sha1_str, +int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str, int num_patches); // bsdiff.c @@ -79,7 +80,8 @@ int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, // imgpatch.c int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value* patch, - SinkFn sink, void* token, SHA_CTX* ctx); + SinkFn sink, void* token, SHA_CTX* ctx, + const Value* bonus_data); // freecache.c int MakeFreeSpaceOnCache(size_t bytes_needed); diff --git a/applypatch/imgdiff.c b/applypatch/imgdiff.c index 6b9ebee5c..05c4f250f 100644 --- a/applypatch/imgdiff.c +++ b/applypatch/imgdiff.c @@ -111,6 +111,14 @@ * * After the header there are 'chunk count' bsdiff patches; the offset * of each from the beginning of the file is specified in the header. + * + * This tool can take an optional file of "bonus data". This is an + * extra file of data that is appended to chunk #1 after it is + * compressed (it must be a CHUNK_DEFLATE chunk). The same file must + * be available (and passed to applypatch with -b) when applying the + * patch. This is used to reduce the size of recovery-from-boot + * patches by combining the boot image with recovery ramdisk + * information that is stored on the system partition. */ #include <errno.h> @@ -772,21 +780,45 @@ void DumpChunks(ImageChunk* chunks, int num_chunks) { } int main(int argc, char** argv) { - if (argc != 4 && argc != 5) { - usage: - printf("usage: %s [-z] <src-img> <tgt-img> <patch-file>\n", - argv[0]); - return 2; - } - int zip_mode = 0; - if (strcmp(argv[1], "-z") == 0) { + if (argc >= 2 && strcmp(argv[1], "-z") == 0) { zip_mode = 1; --argc; ++argv; } + size_t bonus_size = 0; + unsigned char* bonus_data = NULL; + if (argc >= 3 && strcmp(argv[1], "-b") == 0) { + struct stat st; + if (stat(argv[2], &st) != 0) { + printf("failed to stat bonus file %s: %s\n", argv[2], strerror(errno)); + return 1; + } + bonus_size = st.st_size; + bonus_data = malloc(bonus_size); + FILE* f = fopen(argv[2], "rb"); + if (f == NULL) { + printf("failed to open bonus file %s: %s\n", argv[2], strerror(errno)); + return 1; + } + if (fread(bonus_data, 1, bonus_size, f) != bonus_size) { + printf("failed to read bonus file %s: %s\n", argv[2], strerror(errno)); + return 1; + } + fclose(f); + + argc -= 2; + argv += 2; + } + + if (argc != 4) { + usage: + printf("usage: %s [-z] [-b <bonus-file>] <src-img> <tgt-img> <patch-file>\n", + argv[0]); + return 2; + } int num_src_chunks; ImageChunk* src_chunks; @@ -909,6 +941,8 @@ int main(int argc, char** argv) { // Compute bsdiff patches for each chunk's data (the uncompressed // data, in the case of deflate chunks). + DumpChunks(src_chunks, num_src_chunks); + printf("Construct patches for %d chunks...\n", num_tgt_chunks); unsigned char** patch_data = malloc(num_tgt_chunks * sizeof(unsigned char*)); size_t* patch_size = malloc(num_tgt_chunks * sizeof(size_t)); @@ -923,6 +957,13 @@ int main(int argc, char** argv) { patch_data[i] = MakePatch(src_chunks, tgt_chunks+i, patch_size+i); } } else { + if (i == 1 && bonus_data) { + printf(" using %d bytes of bonus data for chunk %d\n", bonus_size, i); + src_chunks[i].data = realloc(src_chunks[i].data, src_chunks[i].len + bonus_size); + memcpy(src_chunks[i].data+src_chunks[i].len, bonus_data, bonus_size); + src_chunks[i].len += bonus_size; + } + patch_data[i] = MakePatch(src_chunks+i, tgt_chunks+i, patch_size+i); } printf("patch %3d is %d bytes (of %d)\n", diff --git a/applypatch/imgpatch.c b/applypatch/imgpatch.c index e3ee80ac0..3a1df3872 100644 --- a/applypatch/imgpatch.c +++ b/applypatch/imgpatch.c @@ -37,7 +37,8 @@ */ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value* patch, - SinkFn sink, void* token, SHA_CTX* ctx) { + SinkFn sink, void* token, SHA_CTX* ctx, + const Value* bonus_data) { ssize_t pos = 12; char* header = patch->data; if (patch->size < 12) { @@ -123,6 +124,12 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, // Decompress the source data; the chunk header tells us exactly // how big we expect it to be when decompressed. + // Note: expanded_len will include the bonus data size if + // the patch was constructed with bonus data. The + // deflation will come up 'bonus_size' bytes short; these + // must be appended from the bonus_data value. + size_t bonus_size = (i == 1 && bonus_data != NULL) ? bonus_data->size : 0; + unsigned char* expanded_source = malloc(expanded_len); if (expanded_source == NULL) { printf("failed to allocate %d bytes for expanded_source\n", @@ -153,13 +160,19 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, 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); + // We should have filled the output buffer exactly, except + // for the bonus_size. + if (strm.avail_out != bonus_size) { + printf("source inflation short by %d bytes\n", strm.avail_out-bonus_size); return -1; } inflateEnd(&strm); + if (bonus_size) { + memcpy(expanded_source + (expanded_len - bonus_size), + bonus_data->data, bonus_size); + } + // Next, apply the bsdiff patch (in memory) to the uncompressed // data. unsigned char* uncompressed_target_data; diff --git a/applypatch/main.c b/applypatch/main.c index 7025a2e2e..f61db5d9e 100644 --- a/applypatch/main.c +++ b/applypatch/main.c @@ -100,6 +100,21 @@ static int ParsePatchArgs(int argc, char** argv, } int PatchMode(int argc, char** argv) { + Value* bonus = NULL; + if (argc >= 3 && strcmp(argv[1], "-b") == 0) { + FileContents fc; + if (LoadFileContents(argv[2], &fc, RETOUCH_DONT_MASK) != 0) { + printf("failed to load bonus file %s\n", argv[2]); + return 1; + } + bonus = malloc(sizeof(Value)); + bonus->type = VAL_BLOB; + bonus->size = fc.size; + bonus->data = (char*)fc.data; + argc -= 2; + argv += 2; + } + if (argc < 6) { return 2; } @@ -120,7 +135,7 @@ int PatchMode(int argc, char** argv) { } int result = applypatch(argv[1], argv[2], argv[3], target_size, - num_patches, sha1s, patches); + num_patches, sha1s, patches, bonus); int i; for (i = 0; i < num_patches; ++i) { @@ -130,6 +145,10 @@ int PatchMode(int argc, char** argv) { free(p); } } + if (bonus) { + free(bonus->data); + free(bonus); + } free(sha1s); free(patches); @@ -163,7 +182,7 @@ int main(int argc, char** argv) { if (argc < 2) { usage: printf( - "usage: %s <src-file> <tgt-file> <tgt-sha1> <tgt-size> " + "usage: %s [-b <bonus-file>] <src-file> <tgt-file> <tgt-sha1> <tgt-size> " "[<src-sha1>:<patch> ...]\n" " or %s -c <file> [<sha1> ...]\n" " or %s -s <bytes>\n" |