MTK 8.1 recovery_log分析

本文详细解析了MTK 8.1设备上的recovery_log,深入探讨了recovery模式启动流程,包括加载分区表、参数解析、界面初始化、selinux配置及更新流程。特别关注了OTA安装过程,从下载更新包到最终安装验证,揭示了系统如何处理更新包的校验、映射和执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

MTK 8.1 recovery_log分析01

 

本次分析以对应last_log推出recovery主要的执行流程

涉及文件:

bootable/recovery/recovery.cpp

bootable/recovery/install.cpp

bootable/recovery/roots.cpp

cache/recovery_log/last_log

out/target/product/k63v2_64_bsp/recovery/root/etc/recovery.fstab,OBJ包中路径/RECOVERY/RAMDISK/etc/recovery.fstab

 

原生 OTA INSTALL信息

 * OTA INSTALL

 * 1. main system downloads OTA package to /cache/some-filename.zip

 * 2. main system writes "--update_package=/cache/some-filename.zip"

 * 3. main system reboots into recovery

 * 4. get_args() writes BCB with "boot-recovery" and "--update_package=..."

 *    -- after this, rebooting will attempt to reinstall the update --

 * 5. install_package() attempts to install the update

 *    NOTE: the package install must itself be restartable from any point

 * 6. finish_recovery() erases BCB

 *    -- after this, rebooting will (try to) restart the main system --

 * 7. ** if install failed **

 *    7a. prompt_and_wait() shows an error icon and waits for the user

 *    7b. the user reboots (pulling the battery, etc) into the main system

 */

 

 

 

Recovery.cpp

 

int main(int argc, char **argv) {

 

1.加载分区表

printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start));

 

Last_log:Starting recovery (pid 193) on Wed Jun 20 12:04:43 2018

//加载recovery.fstab建立分区表信息  

load_volume_table();

//从建立的分区表信息中读取是否有cache分区,log信息存入cache分区中

has_cache = volume_for_mount_point(CACHE_ROOT) != nullptr;

 

Roots.cpp

void load_volume_table(){

 //在last_log中打印分区表信息,顺序为编号,挂载节点,文件系统类型,块设备,长度

    printf("recovery filesystem table\n");

    printf("=========================\n");

    for (i = 0; i < fstab->num_entries; ++i) {

        Volume* v = &fstab->recs[i];

        printf("  %d %s %s %s %lld\n", i, v->mount_point, v->fs_type,

               v->blk_device, v->length);

    }

    printf("\n");

}

Last_log:

[    0.004364] recovery filesystem table

[    0.004383] =========================

[    0.004403]   0 /system ext4 /dev/block/platform/mtk-msdc.0/11120000.msdc0/by-name/system 0

[    0.004416]   1 /vendor ext4 /dev/block/platform/mtk-msdc.0/11120000.msdc0/by-name/vendor 0

[    0.004427]   2 /system ext4 /dev/block/platform/mtk-msdc.0/11120000.msdc0/by-name/system 0

.........

[    0.004717]   28 /sdcard_dev2 vfat /dev/block/platform/mtk-msdc.0/11120000.msdc0/by-name/intsd 0

 

  1. 获取参数信息

//get_args方法获取misc分区的command文件

    std::vector<std::string> args = get_args(argc, argv);

    std::vector<char*> args_to_parse(args.size());

    std::transform(args.cbegin(), args.cend(), args_to_parse.begin(),

                   [](const std::string& arg) { return const_cast<char*>(arg.c_str()); });

Last_log: 

I:Boot command: boot-recovery

I:Got 3 arguments from boot message

 

