Home | History | Annotate | Download | only in vold
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <stdio.h>
     18 #include <stdlib.h>
     19 #include <string.h>
     20 #include <errno.h>
     21 #include <fcntl.h>
     22 #include <fts.h>
     23 #include <unistd.h>
     24 #include <sys/stat.h>
     25 #include <sys/types.h>
     26 #include <sys/mount.h>
     27 #include <dirent.h>
     28 
     29 #include <linux/kdev_t.h>
     30 
     31 #define LOG_TAG "Vold"
     32 
     33 #include <openssl/md5.h>
     34 
     35 #include <cutils/log.h>
     36 
     37 #include <sysutils/NetlinkEvent.h>
     38 
     39 #include <private/android_filesystem_config.h>
     40 
     41 #include "VolumeManager.h"
     42 #include "DirectVolume.h"
     43 #include "ResponseCode.h"
     44 #include "Loop.h"
     45 #include "Ext4.h"
     46 #include "Fat.h"
     47 #include "Devmapper.h"
     48 #include "Process.h"
     49 #include "Asec.h"
     50 #include "cryptfs.h"
     51 
     52 #define MASS_STORAGE_FILE_PATH  "/sys/class/android_usb/android0/f_mass_storage/lun/file"
     53 
     54 VolumeManager *VolumeManager::sInstance = NULL;
     55 
     56 VolumeManager *VolumeManager::Instance() {
     57     if (!sInstance)
     58         sInstance = new VolumeManager();
     59     return sInstance;
     60 }
     61 
     62 VolumeManager::VolumeManager() {
     63     mDebug = false;
     64     mVolumes = new VolumeCollection();
     65     mActiveContainers = new AsecIdCollection();
     66     mBroadcaster = NULL;
     67     mUmsSharingCount = 0;
     68     mSavedDirtyRatio = -1;
     69     // set dirty ratio to 0 when UMS is active
     70     mUmsDirtyRatio = 0;
     71     mVolManagerDisabled = 0;
     72 }
     73 
     74 VolumeManager::~VolumeManager() {
     75     delete mVolumes;
     76     delete mActiveContainers;
     77 }
     78 
     79 char *VolumeManager::asecHash(const char *id, char *buffer, size_t len) {
     80     static const char* digits = "0123456789abcdef";
     81 
     82     unsigned char sig[MD5_DIGEST_LENGTH];
     83 
     84     if (buffer == NULL) {
     85         SLOGE("Destination buffer is NULL");
     86         errno = ESPIPE;
     87         return NULL;
     88     } else if (id == NULL) {
     89         SLOGE("Source buffer is NULL");
     90         errno = ESPIPE;
     91         return NULL;
     92     } else if (len < MD5_ASCII_LENGTH_PLUS_NULL) {
     93         SLOGE("Target hash buffer size < %d bytes (%d)",
     94                 MD5_ASCII_LENGTH_PLUS_NULL, len);
     95         errno = ESPIPE;
     96         return NULL;
     97     }
     98 
     99     MD5(reinterpret_cast<const unsigned char*>(id), strlen(id), sig);
    100 
    101     char *p = buffer;
    102     for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
    103         *p++ = digits[sig[i] >> 4];
    104         *p++ = digits[sig[i] & 0x0F];
    105     }
    106     *p = '\0';
    107 
    108     return buffer;
    109 }
    110 
    111 void VolumeManager::setDebug(bool enable) {
    112     mDebug = enable;
    113     VolumeCollection::iterator it;
    114     for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
    115         (*it)->setDebug(enable);
    116     }
    117 }
    118 
    119 int VolumeManager::start() {
    120     return 0;
    121 }
    122 
    123 int VolumeManager::stop() {
    124     return 0;
    125 }
    126 
    127 int VolumeManager::addVolume(Volume *v) {
    128     mVolumes->push_back(v);
    129     return 0;
    130 }
    131 
    132 void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
    133     const char *devpath = evt->findParam("DEVPATH");
    134 
    135     /* Lookup a volume to handle this device */
    136     VolumeCollection::iterator it;
    137     bool hit = false;
    138     for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
    139         if (!(*it)->handleBlockEvent(evt)) {
    140 #ifdef NETLINK_DEBUG
    141             SLOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel());
    142 #endif
    143             hit = true;
    144             break;
    145         }
    146     }
    147 
    148     if (!hit) {
    149 #ifdef NETLINK_DEBUG
    150         SLOGW("No volumes handled block event for '%s'", devpath);
    151 #endif
    152     }
    153 }
    154 
    155 int VolumeManager::listVolumes(SocketClient *cli) {
    156     VolumeCollection::iterator i;
    157 
    158     for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
    159         char *buffer;
    160         asprintf(&buffer, "%s %s %d",
    161                  (*i)->getLabel(), (*i)->getMountpoint(),
    162                  (*i)->getState());
    163         cli->sendMsg(ResponseCode::VolumeListResult, buffer, false);
    164         free(buffer);
    165     }
    166     cli->sendMsg(ResponseCode::CommandOkay, "Volumes listed.", false);
    167     return 0;
    168 }
    169 
    170 int VolumeManager::formatVolume(const char *label) {
    171     Volume *v = lookupVolume(label);
    172 
    173     if (!v) {
    174         errno = ENOENT;
    175         return -1;
    176     }
    177 
    178     if (mVolManagerDisabled) {
    179         errno = EBUSY;
    180         return -1;
    181     }
    182 
    183     return v->formatVol();
    184 }
    185 
    186 int VolumeManager::getObbMountPath(const char *sourceFile, char *mountPath, int mountPathLen) {
    187     char idHash[33];
    188     if (!asecHash(sourceFile, idHash, sizeof(idHash))) {
    189         SLOGE("Hash of '%s' failed (%s)", sourceFile, strerror(errno));
    190         return -1;
    191     }
    192 
    193     memset(mountPath, 0, mountPathLen);
    194     snprintf(mountPath, mountPathLen, "%s/%s", Volume::LOOPDIR, idHash);
    195 
    196     if (access(mountPath, F_OK)) {
    197         errno = ENOENT;
    198         return -1;
    199     }
    200 
    201     return 0;
    202 }
    203 
    204 int VolumeManager::getAsecMountPath(const char *id, char *buffer, int maxlen) {
    205     char asecFileName[255];
    206 
    207     if (findAsec(id, asecFileName, sizeof(asecFileName))) {
    208         SLOGE("Couldn't find ASEC %s", id);
    209         return -1;
    210     }
    211 
    212     memset(buffer, 0, maxlen);
    213     if (access(asecFileName, F_OK)) {
    214         errno = ENOENT;
    215         return -1;
    216     }
    217 
    218     snprintf(buffer, maxlen, "%s/%s", Volume::ASECDIR, id);
    219     return 0;
    220 }
    221 
    222 int VolumeManager::getAsecFilesystemPath(const char *id, char *buffer, int maxlen) {
    223     char asecFileName[255];
    224 
    225     if (findAsec(id, asecFileName, sizeof(asecFileName))) {
    226         SLOGE("Couldn't find ASEC %s", id);
    227         return -1;
    228     }
    229 
    230     memset(buffer, 0, maxlen);
    231     if (access(asecFileName, F_OK)) {
    232         errno = ENOENT;
    233         return -1;
    234     }
    235 
    236     snprintf(buffer, maxlen, "%s", asecFileName);
    237     return 0;
    238 }
    239 
    240 int VolumeManager::createAsec(const char *id, unsigned int numSectors, const char *fstype,
    241         const char *key, const int ownerUid, bool isExternal) {
    242     struct asec_superblock sb;
    243     memset(&sb, 0, sizeof(sb));
    244 
    245     const bool wantFilesystem = strcmp(fstype, "none");
    246     bool usingExt4 = false;
    247     if (wantFilesystem) {
    248         usingExt4 = !strcmp(fstype, "ext4");
    249         if (usingExt4) {
    250             sb.c_opts |= ASEC_SB_C_OPTS_EXT4;
    251         } else if (strcmp(fstype, "fat")) {
    252             SLOGE("Invalid filesystem type %s", fstype);
    253             errno = EINVAL;
    254             return -1;
    255         }
    256     }
    257 
    258     sb.magic = ASEC_SB_MAGIC;
    259     sb.ver = ASEC_SB_VER;
    260 
    261     if (numSectors < ((1024*1024)/512)) {
    262         SLOGE("Invalid container size specified (%d sectors)", numSectors);
    263         errno = EINVAL;
    264         return -1;
    265     }
    266 
    267     if (lookupVolume(id)) {
    268         SLOGE("ASEC id '%s' currently exists", id);
    269         errno = EADDRINUSE;
    270         return -1;
    271     }
    272 
    273     char asecFileName[255];
    274 
    275     if (!findAsec(id, asecFileName, sizeof(asecFileName))) {
    276         SLOGE("ASEC file '%s' currently exists - destroy it first! (%s)",
    277                 asecFileName, strerror(errno));
    278         errno = EADDRINUSE;
    279         return -1;
    280     }
    281 
    282     const char *asecDir = isExternal ? Volume::SEC_ASECDIR_EXT : Volume::SEC_ASECDIR_INT;
    283 
    284     snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", asecDir, id);
    285 
    286     if (!access(asecFileName, F_OK)) {
    287         SLOGE("ASEC file '%s' currently exists - destroy it first! (%s)",
    288                 asecFileName, strerror(errno));
    289         errno = EADDRINUSE;
    290         return -1;
    291     }
    292 
    293     /*
    294      * Add some headroom
    295      */
    296     unsigned fatSize = (((numSectors * 4) / 512) + 1) * 2;
    297     unsigned numImgSectors = numSectors + fatSize + 2;
    298 
    299     if (numImgSectors % 63) {
    300         numImgSectors += (63 - (numImgSectors % 63));
    301     }
    302 
    303     // Add +1 for our superblock which is at the end
    304     if (Loop::createImageFile(asecFileName, numImgSectors + 1)) {
    305         SLOGE("ASEC image file creation failed (%s)", strerror(errno));
    306         return -1;
    307     }
    308 
    309     char idHash[33];
    310     if (!asecHash(id, idHash, sizeof(idHash))) {
    311         SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
    312         unlink(asecFileName);
    313         return -1;
    314     }
    315 
    316     char loopDevice[255];
    317     if (Loop::create(idHash, asecFileName, loopDevice, sizeof(loopDevice))) {
    318         SLOGE("ASEC loop device creation failed (%s)", strerror(errno));
    319         unlink(asecFileName);
    320         return -1;
    321     }
    322 
    323     char dmDevice[255];
    324     bool cleanupDm = false;
    325 
    326     if (strcmp(key, "none")) {
    327         // XXX: This is all we support for now
    328         sb.c_cipher = ASEC_SB_C_CIPHER_TWOFISH;
    329         if (Devmapper::create(idHash, loopDevice, key, numImgSectors, dmDevice,
    330                              sizeof(dmDevice))) {
    331             SLOGE("ASEC device mapping failed (%s)", strerror(errno));
    332             Loop::destroyByDevice(loopDevice);
    333             unlink(asecFileName);
    334             return -1;
    335         }
    336         cleanupDm = true;
    337     } else {
    338         sb.c_cipher = ASEC_SB_C_CIPHER_NONE;
    339         strcpy(dmDevice, loopDevice);
    340     }
    341 
    342     /*
    343      * Drop down the superblock at the end of the file
    344      */
    345 
    346     int sbfd = open(loopDevice, O_RDWR);
    347     if (sbfd < 0) {
    348         SLOGE("Failed to open new DM device for superblock write (%s)", strerror(errno));
    349         if (cleanupDm) {
    350             Devmapper::destroy(idHash);
    351         }
    352         Loop::destroyByDevice(loopDevice);
    353         unlink(asecFileName);
    354         return -1;
    355     }
    356 
    357     if (lseek(sbfd, (numImgSectors * 512), SEEK_SET) < 0) {
    358         close(sbfd);
    359         SLOGE("Failed to lseek for superblock (%s)", strerror(errno));
    360         if (cleanupDm) {
    361             Devmapper::destroy(idHash);
    362         }
    363         Loop::destroyByDevice(loopDevice);
    364         unlink(asecFileName);
    365         return -1;
    366     }
    367 
    368     if (write(sbfd, &sb, sizeof(sb)) != sizeof(sb)) {
    369         close(sbfd);
    370         SLOGE("Failed to write superblock (%s)", strerror(errno));
    371         if (cleanupDm) {
    372             Devmapper::destroy(idHash);
    373         }
    374         Loop::destroyByDevice(loopDevice);
    375         unlink(asecFileName);
    376         return -1;
    377     }
    378     close(sbfd);
    379 
    380     if (wantFilesystem) {
    381         int formatStatus;
    382         if (usingExt4) {
    383             formatStatus = Ext4::format(dmDevice);
    384         } else {
    385             formatStatus = Fat::format(dmDevice, numImgSectors);
    386         }
    387 
    388         if (formatStatus < 0) {
    389             SLOGE("ASEC fs format failed (%s)", strerror(errno));
    390             if (cleanupDm) {
    391                 Devmapper::destroy(idHash);
    392             }
    393             Loop::destroyByDevice(loopDevice);
    394             unlink(asecFileName);
    395             return -1;
    396         }
    397 
    398         char mountPoint[255];
    399 
    400         snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
    401         if (mkdir(mountPoint, 0000)) {
    402             if (errno != EEXIST) {
    403                 SLOGE("Mountpoint creation failed (%s)", strerror(errno));
    404                 if (cleanupDm) {
    405                     Devmapper::destroy(idHash);
    406                 }
    407                 Loop::destroyByDevice(loopDevice);
    408                 unlink(asecFileName);
    409                 return -1;
    410             }
    411         }
    412 
    413         int mountStatus;
    414         if (usingExt4) {
    415             mountStatus = Ext4::doMount(dmDevice, mountPoint, false, false, false);
    416         } else {
    417             mountStatus = Fat::doMount(dmDevice, mountPoint, false, false, false, ownerUid, 0, 0000,
    418                     false);
    419         }
    420 
    421         if (mountStatus) {
    422             SLOGE("ASEC FAT mount failed (%s)", strerror(errno));
    423             if (cleanupDm) {
    424                 Devmapper::destroy(idHash);
    425             }
    426             Loop::destroyByDevice(loopDevice);
    427             unlink(asecFileName);
    428             return -1;
    429         }
    430 
    431         if (usingExt4) {
    432             int dirfd = open(mountPoint, O_DIRECTORY);
    433             if (dirfd >= 0) {
    434                 if (fchown(dirfd, ownerUid, AID_SYSTEM)
    435                         || fchmod(dirfd, S_IRUSR | S_IWUSR | S_IXUSR | S_ISGID | S_IRGRP | S_IXGRP)) {
    436                     SLOGI("Cannot chown/chmod new ASEC mount point %s", mountPoint);
    437                 }
    438                 close(dirfd);
    439             }
    440         }
    441     } else {
    442         SLOGI("Created raw secure container %s (no filesystem)", id);
    443     }
    444 
    445     mActiveContainers->push_back(new ContainerData(strdup(id), ASEC));
    446     return 0;
    447 }
    448 
    449 int VolumeManager::finalizeAsec(const char *id) {
    450     char asecFileName[255];
    451     char loopDevice[255];
    452     char mountPoint[255];
    453 
    454     if (findAsec(id, asecFileName, sizeof(asecFileName))) {
    455         SLOGE("Couldn't find ASEC %s", id);
    456         return -1;
    457     }
    458 
    459     char idHash[33];
    460     if (!asecHash(id, idHash, sizeof(idHash))) {
    461         SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
    462         return -1;
    463     }
    464 
    465     if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
    466         SLOGE("Unable to finalize %s (%s)", id, strerror(errno));
    467         return -1;
    468     }
    469 
    470     unsigned int nr_sec = 0;
    471     struct asec_superblock sb;
    472 
    473     if (Loop::lookupInfo(loopDevice, &sb, &nr_sec)) {
    474         return -1;
    475     }
    476 
    477     snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
    478 
    479     int result = 0;
    480     if (sb.c_opts & ASEC_SB_C_OPTS_EXT4) {
    481         result = Ext4::doMount(loopDevice, mountPoint, true, true, true);
    482     } else {
    483         result = Fat::doMount(loopDevice, mountPoint, true, true, true, 0, 0, 0227, false);
    484     }
    485 
    486     if (result) {
    487         SLOGE("ASEC finalize mount failed (%s)", strerror(errno));
    488         return -1;
    489     }
    490 
    491     if (mDebug) {
    492         SLOGD("ASEC %s finalized", id);
    493     }
    494     return 0;
    495 }
    496 
    497 int VolumeManager::fixupAsecPermissions(const char *id, gid_t gid, const char* filename) {
    498     char asecFileName[255];
    499     char loopDevice[255];
    500     char mountPoint[255];
    501 
    502     if (gid < AID_APP) {
    503         SLOGE("Group ID is not in application range");
    504         return -1;
    505     }
    506 
    507     if (findAsec(id, asecFileName, sizeof(asecFileName))) {
    508         SLOGE("Couldn't find ASEC %s", id);
    509         return -1;
    510     }
    511 
    512     char idHash[33];
    513     if (!asecHash(id, idHash, sizeof(idHash))) {
    514         SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
    515         return -1;
    516     }
    517 
    518     if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
    519         SLOGE("Unable fix permissions during lookup on %s (%s)", id, strerror(errno));
    520         return -1;
    521     }
    522 
    523     unsigned int nr_sec = 0;
    524     struct asec_superblock sb;
    525 
    526     if (Loop::lookupInfo(loopDevice, &sb, &nr_sec)) {
    527         return -1;
    528     }
    529 
    530     snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
    531 
    532     int result = 0;
    533     if ((sb.c_opts & ASEC_SB_C_OPTS_EXT4) == 0) {
    534         return 0;
    535     }
    536 
    537     int ret = Ext4::doMount(loopDevice, mountPoint,
    538             false /* read-only */,
    539             true  /* remount */,
    540             false /* executable */);
    541     if (ret) {
    542         SLOGE("Unable remount to fix permissions for %s (%s)", id, strerror(errno));
    543         return -1;
    544     }
    545 
    546     char *paths[] = { mountPoint, NULL };
    547 
    548     FTS *fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL);
    549     if (fts) {
    550         // Traverse the entire hierarchy and chown to system UID.
    551         for (FTSENT *ftsent = fts_read(fts); ftsent != NULL; ftsent = fts_read(fts)) {
    552             // We don't care about the lost+found directory.
    553             if (!strcmp(ftsent->fts_name, "lost+found")) {
    554                 continue;
    555             }
    556 
    557             /*
    558              * There can only be one file marked as private right now.
    559              * This should be more robust, but it satisfies the requirements
    560              * we have for right now.
    561              */
    562             const bool privateFile = !strcmp(ftsent->fts_name, filename);
    563 
    564             int fd = open(ftsent->fts_accpath, O_NOFOLLOW);
    565             if (fd < 0) {
    566                 SLOGE("Couldn't open file %s: %s", ftsent->fts_accpath, strerror(errno));
    567                 result = -1;
    568                 continue;
    569             }
    570 
    571             result |= fchown(fd, AID_SYSTEM, privateFile? gid : AID_SYSTEM);
    572 
    573             if (ftsent->fts_info & FTS_D) {
    574                 result |= fchmod(fd, 0755);
    575             } else if (ftsent->fts_info & FTS_F) {
    576                 result |= fchmod(fd, privateFile ? 0640 : 0644);
    577             }
    578             close(fd);
    579         }
    580         fts_close(fts);
    581 
    582         // Finally make the directory readable by everyone.
    583         int dirfd = open(mountPoint, O_DIRECTORY);
    584         if (dirfd < 0 || fchmod(dirfd, 0755)) {
    585             SLOGE("Couldn't change owner of existing directory %s: %s", mountPoint, strerror(errno));
    586             result |= -1;
    587         }
    588         close(dirfd);
    589     } else {
    590         result |= -1;
    591     }
    592 
    593     result |= Ext4::doMount(loopDevice, mountPoint,
    594             true /* read-only */,
    595             true /* remount */,
    596             true /* execute */);
    597 
    598     if (result) {
    599         SLOGE("ASEC fix permissions failed (%s)", strerror(errno));
    600         return -1;
    601     }
    602 
    603     if (mDebug) {
    604         SLOGD("ASEC %s permissions fixed", id);
    605     }
    606     return 0;
    607 }
    608 
    609 int VolumeManager::renameAsec(const char *id1, const char *id2) {
    610     char asecFilename1[255];
    611     char *asecFilename2;
    612     char mountPoint[255];
    613 
    614     const char *dir;
    615 
    616     if (findAsec(id1, asecFilename1, sizeof(asecFilename1), &dir)) {
    617         SLOGE("Couldn't find ASEC %s", id1);
    618         return -1;
    619     }
    620 
    621     asprintf(&asecFilename2, "%s/%s.asec", dir, id2);
    622 
    623     snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id1);
    624     if (isMountpointMounted(mountPoint)) {
    625         SLOGW("Rename attempt when src mounted");
    626         errno = EBUSY;
    627         goto out_err;
    628     }
    629 
    630     snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id2);
    631     if (isMountpointMounted(mountPoint)) {
    632         SLOGW("Rename attempt when dst mounted");
    633         errno = EBUSY;
    634         goto out_err;
    635     }
    636 
    637     if (!access(asecFilename2, F_OK)) {
    638         SLOGE("Rename attempt when dst exists");
    639         errno = EADDRINUSE;
    640         goto out_err;
    641     }
    642 
    643     if (rename(asecFilename1, asecFilename2)) {
    644         SLOGE("Rename of '%s' to '%s' failed (%s)", asecFilename1, asecFilename2, strerror(errno));
    645         goto out_err;
    646     }
    647 
    648     free(asecFilename2);
    649     return 0;
    650 
    651 out_err:
    652     free(asecFilename2);
    653     return -1;
    654 }
    655 
    656 #define UNMOUNT_RETRIES 5
    657 #define UNMOUNT_SLEEP_BETWEEN_RETRY_MS (1000 * 1000)
    658 int VolumeManager::unmountAsec(const char *id, bool force) {
    659     char asecFileName[255];
    660     char mountPoint[255];
    661 
    662     if (findAsec(id, asecFileName, sizeof(asecFileName))) {
    663         SLOGE("Couldn't find ASEC %s", id);
    664         return -1;
    665     }
    666 
    667     snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
    668 
    669     char idHash[33];
    670     if (!asecHash(id, idHash, sizeof(idHash))) {
    671         SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
    672         return -1;
    673     }
    674 
    675     return unmountLoopImage(id, idHash, asecFileName, mountPoint, force);
    676 }
    677 
    678 int VolumeManager::unmountObb(const char *fileName, bool force) {
    679     char mountPoint[255];
    680 
    681     char idHash[33];
    682     if (!asecHash(fileName, idHash, sizeof(idHash))) {
    683         SLOGE("Hash of '%s' failed (%s)", fileName, strerror(errno));
    684         return -1;
    685     }
    686 
    687     snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::LOOPDIR, idHash);
    688 
    689     return unmountLoopImage(fileName, idHash, fileName, mountPoint, force);
    690 }
    691 
    692 int VolumeManager::unmountLoopImage(const char *id, const char *idHash,
    693         const char *fileName, const char *mountPoint, bool force) {
    694     if (!isMountpointMounted(mountPoint)) {
    695         SLOGE("Unmount request for %s when not mounted", id);
    696         errno = ENOENT;
    697         return -1;
    698     }
    699 
    700     int i, rc;
    701     for (i = 1; i <= UNMOUNT_RETRIES; i++) {
    702         rc = umount(mountPoint);
    703         if (!rc) {
    704             break;
    705         }
    706         if (rc && (errno == EINVAL || errno == ENOENT)) {
    707             SLOGI("Container %s unmounted OK", id);
    708             rc = 0;
    709             break;
    710         }
    711         SLOGW("%s unmount attempt %d failed (%s)",
    712               id, i, strerror(errno));
    713 
    714         int action = 0; // default is to just complain
    715 
    716         if (force) {
    717             if (i > (UNMOUNT_RETRIES - 2))
    718                 action = 2; // SIGKILL
    719             else if (i > (UNMOUNT_RETRIES - 3))
    720                 action = 1; // SIGHUP
    721         }
    722 
    723         Process::killProcessesWithOpenFiles(mountPoint, action);
    724         usleep(UNMOUNT_SLEEP_BETWEEN_RETRY_MS);
    725     }
    726 
    727     if (rc) {
    728         errno = EBUSY;
    729         SLOGE("Failed to unmount container %s (%s)", id, strerror(errno));
    730         return -1;
    731     }
    732 
    733     int retries = 10;
    734 
    735     while(retries--) {
    736         if (!rmdir(mountPoint)) {
    737             break;
    738         }
    739 
    740         SLOGW("Failed to rmdir %s (%s)", mountPoint, strerror(errno));
    741         usleep(UNMOUNT_SLEEP_BETWEEN_RETRY_MS);
    742     }
    743 
    744     if (!retries) {
    745         SLOGE("Timed out trying to rmdir %s (%s)", mountPoint, strerror(errno));
    746     }
    747 
    748     if (Devmapper::destroy(idHash) && errno != ENXIO) {
    749         SLOGE("Failed to destroy devmapper instance (%s)", strerror(errno));
    750     }
    751 
    752     char loopDevice[255];
    753     if (!Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
    754         Loop::destroyByDevice(loopDevice);
    755     } else {
    756         SLOGW("Failed to find loop device for {%s} (%s)", fileName, strerror(errno));
    757     }
    758 
    759     AsecIdCollection::iterator it;
    760     for (it = mActiveContainers->begin(); it != mActiveContainers->end(); ++it) {
    761         ContainerData* cd = *it;
    762         if (!strcmp(cd->id, id)) {
    763             free(*it);
    764             mActiveContainers->erase(it);
    765             break;
    766         }
    767     }
    768     if (it == mActiveContainers->end()) {
    769         SLOGW("mActiveContainers is inconsistent!");
    770     }
    771     return 0;
    772 }
    773 
    774 int VolumeManager::destroyAsec(const char *id, bool force) {
    775     char asecFileName[255];
    776     char mountPoint[255];
    777 
    778     if (findAsec(id, asecFileName, sizeof(asecFileName))) {
    779         SLOGE("Couldn't find ASEC %s", id);
    780         return -1;
    781     }
    782 
    783     snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
    784 
    785     if (isMountpointMounted(mountPoint)) {
    786         if (mDebug) {
    787             SLOGD("Unmounting container before destroy");
    788         }
    789         if (unmountAsec(id, force)) {
    790             SLOGE("Failed to unmount asec %s for destroy (%s)", id, strerror(errno));
    791             return -1;
    792         }
    793     }
    794 
    795     if (unlink(asecFileName)) {
    796         SLOGE("Failed to unlink asec '%s' (%s)", asecFileName, strerror(errno));
    797         return -1;
    798     }
    799 
    800     if (mDebug) {
    801         SLOGD("ASEC %s destroyed", id);
    802     }
    803     return 0;
    804 }
    805 
    806 bool VolumeManager::isAsecInDirectory(const char *dir, const char *asecName) const {
    807     int dirfd = open(dir, O_DIRECTORY);
    808     if (dirfd < 0) {
    809         SLOGE("Couldn't open internal ASEC dir (%s)", strerror(errno));
    810         return -1;
    811     }
    812 
    813     bool ret = false;
    814 
    815     if (!faccessat(dirfd, asecName, F_OK, AT_SYMLINK_NOFOLLOW)) {
    816         ret = true;
    817     }
    818 
    819     close(dirfd);
    820 
    821     return ret;
    822 }
    823 
    824 int VolumeManager::findAsec(const char *id, char *asecPath, size_t asecPathLen,
    825         const char **directory) const {
    826     int dirfd, fd;
    827     const int idLen = strlen(id);
    828     char *asecName;
    829 
    830     if (asprintf(&asecName, "%s.asec", id) < 0) {
    831         SLOGE("Couldn't allocate string to write ASEC name");
    832         return -1;
    833     }
    834 
    835     const char *dir;
    836     if (isAsecInDirectory(Volume::SEC_ASECDIR_INT, asecName)) {
    837         dir = Volume::SEC_ASECDIR_INT;
    838     } else if (isAsecInDirectory(Volume::SEC_ASECDIR_EXT, asecName)) {
    839         dir = Volume::SEC_ASECDIR_EXT;
    840     } else {
    841         free(asecName);
    842         return -1;
    843     }
    844 
    845     if (directory != NULL) {
    846         *directory = dir;
    847     }
    848 
    849     if (asecPath != NULL) {
    850         int written = snprintf(asecPath, asecPathLen, "%s/%s", dir, asecName);
    851         if (written < 0 || static_cast<size_t>(written) >= asecPathLen) {
    852             free(asecName);
    853             return -1;
    854         }
    855     }
    856 
    857     free(asecName);
    858     return 0;
    859 }
    860 
    861 int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid) {
    862     char asecFileName[255];
    863     char mountPoint[255];
    864 
    865     if (findAsec(id, asecFileName, sizeof(asecFileName))) {
    866         SLOGE("Couldn't find ASEC %s", id);
    867         return -1;
    868     }
    869 
    870     snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
    871 
    872     if (isMountpointMounted(mountPoint)) {
    873         SLOGE("ASEC %s already mounted", id);
    874         errno = EBUSY;
    875         return -1;
    876     }
    877 
    878     char idHash[33];
    879     if (!asecHash(id, idHash, sizeof(idHash))) {
    880         SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
    881         return -1;
    882     }
    883 
    884     char loopDevice[255];
    885     if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
    886         if (Loop::create(idHash, asecFileName, loopDevice, sizeof(loopDevice))) {
    887             SLOGE("ASEC loop device creation failed (%s)", strerror(errno));
    888             return -1;
    889         }
    890         if (mDebug) {
    891             SLOGD("New loop device created at %s", loopDevice);
    892         }
    893     } else {
    894         if (mDebug) {
    895             SLOGD("Found active loopback for %s at %s", asecFileName, loopDevice);
    896         }
    897     }
    898 
    899     char dmDevice[255];
    900     bool cleanupDm = false;
    901     int fd;
    902     unsigned int nr_sec = 0;
    903     struct asec_superblock sb;
    904 
    905     if (Loop::lookupInfo(loopDevice, &sb, &nr_sec)) {
    906         return -1;
    907     }
    908 
    909     if (mDebug) {
    910         SLOGD("Container sb magic/ver (%.8x/%.2x)", sb.magic, sb.ver);
    911     }
    912     if (sb.magic != ASEC_SB_MAGIC || sb.ver != ASEC_SB_VER) {
    913         SLOGE("Bad container magic/version (%.8x/%.2x)", sb.magic, sb.ver);
    914         Loop::destroyByDevice(loopDevice);
    915         errno = EMEDIUMTYPE;
    916         return -1;
    917     }
    918     nr_sec--; // We don't want the devmapping to extend onto our superblock
    919 
    920     if (strcmp(key, "none")) {
    921         if (Devmapper::lookupActive(idHash, dmDevice, sizeof(dmDevice))) {
    922             if (Devmapper::create(idHash, loopDevice, key, nr_sec,
    923                                   dmDevice, sizeof(dmDevice))) {
    924                 SLOGE("ASEC device mapping failed (%s)", strerror(errno));
    925                 Loop::destroyByDevice(loopDevice);
    926                 return -1;
    927             }
    928             if (mDebug) {
    929                 SLOGD("New devmapper instance created at %s", dmDevice);
    930             }
    931         } else {
    932             if (mDebug) {
    933                 SLOGD("Found active devmapper for %s at %s", asecFileName, dmDevice);
    934             }
    935         }
    936         cleanupDm = true;
    937     } else {
    938         strcpy(dmDevice, loopDevice);
    939     }
    940 
    941     if (mkdir(mountPoint, 0000)) {
    942         if (errno != EEXIST) {
    943             SLOGE("Mountpoint creation failed (%s)", strerror(errno));
    944             if (cleanupDm) {
    945                 Devmapper::destroy(idHash);
    946             }
    947             Loop::destroyByDevice(loopDevice);
    948             return -1;
    949         }
    950     }
    951 
    952     /*
    953      * The device mapper node needs to be created. Sometimes it takes a
    954      * while. Wait for up to 1 second. We could also inspect incoming uevents,
    955      * but that would take more effort.
    956      */
    957     int tries = 25;
    958     while (tries--) {
    959         if (!access(dmDevice, F_OK) || errno != ENOENT) {
    960             break;
    961         }
    962         usleep(40 * 1000);
    963     }
    964 
    965     int result;
    966     if (sb.c_opts & ASEC_SB_C_OPTS_EXT4) {
    967         result = Ext4::doMount(dmDevice, mountPoint, true, false, true);
    968     } else {
    969         result = Fat::doMount(dmDevice, mountPoint, true, false, true, ownerUid, 0, 0222, false);
    970     }
    971 
    972     if (result) {
    973         SLOGE("ASEC mount failed (%s)", strerror(errno));
    974         if (cleanupDm) {
    975             Devmapper::destroy(idHash);
    976         }
    977         Loop::destroyByDevice(loopDevice);
    978         return -1;
    979     }
    980 
    981     mActiveContainers->push_back(new ContainerData(strdup(id), ASEC));
    982     if (mDebug) {
    983         SLOGD("ASEC %s mounted", id);
    984     }
    985     return 0;
    986 }
    987 
    988 Volume* VolumeManager::getVolumeForFile(const char *fileName) {
    989     VolumeCollection::iterator i;
    990 
    991     for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
    992         const char* mountPoint = (*i)->getMountpoint();
    993         if (!strncmp(fileName, mountPoint, strlen(mountPoint))) {
    994             return *i;
    995         }
    996     }
    997 
    998     return NULL;
    999 }
   1000 
   1001 /**
   1002  * Mounts an image file <code>img</code>.
   1003  */
   1004 int VolumeManager::mountObb(const char *img, const char *key, int ownerUid) {
   1005     char mountPoint[255];
   1006 
   1007     char idHash[33];
   1008     if (!asecHash(img, idHash, sizeof(idHash))) {
   1009         SLOGE("Hash of '%s' failed (%s)", img, strerror(errno));
   1010         return -1;
   1011     }
   1012 
   1013     snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::LOOPDIR, idHash);
   1014 
   1015     if (isMountpointMounted(mountPoint)) {
   1016         SLOGE("Image %s already mounted", img);
   1017         errno = EBUSY;
   1018         return -1;
   1019     }
   1020 
   1021     char loopDevice[255];
   1022     if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
   1023         if (Loop::create(idHash, img, loopDevice, sizeof(loopDevice))) {
   1024             SLOGE("Image loop device creation failed (%s)", strerror(errno));
   1025             return -1;
   1026         }
   1027         if (mDebug) {
   1028             SLOGD("New loop device created at %s", loopDevice);
   1029         }
   1030     } else {
   1031         if (mDebug) {
   1032             SLOGD("Found active loopback for %s at %s", img, loopDevice);
   1033         }
   1034     }
   1035 
   1036     char dmDevice[255];
   1037     bool cleanupDm = false;
   1038     int fd;
   1039     unsigned int nr_sec = 0;
   1040 
   1041     if ((fd = open(loopDevice, O_RDWR)) < 0) {
   1042         SLOGE("Failed to open loopdevice (%s)", strerror(errno));
   1043         Loop::destroyByDevice(loopDevice);
   1044         return -1;
   1045     }
   1046 
   1047     if (ioctl(fd, BLKGETSIZE, &nr_sec)) {
   1048         SLOGE("Failed to get loop size (%s)", strerror(errno));
   1049         Loop::destroyByDevice(loopDevice);
   1050         close(fd);
   1051         return -1;
   1052     }
   1053 
   1054     close(fd);
   1055 
   1056     if (strcmp(key, "none")) {
   1057         if (Devmapper::lookupActive(idHash, dmDevice, sizeof(dmDevice))) {
   1058             if (Devmapper::create(idHash, loopDevice, key, nr_sec,
   1059                                   dmDevice, sizeof(dmDevice))) {
   1060                 SLOGE("ASEC device mapping failed (%s)", strerror(errno));
   1061                 Loop::destroyByDevice(loopDevice);
   1062                 return -1;
   1063             }
   1064             if (mDebug) {
   1065                 SLOGD("New devmapper instance created at %s", dmDevice);
   1066             }
   1067         } else {
   1068             if (mDebug) {
   1069                 SLOGD("Found active devmapper for %s at %s", img, dmDevice);
   1070             }
   1071         }
   1072         cleanupDm = true;
   1073     } else {
   1074         strcpy(dmDevice, loopDevice);
   1075     }
   1076 
   1077     if (mkdir(mountPoint, 0755)) {
   1078         if (errno != EEXIST) {
   1079             SLOGE("Mountpoint creation failed (%s)", strerror(errno));
   1080             if (cleanupDm) {
   1081                 Devmapper::destroy(idHash);
   1082             }
   1083             Loop::destroyByDevice(loopDevice);
   1084             return -1;
   1085         }
   1086     }
   1087 
   1088     if (Fat::doMount(dmDevice, mountPoint, true, false, true, ownerUid, 0,
   1089                      0227, false)) {
   1090         SLOGE("Image mount failed (%s)", strerror(errno));
   1091         if (cleanupDm) {
   1092             Devmapper::destroy(idHash);
   1093         }
   1094         Loop::destroyByDevice(loopDevice);
   1095         return -1;
   1096     }
   1097 
   1098     mActiveContainers->push_back(new ContainerData(strdup(img), OBB));
   1099     if (mDebug) {
   1100         SLOGD("Image %s mounted", img);
   1101     }
   1102     return 0;
   1103 }
   1104 
   1105 int VolumeManager::mountVolume(const char *label) {
   1106     Volume *v = lookupVolume(label);
   1107 
   1108     if (!v) {
   1109         errno = ENOENT;
   1110         return -1;
   1111     }
   1112 
   1113     return v->mountVol();
   1114 }
   1115 
   1116 int VolumeManager::listMountedObbs(SocketClient* cli) {
   1117     char device[256];
   1118     char mount_path[256];
   1119     char rest[256];
   1120     FILE *fp;
   1121     char line[1024];
   1122 
   1123     if (!(fp = fopen("/proc/mounts", "r"))) {
   1124         SLOGE("Error opening /proc/mounts (%s)", strerror(errno));
   1125         return -1;
   1126     }
   1127 
   1128     // Create a string to compare against that has a trailing slash
   1129     int loopDirLen = strlen(Volume::LOOPDIR);
   1130     char loopDir[loopDirLen + 2];
   1131     strcpy(loopDir, Volume::LOOPDIR);
   1132     loopDir[loopDirLen++] = '/';
   1133     loopDir[loopDirLen] = '\0';
   1134 
   1135     while(fgets(line, sizeof(line), fp)) {
   1136         line[strlen(line)-1] = '\0';
   1137 
   1138         /*
   1139          * Should look like:
   1140          * /dev/block/loop0 /mnt/obb/fc99df1323fd36424f864dcb76b76d65 ...
   1141          */
   1142         sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
   1143 
   1144         if (!strncmp(mount_path, loopDir, loopDirLen)) {
   1145             int fd = open(device, O_RDONLY);
   1146             if (fd >= 0) {
   1147                 struct loop_info64 li;
   1148                 if (ioctl(fd, LOOP_GET_STATUS64, &li) >= 0) {
   1149                     cli->sendMsg(ResponseCode::AsecListResult,
   1150                             (const char*) li.lo_file_name, false);
   1151                 }
   1152                 close(fd);
   1153             }
   1154         }
   1155     }
   1156 
   1157     fclose(fp);
   1158     return 0;
   1159 }
   1160 
   1161 int VolumeManager::shareEnabled(const char *label, const char *method, bool *enabled) {
   1162     Volume *v = lookupVolume(label);
   1163 
   1164     if (!v) {
   1165         errno = ENOENT;
   1166         return -1;
   1167     }
   1168 
   1169     if (strcmp(method, "ums")) {
   1170         errno = ENOSYS;
   1171         return -1;
   1172     }
   1173 
   1174     if (v->getState() != Volume::State_Shared) {
   1175         *enabled = false;
   1176     } else {
   1177         *enabled = true;
   1178     }
   1179     return 0;
   1180 }
   1181 
   1182 int VolumeManager::shareVolume(const char *label, const char *method) {
   1183     Volume *v = lookupVolume(label);
   1184 
   1185     if (!v) {
   1186         errno = ENOENT;
   1187         return -1;
   1188     }
   1189 
   1190     /*
   1191      * Eventually, we'll want to support additional share back-ends,
   1192      * some of which may work while the media is mounted. For now,
   1193      * we just support UMS
   1194      */
   1195     if (strcmp(method, "ums")) {
   1196         errno = ENOSYS;
   1197         return -1;
   1198     }
   1199 
   1200     if (v->getState() == Volume::State_NoMedia) {
   1201         errno = ENODEV;
   1202         return -1;
   1203     }
   1204 
   1205     if (v->getState() != Volume::State_Idle) {
   1206         // You need to unmount manually befoe sharing
   1207         errno = EBUSY;
   1208         return -1;
   1209     }
   1210 
   1211     if (mVolManagerDisabled) {
   1212         errno = EBUSY;
   1213         return -1;
   1214     }
   1215 
   1216     dev_t d = v->getShareDevice();
   1217     if ((MAJOR(d) == 0) && (MINOR(d) == 0)) {
   1218         // This volume does not support raw disk access
   1219         errno = EINVAL;
   1220         return -1;
   1221     }
   1222 
   1223     int fd;
   1224     char nodepath[255];
   1225     snprintf(nodepath,
   1226              sizeof(nodepath), "/dev/block/vold/%d:%d",
   1227              MAJOR(d), MINOR(d));
   1228 
   1229     if ((fd = open(MASS_STORAGE_FILE_PATH, O_WRONLY)) < 0) {
   1230         SLOGE("Unable to open ums lunfile (%s)", strerror(errno));
   1231         return -1;
   1232     }
   1233 
   1234     if (write(fd, nodepath, strlen(nodepath)) < 0) {
   1235         SLOGE("Unable to write to ums lunfile (%s)", strerror(errno));
   1236         close(fd);
   1237         return -1;
   1238     }
   1239 
   1240     close(fd);
   1241     v->handleVolumeShared();
   1242     if (mUmsSharingCount++ == 0) {
   1243         FILE* fp;
   1244         mSavedDirtyRatio = -1; // in case we fail
   1245         if ((fp = fopen("/proc/sys/vm/dirty_ratio", "r+"))) {
   1246             char line[16];
   1247             if (fgets(line, sizeof(line), fp) && sscanf(line, "%d", &mSavedDirtyRatio)) {
   1248                 fprintf(fp, "%d\n", mUmsDirtyRatio);
   1249             } else {
   1250                 SLOGE("Failed to read dirty_ratio (%s)", strerror(errno));
   1251             }
   1252             fclose(fp);
   1253         } else {
   1254             SLOGE("Failed to open /proc/sys/vm/dirty_ratio (%s)", strerror(errno));
   1255         }
   1256     }
   1257     return 0;
   1258 }
   1259 
   1260 int VolumeManager::unshareVolume(const char *label, const char *method) {
   1261     Volume *v = lookupVolume(label);
   1262 
   1263     if (!v) {
   1264         errno = ENOENT;
   1265         return -1;
   1266     }
   1267 
   1268     if (strcmp(method, "ums")) {
   1269         errno = ENOSYS;
   1270         return -1;
   1271     }
   1272 
   1273     if (v->getState() != Volume::State_Shared) {
   1274         errno = EINVAL;
   1275         return -1;
   1276     }
   1277 
   1278     int fd;
   1279     if ((fd = open(MASS_STORAGE_FILE_PATH, O_WRONLY)) < 0) {
   1280         SLOGE("Unable to open ums lunfile (%s)", strerror(errno));
   1281         return -1;
   1282     }
   1283 
   1284     char ch = 0;
   1285     if (write(fd, &ch, 1) < 0) {
   1286         SLOGE("Unable to write to ums lunfile (%s)", strerror(errno));
   1287         close(fd);
   1288         return -1;
   1289     }
   1290 
   1291     close(fd);
   1292     v->handleVolumeUnshared();
   1293     if (--mUmsSharingCount == 0 && mSavedDirtyRatio != -1) {
   1294         FILE* fp;
   1295         if ((fp = fopen("/proc/sys/vm/dirty_ratio", "r+"))) {
   1296             fprintf(fp, "%d\n", mSavedDirtyRatio);
   1297             fclose(fp);
   1298         } else {
   1299             SLOGE("Failed to open /proc/sys/vm/dirty_ratio (%s)", strerror(errno));
   1300         }
   1301         mSavedDirtyRatio = -1;
   1302     }
   1303     return 0;
   1304 }
   1305 
   1306 extern "C" int vold_disableVol(const char *label) {
   1307     VolumeManager *vm = VolumeManager::Instance();
   1308     vm->disableVolumeManager();
   1309     vm->unshareVolume(label, "ums");
   1310     return vm->unmountVolume(label, true, false);
   1311 }
   1312 
   1313 extern "C" int vold_getNumDirectVolumes(void) {
   1314     VolumeManager *vm = VolumeManager::Instance();
   1315     return vm->getNumDirectVolumes();
   1316 }
   1317 
   1318 int VolumeManager::getNumDirectVolumes(void) {
   1319     VolumeCollection::iterator i;
   1320     int n=0;
   1321 
   1322     for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
   1323         if ((*i)->getShareDevice() != (dev_t)0) {
   1324             n++;
   1325         }
   1326     }
   1327     return n;
   1328 }
   1329 
   1330 extern "C" int vold_getDirectVolumeList(struct volume_info *vol_list) {
   1331     VolumeManager *vm = VolumeManager::Instance();
   1332     return vm->getDirectVolumeList(vol_list);
   1333 }
   1334 
   1335 int VolumeManager::getDirectVolumeList(struct volume_info *vol_list) {
   1336     VolumeCollection::iterator i;
   1337     int n=0;
   1338     dev_t d;
   1339 
   1340     for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
   1341         if ((d=(*i)->getShareDevice()) != (dev_t)0) {
   1342             (*i)->getVolInfo(&vol_list[n]);
   1343             snprintf(vol_list[n].blk_dev, sizeof(vol_list[n].blk_dev),
   1344                      "/dev/block/vold/%d:%d",MAJOR(d), MINOR(d));
   1345             n++;
   1346         }
   1347     }
   1348 
   1349     return 0;
   1350 }
   1351 
   1352 int VolumeManager::unmountVolume(const char *label, bool force, bool revert) {
   1353     Volume *v = lookupVolume(label);
   1354 
   1355     if (!v) {
   1356         errno = ENOENT;
   1357         return -1;
   1358     }
   1359 
   1360     if (v->getState() == Volume::State_NoMedia) {
   1361         errno = ENODEV;
   1362         return -1;
   1363     }
   1364 
   1365     if (v->getState() != Volume::State_Mounted) {
   1366         SLOGW("Attempt to unmount volume which isn't mounted (%d)\n",
   1367              v->getState());
   1368         errno = EBUSY;
   1369         return UNMOUNT_NOT_MOUNTED_ERR;
   1370     }
   1371 
   1372     cleanupAsec(v, force);
   1373 
   1374     return v->unmountVol(force, revert);
   1375 }
   1376 
   1377 extern "C" int vold_unmountAllAsecs(void) {
   1378     int rc;
   1379 
   1380     VolumeManager *vm = VolumeManager::Instance();
   1381     rc = vm->unmountAllAsecsInDir(Volume::SEC_ASECDIR_EXT);
   1382     if (vm->unmountAllAsecsInDir(Volume::SEC_ASECDIR_INT)) {
   1383         rc = -1;
   1384     }
   1385     return rc;
   1386 }
   1387 
   1388 #define ID_BUF_LEN 256
   1389 #define ASEC_SUFFIX ".asec"
   1390 #define ASEC_SUFFIX_LEN (sizeof(ASEC_SUFFIX) - 1)
   1391 int VolumeManager::unmountAllAsecsInDir(const char *directory) {
   1392     DIR *d = opendir(directory);
   1393     int rc = 0;
   1394 
   1395     if (!d) {
   1396         SLOGE("Could not open asec dir %s", directory);
   1397         return -1;
   1398     }
   1399 
   1400     size_t dirent_len = offsetof(struct dirent, d_name) +
   1401             pathconf(directory, _PC_NAME_MAX) + 1;
   1402 
   1403     struct dirent *dent = (struct dirent *) malloc(dirent_len);
   1404     if (dent == NULL) {
   1405         SLOGE("Failed to allocate memory for asec dir");
   1406         return -1;
   1407     }
   1408 
   1409     struct dirent *result;
   1410     while (!readdir_r(d, dent, &result) && result != NULL) {
   1411         if (dent->d_name[0] == '.')
   1412             continue;
   1413         if (dent->d_type != DT_REG)
   1414             continue;
   1415         size_t name_len = strlen(dent->d_name);
   1416         if (name_len > 5 && name_len < (ID_BUF_LEN + ASEC_SUFFIX_LEN - 1) &&
   1417                 !strcmp(&dent->d_name[name_len - 5], ASEC_SUFFIX)) {
   1418             char id[ID_BUF_LEN];
   1419             strlcpy(id, dent->d_name, name_len - 4);
   1420             if (unmountAsec(id, true)) {
   1421                 /* Register the error, but try to unmount more asecs */
   1422                 rc = -1;
   1423             }
   1424         }
   1425     }
   1426     closedir(d);
   1427 
   1428     free(dent);
   1429 
   1430     return rc;
   1431 }
   1432 
   1433 /*
   1434  * Looks up a volume by it's label or mount-point
   1435  */
   1436 Volume *VolumeManager::lookupVolume(const char *label) {
   1437     VolumeCollection::iterator i;
   1438 
   1439     for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
   1440         if (label[0] == '/') {
   1441             if (!strcmp(label, (*i)->getMountpoint()))
   1442                 return (*i);
   1443         } else {
   1444             if (!strcmp(label, (*i)->getLabel()))
   1445                 return (*i);
   1446         }
   1447     }
   1448     return NULL;
   1449 }
   1450 
   1451 bool VolumeManager::isMountpointMounted(const char *mp)
   1452 {
   1453     char device[256];
   1454     char mount_path[256];
   1455     char rest[256];
   1456     FILE *fp;
   1457     char line[1024];
   1458 
   1459     if (!(fp = fopen("/proc/mounts", "r"))) {
   1460         SLOGE("Error opening /proc/mounts (%s)", strerror(errno));
   1461         return false;
   1462     }
   1463 
   1464     while(fgets(line, sizeof(line), fp)) {
   1465         line[strlen(line)-1] = '\0';
   1466         sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
   1467         if (!strcmp(mount_path, mp)) {
   1468             fclose(fp);
   1469             return true;
   1470         }
   1471     }
   1472 
   1473     fclose(fp);
   1474     return false;
   1475 }
   1476 
   1477 int VolumeManager::cleanupAsec(Volume *v, bool force) {
   1478     int rc = unmountAllAsecsInDir(Volume::SEC_ASECDIR_EXT);
   1479 
   1480     AsecIdCollection toUnmount;
   1481     // Find the remaining OBB files that are on external storage.
   1482     for (AsecIdCollection::iterator it = mActiveContainers->begin(); it != mActiveContainers->end();
   1483             ++it) {
   1484         ContainerData* cd = *it;
   1485 
   1486         if (cd->type == ASEC) {
   1487             // nothing
   1488         } else if (cd->type == OBB) {
   1489             if (v == getVolumeForFile(cd->id)) {
   1490                 toUnmount.push_back(cd);
   1491             }
   1492         } else {
   1493             SLOGE("Unknown container type %d!", cd->type);
   1494         }
   1495     }
   1496 
   1497     for (AsecIdCollection::iterator it = toUnmount.begin(); it != toUnmount.end(); ++it) {
   1498         ContainerData *cd = *it;
   1499         SLOGI("Unmounting ASEC %s (dependant on %s)", cd->id, v->getMountpoint());
   1500         if (unmountObb(cd->id, force)) {
   1501             SLOGE("Failed to unmount OBB %s (%s)", cd->id, strerror(errno));
   1502             rc = -1;
   1503         }
   1504     }
   1505 
   1506     return rc;
   1507 
   1508 }
   1509 
   1510