技术标签: android Dm verity Device Mapper AVB专栏
Device mapper是LINUX提供的一种逻辑设备到物理设备的映射框架,中间传递消息的是用户自定义的target driver插件,用户可以编写好具体的IO请求的target driver就行,用户层可以使用ioctl命令的方式向底层进行通讯。
target driver主要定义对IO请求的处理规则,在device mapper中对target driver的操作已定义好了统一的接口,可实现几个常见的方法就行。同时device mapper提供了一种底层消息上报的机制,通过target driver将事件消息传递到用户空间。
在android的init启动的第一阶段中主要调用了InitDeviceMapper函数,来进行device mapper的初始化
原创不易,转载请注明出处 https://blog.csdn.net/jackone12347/article/details/122115691
在android的init启动的第一阶段中主要调用了InitDeviceMapper函数,来进行device mapper的初始化。
分析一下代码@block_dev_initializer.cpp
bool FirstStageMount::InitRequiredDevices(std::set<std::string> devices) {
if (!block_dev_init_.InitDeviceMapper()) {
return false;
}
if (devices.empty()) {
return true;
}
return block_dev_init_.InitDevices(std::move(devices));
}
bool BlockDevInitializer::InitDeviceMapper() {
const std::string dm_path = "/devices/virtual/misc/device-mapper";
bool found = false;
auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
if (uevent.path == dm_path) {
device_handler_->HandleUevent(uevent);
found = true;
return ListenerAction::kStop;
}
return ListenerAction::kContinue;
};
uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
if (!found) {
Timer t;
uevent_listener_.Poll(dm_callback, 10s);
}
if (!found) {
return false;
}
return true;
}
调用InitDmDevice函数初始化Device mapper 设备。通过代码分析来看,直观的功能是通过uevent上报的节点,在其中去查找dtsi中配置的各个分区;
如果找到了就从device集合中删除,添加个10秒的等待,如果10秒内都找不到这个分区,说明此分区可能没有挂载到文件系统,或者配置出错。
bool BlockDevInitializer::InitDmDevice(const std::string& device) {
auto uevent_callback = [&device_name, &device, this, &found](const Uevent& uevent) {
if (uevent.device_name == device_name) {
device_handler_->HandleUevent(uevent);
found = true;
return ListenerAction::kStop;
}
return ListenerAction::kContinue;
};
uevent_listener_.RegenerateUeventsForPath(syspath, uevent_callback);
if (!found) {
Timer t;
uevent_listener_.Poll(uevent_callback, 10s);
}
…
return true;
}
ListenerAction BlockDevInitializer::HandleUevent(const Uevent& uevent,
std::set<std::string>* devices) {
auto name = uevent.partition_name;
if (name.empty()) {
size_t base_idx = uevent.path.rfind('/');
if (base_idx == std::string::npos) {
return ListenerAction::kContinue;
}
name = uevent.path.substr(base_idx + 1);
}
auto iter = devices->find(name);
if (iter == devices->end()) {
return ListenerAction::kContinue;
}
devices->erase(iter);
device_handler_->HandleUevent(uevent);
return devices->empty() ? ListenerAction::kStop : ListenerAction::kContinue;
}
上层应用通过文件系统读取某个block时,调用dm-verity读取对应的block,
dm-verity会根据verity-table调用block_device读取对应的block,然后逐层计算到根block,
计算得到root-hash和kernel中保存的root hash进行对比,hash值进行比较,如果相等,则说明该块没有被修改过,一切正常。
编译system.img时调用了build_image.py脚本
define build-systemimage-target
@echo "Target system fs image: $(1)"
$(call generate-userimage-prop-dictionary, $(systemimage_intermediates)/system_image_info.txt, \
skip_fsck=true)
$(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \
build/make/tools/releasetools/build_image.py \
$(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT) \
/build/tools/releasetools/build_image.py中,调用build_verity_tree程序
def BuildImage(in_dir, prop_dict, out_file, target_out=None):
if verity_supported and is_verity_partition:
if not MakeVerityEnabledImage(out_file, verity_fec_supported, prop_dict):
return False
def MakeVerityEnabledImage(out_file, fec_supported, prop_dict):
# build the verity tree and get the root hash and salt
if not BuildVerityTree(out_file, verity_image_path, prop_dict):
return False
def BuildVerityTree(sparse_image_path, verity_image_path, prop_dict):
cmd = ["build_verity_tree", "-A", FIXED_SALT, sparse_image_path,
verity_image_path]
output, exit_code = RunCommand(cmd)
if exit_code != 0:
print("Could not build verity tree! Error: %s" % output)
return False
root, salt = output.split()
prop_dict["verity_root_hash"] = root
prop_dict["verity_salt"] = salt
return True
调用到system\extras\verity\build_verity_tree.cpp,
过程包括:
根据block_size大小和hash_size大小,计算得到一个block可以存放的hash个数从level 0开始,计算需要多少个level;
创建数组verity_tree_levels, 用于指示每一个level在verity_tree中的起始地址;
创建数组verity_tree_level_blocks, 用于指示每一个level在verity_tree中的长度;
计算ext4 image中各个块的hash值存到verity_tree的level0,然后逐层往上计算各层的hash值,并填入verity_tree中相应的level。
def build_verity_metadata(data_blocks, metadata_image, root_hash, salt,
block_device, signer_path, signing_key, signer_args=None,
verity_disable=False):
# build the verity table
verity_table = build_verity_table(block_device, data_blocks, root_hash, salt)
# build the verity table signature
signature = sign_verity_table(verity_table, signer_path, signing_key, signer_args)
# build the metadata block
metadata_block = build_metadata_block(verity_table, signature, verity_disable)
# write it to the outfile
with open(metadata_image, "wb") as f:
f.write(metadata_block)
build_verity_table函数中,第一个参数block_device描述了dm-verity设备对应了那个底层的block,第二个参数block_device指定了hash-tree存在于哪个block设备上,
BLOCK_SIZE参数为默认的4k, data_blocks描述了有多少个block,data_blocks + (METADATA_SIZE / BLOCK_SIZE)表示hash-tree在对应block设备上的偏移位置,
最后可找到hash-tree,root_hash为hash-tree的根hash。
另外,verity table是存在分区中的super block(超级块)之后的位置,便于找到校验的位置。
def build_verity_table(block_device, data_blocks, root_hash, salt):
table = "1 %s %s %s %s %s %s sha256 %s %s"
table %= ( block_device,
block_device,
BLOCK_SIZE,
BLOCK_SIZE,
data_blocks,
data_blocks,
root_hash,
salt)
return table
调用的入口在Init的第一阶段的SetUpDmVerity函数。
Fstab::iterator end;
if (!MountPartition(current, false /* erase_same_mounts */, &end)) {
…
}
bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
Fstab::iterator* end) {
…
if (!SetUpDmVerity(&(*begin))) {
PLOG(ERROR) << "Failed to setup verity for '" << begin->mount_point << "'";
return false;
}
}
else if (fstab_entry->fs_mgr_flags.avb) {
//InitAvbHandle完成此分区的AVB验证
if (!InitAvbHandle()) return false;
#然后执行创建hash tree table并建立与driver侧的ioctl交互连接。
hashtree_result =
avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
相关代码在:system\core\fs_mgr\libfs_avb\ avb_util.cpp
bool HashtreeDmVeritySetup(FstabEntry* fstab_entry, const FsAvbHashtreeDescriptor& hashtree_desc,
bool wait_for_verity_dev) {
android::dm::DmTable table;
if (!ConstructVerityTable(hashtree_desc, fstab_entry->blk_device, &table) || !table.valid()) {
LERROR << "Failed to construct verity table.";
return false;
}
table.set_readonly(true);
…
if (!dm.CreateDevice(device_name, table, &dev_path, timeout)) {
LERROR << "Couldn't create verity device!";
return false;
}
Hash table传入的格式如下,中间做了序列号处理,其实就是一串长值拼成的字串。
调用的入口在Init的第一阶段的SetUpDmVerity函数。
android::dm::DmTargetVerity target(
0, hashtree_desc.image_size / 512, hashtree_desc.dm_verity_version, blk_device,
blk_device, hashtree_desc.data_block_size, hashtree_desc.hash_block_size,
hashtree_desc.image_size / hashtree_desc.data_block_size,
hashtree_desc.tree_offset / hashtree_desc.hash_block_size, hash_algorithm.str(),
hashtree_desc.root_digest, hashtree_desc.salt);
CreateDevice函数里面有三个函数,分别实现了上述的几个ioctl命令,把拼接好的hashtable传到驱动侧。
bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path,
const std::chrono::milliseconds& timeout_ms) {
std::string uuid = GenerateUuid();
if (!CreateDevice(name, uuid)) {
return false;
}
std::string unique_path;
if (!LoadTableAndActivate(name, table) || !GetDeviceUniquePath(name, &unique_path) ||
!GetDmDevicePathByName(name, path)) {
DeleteDevice(name);
return false;
}
对应的IOCTL命令
ioctl(fd_, DM_DEV_CREATE, &io)
ioctl(fd_, DM_TABLE_LOAD, io)
ioctl(fd_, DM_DEV_SUSPEND, io)
好了,以上就是Device Mapper和Dm verity的简介了,实际上的内容会更多,大家学习的过程中多多发现吧。
为方便与大家及时交流,弄了一个微信公众号,欢迎大家留言沟通~
1,前言微信企业号与微信公众号的配置流程大同小异(殊途同归)。2,准备工作2.1,企业号的申请与域名的申请及备案 企业号(公众号)申请需要提前申请,因为有些东西需要备案,审核啥的; 测试环境/生产环境的域名需要提前准备,无论是个人还是企业应用搭建都需要提前准备,因为域名备案是需要一定时间的;2.2,域名与企业号(公众号)配置产生联系的地..._企业微信和公众号登录开发
Final Cut Pro X Guru: Blending Mode Secrets 中文字幕Final Cut Pro X Guru:混合模式秘密 中文字幕Final Cut Pro X Guru: Blending Mode SecretsFinal Cut Pro中的一个重要调整工具是混合模式混合模式允许两个剪辑之间的微妙和复杂的交互,特别是合成通过混合模式学习日常舒适的关键要素,...
springboot_当拦截器获取到请求参数时,controller不能获取到请求参数
一.SDES加密算法SDES的算法如上图所示,作者将其分为三个部分讲解:1.子密钥的产生 1.1 对于输入的10位密钥K=(k1,k2,k3,k4,k5,k6,k7,k8,k9,k10),做P10置换,得到结果为K1=(k3,k5,k2,k7,k4,k10,k1,k9,k8,k6)。 1.2 将上一步中的K1分成LK1和RK1分别对应K1的高
为什么80%的码农都做不了架构师?>>> ...
从这一篇博客开始,开始利用Open3d来处理点云数据。之后将围绕点云数据的多种处理方式来记录笔记。本篇博客的内容包括点云的文件格式介绍,点云数据的读取,以及点云的配准与点云的法向量计算。_open3d
O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n)如图:_时间复杂度和空间复杂度那个用得多
v-model里传给后台的值即为 :value 里的值目前效果:(1)添加时:(2)添加完成后回写效果期望效果:完成过程:首先再data中定义一个值回写数据部分:即可实现,如果是汉字转为数字改为String()参考:https://segmentfault.com/a/1190000016737140?_ea..._element ui列表显示value对应的label
有时候我们使用Laravel-admin管理数据时,需要保存一些通过程序运算出来的数据,而不只是存储写在表单中的数据,也就是需要在保存数据前可以设置或改变数据。比如存在这么个需求:为了快速创建\管理一些测试数据,在Admin管理端创建一个用户账户的同时,创建相关的用户信息,用户订单、地址库等等一系列信息。以这个需求中用户关联的数据量来说,每个数据都手动输入的话,效率太低了,所以只能自动创..._laravel admin 在关联模型保存前
拓扑排序其实就是对有向无环图的顶点的一种排序,每个顶点出现且只出现一次。对一个AOV网进行拓扑排序的方法:1、从AOV网中选择一个入度为0的顶点并输出;2、从网中删除该顶点和所有以它为起点的有向边;3、重复1和2直到当前的AOV网为空或当前网中不存在入度为0的顶点为止;该算法中假设使用邻接表作为存储结构:时间复杂度为:O(|V|+|E|);若采用邻接矩阵存储时,其时间复杂度为O(|V|^2);祖先结点的DFS函数结束时间大于子孙结点的DFS函数结束时间,利用DFS求出各顶点结束时间,对于拓扑排序,就是_逆拓扑排序
前戏:老牌python GUI程序(Tkinter)import tkinter.messagebox as messageboxclass Application(Frame): def __init__(self,master=None): Frame.__init__(self,master,bg="red") #设置框架类的父类(基于master..._nonetype' object has no attribute 'curselection
安装mosquitto前,电脑的openssl版本是1.1.1a,安装时一直报以下错误:make[1]: 正在进入目录 `/work/MQTT/mosquitto-1.5.5/client'cc -c pub_client.c -o pub_client.o -Wall -ggdb -O2 -I.. -I../lib -DVERSION="\"1.5.5\"" -DWITH_TLS -D..._对‘tls_client_method’未定义的引用