上章内容请点击链接:Android分区挂载原理介绍(上)

三、分区挂载流程介绍    

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分区挂载流程,挂载位置在如下代码

545ac427c47ca13a38e6b185f36ddce4.png

调用通用的MountPartition函数,进行挂载。这里就不再赘述,感兴趣的可以走读一下代码。

7dc87f2574c2caa60200b2c353d0306e.png

3.3 system分区挂载流程

  

System挂载代码如下    

07e127a274d3f72b1c3bac5dfd082e48.png

System分区是一个逻辑分区,前面《dm实现动态卷(逻辑分区)功能介绍》一章中介绍过

System是一个逻辑分区,实际存在在super这个物理分区中。

第一步:

就是先创建一个dm-linear的dm设备,将super分区中对应的system image存放的物理地址映射成一个dm设备,相关代码如下。

6c695ef3d914f4cd29606493886874c0.png

通过fs_mgr_updata_logical_partition找到system分区对应的dm-xx编号

通过InitDmDevice等待/sys/block/dm-xx创建好

第二步:判断是否需要开启dm校验

80fbe85e38535da4790ed9f54da2ac59.png 

第三步:

之后就可以和挂载普通分区一样,挂载system分区    

4fe043195c353e8ad9ca33840c12e960.png

3.4 userdata分区挂载流程

元数据加密(default-key)一节中已经介绍了,userdata分区是依赖dm实现元数据加密的。

所以userdata挂载结构如下图    

f6f9c63c1840d48f1f10f1106afcd411.png

下面就详细介绍一下userdata分区挂载流程

Userdata挂载阶段是在second stage (mount_all --late)阶段。对应rc文件如下。

b5fa8e15a522e84c0ae9f05ce6284ac8.png

进行一些fstab解析后,就调用到了fs_mgr_mount_all进行userdata挂载    

3ae7ee16ba19c9f44820d5f61f71b9c1.png

之前提到,Default-key加密只能在分区首次进行格式化时设置。所以userdata分区挂载就需要分两种情况。情况一:首次开机。情况二:已经完成元数据加密的后续开机。下面就分别介绍这两种情况的流程

情况一:首次开机关键流程如下    

49da49e265bd8487f08498c5ed68bf1c.png

主要流程在encryptFstab中,可以查看前面章节的详细介绍。

功能就是创建用于元数据加密的key,将userdata分区映射为key-default的dm设备,并格式化成f2fs文件系统,挂载这个分区。

情况二:第二次开机关键流程

282729ab707db9847c05924d2fad0bd9.png    

7021993d16e919b91cb6e19afeb6b99d.png

主要流程在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>)

加密策略由如下选项:
encryption参数
     
参数作用
     
Require
     
指定加密策略,可选ref/per_boot_ref
     
None
     
不进行加密
     
Attempt
     
     
DeleteIfNecessary
     
如果解密失败/尝试删除文件后再加密

如果在指定文件夹的加密策略时出现了异常,就会导致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

70e52e28cfbd6ba30803a2c61670e5a8.png

出错代码位置

fbf0e76aaa4dc9609bdc0baf5e34ab78.png

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如下

76549dab3aaa99c205bd7e13440f25e1.png

4.3 enablefilecrypto_failed问题  

故障现象:进recovery,原因为enablefilecrypto failed

Enablefilecrypto,实际执行的是fbeEnable。

02359d905d68953d05fe454d935621c3.png    

相关调用流程是在data分区挂载后,会调用installkey /data 执行fbeEnable

37d34dbbe2f50a7225d85b1091868e95.png

4404b3f031b8c4d4a9a3f4831f9de7f4.png

Fbeenable的详细流程参考前面章节。抽象异常关键步骤如下

ee53cbd95ad6087d66b01242ce58399b.png

同样关注fscrypt_initialize_systemwide_keys返回false的位置

故障点一:创建或读取key异常    

e70d7e5da9814702eb7e028056fc309c.png

典型log:

读取到/data/unencrypted/key后,keystore2(安全模块)报错,导致device_key无法正常初始化

17f3dae6e7a142ac7711815ddef70872.png

其他原因故障暂时没有遇到相关案例,不过遇到问题时,还是从上面的原理入手进行分析。这样才能以不变应万变。

4.4 userdata挂载失败问题  

故障现象:进recovery,原因为init_user0_failed

Userdata挂载失败不会直接导致机器重启,而是由于userdata无法挂载,后续的init user0执行失败,从而导致进recovery。先看一下userdata挂载的几个关键流程。任一流程出错都可能导致userdata分区挂载失败。    

2dab5a2206c958e73870bab2acd10ed3.png

故障点一:

红色部分出现了问题。无法读取到key

2f11c1358450022f4c20521aa7254fde.png

案例一:

因为某些原因,导致metadata分区中保存的key丢失。导致无法完成metadata解密。可以手动删掉/metadata/vold/metadata_encryption/key模拟这种场景

典型log:

89477c4eba0405560871ab9ce05f5691.png

故障点二:

Key可以读取到,但是实际解密失败。    

f3ce3b1571d265dfb3fff2bc842a1f0f.png

案例二:

安全模块异常,导致解密过程失败->无法完成元数据解密->无法生成解密后的dm设备->userdata无法挂载

典型log

2e0a8ed5b7f56696d028c2a5ad7a78f8.png

案例三:

key存在,但是依旧无法进行元数据解密。

例如userdata已经完成元数据加密,此时强制userdata不进行元数据解密挂载(普通用户版本->保留用户数据刷到工厂版本)。就会出现这种问题。

对于userdata是否完成了元数据加密,有个小技巧可以通过回读userdata分区前4k内容查看。

如果没有进行元数据加密,那userdata分区的前4k就是superblock头。如果完成了元数据加密,userdata分区前4k就不是superblock头相关内容。

故障点三:

dm设备相关异常。Userdata解密依赖dm设备驱动,所以当dm设备驱动出现问题后,也会导致userdata无法完成解密

698fc554f722949c22a4a250e5c19d6b.png

案例四:

之前遇到过dm设备失败的问题(DEV_BUS或创建超时),导致userdata无法完成元数据解密。但是bugid暂时无法找到。这个案例不展开。分析问题的时候也需要注意是否是dm设备创建的问题。

4.5 关机时ServiceManager crash导致vold shutdown超时死机重启  

典型log:
结论:

关机时ServiceManager crash了,导致binder通信节点为空,vdc没法通过binder

abort_fuse,从而不能volume shutdown,shutdown超时导致死机重启        

Linux内核并发与同步机制解读(arm64)上

Linux内核并发与同步机制解读(arm64)下

crash实战:手把手教你使用crash分析内核dump

193d91e436e7c2a5bceaad76ba778ea3.gif

长按关注内核工匠微信

Linux内核黑科技| 技术文章| 精选教程

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