VolumeManager.cpp revision a1091cb0c448a933068f9120fe6946c09812bfb6
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 <cutils/log.h> 31 32#include <sysutils/NetlinkEvent.h> 33 34#include "VolumeManager.h" 35#include "DirectVolume.h" 36#include "ResponseCode.h" 37#include "Loop.h" 38#include "Fat.h" 39#include "Devmapper.h" 40#include "Process.h" 41 42VolumeManager *VolumeManager::sInstance = NULL; 43 44VolumeManager *VolumeManager::Instance() { 45 if (!sInstance) 46 sInstance = new VolumeManager(); 47 return sInstance; 48} 49 50VolumeManager::VolumeManager() { 51 mBlockDevices = new BlockDeviceCollection(); 52 mVolumes = new VolumeCollection(); 53 mActiveContainers = new AsecIdCollection(); 54 mBroadcaster = NULL; 55 mUsbMassStorageConnected = false; 56} 57 58VolumeManager::~VolumeManager() { 59 delete mBlockDevices; 60 delete mVolumes; 61 delete mActiveContainers; 62} 63 64int VolumeManager::start() { 65 return 0; 66} 67 68int VolumeManager::stop() { 69 return 0; 70} 71 72int VolumeManager::addVolume(Volume *v) { 73 mVolumes->push_back(v); 74 return 0; 75} 76 77void VolumeManager::notifyUmsConnected(bool connected) { 78 char msg[255]; 79 80 if (connected) { 81 mUsbMassStorageConnected = true; 82 } else { 83 mUsbMassStorageConnected = false; 84 } 85 snprintf(msg, sizeof(msg), "Share method ums now %s", 86 (connected ? "available" : "unavailable")); 87 88 getBroadcaster()->sendBroadcast(ResponseCode::ShareAvailabilityChange, 89 msg, false); 90} 91 92void VolumeManager::handleSwitchEvent(NetlinkEvent *evt) { 93 const char *devpath = evt->findParam("DEVPATH"); 94 const char *name = evt->findParam("SWITCH_NAME"); 95 const char *state = evt->findParam("SWITCH_STATE"); 96 97 if (!name || !state) { 98 LOGW("Switch %s event missing name/state info", devpath); 99 return; 100 } 101 102 if (!strcmp(name, "usb_mass_storage")) { 103 104 if (!strcmp(state, "online")) { 105 notifyUmsConnected(true); 106 } else { 107 notifyUmsConnected(false); 108 } 109 } else { 110 LOGW("Ignoring unknown switch '%s'", name); 111 } 112} 113 114void VolumeManager::handleBlockEvent(NetlinkEvent *evt) { 115 const char *devpath = evt->findParam("DEVPATH"); 116 117 /* Lookup a volume to handle this device */ 118 VolumeCollection::iterator it; 119 bool hit = false; 120 for (it = mVolumes->begin(); it != mVolumes->end(); ++it) { 121 if (!(*it)->handleBlockEvent(evt)) { 122#ifdef NETLINK_DEBUG 123 LOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel()); 124#endif 125 hit = true; 126 break; 127 } 128 } 129 130 if (!hit) { 131#ifdef NETLINK_DEBUG 132 LOGW("No volumes handled block event for '%s'", devpath); 133#endif 134 } 135} 136 137int VolumeManager::listVolumes(SocketClient *cli) { 138 VolumeCollection::iterator i; 139 140 for (i = mVolumes->begin(); i != mVolumes->end(); ++i) { 141 char *buffer; 142 asprintf(&buffer, "%s %s %d", 143 (*i)->getLabel(), (*i)->getMountpoint(), 144 (*i)->getState()); 145 cli->sendMsg(ResponseCode::VolumeListResult, buffer, false); 146 free(buffer); 147 } 148 cli->sendMsg(ResponseCode::CommandOkay, "Volumes listed.", false); 149 return 0; 150} 151 152int VolumeManager::formatVolume(const char *label) { 153 Volume *v = lookupVolume(label); 154 155 if (!v) { 156 errno = ENOENT; 157 return -1; 158 } 159 160 return v->formatVol(); 161} 162 163int VolumeManager::getAsecMountPath(const char *id, char *buffer, int maxlen) { 164 165 snprintf(buffer, maxlen, "%s/%s", Volume::ASECDIR, id); 166 return 0; 167} 168 169int VolumeManager::createAsec(const char *id, unsigned int numSectors, 170 const char *fstype, const char *key, int ownerUid) { 171 172 if (numSectors < ((1024*1024)/512)) { 173 LOGE("Invalid container size specified (%d sectors)", numSectors); 174 errno = EINVAL; 175 return -1; 176 } 177 178 if (lookupVolume(id)) { 179 LOGE("ASEC id '%s' currently exists", id); 180 errno = EADDRINUSE; 181 return -1; 182 } 183 184 char asecFileName[255]; 185 snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id); 186 187 if (!access(asecFileName, F_OK)) { 188 LOGE("ASEC file '%s' currently exists - destroy it first! (%s)", 189 asecFileName, strerror(errno)); 190 errno = EADDRINUSE; 191 return -1; 192 } 193 194 if (Loop::createImageFile(asecFileName, numSectors)) { 195 LOGE("ASEC image file creation failed (%s)", strerror(errno)); 196 return -1; 197 } 198 199 char loopDevice[255]; 200 if (Loop::create(asecFileName, loopDevice, sizeof(loopDevice))) { 201 LOGE("ASEC loop device creation failed (%s)", strerror(errno)); 202 unlink(asecFileName); 203 return -1; 204 } 205 206 char dmDevice[255]; 207 bool cleanupDm = false; 208 209 if (strcmp(key, "none")) { 210 if (Devmapper::create(id, loopDevice, key, numSectors, dmDevice, 211 sizeof(dmDevice))) { 212 LOGE("ASEC device mapping failed (%s)", strerror(errno)); 213 Loop::destroyByDevice(loopDevice); 214 unlink(asecFileName); 215 return -1; 216 } 217 cleanupDm = true; 218 } else { 219 strcpy(dmDevice, loopDevice); 220 } 221 222 if (strcmp(fstype, "none")) { 223 if (strcmp(fstype, "fat")) { 224 LOGW("Unknown fstype '%s' specified for container", fstype); 225 } 226 227 if (Fat::format(dmDevice)) { 228 LOGE("ASEC FAT format failed (%s)", strerror(errno)); 229 if (cleanupDm) { 230 Devmapper::destroy(id); 231 } 232 Loop::destroyByDevice(loopDevice); 233 unlink(asecFileName); 234 return -1; 235 } 236 char mountPoint[255]; 237 238 snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id); 239 if (mkdir(mountPoint, 0777)) { 240 if (errno != EEXIST) { 241 LOGE("Mountpoint creation failed (%s)", strerror(errno)); 242 if (cleanupDm) { 243 Devmapper::destroy(id); 244 } 245 Loop::destroyByDevice(loopDevice); 246 unlink(asecFileName); 247 return -1; 248 } 249 } 250 251 if (Fat::doMount(dmDevice, mountPoint, false, false, ownerUid, 252 0, 0000, false)) { 253 LOGE("ASEC FAT mount failed (%s)", strerror(errno)); 254 if (cleanupDm) { 255 Devmapper::destroy(id); 256 } 257 Loop::destroyByDevice(loopDevice); 258 unlink(asecFileName); 259 return -1; 260 } 261 } else { 262 LOGI("Created raw secure container %s (no filesystem)", id); 263 } 264 265 mActiveContainers->push_back(strdup(id)); 266 return 0; 267} 268 269int VolumeManager::finalizeAsec(const char *id) { 270 char asecFileName[255]; 271 char loopDevice[255]; 272 char mountPoint[255]; 273 274 snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id); 275 276 if (Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) { 277 LOGE("Unable to finalize %s (%s)", id, strerror(errno)); 278 return -1; 279 } 280 281 snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id); 282 // XXX: 283 if (Fat::doMount(loopDevice, mountPoint, true, true, 0, 0, 0227, false)) { 284 LOGE("ASEC finalize mount failed (%s)", strerror(errno)); 285 return -1; 286 } 287 288 LOGD("ASEC %s finalized", id); 289 return 0; 290} 291 292int VolumeManager::renameAsec(const char *id1, const char *id2) { 293 char *asecFilename1; 294 char *asecFilename2; 295 char mountPoint[255]; 296 297 asprintf(&asecFilename1, "%s/%s.asec", Volume::SEC_ASECDIR, id1); 298 asprintf(&asecFilename2, "%s/%s.asec", Volume::SEC_ASECDIR, id2); 299 300 snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id1); 301 if (isMountpointMounted(mountPoint)) { 302 LOGW("Rename attempt when src mounted"); 303 errno = EBUSY; 304 goto out_err; 305 } 306 307 snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id2); 308 if (isMountpointMounted(mountPoint)) { 309 LOGW("Rename attempt when dst mounted"); 310 errno = EBUSY; 311 goto out_err; 312 } 313 314 if (!access(asecFilename2, F_OK)) { 315 LOGE("Rename attempt when dst exists"); 316 errno = EADDRINUSE; 317 goto out_err; 318 } 319 320 if (rename(asecFilename1, asecFilename2)) { 321 LOGE("Rename of '%s' to '%s' failed (%s)", asecFilename1, asecFilename2, strerror(errno)); 322 goto out_err; 323 } 324 325 free(asecFilename1); 326 free(asecFilename2); 327 return 0; 328 329out_err: 330 free(asecFilename1); 331 free(asecFilename2); 332 return -1; 333} 334 335#define ASEC_UNMOUNT_RETRIES 5 336int VolumeManager::unmountAsec(const char *id, bool force) { 337 char asecFileName[255]; 338 char mountPoint[255]; 339 340 snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id); 341 snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id); 342 343 if (!isMountpointMounted(mountPoint)) { 344 LOGE("Unmount request for ASEC %s when not mounted", id); 345 errno = EINVAL; 346 return -1; 347 } 348 349 int i, rc; 350 for (i = 1; i <= ASEC_UNMOUNT_RETRIES; i++) { 351 rc = umount(mountPoint); 352 if (!rc) { 353 break; 354 } 355 if (rc && (errno == EINVAL || errno == ENOENT)) { 356 LOGI("Secure container %s unmounted OK", id); 357 rc = 0; 358 break; 359 } 360 LOGW("ASEC %s unmount attempt %d failed (%s)", 361 id, i, strerror(errno)); 362 363 int action = 0; // default is to just complain 364 365 if (force) { 366 if (i > (ASEC_UNMOUNT_RETRIES - 2)) 367 action = 2; // SIGKILL 368 else if (i > (ASEC_UNMOUNT_RETRIES - 3)) 369 action = 1; // SIGHUP 370 } 371 372 Process::killProcessesWithOpenFiles(mountPoint, action); 373 usleep(1000 * 1000); 374 } 375 376 if (rc) { 377 errno = EBUSY; 378 LOGE("Failed to unmount container %s (%s)", id, strerror(errno)); 379 return -1; 380 } 381 382 int retries = 10; 383 384 while(retries--) { 385 if (!rmdir(mountPoint)) { 386 break; 387 } 388 389 LOGW("Failed to rmdir %s (%s)", mountPoint, strerror(errno)); 390 usleep(1000 * 1000); 391 } 392 393 if (!retries) { 394 LOGE("Timed out trying to rmdir %s (%s)", mountPoint, strerror(errno)); 395 } 396 397 if (Devmapper::destroy(id) && errno != ENXIO) { 398 LOGE("Failed to destroy devmapper instance (%s)", strerror(errno)); 399 } 400 401 char loopDevice[255]; 402 if (!Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) { 403 Loop::destroyByDevice(loopDevice); 404 } 405 406 AsecIdCollection::iterator it; 407 for (it = mActiveContainers->begin(); it != mActiveContainers->end(); ++it) { 408 if (!strcmp(*it, id)) { 409 free(*it); 410 mActiveContainers->erase(it); 411 break; 412 } 413 } 414 if (it == mActiveContainers->end()) { 415 LOGW("mActiveContainers is inconsistent!"); 416 } 417 return 0; 418} 419 420int VolumeManager::destroyAsec(const char *id, bool force) { 421 char asecFileName[255]; 422 char mountPoint[255]; 423 424 snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id); 425 snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id); 426 427 if (isMountpointMounted(mountPoint)) { 428 LOGD("Unmounting container before destroy"); 429 if (unmountAsec(id, force)) { 430 LOGE("Failed to unmount asec %s for destroy (%s)", id, strerror(errno)); 431 return -1; 432 } 433 } 434 435 if (unlink(asecFileName)) { 436 LOGE("Failed to unlink asec '%s' (%s)", asecFileName, strerror(errno)); 437 return -1; 438 } 439 440 LOGD("ASEC %s destroyed", id); 441 return 0; 442} 443 444int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid) { 445 char asecFileName[255]; 446 char mountPoint[255]; 447 448 snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id); 449 snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id); 450 451 if (isMountpointMounted(mountPoint)) { 452 LOGE("ASEC %s already mounted", id); 453 errno = EBUSY; 454 return -1; 455 } 456 457 char loopDevice[255]; 458 if (Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) { 459 if (Loop::create(asecFileName, loopDevice, sizeof(loopDevice))) { 460 LOGE("ASEC loop device creation failed (%s)", strerror(errno)); 461 return -1; 462 } 463 LOGD("New loop device created at %s", loopDevice); 464 } else { 465 LOGD("Found active loopback for %s at %s", asecFileName, loopDevice); 466 } 467 468 char dmDevice[255]; 469 bool cleanupDm = false; 470 if (strcmp(key, "none")) { 471 if (Devmapper::lookupActive(id, dmDevice, sizeof(dmDevice))) { 472 unsigned int nr_sec = 0; 473 int fd; 474 475 if ((fd = open(loopDevice, O_RDWR)) < 0) { 476 LOGE("Failed to open loopdevice (%s)", strerror(errno)); 477 Loop::destroyByDevice(loopDevice); 478 return -1; 479 } 480 481 if (ioctl(fd, BLKGETSIZE, &nr_sec)) { 482 LOGE("Failed to get loop size (%s)", strerror(errno)); 483 Loop::destroyByDevice(loopDevice); 484 close(fd); 485 return -1; 486 } 487 close(fd); 488 if (Devmapper::create(id, loopDevice, key, nr_sec, 489 dmDevice, sizeof(dmDevice))) { 490 LOGE("ASEC device mapping failed (%s)", strerror(errno)); 491 Loop::destroyByDevice(loopDevice); 492 return -1; 493 } 494 LOGD("New devmapper instance created at %s", dmDevice); 495 } else { 496 LOGD("Found active devmapper for %s at %s", asecFileName, dmDevice); 497 } 498 cleanupDm = true; 499 } else { 500 strcpy(dmDevice, loopDevice); 501 } 502 503 if (mkdir(mountPoint, 0777)) { 504 if (errno != EEXIST) { 505 LOGE("Mountpoint creation failed (%s)", strerror(errno)); 506 if (cleanupDm) { 507 Devmapper::destroy(id); 508 } 509 Loop::destroyByDevice(loopDevice); 510 return -1; 511 } 512 } 513 514 if (Fat::doMount(dmDevice, mountPoint, true, false, ownerUid, 0, 515 0222, false)) { 516// 0227, false)) { 517 LOGE("ASEC mount failed (%s)", strerror(errno)); 518 if (cleanupDm) { 519 Devmapper::destroy(id); 520 } 521 Loop::destroyByDevice(loopDevice); 522 return -1; 523 } 524 525 mActiveContainers->push_back(strdup(id)); 526 LOGD("ASEC %s mounted", id); 527 return 0; 528} 529 530int VolumeManager::mountVolume(const char *label) { 531 Volume *v = lookupVolume(label); 532 533 if (!v) { 534 errno = ENOENT; 535 return -1; 536 } 537 538 return v->mountVol(); 539} 540 541int VolumeManager::shareAvailable(const char *method, bool *avail) { 542 543 if (strcmp(method, "ums")) { 544 errno = ENOSYS; 545 return -1; 546 } 547 548 if (mUsbMassStorageConnected) 549 *avail = true; 550 else 551 *avail = false; 552 return 0; 553} 554 555int VolumeManager::shareEnabled(const char *label, const char *method, bool *enabled) { 556 Volume *v = lookupVolume(label); 557 558 if (!v) { 559 errno = ENOENT; 560 return -1; 561 } 562 563 if (strcmp(method, "ums")) { 564 errno = ENOSYS; 565 return -1; 566 } 567 568 if (v->getState() != Volume::State_Shared) { 569 *enabled = false; 570 } else { 571 *enabled = true; 572 } 573 return 0; 574} 575 576int VolumeManager::simulate(const char *cmd, const char *arg) { 577 578 if (!strcmp(cmd, "ums")) { 579 if (!strcmp(arg, "connect")) { 580 notifyUmsConnected(true); 581 } else if (!strcmp(arg, "disconnect")) { 582 notifyUmsConnected(false); 583 } else { 584 errno = EINVAL; 585 return -1; 586 } 587 } else { 588 errno = EINVAL; 589 return -1; 590 } 591 return 0; 592} 593 594int VolumeManager::shareVolume(const char *label, const char *method) { 595 Volume *v = lookupVolume(label); 596 597 if (!v) { 598 errno = ENOENT; 599 return -1; 600 } 601 602 /* 603 * Eventually, we'll want to support additional share back-ends, 604 * some of which may work while the media is mounted. For now, 605 * we just support UMS 606 */ 607 if (strcmp(method, "ums")) { 608 errno = ENOSYS; 609 return -1; 610 } 611 612 if (v->getState() == Volume::State_NoMedia) { 613 errno = ENODEV; 614 return -1; 615 } 616 617 if (v->getState() != Volume::State_Idle) { 618 // You need to unmount manually befoe sharing 619 errno = EBUSY; 620 return -1; 621 } 622 623 dev_t d = v->getDiskDevice(); 624 if ((MAJOR(d) == 0) && (MINOR(d) == 0)) { 625 // This volume does not support raw disk access 626 errno = EINVAL; 627 return -1; 628 } 629 630 int fd; 631 char nodepath[255]; 632 snprintf(nodepath, 633 sizeof(nodepath), "/dev/block/vold/%d:%d", 634 MAJOR(d), MINOR(d)); 635 636 if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0/file", 637 O_WRONLY)) < 0) { 638 LOGE("Unable to open ums lunfile (%s)", strerror(errno)); 639 return -1; 640 } 641 642 if (write(fd, nodepath, strlen(nodepath)) < 0) { 643 LOGE("Unable to write to ums lunfile (%s)", strerror(errno)); 644 close(fd); 645 return -1; 646 } 647 648 close(fd); 649 v->handleVolumeShared(); 650 return 0; 651} 652 653int VolumeManager::unshareVolume(const char *label, const char *method) { 654 Volume *v = lookupVolume(label); 655 656 if (!v) { 657 errno = ENOENT; 658 return -1; 659 } 660 661 if (strcmp(method, "ums")) { 662 errno = ENOSYS; 663 return -1; 664 } 665 666 if (v->getState() != Volume::State_Shared) { 667 errno = EINVAL; 668 return -1; 669 } 670 671 dev_t d = v->getDiskDevice(); 672 673 int fd; 674 char nodepath[255]; 675 snprintf(nodepath, 676 sizeof(nodepath), "/dev/block/vold/%d:%d", 677 MAJOR(d), MINOR(d)); 678 679 if ((fd = open("/sys/devices/platform/usb_mass_storage/lun0/file", O_WRONLY)) < 0) { 680 LOGE("Unable to open ums lunfile (%s)", strerror(errno)); 681 return -1; 682 } 683 684 char ch = 0; 685 if (write(fd, &ch, 1) < 0) { 686 LOGE("Unable to write to ums lunfile (%s)", strerror(errno)); 687 close(fd); 688 return -1; 689 } 690 691 close(fd); 692 v->handleVolumeUnshared(); 693 return 0; 694} 695 696int VolumeManager::unmountVolume(const char *label, bool force) { 697 Volume *v = lookupVolume(label); 698 699 if (!v) { 700 errno = ENOENT; 701 return -1; 702 } 703 704 if (v->getState() == Volume::State_NoMedia) { 705 errno = ENODEV; 706 return -1; 707 } 708 709 if (v->getState() != Volume::State_Mounted) { 710 LOGW("Attempt to unmount volume which isn't mounted (%d)\n", 711 v->getState()); 712 errno = EBUSY; 713 return -1; 714 } 715 716 while(mActiveContainers->size()) { 717 AsecIdCollection::iterator it = mActiveContainers->begin(); 718 LOGI("Unmounting ASEC %s (dependant on %s)", *it, v->getMountpoint()); 719 if (unmountAsec(*it, force)) { 720 LOGE("Failed to unmount ASEC %s (%s)", *it, strerror(errno)); 721 return -1; 722 } 723 } 724 725 return v->unmountVol(force); 726} 727 728/* 729 * Looks up a volume by it's label or mount-point 730 */ 731Volume *VolumeManager::lookupVolume(const char *label) { 732 VolumeCollection::iterator i; 733 734 for (i = mVolumes->begin(); i != mVolumes->end(); ++i) { 735 if (label[0] == '/') { 736 if (!strcmp(label, (*i)->getMountpoint())) 737 return (*i); 738 } else { 739 if (!strcmp(label, (*i)->getLabel())) 740 return (*i); 741 } 742 } 743 return NULL; 744} 745 746bool VolumeManager::isMountpointMounted(const char *mp) 747{ 748 char device[256]; 749 char mount_path[256]; 750 char rest[256]; 751 FILE *fp; 752 char line[1024]; 753 754 if (!(fp = fopen("/proc/mounts", "r"))) { 755 LOGE("Error opening /proc/mounts (%s)", strerror(errno)); 756 return false; 757 } 758 759 while(fgets(line, sizeof(line), fp)) { 760 line[strlen(line)-1] = '\0'; 761 sscanf(line, "%255s %255s %255s\n", device, mount_path, rest); 762 if (!strcmp(mount_path, mp)) { 763 fclose(fp); 764 return true; 765 } 766 767 } 768 769 fclose(fp); 770 return false; 771} 772 773