Move even more vold commands over to Binder.
This moves fstrim, obb and appfuse commands over to the new Binder
interface. This change also separates creating/destroying and
mounting/unmounting of OBB volumes, which means they finally flow
nicely into the modern VolumeInfo/VolumeBase design.
We now generate unique identifiers for all OBB volumes, instead of
using a shady MD5 hash.
Change all "loop" and "dm" devices to tag the kernel resources with
a vold-specific prefix so that we can clean them up if vold crashes;
there are new destroyAll() methods that handle this cleanup.
Move appfuse mounting/unmounting into VolumeManager so it can be
shared. Move various model objects into a separate directory to
tidy things up.
Test: cts-tradefed run commandAndExit cts-dev -m CtsOsTestCases -t android.os.storage.cts.StorageManagerTest
Bug: 13758960
Change-Id: I7294e32b3fb6efe07cb3b77bd20166e70b66958f
diff --git a/Android.mk b/Android.mk
index f6a8da9..7263a5b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -18,11 +18,12 @@
Ext4Crypt.cpp \
VoldUtil.c \
cryptfs.cpp \
- Disk.cpp \
- VolumeBase.cpp \
- PublicVolume.cpp \
- PrivateVolume.cpp \
- EmulatedVolume.cpp \
+ model/Disk.cpp \
+ model/VolumeBase.cpp \
+ model/PublicVolume.cpp \
+ model/PrivateVolume.cpp \
+ model/EmulatedVolume.cpp \
+ model/ObbVolume.cpp \
Utils.cpp \
MoveTask.cpp \
Benchmark.cpp \
diff --git a/CommandListener.cpp b/CommandListener.cpp
index 8da3f69..60c0898 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -44,7 +44,7 @@
#include "CommandListener.h"
#include "VolumeManager.h"
-#include "VolumeBase.h"
+#include "model/VolumeBase.h"
#include "ResponseCode.h"
#include "Process.h"
#include "Loop.h"
@@ -53,7 +53,6 @@
#include "TrimTask.h"
#define DUMP_ARGS 0
-#define DEBUG_APPFUSE 0
using android::base::unique_fd;
@@ -617,157 +616,6 @@
return sendGenericOkFail(cli, 0);
}
-static size_t kAppFuseMaxMountPointName = 32;
-
-static android::status_t getMountPath(uid_t uid, const std::string& name, std::string* path) {
- if (name.size() > kAppFuseMaxMountPointName) {
- LOG(ERROR) << "AppFuse mount name is too long.";
- return -EINVAL;
- }
- for (size_t i = 0; i < name.size(); i++) {
- if (!isalnum(name[i])) {
- LOG(ERROR) << "AppFuse mount name contains invalid character.";
- return -EINVAL;
- }
- }
- *path = android::base::StringPrintf("/mnt/appfuse/%d_%s", uid, name.c_str());
- return android::OK;
-}
-
-static android::status_t mountInNamespace(uid_t uid, int device_fd, const std::string& path) {
- // Remove existing mount.
- android::vold::ForceUnmount(path);
-
- const auto opts = android::base::StringPrintf(
- "fd=%i,"
- "rootmode=40000,"
- "default_permissions,"
- "allow_other,"
- "user_id=%d,group_id=%d,"
- "context=\"u:object_r:app_fuse_file:s0\","
- "fscontext=u:object_r:app_fusefs:s0",
- device_fd,
- uid,
- uid);
-
- const int result = TEMP_FAILURE_RETRY(mount(
- "/dev/fuse", path.c_str(), "fuse",
- MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()));
- if (result != 0) {
- PLOG(ERROR) << "Failed to mount " << path;
- return -errno;
- }
-
- return android::OK;
-}
-
-static android::status_t runCommandInNamespace(const std::string& command,
- uid_t uid,
- pid_t pid,
- const std::string& path,
- int device_fd) {
- if (DEBUG_APPFUSE) {
- LOG(DEBUG) << "Run app fuse command " << command << " for the path " << path
- << " in namespace " << uid;
- }
-
- unique_fd dir(open("/proc", O_RDONLY | O_DIRECTORY | O_CLOEXEC));
- if (dir.get() == -1) {
- PLOG(ERROR) << "Failed to open /proc";
- return -errno;
- }
-
- // Obtains process file descriptor.
- const std::string pid_str = android::base::StringPrintf("%d", pid);
- const unique_fd pid_fd(
- openat(dir.get(), pid_str.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
- if (pid_fd.get() == -1) {
- PLOG(ERROR) << "Failed to open /proc/" << pid;
- return -errno;
- }
-
- // Check UID of process.
- {
- struct stat sb;
- const int result = fstat(pid_fd.get(), &sb);
- if (result == -1) {
- PLOG(ERROR) << "Failed to stat /proc/" << pid;
- return -errno;
- }
- if (sb.st_uid != AID_SYSTEM) {
- LOG(ERROR) << "Only system can mount appfuse. UID expected=" << AID_SYSTEM
- << ", actual=" << sb.st_uid;
- return -EPERM;
- }
- }
-
- // Matches so far, but refuse to touch if in root namespace
- {
- char rootName[PATH_MAX];
- char pidName[PATH_MAX];
- const int root_result =
- android::vold::SaneReadLinkAt(dir.get(), "1/ns/mnt", rootName, PATH_MAX);
- const int pid_result =
- android::vold::SaneReadLinkAt(pid_fd.get(), "ns/mnt", pidName, PATH_MAX);
- if (root_result == -1) {
- LOG(ERROR) << "Failed to readlink for /proc/1/ns/mnt";
- return -EPERM;
- }
- if (pid_result == -1) {
- LOG(ERROR) << "Failed to readlink for /proc/" << pid << "/ns/mnt";
- return -EPERM;
- }
- if (!strcmp(rootName, pidName)) {
- LOG(ERROR) << "Don't mount appfuse in root namespace";
- return -EPERM;
- }
- }
-
- // We purposefully leave the namespace open across the fork
- unique_fd ns_fd(openat(pid_fd.get(), "ns/mnt", O_RDONLY)); // not O_CLOEXEC
- if (ns_fd.get() < 0) {
- PLOG(ERROR) << "Failed to open namespace for /proc/" << pid << "/ns/mnt";
- return -errno;
- }
-
- int child = fork();
- if (child == 0) {
- if (setns(ns_fd.get(), CLONE_NEWNS) != 0) {
- PLOG(ERROR) << "Failed to setns";
- _exit(-errno);
- }
-
- if (command == "mount") {
- _exit(mountInNamespace(uid, device_fd, path));
- } else if (command == "unmount") {
- // If it's just after all FD opened on mount point are closed, umount2 can fail with
- // EBUSY. To avoid the case, specify MNT_DETACH.
- if (umount2(path.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH) != 0 &&
- errno != EINVAL && errno != ENOENT) {
- PLOG(ERROR) << "Failed to unmount directory.";
- _exit(-errno);
- }
- if (rmdir(path.c_str()) != 0) {
- PLOG(ERROR) << "Failed to remove the mount directory.";
- _exit(-errno);
- }
- _exit(android::OK);
- } else {
- LOG(ERROR) << "Unknown appfuse command " << command;
- _exit(-EPERM);
- }
- }
-
- if (child == -1) {
- PLOG(ERROR) << "Failed to folk child process";
- return -errno;
- }
-
- android::status_t status;
- TEMP_FAILURE_RETRY(waitpid(child, &status, 0));
-
- return status;
-}
CommandListener::AppFuseCmd::AppFuseCmd() : VoldCommand("appfuse") {}
@@ -777,66 +625,32 @@
return 0;
}
- const std::string command(argv[1]);
+ VolumeManager *vm = VolumeManager::Instance();
+ std::lock_guard<std::mutex> lock(vm->getLock());
+ const std::string command(argv[1]);
if (command == "mount" && argc == 5) {
const uid_t uid = atoi(argv[2]);
const pid_t pid = atoi(argv[3]);
- const std::string name(argv[4]);
+ const int mountId = atoi(argv[4]);
- // Check mount point name.
- std::string path;
- if (getMountPath(uid, name, &path) != android::OK) {
- return cli->sendMsg(ResponseCode::CommandParameterError,
- "Invalid mount point name.",
- false);
+ unique_fd device_fd;
+ int result = vm->mountAppFuse(uid, pid, mountId, &device_fd);
+ if (result != 0) {
+ return sendGenericOkFail(cli, result);
+ } else {
+ return sendFd(cli, device_fd.get());
}
-
- // Create directories.
- {
- const android::status_t result = android::vold::PrepareDir(path, 0700, 0, 0);
- if (result != android::OK) {
- PLOG(ERROR) << "Failed to prepare directory " << path;
- return sendGenericOkFail(cli, result);
- }
- }
-
- // Open device FD.
- unique_fd device_fd(open("/dev/fuse", O_RDWR)); // not O_CLOEXEC
- if (device_fd.get() == -1) {
- PLOG(ERROR) << "Failed to open /dev/fuse";
- return sendGenericOkFail(cli, -errno);
- }
-
- // Mount.
- {
- const android::status_t result =
- runCommandInNamespace(command, uid, pid, path, device_fd.get());
- if (result != android::OK) {
- return sendGenericOkFail(cli, result);
- }
- }
-
- return sendFd(cli, device_fd.get());
} else if (command == "unmount" && argc == 5) {
const uid_t uid = atoi(argv[2]);
const uid_t pid = atoi(argv[3]);
- const std::string name(argv[4]);
+ const int mountId = atoi(argv[4]);
- // Check mount point name.
- std::string path;
- if (getMountPath(uid, name, &path) != android::OK) {
- return cli->sendMsg(ResponseCode::CommandParameterError,
- "Invalid mount point name.",
- false);
- }
-
- const android::status_t result =
- runCommandInNamespace(command, uid, pid, path, -1 /* device_fd */);
+ int result = vm->unmountAppFuse(uid, pid, mountId);
return sendGenericOkFail(cli, result);
}
- return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown appfuse cmd", false);
+ return cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown appfuse cmd", false);
}
android::status_t CommandListener::AppFuseCmd::sendFd(SocketClient *cli, int fd) {
diff --git a/Devmapper.cpp b/Devmapper.cpp
index 4b6942d..c945e9f 100644
--- a/Devmapper.cpp
+++ b/Devmapper.cpp
@@ -32,12 +32,18 @@
#include <cutils/log.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
#include <sysutils/SocketClient.h>
#include "Devmapper.h"
#define DEVMAPPER_BUFFER_SIZE 4096
+using android::base::StringPrintf;
+
+static const char* kVoldPrefix = "vold:";
+
int Devmapper::dumpState(SocketClient *c) {
char *buffer = (char *) malloc(1024 * 64);
@@ -130,7 +136,10 @@
}
}
-int Devmapper::lookupActive(const char *name, char *ubuffer, size_t len) {
+int Devmapper::lookupActive(const char *name_raw, char *ubuffer, size_t len) {
+ auto name_string = StringPrintf("%s%s", kVoldPrefix, name_raw);
+ const char* name = name_string.c_str();
+
char *buffer = (char *) malloc(DEVMAPPER_BUFFER_SIZE);
if (!buffer) {
SLOGE("Error allocating memory (%s)", strerror(errno));
@@ -163,8 +172,11 @@
return 0;
}
-int Devmapper::create(const char *name, const char *loopFile, const char *key,
+int Devmapper::create(const char *name_raw, const char *loopFile, const char *key,
unsigned long numSectors, char *ubuffer, size_t len) {
+ auto name_string = StringPrintf("%s%s", kVoldPrefix, name_raw);
+ const char* name = name_string.c_str();
+
char *buffer = (char *) malloc(DEVMAPPER_BUFFER_SIZE);
if (!buffer) {
SLOGE("Error allocating memory (%s)", strerror(errno));
@@ -261,7 +273,10 @@
return 0;
}
-int Devmapper::destroy(const char *name) {
+int Devmapper::destroy(const char *name_raw) {
+ auto name_string = StringPrintf("%s%s", kVoldPrefix, name_raw);
+ const char* name = name_string.c_str();
+
char *buffer = (char *) malloc(DEVMAPPER_BUFFER_SIZE);
if (!buffer) {
SLOGE("Error allocating memory (%s)", strerror(errno));
@@ -294,6 +309,74 @@
return 0;
}
+int Devmapper::destroyAll() {
+ char *buffer = (char *) malloc(1024 * 64);
+ if (!buffer) {
+ SLOGE("Error allocating memory (%s)", strerror(errno));
+ return -1;
+ }
+ memset(buffer, 0, (1024 * 64));
+
+ char *buffer2 = (char *) malloc(DEVMAPPER_BUFFER_SIZE);
+ if (!buffer2) {
+ SLOGE("Error allocating memory (%s)", strerror(errno));
+ free(buffer);
+ return -1;
+ }
+
+ int fd;
+ if ((fd = open("/dev/device-mapper", O_RDWR | O_CLOEXEC)) < 0) {
+ SLOGE("Error opening devmapper (%s)", strerror(errno));
+ free(buffer);
+ free(buffer2);
+ return -1;
+ }
+
+ struct dm_ioctl *io = (struct dm_ioctl *) buffer;
+ ioctlInit(io, (1024 * 64), NULL, 0);
+
+ if (ioctl(fd, DM_LIST_DEVICES, io)) {
+ SLOGE("DM_LIST_DEVICES ioctl failed (%s)", strerror(errno));
+ free(buffer);
+ free(buffer2);
+ close(fd);
+ return -1;
+ }
+
+ struct dm_name_list *n = (struct dm_name_list *) (((char *) buffer) + io->data_start);
+ if (!n->dev) {
+ free(buffer);
+ free(buffer2);
+ close(fd);
+ return 0;
+ }
+
+ unsigned nxt = 0;
+ do {
+ n = (struct dm_name_list *) (((char *) n) + nxt);
+ if (strncmp(n->name, kVoldPrefix, strlen(kVoldPrefix)) == 0) {
+ LOG(DEBUG) << "Tearing down stale dm device named " << n->name;
+
+ memset(buffer2, 0, DEVMAPPER_BUFFER_SIZE);
+ struct dm_ioctl *io2 = (struct dm_ioctl *) buffer2;
+ ioctlInit(io2, DEVMAPPER_BUFFER_SIZE, n->name, 0);
+ if (ioctl(fd, DM_DEV_REMOVE, io2)) {
+ if (errno != ENXIO) {
+ PLOG(WARNING) << "Failed to destroy dm device named " << n->name;
+ }
+ }
+ } else {
+ LOG(VERBOSE) << "Found unmanaged dm device named " << n->name;
+ }
+ nxt = n->next;
+ } while (nxt);
+
+ free(buffer);
+ free(buffer2);
+ close(fd);
+ return 0;
+}
+
void *Devmapper::_align(void *ptr, unsigned int a)
{
unsigned long agn = --a;
diff --git a/Devmapper.h b/Devmapper.h
index 5b65b53..dcc39d8 100644
--- a/Devmapper.h
+++ b/Devmapper.h
@@ -27,6 +27,7 @@
static int create(const char *name, const char *loopFile, const char *key,
unsigned long numSectors, char *buffer, size_t len);
static int destroy(const char *name);
+ static int destroyAll();
static int lookupActive(const char *name, char *buffer, size_t len);
static int dumpState(SocketClient *c);
diff --git a/Loop.cpp b/Loop.cpp
index 6ec5e6d..325b0d3 100644
--- a/Loop.cpp
+++ b/Loop.cpp
@@ -45,6 +45,8 @@
using android::base::StringPrintf;
using android::base::unique_fd;
+static const char* kVoldPrefix = "vold:";
+
int Loop::dumpState(SocketClient *c) {
int i;
int fd;
@@ -87,7 +89,10 @@
return 0;
}
-int Loop::lookupActive(const char *id, char *buffer, size_t len) {
+int Loop::lookupActive(const char *id_raw, char *buffer, size_t len) {
+ auto id_string = StringPrintf("%s%s", kVoldPrefix, id_raw);
+ const char* id = id_string.c_str();
+
int i;
int fd;
char filename[256];
@@ -134,7 +139,10 @@
return 0;
}
-int Loop::create(const char *id, const char *loopFile, char *loopDeviceBuffer, size_t len) {
+int Loop::create(const char *id_raw, const char *loopFile, char *loopDeviceBuffer, size_t len) {
+ auto id_string = StringPrintf("%s%s", kVoldPrefix, id_raw);
+ const char* id = id_string.c_str();
+
int i;
int fd;
char filename[256];
@@ -267,6 +275,14 @@
return -errno;
}
+ struct loop_info64 li;
+ memset(&li, 0, sizeof(li));
+ strlcpy((char*) li.lo_crypt_name, kVoldPrefix, LO_NAME_SIZE);
+ if (ioctl(device_fd.get(), LOOP_SET_STATUS64, &li) == -1) {
+ PLOG(ERROR) << "Failed to LOOP_SET_STATUS64";
+ return -errno;
+ }
+
return 0;
}
@@ -289,9 +305,36 @@
return 0;
}
-int Loop::destroyByFile(const char * /*loopFile*/) {
- errno = ENOSYS;
- return -1;
+int Loop::destroyAll() {
+ for (int i = 0; i < LOOP_MAX; i++) {
+ auto path = StringPrintf("/dev/block/loop%d", i);
+
+ unique_fd fd(open(path.c_str(), O_RDWR | O_CLOEXEC));
+ if (fd.get() == -1) {
+ if (errno != ENOENT) {
+ PLOG(WARNING) << "Failed to open " << path;
+ }
+ continue;
+ }
+
+ struct loop_info64 li;
+ if (ioctl(fd.get(), LOOP_GET_STATUS64, &li) < 0) {
+ PLOG(WARNING) << "Failed to LOOP_GET_STATUS64 " << path;
+ continue;
+ }
+
+ char* id = (char*) li.lo_crypt_name;
+ if (strncmp(id, kVoldPrefix, strlen(kVoldPrefix)) == 0) {
+ LOG(DEBUG) << "Tearing down stale loop device at " << path << " named " << id;
+
+ if (ioctl(fd.get(), LOOP_CLR_FD, 0) < 0) {
+ PLOG(WARNING) << "Failed to LOOP_CLR_FD " << path;
+ }
+ } else {
+ LOG(VERBOSE) << "Found unmanaged loop device at " << path << " named " << id;
+ }
+ }
+ return 0;
}
int Loop::createImageFile(const char *file, unsigned long numSectors) {
diff --git a/Loop.h b/Loop.h
index 5d8f427..e3ad239 100644
--- a/Loop.h
+++ b/Loop.h
@@ -32,7 +32,7 @@
static int create(const char *id, const char *loopFile, char *loopDeviceBuffer, size_t len);
static int create(const std::string& file, std::string& out_device);
static int destroyByDevice(const char *loopDevice);
- static int destroyByFile(const char *loopFile);
+ static int destroyAll();
static int createImageFile(const char *file, unsigned long numSectors);
static int resizeImageFile(const char *file, unsigned long numSectors);
diff --git a/MoveTask.h b/MoveTask.h
index b1777c0..cb184c3 100644
--- a/MoveTask.h
+++ b/MoveTask.h
@@ -18,7 +18,7 @@
#define ANDROID_VOLD_MOVE_TASK_H
#include "Utils.h"
-#include "VolumeBase.h"
+#include "model/VolumeBase.h"
#include <thread>
diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp
index f98c15d..3585e96 100644
--- a/VoldNativeService.cpp
+++ b/VoldNativeService.cpp
@@ -17,12 +17,14 @@
#include "VoldNativeService.h"
#include "VolumeManager.h"
#include "MoveTask.h"
+#include "TrimTask.h"
#include <fstream>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <fs_mgr.h>
#include <private/android_filesystem_config.h>
#ifndef LOG_TAG
@@ -56,7 +58,7 @@
if (status == 0) {
return binder::Status::ok();
} else {
- return binder::Status::fromExceptionCode(status);
+ return binder::Status::fromServiceSpecificError(status);
}
}
@@ -134,11 +136,14 @@
return translate(VolumeManager::Instance()->shutdown());
}
-binder::Status VoldNativeService::setDebug(bool debug) {
+binder::Status VoldNativeService::mountAll() {
ENFORCE_UID(AID_SYSTEM);
ACQUIRE_LOCK;
- return translate(VolumeManager::Instance()->setDebug(debug));
+ struct fstab* fstab = fs_mgr_read_fstab_default();
+ int res = fs_mgr_mount_all(fstab, MOUNT_MODE_DEFAULT);
+ fs_mgr_free_fstab(fstab);
+ return translate(res);
}
binder::Status VoldNativeService::onUserAdded(int32_t userId, int32_t userSerial) {
@@ -169,7 +174,8 @@
return translate(VolumeManager::Instance()->onUserStopped(userId));
}
-binder::Status VoldNativeService::partition(const std::string& diskId, int32_t partitionType, int32_t ratio) {
+binder::Status VoldNativeService::partition(const std::string& diskId, int32_t partitionType,
+ int32_t ratio) {
ENFORCE_UID(AID_SYSTEM);
ACQUIRE_LOCK;
@@ -192,7 +198,8 @@
return translate(VolumeManager::Instance()->forgetPartition(partGuid));
}
-binder::Status VoldNativeService::mount(const std::string& volId, int32_t mountFlags, int32_t mountUserId) {
+binder::Status VoldNativeService::mount(const std::string& volId, int32_t mountFlags,
+ int32_t mountUserId) {
ENFORCE_UID(AID_SYSTEM);
ACQUIRE_LOCK;
@@ -241,7 +248,8 @@
return ok();
}
-binder::Status VoldNativeService::moveStorage(const std::string& fromVolId, const std::string& toVolId) {
+binder::Status VoldNativeService::moveStorage(const std::string& fromVolId,
+ const std::string& toVolId) {
ENFORCE_UID(AID_SYSTEM);
ACQUIRE_LOCK;
@@ -278,5 +286,44 @@
return translate(VolumeManager::Instance()->mkdirs(path.c_str()));
}
+binder::Status VoldNativeService::createObb(const std::string& sourcePath,
+ const std::string& sourceKey, int32_t ownerGid, std::string* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ ACQUIRE_LOCK;
+
+ return translate(
+ VolumeManager::Instance()->createObb(sourcePath, sourceKey, ownerGid, _aidl_return));
+}
+
+binder::Status VoldNativeService::destroyObb(const std::string& volId) {
+ ENFORCE_UID(AID_SYSTEM);
+ ACQUIRE_LOCK;
+
+ return translate(VolumeManager::Instance()->destroyObb(volId));
+}
+
+binder::Status VoldNativeService::fstrim(int32_t fstrimFlags) {
+ ENFORCE_UID(AID_SYSTEM);
+ ACQUIRE_LOCK;
+
+ (new android::vold::TrimTask(fstrimFlags))->start();
+ return ok();
+}
+
+binder::Status VoldNativeService::mountAppFuse(int32_t uid, int32_t pid, int32_t mountId,
+ android::base::unique_fd* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ ACQUIRE_LOCK;
+
+ return translate(VolumeManager::Instance()->mountAppFuse(uid, pid, mountId, _aidl_return));
+}
+
+binder::Status VoldNativeService::unmountAppFuse(int32_t uid, int32_t pid, int32_t mountId) {
+ ENFORCE_UID(AID_SYSTEM);
+ ACQUIRE_LOCK;
+
+ return translate(VolumeManager::Instance()->unmountAppFuse(uid, pid, mountId));
+}
+
} // namespace vold
} // namespace android
diff --git a/VoldNativeService.h b/VoldNativeService.h
index d3bce67..f412bfc 100644
--- a/VoldNativeService.h
+++ b/VoldNativeService.h
@@ -17,6 +17,7 @@
#ifndef _VOLD_NATIVE_SERVICE_H_
#define _VOLD_NATIVE_SERVICE_H_
+#include <android-base/unique_fd.h>
#include <binder/BinderService.h>
#include "android/os/BnVold.h"
@@ -32,8 +33,7 @@
binder::Status reset();
binder::Status shutdown();
-
- binder::Status setDebug(bool debug);
+ binder::Status mountAll();
binder::Status onUserAdded(int32_t userId, int32_t userSerial);
binder::Status onUserRemoved(int32_t userId);
@@ -53,6 +53,16 @@
binder::Status remountUid(int32_t uid, int32_t remountMode);
binder::Status mkdirs(const std::string& path);
+
+ binder::Status createObb(const std::string& sourcePath, const std::string& sourceKey,
+ int32_t ownerGid, std::string* _aidl_return);
+ binder::Status destroyObb(const std::string& volId);
+
+ binder::Status fstrim(int32_t fstrimFlags);
+
+ binder::Status mountAppFuse(int32_t uid, int32_t pid, int32_t mountId,
+ android::base::unique_fd* _aidl_return);
+ binder::Status unmountAppFuse(int32_t uid, int32_t pid, int32_t mountId);
};
} // namespace vold
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index ddf06eb..022caff 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -48,7 +48,8 @@
#include <private/android_filesystem_config.h>
#include "Benchmark.h"
-#include "EmulatedVolume.h"
+#include "model/EmulatedVolume.h"
+#include "model/ObbVolume.h"
#include "VolumeManager.h"
#include "NetlinkManager.h"
#include "ResponseCode.h"
@@ -68,6 +69,7 @@
+ ((number) & (~((1U << (po2)) - 1))))
using android::base::StringPrintf;
+using android::base::unique_fd;
/*
* Path to external storage where *only* root can access ASEC image files
@@ -220,6 +222,7 @@
mSavedDirtyRatio = -1;
// set dirty ratio to 0 when UMS is active
mUmsDirtyRatio = 0;
+ mNextObbId = 0;
}
VolumeManager::~VolumeManager() {
@@ -317,6 +320,9 @@
// directories that we own, in case we crashed.
unmountAll();
+ Devmapper::destroyAll();
+ Loop::destroyAll();
+
// Assume that we always have an emulated volume on internal
// storage; the framework will decide if it should be mounted.
CHECK(mInternalEmulated == nullptr);
@@ -437,6 +443,11 @@
return vol;
}
}
+ for (const auto& vol : mObbVolumes) {
+ if (vol->getId() == id) {
+ return vol;
+ }
+ }
return nullptr;
}
@@ -754,7 +765,7 @@
endmntent(fp);
for (const auto& path : toUnmount) {
- SLOGW("Tearing down stale mount %s", path.c_str());
+ LOG(DEBUG) << "Tearing down stale mount " << path;
android::vold::ForceUnmount(path);
}
@@ -1962,3 +1973,223 @@
return -EINVAL;
}
}
+
+static size_t kAppFuseMaxMountPointName = 32;
+
+static android::status_t getMountPath(uid_t uid, const std::string& name, std::string* path) {
+ if (name.size() > kAppFuseMaxMountPointName) {
+ LOG(ERROR) << "AppFuse mount name is too long.";
+ return -EINVAL;
+ }
+ for (size_t i = 0; i < name.size(); i++) {
+ if (!isalnum(name[i])) {
+ LOG(ERROR) << "AppFuse mount name contains invalid character.";
+ return -EINVAL;
+ }
+ }
+ *path = android::base::StringPrintf("/mnt/appfuse/%d_%s", uid, name.c_str());
+ return android::OK;
+}
+
+static android::status_t mountInNamespace(uid_t uid, int device_fd, const std::string& path) {
+ // Remove existing mount.
+ android::vold::ForceUnmount(path);
+
+ const auto opts = android::base::StringPrintf(
+ "fd=%i,"
+ "rootmode=40000,"
+ "default_permissions,"
+ "allow_other,"
+ "user_id=%d,group_id=%d,"
+ "context=\"u:object_r:app_fuse_file:s0\","
+ "fscontext=u:object_r:app_fusefs:s0",
+ device_fd,
+ uid,
+ uid);
+
+ const int result = TEMP_FAILURE_RETRY(mount(
+ "/dev/fuse", path.c_str(), "fuse",
+ MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()));
+ if (result != 0) {
+ PLOG(ERROR) << "Failed to mount " << path;
+ return -errno;
+ }
+
+ return android::OK;
+}
+
+static android::status_t runCommandInNamespace(const std::string& command,
+ uid_t uid,
+ pid_t pid,
+ const std::string& path,
+ int device_fd) {
+ if (DEBUG_APPFUSE) {
+ LOG(DEBUG) << "Run app fuse command " << command << " for the path " << path
+ << " in namespace " << uid;
+ }
+
+ unique_fd dir(open("/proc", O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+ if (dir.get() == -1) {
+ PLOG(ERROR) << "Failed to open /proc";
+ return -errno;
+ }
+
+ // Obtains process file descriptor.
+ const std::string pid_str = android::base::StringPrintf("%d", pid);
+ const unique_fd pid_fd(
+ openat(dir.get(), pid_str.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+ if (pid_fd.get() == -1) {
+ PLOG(ERROR) << "Failed to open /proc/" << pid;
+ return -errno;
+ }
+
+ // Check UID of process.
+ {
+ struct stat sb;
+ const int result = fstat(pid_fd.get(), &sb);
+ if (result == -1) {
+ PLOG(ERROR) << "Failed to stat /proc/" << pid;
+ return -errno;
+ }
+ if (sb.st_uid != AID_SYSTEM) {
+ LOG(ERROR) << "Only system can mount appfuse. UID expected=" << AID_SYSTEM
+ << ", actual=" << sb.st_uid;
+ return -EPERM;
+ }
+ }
+
+ // Matches so far, but refuse to touch if in root namespace
+ {
+ char rootName[PATH_MAX];
+ char pidName[PATH_MAX];
+ const int root_result =
+ android::vold::SaneReadLinkAt(dir.get(), "1/ns/mnt", rootName, PATH_MAX);
+ const int pid_result =
+ android::vold::SaneReadLinkAt(pid_fd.get(), "ns/mnt", pidName, PATH_MAX);
+ if (root_result == -1) {
+ LOG(ERROR) << "Failed to readlink for /proc/1/ns/mnt";
+ return -EPERM;
+ }
+ if (pid_result == -1) {
+ LOG(ERROR) << "Failed to readlink for /proc/" << pid << "/ns/mnt";
+ return -EPERM;
+ }
+ if (!strcmp(rootName, pidName)) {
+ LOG(ERROR) << "Don't mount appfuse in root namespace";
+ return -EPERM;
+ }
+ }
+
+ // We purposefully leave the namespace open across the fork
+ unique_fd ns_fd(openat(pid_fd.get(), "ns/mnt", O_RDONLY)); // not O_CLOEXEC
+ if (ns_fd.get() < 0) {
+ PLOG(ERROR) << "Failed to open namespace for /proc/" << pid << "/ns/mnt";
+ return -errno;
+ }
+
+ int child = fork();
+ if (child == 0) {
+ if (setns(ns_fd.get(), CLONE_NEWNS) != 0) {
+ PLOG(ERROR) << "Failed to setns";
+ _exit(-errno);
+ }
+
+ if (command == "mount") {
+ _exit(mountInNamespace(uid, device_fd, path));
+ } else if (command == "unmount") {
+ // If it's just after all FD opened on mount point are closed, umount2 can fail with
+ // EBUSY. To avoid the case, specify MNT_DETACH.
+ if (umount2(path.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH) != 0 &&
+ errno != EINVAL && errno != ENOENT) {
+ PLOG(ERROR) << "Failed to unmount directory.";
+ _exit(-errno);
+ }
+ if (rmdir(path.c_str()) != 0) {
+ PLOG(ERROR) << "Failed to remove the mount directory.";
+ _exit(-errno);
+ }
+ _exit(android::OK);
+ } else {
+ LOG(ERROR) << "Unknown appfuse command " << command;
+ _exit(-EPERM);
+ }
+ }
+
+ if (child == -1) {
+ PLOG(ERROR) << "Failed to folk child process";
+ return -errno;
+ }
+
+ android::status_t status;
+ TEMP_FAILURE_RETRY(waitpid(child, &status, 0));
+
+ return status;
+}
+
+int VolumeManager::createObb(const std::string& sourcePath, const std::string& sourceKey,
+ int32_t ownerGid, std::string* outVolId) {
+ int id = mNextObbId++;
+
+ auto vol = std::shared_ptr<android::vold::VolumeBase>(
+ new android::vold::ObbVolume(id, sourcePath, sourceKey, ownerGid));
+ vol->create();
+
+ mObbVolumes.push_back(vol);
+ *outVolId = vol->getId();
+ return android::OK;
+}
+
+int VolumeManager::destroyObb(const std::string& volId) {
+ auto i = mObbVolumes.begin();
+ while (i != mObbVolumes.end()) {
+ if ((*i)->getId() == volId) {
+ (*i)->destroy();
+ i = mObbVolumes.erase(i);
+ } else {
+ ++i;
+ }
+ }
+ return android::OK;
+}
+
+int VolumeManager::mountAppFuse(uid_t uid, pid_t pid, int mountId,
+ android::base::unique_fd* device_fd) {
+ std::string name = std::to_string(mountId);
+
+ // Check mount point name.
+ std::string path;
+ if (getMountPath(uid, name, &path) != android::OK) {
+ LOG(ERROR) << "Invalid mount point name";
+ return -1;
+ }
+
+ // Create directories.
+ const android::status_t result = android::vold::PrepareDir(path, 0700, 0, 0);
+ if (result != android::OK) {
+ PLOG(ERROR) << "Failed to prepare directory " << path;
+ return -1;
+ }
+
+ // Open device FD.
+ device_fd->reset(open("/dev/fuse", O_RDWR)); // not O_CLOEXEC
+ if (device_fd->get() == -1) {
+ PLOG(ERROR) << "Failed to open /dev/fuse";
+ return -1;
+ }
+
+ // Mount.
+ return runCommandInNamespace("mount", uid, pid, path, device_fd->get());
+}
+
+int VolumeManager::unmountAppFuse(uid_t uid, pid_t pid, int mountId) {
+ std::string name = std::to_string(mountId);
+
+ // Check mount point name.
+ std::string path;
+ if (getMountPath(uid, name, &path) != android::OK) {
+ LOG(ERROR) << "Invalid mount point name";
+ return -1;
+ }
+
+ return runCommandInNamespace("unmount", uid, pid, path, -1 /* device_fd */);
+}
diff --git a/VolumeManager.h b/VolumeManager.h
index 72c470a..097ce6a 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -29,18 +29,21 @@
#include <unordered_map>
#include <unordered_set>
+#include <android-base/unique_fd.h>
#include <cutils/multiuser.h>
#include <utils/List.h>
#include <utils/Timers.h>
#include <sysutils/SocketListener.h>
#include <sysutils/NetlinkEvent.h>
-#include "Disk.h"
-#include "VolumeBase.h"
+#include "model/Disk.h"
+#include "model/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)
+#define DEBUG_APPFUSE 0
+
typedef enum { ASEC, OBB } container_type_t;
class ContainerData {
@@ -198,6 +201,13 @@
*/
int mkdirs(const char* path);
+ int createObb(const std::string& path, const std::string& key, int32_t ownerGid,
+ std::string* outVolId);
+ int destroyObb(const std::string& volId);
+
+ int mountAppFuse(uid_t uid, pid_t pid, int mountId, android::base::unique_fd* device_fd);
+ int unmountAppFuse(uid_t uid, pid_t pid, int mountId);
+
private:
VolumeManager();
void readInitialState();
@@ -211,6 +221,7 @@
std::list<std::shared_ptr<DiskSource>> mDiskSources;
std::list<std::shared_ptr<android::vold::Disk>> mDisks;
+ std::list<std::shared_ptr<android::vold::VolumeBase>> mObbVolumes;
std::unordered_map<userid_t, int> mAddedUsers;
std::unordered_set<userid_t> mStartedUsers;
@@ -219,6 +230,8 @@
std::shared_ptr<android::vold::Disk> mVirtualDisk;
std::shared_ptr<android::vold::VolumeBase> mInternalEmulated;
std::shared_ptr<android::vold::VolumeBase> mPrimary;
+
+ int mNextObbId;
};
extern "C" {
diff --git a/binder/android/os/IVold.aidl b/binder/android/os/IVold.aidl
index 43d88bc..d945357 100644
--- a/binder/android/os/IVold.aidl
+++ b/binder/android/os/IVold.aidl
@@ -20,8 +20,7 @@
interface IVold {
void reset();
void shutdown();
-
- void setDebug(boolean debug);
+ void mountAll();
void onUserAdded(int userId, int userSerial);
void onUserRemoved(int userId);
@@ -42,6 +41,18 @@
void mkdirs(@utf8InCpp String path);
+ @utf8InCpp String createObb(@utf8InCpp String sourcePath,
+ @utf8InCpp String sourceKey, int ownerGid);
+ void destroyObb(@utf8InCpp String volId);
+
+ void fstrim(int fstrimFlags);
+
+ FileDescriptor mountAppFuse(int uid, int pid, int mountId);
+ void unmountAppFuse(int uid, int pid, int mountId);
+
+ const int FSTRIM_FLAG_DEEP_TRIM = 1;
+ const int FSTRIM_FLAG_BENCHMARK_AFTER = 2;
+
const int MOUNT_FLAG_PRIMARY = 1;
const int MOUNT_FLAG_VISIBLE = 2;
diff --git a/main.cpp b/main.cpp
index ec240ef..8175dc5 100644
--- a/main.cpp
+++ b/main.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "Disk.h"
+#include "model/Disk.h"
#include "VolumeManager.h"
#include "CommandListener.h"
#include "CryptCommandListener.h"
diff --git a/Disk.cpp b/model/Disk.cpp
similarity index 100%
rename from Disk.cpp
rename to model/Disk.cpp
diff --git a/Disk.h b/model/Disk.h
similarity index 100%
rename from Disk.h
rename to model/Disk.h
diff --git a/EmulatedVolume.cpp b/model/EmulatedVolume.cpp
similarity index 100%
rename from EmulatedVolume.cpp
rename to model/EmulatedVolume.cpp
diff --git a/EmulatedVolume.h b/model/EmulatedVolume.h
similarity index 100%
rename from EmulatedVolume.h
rename to model/EmulatedVolume.h
diff --git a/model/ObbVolume.cpp b/model/ObbVolume.cpp
new file mode 100644
index 0000000..709c7a3
--- /dev/null
+++ b/model/ObbVolume.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2017 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 "fs/Vfat.h"
+#include "Devmapper.h"
+#include "Loop.h"
+#include "ObbVolume.h"
+#include "Utils.h"
+#include "VoldUtil.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.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/sysmacros.h>
+#include <sys/wait.h>
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+namespace android {
+namespace vold {
+
+ObbVolume::ObbVolume(int id, const std::string& sourcePath, const std::string& sourceKey,
+ gid_t ownerGid) : VolumeBase(Type::kObb) {
+ setId(StringPrintf("obb:%d", id));
+ mSourcePath = sourcePath;
+ mSourceKey = sourceKey;
+ mOwnerGid = ownerGid;
+}
+
+ObbVolume::~ObbVolume() {
+}
+
+status_t ObbVolume::doCreate() {
+ if (Loop::create(mSourcePath, mLoopPath)) {
+ PLOG(ERROR) << getId() << " failed to create loop";
+ return -1;
+ }
+
+ if (!mSourceKey.empty()) {
+ unsigned long nr_sec = 0;
+ {
+ unique_fd loop_fd(open(mLoopPath.c_str(), O_RDWR | O_CLOEXEC));
+ if (loop_fd.get() == -1) {
+ PLOG(ERROR) << getId() << " failed to open loop";
+ return -1;
+ }
+
+ get_blkdev_size(loop_fd.get(), &nr_sec);
+ if (nr_sec == 0) {
+ PLOG(ERROR) << getId() << " failed to get loop size";
+ return -1;
+ }
+ }
+
+ char tmp[PATH_MAX];
+ if (Devmapper::create(getId().c_str(), mLoopPath.c_str(), mSourceKey.c_str(), nr_sec,
+ tmp, PATH_MAX)) {
+ PLOG(ERROR) << getId() << " failed to create dm";
+ return -1;
+ }
+ mDmPath = tmp;
+ mMountPath = mDmPath;
+ } else {
+ mMountPath = mLoopPath;
+ }
+ return OK;
+}
+
+status_t ObbVolume::doDestroy() {
+ if (!mDmPath.empty() && Devmapper::destroy(getId().c_str())) {
+ PLOG(WARNING) << getId() << " failed to destroy dm";
+ }
+ if (!mLoopPath.empty() && Loop::destroyByDevice(mLoopPath.c_str())) {
+ PLOG(WARNING) << getId() << " failed to destroy loop";
+ }
+ mDmPath.clear();
+ mLoopPath.clear();
+ return OK;
+}
+
+status_t ObbVolume::doMount() {
+ auto path = StringPrintf("/mnt/obb/%s", getId().c_str());
+ setPath(path);
+
+ if (fs_prepare_dir(path.c_str(), 0700, AID_ROOT, AID_ROOT)) {
+ PLOG(ERROR) << getId() << " failed to create mount point";
+ return -1;
+ }
+ if (android::vold::vfat::Mount(mMountPath, path,
+ true, false, true, 0, mOwnerGid, 0227, false)) {
+ PLOG(ERROR) << getId() << " failed to mount";
+ return -1;
+ }
+ return OK;
+}
+
+status_t ObbVolume::doUnmount() {
+ auto path = getPath();
+
+ KillProcessesUsingPath(path);
+ ForceUnmount(path);
+ rmdir(path.c_str());
+
+ return OK;
+}
+
+} // namespace vold
+} // namespace android
diff --git a/model/ObbVolume.h b/model/ObbVolume.h
new file mode 100644
index 0000000..5ec0cde
--- /dev/null
+++ b/model/ObbVolume.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 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_OBB_VOLUME_H
+#define ANDROID_VOLD_OBB_VOLUME_H
+
+#include "VolumeBase.h"
+
+#include <cutils/multiuser.h>
+
+namespace android {
+namespace vold {
+
+/*
+ * OBB container.
+ */
+class ObbVolume : public VolumeBase {
+public:
+ ObbVolume(int id, const std::string& sourcePath, const std::string& sourceKey,
+ gid_t ownerGid);
+ virtual ~ObbVolume();
+
+protected:
+ status_t doCreate() override;
+ status_t doDestroy() override;
+ status_t doMount() override;
+ status_t doUnmount() override;
+
+private:
+ std::string mSourcePath;
+ std::string mSourceKey;
+ gid_t mOwnerGid;
+
+ std::string mLoopPath;
+ std::string mDmPath;
+ std::string mMountPath;
+
+ DISALLOW_COPY_AND_ASSIGN(ObbVolume);
+};
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/PrivateVolume.cpp b/model/PrivateVolume.cpp
similarity index 100%
rename from PrivateVolume.cpp
rename to model/PrivateVolume.cpp
diff --git a/PrivateVolume.h b/model/PrivateVolume.h
similarity index 100%
rename from PrivateVolume.h
rename to model/PrivateVolume.h
diff --git a/PublicVolume.cpp b/model/PublicVolume.cpp
similarity index 100%
rename from PublicVolume.cpp
rename to model/PublicVolume.cpp
diff --git a/PublicVolume.h b/model/PublicVolume.h
similarity index 100%
rename from PublicVolume.h
rename to model/PublicVolume.h
diff --git a/VolumeBase.cpp b/model/VolumeBase.cpp
similarity index 100%
rename from VolumeBase.cpp
rename to model/VolumeBase.cpp
diff --git a/VolumeBase.h b/model/VolumeBase.h
similarity index 100%
rename from VolumeBase.h
rename to model/VolumeBase.h
diff --git a/tests/Android.mk b/tests/Android.mk
index 4b6573e..9cebd1a 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -5,9 +5,10 @@
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_C_INCLUDES := \
- system/core/fs_mgr/include
+ system/core/fs_mgr/include \
+ system/vold/
-LOCAL_STATIC_LIBRARIES := libselinux libvold liblog libcrypto
+LOCAL_STATIC_LIBRARIES := libbase libselinux libvold liblog libcrypto
LOCAL_SRC_FILES := VolumeManager_test.cpp
LOCAL_MODULE := vold_tests