66270a21df
Now that we're treating storage as a runtime permission, we need to grant read/write access without killing the app. This is really tricky, since we had been using GIDs for access control, and they're set in stone once Zygote drops privileges. The only thing left that can change dynamically is the filesystem itself, so let's do that. This means changing the FUSE daemon to present itself as three different views: /mnt/runtime_default/foo - view for apps with no access /mnt/runtime_read/foo - view for apps with read access /mnt/runtime_write/foo - view for apps with write access There is still a single location for all the backing files, and filesystem permissions are derived the same way for each view, but the file modes are masked off differently for each mountpoint. During Zygote fork, it wires up the appropriate storage access into an isolated mount namespace based on the current app permissions. When the app is granted permissions dynamically at runtime, the system asks vold to jump into the existing mount namespace and bind mount the newly granted access model into place. Bug: 21858077 Change-Id: Iade538e4bc7af979fe20095f74416e8a0f165a4a
228 lines
6.5 KiB
C++
228 lines
6.5 KiB
C++
/*
|
|
* 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 "fs/Vfat.h"
|
|
#include "PublicVolume.h"
|
|
#include "Utils.h"
|
|
#include "VolumeManager.h"
|
|
#include "ResponseCode.h"
|
|
|
|
#include <base/stringprintf.h>
|
|
#include <base/logging.h>
|
|
#include <cutils/fs.h>
|
|
#include <private/android_filesystem_config.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
|
|
using android::base::StringPrintf;
|
|
|
|
namespace android {
|
|
namespace vold {
|
|
|
|
static const char* kFusePath = "/system/bin/sdcard";
|
|
|
|
static const char* kAsecPath = "/mnt/secure/asec";
|
|
|
|
PublicVolume::PublicVolume(dev_t device) :
|
|
VolumeBase(Type::kPublic), mDevice(device), mFusePid(0) {
|
|
setId(StringPrintf("public:%u,%u", major(device), minor(device)));
|
|
mDevPath = StringPrintf("/dev/block/vold/%s", getId().c_str());
|
|
}
|
|
|
|
PublicVolume::~PublicVolume() {
|
|
}
|
|
|
|
status_t PublicVolume::readMetadata() {
|
|
status_t res = ReadMetadataUntrusted(mDevPath, mFsType, mFsUuid, mFsLabel);
|
|
notifyEvent(ResponseCode::VolumeFsTypeChanged, mFsType);
|
|
notifyEvent(ResponseCode::VolumeFsUuidChanged, mFsUuid);
|
|
notifyEvent(ResponseCode::VolumeFsLabelChanged, mFsLabel);
|
|
return res;
|
|
}
|
|
|
|
status_t PublicVolume::initAsecStage() {
|
|
std::string legacyPath(mRawPath + "/android_secure");
|
|
std::string securePath(mRawPath + "/.android_secure");
|
|
|
|
// Recover legacy secure path
|
|
if (!access(legacyPath.c_str(), R_OK | X_OK)
|
|
&& access(securePath.c_str(), R_OK | X_OK)) {
|
|
if (rename(legacyPath.c_str(), securePath.c_str())) {
|
|
PLOG(WARNING) << getId() << " failed to rename legacy ASEC dir";
|
|
}
|
|
}
|
|
|
|
if (TEMP_FAILURE_RETRY(mkdir(securePath.c_str(), 0700))) {
|
|
if (errno != EEXIST) {
|
|
PLOG(WARNING) << getId() << " creating ASEC stage failed";
|
|
return -errno;
|
|
}
|
|
}
|
|
|
|
BindMount(securePath, kAsecPath);
|
|
|
|
return OK;
|
|
}
|
|
|
|
status_t PublicVolume::doCreate() {
|
|
return CreateDeviceNode(mDevPath, mDevice);
|
|
}
|
|
|
|
status_t PublicVolume::doDestroy() {
|
|
return DestroyDeviceNode(mDevPath);
|
|
}
|
|
|
|
status_t PublicVolume::doMount() {
|
|
// TODO: expand to support mounting other filesystems
|
|
readMetadata();
|
|
|
|
if (mFsType != "vfat") {
|
|
LOG(ERROR) << getId() << " unsupported filesystem " << mFsType;
|
|
return -EIO;
|
|
}
|
|
|
|
if (vfat::Check(mDevPath)) {
|
|
LOG(ERROR) << getId() << " failed filesystem check";
|
|
return -EIO;
|
|
}
|
|
|
|
// Use UUID as stable name, if available
|
|
std::string stableName = getId();
|
|
if (!mFsUuid.empty()) {
|
|
stableName = mFsUuid;
|
|
}
|
|
|
|
mRawPath = StringPrintf("/mnt/media_rw/%s", stableName.c_str());
|
|
|
|
mFuseDefault = StringPrintf("/mnt/runtime_default/%s", stableName.c_str());
|
|
mFuseRead = StringPrintf("/mnt/runtime_read/%s", stableName.c_str());
|
|
mFuseWrite = StringPrintf("/mnt/runtime_write/%s", stableName.c_str());
|
|
|
|
setInternalPath(mRawPath);
|
|
setPath(StringPrintf("/storage/%s", stableName.c_str()));
|
|
|
|
if (fs_prepare_dir(mRawPath.c_str(), 0700, AID_ROOT, AID_ROOT) ||
|
|
fs_prepare_dir(mFuseDefault.c_str(), 0700, AID_ROOT, AID_ROOT) ||
|
|
fs_prepare_dir(mFuseRead.c_str(), 0700, AID_ROOT, AID_ROOT) ||
|
|
fs_prepare_dir(mFuseWrite.c_str(), 0700, AID_ROOT, AID_ROOT)) {
|
|
PLOG(ERROR) << getId() << " failed to create mount points";
|
|
return -errno;
|
|
}
|
|
|
|
if (vfat::Mount(mDevPath, mRawPath, false, false, false,
|
|
AID_MEDIA_RW, AID_MEDIA_RW, 0007, true)) {
|
|
PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
|
|
return -EIO;
|
|
}
|
|
|
|
if (getMountFlags() & MountFlags::kPrimary) {
|
|
initAsecStage();
|
|
}
|
|
|
|
dev_t before = GetDevice(mFuseWrite);
|
|
|
|
if (!(mFusePid = fork())) {
|
|
if (!(getMountFlags() & MountFlags::kVisible)) {
|
|
// TODO: do we need to wrap this device?
|
|
} else if (getMountFlags() & MountFlags::kPrimary) {
|
|
if (execl(kFusePath, kFusePath,
|
|
"-u", "1023", // AID_MEDIA_RW
|
|
"-g", "1023", // AID_MEDIA_RW
|
|
"-w",
|
|
mRawPath.c_str(),
|
|
stableName.c_str(),
|
|
NULL)) {
|
|
PLOG(ERROR) << "Failed to exec";
|
|
}
|
|
} else {
|
|
if (execl(kFusePath, kFusePath,
|
|
"-u", "1023", // AID_MEDIA_RW
|
|
"-g", "1023", // AID_MEDIA_RW
|
|
mRawPath.c_str(),
|
|
stableName.c_str(),
|
|
NULL)) {
|
|
PLOG(ERROR) << "Failed to exec";
|
|
}
|
|
}
|
|
|
|
PLOG(DEBUG) << "FUSE exiting";
|
|
_exit(1);
|
|
}
|
|
|
|
if (mFusePid == -1) {
|
|
PLOG(ERROR) << getId() << " failed to fork";
|
|
return -errno;
|
|
}
|
|
|
|
while (before == GetDevice(mFuseWrite)) {
|
|
LOG(VERBOSE) << "Waiting for FUSE to spin up...";
|
|
usleep(50000); // 50ms
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
status_t PublicVolume::doUnmount() {
|
|
if (mFusePid > 0) {
|
|
kill(mFusePid, SIGTERM);
|
|
TEMP_FAILURE_RETRY(waitpid(mFusePid, nullptr, 0));
|
|
mFusePid = 0;
|
|
}
|
|
|
|
ForceUnmount(kAsecPath);
|
|
|
|
ForceUnmount(mFuseDefault);
|
|
ForceUnmount(mFuseRead);
|
|
ForceUnmount(mFuseWrite);
|
|
ForceUnmount(mRawPath);
|
|
|
|
rmdir(mFuseDefault.c_str());
|
|
rmdir(mFuseRead.c_str());
|
|
rmdir(mFuseWrite.c_str());
|
|
rmdir(mRawPath.c_str());
|
|
|
|
mFuseDefault.clear();
|
|
mFuseRead.clear();
|
|
mFuseWrite.clear();
|
|
mRawPath.clear();
|
|
|
|
return OK;
|
|
}
|
|
|
|
status_t PublicVolume::doFormat(const std::string& fsType) {
|
|
if (fsType == "vfat" || fsType == "auto") {
|
|
if (WipeBlockDevice(mDevPath) != OK) {
|
|
LOG(WARNING) << getId() << " failed to wipe";
|
|
}
|
|
if (vfat::Format(mDevPath, 0)) {
|
|
LOG(ERROR) << getId() << " failed to format";
|
|
return -errno;
|
|
}
|
|
} else {
|
|
LOG(ERROR) << "Unsupported filesystem " << fsType;
|
|
return -EINVAL;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
} // namespace vold
|
|
} // namespace android
|