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