六、Android recovery升级断电保护机制

本篇将具体分析执行写入的流程,,整理完该流程后,将通过升级的log具体分析断电后重新升级的情况

 

一.如何断电后重启继续进入升级

几句话总结

1.调用framwork接口时会将command写入misc分区

2.升级过程中加入retry标志位,如果第二次升级,会将retry++

3.misc分区的标志位只有在finlsh_recovery方法中,即使升级过程中掉电,重新上电后,misc分区依然是boot-recovey,保证继续跑recovery的流程

二.update-script恢复执行流程

几句话总结

1.调用install_package方法中retry=1,判定为恢复升级

2.校验阶段,会解析transfer.list,move.bisdiff部分如果校验发现已经是升级后的block,则判定校验通过,对于stash部分,优先从上次保存的stash数据中读取进行校验

3.写入阶段,依然会解析transfer.list,首次升级过程中保存了last_command_index,会从中断处继续执行写入,已经写入成功的部分会被跳过

update-scrypt脚本是由updater解析和执行的,所以我们要对照失败包分析updater的代码,首先回忆下上一篇updae.cpp的main方法

1.前情回顾

int main(int argc, char** argv) {
  // Various things log information to stdout or stderr more or less
  // at random (though we've tried to standardize on stdout).  The
  // log file makes more sense if buffering is turned off so things
  // appear in the right order.
  setbuf(stdout, nullptr);
  setbuf(stderr, nullptr);

  // We don't have logcat yet under recovery. Update logs will always be written to stdout
  // (which is redirected to recovery.log).
  android::base::InitLogging(argv, &UpdaterLogger);
  //我们执行update-binary,首先还是看下时什么参数传进了main方法,以上次的经验,还是放在initlog之后
  fprintf(stderr, "\n============updater.cpp main============\n");
  int i;
  for (i = 0; i < argc; i++){
    fprintf(stderr, "argc && argv we need know how much the argc argv\n");
    fprintf(stderr, "argv %d is %s\n", i, argv[i]);
  }
  printf("\n========================================\n");
  if (argc != 4 && argc != 5) {
    LOG(ERROR) << "unexpected number of arguments: " << argc;
    return 1;
  }

  char* version = argv[1];
  if ((version[0] != '1' && version[0] != '2' && version[0] != '3') || version[1] != '\0') {
    // We support version 1, 2, or 3.
    LOG(ERROR) << "wrong updater binary API; expected 1, 2, or 3; got " << argv[1];
    return 2;
  }

  // Set up the pipe for sending commands back to the parent process.
  int fd = atoi(argv[2]);
  FILE* cmd_pipe = fdopen(fd, "wb");
  setlinebuf(cmd_pipe);

  // Extract the script from the package.
  // 从升级包中提取出updater-scrypt
  const char* package_filename = argv[3];
  //加载内存空间
  MemMapping map;
  if (!map.MapFile(package_filename)) {
    LOG(ERROR) << "failed to map package " << argv[3];
    return 3;
  }
  //获取压缩包
  ZipArchiveHandle za;
  int open_err = OpenArchiveFromMemory(map.addr, map.length, argv[3], &za);
  if (open_err != 0) {
    LOG(ERROR) << "failed to open package " << argv[3] << ": " << ErrorCodeString(open_err);
    CloseArchive(za);
    return 3;
  }
  //获取updater-scrypt脚本,读取到内存中
  ZipString script_name(SCRIPT_NAME);
  ZipEntry script_entry;
  int find_err = FindEntry(za, script_name, &script_entry);
  if (find_err != 0) {
    LOG(ERROR) << "failed to find " << SCRIPT_NAME << " in " << package_filename << ": "
               << ErrorCodeString(find_err);
    CloseArchive(za);
    return 4;
  }

  std::string script;
  script.resize(script_entry.uncompressed_length);
  int extract_err = ExtractToMemory(za, &script_entry, reinterpret_cast<uint8_t*>(&script[0]),
                                    script_entry.uncompressed_length);
  if (extract_err != 0) {
    LOG(ERROR) << "failed to read script from package: " << ErrorCodeString(extract_err);
    CloseArchive(za);
    return 5;
  }
#if 1
    //打印出脚本的内容
    fprintf(stderr, "====== Updater-Script:\n");
    fprintf(stderr, "%s\n\n", script.c_str());
#endif
  // Configure edify's functions.
  //注册脚本中语句处理函数,识别脚本中的命令
  RegisterBuiltins();
  RegisterInstallFunctions();
  RegisterBlockImageFunctions();
  RegisterDeviceExtensions();

  // Parse the script.
  // 智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象, unique_ptr 独占所指向的对象
  // 解析update-scrypt脚本
  std::unique_ptr<Expr> root;
  int error_count = 0;
  int error = parse_string(script.c_str(), &root, &error_count);
  if (error != 0 || error_count > 0) {
    LOG(ERROR) << error_count << " parse errors";
    CloseArchive(za);
    return 6;
  }

  sehandle = selinux_android_file_context_handle();
  selinux_android_set_sehandle(sehandle);

  if (!sehandle) {
    fprintf(cmd_pipe, "ui_print Warning: No file_contexts\n");
  }

  // Evaluate the parsed script. 执行解析后的脚本

  UpdaterInfo updater_info;
  updater_info.cmd_pipe = cmd_pipe;
  updater_info.package_zip = za;
  updater_info.version = atoi(version);
  updater_info.package_zip_addr = map.addr;
  updater_info.package_zip_len = map.length;

  State state(script, &updater_info);
  //加入retry标准位,判断是否为二次升级
  if (argc == 5) {
    if (strcmp(argv[4], "retry") == 0) {
      state.is_retry = true;
    } else {
      printf("unexpected argument: %s", argv[4]);
    }
  }
  ota_io_init(za, state.is_retry);

  std::string result;
  //执行脚本,并获取的返回值
  bool status = Evaluate(&state, root, &result);

  if (have_eio_error) {
    fprintf(cmd_pipe, "retry_update\n");
  }
  //根据status判断是否升级成功,如果失败,打印出对应的log
  if (!status) {
    if (state.errmsg.empty()) {
      LOG(ERROR) << "script aborted (no error message)";
      fprintf(cmd_pipe, "ui_print script aborted (no error message)\n");
    } else {
      LOG(ERROR) << "script aborted: " << state.errmsg;
      const std::vector<std::string> lines = android::base::Split(state.errmsg, "\n");
      for (const std::string& line : lines) {
        // Parse the error code in abort message.
        // Example: "E30: This package is for bullhead devices."
        if (!line.empty() && line[0] == 'E') {
          if (sscanf(line.c_str(), "E%d: ", &state.error_code) != 1) {
            LOG(ERROR) << "Failed to parse error code: [" << line << "]";
          }
        }
        fprintf(cmd_pipe, "ui_print %s\n", line.c_str());
      }
    }

    // Installation has been aborted. Set the error code to kScriptExecutionFailure unless
    // a more specific code has been set in errmsg.
    if (state.error_code == kNoError) {
      state.error_code = kScriptExecutionFailure;
    }
    fprintf(cmd_pipe, "log error: %d\n", state.error_code);
    // Cause code should provide additional information about the abort.
    if (state.cause_code != kNoCause) {
      fprintf(cmd_pipe, "log cause: %d\n", state.cause_code);
      if (state.cause_code == kPatchApplicationFailure) {
        LOG(INFO) << "Patch application failed, retry update.";
        fprintf(cmd_pipe, "retry_update\n");
      }
    }

    if (updater_info.package_zip) {
      CloseArchive(updater_info.package_zip);
    }
    return 7;
  } else {
    fprintf(cmd_pipe, "ui_print script succeeded: result was [%s]\n", result.c_str());
  }

  if (updater_info.package_zip) {
    CloseArchive(updater_info.package_zip);
  }

  return 0;
}

RegisterBlockImageFunctions();

updater.cpp
void RegisterBlockImageFunctions() {
  RegisterFunction("block_image_verify", BlockImageVerifyFn);
  RegisterFunction("block_image_update", BlockImageUpdateFn);
  RegisterFunction("block_image_recover", BlockImageRecoverFn);
  RegisterFunction("check_first_block", CheckFirstBlockFn);
  RegisterFunction("range_sha1", RangeSha1Fn);
}

 

开始进入正题,对scrypt脚本中比较重要的部分进行分析,可以分为两个模块,执行校验和执行写入,这里只分析system的情况,vendor和product的情况类似

1.range_sha1

2.block_image_verify

3.check_first_block

4.block_image_recover

5.block_image_update

