首页 > 编程语言 >android源码分析1--updater(l上)

android源码分析1--updater(l上)

时间:2023-09-06 11:35:24浏览次数:40  
标签:script -- package argv char int 源码 updater android



install.cpp中调用updater:

const char* binary = "/tmp/update_binary"; 

 
 
 
 
const char** args = (const char**)malloc(sizeof(char*) * 5);
 
args[0] = binary;
 
args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mk
 
char* temp = (char*)malloc(10);
 
sprintf(temp, "%d", pipefd[1]);
 
args[2] = temp;
 
args[3] = (char*)path;
 
args[4] = NULL;
 
 
 
 

  pid_t pid = fork(); 

 

  if (pid == 0) { 

 

  umask(022); 

 

  close(pipefd[0]); 

 
execv(binary, (char* const*)args);
 
fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno)); //updater执行正确永远不会被调用
 

  _exit(-1); 

 

  } 

 

  close(pipefd[1]); 

 
 
 
 
    #include < 
 unistd.h 
 >
 
        int execv(const char *progname, char *const argv[]);

   1.2 用法介绍


        execv会停止执行当前的进程, 并且以progname应用进程替换被停止执行的进程,进程ID没有改变 。


        progname: 被执行的应用程序。


         argv: 传递给应用程序的参数列表, 注意这个数组的第一个参数应该是应用程序名字本身(即argv[0] = progname), 并且最后一个参数应该为NULL ,不能将多个参数合并为一个参数放入数组。


    1.3 返回值


       如果应用程序正常执行完毕, 那么execv是永远不会返回的;当execv在调用进程中返回时,那么这个应用程序应该出错了 (可能是程序本身没找到,权限不够等), 此时它的返回值应该是-1,具体的错误代码可以通过全局变量errno查看,还可以通过stderr得到具体的错误描述字符串。



调用updater的3个参数:


1 recovery API: the version number for this interface


2 一个管道的fd,updater向这个管道写,用于更新进度条 an fd to which the program can write in order to update the progress bar


int pipefd[2]; 

 

  pipe(pipefd); 

 
 
 
 

  char* temp = (char*)malloc(10); 

 
