am b32ebf30: Merge "Retry unmounts in ext4 encryption"
* commit 'b32ebf308a95ffa937d9b9a36417cdea70add0e5':
Retry unmounts in ext4 encryption
diff --git a/Android.mk b/Android.mk
index d4a178a..c178c62 100644
--- a/Android.mk
+++ b/Android.mk
@@ -6,8 +6,6 @@
VoldCommand.cpp \
NetlinkManager.cpp \
NetlinkHandler.cpp \
- Volume.cpp \
- DirectVolume.cpp \
Process.cpp \
Ext4.cpp \
Fat.cpp \
@@ -18,7 +16,14 @@
Ext4Crypt.cpp \
VoldUtil.c \
fstrim.c \
- cryptfs.c
+ cryptfs.c \
+ Disk.cpp \
+ VolumeBase.cpp \
+ PublicVolume.cpp \
+ PrivateVolume.cpp \
+ EmulatedVolume.cpp \
+ Utils.cpp \
+ MoveTask.cpp \
common_c_includes := \
system/extras/ext4_utils \
@@ -43,7 +48,8 @@
libselinux \
libutils \
libhardware \
- libsoftkeymaster
+ libsoftkeymaster \
+ libbase \
common_static_libraries := \
libfs_mgr \
@@ -53,7 +59,7 @@
libbatteryservice
vold_conlyflags := -std=c11
-vold_cflags := -Werror -Wall -Wno-missing-field-initializers
+vold_cflags := -Werror -Wall -Wno-missing-field-initializers -Wno-unused-variable -Wno-unused-parameter
include $(CLEAR_VARS)
diff --git a/CommandListener.cpp b/CommandListener.cpp
index 73a26e4..11eec02 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -34,12 +34,14 @@
#include "CommandListener.h"
#include "VolumeManager.h"
+#include "VolumeBase.h"
#include "ResponseCode.h"
#include "Process.h"
#include "Loop.h"
#include "Devmapper.h"
#include "cryptfs.h"
#include "fstrim.h"
+#include "MoveTask.h"
#define DUMP_ARGS 0
@@ -86,6 +88,14 @@
void CommandListener::dumpArgs(int /*argc*/, char ** /*argv*/, int /*argObscure*/) { }
#endif
+int CommandListener::sendGenericOkFail(SocketClient *cli, int cond) {
+ if (!cond) {
+ return cli->sendMsg(ResponseCode::CommandOkay, "Command succeeded", false);
+ } else {
+ return cli->sendMsg(ResponseCode::OperationFailed, "Command failed", false);
+ }
+}
+
CommandListener::DumpCmd::DumpCmd() :
VoldCommand("dump") {
}
@@ -129,99 +139,108 @@
}
VolumeManager *vm = VolumeManager::Instance();
- int rc = 0;
+ std::lock_guard<std::mutex> lock(vm->getLock());
- if (!strcmp(argv[1], "list")) {
- bool broadcast = argc >= 3 && !strcmp(argv[2], "broadcast");
- return vm->listVolumes(cli, broadcast);
- } else if (!strcmp(argv[1], "debug")) {
- if (argc != 3 || (argc == 3 && (strcmp(argv[2], "off") && strcmp(argv[2], "on")))) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume debug <off/on>", false);
- return 0;
- }
- vm->setDebug(!strcmp(argv[2], "on") ? true : false);
- } else if (!strcmp(argv[1], "mount")) {
- if (argc != 3) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume mount <path>", false);
- return 0;
- }
- rc = vm->mountVolume(argv[2]);
- } else if (!strcmp(argv[1], "unmount")) {
- if (argc < 3 || argc > 4 ||
- ((argc == 4 && strcmp(argv[3], "force")) &&
- (argc == 4 && strcmp(argv[3], "force_and_revert")))) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume unmount <path> [force|force_and_revert]", false);
- return 0;
+ // TODO: tease out methods not directly related to volumes
+
+ std::string cmd(argv[1]);
+ if (cmd == "reset") {
+ return sendGenericOkFail(cli, vm->reset());
+
+ } else if (cmd == "shutdown") {
+ return sendGenericOkFail(cli, vm->shutdown());
+
+ } else if (cmd == "debug") {
+ return sendGenericOkFail(cli, vm->setDebug(true));
+
+ } else if (cmd == "partition" && argc > 3) {
+ // partition [diskId] [public|private|mixed] [ratio]
+ std::string id(argv[2]);
+ auto disk = vm->findDisk(id);
+ if (disk == nullptr) {
+ return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown disk", false);
}
- bool force = false;
- bool revert = false;
- if (argc >= 4 && !strcmp(argv[3], "force")) {
- force = true;
- } else if (argc >= 4 && !strcmp(argv[3], "force_and_revert")) {
- force = true;
- revert = true;
- }
- rc = vm->unmountVolume(argv[2], force, revert);
- } else if (!strcmp(argv[1], "format")) {
- if (argc < 3 || argc > 4 ||
- (argc == 4 && strcmp(argv[3], "wipe"))) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume format <path> [wipe]", false);
- return 0;
- }
- bool wipe = false;
- if (argc >= 4 && !strcmp(argv[3], "wipe")) {
- wipe = true;
- }
- rc = vm->formatVolume(argv[2], wipe);
- } else if (!strcmp(argv[1], "share")) {
- if (argc != 4) {
- cli->sendMsg(ResponseCode::CommandSyntaxError,
- "Usage: volume share <path> <method>", false);
- return 0;
- }
- rc = vm->shareVolume(argv[2], argv[3]);
- } else if (!strcmp(argv[1], "unshare")) {
- if (argc != 4) {
- cli->sendMsg(ResponseCode::CommandSyntaxError,
- "Usage: volume unshare <path> <method>", false);
- return 0;
- }
- rc = vm->unshareVolume(argv[2], argv[3]);
- } else if (!strcmp(argv[1], "shared")) {
- bool enabled = false;
- if (argc != 4) {
- cli->sendMsg(ResponseCode::CommandSyntaxError,
- "Usage: volume shared <path> <method>", false);
- return 0;
- }
-
- if (vm->shareEnabled(argv[2], argv[3], &enabled)) {
- cli->sendMsg(
- ResponseCode::OperationFailed, "Failed to determine share enable state", true);
+ std::string type(argv[3]);
+ if (type == "public") {
+ return sendGenericOkFail(cli, disk->partitionPublic());
+ } else if (type == "private") {
+ return sendGenericOkFail(cli, disk->partitionPrivate());
+ } else if (type == "mixed") {
+ if (argc < 4) {
+ return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
+ }
+ int frac = atoi(argv[4]);
+ return sendGenericOkFail(cli, disk->partitionMixed(frac));
} else {
- cli->sendMsg(ResponseCode::ShareEnabledResult,
- (enabled ? "Share enabled" : "Share disabled"), false);
+ return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
}
- return 0;
- } else if (!strcmp(argv[1], "mkdirs")) {
- if (argc != 3) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume mkdirs <path>", false);
- return 0;
+
+ } else if (cmd == "mkdirs" && argc > 2) {
+ // mkdirs [path]
+ return sendGenericOkFail(cli, vm->mkdirs(argv[2]));
+
+ } else if (cmd == "start_user" && argc > 2) {
+ // start_user [user]
+ return sendGenericOkFail(cli, vm->startUser(atoi(argv[2])));
+
+ } else if (cmd == "cleanup_user" && argc > 2) {
+ // cleanup_user [user]
+ return sendGenericOkFail(cli, vm->cleanupUser(atoi(argv[2])));
+
+ } else if (cmd == "mount" && argc > 2) {
+ // mount [volId] [flags] [user]
+ std::string id(argv[2]);
+ auto vol = vm->findVolume(id);
+ if (vol == nullptr) {
+ return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
}
- rc = vm->mkdirs(argv[2]);
- } else {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume cmd", false);
+
+ int mountFlags = (argc > 3) ? atoi(argv[3]) : 0;
+ userid_t mountUserId = (argc > 4) ? atoi(argv[4]) : -1;
+
+ vol->setMountFlags(mountFlags);
+ vol->setMountUserId(mountUserId);
+
+ int res = vol->mount();
+ if (mountFlags & android::vold::VolumeBase::MountFlags::kPrimary) {
+ vm->setPrimary(vol);
+ }
+ return sendGenericOkFail(cli, res);
+
+ } else if (cmd == "unmount" && argc > 2) {
+ // unmount [volId]
+ std::string id(argv[2]);
+ auto vol = vm->findVolume(id);
+ if (vol == nullptr) {
+ return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
+ }
+
+ return sendGenericOkFail(cli, vol->unmount());
+
+ } else if (cmd == "format" && argc > 2) {
+ // format [volId]
+ std::string id(argv[2]);
+ auto vol = vm->findVolume(id);
+ if (vol == nullptr) {
+ return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
+ }
+
+ return sendGenericOkFail(cli, vol->format());
+
+ } else if (cmd == "move_storage" && argc > 3) {
+ // move_storage [fromVolId] [toVolId]
+ auto fromVol = vm->findVolume(std::string(argv[2]));
+ auto toVol = vm->findVolume(std::string(argv[3]));
+ if (fromVol == nullptr || toVol == nullptr) {
+ return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown volume", false);
+ }
+
+ (new android::vold::MoveTask(fromVol, toVol))->start();
+ return sendGenericOkFail(cli, 0);
}
- if (!rc) {
- cli->sendMsg(ResponseCode::CommandOkay, "volume operation succeeded", false);
- } else {
- rc = ResponseCode::convertFromErrno();
- cli->sendMsg(rc, "volume operation failed", true);
- }
-
- return 0;
+ return cli->sendMsg(ResponseCode::CommandSyntaxError, nullptr, false);
}
CommandListener::StorageCmd::StorageCmd() :
@@ -346,8 +365,8 @@
if (!strcmp(argv[1], "list")) {
dumpArgs(argc, argv, -1);
- listAsecsInDirectory(cli, Volume::SEC_ASECDIR_EXT);
- listAsecsInDirectory(cli, Volume::SEC_ASECDIR_INT);
+ listAsecsInDirectory(cli, VolumeManager::SEC_ASECDIR_EXT);
+ listAsecsInDirectory(cli, VolumeManager::SEC_ASECDIR_INT);
} else if (!strcmp(argv[1], "create")) {
dumpArgs(argc, argv, 5);
if (argc != 8) {
@@ -616,7 +635,7 @@
if (rc == 0) {
break;
} else if (tries == 0) {
- Process::killProcessesWithOpenFiles(DATA_MNT_POINT, 2);
+ Process::killProcessesWithOpenFiles(DATA_MNT_POINT, SIGKILL);
}
}
} else if (!strcmp(argv[1], "enablefilecrypto")) {
diff --git a/CommandListener.h b/CommandListener.h
index 0bd51d2..8cc1a04 100644
--- a/CommandListener.h
+++ b/CommandListener.h
@@ -18,6 +18,7 @@
#define _COMMANDLISTENER_H__
#include <sysutils/FrameworkListener.h>
+#include <utils/Errors.h>
#include "VoldCommand.h"
class CommandListener : public FrameworkListener {
@@ -27,6 +28,7 @@
private:
static void dumpArgs(int argc, char **argv, int argObscure);
+ static int sendGenericOkFail(SocketClient *cli, int cond);
class DumpCmd : public VoldCommand {
public:
diff --git a/Devmapper.cpp b/Devmapper.cpp
index 703902f..703eade 100644
--- a/Devmapper.cpp
+++ b/Devmapper.cpp
@@ -55,7 +55,7 @@
}
int fd;
- if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
+ if ((fd = open("/dev/device-mapper", O_RDWR | O_CLOEXEC)) < 0) {
SLOGE("Error opening devmapper (%s)", strerror(errno));
free(buffer);
free(buffer2);
@@ -138,7 +138,7 @@
}
int fd;
- if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
+ if ((fd = open("/dev/device-mapper", O_RDWR | O_CLOEXEC)) < 0) {
SLOGE("Error opening devmapper (%s)", strerror(errno));
free(buffer);
return -1;
@@ -172,7 +172,7 @@
}
int fd;
- if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
+ if ((fd = open("/dev/device-mapper", O_RDWR | O_CLOEXEC)) < 0) {
SLOGE("Error opening devmapper (%s)", strerror(errno));
free(buffer);
return -1;
@@ -269,7 +269,7 @@
}
int fd;
- if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
+ if ((fd = open("/dev/device-mapper", O_RDWR | O_CLOEXEC)) < 0) {
SLOGE("Error opening devmapper (%s)", strerror(errno));
free(buffer);
return -1;
diff --git a/DirectVolume.cpp b/DirectVolume.cpp
deleted file mode 100644
index 64d7744..0000000
--- a/DirectVolume.cpp
+++ /dev/null
@@ -1,512 +0,0 @@
-/*
- * 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.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <fnmatch.h>
-
-#include <linux/kdev_t.h>
-
-#define LOG_TAG "DirectVolume"
-
-#include <cutils/log.h>
-#include <sysutils/NetlinkEvent.h>
-
-#include "DirectVolume.h"
-#include "VolumeManager.h"
-#include "ResponseCode.h"
-#include "cryptfs.h"
-
-// #define PARTITION_DEBUG
-
-PathInfo::PathInfo(const char *p)
-{
- warned = false;
- pattern = strdup(p);
-
- if (!strchr(pattern, '*')) {
- patternType = prefix;
- } else {
- patternType = wildcard;
- }
-}
-
-PathInfo::~PathInfo()
-{
- free(pattern);
-}
-
-bool PathInfo::match(const char *path)
-{
- switch (patternType) {
- case prefix:
- {
- bool ret = (strncmp(path, pattern, strlen(pattern)) == 0);
- if (!warned && ret && (strlen(pattern) != strlen(path))) {
- SLOGW("Deprecated implied prefix pattern detected, please use '%s*' instead", pattern);
- warned = true;
- }
- return ret;
- }
- case wildcard:
- return fnmatch(pattern, path, 0) == 0;
- }
- SLOGE("Bad matching type");
- return false;
-}
-
-DirectVolume::DirectVolume(VolumeManager *vm, const fstab_rec* rec, int flags) :
- Volume(vm, rec, flags) {
- mPaths = new PathCollection();
- for (int i = 0; i < MAX_PARTITIONS; i++)
- mPartMinors[i] = -1;
- mPendingPartCount = 0;
- mDiskMajor = -1;
- mDiskMinor = -1;
- mDiskNumParts = 0;
- mIsDecrypted = 0;
-
- if (strcmp(rec->mount_point, "auto") != 0) {
- ALOGE("Vold managed volumes must have auto mount point; ignoring %s",
- rec->mount_point);
- }
-
- char mount[PATH_MAX];
-
- snprintf(mount, PATH_MAX, "%s/%s", Volume::MEDIA_DIR, rec->label);
- mMountpoint = strdup(mount);
- snprintf(mount, PATH_MAX, "%s/%s", Volume::FUSE_DIR, rec->label);
- mFuseMountpoint = strdup(mount);
-
- setState(Volume::State_NoMedia);
-}
-
-DirectVolume::~DirectVolume() {
- PathCollection::iterator it;
-
- for (it = mPaths->begin(); it != mPaths->end(); ++it)
- delete *it;
- delete mPaths;
-}
-
-int DirectVolume::addPath(const char *path) {
- mPaths->push_back(new PathInfo(path));
- return 0;
-}
-
-dev_t DirectVolume::getDiskDevice() {
- return MKDEV(mDiskMajor, mDiskMinor);
-}
-
-dev_t DirectVolume::getShareDevice() {
- if (mPartIdx != -1) {
- return MKDEV(mDiskMajor, mPartIdx);
- } else {
- return MKDEV(mDiskMajor, mDiskMinor);
- }
-}
-
-void DirectVolume::handleVolumeShared() {
- setState(Volume::State_Shared);
-}
-
-void DirectVolume::handleVolumeUnshared() {
- setState(Volume::State_Idle);
-}
-
-int DirectVolume::handleBlockEvent(NetlinkEvent *evt) {
- const char *dp = evt->findParam("DEVPATH");
-
- PathCollection::iterator it;
- for (it = mPaths->begin(); it != mPaths->end(); ++it) {
- if ((*it)->match(dp)) {
- /* We can handle this disk */
- int action = evt->getAction();
- const char *devtype = evt->findParam("DEVTYPE");
-
- if (action == NetlinkEvent::NlActionAdd) {
- int major = atoi(evt->findParam("MAJOR"));
- int minor = atoi(evt->findParam("MINOR"));
- char nodepath[255];
-
- snprintf(nodepath,
- sizeof(nodepath), "/dev/block/vold/%d:%d",
- major, minor);
- if (createDeviceNode(nodepath, major, minor)) {
- SLOGE("Error making device node '%s' (%s)", nodepath,
- strerror(errno));
- }
- if (!strcmp(devtype, "disk")) {
- handleDiskAdded(dp, evt);
- } else {
- handlePartitionAdded(dp, evt);
- }
- /* Send notification iff disk is ready (ie all partitions found) */
- if (getState() == Volume::State_Idle) {
- char msg[255];
-
- snprintf(msg, sizeof(msg),
- "Volume %s %s disk inserted (%d:%d)", getLabel(),
- getFuseMountpoint(), mDiskMajor, mDiskMinor);
- mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,
- msg, false);
- }
- } else if (action == NetlinkEvent::NlActionRemove) {
- if (!strcmp(devtype, "disk")) {
- handleDiskRemoved(dp, evt);
- } else {
- handlePartitionRemoved(dp, evt);
- }
- } else if (action == NetlinkEvent::NlActionChange) {
- if (!strcmp(devtype, "disk")) {
- handleDiskChanged(dp, evt);
- } else {
- handlePartitionChanged(dp, evt);
- }
- } else {
- SLOGW("Ignoring non add/remove/change event");
- }
-
- return 0;
- }
- }
- errno = ENODEV;
- return -1;
-}
-
-void DirectVolume::handleDiskAdded(const char * /*devpath*/,
- NetlinkEvent *evt) {
- mDiskMajor = atoi(evt->findParam("MAJOR"));
- mDiskMinor = atoi(evt->findParam("MINOR"));
-
- const char *tmp = evt->findParam("NPARTS");
- if (tmp) {
- mDiskNumParts = atoi(tmp);
- } else {
- SLOGW("Kernel block uevent missing 'NPARTS'");
- mDiskNumParts = 1;
- }
-
- mPendingPartCount = mDiskNumParts;
- for (int i = 0; i < MAX_PARTITIONS; i++)
- mPartMinors[i] = -1;
-
- if (mDiskNumParts == 0) {
-#ifdef PARTITION_DEBUG
- SLOGD("Dv::diskIns - No partitions - good to go son!");
-#endif
- setState(Volume::State_Idle);
- } else {
-#ifdef PARTITION_DEBUG
- SLOGD("Dv::diskIns - waiting for %d pending partitions", mPendingPartCount);
-#endif
- setState(Volume::State_Pending);
- }
-}
-
-void DirectVolume::handlePartitionAdded(const char *devpath, NetlinkEvent *evt) {
- int major = atoi(evt->findParam("MAJOR"));
- int minor = atoi(evt->findParam("MINOR"));
-
- int part_num;
-
- const char *tmp = evt->findParam("PARTN");
-
- if (tmp) {
- part_num = atoi(tmp);
- } else {
- SLOGW("Kernel block uevent missing 'PARTN'");
- part_num = 1;
- }
-
- if (part_num > MAX_PARTITIONS || part_num < 1) {
- SLOGE("Invalid 'PARTN' value");
- return;
- }
-
- if (part_num > mDiskNumParts) {
- mDiskNumParts = part_num;
- }
-
- if (major != mDiskMajor) {
- SLOGE("Partition '%s' has a different major than its disk!", devpath);
- return;
- }
-#ifdef PARTITION_DEBUG
- SLOGD("Dv:partAdd: part_num = %d, minor = %d\n", part_num, minor);
-#endif
- if (part_num >= MAX_PARTITIONS) {
- SLOGE("Dv:partAdd: ignoring part_num = %d (max: %d)\n", part_num, MAX_PARTITIONS-1);
- } else {
- if ((mPartMinors[part_num - 1] == -1) && mPendingPartCount)
- mPendingPartCount--;
- mPartMinors[part_num -1] = minor;
- }
-
- if (!mPendingPartCount) {
-#ifdef PARTITION_DEBUG
- SLOGD("Dv:partAdd: Got all partitions - ready to rock!");
-#endif
- if (getState() != Volume::State_Formatting) {
- setState(Volume::State_Idle);
- if (mRetryMount == true) {
- mRetryMount = false;
- mountVol();
- }
- }
- } else {
-#ifdef PARTITION_DEBUG
- SLOGD("Dv:partAdd: pending %d disk", mPendingPartCount);
-#endif
- }
-}
-
-void DirectVolume::handleDiskChanged(const char * /*devpath*/,
- NetlinkEvent *evt) {
- int major = atoi(evt->findParam("MAJOR"));
- int minor = atoi(evt->findParam("MINOR"));
-
- if ((major != mDiskMajor) || (minor != mDiskMinor)) {
- return;
- }
-
- SLOGI("Volume %s disk has changed", getLabel());
- const char *tmp = evt->findParam("NPARTS");
- if (tmp) {
- mDiskNumParts = atoi(tmp);
- } else {
- SLOGW("Kernel block uevent missing 'NPARTS'");
- mDiskNumParts = 1;
- }
-
- mPendingPartCount = mDiskNumParts;
- for (int i = 0; i < MAX_PARTITIONS; i++)
- mPartMinors[i] = -1;
-
- if (getState() != Volume::State_Formatting) {
- if (mDiskNumParts == 0) {
- setState(Volume::State_Idle);
- } else {
- setState(Volume::State_Pending);
- }
- }
-}
-
-void DirectVolume::handlePartitionChanged(const char * /*devpath*/,
- NetlinkEvent *evt) {
- int major = atoi(evt->findParam("MAJOR"));
- int minor = atoi(evt->findParam("MINOR"));
- SLOGD("Volume %s %s partition %d:%d changed\n", getLabel(), getMountpoint(), major, minor);
-}
-
-void DirectVolume::handleDiskRemoved(const char * /*devpath*/,
- NetlinkEvent *evt) {
- int major = atoi(evt->findParam("MAJOR"));
- int minor = atoi(evt->findParam("MINOR"));
- char msg[255];
- bool enabled;
-
- SLOGD("Volume %s %s disk %d:%d removed\n", getLabel(), getMountpoint(), major, minor);
- if ((dev_t) MKDEV(major, minor) == mCurrentlyMountedKdev) {
- /*
- * Yikes, our mounted disk is going away!
- */
-
- doUnmount(major, minor);
- } else if (mVm->shareEnabled(getLabel(), "ums", &enabled) == 0 && enabled) {
- mVm->unshareVolume(getLabel(), "ums");
- }
-
- snprintf(msg, sizeof(msg), "Volume %s %s disk removed (%d:%d)",
- getLabel(), getFuseMountpoint(), major, minor);
- mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskRemoved,
- msg, false);
- setState(Volume::State_NoMedia);
-}
-
-void DirectVolume::handlePartitionRemoved(const char * /*devpath*/,
- NetlinkEvent *evt) {
- int major = atoi(evt->findParam("MAJOR"));
- int minor = atoi(evt->findParam("MINOR"));
- char msg[255];
- int state;
-
- SLOGD("Volume %s %s partition %d:%d removed\n", getLabel(), getMountpoint(), major, minor);
-
- /*
- * The framework doesn't need to get notified of
- * partition removal unless it's mounted. Otherwise
- * the removal notification will be sent on the Disk
- * itself
- */
- state = getState();
- if (state != Volume::State_Mounted && state != Volume::State_Shared) {
- return;
- }
-
- if ((dev_t) MKDEV(major, minor) == mCurrentlyMountedKdev) {
- /*
- * Yikes, our mounted partition is going away!
- */
- doUnmount(major, minor);
- } else if (state == Volume::State_Shared) {
- /* removed during mass storage */
- snprintf(msg, sizeof(msg), "Volume %s bad removal (%d:%d)",
- getLabel(), major, minor);
- mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,
- msg, false);
-
- if (mVm->unshareVolume(getLabel(), "ums")) {
- SLOGE("Failed to unshare volume on bad removal (%s)",
- strerror(errno));
- } else {
- SLOGD("Crisis averted");
- }
- }
-}
-
-void DirectVolume::doUnmount(int major, int minor) {
- char msg[255];
- bool providesAsec = (getFlags() & VOL_PROVIDES_ASEC) != 0;
- if (providesAsec && mVm->cleanupAsec(this, true)) {
- SLOGE("Failed to cleanup ASEC - unmount will probably fail!");
- }
-
- snprintf(msg, sizeof(msg), "Volume %s %s bad removal (%d:%d)",
- getLabel(), getFuseMountpoint(), major, minor);
- mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,
- msg, false);
-
- if (Volume::unmountVol(true, false)) {
- SLOGE("Failed to unmount volume on bad removal (%s)",
- strerror(errno));
- // XXX: At this point we're screwed for now
- } else {
- SLOGD("Crisis averted");
- }
-}
-
-/*
- * Called from base to get a list of devicenodes for mounting
- */
-int DirectVolume::getDeviceNodes(dev_t *devs, int max) {
-
- if (mPartIdx == -1) {
- // If the disk has no partitions, try the disk itself
- if (!mDiskNumParts) {
- devs[0] = MKDEV(mDiskMajor, mDiskMinor);
- return 1;
- }
-
- int i;
- for (i = 0; i < mDiskNumParts; i++) {
- if (i == max)
- break;
- devs[i] = MKDEV(mDiskMajor, mPartMinors[i]);
- }
- return mDiskNumParts;
- }
- devs[0] = MKDEV(mDiskMajor, mPartMinors[mPartIdx -1]);
- return 1;
-}
-
-/*
- * Called from base to update device info,
- * e.g. When setting up an dm-crypt mapping for the sd card.
- */
-int DirectVolume::updateDeviceInfo(char *new_path, int new_major, int new_minor)
-{
- PathCollection::iterator it;
-
- if (mPartIdx == -1) {
- SLOGE("Can only change device info on a partition\n");
- return -1;
- }
-
- /*
- * This is to change the sysfs path associated with a partition, in particular,
- * for an internal SD card partition that is encrypted. Thus, the list is
- * expected to be only 1 entry long. Check that and bail if not.
- */
- if (mPaths->size() != 1) {
- SLOGE("Cannot change path if there are more than one for a volume\n");
- return -1;
- }
-
- it = mPaths->begin();
- delete *it; /* Free the string storage */
- mPaths->erase(it); /* Remove it from the list */
- addPath(new_path); /* Put the new path on the list */
-
- /* Save away original info so we can restore it when doing factory reset.
- * Then, when doing the format, it will format the original device in the
- * clear, otherwise it just formats the encrypted device which is not
- * readable when the device boots unencrypted after the reset.
- */
- mOrigDiskMajor = mDiskMajor;
- mOrigDiskMinor = mDiskMinor;
- mOrigPartIdx = mPartIdx;
- memcpy(mOrigPartMinors, mPartMinors, sizeof(mPartMinors));
-
- mDiskMajor = new_major;
- mDiskMinor = new_minor;
- /* Ugh, virual block devices don't use minor 0 for whole disk and minor > 0 for
- * partition number. They don't have partitions, they are just virtual block
- * devices, and minor number 0 is the first dm-crypt device. Luckily the first
- * dm-crypt device is for the userdata partition, which gets minor number 0, and
- * it is not managed by vold. So the next device is minor number one, which we
- * will call partition one.
- */
- mPartIdx = new_minor;
- mPartMinors[new_minor-1] = new_minor;
-
- mIsDecrypted = 1;
-
- return 0;
-}
-
-/*
- * Called from base to revert device info to the way it was before a
- * crypto mapping was created for it.
- */
-void DirectVolume::revertDeviceInfo(void)
-{
- if (mIsDecrypted) {
- mDiskMajor = mOrigDiskMajor;
- mDiskMinor = mOrigDiskMinor;
- mPartIdx = mOrigPartIdx;
- memcpy(mPartMinors, mOrigPartMinors, sizeof(mPartMinors));
-
- mIsDecrypted = 0;
- }
-
- return;
-}
-
-/*
- * Called from base to give cryptfs all the info it needs to encrypt eligible volumes
- */
-int DirectVolume::getVolInfo(struct volume_info *v)
-{
- strcpy(v->label, mLabel);
- strcpy(v->mnt_point, mMountpoint);
- v->flags = getFlags();
- /* Other fields of struct volume_info are filled in by the caller or cryptfs.c */
-
- return 0;
-}
diff --git a/DirectVolume.h b/DirectVolume.h
deleted file mode 100644
index 96f46af..0000000
--- a/DirectVolume.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _DEVICEVOLUME_H
-#define _DEVICEVOLUME_H
-
-#include <utils/List.h>
-
-#include "Volume.h"
-
-class PathInfo {
-public:
- PathInfo(const char *pattern);
- ~PathInfo();
- bool match(const char *path);
-private:
- bool warned;
- char *pattern;
- enum PatternType { prefix, wildcard };
- PatternType patternType;
-};
-
-typedef android::List<PathInfo *> PathCollection;
-
-class DirectVolume : public Volume {
-public:
- static const int MAX_PARTITIONS = 32;
-protected:
- const char* mMountpoint;
- const char* mFuseMountpoint;
-
- PathCollection *mPaths;
- int mDiskMajor;
- int mDiskMinor;
- int mPartMinors[MAX_PARTITIONS];
- int mOrigDiskMajor;
- int mOrigDiskMinor;
- int mOrigPartMinors[MAX_PARTITIONS];
- int mDiskNumParts;
- int mPendingPartCount;
- int mIsDecrypted;
-
-public:
- DirectVolume(VolumeManager *vm, const fstab_rec* rec, int flags);
- virtual ~DirectVolume();
-
- int addPath(const char *path);
-
- const char *getMountpoint() { return mMountpoint; }
- const char *getFuseMountpoint() { return mFuseMountpoint; }
-
- int handleBlockEvent(NetlinkEvent *evt);
- dev_t getDiskDevice();
- dev_t getShareDevice();
- void handleVolumeShared();
- void handleVolumeUnshared();
- int getVolInfo(struct volume_info *v);
-
-protected:
- int getDeviceNodes(dev_t *devs, int max);
- int updateDeviceInfo(char *new_path, int new_major, int new_minor);
- virtual void revertDeviceInfo(void);
- int isDecrypted() { return mIsDecrypted; }
-
-private:
- void handleDiskAdded(const char *devpath, NetlinkEvent *evt);
- void handleDiskRemoved(const char *devpath, NetlinkEvent *evt);
- void handleDiskChanged(const char *devpath, NetlinkEvent *evt);
- void handlePartitionAdded(const char *devpath, NetlinkEvent *evt);
- void handlePartitionRemoved(const char *devpath, NetlinkEvent *evt);
- void handlePartitionChanged(const char *devpath, NetlinkEvent *evt);
-
- int doMountVfat(const char *deviceNode, const char *mountPoint);
- void doUnmount(int major, int minor);
-
-};
-
-typedef android::List<DirectVolume *> DirectVolumeCollection;
-
-#endif
diff --git a/Disk.cpp b/Disk.cpp
new file mode 100644
index 0000000..5b5d769
--- /dev/null
+++ b/Disk.cpp
@@ -0,0 +1,498 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "Disk.h"
+#include "PublicVolume.h"
+#include "PrivateVolume.h"
+#include "Utils.h"
+#include "VolumeBase.h"
+#include "VolumeManager.h"
+#include "ResponseCode.h"
+
+#include <base/file.h>
+#include <base/stringprintf.h>
+#include <base/logging.h>
+#include <diskconfig/diskconfig.h>
+
+#include <vector>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+
+#define ENTIRE_DEVICE_FALLBACK 0
+
+using android::base::ReadFileToString;
+using android::base::WriteStringToFile;
+using android::base::StringPrintf;
+
+namespace android {
+namespace vold {
+
+static const char* kSgdiskPath = "/system/bin/sgdisk";
+static const char* kSgdiskToken = " \t\n";
+
+static const char* kSysfsMmcMaxMinors = "/sys/module/mmcblk/parameters/perdev_minors";
+
+static const unsigned int kMajorBlockScsiA = 8;
+static const unsigned int kMajorBlockScsiB = 65;
+static const unsigned int kMajorBlockScsiC = 66;
+static const unsigned int kMajorBlockScsiD = 67;
+static const unsigned int kMajorBlockScsiE = 68;
+static const unsigned int kMajorBlockScsiF = 69;
+static const unsigned int kMajorBlockScsiG = 70;
+static const unsigned int kMajorBlockScsiH = 71;
+static const unsigned int kMajorBlockScsiI = 128;
+static const unsigned int kMajorBlockScsiJ = 129;
+static const unsigned int kMajorBlockScsiK = 130;
+static const unsigned int kMajorBlockScsiL = 131;
+static const unsigned int kMajorBlockScsiM = 132;
+static const unsigned int kMajorBlockScsiN = 133;
+static const unsigned int kMajorBlockScsiO = 134;
+static const unsigned int kMajorBlockScsiP = 135;
+static const unsigned int kMajorBlockMmc = 179;
+
+static const char* kGptBasicData = "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7";
+static const char* kGptAndroidMeta = "19A710A2-B3CA-11E4-B026-10604B889DCF";
+static const char* kGptAndroidExpand = "193D1EA4-B3CA-11E4-B075-10604B889DCF";
+
+static const char* kKeyPath = "/data/misc/vold";
+
+enum class Table {
+ kUnknown,
+ kMbr,
+ kGpt,
+};
+
+Disk::Disk(const std::string& eventPath, dev_t device,
+ const std::string& nickname, int flags) :
+ mDevice(device), mSize(-1), mNickname(nickname), mFlags(flags), mCreated(
+ false), mJustPartitioned(false) {
+ mId = StringPrintf("disk:%u,%u", major(device), minor(device));
+ mEventPath = eventPath;
+ mSysPath = StringPrintf("/sys/%s", eventPath.c_str());
+ mDevPath = StringPrintf("/dev/block/vold/%s", mId.c_str());
+ CreateDeviceNode(mDevPath, mDevice);
+}
+
+Disk::~Disk() {
+ CHECK(!mCreated);
+ DestroyDeviceNode(mDevPath);
+}
+
+std::shared_ptr<VolumeBase> Disk::findVolume(const std::string& id) {
+ for (auto vol : mVolumes) {
+ if (vol->getId() == id) {
+ return vol;
+ }
+ auto stackedVol = vol->findVolume(id);
+ if (stackedVol != nullptr) {
+ return stackedVol;
+ }
+ }
+ return nullptr;
+}
+
+status_t Disk::create() {
+ CHECK(!mCreated);
+ mCreated = true;
+ notifyEvent(ResponseCode::DiskCreated, StringPrintf("%d", mFlags));
+ readMetadata();
+ readPartitions();
+ return OK;
+}
+
+status_t Disk::destroy() {
+ CHECK(mCreated);
+ destroyAllVolumes();
+ mCreated = false;
+ notifyEvent(ResponseCode::DiskDestroyed);
+ return OK;
+}
+
+static std::string BuildKeyPath(const std::string& partGuid) {
+ return StringPrintf("%s/expand_%s.key", kKeyPath, partGuid.c_str());
+}
+
+void Disk::createPublicVolume(dev_t device) {
+ auto vol = std::shared_ptr<VolumeBase>(new PublicVolume(device));
+ if (mJustPartitioned) {
+ LOG(DEBUG) << "Device just partitioned; silently formatting";
+ vol->setSilent(true);
+ vol->create();
+ vol->format();
+ vol->destroy();
+ vol->setSilent(false);
+ }
+
+ mVolumes.push_back(vol);
+ vol->setDiskId(getId());
+ vol->create();
+}
+
+void Disk::createPrivateVolume(dev_t device, const std::string& partGuid) {
+ std::string tmp;
+ std::string normalizedGuid;
+ if (HexToStr(partGuid, tmp)) {
+ LOG(WARNING) << "Invalid GUID " << partGuid;
+ return;
+ }
+ StrToHex(tmp, normalizedGuid);
+
+ std::string keyRaw;
+ if (!ReadFileToString(BuildKeyPath(normalizedGuid), &keyRaw)) {
+ PLOG(ERROR) << "Failed to load key for GUID " << normalizedGuid;
+ return;
+ }
+
+ LOG(DEBUG) << "Found key for GUID " << normalizedGuid;
+
+ auto vol = std::shared_ptr<VolumeBase>(new PrivateVolume(device, keyRaw));
+ if (mJustPartitioned) {
+ LOG(DEBUG) << "Device just partitioned; silently formatting";
+ vol->setSilent(true);
+ vol->create();
+ vol->format();
+ vol->destroy();
+ vol->setSilent(false);
+ }
+
+ mVolumes.push_back(vol);
+ vol->setDiskId(getId());
+ vol->create();
+}
+
+void Disk::destroyAllVolumes() {
+ for (auto vol : mVolumes) {
+ vol->destroy();
+ }
+ mVolumes.clear();
+}
+
+status_t Disk::readMetadata() {
+ mSize = -1;
+ mLabel.clear();
+
+ int fd = open(mDevPath.c_str(), O_RDONLY | O_CLOEXEC);
+ if (fd != -1) {
+ if (ioctl(fd, BLKGETSIZE64, &mSize)) {
+ mSize = -1;
+ }
+ close(fd);
+ }
+
+ switch (major(mDevice)) {
+ case kMajorBlockScsiA: case kMajorBlockScsiB: case kMajorBlockScsiC: case kMajorBlockScsiD:
+ case kMajorBlockScsiE: case kMajorBlockScsiF: case kMajorBlockScsiG: case kMajorBlockScsiH:
+ case kMajorBlockScsiI: case kMajorBlockScsiJ: case kMajorBlockScsiK: case kMajorBlockScsiL:
+ case kMajorBlockScsiM: case kMajorBlockScsiN: case kMajorBlockScsiO: case kMajorBlockScsiP: {
+ std::string path(mSysPath + "/device/vendor");
+ std::string tmp;
+ if (!ReadFileToString(path, &tmp)) {
+ PLOG(WARNING) << "Failed to read vendor from " << path;
+ return -errno;
+ }
+ mLabel = tmp;
+ break;
+ }
+ case kMajorBlockMmc: {
+ std::string path(mSysPath + "/device/manfid");
+ std::string tmp;
+ if (!ReadFileToString(path, &tmp)) {
+ PLOG(WARNING) << "Failed to read manufacturer from " << path;
+ return -errno;
+ }
+ uint64_t manfid = strtoll(tmp.c_str(), nullptr, 16);
+ // Our goal here is to give the user a meaningful label, ideally
+ // matching whatever is silk-screened on the card. To reduce
+ // user confusion, this list doesn't contain white-label manfid.
+ switch (manfid) {
+ case 0x000003: mLabel = "SanDisk"; break;
+ case 0x00001b: mLabel = "Samsung"; break;
+ case 0x000028: mLabel = "Lexar"; break;
+ case 0x000074: mLabel = "Transcend"; break;
+ }
+ break;
+ }
+ default: {
+ LOG(WARNING) << "Unsupported block major type" << major(mDevice);
+ return -ENOTSUP;
+ }
+ }
+
+ notifyEvent(ResponseCode::DiskSizeChanged, StringPrintf("%" PRId64, mSize));
+ notifyEvent(ResponseCode::DiskLabelChanged, mLabel);
+ return OK;
+}
+
+status_t Disk::readPartitions() {
+ int8_t maxMinors = getMaxMinors();
+ if (maxMinors < 0) {
+ return -ENOTSUP;
+ }
+
+ destroyAllVolumes();
+
+ // Parse partition table
+
+ std::vector<std::string> cmd;
+ cmd.push_back(kSgdiskPath);
+ cmd.push_back("--android-dump");
+ cmd.push_back(mDevPath);
+
+ std::vector<std::string> output;
+ status_t res = ForkExecvp(cmd, output);
+ if (res != OK) {
+ LOG(WARNING) << "sgdisk failed to scan " << mDevPath;
+ notifyEvent(ResponseCode::DiskScanned);
+ mJustPartitioned = false;
+ return res;
+ }
+
+ Table table = Table::kUnknown;
+ bool foundParts = false;
+ for (auto line : output) {
+ char* cline = (char*) line.c_str();
+ char* token = strtok(cline, kSgdiskToken);
+ if (token == nullptr) continue;
+
+ if (!strcmp(token, "DISK")) {
+ const char* type = strtok(nullptr, kSgdiskToken);
+ if (!strcmp(type, "mbr")) {
+ table = Table::kMbr;
+ } else if (!strcmp(type, "gpt")) {
+ table = Table::kGpt;
+ }
+ } else if (!strcmp(token, "PART")) {
+ foundParts = true;
+ int i = strtol(strtok(nullptr, kSgdiskToken), nullptr, 10);
+ if (i <= 0 || i > maxMinors) {
+ LOG(WARNING) << mId << " is ignoring partition " << i
+ << " beyond max supported devices";
+ continue;
+ }
+ dev_t partDevice = makedev(major(mDevice), minor(mDevice) + i);
+
+ if (table == Table::kMbr) {
+ const char* type = strtok(nullptr, kSgdiskToken);
+
+ switch (strtol(type, nullptr, 16)) {
+ case 0x06: // FAT16
+ case 0x0b: // W95 FAT32 (LBA)
+ case 0x0c: // W95 FAT32 (LBA)
+ case 0x0e: // W95 FAT16 (LBA)
+ createPublicVolume(partDevice);
+ break;
+ }
+ } else if (table == Table::kGpt) {
+ const char* typeGuid = strtok(nullptr, kSgdiskToken);
+ const char* partGuid = strtok(nullptr, kSgdiskToken);
+
+ if (!strcasecmp(typeGuid, kGptBasicData)) {
+ createPublicVolume(partDevice);
+ } else if (!strcasecmp(typeGuid, kGptAndroidExpand)) {
+ createPrivateVolume(partDevice, partGuid);
+ }
+ }
+ }
+ }
+
+#if ENTIRE_DEVICE_FALLBACK
+ // Ugly last ditch effort, treat entire disk as partition
+ if (table == Table::kUnknown || !foundParts) {
+ // TODO: use blkid to confirm filesystem before doing this
+ LOG(WARNING) << mId << " has unknown partition table; trying entire device";
+ createPublicVolume(mDevice);
+ }
+#endif
+
+ notifyEvent(ResponseCode::DiskScanned);
+ mJustPartitioned = false;
+ return OK;
+}
+
+status_t Disk::unmountAll() {
+ for (auto vol : mVolumes) {
+ vol->unmount();
+ }
+ return OK;
+}
+
+status_t Disk::partitionPublic() {
+ // TODO: improve this code
+ destroyAllVolumes();
+ mJustPartitioned = true;
+
+ struct disk_info dinfo;
+ memset(&dinfo, 0, sizeof(dinfo));
+
+ if (!(dinfo.part_lst = (struct part_info *) malloc(
+ MAX_NUM_PARTS * sizeof(struct part_info)))) {
+ return -1;
+ }
+
+ memset(dinfo.part_lst, 0, MAX_NUM_PARTS * sizeof(struct part_info));
+ dinfo.device = strdup(mDevPath.c_str());
+ dinfo.scheme = PART_SCHEME_MBR;
+ dinfo.sect_size = 512;
+ dinfo.skip_lba = 2048;
+ dinfo.num_lba = 0;
+ dinfo.num_parts = 1;
+
+ struct part_info *pinfo = &dinfo.part_lst[0];
+
+ pinfo->name = strdup("android_sdcard");
+ pinfo->flags |= PART_ACTIVE_FLAG;
+ pinfo->type = PC_PART_TYPE_FAT32;
+ pinfo->len_kb = -1;
+
+ int rc = apply_disk_config(&dinfo, 0);
+ if (rc) {
+ LOG(ERROR) << "Failed to apply disk configuration: " << rc;
+ goto out;
+ }
+
+out:
+ free(pinfo->name);
+ free(dinfo.device);
+ free(dinfo.part_lst);
+
+ return rc;
+}
+
+status_t Disk::partitionPrivate() {
+ return partitionMixed(0);
+}
+
+status_t Disk::partitionMixed(int8_t ratio) {
+ int res;
+
+ destroyAllVolumes();
+ mJustPartitioned = true;
+
+ // First nuke any existing partition table
+ std::vector<std::string> cmd;
+ cmd.push_back(kSgdiskPath);
+ cmd.push_back("--zap-all");
+ cmd.push_back(mDevPath);
+
+ // Zap sometimes returns an error when it actually succeeded, so
+ // just log as warning and keep rolling forward.
+ if ((res = ForkExecvp(cmd)) != 0) {
+ LOG(WARNING) << "Failed to zap; status " << res;
+ }
+
+ // We've had some success above, so generate both the private partition
+ // GUID and encryption key and persist them.
+ std::string partGuidRaw;
+ std::string keyRaw;
+ if (ReadRandomBytes(16, partGuidRaw) || ReadRandomBytes(16, keyRaw)) {
+ LOG(ERROR) << "Failed to generate GUID or key";
+ return -EIO;
+ }
+
+ std::string partGuid;
+ StrToHex(partGuidRaw, partGuid);
+
+ if (!WriteStringToFile(keyRaw, BuildKeyPath(partGuid))) {
+ LOG(ERROR) << "Failed to persist key";
+ return -EIO;
+ } else {
+ LOG(DEBUG) << "Persisted key for GUID " << partGuid;
+ }
+
+ // Now let's build the new GPT table. We heavily rely on sgdisk to
+ // force optimal alignment on the created partitions.
+ cmd.clear();
+ cmd.push_back(kSgdiskPath);
+
+ // If requested, create a public partition first. Mixed-mode partitioning
+ // like this is an experimental feature.
+ if (ratio > 0) {
+ if (ratio < 10 || ratio > 90) {
+ LOG(ERROR) << "Mixed partition ratio must be between 10-90%";
+ return -EINVAL;
+ }
+
+ uint64_t splitMb = ((mSize / 100) * ratio) / 1024 / 1024;
+ cmd.push_back(StringPrintf("--new=0:0:+%" PRId64 "M", splitMb));
+ cmd.push_back(StringPrintf("--typecode=0:%s", kGptBasicData));
+ cmd.push_back("--change-name=0:shared");
+ }
+
+ // Define a metadata partition which is designed for future use; there
+ // should only be one of these per physical device, even if there are
+ // multiple private volumes.
+ cmd.push_back("--new=0:0:+16M");
+ cmd.push_back(StringPrintf("--typecode=0:%s", kGptAndroidMeta));
+ cmd.push_back("--change-name=0:android_meta");
+
+ // Define a single private partition filling the rest of disk.
+ cmd.push_back("--new=0:0:-0");
+ cmd.push_back(StringPrintf("--typecode=0:%s", kGptAndroidExpand));
+ cmd.push_back(StringPrintf("--partition-guid=0:%s", partGuid.c_str()));
+ cmd.push_back("--change-name=0:android_expand");
+
+ cmd.push_back(mDevPath);
+
+ if ((res = ForkExecvp(cmd)) != 0) {
+ LOG(ERROR) << "Failed to partition; status " << res;
+ return res;
+ }
+
+ return OK;
+}
+
+void Disk::notifyEvent(int event) {
+ VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event,
+ getId().c_str(), false);
+}
+
+void Disk::notifyEvent(int event, const std::string& value) {
+ VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event,
+ StringPrintf("%s %s", getId().c_str(), value.c_str()).c_str(), false);
+}
+
+int Disk::getMaxMinors() {
+ // Figure out maximum partition devices supported
+ switch (major(mDevice)) {
+ case kMajorBlockScsiA: case kMajorBlockScsiB: case kMajorBlockScsiC: case kMajorBlockScsiD:
+ case kMajorBlockScsiE: case kMajorBlockScsiF: case kMajorBlockScsiG: case kMajorBlockScsiH:
+ case kMajorBlockScsiI: case kMajorBlockScsiJ: case kMajorBlockScsiK: case kMajorBlockScsiL:
+ case kMajorBlockScsiM: case kMajorBlockScsiN: case kMajorBlockScsiO: case kMajorBlockScsiP: {
+ // Per Documentation/devices.txt this is static
+ return 15;
+ }
+ case kMajorBlockMmc: {
+ // Per Documentation/devices.txt this is dynamic
+ std::string tmp;
+ if (!ReadFileToString(kSysfsMmcMaxMinors, &tmp)) {
+ LOG(ERROR) << "Failed to read max minors";
+ return -errno;
+ }
+ return atoi(tmp.c_str());
+ }
+ }
+
+ LOG(ERROR) << "Unsupported block major type " << major(mDevice);
+ return -ENOTSUP;
+}
+
+} // namespace vold
+} // namespace android
diff --git a/Disk.h b/Disk.h
new file mode 100644
index 0000000..a8461fb
--- /dev/null
+++ b/Disk.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef ANDROID_VOLD_DISK_H
+#define ANDROID_VOLD_DISK_H
+
+#include "Utils.h"
+
+#include <utils/Errors.h>
+
+#include <vector>
+
+namespace android {
+namespace vold {
+
+class VolumeBase;
+
+/*
+ * Representation of detected physical media.
+ *
+ * Knows how to create volumes based on the partition tables found, and also
+ * how to repartition itself.
+ */
+class Disk {
+public:
+ Disk(const std::string& eventPath, dev_t device, const std::string& nickname, int flags);
+ virtual ~Disk();
+
+ enum Flags {
+ /* Flag that disk is adoptable */
+ kAdoptable = 1 << 0,
+ /* Flag that disk is considered primary when the user hasn't
+ * explicitly picked a primary storage location */
+ kDefaultPrimary = 1 << 1,
+ /* Flag that disk is SD card */
+ kSd = 1 << 2,
+ /* Flag that disk is USB disk */
+ kUsb = 1 << 3,
+ };
+
+ const std::string& getId() { return mId; }
+ const std::string& getEventPath() { return mEventPath; }
+ const std::string& getSysPath() { return mSysPath; }
+ const std::string& getDevPath() { return mDevPath; }
+ dev_t getDevice() { return mDevice; }
+ uint64_t getSize() { return mSize; }
+ const std::string& getLabel() { return mLabel; }
+ int getFlags() { return mFlags; }
+
+ std::shared_ptr<VolumeBase> findVolume(const std::string& id);
+
+ status_t create();
+ status_t destroy();
+
+ status_t readMetadata();
+ status_t readPartitions();
+
+ status_t unmountAll();
+
+ status_t partitionPublic();
+ status_t partitionPrivate();
+ status_t partitionMixed(int8_t ratio);
+
+ void notifyEvent(int msg);
+ void notifyEvent(int msg, const std::string& value);
+
+private:
+ /* ID that uniquely references this disk */
+ std::string mId;
+ /* Original event path */
+ std::string mEventPath;
+ /* Device path under sysfs */
+ std::string mSysPath;
+ /* Device path under dev */
+ std::string mDevPath;
+ /* Kernel device representing disk */
+ dev_t mDevice;
+ /* Size of disk, in bytes */
+ uint64_t mSize;
+ /* User-visible label, such as manufacturer */
+ std::string mLabel;
+ /* Current partitions on disk */
+ std::vector<std::shared_ptr<VolumeBase>> mVolumes;
+ /* Nickname for this disk */
+ std::string mNickname;
+ /* Flags applicable to this disk */
+ int mFlags;
+ /* Flag indicating object is created */
+ bool mCreated;
+ /* Flag that we just partitioned and should format all volumes */
+ bool mJustPartitioned;
+
+ void createPublicVolume(dev_t device);
+ void createPrivateVolume(dev_t device, const std::string& partGuid);
+
+ void destroyAllVolumes();
+
+ int getMaxMinors();
+
+ DISALLOW_COPY_AND_ASSIGN(Disk);
+};
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/EmulatedVolume.cpp b/EmulatedVolume.cpp
new file mode 100644
index 0000000..7098872
--- /dev/null
+++ b/EmulatedVolume.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "EmulatedVolume.h"
+#include "Utils.h"
+
+#include <base/stringprintf.h>
+#include <base/logging.h>
+#include <cutils/fs.h>
+#include <private/android_filesystem_config.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace vold {
+
+static const char* kFusePath = "/system/bin/sdcard";
+
+EmulatedVolume::EmulatedVolume(const std::string& rawPath) :
+ VolumeBase(Type::kEmulated), mFusePid(0) {
+ setId("emulated");
+ mFusePath = "/storage/emulated";
+ mRawPath = rawPath;
+}
+
+EmulatedVolume::EmulatedVolume(const std::string& rawPath, dev_t device,
+ const std::string& fsUuid) : VolumeBase(Type::kEmulated), mFusePid(0) {
+ setId(StringPrintf("emulated:%u,%u", major(device), minor(device)));
+ mFusePath = StringPrintf("/storage/%s", fsUuid.c_str());
+ mRawPath = rawPath;
+}
+
+EmulatedVolume::~EmulatedVolume() {
+}
+
+status_t EmulatedVolume::doMount() {
+ if (fs_prepare_dir(mFusePath.c_str(), 0700, AID_ROOT, AID_ROOT)) {
+ PLOG(ERROR) << getId() << " failed to create mount point " << mFusePath;
+ return -errno;
+ }
+
+ setPath(mFusePath);
+ setInternalPath(mRawPath);
+
+ if (!(mFusePid = fork())) {
+ // TODO: protect when not mounted as visible
+ if (execl(kFusePath, kFusePath,
+ "-u", "1023", // AID_MEDIA_RW
+ "-g", "1023", // AID_MEDIA_RW
+ "-l",
+ mRawPath.c_str(),
+ mFusePath.c_str(),
+ NULL)) {
+ PLOG(ERROR) << "Failed to exec";
+ }
+
+ PLOG(DEBUG) << "FUSE exiting";
+ _exit(1);
+ }
+
+ if (mFusePid == -1) {
+ PLOG(ERROR) << getId() << " failed to fork";
+ return -errno;
+ }
+
+ return OK;
+}
+
+status_t EmulatedVolume::doUnmount() {
+ if (mFusePid > 0) {
+ kill(mFusePid, SIGTERM);
+ TEMP_FAILURE_RETRY(waitpid(mFusePid, nullptr, 0));
+ mFusePid = 0;
+ }
+
+ ForceUnmount(mFusePath);
+ ForceUnmount(mRawPath);
+
+ if (TEMP_FAILURE_RETRY(rmdir(mFusePath.c_str()))) {
+ PLOG(ERROR) << getId() << " failed to rmdir mount point " << mFusePath;
+ return -errno;
+ }
+
+ return OK;
+}
+
+} // namespace vold
+} // namespace android
diff --git a/EmulatedVolume.h b/EmulatedVolume.h
new file mode 100644
index 0000000..04d4508
--- /dev/null
+++ b/EmulatedVolume.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef ANDROID_VOLD_EMULATED_VOLUME_H
+#define ANDROID_VOLD_EMULATED_VOLUME_H
+
+#include "VolumeBase.h"
+
+#include <cutils/multiuser.h>
+
+namespace android {
+namespace vold {
+
+/*
+ * Shared storage emulated on top of private storage.
+ *
+ * Knows how to spawn a FUSE daemon to synthesize permissions. ObbVolume
+ * can be stacked above it.
+ *
+ * This volume is always multi-user aware, but is only binds itself to
+ * users when its primary storage. This volume should never be presented
+ * as secondary storage, since we're strongly encouraging developers to
+ * store data local to their app.
+ */
+class EmulatedVolume : public VolumeBase {
+public:
+ EmulatedVolume(const std::string& rawPath);
+ EmulatedVolume(const std::string& rawPath, dev_t device, const std::string& fsUuid);
+ virtual ~EmulatedVolume();
+
+protected:
+ status_t doMount() override;
+ status_t doUnmount() override;
+
+private:
+ /* Mount point of raw storage */
+ std::string mRawPath;
+ /* Mount point of visible storage */
+ std::string mFusePath;
+ /* PID of FUSE wrapper */
+ pid_t mFusePid;
+
+ DISALLOW_COPY_AND_ASSIGN(EmulatedVolume);
+};
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/Ext4.cpp b/Ext4.cpp
index f5a964a..a208eb5 100644
--- a/Ext4.cpp
+++ b/Ext4.cpp
@@ -23,6 +23,8 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <vector>
+#include <string>
#include <sys/types.h>
#include <sys/stat.h>
@@ -35,16 +37,84 @@
#define LOG_TAG "Vold"
+#include <base/logging.h>
+#include <base/stringprintf.h>
#include <cutils/log.h>
#include <cutils/properties.h>
-
#include <logwrap/logwrap.h>
+#include <selinux/selinux.h>
#include "Ext4.h"
+#include "Utils.h"
#include "VoldUtil.h"
-#define MKEXT4FS_PATH "/system/bin/make_ext4fs"
-#define RESIZE2FS_PATH "/system/bin/resize2fs"
+using android::base::StringPrintf;
+
+static const char* kResizefsPath = "/system/bin/resize2fs";
+static const char* kMkfsPath = "/system/bin/make_ext4fs";
+static const char* kFsckPath = "/system/bin/e2fsck";
+
+int Ext4::check(const char *fsPath, const char *mountPoint) {
+ // The following is shamelessly borrowed from fs_mgr.c, so it should be
+ // kept in sync with any changes over there.
+
+ char* blk_device = (char*) fsPath;
+ char* target = (char*) mountPoint;
+
+ int status;
+ int ret;
+ long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID;
+ char *tmpmnt_opts = (char*) "nomblk_io_submit,errors=remount-ro";
+
+ /*
+ * First try to mount and unmount the filesystem. We do this because
+ * the kernel is more efficient than e2fsck in running the journal and
+ * processing orphaned inodes, and on at least one device with a
+ * performance issue in the emmc firmware, it can take e2fsck 2.5 minutes
+ * to do what the kernel does in about a second.
+ *
+ * After mounting and unmounting the filesystem, run e2fsck, and if an
+ * error is recorded in the filesystem superblock, e2fsck will do a full
+ * check. Otherwise, it does nothing. If the kernel cannot mount the
+ * filesytsem due to an error, e2fsck is still run to do a full check
+ * fix the filesystem.
+ */
+ ret = mount(blk_device, target, "ext4", tmpmnt_flags, tmpmnt_opts);
+ if (!ret) {
+ int i;
+ for (i = 0; i < 5; i++) {
+ // Try to umount 5 times before continuing on.
+ // Should we try rebooting if all attempts fail?
+ int result = umount(target);
+ if (result == 0) {
+ break;
+ }
+ ALOGW("%s(): umount(%s)=%d: %s\n", __func__, target, result, strerror(errno));
+ sleep(1);
+ }
+ }
+
+ /*
+ * Some system images do not have e2fsck for licensing reasons
+ * (e.g. recent SDK system images). Detect these and skip the check.
+ */
+ if (access(kFsckPath, X_OK)) {
+ ALOGD("Not running %s on %s (executable not in system image)\n",
+ kFsckPath, blk_device);
+ } else {
+ ALOGD("Running %s on %s\n", kFsckPath, blk_device);
+
+ std::vector<std::string> cmd;
+ cmd.push_back(kFsckPath);
+ cmd.push_back("-y");
+ cmd.push_back(blk_device);
+
+ // Ext4 devices are currently always trusted
+ return android::vold::ForkExecvp(cmd, android::vold::sFsckContext);
+ }
+
+ return 0;
+}
int Ext4::doMount(const char *fsPath, const char *mountPoint, bool ro, bool remount,
bool executable) {
@@ -69,92 +139,31 @@
}
int Ext4::resize(const char *fspath, unsigned int numSectors) {
- const char *args[4];
- char* size_str;
- int rc;
- int status;
+ std::vector<std::string> cmd;
+ cmd.push_back(kResizefsPath);
+ cmd.push_back("-f");
+ cmd.push_back(fspath);
+ cmd.push_back(StringPrintf("%u", numSectors));
- args[0] = RESIZE2FS_PATH;
- args[1] = "-f";
- args[2] = fspath;
- if (asprintf(&size_str, "%ds", numSectors) < 0)
- {
- SLOGE("Filesystem (ext4) resize failed to set size");
- return -1;
- }
- args[3] = size_str;
- rc = android_fork_execvp(ARRAY_SIZE(args), (char **)args, &status, false,
- true);
- free(size_str);
- if (rc != 0) {
- SLOGE("Filesystem (ext4) resize failed due to logwrap error");
- errno = EIO;
- return -1;
- }
-
- if (!WIFEXITED(status)) {
- SLOGE("Filesystem (ext4) resize did not exit properly");
- errno = EIO;
- return -1;
- }
-
- status = WEXITSTATUS(status);
-
- if (status == 0) {
- SLOGI("Filesystem (ext4) resized OK");
- return 0;
- } else {
- SLOGE("Resize (ext4) failed (unknown exit code %d)", status);
- errno = EIO;
- return -1;
- }
- return 0;
+ return android::vold::ForkExecvp(cmd);
}
int Ext4::format(const char *fsPath, unsigned int numSectors, const char *mountpoint) {
- const char *args[7];
- int rc;
- int status;
+ std::vector<std::string> cmd;
+ cmd.push_back(kMkfsPath);
+ cmd.push_back("-J");
- args[0] = MKEXT4FS_PATH;
- args[1] = "-J";
- args[2] = "-a";
- args[3] = mountpoint;
+ cmd.push_back("-a");
+ cmd.push_back(mountpoint);
+
if (numSectors) {
- char tmp[32];
- snprintf(tmp, sizeof(tmp), "%u", numSectors * 512);
- const char *size = tmp;
- args[4] = "-l";
- args[5] = size;
- args[6] = fsPath;
- rc = android_fork_execvp(ARRAY_SIZE(args), (char **)args, &status, false, true);
- } else {
- args[4] = fsPath;
- rc = android_fork_execvp(5, (char **)args, &status, false, true);
- }
- rc = android_fork_execvp(ARRAY_SIZE(args), (char **)args, &status, false,
- true);
- if (rc != 0) {
- SLOGE("Filesystem (ext4) format failed due to logwrap error");
- errno = EIO;
- return -1;
+ cmd.push_back("-l");
+ cmd.push_back(StringPrintf("%u", numSectors * 512));
}
- if (!WIFEXITED(status)) {
- SLOGE("Filesystem (ext4) format did not exit properly");
- errno = EIO;
- return -1;
- }
+ // Always generate a real UUID
+ cmd.push_back("-u");
+ cmd.push_back(fsPath);
- status = WEXITSTATUS(status);
-
- if (status == 0) {
- SLOGI("Filesystem (ext4) formatted OK");
- return 0;
- } else {
- SLOGE("Format (ext4) failed (unknown exit code %d)", status);
- errno = EIO;
- return -1;
- }
- return 0;
+ return android::vold::ForkExecvp(cmd);
}
diff --git a/Ext4.h b/Ext4.h
index c768f5a..55672af 100644
--- a/Ext4.h
+++ b/Ext4.h
@@ -21,6 +21,7 @@
class Ext4 {
public:
+ static int check(const char *fsPath, const char *mountPoint);
static int doMount(const char *fsPath, const char *mountPoint, bool ro, bool remount,
bool executable);
static int format(const char *fsPath, unsigned int numSectors, const char *mountpoint);
diff --git a/Fat.cpp b/Fat.cpp
index 018fbeb..873088d 100644
--- a/Fat.cpp
+++ b/Fat.cpp
@@ -37,20 +37,25 @@
#define LOG_TAG "Vold"
+#include <base/logging.h>
+#include <base/stringprintf.h>
#include <cutils/log.h>
#include <cutils/properties.h>
+#include <selinux/selinux.h>
#include <logwrap/logwrap.h>
#include "Fat.h"
+#include "Utils.h"
#include "VoldUtil.h"
-static char FSCK_MSDOS_PATH[] = "/system/bin/fsck_msdos";
-static char MKDOSFS_PATH[] = "/system/bin/newfs_msdos";
-extern "C" int mount(const char *, const char *, const char *, unsigned long, const void *);
+using android::base::StringPrintf;
+
+static const char* kMkfsPath = "/system/bin/newfs_msdos";
+static const char* kFsckPath = "/system/bin/fsck_msdos";
int Fat::check(const char *fsPath) {
- if (access(FSCK_MSDOS_PATH, X_OK)) {
+ if (access(kFsckPath, X_OK)) {
SLOGW("Skipping fs checks\n");
return 0;
}
@@ -58,30 +63,22 @@
int pass = 1;
int rc = 0;
do {
- const char *args[4];
- int status;
- args[0] = FSCK_MSDOS_PATH;
- args[1] = "-p";
- args[2] = "-f";
- args[3] = fsPath;
+ std::vector<std::string> cmd;
+ cmd.push_back(kFsckPath);
+ cmd.push_back("-p");
+ cmd.push_back("-f");
+ cmd.push_back(fsPath);
- rc = android_fork_execvp(ARRAY_SIZE(args), (char **)args, &status,
- false, true);
- if (rc != 0) {
+ // Fat devices are currently always untrusted
+ rc = android::vold::ForkExecvp(cmd, android::vold::sFsckUntrustedContext);
+
+ if (rc < 0) {
SLOGE("Filesystem check failed due to logwrap error");
errno = EIO;
return -1;
}
- if (!WIFEXITED(status)) {
- SLOGE("Filesystem check did not exit properly");
- errno = EIO;
- return -1;
- }
-
- status = WEXITSTATUS(status);
-
- switch(status) {
+ switch(rc) {
case 0:
SLOGI("Filesystem check completed OK");
return 0;
@@ -102,7 +99,7 @@
return -1;
default:
- SLOGE("Filesystem check failed (unknown exit code %d)", status);
+ SLOGE("Filesystem check failed (unknown exit code %d)", rc);
errno = EIO;
return -1;
}
@@ -169,57 +166,39 @@
}
int Fat::format(const char *fsPath, unsigned int numSectors, bool wipe) {
- const char *args[11];
- int rc;
- int status;
-
if (wipe) {
Fat::wipe(fsPath, numSectors);
}
- args[0] = MKDOSFS_PATH;
- args[1] = "-F";
- args[2] = "32";
- args[3] = "-O";
- args[4] = "android";
- args[5] = "-c";
- args[6] = "64";
- args[7] = "-A";
+ std::vector<std::string> cmd;
+ cmd.push_back(kMkfsPath);
+ cmd.push_back("-F");
+ cmd.push_back("32");
+ cmd.push_back("-O");
+ cmd.push_back("android");
+ cmd.push_back("-c");
+ cmd.push_back("64");
+ cmd.push_back("-A");
if (numSectors) {
- char tmp[32];
- snprintf(tmp, sizeof(tmp), "%u", numSectors);
- const char *size = tmp;
- args[8] = "-s";
- args[9] = size;
- args[10] = fsPath;
- rc = android_fork_execvp(ARRAY_SIZE(args), (char **)args, &status,
- false, true);
- } else {
- args[8] = fsPath;
- rc = android_fork_execvp(9, (char **)args, &status, false,
- true);
+ cmd.push_back("-s");
+ cmd.push_back(StringPrintf("%u", numSectors));
}
- if (rc != 0) {
+ cmd.push_back(fsPath);
+
+ int rc = android::vold::ForkExecvp(cmd);
+ if (rc < 0) {
SLOGE("Filesystem format failed due to logwrap error");
errno = EIO;
return -1;
}
- if (!WIFEXITED(status)) {
- SLOGE("Filesystem format did not exit properly");
- errno = EIO;
- return -1;
- }
-
- status = WEXITSTATUS(status);
-
- if (status == 0) {
+ if (rc == 0) {
SLOGI("Filesystem formatted OK");
return 0;
} else {
- SLOGE("Format failed (unknown exit code %d)", status);
+ SLOGE("Format failed (unknown exit code %d)", rc);
errno = EIO;
return -1;
}
@@ -229,7 +208,7 @@
void Fat::wipe(const char *fsPath, unsigned int numSectors) {
unsigned long long range[2];
- int fd = open(fsPath, O_RDWR);
+ int fd = open(fsPath, O_RDWR | O_CLOEXEC);
if (fd == -1) {
SLOGE("Fat wipe failed to open device %s", fsPath);
return;
diff --git a/Loop.cpp b/Loop.cpp
index b1e9f6a..8872d7a 100644
--- a/Loop.cpp
+++ b/Loop.cpp
@@ -49,7 +49,7 @@
sprintf(filename, "/dev/block/loop%d", i);
- if ((fd = open(filename, O_RDWR)) < 0) {
+ if ((fd = open(filename, O_RDWR | O_CLOEXEC)) < 0) {
if (errno != ENOENT) {
SLOGE("Unable to open %s (%s)", filename, strerror(errno));
} else {
@@ -93,7 +93,7 @@
sprintf(filename, "/dev/block/loop%d", i);
- if ((fd = open(filename, O_RDWR)) < 0) {
+ if ((fd = open(filename, O_RDWR | O_CLOEXEC)) < 0) {
if (errno != ENOENT) {
SLOGE("Unable to open %s (%s)", filename, strerror(errno));
} else {
@@ -168,7 +168,7 @@
setfscreatecon(NULL);
}
- if ((fd = open(filename, O_RDWR)) < 0) {
+ if ((fd = open(filename, O_RDWR | O_CLOEXEC)) < 0) {
SLOGE("Unable to open %s (%s)", filename, strerror(errno));
return -1;
}
@@ -196,7 +196,7 @@
int file_fd;
- if ((file_fd = open(loopFile, O_RDWR)) < 0) {
+ if ((file_fd = open(loopFile, O_RDWR | O_CLOEXEC)) < 0) {
SLOGE("Unable to open %s (%s)", loopFile, strerror(errno));
close(fd);
return -1;
@@ -231,7 +231,7 @@
int Loop::destroyByDevice(const char *loopDevice) {
int device_fd;
- device_fd = open(loopDevice, O_RDONLY);
+ device_fd = open(loopDevice, O_RDONLY | O_CLOEXEC);
if (device_fd < 0) {
SLOGE("Failed to open loop (%d)", errno);
return -1;
@@ -272,7 +272,7 @@
int Loop::resizeImageFile(const char *file, unsigned int numSectors) {
int fd;
- if ((fd = open(file, O_RDWR)) < 0) {
+ if ((fd = open(file, O_RDWR | O_CLOEXEC)) < 0) {
SLOGE("Error opening imagefile (%s)", strerror(errno));
return -1;
}
@@ -301,7 +301,7 @@
int fd;
struct asec_superblock buffer;
- if ((fd = open(loopDevice, O_RDONLY)) < 0) {
+ if ((fd = open(loopDevice, O_RDONLY | O_CLOEXEC)) < 0) {
SLOGE("Failed to open loopdevice (%s)", strerror(errno));
destroyByDevice(loopDevice);
return -1;
diff --git a/MoveTask.cpp b/MoveTask.cpp
new file mode 100644
index 0000000..e0eec12
--- /dev/null
+++ b/MoveTask.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "MoveTask.h"
+#include "Utils.h"
+#include "VolumeManager.h"
+#include "ResponseCode.h"
+
+#include <base/stringprintf.h>
+#include <base/logging.h>
+#include <private/android_filesystem_config.h>
+
+#include <dirent.h>
+#include <sys/wait.h>
+
+#define CONSTRAIN(amount, low, high) (amount < low ? low : (amount > high ? high : amount))
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace vold {
+
+// TODO: keep in sync with PackageManager
+static const int kMoveSucceeded = -100;
+static const int kMoveFailedInternalError = -6;
+
+static const char* kCpPath = "/system/bin/cp";
+static const char* kRmPath = "/system/bin/rm";
+
+MoveTask::MoveTask(const std::shared_ptr<VolumeBase>& from,
+ const std::shared_ptr<VolumeBase>& to) :
+ mFrom(from), mTo(to) {
+}
+
+MoveTask::~MoveTask() {
+}
+
+void MoveTask::start() {
+ mThread = std::thread(&MoveTask::run, this);
+}
+
+static void notifyProgress(int progress) {
+ VolumeManager::Instance()->getBroadcaster()->sendBroadcast(ResponseCode::MoveStatus,
+ StringPrintf("%d", progress).c_str(), false);
+}
+
+static status_t pushBackContents(const std::string& path, std::vector<std::string>& cmd) {
+ DIR* dir = opendir(path.c_str());
+ if (dir == NULL) {
+ return -1;
+ }
+ bool found = false;
+ struct dirent* ent;
+ while ((ent = readdir(dir)) != NULL) {
+ if ((!strcmp(ent->d_name, ".")) || (!strcmp(ent->d_name, ".."))) {
+ continue;
+ }
+ cmd.push_back(StringPrintf("%s/%s", path.c_str(), ent->d_name));
+ found = true;
+ }
+ closedir(dir);
+ return found ? OK : -1;
+}
+
+static status_t execRm(const std::string& path, int startProgress, int stepProgress) {
+ notifyProgress(startProgress);
+
+ uint64_t expectedBytes = GetTreeBytes(path);
+ uint64_t startFreeBytes = GetFreeBytes(path);
+
+ std::vector<std::string> cmd;
+ cmd.push_back(kRmPath);
+ cmd.push_back("-f"); /* force: remove without confirmation, no error if it doesn't exist */
+ cmd.push_back("-R"); /* recursive: remove directory contents */
+ if (pushBackContents(path, cmd) != OK) {
+ LOG(WARNING) << "No contents in " << path;
+ return OK;
+ }
+
+ pid_t pid = ForkExecvpAsync(cmd);
+ if (pid == -1) return -1;
+
+ int status;
+ while (true) {
+ if (waitpid(pid, &status, WNOHANG) == pid) {
+ if (WIFEXITED(status)) {
+ LOG(DEBUG) << "Finished rm with status " << WEXITSTATUS(status);
+ return (WEXITSTATUS(status) == 0) ? OK : -1;
+ } else {
+ break;
+ }
+ }
+
+ sleep(1);
+ uint64_t deltaFreeBytes = GetFreeBytes(path) - startFreeBytes;
+ notifyProgress(startProgress + CONSTRAIN((int)
+ ((deltaFreeBytes * stepProgress) / expectedBytes), 0, stepProgress));
+ }
+ return -1;
+}
+
+static status_t execCp(const std::string& fromPath, const std::string& toPath,
+ int startProgress, int stepProgress) {
+ notifyProgress(startProgress);
+
+ uint64_t expectedBytes = GetTreeBytes(fromPath);
+ uint64_t startFreeBytes = GetFreeBytes(toPath);
+
+ std::vector<std::string> cmd;
+ cmd.push_back(kCpPath);
+ cmd.push_back("-p"); /* preserve timestamps, ownership, and permissions */
+ cmd.push_back("-R"); /* recurse into subdirectories (DEST must be a directory) */
+ cmd.push_back("-P"); /* Do not follow symlinks [default] */
+ cmd.push_back("-d"); /* don't dereference symlinks */
+ if (pushBackContents(fromPath, cmd) != OK) {
+ LOG(WARNING) << "No contents in " << fromPath;
+ return OK;
+ }
+ cmd.push_back(toPath.c_str());
+
+ pid_t pid = ForkExecvpAsync(cmd);
+ if (pid == -1) return -1;
+
+ int status;
+ while (true) {
+ if (waitpid(pid, &status, WNOHANG) == pid) {
+ if (WIFEXITED(status)) {
+ LOG(DEBUG) << "Finished cp with status " << WEXITSTATUS(status);
+ return (WEXITSTATUS(status) == 0) ? OK : -1;
+ } else {
+ break;
+ }
+ }
+
+ sleep(1);
+ uint64_t deltaFreeBytes = startFreeBytes - GetFreeBytes(toPath);
+ notifyProgress(startProgress + CONSTRAIN((int)
+ ((deltaFreeBytes * stepProgress) / expectedBytes), 0, stepProgress));
+ }
+ return -1;
+}
+
+static void bringOffline(const std::shared_ptr<VolumeBase>& vol) {
+ vol->destroy();
+ vol->setSilent(true);
+ vol->create();
+ vol->setMountFlags(0);
+ vol->mount();
+}
+
+static void bringOnline(const std::shared_ptr<VolumeBase>& vol) {
+ vol->destroy();
+ vol->setSilent(false);
+ vol->create();
+}
+
+void MoveTask::run() {
+ std::string fromPath;
+ std::string toPath;
+
+ // TODO: add support for public volumes
+ if (mFrom->getType() != VolumeBase::Type::kEmulated) goto fail;
+ if (mTo->getType() != VolumeBase::Type::kEmulated) goto fail;
+
+ // Step 1: tear down volumes and mount silently without making
+ // visible to userspace apps
+ bringOffline(mFrom);
+ bringOffline(mTo);
+
+ fromPath = mFrom->getInternalPath();
+ toPath = mTo->getInternalPath();
+
+ // Step 2: clean up any stale data
+ if (execRm(toPath, 10, 10) != OK) {
+ goto fail;
+ }
+
+ // Step 3: perform actual copy
+ if (execCp(fromPath, toPath, 20, 60) != OK) {
+ goto fail;
+ }
+
+ // NOTE: MountService watches for this magic value to know
+ // that move was successful
+ notifyProgress(82);
+ bringOnline(mFrom);
+ bringOnline(mTo);
+
+ // Step 4: clean up old data
+ if (execRm(fromPath, 85, 15) != OK) {
+ goto fail;
+ }
+
+ notifyProgress(kMoveSucceeded);
+ return;
+fail:
+ bringOnline(mFrom);
+ bringOnline(mTo);
+ notifyProgress(kMoveFailedInternalError);
+ return;
+}
+
+} // namespace vold
+} // namespace android
diff --git a/MoveTask.h b/MoveTask.h
new file mode 100644
index 0000000..b1777c0
--- /dev/null
+++ b/MoveTask.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef ANDROID_VOLD_MOVE_TASK_H
+#define ANDROID_VOLD_MOVE_TASK_H
+
+#include "Utils.h"
+#include "VolumeBase.h"
+
+#include <thread>
+
+namespace android {
+namespace vold {
+
+class MoveTask {
+public:
+ MoveTask(const std::shared_ptr<VolumeBase>& from, const std::shared_ptr<VolumeBase>& to);
+ virtual ~MoveTask();
+
+ void start();
+
+private:
+ std::shared_ptr<VolumeBase> mFrom;
+ std::shared_ptr<VolumeBase> mTo;
+ std::thread mThread;
+
+ void run();
+
+ DISALLOW_COPY_AND_ASSIGN(MoveTask);
+};
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/NetlinkManager.cpp b/NetlinkManager.cpp
index d2e16b2..b5069a6 100644
--- a/NetlinkManager.cpp
+++ b/NetlinkManager.cpp
@@ -58,8 +58,8 @@
nladdr.nl_pid = getpid();
nladdr.nl_groups = 0xffffffff;
- if ((mSock = socket(PF_NETLINK,
- SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {
+ if ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC,
+ NETLINK_KOBJECT_UEVENT)) < 0) {
SLOGE("Unable to create uevent socket: %s", strerror(errno));
return -1;
}
diff --git a/PrivateVolume.cpp b/PrivateVolume.cpp
new file mode 100644
index 0000000..ff2c7b3
--- /dev/null
+++ b/PrivateVolume.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "Ext4.h"
+#include "PrivateVolume.h"
+#include "EmulatedVolume.h"
+#include "Utils.h"
+#include "VolumeManager.h"
+#include "ResponseCode.h"
+#include "cryptfs.h"
+
+#include <base/stringprintf.h>
+#include <base/logging.h>
+#include <cutils/fs.h>
+#include <private/android_filesystem_config.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace vold {
+
+PrivateVolume::PrivateVolume(dev_t device, const std::string& keyRaw) :
+ VolumeBase(Type::kPrivate), mRawDevice(device), mKeyRaw(keyRaw) {
+ setId(StringPrintf("private:%u,%u", major(device), minor(device)));
+ mRawDevPath = StringPrintf("/dev/block/vold/%s", getId().c_str());
+}
+
+PrivateVolume::~PrivateVolume() {
+}
+
+status_t PrivateVolume::readMetadata() {
+ status_t res = ReadMetadata(mDmDevPath, mFsType, mFsUuid, mFsLabel);
+ notifyEvent(ResponseCode::VolumeFsTypeChanged, mFsType);
+ notifyEvent(ResponseCode::VolumeFsUuidChanged, mFsUuid);
+ notifyEvent(ResponseCode::VolumeFsLabelChanged, mFsLabel);
+ return res;
+}
+
+status_t PrivateVolume::doCreate() {
+ if (CreateDeviceNode(mRawDevPath, mRawDevice)) {
+ return -EIO;
+ }
+
+ // Recover from stale vold by tearing down any old mappings
+ cryptfs_revert_ext_volume(getId().c_str());
+
+ // TODO: figure out better SELinux labels for private volumes
+
+ unsigned char* key = (unsigned char*) mKeyRaw.data();
+ char crypto_blkdev[MAXPATHLEN];
+ int res = cryptfs_setup_ext_volume(getId().c_str(), mRawDevPath.c_str(),
+ key, mKeyRaw.size(), crypto_blkdev);
+ mDmDevPath = crypto_blkdev;
+ if (res != 0) {
+ PLOG(ERROR) << getId() << " failed to setup cryptfs";
+ return -EIO;
+ }
+
+ return OK;
+}
+
+status_t PrivateVolume::doDestroy() {
+ if (cryptfs_revert_ext_volume(getId().c_str())) {
+ LOG(ERROR) << getId() << " failed to revert cryptfs";
+ }
+ return DestroyDeviceNode(mRawDevPath);
+}
+
+status_t PrivateVolume::doMount() {
+ if (readMetadata()) {
+ LOG(ERROR) << getId() << " failed to read metadata";
+ return -EIO;
+ }
+
+ mPath = StringPrintf("/mnt/expand/%s", mFsUuid.c_str());
+ setPath(mPath);
+
+ if (PrepareDir(mPath, 0700, AID_ROOT, AID_ROOT)) {
+ PLOG(ERROR) << getId() << " failed to create mount point " << mPath;
+ return -EIO;
+ }
+
+ int res = Ext4::check(mDmDevPath.c_str(), mPath.c_str());
+ if (res == 0 || res == 1) {
+ LOG(DEBUG) << getId() << " passed filesystem check";
+ } else {
+ PLOG(ERROR) << getId() << " failed filesystem check";
+ return -EIO;
+ }
+
+ if (Ext4::doMount(mDmDevPath.c_str(), mPath.c_str(), false, false, true)) {
+ PLOG(ERROR) << getId() << " failed to mount";
+ return -EIO;
+ }
+
+ // Verify that common directories are ready to roll
+ if (PrepareDir(mPath + "/app", 0771, AID_SYSTEM, AID_SYSTEM) ||
+ PrepareDir(mPath + "/user", 0711, AID_SYSTEM, AID_SYSTEM) ||
+ PrepareDir(mPath + "/media", 0770, AID_MEDIA_RW, AID_MEDIA_RW) ||
+ PrepareDir(mPath + "/local", 0751, AID_ROOT, AID_ROOT) ||
+ PrepareDir(mPath + "/local/tmp", 0771, AID_SHELL, AID_SHELL)) {
+ PLOG(ERROR) << getId() << " failed to prepare";
+ return -EIO;
+ }
+
+ // Create a new emulated volume stacked above us, it will automatically
+ // be destroyed during unmount
+ std::string mediaPath(mPath + "/media");
+ auto vol = std::shared_ptr<VolumeBase>(
+ new EmulatedVolume(mediaPath, mRawDevice, mFsUuid));
+ addVolume(vol);
+ vol->create();
+
+ return OK;
+}
+
+status_t PrivateVolume::doUnmount() {
+ ForceUnmount(mPath);
+
+ if (TEMP_FAILURE_RETRY(rmdir(mPath.c_str()))) {
+ PLOG(ERROR) << getId() << " failed to rmdir mount point " << mPath;
+ }
+
+ return OK;
+}
+
+status_t PrivateVolume::doFormat() {
+ // TODO: change mountpoint once we have selinux labels
+ if (Ext4::format(mDmDevPath.c_str(), 0, "/data")) {
+ PLOG(ERROR) << getId() << " failed to format";
+ return -EIO;
+ }
+
+ return OK;
+}
+
+} // namespace vold
+} // namespace android
diff --git a/PrivateVolume.h b/PrivateVolume.h
new file mode 100644
index 0000000..bd464e6
--- /dev/null
+++ b/PrivateVolume.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef ANDROID_VOLD_PRIVATE_VOLUME_H
+#define ANDROID_VOLD_PRIVATE_VOLUME_H
+
+#include "VolumeBase.h"
+
+#include <cutils/multiuser.h>
+
+namespace android {
+namespace vold {
+
+/*
+ * Private storage provided by an encrypted partition.
+ *
+ * Given a raw block device, it knows how to wrap it in dm-crypt and
+ * format as ext4/f2fs. EmulatedVolume can be stacked above it.
+ *
+ * This volume is designed to behave much like the internal /data
+ * partition, both in layout and function. For example, apps and
+ * private app data can be safely stored on this volume because the
+ * keys are tightly tied to this device.
+ */
+class PrivateVolume : public VolumeBase {
+public:
+ PrivateVolume(dev_t device, const std::string& keyRaw);
+ virtual ~PrivateVolume();
+
+protected:
+ status_t doCreate() override;
+ status_t doDestroy() override;
+ status_t doMount() override;
+ status_t doUnmount() override;
+ status_t doFormat() override;
+
+ status_t readMetadata();
+
+private:
+ /* Kernel device of raw, encrypted partition */
+ dev_t mRawDevice;
+ /* Path to raw, encrypted block device */
+ std::string mRawDevPath;
+ /* Path to decrypted block device */
+ std::string mDmDevPath;
+ /* Path where decrypted device is mounted */
+ std::string mPath;
+
+ /* Encryption key as raw bytes */
+ std::string mKeyRaw;
+
+ /* Filesystem type */
+ std::string mFsType;
+ /* Filesystem UUID */
+ std::string mFsUuid;
+ /* User-visible filesystem label */
+ std::string mFsLabel;
+
+ DISALLOW_COPY_AND_ASSIGN(PrivateVolume);
+};
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/Process.cpp b/Process.cpp
index cc06998..a6f0cc6 100644
--- a/Process.cpp
+++ b/Process.cpp
@@ -66,7 +66,7 @@
void Process::getProcessName(int pid, char *buffer, size_t max) {
int fd;
snprintf(buffer, max, "/proc/%d/cmdline", pid);
- fd = open(buffer, O_RDONLY);
+ fd = open(buffer, O_RDONLY | O_CLOEXEC);
if (fd < 0) {
strcpy(buffer, "???");
} else {
@@ -170,18 +170,14 @@
return result;
}
-extern "C" void vold_killProcessesWithOpenFiles(const char *path, int action) {
- Process::killProcessesWithOpenFiles(path, action);
+extern "C" void vold_killProcessesWithOpenFiles(const char *path, int signal) {
+ Process::killProcessesWithOpenFiles(path, signal);
}
/*
* Hunt down processes that have files open at the given mount point.
- * action = 0 to just warn,
- * action = 1 to SIGHUP,
- * action = 2 to SIGKILL
*/
-// hunt down and kill processes that have files open on the given mount point
-void Process::killProcessesWithOpenFiles(const char *path, int action) {
+void Process::killProcessesWithOpenFiles(const char *path, int signal) {
DIR* dir;
struct dirent* de;
@@ -213,12 +209,10 @@
} else {
continue;
}
- if (action == 1) {
- SLOGW("Sending SIGHUP to process %d", pid);
- kill(pid, SIGTERM);
- } else if (action == 2) {
- SLOGE("Sending SIGKILL to process %d", pid);
- kill(pid, SIGKILL);
+
+ if (signal != 0) {
+ SLOGW("Sending %s to process %d", strsignal(signal), pid);
+ kill(pid, signal);
}
}
closedir(dir);
diff --git a/Process.h b/Process.h
index e745ca4..81b5f18 100644
--- a/Process.h
+++ b/Process.h
@@ -21,7 +21,7 @@
class Process {
public:
- static void killProcessesWithOpenFiles(const char *path, int action);
+ static void killProcessesWithOpenFiles(const char *path, int signal);
static int getPid(const char *s);
static int checkSymLink(int pid, const char *path, const char *name);
static int checkFileMaps(int pid, const char *path);
@@ -36,7 +36,7 @@
extern "C" {
#endif /* __cplusplus */
- void vold_killProcessesWithOpenFiles(const char *path, int action);
+ void vold_killProcessesWithOpenFiles(const char *path, int signal);
#ifdef __cplusplus
}
#endif
diff --git a/PublicVolume.cpp b/PublicVolume.cpp
new file mode 100644
index 0000000..025d2eb
--- /dev/null
+++ b/PublicVolume.cpp
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "Fat.h"
+#include "PublicVolume.h"
+#include "Utils.h"
+#include "VolumeManager.h"
+#include "ResponseCode.h"
+
+#include <base/stringprintf.h>
+#include <base/logging.h>
+#include <cutils/fs.h>
+#include <private/android_filesystem_config.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace vold {
+
+static const char* kFusePath = "/system/bin/sdcard";
+
+static const char* kAsecPath = "/mnt/secure/asec";
+
+PublicVolume::PublicVolume(dev_t device) :
+ VolumeBase(Type::kPublic), mDevice(device), mFusePid(0) {
+ setId(StringPrintf("public:%u,%u", major(device), minor(device)));
+ mDevPath = StringPrintf("/dev/block/vold/%s", getId().c_str());
+}
+
+PublicVolume::~PublicVolume() {
+}
+
+status_t PublicVolume::readMetadata() {
+ status_t res = ReadMetadataUntrusted(mDevPath, mFsType, mFsUuid, mFsLabel);
+ notifyEvent(ResponseCode::VolumeFsTypeChanged, mFsType);
+ notifyEvent(ResponseCode::VolumeFsUuidChanged, mFsUuid);
+ notifyEvent(ResponseCode::VolumeFsLabelChanged, mFsLabel);
+ return res;
+}
+
+status_t PublicVolume::initAsecStage() {
+ std::string legacyPath(mRawPath + "/android_secure");
+ std::string securePath(mRawPath + "/.android_secure");
+
+ // Recover legacy secure path
+ if (!access(legacyPath.c_str(), R_OK | X_OK)
+ && access(securePath.c_str(), R_OK | X_OK)) {
+ if (rename(legacyPath.c_str(), securePath.c_str())) {
+ PLOG(WARNING) << getId() << " failed to rename legacy ASEC dir";
+ }
+ }
+
+ if (TEMP_FAILURE_RETRY(mkdir(securePath.c_str(), 0700))) {
+ if (errno != EEXIST) {
+ PLOG(WARNING) << getId() << " creating ASEC stage failed";
+ return -errno;
+ }
+ }
+
+ BindMount(securePath, kAsecPath);
+
+ return OK;
+}
+
+status_t PublicVolume::doCreate() {
+ return CreateDeviceNode(mDevPath, mDevice);
+}
+
+status_t PublicVolume::doDestroy() {
+ return DestroyDeviceNode(mDevPath);
+}
+
+status_t PublicVolume::doMount() {
+ // TODO: expand to support mounting other filesystems
+ readMetadata();
+
+ if (Fat::check(mDevPath.c_str())) {
+ LOG(ERROR) << getId() << " failed filesystem check";
+ return -EIO;
+ }
+
+ // Use UUID as stable name, if available
+ std::string stableName = getId();
+ if (!mFsUuid.empty()) {
+ stableName = mFsUuid;
+ }
+
+ mRawPath = StringPrintf("/mnt/media_rw/%s", stableName.c_str());
+ mFusePath = StringPrintf("/storage/%s", stableName.c_str());
+ setPath(mFusePath);
+
+ if (fs_prepare_dir(mRawPath.c_str(), 0700, AID_ROOT, AID_ROOT)) {
+ PLOG(ERROR) << getId() << " failed to create mount point " << mRawPath;
+ return -errno;
+ }
+ if (fs_prepare_dir(mFusePath.c_str(), 0700, AID_ROOT, AID_ROOT)) {
+ PLOG(ERROR) << getId() << " failed to create mount point " << mFusePath;
+ return -errno;
+ }
+
+ if (Fat::doMount(mDevPath.c_str(), mRawPath.c_str(), false, false, false,
+ AID_MEDIA_RW, AID_MEDIA_RW, 0007, true)) {
+ PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
+ return -EIO;
+ }
+
+ if (getMountFlags() & MountFlags::kPrimary) {
+ initAsecStage();
+ }
+
+ // TODO: teach FUSE daemon to protect itself with user-specific GID
+ if (!(mFusePid = fork())) {
+ if (!(getMountFlags() & MountFlags::kVisible)) {
+ // TODO: mount so that only system apps can access
+ if (execl(kFusePath, kFusePath,
+ "-u", "1023", // AID_MEDIA_RW
+ "-g", "1023", // AID_MEDIA_RW
+ mRawPath.c_str(),
+ mFusePath.c_str(),
+ NULL)) {
+ PLOG(ERROR) << "Failed to exec";
+ }
+ } else if (getMountFlags() & MountFlags::kPrimary) {
+ if (execl(kFusePath, kFusePath,
+ "-u", "1023", // AID_MEDIA_RW
+ "-g", "1023", // AID_MEDIA_RW
+ "-d",
+ mRawPath.c_str(),
+ mFusePath.c_str(),
+ NULL)) {
+ PLOG(ERROR) << "Failed to exec";
+ }
+ } else {
+ if (execl(kFusePath, kFusePath,
+ "-u", "1023", // AID_MEDIA_RW
+ "-g", "1023", // AID_MEDIA_RW
+ "-w", "1023", // AID_MEDIA_RW
+ "-d",
+ mRawPath.c_str(),
+ mFusePath.c_str(),
+ NULL)) {
+ PLOG(ERROR) << "Failed to exec";
+ }
+ }
+
+ PLOG(DEBUG) << "FUSE exiting";
+ _exit(1);
+ }
+
+ if (mFusePid == -1) {
+ PLOG(ERROR) << getId() << " failed to fork";
+ return -errno;
+ }
+
+ return OK;
+}
+
+status_t PublicVolume::doUnmount() {
+ if (mFusePid > 0) {
+ kill(mFusePid, SIGTERM);
+ TEMP_FAILURE_RETRY(waitpid(mFusePid, nullptr, 0));
+ mFusePid = 0;
+ }
+
+ ForceUnmount(kAsecPath);
+ ForceUnmount(mFusePath);
+ ForceUnmount(mRawPath);
+
+ if (TEMP_FAILURE_RETRY(rmdir(mRawPath.c_str()))) {
+ PLOG(ERROR) << getId() << " failed to rmdir mount point " << mRawPath;
+ }
+ if (TEMP_FAILURE_RETRY(rmdir(mFusePath.c_str()))) {
+ PLOG(ERROR) << getId() << " failed to rmdir mount point " << mFusePath;
+ }
+
+ mFusePath.clear();
+ mRawPath.clear();
+
+ return OK;
+}
+
+status_t PublicVolume::doFormat() {
+ if (Fat::format(mDevPath.c_str(), 0, true)) {
+ LOG(ERROR) << getId() << " failed to format";
+ return -errno;
+ }
+ return OK;
+}
+
+} // namespace vold
+} // namespace android
diff --git a/PublicVolume.h b/PublicVolume.h
new file mode 100644
index 0000000..45313ec
--- /dev/null
+++ b/PublicVolume.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef ANDROID_VOLD_PUBLIC_VOLUME_H
+#define ANDROID_VOLD_PUBLIC_VOLUME_H
+
+#include "VolumeBase.h"
+
+#include <cutils/multiuser.h>
+
+namespace android {
+namespace vold {
+
+/*
+ * Shared storage provided by public (vfat) partition.
+ *
+ * Knows how to mount itself and then spawn a FUSE daemon to synthesize
+ * permissions. AsecVolume and ObbVolume can be stacked above it.
+ *
+ * This volume is not inherently multi-user aware, so it has two possible
+ * modes of operation:
+ * 1. If primary storage for the device, it only binds itself to the
+ * owner user.
+ * 2. If secondary storage, it binds itself for all users, but masks
+ * away the Android directory for secondary users.
+ */
+class PublicVolume : public VolumeBase {
+public:
+ explicit PublicVolume(dev_t device);
+ virtual ~PublicVolume();
+
+protected:
+ status_t doCreate() override;
+ status_t doDestroy() override;
+ status_t doMount() override;
+ status_t doUnmount() override;
+ status_t doFormat() override;
+
+ status_t readMetadata();
+ status_t initAsecStage();
+
+private:
+ /* Kernel device representing partition */
+ dev_t mDevice;
+ /* Block device path */
+ std::string mDevPath;
+ /* Mount point of raw partition */
+ std::string mRawPath;
+ /* Mount point of FUSE wrapper */
+ std::string mFusePath;
+ /* PID of FUSE wrapper */
+ pid_t mFusePid;
+
+ /* Filesystem type */
+ std::string mFsType;
+ /* Filesystem UUID */
+ std::string mFsUuid;
+ /* User-visible filesystem label */
+ std::string mFsLabel;
+
+ DISALLOW_COPY_AND_ASSIGN(PublicVolume);
+};
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/ResponseCode.h b/ResponseCode.h
index 0dc0500..a975982 100644
--- a/ResponseCode.h
+++ b/ResponseCode.h
@@ -66,6 +66,23 @@
static const int VolumeDiskRemoved = 631;
static const int VolumeBadRemoval = 632;
+ static const int DiskCreated = 640;
+ static const int DiskSizeChanged = 641;
+ static const int DiskLabelChanged = 642;
+ static const int DiskScanned = 643;
+ static const int DiskDestroyed = 649;
+
+ static const int VolumeCreated = 650;
+ static const int VolumeStateChanged = 651;
+ static const int VolumeFsTypeChanged = 652;
+ static const int VolumeFsUuidChanged = 653;
+ static const int VolumeFsLabelChanged = 654;
+ static const int VolumePathChanged = 655;
+ static const int VolumeInternalPathChanged = 656;
+ static const int VolumeDestroyed = 659;
+
+ static const int MoveStatus = 660;
+
static int convertFromErrno();
};
#endif
diff --git a/Utils.cpp b/Utils.cpp
new file mode 100644
index 0000000..f45907a
--- /dev/null
+++ b/Utils.cpp
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "sehandle.h"
+#include "Utils.h"
+#include "Process.h"
+
+#include <base/logging.h>
+#include <base/stringprintf.h>
+#include <cutils/fs.h>
+#include <private/android_filesystem_config.h>
+#include <logwrap/logwrap.h>
+
+#include <mutex>
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/statvfs.h>
+
+#ifndef UMOUNT_NOFOLLOW
+#define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */
+#endif
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace vold {
+
+security_context_t sBlkidContext = nullptr;
+security_context_t sBlkidUntrustedContext = nullptr;
+security_context_t sFsckContext = nullptr;
+security_context_t sFsckUntrustedContext = nullptr;
+
+static const char* kBlkidPath = "/system/bin/blkid";
+
+status_t CreateDeviceNode(const std::string& path, dev_t dev) {
+ const char* cpath = path.c_str();
+ status_t res = 0;
+
+ char* secontext = nullptr;
+ if (sehandle) {
+ if (!selabel_lookup(sehandle, &secontext, cpath, S_IFBLK)) {
+ setfscreatecon(secontext);
+ }
+ }
+
+ mode_t mode = 0660 | S_IFBLK;
+ if (mknod(cpath, mode, dev) < 0) {
+ if (errno != EEXIST) {
+ PLOG(ERROR) << "Failed to create device node for " << major(dev)
+ << ":" << minor(dev) << " at " << path;
+ res = -errno;
+ }
+ }
+
+ if (secontext) {
+ setfscreatecon(nullptr);
+ freecon(secontext);
+ }
+
+ return res;
+}
+
+status_t DestroyDeviceNode(const std::string& path) {
+ const char* cpath = path.c_str();
+ if (TEMP_FAILURE_RETRY(unlink(cpath))) {
+ return -errno;
+ } else {
+ return OK;
+ }
+}
+
+status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
+ const char* cpath = path.c_str();
+
+ char* secontext = nullptr;
+ if (sehandle) {
+ if (!selabel_lookup(sehandle, &secontext, cpath, S_IFDIR)) {
+ setfscreatecon(secontext);
+ }
+ }
+
+ int res = fs_prepare_dir(cpath, mode, uid, gid);
+
+ if (secontext) {
+ setfscreatecon(nullptr);
+ freecon(secontext);
+ }
+
+ if (res == 0) {
+ return OK;
+ } else {
+ return -errno;
+ }
+}
+
+status_t ForceUnmount(const std::string& path) {
+ const char* cpath = path.c_str();
+ if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+ return OK;
+ }
+ PLOG(WARNING) << "Failed to unmount " << path;
+
+ sleep(5);
+ Process::killProcessesWithOpenFiles(cpath, SIGINT);
+
+ if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+ return OK;
+ }
+ PLOG(WARNING) << "Failed to unmount " << path;
+
+ sleep(5);
+ Process::killProcessesWithOpenFiles(cpath, SIGTERM);
+
+ if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+ return OK;
+ }
+ PLOG(WARNING) << "Failed to unmount " << path;
+
+ sleep(5);
+ Process::killProcessesWithOpenFiles(cpath, SIGKILL);
+
+ if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+ return OK;
+ }
+ PLOG(ERROR) << "Failed to unmount " << path;
+
+ return -errno;
+}
+
+status_t BindMount(const std::string& source, const std::string& target) {
+ if (::mount(source.c_str(), target.c_str(), "", MS_BIND, NULL)) {
+ PLOG(ERROR) << "Failed to bind mount " << source << " to " << target;
+ return -errno;
+ }
+ return OK;
+}
+
+static status_t readMetadata(const std::string& path, std::string& fsType,
+ std::string& fsUuid, std::string& fsLabel, bool untrusted) {
+ fsType.clear();
+ fsUuid.clear();
+ fsLabel.clear();
+
+ std::vector<std::string> cmd;
+ cmd.push_back(kBlkidPath);
+ cmd.push_back("-c");
+ cmd.push_back("/dev/null");
+ cmd.push_back(path);
+
+ std::vector<std::string> output;
+ status_t res = ForkExecvp(cmd, output, untrusted ? sBlkidUntrustedContext : sBlkidContext);
+ if (res != OK) {
+ LOG(WARNING) << "blkid failed to identify " << path;
+ return res;
+ }
+
+ char value[128];
+ for (auto line : output) {
+ // Extract values from blkid output, if defined
+ const char* cline = line.c_str();
+ char* start = strstr(cline, "TYPE=");
+ if (start != nullptr && sscanf(start + 5, "\"%127[^\"]\"", value) == 1) {
+ fsType = value;
+ }
+
+ start = strstr(cline, "UUID=");
+ if (start != nullptr && sscanf(start + 5, "\"%127[^\"]\"", value) == 1) {
+ fsUuid = value;
+ }
+
+ start = strstr(cline, "LABEL=");
+ if (start != nullptr && sscanf(start + 6, "\"%127[^\"]\"", value) == 1) {
+ fsLabel = value;
+ }
+ }
+
+ return OK;
+}
+
+status_t ReadMetadata(const std::string& path, std::string& fsType,
+ std::string& fsUuid, std::string& fsLabel) {
+ return readMetadata(path, fsType, fsUuid, fsLabel, false);
+}
+
+status_t ReadMetadataUntrusted(const std::string& path, std::string& fsType,
+ std::string& fsUuid, std::string& fsLabel) {
+ return readMetadata(path, fsType, fsUuid, fsLabel, true);
+}
+
+status_t ForkExecvp(const std::vector<std::string>& args) {
+ return ForkExecvp(args, nullptr);
+}
+
+status_t ForkExecvp(const std::vector<std::string>& args, security_context_t context) {
+ size_t argc = args.size();
+ char** argv = (char**) calloc(argc, sizeof(char*));
+ for (size_t i = 0; i < argc; i++) {
+ argv[i] = (char*) args[i].c_str();
+ if (i == 0) {
+ LOG(VERBOSE) << args[i];
+ } else {
+ LOG(VERBOSE) << " " << args[i];
+ }
+ }
+
+ if (setexeccon(context)) {
+ LOG(ERROR) << "Failed to setexeccon";
+ abort();
+ }
+ status_t res = android_fork_execvp(argc, argv, NULL, false, true);
+ if (setexeccon(nullptr)) {
+ LOG(ERROR) << "Failed to setexeccon";
+ abort();
+ }
+
+ free(argv);
+ return res;
+}
+
+status_t ForkExecvp(const std::vector<std::string>& args,
+ std::vector<std::string>& output) {
+ return ForkExecvp(args, output, nullptr);
+}
+
+status_t ForkExecvp(const std::vector<std::string>& args,
+ std::vector<std::string>& output, security_context_t context) {
+ std::string cmd;
+ for (size_t i = 0; i < args.size(); i++) {
+ cmd += args[i] + " ";
+ if (i == 0) {
+ LOG(VERBOSE) << args[i];
+ } else {
+ LOG(VERBOSE) << " " << args[i];
+ }
+ }
+ output.clear();
+
+ if (setexeccon(context)) {
+ LOG(ERROR) << "Failed to setexeccon";
+ abort();
+ }
+ FILE* fp = popen(cmd.c_str(), "r");
+ if (setexeccon(nullptr)) {
+ LOG(ERROR) << "Failed to setexeccon";
+ abort();
+ }
+
+ if (!fp) {
+ PLOG(ERROR) << "Failed to popen " << cmd;
+ return -errno;
+ }
+ char line[1024];
+ while (fgets(line, sizeof(line), fp) != nullptr) {
+ LOG(VERBOSE) << line;
+ output.push_back(std::string(line));
+ }
+ if (pclose(fp) != 0) {
+ PLOG(ERROR) << "Failed to pclose " << cmd;
+ return -errno;
+ }
+
+ return OK;
+}
+
+pid_t ForkExecvpAsync(const std::vector<std::string>& args) {
+ size_t argc = args.size();
+ char** argv = (char**) calloc(argc + 1, sizeof(char*));
+ for (size_t i = 0; i < argc; i++) {
+ argv[i] = (char*) args[i].c_str();
+ if (i == 0) {
+ LOG(VERBOSE) << args[i];
+ } else {
+ LOG(VERBOSE) << " " << args[i];
+ }
+ }
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ if (execvp(argv[0], argv)) {
+ PLOG(ERROR) << "Failed to exec";
+ }
+
+ _exit(1);
+ }
+
+ if (pid == -1) {
+ PLOG(ERROR) << "Failed to exec";
+ }
+
+ free(argv);
+ return pid;
+}
+
+status_t ReadRandomBytes(size_t bytes, std::string& out) {
+ out.clear();
+
+ int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+ if (fd == -1) {
+ return -errno;
+ }
+
+ char buf[BUFSIZ];
+ size_t n;
+ while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], std::min(sizeof(buf), bytes)))) > 0) {
+ out.append(buf, n);
+ bytes -= n;
+ }
+ TEMP_FAILURE_RETRY(close(fd));
+
+ if (bytes == 0) {
+ return OK;
+ } else {
+ return -EIO;
+ }
+}
+
+status_t HexToStr(const std::string& hex, std::string& str) {
+ str.clear();
+ bool even = true;
+ char cur = 0;
+ for (size_t i = 0; i < hex.size(); i++) {
+ int val = 0;
+ switch (hex[i]) {
+ case ' ': case '-': case ':': continue;
+ case 'f': case 'F': val = 15; break;
+ case 'e': case 'E': val = 14; break;
+ case 'd': case 'D': val = 13; break;
+ case 'c': case 'C': val = 12; break;
+ case 'b': case 'B': val = 11; break;
+ case 'a': case 'A': val = 10; break;
+ case '9': val = 9; break;
+ case '8': val = 8; break;
+ case '7': val = 7; break;
+ case '6': val = 6; break;
+ case '5': val = 5; break;
+ case '4': val = 4; break;
+ case '3': val = 3; break;
+ case '2': val = 2; break;
+ case '1': val = 1; break;
+ case '0': val = 0; break;
+ default: return -EINVAL;
+ }
+
+ if (even) {
+ cur = val << 4;
+ } else {
+ cur += val;
+ str.push_back(cur);
+ cur = 0;
+ }
+ even = !even;
+ }
+ return even ? OK : -EINVAL;
+}
+
+static const char* kLookup = "0123456789abcdef";
+
+status_t StrToHex(const std::string& str, std::string& hex) {
+ hex.clear();
+ for (size_t i = 0; i < str.size(); i++) {
+ hex.push_back(kLookup[(str[i] & 0xF0) >> 4]);
+ hex.push_back(kLookup[str[i] & 0x0F]);
+ }
+ return OK;
+}
+
+uint64_t GetFreeBytes(const std::string& path) {
+ struct statvfs sb;
+ if (statvfs(path.c_str(), &sb) == 0) {
+ return sb.f_bfree * sb.f_bsize;
+ } else {
+ return -1;
+ }
+}
+
+// TODO: borrowed from frameworks/native/libs/diskusage/ which should
+// eventually be migrated into system/
+static int64_t stat_size(struct stat *s) {
+ int64_t blksize = s->st_blksize;
+ // count actual blocks used instead of nominal file size
+ int64_t size = s->st_blocks * 512;
+
+ if (blksize) {
+ /* round up to filesystem block size */
+ size = (size + blksize - 1) & (~(blksize - 1));
+ }
+
+ return size;
+}
+
+// TODO: borrowed from frameworks/native/libs/diskusage/ which should
+// eventually be migrated into system/
+int64_t calculate_dir_size(int dfd) {
+ int64_t size = 0;
+ struct stat s;
+ DIR *d;
+ struct dirent *de;
+
+ d = fdopendir(dfd);
+ if (d == NULL) {
+ close(dfd);
+ return 0;
+ }
+
+ while ((de = readdir(d))) {
+ const char *name = de->d_name;
+ if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
+ size += stat_size(&s);
+ }
+ if (de->d_type == DT_DIR) {
+ int subfd;
+
+ /* always skip "." and ".." */
+ if (name[0] == '.') {
+ if (name[1] == 0)
+ continue;
+ if ((name[1] == '.') && (name[2] == 0))
+ continue;
+ }
+
+ subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY);
+ if (subfd >= 0) {
+ size += calculate_dir_size(subfd);
+ }
+ }
+ }
+ closedir(d);
+ return size;
+}
+
+uint64_t GetTreeBytes(const std::string& path) {
+ int dirfd = open(path.c_str(), O_DIRECTORY, O_RDONLY);
+ if (dirfd < 0) {
+ PLOG(WARNING) << "Failed to open " << path;
+ return -1;
+ } else {
+ uint64_t res = calculate_dir_size(dirfd);
+ close(dirfd);
+ return res;
+ }
+}
+
+} // namespace vold
+} // namespace android
diff --git a/Utils.h b/Utils.h
new file mode 100644
index 0000000..6ad87ae
--- /dev/null
+++ b/Utils.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef ANDROID_VOLD_UTILS_H
+#define ANDROID_VOLD_UTILS_H
+
+#include <utils/Errors.h>
+#include <selinux/selinux.h>
+
+#include <vector>
+#include <string>
+
+// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. It goes in the private:
+// declarations in a class.
+#if !defined(DISALLOW_COPY_AND_ASSIGN)
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&) = delete; \
+ void operator=(const TypeName&) = delete
+#endif
+
+namespace android {
+namespace vold {
+
+/* SELinux contexts used depending on the block device type */
+extern security_context_t sBlkidContext;
+extern security_context_t sBlkidUntrustedContext;
+extern security_context_t sFsckContext;
+extern security_context_t sFsckUntrustedContext;
+
+status_t CreateDeviceNode(const std::string& path, dev_t dev);
+status_t DestroyDeviceNode(const std::string& path);
+
+/* fs_prepare_dir wrapper that creates with SELinux context */
+status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid);
+
+/* Really unmounts the path, killing active processes along the way */
+status_t ForceUnmount(const std::string& path);
+
+/* Creates bind mount from source to target */
+status_t BindMount(const std::string& source, const std::string& target);
+
+/* Reads filesystem metadata from device at path */
+status_t ReadMetadata(const std::string& path, std::string& fsType,
+ std::string& fsUuid, std::string& fsLabel);
+
+/* Reads filesystem metadata from untrusted device at path */
+status_t ReadMetadataUntrusted(const std::string& path, std::string& fsType,
+ std::string& fsUuid, std::string& fsLabel);
+
+/* Returns either WEXITSTATUS() status, or a negative errno */
+status_t ForkExecvp(const std::vector<std::string>& args);
+status_t ForkExecvp(const std::vector<std::string>& args, security_context_t context);
+
+status_t ForkExecvp(const std::vector<std::string>& args,
+ std::vector<std::string>& output);
+status_t ForkExecvp(const std::vector<std::string>& args,
+ std::vector<std::string>& output, security_context_t context);
+
+pid_t ForkExecvpAsync(const std::vector<std::string>& args);
+
+status_t ReadRandomBytes(size_t bytes, std::string& out);
+
+/* Converts hex string to raw bytes, ignoring [ :-] */
+status_t HexToStr(const std::string& hex, std::string& str);
+/* Converts raw bytes to hex string */
+status_t StrToHex(const std::string& str, std::string& hex);
+
+uint64_t GetFreeBytes(const std::string& path);
+uint64_t GetTreeBytes(const std::string& path);
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/Volume.cpp b/Volume.cpp
deleted file mode 100644
index ce4ed1e..0000000
--- a/Volume.cpp
+++ /dev/null
@@ -1,707 +0,0 @@
-/*
- * 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.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <mntent.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/mman.h>
-#include <sys/mount.h>
-#include <sys/param.h>
-
-#include <linux/kdev_t.h>
-
-#include <cutils/properties.h>
-
-#include <diskconfig/diskconfig.h>
-
-#include <private/android_filesystem_config.h>
-
-#define LOG_TAG "Vold"
-
-#include <cutils/fs.h>
-#include <cutils/log.h>
-
-#include <string>
-
-#include "Volume.h"
-#include "VolumeManager.h"
-#include "ResponseCode.h"
-#include "Fat.h"
-#include "Process.h"
-#include "cryptfs.h"
-#include "sehandle.h"
-
-extern "C" void dos_partition_dec(void const *pp, struct dos_partition *d);
-extern "C" void dos_partition_enc(void *pp, struct dos_partition *d);
-
-
-/*
- * Media directory - stuff that only media_rw user can see
- */
-const char *Volume::MEDIA_DIR = "/mnt/media_rw";
-
-/*
- * Fuse directory - location where fuse wrapped filesystems go
- */
-const char *Volume::FUSE_DIR = "/storage";
-
-/*
- * Path to external storage where *only* root can access ASEC image files
- */
-const char *Volume::SEC_ASECDIR_EXT = "/mnt/secure/asec";
-
-/*
- * Path to internal storage where *only* root can access ASEC image files
- */
-const char *Volume::SEC_ASECDIR_INT = "/data/app-asec";
-
-/*
- * Path to where secure containers are mounted
- */
-const char *Volume::ASECDIR = "/mnt/asec";
-
-/*
- * Path to where OBBs are mounted
- */
-const char *Volume::LOOPDIR = "/mnt/obb";
-
-const char *Volume::BLKID_PATH = "/system/bin/blkid";
-
-static const char *stateToStr(int state) {
- if (state == Volume::State_Init)
- return "Initializing";
- else if (state == Volume::State_NoMedia)
- return "No-Media";
- else if (state == Volume::State_Idle)
- return "Idle-Unmounted";
- else if (state == Volume::State_Pending)
- return "Pending";
- else if (state == Volume::State_Mounted)
- return "Mounted";
- else if (state == Volume::State_Unmounting)
- return "Unmounting";
- else if (state == Volume::State_Checking)
- return "Checking";
- else if (state == Volume::State_Formatting)
- return "Formatting";
- else if (state == Volume::State_Shared)
- return "Shared-Unmounted";
- else if (state == Volume::State_SharedMnt)
- return "Shared-Mounted";
- else
- return "Unknown-Error";
-}
-
-Volume::Volume(VolumeManager *vm, const fstab_rec* rec, int flags) {
- mVm = vm;
- mDebug = false;
- mLabel = strdup(rec->label);
- mUuid = NULL;
- mUserLabel = NULL;
- mState = Volume::State_Init;
- mFlags = flags;
- mCurrentlyMountedKdev = -1;
- mPartIdx = rec->partnum;
- mRetryMount = false;
-}
-
-Volume::~Volume() {
- free(mLabel);
- free(mUuid);
- free(mUserLabel);
-}
-
-void Volume::setDebug(bool enable) {
- mDebug = enable;
-}
-
-dev_t Volume::getDiskDevice() {
- return MKDEV(0, 0);
-};
-
-dev_t Volume::getShareDevice() {
- return getDiskDevice();
-}
-
-void Volume::handleVolumeShared() {
-}
-
-void Volume::handleVolumeUnshared() {
-}
-
-int Volume::handleBlockEvent(NetlinkEvent * /*evt*/) {
- errno = ENOSYS;
- return -1;
-}
-
-void Volume::setUuid(const char* uuid) {
- char msg[256];
-
- if (mUuid) {
- free(mUuid);
- }
-
- if (uuid) {
- mUuid = strdup(uuid);
- snprintf(msg, sizeof(msg), "%s %s \"%s\"", getLabel(),
- getFuseMountpoint(), mUuid);
- } else {
- mUuid = NULL;
- snprintf(msg, sizeof(msg), "%s %s", getLabel(), getFuseMountpoint());
- }
-
- mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeUuidChange, msg,
- false);
-}
-
-void Volume::setUserLabel(const char* userLabel) {
- char msg[256];
-
- if (mUserLabel) {
- free(mUserLabel);
- }
-
- if (userLabel) {
- mUserLabel = strdup(userLabel);
- snprintf(msg, sizeof(msg), "%s %s \"%s\"", getLabel(),
- getFuseMountpoint(), mUserLabel);
- } else {
- mUserLabel = NULL;
- snprintf(msg, sizeof(msg), "%s %s", getLabel(), getFuseMountpoint());
- }
-
- mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeUserLabelChange,
- msg, false);
-}
-
-void Volume::setState(int state) {
- char msg[255];
- int oldState = mState;
-
- if (oldState == state) {
- SLOGW("Duplicate state (%d)\n", state);
- return;
- }
-
- if ((oldState == Volume::State_Pending) && (state != Volume::State_Idle)) {
- mRetryMount = false;
- }
-
- mState = state;
-
- SLOGD("Volume %s state changing %d (%s) -> %d (%s)", mLabel,
- oldState, stateToStr(oldState), mState, stateToStr(mState));
- snprintf(msg, sizeof(msg),
- "Volume %s %s state changed from %d (%s) to %d (%s)", getLabel(),
- getFuseMountpoint(), oldState, stateToStr(oldState), mState,
- stateToStr(mState));
-
- mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeStateChange,
- msg, false);
-}
-
-int Volume::createDeviceNode(const char *path, int major, int minor) {
- char *secontext = NULL;
- mode_t mode = 0660 | S_IFBLK;
- dev_t dev = (major << 8) | minor;
- int rc;
- if (sehandle) {
- rc = selabel_lookup(sehandle, &secontext, path, S_IFBLK);
- if (rc == 0)
- setfscreatecon(secontext);
- }
- if (mknod(path, mode, dev) < 0) {
- if (errno != EEXIST) {
- int sverrno = errno;
- if (secontext) {
- freecon(secontext);
- setfscreatecon(NULL);
- }
- errno = sverrno;
- return -1;
- }
- }
- if (secontext) {
- setfscreatecon(NULL);
- freecon(secontext);
- }
- return 0;
-}
-
-int Volume::formatVol(bool wipe) {
-
- if (getState() == Volume::State_NoMedia) {
- errno = ENODEV;
- return -1;
- } else if (getState() != Volume::State_Idle) {
- errno = EBUSY;
- return -1;
- }
-
- if (isMountpointMounted(getMountpoint())) {
- SLOGW("Volume is idle but appears to be mounted - fixing");
- setState(Volume::State_Mounted);
- // mCurrentlyMountedKdev = XXX
- errno = EBUSY;
- return -1;
- }
-
- bool formatEntireDevice = (mPartIdx == -1);
- char devicePath[255];
- dev_t diskNode = getDiskDevice();
- dev_t partNode =
- MKDEV(MAJOR(diskNode),
- MINOR(diskNode) + (formatEntireDevice ? 0 : mPartIdx));
-
- setState(Volume::State_Formatting);
-
- int ret = -1;
- // Only initialize the MBR if we are formatting the entire device
- if (formatEntireDevice) {
- sprintf(devicePath, "/dev/block/vold/%d:%d",
- major(diskNode), minor(diskNode));
-
- if (initializeMbr(devicePath)) {
- SLOGE("Failed to initialize MBR (%s)", strerror(errno));
- goto err;
- }
- }
-
- sprintf(devicePath, "/dev/block/vold/%d:%d",
- major(partNode), minor(partNode));
-
- if (mDebug) {
- SLOGI("Formatting volume %s (%s)", getLabel(), devicePath);
- }
-
- if (Fat::format(devicePath, 0, wipe)) {
- SLOGE("Failed to format (%s)", strerror(errno));
- goto err;
- }
-
- ret = 0;
-
-err:
- setState(Volume::State_Idle);
- return ret;
-}
-
-bool Volume::isMountpointMounted(const char *path) {
- FILE *fp = setmntent("/proc/mounts", "r");
- if (fp == NULL) {
- SLOGE("Error opening /proc/mounts (%s)", strerror(errno));
- return false;
- }
-
- bool found_path = false;
- mntent* mentry;
- while ((mentry = getmntent(fp)) != NULL) {
- if (strcmp(mentry->mnt_dir, path) == 0) {
- found_path = true;
- break;
- }
- }
- endmntent(fp);
- return found_path;
-}
-
-int Volume::mountVol() {
- dev_t deviceNodes[4];
- int n, i;
- char errmsg[255];
-
- int flags = getFlags();
- bool providesAsec = (flags & VOL_PROVIDES_ASEC) != 0;
-
- // TODO: handle "bind" style mounts, for emulated storage
-
- char decrypt_state[PROPERTY_VALUE_MAX];
- char crypto_state[PROPERTY_VALUE_MAX];
- char encrypt_progress[PROPERTY_VALUE_MAX];
-
- property_get("vold.decrypt", decrypt_state, "");
- property_get("vold.encrypt_progress", encrypt_progress, "");
-
- /* Don't try to mount the volumes if we have not yet entered the disk password
- * or are in the process of encrypting.
- */
- if ((getState() == Volume::State_NoMedia) ||
- ((!strcmp(decrypt_state, "1") || encrypt_progress[0]) && providesAsec)) {
- snprintf(errmsg, sizeof(errmsg),
- "Volume %s %s mount failed - no media",
- getLabel(), getFuseMountpoint());
- mVm->getBroadcaster()->sendBroadcast(
- ResponseCode::VolumeMountFailedNoMedia,
- errmsg, false);
- errno = ENODEV;
- return -1;
- } else if (getState() != Volume::State_Idle) {
- errno = EBUSY;
- if (getState() == Volume::State_Pending) {
- mRetryMount = true;
- }
- return -1;
- }
-
- if (isMountpointMounted(getMountpoint())) {
- SLOGW("Volume is idle but appears to be mounted - fixing");
- setState(Volume::State_Mounted);
- // mCurrentlyMountedKdev = XXX
- return 0;
- }
-
- n = getDeviceNodes((dev_t *) &deviceNodes, 4);
- if (!n) {
- SLOGE("Failed to get device nodes (%s)\n", strerror(errno));
- return -1;
- }
-
- /* If we're running encrypted, and the volume is marked as encryptable and nonremovable,
- * and also marked as providing Asec storage, then we need to decrypt
- * that partition, and update the volume object to point to it's new decrypted
- * block device
- */
- property_get("ro.crypto.state", crypto_state, "");
- if (providesAsec &&
- ((flags & (VOL_NONREMOVABLE | VOL_ENCRYPTABLE))==(VOL_NONREMOVABLE | VOL_ENCRYPTABLE)) &&
- !strcmp(crypto_state, "encrypted") && !isDecrypted()) {
- char new_sys_path[MAXPATHLEN];
- char nodepath[256];
- int new_major, new_minor;
-
- if (n != 1) {
- /* We only expect one device node returned when mounting encryptable volumes */
- SLOGE("Too many device nodes returned when mounting %s\n", getMountpoint());
- return -1;
- }
-
- if (cryptfs_setup_volume(getLabel(), MAJOR(deviceNodes[0]), MINOR(deviceNodes[0]),
- new_sys_path, sizeof(new_sys_path),
- &new_major, &new_minor)) {
- SLOGE("Cannot setup encryption mapping for %s\n", getMountpoint());
- return -1;
- }
- /* We now have the new sysfs path for the decrypted block device, and the
- * majore and minor numbers for it. So, create the device, update the
- * path to the new sysfs path, and continue.
- */
- snprintf(nodepath,
- sizeof(nodepath), "/dev/block/vold/%d:%d",
- new_major, new_minor);
- if (createDeviceNode(nodepath, new_major, new_minor)) {
- SLOGE("Error making device node '%s' (%s)", nodepath,
- strerror(errno));
- }
-
- // Todo: Either create sys filename from nodepath, or pass in bogus path so
- // vold ignores state changes on this internal device.
- updateDeviceInfo(nodepath, new_major, new_minor);
-
- /* Get the device nodes again, because they just changed */
- n = getDeviceNodes((dev_t *) &deviceNodes, 4);
- if (!n) {
- SLOGE("Failed to get device nodes (%s)\n", strerror(errno));
- return -1;
- }
- }
-
- for (i = 0; i < n; i++) {
- char devicePath[255];
-
- sprintf(devicePath, "/dev/block/vold/%d:%d", major(deviceNodes[i]),
- minor(deviceNodes[i]));
-
- SLOGI("%s being considered for volume %s\n", devicePath, getLabel());
-
- errno = 0;
- setState(Volume::State_Checking);
-
- if (Fat::check(devicePath)) {
- if (errno == ENODATA) {
- SLOGW("%s does not contain a FAT filesystem\n", devicePath);
- continue;
- }
- errno = EIO;
- /* Badness - abort the mount */
- SLOGE("%s failed FS checks (%s)", devicePath, strerror(errno));
- setState(Volume::State_Idle);
- return -1;
- }
-
- errno = 0;
-
- if (Fat::doMount(devicePath, getMountpoint(), false, false, false,
- AID_MEDIA_RW, AID_MEDIA_RW, 0007, true)) {
- SLOGE("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));
- continue;
- }
-
- extractMetadata(devicePath);
-
- if (providesAsec && mountAsecExternal() != 0) {
- SLOGE("Failed to mount secure area (%s)", strerror(errno));
- umount(getMountpoint());
- setState(Volume::State_Idle);
- return -1;
- }
-
- char service[64];
- snprintf(service, 64, "fuse_%s", getLabel());
- property_set("ctl.start", service);
-
- setState(Volume::State_Mounted);
- mCurrentlyMountedKdev = deviceNodes[i];
- return 0;
- }
-
- SLOGE("Volume %s found no suitable devices for mounting :(\n", getLabel());
- setState(Volume::State_Idle);
-
- return -1;
-}
-
-int Volume::mountAsecExternal() {
- char legacy_path[PATH_MAX];
- char secure_path[PATH_MAX];
-
- snprintf(legacy_path, PATH_MAX, "%s/android_secure", getMountpoint());
- snprintf(secure_path, PATH_MAX, "%s/.android_secure", getMountpoint());
-
- // Recover legacy secure path
- if (!access(legacy_path, R_OK | X_OK) && access(secure_path, R_OK | X_OK)) {
- if (rename(legacy_path, secure_path)) {
- SLOGE("Failed to rename legacy asec dir (%s)", strerror(errno));
- }
- }
-
- if (fs_prepare_dir(secure_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) != 0) {
- SLOGW("fs_prepare_dir failed: %s", strerror(errno));
- return -1;
- }
-
- if (mount(secure_path, SEC_ASECDIR_EXT, "", MS_BIND, NULL)) {
- SLOGE("Failed to bind mount points %s -> %s (%s)", secure_path,
- SEC_ASECDIR_EXT, strerror(errno));
- return -1;
- }
-
- return 0;
-}
-
-int Volume::doUnmount(const char *path, bool force) {
- int retries = 10;
-
- if (mDebug) {
- SLOGD("Unmounting {%s}, force = %d", path, force);
- }
-
- while (retries--) {
- if (!umount(path) || errno == EINVAL || errno == ENOENT) {
- SLOGI("%s sucessfully unmounted", path);
- return 0;
- }
-
- int action = 0;
-
- if (force) {
- if (retries == 1) {
- action = 2; // SIGKILL
- } else if (retries == 2) {
- action = 1; // SIGHUP
- }
- }
-
- SLOGW("Failed to unmount %s (%s, retries %d, action %d)",
- path, strerror(errno), retries, action);
-
- Process::killProcessesWithOpenFiles(path, action);
- usleep(1000*1000);
- }
- errno = EBUSY;
- SLOGE("Giving up on unmount %s (%s)", path, strerror(errno));
- return -1;
-}
-
-int Volume::unmountVol(bool force, bool revert) {
- int flags = getFlags();
- bool providesAsec = (flags & VOL_PROVIDES_ASEC) != 0;
-
- if (getState() != Volume::State_Mounted) {
- SLOGE("Volume %s unmount request when not mounted", getLabel());
- errno = EINVAL;
- return UNMOUNT_NOT_MOUNTED_ERR;
- }
-
- setState(Volume::State_Unmounting);
- usleep(1000 * 1000); // Give the framework some time to react
-
- char service[64];
- snprintf(service, 64, "fuse_%s", getLabel());
- property_set("ctl.stop", service);
- /* Give it a chance to stop. I wish we had a synchronous way to determine this... */
- sleep(1);
-
- // TODO: determine failure mode if FUSE times out
-
- if (providesAsec && doUnmount(Volume::SEC_ASECDIR_EXT, force) != 0) {
- SLOGE("Failed to unmount secure area on %s (%s)", getMountpoint(), strerror(errno));
- goto out_mounted;
- }
-
- /* Now that the fuse daemon is dead, unmount it */
- if (doUnmount(getFuseMountpoint(), force) != 0) {
- SLOGE("Failed to unmount %s (%s)", getFuseMountpoint(), strerror(errno));
- goto fail_remount_secure;
- }
-
- /* Unmount the real sd card */
- if (doUnmount(getMountpoint(), force) != 0) {
- SLOGE("Failed to unmount %s (%s)", getMountpoint(), strerror(errno));
- goto fail_remount_secure;
- }
-
- SLOGI("%s unmounted successfully", getMountpoint());
-
- /* If this is an encrypted volume, and we've been asked to undo
- * the crypto mapping, then revert the dm-crypt mapping, and revert
- * the device info to the original values.
- */
- if (revert && isDecrypted()) {
- cryptfs_revert_volume(getLabel());
- revertDeviceInfo();
- SLOGI("Encrypted volume %s reverted successfully", getMountpoint());
- }
-
- setUuid(NULL);
- setUserLabel(NULL);
- setState(Volume::State_Idle);
- mCurrentlyMountedKdev = -1;
- return 0;
-
-fail_remount_secure:
- if (providesAsec && mountAsecExternal() != 0) {
- SLOGE("Failed to remount secure area (%s)", strerror(errno));
- goto out_nomedia;
- }
-
-out_mounted:
- setState(Volume::State_Mounted);
- return -1;
-
-out_nomedia:
- setState(Volume::State_NoMedia);
- return -1;
-}
-
-int Volume::initializeMbr(const char *deviceNode) {
- struct disk_info dinfo;
-
- memset(&dinfo, 0, sizeof(dinfo));
-
- if (!(dinfo.part_lst = (struct part_info *) malloc(MAX_NUM_PARTS * sizeof(struct part_info)))) {
- SLOGE("Failed to malloc prt_lst");
- return -1;
- }
-
- memset(dinfo.part_lst, 0, MAX_NUM_PARTS * sizeof(struct part_info));
- dinfo.device = strdup(deviceNode);
- dinfo.scheme = PART_SCHEME_MBR;
- dinfo.sect_size = 512;
- dinfo.skip_lba = 2048;
- dinfo.num_lba = 0;
- dinfo.num_parts = 1;
-
- struct part_info *pinfo = &dinfo.part_lst[0];
-
- pinfo->name = strdup("android_sdcard");
- pinfo->flags |= PART_ACTIVE_FLAG;
- pinfo->type = PC_PART_TYPE_FAT32;
- pinfo->len_kb = -1;
-
- int rc = apply_disk_config(&dinfo, 0);
-
- if (rc) {
- SLOGE("Failed to apply disk configuration (%d)", rc);
- goto out;
- }
-
- out:
- free(pinfo->name);
- free(dinfo.device);
- free(dinfo.part_lst);
-
- return rc;
-}
-
-/*
- * Use blkid to extract UUID and label from device, since it handles many
- * obscure edge cases around partition types and formats. Always broadcasts
- * updated metadata values.
- */
-int Volume::extractMetadata(const char* devicePath) {
- int res = 0;
-
- std::string cmd;
- cmd = BLKID_PATH;
- cmd += " -c /dev/null ";
- cmd += devicePath;
-
- FILE* fp = popen(cmd.c_str(), "r");
- if (!fp) {
- ALOGE("Failed to run %s: %s", cmd.c_str(), strerror(errno));
- res = -1;
- goto done;
- }
-
- char line[1024];
- char value[128];
- if (fgets(line, sizeof(line), fp) != NULL) {
- ALOGD("blkid identified as %s", line);
-
- char* start = strstr(line, "UUID=");
- if (start != NULL && sscanf(start + 5, "\"%127[^\"]\"", value) == 1) {
- setUuid(value);
- } else {
- setUuid(NULL);
- }
-
- start = strstr(line, "LABEL=");
- if (start != NULL && sscanf(start + 6, "\"%127[^\"]\"", value) == 1) {
- setUserLabel(value);
- } else {
- setUserLabel(NULL);
- }
- } else {
- ALOGW("blkid failed to identify %s", devicePath);
- res = -1;
- }
-
- pclose(fp);
-
-done:
- if (res == -1) {
- setUuid(NULL);
- setUserLabel(NULL);
- }
- return res;
-}
diff --git a/Volume.h b/Volume.h
deleted file mode 100644
index 1444ed3..0000000
--- a/Volume.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _VOLUME_H
-#define _VOLUME_H
-
-#include <utils/List.h>
-#include <fs_mgr.h>
-
-class NetlinkEvent;
-class VolumeManager;
-
-class Volume {
-private:
- int mState;
- int mFlags;
-
-public:
- static const int State_Init = -1;
- static const int State_NoMedia = 0;
- static const int State_Idle = 1;
- static const int State_Pending = 2;
- static const int State_Checking = 3;
- static const int State_Mounted = 4;
- static const int State_Unmounting = 5;
- static const int State_Formatting = 6;
- static const int State_Shared = 7;
- static const int State_SharedMnt = 8;
-
- static const char *MEDIA_DIR;
- static const char *FUSE_DIR;
- static const char *SEC_ASECDIR_EXT;
- static const char *SEC_ASECDIR_INT;
- static const char *ASECDIR;
- static const char *LOOPDIR;
- static const char *BLKID_PATH;
-
-protected:
- char* mLabel;
- char* mUuid;
- char* mUserLabel;
- VolumeManager *mVm;
- bool mDebug;
- int mPartIdx;
- int mOrigPartIdx;
- bool mRetryMount;
-
- /*
- * The major/minor tuple of the currently mounted filesystem.
- */
- dev_t mCurrentlyMountedKdev;
-
-public:
- Volume(VolumeManager *vm, const fstab_rec* rec, int flags);
- virtual ~Volume();
-
- int mountVol();
- int unmountVol(bool force, bool revert);
- int formatVol(bool wipe);
-
- const char* getLabel() { return mLabel; }
- const char* getUuid() { return mUuid; }
- const char* getUserLabel() { return mUserLabel; }
- int getState() { return mState; }
- int getFlags() { return mFlags; };
-
- /* Mountpoint of the raw volume */
- virtual const char *getMountpoint() = 0;
- virtual const char *getFuseMountpoint() = 0;
-
- virtual int handleBlockEvent(NetlinkEvent *evt);
- virtual dev_t getDiskDevice();
- virtual dev_t getShareDevice();
- virtual void handleVolumeShared();
- virtual void handleVolumeUnshared();
-
- void setDebug(bool enable);
- virtual int getVolInfo(struct volume_info *v) = 0;
-
-protected:
- void setUuid(const char* uuid);
- void setUserLabel(const char* userLabel);
- void setState(int state);
-
- virtual int getDeviceNodes(dev_t *devs, int max) = 0;
- virtual int updateDeviceInfo(char *new_path, int new_major, int new_minor) = 0;
- virtual void revertDeviceInfo(void) = 0;
- virtual int isDecrypted(void) = 0;
-
- int createDeviceNode(const char *path, int major, int minor);
-
-private:
- int initializeMbr(const char *deviceNode);
- bool isMountpointMounted(const char *path);
- int mountAsecExternal();
- int doUnmount(const char *path, bool force);
- int extractMetadata(const char* devicePath);
-};
-
-typedef android::List<Volume *> VolumeCollection;
-
-#endif
diff --git a/VolumeBase.cpp b/VolumeBase.cpp
new file mode 100644
index 0000000..ca056a4
--- /dev/null
+++ b/VolumeBase.cpp
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include "Utils.h"
+#include "VolumeBase.h"
+#include "VolumeManager.h"
+#include "ResponseCode.h"
+
+#include <base/stringprintf.h>
+#include <base/logging.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+using android::base::StringPrintf;
+
+#define DEBUG 1
+
+namespace android {
+namespace vold {
+
+VolumeBase::VolumeBase(Type type) :
+ mType(type), mMountFlags(0), mMountUserId(-1), mCreated(false), mState(
+ State::kUnmounted), mSilent(false) {
+}
+
+VolumeBase::~VolumeBase() {
+ CHECK(!mCreated);
+}
+
+void VolumeBase::setState(State state) {
+ mState = state;
+ notifyEvent(ResponseCode::VolumeStateChanged, StringPrintf("%d", mState));
+}
+
+status_t VolumeBase::setDiskId(const std::string& diskId) {
+ if (mCreated) {
+ LOG(WARNING) << getId() << " diskId change requires destroyed";
+ return -EBUSY;
+ }
+
+ mDiskId = diskId;
+ return OK;
+}
+
+status_t VolumeBase::setMountFlags(int mountFlags) {
+ if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
+ LOG(WARNING) << getId() << " flags change requires state unmounted or unmountable";
+ return -EBUSY;
+ }
+
+ mMountFlags = mountFlags;
+ return OK;
+}
+
+status_t VolumeBase::setMountUserId(userid_t mountUserId) {
+ if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
+ LOG(WARNING) << getId() << " user change requires state unmounted or unmountable";
+ return -EBUSY;
+ }
+
+ mMountUserId = mountUserId;
+ return OK;
+}
+
+status_t VolumeBase::setSilent(bool silent) {
+ if (mCreated) {
+ LOG(WARNING) << getId() << " silence change requires destroyed";
+ return -EBUSY;
+ }
+
+ mSilent = silent;
+ return OK;
+}
+
+status_t VolumeBase::setId(const std::string& id) {
+ if (mCreated) {
+ LOG(WARNING) << getId() << " id change requires not created";
+ return -EBUSY;
+ }
+
+ mId = id;
+ return OK;
+}
+
+status_t VolumeBase::setPath(const std::string& path) {
+ if (mState != State::kChecking) {
+ LOG(WARNING) << getId() << " path change requires state checking";
+ return -EBUSY;
+ }
+
+ mPath = path;
+ notifyEvent(ResponseCode::VolumePathChanged, mPath);
+ return OK;
+}
+
+status_t VolumeBase::setInternalPath(const std::string& internalPath) {
+ if (mState != State::kChecking) {
+ LOG(WARNING) << getId() << " internal path change requires state checking";
+ return -EBUSY;
+ }
+
+ mInternalPath = internalPath;
+ notifyEvent(ResponseCode::VolumeInternalPathChanged, mInternalPath);
+ return OK;
+}
+
+void VolumeBase::notifyEvent(int event) {
+ if (mSilent) return;
+ VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event,
+ getId().c_str(), false);
+}
+
+void VolumeBase::notifyEvent(int event, const std::string& value) {
+ if (mSilent) return;
+ VolumeManager::Instance()->getBroadcaster()->sendBroadcast(event,
+ StringPrintf("%s %s", getId().c_str(), value.c_str()).c_str(), false);
+}
+
+void VolumeBase::addVolume(const std::shared_ptr<VolumeBase>& volume) {
+ mVolumes.push_back(volume);
+}
+
+void VolumeBase::removeVolume(const std::shared_ptr<VolumeBase>& volume) {
+ mVolumes.remove(volume);
+}
+
+std::shared_ptr<VolumeBase> VolumeBase::findVolume(const std::string& id) {
+ for (auto vol : mVolumes) {
+ if (vol->getId() == id) {
+ return vol;
+ }
+ }
+ return nullptr;
+}
+
+status_t VolumeBase::create() {
+ CHECK(!mCreated);
+
+ mCreated = true;
+ status_t res = doCreate();
+ notifyEvent(ResponseCode::VolumeCreated, StringPrintf("%d %s", mType, mDiskId.c_str()));
+ setState(State::kUnmounted);
+ return res;
+}
+
+status_t VolumeBase::doCreate() {
+ return OK;
+}
+
+status_t VolumeBase::destroy() {
+ CHECK(mCreated);
+
+ if (mState == State::kMounted) {
+ unmount();
+ setState(State::kBadRemoval);
+ } else {
+ setState(State::kRemoved);
+ }
+
+ notifyEvent(ResponseCode::VolumeDestroyed);
+ status_t res = doDestroy();
+ mCreated = false;
+ return res;
+}
+
+status_t VolumeBase::doDestroy() {
+ return OK;
+}
+
+status_t VolumeBase::mount() {
+ if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
+ LOG(WARNING) << getId() << " mount requires state unmounted or unmountable";
+ return -EBUSY;
+ }
+
+ setState(State::kChecking);
+ status_t res = doMount();
+ if (res == OK) {
+ setState(State::kMounted);
+ } else {
+ setState(State::kUnmountable);
+ }
+
+ return res;
+}
+
+status_t VolumeBase::unmount() {
+ if (mState != State::kMounted) {
+ LOG(WARNING) << getId() << " unmount requires state mounted";
+ return -EBUSY;
+ }
+
+ setState(State::kEjecting);
+
+ for (auto vol : mVolumes) {
+ if (vol->destroy()) {
+ LOG(WARNING) << getId() << " failed to destroy " << vol->getId()
+ << " stacked above";
+ }
+ }
+ mVolumes.clear();
+
+ status_t res = doUnmount();
+ setState(State::kUnmounted);
+ return res;
+}
+
+status_t VolumeBase::format() {
+ if (mState == State::kMounted) {
+ unmount();
+ }
+
+ if ((mState != State::kUnmounted) && (mState != State::kUnmountable)) {
+ LOG(WARNING) << getId() << " format requires state unmounted or unmountable";
+ return -EBUSY;
+ }
+
+ setState(State::kFormatting);
+ status_t res = doFormat();
+ setState(State::kUnmounted);
+ return res;
+}
+
+status_t VolumeBase::doFormat() {
+ return -ENOTSUP;
+}
+
+} // namespace vold
+} // namespace android
diff --git a/VolumeBase.h b/VolumeBase.h
new file mode 100644
index 0000000..a9975f8
--- /dev/null
+++ b/VolumeBase.h
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_VOLD_VOLUME_BASE_H
+#define ANDROID_VOLD_VOLUME_BASE_H
+
+#include "Utils.h"
+
+#include <cutils/multiuser.h>
+#include <utils/Errors.h>
+
+#include <sys/types.h>
+#include <list>
+#include <string>
+
+namespace android {
+namespace vold {
+
+/*
+ * Representation of a mounted volume ready for presentation.
+ *
+ * Various subclasses handle the different mounting prerequisites, such as
+ * encryption details, etc. Volumes can also be "stacked" above other
+ * volumes to help communicate dependencies. For example, an ASEC volume
+ * can be stacked on a vfat volume.
+ *
+ * Mounted volumes can be asked to manage bind mounts to present themselves
+ * to specific users on the device.
+ *
+ * When an unmount is requested, the volume recursively unmounts any stacked
+ * volumes and removes any bind mounts before finally unmounting itself.
+ */
+class VolumeBase {
+public:
+ virtual ~VolumeBase();
+
+ enum class Type {
+ kPublic = 0,
+ kPrivate,
+ kEmulated,
+ kAsec,
+ kObb,
+ };
+
+ enum MountFlags {
+ /* Flag that volume is primary external storage */
+ kPrimary = 1 << 0,
+ /* Flag that volume is visible to normal apps */
+ kVisible = 1 << 1,
+ };
+
+ enum class State {
+ kUnmounted = 0,
+ kChecking,
+ kMounted,
+ kMountedReadOnly,
+ kFormatting,
+ kEjecting,
+ kUnmountable,
+ kRemoved,
+ kBadRemoval,
+ };
+
+ const std::string& getId() { return mId; }
+ const std::string& getDiskId() { return mDiskId; }
+ Type getType() { return mType; }
+ int getMountFlags() { return mMountFlags; }
+ userid_t getMountUserId() { return mMountUserId; }
+ State getState() { return mState; }
+ const std::string& getPath() { return mPath; }
+ const std::string& getInternalPath() { return mInternalPath; }
+
+ status_t setDiskId(const std::string& diskId);
+ status_t setMountFlags(int mountFlags);
+ status_t setMountUserId(userid_t mountUserId);
+ status_t setSilent(bool silent);
+
+ void addVolume(const std::shared_ptr<VolumeBase>& volume);
+ void removeVolume(const std::shared_ptr<VolumeBase>& volume);
+
+ std::shared_ptr<VolumeBase> findVolume(const std::string& id);
+
+ status_t create();
+ status_t destroy();
+ status_t mount();
+ status_t unmount();
+ status_t format();
+
+protected:
+ explicit VolumeBase(Type type);
+
+ virtual status_t doCreate();
+ virtual status_t doDestroy();
+ virtual status_t doMount() = 0;
+ virtual status_t doUnmount() = 0;
+ virtual status_t doFormat();
+
+ status_t setId(const std::string& id);
+ status_t setPath(const std::string& path);
+ status_t setInternalPath(const std::string& internalPath);
+
+ void notifyEvent(int msg);
+ void notifyEvent(int msg, const std::string& value);
+
+private:
+ /* ID that uniquely references volume while alive */
+ std::string mId;
+ /* ID that uniquely references parent disk while alive */
+ std::string mDiskId;
+ /* Volume type */
+ Type mType;
+ /* Flags used when mounting this volume */
+ int mMountFlags;
+ /* User that owns this volume, otherwise -1 */
+ userid_t mMountUserId;
+ /* Flag indicating object is created */
+ bool mCreated;
+ /* Current state of volume */
+ State mState;
+ /* Path to mounted volume */
+ std::string mPath;
+ /* Path to internal backing storage */
+ std::string mInternalPath;
+ /* Flag indicating that volume should emit no events */
+ bool mSilent;
+
+ /* Volumes stacked on top of this volume */
+ std::list<std::shared_ptr<VolumeBase>> mVolumes;
+
+ void setState(State state);
+
+ DISALLOW_COPY_AND_ASSIGN(VolumeBase);
+};
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index da4deb6..8f73e63 100755
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -34,6 +34,8 @@
#include <openssl/md5.h>
+#include <base/logging.h>
+#include <base/stringprintf.h>
#include <cutils/fs.h>
#include <cutils/log.h>
@@ -43,12 +45,14 @@
#include <private/android_filesystem_config.h>
+#include "EmulatedVolume.h"
#include "VolumeManager.h"
-#include "DirectVolume.h"
+#include "NetlinkManager.h"
#include "ResponseCode.h"
#include "Loop.h"
#include "Ext4.h"
#include "Fat.h"
+#include "Utils.h"
#include "Devmapper.h"
#include "Process.h"
#include "Asec.h"
@@ -60,9 +64,35 @@
#define ROUND_UP_POWER_OF_2(number, po2) (((!!(number & ((1U << po2) - 1))) << po2)\
+ (number & (~((1U << po2) - 1))))
+using android::base::StringPrintf;
+
+/*
+ * Path to external storage where *only* root can access ASEC image files
+ */
+const char *VolumeManager::SEC_ASECDIR_EXT = "/mnt/secure/asec";
+
+/*
+ * Path to internal storage where *only* root can access ASEC image files
+ */
+const char *VolumeManager::SEC_ASECDIR_INT = "/data/app-asec";
+
+/*
+ * Path to where secure containers are mounted
+ */
+const char *VolumeManager::ASECDIR = "/mnt/asec";
+
+/*
+ * Path to where OBBs are mounted
+ */
+const char *VolumeManager::LOOPDIR = "/mnt/obb";
+
+static const char* kUserMountPath = "/mnt/user";
+
+static const unsigned int kMajorBlockMmc = 179;
+
/* 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);
+ int sbfd = open(name, O_RDWR | O_CLOEXEC);
if (sbfd < 0) {
SLOGE("Failed to open %s for superblock write (%s)", name, strerror(errno));
return -1;
@@ -171,18 +201,15 @@
VolumeManager::VolumeManager() {
mDebug = false;
- mVolumes = new VolumeCollection();
mActiveContainers = new AsecIdCollection();
mBroadcaster = NULL;
mUmsSharingCount = 0;
mSavedDirtyRatio = -1;
// set dirty ratio to 0 when UMS is active
mUmsDirtyRatio = 0;
- mVolManagerDisabled = 0;
}
VolumeManager::~VolumeManager() {
- delete mVolumes;
delete mActiveContainers;
}
@@ -218,96 +245,235 @@
return buffer;
}
-void VolumeManager::setDebug(bool enable) {
+int VolumeManager::setDebug(bool enable) {
mDebug = enable;
- VolumeCollection::iterator it;
- for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
- (*it)->setDebug(enable);
- }
+ return 0;
}
int VolumeManager::start() {
+ // Always start from a clean slate by unmounting everything in
+ // directories that we own, in case we crashed.
+ unmountAll();
+
+ // Assume that we always have an emulated volume on internal
+ // storage; the framework will decide if it should be mounted.
+ CHECK(mInternalEmulated == nullptr);
+ mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>(
+ new android::vold::EmulatedVolume("/data/media"));
+ mInternalEmulated->create();
+
return 0;
}
int VolumeManager::stop() {
- return 0;
-}
-
-int VolumeManager::addVolume(Volume *v) {
- mVolumes->push_back(v);
+ CHECK(mInternalEmulated != nullptr);
+ mInternalEmulated->destroy();
+ mInternalEmulated = nullptr;
return 0;
}
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
-#ifdef NETLINK_DEBUG
- const char *devpath = evt->findParam("DEVPATH");
-#endif
+ std::lock_guard<std::mutex> lock(mLock);
- /* Lookup a volume to handle this device */
- VolumeCollection::iterator it;
- bool hit = false;
- for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
- if (!(*it)->handleBlockEvent(evt)) {
-#ifdef NETLINK_DEBUG
- SLOGD("Device '%s' event handled by volume %s\n", devpath, (*it)->getLabel());
-#endif
- hit = true;
- break;
- }
+ if (mDebug) {
+ LOG(VERBOSE) << "----------------";
+ LOG(VERBOSE) << "handleBlockEvent with action " << (int) evt->getAction();
+ evt->dump();
}
- if (!hit) {
-#ifdef NETLINK_DEBUG
- SLOGW("No volumes handled block event for '%s'", devpath);
-#endif
+ std::string eventPath(evt->findParam("DEVPATH"));
+ std::string devType(evt->findParam("DEVTYPE"));
+
+ if (devType != "disk") return;
+
+ int major = atoi(evt->findParam("MAJOR"));
+ int minor = atoi(evt->findParam("MINOR"));
+ dev_t device = makedev(major, minor);
+
+ switch (evt->getAction()) {
+ case NetlinkEvent::Action::kAdd: {
+ for (auto source : mDiskSources) {
+ if (source->matches(eventPath)) {
+ // For now, assume that MMC devices are SD, and that
+ // everything else is USB
+ int flags = source->getFlags();
+ if (major == kMajorBlockMmc) {
+ flags |= android::vold::Disk::Flags::kSd;
+ } else {
+ flags |= android::vold::Disk::Flags::kUsb;
+ }
+
+ auto disk = new android::vold::Disk(eventPath, device,
+ source->getNickname(), flags);
+ disk->create();
+ mDisks.push_back(std::shared_ptr<android::vold::Disk>(disk));
+ break;
+ }
+ }
+ break;
+ }
+ case NetlinkEvent::Action::kChange: {
+ LOG(DEBUG) << "Disk at " << major << ":" << minor << " changed";
+ for (auto disk : mDisks) {
+ if (disk->getDevice() == device) {
+ disk->readMetadata();
+ disk->readPartitions();
+ }
+ }
+ break;
+ }
+ case NetlinkEvent::Action::kRemove: {
+ auto i = mDisks.begin();
+ while (i != mDisks.end()) {
+ if ((*i)->getDevice() == device) {
+ (*i)->destroy();
+ i = mDisks.erase(i);
+ } else {
+ ++i;
+ }
+ }
+ break;
+ }
+ default: {
+ LOG(WARNING) << "Unexpected block event action " << (int) evt->getAction();
+ break;
+ }
}
}
-int VolumeManager::listVolumes(SocketClient *cli, bool broadcast) {
- VolumeCollection::iterator i;
- char msg[256];
+void VolumeManager::addDiskSource(const std::shared_ptr<DiskSource>& diskSource) {
+ mDiskSources.push_back(diskSource);
+}
- for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
- char *buffer;
- asprintf(&buffer, "%s %s %d",
- (*i)->getLabel(), (*i)->getFuseMountpoint(),
- (*i)->getState());
- cli->sendMsg(ResponseCode::VolumeListResult, buffer, false);
- free(buffer);
- 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);
- }
+std::shared_ptr<android::vold::Disk> VolumeManager::findDisk(const std::string& id) {
+ for (auto disk : mDisks) {
+ if (disk->getId() == id) {
+ return disk;
}
}
- cli->sendMsg(ResponseCode::CommandOkay, "Volumes listed.", false);
+ return nullptr;
+}
+
+std::shared_ptr<android::vold::VolumeBase> VolumeManager::findVolume(const std::string& id) {
+ if (mInternalEmulated->getId() == id) {
+ return mInternalEmulated;
+ }
+ for (auto disk : mDisks) {
+ auto vol = disk->findVolume(id);
+ if (vol != nullptr) {
+ return vol;
+ }
+ }
+ return nullptr;
+}
+
+int VolumeManager::linkPrimary(userid_t userId) {
+ std::string source(mPrimary->getPath());
+ if (mPrimary->getType() == android::vold::VolumeBase::Type::kEmulated) {
+ source = StringPrintf("%s/%d", source.c_str(), userId);
+ }
+
+ std::string target(StringPrintf("/mnt/user/%d/primary", userId));
+ if (TEMP_FAILURE_RETRY(unlink(target.c_str()))) {
+ if (errno != ENOENT) {
+ SLOGW("Failed to unlink %s: %s", target.c_str(), strerror(errno));
+ }
+ }
+ LOG(DEBUG) << "Linking " << source << " to " << target;
+ if (TEMP_FAILURE_RETRY(symlink(source.c_str(), target.c_str()))) {
+ SLOGW("Failed to link %s to %s: %s", source.c_str(), target.c_str(),
+ strerror(errno));
+ return -errno;
+ }
return 0;
}
-int VolumeManager::formatVolume(const char *label, bool wipe) {
- Volume *v = lookupVolume(label);
+int VolumeManager::startUser(userid_t userId) {
+ // Note that sometimes the system will spin up processes from Zygote
+ // before actually starting the user, so we're okay if Zygote
+ // already created this directory.
+ std::string path(StringPrintf("%s/%d", kUserMountPath, userId));
+ fs_prepare_dir(path.c_str(), 0755, AID_ROOT, AID_ROOT);
- if (!v) {
- errno = ENOENT;
- return -1;
+ mUsers.push_back(userId);
+ if (mPrimary) {
+ linkPrimary(userId);
+ }
+ return 0;
+}
+
+int VolumeManager::cleanupUser(userid_t userId) {
+ mUsers.remove(userId);
+ return 0;
+}
+
+int VolumeManager::setPrimary(const std::shared_ptr<android::vold::VolumeBase>& vol) {
+ mPrimary = vol;
+ for (userid_t userId : mUsers) {
+ linkPrimary(userId);
+ }
+ return 0;
+}
+
+int VolumeManager::reset() {
+ // Tear down all existing disks/volumes and start from a blank slate so
+ // newly connected framework hears all events.
+ mInternalEmulated->destroy();
+ mInternalEmulated->create();
+ for (auto disk : mDisks) {
+ disk->destroy();
+ disk->create();
+ }
+ mUsers.clear();
+ return 0;
+}
+
+int VolumeManager::shutdown() {
+ mInternalEmulated->destroy();
+ for (auto disk : mDisks) {
+ disk->destroy();
+ }
+ mDisks.clear();
+ return 0;
+}
+
+int VolumeManager::unmountAll() {
+ std::lock_guard<std::mutex> lock(mLock);
+
+ // First, try gracefully unmounting all known devices
+ if (mInternalEmulated != nullptr) {
+ mInternalEmulated->unmount();
+ }
+ for (auto disk : mDisks) {
+ disk->unmountAll();
}
- if (mVolManagerDisabled) {
- errno = EBUSY;
- return -1;
+ // Worst case we might have some stale mounts lurking around, so
+ // force unmount those just to be safe.
+ FILE* fp = setmntent("/proc/mounts", "r");
+ if (fp == NULL) {
+ SLOGE("Error opening /proc/mounts: %s", strerror(errno));
+ return -errno;
}
- return v->formatVol(wipe);
+ // Some volumes can be stacked on each other, so force unmount in
+ // reverse order to give us the best chance of success.
+ std::list<std::string> toUnmount;
+ mntent* mentry;
+ while ((mentry = getmntent(fp)) != NULL) {
+ if (strncmp(mentry->mnt_dir, "/mnt/", 5) == 0
+ || strncmp(mentry->mnt_dir, "/storage/", 9) == 0) {
+ toUnmount.push_front(std::string(mentry->mnt_dir));
+ }
+ }
+ endmntent(fp);
+
+ for (auto path : toUnmount) {
+ SLOGW("Tearing down stale mount %s", path.c_str());
+ android::vold::ForceUnmount(path);
+ }
+
+ return 0;
}
int VolumeManager::getObbMountPath(const char *sourceFile, char *mountPath, int mountPathLen) {
@@ -318,7 +484,7 @@
}
memset(mountPath, 0, mountPathLen);
- int written = snprintf(mountPath, mountPathLen, "%s/%s", Volume::LOOPDIR, idHash);
+ int written = snprintf(mountPath, mountPathLen, "%s/%s", VolumeManager::LOOPDIR, idHash);
if ((written < 0) || (written >= mountPathLen)) {
errno = EINVAL;
return -1;
@@ -352,7 +518,7 @@
return -1;
}
- int written = snprintf(buffer, maxlen, "%s/%s", Volume::ASECDIR, id);
+ int written = snprintf(buffer, maxlen, "%s/%s", VolumeManager::ASECDIR, id);
if ((written < 0) || (written >= maxlen)) {
SLOGE("getAsecMountPath failed for %s: couldn't construct path in buffer", id);
errno = EINVAL;
@@ -424,12 +590,6 @@
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))) {
@@ -439,7 +599,7 @@
return -1;
}
- const char *asecDir = isExternal ? Volume::SEC_ASECDIR_EXT : Volume::SEC_ASECDIR_INT;
+ const char *asecDir = isExternal ? VolumeManager::SEC_ASECDIR_EXT : VolumeManager::SEC_ASECDIR_INT;
int written = snprintf(asecFileName, sizeof(asecFileName), "%s/%s.asec", asecDir, id);
if ((written < 0) || (size_t(written) >= sizeof(asecFileName))) {
@@ -515,7 +675,7 @@
int formatStatus;
char mountPoint[255];
- int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
+ int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::ASECDIR, id);
if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
SLOGE("ASEC fs format failed: couldn't construct mountPoint");
if (cleanupDm) {
@@ -573,7 +733,7 @@
}
if (usingExt4) {
- int dirfd = open(mountPoint, O_DIRECTORY);
+ int dirfd = open(mountPoint, O_DIRECTORY | O_CLOEXEC);
if (dirfd >= 0) {
if (fchown(dirfd, ownerUid, AID_SYSTEM)
|| fchmod(dirfd, S_IRUSR | S_IWUSR | S_IXUSR | S_ISGID | S_IRGRP | S_IXGRP)) {
@@ -606,7 +766,7 @@
return -1;
}
- int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
+ int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::ASECDIR, id);
if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
SLOGE("ASEC resize failed for %s: couldn't construct mountpoint", id);
return -1;
@@ -622,7 +782,7 @@
int fd;
unsigned int oldNumSec = 0;
- if ((fd = open(asecFileName, O_RDONLY)) < 0) {
+ if ((fd = open(asecFileName, O_RDONLY | O_CLOEXEC)) < 0) {
SLOGE("Failed to open ASEC file (%s)", strerror(errno));
return -1;
}
@@ -767,7 +927,7 @@
return -1;
}
- int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
+ int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::ASECDIR, id);
if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
SLOGE("ASEC finalize failed: couldn't construct mountPoint");
return -1;
@@ -830,7 +990,7 @@
return -1;
}
- int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
+ int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::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;
@@ -868,7 +1028,7 @@
*/
const bool privateFile = !strcmp(ftsent->fts_name, filename);
- int fd = open(ftsent->fts_accpath, O_NOFOLLOW);
+ int fd = open(ftsent->fts_accpath, O_NOFOLLOW | O_CLOEXEC);
if (fd < 0) {
SLOGE("Couldn't open file %s: %s", ftsent->fts_accpath, strerror(errno));
result = -1;
@@ -893,7 +1053,7 @@
fts_close(fts);
// Finally make the directory readable by everyone.
- int dirfd = open(mountPoint, O_DIRECTORY);
+ int dirfd = open(mountPoint, O_DIRECTORY | O_CLOEXEC);
if (dirfd < 0 || fchmod(dirfd, 0755)) {
SLOGE("Couldn't change owner of existing directory %s: %s", mountPoint, strerror(errno));
result |= -1;
@@ -945,7 +1105,7 @@
asprintf(&asecFilename2, "%s/%s.asec", dir, id2);
- int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id1);
+ int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::ASECDIR, id1);
if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
SLOGE("Rename failed: couldn't construct mountpoint");
goto out_err;
@@ -957,7 +1117,7 @@
goto out_err;
}
- written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id2);
+ written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::ASECDIR, id2);
if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
SLOGE("Rename failed: couldn't construct mountpoint2");
goto out_err;
@@ -1005,7 +1165,7 @@
return -1;
}
- int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
+ int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::ASECDIR, id);
if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
SLOGE("ASEC unmount failed for %s: couldn't construct mountpoint", id);
return -1;
@@ -1029,7 +1189,7 @@
return -1;
}
- int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::LOOPDIR, idHash);
+ int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::LOOPDIR, idHash);
if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
SLOGE("OBB unmount failed for %s: couldn't construct mountpoint", fileName);
return -1;
@@ -1060,16 +1220,16 @@
SLOGW("%s unmount attempt %d failed (%s)",
id, i, strerror(errno));
- int action = 0; // default is to just complain
+ int signal = 0; // default is to just complain
if (force) {
if (i > (UNMOUNT_RETRIES - 2))
- action = 2; // SIGKILL
+ signal = SIGKILL;
else if (i > (UNMOUNT_RETRIES - 3))
- action = 1; // SIGHUP
+ signal = SIGTERM;
}
- Process::killProcessesWithOpenFiles(mountPoint, action);
+ Process::killProcessesWithOpenFiles(mountPoint, signal);
usleep(UNMOUNT_SLEEP_BETWEEN_RETRY_MS);
}
@@ -1141,7 +1301,7 @@
return -1;
}
- int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
+ int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::ASECDIR, id);
if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
SLOGE("ASEC destroy failed for %s: couldn't construct mountpoint", id);
return -1;
@@ -1201,7 +1361,7 @@
}
bool VolumeManager::isAsecInDirectory(const char *dir, const char *asecName) const {
- int dirfd = open(dir, O_DIRECTORY);
+ int dirfd = open(dir, O_DIRECTORY | O_CLOEXEC);
if (dirfd < 0) {
SLOGE("Couldn't open internal ASEC dir (%s)", strerror(errno));
return false;
@@ -1232,10 +1392,10 @@
}
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;
+ if (isAsecInDirectory(VolumeManager::SEC_ASECDIR_INT, asecName)) {
+ dir = VolumeManager::SEC_ASECDIR_INT;
+ } else if (isAsecInDirectory(VolumeManager::SEC_ASECDIR_EXT, asecName)) {
+ dir = VolumeManager::SEC_ASECDIR_EXT;
} else {
free(asecName);
return -1;
@@ -1273,7 +1433,7 @@
return -1;
}
- int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
+ int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::ASECDIR, id);
if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
SLOGE("ASEC mount failed for %s: couldn't construct mountpoint", id);
return -1;
@@ -1360,19 +1520,6 @@
return 0;
}
-Volume* VolumeManager::getVolumeForFile(const char *fileName) {
- VolumeCollection::iterator i;
-
- for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
- const char* mountPoint = (*i)->getFuseMountpoint();
- if (!strncmp(fileName, mountPoint, strlen(mountPoint))) {
- return *i;
- }
- }
-
- return NULL;
-}
-
/**
* Mounts an image file <code>img</code>.
*/
@@ -1385,7 +1532,7 @@
return -1;
}
- int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::LOOPDIR, idHash);
+ int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", VolumeManager::LOOPDIR, idHash);
if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
SLOGE("OBB mount failed for %s: couldn't construct mountpoint", img);
return -1;
@@ -1406,7 +1553,7 @@
int fd;
unsigned long nr_sec = 0;
- if ((fd = open(loopDevice, O_RDWR)) < 0) {
+ if ((fd = open(loopDevice, O_RDWR | O_CLOEXEC)) < 0) {
SLOGE("Failed to open loopdevice (%s)", strerror(errno));
Loop::destroyByDevice(loopDevice);
return -1;
@@ -1460,17 +1607,6 @@
return 0;
}
-int VolumeManager::mountVolume(const char *label) {
- Volume *v = lookupVolume(label);
-
- if (!v) {
- errno = ENOENT;
- return -1;
- }
-
- return v->mountVol();
-}
-
int VolumeManager::listMountedObbs(SocketClient* cli) {
FILE *fp = setmntent("/proc/mounts", "r");
if (fp == NULL) {
@@ -1479,16 +1615,16 @@
}
// Create a string to compare against that has a trailing slash
- int loopDirLen = strlen(Volume::LOOPDIR);
+ int loopDirLen = strlen(VolumeManager::LOOPDIR);
char loopDir[loopDirLen + 2];
- strcpy(loopDir, Volume::LOOPDIR);
+ strcpy(loopDir, VolumeManager::LOOPDIR);
loopDir[loopDirLen++] = '/';
loopDir[loopDirLen] = '\0';
mntent* mentry;
while ((mentry = getmntent(fp)) != NULL) {
if (!strncmp(mentry->mnt_dir, loopDir, loopDirLen)) {
- int fd = open(mentry->mnt_fsname, O_RDONLY);
+ int fd = open(mentry->mnt_fsname, O_RDONLY | O_CLOEXEC);
if (fd >= 0) {
struct loop_info64 li;
if (ioctl(fd, LOOP_GET_STATUS64, &li) >= 0) {
@@ -1503,299 +1639,9 @@
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::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];
- 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 = 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::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;
-}
-
-extern "C" int vold_disableVol(const char *label) {
+extern "C" int vold_unmountAll(void) {
VolumeManager *vm = VolumeManager::Instance();
- vm->disableVolumeManager();
- vm->unshareVolume(label, "ums");
- return vm->unmountVolume(label, true, false);
-}
-
-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),
- "/dev/block/vold/%d:%d", major(d), minor(d));
- n++;
- }
- }
-
- 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);
-}
-
-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) +
- fpathconf(dirfd(d), _PC_NAME_MAX) + 1;
-
- 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;
-}
-
-/*
- * Looks up a volume by it's label or mount-point
- */
-Volume *VolumeManager::lookupVolume(const char *label) {
- VolumeCollection::iterator i;
-
- for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
- if (label[0] == '/') {
- if (!strcmp(label, (*i)->getFuseMountpoint()))
- return (*i);
- } else {
- if (!strcmp(label, (*i)->getLabel()))
- return (*i);
- }
- }
- return NULL;
+ return vm->unmountAll();
}
bool VolumeManager::isMountpointMounted(const char *mp)
@@ -1818,77 +1664,13 @@
return found_mp;
}
-int VolumeManager::cleanupAsec(Volume *v, bool force) {
- int rc = 0;
-
- char asecFileName[255];
-
- AsecIdCollection removeAsec;
- AsecIdCollection removeObb;
-
- for (AsecIdCollection::iterator it = mActiveContainers->begin(); it != mActiveContainers->end();
- ++it) {
- ContainerData* cd = *it;
-
- if (cd->type == ASEC) {
- 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);
- }
- }
- } else if (cd->type == OBB) {
- if (v == getVolumeForFile(cd->id)) {
- removeObb.push_back(cd);
- }
- } else {
- SLOGE("Unknown container type %d!", cd->type);
- }
- }
-
- 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) {
- ContainerData *cd = *it;
- SLOGI("Unmounting OBB %s (dependent on %s)", cd->id, v->getLabel());
- if (unmountObb(cd->id, force)) {
- SLOGE("Failed to unmount OBB %s (%s)", cd->id, strerror(errno));
- rc = -1;
- }
- }
-
- return rc;
-}
-
int VolumeManager::mkdirs(char* path) {
- // Require that path lives under a volume we manage and is mounted
- const char* emulated_source = getenv("EMULATED_STORAGE_SOURCE");
- const char* root = NULL;
- if (emulated_source && !strncmp(path, emulated_source, strlen(emulated_source))) {
- root = emulated_source;
+ // Only offer to create directories for paths managed by vold
+ if (strncmp(path, "/storage/", 9) == 0) {
+ // fs_mkdirs() does symlink checking and relative path enforcement
+ return fs_mkdirs(path, 0700);
} else {
- Volume* vol = getVolumeForFile(path);
- if (vol && vol->getState() == Volume::State_Mounted) {
- root = vol->getMountpoint();
- }
- }
-
- if (!root) {
SLOGE("Failed to find mounted volume for %s", path);
return -EINVAL;
}
-
- /* fs_mkdirs() does symlink checking and relative path enforcement */
- return fs_mkdirs(path, 0700);
}
diff --git a/VolumeManager.h b/VolumeManager.h
index 17fa6f7..e0b0ac8 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -14,16 +14,26 @@
* limitations under the License.
*/
-#ifndef _VOLUMEMANAGER_H
-#define _VOLUMEMANAGER_H
+#ifndef ANDROID_VOLD_VOLUME_MANAGER_H
+#define ANDROID_VOLD_VOLUME_MANAGER_H
#include <pthread.h>
+#include <fnmatch.h>
+#include <stdlib.h>
#ifdef __cplusplus
+
+#include <list>
+#include <mutex>
+#include <string>
+
+#include <cutils/multiuser.h>
#include <utils/List.h>
#include <sysutils/SocketListener.h>
+#include <sysutils/NetlinkEvent.h>
-#include "Volume.h"
+#include "Disk.h"
+#include "VolumeBase.h"
/* The length of an MD5 hash when encoded into ASCII hex characters */
#define MD5_ASCII_LENGTH_PLUS_NULL ((MD5_DIGEST_LENGTH*2)+1)
@@ -51,13 +61,17 @@
typedef android::List<ContainerData*> AsecIdCollection;
class VolumeManager {
+public:
+ static const char *SEC_ASECDIR_EXT;
+ static const char *SEC_ASECDIR_INT;
+ static const char *ASECDIR;
+ static const char *LOOPDIR;
+
private:
static VolumeManager *sInstance;
-private:
SocketListener *mBroadcaster;
- VolumeCollection *mVolumes;
AsecIdCollection *mActiveContainers;
bool mDebug;
@@ -65,26 +79,53 @@
int mUmsSharingCount;
int mSavedDirtyRatio;
int mUmsDirtyRatio;
- int mVolManagerDisabled;
public:
virtual ~VolumeManager();
+ // TODO: pipe all requests through VM to avoid exposing this lock
+ std::mutex& getLock() { return mLock; }
+
int start();
int stop();
void handleBlockEvent(NetlinkEvent *evt);
- int addVolume(Volume *v);
+ class DiskSource {
+ public:
+ DiskSource(const std::string& sysPattern, const std::string& nickname, int flags) :
+ mSysPattern(sysPattern), mNickname(nickname), mFlags(flags) {
+ }
- int listVolumes(SocketClient *cli, bool broadcast);
- int mountVolume(const char *label);
- int unmountVolume(const char *label, bool force, bool revert);
- int shareVolume(const char *label, const char *method);
- int unshareVolume(const char *label, const char *method);
- int shareEnabled(const char *path, const char *method, bool *enabled);
- int formatVolume(const char *label, bool wipe);
- void disableVolumeManager(void) { mVolManagerDisabled = 1; }
+ bool matches(const std::string& sysPath) {
+ return !fnmatch(mSysPattern.c_str(), sysPath.c_str(), 0);
+ }
+
+ const std::string& getNickname() { return mNickname; }
+ int getFlags() { return mFlags; }
+
+ private:
+ std::string mSysPattern;
+ std::string mNickname;
+ int mFlags;
+ };
+
+ void addDiskSource(const std::shared_ptr<DiskSource>& diskSource);
+
+ std::shared_ptr<android::vold::Disk> findDisk(const std::string& id);
+ std::shared_ptr<android::vold::VolumeBase> findVolume(const std::string& id);
+
+ int startUser(userid_t userId);
+ int cleanupUser(userid_t userId);
+
+ int setPrimary(const std::shared_ptr<android::vold::VolumeBase>& vol);
+
+ /* Reset all internal state, typically during framework boot */
+ int reset();
+ /* Prepare for device shutdown, safely unmounting all devices */
+ int shutdown();
+ /* Unmount all volumes, usually for encryption */
+ int unmountAll();
/* ASEC */
int findAsec(const char *id, char *asecPath = NULL, size_t asecPathLen = 0,
@@ -118,16 +159,11 @@
int unmountObb(const char *fileName, bool force);
int getObbMountPath(const char *id, char *buffer, int maxlen);
- Volume* getVolumeForFile(const char *fileName);
-
/* Shared between ASEC and Loopback images */
int unmountLoopImage(const char *containerId, const char *loopId,
const char *fileName, const char *mountPoint, bool force);
- void setDebug(bool enable);
-
- // XXX: Post froyo this should be moved and cleaned up
- int cleanupAsec(Volume *v, bool force);
+ int setDebug(bool enable);
void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }
SocketListener *getBroadcaster() { return mBroadcaster; }
@@ -136,11 +172,6 @@
static char *asecHash(const char *id, char *buffer, size_t len);
- Volume *lookupVolume(const char *label);
- int getNumDirectVolumes(void);
- int getDirectVolumeList(struct volume_info *vol_list);
- int unmountAllAsecsInDir(const char *directory);
-
/*
* Ensure that all directories along given path exist, creating parent
* directories as needed. Validates that given path is absolute and that
@@ -156,15 +187,24 @@
bool isMountpointMounted(const char *mp);
bool isAsecInDirectory(const char *dir, const char *asec) const;
bool isLegalAsecId(const char *id) const;
+
+ int linkPrimary(userid_t userId);
+
+ std::mutex mLock;
+
+ std::list<std::shared_ptr<DiskSource>> mDiskSources;
+ std::list<std::shared_ptr<android::vold::Disk>> mDisks;
+
+ std::list<userid_t> mUsers;
+
+ std::shared_ptr<android::vold::VolumeBase> mInternalEmulated;
+ std::shared_ptr<android::vold::VolumeBase> mPrimary;
};
extern "C" {
#endif /* __cplusplus */
#define UNMOUNT_NOT_MOUNTED_ERR -2
- int vold_disableVol(const char *label);
- int vold_getNumDirectVolumes(void);
- int vold_getDirectVolumeList(struct volume_info *v);
- int vold_unmountAllAsecs(void);
+ int vold_unmountAll(void);
#ifdef __cplusplus
}
#endif
diff --git a/cryptfs.c b/cryptfs.c
index 5598671..2306e4a 100644
--- a/cryptfs.c
+++ b/cryptfs.c
@@ -237,20 +237,6 @@
// necessary, but is necessary to ensure consistency in
// implementations.
switch (ftr->kdf_type) {
- case KDF_SCRYPT_KEYMASTER_UNPADDED:
- // This is broken: It produces a message which is shorter than
- // the public modulus, failing criterion 2.
- memcpy(to_sign, object, object_size);
- to_sign_size = object_size;
- SLOGI("Signing unpadded object");
- break;
- case KDF_SCRYPT_KEYMASTER_BADLY_PADDED:
- // This is broken: Since the value of object is uniformly
- // distributed, it produces a message that is larger than the
- // public modulus with probability 0.25.
- memcpy(to_sign, object, min(RSA_KEY_SIZE_BYTES, object_size));
- SLOGI("Signing end-padded object");
- break;
case KDF_SCRYPT_KEYMASTER:
// This ensures the most significant byte of the signed message
// is zero. We could have zero-padded to the left instead, but
@@ -395,7 +381,7 @@
struct ext4_super_block sb;
off64_t len;
- if ((fd = open(dev, O_RDONLY)) < 0) {
+ if ((fd = open(dev, O_RDONLY|O_CLOEXEC)) < 0) {
SLOGE("Cannot open device to get filesystem size ");
return 0;
}
@@ -438,7 +424,7 @@
fs_mgr_get_crypt_info(fstab, key_loc, real_blkdev, sizeof(key_loc));
if (!strcmp(key_loc, KEY_IN_FOOTER)) {
- if ( (fd = open(real_blkdev, O_RDWR)) < 0) {
+ if ( (fd = open(real_blkdev, O_RDWR|O_CLOEXEC)) < 0) {
SLOGE("Cannot open real block device %s\n", real_blkdev);
return -1;
}
@@ -500,7 +486,7 @@
SLOGE("Unexpected value for crypto key location\n");
return -1;
}
- if ( (fd = open(fname, O_RDWR | O_CREAT, 0600)) < 0) {
+ if ( (fd = open(fname, O_RDWR | O_CREAT|O_CLOEXEC, 0600)) < 0) {
SLOGE("Cannot open footer file %s for put\n", fname);
return -1;
}
@@ -638,7 +624,7 @@
SLOGE("Unexpected value for crypto key location\n");
return -1;
}
- if ( (fd = open(fname, O_RDWR)) < 0) {
+ if ( (fd = open(fname, O_RDWR|O_CLOEXEC)) < 0) {
SLOGE("Cannot open footer file %s for get\n", fname);
return -1;
}
@@ -763,7 +749,7 @@
return -1;
}
- fd = open(fname, O_RDONLY);
+ fd = open(fname, O_RDONLY|O_CLOEXEC);
if (fd < 0) {
SLOGE("Cannot open %s metadata file", fname);
return -1;
@@ -844,7 +830,7 @@
return -1;
}
- fd = open(fname, O_RDWR);
+ fd = open(fname, O_RDWR|O_CLOEXEC);
if (fd < 0) {
SLOGE("Cannot open %s metadata file", fname);
return -1;
@@ -960,9 +946,8 @@
/* Convert a binary key of specified length into an ascii hex string equivalent,
* without the leading 0x and with null termination
*/
-static void convert_key_to_hex_ascii(unsigned char *master_key, unsigned int keysize,
- char *master_key_ascii)
-{
+static void convert_key_to_hex_ascii(const unsigned char *master_key,
+ unsigned int keysize, char *master_key_ascii) {
unsigned int i, a;
unsigned char nibble;
@@ -980,10 +965,9 @@
}
-static int load_crypto_mapping_table(struct crypt_mnt_ftr *crypt_ftr, unsigned char *master_key,
- char *real_blk_name, const char *name, int fd,
- char *extra_params)
-{
+static int load_crypto_mapping_table(struct crypt_mnt_ftr *crypt_ftr,
+ const unsigned char *master_key, const char *real_blk_name,
+ const char *name, int fd, const char *extra_params) {
_Alignas(struct dm_ioctl) char buffer[DM_CRYPT_BUF_SIZE];
struct dm_ioctl *io;
struct dm_target_spec *tgt;
@@ -1072,9 +1056,9 @@
return -1;
}
-static int create_crypto_blk_dev(struct crypt_mnt_ftr *crypt_ftr, unsigned char *master_key,
- char *real_blk_name, char *crypto_blk_name, const char *name)
-{
+static int create_crypto_blk_dev(struct crypt_mnt_ftr *crypt_ftr,
+ const unsigned char *master_key, const char *real_blk_name,
+ char *crypto_blk_name, const char *name) {
char buffer[DM_CRYPT_BUF_SIZE];
struct dm_ioctl *io;
unsigned int minor;
@@ -1084,7 +1068,7 @@
char *extra_params;
int load_count;
- if ((fd = open("/dev/device-mapper", O_RDWR)) < 0 ) {
+ if ((fd = open("/dev/device-mapper", O_RDWR|O_CLOEXEC)) < 0 ) {
SLOGE("Cannot open device-mapper\n");
goto errout;
}
@@ -1149,7 +1133,7 @@
struct dm_ioctl *io;
int retval = -1;
- if ((fd = open("/dev/device-mapper", O_RDWR)) < 0 ) {
+ if ((fd = open("/dev/device-mapper", O_RDWR|O_CLOEXEC)) < 0 ) {
SLOGE("Cannot open device-mapper\n");
goto errout;
}
@@ -1275,8 +1259,6 @@
get_device_scrypt_params(crypt_ftr);
switch (crypt_ftr->kdf_type) {
- case KDF_SCRYPT_KEYMASTER_UNPADDED:
- case KDF_SCRYPT_KEYMASTER_BADLY_PADDED:
case KDF_SCRYPT_KEYMASTER:
if (keymaster_create_key(crypt_ftr)) {
SLOGE("keymaster_create_key failed");
@@ -1397,9 +1379,7 @@
static void get_kdf_func(struct crypt_mnt_ftr *ftr, kdf_func *kdf, void** kdf_params)
{
- if (ftr->kdf_type == KDF_SCRYPT_KEYMASTER_UNPADDED ||
- ftr->kdf_type == KDF_SCRYPT_KEYMASTER_BADLY_PADDED ||
- ftr->kdf_type == KDF_SCRYPT_KEYMASTER) {
+ if (ftr->kdf_type == KDF_SCRYPT_KEYMASTER) {
*kdf = scrypt_keymaster;
*kdf_params = ftr;
} else if (ftr->kdf_type == KDF_SCRYPT) {
@@ -1437,7 +1417,7 @@
unsigned char key_buf[KEY_LEN_BYTES];
/* Get some random bits for a key */
- fd = open("/dev/urandom", O_RDONLY);
+ fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC);
read(fd, key_buf, sizeof(key_buf));
read(fd, salt, SALT_LEN);
close(fd);
@@ -1470,10 +1450,10 @@
if (kill) {
if (i == (WAIT_UNMOUNT_COUNT - 3)) {
SLOGW("sending SIGHUP to processes with open files\n");
- vold_killProcessesWithOpenFiles(mountpoint, 1);
+ vold_killProcessesWithOpenFiles(mountpoint, SIGTERM);
} else if (i == (WAIT_UNMOUNT_COUNT - 2)) {
SLOGW("sending SIGKILL to processes with open files\n");
- vold_killProcessesWithOpenFiles(mountpoint, 2);
+ vold_killProcessesWithOpenFiles(mountpoint, SIGKILL);
}
}
@@ -1904,36 +1884,18 @@
return rc;
}
-/* Called by vold when it wants to undo the crypto mapping of a volume it
- * manages. This is usually in response to a factory reset, when we want
- * to undo the crypto mapping so the volume is formatted in the clear.
- */
-int cryptfs_revert_volume(const char *label)
-{
- return delete_crypto_blk_dev((char *)label);
-}
-
/*
- * Called by vold when it's asked to mount an encrypted, nonremovable volume.
- * Setup a dm-crypt mapping, use the saved master key from
- * setting up the /data mapping, and return the new device path.
+ * Called by vold when it's asked to mount an encrypted external
+ * storage volume. The incoming partition has no crypto header/footer,
+ * as any metadata is been stored in a separate, small partition.
+ *
+ * out_crypto_blkdev must be MAXPATHLEN.
*/
-int cryptfs_setup_volume(const char *label, int major, int minor,
- char *crypto_sys_path, unsigned int max_path,
- int *new_major, int *new_minor)
-{
- char real_blkdev[MAXPATHLEN], crypto_blkdev[MAXPATHLEN];
- struct crypt_mnt_ftr sd_crypt_ftr;
- struct stat statbuf;
-
- sprintf(real_blkdev, "/dev/block/vold/%d:%d", major, minor);
-
- get_crypt_ftr_and_key(&sd_crypt_ftr);
-
- /* Update the fs_size field to be the size of the volume */
- int fd = open(real_blkdev, O_RDONLY);
+int cryptfs_setup_ext_volume(const char* label, const char* real_blkdev,
+ const unsigned char* key, int keysize, char* out_crypto_blkdev) {
+ int fd = open(real_blkdev, O_RDONLY|O_CLOEXEC);
if (fd == -1) {
- SLOGE("Cannot open volume %s\n", real_blkdev);
+ SLOGE("Failed to open %s: %s", real_blkdev, strerror(errno));
return -1;
}
@@ -1942,25 +1904,26 @@
close(fd);
if (nr_sec == 0) {
- SLOGE("Cannot get size of volume %s\n", real_blkdev);
+ SLOGE("Failed to get size of %s: %s", real_blkdev, strerror(errno));
return -1;
}
- sd_crypt_ftr.fs_size = nr_sec;
- create_crypto_blk_dev(&sd_crypt_ftr, saved_master_key, real_blkdev,
- crypto_blkdev, label);
+ struct crypt_mnt_ftr ext_crypt_ftr;
+ memset(&ext_crypt_ftr, 0, sizeof(ext_crypt_ftr));
+ ext_crypt_ftr.fs_size = nr_sec;
+ ext_crypt_ftr.keysize = keysize;
+ strcpy((char*) ext_crypt_ftr.crypto_type_name, "aes-cbc-essiv:sha256");
- if (stat(crypto_blkdev, &statbuf) < 0) {
- SLOGE("Error get stat for crypto_blkdev %s. err=%d(%s)\n",
- crypto_blkdev, errno, strerror(errno));
- }
- *new_major = MAJOR(statbuf.st_rdev);
- *new_minor = MINOR(statbuf.st_rdev);
+ return create_crypto_blk_dev(&ext_crypt_ftr, key, real_blkdev,
+ out_crypto_blkdev, label);
+}
- /* Create path to sys entry for this block device */
- snprintf(crypto_sys_path, max_path, "/devices/virtual/block/%s", strrchr(crypto_blkdev, '/')+1);
-
- return 0;
+/*
+ * Called by vold when it's asked to unmount an encrypted external
+ * storage volume.
+ */
+int cryptfs_revert_ext_volume(const char* label) {
+ return delete_crypto_blk_dev((char*) label);
}
int cryptfs_crypto_complete(void)
@@ -2491,14 +2454,14 @@
data.real_blkdev = real_blkdev;
data.crypto_blkdev = crypto_blkdev;
- if ( (data.realfd = open(real_blkdev, O_RDWR)) < 0) {
+ if ( (data.realfd = open(real_blkdev, O_RDWR|O_CLOEXEC)) < 0) {
SLOGE("Error opening real_blkdev %s for inplace encrypt. err=%d(%s)\n",
real_blkdev, errno, strerror(errno));
rc = -1;
goto errout;
}
- if ( (data.cryptofd = open(crypto_blkdev, O_WRONLY)) < 0) {
+ if ( (data.cryptofd = open(crypto_blkdev, O_WRONLY|O_CLOEXEC)) < 0) {
SLOGE("Error opening crypto_blkdev %s for ext4 inplace encrypt. err=%d(%s)\n",
crypto_blkdev, errno, strerror(errno));
rc = ENABLE_INPLACE_ERR_DEV;
@@ -2622,12 +2585,12 @@
data.crypto_blkdev = crypto_blkdev;
data.realfd = -1;
data.cryptofd = -1;
- if ( (data.realfd = open64(real_blkdev, O_RDWR)) < 0) {
+ if ( (data.realfd = open64(real_blkdev, O_RDWR|O_CLOEXEC)) < 0) {
SLOGE("Error opening real_blkdev %s for f2fs inplace encrypt\n",
real_blkdev);
goto errout;
}
- if ( (data.cryptofd = open64(crypto_blkdev, O_WRONLY)) < 0) {
+ if ( (data.cryptofd = open64(crypto_blkdev, O_WRONLY|O_CLOEXEC)) < 0) {
SLOGE("Error opening crypto_blkdev %s for f2fs inplace encrypt. err=%d(%s)\n",
crypto_blkdev, errno, strerror(errno));
rc = ENABLE_INPLACE_ERR_DEV;
@@ -2694,12 +2657,12 @@
off64_t one_pct, cur_pct, new_pct;
off64_t blocks_already_done, tot_numblocks;
- if ( (realfd = open(real_blkdev, O_RDONLY)) < 0) {
+ if ( (realfd = open(real_blkdev, O_RDONLY|O_CLOEXEC)) < 0) {
SLOGE("Error opening real_blkdev %s for inplace encrypt\n", real_blkdev);
return ENABLE_INPLACE_ERR_OTHER;
}
- if ( (cryptofd = open(crypto_blkdev, O_WRONLY)) < 0) {
+ if ( (cryptofd = open(crypto_blkdev, O_WRONLY|O_CLOEXEC)) < 0) {
SLOGE("Error opening crypto_blkdev %s for inplace encrypt. err=%d(%s)\n",
crypto_blkdev, errno, strerror(errno));
close(realfd);
@@ -2855,15 +2818,9 @@
#define FRAMEWORK_BOOT_WAIT 60
-static inline int should_encrypt(struct volume_info *volume)
-{
- return (volume->flags & (VOL_ENCRYPTABLE | VOL_NONREMOVABLE)) ==
- (VOL_ENCRYPTABLE | VOL_NONREMOVABLE);
-}
-
static int cryptfs_SHA256_fileblock(const char* filename, __le8* buf)
{
- int fd = open(filename, O_RDONLY);
+ int fd = open(filename, O_RDONLY|O_CLOEXEC);
if (fd == -1) {
SLOGE("Error opening file %s", filename);
return -1;
@@ -2963,10 +2920,7 @@
char encrypted_state[PROPERTY_VALUE_MAX];
char lockid[32] = { 0 };
char key_loc[PROPERTY_VALUE_MAX];
- char fuse_sdcard[PROPERTY_VALUE_MAX];
- char *sd_mnt_point;
int num_vols;
- struct volume_info *vol_list = 0;
off64_t previously_encrypted_upto = 0;
if (!strcmp(howarg, "wipe")) {
@@ -3006,7 +2960,7 @@
fs_mgr_get_crypt_info(fstab, 0, real_blkdev, sizeof(real_blkdev));
/* Get the size of the real block device */
- int fd = open(real_blkdev, O_RDONLY);
+ int fd = open(real_blkdev, O_RDONLY|O_CLOEXEC);
if (fd == -1) {
SLOGE("Cannot open block device %s\n", real_blkdev);
goto error_unencrypted;
@@ -3041,55 +2995,15 @@
snprintf(lockid, sizeof(lockid), "enablecrypto%d", (int) getpid());
acquire_wake_lock(PARTIAL_WAKE_LOCK, lockid);
- /* Get the sdcard mount point */
- sd_mnt_point = getenv("EMULATED_STORAGE_SOURCE");
- if (!sd_mnt_point) {
- sd_mnt_point = getenv("EXTERNAL_STORAGE");
- }
- if (!sd_mnt_point) {
- sd_mnt_point = "/mnt/sdcard";
- }
-
- /* TODO
- * Currently do not have test devices with multiple encryptable volumes.
- * When we acquire some, re-add support.
- */
- num_vols=vold_getNumDirectVolumes();
- vol_list = malloc(sizeof(struct volume_info) * num_vols);
- vold_getDirectVolumeList(vol_list);
-
- for (i=0; i<num_vols; i++) {
- if (should_encrypt(&vol_list[i])) {
- SLOGE("Cannot encrypt if there are multiple encryptable volumes"
- "%s\n", vol_list[i].label);
- goto error_unencrypted;
- }
- }
-
/* The init files are setup to stop the class main and late start when
* vold sets trigger_shutdown_framework.
*/
property_set("vold.decrypt", "trigger_shutdown_framework");
SLOGD("Just asked init to shut down class main\n");
- if (vold_unmountAllAsecs()) {
- /* Just report the error. If any are left mounted,
- * umounting /data below will fail and handle the error.
- */
- SLOGE("Error unmounting internal asecs");
- }
-
- property_get("ro.crypto.fuse_sdcard", fuse_sdcard, "");
- if (!strcmp(fuse_sdcard, "true")) {
- /* This is a device using the fuse layer to emulate the sdcard semantics
- * on top of the userdata partition. vold does not manage it, it is managed
- * by the sdcard service. The sdcard service was killed by the property trigger
- * above, so just unmount it now. We must do this _AFTER_ killing the framework,
- * unlike the case for vold managed devices above.
- */
- if (wait_and_unmount(sd_mnt_point, false)) {
- goto error_shutting_down;
- }
+ /* Ask vold to unmount all devices that it manages */
+ if (vold_unmountAll()) {
+ SLOGE("Failed to unmount all vold managed devices");
}
/* Now unmount the /data partition. */
@@ -3237,8 +3151,6 @@
/* Undo the dm-crypt mapping whether we succeed or not */
delete_crypto_blk_dev("userdata");
- free(vol_list);
-
if (! rc) {
/* Success */
crypt_ftr.flags &= ~CRYPT_INCONSISTENT_STATE;
@@ -3279,7 +3191,7 @@
/* wipe data if encryption failed */
SLOGE("encryption failed - rebooting into recovery to wipe data\n");
mkdir("/cache/recovery", 0700);
- int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC, 0600);
+ int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0600);
if (fd >= 0) {
write(fd, "--wipe_data\n", strlen("--wipe_data\n") + 1);
write(fd, "--reason=cryptfs_enable_internal\n", strlen("--reason=cryptfs_enable_internal\n") + 1);
@@ -3305,7 +3217,6 @@
return rc;
error_unencrypted:
- free(vol_list);
property_set("vold.encrypt_progress", "error_not_encrypted");
if (lockid[0]) {
release_wake_lock(lockid);
@@ -3322,7 +3233,6 @@
/* shouldn't get here */
property_set("vold.encrypt_progress", "error_shutting_down");
- free(vol_list);
if (lockid[0]) {
release_wake_lock(lockid);
}
@@ -3355,6 +3265,7 @@
}
struct crypt_mnt_ftr crypt_ftr;
+ int rc;
/* This is only allowed after we've successfully decrypted the master key */
if (!master_key_saved) {
@@ -3380,18 +3291,20 @@
newpw = adjusted_passwd;
}
- encrypt_master_key(crypt_type == CRYPT_TYPE_DEFAULT ? DEFAULT_PASSWORD
+ rc = encrypt_master_key(crypt_type == CRYPT_TYPE_DEFAULT ? DEFAULT_PASSWORD
: newpw,
crypt_ftr.salt,
saved_master_key,
crypt_ftr.master_key,
&crypt_ftr);
-
+ free(adjusted_passwd);
+ if (rc) {
+ SLOGE("Encrypt master key failed: %d", rc);
+ return -1;
+ }
/* save the key */
put_crypt_ftr_and_key(&crypt_ftr);
- free(adjusted_passwd);
-
#ifdef CONFIG_HW_DISK_ENCRYPTION
if (!strcmp((char *)crypt_ftr.crypto_type_name, "aes-xts")) {
if (crypt_type == CRYPT_TYPE_DEFAULT) {
diff --git a/cryptfs.h b/cryptfs.h
index 7f082cc..94684e2 100644
--- a/cryptfs.h
+++ b/cryptfs.h
@@ -72,10 +72,7 @@
/* Key Derivation Function algorithms */
#define KDF_PBKDF2 1
#define KDF_SCRYPT 2
-/* TODO(paullawrence): Remove KDF_SCRYPT_KEYMASTER_UNPADDED and KDF_SCRYPT_KEYMASTER_BADLY_PADDED
- * when it is safe to do so. */
-#define KDF_SCRYPT_KEYMASTER_UNPADDED 3
-#define KDF_SCRYPT_KEYMASTER_BADLY_PADDED 4
+/* Algorithms 3 & 4 deprecated before shipping outside of google, so removed */
#define KDF_SCRYPT_KEYMASTER 5
/* Maximum allowed keymaster blob size. */
@@ -176,20 +173,6 @@
struct crypt_persist_entry persist_entry[0];
};
-struct volume_info {
- unsigned int size;
- unsigned int flags;
- struct crypt_mnt_ftr crypt_ftr;
- char mnt_point[256];
- char blk_dev[256];
- char crypto_blkdev[256];
- char label[256];
-};
-#define VOL_NONREMOVABLE 0x1
-#define VOL_ENCRYPTABLE 0x2
-#define VOL_PRIMARY 0x4
-#define VOL_PROVIDES_ASEC 0x8
-
#define DATA_MNT_POINT "/data"
/* Return values for cryptfs_crypto_complete */
@@ -238,11 +221,10 @@
int cryptfs_enable(char *flag, int type, char *passwd, int allow_reboot);
int cryptfs_changepw(int type, const char *newpw);
int cryptfs_enable_default(char *flag, int allow_reboot);
+ int cryptfs_setup_ext_volume(const char* label, const char* real_blkdev,
+ const unsigned char* key, int keysize, char* out_crypto_blkdev);
+ int cryptfs_revert_ext_volume(const char* label);
int cryptfs_enable_file();
- int cryptfs_setup_volume(const char *label, int major, int minor,
- char *crypto_dev_path, unsigned int max_pathlen,
- int *new_major, int *new_minor);
- int cryptfs_revert_volume(const char *label);
int cryptfs_getfield(const char *fieldname, char *value, int len);
int cryptfs_setfield(const char *fieldname, const char *value);
int cryptfs_mount_default_encrypted(void);
diff --git a/fstrim.c b/fstrim.c
index 2c24fc9..60c9f24 100644
--- a/fstrim.c
+++ b/fstrim.c
@@ -64,6 +64,7 @@
struct fstrim_range range = { 0 };
extern struct fstab *fstab;
int deep_trim = !!thread_arg;
+ struct fstab_rec *prev_rec = NULL;
SLOGI("Starting fstrim work...\n");
@@ -88,6 +89,16 @@
if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {
continue; /* Should we trim fat32 filesystems? */
}
+ if (fs_mgr_is_notrim(&fstab->recs[i])) {
+ continue;
+ }
+
+ /* Skip the multi-type partitions, which are required to be following each other.
+ * See fs_mgr.c's mount_with_alternatives().
+ */
+ if (prev_rec && !strcmp(prev_rec->mount_point, fstab->recs[i].mount_point)) {
+ continue;
+ }
fd = open(fstab->recs[i].mount_point, O_RDONLY | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
if (fd < 0) {
@@ -108,6 +119,7 @@
SLOGI("Trimmed %llu bytes on %s\n", range.len, fstab->recs[i].mount_point);
}
close(fd);
+ prev_rec = &fstab->recs[i];
}
/* Log the finish time in the event log */
diff --git a/main.cpp b/main.cpp
index c07f48d..ed2a88a 100644
--- a/main.cpp
+++ b/main.cpp
@@ -14,49 +14,59 @@
* limitations under the License.
*/
+#include "Disk.h"
+#include "VolumeManager.h"
+#include "CommandListener.h"
+#include "NetlinkManager.h"
+#include "cryptfs.h"
+#include "sehandle.h"
+
+#include <base/logging.h>
+#include <base/stringprintf.h>
+#include <cutils/klog.h>
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
-
+#include <getopt.h>
#include <fcntl.h>
#include <dirent.h>
#include <fs_mgr.h>
-#define LOG_TAG "Vold"
-
-#include "cutils/klog.h"
-#include "cutils/log.h"
-#include "cutils/properties.h"
-
-#include "VolumeManager.h"
-#include "CommandListener.h"
-#include "NetlinkManager.h"
-#include "DirectVolume.h"
-#include "cryptfs.h"
-#include "sehandle.h"
-
static int process_config(VolumeManager *vm);
static void coldboot(const char *path);
+static void parse_args(int argc, char** argv);
-#define FSTAB_PREFIX "/fstab."
struct fstab *fstab;
struct selabel_handle *sehandle;
-int main() {
+using android::base::StringPrintf;
+
+int main(int argc, char** argv) {
+ setenv("ANDROID_LOG_TAGS", "*:v", 1);
+ android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
+
+ LOG(INFO) << "Vold 3.0 (the awakening) firing up";
VolumeManager *vm;
CommandListener *cl;
NetlinkManager *nm;
- SLOGI("Vold 2.1 (the revenge) firing up");
+ parse_args(argc, argv);
sehandle = selinux_android_file_context_handle();
- if (sehandle)
+ if (sehandle) {
selinux_android_set_sehandle(sehandle);
+ }
+
+ // Quickly throw a CLOEXEC on the socket we just inherited from init
+ fcntl(android_get_control_socket("vold"), F_SETFD, FD_CLOEXEC);
mkdir("/dev/block/vold", 0755);
@@ -65,31 +75,34 @@
/* Create our singleton managers */
if (!(vm = VolumeManager::Instance())) {
- SLOGE("Unable to create VolumeManager");
+ LOG(ERROR) << "Unable to create VolumeManager";
exit(1);
- };
+ }
if (!(nm = NetlinkManager::Instance())) {
- SLOGE("Unable to create NetlinkManager");
+ LOG(ERROR) << "Unable to create NetlinkManager";
exit(1);
- };
+ }
+ if (property_get_bool("vold.debug", false)) {
+ vm->setDebug(true);
+ }
cl = new CommandListener();
vm->setBroadcaster((SocketListener *) cl);
nm->setBroadcaster((SocketListener *) cl);
if (vm->start()) {
- SLOGE("Unable to start VolumeManager (%s)", strerror(errno));
+ PLOG(ERROR) << "Unable to start VolumeManager";
exit(1);
}
if (process_config(vm)) {
- SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));
+ PLOG(ERROR) << "Error reading configuration... continuing anyways";
}
if (nm->start()) {
- SLOGE("Unable to start NetlinkManager (%s)", strerror(errno));
+ PLOG(ERROR) << "Unable to start NetlinkManager";
exit(1);
}
@@ -100,7 +113,7 @@
* Now that we're up, we can respond to commands
*/
if (cl->startListener()) {
- SLOGE("Unable to start CommandListener (%s)", strerror(errno));
+ PLOG(ERROR) << "Unable to start CommandListener";
exit(1);
}
@@ -109,18 +122,41 @@
sleep(1000);
}
- SLOGI("Vold exiting");
+ LOG(ERROR) << "Vold exiting";
exit(0);
}
-static void do_coldboot(DIR *d, int lvl)
-{
+static void parse_args(int argc, char** argv) {
+ static struct option opts[] = {
+ {"blkid_context", required_argument, 0, 'b' },
+ {"blkid_untrusted_context", required_argument, 0, 'B' },
+ {"fsck_context", required_argument, 0, 'f' },
+ {"fsck_untrusted_context", required_argument, 0, 'F' },
+ };
+
+ int c;
+ while ((c = getopt_long(argc, argv, "", opts, nullptr)) != -1) {
+ switch (c) {
+ case 'b': android::vold::sBlkidContext = optarg; break;
+ case 'B': android::vold::sBlkidUntrustedContext = optarg; break;
+ case 'f': android::vold::sFsckContext = optarg; break;
+ case 'F': android::vold::sFsckUntrustedContext = optarg; break;
+ }
+ }
+
+ CHECK(android::vold::sBlkidContext != nullptr);
+ CHECK(android::vold::sBlkidUntrustedContext != nullptr);
+ CHECK(android::vold::sFsckContext != nullptr);
+ CHECK(android::vold::sFsckUntrustedContext != nullptr);
+}
+
+static void do_coldboot(DIR *d, int lvl) {
struct dirent *de;
int dfd, fd;
dfd = dirfd(d);
- fd = openat(dfd, "uevent", O_WRONLY);
+ fd = openat(dfd, "uevent", O_WRONLY | O_CLOEXEC);
if(fd >= 0) {
write(fd, "add\n", 4);
close(fd);
@@ -149,8 +185,7 @@
}
}
-static void coldboot(const char *path)
-{
+static void coldboot(const char *path) {
DIR *d = opendir(path);
if(d) {
do_coldboot(d, 0);
@@ -158,55 +193,47 @@
}
}
-static int process_config(VolumeManager *vm)
-{
- char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
- char propbuf[PROPERTY_VALUE_MAX];
- int i;
- int ret = -1;
- int flags;
+static int process_config(VolumeManager *vm) {
+ char hardware[PROPERTY_VALUE_MAX];
+ property_get("ro.hardware", hardware, "");
+ std::string fstab_filename(StringPrintf("/fstab.%s", hardware));
- property_get("ro.hardware", propbuf, "");
- snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf);
+#ifdef DEBUG_FSTAB
+ if (access(DEBUG_FSTAB, R_OK) == 0) {
+ LOG(DEBUG) << "Found debug fstab; switching!";
+ fstab_filename = DEBUG_FSTAB;
+ }
+#endif
- fstab = fs_mgr_read_fstab(fstab_filename);
+ fstab = fs_mgr_read_fstab(fstab_filename.c_str());
if (!fstab) {
- SLOGE("failed to open %s\n", fstab_filename);
+ PLOG(ERROR) << "Failed to open " << fstab_filename;
return -1;
}
/* Loop through entries looking for ones that vold manages */
- for (i = 0; i < fstab->num_entries; i++) {
+ for (int i = 0; i < fstab->num_entries; i++) {
if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {
- DirectVolume *dv = NULL;
- flags = 0;
-
- /* Set any flags that might be set for this volume */
if (fs_mgr_is_nonremovable(&fstab->recs[i])) {
- flags |= VOL_NONREMOVABLE;
+ LOG(WARNING) << "nonremovable no longer supported; ignoring volume";
+ continue;
}
+
+ std::string sysPattern(fstab->recs[i].blk_device);
+ std::string nickname(fstab->recs[i].label);
+ int flags = 0;
+
if (fs_mgr_is_encryptable(&fstab->recs[i])) {
- flags |= VOL_ENCRYPTABLE;
+ flags |= android::vold::Disk::Flags::kAdoptable;
}
- /* Only set this flag if there is not an emulated sd card */
- if (fs_mgr_is_noemulatedsd(&fstab->recs[i]) &&
- !strcmp(fstab->recs[i].fs_type, "vfat")) {
- flags |= VOL_PROVIDES_ASEC;
- }
- dv = new DirectVolume(vm, &(fstab->recs[i]), flags);
-
- if (dv->addPath(fstab->recs[i].blk_device)) {
- SLOGE("Failed to add devpath %s to volume %s",
- fstab->recs[i].blk_device, fstab->recs[i].label);
- goto out_fail;
+ if (fs_mgr_is_noemulatedsd(&fstab->recs[i])) {
+ flags |= android::vold::Disk::Flags::kDefaultPrimary;
}
- vm->addVolume(dv);
+ vm->addDiskSource(std::shared_ptr<VolumeManager::DiskSource>(
+ new VolumeManager::DiskSource(sysPattern, nickname, flags)));
}
}
- ret = 0;
-
-out_fail:
- return ret;
+ return 0;
}