Exemplo n.º 1
0
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;
}
Exemplo n.º 2
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;
}