本篇将具体分析执行写入的流程,,整理完该流程后,将通过升级的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(¶ms.nti.mu, nullptr); pthread_cond_init(¶ms.nti.cv, nullptr); pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); int error = pthread_create(¶ms.thread, &attr, unzip_new_data, ¶ms.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], ¶ms.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(¶ms.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(¶ms.nti.cv); pthread_mutex_unlock(¶ms.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(¶ms.nti.mu); pthread_cond_destroy(¶ms.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(¶ms.nti.mu); params.nti.writer = std::make_unique<RangeSinkWriter>(params.fd, tgt); pthread_cond_broadcast(¶ms.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(¶ms.nti.mu); return -1; } pthread_cond_wait(¶ms.nti.cv, ¶ms.nti.mu); } pthread_mutex_unlock(¶ms.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执行,所以控制重新写入并不是很复杂,通过一些标志位记录上次升级的进度,从而完成恢复升级.