未完,更新中 。。。
"android"系列分为三部分:
1.正常开机挂载
2.encryption
3.dm-verity
我们知道android有很多分区,如"system","userdata","cache",他们是何时挂载的?如何挂载的?这个系列的文章进行分析。这里介绍第一部分,android手机正常开机各分区的挂载。这里我们以mtk平台进行分析,高通与mtk差别不是很大。
我们知道kernel起来以后执行的第一个文件是init进程,init进程会根据init.rc的规则启动进程或者服务。init.rc通过"import /init.${ro.hardware}.rc"语句导入平台的规则。
device/mediatek/mt6797/init.mt6797.rc on fs
write /proc/bootprof "INIT:Mount_START"
mount_all /fstab.mt6797
chown system system /mobile_info
chmod /mobile_info
exec /system/bin/tune2fs -O has_journal -u -r /dev/block/platform/mtk-msdc./.msdc0/by-name/userdata
write /proc/bootprof "INIT:Mount_END"
mount_all是一条命令,/fstab.mt6797是传入的参数
system/core/init/keywords.h .....
KEYWORD(mount_all, COMMAND, , do_mount_all)
.....
从上面我们可以看出,mount_all命令对应的是do_mount_all函数,/fstab.mt6797是do_mount_all函数的传入参数
system/core/init/builtins.cpp int do_mount_all(int nargs, char **args)
{
pid_t pid;
int ret = -;
int child_ret = -;
int status;
struct fstab *fstab; if (nargs != ) {
return -;
} /*
* Call fs_mgr_mount_all() to mount all filesystems. We fork(2) and //使用fs_mgr_mount_all()函数去挂载所有的文件系统,我们使用fork()函数分配一个新的进程,在子进程中做挂载的事情,这样即使挂载出现问题,也能保护init主进程。
* do the call in the child to provide protection to the main init
* process if anything goes wrong (crash or memory leak), and wait for
* the child to finish in the parent.
*/
pid = fork();
if (pid > ) { //父进程,等待子进程(pid=0)返回
/* Parent. Wait for the child to return */
int wp_ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, ));
if (wp_ret < ) {
/* Unexpected error code. We will continue anyway. */
NOTICE("waitpid failed rc=%d: %s\n", wp_ret, strerror(errno));
} if (WIFEXITED(status)) {
ret = WEXITSTATUS(status);
} else {
ret = -;
}
} else if (pid == ) { //子进程
/* child, call fs_mgr_mount_all() */
klog_set_level(); /* So we can see what fs_mgr_mount_all() does */ //修改kernel log的等级,让我们可以看到fs_mgr_mount_all函数的log
fstab = fs_mgr_read_fstab(args[]); //args[1]是传入的参数/fstab.mt6797,是一个文件。 加载分区挂载文件的内容到fstab结构体中。
child_ret = fs_mgr_mount_all(fstab); //挂载分区*******************
fs_mgr_free_fstab(fstab);
if (child_ret == -) {
ERROR("fs_mgr_mount_all returned an error\n");
}
_exit(child_ret);
} else {
/* fork failed, return an error */
return -;
} if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
property_set("vold.decrypt", "trigger_encryption");
} else if (ret == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "block");
property_set("vold.decrypt", "trigger_default_encryption");
} else if (ret == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
property_set("ro.crypto.state", "unencrypted");
/* If fs_mgr determined this is an unencrypted device, then trigger
* that action.
*/
action_for_each_trigger("nonencrypted", action_add_queue_tail);
} else if (ret == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
/* Setup a wipe via recovery, and reboot into recovery */
ERROR("fs_mgr_mount_all suggested recovery, so wiping data via recovery.\n");
ret = wipe_data_via_recovery();
/* If reboot worked, there is no return. */
} else if (ret == FS_MGR_MNTALL_DEV_DEFAULT_FILE_ENCRYPTED) {
if (e4crypt_install_keyring()) {
return -;
}
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "file"); // Although encrypted, we have device key, so we do not need to
// do anything different from the nonencrypted case.
action_for_each_trigger("nonencrypted", action_add_queue_tail);
} else if (ret == FS_MGR_MNTALL_DEV_NON_DEFAULT_FILE_ENCRYPTED) {
if (e4crypt_install_keyring()) {
return -;
}
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "file");
property_set("vold.decrypt", "trigger_restart_min_framework");
} else if (ret > ) {
ERROR("fs_mgr_mount_all returned unexpected error %d\n", ret);
}
/* else ... < 0: error */ return ret;
}
fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。返回值小于0为error,返回值等于0为子进程,返回值大于0为父进程。
args[1]是传入的参数/fstab.mt6797,是一个文件,生成的位置在/out/target/product/xxx/root/fstab.mt6797,生成这个文件的源文件位于vendor/mediatek/proprietary/hardware/fstab/mt6797/,根据编译规则确定fstab.mt6797文件的内容。
在do_mount_all()函数中,比较重要的两个函数如下,我们分析一下这两个函数
stab = fs_mgr_read_fstab(args[1]);
child_ret = fs_mgr_mount_all(fstab);
首先我们看下fstab结构体和fstab.mt6797文件,fstab结构提要存储fstab.mt6797文件中的挂载信息,
struct fstab {
int num_entries;
struct fstab_rec *recs;
char *fstab_filename;
}; struct fstab_rec {
char *blk_device;
char *mount_point;
char *fs_type;
unsigned long flags;
char *fs_options;
int fs_mgr_flags;
char *key_loc;
char *verity_loc;
long long length;
char *label;
int partnum;
int swap_prio;
unsigned int zram_size;
};
out/target/product/xxx/root/fstab.mt6797
# "vendor/mediatek/proprietary/hardware/fstab/mt6797/fstab.in"
# "<built-in>"
# "<命令行>"
# "vendor/mediatek/proprietary/hardware/fstab/mt6797/fstab.in"
# "vendor/mediatek/proprietary/hardware/fstab/mt6797/fstab.in"
/dev/block/platform/mtk-msdc./.msdc0/by-name/system /system ext4 ro wait,verify /dev/block/platform/mtk-msdc./.msdc0/by-name/userdata /data ext4 noatime,nosuid,nodev,noauto_da_alloc,discard wait,check,resize,forceencrypt=/dev/block/platform/mtk-msdc./.msdc0/by-name/metadata,
/dev/block/platform/mtk-msdc./.msdc0/by-name/cache /cache ext4 noatime,nosuid,nodev,noauto_da_alloc,discard wait,check
/dev/block/platform/mtk-msdc./.msdc0/by-name/protect1 /protect_f ext4 noatime,nosuid,nodev,noauto_da_alloc,commit=,nodelalloc wait,check,formattable
/dev/block/platform/mtk-msdc./.msdc0/by-name/protect2 /protect_s ext4 noatime,nosuid,nodev,noauto_da_alloc,commit=,nodelalloc wait,check,formattable
/dev/block/platform/mtk-msdc./.msdc0/by-name/nvdata /nvdata ext4 noatime,nosuid,nodev,noauto_da_alloc,discard wait,check,formattable
/dev/block/platform/mtk-msdc./.msdc0/by-name/nvcfg /nvcfg ext4 noatime,nosuid,nodev,noauto_da_alloc,commit=,nodelalloc wait,check,formattable
/dev/block/platform/mtk-msdc./.msdc0/by-name/mobile_info /mobile_info ext4 noatime,nosuid,nodev,noauto_da_alloc,discard wait,check
# "vendor/mediatek/proprietary/hardware/fstab/mt6797/fstab.in"
/devices/mtk-msdc./.msdc0* auto vfat defaults voldmanaged=sdcard0:auto
/devices/mtk-msdc./.msdc1* auto auto defaults voldmanaged=sdcard1:auto,encryptable=userdata
/devices/soc/.usb3_xhci* auto vfat defaults voldmanaged=usbotg:auto
.................
我们先分析fstab结构体存放的挂载信息,通过fs_mgr_read_fstab实现
system/core/fs_mgr_fstab.c struct fstab *fs_mgr_read_fstab(const char *fstab_path) //从上面可以知道fstab_path为/fstab.mt6797
{
FILE *fstab_file;
int cnt, entries;
ssize_t len;
size_t alloc_len = ;
char *line = NULL;
const char *delim = " \t";
char *save_ptr, *p;
struct fstab *fstab = NULL;
struct fs_mgr_flag_values flag_vals;
#define FS_OPTIONS_LEN 1024
char tmp_fs_options[FS_OPTIONS_LEN]; fstab_file = fopen(fstab_path, "r"); //打开文件
if (!fstab_file) {
ERROR("Cannot open file %s\n", fstab_path);
return ;
} entries = ;
while ((len = getline(&line, &alloc_len, fstab_file)) != -) { //一行一行的读取文件内容,line是指向存放该行字符的指针 第一次读取文件内容,填充fstab结构体的内容
/* if the last character is a newline, shorten the string by 1 byte */
if (line[len - ] == '\n') { //如果最后一行是新行,缩短一字节的字符串
line[len - ] = '\0';
}
/* Skip any leading whitespace */
p = line;
while (isspace(*p)) {
p++;
}
/* ignore comments or empty lines */ //忽略注释和空格开始的行
if (*p == '#' || *p == '\0')
continue;
entries++; //有用信息的行数
} if (!entries) {
ERROR("No entries found in fstab\n");
goto err;
} /* Allocate and init the fstab structure */
fstab = calloc(, sizeof(struct fstab)); //给fstab结构体分配内存
fstab->num_entries = entries; //fstab->num_entries 存放可用信息的总行数
fstab->fstab_filename = strdup(fstab_path); // fstab->fstab_filename 存放"fstab.mt6797"名称
fstab->recs = calloc(fstab->num_entries, sizeof(struct fstab_rec)); //给fstab->recs结构体分配内存 fseek(fstab_file, , SEEK_SET); cnt = ;
while ((len = getline(&line, &alloc_len, fstab_file)) != -) { //第一次读取文件内容,填充结构体fstab->recs的内容
/* if the last character is a newline, shorten the string by 1 byte */
if (line[len - ] == '\n') {
line[len - ] = '\0';
} /* Skip any leading whitespace */
p = line;
while (isspace(*p)) {
p++;
}
/* ignore comments or empty lines */
if (*p == '#' || *p == '\0')
continue; /* If a non-comment entry is greater than the size we allocated, give an
* error and quit. This can happen in the unlikely case the file changes
* between the two reads.
*/
if (cnt >= entries) {
ERROR("Tried to process more entries than counted\n");
break;
}
//下面以/dev/block/platform/mtk-msdc.0/11230000.msdc0/by-name/userdata /data ext4 noatime,nosuid,nodev,noauto_da_alloc,discard wait,check,resize,forceencrypt=/dev/block/platform/mtk-msdc.0/11230000.msdc0/by-name/metadata,为例
if (!(p = strtok_r(line, delim, &save_ptr))) { //strtok_r字符串分割函数,line表示要分割的字符串,delim要分割的标志,p存放分割后的字符串
ERROR("Error parsing mount source\n");
goto err;
}
fstab->recs[cnt].blk_device = strdup(p); //fstab->recs[cnt].blk_device 存放文件系统绝对路径 /dev/block/platform/mtk-msdc.0/11230000.msdc0/by-name/userdata if (!(p = strtok_r(NULL, delim, &save_ptr))) {
ERROR("Error parsing mount_point\n");
goto err;
}
fstab->recs[cnt].mount_point = strdup(p); //fstab->recs[cnt].mount_point 挂载点 /data if (!(p = strtok_r(NULL, delim, &save_ptr))) {
ERROR("Error parsing fs_type\n");
goto err;
}
fstab->recs[cnt].fs_type = strdup(p); //fstab->recs[cnt].fs_type 文件系统类型 ext4 if (!(p = strtok_r(NULL, delim, &save_ptr))) { //此时的p存放的参数 noatime,nosuid,nodev,noauto_da_alloc,discard
ERROR("Error parsing mount_flags\n");
goto err;
}
tmp_fs_options[] = '\0';
fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL, //fstab->recs[cnt].flags 表示这行有无参数
tmp_fs_options, FS_OPTIONS_LEN);
/*
static struct flag_list mount_flags[] = {
{ "noatime", MS_NOATIME },
{ "noexec", MS_NOEXEC },
{ "nosuid", MS_NOSUID },
{ "nodev", MS_NODEV },
{ "nodiratime", MS_NODIRATIME },
{ "ro", MS_RDONLY },
{ "rw", 0 },
{ "remount", MS_REMOUNT },
{ "bind", MS_BIND },
{ "rec", MS_REC },
{ "unbindable", MS_UNBINDABLE },
{ "private", MS_PRIVATE },
{ "slave", MS_SLAVE },
{ "shared", MS_SHARED },
{ "defaults", 0 },
{ 0, 0 },
};
*/
/* fs_options are optional */
if (tmp_fs_options[]) { //是个flags list,读取noatime,nosuid,nodev,noauto_da_alloc,discard
fstab->recs[cnt].fs_options = strdup(tmp_fs_options);
} else {
fstab->recs[cnt].fs_options = NULL;
} if (!(p = strtok_r(NULL, delim, &save_ptr))) { //此时p存放剩下的参数wait,check,resize,forceencrypt=/dev/block/platform/mtk-msdc.0/11230000.msdc0/by-name/metadata
ERROR("Error parsing fs_mgr_options\n");
goto err;
}
fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
&flag_vals, NULL, ); /*
static struct flag_list fs_mgr_flags[] = {
{ "wait", MF_WAIT },
{ "check", MF_CHECK },
{ "encryptable=",MF_CRYPT },
{ "forceencrypt=",MF_FORCECRYPT },
{ "fileencryption",MF_FILEENCRYPTION },
{ "nonremovable",MF_NONREMOVABLE },
{ "voldmanaged=",MF_VOLDMANAGED},
{ "length=", MF_LENGTH },
{ "recoveryonly",MF_RECOVERYONLY },
{ "swapprio=", MF_SWAPPRIO },
{ "zramsize=", MF_ZRAMSIZE },
{ "verify", MF_VERIFY },
{ "noemulatedsd", MF_NOEMULATEDSD },
{ "notrim", MF_NOTRIM },
{ "formattable", MF_FORMATTABLE },
#ifdef MTK_FSTAB_FLAGS
{ "resize", MF_RESIZE },
#endif
{ "defaults", 0 },
{ 0, 0 },
}; */
fstab->recs[cnt].key_loc = flag_vals.key_loc; //fstab->recs[cnt].key_loc 存放"="后的内容 这里是 "/dev/block/platform/mtk-msdc.0/11230000.msdc0/by-name/metadata"
fstab->recs[cnt].verity_loc = flag_vals.verity_loc;
fstab->recs[cnt].length = flag_vals.part_length;
fstab->recs[cnt].label = flag_vals.label;
fstab->recs[cnt].partnum = flag_vals.partnum;
fstab->recs[cnt].swap_prio = flag_vals.swap_prio;
fstab->recs[cnt].zram_size = flag_vals.zram_size;
cnt++;
}
fclose(fstab_file);
free(line);
return fstab; err:
fclose(fstab_file);
free(line);
if (fstab)
fs_mgr_free_fstab(fstab);
return NULL;
}
fstab.mt6797文件的内容已经读取到fstab结构提中,下面开始分析挂载函数fs_mgr_mount_all,传入的参数就是上面分析的fstab。
system/core/fs_mgr/fs_mgr.c int fs_mgr_mount_all(struct fstab *fstab)
{
int i = ;
int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTED; //这个变量涉及到encryption加密,后面的文章会详细介绍这块
int error_count = ;
int mret = -;
int mount_errno = ;
int attempted_idx = -; if (!fstab) {
return -;
} for (i = ; i < fstab->num_entries; i++) {
/* Don't mount entries that are managed by vold */
if (fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) {
continue;
} /* Skip swap and raw partition entries such as boot, recovery, etc */
if (!strcmp(fstab->recs[i].fs_type, "swap") ||
!strcmp(fstab->recs[i].fs_type, "emmc") ||
!strcmp(fstab->recs[i].fs_type, "mtd")) {
continue;
} /* Translate LABEL= file system labels into block devices */
if (!strcmp(fstab->recs[i].fs_type, "ext2") ||
!strcmp(fstab->recs[i].fs_type, "ext3") ||
!strcmp(fstab->recs[i].fs_type, "ext4")) {
int tret = translate_ext_labels(&fstab->recs[i]);
if (tret < ) {
ERROR("Could not translate label to block device\n");
continue;
}
}
ERROR("blk device name %s\n", fstab->recs[i].blk_device);
#if defined(MTK_UBIFS_SUPPORT) || defined (MTK_FTL_SUPPORT) //这里支持UBIFS/FTL,这里没有使用
if (strcmp(fstab->recs[i].fs_type, "ubifs") == && strncmp("ubi@", fstab->recs[i].blk_device, ) == ) {
char tmp[];
int n = ubi_attach_mtd(fstab->recs[i].blk_device + );
if (n < ) {
ERROR("ubi_attach_mtd fail device name %s\n", fstab->recs[i].blk_device+);
return -;
} n = sprintf(tmp, "/dev/ubi%d_0", n);
free(fstab->recs[i].blk_device);
fstab->recs[i].blk_device = malloc(n+);
sprintf(fstab->recs[i].blk_device, "%s", tmp);
ERROR("debug : ubifs blk_device %s", fstab->recs[i].blk_device);
} else if (!strcmp(fstab->recs[i].fs_type, "rawfs") || !strcmp(fstab->recs[i].fs_type, "yaffs2")) {
char tmp[];
int n = mtd_name_to_number(fstab->recs[i].blk_device + );
if (n < ) {
return -;
} n = sprintf(tmp, "/dev/block/mtdblock%d", n);
free(fstab->recs[i].blk_device);
fstab->recs[i].blk_device = malloc(n+);
sprintf(fstab->recs[i].blk_device, "%s", tmp);
ERROR("debug : rawfs blk_device %s", fstab->recs[i].blk_device);
}
#ifdef MTK_FTL_SUPPORT
else if (!strcmp(fstab->recs[i].fs_type, "ext4") && strstr(fstab->recs[i].blk_device, "ftl")) {
char tmp[];
int err = ;
int n = -;
int ubi_num = fstab->recs[i].blk_device[] - '';
ERROR("debug : mtk_ftl_blk %s ubi_num %d\n", fstab->recs[i].blk_device, ubi_num);
if(strstr(fstab->recs[i].mount_point, "system")){
n = ubi_attach_mtd("system");
}else if(strstr(fstab->recs[i].mount_point, "data")){
n = ubi_attach_mtd("userdata");
}else if(strstr(fstab->recs[i].mount_point, "cache")){
n = ubi_attach_mtd("cache");
}
if((n != ubi_num) && (n >= ))
{
ERROR("ubi number: %d == %d\n", n, ubi_num);
ubi_num = n;
}
n = sprintf(tmp, "/dev/ubi%d_0", ubi_num);
if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
int ret = wait_for_file(tmp, WAIT_TIMEOUT);
ERROR("wait_for_file(%s) ret = %d, errno = %s\n", fstab->recs[i].blk_device, ret, strerror(errno));
}
err = ftl_attach_ubi(ubi_num);
if (err < ) {
return -;
}
}
#endif
#endif
if (fstab->recs[i].fs_mgr_flags & MF_WAIT) { //当有"wait"关键字时,让系统sleep一会
wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
} if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) { //这个if涉及到system的挂载问题,系统默认是把system挂载到dm-01上,用户不可以remount,使能这个if,用户可以remount,这块会在后面的文章详细介绍
int rc = fs_mgr_setup_verity(&fstab->recs[i]);
if (device_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) {
INFO("Verity disabled");
} else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
ERROR("Could not set up verified partition, skipping!\n");
continue;
}
}
//正常开机mount主要从是下面的代码
int last_idx_inspected;
int top_idx = i; mret = mount_with_alternatives(fstab, i, &last_idx_inspected, &attempted_idx, encryptable); //函数挂载
i = last_idx_inspected;
mount_errno = errno; /* Deal with encryptability. */
if (!mret) {
int status = handle_encryptable(fstab, &fstab->recs[attempted_idx]); //处理加密 if (status == FS_MGR_MNTALL_FAIL) {
/* Fatal error - no point continuing */
return status;
} if (status != FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
if (encryptable != FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
// Log and continue
ERROR("Only one encryptable/encrypted partition supported\n");
}
encryptable = status;
} /* Success! Go get the next one */
continue;
} /* mount(2) returned an error, handle the encryptable/formattable case */
bool wiped = partition_wiped(fstab->recs[top_idx].blk_device);
if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
fs_mgr_is_formattable(&fstab->recs[top_idx]) && wiped) {
/* top_idx and attempted_idx point at the same partition, but sometimes
* at two different lines in the fstab. Use the top one for formatting
* as that is the preferred one.
*/
ERROR("%s(): %s is wiped and %s %s is formattable. Format it.\n", __func__,
fstab->recs[top_idx].blk_device, fstab->recs[top_idx].mount_point,
fstab->recs[top_idx].fs_type);
if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) &&
strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) {
int fd = open(fstab->recs[top_idx].key_loc, O_WRONLY, );
if (fd >= ) {
INFO("%s(): also wipe %s\n", __func__, fstab->recs[top_idx].key_loc);
wipe_block_device(fd, get_file_size(fd));
close(fd);
} else {
ERROR("%s(): %s wouldn't open (%s)\n", __func__,
fstab->recs[top_idx].key_loc, strerror(errno));
}
}
if (fs_mgr_do_format(&fstab->recs[top_idx]) == ) {
/* Let's replay the mount actions. */
i = top_idx - ;
continue;
}
}
if (mret && mount_errno != EBUSY && mount_errno != EACCES &&
fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) {
if (wiped) {
ERROR("%s(): %s is wiped and %s %s is encryptable. Suggest recovery...\n", __func__,
fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
fstab->recs[attempted_idx].fs_type);
encryptable = FS_MGR_MNTALL_DEV_NEEDS_RECOVERY;
continue;
} else {
/* Need to mount a tmpfs at this mountpoint for now, and set
* properties that vold will query later for decrypting
*/
ERROR("%s(): possibly an encryptable blkdev %s for mount %s type %s )\n", __func__,
fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
fstab->recs[attempted_idx].fs_type);
if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < ) {
++error_count;
continue;
}
}
encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
} else {
ERROR("Failed to mount an un-encryptable or wiped partition on"
"%s at %s options: %s error: %s\n",
fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point,
fstab->recs[attempted_idx].fs_options, strerror(mount_errno));
++error_count;
continue;
}
} if (error_count) {
return -;
} else {
return encryptable;
}
}