if (range_sha1("/dev/block/platform/bootdevice/by-name/system", "294,1,430,666,674,675,676,698,701,705,706,783,784,4944,4945,6224,6225,7870,7871,7872,7876,8065,8069,8332,8333,10077,10079,11773,11775,11807,11810,11811,11813,11822,11823,11832,11834,11836,11838,11841,11844,11846,11848,11860,11863,12348,12350,12716,12718,13134,13136,13164,13165,13325,13326,13483,13484,13695,13696,13939,13940,14035,14036,14143,14144,15075,15076,15248,15249,15593,15594,17039,17040,17065,17066,17837,17838,18608,18609,18873,18874,19122,19123,19124,19125,19719,19720,19935,19936,20076,20077,21520,21521,21547,21548,21549,21550,25122,25123,25139,25140,25253,25254,25470,25471,25616,25617,25811,25812,25854,25855,25931,25932,27882,27884,27928,27929,27944,27945,27946,27948,29246,29247,29351,29352,29560,29564,30079,30082,30597,30602,30873,30876,30879,30881,30892,30893,31299,31303,31942,31943,31953,31954,31971,31975,31994,31995,32013,32017,32056,32059,32069,32071,32134,32139,32185,32186,32205,32209,32227,32228,32235,32237,32246,32248,32251,32252,32508,32510,32768,32770,32927,32928,33555,33557,33566,33567,33573,33577,33582,33583,33593,33594,33602,33604,33610,33612,33615,33616,33629,33633,33644,33649,33937,33938,33945,33947,33953,33955,33958,33959,34472,34476,34592,34594,34600,34601,34741,34745,34931,34932,35021,35023,35170,35172,35177,35180,35473,35477,36049,36050,36052,36057,50293,50294,53560,53561,53570,53574,53583,53584,53593,53597,53602,53607,62299,62300,65536,65537,75076,75081,75095,75096,75173,75177,75345,75346,75405,75407,75479,75481,75486,75489,75516,75518,75546,98306,98463,98464,98970,131073,131579,163842,163999,164000,164506,196609,197115,229378,229535,229536,230042,262145,262651,294914,295071,295072,295578,327681,328187,360449,360955,393217,393723,425985,426491,447444,622592,622593,645127,648706,649730,650210,650217,655360") == "8a083377999ff3720a5a650d10ab597bf92f826c" || block_image_verify("/dev/block/platform/bootdevice/by-name/system", package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat")) then
ui_print("Verified system image...");
else
check_first_block("/dev/block/platform/bootdevice/by-name/system");
ifelse (block_image_recover("/dev/block/platform/bootdevice/by-name/system", "294,1,430,666,674,675,676,698,701,705,706,783,784,4944,4945,6224,6225,7870,7871,7872,7876,8065,8069,8332,8333,10077,10079,11773,11775,11807,11810,11811,11813,11822,11823,11832,11834,11836,11838,11841,11844,11846,11848,11860,11863,12348,12350,12716,12718,13134,13136,13164,13165,13325,13326,13483,13484,13695,13696,13939,13940,14035,14036,14143,14144,15075,15076,15248,15249,15593,15594,17039,17040,17065,17066,17837,17838,18608,18609,18873,18874,19122,19123,19124,19125,19719,19720,19935,19936,20076,20077,21520,21521,21547,21548,21549,21550,25122,25123,25139,25140,25253,25254,25470,25471,25616,25617,25811,25812,25854,25855,25931,25932,27882,27884,27928,27929,27944,27945,27946,27948,29246,29247,29351,29352,29560,29564,30079,30082,30597,30602,30873,30876,30879,30881,30892,30893,31299,31303,31942,31943,31953,31954,31971,31975,31994,31995,32013,32017,32056,32059,32069,32071,32134,32139,32185,32186,32205,32209,32227,32228,32235,32237,32246,32248,32251,32252,32508,32510,32768,32770,32927,32928,33555,33557,33566,33567,33573,33577,33582,33583,33593,33594,33602,33604,33610,33612,33615,33616,33629,33633,33644,33649,33937,33938,33945,33947,33953,33955,33958,33959,34472,34476,34592,34594,34600,34601,34741,34745,34931,34932,35021,35023,35170,35172,35177,35180,35473,35477,36049,36050,36052,36057,50293,50294,53560,53561,53570,53574,53583,53584,53593,53597,53602,53607,62299,62300,65536,65537,75076,75081,75095,75096,75173,75177,75345,75346,75405,75407,75479,75481,75486,75489,75516,75518,75546,98306,98463,98464,98970,131073,131579,163842,163999,164000,164506,196609,197115,229378,229535,229536,230042,262145,262651,294914,295071,295072,295578,327681,328187,360449,360955,393217,393723,425985,426491,447444,622592,622593,645127,648706,649730,650210,650217,655360") && block_image_verify("/dev/block/platform/bootdevice/by-name/system", package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat"), ui_print("system recovered successfully."), abort("E1004: system partition fails to recover"));
endif;
if (range_sha1("/dev/block/platform/bootdevice/by-name/vendor", "134,1,125,472,483,1720,1721,3963,3965,4552,4553,6502,6504,6519,6520,6535,6536,6582,6584,6941,6944,7223,7224,7768,7769,8067,8068,8403,8404,9872,9873,14221,14222,15475,15476,16519,16520,16756,16757,18377,18378,19010,19011,21856,21857,29115,29116,30085,30086,30463,30465,30473,30475,30502,30503,30508,30509,30538,30539,30574,30575,30673,30675,30683,30684,30703,30704,30743,30744,30905,30906,30907,30909,30923,30924,30929,30930,30940,30941,31494,31495,31527,31528,31979,31980,32768,32770,32803,32804,34904,34905,35433,35434,41785,41786,45487,45488,46020,46021,48394,48395,48636,48637,51841,51842,56276,56277,57302,57303,65536,65537,66830,66831,68681,68682,70651,70653,70658,70659,70664,70665,70670,70671,70709,70712,70716,70718,98304,98306,131072,131073,139096,140194,140201,141312") == "40c9fefe4c4c61b55dc1a6faea1c12971f1f05fb" || block_image_verify("/dev/block/platform/bootdevice/by-name/vendor", package_extract_file("vendor.transfer.list"), "vendor.new.dat", "vendor.patch.dat")) then
ui_print("Verified vendor image...");
else
check_first_block("/dev/block/platform/bootdevice/by-name/vendor");
ifelse (block_image_recover("/dev/block/platform/bootdevice/by-name/vendor", "134,1,125,472,483,1720,1721,3963,3965,4552,4553,6502,6504,6519,6520,6535,6536,6582,6584,6941,6944,7223,7224,7768,7769,8067,8068,8403,8404,9872,9873,14221,14222,15475,15476,16519,16520,16756,16757,18377,18378,19010,19011,21856,21857,29115,29116,30085,30086,30463,30465,30473,30475,30502,30503,30508,30509,30538,30539,30574,30575,30673,30675,30683,30684,30703,30704,30743,30744,30905,30906,30907,30909,30923,30924,30929,30930,30940,30941,31494,31495,31527,31528,31979,31980,32768,32770,32803,32804,34904,34905,35433,35434,41785,41786,45487,45488,46020,46021,48394,48395,48636,48637,51841,51842,56276,56277,57302,57303,65536,65537,66830,66831,68681,68682,70651,70653,70658,70659,70664,70665,70670,70671,70709,70712,70716,70718,98304,98306,131072,131073,139096,140194,140201,141312") && block_image_verify("/dev/block/platform/bootdevice/by-name/vendor", package_extract_file("vendor.transfer.list"), "vendor.new.dat", "vendor.patch.dat"), ui_print("vendor recovered successfully."), abort("E2004: vendor partition fails to recover"));
endif;

# ---- start making changes here ----

ui_print("Patching system image after verification.");
show_progress(0.800000, 0);
block_image_update("/dev/block/platform/bootdevice/by-name/system", package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat") ||
  abort("E1001: Failed to update system image.");
ui_print("Patching vendor image after verification.");
show_progress(0.100000, 0);
block_image_update("/dev/block/platform/bootdevice/by-name/vendor", package_extract_file("vendor.transfer.list"), "vendor.new.dat", "vendor.patch.dat") ||
  abort("E2001: Failed to update vendor image.");

2.range_sha1  RangeSha1Fn方法

range_sha1("/dev/block/platform/bootdevice/by-name/system", "294,1,430,666,674,675,676,698,701,705,706,783,784,4944,4945,6224,6225,7870,7871,7872,7876,8065,80.......") == "8a083377999ff3720a5a650d10ab597bf92f826c"

这里面的参数值怎么来的呢?

2.1 参数分析

 

ota_from_target_files

def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
......
......
  # Verify the existing partitions.
  system_diff.WriteVerifyScript(script, touched_blocks_only=True)
  if vendor_diff:
    vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)

 

  def WriteVerifyScript(self, script, touched_blocks_only=False):
    partition = self.partition

    # full OTA
    if not self.src:
      script.Print("Image %s will be patched unconditionally." % (partition,))

    # incremental OTA
    else:
      #差分情况下touched_blocks_only=True
      if touched_blocks_only:
        #这两个参数都源于blockimgdiff.py,因为比较简单,不再做具体说明
        #self.touched_src_ranges = self.touched_src_ranges.union(xf.src_ranges)
        #ranges是src_ranges的并集
        ranges = self.touched_src_ranges
        #self.touched_src_sha1 = self.src.RangeSha1(self.touched_src_ranges)
        #expected_sha1 是ranges的sha1值
        expected_sha1 = self.touched_src_sha1
      else:
        ranges = self.src.care_map.subtract(self.src.clobbered_blocks)
        expected_sha1 = self.src.TotalSha1()

      # No blocks to be checked, skipping.
      if not ranges:
        return

      ranges_str = ranges.to_string_raw()
      #写入升级包的语句
      script.AppendExtra(('if (range_sha1("%s", "%s") == "%s" || '
                          'block_image_verify("%s", '
                          'package_extract_file("%s.transfer.list"), '
                          '"%s.new.dat", "%s.patch.dat")) then') % (
                          self.device, ranges_str, expected_sha1,
                          self.device, partition, partition, partition))
      script.Print('Verified %s image...' % (partition,))
      script.AppendExtra('else')
      
      #如果version 大于等于4,增加以下升级语句
      if self.version >= 4:

        # Bug: 21124327
        # When generating incrementals for the system and vendor partitions in
        # version 4 or newer, explicitly check the first block (which contains
        # the superblock) of the partition to see if it's what we expect. If
        # this check fails, give an explicit log message about the partition
        # having been remounted R/W (the most likely explanation).
        #在为系统分区和供应商分区生成增量时 新增的校验root的方法
        #在版本4或更高版本中,显式检查分区的第一个块(包含超级块),以查看其是否符合我们的期望。 
        #如果此检查失败,请给出有关已重新安装R / W的分区的明确日志消息(最可能的解释)。
        if self.check_first_block:
          script.AppendExtra('check_first_block("%s");' % (self.device,))

        # If version >= 4, try block recovery before abort update
        if partition == "system":
          code = ErrorCode.SYSTEM_RECOVER_FAILURE
        else:
          code = ErrorCode.VENDOR_RECOVER_FAILURE
        script.AppendExtra((
            'ifelse (block_image_recover("{device}", "{ranges}") && '
            'block_image_verify("{device}", '
            'package_extract_file("{partition}.transfer.list"), '
            '"{partition}.new.dat", "{partition}.patch.dat"), '
            'ui_print("{partition} recovered successfully."), '
            'abort("E{code}: {partition} partition fails to recover"));\n'
            'endif;').format(device=self.device, ranges=ranges_str,
                             partition=partition, code=code))

      # Abort the OTA update. Note that the incremental OTA cannot be applied
      # even if it may match the checksum of the target partition.
      # a) If version < 3, operations like move and erase will make changes
      #    unconditionally and damage the partition.
      # b) If version >= 3, it won't even reach here.
      else:
        if partition == "system":
          code = ErrorCode.SYSTEM_VERIFICATION_FAILURE
        else:
          code = ErrorCode.VENDOR_VERIFICATION_FAILURE
        script.AppendExtra((
            'abort("E%d: %s partition has unexpected contents");\n'
            'endif;') % (code, partition))

2.2 方法分析

 

blockimg.cpp
//首先还注意下传入的参数,分区名称基础区间的合集
Value* RangeSha1Fn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
  if (argv.size() != 2) {
    ErrorAbort(state, kArgsParsingFailure, "range_sha1 expects 2 arguments, got %zu", argv.size());
    return StringValue("");
  }

  std::vector<std::unique_ptr<Value>> args;
  if (!ReadValueArgs(state, argv, &args)) {
    return nullptr;
  }
  //获取节点名称
  const std::unique_ptr<Value>& blockdev_filename = args[0];
  //获取区间合集
  const std::unique_ptr<Value>& ranges = args[1];
  //判断参数是不是string类型
  if (blockdev_filename->type != VAL_STRING) {
    ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string", name);
    return StringValue("");
  }
  if (ranges->type != VAL_STRING) {
    ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name);
    return StringValue("");
  }
  //确认分区节点是否可以打开
  android::base::unique_fd fd(ota_open(blockdev_filename->data.c_str(), O_RDWR));
  if (fd == -1) {
    ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", blockdev_filename->data.c_str(),
               strerror(errno));
    return StringValue("");
  }
  //获取区间
  RangeSet rs = RangeSet::Parse(ranges->data);
  CHECK(static_cast<bool>(rs));

  SHA_CTX ctx;
  SHA1_Init(&ctx);
  //创建容器 4096大小的buffer
  std::vector<uint8_t> buffer(BLOCKSIZE);
  //这段代码大致意思是先判断区间中的对应block在分区中能不能打开,如果可以,取对应数据的sha1值
  for (const auto& range : rs) {
    if (!check_lseek(fd, static_cast<off64_t>(range.first) * BLOCKSIZE, SEEK_SET)) {
      ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", blockdev_filename->data.c_str(),
                 strerror(errno));
      return StringValue("");
    }

    for (size_t j = range.first; j < range.second; ++j) {
      if (read_all(fd, buffer, BLOCKSIZE) == -1) {
        ErrorAbort(state, kFreadFailure, "failed to read %s: %s", blockdev_filename->data.c_str(),
                   strerror(errno));
        return StringValue("");
      }
      
      SHA1_Update(&ctx, buffer.data(), BLOCKSIZE);
    }
  }
  uint8_t digest[SHA_DIGEST_LENGTH];
  SHA1_Final(digest, &ctx);
  //返回计算出的sha1值
  return StringValue(print_sha1(digest));
}

