赞
踩
三、分区挂载流程介绍
3.1 分区挂载顺序总览
挂载顺序 | 实际内容所在物理分区 | 挂载点名称 | 是否为logical分区 | Avb校验 | avb校验信息保存位置 | 挂载阶段 | fstab生效位置 |
1 | metadata | /metadata | 否 | 否 | N/A | init first stage | vendor_boot(ramdisk) |
2 | super | /system | 是 | 是 | vbmeta_system | init first stage | vendor_boot(ramdisk) |
5 | super | /system_ext | 是 | 是 | vbmeta_system | init first stage | vendor_boot(ramdisk) |
6 | super | /product | 是 | 是 | vbmeta_system | init first stage | vendor_boot(ramdisk) |
7 | super | /vendor | 是 | 是 | vbmeta_vendor | init first stage | vendor_boot(ramdisk) |
8 | super | /vendor_dlkm | 是 | 是 | vbmeta_vendor | init first stage | vendor_boot(ramdisk) |
9 | super | /odm | 是 | 否 | N/A | init first stage | vendor_boot(ramdisk) |
10 | super | /odm_dlkm | 是 | 否 | N/A | init first stage | vendor_boot(ramdisk) |
32 | persist | /mnt/vendor/persist | 否 | 否 | N/A | init second stage (mount_all --early) | /vendor/etc/fstab.qcom |
33 | modem | /vendor/firmware | 否 | 否 | N/A | init second stage (mount_all --early) | /vendor/etc/fstab.qcom |
34 | dsp | /vendor/dsp | 否 | 否 | N/A | init second stage (mount_all --early) | /vendor/etc/fstab.qcom |
35 | bluetooth | /vendor/bt_firmware | 否 | 否 | N/A | init second stage (mount_all --early) | /vendor/etc/fstab.qcom |
36 | qmcs | /mnt/vendor/qmcs | 否 | 否 | N/A | init second stage (mount_all --early) | /vendor/etc/fstab.qcom |
37 | userdata | /data | 否 | 否 | N/A | init second stage (mount_all --late) | /vendor/etc/fstab.qcom |
3.2 metadata分区挂载流程
metadata分区是开机后第一个挂载的分区,挂载流程也比较简单。本节内容就介绍一下metadata分区挂载流程,挂载位置在如下代码
调用通用的MountPartition函数,进行挂载。这里就不再赘述,感兴趣的可以走读一下代码。
System挂载代码如下
System分区是一个逻辑分区,前面《dm实现动态卷(逻辑分区)功能介绍》一章中介绍过
System是一个逻辑分区,实际存在在super这个物理分区中。
第一步:
就是先创建一个dm-linear的dm设备,将super分区中对应的system image存放的物理地址映射成一个dm设备,相关代码如下。
通过fs_mgr_updata_logical_partition找到system分区对应的dm-xx编号
通过InitDmDevice等待/sys/block/dm-xx创建好
第二步:判断是否需要开启dm校验
第三步:
之后就可以和挂载普通分区一样,挂载system分区
3.4 userdata分区挂载流程
元数据加密(default-key)一节中已经介绍了,userdata分区是依赖dm实现元数据加密的。
所以userdata挂载结构如下图
下面就详细介绍一下userdata分区挂载流程
Userdata挂载阶段是在second stage (mount_all --late)阶段。对应rc文件如下。
进行一些fstab解析后,就调用到了fs_mgr_mount_all进行userdata挂载
之前提到,Default-key加密只能在分区首次进行格式化时设置。所以userdata分区挂载就需要分两种情况。情况一:首次开机。情况二:已经完成元数据加密的后续开机。下面就分别介绍这两种情况的流程
情况一:首次开机关键流程如下
主要流程在encryptFstab中,可以查看前面章节的详细介绍。
功能就是创建用于元数据加密的key,将userdata分区映射为key-default的dm设备,并格式化成f2fs文件系统,挂载这个分区。
情况二:第二次开机关键流程
主要流程在mountFstab中,细节可以查看前面章节
所以第二次开机会先尝试挂载userdata分区,此时userdata分区已经被元数据加密了。无法挂载(读取不到superblock头)。就会经过判断,通过mountFstab挂载元数据加密的userdata分区。
四、常见问题汇总
4.1 set_policy_failed问题
故障现象:重启进recovery,进recovery原因为set_policy_failed
故障原理介绍:
在FBE加密一章节中我们介绍过,/data路径下部分文件夹需要进行FBE加密。
在启动时会准备 FBE Master key,设置和校验各加密存储位置的加密策略(Encryption Policy)。真正的解密发生在文件I/O时,会根据对应文件设置的加密策略(Encryption Policy),使用对应的解密算法及key,进行解密。而set_policy_failed的原因就是在针对某个文件夹设置加密策略时,出现了错误
rc文件在使用mkdir创建文件夹时,会检查创建文件夹的前缀是否为/data,之后会根据 一些默认的路径,觉得当前路径是否需要进行FBE加密,以及是使用哪种加密策略,一般开机会有两种参考的加密策略
A.ref,对应的是System DE Storage 的key和加密策略
B.per_boot_ref,对应的是每次开机生成的per_boot Storage 的key和加密策略
这两个加密策略分别存放在/data/unencrypted/ref和/data/unencrypted/per_boot_ref
当然我们也可以在rc文件中指定创建的文件夹的加密策略(添加encryption=<加密
策略选项>),或指定FBE加密使用的Master key类型(添加key=<ref/per_boot_ref>)
加密策略由如下选项:
| |
| |
| |
| |
|
|
如果在指定文件夹的加密策略时出现了异常,就会导致set_policy_failed。之后就会
引导机器进recovery,相关代码在make_dir_with_options函数中
设置文件夹加密策略的处理函数如下
那什么情况下会set_policy_failed呢?
Set policy 流程简单总结如下
根据FBE storage的加密策略,对应设置到文件夹的拓展属性中。Set_policy_failed
的原因就是黄色和绿色部分不匹配。可能的原因是
a.黄色部分更新,但绿色部分还是保留的老的加密策略(最常见)
b.文件中的加密策略需要通过ioctl读取,如果没有权限打开对应文件夹的话,也会导
致失败
c.黄色/绿色部分出现损坏,导致两者不匹配
d.keymaster相关错误导致两个不匹配
问题分析思路:
a.出问题的文件路径是什么?
b.使用的哪个FBE storage的加密策略,这个FBE storage加密策略的更新时机是什
么?
c.这个路径删除的时机是什么?
然后去分析加密策略的更新时机与路径的删除时机是否同步。是否存在加密策略更新,但路径未删除或删除失败的情况。
典型案例:
案例一:旧文件未删除,与新生成的FBE storage加密策略不匹配
之前介绍fbeEnable提到,每次开机会生成一个新的per_boot_ref。用于加密/data/per_boot路径。但是如果/data/per_boot路径未删除的话,就会导致/data/per_boot 文件夹拓展属性中的加密策略还是上一次开机的(绿色部分),此时mkdir 指定key=per_boot_ref是本次开机新生成的(黄色部分),
导致两者不匹配报set_policy_failed。
典型log
这个问题最后分析是某些修改导致概率性的rm -rf/data/per_boot导致的。这里就不展开了。
案例二:权限不够,无法执行ioctl设置文件夹加密策略
加密策略是通过ioctl设置到文件的拓展属性中的。所以设置时需要先打开文件夹,通过fd去设置加密策略。如果没有权限打开文件夹的话,就会导致set_policy_failed
相关代码在fscrypt.cpp
主要log
4.2 init_user0_failed问题
故障现象:重启进recovery,进recovery原因为init_user0_failed
故障原理介绍:
init_user0 的主要流程前面章节有详细介绍,摘取如下:
“如果是首次开机(根据user DE key存放路径判断),
创建 User 0 DE Master Key 和生成 User 0 DE Encryption Policy;
创建 User 0 CE Master Key 和生成 User 0 CE Encryption Policy;
创建 User 0 DE Storage,并为这些文件夹设置 User 0 DE Encryption Policy
后续开机
加载 User 0 DE Master Key”
根据原理,init_user0 failed的原因可以结合代码和前面章节中initUser0重要流程的介绍进行查看分析,看fscrypt_init_user0函数中return false的位置。
分析思路也主要是关注几个重点流程
a.保存master key的文件夹是否正常
b.创建Master Key和生成Encryption Policy是否正常
c.设置文件夹的Encryption Policy是否正常
常见的故障原因有
a.userdata分区未正常挂载,导致保存master key的文件夹无法正常正常。从而导致failed
b.Keymaster(安全相关)报错,导致创建Mater Key和生成Encryption policy异常
典型案例
案例一:data分区未挂载,导致init user0创建保存key的文件夹失败(最常见)
典型log
出错代码位置
Fscrypt_init_user0的第一件时间就是创建/data/misc/vold/user_keys文件夹,用于存放生成的master key。所以mkdir失败时,一般都是其他原因导致userdata分区未正常挂载,进而导致init user0 failed。Userdata挂载失败的原因就不在本章节展开,在后面userdata挂载失败的案例分析中详细介绍
案例二:
典型log:这个问题从描述来看,应该是必现的,所以没有保存log。应该是root后查看/data/misc文件夹下内容是乱码
这个问题原因应该是安全有相关修改,导致升级后/data/misc文件夹无法正常进行FBE解密,从而无法读取user DE key,从而导致init_user0 failed
案例三:de key丢失,或tee keymaster安全接口返回异常
这两种情况暂未搜索到对应BUGID,但是在工作中可能也会遇到。例如我们手动将/data/misc/vold/user_keys/de/0/encrypted_key删掉后,就会导致init_user0读取master_key时出现错误。从而导致init_user0 failed
或者在init_user0 出现了keymaster报错导致无法加载key
典型log如下
故障现象:进recovery,原因为enablefilecrypto failed
Enablefilecrypto,实际执行的是fbeEnable。
相关调用流程是在data分区挂载后,会调用installkey /data 执行fbeEnable
Fbeenable的详细流程参考前面章节。抽象异常关键步骤如下
同样关注fscrypt_initialize_systemwide_keys返回false的位置
故障点一:创建或读取key异常
典型log:
读取到/data/unencrypted/key后,keystore2(安全模块)报错,导致device_key无法正常初始化
其他原因故障暂时没有遇到相关案例,不过遇到问题时,还是从上面的原理入手进行分析。这样才能以不变应万变。
故障现象:进recovery,原因为init_user0_failed
Userdata挂载失败不会直接导致机器重启,而是由于userdata无法挂载,后续的init user0执行失败,从而导致进recovery。先看一下userdata挂载的几个关键流程。任一流程出错都可能导致userdata分区挂载失败。
故障点一:
红色部分出现了问题。无法读取到key
案例一:
因为某些原因,导致metadata分区中保存的key丢失。导致无法完成metadata解密。可以手动删掉/metadata/vold/metadata_encryption/key模拟这种场景
典型log:
故障点二:
Key可以读取到,但是实际解密失败。
案例二:
安全模块异常,导致解密过程失败->无法完成元数据解密->无法生成解密后的dm设备->userdata无法挂载
典型log
案例三:
key存在,但是依旧无法进行元数据解密。
例如userdata已经完成元数据加密,此时强制userdata不进行元数据解密挂载(普通用户版本->保留用户数据刷到工厂版本)。就会出现这种问题。
对于userdata是否完成了元数据加密,有个小技巧可以通过回读userdata分区前4k内容查看。
如果没有进行元数据加密,那userdata分区的前4k就是superblock头。如果完成了元数据加密,userdata分区前4k就不是superblock头相关内容。
故障点三:
dm设备相关异常。Userdata解密依赖dm设备驱动,所以当dm设备驱动出现问题后,也会导致userdata无法完成解密
案例四:
之前遇到过dm设备失败的问题(DEV_BUS或创建超时),导致userdata无法完成元数据解密。但是bugid暂时无法找到。这个案例不展开。分析问题的时候也需要注意是否是dm设备创建的问题。
典型log:
结论:
关机时ServiceManager crash了,导致binder通信节点为空,vdc没法通过binder
abort_fuse,从而不能volume shutdown,shutdown超时导致死机重启
往
期
推
荐
长按关注内核工匠微信
Linux内核黑科技| 技术文章| 精选教程
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。