Merge "Mount direct boot apps obb dir after fuse is ready."
diff --git a/Process.cpp b/Process.cpp
index 3d8e3d7..277d6a3 100644
--- a/Process.cpp
+++ b/Process.cpp
@@ -29,6 +29,7 @@
#include <unistd.h>
#include <fstream>
+#include <mntent.h>
#include <unordered_set>
#include <android-base/file.h>
@@ -81,6 +82,51 @@
return false;
}
+// TODO: Refactor the code with KillProcessesWithOpenFiles().
+int KillProcessesWithMounts(const std::string& prefix, int signal) {
+ std::unordered_set<pid_t> pids;
+
+ auto proc_d = std::unique_ptr<DIR, int (*)(DIR*)>(opendir("/proc"), closedir);
+ if (!proc_d) {
+ PLOG(ERROR) << "Failed to open proc";
+ return -1;
+ }
+
+ struct dirent* proc_de;
+ while ((proc_de = readdir(proc_d.get())) != nullptr) {
+ // We only care about valid PIDs
+ pid_t pid;
+ if (proc_de->d_type != DT_DIR) continue;
+ if (!android::base::ParseInt(proc_de->d_name, &pid)) continue;
+
+ // Look for references to prefix
+ std::string mounts_file(StringPrintf("/proc/%d/mounts", pid));
+ auto fp = std::unique_ptr<FILE, int (*)(FILE*)>(
+ setmntent(mounts_file.c_str(), "r"), endmntent);
+ if (!fp) {
+ PLOG(WARNING) << "Failed to open " << mounts_file;
+ continue;
+ }
+
+ // Check if obb directory is mounted, and get all packages of mounted app data directory.
+ mntent* mentry;
+ while ((mentry = getmntent(fp.get())) != nullptr) {
+ if (android::base::StartsWith(mentry->mnt_dir, prefix)) {
+ pids.insert(pid);
+ break;
+ }
+ }
+ }
+ if (signal != 0) {
+ for (const auto& pid : pids) {
+ LOG(WARNING) << "Killing pid "<< pid << " with signal " << strsignal(signal) <<
+ " because it has a mount with prefix " << prefix;
+ kill(pid, signal);
+ }
+ }
+ return pids.size();
+}
+
int KillProcessesWithOpenFiles(const std::string& prefix, int signal) {
std::unordered_set<pid_t> pids;
diff --git a/Process.h b/Process.h
index 1406782..1c59812 100644
--- a/Process.h
+++ b/Process.h
@@ -21,6 +21,7 @@
namespace vold {
int KillProcessesWithOpenFiles(const std::string& path, int signal);
+int KillProcessesWithMounts(const std::string& path, int signal);
} // namespace vold
} // namespace android
diff --git a/Utils.cpp b/Utils.cpp
index 9f9b357..ef9dba6 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -83,6 +83,9 @@
static const char* kAppMediaDir = "/Android/media/";
static const char* kAppObbDir = "/Android/obb/";
+static const char* kMediaProviderCtx = "u:r:mediaprovider:";
+static const char* kMediaProviderAppCtx = "u:r:mediaprovider_app:";
+
// Lock used to protect process-level SELinux changes from racing with each
// other between multiple threads.
static std::mutex kSecurityLock;
@@ -424,6 +427,31 @@
return -errno;
}
+status_t KillProcessesWithMountPrefix(const std::string& path) {
+ if (KillProcessesWithMounts(path, SIGINT) == 0) {
+ return OK;
+ }
+ if (sSleepOnUnmount) sleep(5);
+
+ if (KillProcessesWithMounts(path, SIGTERM) == 0) {
+ return OK;
+ }
+ if (sSleepOnUnmount) sleep(5);
+
+ if (KillProcessesWithMounts(path, SIGKILL) == 0) {
+ return OK;
+ }
+ if (sSleepOnUnmount) sleep(5);
+
+ // Send SIGKILL a second time to determine if we've
+ // actually killed everyone mount
+ if (KillProcessesWithMounts(path, SIGKILL) == 0) {
+ return OK;
+ }
+ PLOG(ERROR) << "Failed to kill processes using " << path;
+ return -EBUSY;
+}
+
status_t KillProcessesUsingPath(const std::string& path) {
if (KillProcessesWithOpenFiles(path, SIGINT) == 0) {
return OK;
@@ -881,6 +909,19 @@
}
}
+// TODO: Use a better way to determine if it's media provider app.
+bool IsFuseDaemon(const pid_t pid) {
+ auto path = StringPrintf("/proc/%d/mounts", pid);
+ char* tmp;
+ if (lgetfilecon(path.c_str(), &tmp) < 0) {
+ return false;
+ }
+ bool result = android::base::StartsWith(tmp, kMediaProviderAppCtx)
+ || android::base::StartsWith(tmp, kMediaProviderCtx);
+ freecon(tmp);
+ return result;
+}
+
bool IsFilesystemSupported(const std::string& fsType) {
std::string supported;
if (!ReadFileToString(kProcFilesystems, &supported)) {
@@ -1240,6 +1281,16 @@
return true;
}
+status_t EnsureDirExists(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
+ if (access(path.c_str(), F_OK) != 0) {
+ PLOG(WARNING) << "Dir does not exist: " << path;
+ if (fs_prepare_dir(path.c_str(), mode, uid, gid) != 0) {
+ return -errno;
+ }
+ }
+ return OK;
+}
+
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));
diff --git a/Utils.h b/Utils.h
index 21abc4d..5e6ff1b 100644
--- a/Utils.h
+++ b/Utils.h
@@ -35,6 +35,7 @@
namespace vold {
static const char* kPropFuse = "persist.sys.fuse";
+static const char* kVoldAppDataIsolationEnabled = "persist.sys.vold_app_data_isolation_enabled";
/* SELinux contexts used depending on the block device type */
extern security_context_t sBlkidContext;
@@ -69,6 +70,9 @@
/* Kills any processes using given path */
status_t KillProcessesUsingPath(const std::string& path);
+/* Kills any processes using given mount prifix */
+status_t KillProcessesWithMountPrefix(const std::string& path);
+
/* Creates bind mount from source to target */
status_t BindMount(const std::string& source, const std::string& target);
@@ -120,6 +124,7 @@
uint64_t GetTreeBytes(const std::string& path);
bool IsFilesystemSupported(const std::string& fsType);
+bool IsFuseDaemon(const pid_t pid);
/* Wipes contents of block device at given path */
status_t WipeBlockDevice(const std::string& path);
@@ -143,6 +148,8 @@
dev_t GetDevice(const std::string& path);
+status_t EnsureDirExists(const std::string& path, mode_t mode, uid_t uid, gid_t gid);
+
status_t RestoreconRecursive(const std::string& path);
// TODO: promote to android::base
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index f7b36bf..2b6565c 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -80,6 +80,7 @@
using android::vold::CreateDir;
using android::vold::DeleteDirContents;
using android::vold::DeleteDirContentsAndDir;
+using android::vold::EnsureDirExists;
using android::vold::IsFilesystemSupported;
using android::vold::PrepareAndroidDirs;
using android::vold::PrepareAppDirFromRoot;
@@ -104,6 +105,8 @@
static const unsigned int kMajorBlockExperimentalMin = 240;
static const unsigned int kMajorBlockExperimentalMax = 254;
+using ScanProcCallback = bool(*)(uid_t uid, pid_t pid, int nsFd, const char* name, void* params);
+
VolumeManager* VolumeManager::sInstance = NULL;
VolumeManager* VolumeManager::Instance() {
@@ -534,9 +537,9 @@
// 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) {
+ const char* name) {
if (setns(nsFd, CLONE_NEWNS) != 0) {
- async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to setns for %s :%s", de->d_name,
+ async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to setns for %s :%s", name,
strerror(errno));
return false;
}
@@ -553,59 +556,82 @@
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));
+ storageSource, 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,
+ "Failed to set MS_SLAVE to /storage for %s :%s", 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));
+ userSource, name, strerror(errno));
return false;
}
return true;
}
-int VolumeManager::remountUid(uid_t uid, int32_t mountMode) {
- if (GetBoolProperty(android::vold::kPropFuse, false)) {
- // TODO(135341433): Implement fuse specific logic.
- return 0;
- }
- std::string mode;
+// Fork the process and remount storage
+bool forkAndRemountChild(uid_t uid, pid_t pid, int nsFd, const char* name, void* params) {
+ int32_t mountMode = *static_cast<int32_t*>(params);
+ std::string userSource;
+ std::string storageSource;
+ pid_t child;
+ // Need to fix these paths to account for when sdcardfs is gone
switch (mountMode) {
case VoldNativeService::REMOUNT_MODE_NONE:
- mode = "none";
- break;
+ return true;
case VoldNativeService::REMOUNT_MODE_DEFAULT:
- mode = "default";
+ storageSource = "/mnt/runtime/default";
break;
case VoldNativeService::REMOUNT_MODE_READ:
- mode = "read";
+ storageSource = "/mnt/runtime/read";
break;
case VoldNativeService::REMOUNT_MODE_WRITE:
case VoldNativeService::REMOUNT_MODE_LEGACY:
case VoldNativeService::REMOUNT_MODE_INSTALLER:
- mode = "write";
+ storageSource = "/mnt/runtime/write";
break;
case VoldNativeService::REMOUNT_MODE_FULL:
- mode = "full";
+ storageSource = "/mnt/runtime/full";
break;
case VoldNativeService::REMOUNT_MODE_PASS_THROUGH:
- mode = "pass_through";
- break;
+ return true;
default:
PLOG(ERROR) << "Unknown mode " << std::to_string(mountMode);
- return -1;
+ return false;
}
- LOG(DEBUG) << "Remounting " << uid << " as mode " << mode;
+ LOG(DEBUG) << "Remounting " << uid << " as " << storageSource;
+ // Fork a child to mount user-specific symlink helper into place
+ userSource = StringPrintf("/mnt/user/%d", multiuser_get_user_id(uid));
+ if (!(child = fork())) {
+ if (childProcess(storageSource.c_str(), userSource.c_str(), nsFd, name)) {
+ _exit(0);
+ } else {
+ _exit(1);
+ }
+ }
+
+ if (child == -1) {
+ PLOG(ERROR) << "Failed to fork";
+ return false;
+ } else {
+ TEMP_FAILURE_RETRY(waitpid(child, nullptr, 0));
+ }
+ return true;
+}
+
+// Helper function to scan all processes in /proc and call the callback if:
+// 1). pid belongs to an app process
+// 2). If input uid is 0 or it matches the process uid
+// 3). If userId is not -1 or userId matches the process userId
+bool scanProcProcesses(uid_t uid, userid_t userId, ScanProcCallback callback, void* params) {
DIR* dir;
struct dirent* de;
std::string rootName;
@@ -613,24 +639,22 @@
int pidFd;
int nsFd;
struct stat sb;
- pid_t child;
- std::string userSource;
- std::string storageSource;
static bool apexUpdatable = android::sysprop::ApexProperties::updatable().value_or(false);
if (!(dir = opendir("/proc"))) {
- PLOG(ERROR) << "Failed to opendir";
- return -1;
+ async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to opendir");
+ return false;
}
// Figure out root namespace to compare against below
if (!android::vold::Readlinkat(dirfd(dir), "1/ns/mnt", &rootName)) {
- PLOG(ERROR) << "Failed to read root namespace";
+ async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to read root namespace");
closedir(dir);
- return -1;
+ return false;
}
+ async_safe_format_log(ANDROID_LOG_INFO, "vold", "Start scanning all processes");
// Poke through all running PIDs look for apps running as UID
while ((de = readdir(dir))) {
pid_t pid;
@@ -645,21 +669,23 @@
goto next;
}
if (fstat(pidFd, &sb) != 0) {
- PLOG(WARNING) << "Failed to stat " << de->d_name;
+ async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to stat %s", de->d_name);
goto next;
}
- if (sb.st_uid != uid) {
+ if (uid != 0 && sb.st_uid != uid) {
+ goto next;
+ }
+ if (userId != static_cast<userid_t>(-1) && multiuser_get_user_id(sb.st_uid) != userId) {
goto next;
}
// Matches so far, but refuse to touch if in root namespace
- LOG(DEBUG) << "Found matching PID " << de->d_name;
if (!android::vold::Readlinkat(pidFd, "ns/mnt", &pidName)) {
- PLOG(WARNING) << "Failed to read namespace for " << de->d_name;
+ async_safe_format_log(ANDROID_LOG_ERROR, "vold",
+ "Failed to read namespacefor %s", de->d_name);
goto next;
}
if (rootName == pidName) {
- LOG(WARNING) << "Skipping due to root namespace";
goto next;
}
@@ -674,11 +700,9 @@
// non-Java process whose UID is < AID_APP_START. (The UID condition
// is required to not filter out child processes spawned by apps.)
if (!android::vold::Readlinkat(pidFd, "exe", &exeName)) {
- PLOG(WARNING) << "Failed to read exe name for " << de->d_name;
goto next;
}
if (!StartsWith(exeName, "/system/bin/app_process") && sb.st_uid < AID_APP_START) {
- LOG(WARNING) << "Skipping due to native system process";
goto next;
}
}
@@ -687,39 +711,13 @@
// NOLINTNEXTLINE(android-cloexec-open): Deliberately not O_CLOEXEC
nsFd = openat(pidFd, "ns/mnt", O_RDONLY);
if (nsFd < 0) {
- PLOG(WARNING) << "Failed to open namespace for " << de->d_name;
+ async_safe_format_log(ANDROID_LOG_ERROR, "vold",
+ "Failed to open namespace for %s", de->d_name);
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 (childProcess(storageSource.c_str(), userSource.c_str(), nsFd, de)) {
- _exit(0);
- } else {
- _exit(1);
- }
- }
-
- if (child == -1) {
- PLOG(ERROR) << "Failed to fork";
- goto next;
- } else {
- TEMP_FAILURE_RETRY(waitpid(child, nullptr, 0));
+ if (!callback(sb.st_uid, pid, nsFd, de->d_name, params)) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed in callback");
}
next:
@@ -727,9 +725,204 @@
close(pidFd);
}
closedir(dir);
+ async_safe_format_log(ANDROID_LOG_INFO, "vold", "Finished scanning all processes");
+ return true;
+}
+
+int VolumeManager::remountUid(uid_t uid, int32_t mountMode) {
+ if (GetBoolProperty(android::vold::kPropFuse, false)) {
+ // TODO(135341433): Implement fuse specific logic.
+ return 0;
+ }
+ return scanProcProcesses(uid, static_cast<userid_t>(-1),
+ forkAndRemountChild, &mountMode) ? 0 : -1;
+}
+
+// Bind mount obb dir for an app if necessary.
+// How it works:
+// 1). Check if a pid is an app uid and not the FuseDaemon, if not then return.
+// 2). Get the mounts for that pid.
+// 3). If obb is already mounted then return, otherwise we need to mount obb for this pid.
+// 4). Get all packages and uid mounted for jit profile. These packages are all packages with
+// same uid or whitelisted apps.
+// 5a). If there's no package, it means it's not a process running app data isolation, so
+// just bind mount Android/obb dir.
+// 5b). Otherwise, for each package, create obb dir if it's not created and bind mount it.
+// TODO: Should we get some reliable data from system server instead of scanning /proc ?
+static bool bindMountAppObbDir(uid_t uid, pid_t pid, int nsFd, const char* name, void* params) {
+ if (uid < AID_APP_START || uid > AID_APP_END) {
+ return true;
+ }
+ if (android::vold::IsFuseDaemon(pid)) {
+ return true;
+ }
+ async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Start mounting obb for uid:%d, pid:%d", uid,
+ pid);
+
+ userid_t userId = multiuser_get_user_id(uid);
+ if (setns(nsFd, CLONE_NEWNS) != 0) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to setns %s", strerror(errno));
+ return false;
+ }
+
+ std::string profiles_path(StringPrintf("/data/misc/profiles/cur/%d/", userId));
+ // We search both .../obb and .../obb/$PKG paths here.
+ std::string obb_path(StringPrintf("/storage/emulated/%d/Android/obb", userId));
+ int profiles_path_len = profiles_path.length();
+ int obb_path_len = obb_path.length();
+
+ // TODO: Refactor the code as a util function so we can reuse the mount parsing code.
+ std::string mounts_file(StringPrintf("/proc/%d/mounts", pid));
+ auto fp = std::unique_ptr<FILE, int (*)(FILE*)>(
+ setmntent(mounts_file.c_str(), "r"), endmntent);
+ if (!fp) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Error opening %s: %s",
+ mounts_file.c_str(), strerror(errno));
+ return false;
+ }
+
+ // Check if obb directory is mounted, and get all packages of mounted app data directory.
+ bool obb_mounted = false;
+ std::vector<std::string> pkg_name_list;
+ mntent* mentry;
+ while ((mentry = getmntent(fp.get())) != nullptr) {
+ if (strncmp(mentry->mnt_dir, profiles_path.c_str(), profiles_path_len) == 0) {
+ pkg_name_list.push_back(std::string(mentry->mnt_dir + profiles_path_len));
+ }
+ if (strncmp(mentry->mnt_dir, obb_path.c_str(), obb_path_len) == 0) {
+ obb_mounted = true;
+ }
+ }
+
+ // Obb mounted in zygote already, so skip it
+ if (obb_mounted) {
+ return true;
+ }
+
+ // Ensure obb parent directory exists
+ std::string obbSource;
+ if (IsFilesystemSupported("sdcardfs")) {
+ obbSource = StringPrintf("/mnt/runtime/default/emulated/%d/Android/obb", userId);
+ } else {
+ obbSource = StringPrintf("/mnt/pass_through/%d/emulated/%d/Android/obb", userId, userId);
+ }
+ std::string obbTarget(StringPrintf("/storage/emulated/%d/Android/obb", userId));
+ auto status = EnsureDirExists(obbSource, 0771, AID_MEDIA_RW, AID_MEDIA_RW);
+ if (status != OK) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to create dir %s %s",
+ obbSource.c_str(), strerror(-status));
+ return false;
+ }
+
+ // It means app data isolation is not applied to this, so we can just bind the whole obb
+ // directory instead.
+ if (pkg_name_list.empty()) {
+ async_safe_format_log(ANDROID_LOG_INFO, "vold",
+ "Bind mounting whole obb directory for pid %d", pid);
+ status = BindMount(obbSource, obbTarget);
+ if (status != OK) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to mount %s %s %s",
+ obbSource.c_str(), obbTarget.c_str(), strerror(-status));
+ return false;
+ }
+ return true;
+ }
+
+ // Bind mount each app's obb directory
+ for (const auto& pkg_name : pkg_name_list) {
+ std::string appObbSource;
+ if (IsFilesystemSupported("sdcardfs")) {
+ appObbSource = StringPrintf("/mnt/runtime/default/emulated/%d/Android/obb/%s",
+ userId, pkg_name.c_str());
+ } else {
+ appObbSource = StringPrintf("/mnt/pass_through/%d/emulated/%d/Android/obb/%s",
+ userId, userId, pkg_name.c_str());
+ }
+ std::string appObbTarget(StringPrintf("/storage/emulated/%d/Android/obb/%s",
+ userId, pkg_name.c_str()));
+
+ status = EnsureDirExists(appObbSource, 0770, uid, AID_MEDIA_RW);
+ if (status != OK) {
+ async_safe_format_log(ANDROID_LOG_INFO, "vold", "Failed to ensure dir %s exists",
+ appObbSource.c_str());
+ continue;
+ }
+ async_safe_format_log(ANDROID_LOG_INFO, "vold",
+ "Bind mounting app obb directory(%s) for pid %d", pkg_name.c_str(),
+ pid);
+ status = BindMount(appObbSource, appObbTarget);
+ if (status != OK) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to mount %s %s %s",
+ obbSource.c_str(), obbTarget.c_str(), strerror(-status));
+ continue;
+ }
+ }
+ return true;
+}
+
+int VolumeManager::remountAppObb(userid_t userId) {
+ if (!GetBoolProperty(android::vold::kPropFuse, false)) {
+ return 0;
+ }
+ LOG(INFO) << "Start remounting app obb";
+ pid_t child;
+ if (!(child = fork())) {
+ // Child process
+ if (daemon(0, 0) == -1) {
+ PLOG(FATAL) << "Cannot create daemon";
+ }
+ // TODO(149548518): Refactor the code so minimize the work after fork to prevent deadlock.
+ if (scanProcProcesses(0, userId, bindMountAppObbDir, nullptr)) {
+ // As some forked zygote processes may not setuid and recognized as an app yet, sleep
+ // 3s and try again to catch 'em all.
+ usleep(3 * 1000 * 1000); // 3s
+ async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Retry remounting app obb");
+ scanProcProcesses(0, userId, bindMountAppObbDir, nullptr);
+ _exit(0);
+ } else {
+ _exit(1);
+ }
+ }
+ if (child == -1) {
+ PLOG(ERROR) << "Failed to fork";
+ return -1;
+ } else if (child == 0) {
+ // Parent
+ int stat_loc;
+ for (;;) {
+ if (waitpid(child, &stat_loc, 0) != -1 || errno != EINTR) {
+ break;
+ }
+ }
+ }
return 0;
}
+bool VolumeManager::updateFuseMountedProperty() {
+ if (mFuseMountedUsers.size() == 0) {
+ android::base::SetProperty("vold.fuse_running_users", "");
+ return true;
+ }
+ std::stringstream stream;
+ char const * sep = "";
+ for (const auto& userId : mFuseMountedUsers) {
+ stream << sep;
+ stream << userId;
+ sep = ", ";
+ }
+ return android::base::SetProperty("vold.fuse_running_users", stream.str());
+}
+
+bool VolumeManager::addFuseMountedUser(userid_t userId) {
+ mFuseMountedUsers.insert(userId);
+ return updateFuseMountedProperty();
+}
+
+bool VolumeManager::removeFuseMountedUser(userid_t userId) {
+ mFuseMountedUsers.erase(userId);
+ return updateFuseMountedProperty();
+}
+
int VolumeManager::reset() {
// Tear down all existing disks/volumes and start from a blank slate so
// newly connected framework hears all events.
@@ -745,6 +938,8 @@
updateVirtualDisk();
mAddedUsers.clear();
mStartedUsers.clear();
+ mFuseMountedUsers.clear();
+ updateFuseMountedProperty();
return 0;
}
@@ -764,6 +959,8 @@
mInternalEmulatedVolumes.clear();
mDisks.clear();
mPendingDisks.clear();
+ mFuseMountedUsers.clear();
+ updateFuseMountedProperty();
android::vold::sSleepOnUnmount = true;
return 0;
}
diff --git a/VolumeManager.h b/VolumeManager.h
index afea54e..a094eae 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -118,6 +118,10 @@
int setPrimary(const std::shared_ptr<android::vold::VolumeBase>& vol);
int remountUid(uid_t uid, int32_t remountMode);
+ int remountAppObb(userid_t userId);
+
+ bool addFuseMountedUser(userid_t userId);
+ bool removeFuseMountedUser(userid_t userId);
/* Reset all internal state, typically during framework boot */
int reset();
@@ -201,6 +205,8 @@
void handleDiskChanged(dev_t device);
void handleDiskRemoved(dev_t device);
+ bool updateFuseMountedProperty();
+
std::mutex mLock;
std::mutex mCryptLock;
@@ -224,6 +230,9 @@
int mNextObbId;
int mNextStubId;
bool mSecureKeyguardShowing;
+
+ // Set of all user id that fuse is ready to use.
+ std::unordered_set<userid_t> mFuseMountedUsers;
};
#endif
diff --git a/model/EmulatedVolume.cpp b/model/EmulatedVolume.cpp
index 8f8c87a..c2f92e4 100644
--- a/model/EmulatedVolume.cpp
+++ b/model/EmulatedVolume.cpp
@@ -49,6 +49,7 @@
mLabel = "emulated";
mFuseMounted = false;
mUseSdcardFs = IsFilesystemSupported("sdcardfs");
+ mAppDataIsolationEnabled = base::GetBoolProperty(kVoldAppDataIsolationEnabled, false);
}
EmulatedVolume::EmulatedVolume(const std::string& rawPath, dev_t device, const std::string& fsUuid,
@@ -59,6 +60,7 @@
mLabel = fsUuid;
mFuseMounted = false;
mUseSdcardFs = IsFilesystemSupported("sdcardfs");
+ mAppDataIsolationEnabled = base::GetBoolProperty(kVoldAppDataIsolationEnabled, false);
}
EmulatedVolume::~EmulatedVolume() {}
@@ -94,14 +96,19 @@
} else {
androidSource = StringPrintf("/%s/%d/Android", mRawPath.c_str(), userId);
}
- std::string androidTarget(
- StringPrintf("/mnt/user/%d/%s/%d/Android", userId, label.c_str(), userId));
- auto status = doFuseBindMount(androidSource, androidTarget);
+ status_t status = OK;
+ // When app data isolation is enabled, obb/ will be mounted per app, otherwise we should
+ // bind mount the whole Android/ to speed up reading.
+ if (!mAppDataIsolationEnabled) {
+ std::string androidTarget(
+ StringPrintf("/mnt/user/%d/%s/%d/Android", userId, label.c_str(), userId));
+ status = doFuseBindMount(androidSource, androidTarget);
+ }
+
if (status != OK) {
return status;
}
-
// Installers get the same view as all other apps, with the sole exception that the
// OBB dirs (Android/obb) are writable to them. On sdcardfs devices, this requires
// a special bind mount, since app-private and OBB dirs share the same GID, but we
@@ -118,6 +125,19 @@
if (status != OK) {
return status;
}
+
+ if (mAppDataIsolationEnabled) {
+ // Starting from now, fuse is running, and zygote will bind app obb data directory
+ if (!VolumeManager::Instance()->addFuseMountedUser(userId)) {
+ return UNKNOWN_ERROR;
+ }
+
+ // As all new processes created by zygote will bind app obb data directory, we just need
+ // to have a snapshot of all existing processes and see if any existing process needs to
+ // remount obb data directory.
+ VolumeManager::Instance()->remountAppObb(userId);
+ }
+
return OK;
}
@@ -135,16 +155,22 @@
// Intentional continue to try to unmount the other bind mount
}
}
+ // When app data isolation is enabled, kill all apps that obb/ is mounted, otherwise we should
+ // umount the whole Android/ dir.
+ if (mAppDataIsolationEnabled) {
+ std::string appObbDir(StringPrintf("%s/%d/Android/obb", getPath().c_str(), userId));
+ KillProcessesWithMountPrefix(appObbDir);
+ } else {
+ std::string androidTarget(
+ StringPrintf("/mnt/user/%d/%s/%d/Android", userId, label.c_str(), userId));
- std::string androidTarget(
- StringPrintf("/mnt/user/%d/%s/%d/Android", userId, label.c_str(), userId));
-
- LOG(INFO) << "Unmounting " << androidTarget;
- auto status = UnmountTree(androidTarget);
- if (status != OK) {
- return status;
+ LOG(INFO) << "Unmounting " << androidTarget;
+ auto status = UnmountTree(androidTarget);
+ if (status != OK) {
+ return status;
+ }
+ LOG(INFO) << "Unmounted " << androidTarget;
}
- LOG(INFO) << "Unmounted " << androidTarget;
return OK;
}
@@ -281,9 +307,15 @@
if (mFuseMounted) {
std::string label = getLabel();
+
+ // Update fuse mounted record
+ if (mAppDataIsolationEnabled &&
+ !VolumeManager::Instance()->removeFuseMountedUser(userId)) {
+ return UNKNOWN_ERROR;
+ }
+
// Ignoring unmount return status because we do want to try to unmount
// the rest cleanly.
-
unmountFuseBindMounts();
if (UnmountUserFuse(userId, getInternalPath(), label) != OK) {
PLOG(INFO) << "UnmountUserFuse failed on emulated fuse volume";
diff --git a/model/EmulatedVolume.h b/model/EmulatedVolume.h
index 3f1b2e3..12d01ec 100644
--- a/model/EmulatedVolume.h
+++ b/model/EmulatedVolume.h
@@ -65,6 +65,9 @@
/* Whether to use sdcardfs for this volume */
bool mUseSdcardFs;
+ /* Whether to use app data isolation is enabled tor this volume */
+ bool mAppDataIsolationEnabled;
+
DISALLOW_COPY_AND_ASSIGN(EmulatedVolume);
};