VolumeManager.cpp 52.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

17
#include <dirent.h>
18
#include <errno.h>
19
#include <fcntl.h>
20
#include <fts.h>
21 22 23 24 25 26
#include <mntent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
27 28
#include <sys/stat.h>
#include <sys/types.h>
29
#include <unistd.h>
30

31
#include <linux/kdev_t.h>
32 33 34

#define LOG_TAG "Vold"

35 36
#include <openssl/md5.h>

Jeff Sharkey's avatar
Jeff Sharkey committed
37
#include <cutils/fs.h>
38 39
#include <cutils/log.h>

40 41
#include <selinux/android.h>

42 43
#include <sysutils/NetlinkEvent.h>

44 45
#include <private/android_filesystem_config.h>

46
#include "VolumeManager.h"
47
#include "DirectVolume.h"
48
#include "ResponseCode.h"
49
#include "Loop.h"
50
#include "Ext4.h"
51
#include "Fat.h"
52
#include "Devmapper.h"
53
#include "Process.h"
54
#include "Asec.h"
55
#include "cryptfs.h"
56

57 58
#define MASS_STORAGE_FILE_PATH  "/sys/class/android_usb/android0/f_mass_storage/lun/file"

59 60 61
#define ROUND_UP_POWER_OF_2(number, po2) (((!!(number & ((1U << po2) - 1))) << po2)\
                                         + (number & (~((1U << po2) - 1))))

62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
/* writes superblock at end of file or device given by name */
static int writeSuperBlock(const char* name, struct asec_superblock *sb, unsigned int numImgSectors) {
    int sbfd = open(name, O_RDWR);
    if (sbfd < 0) {
        SLOGE("Failed to open %s for superblock write (%s)", name, strerror(errno));
        return -1;
    }

    if (lseek(sbfd, (numImgSectors * 512), SEEK_SET) < 0) {
        SLOGE("Failed to lseek for superblock (%s)", strerror(errno));
        close(sbfd);
        return -1;
    }

    if (write(sbfd, sb, sizeof(struct asec_superblock)) != sizeof(struct asec_superblock)) {
        SLOGE("Failed to write superblock (%s)", strerror(errno));
        close(sbfd);
        return -1;
    }
    close(sbfd);
    return 0;
}

static int adjustSectorNumExt4(unsigned numSectors) {
86 87 88 89 90 91
    // Ext4 started to reserve 2% or 4096 clusters, whichever is smaller for
    // preventing costly operations or unexpected ENOSPC error.
    // Ext4::format() uses default block size without clustering.
    unsigned clusterSectors = 4096 / 512;
    unsigned reservedSectors = (numSectors * 2)/100 + (numSectors % 50 > 0);
    numSectors += reservedSectors > (4096 * clusterSectors) ? (4096 * clusterSectors) : reservedSectors;
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
    return ROUND_UP_POWER_OF_2(numSectors, 3);
}

static int adjustSectorNumFAT(unsigned numSectors) {
    /*
    * Add some headroom
    */
    unsigned fatSize = (((numSectors * 4) / 512) + 1) * 2;
    numSectors += fatSize + 2;
    /*
    * FAT is aligned to 32 kb with 512b sectors.
    */
    return ROUND_UP_POWER_OF_2(numSectors, 6);
}

static int setupLoopDevice(char* buffer, size_t len, const char* asecFileName, const char* idHash, bool debug) {
    if (Loop::lookupActive(idHash, buffer, len)) {
        if (Loop::create(idHash, asecFileName, buffer, len)) {
            SLOGE("ASEC loop device creation failed for %s (%s)", asecFileName, strerror(errno));
            return -1;
        }
        if (debug) {
            SLOGD("New loop device created at %s", buffer);
        }
    } else {
        if (debug) {
            SLOGD("Found active loopback for %s at %s", asecFileName, buffer);
        }
    }
    return 0;
}

static int setupDevMapperDevice(char* buffer, size_t len, const char* loopDevice, const char* asecFileName, const char* key, const char* idHash , int numImgSectors, bool* createdDMDevice, bool debug) {
    if (strcmp(key, "none")) {
        if (Devmapper::lookupActive(idHash, buffer, len)) {
            if (Devmapper::create(idHash, loopDevice, key, numImgSectors,
                                  buffer, len)) {
                SLOGE("ASEC device mapping failed for %s (%s)", asecFileName, strerror(errno));
                return -1;
            }
            if (debug) {
                SLOGD("New devmapper instance created at %s", buffer);
            }
        } else {
            if (debug) {
                SLOGD("Found active devmapper for %s at %s", asecFileName, buffer);
            }
        }
        *createdDMDevice = true;
    } else {
        strcpy(buffer, loopDevice);
        *createdDMDevice = false;
    }
    return 0;
}

static void waitForDevMapper(const char *dmDevice) {
    /*
     * Wait for the device mapper node to be created. Sometimes it takes a
     * while. Wait for up to 1 second. We could also inspect incoming uevents,
     * but that would take more effort.
     */
    int tries = 25;
    while (tries--) {
        if (!access(dmDevice, F_OK) || errno != ENOENT) {
            break;
        }
        usleep(40 * 1000);
    }
}

163 164 165 166 167 168 169 170 171
VolumeManager *VolumeManager::sInstance = NULL;

VolumeManager *VolumeManager::Instance() {
    if (!sInstance)
        sInstance = new VolumeManager();
    return sInstance;
}

VolumeManager::VolumeManager() {
San Mehat's avatar
San Mehat committed
172
    mDebug = false;
173
    mVolumes = new VolumeCollection();
174
    mActiveContainers = new AsecIdCollection();
175
    mBroadcaster = NULL;
176 177 178 179
    mUmsSharingCount = 0;
    mSavedDirtyRatio = -1;
    // set dirty ratio to 0 when UMS is active
    mUmsDirtyRatio = 0;
180
    mVolManagerDisabled = 0;
181 182 183
}

VolumeManager::~VolumeManager() {
184 185
    delete mVolumes;
    delete mActiveContainers;
186 187
}

San Mehat's avatar
San Mehat committed
188
char *VolumeManager::asecHash(const char *id, char *buffer, size_t len) {
189 190
    static const char* digits = "0123456789abcdef";

191
    unsigned char sig[MD5_DIGEST_LENGTH];
San Mehat's avatar
San Mehat committed
192

193 194 195 196 197 198 199 200 201
    if (buffer == NULL) {
        SLOGE("Destination buffer is NULL");
        errno = ESPIPE;
        return NULL;
    } else if (id == NULL) {
        SLOGE("Source buffer is NULL");
        errno = ESPIPE;
        return NULL;
    } else if (len < MD5_ASCII_LENGTH_PLUS_NULL) {
Colin Cross's avatar
Colin Cross committed
202
        SLOGE("Target hash buffer size < %d bytes (%zu)",
203
                MD5_ASCII_LENGTH_PLUS_NULL, len);
San Mehat's avatar
San Mehat committed
204 205 206
        errno = ESPIPE;
        return NULL;
    }
207 208

    MD5(reinterpret_cast<const unsigned char*>(id), strlen(id), sig);
San Mehat's avatar
San Mehat committed
209

210
    char *p = buffer;
211
    for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
212 213
        *p++ = digits[sig[i] >> 4];
        *p++ = digits[sig[i] & 0x0F];
San Mehat's avatar
San Mehat committed
214
    }
215
    *p = '\0';
San Mehat's avatar
San Mehat committed
216 217 218 219 220 221 222 223 224 225 226 227

    return buffer;
}

void VolumeManager::setDebug(bool enable) {
    mDebug = enable;
    VolumeCollection::iterator it;
    for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
        (*it)->setDebug(enable);
    }
}

228 229 230 231 232 233 234 235 236 237 238 239 240
int VolumeManager::start() {
    return 0;
}

int VolumeManager::stop() {
    return 0;
}

int VolumeManager::addVolume(Volume *v) {
    mVolumes->push_back(v);
    return 0;
}

241
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
242
#ifdef NETLINK_DEBUG
243
    const char *devpath = evt->findParam("DEVPATH");
244
#endif
245

246
    /* Lookup a volume to handle this device */
247 248 249
    VolumeCollection::iterator it;
    bool hit = false;
    for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
250
        if (!(*it)->handleBlockEvent(evt)) {
251
#ifdef NETLINK_DEBUG
San Mehat's avatar
San Mehat committed
252
            SLOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel());
253
#endif
254 255 256 257 258 259
            hit = true;
            break;
        }
    }

    if (!hit) {
260
#ifdef NETLINK_DEBUG
San Mehat's avatar
San Mehat committed
261
        SLOGW("No volumes handled block event for '%s'", devpath);
262
#endif
263 264 265
    }
}

