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 <stdlib.h> 18 #include <string.h> 19 #include <dirent.h> 20 #include <errno.h> 21 #include <fcntl.h> 22 23 #include <sys/types.h> 24 #include <sys/stat.h> 25 #include <sys/types.h> 26 #include <sys/mman.h> 27 #include <sys/mount.h> 28 #include <sys/param.h> 29 30 #include <linux/kdev_t.h> 31 #include <linux/fs.h> 32 33 #include <cutils/properties.h> 34 35 #include <diskconfig/diskconfig.h> 36 37 #include <private/android_filesystem_config.h> 38 39 #define LOG_TAG "Vold" 40 41 #include <cutils/log.h> 42 43 #include "Volume.h" 44 #include "VolumeManager.h" 45 #include "ResponseCode.h" 46 #include "Fat.h" 47 #include "Process.h" 48 #include "cryptfs.h" 49 50 extern "C" void dos_partition_dec(void const *pp, struct dos_partition *d); 51 extern "C" void dos_partition_enc(void *pp, struct dos_partition *d); 52 53 54 /* 55 * Secure directory - stuff that only root can see 56 */ 57 const char *Volume::SECDIR = "/mnt/secure"; 58 59 /* 60 * Secure staging directory - where media is mounted for preparation 61 */ 62 const char *Volume::SEC_STGDIR = "/mnt/secure/staging"; 63 64 /* 65 * Path to the directory on the media which contains publicly accessable 66 * asec imagefiles. This path will be obscured before the mount is 67 * exposed to non priviledged users. 68 */ 69 const char *Volume::SEC_STG_SECIMGDIR = "/mnt/secure/staging/.android_secure"; 70 71 /* 72 * Path to external storage where *only* root can access ASEC image files 73 */ 74 const char *Volume::SEC_ASECDIR_EXT = "/mnt/secure/asec"; 75 76 /* 77 * Path to internal storage where *only* root can access ASEC image files 78 */ 79 const char *Volume::SEC_ASECDIR_INT = "/data/app-asec"; 80 /* 81 * Path to where secure containers are mounted 82 */ 83 const char *Volume::ASECDIR = "/mnt/asec"; 84 85 /* 86 * Path to where OBBs are mounted 87 */ 88 const char *Volume::LOOPDIR = "/mnt/obb"; 89 90 static const char *stateToStr(int state) { 91 if (state == Volume::State_Init) 92 return "Initializing"; 93 else if (state == Volume::State_NoMedia) 94 return "No-Media"; 95 else if (state == Volume::State_Idle) 96 return "Idle-Unmounted"; 97 else if (state == Volume::State_Pending) 98 return "Pending"; 99 else if (state == Volume::State_Mounted) 100 return "Mounted"; 101 else if (state == Volume::State_Unmounting) 102 return "Unmounting"; 103 else if (state == Volume::State_Checking) 104 return "Checking"; 105 else if (state == Volume::State_Formatting) 106 return "Formatting"; 107 else if (state == Volume::State_Shared) 108 return "Shared-Unmounted"; 109 else if (state == Volume::State_SharedMnt) 110 return "Shared-Mounted"; 111 else 112 return "Unknown-Error"; 113 } 114 115 Volume::Volume(VolumeManager *vm, const char *label, const char *mount_point) { 116 mVm = vm; 117 mDebug = false; 118 mLabel = strdup(label); 119 mMountpoint = strdup(mount_point); 120 mState = Volume::State_Init; 121 mCurrentlyMountedKdev = -1; 122 mPartIdx = -1; 123 mRetryMount = false; 124 } 125 126 Volume::~Volume() { 127 free(mLabel); 128 free(mMountpoint); 129 } 130 131 void Volume::protectFromAutorunStupidity() { 132 char filename[255]; 133 134 snprintf(filename, sizeof(filename), "%s/autorun.inf", SEC_STGDIR); 135 if (!access(filename, F_OK)) { 136 SLOGW("Volume contains an autorun.inf! - removing"); 137 /* 138 * Ensure the filename is all lower-case so 139 * the process killer can find the inode. 140 * Probably being paranoid here but meh. 141 */ 142 rename(filename, filename); 143 Process::killProcessesWithOpenFiles(filename, 2); 144 if (unlink(filename)) { 145 SLOGE("Failed to remove %s (%s)", filename, strerror(errno)); 146 } 147 } 148 } 149 150 void Volume::setDebug(bool enable) { 151 mDebug = enable; 152 } 153 154 dev_t Volume::getDiskDevice() { 155 return MKDEV(0, 0); 156 }; 157 158 dev_t Volume::getShareDevice() { 159 return getDiskDevice(); 160 } 161 162 void Volume::handleVolumeShared() { 163 } 164 165 void Volume::handleVolumeUnshared() { 166 } 167 168 int Volume::handleBlockEvent(NetlinkEvent *evt) { 169 errno = ENOSYS; 170 return -1; 171 } 172 173 void Volume::setState(int state) { 174 char msg[255]; 175 int oldState = mState; 176 177 if (oldState == state) { 178 SLOGW("Duplicate state (%d)\n", state); 179 return; 180 } 181 182 if ((oldState == Volume::State_Pending) && (state != Volume::State_Idle)) { 183 mRetryMount = false; 184 } 185 186 mState = state; 187 188 SLOGD("Volume %s state changing %d (%s) -> %d (%s)", mLabel, 189 oldState, stateToStr(oldState), mState, stateToStr(mState)); 190 snprintf(msg, sizeof(msg), 191 "Volume %s %s state changed from %d (%s) to %d (%s)", getLabel(), 192 getMountpoint(), oldState, stateToStr(oldState), mState, 193 stateToStr(mState)); 194 195 mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeStateChange, 196 msg, false); 197 } 198 199 int Volume::createDeviceNode(const char *path, int major, int minor) { 200 mode_t mode = 0660 | S_IFBLK; 201 dev_t dev = (major << 8) | minor; 202 if (mknod(path, mode, dev) < 0) { 203 if (errno != EEXIST) { 204 return -1; 205 } 206 } 207 return 0; 208 } 209 210 int Volume::formatVol() { 211 212 if (getState() == Volume::State_NoMedia) { 213 errno = ENODEV; 214 return -1; 215 } else if (getState() != Volume::State_Idle) { 216 errno = EBUSY; 217 return -1; 218 } 219 220 if (isMountpointMounted(getMountpoint())) { 221 SLOGW("Volume is idle but appears to be mounted - fixing"); 222 setState(Volume::State_Mounted); 223 // mCurrentlyMountedKdev = XXX 224 errno = EBUSY; 225 return -1; 226 } 227 228 bool formatEntireDevice = (mPartIdx == -1); 229 char devicePath[255]; 230 dev_t diskNode = getDiskDevice(); 231 dev_t partNode = MKDEV(MAJOR(diskNode), (formatEntireDevice ? 1 : mPartIdx)); 232 233 setState(Volume::State_Formatting); 234 235 int ret = -1; 236 // Only initialize the MBR if we are formatting the entire device 237 if (formatEntireDevice) { 238 sprintf(devicePath, "/dev/block/vold/%d:%d", 239 MAJOR(diskNode), MINOR(diskNode)); 240 241 if (initializeMbr(devicePath)) { 242 SLOGE("Failed to initialize MBR (%s)", strerror(errno)); 243 goto err; 244 } 245 } 246 247 sprintf(devicePath, "/dev/block/vold/%d:%d", 248 MAJOR(partNode), MINOR(partNode)); 249 250 if (mDebug) { 251 SLOGI("Formatting volume %s (%s)", getLabel(), devicePath); 252 } 253 254 if (Fat::format(devicePath, 0)) { 255 SLOGE("Failed to format (%s)", strerror(errno)); 256 goto err; 257 } 258 259 ret = 0; 260 261 err: 262 setState(Volume::State_Idle); 263 return ret; 264 } 265 266 bool Volume::isMountpointMounted(const char *path) { 267 char device[256]; 268 char mount_path[256]; 269 char rest[256]; 270 FILE *fp; 271 char line[1024]; 272 273 if (!(fp = fopen("/proc/mounts", "r"))) { 274 SLOGE("Error opening /proc/mounts (%s)", strerror(errno)); 275 return false; 276 } 277 278 while(fgets(line, sizeof(line), fp)) { 279 line[strlen(line)-1] = '\0'; 280 sscanf(line, "%255s %255s %255s\n", device, mount_path, rest); 281 if (!strcmp(mount_path, path)) { 282 fclose(fp); 283 return true; 284 } 285 286 } 287 288 fclose(fp); 289 return false; 290 } 291 292 int Volume::mountVol() { 293 dev_t deviceNodes[4]; 294 int n, i, rc = 0; 295 char errmsg[255]; 296 const char* externalStorage = getenv("EXTERNAL_STORAGE"); 297 bool primaryStorage = externalStorage && !strcmp(getMountpoint(), externalStorage); 298 char decrypt_state[PROPERTY_VALUE_MAX]; 299 char crypto_state[PROPERTY_VALUE_MAX]; 300 char encrypt_progress[PROPERTY_VALUE_MAX]; 301 int flags; 302 303 property_get("vold.decrypt", decrypt_state, ""); 304 property_get("vold.encrypt_progress", encrypt_progress, ""); 305 306 /* Don't try to mount the volumes if we have not yet entered the disk password 307 * or are in the process of encrypting. 308 */ 309 if ((getState() == Volume::State_NoMedia) || 310 ((!strcmp(decrypt_state, "1") || encrypt_progress[0]) && primaryStorage)) { 311 snprintf(errmsg, sizeof(errmsg), 312 "Volume %s %s mount failed - no media", 313 getLabel(), getMountpoint()); 314 mVm->getBroadcaster()->sendBroadcast( 315 ResponseCode::VolumeMountFailedNoMedia, 316 errmsg, false); 317 errno = ENODEV; 318 return -1; 319 } else if (getState() != Volume::State_Idle) { 320 errno = EBUSY; 321 if (getState() == Volume::State_Pending) { 322 mRetryMount = true; 323 } 324 return -1; 325 } 326 327 if (isMountpointMounted(getMountpoint())) { 328 SLOGW("Volume is idle but appears to be mounted - fixing"); 329 setState(Volume::State_Mounted); 330 // mCurrentlyMountedKdev = XXX 331 return 0; 332 } 333 334 n = getDeviceNodes((dev_t *) &deviceNodes, 4); 335 if (!n) { 336 SLOGE("Failed to get device nodes (%s)\n", strerror(errno)); 337 return -1; 338 } 339 340 /* If we're running encrypted, and the volume is marked as encryptable and nonremovable, 341 * and vold is asking to mount the primaryStorage device, then we need to decrypt 342 * that partition, and update the volume object to point to it's new decrypted 343 * block device 344 */ 345 property_get("ro.crypto.state", crypto_state, ""); 346 flags = getFlags(); 347 if (primaryStorage && 348 ((flags & (VOL_NONREMOVABLE | VOL_ENCRYPTABLE))==(VOL_NONREMOVABLE | VOL_ENCRYPTABLE)) && 349 !strcmp(crypto_state, "encrypted") && !isDecrypted()) { 350 char new_sys_path[MAXPATHLEN]; 351 char nodepath[256]; 352 int new_major, new_minor; 353 354 if (n != 1) { 355 /* We only expect one device node returned when mounting encryptable volumes */ 356 SLOGE("Too many device nodes returned when mounting %d\n", getMountpoint()); 357 return -1; 358 } 359 360 if (cryptfs_setup_volume(getLabel(), MAJOR(deviceNodes[0]), MINOR(deviceNodes[0]), 361 new_sys_path, sizeof(new_sys_path), 362 &new_major, &new_minor)) { 363 SLOGE("Cannot setup encryption mapping for %d\n", getMountpoint()); 364 return -1; 365 } 366 /* We now have the new sysfs path for the decrypted block device, and the 367 * majore and minor numbers for it. So, create the device, update the 368 * path to the new sysfs path, and continue. 369 */ 370 snprintf(nodepath, 371 sizeof(nodepath), "/dev/block/vold/%d:%d", 372 new_major, new_minor); 373 if (createDeviceNode(nodepath, new_major, new_minor)) { 374 SLOGE("Error making device node '%s' (%s)", nodepath, 375 strerror(errno)); 376 } 377 378 // Todo: Either create sys filename from nodepath, or pass in bogus path so 379 // vold ignores state changes on this internal device. 380 updateDeviceInfo(nodepath, new_major, new_minor); 381 382 /* Get the device nodes again, because they just changed */ 383 n = getDeviceNodes((dev_t *) &deviceNodes, 4); 384 if (!n) { 385 SLOGE("Failed to get device nodes (%s)\n", strerror(errno)); 386 return -1; 387 } 388 } 389 390 for (i = 0; i < n; i++) { 391 char devicePath[255]; 392 393 sprintf(devicePath, "/dev/block/vold/%d:%d", MAJOR(deviceNodes[i]), 394 MINOR(deviceNodes[i])); 395 396 SLOGI("%s being considered for volume %s\n", devicePath, getLabel()); 397 398 errno = 0; 399 setState(Volume::State_Checking); 400 401 if (Fat::check(devicePath)) { 402 if (errno == ENODATA) { 403 SLOGW("%s does not contain a FAT filesystem\n", devicePath); 404 continue; 405 } 406 errno = EIO; 407 /* Badness - abort the mount */ 408 SLOGE("%s failed FS checks (%s)", devicePath, strerror(errno)); 409 setState(Volume::State_Idle); 410 return -1; 411 } 412 413 /* 414 * Mount the device on our internal staging mountpoint so we can 415 * muck with it before exposing it to non priviledged users. 416 */ 417 errno = 0; 418 int gid; 419 420 if (primaryStorage) { 421 // Special case the primary SD card. 422 // For this we grant write access to the SDCARD_RW group. 423 gid = AID_SDCARD_RW; 424 } else { 425 // For secondary external storage we keep things locked up. 426 gid = AID_MEDIA_RW; 427 } 428 if (Fat::doMount(devicePath, "/mnt/secure/staging", false, false, false, 429 AID_SYSTEM, gid, 0702, true)) { 430 SLOGE("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno)); 431 continue; 432 } 433 434 SLOGI("Device %s, target %s mounted @ /mnt/secure/staging", devicePath, getMountpoint()); 435 436 protectFromAutorunStupidity(); 437 438 // only create android_secure on primary storage 439 if (primaryStorage && createBindMounts()) { 440 SLOGE("Failed to create bindmounts (%s)", strerror(errno)); 441 umount("/mnt/secure/staging"); 442 setState(Volume::State_Idle); 443 return -1; 444 } 445 446 /* 447 * Now that the bindmount trickery is done, atomically move the 448 * whole subtree to expose it to non priviledged users. 449 */ 450 if (doMoveMount("/mnt/secure/staging", getMountpoint(), false)) { 451 SLOGE("Failed to move mount (%s)", strerror(errno)); 452 umount("/mnt/secure/staging"); 453 setState(Volume::State_Idle); 454 return -1; 455 } 456 setState(Volume::State_Mounted); 457 mCurrentlyMountedKdev = deviceNodes[i]; 458 return 0; 459 } 460 461 SLOGE("Volume %s found no suitable devices for mounting :(\n", getLabel()); 462 setState(Volume::State_Idle); 463 464 return -1; 465 } 466 467 int Volume::createBindMounts() { 468 unsigned long flags; 469 470 /* 471 * Rename old /android_secure -> /.android_secure 472 */ 473 if (!access("/mnt/secure/staging/android_secure", R_OK | X_OK) && 474 access(SEC_STG_SECIMGDIR, R_OK | X_OK)) { 475 if (rename("/mnt/secure/staging/android_secure", SEC_STG_SECIMGDIR)) { 476 SLOGE("Failed to rename legacy asec dir (%s)", strerror(errno)); 477 } 478 } 479 480 /* 481 * Ensure that /android_secure exists and is a directory 482 */ 483 if (access(SEC_STG_SECIMGDIR, R_OK | X_OK)) { 484 if (errno == ENOENT) { 485 if (mkdir(SEC_STG_SECIMGDIR, 0777)) { 486 SLOGE("Failed to create %s (%s)", SEC_STG_SECIMGDIR, strerror(errno)); 487 return -1; 488 } 489 } else { 490 SLOGE("Failed to access %s (%s)", SEC_STG_SECIMGDIR, strerror(errno)); 491 return -1; 492 } 493 } else { 494 struct stat sbuf; 495 496 if (stat(SEC_STG_SECIMGDIR, &sbuf)) { 497 SLOGE("Failed to stat %s (%s)", SEC_STG_SECIMGDIR, strerror(errno)); 498 return -1; 499 } 500 if (!S_ISDIR(sbuf.st_mode)) { 501 SLOGE("%s is not a directory", SEC_STG_SECIMGDIR); 502 errno = ENOTDIR; 503 return -1; 504 } 505 } 506 507 /* 508 * Bind mount /mnt/secure/staging/android_secure -> /mnt/secure/asec so we'll 509 * have a root only accessable mountpoint for it. 510 */ 511 if (mount(SEC_STG_SECIMGDIR, SEC_ASECDIR_EXT, "", MS_BIND, NULL)) { 512 SLOGE("Failed to bind mount points %s -> %s (%s)", 513 SEC_STG_SECIMGDIR, SEC_ASECDIR_EXT, strerror(errno)); 514 return -1; 515 } 516 517 /* 518 * Mount a read-only, zero-sized tmpfs on <mountpoint>/android_secure to 519 * obscure the underlying directory from everybody - sneaky eh? ;) 520 */ 521 if (mount("tmpfs", SEC_STG_SECIMGDIR, "tmpfs", MS_RDONLY, "size=0,mode=000,uid=0,gid=0")) { 522 SLOGE("Failed to obscure %s (%s)", SEC_STG_SECIMGDIR, strerror(errno)); 523 umount("/mnt/asec_secure"); 524 return -1; 525 } 526 527 return 0; 528 } 529 530 int Volume::doMoveMount(const char *src, const char *dst, bool force) { 531 unsigned int flags = MS_MOVE; 532 int retries = 5; 533 534 while(retries--) { 535 if (!mount(src, dst, "", flags, NULL)) { 536 if (mDebug) { 537 SLOGD("Moved mount %s -> %s sucessfully", src, dst); 538 } 539 return 0; 540 } else if (errno != EBUSY) { 541 SLOGE("Failed to move mount %s -> %s (%s)", src, dst, strerror(errno)); 542 return -1; 543 } 544 int action = 0; 545 546 if (force) { 547 if (retries == 1) { 548 action = 2; // SIGKILL 549 } else if (retries == 2) { 550 action = 1; // SIGHUP 551 } 552 } 553 SLOGW("Failed to move %s -> %s (%s, retries %d, action %d)", 554 src, dst, strerror(errno), retries, action); 555 Process::killProcessesWithOpenFiles(src, action); 556 usleep(1000*250); 557 } 558 559 errno = EBUSY; 560 SLOGE("Giving up on move %s -> %s (%s)", src, dst, strerror(errno)); 561 return -1; 562 } 563 564 int Volume::doUnmount(const char *path, bool force) { 565 int retries = 10; 566 567 if (mDebug) { 568 SLOGD("Unmounting {%s}, force = %d", path, force); 569 } 570 571 while (retries--) { 572 if (!umount(path) || errno == EINVAL || errno == ENOENT) { 573 SLOGI("%s sucessfully unmounted", path); 574 return 0; 575 } 576 577 int action = 0; 578 579 if (force) { 580 if (retries == 1) { 581 action = 2; // SIGKILL 582 } else if (retries == 2) { 583 action = 1; // SIGHUP 584 } 585 } 586 587 SLOGW("Failed to unmount %s (%s, retries %d, action %d)", 588 path, strerror(errno), retries, action); 589 590 Process::killProcessesWithOpenFiles(path, action); 591 usleep(1000*1000); 592 } 593 errno = EBUSY; 594 SLOGE("Giving up on unmount %s (%s)", path, strerror(errno)); 595 return -1; 596 } 597 598 int Volume::unmountVol(bool force, bool revert) { 599 int i, rc; 600 601 if (getState() != Volume::State_Mounted) { 602 SLOGE("Volume %s unmount request when not mounted", getLabel()); 603 errno = EINVAL; 604 return UNMOUNT_NOT_MOUNTED_ERR; 605 } 606 607 setState(Volume::State_Unmounting); 608 usleep(1000 * 1000); // Give the framework some time to react 609 610 /* 611 * Remove the bindmount we were using to keep a reference to 612 * the previously obscured directory. 613 */ 614 if (doUnmount(Volume::SEC_ASECDIR_EXT, force)) { 615 SLOGE("Failed to remove bindmount on %s (%s)", SEC_ASECDIR_EXT, strerror(errno)); 616 goto fail_remount_tmpfs; 617 } 618 619 /* 620 * Unmount the tmpfs which was obscuring the asec image directory 621 * from non root users 622 */ 623 char secure_dir[PATH_MAX]; 624 snprintf(secure_dir, PATH_MAX, "%s/.android_secure", getMountpoint()); 625 if (doUnmount(secure_dir, force)) { 626 SLOGE("Failed to unmount tmpfs on %s (%s)", secure_dir, strerror(errno)); 627 goto fail_republish; 628 } 629 630 /* 631 * Finally, unmount the actual block device from the staging dir 632 */ 633 if (doUnmount(getMountpoint(), force)) { 634 SLOGE("Failed to unmount %s (%s)", SEC_STGDIR, strerror(errno)); 635 goto fail_recreate_bindmount; 636 } 637 638 SLOGI("%s unmounted sucessfully", getMountpoint()); 639 640 /* If this is an encrypted volume, and we've been asked to undo 641 * the crypto mapping, then revert the dm-crypt mapping, and revert 642 * the device info to the original values. 643 */ 644 if (revert && isDecrypted()) { 645 cryptfs_revert_volume(getLabel()); 646 revertDeviceInfo(); 647 SLOGI("Encrypted volume %s reverted successfully", getMountpoint()); 648 } 649 650 setState(Volume::State_Idle); 651 mCurrentlyMountedKdev = -1; 652 return 0; 653 654 /* 655 * Failure handling - try to restore everything back the way it was 656 */ 657 fail_recreate_bindmount: 658 if (mount(SEC_STG_SECIMGDIR, SEC_ASECDIR_EXT, "", MS_BIND, NULL)) { 659 SLOGE("Failed to restore bindmount after failure! - Storage will appear offline!"); 660 goto out_nomedia; 661 } 662 fail_remount_tmpfs: 663 if (mount("tmpfs", SEC_STG_SECIMGDIR, "tmpfs", MS_RDONLY, "size=0,mode=0,uid=0,gid=0")) { 664 SLOGE("Failed to restore tmpfs after failure! - Storage will appear offline!"); 665 goto out_nomedia; 666 } 667 fail_republish: 668 if (doMoveMount(SEC_STGDIR, getMountpoint(), force)) { 669 SLOGE("Failed to republish mount after failure! - Storage will appear offline!"); 670 goto out_nomedia; 671 } 672 673 setState(Volume::State_Mounted); 674 return -1; 675 676 out_nomedia: 677 setState(Volume::State_NoMedia); 678 return -1; 679 } 680 int Volume::initializeMbr(const char *deviceNode) { 681 struct disk_info dinfo; 682 683 memset(&dinfo, 0, sizeof(dinfo)); 684 685 if (!(dinfo.part_lst = (struct part_info *) malloc(MAX_NUM_PARTS * sizeof(struct part_info)))) { 686 SLOGE("Failed to malloc prt_lst"); 687 return -1; 688 } 689 690 memset(dinfo.part_lst, 0, MAX_NUM_PARTS * sizeof(struct part_info)); 691 dinfo.device = strdup(deviceNode); 692 dinfo.scheme = PART_SCHEME_MBR; 693 dinfo.sect_size = 512; 694 dinfo.skip_lba = 2048; 695 dinfo.num_lba = 0; 696 dinfo.num_parts = 1; 697 698 struct part_info *pinfo = &dinfo.part_lst[0]; 699 700 pinfo->name = strdup("android_sdcard"); 701 pinfo->flags |= PART_ACTIVE_FLAG; 702 pinfo->type = PC_PART_TYPE_FAT32; 703 pinfo->len_kb = -1; 704 705 int rc = apply_disk_config(&dinfo, 0); 706 707 if (rc) { 708 SLOGE("Failed to apply disk configuration (%d)", rc); 709 goto out; 710 } 711 712 out: 713 free(pinfo->name); 714 free(dinfo.device); 715 free(dinfo.part_lst); 716 717 return rc; 718 } 719