diff options
author | Colin Cross <ccross@android.com> | 2012-04-24 23:07:49 -0700 |
---|---|---|
committer | Colin Cross <ccross@android.com> | 2012-07-09 22:09:37 -0700 |
commit | b55dceea986ab24f8b836b5116b389ed619c816e (patch) | |
tree | aa4f4ed6384931ec362c70eca7b25a7cb8dab450 /libsparse | |
parent | 411619e921904b896eddae81c086c1f687c8304d (diff) | |
download | system_core-b55dceea986ab24f8b836b5116b389ed619c816e.tar.gz system_core-b55dceea986ab24f8b836b5116b389ed619c816e.tar.bz2 system_core-b55dceea986ab24f8b836b5116b389ed619c816e.zip |
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
Diffstat (limited to 'libsparse')
-rw-r--r-- | libsparse/backed_block.c | 245 | ||||
-rw-r--r-- | libsparse/backed_block.h | 45 | ||||
-rw-r--r-- | libsparse/output_file.c | 478 | ||||
-rw-r--r-- | libsparse/output_file.h | 11 | ||||
-rw-r--r-- | libsparse/sparse.c | 137 | ||||
-rw-r--r-- | libsparse/sparse_crc32.h | 4 |
6 files changed, 447 insertions, 473 deletions
diff --git a/libsparse/backed_block.c b/libsparse/backed_block.c index 6e8ef4424..b25919008 100644 --- a/libsparse/backed_block.c +++ b/libsparse/backed_block.c @@ -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; - void *data; - const char *filename; - int64_t offset; - struct data_block *next; - u32 fill_val; - u8 fill; - u8 pad1; - u16 pad2; + +struct backed_block { + unsigned int block; + unsigned int len; + enum backed_block_type type; + union { + struct { + void *data; + } data; + struct { + char *filename; + int64_t offset; + } 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); - - free(db); - db = next; + 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(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; + struct backed_block *bb = calloc(1, sizeof(struct backed_block)); + if (bb == NULL) { + return -ENOMEM; } - db->block = block; - db->len = len; - db->filename = strdup(filename); - db->offset = offset; - db->data = NULL; - db->fill = 0; - db->next = NULL; + bb->block = block; + bb->len = len; + bb->type = BACKED_BLOCK_FILE; + bb->file.filename = strdup(filename); + bb->file.offset = offset; + bb->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); - } + return queue_bb(bbl, bb); } diff --git a/libsparse/backed_block.h b/libsparse/backed_block.h index d1bfa1ec3..316650505 100644 --- a/libsparse/backed_block.h +++ b/libsparse/backed_block.h @@ -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 diff --git a/libsparse/output_file.c b/libsparse/output_file.c index 2c4b5573e..f911f8cc5 100644 --- a/libsparse/output_file.c +++ b/libsparse/output_file.c @@ -51,36 +51,50 @@ static inline void *mmap64(void *addr, size_t length, int prot, int flags, } #endif +#define min(a, b) \ + ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; }) + #define SPARSE_HEADER_MAJOR_VER 1 #define SPARSE_HEADER_MINOR_VER 0 #define SPARSE_HEADER_LEN (sizeof(sparse_header_t)) #define CHUNK_HEADER_LEN (sizeof(chunk_header_t)) struct output_file_ops { - int (*seek)(struct output_file *, int64_t); - int (*write)(struct output_file *, u8 *, int); + int (*skip)(struct output_file *, int64_t); + int (*write)(struct output_file *, void *, int); void (*close)(struct output_file *); }; +struct sparse_file_ops { + 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_skip_chunk)(struct output_file *out, int64_t len); + int (*write_end_chunk)(struct output_file *out); +}; + struct output_file { int fd; gzFile gz_fd; bool close_fd; - int sparse; int64_t cur_out_ptr; - u32 chunk_cnt; - u32 crc32; + unsigned int chunk_cnt; + uint32_t crc32; struct output_file_ops *ops; + struct sparse_file_ops *sparse_ops; int use_crc; unsigned int block_size; int64_t len; + char *zero_buf; + uint32_t *fill_buf; }; -static int file_seek(struct output_file *out, int64_t off) +static int file_skip(struct output_file *out, int64_t cnt) { off64_t ret; - ret = lseek64(out->fd, off, SEEK_SET); + ret = lseek64(out->fd, cnt, SEEK_CUR); if (ret < 0) { error_errno("lseek64"); return -1; @@ -88,7 +102,7 @@ static int file_seek(struct output_file *out, int64_t off) return 0; } -static int file_write(struct output_file *out, u8 *data, int len) +static int file_write(struct output_file *out, void *data, int len) { int ret; ret = write(out->fd, data, len); @@ -110,18 +124,17 @@ static void file_close(struct output_file *out) } } - static struct output_file_ops file_ops = { - .seek = file_seek, + .skip = file_skip, .write = file_write, .close = file_close, }; -static int gz_file_seek(struct output_file *out, int64_t off) +static int gz_file_skip(struct output_file *out, int64_t cnt) { off64_t ret; - ret = gzseek(out->gz_fd, off, SEEK_SET); + ret = gzseek(out->gz_fd, cnt, SEEK_CUR); if (ret < 0) { error_errno("gzseek"); return -1; @@ -129,7 +142,7 @@ static int gz_file_seek(struct output_file *out, int64_t off) return 0; } -static int gz_file_write(struct output_file *out, u8 *data, int len) +static int gz_file_write(struct output_file *out, void *data, int len) { int ret; ret = gzwrite(out->gz_fd, data, len); @@ -150,32 +163,16 @@ static void gz_file_close(struct output_file *out) } static struct output_file_ops gz_file_ops = { - .seek = gz_file_seek, + .skip = gz_file_skip, .write = gz_file_write, .close = gz_file_close, }; -static sparse_header_t sparse_header = { - .magic = SPARSE_HEADER_MAGIC, - .major_version = SPARSE_HEADER_MAJOR_VER, - .minor_version = SPARSE_HEADER_MINOR_VER, - .file_hdr_sz = SPARSE_HEADER_LEN, - .chunk_hdr_sz = CHUNK_HEADER_LEN, - .blk_sz = 0, - .total_blks = 0, - .total_chunks = 0, - .image_checksum = 0 -}; - -static u8 *zero_buf; - -static int emit_skip_chunk(struct output_file *out, u64 skip_len) +static int write_sparse_skip_chunk(struct output_file *out, int64_t skip_len) { chunk_header_t chunk_header; int ret, chunk; - //DBG printf("skip chunk: 0x%llx bytes\n", skip_len); - if (skip_len % out->block_size) { error("don't care size %llu is not a multiple of the block size %u", skip_len, out->block_size); @@ -187,7 +184,7 @@ static int emit_skip_chunk(struct output_file *out, u64 skip_len) chunk_header.reserved1 = 0; chunk_header.chunk_sz = skip_len / out->block_size; chunk_header.total_sz = CHUNK_HEADER_LEN; - ret = out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header)); + ret = out->ops->write(out, &chunk_header, sizeof(chunk_header)); if (ret < 0) return -1; @@ -197,71 +194,34 @@ static int emit_skip_chunk(struct output_file *out, u64 skip_len) return 0; } -static int write_chunk_fill(struct output_file *out, int64_t off, u32 fill_val, int len) +static int write_sparse_fill_chunk(struct output_file *out, unsigned int len, + uint32_t fill_val) { chunk_header_t chunk_header; int rnd_up_len, zero_len, count; int ret; unsigned int i; - u32 fill_buf[4096/sizeof(u32)]; /* Maximum size of a block */ - - /* We can assume that all the chunks to be written are in - * ascending order, block-size aligned, and non-overlapping. - * So, if the offset is less than the current output pointer, - * throw an error, and if there is a gap, emit a "don't care" - * chunk. The first write (of the super block) may not be - * blocksize aligned, so we need to deal with that too. - */ - //DBG printf("write chunk: offset 0x%llx, length 0x%x bytes\n", off, len); - - if (off < out->cur_out_ptr) { - error("offset %llu is less than the current output offset %llu", - off, out->cur_out_ptr); - return -1; - } - if (off > out->cur_out_ptr) { - emit_skip_chunk(out, off - out->cur_out_ptr); - } - - if (off % out->block_size) { - error("write chunk offset %llu is not a multiple of the block size %u", - off, out->block_size); - return -1; - } - - if (off != out->cur_out_ptr) { - error("internal error, offset accounting screwy in write_chunk_raw()"); - return -1; - } - - /* Round up the file length to a multiple of the block size */ - rnd_up_len = (len + (out->block_size - 1)) & (~(out->block_size -1)); + /* Round up the fill length to a multiple of the block size */ + rnd_up_len = ALIGN(len, out->block_size); /* Finally we can safely emit a chunk of data */ chunk_header.chunk_type = CHUNK_TYPE_FILL; chunk_header.reserved1 = 0; chunk_header.chunk_sz = rnd_up_len / out->block_size; chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val); - ret = out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header)); + ret = out->ops->write(out, &chunk_header, sizeof(chunk_header)); if (ret < 0) return -1; - ret = out->ops->write(out, (u8 *)&fill_val, sizeof(fill_val)); + ret = out->ops->write(out, &fill_val, sizeof(fill_val)); if (ret < 0) return -1; if (out->use_crc) { - /* Initialize fill_buf with the fill_val */ - for (i = 0; i < (out->block_size / sizeof(u32)); i++) { - fill_buf[i] = fill_val; - } - - count = chunk_header.chunk_sz; - while (count) { - out->crc32 = sparse_crc32(out->crc32, fill_buf, out->block_size); - count--; - } + count = out->block_size / sizeof(uint32_t); + while (count--) + out->crc32 = sparse_crc32(out->crc32, &fill_val, sizeof(uint32_t)); } out->cur_out_ptr += rnd_up_len; @@ -270,44 +230,15 @@ static int write_chunk_fill(struct output_file *out, int64_t off, u32 fill_val, return 0; } -static int write_chunk_raw(struct output_file *out, int64_t off, u8 *data, int len) +static int write_sparse_data_chunk(struct output_file *out, unsigned int len, + void *data) { chunk_header_t chunk_header; int rnd_up_len, zero_len; int ret; - /* We can assume that all the chunks to be written are in - * ascending order, block-size aligned, and non-overlapping. - * So, if the offset is less than the current output pointer, - * throw an error, and if there is a gap, emit a "don't care" - * chunk. The first write (of the super block) may not be - * blocksize aligned, so we need to deal with that too. - */ - //DBG printf("write chunk: offset 0x%llx, length 0x%x bytes\n", off, len); - - if (off < out->cur_out_ptr) { - error("offset %llu is less than the current output offset %llu", - off, out->cur_out_ptr); - return -1; - } - - if (off > out->cur_out_ptr) { - emit_skip_chunk(out, off - out->cur_out_ptr); - } - - if (off % out->block_size) { - error("write chunk offset %llu is not a multiple of the block size %u", - off, out->block_size); - return -1; - } - - if (off != out->cur_out_ptr) { - error("internal error, offset accounting screwy in write_chunk_raw()"); - return -1; - } - - /* Round up the file length to a multiple of the block size */ - rnd_up_len = (len + (out->block_size - 1)) & (~(out->block_size -1)); + /* Round up the data length to a multiple of the block size */ + rnd_up_len = ALIGN(len, out->block_size); zero_len = rnd_up_len - len; /* Finally we can safely emit a chunk of data */ @@ -315,7 +246,7 @@ static int write_chunk_raw(struct output_file *out, int64_t off, u8 *data, int l chunk_header.reserved1 = 0; chunk_header.chunk_sz = rnd_up_len / out->block_size; chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len; - ret = out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header)); + ret = out->ops->write(out, &chunk_header, sizeof(chunk_header)); if (ret < 0) return -1; @@ -323,7 +254,7 @@ static int write_chunk_raw(struct output_file *out, int64_t off, u8 *data, int l if (ret < 0) return -1; if (zero_len) { - ret = out->ops->write(out, zero_buf, zero_len); + ret = out->ops->write(out, out->zero_buf, zero_len); if (ret < 0) return -1; } @@ -331,7 +262,7 @@ static int write_chunk_raw(struct output_file *out, int64_t off, u8 *data, int l if (out->use_crc) { out->crc32 = sparse_crc32(out->crc32, data, len); if (zero_len) - out->crc32 = sparse_crc32(out->crc32, zero_buf, zero_len); + out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len); } out->cur_out_ptr += rnd_up_len; @@ -340,29 +271,115 @@ static int write_chunk_raw(struct output_file *out, int64_t off, u8 *data, int l return 0; } -void close_output_file(struct output_file *out) +int write_sparse_end_chunk(struct output_file *out) { - int ret; chunk_header_t chunk_header; + int ret; - if (out->sparse) { - if (out->use_crc) { - chunk_header.chunk_type = CHUNK_TYPE_CRC32; - chunk_header.reserved1 = 0; - chunk_header.chunk_sz = 0; - chunk_header.total_sz = CHUNK_HEADER_LEN + 4; + if (out->use_crc) { + chunk_header.chunk_type = CHUNK_TYPE_CRC32; + chunk_header.reserved1 = 0; + chunk_header.chunk_sz = 0; + chunk_header.total_sz = CHUNK_HEADER_LEN + 4; + + ret = out->ops->write(out, &chunk_header, sizeof(chunk_header)); + if (ret < 0) { + return ret; + } + out->ops->write(out, &out->crc32, 4); + if (ret < 0) { + return ret; + } + + out->chunk_cnt++; + } + + return 0; +} + +static struct sparse_file_ops sparse_file_ops = { + .write_data_chunk = write_sparse_data_chunk, + .write_fill_chunk = write_sparse_fill_chunk, + .write_skip_chunk = write_sparse_skip_chunk, + .write_end_chunk = write_sparse_end_chunk, +}; + +static int write_normal_data_chunk(struct output_file *out, unsigned int len, + void *data) +{ + int ret; + unsigned int rnd_up_len = ALIGN(len, out->block_size); + + ret = out->ops->write(out, data, len); + if (ret < 0) { + return ret; + } + + if (rnd_up_len > len) { + ret = out->ops->skip(out, rnd_up_len - len); + } - out->ops->write(out, (u8 *)&chunk_header, sizeof(chunk_header)); - out->ops->write(out, (u8 *)&out->crc32, 4); + return ret; +} + +static int write_normal_fill_chunk(struct output_file *out, unsigned int len, + uint32_t fill_val) +{ + int ret; + unsigned int i; + unsigned int write_len; - out->chunk_cnt++; + /* Initialize fill_buf with the fill_val */ + for (i = 0; i < out->block_size / sizeof(uint32_t); i++) { + out->fill_buf[i] = fill_val; + } + + while (len) { + write_len = min(len, out->block_size); + ret = out->ops->write(out, out->fill_buf, write_len); + if (ret < 0) { + return ret; } - if (out->chunk_cnt != sparse_header.total_chunks) - error("sparse chunk count did not match: %d %d", out->chunk_cnt, - sparse_header.total_chunks); + len -= write_len; + } + + return 0; +} + +static int write_normal_skip_chunk(struct output_file *out, int64_t len) +{ + int ret; + + return out->ops->skip(out, len); +} + +int write_normal_end_chunk(struct output_file *out) +{ + int ret; + + ret = ftruncate64(out->fd, out->len); + if (ret < 0) { + return -errno; } + + return 0; +} + +static struct sparse_file_ops normal_file_ops = { + .write_data_chunk = write_normal_data_chunk, + .write_fill_chunk = write_normal_fill_chunk, + .write_skip_chunk = write_normal_skip_chunk, + .write_end_chunk = write_normal_end_chunk, +}; + +void close_output_file(struct output_file *out) +{ + int ret; + + out->sparse_ops->write_end_chunk(out); out->ops->close(out); + free(out); } struct output_file *open_output_fd(int fd, unsigned int block_size, int64_t len, @@ -374,28 +391,37 @@ struct output_file *open_output_fd(int fd, unsigned int block_size, int64_t len, error_errno("malloc struct out"); return NULL; } - zero_buf = malloc(out->block_size); - if (!zero_buf) { + out->zero_buf = calloc(block_size, 1); + if (!out->zero_buf) { error_errno("malloc zero_buf"); - free(out); - return NULL; + goto err_zero_buf; + } + + out->fill_buf = calloc(block_size, 1); + if (!out->fill_buf) { + error_errno("malloc fill_buf"); + goto err_fill_buf; } - memset(zero_buf, '\0', out->block_size); if (gz) { out->ops = &gz_file_ops; out->gz_fd = gzdopen(fd, "wb9"); if (!out->gz_fd) { error_errno("gzopen"); - free(out); - return NULL; + goto err_gzopen; } } else { out->fd = fd; out->ops = &file_ops; } + + if (sparse) { + out->sparse_ops = &sparse_file_ops; + } else { + out->sparse_ops = &normal_file_ops; + } + out->close_fd = false; - out->sparse = sparse; out->cur_out_ptr = 0ll; out->chunk_cnt = 0; @@ -406,19 +432,42 @@ struct output_file *open_output_fd(int fd, unsigned int block_size, int64_t len, out->len = len; out->block_size = block_size; - if (out->sparse) { - sparse_header.blk_sz = out->block_size, - sparse_header.total_blks = out->len / out->block_size, - sparse_header.total_chunks = chunks; - if (out->use_crc) + if (sparse) { + sparse_header_t sparse_header = { + .magic = SPARSE_HEADER_MAGIC, + .major_version = SPARSE_HEADER_MAJOR_VER, + .minor_version = SPARSE_HEADER_MINOR_VER, + .file_hdr_sz = SPARSE_HEADER_LEN, + .chunk_hdr_sz = CHUNK_HEADER_LEN, + .blk_sz = out->block_size, + .total_blks = out->len / out->block_size, + .total_chunks = chunks, + .image_checksum = 0 + }; + + if (out->use_crc) { sparse_header.total_chunks++; + } - ret = out->ops->write(out, (u8 *)&sparse_header, sizeof(sparse_header)); - if (ret < 0) - return NULL; + ret = out->ops->write(out, &sparse_header, sizeof(sparse_header)); + if (ret < 0) { + goto err_write; + } } return out; + +err_write: + if (gz) { + gzclose(out->gz_fd); + } +err_gzopen: + free(out->fill_buf); +err_fill_buf: + free(out->zero_buf); +err_zero_buf: + free(out); + return NULL; } struct output_file *open_output_file(const char *filename, @@ -449,123 +498,31 @@ struct output_file *open_output_file(const char *filename, return file; } -void pad_output_file(struct output_file *out, int64_t len) -{ - int ret; - - if (len > out->len) { - error("attempted to pad file %llu bytes past end of filesystem", - len - out->len); - return; - } - if (out->sparse) { - /* We need to emit a DONT_CARE chunk to pad out the file if the - * cur_out_ptr is not already at the end of the filesystem. - */ - if (len < out->cur_out_ptr) { - error("attempted to pad file %llu bytes less than the current output pointer", - out->cur_out_ptr - len); - return; - } - if (len > out->cur_out_ptr) { - emit_skip_chunk(out, len - out->cur_out_ptr); - } - } else { - //KEN TODO: Fixme. If the filesystem image needs no padding, - // this will overwrite the last byte in the file with 0 - // The answer is to do accounting like the sparse image - // code does and know if there is already data there. - ret = out->ops->seek(out, len - 1); - if (ret < 0) - return; - - ret = out->ops->write(out, (u8*)"", 1); - if (ret < 0) - return; - } -} - /* Write a contiguous region of data blocks from a memory buffer */ -void write_data_block(struct output_file *out, int64_t off, void *data, int len) +int write_data_chunk(struct output_file *out, unsigned int len, void *data) { - int ret; - - if (off + len > out->len) { - error("attempted to write block %llu past end of filesystem", - off + len - out->len); - return; - } - - if (out->sparse) { - write_chunk_raw(out, off, data, len); - } else { - ret = out->ops->seek(out, off); - if (ret < 0) - return; - - ret = out->ops->write(out, data, len); - if (ret < 0) - return; - } + return out->sparse_ops->write_data_chunk(out, len, data); } /* Write a contiguous region of data blocks with a fill value */ -void write_fill_block(struct output_file *out, int64_t off, unsigned int fill_val, int len) +int write_fill_chunk(struct output_file *out, unsigned int len, + uint32_t fill_val) { - int ret; - unsigned int i; - int write_len; - u32 fill_buf[4096/sizeof(u32)]; /* Maximum size of a block */ - - if (off + len > out->len) { - error("attempted to write block %llu past end of filesystem", - off + len - out->len); - return; - } - - if (out->sparse) { - write_chunk_fill(out, off, fill_val, len); - } else { - /* Initialize fill_buf with the fill_val */ - for (i = 0; i < sizeof(fill_buf)/sizeof(u32); i++) { - fill_buf[i] = fill_val; - } - - ret = out->ops->seek(out, off); - if (ret < 0) - return; - - while (len) { - write_len = (len > (int)sizeof(fill_buf) ? (int)sizeof(fill_buf) : len); - ret = out->ops->write(out, (u8 *)fill_buf, write_len); - if (ret < 0) { - return; - } else { - len -= write_len; - } - } - } + return out->sparse_ops->write_fill_chunk(out, len, fill_val); } /* Write a contiguous region of data blocks from a file */ -void write_data_file(struct output_file *out, int64_t off, const char *file, - int64_t offset, int len) +int write_file_chunk(struct output_file *out, unsigned int len, + const char *file, int64_t offset) { int ret; int64_t aligned_offset; int aligned_diff; int buffer_size; - if (off + len >= out->len) { - error("attempted to write block %llu past end of filesystem", - off + len - out->len); - return; - } - int file_fd = open(file, O_RDONLY | O_BINARY); if (file_fd < 0) { - error_errno("open"); - return; + return -errno; } aligned_offset = offset & ~(4096 - 1); @@ -573,41 +530,36 @@ void write_data_file(struct output_file *out, int64_t off, const char *file, buffer_size = len + aligned_diff; #ifndef USE_MINGW - u8 *data = mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, file_fd, + char *data = mmap64(NULL, buffer_size, PROT_READ, MAP_SHARED, file_fd, aligned_offset); if (data == MAP_FAILED) { - error_errno("mmap64"); + ret = -errno; close(file_fd); - return; + return ret; } #else - u8 *data = malloc(buffer_size); + char *data = malloc(buffer_size); if (!data) { - error_errno("malloc"); + ret = -errno; close(file_fd); - return; + return ret; } memset(data, 0, buffer_size); #endif - if (out->sparse) { - write_chunk_raw(out, off, data + aligned_diff, len); - } else { - ret = out->ops->seek(out, off); - if (ret < 0) - goto err; - - ret = out->ops->write(out, data + aligned_diff, len); - if (ret < 0) - goto err; - } + ret = out->sparse_ops->write_data_chunk(out, len, data + aligned_diff); -err: #ifndef USE_MINGW munmap(data, buffer_size); #else - write(file_fd, data, buffer_size); free(data); #endif close(file_fd); + + return ret; +} + +int write_skip_chunk(struct output_file *out, int64_t len) +{ + return out->sparse_ops->write_skip_chunk(out, len); } diff --git a/libsparse/output_file.h b/libsparse/output_file.h index b12194fc7..cb2feb7fb 100644 --- a/libsparse/output_file.h +++ b/libsparse/output_file.h @@ -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 diff --git a/libsparse/sparse.c b/libsparse/sparse.c index a6134c9d2..fce9dbbb6 100644 --- a/libsparse/sparse.c +++ b/libsparse/sparse.c @@ -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; -} - -static void ext4_write_data_block(void *priv, int64_t off, void *data, int len) -{ - write_data_block(priv, off, data, len); -} - -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); -} + 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++; + } + chunks++; + last_block = backed_block_block(bb) + + DIV_ROUND_UP(backed_block_len(bb), s->block_size); + } + 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); diff --git a/libsparse/sparse_crc32.h b/libsparse/sparse_crc32.h index 21625bac9..cad8a8630 100644 --- a/libsparse/sparse_crc32.h +++ b/libsparse/sparse_crc32.h @@ -14,5 +14,7 @@ * limitations under the License. */ -u32 sparse_crc32(u32 crc, const void *buf, size_t size); +#include <stdint.h> + +uint32_t sparse_crc32(uint32_t crc, const void *buf, size_t size); |