am 52f5425f: Merge "Fix Vold to properly handle full-disk file systems"
* commit '52f5425ff53216e3d7e2410bffd05e0df2b13c9c':
Fix Vold to properly handle full-disk file systems
diff --git a/Android.mk b/Android.mk
index 9ad0edd..1203db4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -14,7 +14,7 @@
Loop.cpp \
Devmapper.cpp \
ResponseCode.cpp \
- Xwarp.cpp \
+ CheckBattery.cpp \
VoldUtil.c \
fstrim.c \
cryptfs.c
@@ -24,11 +24,16 @@
external/openssl/include \
external/stlport/stlport \
bionic \
- external/scrypt/lib/crypto
+ external/scrypt/lib/crypto \
+ frameworks/native/include \
+ system/security/keystore \
+ hardware/libhardware/include/hardware \
+ system/security/softkeymaster/include/keymaster
common_shared_libraries := \
libsysutils \
libstlport \
+ libbinder \
libcutils \
liblog \
libdiskconfig \
@@ -36,12 +41,16 @@
liblogwrap \
libext4_utils \
libcrypto \
- libselinux
+ libselinux \
+ libutils \
+ libhardware \
+ libsoftkeymaster
common_static_libraries := \
libfs_mgr \
libscrypt_static \
- libmincrypt
+ libmincrypt \
+ libbatteryservice
include $(CLEAR_VARS)
diff --git a/CheckBattery.cpp b/CheckBattery.cpp
new file mode 100644
index 0000000..f689bb4
--- /dev/null
+++ b/CheckBattery.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#define LOG_TAG "VoldCheckBattery"
+#include <cutils/log.h>
+
+#include <binder/IServiceManager.h>
+#include <batteryservice/IBatteryPropertiesRegistrar.h>
+
+using namespace android;
+
+namespace
+{
+ // How often to check battery in seconds
+ const int CHECK_PERIOD = 30;
+
+ // How charged should the battery be (percent) to start encrypting
+ const int START_THRESHOLD = 10;
+
+ // How charged should the battery be (percent) to continue encrypting
+ const int CONTINUE_THRESHOLD = 5;
+
+ const String16 serviceName("batteryproperties");
+
+ sp<IBinder> bs;
+ sp<IBatteryPropertiesRegistrar> interface;
+
+ bool singletonInitialized = false;
+ time_t last_checked = {0};
+ int last_result = 100;
+
+ int is_battery_ok(int threshold)
+ {
+ time_t now = time(NULL);
+ if (now == -1 || difftime(now, last_checked) < 5) {
+ goto finish;
+ }
+ last_checked = now;
+
+ if (!singletonInitialized) {
+ bs = defaultServiceManager()->checkService(serviceName);
+ if (bs == NULL) {
+ SLOGE("No batteryproperties service!");
+ goto finish;
+ }
+
+ interface = interface_cast<IBatteryPropertiesRegistrar>(bs);
+ if (interface == NULL) {
+ SLOGE("No IBatteryPropertiesRegistrar interface");
+ goto finish;
+ }
+
+ singletonInitialized = true;
+ }
+
+ {
+ BatteryProperty val;
+ status_t status = interface
+ ->getProperty(android::BATTERY_PROP_CAPACITY, &val);
+ if (status == NO_ERROR) {
+ SLOGD("Capacity is %d", (int)val.valueInt64);
+ last_result = val.valueInt64;
+ } else {
+ SLOGE("Failed to get battery charge");
+ last_result = 100;
+ }
+ }
+
+ finish:
+ return last_result >= threshold;
+ }
+}
+
+extern "C"
+{
+ int is_battery_ok_to_start()
+ {
+ return is_battery_ok(START_THRESHOLD);
+ }
+
+ int is_battery_ok_to_continue()
+ {
+ return is_battery_ok(CONTINUE_THRESHOLD);
+ }
+}
diff --git a/CheckBattery.h b/CheckBattery.h
new file mode 100644
index 0000000..dd11ba9
--- /dev/null
+++ b/CheckBattery.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 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 _CHECKBATTERY_H__
+#define _CHECKBATTERY_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int is_battery_ok_to_start();
+int is_battery_ok_to_continue();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/CommandListener.cpp b/CommandListener.cpp
index c8d6848..26d9b24 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -22,6 +22,7 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <fs_mgr.h>
#include <string.h>
#define LOG_TAG "VoldCmdListener"
@@ -34,7 +35,6 @@
#include "VolumeManager.h"
#include "ResponseCode.h"
#include "Process.h"
-#include "Xwarp.h"
#include "Loop.h"
#include "Devmapper.h"
#include "cryptfs.h"
@@ -49,7 +49,6 @@
registerCmd(new AsecCmd());
registerCmd(new ObbCmd());
registerCmd(new StorageCmd());
- registerCmd(new XwarpCmd());
registerCmd(new CryptfsCmd());
registerCmd(new FstrimCmd());
}
@@ -115,13 +114,12 @@
return 0;
}
-
CommandListener::VolumeCmd::VolumeCmd() :
VoldCommand("volume") {
}
int CommandListener::VolumeCmd::runCommand(SocketClient *cli,
- int argc, char **argv) {
+ int argc, char **argv) {
dumpArgs(argc, argv, -1);
if (argc < 2) {
@@ -133,7 +131,8 @@
int rc = 0;
if (!strcmp(argv[1], "list")) {
- return vm->listVolumes(cli);
+ bool broadcast = argc >= 3 && !strcmp(argv[2], "broadcast");
+ return vm->listVolumes(cli, broadcast);
} else if (!strcmp(argv[1], "debug")) {
if (argc != 3 || (argc == 3 && (strcmp(argv[2], "off") && strcmp(argv[2], "on")))) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume debug <off/on>", false);
@@ -231,6 +230,9 @@
int CommandListener::StorageCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
+ /* Guarantied to be initialized by vold's main() before the CommandListener is active */
+ extern struct fstab *fstab;
+
dumpArgs(argc, argv, -1);
if (argc < 2) {
@@ -238,6 +240,15 @@
return 0;
}
+ if (!strcmp(argv[1], "mountall")) {
+ if (argc != 2) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: mountall", false);
+ return 0;
+ }
+ fs_mgr_mount_all(fstab);
+ cli->sendMsg(ResponseCode::CommandOkay, "Mountall ran successfully", false);
+ return 0;
+ }
if (!strcmp(argv[1], "users")) {
DIR *dir;
struct dirent *de;
@@ -349,6 +360,14 @@
unsigned int numSectors = (atoi(argv[3]) * (1024 * 1024)) / 512;
const bool isExternal = (atoi(argv[7]) == 1);
rc = vm->createAsec(argv[2], numSectors, argv[4], argv[5], atoi(argv[6]), isExternal);
+ } else if (!strcmp(argv[1], "resize")) {
+ dumpArgs(argc, argv, -1);
+ if (argc != 5) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: asec resize <container-id> <size_mb> <key>", false);
+ return 0;
+ }
+ unsigned int numSectors = (atoi(argv[3]) * (1024 * 1024)) / 512;
+ rc = vm->resizeAsec(argv[2], numSectors, argv[4]);
} else if (!strcmp(argv[1], "finalize")) {
dumpArgs(argc, argv, -1);
if (argc != 3) {
@@ -512,53 +531,25 @@
return 0;
}
-CommandListener::XwarpCmd::XwarpCmd() :
- VoldCommand("xwarp") {
-}
-
-int CommandListener::XwarpCmd::runCommand(SocketClient *cli,
- int argc, char **argv) {
- if (argc < 2) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);
- return 0;
- }
-
- if (!strcmp(argv[1], "enable")) {
- if (Xwarp::enable()) {
- cli->sendMsg(ResponseCode::OperationFailed, "Failed to enable xwarp", true);
- return 0;
- }
-
- cli->sendMsg(ResponseCode::CommandOkay, "Xwarp mirroring started", false);
- } else if (!strcmp(argv[1], "disable")) {
- if (Xwarp::disable()) {
- cli->sendMsg(ResponseCode::OperationFailed, "Failed to disable xwarp", true);
- return 0;
- }
-
- cli->sendMsg(ResponseCode::CommandOkay, "Xwarp disabled", false);
- } else if (!strcmp(argv[1], "status")) {
- char msg[255];
- bool r;
- unsigned mirrorPos, maxSize;
-
- if (Xwarp::status(&r, &mirrorPos, &maxSize)) {
- cli->sendMsg(ResponseCode::OperationFailed, "Failed to get xwarp status", true);
- return 0;
- }
- snprintf(msg, sizeof(msg), "%s %u %u", (r ? "ready" : "not-ready"), mirrorPos, maxSize);
- cli->sendMsg(ResponseCode::XwarpStatusResult, msg, false);
- } else {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown storage cmd", false);
- }
-
- return 0;
-}
-
CommandListener::CryptfsCmd::CryptfsCmd() :
VoldCommand("cryptfs") {
}
+static int getType(const char* type)
+{
+ if (!strcmp(type, "default")) {
+ return CRYPT_TYPE_DEFAULT;
+ } else if (!strcmp(type, "password")) {
+ return CRYPT_TYPE_PASSWORD;
+ } else if (!strcmp(type, "pin")) {
+ return CRYPT_TYPE_PIN;
+ } else if (!strcmp(type, "pattern")) {
+ return CRYPT_TYPE_PATTERN;
+ } else {
+ return -1;
+ }
+}
+
int CommandListener::CryptfsCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
if ((cli->getUid() != 0) && (cli->getUid() != AID_SYSTEM)) {
@@ -595,19 +586,54 @@
dumpArgs(argc, argv, -1);
rc = cryptfs_crypto_complete();
} else if (!strcmp(argv[1], "enablecrypto")) {
- if ( (argc != 4) || (strcmp(argv[2], "wipe") && strcmp(argv[2], "inplace")) ) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs enablecrypto <wipe|inplace> <passwd>", false);
+ const char* syntax = "Usage: cryptfs enablecrypto <wipe|inplace> "
+ "default|password|pin|pattern [passwd]";
+ if ( (argc != 4 && argc != 5)
+ || (strcmp(argv[2], "wipe") && strcmp(argv[2], "inplace")) ) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
return 0;
}
- dumpArgs(argc, argv, 3);
- rc = cryptfs_enable(argv[2], argv[3]);
+ dumpArgs(argc, argv, 4);
+
+ int tries;
+ for (tries = 0; tries < 2; ++tries) {
+ int type = getType(argv[3]);
+ if (type == -1) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, syntax,
+ false);
+ return 0;
+ } else if (type == CRYPT_TYPE_DEFAULT) {
+ rc = cryptfs_enable_default(argv[2], /*allow_reboot*/false);
+ } else {
+ rc = cryptfs_enable(argv[2], type, argv[4],
+ /*allow_reboot*/false);
+ }
+
+ if (rc == 0) {
+ break;
+ } else if (tries == 0) {
+ Process::killProcessesWithOpenFiles(DATA_MNT_POINT, 2);
+ }
+ }
} else if (!strcmp(argv[1], "changepw")) {
- if (argc != 3) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs changepw <newpasswd>", false);
+ const char* syntax = "Usage: cryptfs changepw "
+ "default|password|pin|pattern [newpasswd]";
+ const char* password;
+ if (argc == 3) {
+ password = "";
+ } else if (argc == 4) {
+ password = argv[3];
+ } else {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
return 0;
- }
- SLOGD("cryptfs changepw {}");
- rc = cryptfs_changepw(argv[2]);
+ }
+ int type = getType(argv[2]);
+ if (type == -1) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false);
+ return 0;
+ }
+ SLOGD("cryptfs changepw %s {}", argv[2]);
+ rc = cryptfs_changepw(type, password);
} else if (!strcmp(argv[1], "verifypw")) {
if (argc != 3) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: cryptfs verifypw <passwd>", false);
@@ -634,9 +660,49 @@
}
dumpArgs(argc, argv, -1);
rc = cryptfs_setfield(argv[2], argv[3]);
+ } else if (!strcmp(argv[1], "mountdefaultencrypted")) {
+ SLOGD("cryptfs mountdefaultencrypted");
+ dumpArgs(argc, argv, -1);
+ rc = cryptfs_mount_default_encrypted();
+ } else if (!strcmp(argv[1], "getpwtype")) {
+ SLOGD("cryptfs getpwtype");
+ dumpArgs(argc, argv, -1);
+ switch(cryptfs_get_password_type()) {
+ case CRYPT_TYPE_PASSWORD:
+ cli->sendMsg(ResponseCode::PasswordTypeResult, "password", false);
+ return 0;
+ case CRYPT_TYPE_PATTERN:
+ cli->sendMsg(ResponseCode::PasswordTypeResult, "pattern", false);
+ return 0;
+ case CRYPT_TYPE_PIN:
+ cli->sendMsg(ResponseCode::PasswordTypeResult, "pin", false);
+ return 0;
+ case CRYPT_TYPE_DEFAULT:
+ cli->sendMsg(ResponseCode::PasswordTypeResult, "default", false);
+ return 0;
+ default:
+ /** @TODO better error and make sure handled by callers */
+ cli->sendMsg(ResponseCode::OpFailedStorageNotFound, "Error", false);
+ return 0;
+ }
+ } else if (!strcmp(argv[1], "getpw")) {
+ SLOGD("cryptfs getpw");
+ dumpArgs(argc, argv, -1);
+ char* password = cryptfs_get_password();
+ if (password) {
+ cli->sendMsg(ResponseCode::CommandOkay, password, false);
+ return 0;
+ }
+ rc = -1;
+ } else if (!strcmp(argv[1], "clearpw")) {
+ SLOGD("cryptfs clearpw");
+ dumpArgs(argc, argv, -1);
+ cryptfs_clear_password();
+ rc = 0;
} else {
dumpArgs(argc, argv, -1);
cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown cryptfs cmd", false);
+ return 0;
}
// Always report that the command succeeded and return the error code.
@@ -671,7 +737,14 @@
return 0;
}
dumpArgs(argc, argv, -1);
- rc = fstrim_filesystems();
+ rc = fstrim_filesystems(0);
+ } else if (!strcmp(argv[1], "dodtrim")) {
+ if (argc != 2) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: fstrim dodtrim", false);
+ return 0;
+ }
+ dumpArgs(argc, argv, -1);
+ rc = fstrim_filesystems(1); /* Do Deep Discard trim */
} else {
dumpArgs(argc, argv, -1);
cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown fstrim cmd", false);
diff --git a/CommandListener.h b/CommandListener.h
index 8cc5b09..0bd51d2 100644
--- a/CommandListener.h
+++ b/CommandListener.h
@@ -65,13 +65,6 @@
int runCommand(SocketClient *c, int argc, char ** argv);
};
- class XwarpCmd : public VoldCommand {
- public:
- XwarpCmd();
- virtual ~XwarpCmd() {}
- int runCommand(SocketClient *c, int argc, char ** argv);
- };
-
class CryptfsCmd : public VoldCommand {
public:
CryptfsCmd();
diff --git a/Devmapper.cpp b/Devmapper.cpp
index 6f43ac0..700e538 100644
--- a/Devmapper.cpp
+++ b/Devmapper.cpp
@@ -125,8 +125,8 @@
io->flags = flags;
if (name) {
size_t ret = strlcpy(io->name, name, sizeof(io->name));
- if (ret >= sizeof(io->name))
- abort();
+ if (ret >= sizeof(io->name))
+ abort();
}
}
diff --git a/DirectVolume.cpp b/DirectVolume.cpp
index b7c73ae..64d7744 100644
--- a/DirectVolume.cpp
+++ b/DirectVolume.cpp
@@ -75,10 +75,11 @@
mPaths = new PathCollection();
for (int i = 0; i < MAX_PARTITIONS; i++)
mPartMinors[i] = -1;
- mPendingPartMap = 0;
+ mPendingPartCount = 0;
mDiskMajor = -1;
mDiskMinor = -1;
mDiskNumParts = 0;
+ mIsDecrypted = 0;
if (strcmp(rec->mount_point, "auto") != 0) {
ALOGE("Vold managed volumes must have auto mount point; ignoring %s",
@@ -201,12 +202,9 @@
mDiskNumParts = 1;
}
- int partmask = 0;
- int i;
- for (i = 1; i <= mDiskNumParts; i++) {
- partmask |= (1 << i);
- }
- mPendingPartMap = partmask;
+ mPendingPartCount = mDiskNumParts;
+ for (int i = 0; i < MAX_PARTITIONS; i++)
+ mPartMinors[i] = -1;
if (mDiskNumParts == 0) {
#ifdef PARTITION_DEBUG
@@ -215,8 +213,7 @@
setState(Volume::State_Idle);
} else {
#ifdef PARTITION_DEBUG
- SLOGD("Dv::diskIns - waiting for %d partitions (mask 0x%x)",
- mDiskNumParts, mPendingPartMap);
+ SLOGD("Dv::diskIns - waiting for %d pending partitions", mPendingPartCount);
#endif
setState(Volume::State_Pending);
}
@@ -256,11 +253,12 @@
if (part_num >= MAX_PARTITIONS) {
SLOGE("Dv:partAdd: ignoring part_num = %d (max: %d)\n", part_num, MAX_PARTITIONS-1);
} else {
+ if ((mPartMinors[part_num - 1] == -1) && mPendingPartCount)
+ mPendingPartCount--;
mPartMinors[part_num -1] = minor;
}
- mPendingPartMap &= ~(1 << part_num);
- if (!mPendingPartMap) {
+ if (!mPendingPartCount) {
#ifdef PARTITION_DEBUG
SLOGD("Dv:partAdd: Got all partitions - ready to rock!");
#endif
@@ -273,7 +271,7 @@
}
} else {
#ifdef PARTITION_DEBUG
- SLOGD("Dv:partAdd: pending mask now = 0x%x", mPendingPartMap);
+ SLOGD("Dv:partAdd: pending %d disk", mPendingPartCount);
#endif
}
}
@@ -296,12 +294,9 @@
mDiskNumParts = 1;
}
- int partmask = 0;
- int i;
- for (i = 1; i <= mDiskNumParts; i++) {
- partmask |= (1 << i);
- }
- mPendingPartMap = partmask;
+ mPendingPartCount = mDiskNumParts;
+ for (int i = 0; i < MAX_PARTITIONS; i++)
+ mPartMinors[i] = -1;
if (getState() != Volume::State_Formatting) {
if (mDiskNumParts == 0) {
diff --git a/DirectVolume.h b/DirectVolume.h
index e598598..96f46af 100644
--- a/DirectVolume.h
+++ b/DirectVolume.h
@@ -50,7 +50,7 @@
int mOrigDiskMinor;
int mOrigPartMinors[MAX_PARTITIONS];
int mDiskNumParts;
- unsigned int mPendingPartMap;
+ int mPendingPartCount;
int mIsDecrypted;
public:
diff --git a/Ext4.cpp b/Ext4.cpp
index b82b4c5..dc31fd0 100644
--- a/Ext4.cpp
+++ b/Ext4.cpp
@@ -43,7 +43,8 @@
#include "Ext4.h"
#include "VoldUtil.h"
-#define MKEXT4FS_PATH "/system/bin/make_ext4fs";
+#define MKEXT4FS_PATH "/system/bin/make_ext4fs"
+#define RESIZE2FS_PATH "/system/bin/resize2fs"
int Ext4::doMount(const char *fsPath, const char *mountPoint, bool ro, bool remount,
bool executable) {
@@ -67,9 +68,52 @@
return rc;
}
-int Ext4::format(const char *fsPath, const char *mountpoint) {
+int Ext4::resize(const char *fspath, unsigned int numSectors) {
+ const char *args[4];
+ char* size_str;
+ int rc;
+ int status;
+
+ args[0] = RESIZE2FS_PATH;
+ args[1] = "-f";
+ args[2] = fspath;
+ if (asprintf(&size_str, "%ds", numSectors) < 0)
+ {
+ SLOGE("Filesystem (ext4) resize failed to set size");
+ return -1;
+ }
+ args[3] = size_str;
+ rc = android_fork_execvp(ARRAY_SIZE(args), (char **)args, &status, false,
+ true);
+ free(size_str);
+ if (rc != 0) {
+ SLOGE("Filesystem (ext4) resize failed due to logwrap error");
+ errno = EIO;
+ return -1;
+ }
+
+ if (!WIFEXITED(status)) {
+ SLOGE("Filesystem (ext4) resize did not exit properly");
+ errno = EIO;
+ return -1;
+ }
+
+ status = WEXITSTATUS(status);
+
+ if (status == 0) {
+ SLOGI("Filesystem (ext4) resized OK");
+ return 0;
+ } else {
+ SLOGE("Resize (ext4) failed (unknown exit code %d)", status);
+ errno = EIO;
+ return -1;
+ }
+ return 0;
+}
+
+int Ext4::format(const char *fsPath, unsigned int numSectors, const char *mountpoint) {
int fd;
- const char *args[5];
+ const char *args[7];
int rc;
int status;
@@ -77,7 +121,18 @@
args[1] = "-J";
args[2] = "-a";
args[3] = mountpoint;
- args[4] = fsPath;
+ 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);
+ }
rc = android_fork_execvp(ARRAY_SIZE(args), (char **)args, &status, false,
true);
if (rc != 0) {
diff --git a/Ext4.h b/Ext4.h
index c5ab78a..c768f5a 100644
--- a/Ext4.h
+++ b/Ext4.h
@@ -23,7 +23,8 @@
public:
static int doMount(const char *fsPath, const char *mountPoint, bool ro, bool remount,
bool executable);
- static int format(const char *fsPath, const char *mountpoint);
+ static int format(const char *fsPath, unsigned int numSectors, const char *mountpoint);
+ static int resize(const char *fsPath, unsigned int numSectors);
};
#endif
diff --git a/Fat.cpp b/Fat.cpp
index c967a90..cd4ea5f 100644
--- a/Fat.cpp
+++ b/Fat.cpp
@@ -171,7 +171,7 @@
int Fat::format(const char *fsPath, unsigned int numSectors, bool wipe) {
int fd;
- const char *args[10];
+ const char *args[11];
int rc;
int status;
@@ -185,20 +185,21 @@
args[3] = "-O";
args[4] = "android";
args[5] = "-c";
- args[6] = "8";
+ args[6] = "64";
+ args[7] = "-A";
if (numSectors) {
char tmp[32];
snprintf(tmp, sizeof(tmp), "%u", numSectors);
const char *size = tmp;
- args[7] = "-s";
- args[8] = size;
- args[9] = fsPath;
+ args[8] = "-s";
+ args[9] = size;
+ args[10] = fsPath;
rc = android_fork_execvp(ARRAY_SIZE(args), (char **)args, &status,
false, true);
} else {
- args[7] = fsPath;
- rc = android_fork_execvp(8, (char **)args, &status, false,
+ args[8] = fsPath;
+ rc = android_fork_execvp(9, (char **)args, &status, false,
true);
}
diff --git a/Loop.cpp b/Loop.cpp
index 3f0ee1e..8672d93 100644
--- a/Loop.cpp
+++ b/Loop.cpp
@@ -249,6 +249,34 @@
return 0;
}
+int Loop::resizeImageFile(const char *file, unsigned int numSectors) {
+ int fd;
+
+ if ((fd = open(file, O_RDWR)) < 0) {
+ SLOGE("Error opening imagefile (%s)", strerror(errno));
+ return -1;
+ }
+
+ SLOGD("Attempting to increase size of %s to %d sectors.", file, numSectors);
+
+ if (fallocate(fd, 0, 0, numSectors * 512)) {
+ if (errno == ENOSYS) {
+ SLOGW("fallocate not found. Falling back to ftruncate.");
+ if (ftruncate(fd, numSectors * 512) < 0) {
+ SLOGE("Error truncating imagefile (%s)", strerror(errno));
+ close(fd);
+ return -1;
+ }
+ } else {
+ SLOGE("Error allocating space (%s)", strerror(errno));
+ close(fd);
+ return -1;
+ }
+ }
+ close(fd);
+ return 0;
+}
+
int Loop::lookupInfo(const char *loopDevice, struct asec_superblock *sb, unsigned int *nr_sec) {
int fd;
struct asec_superblock buffer;
diff --git a/Loop.h b/Loop.h
index d717cf0..45e6115 100644
--- a/Loop.h
+++ b/Loop.h
@@ -32,6 +32,7 @@
static int destroyByDevice(const char *loopDevice);
static int destroyByFile(const char *loopFile);
static int createImageFile(const char *file, unsigned int numSectors);
+ static int resizeImageFile(const char *file, unsigned int numSectors);
static int dumpState(SocketClient *c);
};
diff --git a/ResponseCode.h b/ResponseCode.h
index 5e4c6fa..0dc0500 100644
--- a/ResponseCode.h
+++ b/ResponseCode.h
@@ -33,7 +33,7 @@
static const int ShareStatusResult = 210;
static const int AsecPathResult = 211;
static const int ShareEnabledResult = 212;
- static const int XwarpStatusResult = 213;
+ static const int PasswordTypeResult = 213;
// 400 series - The command was accepted but the requested action
// did not take place.
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index 1ffa939..06daf40 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -24,6 +24,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mount.h>
+#include <sys/ioctl.h>
#include <dirent.h>
#include <linux/kdev_t.h>
@@ -54,6 +55,110 @@
#define MASS_STORAGE_FILE_PATH "/sys/class/android_usb/android0/f_mass_storage/lun/file"
+#define ROUND_UP_POWER_OF_2(number, po2) (((!!(number & ((1U << po2) - 1))) << po2)\
+ + (number & (~((1U << po2) - 1))))
+
+/* writes superblock at end of file or device given by name */
+static int writeSuperBlock(const char* name, struct asec_superblock *sb, unsigned int numImgSectors) {
+ int sbfd = open(name, O_RDWR);
+ if (sbfd < 0) {
+ SLOGE("Failed to open %s for superblock write (%s)", name, strerror(errno));
+ return -1;
+ }
+
+ if (lseek(sbfd, (numImgSectors * 512), SEEK_SET) < 0) {
+ SLOGE("Failed to lseek for superblock (%s)", strerror(errno));
+ close(sbfd);
+ return -1;
+ }
+
+ if (write(sbfd, sb, sizeof(struct asec_superblock)) != sizeof(struct asec_superblock)) {
+ SLOGE("Failed to write superblock (%s)", strerror(errno));
+ close(sbfd);
+ return -1;
+ }
+ close(sbfd);
+ return 0;
+}
+
+static int adjustSectorNumExt4(unsigned numSectors) {
+ // Ext4 started to reserve 2% or 4096 clusters, whichever is smaller for
+ // preventing costly operations or unexpected ENOSPC error.
+ // Ext4::format() uses default block size without clustering.
+ unsigned clusterSectors = 4096 / 512;
+ unsigned reservedSectors = (numSectors * 2)/100 + (numSectors % 50 > 0);
+ numSectors += reservedSectors > (4096 * clusterSectors) ? (4096 * clusterSectors) : reservedSectors;
+ return ROUND_UP_POWER_OF_2(numSectors, 3);
+}
+
+static int adjustSectorNumFAT(unsigned numSectors) {
+ /*
+ * Add some headroom
+ */
+ unsigned fatSize = (((numSectors * 4) / 512) + 1) * 2;
+ numSectors += fatSize + 2;
+ /*
+ * FAT is aligned to 32 kb with 512b sectors.
+ */
+ return ROUND_UP_POWER_OF_2(numSectors, 6);
+}
+
+static int setupLoopDevice(char* buffer, size_t len, const char* asecFileName, const char* idHash, bool debug) {
+ if (Loop::lookupActive(idHash, buffer, len)) {
+ if (Loop::create(idHash, asecFileName, buffer, len)) {
+ SLOGE("ASEC loop device creation failed for %s (%s)", asecFileName, strerror(errno));
+ return -1;
+ }
+ if (debug) {
+ SLOGD("New loop device created at %s", buffer);
+ }
+ } else {
+ if (debug) {
+ SLOGD("Found active loopback for %s at %s", asecFileName, buffer);
+ }
+ }
+ return 0;
+}
+
+static int setupDevMapperDevice(char* buffer, size_t len, const char* loopDevice, const char* asecFileName, const char* key, const char* idHash , int numImgSectors, bool* createdDMDevice, bool debug) {
+ if (strcmp(key, "none")) {
+ if (Devmapper::lookupActive(idHash, buffer, len)) {
+ if (Devmapper::create(idHash, loopDevice, key, numImgSectors,
+ buffer, len)) {
+ SLOGE("ASEC device mapping failed for %s (%s)", asecFileName, strerror(errno));
+ return -1;
+ }
+ if (debug) {
+ SLOGD("New devmapper instance created at %s", buffer);
+ }
+ } else {
+ if (debug) {
+ SLOGD("Found active devmapper for %s at %s", asecFileName, buffer);
+ }
+ }
+ *createdDMDevice = true;
+ } else {
+ strcpy(buffer, loopDevice);
+ *createdDMDevice = false;
+ }
+ return 0;
+}
+
+static void waitForDevMapper(const char *dmDevice) {
+ /*
+ * Wait for the device mapper node to be created. Sometimes it takes a
+ * while. Wait for up to 1 second. We could also inspect incoming uevents,
+ * but that would take more effort.
+ */
+ int tries = 25;
+ while (tries--) {
+ if (!access(dmDevice, F_OK) || errno != ENOENT) {
+ break;
+ }
+ usleep(40 * 1000);
+ }
+}
+
VolumeManager *VolumeManager::sInstance = NULL;
VolumeManager *VolumeManager::Instance() {
@@ -155,8 +260,9 @@
}
}
-int VolumeManager::listVolumes(SocketClient *cli) {
+int VolumeManager::listVolumes(SocketClient *cli, bool broadcast) {
VolumeCollection::iterator i;
+ char msg[256];
for (i = mVolumes->begin(); i != mVolumes->end(); ++i) {
char *buffer;
@@ -165,6 +271,20 @@
(*i)->getState());
cli->sendMsg(ResponseCode::VolumeListResult, buffer, false);
free(buffer);
+ if (broadcast) {
+ if((*i)->getUuid()) {
+ snprintf(msg, sizeof(msg), "%s %s \"%s\"", (*i)->getLabel(),
+ (*i)->getFuseMountpoint(), (*i)->getUuid());
+ mBroadcaster->sendBroadcast(ResponseCode::VolumeUuidChange,
+ msg, false);
+ }
+ if((*i)->getUserLabel()) {
+ snprintf(msg, sizeof(msg), "%s %s \"%s\"", (*i)->getLabel(),
+ (*i)->getFuseMountpoint(), (*i)->getUserLabel());
+ mBroadcaster->sendBroadcast(ResponseCode::VolumeUserLabelChange,
+ msg, false);
+ }
+ }
}
cli->sendMsg(ResponseCode::CommandOkay, "Volumes listed.", false);
return 0;
@@ -330,15 +450,11 @@
return -1;
}
- /*
- * Add some headroom
- */
- unsigned fatSize = (((numSectors * 4) / 512) + 1) * 2;
- unsigned numImgSectors = numSectors + fatSize + 2;
-
- if (numImgSectors % 63) {
- numImgSectors += (63 - (numImgSectors % 63));
- }
+ unsigned numImgSectors;
+ if (usingExt4)
+ numImgSectors = adjustSectorNumExt4(numSectors);
+ else
+ numImgSectors = adjustSectorNumFAT(numSectors);
// Add +1 for our superblock which is at the end
if (Loop::createImageFile(asecFileName, numImgSectors + 1)) {
@@ -382,10 +498,7 @@
/*
* Drop down the superblock at the end of the file
*/
-
- int sbfd = open(loopDevice, O_RDWR);
- if (sbfd < 0) {
- SLOGE("Failed to open new DM device for superblock write (%s)", strerror(errno));
+ if (writeSuperBlock(loopDevice, &sb, numImgSectors)) {
if (cleanupDm) {
Devmapper::destroy(idHash);
}
@@ -394,29 +507,6 @@
return -1;
}
- if (lseek(sbfd, (numImgSectors * 512), SEEK_SET) < 0) {
- close(sbfd);
- SLOGE("Failed to lseek for superblock (%s)", strerror(errno));
- if (cleanupDm) {
- Devmapper::destroy(idHash);
- }
- Loop::destroyByDevice(loopDevice);
- unlink(asecFileName);
- return -1;
- }
-
- if (write(sbfd, &sb, sizeof(sb)) != sizeof(sb)) {
- close(sbfd);
- SLOGE("Failed to write superblock (%s)", strerror(errno));
- if (cleanupDm) {
- Devmapper::destroy(idHash);
- }
- Loop::destroyByDevice(loopDevice);
- unlink(asecFileName);
- return -1;
- }
- close(sbfd);
-
if (wantFilesystem) {
int formatStatus;
char mountPoint[255];
@@ -433,7 +523,7 @@
}
if (usingExt4) {
- formatStatus = Ext4::format(dmDevice, mountPoint);
+ formatStatus = Ext4::format(dmDevice, numImgSectors, mountPoint);
} else {
formatStatus = Fat::format(dmDevice, numImgSectors, 0);
}
@@ -496,6 +586,146 @@
return 0;
}
+int VolumeManager::resizeAsec(const char *id, unsigned numSectors, const char *key) {
+ char asecFileName[255];
+ char mountPoint[255];
+ bool cleanupDm = false;
+
+ if (!isLegalAsecId(id)) {
+ SLOGE("resizeAsec: Invalid asec id \"%s\"", id);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (findAsec(id, asecFileName, sizeof(asecFileName))) {
+ SLOGE("Couldn't find ASEC %s", id);
+ return -1;
+ }
+
+ int written = snprintf(mountPoint, sizeof(mountPoint), "%s/%s", Volume::ASECDIR, id);
+ if ((written < 0) || (size_t(written) >= sizeof(mountPoint))) {
+ SLOGE("ASEC resize failed for %s: couldn't construct mountpoint", id);
+ return -1;
+ }
+
+ if (isMountpointMounted(mountPoint)) {
+ SLOGE("ASEC %s mounted. Unmount before resizing", id);
+ errno = EBUSY;
+ return -1;
+ }
+
+ struct asec_superblock sb;
+ int fd;
+ unsigned int oldNumSec = 0;
+
+ if ((fd = open(asecFileName, O_RDONLY)) < 0) {
+ SLOGE("Failed to open ASEC file (%s)", strerror(errno));
+ return -1;
+ }
+
+ struct stat info;
+ if (fstat(fd, &info) < 0) {
+ SLOGE("Failed to get file size (%s)", strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ oldNumSec = info.st_size / 512;
+
+ unsigned numImgSectors;
+ if (sb.c_opts & ASEC_SB_C_OPTS_EXT4)
+ numImgSectors = adjustSectorNumExt4(numSectors);
+ else
+ numImgSectors = adjustSectorNumFAT(numSectors);
+ /*
+ * add one block for the superblock
+ */
+ SLOGD("Resizing from %d sectors to %d sectors", oldNumSec, numImgSectors + 1);
+ if (oldNumSec >= numImgSectors + 1) {
+ SLOGE("Only growing is currently supported.");
+ close(fd);
+ return -1;
+ }
+
+ /*
+ * Try to read superblock.
+ */
+ memset(&sb, 0, sizeof(struct asec_superblock));
+ if (lseek(fd, ((oldNumSec - 1) * 512), SEEK_SET) < 0) {
+ SLOGE("lseek failed (%s)", strerror(errno));
+ close(fd);
+ return -1;
+ }
+ if (read(fd, &sb, sizeof(struct asec_superblock)) != sizeof(struct asec_superblock)) {
+ SLOGE("superblock read failed (%s)", strerror(errno));
+ close(fd);
+ return -1;
+ }
+ close(fd);
+
+ if (mDebug) {
+ SLOGD("Container sb magic/ver (%.8x/%.2x)", sb.magic, sb.ver);
+ }
+ if (sb.magic != ASEC_SB_MAGIC || sb.ver != ASEC_SB_VER) {
+ SLOGE("Bad container magic/version (%.8x/%.2x)", sb.magic, sb.ver);
+ errno = EMEDIUMTYPE;
+ return -1;
+ }
+
+ if (!(sb.c_opts & ASEC_SB_C_OPTS_EXT4)) {
+ SLOGE("Only ext4 partitions are supported for resize");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (Loop::resizeImageFile(asecFileName, numImgSectors + 1)) {
+ SLOGE("Resize of ASEC image file failed. Could not resize %s", id);
+ return -1;
+ }
+
+ /*
+ * Drop down a copy of the superblock at the end of the file
+ */
+ if (writeSuperBlock(asecFileName, &sb, numImgSectors))
+ goto fail;
+
+ char idHash[33];
+ if (!asecHash(id, idHash, sizeof(idHash))) {
+ SLOGE("Hash of '%s' failed (%s)", id, strerror(errno));
+ goto fail;
+ }
+
+ char loopDevice[255];
+ if (setupLoopDevice(loopDevice, sizeof(loopDevice), asecFileName, idHash, mDebug))
+ goto fail;
+
+ char dmDevice[255];
+
+ if (setupDevMapperDevice(dmDevice, sizeof(dmDevice), loopDevice, asecFileName, key, idHash, numImgSectors, &cleanupDm, mDebug)) {
+ Loop::destroyByDevice(loopDevice);
+ goto fail;
+ }
+
+ /*
+ * Wait for the device mapper node to be created.
+ */
+ waitForDevMapper(dmDevice);
+
+ if (Ext4::resize(dmDevice, numImgSectors)) {
+ SLOGE("Unable to resize %s (%s)", id, strerror(errno));
+ if (cleanupDm) {
+ Devmapper::destroy(idHash);
+ }
+ Loop::destroyByDevice(loopDevice);
+ goto fail;
+ }
+
+ return 0;
+fail:
+ Loop::resizeImageFile(asecFileName, oldNumSec);
+ return -1;
+}
+
int VolumeManager::finalizeAsec(const char *id) {
char asecFileName[255];
char loopDevice[255];
@@ -1053,19 +1283,8 @@
}
char loopDevice[255];
- if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
- if (Loop::create(idHash, asecFileName, loopDevice, sizeof(loopDevice))) {
- SLOGE("ASEC loop device creation failed (%s)", strerror(errno));
- return -1;
- }
- if (mDebug) {
- SLOGD("New loop device created at %s", loopDevice);
- }
- } else {
- if (mDebug) {
- SLOGD("Found active loopback for %s at %s", asecFileName, loopDevice);
- }
- }
+ if (setupLoopDevice(loopDevice, sizeof(loopDevice), asecFileName, idHash, mDebug))
+ return -1;
char dmDevice[255];
bool cleanupDm = false;
@@ -1088,25 +1307,9 @@
}
nr_sec--; // We don't want the devmapping to extend onto our superblock
- if (strcmp(key, "none")) {
- if (Devmapper::lookupActive(idHash, dmDevice, sizeof(dmDevice))) {
- if (Devmapper::create(idHash, loopDevice, key, nr_sec,
- dmDevice, sizeof(dmDevice))) {
- SLOGE("ASEC device mapping failed (%s)", strerror(errno));
- Loop::destroyByDevice(loopDevice);
- return -1;
- }
- if (mDebug) {
- SLOGD("New devmapper instance created at %s", dmDevice);
- }
- } else {
- if (mDebug) {
- SLOGD("Found active devmapper for %s at %s", asecFileName, dmDevice);
- }
- }
- cleanupDm = true;
- } else {
- strcpy(dmDevice, loopDevice);
+ if (setupDevMapperDevice(dmDevice, sizeof(dmDevice), loopDevice, asecFileName, key, idHash , nr_sec, &cleanupDm, mDebug)) {
+ Loop::destroyByDevice(loopDevice);
+ return -1;
}
if (mkdir(mountPoint, 0000)) {
@@ -1121,17 +1324,9 @@
}
/*
- * The device mapper node needs to be created. Sometimes it takes a
- * while. Wait for up to 1 second. We could also inspect incoming uevents,
- * but that would take more effort.
+ * Wait for the device mapper node to be created.
*/
- int tries = 25;
- while (tries--) {
- if (!access(dmDevice, F_OK) || errno != ENOENT) {
- break;
- }
- usleep(40 * 1000);
- }
+ waitForDevMapper(dmDevice);
int result;
if (sb.c_opts & ASEC_SB_C_OPTS_EXT4) {
@@ -1194,19 +1389,8 @@
}
char loopDevice[255];
- if (Loop::lookupActive(idHash, loopDevice, sizeof(loopDevice))) {
- if (Loop::create(idHash, img, loopDevice, sizeof(loopDevice))) {
- SLOGE("Image loop device creation failed (%s)", strerror(errno));
- return -1;
- }
- if (mDebug) {
- SLOGD("New loop device created at %s", loopDevice);
- }
- } else {
- if (mDebug) {
- SLOGD("Found active loopback for %s at %s", img, loopDevice);
- }
- }
+ if (setupLoopDevice(loopDevice, sizeof(loopDevice), img, idHash, mDebug))
+ return -1;
char dmDevice[255];
bool cleanupDm = false;
@@ -1228,25 +1412,9 @@
close(fd);
- if (strcmp(key, "none")) {
- if (Devmapper::lookupActive(idHash, dmDevice, sizeof(dmDevice))) {
- if (Devmapper::create(idHash, loopDevice, key, nr_sec,
- dmDevice, sizeof(dmDevice))) {
- SLOGE("ASEC device mapping failed (%s)", strerror(errno));
- Loop::destroyByDevice(loopDevice);
- return -1;
- }
- if (mDebug) {
- SLOGD("New devmapper instance created at %s", dmDevice);
- }
- } else {
- if (mDebug) {
- SLOGD("Found active devmapper for %s at %s", img, dmDevice);
- }
- }
- cleanupDm = true;
- } else {
- strcpy(dmDevice, loopDevice);
+ if (setupDevMapperDevice(dmDevice, sizeof(loopDevice), loopDevice, img,key, idHash , nr_sec, &cleanupDm, mDebug)) {
+ Loop::destroyByDevice(loopDevice);
+ return -1;
}
if (mkdir(mountPoint, 0755)) {
@@ -1708,20 +1876,20 @@
}
int VolumeManager::mkdirs(char* path) {
- // Require that path lives under a volume we manage
+ // Require that path lives under a volume we manage and is mounted
const char* emulated_source = getenv("EMULATED_STORAGE_SOURCE");
const char* root = NULL;
if (emulated_source && !strncmp(path, emulated_source, strlen(emulated_source))) {
root = emulated_source;
} else {
Volume* vol = getVolumeForFile(path);
- if (vol) {
+ if (vol && vol->getState() == Volume::State_Mounted) {
root = vol->getMountpoint();
}
}
if (!root) {
- SLOGE("Failed to find volume for %s", path);
+ SLOGE("Failed to find mounted volume for %s", path);
return -EINVAL;
}
diff --git a/VolumeManager.h b/VolumeManager.h
index cc8958d..0d41b23 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -77,7 +77,7 @@
int addVolume(Volume *v);
- int listVolumes(SocketClient *cli);
+ int listVolumes(SocketClient *cli, bool broadcast);
int mountVolume(const char *label);
int unmountVolume(const char *label, bool force, bool revert);
int shareVolume(const char *label, const char *method);
@@ -91,6 +91,7 @@
const char **directory = NULL) const;
int createAsec(const char *id, unsigned numSectors, const char *fstype,
const char *key, const int ownerUid, bool isExternal);
+ int resizeAsec(const char *id, unsigned numSectors, const char *key);
int finalizeAsec(const char *id);
/**
diff --git a/Xwarp.cpp b/Xwarp.cpp
deleted file mode 100644
index 2973ff8..0000000
--- a/Xwarp.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.
- */
-
-#include <stdio.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#define LOG_TAG "Vold"
-
-#include <cutils/log.h>
-
-#include "Xwarp.h"
-const char *Xwarp::XWARP_BACKINGFILE = "/mnt/secure/asec/xwarp.img";
-const char *Xwarp::XWARP_CFG = "/sys/fs/yaffs/mtd3/xwarp-backing-store";
-const char *Xwarp::XWARP_READY = "/sys/fs/yaffs/mtd3/xwarp-ready";
-const char *Xwarp::XWARP_MIRROR_STATUS = "/sys/fs/yaffs/mtd3/xwarp-mirror";
-
-int Xwarp::enable() {
- return doEnableDisable(true);
-}
-
-int Xwarp::disable() {
- return doEnableDisable(false);
-}
-
-int Xwarp::status(bool *ready, unsigned *mirrorPos, unsigned *maxSize) {
- FILE *fp;
-
- *ready = false;
- *mirrorPos = 0;
- *maxSize = 0;
- if (!(fp = fopen(XWARP_READY, "r"))) {
- return -1;
- }
-
- fscanf(fp, "%d", (int *) ready);
- fclose(fp);
-
- if (!(fp = fopen(XWARP_MIRROR_STATUS, "r"))) {
- return -1;
- }
-
- fscanf(fp, "%u %u", mirrorPos, maxSize);
- fclose(fp);
- return 0;
-}
-
-int Xwarp::doEnableDisable(bool enable) {
- const char *tmp;
- int fd = open(XWARP_CFG, O_WRONLY);
-
- if (fd < 0)
- return -1;
-
- tmp = (enable ? XWARP_BACKINGFILE : "");
-
- if (write(fd, tmp, strlen(tmp)+1) < 0) {
- SLOGE("Failed to write xwarp cfg (%s)", strerror(errno));
- close(fd);
- return -1;
- }
-
- close(fd);
- return 0;
-}
diff --git a/Xwarp.h b/Xwarp.h
deleted file mode 100644
index 918a843..0000000
--- a/Xwarp.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _XWARP_H
-#define _XWARP_H
-
-#include <unistd.h>
-
-class Xwarp {
- static const char *XWARP_BACKINGFILE;
- static const char *XWARP_CFG;
- static const char *XWARP_READY;
- static const char *XWARP_MIRROR_STATUS;
-
-public:
- static int enable();
- static int disable();
- static int status(bool *ready, unsigned *mirrorPos, unsigned *maxSize);
-
-private:
- static int doEnableDisable(bool enable);
-};
-
-#endif
diff --git a/cryptfs.c b/cryptfs.c
index 99f6069..93680e5 100644
--- a/cryptfs.c
+++ b/cryptfs.c
@@ -23,6 +23,7 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
+#include <ctype.h>
#include <fcntl.h>
#include <inttypes.h>
#include <unistd.h>
@@ -35,7 +36,6 @@
#include <string.h>
#include <sys/mount.h>
#include <openssl/evp.h>
-#include <openssl/sha.h>
#include <errno.h>
#include <ext4.h>
#include <linux/kdev_t.h>
@@ -50,11 +50,16 @@
#include "VolumeManager.h"
#include "VoldUtil.h"
#include "crypto_scrypt.h"
+#include "ext4_utils.h"
+#include "CheckBattery.h"
+
+#include <hardware/keymaster.h>
+
+#define UNUSED __attribute__((unused))
#define UNUSED __attribute__((unused))
#define DM_CRYPT_BUF_SIZE 4096
-#define DATA_MNT_POINT "/data"
#define HASH_COUNT 2000
#define KEY_LEN_BYTES 16
@@ -62,11 +67,17 @@
#define KEY_IN_FOOTER "footer"
+// "default_password" encoded into hex (d=0x64 etc)
+#define DEFAULT_PASSWORD "64656661756c745f70617373776f7264"
+
#define EXT4_FS 1
-#define FAT_FS 2
+#define F2FS_FS 2
#define TABLE_LOAD_RETRIES 10
+#define RSA_DEFAULT_KEY_SIZE 2048
+#define RSA_DEFAULT_EXPONENT 0x10001
+
char *me = "cryptfs";
static unsigned char saved_master_key[KEY_LEN_BYTES];
@@ -74,15 +85,173 @@
static int master_key_saved = 0;
static struct crypt_persist_data *persist_data = NULL;
+static int keymaster_init(keymaster_device_t **keymaster_dev)
+{
+ int rc;
+
+ const hw_module_t* mod;
+ rc = hw_get_module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &mod);
+ if (rc) {
+ ALOGE("could not find any keystore module");
+ goto out;
+ }
+
+ rc = keymaster_open(mod, keymaster_dev);
+ if (rc) {
+ ALOGE("could not open keymaster device in %s (%s)",
+ KEYSTORE_HARDWARE_MODULE_ID, strerror(-rc));
+ goto out;
+ }
+
+ return 0;
+
+out:
+ *keymaster_dev = NULL;
+ return rc;
+}
+
+/* Should we use keymaster? */
+static int keymaster_check_compatibility()
+{
+ keymaster_device_t *keymaster_dev = 0;
+ int rc = 0;
+
+ if (keymaster_init(&keymaster_dev)) {
+ SLOGE("Failed to init keymaster");
+ rc = -1;
+ goto out;
+ }
+
+ SLOGI("keymaster version is %d", keymaster_dev->common.module->module_api_version);
+
+ if (keymaster_dev->common.module->module_api_version
+ < KEYMASTER_MODULE_API_VERSION_0_3) {
+ rc = 0;
+ goto out;
+ }
+
+ if (keymaster_dev->flags & KEYMASTER_BLOBS_ARE_STANDALONE) {
+ rc = 1;
+ }
+
+out:
+ keymaster_close(keymaster_dev);
+ return rc;
+}
+
+/* Create a new keymaster key and store it in this footer */
+static int keymaster_create_key(struct crypt_mnt_ftr *ftr)
+{
+ uint8_t* key = 0;
+ keymaster_device_t *keymaster_dev = 0;
+
+ if (keymaster_init(&keymaster_dev)) {
+ SLOGE("Failed to init keymaster");
+ return -1;
+ }
+
+ int rc = 0;
+
+ keymaster_rsa_keygen_params_t params;
+ memset(¶ms, '\0', sizeof(params));
+ params.public_exponent = RSA_DEFAULT_EXPONENT;
+ params.modulus_size = RSA_DEFAULT_KEY_SIZE;
+
+ size_t key_size;
+ if (keymaster_dev->generate_keypair(keymaster_dev, TYPE_RSA, ¶ms,
+ &key, &key_size)) {
+ SLOGE("Failed to generate keypair");
+ rc = -1;
+ goto out;
+ }
+
+ if (key_size > KEYMASTER_BLOB_SIZE) {
+ SLOGE("Keymaster key too large for crypto footer");
+ rc = -1;
+ goto out;
+ }
+
+ memcpy(ftr->keymaster_blob, key, key_size);
+ ftr->keymaster_blob_size = key_size;
+
+out:
+ keymaster_close(keymaster_dev);
+ free(key);
+ return rc;
+}
+
+/* This signs the given object using the keymaster key */
+static int keymaster_sign_object(struct crypt_mnt_ftr *ftr,
+ const unsigned char *object,
+ const size_t object_size,
+ unsigned char **signature,
+ size_t *signature_size)
+{
+ int rc = 0;
+ keymaster_device_t *keymaster_dev = 0;
+ if (keymaster_init(&keymaster_dev)) {
+ SLOGE("Failed to init keymaster");
+ return -1;
+ }
+
+ /* We currently set the digest type to DIGEST_NONE because it's the
+ * only supported value for keymaster. A similar issue exists with
+ * PADDING_NONE. Long term both of these should likely change.
+ */
+ keymaster_rsa_sign_params_t params;
+ params.digest_type = DIGEST_NONE;
+ params.padding_type = PADDING_NONE;
+
+ rc = keymaster_dev->sign_data(keymaster_dev,
+ ¶ms,
+ ftr->keymaster_blob,
+ ftr->keymaster_blob_size,
+ object,
+ object_size,
+ signature,
+ signature_size);
+
+ keymaster_close(keymaster_dev);
+ return rc;
+}
+
+/* Store password when userdata is successfully decrypted and mounted.
+ * Cleared by cryptfs_clear_password
+ *
+ * To avoid a double prompt at boot, we need to store the CryptKeeper
+ * password and pass it to KeyGuard, which uses it to unlock KeyStore.
+ * Since the entire framework is torn down and rebuilt after encryption,
+ * we have to use a daemon or similar to store the password. Since vold
+ * is secured against IPC except from system processes, it seems a reasonable
+ * place to store this.
+ *
+ * password should be cleared once it has been used.
+ *
+ * password is aged out after password_max_age_seconds seconds.
+ */
+static char* password = 0;
+static int password_expiry_time = 0;
+static const int password_max_age_seconds = 60;
+
extern struct fstab *fstab;
-static void cryptfs_reboot(int recovery)
+enum RebootType {reboot, recovery, shutdown};
+static void cryptfs_reboot(enum RebootType rt)
{
- if (recovery) {
- property_set(ANDROID_RB_PROPERTY, "reboot,recovery");
- } else {
- property_set(ANDROID_RB_PROPERTY, "reboot");
+ switch(rt) {
+ case reboot:
+ property_set(ANDROID_RB_PROPERTY, "reboot");
+ break;
+
+ case recovery:
+ property_set(ANDROID_RB_PROPERTY, "reboot,recovery");
+ break;
+
+ case shutdown:
+ property_set(ANDROID_RB_PROPERTY, "shutdown");
+ break;
}
+
sleep(20);
/* Shouldn't get here, reboot should happen before sleep times out */
@@ -352,7 +521,7 @@
crypt_ftr->minor_version = 1;
}
- if ((crypt_ftr->major_version == 1) && (crypt_ftr->minor_version)) {
+ if ((crypt_ftr->major_version == 1) && (crypt_ftr->minor_version == 1)) {
SLOGW("upgrading crypto footer to 1.2");
/* But keep the old kdf_type.
* It will get updated later to KDF_SCRYPT after the password has been verified.
@@ -362,6 +531,12 @@
crypt_ftr->minor_version = 2;
}
+ if ((crypt_ftr->major_version == 1) && (crypt_ftr->minor_version == 2)) {
+ SLOGW("upgrading crypto footer to 1.3");
+ crypt_ftr->crypt_type = CRYPT_TYPE_PASSWORD;
+ crypt_ftr->minor_version = 3;
+ }
+
if ((orig_major != crypt_ftr->major_version) || (orig_minor != crypt_ftr->minor_version)) {
if (lseek64(fd, offset, SEEK_SET) == -1) {
SLOGE("Cannot seek to crypt footer\n");
@@ -499,7 +674,8 @@
return -1;
}
- if ((crypt_ftr.major_version != 1) || (crypt_ftr.minor_version != 1)) {
+ if ((crypt_ftr.major_version < 1)
+ || (crypt_ftr.major_version == 1 && crypt_ftr.minor_version < 1)) {
SLOGE("Crypt_ftr version doesn't support persistent data");
return -1;
}
@@ -580,7 +756,8 @@
return -1;
}
- if ((crypt_ftr.major_version != 1) || (crypt_ftr.minor_version != 1)) {
+ if ((crypt_ftr.major_version < 1)
+ || (crypt_ftr.major_version == 1 && crypt_ftr.minor_version < 1)) {
SLOGE("Crypt_ftr version doesn't support persistent data");
return -1;
}
@@ -664,10 +841,53 @@
return -1;
}
+static int hexdigit (char c)
+{
+ if (c >= '0' && c <= '9') return c - '0';
+ c = tolower(c);
+ if (c >= 'a' && c <= 'f') return c - 'a' + 10;
+ return -1;
+}
+
+static unsigned char* convert_hex_ascii_to_key(const char* master_key_ascii,
+ unsigned int* out_keysize)
+{
+ unsigned int i;
+ *out_keysize = 0;
+
+ size_t size = strlen (master_key_ascii);
+ if (size % 2) {
+ SLOGE("Trying to convert ascii string of odd length");
+ return NULL;
+ }
+
+ unsigned char* master_key = (unsigned char*) malloc(size / 2);
+ if (master_key == 0) {
+ SLOGE("Cannot allocate");
+ return NULL;
+ }
+
+ for (i = 0; i < size; i += 2) {
+ int high_nibble = hexdigit (master_key_ascii[i]);
+ int low_nibble = hexdigit (master_key_ascii[i + 1]);
+
+ if(high_nibble < 0 || low_nibble < 0) {
+ SLOGE("Invalid hex string");
+ free (master_key);
+ return NULL;
+ }
+
+ master_key[*out_keysize] = high_nibble * 16 + low_nibble;
+ (*out_keysize)++;
+ }
+
+ return master_key;
+}
+
/* Convert a binary key of specified length into an ascii hex string equivalent,
* without the leading 0x and with null termination
*/
-void convert_key_to_hex_ascii(unsigned char *master_key, unsigned int keysize,
+static void convert_key_to_hex_ascii(unsigned char *master_key, unsigned int keysize,
char *master_key_ascii)
{
unsigned int i, a;
@@ -871,13 +1091,28 @@
}
-static void pbkdf2(char *passwd, unsigned char *salt, unsigned char *ikey, void *params UNUSED) {
+static int pbkdf2(const char *passwd, const unsigned char *salt,
+ unsigned char *ikey, void *params UNUSED)
+{
+ SLOGI("Using pbkdf2 for cryptfs KDF");
+
/* Turn the password into a key and IV that can decrypt the master key */
- PKCS5_PBKDF2_HMAC_SHA1(passwd, strlen(passwd), salt, SALT_LEN,
+ unsigned int keysize;
+ char* master_key = (char*)convert_hex_ascii_to_key(passwd, &keysize);
+ if (!master_key) return -1;
+ PKCS5_PBKDF2_HMAC_SHA1(master_key, keysize, salt, SALT_LEN,
HASH_COUNT, KEY_LEN_BYTES+IV_LEN_BYTES, ikey);
+
+ memset(master_key, 0, keysize);
+ free (master_key);
+ return 0;
}
-static void scrypt(char *passwd, unsigned char *salt, unsigned char *ikey, void *params) {
+static int scrypt(const char *passwd, const unsigned char *salt,
+ unsigned char *ikey, void *params)
+{
+ SLOGI("Using scrypt for cryptfs KDF");
+
struct crypt_mnt_ftr *ftr = (struct crypt_mnt_ftr *) params;
int N = 1 << ftr->N_factor;
@@ -885,22 +1120,103 @@
int p = 1 << ftr->p_factor;
/* Turn the password into a key and IV that can decrypt the master key */
- crypto_scrypt((unsigned char *) passwd, strlen(passwd), salt, SALT_LEN, N, r, p, ikey,
+ unsigned int keysize;
+ unsigned char* master_key = convert_hex_ascii_to_key(passwd, &keysize);
+ if (!master_key) return -1;
+ crypto_scrypt(master_key, keysize, salt, SALT_LEN, N, r, p, ikey,
KEY_LEN_BYTES + IV_LEN_BYTES);
+
+ memset(master_key, 0, keysize);
+ free (master_key);
+ return 0;
}
-static int encrypt_master_key(char *passwd, unsigned char *salt,
- unsigned char *decrypted_master_key,
+static int scrypt_keymaster(const char *passwd, const unsigned char *salt,
+ unsigned char *ikey, void *params)
+{
+ SLOGI("Using scrypt with keymaster for cryptfs KDF");
+
+ int rc;
+ unsigned int key_size;
+ size_t signature_size;
+ unsigned char* signature;
+ struct crypt_mnt_ftr *ftr = (struct crypt_mnt_ftr *) params;
+
+ int N = 1 << ftr->N_factor;
+ int r = 1 << ftr->r_factor;
+ int p = 1 << ftr->p_factor;
+
+ unsigned char* master_key = convert_hex_ascii_to_key(passwd, &key_size);
+ if (!master_key) {
+ SLOGE("Failed to convert passwd from hex");
+ return -1;
+ }
+
+ rc = crypto_scrypt(master_key, key_size, salt, SALT_LEN,
+ N, r, p, ikey, KEY_LEN_BYTES + IV_LEN_BYTES);
+ memset(master_key, 0, key_size);
+ free(master_key);
+
+ if (rc) {
+ SLOGE("scrypt failed");
+ return -1;
+ }
+
+ if (keymaster_sign_object(ftr, ikey, KEY_LEN_BYTES + IV_LEN_BYTES,
+ &signature, &signature_size)) {
+ SLOGE("Signing failed");
+ return -1;
+ }
+
+ rc = crypto_scrypt(signature, signature_size, salt, SALT_LEN,
+ N, r, p, ikey, KEY_LEN_BYTES + IV_LEN_BYTES);
+ free(signature);
+
+ if (rc) {
+ SLOGE("scrypt failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int encrypt_master_key(const char *passwd, const unsigned char *salt,
+ const unsigned char *decrypted_master_key,
unsigned char *encrypted_master_key,
struct crypt_mnt_ftr *crypt_ftr)
{
unsigned char ikey[32+32] = { 0 }; /* Big enough to hold a 256 bit key and 256 bit IV */
EVP_CIPHER_CTX e_ctx;
int encrypted_len, final_len;
+ int rc = 0;
- /* Turn the password into a key and IV that can decrypt the master key */
+ /* Turn the password into an intermediate key and IV that can decrypt the master key */
get_device_scrypt_params(crypt_ftr);
- scrypt(passwd, salt, ikey, crypt_ftr);
+
+ switch (crypt_ftr->kdf_type) {
+ case KDF_SCRYPT_KEYMASTER:
+ if (keymaster_create_key(crypt_ftr)) {
+ SLOGE("keymaster_create_key failed");
+ return -1;
+ }
+
+ if (scrypt_keymaster(passwd, salt, ikey, crypt_ftr)) {
+ SLOGE("scrypt failed");
+ return -1;
+ }
+ break;
+
+ case KDF_SCRYPT:
+ if (scrypt(passwd, salt, ikey, crypt_ftr)) {
+ SLOGE("scrypt failed");
+ return -1;
+ }
+ break;
+
+ default:
+ SLOGE("Invalid kdf_type");
+ return -1;
+ }
/* Initialize the decryption engine */
if (! EVP_EncryptInit(&e_ctx, EVP_aes_128_cbc(), ikey, ikey+KEY_LEN_BYTES)) {
@@ -923,22 +1239,46 @@
if (encrypted_len + final_len != KEY_LEN_BYTES) {
SLOGE("EVP_Encryption length check failed with %d, %d bytes\n", encrypted_len, final_len);
return -1;
- } else {
- return 0;
}
+
+ /* Store the scrypt of the intermediate key, so we can validate if it's a
+ password error or mount error when things go wrong.
+ Note there's no need to check for errors, since if this is incorrect, we
+ simply won't wipe userdata, which is the correct default behavior
+ */
+ int N = 1 << crypt_ftr->N_factor;
+ int r = 1 << crypt_ftr->r_factor;
+ int p = 1 << crypt_ftr->p_factor;
+
+ rc = crypto_scrypt(ikey, KEY_LEN_BYTES,
+ crypt_ftr->salt, sizeof(crypt_ftr->salt), N, r, p,
+ crypt_ftr->scrypted_intermediate_key,
+ sizeof(crypt_ftr->scrypted_intermediate_key));
+
+ if (rc) {
+ SLOGE("encrypt_master_key: crypto_scrypt failed");
+ }
+
+ return 0;
}
static int decrypt_master_key_aux(char *passwd, unsigned char *salt,
- unsigned char *encrypted_master_key,
- unsigned char *decrypted_master_key,
- kdf_func kdf, void *kdf_params)
+ unsigned char *encrypted_master_key,
+ unsigned char *decrypted_master_key,
+ kdf_func kdf, void *kdf_params,
+ unsigned char** intermediate_key,
+ size_t* intermediate_key_size)
{
unsigned char ikey[32+32] = { 0 }; /* Big enough to hold a 256 bit key and 256 bit IV */
EVP_CIPHER_CTX d_ctx;
int decrypted_len, final_len;
- /* Turn the password into a key and IV that can decrypt the master key */
- kdf(passwd, salt, ikey, kdf_params);
+ /* Turn the password into an intermediate key and IV that can decrypt the
+ master key */
+ if (kdf(passwd, salt, ikey, kdf_params)) {
+ SLOGE("kdf failed");
+ return -1;
+ }
/* Initialize the decryption engine */
if (! EVP_DecryptInit(&d_ctx, EVP_aes_128_cbc(), ikey, ikey+KEY_LEN_BYTES)) {
@@ -956,14 +1296,26 @@
if (decrypted_len + final_len != KEY_LEN_BYTES) {
return -1;
- } else {
- return 0;
}
+
+ /* Copy intermediate key if needed by params */
+ if (intermediate_key && intermediate_key_size) {
+ *intermediate_key = (unsigned char*) malloc(KEY_LEN_BYTES);
+ if (intermediate_key) {
+ memcpy(*intermediate_key, ikey, KEY_LEN_BYTES);
+ *intermediate_key_size = KEY_LEN_BYTES;
+ }
+ }
+
+ return 0;
}
static void get_kdf_func(struct crypt_mnt_ftr *ftr, kdf_func *kdf, void** kdf_params)
{
- if (ftr->kdf_type == KDF_SCRYPT) {
+ if (ftr->kdf_type == KDF_SCRYPT_KEYMASTER) {
+ *kdf = scrypt_keymaster;
+ *kdf_params = ftr;
+ } else if (ftr->kdf_type == KDF_SCRYPT) {
*kdf = scrypt;
*kdf_params = ftr;
} else {
@@ -973,15 +1325,18 @@
}
static int decrypt_master_key(char *passwd, unsigned char *decrypted_master_key,
- struct crypt_mnt_ftr *crypt_ftr)
+ struct crypt_mnt_ftr *crypt_ftr,
+ unsigned char** intermediate_key,
+ size_t* intermediate_key_size)
{
kdf_func kdf;
void *kdf_params;
int ret;
get_kdf_func(crypt_ftr, &kdf, &kdf_params);
- ret = decrypt_master_key_aux(passwd, crypt_ftr->salt, crypt_ftr->master_key, decrypted_master_key, kdf,
- kdf_params);
+ ret = decrypt_master_key_aux(passwd, crypt_ftr->salt, crypt_ftr->master_key,
+ decrypted_master_key, kdf, kdf_params,
+ intermediate_key, intermediate_key_size);
if (ret != 0) {
SLOGW("failure decrypting master key");
}
@@ -1069,7 +1424,7 @@
}
}
-int cryptfs_restart(void)
+static int cryptfs_restart_internal(int restart_main)
{
char fs_type[32];
char real_blkdev[MAXPATHLEN];
@@ -1091,32 +1446,34 @@
return -1;
}
- /* Here is where we shut down the framework. The init scripts
- * start all services in one of three classes: core, main or late_start.
- * On boot, we start core and main. Now, we stop main, but not core,
- * as core includes vold and a few other really important things that
- * we need to keep running. Once main has stopped, we should be able
- * to umount the tmpfs /data, then mount the encrypted /data.
- * We then restart the class main, and also the class late_start.
- * At the moment, I've only put a few things in late_start that I know
- * are not needed to bring up the framework, and that also cause problems
- * with unmounting the tmpfs /data, but I hope to add add more services
- * to the late_start class as we optimize this to decrease the delay
- * till the user is asked for the password to the filesystem.
- */
+ if (restart_main) {
+ /* Here is where we shut down the framework. The init scripts
+ * start all services in one of three classes: core, main or late_start.
+ * On boot, we start core and main. Now, we stop main, but not core,
+ * as core includes vold and a few other really important things that
+ * we need to keep running. Once main has stopped, we should be able
+ * to umount the tmpfs /data, then mount the encrypted /data.
+ * We then restart the class main, and also the class late_start.
+ * At the moment, I've only put a few things in late_start that I know
+ * are not needed to bring up the framework, and that also cause problems
+ * with unmounting the tmpfs /data, but I hope to add add more services
+ * to the late_start class as we optimize this to decrease the delay
+ * till the user is asked for the password to the filesystem.
+ */
- /* The init files are setup to stop the class main when vold.decrypt is
- * set to trigger_reset_main.
- */
- property_set("vold.decrypt", "trigger_reset_main");
- SLOGD("Just asked init to shut down class main\n");
+ /* The init files are setup to stop the class main when vold.decrypt is
+ * set to trigger_reset_main.
+ */
+ property_set("vold.decrypt", "trigger_reset_main");
+ SLOGD("Just asked init to shut down class main\n");
- /* Ugh, shutting down the framework is not synchronous, so until it
- * can be fixed, this horrible hack will wait a moment for it all to
- * shut down before proceeding. Without it, some devices cannot
- * restart the graphics services.
- */
- sleep(2);
+ /* Ugh, shutting down the framework is not synchronous, so until it
+ * can be fixed, this horrible hack will wait a moment for it all to
+ * shut down before proceeding. Without it, some devices cannot
+ * restart the graphics services.
+ */
+ sleep(2);
+ }
/* Now that the framework is shutdown, we should be able to umount()
* the tmpfs filesystem, and mount the real one.
@@ -1129,6 +1486,17 @@
}
if (! (rc = wait_and_unmount(DATA_MNT_POINT)) ) {
+ /* If ro.crypto.readonly is set to 1, mount the decrypted
+ * filesystem readonly. This is used when /data is mounted by
+ * recovery mode.
+ */
+ char ro_prop[PROPERTY_VALUE_MAX];
+ property_get("ro.crypto.readonly", ro_prop, "");
+ if (strlen(ro_prop) > 0 && atoi(ro_prop)) {
+ struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab, DATA_MNT_POINT);
+ rec->flags |= MS_RDONLY;
+ }
+
/* If that succeeded, then mount the decrypted filesystem */
fs_mgr_do_mount(fstab, DATA_MNT_POINT, crypto_blkdev, 0);
@@ -1153,6 +1521,12 @@
return rc;
}
+int cryptfs_restart(void)
+{
+ /* Call internal implementation forcing a restart of main service group */
+ return cryptfs_restart_internal(1);
+}
+
static int do_crypto_complete(char *mount_point UNUSED)
{
struct crypt_mnt_ftr crypt_ftr;
@@ -1184,7 +1558,8 @@
}
}
- if (crypt_ftr.flags & CRYPT_ENCRYPTION_IN_PROGRESS) {
+ if (crypt_ftr.flags
+ & (CRYPT_ENCRYPTION_IN_PROGRESS | CRYPT_INCONSISTENT_STATE)) {
SLOGE("Encryption process didn't finish successfully\n");
return -2; /* -2 is the clue to the UI that there is no usable data on the disk,
* and give the user an option to wipe the disk */
@@ -1194,47 +1569,42 @@
return 0;
}
-static int test_mount_encrypted_fs(char *passwd, char *mount_point, char *label)
+static int test_mount_encrypted_fs(struct crypt_mnt_ftr* crypt_ftr,
+ char *passwd, char *mount_point, char *label)
{
- struct crypt_mnt_ftr crypt_ftr;
/* Allocate enough space for a 256 bit key, but we may use less */
unsigned char decrypted_master_key[32];
char crypto_blkdev[MAXPATHLEN];
char real_blkdev[MAXPATHLEN];
char tmp_mount_point[64];
unsigned int orig_failed_decrypt_count;
- char encrypted_state[PROPERTY_VALUE_MAX];
int rc;
kdf_func kdf;
void *kdf_params;
+ int use_keymaster = 0;
+ int upgrade = 0;
+ unsigned char* intermediate_key = 0;
+ size_t intermediate_key_size = 0;
- property_get("ro.crypto.state", encrypted_state, "");
- if ( master_key_saved || strcmp(encrypted_state, "encrypted") ) {
- SLOGE("encrypted fs already validated or not running with encryption, aborting");
- return -1;
+ SLOGD("crypt_ftr->fs_size = %lld\n", crypt_ftr->fs_size);
+ orig_failed_decrypt_count = crypt_ftr->failed_decrypt_count;
+
+ if (! (crypt_ftr->flags & CRYPT_MNT_KEY_UNENCRYPTED) ) {
+ if (decrypt_master_key(passwd, decrypted_master_key, crypt_ftr,
+ &intermediate_key, &intermediate_key_size)) {
+ SLOGE("Failed to decrypt master key\n");
+ rc = -1;
+ goto errout;
+ }
}
fs_mgr_get_crypt_info(fstab, 0, real_blkdev, sizeof(real_blkdev));
- if (get_crypt_ftr_and_key(&crypt_ftr)) {
- SLOGE("Error getting crypt footer and key\n");
- return -1;
- }
-
- SLOGD("crypt_ftr->fs_size = %lld\n", crypt_ftr.fs_size);
- orig_failed_decrypt_count = crypt_ftr.failed_decrypt_count;
-
- if (! (crypt_ftr.flags & CRYPT_MNT_KEY_UNENCRYPTED) ) {
- if (decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr)) {
- SLOGE("Failed to decrypt master key\n");
- return -1;
- }
- }
-
- if (create_crypto_blk_dev(&crypt_ftr, decrypted_master_key,
- real_blkdev, crypto_blkdev, label)) {
+ if (create_crypto_blk_dev(crypt_ftr, decrypted_master_key,
+ real_blkdev, crypto_blkdev, label)) {
SLOGE("Error creating decrypted block device\n");
- return -1;
+ rc = -1;
+ goto errout;
}
/* If init detects an encrypted filesystem, it writes a file for each such
@@ -1249,25 +1619,37 @@
if (fs_mgr_do_mount(fstab, DATA_MNT_POINT, crypto_blkdev, tmp_mount_point)) {
SLOGE("Error temp mounting decrypted block device\n");
delete_crypto_blk_dev(label);
- crypt_ftr.failed_decrypt_count++;
+
+ /* Work out if the problem is the password or the data */
+ unsigned char scrypted_intermediate_key[sizeof(crypt_ftr->
+ scrypted_intermediate_key)];
+ int N = 1 << crypt_ftr->N_factor;
+ int r = 1 << crypt_ftr->r_factor;
+ int p = 1 << crypt_ftr->p_factor;
+
+ rc = crypto_scrypt(intermediate_key, intermediate_key_size,
+ crypt_ftr->salt, sizeof(crypt_ftr->salt),
+ N, r, p, scrypted_intermediate_key,
+ sizeof(scrypted_intermediate_key));
+ if (rc == 0 && memcmp(scrypted_intermediate_key,
+ crypt_ftr->scrypted_intermediate_key,
+ sizeof(scrypted_intermediate_key)) == 0) {
+ SLOGE("Right password, so wipe");
+ rc = -1;
+ } else {
+ SLOGE(rc ? "scrypt failure, so allow retry" :
+ "Wrong password, so allow retry");
+ rc = ++crypt_ftr->failed_decrypt_count;
+ put_crypt_ftr_and_key(crypt_ftr);
+ }
} else {
- /* Success, so just umount and we'll mount it properly when we restart
- * the framework.
+ /* Success!
+ * umount and we'll mount it properly when we restart the framework.
*/
umount(tmp_mount_point);
- crypt_ftr.failed_decrypt_count = 0;
- }
+ crypt_ftr->failed_decrypt_count = 0;
- if (orig_failed_decrypt_count != crypt_ftr.failed_decrypt_count) {
- put_crypt_ftr_and_key(&crypt_ftr);
- }
-
- if (crypt_ftr.failed_decrypt_count) {
- /* We failed to mount the device, so return an error */
- rc = crypt_ftr.failed_decrypt_count;
-
- } else {
- /* Woot! Success! Save the name of the crypto block device
+ /* Save the name of the crypto block device
* so we can mount it when restarting the framework.
*/
property_set("ro.crypto.fs_crypto_blkdev", crypto_blkdev);
@@ -1280,20 +1662,36 @@
master_key_saved = 1;
SLOGD("%s(): Master key saved\n", __FUNCTION__);
rc = 0;
+
/*
* Upgrade if we're not using the latest KDF.
*/
- if (crypt_ftr.kdf_type != KDF_SCRYPT) {
- crypt_ftr.kdf_type = KDF_SCRYPT;
- rc = encrypt_master_key(passwd, crypt_ftr.salt, saved_master_key, crypt_ftr.master_key,
- &crypt_ftr);
+ use_keymaster = keymaster_check_compatibility();
+ if (crypt_ftr->kdf_type == KDF_SCRYPT_KEYMASTER) {
+ // Don't allow downgrade to KDF_SCRYPT
+ } else if (use_keymaster == 1 && crypt_ftr->kdf_type != KDF_SCRYPT_KEYMASTER) {
+ crypt_ftr->kdf_type = KDF_SCRYPT_KEYMASTER;
+ upgrade = 1;
+ } else if (use_keymaster == 0 && crypt_ftr->kdf_type != KDF_SCRYPT) {
+ crypt_ftr->kdf_type = KDF_SCRYPT;
+ upgrade = 1;
+ }
+
+ if (upgrade) {
+ rc = encrypt_master_key(passwd, crypt_ftr->salt, saved_master_key,
+ crypt_ftr->master_key, crypt_ftr);
if (!rc) {
- rc = put_crypt_ftr_and_key(&crypt_ftr);
+ rc = put_crypt_ftr_and_key(crypt_ftr);
}
SLOGD("Key Derivation Function upgrade: rc=%d\n", rc);
}
}
+ errout:
+ if (intermediate_key) {
+ memset(intermediate_key, 0, intermediate_key_size);
+ free(intermediate_key);
+ }
return rc;
}
@@ -1352,11 +1750,43 @@
return do_crypto_complete("/data");
}
+int check_unmounted_and_get_ftr(struct crypt_mnt_ftr* crypt_ftr)
+{
+ char encrypted_state[PROPERTY_VALUE_MAX];
+ property_get("ro.crypto.state", encrypted_state, "");
+ if ( master_key_saved || strcmp(encrypted_state, "encrypted") ) {
+ SLOGE("encrypted fs already validated or not running with encryption,"
+ " aborting");
+ return -1;
+ }
+
+ if (get_crypt_ftr_and_key(crypt_ftr)) {
+ SLOGE("Error getting crypt footer and key");
+ return -1;
+ }
+
+ return 0;
+}
+
int cryptfs_check_passwd(char *passwd)
{
- int rc = -1;
+ struct crypt_mnt_ftr crypt_ftr;
+ int rc;
- rc = test_mount_encrypted_fs(passwd, DATA_MNT_POINT, "userdata");
+ rc = check_unmounted_and_get_ftr(&crypt_ftr);
+ if (rc)
+ return rc;
+
+ rc = test_mount_encrypted_fs(&crypt_ftr, passwd,
+ DATA_MNT_POINT, "userdata");
+
+ if (rc == 0 && crypt_ftr.crypt_type != CRYPT_TYPE_DEFAULT) {
+ cryptfs_clear_password();
+ password = strdup(passwd);
+ struct timespec now;
+ clock_gettime(CLOCK_BOOTTIME, &now);
+ password_expiry_time = now.tv_sec + password_max_age_seconds;
+ }
return rc;
}
@@ -1394,7 +1824,7 @@
/* If the device has no password, then just say the password is valid */
rc = 0;
} else {
- decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr);
+ decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr, 0, 0);
if (!memcmp(decrypted_master_key, saved_master_key, crypt_ftr.keysize)) {
/* They match, the password is correct */
rc = 0;
@@ -1413,7 +1843,7 @@
* Presumably, at a minimum, the caller will update the
* filesystem size and crypto_type_name after calling this function.
*/
-static void cryptfs_init_crypt_mnt_ftr(struct crypt_mnt_ftr *ftr)
+static int cryptfs_init_crypt_mnt_ftr(struct crypt_mnt_ftr *ftr)
{
off64_t off;
@@ -1424,7 +1854,20 @@
ftr->ftr_size = sizeof(struct crypt_mnt_ftr);
ftr->keysize = KEY_LEN_BYTES;
- ftr->kdf_type = KDF_SCRYPT;
+ switch (keymaster_check_compatibility()) {
+ case 1:
+ ftr->kdf_type = KDF_SCRYPT_KEYMASTER;
+ break;
+
+ case 0:
+ ftr->kdf_type = KDF_SCRYPT;
+ break;
+
+ default:
+ SLOGE("keymaster_check_compatibility failed");
+ return -1;
+ }
+
get_device_scrypt_params(ftr);
ftr->persist_data_size = CRYPT_PERSIST_DATA_SIZE;
@@ -1433,6 +1876,8 @@
ftr->persist_data_offset[1] = off + CRYPT_FOOTER_TO_PERSIST_OFFSET +
ftr->persist_data_size;
}
+
+ return 0;
}
static int cryptfs_enable_wipe(char *crypto_blkdev, off64_t size, int type)
@@ -1455,22 +1900,16 @@
num_args = 6;
SLOGI("Making empty filesystem with command %s %s %s %s %s %s\n",
args[0], args[1], args[2], args[3], args[4], args[5]);
- } else if (type== FAT_FS) {
- args[0] = "/system/bin/newfs_msdos";
- args[1] = "-F";
- args[2] = "32";
- args[3] = "-O";
- args[4] = "android";
- args[5] = "-c";
- args[6] = "8";
- args[7] = "-s";
+ } else if (type == F2FS_FS) {
+ args[0] = "/system/bin/mkfs.f2fs";
+ args[1] = "-t";
+ args[2] = "-d1";
+ args[3] = crypto_blkdev;
snprintf(size_str, sizeof(size_str), "%" PRId64, size);
- args[8] = size_str;
- args[9] = crypto_blkdev;
- num_args = 10;
- SLOGI("Making empty filesystem with command %s %s %s %s %s %s %s %s %s %s\n",
- args[0], args[1], args[2], args[3], args[4], args[5],
- args[6], args[7], args[8], args[9]);
+ args[4] = size_str;
+ num_args = 5;
+ SLOGI("Making empty filesystem with command %s %s %s %s %s\n",
+ args[0], args[1], args[2], args[3], args[4]);
} else {
SLOGE("cryptfs_enable_wipe(): unknown filesystem type %d\n", type);
return -1;
@@ -1498,9 +1937,269 @@
}
#define CRYPT_INPLACE_BUFSIZE 4096
-#define CRYPT_SECTORS_PER_BUFSIZE (CRYPT_INPLACE_BUFSIZE / 512)
-static int cryptfs_enable_inplace(char *crypto_blkdev, char *real_blkdev, off64_t size,
- off64_t *size_already_done, off64_t tot_size)
+#define CRYPT_SECTORS_PER_BUFSIZE (CRYPT_INPLACE_BUFSIZE / CRYPT_SECTOR_SIZE)
+#define CRYPT_SECTOR_SIZE 512
+
+/* aligned 32K writes tends to make flash happy.
+ * SD card association recommends it.
+ */
+#define BLOCKS_AT_A_TIME 8
+
+struct encryptGroupsData
+{
+ int realfd;
+ int cryptofd;
+ off64_t numblocks;
+ off64_t one_pct, cur_pct, new_pct;
+ off64_t blocks_already_done, tot_numblocks;
+ off64_t used_blocks_already_done, tot_used_blocks;
+ char* real_blkdev, * crypto_blkdev;
+ int count;
+ off64_t offset;
+ char* buffer;
+ off64_t last_written_sector;
+ int completed;
+ time_t time_started;
+ int remaining_time;
+};
+
+static void update_progress(struct encryptGroupsData* data, int is_used)
+{
+ data->blocks_already_done++;
+
+ if (is_used) {
+ data->used_blocks_already_done++;
+ }
+
+ if (data->tot_used_blocks) {
+ data->new_pct = data->used_blocks_already_done / data->one_pct;
+ } else {
+ data->new_pct = data->blocks_already_done / data->one_pct;
+ }
+
+ if (data->new_pct > data->cur_pct) {
+ char buf[8];
+ data->cur_pct = data->new_pct;
+ snprintf(buf, sizeof(buf), "%" PRId64, data->cur_pct);
+ property_set("vold.encrypt_progress", buf);
+ SLOGI("Encrypted %" PRId64 " percent of drive", data->cur_pct);
+ }
+
+ if (data->cur_pct >= 5) {
+ double elapsed_time = difftime(time(NULL), data->time_started);
+ off64_t remaining_blocks = data->tot_used_blocks
+ - data->used_blocks_already_done;
+ int remaining_time = (int)(elapsed_time * remaining_blocks
+ / data->used_blocks_already_done);
+ if (data->remaining_time == -1
+ || remaining_time < data->remaining_time) {
+ char buf[8];
+ snprintf(buf, sizeof(buf), "%d", remaining_time);
+ property_set("vold.encrypt_time_remaining", buf);
+
+ SLOGI("Encrypted %" PRId64 " percent of drive, %d seconds to go",
+ data->cur_pct, remaining_time);
+ data->remaining_time = remaining_time;
+ }
+ }
+}
+
+static int flush_outstanding_data(struct encryptGroupsData* data)
+{
+ if (data->count == 0) {
+ return 0;
+ }
+
+ SLOGV("Copying %d blocks at offset %" PRIx64, data->count, data->offset);
+
+ if (pread64(data->realfd, data->buffer,
+ info.block_size * data->count, data->offset)
+ <= 0) {
+ SLOGE("Error reading real_blkdev %s for inplace encrypt",
+ data->real_blkdev);
+ return -1;
+ }
+
+ if (pwrite64(data->cryptofd, data->buffer,
+ info.block_size * data->count, data->offset)
+ <= 0) {
+ SLOGE("Error writing crypto_blkdev %s for inplace encrypt",
+ data->crypto_blkdev);
+ return -1;
+ } else {
+ SLOGI("Encrypted %d blocks at sector %" PRId64,
+ data->count, data->offset / info.block_size * CRYPT_SECTOR_SIZE);
+ }
+
+ data->count = 0;
+ data->last_written_sector = (data->offset + data->count)
+ / info.block_size * CRYPT_SECTOR_SIZE - 1;
+ return 0;
+}
+
+static int encrypt_groups(struct encryptGroupsData* data)
+{
+ unsigned int i;
+ u8 *block_bitmap = 0;
+ unsigned int block;
+ off64_t ret;
+ int rc = -1;
+
+ data->buffer = malloc(info.block_size * BLOCKS_AT_A_TIME);
+ if (!data->buffer) {
+ SLOGE("Failed to allocate crypto buffer");
+ goto errout;
+ }
+
+ block_bitmap = malloc(info.block_size);
+ if (!block_bitmap) {
+ SLOGE("failed to allocate block bitmap");
+ goto errout;
+ }
+
+ for (i = 0; i < aux_info.groups; ++i) {
+ SLOGI("Encrypting group %d", i);
+
+ u32 first_block = aux_info.first_data_block + i * info.blocks_per_group;
+ u32 block_count = min(info.blocks_per_group,
+ aux_info.len_blocks - first_block);
+
+ off64_t offset = (u64)info.block_size
+ * aux_info.bg_desc[i].bg_block_bitmap;
+
+ ret = pread64(data->realfd, block_bitmap, info.block_size, offset);
+ if (ret != (int)info.block_size) {
+ SLOGE("failed to read all of block group bitmap %d", i);
+ goto errout;
+ }
+
+ offset = (u64)info.block_size * first_block;
+
+ data->count = 0;
+
+ for (block = 0; block < block_count; block++) {
+ int used = bitmap_get_bit(block_bitmap, block);
+ update_progress(data, used);
+ if (used) {
+ if (data->count == 0) {
+ data->offset = offset;
+ }
+ data->count++;
+ } else {
+ if (flush_outstanding_data(data)) {
+ goto errout;
+ }
+ }
+
+ offset += info.block_size;
+
+ /* Write data if we are aligned or buffer size reached */
+ if (offset % (info.block_size * BLOCKS_AT_A_TIME) == 0
+ || data->count == BLOCKS_AT_A_TIME) {
+ if (flush_outstanding_data(data)) {
+ goto errout;
+ }
+ }
+
+ if (!is_battery_ok_to_continue()) {
+ SLOGE("Stopping encryption due to low battery");
+ rc = 0;
+ goto errout;
+ }
+
+ }
+ if (flush_outstanding_data(data)) {
+ goto errout;
+ }
+ }
+
+ data->completed = 1;
+ rc = 0;
+
+errout:
+ free(data->buffer);
+ free(block_bitmap);
+ return rc;
+}
+
+static int cryptfs_enable_inplace_ext4(char *crypto_blkdev,
+ char *real_blkdev,
+ off64_t size,
+ off64_t *size_already_done,
+ off64_t tot_size,
+ off64_t previously_encrypted_upto)
+{
+ u32 i;
+ struct encryptGroupsData data;
+ int rc = -1;
+
+ if (previously_encrypted_upto > *size_already_done) {
+ SLOGD("Not fast encrypting since resuming part way through");
+ return -1;
+ }
+
+ memset(&data, 0, sizeof(data));
+ data.real_blkdev = real_blkdev;
+ data.crypto_blkdev = crypto_blkdev;
+
+ if ( (data.realfd = open(real_blkdev, O_RDWR)) < 0) {
+ SLOGE("Error opening real_blkdev %s for inplace encrypt\n",
+ real_blkdev);
+ goto errout;
+ }
+
+ if ( (data.cryptofd = open(crypto_blkdev, O_WRONLY)) < 0) {
+ SLOGE("Error opening crypto_blkdev %s for inplace encrypt\n",
+ crypto_blkdev);
+ goto errout;
+ }
+
+ if (setjmp(setjmp_env)) {
+ SLOGE("Reading extent caused an exception");
+ goto errout;
+ }
+
+ if (read_ext(data.realfd, 0) != 0) {
+ SLOGE("Failed to read extent");
+ goto errout;
+ }
+
+ data.numblocks = size / CRYPT_SECTORS_PER_BUFSIZE;
+ data.tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE;
+ data.blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE;
+
+ SLOGI("Encrypting filesystem in place...");
+
+ data.tot_used_blocks = data.numblocks;
+ for (i = 0; i < aux_info.groups; ++i) {
+ data.tot_used_blocks -= aux_info.bg_desc[i].bg_free_blocks_count;
+ }
+
+ data.one_pct = data.tot_used_blocks / 100;
+ data.cur_pct = 0;
+ data.time_started = time(NULL);
+ data.remaining_time = -1;
+
+ rc = encrypt_groups(&data);
+ if (rc) {
+ SLOGE("Error encrypting groups");
+ goto errout;
+ }
+
+ *size_already_done += data.completed ? size : data.last_written_sector;
+ rc = 0;
+
+errout:
+ close(data.realfd);
+ close(data.cryptofd);
+
+ return rc;
+}
+
+static int cryptfs_enable_inplace_full(char *crypto_blkdev, char *real_blkdev,
+ off64_t size, off64_t *size_already_done,
+ off64_t tot_size,
+ off64_t previously_encrypted_upto)
{
int realfd, cryptofd;
char *buf[CRYPT_INPLACE_BUFSIZE];
@@ -1532,10 +2231,37 @@
SLOGE("Encrypting filesystem in place...");
+ i = previously_encrypted_upto + 1 - *size_already_done;
+
+ if (lseek64(realfd, i * CRYPT_SECTOR_SIZE, SEEK_SET) < 0) {
+ SLOGE("Cannot seek to previously encrypted point on %s", real_blkdev);
+ goto errout;
+ }
+
+ if (lseek64(cryptofd, i * CRYPT_SECTOR_SIZE, SEEK_SET) < 0) {
+ SLOGE("Cannot seek to previously encrypted point on %s", crypto_blkdev);
+ goto errout;
+ }
+
+ for (;i < size && i % CRYPT_SECTORS_PER_BUFSIZE != 0; ++i) {
+ if (unix_read(realfd, buf, CRYPT_SECTOR_SIZE) <= 0) {
+ SLOGE("Error reading initial sectors from real_blkdev %s for "
+ "inplace encrypt\n", crypto_blkdev);
+ goto errout;
+ }
+ if (unix_write(cryptofd, buf, CRYPT_SECTOR_SIZE) <= 0) {
+ SLOGE("Error writing initial sectors to crypto_blkdev %s for "
+ "inplace encrypt\n", crypto_blkdev);
+ goto errout;
+ } else {
+ SLOGI("Encrypted 1 block at %" PRId64, i);
+ }
+ }
+
one_pct = tot_numblocks / 100;
cur_pct = 0;
/* process the majority of the filesystem in blocks */
- for (i=0; i<numblocks; i++) {
+ for (i/=CRYPT_SECTORS_PER_BUFSIZE; i<numblocks; i++) {
new_pct = (i + blocks_already_done) / one_pct;
if (new_pct > cur_pct) {
char buf[8];
@@ -1545,24 +2271,37 @@
property_set("vold.encrypt_progress", buf);
}
if (unix_read(realfd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) {
- SLOGE("Error reading real_blkdev %s for inplace encrypt\n", crypto_blkdev);
+ SLOGE("Error reading real_blkdev %s for inplace encrypt", crypto_blkdev);
goto errout;
}
if (unix_write(cryptofd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) {
- SLOGE("Error writing crypto_blkdev %s for inplace encrypt\n", crypto_blkdev);
+ SLOGE("Error writing crypto_blkdev %s for inplace encrypt", crypto_blkdev);
+ goto errout;
+ } else {
+ SLOGD("Encrypted %d block at %" PRId64,
+ CRYPT_SECTORS_PER_BUFSIZE,
+ i * CRYPT_SECTORS_PER_BUFSIZE);
+ }
+
+ if (!is_battery_ok_to_continue()) {
+ SLOGE("Stopping encryption due to low battery");
+ *size_already_done += (i + 1) * CRYPT_SECTORS_PER_BUFSIZE - 1;
+ rc = 0;
goto errout;
}
}
/* Do any remaining sectors */
for (i=0; i<remainder; i++) {
- if (unix_read(realfd, buf, 512) <= 0) {
- SLOGE("Error reading rival sectors from real_blkdev %s for inplace encrypt\n", crypto_blkdev);
+ if (unix_read(realfd, buf, CRYPT_SECTOR_SIZE) <= 0) {
+ SLOGE("Error reading final sectors from real_blkdev %s for inplace encrypt", crypto_blkdev);
goto errout;
}
- if (unix_write(cryptofd, buf, 512) <= 0) {
- SLOGE("Error writing final sectors to crypto_blkdev %s for inplace encrypt\n", crypto_blkdev);
+ if (unix_write(cryptofd, buf, CRYPT_SECTOR_SIZE) <= 0) {
+ SLOGE("Error writing final sectors to crypto_blkdev %s for inplace encrypt", crypto_blkdev);
goto errout;
+ } else {
+ SLOGI("Encrypted 1 block at next location");
}
}
@@ -1576,6 +2315,31 @@
return rc;
}
+static int cryptfs_enable_inplace(char *crypto_blkdev, char *real_blkdev,
+ off64_t size, off64_t *size_already_done,
+ off64_t tot_size,
+ off64_t previously_encrypted_upto)
+{
+ if (previously_encrypted_upto) {
+ SLOGD("Continuing encryption from %" PRId64, previously_encrypted_upto);
+ }
+
+ if (*size_already_done + size < previously_encrypted_upto) {
+ *size_already_done += size;
+ return 0;
+ }
+
+ if (cryptfs_enable_inplace_ext4(crypto_blkdev, real_blkdev,
+ size, size_already_done,
+ tot_size, previously_encrypted_upto) == 0) {
+ return 0;
+ }
+
+ return cryptfs_enable_inplace_full(crypto_blkdev, real_blkdev,
+ size, size_already_done, tot_size,
+ previously_encrypted_upto);
+}
+
#define CRYPTO_ENABLE_WIPE 1
#define CRYPTO_ENABLE_INPLACE 2
@@ -1583,37 +2347,112 @@
static inline int should_encrypt(struct volume_info *volume)
{
- return (volume->flags & (VOL_ENCRYPTABLE | VOL_NONREMOVABLE)) ==
+ return (volume->flags & (VOL_ENCRYPTABLE | VOL_NONREMOVABLE)) ==
(VOL_ENCRYPTABLE | VOL_NONREMOVABLE);
}
-int cryptfs_enable(char *howarg, char *passwd)
+static int cryptfs_SHA256_fileblock(const char* filename, __le8* buf)
+{
+ int fd = open(filename, O_RDONLY);
+ if (fd == -1) {
+ SLOGE("Error opening file %s", filename);
+ return -1;
+ }
+
+ char block[CRYPT_INPLACE_BUFSIZE];
+ memset(block, 0, sizeof(block));
+ if (unix_read(fd, block, sizeof(block)) < 0) {
+ SLOGE("Error reading file %s", filename);
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+
+ SHA256_CTX c;
+ SHA256_Init(&c);
+ SHA256_Update(&c, block, sizeof(block));
+ SHA256_Final(buf, &c);
+
+ return 0;
+}
+
+static int get_fs_type(struct fstab_rec *rec)
+{
+ if (!strcmp(rec->fs_type, "ext4")) {
+ return EXT4_FS;
+ } else if (!strcmp(rec->fs_type, "f2fs")) {
+ return F2FS_FS;
+ } else {
+ return -1;
+ }
+}
+
+static int cryptfs_enable_all_volumes(struct crypt_mnt_ftr *crypt_ftr, int how,
+ char *crypto_blkdev, char *real_blkdev,
+ int previously_encrypted_upto)
+{
+ off64_t cur_encryption_done=0, tot_encryption_size=0;
+ int i, rc = -1;
+
+ if (!is_battery_ok_to_start()) {
+ SLOGW("Not starting encryption due to low battery");
+ return 0;
+ }
+
+ /* The size of the userdata partition, and add in the vold volumes below */
+ tot_encryption_size = crypt_ftr->fs_size;
+
+ if (how == CRYPTO_ENABLE_WIPE) {
+ struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab, DATA_MNT_POINT);
+ int fs_type = get_fs_type(rec);
+ if (fs_type < 0) {
+ SLOGE("cryptfs_enable: unsupported fs type %s\n", rec->fs_type);
+ return -1;
+ }
+ rc = cryptfs_enable_wipe(crypto_blkdev, crypt_ftr->fs_size, fs_type);
+ } else if (how == CRYPTO_ENABLE_INPLACE) {
+ rc = cryptfs_enable_inplace(crypto_blkdev, real_blkdev,
+ crypt_ftr->fs_size, &cur_encryption_done,
+ tot_encryption_size,
+ previously_encrypted_upto);
+
+ if (!rc) {
+ crypt_ftr->encrypted_upto = cur_encryption_done;
+ }
+
+ if (!rc && crypt_ftr->encrypted_upto == crypt_ftr->fs_size) {
+ /* The inplace routine never actually sets the progress to 100% due
+ * to the round down nature of integer division, so set it here */
+ property_set("vold.encrypt_progress", "100");
+ }
+ } else {
+ /* Shouldn't happen */
+ SLOGE("cryptfs_enable: internal error, unknown option\n");
+ rc = -1;
+ }
+
+ return rc;
+}
+
+int cryptfs_enable_internal(char *howarg, int crypt_type, char *passwd,
+ int allow_reboot)
{
int how = 0;
- char crypto_blkdev[MAXPATHLEN], real_blkdev[MAXPATHLEN], sd_crypto_blkdev[MAXPATHLEN];
+ char crypto_blkdev[MAXPATHLEN], real_blkdev[MAXPATHLEN];
unsigned long nr_sec;
unsigned char decrypted_master_key[KEY_LEN_BYTES];
int rc=-1, fd, i, ret;
- struct crypt_mnt_ftr crypt_ftr, sd_crypt_ftr;;
+ struct crypt_mnt_ftr crypt_ftr;
struct crypt_persist_data *pdata;
- char tmpfs_options[PROPERTY_VALUE_MAX];
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;
- char sd_blk_dev[256] = { 0 };
int num_vols;
struct volume_info *vol_list = 0;
- off64_t cur_encryption_done=0, tot_encryption_size=0;
-
- property_get("ro.crypto.state", encrypted_state, "");
- if (strcmp(encrypted_state, "unencrypted")) {
- SLOGE("Device is already running encrypted, aborting");
- goto error_unencrypted;
- }
-
- fs_mgr_get_crypt_info(fstab, key_loc, 0, sizeof(key_loc));
+ off64_t previously_encrypted_upto = 0;
if (!strcmp(howarg, "wipe")) {
how = CRYPTO_ENABLE_WIPE;
@@ -1624,6 +2463,31 @@
goto error_unencrypted;
}
+ /* See if an encryption was underway and interrupted */
+ if (how == CRYPTO_ENABLE_INPLACE
+ && get_crypt_ftr_and_key(&crypt_ftr) == 0
+ && (crypt_ftr.flags & CRYPT_ENCRYPTION_IN_PROGRESS)) {
+ previously_encrypted_upto = crypt_ftr.encrypted_upto;
+ crypt_ftr.encrypted_upto = 0;
+ crypt_ftr.flags &= ~CRYPT_ENCRYPTION_IN_PROGRESS;
+
+ /* At this point, we are in an inconsistent state. Until we successfully
+ complete encryption, a reboot will leave us broken. So mark the
+ encryption failed in case that happens.
+ On successfully completing encryption, remove this flag */
+ crypt_ftr.flags |= CRYPT_INCONSISTENT_STATE;
+
+ put_crypt_ftr_and_key(&crypt_ftr);
+ }
+
+ property_get("ro.crypto.state", encrypted_state, "");
+ if (!strcmp(encrypted_state, "encrypted") && !previously_encrypted_upto) {
+ SLOGE("Device is already running encrypted, aborting");
+ goto error_unencrypted;
+ }
+
+ // TODO refactor fs_mgr_get_crypt_info to get both in one call
+ fs_mgr_get_crypt_info(fstab, key_loc, 0, sizeof(key_loc));
fs_mgr_get_crypt_info(fstab, 0, real_blkdev, sizeof(real_blkdev));
/* Get the size of the real block device */
@@ -1639,7 +2503,7 @@
unsigned int fs_size_sec, max_fs_size_sec;
fs_size_sec = get_fs_size(real_blkdev);
- max_fs_size_sec = nr_sec - (CRYPT_FOOTER_OFFSET / 512);
+ max_fs_size_sec = nr_sec - (CRYPT_FOOTER_OFFSET / CRYPT_SECTOR_SIZE);
if (fs_size_sec > max_fs_size_sec) {
SLOGE("Orig filesystem overlaps crypto footer region. Cannot encrypt in place.");
@@ -1663,26 +2527,19 @@
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])) {
- fd = open(vol_list[i].blk_dev, O_RDONLY);
- if ( (vol_list[i].size = get_blkdev_size(fd)) == 0) {
- SLOGE("Cannot get size of block device %s\n", vol_list[i].blk_dev);
- goto error_unencrypted;
- }
- close(fd);
-
- ret=vold_disableVol(vol_list[i].label);
- if ((ret < 0) && (ret != UNMOUNT_NOT_MOUNTED_ERR)) {
- /* -2 is returned when the device exists but is not currently mounted.
- * ignore the error and continue. */
- SLOGE("Failed to unmount volume %s\n", vol_list[i].label);
- goto error_unencrypted;
- }
+ SLOGE("Cannot encrypt if there are multiple encryptable volumes"
+ "%s\n", vol_list[i].label);
+ goto error_unencrypted;
}
}
@@ -1714,7 +2571,11 @@
/* Now unmount the /data partition. */
if (wait_and_unmount(DATA_MNT_POINT)) {
- goto error_shutting_down;
+ if (allow_reboot) {
+ goto error_shutting_down;
+ } else {
+ goto error_unencrypted;
+ }
}
/* Do extra work for a better UX when doing the long inplace encryption */
@@ -1755,113 +2616,107 @@
/* Start the actual work of making an encrypted filesystem */
/* Initialize a crypt_mnt_ftr for the partition */
- cryptfs_init_crypt_mnt_ftr(&crypt_ftr);
+ if (previously_encrypted_upto == 0) {
+ if (cryptfs_init_crypt_mnt_ftr(&crypt_ftr)) {
+ goto error_shutting_down;
+ }
- if (!strcmp(key_loc, KEY_IN_FOOTER)) {
- crypt_ftr.fs_size = nr_sec - (CRYPT_FOOTER_OFFSET / 512);
- } else {
- crypt_ftr.fs_size = nr_sec;
- }
- crypt_ftr.flags |= CRYPT_ENCRYPTION_IN_PROGRESS;
- strcpy((char *)crypt_ftr.crypto_type_name, "aes-cbc-essiv:sha256");
+ if (!strcmp(key_loc, KEY_IN_FOOTER)) {
+ crypt_ftr.fs_size = nr_sec
+ - (CRYPT_FOOTER_OFFSET / CRYPT_SECTOR_SIZE);
+ } else {
+ crypt_ftr.fs_size = nr_sec;
+ }
+ /* At this point, we are in an inconsistent state. Until we successfully
+ complete encryption, a reboot will leave us broken. So mark the
+ encryption failed in case that happens.
+ On successfully completing encryption, remove this flag */
+ crypt_ftr.flags |= CRYPT_INCONSISTENT_STATE;
+ crypt_ftr.crypt_type = crypt_type;
+ strcpy((char *)crypt_ftr.crypto_type_name, "aes-cbc-essiv:sha256");
- /* Make an encrypted master key */
- if (create_encrypted_random_key(passwd, crypt_ftr.master_key, crypt_ftr.salt, &crypt_ftr)) {
- SLOGE("Cannot create encrypted master key\n");
- goto error_unencrypted;
+ /* Make an encrypted master key */
+ if (create_encrypted_random_key(passwd, crypt_ftr.master_key, crypt_ftr.salt, &crypt_ftr)) {
+ SLOGE("Cannot create encrypted master key\n");
+ goto error_shutting_down;
+ }
+
+ /* Write the key to the end of the partition */
+ put_crypt_ftr_and_key(&crypt_ftr);
+
+ /* If any persistent data has been remembered, save it.
+ * If none, create a valid empty table and save that.
+ */
+ if (!persist_data) {
+ pdata = malloc(CRYPT_PERSIST_DATA_SIZE);
+ if (pdata) {
+ init_empty_persist_data(pdata, CRYPT_PERSIST_DATA_SIZE);
+ persist_data = pdata;
+ }
+ }
+ if (persist_data) {
+ save_persistent_data();
+ }
}
- /* Write the key to the end of the partition */
- put_crypt_ftr_and_key(&crypt_ftr);
-
- /* If any persistent data has been remembered, save it.
- * If none, create a valid empty table and save that.
- */
- if (!persist_data) {
- pdata = malloc(CRYPT_PERSIST_DATA_SIZE);
- if (pdata) {
- init_empty_persist_data(pdata, CRYPT_PERSIST_DATA_SIZE);
- persist_data = pdata;
- }
- }
- if (persist_data) {
- save_persistent_data();
- }
-
- decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr);
+ decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr, 0, 0);
create_crypto_blk_dev(&crypt_ftr, decrypted_master_key, real_blkdev, crypto_blkdev,
"userdata");
- /* The size of the userdata partition, and add in the vold volumes below */
- tot_encryption_size = crypt_ftr.fs_size;
+ /* If we are continuing, check checksums match */
+ rc = 0;
+ if (previously_encrypted_upto) {
+ __le8 hash_first_block[SHA256_DIGEST_LENGTH];
+ rc = cryptfs_SHA256_fileblock(crypto_blkdev, hash_first_block);
- /* setup crypto mapping for all encryptable volumes handled by vold */
- for (i=0; i<num_vols; i++) {
- if (should_encrypt(&vol_list[i])) {
- vol_list[i].crypt_ftr = crypt_ftr; /* gotta love struct assign */
- vol_list[i].crypt_ftr.fs_size = vol_list[i].size;
- create_crypto_blk_dev(&vol_list[i].crypt_ftr, decrypted_master_key,
- vol_list[i].blk_dev, vol_list[i].crypto_blkdev,
- vol_list[i].label);
- tot_encryption_size += vol_list[i].size;
+ if (!rc && memcmp(hash_first_block, crypt_ftr.hash_first_block,
+ sizeof(hash_first_block)) != 0) {
+ SLOGE("Checksums do not match - trigger wipe");
+ rc = -1;
}
}
- if (how == CRYPTO_ENABLE_WIPE) {
- rc = cryptfs_enable_wipe(crypto_blkdev, crypt_ftr.fs_size, EXT4_FS);
- /* Encrypt all encryptable volumes handled by vold */
- if (!rc) {
- for (i=0; i<num_vols; i++) {
- if (should_encrypt(&vol_list[i])) {
- rc = cryptfs_enable_wipe(vol_list[i].crypto_blkdev,
- vol_list[i].crypt_ftr.fs_size, FAT_FS);
- }
- }
+ if (!rc) {
+ rc = cryptfs_enable_all_volumes(&crypt_ftr, how,
+ crypto_blkdev, real_blkdev,
+ previously_encrypted_upto);
+ }
+
+ /* Calculate checksum if we are not finished */
+ if (!rc && crypt_ftr.encrypted_upto != crypt_ftr.fs_size) {
+ rc = cryptfs_SHA256_fileblock(crypto_blkdev,
+ crypt_ftr.hash_first_block);
+ if (rc) {
+ SLOGE("Error calculating checksum for continuing encryption");
+ rc = -1;
}
- } else if (how == CRYPTO_ENABLE_INPLACE) {
- rc = cryptfs_enable_inplace(crypto_blkdev, real_blkdev, crypt_ftr.fs_size,
- &cur_encryption_done, tot_encryption_size);
- /* Encrypt all encryptable volumes handled by vold */
- if (!rc) {
- for (i=0; i<num_vols; i++) {
- if (should_encrypt(&vol_list[i])) {
- rc = cryptfs_enable_inplace(vol_list[i].crypto_blkdev,
- vol_list[i].blk_dev,
- vol_list[i].crypt_ftr.fs_size,
- &cur_encryption_done, tot_encryption_size);
- }
- }
- }
- if (!rc) {
- /* The inplace routine never actually sets the progress to 100%
- * due to the round down nature of integer division, so set it here */
- property_set("vold.encrypt_progress", "100");
- }
- } else {
- /* Shouldn't happen */
- SLOGE("cryptfs_enable: internal error, unknown option\n");
- goto error_unencrypted;
}
/* Undo the dm-crypt mapping whether we succeed or not */
delete_crypto_blk_dev("userdata");
- for (i=0; i<num_vols; i++) {
- if (should_encrypt(&vol_list[i])) {
- delete_crypto_blk_dev(vol_list[i].label);
- }
- }
free(vol_list);
if (! rc) {
/* Success */
+ crypt_ftr.flags &= ~CRYPT_INCONSISTENT_STATE;
- /* Clear the encryption in progres flag in the footer */
- crypt_ftr.flags &= ~CRYPT_ENCRYPTION_IN_PROGRESS;
+ if (crypt_ftr.encrypted_upto != crypt_ftr.fs_size) {
+ SLOGD("Encrypted up to sector %lld - will continue after reboot",
+ crypt_ftr.encrypted_upto);
+ crypt_ftr.flags |= CRYPT_ENCRYPTION_IN_PROGRESS;
+ }
+
put_crypt_ftr_and_key(&crypt_ftr);
sleep(2); /* Give the UI a chance to show 100% progress */
- cryptfs_reboot(0);
+ /* Partially encrypted - ensure writes are flushed to ssd */
+
+ if (crypt_ftr.encrypted_upto == crypt_ftr.fs_size) {
+ cryptfs_reboot(reboot);
+ } else {
+ cryptfs_reboot(shutdown);
+ }
} else {
char value[PROPERTY_VALUE_MAX];
@@ -1877,7 +2732,7 @@
} else {
SLOGE("could not open /cache/recovery/command\n");
}
- cryptfs_reboot(1);
+ cryptfs_reboot(recovery);
} else {
/* set property to trigger dialog */
property_set("vold.encrypt_progress", "error_partially_encrypted");
@@ -1908,7 +2763,7 @@
* vold to restart the system.
*/
SLOGE("Error enabling encryption after framework is shutdown, no data changed, restarting system");
- cryptfs_reboot(0);
+ cryptfs_reboot(reboot);
/* shouldn't get here */
property_set("vold.encrypt_progress", "error_shutting_down");
@@ -1919,24 +2774,47 @@
return -1;
}
-int cryptfs_changepw(char *newpw)
+int cryptfs_enable(char *howarg, int type, char *passwd, int allow_reboot)
+{
+ return cryptfs_enable_internal(howarg, type, passwd, allow_reboot);
+}
+
+int cryptfs_enable_default(char *howarg, int allow_reboot)
+{
+ return cryptfs_enable_internal(howarg, CRYPT_TYPE_DEFAULT,
+ DEFAULT_PASSWORD, allow_reboot);
+}
+
+int cryptfs_changepw(int crypt_type, const char *newpw)
{
struct crypt_mnt_ftr crypt_ftr;
unsigned char decrypted_master_key[KEY_LEN_BYTES];
/* This is only allowed after we've successfully decrypted the master key */
- if (! master_key_saved) {
+ if (!master_key_saved) {
SLOGE("Key not saved, aborting");
return -1;
}
+ if (crypt_type < 0 || crypt_type > CRYPT_TYPE_MAX_TYPE) {
+ SLOGE("Invalid crypt_type %d", crypt_type);
+ return -1;
+ }
+
/* get key */
if (get_crypt_ftr_and_key(&crypt_ftr)) {
- SLOGE("Error getting crypt footer and key");
- return -1;
+ SLOGE("Error getting crypt footer and key");
+ return -1;
}
- encrypt_master_key(newpw, crypt_ftr.salt, saved_master_key, crypt_ftr.master_key, &crypt_ftr);
+ crypt_ftr.crypt_type = crypt_type;
+
+ encrypt_master_key(crypt_type == CRYPT_TYPE_DEFAULT ? DEFAULT_PASSWORD
+ : newpw,
+ crypt_ftr.salt,
+ saved_master_key,
+ crypt_ftr.master_key,
+ &crypt_ftr);
/* save the key */
put_crypt_ftr_and_key(&crypt_ftr);
@@ -2083,3 +2961,80 @@
out:
return rc;
}
+
+/* Checks userdata. Attempt to mount the volume if default-
+ * encrypted.
+ * On success trigger next init phase and return 0.
+ * Currently do not handle failure - see TODO below.
+ */
+int cryptfs_mount_default_encrypted(void)
+{
+ char decrypt_state[PROPERTY_VALUE_MAX];
+ property_get("vold.decrypt", decrypt_state, "0");
+ if (!strcmp(decrypt_state, "0")) {
+ SLOGE("Not encrypted - should not call here");
+ } else {
+ int crypt_type = cryptfs_get_password_type();
+ if (crypt_type < 0 || crypt_type > CRYPT_TYPE_MAX_TYPE) {
+ SLOGE("Bad crypt type - error");
+ } else if (crypt_type != CRYPT_TYPE_DEFAULT) {
+ SLOGD("Password is not default - "
+ "starting min framework to prompt");
+ property_set("vold.decrypt", "trigger_restart_min_framework");
+ return 0;
+ } else if (cryptfs_check_passwd(DEFAULT_PASSWORD) == 0) {
+ SLOGD("Password is default - restarting filesystem");
+ cryptfs_restart_internal(0);
+ return 0;
+ } else {
+ SLOGE("Encrypted, default crypt type but can't decrypt");
+ }
+ }
+
+ /** Corrupt. Allow us to boot into framework, which will detect bad
+ crypto when it calls do_crypto_complete, then do a factory reset
+ */
+ property_set("vold.decrypt", "trigger_restart_min_framework");
+ return 0;
+}
+
+/* Returns type of the password, default, pattern, pin or password.
+ */
+int cryptfs_get_password_type(void)
+{
+ struct crypt_mnt_ftr crypt_ftr;
+
+ if (get_crypt_ftr_and_key(&crypt_ftr)) {
+ SLOGE("Error getting crypt footer and key\n");
+ return -1;
+ }
+
+ if (crypt_ftr.flags & CRYPT_INCONSISTENT_STATE) {
+ return -1;
+ }
+
+ return crypt_ftr.crypt_type;
+}
+
+char* cryptfs_get_password()
+{
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ if (now.tv_sec < password_expiry_time) {
+ return password;
+ } else {
+ cryptfs_clear_password();
+ return 0;
+ }
+}
+
+void cryptfs_clear_password()
+{
+ if (password) {
+ size_t len = strlen(password);
+ memset(password, 0, len);
+ free(password);
+ password = 0;
+ password_expiry_time = 0;
+ }
+}
diff --git a/cryptfs.h b/cryptfs.h
index bd44dfa..0b4cda6 100644
--- a/cryptfs.h
+++ b/cryptfs.h
@@ -27,10 +27,11 @@
*/
#include <cutils/properties.h>
+#include <openssl/sha.h>
/* The current cryptfs version */
#define CURRENT_MAJOR_VERSION 1
-#define CURRENT_MINOR_VERSION 2
+#define CURRENT_MINOR_VERSION 3
#define CRYPT_FOOTER_OFFSET 0x4000
#define CRYPT_FOOTER_TO_PERSIST_OFFSET 0x1000
@@ -40,11 +41,25 @@
#define MAX_KEY_LEN 48
#define SALT_LEN 16
+#define SCRYPT_LEN 32
/* definitions of flags in the structure below */
#define CRYPT_MNT_KEY_UNENCRYPTED 0x1 /* The key for the partition is not encrypted. */
-#define CRYPT_ENCRYPTION_IN_PROGRESS 0x2 /* Set when starting encryption,
- * clear when done before rebooting */
+#define CRYPT_ENCRYPTION_IN_PROGRESS 0x2 /* Encryption partially completed,
+ encrypted_upto valid*/
+#define CRYPT_INCONSISTENT_STATE 0x4 /* Set when starting encryption, clear when
+ exit cleanly, either through success or
+ correctly marked partial encryption */
+
+/* Allowed values for type in the structure below */
+#define CRYPT_TYPE_PASSWORD 0 /* master_key is encrypted with a password
+ * Must be zero to be compatible with pre-L
+ * devices where type is always password.*/
+#define CRYPT_TYPE_DEFAULT 1 /* master_key is encrypted with default
+ * password */
+#define CRYPT_TYPE_PATTERN 2 /* master_key is encrypted with a pattern */
+#define CRYPT_TYPE_PIN 3 /* master_key is encrypted with a pin */
+#define CRYPT_TYPE_MAX_TYPE 3 /* type cannot be larger than this value */
#define CRYPT_MNT_MAGIC 0xD0B5B1C4
#define PERSIST_DATA_MAGIC 0xE950CD44
@@ -55,24 +70,29 @@
/* Key Derivation Function algorithms */
#define KDF_PBKDF2 1
#define KDF_SCRYPT 2
+#define KDF_SCRYPT_KEYMASTER 3
+
+/* Maximum allowed keymaster blob size. */
+#define KEYMASTER_BLOB_SIZE 2048
/* __le32 and __le16 defined in system/extras/ext4_utils/ext4_utils.h */
#define __le8 unsigned char
struct crypt_mnt_ftr {
- __le32 magic; /* See above */
+ __le32 magic; /* See above */
__le16 major_version;
__le16 minor_version;
- __le32 ftr_size; /* in bytes, not including key following */
- __le32 flags; /* See above */
- __le32 keysize; /* in bytes */
- __le32 spare1; /* ignored */
+ __le32 ftr_size; /* in bytes, not including key following */
+ __le32 flags; /* See above */
+ __le32 keysize; /* in bytes */
+ __le32 crypt_type; /* how master_key is encrypted. Must be a
+ * CRYPT_TYPE_XXX value */
__le64 fs_size; /* Size of the encrypted fs, in 512 byte sectors */
__le32 failed_decrypt_count; /* count of # of failed attempts to decrypt and
- mount, set to 0 on successful mount */
+ mount, set to 0 on successful mount */
unsigned char crypto_type_name[MAX_CRYPTO_TYPE_NAME_LEN]; /* The type of encryption
- needed to decrypt this
- partition, null terminated */
+ needed to decrypt this
+ partition, null terminated */
__le32 spare2; /* ignored */
unsigned char master_key[MAX_KEY_LEN]; /* The encrypted key for decrypting the filesystem */
unsigned char salt[SALT_LEN]; /* The salt used for this encryption */
@@ -89,6 +109,35 @@
__le8 N_factor; /* (1 << N) */
__le8 r_factor; /* (1 << r) */
__le8 p_factor; /* (1 << p) */
+ __le64 encrypted_upto; /* If we are in state CRYPT_ENCRYPTION_IN_PROGRESS and
+ we have to stop (e.g. power low) this is the last
+ encrypted 512 byte sector.*/
+ __le8 hash_first_block[SHA256_DIGEST_LENGTH]; /* When CRYPT_ENCRYPTION_IN_PROGRESS
+ set, hash of first block, used
+ to validate before continuing*/
+
+ /* key_master key, used to sign the derived key which is then used to generate
+ * the intermediate key
+ * This key should be used for no other purposes! We use this key to sign unpadded
+ * data, which is acceptable but only if the key is not reused elsewhere. */
+ __le8 keymaster_blob[KEYMASTER_BLOB_SIZE];
+ __le32 keymaster_blob_size;
+
+ /* Store scrypt of salted intermediate key. When decryption fails, we can
+ check if this matches, and if it does, we know that the problem is with the
+ drive, and there is no point in asking the user for more passwords.
+
+ Note that if any part of this structure is corrupt, this will not match and
+ we will continue to believe the user entered the wrong password. In that
+ case the only solution is for the user to enter a password enough times to
+ force a wipe.
+
+ Note also that there is no need to worry about migration. If this data is
+ wrong, we simply won't recognise a right password, and will continue to
+ prompt. On the first password change, this value will be populated and
+ then we will be OK.
+ */
+ unsigned char scrypted_intermediate_key[SCRYPT_LEN];
};
/* Persistant data that should be available before decryption.
@@ -131,24 +180,32 @@
#define VOL_PRIMARY 0x4
#define VOL_PROVIDES_ASEC 0x8
+#define DATA_MNT_POINT "/data"
+
#ifdef __cplusplus
extern "C" {
#endif
- typedef void (*kdf_func)(char *passwd, unsigned char *salt, unsigned char *ikey, void *params);
+ typedef int (*kdf_func)(const char *passwd, const unsigned char *salt,
+ unsigned char *ikey, void *params);
int cryptfs_crypto_complete(void);
int cryptfs_check_passwd(char *pw);
int cryptfs_verify_passwd(char *newpw);
int cryptfs_restart(void);
- int cryptfs_enable(char *flag, char *passwd);
- int cryptfs_changepw(char *newpw);
+ 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_getfield(char *fieldname, char *value, int len);
int cryptfs_setfield(char *fieldname, char *value);
+ int cryptfs_mount_default_encrypted(void);
+ int cryptfs_get_password_type(void);
+ char* cryptfs_get_password(void);
+ void cryptfs_clear_password(void);
#ifdef __cplusplus
}
#endif
diff --git a/fstrim.c b/fstrim.c
index 9a6637d..895d44f 100644
--- a/fstrim.c
+++ b/fstrim.c
@@ -39,6 +39,9 @@
#define UNUSED __attribute__((unused))
+/* From a would-be kernel header */
+#define FIDTRIM _IOWR('f', 128, struct fstrim_range) /* Deep discard trim */
+
static unsigned long long get_boot_time_ms(void)
{
struct timespec t;
@@ -52,7 +55,7 @@
return time_ms;
}
-static void *do_fstrim_filesystems(void *ignored UNUSED)
+static void *do_fstrim_filesystems(void *thread_arg)
{
int i;
int fd;
@@ -60,6 +63,7 @@
struct fstrim_range range = { 0 };
struct stat sb;
extern struct fstab *fstab;
+ int deep_trim = !!thread_arg;
SLOGI("Starting fstrim work...\n");
@@ -100,9 +104,11 @@
memset(&range, 0, sizeof(range));
range.len = ULLONG_MAX;
- SLOGI("Invoking FITRIM ioctl on %s", fstab->recs[i].mount_point);
- if (ioctl(fd, FITRIM, &range)) {
- SLOGE("FITRIM ioctl failed on %s", fstab->recs[i].mount_point);
+ SLOGI("Invoking %s ioctl on %s", deep_trim ? "FIDTRIM" : "FITRIM", fstab->recs[i].mount_point);
+
+ ret = ioctl(fd, deep_trim ? FIDTRIM : FITRIM, &range);
+ if (ret) {
+ SLOGE("%s ioctl failed on %s (error %d/%s)", deep_trim ? "FIDTRIM" : "FITRIM", fstab->recs[i].mount_point, errno, strerror(errno));
ret = -1;
} else {
SLOGI("Trimmed %llu bytes on %s\n", range.len, fstab->recs[i].mount_point);
@@ -121,7 +127,7 @@
return (void *)(uintptr_t)ret;
}
-int fstrim_filesystems(void)
+int fstrim_filesystems(int deep_trim)
{
pthread_t t;
int ret;
@@ -142,7 +148,7 @@
* the kernel will "do the right thing" and split the work between
* the two ioctls invoked in separate threads.
*/
- ret = pthread_create(&t, NULL, do_fstrim_filesystems, NULL);
+ ret = pthread_create(&t, NULL, do_fstrim_filesystems, (void *)(intptr_t)deep_trim);
if (ret) {
SLOGE("Cannot create thread to do fstrim");
return ret;
diff --git a/fstrim.h b/fstrim.h
index e46e804..185d998 100644
--- a/fstrim.h
+++ b/fstrim.h
@@ -17,7 +17,7 @@
#ifdef __cplusplus
extern "C" {
#endif
- int fstrim_filesystems(void);
+ int fstrim_filesystems(int deep_trim);
#ifdef __cplusplus
}
#endif
diff --git a/vdc.c b/vdc.c
index a810872..dcd2bc3 100644
--- a/vdc.c
+++ b/vdc.c
@@ -38,20 +38,38 @@
int main(int argc, char **argv) {
int sock;
+ int wait_for_socket;
+ char *progname;
- if (argc < 2)
- usage(argv[0]);
+ progname = argv[0];
- if ((sock = socket_local_client("vold",
- ANDROID_SOCKET_NAMESPACE_RESERVED,
- SOCK_STREAM)) < 0) {
- fprintf(stderr, "Error connecting (%s)\n", strerror(errno));
- exit(4);
+ wait_for_socket = argc > 1 && strcmp(argv[1], "--wait") == 0;
+ if(wait_for_socket) {
+ argv++;
+ argc--;
}
- if (!strcmp(argv[1], "monitor"))
+ if(argc < 2) {
+ usage(progname);
+ exit(5);
+ }
+
+ while ((sock = socket_local_client("vold",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_STREAM)) < 0) {
+ if(!wait_for_socket) {
+ fprintf(stderr, "Error connecting (%s)\n", strerror(errno));
+ exit(4);
+ } else {
+ sleep(1);
+ }
+ }
+
+ if (!strcmp(argv[1], "monitor")) {
exit(do_monitor(sock, 0));
- exit(do_cmd(sock, argc, argv));
+ } else {
+ exit(do_cmd(sock, argc, argv));
+ }
}
static int do_cmd(int sock, int argc, char **argv) {
@@ -118,7 +136,7 @@
return ECONNRESET;
return errno;
}
-
+
int offset = 0;
int i = 0;
@@ -146,7 +164,7 @@
}
static void usage(char *progname) {
- fprintf(stderr, "Usage: %s <monitor>|<cmd> [arg1] [arg2...]\n", progname);
- exit(1);
-}
+ fprintf(stderr,
+ "Usage: %s [--wait] <monitor>|<cmd> [arg1] [arg2...]\n", progname);
+ }