int VolumeManager::mountVolume(const char *label) { Volume *v = lookupVolume(label); if (!v) { errno = ENOENT; return -1; } return v->mountVol(); }
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; int lun_number; // /mnt/sdcard to lun0 and anything else to lun1. Fix this. if (v->isPrimaryStorage()) { lun_number = 0; } else { lun_number = SECOND_LUN_NUM; } if ((fd = openLun(lun_number)) < 0) { return -1; } char ch = 0; if (write(fd, &ch, 1) < 0) { SLOGE("Unable to write to ums lunfile (%s)", strerror(errno)); close(fd); return -1; } close(fd); v->handleVolumeUnshared(); 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; } return 0; }
int VolumeManager::formatVolume(const char *label, bool wipe, const char *fstype) { Volume *v = lookupVolume(label); if (!v) { errno = ENOENT; return -1; } if (mVolManagerDisabled) { errno = EBUSY; return -1; } return v->formatVol(wipe, fstype); }
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; if ((fd = open(MASS_STORAGE_FILE_PATH, O_WRONLY)) < 0) { SLOGE("Unable to open ums lunfile (%s)", strerror(errno)); return -1; } char ch = 0; if (write(fd, &ch, 1) < 0) { SLOGE("Unable to write to ums lunfile (%s)", strerror(errno)); close(fd); return -1; } close(fd); v->handleVolumeUnshared(); 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; } return 0; }
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; } else { *enabled = true; } return 0; }
int VolumeManager::unmountVolume(const char *label, bool force, bool revert) { Volume *v = lookupVolume(label); if (!v) { errno = ENOENT; return -1; } if (v->getState() == Volume::State_NoMedia) { errno = ENODEV; return -1; } if (v->getState() != Volume::State_Mounted) { SLOGW("Attempt to unmount volume which isn't mounted (%d)\n", v->getState()); errno = EBUSY; return UNMOUNT_NOT_MOUNTED_ERR; } cleanupAsec(v, force); return v->unmountVol(force, revert); }
int VolumeManager::createAsec(const char *id, unsigned int numSectors, const char *fstype, const char *key, const int ownerUid, bool isExternal) { struct asec_superblock sb; memset(&sb, 0, sizeof(sb)); if (!isLegalAsecId(id)) { SLOGE("createAsec: Invalid asec id \"%s\"", id); errno = EINVAL; return -1; } 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; } } sb.magic = ASEC_SB_MAGIC; sb.ver = ASEC_SB_VER; if (numSectors < ((1024*1024)/512)) { SLOGE("Invalid container size specified (%d sectors)", numSectors); errno = EINVAL; return -1; } if (lookupVolume(id)) { SLOGE("ASEC id '%s' currently exists", id); errno = EADDRINUSE; return -1; } char asecFileName[255]; 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; int written = snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", asecDir, id); if ((written < 0) || (size_t(written) >= sizeof(asecFileName))) { errno = EINVAL; return -1; } if (!access(asecFileName, F_OK)) { SLOGE("ASEC file '%s' currently exists - destroy it first! (%s)", asecFileName, strerror(errno)); errno = EADDRINUSE; return -1; } /* * Add some headroom */ unsigned fatSize = (((numSectors * 4) / 512) + 1) * 2; unsigned numImgSectors = numSectors + fatSize + 2; if (numImgSectors % 63) { numImgSectors += (63 - (numImgSectors % 63)); } // Add +1 for our superblock which is at the end if (Loop::createImageFile(asecFileName, numImgSectors + 1)) { SLOGE("ASEC image file creation failed (%s)", strerror(errno)); return -1; } char idHash[33]; if (!asecHash(id, idHash, sizeof(idHash))) { SLOGE("Hash of '%s' failed (%s)", id, strerror(errno)); unlink(asecFileName); return -1; } char loopDevice[255]; if (Loop::create(idHash, asecFileName, loopDevice, sizeof(loopDevice))) { SLOGE("ASEC loop device creation failed (%s)", strerror(errno)); unlink(asecFileName); return -1; } char dmDevice[255]; bool cleanupDm = false; if (strcmp(key, "none")) { // XXX: This is all we support for now sb.c_cipher = ASEC_SB_C_CIPHER_TWOFISH; if (Devmapper::create(idHash, loopDevice, key, numImgSectors, dmDevice, sizeof(dmDevice))) { SLOGE("ASEC device mapping failed (%s)", strerror(errno)); Loop::destroyByDevice(loopDevice); unlink(asecFileName); return -1; } cleanupDm = true; } else { sb.c_cipher = ASEC_SB_C_CIPHER_NONE; strcpy(dmDevice, loopDevice); } /* * Drop down the superblock at the end of the file */ int sbfd = open(loopDevice, O_RDWR); if (sbfd < 0) { SLOGE("Failed to open new DM device for superblock write (%s)", strerror(errno)); if (cleanupDm) { Devmapper::destroy(idHash); } Loop::destroyByDevice(loopDevice); unlink(asecFileName); return -1; } if (lseek(sbfd, (numImgSectors * 512), SEEK_SET) < 0) { close(sbfd); SLOGE("Failed to lseek for superblock (%s)", strerror(errno)); if (cleanupDm) { Devmapper::destroy(idHash); } Loop::destroyByDevice(loopDevice); unlink(asecFileName); return -1; } if (write(sbfd, &sb, sizeof(sb)) != sizeof(sb)) { close(sbfd); SLOGE("Failed to write superblock (%s)", strerror(errno)); if (cleanupDm) { Devmapper::destroy(idHash); } Loop::destroyByDevice(loopDevice); unlink(asecFileName); return -1; } close(sbfd); if (wantFilesystem) { int formatStatus; char mountPoint[255]; 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; } if (usingExt4) { formatStatus = Ext4::format(dmDevice, mountPoint); } else { formatStatus = Fat::format(dmDevice, numImgSectors, 0); } if (formatStatus < 0) { SLOGE("ASEC fs format failed (%s)", strerror(errno)); if (cleanupDm) { Devmapper::destroy(idHash); } Loop::destroyByDevice(loopDevice); unlink(asecFileName); return -1; } if (mkdir(mountPoint, 0000)) { if (errno != EEXIST) { SLOGE("Mountpoint creation failed (%s)", strerror(errno)); if (cleanupDm) { Devmapper::destroy(idHash); } Loop::destroyByDevice(loopDevice); unlink(asecFileName); return -1; } } int mountStatus; if (usingExt4) { mountStatus = Ext4::doMount(dmDevice, mountPoint, false, false, false, false); } else { mountStatus = Fat::doMount(dmDevice, mountPoint, false, false, false, ownerUid, 0, 0000, false); } if (mountStatus) { SLOGE("ASEC FAT mount failed (%s)", strerror(errno)); if (cleanupDm) { Devmapper::destroy(idHash); } Loop::destroyByDevice(loopDevice); unlink(asecFileName); return -1; } 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); } } } else { SLOGI("Created raw secure container %s (no filesystem)", id); } mActiveContainers->push_back(new ContainerData(strdup(id), ASEC)); return 0; }
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; } if (v->getState() != Volume::State_Idle) { // You need to unmount manually befoe sharing errno = EBUSY; return -1; } if (mVolManagerDisabled) { errno = EBUSY; return -1; } dev_t d = v->getShareDevice(); if ((MAJOR(d) == 0) && (MINOR(d) == 0)) { // This volume does not support raw disk access errno = EINVAL; return -1; } #ifdef VOLD_EMMC_SHARES_DEV_MAJOR // If emmc and sdcard share dev major number, vold may pick // incorrectly based on partition nodes alone. Use device nodes instead. v->getDeviceNodes((dev_t *) &d, 1); if ((MAJOR(d) == 0) && (MINOR(d) == 0)) { // This volume does not support raw disk access errno = EINVAL; return -1; } #endif int fd; char nodepath[255]; int written = snprintf(nodepath, sizeof(nodepath), "/dev/block/vold/%d:%d", MAJOR(d), MINOR(d)); if ((written < 0) || (size_t(written) >= sizeof(nodepath))) { SLOGE("shareVolume failed: couldn't construct nodepath"); return -1; } if ((fd = openLun(v->getLunNumber())) < 0) { return -1; } if (write(fd, nodepath, strlen(nodepath)) < 0) { SLOGE("Unable to write to ums lunfile (%s)", strerror(errno)); close(fd); return -1; } close(fd); v->handleVolumeShared(); 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)); } } return 0; }
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; } if (v->getState() != Volume::State_Idle) { // You need to unmount manually befoe sharing errno = EBUSY; return -1; } if (mVolManagerDisabled) { errno = EBUSY; return -1; } dev_t d = v->getShareDevice(); if ((MAJOR(d) == 0) && (MINOR(d) == 0)) { // This volume does not support raw disk access errno = EINVAL; return -1; } int fd; char nodepath[255]; snprintf(nodepath, sizeof(nodepath), "/dev/block/vold/%d:%d", MAJOR(d), MINOR(d)); if ((fd = open(MASS_STORAGE_FILE_PATH, O_WRONLY)) < 0) { SLOGE("Unable to open ums lunfile (%s)", strerror(errno)); return -1; } if (write(fd, nodepath, strlen(nodepath)) < 0) { SLOGE("Unable to write to ums lunfile (%s)", strerror(errno)); close(fd); return -1; } close(fd); v->handleVolumeShared(); 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)); } } return 0; }
int VolumeManager::createAsec(const char *id, unsigned int numSectors, const char *fstype, const char *key, int ownerUid) { struct asec_superblock sb; memset(&sb, 0, sizeof(sb)); sb.magic = ASEC_SB_MAGIC; sb.ver = ASEC_SB_VER; if (numSectors < ((1024*1024)/512)) { SLOGE("Invalid container size specified (%d sectors)", numSectors); errno = EINVAL; return -1; } if (lookupVolume(id)) { SLOGE("ASEC id '%s' currently exists", id); errno = EADDRINUSE; return -1; } char asecFileName[255]; snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", Volume::SEC_ASECDIR, id); if (!access(asecFileName, F_OK)) { SLOGE("ASEC file '%s' currently exists - destroy it first! (%s)", asecFileName, strerror(errno)); errno = EADDRINUSE; return -1; } /* * Add some headroom */ unsigned fatSize = (((numSectors * 4) / 512) + 1) * 2; unsigned numImgSectors = numSectors + fatSize + 2; if (numImgSectors % 63) { numImgSectors += (63 - (numImgSectors % 63)); } // Add +1 for our superblock which is at the end if (Loop::createImageFile(asecFileName, numImgSectors + 1)) { SLOGE("ASEC image file creation failed (%s)", strerror(errno)); return -1; } char idHash[33]; if (!asecHash(id, idHash, sizeof(idHash))) { SLOGE("Hash of '%s' failed (%s)", id, strerror(errno)); unlink(asecFileName); return -1; } char loopDevice[255]; if (Loop::create(idHash, asecFileName, loopDevice, sizeof(loopDevice))) { SLOGE("ASEC loop device creation failed (%s)", strerror(errno)); unlink(asecFileName); return -1; } char dmDevice[255]; bool cleanupDm = false; if (strcmp(key, "none")) { // XXX: This is all we support for now sb.c_cipher = ASEC_SB_C_CIPHER_TWOFISH; if (Devmapper::create(idHash, loopDevice, key, numImgSectors, dmDevice, sizeof(dmDevice))) { SLOGE("ASEC device mapping failed (%s)", strerror(errno)); Loop::destroyByDevice(loopDevice); unlink(asecFileName); return -1; } cleanupDm = true; } else { sb.c_cipher = ASEC_SB_C_CIPHER_NONE; strcpy(dmDevice, loopDevice); } /* * Drop down the superblock at the end of the file */ int sbfd = open(loopDevice, O_RDWR); if (sbfd < 0) { SLOGE("Failed to open new DM device for superblock write (%s)", strerror(errno)); if (cleanupDm) { Devmapper::destroy(idHash); } Loop::destroyByDevice(loopDevice); unlink(asecFileName); return -1; } if (lseek(sbfd, (numImgSectors * 512), SEEK_SET) < 0) { close(sbfd); SLOGE("Failed to lseek for superblock (%s)", strerror(errno)); if (cleanupDm) { Devmapper::destroy(idHash); } Loop::destroyByDevice(loopDevice); unlink(asecFileName); return -1; } if (write(sbfd, &sb, sizeof(sb)) != sizeof(sb)) { close(sbfd); SLOGE("Failed to write superblock (%s)", strerror(errno)); if (cleanupDm) { Devmapper::destroy(idHash); } Loop::destroyByDevice(loopDevice); unlink(asecFileName); return -1; } close(sbfd); if (strcmp(fstype, "none")) { if (strcmp(fstype, "fat")) { SLOGW("Unknown fstype '%s' specified for container", fstype); } if (Fat::format(dmDevice, numImgSectors)) { SLOGE("ASEC FAT format failed (%s)", strerror(errno)); if (cleanupDm) { Devmapper::destroy(idHash); } Loop::destroyByDevice(loopDevice); unlink(asecFileName); return -1; } char mountPoint[255]; snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id); if (mkdir(mountPoint, 0777)) { if (errno != EEXIST) { SLOGE("Mountpoint creation failed (%s)", strerror(errno)); if (cleanupDm) { Devmapper::destroy(idHash); } Loop::destroyByDevice(loopDevice); unlink(asecFileName); return -1; } } if (Fat::doMount(dmDevice, mountPoint, false, false, false, ownerUid, 0, 0000, false)) { SLOGE("ASEC FAT mount failed (%s)", strerror(errno)); if (cleanupDm) { Devmapper::destroy(idHash); } Loop::destroyByDevice(loopDevice); unlink(asecFileName); return -1; } } else { SLOGI("Created raw secure container %s (no filesystem)", id); } mActiveContainers->push_back(new ContainerData(strdup(id), ASEC)); return 0; }