add option to make_ext4fs to record blocks for each file

Add the -B option to make_ext4fs, which causes it to record the block
ranges used for each (regular) file in the image.  We can use this
information to construct more efficient block-based incremental OTAs.

Bug: 16984795
Change-Id: I2a5325bb89948f63c76e93fdfa84c51f9b050104
diff --git a/ext4_utils/allocate.c b/ext4_utils/allocate.c
index 6397270..cca3dc1 100644
--- a/ext4_utils/allocate.c
+++ b/ext4_utils/allocate.c
@@ -22,18 +22,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-struct region_list {
-	struct region *first;
-	struct region *last;
-	struct region *iter;
-	u32 partial_iter;
-};
-
-struct block_allocation {
-	struct region_list list;
-	struct region_list oob_list;
-};
-
 struct region {
 	u32 block;
 	u32 len;
@@ -76,6 +64,8 @@
 	alloc->list.partial_iter = 0;
 	alloc->oob_list.iter = NULL;
 	alloc->oob_list.partial_iter = 0;
+	alloc->filename = NULL;
+	alloc->next = NULL;
 	return alloc;
 }
 
@@ -137,9 +127,7 @@
 {
 	for (; reg; reg = reg->next) {
 		printf("%p: Blocks %d-%d (%d)\n", reg,
-				reg->bg * info.blocks_per_group + reg->block,
-				reg->bg * info.blocks_per_group + reg->block + reg->len - 1,
-				reg->len);
+			   reg->block, reg->block + reg->len - 1, reg->len)
 	}
 }
 
@@ -153,6 +141,19 @@
 }
 #endif
 
+void print_blocks(FILE* f, struct block_allocation *alloc)
+{
+	struct region *reg;
+	for (reg = alloc->list.first; reg; reg = reg->next) {
+		if (reg->len == 1) {
+			fprintf(f, " %d", reg->block);
+		} else {
+			fprintf(f, " %d-%d", reg->block, reg->block + reg->len - 1);
+		}
+	}
+	fputc('\n', f);
+}
+
 void append_region(struct block_allocation *alloc,
 		u32 block, u32 len, int bg_num)
 {
diff --git a/ext4_utils/allocate.h b/ext4_utils/allocate.h
index a0999e4..5c26792 100644
--- a/ext4_utils/allocate.h
+++ b/ext4_utils/allocate.h
@@ -21,7 +21,22 @@
 
 #include "ext4_utils.h"
 
-struct block_allocation;
+struct region;
+
+struct region_list {
+	struct region *first;
+	struct region *last;
+	struct region *iter;
+	u32 partial_iter;
+};
+
+struct block_allocation {
+	struct region_list list;
+	struct region_list oob_list;
+	char* filename;
+	struct block_allocation* next;
+};
+
 
 void block_allocator_init();
 void block_allocator_free();
@@ -54,5 +69,6 @@
 	u32 block, u32 len, int bg);
 struct block_allocation *create_allocation();
 int append_oob_allocation(struct block_allocation *alloc, u32 len);
+void print_blocks(FILE* f, struct block_allocation *alloc);
 
 #endif
diff --git a/ext4_utils/contents.c b/ext4_utils/contents.c
index 4aa287f..fb3a38d 100644
--- a/ext4_utils/contents.c
+++ b/ext4_utils/contents.c
@@ -38,6 +38,12 @@
 #define S_IFLNK 0  /* used by make_link, not needed under mingw */
 #endif
 
+static struct block_allocation* saved_allocation_head = NULL;
+
+struct block_allocation* get_saved_allocation_chain() {
+	return saved_allocation_head;
+}
+
 static u32 dentry_size(u32 entries, struct dentry *dentries)
 {
 	u32 len = 24;
@@ -186,8 +192,13 @@
 		return EXT4_ALLOCATE_FAILED;
 	}
 
-	if (len > 0)
-		inode_allocate_file_extents(inode, len, filename);
+	if (len > 0) {
+		struct block_allocation* alloc = inode_allocate_file_extents(inode, len, filename);
+
+		alloc->filename = strdup(filename);
+		alloc->next = saved_allocation_head;
+		saved_allocation_head = alloc;
+	}
 
 	inode->i_mode = S_IFREG;
 	inode->i_links_count = 1;
@@ -476,4 +487,3 @@
 	return xattr_add(inode_num, EXT4_XATTR_INDEX_SECURITY,
 		XATTR_CAPS_SUFFIX, &cap_data, sizeof(cap_data));
 }
