四、install_package详细流程

2023-12-18 AndroidR RK356X 新增一个分区,这个分区可写入读取,恢复出厂后数据也不会被删除。 2.2 查看分区数,64M等于1024X64等于65536(KB)。2.1 查看分区挂载情况。 阅读详情

四、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.youkuaiyun.com/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.youkuaiyun.com/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.youkuaiyun.com/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进行具体升级流程

 

CLEAN,PACKAGE,INSTALL和DEPLOY的区别 这里是修真院后端小课堂,每篇分享文从 【背景介绍】【知识剖析】【常见问题】【解决方案】【编码实战】【扩展思考】【更多讨论】【参考文献】 八个方面深度解析后端知识/技能,本篇分享的是: 【 CLEAN,PACKAGE,INSTALL和DEPLOY的区别】 大家好,我是IT修真院上海分院第02期学员,一枚正直善良的java程序员。 今天给大家分享一下,修真院官网java任务中可能会使用到... 阅读详情

相关推荐

Maven构建生命周期:从package到install的进阶之路

本文深入解析Maven构建生命周期中package和install命令的核心区别与应用场景。通过工厂流水线类比,详细阐述从编译、测试到打包入库的完整流程,特别强调install在多模块协作中的关键作用,并提供企业级最佳实践与常见问题解决方案,帮助开发者高效管理项目构建。

weixin_30376509的博客 317

头条一面竟然问我maven?

maven package和maven install 有什么区别? 你常用的maven命令有哪些? <dependencyManagement> 是干什么的? 还有用过其它构建工具吗? 和maven有啥区别? 这几个问题都可以脱口而出,你应该是有点 maven 能耐,写代码去吧,不用看了 点赞+收藏 就学会系列,文章收录在 GitHub JavaEgg ,N线互联网开发必备技...

新之助的学习之路 1991

使用R语言的install.packages函数从资源库下载和安装R包

