Support for private (adopted) volumes.
This adds support for private volumes which is just a filesystem wrapped in a dm-crypt layer. For now we're using the exact same configuration as internal encryption (aes-cbc-essiv:sha256), but we don't store any key material on the removable media. Instead, we store the key on internal storage, and use the GPT partition GUID to identify which key should be used. This means that private external storage is effectively as secure as the internal storage of the device. That is, if the internal storage is encrypted, then our external storage key is also encrypted. When partitioning disks, we now support a "private" mode which has a PrivateVolume partition, and a currently unused 16MB metadata partition reserved for future use. It also supports a "mixed" mode which creates both a PublicVolume and PrivateVolume on the same disk. Mixed mode is currently experimental. For now, just add ext4 support to PrivateVolume; we'll look at f2fs in a future change. Add VolumeBase lifecycle for setting up crypto mappings, and extract blkid logic into shared method. Sprinkle some more "static" around the cryptfs code to improve invariants. Bug: 19993667 Change-Id: Ibd1df6250735b706959a1eb9d9f7219ea85912a0
This commit is contained in:
parent
38cfc02860
commit
9c48498f45
19 changed files with 719 additions and 272 deletions
|
@ -21,6 +21,7 @@ common_src_files := \
|
|||
Disk.cpp \
|
||||
VolumeBase.cpp \
|
||||
PublicVolume.cpp \
|
||||
PrivateVolume.cpp \
|
||||
EmulatedVolume.cpp \
|
||||
Utils.cpp \
|
||||
|
||||
|
|
125
Disk.cpp
125
Disk.cpp
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "Disk.h"
|
||||
#include "PublicVolume.h"
|
||||
#include "PrivateVolume.h"
|
||||
#include "Utils.h"
|
||||
#include "VolumeBase.h"
|
||||
#include "VolumeManager.h"
|
||||
|
@ -26,6 +27,7 @@
|
|||
#include <base/logging.h>
|
||||
#include <diskconfig/diskconfig.h>
|
||||
|
||||
#include <vector>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
@ -35,6 +37,7 @@
|
|||
#include <sys/mount.h>
|
||||
|
||||
using android::base::ReadFileToString;
|
||||
using android::base::WriteStringToFile;
|
||||
using android::base::StringPrintf;
|
||||
|
||||
namespace android {
|
||||
|
@ -52,6 +55,8 @@ static const char* kGptBasicData = "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7";
|
|||
static const char* kGptAndroidMeta = "19A710A2-B3CA-11E4-B026-10604B889DCF";
|
||||
static const char* kGptAndroidExt = "193D1EA4-B3CA-11E4-B075-10604B889DCF";
|
||||
|
||||
static const char* kKeyPath = "/data/misc/vold";
|
||||
|
||||
enum class Table {
|
||||
kUnknown,
|
||||
kMbr,
|
||||
|
@ -105,18 +110,44 @@ status_t Disk::destroy() {
|
|||
return OK;
|
||||
}
|
||||
|
||||
static std::string BuildKeyPath(const std::string& partGuid) {
|
||||
return StringPrintf("%s/ext_%s.key", kKeyPath, partGuid.c_str());
|
||||
}
|
||||
|
||||
void Disk::createPublicVolume(dev_t device) {
|
||||
auto vol = new PublicVolume(device);
|
||||
auto vol = std::shared_ptr<VolumeBase>(new PublicVolume(device));
|
||||
vol->create();
|
||||
|
||||
mVolumes.push_back(std::shared_ptr<VolumeBase>(vol));
|
||||
mVolumes.push_back(vol);
|
||||
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
|
||||
ResponseCode::DiskVolumeCreated,
|
||||
StringPrintf("%s %s", getId().c_str(), vol->getId().c_str()).c_str(), false);
|
||||
}
|
||||
|
||||
void Disk::createPrivateVolume(dev_t device) {
|
||||
// TODO: create and add
|
||||
void Disk::createPrivateVolume(dev_t device, const std::string& partGuid) {
|
||||
std::string tmp;
|
||||
std::string normalizedGuid;
|
||||
if (HexToStr(partGuid, tmp)) {
|
||||
LOG(WARNING) << "Invalid GUID " << partGuid;
|
||||
return;
|
||||
}
|
||||
StrToHex(tmp, normalizedGuid);
|
||||
|
||||
std::string keyRaw;
|
||||
if (!ReadFileToString(BuildKeyPath(normalizedGuid), &keyRaw)) {
|
||||
PLOG(ERROR) << "Failed to load key for GUID " << normalizedGuid;
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "Found key for GUID " << normalizedGuid;
|
||||
|
||||
auto vol = std::shared_ptr<VolumeBase>(new PrivateVolume(device, keyRaw));
|
||||
vol->create();
|
||||
|
||||
mVolumes.push_back(vol);
|
||||
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
|
||||
ResponseCode::DiskVolumeCreated,
|
||||
StringPrintf("%s %s", getId().c_str(), vol->getId().c_str()).c_str(), false);
|
||||
}
|
||||
|
||||
void Disk::destroyAllVolumes() {
|
||||
|
@ -246,7 +277,7 @@ status_t Disk::readPartitions() {
|
|||
if (!strcasecmp(typeGuid, kGptBasicData)) {
|
||||
createPublicVolume(partDevice);
|
||||
} else if (!strcasecmp(typeGuid, kGptAndroidExt)) {
|
||||
createPrivateVolume(partDevice);
|
||||
createPrivateVolume(partDevice, partGuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -262,6 +293,13 @@ status_t Disk::readPartitions() {
|
|||
return OK;
|
||||
}
|
||||
|
||||
status_t Disk::unmountAll() {
|
||||
for (auto vol : mVolumes) {
|
||||
vol->unmount();
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t Disk::partitionPublic() {
|
||||
// TODO: improve this code
|
||||
destroyAllVolumes();
|
||||
|
@ -304,13 +342,84 @@ out:
|
|||
}
|
||||
|
||||
status_t Disk::partitionPrivate() {
|
||||
destroyAllVolumes();
|
||||
return -ENOTSUP;
|
||||
return partitionMixed(0);
|
||||
}
|
||||
|
||||
status_t Disk::partitionMixed(int8_t ratio) {
|
||||
int status = 0;
|
||||
|
||||
destroyAllVolumes();
|
||||
return -ENOTSUP;
|
||||
|
||||
// First nuke any existing partition table
|
||||
std::vector<std::string> cmd;
|
||||
cmd.push_back(kSgdiskPath);
|
||||
cmd.push_back("--zap-all");
|
||||
cmd.push_back(mDevPath);
|
||||
|
||||
if (ForkExecvp(cmd, &status, false, true)) {
|
||||
LOG(ERROR) << "Failed to zap; status " << status;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
// We've had some success above, so generate both the private partition
|
||||
// GUID and encryption key and persist them.
|
||||
std::string partGuidRaw;
|
||||
std::string keyRaw;
|
||||
if (ReadRandomBytes(16, partGuidRaw) || ReadRandomBytes(16, keyRaw)) {
|
||||
LOG(ERROR) << "Failed to generate GUID or key";
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
std::string partGuid;
|
||||
StrToHex(partGuidRaw, partGuid);
|
||||
|
||||
if (!WriteStringToFile(keyRaw, BuildKeyPath(partGuid))) {
|
||||
LOG(ERROR) << "Failed to persist key";
|
||||
return -EIO;
|
||||
} else {
|
||||
LOG(DEBUG) << "Persisted key for GUID " << partGuid;
|
||||
}
|
||||
|
||||
// Now let's build the new GPT table. We heavily rely on sgdisk to
|
||||
// force optimal alignment on the created partitions.
|
||||
cmd.clear();
|
||||
cmd.push_back(kSgdiskPath);
|
||||
|
||||
// If requested, create a public partition first. Mixed-mode partitioning
|
||||
// like this is an experimental feature.
|
||||
if (ratio > 0) {
|
||||
if (ratio < 10 || ratio > 90) {
|
||||
LOG(ERROR) << "Mixed partition ratio must be between 10-90%";
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
uint64_t splitMb = ((mSize / 100) * ratio) / 1024 / 1024;
|
||||
cmd.push_back(StringPrintf("--new=0:0:+%" PRId64 "M", splitMb));
|
||||
cmd.push_back(StringPrintf("--typecode=0:%s", kGptBasicData));
|
||||
cmd.push_back("--change-name=0:shared");
|
||||
}
|
||||
|
||||
// Define a metadata partition which is designed for future use; there
|
||||
// should only be one of these per physical device, even if there are
|
||||
// multiple private volumes.
|
||||
cmd.push_back("--new=0:0:+16M");
|
||||
cmd.push_back(StringPrintf("--typecode=0:%s", kGptAndroidMeta));
|
||||
cmd.push_back("--change-name=0:android_meta");
|
||||
|
||||
// Define a single private partition filling the rest of disk.
|
||||
cmd.push_back("--new=0:0:-0");
|
||||
cmd.push_back(StringPrintf("--typecode=0:%s", kGptAndroidExt));
|
||||
cmd.push_back(StringPrintf("--partition-guid=0:%s", partGuid.c_str()));
|
||||
cmd.push_back("--change-name=0:android_ext");
|
||||
|
||||
cmd.push_back(mDevPath);
|
||||
|
||||
if (ForkExecvp(cmd, &status, false, true)) {
|
||||
LOG(ERROR) << "Failed to partition; status " << status;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int Disk::getMaxMinors() {
|
||||
|
|
4
Disk.h
4
Disk.h
|
@ -68,6 +68,8 @@ public:
|
|||
status_t readMetadata();
|
||||
status_t readPartitions();
|
||||
|
||||
status_t unmountAll();
|
||||
|
||||
status_t partitionPublic();
|
||||
status_t partitionPrivate();
|
||||
status_t partitionMixed(int8_t ratio);
|
||||
|
@ -97,7 +99,7 @@ private:
|
|||
bool mCreated;
|
||||
|
||||
void createPublicVolume(dev_t device);
|
||||
void createPrivateVolume(dev_t device);
|
||||
void createPrivateVolume(dev_t device, const std::string& partGuid);
|
||||
|
||||
void destroyAllVolumes();
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ EmulatedVolume::~EmulatedVolume() {
|
|||
|
||||
status_t EmulatedVolume::doMount() {
|
||||
if (fs_prepare_dir(mFusePath.c_str(), 0700, AID_ROOT, AID_ROOT)) {
|
||||
PLOG(ERROR) << "Failed to create mount point " << mFusePath;
|
||||
PLOG(ERROR) << getId() << " failed to create mount point " << mFusePath;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,7 @@ status_t EmulatedVolume::doMount() {
|
|||
}
|
||||
|
||||
if (mFusePid == -1) {
|
||||
PLOG(ERROR) << "Failed to fork";
|
||||
PLOG(ERROR) << getId() << " failed to fork";
|
||||
return -errno;
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,7 @@ status_t EmulatedVolume::doUnmount() {
|
|||
ForceUnmount(mRawPath);
|
||||
|
||||
if (TEMP_FAILURE_RETRY(rmdir(mFusePath.c_str()))) {
|
||||
PLOG(ERROR) << "Failed to rmdir mount point " << mFusePath;
|
||||
PLOG(ERROR) << getId() << " failed to rmdir mount point " << mFusePath;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
|
|
112
Ext4.cpp
112
Ext4.cpp
|
@ -23,6 +23,8 @@
|
|||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
@ -35,17 +37,92 @@
|
|||
|
||||
#define LOG_TAG "Vold"
|
||||
|
||||
#include <base/stringprintf.h>
|
||||
#include <cutils/log.h>
|
||||
#include <cutils/properties.h>
|
||||
|
||||
#include <logwrap/logwrap.h>
|
||||
|
||||
#include "Ext4.h"
|
||||
#include "Utils.h"
|
||||
#include "VoldUtil.h"
|
||||
|
||||
#define MKEXT4FS_PATH "/system/bin/make_ext4fs"
|
||||
#define RESIZE2FS_PATH "/system/bin/resize2fs"
|
||||
|
||||
using android::base::StringPrintf;
|
||||
|
||||
static const char* kMkfsPath = "/system/bin/make_ext4fs";
|
||||
|
||||
static const char* kFsckPath = "/system/bin/e2fsck";
|
||||
static const char* kFsckLogFile = "/dev/fscklogs/log";
|
||||
|
||||
int Ext4::check(const char *fsPath, const char *mountPoint) {
|
||||
// The following is shamelessly borrowed from fs_mgr.c, so it should be
|
||||
// kept in sync with any changes over there.
|
||||
|
||||
char* blk_device = (char*) fsPath;
|
||||
char* target = (char*) mountPoint;
|
||||
|
||||
int status;
|
||||
int ret;
|
||||
long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID;
|
||||
char *tmpmnt_opts = (char*) "nomblk_io_submit,errors=remount-ro";
|
||||
char *e2fsck_argv[] = {
|
||||
(char*) kFsckPath,
|
||||
(char*) "-y",
|
||||
blk_device
|
||||
};
|
||||
|
||||
/*
|
||||
* First try to mount and unmount the filesystem. We do this because
|
||||
* the kernel is more efficient than e2fsck in running the journal and
|
||||
* processing orphaned inodes, and on at least one device with a
|
||||
* performance issue in the emmc firmware, it can take e2fsck 2.5 minutes
|
||||
* to do what the kernel does in about a second.
|
||||
*
|
||||
* After mounting and unmounting the filesystem, run e2fsck, and if an
|
||||
* error is recorded in the filesystem superblock, e2fsck will do a full
|
||||
* check. Otherwise, it does nothing. If the kernel cannot mount the
|
||||
* filesytsem due to an error, e2fsck is still run to do a full check
|
||||
* fix the filesystem.
|
||||
*/
|
||||
ret = mount(blk_device, target, "ext4", tmpmnt_flags, tmpmnt_opts);
|
||||
if (!ret) {
|
||||
int i;
|
||||
for (i = 0; i < 5; i++) {
|
||||
// Try to umount 5 times before continuing on.
|
||||
// Should we try rebooting if all attempts fail?
|
||||
int result = umount(target);
|
||||
if (result == 0) {
|
||||
break;
|
||||
}
|
||||
ALOGW("%s(): umount(%s)=%d: %s\n", __func__, target, result, strerror(errno));
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Some system images do not have e2fsck for licensing reasons
|
||||
* (e.g. recent SDK system images). Detect these and skip the check.
|
||||
*/
|
||||
if (access(kFsckPath, X_OK)) {
|
||||
ALOGD("Not running %s on %s (executable not in system image)\n",
|
||||
kFsckPath, blk_device);
|
||||
} else {
|
||||
ALOGD("Running %s on %s\n", kFsckPath, blk_device);
|
||||
|
||||
ret = android_fork_execvp(ARRAY_SIZE(e2fsck_argv), e2fsck_argv,
|
||||
&status, false, true);
|
||||
|
||||
if (ret < 0) {
|
||||
/* No need to check for error in fork, we can't really handle it now */
|
||||
ALOGW("Failed trying to run %s\n", kFsckPath);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Ext4::doMount(const char *fsPath, const char *mountPoint, bool ro, bool remount,
|
||||
bool executable) {
|
||||
int rc;
|
||||
|
@ -112,28 +189,23 @@ int Ext4::resize(const char *fspath, unsigned int numSectors) {
|
|||
}
|
||||
|
||||
int Ext4::format(const char *fsPath, unsigned int numSectors, const char *mountpoint) {
|
||||
const char *args[7];
|
||||
int rc;
|
||||
int status;
|
||||
|
||||
args[0] = MKEXT4FS_PATH;
|
||||
args[1] = "-J";
|
||||
args[2] = "-a";
|
||||
args[3] = mountpoint;
|
||||
std::vector<std::string> cmd;
|
||||
cmd.push_back(kMkfsPath);
|
||||
cmd.push_back("-J");
|
||||
|
||||
cmd.push_back("-a");
|
||||
cmd.push_back(mountpoint);
|
||||
|
||||
if (numSectors) {
|
||||
char tmp[32];
|
||||
snprintf(tmp, sizeof(tmp), "%u", numSectors * 512);
|
||||
const char *size = tmp;
|
||||
args[4] = "-l";
|
||||
args[5] = size;
|
||||
args[6] = fsPath;
|
||||
rc = android_fork_execvp(ARRAY_SIZE(args), (char **)args, &status, false, true);
|
||||
} else {
|
||||
args[4] = fsPath;
|
||||
rc = android_fork_execvp(5, (char **)args, &status, false, true);
|
||||
cmd.push_back("-l");
|
||||
cmd.push_back(StringPrintf("%u", numSectors * 512));
|
||||
}
|
||||
rc = android_fork_execvp(ARRAY_SIZE(args), (char **)args, &status, false,
|
||||
true);
|
||||
|
||||
cmd.push_back(fsPath);
|
||||
|
||||
int rc = android::vold::ForkExecvp(cmd, &status, false, true);
|
||||
if (rc != 0) {
|
||||
SLOGE("Filesystem (ext4) format failed due to logwrap error");
|
||||
errno = EIO;
|
||||
|
|
1
Ext4.h
1
Ext4.h
|
@ -21,6 +21,7 @@
|
|||
|
||||
class Ext4 {
|
||||
public:
|
||||
static int check(const char *fsPath, const char *mountPoint);
|
||||
static int doMount(const char *fsPath, const char *mountPoint, bool ro, bool remount,
|
||||
bool executable);
|
||||
static int format(const char *fsPath, unsigned int numSectors, const char *mountpoint);
|
||||
|
|
145
PrivateVolume.cpp
Normal file
145
PrivateVolume.cpp
Normal file
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* 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 "Ext4.h"
|
||||
#include "PrivateVolume.h"
|
||||
#include "Utils.h"
|
||||
#include "VolumeManager.h"
|
||||
#include "ResponseCode.h"
|
||||
#include "cryptfs.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>
|
||||
#include <sys/param.h>
|
||||
|
||||
using android::base::StringPrintf;
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
PrivateVolume::PrivateVolume(dev_t device, const std::string& keyRaw) :
|
||||
VolumeBase(Type::kPrivate), mRawDevice(device), mKeyRaw(keyRaw) {
|
||||
setId(StringPrintf("private:%u,%u", major(device), minor(device)));
|
||||
mRawDevPath = StringPrintf("/dev/block/vold/%s", getId().c_str());
|
||||
mPath = StringPrintf("/mnt/secure/%s", getId().c_str());
|
||||
}
|
||||
|
||||
PrivateVolume::~PrivateVolume() {
|
||||
}
|
||||
|
||||
status_t PrivateVolume::readMetadata() {
|
||||
status_t res = ReadMetadata(mDmDevPath, mFsType, mFsUuid, mFsLabel);
|
||||
|
||||
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
|
||||
ResponseCode::VolumeFsTypeChanged,
|
||||
StringPrintf("%s %s", getId().c_str(), mFsType.c_str()).c_str(), false);
|
||||
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
|
||||
ResponseCode::VolumeFsUuidChanged,
|
||||
StringPrintf("%s %s", getId().c_str(), mFsUuid.c_str()).c_str(), false);
|
||||
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
|
||||
ResponseCode::VolumeFsLabelChanged,
|
||||
StringPrintf("%s %s", getId().c_str(), mFsLabel.c_str()).c_str(), false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
status_t PrivateVolume::doCreate() {
|
||||
if (CreateDeviceNode(mRawDevPath, mRawDevice)) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
// Recover from stale vold by tearing down any old mappings
|
||||
cryptfs_revert_ext_volume(getId().c_str());
|
||||
|
||||
// TODO: figure out better SELinux labels for private volumes
|
||||
|
||||
unsigned char* key = (unsigned char*) mKeyRaw.data();
|
||||
char crypto_blkdev[MAXPATHLEN];
|
||||
int res = cryptfs_setup_ext_volume(getId().c_str(), mRawDevPath.c_str(),
|
||||
key, mKeyRaw.size(), crypto_blkdev);
|
||||
mDmDevPath = crypto_blkdev;
|
||||
if (res != 0) {
|
||||
PLOG(ERROR) << getId() << " failed to setup cryptfs";
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t PrivateVolume::doDestroy() {
|
||||
if (cryptfs_revert_ext_volume(getId().c_str())) {
|
||||
LOG(ERROR) << getId() << " failed to revert cryptfs";
|
||||
}
|
||||
return DestroyDeviceNode(mRawDevPath);
|
||||
}
|
||||
|
||||
status_t PrivateVolume::doMount() {
|
||||
if (readMetadata()) {
|
||||
LOG(ERROR) << getId() << " failed to read metadata";
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (Ext4::check(mDmDevPath.c_str(), mPath.c_str())) {
|
||||
PLOG(ERROR) << getId() << " failed filesystem check";
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
setPath(mPath);
|
||||
|
||||
if (fs_prepare_dir(mPath.c_str(), 0700, AID_ROOT, AID_ROOT)) {
|
||||
PLOG(ERROR) << getId() << " failed to create mount point " << mPath;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (Ext4::doMount(mDmDevPath.c_str(), mPath.c_str(), false, false, true)) {
|
||||
PLOG(ERROR) << getId() << " failed to mount";
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t PrivateVolume::doUnmount() {
|
||||
ForceUnmount(mPath);
|
||||
|
||||
if (TEMP_FAILURE_RETRY(rmdir(mPath.c_str()))) {
|
||||
PLOG(ERROR) << getId() << " failed to rmdir mount point " << mPath;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t PrivateVolume::doFormat() {
|
||||
// TODO: change mountpoint once we have selinux labels
|
||||
if (Ext4::format(mDmDevPath.c_str(), 0, "/data")) {
|
||||
PLOG(ERROR) << getId() << " failed to format";
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
78
PrivateVolume.h
Normal file
78
PrivateVolume.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_VOLD_PRIVATE_VOLUME_H
|
||||
#define ANDROID_VOLD_PRIVATE_VOLUME_H
|
||||
|
||||
#include "VolumeBase.h"
|
||||
|
||||
#include <cutils/multiuser.h>
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
/*
|
||||
* Private storage provided by an encrypted partition.
|
||||
*
|
||||
* Given a raw block device, it knows how to wrap it in dm-crypt and
|
||||
* format as ext4/f2fs. EmulatedVolume can be stacked above it.
|
||||
*
|
||||
* This volume is designed to behave much like the internal /data
|
||||
* partition, both in layout and function. For example, apps and
|
||||
* private app data can be safely stored on this volume because the
|
||||
* keys are tightly tied to this device.
|
||||
*/
|
||||
class PrivateVolume : public VolumeBase {
|
||||
public:
|
||||
PrivateVolume(dev_t device, const std::string& keyRaw);
|
||||
virtual ~PrivateVolume();
|
||||
|
||||
protected:
|
||||
status_t doCreate() override;
|
||||
status_t doDestroy() override;
|
||||
status_t doMount() override;
|
||||
status_t doUnmount() override;
|
||||
status_t doFormat() override;
|
||||
|
||||
status_t readMetadata();
|
||||
|
||||
private:
|
||||
/* Kernel device of raw, encrypted partition */
|
||||
dev_t mRawDevice;
|
||||
/* Path to raw, encrypted block device */
|
||||
std::string mRawDevPath;
|
||||
/* Path to decrypted block device */
|
||||
std::string mDmDevPath;
|
||||
/* Path where decrypted device is mounted */
|
||||
std::string mPath;
|
||||
|
||||
/* Encryption key as raw bytes */
|
||||
std::string mKeyRaw;
|
||||
|
||||
/* Filesystem type */
|
||||
std::string mFsType;
|
||||
/* Filesystem UUID */
|
||||
std::string mFsUuid;
|
||||
/* User-visible filesystem label */
|
||||
std::string mFsLabel;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PrivateVolume);
|
||||
};
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
||||
|
||||
#endif
|
|
@ -37,7 +37,6 @@ using android::base::StringPrintf;
|
|||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
static const char* kBlkidPath = "/system/bin/blkid";
|
||||
static const char* kFusePath = "/system/bin/sdcard";
|
||||
|
||||
static const char* kAsecPath = "/mnt/secure/asec";
|
||||
|
@ -46,51 +45,13 @@ 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());
|
||||
CreateDeviceNode(mDevPath, mDevice);
|
||||
}
|
||||
|
||||
PublicVolume::~PublicVolume() {
|
||||
DestroyDeviceNode(mDevPath);
|
||||
}
|
||||
|
||||
status_t PublicVolume::readMetadata() {
|
||||
mFsType.clear();
|
||||
mFsUuid.clear();
|
||||
mFsLabel.clear();
|
||||
|
||||
std::string path(StringPrintf("%s -c /dev/null %s", kBlkidPath, mDevPath.c_str()));
|
||||
FILE* fp = popen(path.c_str(), "r");
|
||||
if (!fp) {
|
||||
PLOG(ERROR) << "Failed to run " << path;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
status_t res = OK;
|
||||
char line[1024];
|
||||
char value[128];
|
||||
if (fgets(line, sizeof(line), fp) != nullptr) {
|
||||
LOG(DEBUG) << "blkid identified as " << line;
|
||||
|
||||
char* start = strstr(line, "TYPE=");
|
||||
if (start != nullptr && sscanf(start + 5, "\"%127[^\"]\"", value) == 1) {
|
||||
mFsType = value;
|
||||
}
|
||||
|
||||
start = strstr(line, "UUID=");
|
||||
if (start != nullptr && sscanf(start + 5, "\"%127[^\"]\"", value) == 1) {
|
||||
mFsUuid = value;
|
||||
}
|
||||
|
||||
start = strstr(line, "LABEL=");
|
||||
if (start != nullptr && sscanf(start + 6, "\"%127[^\"]\"", value) == 1) {
|
||||
mFsLabel = value;
|
||||
}
|
||||
} else {
|
||||
LOG(WARNING) << "blkid failed to identify " << mDevPath;
|
||||
res = -ENODATA;
|
||||
}
|
||||
|
||||
pclose(fp);
|
||||
status_t res = ReadMetadata(mDevPath, mFsType, mFsUuid, mFsLabel);
|
||||
|
||||
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
|
||||
ResponseCode::VolumeFsTypeChanged,
|
||||
|
@ -113,13 +74,13 @@ status_t PublicVolume::initAsecStage() {
|
|||
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) << "Failed to rename legacy ASEC dir";
|
||||
PLOG(WARNING) << getId() << " failed to rename legacy ASEC dir";
|
||||
}
|
||||
}
|
||||
|
||||
if (TEMP_FAILURE_RETRY(mkdir(securePath.c_str(), 0700))) {
|
||||
if (errno != EEXIST) {
|
||||
PLOG(WARNING) << "Creating ASEC stage failed";
|
||||
PLOG(WARNING) << getId() << " creating ASEC stage failed";
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
@ -129,15 +90,23 @@ status_t PublicVolume::initAsecStage() {
|
|||
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 (Fat::check(mDevPath.c_str())) {
|
||||
LOG(ERROR) << "Failed filesystem check; not mounting";
|
||||
LOG(ERROR) << getId() << " failed filesystem check";
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
readMetadata();
|
||||
|
||||
// Use UUID as stable name, if available
|
||||
std::string stableName = getId();
|
||||
if (!mFsUuid.empty()) {
|
||||
|
@ -149,17 +118,17 @@ status_t PublicVolume::doMount() {
|
|||
setPath(mFusePath);
|
||||
|
||||
if (fs_prepare_dir(mRawPath.c_str(), 0700, AID_ROOT, AID_ROOT)) {
|
||||
PLOG(ERROR) << "Failed to create mount point " << mRawPath;
|
||||
PLOG(ERROR) << getId() << " failed to create mount point " << mRawPath;
|
||||
return -errno;
|
||||
}
|
||||
if (fs_prepare_dir(mFusePath.c_str(), 0700, AID_ROOT, AID_ROOT)) {
|
||||
PLOG(ERROR) << "Failed to create mount point " << mFusePath;
|
||||
PLOG(ERROR) << getId() << " failed to create mount point " << mFusePath;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (Fat::doMount(mDevPath.c_str(), mRawPath.c_str(), false, false, false,
|
||||
AID_MEDIA_RW, AID_MEDIA_RW, 0007, true)) {
|
||||
PLOG(ERROR) << "Failed to mount " << mDevPath;
|
||||
PLOG(ERROR) << getId() << " failed to mount " << mDevPath;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
@ -202,7 +171,7 @@ status_t PublicVolume::doMount() {
|
|||
}
|
||||
|
||||
if (mFusePid == -1) {
|
||||
PLOG(ERROR) << "Failed to fork";
|
||||
PLOG(ERROR) << getId() << " failed to fork";
|
||||
return -errno;
|
||||
}
|
||||
|
||||
|
@ -220,10 +189,10 @@ status_t PublicVolume::doUnmount() {
|
|||
ForceUnmount(mRawPath);
|
||||
|
||||
if (TEMP_FAILURE_RETRY(rmdir(mRawPath.c_str()))) {
|
||||
PLOG(ERROR) << "Failed to rmdir mount point " << mRawPath;
|
||||
PLOG(ERROR) << getId() << " failed to rmdir mount point " << mRawPath;
|
||||
}
|
||||
if (TEMP_FAILURE_RETRY(rmdir(mFusePath.c_str()))) {
|
||||
PLOG(ERROR) << "Failed to rmdir mount point " << mFusePath;
|
||||
PLOG(ERROR) << getId() << " failed to rmdir mount point " << mFusePath;
|
||||
}
|
||||
|
||||
mFusePath.clear();
|
||||
|
@ -234,7 +203,7 @@ status_t PublicVolume::doUnmount() {
|
|||
|
||||
status_t PublicVolume::doFormat() {
|
||||
if (Fat::format(mDevPath.c_str(), 0, true)) {
|
||||
LOG(ERROR) << "Failed to format";
|
||||
LOG(ERROR) << getId() << " failed to format";
|
||||
return -errno;
|
||||
}
|
||||
return OK;
|
||||
|
|
|
@ -43,6 +43,8 @@ public:
|
|||
virtual ~PublicVolume();
|
||||
|
||||
protected:
|
||||
status_t doCreate() override;
|
||||
status_t doDestroy() override;
|
||||
status_t doMount() override;
|
||||
status_t doUnmount() override;
|
||||
status_t doFormat() override;
|
||||
|
|
143
Utils.cpp
143
Utils.cpp
|
@ -19,8 +19,10 @@
|
|||
#include "Process.h"
|
||||
|
||||
#include <base/logging.h>
|
||||
#include <base/stringprintf.h>
|
||||
#include <cutils/fs.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
#include <logwrap/logwrap.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <linux/fs.h>
|
||||
|
@ -34,9 +36,13 @@
|
|||
#define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */
|
||||
#endif
|
||||
|
||||
using android::base::StringPrintf;
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
static const char* kBlkidPath = "/system/bin/blkid";
|
||||
|
||||
status_t CreateDeviceNode(const std::string& path, dev_t dev) {
|
||||
const char* cpath = path.c_str();
|
||||
status_t res = 0;
|
||||
|
@ -81,14 +87,14 @@ status_t ForceUnmount(const std::string& path) {
|
|||
}
|
||||
PLOG(WARNING) << "Failed to unmount " << path << "; sending SIGTERM";
|
||||
Process::killProcessesWithOpenFiles(cpath, SIGTERM);
|
||||
sleep(1);
|
||||
sleep(5);
|
||||
|
||||
if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
|
||||
return OK;
|
||||
}
|
||||
PLOG(WARNING) << "Failed to unmount " << path << "; sending SIGKILL";
|
||||
Process::killProcessesWithOpenFiles(cpath, SIGKILL);
|
||||
sleep(1);
|
||||
sleep(5);
|
||||
|
||||
if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
|
||||
return OK;
|
||||
|
@ -105,5 +111,138 @@ status_t BindMount(const std::string& source, const std::string& target) {
|
|||
return OK;
|
||||
}
|
||||
|
||||
status_t ReadMetadata(const std::string& path, std::string& fsType,
|
||||
std::string& fsUuid, std::string& fsLabel) {
|
||||
fsType.clear();
|
||||
fsUuid.clear();
|
||||
fsLabel.clear();
|
||||
|
||||
std::string cmd(StringPrintf("%s -c /dev/null %s", kBlkidPath, path.c_str()));
|
||||
FILE* fp = popen(cmd.c_str(), "r");
|
||||
if (!fp) {
|
||||
PLOG(ERROR) << "Failed to run " << cmd;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
status_t res = OK;
|
||||
char line[1024];
|
||||
char value[128];
|
||||
if (fgets(line, sizeof(line), fp) != nullptr) {
|
||||
LOG(DEBUG) << "blkid identified " << path << " as " << line;
|
||||
|
||||
// Extract values from blkid output, if defined
|
||||
char* start = strstr(line, "TYPE=");
|
||||
if (start != nullptr && sscanf(start + 5, "\"%127[^\"]\"", value) == 1) {
|
||||
fsType = value;
|
||||
}
|
||||
|
||||
start = strstr(line, "UUID=");
|
||||
if (start != nullptr && sscanf(start + 5, "\"%127[^\"]\"", value) == 1) {
|
||||
fsUuid = value;
|
||||
}
|
||||
|
||||
start = strstr(line, "LABEL=");
|
||||
if (start != nullptr && sscanf(start + 6, "\"%127[^\"]\"", value) == 1) {
|
||||
fsLabel = value;
|
||||
}
|
||||
} else {
|
||||
LOG(WARNING) << "blkid failed to identify " << path;
|
||||
res = -ENODATA;
|
||||
}
|
||||
|
||||
pclose(fp);
|
||||
return res;
|
||||
}
|
||||
|
||||
status_t ForkExecvp(const std::vector<std::string>& args, int* status,
|
||||
bool ignore_int_quit, bool logwrap) {
|
||||
int argc = args.size();
|
||||
char** argv = (char**) calloc(argc, sizeof(char*));
|
||||
for (int i = 0; i < argc; i++) {
|
||||
argv[i] = (char*) args[i].c_str();
|
||||
if (i == 0) {
|
||||
LOG(VERBOSE) << args[i];
|
||||
} else {
|
||||
LOG(VERBOSE) << " " << args[i];
|
||||
}
|
||||
}
|
||||
int res = android_fork_execvp(argc, argv, status, ignore_int_quit, logwrap);
|
||||
free(argv);
|
||||
return res;
|
||||
}
|
||||
|
||||
status_t ReadRandomBytes(size_t bytes, std::string& out) {
|
||||
out.clear();
|
||||
|
||||
int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
|
||||
if (fd == -1) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
char buf[BUFSIZ];
|
||||
size_t n;
|
||||
while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], std::min(sizeof(buf), bytes)))) > 0) {
|
||||
out.append(buf, n);
|
||||
bytes -= n;
|
||||
}
|
||||
TEMP_FAILURE_RETRY(close(fd));
|
||||
|
||||
if (bytes == 0) {
|
||||
return OK;
|
||||
} else {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
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]) {
|
||||
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;
|
||||
}
|
||||
|
||||
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++) {
|
||||
hex.push_back(kLookup[str[i] >> 4]);
|
||||
hex.push_back(kLookup[str[i] & 0x0F]);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
||||
|
|
13
Utils.h
13
Utils.h
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <utils/Errors.h>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. It goes in the private:
|
||||
|
@ -41,6 +42,18 @@ status_t ForceUnmount(const std::string& path);
|
|||
/* Creates bind mount from source to target */
|
||||
status_t BindMount(const std::string& source, const std::string& target);
|
||||
|
||||
/* Reads filesystem metadata from device at path */
|
||||
status_t ReadMetadata(const std::string& path, std::string& fsType,
|
||||
std::string& fsUuid, std::string& fsLabel);
|
||||
|
||||
status_t ForkExecvp(const std::vector<std::string>& args, int* status,
|
||||
bool ignore_int_quit, bool logwrap);
|
||||
|
||||
status_t ReadRandomBytes(size_t bytes, std::string& out);
|
||||
|
||||
status_t HexToStr(const std::string& hex, std::string& str);
|
||||
status_t StrToHex(const std::string& str, std::string& hex);
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
||||
|
||||
|
|
12
Volume.cpp
12
Volume.cpp
|
@ -395,12 +395,12 @@ int Volume::mountVol() {
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (cryptfs_setup_volume(getLabel(), MAJOR(deviceNodes[0]), MINOR(deviceNodes[0]),
|
||||
new_sys_path, sizeof(new_sys_path),
|
||||
&new_major, &new_minor)) {
|
||||
// if (cryptfs_setup_volume(getLabel(), MAJOR(deviceNodes[0]), MINOR(deviceNodes[0]),
|
||||
// new_sys_path, sizeof(new_sys_path),
|
||||
// &new_major, &new_minor)) {
|
||||
SLOGE("Cannot setup encryption mapping for %s\n", getMountpoint());
|
||||
return -1;
|
||||
}
|
||||
// }
|
||||
/* We now have the new sysfs path for the decrypted block device, and the
|
||||
* majore and minor numbers for it. So, create the device, update the
|
||||
* path to the new sysfs path, and continue.
|
||||
|
@ -587,8 +587,8 @@ int Volume::unmountVol(bool force, bool revert) {
|
|||
* the device info to the original values.
|
||||
*/
|
||||
if (revert && isDecrypted()) {
|
||||
cryptfs_revert_volume(getLabel());
|
||||
revertDeviceInfo();
|
||||
// cryptfs_revert_volume(getLabel());
|
||||
// revertDeviceInfo();
|
||||
SLOGI("Encrypted volume %s reverted successfully", getMountpoint());
|
||||
}
|
||||
|
||||
|
|
|
@ -113,10 +113,16 @@ std::shared_ptr<VolumeBase> VolumeBase::findVolume(const std::string& id) {
|
|||
|
||||
status_t VolumeBase::create() {
|
||||
CHECK(!mCreated);
|
||||
|
||||
mCreated = true;
|
||||
status_t res = doCreate();
|
||||
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
|
||||
ResponseCode::VolumeCreated,
|
||||
StringPrintf("%s %d", getId().c_str(), mType).c_str(), false);
|
||||
return res;
|
||||
}
|
||||
|
||||
status_t VolumeBase::doCreate() {
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
@ -127,9 +133,14 @@ status_t VolumeBase::destroy() {
|
|||
unmount();
|
||||
}
|
||||
|
||||
mCreated = false;
|
||||
VolumeManager::Instance()->getBroadcaster()->sendBroadcast(
|
||||
ResponseCode::VolumeDestroyed, getId().c_str(), false);
|
||||
status_t res = doDestroy();
|
||||
mCreated = false;
|
||||
return res;
|
||||
}
|
||||
|
||||
status_t VolumeBase::doDestroy() {
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -87,7 +87,6 @@ public:
|
|||
|
||||
status_t create();
|
||||
status_t destroy();
|
||||
|
||||
status_t mount();
|
||||
status_t unmount();
|
||||
status_t format();
|
||||
|
@ -95,6 +94,8 @@ public:
|
|||
protected:
|
||||
explicit VolumeBase(Type type);
|
||||
|
||||
virtual status_t doCreate();
|
||||
virtual status_t doDestroy();
|
||||
virtual status_t doMount() = 0;
|
||||
virtual status_t doUnmount() = 0;
|
||||
virtual status_t doFormat();
|
||||
|
|
|
@ -243,30 +243,7 @@ void VolumeManager::setDebug(bool enable) {
|
|||
int VolumeManager::start() {
|
||||
// Always start from a clean slate by unmounting everything in
|
||||
// directories that we own, in case we crashed.
|
||||
FILE* fp = setmntent("/proc/mounts", "r");
|
||||
if (fp == NULL) {
|
||||
SLOGE("Error opening /proc/mounts: %s", strerror(errno));
|
||||
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) {
|
||||
if (strncmp(mentry->mnt_dir, "/mnt/", 5) == 0
|
||||
|| strncmp(mentry->mnt_dir, "/storage/", 9) == 0) {
|
||||
toUnmount.push_front(std::string(mentry->mnt_dir));
|
||||
}
|
||||
}
|
||||
endmntent(fp);
|
||||
|
||||
for (auto path : toUnmount) {
|
||||
SLOGW("Tearing down stale mount %s", path.c_str());
|
||||
android::vold::ForceUnmount(path);
|
||||
}
|
||||
|
||||
// TODO: nuke all files under mnt and storage tmpfs too?
|
||||
unmountAll();
|
||||
|
||||
// Assume that we always have an emulated volume on internal
|
||||
// storage; the framework will decide if it should be mounted.
|
||||
|
@ -440,6 +417,7 @@ int VolumeManager::reset() {
|
|||
}
|
||||
|
||||
int VolumeManager::shutdown() {
|
||||
mInternalEmulated->destroy();
|
||||
for (auto disk : mDisks) {
|
||||
disk->destroy();
|
||||
}
|
||||
|
@ -447,6 +425,43 @@ int VolumeManager::shutdown() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int VolumeManager::unmountAll() {
|
||||
// First, try gracefully unmounting all known devices
|
||||
if (mInternalEmulated != nullptr) {
|
||||
mInternalEmulated->unmount();
|
||||
}
|
||||
for (auto disk : mDisks) {
|
||||
disk->unmountAll();
|
||||
}
|
||||
|
||||
// Worst case we might have some stale mounts lurking around, so
|
||||
// force unmount those just to be safe.
|
||||
FILE* fp = setmntent("/proc/mounts", "r");
|
||||
if (fp == NULL) {
|
||||
SLOGE("Error opening /proc/mounts: %s", strerror(errno));
|
||||
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) {
|
||||
if (strncmp(mentry->mnt_dir, "/mnt/", 5) == 0
|
||||
|| strncmp(mentry->mnt_dir, "/storage/", 9) == 0) {
|
||||
toUnmount.push_front(std::string(mentry->mnt_dir));
|
||||
}
|
||||
}
|
||||
endmntent(fp);
|
||||
|
||||
for (auto path : toUnmount) {
|
||||
SLOGW("Tearing down stale mount %s", path.c_str());
|
||||
android::vold::ForceUnmount(path);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int VolumeManager::listVolumes(SocketClient *cli, bool broadcast) {
|
||||
VolumeCollection::iterator i;
|
||||
char msg[256];
|
||||
|
@ -1831,35 +1846,6 @@ int VolumeManager::unshareVolume(const char *label, const char *method) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int vold_disableVol(const char *label) {
|
||||
VolumeManager *vm = VolumeManager::Instance();
|
||||
vm->disableVolumeManager();
|
||||
vm->unshareVolume(label, "ums");
|
||||
return vm->unmountVolume(label, true, false);
|
||||
}
|
||||
|
||||
extern "C" int vold_getNumDirectVolumes(void) {
|
||||
VolumeManager *vm = VolumeManager::Instance();
|
||||
return vm->getNumDirectVolumes();
|
||||
}
|
||||
|
||||
int VolumeManager::getNumDirectVolumes(void) {
|
||||
VolumeCollection::iterator i;
|
||||
int n=0;
|
||||
|
||||
for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
|
||||
if ((*i)->getShareDevice() != (dev_t)0) {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
extern "C" int vold_getDirectVolumeList(struct volume_info *vol_list) {
|
||||
VolumeManager *vm = VolumeManager::Instance();
|
||||
return vm->getDirectVolumeList(vol_list);
|
||||
}
|
||||
|
||||
int VolumeManager::getDirectVolumeList(struct volume_info *vol_list) {
|
||||
VolumeCollection::iterator i;
|
||||
int n=0;
|
||||
|
@ -1902,15 +1888,9 @@ int VolumeManager::unmountVolume(const char *label, bool force, bool revert) {
|
|||
return v->unmountVol(force, revert);
|
||||
}
|
||||
|
||||
extern "C" int vold_unmountAllAsecs(void) {
|
||||
int rc;
|
||||
|
||||
extern "C" int vold_unmountAll(void) {
|
||||
VolumeManager *vm = VolumeManager::Instance();
|
||||
rc = vm->unmountAllAsecsInDir(Volume::SEC_ASECDIR_EXT);
|
||||
if (vm->unmountAllAsecsInDir(Volume::SEC_ASECDIR_INT)) {
|
||||
rc = -1;
|
||||
}
|
||||
return rc;
|
||||
return vm->unmountAll();
|
||||
}
|
||||
|
||||
#define ID_BUF_LEN 256
|
||||
|
|
|
@ -118,6 +118,8 @@ public:
|
|||
int reset();
|
||||
/* Prepare for device shutdown, safely unmounting all devices */
|
||||
int shutdown();
|
||||
/* Unmount all volumes, usually for encryption */
|
||||
int unmountAll();
|
||||
|
||||
int listVolumes(SocketClient *cli, bool broadcast);
|
||||
int mountVolume(const char *label);
|
||||
|
@ -213,10 +215,7 @@ private:
|
|||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
#define UNMOUNT_NOT_MOUNTED_ERR -2
|
||||
int vold_disableVol(const char *label);
|
||||
int vold_getNumDirectVolumes(void);
|
||||
int vold_getDirectVolumeList(struct volume_info *v);
|
||||
int vold_unmountAllAsecs(void);
|
||||
int vold_unmountAll(void);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
143
cryptfs.c
143
cryptfs.c
|
@ -945,9 +945,8 @@ static unsigned char* convert_hex_ascii_to_key(const char* master_key_ascii,
|
|||
/* Convert a binary key of specified length into an ascii hex string equivalent,
|
||||
* without the leading 0x and with null termination
|
||||
*/
|
||||
static void convert_key_to_hex_ascii(unsigned char *master_key, unsigned int keysize,
|
||||
char *master_key_ascii)
|
||||
{
|
||||
static void convert_key_to_hex_ascii(const unsigned char *master_key,
|
||||
unsigned int keysize, char *master_key_ascii) {
|
||||
unsigned int i, a;
|
||||
unsigned char nibble;
|
||||
|
||||
|
@ -965,10 +964,9 @@ static void convert_key_to_hex_ascii(unsigned char *master_key, unsigned int key
|
|||
|
||||
}
|
||||
|
||||
static int load_crypto_mapping_table(struct crypt_mnt_ftr *crypt_ftr, unsigned char *master_key,
|
||||
char *real_blk_name, const char *name, int fd,
|
||||
char *extra_params)
|
||||
{
|
||||
static int load_crypto_mapping_table(struct crypt_mnt_ftr *crypt_ftr,
|
||||
const unsigned char *master_key, const char *real_blk_name,
|
||||
const char *name, int fd, const char *extra_params) {
|
||||
_Alignas(struct dm_ioctl) char buffer[DM_CRYPT_BUF_SIZE];
|
||||
struct dm_ioctl *io;
|
||||
struct dm_target_spec *tgt;
|
||||
|
@ -1057,9 +1055,9 @@ static int get_dm_crypt_version(int fd, const char *name, int *version)
|
|||
return -1;
|
||||
}
|
||||
|
||||
static int create_crypto_blk_dev(struct crypt_mnt_ftr *crypt_ftr, unsigned char *master_key,
|
||||
char *real_blk_name, char *crypto_blk_name, const char *name)
|
||||
{
|
||||
static int create_crypto_blk_dev(struct crypt_mnt_ftr *crypt_ftr,
|
||||
const unsigned char *master_key, const char *real_blk_name,
|
||||
char *crypto_blk_name, const char *name) {
|
||||
char buffer[DM_CRYPT_BUF_SIZE];
|
||||
struct dm_ioctl *io;
|
||||
unsigned int minor;
|
||||
|
@ -1885,36 +1883,18 @@ static int test_mount_encrypted_fs(struct crypt_mnt_ftr* crypt_ftr,
|
|||
return rc;
|
||||
}
|
||||
|
||||
/* Called by vold when it wants to undo the crypto mapping of a volume it
|
||||
* manages. This is usually in response to a factory reset, when we want
|
||||
* to undo the crypto mapping so the volume is formatted in the clear.
|
||||
*/
|
||||
int cryptfs_revert_volume(const char *label)
|
||||
{
|
||||
return delete_crypto_blk_dev((char *)label);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by vold when it's asked to mount an encrypted, nonremovable volume.
|
||||
* Setup a dm-crypt mapping, use the saved master key from
|
||||
* setting up the /data mapping, and return the new device path.
|
||||
* Called by vold when it's asked to mount an encrypted external
|
||||
* storage volume. The incoming partition has no crypto header/footer,
|
||||
* as any metadata is been stored in a separate, small partition.
|
||||
*
|
||||
* out_crypto_blkdev must be MAXPATHLEN.
|
||||
*/
|
||||
int cryptfs_setup_volume(const char *label, int major, int minor,
|
||||
char *crypto_sys_path, unsigned int max_path,
|
||||
int *new_major, int *new_minor)
|
||||
{
|
||||
char real_blkdev[MAXPATHLEN], crypto_blkdev[MAXPATHLEN];
|
||||
struct crypt_mnt_ftr sd_crypt_ftr;
|
||||
struct stat statbuf;
|
||||
|
||||
sprintf(real_blkdev, "/dev/block/vold/%d:%d", major, minor);
|
||||
|
||||
get_crypt_ftr_and_key(&sd_crypt_ftr);
|
||||
|
||||
/* Update the fs_size field to be the size of the volume */
|
||||
int cryptfs_setup_ext_volume(const char* label, const char* real_blkdev,
|
||||
const unsigned char* key, int keysize, char* out_crypto_blkdev) {
|
||||
int fd = open(real_blkdev, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
SLOGE("Cannot open volume %s\n", real_blkdev);
|
||||
SLOGE("Failed to open %s: %s", real_blkdev, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1923,25 +1903,26 @@ int cryptfs_setup_volume(const char *label, int major, int minor,
|
|||
close(fd);
|
||||
|
||||
if (nr_sec == 0) {
|
||||
SLOGE("Cannot get size of volume %s\n", real_blkdev);
|
||||
SLOGE("Failed to get size of %s: %s", real_blkdev, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
sd_crypt_ftr.fs_size = nr_sec;
|
||||
create_crypto_blk_dev(&sd_crypt_ftr, saved_master_key, real_blkdev,
|
||||
crypto_blkdev, label);
|
||||
struct crypt_mnt_ftr ext_crypt_ftr;
|
||||
memset(&ext_crypt_ftr, 0, sizeof(ext_crypt_ftr));
|
||||
ext_crypt_ftr.fs_size = nr_sec;
|
||||
ext_crypt_ftr.keysize = keysize;
|
||||
strcpy((char*) ext_crypt_ftr.crypto_type_name, "aes-cbc-essiv:sha256");
|
||||
|
||||
if (stat(crypto_blkdev, &statbuf) < 0) {
|
||||
SLOGE("Error get stat for crypto_blkdev %s. err=%d(%s)\n",
|
||||
crypto_blkdev, errno, strerror(errno));
|
||||
}
|
||||
*new_major = MAJOR(statbuf.st_rdev);
|
||||
*new_minor = MINOR(statbuf.st_rdev);
|
||||
return create_crypto_blk_dev(&ext_crypt_ftr, key, real_blkdev,
|
||||
out_crypto_blkdev, label);
|
||||
}
|
||||
|
||||
/* Create path to sys entry for this block device */
|
||||
snprintf(crypto_sys_path, max_path, "/devices/virtual/block/%s", strrchr(crypto_blkdev, '/')+1);
|
||||
|
||||
return 0;
|
||||
/*
|
||||
* Called by vold when it's asked to unmount an encrypted external
|
||||
* storage volume.
|
||||
*/
|
||||
int cryptfs_revert_ext_volume(const char* label) {
|
||||
return delete_crypto_blk_dev((char*) label);
|
||||
}
|
||||
|
||||
int cryptfs_crypto_complete(void)
|
||||
|
@ -2836,12 +2817,6 @@ static int cryptfs_enable_inplace(char *crypto_blkdev, char *real_blkdev,
|
|||
|
||||
#define FRAMEWORK_BOOT_WAIT 60
|
||||
|
||||
static inline int should_encrypt(struct volume_info *volume)
|
||||
{
|
||||
return (volume->flags & (VOL_ENCRYPTABLE | VOL_NONREMOVABLE)) ==
|
||||
(VOL_ENCRYPTABLE | VOL_NONREMOVABLE);
|
||||
}
|
||||
|
||||
static int cryptfs_SHA256_fileblock(const char* filename, __le8* buf)
|
||||
{
|
||||
int fd = open(filename, O_RDONLY);
|
||||
|
@ -2944,10 +2919,7 @@ int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
|
|||
char encrypted_state[PROPERTY_VALUE_MAX];
|
||||
char lockid[32] = { 0 };
|
||||
char key_loc[PROPERTY_VALUE_MAX];
|
||||
char fuse_sdcard[PROPERTY_VALUE_MAX];
|
||||
char *sd_mnt_point;
|
||||
int num_vols;
|
||||
struct volume_info *vol_list = 0;
|
||||
off64_t previously_encrypted_upto = 0;
|
||||
|
||||
if (!strcmp(howarg, "wipe")) {
|
||||
|
@ -3022,58 +2994,15 @@ int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
|
|||
snprintf(lockid, sizeof(lockid), "enablecrypto%d", (int) getpid());
|
||||
acquire_wake_lock(PARTIAL_WAKE_LOCK, lockid);
|
||||
|
||||
/* Get the sdcard mount point */
|
||||
sd_mnt_point = getenv("EMULATED_STORAGE_SOURCE");
|
||||
if (!sd_mnt_point) {
|
||||
sd_mnt_point = getenv("EXTERNAL_STORAGE");
|
||||
}
|
||||
if (!sd_mnt_point) {
|
||||
sd_mnt_point = "/mnt/sdcard";
|
||||
}
|
||||
|
||||
/* TODO
|
||||
* Currently do not have test devices with multiple encryptable volumes.
|
||||
* When we acquire some, re-add support.
|
||||
*/
|
||||
num_vols=vold_getNumDirectVolumes();
|
||||
vol_list = malloc(sizeof(struct volume_info) * num_vols);
|
||||
vold_getDirectVolumeList(vol_list);
|
||||
|
||||
for (i=0; i<num_vols; i++) {
|
||||
if (should_encrypt(&vol_list[i])) {
|
||||
SLOGE("Cannot encrypt if there are multiple encryptable volumes"
|
||||
"%s\n", vol_list[i].label);
|
||||
goto error_unencrypted;
|
||||
}
|
||||
}
|
||||
|
||||
/* The init files are setup to stop the class main and late start when
|
||||
* vold sets trigger_shutdown_framework.
|
||||
*/
|
||||
property_set("vold.decrypt", "trigger_shutdown_framework");
|
||||
SLOGD("Just asked init to shut down class main\n");
|
||||
|
||||
if (vold_unmountAllAsecs()) {
|
||||
/* Just report the error. If any are left mounted,
|
||||
* umounting /data below will fail and handle the error.
|
||||
*/
|
||||
SLOGE("Error unmounting internal asecs");
|
||||
}
|
||||
|
||||
property_get("ro.crypto.fuse_sdcard", fuse_sdcard, "");
|
||||
if (!strcmp(fuse_sdcard, "true")) {
|
||||
// STOPSHIP: UNMOUNT ALL STORAGE BEFORE REACHING HERE, SINCE VOLD NOW MANAGES FUSE
|
||||
// "ro.crypto.fuse_sdcard" is now deprecated
|
||||
|
||||
/* This is a device using the fuse layer to emulate the sdcard semantics
|
||||
* on top of the userdata partition. vold does not manage it, it is managed
|
||||
* by the sdcard service. The sdcard service was killed by the property trigger
|
||||
* above, so just unmount it now. We must do this _AFTER_ killing the framework,
|
||||
* unlike the case for vold managed devices above.
|
||||
*/
|
||||
if (wait_and_unmount(sd_mnt_point, false)) {
|
||||
goto error_shutting_down;
|
||||
}
|
||||
/* Ask vold to unmount all devices that it manages */
|
||||
if (vold_unmountAll()) {
|
||||
SLOGE("Failed to unmount all vold managed devices");
|
||||
}
|
||||
|
||||
/* Now unmount the /data partition. */
|
||||
|
@ -3221,8 +3150,6 @@ int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
|
|||
/* Undo the dm-crypt mapping whether we succeed or not */
|
||||
delete_crypto_blk_dev("userdata");
|
||||
|
||||
free(vol_list);
|
||||
|
||||
if (! rc) {
|
||||
/* Success */
|
||||
crypt_ftr.flags &= ~CRYPT_INCONSISTENT_STATE;
|
||||
|
@ -3289,7 +3216,6 @@ int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
|
|||
return rc;
|
||||
|
||||
error_unencrypted:
|
||||
free(vol_list);
|
||||
property_set("vold.encrypt_progress", "error_not_encrypted");
|
||||
if (lockid[0]) {
|
||||
release_wake_lock(lockid);
|
||||
|
@ -3306,7 +3232,6 @@ error_shutting_down:
|
|||
|
||||
/* shouldn't get here */
|
||||
property_set("vold.encrypt_progress", "error_shutting_down");
|
||||
free(vol_list);
|
||||
if (lockid[0]) {
|
||||
release_wake_lock(lockid);
|
||||
}
|
||||
|
|
|
@ -172,6 +172,7 @@ struct crypt_persist_data {
|
|||
struct crypt_persist_entry persist_entry[0];
|
||||
};
|
||||
|
||||
// TODO: remove this deprecated struct
|
||||
struct volume_info {
|
||||
unsigned int size;
|
||||
unsigned int flags;
|
||||
|
@ -232,10 +233,9 @@ extern "C" {
|
|||
int cryptfs_enable(char *flag, int type, char *passwd, int allow_reboot);
|
||||
int cryptfs_changepw(int type, const char *newpw);
|
||||
int cryptfs_enable_default(char *flag, int allow_reboot);
|
||||
int cryptfs_setup_volume(const char *label, int major, int minor,
|
||||
char *crypto_dev_path, unsigned int max_pathlen,
|
||||
int *new_major, int *new_minor);
|
||||
int cryptfs_revert_volume(const char *label);
|
||||
int cryptfs_setup_ext_volume(const char* label, const char* real_blkdev,
|
||||
const unsigned char* key, int keysize, char* out_crypto_blkdev);
|
||||
int cryptfs_revert_ext_volume(const char* label);
|
||||
int cryptfs_getfield(const char *fieldname, char *value, int len);
|
||||
int cryptfs_setfield(const char *fieldname, const char *value);
|
||||
int cryptfs_mount_default_encrypted(void);
|
||||
|
|
Loading…
Reference in a new issue