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