详谈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文件系统的挂载和卸载问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。