2009-10-11 02:22:08 +02:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2008 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.
|
|
|
|
*/
|
|
|
|
|
2017-09-22 01:08:43 +02:00
|
|
|
#define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
|
|
|
|
|
2015-01-02 22:28:28 +01:00
|
|
|
#include <dirent.h>
|
2009-10-11 02:22:08 +02:00
|
|
|
#include <errno.h>
|
2009-12-13 19:40:18 +01:00
|
|
|
#include <fcntl.h>
|
2020-05-07 17:01:33 +02:00
|
|
|
#include <limits.h>
|
2015-01-02 22:28:28 +01:00
|
|
|
#include <mntent.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/mount.h>
|
2010-01-06 19:33:53 +01:00
|
|
|
#include <sys/stat.h>
|
2017-05-18 18:08:24 +02:00
|
|
|
#include <sys/sysmacros.h>
|
2018-09-19 00:14:18 +02:00
|
|
|
#include <sys/types.h>
|
2015-06-24 20:49:24 +02:00
|
|
|
#include <sys/wait.h>
|
2015-01-02 22:28:28 +01:00
|
|
|
#include <unistd.h>
|
2018-09-18 22:07:45 +02:00
|
|
|
#include <array>
|
2010-01-06 19:33:53 +01:00
|
|
|
|
2009-12-13 19:40:18 +01:00
|
|
|
#include <linux/kdev_t.h>
|
2009-10-11 02:22:08 +02:00
|
|
|
|
2023-08-11 19:27:24 +02:00
|
|
|
#include <android-base/file.h>
|
2015-12-05 00:50:53 +01:00
|
|
|
#include <android-base/logging.h>
|
2017-10-24 02:12:31 +02:00
|
|
|
#include <android-base/parseint.h>
|
2017-10-07 02:02:53 +02:00
|
|
|
#include <android-base/properties.h>
|
2015-12-05 00:50:53 +01:00
|
|
|
#include <android-base/stringprintf.h>
|
2017-10-24 02:12:31 +02:00
|
|
|
#include <android-base/strings.h>
|
2019-11-27 11:53:51 +01:00
|
|
|
#include <async_safe/log.h>
|
2017-10-24 02:12:31 +02:00
|
|
|
|
2013-09-18 02:24:38 +02:00
|
|
|
#include <cutils/fs.h>
|
2017-09-22 01:08:43 +02:00
|
|
|
#include <utils/Trace.h>
|
2009-10-11 02:22:08 +02:00
|
|
|
|
2014-02-04 16:53:00 +01:00
|
|
|
#include <selinux/android.h>
|
|
|
|
|
2009-10-12 20:32:47 +02:00
|
|
|
#include <sysutils/NetlinkEvent.h>
|
|
|
|
|
2012-04-04 02:23:01 +02:00
|
|
|
#include <private/android_filesystem_config.h>
|
|
|
|
|
2018-10-23 22:06:55 +02:00
|
|
|
#include <fscrypt/fscrypt.h>
|
2023-08-11 19:27:24 +02:00
|
|
|
#include <libdm/dm.h>
|
2017-10-24 23:54:43 +02:00
|
|
|
|
2018-11-01 04:59:47 +01:00
|
|
|
#include "AppFuseUtil.h"
|
2018-10-23 22:06:55 +02:00
|
|
|
#include "FsCrypt.h"
|
2017-10-24 23:54:43 +02:00
|
|
|
#include "Loop.h"
|
|
|
|
#include "NetlinkManager.h"
|
2010-02-17 02:12:00 +01:00
|
|
|
#include "Process.h"
|
2017-10-24 23:54:43 +02:00
|
|
|
#include "Utils.h"
|
2018-09-18 22:07:45 +02:00
|
|
|
#include "VoldNativeService.h"
|
2015-02-04 05:29:15 +01:00
|
|
|
#include "VoldUtil.h"
|
2017-10-24 23:54:43 +02:00
|
|
|
#include "VolumeManager.h"
|
|
|
|
#include "fs/Ext4.h"
|
|
|
|
#include "fs/Vfat.h"
|
|
|
|
#include "model/EmulatedVolume.h"
|
|
|
|
#include "model/ObbVolume.h"
|
2019-09-25 15:37:38 +02:00
|
|
|
#include "model/PrivateVolume.h"
|
2023-07-27 23:15:00 +02:00
|
|
|
#include "model/PublicVolume.h"
|
2018-10-29 00:52:56 +01:00
|
|
|
#include "model/StubVolume.h"
|
2010-01-09 16:08:06 +01:00
|
|
|
|
2019-02-14 20:09:51 +01:00
|
|
|
using android::OK;
|
2018-08-01 19:24:13 +02:00
|
|
|
using android::base::GetBoolProperty;
|
2018-09-20 19:09:27 +02:00
|
|
|
using android::base::StartsWith;
|
2018-08-01 19:24:13 +02:00
|
|
|
using android::base::StringAppendF;
|
2015-03-14 00:09:20 +01:00
|
|
|
using android::base::StringPrintf;
|
2017-09-11 18:32:01 +02:00
|
|
|
using android::base::unique_fd;
|
2019-02-06 21:39:19 +01:00
|
|
|
using android::vold::BindMount;
|
2019-02-14 20:09:51 +01:00
|
|
|
using android::vold::CreateDir;
|
2019-02-23 02:03:02 +01:00
|
|
|
using android::vold::DeleteDirContents;
|
2019-02-06 21:39:19 +01:00
|
|
|
using android::vold::DeleteDirContentsAndDir;
|
2020-02-11 15:31:24 +01:00
|
|
|
using android::vold::EnsureDirExists;
|
Support bind mounting volumes into other volume's mountpoint.
With the way the FUSE mount point are currently setup for emulated
volumes, there can be multiple paths that serve the same files on the
lower filesystem; eg
* /mnt/user/0/emulated/0/Android
* /mnt/user/10/emulated/0/Android
both refer to the same file on the lower filesystem:
* /data/media/0/Android
this is normally not a problem, because cross-user file access is not
allowed, and so the FUSE daemon won't serve files for other users.
With clone profiles this is no longer true however, as their volumes
are accessible by each other.
So, it can happen that an app running in clone profile 10 accesses
"/mnt/user/10/emulated/0/Android", which would be served by the FUSE
daemon for the user 10 filesystem.
At the same time, an app running in the owner profile 0 accesses
"mnt/user/0/emulated/0/Android", which would be served by the FUSE
daemon for the user 0 filesystem.
This can cause page cache inconsistencies, because multiple FUSE daemons
can be running on top of the same entries in the lower filesystem.
To prevent this, use bind mounts to make sure that cross-profile
accesses actually end up in the FUSE daemon to which the volume
belongs: "/mnt/user/10/emulated/0" is bind-mounted to
"/mnt/user/0/emulated/0", and vice-versa.
Bug: 228271997
Test: manual
Change-Id: Iefcbc813670628b329a1a5d408b6126b84991e09
2022-07-12 10:11:02 +02:00
|
|
|
using android::vold::GetFuseMountPathForUser;
|
2020-01-31 15:23:09 +01:00
|
|
|
using android::vold::IsFilesystemSupported;
|
2020-05-12 07:58:42 +02:00
|
|
|
using android::vold::IsSdcardfsUsed;
|
2020-05-15 01:35:03 +02:00
|
|
|
using android::vold::IsVirtioBlkDevice;
|
2020-01-31 15:23:09 +01:00
|
|
|
using android::vold::PrepareAndroidDirs;
|
2020-02-10 23:48:11 +01:00
|
|
|
using android::vold::PrepareAppDirFromRoot;
|
2019-09-25 15:37:38 +02:00
|
|
|
using android::vold::PrivateVolume;
|
2023-07-27 23:15:00 +02:00
|
|
|
using android::vold::PublicVolume;
|
2019-02-06 21:39:19 +01:00
|
|
|
using android::vold::Symlink;
|
|
|
|
using android::vold::Unlink;
|
|
|
|
using android::vold::UnmountTree;
|
2018-12-12 21:43:38 +01:00
|
|
|
using android::vold::VoldNativeService;
|
2019-09-25 15:37:38 +02:00
|
|
|
using android::vold::VolumeBase;
|
2015-03-14 00:09:20 +01:00
|
|
|
|
2017-03-26 06:49:13 +02:00
|
|
|
static const char* kPathVirtualDisk = "/data/misc/vold/virtual_disk";
|
|
|
|
|
|
|
|
static const char* kPropVirtualDisk = "persist.sys.virtual_disk";
|
|
|
|
|
2018-08-01 19:24:13 +02:00
|
|
|
static const std::string kEmptyString("");
|
|
|
|
|
2017-03-26 06:49:13 +02:00
|
|
|
/* 512MiB is large enough for testing purposes */
|
|
|
|
static const unsigned int kSizeVirtualDisk = 536870912;
|
2015-03-14 00:09:20 +01:00
|
|
|
|
|
|
|
static const unsigned int kMajorBlockMmc = 179;
|
|
|
|
|
2020-02-11 15:31:24 +01:00
|
|
|
using ScanProcCallback = bool(*)(uid_t uid, pid_t pid, int nsFd, const char* name, void* params);
|
|
|
|
|
2018-09-19 00:14:18 +02:00
|
|
|
VolumeManager* VolumeManager::sInstance = NULL;
|
2009-10-11 02:22:08 +02:00
|
|
|
|
2018-09-19 00:14:18 +02:00
|
|
|
VolumeManager* VolumeManager::Instance() {
|
|
|
|
if (!sInstance) sInstance = new VolumeManager();
|
2009-10-11 02:22:08 +02:00
|
|
|
return sInstance;
|
|
|
|
}
|
|
|
|
|
|
|
|
VolumeManager::VolumeManager() {
|
2010-03-12 22:32:47 +01:00
|
|
|
mDebug = false;
|
2017-09-11 18:32:01 +02:00
|
|
|
mNextObbId = 0;
|
2020-02-04 08:07:21 +01:00
|
|
|
mNextStubId = 0;
|
2017-12-15 06:15:20 +01:00
|
|
|
// For security reasons, assume that a secure keyguard is
|
|
|
|
// showing until we hear otherwise
|
|
|
|
mSecureKeyguardShowing = true;
|
2009-10-11 02:22:08 +02:00
|
|
|
}
|
|
|
|
|
2018-09-19 00:14:18 +02:00
|
|
|
VolumeManager::~VolumeManager() {}
|
2010-03-12 22:32:47 +01:00
|
|
|
|
2017-03-26 06:49:13 +02:00
|
|
|
int VolumeManager::updateVirtualDisk() {
|
2017-09-22 01:08:43 +02:00
|
|
|
ATRACE_NAME("VolumeManager::updateVirtualDisk");
|
2018-09-20 19:09:27 +02:00
|
|
|
if (GetBoolProperty(kPropVirtualDisk, false)) {
|
2017-03-26 06:49:13 +02:00
|
|
|
if (access(kPathVirtualDisk, F_OK) != 0) {
|
|
|
|
Loop::createImageFile(kPathVirtualDisk, kSizeVirtualDisk / 512);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mVirtualDisk == nullptr) {
|
|
|
|
if (Loop::create(kPathVirtualDisk, mVirtualDiskPath) != 0) {
|
|
|
|
LOG(ERROR) << "Failed to create virtual disk";
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct stat buf;
|
|
|
|
if (stat(mVirtualDiskPath.c_str(), &buf) < 0) {
|
|
|
|
PLOG(ERROR) << "Failed to stat " << mVirtualDiskPath;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-09-19 00:14:18 +02:00
|
|
|
auto disk = new android::vold::Disk(
|
|
|
|
"virtual", buf.st_rdev, "virtual",
|
|
|
|
android::vold::Disk::Flags::kAdoptable | android::vold::Disk::Flags::kSd);
|
2017-03-26 06:49:13 +02:00
|
|
|
mVirtualDisk = std::shared_ptr<android::vold::Disk>(disk);
|
2017-12-15 06:15:20 +01:00
|
|
|
handleDiskAdded(mVirtualDisk);
|
2017-03-26 06:49:13 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (mVirtualDisk != nullptr) {
|
|
|
|
dev_t device = mVirtualDisk->getDevice();
|
2017-12-15 06:15:20 +01:00
|
|
|
handleDiskRemoved(device);
|
2017-03-26 06:49:13 +02:00
|
|
|
|
|
|
|
Loop::destroyByDevice(mVirtualDiskPath.c_str());
|
|
|
|
mVirtualDisk = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (access(kPathVirtualDisk, F_OK) == 0) {
|
|
|
|
unlink(kPathVirtualDisk);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-04-18 02:35:20 +02:00
|
|
|
int VolumeManager::setDebug(bool enable) {
|
2010-03-12 22:32:47 +01:00
|
|
|
mDebug = enable;
|
2015-04-18 02:35:20 +02:00
|
|
|
return 0;
|
2010-03-12 22:32:47 +01:00
|
|
|
}
|
|
|
|
|
2009-10-11 02:22:08 +02:00
|
|
|
int VolumeManager::start() {
|
2017-09-22 01:08:43 +02:00
|
|
|
ATRACE_NAME("VolumeManager::start");
|
|
|
|
|
2015-03-14 00:09:20 +01:00
|
|
|
// Always start from a clean slate by unmounting everything in
|
|
|
|
// directories that we own, in case we crashed.
|
2015-03-31 19:35:33 +02:00
|
|
|
unmountAll();
|
2015-03-14 00:09:20 +01:00
|
|
|
|
2017-09-11 18:32:01 +02:00
|
|
|
Loop::destroyAll();
|
|
|
|
|
2015-03-14 00:09:20 +01:00
|
|
|
// Assume that we always have an emulated volume on internal
|
|
|
|
// storage; the framework will decide if it should be mounted.
|
2019-09-25 15:37:38 +02:00
|
|
|
CHECK(mInternalEmulatedVolumes.empty());
|
|
|
|
|
|
|
|
auto vol = std::shared_ptr<android::vold::VolumeBase>(
|
|
|
|
new android::vold::EmulatedVolume("/data/media", 0));
|
|
|
|
vol->setMountUserId(0);
|
|
|
|
vol->create();
|
|
|
|
mInternalEmulatedVolumes.push_back(vol);
|
2015-03-14 00:09:20 +01:00
|
|
|
|
2017-03-26 06:49:13 +02:00
|
|
|
// Consider creating a virtual disk
|
|
|
|
updateVirtualDisk();
|
|
|
|
|
2009-10-11 02:22:08 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-09-19 00:14:18 +02:00
|
|
|
void VolumeManager::handleBlockEvent(NetlinkEvent* evt) {
|
2015-04-21 21:14:17 +02:00
|
|
|
std::lock_guard<std::mutex> lock(mLock);
|
|
|
|
|
2015-04-18 02:35:20 +02:00
|
|
|
if (mDebug) {
|
2018-09-21 19:54:54 +02:00
|
|
|
LOG(DEBUG) << "----------------";
|
|
|
|
LOG(DEBUG) << "handleBlockEvent with action " << (int)evt->getAction();
|
2015-04-18 02:35:20 +02:00
|
|
|
evt->dump();
|
|
|
|
}
|
2009-10-11 02:22:08 +02:00
|
|
|
|
2018-09-19 00:14:18 +02:00
|
|
|
std::string eventPath(evt->findParam("DEVPATH") ? evt->findParam("DEVPATH") : "");
|
|
|
|
std::string devType(evt->findParam("DEVTYPE") ? evt->findParam("DEVTYPE") : "");
|
2015-03-14 00:09:20 +01:00
|
|
|
|
|
|
|
if (devType != "disk") return;
|
|
|
|
|
2017-09-19 02:19:28 +02:00
|
|
|
int major = std::stoi(evt->findParam("MAJOR"));
|
|
|
|
int minor = std::stoi(evt->findParam("MINOR"));
|
2015-03-14 00:09:20 +01:00
|
|
|
dev_t device = makedev(major, minor);
|
|
|
|
|
|
|
|
switch (evt->getAction()) {
|
2018-09-19 00:14:18 +02:00
|
|
|
case NetlinkEvent::Action::kAdd: {
|
|
|
|
for (const auto& source : mDiskSources) {
|
|
|
|
if (source->matches(eventPath)) {
|
|
|
|
// For now, assume that MMC and virtio-blk (the latter is
|
2020-05-15 01:35:03 +02:00
|
|
|
// specific to virtual platforms; see Utils.cpp for details)
|
|
|
|
// devices are SD, and that everything else is USB
|
2018-09-19 00:14:18 +02:00
|
|
|
int flags = source->getFlags();
|
2020-05-15 01:35:03 +02:00
|
|
|
if (major == kMajorBlockMmc || IsVirtioBlkDevice(major)) {
|
2018-09-19 00:14:18 +02:00
|
|
|
flags |= android::vold::Disk::Flags::kSd;
|
|
|
|
} else {
|
|
|
|
flags |= android::vold::Disk::Flags::kUsb;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto disk =
|
|
|
|
new android::vold::Disk(eventPath, device, source->getNickname(), flags);
|
|
|
|
handleDiskAdded(std::shared_ptr<android::vold::Disk>(disk));
|
|
|
|
break;
|
2015-03-14 00:09:20 +01:00
|
|
|
}
|
|
|
|
}
|
2018-09-19 00:14:18 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NetlinkEvent::Action::kChange: {
|
2021-07-28 03:58:57 +02:00
|
|
|
LOG(VERBOSE) << "Disk at " << major << ":" << minor << " changed";
|
2018-09-19 00:14:18 +02:00
|
|
|
handleDiskChanged(device);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NetlinkEvent::Action::kRemove: {
|
|
|
|
handleDiskRemoved(device);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: {
|
|
|
|
LOG(WARNING) << "Unexpected block event action " << (int)evt->getAction();
|
|
|
|
break;
|
2015-03-14 00:09:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-10-11 02:22:08 +02:00
|
|
|
|
2017-12-15 06:15:20 +01:00
|
|
|
void VolumeManager::handleDiskAdded(const std::shared_ptr<android::vold::Disk>& disk) {
|
|
|
|
// For security reasons, if secure keyguard is showing, wait
|
|
|
|
// until the user unlocks the device to actually touch it
|
2020-01-03 14:36:45 +01:00
|
|
|
// Additionally, wait until user 0 is actually started, since we need
|
|
|
|
// the user to be up before we can mount a FUSE daemon to handle the disk.
|
|
|
|
bool userZeroStarted = mStartedUsers.find(0) != mStartedUsers.end();
|
2017-12-15 06:15:20 +01:00
|
|
|
if (mSecureKeyguardShowing) {
|
|
|
|
LOG(INFO) << "Found disk at " << disk->getEventPath()
|
2018-09-19 00:14:18 +02:00
|
|
|
<< " but delaying scan due to secure keyguard";
|
2017-12-15 06:15:20 +01:00
|
|
|
mPendingDisks.push_back(disk);
|
2020-01-03 14:36:45 +01:00
|
|
|
} else if (!userZeroStarted) {
|
|
|
|
LOG(INFO) << "Found disk at " << disk->getEventPath()
|
|
|
|
<< " but delaying scan due to user zero not having started";
|
|
|
|
mPendingDisks.push_back(disk);
|
2017-12-15 06:15:20 +01:00
|
|
|
} else {
|
|
|
|
disk->create();
|
|
|
|
mDisks.push_back(disk);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VolumeManager::handleDiskChanged(dev_t device) {
|
|
|
|
for (const auto& disk : mDisks) {
|
|
|
|
if (disk->getDevice() == device) {
|
|
|
|
disk->readMetadata();
|
|
|
|
disk->readPartitions();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// For security reasons, we ignore all pending disks, since
|
|
|
|
// we'll scan them once the device is unlocked
|
|
|
|
}
|
|
|
|
|
|
|
|
void VolumeManager::handleDiskRemoved(dev_t device) {
|
|
|
|
auto i = mDisks.begin();
|
|
|
|
while (i != mDisks.end()) {
|
|
|
|
if ((*i)->getDevice() == device) {
|
|
|
|
(*i)->destroy();
|
|
|
|
i = mDisks.erase(i);
|
|
|
|
} else {
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
auto j = mPendingDisks.begin();
|
|
|
|
while (j != mPendingDisks.end()) {
|
|
|
|
if ((*j)->getDevice() == device) {
|
|
|
|
j = mPendingDisks.erase(j);
|
|
|
|
} else {
|
|
|
|
++j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-14 00:09:20 +01:00
|
|
|
void VolumeManager::addDiskSource(const std::shared_ptr<DiskSource>& diskSource) {
|
2017-01-20 20:52:33 +01:00
|
|
|
std::lock_guard<std::mutex> lock(mLock);
|
2015-03-14 00:09:20 +01:00
|
|
|
mDiskSources.push_back(diskSource);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<android::vold::Disk> VolumeManager::findDisk(const std::string& id) {
|
|
|
|
for (auto disk : mDisks) {
|
|
|
|
if (disk->getId() == id) {
|
|
|
|
return disk;
|
|
|
|
}
|
2009-10-11 02:22:08 +02:00
|
|
|
}
|
2015-03-14 00:09:20 +01:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<android::vold::VolumeBase> VolumeManager::findVolume(const std::string& id) {
|
2019-09-25 15:37:38 +02:00
|
|
|
for (const auto& vol : mInternalEmulatedVolumes) {
|
|
|
|
if (vol->getId() == id) {
|
|
|
|
return vol;
|
|
|
|
}
|
2015-03-14 00:09:20 +01:00
|
|
|
}
|
2016-07-27 23:11:02 +02:00
|
|
|
for (const auto& disk : mDisks) {
|
2015-03-14 00:09:20 +01:00
|
|
|
auto vol = disk->findVolume(id);
|
|
|
|
if (vol != nullptr) {
|
|
|
|
return vol;
|
|
|
|
}
|
|
|
|
}
|
2017-09-11 18:32:01 +02:00
|
|
|
for (const auto& vol : mObbVolumes) {
|
|
|
|
if (vol->getId() == id) {
|
|
|
|
return vol;
|
|
|
|
}
|
|
|
|
}
|
2015-03-14 00:09:20 +01:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-12-18 17:42:08 +01:00
|
|
|
void VolumeManager::listVolumes(android::vold::VolumeBase::Type type,
|
|
|
|
std::list<std::string>& list) const {
|
2015-06-26 23:02:09 +02:00
|
|
|
list.clear();
|
2016-07-27 23:11:02 +02:00
|
|
|
for (const auto& disk : mDisks) {
|
2015-06-26 23:02:09 +02:00
|
|
|
disk->listVolumes(type, list);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-02 00:36:55 +02:00
|
|
|
bool VolumeManager::forgetPartition(const std::string& partGuid, const std::string& fsUuid) {
|
2015-06-18 23:25:08 +02:00
|
|
|
std::string normalizedGuid;
|
|
|
|
if (android::vold::NormalizeHex(partGuid, normalizedGuid)) {
|
|
|
|
LOG(WARNING) << "Invalid GUID " << partGuid;
|
2023-08-02 00:36:55 +02:00
|
|
|
return false;
|
2015-06-18 23:25:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string keyPath = android::vold::BuildKeyPath(normalizedGuid);
|
|
|
|
if (unlink(keyPath.c_str()) != 0) {
|
|
|
|
LOG(ERROR) << "Failed to unlink " << keyPath;
|
2023-08-02 00:36:55 +02:00
|
|
|
return false;
|
2015-06-18 23:25:08 +02:00
|
|
|
}
|
2023-08-02 00:36:55 +02:00
|
|
|
return true;
|
2015-06-18 23:25:08 +02:00
|
|
|
}
|
|
|
|
|
2019-09-25 15:37:38 +02:00
|
|
|
void VolumeManager::destroyEmulatedVolumesForUser(userid_t userId) {
|
|
|
|
// Destroy and remove all unstacked EmulatedVolumes for the user
|
|
|
|
auto i = mInternalEmulatedVolumes.begin();
|
|
|
|
while (i != mInternalEmulatedVolumes.end()) {
|
|
|
|
auto vol = *i;
|
|
|
|
if (vol->getMountUserId() == userId) {
|
|
|
|
vol->destroy();
|
|
|
|
i = mInternalEmulatedVolumes.erase(i);
|
|
|
|
} else {
|
|
|
|
i++;
|
|
|
|
}
|
2018-09-18 22:07:45 +02:00
|
|
|
}
|
|
|
|
|
2019-09-25 15:37:38 +02:00
|
|
|
// Destroy and remove all stacked EmulatedVolumes for the user on each mounted private volume
|
|
|
|
std::list<std::string> private_vols;
|
|
|
|
listVolumes(VolumeBase::Type::kPrivate, private_vols);
|
|
|
|
for (const std::string& id : private_vols) {
|
|
|
|
PrivateVolume* pvol = static_cast<PrivateVolume*>(findVolume(id).get());
|
|
|
|
std::list<std::shared_ptr<VolumeBase>> vols_to_remove;
|
|
|
|
if (pvol->getState() == VolumeBase::State::kMounted) {
|
|
|
|
for (const auto& vol : pvol->getVolumes()) {
|
|
|
|
if (vol->getMountUserId() == userId) {
|
|
|
|
vols_to_remove.push_back(vol);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const auto& vol : vols_to_remove) {
|
|
|
|
vol->destroy();
|
|
|
|
pvol->removeVolume(vol);
|
|
|
|
}
|
|
|
|
} // else EmulatedVolumes will be destroyed on VolumeBase#unmount
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VolumeManager::createEmulatedVolumesForUser(userid_t userId) {
|
|
|
|
// Create unstacked EmulatedVolumes for the user
|
|
|
|
auto vol = std::shared_ptr<android::vold::VolumeBase>(
|
|
|
|
new android::vold::EmulatedVolume("/data/media", userId));
|
|
|
|
vol->setMountUserId(userId);
|
|
|
|
mInternalEmulatedVolumes.push_back(vol);
|
|
|
|
vol->create();
|
|
|
|
|
|
|
|
// Create stacked EmulatedVolumes for the user on each PrivateVolume
|
|
|
|
std::list<std::string> private_vols;
|
|
|
|
listVolumes(VolumeBase::Type::kPrivate, private_vols);
|
|
|
|
for (const std::string& id : private_vols) {
|
|
|
|
PrivateVolume* pvol = static_cast<PrivateVolume*>(findVolume(id).get());
|
|
|
|
if (pvol->getState() == VolumeBase::State::kMounted) {
|
|
|
|
auto evol =
|
|
|
|
std::shared_ptr<android::vold::VolumeBase>(new android::vold::EmulatedVolume(
|
|
|
|
pvol->getPath() + "/media", pvol->getRawDevice(), pvol->getFsUuid(),
|
|
|
|
userId));
|
|
|
|
evol->setMountUserId(userId);
|
|
|
|
pvol->addVolume(evol);
|
|
|
|
evol->create();
|
|
|
|
} // else EmulatedVolumes will be created per user when on PrivateVolume#doMount
|
|
|
|
}
|
2018-09-18 22:07:45 +02:00
|
|
|
}
|
|
|
|
|
Support bind mounting volumes into other volume's mountpoint.
With the way the FUSE mount point are currently setup for emulated
volumes, there can be multiple paths that serve the same files on the
lower filesystem; eg
* /mnt/user/0/emulated/0/Android
* /mnt/user/10/emulated/0/Android
both refer to the same file on the lower filesystem:
* /data/media/0/Android
this is normally not a problem, because cross-user file access is not
allowed, and so the FUSE daemon won't serve files for other users.
With clone profiles this is no longer true however, as their volumes
are accessible by each other.
So, it can happen that an app running in clone profile 10 accesses
"/mnt/user/10/emulated/0/Android", which would be served by the FUSE
daemon for the user 10 filesystem.
At the same time, an app running in the owner profile 0 accesses
"mnt/user/0/emulated/0/Android", which would be served by the FUSE
daemon for the user 0 filesystem.
This can cause page cache inconsistencies, because multiple FUSE daemons
can be running on top of the same entries in the lower filesystem.
To prevent this, use bind mounts to make sure that cross-profile
accesses actually end up in the FUSE daemon to which the volume
belongs: "/mnt/user/10/emulated/0" is bind-mounted to
"/mnt/user/0/emulated/0", and vice-versa.
Bug: 228271997
Test: manual
Change-Id: Iefcbc813670628b329a1a5d408b6126b84991e09
2022-07-12 10:11:02 +02:00
|
|
|
userid_t VolumeManager::getSharedStorageUser(userid_t userId) {
|
|
|
|
if (mSharedStorageUser.find(userId) == mSharedStorageUser.end()) {
|
|
|
|
return USER_UNKNOWN;
|
|
|
|
}
|
|
|
|
return mSharedStorageUser.at(userId);
|
|
|
|
}
|
|
|
|
|
|
|
|
int VolumeManager::onUserAdded(userid_t userId, int userSerialNumber,
|
|
|
|
userid_t sharesStorageWithUserId) {
|
2019-09-25 15:37:38 +02:00
|
|
|
LOG(INFO) << "onUserAdded: " << userId;
|
|
|
|
|
2015-06-10 18:42:01 +02:00
|
|
|
mAddedUsers[userId] = userSerialNumber;
|
Support bind mounting volumes into other volume's mountpoint.
With the way the FUSE mount point are currently setup for emulated
volumes, there can be multiple paths that serve the same files on the
lower filesystem; eg
* /mnt/user/0/emulated/0/Android
* /mnt/user/10/emulated/0/Android
both refer to the same file on the lower filesystem:
* /data/media/0/Android
this is normally not a problem, because cross-user file access is not
allowed, and so the FUSE daemon won't serve files for other users.
With clone profiles this is no longer true however, as their volumes
are accessible by each other.
So, it can happen that an app running in clone profile 10 accesses
"/mnt/user/10/emulated/0/Android", which would be served by the FUSE
daemon for the user 10 filesystem.
At the same time, an app running in the owner profile 0 accesses
"mnt/user/0/emulated/0/Android", which would be served by the FUSE
daemon for the user 0 filesystem.
This can cause page cache inconsistencies, because multiple FUSE daemons
can be running on top of the same entries in the lower filesystem.
To prevent this, use bind mounts to make sure that cross-profile
accesses actually end up in the FUSE daemon to which the volume
belongs: "/mnt/user/10/emulated/0" is bind-mounted to
"/mnt/user/0/emulated/0", and vice-versa.
Bug: 228271997
Test: manual
Change-Id: Iefcbc813670628b329a1a5d408b6126b84991e09
2022-07-12 10:11:02 +02:00
|
|
|
if (sharesStorageWithUserId != USER_UNKNOWN) {
|
|
|
|
mSharedStorageUser[userId] = sharesStorageWithUserId;
|
|
|
|
}
|
2015-06-10 18:42:01 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int VolumeManager::onUserRemoved(userid_t userId) {
|
2019-09-25 15:37:38 +02:00
|
|
|
LOG(INFO) << "onUserRemoved: " << userId;
|
|
|
|
|
2019-11-14 17:19:05 +01:00
|
|
|
onUserStopped(userId);
|
2015-06-10 18:42:01 +02:00
|
|
|
mAddedUsers.erase(userId);
|
Support bind mounting volumes into other volume's mountpoint.
With the way the FUSE mount point are currently setup for emulated
volumes, there can be multiple paths that serve the same files on the
lower filesystem; eg
* /mnt/user/0/emulated/0/Android
* /mnt/user/10/emulated/0/Android
both refer to the same file on the lower filesystem:
* /data/media/0/Android
this is normally not a problem, because cross-user file access is not
allowed, and so the FUSE daemon won't serve files for other users.
With clone profiles this is no longer true however, as their volumes
are accessible by each other.
So, it can happen that an app running in clone profile 10 accesses
"/mnt/user/10/emulated/0/Android", which would be served by the FUSE
daemon for the user 10 filesystem.
At the same time, an app running in the owner profile 0 accesses
"mnt/user/0/emulated/0/Android", which would be served by the FUSE
daemon for the user 0 filesystem.
This can cause page cache inconsistencies, because multiple FUSE daemons
can be running on top of the same entries in the lower filesystem.
To prevent this, use bind mounts to make sure that cross-profile
accesses actually end up in the FUSE daemon to which the volume
belongs: "/mnt/user/10/emulated/0" is bind-mounted to
"/mnt/user/0/emulated/0", and vice-versa.
Bug: 228271997
Test: manual
Change-Id: Iefcbc813670628b329a1a5d408b6126b84991e09
2022-07-12 10:11:02 +02:00
|
|
|
mSharedStorageUser.erase(userId);
|
2015-06-10 18:42:01 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-04-29 19:46:35 +02:00
|
|
|
int VolumeManager::onUserStarted(userid_t userId) {
|
2019-09-25 15:37:38 +02:00
|
|
|
LOG(INFO) << "onUserStarted: " << userId;
|
2015-03-14 00:09:20 +01:00
|
|
|
|
2019-11-14 17:19:05 +01:00
|
|
|
if (mStartedUsers.find(userId) == mStartedUsers.end()) {
|
|
|
|
createEmulatedVolumesForUser(userId);
|
2023-07-27 23:15:00 +02:00
|
|
|
std::list<std::string> public_vols;
|
|
|
|
listVolumes(VolumeBase::Type::kPublic, public_vols);
|
|
|
|
for (const std::string& id : public_vols) {
|
|
|
|
PublicVolume* pvol = static_cast<PublicVolume*>(findVolume(id).get());
|
|
|
|
if (pvol->getState() != VolumeBase::State::kMounted) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (pvol->isVisible() == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
userid_t mountUserId = pvol->getMountUserId();
|
|
|
|
if (userId == mountUserId) {
|
|
|
|
// No need to bind mount for the user that owns the mount
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (mountUserId != VolumeManager::Instance()->getSharedStorageUser(userId)) {
|
|
|
|
// No need to bind if the user does not share storage with the mount owner
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
auto bindMountStatus = pvol->bindMountForUser(userId);
|
|
|
|
if (bindMountStatus != OK) {
|
|
|
|
LOG(ERROR) << "Bind Mounting Public Volume: " << pvol << " for user: " << userId
|
|
|
|
<< "Failed. Error: " << bindMountStatus;
|
|
|
|
}
|
|
|
|
}
|
2019-11-14 17:19:05 +01:00
|
|
|
}
|
|
|
|
|
2019-09-25 15:37:38 +02:00
|
|
|
mStartedUsers.insert(userId);
|
2020-01-03 14:36:45 +01:00
|
|
|
|
|
|
|
createPendingDisksIfNeeded();
|
2015-03-14 00:09:20 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-06-10 18:42:01 +02:00
|
|
|
int VolumeManager::onUserStopped(userid_t userId) {
|
2018-09-18 22:07:45 +02:00
|
|
|
LOG(VERBOSE) << "onUserStopped: " << userId;
|
2019-09-25 15:37:38 +02:00
|
|
|
|
2019-11-14 17:19:05 +01:00
|
|
|
if (mStartedUsers.find(userId) != mStartedUsers.end()) {
|
2019-09-25 15:37:38 +02:00
|
|
|
destroyEmulatedVolumesForUser(userId);
|
|
|
|
}
|
|
|
|
|
2015-06-10 18:42:01 +02:00
|
|
|
mStartedUsers.erase(userId);
|
2018-10-05 01:26:22 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-01-03 14:36:45 +01:00
|
|
|
void VolumeManager::createPendingDisksIfNeeded() {
|
|
|
|
bool userZeroStarted = mStartedUsers.find(0) != mStartedUsers.end();
|
|
|
|
if (!mSecureKeyguardShowing && userZeroStarted) {
|
|
|
|
// Now that secure keyguard has been dismissed and user 0 has
|
|
|
|
// started, process any pending disks
|
2017-12-15 06:15:20 +01:00
|
|
|
for (const auto& disk : mPendingDisks) {
|
|
|
|
disk->create();
|
|
|
|
mDisks.push_back(disk);
|
|
|
|
}
|
|
|
|
mPendingDisks.clear();
|
|
|
|
}
|
2020-01-03 14:36:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int VolumeManager::onSecureKeyguardStateChanged(bool isShowing) {
|
|
|
|
mSecureKeyguardShowing = isShowing;
|
|
|
|
createPendingDisksIfNeeded();
|
2017-12-15 06:15:20 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-11-27 11:53:51 +01:00
|
|
|
// This code is executed after a fork so it's very important that the set of
|
|
|
|
// methods we call here is strictly limited.
|
|
|
|
//
|
|
|
|
// TODO: Get rid of this guesswork altogether and instead exec a process
|
|
|
|
// immediately after fork to do our bindding for us.
|
|
|
|
static bool childProcess(const char* storageSource, const char* userSource, int nsFd,
|
2020-02-11 15:31:24 +01:00
|
|
|
const char* name) {
|
2019-11-27 11:53:51 +01:00
|
|
|
if (setns(nsFd, CLONE_NEWNS) != 0) {
|
2020-02-11 15:31:24 +01:00
|
|
|
async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to setns for %s :%s", name,
|
2019-11-27 11:53:51 +01:00
|
|
|
strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: Inlined from vold::UnmountTree here to avoid using PLOG methods and
|
|
|
|
// to also protect against future changes that may cause issues across a
|
|
|
|
// fork.
|
|
|
|
if (TEMP_FAILURE_RETRY(umount2("/storage/", MNT_DETACH)) < 0 && errno != EINVAL &&
|
|
|
|
errno != ENOENT) {
|
|
|
|
async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to unmount /storage/ :%s",
|
|
|
|
strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TEMP_FAILURE_RETRY(mount(storageSource, "/storage", NULL, MS_BIND | MS_REC, NULL)) == -1) {
|
|
|
|
async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to mount %s for %s :%s",
|
2020-02-11 15:31:24 +01:00
|
|
|
storageSource, name, strerror(errno));
|
2019-11-27 11:53:51 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TEMP_FAILURE_RETRY(mount(NULL, "/storage", NULL, MS_REC | MS_SLAVE, NULL)) == -1) {
|
|
|
|
async_safe_format_log(ANDROID_LOG_ERROR, "vold",
|
2020-02-11 15:31:24 +01:00
|
|
|
"Failed to set MS_SLAVE to /storage for %s :%s", name,
|
2019-11-27 11:53:51 +01:00
|
|
|
strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TEMP_FAILURE_RETRY(mount(userSource, "/storage/self", NULL, MS_BIND, NULL)) == -1) {
|
|
|
|
async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to mount %s for %s :%s",
|
2020-02-11 15:31:24 +01:00
|
|
|
userSource, name, strerror(errno));
|
2019-11-27 11:53:51 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-02-11 15:31:24 +01:00
|
|
|
// Fork the process and remount storage
|
|
|
|
bool forkAndRemountChild(uid_t uid, pid_t pid, int nsFd, const char* name, void* params) {
|
|
|
|
int32_t mountMode = *static_cast<int32_t*>(params);
|
|
|
|
std::string userSource;
|
|
|
|
std::string storageSource;
|
|
|
|
pid_t child;
|
|
|
|
// Need to fix these paths to account for when sdcardfs is gone
|
2018-12-14 02:40:28 +01:00
|
|
|
switch (mountMode) {
|
|
|
|
case VoldNativeService::REMOUNT_MODE_NONE:
|
2020-02-11 15:31:24 +01:00
|
|
|
return true;
|
2018-12-14 02:40:28 +01:00
|
|
|
case VoldNativeService::REMOUNT_MODE_DEFAULT:
|
2020-02-11 15:31:24 +01:00
|
|
|
storageSource = "/mnt/runtime/default";
|
2018-12-14 02:40:28 +01:00
|
|
|
break;
|
2021-02-24 13:33:25 +01:00
|
|
|
case VoldNativeService::REMOUNT_MODE_ANDROID_WRITABLE:
|
2019-04-12 22:55:28 +02:00
|
|
|
case VoldNativeService::REMOUNT_MODE_INSTALLER:
|
2020-02-11 15:31:24 +01:00
|
|
|
storageSource = "/mnt/runtime/write";
|
2018-12-14 02:40:28 +01:00
|
|
|
break;
|
2019-09-09 11:24:44 +02:00
|
|
|
case VoldNativeService::REMOUNT_MODE_PASS_THROUGH:
|
2020-02-11 15:31:24 +01:00
|
|
|
return true;
|
2018-12-14 02:40:28 +01:00
|
|
|
default:
|
|
|
|
PLOG(ERROR) << "Unknown mode " << std::to_string(mountMode);
|
2020-02-11 15:31:24 +01:00
|
|
|
return false;
|
2018-12-14 02:40:28 +01:00
|
|
|
}
|
2020-02-11 15:31:24 +01:00
|
|
|
LOG(DEBUG) << "Remounting " << uid << " as " << storageSource;
|
2015-06-24 20:49:24 +02:00
|
|
|
|
2020-02-11 15:31:24 +01:00
|
|
|
// Fork a child to mount user-specific symlink helper into place
|
|
|
|
userSource = StringPrintf("/mnt/user/%d", multiuser_get_user_id(uid));
|
|
|
|
if (!(child = fork())) {
|
|
|
|
if (childProcess(storageSource.c_str(), userSource.c_str(), nsFd, name)) {
|
|
|
|
_exit(0);
|
|
|
|
} else {
|
|
|
|
_exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (child == -1) {
|
|
|
|
PLOG(ERROR) << "Failed to fork";
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
TEMP_FAILURE_RETRY(waitpid(child, nullptr, 0));
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Helper function to scan all processes in /proc and call the callback if:
|
|
|
|
// 1). pid belongs to an app process
|
|
|
|
// 2). If input uid is 0 or it matches the process uid
|
|
|
|
// 3). If userId is not -1 or userId matches the process userId
|
|
|
|
bool scanProcProcesses(uid_t uid, userid_t userId, ScanProcCallback callback, void* params) {
|
2015-06-24 20:49:24 +02:00
|
|
|
DIR* dir;
|
|
|
|
struct dirent* de;
|
2017-10-07 02:02:53 +02:00
|
|
|
std::string rootName;
|
|
|
|
std::string pidName;
|
2023-08-25 08:38:12 +02:00
|
|
|
std::string exeName;
|
2015-06-24 20:49:24 +02:00
|
|
|
int pidFd;
|
|
|
|
int nsFd;
|
|
|
|
struct stat sb;
|
|
|
|
|
|
|
|
if (!(dir = opendir("/proc"))) {
|
2020-02-11 15:31:24 +01:00
|
|
|
async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to opendir");
|
|
|
|
return false;
|
2015-06-24 20:49:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Figure out root namespace to compare against below
|
2017-10-07 02:02:53 +02:00
|
|
|
if (!android::vold::Readlinkat(dirfd(dir), "1/ns/mnt", &rootName)) {
|
2020-02-11 15:31:24 +01:00
|
|
|
async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to read root namespace");
|
2015-06-24 20:49:24 +02:00
|
|
|
closedir(dir);
|
2020-02-11 15:31:24 +01:00
|
|
|
return false;
|
2015-06-24 20:49:24 +02:00
|
|
|
}
|
|
|
|
|
2020-02-11 15:31:24 +01:00
|
|
|
async_safe_format_log(ANDROID_LOG_INFO, "vold", "Start scanning all processes");
|
2015-06-24 20:49:24 +02:00
|
|
|
// Poke through all running PIDs look for apps running as UID
|
|
|
|
while ((de = readdir(dir))) {
|
2017-10-24 02:12:31 +02:00
|
|
|
pid_t pid;
|
|
|
|
if (de->d_type != DT_DIR) continue;
|
|
|
|
if (!android::base::ParseInt(de->d_name, &pid)) continue;
|
|
|
|
|
2015-06-24 20:49:24 +02:00
|
|
|
pidFd = -1;
|
|
|
|
nsFd = -1;
|
|
|
|
|
|
|
|
pidFd = openat(dirfd(dir), de->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
|
|
|
|
if (pidFd < 0) {
|
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
if (fstat(pidFd, &sb) != 0) {
|
2020-02-11 15:31:24 +01:00
|
|
|
async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to stat %s", de->d_name);
|
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
if (uid != 0 && sb.st_uid != uid) {
|
2015-06-24 20:49:24 +02:00
|
|
|
goto next;
|
|
|
|
}
|
2020-02-11 15:31:24 +01:00
|
|
|
if (userId != static_cast<userid_t>(-1) && multiuser_get_user_id(sb.st_uid) != userId) {
|
2015-06-24 20:49:24 +02:00
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Matches so far, but refuse to touch if in root namespace
|
2017-10-07 02:02:53 +02:00
|
|
|
if (!android::vold::Readlinkat(pidFd, "ns/mnt", &pidName)) {
|
2020-02-11 15:31:24 +01:00
|
|
|
async_safe_format_log(ANDROID_LOG_ERROR, "vold",
|
|
|
|
"Failed to read namespacefor %s", de->d_name);
|
2015-06-24 20:49:24 +02:00
|
|
|
goto next;
|
|
|
|
}
|
2017-10-07 02:02:53 +02:00
|
|
|
if (rootName == pidName) {
|
2015-06-24 20:49:24 +02:00
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
|
2023-08-25 08:38:12 +02:00
|
|
|
// Some early native processes have mount namespaces that are different
|
|
|
|
// from that of the init. Therefore, above check can't filter them out.
|
|
|
|
// Since the propagation type of / is 'shared', unmounting /storage
|
|
|
|
// for the early native processes affects other processes including
|
|
|
|
// init. Filter out such processes by skipping if a process is a
|
|
|
|
// non-Java process whose UID is < AID_APP_START. (The UID condition
|
|
|
|
// is required to not filter out child processes spawned by apps.)
|
|
|
|
if (!android::vold::Readlinkat(pidFd, "exe", &exeName)) {
|
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
if (!StartsWith(exeName, "/system/bin/app_process") && sb.st_uid < AID_APP_START) {
|
|
|
|
goto next;
|
Don't unmount /storage for early native processes
Motivation:
Early processes launched before the runtime APEX - that hosts the bionic
libs - is activated can't use the bionic libs from the APEX, but from the
system partition (which we call the bootstrap bionic). Other processes
after the APEX activation should use the bionic libs from the APEX.
In order to let both types of processes to access the bionic libs via
the same standard paths /system/lib/{libc|libdl|libm}.so, some mount
namespace magic is used.
To be specific, when the device boots, the init initially bind-mounts
the bootstrap bionic libs to the standard paths with MS_PRIVATE. Early
processes are then executed with their own mount namespaces (via
unshare(CLONE_NEWNS)). After the runtime APEX is activated, init
bind-mounts the bionic libs in the APEX to the same standard paths.
Processes launched thereafter use the bionic libs from the APEX (which
can be updated.)
Important thing is that, since the propagation type of the mount points
(the standard paths) is 'private', the new bind-mount events for the
updated bionic libs should not affect the early processes. Otherwise,
they would experience sudden change of bionic libs at runtime. However,
other mount/unmounts events outside of the private mount points are
still shared across early/late processes as before. This is made possible
because the propagation type of / is 'shared' .
Problem:
vold uses the equality of the mount namespace to filter-out processes
that share the global mount namespace (the namespace of the init). However,
due to the aforementioned change, the early processes are not filtered
out because they have different mount namespaces. As a result,
umount2("/storage/") is executed on them and this unmount event
becomes visible to the global mount namespace (because as mentioned before /
is 'shared').
Solution:
Fiter-out the early processes by skipping a native (non-Java) process
whose UID is < AID_APP. The former condition is because all early
processes are native ones; i.e., zygote is started after the runtime
APEX is activated. The latter condition is to not filter-out native
processes created locally by apps.
Bug: 120266448
Test: m; device boots
Change-Id: I054deedc4af8421854cf35be84e14995523a259a
2019-01-04 05:35:25 +01:00
|
|
|
}
|
|
|
|
|
2015-06-24 20:49:24 +02:00
|
|
|
// We purposefully leave the namespace open across the fork
|
2019-03-30 00:03:51 +01:00
|
|
|
// NOLINTNEXTLINE(android-cloexec-open): Deliberately not O_CLOEXEC
|
|
|
|
nsFd = openat(pidFd, "ns/mnt", O_RDONLY);
|
2015-06-24 20:49:24 +02:00
|
|
|
if (nsFd < 0) {
|
2020-02-11 15:31:24 +01:00
|
|
|
async_safe_format_log(ANDROID_LOG_ERROR, "vold",
|
|
|
|
"Failed to open namespace for %s", de->d_name);
|
2015-06-24 20:49:24 +02:00
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
|
2020-02-11 15:31:24 +01:00
|
|
|
if (!callback(sb.st_uid, pid, nsFd, de->d_name, params)) {
|
|
|
|
async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed in callback");
|
2019-11-27 11:53:51 +01:00
|
|
|
}
|
2015-06-24 20:49:24 +02:00
|
|
|
|
2020-02-11 15:31:24 +01:00
|
|
|
next:
|
|
|
|
close(nsFd);
|
|
|
|
close(pidFd);
|
|
|
|
}
|
|
|
|
closedir(dir);
|
|
|
|
async_safe_format_log(ANDROID_LOG_INFO, "vold", "Finished scanning all processes");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-01-15 15:03:23 +01:00
|
|
|
// In each app's namespace, unmount obb and data dirs
|
|
|
|
static bool umountStorageDirs(int nsFd, const char* android_data_dir, const char* android_obb_dir,
|
|
|
|
int uid, const char* targets[], int size) {
|
|
|
|
// This code is executed after a fork so it's very important that the set of
|
|
|
|
// methods we call here is strictly limited.
|
|
|
|
if (setns(nsFd, CLONE_NEWNS) != 0) {
|
|
|
|
async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to setns %s", strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unmount of Android/data/foo needs to be done before Android/data below.
|
|
|
|
bool result = true;
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
|
if (TEMP_FAILURE_RETRY(umount2(targets[i], MNT_DETACH)) < 0 && errno != EINVAL &&
|
|
|
|
errno != ENOENT) {
|
|
|
|
async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to umount %s: %s",
|
|
|
|
targets[i], strerror(errno));
|
|
|
|
result = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mount tmpfs on Android/data and Android/obb
|
|
|
|
if (TEMP_FAILURE_RETRY(umount2(android_data_dir, MNT_DETACH)) < 0 && errno != EINVAL &&
|
|
|
|
errno != ENOENT) {
|
|
|
|
async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to umount %s :%s",
|
|
|
|
android_data_dir, strerror(errno));
|
|
|
|
result = false;
|
|
|
|
}
|
|
|
|
if (TEMP_FAILURE_RETRY(umount2(android_obb_dir, MNT_DETACH)) < 0 && errno != EINVAL &&
|
|
|
|
errno != ENOENT) {
|
|
|
|
async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to umount %s :%s",
|
|
|
|
android_obb_dir, strerror(errno));
|
|
|
|
result = false;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2020-05-07 17:01:33 +02:00
|
|
|
// In each app's namespace, mount tmpfs on obb and data dir, and bind mount obb and data
|
|
|
|
// package dirs.
|
|
|
|
static bool remountStorageDirs(int nsFd, const char* android_data_dir, const char* android_obb_dir,
|
|
|
|
int uid, const char* sources[], const char* targets[], int size) {
|
2020-02-28 17:30:47 +01:00
|
|
|
// This code is executed after a fork so it's very important that the set of
|
|
|
|
// methods we call here is strictly limited.
|
2020-02-11 15:31:24 +01:00
|
|
|
if (setns(nsFd, CLONE_NEWNS) != 0) {
|
|
|
|
async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to setns %s", strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-05-07 17:01:33 +02:00
|
|
|
// Mount tmpfs on Android/data and Android/obb
|
|
|
|
if (TEMP_FAILURE_RETRY(mount("tmpfs", android_data_dir, "tmpfs",
|
|
|
|
MS_NOSUID | MS_NODEV | MS_NOEXEC, "uid=0,gid=0,mode=0751")) == -1) {
|
|
|
|
async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to mount tmpfs to %s :%s",
|
|
|
|
android_data_dir, strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (TEMP_FAILURE_RETRY(mount("tmpfs", android_obb_dir, "tmpfs",
|
|
|
|
MS_NOSUID | MS_NODEV | MS_NOEXEC, "uid=0,gid=0,mode=0751")) == -1) {
|
|
|
|
async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to mount tmpfs to %s :%s",
|
|
|
|
android_obb_dir, strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-02-28 17:30:47 +01:00
|
|
|
for (int i = 0; i < size; i++) {
|
2020-05-07 17:01:33 +02:00
|
|
|
// Create package dir and bind mount it to the actual one.
|
|
|
|
if (TEMP_FAILURE_RETRY(mkdir(targets[i], 0700)) == -1) {
|
|
|
|
async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to mkdir %s %s",
|
|
|
|
targets[i], strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
2020-02-28 17:30:47 +01:00
|
|
|
if (TEMP_FAILURE_RETRY(mount(sources[i], targets[i], NULL, MS_BIND | MS_REC, NULL)) == -1) {
|
|
|
|
async_safe_format_log(ANDROID_LOG_ERROR, "vold", "Failed to mount %s to %s :%s",
|
|
|
|
sources[i], targets[i], strerror(errno));
|
|
|
|
return false;
|
2020-02-11 15:31:24 +01:00
|
|
|
}
|
|
|
|
}
|
2020-02-28 17:30:47 +01:00
|
|
|
return true;
|
|
|
|
}
|
2020-02-11 15:31:24 +01:00
|
|
|
|
2020-02-28 17:30:47 +01:00
|
|
|
static std::string getStorageDirSrc(userid_t userId, const std::string& dirName,
|
|
|
|
const std::string& packageName) {
|
2020-05-12 07:58:42 +02:00
|
|
|
if (IsSdcardfsUsed()) {
|
2020-02-28 17:30:47 +01:00
|
|
|
return StringPrintf("/mnt/runtime/default/emulated/%d/%s/%s",
|
|
|
|
userId, dirName.c_str(), packageName.c_str());
|
2020-02-11 15:31:24 +01:00
|
|
|
} else {
|
2020-02-28 17:30:47 +01:00
|
|
|
return StringPrintf("/mnt/pass_through/%d/emulated/%d/%s/%s",
|
|
|
|
userId, userId, dirName.c_str(), packageName.c_str());
|
2020-02-11 15:31:24 +01:00
|
|
|
}
|
2020-02-28 17:30:47 +01:00
|
|
|
}
|
2020-02-20 17:10:01 +01:00
|
|
|
|
2020-02-28 17:30:47 +01:00
|
|
|
static std::string getStorageDirTarget(userid_t userId, std::string dirName,
|
|
|
|
std::string packageName) {
|
|
|
|
return StringPrintf("/storage/emulated/%d/%s/%s",
|
|
|
|
userId, dirName.c_str(), packageName.c_str());
|
|
|
|
}
|
|
|
|
|
2021-01-15 15:03:23 +01:00
|
|
|
// Fork the process and remount / unmount app data and obb dirs
|
|
|
|
bool VolumeManager::forkAndRemountStorage(int uid, int pid, bool doUnmount,
|
2020-03-23 12:59:43 +01:00
|
|
|
const std::vector<std::string>& packageNames) {
|
2020-02-28 17:30:47 +01:00
|
|
|
userid_t userId = multiuser_get_user_id(uid);
|
|
|
|
std::string mnt_path = StringPrintf("/proc/%d/ns/mnt", pid);
|
|
|
|
android::base::unique_fd nsFd(
|
|
|
|
TEMP_FAILURE_RETRY(open(mnt_path.c_str(), O_RDONLY | O_CLOEXEC)));
|
|
|
|
if (nsFd == -1) {
|
|
|
|
PLOG(ERROR) << "Unable to open " << mnt_path.c_str();
|
2020-02-20 17:10:01 +01:00
|
|
|
return false;
|
|
|
|
}
|
2020-02-28 17:30:47 +01:00
|
|
|
// Storing both Android/obb and Android/data paths.
|
|
|
|
int size = packageNames.size() * 2;
|
2020-02-11 15:31:24 +01:00
|
|
|
|
2020-02-28 17:30:47 +01:00
|
|
|
std::unique_ptr<std::string[]> sources(new std::string[size]);
|
|
|
|
std::unique_ptr<std::string[]> targets(new std::string[size]);
|
|
|
|
std::unique_ptr<const char*[]> sources_uptr(new const char*[size]);
|
|
|
|
std::unique_ptr<const char*[]> targets_uptr(new const char*[size]);
|
|
|
|
const char** sources_cstr = sources_uptr.get();
|
|
|
|
const char** targets_cstr = targets_uptr.get();
|
2015-06-24 20:49:24 +02:00
|
|
|
|
2020-02-28 17:30:47 +01:00
|
|
|
for (int i = 0; i < size; i += 2) {
|
|
|
|
std::string const& packageName = packageNames[i/2];
|
|
|
|
sources[i] = getStorageDirSrc(userId, "Android/data", packageName);
|
|
|
|
targets[i] = getStorageDirTarget(userId, "Android/data", packageName);
|
|
|
|
sources[i+1] = getStorageDirSrc(userId, "Android/obb", packageName);
|
|
|
|
targets[i+1] = getStorageDirTarget(userId, "Android/obb", packageName);
|
2020-02-11 15:31:24 +01:00
|
|
|
|
2020-02-28 17:30:47 +01:00
|
|
|
sources_cstr[i] = sources[i].c_str();
|
|
|
|
targets_cstr[i] = targets[i].c_str();
|
|
|
|
sources_cstr[i+1] = sources[i+1].c_str();
|
|
|
|
targets_cstr[i+1] = targets[i+1].c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < size; i++) {
|
2021-01-19 12:27:36 +01:00
|
|
|
// Make sure /storage/emulated/... paths are setup correctly
|
|
|
|
// This needs to be done before EnsureDirExists to ensure Android/ is created.
|
|
|
|
auto status = setupAppDir(targets_cstr[i], uid, false /* fixupExistingOnly */);
|
2020-02-11 15:31:24 +01:00
|
|
|
if (status != OK) {
|
2021-01-19 12:27:36 +01:00
|
|
|
PLOG(ERROR) << "Failed to create dir: " << targets_cstr[i];
|
2020-02-28 17:30:47 +01:00
|
|
|
return false;
|
2020-02-11 15:31:24 +01:00
|
|
|
}
|
2021-01-19 12:27:36 +01:00
|
|
|
status = EnsureDirExists(sources_cstr[i], 0771, AID_MEDIA_RW, AID_MEDIA_RW);
|
2020-02-11 15:31:24 +01:00
|
|
|
if (status != OK) {
|
2021-01-19 12:27:36 +01:00
|
|
|
PLOG(ERROR) << "Failed to create dir: " << sources_cstr[i];
|
2020-02-28 17:30:47 +01:00
|
|
|
return false;
|
2020-02-20 17:10:01 +01:00
|
|
|
}
|
2020-02-11 15:31:24 +01:00
|
|
|
}
|
2015-06-24 20:49:24 +02:00
|
|
|
|
2020-05-07 17:01:33 +02:00
|
|
|
char android_data_dir[PATH_MAX];
|
|
|
|
char android_obb_dir[PATH_MAX];
|
|
|
|
snprintf(android_data_dir, PATH_MAX, "/storage/emulated/%d/Android/data", userId);
|
|
|
|
snprintf(android_obb_dir, PATH_MAX, "/storage/emulated/%d/Android/obb", userId);
|
|
|
|
|
2020-02-11 15:31:24 +01:00
|
|
|
pid_t child;
|
2020-02-28 17:30:47 +01:00
|
|
|
// Fork a child to mount Android/obb android Android/data dirs, as we don't want it to affect
|
|
|
|
// original vold process mount namespace.
|
2020-02-11 15:31:24 +01:00
|
|
|
if (!(child = fork())) {
|
2021-01-15 15:03:23 +01:00
|
|
|
if (doUnmount) {
|
|
|
|
if (umountStorageDirs(nsFd, android_data_dir, android_obb_dir, uid,
|
|
|
|
targets_cstr, size)) {
|
|
|
|
_exit(0);
|
|
|
|
} else {
|
|
|
|
_exit(1);
|
|
|
|
}
|
2020-02-11 15:31:24 +01:00
|
|
|
} else {
|
2021-01-15 15:03:23 +01:00
|
|
|
if (remountStorageDirs(nsFd, android_data_dir, android_obb_dir, uid,
|
|
|
|
sources_cstr, targets_cstr, size)) {
|
|
|
|
_exit(0);
|
|
|
|
} else {
|
|
|
|
_exit(1);
|
|
|
|
}
|
2020-02-11 15:31:24 +01:00
|
|
|
}
|
|
|
|
}
|
2020-02-28 17:30:47 +01:00
|
|
|
|
2020-02-11 15:31:24 +01:00
|
|
|
if (child == -1) {
|
|
|
|
PLOG(ERROR) << "Failed to fork";
|
2020-02-28 17:30:47 +01:00
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
int status;
|
|
|
|
if (TEMP_FAILURE_RETRY(waitpid(child, &status, 0)) == -1) {
|
|
|
|
PLOG(ERROR) << "Failed to waitpid: " << child;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!WIFEXITED(status)) {
|
|
|
|
PLOG(ERROR) << "Process did not exit normally, status: " << status;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (WEXITSTATUS(status)) {
|
|
|
|
PLOG(ERROR) << "Process exited with code: " << WEXITSTATUS(status);
|
|
|
|
return false;
|
2020-02-11 15:31:24 +01:00
|
|
|
}
|
2015-06-24 20:49:24 +02:00
|
|
|
}
|
2020-02-28 17:30:47 +01:00
|
|
|
return true;
|
2015-06-24 20:49:24 +02:00
|
|
|
}
|
|
|
|
|
2021-01-15 15:03:23 +01:00
|
|
|
int VolumeManager::handleAppStorageDirs(int uid, int pid,
|
|
|
|
bool doUnmount, const std::vector<std::string>& packageNames) {
|
2020-02-28 17:30:47 +01:00
|
|
|
// Only run the remount if fuse is mounted for that user.
|
|
|
|
userid_t userId = multiuser_get_user_id(uid);
|
|
|
|
bool fuseMounted = false;
|
|
|
|
for (auto& vol : mInternalEmulatedVolumes) {
|
|
|
|
if (vol->getMountUserId() == userId && vol->getState() == VolumeBase::State::kMounted) {
|
|
|
|
auto* emulatedVol = static_cast<android::vold::EmulatedVolume*>(vol.get());
|
|
|
|
if (emulatedVol) {
|
|
|
|
fuseMounted = emulatedVol->isFuseMounted();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2020-02-11 15:31:24 +01:00
|
|
|
}
|
2020-02-28 17:30:47 +01:00
|
|
|
if (fuseMounted) {
|
2021-01-15 15:03:23 +01:00
|
|
|
forkAndRemountStorage(uid, pid, doUnmount, packageNames);
|
2020-02-11 15:31:24 +01:00
|
|
|
}
|
2020-02-28 17:30:47 +01:00
|
|
|
return 0;
|
2020-02-11 15:31:24 +01:00
|
|
|
}
|
|
|
|
|
2020-04-29 07:49:41 +02:00
|
|
|
int VolumeManager::abortFuse() {
|
|
|
|
return android::vold::AbortFuseConnections();
|
|
|
|
}
|
|
|
|
|
2015-03-14 00:09:20 +01:00
|
|
|
int VolumeManager::reset() {
|
|
|
|
// Tear down all existing disks/volumes and start from a blank slate so
|
|
|
|
// newly connected framework hears all events.
|
2019-09-25 15:37:38 +02:00
|
|
|
for (const auto& vol : mInternalEmulatedVolumes) {
|
|
|
|
vol->destroy();
|
2017-08-14 05:32:13 +02:00
|
|
|
}
|
2019-09-25 15:37:38 +02:00
|
|
|
mInternalEmulatedVolumes.clear();
|
|
|
|
|
2021-11-25 08:24:35 +01:00
|
|
|
// Destroy and recreate all disks except that StubVolume disks are just
|
|
|
|
// destroyed and removed from both mDisks and mPendingDisks.
|
|
|
|
// StubVolumes are managed from outside Android (e.g. from Chrome OS) and
|
|
|
|
// their disk recreation on reset events should be handled from outside by
|
|
|
|
// calling createStubVolume() again.
|
2016-07-27 23:11:02 +02:00
|
|
|
for (const auto& disk : mDisks) {
|
2015-03-14 00:09:20 +01:00
|
|
|
disk->destroy();
|
2021-11-25 08:24:35 +01:00
|
|
|
if (!disk->isStub()) {
|
|
|
|
disk->create();
|
|
|
|
}
|
2015-03-14 00:09:20 +01:00
|
|
|
}
|
2021-11-25 08:24:35 +01:00
|
|
|
const auto isStub = [](const auto& disk) { return disk->isStub(); };
|
|
|
|
mDisks.remove_if(isStub);
|
|
|
|
mPendingDisks.remove_if(isStub);
|
|
|
|
|
2017-03-26 06:49:13 +02:00
|
|
|
updateVirtualDisk();
|
2015-06-10 18:42:01 +02:00
|
|
|
mAddedUsers.clear();
|
2019-02-06 21:39:19 +01:00
|
|
|
mStartedUsers.clear();
|
Support bind mounting volumes into other volume's mountpoint.
With the way the FUSE mount point are currently setup for emulated
volumes, there can be multiple paths that serve the same files on the
lower filesystem; eg
* /mnt/user/0/emulated/0/Android
* /mnt/user/10/emulated/0/Android
both refer to the same file on the lower filesystem:
* /data/media/0/Android
this is normally not a problem, because cross-user file access is not
allowed, and so the FUSE daemon won't serve files for other users.
With clone profiles this is no longer true however, as their volumes
are accessible by each other.
So, it can happen that an app running in clone profile 10 accesses
"/mnt/user/10/emulated/0/Android", which would be served by the FUSE
daemon for the user 10 filesystem.
At the same time, an app running in the owner profile 0 accesses
"mnt/user/0/emulated/0/Android", which would be served by the FUSE
daemon for the user 0 filesystem.
This can cause page cache inconsistencies, because multiple FUSE daemons
can be running on top of the same entries in the lower filesystem.
To prevent this, use bind mounts to make sure that cross-profile
accesses actually end up in the FUSE daemon to which the volume
belongs: "/mnt/user/10/emulated/0" is bind-mounted to
"/mnt/user/0/emulated/0", and vice-versa.
Bug: 228271997
Test: manual
Change-Id: Iefcbc813670628b329a1a5d408b6126b84991e09
2022-07-12 10:11:02 +02:00
|
|
|
mSharedStorageUser.clear();
|
2022-05-19 17:53:22 +02:00
|
|
|
|
|
|
|
// Abort all FUSE connections to avoid deadlocks if the FUSE daemon was killed
|
|
|
|
// with FUSE fds open.
|
|
|
|
abortFuse();
|
2015-03-14 00:09:20 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-03-14 02:02:50 +01:00
|
|
|
// Can be called twice (sequentially) during shutdown. should be safe for that.
|
2015-03-14 00:09:20 +01:00
|
|
|
int VolumeManager::shutdown() {
|
2019-09-25 15:37:38 +02:00
|
|
|
if (mInternalEmulatedVolumes.empty()) {
|
2018-09-19 00:14:18 +02:00
|
|
|
return 0; // already shutdown
|
2017-03-14 02:02:50 +01:00
|
|
|
}
|
2017-10-20 17:07:53 +02:00
|
|
|
android::vold::sSleepOnUnmount = false;
|
2019-09-25 15:37:38 +02:00
|
|
|
for (const auto& vol : mInternalEmulatedVolumes) {
|
|
|
|
vol->destroy();
|
|
|
|
}
|
2016-07-27 23:11:02 +02:00
|
|
|
for (const auto& disk : mDisks) {
|
2015-03-14 00:09:20 +01:00
|
|
|
disk->destroy();
|
|
|
|
}
|
2019-09-25 15:37:38 +02:00
|
|
|
|
|
|
|
mInternalEmulatedVolumes.clear();
|
2015-03-14 00:09:20 +01:00
|
|
|
mDisks.clear();
|
2017-12-15 06:15:20 +01:00
|
|
|
mPendingDisks.clear();
|
2017-10-20 17:07:53 +02:00
|
|
|
android::vold::sSleepOnUnmount = true;
|
2020-04-29 07:49:41 +02:00
|
|
|
|
2015-03-14 00:09:20 +01:00
|
|
|
return 0;
|
2009-10-11 02:22:08 +02:00
|
|
|
}
|
|
|
|
|
2015-03-31 19:35:33 +02:00
|
|
|
int VolumeManager::unmountAll() {
|
2015-04-21 21:14:17 +02:00
|
|
|
std::lock_guard<std::mutex> lock(mLock);
|
2017-09-22 01:08:43 +02:00
|
|
|
ATRACE_NAME("VolumeManager::unmountAll()");
|
2015-04-21 21:14:17 +02:00
|
|
|
|
2015-03-31 19:35:33 +02:00
|
|
|
// First, try gracefully unmounting all known devices
|
2019-09-25 15:37:38 +02:00
|
|
|
for (const auto& vol : mInternalEmulatedVolumes) {
|
|
|
|
vol->unmount();
|
2015-03-31 19:35:33 +02:00
|
|
|
}
|
2016-07-27 23:11:02 +02:00
|
|
|
for (const auto& disk : mDisks) {
|
2015-03-31 19:35:33 +02:00
|
|
|
disk->unmountAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Worst case we might have some stale mounts lurking around, so
|
|
|
|
// force unmount those just to be safe.
|
2018-11-23 12:27:35 +01:00
|
|
|
FILE* fp = setmntent("/proc/mounts", "re");
|
2015-03-31 19:35:33 +02:00
|
|
|
if (fp == NULL) {
|
2017-10-07 02:02:53 +02:00
|
|
|
PLOG(ERROR) << "Failed to open /proc/mounts";
|
2015-03-31 19:35:33 +02:00
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Some volumes can be stacked on each other, so force unmount in
|
|
|
|
// reverse order to give us the best chance of success.
|
|
|
|
std::list<std::string> toUnmount;
|
|
|
|
mntent* mentry;
|
|
|
|
while ((mentry = getmntent(fp)) != NULL) {
|
2017-10-07 02:02:53 +02:00
|
|
|
auto test = std::string(mentry->mnt_dir);
|
2018-09-20 19:09:27 +02:00
|
|
|
if ((StartsWith(test, "/mnt/") &&
|
|
|
|
#ifdef __ANDROID_DEBUGGABLE__
|
|
|
|
!StartsWith(test, "/mnt/scratch") &&
|
|
|
|
#endif
|
2020-01-11 19:24:26 +01:00
|
|
|
!StartsWith(test, "/mnt/vendor") && !StartsWith(test, "/mnt/product") &&
|
2020-04-07 14:43:20 +02:00
|
|
|
!StartsWith(test, "/mnt/installer") && !StartsWith(test, "/mnt/androidwritable")) ||
|
2018-09-20 19:09:27 +02:00
|
|
|
StartsWith(test, "/storage/")) {
|
2017-10-07 02:02:53 +02:00
|
|
|
toUnmount.push_front(test);
|
2015-03-31 19:35:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
endmntent(fp);
|
|
|
|
|
2016-07-27 23:11:02 +02:00
|
|
|
for (const auto& path : toUnmount) {
|
2017-09-11 18:32:01 +02:00
|
|
|
LOG(DEBUG) << "Tearing down stale mount " << path;
|
2015-03-31 19:35:33 +02:00
|
|
|
android::vold::ForceUnmount(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-12-03 16:32:52 +01:00
|
|
|
int VolumeManager::ensureAppDirsCreated(const std::vector<std::string>& paths, int32_t appUid) {
|
|
|
|
int size = paths.size();
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
|
int result = setupAppDir(paths[i], appUid, false /* fixupExistingOnly */,
|
|
|
|
true /* skipIfDirExists */);
|
|
|
|
if (result != OK) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int VolumeManager::setupAppDir(const std::string& path, int32_t appUid, bool fixupExistingOnly,
|
|
|
|
bool skipIfDirExists) {
|
2015-03-14 00:09:20 +01:00
|
|
|
// Only offer to create directories for paths managed by vold
|
2019-12-24 12:57:16 +01:00
|
|
|
if (!StartsWith(path, "/storage/")) {
|
2017-10-07 02:02:53 +02:00
|
|
|
LOG(ERROR) << "Failed to find mounted volume for " << path;
|
2013-09-18 02:24:38 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2019-12-24 12:57:16 +01:00
|
|
|
|
2020-01-24 16:17:32 +01:00
|
|
|
// Find the volume it belongs to
|
|
|
|
auto filter_fn = [&](const VolumeBase& vol) {
|
|
|
|
if (vol.getState() != VolumeBase::State::kMounted) {
|
|
|
|
// The volume must be mounted
|
|
|
|
return false;
|
|
|
|
}
|
2021-11-11 03:23:01 +01:00
|
|
|
if (!vol.isVisibleForWrite()) {
|
|
|
|
// App dirs should only be created for writable volumes.
|
2020-01-24 16:17:32 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (vol.getInternalPath().empty()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (vol.getMountUserId() != USER_UNKNOWN &&
|
|
|
|
vol.getMountUserId() != multiuser_get_user_id(appUid)) {
|
|
|
|
// The app dir must be created on a volume with the same user-id
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!path.empty() && StartsWith(path, vol.getPath())) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
auto volume = findVolumeWithFilter(filter_fn);
|
|
|
|
if (volume == nullptr) {
|
|
|
|
LOG(ERROR) << "Failed to find mounted volume for " << path;
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2020-01-06 22:48:16 +01:00
|
|
|
// Convert paths to lower filesystem paths to avoid making FUSE requests for these reasons:
|
|
|
|
// 1. A FUSE request from vold puts vold at risk of hanging if the FUSE daemon is down
|
|
|
|
// 2. The FUSE daemon prevents requests on /mnt/user/0/emulated/<userid != 0> and a request
|
|
|
|
// on /storage/emulated/10 means /mnt/user/0/emulated/10
|
2020-01-24 16:17:32 +01:00
|
|
|
const std::string lowerPath =
|
|
|
|
volume->getInternalPath() + path.substr(volume->getPath().length());
|
2020-01-06 22:48:16 +01:00
|
|
|
|
2020-01-31 15:23:09 +01:00
|
|
|
const std::string volumeRoot = volume->getRootPath(); // eg /data/media/0
|
|
|
|
|
2020-12-03 16:32:52 +01:00
|
|
|
const int access_result = access(lowerPath.c_str(), F_OK);
|
|
|
|
if (fixupExistingOnly && access_result != 0) {
|
2020-02-18 15:06:37 +01:00
|
|
|
// Nothing to fixup
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2020-12-03 16:32:52 +01:00
|
|
|
if (skipIfDirExists && access_result == 0) {
|
|
|
|
// It's safe to assume it's ok as it will be used for zygote to bind mount dir only,
|
|
|
|
// which the dir doesn't need to have correct permission for now yet.
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2020-04-20 15:14:48 +02:00
|
|
|
if (volume->getType() == VolumeBase::Type::kPublic) {
|
|
|
|
// On public volumes, we don't need to setup permissions, as everything goes through
|
|
|
|
// FUSE; just create the dirs and be done with it.
|
|
|
|
return fs_mkdirs(lowerPath.c_str(), 0700);
|
|
|
|
}
|
|
|
|
|
2020-02-13 23:30:38 +01:00
|
|
|
// Create the app paths we need from the root
|
2020-02-18 15:06:37 +01:00
|
|
|
return PrepareAppDirFromRoot(lowerPath, volumeRoot, appUid, fixupExistingOnly);
|
|
|
|
}
|
|
|
|
|
|
|
|
int VolumeManager::fixupAppDir(const std::string& path, int32_t appUid) {
|
2020-05-12 07:58:42 +02:00
|
|
|
if (IsSdcardfsUsed()) {
|
2020-02-18 15:06:37 +01:00
|
|
|
//sdcardfs magically does this for us
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
return setupAppDir(path, appUid, true /* fixupExistingOnly */);
|
2013-09-18 02:24:38 +02:00
|
|
|
}
|
2017-09-11 18:32:01 +02:00
|
|
|
|
Remove broken code for mounting encrypted OBB files
Mounting encrypted OBB files has never worked reliably across devices,
partly due to its reliance on Twofish encryption support in the kernel.
This is because Twofish support (CONFIG_CRYPTO_TWOFISH) has never been
required or even recommended for Android. It has never been enabled in
GKI, but even before GKI it wasn't required or recommended. Moreover,
this is now the only Android feature that still uses dm-crypt
(CONFIG_DM_CRYPT), and some devices don't have that enabled either.
Therefore, it appears that this feature is unused. That's perhaps not
surprising, considering that the documentation for OBBs
(https://developer.android.com/google/play/expansion-files) says that
they are deprecated, and also it explains OBBs as being app files that
are opaque to the platform; the ability of the platform to mount OBBs
that happen to be in a particular format is never mentioned. That means
that OBB mounting is probably rarely used even with unencrypted OBBs.
Finally, the usefulness of OBBs having their own encryption layer (in
addition to what the platform already provides via FBE) is not clear
either, especially with such an unusual choice of cipher.
To avoid the confusion that is being caused by having the broken code
for mounting encrypted OBBs still sitting around, let's remove it.
Test: atest StorageManagerTest # on Cuttlefish
Test: atest StorageManagerIntegrationTest # on Cuttlefish
Bug: 216475849
Change-Id: Iaef32cce90f95ea745ba2b143f89e66f533f3479
2022-03-01 22:19:18 +01:00
|
|
|
int VolumeManager::createObb(const std::string& sourcePath, int32_t ownerGid,
|
|
|
|
std::string* outVolId) {
|
2017-09-11 18:32:01 +02:00
|
|
|
int id = mNextObbId++;
|
|
|
|
|
2020-11-19 10:49:45 +01:00
|
|
|
std::string lowerSourcePath;
|
|
|
|
|
|
|
|
// Convert to lower filesystem path
|
|
|
|
if (StartsWith(sourcePath, "/storage/")) {
|
|
|
|
auto filter_fn = [&](const VolumeBase& vol) {
|
|
|
|
if (vol.getState() != VolumeBase::State::kMounted) {
|
|
|
|
// The volume must be mounted
|
|
|
|
return false;
|
|
|
|
}
|
2021-11-11 03:23:01 +01:00
|
|
|
if (!vol.isVisibleForWrite()) {
|
|
|
|
// Obb volume should only be created for writable volumes.
|
2020-11-19 10:49:45 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (vol.getInternalPath().empty()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!sourcePath.empty() && StartsWith(sourcePath, vol.getPath())) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
auto volume = findVolumeWithFilter(filter_fn);
|
|
|
|
if (volume == nullptr) {
|
|
|
|
LOG(ERROR) << "Failed to find mounted volume for " << sourcePath;
|
|
|
|
return -EINVAL;
|
|
|
|
} else {
|
|
|
|
lowerSourcePath =
|
|
|
|
volume->getInternalPath() + sourcePath.substr(volume->getPath().length());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
lowerSourcePath = sourcePath;
|
|
|
|
}
|
|
|
|
|
2017-09-11 18:32:01 +02:00
|
|
|
auto vol = std::shared_ptr<android::vold::VolumeBase>(
|
Remove broken code for mounting encrypted OBB files
Mounting encrypted OBB files has never worked reliably across devices,
partly due to its reliance on Twofish encryption support in the kernel.
This is because Twofish support (CONFIG_CRYPTO_TWOFISH) has never been
required or even recommended for Android. It has never been enabled in
GKI, but even before GKI it wasn't required or recommended. Moreover,
this is now the only Android feature that still uses dm-crypt
(CONFIG_DM_CRYPT), and some devices don't have that enabled either.
Therefore, it appears that this feature is unused. That's perhaps not
surprising, considering that the documentation for OBBs
(https://developer.android.com/google/play/expansion-files) says that
they are deprecated, and also it explains OBBs as being app files that
are opaque to the platform; the ability of the platform to mount OBBs
that happen to be in a particular format is never mentioned. That means
that OBB mounting is probably rarely used even with unencrypted OBBs.
Finally, the usefulness of OBBs having their own encryption layer (in
addition to what the platform already provides via FBE) is not clear
either, especially with such an unusual choice of cipher.
To avoid the confusion that is being caused by having the broken code
for mounting encrypted OBBs still sitting around, let's remove it.
Test: atest StorageManagerTest # on Cuttlefish
Test: atest StorageManagerIntegrationTest # on Cuttlefish
Bug: 216475849
Change-Id: Iaef32cce90f95ea745ba2b143f89e66f533f3479
2022-03-01 22:19:18 +01:00
|
|
|
new android::vold::ObbVolume(id, lowerSourcePath, ownerGid));
|
2017-09-11 18:32:01 +02:00
|
|
|
vol->create();
|
|
|
|
|
|
|
|
mObbVolumes.push_back(vol);
|
|
|
|
*outVolId = vol->getId();
|
|
|
|
return android::OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int VolumeManager::destroyObb(const std::string& volId) {
|
|
|
|
auto i = mObbVolumes.begin();
|
|
|
|
while (i != mObbVolumes.end()) {
|
|
|
|
if ((*i)->getId() == volId) {
|
|
|
|
(*i)->destroy();
|
|
|
|
i = mObbVolumes.erase(i);
|
|
|
|
} else {
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return android::OK;
|
|
|
|
}
|
|
|
|
|
2018-10-29 00:52:56 +01:00
|
|
|
int VolumeManager::createStubVolume(const std::string& sourcePath, const std::string& mountPath,
|
|
|
|
const std::string& fsType, const std::string& fsUuid,
|
2020-02-07 10:03:44 +01:00
|
|
|
const std::string& fsLabel, int32_t flags,
|
2020-02-04 08:07:21 +01:00
|
|
|
std::string* outVolId) {
|
|
|
|
dev_t stubId = --mNextStubId;
|
|
|
|
auto vol = std::shared_ptr<android::vold::StubVolume>(
|
|
|
|
new android::vold::StubVolume(stubId, sourcePath, mountPath, fsType, fsUuid, fsLabel));
|
|
|
|
|
2020-07-07 23:08:48 +02:00
|
|
|
int32_t passedFlags = 0;
|
2020-02-07 10:03:44 +01:00
|
|
|
passedFlags |= (flags & android::vold::Disk::Flags::kUsb);
|
|
|
|
passedFlags |= (flags & android::vold::Disk::Flags::kSd);
|
2020-07-07 23:08:48 +02:00
|
|
|
if (flags & android::vold::Disk::Flags::kStubVisible) {
|
|
|
|
passedFlags |= (flags & android::vold::Disk::Flags::kStubVisible);
|
|
|
|
} else {
|
|
|
|
passedFlags |= (flags & android::vold::Disk::Flags::kStubInvisible);
|
|
|
|
}
|
2020-02-04 08:07:21 +01:00
|
|
|
// StubDisk doesn't have device node corresponds to it. So, a fake device
|
2020-02-07 10:03:44 +01:00
|
|
|
// number is used.
|
2020-02-04 08:07:21 +01:00
|
|
|
auto disk = std::shared_ptr<android::vold::Disk>(
|
2020-02-07 10:03:44 +01:00
|
|
|
new android::vold::Disk("stub", stubId, "stub", passedFlags));
|
2020-02-04 08:07:21 +01:00
|
|
|
disk->initializePartition(vol);
|
|
|
|
handleDiskAdded(disk);
|
2018-10-29 00:52:56 +01:00
|
|
|
*outVolId = vol->getId();
|
|
|
|
return android::OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int VolumeManager::destroyStubVolume(const std::string& volId) {
|
2020-02-04 08:07:21 +01:00
|
|
|
auto tokens = android::base::Split(volId, ":");
|
|
|
|
CHECK(tokens.size() == 2);
|
|
|
|
dev_t stubId;
|
|
|
|
CHECK(android::base::ParseUint(tokens[1], &stubId));
|
|
|
|
handleDiskRemoved(stubId);
|
2018-10-29 00:52:56 +01:00
|
|
|
return android::OK;
|
|
|
|
}
|
|
|
|
|
2018-10-27 04:56:45 +02:00
|
|
|
int VolumeManager::mountAppFuse(uid_t uid, int mountId, unique_fd* device_fd) {
|
2018-11-01 04:59:47 +01:00
|
|
|
return android::vold::MountAppFuse(uid, mountId, device_fd);
|
2017-09-11 18:32:01 +02:00
|
|
|
}
|
|
|
|
|
2018-10-27 04:56:45 +02:00
|
|
|
int VolumeManager::unmountAppFuse(uid_t uid, int mountId) {
|
2018-11-01 04:59:47 +01:00
|
|
|
return android::vold::UnmountAppFuse(uid, mountId);
|
2018-10-27 04:56:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int VolumeManager::openAppFuseFile(uid_t uid, int mountId, int fileId, int flags) {
|
2018-11-01 04:59:47 +01:00
|
|
|
return android::vold::OpenAppFuseFile(uid, mountId, fileId, flags);
|
2017-09-11 18:32:01 +02:00
|
|
|
}
|
2023-08-11 19:27:24 +02:00
|
|
|
|
|
|
|
android::status_t android::vold::GetStorageSize(int64_t* storageSize) {
|
|
|
|
// Start with the /data mount point from fs_mgr
|
|
|
|
auto entry = android::fs_mgr::GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT);
|
|
|
|
if (entry == nullptr) {
|
|
|
|
LOG(ERROR) << "No mount point entry for " << DATA_MNT_POINT;
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Follow any symbolic links
|
|
|
|
std::string blkDevice = entry->blk_device;
|
|
|
|
std::string dataDevice;
|
|
|
|
if (!android::base::Realpath(blkDevice, &dataDevice)) {
|
|
|
|
dataDevice = blkDevice;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle mapped volumes.
|
|
|
|
auto& dm = android::dm::DeviceMapper::Instance();
|
|
|
|
for (;;) {
|
|
|
|
auto parent = dm.GetParentBlockDeviceByPath(dataDevice);
|
|
|
|
if (!parent.has_value()) break;
|
|
|
|
dataDevice = *parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the potential /sys/block entry
|
|
|
|
std::size_t leaf = dataDevice.rfind('/');
|
|
|
|
if (leaf == std::string::npos) {
|
|
|
|
LOG(ERROR) << "data device " << dataDevice << " is not a path";
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
if (dataDevice.substr(0, leaf) != "/dev/block") {
|
|
|
|
LOG(ERROR) << "data device " << dataDevice << " is not a block device";
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
std::string sysfs = std::string() + "/sys/block/" + dataDevice.substr(leaf + 1);
|
|
|
|
|
|
|
|
// Look for a directory in /sys/block containing size where the name is a shortened
|
|
|
|
// version of the name we now have
|
|
|
|
// Typically we start with something like /sys/block/sda2, and we want /sys/block/sda
|
|
|
|
// Note that this directory only contains actual disks, not partitions, so this is
|
|
|
|
// not going to find anything other than the disks
|
|
|
|
std::string size;
|
|
|
|
std::string sizeFile;
|
|
|
|
for (std::string sysfsDir = sysfs;; sysfsDir = sysfsDir.substr(0, sysfsDir.size() - 1)) {
|
|
|
|
if (sysfsDir.back() == '/') {
|
|
|
|
LOG(ERROR) << "Could not find valid block device from " << sysfs;
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
sizeFile = sysfsDir + "/size";
|
|
|
|
if (android::base::ReadFileToString(sizeFile, &size, true)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the size file and be done
|
|
|
|
std::stringstream ssSize(size);
|
|
|
|
ssSize >> *storageSize;
|
|
|
|
if (ssSize.fail()) {
|
|
|
|
LOG(ERROR) << sizeFile << " cannot be read as an integer";
|
|
|
|
return EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*storageSize *= 512;
|
|
|
|
return OK;
|
|
|
|
}
|