From 11c2d380a786d9e304416be98881b90b74ff666d Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Mon, 11 Sep 2017 10:32:01 -0600 Subject: [PATCH] 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 --- Android.mk | 11 +- CommandListener.cpp | 214 ++-------------- Devmapper.cpp | 89 ++++++- Devmapper.h | 1 + Loop.cpp | 53 +++- Loop.h | 2 +- MoveTask.h | 2 +- VoldNativeService.cpp | 59 ++++- VoldNativeService.h | 14 +- VolumeManager.cpp | 235 +++++++++++++++++- VolumeManager.h | 17 +- binder/android/os/IVold.aidl | 15 +- main.cpp | 2 +- Disk.cpp => model/Disk.cpp | 0 Disk.h => model/Disk.h | 0 .../EmulatedVolume.cpp | 0 EmulatedVolume.h => model/EmulatedVolume.h | 0 model/ObbVolume.cpp | 130 ++++++++++ model/ObbVolume.h | 57 +++++ PrivateVolume.cpp => model/PrivateVolume.cpp | 0 PrivateVolume.h => model/PrivateVolume.h | 0 PublicVolume.cpp => model/PublicVolume.cpp | 0 PublicVolume.h => model/PublicVolume.h | 0 VolumeBase.cpp => model/VolumeBase.cpp | 0 VolumeBase.h => model/VolumeBase.h | 0 tests/Android.mk | 5 +- 26 files changed, 674 insertions(+), 232 deletions(-) rename Disk.cpp => model/Disk.cpp (100%) rename Disk.h => model/Disk.h (100%) rename EmulatedVolume.cpp => model/EmulatedVolume.cpp (100%) rename EmulatedVolume.h => model/EmulatedVolume.h (100%) create mode 100644 model/ObbVolume.cpp create mode 100644 model/ObbVolume.h rename PrivateVolume.cpp => model/PrivateVolume.cpp (100%) rename PrivateVolume.h => model/PrivateVolume.h (100%) rename PublicVolume.cpp => model/PublicVolume.cpp (100%) rename PublicVolume.h => model/PublicVolume.h (100%) rename VolumeBase.cpp => model/VolumeBase.cpp (100%) rename VolumeBase.h => model/VolumeBase.h (100%) diff --git a/Android.mk b/Android.mk index f6a8da9..7263a5b 100644 --- a/Android.mk +++ b/Android.mk @@ -18,11 +18,12 @@ common_src_files := \ 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 @@ int CommandListener::FstrimCmd::runCommand(SocketClient *cli, 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 @@ int CommandListener::AppFuseCmd::runCommand(SocketClient *cli, int argc, char ** return 0; } - const std::string command(argv[1]); + VolumeManager *vm = VolumeManager::Instance(); + std::lock_guard 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]); - - // Check mount point name. - std::string path; - if (getMountPath(uid, name, &path) != android::OK) { - return cli->sendMsg(ResponseCode::CommandParameterError, - "Invalid mount point name.", - false); - } - - // 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); - } - } + const int mountId = atoi(argv[4]); - // 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); - } + 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()); } - - 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]); - - // 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 int mountId = atoi(argv[4]); - 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 +#include +#include #include #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 @@ void Devmapper::ioctlInit(struct dm_ioctl *io, size_t dataSize, } } -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 @@ int Devmapper::lookupActive(const char *name, char *ubuffer, size_t len) { 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 @@ int Devmapper::create(const char *name, const char *loopFile, const char *key, 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 @@ int Devmapper::destroy(const char *name) { 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 @@ public: 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 @@ int Loop::dumpState(SocketClient *c) { 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 @@ int Loop::lookupActive(const char *id, char *buffer, size_t len) { 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 @@ int Loop::create(const std::string& target, std::string& out_device) { 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 @@ int Loop::destroyByDevice(const char *loopDevice) { 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 @@ public: 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 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 #include #include #include +#include #include #ifndef LOG_TAG @@ -56,7 +58,7 @@ static binder::Status translate(uint32_t status) { if (status == 0) { return binder::Status::ok(); } else { - return binder::Status::fromExceptionCode(status); + return binder::Status::fromServiceSpecificError(status); } } @@ -134,11 +136,14 @@ binder::Status VoldNativeService::shutdown() { 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 @@ binder::Status VoldNativeService::onUserStopped(int32_t userId) { 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 @@ binder::Status VoldNativeService::forgetPartition(const std::string& partGuid) { 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 @@ binder::Status VoldNativeService::benchmark(const std::string& volId, int64_t* _ 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 @@ binder::Status VoldNativeService::mkdirs(const std::string& path) { 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 #include #include "android/os/BnVold.h" @@ -32,8 +33,7 @@ public: 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 @@ public: 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 #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 @@ VolumeManager::VolumeManager() { mSavedDirtyRatio = -1; // set dirty ratio to 0 when UMS is active mUmsDirtyRatio = 0; + mNextObbId = 0; } VolumeManager::~VolumeManager() { @@ -317,6 +320,9 @@ int VolumeManager::start() { // 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 @@ std::shared_ptr VolumeManager::findVolume(const std:: return vol; } } + for (const auto& vol : mObbVolumes) { + if (vol->getId() == id) { + return vol; + } + } return nullptr; } @@ -754,7 +765,7 @@ int VolumeManager::unmountAll() { 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 @@ int VolumeManager::mkdirs(const char* path) { 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( + 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 #include +#include #include #include #include #include #include -#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 @@ public: */ 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 @@ private: std::list> mDiskSources; std::list> mDisks; + std::list> mObbVolumes; std::unordered_map mAddedUsers; std::unordered_set mStartedUsers; @@ -219,6 +230,8 @@ private: std::shared_ptr mVirtualDisk; std::shared_ptr mInternalEmulated; std::shared_ptr 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 @@ package android.os; 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 @@ interface IVold { 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +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 + +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 @@ include $(CLEAR_VARS) 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