Mount /dev/fuse on /mnt/user/<userid>/<volumeid>
Since system_server cannot mount devices by itself, add a binder interface to vold that system_server can call to initiate this mount when required. BUG: 135341433 Test: manual Test: atest --test-mapping packages/providers/MediaProvider Test: ExternalStorageHostTest DownloadProviderTests Change-Id: If4fd02a1f1a8d921a3f96783d8c73e085c5b7ca1
This commit is contained in:
parent
7cd6ceb835
commit
3623a212e3
10 changed files with 197 additions and 4 deletions
47
Utils.cpp
47
Utils.cpp
|
@ -165,7 +165,7 @@ status_t ForceUnmount(const std::string& path) {
|
|||
if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
PLOG(INFO) << "ForceUnmount failed";
|
||||
return -errno;
|
||||
}
|
||||
|
||||
|
@ -985,5 +985,50 @@ bool writeStringToFile(const std::string& payload, const std::string& filename)
|
|||
return true;
|
||||
}
|
||||
|
||||
int MountUserFuse(userid_t user_id, const std::string& relative_path, int* device_fd) {
|
||||
std::string path(StringPrintf("/mnt/user/%d/%s", user_id, relative_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 = android::vold::ForceUnmount(path);
|
||||
if (result != android::OK) {
|
||||
PLOG(ERROR) << "Failed to unmount " << path;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create directories.
|
||||
result = android::vold::PrepareDir(path, 0700, AID_ROOT, AID_ROOT);
|
||||
if (result != android::OK) {
|
||||
PLOG(ERROR) << "Failed to prepare directory " << path;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Open device fd.
|
||||
*device_fd = open("/dev/fuse", O_RDWR | O_CLOEXEC);
|
||||
if (*device_fd == -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,",
|
||||
*device_fd);
|
||||
|
||||
const int result_int =
|
||||
TEMP_FAILURE_RETRY(mount("/dev/fuse", path.c_str(), "fuse",
|
||||
MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()));
|
||||
if (result_int != 0) {
|
||||
PLOG(ERROR) << "Failed to mount " << path;
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
||||
|
|
5
Utils.h
5
Utils.h
|
@ -33,6 +33,8 @@ struct DIR;
|
|||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
static const char* kPropFuse = "persist.sys.fuse";
|
||||
|
||||
/* SELinux contexts used depending on the block device type */
|
||||
extern security_context_t sBlkidContext;
|
||||
extern security_context_t sBlkidUntrustedContext;
|
||||
|
@ -147,6 +149,9 @@ status_t WaitForFile(const char* filename, std::chrono::nanoseconds timeout);
|
|||
bool FsyncDirectory(const std::string& dirname);
|
||||
|
||||
bool writeStringToFile(const std::string& payload, const std::string& filename);
|
||||
|
||||
int MountUserFuse(userid_t user_id, const std::string& relative_path, int* device_fd);
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
||||
|
||||
|
|
|
@ -326,7 +326,8 @@ binder::Status VoldNativeService::forgetPartition(const std::string& partGuid,
|
|||
}
|
||||
|
||||
binder::Status VoldNativeService::mount(const std::string& volId, int32_t mountFlags,
|
||||
int32_t mountUserId) {
|
||||
int32_t mountUserId,
|
||||
android::base::unique_fd* _aidl_return) {
|
||||
ENFORCE_UID(AID_SYSTEM);
|
||||
CHECK_ARGUMENT_ID(volId);
|
||||
ACQUIRE_LOCK;
|
||||
|
@ -343,6 +344,14 @@ binder::Status VoldNativeService::mount(const std::string& volId, int32_t mountF
|
|||
if (res != OK) {
|
||||
return translate(res);
|
||||
}
|
||||
|
||||
_aidl_return->reset(vol->getDeviceFd());
|
||||
if (_aidl_return->get() == -1) {
|
||||
// Let's not return invalid fd since binder will not allow null fds. Instead give it a
|
||||
// default value.
|
||||
_aidl_return->reset(open("/dev/null", O_RDONLY | O_CLOEXEC));
|
||||
}
|
||||
|
||||
if ((mountFlags & MOUNT_FLAG_PRIMARY) != 0) {
|
||||
res = VolumeManager::Instance()->setPrimary(vol);
|
||||
if (res != OK) {
|
||||
|
|
|
@ -52,7 +52,8 @@ class VoldNativeService : public BinderService<VoldNativeService>, public os::Bn
|
|||
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,
|
||||
android::base::unique_fd* _aidl_return);
|
||||
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,
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <array>
|
||||
#include <thread>
|
||||
|
||||
#include <linux/kdev_t.h>
|
||||
|
||||
|
@ -100,6 +101,22 @@ static const unsigned int kMajorBlockExperimentalMax = 254;
|
|||
|
||||
VolumeManager* VolumeManager::sInstance = NULL;
|
||||
|
||||
static void* symlinkPrimary(void* data) {
|
||||
std::unique_ptr<std::pair<std::string, std::string>> linkInfo(
|
||||
static_cast<std::pair<std::string, std::string>*>(data));
|
||||
std::string* source = &linkInfo->first;
|
||||
std::string* target = &linkInfo->second;
|
||||
|
||||
fs_prepare_dir(source->c_str(), 0755, AID_ROOT, AID_ROOT);
|
||||
fs_prepare_dir(target->c_str(), 0755, AID_ROOT, AID_ROOT);
|
||||
*target = *target + "/primary";
|
||||
|
||||
// Link source to target
|
||||
LOG(DEBUG) << "Linking " << *source << " to " << *target;
|
||||
Symlink(*source, *target);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VolumeManager* VolumeManager::Instance() {
|
||||
if (!sInstance) sInstance = new VolumeManager();
|
||||
return sInstance;
|
||||
|
@ -365,6 +382,21 @@ int VolumeManager::forgetPartition(const std::string& partGuid, const std::strin
|
|||
}
|
||||
|
||||
int VolumeManager::linkPrimary(userid_t userId) {
|
||||
bool isFuse = GetBoolProperty(android::vold::kPropFuse, false);
|
||||
|
||||
if (isFuse) {
|
||||
// Here we have to touch /mnt/user/userid>/<volumeid> which was already mounted as part of
|
||||
// the boot sequence, requiring waiting till a fuse handler is available. If we do this work
|
||||
// in foreground we could hang the caller, i.e. system server, which needs to start the fuse
|
||||
// handler. So do it in the background.
|
||||
std::string source(
|
||||
StringPrintf("/mnt/user/%d/%s/%d", userId, mPrimary->getId().c_str(), userId));
|
||||
std::string target(StringPrintf("/mnt/user/%d/self", userId));
|
||||
|
||||
auto symlinkInfo = new std::pair<std::string, std::string>(source, target);
|
||||
std::thread(symlinkPrimary, symlinkInfo).detach();
|
||||
}
|
||||
|
||||
std::string source(mPrimary->getPath());
|
||||
if (mPrimary->isEmulated()) {
|
||||
source = StringPrintf("%s/%d", source.c_str(), userId);
|
||||
|
@ -431,6 +463,10 @@ int VolumeManager::setPrimary(const std::shared_ptr<android::vold::VolumeBase>&
|
|||
}
|
||||
|
||||
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;
|
||||
switch (mountMode) {
|
||||
case VoldNativeService::REMOUNT_MODE_NONE:
|
||||
|
|
|
@ -40,7 +40,7 @@ interface IVold {
|
|||
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);
|
||||
FileDescriptor mount(@utf8InCpp String volId, int mountFlags, int mountUserId);
|
||||
void unmount(@utf8InCpp String volId);
|
||||
void format(@utf8InCpp String volId, @utf8InCpp String fsType);
|
||||
void benchmark(@utf8InCpp String volId, IVoldTaskListener listener);
|
||||
|
|
|
@ -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>
|
||||
|
@ -81,7 +84,22 @@ status_t EmulatedVolume::doMount() {
|
|||
|
||||
dev_t before = GetDevice(mFuseFull);
|
||||
|
||||
bool isFuse = base::GetBoolProperty(kPropFuse, false);
|
||||
|
||||
if (isFuse) {
|
||||
LOG(INFO) << "Mounting emulated fuse volume";
|
||||
int fd = -1;
|
||||
int result = MountUserFuse(getMountUserId(), label, &fd);
|
||||
if (result != 0) {
|
||||
PLOG(ERROR) << "Failed to mount emulated fuse volume";
|
||||
return -result;
|
||||
}
|
||||
setDeviceFd(fd);
|
||||
return OK;
|
||||
}
|
||||
|
||||
if (!(mFusePid = fork())) {
|
||||
LOG(INFO) << "Executing sdcardfs";
|
||||
// clang-format off
|
||||
if (execl(kFusePath, kFusePath,
|
||||
"-u", "1023", // AID_MEDIA_RW
|
||||
|
@ -131,6 +149,31 @@ status_t EmulatedVolume::doUnmount() {
|
|||
// 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(kPropFuse, 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 path(StringPrintf("/mnt/user/%d/%s", getMountUserId(), label.c_str()));
|
||||
status_t result = ForceUnmount(path);
|
||||
if (result != OK) {
|
||||
// TODO(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.
|
||||
if (!umount2(path.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH) || errno == EINVAL ||
|
||||
errno == ENOENT) {
|
||||
PLOG(INFO) << "ForceUnmount failed on emulated fuse volume";
|
||||
}
|
||||
}
|
||||
|
||||
rmdir(path.c_str());
|
||||
setDeviceFd(-1);
|
||||
return OK;
|
||||
}
|
||||
|
||||
ForceUnmount(mFuseDefault);
|
||||
ForceUnmount(mFuseRead);
|
||||
ForceUnmount(mFuseWrite);
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
|
||||
#include "PublicVolume.h"
|
||||
|
||||
#include "AppFuseUtil.h"
|
||||
#include "Utils.h"
|
||||
#include "VolumeManager.h"
|
||||
#include "fs/Exfat.h"
|
||||
|
@ -167,6 +169,20 @@ status_t PublicVolume::doMount() {
|
|||
|
||||
dev_t before = GetDevice(mFuseFull);
|
||||
|
||||
bool isFuse = base::GetBoolProperty(kPropFuse, false);
|
||||
|
||||
if (isFuse) {
|
||||
LOG(INFO) << "Mounting public fuse volume";
|
||||
int fd = -1;
|
||||
int result = MountUserFuse(getMountUserId(), stableName, &fd);
|
||||
if (result != 0) {
|
||||
LOG(ERROR) << "Failed to mount public fuse volume";
|
||||
return -result;
|
||||
}
|
||||
setDeviceFd(fd);
|
||||
return OK;
|
||||
}
|
||||
|
||||
if (!(mFusePid = fork())) {
|
||||
if (getMountFlags() & MountFlags::kPrimary) {
|
||||
// clang-format off
|
||||
|
@ -229,6 +245,30 @@ status_t PublicVolume::doUnmount() {
|
|||
// error code and might cause broken behaviour in applications.
|
||||
KillProcessesUsingPath(getPath());
|
||||
|
||||
bool isFuse = base::GetBoolProperty(kPropFuse, false);
|
||||
if (isFuse) {
|
||||
// Use UUID as stable name, if available
|
||||
std::string stableName = getId();
|
||||
if (!mFsUuid.empty()) {
|
||||
stableName = mFsUuid;
|
||||
}
|
||||
|
||||
std::string path(StringPrintf("/mnt/user/%d/%s", getMountUserId(), stableName.c_str()));
|
||||
status_t result = ForceUnmount(path);
|
||||
if (result != OK) {
|
||||
// TODO(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.
|
||||
if (!umount2(path.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH) || errno == EINVAL ||
|
||||
errno == ENOENT) {
|
||||
PLOG(INFO) << "ForceUnmount failed on public fuse volume";
|
||||
}
|
||||
}
|
||||
|
||||
rmdir(path.c_str());
|
||||
setDeviceFd(-1);
|
||||
return OK;
|
||||
}
|
||||
|
||||
ForceUnmount(kAsecPath);
|
||||
|
||||
ForceUnmount(mFuseDefault);
|
||||
|
|
|
@ -143,6 +143,16 @@ status_t VolumeBase::setInternalPath(const std::string& internalPath) {
|
|||
return OK;
|
||||
}
|
||||
|
||||
status_t VolumeBase::setDeviceFd(int deviceFd) {
|
||||
if ((mState != State::kChecking)) {
|
||||
LOG(WARNING) << getId() << " device fd change requires state checking";
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
mDeviceFd.reset(deviceFd);
|
||||
return OK;
|
||||
}
|
||||
|
||||
android::sp<android::os::IVoldListener> VolumeBase::getListener() const {
|
||||
if (mSilent) {
|
||||
return nullptr;
|
||||
|
|
|
@ -87,6 +87,7 @@ class VolumeBase {
|
|||
State getState() const { return mState; }
|
||||
const std::string& getPath() const { return mPath; }
|
||||
const std::string& getInternalPath() const { return mInternalPath; }
|
||||
int getDeviceFd() const { return dup(mDeviceFd.get()); }
|
||||
|
||||
status_t setDiskId(const std::string& diskId);
|
||||
status_t setPartGuid(const std::string& partGuid);
|
||||
|
@ -121,6 +122,7 @@ class VolumeBase {
|
|||
status_t setId(const std::string& id);
|
||||
status_t setPath(const std::string& path);
|
||||
status_t setInternalPath(const std::string& internalPath);
|
||||
status_t setDeviceFd(int deviceFd);
|
||||
|
||||
android::sp<android::os::IVoldListener> getListener() const;
|
||||
|
||||
|
@ -147,6 +149,8 @@ class VolumeBase {
|
|||
std::string mInternalPath;
|
||||
/* Flag indicating that volume should emit no events */
|
||||
bool mSilent;
|
||||
/* The filedescriptor for the fuse device, if the volume uses fuse, or -1 otherwise */
|
||||
android::base::unique_fd mDeviceFd;
|
||||
|
||||
/* Volumes stacked on top of this volume */
|
||||
std::list<std::shared_ptr<VolumeBase>> mVolumes;
|
||||
|
|
Loading…
Reference in a new issue