只讲VolumeCmd类的runCommand函数的实现。
VolumeCmd可以说是最重要的,该类的实现处理了list,debug,mount,unmount,format,unshare,shared等等操作,详细说明:
list: 在启动vold之后,接收的第一条命令就是list,这里是获取系统的所有磁盘对象,一般只有sd卡。
debug: 设置USB的调试模式
mount: 挂载磁盘
unmount: 卸载磁盘
format: 格式化磁盘
unshare: 关闭USB的大容量存储模式,相当于断开手机与电脑的连接
shared: 开启USB的大容量存储模式,相当于手机与电脑连接,并挂载在电脑

下面把该函数的源码贴出来,一目了然,程序员,代码说话:

  1. intCommandListener::VolumeCmd::runCommand(SocketClient*cli,
  2. intargc,char**argv){
  3. dumpArgs(argc,argv,-1);
  4. if(argc<2){
  5. cli->sendMsg(ResponseCode::CommandSyntaxError,"MissingArgument",false);
  6. return0;
  7. }
  8. VolumeManager*vm=VolumeManager::Instance();
  9. intrc=0;
  10. if(!strcmp(argv[1],"list")){
  11. returnvm->listVolumes(cli);
  12. }elseif(!strcmp(argv[1],"debug")){
  13. if(argc!=3||(argc==3&&(strcmp(argv[2],"off")&&strcmp(argv[2],"on")))){
  14. cli->sendMsg(ResponseCode::CommandSyntaxError,"Usage:volumedebug<off/on>",false);
  15. return0;
  16. }
  17. vm->setDebug(!strcmp(argv[2],"on")?true:false);
  18. }elseif(!strcmp(argv[1],"mount")){
  19. if(argc!=3){
  20. cli->sendMsg(ResponseCode::CommandSyntaxError,"Usage:volumemount<path>",false);
  21. return0;
  22. }
  23. rc=vm->mountVolume(argv[2]);
  24. }elseif(!strcmp(argv[1],"unmount")){
  25. if(argc<3||argc>4||(argc==4&&strcmp(argv[3],"force"))){
  26. cli->sendMsg(ResponseCode::CommandSyntaxError,"Usage:volumeunmount<path>[force]",false);
  27. return0;
  28. }
  29. boolforce=false;
  30. if(argc>=4&&!strcmp(argv[3],"force")){
  31. force=true;
  32. }
  33. rc=vm->unmountVolume(argv[2],force);
  34. }elseif(!strcmp(argv[1],"format")){
  35. if(argc!=3){
  36. cli->sendMsg(ResponseCode::CommandSyntaxError,"Usage:volumeformat<path>",false);
  37. return0;
  38. }
  39. rc=vm->formatVolume(argv[2]);
  40. }elseif(!strcmp(argv[1],"share")){
  41. if(argc!=4){
  42. cli->sendMsg(ResponseCode::CommandSyntaxError,
  43. "Usage:volumeshare<path><method>",false);
  44. return0;
  45. }
  46. rc=vm->shareVolume(argv[2],argv[3]);
  47. }elseif(!strcmp(argv[1],"unshare")){
  48. if(argc!=4){
  49. cli->sendMsg(ResponseCode::CommandSyntaxError,
  50. "Usage:volumeunshare<path><method>",false);
  51. return0;
  52. }
  53. rc=vm->unshareVolume(argv[2],argv[3]);
  54. }elseif(!strcmp(argv[1],"shared")){
  55. boolenabled=false;
  56. if(argc!=4){
  57. cli->sendMsg(ResponseCode::CommandSyntaxError,
  58. "Usage:volumeshared<path><method>",false);
  59. return0;
  60. }
  61. if(vm->shareEnabled(argv[2],argv[3],&enabled)){
  62. cli->sendMsg(
  63. ResponseCode::OperationFailed,"Failedtodetermineshareenablestate",true);
  64. }else{
  65. cli->sendMsg(ResponseCode::ShareEnabledResult,
  66. (enabled?"Shareenabled":"Sharedisabled"),false);
  67. }
  68. return0;
  69. }else{
  70. cli->sendMsg(ResponseCode::CommandSyntaxError,"Unknownvolumecmd",false);
  71. }
  72. if(!rc){
  73. cli->sendMsg(ResponseCode::CommandOkay,"volumeoperationsucceeded",false);
  74. }else{
  75. interno=errno;
  76. rc=ResponseCode::convertFromErrno();
  77. cli->sendMsg(rc,"volumeoperationfailed",true);
  78. }
  79. return0;
  80. }

