imgdiff工具使用

2017/11/21 Android recovery

imgdiff 源码分析

前言

说到imgdiff,就不得不先提一下bsdiff,它是一个二进制差分工具,bsdiff/bspatch,代码位于external/bsdiff目录下。

虽然针对普通文件bsdiff/bspatch效率非常高。但对于一些压缩文件,由于压缩前内容非常小的变化会导致压缩后整个二进制文件产生非常巨大的变化,这样patch文件会非常大。如Android系统中常用的boot.img/recovery.img,本质上就是一块包含压缩和非压缩数据的二进制文件。

为了解决这个问题,Android也提供了imgdiff工具(bootable/recovery/applypatch)。在source文件中搜索压缩部分(0x1f8b0800),将其划分成多个小块。 非压缩部分称为normal,直接使用bsdiff生成补丁。gzip压缩部分成为GZIP类型,先将内容解压缩,逐个文件使用bsdiff生成补丁,将这些补丁通过GZIP再压缩到一起。最后将各部分的补丁合并到一起添加一个文件头生成最终的补丁文件。

imgdiff流程 分析

  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;
  }

首先用于解析imgdiff命令,可以看到imgdiff 可以使用额外的参数“-b”(用于使用bonusdata),”-z”(用于使用压缩包的patch)。

ReadImage(argv[1], &num_src_chunks, &src_chunks)

调用ReadImage函数解析img文件,我们知道boot.img是由压缩和非压缩文件构成的,那ReadImage函数就将img文件分解成各个chunk,关于boot.img文件的构造可以参考:android boot.img 结构 需要注意的是,不能随意改变img的构造,imgdiff工具中的注释有一段

Android boot and recovery images currently
 * consist of five chunks:  a small normal header, a gzipped kernel, a
 *  * small normal section, a gzipped ramdisk, and finally a small normal
 *   * footer.

之后的代码就不进行详细分析了,基本都是处理操作,对各个不同的chunk执行不同的patch操作

applypatch

对recovery.img镜像进行操作的流程:init.rc->install-recovery.sh->applypatch->通过boot.img升级recovery.img 我们先来看一下install-recovery.sh的代码

#!/system/bin/sh
if ! applypatch -c EMMC:/dev/block/platform/13500000.dwmmc0/by-name/recovery:   25198864:c62a38bc59eb26d5270645144b510b072d3b9fa0; then
  applypatch -b /system/etc/recovery-resource.dat EMMC:/dev/block/platform/     13500000.dwmmc0/by-name/boot:16765200:0ca2eb5c05f87c87203dcd847a5e10ce94058a0d  EMMC:/dev/block/platform/13500000.dwmmc0/by-name/recovery                       c62a38bc59eb26d5270645144b510b072d3b9fa0 25198864                               0ca2eb5c05f87c87203dcd847a5e10ce94058a0d:/system/recovery-from-boot.p && log -t recovery "Installing new recovery image: succeeded" || log -t recovery          "Installing new recovery image: failed"
else
  log -t recovery "Recovery image already installed"
fi

Search

    Table of Contents