本来想直接分析Android磁盘加密部分的代码的。但是看了下代码,还是先认真看看磁盘挂载部分的代码吧,本文以Google Pixel + 8.1.0作为实例分析。
分区挂载入口:init
init进程是Android的第一个进程,磁盘挂载就是通过它来实现的。init进程主要是通过分析各种.rc脚本来实现对应的动作。Pixel挂载过程使用的.rc文件在device/google/marlin/init.common.rc文件
1 2 3 4 5 6 |
on fs mount_all /vendor/etc/fstab.${ro.hardware} --early on late-fs # Mount RW partitions which need run fsck mount_all /vendor/etc/fstab.${ro.hardware} --late |
可以看到init初始化设备过程中,共调用了两次mount_all过程,使用的都是同一个fstab文件。对于Pixel,对应的文件路径就是/vendor/etc/fstab.sailfish,文件的内容如下,在分析的时候我们使用userdata这一栏作为例子来进行分析:
1 2 3 4 5 6 7 |
#<src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags> /dev/block/platform/soc/624000.ufshc/by-name/system / ext4 ro,barrier=1 wait,slotselect,verify /dev/block/platform/soc/624000.ufshc/by-name/modem /firmware/radio vfat ro,shortname=lower,uid=1000,gid=0,dmask=227,fmask=337,context=u:object_r:firmware_file:s0 wait,slotselect /dev/block/platform/soc/624000.ufshc/by-name/userdata /data ext4 errors=panic,noatime,nosuid,nodev,barrier=1,noauto_da_alloc latemount,wait,check,formattable,fileencryption=ice,quota /dev/block/zram0 none swap defaults zramsize=536870912,max_comp_streams=4 /dev/block/platform/soc/624000.ufshc/by-name/misc /misc emmc defaults defaults /devices/*/xhci-hcd.0.auto/usb* auto vfat defaults voldmanaged=usb:auto |
在init.rc文件中使用mount_all动作,最后是会调用到do_mount_all函数的。所以下面我们就直接从这个函数开始分析。
do_mount_all开始分析
do_mount_all函数定义在system/core/init/builtins.cpp中,源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
/* mount_all <fstab> [ <path> ]* [--<options>]* * * This function might request a reboot, in which case it will * not return. */ static int do_mount_all(const std::vector<std::string>& args) { std::size_t na = 0; bool import_rc = true; bool queue_event = true; int mount_mode = MOUNT_MODE_DEFAULT; // 挂载文件名 const char* fstabfile = args[1].c_str(); std::size_t path_arg_end = args.size(); const char* prop_post_fix = "default"; for (na = args.size() - 1; na > 1; --na) { // 从fstab.sailfish中可以知道,挂载分为early和late if (args[na] == "--early") { path_arg_end = na; queue_event = false; mount_mode = MOUNT_MODE_EARLY; prop_post_fix = "early"; } else if (args[na] == "--late") { // userdata分区走这里 path_arg_end = na; import_rc = false; mount_mode = MOUNT_MODE_LATE; prop_post_fix = "late"; } } std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix; android::base::Timer t; // mount_fstab的主要工作在这里完成 int ret = mount_fstab(fstabfile, mount_mode); property_set(prop_name, std::to_string(t.duration().count())); if (import_rc) { /* Paths of .rc files are specified at the 2nd argument and beyond */ import_late(args, 2, path_arg_end); } if (queue_event) { /* queue_fs_event will queue event based on mount_fstab return code * and return processed return code*/ ret = queue_fs_event(ret); } return ret; } |
do_mount_all –> mount_fstab
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
// 其中方式fstablefile="/vendor/etc/fstab.sailfish" // mount_mode=MOUNT_MODE_EARLY或者MOUNT_MODE_LATE static int mount_fstab(const char* fstabfile, int mount_mode) { int ret = -1; // 这里调用fork创建一个线程来完成mount工作,以防止在mount过程出错导致init进程奔溃 pid_t pid = fork(); if (pid > 0) { // 父进程会等待子进程完成mount工作,并记录返回值 int status; int wp_ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0)); if (wp_ret == -1) { // Unexpected error code. We will continue anyway. PLOG(WARNING) << "waitpid failed"; } if (WIFEXITED(status)) { ret = WEXITSTATUS(status); } else { ret = -1; } } else if (pid == 0) { android::base::ScopedLogSeverity info(android::base::INFO); // 调用fs_mgr_read_fstab,将fstab.sailfish解析填充到fstab结构体 struct fstab* fstab = fs_mgr_read_fstab(fstabfile); // 调用fs_mgr_mount_all执行mount操作 int child_ret = fs_mgr_mount_all(fstab, mount_mode); fs_mgr_free_fstab(fstab); if (child_ret == -1) { PLOG(ERROR) << "fs_mgr_mount_all returned an error"; } _exit(child_ret); } else { /* fork failed, return an error */ return -1; } return ret; } |
过程在上面的注释中已经解析得很清楚了,首先会读取并解析fstab.sailfish文件的内容并填充到fstab结构体中,然后在调用fs_mgr_mount_all进行实际mount
其中对fstab.sailfish文件解析得过程没什么好解释的,主要也就是逐行读取文件内容,然后解析填充结构体就可以了。需要注意的是fstab结构体中的flags字段对应关系,后面有可能会用到,这里我将他们截取出来列在下面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
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 }, }; static struct flag_list fs_mgr_flags[] = { {"wait", MF_WAIT}, {"check", MF_CHECK}, {"encryptable=", MF_CRYPT}, {"forceencrypt=", MF_FORCECRYPT}, {"fileencryption=", MF_FILEENCRYPTION}, {"forcefdeorfbe=", MF_FORCEFDEORFBE}, {"keydirectory=", MF_KEYDIRECTORY}, {"nonremovable", MF_NONREMOVABLE}, {"voldmanaged=", MF_VOLDMANAGED}, {"length=", MF_LENGTH}, {"recoveryonly", MF_RECOVERYONLY}, {"swapprio=", MF_SWAPPRIO}, {"zramsize=", MF_ZRAMSIZE}, {"max_comp_streams=", MF_MAX_COMP_STREAMS}, {"verifyatboot", MF_VERIFYATBOOT}, {"verify", MF_VERIFY}, {"avb", MF_AVB}, {"noemulatedsd", MF_NOEMULATEDSD}, {"notrim", MF_NOTRIM}, {"formattable", MF_FORMATTABLE}, {"slotselect", MF_SLOTSELECT}, {"nofail", MF_NOFAIL}, {"latemount", MF_LATEMOUNT}, {"reservedsize=", MF_RESERVEDSIZE}, {"quota", MF_QUOTA}, {"eraseblk=", MF_ERASEBLKSIZE}, {"logicalblk=", MF_LOGICALBLKSIZE}, {"defaults", 0}, {0, 0}, }; |
接下来继续看另一个函数
mount_fstab –> fs_mgr_mount_all
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
// fstab结构体包含fstab.sailfish文件内容 // mount_mode=MOUNT_MODE_EARLY或者MOUNT_MODE_LATE int fs_mgr_mount_all(struct fstab *fstab, int mount_mode) { int i = 0; int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE; int error_count = 0; int mret = -1; int mount_errno = 0; int attempted_idx = -1; FsManagerAvbUniquePtr avb_handle(nullptr); // fstab解析出错,返回FAIL if (!fstab) { return FS_MGR_MNTALL_FAIL; } // 根据fstab记录的条目数量,逐个遍历执行 for (i = 0; i < fstab->num_entries; i++) { // 如果mount flags中带有"voldmanaged="或者"recoveryonly"就跳过 // 这里对比fstab.sailfish可以知道mount usb的时候是跳过的 if ((fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) || // 如果mount_mode是MOUNT_MODE_LATE,但是flags中没有带latemount也跳过 ((mount_mode == MOUNT_MODE_LATE) && !fs_mgr_is_latemount(&fstab->recs[i])) || // 如果mount_mode是MOUNT_MODE_EARLY,但是flags中带有latemount就跳过 ((mount_mode == MOUNT_MODE_EARLY) && fs_mgr_is_latemount(&fstab->recs[i]))) { 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; } /* Skip mounting the root partition, as it will already have been mounted */ if (!strcmp(fstab->recs[i].mount_point, "/")) { if ((fstab->recs[i].fs_mgr_flags & MS_RDONLY) != 0) { fs_mgr_set_blk_ro(fstab->recs[i].blk_device); } continue; } /* Translate LABEL= file system labels into block devices */ if (is_extfs(fstab->recs[i].fs_type)) { int tret = translate_ext_labels(&fstab->recs[i]); if (tret < 0) { LERROR << "Could not translate label to block device"; continue; } } // flags带有wait,最多等待20秒挂载完成 if (fstab->recs[i].fs_mgr_flags & MF_WAIT && !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) { LERROR << "Skipping '" << fstab->recs[i].blk_device << "' during mount_all"; continue; } if (fstab->recs[i].fs_mgr_flags & MF_AVB) { if (!avb_handle) { avb_handle = FsManagerAvbHandle::Open(*fstab); if (!avb_handle) { LERROR << "Failed to open FsManagerAvbHandle"; return FS_MGR_MNTALL_FAIL; } } if (avb_handle->SetUpAvbHashtree(&fstab->recs[i], true /* wait_for_verity_dev */) == SetUpAvbHashtreeResult::kFail) { LERROR << "Failed to set up AVB on partition: " << fstab->recs[i].mount_point << ", skipping!"; /* Skips mounting the device. */ continue; } // 是否启用verity,emmmm,这篇重点不在这,后面再开一篇分析 } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && is_device_secure()) { int rc = fs_mgr_setup_verity(&fstab->recs[i], true); if (__android_log_is_debuggable() && (rc == FS_MGR_SETUP_VERITY_DISABLED || rc == FS_MGR_SETUP_VERITY_SKIPPED)) { LINFO << "Verity disabled"; } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) { LERROR << "Could not set up verified partition, skipping!"; continue; } } int last_idx_inspected; int top_idx = i; // 从这里开始执行真正的mount mret = mount_with_alternatives(fstab, i, &last_idx_inspected, &attempted_idx); i = last_idx_inspected; mount_errno = errno; // 这后面的是分区加密的内容,之后再讲。 if (!mret) { int status = handle_encryptable(&fstab->recs[attempted_idx]); if (status == FS_MGR_MNTALL_FAIL) { /* Fatal error - no point continuing */ return status; } if (status != FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) { if (encryptable != FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) { // Log and continue LERROR << "Only one encryptable/encrypted partition supported"; } encryptable = status; } /* Success! Go get the next one */ continue; } ................. } |
fs_mgr_mount_all –> mount_with_alternatives
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
static int mount_with_alternatives(struct fstab *fstab, int start_idx, int *end_idx, int *attempted_idx) { int i; int mount_errno = 0; int mounted = 0; if (!end_idx || !attempted_idx || start_idx >= fstab->num_entries) { errno = EINVAL; if (end_idx) *end_idx = start_idx; if (attempted_idx) *attempted_idx = start_idx; return -1; } /* Hunt down an fstab entry for the same mount point that might succeed */ for (i = start_idx; // 确定mount在合适的mount point上 i < fstab->num_entries && !strcmp(fstab->recs[start_idx].mount_point, fstab->recs[i].mount_point); i++) { if (mounted) { LERROR << __FUNCTION__ << "(): skipping fstab dup mountpoint=" << fstab->recs[i].mount_point << " rec[" << i << "].fs_type=" << fstab->recs[i].fs_type << " already mounted as " << fstab->recs[*attempted_idx].fs_type; continue; } // mount前对参数进行预分析 int fs_stat = prepare_fs_for_mount(fstab->recs[i].blk_device, &fstab->recs[i]); if (fs_stat & FS_STAT_EXT4_INVALID_MAGIC) { LERROR << __FUNCTION__ << "(): skipping mount, invalid ext4, mountpoint=" << fstab->recs[i].mount_point << " rec[" << i << "].fs_type=" << fstab->recs[i].fs_type; mount_errno = EINVAL; // continue bootup for FDE continue; } int retry_count = 2; while (retry_count-- > 0) { // __mount最后会调用到底层的mount,进行分区挂载 if (!__mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point, &fstab->recs[i])) { // 从log来看,userdata分区是挂载会成功的,所以走这里 *attempted_idx = i; mounted = 1; if (i != start_idx) { LERROR << __FUNCTION__ << "(): Mounted " << fstab->recs[i].blk_device << " on " << fstab->recs[i].mount_point << " with fs_type=" << fstab->recs[i].fs_type << " instead of " << fstab->recs[start_idx].fs_type; } fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED; mount_errno = 0; break; } else { if (retry_count <= 0) break; // run check_fs only once fs_stat |= FS_STAT_FULL_MOUNT_FAILED; /* back up the first errno for crypto decisions */ if (mount_errno == 0) { mount_errno = errno; } // retry after fsck check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type, fstab->recs[i].mount_point, &fs_stat); } } log_fs_stat(fstab->recs[i].blk_device, fs_stat); } /* Adjust i for the case where it was still withing the recs[] */ if (i < fstab->num_entries) --i; *end_idx = i; if (!mounted) { *attempted_idx = start_idx; errno = mount_errno; return -1; } return 0; } |
最后我们再看看prepare_fs_for_mount函数
mount_with_alternatives –> prepare_fs_for_mount
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
static int prepare_fs_for_mount(const char* blk_device, const struct fstab_rec* rec) { int fs_stat = 0; // 如果是ext类型的分区 if (is_extfs(rec->fs_type)) { // 创建ext4类型的superblock struct ext4_super_block sb; // 读取ext4分区的superblock,保存到sb中 if (read_ext4_superblock(blk_device, &sb, &fs_stat)) { // 看log输出,大意应该是判断分区是否需要recovery if ((sb.s_feature_incompat & EXT4_FEATURE_INCOMPAT_RECOVER) != 0 || (sb.s_state & EXT4_VALID_FS) == 0) { LINFO << "Filesystem on " << blk_device << " was not cleanly shutdown; " << "state flags: 0x" << std::hex << sb.s_state << ", " << "incompat feature flags: 0x" << std::hex << sb.s_feature_incompat; fs_stat |= FS_STAT_UNCLEAN_SHUTDOWN; } // Note: quotas should be enabled before running fsck. tune_quota(blk_device, rec, &sb, &fs_stat); } else { return fs_stat; } } // 是否带检查标志,如果有就执行分区检查 if ((rec->fs_mgr_flags & MF_CHECK) || (fs_stat & (FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED))) { check_fs(blk_device, rec->fs_type, rec->mount_point, &fs_stat); } // 是否带"reservedsize="或者"fileencryption="标志 if (is_extfs(rec->fs_type) && (rec->fs_mgr_flags & (MF_RESERVEDSIZE | MF_FILEENCRYPTION))) { struct ext4_super_block sb; if (read_ext4_superblock(blk_device, &sb, &fs_stat)) { tune_reserved_size(blk_device, rec, &sb, &fs_stat); tune_encrypt(blk_device, rec, &sb, &fs_stat); } } return fs_stat; } |
分区设置的内容大概就到这里了,后面的加密部分,会在下一篇再做分析。
你好,
知道quota的配额限制在哪儿设置的吗?谢谢
文章最后到prepare_fs_for_mount之后会调用tune_quota函数,应该就是设置quota限额的,如果说是具体配额大小怎么设置的话,这个就没有研究过。希望能帮到你