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

Commit b55dceea authored by Colin Cross's avatar Colin Cross
Browse files

libsparse: cleanups

Move block loops into sparse.c with iterator helpers in backed_block.c.
Simplify chunk writing by moving skip chunk calls from output_file.c to
sparse.c.
Rename variables to be consistent with new naming.
Remove use of u8, u32, u64.

Change-Id: Ic138ad58bef9f96239266ccee12ee83ea285e7eb
parent 411619e9
Loading
Loading
Loading
Loading
+137 −108
Original line number Diff line number Diff line
@@ -14,30 +14,87 @@
 * limitations under the License.
 */

#include <assert.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "backed_block.h"
#include "sparse_defs.h"

struct data_block {
	u32 block;
	u32 len;
struct backed_block {
	unsigned int block;
	unsigned int len;
	enum backed_block_type type;
	union {
		struct {
			void *data;
	const char *filename;
		} data;
		struct {
			char *filename;
			int64_t offset;
	struct data_block *next;
	u32 fill_val;
	u8 fill;
	u8 pad1;
	u16 pad2;
		} file;
		struct {
			uint32_t val;
		} fill;
	};
	struct backed_block *next;
};

struct backed_block_list {
	struct data_block *data_blocks;
	struct data_block *last_used;
	struct backed_block *data_blocks;
	struct backed_block *last_used;
};

struct backed_block *backed_block_iter_new(struct backed_block_list *bbl)
{
	return bbl->data_blocks;
}

struct backed_block *backed_block_iter_next(struct backed_block *bb)
{
	return bb->next;
}

unsigned int backed_block_len(struct backed_block *bb)
{
	return bb->len;
}

unsigned int backed_block_block(struct backed_block *bb)
{
	return bb->block;
}

void *backed_block_data(struct backed_block *bb)
{
	assert(bb->type == BACKED_BLOCK_DATA);
	return bb->data.data;
}

const char *backed_block_filename(struct backed_block *bb)
{
	assert(bb->type == BACKED_BLOCK_FILE);
	return bb->file.filename;
}

int64_t backed_block_file_offset(struct backed_block *bb)
{
	assert(bb->type == BACKED_BLOCK_FILE);
	return bb->file.offset;
}

uint32_t backed_block_fill_val(struct backed_block *bb)
{
	assert(bb->type == BACKED_BLOCK_FILL);
	return bb->fill.val;
}

enum backed_block_type backed_block_type(struct backed_block *bb)
{
	return bb->type;
}

struct backed_block_list *backed_block_list_new(void)
{
	struct backed_block_list *b = calloc(sizeof(struct backed_block_list), 1);
@@ -45,140 +102,112 @@ struct backed_block_list *backed_block_list_new(void)
	return b;
}

void backed_block_list_destroy(struct backed_block_list *b)
void backed_block_list_destroy(struct backed_block_list *bbl)
{
	if (b->data_blocks) {
		struct data_block *db = b->data_blocks;
		while (db) {
			struct data_block *next = db->next;
			free((void*)db->filename);
	if (bbl->data_blocks) {
		struct backed_block *bb = bbl->data_blocks;
		while (bb) {
			struct backed_block *next = bb->next;
			if (bb->type == BACKED_BLOCK_FILE) {
				free(bb->file.filename);
			}

			free(db);
			db = next;
			free(bb);
			bb = next;
		}
	}

	free(b);
	free(bbl);
}

static void queue_db(struct backed_block_list *b, struct data_block *new_db)
static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb)
{
	struct data_block *db;
	struct backed_block *bb;

	if (b->data_blocks == NULL) {
		b->data_blocks = new_db;
		return;
	if (bbl->data_blocks == NULL) {
		bbl->data_blocks = new_bb;
		return 0;
	}

	if (b->data_blocks->block > new_db->block) {
		new_db->next = b->data_blocks;
		b->data_blocks = new_db;
		return;
	if (bbl->data_blocks->block > new_bb->block) {
		new_bb->next = bbl->data_blocks;
		bbl->data_blocks = new_bb;
		return 0;
	}

	/* Optimization: blocks are mostly queued in sequence, so save the
	   pointer to the last db that was added, and start searching from
	   pointer to the last bb that was added, and start searching from
	   there if the next block number is higher */
	if (b->last_used && new_db->block > b->last_used->block)
		db = b->last_used;
	if (bbl->last_used && new_bb->block > bbl->last_used->block)
		bb = bbl->last_used;
	else
		db = b->data_blocks;
	b->last_used = new_db;
		bb = bbl->data_blocks;
	bbl->last_used = new_bb;

	for (; db->next && db->next->block < new_db->block; db = db->next)
	for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)
		;

	if (db->next == NULL) {
		db->next = new_db;
	if (bb->next == NULL) {
		bb->next = new_bb;
	} else {
		new_db->next = db->next;
		db->next = new_db;
		new_bb->next = bb->next;
		bb->next = new_bb;
	}

	return 0;
}

/* Queues a fill block of memory to be written to the specified data blocks */
void queue_fill_block(struct backed_block_list *b, unsigned int fill_val,
int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
		unsigned int len, unsigned int block)
{
	struct data_block *db = calloc(1, sizeof(struct data_block));
	if (db == NULL) {
		error_errno("malloc");
		return;
	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
	if (bb == NULL) {
		return -ENOMEM;
	}

	db->block = block;
	db->len = len;
	db->fill = 1;
	db->fill_val = fill_val;
	db->data = NULL;
	db->filename = NULL;
	db->next = NULL;
	bb->block = block;
	bb->len = len;
	bb->type = BACKED_BLOCK_FILL;
	bb->fill.val = fill_val;
	bb->next = NULL;

	queue_db(b, db);
	return queue_bb(bbl, bb);
}

/* Queues a block of memory to be written to the specified data blocks */
void queue_data_block(struct backed_block_list *b, void *data, unsigned int len,
		unsigned int block)
int backed_block_add_data(struct backed_block_list *bbl, void *data,
		unsigned int len, unsigned int block)
{
	struct data_block *db = malloc(sizeof(struct data_block));
	if (db == NULL) {
		error_errno("malloc");
		return;
	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
	if (bb == NULL) {
		return -ENOMEM;
	}

	db->block = block;
	db->len = len;
	db->data = data;
	db->filename = NULL;
	db->fill = 0;
	db->next = NULL;
	bb->block = block;
	bb->len = len;
	bb->type = BACKED_BLOCK_DATA;
	bb->data.data = data;
	bb->next = NULL;

	queue_db(b, db);
	return queue_bb(bbl, bb);
}

/* Queues a chunk of a file on disk to be written to the specified data blocks */
void queue_data_file(struct backed_block_list *b, const char *filename,
int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
		int64_t offset, unsigned int len, unsigned int block)
{
	struct data_block *db = malloc(sizeof(struct data_block));
	if (db == NULL) {
		error_errno("malloc");
		return;
	}

	db->block = block;
	db->len = len;
	db->filename = strdup(filename);
	db->offset = offset;
	db->data = NULL;
	db->fill = 0;
	db->next = NULL;

	queue_db(b, db);
}

/* Iterates over the queued data blocks, calling data_func for each contiguous
   data block, and file_func for each contiguous file block */
void for_each_data_block(struct backed_block_list *b,
	data_block_callback_t data_func,
	data_block_file_callback_t file_func,
	data_block_fill_callback_t fill_func,
	void *priv, unsigned int block_size)
{
	struct data_block *db;
	u32 last_block = 0;

	for (db = b->data_blocks; db; db = db->next) {
		if (db->block < last_block)
			error("data blocks out of order: %u < %u", db->block, last_block);
		last_block = db->block + DIV_ROUND_UP(db->len, block_size) - 1;

		if (db->filename)
			file_func(priv, (u64)db->block * block_size, db->filename, db->offset, db->len);
		else if (db->fill)
			fill_func(priv, (u64)db->block * block_size, db->fill_val, db->len);
		else
			data_func(priv, (u64)db->block * block_size, db->data, db->len);
	struct backed_block *bb = calloc(1, sizeof(struct backed_block));
	if (bb == NULL) {
		return -ENOMEM;
	}

	bb->block = block;
	bb->len = len;
	bb->type = BACKED_BLOCK_FILE;
	bb->file.filename = strdup(filename);
	bb->file.offset = offset;
	bb->next = NULL;

	return queue_bb(bbl, bb);
}
+27 −18
Original line number Diff line number Diff line
@@ -17,29 +17,38 @@
#ifndef _BACKED_BLOCK_H_
#define _BACKED_BLOCK_H_

#include <stdint.h>

struct backed_block_list;
struct backed_block;

enum backed_block_type {
	BACKED_BLOCK_DATA,
	BACKED_BLOCK_FILE,
	BACKED_BLOCK_FILL,
};

typedef void (*data_block_callback_t)(void *priv, int64_t off, void *data,
		int len);
typedef void (*data_block_fill_callback_t)(void *priv, int64_t off,
		unsigned int fill_val, int len);
typedef void (*data_block_file_callback_t)(void *priv, int64_t off,
		const char *file, int64_t offset, int len);

void for_each_data_block(struct backed_block_list *b,
	data_block_callback_t data_func,
	data_block_file_callback_t file_func,
	data_block_fill_callback_t fill_func,
	void *priv, unsigned int);

void queue_data_block(struct backed_block_list *b,void *data, unsigned int len,
		unsigned int block);
void queue_fill_block(struct backed_block_list *b,unsigned int fill_val,
int backed_block_add_data(struct backed_block_list *bbl, void *data,
		unsigned int len, unsigned int block);
int backed_block_add_fill(struct backed_block_list *bbl, unsigned int fill_val,
		unsigned int len, unsigned int block);
void queue_data_file(struct backed_block_list *b,const char *filename,
int backed_block_add_file(struct backed_block_list *bbl, const char *filename,
		int64_t offset, unsigned int len, unsigned int block);

struct backed_block *backed_block_iter_new(struct backed_block_list *bbl);
struct backed_block *backed_block_iter_next(struct backed_block *bb);
unsigned int backed_block_len(struct backed_block *bb);
unsigned int backed_block_block(struct backed_block *bb);
void *backed_block_data(struct backed_block *bb);
const char *backed_block_filename(struct backed_block *bb);
int64_t backed_block_file_offset(struct backed_block *bb);
uint32_t backed_block_fill_val(struct backed_block *bb);
enum backed_block_type backed_block_type(struct backed_block *bb);

struct backed_block *backed_block_iter_new(struct backed_block_list *bbl);
struct backed_block *backed_block_iter_next(struct backed_block *bb);

struct backed_block_list *backed_block_list_new(void);
void backed_block_list_destroy(struct backed_block_list *b);
void backed_block_list_destroy(struct backed_block_list *bbl);

#endif
+215 −263

File changed.

Preview size limit exceeded, changes collapsed.

+6 −5
Original line number Diff line number Diff line
@@ -26,11 +26,12 @@ struct output_file *open_output_file(const char *filename,
		int gz, int sparse, int chunks, int crc);
struct output_file *open_output_fd(int fd, unsigned int block_size, int64_t len,
		int gz, int sparse, int chunks, int crc);
void write_data_block(struct output_file *out, int64_t off, void *data, int len);
void write_fill_block(struct output_file *out, int64_t off, unsigned int fill_val, int len);
void write_data_file(struct output_file *out, int64_t off, const char *file,
		int64_t offset, int len);
void pad_output_file(struct output_file *out, int64_t len);
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);
int write_file_chunk(struct output_file *out, unsigned int len,
		const char *file, int64_t offset);
int write_skip_chunk(struct output_file *out, int64_t len);
void close_output_file(struct output_file *out);

#endif
+59 −78
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
 * limitations under the License.
 */

#include <assert.h>
#include <stdlib.h>

#include <sparse/sparse.h>
@@ -24,7 +25,6 @@
#include "backed_block.h"
#include "sparse_defs.h"


struct sparse_file *sparse_file_new(unsigned int block_size, int64_t len)
{
	struct sparse_file *s = calloc(sizeof(struct sparse_file), 1);
@@ -53,108 +53,89 @@ void sparse_file_destroy(struct sparse_file *s)
int sparse_file_add_data(struct sparse_file *s,
		void *data, unsigned int len, unsigned int block)
{
	queue_data_block(s->backed_block_list, data, len, block);

	return 0;
	return backed_block_add_data(s->backed_block_list, data, len, block);
}

int sparse_file_add_fill(struct sparse_file *s,
		uint32_t fill_val, unsigned int len, unsigned int block)
{
	queue_fill_block(s->backed_block_list, fill_val, len, block);

	return 0;
	return backed_block_add_fill(s->backed_block_list, fill_val, len, block);
}

int sparse_file_add_file(struct sparse_file *s,
		const char *filename, int64_t file_offset, unsigned int len,
		unsigned int block)
{
	queue_data_file(s->backed_block_list, filename, file_offset, len, block);

	return 0;
}

struct count_chunks {
	unsigned int chunks;
	int64_t cur_ptr;
	unsigned int block_size;
};

static void count_data_block(void *priv, int64_t off, void *data, int len)
{
	struct count_chunks *count_chunks = priv;
	if (off > count_chunks->cur_ptr)
		count_chunks->chunks++;
	count_chunks->cur_ptr = off + ALIGN(len, count_chunks->block_size);
	count_chunks->chunks++;
}

static void count_fill_block(void *priv, int64_t off, unsigned int fill_val, int len)
{
	struct count_chunks *count_chunks = priv;
	if (off > count_chunks->cur_ptr)
		count_chunks->chunks++;
	count_chunks->cur_ptr = off + ALIGN(len, count_chunks->block_size);
	count_chunks->chunks++;
}

static void count_file_block(void *priv, int64_t off, const char *file,
		int64_t offset, int len)
{
	struct count_chunks *count_chunks = priv;
	if (off > count_chunks->cur_ptr)
		count_chunks->chunks++;
	count_chunks->cur_ptr = off + ALIGN(len, count_chunks->block_size);
	count_chunks->chunks++;
	return backed_block_add_file(s->backed_block_list, filename, file_offset,
			len, block);
}

static int count_sparse_chunks(struct backed_block_list *b,
		unsigned int block_size, int64_t len)
unsigned int sparse_count_chunks(struct sparse_file *s)
{
	struct count_chunks count_chunks = {0, 0, block_size};

	for_each_data_block(b, count_data_block, count_file_block,
			count_fill_block, &count_chunks, block_size);

	if (count_chunks.cur_ptr != len)
		count_chunks.chunks++;

	return count_chunks.chunks;
	struct backed_block *bb;
	unsigned int last_block = 0;
	unsigned int chunks = 0;

	for (bb = backed_block_iter_new(s->backed_block_list); bb;
			bb = backed_block_iter_next(bb)) {
		if (backed_block_block(bb) > last_block) {
			/* If there is a gap between chunks, add a skip chunk */
			chunks++;
		}

static void ext4_write_data_block(void *priv, int64_t off, void *data, int len)
{
	write_data_block(priv, off, data, len);
		chunks++;
		last_block = backed_block_block(bb) +
				DIV_ROUND_UP(backed_block_len(bb), s->block_size);
	}

static void ext4_write_fill_block(void *priv, int64_t off, unsigned int fill_val, int len)
{
	write_fill_block(priv, off, fill_val, len);
	if (last_block < DIV_ROUND_UP(s->len, s->block_size)) {
		chunks++;
	}

static void ext4_write_data_file(void *priv, int64_t off, const char *file,
		int64_t offset, int len)
{
	write_data_file(priv, off, file, offset, len);
	return chunks;
}

int sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse,
		bool crc)
{
	int chunks = count_sparse_chunks(s->backed_block_list, s->block_size,
			s->len);
	struct output_file *out = open_output_fd(fd, s->block_size, s->len,
			gz, sparse, chunks, crc);
	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_each_data_block(s->backed_block_list, ext4_write_data_block,
			ext4_write_data_file, ext4_write_fill_block, out, s->block_size);
	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));
			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_FILL:
			write_fill_chunk(out, backed_block_len(bb),
					backed_block_fill_val(bb));
			break;
		}
		last_block = backed_block_block(bb) +
				DIV_ROUND_UP(backed_block_len(bb), s->block_size);
	}

	if (s->len)
		pad_output_file(out, s->len);
	pad = s->len - last_block * s->block_size;
	assert(pad >= 0);
	if (pad > 0) {
		write_skip_chunk(out, pad);
	}

	close_output_file(out);

Loading