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";
}
mFuDefault =StringPrintf("/mnt/runtime/default/%s", label.c_str());//默认权限,⼀般是只读权限;
mFuRead =StringPrintf("/mnt/runtime/read/%s", label.c_str());//读权限
mFuWrite =StringPrintf("/mnt/runtime/write/%s", label.c_str());//写权限
mFuFull =StringPrintf("/mnt/runtime/full/%s", label.c_str());//所有权限
tInternalPath(mRawPath);
tPath(StringPrintf("/storage/%s", label.c_str()));
//创建对应的四个⽬录
if(fs_prepare_dir(mFuDefault.c_str(),0700, AID_ROOT, AID_ROOT)||
fs_prepare_dir(mFuRead.c_str(),0700, AID_ROOT, AID_ROOT)||
fs_prepare_dir(mFuWrite.c_str(),0700, AID_ROOT, AID_ROOT)||
fs_prepare_dir(mFuFull.c_str(),0700, AID_ROOT, AID_ROOT)){
PLOG(ERROR)<<getId()<<" failed to create mount points";
return-errno;
}
dev_t before =GetDevice(mFuFull);
//创建⼦进程,启动sdcard进程来处理具体的挂在流程;
if(!(mFuPid =fork())){chess怎么读
// clang-format off
if(execl(kFuPath, kFuPath,//执⾏sdcard进程
"-u","1023",// AID_MEDIA_RW 挂载使⽤的uid
"-g","1023",// AID_MEDIA_RW 挂载使⽤的gid
"-m",
"-w",
"-G",
bungalow"-i",
"-o",
mRawPath.c_str(),
学韩语视频label.c_str(),
NULL)){
// clang-format on
PLOG(ERROR)<<"Failed to exec";
}
LOG(ERROR)<<"FUSE exiting";
_exit(1);
}
if(mFuPid ==-1){
PLOG(ERROR)<<getId()<<" failed to fork";
return-errno;
}
ncs_t start =systemTime(SYSTEM_TIME_BOOTTIME);
while(before ==GetDevice(mFuFull)){
LOG(DEBUG)<<"Waiting for FUSE to ";
usleep(50000);// 50ms
ncs_t now =systemTime(SYSTEM_TIME_BOOTTIME);
if(nanoconds_to_milliconds(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(mFuPid,nullptr,0));
mFuPid =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, urid_t urid,bool multi_ur,bool full_write,
bool derive_gid,bool default_normal,bool unshared_obb,bool u_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_ur){
// Multi-ur storage is fully isolated per ur, so "other"
// permissions are completely masked off.
if(!sdcardfs_tup(source_path, dest_path_default, uid, gid, multi_ur, urid,
AID_SDCARD_RW,0006, derive_gid, default_normal, unshared_obb,
u_esdfs)||
!sdcardfs_tup_condary(dest_path_default, source_path, dest_path_read, uid, gid,
multi_ur, urid, AID_EVERYBODY,0027, derive_gid,
default_normal, unshared_obb, u_esdfs)||
!
sdcardfs_tup_condary(dest_path_default, source_path, dest_path_write, uid, gid,
multi_ur, urid, AID_EVERYBODY, full_write ?0007:0027,
derive_gid, default_normal, unshared_obb, u_esdfs)||
!sdcardfs_tup_condary(dest_path_default, source_path, dest_path_full, uid, gid,
multi_ur, urid, AID_EVERYBODY,0007, derive_gid,
default_normal, unshared_obb, u_esdfs)){
LOG(FATAL)<<"failed to sdcardfs_tup";
}
}el{
...
}
}
// Will abort if priv-dropping fails.
drop_privs(uid, gid);
if(multi_ur){
std::string obb_path = source_path +"/obb";
fs_prepare_dir(obb_path.c_str(),0775, uid, gid);
}
exit(0);
}
static bool sdcardfs_tup(const std::string& source_path,const std::string& dest_path,
uid_t fsuid, gid_t fsgid,bool multi_ur, urid_t urid, gid_t gid,
mode_t mask,bool derive_gid,bool default_normal,bool unshared_obb,
bool u_esdfs){
// Add new options at the end of the vector.
std::vector<std::string> new_opts_list;
if(multi_ur) new_opts_list.push_back("multiur,");
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 veral 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::ba::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,urid=%d,gid=%d",
fsuid, fsgid, new_opts.c_str(), mask, urid, gid);
if(mount(source_path.c_str(), dest_path.c_str(), u_esdfs ?"esdfs":"sdcardfs",
MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str())==-1){
PLOG(WARNING)<<"Failed to mount sdcardfs with options "<< opts;
}el{
return true;
}
}
return fal;
}
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;
}
}el if(mFsType =="exfat"&& exfat::IsSupported()){
if(exfat::Check(mDevPath)){
LOG(ERROR)<<getId()<<" failed filesystem check";
return-EIO;
}
}el{
LOG(ERROR)<<getId()<<" unsupported filesystem "<< mFsType;
return-EIO;
}
// U UUID as stable name, if available
std::string stableName =getId();
if(!pty()){
}
mRawPath =StringPrintf("/mnt/media_rw/%s", stableName.c_str());
mFuDefault =StringPrintf("/mnt/runtime/default/%s", stableName.c_str());
mFuRead =StringPrintf("/mnt/runtime/read/%s", stableName.c_str());
mFuWrite =StringPrintf("/mnt/runtime/write/%s", stableName.c_str());
mFuFull =StringPrintf("/mnt/runtime/full/%s", stableName.c_str());
tInternalPath(mRawPath);
if(getMountFlags()& MountFlags::kVisible){
tPath(StringPrintf("/storage/%s", stableName.c_str()));
}el{
tPath(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,fal,fal,fal, 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;
}
}el 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){
usd是什么initAcStage();
}
if(!(getMountFlags()& MountFlags::kVisible)){
// Not visible to apps, so no need to spin up FUSE
return OK;
}
if(fs_prepare_dir(mFuDefault.c_str(),0700, AID_ROOT, AID_ROOT)||
fs_prepare_dir(mFuRead.c_str(),0700, AID_ROOT, AID_ROOT)||
fs_prepare_dir(mFuWrite.c_str(),0700, AID_ROOT, AID_ROOT)||
fs_prepare_dir(mFuFull.c_str(),0700, AID_ROOT, AID_ROOT)){
PLOG(ERROR)<<getId()<<" failed to create FUSE mount points";
return-errno;
}
dev_t before =GetDevice(mFuFull);
if(!(mFuPid =fork())){
if(getMountFlags()& MountFlags::kPrimary){
// clang-format off
if(execl(kFuPath, kFuPath,
"-u","1023",// AID_MEDIA_RW
proceeding
"-g","1023",// AID_MEDIA_RW
"-U", std::to_string(getMountUrId()).c_str(),
"-w",
mRawPath.c_str(),
NULL)){
// clang-format on
如何快速学习韩语PLOG(ERROR)<<"Failed to exec";
}
}el{
// clang-format off
if(execl(kFuPath, kFuPath,
"-u","1023",// AID_MEDIA_RW
英文书名"-g","1023",// AID_MEDIA_RW
"-U", std::to_string(getMountUrId()).c_str(),
mRawPath.c_str(),
stableName.c_str(),
NULL)){
// clang-format on
bally是什么意思>areyoukiddingmePLOG(ERROR)<<"Failed to exec";
}
}
LOG(ERROR)<<"FUSE exiting";
_exit(1);
}
if(mFuPid ==-1){
PLOG(ERROR)<<getId()<<" failed to fork";
return-errno;
}
ncs_t start =systemTime(SYSTEM_TIME_BOOTTIME);
while(before ==GetDevice(mFuFull)){
LOG(DEBUG)<<"Waiting for FUSE to ";
usleep(50000);// 50ms
ncs_t now =systemTime(SYSTEM_TIME_BOOTTIME);
if(nanoconds_to_milliconds(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(mFuPid,nullptr,0));
mFuPid =0;
return OK;
}
以上就是vold处理存储介质挂载的流程,具体的mount流程处于kernel部分,不同的⽂件系统有不同的特性,mount流程也会不⼀样,通过系统调⽤由VFS来分配是由哪个⽂件系统来处理。
本⽚代码针对的是Android Q,在R版本Google已经强制开始使⽤Fu,并且从kernel中取出sdcardfs,所以vold挂载时使⽤Fu。