266
int VolumeManager::listVolumes(SocketClient *cli, bool broadcast) {
267
    VolumeCollection::iterator i;
268
    char msg[256];
269 270 271 272

    for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
        char *buffer;
        asprintf(&buffer, "%s %s %d",
273
                 (*i)->getLabel(), (*i)->getFuseMountpoint(),
274
                 (*i)->getState());
275
        cli->sendMsg(ResponseCode::VolumeListResult, buffer, false);
276
        free(buffer);
277 278 279 280 281 282 283 284 285 286 287 288 289 290
        if (broadcast) {
            if((*i)->getUuid()) {
                snprintf(msg, sizeof(msg), "%s %s \"%s\"", (*i)->getLabel(),
                    (*i)->getFuseMountpoint(), (*i)->getUuid());
                mBroadcaster->sendBroadcast(ResponseCode::VolumeUuidChange,
                    msg, false);
            }
            if((*i)->getUserLabel()) {
                snprintf(msg, sizeof(msg), "%s %s \"%s\"", (*i)->getLabel(),
                    (*i)->getFuseMountpoint(), (*i)->getUserLabel());
                mBroadcaster->sendBroadcast(ResponseCode::VolumeUserLabelChange,
                    msg, false);
            }
        }
291
    }
292
    cli->sendMsg(ResponseCode::CommandOkay, "Volumes listed.", false);
293 294
    return 0;
}
295

296
int VolumeManager::formatVolume(const char *label, bool wipe) {
297 298 299 300 301 302 303
    Volume *v = lookupVolume(label);

    if (!v) {
        errno = ENOENT;
        return -1;
    }

304 305 306 307 308
    if (mVolManagerDisabled) {
        errno = EBUSY;
        return -1;
    }

309
    return v->formatVol(wipe);
310 311
}

Kenny Root's avatar
Kenny Root committed
312 313 314 315 316 317 318 319
int VolumeManager::getObbMountPath(const char *sourceFile, char *mountPath, int mountPathLen) {
    char idHash[33];
    if (!asecHash(sourceFile, idHash, sizeof(idHash))) {
        SLOGE("Hash of '%s' failed (%s)", sourceFile, strerror(errno));
        return -1;
    }

    memset(mountPath, 0, mountPathLen);
320 321 322 323 324
    int written = snprintf(mountPath, mountPathLen, "%s/%s", Volume::LOOPDIR, idHash);
    if ((written < 0) || (written >= mountPathLen)) {
        errno = EINVAL;
        return -1;
    }
Kenny Root's avatar
Kenny Root committed
325 326 327 328 329 330 331 332 333

    if (access(mountPath, F_OK)) {
        errno = ENOENT;
        return -1;
    }

    return 0;
}

334
int VolumeManager::getAsecMountPath(const char *id, char *buffer, int maxlen) {
335
    char asecFileName[255];
336

Nick Kralevich's avatar
Nick Kralevich committed
337 338 339 340 341 342
    if (!isLegalAsecId(id)) {
        SLOGE("getAsecMountPath: Invalid asec id \"%s\"", id);
        errno = EINVAL;
        return -1;
    }

343 344 345 346
    if (findAsec(id, asecFileName, sizeof(asecFileName))) {
        SLOGE("Couldn't find ASEC %s", id);
        return -1;
    }
347 348 349 350 351 352

    memset(buffer, 0, maxlen);
    if (access(asecFileName, F_OK)) {
        errno = ENOENT;
        return -1;
    }
353

354 355 356 357 358 359 360
    int written = snprintf(buffer, maxlen, "%s/%s", Volume::ASECDIR, id);
    if ((written < 0) || (written >= maxlen)) {
        SLOGE("getAsecMountPath failed for %s: couldn't construct path in buffer", id);
        errno = EINVAL;
        return -1;
    }

361 362 363
    return 0;
}

364 365
int VolumeManager::getAsecFilesystemPath(const char *id, char *buffer, int maxlen) {
    char asecFileName[255];
366

Nick Kralevich's avatar
Nick Kralevich committed
367 368 369 370 371 372
    if (!isLegalAsecId(id)) {
        SLOGE("getAsecFilesystemPath: Invalid asec id \"%s\"", id);
        errno = EINVAL;
        return -1;
    }

373 374 375 376
    if (findAsec(id, asecFileName, sizeof(asecFileName))) {
        SLOGE("Couldn't find ASEC %s", id);
        return -1;
    }
377 378 379 380 381 382 383

    memset(buffer, 0, maxlen);
    if (access(asecFileName, F_OK)) {
        errno = ENOENT;
        return -1;
    }

384 385 386 387 388 389
    int written = snprintf(buffer, maxlen, "%s", asecFileName);
    if ((written < 0) || (written >= maxlen)) {
        errno = EINVAL;
        return -1;
    }

390 391 392
    return 0;
}