看着像是什么都没说,其实时我没细看,大致意思就是sha1的比对

恢复升级该方法将会无法校验通过,将依赖block_image_verify完成校验

3.block_image_verify   block_image_update

这里verify和update走了相同的方法,所以可以放在一起分析

 

Value* BlockImageVerifyFn(const char* name, State* state,
                          const std::vector<std::unique_ptr<Expr>>& argv) {
    // Commands which are not tested are set to nullptr to skip them completely
    const Command commands[] = {
        { "bsdiff",     PerformCommandDiff  },
        { "erase",      nullptr             },
        { "free",       PerformCommandFree  },
        { "imgdiff",    PerformCommandDiff  },
        { "move",       PerformCommandMove  },
        { "new",        nullptr             },
        { "stash",      PerformCommandStash },
        { "zero",       nullptr             }
    };

    // Perform a dry run without writing to test if an update can proceed
    // 这里verify和updae用的同一个方法,差别在于最后一个参数
    return PerformBlockImageUpdate(name, state, argv, commands,
                sizeof(commands) / sizeof(commands[0]), true);
}

Value* BlockImageUpdateFn(const char* name, State* state,
                          const std::vector<std::unique_ptr<Expr>>& argv) {
    const Command commands[] = {
        { "bsdiff",     PerformCommandDiff  },
        { "erase",      PerformCommandErase },
        { "free",       PerformCommandFree  },
        { "imgdiff",    PerformCommandDiff  },
        { "move",       PerformCommandMove  },
        { "new",        PerformCommandNew   },
        { "stash",      PerformCommandStash },
        { "zero",       PerformCommandZero  }
    };

    return PerformBlockImageUpdate(name, state, argv, commands,
                sizeof(commands) / sizeof(commands[0]), false);
}

3.1 PerformBlockImageUpdate

该方法大致总结:

1.对传入的参数进行了解析,设定标志位params.canwrite区分开校验和写入

2.首先解析了transfer.list的前四行 blockimg_verison,写入的总block,同时存储项,最大的存储数据

3.调用CreatStash创建stash目录,cache/recovery/分区hash/ 如果已经存在,则使用以前的

4.调用ParseLastCommandFile解析last_command文件 获取最后升级执行的command赋值给last_command_index

5.从transfer.list第五行开始执行每行cmd命令

6.校验阶段会执行move,diff,stash,同时设定了target_verifyed的标志位,如果校验后target_verifyed=false,说明之前首次升级的时候有失败的地方,校验失败

7.升级阶段,所有的cmd都会执行,根据last_command_index最后的指针,将跳过指针之前的cmd

8.获取返回值,cmd成功执行或者失败

 

veryfy log输出:

[   14.927563] This update is a retry.
[   14.938794] blockimg version is 4
[   14.938881] maximum stash entries 0
[   14.939101] using existing stash /cache/recovery/1fb420b778bead14ec9b2cf264fcaa8e147e3e39/
[   14.940471] 403324928 bytes free on /cache (61513728 needed)

update log输出:

[   50.086541] Patching system image after verification.
[   50.086612] performing update
[   50.086652] This update is a retry.
[   50.097838] blockimg version is 4
[   50.097934] maximum stash entries 0
[   50.098027] using existing stash /cache/recovery/1fb420b778bead14ec9b2cf264fcaa8e147e3e39/
[   50.098817] 403324928 bytes free on /cache (61513728 needed)
[   50.098973] Skipping already executed command: 0, last executed command for previous update: 862
[   50.099030] Skipping already executed command: 1, last executed command for previous update: 862
[   50.099081] Skipping already executed command: 2, last executed command for previous update: 862
[   50.099128] Skipping already executed command: 3, last executed command for previous update: 862
[   50.099175] Skipping already executed command: 4, last executed command for previous update: 862
[   50.099222] Skipping already executed command: 5, last executed command for previous update: 862
[   50.099268] Skipping already executed command: 6, last executed command for previous update: 862
[   50.099315] Skipping already executed command: 7, last executed command for previous update: 862

 

// args:
//    - block device (or file) to modify in-place
//    - transfer list (blob)
//    - new data stream (filename within package.zip)
//    - patch stream (filename within package.zip, must be uncompressed)

