最近项目碰到一个奇怪的问题,wifi组报告说自己的rc脚本创建目录不成功,提示如下错误
11-19 18:40:57.855 1000 12091 13106 E WifiApBackupRestore: FileWriter exception java.io.FileNotFoundException: /data/misc/wifi_hostapd/backup.conf: open failed: ENOENT (No such file or directory)
说原因是因为rc脚本在开机的时候,创建/data/misc/wifi_hostapd目录失败
Line 44430: <14>[ 14.878830] [7: init: 1] init: Command 'mkdir /data/misc/wifi_hostapd 0771 wifi system' action=post-fs-data (/vendor/etc/init/wifi.rc:8) took 0ms and failed: mkdir() failed: Permission denied
Line 44430: <14>[ 14.878830] [7: init: 1] init: Command 'mkdir /data/misc/wifi_hostapd 0771 wifi system' action=post-fs-data (/vendor/etc/init/wifi.rc:8) took 0ms and failed: mkdir() failed: Permission denied
创建目录的操作在/vendor/etc/init/wifi.rc里面,开机的时候被init进程load进来执行
android/system/core/init/init.cpp
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
147 parser.ParseConfig("/init.rc");
...
160 if (!parser.ParseConfig("/vendor/etc/init")) {
161 late_import_paths.emplace_back("/vendor/etc/init");
162 }
整个/vendor/etc/init目录下的rc文件都会被加载进来。
执行的时机是action=post-fs-data,在文件系统加载成功之后,理论上不应该有问题,毕竟init进程具有root权限,在data分区创建目录也不太可能被selinux拦截,因为其他rc里面也要创建目录,但只看到wifi.rc出现没有权限的情况。但是log的确打印了init无权限创建,显得很矛盾。
保险起见还是打印下内核态创建/data/misc/wifi_hostapd的一些状态,把进程号打印出来
结果发现虽然main log打印的是init 1号进程无权限创建目录,但是内核显示是另外一个进程
<6>[ 16.733758] [4: init: 572] [SYSC_mkdirat:3951] mkdirat found target wifi_hostapd
<6>[ 16.733852] [4: init: 572] [vfs_mkdir2:3899] error:-13
<6>[ 16.733893] [4: init: 572] [SYSC_mkdirat:3964] error:-13
<6>[ 16.733934] [4: init: 572] pid:572 process:init
<6>[ 16.733970] [4: init: 572] [SYSC_mkdirat:3973] return error:-13
进程572是个什么东西?
starqltechn:/ # ps -ATZ | grep init
u:r:init:s0 root 1 1 0 96364 5980 SyS_epoll_wait 0 S init
u:r:vendor_init:s0 root 572 572 1 41796 3280 poll_schedule_timeout 0 S init
u:r:vendor_init:s0 root 573 573 1 40452 3044 poll_schedule_timeout 0 S init
看出来,进程572和573都是init进程1的子进程,全新进程,但是最关键的是selinux标签不一样了,这完全可以解释572进程没有/data目录的mkdir权限,显然是selinux限制了。
但是vendor_init是什么东西?通过检索谷歌,发现原来vendor init是init进程分化出来的一个受限进程,专门用来执行vendor的rc脚本,防止vendor的rc脚本里面越权做一些非法的事情。因为wifi.rc属于vendor rc,因此其不能创建目录也就在理了。
搜索init的代码发现,init初始化的时候会创建这两个子进程,一个专门处理vendor的rc,另外一个专门处理/odm
/android/system/core/init/subcontext.cpp
const std::string kVendorContext = "u:r:vendor_init:s0";
53 const char* const paths_and_secontexts[2][2] = {
54 {"/vendor", kVendorContext.c_str()},
55 {"/odm", kVendorContext.c_str()},
56 };
静态数组kVendorContext在初始化过程会被转换成一个进程,具体流程可以自行参考代码。
那解决这个问题的办法就有两种
1. 在非vendor的rc脚本里面创建/data目录下的需要的子目录
2. 修改vendor init的te策略文件,授权mkdir操作。
看起来还是方案1比较安全。
最后剩下一个问题,为什么main log里面打印的进程号是init 1号进程,不是init 572。
原因是因为init 572的log都传回给了init 1号进程去打印,导致误以为init 1进程没权限。