summaryrefslogtreecommitdiffstats
path: root/libsparse
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2012-05-21 16:35:45 -0700
committerColin Cross <ccross@android.com>2012-07-09 22:09:37 -0700
commit1e17b313a6257b7b5081e178e81435c09d60378e (patch)
treeaea5379ca96c99ec14f1c5c8dc202b7b5b36ca47 /libsparse
parentb4cd267db30c152245e6308598e0066d87c5c55d (diff)
downloadsystem_core-1e17b313a6257b7b5081e178e81435c09d60378e.tar.gz
system_core-1e17b313a6257b7b5081e178e81435c09d60378e.tar.bz2
system_core-1e17b313a6257b7b5081e178e81435c09d60378e.zip
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
Diffstat (limited to 'libsparse')
-rw-r--r--libsparse/include/sparse/sparse.h21
-rw-r--r--libsparse/output_file.c87
-rw-r--r--libsparse/output_file.h3
-rw-r--r--libsparse/sparse.c92
4 files changed, 175 insertions, 28 deletions
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index ae5495599..b2152270b 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -158,6 +158,27 @@ int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse,
bool crc);
/**
+ * sparse_file_callback - call a callback for blocks in sparse file
+ *
+ * @s - sparse file cookie
+ * @sparse - write in the Android sparse file format
+ * @crc - append a crc chunk
+ * @write - function to call for each block
+ * @priv - value that will be passed as the first argument to write
+ *
+ * Writes a sparse file by calling a callback function. If sparse is true, the
+ * file will be written in the Android sparse file format. If crc is true, the
+ * crc of the expanded data will be calculated and appended in a crc chunk.
+ * The callback 'write' will be called with data and length for each data,
+ * and with data==NULL to skip over a region (only used for non-sparse format).
+ * The callback should return negative on error, 0 on success.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int sparse_file_callback(struct sparse_file *s, bool sparse, bool crc,
+ int (*write)(void *priv, const void *data, int len), void *priv);
+
+/**
* sparse_file_read - read a file into a sparse file cookie
*
* @s - sparse file cookie
diff --git a/libsparse/output_file.c b/libsparse/output_file.c
index 14b057a9e..dc56149e7 100644
--- a/libsparse/output_file.c
+++ b/libsparse/output_file.c
@@ -18,6 +18,7 @@
#define _LARGEFILE64_SOURCE 1
#include <fcntl.h>
+#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
@@ -112,6 +113,15 @@ struct output_file_normal {
#define to_output_file_normal(_o) \
container_of((_o), struct output_file_normal, out)
+struct output_file_callback {
+ struct output_file out;
+ void *priv;
+ int (*write)(void *priv, const void *buf, int len);
+};
+
+#define to_output_file_callback(_o) \
+ container_of((_o), struct output_file_callback, out)
+
static int file_open(struct output_file *out, int fd)
{
struct output_file_normal *outn = to_output_file_normal(out);
@@ -262,6 +272,57 @@ static struct output_file_ops gz_file_ops = {
.close = gz_file_close,
};
+static int callback_file_open(struct output_file *out, int fd)
+{
+ return 0;
+}
+
+static int callback_file_skip(struct output_file *out, int64_t off)
+{
+ struct output_file_callback *outc = to_output_file_callback(out);
+ int to_write;
+ int ret;
+
+ while (off > 0) {
+ to_write = min(off, (int64_t)INT_MAX);
+ ret = outc->write(outc->priv, NULL, to_write);
+ if (ret < 0) {
+ return ret;
+ }
+ off -= to_write;
+ }
+
+ return 0;
+}
+
+static int callback_file_pad(struct output_file *out, int64_t len)
+{
+ return -1;
+}
+
+static int callback_file_write(struct output_file *out, void *data, int len)
+{
+ int ret;
+ struct output_file_callback *outc = to_output_file_callback(out);
+
+ return outc->write(outc->priv, data, len);
+}
+
+static void callback_file_close(struct output_file *out)
+{
+ struct output_file_callback *outc = to_output_file_callback(out);
+
+ free(outc);
+}
+
+static struct output_file_ops callback_file_ops = {
+ .open = callback_file_open,
+ .skip = callback_file_skip,
+ .pad = callback_file_pad,
+ .write = callback_file_write,
+ .close = callback_file_close,
+};
+
int read_all(int fd, void *buf, size_t len)
{
size_t total = 0;
@@ -577,6 +638,32 @@ static struct output_file *output_file_new_normal(void)
return &outn->out;
}
+struct output_file *open_output_callback(int (*write)(void *, const void *, int),
+ void *priv, unsigned int block_size, int64_t len, int gz, int sparse,
+ int chunks, int crc)
+{
+ int ret;
+ struct output_file_callback *outc;
+
+ outc = calloc(1, sizeof(struct output_file_callback));
+ if (!outc) {
+ error_errno("malloc struct outc");
+ return NULL;
+ }
+
+ outc->out.ops = &callback_file_ops;
+ outc->priv = priv;
+ outc->write = write;
+
+ ret = output_file_init(&outc->out, block_size, len, sparse, chunks, crc);
+ if (ret < 0) {
+ free(outc);
+ return NULL;
+ }
+
+ return &outc->out;
+}
+
struct output_file *open_output_fd(int fd, unsigned int block_size, int64_t len,
int gz, int sparse, int chunks, int crc)
{
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
index 24496f7d4..7a9fa2434 100644
--- a/libsparse/output_file.h
+++ b/libsparse/output_file.h
@@ -23,6 +23,9 @@ struct output_file;
struct output_file *open_output_fd(int fd, unsigned int block_size, int64_t len,
int gz, int sparse, int chunks, int crc);
+struct output_file *open_output_callback(int (*write)(void *, const void *, int),
+ void *priv, unsigned int block_size, int64_t len, int gz, int sparse,
+ int chunks, int crc);
int write_data_chunk(struct output_file *out, unsigned int len, void *data);
int write_fill_chunk(struct output_file *out, unsigned int len,
uint32_t fill_val);
diff --git a/libsparse/sparse.c b/libsparse/sparse.c
index d778e1dc6..c560ec93c 100644
--- a/libsparse/sparse.c
+++ b/libsparse/sparse.c
@@ -99,20 +99,33 @@ unsigned int sparse_count_chunks(struct sparse_file *s)
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 @@ int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse,
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 @@ int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse,
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)