2023年11月29日发(作者:)
AndroidVold简介(三)
之前两篇主要介绍了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";
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 ";
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;
}
真正挂载的命令是在⽂件中执⾏的,从代码中⼜看到了熟悉的四个⽬录,不错,就是针对不同权限给的四个⽬录;四个⽬录都
会挂载到/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;
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(),
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 ";
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。


发布评论