ext4_utils: mark uninitialized inode tables in block groups

Block groups that have no used inodes have their inode table left
uninitialized, unless -t is specified, in which case they are
explicitly zeroed.  When they are uninitialized, writing a sparse
ext4 image over existing data can cause e2fsck to confuse the
uninitialized data for lost inodes.

Set the EXT4_BG_INODE_UNINIT flags on block groups that have no
used inodes.  This flag requires the block group checksum feature
to be enabled, so also enable the checksum feature in the superblock
and compute the checksum for the block group.

Since zeroing the inode tables is now useless, remove the code for
it and deprecate the -t command line option.

Change-Id: I4927c1d866d051547cf0dadc8c8703ded0163925
diff --git a/ext4_utils/Android.mk b/ext4_utils/Android.mk
index 1fef735..6eba3b1 100644
--- a/ext4_utils/Android.mk
+++ b/ext4_utils/Android.mk
@@ -12,7 +12,8 @@
     indirect.c \
     uuid.c \
     sha1.c \
-    wipe.c
+    wipe.c \
+    crc16.c
 
 #
 # -- All host/targets including windows
diff --git a/ext4_utils/allocate.c b/ext4_utils/allocate.c
index adf91ba..3229abe 100644
--- a/ext4_utils/allocate.c
+++ b/ext4_utils/allocate.c
@@ -56,6 +56,7 @@
 	u32 first_free_block;
 	u32 free_inodes;
 	u32 first_free_inode;
+	u16 flags;
 	u16 used_dirs;
 };
 
@@ -157,24 +158,8 @@
 
 	sparse_file_add_data(info.sparse_file, bg->inode_table,
 			aux_info.inode_table_blocks	* info.block_size, block);
-}
 
-void init_unused_inode_tables(void)
-{
-	unsigned int i;
-	u32 block;
-	struct block_group_info *bg;
-
-	for (i = 0; i < aux_info.groups; i++) {
-		if (!aux_info.bgs[i].inode_table) {
-			bg = &aux_info.bgs[i];
-			block = bg->first_block + 2;
-			if (bg->has_superblock)
-				block += aux_info.bg_desc_blocks + info.bg_desc_reserve_blocks + 1;
-			sparse_file_add_fill(info.sparse_file, 0,
-					aux_info.inode_table_blocks * info.block_size, block);
-		}
-	}
+	bg->flags &= ~EXT4_BG_INODE_UNINIT;
 }
 
 static int bitmap_set_bit(u8 *bitmap, u32 bit)
@@ -297,6 +282,7 @@
 	bg->first_free_block = 0;
 	bg->free_inodes = info.inodes_per_group;
 	bg->first_free_inode = 1;
+	bg->flags = EXT4_BG_INODE_UNINIT;
 
 	if (reserve_blocks(bg, bg->first_free_block, bg->header_blocks) < 0)
 		error("failed to reserve %u blocks in block group %u\n", bg->header_blocks, i);
@@ -744,6 +730,12 @@
 	return aux_info.bgs[bg].used_dirs;
 }
 
