正如Linux的/etc/fstab文件,Android的开机挂载位置在特定的fstab文件中决定。vold的main函数会调用process_config对其进行解析,本例中的fstab文件为板子根目录下的fstab.rk30board文件。
fstab.rk30board
# Android fstab file.
#<src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags>
# The filesystem that contains the filesystem checker binary (typically /system) cannot
# specify MF_CHECK, and must come before any filesystems that do specify MF_CHECK
/dev/block/platform/fe330000.sdhci/by-name/system /system ext4 ro,noatime,nodiratime,noauto_da_alloc wait,resize
# use this line below instead to enable verity
#/dev/block/platform/fe330000.sdhci/by-name/system /system ext4 ro,noatime,nodiratime,noauto_da_alloc wait,check,verify
/dev/block/platform/fe330000.sdhci/by-name/cache /cache ext4 noatime,nodiratime,nosuid,nodev,noauto_da_alloc,discard wait,check
/dev/block/platform/fe330000.sdhci/by-name/metadata /metadata ext4 noatime,nodiratime,nosuid,nodev,noauto_da_alloc,discard wait,check
/dev/block/platform/fe330000.sdhci/by-name/userdata /data f2fs noatime,nodiratime,nosuid,nodev,discard,inline_xattr wait,check,notrim,encryptable=/metadata/key_file
#data for f2fs nobarrier
#/dev/block/platform/fe330000.sdhci/by-name/userdata /data f2fs noatime,nodiratime,nosuid,nodev,discard,inline_xattr,nobarrier wait,check,notrim,encryptable=/metadata/key_file
#data for ext4
#/dev/block/platform/fe330000.sdhci/by-name/userdata /data ext4 noatime,nodiratime,nosuid,nodev,noauto_da_alloc,discard,errors=panic wait,check,encryptable=/metadata/key_file
# sdcard
/devices/platform/fe320000.dwmmc/mmc_host* auto auto defaults voldmanaged=sdcard1:auto,encryptable=userdata
# for usb2.0
/devices/platform/*.usb* auto vfat defaults voldmanaged=usb:auto
# for usb3.0
/devices/platform/usb@*/*.dwc3* auto vfat defaults voldmanaged=usb:auto
/dev/block/zram0 none swap defaults zramsize=533413200
DefaultFstabPath函数返回上述的fstab文件的路径。fs_mgr_read_fstab是解析的主要函数解析结果保存在结构体struct fstab中。先进入fs_mgr_read_fstab看看。
/system/vold/main.cpp
static int process_config(VolumeManager *vm) {
std::string path(android::vold::DefaultFstabPath());
fstab = fs_mgr_read_fstab(path.c_str());
if (!fstab) {
PLOG(ERROR) << "Failed to open default fstab " << path;
return -1;
}
/* Loop through entries looking for ones that vold manages */
bool has_adoptable = false;
for (int i = 0; i < fstab->num_entries; i++) {
if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {
if (fs_mgr_is_nonremovable(&fstab->recs[i])) {
LOG(WARNING) << "nonremovable no longer supported; ignoring volume";
continue;
}
std::string sysPattern(fstab->recs[i].blk_device);
std::string nickname(fstab->recs[i].label);
int flags = 0;
if (fs_mgr_is_encryptable(&fstab->recs[i])) {
flags |= android::vold::Disk::Flags::kAdoptable;
has_adoptable = true;
}
if (fs_mgr_is_noemulatedsd(&fstab->recs[i])
|| property_get_bool("vold.debug.default_primary", false)) {
flags |= android::vold::Disk::Flags::kDefaultPrimary;
}
vm->addDiskSource(std::shared_ptr<VolumeManager::DiskSource>(
new VolumeManager::DiskSource(sysPattern, nickname, flags)));
}
}
property_set("vold.has_adoptable", has_adoptable ? "1" : "0");
return 0;
}
entries的值表示fstab文件中真正的挂载信息行的数量。得到entries的值后便开始初始化struct fstab结构体。struct fstab的num_entries成员初始化为entries的值,fstab_filename初始化为fstab文件路径表示的字符串,recs初始化为元素数量为num_entries的struct fstab_rec数组的指针。
接下来遍历每一个挂载信息行,逐一初始化对应的struct fstab_rec元素。正如fstab文件所示,< src>,< mnt_point>,< type>,< mnt_flags and options>,< fs_mgr_flags>标签的内容分别用来初始化struct fstab_rec的成员变量。blk_device,mount_point,fs_type的初始化比较简单,只是直接的字符串拷贝。flags和fs_mgr_flags成员需要经过parse_flags函数处理对应标签内容后才能决定。
/system/core/fs_mgr/include/fs_mgr.h
struct fstab {
int num_entries;
struct fstab_rec *recs;
char *fstab_filename;
};
/system/core/fs_mgr/include/fs_mgr.h
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;
};
看看parse_flags函数。考虑到不同的struct fstab_rec成员的初始化需要对parse_flags传入不同的参数实现。下面分情况讨论:
先说下flags,fs_options成员的初始化。对于< mnt_flags and options>标签项每一个以逗号为分隔的name,在mount_flags这个struct flag_list结构体数组会有对应的flag值,若没有,说明这个name是文件系统特有的。对于非文件系统特有的name,parse_flags函数会将这些name对应的flags值进行或运算返回。对于文件系统特有的name,处理方法是将它们拷贝到入参fs_options中,以逗号隔开。parse_flags函数返回到fs_mgr_read_fstab函数后,parse_flags函数的返回值用来初始化struct fstab_rec的flags成员,保存在tmp_fs_options的文件系统特有选项用来初始化fs_options成员。
再来说说剩余的其他struct fstab_rec成员的初始化。这次解析的是< fs_mgr_flags>标签项,根据name查找的依据是fs_mgr_flags的struct flag_list结构体数组。跟上段的相同,一方面,< fs_mgr_flags>标签项的的所有逗号分隔的name是用来设置flags的,另一方面,有一些name是用来初始化入参flag_vals的,具体方法见code。parse_flags函数返回到fs_mgr_read_fstab函数后,返回的flags值用来初始化struct fstab_rec的fs_mgr_flags成员。剩下的成员由入参flag_vals的对应值初始化。
/system/core/fs_mgr/fs_mgr_fstab.c
struct flag_list {
const char *name;
unsigned flag;
};
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 },
{ "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 },
{ "defaults", 0 },
{ 0, 0 },
};
/system/core/fs_mgr/fs_mgr_fstab.c
static int parse_flags(char *flags, struct flag_list *fl,
struct fs_mgr_flag_values *flag_vals,
char *fs_options, int fs_options_len)
{
int f = 0;
int i;
char *p;
char *savep;
/* initialize flag values. If we find a relevant flag, we'll
* update the value */
if (flag_vals) {
memset(flag_vals, 0, sizeof(*flag_vals));
flag_vals->partnum = -1;
flag_vals->swap_prio = -1; /* negative means it wasn't specified. */
}
/* initialize fs_options to the null string */
if (fs_options && (fs_options_len > 0)) {
fs_options[0] = '\0';
}
p = strtok_r(flags, ",", &savep);
while (p) {
/* Look for the flag "p" in the flag list "fl"
* If not found, the loop exits with fl[i].name being null.
*/
for (i = 0; fl[i].name; i++) {
if (!strncmp(p, fl[i].name, strlen(fl[i].name))) {
f |= fl[i].flag;
if ((fl[i].flag == MF_CRYPT) && flag_vals) {
/* The encryptable flag is followed by an = and the
* location of the keys. Get it and return it.
*/
flag_vals->key_loc = strdup(strchr(p, '=') + 1);
} else if ((fl[i].flag == MF_VERIFY) && flag_vals) {
/* If the verify flag is followed by an = and the
* location for the verity state, get it and return it.
*/
char *start = strchr(p, '=');
if (start) {
flag_vals->verity_loc = strdup(start + 1);
}
} else if ((fl[i].flag == MF_FORCECRYPT) && flag_vals) {
/* The forceencrypt flag is followed by an = and the
* location of the keys. Get it and return it.
*/
flag_vals->key_loc = strdup(strchr(p, '=') + 1);
} else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
/* The length flag is followed by an = and the
* size of the partition. Get it and return it.
*/
flag_vals->part_length = strtoll(strchr(p, '=') + 1, NULL, 0);
} else if ((fl[i].flag == MF_VOLDMANAGED) && flag_vals) {
/* The voldmanaged flag is followed by an = and the
* label, a colon and the partition number or the
* word "auto", e.g.
* voldmanaged=sdcard:3
* Get and return them.
*/
char *label_start;
char *label_end;
char *part_start;
label_start = strchr(p, '=') + 1;
label_end = strchr(p, ':');
if (label_end) {
flag_vals->label = strndup(label_start,
(int) (label_end - label_start));
part_start = strchr(p, ':') + 1;
if (!strcmp(part_start, "auto")) {
flag_vals->partnum = -1;
} else {
flag_vals->partnum = strtol(part_start, NULL, 0);
}
} else {
ERROR("Warning: voldmanaged= flag malformed\n");
}
} else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) {
flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0);
} else if ((fl[i].flag == MF_ZRAMSIZE) && flag_vals) {
int is_percent = !!strrchr(p, '%');
unsigned int val = strtoll(strchr(p, '=') + 1, NULL, 0);
if (is_percent)
flag_vals->zram_size = calculate_zram_size(val);
else
flag_vals->zram_size = val;
}
break;
}
}
if (!fl[i].name) {
if (fs_options) {
/* It's not a known flag, so it must be a filesystem specific
* option. Add it to fs_options if it was passed in.
*/
strlcat(fs_options, p, fs_options_len);
strlcat(fs_options, ",", fs_options_len);
} else {
/* fs_options was not passed in, so if the flag is unknown
* it's an error.
*/
ERROR("Warning: unknown flag %s\n", p);
}
}
p = strtok_r(NULL, ",", &savep);
}
if (fs_options && fs_options[0]) {
/* remove the last trailing comma from the list of options */
fs_options[strlen(fs_options) - 1] = '\0';
}
return f;
}
/system/core/fs_mgr/fs_mgr_fstab.c
struct fstab *fs_mgr_read_fstab(const char *fstab_path)
{
FILE *fstab_file;
int cnt, entries;
ssize_t len;
size_t alloc_len = 0;
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 0;
}
entries = 0;
while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
/* if the last character is a newline, shorten the string by 1 byte */
if (line[len - 1] == '\n') {
line[len - 1] = '\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(1, sizeof(struct fstab));
fstab->num_entries = entries;
fstab->fstab_filename = strdup(fstab_path);
fstab->recs = calloc(fstab->num_entries, sizeof(struct fstab_rec));
fseek(fstab_file, 0, SEEK_SET);
cnt = 0;
while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
/* if the last character is a newline, shorten the string by 1 byte */
if (line[len - 1] == '\n') {
line[len - 1] = '\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;
}
if (!(p = strtok_r(line, delim, &save_ptr))) {
ERROR("Error parsing mount source\n");
goto err;
}
fstab->recs[cnt].blk_device = strdup(p);
if (!(p = strtok_r(NULL, delim, &save_ptr))) {
ERROR("Error parsing mount_point\n");
goto err;
}
fstab->recs[cnt].mount_point = strdup(p);
if (!(p = strtok_r(NULL, delim, &save_ptr))) {
ERROR("Error parsing fs_type\n");
goto err;
}
fstab->recs[cnt].fs_type = strdup(p);
if (!(p = strtok_r(NULL, delim, &save_ptr))) {
ERROR("Error parsing mount_flags\n");
goto err;
}
tmp_fs_options[0] = '\0';
fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL,
tmp_fs_options, FS_OPTIONS_LEN);
/* fs_options are optional */
if (tmp_fs_options[0]) {
fstab->recs[cnt].fs_options = strdup(tmp_fs_options);
} else {
fstab->recs[cnt].fs_options = NULL;
}
if (!(p = strtok_r(NULL, delim, &save_ptr))) {
ERROR("Error parsing fs_mgr_options\n");
goto err;
}
fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,
&flag_vals, NULL, 0);
fstab->recs[cnt].key_loc = flag_vals.key_loc;
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;
}
回到process_config函数,现在已经得到一个表示挂载情况的struct fstab,接着处理由vold控制的挂载点(带有”voldmanaged=”标志)。根据挂载设备路径,label值(即”voldmanaged”后面’=’和’:’之间的字符串),flag值(由code逻辑得到)构建一个DiskSource对象,并push其指针到VolumeManager的mDiskSources中。vold会从这些DiskSource对象接收kernel传来的uevent事件。