Chinaunix首页 | 论坛 | 博客
  • 博客访问: 61646
  • 博文数量: 13
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 47
  • 用 户 组: 普通用户
  • 注册时间: 2012-12-03 22:13
文章分类
文章存档

2015年(10)

2013年(3)

我的朋友

分类: LINUX

2015-04-01 20:52:11

recovery代码分析之二


1.函数get_args()

        首先考虑如下情形:在main system下,我们通过OTA客户端(自己编写)选择升级包update.zip(假设存放于/mnt/sdcard中)后进入recovery模式下进行升级。在这一过程中,需要解决两个问题,第一:如果使机器重启并自动进入recovery模式;第二:如何将升级包的路径/mnt/sdcard/update.zip传入到recovery模式,使其能查找到升级包。这涉及到android系统的启动流程,如下图所示:

图1 系统启动流程

        机器启动时,首先检测是否有组合键按下,如检测到(音量下+power)组合键,则进入recovery;否则检测系统的/misc分区,根据此分区存储的命令选择不同的模式。

/misc分区下存储着结构体bootloader_message,称之为BCB块,其定义如下:

struct bootloader_message{

      char command[32];    //存放不同的启动命令

      char status[32];     //存放执行结果

      char recovery[1024];   //存放/cache/recovery/command中的命令

 };

        结构体成员command[32]中存放着不同的启动命令:

  •         boot-recovery:系统会进入Recovery模式
  •         update-radia或update-hboot:
  •         command为空:正常启动,进入main system

        而recovery[1024]中则存放着升级包路径,其存储结构如下:第一行存放字符串“recovery”;第二行存放路径信息“--update=/mnt/sdcard/update.zip”等。

        除了BCB块外,还可以将路径信息--update=/mnt/sdcard/update.zip写入文件/cache/recovery/command传递给recovery模式。

        进入recovery模式,系统通过get_args函数(如下代码所示)获取升级包信息。此函数首先获取BCB块信息(代码07-34),如果未检测到相关信息,则继续检测/cache/

recovery/command文件(代码36-53);最后,将启动命令boot-recovery及升级包路径--update=/mnt/sdcard/update.zip重新写入到BCB块中(代码55-64),以便系统下次启动时再次进入到recovery模式,直到升级成功后执行finish_recovery函数清空BCB及/cache/recovery/command文件。

  1. // command line args come from, in decreasing precedence:  
  2. //   - the actual command line   
  3. //   - the bootloader control block (one per line, after "recovery")  
  4. //   - the contents of COMMAND_FILE (one per line)  
  5. static void  
  6. get_args(int *argc, char ***argv) {  
  7.     struct bootloader_message boot;  
  8.     memset(&boot, 0, sizeof(boot));  
  9.     get_bootloader_message(&boot);  // this may fail, leaving a zeroed structure  
  10.   
  11.     if (boot.command[0] != 0 && boot.command[0] != 255) {  
  12.         LOGI("Boot command: %.*s\n"sizeof(boot.command), boot.command);  
  13.     }  
  14.   
  15.     if (boot.status[0] != 0 && boot.status[0] != 255) {  
  16.         LOGI("Boot status: %.*s\n"sizeof(boot.status), boot.status);  
  17.     }  
  18.   
  19.     // --- if arguments weren't supplied, look in the bootloader control block  
  20.     if (*argc <= 1) {  
  21.         boot.recovery[sizeof(boot.recovery) - 1] = '\0';  // Ensure termination  
  22.         const char *arg = strtok(boot.recovery, "\n");  
  23.         if (arg != NULL && !strcmp(arg, "recovery")) {  
  24.             *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);  
  25.             (*argv)[0] = strdup(arg);  
  26.             for (*argc = 1; *argc < MAX_ARGS; ++*argc) {  
  27.                 if ((arg = strtok(NULL, "\n")) == NULL) break;  
  28.                 (*argv)[*argc] = strdup(arg);  
  29.             }  
  30.             LOGI("Got arguments from boot message\n");  
  31.         } else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {  
  32.             LOGE("Bad boot message\n\"%.20s\"\n", boot.recovery);  
  33.         }  
  34.     }  
  35.   
  36.     // --- if that doesn't work, try the command file  
  37.     if (*argc <= 1) {  
  38.         FILE *fp = fopen_path(COMMAND_FILE, "r");  
  39.         if (fp != NULL) {  
  40.             char *argv0 = (*argv)[0];  
  41.             *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);  
  42.             (*argv)[0] = argv0;  // use the same program name  
  43.   
  44.             char buf[MAX_ARG_LENGTH];  
  45.             for (*argc = 1; *argc < MAX_ARGS; ++*argc) {  
  46.                 if (!fgets(buf, sizeof(buf), fp)) break;  
  47.                 (*argv)[*argc] = strdup(strtok(buf, "\r\n"));  // Strip newline.  
  48.             }  
  49.   
  50.             check_and_fclose(fp, COMMAND_FILE);  
  51.             LOGI("Got arguments from %s\n", COMMAND_FILE);  
  52.         }  
  53.     }  
  54.   
  55.     // --> write the arguments we have back into the bootloader control block  
  56.     // always boot into recovery after this (until finish_recovery() is called)  
  57.     strlcpy(boot.command, "boot-recovery"sizeof(boot.command));  
  58.     strlcpy(boot.recovery, "recovery\n"sizeof(boot.recovery));  
  59.     int i;  
  60.     for (i = 1; i < *argc; ++i) {  
  61.         strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));  
  62.         strlcat(boot.recovery, "\n"sizeof(boot.recovery));  
  63.     }  
  64.     set_bootloader_message(&boot);  
  65. }  

