2015-03-03 06:01:40 +01:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2015 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "Utils.h"
|
2017-10-20 17:07:53 +02:00
|
|
|
|
2015-03-03 06:01:40 +01:00
|
|
|
#include "Process.h"
|
2017-10-20 17:07:53 +02:00
|
|
|
#include "sehandle.h"
|
2015-03-03 06:01:40 +01:00
|
|
|
|
2018-10-30 23:59:24 +01:00
|
|
|
#include <android-base/chrono_utils.h>
|
2015-12-05 00:50:53 +01:00
|
|
|
#include <android-base/file.h>
|
|
|
|
#include <android-base/logging.h>
|
2017-06-16 02:13:56 +02:00
|
|
|
#include <android-base/properties.h>
|
2023-03-01 21:08:59 +01:00
|
|
|
#include <android-base/scopeguard.h>
|
2015-12-05 00:50:53 +01:00
|
|
|
#include <android-base/stringprintf.h>
|
2018-09-18 22:30:21 +02:00
|
|
|
#include <android-base/strings.h>
|
2018-09-18 22:07:45 +02:00
|
|
|
#include <android-base/unique_fd.h>
|
2015-03-03 06:01:40 +01:00
|
|
|
#include <cutils/fs.h>
|
2015-03-31 19:35:33 +02:00
|
|
|
#include <logwrap/logwrap.h>
|
2017-06-16 02:13:56 +02:00
|
|
|
#include <private/android_filesystem_config.h>
|
2020-02-18 16:29:25 +01:00
|
|
|
#include <private/android_projectid_config.h>
|
2015-03-03 06:01:40 +01:00
|
|
|
|
2015-04-25 01:00:03 +02:00
|
|
|
#include <dirent.h>
|
2015-03-03 06:01:40 +01:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <linux/fs.h>
|
2020-02-11 12:37:25 +01:00
|
|
|
#include <linux/posix_acl.h>
|
|
|
|
#include <linux/posix_acl_xattr.h>
|
2018-09-25 23:22:07 +02:00
|
|
|
#include <mntent.h>
|
|
|
|
#include <stdio.h>
|
2015-03-03 06:01:40 +01:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/mount.h>
|
|
|
|
#include <sys/stat.h>
|
2018-09-18 22:30:21 +02:00
|
|
|
#include <sys/statvfs.h>
|
2017-05-18 18:08:24 +02:00
|
|
|
#include <sys/sysmacros.h>
|
2018-09-18 22:30:21 +02:00
|
|
|
#include <sys/types.h>
|
2015-03-03 06:01:40 +01:00
|
|
|
#include <sys/wait.h>
|
2020-02-11 12:37:25 +01:00
|
|
|
#include <sys/xattr.h>
|
2019-04-05 13:09:57 +02:00
|
|
|
#include <unistd.h>
|
2018-10-30 23:59:24 +01:00
|
|
|
|
2020-02-18 15:06:37 +01:00
|
|
|
#include <filesystem>
|
2018-09-25 23:22:07 +02:00
|
|
|
#include <list>
|
2018-09-18 22:30:21 +02:00
|
|
|
#include <mutex>
|
2020-02-10 23:48:11 +01:00
|
|
|
#include <regex>
|
2018-10-30 23:59:24 +01:00
|
|
|
#include <thread>
|
2015-03-03 06:01:40 +01:00
|
|
|
|
|
|
|
#ifndef UMOUNT_NOFOLLOW
|
2018-09-18 22:30:21 +02:00
|
|
|
#define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */
|
2015-03-03 06:01:40 +01:00
|
|
|
#endif
|
|
|
|
|
2018-10-30 23:59:24 +01:00
|
|
|
using namespace std::chrono_literals;
|
2020-01-31 15:49:24 +01:00
|
|
|
using android::base::EndsWith;
|
2015-05-22 07:35:42 +02:00
|
|
|
using android::base::ReadFileToString;
|
2019-12-24 12:57:16 +01:00
|
|
|
using android::base::StartsWith;
|
2015-03-31 19:35:33 +02:00
|
|
|
using android::base::StringPrintf;
|
2020-06-12 13:59:45 +02:00
|
|
|
using android::base::unique_fd;
|
2015-03-31 19:35:33 +02:00
|
|
|
|
2015-03-03 06:01:40 +01:00
|
|
|
namespace android {
|
|
|
|
namespace vold {
|
|
|
|
|
2021-09-10 10:51:08 +02:00
|
|
|
char* sBlkidContext = nullptr;
|
|
|
|
char* sBlkidUntrustedContext = nullptr;
|
|
|
|
char* sFsckContext = nullptr;
|
|
|
|
char* sFsckUntrustedContext = nullptr;
|
2015-04-01 20:54:32 +02:00
|
|
|
|
2017-10-20 17:07:53 +02:00
|
|
|
bool sSleepOnUnmount = true;
|
|
|
|
|
2015-03-31 19:35:33 +02:00
|
|
|
static const char* kBlkidPath = "/system/bin/blkid";
|
2015-06-18 23:25:08 +02:00
|
|
|
static const char* kKeyPath = "/data/misc/vold";
|
2015-03-31 19:35:33 +02:00
|
|
|
|
2020-05-15 01:35:03 +02:00
|
|
|
static const char* kProcDevices = "/proc/devices";
|
2015-05-22 07:35:42 +02:00
|
|
|
static const char* kProcFilesystems = "/proc/filesystems";
|
|
|
|
|
2020-02-10 23:48:11 +01:00
|
|
|
static const char* kAndroidDir = "/Android/";
|
|
|
|
static const char* kAppDataDir = "/Android/data/";
|
|
|
|
static const char* kAppMediaDir = "/Android/media/";
|
|
|
|
static const char* kAppObbDir = "/Android/obb/";
|
|
|
|
|
2020-02-11 15:31:24 +01:00
|
|
|
static const char* kMediaProviderCtx = "u:r:mediaprovider:";
|
|
|
|
static const char* kMediaProviderAppCtx = "u:r:mediaprovider_app:";
|
|
|
|
|
2017-10-19 01:02:21 +02:00
|
|
|
// Lock used to protect process-level SELinux changes from racing with each
|
|
|
|
// other between multiple threads.
|
|
|
|
static std::mutex kSecurityLock;
|
|
|
|
|
2020-06-12 13:59:45 +02:00
|
|
|
std::string GetFuseMountPathForUser(userid_t user_id, const std::string& relative_upper_path) {
|
|
|
|
return StringPrintf("/mnt/user/%d/%s", user_id, relative_upper_path.c_str());
|
|
|
|
}
|
|
|
|
|
2015-03-03 06:01:40 +01:00
|
|
|
status_t CreateDeviceNode(const std::string& path, dev_t dev) {
|
2017-10-19 01:02:21 +02:00
|
|
|
std::lock_guard<std::mutex> lock(kSecurityLock);
|
2015-03-03 06:01:40 +01:00
|
|
|
const char* cpath = path.c_str();
|
2023-03-01 21:08:59 +01:00
|
|
|
auto clearfscreatecon = android::base::make_scope_guard([] { setfscreatecon(nullptr); });
|
|
|
|
auto secontext = std::unique_ptr<char, void (*)(char*)>(nullptr, freecon);
|
|
|
|
char* tmp_secontext;
|
|
|
|
|
|
|
|
if (selabel_lookup(sehandle, &tmp_secontext, cpath, S_IFBLK) == 0) {
|
|
|
|
secontext.reset(tmp_secontext);
|
|
|
|
if (setfscreatecon(secontext.get()) != 0) {
|
|
|
|
LOG(ERROR) << "Failed to setfscreatecon for device node " << path;
|
|
|
|
return -EINVAL;
|
2023-02-28 22:58:08 +01:00
|
|
|
}
|
2023-03-01 21:08:59 +01:00
|
|
|
} else if (errno == ENOENT) {
|
|
|
|
LOG(DEBUG) << "No selabel defined for device node " << path;
|
|
|
|
} else {
|
|
|
|
PLOG(ERROR) << "Failed to look up selabel for device node " << path;
|
|
|
|
return -errno;
|
2015-03-03 06:01:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
mode_t mode = 0660 | S_IFBLK;
|
|
|
|
if (mknod(cpath, mode, dev) < 0) {
|
|
|
|
if (errno != EEXIST) {
|
2018-09-18 22:30:21 +02:00
|
|
|
PLOG(ERROR) << "Failed to create device node for " << major(dev) << ":" << minor(dev)
|
|
|
|
<< " at " << path;
|
2023-03-01 21:08:59 +01:00
|
|
|
return -errno;
|
2015-03-03 06:01:40 +01:00
|
|
|
}
|
|
|
|
}
|
2023-03-01 21:08:59 +01:00
|
|
|
return OK;
|
2015-03-03 06:01:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
status_t DestroyDeviceNode(const std::string& path) {
|
|
|
|
const char* cpath = path.c_str();
|
|
|
|
if (TEMP_FAILURE_RETRY(unlink(cpath))) {
|
|
|
|
return -errno;
|
|
|
|
} else {
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-18 13:44:59 +01:00
|
|
|
// Sets a default ACL on the directory.
|
2021-02-01 08:57:02 +01:00
|
|
|
status_t SetDefaultAcl(const std::string& path, mode_t mode, uid_t uid, gid_t gid,
|
|
|
|
std::vector<gid_t> additionalGids) {
|
2020-05-12 07:58:42 +02:00
|
|
|
if (IsSdcardfsUsed()) {
|
2020-02-11 12:37:25 +01:00
|
|
|
// sdcardfs magically takes care of this
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2020-06-16 14:58:52 +02:00
|
|
|
size_t num_entries = 3 + (additionalGids.size() > 0 ? additionalGids.size() + 1 : 0);
|
|
|
|
size_t size = sizeof(posix_acl_xattr_header) + num_entries * sizeof(posix_acl_xattr_entry);
|
2020-02-11 12:37:25 +01:00
|
|
|
auto buf = std::make_unique<uint8_t[]>(size);
|
|
|
|
|
|
|
|
posix_acl_xattr_header* acl_header = reinterpret_cast<posix_acl_xattr_header*>(buf.get());
|
|
|
|
acl_header->a_version = POSIX_ACL_XATTR_VERSION;
|
|
|
|
|
|
|
|
posix_acl_xattr_entry* entry =
|
|
|
|
reinterpret_cast<posix_acl_xattr_entry*>(buf.get() + sizeof(posix_acl_xattr_header));
|
|
|
|
|
2020-06-16 14:58:52 +02:00
|
|
|
int tag_index = 0;
|
|
|
|
|
|
|
|
entry[tag_index].e_tag = ACL_USER_OBJ;
|
2020-02-18 13:44:59 +01:00
|
|
|
// The existing mode_t mask has the ACL in the lower 9 bits:
|
|
|
|
// the lowest 3 for "other", the next 3 the group, the next 3 for the owner
|
|
|
|
// Use the mode_t masks to get these bits out, and shift them to get the
|
|
|
|
// correct value per entity.
|
|
|
|
//
|
|
|
|
// Eg if mode_t = 0700, rwx for the owner, then & S_IRWXU >> 6 results in 7
|
2020-06-16 14:58:52 +02:00
|
|
|
entry[tag_index].e_perm = (mode & S_IRWXU) >> 6;
|
|
|
|
entry[tag_index].e_id = uid;
|
|
|
|
tag_index++;
|
|
|
|
|
|
|
|
entry[tag_index].e_tag = ACL_GROUP_OBJ;
|
|
|
|
entry[tag_index].e_perm = (mode & S_IRWXG) >> 3;
|
|
|
|
entry[tag_index].e_id = gid;
|
|
|
|
tag_index++;
|
|
|
|
|
|
|
|
if (additionalGids.size() > 0) {
|
|
|
|
for (gid_t additional_gid : additionalGids) {
|
|
|
|
entry[tag_index].e_tag = ACL_GROUP;
|
|
|
|
entry[tag_index].e_perm = (mode & S_IRWXG) >> 3;
|
|
|
|
entry[tag_index].e_id = additional_gid;
|
|
|
|
tag_index++;
|
|
|
|
}
|
2020-02-11 12:37:25 +01:00
|
|
|
|
2020-06-16 14:58:52 +02:00
|
|
|
entry[tag_index].e_tag = ACL_MASK;
|
|
|
|
entry[tag_index].e_perm = (mode & S_IRWXG) >> 3;
|
|
|
|
entry[tag_index].e_id = 0;
|
|
|
|
tag_index++;
|
|
|
|
}
|
2020-02-11 12:37:25 +01:00
|
|
|
|
2020-06-16 14:58:52 +02:00
|
|
|
entry[tag_index].e_tag = ACL_OTHER;
|
|
|
|
entry[tag_index].e_perm = mode & S_IRWXO;
|
|
|
|
entry[tag_index].e_id = 0;
|
2020-02-11 12:37:25 +01:00
|
|
|
|
|
|
|
int ret = setxattr(path.c_str(), XATTR_NAME_POSIX_ACL_DEFAULT, acl_header, size, 0);
|
|
|
|
|
|
|
|
if (ret != 0) {
|
|
|
|
PLOG(ERROR) << "Failed to set default ACL on " << path;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-02-06 18:57:47 +01:00
|
|
|
int SetQuotaInherit(const std::string& path) {
|
2020-10-24 01:32:58 +02:00
|
|
|
unsigned int flags;
|
2020-02-06 18:57:47 +01:00
|
|
|
|
|
|
|
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
|
|
|
|
if (fd == -1) {
|
|
|
|
PLOG(ERROR) << "Failed to open " << path << " to set project id inheritance.";
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
|
|
|
|
if (ret == -1) {
|
|
|
|
PLOG(ERROR) << "Failed to get flags for " << path << " to set project id inheritance.";
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
flags |= FS_PROJINHERIT_FL;
|
|
|
|
|
|
|
|
ret = ioctl(fd, FS_IOC_SETFLAGS, &flags);
|
|
|
|
if (ret == -1) {
|
|
|
|
PLOG(ERROR) << "Failed to set flags for " << path << " to set project id inheritance.";
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SetQuotaProjectId(const std::string& path, long projectId) {
|
2020-01-16 01:25:27 +01:00
|
|
|
struct fsxattr fsx;
|
|
|
|
|
|
|
|
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
|
|
|
|
if (fd == -1) {
|
|
|
|
PLOG(ERROR) << "Failed to open " << path << " to set project id.";
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ret = ioctl(fd, FS_IOC_FSGETXATTR, &fsx);
|
|
|
|
if (ret == -1) {
|
|
|
|
PLOG(ERROR) << "Failed to get extended attributes for " << path << " to get project id.";
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
fsx.fsx_projid = projectId;
|
2021-05-03 21:29:40 +02:00
|
|
|
ret = ioctl(fd, FS_IOC_FSSETXATTR, &fsx);
|
|
|
|
if (ret == -1) {
|
|
|
|
PLOG(ERROR) << "Failed to set project id on " << path;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
return 0;
|
2020-01-16 01:25:27 +01:00
|
|
|
}
|
|
|
|
|
2020-02-10 23:48:11 +01:00
|
|
|
int PrepareDirWithProjectId(const std::string& path, mode_t mode, uid_t uid, gid_t gid,
|
|
|
|
long projectId) {
|
|
|
|
int ret = fs_prepare_dir(path.c_str(), mode, uid, gid);
|
|
|
|
|
|
|
|
if (ret != 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-05-12 07:58:42 +02:00
|
|
|
if (!IsSdcardfsUsed()) {
|
2020-02-10 23:48:11 +01:00
|
|
|
ret = SetQuotaProjectId(path, projectId);
|
2019-12-24 12:57:16 +01:00
|
|
|
}
|
2020-01-31 15:49:24 +01:00
|
|
|
|
2020-02-10 23:48:11 +01:00
|
|
|
return ret;
|
|
|
|
}
|
2019-12-24 12:57:16 +01:00
|
|
|
|
2020-02-18 15:06:37 +01:00
|
|
|
static int FixupAppDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid, long projectId) {
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
|
|
|
|
// Setup the directory itself correctly
|
|
|
|
int ret = PrepareDirWithProjectId(path, mode, uid, gid, projectId);
|
|
|
|
if (ret != OK) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fixup all of its file entries
|
|
|
|
for (const auto& itEntry : fs::directory_iterator(path)) {
|
|
|
|
ret = lchown(itEntry.path().c_str(), uid, gid);
|
|
|
|
if (ret != 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = chmod(itEntry.path().c_str(), mode);
|
|
|
|
if (ret != 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-05-12 07:58:42 +02:00
|
|
|
if (!IsSdcardfsUsed()) {
|
2020-02-18 15:06:37 +01:00
|
|
|
ret = SetQuotaProjectId(itEntry.path(), projectId);
|
|
|
|
if (ret != 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int PrepareAppDirFromRoot(const std::string& path, const std::string& root, int appUid,
|
|
|
|
bool fixupExisting) {
|
2020-02-13 23:30:38 +01:00
|
|
|
long projectId;
|
|
|
|
size_t pos;
|
|
|
|
int ret = 0;
|
2020-05-12 07:58:42 +02:00
|
|
|
bool sdcardfsSupport = IsSdcardfsUsed();
|
2020-02-13 23:30:38 +01:00
|
|
|
|
|
|
|
// Make sure the Android/ directories exist and are setup correctly
|
|
|
|
ret = PrepareAndroidDirs(root);
|
|
|
|
if (ret != 0) {
|
|
|
|
LOG(ERROR) << "Failed to prepare Android/ directories.";
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now create the application-specific subdir(s)
|
|
|
|
// path is something like /data/media/0/Android/data/com.foo/files
|
|
|
|
// First, chop off the volume root, eg /data/media/0
|
|
|
|
std::string pathFromRoot = path.substr(root.length());
|
|
|
|
|
|
|
|
uid_t uid = appUid;
|
2020-02-10 23:48:11 +01:00
|
|
|
gid_t gid = AID_MEDIA_RW;
|
2020-06-16 14:58:52 +02:00
|
|
|
std::vector<gid_t> additionalGids;
|
2020-02-13 23:30:38 +01:00
|
|
|
std::string appDir;
|
|
|
|
|
|
|
|
// Check that the next part matches one of the allowed Android/ dirs
|
|
|
|
if (StartsWith(pathFromRoot, kAppDataDir)) {
|
|
|
|
appDir = kAppDataDir;
|
2020-05-12 07:58:42 +02:00
|
|
|
if (!sdcardfsSupport) {
|
2020-02-10 23:48:11 +01:00
|
|
|
gid = AID_EXT_DATA_RW;
|
2020-06-16 14:58:52 +02:00
|
|
|
// Also add the app's own UID as a group; since apps belong to a group
|
|
|
|
// that matches their UID, this ensures that they will always have access to
|
|
|
|
// the files created in these dirs, even if they are created by other processes
|
|
|
|
additionalGids.push_back(uid);
|
2020-02-13 23:30:38 +01:00
|
|
|
}
|
|
|
|
} else if (StartsWith(pathFromRoot, kAppMediaDir)) {
|
|
|
|
appDir = kAppMediaDir;
|
2020-05-12 07:58:42 +02:00
|
|
|
if (!sdcardfsSupport) {
|
2020-02-10 23:48:11 +01:00
|
|
|
gid = AID_MEDIA_RW;
|
2020-01-31 15:49:24 +01:00
|
|
|
}
|
2020-02-17 19:57:01 +01:00
|
|
|
} else if (StartsWith(pathFromRoot, kAppObbDir)) {
|
2020-02-13 23:30:38 +01:00
|
|
|
appDir = kAppObbDir;
|
2020-05-12 07:58:42 +02:00
|
|
|
if (!sdcardfsSupport) {
|
2020-02-13 23:30:38 +01:00
|
|
|
gid = AID_EXT_OBB_RW;
|
2020-06-16 14:58:52 +02:00
|
|
|
// See comments for kAppDataDir above
|
|
|
|
additionalGids.push_back(uid);
|
2020-02-13 23:30:38 +01:00
|
|
|
}
|
|
|
|
} else {
|
2020-02-10 23:48:11 +01:00
|
|
|
LOG(ERROR) << "Invalid application directory: " << path;
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// mode = 770, plus sticky bit on directory to inherit GID when apps
|
|
|
|
// create subdirs
|
|
|
|
mode_t mode = S_IRWXU | S_IRWXG | S_ISGID;
|
2020-02-13 23:30:38 +01:00
|
|
|
// the project ID for application-specific directories is directly
|
|
|
|
// derived from their uid
|
|
|
|
|
|
|
|
// Chop off the generic application-specific part, eg /Android/data/
|
|
|
|
// this leaves us with something like com.foo/files/
|
|
|
|
std::string leftToCreate = pathFromRoot.substr(appDir.length());
|
|
|
|
if (!EndsWith(leftToCreate, "/")) {
|
|
|
|
leftToCreate += "/";
|
2020-02-10 23:48:11 +01:00
|
|
|
}
|
2020-02-13 23:30:38 +01:00
|
|
|
std::string pathToCreate = root + appDir;
|
|
|
|
int depth = 0;
|
2020-02-18 16:29:25 +01:00
|
|
|
// Derive initial project ID
|
|
|
|
if (appDir == kAppDataDir || appDir == kAppMediaDir) {
|
|
|
|
projectId = uid - AID_APP_START + PROJECT_ID_EXT_DATA_START;
|
|
|
|
} else if (appDir == kAppObbDir) {
|
|
|
|
projectId = uid - AID_APP_START + PROJECT_ID_EXT_OBB_START;
|
|
|
|
}
|
|
|
|
|
2020-02-13 23:30:38 +01:00
|
|
|
while ((pos = leftToCreate.find('/')) != std::string::npos) {
|
|
|
|
std::string component = leftToCreate.substr(0, pos + 1);
|
|
|
|
leftToCreate = leftToCreate.erase(0, pos + 1);
|
|
|
|
pathToCreate = pathToCreate + component;
|
|
|
|
|
|
|
|
if (appDir == kAppDataDir && depth == 1 && component == "cache/") {
|
|
|
|
// All dirs use the "app" project ID, except for the cache dirs in
|
|
|
|
// Android/data, eg Android/data/com.foo/cache
|
|
|
|
// Note that this "sticks" - eg subdirs of this dir need the same
|
|
|
|
// project ID.
|
2020-02-18 16:29:25 +01:00
|
|
|
projectId = uid - AID_APP_START + PROJECT_ID_EXT_CACHE_START;
|
2020-02-13 23:30:38 +01:00
|
|
|
}
|
2020-02-18 15:06:37 +01:00
|
|
|
|
|
|
|
if (fixupExisting && access(pathToCreate.c_str(), F_OK) == 0) {
|
|
|
|
// Fixup all files in this existing directory with the correct UID/GID
|
|
|
|
// and project ID.
|
|
|
|
ret = FixupAppDir(pathToCreate, mode, uid, gid, projectId);
|
|
|
|
} else {
|
|
|
|
ret = PrepareDirWithProjectId(pathToCreate, mode, uid, gid, projectId);
|
|
|
|
}
|
|
|
|
|
2020-02-13 23:30:38 +01:00
|
|
|
if (ret != 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
2020-02-10 23:48:11 +01:00
|
|
|
|
2020-02-13 23:30:38 +01:00
|
|
|
if (depth == 0) {
|
|
|
|
// Set the default ACL on the top-level application-specific directories,
|
|
|
|
// to ensure that even if applications run with a umask of 0077,
|
|
|
|
// new directories within these directories will allow the GID
|
|
|
|
// specified here to write; this is necessary for apps like
|
|
|
|
// installers and MTP, that require access here.
|
|
|
|
//
|
|
|
|
// See man (5) acl for more details.
|
2020-06-16 14:58:52 +02:00
|
|
|
ret = SetDefaultAcl(pathToCreate, mode, uid, gid, additionalGids);
|
2020-02-13 23:30:38 +01:00
|
|
|
if (ret != 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
Only set quota project ID inheritance on app-private dirs.
Previously every directory on external storage had project ID quota
inheritance enabled; this means that if any new file/directory is
created under such a directory, it will inherit the project ID from the
parent. We use a default project ID of 1000 for generic directories, and
application-specific project IDs for app-specific directories.
MediaProvider is responsible for updating the quota type in the generic
directories, as it scans all files there. However, there is a problem
with this approach: if you move a file to a directory with project ID
inheritance set, and the project ID of that file differs from the
project ID of the dir, that results in an EXDEV error, and requires a
copy instead. For example, if /sdcard/DCIM/test.jpg has a project ID of
1003 (for images), and you try to move it to /sdcard/Pictures/test.jpg,
that would require a copy, because the project ID of /sdcard/Pictures is
1000.
While this is not a very common scenario, it's still better to avoid it.
Luckily we can - since MediaProvider anyway scans all files, it will set
the project ID on individual files correctly - there's no need to
inherit them.
We then only need to inherit quota in application-specific directories,
since in those directories the app can create files itself, and those
need to be tagged correctly.
This change enables that, by removing quota inheritance setting from the
top-level directory, and instead doing it for app-specific directories
instead.
Bug: 151078664
Test: atest StorageHostTest
atest com.android.tests.fused.host.FuseDaemonHostTest#testRenameAndReplaceFile
Change-Id: I38a057ec61cb627e39a3ff7ac58c7218dc251bdc
2020-03-11 11:51:45 +01:00
|
|
|
|
2020-05-12 07:58:42 +02:00
|
|
|
if (!sdcardfsSupport) {
|
Only set quota project ID inheritance on app-private dirs.
Previously every directory on external storage had project ID quota
inheritance enabled; this means that if any new file/directory is
created under such a directory, it will inherit the project ID from the
parent. We use a default project ID of 1000 for generic directories, and
application-specific project IDs for app-specific directories.
MediaProvider is responsible for updating the quota type in the generic
directories, as it scans all files there. However, there is a problem
with this approach: if you move a file to a directory with project ID
inheritance set, and the project ID of that file differs from the
project ID of the dir, that results in an EXDEV error, and requires a
copy instead. For example, if /sdcard/DCIM/test.jpg has a project ID of
1003 (for images), and you try to move it to /sdcard/Pictures/test.jpg,
that would require a copy, because the project ID of /sdcard/Pictures is
1000.
While this is not a very common scenario, it's still better to avoid it.
Luckily we can - since MediaProvider anyway scans all files, it will set
the project ID on individual files correctly - there's no need to
inherit them.
We then only need to inherit quota in application-specific directories,
since in those directories the app can create files itself, and those
need to be tagged correctly.
This change enables that, by removing quota inheritance setting from the
top-level directory, and instead doing it for app-specific directories
instead.
Bug: 151078664
Test: atest StorageHostTest
atest com.android.tests.fused.host.FuseDaemonHostTest#testRenameAndReplaceFile
Change-Id: I38a057ec61cb627e39a3ff7ac58c7218dc251bdc
2020-03-11 11:51:45 +01:00
|
|
|
// Set project ID inheritance, so that future subdirectories inherit the
|
|
|
|
// same project ID
|
|
|
|
ret = SetQuotaInherit(pathToCreate);
|
|
|
|
if (ret != 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
2020-02-13 23:30:38 +01:00
|
|
|
}
|
2020-02-11 12:37:25 +01:00
|
|
|
|
2020-02-13 23:30:38 +01:00
|
|
|
depth++;
|
2020-02-10 23:48:11 +01:00
|
|
|
}
|
|
|
|
|
2020-02-13 23:30:38 +01:00
|
|
|
return OK;
|
2019-12-24 12:57:16 +01:00
|
|
|
}
|
|
|
|
|
2020-08-13 03:31:43 +02:00
|
|
|
int SetAttrs(const std::string& path, unsigned int attrs) {
|
2020-10-24 01:32:58 +02:00
|
|
|
unsigned int flags;
|
2020-08-13 03:31:43 +02:00
|
|
|
android::base::unique_fd fd(
|
|
|
|
TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
|
|
|
|
|
|
|
|
if (fd == -1) {
|
|
|
|
PLOG(ERROR) << "Failed to open " << path;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-10-24 01:32:58 +02:00
|
|
|
if (ioctl(fd, FS_IOC_GETFLAGS, &flags)) {
|
2020-08-13 03:31:43 +02:00
|
|
|
PLOG(ERROR) << "Failed to get flags for " << path;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((flags & attrs) == attrs) return 0;
|
|
|
|
flags |= attrs;
|
2020-10-24 01:32:58 +02:00
|
|
|
if (ioctl(fd, FS_IOC_SETFLAGS, &flags)) {
|
2020-08-13 03:31:43 +02:00
|
|
|
PLOG(ERROR) << "Failed to set flags for " << path << "(0x" << std::hex << attrs << ")";
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid,
|
|
|
|
unsigned int attrs) {
|
2017-10-19 01:02:21 +02:00
|
|
|
std::lock_guard<std::mutex> lock(kSecurityLock);
|
2015-04-06 23:08:45 +02:00
|
|
|
const char* cpath = path.c_str();
|
2023-03-01 21:08:59 +01:00
|
|
|
auto clearfscreatecon = android::base::make_scope_guard([] { setfscreatecon(nullptr); });
|
|
|
|
auto secontext = std::unique_ptr<char, void (*)(char*)>(nullptr, freecon);
|
|
|
|
char* tmp_secontext;
|
|
|
|
|
|
|
|
if (selabel_lookup(sehandle, &tmp_secontext, cpath, S_IFDIR) == 0) {
|
|
|
|
secontext.reset(tmp_secontext);
|
|
|
|
if (setfscreatecon(secontext.get()) != 0) {
|
|
|
|
LOG(ERROR) << "Failed to setfscreatecon for directory " << path;
|
|
|
|
return -EINVAL;
|
2023-02-28 22:58:08 +01:00
|
|
|
}
|
2023-03-01 21:08:59 +01:00
|
|
|
} else if (errno == ENOENT) {
|
|
|
|
LOG(DEBUG) << "No selabel defined for directory " << path;
|
2023-02-28 22:58:08 +01:00
|
|
|
} else {
|
2023-03-01 21:08:59 +01:00
|
|
|
PLOG(ERROR) << "Failed to look up selabel for directory " << path;
|
2023-02-28 22:58:08 +01:00
|
|
|
return -errno;
|
|
|
|
}
|
2023-03-01 21:08:59 +01:00
|
|
|
|
|
|
|
if (fs_prepare_dir(cpath, mode, uid, gid) != 0) return -errno;
|
|
|
|
if (attrs && SetAttrs(path, attrs) != 0) return -errno;
|
|
|
|
return OK;
|
2015-04-06 23:08:45 +02:00
|
|
|
}
|
|
|
|
|
2015-03-03 06:01:40 +01:00
|
|
|
status_t ForceUnmount(const std::string& path) {
|
|
|
|
const char* cpath = path.c_str();
|
|
|
|
if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
|
|
|
|
return OK;
|
|
|
|
}
|
2015-10-21 21:16:12 +02:00
|
|
|
// Apps might still be handling eject request, so wait before
|
|
|
|
// we start sending signals
|
2017-10-20 17:07:53 +02:00
|
|
|
if (sSleepOnUnmount) sleep(5);
|
2015-04-09 06:07:21 +02:00
|
|
|
|
2017-10-07 02:02:53 +02:00
|
|
|
KillProcessesWithOpenFiles(path, SIGINT);
|
2017-10-20 17:07:53 +02:00
|
|
|
if (sSleepOnUnmount) sleep(5);
|
2015-04-09 06:07:21 +02:00
|
|
|
if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2017-10-07 02:02:53 +02:00
|
|
|
KillProcessesWithOpenFiles(path, SIGTERM);
|
2017-10-20 17:07:53 +02:00
|
|
|
if (sSleepOnUnmount) sleep(5);
|
2015-03-03 06:01:40 +01:00
|
|
|
if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
|
|
|
|
return OK;
|
|
|
|
}
|
2015-04-06 23:08:45 +02:00
|
|
|
|
2017-10-07 02:02:53 +02:00
|
|
|
KillProcessesWithOpenFiles(path, SIGKILL);
|
2017-10-20 17:07:53 +02:00
|
|
|
if (sSleepOnUnmount) sleep(5);
|
2015-03-03 06:01:40 +01:00
|
|
|
if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
|
|
|
|
return OK;
|
|
|
|
}
|
2019-07-19 17:46:53 +02:00
|
|
|
PLOG(INFO) << "ForceUnmount failed";
|
2015-03-03 06:01:40 +01:00
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
2021-04-30 10:53:07 +02:00
|
|
|
status_t KillProcessesWithTmpfsMountPrefix(const std::string& path) {
|
|
|
|
if (KillProcessesWithTmpfsMounts(path, SIGINT) == 0) {
|
2020-02-11 15:31:24 +01:00
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
if (sSleepOnUnmount) sleep(5);
|
|
|
|
|
2021-04-30 10:53:07 +02:00
|
|
|
if (KillProcessesWithTmpfsMounts(path, SIGTERM) == 0) {
|
2020-02-11 15:31:24 +01:00
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
if (sSleepOnUnmount) sleep(5);
|
|
|
|
|
2021-04-30 10:53:07 +02:00
|
|
|
if (KillProcessesWithTmpfsMounts(path, SIGKILL) == 0) {
|
2020-02-11 15:31:24 +01:00
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
if (sSleepOnUnmount) sleep(5);
|
|
|
|
|
|
|
|
// Send SIGKILL a second time to determine if we've
|
|
|
|
// actually killed everyone mount
|
2021-04-30 10:53:07 +02:00
|
|
|
if (KillProcessesWithTmpfsMounts(path, SIGKILL) == 0) {
|
2020-02-11 15:31:24 +01:00
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
PLOG(ERROR) << "Failed to kill processes using " << path;
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2015-10-21 21:16:12 +02:00
|
|
|
status_t KillProcessesUsingPath(const std::string& path) {
|
2021-03-04 13:21:24 +01:00
|
|
|
if (KillProcessesWithOpenFiles(path, SIGINT, false /* killFuseDaemon */) == 0) {
|
2015-10-21 21:16:12 +02:00
|
|
|
return OK;
|
|
|
|
}
|
2017-10-20 17:07:53 +02:00
|
|
|
if (sSleepOnUnmount) sleep(5);
|
2015-10-21 21:16:12 +02:00
|
|
|
|
2021-03-04 13:21:24 +01:00
|
|
|
if (KillProcessesWithOpenFiles(path, SIGTERM, false /* killFuseDaemon */) == 0) {
|
2015-10-21 21:16:12 +02:00
|
|
|
return OK;
|
|
|
|
}
|
2017-10-20 17:07:53 +02:00
|
|
|
if (sSleepOnUnmount) sleep(5);
|
2015-10-21 21:16:12 +02:00
|
|
|
|
2021-03-04 13:21:24 +01:00
|
|
|
if (KillProcessesWithOpenFiles(path, SIGKILL, false /* killFuseDaemon */) == 0) {
|
2015-10-21 21:16:12 +02:00
|
|
|
return OK;
|
|
|
|
}
|
2017-10-20 17:07:53 +02:00
|
|
|
if (sSleepOnUnmount) sleep(5);
|
2015-10-21 21:16:12 +02:00
|
|
|
|
|
|
|
// Send SIGKILL a second time to determine if we've
|
|
|
|
// actually killed everyone with open files
|
2021-03-04 13:21:24 +01:00
|
|
|
// This time, we also kill the FUSE daemon if found
|
|
|
|
if (KillProcessesWithOpenFiles(path, SIGKILL, true /* killFuseDaemon */) == 0) {
|
2015-10-21 21:16:12 +02:00
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
PLOG(ERROR) << "Failed to kill processes using " << path;
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2015-03-14 00:09:20 +01:00
|
|
|
status_t BindMount(const std::string& source, const std::string& target) {
|
2019-02-06 21:39:19 +01:00
|
|
|
if (UnmountTree(target) < 0) {
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
if (TEMP_FAILURE_RETRY(mount(source.c_str(), target.c_str(), nullptr, MS_BIND, nullptr)) < 0) {
|
2015-03-14 00:09:20 +01:00
|
|
|
PLOG(ERROR) << "Failed to bind mount " << source << " to " << target;
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2019-02-06 21:39:19 +01:00
|
|
|
status_t Symlink(const std::string& target, const std::string& linkpath) {
|
|
|
|
if (Unlink(linkpath) < 0) {
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
if (TEMP_FAILURE_RETRY(symlink(target.c_str(), linkpath.c_str())) < 0) {
|
|
|
|
PLOG(ERROR) << "Failed to create symlink " << linkpath << " to " << target;
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
status_t Unlink(const std::string& linkpath) {
|
|
|
|
if (TEMP_FAILURE_RETRY(unlink(linkpath.c_str())) < 0 && errno != EINVAL && errno != ENOENT) {
|
|
|
|
PLOG(ERROR) << "Failed to unlink " << linkpath;
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2019-02-14 20:09:51 +01:00
|
|
|
status_t CreateDir(const std::string& dir, mode_t mode) {
|
|
|
|
struct stat sb;
|
|
|
|
if (TEMP_FAILURE_RETRY(stat(dir.c_str(), &sb)) == 0) {
|
|
|
|
if (S_ISDIR(sb.st_mode)) {
|
|
|
|
return OK;
|
|
|
|
} else if (TEMP_FAILURE_RETRY(unlink(dir.c_str())) == -1) {
|
|
|
|
PLOG(ERROR) << "Failed to unlink " << dir;
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
} else if (errno != ENOENT) {
|
|
|
|
PLOG(ERROR) << "Failed to stat " << dir;
|
|
|
|
return -errno;
|
|
|
|
}
|
2019-02-19 23:12:20 +01:00
|
|
|
if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), mode)) == -1 && errno != EEXIST) {
|
2019-02-14 20:09:51 +01:00
|
|
|
PLOG(ERROR) << "Failed to mkdir " << dir;
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2017-10-07 02:02:53 +02:00
|
|
|
bool FindValue(const std::string& raw, const std::string& key, std::string* value) {
|
|
|
|
auto qual = key + "=\"";
|
2019-02-06 00:33:34 +01:00
|
|
|
size_t start = 0;
|
|
|
|
while (true) {
|
|
|
|
start = raw.find(qual, start);
|
|
|
|
if (start == std::string::npos) return false;
|
|
|
|
if (start == 0 || raw[start - 1] == ' ') {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
start += 1;
|
2017-10-07 02:02:53 +02:00
|
|
|
}
|
|
|
|
start += qual.length();
|
|
|
|
|
|
|
|
auto end = raw.find("\"", start);
|
|
|
|
if (end == std::string::npos) return false;
|
|
|
|
|
|
|
|
*value = raw.substr(start, end - start);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-09-18 22:30:21 +02:00
|
|
|
static status_t readMetadata(const std::string& path, std::string* fsType, std::string* fsUuid,
|
|
|
|
std::string* fsLabel, bool untrusted) {
|
2017-10-07 02:02:53 +02:00
|
|
|
fsType->clear();
|
|
|
|
fsUuid->clear();
|
|
|
|
fsLabel->clear();
|
2015-03-31 19:35:33 +02:00
|
|
|
|
2015-04-09 06:07:21 +02:00
|
|
|
std::vector<std::string> cmd;
|
|
|
|
cmd.push_back(kBlkidPath);
|
|
|
|
cmd.push_back("-c");
|
|
|
|
cmd.push_back("/dev/null");
|
2015-08-13 01:04:35 +02:00
|
|
|
cmd.push_back("-s");
|
|
|
|
cmd.push_back("TYPE");
|
|
|
|
cmd.push_back("-s");
|
|
|
|
cmd.push_back("UUID");
|
|
|
|
cmd.push_back("-s");
|
|
|
|
cmd.push_back("LABEL");
|
2015-04-09 06:07:21 +02:00
|
|
|
cmd.push_back(path);
|
|
|
|
|
|
|
|
std::vector<std::string> output;
|
2018-11-30 20:43:47 +01:00
|
|
|
status_t res = ForkExecvp(cmd, &output, untrusted ? sBlkidUntrustedContext : sBlkidContext);
|
2015-04-09 06:07:21 +02:00
|
|
|
if (res != OK) {
|
|
|
|
LOG(WARNING) << "blkid failed to identify " << path;
|
|
|
|
return res;
|
2015-03-31 19:35:33 +02:00
|
|
|
}
|
|
|
|
|
2016-07-27 23:11:02 +02:00
|
|
|
for (const auto& line : output) {
|
2015-03-31 19:35:33 +02:00
|
|
|
// Extract values from blkid output, if defined
|
2017-10-07 02:02:53 +02:00
|
|
|
FindValue(line, "TYPE", fsType);
|
|
|
|
FindValue(line, "UUID", fsUuid);
|
|
|
|
FindValue(line, "LABEL", fsLabel);
|
2015-03-31 19:35:33 +02:00
|
|
|
}
|
|
|
|
|
2015-04-09 06:07:21 +02:00
|
|
|
return OK;
|
2015-03-31 19:35:33 +02:00
|
|
|
}
|
|
|
|
|
2018-09-18 22:30:21 +02:00
|
|
|
status_t ReadMetadata(const std::string& path, std::string* fsType, std::string* fsUuid,
|
|
|
|
std::string* fsLabel) {
|
2015-04-01 20:54:32 +02:00
|
|
|
return readMetadata(path, fsType, fsUuid, fsLabel, false);
|
|
|
|
}
|
|
|
|
|
2018-09-18 22:30:21 +02:00
|
|
|
status_t ReadMetadataUntrusted(const std::string& path, std::string* fsType, std::string* fsUuid,
|
|
|
|
std::string* fsLabel) {
|
2015-04-01 20:54:32 +02:00
|
|
|
return readMetadata(path, fsType, fsUuid, fsLabel, true);
|
|
|
|
}
|
|
|
|
|
2018-11-30 20:43:47 +01:00
|
|
|
static std::vector<const char*> ConvertToArgv(const std::vector<std::string>& args) {
|
|
|
|
std::vector<const char*> argv;
|
|
|
|
argv.reserve(args.size() + 1);
|
|
|
|
for (const auto& arg : args) {
|
|
|
|
if (argv.empty()) {
|
|
|
|
LOG(DEBUG) << arg;
|
2015-03-31 19:35:33 +02:00
|
|
|
} else {
|
2018-11-30 20:43:47 +01:00
|
|
|
LOG(DEBUG) << " " << arg;
|
2015-03-31 19:35:33 +02:00
|
|
|
}
|
2018-11-30 20:43:47 +01:00
|
|
|
argv.emplace_back(arg.data());
|
2015-03-31 19:35:33 +02:00
|
|
|
}
|
2018-11-30 20:43:47 +01:00
|
|
|
argv.emplace_back(nullptr);
|
|
|
|
return argv;
|
|
|
|
}
|
2015-04-09 06:07:21 +02:00
|
|
|
|
2018-11-30 20:43:47 +01:00
|
|
|
static status_t ReadLinesFromFdAndLog(std::vector<std::string>* output,
|
|
|
|
android::base::unique_fd ufd) {
|
|
|
|
std::unique_ptr<FILE, int (*)(FILE*)> fp(android::base::Fdopen(std::move(ufd), "r"), fclose);
|
|
|
|
if (!fp) {
|
|
|
|
PLOG(ERROR) << "fdopen in ReadLinesFromFdAndLog";
|
|
|
|
return -errno;
|
2015-04-25 01:00:03 +02:00
|
|
|
}
|
2018-11-30 20:43:47 +01:00
|
|
|
if (output) output->clear();
|
|
|
|
char line[1024];
|
|
|
|
while (fgets(line, sizeof(line), fp.get()) != nullptr) {
|
|
|
|
LOG(DEBUG) << line;
|
|
|
|
if (output) output->emplace_back(line);
|
2015-04-09 06:07:21 +02:00
|
|
|
}
|
2018-11-30 20:43:47 +01:00
|
|
|
return OK;
|
2015-04-09 06:07:21 +02:00
|
|
|
}
|
|
|
|
|
2018-11-30 20:43:47 +01:00
|
|
|
status_t ForkExecvp(const std::vector<std::string>& args, std::vector<std::string>* output,
|
2021-09-10 10:51:08 +02:00
|
|
|
char* context) {
|
2018-11-30 20:43:47 +01:00
|
|
|
auto argv = ConvertToArgv(args);
|
|
|
|
|
2018-11-30 20:43:47 +01:00
|
|
|
android::base::unique_fd pipe_read, pipe_write;
|
|
|
|
if (!android::base::Pipe(&pipe_read, &pipe_write)) {
|
|
|
|
PLOG(ERROR) << "Pipe in ForkExecvp";
|
2018-11-30 20:43:47 +01:00
|
|
|
return -errno;
|
2015-04-09 06:07:21 +02:00
|
|
|
}
|
|
|
|
|
2018-11-30 20:43:47 +01:00
|
|
|
pid_t pid = fork();
|
|
|
|
if (pid == 0) {
|
|
|
|
if (context) {
|
|
|
|
if (setexeccon(context)) {
|
2018-11-30 20:43:47 +01:00
|
|
|
LOG(ERROR) << "Failed to setexeccon in ForkExecvp";
|
2018-11-30 20:43:47 +01:00
|
|
|
abort();
|
|
|
|
}
|
2017-10-20 17:17:54 +02:00
|
|
|
}
|
2018-11-30 20:43:47 +01:00
|
|
|
pipe_read.reset();
|
2018-12-07 21:23:25 +01:00
|
|
|
if (dup2(pipe_write.get(), STDOUT_FILENO) == -1) {
|
|
|
|
PLOG(ERROR) << "dup2 in ForkExecvp";
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
}
|
2018-11-30 20:43:47 +01:00
|
|
|
pipe_write.reset();
|
2018-11-30 20:43:47 +01:00
|
|
|
execvp(argv[0], const_cast<char**>(argv.data()));
|
2018-11-30 20:43:47 +01:00
|
|
|
PLOG(ERROR) << "exec in ForkExecvp";
|
2018-11-30 20:43:47 +01:00
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
if (pid == -1) {
|
|
|
|
PLOG(ERROR) << "fork in ForkExecvp";
|
|
|
|
return -errno;
|
2015-04-09 06:07:21 +02:00
|
|
|
}
|
|
|
|
|
2018-11-30 20:43:47 +01:00
|
|
|
pipe_write.reset();
|
|
|
|
auto st = ReadLinesFromFdAndLog(output, std::move(pipe_read));
|
|
|
|
if (st != 0) return st;
|
|
|
|
|
|
|
|
int status;
|
|
|
|
if (waitpid(pid, &status, 0) == -1) {
|
|
|
|
PLOG(ERROR) << "waitpid in ForkExecvp";
|
2015-04-09 06:07:21 +02:00
|
|
|
return -errno;
|
|
|
|
}
|
2018-11-30 20:43:47 +01:00
|
|
|
if (!WIFEXITED(status)) {
|
|
|
|
LOG(ERROR) << "Process did not exit normally, status: " << status;
|
|
|
|
return -ECHILD;
|
2015-04-09 06:07:21 +02:00
|
|
|
}
|
2018-11-30 20:43:47 +01:00
|
|
|
if (WEXITSTATUS(status)) {
|
|
|
|
LOG(ERROR) << "Process exited with code: " << WEXITSTATUS(status);
|
|
|
|
return WEXITSTATUS(status);
|
2015-04-09 06:07:21 +02:00
|
|
|
}
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2021-09-15 02:32:06 +02:00
|
|
|
status_t ForkExecvpTimeout(const std::vector<std::string>& args, std::chrono::seconds timeout,
|
|
|
|
char* context) {
|
|
|
|
int status;
|
|
|
|
|
|
|
|
pid_t wait_timeout_pid = fork();
|
|
|
|
if (wait_timeout_pid == 0) {
|
|
|
|
pid_t pid = ForkExecvpAsync(args, context);
|
|
|
|
if (pid == -1) {
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
pid_t timer_pid = fork();
|
|
|
|
if (timer_pid == 0) {
|
2022-11-22 01:46:13 +01:00
|
|
|
std::this_thread::sleep_for(timeout);
|
2021-09-15 02:32:06 +02:00
|
|
|
_exit(ETIMEDOUT);
|
|
|
|
}
|
|
|
|
if (timer_pid == -1) {
|
|
|
|
PLOG(ERROR) << "fork in ForkExecvpAsync_timeout";
|
|
|
|
kill(pid, SIGTERM);
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
pid_t finished = wait(&status);
|
|
|
|
if (finished == pid) {
|
|
|
|
kill(timer_pid, SIGTERM);
|
|
|
|
} else {
|
|
|
|
kill(pid, SIGTERM);
|
|
|
|
}
|
|
|
|
if (!WIFEXITED(status)) {
|
|
|
|
_exit(ECHILD);
|
|
|
|
}
|
|
|
|
_exit(WEXITSTATUS(status));
|
|
|
|
}
|
|
|
|
if (waitpid(wait_timeout_pid, &status, 0) == -1) {
|
|
|
|
PLOG(ERROR) << "waitpid in ForkExecvpAsync_timeout";
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
if (!WIFEXITED(status)) {
|
|
|
|
LOG(ERROR) << "Process did not exit normally, status: " << status;
|
|
|
|
return -ECHILD;
|
|
|
|
}
|
|
|
|
if (WEXITSTATUS(status)) {
|
|
|
|
LOG(ERROR) << "Process exited with code: " << WEXITSTATUS(status);
|
|
|
|
return WEXITSTATUS(status);
|
|
|
|
}
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
pid_t ForkExecvpAsync(const std::vector<std::string>& args, char* context) {
|
2018-11-30 20:43:47 +01:00
|
|
|
auto argv = ConvertToArgv(args);
|
2015-04-25 01:00:03 +02:00
|
|
|
|
|
|
|
pid_t pid = fork();
|
|
|
|
if (pid == 0) {
|
|
|
|
close(STDIN_FILENO);
|
|
|
|
close(STDOUT_FILENO);
|
|
|
|
close(STDERR_FILENO);
|
2021-09-15 02:32:06 +02:00
|
|
|
if (context) {
|
|
|
|
if (setexeccon(context)) {
|
|
|
|
LOG(ERROR) << "Failed to setexeccon in ForkExecvpAsync";
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
2015-04-25 01:00:03 +02:00
|
|
|
|
2018-11-30 20:43:47 +01:00
|
|
|
execvp(argv[0], const_cast<char**>(argv.data()));
|
|
|
|
PLOG(ERROR) << "exec in ForkExecvpAsync";
|
|
|
|
_exit(EXIT_FAILURE);
|
2015-04-25 01:00:03 +02:00
|
|
|
}
|
|
|
|
if (pid == -1) {
|
2018-11-30 20:43:47 +01:00
|
|
|
PLOG(ERROR) << "fork in ForkExecvpAsync";
|
|
|
|
return -1;
|
2015-04-25 01:00:03 +02:00
|
|
|
}
|
|
|
|
return pid;
|
|
|
|
}
|
|
|
|
|
2015-03-31 19:35:33 +02:00
|
|
|
status_t ReadRandomBytes(size_t bytes, std::string& out) {
|
2017-08-01 18:15:53 +02:00
|
|
|
out.resize(bytes);
|
|
|
|
return ReadRandomBytes(bytes, &out[0]);
|
|
|
|
}
|
2015-03-31 19:35:33 +02:00
|
|
|
|
2017-08-01 18:15:53 +02:00
|
|
|
status_t ReadRandomBytes(size_t bytes, char* buf) {
|
2015-03-31 19:35:33 +02:00
|
|
|
int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
|
|
|
|
if (fd == -1) {
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
2019-01-16 22:05:34 +01:00
|
|
|
ssize_t n;
|
2017-08-01 18:15:53 +02:00
|
|
|
while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], bytes))) > 0) {
|
2015-03-31 19:35:33 +02:00
|
|
|
bytes -= n;
|
2017-08-01 18:15:53 +02:00
|
|
|
buf += n;
|
2015-03-31 19:35:33 +02:00
|
|
|
}
|
2015-05-16 03:34:24 +02:00
|
|
|
close(fd);
|
2015-03-31 19:35:33 +02:00
|
|
|
|
|
|
|
if (bytes == 0) {
|
|
|
|
return OK;
|
|
|
|
} else {
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Progress towards FBE and adoptable storage.
Offer to adopt storage devices on FBE devices, but keep it guarded
behind a system property for now, since we still need to work out key
storage details.
When migrating shared storage, leave user-specific /data/media
directories in place, since they already have the needed crypto
policies defined.
Enable journaling, quotas, and encrypt options when formatting
newly adopted devices. installd already gracefully handles older
partitions without quota enabled.
Test: cts-tradefed run commandAndExit cts-dev --abi armeabi-v7a -m CtsAppSecurityHostTestCases -t android.appsecurity.cts.AdoptableHostTest
Bug: 62290006, 36757864, 29117062, 37395736
Bug: 29923055, 25861755, 30230655, 37436961
Change-Id: Ibbeb6ec9db2394a279bbac221a2b20711d65494e
2017-06-21 21:52:23 +02:00
|
|
|
status_t GenerateRandomUuid(std::string& out) {
|
|
|
|
status_t res = ReadRandomBytes(16, out);
|
|
|
|
if (res == OK) {
|
2018-09-18 22:30:21 +02:00
|
|
|
out[6] &= 0x0f; /* clear version */
|
|
|
|
out[6] |= 0x40; /* set to version 4 */
|
|
|
|
out[8] &= 0x3f; /* clear variant */
|
|
|
|
out[8] |= 0x80; /* set to IETF variant */
|
Progress towards FBE and adoptable storage.
Offer to adopt storage devices on FBE devices, but keep it guarded
behind a system property for now, since we still need to work out key
storage details.
When migrating shared storage, leave user-specific /data/media
directories in place, since they already have the needed crypto
policies defined.
Enable journaling, quotas, and encrypt options when formatting
newly adopted devices. installd already gracefully handles older
partitions without quota enabled.
Test: cts-tradefed run commandAndExit cts-dev --abi armeabi-v7a -m CtsAppSecurityHostTestCases -t android.appsecurity.cts.AdoptableHostTest
Bug: 62290006, 36757864, 29117062, 37395736
Bug: 29923055, 25861755, 30230655, 37436961
Change-Id: Ibbeb6ec9db2394a279bbac221a2b20711d65494e
2017-06-21 21:52:23 +02:00
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2015-03-31 19:35:33 +02:00
|
|
|
status_t HexToStr(const std::string& hex, std::string& str) {
|
|
|
|
str.clear();
|
|
|
|
bool even = true;
|
|
|
|
char cur = 0;
|
|
|
|
for (size_t i = 0; i < hex.size(); i++) {
|
|
|
|
int val = 0;
|
|
|
|
switch (hex[i]) {
|
2018-09-18 22:30:21 +02:00
|
|
|
// clang-format off
|
|
|
|
case ' ': case '-': case ':': continue;
|
|
|
|
case 'f': case 'F': val = 15; break;
|
|
|
|
case 'e': case 'E': val = 14; break;
|
|
|
|
case 'd': case 'D': val = 13; break;
|
|
|
|
case 'c': case 'C': val = 12; break;
|
|
|
|
case 'b': case 'B': val = 11; break;
|
|
|
|
case 'a': case 'A': val = 10; break;
|
|
|
|
case '9': val = 9; break;
|
|
|
|
case '8': val = 8; break;
|
|
|
|
case '7': val = 7; break;
|
|
|
|
case '6': val = 6; break;
|
|
|
|
case '5': val = 5; break;
|
|
|
|
case '4': val = 4; break;
|
|
|
|
case '3': val = 3; break;
|
|
|
|
case '2': val = 2; break;
|
|
|
|
case '1': val = 1; break;
|
|
|
|
case '0': val = 0; break;
|
|
|
|
default: return -EINVAL;
|
|
|
|
// clang-format on
|
2015-03-31 19:35:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (even) {
|
|
|
|
cur = val << 4;
|
|
|
|
} else {
|
|
|
|
cur += val;
|
|
|
|
str.push_back(cur);
|
|
|
|
cur = 0;
|
|
|
|
}
|
|
|
|
even = !even;
|
|
|
|
}
|
|
|
|
return even ? OK : -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char* kLookup = "0123456789abcdef";
|
|
|
|
|
|
|
|
status_t StrToHex(const std::string& str, std::string& hex) {
|
|
|
|
hex.clear();
|
|
|
|
for (size_t i = 0; i < str.size(); i++) {
|
2015-04-30 00:57:48 +02:00
|
|
|
hex.push_back(kLookup[(str[i] & 0xF0) >> 4]);
|
2015-03-31 19:35:33 +02:00
|
|
|
hex.push_back(kLookup[str[i] & 0x0F]);
|
|
|
|
}
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2017-08-01 18:15:53 +02:00
|
|
|
status_t StrToHex(const KeyBuffer& str, KeyBuffer& hex) {
|
|
|
|
hex.clear();
|
|
|
|
for (size_t i = 0; i < str.size(); i++) {
|
|
|
|
hex.push_back(kLookup[(str.data()[i] & 0xF0) >> 4]);
|
|
|
|
hex.push_back(kLookup[str.data()[i] & 0x0F]);
|
|
|
|
}
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2015-06-18 23:25:08 +02:00
|
|
|
status_t NormalizeHex(const std::string& in, std::string& out) {
|
|
|
|
std::string tmp;
|
|
|
|
if (HexToStr(in, tmp)) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
return StrToHex(tmp, out);
|
|
|
|
}
|
|
|
|
|
2018-05-23 10:50:46 +02:00
|
|
|
status_t GetBlockDevSize(int fd, uint64_t* size) {
|
|
|
|
if (ioctl(fd, BLKGETSIZE64, size)) {
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
status_t GetBlockDevSize(const std::string& path, uint64_t* size) {
|
|
|
|
int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
|
|
|
|
status_t res = OK;
|
|
|
|
|
|
|
|
if (fd < 0) {
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
res = GetBlockDevSize(fd, size);
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
status_t GetBlockDev512Sectors(const std::string& path, uint64_t* nr_sec) {
|
|
|
|
uint64_t size;
|
|
|
|
status_t res = GetBlockDevSize(path, &size);
|
|
|
|
|
|
|
|
if (res != OK) {
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
*nr_sec = size / 512;
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2015-04-25 01:00:03 +02:00
|
|
|
uint64_t GetFreeBytes(const std::string& path) {
|
|
|
|
struct statvfs sb;
|
|
|
|
if (statvfs(path.c_str(), &sb) == 0) {
|
2018-09-18 22:30:21 +02:00
|
|
|
return (uint64_t)sb.f_bavail * sb.f_frsize;
|
2015-04-25 01:00:03 +02:00
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: borrowed from frameworks/native/libs/diskusage/ which should
|
|
|
|
// eventually be migrated into system/
|
2018-09-18 22:30:21 +02:00
|
|
|
static int64_t stat_size(struct stat* s) {
|
2015-04-25 01:00:03 +02:00
|
|
|
int64_t blksize = s->st_blksize;
|
|
|
|
// count actual blocks used instead of nominal file size
|
|
|
|
int64_t size = s->st_blocks * 512;
|
|
|
|
|
|
|
|
if (blksize) {
|
|
|
|
/* round up to filesystem block size */
|
|
|
|
size = (size + blksize - 1) & (~(blksize - 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: borrowed from frameworks/native/libs/diskusage/ which should
|
|
|
|
// eventually be migrated into system/
|
|
|
|
int64_t calculate_dir_size(int dfd) {
|
|
|
|
int64_t size = 0;
|
|
|
|
struct stat s;
|
2018-09-18 22:30:21 +02:00
|
|
|
DIR* d;
|
|
|
|
struct dirent* de;
|
2015-04-25 01:00:03 +02:00
|
|
|
|
|
|
|
d = fdopendir(dfd);
|
|
|
|
if (d == NULL) {
|
|
|
|
close(dfd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((de = readdir(d))) {
|
2018-09-18 22:30:21 +02:00
|
|
|
const char* name = de->d_name;
|
2015-04-25 01:00:03 +02:00
|
|
|
if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
|
|
|
|
size += stat_size(&s);
|
|
|
|
}
|
|
|
|
if (de->d_type == DT_DIR) {
|
|
|
|
int subfd;
|
|
|
|
|
|
|
|
/* always skip "." and ".." */
|
2020-11-03 00:31:56 +01:00
|
|
|
if (IsDotOrDotDot(*de)) continue;
|
2015-04-25 01:00:03 +02:00
|
|
|
|
2017-03-27 18:49:21 +02:00
|
|
|
subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
|
2015-04-25 01:00:03 +02:00
|
|
|
if (subfd >= 0) {
|
|
|
|
size += calculate_dir_size(subfd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
closedir(d);
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t GetTreeBytes(const std::string& path) {
|
2017-03-27 18:49:21 +02:00
|
|
|
int dirfd = open(path.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC);
|
2015-04-25 01:00:03 +02:00
|
|
|
if (dirfd < 0) {
|
|
|
|
PLOG(WARNING) << "Failed to open " << path;
|
|
|
|
return -1;
|
|
|
|
} else {
|
2018-05-30 04:05:16 +02:00
|
|
|
return calculate_dir_size(dirfd);
|
2015-04-25 01:00:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-11 15:31:24 +01:00
|
|
|
// TODO: Use a better way to determine if it's media provider app.
|
|
|
|
bool IsFuseDaemon(const pid_t pid) {
|
|
|
|
auto path = StringPrintf("/proc/%d/mounts", pid);
|
|
|
|
char* tmp;
|
|
|
|
if (lgetfilecon(path.c_str(), &tmp) < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool result = android::base::StartsWith(tmp, kMediaProviderAppCtx)
|
|
|
|
|| android::base::StartsWith(tmp, kMediaProviderCtx);
|
|
|
|
freecon(tmp);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-05-22 07:35:42 +02:00
|
|
|
bool IsFilesystemSupported(const std::string& fsType) {
|
|
|
|
std::string supported;
|
|
|
|
if (!ReadFileToString(kProcFilesystems, &supported)) {
|
|
|
|
PLOG(ERROR) << "Failed to read supported filesystems";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return supported.find(fsType + "\n") != std::string::npos;
|
|
|
|
}
|
|
|
|
|
2020-05-12 07:58:42 +02:00
|
|
|
bool IsSdcardfsUsed() {
|
|
|
|
return IsFilesystemSupported("sdcardfs") &&
|
|
|
|
base::GetBoolProperty(kExternalStorageSdcardfs, true);
|
|
|
|
}
|
|
|
|
|
2015-05-22 07:35:42 +02:00
|
|
|
status_t WipeBlockDevice(const std::string& path) {
|
|
|
|
status_t res = -1;
|
|
|
|
const char* c_path = path.c_str();
|
2018-05-23 10:50:46 +02:00
|
|
|
uint64_t range[2] = {0, 0};
|
2015-05-22 07:35:42 +02:00
|
|
|
|
|
|
|
int fd = TEMP_FAILURE_RETRY(open(c_path, O_RDWR | O_CLOEXEC));
|
|
|
|
if (fd == -1) {
|
|
|
|
PLOG(ERROR) << "Failed to open " << path;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2018-05-23 10:50:46 +02:00
|
|
|
if (GetBlockDevSize(fd, &range[1]) != OK) {
|
2015-05-22 07:35:42 +02:00
|
|
|
PLOG(ERROR) << "Failed to determine size of " << path;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG(INFO) << "About to discard " << range[1] << " on " << path;
|
|
|
|
if (ioctl(fd, BLKDISCARD, &range) == 0) {
|
|
|
|
LOG(INFO) << "Discard success on " << path;
|
|
|
|
res = 0;
|
|
|
|
} else {
|
|
|
|
PLOG(ERROR) << "Discard failure on " << path;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
close(fd);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2015-11-09 02:56:23 +01:00
|
|
|
static bool isValidFilename(const std::string& name) {
|
2018-09-18 22:30:21 +02:00
|
|
|
if (name.empty() || (name == ".") || (name == "..") || (name.find('/') != std::string::npos)) {
|
2015-11-09 02:56:23 +01:00
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-18 23:25:08 +02:00
|
|
|
std::string BuildKeyPath(const std::string& partGuid) {
|
|
|
|
return StringPrintf("%s/expand_%s.key", kKeyPath, partGuid.c_str());
|
|
|
|
}
|
|
|
|
|
2016-04-15 04:45:16 +02:00
|
|
|
std::string BuildDataSystemLegacyPath(userid_t userId) {
|
2017-10-09 19:55:21 +02:00
|
|
|
return StringPrintf("%s/system/users/%u", BuildDataPath("").c_str(), userId);
|
2016-04-15 04:45:16 +02:00
|
|
|
}
|
|
|
|
|
2015-11-09 02:56:23 +01:00
|
|
|
std::string BuildDataSystemCePath(userid_t userId) {
|
2017-10-09 19:55:21 +02:00
|
|
|
return StringPrintf("%s/system_ce/%u", BuildDataPath("").c_str(), userId);
|
2016-02-02 01:02:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string BuildDataSystemDePath(userid_t userId) {
|
2017-10-09 19:55:21 +02:00
|
|
|
return StringPrintf("%s/system_de/%u", BuildDataPath("").c_str(), userId);
|
2016-02-02 01:02:29 +01:00
|
|
|
}
|
|
|
|
|
2016-04-15 04:45:16 +02:00
|
|
|
std::string BuildDataMiscLegacyPath(userid_t userId) {
|
2017-10-09 19:55:21 +02:00
|
|
|
return StringPrintf("%s/misc/user/%u", BuildDataPath("").c_str(), userId);
|
2016-04-15 04:45:16 +02:00
|
|
|
}
|
|
|
|
|
2016-02-17 21:14:46 +01:00
|
|
|
// Keep in sync with installd (frameworks/native/cmds/installd/utils.h)
|
|
|
|
std::string BuildDataProfilesDePath(userid_t userId) {
|
2017-10-09 19:55:21 +02:00
|
|
|
return StringPrintf("%s/misc/profiles/cur/%u", BuildDataPath("").c_str(), userId);
|
2016-02-17 21:14:46 +01:00
|
|
|
}
|
|
|
|
|
2018-01-22 20:25:29 +01:00
|
|
|
std::string BuildDataVendorCePath(userid_t userId) {
|
|
|
|
return StringPrintf("%s/vendor_ce/%u", BuildDataPath("").c_str(), userId);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string BuildDataVendorDePath(userid_t userId) {
|
|
|
|
return StringPrintf("%s/vendor_de/%u", BuildDataPath("").c_str(), userId);
|
|
|
|
}
|
|
|
|
|
2017-10-09 19:55:21 +02:00
|
|
|
std::string BuildDataPath(const std::string& volumeUuid) {
|
2015-11-09 02:56:23 +01:00
|
|
|
// TODO: unify with installd path generation logic
|
2017-10-09 19:55:21 +02:00
|
|
|
if (volumeUuid.empty()) {
|
2015-11-09 02:56:23 +01:00
|
|
|
return "/data";
|
|
|
|
} else {
|
|
|
|
CHECK(isValidFilename(volumeUuid));
|
2017-10-09 19:55:21 +02:00
|
|
|
return StringPrintf("/mnt/expand/%s", volumeUuid.c_str());
|
2015-11-09 02:56:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-09 19:55:21 +02:00
|
|
|
std::string BuildDataMediaCePath(const std::string& volumeUuid, userid_t userId) {
|
2015-12-08 01:27:01 +01:00
|
|
|
// TODO: unify with installd path generation logic
|
|
|
|
std::string data(BuildDataPath(volumeUuid));
|
|
|
|
return StringPrintf("%s/media/%u", data.c_str(), userId);
|
|
|
|
}
|
|
|
|
|
2022-03-07 21:27:06 +01:00
|
|
|
std::string BuildDataMiscCePath(const std::string& volumeUuid, userid_t userId) {
|
|
|
|
return StringPrintf("%s/misc_ce/%u", BuildDataPath(volumeUuid).c_str(), userId);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string BuildDataMiscDePath(const std::string& volumeUuid, userid_t userId) {
|
|
|
|
return StringPrintf("%s/misc_de/%u", BuildDataPath(volumeUuid).c_str(), userId);
|
|
|
|
}
|
|
|
|
|
2017-10-09 19:55:21 +02:00
|
|
|
std::string BuildDataUserCePath(const std::string& volumeUuid, userid_t userId) {
|
2015-11-09 02:56:23 +01:00
|
|
|
// TODO: unify with installd path generation logic
|
|
|
|
std::string data(BuildDataPath(volumeUuid));
|
2017-04-11 18:09:00 +02:00
|
|
|
return StringPrintf("%s/user/%u", data.c_str(), userId);
|
2015-11-09 02:56:23 +01:00
|
|
|
}
|
|
|
|
|
2017-10-09 19:55:21 +02:00
|
|
|
std::string BuildDataUserDePath(const std::string& volumeUuid, userid_t userId) {
|
2015-11-09 02:56:23 +01:00
|
|
|
// TODO: unify with installd path generation logic
|
|
|
|
std::string data(BuildDataPath(volumeUuid));
|
|
|
|
return StringPrintf("%s/user_de/%u", data.c_str(), userId);
|
|
|
|
}
|
|
|
|
|
2015-06-24 20:49:24 +02:00
|
|
|
dev_t GetDevice(const std::string& path) {
|
|
|
|
struct stat sb;
|
|
|
|
if (stat(path.c_str(), &sb)) {
|
|
|
|
PLOG(WARNING) << "Failed to stat " << path;
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return sb.st_dev;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-11 07:33:25 +02:00
|
|
|
// Returns true if |path| exists and is a symlink.
|
|
|
|
bool IsSymlink(const std::string& path) {
|
|
|
|
struct stat stbuf;
|
|
|
|
return lstat(path.c_str(), &stbuf) == 0 && S_ISLNK(stbuf.st_mode);
|
|
|
|
}
|
|
|
|
|
2020-11-06 04:58:26 +01:00
|
|
|
// Returns true if |path1| names the same existing file or directory as |path2|.
|
|
|
|
bool IsSameFile(const std::string& path1, const std::string& path2) {
|
|
|
|
struct stat stbuf1, stbuf2;
|
|
|
|
if (stat(path1.c_str(), &stbuf1) != 0 || stat(path2.c_str(), &stbuf2) != 0) return false;
|
|
|
|
return stbuf1.st_ino == stbuf2.st_ino && stbuf1.st_dev == stbuf2.st_dev;
|
|
|
|
}
|
|
|
|
|
2016-07-16 00:20:22 +02:00
|
|
|
status_t RestoreconRecursive(const std::string& path) {
|
2018-09-21 19:54:54 +02:00
|
|
|
LOG(DEBUG) << "Starting restorecon of " << path;
|
2016-07-16 00:20:22 +02:00
|
|
|
|
2017-06-16 02:13:56 +02:00
|
|
|
static constexpr const char* kRestoreconString = "selinux.restorecon_recursive";
|
|
|
|
|
|
|
|
android::base::SetProperty(kRestoreconString, "");
|
|
|
|
android::base::SetProperty(kRestoreconString, path);
|
|
|
|
|
|
|
|
android::base::WaitForProperty(kRestoreconString, path);
|
2016-07-16 00:20:22 +02:00
|
|
|
|
2018-09-21 19:54:54 +02:00
|
|
|
LOG(DEBUG) << "Finished restorecon of " << path;
|
2016-07-16 00:20:22 +02:00
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2017-10-07 02:02:53 +02:00
|
|
|
bool Readlinkat(int dirfd, const std::string& path, std::string* result) {
|
|
|
|
// Shamelessly borrowed from android::base::Readlink()
|
|
|
|
result->clear();
|
|
|
|
|
|
|
|
// Most Linux file systems (ext2 and ext4, say) limit symbolic links to
|
|
|
|
// 4095 bytes. Since we'll copy out into the string anyway, it doesn't
|
|
|
|
// waste memory to just start there. We add 1 so that we can recognize
|
|
|
|
// whether it actually fit (rather than being truncated to 4095).
|
|
|
|
std::vector<char> buf(4095 + 1);
|
|
|
|
while (true) {
|
|
|
|
ssize_t size = readlinkat(dirfd, path.c_str(), &buf[0], buf.size());
|
|
|
|
// Unrecoverable error?
|
2018-09-18 22:30:21 +02:00
|
|
|
if (size == -1) return false;
|
2017-10-07 02:02:53 +02:00
|
|
|
// It fit! (If size == buf.size(), it may have been truncated.)
|
|
|
|
if (static_cast<size_t>(size) < buf.size()) {
|
|
|
|
result->assign(&buf[0], size);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Double our buffer and try again.
|
|
|
|
buf.resize(buf.size() * 2);
|
2016-01-29 06:33:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-15 01:35:03 +02:00
|
|
|
static unsigned int GetMajorBlockVirtioBlk() {
|
|
|
|
std::string devices;
|
|
|
|
if (!ReadFileToString(kProcDevices, &devices)) {
|
|
|
|
PLOG(ERROR) << "Unable to open /proc/devices";
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool blockSection = false;
|
|
|
|
for (auto line : android::base::Split(devices, "\n")) {
|
|
|
|
if (line == "Block devices:") {
|
|
|
|
blockSection = true;
|
|
|
|
} else if (line == "Character devices:") {
|
|
|
|
blockSection = false;
|
|
|
|
} else if (blockSection) {
|
|
|
|
auto tokens = android::base::Split(line, " ");
|
|
|
|
if (tokens.size() == 2 && tokens[1] == "virtblk") {
|
|
|
|
return std::stoul(tokens[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsVirtioBlkDevice(unsigned int major) {
|
|
|
|
// Most virtualized platforms expose block devices with the virtio-blk
|
|
|
|
// block device driver. Unfortunately, this driver does not use a fixed
|
|
|
|
// major number, but relies on the kernel to assign one from a specific
|
|
|
|
// range of block majors, which are allocated for "LOCAL/EXPERIMENAL USE"
|
|
|
|
// per Documentation/devices.txt. This is true even for the latest Linux
|
|
|
|
// kernel (4.4; see init() in drivers/block/virtio_blk.c).
|
|
|
|
static unsigned int kMajorBlockVirtioBlk = GetMajorBlockVirtioBlk();
|
|
|
|
return kMajorBlockVirtioBlk && major == kMajorBlockVirtioBlk;
|
2016-01-08 10:36:47 +01:00
|
|
|
}
|
|
|
|
|
2019-01-17 08:04:07 +01:00
|
|
|
status_t UnmountTree(const std::string& mountPoint) {
|
2019-02-06 21:39:19 +01:00
|
|
|
if (TEMP_FAILURE_RETRY(umount2(mountPoint.c_str(), MNT_DETACH)) < 0 && errno != EINVAL &&
|
|
|
|
errno != ENOENT) {
|
2019-01-17 08:04:07 +01:00
|
|
|
PLOG(ERROR) << "Failed to unmount " << mountPoint;
|
2018-09-25 23:22:07 +02:00
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2020-11-03 00:31:56 +01:00
|
|
|
bool IsDotOrDotDot(const struct dirent& ent) {
|
|
|
|
return strcmp(ent.d_name, ".") == 0 || strcmp(ent.d_name, "..") == 0;
|
|
|
|
}
|
|
|
|
|
2018-09-18 22:07:45 +02:00
|
|
|
static status_t delete_dir_contents(DIR* dir) {
|
|
|
|
// Shamelessly borrowed from android::installd
|
|
|
|
int dfd = dirfd(dir);
|
|
|
|
if (dfd < 0) {
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
2019-01-17 22:38:10 +01:00
|
|
|
status_t result = OK;
|
2018-09-18 22:07:45 +02:00
|
|
|
struct dirent* de;
|
|
|
|
while ((de = readdir(dir))) {
|
|
|
|
const char* name = de->d_name;
|
|
|
|
if (de->d_type == DT_DIR) {
|
|
|
|
/* always skip "." and ".." */
|
2020-11-03 00:31:56 +01:00
|
|
|
if (IsDotOrDotDot(*de)) continue;
|
2018-09-18 22:07:45 +02:00
|
|
|
|
|
|
|
android::base::unique_fd subfd(
|
|
|
|
openat(dfd, name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC));
|
|
|
|
if (subfd.get() == -1) {
|
|
|
|
PLOG(ERROR) << "Couldn't openat " << name;
|
|
|
|
result = -errno;
|
|
|
|
continue;
|
|
|
|
}
|
2018-11-05 22:47:28 +01:00
|
|
|
std::unique_ptr<DIR, decltype(&closedir)> subdirp(
|
|
|
|
android::base::Fdopendir(std::move(subfd)), closedir);
|
2018-09-18 22:07:45 +02:00
|
|
|
if (!subdirp) {
|
|
|
|
PLOG(ERROR) << "Couldn't fdopendir " << name;
|
|
|
|
result = -errno;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
result = delete_dir_contents(subdirp.get());
|
|
|
|
if (unlinkat(dfd, name, AT_REMOVEDIR) < 0) {
|
|
|
|
PLOG(ERROR) << "Couldn't unlinkat " << name;
|
|
|
|
result = -errno;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (unlinkat(dfd, name, 0) < 0) {
|
|
|
|
PLOG(ERROR) << "Couldn't unlinkat " << name;
|
|
|
|
result = -errno;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
status_t DeleteDirContentsAndDir(const std::string& pathname) {
|
2019-02-23 02:03:02 +01:00
|
|
|
status_t res = DeleteDirContents(pathname);
|
2018-09-18 22:07:45 +02:00
|
|
|
if (res < 0) {
|
|
|
|
return res;
|
|
|
|
}
|
2019-02-25 21:21:23 +01:00
|
|
|
if (TEMP_FAILURE_RETRY(rmdir(pathname.c_str())) < 0 && errno != ENOENT) {
|
2018-09-18 22:07:45 +02:00
|
|
|
PLOG(ERROR) << "rmdir failed on " << pathname;
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
LOG(VERBOSE) << "Success: rmdir on " << pathname;
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2019-02-23 02:03:02 +01:00
|
|
|
status_t DeleteDirContents(const std::string& pathname) {
|
|
|
|
// Shamelessly borrowed from android::installd
|
|
|
|
std::unique_ptr<DIR, decltype(&closedir)> dirp(opendir(pathname.c_str()), closedir);
|
|
|
|
if (!dirp) {
|
|
|
|
if (errno == ENOENT) {
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
PLOG(ERROR) << "Failed to opendir " << pathname;
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
return delete_dir_contents(dirp.get());
|
|
|
|
}
|
|
|
|
|
2018-10-30 23:59:24 +01:00
|
|
|
// TODO(118708649): fix duplication with init/util.h
|
|
|
|
status_t WaitForFile(const char* filename, std::chrono::nanoseconds timeout) {
|
|
|
|
android::base::Timer t;
|
|
|
|
while (t.duration() < timeout) {
|
|
|
|
struct stat sb;
|
|
|
|
if (stat(filename, &sb) != -1) {
|
|
|
|
LOG(INFO) << "wait for '" << filename << "' took " << t;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
std::this_thread::sleep_for(10ms);
|
|
|
|
}
|
|
|
|
LOG(WARNING) << "wait for '" << filename << "' timed out and took " << t;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-02-17 00:59:17 +01:00
|
|
|
bool pathExists(const std::string& path) {
|
|
|
|
return access(path.c_str(), F_OK) == 0;
|
|
|
|
}
|
|
|
|
|
2018-12-08 00:36:09 +01:00
|
|
|
bool FsyncDirectory(const std::string& dirname) {
|
|
|
|
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dirname.c_str(), O_RDONLY | O_CLOEXEC)));
|
|
|
|
if (fd == -1) {
|
|
|
|
PLOG(ERROR) << "Failed to open " << dirname;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (fsync(fd) == -1) {
|
|
|
|
if (errno == EROFS || errno == EINVAL) {
|
|
|
|
PLOG(WARNING) << "Skip fsync " << dirname
|
|
|
|
<< " on a file system does not support synchronization";
|
|
|
|
} else {
|
|
|
|
PLOG(ERROR) << "Failed to fsync " << dirname;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-02-17 00:59:17 +01:00
|
|
|
bool FsyncParentDirectory(const std::string& path) {
|
|
|
|
return FsyncDirectory(android::base::Dirname(path));
|
|
|
|
}
|
|
|
|
|
2021-02-17 00:59:17 +01:00
|
|
|
// Creates all parent directories of |path| that don't already exist. Assigns
|
|
|
|
// the specified |mode| to any new directories, and also fsync()s their parent
|
|
|
|
// directories so that the new directories get written to disk right away.
|
|
|
|
bool MkdirsSync(const std::string& path, mode_t mode) {
|
|
|
|
if (path[0] != '/') {
|
|
|
|
LOG(ERROR) << "MkdirsSync() needs an absolute path, but got " << path;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
std::vector<std::string> components = android::base::Split(android::base::Dirname(path), "/");
|
|
|
|
|
|
|
|
std::string current_dir = "/";
|
|
|
|
for (const std::string& component : components) {
|
|
|
|
if (component.empty()) continue;
|
|
|
|
|
|
|
|
std::string parent_dir = current_dir;
|
|
|
|
if (current_dir != "/") current_dir += "/";
|
|
|
|
current_dir += component;
|
|
|
|
|
|
|
|
if (!pathExists(current_dir)) {
|
|
|
|
if (mkdir(current_dir.c_str(), mode) != 0) {
|
|
|
|
PLOG(ERROR) << "Failed to create " << current_dir;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!FsyncDirectory(parent_dir)) return false;
|
|
|
|
LOG(DEBUG) << "Created directory " << current_dir;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-03-26 10:18:09 +01:00
|
|
|
bool writeStringToFile(const std::string& payload, const std::string& filename) {
|
|
|
|
android::base::unique_fd fd(TEMP_FAILURE_RETRY(
|
|
|
|
open(filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0666)));
|
|
|
|
if (fd == -1) {
|
|
|
|
PLOG(ERROR) << "Failed to open " << filename;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!android::base::WriteStringToFd(payload, fd)) {
|
|
|
|
PLOG(ERROR) << "Failed to write to " << filename;
|
|
|
|
unlink(filename.c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// fsync as close won't guarantee flush data
|
|
|
|
// see close(2), fsync(2) and b/68901441
|
|
|
|
if (fsync(fd) == -1) {
|
|
|
|
if (errno == EROFS || errno == EINVAL) {
|
|
|
|
PLOG(WARNING) << "Skip fsync " << filename
|
|
|
|
<< " on a file system does not support synchronization";
|
|
|
|
} else {
|
|
|
|
PLOG(ERROR) << "Failed to fsync " << filename;
|
|
|
|
unlink(filename.c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-04-29 07:49:41 +02:00
|
|
|
status_t AbortFuseConnections() {
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
|
2022-11-02 14:26:53 +01:00
|
|
|
static constexpr const char* kFuseConnections = "/sys/fs/fuse/connections";
|
|
|
|
|
|
|
|
std::error_code ec;
|
|
|
|
for (const auto& itEntry : fs::directory_iterator(kFuseConnections, ec)) {
|
2020-08-07 04:31:21 +02:00
|
|
|
std::string fsPath = itEntry.path().string() + "/filesystem";
|
|
|
|
std::string fs;
|
|
|
|
|
|
|
|
// Virtiofs is on top of fuse and there isn't any user space daemon.
|
|
|
|
// Android user space doesn't manage it.
|
|
|
|
if (android::base::ReadFileToString(fsPath, &fs, false) &&
|
|
|
|
android::base::Trim(fs) == "virtiofs") {
|
|
|
|
LOG(INFO) << "Ignore virtiofs connection entry " << itEntry.path().string();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-04-29 07:49:41 +02:00
|
|
|
std::string abortPath = itEntry.path().string() + "/abort";
|
|
|
|
LOG(DEBUG) << "Aborting fuse connection entry " << abortPath;
|
|
|
|
bool ret = writeStringToFile("1", abortPath);
|
|
|
|
if (!ret) {
|
|
|
|
LOG(WARNING) << "Failed to write to " << abortPath;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-02 14:26:53 +01:00
|
|
|
if (ec) {
|
|
|
|
LOG(WARNING) << "Failed to iterate through " << kFuseConnections << ": " << ec.message();
|
|
|
|
return -ec.value();
|
|
|
|
}
|
|
|
|
|
2020-04-29 07:49:41 +02:00
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2020-02-11 15:31:24 +01:00
|
|
|
status_t EnsureDirExists(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
|
|
|
|
if (access(path.c_str(), F_OK) != 0) {
|
|
|
|
PLOG(WARNING) << "Dir does not exist: " << path;
|
|
|
|
if (fs_prepare_dir(path.c_str(), mode, uid, gid) != 0) {
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2020-06-29 11:53:34 +02:00
|
|
|
// Gets the sysfs path for parameters of the backing device info (bdi)
|
|
|
|
static std::string getBdiPathForMount(const std::string& mount) {
|
|
|
|
// First figure out MAJOR:MINOR of mount. Simplest way is to stat the path.
|
|
|
|
struct stat info;
|
|
|
|
if (stat(mount.c_str(), &info) != 0) {
|
|
|
|
PLOG(ERROR) << "Failed to stat " << mount;
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
unsigned int maj = major(info.st_dev);
|
|
|
|
unsigned int min = minor(info.st_dev);
|
|
|
|
|
|
|
|
return StringPrintf("/sys/class/bdi/%u:%u", maj, min);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Configures max_ratio for the FUSE filesystem.
|
|
|
|
void ConfigureMaxDirtyRatioForFuse(const std::string& fuse_mount, unsigned int max_ratio) {
|
|
|
|
LOG(INFO) << "Configuring max_ratio of " << fuse_mount << " fuse filesystem to " << max_ratio;
|
|
|
|
if (max_ratio > 100) {
|
|
|
|
LOG(ERROR) << "Invalid max_ratio: " << max_ratio;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::string fuseBdiPath = getBdiPathForMount(fuse_mount);
|
|
|
|
if (fuseBdiPath == "") {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
std::string max_ratio_file = StringPrintf("%s/max_ratio", fuseBdiPath.c_str());
|
|
|
|
unique_fd fd(TEMP_FAILURE_RETRY(open(max_ratio_file.c_str(), O_WRONLY | O_CLOEXEC)));
|
|
|
|
if (fd.get() == -1) {
|
|
|
|
PLOG(ERROR) << "Failed to open " << max_ratio_file;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
LOG(INFO) << "Writing " << max_ratio << " to " << max_ratio_file;
|
|
|
|
if (!WriteStringToFd(std::to_string(max_ratio), fd)) {
|
|
|
|
PLOG(ERROR) << "Failed to write to " << max_ratio_file;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-12 13:59:45 +02:00
|
|
|
// Configures read ahead property of the fuse filesystem with the mount point |fuse_mount| by
|
|
|
|
// writing |read_ahead_kb| to the /sys/class/bdi/MAJOR:MINOR/read_ahead_kb.
|
|
|
|
void ConfigureReadAheadForFuse(const std::string& fuse_mount, size_t read_ahead_kb) {
|
|
|
|
LOG(INFO) << "Configuring read_ahead of " << fuse_mount << " fuse filesystem to "
|
|
|
|
<< read_ahead_kb << "kb";
|
2020-06-29 11:53:34 +02:00
|
|
|
std::string fuseBdiPath = getBdiPathForMount(fuse_mount);
|
|
|
|
if (fuseBdiPath == "") {
|
2020-06-12 13:59:45 +02:00
|
|
|
return;
|
|
|
|
}
|
2020-06-29 11:53:34 +02:00
|
|
|
// We found the bdi path for our filesystem, time to configure read ahead!
|
|
|
|
std::string read_ahead_file = StringPrintf("%s/read_ahead_kb", fuseBdiPath.c_str());
|
2020-06-12 13:59:45 +02:00
|
|
|
unique_fd fd(TEMP_FAILURE_RETRY(open(read_ahead_file.c_str(), O_WRONLY | O_CLOEXEC)));
|
|
|
|
if (fd.get() == -1) {
|
|
|
|
PLOG(ERROR) << "Failed to open " << read_ahead_file;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
LOG(INFO) << "Writing " << read_ahead_kb << " to " << read_ahead_file;
|
|
|
|
if (!WriteStringToFd(std::to_string(read_ahead_kb), fd)) {
|
|
|
|
PLOG(ERROR) << "Failed to write to " << read_ahead_file;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-25 15:37:38 +02:00
|
|
|
status_t MountUserFuse(userid_t user_id, const std::string& absolute_lower_path,
|
|
|
|
const std::string& relative_upper_path, android::base::unique_fd* fuse_fd) {
|
|
|
|
std::string pre_fuse_path(StringPrintf("/mnt/user/%d", user_id));
|
|
|
|
std::string fuse_path(
|
|
|
|
StringPrintf("%s/%s", pre_fuse_path.c_str(), relative_upper_path.c_str()));
|
|
|
|
|
|
|
|
std::string pre_pass_through_path(StringPrintf("/mnt/pass_through/%d", user_id));
|
|
|
|
std::string pass_through_path(
|
|
|
|
StringPrintf("%s/%s", pre_pass_through_path.c_str(), relative_upper_path.c_str()));
|
2019-07-19 17:46:53 +02:00
|
|
|
|
2020-01-22 19:22:29 +01:00
|
|
|
// Ensure that /mnt/user is 0700. With FUSE, apps don't need access to /mnt/user paths directly.
|
|
|
|
// Without FUSE however, apps need /mnt/user access so /mnt/user in init.rc is 0755 until here
|
2020-01-29 03:44:46 +01:00
|
|
|
auto result = PrepareDir("/mnt/user", 0750, AID_ROOT, AID_MEDIA_RW);
|
2020-01-22 19:22:29 +01:00
|
|
|
if (result != android::OK) {
|
|
|
|
PLOG(ERROR) << "Failed to prepare directory /mnt/user";
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-01-05 03:11:47 +01:00
|
|
|
// Shell is neither AID_ROOT nor AID_EVERYBODY. Since it equally needs 'execute' access to
|
|
|
|
// /mnt/user/0 to 'adb shell ls /sdcard' for instance, we set the uid bit of /mnt/user/0 to
|
|
|
|
// AID_SHELL. This gives shell access along with apps running as group everybody (user 0 apps)
|
|
|
|
// These bits should be consistent with what is set in zygote in
|
|
|
|
// com_android_internal_os_Zygote#MountEmulatedStorage on volume bind mount during app fork
|
2020-01-22 19:22:29 +01:00
|
|
|
result = PrepareDir(pre_fuse_path, 0710, user_id ? AID_ROOT : AID_SHELL,
|
2020-01-05 03:11:47 +01:00
|
|
|
multiuser_get_uid(user_id, AID_EVERYBODY));
|
2019-09-25 15:37:38 +02:00
|
|
|
if (result != android::OK) {
|
|
|
|
PLOG(ERROR) << "Failed to prepare directory " << pre_fuse_path;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = PrepareDir(fuse_path, 0700, AID_ROOT, AID_ROOT);
|
|
|
|
if (result != android::OK) {
|
|
|
|
PLOG(ERROR) << "Failed to prepare directory " << fuse_path;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-01-31 17:00:58 +01:00
|
|
|
result = PrepareDir(pre_pass_through_path, 0710, AID_ROOT, AID_MEDIA_RW);
|
2019-09-25 15:37:38 +02:00
|
|
|
if (result != android::OK) {
|
|
|
|
PLOG(ERROR) << "Failed to prepare directory " << pre_pass_through_path;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-01-31 17:00:58 +01:00
|
|
|
result = PrepareDir(pass_through_path, 0710, AID_ROOT, AID_MEDIA_RW);
|
2019-07-19 17:46:53 +02:00
|
|
|
if (result != android::OK) {
|
2019-09-25 15:37:38 +02:00
|
|
|
PLOG(ERROR) << "Failed to prepare directory " << pass_through_path;
|
2019-07-19 17:46:53 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-09-25 15:37:38 +02:00
|
|
|
if (relative_upper_path == "emulated") {
|
2019-11-15 12:44:12 +01:00
|
|
|
std::string linkpath(StringPrintf("/mnt/user/%d/self", user_id));
|
|
|
|
result = PrepareDir(linkpath, 0755, AID_ROOT, AID_ROOT);
|
2019-09-25 15:37:38 +02:00
|
|
|
if (result != android::OK) {
|
2019-11-15 12:44:12 +01:00
|
|
|
PLOG(ERROR) << "Failed to prepare directory " << linkpath;
|
2019-09-25 15:37:38 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2019-11-15 12:44:12 +01:00
|
|
|
linkpath += "/primary";
|
2020-01-17 02:21:24 +01:00
|
|
|
Symlink("/storage/emulated/" + std::to_string(user_id), linkpath);
|
2020-01-08 12:09:47 +01:00
|
|
|
|
|
|
|
std::string pass_through_linkpath(StringPrintf("/mnt/pass_through/%d/self", user_id));
|
2020-01-31 17:00:58 +01:00
|
|
|
result = PrepareDir(pass_through_linkpath, 0710, AID_ROOT, AID_MEDIA_RW);
|
2020-01-08 12:09:47 +01:00
|
|
|
if (result != android::OK) {
|
|
|
|
PLOG(ERROR) << "Failed to prepare directory " << pass_through_linkpath;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
pass_through_linkpath += "/primary";
|
2020-01-17 02:21:24 +01:00
|
|
|
Symlink("/storage/emulated/" + std::to_string(user_id), pass_through_linkpath);
|
2019-09-25 15:37:38 +02:00
|
|
|
}
|
|
|
|
|
2019-08-29 16:22:42 +02:00
|
|
|
// Open fuse fd.
|
|
|
|
fuse_fd->reset(open("/dev/fuse", O_RDWR | O_CLOEXEC));
|
|
|
|
if (fuse_fd->get() == -1) {
|
2019-07-19 17:46:53 +02:00
|
|
|
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,",
|
2019-08-29 16:22:42 +02:00
|
|
|
fuse_fd->get());
|
2019-07-19 17:46:53 +02:00
|
|
|
|
2019-09-25 15:37:38 +02:00
|
|
|
result = TEMP_FAILURE_RETRY(mount("/dev/fuse", fuse_path.c_str(), "fuse",
|
|
|
|
MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME | MS_LAZYTIME,
|
|
|
|
opts.c_str()));
|
|
|
|
if (result != 0) {
|
|
|
|
PLOG(ERROR) << "Failed to mount " << fuse_path;
|
2019-07-19 17:46:53 +02:00
|
|
|
return -errno;
|
|
|
|
}
|
2019-11-28 11:53:53 +01:00
|
|
|
|
2020-05-12 07:58:42 +02:00
|
|
|
if (IsSdcardfsUsed()) {
|
2020-01-06 09:48:14 +01:00
|
|
|
std::string sdcardfs_path(
|
|
|
|
StringPrintf("/mnt/runtime/full/%s", relative_upper_path.c_str()));
|
|
|
|
|
|
|
|
LOG(INFO) << "Bind mounting " << sdcardfs_path << " to " << pass_through_path;
|
|
|
|
return BindMount(sdcardfs_path, pass_through_path);
|
|
|
|
} else {
|
|
|
|
LOG(INFO) << "Bind mounting " << absolute_lower_path << " to " << pass_through_path;
|
|
|
|
return BindMount(absolute_lower_path, pass_through_path);
|
|
|
|
}
|
2019-09-25 15:37:38 +02:00
|
|
|
}
|
|
|
|
|
2019-11-28 11:53:53 +01:00
|
|
|
status_t UnmountUserFuse(userid_t user_id, const std::string& absolute_lower_path,
|
|
|
|
const std::string& relative_upper_path) {
|
|
|
|
std::string fuse_path(StringPrintf("/mnt/user/%d/%s", user_id, relative_upper_path.c_str()));
|
|
|
|
std::string pass_through_path(
|
|
|
|
StringPrintf("/mnt/pass_through/%d/%s", user_id, relative_upper_path.c_str()));
|
|
|
|
|
|
|
|
LOG(INFO) << "Unmounting fuse path " << fuse_path;
|
2019-09-25 15:37:38 +02:00
|
|
|
android::status_t result = ForceUnmount(fuse_path);
|
|
|
|
if (result != android::OK) {
|
|
|
|
// TODO(b/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.
|
|
|
|
PLOG(ERROR) << "Failed to unmount. Trying MNT_DETACH " << fuse_path << " ...";
|
|
|
|
if (umount2(fuse_path.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH) && errno != EINVAL &&
|
|
|
|
errno != ENOENT) {
|
|
|
|
PLOG(ERROR) << "Failed to unmount with MNT_DETACH " << fuse_path;
|
|
|
|
return -errno;
|
|
|
|
}
|
2019-11-28 11:56:13 +01:00
|
|
|
result = android::OK;
|
2019-09-25 15:37:38 +02:00
|
|
|
}
|
2019-11-28 11:56:13 +01:00
|
|
|
rmdir(fuse_path.c_str());
|
|
|
|
|
2020-11-05 10:34:28 +01:00
|
|
|
LOG(INFO) << "Unmounting pass_through_path " << pass_through_path;
|
|
|
|
auto status = ForceUnmount(pass_through_path);
|
|
|
|
if (status != android::OK) {
|
|
|
|
LOG(ERROR) << "Failed to unmount " << pass_through_path;
|
|
|
|
}
|
|
|
|
rmdir(pass_through_path.c_str());
|
|
|
|
|
2019-09-25 15:37:38 +02:00
|
|
|
return result;
|
2019-07-19 17:46:53 +02:00
|
|
|
}
|
|
|
|
|
2020-01-31 15:23:09 +01:00
|
|
|
status_t PrepareAndroidDirs(const std::string& volumeRoot) {
|
|
|
|
std::string androidDir = volumeRoot + kAndroidDir;
|
|
|
|
std::string androidDataDir = volumeRoot + kAppDataDir;
|
|
|
|
std::string androidObbDir = volumeRoot + kAppObbDir;
|
2020-02-07 17:39:31 +01:00
|
|
|
std::string androidMediaDir = volumeRoot + kAppMediaDir;
|
2020-01-31 15:23:09 +01:00
|
|
|
|
2020-05-12 07:58:42 +02:00
|
|
|
bool useSdcardFs = IsSdcardfsUsed();
|
2020-01-31 15:23:09 +01:00
|
|
|
|
2020-02-18 10:41:37 +01:00
|
|
|
// mode 0771 + sticky bit for inheriting GIDs
|
|
|
|
mode_t mode = S_IRWXU | S_IRWXG | S_IXOTH | S_ISGID;
|
|
|
|
if (fs_prepare_dir(androidDir.c_str(), mode, AID_MEDIA_RW, AID_MEDIA_RW) != 0) {
|
2020-01-31 15:23:09 +01:00
|
|
|
PLOG(ERROR) << "Failed to create " << androidDir;
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
gid_t dataGid = useSdcardFs ? AID_MEDIA_RW : AID_EXT_DATA_RW;
|
2020-02-18 10:41:37 +01:00
|
|
|
if (fs_prepare_dir(androidDataDir.c_str(), mode, AID_MEDIA_RW, dataGid) != 0) {
|
2020-01-31 15:23:09 +01:00
|
|
|
PLOG(ERROR) << "Failed to create " << androidDataDir;
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
gid_t obbGid = useSdcardFs ? AID_MEDIA_RW : AID_EXT_OBB_RW;
|
2020-02-18 10:41:37 +01:00
|
|
|
if (fs_prepare_dir(androidObbDir.c_str(), mode, AID_MEDIA_RW, obbGid) != 0) {
|
2020-01-31 15:23:09 +01:00
|
|
|
PLOG(ERROR) << "Failed to create " << androidObbDir;
|
|
|
|
return -errno;
|
|
|
|
}
|
2020-02-18 13:44:59 +01:00
|
|
|
// Some other apps, like installers, have write access to the OBB directory
|
|
|
|
// to pre-download them. To make sure newly created folders in this directory
|
|
|
|
// have the right permissions, set a default ACL.
|
2020-06-16 14:58:52 +02:00
|
|
|
SetDefaultAcl(androidObbDir, mode, AID_MEDIA_RW, obbGid, {});
|
2020-01-31 15:23:09 +01:00
|
|
|
|
2020-02-18 10:41:37 +01:00
|
|
|
if (fs_prepare_dir(androidMediaDir.c_str(), mode, AID_MEDIA_RW, AID_MEDIA_RW) != 0) {
|
2020-02-07 17:39:31 +01:00
|
|
|
PLOG(ERROR) << "Failed to create " << androidMediaDir;
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
2020-01-31 15:23:09 +01:00
|
|
|
return OK;
|
|
|
|
}
|
2021-10-19 07:33:15 +02:00
|
|
|
|
|
|
|
namespace ab = android::base;
|
|
|
|
|
|
|
|
static ab::unique_fd openDirFd(int parentFd, const char* name) {
|
|
|
|
return ab::unique_fd{::openat(parentFd, name, O_CLOEXEC | O_DIRECTORY | O_PATH | O_NOFOLLOW)};
|
|
|
|
}
|
|
|
|
|
|
|
|
static ab::unique_fd openAbsolutePathFd(std::string_view path) {
|
|
|
|
if (path.empty() || path[0] != '/') {
|
|
|
|
errno = EINVAL;
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
if (path == "/") {
|
|
|
|
return openDirFd(-1, "/");
|
|
|
|
}
|
|
|
|
|
|
|
|
// first component is special - it includes the leading slash
|
|
|
|
auto next = path.find('/', 1);
|
|
|
|
auto component = std::string(path.substr(0, next));
|
|
|
|
if (component == "..") {
|
|
|
|
errno = EINVAL;
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
auto fd = openDirFd(-1, component.c_str());
|
|
|
|
if (!fd.ok()) {
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
path.remove_prefix(std::min(next + 1, path.size()));
|
|
|
|
while (next != path.npos && !path.empty()) {
|
|
|
|
next = path.find('/');
|
|
|
|
component.assign(path.substr(0, next));
|
|
|
|
fd = openDirFd(fd, component.c_str());
|
|
|
|
if (!fd.ok()) {
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
path.remove_prefix(std::min(next + 1, path.size()));
|
|
|
|
}
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<android::base::unique_fd, std::string> OpenDirInProcfs(std::string_view path) {
|
|
|
|
auto fd = openAbsolutePathFd(path);
|
|
|
|
if (!fd.ok()) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
auto linkPath = std::string("/proc/self/fd/") += std::to_string(fd.get());
|
|
|
|
return {std::move(fd), std::move(linkPath)};
|
|
|
|
}
|
|
|
|
|
2023-04-14 19:06:49 +02:00
|
|
|
static bool IsPropertySet(const char* name, bool& value) {
|
|
|
|
if (base::GetProperty(name, "") == "") return false;
|
|
|
|
|
|
|
|
value = base::GetBoolProperty(name, false);
|
|
|
|
LOG(INFO) << "fuse-bpf is " << (value ? "enabled" : "disabled") << " because of property "
|
|
|
|
<< name;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-03-23 19:27:59 +01:00
|
|
|
bool IsFuseBpfEnabled() {
|
2023-04-14 19:06:49 +02:00
|
|
|
// This logic is reproduced in packages/providers/MediaProvider/jni/FuseDaemon.cpp
|
|
|
|
// so changes made here must be reflected there
|
|
|
|
bool enabled = false;
|
|
|
|
|
|
|
|
if (IsPropertySet("ro.fuse.bpf.is_running", enabled)) return enabled;
|
|
|
|
|
|
|
|
if (!IsPropertySet("persist.sys.fuse.bpf.override", enabled) &&
|
|
|
|
!IsPropertySet("ro.fuse.bpf.enabled", enabled)) {
|
|
|
|
// If the kernel has fuse-bpf, /sys/fs/fuse/features/fuse_bpf will exist and have the
|
|
|
|
// contents 'supported\n' - see fs/fuse/inode.c in the kernel source
|
|
|
|
std::string contents;
|
|
|
|
const char* filename = "/sys/fs/fuse/features/fuse_bpf";
|
|
|
|
if (!base::ReadFileToString(filename, &contents)) {
|
|
|
|
LOG(INFO) << "fuse-bpf is disabled because " << filename << " cannot be read";
|
|
|
|
enabled = false;
|
|
|
|
} else if (contents == "supported\n") {
|
|
|
|
LOG(INFO) << "fuse-bpf is enabled because " << filename << " reads 'supported'";
|
|
|
|
enabled = true;
|
|
|
|
} else {
|
|
|
|
LOG(INFO) << "fuse-bpf is disabled because " << filename
|
|
|
|
<< " does not read 'supported'";
|
|
|
|
enabled = false;
|
|
|
|
}
|
2022-03-23 19:27:59 +01:00
|
|
|
}
|
2023-04-14 19:06:49 +02:00
|
|
|
|
|
|
|
std::string value = enabled ? "true" : "false";
|
|
|
|
LOG(INFO) << "Setting ro.fuse.bpf.is_running to " << value;
|
|
|
|
base::SetProperty("ro.fuse.bpf.is_running", value);
|
|
|
|
return enabled;
|
2022-03-23 19:27:59 +01:00
|
|
|
}
|
|
|
|
|
2015-03-03 06:01:40 +01:00
|
|
|
} // namespace vold
|
|
|
|
} // namespace android
|