diff options
Diffstat (limited to 'lib/sqfs/data_writer')
-rw-r--r-- | lib/sqfs/data_writer/fileapi.c | 101 | ||||
-rw-r--r-- | lib/sqfs/data_writer/internal.h | 12 | ||||
-rw-r--r-- | lib/sqfs/data_writer/pthread.c | 31 | ||||
-rw-r--r-- | lib/sqfs/data_writer/serial.c | 16 |
4 files changed, 135 insertions, 25 deletions
diff --git a/lib/sqfs/data_writer/fileapi.c b/lib/sqfs/data_writer/fileapi.c new file mode 100644 index 0000000..b78d012 --- /dev/null +++ b/lib/sqfs/data_writer/fileapi.c @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * fileapi.c + * + * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at> + */ +#define SQFS_BUILDING_DLL +#include "internal.h" + +static bool is_zero_block(unsigned char *ptr, size_t size) +{ + return ptr[0] == 0 && memcmp(ptr, ptr + 1, size - 1) == 0; +} + +static int add_sentinel_block(sqfs_data_writer_t *proc) +{ + sqfs_block_t *blk = calloc(1, sizeof(*blk)); + + if (blk == NULL) + return test_and_set_status(proc, SQFS_ERROR_ALLOC); + + blk->inode = proc->inode; + blk->flags = proc->blk_flags | SQFS_BLK_LAST_BLOCK; + + return data_writer_enqueue(proc, blk); +} + +int sqfs_data_writer_begin_file(sqfs_data_writer_t *proc, + sqfs_inode_generic_t *inode, uint32_t flags) +{ + if (proc->inode != NULL) + return test_and_set_status(proc, SQFS_ERROR_INTERNAL); + + if (flags & ~SQFS_BLK_USER_SETTABLE_FLAGS) + return test_and_set_status(proc, SQFS_ERROR_UNSUPPORTED); + + proc->inode = inode; + proc->blk_flags = flags; + proc->blk_index = 0; + proc->had_fragment = false; + return 0; +} + +int sqfs_data_writer_enqueue(sqfs_data_writer_t *proc, sqfs_block_t *block) +{ + int err; + + block->index = proc->blk_index++; + block->flags = proc->blk_flags; + block->inode = proc->inode; + + if (is_zero_block(block->data, block->size)) { + sqfs_inode_make_extended(proc->inode); + proc->inode->data.file_ext.sparse += block->size; + proc->inode->num_file_blocks += 1; + proc->inode->block_sizes[block->index] = 0; + free(block); + return 0; + } + + if (block->size < proc->max_block_size) { + if (block->index > 0) { + err = add_sentinel_block(proc); + + if (err) { + free(block); + return err; + } + } + + proc->had_fragment = true; + block->flags |= SQFS_BLK_IS_FRAGMENT; + } else { + if (block->index == 0) + block->flags |= SQFS_BLK_FIRST_BLOCK; + + proc->inode->num_file_blocks += 1; + } + + return data_writer_enqueue(proc, block); +} + +int sqfs_data_writer_end_file(sqfs_data_writer_t *proc) +{ + int err; + + if (proc->inode == NULL) + return test_and_set_status(proc, SQFS_ERROR_INTERNAL); + + if (!proc->had_fragment && proc->inode->num_file_blocks > 0) { + err = add_sentinel_block(proc); + if (err) + return err; + } + + proc->inode = NULL; + proc->blk_flags = 0; + proc->blk_index = 0; + proc->had_fragment = false; + return 0; +} diff --git a/lib/sqfs/data_writer/internal.h b/lib/sqfs/data_writer/internal.h index 53ddc5f..9072c19 100644 --- a/lib/sqfs/data_writer/internal.h +++ b/lib/sqfs/data_writer/internal.h @@ -101,6 +101,12 @@ struct sqfs_data_writer_t { const sqfs_block_hooks_t *hooks; void *user_ptr; + /* file API */ + sqfs_inode_generic_t *inode; + uint32_t blk_flags; + size_t blk_index; + bool had_fragment; + /* used only by workers */ size_t max_block_size; @@ -138,4 +144,10 @@ SQFS_INTERNAL int data_writer_do_block(sqfs_block_t *block, sqfs_compressor_t *cmp, uint8_t *scratch, size_t scratch_size); +SQFS_INTERNAL +int test_and_set_status(sqfs_data_writer_t *proc, int status); + +SQFS_INTERNAL +int data_writer_enqueue(sqfs_data_writer_t *proc, sqfs_block_t *block); + #endif /* INTERNAL_H */ diff --git a/lib/sqfs/data_writer/pthread.c b/lib/sqfs/data_writer/pthread.c index 3170c11..9c9b0a2 100644 --- a/lib/sqfs/data_writer/pthread.c +++ b/lib/sqfs/data_writer/pthread.c @@ -156,18 +156,6 @@ static void append_to_work_queue(sqfs_data_writer_t *proc, pthread_cond_broadcast(&proc->queue_cond); } -static int test_and_set_status(sqfs_data_writer_t *proc, int status) -{ - pthread_mutex_lock(&proc->mtx); - if (proc->status == 0) { - proc->status = status; - } else { - status = proc->status; - } - pthread_cond_broadcast(&proc->queue_cond); - return status; -} - static sqfs_block_t *try_dequeue(sqfs_data_writer_t *proc) { sqfs_block_t *queue, *it, *prev; @@ -260,16 +248,23 @@ static int process_done_queue(sqfs_data_writer_t *proc, sqfs_block_t *queue) return status; } -int sqfs_data_writer_enqueue(sqfs_data_writer_t *proc, sqfs_block_t *block) +int test_and_set_status(sqfs_data_writer_t *proc, int status) +{ + pthread_mutex_lock(&proc->mtx); + if (proc->status == 0) { + proc->status = status; + } else { + status = proc->status; + } + pthread_cond_broadcast(&proc->queue_cond); + return status; +} + +int data_writer_enqueue(sqfs_data_writer_t *proc, sqfs_block_t *block) { sqfs_block_t *queue; int status; - if (block->flags & ~SQFS_BLK_USER_SETTABLE_FLAGS) { - free(block); - return test_and_set_status(proc, SQFS_ERROR_UNSUPPORTED); - } - pthread_mutex_lock(&proc->mtx); while (proc->backlog > proc->max_backlog && proc->status == 0) pthread_cond_wait(&proc->done_cond, &proc->mtx); diff --git a/lib/sqfs/data_writer/serial.c b/lib/sqfs/data_writer/serial.c index 38dcc58..916f497 100644 --- a/lib/sqfs/data_writer/serial.c +++ b/lib/sqfs/data_writer/serial.c @@ -35,7 +35,15 @@ void sqfs_data_writer_destroy(sqfs_data_writer_t *proc) data_writer_cleanup(proc); } -int sqfs_data_writer_enqueue(sqfs_data_writer_t *proc, sqfs_block_t *block) +int test_and_set_status(sqfs_data_writer_t *proc, int status) +{ + if (proc->status == 0) + proc->status = status; + + return proc->status; +} + +int data_writer_enqueue(sqfs_data_writer_t *proc, sqfs_block_t *block) { sqfs_block_t *fragblk = NULL; @@ -44,12 +52,6 @@ int sqfs_data_writer_enqueue(sqfs_data_writer_t *proc, sqfs_block_t *block) return proc->status; } - if (block->flags & ~SQFS_BLK_USER_SETTABLE_FLAGS) { - proc->status = SQFS_ERROR_UNSUPPORTED; - free(block); - return proc->status; - } - if (block->flags & SQFS_BLK_IS_FRAGMENT) { block->checksum = crc32(0, block->data, block->size); |