-
diff --git a/ext4_utils/contents.h b/ext4_utils/contents.h
index 4272000..e57687e 100644
--- a/ext4_utils/contents.h
+++ b/ext4_utils/contents.h
@@ -40,4 +40,6 @@
 int inode_set_permissions(u32 inode_num, u16 mode, u16 uid, u16 gid, u32 mtime);
 int inode_set_selinux(u32 inode_num, const char *secon);
 int inode_set_capabilities(u32 inode_num, uint64_t capabilities);
+struct block_allocation* get_saved_allocation_chain();
+
 #endif
diff --git a/ext4_utils/ext4_utils.h b/ext4_utils/ext4_utils.h
index 190fdb3..499753f 100644
--- a/ext4_utils/ext4_utils.h
+++ b/ext4_utils/ext4_utils.h
@@ -158,7 +158,8 @@
 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,
-						 struct selabel_handle *sehnd, int verbose, time_t fixed_time);
+						 struct selabel_handle *sehnd, int verbose, time_t fixed_time,
+						 FILE* block_list_file);
 
 int read_ext(int fd, int verbose);
 
diff --git a/ext4_utils/extent.c b/ext4_utils/extent.c
index abb30ce..7142c8b 100644
--- a/ext4_utils/extent.c
+++ b/ext4_utils/extent.c
@@ -202,7 +202,7 @@
 
 /* Allocates enough blocks to hold len bytes, queues them to be written
    from a file, and connects them to an inode. */
-void inode_allocate_file_extents(struct ext4_inode *inode, u64 len,
+struct block_allocation* inode_allocate_file_extents(struct ext4_inode *inode, u64 len,
 	const char *filename)
 {
 	struct block_allocation *alloc;
@@ -214,8 +214,7 @@
 	}
 
 	extent_create_backing_file(alloc, len, filename);
-
-	free_alloc(alloc);
+    return alloc;
 }
 
 /* Allocates enough blocks to hold len bytes and connects them to an inode */
diff --git a/ext4_utils/extent.h b/ext4_utils/extent.h
index a1ddeb1..a78a7b0 100644
--- a/ext4_utils/extent.h
+++ b/ext4_utils/extent.h
@@ -21,8 +21,8 @@
 #include "ext4_utils.h"
 
 void inode_allocate_extents(struct ext4_inode *inode, u64 len);
-void inode_allocate_file_extents(struct ext4_inode *inode, u64 len,
-	const char *filename);
+struct block_allocation* inode_allocate_file_extents(
+	struct ext4_inode *inode, u64 len, const char *filename);
 u8 *inode_allocate_data_extents(struct ext4_inode *inode, u64 len,
 	u64 backing_len);
 void free_extent_blocks();
diff --git a/ext4_utils/make_ext4fs.c b/ext4_utils/make_ext4fs.c
index a490415..2f89ae8 100644
--- a/ext4_utils/make_ext4fs.c
+++ b/ext4_utils/make_ext4fs.c
@@ -396,7 +396,7 @@
 	reset_ext4fs_info();
 	info.len = len;
 
-	return make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 1, 0, 0, sehnd, 0, -1);
+	return make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 1, 0, 0, sehnd, 0, -1, NULL);
 }
 
 int make_ext4fs(const char *filename, long long len,
@@ -414,7 +414,7 @@
 		return EXIT_FAILURE;
 	}
 
-	status = make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 0, 0, 1, sehnd, 0, -1);
+	status = make_ext4fs_internal(fd, NULL, mountpoint, NULL, 0, 0, 0, 1, sehnd, 0, -1, NULL);
 	close(fd);
 
 	return status;
@@ -483,7 +483,8 @@
 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,
-						 struct selabel_handle *sehnd, int verbose, time_t fixed_time)
+						 struct selabel_handle *sehnd, int verbose, time_t fixed_time,
+						 FILE* block_list_file)
 {
 	u32 root_inode_num;
 	u16 root_mode;
@@ -592,7 +593,7 @@
 #else
 	if (directory)
 		root_inode_num = build_directory_structure(directory, mountpoint, 0,
-				fs_config_func, sehnd, verbose, fixed_time);
+			fs_config_func, sehnd, verbose, fixed_time);
 	else
 		root_inode_num = build_default_directory_structure(mountpoint, sehnd);
 #endif
@@ -621,6 +622,23 @@
 
 	ext4_queue_sb();
 
