android Q的init rc脚本创建目录失败问题调查

最近项目碰到一个奇怪的问题,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进程没权限。

你可能感兴趣的:(android)