//通过getopt_long方法将command参数转化为字符打印到last_log中

    while ((arg = getopt_long(args_to_parse.size(), args_to_parse.data(), "", OPTIONS,

                              &option_index)) != -1) {

    if (locale.empty()) {

        if (has_cache) {

            locale = load_locale_from_cache();

        }

 

        if (locale.empty()) {

            locale = DEFAULT_LOCALE;

        }

    }

    printf("locale is [%s]\n", locale.c_str());

    printf("stage is [%s]\n", stage.c_str());

    printf("reason is [%s]\n", reason);

 

Last_log:

[    0.032947] locale is [en-US]

[    0.032978] stage is []

[    0.032990] reason is [(null)]

 

//界面显示系统更新,如果有--security界面显示系统安全更新

   ui->SetSystemUpdateText(security_update);

    int st_cur, st_max;

    if (!stage.empty() && sscanf(stage.c_str(), "%d/%d", &st_cur, &st_max) == 2) {

        ui->SetStage(st_cur, st_max);

}

//显示recovery的背景

ui->SetBackground(RecoveryUI::NONE);

//显示recovery的菜单

if (show_text) ui->ShowText(true);

//设置selinux的权限

    sehandle = selinux_android_file_context_handle();

    selinux_android_set_sehandle(sehandle);

    if (!sehandle) {

        ui->Print("Warning: No file_contexts\n");

    }

device->StartRecovery();

//打印command信息

printf("Command:");

 

 

 

Last_log:

[    0.943145] SELinux: Loaded file_contexts

[    0.943218] Command: "/sbin/recovery" "--update_package=/storage/3991-1BE4/Android/data/com.adups.fota/files/adupsfota/update.zip" "--locale=en-US"

//引入FOTA lib路径转换

    //adupsfota start

    #ifdef ADUPS_FOTA_SUPPORT

    if (perform_fota == 1) {

find_update_package_new(update_package);

Last_log:

[    0.943249] E:fota lib version: V1.0.2_20171214

[    0.943261] (replacing path "/storage/3991-1BE4/Android/data/com.adups.fota/files/adupsfota/update.zip" with "/sdcard/Android/data/com.adups.fota/files/adupsfota/update.zip")

 

//打印出手机里的属性信息,所以last_log里的属性值是基础版本中的

    property_list(print_property, NULL);

    printf("\n");

 

    ui->Print("Supported API: %d\n", RECOVERY_API_VERSION);

fprintf(stdout, "update_package = %s\n", update_package ? update_package : "NULL");

 

Last_log:

[    0.944492] persist.mtk.wcn.combo.chipid=-1

[    0.944517] persist.mtk.wcn.patch.version=-1

........

[    0.953494] ro.config.alarm_alert=RingingAlarm.ogg

[    0.953505] ro.config.notification_sound=MyPhone_Tune.ogg

[    0.953516]

[    0.953526] Supported API: 3

[    0.969002] update_package = /sdcard/Android/data/com.adups.fota/files/adupsfota/update.zip

[    0.971334] charge_status 3, charged 0, status 0, capacity 36

  1. 进入升级流程

    int status = INSTALL_SUCCESS;

 

    if (update_package != NULL) {

        // It's not entirely true that we will modify the flash. But we want

        // to log the update attempt since update_package is non-NULL.

        modified_flash = true;

    ......

    } else {

            status = install_package(update_package, &should_wipe_cache,

                                     TEMPORARY_INSTALL_FILE, true, retry_count);

            //adupsfota start

            #ifdef ADUPS_FOTA_SUPPORT

            if (perform_fota == 1) {

                finish_adupsfota(update_package, TEMPORARY_LOG_FILE, status);

        }

            #endif

            //adupsfota end

 

Install.cpp

//具体执行安装在really_install_package方法中

int install_package(const std::string& path, bool* wipe_cache, const std::string& install_file,

                    bool needs_mount, int retry_count) {

 

    result = really_install_package(path, wipe_cache, needs_mount, &log_buffer, retry_count,

                                    &max_temperature);

 

 

static int really_install_package(const std::string& path, bool* wipe_cache, bool needs_mount,

                                  std::vector<std::string>* log_buffer, int retry_count,

                                  int* max_temperature) {

//屏幕显示正在更新

  ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);

  ui->Print("Finding update package...\n");

  // Give verification half the progress bar...

//设置进度条的类型

  ui->SetProgressType(RecoveryUI::DETERMINATE);

//显示进度条

  ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME);

//log中显示升级的路径

  LOG(INFO) << "Update location: " << path;

 

Last_log:

[    1.020396] Finding update package...

[    1.088289] I:Update location: /sdcard/Android/data/com.adups.fota/files/adupsfota/update.zip

 

  // Map the update package into memory. 映射升级包路径到内存

  ui->Print("Opening update package...\n");

//判断路径是否需要去挂载,带@的路径是加密的data,不带@的是sdcard

  if (needs_mount) {

    if (path[0] == '@') {

      ensure_path_mounted(path.substr(1).c_str());

    } else {

      ensure_path_mounted(path.c_str());

    }

  }

//映射升级包路径到内存

  MemMapping map;

  if (!map.MapFile(path)) {

    LOG(ERROR) << "failed to map file";

    return INSTALL_CORRUPT;

  }

Last_log: [    1.088382] Opening update package...

  // Verify package.  校验升级包,所以校验签名的方法是在install.cpp中verify_package执行

  if (!verify_package(map.addr, map.length)) {

    log_buffer->push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure));

    return INSTALL_CORRUPT;

  }

Last_log:

[    1.149501] I:read key e=65537 hash=20

[    1.149677] I:1 key(s) loaded from /res/keys

[    1.149730] Verifying update package...

[    1.160982] I:comment is 1555 bytes; signature is 1537 bytes from end

[    1.649360] I:signature (offset: 8a60c3, length: 5fb): 308205f706092a864886f70d010702a08205e8308205e4020101310b300906052b0e03021a0500300b06092a864886f70d010701a08203f930820[    1.655325] I:whole-file signature verified against RSA key 0

[    1.655373] Update package verification took 0.5 s (result 0).

 

  // Try to open the package.

  ZipArchiveHandle zip;

  int err = OpenArchiveFromMemory(map.addr, map.length, path.c_str(), &zip);

  if (err != 0) {

    LOG(ERROR) << "Can't open " << path << " : " << ErrorCodeString(err);

    log_buffer->push_back(android::base::StringPrintf("error: %d", kZipOpenFailure));

    CloseArchive(zip);

    return INSTALL_CORRUPT;

  }

  // Check partition size between source and target  校验分区表

#ifndef AB_OTA_UPDATER

  int ret=INSTALL_SUCCESS;

  if (mt_really_install_package_check_part_size(ret, path.c_str(), needs_mount, zip)) {

    CloseArchive(zip);

    return ret;

  }

#endif

 

Last_log:

[    1.684768] ====== Scatter File:

[    1.684832] preloader 0x0

.......

.......

[    1.685539] userdata 0x7e000000

[    1.685571] mt_is_support_gpt gpt prefix is /dev/block/platform/mtk-msdc.0/11120000.msdc0/by-name

[    1.685596] is_gpt = 1

[    1.685619] gpt prefix is /dev/block/platform/mtk-msdc.0/11120000.msdc0/by-name

[    1.703289] Parse Partition sucessfully

 

  // Additionally verify the compatibility of the package.

  if (!verify_package_compatibility(zip)) {

    log_buffer->push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure));

    CloseArchive(zip);

    return INSTALL_CORRUPT;

  }

 