每次操作完成后,都要将操作结果汇报给framework,阶级负责工作,一定要把工作结果汇报给领导,这里使用了sendMsg函数。
1.搜索需要挂载的磁盘对象信息列表。先从listVolumes函数开始,贴源码:

  1. intVolumeManager::listVolumes(SocketClient*cli){
  2. VolumeCollection::iteratori;
  3. for(i=mVolumes->begin();i!=mVolumes->end();++i){
  4. char*buffer;
  5. asprintf(&buffer,"%s%s%d",
  6. (*i)->getLabel(),(*i)->getMountpoint(),
  7. (*i)->getState());
  8. cli->sendMsg(ResponseCode::VolumeListResult,buffer,false);
  9. free(buffer);
  10. }
  11. cli->sendMsg(ResponseCode::CommandOkay,"Volumeslisted.",false);
  12. return0;
  13. }

这函数扫描了mVolumes容器,里面保存着从配置文件/etc/vold.fstab解析出来的磁盘。
该容器是在main函数中添加的:vm->addVolume(dv);
这函数先将每个磁盘的标签,挂载点,状态汇报给framework,然后再循环工作结束后,再次向领导汇报工作结束。
2.设置调试模式。贴源码:

  1. voidVolumeManager::setDebug(boolenable){
  2. mDebug=enable;
  3. VolumeCollection::iteratorit;
  4. for(it=mVolumes->begin();it!=mVolumes->end();++it){
  5. (*it)->setDebug(enable);
  6. }
  7. }