+	if (block_list_file) {
+		size_t dirlen = directory ? strlen(directory) : 0;
+		struct block_allocation* p = get_saved_allocation_chain();
+		while (p) {
+			if (directory && strncmp(p->filename, directory, dirlen) == 0) {
+				// substitute mountpoint for the leading directory in the filename, in the output file
+				fprintf(block_list_file, "%s%s", mountpoint, p->filename + dirlen);
+			} else {
+				fprintf(block_list_file, "%s", p->filename);
+			}
+			print_blocks(block_list_file, p);
+			struct block_allocation* pn = p->next;
+			free_alloc(p);
+			p = pn;
+		}
+	}
+
 	printf("Created filesystem with %d/%d inodes and %d/%d blocks\n",
 			aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count,
 			aux_info.sb->s_inodes_count,
diff --git a/ext4_utils/make_ext4fs_main.c b/ext4_utils/make_ext4fs_main.c
index 87eaecd..a6c5f61 100644
--- a/ext4_utils/make_ext4fs_main.c
+++ b/ext4_utils/make_ext4fs_main.c
@@ -54,7 +54,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 ] [ -C fs_config ] [ -T timestamp ]\n");
-	fprintf(stderr, "    [ -z | -s ] [ -w ] [ -c ] [ -J ] [ -v ]\n");
+	fprintf(stderr, "    [ -z | -s ] [ -w ] [ -c ] [ -J ] [ -v ] [ -B <block_list_file> ]\n");
 	fprintf(stderr, "    <filename> [<directory>]\n");
 }
 
@@ -75,11 +75,12 @@
 	int verbose = 0;
 	time_t fixed_time = -1;
 	struct selabel_handle *sehnd = NULL;
+	FILE* block_list_file = NULL;
 #ifndef USE_MINGW
 	struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, "" } };
 #endif
 
-	while ((opt = getopt(argc, argv, "l:j:b:g:i:I:L:a:S:T:C:fwzJsctv")) != -1) {
+	while ((opt = getopt(argc, argv, "l:j:b:g:i:I:L:a:S:T:C:B:fwzJsctv")) != -1) {
 		switch (opt) {
 		case 'l':
 			info.len = parse_num(optarg);
@@ -151,6 +152,13 @@
 		case 'C':
 			fs_config_file = optarg;
 			break;
+		case 'B':
+			block_list_file = fopen(optarg, "w");
+			if (block_list_file == NULL) {
+				fprintf(stderr, "failed to open block_list_file: %s\n", strerror(errno));
+				exit(EXIT_FAILURE);
+			}
+			break;
 		default: /* '?' */
 			usage(argv[0]);
 			exit(EXIT_FAILURE);
@@ -219,8 +227,10 @@
 	}
 
 	exitcode = make_ext4fs_internal(fd, directory, mountpoint, fs_config_func, gzip,
-			sparse, crc, wipe, sehnd, verbose, fixed_time);
+		sparse, crc, wipe, sehnd, verbose, fixed_time, block_list_file);
 	close(fd);
+	if (block_list_file)
+		fclose(block_list_file);
 	if (exitcode && strcmp(filename, "-"))
 		unlink(filename);
 	return exitcode;
diff --git a/ext4_utils/mkuserimg.sh b/ext4_utils/mkuserimg.sh
index d32eadb..c8b83e0 100755
--- a/ext4_utils/mkuserimg.sh
+++ b/ext4_utils/mkuserimg.sh
@@ -5,7 +5,8 @@
 function usage() {
 cat<<EOT
 Usage:
-mkuserimg.sh [-s] SRC_DIR OUTPUT_FILE EXT_VARIANT MOUNT_POINT SIZE [-T TIMESTAMP] [-C FS_CONFIG] [FILE_CONTEXTS]
+mkuserimg.sh [-s] SRC_DIR OUTPUT_FILE EXT_VARIANT MOUNT_POINT SIZE
+             [-T TIMESTAMP] [-C FS_CONFIG] [-B BLOCK_LIST_FILE] [FILE_CONTEXTS]
 EOT
 }
 
@@ -44,6 +45,12 @@
   shift; shift
 fi
 
+BLOCK_LIST=
+if [[ "$1" == "-B" ]]; then
+  BLOCK_LIST=$2
+  shift; shift
+fi
+
 FC=$1
 
 case $EXT_VARIANT in
@@ -68,6 +75,9 @@
 if [ -n "$FS_CONFIG" ]; then
   OPT="$OPT -C $FS_CONFIG"
 fi
+if [ -n "$BLOCK_LIST" ]; then
+  OPT="$OPT -B $BLOCK_LIST"
+fi
 
 MAKE_EXT4FS_CMD="make_ext4fs $ENABLE_SPARSE_IMAGE -T $TIMESTAMP $OPT -l $SIZE -a $MOUNT_POINT $OUTPUT_FILE $SRC_DIR"
 echo $MAKE_EXT4FS_CMD