Android recovery升级断电保护机制

https://blog.csdn.net/Android_2016/article/details/104793320

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

一.如何断电后重启继续进入升级
几句话总结

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

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

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

二.update-script恢复执行流程
几句话总结

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

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

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

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

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

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

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

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

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

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

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

sehandle = selinux_android_file_context_handle();
selinux_android_set_sehandle(sehandle);

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

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

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

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

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

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

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

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

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

return 0;
}
RegisterBlockImageFunctions();

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

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

1.range_sha1

2.block_image_verify

3.check_first_block

4.block_image_recover

5.block_image_update

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

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

ui_print("Patching system image after verification.");
show_progress(0.800000, 0);
block_image_update("/dev/block/platform/bootdevice/by-name/system", package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat") ||
abort("E1001: Failed to update system image.");
ui_print("Patching vendor image after verification.");
show_progress(0.100000, 0);
block_image_update("/dev/block/platform/bootdevice/by-name/vendor", package_extract_file("vendor.transfer.list"), "vendor.new.dat", "vendor.patch.dat") ||
abort("E2001: Failed to update vendor image.");
2.range_sha1 RangeSha1Fn方法
range_sha1("/dev/block/platform/bootdevice/by-name/system", "294,1,430,666,674,675,676,698,701,705,706,783,784,4944,4945,6224,6225,7870,7871,7872,7876,8065,80.......") == "8a083377999ff3720a5a650d10ab597bf92f826c"

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

2.1 参数分析

ota_from_target_files

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

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

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

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

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

ranges_str = ranges.to_string_raw()
#写入升级包的语句
script.AppendExtra(('if (range_sha1("%s", "%s") == "%s" || '
'block_image_verify("%s", '
'package_extract_file("%s.transfer.list"), '
'"%s.new.dat", "%s.patch.dat")) then') % (
self.device, ranges_str, expected_sha1,
self.device, partition, partition, partition))
script.Print('Verified %s image...' % (partition,))
script.AppendExtra('else')

#如果version 大于等于4,增加以下升级语句
if self.version >= 4:

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

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

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

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

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

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

for (size_t j = range.first; j < range.second; ++j) {
if (read_all(fd, buffer, BLOCKSIZE) == -1) {
ErrorAbort(state, kFreadFailure, "failed to read %s: %s", blockdev_filename->data.c_str(),
strerror(errno));
return StringValue("");
}

SHA1_Update(&ctx, buffer.data(), BLOCKSIZE);
}
}
uint8_t digest[SHA_DIGEST_LENGTH];
SHA1_Final(digest, &ctx);
//返回计算出的sha1值
return StringValue(print_sha1(digest));
}
看着像是什么都没说,其实时我没细看,大致意思就是sha1的比对

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

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

 

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

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

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

return PerformBlockImageUpdate(name, state, argv, commands,
sizeof(commands) / sizeof(commands[0]), false);
}
3.1 PerformBlockImageUpdate
该方法大致总结:

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

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

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

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

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

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

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

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

 

veryfy log输出:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

start += 2;

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

int rc = -1;

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

//如果在整个cmd_map里没找到 则报错
if (cmd_map.find(params.cmdname) == cmd_map.end()) {
LOG(ERROR) << "unexpected command [" << params.cmdname << "]";
goto pbiudone;
}
//定义cmd 对应的执行的方法
const Command* cmd = cmd_map[params.cmdname];

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

// Skip all commands before the saved last command index when resuming an update.
// 恢复更新时,请跳过所有保存的最后一个命令索引之前的所有命令。verify不会执行
if (params.canwrite && params.cmdindex != -1 && params.cmdindex <= saved_last_command_index) {
LOG(INFO) << "Skipping already executed command: " << params.cmdindex
<< ", last executed command for previous update: " << saved_last_command_index;
continue;
}

//无法执行单行指令时报错 这里应该就是执行的地方了,依然没有看到verify只执行stash的地方
if (cmd->f(params) == -1) {
LOG(ERROR) << "failed to execute command [" << line << "]";
goto pbiudone;
}

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

