summaryrefslogtreecommitdiff
path: root/lib/sqfshelper/data_reader.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqfshelper/data_reader.c')
-rw-r--r--lib/sqfshelper/data_reader.c160
1 files changed, 106 insertions, 54 deletions
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,