当前位置: 代码迷 >> 综合 >> Android-vold源码分析之runCommand
  详细解决方案

Android-vold源码分析之runCommand

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

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

[cpp]  view plain copy
  1. int CommandListener::VolumeCmd::runCommand(SocketClient *cli,  
  2.                                                       int argc, char **argv) {  
  3.     dumpArgs(argc, argv, -1);  
  4.   
  5.     if (argc < 2) {  
  6.         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);  
  7.         return 0;  
  8.     }  
  9.   
  10.     VolumeManager *vm = VolumeManager::Instance();  
  11.     int rc = 0;  
  12.   
  13.     if (!strcmp(argv[1], "list")) {  
  14.         return vm->listVolumes(cli);  
  15.     } else if (!strcmp(argv[1], "debug")) {  
  16.         if (argc != 3 || (argc == 3 && (strcmp(argv[2], "off") && strcmp(argv[2], "on")))) {  
  17.             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume debug <off/on>", false);  
  18.             return 0;  
  19.         }  
  20.         vm->setDebug(!strcmp(argv[2], "on") ? true : false);  
  21.     } else if (!strcmp(argv[1], "mount")) {  
  22.         if (argc != 3) {  
  23.             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume mount <path>", false);  
  24.             return 0;  
  25.         }  
  26.         rc = vm->mountVolume(argv[2]);  
  27.     } else if (!strcmp(argv[1], "unmount")) {  
  28.         if (argc < 3 || argc > 4 || (argc == 4 && strcmp(argv[3], "force"))) {  
  29.             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume unmount <path> [force]", false);  
  30.             return 0;  
  31.         }  
  32.   
  33.         bool force = false;  
  34.         if (argc >= 4 && !strcmp(argv[3], "force")) {  
  35.             force = true;  
  36.         }  
  37.         rc = vm->unmountVolume(argv[2], force);  
  38.     } else if (!strcmp(argv[1], "format")) {  
  39.         if (argc != 3) {  
  40.             cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume format <path>", false);  
  41.             return 0;  
  42.         }  
  43.         rc = vm->formatVolume(argv[2]);  
  44.     } else if (!strcmp(argv[1], "share")) {  
  45.         if (argc != 4) {  
  46.             cli->sendMsg(ResponseCode::CommandSyntaxError,  
  47.                     "Usage: volume share <path> <method>", false);  
  48.             return 0;  
  49.         }  
  50.         rc = vm->shareVolume(argv[2], argv[3]);  
  51.     } else if (!strcmp(argv[1], "unshare")) {  
  52.         if (argc != 4) {  
  53.             cli->sendMsg(ResponseCode::CommandSyntaxError,  
  54.                     "Usage: volume unshare <path> <method>", false);  
  55.             return 0;  
  56.         }  
  57.         rc = vm->unshareVolume(argv[2], argv[3]);  
  58.     } else if (!strcmp(argv[1], "shared")) {  
  59.         bool enabled = false;  
  60.         if (argc != 4) {  
  61.             cli->sendMsg(ResponseCode::CommandSyntaxError,  
  62.                     "Usage: volume shared <path> <method>", false);  
  63.             return 0;  
  64.         }  
  65.   
  66.         if (vm->shareEnabled(argv[2], argv[3], &enabled)) {  
  67.             cli->sendMsg(  
  68.                     ResponseCode::OperationFailed, "Failed to determine share enable state", true);  
  69.         } else {  
  70.             cli->sendMsg(ResponseCode::ShareEnabledResult,  
  71.                     (enabled ? "Share enabled" : "Share disabled"), false);  
  72.         }  
  73.         return 0;  
  74.     } else {  
  75.         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume cmd", false);  
  76.     }  
  77.   
  78.     if (!rc) {  
  79.         cli->sendMsg(ResponseCode::CommandOkay, "volume operation succeeded", false);  
  80.     } else {  
  81.         int erno = errno;  
  82.         rc = ResponseCode::convertFromErrno();  
  83.         cli->sendMsg(rc, "volume operation failed", true);  
  84.     }  
  85.   
  86.     return 0;  
  87. }  

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

