vold: allow to store key in a file on another partition

Add support for keeping the keys in a separate file on another partition,
for devices with no space reserved for a footer after the userdata filesystem.

Add support for encrypting the volumes managed by vold, if they meet certain
criteria, namely being marked as nonremovable and encryptable in vold.fstab.
A bit of trickiness is required to keep vold happy.

Change-Id: Idf0611f74b56c1026c45742ca82e0c26e58828fe
diff --git a/Volume.cpp b/Volume.cpp
index ce41455..40a3669 100644
--- a/Volume.cpp
+++ b/Volume.cpp
@@ -25,6 +25,7 @@
 #include <sys/types.h>
 #include <sys/mman.h>
 #include <sys/mount.h>
+#include <sys/param.h>
 
 #include <linux/kdev_t.h>
 #include <linux/fs.h>
@@ -44,6 +45,7 @@
 #include "ResponseCode.h"
 #include "Fat.h"
 #include "Process.h"
+#include "cryptfs.h"
 
 extern "C" void dos_partition_dec(void const *pp, struct dos_partition *d);
 extern "C" void dos_partition_enc(void *pp, struct dos_partition *d);
@@ -284,8 +286,19 @@
     char errmsg[255];
     const char* externalStorage = getenv("EXTERNAL_STORAGE");
     bool primaryStorage = externalStorage && !strcmp(getMountpoint(), externalStorage);
+    char decrypt_state[PROPERTY_VALUE_MAX];
+    char crypto_state[PROPERTY_VALUE_MAX];
+    char encrypt_progress[PROPERTY_VALUE_MAX];
+    int flags;
 
-    if (getState() == Volume::State_NoMedia) {
+    property_get("vold.decrypt", decrypt_state, "");
+    property_get("vold.encrypt_progress", encrypt_progress, "");
+
+    /* Don't try to mount the volumes if we have not yet entered the disk password
+     * or are in the process of encrypting.
+     */
+    if ((getState() == Volume::State_NoMedia) ||
+        ((!strcmp(decrypt_state, "1") || encrypt_progress[0]) && primaryStorage)) {
         snprintf(errmsg, sizeof(errmsg),
                  "Volume %s %s mount failed - no media",
                  getLabel(), getMountpoint());
@@ -312,6 +325,56 @@
         return -1;
     }
 
+    /* If we're running encrypted, and the volume is marked as encryptable and nonremovable,
+     * and vold is asking to mount the primaryStorage device, then we need to decrypt
+     * that partition, and update the volume object to point to it's new decrypted
+     * block device
+     */
+    property_get("ro.crypto.state", crypto_state, "");
+    flags = getFlags();
+    if (primaryStorage &&
+        ((flags & (VOL_NONREMOVABLE | VOL_ENCRYPTABLE))==(VOL_NONREMOVABLE | VOL_ENCRYPTABLE)) &&
+        !strcmp(crypto_state, "encrypted") && !isDecrypted()) {
+       char new_sys_path[MAXPATHLEN];
+       char nodepath[256];
+       int new_major, new_minor;
+
+       if (n != 1) {
+           /* We only expect one device node returned when mounting encryptable volumes */
+           SLOGE("Too many device nodes returned when mounting %d\n", getMountpoint());
+           return -1;
+       }
+
+       if (cryptfs_setup_volume(getLabel(), MAJOR(deviceNodes[0]), MINOR(deviceNodes[0]),
+                                new_sys_path, sizeof(new_sys_path),
+                                &new_major, &new_minor)) {
+           SLOGE("Cannot setup encryption mapping for %d\n", getMountpoint());
+           return -1;
+       }
+       /* We now have the new sysfs path for the decrypted block device, and the
+        * majore and minor numbers for it.  So, create the device, update the
+        * path to the new sysfs path, and continue.
+        */
+        snprintf(nodepath,
+                 sizeof(nodepath), "/dev/block/vold/%d:%d",
+                 new_major, new_minor);
+        if (createDeviceNode(nodepath, new_major, new_minor)) {
+            SLOGE("Error making device node '%s' (%s)", nodepath,
+                                                       strerror(errno));
+        }
+
+        // Todo: Either create sys filename from nodepath, or pass in bogus path so
+        //       vold ignores state changes on this internal device.
+        updateDeviceInfo(nodepath, new_major, new_minor);
+
+        /* Get the device nodes again, because they just changed */
+        n = getDeviceNodes((dev_t *) &deviceNodes, 4);
+        if (!n) {
+            SLOGE("Failed to get device nodes (%s)\n", strerror(errno));
+            return -1;
+        }
+    }
+
     for (i = 0; i < n; i++) {
         char devicePath[255];