Last_log:

[    1.703340] I:Verifying package compatibility...

[    1.703393] I:Package doesn't contain compatibility.zip entry

 

  // Verify and install the contents of the package.

  ui->Print("Installing update...\n");

  if (retry_count > 0) {

    ui->Print("Retry attempt: %d\n", retry_count);

  }

  ui->SetEnableReboot(false);

//具体执行升级的地方

  int result = try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature);

  ui->SetEnableReboot(true);

  ui->Print("\n");

 

Last_log:

[    1.703416] Installing update...

[    1.771248] platform fs_type: EMMC

[    1.771299] ====== Updater-Script:

.........

 

  CloseArchive(zip);

  return result;

}

 

 

 

 

 

<think>首先,用户的问题是:“我想分析函数的功能和作用 请问分析 static bool recovery_check 函数的作用及实现逻辑”。用户要求分析一个名为 `recovery_check` 的静态布尔函数的作用和实现逻辑。 从系统级指令中,我需要: - 所有行内数学表达式使用 `$...$` 格式。 - 独立公式使用 `$$...$$` 格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,例如 [^1]。 用户可见层的指令: - 回答结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠。 用户提供了几个引用: - 引用[1]: 关于 `ipoib_reap_neigh` 函数。 - 引用[2]: 关于 `autoboot_command` 函数。 - 引用[3]: 关于 MySQL relay_log_recovery 的代码片段。 - 引用[4]: 关于 Xposed 和 Android zygote 的分析。 用户说:“上述引用仅供参考,并不是用户真正的需求,请尽可能多的参考用户上一次的问题和你的回答。” 但在这个对话中,这是第一条消息,所以没有“上一次的问题”。我需要基于当前问题回答。 用户的问题是关于分析 `static bool recovery_check` 函数的作用和实现逻辑。但用户没有提供这个函数的代码或上下文。用户提到的引用可能与这个函数相关?引用[3] 提到了 `recovery_check`:在 MySQL relay_log_recovery 的上下文中。 看引用[3]: “MySQL relay_log_recovery源码分析。if (!init_error & mi->rli->is_relay_log_recovery && mi->rli->mts_recovery_group_cnt) init_error = fill_mts_gaps_and_recover(mi);” 这看起来像是代码片段,但并没有直接提到 `recovery_check` 函数。用户说“分析 static bool recovery_check 函数”,但引用中没有这个函数名。可能用户指的是这个代码片段或相关部分。 在引用[3]中,有 `mi->rli->is_relay_log_recovery`,这可能是一个标志,但没有 `recovery_check` 函数。或许在完整的上下文中存在。 用户说:“我想分析函数的功能和作用 请问分析 static bool recovery_check 函数的作用及实现逻辑”,并提供了引用。可能用户认为引用[3] 与这个函数相关。 在引用[3]中,没有 `recovery_check` 函数。让我检查其他引用: - 引用[1]: `ipoib_reap_neigh` – 似乎不相关。 - 引用[2]: `autoboot_command` – 不相关。 - 引用[3]: MySQL relay log recovery – 可能相关。 - 引用[4]: Xposed and Android – 可能不直接相关。 用户可能在之前的上下文中有提到 `recovery_check`,但在这个消息中是第一次。我需要基于提供的信息。 用户说:“请尽可能多的参考用户上一次的问题和你的回答”,但既然这是第一条消息,我应该假设没有“上一次”。 或许用户错误地粘贴了引用,或者 `recovery_check` 是引用[3] 的一部分。在引用[3]中,有 `is_relay_log_recovery`,这可能是一个布尔变量,而不是函数。 用户明确说“static bool recovery_check 函数”,所以我需要分析这个函数。但代码没有提供。 在引用[3]中,有“init_error = fill_mts_gaps_and_recover(mi);”,这似乎是一个函数调用,但不是 `recovery_check`。 可能用户指的是一个通用的函数分析,或者期望我从引用中推断。 为了回答,我需要假设 `recovery_check` 是一个函数名,可能来自数据库或系统编程上下文。既然引用[3] 是关于 MySQL 的,我可以基于那个。 或者,用户可能想让我分析引用[3] 中的代码作为例子。 用户说:“分析 static bool recovery_check 函数”,所以我应该聚焦于那个。 但既然没有代码,我可以提供一个一般性的分析框架,或者基于引用[3] 解释类似函数。 引用[3] 说:“MySQL relay_log_recovery源码分析”,并显示代码:`if (!init_error & mi->rli->is_relay_log_recovery && mi->rli->mts_recovery_group_cnt) init_error = fill_mts_gaps_and_recover(mi);` 这看起来是检查是否需要进行恢复的逻辑。`is_relay_log_recovery` 可能是一个标志,`mts_recovery_group_cnt` 可能表示需要恢复的组数。 用户提到的 `recovery_check` 可能是一个函数,用于执行类似的检查。 为了回答,我可以: 1. 解释一般函数分析的方法。 2. 基于引用[3] 分析类似逻辑。 3. 生成相关问题。 结构回答: - 逐步解决问题。 - 先解释函数的作用。 - 然后分析实现逻辑。 - 使用中文。 - 添加引用标识。 - 生成相关问题。 由于没有具体代码,我可以假设一个场景或基于引用。 或许用户的意思是分析引用中的函数,但引用中没有 `recovery_check`。用户说“分析 static bool recovery_check 函数”,所以可能是一个错误。 在引用[4]中,有“recovery”,但也不是函数。 我应该询问更多细节,但作为AI,我需要基于现有信息回答。 假设 `recovery_check` 是一个函数,用于检查系统是否需要恢复操作,常见于数据库或系统恢复上下文。 基于引用[3],我可以推断。 定义函数: static bool recovery_check(/* 参数 */) { // 实现 } 作用:检查是否满足恢复条件。 实现逻辑:可能检查一些标志或状态。 在回答中,我可以这样说: 由于没有提供具体代码,我将基于常见场景分析。如果 `recovery_check` 是类似引用[3] 中的函数,它可能用于检查恢复标志。 然后分析引用[3] 的代码。 在引用[3]中,代码是:`if (!init_error & mi->rli->is_relay_log_recovery && mi->rli->mts_recovery_group_cnt) init_error = fill_mts_gaps_and_recover(mi);` 这可以看作是一个检查:如果初始化没有错误,并且启用了中继日志恢复,并且有需要恢复的组,则调用恢复函数。 所以,一个 `recovery_check` 函数可能返回布尔值表示是否需要恢复。 在回答中: - 介绍函数作用。 - 分析逻辑。 - 使用引用[3] 作为例子。 - 添加引用标识。 生成相关问题。 确保使用中文。 最终回答结构: 1. 函数作用解释。 2. 实现逻辑分析。 3. 基于引用的例子。 4. 相关问题。</think>### 分析 `static bool recovery_check` 函数的作用及实现逻辑 由于您未提供 `static bool recovery_check` 函数的具体代码,我将基于常见编程上下文(特别是数据库或系统恢复场景)和您提供的引用信息(尤其是引用[3]关于 MySQL `relay_log_recovery` 的分析)进行推断性分析。`recovery_check` 函数通常出现在需要检查系统状态以决定是否执行恢复操作的模块中,例如数据库恢复、系统错误处理或进程监控。以下分析将逐步展开,确保结构清晰可靠。 #### 1. **函数的作用** - **核心作用**:`recovery_check` 是一个静态布尔函数,主要用于检查系统是否满足恢复条件(如错误状态、日志不一致或资源异常)。如果条件满足,它返回 `true`,触发后续恢复流程;否则返回 `false`,表示无需恢复。这有助于实现自动化错误处理和状态恢复,提高系统鲁棒性。 - **典型应用场景**: - 在数据库系统中(如 MySQL),用于检测中继日志(relay log)是否需要恢复,确保数据一致性和复制可靠性(参考引用[3])。 - 在嵌入式或系统级编程中,用于检查启动或运行时的错误状态,类似引用[2]中的 `autoboot_command` 函数对倒计时的检测。 - 一般用于模块化设计:作为内部辅助函数,封装状态检查逻辑,避免代码重复。 例如,在 MySQL 的上下文中,类似函数可能检查中继日志的恢复标志和未处理任务数量,以决定是否调用 `fill_mts_gaps_and_recover` 等恢复函数[^3]。 #### 2. **实现逻辑分析** 基于常见实现模式(参考引用[3]的代码片段),`recovery_check` 函数的逻辑通常包括以下步骤: - **输入参数**:函数可能接收指向结构体或对象的指针(如 `mi` 表示 Master Info),用于访问系统状态变量。 - **内部检查**:通过布尔逻辑组合多个条件,例如: - 检查初始化错误标志(如 `init_error`)是否为假(表示无错误)。 - 验证恢复标志(如 `is_relay_log_recovery`)是否启用。 - 判断待恢复任务数量(如 `mts_recovery_group_cnt`)是否大于零。 - **返回值**:如果所有条件满足,返回 `true`(表示需要恢复);否则返回 `false`。 - **错误处理**:函数通常不直接执行恢复操作,而是作为“检查器”为上层函数(如 `recovery_main`)提供决策依据。 **伪代码示例(基于引用[3]推断)**: ```c static bool recovery_check(struct MasterInfo *mi) { // 步骤1: 检查初始化是否无错误 if (init_error) { return false; // 有错误,无需恢复 } // 步骤2: 验证恢复标志和任务数量 if (mi->rli->is_relay_log_recovery && mi->rli->mts_recovery_group_cnt > 0) { return true; // 条件满足,需要恢复 } return false; // 默认返回false } ``` - **逻辑解释**: - **条件组合**:使用逻辑运算符(如 `&&`)确保所有条件同时满足。这类似于引用[3]中的 `if (!init_error & mi->rli->is_relay_log_recovery && mi->rli->mts_recovery_group_cnt)`,该检查用于决定是否调用 `fill_mts_gaps_and_recover`[^3]。 - **性能优化**:函数是 `static`,限制作用域在当前文件,提高封装性和安全性;返回 `bool` 类型确保结果简洁。 - **可靠性机制**:在系统恢复中,此类函数常与定时任务或事件驱动结合(如引用[1]中的 `ipoib_reap_neigh` 使用工作队列定期执行回收)[^1]。 #### 3. **与引用示例的关联** - 在引用[3]中,MySQL 的恢复逻辑直接内联了类似 `recovery_check` 的功能(即条件检查)。如果模块化,`recovery_check` 可封装此逻辑,使代码更清晰[^3]。 - 类似地,引用[2]的 `autoboot_command` 函数也涉及状态检查(如 `abortboot` 检测倒计时),但更侧重控制流而非恢复[^2]。 - 整体上,`recovery_check` 体现了“检查-执行”模式,常见于系统级编程以减少错误传播风险。 #### 4. **注意事项** - **实际实现依赖上下文**:真实函数可能涉及更多细节,如日志记录、资源锁或错误码处理。如果您提供具体代码,我可以进行更精准分析。 - **最佳实践**:此类函数应设计为无副作用(pure function),避免在检查过程中修改状态,确保可预测性。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值