// 现在看verify的部分有个问题1.看logverify部分只走了stash的部分
static Value* PerformBlockImageUpdate(const char* name, State* state,
                                      const std::vector<std::unique_ptr<Expr>>& argv,
                                     const Command* commands, size_t cmdcount, bool dryrun) {
  //创建命令参数字典
  CommandParameters params = {};
  //根据传入的dryrun判断是不是需要写入
  params.canwrite = !dryrun;

  LOG(INFO) << "performing " << (dryrun ? "verification" : "update");
  //根据is_retry标志位判断是不是恢复升级
  if (state->is_retry) {
    is_retry = true;
    LOG(INFO) << "This update is a retry.";
  }
  //这里是update-scrypt中调用verify传入的四个参数
  if (argv.size() != 4) {
    ErrorAbort(state, kArgsParsingFailure, "block_image_update expects 4 arguments, got %zu",
               argv.size());
    return StringValue("");
  }

  std::vector<std::unique_ptr<Value>> args;
  if (!ReadValueArgs(state, argv, &args)) {
    return nullptr;
  }
  //"/dev/block/platform/bootdevice/by-name/system"
  const std::unique_ptr<Value>& blockdev_filename = args[0];
  //package_extract_file("system.transfer.list")
  const std::unique_ptr<Value>& transfer_list_value = args[1];
  //system.new.dat
  const std::unique_ptr<Value>& new_data_fn = args[2];
  //system.patch.dat
  const std::unique_ptr<Value>& patch_data_fn = args[3];

  //判断参数的格式
  if (blockdev_filename->type != VAL_STRING) {
    ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string", name);
    return StringValue("");
  }
  if (transfer_list_value->type != VAL_BLOB) {
    ErrorAbort(state, kArgsParsingFailure, "transfer_list argument to %s must be blob", name);
    return StringValue("");
  }
  if (new_data_fn->type != VAL_STRING) {
    ErrorAbort(state, kArgsParsingFailure, "new_data_fn argument to %s must be string", name);
    return StringValue("");
  }
  if (patch_data_fn->type != VAL_STRING) {
    ErrorAbort(state, kArgsParsingFailure, "patch_data_fn argument to %s must be string", name);
    return StringValue("");
  }

  UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie);
  if (ui == nullptr) {
    return StringValue("");
  }

  FILE* cmd_pipe = ui->cmd_pipe;
  ZipArchiveHandle za = ui->package_zip;

  if (cmd_pipe == nullptr || za == nullptr) {
    return StringValue("");
  }
  //数据输出为string
  ZipString path_data(patch_data_fn->data.c_str());
  ZipEntry patch_entry;
  if (FindEntry(za, path_data, &patch_entry) != 0) {
    LOG(ERROR) << name << "(): no file \"" << patch_data_fn->data << "\" in package";
    return StringValue("");
  }

  params.patch_start = ui->package_zip_addr + patch_entry.offset;
  ZipString new_data(new_data_fn->data.c_str());
  ZipEntry new_entry;
  if (FindEntry(za, new_data, &new_entry) != 0) {
    LOG(ERROR) << name << "(): no file \"" << new_data_fn->data << "\" in package";
    return StringValue("");
  }

  params.fd.reset(TEMP_FAILURE_RETRY(ota_open(blockdev_filename->data.c_str(), O_RDWR)));
  if (params.fd == -1) {
    PLOG(ERROR) << "open \"" << blockdev_filename->data << "\" failed";
    return StringValue("");
  }
  //verity的情况下该标志位为false,所以跳过这个方法
  if (params.canwrite) {
    params.nti.za = za;
    params.nti.entry = new_entry;
    params.nti.brotli_compressed = android::base::EndsWith(new_data_fn->data, ".br");
    if (params.nti.brotli_compressed) {
      // Initialize brotli decoder state.
      params.nti.brotli_decoder_state = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
    }
    params.nti.receiver_available = true;

    pthread_mutex_init(&params.nti.mu, nullptr);
    pthread_cond_init(&params.nti.cv, nullptr);
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

    int error = pthread_create(&params.thread, &attr, unzip_new_data, &params.nti);
    if (error != 0) {
      PLOG(ERROR) << "pthread_create failed";
      return StringValue("");
    }
  }
  //对transfer.list数据进行分割,如果容器的size小于2 则报错
  std::vector<std::string> lines = android::base::Split(transfer_list_value->data, "\n");
  if (lines.size() < 2) {
    ErrorAbort(state, kArgsParsingFailure, "too few lines in the transfer list [%zd]",
               lines.size());
    return StringValue("");
  }

  // First line in transfer list is the version number.
  //判断transfer.list首行的参数,blockimg version应该为3或者4
  if (!android::base::ParseInt(lines[0], &params.version, 3, 4)) {
    LOG(ERROR) << "unexpected transfer list version [" << lines[0] << "]";
    return StringValue("");
  }

  LOG(INFO) << "blockimg version is " << params.version;

  // Second line in transfer list is the total number of blocks we expect to write.
  // 第二行时需要写入的总block数
  size_t total_blocks;
  if (!android::base::ParseUint(lines[1], &total_blocks)) {
    ErrorAbort(state, kArgsParsingFailure, "unexpected block count [%s]", lines[1].c_str());
    return StringValue("");
  }

  if (total_blocks == 0) {
    return StringValue("t");
  }

  size_t start = 2;
  if (lines.size() < 4) {
    ErrorAbort(state, kArgsParsingFailure, "too few lines in the transfer list [%zu]",
               lines.size());
    return StringValue("");
  }

  // Third line is how many stash entries are needed simultaneously.
  // 第三行是有多少需要同时存储项
  LOG(INFO) << "maximum stash entries " << lines[2];

  // Fourth line is the maximum number of blocks that will be stashed simultaneously
  // 第四行是可同时存放的最大块数,这也是脚本中判断cache所需空间的大小
  size_t stash_max_blocks;
  if (!android::base::ParseUint(lines[3], &stash_max_blocks)) {
    ErrorAbort(state, kArgsParsingFailure, "unexpected maximum stash blocks [%s]",
               lines[3].c_str());
    return StringValue("");
  }
  //制造存放数据,stash_max_blocks存储的最大block数,blockdev_filename 挂载节点名称
  int res = CreateStash(state, stash_max_blocks, blockdev_filename->data, params.stashbase);
  if (res == -1) {
    return StringValue("");
  }
  //对于恢复升级 res=0
  params.createdstash = res;

  // When performing an update, save the index and cmdline of the current command into
  // the last_command_file if this command writes to the stash either explicitly of implicitly.
  // Upon resuming an update, read the saved index first; then
  //   1. In verification mode, check if the 'move' or 'diff' commands before the saved index has
  //      the expected target blocks already. If not, these commands cannot be skipped and we need
  //      to attempt to execute them again. Therefore, we will delete the last_command_file so that
  //      the update will resume from the start of the transfer list.
  //   2. In update mode, skip all commands before the saved index. Therefore, we can avoid deleting
  //      stashes with duplicate id unintentionally (b/69858743); and also speed up the update.
  // If an update succeeds or is unresumable, delete the last_command_file.
  //执行更新时,如果此命令隐式写入显存中,则将当前命令的索引和cmdline保存到last_command_file中。 恢复更新后,请先读取保存的索引。 然后
  //1.在验证模式下,检查保存的索引之前的“ move”或“ diff”命令是否已具有预期的目标块。 如果没有,则不能跳过这些命令,我们需要尝试再次执行它们。 
  // 因此,我们将删除last_command_file,以便更新将从传输列表的开头恢复。
  //2.在更新模式下,在保存的索引之前跳过所有命令。 因此,我们可以避免意外删除具有重复ID的存储区(b / 69858743);如果更新成功或无法恢复,请删除last_command_file。
  // 定义保存的last_command的索引
  int saved_last_command_index;
  //判断是否读取完成
  if (!ParseLastCommandFile(&saved_last_command_index)) {
    DeleteLastCommandFile();
    // We failed to parse the last command, set it explicitly to -1.
    saved_last_command_index = -1;
  }

  start += 2;

  // Build a map of the available commands 建立可用指令的map
  std::unordered_map<std::string, const Command*> cmd_map;
  for (size_t i = 0; i < cmdcount; ++i) {
    if (cmd_map.find(commands[i].name) != cmd_map.end()) {
      LOG(ERROR) << "Error: command [" << commands[i].name << "] already exists in the cmd map.";
      return StringValue(strdup(""));
    }
    cmd_map[commands[i].name] = &commands[i];
  }

  int rc = -1;

  // Subsequent lines are all individual transfer commands 后续的行都是单独的传输指令
  // 这还是继续处理transfer.list的内容,整了前四行之后的,都是一行一行的命令
  for (size_t i = start; i < lines.size(); i++) {
    const std::string& line = lines[i];
    if (line.empty()) continue;
    //每一行以空格进行分割
    params.tokens = android::base::Split(line, " ");
    params.cpos = 0;
    //如果i大于init类型的最大值,取cmdindex为-1
    if (i - start > std::numeric_limits<int>::max()) {
      params.cmdindex = -1;
    } else {
      //params.cmdindex 时从1开始的
      params.cmdindex = i - start;
    }
    //cmd的名称 比如basdiff move等 执行该行代码之后之后params.cpos++ = 1
    params.cmdname = params.tokens[params.cpos++].c_str();
    //一整行的数据
    params.cmdline = line.c_str();
    params.target_verified = false;
    
    //如果在整个cmd_map里没找到 则报错
    if (cmd_map.find(params.cmdname) == cmd_map.end()) {
      LOG(ERROR) << "unexpected command [" << params.cmdname << "]";
      goto pbiudone;
    }
    //定义cmd 对应的执行的方法
    const Command* cmd = cmd_map[params.cmdname];

    // Skip the command if we explicitly set the corresponding function pointer to nullptr, e.g.
    // "erase" during block_image_verify.
    //如果对应执行的方法时空的,那么输出logs 跳过该指令行
    if (cmd->f == nullptr) {
      LOG(DEBUG) << "skip executing command [" << line << "]";
      continue;
    }

    // Skip all commands before the saved last command index when resuming an update.
    // 恢复更新时,请跳过所有保存的最后一个命令索引之前的所有命令。verify不会执行
    if (params.canwrite && params.cmdindex != -1 && params.cmdindex <= saved_last_command_index) {
      LOG(INFO) << "Skipping already executed command: " << params.cmdindex
                << ", last executed command for previous update: " << saved_last_command_index;
      continue;
    }
    
    //无法执行单行指令时报错 这里应该就是执行的地方了,依然没有看到verify只执行stash的地方
    if (cmd->f(params) == -1) {
      LOG(ERROR) << "failed to execute command [" << line << "]";
      goto pbiudone;
    }

    // In verify mode, check if the commands before the saved last_command_index have been
    // executed correctly. If some target blocks have unexpected contents, delete the last command
    // file so that we will resume the update from the first command in the transfer list.
    // 在验证模式下,检查保存的last_command_index之前的命令是否已正确执行。 
    // 如果某些目标块的内容意外,请删除最后一个命令文件,以便我们从传输列表中的第一个命令恢复更新。
    if (!params.canwrite && saved_last_command_index != -1 && params.cmdindex != -1 &&
        params.cmdindex <= saved_last_command_index) {
      // TODO(xunchang) check that the cmdline of the saved index is correct.
      std::string cmdname = std::string(params.cmdname);
      if ((cmdname == "move" || cmdname == "bsdiff" || cmdname == "imgdiff") &&
          !params.target_verified) {
        LOG(WARNING) << "Previously executed command " << saved_last_command_index << ": "
                     << params.cmdline << " doesn't produce expected target blocks.";
        saved_last_command_index = -1;
        DeleteLastCommandFile();
      }
    }
    if (params.canwrite) {
      if (ota_fsync(params.fd) == -1) {
        failure_type = kFsyncFailure;
        PLOG(ERROR) << "fsync failed";
        goto pbiudone;
      }
      fprintf(cmd_pipe, "set_progress %.4f\n", static_cast<double>(params.written) / total_blocks);
      fflush(cmd_pipe);
    }
  }

  rc = 0;

pbiudone:
  if (params.canwrite) {
    pthread_mutex_lock(&params.nti.mu);
    if (params.nti.receiver_available) {
      LOG(WARNING) << "new data receiver is still available after executing all commands.";
    }
    params.nti.receiver_available = false;
    pthread_cond_broadcast(&params.nti.cv);
    pthread_mutex_unlock(&params.nti.mu);
    int ret = pthread_join(params.thread, nullptr);
    if (ret != 0) {
      LOG(WARNING) << "pthread join returned with " << strerror(ret);
    }

    if (rc == 0) {
      LOG(INFO) << "wrote " << params.written << " blocks; expected " << total_blocks;
      LOG(INFO) << "stashed " << params.stashed << " blocks";
      LOG(INFO) << "max alloc needed was " << params.buffer.size();

      const char* partition = strrchr(blockdev_filename->data.c_str(), '/');
      if (partition != nullptr && *(partition + 1) != 0) {
        fprintf(cmd_pipe, "log bytes_written_%s: %zu\n", partition + 1, params.written * BLOCKSIZE);
        fprintf(cmd_pipe, "log bytes_stashed_%s: %zu\n", partition + 1, params.stashed * BLOCKSIZE);
        fflush(cmd_pipe);
      }
      // Delete stash only after successfully completing the update, as it may contain blocks needed
      // to complete the update later.
      DeleteStash(params.stashbase);
      DeleteLastCommandFile();
    }

    pthread_mutex_destroy(&params.nti.mu);
    pthread_cond_destroy(&params.nti.cv);
  } else if (rc == 0) {
    LOG(INFO) << "verified partition contents; update may be resumed";
  }

  if (ota_fsync(params.fd) == -1) {
    failure_type = kFsyncFailure;
    PLOG(ERROR) << "fsync failed";
  }
  // params.fd will be automatically closed because it's a unique_fd.

  if (params.nti.brotli_decoder_state != nullptr) {
    BrotliDecoderDestroyInstance(params.nti.brotli_decoder_state);
  }

  // Delete the last command file if the update cannot be resumed.
  if (params.isunresumable) {
    DeleteLastCommandFile();
  }

  // Only delete the stash if the update cannot be resumed, or it's a verification run and we
  // created the stash.
  if (params.isunresumable || (!params.canwrite && params.createdstash)) {
    DeleteStash(params.stashbase);
  }

  if (failure_type != kNoCause && state->cause_code == kNoCause) {
    state->cause_code = failure_type;
  }

  return StringValue(rc == 0 ? "t" : "");
}

 

3.2 CreateStash

创建stash目录,如果有,就使用以前创建好的

// Creates a directory for storing stash files and checks if the /cache partition
// hash enough space for the expected amount of blocks we need to store. Returns
// >0 if we created the directory, zero if it existed already, and <0 of failure.