每个Volume对象都有一个setDebug函数设置调试状态:

  1. voidVolume::setDebug(boolenable){
  2. mDebug=enable;
  3. }
  4. 3.挂载磁盘。这里都有一个const char *类型的参数,这参数保存着每个磁盘的标签信息,比如sd卡的label是sdcard。

    1. intVolumeManager::mountVolume(constchar*label){
    2. Volume*v=lookupVolume(label);
    3. if(!v){
    4. errno=ENOENT;
    5. return-1;
    6. }
    7. returnv->mountVol();
    8. }

    lookupVolume函数寻找与label匹配的对象:

    1. Volume*VolumeManager::lookupVolume(constchar*label){
    2. VolumeCollection::iteratori;
    3. for(i=mVolumes->begin();i!=mVolumes->end();++i){
    4. if(label[0]=='/'){
    5. if(!strcmp(label,(*i)->getMountpoint()))
    6. return(*i);
    7. }else{
    8. if(!strcmp(label,(*i)->getLabel()))
    9. return(*i);
    10. }
    11. }
    12. returnNULL;
    13. }

    如果找到,直接返回磁盘对象Volume*,挂载操作在mountVol函数里面,该函数内容有点多,贴源码:

    1. intVolume::mountVol(){
    2. dev_tdeviceNodes[4];
    3. intn,i,rc=0;
    4. charerrmsg[255];
    5. if(getState()==Volume::State_NoMedia){
    6. snprintf(errmsg,sizeof(errmsg),
    7. "Volume%s%smountfailed-nomedia",
    8. getLabel(),getMountpoint());
    9. mVm->getBroadcaster()->sendBroadcast(
    10. ResponseCode::VolumeMountFailedNoMedia,
    11. errmsg,false);
    12. errno=ENODEV;
    13. return-1;
    14. }elseif(getState()!=Volume::State_Idle){
    15. errno=EBUSY;
    16. return-1;
    17. }
    18. /*判断该挂载点是否已经挂载,若已经挂载,则直接返回。*/
    19. if(isMountpointMounted(getMountpoint())){
    20. SLOGW("Volumeisidlebutappearstobemounted-fixing");
    21. /*这里的setState函数用得很频繁,这函数就是将状态通知给framework*/
    22. setState(Volume::State_Mounted);
    23. //mCurrentlyMountedKdev=XXX
    24. return0;
    25. }
    26. /*获取磁盘的设备号与分区数量,在下面说明*/
    27. n=getDeviceNodes((dev_t*)&deviceNodes,4);
    28. if(!n){
    29. SLOGE("Failedtogetdevicenodes(%s)\n",strerror(errno));
    30. return-1;
    31. }
    32. /*将循环挂载n个分区,但从代码上看,只适用于挂载一个分区*/
    33. for(i=0;i<n;i++){
    34. chardevicePath[255];
    35. /*这里看到了吧,用这种方式来使用磁盘的设备节点很方便,直接用主次设备号来命令*/
    36. sprintf(devicePath,"/dev/block/vold/%d:%d",MAJOR(deviceNodes[i]),
    37. MINOR(deviceNodes[i]));
    38. SLOGI("%sbeingconsideredforvolume%s\n",devicePath,getLabel());
    39. errno=0;
    40. setState(Volume::State_Checking);
    41. /*挂载之前先检测一下该分区是否是fat分区,如果不是fat格式,返回-1*/
    42. if(Fat::check(devicePath)){
    43. if(errno==ENODATA){
    44. SLOGW("%sdoesnotcontainaFATfilesystem\n",devicePath);
    45. continue;
    46. }
    47. errno=EIO;
    48. /*Badness-abortthemount*/
    49. SLOGE("%sfailedFSchecks(%s)",devicePath,strerror(errno));
    50. setState(Volume::State_Idle);
    51. return-1;
    52. }
    53. /*
    54. *Mountthedeviceonourinternalstagingmountpointsowecan
    55. *muckwithitbeforeexposingittononpriviledgedusers.
    56. */
    57. errno=0;
    58. /*将设备节点挂载在/mnt/secure/staging目录*/
    59. if(Fat::doMount(devicePath,"/mnt/secure/staging",false,false,1000,1015,0702,true)){
    60. SLOGE("%sfailedtomountviaVFAT(%s)\n",devicePath,strerror(errno));
    61. continue;
    62. }
    63. SLOGI("Device%s,target%[email protected]/mnt/secure/staging",devicePath,getMountpoint());
    64. protectFromAutorunStupidity();
    65. /*挂载一个只有root用户能够访问的目录,这函数挂载了两次
    66. 将/mnt/secure/staging/.android_secure挂载在/mnt/secure/asec,
    67. 将tmpfs挂载在/mnt/secure/staging/.android_secure*/
    68. if(createBindMounts()){
    69. SLOGE("Failedtocreatebindmounts(%s)",strerror(errno));
    70. umount("/mnt/secure/staging");
    71. setState(Volume::State_Idle);
    72. return-1;
    73. }
    74. /*
    75. *Nowthatthebindmounttrickeryisdone,atomicallymovethe
    76. *wholesubtreetoexposeittononpriviledgedusers.
    77. */
    78. /*将挂载点挂载的目录再挂载到最终的目录/mnt/sdcard*/
    79. if(doMoveMount("/mnt/secure/staging",getMountpoint(),false)){
    80. SLOGE("Failedtomovemount(%s)",strerror(errno));
    81. umount("/mnt/secure/staging");
    82. setState(Volume::State_Idle);
    83. return-1;
    84. }
    85. setState(Volume::State_Mounted);
    86. mLastMountedKdev=mCurrentlyMountedKdev=deviceNodes[i];
    87. return0;
    88. }
    89. SLOGE("Volume%sfoundnosuitabledevicesformounting:(\n",getLabel());
    90. setState(Volume::State_Idle);
    91. return-1;
    92. }

    这个挂载函数看起来,会发现很繁琐,好几个目录的挂载关系,有以下挂载目录:
    /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中实现,源码:

    1. intDirectVolume::getDeviceNodes(dev_t*devs,intmax){
    2. if(mPartIdx==-1){
    3. //Ifthediskhasnopartitions,trythediskitself
    4. if(!mDiskNumParts){
    5. devs[0]=MKDEV(mDiskMajor,mDiskMinor);
    6. return1;
    7. }
    8. inti;
    9. for(i=0;i<mDiskNumParts;i++){
    10. if(i==max)
    11. break;
    12. devs[i]=MKDEV(mDiskMajor,mPartMinors[i]);
    13. }
    14. returnmDiskNumParts;
    15. }
    16. devs[0]=MKDEV(mDiskMajor,mPartMinors[mPartIdx-1]);
    17. return1;
    18. }

    下面贴一些mountVol里面挂载的源码:

    1. intFat::doMount(constchar*fsPath,constchar*mountPoint,
    2. boolro,boolremount,intownerUid,intownerGid,
    3. intpermMask,boolcreateLost){
    4. intrc;
    5. unsignedlongflags;
    6. charmountData[255];
    7. flags=MS_NODEV|MS_NOEXEC|MS_NOSUID|MS_DIRSYNC;
    8. flags|=(ro?MS_RDONLY:0);
    9. flags|=(remount?MS_REMOUNT:0);
    10. /*
    11. *Note:Thisisatemporaryhack.Ifthesamplingprofilerisenabled,
    12. *wemaketheSDcardworld-writablesoanyprocesscanwritesnapshots.
    13. *
    14. *TODO:Removethiscodeoncewehaveadropboxinsystem_server.
    15. */
    16. charvalue[PROPERTY_VALUE_MAX];
    17. property_get("persist.sampling_profiler",value,"");
    18. if(value[0]=='1'){
    19. SLOGW("TheSDcardisworld-writablebecausethe"
    20. "'persist.sampling_profiler'systempropertyissetto'1'.");
    21. permMask=0;
    22. }
    23. sprintf(mountData,
    24. "utf8,uid=%d,gid=%d,fmask=%o,dmask=%o,shortname=mixed",
    25. ownerUid,ownerGid,permMask,permMask);
    26. rc=mount(fsPath,mountPoint,"vfat",flags,mountData);
    27. if(rc&&errno==EROFS){
    28. SLOGE("%sappearstobeareadonlyfilesystem-retryingmountRO",fsPath);
    29. flags|=MS_RDONLY;
    30. rc=mount(fsPath,mountPoint,"vfat",flags,mountData);
    31. }
    32. if(rc==0&&createLost){
    33. char*lost_path;
    34. asprintf(&lost_path,"%s/LOST.DIR",mountPoint);
    35. if(access(lost_path,F_OK)){
    36. /*
    37. *CreateaLOST.DIRintherootsowehavesomewheretoput
    38. *lostclusterchains(fsck_msdosdoesn'tcurrentlydothis)
    39. */
    40. if(mkdir(lost_path,0755)){
    41. SLOGE("UnabletocreateLOST.DIR(%s)",strerror(errno));
    42. }
    43. }
    44. free(lost_path);
    45. }
    46. returnrc;
    47. }
    48. intVolume::createBindMounts(){
    49. unsignedlongflags;
    50. /*
    51. *Renameold/android_secure->/.android_secure
    52. */
    53. if(!access("/mnt/secure/staging/android_secure",R_OK|X_OK)&&
    54. access(SEC_STG_SECIMGDIR,R_OK|X_OK)){
    55. if(rename("/mnt/secure/staging/android_secure",SEC_STG_SECIMGDIR)){
    56. SLOGE("Failedtorenamelegacyasecdir(%s)",strerror(errno));
    57. }
    58. }
    59. /*
    60. *Ensurethat/android_secureexistsandisadirectory
    61. */
    62. if(access(SEC_STG_SECIMGDIR,R_OK|X_OK)){
    63. if(errno==ENOENT){
    64. if(mkdir(SEC_STG_SECIMGDIR,0777)){
    65. SLOGE("Failedtocreate%s(%s)",SEC_STG_SECIMGDIR,strerror(errno));
    66. return-1;
    67. }
    68. }else{
    69. SLOGE("Failedtoaccess%s(%s)",SEC_STG_SECIMGDIR,strerror(errno));
    70. return-1;
    71. }
    72. }else{
    73. structstatsbuf;
    74. if(stat(SEC_STG_SECIMGDIR,&sbuf)){
    75. SLOGE("Failedtostat%s(%s)",SEC_STG_SECIMGDIR,strerror(errno));
    76. return-1;
    77. }
    78. if(!S_ISDIR(sbuf.st_mode)){
    79. SLOGE("%sisnotadirectory",SEC_STG_SECIMGDIR);
    80. errno=ENOTDIR;
    81. return-1;
    82. }
    83. }
    84. /*
    85. *Bindmount/mnt/secure/staging/android_secure->/mnt/secure/asecsowe'll
    86. *havearootonlyaccessablemountpointforit.
    87. */
    88. if(mount(SEC_STG_SECIMGDIR,SEC_ASECDIR,"",MS_BIND,NULL)){
    89. SLOGE("Failedtobindmountpoints%s->%s(%s)",
    90. SEC_STG_SECIMGDIR,SEC_ASECDIR,strerror(errno));
    91. return-1;
    92. }
    93. /*
    94. *Mountaread-only,zero-sizedtmpfson<mountpoint>/android_secureto
    95. *obscuretheunderlyingdirectoryfromeverybody-sneakyeh?;)
    96. */
    97. if(mount("tmpfs",SEC_STG_SECIMGDIR,"tmpfs",MS_RDONLY,"size=0,mode=000,uid=0,gid=0")){
    98. SLOGE("Failedtoobscure%s(%s)",SEC_STG_SECIMGDIR,strerror(errno));
    99. umount("/mnt/asec_secure");
    100. return-1;
    101. }
    102. return0;
    103. }
    104. intVolume::doMoveMount(constchar*src,constchar*dst,boolforce){
    105. unsignedintflags=MS_MOVE;
    106. intretries=5;
    107. while(retries--){
    108. if(!mount(src,dst,"",flags,NULL)){
    109. if(mDebug){
    110. SLOGD("Movedmount%s->%ssucessfully",src,dst);
    111. }
    112. return0;
    113. }elseif(errno!=EBUSY){
    114. SLOGE("Failedtomovemount%s->%s(%s)",src,dst,strerror(errno));
    115. return-1;
    116. }
    117. intaction=0;
    118. if(force){
    119. if(retries==1){
    120. action=2;//SIGKILL
    121. }elseif(retries==2){
    122. action=1;//SIGHUP
    123. }
    124. }
    125. SLOGW("Failedtomove%s->%s(%s,retries%d,action%d)",
    126. src,dst,strerror(errno),retries,action);
    127. Process::killProcessesWithOpenFiles(src,action);
    128. usleep(1000*250);
    129. }
    130. errno=EBUSY;
    131. SLOGE("Givinguponmove%s->%s(%s)",src,dst,strerror(errno));
    132. return-1;
    133. }

    篇幅有点长了,挂载点也挂载了好几个,mountVol处理的事情最多的,也最繁琐,但都是简单的,主要是有点摸不着头脑,
  5. 提供了挂载,卸载与格式化的功能。在这里,我们可以回忆一下,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类其中的源码:

    1. elseif(!strcmp(argv[1],"unmount")){
    2. if(argc<3||argc>4||(argc==4&&strcmp(argv[3],"force"))){
    3. cli->sendMsg(ResponseCode::CommandSyntaxError,"Usage:volumeunmount<path>[force]",false);
    4. return0;
    5. }
    6. boolforce=false;
    7. if(argc>=4&&!strcmp(argv[3],"force")){
    8. force=true;
    9. }
    10. rc=vm->unmountVolume(argv[2],force);
    11. }
    这条卸载命令的格式是:volume unmount sdcard true/false.
    这里有一个强制卸载的标志,也就是force参数,如果命令包含“force”字符串,那么就强制
    卸载sd卡,接下来看unmountVolume卸载函数:

    1. intVolumeManager::unmountVolume(constchar*label,boolforce){
    2. Volume*v=lookupVolume(label);
    3. ......
    4. /*这函数清理了/mnt/asec目录的一些内容,其实看不见*/
    5. cleanupAsec(v,force);
    6. returnv->unmountVol(force);
    7. }

    开始看卸载函数,以下函数均做简化,不再贴出大量源码,Android系统的源码实在太多:

    1. intVolume::unmountVol(boolforce){
    2. /*广播sd卡正在卸载的状态*/
    3. setState(Volume::State_Unmounting);
    4. if(doMoveMount(getMountpoint(),SEC_STGDIR,force)){
    5. SLOGE("Failedtomovemount%s=>%s(%s)",getMountpoint(),SEC_STGDIR,strerror(errno));
    6. setState(Volume::State_Mounted);
    7. return-1;
    8. }
    9. if(doUnmount(Volume::SEC_STG_SECIMGDIR,force)){
    10. SLOGE("Failedtounmounttmpfson%s(%s)",SEC_STG_SECIMGDIR,strerror(errno));
    11. gotofail_republish;
    12. }
    13. if(doUnmount(Volume::SEC_ASECDIR,force)){
    14. SLOGE("Failedtoremovebindmounton%s(%s)",SEC_ASECDIR,strerror(errno));
    15. gotofail_remount_tmpfs;
    16. }
    17. if(doUnmount(Volume::SEC_STGDIR,force)){
    18. SLOGE("Failedtounmount%s(%s)",SEC_STGDIR,strerror(errno));
    19. gotofail_recreate_bindmount;
    20. }
    21. /*以上doMoveMount,doUnmount等函数做些清理工作,就是之前挂载到系统的目录
    22. 清理干净*/
    23. /*卸载工作做完,告诉framework,该磁盘处于空闲状态*/
    24. setState(Volume::State_Idle);
    25. /*用来保存正在挂载的设备号的全局变量,现在赋为-1*/
    26. mCurrentlyMountedKdev=-1;
    27. return0;
    28. }

    二、vold捕获移除事件,调用卸载函数:

    1. voidDirectVolume::handlePartitionRemoved(constchar*devpath,NetlinkEvent*evt){
    2. intmajor=atoi(evt->findParam("MAJOR"));
    3. intminor=atoi(evt->findParam("MINOR"));
    4. charmsg[255];
    5. intstate;
    6. SLOGD("Volume%s%spartition%d:%dremoved\n",getLabel(),getMountpoint(),major,minor);
    7. /*
    8. *Theframeworkdoesn'tneedtogetnotifiedof
    9. *partitionremovalunlessit'smounted.Otherwise
    10. *theremovalnotificationwillbesentontheDisk
    11. *itself
    12. */
    13. state=getState();
    14. if(state!=Volume::State_Mounted&&state!=Volume::State_Shared){
    15. return;
    16. }
    17. /*判断,如果移除事件的设备号与正在挂载系统的设备号相等,
    18. 说明此时是没有安全删除,所以下面会广播VolumeBadRemoval反馈码*/
    19. if((dev_t)MKDEV(major,minor)==mCurrentlyMountedKdev){
    20. /*
    21. *Yikes,ourmountedpartitionisgoingaway!
    22. */
    23. snprintf(msg,sizeof(msg),"Volume%s%sbadremoval(%d:%d)",
    24. getLabel(),getMountpoint(),major,minor);
    25. mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,
    26. msg,false);
    27. /*清理asec目录*/
    28. if(mVm->cleanupAsec(this,true)){
    29. SLOGE("FailedtocleanupASEC-unmountwillprobablyfail!");
    30. }
    31. /*在这里调用了卸载函数,跟framework下发命令卸载一样。*/
    32. if(Volume::unmountVol(true)){
    33. SLOGE("Failedtounmountvolumeonbadremoval(%s)",
    34. strerror(errno));
    35. //XXX:Atthispointwe'rescrewedfornow
    36. }else{
    37. SLOGD("Crisisaverted");
    38. }
    39. }elseif(state==Volume::State_Shared){
    40. /*这里的判断是,如果此时手机与电脑正在连接着使用时,移除事件的情况*/
    41. /*removedduringmassstorage*/
    42. snprintf(msg,sizeof(msg),"Volume%sbadremoval(%d:%d)",
    43. getLabel(),major,minor);
    44. mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,
    45. msg,false);
    46. /*卸载大容量存储*/
    47. if(mVm->unshareVolume(getLabel(),"ums")){
    48. SLOGE("Failedtounsharevolumeonbadremoval(%s)",
    49. strerror(errno));
    50. }else{
    51. SLOGD("Crisisaverted");
    52. }
    53. }
    54. }


  6. 操作有这几步:
    1.将分区信息写到硬盘的第一个设备节点的MBR结构中的分区表;
    2.格式化分区到指定的文件系统类型。

    MBR中存放分区表的位置在446-509,占用了64字节,MBR结构只支持4个主分区,所以

    有4个16字节的区域,先简要说明一下MBR的分区表的结构:

    Android-vold源码分析之runCommand
    从这个表格可以看出,相对于446-509的分区表区域,每个主分区的第5个字节存放的是文件
    系统标志位,用来识别什么分区,用fdisk工具查看一下,有如下文件系统对应的十六进制标志:

    Android-vold源码分析之runCommand
    若需要读取这些文件系统标志,只需读取MBR的450个位置,占用一个字节大小。

    扯得太远了,回到正题,本文是要分析Android格式化sd卡的功能,在格式化部分,涉及到
    了系统的一些函数,与vold无关,简单的说明一下即可。
    Android系统在格式化sd卡的时候,首先会判断sd卡是否存在分区,如果sd卡不存在分区,
    那么需要重新初始化MBR区域,所以上面简要的介绍了MBR结构中分区表的区域。

    1. intVolume::formatVol(){
    2. if(getState()==Volume::State_NoMedia){
    3. errno=ENODEV;
    4. return-1;
    5. }elseif(getState()!=Volume::State_Idle){
    6. errno=EBUSY;
    7. return-1;
    8. }
    9. /*如果该分区为卸载,那么格式化失败,返回错误*/
    10. if(isMountpointMounted(getMountpoint())){
    11. SLOGW("Volumeisidlebutappearstobemounted-fixing");
    12. setState(Volume::State_Mounted);
    13. //mCurrentlyMountedKdev=XXX
    14. errno=EBUSY;
    15. return-1;
    16. }
    17. chardevicePath[255];
    18. dev_tdiskNode=getDiskDevice();
    19. dev_tpartNode;
    20. if(mDebug){
    21. SLOGI("Formattingvolume%s(%s)",getLabel(),devicePath);
    22. }
    23. setState(Volume::State_Formatting);
    24. if(!mLastMountedKdev){
    25. dev_tdeviceNodes[2];
    26. intn=getDeviceNodes(deviceNodes,2);
    27. //initializeMBRifnopartition,orhasmultiplepartitions
    28. //butnoneisselected
    29. if((diskNode==deviceNodes[0])||(n>1)){
    30. sprintf(devicePath,"/dev/block/vold/%d:%d",
    31. MAJOR(diskNode),MINOR(diskNode));
    32. if(initializeMbr(devicePath)){
    33. SLOGE("FailedtoinitializeMBR(%s)",strerror(errno));
    34. partNode=diskNode;//trytousewholedisk
    35. }else{
    36. partNode=MKDEV(MAJOR(diskNode),MINOR(diskNode)+1);
    37. }
    38. }else{
    39. partNode=deviceNodes[0];
    40. }
    41. }else{
    42. partNode=mLastMountedKdev;
    43. }
    44. sprintf(devicePath,"/dev/block/vold/%d:%d",
    45. MAJOR(partNode),MINOR(partNode));
    46. intret=Fat::format(devicePath,0);
    47. SLOGE_IF(ret,"Failedtoformat(%s)",strerror(errno));
    48. setState(Volume::State_Idle);
    49. returnret;
    50. }

    格式化函数两个主要工作交给了initializeMbr和Fat::format函数:
    1.initializeMbr函数负责初始化MBR;
    2.Fat::format函数负责格式化分区。
    先来看initializeMbr函数的初始化工作:

    1. intVolume::initializeMbr(constchar*deviceNode){
    2. structdisk_infodinfo;
    3. memset(&dinfo,0,sizeof(dinfo));
    4. if(!(dinfo.part_lst=(structpart_info*)malloc(MAX_NUM_PARTS*sizeof(structpart_info)))){
    5. SLOGE("Failedtomallocprt_lst");
    6. return-1;
    7. }
    8. memset(dinfo.part_lst,0,MAX_NUM_PARTS*sizeof(structpart_info));
    9. dinfo.device=strdup(deviceNode);
    10. dinfo.scheme=PART_SCHEME_MBR;
    11. dinfo.sect_size=512;
    12. dinfo.skip_lba=2048;
    13. dinfo.num_lba=0;
    14. dinfo.num_parts=1;
    15. structpart_info*pinfo=&dinfo.part_lst[0];
    16. pinfo->name=strdup("android_sdcard");
    17. pinfo->flags|=PART_ACTIVE_FLAG;
    18. pinfo->type=PC_PART_TYPE_FAT32;
    19. pinfo->len_kb=-1;
    20. intrc=apply_disk_config(&dinfo,0);
    21. if(rc){
    22. SLOGE("Failedtoapplydiskconfiguration(%d)",rc);
    23. gotoout;
    24. }
    25. out:
    26. free(pinfo->name);
    27. free(dinfo.device);
    28. free(dinfo.part_lst);
    29. returnrc;
    30. }

    这里贴出一些重要的结构体:

    1. structpart_info{
    2. char*name;
    3. uint8_tflags;
    4. uint8_ttype;
    5. uint32_tlen_kb;/*in1K-bytes*/
    6. uint32_tstart_lba;/*theLBAwherethispartitionbegins*/
    7. };
    8. structdisk_info{
    9. char*device;
    10. uint8_tscheme;
    11. intsect_size;/*expectedsectorsizeinbytes.MUSTBEPOWEROF2*/
    12. uint32_tskip_lba;/*insectors(1unitofLBA)*/
    13. uint32_tnum_lba;/*thesizeofthediskinLBAunits*/
    14. structpart_info*part_lst;
    15. intnum_parts;
    16. };

    初始化完成后,将该结构体变量pinfo通过apply_disk_config函数进行设置:

    1. intapply_disk_config(structdisk_info*dinfo,inttest)
    2. {
    3. intfd;
    4. structwrite_list*wr_lst=NULL;
    5. intrv;
    6. if(validate_and_config(dinfo,&fd,&wr_lst)!=0){
    7. LOGE("Configurationisinvalid.");
    8. gotofail;
    9. }
    10. if((rv=wlist_commit(fd,wr_lst,test))>=0)
    11. rv=test?0:sync_ptable(fd);
    12. close(fd);
    13. wlist_free(wr_lst);
    14. returnrv;
    15. fail:
    16. close(fd);
    17. if(wr_lst)
    18. wlist_free(wr_lst);
    19. return1;
    20. }

    该函数先打开sd卡的设备节点,然后将MBR的初始化信息写到第一个block(512B)中,
    这里涉及到非常多函数,不在vold的讨论范围。
    写完MBR后,就要对分区进行格式化,要格式化成FAT32格式,Fat::format函数直接调用
    系统命令newfs_msdos来格式化新分区,检测磁盘是,Fat::check函数直接调用系统命令
    fsck_msdos来检测分区。

    最后格式化完成功,通知Framework,sd卡处于空闲状态:
    1. setState(Volume::State_Idle);

    然后,Framework又要重复挂载和卸载的操作了。

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-10-30
猜你喜欢
  • 2021-04-03
  • 2022-12-23
  • 2021-11-17
  • 2022-12-23
  • 2021-11-18
  • 2021-11-18
  • 2021-11-23
相关资源
相似解决方案