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
diff --git a/PrivateVolume.cpp b/PrivateVolume.cpp
new file mode 100644
index 0000000..42eea64
--- /dev/null
+++ b/PrivateVolume.cpp
@@ -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