ext4_utils: Add an optional CRC chunk at the end of sparse files

Change-Id: Ibfcf1cdeab47ca13870350184abf83e530acbc07
diff --git a/ext4_utils/ext4_utils.c b/ext4_utils/ext4_utils.c
index 9d8a9b0..aac9c71 100644
--- a/ext4_utils/ext4_utils.c
+++ b/ext4_utils/ext4_utils.c
@@ -120,11 +120,11 @@
 }
 
 /* Write the filesystem image to a file */
-void write_ext4_image(const char *filename, int gz, int sparse)
+void write_ext4_image(const char *filename, int gz, int sparse, int crc)
 {
 	int ret = 0;
 	struct output_file *out = open_output_file(filename, gz, sparse,
-	        count_sparse_chunks());
+	        count_sparse_chunks(), crc);
 
 	if (!out)
 		return;
diff --git a/ext4_utils/ext4_utils.h b/ext4_utils/ext4_utils.h
index a78b095..6e6c1ec 100644
--- a/ext4_utils/ext4_utils.h
+++ b/ext4_utils/ext4_utils.h
@@ -136,7 +136,7 @@
 }
 
 int ext4_bg_has_super_block(int bg);
-void write_ext4_image(const char *filename, int gz, int sparse);
+void write_ext4_image(const char *filename, int gz, int sparse, int crc);
 void ext4_create_fs_aux_info(void);
 void ext4_free_fs_aux_info(void);
 void ext4_fill_in_sb(void);
diff --git a/ext4_utils/make_ext4fs.c b/ext4_utils/make_ext4fs.c
index 3f0141d..2160d56 100644
--- a/ext4_utils/make_ext4fs.c
+++ b/ext4_utils/make_ext4fs.c
@@ -242,7 +242,8 @@
 }
 
 int make_ext4fs(const char *filename, const char *directory,
-                char *mountpoint, int android, int gzip, int sparse)
+                char *mountpoint, int android, int gzip, int sparse,
+                int crc)
 {
         u32 root_inode_num;
         u16 root_mode;
@@ -340,7 +341,7 @@
 			aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo,
 			aux_info.sb->s_blocks_count_lo);
 
-	write_ext4_image(filename, gzip, sparse);
+	write_ext4_image(filename, gzip, sparse, crc);
 
 	return 0;
 }
diff --git a/ext4_utils/make_ext4fs.h b/ext4_utils/make_ext4fs.h
index 8c6b259..78fcb07 100644
--- a/ext4_utils/make_ext4fs.h
+++ b/ext4_utils/make_ext4fs.h
@@ -22,6 +22,7 @@
 
 void reset_ext4fs_info();
 int make_ext4fs(const char *filename, const char *directory,
-                char *mountpoint, int android, int gzip, int sparse);
+                char *mountpoint, int android, int gzip, int sparse,
+                int crc);
 
 #endif
diff --git a/ext4_utils/make_ext4fs_main.c b/ext4_utils/make_ext4fs_main.c
index 66d7aac..2b59082 100644
--- a/ext4_utils/make_ext4fs_main.c
+++ b/ext4_utils/make_ext4fs_main.c
@@ -46,8 +46,9 @@
         int android = 0;
         int gzip = 0;
         int sparse = 0;
+        int crc = 0;
 
-        while ((opt = getopt(argc, argv, "l:j:b:g:i:I:L:a:fzJs")) != -1) {
+        while ((opt = getopt(argc, argv, "l:j:b:g:i:I:L:a:fzJsc")) != -1) {
                 switch (opt) {
                 case 'l':
                         info.len = parse_num(optarg);
@@ -83,6 +84,9 @@
 		case 'J':
 			info.no_journal = 1;
 			break;
+		case 'c':
+			crc = 1;
+			break;
                 case 's':
                         sparse = 1;
                         break;
@@ -115,5 +119,6 @@
                 exit(EXIT_FAILURE);
         }
 
-        return make_ext4fs(filename, directory, mountpoint, android, gzip, sparse);
+        return make_ext4fs(filename, directory, mountpoint, android, gzip,
+        		sparse, crc);
 }
diff --git a/ext4_utils/output_file.c b/ext4_utils/output_file.c
index d147b0b..66550d5 100644
--- a/ext4_utils/output_file.c
+++ b/ext4_utils/output_file.c
@@ -52,6 +52,7 @@
 	u32 chunk_cnt;
 	u32 crc32;
 	struct output_file_ops *ops;
+	int use_crc;
 };
 
 static int file_seek(struct output_file *out, off64_t off)
