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