Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 02d444b0 authored by Doug Zongker's avatar Doug Zongker
Browse files

new image diffing tool and support for image patches in applypatch

Images (like boot and recovery) consist of large sections of gzipped
data interspersed with other data.  To do effective binary patching of
these files, we need to apply patches to the gzipped parts in
'uncompressed space', that is, we decompress, apply a patch, then
recompress to obtain the desired output.

This change defines a new format with these patches, which is
basically a description of how the source and target files are to be
divided up into chunks and a bsdiff patch for each chunk.  We add a
new host executable, "imgdiff", for generating these patches from
source and target images, and add support in applypatch for
recognizing this format and applying it on the device.
parent 8b70e8c6
Loading
Loading
Loading
Loading
+16 −5
Original line number Diff line number Diff line
@@ -12,18 +12,29 @@
# See the License for the specific language governing permissions and
# limitations under the License.

ifneq ($(TARGET_SIMULATOR),true)

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

ifneq ($(TARGET_SIMULATOR),true)

LOCAL_SRC_FILES := applypatch.c bsdiff.c freecache.c
LOCAL_SRC_FILES := applypatch.c bsdiff.c freecache.c imgpatch.c
LOCAL_MODULE := applypatch
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_TAGS := eng
LOCAL_C_INCLUDES += external/bzip2
LOCAL_STATIC_LIBRARIES += libmincrypt libbz libc
LOCAL_C_INCLUDES += external/bzip2 external/zlib
LOCAL_STATIC_LIBRARIES += libmincrypt libbz libz libc

include $(BUILD_EXECUTABLE)

include $(CLEAR_VARS)

LOCAL_SRC_FILES := imgdiff.c
LOCAL_MODULE := imgdiff
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_TAGS := eng
LOCAL_C_INCLUDES += external/zlib
LOCAL_STATIC_LIBRARIES += libz

include $(BUILD_HOST_EXECUTABLE)

endif  # !TARGET_SIMULATOR
+9 −1
Original line number Diff line number Diff line
@@ -431,11 +431,19 @@ int main(int argc, char** argv) {
  } else if (header_bytes_read >= 8 &&
             memcmp(header, "BSDIFF40", 8) == 0) {
    int result = ApplyBSDiffPatch(source_to_use->data, source_to_use->size,
                                  patch_filename, output, &ctx);
                                  patch_filename, 0, output, &ctx);
    if (result != 0) {
      fprintf(stderr, "ApplyBSDiffPatch failed\n");
      return result;
    }
  } else if (header_bytes_read >= 8 &&
             memcmp(header, "IMGDIFF1", 8) == 0) {
    int result = ApplyImagePatch(source_to_use->data, source_to_use->size,
                                 patch_filename, output, &ctx);
    if (result != 0) {
      fprintf(stderr, "ApplyImagePatch failed\n");
      return result;
    }
  } else {
    fprintf(stderr, "Unknown patch file format");
    return 1;
+8 −0
Original line number Diff line number Diff line
@@ -44,6 +44,14 @@ size_t FreeSpaceForFile(const char* filename);
// bsdiff.c
void ShowBSDiffLicense();
int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
                     const char* patch_filename, ssize_t offset,
                     FILE* output, SHA_CTX* ctx);
int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
                        const char* patch_filename, ssize_t patch_offset,
                        unsigned char** new_data, ssize_t* new_size);

// imgpatch.c
int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
                     const char* patch_filename,
                     FILE* output, SHA_CTX* ctx);

+42 −22
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include <bzlib.h>

#include "mincrypt/sha.h"
#include "applypatch.h"

void ShowBSDiffLicense() {
  puts("The bsdiff library used herein is:\n"
@@ -80,10 +81,34 @@ static off_t offtin(u_char *buf)
  return y;
}


int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
                     const char* patch_filename,
                     const char* patch_filename, ssize_t patch_offset,
                     FILE* output, 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 (fwrite(new_data, 1, new_size, output) < new_size) {
    fprintf(stderr, "short write of output: %d (%s)\n", errno, strerror(errno));
    return 1;
  }
  if (ctx) {
    SHA_update(ctx, new_data, new_size);
  }
  free(new_data);

  return 0;
}

int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
                        const char* patch_filename, ssize_t patch_offset,
                        unsigned char** new_data, ssize_t* new_size) {

  FILE* f;
  if ((f = fopen(patch_filename, "rb")) == NULL) {
    fprintf(stderr, "failed to open patch file\n");
@@ -102,6 +127,8 @@ int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
  // 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");
@@ -109,17 +136,16 @@ int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
  }

  if (memcmp(header, "BSDIFF40", 8) != 0) {
    fprintf(stderr, "corrupt patch file header (magic number)\n");
    fprintf(stderr, "corrupt bsdiff patch file header (magic number)\n");
    return 1;
  }

  ssize_t ctrl_len, data_len;
  ssize_t new_size;
  ctrl_len = offtin(header+8);
  data_len = offtin(header+16);
  new_size = offtin(header+24);
  *new_size = offtin(header+24);

  if (ctrl_len < 0 || data_len < 0 || new_size < 0) {
  if (ctrl_len < 0 || data_len < 0 || *new_size < 0) {
    fprintf(stderr, "corrupt patch file header (data lengths)\n");
    return 1;
  }
@@ -135,7 +161,7 @@ int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
    fprintf(stderr, "failed to open patch file\n");                      \
    return 1;                                                            \
  }                                                                      \
  if (fseeko(f, offset, SEEK_SET)) {                                     \
  if (fseeko(f, offset+patch_offset, SEEK_SET)) {                        \
    fprintf(stderr, "failed to seek in patch file\n");                   \
    return 1;                                                            \
  }                                                                      \
@@ -150,9 +176,10 @@ int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,

#undef OPEN_AT

  unsigned char* new_data = malloc(new_size);
  if (new_data == NULL) {
    fprintf(stderr, "failed to allocate memory for output file\n");
  *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;
  }

@@ -161,7 +188,7 @@ int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
  off_t len_read;
  int i;
  unsigned char buf[8];
  while (newpos < new_size) {
  while (newpos < *new_size) {
    // Read control data
    for (i = 0; i < 3; ++i) {
      len_read = BZ2_bzRead(&bzerr, cpfbz2, buf, 8);
@@ -173,13 +200,13 @@ int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
    }

    // Sanity check
    if (newpos + ctrl[0] > new_size) {
    if (newpos + ctrl[0] > *new_size) {
      fprintf(stderr, "corrupt patch (new file overrun)\n");
      return 1;
    }

    // Read diff string
    len_read = BZ2_bzRead(&bzerr, dpfbz2, new_data + newpos, ctrl[0]);
    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;
@@ -188,7 +215,7 @@ int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
    // 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];
        (*new_data)[newpos+i] += old_data[oldpos+i];
      }
    }

@@ -197,13 +224,13 @@ int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
    oldpos += ctrl[0];

    // Sanity check
    if (newpos + ctrl[1] > new_size) {
    if (newpos + ctrl[1] > *new_size) {
      fprintf(stderr, "corrupt patch (new file overrun)\n");
      return 1;
    }

    // Read extra string
    len_read = BZ2_bzRead(&bzerr, epfbz2, new_data + newpos, ctrl[1]);
    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;
@@ -221,12 +248,5 @@ int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
  fclose(dpf);
  fclose(epf);

  if (fwrite(new_data, 1, new_size, output) < new_size) {
    fprintf(stderr, "short write of output: %d (%s)\n", errno, strerror(errno));
    return 1;
  }
  SHA_update(ctx, new_data, new_size);
  free(new_data);

  return 0;
}
+560 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading