From 5700261e5a5e308fbbec9347e7009fa3917c2571 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Thu, 28 Nov 2019 11:56:13 +0100 Subject: [PATCH] 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 --- Utils.cpp | 5 +++- model/EmulatedVolume.cpp | 55 +++++++++++++++++++++++++++++++++++----- model/PublicVolume.cpp | 7 ----- 3 files changed, 52 insertions(+), 15 deletions(-) diff --git a/Utils.cpp b/Utils.cpp index 841aab6..d483418 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -1076,6 +1076,7 @@ status_t UnmountUserFuse(userid_t user_id, const std::string& absolute_lower_pat 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 @@ status_t UnmountUserFuse(userid_t user_id, const std::string& absolute_lower_pat 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 @@ std::string EmulatedVolume::getLabel() { } } +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 @@ status_t EmulatedVolume::doMount() { 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 @@ status_t EmulatedVolume::doUnmount() { // 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 @@ status_t PublicVolume::doUnmount() { 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; }