Enable allow_discards if dm-crypt supports it

dm-crypt version 1.11.0 and later supports the allow_discards option
when setting up a crypto device.  This passes discard requests from
the filesytem to the underlying block device.  This helps make flash
based storage faster.  So query the dm-crypt version, and pass the
option if the version is 1.11.0 or greater.

Change-Id: If30e9db5a2dbd6ea0281d91344e5b2c35e75131e
diff --git a/cryptfs.c b/cryptfs.c
index bcacc39..7da84bc 100644
--- a/cryptfs.c
+++ b/cryptfs.c
@@ -377,6 +377,86 @@
 
 }
 
+static int load_crypto_mapping_table(struct crypt_mnt_ftr *crypt_ftr, unsigned char *master_key,
+                                     char *real_blk_name, const char *name, int fd,
+                                     char *extra_params)
+{
+  char buffer[DM_CRYPT_BUF_SIZE];
+  struct dm_ioctl *io;
+  struct dm_target_spec *tgt;
+  char *crypt_params;
+  char master_key_ascii[129]; /* Large enough to hold 512 bit key and null */
+  int i;
+
+  io = (struct dm_ioctl *) buffer;
+
+  /* Load the mapping table for this device */
+  tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
+
+  ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+  io->target_count = 1;
+  tgt->status = 0;
+  tgt->sector_start = 0;
+  tgt->length = crypt_ftr->fs_size;
+  strcpy(tgt->target_type, "crypt");
+
+  crypt_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
+  convert_key_to_hex_ascii(master_key, crypt_ftr->keysize, master_key_ascii);
+  sprintf(crypt_params, "%s %s 0 %s 0 %s", crypt_ftr->crypto_type_name,
+          master_key_ascii, real_blk_name, extra_params);
+  crypt_params += strlen(crypt_params) + 1;
+  crypt_params = (char *) (((unsigned long)crypt_params + 7) & ~8); /* Align to an 8 byte boundary */
+  tgt->next = crypt_params - buffer;
+
+  for (i = 0; i < TABLE_LOAD_RETRIES; i++) {
+    if (! ioctl(fd, DM_TABLE_LOAD, io)) {
+      break;
+    }
+    usleep(500000);
+  }
+
+  if (i == TABLE_LOAD_RETRIES) {
+    /* We failed to load the table, return an error */
+    return -1;
+  } else {
+    return i + 1;
+  }
+}
+
+
+static int get_dm_crypt_version(int fd, const char *name,  int *version)
+{
+    char buffer[DM_CRYPT_BUF_SIZE];
+    struct dm_ioctl *io;
+    struct dm_target_versions *v;
+    int i;
+
+    io = (struct dm_ioctl *) buffer;
+
+    ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+
+    if (ioctl(fd, DM_LIST_VERSIONS, io)) {
+        return -1;
+    }
+
+    /* Iterate over the returned versions, looking for name of "crypt".
+     * When found, get and return the version.
+     */
+    v = (struct dm_target_versions *) &buffer[sizeof(struct dm_ioctl)];
+    while (v->next) {
+        if (! strcmp(v->name, "crypt")) {
+            /* We found the crypt driver, return the version, and get out */
+            version[0] = v->version[0];
+            version[1] = v->version[1];
+            version[2] = v->version[2];
+            return 0;
+        }
+        v = (struct dm_target_versions *)(((char *)v) + v->next);
+    }
+
+    return -1;
+}
+
 static int create_crypto_blk_dev(struct crypt_mnt_ftr *crypt_ftr, unsigned char *master_key,
                                     char *real_blk_name, char *crypto_blk_name, const char *name)
 {
@@ -389,6 +469,9 @@
   int fd;
   int i;
   int retval = -1;
+  int version[3];
+  char *extra_params;
+  int load_count;
 
   if ((fd = open("/dev/device-mapper", O_RDWR)) < 0 ) {
     SLOGE("Cannot open device-mapper\n");
@@ -412,40 +495,27 @@
   minor = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
   snprintf(crypto_blk_name, MAXPATHLEN, "/dev/block/dm-%u", minor);
 
-  /* Load the mapping table for this device */
-  tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
-
-  ioctl_init(io, 4096, name, 0);
-  io->target_count = 1;
-  tgt->status = 0;
-  tgt->sector_start = 0;
-  tgt->length = crypt_ftr->fs_size;
-  strcpy(tgt->target_type, "crypt");
-
-  crypt_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
-  convert_key_to_hex_ascii(master_key, crypt_ftr->keysize, master_key_ascii);
-  sprintf(crypt_params, "%s %s 0 %s 0", crypt_ftr->crypto_type_name,
-          master_key_ascii, real_blk_name);
-  crypt_params += strlen(crypt_params) + 1;
-  crypt_params = (char *) (((unsigned long)crypt_params + 7) & ~8); /* Align to an 8 byte boundary */
-  tgt->next = crypt_params - buffer;
-
-  for (i = 0; i < TABLE_LOAD_RETRIES; i++) {
-    if (! ioctl(fd, DM_TABLE_LOAD, io)) {
-      break;
-    }
-    usleep(500000);
+  extra_params = "";
+  if (! get_dm_crypt_version(fd, name, version)) {
+      /* Support for allow_discards was added in version 1.11.0 */
+      if ((version[0] >= 2) ||
+          ((version[0] == 1) && (version[1] >= 11))) {
+          extra_params = "1 allow_discards";
+          SLOGI("Enabling support for allow_discards in dmcrypt.\n");
+      }
   }
 
-  if (i == TABLE_LOAD_RETRIES) {
+  load_count = load_crypto_mapping_table(crypt_ftr, master_key, real_blk_name, name,
+                                         fd, extra_params);
+  if (load_count < 0) {
       SLOGE("Cannot load dm-crypt mapping table.\n");
       goto errout;
-  } else if (i) {
-      SLOGI("Took %d tries to load dmcrypt table.\n", i + 1);
+  } else if (load_count > 1) {
+      SLOGI("Took %d tries to load dmcrypt table.\n", load_count);
   }
 
   /* Resume this device to activate it */
-  ioctl_init(io, 4096, name, 0);
+  ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
 
   if (ioctl(fd, DM_DEV_SUSPEND, io)) {
     SLOGE("Cannot resume the dm-crypt device\n");