[cpp]  view plain copy
  1. int VolumeManager::listVolumes(SocketClient *cli) {  
  2.     VolumeCollection::iterator i;  
  3.   
  4.     for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {  
  5.         char *buffer;  
  6.         asprintf(&buffer, "%s %s %d",  
  7.                  (*i)->getLabel(), (*i)->getMountpoint(),  
  8.                  (*i)->getState());  
  9.         cli->sendMsg(ResponseCode::VolumeListResult, buffer, false);  
  10.         free(buffer);  
  11.     }  
  12.     cli->sendMsg(ResponseCode::CommandOkay, "Volumes listed.", false);  
  13.     return 0;  
  14. }  

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

[cpp]  view plain copy
  1. void VolumeManager::setDebug(bool enable) {  
  2.     mDebug = enable;  
  3.     VolumeCollection::iterator it;  
  4.     for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {  
  5.         (*it)->setDebug(enable);  
  6.     }  
  7. }  

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

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

    [cpp]  view plain copy
    1. int VolumeManager::mountVolume(const char *label) {  
    2.     Volume *v = lookupVolume(label);  
    3.   
    4.     if (!v) {  
    5.         errno = ENOENT;  
    6.         return -1;  
    7.     }  
    8.   
    9.     return v->mountVol();  
    10. }  

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

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

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

    [cpp]  view plain copy
    1. int Volume::mountVol() {  
    2.     dev_t deviceNodes[4];  
    3.     int n, i, rc = 0;  
    4.     char errmsg[255];  
    5.   
    6.     if (getState() == Volume::State_NoMedia) {  
    7.         snprintf(errmsg, sizeof(errmsg),  
    8.                  "Volume %s %s mount failed - no media",  
    9.                  getLabel(), getMountpoint());  
    10.         mVm->getBroadcaster()->sendBroadcast(  
    11.                                          ResponseCode::VolumeMountFailedNoMedia,  
    12.                                          errmsg, false);  
    13.         errno = ENODEV;  
    14.         return -1;  
    15.     } else if (getState() != Volume::State_Idle) {  
    16.         errno = EBUSY;  
    17.         return -1;  
    18.     }  
    19.     /*判断该挂载点是否已经挂载,若已经挂载,则直接返回。*/  
    20.     if (isMountpointMounted(getMountpoint())) {  
    21.         SLOGW("Volume is idle but appears to be mounted - fixing");  
    22.         /*这里的setState函数用得很频繁,这函数就是将状态通知给framework*/  
    23.         setState(Volume::State_Mounted);  
    24.         // mCurrentlyMountedKdev = XXX  
    25.         return 0;  
    26.     }  
    27.     /*获取磁盘的设备号与分区数量,在下面说明*/  
    28.     n = getDeviceNodes((dev_t *) &deviceNodes, 4);  
    29.     if (!n) {  
    30.         SLOGE("Failed to get device nodes (%s)\n", strerror(errno));  
    31.         return -1;  
    32.     }  
    33.     /*将循环挂载n个分区,但从代码上看,只适用于挂载一个分区*/  
    34.     for (i = 0; i < n; i++) {  
    35.         char devicePath[255];  
    36.         /*这里看到了吧,用这种方式来使用磁盘的设备节点很方便,直接用主次设备号来命令*/  
    37.         sprintf(devicePath, "/dev/block/vold/%d:%d", MAJOR(deviceNodes[i]),  
    38.                 MINOR(deviceNodes[i]));  
    39.   
    40.         SLOGI("%s being considered for volume %s\n", devicePath, getLabel());  
    41.   
    42.         errno = 0;  
    43.         setState(Volume::State_Checking);  
    44.         /*挂载之前先检测一下该分区是否是fat分区,如果不是fat格式,返回-1*/  
    45.         if (Fat::check(devicePath)) {  
    46.             if (errno == ENODATA) {  
    47.                 SLOGW("%s does not contain a FAT filesystem\n", devicePath);  
    48.                 continue;  
    49.             }  
    50.             errno = EIO;  
    51.             /* Badness - abort the mount */  
    52.             SLOGE("%s failed FS checks (%s)", devicePath, strerror(errno));  
    53.             setState(Volume::State_Idle);  
    54.             return -1;  
    55.         }  
    56.   
    57.         /* 
    58.          * Mount the device on our internal staging mountpoint so we can 
    59.          * muck with it before exposing it to non priviledged users. 
    60.          */  
    61.         errno = 0;  
    62.         /*将设备节点挂载在/mnt/secure/staging目录*/  
    63.         if (Fat::doMount(devicePath, "/mnt/secure/staging", false, false, 1000, 1015, 0702, true)) {  
    64.             SLOGE("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));  
    65.             continue;  
    66.         }  
    67.   
    68.         SLOGI("Device %s, target %s mounted @ /mnt/secure/staging", devicePath, getMountpoint());  
    69.   
    70.         protectFromAutorunStupidity();  
    71.         /*挂载一个只有root用户能够访问的目录,这函数挂载了两次 
    72.         将/mnt/secure/staging/.android_secure  挂载在 /mnt/secure/asec, 
    73.         将tmpfs 挂载在 /mnt/secure/staging/.android_secure*/  
    74.         if (createBindMounts()) {  
    75.             SLOGE("Failed to create bindmounts (%s)", strerror(errno));  
    76.             umount("/mnt/secure/staging");  
    77.             setState(Volume::State_Idle);  
    78.             return -1;  
    79.         }  
    80.   
    81.         /* 
    82.          * Now that the bindmount trickery is done, atomically move the 
    83.          * whole subtree to expose it to non priviledged users. 
    84.          */  
    85.         /*将挂载点挂载的目录再挂载到最终的目录/mnt/sdcard*/  
    86.         if (doMoveMount("/mnt/secure/staging", getMountpoint(), false)) {  
    87.             SLOGE("Failed to move mount (%s)", strerror(errno));  
    88.             umount("/mnt/secure/staging");  
    89.             setState(Volume::State_Idle);  
    90.             return -1;  
    91.         }  
    92.         setState(Volume::State_Mounted);  
    93.         mLastMountedKdev = mCurrentlyMountedKdev = deviceNodes[i];  
    94.         return 0;  
    95.     }  
    96.   
    97.     SLOGE("Volume %s found no suitable devices for mounting :(\n", getLabel());  
    98.     setState(Volume::State_Idle);  
    99.   
    100.     return -1;  
    101. }  

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

    [cpp]  view plain copy
    1. int DirectVolume::getDeviceNodes(dev_t *devs, int max) {  
    2.     if (mPartIdx == -1) {  
    3.         // If the disk has no partitions, try the disk itself  
    4.         if (!mDiskNumParts) {  
    5.             devs[0] = MKDEV(mDiskMajor, mDiskMinor);  
    6.             return 1;  
    7.         }  
    8.   
    9.         int i;  
    10.         for (i = 0; i < mDiskNumParts; i++) {  
    11.             if (i == max)  
    12.                 break;  
    13.             devs[i] = MKDEV(mDiskMajor, mPartMinors[i]);  
    14.         }  
    15.         return mDiskNumParts;  
    16.     }  
    17.     devs[0] = MKDEV(mDiskMajor, mPartMinors[mPartIdx -1]);  
    18.     return 1;  
    19. }  

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

    [cpp]  view plain copy
    1. int Fat::doMount(const char *fsPath, const char *mountPoint,  
    2.                  bool ro, bool remount, int ownerUid, int ownerGid,  
    3.                  int permMask, bool createLost) {  
    4.     int rc;  
    5.     unsigned long flags;  
    6.     char mountData[255];  
    7.   
    8.     flags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_DIRSYNC;  
    9.   
    10.     flags |= (ro ? MS_RDONLY : 0);  
    11.     flags |= (remount ? MS_REMOUNT : 0);  
    12.   
    13.     /* 
    14.      * Note: This is a temporary hack. If the sampling profiler is enabled, 
    15.      * we make the SD card world-writable so any process can write snapshots. 
    16.      * 
    17.      * TODO: Remove this code once we have a drop box in system_server. 
    18.      */  
    19.     char value[PROPERTY_VALUE_MAX];  
    20.     property_get("persist.sampling_profiler", value, "");  
    21.     if (value[0] == '1') {  
    22.         SLOGW("The SD card is world-writable because the"  
    23.             " 'persist.sampling_profiler' system property is set to '1'.");  
    24.         permMask = 0;  
    25.     }  
    26.   
    27.     sprintf(mountData,  
    28.             "utf8,uid=%d,gid=%d,fmask=%o,dmask=%o,shortname=mixed",  
    29.             ownerUid, ownerGid, permMask, permMask);  
    30.   
    31.     rc = mount(fsPath, mountPoint, "vfat", flags, mountData);  
    32.   
    33.     if (rc && errno == EROFS) {  
    34.         SLOGE("%s appears to be a read only filesystem - retrying mount RO", fsPath);  
    35.         flags |= MS_RDONLY;  
    36.         rc = mount(fsPath, mountPoint, "vfat", flags, mountData);  
    37.     }  
    38.   
    39.     if (rc == 0 && createLost) {  
    40.         char *lost_path;  
    41.         asprintf(&lost_path, "%s/LOST.DIR", mountPoint);  
    42.         if (access(lost_path, F_OK)) {  
    43.             /* 
    44.              * Create a LOST.DIR in the root so we have somewhere to put 
    45.              * lost cluster chains (fsck_msdos doesn't currently do this) 
    46.              */  
    47.             if (mkdir(lost_path, 0755)) {  
    48.                 SLOGE("Unable to create LOST.DIR (%s)", strerror(errno));  
    49.             }  
    50.         }  
    51.         free(lost_path);  
    52.     }  
    53.   
    54.     return rc;  
    55. }  
    56.   
    57. int Volume::createBindMounts() {  
    58.     unsigned long flags;  
    59.   
    60.     /* 
    61.      * Rename old /android_secure -> /.android_secure 
    62.      */  
    63.     if (!access("/mnt/secure/staging/android_secure", R_OK | X_OK) &&  
    64.          access(SEC_STG_SECIMGDIR, R_OK | X_OK)) {  
    65.         if (rename("/mnt/secure/staging/android_secure", SEC_STG_SECIMGDIR)) {  
    66.             SLOGE("Failed to rename legacy asec dir (%s)", strerror(errno));  
    67.         }  
    68.     }  
    69.   
    70.     /* 
    71.      * Ensure that /android_secure exists and is a directory 
    72.      */  
    73.     if (access(SEC_STG_SECIMGDIR, R_OK | X_OK)) {  
    74.         if (errno == ENOENT) {  
    75.             if (mkdir(SEC_STG_SECIMGDIR, 0777)) {  
    76.                 SLOGE("Failed to create %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));  
    77.                 return -1;  
    78.             }  
    79.         } else {  
    80.             SLOGE("Failed to access %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));  
    81.             return -1;  
    82.         }  
    83.     } else {  
    84.         struct stat sbuf;  
    85.   
    86.         if (stat(SEC_STG_SECIMGDIR, &sbuf)) {  
    87.             SLOGE("Failed to stat %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));  
    88.             return -1;  
    89.         }  
    90.         if (!S_ISDIR(sbuf.st_mode)) {  
    91.             SLOGE("%s is not a directory", SEC_STG_SECIMGDIR);  
    92.             errno = ENOTDIR;  
    93.             return -1;  
    94.         }  
    95.     }  
    96.   
    97.     /* 
    98.      * Bind mount /mnt/secure/staging/android_secure -> /mnt/secure/asec so we'll 
    99.      * have a root only accessable mountpoint for it. 
    100.      */  
    101.     if (mount(SEC_STG_SECIMGDIR, SEC_ASECDIR, "", MS_BIND, NULL)) {  
    102.         SLOGE("Failed to bind mount points %s -> %s (%s)",  
    103.                 SEC_STG_SECIMGDIR, SEC_ASECDIR, strerror(errno));  
    104.         return -1;  
    105.     }  
    106.   
    107.     /* 
    108.      * Mount a read-only, zero-sized tmpfs  on <mountpoint>/android_secure to 
    109.      * obscure the underlying directory from everybody - sneaky eh? ;) 
    110.      */  
    111.     if (mount("tmpfs", SEC_STG_SECIMGDIR, "tmpfs", MS_RDONLY, "size=0,mode=000,uid=0,gid=0")) {  
    112.         SLOGE("Failed to obscure %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));  
    113.         umount("/mnt/asec_secure");  
    114.         return -1;  
    115.     }  
    116.   
    117.     return 0;  
    118. }  
    119.   
    120. int Volume::doMoveMount(const char *src, const char *dst, bool force) {  
    121.     unsigned int flags = MS_MOVE;  
    122.     int retries = 5;  
    123.   
    124.     while(retries--) {  
    125.         if (!mount(src, dst, "", flags, NULL)) {  
    126.             if (mDebug) {  
    127.                 SLOGD("Moved mount %s -> %s sucessfully", src, dst);  
    128.             }  
    129.             return 0;  
    130.         } else if (errno != EBUSY) {  
    131.             SLOGE("Failed to move mount %s -> %s (%s)", src, dst, strerror(errno));  
    132.             return -1;  
    133.         }  
    134.         int action = 0;  
    135.   
    136.         if (force) {  
    137.             if (retries == 1) {  
    138.                 action = 2; // SIGKILL  
    139.             } else if (retries == 2) {  
    140.                 action = 1; // SIGHUP  
    141.             }  
    142.         }  
    143.         SLOGW("Failed to move %s -> %s (%s, retries %d, action %d)",  
    144.                 src, dst, strerror(errno), retries, action);  
    145.         Process::killProcessesWithOpenFiles(src, action);  
    146.         usleep(1000*250);  
    147.     }  
    148.   
    149.     errno = EBUSY;  
    150.     SLOGE("Giving up on move %s -> %s (%s)", src, dst, strerror(errno));  
    151.     return -1;  
    152. }  

    篇幅有点长了,挂载点也挂载了好几个,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类其中的源码:

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

    [cpp]  view plain copy
    1. int VolumeManager::unmountVolume(const char *label, bool force) {  
    2.     Volume *v = lookupVolume(label);   
    3.     ......  
    4.     /*这函数清理了/mnt/asec目录的一些内容,其实看不见*/  
    5.     cleanupAsec(v, force);  
    6.   
    7.     return v->unmountVol(force);  
    8. }  

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

    [cpp]  view plain copy
    1. int Volume::unmountVol(bool force) {  
    2.     /*广播sd卡正在卸载的状态*/  
    3.     setState(Volume::State_Unmounting);  
    4.   
    5.     if (doMoveMount(getMountpoint(), SEC_STGDIR, force)) {  
    6.         SLOGE("Failed to move mount %s => %s (%s)", getMountpoint(), SEC_STGDIR, strerror(errno));  
    7.         setState(Volume::State_Mounted);  
    8.         return -1;  
    9.     }  
    10.   
    11.     if (doUnmount(Volume::SEC_STG_SECIMGDIR, force)) {  
    12.         SLOGE("Failed to unmount tmpfs on %s (%s)", SEC_STG_SECIMGDIR, strerror(errno));  
    13.         goto fail_republish;  
    14.     }  
    15.   
    16.     if (doUnmount(Volume::SEC_ASECDIR, force)) {  
    17.         SLOGE("Failed to remove bindmount on %s (%s)", SEC_ASECDIR, strerror(errno));  
    18.         goto fail_remount_tmpfs;  
    19.     }  
    20.     if (doUnmount(Volume::SEC_STGDIR, force)) {  
    21.         SLOGE("Failed to unmount %s (%s)", SEC_STGDIR, strerror(errno));  
    22.         goto fail_recreate_bindmount;  
    23.     }  
    24.     /*以上doMoveMount, doUnmount等函数做些清理工作,就是之前挂载到系统的目录 
    25.     清理干净*/  
    26.     /*卸载工作做完,告诉framework,该磁盘处于空闲状态*/  
    27.     setState(Volume::State_Idle);  
    28.     /*用来保存正在挂载的设备号的全局变量,现在赋为-1*/  
    29.     mCurrentlyMountedKdev = -1;  
    30.     return 0;  
    31. }  

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

    [cpp]  view plain copy
    1. void DirectVolume::handlePartitionRemoved(const char *devpath, NetlinkEvent *evt) {  
    2.     int major = atoi(evt->findParam("MAJOR"));  
    3.     int minor = atoi(evt->findParam("MINOR"));  
    4.     char msg[255];  
    5.     int state;  
    6.   
    7.     SLOGD("Volume %s %s partition %d:%d removed\n", getLabel(), getMountpoint(), major, minor);  
    8.   
    9.     /* 
    10.      * The framework doesn't need to get notified of 
    11.      * partition removal unless it's mounted. Otherwise 
    12.      * the removal notification will be sent on the Disk 
    13.      * itself 
    14.      */  
    15.     state = getState();  
    16.     if (state != Volume::State_Mounted && state != Volume::State_Shared) {  
    17.         return;  
    18.     }  
    19.     /*判断,如果移除事件的设备号与正在挂载系统的设备号相等, 
    20.     说明此时是没有安全删除,所以下面会广播VolumeBadRemoval反馈码*/  
    21.     if ((dev_t) MKDEV(major, minor) == mCurrentlyMountedKdev) {  
    22.         /* 
    23.          * Yikes, our mounted partition is going away! 
    24.          */  
    25.   
    26.         snprintf(msg, sizeof(msg), "Volume %s %s bad removal (%d:%d)",  
    27.                  getLabel(), getMountpoint(), major, minor);  
    28.         mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,  
    29.                                              msg, false);  
    30.         /*清理asec目录*/  
    31.         if (mVm->cleanupAsec(this, true)) {  
    32.             SLOGE("Failed to cleanup ASEC - unmount will probably fail!");  
    33.         }  
    34.         /*在这里调用了卸载函数,跟framework下发命令卸载一样。*/  
    35.         if (Volume::unmountVol(true)) {  
    36.             SLOGE("Failed to unmount volume on bad removal (%s)",   
    37.                  strerror(errno));  
    38.             // XXX: At this point we're screwed for now  
    39.         } else {  
    40.             SLOGD("Crisis averted");  
    41.         }  
    42.     } else if (state == Volume::State_Shared) {  
    43.         /*这里的判断是,如果此时手机与电脑正在连接着使用时,移除事件的情况*/  
    44.         /* removed during mass storage */  
    45.         snprintf(msg, sizeof(msg), "Volume %s bad removal (%d:%d)",  
    46.                  getLabel(), major, minor);  
    47.         mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,  
    48.                                              msg, false);  
    49.         /*卸载大容量存储*/  
    50.         if (mVm->unshareVolume(getLabel(), "ums")) {  
    51.             SLOGE("Failed to unshare volume on bad removal (%s)",  
    52.                 strerror(errno));  
    53.         } else {  
    54.             SLOGD("Crisis averted");  
    55.         }  
    56.     }  
    57. }  


  6. 操作有这几步:
    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结构中分区表的区域。

    [cpp]  view plain copy
    1. int Volume::formatVol() {  
    2.     if (getState() == Volume::State_NoMedia) {  
    3.         errno = ENODEV;  
    4.         return -1;  
    5.     } else if (getState() != Volume::State_Idle) {  
    6.         errno = EBUSY;  
    7.         return -1;  
    8.     }  
    9.     /*如果该分区为卸载,那么格式化失败,返回错误*/  
    10.     if (isMountpointMounted(getMountpoint())) {  
    11.         SLOGW("Volume is idle but appears to be mounted - fixing");  
    12.         setState(Volume::State_Mounted);  
    13.         // mCurrentlyMountedKdev = XXX  
    14.         errno = EBUSY;  
    15.         return -1;  
    16.     }  
    17.   
    18.     char devicePath[255];  
    19.     dev_t diskNode = getDiskDevice();  
    20.     dev_t partNode;  
    21.   
    22.     if (mDebug) {  
    23.         SLOGI("Formatting volume %s (%s)", getLabel(), devicePath);  
    24.     }  
    25.     setState(Volume::State_Formatting);  
    26.   
    27.     if (!mLastMountedKdev) {  
    28.         dev_t deviceNodes[2];  
    29.         int n = getDeviceNodes(deviceNodes, 2);  
    30.         // initialize MBR if no partition, or has multiple partitions  
    31.         // but none is selected  
    32.         if ((diskNode == deviceNodes[0]) || (n > 1)) {  
    33.             sprintf(devicePath, "/dev/block/vold/%d:%d",  
    34.                     MAJOR(diskNode), MINOR(diskNode));  
    35.             if (initializeMbr(devicePath)) {  
    36.                 SLOGE("Failed to initialize MBR (%s)", strerror(errno));  
    37.                 partNode = diskNode; // try to use whole disk  
    38.             } else {  
    39.                 partNode = MKDEV(MAJOR(diskNode), MINOR(diskNode) + 1);  
    40.             }  
    41.         } else {  
    42.             partNode = deviceNodes[0];  
    43.         }  
    44.     } else {  
    45.         partNode = mLastMountedKdev;  
    46.     }  
    47.   
    48.     sprintf(devicePath, "/dev/block/vold/%d:%d",  
    49.             MAJOR(partNode), MINOR(partNode));  
    50.   
    51.     int ret = Fat::format(devicePath, 0);  
    52.     SLOGE_IF(ret, "Failed to format (%s)", strerror(errno));  
    53.   
    54.     setState(Volume::State_Idle);  
    55.     return ret;  
    56. }  

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

    [cpp]  view plain copy
    1. int Volume::initializeMbr(const char *deviceNode) {  
    2.     struct disk_info dinfo;  
    3.   
    4.     memset(&dinfo, 0, sizeof(dinfo));  
    5.   
    6.     if (!(dinfo.part_lst = (struct part_info *) malloc(MAX_NUM_PARTS * sizeof(struct part_info)))) {  
    7.         SLOGE("Failed to malloc prt_lst");  
    8.         return -1;  
    9.     }  
    10.   
    11.     memset(dinfo.part_lst, 0, MAX_NUM_PARTS * sizeof(struct part_info));  
    12.     dinfo.device = strdup(deviceNode);  
    13.     dinfo.scheme = PART_SCHEME_MBR;  
    14.     dinfo.sect_size = 512;  
    15.     dinfo.skip_lba = 2048;  
    16.     dinfo.num_lba = 0;  
    17.     dinfo.num_parts = 1;  
    18.   
    19.     struct part_info *pinfo = &dinfo.part_lst[0];  
    20.   
    21.     pinfo->name = strdup("android_sdcard");  
    22.     pinfo->flags |= PART_ACTIVE_FLAG;  
    23.     pinfo->type = PC_PART_TYPE_FAT32;  
    24.     pinfo->len_kb = -1;  
    25.   
    26.     int rc = apply_disk_config(&dinfo, 0);  
    27.   
    28.     if (rc) {  
    29.         SLOGE("Failed to apply disk configuration (%d)", rc);  
    30.         goto out;  
    31.     }  
    32.   
    33.  out:  
    34.     free(pinfo->name);  
    35.     free(dinfo.device);  
    36.     free(dinfo.part_lst);  
    37.   
    38.     return rc;  
    39. }  

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

    [cpp]  view plain copy
    1. struct part_info {  
    2.     char *name;  
    3.     uint8_t flags;  
    4.     uint8_t type;  
    5.     uint32_t len_kb;       /* in 1K-bytes */  
    6.     uint32_t start_lba;    /* the LBA where this partition begins */  
    7. };  
    8. struct disk_info {  
    9.     char *device;  
    10.     uint8_t scheme;  
    11.     int sect_size;       /* expected sector size in bytes. MUST BE POWER OF 2 */  
    12.     uint32_t skip_lba;   /* in sectors (1 unit of LBA) */  
    13.     uint32_t num_lba;    /* the size of the disk in LBA units */  
    14.     struct part_info *part_lst;  
    15.     int num_parts;  
    16. };  

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

    [cpp]  view plain copy
    1. int apply_disk_config(struct disk_info *dinfo, int test)  
    2. {  
    3.     int fd;  
    4.     struct write_list *wr_lst = NULL;  
    5.     int rv;  
    6.   
    7.     if (validate_and_config(dinfo, &fd, &wr_lst) != 0) {  
    8.         LOGE("Configuration is invalid.");  
    9.         goto fail;  
    10.     }  
    11.   
    12.     if ((rv = wlist_commit(fd, wr_lst, test)) >= 0)  
    13.         rv = test ? 0 : sync_ptable(fd);  
    14.   
    15.     close(fd);  
    16.     wlist_free(wr_lst);  
    17.     return rv;  
    18.   
    19. fail:  
    20.     close(fd);  
    21.     if (wr_lst)  
    22.         wlist_free(wr_lst);  
    23.     return 1;  
    24. }  

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

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

    然后,Framework又要重复挂载和卸载的操作了。
  相关解决方案