VolumeCmd可以说是最重要的,该类的实现处理了list,debug,mount,unmount,format,unshare,shared等等操作,详细说明:
list: 在启动vold之后,接收的第一条命令就是list,这里是获取系统的所有磁盘对象,一般只有sd卡。
debug: 设置USB的调试模式
mount: 挂载磁盘
unmount: 卸载磁盘
format: 格式化磁盘
unshare: 关闭USB的大容量存储模式,相当于断开手机与电脑的连接
shared: 开启USB的大容量存储模式,相当于手机与电脑连接,并挂载在电脑
下面把该函数的源码贴出来,一目了然,程序员,代码说话:
- intCommandListener::VolumeCmd::runCommand(SocketClient*cli,
- intargc,char**argv){
- dumpArgs(argc,argv,-1);
- if(argc<2){
- cli->sendMsg(ResponseCode::CommandSyntaxError,"MissingArgument",false);
- return0;
- }
- VolumeManager*vm=VolumeManager::Instance();
- intrc=0;
- if(!strcmp(argv[1],"list")){
- returnvm->listVolumes(cli);
- }elseif(!strcmp(argv[1],"debug")){
- if(argc!=3||(argc==3&&(strcmp(argv[2],"off")&&strcmp(argv[2],"on")))){
- cli->sendMsg(ResponseCode::CommandSyntaxError,"Usage:volumedebug<off/on>",false);
- return0;
- }
- vm->setDebug(!strcmp(argv[2],"on")?true:false);
- }elseif(!strcmp(argv[1],"mount")){
- if(argc!=3){
- cli->sendMsg(ResponseCode::CommandSyntaxError,"Usage:volumemount<path>",false);
- return0;
- }
- rc=vm->mountVolume(argv[2]);
- }elseif(!strcmp(argv[1],"unmount")){
- if(argc<3||argc>4||(argc==4&&strcmp(argv[3],"force"))){
- cli->sendMsg(ResponseCode::CommandSyntaxError,"Usage:volumeunmount<path>[force]",false);
- return0;
- }
- boolforce=false;
- if(argc>=4&&!strcmp(argv[3],"force")){
- force=true;
- }
- rc=vm->unmountVolume(argv[2],force);
- }elseif(!strcmp(argv[1],"format")){
- if(argc!=3){
- cli->sendMsg(ResponseCode::CommandSyntaxError,"Usage:volumeformat<path>",false);
- return0;
- }
- rc=vm->formatVolume(argv[2]);
- }elseif(!strcmp(argv[1],"share")){
- if(argc!=4){
- cli->sendMsg(ResponseCode::CommandSyntaxError,
- "Usage:volumeshare<path><method>",false);
- return0;
- }
- rc=vm->shareVolume(argv[2],argv[3]);
- }elseif(!strcmp(argv[1],"unshare")){
- if(argc!=4){
- cli->sendMsg(ResponseCode::CommandSyntaxError,
- "Usage:volumeunshare<path><method>",false);
- return0;
- }
- rc=vm->unshareVolume(argv[2],argv[3]);
- }elseif(!strcmp(argv[1],"shared")){
- boolenabled=false;
- if(argc!=4){
- cli->sendMsg(ResponseCode::CommandSyntaxError,
- "Usage:volumeshared<path><method>",false);
- return0;
- }
- if(vm->shareEnabled(argv[2],argv[3],&enabled)){
- cli->sendMsg(
- ResponseCode::OperationFailed,"Failedtodetermineshareenablestate",true);
- }else{
- cli->sendMsg(ResponseCode::ShareEnabledResult,
- (enabled?"Shareenabled":"Sharedisabled"),false);
- }
- return0;
- }else{
- cli->sendMsg(ResponseCode::CommandSyntaxError,"Unknownvolumecmd",false);
- }
- if(!rc){
- cli->sendMsg(ResponseCode::CommandOkay,"volumeoperationsucceeded",false);
- }else{
- interno=errno;
- rc=ResponseCode::convertFromErrno();
- cli->sendMsg(rc,"volumeoperationfailed",true);
- }
- return0;
- }
每次操作完成后,都要将操作结果汇报给framework,阶级负责工作,一定要把工作结果汇报给领导,这里使用了sendMsg函数。
1.搜索需要挂载的磁盘对象信息列表。先从listVolumes函数开始,贴源码:
- intVolumeManager::listVolumes(SocketClient*cli){
- VolumeCollection::iteratori;
- for(i=mVolumes->begin();i!=mVolumes->end();++i){
- char*buffer;
- asprintf(&buffer,"%s%s%d",
- (*i)->getLabel(),(*i)->getMountpoint(),
- (*i)->getState());
- cli->sendMsg(ResponseCode::VolumeListResult,buffer,false);
- free(buffer);
- }
- cli->sendMsg(ResponseCode::CommandOkay,"Volumeslisted.",false);
- return0;
- }
这函数扫描了mVolumes容器,里面保存着从配置文件/etc/vold.fstab解析出来的磁盘。
该容器是在main函数中添加的:vm->addVolume(dv);
这函数先将每个磁盘的标签,挂载点,状态汇报给framework,然后再循环工作结束后,再次向领导汇报工作结束。
2.设置调试模式。贴源码:
- voidVolumeManager::setDebug(boolenable){
- mDebug=enable;
- VolumeCollection::iteratorit;
- for(it=mVolumes->begin();it!=mVolumes->end();++it){
- (*it)->setDebug(enable);
- }
- }
每个Volume对象都有一个setDebug函数设置调试状态:
- voidVolume::setDebug(boolenable){
- mDebug=enable;
- }
-
3.挂载磁盘。这里都有一个const char *类型的参数,这参数保存着每个磁盘的标签信息,比如sd卡的label是sdcard。
- intVolumeManager::mountVolume(constchar*label){
- Volume*v=lookupVolume(label);
- if(!v){
- errno=ENOENT;
- return-1;
- }
- returnv->mountVol();
- }
lookupVolume函数寻找与label匹配的对象:- Volume*VolumeManager::lookupVolume(constchar*label){
- VolumeCollection::iteratori;
- for(i=mVolumes->begin();i!=mVolumes->end();++i){
- if(label[0]=='/'){
- if(!strcmp(label,(*i)->getMountpoint()))
- return(*i);
- }else{
- if(!strcmp(label,(*i)->getLabel()))
- return(*i);
- }
- }
- returnNULL;
- }
如果找到,直接返回磁盘对象Volume*,挂载操作在mountVol函数里面,该函数内容有点多,贴源码:- intVolume::mountVol(){
- dev_tdeviceNodes[4];
- intn,i,rc=0;
- charerrmsg[255];
- if(getState()==Volume::State_NoMedia){
- snprintf(errmsg,sizeof(errmsg),
- "Volume%s%smountfailed-nomedia",
- getLabel(),getMountpoint());
- mVm->getBroadcaster()->sendBroadcast(
- ResponseCode::VolumeMountFailedNoMedia,
- errmsg,false);
- errno=ENODEV;
- return-1;
- }elseif(getState()!=Volume::State_Idle){
- errno=EBUSY;
- return-1;
- }
- /*判断该挂载点是否已经挂载,若已经挂载,则直接返回。*/
- if(isMountpointMounted(getMountpoint())){
- SLOGW("Volumeisidlebutappearstobemounted-fixing");
- /*这里的setState函数用得很频繁,这函数就是将状态通知给framework*/
- setState(Volume::State_Mounted);
- //mCurrentlyMountedKdev=XXX
- return0;
- }
- /*获取磁盘的设备号与分区数量,在下面说明*/
- n=getDeviceNodes((dev_t*)&deviceNodes,4);
- if(!n){
- SLOGE("Failedtogetdevicenodes(%s)\n",strerror(errno));
- return-1;
- }
- /*将循环挂载n个分区,但从代码上看,只适用于挂载一个分区*/
- for(i=0;i<n;i++){
- chardevicePath[255];
- /*这里看到了吧,用这种方式来使用磁盘的设备节点很方便,直接用主次设备号来命令*/
- sprintf(devicePath,"/dev/block/vold/%d:%d",MAJOR(deviceNodes[i]),
- MINOR(deviceNodes[i]));
- SLOGI("%sbeingconsideredforvolume%s\n",devicePath,getLabel());
- errno=0;
- setState(Volume::State_Checking);
- /*挂载之前先检测一下该分区是否是fat分区,如果不是fat格式,返回-1*/
- if(Fat::check(devicePath)){
- if(errno==ENODATA){
- SLOGW("%sdoesnotcontainaFATfilesystem\n",devicePath);
- continue;
- }
- errno=EIO;
- /*Badness-abortthemount*/
- SLOGE("%sfailedFSchecks(%s)",devicePath,strerror(errno));
- setState(Volume::State_Idle);
- return-1;
- }
- /*
- *Mountthedeviceonourinternalstagingmountpointsowecan
- *muckwithitbeforeexposingittononpriviledgedusers.
- */
- errno=0;
- /*将设备节点挂载在/mnt/secure/staging目录*/
- if(Fat::doMount(devicePath,"/mnt/secure/staging",false,false,1000,1015,0702,true)){
- SLOGE("%sfailedtomountviaVFAT(%s)\n",devicePath,strerror(errno));
- continue;
- }
- SLOGI("Device%s,target%[email protected]/mnt/secure/staging",devicePath,getMountpoint());
- protectFromAutorunStupidity();
- /*挂载一个只有root用户能够访问的目录,这函数挂载了两次
- 将/mnt/secure/staging/.android_secure挂载在/mnt/secure/asec,
- 将tmpfs挂载在/mnt/secure/staging/.android_secure*/
- if(createBindMounts()){
- SLOGE("Failedtocreatebindmounts(%s)",strerror(errno));
- umount("/mnt/secure/staging");
- setState(Volume::State_Idle);
- return-1;
- }
- /*
- *Nowthatthebindmounttrickeryisdone,atomicallymovethe
- *wholesubtreetoexposeittononpriviledgedusers.
- */
- /*将挂载点挂载的目录再挂载到最终的目录/mnt/sdcard*/
- if(doMoveMount("/mnt/secure/staging",getMountpoint(),false)){
- SLOGE("Failedtomovemount(%s)",strerror(errno));
- umount("/mnt/secure/staging");
- setState(Volume::State_Idle);
- return-1;
- }
- setState(Volume::State_Mounted);
- mLastMountedKdev=mCurrentlyMountedKdev=deviceNodes[i];
- return0;
- }
- SLOGE("Volume%sfoundnosuitabledevicesformounting:(\n",getLabel());
- setState(Volume::State_Idle);
- return-1;
- }
这个挂载函数看起来,会发现很繁琐,好几个目录的挂载关系,有以下挂载目录:
/dev/block/vold/8:1 挂载在-> /mnt/secure/staging
/mnt/secure/staging/.android_secure 挂载在-> /mnt/secure/asec
tmpfs 挂载在-> /mnt/secure/staging/.android_secure
/mnt/secure/staging 挂载在-> /mnt/sdcard
从程序的注释看,这样的目的是挂载一个只有root用户能查看的目录,具体还是没搞清楚谷歌为什么要这样挂载,
还是有疑问,希望有清楚的高手指点一下。
sd卡的挂载比较清楚,中间多了一个中介,将设备节点8:1挂载在/mnt/secure/staging,最后又将该目录挂载在/mnt/sdcard,
这目录就是最终用户能够看到文件的目录。
函数里面涉及到几个函数:
getDeviceNodes函数获取挂载设备的设备号与分区数量,是Volume类的一个纯虚函数,在子类DirectVolume中实现,源码:- intDirectVolume::getDeviceNodes(dev_t*devs,intmax){
- if(mPartIdx==-1){
- //Ifthediskhasnopartitions,trythediskitself
- if(!mDiskNumParts){
- devs[0]=MKDEV(mDiskMajor,mDiskMinor);
- return1;
- }
- inti;
- for(i=0;i<mDiskNumParts;i++){
- if(i==max)
- break;
- devs[i]=MKDEV(mDiskMajor,mPartMinors[i]);
- }
- returnmDiskNumParts;
- }
- devs[0]=MKDEV(mDiskMajor,mPartMinors[mPartIdx-1]);
- return1;
- }
下面贴一些mountVol里面挂载的源码:- intFat::doMount(constchar*fsPath,constchar*mountPoint,
- boolro,boolremount,intownerUid,intownerGid,
- intpermMask,boolcreateLost){
- intrc;
- unsignedlongflags;
- charmountData[255];
- flags=MS_NODEV|MS_NOEXEC|MS_NOSUID|MS_DIRSYNC;
- flags|=(ro?MS_RDONLY:0);
- flags|=(remount?MS_REMOUNT:0);
- /*
- *Note:Thisisatemporaryhack.Ifthesamplingprofilerisenabled,
- *wemaketheSDcardworld-writablesoanyprocesscanwritesnapshots.
- *
- *TODO:Removethiscodeoncewehaveadropboxinsystem_server.
- */
- charvalue[PROPERTY_VALUE_MAX];
- property_get("persist.sampling_profiler",value,"");
- if(value[0]=='1'){
- SLOGW("TheSDcardisworld-writablebecausethe"
- "'persist.sampling_profiler'systempropertyissetto'1'.");
- permMask=0;
- }
- sprintf(mountData,
- "utf8,uid=%d,gid=%d,fmask=%o,dmask=%o,shortname=mixed",
- ownerUid,ownerGid,permMask,permMask);
- rc=mount(fsPath,mountPoint,"vfat",flags,mountData);
- if(rc&&errno==EROFS){
- SLOGE("%sappearstobeareadonlyfilesystem-retryingmountRO",fsPath);
- flags|=MS_RDONLY;
- rc=mount(fsPath,mountPoint,"vfat",flags,mountData);
- }
- if(rc==0&&createLost){
- char*lost_path;
- asprintf(&lost_path,"%s/LOST.DIR",mountPoint);
- if(access(lost_path,F_OK)){
- /*
- *CreateaLOST.DIRintherootsowehavesomewheretoput
- *lostclusterchains(fsck_msdosdoesn'tcurrentlydothis)
- */
- if(mkdir(lost_path,0755)){
- SLOGE("UnabletocreateLOST.DIR(%s)",strerror(errno));
- }
- }
- free(lost_path);
- }
- returnrc;
- }
- intVolume::createBindMounts(){
- unsignedlongflags;
- /*
- *Renameold/android_secure->/.android_secure
- */
- if(!access("/mnt/secure/staging/android_secure",R_OK|X_OK)&&
- access(SEC_STG_SECIMGDIR,R_OK|X_OK)){
- if(rename("/mnt/secure/staging/android_secure",SEC_STG_SECIMGDIR)){
- SLOGE("Failedtorenamelegacyasecdir(%s)",strerror(errno));
- }
- }
- /*
- *Ensurethat/android_secureexistsandisadirectory
- */
- if(access(SEC_STG_SECIMGDIR,R_OK|X_OK)){
- if(errno==ENOENT){
- if(mkdir(SEC_STG_SECIMGDIR,0777)){
- SLOGE("Failedtocreate%s(%s)",SEC_STG_SECIMGDIR,strerror(errno));
- return-1;
- }
- }else{
- SLOGE("Failedtoaccess%s(%s)",SEC_STG_SECIMGDIR,strerror(errno));
- return-1;
- }
- }else{
- structstatsbuf;
- if(stat(SEC_STG_SECIMGDIR,&sbuf)){
- SLOGE("Failedtostat%s(%s)",SEC_STG_SECIMGDIR,strerror(errno));
- return-1;
- }
- if(!S_ISDIR(sbuf.st_mode)){
- SLOGE("%sisnotadirectory",SEC_STG_SECIMGDIR);
- errno=ENOTDIR;
- return-1;
- }
- }
- /*
- *Bindmount/mnt/secure/staging/android_secure->/mnt/secure/asecsowe'll
- *havearootonlyaccessablemountpointforit.
- */
- if(mount(SEC_STG_SECIMGDIR,SEC_ASECDIR,"",MS_BIND,NULL)){
- SLOGE("Failedtobindmountpoints%s->%s(%s)",
- SEC_STG_SECIMGDIR,SEC_ASECDIR,strerror(errno));
- return-1;
- }
- /*
- *Mountaread-only,zero-sizedtmpfson<mountpoint>/android_secureto
- *obscuretheunderlyingdirectoryfromeverybody-sneakyeh?;)
- */
- if(mount("tmpfs",SEC_STG_SECIMGDIR,"tmpfs",MS_RDONLY,"size=0,mode=000,uid=0,gid=0")){
- SLOGE("Failedtoobscure%s(%s)",SEC_STG_SECIMGDIR,strerror(errno));
- umount("/mnt/asec_secure");
- return-1;
- }
- return0;
- }
- intVolume::doMoveMount(constchar*src,constchar*dst,boolforce){
- unsignedintflags=MS_MOVE;
- intretries=5;
- while(retries--){
- if(!mount(src,dst,"",flags,NULL)){
- if(mDebug){
- SLOGD("Movedmount%s->%ssucessfully",src,dst);
- }
- return0;
- }elseif(errno!=EBUSY){
- SLOGE("Failedtomovemount%s->%s(%s)",src,dst,strerror(errno));
- return-1;
- }
- intaction=0;
- if(force){
- if(retries==1){
- action=2;//SIGKILL
- }elseif(retries==2){
- action=1;//SIGHUP
- }
- }
- SLOGW("Failedtomove%s->%s(%s,retries%d,action%d)",
- src,dst,strerror(errno),retries,action);
- Process::killProcessesWithOpenFiles(src,action);
- usleep(1000*250);
- }
- errno=EBUSY;
- SLOGE("Givinguponmove%s->%s(%s)",src,dst,strerror(errno));
- return-1;
- }
篇幅有点长了,挂载点也挂载了好几个,mountVol处理的事情最多的,也最繁琐,但都是简单的,主要是有点摸不着头脑,
-
提供了挂载,卸载与格式化的功能。在这里,我们可以回忆一下,Android在挂载sd卡有几个途径,
卸载又是有几种方法?
一、挂载sd卡,有2种方式:
1.用户自己挂载sd卡;
2.自动挂载sd卡,这时候是在Android系统启动完成后挂载的,所以可以看成是自动挂载;
二、卸载sd卡,有2种方式:
1.用户自己卸载sd卡;
2.用户移除sd卡;
卸载sd卡,1和2看起来有点矛盾,但这相当于平时我们在电脑使用U盘一样,
一种是先安全删除再移除U盘;
一种是不安全删除直接移除U盘;
所以在Android系统中,卸载sd卡有两个途径:
1.Framework下发卸载命令(volume unmount sdcard force),卸载sd卡;
2.vold捕获到移除事件,如果是非安全删除sd卡,那么就卸载sd卡。
所以挂载与卸载sd卡的两种方法不同,挂载sd卡都是由Framework下发命令,而卸载sd卡则不同。
一、先来分析Framework下发卸载命令:
截取CommandListener类其中的源码:
这条卸载命令的格式是:volume unmount sdcard true/false.- elseif(!strcmp(argv[1],"unmount")){
- if(argc<3||argc>4||(argc==4&&strcmp(argv[3],"force"))){
- cli->sendMsg(ResponseCode::CommandSyntaxError,"Usage:volumeunmount<path>[force]",false);
- return0;
- }
- boolforce=false;
- if(argc>=4&&!strcmp(argv[3],"force")){
- force=true;
- }
- rc=vm->unmountVolume(argv[2],force);
- }
这里有一个强制卸载的标志,也就是force参数,如果命令包含“force”字符串,那么就强制
卸载sd卡,接下来看unmountVolume卸载函数:- intVolumeManager::unmountVolume(constchar*label,boolforce){
- Volume*v=lookupVolume(label);
- ......
- /*这函数清理了/mnt/asec目录的一些内容,其实看不见*/
- cleanupAsec(v,force);
- returnv->unmountVol(force);
- }
开始看卸载函数,以下函数均做简化,不再贴出大量源码,Android系统的源码实在太多:- intVolume::unmountVol(boolforce){
- /*广播sd卡正在卸载的状态*/
- setState(Volume::State_Unmounting);
- if(doMoveMount(getMountpoint(),SEC_STGDIR,force)){
- SLOGE("Failedtomovemount%s=>%s(%s)",getMountpoint(),SEC_STGDIR,strerror(errno));
- setState(Volume::State_Mounted);
- return-1;
- }
- if(doUnmount(Volume::SEC_STG_SECIMGDIR,force)){
- SLOGE("Failedtounmounttmpfson%s(%s)",SEC_STG_SECIMGDIR,strerror(errno));
- gotofail_republish;
- }
- if(doUnmount(Volume::SEC_ASECDIR,force)){
- SLOGE("Failedtoremovebindmounton%s(%s)",SEC_ASECDIR,strerror(errno));
- gotofail_remount_tmpfs;
- }
- if(doUnmount(Volume::SEC_STGDIR,force)){
- SLOGE("Failedtounmount%s(%s)",SEC_STGDIR,strerror(errno));
- gotofail_recreate_bindmount;
- }
- /*以上doMoveMount,doUnmount等函数做些清理工作,就是之前挂载到系统的目录
- 清理干净*/
- /*卸载工作做完,告诉framework,该磁盘处于空闲状态*/
- setState(Volume::State_Idle);
- /*用来保存正在挂载的设备号的全局变量,现在赋为-1*/
- mCurrentlyMountedKdev=-1;
- return0;
- }
二、vold捕获移除事件,调用卸载函数:
- voidDirectVolume::handlePartitionRemoved(constchar*devpath,NetlinkEvent*evt){
- intmajor=atoi(evt->findParam("MAJOR"));
- intminor=atoi(evt->findParam("MINOR"));
- charmsg[255];
- intstate;
- SLOGD("Volume%s%spartition%d:%dremoved\n",getLabel(),getMountpoint(),major,minor);
- /*
- *Theframeworkdoesn'tneedtogetnotifiedof
- *partitionremovalunlessit'smounted.Otherwise
- *theremovalnotificationwillbesentontheDisk
- *itself
- */
- state=getState();
- if(state!=Volume::State_Mounted&&state!=Volume::State_Shared){
- return;
- }
- /*判断,如果移除事件的设备号与正在挂载系统的设备号相等,
- 说明此时是没有安全删除,所以下面会广播VolumeBadRemoval反馈码*/
- if((dev_t)MKDEV(major,minor)==mCurrentlyMountedKdev){
- /*
- *Yikes,ourmountedpartitionisgoingaway!
- */
- snprintf(msg,sizeof(msg),"Volume%s%sbadremoval(%d:%d)",
- getLabel(),getMountpoint(),major,minor);
- mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,
- msg,false);
- /*清理asec目录*/
- if(mVm->cleanupAsec(this,true)){
- SLOGE("FailedtocleanupASEC-unmountwillprobablyfail!");
- }
- /*在这里调用了卸载函数,跟framework下发命令卸载一样。*/
- if(Volume::unmountVol(true)){
- SLOGE("Failedtounmountvolumeonbadremoval(%s)",
- strerror(errno));
- //XXX:Atthispointwe'rescrewedfornow
- }else{
- SLOGD("Crisisaverted");
- }
- }elseif(state==Volume::State_Shared){
- /*这里的判断是,如果此时手机与电脑正在连接着使用时,移除事件的情况*/
- /*removedduringmassstorage*/
- snprintf(msg,sizeof(msg),"Volume%sbadremoval(%d:%d)",
- getLabel(),major,minor);
- mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,
- msg,false);
- /*卸载大容量存储*/
- if(mVm->unshareVolume(getLabel(),"ums")){
- SLOGE("Failedtounsharevolumeonbadremoval(%s)",
- strerror(errno));
- }else{
- SLOGD("Crisisaverted");
- }
- }
- }
-
操作有这几步:
1.将分区信息写到硬盘的第一个设备节点的MBR结构中的分区表;
2.格式化分区到指定的文件系统类型。
MBR中存放分区表的位置在446-509,占用了64字节,MBR结构只支持4个主分区,所以
有4个16字节的区域,先简要说明一下MBR的分区表的结构:
从这个表格可以看出,相对于446-509的分区表区域,每个主分区的第5个字节存放的是文件
系统标志位,用来识别什么分区,用fdisk工具查看一下,有如下文件系统对应的十六进制标志:
若需要读取这些文件系统标志,只需读取MBR的450个位置,占用一个字节大小。
扯得太远了,回到正题,本文是要分析Android格式化sd卡的功能,在格式化部分,涉及到
了系统的一些函数,与vold无关,简单的说明一下即可。
Android系统在格式化sd卡的时候,首先会判断sd卡是否存在分区,如果sd卡不存在分区,
那么需要重新初始化MBR区域,所以上面简要的介绍了MBR结构中分区表的区域。- intVolume::formatVol(){
- if(getState()==Volume::State_NoMedia){
- errno=ENODEV;
- return-1;
- }elseif(getState()!=Volume::State_Idle){
- errno=EBUSY;
- return-1;
- }
- /*如果该分区为卸载,那么格式化失败,返回错误*/
- if(isMountpointMounted(getMountpoint())){
- SLOGW("Volumeisidlebutappearstobemounted-fixing");
- setState(Volume::State_Mounted);
- //mCurrentlyMountedKdev=XXX
- errno=EBUSY;
- return-1;
- }
- chardevicePath[255];
- dev_tdiskNode=getDiskDevice();
- dev_tpartNode;
- if(mDebug){
- SLOGI("Formattingvolume%s(%s)",getLabel(),devicePath);
- }
- setState(Volume::State_Formatting);
- if(!mLastMountedKdev){
- dev_tdeviceNodes[2];
- intn=getDeviceNodes(deviceNodes,2);
- //initializeMBRifnopartition,orhasmultiplepartitions
- //butnoneisselected
- if((diskNode==deviceNodes[0])||(n>1)){
- sprintf(devicePath,"/dev/block/vold/%d:%d",
- MAJOR(diskNode),MINOR(diskNode));
- if(initializeMbr(devicePath)){
- SLOGE("FailedtoinitializeMBR(%s)",strerror(errno));
- partNode=diskNode;//trytousewholedisk
- }else{
- partNode=MKDEV(MAJOR(diskNode),MINOR(diskNode)+1);
- }
- }else{
- partNode=deviceNodes[0];
- }
- }else{
- partNode=mLastMountedKdev;
- }
- sprintf(devicePath,"/dev/block/vold/%d:%d",
- MAJOR(partNode),MINOR(partNode));
- intret=Fat::format(devicePath,0);
- SLOGE_IF(ret,"Failedtoformat(%s)",strerror(errno));
- setState(Volume::State_Idle);
- returnret;
- }
格式化函数两个主要工作交给了initializeMbr和Fat::format函数:
1.initializeMbr函数负责初始化MBR;
2.Fat::format函数负责格式化分区。
先来看initializeMbr函数的初始化工作:- intVolume::initializeMbr(constchar*deviceNode){
- structdisk_infodinfo;
- memset(&dinfo,0,sizeof(dinfo));
- if(!(dinfo.part_lst=(structpart_info*)malloc(MAX_NUM_PARTS*sizeof(structpart_info)))){
- SLOGE("Failedtomallocprt_lst");
- return-1;
- }
- memset(dinfo.part_lst,0,MAX_NUM_PARTS*sizeof(structpart_info));
- dinfo.device=strdup(deviceNode);
- dinfo.scheme=PART_SCHEME_MBR;
- dinfo.sect_size=512;
- dinfo.skip_lba=2048;
- dinfo.num_lba=0;
- dinfo.num_parts=1;
- structpart_info*pinfo=&dinfo.part_lst[0];
- pinfo->name=strdup("android_sdcard");
- pinfo->flags|=PART_ACTIVE_FLAG;
- pinfo->type=PC_PART_TYPE_FAT32;
- pinfo->len_kb=-1;
- intrc=apply_disk_config(&dinfo,0);
- if(rc){
- SLOGE("Failedtoapplydiskconfiguration(%d)",rc);
- gotoout;
- }
- out:
- free(pinfo->name);
- free(dinfo.device);
- free(dinfo.part_lst);
- returnrc;
- }
这里贴出一些重要的结构体:- structpart_info{
- char*name;
- uint8_tflags;
- uint8_ttype;
- uint32_tlen_kb;/*in1K-bytes*/
- uint32_tstart_lba;/*theLBAwherethispartitionbegins*/
- };
- structdisk_info{
- char*device;
- uint8_tscheme;
- intsect_size;/*expectedsectorsizeinbytes.MUSTBEPOWEROF2*/
- uint32_tskip_lba;/*insectors(1unitofLBA)*/
- uint32_tnum_lba;/*thesizeofthediskinLBAunits*/
- structpart_info*part_lst;
- intnum_parts;
- };
初始化完成后,将该结构体变量pinfo通过apply_disk_config函数进行设置:- intapply_disk_config(structdisk_info*dinfo,inttest)
- {
- intfd;
- structwrite_list*wr_lst=NULL;
- intrv;
- if(validate_and_config(dinfo,&fd,&wr_lst)!=0){
- LOGE("Configurationisinvalid.");
- gotofail;
- }
- if((rv=wlist_commit(fd,wr_lst,test))>=0)
- rv=test?0:sync_ptable(fd);
- close(fd);
- wlist_free(wr_lst);
- returnrv;
- fail:
- close(fd);
- if(wr_lst)
- wlist_free(wr_lst);
- return1;
- }
该函数先打开sd卡的设备节点,然后将MBR的初始化信息写到第一个block(512B)中,
这里涉及到非常多函数,不在vold的讨论范围。
写完MBR后,就要对分区进行格式化,要格式化成FAT32格式,Fat::format函数直接调用
系统命令newfs_msdos来格式化新分区,检测磁盘是,Fat::check函数直接调用系统命令
fsck_msdos来检测分区。
最后格式化完成功,通知Framework,sd卡处于空闲状态:- setState(Volume::State_Idle);
然后,Framework又要重复挂载和卸载的操作了。