int main(int argc, char** argv) { if (argc != 4) { fprintf(stderr, "usage: %s <src-img> <tgt-img> <patch-file>\n", argv[0]); return 2; } int num_src_chunks; ImageChunk* src_chunks; if (ReadImage(argv[1], &num_src_chunks, &src_chunks) == NULL) { fprintf(stderr, "failed to break apart source image\n"); return 1; } int num_tgt_chunks; ImageChunk* tgt_chunks; if (ReadImage(argv[2], &num_tgt_chunks, &tgt_chunks) == NULL) { fprintf(stderr, "failed to break apart target image\n"); return 1; } // Verify that the source and target images have the same chunk // structure (ie, the same sequence of gzip and normal chunks). if (num_src_chunks != num_tgt_chunks) { fprintf(stderr, "source and target don't have same number of chunks!\n"); return 1; } int i; for (i = 0; i < num_src_chunks; ++i) { if (src_chunks[i].type != tgt_chunks[i].type) { fprintf(stderr, "source and target don't have same chunk " "structure! (chunk %d)\n", i); return 1; } } // Confirm that given the uncompressed chunk data in the target, we // can recompress it and get exactly the same bits as are in the // input target image. If this fails, treat the chunk as a normal // non-gzipped chunk. for (i = 0; i < num_tgt_chunks; ++i) { if (tgt_chunks[i].type == CHUNK_GZIP) { if (ReconstructGzipChunk(tgt_chunks+i) < 0) { printf("failed to reconstruct target gzip chunk %d; " "treating as normal chunk\n", i); ChangeGzipChunkToNormal(tgt_chunks+i); ChangeGzipChunkToNormal(src_chunks+i); } else { printf("reconstructed target gzip chunk %d\n", i); } } } // Compute bsdiff patches for each chunk's data (the uncompressed // data, in the case of gzip chunks). unsigned char** patch_data = malloc(num_src_chunks * sizeof(unsigned char*)); size_t* patch_size = malloc(num_src_chunks * sizeof(size_t)); for (i = 0; i < num_src_chunks; ++i) { patch_data[i] = MakePatch(src_chunks+i, tgt_chunks+i, patch_size+i); printf("patch %d is %d bytes (of %d)\n", i, patch_size[i], tgt_chunks[i].type == CHUNK_NORMAL ? tgt_chunks[i].len : tgt_chunks[i].gzip_len); } // Figure out how big the imgdiff file header is going to be, so // that we can correctly compute the offset of each bsdiff patch // within the file. size_t total_header_size = 12; for (i = 0; i < num_src_chunks; ++i) { total_header_size += 4 + 8*3; if (src_chunks[i].type == CHUNK_GZIP) { total_header_size += 8*2 + 4*6 + tgt_chunks[i].gzip_header_len + 8; } } size_t offset = total_header_size; FILE* f = fopen(argv[3], "wb"); // Write out the headers. fwrite("IMGDIFF1", 1, 8, f); Write4(num_src_chunks, f); for (i = 0; i < num_tgt_chunks; ++i) { Write4(tgt_chunks[i].type, f); Write8(src_chunks[i].start, f); Write8(src_chunks[i].type == CHUNK_NORMAL ? src_chunks[i].len : (src_chunks[i].gzip_len + src_chunks[i].gzip_header_len + 8), f); Write8(offset, f); if (tgt_chunks[i].type == CHUNK_GZIP) { Write8(src_chunks[i].len, f); Write8(tgt_chunks[i].len, f); Write4(tgt_chunks[i].level, f); Write4(tgt_chunks[i].method, f); Write4(tgt_chunks[i].windowBits, f); Write4(tgt_chunks[i].memLevel, f); Write4(tgt_chunks[i].strategy, f); Write4(tgt_chunks[i].gzip_header_len, f); fwrite(tgt_chunks[i].gzip_header, 1, tgt_chunks[i].gzip_header_len, f); fwrite(tgt_chunks[i].gzip_footer, 1, GZIP_FOOTER_LEN, f); } offset += patch_size[i]; } // Append each chunk's bsdiff patch, in order. for (i = 0; i < num_tgt_chunks; ++i) { fwrite(patch_data[i], 1, patch_size[i], f); } fclose(f); return 0; }
int main(int argc, char** argv) { int zip_mode = 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; int num_tgt_chunks; ImageChunk* tgt_chunks; int i; if (zip_mode) { if (ReadZip(argv[1], &num_src_chunks, &src_chunks, 1) == NULL) { printf("failed to break apart source zip file\n"); return 1; } if (ReadZip(argv[2], &num_tgt_chunks, &tgt_chunks, 0) == NULL) { printf("failed to break apart target zip file\n"); return 1; } } else { if (ReadImage(argv[1], &num_src_chunks, &src_chunks) == NULL) { printf("failed to break apart source image\n"); return 1; } if (ReadImage(argv[2], &num_tgt_chunks, &tgt_chunks) == NULL) { printf("failed to break apart target image\n"); return 1; } // Verify that the source and target images have the same chunk // structure (ie, the same sequence of deflate and normal chunks). if (!zip_mode) { // Merge the gzip header and footer in with any adjacent // normal chunks. MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks); MergeAdjacentNormalChunks(src_chunks, &num_src_chunks); } if (num_src_chunks != num_tgt_chunks) { printf("source and target don't have same number of chunks!\n"); printf("source chunks:\n"); DumpChunks(src_chunks, num_src_chunks); printf("target chunks:\n"); DumpChunks(tgt_chunks, num_tgt_chunks); return 1; } for (i = 0; i < num_src_chunks; ++i) { if (src_chunks[i].type != tgt_chunks[i].type) { printf("source and target don't have same chunk " "structure! (chunk %d)\n", i); printf("source chunks:\n"); DumpChunks(src_chunks, num_src_chunks); printf("target chunks:\n"); DumpChunks(tgt_chunks, num_tgt_chunks); return 1; } } } for (i = 0; i < num_tgt_chunks; ++i) { if (tgt_chunks[i].type == CHUNK_DEFLATE) { // Confirm that given the uncompressed chunk data in the target, we // can recompress it and get exactly the same bits as are in the // input target image. If this fails, treat the chunk as a normal // non-deflated chunk. if (ReconstructDeflateChunk(tgt_chunks+i) < 0) { printf("failed to reconstruct target deflate chunk %d [%s]; " "treating as normal\n", i, tgt_chunks[i].filename); ChangeDeflateChunkToNormal(tgt_chunks+i); if (zip_mode) { ImageChunk* src = FindChunkByName(tgt_chunks[i].filename, src_chunks, num_src_chunks); if (src) { ChangeDeflateChunkToNormal(src); } } else { ChangeDeflateChunkToNormal(src_chunks+i); } continue; } // If two deflate chunks are identical (eg, the kernel has not // changed between two builds), treat them as normal chunks. // This makes applypatch much faster -- it can apply a trivial // patch to the compressed data, rather than uncompressing and // recompressing to apply the trivial patch to the uncompressed // data. ImageChunk* src; if (zip_mode) { src = FindChunkByName(tgt_chunks[i].filename, src_chunks, num_src_chunks); } else { src = src_chunks+i; } if (src == NULL || AreChunksEqual(tgt_chunks+i, src)) { ChangeDeflateChunkToNormal(tgt_chunks+i); if (src) { ChangeDeflateChunkToNormal(src); } } } } // Merging neighboring normal chunks. if (zip_mode) { // For zips, we only need to do this to the target: deflated // chunks are matched via filename, and normal chunks are patched // using the entire source file as the source. MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks); } else { // For images, we need to maintain the parallel structure of the // chunk lists, so do the merging in both the source and target // lists. MergeAdjacentNormalChunks(tgt_chunks, &num_tgt_chunks); MergeAdjacentNormalChunks(src_chunks, &num_src_chunks); if (num_src_chunks != num_tgt_chunks) { // This shouldn't happen. printf("merging normal chunks went awry\n"); return 1; } } // 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)); for (i = 0; i < num_tgt_chunks; ++i) { if (zip_mode) { ImageChunk* src; if (tgt_chunks[i].type == CHUNK_DEFLATE && (src = FindChunkByName(tgt_chunks[i].filename, src_chunks, num_src_chunks))) { patch_data[i] = MakePatch(src, tgt_chunks+i, patch_size+i); } else { patch_data[i] = MakePatch(src_chunks, tgt_chunks+i, patch_size+i); } } else { if (i == 1 && bonus_data) { printf(" using %zu 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 %zd bytes (of %zd)\n", i, patch_size[i], tgt_chunks[i].source_len); } // Figure out how big the imgdiff file header is going to be, so // that we can correctly compute the offset of each bsdiff patch // within the file. size_t total_header_size = 12; for (i = 0; i < num_tgt_chunks; ++i) { total_header_size += 4; switch (tgt_chunks[i].type) { case CHUNK_NORMAL: total_header_size += 8*4; break; case CHUNK_DEFLATE: total_header_size += 8*6 + 4*5; break; case CHUNK_RAW: total_header_size += 4 + patch_size[i]; break; } } size_t offset = total_header_size; FILE* f = fopen(argv[3], "wb"); // Write out the headers. fwrite("IMGDIFFX", 1, 8, f); Write4(num_tgt_chunks, f); for (i = 0; i < num_tgt_chunks; ++i) { Write4(tgt_chunks[i].type, f); switch (tgt_chunks[i].type) { case CHUNK_NORMAL: printf("chunk %3d: normal (%10zd, %10zd) %10zd\n", i, tgt_chunks[i].start, tgt_chunks[i].len, patch_size[i]); Write8(tgt_chunks[i].source_start, f); Write8(tgt_chunks[i].source_len, f); Write8(offset, f); Write8(patch_size[i], f); offset += patch_size[i]; break; case CHUNK_DEFLATE: printf("chunk %3d: deflate (%10zd, %10zd) %10zd %s\n", i, tgt_chunks[i].start, tgt_chunks[i].deflate_len, patch_size[i], tgt_chunks[i].filename); Write8(tgt_chunks[i].source_start, f); Write8(tgt_chunks[i].source_len, f); Write8(offset, f); Write8(patch_size[i], f); Write8(tgt_chunks[i].source_uncompressed_len, f); Write8(tgt_chunks[i].len, f); Write4(tgt_chunks[i].level, f); Write4(tgt_chunks[i].method, f); Write4(tgt_chunks[i].windowBits, f); Write4(tgt_chunks[i].memLevel, f); Write4(tgt_chunks[i].strategy, f); offset += patch_size[i]; break; case CHUNK_RAW: printf("chunk %3d: raw (%10zd, %10zd)\n", i, tgt_chunks[i].start, tgt_chunks[i].len); Write4(patch_size[i], f); fwrite(patch_data[i], 1, patch_size[i], f); break; } } // Append each chunk's bsdiff patch, in order. for (i = 0; i < num_tgt_chunks; ++i) { if (tgt_chunks[i].type != CHUNK_RAW) { fwrite(patch_data[i], 1, patch_size[i], f); } } fclose(f); return 0; }