函数之前,首先需要确定要安装的R包的名称。你可以在R包的官方网站、CRAN(Comprehensive R Archive Network)网站或其他R包资源库中找到R包的名称。在R语言中,要使用特定功能或扩展库,通常需要下载并安装相应的软件包。使用R语言的install.packages函数从资源库下载和安装R包。函数下载和安装R包有所`函数下载和安装R包有所帮助!,可以从资源库中下载和安装R包。替换为要安装的R包的实际名称。一旦确定了要安装的R包的名称,就可以使用。替换为要安装的R包的名称,并将。

DevNinja的博客 3142

当NuGet中的install -package命令失效

缘起: 在程序包管理器控制台中输入如下命令: 无法找到并安装。 解决方法: 1、打开工具-》NuGet包管理器-》管理解决方案的NuGet程序包。 2、搜索包,如Microsoft.EntityFrameworkCore.SqlServer 3、选择2.1.1(因用的是vs2017,最新版本是3.1.0)并安装。 4、结果。 ...

Winfredzhang的博客 3181

多机器人编队人工势场法协同避障算法原理及实现

ROS及SLAM进阶教程(十一)多机器人编队人工势场法协同避障算法原理及实现避障算法原理避障算法仿真 多机器人协同编队需要将理论和实践紧密地结合起来,其应用包括编队队形生成、保持、变换和路径规划与避障等等都是基于图论的理论基础完成的。 详细请参考ROS及SLAM进阶教程(八)多机器人协同编队算法原理及实现 自主避障功能是机器人编队在各种环境中保持自身安全的重要功能,在编队的基础上加入避障的功能,机器人可扫描到一定范围内的障碍物(包括其他机器人),在即将与之发生冲突时提前规避冲突,以保证自身的安全性,同时

Redglass的博客 1万+

lidar360 V8.0 & lidar360MLS V8.0雷达点云数据处理软件

机载林业 — 基于机载激光雷达点云数据提取一系列森林结构参数(如:高度分位数、叶面积指数、郁闭度等),分割单木并提取单木参数(包括树的位置、树高、胸径、枝下高、冠幅等属性),利用软件的多种回归分析功能可以结合地面调查数据反演森林生物量、蓄积量等功能参数。还可以生成等高线、山体阴影、坡度、坡向、粗糙度等多种产品。分类 — LiDAR360提供多种分类功能,包括地面点分类、模型关键点分类、选择区域地面点分类、机器学习模型分类(可高效地分离建筑、植被、路灯等通用类别)、深度学习模型分类、自定义深度学习分类等。

2403_83796253的博客 5154

package命令和install的区别

maven打包时package和install的区别 1.package只是简单的打包命令,项目编译打包之后放在项目的target下面 2.install命令把项目先打包,然后放到maven仓库里面

weixin_44747048的博客 1万+

计算机视觉(相机标定;内参;外参;畸变系数)

目录 一、预备知识 1、坐标系变换过程(相机成像过程) (1)相机坐标系转换为图像坐标系(透视投影变换遵循的是针孔成像原理) (2)齐次坐标的引入原因:(为什么引入齐次坐标???) 2、内参与外参矩阵的构成 3、畸变参数 二、相机标定 1、张正友标定法(光学标定)及其求解思路 2、求解内参矩阵与外参矩阵的思路: (1)计算内参与外参矩阵的乘积M (2)求解内参矩阵A (3)求解外参矩阵(R1 R2 T) 三、参数优化 1、L-M算法 2、相机标定步骤 四、相关代码及其实验结.

QYJ_WORKHARDING的博客 6万+

【ROS实践指南】从零构建Catkin工作空间与功能包:编译、配置与核心文件解析

本文详细介绍了如何从零开始构建Catkin工作空间与功能包,涵盖编译、配置与核心文件解析的全过程。通过清晰的步骤指导和实用技巧,帮助开发者快速掌握ROS开发的基础知识,包括工作空间目录结构、功能包创建规范、编译配置及常见问题排查。适合ROS初学者和需要系统学习Catkin工具链的开发者。

weixin_30896511的博客 377

Buck电路中自举电容串联小电阻的作用

在Buck电路中,自举电容(Cboot)驱动高边MOSFET时,因寄生电感和电容会形成LC谐振回路,导致SW节点高频振铃,引发EMI问题。串联小电阻(Rboot)可引入阻尼效应,抑制振铃、改善波形和EMI性能,但会略微增加开关损耗。设计时建议预留可调电阻位(2Ω~20Ω),根据实测结果平衡效率与系统稳定性,提升电源可靠性。这一措施是优化开关电源EMC的常见实践。

硬核科技 626

Windows/Linux双平台指南:3种方法快速释放被占用的GPU内存(含任务管理器替代方案)

本文详细介绍了在Windows和Linux双平台下快速释放被占用GPU内存的3种方法,包括任务管理器替代方案。从基础操作到高阶技巧,帮助用户彻底解决GPU内存驻留问题,提升计算资源利用率。特别适用于深度学习开发者和服务器管理员处理显存泄漏等常见问题。

j0k1l2m3n的博客 262

理解maven命令package、install、deploy的联系与区别

  我们在用maven构建java项目时,最常用的打包命令有mvn package、mvn install、deploy,这三个命令都可完成打jar包或war(当然也可以是其它形式的包)的功能,但这三个命令还是有区别的。下面通过分别执行这三个命令的输出结果,来分析各自所执行的maven的生命周期。 mvn clean package mvn clean install m...

zhaojianting的博客 44万+

Matlab实战:从零搭建弹道导弹六自由度仿真模型(附完整代码)

本文详细介绍了如何使用Matlab从零搭建弹道导弹六自由度仿真模型,涵盖仿真环境配置、核心运动方程实现、气动力建模及仿真结果分析。通过完整代码示例,帮助读者掌握弹道导弹六自由度仿真的关键技术,适用于航空航天工程和复杂系统建模领域。

x8y9z0的博客 947

Maven命令 install 和 package的区别

Maven命令 install 和 package的区别 Maven是目前十分流行的项目构建工具以及依赖解决工具,其提供的常用指令中有两个很容易引起使用者的疑惑, 那就是 install 和 package , 那么这两个命令到底有啥区别呢? Maven install 安装指令,其做了两件事情: 1. 将项目打包(jar/war),将打包结果放到项目下的 target 目录下 2. 同时将上述打包结果放到本地仓库的相应目录中,供其他项目或模块引用 Maven install 安装指令执行步骤:(idea)

qq_43842093的博客 2万+

【ArcGIS Engine二次开发】入门基础(2):ArcGIS开发方式(VBA、DLL、Add-in、Engine)对比

文章目录2.1 使用VBA进行桌面软件开发2.1.1 VBA开发方式2.1.1 VBA代码的安全性2.2 使用DLL进行桌面软件开发2.2.1 DLL开发方式2.2.2 DLL功能的应用部署2.3 使用Add-in进行桌面软件开发2.4 使用ArcGIS Engine构建独立应用程序 对于桌面GIS应用来说,ArcGIS平台不仅提供了用于开发独立运行的GIS应用软件的二次开发工包ArcGIS Engine,还提供了对ArcGIS Deskop桌面软件进行扩展的开发的话,在开发个具体的GIS 行业应用软件时

智慧空间实验室 6510

构建OneService:打造高效、安全、灵活的企业级数据服务平台

数据服务(OneService)作为统一的数据服务出口,实现了数据的统一市场化管理,有效地降低数据开放门槛的同时,保障了数据开放的安全。

数据与算法架构提升之路专栏 2107

maven lifestyle 下的clean,package,install等命令作用及区别

如标题,下面放图: 這就是一个pom对应的maven操作命令, 那这些命令中,最常用的打包项目的命令是什么? 两种最常用打包方法: 1.先 clean,然后 package 2.先 clean,然后install 下面详细介绍它们: clean:翻译:打扫清理,最直接的就是作用于橙色的target目录。在进行真正的构建之前进行一些清理工作,移除所有上一次构建生成的文件。执行该命令会删除项目...

qq_38374562的博客 1万+

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Maven 命令clean、package、install、deploy的区别

Maven 命令clean、package、install、deploy的区别 clean 命令清除项目target文件夹 package 命令完成了项目编译、单元测试、打包功能,但没有把打好的可执行jar包(war包或其它形式的包)布署到本地maven仓库和远程maven私服仓库 install 命令完成了项目编译、单元测试、打包功能,同时把打好的可执行jar包(war包或其它形式的包)布署到...

华星详谈的博客 6185

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

maven项目 package与install的区别

一直以来都使用的是package打包项目部署在服务器运行,未曾使用过install,今天同事问我这两个的区别把我给问住了,赶紧去查询了解一下,并做一下记录。 package: 当我们使用package来打包项目的时候,就会在我们的项目中生成一个target文件夹,web项目比如mvc项目就会在该文件夹下生成一个war包,放在服务器中的tomcat/webapps目录里运行。 Java项目的话就会在该文件夹下生成一个jar包,放在服务器中指定目录下使用“java -jar jar...

流苏打的博客 2万+

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

maven 编译过程及package,install,deploy的区别

Maven作为依赖管理工具,能够管理规模庞大jar包。使用Maven后,依赖对应的Jar包能够自动下载、方便、快捷且规范。Maven作为构建管理工具。当我们使用 IDEA 进行开发时,构建是 IDEA 替我们做的。脱离IDEA环境执行构建操作,需要专门工具。

huangli的博客 1247

Maven - 常用命令大全 clean compile package install实战

本文介绍了Maven的核心命令及其应用场景,重点解析了clean、compile、package和install四个常用命令。文章从Maven生命周期概述入手,详细阐述了每个命令的作用、执行流程和实际应用,并通过示例项目演示了命令执行后的效果。掌握这些命令能帮助开发者高效完成项目构建、测试验证和部署发布等关键环节,是Java开发必备的基础技能。

千淘万漉虽辛苦,吹尽狂沙始到金 2万+

maven install和package 有什么区别

将项目编译后的 class 文件、资源文件等打包成指定格式的归档文件(如 JAR、WAR、EAR 等,由 pom.xml 中的 标签指定)。:在 package 命令的基础上,将打包好的文件安装到本地 Maven 仓库(默认路径为 ~/.m2/repository)。:当当前项目需要被本地其他项目作为依赖引用时(例如多模块项目中,模块 A 依赖模块 B,需先 install 模块 B)。:打包后的文件生成在项目的 target/ 目录下(例如 target/xxx.jar)。

qq_38664352的博客 846

maven的package与install命令区别

maven项目管理工具,集成了打包功能(生成jar包) mvn clean :清除目标目录中的生成结果 mvn package : 依据项目生成 jar 文件 mvn install :在本地 Repository 中安装 jar package(依赖的jar包会在本地仓库生成,本项目的jar包没有在本地仓库生成) install本项目jar包会在本地仓库生成 packag...

WaiCyuyan的专栏 3269

maven package 和 install 区别

仅仅是编译并打包项目文件,不会将其安装到本地仓库。:会在执行后,将打包好的文件安装到本地 Maven 仓库,供其他项目使用。如果只是想构建并生成可分发的文件,可以使用。如果需要将构建结果放入本地仓库,供其他项目引用,应该使用。

qq_41577703的博客 2561

npm install安装依赖的流程及package.json和package.lock.json关系解析

npm install流程

Curtis的博客 2639
上一篇: 三、sbin/recovery/服务流程-recovery.cpp main方法
下一篇: 五、try_update_binary详细流程
李标标
博客等级 码龄11年 124粉丝 43原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值