Bind mount Android/ directory in FUSE.
For apps seeing the FUSE filesystem, we want to bind-mount the Android/
directory to the lower filesystem. The main reason for this is game
performance - Android/ contains both OBBs and app-private external data,
and both are heavily accessed during game startup. This is a pretty
straightforward bind-mount on top of /mnt/user.
Bug: 137890172
Test: Running the following:
df /storge/emulated/0 ==> /dev/fuse (FUSE)
df /storage/emulated/0/Android ==> /data/media (sdcardfs)
Test: atest AdoptableHostTest
Change-Id: Ic17a5751b5a94846ee565ff935644a078044ab06
diff --git a/model/EmulatedVolume.cpp b/model/EmulatedVolume.cpp
index 99abdd5..9abab89 100644
--- a/model/EmulatedVolume.cpp
+++ b/model/EmulatedVolume.cpp
@@ -69,6 +69,46 @@
}
}
+static status_t mountFuseBindMounts(int userId, const std::string& label) {
+ // TODO(b/134706060) we don't actually want to mount the "write" view by
+ // default, since it gives write access to all OBB dirs.
+ std::string androidSource(
+ StringPrintf("/mnt/runtime/write/%s/%d/Android", label.c_str(), userId));
+ std::string androidTarget(
+ StringPrintf("/mnt/user/%d/%s/%d/Android", userId, label.c_str(), userId));
+
+ if (access(androidSource.c_str(), F_OK) != 0) {
+ // Android path may not exist yet if users has just been created; create it on
+ // the lower fs.
+ if (fs_prepare_dir(androidSource.c_str(), 0771, AID_ROOT, AID_ROOT) != 0) {
+ PLOG(ERROR) << "Failed to create " << androidSource;
+ return -errno;
+ }
+ }
+ LOG(INFO) << "Bind mounting " << androidSource << " on " << androidTarget;
+ auto status = BindMount(androidSource, androidTarget);
+ if (status != OK) {
+ return status;
+ }
+ LOG(INFO) << "Bind mounted " << androidSource << " on " << androidTarget;
+
+ return OK;
+}
+
+static status_t unmountFuseBindMounts(int userId, const std::string& label) {
+ 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) << "Unmounted " << androidTarget;
+
+ return OK;
+}
+
status_t EmulatedVolume::doMount() {
std::string label = getLabel();
bool isVisible = getMountFlags() & MountFlags::kVisible;
@@ -159,6 +199,9 @@
return -EIO;
}
}
+
+ // Only do the bind-mounts when we know for sure the FUSE daemon can resolve the path.
+ return mountFuseBindMounts(user_id, label);
}
return OK;
@@ -171,20 +214,18 @@
// error code and might cause broken behaviour in applications.
KillProcessesUsingPath(getPath());
+ int userId = getMountUserId();
if (mFuseMounted) {
std::string label = getLabel();
+ // Ignoring unmount return status because we do want to try to unmount
+ // the rest cleanly.
- 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(getMountUserId(), getInternalPath(), label) != OK) {
+ unmountFuseBindMounts(userId, label);
+ if (UnmountUserFuse(userId, getInternalPath(), label) != OK) {
PLOG(INFO) << "UnmountUserFuse failed on emulated fuse volume";
return -errno;
}
- rmdir(fuse_path.c_str());
- rmdir(pass_through_path.c_str());
-
mFuseMounted = false;
}
if (getMountUserId() != 0) {