diff options
Diffstat (limited to 'lib/sqfshelper')
-rw-r--r-- | lib/sqfshelper/Makemodule.am | 1 | ||||
-rw-r--r-- | lib/sqfshelper/data_reader.c | 160 | ||||
-rw-r--r-- | lib/sqfshelper/data_reader_dump.c | 87 |
3 files changed, 194 insertions, 54 deletions
diff --git a/lib/sqfshelper/Makemodule.am b/lib/sqfshelper/Makemodule.am index e155fa6..d711cf2 100644 --- a/lib/sqfshelper/Makemodule.am +++ b/lib/sqfshelper/Makemodule.am @@ -4,6 +4,7 @@ libsqfshelper_a_SOURCES += lib/sqfshelper/tree_node_to_inode.c libsqfshelper_a_SOURCES += lib/sqfshelper/write_export_table.c libsqfshelper_a_SOURCES += lib/sqfshelper/print_version.c libsqfshelper_a_SOURCES += lib/sqfshelper/inode_stat.c +libsqfshelper_a_SOURCES += lib/sqfshelper/data_reader_dump.c libsqfshelper_a_SOURCES += lib/sqfshelper/compress.c lib/sqfshelper/comp_opt.c libsqfshelper_a_SOURCES += include/data_reader.h lib/sqfshelper/data_reader.c libsqfshelper_a_SOURCES += include/data_writer.h lib/sqfshelper/data_writer.c diff --git a/lib/sqfshelper/data_reader.c b/lib/sqfshelper/data_reader.c index 0780da0..929b7ee 100644 --- a/lib/sqfshelper/data_reader.c +++ b/lib/sqfshelper/data_reader.c @@ -6,6 +6,9 @@ */ #include "config.h" +#include "sqfs/block_processor.h" +#include "sqfs/error.h" + #include "data_reader.h" #include "highlevel.h" #include "util.h" @@ -32,6 +35,55 @@ struct data_reader_t { void *frag_block; }; +static int get_block(data_reader_t *data, uint64_t off, uint32_t size, + size_t unpacked_size, sqfs_block_t **out) +{ + sqfs_block_t *blk = alloc_flex(sizeof(*blk), 1, unpacked_size); + size_t on_disk_size; + ssize_t ret; + int err; + + if (blk == NULL) + return SQFS_ERROR_ALLOC; + + blk->size = unpacked_size; + + if (SQFS_IS_SPARSE_BLOCK(size)) { + *out = blk; + return 0; + } + + on_disk_size = SQFS_ON_DISK_BLOCK_SIZE(size); + + if (on_disk_size > unpacked_size) + return SQFS_ERROR_OVERFLOW; + + if (SQFS_IS_BLOCK_COMPRESSED(size)) { + err = data->file->read_at(data->file, off, + data->scratch, on_disk_size); + if (err) { + free(blk); + return err; + } + + ret = data->cmp->do_block(data->cmp, data->scratch, + on_disk_size, blk->data, blk->size); + if (ret <= 0) + err = ret < 0 ? ret : SQFS_ERROR_OVERFLOW; + } else { + err = data->file->read_at(data->file, off, + blk->data, on_disk_size); + } + + if (err) { + free(blk); + return err; + } + + *out = blk; + return 0; +} + static ssize_t read_block(data_reader_t *data, off_t offset, uint32_t size, void *dst) { @@ -172,79 +224,79 @@ void data_reader_destroy(data_reader_t *data) free(data); } -int data_reader_dump(data_reader_t *data, const sqfs_inode_generic_t *inode, - int outfd, bool allow_sparse) +int data_reader_get_block(data_reader_t *data, + const sqfs_inode_generic_t *inode, + size_t index, sqfs_block_t **out) { - uint32_t frag_idx, frag_off; - uint64_t filesz; - size_t i, diff; - off_t off; + size_t i, unpacked_size; + uint64_t off, filesz; if (inode->base.type == SQFS_INODE_FILE) { - filesz = inode->data.file.file_size; off = inode->data.file.blocks_start; - frag_idx = inode->data.file.fragment_index; - frag_off = inode->data.file.fragment_offset; + filesz = inode->data.file.file_size; } else if (inode->base.type == SQFS_INODE_EXT_FILE) { - filesz = inode->data.file_ext.file_size; off = inode->data.file_ext.blocks_start; - frag_idx = inode->data.file_ext.fragment_idx; - frag_off = inode->data.file_ext.fragment_offset; + filesz = inode->data.file_ext.file_size; } else { - return -1; + return SQFS_ERROR_NOT_FILE; } - if (allow_sparse && ftruncate(outfd, filesz)) - goto fail_sparse; + if (index >= inode->num_file_blocks) + return SQFS_ERROR_OUT_OF_BOUNDS; - for (i = 0; i < inode->num_file_blocks; ++i) { - diff = filesz > data->block_size ? data->block_size : filesz; - filesz -= diff; + for (i = 0; i < index; ++i) { + off += SQFS_ON_DISK_BLOCK_SIZE(inode->block_sizes[i]); + filesz -= data->block_size; + } - if (SQFS_IS_SPARSE_BLOCK(inode->block_sizes[i])) { - if (allow_sparse) { - if (lseek(outfd, diff, SEEK_CUR) == (off_t)-1) - goto fail_sparse; - continue; - } - memset(data->block, 0, diff); - } else { - if (precache_data_block(data, off, - inode->block_sizes[i])) - return -1; - off += SQFS_ON_DISK_BLOCK_SIZE(inode->block_sizes[i]); - } + unpacked_size = filesz < data->block_size ? filesz : data->block_size; - if (write_data("writing uncompressed block", - outfd, data->block, diff)) { - return -1; - } + return get_block(data, off, inode->block_sizes[index], + unpacked_size, out); +} + +int data_reader_get_fragment(data_reader_t *data, + const sqfs_inode_generic_t *inode, + sqfs_block_t **out) +{ + uint32_t frag_idx, frag_off, frag_sz; + sqfs_block_t *blk; + uint64_t filesz; + + if (inode->base.type == SQFS_INODE_EXT_FILE) { + filesz = inode->data.file_ext.file_size; + frag_idx = inode->data.file_ext.fragment_idx; + frag_off = inode->data.file_ext.fragment_offset; + } else if (inode->base.type == SQFS_INODE_FILE) { + filesz = inode->data.file.file_size; + frag_idx = inode->data.file.fragment_index; + frag_off = inode->data.file.fragment_offset; + } else { + return -1; } - if (filesz > 0 && frag_off != 0xFFFFFFFF) { - if (precache_fragment_block(data, frag_idx)) - return -1; + if (inode->num_file_blocks * data->block_size >= filesz) { + *out = NULL; + return 0; + } - if (frag_off >= data->frag_used) - goto fail_range; + frag_sz = filesz % data->block_size; - if ((frag_off + filesz - 1) >= data->frag_used) - goto fail_range; + if (precache_fragment_block(data, frag_idx)) + return -1; - if (write_data("writing uncompressed fragment", outfd, - (char *)data->frag_block + frag_off, - filesz)) { - return -1; - } - } + if (frag_off + frag_sz > data->frag_used) + return -1; + + blk = alloc_flex(sizeof(*blk), 1, frag_sz); + if (blk == NULL) + return -1; + + blk->size = frag_sz; + memcpy(blk->data, (char *)data->frag_block + frag_off, frag_sz); + *out = blk; return 0; -fail_range: - fputs("attempted to read past fragment block limits\n", stderr); - return -1; -fail_sparse: - perror("creating sparse output file"); - return -1; } ssize_t data_reader_read(data_reader_t *data, diff --git a/lib/sqfshelper/data_reader_dump.c b/lib/sqfshelper/data_reader_dump.c new file mode 100644 index 0000000..23cd482 --- /dev/null +++ b/lib/sqfshelper/data_reader_dump.c @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * data_reader_dump.c + * + * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at> + */ +#include "config.h" + +#include "sqfs/block_processor.h" +#include "data_reader.h" +#include "highlevel.h" +#include "util.h" + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> + +int data_reader_dump(data_reader_t *data, const sqfs_inode_generic_t *inode, + int outfd, size_t block_size, bool allow_sparse) +{ + sqfs_block_t *blk; + uint64_t filesz; + size_t i, diff; + int err; + + if (inode->base.type == SQFS_INODE_EXT_FILE) { + filesz = inode->data.file_ext.file_size; + } else { + filesz = inode->data.file.file_size; + } + + if (allow_sparse && ftruncate(outfd, filesz)) + goto fail_sparse; + + for (i = 0; i < inode->num_file_blocks; ++i) { + if (SQFS_IS_SPARSE_BLOCK(inode->block_sizes[i]) && + allow_sparse) { + if (filesz < block_size) { + diff = filesz; + filesz = 0; + } else { + diff = block_size; + filesz -= block_size; + } + + if (lseek(outfd, diff, SEEK_CUR) == (off_t)-1) + goto fail_sparse; + } else { + err = data_reader_get_block(data, inode, i, &blk); + if (err) { + fprintf(stderr, "error reading " + "data block: %d\n", err); + return -1; + } + + if (write_data("writing uncompressed block", + outfd, blk->data, blk->size)) { + free(blk); + return -1; + } + + free(blk); + filesz -= blk->size; + } + } + + if (filesz > 0) { + if (data_reader_get_fragment(data, inode, &blk)) { + fputs("error reading fragment block", stderr); + return -1; + } + + if (write_data("writing uncompressed fragment", outfd, + blk->data, blk->size)) { + free(blk); + return -1; + } + + free(blk); + } + + return 0; +fail_sparse: + perror("creating sparse output file"); + return -1; +} |