Merge "[incremental] use vold to mount/unmount IncrementalFileSystem" am: 6bdfb77d8b am: a0945f468a
am: 1119bc8531
Change-Id: I772667d5c43cdf1ff37b156db9f47b61820433c7
diff --git a/Android.bp b/Android.bp
index 8d88e5f..e1877b9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -28,6 +28,7 @@
name: "vold_default_libs",
static_libs: [
+ "libasync_safe",
"libavb",
"libbootloader_message",
"libdm",
@@ -235,6 +236,7 @@
"libhardware_legacy",
"libhidlbase",
"libkeymaster4support",
+ "libutils",
],
}
@@ -271,6 +273,7 @@
srcs: [
"binder/android/os/IVold.aidl",
"binder/android/os/IVoldListener.aidl",
+ "binder/android/os/IVoldMountCallback.aidl",
"binder/android/os/IVoldTaskListener.aidl",
],
path: "binder",
diff --git a/Keymaster.h b/Keymaster.h
index 42a2b5d..9a0616d 100644
--- a/Keymaster.h
+++ b/Keymaster.h
@@ -115,7 +115,7 @@
bool isSecure();
private:
- std::unique_ptr<KmDevice> mDevice;
+ sp<KmDevice> mDevice;
DISALLOW_COPY_AND_ASSIGN(Keymaster);
static bool hmacKeyGenerated;
};
diff --git a/Utils.cpp b/Utils.cpp
index 1616d80..af93824 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -165,7 +165,7 @@
if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
return OK;
}
-
+ PLOG(INFO) << "ForceUnmount failed";
return -errno;
}
@@ -985,5 +985,107 @@
return true;
}
+status_t MountUserFuse(userid_t user_id, const std::string& absolute_lower_path,
+ const std::string& relative_upper_path, android::base::unique_fd* fuse_fd) {
+ std::string pre_fuse_path(StringPrintf("/mnt/user/%d", user_id));
+ std::string fuse_path(
+ StringPrintf("%s/%s", pre_fuse_path.c_str(), relative_upper_path.c_str()));
+
+ std::string pre_pass_through_path(StringPrintf("/mnt/pass_through/%d", user_id));
+ std::string pass_through_path(
+ StringPrintf("%s/%s", pre_pass_through_path.c_str(), relative_upper_path.c_str()));
+
+ // Force remove the existing mount before we attempt to prepare the
+ // directory. If we have a dangling mount, then PrepareDir may fail if the
+ // indirection to FUSE doesn't work.
+ android::status_t result = UnmountUserFuse(pass_through_path, fuse_path);
+ if (result != android::OK) {
+ return -1;
+ }
+
+ // Create directories.
+ result = PrepareDir(pre_fuse_path, 0700, AID_ROOT, AID_ROOT);
+ if (result != android::OK) {
+ PLOG(ERROR) << "Failed to prepare directory " << pre_fuse_path;
+ return -1;
+ }
+
+ result = PrepareDir(fuse_path, 0700, AID_ROOT, AID_ROOT);
+ if (result != android::OK) {
+ PLOG(ERROR) << "Failed to prepare directory " << fuse_path;
+ return -1;
+ }
+
+ result = PrepareDir(pre_pass_through_path, 0755, AID_ROOT, AID_ROOT);
+ if (result != android::OK) {
+ PLOG(ERROR) << "Failed to prepare directory " << pre_pass_through_path;
+ return -1;
+ }
+
+ result = PrepareDir(pass_through_path, 0755, AID_ROOT, AID_ROOT);
+ if (result != android::OK) {
+ PLOG(ERROR) << "Failed to prepare directory " << pass_through_path;
+ return -1;
+ }
+
+ if (relative_upper_path == "emulated") {
+ std::string linkpath(StringPrintf("/mnt/user/%d/self", user_id));
+ result = PrepareDir(linkpath, 0755, AID_ROOT, AID_ROOT);
+ if (result != android::OK) {
+ PLOG(ERROR) << "Failed to prepare directory " << linkpath;
+ return -1;
+ }
+ linkpath += "/primary";
+
+ Symlink(fuse_path + "/" + std::to_string(user_id), linkpath);
+ }
+
+ // Open fuse fd.
+ fuse_fd->reset(open("/dev/fuse", O_RDWR | O_CLOEXEC));
+ if (fuse_fd->get() == -1) {
+ PLOG(ERROR) << "Failed to open /dev/fuse";
+ return -1;
+ }
+
+ // Note: leaving out default_permissions since we don't want kernel to do lower filesystem
+ // permission checks before routing to FUSE daemon.
+ const auto opts = StringPrintf(
+ "fd=%i,"
+ "rootmode=40000,"
+ "allow_other,"
+ "user_id=0,group_id=0,",
+ fuse_fd->get());
+
+ result = TEMP_FAILURE_RETRY(mount("/dev/fuse", fuse_path.c_str(), "fuse",
+ MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME | MS_LAZYTIME,
+ opts.c_str()));
+ if (result != 0) {
+ PLOG(ERROR) << "Failed to mount " << fuse_path;
+ return -errno;
+ }
+ LOG(INFO) << "Bind mounting to " << absolute_lower_path;
+ return BindMount(absolute_lower_path, pass_through_path);
+}
+
+status_t UnmountUserFuse(const std::string& pass_through_path, const std::string& fuse_path) {
+ // Best effort unmount pass_through path
+ sSleepOnUnmount = false;
+ ForceUnmount(pass_through_path);
+ android::status_t result = ForceUnmount(fuse_path);
+ sSleepOnUnmount = true;
+ if (result != android::OK) {
+ // TODO(b/135341433): MNT_DETACH is needed for fuse because umount2 can fail with EBUSY.
+ // Figure out why we get EBUSY and remove this special casing if possible.
+ PLOG(ERROR) << "Failed to unmount. Trying MNT_DETACH " << fuse_path << " ...";
+ if (umount2(fuse_path.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH) && errno != EINVAL &&
+ errno != ENOENT) {
+ PLOG(ERROR) << "Failed to unmount with MNT_DETACH " << fuse_path;
+ return -errno;
+ }
+ return android::OK;
+ }
+ return result;
+}
+
} // namespace vold
} // namespace android
diff --git a/Utils.h b/Utils.h
index af4e401..375e175 100644
--- a/Utils.h
+++ b/Utils.h
@@ -20,6 +20,7 @@
#include "KeyBuffer.h"
#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
#include <cutils/multiuser.h>
#include <selinux/selinux.h>
#include <utils/Errors.h>
@@ -33,6 +34,8 @@
namespace android {
namespace vold {
+static const char* kPropFuseSnapshot = "sys.fuse_snapshot";
+
/* SELinux contexts used depending on the block device type */
extern security_context_t sBlkidContext;
extern security_context_t sBlkidUntrustedContext;
@@ -147,6 +150,12 @@
bool FsyncDirectory(const std::string& dirname);
bool writeStringToFile(const std::string& payload, const std::string& filename);
+
+status_t MountUserFuse(userid_t user_id, const std::string& absolute_lower_path,
+ const std::string& relative_upper_path, android::base::unique_fd* fuse_fd);
+
+status_t UnmountUserFuse(const std::string& pass_through_path, const std::string& fuse_path);
+
} // namespace vold
} // namespace android
diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp
index cc32820..b82ea06 100644
--- a/VoldNativeService.cpp
+++ b/VoldNativeService.cpp
@@ -108,7 +108,7 @@
return exception(binder::Status::EX_ILLEGAL_ARGUMENT, "Missing ID");
}
for (const char& c : id) {
- if (!std::isalnum(c) && c != ':' && c != ',') {
+ if (!std::isalnum(c) && c != ':' && c != ',' && c != ';') {
return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
StringPrintf("ID %s is malformed", id.c_str()));
}
@@ -326,8 +326,9 @@
return translate(VolumeManager::Instance()->forgetPartition(partGuid, fsUuid));
}
-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,
+ const android::sp<android::os::IVoldMountCallback>& callback) {
ENFORCE_SYSTEM_OR_ROOT;
CHECK_ARGUMENT_ID(volId);
ACQUIRE_LOCK;
@@ -340,10 +341,14 @@
vol->setMountFlags(mountFlags);
vol->setMountUserId(mountUserId);
+ vol->setMountCallback(callback);
int res = vol->mount();
+ vol->setMountCallback(nullptr);
+
if (res != OK) {
return translate(res);
}
+
if ((mountFlags & MOUNT_FLAG_PRIMARY) != 0) {
res = VolumeManager::Instance()->setPrimary(vol);
if (res != OK) {
diff --git a/VoldNativeService.h b/VoldNativeService.h
index 0718263..ebd9041 100644
--- a/VoldNativeService.h
+++ b/VoldNativeService.h
@@ -52,7 +52,8 @@
binder::Status partition(const std::string& diskId, int32_t partitionType, int32_t ratio);
binder::Status forgetPartition(const std::string& partGuid, const std::string& fsUuid);
- binder::Status mount(const std::string& volId, int32_t mountFlags, int32_t mountUserId);
+ binder::Status mount(const std::string& volId, int32_t mountFlags, int32_t mountUserId,
+ const android::sp<android::os::IVoldMountCallback>& callback);
binder::Status unmount(const std::string& volId);
binder::Status format(const std::string& volId, const std::string& fsType);
binder::Status benchmark(const std::string& volId,
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index 44bff5a..f1cd232 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -40,6 +40,7 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <async_safe/log.h>
#include <cutils/fs.h>
#include <utils/Trace.h>
@@ -67,6 +68,7 @@
#include "fs/Vfat.h"
#include "model/EmulatedVolume.h"
#include "model/ObbVolume.h"
+#include "model/PrivateVolume.h"
#include "model/StubVolume.h"
using android::OK;
@@ -79,10 +81,12 @@
using android::vold::CreateDir;
using android::vold::DeleteDirContents;
using android::vold::DeleteDirContentsAndDir;
+using android::vold::PrivateVolume;
using android::vold::Symlink;
using android::vold::Unlink;
using android::vold::UnmountTree;
using android::vold::VoldNativeService;
+using android::vold::VolumeBase;
static const char* kPathUserMount = "/mnt/user";
static const char* kPathVirtualDisk = "/data/misc/vold/virtual_disk";
@@ -174,10 +178,13 @@
// Assume that we always have an emulated volume on internal
// storage; the framework will decide if it should be mounted.
- CHECK(mInternalEmulated == nullptr);
- mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>(
- new android::vold::EmulatedVolume("/data/media"));
- mInternalEmulated->create();
+ CHECK(mInternalEmulatedVolumes.empty());
+
+ auto vol = std::shared_ptr<android::vold::VolumeBase>(
+ new android::vold::EmulatedVolume("/data/media", 0));
+ vol->setMountUserId(0);
+ vol->create();
+ mInternalEmulatedVolumes.push_back(vol);
// Consider creating a virtual disk
updateVirtualDisk();
@@ -186,9 +193,12 @@
}
int VolumeManager::stop() {
- CHECK(mInternalEmulated != nullptr);
- mInternalEmulated->destroy();
- mInternalEmulated = nullptr;
+ CHECK(!mInternalEmulatedVolumes.empty());
+ for (const auto& vol : mInternalEmulatedVolumes) {
+ vol->destroy();
+ }
+ mInternalEmulatedVolumes.clear();
+
return 0;
}
@@ -310,11 +320,10 @@
}
std::shared_ptr<android::vold::VolumeBase> VolumeManager::findVolume(const std::string& id) {
- // Vold could receive "mount" after "shutdown" command in the extreme case.
- // If this happens, mInternalEmulated will equal nullptr and
- // we need to deal with it in order to avoid null pointer crash.
- if (mInternalEmulated != nullptr && mInternalEmulated->getId() == id) {
- return mInternalEmulated;
+ for (const auto& vol : mInternalEmulatedVolumes) {
+ if (vol->getId() == id) {
+ return vol;
+ }
}
for (const auto& disk : mDisks) {
auto vol = disk->findVolume(id);
@@ -365,45 +374,123 @@
}
int VolumeManager::linkPrimary(userid_t userId) {
- std::string source(mPrimary->getPath());
- if (mPrimary->isEmulated()) {
- source = StringPrintf("%s/%d", source.c_str(), userId);
- fs_prepare_dir(source.c_str(), 0755, AID_ROOT, AID_ROOT);
- }
+ if (!GetBoolProperty(android::vold::kPropFuseSnapshot, false)) {
+ std::string source(mPrimary->getPath());
+ if (mPrimary->isEmulated()) {
+ source = StringPrintf("%s/%d", source.c_str(), userId);
+ fs_prepare_dir(source.c_str(), 0755, AID_ROOT, AID_ROOT);
+ }
- std::string target(StringPrintf("/mnt/user/%d/primary", userId));
- LOG(DEBUG) << "Linking " << source << " to " << target;
- Symlink(source, target);
+ std::string target(StringPrintf("/mnt/user/%d/primary", userId));
+ LOG(DEBUG) << "Linking " << source << " to " << target;
+ Symlink(source, target);
+ }
return 0;
}
+void VolumeManager::destroyEmulatedVolumesForUser(userid_t userId) {
+ // Destroy and remove all unstacked EmulatedVolumes for the user
+ auto i = mInternalEmulatedVolumes.begin();
+ while (i != mInternalEmulatedVolumes.end()) {
+ auto vol = *i;
+ if (vol->getMountUserId() == userId) {
+ vol->destroy();
+ i = mInternalEmulatedVolumes.erase(i);
+ } else {
+ i++;
+ }
+ }
+
+ // Destroy and remove all stacked EmulatedVolumes for the user on each mounted private volume
+ std::list<std::string> private_vols;
+ listVolumes(VolumeBase::Type::kPrivate, private_vols);
+ for (const std::string& id : private_vols) {
+ PrivateVolume* pvol = static_cast<PrivateVolume*>(findVolume(id).get());
+ std::list<std::shared_ptr<VolumeBase>> vols_to_remove;
+ if (pvol->getState() == VolumeBase::State::kMounted) {
+ for (const auto& vol : pvol->getVolumes()) {
+ if (vol->getMountUserId() == userId) {
+ vols_to_remove.push_back(vol);
+ }
+ }
+ for (const auto& vol : vols_to_remove) {
+ vol->destroy();
+ pvol->removeVolume(vol);
+ }
+ } // else EmulatedVolumes will be destroyed on VolumeBase#unmount
+ }
+}
+
+void VolumeManager::createEmulatedVolumesForUser(userid_t userId) {
+ // Create unstacked EmulatedVolumes for the user
+ auto vol = std::shared_ptr<android::vold::VolumeBase>(
+ new android::vold::EmulatedVolume("/data/media", userId));
+ vol->setMountUserId(userId);
+ mInternalEmulatedVolumes.push_back(vol);
+ vol->create();
+
+ // Create stacked EmulatedVolumes for the user on each PrivateVolume
+ std::list<std::string> private_vols;
+ listVolumes(VolumeBase::Type::kPrivate, private_vols);
+ for (const std::string& id : private_vols) {
+ PrivateVolume* pvol = static_cast<PrivateVolume*>(findVolume(id).get());
+ if (pvol->getState() == VolumeBase::State::kMounted) {
+ auto evol =
+ std::shared_ptr<android::vold::VolumeBase>(new android::vold::EmulatedVolume(
+ pvol->getPath() + "/media", pvol->getRawDevice(), pvol->getFsUuid(),
+ userId));
+ evol->setMountUserId(userId);
+ pvol->addVolume(evol);
+ evol->create();
+ } // else EmulatedVolumes will be created per user when on PrivateVolume#doMount
+ }
+}
+
int VolumeManager::onUserAdded(userid_t userId, int userSerialNumber) {
+ LOG(INFO) << "onUserAdded: " << userId;
+
mAddedUsers[userId] = userSerialNumber;
return 0;
}
int VolumeManager::onUserRemoved(userid_t userId) {
+ LOG(INFO) << "onUserRemoved: " << userId;
+
+ onUserStopped(userId);
mAddedUsers.erase(userId);
return 0;
}
int VolumeManager::onUserStarted(userid_t userId) {
- LOG(VERBOSE) << "onUserStarted: " << userId;
- // Note that sometimes the system will spin up processes from Zygote
- // before actually starting the user, so we're okay if Zygote
- // already created this directory.
- std::string path(StringPrintf("%s/%d", kPathUserMount, userId));
- fs_prepare_dir(path.c_str(), 0755, AID_ROOT, AID_ROOT);
+ LOG(INFO) << "onUserStarted: " << userId;
+
+ if (mStartedUsers.find(userId) == mStartedUsers.end()) {
+ createEmulatedVolumesForUser(userId);
+ }
+
+ if (!GetBoolProperty(android::vold::kPropFuseSnapshot, false)) {
+ // Note that sometimes the system will spin up processes from Zygote
+ // before actually starting the user, so we're okay if Zygote
+ // already created this directory.
+ std::string path(StringPrintf("%s/%d", kPathUserMount, userId));
+ fs_prepare_dir(path.c_str(), 0755, AID_ROOT, AID_ROOT);
+
+ if (mPrimary) {
+ linkPrimary(userId);
+ }
+ }
mStartedUsers.insert(userId);
- if (mPrimary) {
- linkPrimary(userId);
- }
return 0;
}
int VolumeManager::onUserStopped(userid_t userId) {
LOG(VERBOSE) << "onUserStopped: " << userId;
+
+ if (mStartedUsers.find(userId) != mStartedUsers.end()) {
+ destroyEmulatedVolumesForUser(userId);
+ }
+
mStartedUsers.erase(userId);
return 0;
}
@@ -430,7 +517,56 @@
return 0;
}
+// This code is executed after a fork so it's very important that the set of
+// methods we call here is strictly limited.
+//
+// TODO: Get rid of this guesswork altogether and instead exec a process
+// immediately after fork to do our bindding for us.
+static bool childProcess(const char* storageSource, const char* userSource, int nsFd,
+ struct dirent* de) {
+ if (setns(nsFd, CLONE_NEWNS) != 0) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to setns for %s :%s", de->d_name,
+ strerror(errno));
+ return false;
+ }
+
+ // NOTE: Inlined from vold::UnmountTree here to avoid using PLOG methods and
+ // to also protect against future changes that may cause issues across a
+ // fork.
+ if (TEMP_FAILURE_RETRY(umount2("/storage/", MNT_DETACH)) < 0 && errno != EINVAL &&
+ errno != ENOENT) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to unmount /storage/ :%s",
+ strerror(errno));
+ return false;
+ }
+
+ if (TEMP_FAILURE_RETRY(mount(storageSource, "/storage", NULL, MS_BIND | MS_REC, NULL)) == -1) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to mount %s for %s :%s",
+ storageSource, de->d_name, strerror(errno));
+ return false;
+ }
+
+ if (TEMP_FAILURE_RETRY(mount(NULL, "/storage", NULL, MS_REC | MS_SLAVE, NULL)) == -1) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "vold",
+ "Failed to set MS_SLAVE to /storage for %s :%s", de->d_name,
+ strerror(errno));
+ return false;
+ }
+
+ if (TEMP_FAILURE_RETRY(mount(userSource, "/storage/self", NULL, MS_BIND, NULL)) == -1) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to mount %s for %s :%s",
+ userSource, de->d_name, strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
int VolumeManager::remountUid(uid_t uid, int32_t mountMode) {
+ if (GetBoolProperty(android::vold::kPropFuseSnapshot, false)) {
+ // TODO(135341433): Implement fuse specific logic.
+ return 0;
+ }
std::string mode;
switch (mountMode) {
case VoldNativeService::REMOUNT_MODE_NONE:
@@ -450,6 +586,9 @@
case VoldNativeService::REMOUNT_MODE_FULL:
mode = "full";
break;
+ case VoldNativeService::REMOUNT_MODE_PASS_THROUGH:
+ mode = "pass_through";
+ break;
default:
PLOG(ERROR) << "Unknown mode " << std::to_string(mountMode);
return -1;
@@ -464,6 +603,8 @@
int nsFd;
struct stat sb;
pid_t child;
+ std::string userSource;
+ std::string storageSource;
static bool apexUpdatable = android::sysprop::ApexProperties::updatable().value_or(false);
@@ -539,47 +680,28 @@
goto next;
}
+ if (mode == "default") {
+ storageSource = "/mnt/runtime/default";
+ } else if (mode == "read") {
+ storageSource = "/mnt/runtime/read";
+ } else if (mode == "write") {
+ storageSource = "/mnt/runtime/write";
+ } else if (mode == "full") {
+ storageSource = "/mnt/runtime/full";
+ } else {
+ // Sane default of no storage visible. No need to fork a child
+ // to remount uid.
+ goto next;
+ }
+
+ // Mount user-specific symlink helper into place
+ userSource = StringPrintf("/mnt/user/%d", multiuser_get_user_id(uid));
if (!(child = fork())) {
- if (setns(nsFd, CLONE_NEWNS) != 0) {
- PLOG(ERROR) << "Failed to setns for " << de->d_name;
- _exit(1);
- }
-
- android::vold::UnmountTree("/storage/");
-
- std::string storageSource;
- if (mode == "default") {
- storageSource = "/mnt/runtime/default";
- } else if (mode == "read") {
- storageSource = "/mnt/runtime/read";
- } else if (mode == "write") {
- storageSource = "/mnt/runtime/write";
- } else if (mode == "full") {
- storageSource = "/mnt/runtime/full";
- } else {
- // Sane default of no storage visible
+ if (childProcess(storageSource.c_str(), userSource.c_str(), nsFd, de)) {
_exit(0);
- }
- if (TEMP_FAILURE_RETRY(
- mount(storageSource.c_str(), "/storage", NULL, MS_BIND | MS_REC, NULL)) == -1) {
- PLOG(ERROR) << "Failed to mount " << storageSource << " for " << de->d_name;
+ } else {
_exit(1);
}
- if (TEMP_FAILURE_RETRY(mount(NULL, "/storage", NULL, MS_REC | MS_SLAVE, NULL)) == -1) {
- PLOG(ERROR) << "Failed to set MS_SLAVE to /storage for " << de->d_name;
- _exit(1);
- }
-
- // Mount user-specific symlink helper into place
- userid_t user_id = multiuser_get_user_id(uid);
- std::string userSource(StringPrintf("/mnt/user/%d", user_id));
- if (TEMP_FAILURE_RETRY(
- mount(userSource.c_str(), "/storage/self", NULL, MS_BIND, NULL)) == -1) {
- PLOG(ERROR) << "Failed to mount " << userSource << " for " << de->d_name;
- _exit(1);
- }
-
- _exit(0);
}
if (child == -1) {
@@ -600,10 +722,11 @@
int VolumeManager::reset() {
// Tear down all existing disks/volumes and start from a blank slate so
// newly connected framework hears all events.
- if (mInternalEmulated != nullptr) {
- mInternalEmulated->destroy();
- mInternalEmulated->create();
+ for (const auto& vol : mInternalEmulatedVolumes) {
+ vol->destroy();
}
+ mInternalEmulatedVolumes.clear();
+
for (const auto& disk : mDisks) {
disk->destroy();
disk->create();
@@ -616,15 +739,18 @@
// Can be called twice (sequentially) during shutdown. should be safe for that.
int VolumeManager::shutdown() {
- if (mInternalEmulated == nullptr) {
+ if (mInternalEmulatedVolumes.empty()) {
return 0; // already shutdown
}
android::vold::sSleepOnUnmount = false;
- mInternalEmulated->destroy();
- mInternalEmulated = nullptr;
+ for (const auto& vol : mInternalEmulatedVolumes) {
+ vol->destroy();
+ }
for (const auto& disk : mDisks) {
disk->destroy();
}
+
+ mInternalEmulatedVolumes.clear();
mStubVolumes.clear();
mDisks.clear();
mPendingDisks.clear();
@@ -637,8 +763,8 @@
ATRACE_NAME("VolumeManager::unmountAll()");
// First, try gracefully unmounting all known devices
- if (mInternalEmulated != nullptr) {
- mInternalEmulated->unmount();
+ for (const auto& vol : mInternalEmulatedVolumes) {
+ vol->unmount();
}
for (const auto& stub : mStubVolumes) {
stub->unmount();
diff --git a/VolumeManager.h b/VolumeManager.h
index 9bf7599..aff5aaf 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -84,6 +84,8 @@
void listVolumes(android::vold::VolumeBase::Type type, std::list<std::string>& list) const;
+ const std::unordered_set<userid_t>& getStartedUsers() const { return mStartedUsers; }
+
int forgetPartition(const std::string& partGuid, const std::string& fsUuid);
int onUserAdded(userid_t userId, int userSerialNumber);
@@ -137,6 +139,9 @@
int linkPrimary(userid_t userId);
+ void createEmulatedVolumesForUser(userid_t userId);
+ void destroyEmulatedVolumesForUser(userid_t userId);
+
void handleDiskAdded(const std::shared_ptr<android::vold::Disk>& disk);
void handleDiskChanged(dev_t device);
void handleDiskRemoved(dev_t device);
@@ -151,13 +156,13 @@
std::list<std::shared_ptr<android::vold::Disk>> mPendingDisks;
std::list<std::shared_ptr<android::vold::VolumeBase>> mObbVolumes;
std::list<std::shared_ptr<android::vold::VolumeBase>> mStubVolumes;
+ std::list<std::shared_ptr<android::vold::VolumeBase>> mInternalEmulatedVolumes;
std::unordered_map<userid_t, int> mAddedUsers;
std::unordered_set<userid_t> mStartedUsers;
std::string mVirtualDiskPath;
std::shared_ptr<android::vold::Disk> mVirtualDisk;
- std::shared_ptr<android::vold::VolumeBase> mInternalEmulated;
std::shared_ptr<android::vold::VolumeBase> mPrimary;
int mNextObbId;
diff --git a/binder/android/os/IVold.aidl b/binder/android/os/IVold.aidl
index 681e9dc..975d94c 100644
--- a/binder/android/os/IVold.aidl
+++ b/binder/android/os/IVold.aidl
@@ -18,6 +18,7 @@
import android.os.incremental.IncrementalFileSystemControlParcel;
import android.os.IVoldListener;
+import android.os.IVoldMountCallback;
import android.os.IVoldTaskListener;
/** {@hide} */
@@ -41,7 +42,8 @@
void partition(@utf8InCpp String diskId, int partitionType, int ratio);
void forgetPartition(@utf8InCpp String partGuid, @utf8InCpp String fsUuid);
- void mount(@utf8InCpp String volId, int mountFlags, int mountUserId);
+ void mount(@utf8InCpp String volId, int mountFlags, int mountUserId,
+ @nullable IVoldMountCallback callback);
void unmount(@utf8InCpp String volId);
void format(@utf8InCpp String volId, @utf8InCpp String fsType);
void benchmark(@utf8InCpp String volId, IVoldTaskListener listener);
@@ -164,6 +166,7 @@
const int REMOUNT_MODE_LEGACY = 4;
const int REMOUNT_MODE_INSTALLER = 5;
const int REMOUNT_MODE_FULL = 6;
+ const int REMOUNT_MODE_PASS_THROUGH = 7;
const int VOLUME_STATE_UNMOUNTED = 0;
const int VOLUME_STATE_CHECKING = 1;
diff --git a/binder/android/os/IVoldListener.aidl b/binder/android/os/IVoldListener.aidl
index 0dcfc04..b3e4ba5 100644
--- a/binder/android/os/IVoldListener.aidl
+++ b/binder/android/os/IVoldListener.aidl
@@ -25,7 +25,7 @@
void onDiskDestroyed(@utf8InCpp String diskId);
void onVolumeCreated(@utf8InCpp String volId,
- int type, @utf8InCpp String diskId, @utf8InCpp String partGuid);
+ int type, @utf8InCpp String diskId, @utf8InCpp String partGuid, int userId);
void onVolumeStateChanged(@utf8InCpp String volId, int state);
void onVolumeMetadataChanged(@utf8InCpp String volId,
@utf8InCpp String fsType, @utf8InCpp String fsUuid, @utf8InCpp String fsLabel);
diff --git a/binder/android/os/IVoldMountCallback.aidl b/binder/android/os/IVoldMountCallback.aidl
new file mode 100644
index 0000000..6bf46d7
--- /dev/null
+++ b/binder/android/os/IVoldMountCallback.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+package android.os;
+
+/** {@hide} */
+interface IVoldMountCallback {
+ boolean onVolumeChecking(FileDescriptor fuseFd, @utf8InCpp String path,
+ @utf8InCpp String internalPath);
+}
diff --git a/model/EmulatedVolume.cpp b/model/EmulatedVolume.cpp
index 552fe2f..eb6b8a3 100644
--- a/model/EmulatedVolume.cpp
+++ b/model/EmulatedVolume.cpp
@@ -15,10 +15,13 @@
*/
#include "EmulatedVolume.h"
+
+#include "AppFuseUtil.h"
#include "Utils.h"
#include "VolumeManager.h"
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <cutils/fs.h>
#include <private/android_filesystem_config.h>
@@ -39,16 +42,17 @@
static const char* kFusePath = "/system/bin/sdcard";
-EmulatedVolume::EmulatedVolume(const std::string& rawPath)
+EmulatedVolume::EmulatedVolume(const std::string& rawPath, int userId)
: VolumeBase(Type::kEmulated), mFusePid(0) {
- setId("emulated");
+ setId(StringPrintf("emulated;%u", userId));
mRawPath = rawPath;
mLabel = "emulated";
}
-EmulatedVolume::EmulatedVolume(const std::string& rawPath, dev_t device, const std::string& fsUuid)
+EmulatedVolume::EmulatedVolume(const std::string& rawPath, dev_t device, const std::string& fsUuid,
+ int userId)
: VolumeBase(Type::kEmulated), mFusePid(0) {
- setId(StringPrintf("emulated:%u,%u", major(device), minor(device)));
+ setId(StringPrintf("emulated:%u,%u;%u", major(device), minor(device), userId));
mRawPath = rawPath;
mLabel = fsUuid;
}
@@ -81,6 +85,37 @@
dev_t before = GetDevice(mFuseFull);
+ bool isFuse = base::GetBoolProperty(kPropFuseSnapshot, false);
+
+ if (isFuse) {
+ LOG(INFO) << "Mounting emulated fuse volume";
+ android::base::unique_fd fd;
+ int user_id = getMountUserId();
+ int result = MountUserFuse(user_id, getInternalPath(), label, &fd);
+
+ if (result != 0) {
+ PLOG(ERROR) << "Failed to mount emulated fuse volume";
+ return -result;
+ }
+
+ auto callback = getMountCallback();
+ if (callback) {
+ bool is_ready = false;
+ callback->onVolumeChecking(std::move(fd), getPath(), getInternalPath(), &is_ready);
+ if (!is_ready) {
+ return -EIO;
+ }
+ }
+
+ return OK;
+ } else if (getMountUserId() != 0) {
+ // For sdcardfs, only mount for user 0, since user 0 will always be running
+ // and the paths don't change for different users. Trying to double mount
+ // will cause sepolicy to scream since sdcardfs prevents 'mounton'
+ return OK;
+ }
+
+ LOG(INFO) << "Executing sdcardfs";
if (!(mFusePid = fork())) {
// clang-format off
if (execl(kFusePath, kFusePath,
@@ -131,6 +166,33 @@
// ENOTCONN until the unmount completes. This is an exotic and unusual
// error code and might cause broken behaviour in applications.
KillProcessesUsingPath(getPath());
+
+ bool isFuse = base::GetBoolProperty(kPropFuseSnapshot, false);
+ if (isFuse) {
+ // We could have migrated storage to an adopted private volume, so always
+ // call primary storage "emulated" to avoid media rescans.
+ std::string label = mLabel;
+ if (getMountFlags() & MountFlags::kPrimary) {
+ label = "emulated";
+ }
+
+ std::string fuse_path(StringPrintf("/mnt/user/%d/%s", getMountUserId(), label.c_str()));
+ std::string pass_through_path(
+ StringPrintf("/mnt/pass_through/%d/%s", getMountUserId(), label.c_str()));
+ if (UnmountUserFuse(pass_through_path, fuse_path) != OK) {
+ PLOG(INFO) << "UnmountUserFuse failed on emulated fuse volume";
+ return -errno;
+ }
+
+ rmdir(fuse_path.c_str());
+ rmdir(pass_through_path.c_str());
+ return OK;
+ } else if (getMountUserId() != 0) {
+ // For sdcardfs, only unmount for user 0, since user 0 will always be running
+ // and the paths don't change for different users.
+ return OK;
+ }
+
ForceUnmount(mFuseDefault);
ForceUnmount(mFuseRead);
ForceUnmount(mFuseWrite);
diff --git a/model/EmulatedVolume.h b/model/EmulatedVolume.h
index fddfe4e..8d4c490 100644
--- a/model/EmulatedVolume.h
+++ b/model/EmulatedVolume.h
@@ -37,8 +37,8 @@
*/
class EmulatedVolume : public VolumeBase {
public:
- explicit EmulatedVolume(const std::string& rawPath);
- EmulatedVolume(const std::string& rawPath, dev_t device, const std::string& fsUuid);
+ explicit EmulatedVolume(const std::string& rawPath, int userId);
+ EmulatedVolume(const std::string& rawPath, dev_t device, const std::string& fsUuid, int userId);
virtual ~EmulatedVolume();
protected:
diff --git a/model/PrivateVolume.cpp b/model/PrivateVolume.cpp
index de2a09f..5098e5d 100644
--- a/model/PrivateVolume.cpp
+++ b/model/PrivateVolume.cpp
@@ -155,12 +155,18 @@
return -EIO;
}
- // Create a new emulated volume stacked above us, it will automatically
- // be destroyed during unmount
+ auto vol_manager = VolumeManager::Instance();
std::string mediaPath(mPath + "/media");
- auto vol = std::shared_ptr<VolumeBase>(new EmulatedVolume(mediaPath, mRawDevice, mFsUuid));
- addVolume(vol);
- vol->create();
+
+ // Create a new emulated volume stacked above us for all added users, they will automatically
+ // be destroyed during unmount
+ for (userid_t user : vol_manager->getStartedUsers()) {
+ auto vol = std::shared_ptr<VolumeBase>(
+ new EmulatedVolume(mediaPath, mRawDevice, mFsUuid, user));
+ vol->setMountUserId(user);
+ addVolume(vol);
+ vol->create();
+ }
return OK;
}
diff --git a/model/PrivateVolume.h b/model/PrivateVolume.h
index cb8e75d..656172f 100644
--- a/model/PrivateVolume.h
+++ b/model/PrivateVolume.h
@@ -42,6 +42,8 @@
const std::string& getFsType() const { return mFsType; };
const std::string& getRawDevPath() const { return mRawDevPath; };
const std::string& getRawDmDevPath() const { return mDmDevPath; };
+ const std::string& getFsUuid() const { return mFsUuid; };
+ dev_t getRawDevice() const { return mRawDevice; };
protected:
status_t doCreate() override;
diff --git a/model/PublicVolume.cpp b/model/PublicVolume.cpp
index 0a6b351..d1b63a3 100644
--- a/model/PublicVolume.cpp
+++ b/model/PublicVolume.cpp
@@ -15,6 +15,8 @@
*/
#include "PublicVolume.h"
+
+#include "AppFuseUtil.h"
#include "Utils.h"
#include "VolumeManager.h"
#include "fs/Exfat.h"
@@ -167,6 +169,30 @@
dev_t before = GetDevice(mFuseFull);
+ bool isFuse = base::GetBoolProperty(kPropFuseSnapshot, false);
+ if (isFuse) {
+ LOG(INFO) << "Mounting public fuse volume";
+ android::base::unique_fd fd;
+ int user_id = getMountUserId();
+ int result = MountUserFuse(user_id, getInternalPath(), stableName, &fd);
+
+ if (result != 0) {
+ LOG(ERROR) << "Failed to mount public fuse volume";
+ return -result;
+ }
+
+ auto callback = getMountCallback();
+ if (callback) {
+ bool is_ready = false;
+ callback->onVolumeChecking(std::move(fd), getPath(), getInternalPath(), &is_ready);
+ if (!is_ready) {
+ return -EIO;
+ }
+ }
+
+ return OK;
+ }
+
if (!(mFusePid = fork())) {
if (getMountFlags() & MountFlags::kPrimary) {
// clang-format off
@@ -229,6 +255,32 @@
// error code and might cause broken behaviour in applications.
KillProcessesUsingPath(getPath());
+ bool isFuse = base::GetBoolProperty(kPropFuseSnapshot, false);
+ if (isFuse) {
+ // Use UUID as stable name, if available
+ std::string stableName = getId();
+ if (!mFsUuid.empty()) {
+ stableName = mFsUuid;
+ }
+
+ std::string fuse_path(
+ StringPrintf("/mnt/user/%d/%s", getMountUserId(), stableName.c_str()));
+ std::string pass_through_path(
+ StringPrintf("/mnt/pass_through/%d/%s", getMountUserId(), stableName.c_str()));
+ if (UnmountUserFuse(pass_through_path, fuse_path) != OK) {
+ PLOG(INFO) << "UnmountUserFuse failed on public fuse volume";
+ return -errno;
+ }
+ ForceUnmount(kAsecPath);
+ ForceUnmount(mRawPath);
+
+ rmdir(fuse_path.c_str());
+ rmdir(pass_through_path.c_str());
+ rmdir(mRawPath.c_str());
+ mRawPath.clear();
+ return OK;
+ }
+
ForceUnmount(kAsecPath);
ForceUnmount(mFuseDefault);
diff --git a/model/VolumeBase.cpp b/model/VolumeBase.cpp
index ffc7900..636c065 100644
--- a/model/VolumeBase.cpp
+++ b/model/VolumeBase.cpp
@@ -143,6 +143,16 @@
return OK;
}
+status_t VolumeBase::setMountCallback(
+ const android::sp<android::os::IVoldMountCallback>& callback) {
+ mMountCallback = callback;
+ return OK;
+}
+
+sp<android::os::IVoldMountCallback> VolumeBase::getMountCallback() const {
+ return mMountCallback;
+}
+
android::sp<android::os::IVoldListener> VolumeBase::getListener() const {
if (mSilent) {
return nullptr;
@@ -176,7 +186,8 @@
auto listener = getListener();
if (listener) {
- listener->onVolumeCreated(getId(), static_cast<int32_t>(mType), mDiskId, mPartGuid);
+ listener->onVolumeCreated(getId(), static_cast<int32_t>(mType), mDiskId, mPartGuid,
+ mMountUserId);
}
setState(State::kUnmounted);
diff --git a/model/VolumeBase.h b/model/VolumeBase.h
index 53eeb6f..1d88d1b 100644
--- a/model/VolumeBase.h
+++ b/model/VolumeBase.h
@@ -19,6 +19,7 @@
#include "Utils.h"
#include "android/os/IVoldListener.h"
+#include "android/os/IVoldMountCallback.h"
#include <cutils/multiuser.h>
#include <utils/Errors.h>
@@ -87,11 +88,13 @@
State getState() const { return mState; }
const std::string& getPath() const { return mPath; }
const std::string& getInternalPath() const { return mInternalPath; }
+ const std::list<std::shared_ptr<VolumeBase>>& getVolumes() const { return mVolumes; }
status_t setDiskId(const std::string& diskId);
status_t setPartGuid(const std::string& partGuid);
status_t setMountFlags(int mountFlags);
status_t setMountUserId(userid_t mountUserId);
+ status_t setMountCallback(const android::sp<android::os::IVoldMountCallback>& callback);
status_t setSilent(bool silent);
void addVolume(const std::shared_ptr<VolumeBase>& volume);
@@ -123,6 +126,7 @@
status_t setInternalPath(const std::string& internalPath);
android::sp<android::os::IVoldListener> getListener() const;
+ android::sp<android::os::IVoldMountCallback> getMountCallback() const;
private:
/* ID that uniquely references volume while alive */
@@ -147,6 +151,7 @@
std::string mInternalPath;
/* Flag indicating that volume should emit no events */
bool mSilent;
+ android::sp<android::os::IVoldMountCallback> mMountCallback;
/* Volumes stacked on top of this volume */
std::list<std::shared_ptr<VolumeBase>> mVolumes;