+/* Returns the flags for a block group */
+u16 get_bg_flags(int bg)
+{
+	return aux_info.bgs[bg].flags;
+}
+
 /* Frees the memory used by a linked list of allocation regions */
 void free_alloc(struct block_allocation *alloc)
 {
diff --git a/ext4_utils/allocate.h b/ext4_utils/allocate.h
index fb6e0f7..0575e84 100644
--- a/ext4_utils/allocate.h
+++ b/ext4_utils/allocate.h
@@ -41,6 +41,7 @@
 u32 reserve_inodes(int bg, u32 inodes);
 void add_directory(u32 inode);
 u16 get_directories(int bg);
+u16 get_bg_flags(int bg);
 void init_unused_inode_tables(void);
 u32 allocate_inode();
 void free_alloc(struct block_allocation *alloc);
diff --git a/ext4_utils/crc16.c b/ext4_utils/crc16.c
new file mode 100644
index 0000000..2575812
--- /dev/null
+++ b/ext4_utils/crc16.c
@@ -0,0 +1,58 @@
+/*-
+ *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
+ *  code or tables extracted from it, as desired without restriction.
+ */
+
+/* CRC32 code derived from work by Gary S. Brown. */
+
+/* Code taken from FreeBSD 8 */
+
+/* Converted to crc16 */
+
+#include "ext4_utils.h"
+
+static u16 crc16_tab[] = {
+		0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
+		0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
+		0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
+		0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
+		0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
+		0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
+		0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
+		0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
+		0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
+		0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
+		0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
+		0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
+		0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
+		0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
+		0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
+		0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
+		0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
+		0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
+		0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
+		0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
+		0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
+		0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
+		0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
+		0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
+		0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
+		0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
+		0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
+		0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
+		0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
+		0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
+		0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
+		0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040,
+};
+
+u16 ext4_crc16(u16 crc_in, const void *buf, int size)
+{
+        const u8 *p = buf;
+        u16 crc = crc_in;
+
+        while (size--)
+                crc = crc16_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
+
+        return crc;
+}
diff --git a/ext4_utils/ext4_utils.c b/ext4_utils/ext4_utils.c
index 43b4480..4b87c6e 100644
--- a/ext4_utils/ext4_utils.c
+++ b/ext4_utils/ext4_utils.c
@@ -25,6 +25,7 @@
 #include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <stddef.h>
 #include <string.h>
 
 #ifdef USE_MINGW
@@ -362,11 +363,12 @@
    block group */
 void ext4_update_free()
 {
-	unsigned int i;
+	u32 i;
 
 	for (i = 0; i < aux_info.groups; i++) {
 		u32 bg_free_blocks = get_free_blocks(i);
 		u32 bg_free_inodes = get_free_inodes(i);
+		u16 crc;
 
 		aux_info.bg_desc[i].bg_free_blocks_count = bg_free_blocks;
 		aux_info.sb->s_free_blocks_count_lo += bg_free_blocks;
@@ -375,6 +377,13 @@
 		aux_info.sb->s_free_inodes_count += bg_free_inodes;
 
 		aux_info.bg_desc[i].bg_used_dirs_count += get_directories(i);
+
+		aux_info.bg_desc[i].bg_flags = get_bg_flags(i);
+
+		crc = ext4_crc16(~0, aux_info.sb->s_uuid, sizeof(aux_info.sb->s_uuid));
+		crc = ext4_crc16(crc, &i, sizeof(i));
+		crc = ext4_crc16(crc, &aux_info.bg_desc[i], offsetof(struct ext2_group_desc, bg_checksum));
+		aux_info.bg_desc[i].bg_checksum = crc;
 	}
 }
 
diff --git a/ext4_utils/ext4_utils.h b/ext4_utils/ext4_utils.h
index 26952c0..0d0b6bc 100644
--- a/ext4_utils/ext4_utils.h
+++ b/ext4_utils/ext4_utils.h
@@ -99,8 +99,10 @@
 	__le16 bg_free_blocks_count;
 	__le16 bg_free_inodes_count;
 	__le16 bg_used_dirs_count;
-	__le16 bg_pad;
-	__le32 bg_reserved[3];
+	__le16 bg_flags;
+	__le32 bg_reserved[2];
+	__le16 bg_reserved16;
+	__le16 bg_checksum;
 };
 
 struct fs_info {
@@ -166,6 +168,7 @@
 u64 get_file_size(int fd);
 u64 parse_num(const char *arg);
 void ext4_parse_sb(struct ext4_super_block *sb);
+u16 ext4_crc16(u16 crc_in, const void *buf, int size);
 
 typedef void (*fs_config_func_t)(const char *path, int dir, unsigned *uid, unsigned *gid,
         unsigned *mode);
@@ -174,7 +177,7 @@
 
 int make_ext4fs_internal(int fd, const char *directory,
                          const char *mountpoint, fs_config_func_t fs_config_func, int gzip,
-                         int sparse, int crc, int wipe, int init_itabs,
+                         int sparse, int crc, int wipe,
                          struct selabel_handle *sehnd, int verbose);
 
 #ifdef __cplusplus
diff --git a/ext4_utils/make_ext4fs.c b/ext4_utils/make_ext4fs.c
index f62fee9..b2d1426 100644
--- a/ext4_utils/make_ext4fs.c
+++ b/ext4_utils/make_ext4fs.c
@@ -361,7 +361,7 @@
 	reset_ext4fs_info();
 	info.len = len;
 
-	return make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 1, 0, 0, 0, sehnd, 0);
+	return make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 1, 0, 0, sehnd, 0);
 }
 
 int make_ext4fs(const char *filename, long long len,
@@ -379,7 +379,7 @@
 		return EXIT_FAILURE;
 	}
 