393 394
int VolumeManager::createAsec(const char *id, unsigned int numSectors, const char *fstype,
        const char *key, const int ownerUid, bool isExternal) {
395 396 397
    struct asec_superblock sb;
    memset(&sb, 0, sizeof(sb));

Nick Kralevich's avatar
Nick Kralevich committed
398 399 400 401 402 403
    if (!isLegalAsecId(id)) {
        SLOGE("createAsec: Invalid asec id \"%s\"", id);
        errno = EINVAL;
        return -1;
    }

404 405 406 407 408 409 410 411 412 413 414 415 416
    const bool wantFilesystem = strcmp(fstype, "none");
    bool usingExt4 = false;
    if (wantFilesystem) {
        usingExt4 = !strcmp(fstype, "ext4");
        if (usingExt4) {
            sb.c_opts |= ASEC_SB_C_OPTS_EXT4;
        } else if (strcmp(fstype, "fat")) {
            SLOGE("Invalid filesystem type %s", fstype);
            errno = EINVAL;
            return -1;
        }
    }

417 418
    sb.magic = ASEC_SB_MAGIC;
    sb.ver = ASEC_SB_VER;
419

420
    if (numSectors < ((1024*1024)/512)) {
San Mehat's avatar
San Mehat committed
421
        SLOGE("Invalid container size specified (%d sectors)", numSectors);
422 423 424 425
        errno = EINVAL;
        return -1;
    }

426
    if (lookupVolume(id)) {
San Mehat's avatar
San Mehat committed
427
        SLOGE("ASEC id '%s' currently exists", id);
428 429 430 431 432
        errno = EADDRINUSE;
        return -1;
    }

    char asecFileName[255];
433 434 435 436 437 438 439 440 441 442

    if (!findAsec(id, asecFileName, sizeof(asecFileName))) {
        SLOGE("ASEC file '%s' currently exists - destroy it first! (%s)",
                asecFileName, strerror(errno));
        errno = EADDRINUSE;
        return -1;
    }

    const char *asecDir = isExternal ? Volume::SEC_ASECDIR_EXT : Volume::SEC_ASECDIR_INT;

443 444 445 446 447
    int written = snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", asecDir, id);
    if ((written < 0) || (size_t(written) >= sizeof(asecFileName))) {
        errno = EINVAL;
        return -1;
    }
448 449

    if (!access(asecFileName, F_OK)) {
San Mehat's avatar
San Mehat committed
450
        SLOGE("ASEC file '%s' currently exists - destroy it first! (%s)",
451
                asecFileName, strerror(errno));
452 453 454 455
        errno = EADDRINUSE;
        return -1;
    }

456
    unsigned numImgSectors;
457
    if (usingExt4)
458
        numImgSectors = adjustSectorNumExt4(numSectors);
459
    else
460
        numImgSectors = adjustSectorNumFAT(numSectors);
461 462 463

    // Add +1 for our superblock which is at the end
    if (Loop::createImageFile(asecFileName, numImgSectors + 1)) {
San Mehat's avatar
San Mehat committed
464
        SLOGE("ASEC image file creation failed (%s)", strerror(errno));
465 466 467
        return -1;
    }

San Mehat's avatar
San Mehat committed
468 469
    char idHash[33];
    if (!asecHash(id, idHash, sizeof(idHash))) {
San Mehat's avatar
San Mehat committed
470
        SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
San Mehat's avatar
San Mehat committed
471 472 473 474
        unlink(asecFileName);
        return -1;
    }

475
    char loopDevice[255];
San Mehat's avatar
San Mehat committed
476
    if (Loop::create(idHash, asecFileName, loopDevice, sizeof(loopDevice))) {
San Mehat's avatar
San Mehat committed
477
        SLOGE("ASEC loop device creation failed (%s)", strerror(errno));
478 479 480 481
        unlink(asecFileName);
        return -1;
    }

482 483
    char dmDevice[255];
    bool cleanupDm = false;
484

485
    if (strcmp(key, "none")) {
486 487
        // XXX: This is all we support for now
        sb.c_cipher = ASEC_SB_C_CIPHER_TWOFISH;
San Mehat's avatar
San Mehat committed
488
        if (Devmapper::create(idHash, loopDevice, key, numImgSectors, dmDevice,
489
                             sizeof(dmDevice))) {
San Mehat's avatar
San Mehat committed
490
            SLOGE("ASEC device mapping failed (%s)", strerror(errno));
491 492 493 494 495 496
            Loop::destroyByDevice(loopDevice);
            unlink(asecFileName);
            return -1;
        }
        cleanupDm = true;
    } else {
497
        sb.c_cipher = ASEC_SB_C_CIPHER_NONE;
498 499 500
        strcpy(dmDevice, loopDevice);
    }

501 502 503
    /*
     * Drop down the superblock at the end of the file
     */
504
    if (writeSuperBlock(loopDevice, &sb, numImgSectors)) {
505
        if (cleanupDm) {
San Mehat's avatar
San Mehat committed
506
            Devmapper::destroy(idHash);
507 508 509 510 511 512
        }
        Loop::destroyByDevice(loopDevice);
        unlink(asecFileName);
        return -1;
    }

513 514
    if (wantFilesystem) {
        int formatStatus;
515 516
        char mountPoint[255];

517 518 519 520 521 522 523 524 525 526
        int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
        if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
            SLOGE("ASEC fs format failed: couldn't construct mountPoint");
            if (cleanupDm) {
                Devmapper::destroy(idHash);
            }
            Loop::destroyByDevice(loopDevice);
            unlink(asecFileName);
            return -1;
        }
527

528
        if (usingExt4) {
529
            formatStatus = Ext4::format(dmDevice, numImgSectors, mountPoint);
530
        } else {
531
            formatStatus = Fat::format(dmDevice, numImgSectors, 0);
532
        }
533

534 535
        if (formatStatus < 0) {
            SLOGE("ASEC fs format failed (%s)", strerror(errno));
536
            if (cleanupDm) {
San Mehat's avatar
San Mehat committed
537
                Devmapper::destroy(idHash);
538
            }
539 540 541 542
            Loop::destroyByDevice(loopDevice);
            unlink(asecFileName);
            return -1;
        }
543 544

        if (mkdir(mountPoint, 0000)) {
545
            if (errno != EEXIST) {
San Mehat's avatar
San Mehat committed
546
                SLOGE("Mountpoint creation failed (%s)", strerror(errno));
547
                if (cleanupDm) {
San Mehat's avatar
San Mehat committed
548
                    Devmapper::destroy(idHash);
549 550 551 552 553 554
                }
                Loop::destroyByDevice(loopDevice);
                unlink(asecFileName);
                return -1;
            }
        }
555

556 557 558 559 560 561 562 563 564
        int mountStatus;
        if (usingExt4) {
            mountStatus = Ext4::doMount(dmDevice, mountPoint, false, false, false);
        } else {
            mountStatus = Fat::doMount(dmDevice, mountPoint, false, false, false, ownerUid, 0, 0000,
                    false);
        }

        if (mountStatus) {
San Mehat's avatar
San Mehat committed
565
            SLOGE("ASEC FAT mount failed (%s)", strerror(errno));
566
            if (cleanupDm) {
San Mehat's avatar
San Mehat committed
567
                Devmapper::destroy(idHash);
568 569 570 571
            }
            Loop::destroyByDevice(loopDevice);
            unlink(asecFileName);
            return -1;
572
        }
573 574 575 576 577 578 579 580 581 582 583

        if (usingExt4) {
            int dirfd = open(mountPoint, O_DIRECTORY);
            if (dirfd >= 0) {
                if (fchown(dirfd, ownerUid, AID_SYSTEM)
                        || fchmod(dirfd, S_IRUSR | S_IWUSR | S_IXUSR | S_ISGID | S_IRGRP | S_IXGRP)) {
                    SLOGI("Cannot chown/chmod new ASEC mount point %s", mountPoint);
                }
                close(dirfd);
            }
        }
584
    } else {
San Mehat's avatar
San Mehat committed
585
        SLOGI("Created raw secure container %s (no filesystem)", id);
586
    }
587

Kenny Root's avatar
Kenny Root committed
588
    mActiveContainers->push_back(new ContainerData(strdup(id), ASEC));
589 590 591
    return 0;
}

592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
int VolumeManager::resizeAsec(const char *id, unsigned numSectors, const char *key) {
    char asecFileName[255];
    char mountPoint[255];
    bool cleanupDm = false;

    if (!isLegalAsecId(id)) {
        SLOGE("resizeAsec: Invalid asec id \"%s\"", id);
        errno = EINVAL;
        return -1;
    }

    if (findAsec(id, asecFileName, sizeof(asecFileName))) {
        SLOGE("Couldn't find ASEC %s", id);
        return -1;
    }

    int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
    if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
       SLOGE("ASEC resize failed for %s: couldn't construct mountpoint", id);
       return -1;
    }

    if (isMountpointMounted(mountPoint)) {
       SLOGE("ASEC %s mounted. Unmount before resizing", id);
       errno = EBUSY;
       return -1;
    }

    struct asec_superblock sb;
    int fd;
    unsigned int oldNumSec = 0;

    if ((fd = open(asecFileName, O_RDONLY)) < 0) {
        SLOGE("Failed to open ASEC file (%s)", strerror(errno));
        return -1;
    }

    struct stat info;
    if (fstat(fd, &info) < 0) {
        SLOGE("Failed to get file size (%s)", strerror(errno));
        close(fd);
        return -1;
    }

    oldNumSec = info.st_size / 512;

    unsigned numImgSectors;
    if (sb.c_opts & ASEC_SB_C_OPTS_EXT4)
        numImgSectors = adjustSectorNumExt4(numSectors);
    else
        numImgSectors = adjustSectorNumFAT(numSectors);
    /*
     *  add one block for the superblock
     */
    SLOGD("Resizing from %d sectors to %d sectors", oldNumSec, numImgSectors + 1);
647 648 649 650
    if (oldNumSec == numImgSectors + 1) {
        SLOGW("Size unchanged; ignoring resize request");
        return 0;
    } else if (oldNumSec > numImgSectors + 1) {
651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
        SLOGE("Only growing is currently supported.");
        close(fd);
        return -1;
    }

    /*
     * Try to read superblock.
     */
    memset(&sb, 0, sizeof(struct asec_superblock));
    if (lseek(fd, ((oldNumSec - 1) * 512), SEEK_SET) < 0) {
        SLOGE("lseek failed (%s)", strerror(errno));
        close(fd);
        return -1;
    }
    if (read(fd, &sb, sizeof(struct asec_superblock)) != sizeof(struct asec_superblock)) {
        SLOGE("superblock read failed (%s)", strerror(errno));
        close(fd);
        return -1;
    }
    close(fd);

    if (mDebug) {
        SLOGD("Container sb magic/ver (%.8x/%.2x)", sb.magic, sb.ver);
    }
    if (sb.magic != ASEC_SB_MAGIC || sb.ver != ASEC_SB_VER) {
        SLOGE("Bad container magic/version (%.8x/%.2x)", sb.magic, sb.ver);
        errno = EMEDIUMTYPE;
        return -1;
    }

    if (!(sb.c_opts & ASEC_SB_C_OPTS_EXT4)) {
        SLOGE("Only ext4 partitions are supported for resize");
        errno = EINVAL;
        return -1;
    }

    if (Loop::resizeImageFile(asecFileName, numImgSectors + 1)) {
        SLOGE("Resize of ASEC image file failed. Could not resize %s", id);
        return -1;
    }

    /*
     * Drop down a copy of the superblock at the end of the file
     */
    if (writeSuperBlock(asecFileName, &sb, numImgSectors))
        goto fail;

    char idHash[33];
    if (!asecHash(id, idHash, sizeof(idHash))) {
        SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
        goto fail;
    }

    char loopDevice[255];
    if (setupLoopDevice(loopDevice, sizeof(loopDevice), asecFileName, idHash, mDebug))
        goto fail;

    char dmDevice[255];

    if (setupDevMapperDevice(dmDevice, sizeof(dmDevice), loopDevice, asecFileName, key, idHash, numImgSectors, &cleanupDm, mDebug)) {
        Loop::destroyByDevice(loopDevice);
        goto fail;
    }

    /*
     * Wait for the device mapper node to be created.
     */
    waitForDevMapper(dmDevice);

    if (Ext4::resize(dmDevice, numImgSectors)) {
        SLOGE("Unable to resize %s (%s)", id, strerror(errno));
        if (cleanupDm) {
            Devmapper::destroy(idHash);
        }
        Loop::destroyByDevice(loopDevice);
        goto fail;
    }

    return 0;
