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 <fts.h> 23 #include <unistd.h> 24 #include <sys/stat.h> 25 #include <sys/types.h> 26 #include <sys/mount.h> 27 #include <dirent.h> 28 29 #include <linux/kdev_t.h> 30 31 #define LOG_TAG "Vold" 32 33 #include <openssl/md5.h> 34 35 #include <cutils/log.h> 36 37 #include <sysutils/NetlinkEvent.h> 38 39 #include <private/android_filesystem_config.h> 40 41 #include "VolumeManager.h" 42 #include "DirectVolume.h" 43 #include "ResponseCode.h" 44 #include "Loop.h" 45 #include "Ext4.h" 46 #include "Fat.h" 47 #include "Devmapper.h" 48 #include "Process.h" 49 #include "Asec.h" 50 #include "cryptfs.h" 51 52 #define MASS_STORAGE_FILE_PATH "/sys/class/android_usb/android0/f_mass_storage/lun/file" 53 54 VolumeManager *VolumeManager::sInstance = NULL; 55 56 VolumeManager *VolumeManager::Instance() { 57 if (!sInstance) 58 sInstance = new VolumeManager(); 59 return sInstance; 60 } 61 62 VolumeManager::VolumeManager() { 63 mDebug = false; 64 mVolumes = new VolumeCollection(); 65 mActiveContainers = new AsecIdCollection(); 66 mBroadcaster = NULL; 67 mUmsSharingCount = 0; 68 mSavedDirtyRatio = -1; 69 // set dirty ratio to 0 when UMS is active 70 mUmsDirtyRatio = 0; 71 mVolManagerDisabled = 0; 72 } 73 74 VolumeManager::~VolumeManager() { 75 delete mVolumes; 76 delete mActiveContainers; 77 } 78 79 char *VolumeManager::asecHash(const char *id, char *buffer, size_t len) { 80 static const char* digits = "0123456789abcdef"; 81 82 unsigned char sig[MD5_DIGEST_LENGTH]; 83 84 if (buffer == NULL) { 85 SLOGE("Destination buffer is NULL"); 86 errno = ESPIPE; 87 return NULL; 88 } else if (id == NULL) { 89 SLOGE("Source buffer is NULL"); 90 errno = ESPIPE; 91 return NULL; 92 } else if (len < MD5_ASCII_LENGTH_PLUS_NULL) { 93 SLOGE("Target hash buffer size < %d bytes (%d)", 94 MD5_ASCII_LENGTH_PLUS_NULL, len); 95 errno = ESPIPE; 96 return NULL; 97 } 98 99 MD5(reinterpret_cast<const unsigned char*>(id), strlen(id), sig); 100 101 char *p = buffer; 102 for (int i = 0; i < MD5_DIGEST_LENGTH; i++) { 103 *p++ = digits[sig[i] >> 4]; 104 *p++ = digits[sig[i] & 0x0F]; 105 } 106 *p = '\0'; 107 108 return buffer; 109 } 110 111 void VolumeManager::setDebug(bool enable) { 112 mDebug = enable; 113 VolumeCollection::iterator it; 114 for (it = mVolumes->begin(); it != mVolumes->end(); ++it) { 115 (*it)->setDebug(enable); 116 } 117 } 118 119 int VolumeManager::start() { 120 return 0; 121 } 122 123 int VolumeManager::stop() { 124 return 0; 125 } 126 127 int VolumeManager::addVolume(Volume *v) { 128 mVolumes->push_back(v); 129 return 0; 130 } 131 132 void VolumeManager::handleBlockEvent(NetlinkEvent *evt) { 133 const char *devpath = evt->findParam("DEVPATH"); 134 135 /* Lookup a volume to handle this device */ 136 VolumeCollection::iterator it; 137 bool hit = false; 138 for (it = mVolumes->begin(); it != mVolumes->end(); ++it) { 139 if (!(*it)->handleBlockEvent(evt)) { 140 #ifdef NETLINK_DEBUG 141 SLOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel()); 142 #endif 143 hit = true; 144 break; 145 } 146 } 147 148 if (!hit) { 149 #ifdef NETLINK_DEBUG 150 SLOGW("No volumes handled block event for '%s'", devpath); 151 #endif 152 } 153 } 154 155 int VolumeManager::listVolumes(SocketClient *cli) { 156 VolumeCollection::iterator i; 157 158 for (i = mVolumes->begin(); i != mVolumes->end(); ++i) { 159 char *buffer; 160 asprintf(&buffer, "%s %s %d", 161 (*i)->getLabel(), (*i)->getMountpoint(), 162 (*i)->getState()); 163 cli->sendMsg(ResponseCode::VolumeListResult, buffer, false); 164 free(buffer); 165 } 166 cli->sendMsg(ResponseCode::CommandOkay, "Volumes listed.", false); 167 return 0; 168 } 169 170 int VolumeManager::formatVolume(const char *label) { 171 Volume *v = lookupVolume(label); 172 173 if (!v) { 174 errno = ENOENT; 175 return -1; 176 } 177 178 if (mVolManagerDisabled) { 179 errno = EBUSY; 180 return -1; 181 } 182 183 return v->formatVol(); 184 } 185 186 int VolumeManager::getObbMountPath(const char *sourceFile, char *mountPath, int mountPathLen) { 187 char idHash[33]; 188 if (!asecHash(sourceFile, idHash, sizeof(idHash))) { 189 SLOGE("Hash of '%s' failed (%s)", sourceFile, strerror(errno)); 190 return -1; 191 } 192 193 memset(mountPath, 0, mountPathLen); 194 int written = snprintf(mountPath, mountPathLen, "%s/%s", Volume::LOOPDIR, idHash); 195 if ((written < 0) || (written >= mountPathLen)) { 196 errno = EINVAL; 197 return -1; 198 } 199 200 if (access(mountPath, F_OK)) { 201 errno = ENOENT; 202 return -1; 203 } 204 205 return 0; 206 } 207 208 int VolumeManager::getAsecMountPath(const char *id, char *buffer, int maxlen) { 209 char asecFileName[255]; 210 211 if (findAsec(id, asecFileName, sizeof(asecFileName))) { 212 SLOGE("Couldn't find ASEC %s", id); 213 return -1; 214 } 215 216 memset(buffer, 0, maxlen); 217 if (access(asecFileName, F_OK)) { 218 errno = ENOENT; 219 return -1; 220 } 221 222 int written = snprintf(buffer, maxlen, "%s/%s", Volume::ASECDIR, id); 223 if ((written < 0) || (written >= maxlen)) { 224 SLOGE("getAsecMountPath failed for %s: couldn't construct path in buffer", id); 225 errno = EINVAL; 226 return -1; 227 } 228 229 return 0; 230 } 231 232 int VolumeManager::getAsecFilesystemPath(const char *id, char *buffer, int maxlen) { 233 char asecFileName[255]; 234 235 if (findAsec(id, asecFileName, sizeof(asecFileName))) { 236 SLOGE("Couldn't find ASEC %s", id); 237 return -1; 238 } 239 240 memset(buffer, 0, maxlen); 241 if (access(asecFileName, F_OK)) { 242 errno = ENOENT; 243 return -1; 244 } 245 246 int written = snprintf(buffer, maxlen, "%s", asecFileName); 247 if ((written < 0) || (written >= maxlen)) { 248 errno = EINVAL; 249 return -1; 250 } 251 252 return 0; 253 } 254 255 int VolumeManager::createAsec(const char *id, unsigned int numSectors, const char *fstype, 256 const char *key, const int ownerUid, bool isExternal) { 257 struct asec_superblock sb; 258 memset(&sb, 0, sizeof(sb)); 259 260 const bool wantFilesystem = strcmp(fstype, "none"); 261 bool usingExt4 = false; 262 if (wantFilesystem) { 263 usingExt4 = !strcmp(fstype, "ext4"); 264 if (usingExt4) { 265 sb.c_opts |= ASEC_SB_C_OPTS_EXT4; 266 } else if (strcmp(fstype, "fat")) { 267 SLOGE("Invalid filesystem type %s", fstype); 268 errno = EINVAL; 269 return -1; 270 } 271 } 272 273 sb.magic = ASEC_SB_MAGIC; 274 sb.ver = ASEC_SB_VER; 275 276 if (numSectors < ((1024*1024)/512)) { 277 SLOGE("Invalid container size specified (%d sectors)", numSectors); 278 errno = EINVAL; 279 return -1; 280 } 281 282 if (lookupVolume(id)) { 283 SLOGE("ASEC id '%s' currently exists", id); 284 errno = EADDRINUSE; 285 return -1; 286 } 287 288 char asecFileName[255]; 289 290 if (!findAsec(id, asecFileName, sizeof(asecFileName))) { 291 SLOGE("ASEC file '%s' currently exists - destroy it first! (%s)", 292 asecFileName, strerror(errno)); 293 errno = EADDRINUSE; 294 return -1; 295 } 296 297 const char *asecDir = isExternal ? Volume::SEC_ASECDIR_EXT : Volume::SEC_ASECDIR_INT; 298 299 int written = snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", asecDir, id); 300 if ((written < 0) || (size_t(written) >= sizeof(asecFileName))) { 301 errno = EINVAL; 302 return -1; 303 } 304 305 if (!access(asecFileName, F_OK)) { 306 SLOGE("ASEC file '%s' currently exists - destroy it first! (%s)", 307 asecFileName, strerror(errno)); 308 errno = EADDRINUSE; 309 return -1; 310 } 311 312 /* 313 * Add some headroom 314 */ 315 unsigned fatSize = (((numSectors * 4) / 512) + 1) * 2; 316 unsigned numImgSectors = numSectors + fatSize + 2; 317 318 if (numImgSectors % 63) { 319 numImgSectors += (63 - (numImgSectors % 63)); 320 } 321 322 // Add +1 for our superblock which is at the end 323 if (Loop::createImageFile(asecFileName, numImgSectors + 1)) { 324 SLOGE("ASEC image file creation failed (%s)", strerror(errno)); 325 return -1; 326 } 327 328 char idHash[33]; 329 if (!asecHash(id, idHash, sizeof(idHash))) { 330 SLOGE("Hash of '%s' failed (%s)", id, strerror(errno)); 331 unlink(asecFileName); 332 return -1; 333 } 334 335 char loopDevice[255]; 336 if (Loop::create(idHash, asecFileName, loopDevice, sizeof(loopDevice))) { 337 SLOGE("ASEC loop device creation failed (%s)", strerror(errno)); 338 unlink(asecFileName); 339 return -1; 340 } 341 342 char dmDevice[255]; 343 bool cleanupDm = false; 344 345 if (strcmp(key, "none")) { 346 // XXX: This is all we support for now 347 sb.c_cipher = ASEC_SB_C_CIPHER_TWOFISH; 348 if (Devmapper::create(idHash, loopDevice, key, numImgSectors, dmDevice, 349 sizeof(dmDevice))) { 350 SLOGE("ASEC device mapping failed (%s)", strerror(errno)); 351 Loop::destroyByDevice(loopDevice); 352 unlink(asecFileName); 353 return -1; 354 } 355 cleanupDm = true; 356 } else { 357 sb.c_cipher = ASEC_SB_C_CIPHER_NONE; 358 strcpy(dmDevice, loopDevice); 359 } 360 361 /* 362 * Drop down the superblock at the end of the file 363 */ 364 365 int sbfd = open(loopDevice, O_RDWR); 366 if (sbfd < 0) { 367 SLOGE("Failed to open new DM device for superblock write (%s)", strerror(errno)); 368 if (cleanupDm) { 369 Devmapper::destroy(idHash); 370 } 371 Loop::destroyByDevice(loopDevice); 372 unlink(asecFileName); 373 return -1; 374 } 375 376 if (lseek(sbfd, (numImgSectors * 512), SEEK_SET) < 0) { 377 close(sbfd); 378 SLOGE("Failed to lseek for superblock (%s)", strerror(errno)); 379 if (cleanupDm) { 380 Devmapper::destroy(idHash); 381 } 382 Loop::destroyByDevice(loopDevice); 383 unlink(asecFileName); 384 return -1; 385 } 386 387 if (write(sbfd, &sb, sizeof(sb)) != sizeof(sb)) { 388 close(sbfd); 389 SLOGE("Failed to write superblock (%s)", strerror(errno)); 390 if (cleanupDm) { 391 Devmapper::destroy(idHash); 392 } 393 Loop::destroyByDevice(loopDevice); 394 unlink(asecFileName); 395 return -1; 396 } 397 close(sbfd); 398 399 if (wantFilesystem) { 400 int formatStatus; 401 char mountPoint[255]; 402 403 int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id); 404 if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) { 405 SLOGE("ASEC fs format failed: couldn't construct mountPoint"); 406 if (cleanupDm) { 407 Devmapper::destroy(idHash); 408 } 409 Loop::destroyByDevice(loopDevice); 410 unlink(asecFileName); 411 return -1; 412 } 413 414 if (usingExt4) { 415 formatStatus = Ext4::format(dmDevice, mountPoint); 416 } else { 417 formatStatus = Fat::format(dmDevice, numImgSectors); 418 } 419 420 if (formatStatus < 0) { 421 SLOGE("ASEC fs format failed (%s)", strerror(errno)); 422 if (cleanupDm) { 423 Devmapper::destroy(idHash); 424 } 425 Loop::destroyByDevice(loopDevice); 426 unlink(asecFileName); 427 return -1; 428 } 429 430 if (mkdir(mountPoint, 0000)) { 431 if (errno != EEXIST) { 432 SLOGE("Mountpoint creation failed (%s)", strerror(errno)); 433 if (cleanupDm) { 434 Devmapper::destroy(idHash); 435 } 436 Loop::destroyByDevice(loopDevice); 437 unlink(asecFileName); 438 return -1; 439 } 440 } 441 442 int mountStatus; 443 if (usingExt4) { 444 mountStatus = Ext4::doMount(dmDevice, mountPoint, false, false, false); 445 } else { 446 mountStatus = Fat::doMount(dmDevice, mountPoint, false, false, false, ownerUid, 0, 0000, 447 false); 448 } 449 450 if (mountStatus) { 451 SLOGE("ASEC FAT mount failed (%s)", strerror(errno)); 452 if (cleanupDm) { 453 Devmapper::destroy(idHash); 454 } 455 Loop::destroyByDevice(loopDevice); 456 unlink(asecFileName); 457 return -1; 458 } 459 460 if (usingExt4) { 461 int dirfd = open(mountPoint, O_DIRECTORY); 462 if (dirfd >= 0) { 463 if (fchown(dirfd, ownerUid, AID_SYSTEM) 464 || fchmod(dirfd, S_IRUSR | S_IWUSR | S_IXUSR | S_ISGID | S_IRGRP | S_IXGRP)) { 465 SLOGI("Cannot chown/chmod new ASEC mount point %s", mountPoint); 466 } 467 close(dirfd); 468 } 469 } 470 } else { 471 SLOGI("Created raw secure container %s (no filesystem)", id); 472 } 473 474 mActiveContainers->push_back(new ContainerData(strdup(id), ASEC)); 475 return 0; 476 } 477 478 int VolumeManager::finalizeAsec(const char *id) { 479 char asecFileName[255]; 480 char loopDevice[255]; 481 char mountPoint[255]; 482 483 if (findAsec(id, asecFileName, sizeof(asecFileName))) { 484 SLOGE("Couldn't find ASEC %s", id); 485 return -1; 486 } 487 488 char idHash[33]; 489 if (!asecHash(id, idHash, sizeof(idHash))) { 490 SLOGE("Hash of '%s' failed (%s)", id, strerror(errno)); 491 return -1; 492 } 493 494 if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) { 495 SLOGE("Unable to finalize %s (%s)", id, strerror(errno)); 496 return -1; 497 } 498 499 unsigned int nr_sec = 0; 500 struct asec_superblock sb; 501 502 if (Loop::lookupInfo(loopDevice, &sb, &nr_sec)) { 503 return -1; 504 } 505 506 int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id); 507 if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) { 508 SLOGE("ASEC finalize failed: couldn't construct mountPoint"); 509 return -1; 510 } 511 512 int result = 0; 513 if (sb.c_opts & ASEC_SB_C_OPTS_EXT4) { 514 result = Ext4::doMount(loopDevice, mountPoint, true, true, true); 515 } else { 516 result = Fat::doMount(loopDevice, mountPoint, true, true, true, 0, 0, 0227, false); 517 } 518 519 if (result) { 520 SLOGE("ASEC finalize mount failed (%s)", strerror(errno)); 521 return -1; 522 } 523 524 if (mDebug) { 525 SLOGD("ASEC %s finalized", id); 526 } 527 return 0; 528 } 529 530 int VolumeManager::fixupAsecPermissions(const char *id, gid_t gid, const char* filename) { 531 char asecFileName[255]; 532 char loopDevice[255]; 533 char mountPoint[255]; 534 535 if (gid < AID_APP) { 536 SLOGE("Group ID is not in application range"); 537 return -1; 538 } 539 540 if (findAsec(id, asecFileName, sizeof(asecFileName))) { 541 SLOGE("Couldn't find ASEC %s", id); 542 return -1; 543 } 544 545 char idHash[33]; 546 if (!asecHash(id, idHash, sizeof(idHash))) { 547 SLOGE("Hash of '%s' failed (%s)", id, strerror(errno)); 548 return -1; 549 } 550 551 if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) { 552 SLOGE("Unable fix permissions during lookup on %s (%s)", id, strerror(errno)); 553 return -1; 554 } 555 556 unsigned int nr_sec = 0; 557 struct asec_superblock sb; 558 559 if (Loop::lookupInfo(loopDevice, &sb, &nr_sec)) { 560 return -1; 561 } 562 563 int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id); 564 if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) { 565 SLOGE("Unable remount to fix permissions for %s: couldn't construct mountpoint", id); 566 return -1; 567 } 568 569 int result = 0; 570 if ((sb.c_opts & ASEC_SB_C_OPTS_EXT4) == 0) { 571 return 0; 572 } 573 574 int ret = Ext4::doMount(loopDevice, mountPoint, 575 false /* read-only */, 576 true /* remount */, 577 false /* executable */); 578 if (ret) { 579 SLOGE("Unable remount to fix permissions for %s (%s)", id, strerror(errno)); 580 return -1; 581 } 582 583 char *paths[] = { mountPoint, NULL }; 584 585 FTS *fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL); 586 if (fts) { 587 // Traverse the entire hierarchy and chown to system UID. 588 for (FTSENT *ftsent = fts_read(fts); ftsent != NULL; ftsent = fts_read(fts)) { 589 // We don't care about the lost+found directory. 590 if (!strcmp(ftsent->fts_name, "lost+found")) { 591 continue; 592 } 593 594 /* 595 * There can only be one file marked as private right now. 596 * This should be more robust, but it satisfies the requirements 597 * we have for right now. 598 */ 599 const bool privateFile = !strcmp(ftsent->fts_name, filename); 600 601 int fd = open(ftsent->fts_accpath, O_NOFOLLOW); 602 if (fd < 0) { 603 SLOGE("Couldn't open file %s: %s", ftsent->fts_accpath, strerror(errno)); 604 result = -1; 605 continue; 606 } 607 608 result |= fchown(fd, AID_SYSTEM, privateFile? gid : AID_SYSTEM); 609 610 if (ftsent->fts_info & FTS_D) { 611 result |= fchmod(fd, 0755); 612 } else if (ftsent->fts_info & FTS_F) { 613 result |= fchmod(fd, privateFile ? 0640 : 0644); 614 } 615 close(fd); 616 } 617 fts_close(fts); 618 619 // Finally make the directory readable by everyone. 620 int dirfd = open(mountPoint, O_DIRECTORY); 621 if (dirfd < 0 || fchmod(dirfd, 0755)) { 622 SLOGE("Couldn't change owner of existing directory %s: %s", mountPoint, strerror(errno)); 623 result |= -1; 624 } 625 close(dirfd); 626 } else { 627 result |= -1; 628 } 629 630 result |= Ext4::doMount(loopDevice, mountPoint, 631 true /* read-only */, 632 true /* remount */, 633 true /* execute */); 634 635 if (result) { 636 SLOGE("ASEC fix permissions failed (%s)", strerror(errno)); 637 return -1; 638 } 639 640 if (mDebug) { 641 SLOGD("ASEC %s permissions fixed", id); 642 } 643 return 0; 644 } 645 646 int VolumeManager::renameAsec(const char *id1, const char *id2) { 647 char asecFilename1[255]; 648 char *asecFilename2; 649 char mountPoint[255]; 650 651 const char *dir; 652 653 if (findAsec(id1, asecFilename1, sizeof(asecFilename1), &dir)) { 654 SLOGE("Couldn't find ASEC %s", id1); 655 return -1; 656 } 657 658 asprintf(&asecFilename2, "%s/%s.asec", dir, id2); 659 660 int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id1); 661 if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) { 662 SLOGE("Rename failed: couldn't construct mountpoint"); 663 goto out_err; 664 } 665 666 if (isMountpointMounted(mountPoint)) { 667 SLOGW("Rename attempt when src mounted"); 668 errno = EBUSY; 669 goto out_err; 670 } 671 672 written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id2); 673 if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) { 674 SLOGE("Rename failed: couldn't construct mountpoint2"); 675 goto out_err; 676 } 677 678 if (isMountpointMounted(mountPoint)) { 679 SLOGW("Rename attempt when dst mounted"); 680 errno = EBUSY; 681 goto out_err; 682 } 683 684 if (!access(asecFilename2, F_OK)) { 685 SLOGE("Rename attempt when dst exists"); 686 errno = EADDRINUSE; 687 goto out_err; 688 } 689 690 if (rename(asecFilename1, asecFilename2)) { 691 SLOGE("Rename of '%s' to '%s' failed (%s)", asecFilename1, asecFilename2, strerror(errno)); 692 goto out_err; 693 } 694 695 free(asecFilename2); 696 return 0; 697 698 out_err: 699 free(asecFilename2); 700 return -1; 701 } 702 703 #define UNMOUNT_RETRIES 5 704 #define UNMOUNT_SLEEP_BETWEEN_RETRY_MS (1000 * 1000) 705 int VolumeManager::unmountAsec(const char *id, bool force) { 706 char asecFileName[255]; 707 char mountPoint[255]; 708 709 if (findAsec(id, asecFileName, sizeof(asecFileName))) { 710 SLOGE("Couldn't find ASEC %s", id); 711 return -1; 712 } 713 714 int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id); 715 if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) { 716 SLOGE("ASEC unmount failed for %s: couldn't construct mountpoint", id); 717 return -1; 718 } 719 720 char idHash[33]; 721 if (!asecHash(id, idHash, sizeof(idHash))) { 722 SLOGE("Hash of '%s' failed (%s)", id, strerror(errno)); 723 return -1; 724 } 725 726 return unmountLoopImage(id, idHash, asecFileName, mountPoint, force); 727 } 728 729 int VolumeManager::unmountObb(const char *fileName, bool force) { 730 char mountPoint[255]; 731 732 char idHash[33]; 733 if (!asecHash(fileName, idHash, sizeof(idHash))) { 734 SLOGE("Hash of '%s' failed (%s)", fileName, strerror(errno)); 735 return -1; 736 } 737 738 int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::LOOPDIR, idHash); 739 if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) { 740 SLOGE("OBB unmount failed for %s: couldn't construct mountpoint", fileName); 741 return -1; 742 } 743 744 return unmountLoopImage(fileName, idHash, fileName, mountPoint, force); 745 } 746 747 int VolumeManager::unmountLoopImage(const char *id, const char *idHash, 748 const char *fileName, const char *mountPoint, bool force) { 749 if (!isMountpointMounted(mountPoint)) { 750 SLOGE("Unmount request for %s when not mounted", id); 751 errno = ENOENT; 752 return -1; 753 } 754 755 int i, rc; 756 for (i = 1; i <= UNMOUNT_RETRIES; i++) { 757 rc = umount(mountPoint); 758 if (!rc) { 759 break; 760 } 761 if (rc && (errno == EINVAL || errno == ENOENT)) { 762 SLOGI("Container %s unmounted OK", id); 763 rc = 0; 764 break; 765 } 766 SLOGW("%s unmount attempt %d failed (%s)", 767 id, i, strerror(errno)); 768 769 int action = 0; // default is to just complain 770 771 if (force) { 772 if (i > (UNMOUNT_RETRIES - 2)) 773 action = 2; // SIGKILL 774 else if (i > (UNMOUNT_RETRIES - 3)) 775 action = 1; // SIGHUP 776 } 777 778 Process::killProcessesWithOpenFiles(mountPoint, action); 779 usleep(UNMOUNT_SLEEP_BETWEEN_RETRY_MS); 780 } 781 782 if (rc) { 783 errno = EBUSY; 784 SLOGE("Failed to unmount container %s (%s)", id, strerror(errno)); 785 return -1; 786 } 787 788 int retries = 10; 789 790 while(retries--) { 791 if (!rmdir(mountPoint)) { 792 break; 793 } 794 795 SLOGW("Failed to rmdir %s (%s)", mountPoint, strerror(errno)); 796 usleep(UNMOUNT_SLEEP_BETWEEN_RETRY_MS); 797 } 798 799 if (!retries) { 800 SLOGE("Timed out trying to rmdir %s (%s)", mountPoint, strerror(errno)); 801 } 802 803 if (Devmapper::destroy(idHash) && errno != ENXIO) { 804 SLOGE("Failed to destroy devmapper instance (%s)", strerror(errno)); 805 } 806 807 char loopDevice[255]; 808 if (!Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) { 809 Loop::destroyByDevice(loopDevice); 810 } else { 811 SLOGW("Failed to find loop device for {%s} (%s)", fileName, strerror(errno)); 812 } 813 814 AsecIdCollection::iterator it; 815 for (it = mActiveContainers->begin(); it != mActiveContainers->end(); ++it) { 816 ContainerData* cd = *it; 817 if (!strcmp(cd->id, id)) { 818 free(*it); 819 mActiveContainers->erase(it); 820 break; 821 } 822 } 823 if (it == mActiveContainers->end()) { 824 SLOGW("mActiveContainers is inconsistent!"); 825 } 826 return 0; 827 } 828 829 int VolumeManager::destroyAsec(const char *id, bool force) { 830 char asecFileName[255]; 831 char mountPoint[255]; 832 833 if (findAsec(id, asecFileName, sizeof(asecFileName))) { 834 SLOGE("Couldn't find ASEC %s", id); 835 return -1; 836 } 837 838 int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id); 839 if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) { 840 SLOGE("ASEC destroy failed for %s: couldn't construct mountpoint", id); 841 return -1; 842 } 843 844 if (isMountpointMounted(mountPoint)) { 845 if (mDebug) { 846 SLOGD("Unmounting container before destroy"); 847 } 848 if (unmountAsec(id, force)) { 849 SLOGE("Failed to unmount asec %s for destroy (%s)", id, strerror(errno)); 850 return -1; 851 } 852 } 853 854 if (unlink(asecFileName)) { 855 SLOGE("Failed to unlink asec '%s' (%s)", asecFileName, strerror(errno)); 856 return -1; 857 } 858 859 if (mDebug) { 860 SLOGD("ASEC %s destroyed", id); 861 } 862 return 0; 863 } 864 865 bool VolumeManager::isAsecInDirectory(const char *dir, const char *asecName) const { 866 int dirfd = open(dir, O_DIRECTORY); 867 if (dirfd < 0) { 868 SLOGE("Couldn't open internal ASEC dir (%s)", strerror(errno)); 869 return -1; 870 } 871 872 bool ret = false; 873 874 if (!faccessat(dirfd, asecName, F_OK, AT_SYMLINK_NOFOLLOW)) { 875 ret = true; 876 } 877 878 close(dirfd); 879 880 return ret; 881 } 882 883 int VolumeManager::findAsec(const char *id, char *asecPath, size_t asecPathLen, 884 const char **directory) const { 885 int dirfd, fd; 886 const int idLen = strlen(id); 887 char *asecName; 888 889 if (asprintf(&asecName, "%s.asec", id) < 0) { 890 SLOGE("Couldn't allocate string to write ASEC name"); 891 return -1; 892 } 893 894 const char *dir; 895 if (isAsecInDirectory(Volume::SEC_ASECDIR_INT, asecName)) { 896 dir = Volume::SEC_ASECDIR_INT; 897 } else if (isAsecInDirectory(Volume::SEC_ASECDIR_EXT, asecName)) { 898 dir = Volume::SEC_ASECDIR_EXT; 899 } else { 900 free(asecName); 901 return -1; 902 } 903 904 if (directory != NULL) { 905 *directory = dir; 906 } 907 908 if (asecPath != NULL) { 909 int written = snprintf(asecPath, asecPathLen, "%s/%s", dir, asecName); 910 if ((written < 0) || (size_t(written) >= asecPathLen)) { 911 SLOGE("findAsec failed for %s: couldn't construct ASEC path", id); 912 free(asecName); 913 return -1; 914 } 915 } 916 917 free(asecName); 918 return 0; 919 } 920 921 int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid) { 922 char asecFileName[255]; 923 char mountPoint[255]; 924 925 if (findAsec(id, asecFileName, sizeof(asecFileName))) { 926 SLOGE("Couldn't find ASEC %s", id); 927 return -1; 928 } 929 930 int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id); 931 if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) { 932 SLOGE("ASEC mount failed: couldn't construct mountpoint", id); 933 return -1; 934 } 935 936 if (isMountpointMounted(mountPoint)) { 937 SLOGE("ASEC %s already mounted", id); 938 errno = EBUSY; 939 return -1; 940 } 941 942 char idHash[33]; 943 if (!asecHash(id, idHash, sizeof(idHash))) { 944 SLOGE("Hash of '%s' failed (%s)", id, strerror(errno)); 945 return -1; 946 } 947 948 char loopDevice[255]; 949 if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) { 950 if (Loop::create(idHash, asecFileName, loopDevice, sizeof(loopDevice))) { 951 SLOGE("ASEC loop device creation failed (%s)", strerror(errno)); 952 return -1; 953 } 954 if (mDebug) { 955 SLOGD("New loop device created at %s", loopDevice); 956 } 957 } else { 958 if (mDebug) { 959 SLOGD("Found active loopback for %s at %s", asecFileName, loopDevice); 960 } 961 } 962 963 char dmDevice[255]; 964 bool cleanupDm = false; 965 int fd; 966 unsigned int nr_sec = 0; 967 struct asec_superblock sb; 968 969 if (Loop::lookupInfo(loopDevice, &sb, &nr_sec)) { 970 return -1; 971 } 972 973 if (mDebug) { 974 SLOGD("Container sb magic/ver (%.8x/%.2x)", sb.magic, sb.ver); 975 } 976 if (sb.magic != ASEC_SB_MAGIC || sb.ver != ASEC_SB_VER) { 977 SLOGE("Bad container magic/version (%.8x/%.2x)", sb.magic, sb.ver); 978 Loop::destroyByDevice(loopDevice); 979 errno = EMEDIUMTYPE; 980 return -1; 981 } 982 nr_sec--; // We don't want the devmapping to extend onto our superblock 983 984 if (strcmp(key, "none")) { 985 if (Devmapper::lookupActive(idHash, dmDevice, sizeof(dmDevice))) { 986 if (Devmapper::create(idHash, loopDevice, key, nr_sec, 987 dmDevice, sizeof(dmDevice))) { 988 SLOGE("ASEC device mapping failed (%s)", strerror(errno)); 989 Loop::destroyByDevice(loopDevice); 990 return -1; 991 } 992 if (mDebug) { 993 SLOGD("New devmapper instance created at %s", dmDevice); 994 } 995 } else { 996 if (mDebug) { 997 SLOGD("Found active devmapper for %s at %s", asecFileName, dmDevice); 998 } 999 } 1000 cleanupDm = true; 1001 } else { 1002 strcpy(dmDevice, loopDevice); 1003 } 1004 1005 if (mkdir(mountPoint, 0000)) { 1006 if (errno != EEXIST) { 1007 SLOGE("Mountpoint creation failed (%s)", strerror(errno)); 1008 if (cleanupDm) { 1009 Devmapper::destroy(idHash); 1010 } 1011 Loop::destroyByDevice(loopDevice); 1012 return -1; 1013 } 1014 } 1015 1016 /* 1017 * The device mapper node needs to be created. Sometimes it takes a 1018 * while. Wait for up to 1 second. We could also inspect incoming uevents, 1019 * but that would take more effort. 1020 */ 1021 int tries = 25; 1022 while (tries--) { 1023 if (!access(dmDevice, F_OK) || errno != ENOENT) { 1024 break; 1025 } 1026 usleep(40 * 1000); 1027 } 1028 1029 int result; 1030 if (sb.c_opts & ASEC_SB_C_OPTS_EXT4) { 1031 result = Ext4::doMount(dmDevice, mountPoint, true, false, true); 1032 } else { 1033 result = Fat::doMount(dmDevice, mountPoint, true, false, true, ownerUid, 0, 0222, false); 1034 } 1035 1036 if (result) { 1037 SLOGE("ASEC mount failed (%s)", strerror(errno)); 1038 if (cleanupDm) { 1039 Devmapper::destroy(idHash); 1040 } 1041 Loop::destroyByDevice(loopDevice); 1042 return -1; 1043 } 1044 1045 mActiveContainers->push_back(new ContainerData(strdup(id), ASEC)); 1046 if (mDebug) { 1047 SLOGD("ASEC %s mounted", id); 1048 } 1049 return 0; 1050 } 1051 1052 Volume* VolumeManager::getVolumeForFile(const char *fileName) { 1053 VolumeCollection::iterator i; 1054 1055 for (i = mVolumes->begin(); i != mVolumes->end(); ++i) { 1056 const char* mountPoint = (*i)->getMountpoint(); 1057 if (!strncmp(fileName, mountPoint, strlen(mountPoint))) { 1058 return *i; 1059 } 1060 } 1061 1062 return NULL; 1063 } 1064 1065 /** 1066 * Mounts an image file <code>img</code>. 1067 */ 1068 int VolumeManager::mountObb(const char *img, const char *key, int ownerGid) { 1069 char mountPoint[255]; 1070 1071 char idHash[33]; 1072 if (!asecHash(img, idHash, sizeof(idHash))) { 1073 SLOGE("Hash of '%s' failed (%s)", img, strerror(errno)); 1074 return -1; 1075 } 1076 1077 int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::LOOPDIR, idHash); 1078 if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) { 1079 SLOGE("OBB mount failed: couldn't construct mountpoint", img); 1080 return -1; 1081 } 1082 1083 if (isMountpointMounted(mountPoint)) { 1084 SLOGE("Image %s already mounted", img); 1085 errno = EBUSY; 1086 return -1; 1087 } 1088 1089 char loopDevice[255]; 1090 if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) { 1091 if (Loop::create(idHash, img, loopDevice, sizeof(loopDevice))) { 1092 SLOGE("Image loop device creation failed (%s)", strerror(errno)); 1093 return -1; 1094 } 1095 if (mDebug) { 1096 SLOGD("New loop device created at %s", loopDevice); 1097 } 1098 } else { 1099 if (mDebug) { 1100 SLOGD("Found active loopback for %s at %s", img, loopDevice); 1101 } 1102 } 1103 1104 char dmDevice[255]; 1105 bool cleanupDm = false; 1106 int fd; 1107 unsigned int nr_sec = 0; 1108 1109 if ((fd = open(loopDevice, O_RDWR)) < 0) { 1110 SLOGE("Failed to open loopdevice (%s)", strerror(errno)); 1111 Loop::destroyByDevice(loopDevice); 1112 return -1; 1113 } 1114 1115 if (ioctl(fd, BLKGETSIZE, &nr_sec)) { 1116 SLOGE("Failed to get loop size (%s)", strerror(errno)); 1117 Loop::destroyByDevice(loopDevice); 1118 close(fd); 1119 return -1; 1120 } 1121 1122 close(fd); 1123 1124 if (strcmp(key, "none")) { 1125 if (Devmapper::lookupActive(idHash, dmDevice, sizeof(dmDevice))) { 1126 if (Devmapper::create(idHash, loopDevice, key, nr_sec, 1127 dmDevice, sizeof(dmDevice))) { 1128 SLOGE("ASEC device mapping failed (%s)", strerror(errno)); 1129 Loop::destroyByDevice(loopDevice); 1130 return -1; 1131 } 1132 if (mDebug) { 1133 SLOGD("New devmapper instance created at %s", dmDevice); 1134 } 1135 } else { 1136 if (mDebug) { 1137 SLOGD("Found active devmapper for %s at %s", img, dmDevice); 1138 } 1139 } 1140 cleanupDm = true; 1141 } else { 1142 strcpy(dmDevice, loopDevice); 1143 } 1144 1145 if (mkdir(mountPoint, 0755)) { 1146 if (errno != EEXIST) { 1147 SLOGE("Mountpoint creation failed (%s)", strerror(errno)); 1148 if (cleanupDm) { 1149 Devmapper::destroy(idHash); 1150 } 1151 Loop::destroyByDevice(loopDevice); 1152 return -1; 1153 } 1154 } 1155 1156 if (Fat::doMount(dmDevice, mountPoint, true, false, true, 0, ownerGid, 1157 0227, false)) { 1158 SLOGE("Image mount failed (%s)", strerror(errno)); 1159 if (cleanupDm) { 1160 Devmapper::destroy(idHash); 1161 } 1162 Loop::destroyByDevice(loopDevice); 1163 return -1; 1164 } 1165 1166 mActiveContainers->push_back(new ContainerData(strdup(img), OBB)); 1167 if (mDebug) { 1168 SLOGD("Image %s mounted", img); 1169 } 1170 return 0; 1171 } 1172 1173 int VolumeManager::mountVolume(const char *label) { 1174 Volume *v = lookupVolume(label); 1175 1176 if (!v) { 1177 errno = ENOENT; 1178 return -1; 1179 } 1180 1181 return v->mountVol(); 1182 } 1183 1184 int VolumeManager::listMountedObbs(SocketClient* cli) { 1185 char device[256]; 1186 char mount_path[256]; 1187 char rest[256]; 1188 FILE *fp; 1189 char line[1024]; 1190 1191 if (!(fp = fopen("/proc/mounts", "r"))) { 1192 SLOGE("Error opening /proc/mounts (%s)", strerror(errno)); 1193 return -1; 1194 } 1195 1196 // Create a string to compare against that has a trailing slash 1197 int loopDirLen = strlen(Volume::LOOPDIR); 1198 char loopDir[loopDirLen + 2]; 1199 strcpy(loopDir, Volume::LOOPDIR); 1200 loopDir[loopDirLen++] = '/'; 1201 loopDir[loopDirLen] = '\0'; 1202 1203 while(fgets(line, sizeof(line), fp)) { 1204 line[strlen(line)-1] = '\0'; 1205 1206 /* 1207 * Should look like: 1208 * /dev/block/loop0 /mnt/obb/fc99df1323fd36424f864dcb76b76d65 ... 1209 */ 1210 sscanf(line, "%255s %255s %255s\n", device, mount_path, rest); 1211 1212 if (!strncmp(mount_path, loopDir, loopDirLen)) { 1213 int fd = open(device, O_RDONLY); 1214 if (fd >= 0) { 1215 struct loop_info64 li; 1216 if (ioctl(fd, LOOP_GET_STATUS64, &li) >= 0) { 1217 cli->sendMsg(ResponseCode::AsecListResult, 1218 (const char*) li.lo_file_name, false); 1219 } 1220 close(fd); 1221 } 1222 } 1223 } 1224 1225 fclose(fp); 1226 return 0; 1227 } 1228 1229 int VolumeManager::shareEnabled(const char *label, const char *method, bool *enabled) { 1230 Volume *v = lookupVolume(label); 1231 1232 if (!v) { 1233 errno = ENOENT; 1234 return -1; 1235 } 1236 1237 if (strcmp(method, "ums")) { 1238 errno = ENOSYS; 1239 return -1; 1240 } 1241 1242 if (v->getState() != Volume::State_Shared) { 1243 *enabled = false; 1244 } else { 1245 *enabled = true; 1246 } 1247 return 0; 1248 } 1249 1250 int VolumeManager::shareVolume(const char *label, const char *method) { 1251 Volume *v = lookupVolume(label); 1252 1253 if (!v) { 1254 errno = ENOENT; 1255 return -1; 1256 } 1257 1258 /* 1259 * Eventually, we'll want to support additional share back-ends, 1260 * some of which may work while the media is mounted. For now, 1261 * we just support UMS 1262 */ 1263 if (strcmp(method, "ums")) { 1264 errno = ENOSYS; 1265 return -1; 1266 } 1267 1268 if (v->getState() == Volume::State_NoMedia) { 1269 errno = ENODEV; 1270 return -1; 1271 } 1272 1273 if (v->getState() != Volume::State_Idle) { 1274 // You need to unmount manually befoe sharing 1275 errno = EBUSY; 1276 return -1; 1277 } 1278 1279 if (mVolManagerDisabled) { 1280 errno = EBUSY; 1281 return -1; 1282 } 1283 1284 dev_t d = v->getShareDevice(); 1285 if ((MAJOR(d) == 0) && (MINOR(d) == 0)) { 1286 // This volume does not support raw disk access 1287 errno = EINVAL; 1288 return -1; 1289 } 1290 1291 int fd; 1292 char nodepath[255]; 1293 int written = snprintf(nodepath, 1294 sizeof(nodepath), "/dev/block/vold/%d:%d", 1295 MAJOR(d), MINOR(d)); 1296 1297 if ((written < 0) || (size_t(written) >= sizeof(nodepath))) { 1298 SLOGE("shareVolume failed: couldn't construct nodepath"); 1299 return -1; 1300 } 1301 1302 if ((fd = open(MASS_STORAGE_FILE_PATH, O_WRONLY)) < 0) { 1303 SLOGE("Unable to open ums lunfile (%s)", strerror(errno)); 1304 return -1; 1305 } 1306 1307 if (write(fd, nodepath, strlen(nodepath)) < 0) { 1308 SLOGE("Unable to write to ums lunfile (%s)", strerror(errno)); 1309 close(fd); 1310 return -1; 1311 } 1312 1313 close(fd); 1314 v->handleVolumeShared(); 1315 if (mUmsSharingCount++ == 0) { 1316 FILE* fp; 1317 mSavedDirtyRatio = -1; // in case we fail 1318 if ((fp = fopen("/proc/sys/vm/dirty_ratio", "r+"))) { 1319 char line[16]; 1320 if (fgets(line, sizeof(line), fp) && sscanf(line, "%d", &mSavedDirtyRatio)) { 1321 fprintf(fp, "%d\n", mUmsDirtyRatio); 1322 } else { 1323 SLOGE("Failed to read dirty_ratio (%s)", strerror(errno)); 1324 } 1325 fclose(fp); 1326 } else { 1327 SLOGE("Failed to open /proc/sys/vm/dirty_ratio (%s)", strerror(errno)); 1328 } 1329 } 1330 return 0; 1331 } 1332 1333 int VolumeManager::unshareVolume(const char *label, const char *method) { 1334 Volume *v = lookupVolume(label); 1335 1336 if (!v) { 1337 errno = ENOENT; 1338 return -1; 1339 } 1340 1341 if (strcmp(method, "ums")) { 1342 errno = ENOSYS; 1343 return -1; 1344 } 1345 1346 if (v->getState() != Volume::State_Shared) { 1347 errno = EINVAL; 1348 return -1; 1349 } 1350 1351 int fd; 1352 if ((fd = open(MASS_STORAGE_FILE_PATH, O_WRONLY)) < 0) { 1353 SLOGE("Unable to open ums lunfile (%s)", strerror(errno)); 1354 return -1; 1355 } 1356 1357 char ch = 0; 1358 if (write(fd, &ch, 1) < 0) { 1359 SLOGE("Unable to write to ums lunfile (%s)", strerror(errno)); 1360 close(fd); 1361 return -1; 1362 } 1363 1364 close(fd); 1365 v->handleVolumeUnshared(); 1366 if (--mUmsSharingCount == 0 && mSavedDirtyRatio != -1) { 1367 FILE* fp; 1368 if ((fp = fopen("/proc/sys/vm/dirty_ratio", "r+"))) { 1369 fprintf(fp, "%d\n", mSavedDirtyRatio); 1370 fclose(fp); 1371 } else { 1372 SLOGE("Failed to open /proc/sys/vm/dirty_ratio (%s)", strerror(errno)); 1373 } 1374 mSavedDirtyRatio = -1; 1375 } 1376 return 0; 1377 } 1378 1379 extern "C" int vold_disableVol(const char *label) { 1380 VolumeManager *vm = VolumeManager::Instance(); 1381 vm->disableVolumeManager(); 1382 vm->unshareVolume(label, "ums"); 1383 return vm->unmountVolume(label, true, false); 1384 } 1385 1386 extern "C" int vold_getNumDirectVolumes(void) { 1387 VolumeManager *vm = VolumeManager::Instance(); 1388 return vm->getNumDirectVolumes(); 1389 } 1390 1391 int VolumeManager::getNumDirectVolumes(void) { 1392 VolumeCollection::iterator i; 1393 int n=0; 1394 1395 for (i = mVolumes->begin(); i != mVolumes->end(); ++i) { 1396 if ((*i)->getShareDevice() != (dev_t)0) { 1397 n++; 1398 } 1399 } 1400 return n; 1401 } 1402 1403 extern "C" int vold_getDirectVolumeList(struct volume_info *vol_list) { 1404 VolumeManager *vm = VolumeManager::Instance(); 1405 return vm->getDirectVolumeList(vol_list); 1406 } 1407 1408 int VolumeManager::getDirectVolumeList(struct volume_info *vol_list) { 1409 VolumeCollection::iterator i; 1410 int n=0; 1411 dev_t d; 1412 1413 for (i = mVolumes->begin(); i != mVolumes->end(); ++i) { 1414 if ((d=(*i)->getShareDevice()) != (dev_t)0) { 1415 (*i)->getVolInfo(&vol_list[n]); 1416 snprintf(vol_list[n].blk_dev, sizeof(vol_list[n].blk_dev), 1417 "/dev/block/vold/%d:%d",MAJOR(d), MINOR(d)); 1418 n++; 1419 } 1420 } 1421 1422 return 0; 1423 } 1424 1425 int VolumeManager::unmountVolume(const char *label, bool force, bool revert) { 1426 Volume *v = lookupVolume(label); 1427 1428 if (!v) { 1429 errno = ENOENT; 1430 return -1; 1431 } 1432 1433 if (v->getState() == Volume::State_NoMedia) { 1434 errno = ENODEV; 1435 return -1; 1436 } 1437 1438 if (v->getState() != Volume::State_Mounted) { 1439 SLOGW("Attempt to unmount volume which isn't mounted (%d)\n", 1440 v->getState()); 1441 errno = EBUSY; 1442 return UNMOUNT_NOT_MOUNTED_ERR; 1443 } 1444 1445 cleanupAsec(v, force); 1446 1447 return v->unmountVol(force, revert); 1448 } 1449 1450 extern "C" int vold_unmountAllAsecs(void) { 1451 int rc; 1452 1453 VolumeManager *vm = VolumeManager::Instance(); 1454 rc = vm->unmountAllAsecsInDir(Volume::SEC_ASECDIR_EXT); 1455 if (vm->unmountAllAsecsInDir(Volume::SEC_ASECDIR_INT)) { 1456 rc = -1; 1457 } 1458 return rc; 1459 } 1460 1461 #define ID_BUF_LEN 256 1462 #define ASEC_SUFFIX ".asec" 1463 #define ASEC_SUFFIX_LEN (sizeof(ASEC_SUFFIX) - 1) 1464 int VolumeManager::unmountAllAsecsInDir(const char *directory) { 1465 DIR *d = opendir(directory); 1466 int rc = 0; 1467 1468 if (!d) { 1469 SLOGE("Could not open asec dir %s", directory); 1470 return -1; 1471 } 1472 1473 size_t dirent_len = offsetof(struct dirent, d_name) + 1474 fpathconf(dirfd(d), _PC_NAME_MAX) + 1; 1475 1476 struct dirent *dent = (struct dirent *) malloc(dirent_len); 1477 if (dent == NULL) { 1478 SLOGE("Failed to allocate memory for asec dir"); 1479 return -1; 1480 } 1481 1482 struct dirent *result; 1483 while (!readdir_r(d, dent, &result) && result != NULL) { 1484 if (dent->d_name[0] == '.') 1485 continue; 1486 if (dent->d_type != DT_REG) 1487 continue; 1488 size_t name_len = strlen(dent->d_name); 1489 if (name_len > 5 && name_len < (ID_BUF_LEN + ASEC_SUFFIX_LEN - 1) && 1490 !strcmp(&dent->d_name[name_len - 5], ASEC_SUFFIX)) { 1491 char id[ID_BUF_LEN]; 1492 strlcpy(id, dent->d_name, name_len - 4); 1493 if (unmountAsec(id, true)) { 1494 /* Register the error, but try to unmount more asecs */ 1495 rc = -1; 1496 } 1497 } 1498 } 1499 closedir(d); 1500 1501 free(dent); 1502 1503 return rc; 1504 } 1505 1506 /* 1507 * Looks up a volume by it's label or mount-point 1508 */ 1509 Volume *VolumeManager::lookupVolume(const char *label) { 1510 VolumeCollection::iterator i; 1511 1512 for (i = mVolumes->begin(); i != mVolumes->end(); ++i) { 1513 if (label[0] == '/') { 1514 if (!strcmp(label, (*i)->getMountpoint())) 1515 return (*i); 1516 } else { 1517 if (!strcmp(label, (*i)->getLabel())) 1518 return (*i); 1519 } 1520 } 1521 return NULL; 1522 } 1523 1524 bool VolumeManager::isMountpointMounted(const char *mp) 1525 { 1526 char device[256]; 1527 char mount_path[256]; 1528 char rest[256]; 1529 FILE *fp; 1530 char line[1024]; 1531 1532 if (!(fp = fopen("/proc/mounts", "r"))) { 1533 SLOGE("Error opening /proc/mounts (%s)", strerror(errno)); 1534 return false; 1535 } 1536 1537 while(fgets(line, sizeof(line), fp)) { 1538 line[strlen(line)-1] = '\0'; 1539 sscanf(line, "%255s %255s %255s\n", device, mount_path, rest); 1540 if (!strcmp(mount_path, mp)) { 1541 fclose(fp); 1542 return true; 1543 } 1544 } 1545 1546 fclose(fp); 1547 return false; 1548 } 1549 1550 int VolumeManager::cleanupAsec(Volume *v, bool force) { 1551 int rc = unmountAllAsecsInDir(Volume::SEC_ASECDIR_EXT); 1552 1553 AsecIdCollection toUnmount; 1554 // Find the remaining OBB files that are on external storage. 1555 for (AsecIdCollection::iterator it = mActiveContainers->begin(); it != mActiveContainers->end(); 1556 ++it) { 1557 ContainerData* cd = *it; 1558 1559 if (cd->type == ASEC) { 1560 // nothing 1561 } else if (cd->type == OBB) { 1562 if (v == getVolumeForFile(cd->id)) { 1563 toUnmount.push_back(cd); 1564 } 1565 } else { 1566 SLOGE("Unknown container type %d!", cd->type); 1567 } 1568 } 1569 1570 for (AsecIdCollection::iterator it = toUnmount.begin(); it != toUnmount.end(); ++it) { 1571 ContainerData *cd = *it; 1572 SLOGI("Unmounting ASEC %s (dependant on %s)", cd->id, v->getMountpoint()); 1573 if (unmountObb(cd->id, force)) { 1574 SLOGE("Failed to unmount OBB %s (%s)", cd->id, strerror(errno)); 1575 rc = -1; 1576 } 1577 } 1578 1579 return rc; 1580 1581 } 1582 1583