Merge "Don't reboot after default encrypting" into lmp-dev
diff --git a/Android.mk b/Android.mk
index 1203db4..1a1d28a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -21,6 +21,7 @@
 
 common_c_includes := \
 	system/extras/ext4_utils \
+	system/extras/f2fs_utils \
 	external/openssl/include \
 	external/stlport/stlport \
 	bionic \
@@ -40,6 +41,7 @@
 	libhardware_legacy \
 	liblogwrap \
 	libext4_utils \
+	libf2fs_sparseblock \
 	libcrypto \
 	libselinux \
 	libutils \
diff --git a/CommandListener.cpp b/CommandListener.cpp
index 26d9b24..8003095 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -403,12 +403,16 @@
         rc = vm->destroyAsec(argv[2], force);
     } else if (!strcmp(argv[1], "mount")) {
         dumpArgs(argc, argv, 3);
-        if (argc != 5) {
+        if (argc != 6) {
             cli->sendMsg(ResponseCode::CommandSyntaxError,
-                    "Usage: asec mount <namespace-id> <key> <ownerUid>", false);
+                    "Usage: asec mount <namespace-id> <key> <ownerUid> <ro|rw>", false);
             return 0;
         }
-        rc = vm->mountAsec(argv[2], argv[3], atoi(argv[4]));
+        bool readOnly = true;
+        if (!strcmp(argv[5], "rw")) {
+            readOnly = false;
+        }
+        rc = vm->mountAsec(argv[2], argv[3], atoi(argv[4]), readOnly);
     } else if (!strcmp(argv[1], "unmount")) {
         dumpArgs(argc, argv, -1);
         if (argc < 3) {
diff --git a/Loop.cpp b/Loop.cpp
index 8672d93..11c114f 100644
--- a/Loop.cpp
+++ b/Loop.cpp
@@ -260,7 +260,7 @@
     SLOGD("Attempting to increase size of %s to %d sectors.", file, numSectors);
 
     if (fallocate(fd, 0, 0, numSectors * 512)) {
-        if (errno == ENOSYS) {
+        if (errno == ENOSYS || errno == ENOTSUP) {
             SLOGW("fallocate not found. Falling back to ftruncate.");
             if (ftruncate(fd, numSectors * 512) < 0) {
                 SLOGE("Error truncating imagefile (%s)", strerror(errno));
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index 06daf40..b2b0cf6 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -641,7 +641,10 @@
      *  add one block for the superblock
      */
     SLOGD("Resizing from %d sectors to %d sectors", oldNumSec, numImgSectors + 1);
-    if (oldNumSec >= numImgSectors + 1) {
+    if (oldNumSec == numImgSectors + 1) {
+        SLOGW("Size unchanged; ignoring resize request");
+        return 0;
+    } else if (oldNumSec > numImgSectors + 1) {
         SLOGE("Only growing is currently supported.");
         close(fd);
         return -1;
@@ -1249,7 +1252,7 @@
     return 0;
 }
 
-int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid) {
+int VolumeManager::mountAsec(const char *id, const char *key, int ownerUid, bool readOnly) {
     char asecFileName[255];
     char mountPoint[255];
 
@@ -1330,9 +1333,9 @@
 
     int result;
     if (sb.c_opts & ASEC_SB_C_OPTS_EXT4) {
-        result = Ext4::doMount(dmDevice, mountPoint, true, false, true);
+        result = Ext4::doMount(dmDevice, mountPoint, readOnly, false, readOnly);
     } else {
-        result = Fat::doMount(dmDevice, mountPoint, true, false, true, ownerUid, 0, 0222, false);
+        result = Fat::doMount(dmDevice, mountPoint, readOnly, false, readOnly, ownerUid, 0, 0222, false);
     }
 
     if (result) {
diff --git a/VolumeManager.h b/VolumeManager.h
index 0d41b23..17fa6f7 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -106,7 +106,7 @@
      */
     int fixupAsecPermissions(const char *id, gid_t gid, const char* privateFilename);
     int destroyAsec(const char *id, bool force);
-    int mountAsec(const char *id, const char *key, int ownerUid);
+    int mountAsec(const char *id, const char *key, int ownerUid, bool readOnly);
     int unmountAsec(const char *id, bool force);
     int renameAsec(const char *id1, const char *id2);
     int getAsecMountPath(const char *id, char *buffer, int maxlen);
diff --git a/cryptfs.c b/cryptfs.c
index 57f24e7..ac250e5 100644
--- a/cryptfs.c
+++ b/cryptfs.c
@@ -51,6 +51,7 @@
 #include "VoldUtil.h"
 #include "crypto_scrypt.h"
 #include "ext4_utils.h"
+#include "f2fs_sparseblock.h"
 #include "CheckBattery.h"
 
 #include <hardware/keymaster.h>
@@ -345,6 +346,10 @@
 
     close(fd);
 
+    if (le32_to_cpu(sb.s_magic) != EXT4_SUPER_MAGIC) {
+        SLOGE("Not a valid ext4 superblock");
+        return 0;
+    }
     block_size = 1024 << sb.s_log_block_size;
     /* compute length in bytes */
     len = ( ((off64_t)sb.s_blocks_count_hi << 32) + sb.s_blocks_count_lo) * block_size;
@@ -1970,7 +1975,6 @@
     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 {
@@ -1991,8 +1995,12 @@
                                    - data->used_blocks_already_done;
         int remaining_time = (int)(elapsed_time * remaining_blocks
                                    / data->used_blocks_already_done);
+
+        // Change time only if not yet set, lower, or a lot higher for
+        // best user experience
         if (data->remaining_time == -1
-            || remaining_time < data->remaining_time) {
+            || remaining_time < data->remaining_time
+            || remaining_time > data->remaining_time + 60) {
             char buf[8];
             snprintf(buf, sizeof(buf), "%d", remaining_time);
             property_set("vold.encrypt_time_remaining", buf);
@@ -2196,6 +2204,107 @@
     return rc;
 }
 
+static int encrypt_one_block_f2fs(u64 pos, void *data)
+{
+    struct encryptGroupsData *priv_dat = (struct encryptGroupsData *)data;
+
+    priv_dat->blocks_already_done = pos - 1;
+    update_progress(priv_dat, 1);
+
+    off64_t offset = pos * CRYPT_INPLACE_BUFSIZE;
+
+    if (pread64(priv_dat->realfd, priv_dat->buffer, CRYPT_INPLACE_BUFSIZE, offset) <= 0) {
+        SLOGE("Error reading real_blkdev %s for inplace encrypt", priv_dat->crypto_blkdev);
+        return -1;
+    }
+
+    if (pwrite64(priv_dat->cryptofd, priv_dat->buffer, CRYPT_INPLACE_BUFSIZE, offset) <= 0) {
+        SLOGE("Error writing crypto_blkdev %s for inplace encrypt", priv_dat->crypto_blkdev);
+        return -1;
+    } else {
+        SLOGD("Encrypted block %"PRIu64, pos);
+    }
+
+    return 0;
+}
+
+static int cryptfs_enable_inplace_f2fs(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;
+    struct f2fs_info *f2fs_info = NULL;
+    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;
+    data.realfd = -1;
+    data.cryptofd = -1;
+    if ( (data.realfd = open64(real_blkdev, O_RDWR)) < 0) {
+        SLOGE("Error opening real_blkdev %s for inplace encrypt\n",
+              real_blkdev);
+        goto errout;
+    }
+    if ( (data.cryptofd = open64(crypto_blkdev, O_WRONLY)) < 0) {
+        SLOGE("Error opening crypto_blkdev %s for inplace encrypt\n",
+              crypto_blkdev);
+        goto errout;
+    }
+
+    f2fs_info = generate_f2fs_info(data.realfd);
+    if (!f2fs_info)
+      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;
+
+    data.tot_used_blocks = get_num_blocks_used(f2fs_info);
+
+    data.one_pct = data.tot_used_blocks / 100;
+    data.cur_pct = 0;
+    data.time_started = time(NULL);
+    data.remaining_time = -1;
+
+    data.buffer = malloc(f2fs_info->block_size);
+    if (!data.buffer) {
+        SLOGE("Failed to allocate crypto buffer");
+        goto errout;
+    }
+
+    data.count = 0;
+
+    /* Currently, this either runs to completion, or hits a nonrecoverable error */
+    rc = run_on_used_blocks(data.blocks_already_done, f2fs_info, &encrypt_one_block_f2fs, &data);
+
+    if (rc) {
+        SLOGE("Error in running over blocks");
+        goto errout;
+    }
+
+    *size_already_done += size;
+    rc = 0;
+
+errout:
+    if (rc)
+        SLOGE("Failed to encrypt f2fs filesystem on %s", real_blkdev);
+
+    free(f2fs_info);
+    free(data.buffer);
+    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,
@@ -2329,10 +2438,20 @@
         return 0;
     }
 
+    /* TODO: identify filesystem type.
+     * As is, cryptfs_enable_inplace_ext4 will fail on an f2fs partition, and
+     * then we will drop down to cryptfs_enable_inplace_f2fs.
+     * */
     if (cryptfs_enable_inplace_ext4(crypto_blkdev, real_blkdev,
-                                    size, size_already_done,
-                                    tot_size, previously_encrypted_upto) == 0) {
-        return 0;
+                                size, size_already_done,
+                                tot_size, previously_encrypted_upto) == 0) {
+      return 0;
+    }
+
+    if (cryptfs_enable_inplace_f2fs(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,
@@ -2501,8 +2620,10 @@
     /* If doing inplace encryption, make sure the orig fs doesn't include the crypto footer */
     if ((how == CRYPTO_ENABLE_INPLACE) && (!strcmp(key_loc, KEY_IN_FOOTER))) {
         unsigned int fs_size_sec, max_fs_size_sec;
-
         fs_size_sec = get_fs_size(real_blkdev);
+        if (fs_size_sec == 0)
+            fs_size_sec = get_f2fs_filesystem_size_sec(real_blkdev);
+
         max_fs_size_sec = nr_sec - (CRYPT_FOOTER_OFFSET / CRYPT_SECTOR_SIZE);
 
         if (fs_size_sec > max_fs_size_sec) {