fail:
    Loop::resizeImageFile(asecFileName, oldNumSec);
    return -1;
}

735 736 737 738 739
int VolumeManager::finalizeAsec(const char *id) {
    char asecFileName[255];
    char loopDevice[255];
    char mountPoint[255];

Nick Kralevich's avatar
Nick Kralevich committed
740 741 742 743 744 745
    if (!isLegalAsecId(id)) {
        SLOGE("finalizeAsec: Invalid asec id \"%s\"", id);
        errno = EINVAL;
        return -1;
    }

746 747 748 749
    if (findAsec(id, asecFileName, sizeof(asecFileName))) {
        SLOGE("Couldn't find ASEC %s", id);
        return -1;
    }
750

San Mehat's avatar
San Mehat committed
751 752
    char idHash[33];
    if (!asecHash(id, idHash, sizeof(idHash))) {
San Mehat's avatar
San Mehat committed
753
        SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
San Mehat's avatar
San Mehat committed
754 755 756 757
        return -1;
    }

    if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
San Mehat's avatar
San Mehat committed
758
        SLOGE("Unable to finalize %s (%s)", id, strerror(errno));
759 760 761
        return -1;
    }

762 763 764 765 766 767 768
    unsigned int nr_sec = 0;
    struct asec_superblock sb;

    if (Loop::lookupInfo(loopDevice, &sb, &nr_sec)) {
        return -1;
    }

769 770 771 772 773
    int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
    if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
        SLOGE("ASEC finalize failed: couldn't construct mountPoint");
        return -1;
    }
774 775 776 777 778 779 780 781 782

    int result = 0;
    if (sb.c_opts & ASEC_SB_C_OPTS_EXT4) {
        result = Ext4::doMount(loopDevice, mountPoint, true, true, true);
    } else {
        result = Fat::doMount(loopDevice, mountPoint, true, true, true, 0, 0, 0227, false);
    }

    if (result) {
San Mehat's avatar
San Mehat committed
783
        SLOGE("ASEC finalize mount failed (%s)", strerror(errno));
784 785 786
        return -1;
    }

San Mehat's avatar
San Mehat committed
787
    if (mDebug) {
San Mehat's avatar
San Mehat committed
788
        SLOGD("ASEC %s finalized", id);
San Mehat's avatar
San Mehat committed
789
    }
790 791 792
    return 0;
}

793 794 795 796 797 798 799 800 801 802
int VolumeManager::fixupAsecPermissions(const char *id, gid_t gid, const char* filename) {
    char asecFileName[255];
    char loopDevice[255];
    char mountPoint[255];

    if (gid < AID_APP) {
        SLOGE("Group ID is not in application range");
        return -1;
    }

Nick Kralevich's avatar
Nick Kralevich committed
803 804 805 806 807 808
    if (!isLegalAsecId(id)) {
        SLOGE("fixupAsecPermissions: Invalid asec id \"%s\"", id);
        errno = EINVAL;
        return -1;
    }

809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831
    if (findAsec(id, asecFileName, sizeof(asecFileName))) {
        SLOGE("Couldn't find ASEC %s", id);
        return -1;
    }

    char idHash[33];
    if (!asecHash(id, idHash, sizeof(idHash))) {
        SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
        return -1;
    }

    if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
        SLOGE("Unable fix permissions during lookup on %s (%s)", id, strerror(errno));
        return -1;
    }

    unsigned int nr_sec = 0;
    struct asec_superblock sb;

    if (Loop::lookupInfo(loopDevice, &sb, &nr_sec)) {
        return -1;
    }

832 833 834 835 836
    int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
    if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
        SLOGE("Unable remount to fix permissions for %s: couldn't construct mountpoint", id);
        return -1;
    }
837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879

    int result = 0;
    if ((sb.c_opts & ASEC_SB_C_OPTS_EXT4) == 0) {
        return 0;
    }

    int ret = Ext4::doMount(loopDevice, mountPoint,
            false /* read-only */,
            true  /* remount */,
            false /* executable */);
    if (ret) {
        SLOGE("Unable remount to fix permissions for %s (%s)", id, strerror(errno));
        return -1;
    }

    char *paths[] = { mountPoint, NULL };

    FTS *fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL);
    if (fts) {
        // Traverse the entire hierarchy and chown to system UID.
        for (FTSENT *ftsent = fts_read(fts); ftsent != NULL; ftsent = fts_read(fts)) {
            // We don't care about the lost+found directory.
            if (!strcmp(ftsent->fts_name, "lost+found")) {
                continue;
            }

            /*
             * There can only be one file marked as private right now.
             * This should be more robust, but it satisfies the requirements
             * we have for right now.
             */
            const bool privateFile = !strcmp(ftsent->fts_name, filename);

            int fd = open(ftsent->fts_accpath, O_NOFOLLOW);
            if (fd < 0) {
                SLOGE("Couldn't open file %s: %s", ftsent->fts_accpath, strerror(errno));
                result = -1;
                continue;
            }

            result |= fchown(fd, AID_SYSTEM, privateFile? gid : AID_SYSTEM);

            if (ftsent->fts_info & FTS_D) {
880
                result |= fchmod(fd, 0755);
881
            } else if (ftsent->fts_info & FTS_F) {
882 883
                result |= fchmod(fd, privateFile ? 0640 : 0644);
            }
884

885
            if (selinux_android_restorecon(ftsent->fts_path, 0) < 0) {
886 887 888 889
                SLOGE("restorecon failed for %s: %s\n", ftsent->fts_path, strerror(errno));
                result |= -1;
            }

890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920
            close(fd);
        }
        fts_close(fts);

        // Finally make the directory readable by everyone.
        int dirfd = open(mountPoint, O_DIRECTORY);
        if (dirfd < 0 || fchmod(dirfd, 0755)) {
            SLOGE("Couldn't change owner of existing directory %s: %s", mountPoint, strerror(errno));
            result |= -1;
        }
        close(dirfd);
    } else {
        result |= -1;
    }

    result |= Ext4::doMount(loopDevice, mountPoint,
            true /* read-only */,
            true /* remount */,
            true /* execute */);

    if (result) {
        SLOGE("ASEC fix permissions failed (%s)", strerror(errno));
        return -1;
    }

    if (mDebug) {
        SLOGD("ASEC %s permissions fixed", id);
    }
    return 0;
}

921
int VolumeManager::renameAsec(const char *id1, const char *id2) {
922
    char asecFilename1[255];
923 924 925
    char *asecFilename2;
    char mountPoint[255];

926 927
    const char *dir;

Nick Kralevich's avatar
Nick Kralevich committed
928 929 930 931 932 933 934 935 936 937 938 939
    if (!isLegalAsecId(id1)) {
        SLOGE("renameAsec: Invalid asec id1 \"%s\"", id1);
        errno = EINVAL;
        return -1;
    }

    if (!isLegalAsecId(id2)) {
        SLOGE("renameAsec: Invalid asec id2 \"%s\"", id2);
        errno = EINVAL;
        return -1;
    }

940 941 942 943 944 945
    if (findAsec(id1, asecFilename1, sizeof(asecFilename1), &dir)) {
        SLOGE("Couldn't find ASEC %s", id1);
        return -1;
    }

    asprintf(&asecFilename2, "%s/%s.asec", dir, id2);
946

947 948 949 950 951 952
    int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id1);
    if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
        SLOGE("Rename failed: couldn't construct mountpoint");
        goto out_err;
    }

953
    if (isMountpointMounted(mountPoint)) {
San Mehat's avatar
San Mehat committed
954
        SLOGW("Rename attempt when src mounted");
955 956 957 958
        errno = EBUSY;
        goto out_err;
    }

959 960 961 962 963 964
    written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id2);
    if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
        SLOGE("Rename failed: couldn't construct mountpoint2");
        goto out_err;
    }

965
    if (isMountpointMounted(mountPoint)) {
San Mehat's avatar
San Mehat committed
966
        SLOGW("Rename attempt when dst mounted");
967 968 969 970
        errno = EBUSY;
        goto out_err;
    }

971
    if (!access(asecFilename2, F_OK)) {
San Mehat's avatar
San Mehat committed
972
        SLOGE("Rename attempt when dst exists");
973 974 975 976 977
        errno = EADDRINUSE;
        goto out_err;
    }

    if (rename(asecFilename1, asecFilename2)) {
San Mehat's avatar
San Mehat committed
978
        SLOGE("Rename of '%s' to '%s' failed (%s)", asecFilename1, asecFilename2, strerror(errno));
979 980 981 982 983 984 985 986 987 988 989
        goto out_err;
    }

    free(asecFilename2);
    return 0;

out_err:
    free(asecFilename2);
    return -1;
}

