aboutsummaryrefslogtreecommitdiff
path: root/lib/sqfs/data_writer
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqfs/data_writer')
-rw-r--r--lib/sqfs/data_writer/fileapi.c101
-rw-r--r--lib/sqfs/data_writer/internal.h12
-rw-r--r--lib/sqfs/data_writer/pthread.c31
-rw-r--r--lib/sqfs/data_writer/serial.c16
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);