sprintf(temp, "%d", pipefd[1]); //把pipefd[1]代表的管道的fd,作为字符串参数传给updater
 

  args[2] = temp; 

 

  args[3] = (char*)path; 

 

  args[4] = NULL; 

 
 
 
 

  pid_t pid = fork(); 

 

  if (pid == 0) { 

 

  umask(022); 

 

  close(pipefd[0]); //updater中关闭pipefd[0] 

 

  execv(binary, (char* const*)args); 

 

  fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno)); 

 

  _exit(-1); 

 

  } 

 

  close(pipefd[1]); //recovery中关闭pipefd[1] 

 
 
 
 

  FILE* from_child = fdopen(pipefd[0], "r"); //recovery从管道接收来自updater的信息 

 

  while (fgets(buffer, sizeof(buffer), from_child) != NULL) {


3 升级包路径 the name of the package zip file



二 recovery/updater.c中:


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, NULL); 
 

  // 创建子进程时,父进程的缓冲区也被复制到子进程了。所以子进程在printf时,就一起printf出来了,因为recovery中已经将 stdout stderr重定向到了文件中,所以这里把输出缓冲区设置为无缓冲,直接从流输出数据 

 
 
 
     
 setbuf 
 (stderr, NULL);
 
 
 
     
 if 
   
 (argc != 4) {
 
         
 printf 
 ( 
 "unexpected number of arguments (%d)\n" 
 , 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.
 
         
 printf 
 ( 
 "wrong updater binary API; expected 1, 2, or 3; "
 
                         
 "got %s\n" 
 ,
 
                 
 argv[1]);
 
         
 return 
   
 2;
 
     
 }
 
 
 
     
 // Set up the pipe for sending commands back to the parent process.
 
  // 将recovery传递的字符串格式的pipe[1]的fd,转换为fd,再打开这个管道
 
     
 int 
   
 fd =  
 atoi 
 (argv[2]);
 
     
 FILE 
 * cmd_pipe = fdopen(fd,  
 "wb" 
 );
 
     
 setlinebuf(cmd_pipe);
 
 
 
     
 // Extract the script from the package.
 
 
 
     
 const 
   
 char 
 * package_filename = argv[3]; //argv[3]就是升级包完成路径
 
     
 MemMapping map;
 
     
 if 
   
 (sysMapFile(package_filename, &map) != 0) { //将升级包映射到内存中
 
         
 printf 
 ( 
 "failed to map package %s\n" 
 , argv[3]);
 
         
 return 
   
 3;
 
     
 }
 
     
 ZipArchive za;
 
     
 int 
   
 err;
 
     
 err = mzOpenZipArchive(map.addr, map.length, &za); //根据内存中的起始地址和长度,打开这个文件
 
     
 if 
   
 (err != 0) {
 
         
 printf 
 ( 
 "failed to open package %s: %s\n" 
 ,
 
                
 argv[3],  
 strerror 
 (err));
 
         
 return 
   
 3;
 
     
 }
 
 
 
     
 const 
   
 ZipEntry* script_entry = mzFindZipEntry(&za, SCRIPT_NAME); //在文件中查找升级脚本这个entry
 
     
 if 
   
 (script_entry == NULL) {
 
         
 printf 
 ( 
 "failed to find %s in %s\n" 
 , SCRIPT_NAME, package_filename);
 
         
 return 
   
 4;
 
     
 }
 
 
 
     
 char 
 * script =  
 malloc 
 (script_entry->uncompLen+1) 
 ;
 
// 根据升级脚本的实际大小分配一段内存,将升级脚本所有内容读到script中
 
     
 if 
   
 (!mzReadZipEntry(&za, script_entry, script, script_entry->uncompLen)) {
 
         
 printf 
 ( 
 "failed to read script from package\n" 
 );
 
         
 return 
   
 5;
 
     
 }
 
     
 script[script_entry->uncompLen] =  
 '\0' 
 ;
 
 
 
     
 // Configure edify's functions.
 
 
 
     
 RegisterBuiltins();
 
     
 RegisterInstallFunctions();
 
     
 RegisterBlockImageFunctions();
 
     
 RegisterDeviceExtensions();
 
     
 FinishRegistration();
 
 
 
     
 // Parse the script.
 
 
 
     
 Expr* root;
 

  struct Expr { 

 

  Function fn; 

 

  char* name; 

 

  int argc; 

 

  Expr** argv; 

 

  int start, end; 

 

  }; 

 
     
 int 
   
 error_count = 0;
 
     
 int 
   
 error = parse_string(script, &root, &error_count); //解析脚本
 
     
 if 
   
 (error != 0 || error_count > 0) {
 
         
 printf 
 ( 
 "%d parse errors\n" 
 , error_count);
 
         
 return 
   
 6;
 
     
 }
 
 
 
     
 struct 
   
 selinux_opt seopts[] = {
 
       
 { SELABEL_OPT_PATH,  
 "/file_contexts" 
   
 }
 
     
 };
 
 
 
     
 sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
 
 
 
     
 if 
   
 (!sehandle) {
 
         
 fprintf 
 (cmd_pipe,  
 "ui_print Warning: No file_contexts\n" 
 );
 
     
 }
 
 
 
     
 // Evaluate the parsed script.
 
 
 
     
 UpdaterInfo updater_info;
 

  typedef struct { 

 

  FILE* cmd_pipe; 

 

  ZipArchive* package_zip; 

 

  int version; 

 
 
 
 

  uint8_t* package_zip_addr; 

 

  size_t package_zip_len; 

 
 UpdaterInfo; 

 
     
 updater_info.cmd_pipe = cmd_pipe; //updater_info.cmd_pipe取得了updater打开的管道
 
     
 updater_info.package_zip = &za; //updater_info.package_zip 内存中的zip升级包
 
     
 updater_info.version =  
 atoi 
 (version); //updater_info.version recovery api版本
 
     
 updater_info.package_zip_addr = map.addr; //updater_info.package_zip_addr zip升级包在内存中的起始地址
 
     
 updater_info.package_zip_len = map.length; // updater_info.package_zip_len zip升级包在内存中的长度
 
 
 
     
 State state;
 

  typedef struct { 

 

  // Optional pointer to app-specific data; the core of edify never 

 

  // uses this value. 

 

  void* cookie; 

 

  // The source of the original script. Must be NULL-terminated, 

 

  // and in writable memory (Evaluate may make temporary changes to 

 

  // it but will restore it when done). 

 

  char* script; 

 

  // The error message (if any) returned if the evaluation aborts. 

 

  // Should be NULL initially, will be either NULL or a malloc'd 

 

  // pointer after Evaluate() returns. 

 

  char* errmsg; 

 

  } State; 

 
     
 state.cookie = &updater_info;
 
     
 state.script = script; //现在state.script指向的就是脚本内容
 
     
 state.errmsg = NULL;
 
 
 
     
 char 
 * result = Evaluate(&state, root); //执行脚本
 
     
 if 
   
 (result == NULL) {
 
         
 if 
   
 (state.errmsg == NULL) {
 
             
 printf 
 ( 
 "script aborted (no error message)\n" 
 );
 
             
 fprintf 
 (cmd_pipe,  
 "ui_print script aborted (no error message)\n" 
 );
 
         
 }  
 else 
   
 {
 
             
 printf 
 ( 
 "script aborted: %s\n" 
 , state.errmsg);
 
             
 char 
 * line =  
 strtok 
 (state.errmsg,  
 "\n" 
 );
 
             
 while 
   
 (line) {
 
                 
 fprintf 
 (cmd_pipe,  
 "ui_print %s\n" 
 , line);
 
                 
 line =  
 strtok 
 (NULL,  
 "\n" 
 );
 
             
 }
 
             
 fprintf 
 (cmd_pipe,  
 "ui_print\n" 
 );
 
         
 }
 
         
 free 
 (state.errmsg);
 
         
 return 
   
 7;
 
     
 }  
 else 
   
 {
 
         
 fprintf 
 (cmd_pipe,  
 "ui_print script succeeded: result was [%s]\n" 
 , result);
 
         
 free 
 (result);
 
     
 }
 
 
 
     
 if 
   
 (updater_info.package_zip) {
 
         
 mzCloseZipArchive(updater_info.package_zip);
 
     
 }
 
     
 sysReleaseMap(&map);
 
     
 free 
 (script);
 
 
 
     
 return 
   
 0;
 
}
 
 
 
 
三 recovery/edify/expr.c
 

  typedef struct { 

 

  int type; 

 

  ssize_t size; 

 

  char* data; 

 

  } Value; 

 

  struct Expr { 

 

  Function fn; 

 

  char* name; 

 

  int argc; 

 

  Expr** argv; 

 

  int start, end; 

 

  }; 

 

  typedef struct { 

 

  void* cookie; 

 

  char* script; 

 

  char* errmsg; 

 

  } State; 

 
char 
 * Evaluate(State* state, Expr* expr) {
 
     
 Value* v = expr->fn(expr->name, state, expr->argc, expr->argv);
 

  typedef Value* (*Function)(const char* name, State* state, int argc, Expr* argv[]); 

 
     
 if 
   
 (v == NULL)  
 return 
   
 NULL;
 
     
 if 
   
 (v->type != VAL_STRING) {
 
         
 ErrorAbort(state,  
 "expecting string, got value type %d" 
 , v->type);
 
         
 FreeValue(v);
 
         
 return 
   
 NULL;
 
     
 }
 
     
 char 
 * result = v->data;
 
     
 free 
 (v);
 
     
 return 
   
 result;
 
}



Evaluate()函数主要是调用了expr的fn()函数,参数expr的类型是Expr,定义如下:

1. struct Expr {  
2.     Function fn;  
3.     char* name;  
4.     int argc;  
5.     Expr** argv;  
6.     int start, end;  
7. };

从Expr的定义中可以看到它有一个字段argv,这个字段是Expr指针的指针类型,它实际上会指向一个Expr指针的数组对象,表示Expr对象的所有下一级对象。通过这个字段,脚本解析后得到的所有命令都串接在一起,而且命令的执行函数还会调用Ecaluate()来继续执行argv中的Expr对象,因此,虽然Evaluate()中只调用了root对象的fn()函数,但是实际上会执行脚本中的所有命令。

// 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) 

 
 
 
 

  Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) { 

 

  Value* blockdev_filename; 

 

  Value* transfer_list_value; 

 

  char* transfer_list = NULL; 

 

  Value* new_data_fn; 

 

  Value* patch_data_fn; 

 

  bool success = false; 

 
 
 
 

  if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value, 

 

  &new_data_fn, &patch_data_fn) < 0) { 

 

  return NULL; 

 

  }