990 991
#define UNMOUNT_RETRIES 5
#define UNMOUNT_SLEEP_BETWEEN_RETRY_MS (1000 * 1000)
992
int VolumeManager::unmountAsec(const char *id, bool force) {
993 994 995
    char asecFileName[255];
    char mountPoint[255];

Nick Kralevich's avatar
Nick Kralevich committed
996 997 998 999 1000 1001
    if (!isLegalAsecId(id)) {
        SLOGE("unmountAsec: Invalid asec id \"%s\"", id);
        errno = EINVAL;
        return -1;
    }

1002 1003 1004 1005 1006
    if (findAsec(id, asecFileName, sizeof(asecFileName))) {
        SLOGE("Couldn't find ASEC %s", id);
        return -1;
    }

1007 1008 1009 1010 1011
    int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
    if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
        SLOGE("ASEC unmount failed for %s: couldn't construct mountpoint", id);
        return -1;
    }
1012

San Mehat's avatar
San Mehat committed
1013 1014
    char idHash[33];
    if (!asecHash(id, idHash, sizeof(idHash))) {
San Mehat's avatar
San Mehat committed
1015
        SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
San Mehat's avatar
San Mehat committed
1016 1017 1018
        return -1;
    }

1019 1020 1021
    return unmountLoopImage(id, idHash, asecFileName, mountPoint, force);
}

Kenny Root's avatar
Kenny Root committed
1022
int VolumeManager::unmountObb(const char *fileName, bool force) {
1023 1024 1025 1026 1027 1028 1029 1030
    char mountPoint[255];

    char idHash[33];
    if (!asecHash(fileName, idHash, sizeof(idHash))) {
        SLOGE("Hash of '%s' failed (%s)", fileName, strerror(errno));
        return -1;
    }

1031 1032 1033 1034 1035
    int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::LOOPDIR, idHash);
    if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
        SLOGE("OBB unmount failed for %s: couldn't construct mountpoint", fileName);
        return -1;
    }
1036 1037 1038 1039 1040 1041

    return unmountLoopImage(fileName, idHash, fileName, mountPoint, force);
}

int VolumeManager::unmountLoopImage(const char *id, const char *idHash,
        const char *fileName, const char *mountPoint, bool force) {
1042
    if (!isMountpointMounted(mountPoint)) {
1043
        SLOGE("Unmount request for %s when not mounted", id);
1044
        errno = ENOENT;
1045 1046
        return -1;
    }
1047

1048
    int i, rc;
1049
    for (i = 1; i <= UNMOUNT_RETRIES; i++) {
1050 1051 1052
        rc = umount(mountPoint);
        if (!rc) {
            break;
1053
        }
1054
        if (rc && (errno == EINVAL || errno == ENOENT)) {
1055
            SLOGI("Container %s unmounted OK", id);
1056 1057
            rc = 0;
            break;
1058
        }
1059
        SLOGW("%s unmount attempt %d failed (%s)",
1060 1061
              id, i, strerror(errno));

1062 1063 1064
        int action = 0; // default is to just complain

        if (force) {
1065
            if (i > (UNMOUNT_RETRIES - 2))
1066
                action = 2; // SIGKILL
1067
            else if (i > (UNMOUNT_RETRIES - 3))
1068 1069
                action = 1; // SIGHUP
        }
1070

1071
        Process::killProcessesWithOpenFiles(mountPoint, action);
1072
        usleep(UNMOUNT_SLEEP_BETWEEN_RETRY_MS);
1073 1074 1075
    }

    if (rc) {
1076
        errno = EBUSY;
San Mehat's avatar
San Mehat committed
1077
        SLOGE("Failed to unmount container %s (%s)", id, strerror(errno));
1078 1079 1080
        return -1;
    }

1081 1082 1083 1084 1085 1086 1087
    int retries = 10;

    while(retries--) {
        if (!rmdir(mountPoint)) {
            break;
        }

San Mehat's avatar
San Mehat committed
1088
        SLOGW("Failed to rmdir %s (%s)", mountPoint, strerror(errno));
1089
        usleep(UNMOUNT_SLEEP_BETWEEN_RETRY_MS);
1090 1091 1092
    }

    if (!retries) {
San Mehat's avatar
San Mehat committed
1093
        SLOGE("Timed out trying to rmdir %s (%s)", mountPoint, strerror(errno));
1094
    }
1095

1096 1097 1098 1099 1100 1101 1102 1103
    for (i=1; i <= UNMOUNT_RETRIES; i++) {
        if (Devmapper::destroy(idHash) && errno != ENXIO) {
            SLOGE("Failed to destroy devmapper instance (%s)", strerror(errno));
            usleep(UNMOUNT_SLEEP_BETWEEN_RETRY_MS);
            continue;
        } else {
          break;
        }
1104 1105 1106
    }

    char loopDevice[255];
San Mehat's avatar
San Mehat committed
1107
    if (!Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
1108
        Loop::destroyByDevice(loopDevice);
San Mehat's avatar
San Mehat committed
1109
    } else {
1110
        SLOGW("Failed to find loop device for {%s} (%s)", fileName, strerror(errno));
1111
    }
1112 1113 1114

    AsecIdCollection::iterator it;
    for (it = mActiveContainers->begin(); it != mActiveContainers->end(); ++it) {
Kenny Root's avatar
Kenny Root committed
1115 1116
        ContainerData* cd = *it;
        if (!strcmp(cd->id, id)) {
1117 1118 1119 1120 1121 1122
            free(*it);
            mActiveContainers->erase(it);
            break;
        }
    }
    if (it == mActiveContainers->end()) {
San Mehat's avatar
San Mehat committed
1123
        SLOGW("mActiveContainers is inconsistent!");
1124
    }
1125 1126 1127
    return 0;
}

1128
int VolumeManager::destroyAsec(const char *id, bool force) {
1129 1130 1131
    char asecFileName[255];
    char mountPoint[255];

Nick Kralevich's avatar
Nick Kralevich committed
1132 1133 1134 1135 1136 1137
    if (!isLegalAsecId(id)) {
        SLOGE("destroyAsec: Invalid asec id \"%s\"", id);
        errno = EINVAL;
        return -1;
    }

1138 1139 1140 1141 1142
    if (findAsec(id, asecFileName, sizeof(asecFileName))) {
        SLOGE("Couldn't find ASEC %s", id);
        return -1;
    }

1143 1144 1145 1146 1147
    int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
    if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
        SLOGE("ASEC destroy failed for %s: couldn't construct mountpoint", id);
        return -1;
    }
1148

1149
    if (isMountpointMounted(mountPoint)) {
San Mehat's avatar
San Mehat committed
1150
        if (mDebug) {
San Mehat's avatar
San Mehat committed
1151
            SLOGD("Unmounting container before destroy");
San Mehat's avatar
San Mehat committed
1152
        }
1153
        if (unmountAsec(id, force)) {
San Mehat's avatar
San Mehat committed
1154
            SLOGE("Failed to unmount asec %s for destroy (%s)", id, strerror(errno));
1155 1156 1157
            return -1;
        }
    }
1158

1159
    if (unlink(asecFileName)) {
San Mehat's avatar
San Mehat committed
1160
        SLOGE("Failed to unlink asec '%s' (%s)", asecFileName, strerror(errno));
1161 1162
        return -1;
    }
1163

San Mehat's avatar
San Mehat committed
1164
    if (mDebug) {
San Mehat's avatar
San Mehat committed
1165
        SLOGD("ASEC %s destroyed", id);
San Mehat's avatar
San Mehat committed
1166
    }
1167 1168 1169
    return 0;
}

Nick Kralevich's avatar
Nick Kralevich committed
1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201
/*
 * Legal ASEC ids consist of alphanumeric characters, '-',
 * '_', or '.'. ".." is not allowed. The first or last character
 * of the ASEC id cannot be '.' (dot).
 */
bool VolumeManager::isLegalAsecId(const char *id) const {
    size_t i;
    size_t len = strlen(id);

    if (len == 0) {
        return false;
    }
    if ((id[0] == '.') || (id[len - 1] == '.')) {
        return false;
    }

    for (i = 0; i < len; i++) {
        if (id[i] == '.') {
            // i=0 is guaranteed never to have a dot. See above.
            if (id[i-1] == '.') return false;
            continue;
        }
        if (id[i] == '_' || id[i] == '-') continue;
        if (id[i] >= 'a' && id[i] <= 'z') continue;
        if (id[i] >= 'A' && id[i] <= 'Z') continue;
        if (id[i] >= '0' && id[i] <= '9') continue;
        return false;
    }

    return true;
}

