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/Ext4.cpp b/Ext4.cpp
index f5a964a..42d6cd3 100644
--- a/Ext4.cpp
+++ b/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::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;