详谈android 6.0 fuse文件系统的挂载和卸载问题
android4.4的时候vold,也是利用fuse文件系统达到,将sd卡的目录(storage目录)获取sd实际挂载目录(mnt/media_rw)的权限。但是android4.4的时候vold只是写属性而已,然后init监测这个属性,属性改变时,才会去启动sdcard进程。
然后android6.0直接在vold中,fork一个进程直接开启sdcard进程挂载fuse文件系统。并且在卸载sd的时候,在vold中卸载fuse文件系统。
一、挂载sd卡
下面是解析android6.0vold,挂载sd卡是的一段代码,我们来看下。显示挂载sd卡,然后进行fuse操作。
if(vfat::Mount(mDevPath,mRawPath,false,false,false,//挂载sd卡
AID_MEDIA_RW,AID_MEDIA_RW,0007,true)){
PLOG(ERROR)<
我们再来看看fuse的代码,也就是在sdcard中。先在main函数中获取数据,
intmain(intargc,char**argv){
constchar*source_path=NULL;
constchar*label=NULL;
uid_tuid=0;
gid_tgid=0;
userid_tuserid=0;
boolmulti_user=false;
boolfull_write=false;
inti;
structrlimitrlim;
intfs_version;
intopt;
while((opt=getopt(argc,argv,"u:g:U:mw"))!=-1){
switch(opt){
case'u':
uid=strtoul(optarg,NULL,10);
break;
case'g':
gid=strtoul(optarg,NULL,10);
break;
case'U':
userid=strtoul(optarg,NULL,10);
break;
case'm':
multi_user=true;
break;
case'w':
full_write=true;
break;
case'?':
default:
returnusage();
}
}
for(i=optind;i
其中source_path就是sd卡实际挂载的地址,然后调用run函数,run函数中进行了一些初始化,然后挂载了default,read,write3个fuse文件系统,后面又开启3个线程处理这3个文件系统的read,write,open等处理。
staticvoidrun(constchar*source_path,constchar*label,uid_tuid,
gid_tgid,userid_tuserid,boolmulti_user,boolfull_write){
structfuse_globalglobal;
structfusefuse_default;
structfusefuse_read;
structfusefuse_write;
structfuse_handlerhandler_default;
structfuse_handlerhandler_read;
structfuse_handlerhandler_write;
pthread_tthread_default;
pthread_tthread_read;
pthread_tthread_write;
memset(&global,0,sizeof(global));
memset(&fuse_default,0,sizeof(fuse_default));
memset(&fuse_read,0,sizeof(fuse_read));
memset(&fuse_write,0,sizeof(fuse_write));
memset(&handler_default,0,sizeof(handler_default));
memset(&handler_read,0,sizeof(handler_read));
memset(&handler_write,0,sizeof(handler_write));
pthread_mutex_init(&global.lock,NULL);
global.package_to_appid=hashmapCreate(256,str_hash,str_icase_equals);
global.uid=uid;
global.gid=gid;
global.multi_user=multi_user;
global.next_generation=0;
global.inode_ctr=1;
memset(&global.root,0,sizeof(global.root));
global.root.nid=FUSE_ROOT_ID;/*1*/
global.root.refcount=2;
global.root.namelen=strlen(source_path);
global.root.name=strdup(source_path);
global.root.userid=userid;
global.root.uid=AID_ROOT;
global.root.under_android=false;
strcpy(global.source_path,source_path);
if(multi_user){
global.root.perm=PERM_PRE_ROOT;
snprintf(global.obb_path,sizeof(global.obb_path),"%s/obb",source_path);
}else{
global.root.perm=PERM_ROOT;
snprintf(global.obb_path,sizeof(global.obb_path),"%s/Android/obb",source_path);
}
fuse_default.global=&global;
fuse_read.global=&global;
fuse_write.global=&global;
global.fuse_default=&fuse_default;
global.fuse_read=&fuse_read;
global.fuse_write=&fuse_write;
snprintf(fuse_default.dest_path,PATH_MAX,"/mnt/runtime/default/%s",label);
snprintf(fuse_read.dest_path,PATH_MAX,"/mnt/runtime/read/%s",label);
snprintf(fuse_write.dest_path,PATH_MAX,"/mnt/runtime/write/%s",label);
handler_default.fuse=&fuse_default;
handler_read.fuse=&fuse_read;
handler_write.fuse=&fuse_write;
handler_default.token=0;
handler_read.token=1;
handler_write.token=2;
umask(0);
if(multi_user){
/*Multi-userstorageisfullyisolatedperuser,so"other"
*permissionsarecompletelymaskedoff.*/
if(fuse_setup(&fuse_default,AID_SDCARD_RW,0006)
||fuse_setup(&fuse_read,AID_EVERYBODY,0027)
||fuse_setup(&fuse_write,AID_EVERYBODY,full_write?0007:0027)){
ERROR("failedtofuse_setup\n");
exit(1);
}
}else{
/*Physicalstorageisreadablebyallusersondevice,but
*theAndroiddirectoriesaremaskedofftoasingleuser
*deepinsideattr_from_stat().*/
if(fuse_setup(&fuse_default,AID_SDCARD_RW,0006)
||fuse_setup(&fuse_read,AID_EVERYBODY,full_write?0027:0022)
||fuse_setup(&fuse_write,AID_EVERYBODY,full_write?0007:0022)){
ERROR("failedtofuse_setup\n");
exit(1);
}
}
/*Dropprivs*/
if(setgroups(sizeof(kGroups)/sizeof(kGroups[0]),kGroups)<0){
ERROR("cannotsetgroups:%s\n",strerror(errno));
exit(1);
}
if(setgid(gid)<0){
ERROR("cannotsetgid:%s\n",strerror(errno));
exit(1);
}
if(setuid(uid)<0){
ERROR("cannotsetuid:%s\n",strerror(errno));
exit(1);
}
if(multi_user){
fs_prepare_dir(global.obb_path,0775,uid,gid);
}
if(pthread_create(&thread_default,NULL,start_handler,&handler_default)
||pthread_create(&thread_read,NULL,start_handler,&handler_read)
||pthread_create(&thread_write,NULL,start_handler,&handler_write)){
ERROR("failedtopthread_create\n");
exit(1);
}
watch_package_list(&global);
ERROR("terminatedprematurely\n");
exit(1);
}
其中在fuse_setup中挂载了fuse文件系统
staticintfuse_setup(structfuse*fuse,gid_tgid,mode_tmask){
charopts[256];
fuse->fd=open("/dev/fuse",O_RDWR);
if(fuse->fd==-1){
ERROR("failedtoopenfusedevice:%s\n",strerror(errno));
return-1;
}
umount2(fuse->dest_path,MNT_DETACH);
snprintf(opts,sizeof(opts),
"fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d",
fuse->fd,fuse->global->uid,fuse->global->gid);
if(mount("/dev/fuse",fuse->dest_path,"fuse",MS_NOSUID|MS_NODEV|MS_NOEXEC|
MS_NOATIME,opts)!=0){
ERROR("failedtomountfusefilesystem:%s\n",strerror(errno));
return-1;
}
fuse->gid=gid;
fuse->mask=mask;
return0;
}
但是虽然挂载了default,read,write3个fuse文件系统。
但好像只有default有用,因为在init.rc中将default目录直接挂载到了storage,而应用也就只能拿到storage目录,所以只有default的fuse实际有用。
onpost-fs
startlogd
#addforamt
chmod0755/amt
#onceeverythingissetup,noneedtomodify/
mountrootfsrootfs/roremount
#Mountsharedsochangespropagateintochildnamespaces
mountrootfsrootfs/sharedrec
#Mountdefaultstorageintorootnamespace
mountnone/mnt/runtime/default/storageslavebindrec
二、sd卡卸载过程
然后我们再来看看android卸载sd卡的过程,卸载sd卡的时候,是先卸载了fuse文件系统,然后在卸载了sd卡的mount地址。
status_tPublicVolume::doUnmount(){
if(mFusePid>0){
kill(mFusePid,SIGTERM);
TEMP_FAILURE_RETRY(waitpid(mFusePid,nullptr,0));
mFusePid=0;
}
ForceUnmount(kAsecPath);
ForceUnmount(mFuseDefault);
ForceUnmount(mFuseRead);
ForceUnmount(mFuseWrite);
ForceUnmount(mRawPath);
rmdir(mFuseDefault.c_str());
rmdir(mFuseRead.c_str());
rmdir(mFuseWrite.c_str());
rmdir(mRawPath.c_str());
mFuseDefault.clear();
mFuseRead.clear();
mFuseWrite.clear();
mRawPath.clear();
returnOK;
}
总所周知,卸载sd卡mount地址的时候,会去检查哪些进程在使用sd卡中的文件。
如何检查呢?是通过proc/pid下面各个文件的软链接,然后通过readlink找到真正的文件地址,来判定是否正在占用sd卡中的文件。
但是在卸载fuse文件系统的时候,比如你有进程在操作sd卡中的文件,这个时候操作sd卡的storage目录会fuse到sd卡真正的挂载地址上,实际上fuse文件系统是在工作的,导致不能卸载。
但是这个时候去查找谁占用fuse文件又是查不出来的,因为是进程在操作sd卡文件,会导致fuse文件系统的操作,才会卸载不掉fuse文件系统。但是能找到占用的文件只能是sd卡的。
而且实际中也碰到这样的问题,所以个人认为应该先kill正在使用sd卡的进程,然后再卸载fuse文件系统。这样就不会有进程操作sd卡中的文件的时候,导致fuse文件系统也在忙而卸载不掉了。我碰到的问题是:一个如下进程占用的sd卡文件
root@lte26007:/proc/2365/fd#ls-l
lrwx------rootradio2016-05-2513:420->/dev/null
lrwx------rootradio2016-05-2513:421->/dev/null
lrwx------rootradio2016-05-2513:4210->/storage/2C10-0CCC/elog/elog_20160525_134206/PHY0/test_20160525_134208_00.bin_last_0
lrwx------rootradio2016-05-2513:4211->/storage/2C10-0CCC/elog/elog_20160525_134206/PHY0/log_up_data.dat
lrwx------rootradio2016-05-2513:4212->/storage/2C10-0CCC/elog/elog_20160525_134206/PHY1/test_20160525_134209_head.bin
lrwx------rootradio2016-05-2513:4213->/storage/2C10-0CCC/elog/elog_20160525_134206/PHY1/test_20160525_134209_00.bin_last_0
lrwx------rootradio2016-05-2513:4214->/storage/2C10-0CCC/elog/elog_20160525_134206/PHY1/log_up_data.dat
lrwx------rootradio2016-05-2513:4215->/storage/2C10-0CCC/elog/elog_20160525_134206/PFM/test_20160525_134209_head.bin
lrwx------rootradio2016-05-2513:4216->/storage/2C10-0CCC/elog/elog_20160525_134206/PFM/test_20160525_134209_00.bin_last_0
lrwx------rootradio2016-05-2513:4217->/storage/2C10-0CCC/elog/elog_20160525_134206/PFM/log_up_data.dat
lrwx------rootradio2016-05-2513:4218->/dev/lmi10
lrwx------rootradio2016-05-2513:4219->/dev/TPC0
lrwx------rootradio2016-05-2513:422->/dev/null
lrwx------rootradio2016-05-2513:4220->/dev/modem
lrwx------rootradio2016-05-2513:4221->/dev/TPC1
lrwx------rootradio2016-05-2513:4222->/dev/modem
lrwx------rootradio2016-05-2513:4223->/dev/lmi9
lrwx------rootradio2016-05-2513:4224->socket:[14761]
lrwx------rootradio2016-05-2513:4226->socket:[14764]
lrwx------rootradio2016-05-2513:423->socket:[15482]
lrwx------rootradio2016-05-2513:424->/tmp/lc-elog.pid
lrwx------rootradio2016-05-2513:425->/storage/2C10-0CCC/elog/elog_20160525_134206/HLS/test_20160525_134208_head.bin
lrwx------rootradio2016-05-2513:426->/storage/2C10-0CCC/elog/elog_20160525_134206/HLS/test_20160525_134208_00.bin_last_0
lrwx------rootradio2016-05-2513:427->/storage/2C10-0CCC/elog/elog_20160525_134206/HLS/log_up_data.dat
lrwx------rootradio2016-05-2513:428->/storage/2C10-0CCC/elog/elog_20160525_134206/PHY0/test_20160525_134208_head.bin
lr-x------rootradio2016-05-2513:429->/dev/__properties__
root@lte26007:/proc/2365/fd
至于如何kill正在使用sd卡的进程呢:
status_tPublicVolume::doUnmount(){
if(mFusePid>0){
kill(mFusePid,SIGTERM);
TEMP_FAILURE_RETRY(waitpid(mFusePid,nullptr,0));
mFusePid=0;
}
ForceUnmount(kAsecPath);
LOG(VERBOSE)<<"start";
KillProcessesUsingPath(getPath());
LOG(VERBOSE)<<"end";
ForceUnmount(mFuseDefault);
ForceUnmount(mFuseRead);
ForceUnmount(mFuseWrite);
ForceUnmount(mRawPath);
rmdir(mFuseDefault.c_str());
rmdir(mFuseRead.c_str());
rmdir(mFuseWrite.c_str());
rmdir(mRawPath.c_str());
mFuseDefault.clear();
mFuseRead.clear();
mFuseWrite.clear();
mRawPath.clear();
returnOK;
}
可以在卸载fuse文件系统之前,调用KillProcessesUsingPath,来kill那些正在使用sd卡目录的进程。而这个mPath路径,如果是sd卡的话,它是storage下的目录,而不是sd卡的mount地址。如果是otg插的sd卡的话,是sd卡的mount地址,因为otg在storage目录下没有目录,只有一个mount地址访问,也有没有fuse。这样问题就解决了。
以上这篇详谈android6.0fuse文件系统的挂载和卸载问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。