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