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 <stdlib.h>
     18 #include <string.h>
     19 #include <dirent.h>
     20 #include <errno.h>
     21 #include <fcntl.h>
     22 
     23 #include <sys/types.h>
     24 #include <sys/stat.h>
     25 #include <sys/types.h>
     26 #include <sys/mman.h>
     27 #include <sys/mount.h>
     28 #include <sys/param.h>
     29 
     30 #include <linux/kdev_t.h>
     31 
     32 #include <cutils/properties.h>
     33 
     34 #include <diskconfig/diskconfig.h>
     35 
     36 #include <private/android_filesystem_config.h>
     37 
     38 #define LOG_TAG "Vold"
     39 
     40 #include <cutils/fs.h>
     41 #include <cutils/log.h>
     42 
     43 #include <string>
     44 
     45 #include "Volume.h"
     46 #include "VolumeManager.h"
     47 #include "ResponseCode.h"
     48 #include "Fat.h"
     49 #include "Process.h"
     50 #include "cryptfs.h"
     51 
     52 extern "C" void dos_partition_dec(void const *pp, struct dos_partition *d);
     53 extern "C" void dos_partition_enc(void *pp, struct dos_partition *d);
     54 
     55 
     56 /*
     57  * Media directory - stuff that only media_rw user can see
     58  */
     59 const char *Volume::MEDIA_DIR           = "/mnt/media_rw";
     60 
     61 /*
     62  * Fuse directory - location where fuse wrapped filesystems go
     63  */
     64 const char *Volume::FUSE_DIR           = "/storage";
     65 
     66 /*
     67  * Path to external storage where *only* root can access ASEC image files
     68  */
     69 const char *Volume::SEC_ASECDIR_EXT   = "/mnt/secure/asec";
     70 
     71 /*
     72  * Path to internal storage where *only* root can access ASEC image files
     73  */
     74 const char *Volume::SEC_ASECDIR_INT   = "/data/app-asec";
     75 
     76 /*
     77  * Path to where secure containers are mounted
     78  */
     79 const char *Volume::ASECDIR           = "/mnt/asec";
     80 
     81 /*
     82  * Path to where OBBs are mounted
     83  */
     84 const char *Volume::LOOPDIR           = "/mnt/obb";
     85 
     86 const char *Volume::BLKID_PATH = "/system/bin/blkid";
     87 
     88 static const char *stateToStr(int state) {
     89     if (state == Volume::State_Init)
     90         return "Initializing";
     91     else if (state == Volume::State_NoMedia)
     92         return "No-Media";
     93     else if (state == Volume::State_Idle)
     94         return "Idle-Unmounted";
     95     else if (state == Volume::State_Pending)
     96         return "Pending";
     97     else if (state == Volume::State_Mounted)
     98         return "Mounted";
     99     else if (state == Volume::State_Unmounting)
    100         return "Unmounting";
    101     else if (state == Volume::State_Checking)
    102         return "Checking";
    103     else if (state == Volume::State_Formatting)
    104         return "Formatting";
    105     else if (state == Volume::State_Shared)
    106         return "Shared-Unmounted";
    107     else if (state == Volume::State_SharedMnt)
    108         return "Shared-Mounted";
    109     else
    110         return "Unknown-Error";
    111 }
    112 
    113 Volume::Volume(VolumeManager *vm, const fstab_rec* rec, int flags) {
    114     mVm = vm;
    115     mDebug = false;
    116     mLabel = strdup(rec->label);
    117     mUuid = NULL;
    118     mUserLabel = NULL;
    119     mState = Volume::State_Init;
    120     mFlags = flags;
    121     mCurrentlyMountedKdev = -1;
    122     mPartIdx = rec->partnum;
    123     mRetryMount = false;
    124 }
    125 
    126 Volume::~Volume() {
    127     free(mLabel);
    128     free(mUuid);
    129     free(mUserLabel);
    130 }
    131 
    132 void Volume::setDebug(bool enable) {
    133     mDebug = enable;
    134 }
    135 
    136 dev_t Volume::getDiskDevice() {
    137     return MKDEV(0, 0);
    138 };
    139 
    140 dev_t Volume::getShareDevice() {
    141     return getDiskDevice();
    142 }
    143 
    144 void Volume::handleVolumeShared() {
    145 }
    146 
    147 void Volume::handleVolumeUnshared() {
    148 }
    149 
    150 int Volume::handleBlockEvent(NetlinkEvent * /*evt*/) {
    151     errno = ENOSYS;
    152     return -1;
    153 }
    154 
    155 void Volume::setUuid(const char* uuid) {
    156     char msg[256];
    157 
    158     if (mUuid) {
    159         free(mUuid);
    160     }
    161 
    162     if (uuid) {
    163         mUuid = strdup(uuid);
    164         snprintf(msg, sizeof(msg), "%s %s \"%s\"", getLabel(),
    165                 getFuseMountpoint(), mUuid);
    166     } else {
    167         mUuid = NULL;
    168         snprintf(msg, sizeof(msg), "%s %s", getLabel(), getFuseMountpoint());
    169     }
    170 
    171     mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeUuidChange, msg,
    172             false);
    173 }
    174 
    175 void Volume::setUserLabel(const char* userLabel) {
    176     char msg[256];
    177 
    178     if (mUserLabel) {
    179         free(mUserLabel);
    180     }
    181 
    182     if (userLabel) {
    183         mUserLabel = strdup(userLabel);
    184         snprintf(msg, sizeof(msg), "%s %s \"%s\"", getLabel(),
    185                 getFuseMountpoint(), mUserLabel);
    186     } else {
    187         mUserLabel = NULL;
    188         snprintf(msg, sizeof(msg), "%s %s", getLabel(), getFuseMountpoint());
    189     }
    190 
    191     mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeUserLabelChange,
    192             msg, false);
    193 }
    194 
    195 void Volume::setState(int state) {
    196     char msg[255];
    197     int oldState = mState;
    198 
    199     if (oldState == state) {
    200         SLOGW("Duplicate state (%d)\n", state);
    201         return;
    202     }
    203 
    204     if ((oldState == Volume::State_Pending) && (state != Volume::State_Idle)) {
    205         mRetryMount = false;
    206     }
    207 
    208     mState = state;
    209 
    210     SLOGD("Volume %s state changing %d (%s) -> %d (%s)", mLabel,
    211          oldState, stateToStr(oldState), mState, stateToStr(mState));
    212     snprintf(msg, sizeof(msg),
    213              "Volume %s %s state changed from %d (%s) to %d (%s)", getLabel(),
    214              getFuseMountpoint(), oldState, stateToStr(oldState), mState,
    215              stateToStr(mState));
    216 
    217     mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeStateChange,
    218                                          msg, false);
    219 }
    220 
    221 int Volume::createDeviceNode(const char *path, int major, int minor) {
    222     mode_t mode = 0660 | S_IFBLK;
    223     dev_t dev = (major << 8) | minor;
    224     if (mknod(path, mode, dev) < 0) {
    225         if (errno != EEXIST) {
    226             return -1;
    227         }
    228     }
    229     return 0;
    230 }
    231 
    232 int Volume::formatVol(bool wipe) {
    233 
    234     if (getState() == Volume::State_NoMedia) {
    235         errno = ENODEV;
    236         return -1;
    237     } else if (getState() != Volume::State_Idle) {
    238         errno = EBUSY;
    239         return -1;
    240     }
    241 
    242     if (isMountpointMounted(getMountpoint())) {
    243         SLOGW("Volume is idle but appears to be mounted - fixing");
    244         setState(Volume::State_Mounted);
    245         // mCurrentlyMountedKdev = XXX
    246         errno = EBUSY;
    247         return -1;
    248     }
    249 
    250     bool formatEntireDevice = (mPartIdx == -1);
    251     char devicePath[255];
    252     dev_t diskNode = getDiskDevice();
    253     dev_t partNode =
    254         MKDEV(MAJOR(diskNode),
    255               MINOR(diskNode) + (formatEntireDevice ? 1 : mPartIdx));
    256 
    257     setState(Volume::State_Formatting);
    258 
    259     int ret = -1;
    260     // Only initialize the MBR if we are formatting the entire device
    261     if (formatEntireDevice) {
    262         sprintf(devicePath, "/dev/block/vold/%d:%d",
    263                 major(diskNode), minor(diskNode));
    264 
    265         if (initializeMbr(devicePath)) {
    266             SLOGE("Failed to initialize MBR (%s)", strerror(errno));
    267             goto err;
    268         }
    269     }
    270 
    271     sprintf(devicePath, "/dev/block/vold/%d:%d",
    272             major(partNode), minor(partNode));
    273 
    274     if (mDebug) {
    275         SLOGI("Formatting volume %s (%s)", getLabel(), devicePath);
    276     }
    277 
    278     if (Fat::format(devicePath, 0, wipe)) {
    279         SLOGE("Failed to format (%s)", strerror(errno));
    280         goto err;
    281     }
    282 
    283     ret = 0;
    284 
    285 err:
    286     setState(Volume::State_Idle);
    287     return ret;
    288 }
    289 
    290 bool Volume::isMountpointMounted(const char *path) {
    291     char device[256];
    292     char mount_path[256];
    293     char rest[256];
    294     FILE *fp;
    295     char line[1024];
    296 
    297     if (!(fp = fopen("/proc/mounts", "r"))) {
    298         SLOGE("Error opening /proc/mounts (%s)", strerror(errno));
    299         return false;
    300     }
    301 
    302     while(fgets(line, sizeof(line), fp)) {
    303         line[strlen(line)-1] = '\0';
    304         sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
    305         if (!strcmp(mount_path, path)) {
    306             fclose(fp);
    307             return true;
    308         }
    309     }
    310 
    311     fclose(fp);
    312     return false;
    313 }
    314 
    315 int Volume::mountVol() {
    316     dev_t deviceNodes[4];
    317     int n, i, rc = 0;
    318     char errmsg[255];
    319 
    320     int flags = getFlags();
    321     bool providesAsec = (flags & VOL_PROVIDES_ASEC) != 0;
    322 
    323     // TODO: handle "bind" style mounts, for emulated storage
    324 
    325     char decrypt_state[PROPERTY_VALUE_MAX];
    326     char crypto_state[PROPERTY_VALUE_MAX];
    327     char encrypt_progress[PROPERTY_VALUE_MAX];
    328 
    329     property_get("vold.decrypt", decrypt_state, "");
    330     property_get("vold.encrypt_progress", encrypt_progress, "");
    331 
    332     /* Don't try to mount the volumes if we have not yet entered the disk password
    333      * or are in the process of encrypting.
    334      */
    335     if ((getState() == Volume::State_NoMedia) ||
    336         ((!strcmp(decrypt_state, "1") || encrypt_progress[0]) && providesAsec)) {
    337         snprintf(errmsg, sizeof(errmsg),
    338                  "Volume %s %s mount failed - no media",
    339                  getLabel(), getFuseMountpoint());
    340         mVm->getBroadcaster()->sendBroadcast(
    341                                          ResponseCode::VolumeMountFailedNoMedia,
    342                                          errmsg, false);
    343         errno = ENODEV;
    344         return -1;
    345     } else if (getState() != Volume::State_Idle) {
    346         errno = EBUSY;
    347         if (getState() == Volume::State_Pending) {
    348             mRetryMount = true;
    349         }
    350         return -1;
    351     }
    352 
    353     if (isMountpointMounted(getMountpoint())) {
    354         SLOGW("Volume is idle but appears to be mounted - fixing");
    355         setState(Volume::State_Mounted);
    356         // mCurrentlyMountedKdev = XXX
    357         return 0;
    358     }
    359 
    360     n = getDeviceNodes((dev_t *) &deviceNodes, 4);
    361     if (!n) {
    362         SLOGE("Failed to get device nodes (%s)\n", strerror(errno));
    363         return -1;
    364     }
    365 
    366     /* If we're running encrypted, and the volume is marked as encryptable and nonremovable,
    367      * and also marked as providing Asec storage, then we need to decrypt
    368      * that partition, and update the volume object to point to it's new decrypted
    369      * block device
    370      */
    371     property_get("ro.crypto.state", crypto_state, "");
    372     if (providesAsec &&
    373         ((flags & (VOL_NONREMOVABLE | VOL_ENCRYPTABLE))==(VOL_NONREMOVABLE | VOL_ENCRYPTABLE)) &&
    374         !strcmp(crypto_state, "encrypted") && !isDecrypted()) {
    375        char new_sys_path[MAXPATHLEN];
    376        char nodepath[256];
    377        int new_major, new_minor;
    378 
    379        if (n != 1) {
    380            /* We only expect one device node returned when mounting encryptable volumes */
    381            SLOGE("Too many device nodes returned when mounting %s\n", getMountpoint());
    382            return -1;
    383        }
    384 
    385        if (cryptfs_setup_volume(getLabel(), MAJOR(deviceNodes[0]), MINOR(deviceNodes[0]),
    386                                 new_sys_path, sizeof(new_sys_path),
    387                                 &new_major, &new_minor)) {
    388            SLOGE("Cannot setup encryption mapping for %s\n", getMountpoint());
    389            return -1;
    390        }
    391        /* We now have the new sysfs path for the decrypted block device, and the
    392         * majore and minor numbers for it.  So, create the device, update the
    393         * path to the new sysfs path, and continue.
    394         */
    395         snprintf(nodepath,
    396                  sizeof(nodepath), "/dev/block/vold/%d:%d",
    397                  new_major, new_minor);
    398         if (createDeviceNode(nodepath, new_major, new_minor)) {
    399             SLOGE("Error making device node '%s' (%s)", nodepath,
    400                                                        strerror(errno));
    401         }
    402 
    403         // Todo: Either create sys filename from nodepath, or pass in bogus path so
    404         //       vold ignores state changes on this internal device.
    405         updateDeviceInfo(nodepath, new_major, new_minor);
    406 
    407         /* Get the device nodes again, because they just changed */
    408         n = getDeviceNodes((dev_t *) &deviceNodes, 4);
    409         if (!n) {
    410             SLOGE("Failed to get device nodes (%s)\n", strerror(errno));
    411             return -1;
    412         }
    413     }
    414 
    415     for (i = 0; i < n; i++) {
    416         char devicePath[255];
    417 
    418         sprintf(devicePath, "/dev/block/vold/%d:%d", major(deviceNodes[i]),
    419                 minor(deviceNodes[i]));
    420 
    421         SLOGI("%s being considered for volume %s\n", devicePath, getLabel());
    422 
    423         errno = 0;
    424         setState(Volume::State_Checking);
    425 
    426         if (Fat::check(devicePath)) {
    427             if (errno == ENODATA) {
    428                 SLOGW("%s does not contain a FAT filesystem\n", devicePath);
    429                 continue;
    430             }
    431             errno = EIO;
    432             /* Badness - abort the mount */
    433             SLOGE("%s failed FS checks (%s)", devicePath, strerror(errno));
    434             setState(Volume::State_Idle);
    435             return -1;
    436         }
    437 
    438         errno = 0;
    439         int gid;
    440 
    441         if (Fat::doMount(devicePath, getMountpoint(), false, false, false,
    442                 AID_MEDIA_RW, AID_MEDIA_RW, 0007, true)) {
    443             SLOGE("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));
    444             continue;
    445         }
    446 
    447         extractMetadata(devicePath);
    448 
    449         if (providesAsec && mountAsecExternal() != 0) {
    450             SLOGE("Failed to mount secure area (%s)", strerror(errno));
    451             umount(getMountpoint());
    452             setState(Volume::State_Idle);
    453             return -1;
    454         }
    455 
    456         char service[64];
    457         snprintf(service, 64, "fuse_%s", getLabel());
    458         property_set("ctl.start", service);
    459 
    460         setState(Volume::State_Mounted);
    461         mCurrentlyMountedKdev = deviceNodes[i];
    462         return 0;
    463     }
    464 
    465     SLOGE("Volume %s found no suitable devices for mounting :(\n", getLabel());
    466     setState(Volume::State_Idle);
    467 
    468     return -1;
    469 }
    470 
    471 int Volume::mountAsecExternal() {
    472     char legacy_path[PATH_MAX];
    473     char secure_path[PATH_MAX];
    474 
    475     snprintf(legacy_path, PATH_MAX, "%s/android_secure", getMountpoint());
    476     snprintf(secure_path, PATH_MAX, "%s/.android_secure", getMountpoint());
    477 
    478     // Recover legacy secure path
    479     if (!access(legacy_path, R_OK | X_OK) && access(secure_path, R_OK | X_OK)) {
    480         if (rename(legacy_path, secure_path)) {
    481             SLOGE("Failed to rename legacy asec dir (%s)", strerror(errno));
    482         }
    483     }
    484 
    485     if (fs_prepare_dir(secure_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) != 0) {
    486         SLOGW("fs_prepare_dir failed: %s", strerror(errno));
    487         return -1;
    488     }
    489 
    490     if (mount(secure_path, SEC_ASECDIR_EXT, "", MS_BIND, NULL)) {
    491         SLOGE("Failed to bind mount points %s -> %s (%s)", secure_path,
    492                 SEC_ASECDIR_EXT, strerror(errno));
    493         return -1;
    494     }
    495 
    496     return 0;
    497 }
    498 
    499 int Volume::doUnmount(const char *path, bool force) {
    500     int retries = 10;
    501 
    502     if (mDebug) {
    503         SLOGD("Unmounting {%s}, force = %d", path, force);
    504     }
    505 
    506     while (retries--) {
    507         if (!umount(path) || errno == EINVAL || errno == ENOENT) {
    508             SLOGI("%s sucessfully unmounted", path);
    509             return 0;
    510         }
    511 
    512         int action = 0;
    513 
    514         if (force) {
    515             if (retries == 1) {
    516                 action = 2; // SIGKILL
    517             } else if (retries == 2) {
    518                 action = 1; // SIGHUP
    519             }
    520         }
    521 
    522         SLOGW("Failed to unmount %s (%s, retries %d, action %d)",
    523                 path, strerror(errno), retries, action);
    524 
    525         Process::killProcessesWithOpenFiles(path, action);
    526         usleep(1000*1000);
    527     }
    528     errno = EBUSY;
    529     SLOGE("Giving up on unmount %s (%s)", path, strerror(errno));
    530     return -1;
    531 }
    532 
    533 int Volume::unmountVol(bool force, bool revert) {
    534     int i, rc;
    535 
    536     int flags = getFlags();
    537     bool providesAsec = (flags & VOL_PROVIDES_ASEC) != 0;
    538 
    539     if (getState() != Volume::State_Mounted) {
    540         SLOGE("Volume %s unmount request when not mounted", getLabel());
    541         errno = EINVAL;
    542         return UNMOUNT_NOT_MOUNTED_ERR;
    543     }
    544 
    545     setState(Volume::State_Unmounting);
    546     usleep(1000 * 1000); // Give the framework some time to react
    547 
    548     char service[64];
    549     snprintf(service, 64, "fuse_%s", getLabel());
    550     property_set("ctl.stop", service);
    551     /* Give it a chance to stop.  I wish we had a synchronous way to determine this... */
    552     sleep(1);
    553 
    554     // TODO: determine failure mode if FUSE times out
    555 
    556     if (providesAsec && doUnmount(Volume::SEC_ASECDIR_EXT, force) != 0) {
    557         SLOGE("Failed to unmount secure area on %s (%s)", getMountpoint(), strerror(errno));
    558         goto out_mounted;
    559     }
    560 
    561     /* Now that the fuse daemon is dead, unmount it */
    562     if (doUnmount(getFuseMountpoint(), force) != 0) {
    563         SLOGE("Failed to unmount %s (%s)", getFuseMountpoint(), strerror(errno));
    564         goto fail_remount_secure;
    565     }
    566 
    567     /* Unmount the real sd card */
    568     if (doUnmount(getMountpoint(), force) != 0) {
    569         SLOGE("Failed to unmount %s (%s)", getMountpoint(), strerror(errno));
    570         goto fail_remount_secure;
    571     }
    572 
    573     SLOGI("%s unmounted successfully", getMountpoint());
    574 
    575     /* If this is an encrypted volume, and we've been asked to undo
    576      * the crypto mapping, then revert the dm-crypt mapping, and revert
    577      * the device info to the original values.
    578      */
    579     if (revert && isDecrypted()) {
    580         cryptfs_revert_volume(getLabel());
    581         revertDeviceInfo();
    582         SLOGI("Encrypted volume %s reverted successfully", getMountpoint());
    583     }
    584 
    585     setUuid(NULL);
    586     setUserLabel(NULL);
    587     setState(Volume::State_Idle);
    588     mCurrentlyMountedKdev = -1;
    589     return 0;
    590 
    591 fail_remount_secure:
    592     if (providesAsec && mountAsecExternal() != 0) {
    593         SLOGE("Failed to remount secure area (%s)", strerror(errno));
    594         goto out_nomedia;
    595     }
    596 
    597 out_mounted:
    598     setState(Volume::State_Mounted);
    599     return -1;
    600 
    601 out_nomedia:
    602     setState(Volume::State_NoMedia);
    603     return -1;
    604 }
    605 
    606 int Volume::initializeMbr(const char *deviceNode) {
    607     struct disk_info dinfo;
    608 
    609     memset(&dinfo, 0, sizeof(dinfo));
    610 
    611     if (!(dinfo.part_lst = (struct part_info *) malloc(MAX_NUM_PARTS * sizeof(struct part_info)))) {
    612         SLOGE("Failed to malloc prt_lst");
    613         return -1;
    614     }
    615 
    616     memset(dinfo.part_lst, 0, MAX_NUM_PARTS * sizeof(struct part_info));
    617     dinfo.device = strdup(deviceNode);
    618     dinfo.scheme = PART_SCHEME_MBR;
    619     dinfo.sect_size = 512;
    620     dinfo.skip_lba = 2048;
    621     dinfo.num_lba = 0;
    622     dinfo.num_parts = 1;
    623 
    624     struct part_info *pinfo = &dinfo.part_lst[0];
    625 
    626     pinfo->name = strdup("android_sdcard");
    627     pinfo->flags |= PART_ACTIVE_FLAG;
    628     pinfo->type = PC_PART_TYPE_FAT32;
    629     pinfo->len_kb = -1;
    630 
    631     int rc = apply_disk_config(&dinfo, 0);
    632 
    633     if (rc) {
    634         SLOGE("Failed to apply disk configuration (%d)", rc);
    635         goto out;
    636     }
    637 
    638  out:
    639     free(pinfo->name);
    640     free(dinfo.device);
    641     free(dinfo.part_lst);
    642 
    643     return rc;
    644 }
    645 
    646 /*
    647  * Use blkid to extract UUID and label from device, since it handles many
    648  * obscure edge cases around partition types and formats. Always broadcasts
    649  * updated metadata values.
    650  */
    651 int Volume::extractMetadata(const char* devicePath) {
    652     int res = 0;
    653 
    654     std::string cmd;
    655     cmd = BLKID_PATH;
    656     cmd += " -c /dev/null ";
    657     cmd += devicePath;
    658 
    659     FILE* fp = popen(cmd.c_str(), "r");
    660     if (!fp) {
    661         ALOGE("Failed to run %s: %s", cmd.c_str(), strerror(errno));
    662         res = -1;
    663         goto done;
    664     }
    665 
    666     char line[1024];
    667     char value[128];
    668     if (fgets(line, sizeof(line), fp) != NULL) {
    669         ALOGD("blkid identified as %s", line);
    670 
    671         char* start = strstr(line, "UUID=");
    672         if (start != NULL && sscanf(start + 5, "\"%127[^\"]\"", value) == 1) {
    673             setUuid(value);
    674         } else {
    675             setUuid(NULL);
    676         }
    677 
    678         start = strstr(line, "LABEL=");
    679         if (start != NULL && sscanf(start + 6, "\"%127[^\"]\"", value) == 1) {
    680             setUserLabel(value);
    681         } else {
    682             setUserLabel(NULL);
    683         }
    684     } else {
    685         ALOGW("blkid failed to identify %s", devicePath);
    686         res = -1;
    687     }
    688 
    689     pclose(fp);
    690 
    691 done:
    692     if (res == -1) {
    693         setUuid(NULL);
    694         setUserLabel(NULL);
    695     }
    696     return res;
    697 }
    698