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)