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 
     33 // #define PARTITION_DEBUG
     34 
     35 DirectVolume::DirectVolume(VolumeManager *vm, const char *label,
     36                            const char *mount_point, int partIdx) :
     37               Volume(vm, label, mount_point) {
     38     mPartIdx = partIdx;
     39 
     40     mPaths = new PathCollection();
     41     for (int i = 0; i < MAX_PARTITIONS; i++)
     42         mPartMinors[i] = -1;
     43     mPendingPartMap = 0;
     44     mDiskMajor = -1;
     45     mDiskMinor = -1;
     46     mDiskNumParts = 0;
     47 
     48     setState(Volume::State_NoMedia);
     49 }
     50 
     51 DirectVolume::~DirectVolume() {
     52     PathCollection::iterator it;
     53 
     54     for (it = mPaths->begin(); it != mPaths->end(); ++it)
     55         free(*it);
     56     delete mPaths;
     57 }
     58 
     59 int DirectVolume::addPath(const char *path) {
     60     mPaths->push_back(strdup(path));
     61     return 0;
     62 }
     63 
     64 dev_t DirectVolume::getDiskDevice() {
     65     return MKDEV(mDiskMajor, mDiskMinor);
     66 }
     67 
     68 void DirectVolume::handleVolumeShared() {
     69     setState(Volume::State_Shared);
     70 }
     71 
     72 void DirectVolume::handleVolumeUnshared() {
     73     setState(Volume::State_Idle);
     74 }
     75 
     76 int DirectVolume::handleBlockEvent(NetlinkEvent *evt) {
     77     const char *dp = evt->findParam("DEVPATH");
     78 
     79     PathCollection::iterator  it;
     80     for (it = mPaths->begin(); it != mPaths->end(); ++it) {
     81         if (!strncmp(dp, *it, strlen(*it))) {
     82             /* We can handle this disk */
     83             int action = evt->getAction();
     84             const char *devtype = evt->findParam("DEVTYPE");
     85 
     86             if (action == NetlinkEvent::NlActionAdd) {
     87                 int major = atoi(evt->findParam("MAJOR"));
     88                 int minor = atoi(evt->findParam("MINOR"));
     89                 char nodepath[255];
     90 
     91                 snprintf(nodepath,
     92                          sizeof(nodepath), "/dev/block/vold/%d:%d",
     93                          major, minor);
     94                 if (createDeviceNode(nodepath, major, minor)) {
     95                     SLOGE("Error making device node '%s' (%s)", nodepath,
     96                                                                strerror(errno));
     97                 }
     98                 if (!strcmp(devtype, "disk")) {
     99                     handleDiskAdded(dp, evt);
    100                 } else {
    101                     handlePartitionAdded(dp, evt);
    102                 }
    103             } else if (action == NetlinkEvent::NlActionRemove) {
    104                 if (!strcmp(devtype, "disk")) {
    105                     handleDiskRemoved(dp, evt);
    106                 } else {
    107                     handlePartitionRemoved(dp, evt);
    108                 }
    109             } else if (action == NetlinkEvent::NlActionChange) {
    110                 if (!strcmp(devtype, "disk")) {
    111                     handleDiskChanged(dp, evt);
    112                 } else {
    113                     handlePartitionChanged(dp, evt);
    114                 }
    115             } else {
    116                     SLOGW("Ignoring non add/remove/change event");
    117             }
    118 
    119             return 0;
    120         }
    121     }
    122     errno = ENODEV;
    123     return -1;
    124 }
    125 
    126 void DirectVolume::handleDiskAdded(const char *devpath, NetlinkEvent *evt) {
    127     mDiskMajor = atoi(evt->findParam("MAJOR"));
    128     mDiskMinor = atoi(evt->findParam("MINOR"));
    129 
    130     const char *tmp = evt->findParam("NPARTS");
    131     if (tmp) {
    132         mDiskNumParts = atoi(tmp);
    133     } else {
    134         SLOGW("Kernel block uevent missing 'NPARTS'");
    135         mDiskNumParts = 1;
    136     }
    137 
    138     char msg[255];
    139 
    140     int partmask = 0;
    141     int i;
    142     for (i = 1; i <= mDiskNumParts; i++) {
    143         partmask |= (1 << i);
    144     }
    145     mPendingPartMap = partmask;
    146 
    147     if (mDiskNumParts == 0) {
    148 #ifdef PARTITION_DEBUG
    149         SLOGD("Dv::diskIns - No partitions - good to go son!");
    150 #endif
    151         setState(Volume::State_Idle);
    152     } else {
    153 #ifdef PARTITION_DEBUG
    154         SLOGD("Dv::diskIns - waiting for %d partitions (mask 0x%x)",
    155              mDiskNumParts, mPendingPartMap);
    156 #endif
    157         setState(Volume::State_Pending);
    158     }
    159 
    160     snprintf(msg, sizeof(msg), "Volume %s %s disk inserted (%d:%d)",
    161              getLabel(), getMountpoint(), mDiskMajor, mDiskMinor);
    162     mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,
    163                                              msg, false);
    164 }
    165 
    166 void DirectVolume::handlePartitionAdded(const char *devpath, NetlinkEvent *evt) {
    167     int major = atoi(evt->findParam("MAJOR"));
    168     int minor = atoi(evt->findParam("MINOR"));
    169 
    170     int part_num;
    171 
    172     const char *tmp = evt->findParam("PARTN");
    173 
    174     if (tmp) {
    175         part_num = atoi(tmp);
    176     } else {
    177         SLOGW("Kernel block uevent missing 'PARTN'");
    178         part_num = 1;
    179     }
    180 
    181     if (part_num > mDiskNumParts) {
    182         mDiskNumParts = part_num;
    183     }
    184 
    185     if (major != mDiskMajor) {
    186         SLOGE("Partition '%s' has a different major than its disk!", devpath);
    187         return;
    188     }
    189 #ifdef PARTITION_DEBUG
    190     SLOGD("Dv:partAdd: part_num = %d, minor = %d\n", part_num, minor);
    191 #endif
    192     mPartMinors[part_num -1] = minor;
    193 
    194     mPendingPartMap &= ~(1 << part_num);
    195     if (!mPendingPartMap) {
    196 #ifdef PARTITION_DEBUG
    197         SLOGD("Dv:partAdd: Got all partitions - ready to rock!");
    198 #endif
    199         if (getState() != Volume::State_Formatting) {
    200             setState(Volume::State_Idle);
    201         }
    202     } else {
    203 #ifdef PARTITION_DEBUG
    204         SLOGD("Dv:partAdd: pending mask now = 0x%x", mPendingPartMap);
    205 #endif
    206     }
    207 }
    208 
    209 void DirectVolume::handleDiskChanged(const char *devpath, NetlinkEvent *evt) {
    210     int major = atoi(evt->findParam("MAJOR"));
    211     int minor = atoi(evt->findParam("MINOR"));
    212 
    213     if ((major != mDiskMajor) || (minor != mDiskMinor)) {
    214         return;
    215     }
    216 
    217     SLOGI("Volume %s disk has changed", getLabel());
    218     const char *tmp = evt->findParam("NPARTS");
    219     if (tmp) {
    220         mDiskNumParts = atoi(tmp);
    221     } else {
    222         SLOGW("Kernel block uevent missing 'NPARTS'");
    223         mDiskNumParts = 1;
    224     }
    225 
    226     int partmask = 0;
    227     int i;
    228     for (i = 1; i <= mDiskNumParts; i++) {
    229         partmask |= (1 << i);
    230     }
    231     mPendingPartMap = partmask;
    232 
    233     if (getState() != Volume::State_Formatting) {
    234         if (mDiskNumParts == 0) {
    235             setState(Volume::State_Idle);
    236         } else {
    237             setState(Volume::State_Pending);
    238         }
    239     }
    240 }
    241 
    242 void DirectVolume::handlePartitionChanged(const char *devpath, NetlinkEvent *evt) {
    243     int major = atoi(evt->findParam("MAJOR"));
    244     int minor = atoi(evt->findParam("MINOR"));
    245     SLOGD("Volume %s %s partition %d:%d changed\n", getLabel(), getMountpoint(), major, minor);
    246 }
    247 
    248 void DirectVolume::handleDiskRemoved(const char *devpath, NetlinkEvent *evt) {
    249     int major = atoi(evt->findParam("MAJOR"));
    250     int minor = atoi(evt->findParam("MINOR"));
    251     char msg[255];
    252 
    253     SLOGD("Volume %s %s disk %d:%d removed\n", getLabel(), getMountpoint(), major, minor);
    254     snprintf(msg, sizeof(msg), "Volume %s %s disk removed (%d:%d)",
    255              getLabel(), getMountpoint(), major, minor);
    256     mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskRemoved,
    257                                              msg, false);
    258     setState(Volume::State_NoMedia);
    259 }
    260 
    261 void DirectVolume::handlePartitionRemoved(const char *devpath, NetlinkEvent *evt) {
    262     int major = atoi(evt->findParam("MAJOR"));
    263     int minor = atoi(evt->findParam("MINOR"));
    264     char msg[255];
    265 
    266     SLOGD("Volume %s %s partition %d:%d removed\n", getLabel(), getMountpoint(), major, minor);
    267 
    268     /*
    269      * The framework doesn't need to get notified of
    270      * partition removal unless it's mounted. Otherwise
    271      * the removal notification will be sent on the Disk
    272      * itself
    273      */
    274     if (getState() != Volume::State_Mounted) {
    275         return;
    276     }
    277 
    278     if ((dev_t) MKDEV(major, minor) == mCurrentlyMountedKdev) {
    279         /*
    280          * Yikes, our mounted partition is going away!
    281          */
    282 
    283         snprintf(msg, sizeof(msg), "Volume %s %s bad removal (%d:%d)",
    284                  getLabel(), getMountpoint(), major, minor);
    285         mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,
    286                                              msg, false);
    287 
    288 	if (mVm->cleanupAsec(this, true)) {
    289             SLOGE("Failed to cleanup ASEC - unmount will probably fail!");
    290         }
    291 
    292         if (Volume::unmountVol(true)) {
    293             SLOGE("Failed to unmount volume on bad removal (%s)",
    294                  strerror(errno));
    295             // XXX: At this point we're screwed for now
    296         } else {
    297             SLOGD("Crisis averted");
    298         }
    299     }
    300 }
    301 
    302 /*
    303  * Called from base to get a list of devicenodes for mounting
    304  */
    305 int DirectVolume::getDeviceNodes(dev_t *devs, int max) {
    306 
    307     if (mPartIdx == -1) {
    308         // If the disk has no partitions, try the disk itself
    309         if (!mDiskNumParts) {
    310             devs[0] = MKDEV(mDiskMajor, mDiskMinor);
    311             return 1;
    312         }
    313 
    314         int i;
    315         for (i = 0; i < mDiskNumParts; i++) {
    316             if (i == max)
    317                 break;
    318             devs[i] = MKDEV(mDiskMajor, mPartMinors[i]);
    319         }
    320         return mDiskNumParts;
    321     }
    322     devs[0] = MKDEV(mDiskMajor, mPartMinors[mPartIdx -1]);
    323     return 1;
    324 }
    325