之前两篇主要介绍了Vold的架构以及运行机制,本篇主要来介绍下Vold是内置存储和外置存储的mount流程。前面已经介绍过,无论什么存储,最终都会调用doMount()这个虚函数,对于不同类型的则会有不同的mount流程(即挂在到不同的文件系统下);对于内置存储,使用EmulatedVolume类来处理,而外接sd卡或者OTG设备则基本上都使用PublicVolume类来处理,因此uevent传递上来的消息会通知到vold到底这个存储介质属于什么类型。
首先来看下EmulatedVolume处理流程,首先会判断Primary标志,表明是内置主要的存储,一般都会跑进这个逻辑,因为只有data分区下的存储才会走这边;接着创建/mnt/runtime/default/emulated等四个目录,这个是Google针对sdcardfs添加的用来处理app之间相互的访问权限。拥有不同权限的app会bind mount到对应的目录,例如只有读权限的app则会bind到/mnt/runtime/read/emulated。
status_t EmulatedVolume::doMount() {
// We could have migrated storage to an adopted private volume, so always
// call primary storage "emulated" to avoid media rescans.
std::string label = mLabel; //存储介质的卷名
if (getMountFlags() & MountFlags::kPrimary) {
label = "emulated";
}
mFuseDefault = StringPrintf("/mnt/runtime/default/%s", label.c_str()); //默认权限,一般是只读权限;
mFuseRead = StringPrintf("/mnt/runtime/read/%s", label.c_str()); //读权限
mFuseWrite = StringPrintf("/mnt/runtime/write/%s", label.c_str()); //写权限
mFuseFull = StringPrintf("/mnt/runtime/full/%s", label.c_str()); //所有权限
setInternalPath(mRawPath);
setPath(StringPrintf("/storage/%s", label.c_str()));
//创建对应的四个目录
if (fs_prepare_dir(mFuseDefault.c_str(), 0700, AID_ROOT, AID_ROOT) ||
fs_prepare_dir(mFuseRead.c_str(), 0700, AID_ROOT, AID_ROOT) ||
fs_prepare_dir(mFuseWrite.c_str(), 0700, AID_ROOT, AID_ROOT) ||
fs_prepare_dir(mFuseFull.c_str(), 0700, AID_ROOT, AID_ROOT)) {
PLOG(ERROR) << getId() << " failed to create mount points";
return -errno;
}
dev_t before = GetDevice(mFuseFull);
//创建子进程,启动sdcard进程来处理具体的挂在流程;
if (!(mFusePid = fork())) {
// clang-format off
if (execl(kFusePath, kFusePath, //执行sdcard进程
"-u", "1023", // AID_MEDIA_RW 挂载使用的uid
"-g", "1023", // AID_MEDIA_RW 挂载使用的gid
"-m",
"-w",
"-G",
"-i",
"-o",
mRawPath.c_str(),
label.c_str(),
NULL)) {
// clang-format on
PLOG(ERROR) << "Failed to exec";
}
LOG(ERROR) << "FUSE exiting";
_exit(1);
}
if (mFusePid == -1) {
PLOG(ERROR) << getId() << " failed to fork";
return -errno;
}
nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME);
while (before == GetDevice(mFuseFull)) {
LOG(DEBUG) << "Waiting for FUSE to spin up...";
usleep(50000); // 50ms
nsecs_t now = systemTime(SYSTEM_TIME_BOOTTIME);
if (nanoseconds_to_milliseconds(now - start) > 5000) {
LOG(WARNING) << "Timed out while waiting for FUSE to spin up";
return -ETIMEDOUT;
}
}
/* sdcardfs will have exited already. FUSE will still be running */
TEMP_FAILURE_RETRY(waitpid(mFusePid, nullptr, 0));
mFusePid = 0;
return OK;
}
真正挂载的命令是在sdcard.cpp文件中执行的,从代码中又看到了熟悉的四个目录,不错,就是针对不同权限给的四个目录;四个目录都会挂载到/storage/emulated/目录下,区别就在于挂载的时候给的参数不同,包括传递的属性掩码以及用户组,只有default是传递的1023,其他都是9997(AID_EVERYBODY)。这都是挂载到了sdcardfs下,如果有esdfs则会挂载到对应的文件系统,一般没有这个标志,也就都使用了sdcardfs。
static void run_sdcardfs(const std::string& source_path, const std::string& label, uid_t uid,
gid_t gid, userid_t userid, bool multi_user, bool full_write,
bool derive_gid, bool default_normal, bool unshared_obb, bool use_esdfs) {
std::string dest_path_default = "/mnt/runtime/default/" + label;
std::string dest_path_read = "/mnt/runtime/read/" + label;
std::string dest_path_write = "/mnt/runtime/write/" + label;
std::string dest_path_full = "/mnt/runtime/full/" + label;
umask(0);
if (multi_user) {
// Multi-user storage is fully isolated per user, so "other"
// permissions are completely masked off.
if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
AID_SDCARD_RW, 0006, derive_gid, default_normal, unshared_obb,
use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,
multi_user, userid, AID_EVERYBODY, 0027, derive_gid,
default_normal, unshared_obb, use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,
multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0027,
derive_gid, default_normal, unshared_obb, use_esdfs) ||
!sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,
multi_user, userid, AID_EVERYBODY, 0007, derive_gid,
default_normal, unshared_obb, use_esdfs)) {
LOG(FATAL) << "failed to sdcardfs_setup";
}
} else {
...
}
}
// Will abort if priv-dropping fails.
drop_privs(uid, gid);
if (multi_user) {
std::string obb_path = source_path + "/obb";
fs_prepare_dir(obb_path.c_str(), 0775, uid, gid);
}
exit(0);
}
static bool sdcardfs_setup(const std::string& source_path, const std::string& dest_path,
uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid, gid_t gid,
mode_t mask, bool derive_gid, bool default_normal, bool unshared_obb,
bool use_esdfs) {
// Add new options at the end of the vector.
std::vector<std::string> new_opts_list;
if (multi_user) new_opts_list.push_back("multiuser,");
if (derive_gid) new_opts_list.push_back("derive_gid,");
if (default_normal) new_opts_list.push_back("default_normal,");
if (unshared_obb) new_opts_list.push_back("unshared_obb,");
// Try several attempts, each time with one less option, to gracefully
// handle older kernels that aren't updated yet.
for (int i = 0; i <= new_opts_list.size(); ++i) {
std::string new_opts;
for (int j = 0; j < new_opts_list.size() - i; ++j) {
new_opts += new_opts_list[j];
}
auto opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d",
fsuid, fsgid, new_opts.c_str(), mask, userid, gid);
if (mount(source_path.c_str(), dest_path.c_str(), use_esdfs ? "esdfs" : "sdcardfs",
MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()) == -1) {
PLOG(WARNING) << "Failed to mount sdcardfs with options " << opts;
} else {
return true;
}
}
return false;
}
sdcardfs并不是真正传统的文件系统,他只是Google为了控制访问权限在原本文件系统上增加的一个保护壳,真正的文件系统还是得看data分区是使用哪一个文件系统,一般是ext4或者f2fs。这样的话内置存储就有两个文件系统支持,文件的操作还是在底层文件系统中完成,sdcardfs用来做进一步的检查和控制。
第二个来看一下PublicVolume这个类型的挂载流程,和EmulatedVolume挂载类似,但是挂载不再只使用sdcard,而是先将整个存储介质挂载到storage下,有vfat和exfat两种类型,当然如果有其他文件系统也可以增加,例如ntfs等。这个相当于优先挂载底层文件系统,如果有需要则再挂载到sdcardfs中,而这个条件则取决于是否对外可见,由变量kVisible来判断。
status_t PublicVolume::doMount() {
readMetadata();
if (mFsType == "vfat" && vfat::IsSupported()) {
if (vfat::Check(mDevPath)) {
LOG(ERROR) << getId() << " failed filesystem check";
return -EIO;
}
} else if (mFsType == "exfat" && exfat::IsSupported()) {
if (exfat::Check(mDevPath)) {
LOG(ERROR) << getId() << " failed filesystem check";
return -EIO;
}
} else {
LOG(ERROR) << getId() << " unsupported filesystem " << mFsType;
return -EIO;
}
// Use UUID as stable name, if available
std::string stableName = getId();
if (!mFsUuid.empty()) {
stableName = mFsUuid;
}
mRawPath = StringPrintf("/mnt/media_rw/%s", stableName.c_str());
mFuseDefault = StringPrintf("/mnt/runtime/default/%s", stableName.c_str());
mFuseRead = StringPrintf("/mnt/runtime/read/%s", stableName.c_str());
mFuseWrite = StringPrintf("/mnt/runtime/write/%s", stableName.c_str());
mFuseFull = StringPrintf("/mnt/runtime/full/%s", stableName.c_str());
setInternalPath(mRawPath);
if (getMountFlags() & MountFlags::kVisible) {
setPath(StringPrintf("/storage/%s", stableName.c_str()));
} else {
setPath(mRawPath);
}
if (fs_prepare_dir(mRawPath.c_str(), 0700, AID_ROOT, AID_ROOT)) {
PLOG(ERROR) << getId() << " failed to create mount points";
return -errno;
}
if (mFsType == "vfat") {
if (vfat::Mount(mDevPath, mRawPath, false, false, false, AID_MEDIA_RW, AID_MEDIA_RW, 0007,
true)) {
//mount(mDevPath, mRawPath, "vfat", flags, mountData.c_str());
PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
return -EIO;
}
} else if (mFsType == "exfat") {
if (exfat::Mount(mDevPath, mRawPath, AID_MEDIA_RW, AID_MEDIA_RW, 0007)) {
//mount(mDevPath, mRawPath, "exfat", flags, mountData.c_str());
PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
return -EIO;
}
}
if (getMountFlags() & MountFlags::kPrimary) {
initAsecStage();
}
if (!(getMountFlags() & MountFlags::kVisible)) {
// Not visible to apps, so no need to spin up FUSE
return OK;
}
if (fs_prepare_dir(mFuseDefault.c_str(), 0700, AID_ROOT, AID_ROOT) ||
fs_prepare_dir(mFuseRead.c_str(), 0700, AID_ROOT, AID_ROOT) ||
fs_prepare_dir(mFuseWrite.c_str(), 0700, AID_ROOT, AID_ROOT) ||
fs_prepare_dir(mFuseFull.c_str(), 0700, AID_ROOT, AID_ROOT)) {
PLOG(ERROR) << getId() << " failed to create FUSE mount points";
return -errno;
}
dev_t before = GetDevice(mFuseFull);
if (!(mFusePid = fork())) {
if (getMountFlags() & MountFlags::kPrimary) {
// clang-format off
if (execl(kFusePath, kFusePath,
"-u", "1023", // AID_MEDIA_RW
"-g", "1023", // AID_MEDIA_RW
"-U", std::to_string(getMountUserId()).c_str(),
"-w",
mRawPath.c_str(),
stableName.c_str(),
NULL)) {
// clang-format on
PLOG(ERROR) << "Failed to exec";
}
} else {
// clang-format off
if (execl(kFusePath, kFusePath,
"-u", "1023", // AID_MEDIA_RW
"-g", "1023", // AID_MEDIA_RW
"-U", std::to_string(getMountUserId()).c_str(),
mRawPath.c_str(),
stableName.c_str(),
NULL)) {
// clang-format on
PLOG(ERROR) << "Failed to exec";
}
}
LOG(ERROR) << "FUSE exiting";
_exit(1);
}
if (mFusePid == -1) {
PLOG(ERROR) << getId() << " failed to fork";
return -errno;
}
nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME);
while (before == GetDevice(mFuseFull)) {
LOG(DEBUG) << "Waiting for FUSE to spin up...";
usleep(50000); // 50ms
nsecs_t now = systemTime(SYSTEM_TIME_BOOTTIME);
if (nanoseconds_to_milliseconds(now - start) > 5000) {
LOG(WARNING) << "Timed out while waiting for FUSE to spin up";
return -ETIMEDOUT;
}
}
/* sdcardfs will have exited already. FUSE will still be running */
TEMP_FAILURE_RETRY(waitpid(mFusePid, nullptr, 0));
mFusePid = 0;
return OK;
}
以上就是vold处理存储介质挂载的流程,具体的mount流程处于kernel部分,不同的文件系统有不同的特性,mount流程也会不一样,通过系统调用由VFS来分配是由哪个文件系统来处理。
本片代码针对的是Android Q,在R版本Google已经强制开始使用Fuse,并且从kernel中取出sdcardfs,所以vold挂载时使用Fuse。