本篇将具体分析执行写入的流程,,整理完该流程后,将通过升级的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-scryptconst 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判断是否升级成功,如果失败,打印出对应的logif (!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_filesdef 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 OTAif not self.src:script.Print("Image %s will be patched unconditionally." % (partition,))# incremental OTAelse:#差分情况下touched_blocks_only=Trueif 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_sha1else:ranges = self.src.care_map.subtract(self.src.clobbered_blocks)expected_sha1 = self.src.TotalSha1()# No blocks to be checked, skipping.if not ranges:returnranges_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 updateif partition == "system":code = ErrorCode.SYSTEM_RECOVER_FAILUREelse:code = ErrorCode.VENDOR_RECOVER_FAILUREscript.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_FAILUREelse:code = ErrorCode.VENDOR_VERIFICATION_FAILUREscript.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大小的bufferstd::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 completelyconst 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.datconst std::unique_ptr<Value>& new_data_fn = args[2];//system.patch.datconst 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("");}//数据输出为stringZipString 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或者4if (!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=0params.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 建立可用指令的mapstd::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为-1if (i - start > std::numeric_limits<int>::max()) {params.cmdindex = -1;} else {//params.cmdindex 时从1开始的params.cmdindex = i - start;}//cmd的名称 比如basdiff move等 执行该行代码之后之后params.cpos++ = 1params.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 userErrorAbort(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)));//如果打不开,那啥也不说了,返回falseif (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_indexif (!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 bytesblocks[0] = range.first * static_cast<uint64_t>(BLOCKSIZE);// length in bytesblocks[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位置上的偏移量offsetif (!android::base::ParseUint(params.tokens[params.cpos++], &offset)) {LOG(ERROR) << "invalid patch offset";return -1;}size_t len; //获取 2位置上的长度lenif (!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 是不是命令行里只有一个hashint 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') {  // imgdiffif (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;}//获取srchashstd::string srchash = params.tokens[params.cpos++];std::string tgthash;//如果是move的情况,onehash值为true,diff情况为falseif (onehash) {tgthash = srchash;} else {if (params.cpos >= params.tokens.size()) {LOG(ERROR) << "missing target hash";return -1;}//获取tgthashtgthash = 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和overlapif (LoadSourceBlocks(params, tgt, src_blocks, overlap) == -1) {return -1;}//校验源数据的hash是否匹配,如果校验通过,verify直接返回0if (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_indexif (!UpdateLastCommandIndex(params.cmdindex, params.cmdline)) {LOG(WARNING) << "Failed to update the last command file.";}//stash存成功了,那就将src_blocks追加到params.stashedparams.stashed += *src_blocks;// Can be deleted when the write has completed.// 如果写入成功了,srchash赋值给params.freestashif (!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 stashesparams.cpos++;} else {//获取源区间RangeSet src = RangeSet::Parse(params.tokens[params.cpos++]);CHECK(static_cast<bool>(src));//获取源区间和升级区间的交集*overlap = src.Overlaps(tgt);//对比源block数和src_rangeif (ReadBlocks(src, params.buffer, params.fd) == -1) {return -1;}//如果没有了,说明没stash的情况 if (params.cpos >= params.tokens.size()) {// no stashes, only source rangereturn 0;}//后面时处理move的情况,首先获取的src_locRangeSet 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 failureLOG(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为falseif (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 userPLOG(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/1fb420b778bead14ec9b2cf264fcaa8e147e3e39std::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=2const 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_mapstash_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.在验证模式下,我们不需要存储blockif (!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 0xEF53time_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_rangesconst 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 attemptedLOG(INFO) << filename->data << " image corrupted, attempting to recover...";// When opened with O_RDWR, libfec rewrites corrupted blocks when they are readfec::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 metadataif (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执行,所以控制重新写入并不是很复杂,通过一些标志位记录上次升级的进度,从而完成恢复升级.

六、Android recovery升级断电保护机制相关推荐

  1. Android Recovery升级原理

    文章目录 Android Recovery升级原理 声明 摘要 1. Recovery相关概念 2. Android系统的启动模式 2.1 Android 各个分区介绍 2.2 Android的启动模 ...

  2. android 系统升级 方法,Android Recovery 升级方法

    注意:升级有风险,请注意备份好相关数据 前置条件 1.安装 Android SDK Platform-Tools 2.使用 ADB 调试工具 1.打开调试模式开关 目前,升级需要执行 adb 命令,使 ...

  3. android受限广播保护机制,Android受限广播保护机制

    广播(Broadcast)作为消息传递的一种方式,在Android系统中有着广泛的应用.系统提供了一系列的广播发送.接收接口,可以非常方便的实现广播的传递.系统中相当部分的状态变化也都是通过广播的方式 ...

  4. android recovery 模块知识需求汇总

    关于android recovery的一个目录,后续继续更新,未完待续...... Android recovery模块介绍: (一)android为什么需要recovery升级? (二)androi ...

  5. Android OTA升级原理和流程分析(五)---update.zip包从上层进入Recovery服务

    转载自:http://blog.chinaunix.net/uid-22028566-id-3533854.html 文章开头我们就提到update.zip包来源有两种: 一个是OTA在线下载(一般下 ...

  6. Android系统升级 Recovery模式(02)Recovery升级过程

    该系列文章总纲链接:专题分纲目录 Android系统升级 Recovery模式 本章关键点总结 & 说明: 导图是不断迭代的,这里主要关注➕ recovery升级过程部分即可,主要从 一般升级 ...

  7. Android原生OTA和Recovery升级过程步骤

    本文介绍了Android原生OTA和Recovery升级过程步骤. 进入升级 - 1.1 正常启动和进入Recovery的区别 下面给出了升级流程的简单示意图. 上图中的上下两个部分,上面一部分是正常 ...

  8. Android OTA 升级之三:生成recovery.img

    Android OTA 升级之三:生成recovery.img 作者: 宋立新 Email:zjujoe@yahoo.com 前言 得到了ota升级包后,我们就可以用它来升级系统了.Android 手 ...

  9. Android OTA 升级专栏文章导读

    Android OTA 升级专栏文章导读 文章目录 Android OTA 升级专栏文章导读 1. 快速入口 2. 简要介绍 1. 基础入门:<Android A/B 系统>系列 2. 核 ...

最新文章

  1. 在家学习的核心就是专注
  2. apache URL重写
  3. luogu P1330 封锁阳光大学
  4. MySQL Workbench的使用教程 (初级入门版)
  5. 【mmdetection2.0错误】——ModuleNotFoundError: No module named ‘mmdet‘
  6. 理解JS的6种继承方式
  7. 使用ANT编译项目报错 com.sun.image.codec.jpeg does not exist 解决方法
  8. Linux系统各个目录的作用(中英文对照)
  9. 因子分析后如何进行聚类分析?
  10. nio java是什么_JAVA NIO是什么(zz)
  11. 北美票房:奥斯卡提名影片票房回春
  12. 1. 如何创建python环境
  13. IP数据报首部的格式:关于标识(identification)、标志(flag)和片偏移
  14. 新增10所高校获批虚拟现实技术本科专业,中国市场将超千亿
  15. 超级经典的人生格言(搞笑)
  16. VPN 虚拟专用网络隧道
  17. 浅谈四种常见的agv导航方式及各自的优缺点
  18. 操作系统 计算机操作系统教程笔记
  19. 阵列卡直通模式和raid模式_DNF:希洛克Raid攻略,一不小心就被全屏秒杀
  20. iOS - CoreData了解和简单应用

热门文章

  1. 代码审计—espcmsv5注入测试复现
  2. 百度地图AndroidSDK:定位、画区域、线路规划、搜索
  3. 明解C语言入门篇_第5章_数组
  4. 关于64QAM调制,软硬解调详解
  5. GEE 遥感特征指数谐波分析 以Sentinel-2 NDVI为例(含完整代码链接)
  6. qnx环境下编译ffmpeg及解码mp4实践
  7. 解决打开VM虚拟机蓝屏问题
  8. 工业智能网关BL110应用之58:如何实现智能楼宇控制BACnet 接入OPC UA云平台
  9. 你的优质开发助手,一键自动生成代码
  10. 基于相关性变量筛选偏最小二乘回归的多维相关时间序列建模方法