diff options
author | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2023-07-18 21:54:50 +0200 |
---|---|---|
committer | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2023-08-10 09:28:27 +0200 |
commit | 5a3b741b92b793be7221a481efca316aec208ebe (patch) | |
tree | 81eaa29e7f4510f4419a3dc2f0b29120aa5114ab /lib | |
parent | 9d431639effb4e33169110031a689fd1e9d435cf (diff) |
Add a data reader based sqfs_istream_t implementation
To the sqfs_data_reader_t is added, an sqfs_istream_t implementation
that internally reads through the data reader. The uses of the
data_reader_dump function are removed and the function is subsequently
removed from libcommon.
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/common/Makemodule.am | 2 | ||||
-rw-r--r-- | lib/common/src/data_reader_dump.c | 67 | ||||
-rw-r--r-- | lib/sqfs/src/data_reader.c | 197 |
3 files changed, 198 insertions, 68 deletions
diff --git a/lib/common/Makemodule.am b/lib/common/Makemodule.am index 61d87d4..e0cc551 100644 --- a/lib/common/Makemodule.am +++ b/lib/common/Makemodule.am @@ -1,7 +1,7 @@ libcommon_a_SOURCES = include/common.h include/simple_writer.h \ include/compress_cli.h \ lib/common/src/hardlink.c lib/common/src/print_version.c \ - lib/common/src/data_reader_dump.c lib/common/src/compress.c \ + lib/common/src/compress.c \ lib/common/src/comp_opt.c lib/common/src/data_writer.c \ lib/common/src/parse_size.c lib/common/src/print_size.c \ lib/common/src/writer/init.c lib/common/src/writer/cleanup.c \ diff --git a/lib/common/src/data_reader_dump.c b/lib/common/src/data_reader_dump.c deleted file mode 100644 index 920b2bd..0000000 --- a/lib/common/src/data_reader_dump.c +++ /dev/null @@ -1,67 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * data_reader_dump.c - * - * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at> - */ -#include "common.h" - -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <errno.h> - -int sqfs_data_reader_dump(const char *name, sqfs_data_reader_t *data, - const sqfs_inode_generic_t *inode, - sqfs_ostream_t *fp, size_t block_size) -{ - size_t i, diff, chunk_size; - sqfs_u64 filesz; - sqfs_u8 *chunk; - int err; - - sqfs_inode_get_file_size(inode, &filesz); - - for (i = 0; i < sqfs_inode_get_file_block_count(inode); ++i) { - diff = (filesz < block_size) ? filesz : block_size; - - if (SQFS_IS_SPARSE_BLOCK(inode->extra[i])) { - err = fp->append(fp, NULL, diff); - } else { - err = sqfs_data_reader_get_block(data, inode, i, - &chunk_size, &chunk); - if (err) { - sqfs_perror(name, "reading data block", err); - return -1; - } - - err = fp->append(fp, chunk, chunk_size); - free(chunk); - } - - if (err) - goto fail_io; - - filesz -= diff; - } - - if (filesz > 0) { - err = sqfs_data_reader_get_fragment(data, inode, - &chunk_size, &chunk); - if (err) { - sqfs_perror(name, "reading fragment block", err); - return -1; - } - - err = fp->append(fp, chunk, chunk_size); - free(chunk); - - if (err) - goto fail_io; - } - - return 0; -fail_io: - sqfs_perror(fp->get_filename(fp), "writing data block", err); - return -1; -} diff --git a/lib/sqfs/src/data_reader.c b/lib/sqfs/src/data_reader.c index 3f0cd74..e87259f 100644 --- a/lib/sqfs/src/data_reader.c +++ b/lib/sqfs/src/data_reader.c @@ -372,3 +372,200 @@ sqfs_s32 sqfs_data_reader_read(sqfs_data_reader_t *data, return total; } + +/*****************************************************************************/ + +typedef struct { + sqfs_istream_t base; + + sqfs_data_reader_t *rd; + const char *filename; + const sqfs_u32 *blocks; + + sqfs_u8 *buffer; + size_t buf_used; + size_t buf_off; + + sqfs_u64 filesz; + sqfs_u64 disk_offset; + + sqfs_u32 frag_idx; + sqfs_u32 frag_off; + + sqfs_u32 blk_idx; + sqfs_u32 blk_count; + + sqfs_u32 inodata[]; +} data_reader_istream_t; + +static int dr_stream_get_buffered_data(sqfs_istream_t *base, + const sqfs_u8 **out, + size_t *size, size_t want) +{ + data_reader_istream_t *stream = (data_reader_istream_t *)base; + sqfs_data_reader_t *rd = stream->rd; + int ret; + (void)want; + + if (stream->buf_off < stream->buf_used) { + *out = stream->buffer + stream->buf_off; + *size = stream->buf_used - stream->buf_off; + return 0; + } + + if (stream->filesz == 0) { + ret = 1; + goto fail; + } + + stream->buf_off = 0; + stream->buf_used = rd->block_size; + if (stream->filesz < (sqfs_u64)stream->buf_used) + stream->buf_used = stream->filesz; + + if (stream->blk_idx < stream->blk_count) { + sqfs_u32 blkword = stream->blocks[stream->blk_idx++]; + sqfs_u32 disksz = SQFS_ON_DISK_BLOCK_SIZE(blkword); + + if (disksz == 0) { + memset(stream->buffer, 0, stream->buf_used); + } else if (SQFS_IS_BLOCK_COMPRESSED(blkword)) { + ret = rd->file->read_at(rd->file, stream->disk_offset, + rd->scratch, disksz); + if (ret) + goto fail; + + ret = rd->cmp->do_block(rd->cmp, rd->scratch, disksz, + stream->buffer, + stream->buf_used); + if (ret <= 0) { + ret = ret < 0 ? ret : SQFS_ERROR_OVERFLOW; + goto fail; + } + + if ((size_t)ret < stream->buf_used) { + memset(stream->buffer + ret, 0, + stream->buf_used - ret); + } + } else { + ret = rd->file->read_at(rd->file, stream->disk_offset, + stream->buffer, disksz); + if (ret) + goto fail; + if (disksz < stream->buf_used) { + memset(stream->buffer + disksz, 0, + stream->buf_used - disksz); + } + } + + stream->disk_offset += disksz; + } else { + ret = precache_fragment_block(rd, stream->frag_idx); + if (ret) + return ret; + + if (rd->frag_blk_size < stream->frag_off || + (rd->frag_blk_size - stream->frag_off) < stream->buf_used) { + ret = SQFS_ERROR_CORRUPTED; + goto fail; + } + + memcpy(stream->buffer, rd->frag_block + stream->frag_off, + stream->buf_used); + } + + stream->filesz -= stream->buf_used; + *out = stream->buffer; + *size = stream->buf_used; + return 0; +fail: + free(stream->buffer); + stream->buffer = NULL; + stream->buf_used = 0; + stream->buf_off = 0; + stream->filesz = 0; + *out = NULL; + *size = 0; + return ret; +} + +static void dr_stream_advance_buffer(sqfs_istream_t *base, size_t count) +{ + data_reader_istream_t *stream = (data_reader_istream_t *)base; + size_t diff = stream->buf_used - stream->buf_off; + + stream->buf_off += (diff < count ? diff : count); +} + +static const char *dr_stream_get_filename(sqfs_istream_t *base) +{ + return ((data_reader_istream_t *)base)->filename; +} + +static void dr_stream_destroy(sqfs_object_t *obj) +{ + data_reader_istream_t *stream = (data_reader_istream_t *)obj; + + sqfs_drop(stream->rd); + free(stream->buffer); + free(stream); +} + +int sqfs_data_reader_create_stream(sqfs_data_reader_t *data, + const sqfs_inode_generic_t *inode, + const char *filename, sqfs_istream_t **out) +{ + data_reader_istream_t *stream; + size_t ino_sz, namelen, sz; + sqfs_u64 filesz; + char *nameptr; + int ret; + + *out = NULL; + + ret = sqfs_inode_get_file_size(inode, &filesz); + if (ret != 0) + return ret; + + ino_sz = inode->payload_bytes_used; + namelen = strlen(filename) + 1; + + if (SZ_ADD_OV(ino_sz, namelen, &sz)) + return SQFS_ERROR_ALLOC; + if (SZ_ADD_OV(sz, sizeof(*stream), &sz)) + return SQFS_ERROR_ALLOC; + + stream = calloc(1, sz); + if (stream == NULL) + return SQFS_ERROR_ALLOC; + + stream->buffer = malloc(data->block_size); + if (stream->buffer == NULL) { + free(stream); + return SQFS_ERROR_ALLOC; + } + + sqfs_object_init(stream, dr_stream_destroy, NULL); + + memcpy(stream->inodata, inode->extra, ino_sz); + stream->blocks = stream->inodata; + stream->blk_count = ino_sz / sizeof(stream->blocks[0]); + stream->filesz = filesz; + + nameptr = (char *)stream->inodata + ino_sz; + memcpy(nameptr, filename, namelen); + stream->filename = nameptr; + + sqfs_inode_get_file_block_start(inode, &stream->disk_offset); + sqfs_inode_get_frag_location(inode, &stream->frag_idx, + &stream->frag_off); + stream->rd = sqfs_grab(data); + + ((sqfs_istream_t *)stream)->advance_buffer = dr_stream_advance_buffer; + ((sqfs_istream_t *)stream)->get_filename = dr_stream_get_filename; + ((sqfs_istream_t *)stream)->get_buffered_data = + dr_stream_get_buffered_data; + + *out = (sqfs_istream_t *)stream; + return 0; +} |