代码段1 get_args()函数

2.函数install_package()

        此函数定义如下

  1. int  
  2. install_package(const char* path, int* wipe_cache, const char* install_file)  
  3. {  
  4.     FILE* install_log = fopen_path(install_file, "w");  
  5.     if (install_log) {  
  6.         fputs(path, install_log);  
  7.         fputc('\n', install_log);  
  8.     } else {  
  9.         LOGE("failed to open last_install: %s\n", strerror(errno));  
  10.     }  
  11.     int result = really_install_package(path, wipe_cache);  
  12.     if (install_log) {  
  13.         fputc(result == INSTALL_SUCCESS ? '1' : '0', install_log);  
  14.         fputc('\n', install_log);  
  15.         fclose(install_log);  
  16.     }  
  17.     return result;  
  18. }  

代码段2 install_package()函数定义
       

此函数所实现的功能为:调用函数really_install_package进行升级,并将升级结果写入到文件install_file中。recovery.c中对于存放升级结果的文件定义如下:

static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install";

当升级完成后进入main system,可以通过此文件读取升级结果,并给出用户相应的提示:升级成功或失败。

而函数really_install_package会对升级包进行一系列的校验,通过校验后,调用try_update_binary函数完成升级。因此,try_update_binary()才是真正升级的地方。如下:

  1. // If the package contains an update binary, extract it and run it.  
  2. static int  
  3. try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) {  
  4.     const ZipEntry* binary_entry =  
  5.             mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME);  
  6.     if (binary_entry == NULL) {  
  7.         mzCloseZipArchive(zip);  
  8.         return INSTALL_CORRUPT;  
  9.     }  
  10.   
  11.     char* binary = "/tmp/update_binary";  
  12.     unlink(binary);  
  13.     int fd = creat(binary, 0755);  
  14.     if (fd < 0) {  
  15.         mzCloseZipArchive(zip);  
  16.         LOGE("Can't make %s\n", binary);  
  17.         return INSTALL_ERROR;  
  18.     }  
  19.     bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd);  
  20.     close(fd);  
  21.     mzCloseZipArchive(zip);  
  22.   
  23.     if (!ok) {  
  24.         LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME);  
  25.         return INSTALL_ERROR;  
  26.     }  
  27.   
  28.     int pipefd[2];  
  29.     pipe(pipefd);  
  30.   
  31.     // When executing the update binary contained in the package, the  
  32.     // arguments passed are:   
  33.     //   
  34.     //   - the version number for this interface  
  35.     //   
  36.     //   - an fd to which the program can write in order to update the  
  37.     //     progress bar.  The program can write single-line commands:  
  38.     //   
  39.     //        progress    
  40.     //            fill up the next  part of of the progress bar  
  41.     //            over  seconds.  If  is zero, use  
  42.     //            set_progress commands to manually control the  
  43.     //            progress of this segment of the bar  
  44.     //   
  45.     //        set_progress   
  46.     //             should be between 0.0 and 1.0; sets the  
  47.     //            progress bar within the segment defined by the most  
  48.     //            recent progress command.  
  49.     //   
  50.     //        firmware <"hboot"|"radio">   
  51.     //            arrange to install the contents of  in the  
  52.     //            given partition on reboot.  
  53.     //   
  54.     //            (API v2:  may start with "PACKAGE:" to  
  55.     //            indicate taking a file from the OTA package.)  
  56.     //   
  57.     //            (API v3: this command no longer exists.)  
  58.     //   
  59.     //        ui_print   
  60.     //            display  on the screen.  
  61.     //   
  62.     //   - the name of the package zip file.  
  63.     //   
  64.   
  65.     char** args = malloc(sizeof(char*) * 5);  
  66.     args[0] = binary;  
  67.     args[1] = EXPAND(RECOVERY_API_VERSION);   // defined in Android.mk  
  68.     args[2] = malloc(10);  
  69.     sprintf(args[2], "%d", pipefd[1]);  
  70.     args[3] = (char*)path;  
  71.     args[4] = NULL;  
  72.   
  73.     pid_t pid = fork();  
  74.     if (pid == 0) {  
  75.         close(pipefd[0]);  
  76.         execv(binary, args);  
  77.         fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno));  
  78.         _exit(-1);  
  79.     }  
  80.     close(pipefd[1]);  
  81.   
  82.     *wipe_cache = 0;  
  83.   
  84.     char buffer[1024];  
  85.     FILE* from_child = fdopen(pipefd[0], "r");  
  86.     while (fgets(buffer, sizeof(buffer), from_child) != NULL) {  
  87.         char* command = strtok(buffer, " \n");  
  88.         if (command == NULL) {  
  89.             continue;  
  90.         } else if (strcmp(command, "progress") == 0) {  
  91.             char* fraction_s = strtok(NULL, " \n");  
  92.             char* seconds_s = strtok(NULL, " \n");  
  93.   
  94.             float fraction = strtof(fraction_s, NULL);  
  95.             int seconds = strtol(seconds_s, NULL, 10);  
  96.   
  97.             ui_show_progress(fraction * (1-VERIFICATION_PROGRESS_FRACTION),  
  98.                              seconds);  
  99.         } else if (strcmp(command, "set_progress") == 0) {  
  100.             char* fraction_s = strtok(NULL, " \n");  
  101.             float fraction = strtof(fraction_s, NULL);  
  102.             ui_set_progress(fraction);  
  103.         } else if (strcmp(command, "ui_print") == 0) {  
  104.             char* str = strtok(NULL, "\n");  
  105.             if (str) {  
  106.                 ui_print("%s", str);  
  107.             } else {  
  108.                 ui_print("\n");  
  109.             }  
  110.         } else if (strcmp(command, "wipe_cache") == 0) {  
  111.             *wipe_cache = 1;  
  112.         } else {  
  113.             LOGE("unknown command [%s]\n", command);  
  114.         }  
  115.     }  
  116.     fclose(from_child);  
  117.   
  118.     int status;  
  119.     waitpid(pid, &status, 0);  
  120.     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {  
  121.         LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status));  
  122.         return INSTALL_ERROR;  
  123.     }  
  124.   
  125.     return INSTALL_SUCCESS;  
  126. }  

代码段3 try_update_binary

代码04-21:提取升级包中的"META-INF/com/google/android/update-binary"文件,此文件定义了系统升级所需要进行的操作,系统将根据其中的命令执行相应的操作;

代码73-80:创建一个子进程,执行脚本文件META-INF/com/google/android/update-binary,这是系统升级的核心所在。如下是某升级包中update-script(update-script

                       是update-binary文件的文本形式,我们可以通过此文件来了解升级时的具体操作)中的部分指令:

  1. mount("ext4""EMMC""/dev/block/mmcblk0p12""/system");  
  2. assert(file_getprop("/system/build.prop""ro.build.fingerprint") == "ETON/sp8825eabase/sp8825ea:4.0.3/IML74K/D525A-V1_A04_20130322-094434:userdebug/test-keys" ||  
  3.        file_getprop("/system/build.prop""ro.build.fingerprint") == "ETON/sp8825eabase/sp8825ea:4.0.3/IML74K/D525A-V1_A05_20130322-094434:userdebug/test-keys");  
  4. assert(getprop("ro.product.device") == "sp8825ea" ||  
  5.        getprop("ro.build.product") == "sp8825ea");  
  6. ui_print("Verifying current system...");  
  7. ui_print("pre-build:ETON/sp8825eabase/sp8825ea:4.0.3/IML74K/D525A-V1_A04_20130322-094434:userdebug/test-keys");  
  8. ui_print("post-build:ETON/sp8825eabase/sp8825ea:4.0.3/IML74K/D525A-V1_A05_20130322-094434:userdebug/test-keys");  
  9. show_progress(0.100000, 0);  
  10. assert(apply_disk_space(8928364));  
  11. assert(apply_patch_check("/system/build.prop""098db2c72e97c3e84a1b14b782befe9446e44f49""64a46c872b8766b20feeaf2a4802ecae53a75e97"));  
  12. set_progress(0.000285);  
  13. assert(apply_patch_check("/system/lib/modules/blcr.ko""78dbaacbe60f9106d160dbe5cbd3778441c3b999""7eb55dbf8b929c8296a51f62d2c0cfb56a8a7f32"));  
  14. set_progress(0.014533);  
  15. assert(apply_patch_check("/system/lib/modules/blcr_imports.ko""9a42096b4b1da8345e9f87da68a492740de50a61""288ff8c8aafbd6c00b467ed89c4f2580937f77a8"));  
  16. set_progress(0.016106);  
  17. assert(apply_patch_check("EMMC:/dev/block/mmcblk0p2:5509716:78bbdc657109f192eb606371f12a30b881fbeda9:5512524:b1d56633f28bbed2a7530daeeee85b6710173ee0"));  
  18. set_progress(0.623307);  
  19. assert(apply_patch_check("EMMC:/dev/block/mmcblk0p3:3014656:406b98e17bae26628b46003189122b74b64c5992:3014656:c0e9e938c6a42fe42531d92cfb8dc0301be99885"));  
  20. set_progress(0.955538);  
  21. assert(apply_patch_check("EMMC:/dev/block/mmcblk0p1:344064:3e46eecb33050e461a80d38787e9e773ab09ff04:339968:c2e4ed3c6d9554fd1415a473f7097ec1a5d0845f"));  
  22. set_progress(0.993456);  
  23. assert(apply_patch_space(129286));  
  24.   
  25. # ---- start making changes here ----  
  26.   
  27. ui_print("Removing unneeded files...");  
  28. delete("/system/app/CalllogManager.apk""/system/lib/libext2_blkid.so",  
  29.        "/system/lib/libext2_com_err.so""/system/lib/libext2_e2p.so",  
  30.        "/system/lib/libext2_profile.so""/system/lib/libext2_uuid.so",  
  31.        "/system/lib/libext2fs.so",  
  32.        "/system/recovery.img");  
  33. show_progress(0.800000, 0);  
  34. ui_print("Patching system files...");  
  35. apply_patch("/system/lib/modules/blcr.ko""-",  
  36.             78dbaacbe60f9106d160dbe5cbd3778441c3b999, 129286,  
  37.             7eb55dbf8b929c8296a51f62d2c0cfb56a8a7f32, package_extract_file("patch/system/lib/modules/blcr.ko.p"));  
  38. set_progress(0.014247);  
  39. apply_patch("/system/lib/modules/blcr_imports.ko""-",  
  40.             9a42096b4b1da8345e9f87da68a492740de50a61, 14273,  
  41.             288ff8c8aafbd6c00b467ed89c4f2580937f77a8, package_extract_file("patch/system/lib/modules/blcr_imports.ko.p"));  
  42. set_progress(0.015820);  
  43. delete("/system/recovery-from-boot.p",  
  44.        "/system/etc/install-recovery.sh");  
  45. ui_print("Patching modem ...");  
  46. apply_patch("EMMC:/dev/block/mmcblk0p2:5509716:78bbdc657109f192eb606371f12a30b881fbeda9:5512524:b1d56633f28bbed2a7530daeeee85b6710173ee0",  
  47.             "/sdcard/modem.bin", b1d56633f28bbed2a7530daeeee85b6710173ee0,  
  48.             5512524,  
  49.             78bbdc657109f192eb606371f12a30b881fbeda9, package_extract_file("patch/modem.bin.p"));  
  50. set_progress(0.623294);  

