diff --git a/Utils.cpp b/Utils.cpp index 3915667..be4d293 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -134,7 +134,7 @@ int SetQuotaProjectId(std::string path, long projectId) { return ioctl(fd, FS_IOC_FSSETXATTR, &fsx); } -int PrepareDirsFromRoot(std::string path, std::string root, mode_t mode, uid_t uid, gid_t gid) { +int PrepareAppDirsFromRoot(std::string path, std::string root, mode_t mode, uid_t uid, gid_t gid) { int ret = 0; if (!StartsWith(path, root)) { return -1; @@ -1164,5 +1164,31 @@ status_t UnmountUserFuse(userid_t user_id, const std::string& absolute_lower_pat return result; } +status_t PrepareAndroidDirs(const std::string& volumeRoot) { + std::string androidDir = volumeRoot + kAndroidDir; + std::string androidDataDir = volumeRoot + kAppDataDir; + std::string androidObbDir = volumeRoot + kAppObbDir; + + bool useSdcardFs = IsFilesystemSupported("sdcardfs"); + + if (fs_prepare_dir(androidDir.c_str(), 0771, AID_MEDIA_RW, AID_MEDIA_RW) != 0) { + PLOG(ERROR) << "Failed to create " << androidDir; + return -errno; + } + + gid_t dataGid = useSdcardFs ? AID_MEDIA_RW : AID_EXT_DATA_RW; + if (fs_prepare_dir(androidDataDir.c_str(), 0771, AID_MEDIA_RW, dataGid) != 0) { + PLOG(ERROR) << "Failed to create " << androidDataDir; + return -errno; + } + + gid_t obbGid = useSdcardFs ? AID_MEDIA_RW : AID_EXT_OBB_RW; + if (fs_prepare_dir(androidObbDir.c_str(), 0771, AID_MEDIA_RW, obbGid) != 0) { + PLOG(ERROR) << "Failed to create " << androidObbDir; + return -errno; + } + + return OK; +} } // namespace vold } // namespace android diff --git a/Utils.h b/Utils.h index 42e8b4e..ec42f39 100644 --- a/Utils.h +++ b/Utils.h @@ -36,6 +36,11 @@ namespace vold { static const char* kPropFuse = "persist.sys.fuse"; +static const char* kAndroidDir = "/Android/"; +static const char* kAppDataDir = "/Android/data/"; +static const char* kAppMediaDir = "/Android/media/"; +static const char* kAppObbDir = "/Android/obb/"; + /* SELinux contexts used depending on the block device type */ extern security_context_t sBlkidContext; extern security_context_t sBlkidUntrustedContext; @@ -52,8 +57,10 @@ int SetQuotaProjectId(std::string path, long projectId); /* * Recursively calls fs_prepare_dir() on all components in 'path', starting at 'root'. * 'path' must start with 'root' + * ONLY for use with app-specific data directories on external storage! + * (eg, /Android/data/com.foo, /Android/obb/com.foo, etc.) */ -int PrepareDirsFromRoot(std::string path, std::string root, mode_t mode, uid_t uid, gid_t gid); +int PrepareAppDirsFromRoot(std::string path, std::string root, mode_t mode, uid_t uid, gid_t gid); /* fs_prepare_dir wrapper that creates with SELinux context */ status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid); @@ -164,6 +171,7 @@ status_t MountUserFuse(userid_t user_id, const std::string& absolute_lower_path, status_t UnmountUserFuse(userid_t userId, const std::string& absolute_lower_path, const std::string& relative_upper_path); +status_t PrepareAndroidDirs(const std::string& volumeRoot); } // namespace vold } // namespace android diff --git a/VolumeManager.cpp b/VolumeManager.cpp index 3de89ab..e6d593a 100644 --- a/VolumeManager.cpp +++ b/VolumeManager.cpp @@ -81,7 +81,9 @@ using android::vold::BindMount; using android::vold::CreateDir; using android::vold::DeleteDirContents; using android::vold::DeleteDirContentsAndDir; -using android::vold::PrepareDirsFromRoot; +using android::vold::IsFilesystemSupported; +using android::vold::PrepareAndroidDirs; +using android::vold::PrepareAppDirsFromRoot; using android::vold::PrivateVolume; using android::vold::Symlink; using android::vold::Unlink; @@ -822,6 +824,29 @@ int VolumeManager::unmountAll() { return 0; } +static gid_t getAppDirGid(const std::string& appDir) { + // Create app-specific dirs with the correct UID/GID + gid_t gid = AID_MEDIA_RW; + if (!IsFilesystemSupported("sdcardfs")) { + if (appDir == android::vold::kAppDataDir) { + gid = AID_EXT_DATA_RW; + } else if (appDir == android::vold::kAppObbDir) { + gid = AID_EXT_OBB_RW; + } else if (appDir == android::vold::kAppMediaDir) { + gid = AID_MEDIA_RW; + } else { + gid = AID_MEDIA_RW; + } + } + + return gid; +} + +static bool isValidAppDirRoot(const std::string& appDirRoot) { + return appDirRoot == android::vold::kAppDataDir || appDirRoot == android::vold::kAppMediaDir || + appDirRoot == android::vold::kAppObbDir; +} + int VolumeManager::setupAppDir(const std::string& path, const std::string& appDirRoot, int32_t appUid) { // Only offer to create directories for paths managed by vold @@ -868,14 +893,25 @@ int VolumeManager::setupAppDir(const std::string& path, const std::string& appDi const std::string lowerAppDirRoot = volume->getInternalPath() + appDirRoot.substr(volume->getPath().length()); - // First create the root which holds app dirs, if needed. - int ret = PrepareDirsFromRoot(lowerAppDirRoot, volume->getInternalPath(), 0771, AID_MEDIA_RW, - AID_MEDIA_RW); + // Do some sanity checking on the app dir (relative from root) + const std::string volumeRoot = volume->getRootPath(); // eg /data/media/0 + + // eg, if lowerAppDirRoot = /data/media/0/Android/data, this is /Android/data + const std::string relativeAppRoot = lowerAppDirRoot.substr(volumeRoot.length()); + if (!isValidAppDirRoot(relativeAppRoot)) { + LOG(ERROR) << path << " is not a valid application directory."; + return -EINVAL; + } + + // Make sure the Android/ directories exist and are setup correctly + int ret = PrepareAndroidDirs(volumeRoot); if (ret != 0) { + LOG(ERROR) << "Failed to prepare Android/ directories."; return ret; } - // Then, create app-specific dirs with the correct UID/GID - return PrepareDirsFromRoot(lowerPath, lowerAppDirRoot, 0770, appUid, AID_MEDIA_RW); + + gid_t gid = getAppDirGid(relativeAppRoot); + return PrepareAppDirsFromRoot(lowerPath, lowerAppDirRoot, 0770, appUid, gid); } int VolumeManager::createObb(const std::string& sourcePath, const std::string& sourceKey, diff --git a/model/EmulatedVolume.cpp b/model/EmulatedVolume.cpp index 082dea5..8f8c87a 100644 --- a/model/EmulatedVolume.cpp +++ b/model/EmulatedVolume.cpp @@ -73,17 +73,8 @@ std::string EmulatedVolume::getLabel() { } } -// Creates a bind mount from source to target, creating the source (!) directory -// if not yet present. +// Creates a bind mount from source to target static status_t doFuseBindMount(const std::string& source, const std::string& target) { - if (access(source.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(source.c_str(), 0771, AID_MEDIA_RW, AID_MEDIA_RW) != 0) { - PLOG(ERROR) << "Failed to create " << source; - return -errno; - } - } LOG(INFO) << "Bind mounting " << source << " on " << target; auto status = BindMount(source, target); if (status != OK) { @@ -232,11 +223,19 @@ status_t EmulatedVolume::doMount() { LOG(INFO) << "Mounting emulated fuse volume"; android::base::unique_fd fd; int user_id = getMountUserId(); - int result = MountUserFuse(user_id, getInternalPath(), label, &fd); + auto volumeRoot = getRootPath(); - if (result != 0) { + // Make sure Android/ dirs exist for bind mounting + status_t res = PrepareAndroidDirs(volumeRoot); + if (res != OK) { + LOG(ERROR) << "Failed to prepare Android/ directories"; + return res; + } + + res = MountUserFuse(user_id, getInternalPath(), label, &fd); + if (res != 0) { PLOG(ERROR) << "Failed to mount emulated fuse volume"; - return -result; + return res; } mFuseMounted = true; @@ -252,7 +251,7 @@ status_t EmulatedVolume::doMount() { } // Only do the bind-mounts when we know for sure the FUSE daemon can resolve the path. - status_t res = mountFuseBindMounts(); + res = mountFuseBindMounts(); if (res != OK) { fd.reset(); doUnmount(); @@ -317,5 +316,12 @@ status_t EmulatedVolume::doUnmount() { return OK; } +std::string EmulatedVolume::getRootPath() const { + int user_id = getMountUserId(); + std::string volumeRoot = StringPrintf("%s/%d", getInternalPath().c_str(), user_id); + + return volumeRoot; +} + } // namespace vold } // namespace android diff --git a/model/EmulatedVolume.h b/model/EmulatedVolume.h index 4f76a60..3f1b2e3 100644 --- a/model/EmulatedVolume.h +++ b/model/EmulatedVolume.h @@ -40,6 +40,7 @@ class EmulatedVolume : public VolumeBase { explicit EmulatedVolume(const std::string& rawPath, int userId); EmulatedVolume(const std::string& rawPath, dev_t device, const std::string& fsUuid, int userId); virtual ~EmulatedVolume(); + std::string getRootPath() const override; protected: status_t doMount() override; diff --git a/model/VolumeBase.cpp b/model/VolumeBase.cpp index 636c065..687d4f7 100644 --- a/model/VolumeBase.cpp +++ b/model/VolumeBase.cpp @@ -274,6 +274,11 @@ status_t VolumeBase::doFormat(const std::string& fsType) { return -ENOTSUP; } +std::string VolumeBase::getRootPath() const { + // Usually the same as the internal path, except for emulated volumes. + return getInternalPath(); +} + std::ostream& VolumeBase::operator<<(std::ostream& stream) const { return stream << " VolumeBase{id=" << mId << ",mountFlags=" << mMountFlags << ",mountUserId=" << mMountUserId << "}"; diff --git a/model/VolumeBase.h b/model/VolumeBase.h index 1d88d1b..078bb0c 100644 --- a/model/VolumeBase.h +++ b/model/VolumeBase.h @@ -110,6 +110,8 @@ class VolumeBase { status_t unmount(); status_t format(const std::string& fsType); + virtual std::string getRootPath() const; + std::ostream& operator<<(std::ostream& stream) const; protected: