四、install_package详细流程
本篇将介绍install_package的流程,将重点分析install.cpp和roots.cpp,大致分为
1、UI设置
2、升级包路径挂载
3、分配内存空间
4、校验升级包
5、校验compatibility
6、try_update_binary(下个篇幅单独讲解,核心内容)
install.cpp //返回status int类型 int install_package(const std::string& path, bool* wipe_cache, const std::string& install_file, bool needs_mount, int retry_count) { //传进来的参数中,needs_mount = true retry_count = 0 path = update_package CHECK(!path.empty()); CHECK(!install_file.empty()); CHECK(wipe_cache != nullptr); modified_flash = true; //std::chrono::system_clock 它表示当前的系统时钟,系统中运行的所有进程使用now()得到的时间是一致的。now() 当前时间time_point auto start = std::chrono::system_clock::now(); int start_temperature = GetMaxValueFromThermalZone(); int max_temperature = start_temperature; int result; std::vector<std::string> log_buffer; //追踪判断roots.cpp if (setup_install_mounts() != 0) { LOG(ERROR) << "failed to set up expected mounts for install; aborting"; result = INSTALL_ERROR; } else { //主要install流程 result = really_install_package(path, wipe_cache, needs_mount, &log_buffer, retry_count, &max_temperature); }
setup_install_mounts() != 0方法分析
roots.cpp int setup_install_mounts() { //判断static struct fstab* fstab是否为空 if (fstab == nullptr) { LOG(ERROR) << "can't set up install mounts: no fstab loaded"; return -1; } printf("\n====roots.cpp setup_install_mounts====\n"); for (int i = 0; i < fstab->num_entries; ++i) { const Volume* v = fstab->recs + i; //这里打印出来的,其实跟我们之前加载分区表得到的时相同的,不过只是再次确认下mount_point的信息 char* mount_point printf(" %d %s %s %s %lld\n", i, v->mount_point, v->fs_type, v->blk_device, v->length); // We don't want to do anything with "/".这里通过log其实可以看到挂载点为/ 的只有system if (strcmp(v->mount_point, "/") == 0) { continue; } //如果mount_point中有tmp 和cache,判断是不是可以挂载成功 if (strcmp(v->mount_point, "/tmp") == 0 || strcmp(v->mount_point, "/cache") == 0) { if (ensure_path_mounted(v->mount_point) != 0) { LOG(ERROR) << "Failed to mount " << v->mount_point; return -1; } } else { //如果不是tmp和cache,判断是不是可以不挂载 if (ensure_path_unmounted(v->mount_point) != 0) { LOG(ERROR) << "Failed to unmount " << v->mount_point; return -1; } } } return 0; }
ensure_path_mounted方法 看这个方法之前,我们先把其中一些调用方法整理清楚
Volume* v = volume_for_path(path)
roots.cpp //传入mount_point 具体为/tmp 和 /cache static Volume* volume_for_path(const char* path) { //如果路径为空,或者路径第一个字节为空,返回nullptr if (path == nullptr || path[0] == '\0') return nullptr; //将路径转换为string格式 std::string str(path); while (true) { //传入获取的静态fstab,和路径,获取到对应的fstab_rec结构体 Volume* result = fs_mgr_get_entry_for_mount_point(fstab, str); if (result != nullptr || str == "/") { return result; } //为了使自己的程序有很好的移植性,c++程序员应该尽量使用size_t和size_type而不是int, unsigned //size_t是全局定义的类型;size_type是STL类中定义的类型属性,用以保存任意string和vector类对象的长度 size_t slash = str.find_last_of('/'); if (slash == std::string::npos) return nullptr; if (slash == 0) { str = "/"; } else { str = str.substr(0, slash); } } return nullptr; }
fs_mgr_get_entry_for_mount_point
system/core/fs_mgr/fs_mgr_fstab.cpp /* * Returns the fstab_rec* whose mount_point is path. 返回fstab_rec结构体 * Returns nullptr if not found. */ struct fstab_rec* fs_mgr_get_entry_for_mount_point(struct fstab* fstab, const std::string& path) { if (!fstab) { return nullptr; } //如果当前路径在recs的挂载点中存在,返回该挂载点的fstab_rec结构体,之前我们已经知道了结构体fstab和fstab_rec的内容 //fstab包含了分区表中条目的个数,recs,分区表的名称,fstab_rec包含了详细的条目信息,这里返回的就是包含当前挂载点的条目信息 for (int i = 0; i < fstab->num_entries; i++) { //如果当前条目mount_point不为空,而且等于我们传进来的mount_point,返回这个条目 if (fstab->recs[i].mount_point && path == fstab->recs[i].mount_point) { return &fstab->recs[i]; } } return nullptr; }
!scan_mounted_volumes()
bootable/recovery/mounts.cpp struct MountedVolume { std::string device; std::string mount_point; std::string filesystem; std::string flags; }; std::vector<MountedVolume*> g_mounts_state; bool scan_mounted_volumes() { for (size_t i = 0; i < g_mounts_state.size(); ++i) { delete g_mounts_state[i]; } //执行之前先清除了容器中的内容,其实按目前的代码看,本身就是没有赋值的, g_mounts_state.clear(); // Open and read mount table entries. //setmntent,getmument,endmnment获取文件信息,得到mnment结构体,拿到文件中的详细信息 //介绍非常详细https://blog.csdn.net/lixiaogang_theanswer/article/details/79431318 FILE* fp = setmntent("/proc/mounts", "re"); if (fp == NULL) { return false; } mntent* e; //遍历出mntent中的参数,赋值给g_mounts_state while ((e = getmntent(fp)) != NULL) { MountedVolume* v = new MountedVolume; v->device = e->mnt_fsname; v->mount_point = e->mnt_dir; v->filesystem = e->mnt_type; v->flags = e->mnt_opts; //将组装好的结构体放入g_mounts_state容器中 g_mounts_state.push_back(v); } endmntent(fp); printf("\n==========mounts.cpp scan_mounted_volumes g_mounts_state==========\n"); for (size_t i = 0; i < g_mounts_state.size(); ++i) { printf(" %s %s %s %s\n", g_mounts_state[i]->mount_point.c_str(), g_mounts_state[i]->device.c_str(), g_mounts_state[i]->filesystem.c_str(), g_mounts_state[i]->flags.c_str()); } printf("\n==================================================================\n"); return true; }
find_mounted_volume_by_mount_point
bootable/recovery/mounts.cpp MountedVolume* find_mounted_volume_by_mount_point(const char* mount_point) { printf("\n==========mounts.cpp find_mounted_volume_by_mount_point before for==========\n"); for (size_t i = 0; i < g_mounts_state.size(); ++i) { //如果g_mounts_state[i]的挂载点存在传入的mount_point,那么就返回当前位置在的MountedVolumes结构体 if (g_mounts_state[i]->mount_point == mount_point){ printf("\n==========mounts.cpp find_mounted_volume_by_mount_point after for==========\n"); printf(" %s %s %s %s\n", g_mounts_state[i]->mount_point.c_str(), g_mounts_state[i]->device.c_str(), g_mounts_state[i]->filesystem.c_str(), g_mounts_state[i]->flags.c_str()); return g_mounts_state[i]; } } return nullptr; }
ensure_path_mounted这个方法在下面really_install_package中也有走到,上述几个方法是加在该方法中的一些条件,我加了很多的打印,具体看了下,下载到data和sdcard中的一些区别,以下代码中会做一些说明
roots.cpp //注意参数这里传入的是mount_point int ensure_path_mounted(const char* path) { // Mount at the default mount point. return ensure_path_mounted_at(path, nullptr); } //传进来的mount_point为空 int ensure_path_mounted_at(const char* path, const char* mount_point) { Volume* v = volume_for_path(path); printf("\n==========ensure_path_mounted_at volume_for_path==========\n"); printf(" %s %s %s\n",v->mount_point, v->fs_type, v->blk_device); //adupsfota start #ifdef ADUPS_FOTA_SUPPORT if (strncmp(path, "/fotaupdate/", 12) == 0) { return 0; } #endif //adupsfota end //如果得到的fstab_rec为空,则打印错误unknown volume for path if (v == nullptr) { LOG(ERROR) << "unknown volume for path [" << path << "]"; return -1; } //如果得到的fstab_rec中fs_type为 ramdisk 则返回0 无需执行挂载操作 if (strcmp(v->fs_type, "ramdisk") == 0) { // The ramdisk is always mounted. return 0; } //这个判断条件中是从mounts文件中读取数据,在机器中路径为/proc/mounts,pull出来可以看到,具体打印如下 //[ 6.937196] ==========mounts.cpp scan_mounted_volumes g_mounts_state========== //[ 6.937230] / rootfs rootfs rw,seclabel //[ 6.937263] /dev tmpfs tmpfs rw,seclabel,nosuid,relatime,mode=755 //[ 6.937295] /dev/pts devpts devpts rw,seclabel,relatime,mode=600 //[ 6.937326] /proc proc proc rw,relatime,gid=3009,hidepid=2 //[ 6.937357] /sys sysfs sysfs rw,seclabel,relatime //[ 6.937389] /sys/fs/selinux selinuxfs selinuxfs rw,relatime //[ 6.937421] /mnt tmpfs tmpfs rw,seclabel,nosuid,nodev,noexec,relatime,mode=755,gid=1000 //[ 6.937452] /acct none cgroup rw,relatime,cpuacct //[ 6.937483] /tmp tmpfs tmpfs rw,seclabel,relatime //[ 6.937514] /config none configfs rw,relatime //[ 6.937545] /dev/usb-ffs/adb adb functionfs rw,relatime //[ 6.937577] /cache /dev/block/platform/bootdevice/by-name/cache ext4 rw,seclabel,nosuid,nodev,noatime,discard,noauto_da_alloc,data=ordered if (!scan_mounted_volumes()) { LOG(ERROR) << "Failed to scan mounted volumes"; return -1; } //如果mount_point为空,把fstab_rec中的值给到mount_point if (!mount_point) { mount_point = v->mount_point; } //从g_mounts_state中读取数据,看是否有对应的挂载点 //根据我们上个方法拿到的数据不难发现,g_mount_state中并没有sdcard的条目 //所以 路径为data时,mv不为空,直接return 0,路径为sdcard时 mv为空,需要执行后续的mount方法 //data打印结果 //[ 7.032122] ==========roots.cpp find_mounted_volume_by_mount_point before========== //[ 7.032179] ==========mounts.cpp find_mounted_volume_by_mount_point before for========== //[ 7.032237] ==========mounts.cpp find_mounted_volume_by_mount_point after for========== //[ 7.032269] /cache /dev/block/platform/bootdevice/by-name/cache ext4 rw,seclabel,nosuid,nodev,noatime,discard,noauto_da_alloc,data=ordered //[ 7.032326] ==========roots.cpp find_mounted_volume_by_mount_point after========== //sdcard打印结果 //[ 7.022144] ==========roots.cpp find_mounted_volume_by_mount_point before========== //[ 7.022393] ==========mounts.cpp find_mounted_volume_by_mount_point before for========== //[ 7.022647] ==========roots.cpp find_mounted_volume_by_mount_point after========== printf("\n==========roots.cpp find_mounted_volume_by_mount_point before==========\n"); const MountedVolume* mv = find_mounted_volume_by_mount_point(mount_point); printf("\n==========roots.cpp find_mounted_volume_by_mount_point after==========\n"); if (mv != nullptr) { printf("\n==========roots.cpp mv != nullptr==========\n"); return 0; } mkdir(mount_point, 0755); // in case it doesn't already exist if (strcmp(v->fs_type, "ext4") == 0 || strcmp(v->fs_type, "squashfs") == 0 || strcmp(v->fs_type, "vfat") == 0) { int result = mount(v->blk_device, mount_point, v->fs_type, v->flags, v->fs_options); if (result == -1 && fs_mgr_is_formattable(v)) { PLOG(ERROR) << "Failed to mount " << mount_point << "; formatting"; bool crypt_footer = fs_mgr_is_encryptable(v) && !strcmp(v->key_loc, "footer"); if (fs_mgr_do_format(v, crypt_footer) == 0) { result = mount(v->blk_device, mount_point, v->fs_type, v->flags, v->fs_options); } else { PLOG(ERROR) << "Failed to format " << mount_point; return -1; } } if (result == -1) { PLOG(ERROR) << "Failed to mount " << mount_point; return -1; } return 0; } LOG(ERROR) << "unknown fs_type \"" << v->fs_type << "\" for " << mount_point; return -1; }
really_install_package
install.cpp //还是先说明传进来的参数 needs_mount = true retry_count = 0 path = update_package 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部分的处理设置背景和进度条 TODO 后续有时间再处理UI部分 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(INFO) << "Update location: " << path; // Map the update package into memory. ui->Print("Opening update package...\n"); //needs_mount =true,所以进入判断 if (needs_mount) { //如果path的开始位置为@,也就是内置存储转换之后的路径,那么取去除第一个位置之后的string 也就是cache/recovery/block.map //ensure_path_mounted,以上已经做过分析,这里不做描述 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"; log_buffer->push_back(android::base::StringPrintf("error: %d", kMapFileFailure)); return INSTALL_CORRUPT; } // Verify package.校验升级包签名 if (!verify_package(map.addr, map.length)) { log_buffer->push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure)); return INSTALL_CORRUPT; } // 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(), zip)) { CloseArchive(zip); return ret; } #endif // Additionally verify the compatibility of the package. // 校验升级包中的compatibility.zip // 关于compatibility.zip的介绍https://blog.csdn.net/csdn66_2016/article/details/81704720 if (!verify_package_compatibility(zip)) { log_buffer->push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure)); CloseArchive(zip); return INSTALL_CORRUPT; } // 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"); CloseArchive(zip); return result; }
map.MapFile(path)方法,虽然对于路径也有方法的区分,但是大致的流程都需要把升级包的数据映射到内存空间,后续校验方法传入的也是内存映射地址
bootable/recovery/otautil/SysUtil.cpp bool MemMapping::MapFile(const std::string& fn) { if (fn.empty()) { LOG(ERROR) << "Empty filename"; return false; } //这里分为了两种情况,走了不同的内存地址映射的方法,data区和其他路径 if (fn[0] == '@') { // Block map file "@/cache/recovery/block.map". if (!MapBlockFile(fn.substr(1))) { LOG(ERROR) << "Map of '" << fn << "' failed"; return false; } } else { // This is a regular file. android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(fn.c_str(), O_RDONLY))); if (fd == -1) { PLOG(ERROR) << "Unable to open '" << fn << "'"; return false; } if (!MapFD(fd)) { LOG(ERROR) << "Map of '" << fn << "' failed"; return false; } } return true; }
针对cache/recovery/block.map的MapBlockFile
bootable/recovery/otautil/SysUtil.cpp bool MemMapping::MapBlockFile(const std::string& filename) { std::string content; //以是否可以读取出字符串判断,如果block.map为空,将会报错failed to read if (!android::base::ReadFileToString(filename, &content)) { PLOG(ERROR) << "Failed to read " << filename; return false; } //以换行分割block.map到string类型的容器中 std::vector<std::string> lines = android::base::Split(android::base::Trim(content), "\n"); //加了打印用于输出lines容器中的内容,输出结果: //[ 7.034186] =================MapBlockFile block.map data "/dev/block/platform/bootdevice/by-name/userdata" //[ 7.034245] "19485612 4096" //[ 7.034276] "2" //[ 7.034307] "556032 557056" //[ 7.034337] "583680 587414" printf("\n=================MapBlockFile block.map data====================\n"); for (const auto& line : lines) { printf(" \"%s\"\n", line.c_str()); } if (lines.size() < 4) { LOG(ERROR) << "Block map file is too short: " << lines.size(); return false; } size_t size; size_t blksize; // %d输出int型。 %zu输出size_t型 // int sscanf() 从字符串读取格式化输入 返回输入时几个参数,这个读取的第二行的两个参数,如果返回的不是2 那说明block.map缺失数据 // sscanf介绍:https://www.cnblogs.com/wwjyt/p/3182892.html if (sscanf(lines[1].c_str(), "%zu %zu", &size, &blksize) != 2) { LOG(ERROR) << "Failed to parse file size and block size: " << lines[1]; return false; } size_t range_count; //判断第三行有没有block区间的总数 if (sscanf(lines[2].c_str(), "%zu", &range_count) != 1) { LOG(ERROR) << "Failed to parse block map header: " << lines[2]; return false; } size_t blocks; //如果blksize不为空,那么计算出blocks的数量 if (blksize != 0) { blocks = ((size - 1) / blksize) + 1; } //再次确认各项数值有无异常和空值 //这里的SIZE_MAX 是./bionic/libc/include/stdint.h中宏定义的数值 #define SIZE_MAX UINT64_MAX if (size == 0 || blksize == 0 || blocks > SIZE_MAX / blksize || range_count == 0 || lines.size() != 3 + range_count) { LOG(ERROR) << "Invalid data in block map file: size " << size << ", blksize " << blksize << ", range_count " << range_count << ", lines " << lines.size(); return false; } // Reserve enough contiguous address space for the whole file. //使用mmap用来在进程虚拟地址空间中分配创建一片虚拟内存地址映射,返回值:成功返回映射的虚拟内存地址的起始地址,失败返回MAP_FAILED //内存映射:https://blog.csdn.net/qq_33611327/article/details/81738195 http://blog.itpub.net/7728585/viewspace-2142411/ void* reserve = mmap(nullptr, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); if (reserve == MAP_FAILED) { PLOG(ERROR) << "failed to reserve address space"; return false; } ///dev/block/platform/msm_sdcc.1/by-name/userdata //o_rdonly read only 只读 o_wronly write only 只写 o_rdwr read write 可读可写,fd为文件描述符 const std::string& block_dev = lines[0]; android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(block_dev.c_str(), O_RDONLY))); if (fd == -1) { PLOG(ERROR) << "failed to open block device " << block_dev; munmap(reserve, blocks * blksize); return false; } //ranges_ 在sysutil.h中定义 ,是每个元素为结构体MappedRange的容器,std::vector<MappedRange> ranges_; // struct MappedRange { // void* addr; // size_t length; //}; //装入之前清除容器中内容 ranges_.clear(); //static_cast相当于传统的C语言里的强制转换,将reserve强制转换为char类型 unsigned char* next = static_cast<unsigned char*>(reserve); //blocks = ((size - 1) / blksize) + 1; size_t remaining_size = blocks * blksize; bool success = true; for (size_t i = 0; i < range_count; ++i) { const std::string& line = lines[i + 3]; size_t start, end;//1000 1008 从第四行开始处理 if (sscanf(line.c_str(), "%zu %zu\n", &start, &end) != 2) { LOG(ERROR) << "failed to parse range " << i << ": " << line; success = false; break; } //区间的大小为区间长度×块大小 size_t range_size = (end - start) * blksize; if (end <= start || (end - start) > SIZE_MAX / blksize || range_size > remaining_size) { LOG(ERROR) << "Invalid range: " << start << " " << end; success = false; break; } //将第一个block range放入到我们分配的内存地址中,偏移量为第一个区间的起始位置 void* range_start = mmap(next, range_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, static_cast<off_t>(start) * blksize); if (range_start == MAP_FAILED) { PLOG(ERROR) << "failed to map range " << i << ": " << line; success = false; break; } //将第一个区间的MappedRange结构体放入到ranges容器中 ranges_.emplace_back(MappedRange{ range_start, range_size }); //第一个区间放入后,将起始位置加上之前的range_size next += range_size; //而总的大小减去第一个ranges,这两个数据的变动是为了第二个区间的放入 remaining_size -= range_size; } if (success && remaining_size != 0) { LOG(ERROR) << "Invalid ranges: remaining_size " << remaining_size; success = false; } if (!success) { munmap(reserve, blocks * blksize); return false; } //数据全部放入后,拿到的addr内存映射地址就是我们升级包的数据 addr = static_cast<unsigned char*>(reserve); length = size; LOG(INFO) << "mmapped " << range_count << " ranges"; return true; }
针对sdcard的MapFD方法
bootable/recovery/otautil/SysUtil.cpp bool MemMapping::MapFD(int fd) { struct stat sb; if (fstat(fd, &sb) == -1) { PLOG(ERROR) << "fstat(" << fd << ") failed"; return false; } //分配内存地址映射 void* memPtr = mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (memPtr == MAP_FAILED) { PLOG(ERROR) << "mmap(" << sb.st_size << ", R, PRIVATE, " << fd << ", 0) failed"; return false; } addr = static_cast<unsigned char*>(memPtr); length = sb.st_size; ranges_.clear(); ranges_.emplace_back(MappedRange{ memPtr, static_cast<size_t>(sb.st_size) }); return true; }
verify_package校验签名
bootable/recovery/install.cpp TODO 这里可以作为了解,看下里面的打印信息,知道大概流程 bool verify_package(const unsigned char* package_data, size_t package_size) { static constexpr const char* PUBLIC_KEYS_FILE = "/res/keys"; std::vector<Certificate> loadedKeys; if (!load_keys(PUBLIC_KEYS_FILE, loadedKeys)) { LOG(ERROR) << "Failed to load keys"; return false; } LOG(INFO) << loadedKeys.size() << " key(s) loaded from " << PUBLIC_KEYS_FILE; // Verify package. ui->Print("Verifying update package...\n"); auto t0 = std::chrono::system_clock::now(); int err = verify_file(package_data, package_size, loadedKeys, std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1)); std::chrono::duration<double> duration = std::chrono::system_clock::now() - t0; ui->Print("Update package verification took %.1f s (result %d).\n", duration.count(), err); if (err != VERIFY_SUCCESS) { LOG(ERROR) << "Signature verification failed"; LOG(ERROR) << "error: " << kZipVerificationFailure; return false; } return true; }
try_update_binary将作为单独讲解,recovery使用update_binary,解析update-scrypt进行具体升级流程