mount-encrypted: use minimum mkfs size on migration

When doing a migration, try to guess at a smaller minimum size for the
initial filesystem so that systems with giant drives are not needlessly
penalized. Start with an even smaller initial filesystem size (16M).

Move debug time counters into the main .o file to avoid compiler
insanity when turning debug on and off.

BUG=chromium-os:22172
TEST=link build & boot, manual testing

Change-Id: I47c3ffb6e4cd88c4f0ead6fa21724704c7ed1630
Signed-off-by: Kees Cook <keescook@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/25638
Reviewed-by: Elly Jones <ellyjones@chromium.org>
diff --git a/utility/mount-encrypted.c b/utility/mount-encrypted.c
index d3f7174..6fd03c2 100644
--- a/utility/mount-encrypted.c
+++ b/utility/mount-encrypted.c
@@ -48,13 +48,14 @@
 static const gchar * const kTpmDev = "/dev/tpm0";
 static const gchar * const kNullDev = "/dev/null";
 static const float kSizePercent = 0.3;
+static const float kMigrationSizeMultiplier = 1.1;
 static const uint32_t kLockboxIndex = 0x20000004;
 static const uint32_t kLockboxSizeV1 = 0x2c;
 static const uint32_t kLockboxSizeV2 = 0x45;
 static const uint32_t kLockboxSaltOffset = 0x5;
 static const size_t kSectorSize = 512;
 static const size_t kExt4BlockSize = 4096;
-static const size_t kExt4MinBytes = 64 * 1024 * 1024;
+static const size_t kExt4MinBytes = 16 * 1024 * 1024;
 
 enum migration_method {
 	MIGRATE_TEST_ONLY,
@@ -87,6 +88,11 @@
 	{ },
 };
 
+#if DEBUG_ENABLED
+struct timeval tick = { };
+struct timeval tick_start = { };
+#endif
+
 static struct bind_mount *bind_mounts = NULL;
 static gchar *rootdir = NULL;
 static gchar *stateful_mount = NULL;
@@ -654,6 +660,7 @@
 	size_t sectors;
 	struct bind_mount *bind;
 	int sparsefd;
+	struct statvfs stateful_statbuf;
 	size_t blocks_min, blocks_max;
 
 	/* Use the "system key" to decrypt the "encryption key" stored in
@@ -681,27 +688,26 @@
 	}
 
 	if (rebuild) {
-		struct statvfs buf;
-		off_t size;
+		off_t fs_bytes_max;
 
 		/* Wipe out the old files, and ignore errors. */
 		unlink(key_path);
 		unlink(block_path);
 
 		/* Calculate the desired size of the new partition. */
-		if (statvfs(stateful_mount, &buf)) {
+		if (statvfs(stateful_mount, &stateful_statbuf)) {
 			PERROR(stateful_mount);
 			return 0;
 		}
-		size = buf.f_blocks;
-		size *= kSizePercent;
-		size *= buf.f_frsize;
+		fs_bytes_max = stateful_statbuf.f_blocks;
+		fs_bytes_max *= kSizePercent;
+		fs_bytes_max *= stateful_statbuf.f_frsize;
 
 		INFO("Creating sparse backing file with size %llu.",
-		     (unsigned long long)size);
+		     (unsigned long long)fs_bytes_max);
 
 		/* Create the sparse file. */
-		sparsefd = sparse_create(block_path, size);
+		sparsefd = sparse_create(block_path, fs_bytes_max);
 		if (sparsefd < 0) {
 			PERROR(block_path);
 			return 0;
@@ -752,8 +758,42 @@
 
 	/* Calculate filesystem min/max size. */
 	blocks_max = sectors / (kExt4BlockSize / kSectorSize);
-	blocks_min = migrate_needed ? blocks_max :
-			kExt4MinBytes / kExt4BlockSize;
+	blocks_min = kExt4MinBytes / kExt4BlockSize;
+	if (migrate_needed && migrate_allowed) {
+		off_t fs_bytes_min;
+		size_t calc_blocks_min;
+		/* When doing a migration, the new filesystem must be
+		 * large enough to hold what we're going to migrate.
+		 * Instead of walking the bind mount sources, which would
+		 * be IO and time expensive, just read the bytes-used
+		 * value from statvfs (plus 10% for overhead). It will
+		 * be too large, since it includes the eCryptFS data, so
+		 * we must cap at the max filesystem size just in case.
+		 */
+
+		/* Bytes used in stateful partition plus 10%. */
+		fs_bytes_min = stateful_statbuf.f_blocks -
+			       stateful_statbuf.f_bfree;
+		fs_bytes_min *= stateful_statbuf.f_frsize;
+		DEBUG("Stateful bytes used: %llu",
+			(unsigned long long)fs_bytes_min);
+		fs_bytes_min *= kMigrationSizeMultiplier;
+
+		/* Minimum blocks needed for that many bytes. */
+		calc_blocks_min = fs_bytes_min / kExt4BlockSize;
+		/* Do not use more than blocks_max. */
+		if (calc_blocks_min > blocks_max)
+			calc_blocks_min = blocks_max;
+		/* Do not use less than blocks_min. */
+		else if (calc_blocks_min < blocks_min)
+			calc_blocks_min = blocks_min;
+
+		DEBUG("Maximum fs blocks: %zu", blocks_max);
+		DEBUG("Minimum fs blocks: %zu", blocks_min);
+		DEBUG("Migration blocks chosen: %zu", calc_blocks_min);
+		blocks_min = calc_blocks_min;
+	}
+
 	if (rebuild) {
 		INFO("Building filesystem on %s "
 			"(blocksize:%zu, min:%zu, max:%zu).",
diff --git a/utility/mount-encrypted.h b/utility/mount-encrypted.h
index 3707a85..e48617d 100644
--- a/utility/mount-encrypted.h
+++ b/utility/mount-encrypted.h
@@ -39,8 +39,8 @@
 } while (0)
 
 #if DEBUG_ENABLED
-static struct timeval tick;
-static struct timeval tick_start;
+extern struct timeval tick;
+extern struct timeval tick_start;
 # define TICK_INIT() do { \
 	gettimeofday(&tick, NULL); \
 	tick_start = tick; \
diff --git a/utility/mount-helpers.c b/utility/mount-helpers.c
index 751d3ad..9a472bb 100644
--- a/utility/mount-helpers.c
+++ b/utility/mount-helpers.c
@@ -658,7 +658,7 @@
 	}
 	length = cipher_length + final_len;
 
-	DEBUG("Writing %d bytes to %s", length, keyfile);
+	DEBUG("Writing %zu bytes to %s", length, keyfile);
 	/* TODO(keescook): replace this with a mode-400 writer. */
 	if (!g_file_set_contents(keyfile, (gchar *)cipher, length, &error)) {
 		ERROR("Unable to write %s: %s", keyfile, error->message);