-	status = make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 0, 0, 1, 0, sehnd, 0);
+	status = make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 0, 0, 1, sehnd, 0);
 	close(fd);
 
 	return status;
@@ -444,7 +444,7 @@
 
 int make_ext4fs_internal(int fd, const char *_directory,
                          const char *_mountpoint, fs_config_func_t fs_config_func, int gzip,
-                         int sparse, int crc, int wipe, int init_itabs,
+                         int sparse, int crc, int wipe,
                          struct selabel_handle *sehnd, int verbose)
 {
 	u32 root_inode_num;
@@ -507,7 +507,8 @@
 
 	info.feat_ro_compat |=
 			EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER |
-			EXT4_FEATURE_RO_COMPAT_LARGE_FILE;
+			EXT4_FEATURE_RO_COMPAT_LARGE_FILE |
+			EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
 
 	info.feat_incompat |=
 			EXT4_FEATURE_INCOMPAT_EXTENTS |
@@ -580,9 +581,6 @@
 
 	ext4_update_free();
 
-	if (init_itabs)
-		init_unused_inode_tables();
-
 	ext4_queue_sb();
 
 	printf("Created filesystem with %d/%d inodes and %d/%d blocks\n",
diff --git a/ext4_utils/make_ext4fs_main.c b/ext4_utils/make_ext4fs_main.c
index 739c4a1..b6c740d 100644
--- a/ext4_utils/make_ext4fs_main.c
+++ b/ext4_utils/make_ext4fs_main.c
@@ -53,7 +53,7 @@
 	fprintf(stderr, "    [ -g <blocks per group> ] [ -i <inodes> ] [ -I <inode size> ]\n");
 	fprintf(stderr, "    [ -L <label> ] [ -f ] [ -a <android mountpoint> ]\n");
 	fprintf(stderr, "    [ -S file_contexts ]\n");
-	fprintf(stderr, "    [ -z | -s ] [ -t ] [ -w ] [ -c ] [ -J ] [ -v ]\n");
+	fprintf(stderr, "    [ -z | -s ] [ -w ] [ -c ] [ -J ] [ -v ]\n");
 	fprintf(stderr, "    <filename> [<directory>]\n");
 }
 
@@ -68,7 +68,6 @@
 	int sparse = 0;
 	int crc = 0;
 	int wipe = 0;
-	int init_itabs = 0;
 	int fd;
 	int exitcode;
 	int verbose = 0;
@@ -129,7 +128,7 @@
 			sparse = 1;
 			break;
 		case 't':
-			init_itabs = 1;
+			fprintf(stderr, "Warning: -t (initialize inode tables) is deprecated\n");
 			break;
 		case 'S':
 #ifndef USE_MINGW
@@ -202,7 +201,7 @@
 	}
 
 	exitcode = make_ext4fs_internal(fd, directory, mountpoint, fs_config_func, gzip,
-			sparse, crc, wipe, init_itabs, sehnd, verbose);
+			sparse, crc, wipe, sehnd, verbose);
 	close(fd);
 
 	return exitcode;