Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1e17b313 authored by Colin Cross's avatar Colin Cross
Browse files

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
parent b4cd267d
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -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
 *
+87 −0
Original line number Diff line number Diff line
@@ -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)
{
+3 −0
Original line number Diff line number Diff line
@@ -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);
+64 −28
Original line number Diff line number Diff line
@@ -99,27 +99,9 @@ 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)
{
	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)) {
		if (backed_block_block(bb) > last_block) {
			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));
@@ -137,6 +119,21 @@ int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse,
				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;

	for (bb = backed_block_iter_new(s->backed_block_list); bb;
			bb = backed_block_iter_next(bb)) {
		if (backed_block_block(bb) > last_block) {
			unsigned int blocks = backed_block_block(bb) - last_block;
			write_skip_chunk(out, (int64_t)blocks * s->block_size);
		}
		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 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 0;
	return ret;
}

void sparse_file_verbose(struct sparse_file *s)