1202 1203 1204 1205
bool VolumeManager::isAsecInDirectory(const char *dir, const char *asecName) const {
    int dirfd = open(dir, O_DIRECTORY);
    if (dirfd < 0) {
        SLOGE("Couldn't open internal ASEC dir (%s)", strerror(errno));
1206
        return false;
1207 1208
    }

1209 1210 1211
    struct stat sb;
    bool ret = (fstatat(dirfd, asecName, &sb, AT_SYMLINK_NOFOLLOW) == 0)
        && S_ISREG(sb.st_mode);
1212 1213 1214 1215 1216 1217 1218 1219 1220 1221

    close(dirfd);

    return ret;
}

int VolumeManager::findAsec(const char *id, char *asecPath, size_t asecPathLen,
        const char **directory) const {
    char *asecName;

Nick Kralevich's avatar
Nick Kralevich committed
1222 1223 1224 1225 1226 1227
    if (!isLegalAsecId(id)) {
        SLOGE("findAsec: Invalid asec id \"%s\"", id);
        errno = EINVAL;
        return -1;
    }

1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248
    if (asprintf(&asecName, "%s.asec", id) < 0) {
        SLOGE("Couldn't allocate string to write ASEC name");
        return -1;
    }

    const char *dir;
    if (isAsecInDirectory(Volume::SEC_ASECDIR_INT, asecName)) {
        dir = Volume::SEC_ASECDIR_INT;
    } else if (isAsecInDirectory(Volume::SEC_ASECDIR_EXT, asecName)) {
        dir = Volume::SEC_ASECDIR_EXT;
    } else {
        free(asecName);
        return -1;
    }

    if (directory != NULL) {
        *directory = dir;
    }

    if (asecPath != NULL) {
        int written = snprintf(asecPath, asecPathLen, "%s/%s", dir, asecName);
1249 1250
        if ((written < 0) || (size_t(written) >= asecPathLen)) {
            SLOGE("findAsec failed for %s: couldn't construct ASEC path", id);
1251 1252 1253 1254 1255 1256 1257 1258 1259
            free(asecName);
            return -1;
        }
    }

    free(asecName);
    return 0;
}

1260
int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid, bool readOnly) {
1261 1262 1263
    char asecFileName[255];
    char mountPoint[255];

Nick Kralevich's avatar
Nick Kralevich committed
1264 1265 1266 1267 1268 1269
    if (!isLegalAsecId(id)) {
        SLOGE("mountAsec: Invalid asec id \"%s\"", id);
        errno = EINVAL;
        return -1;
    }

1270 1271 1272 1273 1274
    if (findAsec(id, asecFileName, sizeof(asecFileName))) {
        SLOGE("Couldn't find ASEC %s", id);
        return -1;
    }

1275 1276
    int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
    if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
Colin Cross's avatar
Colin Cross committed
1277
        SLOGE("ASEC mount failed for %s: couldn't construct mountpoint", id);
1278 1279
        return -1;
    }
1280 1281

    if (isMountpointMounted(mountPoint)) {
San Mehat's avatar
San Mehat committed
1282
        SLOGE("ASEC %s already mounted", id);
1283 1284 1285 1286
        errno = EBUSY;
        return -1;
    }

San Mehat's avatar
San Mehat committed
1287 1288
    char idHash[33];
    if (!asecHash(id, idHash, sizeof(idHash))) {
San Mehat's avatar
San Mehat committed
1289
        SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
San Mehat's avatar
San Mehat committed
1290 1291
        return -1;
    }
1292

1293
    char loopDevice[255];
1294 1295
    if (setupLoopDevice(loopDevice, sizeof(loopDevice), asecFileName, idHash, mDebug))
        return -1;
1296 1297 1298

    char dmDevice[255];
    bool cleanupDm = false;
1299

1300 1301
    unsigned int nr_sec = 0;
    struct asec_superblock sb;
1302 1303

    if (Loop::lookupInfo(loopDevice, &sb, &nr_sec)) {
1304 1305 1306
        return -1;
    }

San Mehat's avatar
San Mehat committed
1307
    if (mDebug) {
San Mehat's avatar
San Mehat committed
1308
        SLOGD("Container sb magic/ver (%.8x/%.2x)", sb.magic, sb.ver);
San Mehat's avatar
San Mehat committed
1309
    }
1310
    if (sb.magic != ASEC_SB_MAGIC || sb.ver != ASEC_SB_VER) {
San Mehat's avatar
San Mehat committed
1311
        SLOGE("Bad container magic/version (%.8x/%.2x)", sb.magic, sb.ver);
1312 1313 1314 1315 1316 1317
        Loop::destroyByDevice(loopDevice);
        errno = EMEDIUMTYPE;
        return -1;
    }
    nr_sec--; // We don't want the devmapping to extend onto our superblock

1318 1319 1320
    if (setupDevMapperDevice(dmDevice, sizeof(dmDevice), loopDevice, asecFileName, key, idHash , nr_sec, &cleanupDm, mDebug)) {
        Loop::destroyByDevice(loopDevice);
        return -1;
1321 1322
    }

1323
    if (mkdir(mountPoint, 0000)) {
1324
        if (errno != EEXIST) {
San Mehat's avatar
San Mehat committed
1325
            SLOGE("Mountpoint creation failed (%s)", strerror(errno));
1326
            if (cleanupDm) {
San Mehat's avatar
San Mehat committed
1327
                Devmapper::destroy(idHash);
1328 1329 1330 1331
            }
            Loop::destroyByDevice(loopDevice);
            return -1;
        }
1332 1333
    }

1334
    /*
1335
     * Wait for the device mapper node to be created.
1336
     */
1337
    waitForDevMapper(dmDevice);
1338

1339 1340
    int result;
    if (sb.c_opts & ASEC_SB_C_OPTS_EXT4) {
1341
        result = Ext4::doMount(dmDevice, mountPoint, readOnly, false, readOnly);
1342
    } else {
1343
        result = Fat::doMount(dmDevice, mountPoint, readOnly, false, readOnly, ownerUid, 0, 0222, false);
1344 1345 1346
    }

    if (result) {
San Mehat's avatar
San Mehat committed
1347
        SLOGE("ASEC mount failed (%s)", strerror(errno));
1348
        if (cleanupDm) {
San Mehat's avatar
San Mehat committed
1349
            Devmapper::destroy(idHash);
1350 1351
        }
        Loop::destroyByDevice(loopDevice);
1352 1353 1354
        return -1;
    }

Kenny Root's avatar
Kenny Root committed
1355
    mActiveContainers->push_back(new ContainerData(strdup(id), ASEC));
San Mehat's avatar
San Mehat committed
1356
    if (mDebug) {
San Mehat's avatar
San Mehat committed
1357
        SLOGD("ASEC %s mounted", id);
San Mehat's avatar
San Mehat committed
1358
    }
1359 1360 1361
    return 0;
}

1362 1363 1364 1365
Volume* VolumeManager::getVolumeForFile(const char *fileName) {
    VolumeCollection::iterator i;

    for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
1366
        const char* mountPoint = (*i)->getFuseMountpoint();
1367 1368 1369 1370 1371 1372 1373 1374
        if (!strncmp(fileName, mountPoint, strlen(mountPoint))) {
            return *i;
        }
    }

    return NULL;
}

1375 1376 1377
/**
 * Mounts an image file <code>img</code>.
 */
1378
int VolumeManager::mountObb(const char *img, const char *key, int ownerGid) {
1379 1380 1381 1382 1383 1384 1385 1386
    char mountPoint[255];

    char idHash[33];
    if (!asecHash(img, idHash, sizeof(idHash))) {
        SLOGE("Hash of '%s' failed (%s)", img, strerror(errno));
        return -1;
    }

1387 1388
    int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::LOOPDIR, idHash);
    if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
Colin Cross's avatar
Colin Cross committed
1389
        SLOGE("OBB mount failed for %s: couldn't construct mountpoint", img);
1390 1391
        return -1;
    }
1392 1393 1394 1395 1396 1397 1398 1399

    if (isMountpointMounted(mountPoint)) {
        SLOGE("Image %s already mounted", img);
        errno = EBUSY;
        return -1;
    }

    char loopDevice[255];
1400 1401
    if (setupLoopDevice(loopDevice, sizeof(loopDevice), img, idHash, mDebug))
        return -1;
1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422

    char dmDevice[255];
    bool cleanupDm = false;
    int fd;
    unsigned int nr_sec = 0;

    if ((fd = open(loopDevice, O_RDWR)) < 0) {
        SLOGE("Failed to open loopdevice (%s)", strerror(errno));
        Loop::destroyByDevice(loopDevice);
        return -1;
    }

    if (ioctl(fd, BLKGETSIZE, &nr_sec)) {
        SLOGE("Failed to get loop size (%s)", strerror(errno));
        Loop::destroyByDevice(loopDevice);
        close(fd);
        return -1;
    }

    close(fd);

