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/Utils.cpp b/Utils.cpp
index 841aab6..d483418 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -1076,6 +1076,7 @@
if (status != android::OK) {
LOG(ERROR) << "Failed to unmount " << pass_through_path;
}
+ rmdir(pass_through_path.c_str());
LOG(INFO) << "Unmounting fuse path " << fuse_path;
android::status_t result = ForceUnmount(fuse_path);
@@ -1089,8 +1090,10 @@
PLOG(ERROR) << "Failed to unmount with MNT_DETACH " << fuse_path;
return -errno;
}
- return android::OK;
+ result = android::OK;
}
+ rmdir(fuse_path.c_str());
+
return result;
}
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) {
diff --git a/model/PublicVolume.cpp b/model/PublicVolume.cpp
index e1606a3..3b5e6f0 100644
--- a/model/PublicVolume.cpp
+++ b/model/PublicVolume.cpp
@@ -269,13 +269,6 @@
return -errno;
}
- 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()));
- rmdir(fuse_path.c_str());
- rmdir(pass_through_path.c_str());
-
mFuseMounted = false;
}