//创建文件夹用于存放 存储文件 并检查cache空间是否足够.
//如果返回大于0 则创建文件夹 如果为0 说明已经存在.如果小于0 则失败
static int CreateStash(State* state, size_t maxblocks, const std::string& blockdev,
                       std::string& base) {
  if (blockdev.empty()) {
    return -1;
  }

  // Stash directory should be different for each partition to avoid conflicts
  // when updating multiple partitions at the same time, so we use the hash of
  // the block device name as the base directory
  // 每个分区的存储目录应该不同,以避免冲突
  // 同时更新多个分区时,因此我们将块设备名称的哈希值用作基本目录
  uint8_t digest[SHA_DIGEST_LENGTH];
  SHA1(reinterpret_cast<const uint8_t*>(blockdev.data()), blockdev.size(), digest);
  base = print_sha1(digest);
  //这里就看0和1的情况
  std::string dirname = GetStashFileName(base, "", "");
  struct stat sb;
  int res = stat(dirname.c_str(), &sb);
  size_t max_stash_size = maxblocks * BLOCKSIZE;
  
  //等于-1的情况,创建失败
  if (res == -1 && errno != ENOENT) {
    ErrorAbort(state, kStashCreationFailure, "stat \"%s\" failed: %s", dirname.c_str(),
               strerror(errno));
    return -1;
  //如果不等于0 创建文件夹
  } else if (res != 0) {
    LOG(INFO) << "creating stash " << dirname;
    res = mkdir(dirname.c_str(), STASH_DIRECTORY_MODE);

    if (res != 0) {
      ErrorAbort(state, kStashCreationFailure, "mkdir \"%s\" failed: %s", dirname.c_str(),
                 strerror(errno));
      return -1;
    }

    if (chown(dirname.c_str(), AID_SYSTEM, AID_SYSTEM) != 0) {  // system user
      ErrorAbort(state, kStashCreationFailure, "chown \"%s\" failed: %s", dirname.c_str(),
                 strerror(errno));
      return -1;
    }

    if (CacheSizeCheck(max_stash_size) != 0) {
      ErrorAbort(state, kStashCreationFailure, "not enough space for stash (%zu needed)",
                 max_stash_size);
      return -1;
    }

    return 1;  // Created directory
  }
  //如果为0 直接使用已经创建过的
  LOG(INFO) << "using existing stash " << dirname;

  // If the directory already exists, calculate the space already allocated to stash files and check
  // if there's enough for all required blocks. Delete any partially completed stash files first.
  // 如果文件夹已经存在,计算已经分配给存储文件的空间,并检查是否有足够的空间用于所有必需的块,首先删除任何部分完成的存储文件。
  EnumerateStash(dirname, [](const std::string& fn) {
    if (android::base::EndsWith(fn, ".partial")) {
      DeleteFile(fn);
    }
  });

  size_t existing = 0;
  EnumerateStash(dirname, [&existing](const std::string& fn) {
    if (fn.empty()) return;
    struct stat sb;
    if (stat(fn.c_str(), &sb) == -1) {
      PLOG(ERROR) << "stat \"" << fn << "\" failed";
      return;
    }
    existing += static_cast<size_t>(sb.st_size);
  });

  if (max_stash_size > existing) {
    size_t needed = max_stash_size - existing;
    if (CacheSizeCheck(needed) != 0) {
      ErrorAbort(state, kStashCreationFailure, "not enough space for stash (%zu more needed)",
                 needed);
      return -1;
    }
  }

  return 0;  // Using existing directory
}

 

3.3 ParseLastCommandFile

解析出最后升级的cmd 赋值给last_command_index

// Parse the last command index of the last update and save the result to |last_command_index|.
// Return true if we successfully read the index.
// 解析最后升级的最后指令索引,并且保存到last_command_index 如果我们能成功读取到这个索引,则返回true
static bool ParseLastCommandFile(int* last_command_index) {
  //确认该文件的存在,并且是否可以打开
  std::string last_command_file = CacheLocation::location().last_command_file();
  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(last_command_file.c_str(), O_RDONLY)));
  //如果打不开,那啥也不说了,返回false
  if (fd == -1) {
    if (errno != ENOENT) {
      PLOG(ERROR) << "Failed to open " << last_command_file;
      return false;
    }

    LOG(INFO) << last_command_file << " doesn't exist.";
    return false;
  }

  // Now that the last_command file exists, parse the last command index of previous update.
  // 如果文件存在,解析上次升级最后的指令索引
  std::string content;
  //看能不能读取成string格式
  if (!android::base::ReadFdToString(fd.get(), &content)) {
    LOG(ERROR) << "Failed to read: " << last_command_file;
    return false;
  }
  //对string进行分割
  std::vector<std::string> lines = android::base::Split(android::base::Trim(content), "\n");
  if (lines.size() != 2) {
    LOG(ERROR) << "Unexpected line counts in last command file: " << content;
    return false;
  }
  //将第一行的索引放入last_command_index
  if (!android::base::ParseInt(lines[0], last_command_index)) {
    LOG(ERROR) << "Failed to parse integer in: " << lines[0];
    return false;
  }

  return true;
}

 

后面的比较复杂,还是分开情况分析吧,整个代码下来基本上就是,看的快吐了,以下将按我测试的一份9.0 transfer.list进行分析

3.4 PerformCommandErase

erase 4,447643,622080,623105,644615

 

first:

verify     不走该方法,不用分析,漂亮

update   清空 erasing 195947 blocks 删除447643-622080 623105-644615的数据

 

retry

verify     不走该方法,不用分析,漂亮

update   Skipping already executed command: 0, last executed command for previous update: 862

static int PerformCommandErase(CommandParameters& params) {
  if (DEBUG_ERASE) {
    return PerformCommandZero(params);
  }

  struct stat sb;
  if (fstat(params.fd, &sb) == -1) {
    PLOG(ERROR) << "failed to fstat device to erase";
    return -1;
  }

  if (!S_ISBLK(sb.st_mode)) {
    LOG(ERROR) << "not a block device; skipping erase";
    return -1;
  }

  if (params.cpos >= params.tokens.size()) {
    LOG(ERROR) << "missing target blocks for erase";
    return -1;
  }

  RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]);
  CHECK(static_cast<bool>(tgt));
  //update流程
  if (params.canwrite) {
    LOG(INFO) << " erasing " << tgt.blocks() << " blocks";

    for (const auto& range : tgt) {
      uint64_t blocks[2];
      // offset in bytes
      blocks[0] = range.first * static_cast<uint64_t>(BLOCKSIZE);
      // length in bytes
      blocks[1] = (range.second - range.first) * static_cast<uint64_t>(BLOCKSIZE);

      if (ioctl(params.fd, BLKDISCARD, &blocks) == -1) {
        PLOG(ERROR) << "BLKDISCARD ioctl failed";
        return -1;
      }
    }
  }

  return 0;
}

 

3.5 PerformCommandDiff

bsdiff 0 1243697 0cee47e21239852586ca5f509b7c7d20c03b921f cbbc6dfd7270d4c6dd6be6cc3c06126ed3d62b06 2,76853,77650 1487 2,76464,77951

 

读取源block区间数据(block 76464至block 77951,hash值为 0cee47e21239852586ca5f509b7c7d20c03b921f),与system.patch.dat文件里偏移量为0,长度为1243697的数据进行bsdiff差分算法运算,生成的新数据存储至目标块区间blokc 76853至block 77650(hash值为cbbc6dfd7270d4c6dd6be6cc3c06126ed3d62b06)

first:

verify    校验hash是否匹配源数据,如果校验通过,verify直接返回

update  校验hash是否匹配源数据,如果校验通过,判断是否有交集,更新last_command_index,执行写入的动作

区间有交集:

1.有的话将基础版本的数据写入到stash

writing 1487 blocks to /cache/recovery/1fb420b778bead14ec9b2cf264fcaa8e147e3e39/0cee47e21239852586ca5f509b7c7d20c03b921f

2.执行patch

patching 1487 blocks to 797

3.删除stash

deleting /cache/recovery/1fb420b778bead14ec9b2cf264fcaa8e147e3e39/0cee47e21239852586ca5f509b7c7d20c03b921f

区间无交集:

执行patch patching 32 blocks to 19

 

retry:

verify      校验hash是否匹配源目标数据,如果校验通过,verify直接返回

update   Skipping already executed command: 0, last executed command for previous update: 862

//指令为diff的部分 
//bsdiff 0 1243697 0cee47e21239852586ca5f509b7c7d20c03b921f cbbc6dfd7270d4c6dd6be6cc3c06126ed3d62b06 2,76853,77650 1487 2,76464,77951
static int PerformCommandDiff(CommandParameters& params) {
  // <offset> <length>
  if (params.cpos + 1 >= params.tokens.size()) {
    LOG(ERROR) << "missing patch offset or length for " << params.cmdname;
    return -1;
  }

  size_t offset; //获取 1位置上的偏移量offset
  if (!android::base::ParseUint(params.tokens[params.cpos++], &offset)) {
    LOG(ERROR) << "invalid patch offset";
    return -1;
  }

  size_t len; //获取 2位置上的长度len
  if (!android::base::ParseUint(params.tokens[params.cpos++], &len)) {
    LOG(ERROR) << "invalid patch len";
    return -1;
  }
  
  //注意这里的参数,tgt时升级后的区间
  RangeSet tgt;
  //定义block参数
  size_t blocks = 0;
  //判断基础和升级的区间是否有重叠的情况
  bool overlap = false; 
  //false是onehash 是不是命令行里只有一个hash
  int status = LoadSrcTgtVersion3(params, tgt, &blocks, false, &overlap);

  if (status == -1) {
    LOG(ERROR) << "failed to read blocks for diff";
    return -1;
  }

  if (status == 0) {
    params.foundwrites = true;
  } else {
    //target_verified为true 说明已经升级好了
    params.target_verified = true;
    if (params.foundwrites) {
      LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]";
    }
  }

  if (params.canwrite) {
    if (status == 0) {
      LOG(INFO) << "patching " << blocks << " blocks to " << tgt.blocks();
      Value patch_value(
          VAL_BLOB, std::string(reinterpret_cast<const char*>(params.patch_start + offset), len));

      RangeSinkWriter writer(params.fd, tgt);
      if (params.cmdname[0] == 'i') {  // imgdiff
        if (ApplyImagePatch(params.buffer.data(), blocks * BLOCKSIZE, patch_value,
                            std::bind(&RangeSinkWriter::Write, &writer, std::placeholders::_1,
                                      std::placeholders::_2),
                            nullptr, nullptr) != 0) {
          LOG(ERROR) << "Failed to apply image patch.";
          failure_type = kPatchApplicationFailure;
          return -1;
        }
      } else {
        if (ApplyBSDiffPatch(params.buffer.data(), blocks * BLOCKSIZE, patch_value, 0,
                             std::bind(&RangeSinkWriter::Write, &writer, std::placeholders::_1,
                                       std::placeholders::_2),
                             nullptr) != 0) {
          LOG(ERROR) << "Failed to apply bsdiff patch.";
          failure_type = kPatchApplicationFailure;
          return -1;
        }
      }

      // We expect the output of the patcher to fill the tgt ranges exactly.
      if (!writer.Finished()) {
        LOG(ERROR) << "range sink underrun?";
      }
    } else {
      LOG(INFO) << "skipping " << blocks << " blocks already patched to " << tgt.blocks() << " ["
                << params.cmdline << "]";
    }
  }

  if (!params.freestash.empty()) {
    FreeStash(params.stashbase, params.freestash);
    params.freestash.clear();
  }

  params.written += tgt.blocks();

  return 0;
}

 

