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