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 <fcntl.h> 22 #include <sys/stat.h> 23 #include <sys/types.h> 24 #include <sys/mount.h> 25 26 #include <linux/kdev_t.h> 27 28 #define LOG_TAG "Vold" 29 30 #include <openssl/md5.h> 31 32 #include <cutils/log.h> 33 34 #include <sysutils/NetlinkEvent.h> 35 36 #include "VolumeManager.h" 37 #include "DirectVolume.h" 38 #include "ResponseCode.h" 39 #include "Loop.h" 40 #include "Fat.h" 41 #include "Devmapper.h" 42 #include "Process.h" 43 #include "Asec.h" 44 #include "cryptfs.h" 45 46 #define MASS_STORAGE_FILE_PATH "/sys/class/android_usb/android0/f_mass_storage/lun/file" 47 48 VolumeManager *VolumeManager::sInstance = NULL; 49 50 VolumeManager *VolumeManager::Instance() { 51 if (!sInstance) 52 sInstance = new VolumeManager(); 53 return sInstance; 54 } 55 56 VolumeManager::VolumeManager() { 57 mDebug = false; 58 mVolumes = new VolumeCollection(); 59 mActiveContainers = new AsecIdCollection(); 60 mBroadcaster = NULL; 61 mUmsSharingCount = 0; 62 mSavedDirtyRatio = -1; 63 // set dirty ratio to 0 when UMS is active 64 mUmsDirtyRatio = 0; 65 mVolManagerDisabled = 0; 66 } 67 68 VolumeManager::~VolumeManager() { 69 delete mVolumes; 70 delete mActiveContainers; 71 } 72 73 char *VolumeManager::asecHash(const char *id, char *buffer, size_t len) { 74 static const char* digits = "0123456789abcdef"; 75 76 unsigned char sig[MD5_DIGEST_LENGTH]; 77 78 if (buffer == NULL) { 79 SLOGE("Destination buffer is NULL"); 80 errno = ESPIPE; 81 return NULL; 82 } else if (id == NULL) { 83 SLOGE("Source buffer is NULL"); 84 errno = ESPIPE; 85 return NULL; 86 } else if (len < MD5_ASCII_LENGTH_PLUS_NULL) { 87 SLOGE("Target hash buffer size < %d bytes (%d)", 88 MD5_ASCII_LENGTH_PLUS_NULL, len); 89 errno = ESPIPE; 90 return NULL; 91 } 92 93 MD5(reinterpret_cast<const unsigned char*>(id), strlen(id), sig); 94 95 char *p = buffer; 96 for (int i = 0; i < MD5_DIGEST_LENGTH; i++) { 97 *p++ = digits[sig[i] >> 4]; 98 *p++ = digits[sig[i] & 0x0F]; 99 } 100 *p = '\0'; 101 102 return buffer; 103 } 104 105 void VolumeManager::setDebug(bool enable) { 106 mDebug = enable; 107 VolumeCollection::iterator it; 108 for (it = mVolumes->begin(); it != mVolumes->end(); ++it) { 109 (*it)->setDebug(enable); 110 } 111 } 112 113 int VolumeManager::start() { 114 return 0; 115 } 116 117 int VolumeManager::stop() { 118 return 0; 119 } 120 121 int VolumeManager::addVolume(Volume *v) { 122 mVolumes->push_back(v); 123 return 0; 124 } 125 126 void VolumeManager::handleBlockEvent(NetlinkEvent *evt) { 127 const char *devpath = evt->findParam("DEVPATH"); 128 129 /* Lookup a volume to handle this device */ 130 VolumeCollection::iterator it; 131 bool hit = false; 132 for (it = mVolumes->begin(); it != mVolumes->end(); ++it) { 133 if (!(*it)->handleBlockEvent(evt)) { 134 #ifdef NETLINK_DEBUG 135 SLOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel()); 136 #endif 137 hit = true; 138 break; 139 } 140 } 141 142 if (!hit) { 143 #ifdef NETLINK_DEBUG 144 SLOGW("No volumes handled block event for '%s'", devpath); 145 #endif 146 } 147 } 148 149 int VolumeManager::listVolumes(SocketClient *cli) { 150 VolumeCollection::iterator i; 151 152 for (i = mVolumes->begin(); i != mVolumes->end(); ++i) { 153 char *buffer; 154 asprintf(&buffer, "%s %s %d", 155 (*i)->getLabel(), (*i)->getMountpoint(), 156 (*i)->getState()); 157 cli->sendMsg(ResponseCode::VolumeListResult, buffer, false); 158 free(buffer); 159 } 160 cli->sendMsg(ResponseCode::CommandOkay, "Volumes listed.", false); 161 return 0; 162 } 163 164 int VolumeManager::formatVolume(const char *label) { 165 Volume *v = lookupVolume(label); 166 167 if (!v) { 168 errno = ENOENT; 169 return -1; 170 } 171 172 if (mVolManagerDisabled) { 173 errno = EBUSY; 174 return -1; 175 } 176 177 return v->formatVol(); 178 } 179 180 int VolumeManager::getObbMountPath(const char *sourceFile, char *mountPath, int mountPathLen) { 181 char idHash[33]; 182 if (!asecHash(sourceFile, idHash, sizeof(idHash))) { 183 SLOGE("Hash of '%s' failed (%s)", sourceFile, strerror(errno)); 184 return -1; 185 } 186 187 memset(mountPath, 0, mountPathLen); 188 snprintf(mountPath, mountPathLen, "%s/%s", Volume::LOOPDIR, idHash); 189 190 if (access(mountPath, F_OK)) { 191 errno = ENOENT; 192 return -1; 193 } 194 195 return 0; 196 } 197 198 int VolumeManager::getAsecMountPath(const char *id, char *buffer, int maxlen) { 199 char asecFileName[255]; 200 snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id); 201 202 memset(buffer, 0, maxlen); 203 if (access(asecFileName, F_OK)) { 204 errno = ENOENT; 205 return -1; 206 } 207 208 snprintf(buffer, maxlen, "%s/%s", Volume::ASECDIR, id); 209 return 0; 210 } 211 212 int VolumeManager::getAsecFilesystemPath(const char *id, char *buffer, int maxlen) { 213 char asecFileName[255]; 214 snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id); 215 216 memset(buffer, 0, maxlen); 217 if (access(asecFileName, F_OK)) { 218 errno = ENOENT; 219 return -1; 220 } 221 222 snprintf(buffer, maxlen, "%s", asecFileName); 223 return 0; 224 } 225 226 int VolumeManager::createAsec(const char *id, unsigned int numSectors, 227 const char *fstype, const char *key, int ownerUid) { 228 struct asec_superblock sb; 229 memset(&sb, 0, sizeof(sb)); 230 231 sb.magic = ASEC_SB_MAGIC; 232 sb.ver = ASEC_SB_VER; 233 234 if (numSectors < ((1024*1024)/512)) { 235 SLOGE("Invalid container size specified (%d sectors)", numSectors); 236 errno = EINVAL; 237 return -1; 238 } 239 240 if (lookupVolume(id)) { 241 SLOGE("ASEC id '%s' currently exists", id); 242 errno = EADDRINUSE; 243 return -1; 244 } 245 246 char asecFileName[255]; 247 snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id); 248 249 if (!access(asecFileName, F_OK)) { 250 SLOGE("ASEC file '%s' currently exists - destroy it first! (%s)", 251 asecFileName, strerror(errno)); 252 errno = EADDRINUSE; 253 return -1; 254 } 255 256 /* 257 * Add some headroom 258 */ 259 unsigned fatSize = (((numSectors * 4) / 512) + 1) * 2; 260 unsigned numImgSectors = numSectors + fatSize + 2; 261 262 if (numImgSectors % 63) { 263 numImgSectors += (63 - (numImgSectors % 63)); 264 } 265 266 // Add +1 for our superblock which is at the end 267 if (Loop::createImageFile(asecFileName, numImgSectors + 1)) { 268 SLOGE("ASEC image file creation failed (%s)", strerror(errno)); 269 return -1; 270 } 271 272 char idHash[33]; 273 if (!asecHash(id, idHash, sizeof(idHash))) { 274 SLOGE("Hash of '%s' failed (%s)", id, strerror(errno)); 275 unlink(asecFileName); 276 return -1; 277 } 278 279 char loopDevice[255]; 280 if (Loop::create(idHash, asecFileName, loopDevice, sizeof(loopDevice))) { 281 SLOGE("ASEC loop device creation failed (%s)", strerror(errno)); 282 unlink(asecFileName); 283 return -1; 284 } 285 286 char dmDevice[255]; 287 bool cleanupDm = false; 288 289 if (strcmp(key, "none")) { 290 // XXX: This is all we support for now 291 sb.c_cipher = ASEC_SB_C_CIPHER_TWOFISH; 292 if (Devmapper::create(idHash, loopDevice, key, numImgSectors, dmDevice, 293 sizeof(dmDevice))) { 294 SLOGE("ASEC device mapping failed (%s)", strerror(errno)); 295 Loop::destroyByDevice(loopDevice); 296 unlink(asecFileName); 297 return -1; 298 } 299 cleanupDm = true; 300 } else { 301 sb.c_cipher = ASEC_SB_C_CIPHER_NONE; 302 strcpy(dmDevice, loopDevice); 303 } 304 305 /* 306 * Drop down the superblock at the end of the file 307 */ 308 309 int sbfd = open(loopDevice, O_RDWR); 310 if (sbfd < 0) { 311 SLOGE("Failed to open new DM device for superblock write (%s)", strerror(errno)); 312 if (cleanupDm) { 313 Devmapper::destroy(idHash); 314 } 315 Loop::destroyByDevice(loopDevice); 316 unlink(asecFileName); 317 return -1; 318 } 319 320 if (lseek(sbfd, (numImgSectors * 512), SEEK_SET) < 0) { 321 close(sbfd); 322 SLOGE("Failed to lseek for superblock (%s)", strerror(errno)); 323 if (cleanupDm) { 324 Devmapper::destroy(idHash); 325 } 326 Loop::destroyByDevice(loopDevice); 327 unlink(asecFileName); 328 return -1; 329 } 330 331 if (write(sbfd, &sb, sizeof(sb)) != sizeof(sb)) { 332 close(sbfd); 333 SLOGE("Failed to write superblock (%s)", strerror(errno)); 334 if (cleanupDm) { 335 Devmapper::destroy(idHash); 336 } 337 Loop::destroyByDevice(loopDevice); 338 unlink(asecFileName); 339 return -1; 340 } 341 close(sbfd); 342 343 if (strcmp(fstype, "none")) { 344 if (strcmp(fstype, "fat")) { 345 SLOGW("Unknown fstype '%s' specified for container", fstype); 346 } 347 348 if (Fat::format(dmDevice, numImgSectors)) { 349 SLOGE("ASEC FAT format failed (%s)", strerror(errno)); 350 if (cleanupDm) { 351 Devmapper::destroy(idHash); 352 } 353 Loop::destroyByDevice(loopDevice); 354 unlink(asecFileName); 355 return -1; 356 } 357 char mountPoint[255]; 358 359 snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id); 360 if (mkdir(mountPoint, 0777)) { 361 if (errno != EEXIST) { 362 SLOGE("Mountpoint creation failed (%s)", strerror(errno)); 363 if (cleanupDm) { 364 Devmapper::destroy(idHash); 365 } 366 Loop::destroyByDevice(loopDevice); 367 unlink(asecFileName); 368 return -1; 369 } 370 } 371 372 if (Fat::doMount(dmDevice, mountPoint, false, false, false, ownerUid, 373 0, 0000, false)) { 374 SLOGE("ASEC FAT mount failed (%s)", strerror(errno)); 375 if (cleanupDm) { 376 Devmapper::destroy(idHash); 377 } 378 Loop::destroyByDevice(loopDevice); 379 unlink(asecFileName); 380 return -1; 381 } 382 } else { 383 SLOGI("Created raw secure container %s (no filesystem)", id); 384 } 385 386 mActiveContainers->push_back(new ContainerData(strdup(id), ASEC)); 387 return 0; 388 } 389 390 int VolumeManager::finalizeAsec(const char *id) { 391 char asecFileName[255]; 392 char loopDevice[255]; 393 char mountPoint[255]; 394 395 snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id); 396 397 char idHash[33]; 398 if (!asecHash(id, idHash, sizeof(idHash))) { 399 SLOGE("Hash of '%s' failed (%s)", id, strerror(errno)); 400 return -1; 401 } 402 403 if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) { 404 SLOGE("Unable to finalize %s (%s)", id, strerror(errno)); 405 return -1; 406 } 407 408 snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id); 409 // XXX: 410 if (Fat::doMount(loopDevice, mountPoint, true, true, true, 0, 0, 0227, false)) { 411 SLOGE("ASEC finalize mount failed (%s)", strerror(errno)); 412 return -1; 413 } 414 415 if (mDebug) { 416 SLOGD("ASEC %s finalized", id); 417 } 418 return 0; 419 } 420 421 int VolumeManager::renameAsec(const char *id1, const char *id2) { 422 char *asecFilename1; 423 char *asecFilename2; 424 char mountPoint[255]; 425 426 asprintf(&asecFilename1, "%s/%s.asec", Volume::SEC_ASECDIR, id1); 427 asprintf(&asecFilename2, "%s/%s.asec", Volume::SEC_ASECDIR, id2); 428 429 snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id1); 430 if (isMountpointMounted(mountPoint)) { 431 SLOGW("Rename attempt when src mounted"); 432 errno = EBUSY; 433 goto out_err; 434 } 435 436 snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id2); 437 if (isMountpointMounted(mountPoint)) { 438 SLOGW("Rename attempt when dst mounted"); 439 errno = EBUSY; 440 goto out_err; 441 } 442 443 if (!access(asecFilename2, F_OK)) { 444 SLOGE("Rename attempt when dst exists"); 445 errno = EADDRINUSE; 446 goto out_err; 447 } 448 449 if (rename(asecFilename1, asecFilename2)) { 450 SLOGE("Rename of '%s' to '%s' failed (%s)", asecFilename1, asecFilename2, strerror(errno)); 451 goto out_err; 452 } 453 454 free(asecFilename1); 455 free(asecFilename2); 456 return 0; 457 458 out_err: 459 free(asecFilename1); 460 free(asecFilename2); 461 return -1; 462 } 463 464 #define UNMOUNT_RETRIES 5 465 #define UNMOUNT_SLEEP_BETWEEN_RETRY_MS (1000 * 1000) 466 int VolumeManager::unmountAsec(const char *id, bool force) { 467 char asecFileName[255]; 468 char mountPoint[255]; 469 470 snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id); 471 snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id); 472 473 char idHash[33]; 474 if (!asecHash(id, idHash, sizeof(idHash))) { 475 SLOGE("Hash of '%s' failed (%s)", id, strerror(errno)); 476 return -1; 477 } 478 479 return unmountLoopImage(id, idHash, asecFileName, mountPoint, force); 480 } 481 482 int VolumeManager::unmountObb(const char *fileName, bool force) { 483 char mountPoint[255]; 484 485 char idHash[33]; 486 if (!asecHash(fileName, idHash, sizeof(idHash))) { 487 SLOGE("Hash of '%s' failed (%s)", fileName, strerror(errno)); 488 return -1; 489 } 490 491 snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::LOOPDIR, idHash); 492 493 return unmountLoopImage(fileName, idHash, fileName, mountPoint, force); 494 } 495 496 int VolumeManager::unmountLoopImage(const char *id, const char *idHash, 497 const char *fileName, const char *mountPoint, bool force) { 498 if (!isMountpointMounted(mountPoint)) { 499 SLOGE("Unmount request for %s when not mounted", id); 500 errno = ENOENT; 501 return -1; 502 } 503 504 int i, rc; 505 for (i = 1; i <= UNMOUNT_RETRIES; i++) { 506 rc = umount(mountPoint); 507 if (!rc) { 508 break; 509 } 510 if (rc && (errno == EINVAL || errno == ENOENT)) { 511 SLOGI("Container %s unmounted OK", id); 512 rc = 0; 513 break; 514 } 515 SLOGW("%s unmount attempt %d failed (%s)", 516 id, i, strerror(errno)); 517 518 int action = 0; // default is to just complain 519 520 if (force) { 521 if (i > (UNMOUNT_RETRIES - 2)) 522 action = 2; // SIGKILL 523 else if (i > (UNMOUNT_RETRIES - 3)) 524 action = 1; // SIGHUP 525 } 526 527 Process::killProcessesWithOpenFiles(mountPoint, action); 528 usleep(UNMOUNT_SLEEP_BETWEEN_RETRY_MS); 529 } 530 531 if (rc) { 532 errno = EBUSY; 533 SLOGE("Failed to unmount container %s (%s)", id, strerror(errno)); 534 return -1; 535 } 536 537 int retries = 10; 538 539 while(retries--) { 540 if (!rmdir(mountPoint)) { 541 break; 542 } 543 544 SLOGW("Failed to rmdir %s (%s)", mountPoint, strerror(errno)); 545 usleep(UNMOUNT_SLEEP_BETWEEN_RETRY_MS); 546 } 547 548 if (!retries) { 549 SLOGE("Timed out trying to rmdir %s (%s)", mountPoint, strerror(errno)); 550 } 551 552 if (Devmapper::destroy(idHash) && errno != ENXIO) { 553 SLOGE("Failed to destroy devmapper instance (%s)", strerror(errno)); 554 } 555 556 char loopDevice[255]; 557 if (!Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) { 558 Loop::destroyByDevice(loopDevice); 559 } else { 560 SLOGW("Failed to find loop device for {%s} (%s)", fileName, strerror(errno)); 561 } 562 563 AsecIdCollection::iterator it; 564 for (it = mActiveContainers->begin(); it != mActiveContainers->end(); ++it) { 565 ContainerData* cd = *it; 566 if (!strcmp(cd->id, id)) { 567 free(*it); 568 mActiveContainers->erase(it); 569 break; 570 } 571 } 572 if (it == mActiveContainers->end()) { 573 SLOGW("mActiveContainers is inconsistent!"); 574 } 575 return 0; 576 } 577 578 int VolumeManager::destroyAsec(const char *id, bool force) { 579 char asecFileName[255]; 580 char mountPoint[255]; 581 582 snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id); 583 snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id); 584 585 if (isMountpointMounted(mountPoint)) { 586 if (mDebug) { 587 SLOGD("Unmounting container before destroy"); 588 } 589 if (unmountAsec(id, force)) { 590 SLOGE("Failed to unmount asec %s for destroy (%s)", id, strerror(errno)); 591 return -1; 592 } 593 } 594 595 if (unlink(asecFileName)) { 596 SLOGE("Failed to unlink asec '%s' (%s)", asecFileName, strerror(errno)); 597 return -1; 598 } 599 600 if (mDebug) { 601 SLOGD("ASEC %s destroyed", id); 602 } 603 return 0; 604 } 605 606 int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid) { 607 char asecFileName[255]; 608 char mountPoint[255]; 609 610 snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id); 611 snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id); 612 613 if (isMountpointMounted(mountPoint)) { 614 SLOGE("ASEC %s already mounted", id); 615 errno = EBUSY; 616 return -1; 617 } 618 619 char idHash[33]; 620 if (!asecHash(id, idHash, sizeof(idHash))) { 621 SLOGE("Hash of '%s' failed (%s)", id, strerror(errno)); 622 return -1; 623 } 624 625 char loopDevice[255]; 626 if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) { 627 if (Loop::create(idHash, asecFileName, loopDevice, sizeof(loopDevice))) { 628 SLOGE("ASEC loop device creation failed (%s)", strerror(errno)); 629 return -1; 630 } 631 if (mDebug) { 632 SLOGD("New loop device created at %s", loopDevice); 633 } 634 } else { 635 if (mDebug) { 636 SLOGD("Found active loopback for %s at %s", asecFileName, loopDevice); 637 } 638 } 639 640 char dmDevice[255]; 641 bool cleanupDm = false; 642 int fd; 643 unsigned int nr_sec = 0; 644 645 if ((fd = open(loopDevice, O_RDWR)) < 0) { 646 SLOGE("Failed to open loopdevice (%s)", strerror(errno)); 647 Loop::destroyByDevice(loopDevice); 648 return -1; 649 } 650 651 if (ioctl(fd, BLKGETSIZE, &nr_sec)) { 652 SLOGE("Failed to get loop size (%s)", strerror(errno)); 653 Loop::destroyByDevice(loopDevice); 654 close(fd); 655 return -1; 656 } 657 658 /* 659 * Validate superblock 660 */ 661 struct asec_superblock sb; 662 memset(&sb, 0, sizeof(sb)); 663 if (lseek(fd, ((nr_sec-1) * 512), SEEK_SET) < 0) { 664 SLOGE("lseek failed (%s)", strerror(errno)); 665 close(fd); 666 Loop::destroyByDevice(loopDevice); 667 return -1; 668 } 669 if (read(fd, &sb, sizeof(sb)) != sizeof(sb)) { 670 SLOGE("superblock read failed (%s)", strerror(errno)); 671 close(fd); 672 Loop::destroyByDevice(loopDevice); 673 return -1; 674 } 675 676 close(fd); 677 678 if (mDebug) { 679 SLOGD("Container sb magic/ver (%.8x/%.2x)", sb.magic, sb.ver); 680 } 681 if (sb.magic != ASEC_SB_MAGIC || sb.ver != ASEC_SB_VER) { 682 SLOGE("Bad container magic/version (%.8x/%.2x)", sb.magic, sb.ver); 683 Loop::destroyByDevice(loopDevice); 684 errno = EMEDIUMTYPE; 685 return -1; 686 } 687 nr_sec--; // We don't want the devmapping to extend onto our superblock 688 689 if (strcmp(key, "none")) { 690 if (Devmapper::lookupActive(idHash, dmDevice, sizeof(dmDevice))) { 691 if (Devmapper::create(idHash, loopDevice, key, nr_sec, 692 dmDevice, sizeof(dmDevice))) { 693 SLOGE("ASEC device mapping failed (%s)", strerror(errno)); 694 Loop::destroyByDevice(loopDevice); 695 return -1; 696 } 697 if (mDebug) { 698 SLOGD("New devmapper instance created at %s", dmDevice); 699 } 700 } else { 701 if (mDebug) { 702 SLOGD("Found active devmapper for %s at %s", asecFileName, dmDevice); 703 } 704 } 705 cleanupDm = true; 706 } else { 707 strcpy(dmDevice, loopDevice); 708 } 709 710 if (mkdir(mountPoint, 0777)) { 711 if (errno != EEXIST) { 712 SLOGE("Mountpoint creation failed (%s)", strerror(errno)); 713 if (cleanupDm) { 714 Devmapper::destroy(idHash); 715 } 716 Loop::destroyByDevice(loopDevice); 717 return -1; 718 } 719 } 720 721 if (Fat::doMount(dmDevice, mountPoint, true, false, true, ownerUid, 0, 722 0222, false)) { 723 // 0227, false)) { 724 SLOGE("ASEC mount failed (%s)", strerror(errno)); 725 if (cleanupDm) { 726 Devmapper::destroy(idHash); 727 } 728 Loop::destroyByDevice(loopDevice); 729 return -1; 730 } 731 732 mActiveContainers->push_back(new ContainerData(strdup(id), ASEC)); 733 if (mDebug) { 734 SLOGD("ASEC %s mounted", id); 735 } 736 return 0; 737 } 738 739 /** 740 * Mounts an image file <code>img</code>. 741 */ 742 int VolumeManager::mountObb(const char *img, const char *key, int ownerUid) { 743 char mountPoint[255]; 744 745 char idHash[33]; 746 if (!asecHash(img, idHash, sizeof(idHash))) { 747 SLOGE("Hash of '%s' failed (%s)", img, strerror(errno)); 748 return -1; 749 } 750 751 snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::LOOPDIR, idHash); 752 753 if (isMountpointMounted(mountPoint)) { 754 SLOGE("Image %s already mounted", img); 755 errno = EBUSY; 756 return -1; 757 } 758 759 char loopDevice[255]; 760 if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) { 761 if (Loop::create(idHash, img, loopDevice, sizeof(loopDevice))) { 762 SLOGE("Image loop device creation failed (%s)", strerror(errno)); 763 return -1; 764 } 765 if (mDebug) { 766 SLOGD("New loop device created at %s", loopDevice); 767 } 768 } else { 769 if (mDebug) { 770 SLOGD("Found active loopback for %s at %s", img, loopDevice); 771 } 772 } 773 774 char dmDevice[255]; 775 bool cleanupDm = false; 776 int fd; 777 unsigned int nr_sec = 0; 778 779 if ((fd = open(loopDevice, O_RDWR)) < 0) { 780 SLOGE("Failed to open loopdevice (%s)", strerror(errno)); 781 Loop::destroyByDevice(loopDevice); 782 return -1; 783 } 784 785 if (ioctl(fd, BLKGETSIZE, &nr_sec)) { 786 SLOGE("Failed to get loop size (%s)", strerror(errno)); 787 Loop::destroyByDevice(loopDevice); 788 close(fd); 789 return -1; 790 } 791 792 close(fd); 793 794 if (strcmp(key, "none")) { 795 if (Devmapper::lookupActive(idHash, dmDevice, sizeof(dmDevice))) { 796 if (Devmapper::create(idHash, loopDevice, key, nr_sec, 797 dmDevice, sizeof(dmDevice))) { 798 SLOGE("ASEC device mapping failed (%s)", strerror(errno)); 799 Loop::destroyByDevice(loopDevice); 800 return -1; 801 } 802 if (mDebug) { 803 SLOGD("New devmapper instance created at %s", dmDevice); 804 } 805 } else { 806 if (mDebug) { 807 SLOGD("Found active devmapper for %s at %s", img, dmDevice); 808 } 809 } 810 cleanupDm = true; 811 } else { 812 strcpy(dmDevice, loopDevice); 813 } 814 815 if (mkdir(mountPoint, 0755)) { 816 if (errno != EEXIST) { 817 SLOGE("Mountpoint creation failed (%s)", strerror(errno)); 818 if (cleanupDm) { 819 Devmapper::destroy(idHash); 820 } 821 Loop::destroyByDevice(loopDevice); 822 return -1; 823 } 824 } 825 826 if (Fat::doMount(dmDevice, mountPoint, true, false, true, ownerUid, 0, 827 0227, false)) { 828 SLOGE("Image mount failed (%s)", strerror(errno)); 829 if (cleanupDm) { 830 Devmapper::destroy(idHash); 831 } 832 Loop::destroyByDevice(loopDevice); 833 return -1; 834 } 835 836 mActiveContainers->push_back(new ContainerData(strdup(img), OBB)); 837 if (mDebug) { 838 SLOGD("Image %s mounted", img); 839 } 840 return 0; 841 } 842 843 int VolumeManager::mountVolume(const char *label) { 844 Volume *v = lookupVolume(label); 845 846 if (!v) { 847 errno = ENOENT; 848 return -1; 849 } 850 851 return v->mountVol(); 852 } 853 854 int VolumeManager::listMountedObbs(SocketClient* cli) { 855 char device[256]; 856 char mount_path[256]; 857 char rest[256]; 858 FILE *fp; 859 char line[1024]; 860 861 if (!(fp = fopen("/proc/mounts", "r"))) { 862 SLOGE("Error opening /proc/mounts (%s)", strerror(errno)); 863 return -1; 864 } 865 866 // Create a string to compare against that has a trailing slash 867 int loopDirLen = sizeof(Volume::LOOPDIR); 868 char loopDir[loopDirLen + 2]; 869 strcpy(loopDir, Volume::LOOPDIR); 870 loopDir[loopDirLen++] = '/'; 871 loopDir[loopDirLen] = '\0'; 872 873 while(fgets(line, sizeof(line), fp)) { 874 line[strlen(line)-1] = '\0'; 875 876 /* 877 * Should look like: 878 * /dev/block/loop0 /mnt/obb/fc99df1323fd36424f864dcb76b76d65 ... 879 */ 880 sscanf(line, "%255s %255s %255s\n", device, mount_path, rest); 881 882 if (!strncmp(mount_path, loopDir, loopDirLen)) { 883 int fd = open(device, O_RDONLY); 884 if (fd >= 0) { 885 struct loop_info64 li; 886 if (ioctl(fd, LOOP_GET_STATUS64, &li) >= 0) { 887 cli->sendMsg(ResponseCode::AsecListResult, 888 (const char*) li.lo_file_name, false); 889 } 890 close(fd); 891 } 892 } 893 } 894 895 fclose(fp); 896 return 0; 897 } 898 899 int VolumeManager::shareEnabled(const char *label, const char *method, bool *enabled) { 900 Volume *v = lookupVolume(label); 901 902 if (!v) { 903 errno = ENOENT; 904 return -1; 905 } 906 907 if (strcmp(method, "ums")) { 908 errno = ENOSYS; 909 return -1; 910 } 911 912 if (v->getState() != Volume::State_Shared) { 913 *enabled = false; 914 } else { 915 *enabled = true; 916 } 917 return 0; 918 } 919 920 int VolumeManager::shareVolume(const char *label, const char *method) { 921 Volume *v = lookupVolume(label); 922 923 if (!v) { 924 errno = ENOENT; 925 return -1; 926 } 927 928 /* 929 * Eventually, we'll want to support additional share back-ends, 930 * some of which may work while the media is mounted. For now, 931 * we just support UMS 932 */ 933 if (strcmp(method, "ums")) { 934 errno = ENOSYS; 935 return -1; 936 } 937 938 if (v->getState() == Volume::State_NoMedia) { 939 errno = ENODEV; 940 return -1; 941 } 942 943 if (v->getState() != Volume::State_Idle) { 944 // You need to unmount manually befoe sharing 945 errno = EBUSY; 946 return -1; 947 } 948 949 if (mVolManagerDisabled) { 950 errno = EBUSY; 951 return -1; 952 } 953 954 dev_t d = v->getShareDevice(); 955 if ((MAJOR(d) == 0) && (MINOR(d) == 0)) { 956 // This volume does not support raw disk access 957 errno = EINVAL; 958 return -1; 959 } 960 961 int fd; 962 char nodepath[255]; 963 snprintf(nodepath, 964 sizeof(nodepath), "/dev/block/vold/%d:%d", 965 MAJOR(d), MINOR(d)); 966 967 if ((fd = open(MASS_STORAGE_FILE_PATH, O_WRONLY)) < 0) { 968 SLOGE("Unable to open ums lunfile (%s)", strerror(errno)); 969 return -1; 970 } 971 972 if (write(fd, nodepath, strlen(nodepath)) < 0) { 973 SLOGE("Unable to write to ums lunfile (%s)", strerror(errno)); 974 close(fd); 975 return -1; 976 } 977 978 close(fd); 979 v->handleVolumeShared(); 980 if (mUmsSharingCount++ == 0) { 981 FILE* fp; 982 mSavedDirtyRatio = -1; // in case we fail 983 if ((fp = fopen("/proc/sys/vm/dirty_ratio", "r+"))) { 984 char line[16]; 985 if (fgets(line, sizeof(line), fp) && sscanf(line, "%d", &mSavedDirtyRatio)) { 986 fprintf(fp, "%d\n", mUmsDirtyRatio); 987 } else { 988 SLOGE("Failed to read dirty_ratio (%s)", strerror(errno)); 989 } 990 fclose(fp); 991 } else { 992 SLOGE("Failed to open /proc/sys/vm/dirty_ratio (%s)", strerror(errno)); 993 } 994 } 995 return 0; 996 } 997 998 int VolumeManager::unshareVolume(const char *label, const char *method) { 999 Volume *v = lookupVolume(label); 1000 1001 if (!v) { 1002 errno = ENOENT; 1003 return -1; 1004 } 1005 1006 if (strcmp(method, "ums")) { 1007 errno = ENOSYS; 1008 return -1; 1009 } 1010 1011 if (v->getState() != Volume::State_Shared) { 1012 errno = EINVAL; 1013 return -1; 1014 } 1015 1016 int fd; 1017 if ((fd = open(MASS_STORAGE_FILE_PATH, O_WRONLY)) < 0) { 1018 SLOGE("Unable to open ums lunfile (%s)", strerror(errno)); 1019 return -1; 1020 } 1021 1022 char ch = 0; 1023 if (write(fd, &ch, 1) < 0) { 1024 SLOGE("Unable to write to ums lunfile (%s)", strerror(errno)); 1025 close(fd); 1026 return -1; 1027 } 1028 1029 close(fd); 1030 v->handleVolumeUnshared(); 1031 if (--mUmsSharingCount == 0 && mSavedDirtyRatio != -1) { 1032 FILE* fp; 1033 if ((fp = fopen("/proc/sys/vm/dirty_ratio", "r+"))) { 1034 fprintf(fp, "%d\n", mSavedDirtyRatio); 1035 fclose(fp); 1036 } else { 1037 SLOGE("Failed to open /proc/sys/vm/dirty_ratio (%s)", strerror(errno)); 1038 } 1039 mSavedDirtyRatio = -1; 1040 } 1041 return 0; 1042 } 1043 1044 extern "C" int vold_disableVol(const char *label) { 1045 VolumeManager *vm = VolumeManager::Instance(); 1046 vm->disableVolumeManager(); 1047 vm->unshareVolume(label, "ums"); 1048 return vm->unmountVolume(label, true, false); 1049 } 1050 1051 extern "C" int vold_getNumDirectVolumes(void) { 1052 VolumeManager *vm = VolumeManager::Instance(); 1053 return vm->getNumDirectVolumes(); 1054 } 1055 1056 int VolumeManager::getNumDirectVolumes(void) { 1057 VolumeCollection::iterator i; 1058 int n=0; 1059 1060 for (i = mVolumes->begin(); i != mVolumes->end(); ++i) { 1061 if ((*i)->getShareDevice() != (dev_t)0) { 1062 n++; 1063 } 1064 } 1065 return n; 1066 } 1067 1068 extern "C" int vold_getDirectVolumeList(struct volume_info *vol_list) { 1069 VolumeManager *vm = VolumeManager::Instance(); 1070 return vm->getDirectVolumeList(vol_list); 1071 } 1072 1073 int VolumeManager::getDirectVolumeList(struct volume_info *vol_list) { 1074 VolumeCollection::iterator i; 1075 int n=0; 1076 dev_t d; 1077 1078 for (i = mVolumes->begin(); i != mVolumes->end(); ++i) { 1079 if ((d=(*i)->getShareDevice()) != (dev_t)0) { 1080 (*i)->getVolInfo(&vol_list[n]); 1081 snprintf(vol_list[n].blk_dev, sizeof(vol_list[n].blk_dev), 1082 "/dev/block/vold/%d:%d",MAJOR(d), MINOR(d)); 1083 n++; 1084 } 1085 } 1086 1087 return 0; 1088 } 1089 1090 int VolumeManager::unmountVolume(const char *label, bool force, bool revert) { 1091 Volume *v = lookupVolume(label); 1092 1093 if (!v) { 1094 errno = ENOENT; 1095 return -1; 1096 } 1097 1098 if (v->getState() == Volume::State_NoMedia) { 1099 errno = ENODEV; 1100 return -1; 1101 } 1102 1103 if (v->getState() != Volume::State_Mounted) { 1104 SLOGW("Attempt to unmount volume which isn't mounted (%d)\n", 1105 v->getState()); 1106 errno = EBUSY; 1107 return UNMOUNT_NOT_MOUNTED_ERR; 1108 } 1109 1110 cleanupAsec(v, force); 1111 1112 return v->unmountVol(force, revert); 1113 } 1114 1115 /* 1116 * Looks up a volume by it's label or mount-point 1117 */ 1118 Volume *VolumeManager::lookupVolume(const char *label) { 1119 VolumeCollection::iterator i; 1120 1121 for (i = mVolumes->begin(); i != mVolumes->end(); ++i) { 1122 if (label[0] == '/') { 1123 if (!strcmp(label, (*i)->getMountpoint())) 1124 return (*i); 1125 } else { 1126 if (!strcmp(label, (*i)->getLabel())) 1127 return (*i); 1128 } 1129 } 1130 return NULL; 1131 } 1132 1133 bool VolumeManager::isMountpointMounted(const char *mp) 1134 { 1135 char device[256]; 1136 char mount_path[256]; 1137 char rest[256]; 1138 FILE *fp; 1139 char line[1024]; 1140 1141 if (!(fp = fopen("/proc/mounts", "r"))) { 1142 SLOGE("Error opening /proc/mounts (%s)", strerror(errno)); 1143 return false; 1144 } 1145 1146 while(fgets(line, sizeof(line), fp)) { 1147 line[strlen(line)-1] = '\0'; 1148 sscanf(line, "%255s %255s %255s\n", device, mount_path, rest); 1149 if (!strcmp(mount_path, mp)) { 1150 fclose(fp); 1151 return true; 1152 } 1153 } 1154 1155 fclose(fp); 1156 return false; 1157 } 1158 1159 int VolumeManager::cleanupAsec(Volume *v, bool force) { 1160 while(mActiveContainers->size()) { 1161 AsecIdCollection::iterator it = mActiveContainers->begin(); 1162 ContainerData* cd = *it; 1163 SLOGI("Unmounting ASEC %s (dependant on %s)", cd->id, v->getMountpoint()); 1164 if (cd->type == ASEC) { 1165 if (unmountAsec(cd->id, force)) { 1166 SLOGE("Failed to unmount ASEC %s (%s)", cd->id, strerror(errno)); 1167 return -1; 1168 } 1169 } else if (cd->type == OBB) { 1170 if (unmountObb(cd->id, force)) { 1171 SLOGE("Failed to unmount OBB %s (%s)", cd->id, strerror(errno)); 1172 return -1; 1173 } 1174 } else { 1175 SLOGE("Unknown container type %d!", cd->type); 1176 return -1; 1177 } 1178 } 1179 return 0; 1180 } 1181 1182