1423 1424 1425
    if (setupDevMapperDevice(dmDevice, sizeof(loopDevice), loopDevice, img,key, idHash , nr_sec, &cleanupDm, mDebug)) {
        Loop::destroyByDevice(loopDevice);
        return -1;
1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438
    }

    if (mkdir(mountPoint, 0755)) {
        if (errno != EEXIST) {
            SLOGE("Mountpoint creation failed (%s)", strerror(errno));
            if (cleanupDm) {
                Devmapper::destroy(idHash);
            }
            Loop::destroyByDevice(loopDevice);
            return -1;
        }
    }

1439
    if (Fat::doMount(dmDevice, mountPoint, true, false, true, 0, ownerGid,
1440 1441 1442 1443 1444 1445 1446 1447 1448
                     0227, false)) {
        SLOGE("Image mount failed (%s)", strerror(errno));
        if (cleanupDm) {
            Devmapper::destroy(idHash);
        }
        Loop::destroyByDevice(loopDevice);
        return -1;
    }

Kenny Root's avatar
Kenny Root committed
1449
    mActiveContainers->push_back(new ContainerData(strdup(img), OBB));
1450 1451 1452 1453 1454 1455
    if (mDebug) {
        SLOGD("Image %s mounted", img);
    }
    return 0;
}

1456 1457 1458 1459 1460 1461 1462 1463
int VolumeManager::mountVolume(const char *label) {
    Volume *v = lookupVolume(label);

    if (!v) {
        errno = ENOENT;
        return -1;
    }

1464 1465 1466
    return v->mountVol();
}

Kenny Root's avatar
Kenny Root committed
1467
int VolumeManager::listMountedObbs(SocketClient* cli) {
1468 1469
    FILE *fp = setmntent("/proc/mounts", "r");
    if (fp == NULL) {
Kenny Root's avatar
Kenny Root committed
1470 1471 1472 1473 1474
        SLOGE("Error opening /proc/mounts (%s)", strerror(errno));
        return -1;
    }

    // Create a string to compare against that has a trailing slash
1475
    int loopDirLen = strlen(Volume::LOOPDIR);
Kenny Root's avatar
Kenny Root committed
1476 1477 1478 1479 1480
    char loopDir[loopDirLen + 2];
    strcpy(loopDir, Volume::LOOPDIR);
    loopDir[loopDirLen++] = '/';
    loopDir[loopDirLen] = '\0';

1481 1482 1483 1484
    mntent* mentry;
    while ((mentry = getmntent(fp)) != NULL) {
        if (!strncmp(mentry->mnt_dir, loopDir, loopDirLen)) {
            int fd = open(mentry->mnt_fsname, O_RDONLY);
Kenny Root's avatar
Kenny Root committed
1485 1486 1487 1488 1489 1490 1491 1492 1493 1494
            if (fd >= 0) {
                struct loop_info64 li;
                if (ioctl(fd, LOOP_GET_STATUS64, &li) >= 0) {
                    cli->sendMsg(ResponseCode::AsecListResult,
                            (const char*) li.lo_file_name, false);
                }
                close(fd);
            }
        }
    }
1495
    endmntent(fp);
Kenny Root's avatar
Kenny Root committed
1496 1497 1498
    return 0;
}

San Mehat's avatar
San Mehat committed
1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513
int VolumeManager::shareEnabled(const char *label, const char *method, bool *enabled) {
    Volume *v = lookupVolume(label);

    if (!v) {
        errno = ENOENT;
        return -1;
    }

    if (strcmp(method, "ums")) {
        errno = ENOSYS;
        return -1;
    }

    if (v->getState() != Volume::State_Shared) {
        *enabled = false;
San Mehat's avatar
San Mehat committed
1514 1515
    } else {
        *enabled = true;
San Mehat's avatar
San Mehat committed
1516 1517 1518 1519
    }
    return 0;
}

1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542
int VolumeManager::shareVolume(const char *label, const char *method) {
    Volume *v = lookupVolume(label);

    if (!v) {
        errno = ENOENT;
        return -1;
    }

    /*
     * Eventually, we'll want to support additional share back-ends,
     * some of which may work while the media is mounted. For now,
     * we just support UMS
     */
    if (strcmp(method, "ums")) {
        errno = ENOSYS;
        return -1;
    }

    if (v->getState() == Volume::State_NoMedia) {
        errno = ENODEV;
        return -1;
    }

1543
    if (v->getState() != Volume::State_Idle) {
1544
        // You need to unmount manually befoe sharing
1545 1546 1547 1548
        errno = EBUSY;
        return -1;
    }

1549 1550 1551 1552 1553
    if (mVolManagerDisabled) {
        errno = EBUSY;
        return -1;
    }

1554
    dev_t d = v->getShareDevice();
1555 1556 1557 1558 1559 1560 1561 1562
    if ((MAJOR(d) == 0) && (MINOR(d) == 0)) {
        // This volume does not support raw disk access
        errno = EINVAL;
        return -1;
    }

    int fd;
    char nodepath[255];
1563
    int written = snprintf(nodepath,
1564
             sizeof(nodepath), "/dev/block/vold/%d:%d",
Colin Cross's avatar
Colin Cross committed
1565
             major(d), minor(d));
1566

1567 1568 1569 1570 1571
    if ((written < 0) || (size_t(written) >= sizeof(nodepath))) {
        SLOGE("shareVolume failed: couldn't construct nodepath");
        return -1;
    }

1572
    if ((fd = open(MASS_STORAGE_FILE_PATH, O_WRONLY)) < 0) {
San Mehat's avatar
San Mehat committed
1573
        SLOGE("Unable to open ums lunfile (%s)", strerror(errno));
1574 1575 1576 1577
        return -1;
    }

    if (write(fd, nodepath, strlen(nodepath)) < 0) {
San Mehat's avatar
San Mehat committed
1578
        SLOGE("Unable to write to ums lunfile (%s)", strerror(errno));
1579 1580 1581 1582 1583 1584
        close(fd);
        return -1;
    }

    close(fd);
    v->handleVolumeShared();
1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599
    if (mUmsSharingCount++ == 0) {
        FILE* fp;
        mSavedDirtyRatio = -1; // in case we fail
        if ((fp = fopen("/proc/sys/vm/dirty_ratio", "r+"))) {
            char line[16];
            if (fgets(line, sizeof(line), fp) && sscanf(line, "%d", &mSavedDirtyRatio)) {
                fprintf(fp, "%d\n", mUmsDirtyRatio);
            } else {
                SLOGE("Failed to read dirty_ratio (%s)", strerror(errno));
            }
            fclose(fp);
        } else {
            SLOGE("Failed to open /proc/sys/vm/dirty_ratio (%s)", strerror(errno));
        }
    }
1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621
    return 0;
}

int VolumeManager::unshareVolume(const char *label, const char *method) {
    Volume *v = lookupVolume(label);

    if (!v) {
        errno = ENOENT;
        return -1;
    }

    if (strcmp(method, "ums")) {
        errno = ENOSYS;
        return -1;
    }

    if (v->getState() != Volume::State_Shared) {
        errno = EINVAL;
        return -1;
    }

    int fd;
1622
    if ((fd = open(MASS_STORAGE_FILE_PATH, O_WRONLY)) < 0) {
San Mehat's avatar
San Mehat committed
1623
        SLOGE("Unable to open ums lunfile (%s)", strerror(errno));
1624 1625 1626 1627 1628
        return -1;
    }

    char ch = 0;
    if (write(fd, &ch, 1) < 0) {
San Mehat's avatar
San Mehat committed
1629
        SLOGE("Unable to write to ums lunfile (%s)", strerror(errno));
1630 1631 1632 1633 1634 1635
        close(fd);
        return -1;
    }

    close(fd);
    v->handleVolumeUnshared();
1636 1637 1638 1639 1640 1641 1642 1643 1644 1645
    if (--mUmsSharingCount == 0 && mSavedDirtyRatio != -1) {
        FILE* fp;
        if ((fp = fopen("/proc/sys/vm/dirty_ratio", "r+"))) {
            fprintf(fp, "%d\n", mSavedDirtyRatio);
            fclose(fp);
        } else {
            SLOGE("Failed to open /proc/sys/vm/dirty_ratio (%s)", strerror(errno));
        }
        mSavedDirtyRatio = -1;
    }
1646
    return 0;
1647 1648
}

1649
extern "C" int vold_disableVol(const char *label) {
1650
    VolumeManager *vm = VolumeManager::Instance();
1651 1652
    vm->disableVolumeManager();
    vm->unshareVolume(label, "ums");
1653
    return vm->unmountVolume(label, true, false);
1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686
}

extern "C" int vold_getNumDirectVolumes(void) {
    VolumeManager *vm = VolumeManager::Instance();
    return vm->getNumDirectVolumes();
}

