vold: Add encrypted ASEC support via devmapper
- Supports up to 4096 containers
- Keys are now implemented - specifying a key of 'none' means no encryption.
Otherwise, the key must be a string of 32 characters
Signed-off-by: San Mehat <san@google.com>
diff --git a/Android.mk b/Android.mk
index 1243ad7..d90b399 100644
--- a/Android.mk
+++ b/Android.mk
@@ -23,7 +23,8 @@
ProcessKiller.c \
geom_mbr_enc.c \
Fat.cpp \
- Loop.cpp
+ Loop.cpp \
+ Devmapper.cpp
LOCAL_MODULE:= vold
diff --git a/Devmapper.cpp b/Devmapper.cpp
new file mode 100644
index 0000000..2b7d9a0
--- /dev/null
+++ b/Devmapper.cpp
@@ -0,0 +1,210 @@
+/*
+ * 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/ioctl.h>
+#include <sys/stat.h>
+
+#define LOG_TAG "Vold"
+
+#include <cutils/log.h>
+
+#include "Devmapper.h"
+
+void Devmapper::ioctlInit(struct dm_ioctl *io, size_t dataSize,
+ const char *name, unsigned flags) {
+ memset(io, 0, dataSize);
+ io->data_size = dataSize;
+ io->data_start = sizeof(struct dm_ioctl);
+ io->version[0] = 4;
+ io->version[1] = 0;
+ io->version[2] = 0;
+ io->flags = flags;
+ strncpy(io->name, name, sizeof(io->name));
+}
+
+int Devmapper::lookupActive(const char *name, char *ubuffer, size_t len) {
+ char *buffer = (char *) malloc(4096);
+ if (!buffer) {
+ LOGE("Error allocating memory (%s)", strerror(errno));
+ return -1;
+ }
+
+ int fd;
+ if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
+ LOGE("Error opening devmapper (%s)", strerror(errno));
+ free(buffer);
+ return -1;
+ }
+
+ struct dm_ioctl *io = (struct dm_ioctl *) buffer;
+
+ ioctlInit(io, 4096, name, 0);
+ if (ioctl(fd, DM_DEV_STATUS, io)) {
+ if (errno != ENODEV) {
+ LOGE("Error retrieving device status (%s)", strerror(errno));
+ }
+ free(buffer);
+ close(fd);
+ return -1;
+ }
+ close(fd);
+
+ unsigned minor = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
+ free(buffer);
+ LOGD("Newly created devmapper instance minor = %d\n", minor);
+ snprintf(ubuffer, len, "/dev/block/dm-%u", minor);
+ return 0;
+}
+
+int Devmapper::create(const char *name, const char *loopFile, const char *key, int sizeMb,
+ char *ubuffer, size_t len) {
+ char *buffer = (char *) malloc(4096);
+ if (!buffer) {
+ LOGE("Error allocating memory (%s)", strerror(errno));
+ return -1;
+ }
+
+ int fd;
+ if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
+ LOGE("Error opening devmapper (%s)", strerror(errno));
+ free(buffer);
+ return -1;
+ }
+
+ struct dm_ioctl *io = (struct dm_ioctl *) buffer;
+
+ // Create the DM device
+ ioctlInit(io, 4096, name, 0);
+
+ if (ioctl(fd, DM_DEV_CREATE, io)) {
+ LOGE("Error creating device mapping (%s)", strerror(errno));
+ free(buffer);
+ close(fd);
+ return -1;
+ }
+
+ // Set the legacy geometry
+ ioctlInit(io, 4096, name, 0);
+
+ char *geoParams = buffer + sizeof(struct dm_ioctl);
+ // bps=512 spc=8 res=32 nft=2 sec=8190 mid=0xf0 spt=63 hds=64 hid=0 bspf=8 rdcl=2 infs=1 bkbs=2
+ strcpy(geoParams, "0 64 63 0");
+ geoParams += strlen(geoParams) + 1;
+ geoParams = (char *) _align(geoParams, 8);
+ if (ioctl(fd, DM_DEV_SET_GEOMETRY, io)) {
+ LOGE("Error setting device geometry (%s)", strerror(errno));
+ free(buffer);
+ close(fd);
+ return -1;
+ }
+
+ // Retrieve the device number we were allocated
+ ioctlInit(io, 4096, name, 0);
+ if (ioctl(fd, DM_DEV_STATUS, io)) {
+ LOGE("Error retrieving device status (%s)", strerror(errno));
+ free(buffer);
+ close(fd);
+ return -1;
+ }
+
+ unsigned minor = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
+ LOGD("Newly created devmapper instance minor = %d\n", minor);
+ snprintf(ubuffer, len, "/dev/block/dm-%u", minor);
+
+ // Load the table
+ struct dm_target_spec *tgt;
+ tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
+
+ ioctlInit(io, 4096, name, DM_STATUS_TABLE_FLAG);
+ io->target_count = 1;
+ tgt->status = 0;
+ tgt->sector_start = 0;
+ tgt->length = (sizeMb * (1024 * 1024)) / 512;
+ strcpy(tgt->target_type, "crypt");
+
+ char *cryptParams = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
+ sprintf(cryptParams, "twofish %s 0 %s 0", key, loopFile);
+ cryptParams += strlen(cryptParams) + 1;
+ cryptParams = (char *) _align(cryptParams, 8);
+ tgt->next = cryptParams - buffer;
+
+ if (ioctl(fd, DM_TABLE_LOAD, io)) {
+ LOGE("Error loading mapping table (%s)", strerror(errno));
+ free(buffer);
+ close(fd);
+ return -1;
+ }
+
+ // Resume the new table
+ ioctlInit(io, 4096, name, 0);
+
+ if (ioctl(fd, DM_DEV_SUSPEND, io)) {
+ LOGE("Error Resuming (%s)", strerror(errno));
+ free(buffer);
+ close(fd);
+ return -1;
+ }
+
+ free(buffer);
+
+ return 0;
+}
+
+int Devmapper::destroy(const char *name) {
+ char *buffer = (char *) malloc(4096);
+ if (!buffer) {
+ LOGE("Error allocating memory (%s)", strerror(errno));
+ return -1;
+ }
+
+ int fd;
+ if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
+ LOGE("Error opening devmapper (%s)", strerror(errno));
+ free(buffer);
+ return -1;
+ }
+
+ struct dm_ioctl *io = (struct dm_ioctl *) buffer;
+
+ // Create the DM device
+ ioctlInit(io, 4096, name, 0);
+
+ if (ioctl(fd, DM_DEV_REMOVE, io)) {
+ LOGE("Error destroying device mapping (%s)", strerror(errno));
+ free(buffer);
+ close(fd);
+ return -1;
+ }
+
+ free(buffer);
+ close(fd);
+ return 0;
+}
+
+void *Devmapper::_align(void *ptr, unsigned int a)
+{
+ register unsigned long agn = --a;
+
+ return (void *) (((unsigned long) ptr + agn) & ~agn);
+}
+
diff --git a/Devmapper.h b/Devmapper.h
new file mode 100644
index 0000000..d6cdd05
--- /dev/null
+++ b/Devmapper.h
@@ -0,0 +1,35 @@
+/*
+ * 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 _DEVMAPPER_H
+#define _DEVMAPPER_H
+
+#include <unistd.h>
+#include <linux/dm-ioctl.h>
+
+class Devmapper {
+public:
+ static int create(const char *name, const char *loopFile, const char *key, int sizeMb,
+ char *buffer, size_t len);
+ static int destroy(const char *name);
+ static int lookupActive(const char *name, char *buffer, size_t len);
+private:
+ static void *_align(void *ptr, unsigned int a);
+ static void ioctlInit(struct dm_ioctl *io, size_t data_size,
+ const char *name, unsigned flags);
+};
+
+#endif
diff --git a/Loop.cpp b/Loop.cpp
index 1b3879a..b8106ef 100644
--- a/Loop.cpp
+++ b/Loop.cpp
@@ -43,7 +43,9 @@
sprintf(filename, "/dev/block/loop%d", i);
if ((fd = open(filename, O_RDWR)) < 0) {
- LOGE("Unable to open %s (%s)", filename, strerror(errno));
+ if (errno != ENOENT) {
+ LOGE("Unable to open %s (%s)", filename, strerror(errno));
+ }
return -1;
}
@@ -88,7 +90,7 @@
* are created on-demand if needed.
*/
mode_t mode = 0660 | S_IFBLK;
- dev_t dev = (7 << 8) | i;
+ unsigned int dev = (0xff & i) | ((i << 12) & 0xfff00000) | (7 << 8);
if (mknod(filename, mode, dev) < 0) {
if (errno != EEXIST) {
LOGE("Error creating loop device node (%s)", strerror(errno));
@@ -189,7 +191,7 @@
return -1;
}
- if (ftruncate(fd, (sizeMb * (1024 * 1024))) < 0) {
+ if (ftruncate(fd, 1024 + (sizeMb * (1024 * 1024))) < 0) {
LOGE("Error truncating imagefile (%s)", strerror(errno));
close(fd);
return -1;
diff --git a/Loop.h b/Loop.h
index 1ade4f9..da91dc2 100644
--- a/Loop.h
+++ b/Loop.h
@@ -22,7 +22,7 @@
class Loop {
public:
- static const int LOOP_MAX = 255;
+ static const int LOOP_MAX = 4096;
public:
static int lookupActive(const char *loopFile, char *buffer, size_t len);
static int create(const char *loopFile, char *loopDeviceBuffer, size_t len);
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index a462196..201ad98 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -36,6 +36,7 @@
#include "ResponseCode.h"
#include "Loop.h"
#include "Fat.h"
+#include "Devmapper.h"
extern "C" void KillProcessesWithOpenFiles(const char *, int, int, int);
@@ -204,10 +205,27 @@
return -1;
}
- /* XXX: Start devmapper */
+ char dmDevice[255];
+ bool cleanupDm = false;
- if (Fat::format(loopDevice)) {
+ if (strcmp(key, "none")) {
+ if (Devmapper::create(id, loopDevice, key, sizeMb, dmDevice,
+ sizeof(dmDevice))) {
+ LOGE("ASEC device mapping failed (%s)", strerror(errno));
+ Loop::destroyByDevice(loopDevice);
+ unlink(asecFileName);
+ return -1;
+ }
+ cleanupDm = true;
+ } else {
+ strcpy(dmDevice, loopDevice);
+ }
+
+ if (Fat::format(dmDevice)) {
LOGE("ASEC FAT format failed (%s)", strerror(errno));
+ if (cleanupDm) {
+ Devmapper::destroy(id);
+ }
Loop::destroyByDevice(loopDevice);
unlink(asecFileName);
return -1;
@@ -219,16 +237,22 @@
if (mkdir(mountPoint, 0777)) {
if (errno != EEXIST) {
LOGE("Mountpoint creation failed (%s)", strerror(errno));
+ if (cleanupDm) {
+ Devmapper::destroy(id);
+ }
Loop::destroyByDevice(loopDevice);
unlink(asecFileName);
return -1;
}
}
- if (Fat::doMount(loopDevice, mountPoint, false, false, ownerUid,
+ if (Fat::doMount(dmDevice, mountPoint, false, false, ownerUid,
0, 0000, false)) {
// 0, 0007, false)) {
LOGE("ASEC FAT mount failed (%s)", strerror(errno));
+ if (cleanupDm) {
+ Devmapper::destroy(id);
+ }
Loop::destroyByDevice(loopDevice);
unlink(asecFileName);
return -1;
@@ -261,7 +285,7 @@
return 0;
}
-int VolumeManager::destroyAsec(const char *id) {
+int VolumeManager::unmountAsec(const char *id) {
char asecFileName[255];
char mountPoint[255];
@@ -270,35 +294,57 @@
snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
if (isMountpointMounted(mountPoint)) {
- int i, rc;
- for (i = 0; i < 10; i++) {
- rc = umount(mountPoint);
- if (!rc) {
- break;
- }
- if (rc && (errno == EINVAL || errno == ENOENT)) {
- rc = 0;
- break;
- }
- LOGW("ASEC %s unmount attempt %d failed (%s)",
- id, i +1, strerror(errno));
+ LOGE("Unmount request for ASEC %s when not mounted", id);
+ errno = EINVAL;
+ return -1;
+ }
- if (i >= 5) {
- KillProcessesWithOpenFiles(mountPoint, (i < 7 ? 0 : 1),
- NULL, 0);
- }
- usleep(1000 * 250);
+ int i, rc;
+ for (i = 0; i < 10; i++) {
+ rc = umount(mountPoint);
+ if (!rc) {
+ break;
}
- if (rc) {
- LOGE("Failed to unmount ASEC %s for destroy", id);
- return -1;
+ if (rc && (errno == EINVAL || errno == ENOENT)) {
+ rc = 0;
+ break;
}
+ LOGW("ASEC %s unmount attempt %d failed (%s)",
+ id, i +1, strerror(errno));
+
+ if (i >= 5) {
+ KillProcessesWithOpenFiles(mountPoint, (i < 7 ? 0 : 1),
+ NULL, 0);
+ }
+ usleep(1000 * 250);
+ }
+
+ if (rc) {
+ LOGE("Failed to unmount ASEC %s", id);
+ return -1;
+ }
+
+ if (Devmapper::destroy(id) && errno != ENXIO) {
+ LOGE("Failed to destroy devmapper instance (%s)", strerror(errno));
}
char loopDevice[255];
if (!Loop::lookupActive(asecFileName, loopDevice, sizeof(loopDevice))) {
Loop::destroyByDevice(loopDevice);
}
+ return 0;
+}
+
+int VolumeManager::destroyAsec(const char *id) {
+ char asecFileName[255];
+ char mountPoint[255];
+
+ snprintf(asecFileName, sizeof(asecFileName),
+ "/sdcard/android_secure/%s.asec", id);
+ snprintf(mountPoint, sizeof(mountPoint), "/asec/%s", id);
+
+ if (unmountAsec(id))
+ return -1;
unlink(asecFileName);
@@ -326,17 +372,66 @@
LOGE("ASEC loop device creation failed (%s)", strerror(errno));
return -1;
}
+ LOGD("New loop device created at %s", loopDevice);
+ } else {
+ LOGD("Found active loopback for %s at %s", asecFileName, loopDevice);
+ }
+
+ char dmDevice[255];
+ bool cleanupDm = false;
+ if (strcmp(key, "none")) {
+ if (Devmapper::lookupActive(id, dmDevice, sizeof(dmDevice))) {
+ unsigned int nr_sec = 0;
+ int fd;
+
+ if ((fd = open(loopDevice, O_RDWR)) < 0) {
+ LOGE("Failed to open loopdevice (%s)", strerror(errno));
+ Loop::destroyByDevice(loopDevice);
+ return -1;
+ }
+
+ if (ioctl(fd, BLKGETSIZE, &nr_sec)) {
+ LOGE("Failed to get loop size (%s)", strerror(errno));
+ Loop::destroyByDevice(loopDevice);
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ if (Devmapper::create(id, loopDevice, key,
+ (nr_sec * 512) / (1024 * 1024),
+ dmDevice, sizeof(dmDevice))) {
+ LOGE("ASEC device mapping failed (%s)", strerror(errno));
+ Loop::destroyByDevice(loopDevice);
+ return -1;
+ }
+ LOGD("New devmapper instance created at %s", dmDevice);
+ } else {
+ LOGD("Found active devmapper for %s at %s", asecFileName, dmDevice);
+ }
+ cleanupDm = true;
+ } else {
+ strcpy(dmDevice, loopDevice);
}
if (mkdir(mountPoint, 0777)) {
- LOGE("Mountpoint creation failed (%s)", strerror(errno));
- return -1;
+ if (errno != EEXIST) {
+ LOGE("Mountpoint creation failed (%s)", strerror(errno));
+ if (cleanupDm) {
+ Devmapper::destroy(id);
+ }
+ Loop::destroyByDevice(loopDevice);
+ return -1;
+ }
}
- if (Fat::doMount(loopDevice, mountPoint, true, false, ownerUid, 0,
+ if (Fat::doMount(dmDevice, mountPoint, true, false, ownerUid, 0,
0222, false)) {
// 0227, false)) {
LOGE("ASEC mount failed (%s)", strerror(errno));
+ if (cleanupDm) {
+ Devmapper::destroy(id);
+ }
+ Loop::destroyByDevice(loopDevice);
return -1;
}
diff --git a/VolumeManager.h b/VolumeManager.h
index a0b63ac..80b4ccf 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -60,6 +60,7 @@
int finalizeAsec(const char *id);
int destroyAsec(const char *id);
int mountAsec(const char *id, const char *key, int ownerUid);
+ int unmountAsec(const char *id);
int getAsecMountPath(const char *id, char *buffer, int maxlen);
// XXX: This should be moved private once switch uevents are working