Android-vold源码分析之挂载SD卡

来源:互联网 发布:淘宝网处罚考试2017 编辑:程序博客网 时间:2024/05/21 09:03

挂载磁盘。这里都有一个const char *类型的参数,这参数保存着每个磁盘的标签信息,比如sd卡的label是sdcard。

[cpp] view plaincopy
  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 plaincopy
  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 plaincopy
  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"falsefalse, 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 plaincopy
  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 plaincopy
  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. }