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
This commit is contained in:
Jeff Sharkey 2017-09-11 10:32:01 -06:00
parent a94fc7cdcd
commit 11c2d380a7
26 changed files with 674 additions and 232 deletions

View file

@ -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 \

View file

@ -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<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) {

View file

@ -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 @@ 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;

View file

@ -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);

View file

@ -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) {

2
Loop.h
View file

@ -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);

View file

@ -18,7 +18,7 @@
#define ANDROID_VOLD_MOVE_TASK_H
#include "Utils.h"
#include "VolumeBase.h"
#include "model/VolumeBase.h"
#include <thread>

View file

@ -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 @@ 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

View file

@ -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 @@ 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

View file

@ -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 @@ 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<android::vold::VolumeBase> 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<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 */);
}

View file

@ -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 @@ 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<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 @@ private:
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" {

View file

@ -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;

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
#include "Disk.h"
#include "model/Disk.h"
#include "VolumeManager.h"
#include "CommandListener.h"
#include "CryptCommandListener.h"

130
model/ObbVolume.cpp Normal file
View file

@ -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

57
model/ObbVolume.h Normal file
View file

@ -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

View file

@ -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