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 bool providesAsec = (getFlags() & VOL_PROVIDES_ASEC) != 0; 325 if (providesAsec && mVm->cleanupAsec(this, true)) { 326 SLOGE("Failed to cleanup ASEC - unmount will probably fail!"); 327 } 328 329 snprintf(msg, sizeof(msg), "Volume %s %s bad removal (%d:%d)", 330 getLabel(), getFuseMountpoint(), major, minor); 331 mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval, 332 msg, false); 333 334 if (Volume::unmountVol(true, false)) { 335 SLOGE("Failed to unmount volume on bad removal (%s)", 336 strerror(errno)); 337 // XXX: At this point we're screwed for now 338 } else { 339 SLOGD("Crisis averted"); 340 } 341 } else if (state == Volume::State_Shared) { 342 /* removed during mass storage */ 343 snprintf(msg, sizeof(msg), "Volume %s bad removal (%d:%d)", 344 getLabel(), major, minor); 345 mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval, 346 msg, false); 347 348 if (mVm->unshareVolume(getLabel(), "ums")) { 349 SLOGE("Failed to unshare volume on bad removal (%s)", 350 strerror(errno)); 351 } else { 352 SLOGD("Crisis averted"); 353 } 354 } 355 } 356 357 /* 358 * Called from base to get a list of devicenodes for mounting 359 */ 360 int DirectVolume::getDeviceNodes(dev_t *devs, int max) { 361 362 if (mPartIdx == -1) { 363 // If the disk has no partitions, try the disk itself 364 if (!mDiskNumParts) { 365 devs[0] = MKDEV(mDiskMajor, mDiskMinor); 366 return 1; 367 } 368 369 int i; 370 for (i = 0; i < mDiskNumParts; i++) { 371 if (i == max) 372 break; 373 devs[i] = MKDEV(mDiskMajor, mPartMinors[i]); 374 } 375 return mDiskNumParts; 376 } 377 devs[0] = MKDEV(mDiskMajor, mPartMinors[mPartIdx -1]); 378 return 1; 379 } 380 381 /* 382 * Called from base to update device info, 383 * e.g. When setting up an dm-crypt mapping for the sd card. 384 */ 385 int DirectVolume::updateDeviceInfo(char *new_path, int new_major, int new_minor) 386 { 387 PathCollection::iterator it; 388 389 if (mPartIdx == -1) { 390 SLOGE("Can only change device info on a partition\n"); 391 return -1; 392 } 393 394 /* 395 * This is to change the sysfs path associated with a partition, in particular, 396 * for an internal SD card partition that is encrypted. Thus, the list is 397 * expected to be only 1 entry long. Check that and bail if not. 398 */ 399 if (mPaths->size() != 1) { 400 SLOGE("Cannot change path if there are more than one for a volume\n"); 401 return -1; 402 } 403 404 it = mPaths->begin(); 405 free(*it); /* Free the string storage */ 406 mPaths->erase(it); /* Remove it from the list */ 407 addPath(new_path); /* Put the new path on the list */ 408 409 /* Save away original info so we can restore it when doing factory reset. 410 * Then, when doing the format, it will format the original device in the 411 * clear, otherwise it just formats the encrypted device which is not 412 * readable when the device boots unencrypted after the reset. 413 */ 414 mOrigDiskMajor = mDiskMajor; 415 mOrigDiskMinor = mDiskMinor; 416 mOrigPartIdx = mPartIdx; 417 memcpy(mOrigPartMinors, mPartMinors, sizeof(mPartMinors)); 418 419 mDiskMajor = new_major; 420 mDiskMinor = new_minor; 421 /* Ugh, virual block devices don't use minor 0 for whole disk and minor > 0 for 422 * partition number. They don't have partitions, they are just virtual block 423 * devices, and minor number 0 is the first dm-crypt device. Luckily the first 424 * dm-crypt device is for the userdata partition, which gets minor number 0, and 425 * it is not managed by vold. So the next device is minor number one, which we 426 * will call partition one. 427 */ 428 mPartIdx = new_minor; 429 mPartMinors[new_minor-1] = new_minor; 430 431 mIsDecrypted = 1; 432 433 return 0; 434 } 435 436 /* 437 * Called from base to revert device info to the way it was before a 438 * crypto mapping was created for it. 439 */ 440 void DirectVolume::revertDeviceInfo(void) 441 { 442 if (mIsDecrypted) { 443 mDiskMajor = mOrigDiskMajor; 444 mDiskMinor = mOrigDiskMinor; 445 mPartIdx = mOrigPartIdx; 446 memcpy(mPartMinors, mOrigPartMinors, sizeof(mPartMinors)); 447 448 mIsDecrypted = 0; 449 } 450 451 return; 452 } 453 454 /* 455 * Called from base to give cryptfs all the info it needs to encrypt eligible volumes 456 */ 457 int DirectVolume::getVolInfo(struct volume_info *v) 458 { 459 strcpy(v->label, mLabel); 460 strcpy(v->mnt_point, mMountpoint); 461 v->flags = getFlags(); 462 /* Other fields of struct volume_info are filled in by the caller or cryptfs.c */ 463 464 return 0; 465 } 466