libsparse: add callback output file type

Add a new output file subclass that will call a callback for
each block as it is written.  Will be used to measure the space
used by each sparse block to allow resparsing files.

Also add sparse_file_callback, which will write out a sparse
file by calling the provided write function.

Change-Id: I18707bd9c357b68da319cc07982e93d1c2b2bee2
diff --git a/libsparse/sparse.c b/libsparse/sparse.c
index d778e1d..c560ec9 100644
--- a/libsparse/sparse.c
+++ b/libsparse/sparse.c
@@ -99,20 +99,33 @@
 	return chunks;
 }
 
-int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse,
-		bool crc)
+static void sparse_file_write_block(struct output_file *out,
+		struct backed_block *bb)
+{
+	switch (backed_block_type(bb)) {
+	case BACKED_BLOCK_DATA:
+		write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));
+		break;
+	case BACKED_BLOCK_FILE:
+		write_file_chunk(out, backed_block_len(bb),
+				backed_block_filename(bb), backed_block_file_offset(bb));
+		break;
+	case BACKED_BLOCK_FD:
+		write_fd_chunk(out, backed_block_len(bb),
+				backed_block_fd(bb), backed_block_file_offset(bb));
+		break;
+	case BACKED_BLOCK_FILL:
+		write_fill_chunk(out, backed_block_len(bb),
+				backed_block_fill_val(bb));
+		break;
+	}
+}
+
+static int write_all_blocks(struct sparse_file *s, struct output_file *out)
 {
 	struct backed_block *bb;
 	unsigned int last_block = 0;
 	int64_t pad;
-	int chunks;
-	struct output_file *out;
-
-	chunks = sparse_count_chunks(s);
-	out = open_output_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc);
-
-	if (!out)
-		return -ENOMEM;
 
 	for (bb = backed_block_iter_new(s->backed_block_list); bb;
 			bb = backed_block_iter_next(bb)) {
@@ -120,23 +133,7 @@
 			unsigned int blocks = backed_block_block(bb) - last_block;
 			write_skip_chunk(out, (int64_t)blocks * s->block_size);
 		}
-		switch (backed_block_type(bb)) {
-		case BACKED_BLOCK_DATA:
-			write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));
-			break;
-		case BACKED_BLOCK_FILE:
-			write_file_chunk(out, backed_block_len(bb),
-					backed_block_filename(bb), backed_block_file_offset(bb));
-			break;
-		case BACKED_BLOCK_FD:
-			write_fd_chunk(out, backed_block_len(bb),
-					backed_block_fd(bb), backed_block_file_offset(bb));
-			break;
-		case BACKED_BLOCK_FILL:
-			write_fill_chunk(out, backed_block_len(bb),
-					backed_block_fill_val(bb));
-			break;
-		}
+		sparse_file_write_block(out, bb);
 		last_block = backed_block_block(bb) +
 				DIV_ROUND_UP(backed_block_len(bb), s->block_size);
 	}
@@ -147,9 +144,48 @@
 		write_skip_chunk(out, pad);
 	}
 
+	return 0;
+}
+
+int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse,
+		bool crc)
+{
+	int ret;
+	int chunks;
+	struct output_file *out;
+
+	chunks = sparse_count_chunks(s);
+	out = open_output_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc);
+
+	if (!out)
+		return -ENOMEM;
+
+	ret = write_all_blocks(s, out);
+
 	close_output_file(out);
 
-	return 0;
+	return ret;
+}
+
+int sparse_file_callback(struct sparse_file *s, bool sparse, bool crc,
+		int (*write)(void *priv, const void *data, int len), void *priv)
+{
+	int ret;
+	int chunks;
+	struct output_file *out;
+
+	chunks = sparse_count_chunks(s);
+	out = open_output_callback(write, priv, s->block_size, s->len, false,
+			sparse, chunks, crc);
+
+	if (!out)
+		return -ENOMEM;
+
+	ret = write_all_blocks(s, out);
+
+	close_output_file(out);
+
+	return ret;
 }
 
 void sparse_file_verbose(struct sparse_file *s)