代码段4 update-script内容

尽管并不确定这些代码的含义,但一眼望去我们仍能够分辨出其中包含的一些命令,诸如:mount,assert,ui_print,package_extract_file,apply_patch等。OK,继续往下读try_update_binary函数,读到一段while循环(86-116),心中一喜,这应该就是对应于各种命令的执行代码吧。遗憾的是,仔细读来,发现并非如此。while循环中只针对于progress、set_progress、wipe_cache、ui_print四种命令进行了处理,而package_extract_file、apply_patch等关键命令却了无踪影。显然,升级代码并非我想象的这么简单,在这个try_update_binary之后,一定还隐藏着其他东东。于是在./bootable目录下尝试搜索了apply_patch关键词,终于在./bootable/recovery/updater/install.c中找到了答案。

先看./bootable/recovery/updater/install.c中的一段代码:

  1. void RegisterInstallFunctions() {  
  2.     RegisterFunction("mount", MountFn);  
  3.     RegisterFunction("is_mounted", IsMountedFn);  
  4.     RegisterFunction("unmount", UnmountFn);  
  5.     RegisterFunction("format", FormatFn);  
  6.     RegisterFunction("show_progress", ShowProgressFn);  
  7.     RegisterFunction("set_progress", SetProgressFn);  
  8.     RegisterFunction("delete", DeleteFn);  
  9.     RegisterFunction("delete_recursive", DeleteFn);  
  10.     RegisterFunction("package_extract_dir", PackageExtractDirFn);  
  11.     RegisterFunction("package_extract_file", PackageExtractFileFn);  
  12.     RegisterFunction("retouch_binaries", RetouchBinariesFn);  
  13.     RegisterFunction("undo_retouch_binaries", UndoRetouchBinariesFn);  
  14.     RegisterFunction("symlink", SymlinkFn);  
  15.     RegisterFunction("set_perm", SetPermFn);  
  16.     RegisterFunction("set_perm_recursive", SetPermFn);  
  17.   
  18.     RegisterFunction("getprop", GetPropFn);  
  19.     RegisterFunction("file_getprop", FileGetPropFn);  
  20.     RegisterFunction("write_raw_image", WriteRawImageFn);  
  21.   
  22.     RegisterFunction("apply_patch", ApplyPatchFn);  
  23.     RegisterFunction("apply_patch_check", ApplyPatchCheckFn);  
  24.     RegisterFunction("apply_patch_space", ApplyPatchSpaceFn);  
  25.   
  26.     RegisterFunction("read_file", ReadFileFn);  
  27.     RegisterFunction("sha1_check", Sha1CheckFn);  
  28.   
  29.     RegisterFunction("wipe_cache", WipeCacheFn);  
  30.   
  31.     RegisterFunction("ui_print", UIPrintFn);  
  32.   
  33.     RegisterFunction("run_program", RunProgramFn);  
  34. }  