@@ -170,13 +171,6 @@
 	out->cur_out_ptr += skip_len;
 	out->chunk_cnt++;
 
-	/* Compute the CRC for all those zeroes.  Do it block_size bytes at a time. */
-	while (skip_len) {
-		chunk = (skip_len > info.block_size) ? info.block_size : skip_len;
-		out->crc32 = sparse_crc32(out->crc32, zero_buf, chunk);
-		skip_len -= chunk;
-	}
-
 	return 0;
 }
 
@@ -238,9 +232,12 @@
 			return -1;
 	}
 
-	out->crc32 = sparse_crc32(out->crc32, data, len);
-	if (zero_len)
-		out->crc32 = sparse_crc32(out->crc32, zero_buf, zero_len);
+	if (out->use_crc) {
+		out->crc32 = sparse_crc32(out->crc32, data, len);
+		if (zero_len)
+			out->crc32 = sparse_crc32(out->crc32, zero_buf, zero_len);
+	}
+
 	out->cur_out_ptr += rnd_up_len;
 	out->chunk_cnt++;
 
@@ -253,6 +250,18 @@
 	chunk_header_t chunk_header;
 
 	if (out->sparse) {
+		if (out->use_crc) {
+			chunk_header.chunk_type = CHUNK_TYPE_CRC32;
+			chunk_header.reserved1 = 0;
+			chunk_header.chunk_sz = 0;
+			chunk_header.total_sz = CHUNK_HEADER_LEN + 4;
+
+			out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header));
+			out->ops->write(out, (u8 *)&out->crc32, 4);
+
+			out->chunk_cnt++;
+		}
+
 		if (out->chunk_cnt != sparse_header.total_chunks)
 			error("sparse chunk count did not match: %d %d", out->chunk_cnt,
 					sparse_header.total_chunks);