int VolumeManager::getNumDirectVolumes(void) {
    VolumeCollection::iterator i;
    int n=0;

    for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
        if ((*i)->getShareDevice() != (dev_t)0) {
            n++;
        }
    }
    return n;
}

extern "C" int vold_getDirectVolumeList(struct volume_info *vol_list) {
    VolumeManager *vm = VolumeManager::Instance();
    return vm->getDirectVolumeList(vol_list);
}

int VolumeManager::getDirectVolumeList(struct volume_info *vol_list) {
    VolumeCollection::iterator i;
    int n=0;
    dev_t d;

    for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
        if ((d=(*i)->getShareDevice()) != (dev_t)0) {
            (*i)->getVolInfo(&vol_list[n]);
            snprintf(vol_list[n].blk_dev, sizeof(vol_list[n].blk_dev),
Colin Cross's avatar
Colin Cross committed
1687
                     "/dev/block/vold/%d:%d", major(d), minor(d));
1688 1689 1690 1691 1692 1693 1694
            n++;
        }
    }

    return 0;
}

1695
int VolumeManager::unmountVolume(const char *label, bool force, bool revert) {
1696 1697 1698 1699 1700 1701 1702
    Volume *v = lookupVolume(label);

    if (!v) {
        errno = ENOENT;
        return -1;
    }

1703 1704 1705 1706 1707
    if (v->getState() == Volume::State_NoMedia) {
        errno = ENODEV;
        return -1;
    }

1708
    if (v->getState() != Volume::State_Mounted) {
San Mehat's avatar
San Mehat committed
1709
        SLOGW("Attempt to unmount volume which isn't mounted (%d)\n",
1710
             v->getState());
1711
        errno = EBUSY;
1712
        return UNMOUNT_NOT_MOUNTED_ERR;
1713 1714
    }

1715
    cleanupAsec(v, force);
1716

1717
    return v->unmountVol(force, revert);
1718 1719
}

1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743
extern "C" int vold_unmountAllAsecs(void) {
    int rc;

    VolumeManager *vm = VolumeManager::Instance();
    rc = vm->unmountAllAsecsInDir(Volume::SEC_ASECDIR_EXT);
    if (vm->unmountAllAsecsInDir(Volume::SEC_ASECDIR_INT)) {
        rc = -1;
    }
    return rc;
}

#define ID_BUF_LEN 256
#define ASEC_SUFFIX ".asec"
#define ASEC_SUFFIX_LEN (sizeof(ASEC_SUFFIX) - 1)
int VolumeManager::unmountAllAsecsInDir(const char *directory) {
    DIR *d = opendir(directory);
    int rc = 0;

    if (!d) {
        SLOGE("Could not open asec dir %s", directory);
        return -1;
    }

    size_t dirent_len = offsetof(struct dirent, d_name) +
1744
            fpathconf(dirfd(d), _PC_NAME_MAX) + 1;
1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775

    struct dirent *dent = (struct dirent *) malloc(dirent_len);
    if (dent == NULL) {
        SLOGE("Failed to allocate memory for asec dir");
        return -1;
    }

    struct dirent *result;
    while (!readdir_r(d, dent, &result) && result != NULL) {
        if (dent->d_name[0] == '.')
            continue;
        if (dent->d_type != DT_REG)
            continue;
        size_t name_len = strlen(dent->d_name);
        if (name_len > 5 && name_len < (ID_BUF_LEN + ASEC_SUFFIX_LEN - 1) &&
                !strcmp(&dent->d_name[name_len - 5], ASEC_SUFFIX)) {
            char id[ID_BUF_LEN];
            strlcpy(id, dent->d_name, name_len - 4);
            if (unmountAsec(id, true)) {
                /* Register the error, but try to unmount more asecs */
                rc = -1;
            }
        }
    }
    closedir(d);

    free(dent);

    return rc;
}

1776 1777 1778
/*
 * Looks up a volume by it's label or mount-point
 */
1779 1780 1781 1782
Volume *VolumeManager::lookupVolume(const char *label) {
    VolumeCollection::iterator i;

    for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
1783
        if (label[0] == '/') {
1784
            if (!strcmp(label, (*i)->getFuseMountpoint()))
1785 1786 1787 1788 1789
                return (*i);
        } else {
            if (!strcmp(label, (*i)->getLabel()))
                return (*i);
        }
1790 1791 1792
    }
    return NULL;
}
1793 1794 1795

bool VolumeManager::isMountpointMounted(const char *mp)
{
1796 1797
    FILE *fp = setmntent("/proc/mounts", "r");
    if (fp == NULL) {
San Mehat's avatar
San Mehat committed
1798
        SLOGE("Error opening /proc/mounts (%s)", strerror(errno));
1799 1800 1801
        return false;
    }

1802 1803 1804 1805 1806 1807
    bool found_mp = false;
    mntent* mentry;
    while ((mentry = getmntent(fp)) != NULL) {
        if (strcmp(mentry->mnt_dir, mp) == 0) {
            found_mp = true;
            break;
1808 1809
        }
    }
1810 1811
    endmntent(fp);
    return found_mp;
1812 1813
}

1814
int VolumeManager::cleanupAsec(Volume *v, bool force) {
1815 1816 1817 1818 1819 1820
    int rc = 0;

    char asecFileName[255];

    AsecIdCollection removeAsec;
    AsecIdCollection removeObb;
1821 1822 1823

    for (AsecIdCollection::iterator it = mActiveContainers->begin(); it != mActiveContainers->end();
            ++it) {
Kenny Root's avatar
Kenny Root committed
1824
        ContainerData* cd = *it;
1825

Kenny Root's avatar
Kenny Root committed
1826
        if (cd->type == ASEC) {
1827 1828 1829 1830 1831 1832 1833 1834 1835 1836
            if (findAsec(cd->id, asecFileName, sizeof(asecFileName))) {
                SLOGE("Couldn't find ASEC %s; cleaning up", cd->id);
                removeAsec.push_back(cd);
            } else {
                SLOGD("Found ASEC at path %s", asecFileName);
                if (!strncmp(asecFileName, Volume::SEC_ASECDIR_EXT,
                        strlen(Volume::SEC_ASECDIR_EXT))) {
                    removeAsec.push_back(cd);
                }
            }
Kenny Root's avatar
Kenny Root committed
1837
        } else if (cd->type == OBB) {
1838
            if (v == getVolumeForFile(cd->id)) {
1839
                removeObb.push_back(cd);
Kenny Root's avatar
Kenny Root committed
1840 1841 1842
            }
        } else {
            SLOGE("Unknown container type %d!", cd->type);
1843 1844
        }
    }
1845

1846 1847 1848 1849 1850 1851 1852 1853 1854 1855
    for (AsecIdCollection::iterator it = removeAsec.begin(); it != removeAsec.end(); ++it) {
        ContainerData *cd = *it;
        SLOGI("Unmounting ASEC %s (dependent on %s)", cd->id, v->getLabel());
        if (unmountAsec(cd->id, force)) {
            SLOGE("Failed to unmount ASEC %s (%s)", cd->id, strerror(errno));
            rc = -1;
        }
    }

    for (AsecIdCollection::iterator it = removeObb.begin(); it != removeObb.end(); ++it) {
1856
        ContainerData *cd = *it;
1857
        SLOGI("Unmounting OBB %s (dependent on %s)", cd->id, v->getLabel());
1858 1859 1860 1861 1862 1863 1864
        if (unmountObb(cd->id, force)) {
            SLOGE("Failed to unmount OBB %s (%s)", cd->id, strerror(errno));
            rc = -1;
        }
    }

    return rc;
1865 1866
}

Jeff Sharkey's avatar
Jeff Sharkey committed
1867
int VolumeManager::mkdirs(char* path) {
1868
    // Require that path lives under a volume we manage and is mounted
Jeff Sharkey's avatar
Jeff Sharkey committed
1869 1870
    const char* emulated_source = getenv("EMULATED_STORAGE_SOURCE");
    const char* root = NULL;
1871
    if (emulated_source && !strncmp(path, emulated_source, strlen(emulated_source))) {
Jeff Sharkey's avatar
Jeff Sharkey committed
1872 1873 1874
        root = emulated_source;
    } else {
        Volume* vol = getVolumeForFile(path);
1875
        if (vol && vol->getState() == Volume::State_Mounted) {
Jeff Sharkey's avatar
Jeff Sharkey committed
1876 1877 1878 1879 1880
            root = vol->getMountpoint();
        }
    }

    if (!root) {
1881
        SLOGE("Failed to find mounted volume for %s", path);
Jeff Sharkey's avatar
Jeff Sharkey committed
1882 1883 1884 1885 1886 1887
        return -EINVAL;
    }

    /* fs_mkdirs() does symlink checking and relative path enforcement */
    return fs_mkdirs(path, 0700);
}