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