3.6 PerformCommandMove

move 6374345275f5e3ea18588cf47e5d4c42796ae44f 2,77670,77706 36 2,77984,78020


将源block 77984至block 78020(共4 block,hash值为6374345275f5e3ea18588cf47e5d4c42796ae44f)的数据移动至目标block 77670至block 77706的空间

first:

verify    校验hash是否匹配源数据,如果校验通过,verify直接返回

update  校验hash是否匹配源数据,如果校验通过,判断是否有交集,更新last_command_index,执行写入的动作

区间有交集:

1.有的话将基础版本的数据写入到stash

writing 1487 blocks to /cache/recovery/1fb420b778bead14ec9b2cf264fcaa8e147e3e39/0cee47e21239852586ca5f509b7c7d20c03b921f

2.执行move

moving 1487 blocks to 797

3.删除stash

deleting /cache/recovery/1fb420b778bead14ec9b2cf264fcaa8e147e3e39/0cee47e21239852586ca5f509b7c7d20c03b921f

区间无交集:

执行move moving 36 blocks

 

retry:

verify      校验hash是否匹配源目标数据,如果校验通过,verify直接返回

update   Skipping already executed command: 2, last executed command for previous update: 862

//执行指令为move的情况,move 6374345275f5e3ea18588cf47e5d4c42796ae44f 2,77670,77706 36 2,77984,78020
static int PerformCommandMove(CommandParameters& params) {
  size_t blocks = 0;
  bool overlap = false;
  RangeSet tgt;
  int status = LoadSrcTgtVersion3(params, tgt, &blocks, true, &overlap);

  if (status == -1) {
    LOG(ERROR) << "failed to read blocks for move";
    return -1;
  }

  if (status == 0) {
    params.foundwrites = true;
  } else {
    params.target_verified = true;
    if (params.foundwrites) {
      LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]";
    }
  }

  if (params.canwrite) {
    if (status == 0) {
      LOG(INFO) << "  moving " << blocks << " blocks";

      if (WriteBlocks(tgt, params.buffer, params.fd) == -1) {
        return -1;
      }
    } else {
      LOG(INFO) << "skipping " << blocks << " already moved blocks";
    }
  }

  if (!params.freestash.empty()) {
    FreeStash(params.stashbase, params.freestash);
    params.freestash.clear();
  }

  params.written += tgt.blocks();

  return 0;
}

 

3.7 command调用的通用方法

3.7.1 LoadSrcTgtVersion3

这个方法diff move都会运行,所以我们先分析下

1.获取参数信息 diff为双hash move为单hash

2.确认目标区间能不能读取到数据,即能不能写入

3.优先校验目标区间的数据已经已经写入过

4.如果没有写入过,加载源版本src_block,并通过区间对比赋值overlap,是否存在交际

5.如果校验srchash通过,说明源数据没有问题,校验阶段在此结束

6.如果校验srchash通过,写入阶段首先存储重叠的块,更新last_command_index

//参数 tgt目标区间 源block块 是否为单个hash 是否存在交集
static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t* src_blocks,
                              bool onehash, bool* overlap) {
  CHECK(src_blocks != nullptr);
  CHECK(overlap != nullptr);

  if (params.cpos >= params.tokens.size()) {
    LOG(ERROR) << "missing source hash";
    return -1;
  }
  //获取srchash
  std::string srchash = params.tokens[params.cpos++];
  std::string tgthash;
  //如果是move的情况,onehash值为true,diff情况为false
  if (onehash) {
    tgthash = srchash;
  } else {
    if (params.cpos >= params.tokens.size()) {
      LOG(ERROR) << "missing target hash";
      return -1;
    }
    //获取tgthash
    tgthash = params.tokens[params.cpos++];
  }

  // At least it needs to provide three parameters: <tgt_range>, <src_block_count> and
  // "-"/<src_range>. 最少需要三个参数,这里进行了判断
  if (params.cpos + 2 >= params.tokens.size()) {
    LOG(ERROR) << "invalid parameters";
    return -1;
  }

  // <tgt_range> 获取tgt区间
  tgt = RangeSet::Parse(params.tokens[params.cpos++]);
  CHECK(static_cast<bool>(tgt));
  
  //将tgt的blocks放入容器中
  std::vector<uint8_t> tgtbuffer(tgt.blocks() * BLOCKSIZE);
  if (ReadBlocks(tgt, tgtbuffer, params.fd) == -1) {
    return -1;
  }

  // Return now if target blocks already have expected content.
  // 如果目标块已经具有预期的内容,请立即返回。其实就是已经升级过了
  if (VerifyBlocks(tgthash, tgtbuffer, tgt.blocks(), false) == 0) {
    return 1;
  }

  // Load source blocks.加载源block 这一步会赋值给src_block和overlap
  if (LoadSourceBlocks(params, tgt, src_blocks, overlap) == -1) {
    return -1;
  }
  //校验源数据的hash是否匹配,如果校验通过,verify直接返回0
  if (VerifyBlocks(srchash, params.buffer, *src_blocks, true) == 0) {
    // If source and target blocks overlap, stash the source blocks so we can
    // resume from possible write errors. In verify mode, we can skip stashing
    // because the source blocks won't be overwritten.
    // 如果基础和升级的有重叠,存储源块,如过写入失败我们可以恢复,对于校验模式
    // 我们不用存储,因为也不会写入
    if (*overlap && params.canwrite) {
      LOG(INFO) << "stashing " << *src_blocks << " overlapping blocks to " << srchash;
     
      bool stash_exists = false;
      //存储重叠的块
      if (WriteStash(params.stashbase, srchash, *src_blocks, params.buffer, true,
                     &stash_exists) != 0) {
        LOG(ERROR) << "failed to stash overlapping source blocks";
        return -1;
      }
      //更新last_command_index
      if (!UpdateLastCommandIndex(params.cmdindex, params.cmdline)) {
        LOG(WARNING) << "Failed to update the last command file.";
      }
      //stash存成功了,那就将src_blocks追加到params.stashed
      params.stashed += *src_blocks;
      // Can be deleted when the write has completed.
      // 如果写入成功了,srchash赋值给params.freestash
      if (!stash_exists) {
        params.freestash = srchash;
      }
    }

    // Source blocks have expected content, command can proceed.
    return 0;
  }
  //如果source没校验过去,进入该判断
  if (*overlap && LoadStash(params, srchash, true, nullptr, params.buffer, true) == 0) {
    // Overlapping source blocks were previously stashed, command can proceed. We are recovering
    // from an interrupted command, so we don't know if the stash can safely be deleted after this
    // command.
    //先前隐藏了重叠的源块,可以继续执行命令。 
    //我们正在从中断的命令中恢复,因此我们不知道在此命令之后是否可以安全地删除隐藏项。
    return 0;
  }

  // Valid source data not available, update cannot be resumed.
  LOG(ERROR) << "partition has unexpected contents";
  PrintHashForCorruptedSourceBlocks(params, params.buffer);

  params.isunresumable = true;

  return -1;
}

 

3.7.2 ReadBlocks

static int ReadBlocks(const RangeSet& src, std::vector<uint8_t>& buffer, int fd) {
  size_t p = 0;
  for (const auto& range : src) {
    if (!check_lseek(fd, static_cast<off64_t>(range.first) * BLOCKSIZE, SEEK_SET)) {
      return -1;
    }

    size_t size = (range.second - range.first) * BLOCKSIZE;
    if (read_all(fd, buffer.data() + p, size) == -1) {
      return -1;
    }

    p += size;
  }

  return 0;
}
static int read_all(int fd, uint8_t* data, size_t size) {
    size_t so_far = 0;
    while (so_far < size) {
        ssize_t r = TEMP_FAILURE_RETRY(ota_read(fd, data+so_far, size-so_far));
        if (r == -1) {
            failure_type = kFreadFailure;
            PLOG(ERROR) << "read failed";
            return -1;
        } else if (r == 0) {
            failure_type = kFreadFailure;
            LOG(ERROR) << "read reached unexpected EOF.";
            return -1;
        }
        so_far += r;
    }
    return 0;
}

 

3.7.3 VerifyBlocks

static int VerifyBlocks(const std::string& expected, const std::vector<uint8_t>& buffer,
        const size_t blocks, bool printerror) {
    uint8_t digest[SHA_DIGEST_LENGTH];
    const uint8_t* data = buffer.data();

    SHA1(data, blocks * BLOCKSIZE, digest);

    std::string hexdigest = print_sha1(digest);

    if (hexdigest != expected) {
        if (printerror) {
            LOG(ERROR) << "failed to verify blocks (expected " << expected << ", read "
                       << hexdigest << ")";
        }
        return -1;
    }

    return 0;
}

 

3.7.4 LoadSourceBlocks

//*返回时,params.buffer填充了已加载的源数据(重新排列并与
//  *根据需要隐藏数据)。 如果需要容纳源数据,可以重新分配缓冲区。
//  * tgt是用于检测重叠的目标RangeSet。 所需的任何隐藏都使用
//  * LoadStash。
static int LoadSourceBlocks(CommandParameters& params, const RangeSet& tgt, size_t* src_blocks,
                            bool* overlap) {
  CHECK(src_blocks != nullptr);
  CHECK(overlap != nullptr);

  // <src_block_count> 获取源版本block数目,放入src_block中
  const std::string& token = params.tokens[params.cpos++];
  if (!android::base::ParseUint(token, src_blocks)) {
    LOG(ERROR) << "invalid src_block_count \"" << token << "\"";
    return -1;
  }
  //分配params.buffer内存
  allocate(*src_blocks * BLOCKSIZE, params.buffer);

  // "-" or <src_range> [<src_loc>] 看下是不是 -
  if (params.tokens[params.cpos] == "-") {
    // no source ranges, only stashes
    params.cpos++;
  } else {
    //获取源区间
    RangeSet src = RangeSet::Parse(params.tokens[params.cpos++]);
    CHECK(static_cast<bool>(src));
    //获取源区间和升级区间的交集
    *overlap = src.Overlaps(tgt);
    //对比源block数和src_range
    if (ReadBlocks(src, params.buffer, params.fd) == -1) {
      return -1;
    }
    
    //如果没有了,说明没stash的情况 
    if (params.cpos >= params.tokens.size()) {
      // no stashes, only source range
      return 0;
    }
    //后面时处理move的情况,首先获取的src_loc
    RangeSet locs = RangeSet::Parse(params.tokens[params.cpos++]);
    CHECK(static_cast<bool>(locs));
    //重新分配内存空间
    MoveRange(params.buffer, locs, params.buffer);
  }

  // <[stash_id:stash_range]> 终于到最后了,处理最后stash_id和stash_range的部分
  while (params.cpos < params.tokens.size()) {
    // Each word is a an index into the stash table, a colon, and then a RangeSet describing where
    // in the source block that stashed data should go.
    std::vector<std::string> tokens = android::base::Split(params.tokens[params.cpos++], ":");
    if (tokens.size() != 2) {
      LOG(ERROR) << "invalid parameter";
      return -1;
    }

    std::vector<uint8_t> stash;
    if (LoadStash(params, tokens[0], false, nullptr, stash, true) == -1) {
      // These source blocks will fail verification if used later, but we
      // will let the caller decide if this is a fatal failure
      LOG(ERROR) << "failed to load stash " << tokens[0];
      continue;
    }

    RangeSet locs = RangeSet::Parse(tokens[1]);
    CHECK(static_cast<bool>(locs));
    MoveRange(params.buffer, locs, stash);
  }

  return 0;
}

 

