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 
     22 #include <linux/kdev_t.h>
     23 
     24 #define LOG_TAG "DirectVolume"
     25 
     26 #include <cutils/log.h>
     27 #include <sysutils/NetlinkEvent.h>
     28 
     29 #include "DirectVolume.h"
     30 #include "VolumeManager.h"
     31 #include "ResponseCode.h"
     32 #include "cryptfs.h"
     33 
     34 // #define PARTITION_DEBUG
     35 
     36 DirectVolume::DirectVolume(VolumeManager *vm, const char *label,
     37                            const char *mount_point, int partIdx) :
     38               Volume(vm, label, mount_point) {
     39     mPartIdx = partIdx;
     40 
     41     mPaths = new PathCollection();
     42     for (int i = 0; i < MAX_PARTITIONS; i++)
     43         mPartMinors[i] = -1;
     44     mPendingPartMap = 0;
     45     mDiskMajor = -1;
     46     mDiskMinor = -1;
     47     mDiskNumParts = 0;
     48 
     49     setState(Volume::State_NoMedia);
     50 }
     51 
     52 DirectVolume::~DirectVolume() {
     53     PathCollection::iterator it;
     54 
     55     for (it = mPaths->begin(); it != mPaths->end(); ++it)
     56         free(*it);
     57     delete mPaths;
     58 }
     59 
     60 int DirectVolume::addPath(const char *path) {
     61     mPaths->push_back(strdup(path));
     62     return 0;
     63 }
     64 
     65 void DirectVolume::setFlags(int flags) {
     66     mFlags = flags;
     67 }
     68 
     69 dev_t DirectVolume::getDiskDevice() {
     70     return MKDEV(mDiskMajor, mDiskMinor);
     71 }
     72 
     73 dev_t DirectVolume::getShareDevice() {
     74     if (mPartIdx != -1) {
     75         return MKDEV(mDiskMajor, mPartIdx);
     76     } else {
     77         return MKDEV(mDiskMajor, mDiskMinor);
     78     }
     79 }
     80 
     81 void DirectVolume::handleVolumeShared() {
     82     setState(Volume::State_Shared);
     83 }
     84 
     85 void DirectVolume::handleVolumeUnshared() {
     86     setState(Volume::State_Idle);
     87 }
     88 
     89 int DirectVolume::handleBlockEvent(NetlinkEvent *evt) {
     90     const char *dp = evt->findParam("DEVPATH");
     91 
     92     PathCollection::iterator  it;
     93     for (it = mPaths->begin(); it != mPaths->end(); ++it) {
     94         if (!strncmp(dp, *it, strlen(*it))) {
     95             /* We can handle this disk */
     96             int action = evt->getAction();
     97             const char *devtype = evt->findParam("DEVTYPE");
     98 
     99             if (action == NetlinkEvent::NlActionAdd) {
    100                 int major = atoi(evt->findParam("MAJOR"));
    101                 int minor = atoi(evt->findParam("MINOR"));
    102                 char nodepath[255];
    103 
    104                 snprintf(nodepath,
    105                          sizeof(nodepath), "/dev/block/vold/%d:%d",
    106                          major, minor);
    107                 if (createDeviceNode(nodepath, major, minor)) {
    108                     SLOGE("Error making device node '%s' (%s)", nodepath,
    109                                                                strerror(errno));
    110                 }
    111                 if (!strcmp(devtype, "disk")) {
    112                     handleDiskAdded(dp, evt);
    113                 } else {
    114                     handlePartitionAdded(dp, evt);
    115                 }
    116             } else if (action == NetlinkEvent::NlActionRemove) {
    117                 if (!strcmp(devtype, "disk")) {
    118                     handleDiskRemoved(dp, evt);
    119                 } else {
    120                     handlePartitionRemoved(dp, evt);
    121                 }
    122             } else if (action == NetlinkEvent::NlActionChange) {
    123                 if (!strcmp(devtype, "disk")) {
    124                     handleDiskChanged(dp, evt);
    125                 } else {
    126                     handlePartitionChanged(dp, evt);
    127                 }
    128             } else {
    129                     SLOGW("Ignoring non add/remove/change event");
    130             }
    131 
    132             return 0;
    133         }
    134     }
    135     errno = ENODEV;
    136     return -1;
    137 }
    138 
    139 void DirectVolume::handleDiskAdded(const char *devpath, NetlinkEvent *evt) {
    140     mDiskMajor = atoi(evt->findParam("MAJOR"));
    141     mDiskMinor = atoi(evt->findParam("MINOR"));
    142 
    143     const char *tmp = evt->findParam("NPARTS");
    144     if (tmp) {
    145         mDiskNumParts = atoi(tmp);
    146     } else {
    147         SLOGW("Kernel block uevent missing 'NPARTS'");
    148         mDiskNumParts = 1;
    149     }
    150 
    151     char msg[255];
    152 
    153     int partmask = 0;
    154     int i;
    155     for (i = 1; i <= mDiskNumParts; i++) {
    156         partmask |= (1 << i);
    157     }
    158     mPendingPartMap = partmask;
    159 
    160     if (mDiskNumParts == 0) {
    161 #ifdef PARTITION_DEBUG
    162         SLOGD("Dv::diskIns - No partitions - good to go son!");
    163 #endif
    164         setState(Volume::State_Idle);
    165     } else {
    166 #ifdef PARTITION_DEBUG
    167         SLOGD("Dv::diskIns - waiting for %d partitions (mask 0x%x)",
    168              mDiskNumParts, mPendingPartMap);
    169 #endif
    170         setState(Volume::State_Pending);
    171     }
    172 
    173     snprintf(msg, sizeof(msg), "Volume %s %s disk inserted (%d:%d)",
    174              getLabel(), getMountpoint(), mDiskMajor, mDiskMinor);
    175     mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,
    176                                              msg, false);
    177 }
    178 
    179 void DirectVolume::handlePartitionAdded(const char *devpath, NetlinkEvent *evt) {
    180     int major = atoi(evt->findParam("MAJOR"));
    181     int minor = atoi(evt->findParam("MINOR"));
    182 
    183     int part_num;
    184 
    185     const char *tmp = evt->findParam("PARTN");
    186 
    187     if (tmp) {
    188         part_num = atoi(tmp);
    189     } else {
    190         SLOGW("Kernel block uevent missing 'PARTN'");
    191         part_num = 1;
    192     }
    193 
    194     if (part_num > MAX_PARTITIONS || part_num < 1) {
    195         SLOGE("Invalid 'PARTN' value");
    196         return;
    197     }
    198 
    199     if (part_num > mDiskNumParts) {
    200         mDiskNumParts = part_num;
    201     }
    202 
    203     if (major != mDiskMajor) {
    204         SLOGE("Partition '%s' has a different major than its disk!", devpath);
    205         return;
    206     }
    207 #ifdef PARTITION_DEBUG
    208     SLOGD("Dv:partAdd: part_num = %d, minor = %d\n", part_num, minor);
    209 #endif
    210     if (part_num >= MAX_PARTITIONS) {
    211         SLOGE("Dv:partAdd: ignoring part_num = %d (max: %d)\n", part_num, MAX_PARTITIONS-1);
    212     } else {
    213         mPartMinors[part_num -1] = minor;
    214     }
    215     mPendingPartMap &= ~(1 << part_num);
    216 
    217     if (!mPendingPartMap) {
    218 #ifdef PARTITION_DEBUG
    219         SLOGD("Dv:partAdd: Got all partitions - ready to rock!");
    220 #endif
    221         if (getState() != Volume::State_Formatting) {
    222             setState(Volume::State_Idle);
    223             if (mRetryMount == true) {
    224                 mRetryMount = false;
    225                 mountVol();
    226             }
    227         }
    228     } else {
    229 #ifdef PARTITION_DEBUG
    230         SLOGD("Dv:partAdd: pending mask now = 0x%x", mPendingPartMap);
    231 #endif
    232     }
    233 }
    234 
    235 void DirectVolume::handleDiskChanged(const char *devpath, NetlinkEvent *evt) {
    236     int major = atoi(evt->findParam("MAJOR"));
    237     int minor = atoi(evt->findParam("MINOR"));
    238 
    239     if ((major != mDiskMajor) || (minor != mDiskMinor)) {
    240         return;
    241     }
    242 
    243     SLOGI("Volume %s disk has changed", getLabel());
    244     const char *tmp = evt->findParam("NPARTS");
    245     if (tmp) {
    246         mDiskNumParts = atoi(tmp);
    247     } else {
    248         SLOGW("Kernel block uevent missing 'NPARTS'");
    249         mDiskNumParts = 1;
    250     }
    251 
    252     int partmask = 0;
    253     int i;
    254     for (i = 1; i <= mDiskNumParts; i++) {
    255         partmask |= (1 << i);
    256     }
    257     mPendingPartMap = partmask;
    258 
    259     if (getState() != Volume::State_Formatting) {
    260         if (mDiskNumParts == 0) {
    261             setState(Volume::State_Idle);
    262         } else {
    263             setState(Volume::State_Pending);
    264         }
    265     }
    266 }
    267 
    268 void DirectVolume::handlePartitionChanged(const char *devpath, NetlinkEvent *evt) {
    269     int major = atoi(evt->findParam("MAJOR"));
    270     int minor = atoi(evt->findParam("MINOR"));
    271     SLOGD("Volume %s %s partition %d:%d changed\n", getLabel(), getMountpoint(), major, minor);
    272 }
    273 
    274 void DirectVolume::handleDiskRemoved(const char *devpath, NetlinkEvent *evt) {
    275     int major = atoi(evt->findParam("MAJOR"));
    276     int minor = atoi(evt->findParam("MINOR"));
    277     char msg[255];
    278 
    279     SLOGD("Volume %s %s disk %d:%d removed\n", getLabel(), getMountpoint(), major, minor);
    280     snprintf(msg, sizeof(msg), "Volume %s %s disk removed (%d:%d)",
    281              getLabel(), getMountpoint(), major, minor);
    282     mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskRemoved,
    283                                              msg, false);
    284     setState(Volume::State_NoMedia);
    285 }
    286 
    287 void DirectVolume::handlePartitionRemoved(const char *devpath, NetlinkEvent *evt) {
    288     int major = atoi(evt->findParam("MAJOR"));
    289     int minor = atoi(evt->findParam("MINOR"));
    290     char msg[255];
    291     int state;
    292 
    293     SLOGD("Volume %s %s partition %d:%d removed\n", getLabel(), getMountpoint(), major, minor);
    294 
    295     /*
    296      * The framework doesn't need to get notified of
    297      * partition removal unless it's mounted. Otherwise
    298      * the removal notification will be sent on the Disk
    299      * itself
    300      */
    301     state = getState();
    302     if (state != Volume::State_Mounted && state != Volume::State_Shared) {
    303         return;
    304     }
    305 
    306     if ((dev_t) MKDEV(major, minor) == mCurrentlyMountedKdev) {
    307         /*
    308          * Yikes, our mounted partition is going away!
    309          */
    310 
    311         snprintf(msg, sizeof(msg), "Volume %s %s bad removal (%d:%d)",
    312                  getLabel(), getMountpoint(), major, minor);
    313         mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,
    314                                              msg, false);
    315 
    316 	if (mVm->cleanupAsec(this, true)) {
    317             SLOGE("Failed to cleanup ASEC - unmount will probably fail!");
    318         }
    319 
    320         if (Volume::unmountVol(true, false)) {
    321             SLOGE("Failed to unmount volume on bad removal (%s)",
    322                  strerror(errno));
    323             // XXX: At this point we're screwed for now
    324         } else {
    325             SLOGD("Crisis averted");
    326         }
    327     } else if (state == Volume::State_Shared) {
    328         /* removed during mass storage */
    329         snprintf(msg, sizeof(msg), "Volume %s bad removal (%d:%d)",
    330                  getLabel(), major, minor);
    331         mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,
    332                                              msg, false);
    333 
    334         if (mVm->unshareVolume(getLabel(), "ums")) {
    335             SLOGE("Failed to unshare volume on bad removal (%s)",
    336                 strerror(errno));
    337         } else {
    338             SLOGD("Crisis averted");
    339         }
    340     }
    341 }
    342 
    343 /*
    344  * Called from base to get a list of devicenodes for mounting
    345  */
    346 int DirectVolume::getDeviceNodes(dev_t *devs, int max) {
    347 
    348     if (mPartIdx == -1) {
    349         // If the disk has no partitions, try the disk itself
    350         if (!mDiskNumParts) {
    351             devs[0] = MKDEV(mDiskMajor, mDiskMinor);
    352             return 1;
    353         }
    354 
    355         int i;
    356         for (i = 0; i < mDiskNumParts; i++) {
    357             if (i == max)
    358                 break;
    359             devs[i] = MKDEV(mDiskMajor, mPartMinors[i]);
    360         }
    361         return mDiskNumParts;
    362     }
    363     devs[0] = MKDEV(mDiskMajor, mPartMinors[mPartIdx -1]);
    364     return 1;
    365 }
    366 
    367 /*
    368  * Called from base to update device info,
    369  * e.g. When setting up an dm-crypt mapping for the sd card.
    370  */
    371 int DirectVolume::updateDeviceInfo(char *new_path, int new_major, int new_minor)
    372 {
    373     PathCollection::iterator it;
    374 
    375     if (mPartIdx == -1) {
    376         SLOGE("Can only change device info on a partition\n");
    377         return -1;
    378     }
    379 
    380     /*
    381      * This is to change the sysfs path associated with a partition, in particular,
    382      * for an internal SD card partition that is encrypted.  Thus, the list is
    383      * expected to be only 1 entry long.  Check that and bail if not.
    384      */
    385     if (mPaths->size() != 1) {
    386         SLOGE("Cannot change path if there are more than one for a volume\n");
    387         return -1;
    388     }
    389 
    390     it = mPaths->begin();
    391     free(*it); /* Free the string storage */
    392     mPaths->erase(it); /* Remove it from the list */
    393     addPath(new_path); /* Put the new path on the list */
    394 
    395     /* Save away original info so we can restore it when doing factory reset.
    396      * Then, when doing the format, it will format the original device in the
    397      * clear, otherwise it just formats the encrypted device which is not
    398      * readable when the device boots unencrypted after the reset.
    399      */
    400     mOrigDiskMajor = mDiskMajor;
    401     mOrigDiskMinor = mDiskMinor;
    402     mOrigPartIdx = mPartIdx;
    403     memcpy(mOrigPartMinors, mPartMinors, sizeof(mPartMinors));
    404 
    405     mDiskMajor = new_major;
    406     mDiskMinor = new_minor;
    407     /* Ugh, virual block devices don't use minor 0 for whole disk and minor > 0 for
    408      * partition number.  They don't have partitions, they are just virtual block
    409      * devices, and minor number 0 is the first dm-crypt device.  Luckily the first
    410      * dm-crypt device is for the userdata partition, which gets minor number 0, and
    411      * it is not managed by vold.  So the next device is minor number one, which we
    412      * will call partition one.
    413      */
    414     mPartIdx = new_minor;
    415     mPartMinors[new_minor-1] = new_minor;
    416 
    417     mIsDecrypted = 1;
    418 
    419     return 0;
    420 }
    421 
    422 /*
    423  * Called from base to revert device info to the way it was before a
    424  * crypto mapping was created for it.
    425  */
    426 void DirectVolume::revertDeviceInfo(void)
    427 {
    428     if (mIsDecrypted) {
    429         mDiskMajor = mOrigDiskMajor;
    430         mDiskMinor = mOrigDiskMinor;
    431         mPartIdx = mOrigPartIdx;
    432         memcpy(mPartMinors, mOrigPartMinors, sizeof(mPartMinors));
    433 
    434         mIsDecrypted = 0;
    435     }
    436 
    437     return;
    438 }
    439 
    440 /*
    441  * Called from base to give cryptfs all the info it needs to encrypt eligible volumes
    442  */
    443 int DirectVolume::getVolInfo(struct volume_info *v)
    444 {
    445     strcpy(v->label, mLabel);
    446     strcpy(v->mnt_point, mMountpoint);
    447     v->flags=mFlags;
    448     /* Other fields of struct volume_info are filled in by the caller or cryptfs.c */
    449 
    450     return 0;
    451 }
    452