文章目录
一、Device Mapper
1. Device Mapper概述
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
2. Device Mapper的使用
在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;
}
二、Dm Verity
1. Dm Verity验证思想
上层应用通过文件系统读取某个block时,调用dm-verity读取对应的block,
dm-verity会根据verity-table调用block_device读取对应的block,然后逐层计算到根block,
计算得到root-hash和kernel中保存的root hash进行对比,hash值进行比较,如果相等,则说明该块没有被修改过,一切正常。
2. Hashtree脚本处理
2.1 镜像编译
编译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) \
2.2 创建verity tree
/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。
2.3 创建metadata
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
3. Dm verity设备的创建
3.1 SetUpDmVerity函数
调用的入口在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;
}
3.2 hash table处理
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的简介了,实际上的内容会更多,大家学习的过程中多多发现吧。
为方便与大家及时交流,弄了一个微信公众号,欢迎大家留言沟通~