3.7.5 WriteStash

static int WriteStash(const std::string& base, const std::string& id, int blocks,
                      std::vector<uint8_t>& buffer, bool checkspace, bool* exists) {
    if (base.empty()) {
        return -1;
    }

    if (checkspace && CacheSizeCheck(blocks * BLOCKSIZE) != 0) {
        LOG(ERROR) << "not enough space to write stash";
        return -1;
    }

    std::string fn = GetStashFileName(base, id, ".partial");
    std::string cn = GetStashFileName(base, id, "");
    //传入的exists为false
    if (exists) {
        struct stat sb;
        int res = stat(cn.c_str(), &sb);

        if (res == 0) {
            // The file already exists and since the name is the hash of the contents,
            // it's safe to assume the contents are identical (accidental hash collisions
            // are unlikely)
            // 该文件已经存在,并且由于名称是内容的哈希可以安全地假定内容相同(偶然的哈希冲突不太可能)
            LOG(INFO) << " skipping " << blocks << " existing blocks in " << cn;
            *exists = true;
            return 0;
        }

        *exists = false;
    }
    //执行写入
    LOG(INFO) << " writing " << blocks << " blocks to " << cn;

    android::base::unique_fd fd(
        TEMP_FAILURE_RETRY(ota_open(fn.c_str(), O_WRONLY | O_CREAT | O_TRUNC, STASH_FILE_MODE)));
    if (fd == -1) {
        PLOG(ERROR) << "failed to create \"" << fn << "\"";
        return -1;
    }

    if (fchown(fd, AID_SYSTEM, AID_SYSTEM) != 0) {  // system user
        PLOG(ERROR) << "failed to chown \"" << fn << "\"";
        return -1;
    }

    if (write_all(fd, buffer, blocks * BLOCKSIZE) == -1) {
        return -1;
    }

    if (ota_fsync(fd) == -1) {
        failure_type = kFsyncFailure;
        PLOG(ERROR) << "fsync \"" << fn << "\" failed";
        return -1;
    }

    if (rename(fn.c_str(), cn.c_str()) == -1) {
        PLOG(ERROR) << "rename(\"" << fn << "\", \"" << cn << "\") failed";
        return -1;
    }

    std::string dname = GetStashFileName(base, "", "");
    android::base::unique_fd dfd(TEMP_FAILURE_RETRY(ota_open(dname.c_str(),
                                                             O_RDONLY | O_DIRECTORY)));
    if (dfd == -1) {
        failure_type = kFileOpenFailure;
        PLOG(ERROR) << "failed to open \"" << dname << "\" failed";
        return -1;
    }

    if (ota_fsync(dfd) == -1) {
        failure_type = kFsyncFailure;
        PLOG(ERROR) << "fsync \"" << dname << "\" failed";
        return -1;
    }

    return 0;
}

 

3.7.6 LoadStash

static int LoadStash(CommandParameters& params, const std::string& id, bool verify, size_t* blocks,
                     std::vector<uint8_t>& buffer, bool printnoent) {
  // In verify mode, if source range_set was saved for the given hash, check contents in the source
  // blocks first. If the check fails, search for the stashed files on /cache as usual.
  // 在验证模式下,如果为给定的哈希保存了源range_set,请首先检查源块中的内容。 如果检查失败,请照常在/ cache上搜索隐藏的文件。
  // verify模式进入判断
  if (!params.canwrite) {
    //如果id号在stash_map里找到,则进入判断,现在这个stash_map应该是空的,没装东西, 所以也进不去判断
    if (stash_map.find(id) != stash_map.end()) {
      const RangeSet& src = stash_map[id];
      allocate(src.blocks() * BLOCKSIZE, buffer);

      if (ReadBlocks(src, buffer, params.fd) == -1) {
        LOG(ERROR) << "failed to read source blocks in stash map.";
        return -1;
      }
      if (VerifyBlocks(id, buffer, src.blocks(), true) != 0) {
        LOG(ERROR) << "failed to verify loaded source blocks in stash map.";
        PrintHashForCorruptedStashedBlocks(id, buffer, src);
        return -1;
      }
      return 0;
    }
  }

  size_t blockcount = 0;
  if (!blocks) {
    blocks = &blockcount;
  }
  //获取到的fn 为/cache/recovery/1fb420b778bead14ec9b2cf264fcaa8e147e3e39/46ffdd400e281a9c9a638ad5d6e6e12644384b0e
  //stashbase为/cache/recovery/1fb420b778bead14ec9b2cf264fcaa8e147e3e39
  std::string fn = GetStashFileName(params.stashbase, id, "");
  //如果以上stash_map没加载到,那就从保存的stash中进行查找 并进行对比
  struct stat sb;
  if (stat(fn.c_str(), &sb) == -1) {
    if (errno != ENOENT || printnoent) {
      PLOG(ERROR) << "stat \"" << fn << "\" failed";
      PrintHashForMissingStashedBlocks(id, params.fd);
    }
    return -1;
  }

  LOG(INFO) << " loading " << fn;

  if ((sb.st_size % BLOCKSIZE) != 0) {
    LOG(ERROR) << fn << " size " << sb.st_size << " not multiple of block size " << BLOCKSIZE;
    return -1;
  }

  android::base::unique_fd fd(TEMP_FAILURE_RETRY(ota_open(fn.c_str(), O_RDONLY)));
  if (fd == -1) {
    PLOG(ERROR) << "open \"" << fn << "\" failed";
    return -1;
  }

  allocate(sb.st_size, buffer);

  if (read_all(fd, buffer, sb.st_size) == -1) {
    return -1;
  }

  *blocks = sb.st_size / BLOCKSIZE;

  if (verify && VerifyBlocks(id, buffer, *blocks, true) != 0) {
    LOG(ERROR) << "unexpected contents in " << fn;
    if (stash_map.find(id) == stash_map.end()) {
      LOG(ERROR) << "failed to find source blocks number for stash " << id
                 << " when executing command: " << params.cmdname;
    } else {
      const RangeSet& src = stash_map[id];
      PrintHashForCorruptedStashedBlocks(id, buffer, src);
    }
    DeleteFile(fn);
    return -1;
  }

  return 0;
}

 

3.7.7 FreeStash

static int FreeStash(const std::string& base, const std::string& id) {
  if (base.empty() || id.empty()) {
    return -1;
  }

  DeleteFile(GetStashFileName(base, id, ""));

  return 0;
}

 

// Deletes the stash directory and all files in it. Assumes that it only
// contains files. There is nothing we can do about unlikely, but possible
// errors, so they are merely logged.
// 删除存储目录及其中的所有文件。 假设它只是包含文件。 
// 对于不可能的事,我们无能为力,但有可能错误,因此仅记录它们。
static void DeleteFile(const std::string& fn) {
  if (fn.empty()) return;

  LOG(INFO) << "deleting " << fn;

  if (unlink(fn.c_str()) == -1 && errno != ENOENT) {
    PLOG(ERROR) << "unlink \"" << fn << "\" failed";
  }
}

static void DeleteStash(const std::string& base) {
  if (base.empty()) return;

  LOG(INFO) << "deleting stash " << base;

  std::string dirname = GetStashFileName(base, "", "");
  EnumerateStash(dirname, DeleteFile);

  if (rmdir(dirname.c_str()) == -1) {
    if (errno != ENOENT && errno != ENOTDIR) {
      PLOG(ERROR) << "rmdir \"" << dirname << "\" failed";
    }
  }
}

 

3.8 PerformCommandStash

执行stash命令行的方法

stash 357e80f1cda6acf368741046b37a6bc34e291c53 6,77983,77984,78020,78022,78029,78030

 

将读取到的源块数据(从77983至77984,78020至78022,78029至78030),并确认其hash值为357e80f1cda6acf368741046b37a6bc34e291c53后,存储于cache目录下文件名为357e80f1cda6acf368741046b37a6bc34e291c53的stash文件。

 

first:

verify     啥也没干,直接返回,In verify mode, we don't need to stash any block

update  存储stash

stashing 4 blocks to 357e80f1cda6acf368741046b37a6bc34e291c53

writing 4 blocks to /cache/recovery/1fb420b778bead14ec9b2cf264fcaa8e147e3e39/357e80f1cda6acf368741046b37a6bc34e291c53

 

retry:  

verify     加载stash,确认时后已经存在

loading /cache/recovery/1fb420b778bead14ec9b2cf264fcaa8e147e3e39/357e80f1cda6acf368741046b37a6bc34e291c53

update  

Skipping already executed command: 68, last executed command for previous update: 862