rc = 0;

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

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

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

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

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

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

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

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

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

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

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

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

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

// Stash directory should be different for each partition to avoid conflicts
// when updating multiple partitions at the same time, so we use the hash of
// the block device name as the base directory
// 每个分区的存储目录应该不同,以避免冲突
// 同时更新多个分区时,因此我们将块设备名称的哈希值用作基本目录
uint8_t digest[SHA_DIGEST_LENGTH];
SHA1(reinterpret_cast<const uint8_t*>(blockdev.data()), blockdev.size(), digest);
base = print_sha1(digest);
//这里就看0和1的情况
std::string dirname = GetStashFileName(base, "", "");
struct stat sb;
int res = stat(dirname.c_str(), &sb);
size_t max_stash_size = maxblocks * BLOCKSIZE;

//等于-1的情况,创建失败
if (res == -1 && errno != ENOENT) {
ErrorAbort(state, kStashCreationFailure, "stat \"%s\" failed: %s", dirname.c_str(),
strerror(errno));
return -1;
//如果不等于0 创建文件夹
} else if (res != 0) {
LOG(INFO) << "creating stash " << dirname;
res = mkdir(dirname.c_str(), STASH_DIRECTORY_MODE);

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

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

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

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

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

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

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

return 0; // Using existing directory
}

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

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

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

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

return true;
}

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

3.4 PerformCommandErase
erase 4,447643,622080,623105,644615

 

first:

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

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

 

retry

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

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

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

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

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

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

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

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

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

return 0;
}

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

 

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

first:

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

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

区间有交集:

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

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

2.执行patch

patching 1487 blocks to 797

3.删除stash

deleting /cache/recovery/1fb420b778bead14ec9b2cf264fcaa8e147e3e39/0cee47e21239852586ca5f509b7c7d20c03b921f

区间无交集:

执行patch patching 32 blocks to 19

 

retry:

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

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

android recovery原理讲解
pdf

4星
超过85%的资源
225KB

下载
//指令为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

android一键recovery升级程序实例
7z

4星
超过85%的资源
27KB

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

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

return 0;
}

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

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

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

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

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

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

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

3.8 PerformCommandStash
执行stash命令行的方法

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

 

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

 

first:

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

update 存储stash

stashing 4 blocks to 357e80f1cda6acf368741046b37a6bc34e291c53

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

 

retry:

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

loading /cache/recovery/1fb420b778bead14ec9b2cf264fcaa8e147e3e39/357e80f1cda6acf368741046b37a6bc34e291c53

update

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

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

allocate(src.blocks() * BLOCKSIZE, params.buffer);
//判断是不是可以正常读取
if (ReadBlocks(src, params.buffer, params.fd) == -1) {
return -1;
}
blocks = src.blocks();
//将src放入stash_map
stash_map[id] = src;

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

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

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

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

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

3.9 PerformCommandFree
指令为free的处理 free 9ebd6c1590f22c76c1fc8ff13c691d1d011ad9ee

deleting /cache/recovery/1fb420b778bead14ec9b2cf264fcaa8e147e3e39/9ebd6c1590f22c76c1fc8ff13c691d1d011ad9ee

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

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

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

return 0;
}

 

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

zeroing 1024 blocks

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

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

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

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

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

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

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

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

return 0;
}

3.11 PerformCommandNew
new 2,0,1

writing 1 blocks of new data

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

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

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

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

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

pthread_mutex_unlock(&params.nti.mu);
}

params.written += tgt.blocks();

return 0;
}

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

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

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

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

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

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

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

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

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

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

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

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

return StringValue("t");
}

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

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

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

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

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

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

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

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

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

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

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

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

本次主要分析了 verify阶段和update阶段的的具体升级流程,总的来说,因为transfer.list 是逐行cmd执行,所以控制重新写入并不是很复杂,通过一些标志位记录上次升级的进度,从而完成恢复升级.
————————————————
版权声明:本文为CSDN博主「李标标」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Android_2016/article/details/104793320

posted @ 2022-08-05 14:11  leo21sun  阅读(1049)  评论(0编辑  收藏  举报