diff options
Diffstat (limited to 'lib/sqfs/block_processor/block_processor.c')
-rw-r--r-- | lib/sqfs/block_processor/block_processor.c | 110 |
1 files changed, 106 insertions, 4 deletions
diff --git a/lib/sqfs/block_processor/block_processor.c b/lib/sqfs/block_processor/block_processor.c index 19a538a..8303dce 100644 --- a/lib/sqfs/block_processor/block_processor.c +++ b/lib/sqfs/block_processor/block_processor.c @@ -44,11 +44,109 @@ static int process_block(void *userptr, void *workitem) return 0; } +static int load_frag_block(sqfs_block_processor_t *proc, sqfs_u32 index) +{ + sqfs_fragment_t info; + size_t size; + int ret; + + if (proc->cached_frag_blk == NULL) { + size = sizeof(*proc->cached_frag_blk); + + proc->cached_frag_blk = alloc_flex(size, 1, + proc->max_block_size); + + if (proc->cached_frag_blk == NULL) + return SQFS_ERROR_ALLOC; + } else { + if (proc->cached_frag_blk->index == index) + return 0; + } + + ret = sqfs_frag_table_lookup(proc->frag_tbl, index, &info); + if (ret != 0) + return ret; + + size = SQFS_ON_DISK_BLOCK_SIZE(info.size); + if (size >= proc->max_block_size) + return SQFS_ERROR_CORRUPTED; + + if (SQFS_IS_BLOCK_COMPRESSED(info.size)) { + ret = proc->file->read_at(proc->file, info.start_offset, + proc->scratch, size); + if (ret != 0) + return ret; + + ret = proc->uncmp->do_block(proc->uncmp, proc->scratch, size, + proc->cached_frag_blk->data, + proc->max_block_size); + if (ret <= 0) + return ret ? ret : SQFS_ERROR_OVERFLOW; + + size = ret; + } else { + ret = proc->file->read_at(proc->file, info.start_offset, + proc->cached_frag_blk->data, size); + if (ret != 0) + return ret; + } + + proc->cached_frag_blk->size = size; + proc->cached_frag_blk->index = index; + return 0; +} + static bool chunk_info_equals(void *user, const void *k, const void *c) { const chunk_info_t *key = k, *cmp = c; - (void)user; - return key->size == cmp->size && key->hash == cmp->hash; + sqfs_block_processor_t *proc = user; + sqfs_block_t *it; + int ret; + + if (key->size != cmp->size || key->hash != cmp->hash) + return false; + + if (proc->uncmp == NULL || proc->file == NULL) + return true; + + if (proc->current_frag == NULL || proc->frag_tbl == NULL) + return true; + + if (proc->fblk_lookup_error != 0) + return false; + + for (it = proc->fblk_in_flight; it != NULL; it = it->next) { + if (it->index == cmp->index) + break; + } + + if (it == NULL && proc->frag_block != NULL) { + if (proc->frag_block->index == cmp->index) + it = proc->frag_block; + } + + if (it == NULL) { + ret = load_frag_block(proc, cmp->index); + if (ret != 0) { + proc->fblk_lookup_error = ret; + return false; + } + + it = proc->cached_frag_blk; + } + + if (cmp->offset >= it->size || (it->size - cmp->offset) < cmp->size) { + proc->fblk_lookup_error = SQFS_ERROR_CORRUPTED; + return false; + } + + if (cmp->size != proc->current_frag->size) { + proc->fblk_lookup_error = SQFS_ERROR_CORRUPTED; + return false; + } + + return memcmp(it->data + cmp->offset, + proc->current_frag->data, cmp->size) == 0; } static void ht_delete_function(struct hash_entry *entry) @@ -71,6 +169,7 @@ static void block_processor_destroy(sqfs_object_t *base) free(proc->frag_block); free(proc->blk_current); + free(proc->cached_frag_blk); free_block_list(proc->free_list); free_block_list(proc->io_queue); @@ -155,14 +254,17 @@ const sqfs_block_processor_stats_t int sqfs_block_processor_create_ex(const sqfs_block_processor_desc_t *desc, sqfs_block_processor_t **out) { + size_t i, count, scratch_size = 0; sqfs_block_processor_t *proc; - size_t i, count; int ret; if (desc->size != sizeof(sqfs_block_processor_desc_t)) return SQFS_ERROR_ARG_INVALID; - proc = calloc(1, sizeof(*proc)); + if (desc->file != NULL && desc->uncmp != NULL) + scratch_size = desc->max_block_size; + + proc = alloc_flex(sizeof(*proc), 1, scratch_size); if (proc == NULL) return SQFS_ERROR_ALLOC; |