DirectVolume.cpp revision d766090b7a72562be9e64700e13882663004650e
1be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root/* 2be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * Copyright (C) 2008 The Android Open Source Project 3be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * 4be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * Licensed under the Apache License, Version 2.0 (the "License"); 5be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * you may not use this file except in compliance with the License. 6be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * You may obtain a copy of the License at 7be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * 8be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * http://www.apache.org/licenses/LICENSE-2.0 9be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * 10be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * Unless required by applicable law or agreed to in writing, software 11be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * distributed under the License is distributed on an "AS IS" BASIS, 12be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * See the License for the specific language governing permissions and 14be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * limitations under the License. 15be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root */ 16be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root 17be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#include <stdio.h> 18be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#include <stdlib.h> 19be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#include <string.h> 20be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#include <errno.h> 21be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root 22be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#include <linux/kdev_t.h> 23be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root 24be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#define LOG_TAG "DirectVolume" 25be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root 26be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#include <cutils/log.h> 27be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#include <sysutils/NetlinkEvent.h> 28be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root 29be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#include "DirectVolume.h" 30be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#include "VolumeManager.h" 31be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#include "ResponseCode.h" 32be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root 33be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root// #define PARTITION_DEBUG 34be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root 35be857d42849eaaa554d4772dbba7755f8a0f3547Kenny RootDirectVolume::DirectVolume(VolumeManager *vm, const char *label, 36be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root const char *mount_point, int partIdx) : 37be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root Volume(vm, label, mount_point) { 38be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root mPartIdx = partIdx; 39be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root 40be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root mPaths = new PathCollection(); 41be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root for (int i = 0; i < MAX_PARTITIONS; i++) 42be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root mPartMinors[i] = -1; 43be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root mPendingPartMap = 0; 44be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root mDiskMajor = -1; 45be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root mDiskMinor = -1; 46be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root mDiskNumParts = 0; 47be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root 48be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root setState(Volume::State_NoMedia); 49be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root} 50be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root 51be857d42849eaaa554d4772dbba7755f8a0f3547Kenny RootDirectVolume::~DirectVolume() { 52be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root PathCollection::iterator it; 53be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root 54 for (it = mPaths->begin(); it != mPaths->end(); ++it) 55 free(*it); 56 delete mPaths; 57} 58 59int DirectVolume::addPath(const char *path) { 60 mPaths->push_back(strdup(path)); 61 return 0; 62} 63 64dev_t DirectVolume::getDiskDevice() { 65 return MKDEV(mDiskMajor, mDiskMinor); 66} 67 68void DirectVolume::handleVolumeShared() { 69 setState(Volume::State_Shared); 70} 71 72void DirectVolume::handleVolumeUnshared() { 73 setState(Volume::State_Idle); 74} 75 76int 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 126void 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 166void 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 if (part_num >= MAX_PARTITIONS) { 193 SLOGE("Dv:partAdd: ignoring part_num = %d (max: %d)\n", part_num, MAX_PARTITIONS-1); 194 } else { 195 mPartMinors[part_num -1] = minor; 196 } 197 mPendingPartMap &= ~(1 << part_num); 198 199 if (!mPendingPartMap) { 200#ifdef PARTITION_DEBUG 201 SLOGD("Dv:partAdd: Got all partitions - ready to rock!"); 202#endif 203 if (getState() != Volume::State_Formatting) { 204 setState(Volume::State_Idle); 205 } 206 } else { 207#ifdef PARTITION_DEBUG 208 SLOGD("Dv:partAdd: pending mask now = 0x%x", mPendingPartMap); 209#endif 210 } 211} 212 213void DirectVolume::handleDiskChanged(const char *devpath, NetlinkEvent *evt) { 214 int major = atoi(evt->findParam("MAJOR")); 215 int minor = atoi(evt->findParam("MINOR")); 216 217 if ((major != mDiskMajor) || (minor != mDiskMinor)) { 218 return; 219 } 220 221 SLOGI("Volume %s disk has changed", getLabel()); 222 const char *tmp = evt->findParam("NPARTS"); 223 if (tmp) { 224 mDiskNumParts = atoi(tmp); 225 } else { 226 SLOGW("Kernel block uevent missing 'NPARTS'"); 227 mDiskNumParts = 1; 228 } 229 230 int partmask = 0; 231 int i; 232 for (i = 1; i <= mDiskNumParts; i++) { 233 partmask |= (1 << i); 234 } 235 mPendingPartMap = partmask; 236 237 if (getState() != Volume::State_Formatting) { 238 if (mDiskNumParts == 0) { 239 setState(Volume::State_Idle); 240 } else { 241 setState(Volume::State_Pending); 242 } 243 } 244} 245 246void DirectVolume::handlePartitionChanged(const char *devpath, NetlinkEvent *evt) { 247 int major = atoi(evt->findParam("MAJOR")); 248 int minor = atoi(evt->findParam("MINOR")); 249 SLOGD("Volume %s %s partition %d:%d changed\n", getLabel(), getMountpoint(), major, minor); 250} 251 252void DirectVolume::handleDiskRemoved(const char *devpath, NetlinkEvent *evt) { 253 int major = atoi(evt->findParam("MAJOR")); 254 int minor = atoi(evt->findParam("MINOR")); 255 char msg[255]; 256 257 SLOGD("Volume %s %s disk %d:%d removed\n", getLabel(), getMountpoint(), major, minor); 258 snprintf(msg, sizeof(msg), "Volume %s %s disk removed (%d:%d)", 259 getLabel(), getMountpoint(), major, minor); 260 mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskRemoved, 261 msg, false); 262 setState(Volume::State_NoMedia); 263} 264 265void DirectVolume::handlePartitionRemoved(const char *devpath, NetlinkEvent *evt) { 266 int major = atoi(evt->findParam("MAJOR")); 267 int minor = atoi(evt->findParam("MINOR")); 268 char msg[255]; 269 270 SLOGD("Volume %s %s partition %d:%d removed\n", getLabel(), getMountpoint(), major, minor); 271 272 /* 273 * The framework doesn't need to get notified of 274 * partition removal unless it's mounted. Otherwise 275 * the removal notification will be sent on the Disk 276 * itself 277 */ 278 if (getState() != Volume::State_Mounted) { 279 return; 280 } 281 282 if ((dev_t) MKDEV(major, minor) == mCurrentlyMountedKdev) { 283 /* 284 * Yikes, our mounted partition is going away! 285 */ 286 287 snprintf(msg, sizeof(msg), "Volume %s %s bad removal (%d:%d)", 288 getLabel(), getMountpoint(), major, minor); 289 mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval, 290 msg, false); 291 292 if (mVm->cleanupAsec(this, true)) { 293 SLOGE("Failed to cleanup ASEC - unmount will probably fail!"); 294 } 295 296 if (Volume::unmountVol(true)) { 297 SLOGE("Failed to unmount volume on bad removal (%s)", 298 strerror(errno)); 299 // XXX: At this point we're screwed for now 300 } else { 301 SLOGD("Crisis averted"); 302 } 303 } 304} 305 306/* 307 * Called from base to get a list of devicenodes for mounting 308 */ 309int DirectVolume::getDeviceNodes(dev_t *devs, int max) { 310 311 if (mPartIdx == -1) { 312 // If the disk has no partitions, try the disk itself 313 if (!mDiskNumParts) { 314 devs[0] = MKDEV(mDiskMajor, mDiskMinor); 315 return 1; 316 } 317 318 int i; 319 for (i = 0; i < mDiskNumParts; i++) { 320 if (i == max) 321 break; 322 devs[i] = MKDEV(mDiskMajor, mPartMinors[i]); 323 } 324 return mDiskNumParts; 325 } 326 devs[0] = MKDEV(mDiskMajor, mPartMinors[mPartIdx -1]); 327 return 1; 328} 329