@@ -261,7 +270,7 @@
 }
 
 struct output_file *open_output_file(const char *filename, int gz, int sparse,
-        int chunks)
+        int chunks, int crc)
 {
 	int ret;
 	struct output_file *out = malloc(sizeof(struct output_file));
@@ -303,14 +312,15 @@
 
 	/* Initialize the crc32 value */
 	out->crc32 = 0;
+	out->use_crc = crc;
 
 	if (out->sparse) {
-		/* Write out the file header.  We'll update the unknown fields
-		 * when we close the file.
-		 */
 		sparse_header.blk_sz = info.block_size,
 		sparse_header.total_blks = info.len / info.block_size,
 		sparse_header.total_chunks = chunks;
+		if (out->use_crc)
+			sparse_header.total_chunks++;
+
 		ret = out->ops->write(out, (u8 *)&sparse_header, sizeof(sparse_header));
 		if (ret < 0)
 			return NULL;
@@ -331,7 +341,6 @@
 	if (out->sparse) {
 		/* We need to emit a DONT_CARE chunk to pad out the file if the
 		 * cur_out_ptr is not already at the end of the filesystem.
-		 * We also need to compute the CRC for it.
 		 */
 		if (len < out->cur_out_ptr) {
 			error("attempted to pad file %llu bytes less than the current output pointer",
@@ -431,4 +440,3 @@
 	munmap(data, len);
 	close(file_fd);
 }
-
diff --git a/ext4_utils/output_file.h b/ext4_utils/output_file.h
index 0ac707c..c174cc3 100644
--- a/ext4_utils/output_file.h
+++ b/ext4_utils/output_file.h
@@ -20,7 +20,7 @@
 struct output_file;
 
 struct output_file *open_output_file(const char *filename, int gz, int sparse,
-        int chunks);
+        int chunks, int crc);
 void write_data_block(struct output_file *out, u64 off, u8 *data, int len);
 void write_data_file(struct output_file *out, u64 off, const char *file,
 		     off64_t offset, int len);
diff --git a/ext4_utils/simg2img.c b/ext4_utils/simg2img.c
index 9ef2509..38b6191 100644
--- a/ext4_utils/simg2img.c
+++ b/ext4_utils/simg2img.c
@@ -76,30 +76,38 @@
 	 * as a 32 bit value of blocks.
 	 */
 	u64 len = (u64)blocks * blk_sz;
-	u64 len_save;
 	u32 skip_chunk;
 
 	/* Fseek takes the offset as a long, which may be 32 bits on some systems.
 	 * So, lets do a sequence of fseeks() with SEEK_CUR to get the file pointer
 	 * where we want it.
 	 */
-	len_save = len;
 	while (len) {
 		skip_chunk = (len > 0x80000000) ? 0x80000000 : len;
 		fseek(out, skip_chunk, SEEK_CUR);
 		len -= skip_chunk;
 	}
-	/* And compute the CRC of the skipped region a chunk at a time */
-	len = len_save;
-	while (len) {
-		skip_chunk = (skip_chunk > blk_sz) ? blk_sz : skip_chunk;
-		*crc32 = sparse_crc32(*crc32, zerobuf, skip_chunk);
-		len -= skip_chunk;
-	}
 
 	return blocks;
 }
 
+int process_crc32_chunk(FILE *in, u32 crc32)
+{
+	u32 file_crc32;
+	if (fread(&file_crc32, 4, 1, in) != 1) {
+		fprintf(stderr, "fread returned an error copying a crc32 chunk\n");
+		exit(-1);
+	}
+
+	if (file_crc32 != crc32) {
+		fprintf(stderr, "computed crc32 of 0x%8.8x, expected 0x%8.8x\n",
+			 crc32, file_crc32);
+		exit(-1);
+	}
+
+	return 0;
+}
+
 int main(int argc, char *argv[])
 {
 	FILE *in, *out;
@@ -187,6 +195,9 @@
 			total_blocks += process_skip_chunk(out,
 					 chunk_header.chunk_sz, sparse_header.blk_sz, &crc32);
 			break;
+		    case CHUNK_TYPE_CRC32:
+			process_crc32_chunk(in, crc32);
+			break;
 		    default:
 			fprintf(stderr, "Unknown chunk type 0x%4.4x\n", chunk_header.chunk_type);
 			exit(-1);
@@ -213,12 +224,6 @@
 		exit(-1);
 	}
 
-	if (sparse_header.image_checksum != crc32) {
-		fprintf(stderr, "computed crc32 of 0x%8.8x, expected 0x%8.8x\n",
-			 crc32, sparse_header.image_checksum);
-		exit(-1);
-	}
-
 	exit(0);
 }
 
diff --git a/ext4_utils/sparse_format.h b/ext4_utils/sparse_format.h
index ba13214..6c62c34 100644
--- a/ext4_utils/sparse_format.h
+++ b/ext4_utils/sparse_format.h
@@ -33,6 +33,7 @@
 #define CHUNK_TYPE_RAW		0xCAC1
 #define CHUNK_TYPE_FILL		0xCAC2
 #define CHUNK_TYPE_DONT_CARE	0xCAC3
+#define CHUNK_TYPE_CRC32    0xCAC4
 
 typedef struct chunk_header {
   __le16	chunk_type;	/* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */
@@ -41,7 +42,9 @@
   __le32	total_sz;	/* in bytes of chunk input file including chunk header and data */
 } chunk_header_t;
 
-/* Following a Raw or Fill chunk is data.  For a Raw chunk, it's the data in chunk_sz * blk_sz.
+/* Following a Raw or Fill or CRC32 chunk is data.
+ *  For a Raw chunk, it's the data in chunk_sz * blk_sz.
  *  For a Fill chunk, it's 4 bytes of the fill data.
+ *  For a CRC32 chunk, it's 4 bytes of CRC32
  */