代码段5 updater-script与执行函数的映射

显而易见,对应于updater-script中的命令操作全都定义在./bootable/recovery/updater/install.c。系统升级时,由try_update_binary中创建的子进程执行这些操作,而try_

update_binary中的while循环只是负责更新升级进度条而已。

结合脚本与实现代码,可以对操作命令有较为清楚的认识。以脚本appy_patch为例,可以将其以函数的形式写为apply_patch(src_file, target_file, target_sha1, target_size, sha1_1, patch_1),它实现的功能为:将源文件src_file利用差分文件patch_1进行升级,并将结果文件写入到target_file中。target_size表示目录文件的大小,target_sha和sha1_1分别为相应的校验数据。

其他命令及对应的代码不再详述。

3.函数finish_recovery()

  1. static void  
  2. finish_recovery(const char *send_intent) {  
  3.     // By this point, we're ready to return to the main system...  
  4.     if (send_intent != NULL) {  
  5.         FILE *fp = fopen_path(INTENT_FILE, "w");  
  6.         if (fp == NULL) {  
  7.             LOGE("Can't open %s\n", INTENT_FILE);  
  8.         } else {  
  9.             fputs(send_intent, fp);  
  10.             check_and_fclose(fp, INTENT_FILE);  
  11.         }  
  12.     }  
  13.   
  14.     // Copy logs to cache so the system can find out what happened.  
  15.     copy_log_file(TEMPORARY_LOG_FILE, LOG_FILE, true);  
  16.     copy_log_file(TEMPORARY_LOG_FILE, LAST_LOG_FILE, false);  
  17.     copy_log_file(TEMPORARY_INSTALL_FILE, LAST_INSTALL_FILE, false);  
  18.     chmod(LOG_FILE, 0600);  
  19.     chown(LOG_FILE, 1000, 1000);   // system user  
  20.     chmod(LAST_LOG_FILE, 0640);  
  21.     chmod(LAST_INSTALL_FILE, 0644);  
  22.   
  23.     // Reset to mormal system boot so recovery won't cycle indefinitely.  
  24.     struct bootloader_message boot;  
  25.     memset(&boot, 0, sizeof(boot));  
  26.     set_bootloader_message(&boot);  
  27.   
  28.     // Remove the command file, so recovery won't repeat indefinitely.  
  29.     if (ensure_path_mounted(COMMAND_FILE) != 0 ||  
  30.         (unlink(COMMAND_FILE) && errno != ENOENT)) {  
  31.         LOGW("Can't unlink %s\n", COMMAND_FILE);  
  32.     }  
  33.   
  34.     ensure_path_unmounted(CACHE_ROOT);  
  35.     sync();  // For good measure.  
  36. }  

代码段6 finish_recovery函数

此函数完成的功能包括:

代码04-12:将send_intent写入文件并传递给main system;

代码14-21:将日志信息从TEMPORARY_LOG_FILE复制到LOG_FILE、LAST_LOG_FILE等文件中(这些文件存放在/cache目录);

代码23-26:清空BCB;

代码28-32:删除/cache/recovery/command文件。

显然,当系统升级成功后,执行finish_recovery函数重启,系统将不再进入recovery模式。


原文链接

阅读(1783) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~