2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
25 #include <sys/types.h>
26 #include <sys/mount.h>
29 #include <linux/kdev_t.h>
31 #define LOG_TAG "Vold"
33 #include <openssl/md5.h>
35 #include <cutils/fs.h>
36 #include <cutils/log.h>
38 #include <selinux/android.h>
40 #include <sysutils/NetlinkEvent.h>
42 #include <private/android_filesystem_config.h>
44 #include "VolumeManager.h"
45 #include "DirectVolume.h"
46 #include "ResponseCode.h"
50 #include "Devmapper.h"
55 #define MASS_STORAGE_FILE_PATH "/sys/class/android_usb/android0/f_mass_storage/lun/file"
57 VolumeManager *VolumeManager::sInstance = NULL;
59 VolumeManager *VolumeManager::Instance() {
61 sInstance = new VolumeManager();
65 VolumeManager::VolumeManager() {
67 mVolumes = new VolumeCollection();
68 mActiveContainers = new AsecIdCollection();
71 mSavedDirtyRatio = -1;
72 // set dirty ratio to 0 when UMS is active
74 mVolManagerDisabled = 0;
77 VolumeManager::~VolumeManager() {
79 delete mActiveContainers;
82 char *VolumeManager::asecHash(const char *id, char *buffer, size_t len) {
83 static const char* digits = "0123456789abcdef";
85 unsigned char sig[MD5_DIGEST_LENGTH];
88 SLOGE("Destination buffer is NULL");
91 } else if (id == NULL) {
92 SLOGE("Source buffer is NULL");
95 } else if (len < MD5_ASCII_LENGTH_PLUS_NULL) {
96 SLOGE("Target hash buffer size < %d bytes (%zu)",
97 MD5_ASCII_LENGTH_PLUS_NULL, len);
102 MD5(reinterpret_cast<const unsigned char*>(id), strlen(id), sig);
105 for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
106 *p++ = digits[sig[i] >> 4];
107 *p++ = digits[sig[i] & 0x0F];
114 void VolumeManager::setDebug(bool enable) {
116 VolumeCollection::iterator it;
117 for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
118 (*it)->setDebug(enable);
122 int VolumeManager::start() {
126 int VolumeManager::stop() {
130 int VolumeManager::addVolume(Volume *v) {
131 mVolumes->push_back(v);
135 void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
136 const char *devpath = evt->findParam("DEVPATH");
138 /* Lookup a volume to handle this device */
139 VolumeCollection::iterator it;
141 for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
142 if (!(*it)->handleBlockEvent(evt)) {
144 SLOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel());
153 SLOGW("No volumes handled block event for '%s'", devpath);
158 int VolumeManager::listVolumes(SocketClient *cli) {
159 VolumeCollection::iterator i;
161 for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
163 asprintf(&buffer, "%s %s %d",
164 (*i)->getLabel(), (*i)->getFuseMountpoint(),
166 cli->sendMsg(ResponseCode::VolumeListResult, buffer, false);
169 cli->sendMsg(ResponseCode::CommandOkay, "Volumes listed.", false);
173 int VolumeManager::formatVolume(const char *label, bool wipe) {
174 Volume *v = lookupVolume(label);
181 if (mVolManagerDisabled) {
186 return v->formatVol(wipe);
189 int VolumeManager::getObbMountPath(const char *sourceFile, char *mountPath, int mountPathLen) {
191 if (!asecHash(sourceFile, idHash, sizeof(idHash))) {
192 SLOGE("Hash of '%s' failed (%s)", sourceFile, strerror(errno));
196 memset(mountPath, 0, mountPathLen);
197 int written = snprintf(mountPath, mountPathLen, "%s/%s", Volume::LOOPDIR, idHash);
198 if ((written < 0) || (written >= mountPathLen)) {
203 if (access(mountPath, F_OK)) {
211 int VolumeManager::getAsecMountPath(const char *id, char *buffer, int maxlen) {
212 char asecFileName[255];
214 if (!isLegalAsecId(id)) {
215 SLOGE("getAsecMountPath: Invalid asec id \"%s\"", id);
220 if (findAsec(id, asecFileName, sizeof(asecFileName))) {
221 SLOGE("Couldn't find ASEC %s", id);
225 memset(buffer, 0, maxlen);
226 if (access(asecFileName, F_OK)) {
231 int written = snprintf(buffer, maxlen, "%s/%s", Volume::ASECDIR, id);
232 if ((written < 0) || (written >= maxlen)) {
233 SLOGE("getAsecMountPath failed for %s: couldn't construct path in buffer", id);
241 int VolumeManager::getAsecFilesystemPath(const char *id, char *buffer, int maxlen) {
242 char asecFileName[255];
244 if (!isLegalAsecId(id)) {
245 SLOGE("getAsecFilesystemPath: Invalid asec id \"%s\"", id);
250 if (findAsec(id, asecFileName, sizeof(asecFileName))) {
251 SLOGE("Couldn't find ASEC %s", id);
255 memset(buffer, 0, maxlen);
256 if (access(asecFileName, F_OK)) {
261 int written = snprintf(buffer, maxlen, "%s", asecFileName);
262 if ((written < 0) || (written >= maxlen)) {
270 int VolumeManager::createAsec(const char *id, unsigned int numSectors, const char *fstype,
271 const char *key, const int ownerUid, bool isExternal) {
272 struct asec_superblock sb;
273 memset(&sb, 0, sizeof(sb));
275 if (!isLegalAsecId(id)) {
276 SLOGE("createAsec: Invalid asec id \"%s\"", id);
281 const bool wantFilesystem = strcmp(fstype, "none");
282 bool usingExt4 = false;
283 if (wantFilesystem) {
284 usingExt4 = !strcmp(fstype, "ext4");
286 sb.c_opts |= ASEC_SB_C_OPTS_EXT4;
287 } else if (strcmp(fstype, "fat")) {
288 SLOGE("Invalid filesystem type %s", fstype);
294 sb.magic = ASEC_SB_MAGIC;
295 sb.ver = ASEC_SB_VER;
297 if (numSectors < ((1024*1024)/512)) {
298 SLOGE("Invalid container size specified (%d sectors)", numSectors);
303 if (lookupVolume(id)) {
304 SLOGE("ASEC id '%s' currently exists", id);
309 char asecFileName[255];
311 if (!findAsec(id, asecFileName, sizeof(asecFileName))) {
312 SLOGE("ASEC file '%s' currently exists - destroy it first! (%s)",
313 asecFileName, strerror(errno));
318 const char *asecDir = isExternal ? Volume::SEC_ASECDIR_EXT : Volume::SEC_ASECDIR_INT;
320 int written = snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", asecDir, id);
321 if ((written < 0) || (size_t(written) >= sizeof(asecFileName))) {
326 if (!access(asecFileName, F_OK)) {
327 SLOGE("ASEC file '%s' currently exists - destroy it first! (%s)",
328 asecFileName, strerror(errno));
336 unsigned fatSize = (((numSectors * 4) / 512) + 1) * 2;
337 unsigned numImgSectors = numSectors + fatSize + 2;
339 if (numImgSectors % 63) {
340 numImgSectors += (63 - (numImgSectors % 63));
343 // Add +1 for our superblock which is at the end
344 if (Loop::createImageFile(asecFileName, numImgSectors + 1)) {
345 SLOGE("ASEC image file creation failed (%s)", strerror(errno));
350 if (!asecHash(id, idHash, sizeof(idHash))) {
351 SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
352 unlink(asecFileName);
356 char loopDevice[255];
357 if (Loop::create(idHash, asecFileName, loopDevice, sizeof(loopDevice))) {
358 SLOGE("ASEC loop device creation failed (%s)", strerror(errno));
359 unlink(asecFileName);
364 bool cleanupDm = false;
366 if (strcmp(key, "none")) {
367 // XXX: This is all we support for now
368 sb.c_cipher = ASEC_SB_C_CIPHER_TWOFISH;
369 if (Devmapper::create(idHash, loopDevice, key, numImgSectors, dmDevice,
371 SLOGE("ASEC device mapping failed (%s)", strerror(errno));
372 Loop::destroyByDevice(loopDevice);
373 unlink(asecFileName);
378 sb.c_cipher = ASEC_SB_C_CIPHER_NONE;
379 strcpy(dmDevice, loopDevice);
383 * Drop down the superblock at the end of the file
386 int sbfd = open(loopDevice, O_RDWR);
388 SLOGE("Failed to open new DM device for superblock write (%s)", strerror(errno));
390 Devmapper::destroy(idHash);
392 Loop::destroyByDevice(loopDevice);
393 unlink(asecFileName);
397 if (lseek(sbfd, (numImgSectors * 512), SEEK_SET) < 0) {
399 SLOGE("Failed to lseek for superblock (%s)", strerror(errno));
401 Devmapper::destroy(idHash);
403 Loop::destroyByDevice(loopDevice);
404 unlink(asecFileName);
408 if (write(sbfd, &sb, sizeof(sb)) != sizeof(sb)) {
410 SLOGE("Failed to write superblock (%s)", strerror(errno));
412 Devmapper::destroy(idHash);
414 Loop::destroyByDevice(loopDevice);
415 unlink(asecFileName);
420 if (wantFilesystem) {
422 char mountPoint[255];
424 int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
425 if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
426 SLOGE("ASEC fs format failed: couldn't construct mountPoint");
428 Devmapper::destroy(idHash);
430 Loop::destroyByDevice(loopDevice);
431 unlink(asecFileName);
436 formatStatus = Ext4::format(dmDevice, mountPoint);
438 formatStatus = Fat::format(dmDevice, numImgSectors, 0);
441 if (formatStatus < 0) {
442 SLOGE("ASEC fs format failed (%s)", strerror(errno));
444 Devmapper::destroy(idHash);
446 Loop::destroyByDevice(loopDevice);
447 unlink(asecFileName);
451 if (mkdir(mountPoint, 0000)) {
452 if (errno != EEXIST) {
453 SLOGE("Mountpoint creation failed (%s)", strerror(errno));
455 Devmapper::destroy(idHash);
457 Loop::destroyByDevice(loopDevice);
458 unlink(asecFileName);
465 mountStatus = Ext4::doMount(dmDevice, mountPoint, false, false, false);
467 mountStatus = Fat::doMount(dmDevice, mountPoint, false, false, false, ownerUid, 0, 0000,
472 SLOGE("ASEC FAT mount failed (%s)", strerror(errno));
474 Devmapper::destroy(idHash);
476 Loop::destroyByDevice(loopDevice);
477 unlink(asecFileName);
482 int dirfd = open(mountPoint, O_DIRECTORY);
484 if (fchown(dirfd, ownerUid, AID_SYSTEM)
485 || fchmod(dirfd, S_IRUSR | S_IWUSR | S_IXUSR | S_ISGID | S_IRGRP | S_IXGRP)) {
486 SLOGI("Cannot chown/chmod new ASEC mount point %s", mountPoint);
492 SLOGI("Created raw secure container %s (no filesystem)", id);
495 mActiveContainers->push_back(new ContainerData(strdup(id), ASEC));
499 int VolumeManager::finalizeAsec(const char *id) {
500 char asecFileName[255];
501 char loopDevice[255];
502 char mountPoint[255];
504 if (!isLegalAsecId(id)) {
505 SLOGE("finalizeAsec: Invalid asec id \"%s\"", id);
510 if (findAsec(id, asecFileName, sizeof(asecFileName))) {
511 SLOGE("Couldn't find ASEC %s", id);
516 if (!asecHash(id, idHash, sizeof(idHash))) {
517 SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
521 if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
522 SLOGE("Unable to finalize %s (%s)", id, strerror(errno));
526 unsigned int nr_sec = 0;
527 struct asec_superblock sb;
529 if (Loop::lookupInfo(loopDevice, &sb, &nr_sec)) {
533 int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
534 if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
535 SLOGE("ASEC finalize failed: couldn't construct mountPoint");
540 if (sb.c_opts & ASEC_SB_C_OPTS_EXT4) {
541 result = Ext4::doMount(loopDevice, mountPoint, true, true, true);
543 result = Fat::doMount(loopDevice, mountPoint, true, true, true, 0, 0, 0227, false);
547 SLOGE("ASEC finalize mount failed (%s)", strerror(errno));
552 SLOGD("ASEC %s finalized", id);
557 int VolumeManager::fixupAsecPermissions(const char *id, gid_t gid, const char* filename) {
558 char asecFileName[255];
559 char loopDevice[255];
560 char mountPoint[255];
563 SLOGE("Group ID is not in application range");
567 if (!isLegalAsecId(id)) {
568 SLOGE("fixupAsecPermissions: Invalid asec id \"%s\"", id);
573 if (findAsec(id, asecFileName, sizeof(asecFileName))) {
574 SLOGE("Couldn't find ASEC %s", id);
579 if (!asecHash(id, idHash, sizeof(idHash))) {
580 SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
584 if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
585 SLOGE("Unable fix permissions during lookup on %s (%s)", id, strerror(errno));
589 unsigned int nr_sec = 0;
590 struct asec_superblock sb;
592 if (Loop::lookupInfo(loopDevice, &sb, &nr_sec)) {
596 int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
597 if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
598 SLOGE("Unable remount to fix permissions for %s: couldn't construct mountpoint", id);
603 if ((sb.c_opts & ASEC_SB_C_OPTS_EXT4) == 0) {
607 int ret = Ext4::doMount(loopDevice, mountPoint,
608 false /* read-only */,
610 false /* executable */);
612 SLOGE("Unable remount to fix permissions for %s (%s)", id, strerror(errno));
616 char *paths[] = { mountPoint, NULL };
618 FTS *fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, NULL);
620 // Traverse the entire hierarchy and chown to system UID.
621 for (FTSENT *ftsent = fts_read(fts); ftsent != NULL; ftsent = fts_read(fts)) {
622 // We don't care about the lost+found directory.
623 if (!strcmp(ftsent->fts_name, "lost+found")) {
628 * There can only be one file marked as private right now.
629 * This should be more robust, but it satisfies the requirements
630 * we have for right now.
632 const bool privateFile = !strcmp(ftsent->fts_name, filename);
634 int fd = open(ftsent->fts_accpath, O_NOFOLLOW);
636 SLOGE("Couldn't open file %s: %s", ftsent->fts_accpath, strerror(errno));
641 result |= fchown(fd, AID_SYSTEM, privateFile? gid : AID_SYSTEM);
643 if (ftsent->fts_info & FTS_D) {
644 result |= fchmod(fd, 0755);
645 } else if (ftsent->fts_info & FTS_F) {
646 result |= fchmod(fd, privateFile ? 0640 : 0644);
649 if (selinux_android_restorecon(ftsent->fts_path, 0) < 0) {
650 SLOGE("restorecon failed for %s: %s\n", ftsent->fts_path, strerror(errno));
658 // Finally make the directory readable by everyone.
659 int dirfd = open(mountPoint, O_DIRECTORY);
660 if (dirfd < 0 || fchmod(dirfd, 0755)) {
661 SLOGE("Couldn't change owner of existing directory %s: %s", mountPoint, strerror(errno));
669 result |= Ext4::doMount(loopDevice, mountPoint,
670 true /* read-only */,
675 SLOGE("ASEC fix permissions failed (%s)", strerror(errno));
680 SLOGD("ASEC %s permissions fixed", id);
685 int VolumeManager::renameAsec(const char *id1, const char *id2) {
686 char asecFilename1[255];
688 char mountPoint[255];
692 if (!isLegalAsecId(id1)) {
693 SLOGE("renameAsec: Invalid asec id1 \"%s\"", id1);
698 if (!isLegalAsecId(id2)) {
699 SLOGE("renameAsec: Invalid asec id2 \"%s\"", id2);
704 if (findAsec(id1, asecFilename1, sizeof(asecFilename1), &dir)) {
705 SLOGE("Couldn't find ASEC %s", id1);
709 asprintf(&asecFilename2, "%s/%s.asec", dir, id2);
711 int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id1);
712 if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
713 SLOGE("Rename failed: couldn't construct mountpoint");
717 if (isMountpointMounted(mountPoint)) {
718 SLOGW("Rename attempt when src mounted");
723 written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id2);
724 if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
725 SLOGE("Rename failed: couldn't construct mountpoint2");
729 if (isMountpointMounted(mountPoint)) {
730 SLOGW("Rename attempt when dst mounted");
735 if (!access(asecFilename2, F_OK)) {
736 SLOGE("Rename attempt when dst exists");
741 if (rename(asecFilename1, asecFilename2)) {
742 SLOGE("Rename of '%s' to '%s' failed (%s)", asecFilename1, asecFilename2, strerror(errno));
754 #define UNMOUNT_RETRIES 5
755 #define UNMOUNT_SLEEP_BETWEEN_RETRY_MS (1000 * 1000)
756 int VolumeManager::unmountAsec(const char *id, bool force) {
757 char asecFileName[255];
758 char mountPoint[255];
760 if (!isLegalAsecId(id)) {
761 SLOGE("unmountAsec: Invalid asec id \"%s\"", id);
766 if (findAsec(id, asecFileName, sizeof(asecFileName))) {
767 SLOGE("Couldn't find ASEC %s", id);
771 int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
772 if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
773 SLOGE("ASEC unmount failed for %s: couldn't construct mountpoint", id);
778 if (!asecHash(id, idHash, sizeof(idHash))) {
779 SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
783 return unmountLoopImage(id, idHash, asecFileName, mountPoint, force);
786 int VolumeManager::unmountObb(const char *fileName, bool force) {
787 char mountPoint[255];
790 if (!asecHash(fileName, idHash, sizeof(idHash))) {
791 SLOGE("Hash of '%s' failed (%s)", fileName, strerror(errno));
795 int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::LOOPDIR, idHash);
796 if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
797 SLOGE("OBB unmount failed for %s: couldn't construct mountpoint", fileName);
801 return unmountLoopImage(fileName, idHash, fileName, mountPoint, force);
804 int VolumeManager::unmountLoopImage(const char *id, const char *idHash,
805 const char *fileName, const char *mountPoint, bool force) {
806 if (!isMountpointMounted(mountPoint)) {
807 SLOGE("Unmount request for %s when not mounted", id);
813 for (i = 1; i <= UNMOUNT_RETRIES; i++) {
814 rc = umount(mountPoint);
818 if (rc && (errno == EINVAL || errno == ENOENT)) {
819 SLOGI("Container %s unmounted OK", id);
823 SLOGW("%s unmount attempt %d failed (%s)",
824 id, i, strerror(errno));
826 int action = 0; // default is to just complain
829 if (i > (UNMOUNT_RETRIES - 2))
830 action = 2; // SIGKILL
831 else if (i > (UNMOUNT_RETRIES - 3))
832 action = 1; // SIGHUP
835 Process::killProcessesWithOpenFiles(mountPoint, action);
836 usleep(UNMOUNT_SLEEP_BETWEEN_RETRY_MS);
841 SLOGE("Failed to unmount container %s (%s)", id, strerror(errno));
848 if (!rmdir(mountPoint)) {
852 SLOGW("Failed to rmdir %s (%s)", mountPoint, strerror(errno));
853 usleep(UNMOUNT_SLEEP_BETWEEN_RETRY_MS);
857 SLOGE("Timed out trying to rmdir %s (%s)", mountPoint, strerror(errno));
860 if (Devmapper::destroy(idHash) && errno != ENXIO) {
861 SLOGE("Failed to destroy devmapper instance (%s)", strerror(errno));
864 char loopDevice[255];
865 if (!Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
866 Loop::destroyByDevice(loopDevice);
868 SLOGW("Failed to find loop device for {%s} (%s)", fileName, strerror(errno));
871 AsecIdCollection::iterator it;
872 for (it = mActiveContainers->begin(); it != mActiveContainers->end(); ++it) {
873 ContainerData* cd = *it;
874 if (!strcmp(cd->id, id)) {
876 mActiveContainers->erase(it);
880 if (it == mActiveContainers->end()) {
881 SLOGW("mActiveContainers is inconsistent!");
886 int VolumeManager::destroyAsec(const char *id, bool force) {
887 char asecFileName[255];
888 char mountPoint[255];
890 if (!isLegalAsecId(id)) {
891 SLOGE("destroyAsec: Invalid asec id \"%s\"", id);
896 if (findAsec(id, asecFileName, sizeof(asecFileName))) {
897 SLOGE("Couldn't find ASEC %s", id);
901 int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
902 if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
903 SLOGE("ASEC destroy failed for %s: couldn't construct mountpoint", id);
907 if (isMountpointMounted(mountPoint)) {
909 SLOGD("Unmounting container before destroy");
911 if (unmountAsec(id, force)) {
912 SLOGE("Failed to unmount asec %s for destroy (%s)", id, strerror(errno));
917 if (unlink(asecFileName)) {
918 SLOGE("Failed to unlink asec '%s' (%s)", asecFileName, strerror(errno));
923 SLOGD("ASEC %s destroyed", id);
929 * Legal ASEC ids consist of alphanumeric characters, '-',
930 * '_', or '.'. ".." is not allowed. The first or last character
931 * of the ASEC id cannot be '.' (dot).
933 bool VolumeManager::isLegalAsecId(const char *id) const {
935 size_t len = strlen(id);
940 if ((id[0] == '.') || (id[len - 1] == '.')) {
944 for (i = 0; i < len; i++) {
946 // i=0 is guaranteed never to have a dot. See above.
947 if (id[i-1] == '.') return false;
950 if (id[i] == '_' || id[i] == '-') continue;
951 if (id[i] >= 'a' && id[i] <= 'z') continue;
952 if (id[i] >= 'A' && id[i] <= 'Z') continue;
953 if (id[i] >= '0' && id[i] <= '9') continue;
960 bool VolumeManager::isAsecInDirectory(const char *dir, const char *asecName) const {
961 int dirfd = open(dir, O_DIRECTORY);
963 SLOGE("Couldn't open internal ASEC dir (%s)", strerror(errno));
969 if (!faccessat(dirfd, asecName, F_OK, AT_SYMLINK_NOFOLLOW)) {
978 int VolumeManager::findAsec(const char *id, char *asecPath, size_t asecPathLen,
979 const char **directory) const {
981 const int idLen = strlen(id);
984 if (!isLegalAsecId(id)) {
985 SLOGE("findAsec: Invalid asec id \"%s\"", id);
990 if (asprintf(&asecName, "%s.asec", id) < 0) {
991 SLOGE("Couldn't allocate string to write ASEC name");
996 if (isAsecInDirectory(Volume::SEC_ASECDIR_INT, asecName)) {
997 dir = Volume::SEC_ASECDIR_INT;
998 } else if (isAsecInDirectory(Volume::SEC_ASECDIR_EXT, asecName)) {
999 dir = Volume::SEC_ASECDIR_EXT;
1005 if (directory != NULL) {
1009 if (asecPath != NULL) {
1010 int written = snprintf(asecPath, asecPathLen, "%s/%s", dir, asecName);
1011 if ((written < 0) || (size_t(written) >= asecPathLen)) {
1012 SLOGE("findAsec failed for %s: couldn't construct ASEC path", id);
1022 int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid) {
1023 char asecFileName[255];
1024 char mountPoint[255];
1026 if (!isLegalAsecId(id)) {
1027 SLOGE("mountAsec: Invalid asec id \"%s\"", id);
1032 if (findAsec(id, asecFileName, sizeof(asecFileName))) {
1033 SLOGE("Couldn't find ASEC %s", id);
1037 int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
1038 if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
1039 SLOGE("ASEC mount failed for %s: couldn't construct mountpoint", id);
1043 if (isMountpointMounted(mountPoint)) {
1044 SLOGE("ASEC %s already mounted", id);
1050 if (!asecHash(id, idHash, sizeof(idHash))) {
1051 SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
1055 char loopDevice[255];
1056 if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
1057 if (Loop::create(idHash, asecFileName, loopDevice, sizeof(loopDevice))) {
1058 SLOGE("ASEC loop device creation failed (%s)", strerror(errno));
1062 SLOGD("New loop device created at %s", loopDevice);
1066 SLOGD("Found active loopback for %s at %s", asecFileName, loopDevice);
1071 bool cleanupDm = false;
1073 unsigned int nr_sec = 0;
1074 struct asec_superblock sb;
1076 if (Loop::lookupInfo(loopDevice, &sb, &nr_sec)) {
1081 SLOGD("Container sb magic/ver (%.8x/%.2x)", sb.magic, sb.ver);
1083 if (sb.magic != ASEC_SB_MAGIC || sb.ver != ASEC_SB_VER) {
1084 SLOGE("Bad container magic/version (%.8x/%.2x)", sb.magic, sb.ver);
1085 Loop::destroyByDevice(loopDevice);
1086 errno = EMEDIUMTYPE;
1089 nr_sec--; // We don't want the devmapping to extend onto our superblock
1091 if (strcmp(key, "none")) {
1092 if (Devmapper::lookupActive(idHash, dmDevice, sizeof(dmDevice))) {
1093 if (Devmapper::create(idHash, loopDevice, key, nr_sec,
1094 dmDevice, sizeof(dmDevice))) {
1095 SLOGE("ASEC device mapping failed (%s)", strerror(errno));
1096 Loop::destroyByDevice(loopDevice);
1100 SLOGD("New devmapper instance created at %s", dmDevice);
1104 SLOGD("Found active devmapper for %s at %s", asecFileName, dmDevice);
1109 strcpy(dmDevice, loopDevice);
1112 if (mkdir(mountPoint, 0000)) {
1113 if (errno != EEXIST) {
1114 SLOGE("Mountpoint creation failed (%s)", strerror(errno));
1116 Devmapper::destroy(idHash);
1118 Loop::destroyByDevice(loopDevice);
1124 * The device mapper node needs to be created. Sometimes it takes a
1125 * while. Wait for up to 1 second. We could also inspect incoming uevents,
1126 * but that would take more effort.
1130 if (!access(dmDevice, F_OK) || errno != ENOENT) {
1137 if (sb.c_opts & ASEC_SB_C_OPTS_EXT4) {
1138 result = Ext4::doMount(dmDevice, mountPoint, true, false, true);
1140 result = Fat::doMount(dmDevice, mountPoint, true, false, true, ownerUid, 0, 0222, false);
1144 SLOGE("ASEC mount failed (%s)", strerror(errno));
1146 Devmapper::destroy(idHash);
1148 Loop::destroyByDevice(loopDevice);
1152 mActiveContainers->push_back(new ContainerData(strdup(id), ASEC));
1154 SLOGD("ASEC %s mounted", id);
1159 Volume* VolumeManager::getVolumeForFile(const char *fileName) {
1160 VolumeCollection::iterator i;
1162 for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
1163 const char* mountPoint = (*i)->getFuseMountpoint();
1164 if (!strncmp(fileName, mountPoint, strlen(mountPoint))) {
1173 * Mounts an image file <code>img</code>.
1175 int VolumeManager::mountObb(const char *img, const char *key, int ownerGid) {
1176 char mountPoint[255];
1179 if (!asecHash(img, idHash, sizeof(idHash))) {
1180 SLOGE("Hash of '%s' failed (%s)", img, strerror(errno));
1184 int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::LOOPDIR, idHash);
1185 if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
1186 SLOGE("OBB mount failed for %s: couldn't construct mountpoint", img);
1190 if (isMountpointMounted(mountPoint)) {
1191 SLOGE("Image %s already mounted", img);
1196 char loopDevice[255];
1197 if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
1198 if (Loop::create(idHash, img, loopDevice, sizeof(loopDevice))) {
1199 SLOGE("Image loop device creation failed (%s)", strerror(errno));
1203 SLOGD("New loop device created at %s", loopDevice);
1207 SLOGD("Found active loopback for %s at %s", img, loopDevice);
1212 bool cleanupDm = false;
1214 unsigned int nr_sec = 0;
1216 if ((fd = open(loopDevice, O_RDWR)) < 0) {
1217 SLOGE("Failed to open loopdevice (%s)", strerror(errno));
1218 Loop::destroyByDevice(loopDevice);
1222 if (ioctl(fd, BLKGETSIZE, &nr_sec)) {
1223 SLOGE("Failed to get loop size (%s)", strerror(errno));
1224 Loop::destroyByDevice(loopDevice);
1231 if (strcmp(key, "none")) {
1232 if (Devmapper::lookupActive(idHash, dmDevice, sizeof(dmDevice))) {
1233 if (Devmapper::create(idHash, loopDevice, key, nr_sec,
1234 dmDevice, sizeof(dmDevice))) {
1235 SLOGE("ASEC device mapping failed (%s)", strerror(errno));
1236 Loop::destroyByDevice(loopDevice);
1240 SLOGD("New devmapper instance created at %s", dmDevice);
1244 SLOGD("Found active devmapper for %s at %s", img, dmDevice);
1249 strcpy(dmDevice, loopDevice);
1252 if (mkdir(mountPoint, 0755)) {
1253 if (errno != EEXIST) {
1254 SLOGE("Mountpoint creation failed (%s)", strerror(errno));
1256 Devmapper::destroy(idHash);
1258 Loop::destroyByDevice(loopDevice);
1263 if (Fat::doMount(dmDevice, mountPoint, true, false, true, 0, ownerGid,
1265 SLOGE("Image mount failed (%s)", strerror(errno));
1267 Devmapper::destroy(idHash);
1269 Loop::destroyByDevice(loopDevice);
1273 mActiveContainers->push_back(new ContainerData(strdup(img), OBB));
1275 SLOGD("Image %s mounted", img);
1280 int VolumeManager::mountVolume(const char *label) {
1281 Volume *v = lookupVolume(label);
1288 return v->mountVol();
1291 int VolumeManager::listMountedObbs(SocketClient* cli) {
1293 char mount_path[256];
1298 if (!(fp = fopen("/proc/mounts", "r"))) {
1299 SLOGE("Error opening /proc/mounts (%s)", strerror(errno));
1303 // Create a string to compare against that has a trailing slash
1304 int loopDirLen = strlen(Volume::LOOPDIR);
1305 char loopDir[loopDirLen + 2];
1306 strcpy(loopDir, Volume::LOOPDIR);
1307 loopDir[loopDirLen++] = '/';
1308 loopDir[loopDirLen] = '\0';
1310 while(fgets(line, sizeof(line), fp)) {
1311 line[strlen(line)-1] = '\0';
1315 * /dev/block/loop0 /mnt/obb/fc99df1323fd36424f864dcb76b76d65 ...
1317 sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
1319 if (!strncmp(mount_path, loopDir, loopDirLen)) {
1320 int fd = open(device, O_RDONLY);
1322 struct loop_info64 li;
1323 if (ioctl(fd, LOOP_GET_STATUS64, &li) >= 0) {
1324 cli->sendMsg(ResponseCode::AsecListResult,
1325 (const char*) li.lo_file_name, false);
1336 int VolumeManager::shareEnabled(const char *label, const char *method, bool *enabled) {
1337 Volume *v = lookupVolume(label);
1344 if (strcmp(method, "ums")) {
1349 if (v->getState() != Volume::State_Shared) {
1357 int VolumeManager::shareVolume(const char *label, const char *method) {
1358 Volume *v = lookupVolume(label);
1366 * Eventually, we'll want to support additional share back-ends,
1367 * some of which may work while the media is mounted. For now,
1368 * we just support UMS
1370 if (strcmp(method, "ums")) {
1375 if (v->getState() == Volume::State_NoMedia) {
1380 if (v->getState() != Volume::State_Idle) {
1381 // You need to unmount manually befoe sharing
1386 if (mVolManagerDisabled) {
1391 dev_t d = v->getShareDevice();
1392 if ((MAJOR(d) == 0) && (MINOR(d) == 0)) {
1393 // This volume does not support raw disk access
1400 int written = snprintf(nodepath,
1401 sizeof(nodepath), "/dev/block/vold/%d:%d",
1402 major(d), minor(d));
1404 if ((written < 0) || (size_t(written) >= sizeof(nodepath))) {
1405 SLOGE("shareVolume failed: couldn't construct nodepath");
1409 if ((fd = open(MASS_STORAGE_FILE_PATH, O_WRONLY)) < 0) {
1410 SLOGE("Unable to open ums lunfile (%s)", strerror(errno));
1414 if (write(fd, nodepath, strlen(nodepath)) < 0) {
1415 SLOGE("Unable to write to ums lunfile (%s)", strerror(errno));
1421 v->handleVolumeShared();
1422 if (mUmsSharingCount++ == 0) {
1424 mSavedDirtyRatio = -1; // in case we fail
1425 if ((fp = fopen("/proc/sys/vm/dirty_ratio", "r+"))) {
1427 if (fgets(line, sizeof(line), fp) && sscanf(line, "%d", &mSavedDirtyRatio)) {
1428 fprintf(fp, "%d\n", mUmsDirtyRatio);
1430 SLOGE("Failed to read dirty_ratio (%s)", strerror(errno));
1434 SLOGE("Failed to open /proc/sys/vm/dirty_ratio (%s)", strerror(errno));
1440 int VolumeManager::unshareVolume(const char *label, const char *method) {
1441 Volume *v = lookupVolume(label);
1448 if (strcmp(method, "ums")) {
1453 if (v->getState() != Volume::State_Shared) {
1459 if ((fd = open(MASS_STORAGE_FILE_PATH, O_WRONLY)) < 0) {
1460 SLOGE("Unable to open ums lunfile (%s)", strerror(errno));
1465 if (write(fd, &ch, 1) < 0) {
1466 SLOGE("Unable to write to ums lunfile (%s)", strerror(errno));
1472 v->handleVolumeUnshared();
1473 if (--mUmsSharingCount == 0 && mSavedDirtyRatio != -1) {
1475 if ((fp = fopen("/proc/sys/vm/dirty_ratio", "r+"))) {
1476 fprintf(fp, "%d\n", mSavedDirtyRatio);
1479 SLOGE("Failed to open /proc/sys/vm/dirty_ratio (%s)", strerror(errno));
1481 mSavedDirtyRatio = -1;
1486 extern "C" int vold_disableVol(const char *label) {
1487 VolumeManager *vm = VolumeManager::Instance();
1488 vm->disableVolumeManager();
1489 vm->unshareVolume(label, "ums");
1490 return vm->unmountVolume(label, true, false);
1493 extern "C" int vold_getNumDirectVolumes(void) {
1494 VolumeManager *vm = VolumeManager::Instance();
1495 return vm->getNumDirectVolumes();
1498 int VolumeManager::getNumDirectVolumes(void) {
1499 VolumeCollection::iterator i;
1502 for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
1503 if ((*i)->getShareDevice() != (dev_t)0) {
1510 extern "C" int vold_getDirectVolumeList(struct volume_info *vol_list) {
1511 VolumeManager *vm = VolumeManager::Instance();
1512 return vm->getDirectVolumeList(vol_list);
1515 int VolumeManager::getDirectVolumeList(struct volume_info *vol_list) {
1516 VolumeCollection::iterator i;
1520 for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
1521 if ((d=(*i)->getShareDevice()) != (dev_t)0) {
1522 (*i)->getVolInfo(&vol_list[n]);
1523 snprintf(vol_list[n].blk_dev, sizeof(vol_list[n].blk_dev),
1524 "/dev/block/vold/%d:%d", major(d), minor(d));
1532 int VolumeManager::unmountVolume(const char *label, bool force, bool revert) {
1533 Volume *v = lookupVolume(label);
1540 if (v->getState() == Volume::State_NoMedia) {
1545 if (v->getState() != Volume::State_Mounted) {
1546 SLOGW("Attempt to unmount volume which isn't mounted (%d)\n",
1549 return UNMOUNT_NOT_MOUNTED_ERR;
1552 cleanupAsec(v, force);
1554 return v->unmountVol(force, revert);
1557 extern "C" int vold_unmountAllAsecs(void) {
1560 VolumeManager *vm = VolumeManager::Instance();
1561 rc = vm->unmountAllAsecsInDir(Volume::SEC_ASECDIR_EXT);
1562 if (vm->unmountAllAsecsInDir(Volume::SEC_ASECDIR_INT)) {
1568 #define ID_BUF_LEN 256
1569 #define ASEC_SUFFIX ".asec"
1570 #define ASEC_SUFFIX_LEN (sizeof(ASEC_SUFFIX) - 1)
1571 int VolumeManager::unmountAllAsecsInDir(const char *directory) {
1572 DIR *d = opendir(directory);
1576 SLOGE("Could not open asec dir %s", directory);
1580 size_t dirent_len = offsetof(struct dirent, d_name) +
1581 fpathconf(dirfd(d), _PC_NAME_MAX) + 1;
1583 struct dirent *dent = (struct dirent *) malloc(dirent_len);
1585 SLOGE("Failed to allocate memory for asec dir");
1589 struct dirent *result;
1590 while (!readdir_r(d, dent, &result) && result != NULL) {
1591 if (dent->d_name[0] == '.')
1593 if (dent->d_type != DT_REG)
1595 size_t name_len = strlen(dent->d_name);
1596 if (name_len > 5 && name_len < (ID_BUF_LEN + ASEC_SUFFIX_LEN - 1) &&
1597 !strcmp(&dent->d_name[name_len - 5], ASEC_SUFFIX)) {
1598 char id[ID_BUF_LEN];
1599 strlcpy(id, dent->d_name, name_len - 4);
1600 if (unmountAsec(id, true)) {
1601 /* Register the error, but try to unmount more asecs */
1614 * Looks up a volume by it's label or mount-point
1616 Volume *VolumeManager::lookupVolume(const char *label) {
1617 VolumeCollection::iterator i;
1619 for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
1620 if (label[0] == '/') {
1621 if (!strcmp(label, (*i)->getFuseMountpoint()))
1624 if (!strcmp(label, (*i)->getLabel()))
1631 bool VolumeManager::isMountpointMounted(const char *mp)
1634 char mount_path[256];
1639 if (!(fp = fopen("/proc/mounts", "r"))) {
1640 SLOGE("Error opening /proc/mounts (%s)", strerror(errno));
1644 while(fgets(line, sizeof(line), fp)) {
1645 line[strlen(line)-1] = '\0';
1646 sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
1647 if (!strcmp(mount_path, mp)) {
1657 int VolumeManager::cleanupAsec(Volume *v, bool force) {
1660 char asecFileName[255];
1662 AsecIdCollection removeAsec;
1663 AsecIdCollection removeObb;
1665 for (AsecIdCollection::iterator it = mActiveContainers->begin(); it != mActiveContainers->end();
1667 ContainerData* cd = *it;
1669 if (cd->type == ASEC) {
1670 if (findAsec(cd->id, asecFileName, sizeof(asecFileName))) {
1671 SLOGE("Couldn't find ASEC %s; cleaning up", cd->id);
1672 removeAsec.push_back(cd);
1674 SLOGD("Found ASEC at path %s", asecFileName);
1675 if (!strncmp(asecFileName, Volume::SEC_ASECDIR_EXT,
1676 strlen(Volume::SEC_ASECDIR_EXT))) {
1677 removeAsec.push_back(cd);
1680 } else if (cd->type == OBB) {
1681 if (v == getVolumeForFile(cd->id)) {
1682 removeObb.push_back(cd);
1685 SLOGE("Unknown container type %d!", cd->type);
1689 for (AsecIdCollection::iterator it = removeAsec.begin(); it != removeAsec.end(); ++it) {
1690 ContainerData *cd = *it;
1691 SLOGI("Unmounting ASEC %s (dependent on %s)", cd->id, v->getLabel());
1692 if (unmountAsec(cd->id, force)) {
1693 SLOGE("Failed to unmount ASEC %s (%s)", cd->id, strerror(errno));
1698 for (AsecIdCollection::iterator it = removeObb.begin(); it != removeObb.end(); ++it) {
1699 ContainerData *cd = *it;
1700 SLOGI("Unmounting OBB %s (dependent on %s)", cd->id, v->getLabel());
1701 if (unmountObb(cd->id, force)) {
1702 SLOGE("Failed to unmount OBB %s (%s)", cd->id, strerror(errno));
1710 int VolumeManager::mkdirs(char* path) {
1711 // Require that path lives under a volume we manage
1712 const char* emulated_source = getenv("EMULATED_STORAGE_SOURCE");
1713 const char* root = NULL;
1714 if (emulated_source && !strncmp(path, emulated_source, strlen(emulated_source))) {
1715 root = emulated_source;
1717 Volume* vol = getVolumeForFile(path);
1719 root = vol->getMountpoint();
1724 SLOGE("Failed to find volume for %s", path);
1728 /* fs_mkdirs() does symlink checking and relative path enforcement */
1729 return fs_mkdirs(path, 0700);