脚本中调用block_image_update("/dev/block/bootdevice/by-name/system", package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat");


就会执行BlockImageUpdateFn函数


1 ReadValueArgs取得脚本中的/dev/block/bootdevice/by-name/system,package_extract_file("system.transfer.list"),system.new.dat", "system.patch.dat"这四个参数,赋值给blockdev_filename ,transfer_list_value, new_data_fn,patch_data_fn


typedef struct { 

 

  int type; 

 

  ssize_t size; 

 

  char* data; 

 

  } Value; 

 

  2




标签:script,--,package,argv,char,int,源码,updater,android
From: https://blog.51cto.com/u_16248677/7384722

相关文章

  • 输出编译ota升级包时的打包参数
    build / tools/releasetools/common.pydefParseOptions(argv,docstring,extra_opts="",extra_long_opts=(),extra_option_handler=None):"""Parsetheoptionsinargvandreturn......
  • 若依 Nginx 图片回显访问配置
    若依Nginx图片回显访问配置location/profile/{#方式二:指向目录,对应后台`application.yml`中的`profile`配置alias/home/ruoyi/uploadPath/;}参考文章:https://doc.ruoyi.vip/ruoyi-vue/other/faq.html#%E5%89%8D%E7%AB%AF%E5%A6%82%E4%BD%95%E9%85%8D%E7%BD%AE......
  • 恶意爬虫防护
    引言如果您仔细分析过任何一个网站的请求日志,您肯定会发现一些可疑的流量,那可能就是爬虫流量。根据Imperva发布的《2023ImpervaBadBotReport》在2022年的所有互联网流量中,47.4%是爬虫流量。与2021年的42.3%相比,增长了5.1%。在这些爬虫流量中,30.2%是恶意爬虫,比2021年的27.7%增......
  • aosp源码分析 5.0 BlockImageUpdateFn
    block_image_update("/dev/block/bootdevice/by-name/system",package_extract_file("system.transfer.list"),"system.new.dat","system.patch.dat");//args://-blockdevice(orfile)tomodifyin-place......
  • vue3+typescript +uniapp中select标签
    <select:value="state.year"@change="handleSelectChange($event.target)"> <option:value="i"v-for="iinstate.yearrange">{{i}}</option> </select> ts的代码:``相当于v-model<se......
  • DSP集成麦克风阵列声源定位模组AR-1105​
    麦克风阵列声源定位模组AR-1105是采用DSP音频处理器集成麦克风阵列声源定位技术进行研发,模组具有全硬件集成.体积小巧,外围电路简洁,无需软件调试,易上手等优点的情况下同时保持反应灵敏,定位准确等特性.模组分为:声音定位核心主板麦克风阵列板声源定位LED显示板声音定位核心主板......
  • Sphingomonas鞘氨醇单胞菌属
    鞘氨醇单胞菌属(学名:Sphingomonas)为鞘脂单胞菌目鞘脂单胞菌科的一属好氧或兼性厌氧发酵型革兰氏阴性杆菌。无芽孢的直杆状菌。此属的模式种为少动鞘氨醇单胞菌(Sphingomonaspaucimobilis)。1、NEWBing的回答根际微生物群落中的Sphingomonas是一种革兰氏阴性的细菌属,属于拟杆菌......
  • ota升级包中update-script脚本的生成
    控制升级流程的主要逻辑,实际控制着升级过程中大部分重要操作的实施细节,而处于升级包中同目录下的update-bianry负责真正执行update-script记录的操作。 在负责生成升级包的脚本ota_from_target_files.py中,不论我们调用这个脚本来产生全量包,增量包,或者是安卓新加入的block方式的......
  • 大屏适配
    1,安装 amfe-flexible npminstall amfe-flexible --save-dev  2,安装  [email protected]  3,vue.config.js中添加配置 css:{  loaderOptions:{   postcss:{    plugins:[     ......
  • 如何编译lex文件和yacc文件
    lex文件:flexfilename.l或者flexfilename.lexyacc文件:bisonfilename.y或者flexfilename.yacclex解析此法yacc解析语法lex说穿了,基本套路就是:1.创建lex文件2.使用flexxxx.lex来生成.c文件3.使用c编译器(如gcc),来生成一个可执行文件。......