//执行stash指令的方法 stash 91aeb61eed65c06adc7e24dd6eb51245e36b41ea 2,277548,277661
static int PerformCommandStash(CommandParameters& params) {
  // <stash_id> <src_range> 如果分割之后参数 小于等于2 那说明该行缺参数
  if (params.cpos + 1 >= params.tokens.size()) {
    LOG(ERROR) << "missing id and/or src range fields in stash command";
    return -1;
  }
  // 获取stash_id 执行该行代码后 params.cpos=2
  const std::string& id = params.tokens[params.cpos++];
  size_t blocks = 0;
  //加载stash 看是否存在 所有stash命令行的都会先从stash存储中读取
  if (LoadStash(params, id, true, &blocks, params.buffer, false) == 0) {
    // Stash file already exists and has expected contents. Do not read from source again, as the
    // source may have been already overwritten during a previous attempt.
    // 存储文件已存在,并且具有预期的内容。不要再次从源中读取数据,因为在上一次尝试中该源可能已被覆盖。
    return 0;
  }
  // 获取<src_range>
  RangeSet src = RangeSet::Parse(params.tokens[params.cpos++]);
  CHECK(static_cast<bool>(src));

  allocate(src.blocks() * BLOCKSIZE, params.buffer);
  //判断是不是可以正常读取
  if (ReadBlocks(src, params.buffer, params.fd) == -1) {
    return -1;
  }
  blocks = src.blocks();
  //将src放入stash_map
  stash_map[id] = src;
  
  //校验失败为什么不影响结果 依然return 0???
  if (VerifyBlocks(id, params.buffer, blocks, true) != 0) {
    // Source blocks have unexpected contents. If we actually need this data later, this is an
    // unrecoverable error. However, the command that uses the data may have already completed
    // previously, so the possible failure will occur during source block verification.
    // 源块包含意外内容。 如果我们稍后确实需要此数据,则这是不可恢复的错误。 
    // 但是,使用该数据的命令之前可能已经完成,因此可能在源块验证期间发生故障。
    LOG(ERROR) << "failed to load source blocks for stash " << id;
    return 0;
  }

  // In verify mode, we don't need to stash any blocks.在验证模式下,我们不需要存储block
  if (!params.canwrite) {
    return 0;
  }

  LOG(INFO) << "stashing " << blocks << " blocks to " << id;
  int result = WriteStash(params.stashbase, id, blocks, params.buffer, false, nullptr);
  if (result == 0) {
    if (!UpdateLastCommandIndex(params.cmdindex, params.cmdline)) {
      LOG(WARNING) << "Failed to update the last command file.";
    }

    params.stashed += blocks;
  }
  return result;
}

 

free zero new这是哪个cmd比较简单,这里不做详细分析

3.9 PerformCommandFree

指令为free的处理 free 9ebd6c1590f22c76c1fc8ff13c691d1d011ad9ee

deleting /cache/recovery/1fb420b778bead14ec9b2cf264fcaa8e147e3e39/9ebd6c1590f22c76c1fc8ff13c691d1d011ad9ee

//指令为free的处理 free 9ebd6c1590f22c76c1fc8ff13c691d1d011ad9ee
static int PerformCommandFree(CommandParameters& params) {
  // <stash_id>
  if (params.cpos >= params.tokens.size()) {
    LOG(ERROR) << "missing stash id in free command";
    return -1;
  }

  const std::string& id = params.tokens[params.cpos++];
  stash_map.erase(id);

  if (params.createdstash || params.canwrite) {
    return FreeStash(params.stashbase, id);
  }

  return 0;
}

 

 

3.10 PerformCommandZero

zero 8,430,666,32770,32927,32928,33434,65537,65662

zeroing 1024 blocks

static int PerformCommandZero(CommandParameters& params) {
  if (params.cpos >= params.tokens.size()) {
    LOG(ERROR) << "missing target blocks for zero";
    return -1;
  }

  RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]);
  CHECK(static_cast<bool>(tgt));

  LOG(INFO) << "  zeroing " << tgt.blocks() << " blocks";

  allocate(BLOCKSIZE, params.buffer);
  memset(params.buffer.data(), 0, BLOCKSIZE);

  if (params.canwrite) {
    for (const auto& range : tgt) {
      off64_t offset = static_cast<off64_t>(range.first) * BLOCKSIZE;
      size_t size = (range.second - range.first) * BLOCKSIZE;
      if (!discard_blocks(params.fd, offset, size)) {
        return -1;
      }

      if (!check_lseek(params.fd, offset, SEEK_SET)) {
        return -1;
      }

      for (size_t j = range.first; j < range.second; ++j) {
        if (write_all(params.fd, params.buffer, BLOCKSIZE) == -1) {
          return -1;
        }
      }
    }
  }

  if (params.cmdname[0] == 'z') {
    // Update only for the zero command, as the erase command will call
    // this if DEBUG_ERASE is defined.
    params.written += tgt.blocks();
  }

  return 0;
}

 

3.11 PerformCommandNew

new 2,0,1

writing 1 blocks of new data

static int PerformCommandNew(CommandParameters& params) {
  if (params.cpos >= params.tokens.size()) {
    LOG(ERROR) << "missing target blocks for new";
    return -1;
  }

  RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]);
  CHECK(static_cast<bool>(tgt));

  if (params.canwrite) {
    LOG(INFO) << " writing " << tgt.blocks() << " blocks of new data";

    pthread_mutex_lock(&params.nti.mu);
    params.nti.writer = std::make_unique<RangeSinkWriter>(params.fd, tgt);
    pthread_cond_broadcast(&params.nti.cv);

    while (params.nti.writer != nullptr) {
      if (!params.nti.receiver_available) {
        LOG(ERROR) << "missing " << (tgt.blocks() * BLOCKSIZE - params.nti.writer->BytesWritten())
                   << " bytes of new data";
        pthread_mutex_unlock(&params.nti.mu);
        return -1;
      }
      pthread_cond_wait(&params.nti.cv, &params.nti.mu);
    }

    pthread_mutex_unlock(&params.nti.mu);
  }

  params.written += tgt.blocks();

  return 0;
}

 

4.CheckFirstBlockFn

其实就是校验分区的超级块,检查是不是机器执行过root,如果有将会在校验阶段报错

//此功能检查在增量OTA更新之前是否已将设备重新读/写。 这是更新中止的常见原因。 
//该功能读取每个分区的第一个块,并检查安装时间/计数。 如果成功执行,则返回字符串“ t”,
//否则返回空字符串。

Value* CheckFirstBlockFn(const char* name, State* state,
                         const std::vector<std::unique_ptr<Expr>>& argv) {
  if (argv.size() != 1) {
    ErrorAbort(state, kArgsParsingFailure, "check_first_block expects 1 argument, got %zu",
               argv.size());
    return StringValue("");
  }

  std::vector<std::unique_ptr<Value>> args;
  if (!ReadValueArgs(state, argv, &args)) {
    return nullptr;
  }

  const std::unique_ptr<Value>& arg_filename = args[0];

  if (arg_filename->type != VAL_STRING) {
    ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name);
    return StringValue("");
  }

  android::base::unique_fd fd(ota_open(arg_filename->data.c_str(), O_RDONLY));
  if (fd == -1) {
    ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", arg_filename->data.c_str(),
               strerror(errno));
    return StringValue("");
  }

  RangeSet blk0(std::vector<Range>{ Range{ 0, 1 } });
  std::vector<uint8_t> block0_buffer(BLOCKSIZE);

  if (ReadBlocks(blk0, block0_buffer, fd) == -1) {
    ErrorAbort(state, kFreadFailure, "failed to read %s: %s", arg_filename->data.c_str(),
               strerror(errno));
    return StringValue("");
  }

  // https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout
  // Super block starts from block 0, offset 0x400
  //   0x2C: len32 Mount time
  //   0x30: len32 Write time
  //   0x34: len16 Number of mounts since the last fsck
  //   0x38: len16 Magic signature 0xEF53

  time_t mount_time = *reinterpret_cast<uint32_t*>(&block0_buffer[0x400 + 0x2C]);
  uint16_t mount_count = *reinterpret_cast<uint16_t*>(&block0_buffer[0x400 + 0x34]);

  if (mount_count > 0) {
    uiPrintf(state, "Device was remounted R/W %" PRIu16 " times", mount_count);
    uiPrintf(state, "Last remount happened on %s", ctime(&mount_time));
  }

  return StringValue("t");
}

 

5.BlockImageRecoverFn

没看明白啥意思,后续补上吧 TODO....

Value* BlockImageRecoverFn(const char* name, State* state,
                           const std::vector<std::unique_ptr<Expr>>& argv) {
  if (argv.size() != 2) {
    ErrorAbort(state, kArgsParsingFailure, "block_image_recover expects 2 arguments, got %zu",
               argv.size());
    return StringValue("");
  }

  std::vector<std::unique_ptr<Value>> args;
  if (!ReadValueArgs(state, argv, &args)) {
    return nullptr;
  }
  //分区节点
  const std::unique_ptr<Value>& filename = args[0];
  //src_ranges
  const std::unique_ptr<Value>& ranges = args[1];

  if (filename->type != VAL_STRING) {
    ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name);
    return StringValue("");
  }
  if (ranges->type != VAL_STRING) {
    ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name);
    return StringValue("");
  }
  RangeSet rs = RangeSet::Parse(ranges->data);
  if (!rs) {
    ErrorAbort(state, kArgsParsingFailure, "failed to parse ranges: %s", ranges->data.c_str());
    return StringValue("");
  }

  // Output notice to log when recover is attempted
  LOG(INFO) << filename->data << " image corrupted, attempting to recover...";

  // When opened with O_RDWR, libfec rewrites corrupted blocks when they are read
  fec::io fh(filename->data, O_RDWR);

  if (!fh) {
    ErrorAbort(state, kLibfecFailure, "fec_open \"%s\" failed: %s", filename->data.c_str(),
               strerror(errno));
    return StringValue("");
  }

  if (!fh.has_ecc() || !fh.has_verity()) {
    ErrorAbort(state, kLibfecFailure, "unable to use metadata to correct errors");
    return StringValue("");
  }

  fec_status status;
  if (!fh.get_status(status)) {
    ErrorAbort(state, kLibfecFailure, "failed to read FEC status");
    return StringValue("");
  }

  uint8_t buffer[BLOCKSIZE];
  for (const auto& range : rs) {
    for (size_t j = range.first; j < range.second; ++j) {
      // Stay within the data area, libfec validates and corrects metadata
      if (status.data_size <= static_cast<uint64_t>(j) * BLOCKSIZE) {
        continue;
      }

      if (fh.pread(buffer, BLOCKSIZE, static_cast<off64_t>(j) * BLOCKSIZE) != BLOCKSIZE) {
        ErrorAbort(state, kLibfecFailure, "failed to recover %s (block %zu): %s",
                   filename->data.c_str(), j, strerror(errno));
        return StringValue("");
      }

      // If we want to be able to recover from a situation where rewriting a corrected
      // block doesn't guarantee the same data will be returned when re-read later, we
      // can save a copy of corrected blocks to /cache. Note:
      //
      //  1. Maximum space required from /cache is the same as the maximum number of
      //     corrupted blocks we can correct. For RS(255, 253) and a 2 GiB partition,
      //     this would be ~16 MiB, for example.
      //
      //  2. To find out if this block was corrupted, call fec_get_status after each
      //     read and check if the errors field value has increased.
    }
  }
  LOG(INFO) << "..." << filename->data << " image recovered successfully.";
  return StringValue("t");
}

总结:

本次主要分析了 verify阶段和update阶段的的具体升级流程,总的来说,因为transfer.list 是逐行cmd执行,所以控制重新写入并不是很复杂,通过一些标志位记录上次升级的进度,从而完成恢复升级.

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值