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
This commit is contained in:
parent
b4cd267db3
commit
1e17b313a6
|
@ -157,6 +157,27 @@ int sparse_file_add_fd(struct sparse_file *s,
|
|||
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
|
||||
*
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue