From 1b35319762cd83982dacbe96eccf07fd00d7858a Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Fri, 12 Jul 2019 13:48:38 +0200 Subject: Add generic support for reading files without fragments This commit extends the special case handling for sparse files to generically support reading files that don't have a fragment but instead have a trunkated final block. Signed-off-by: David Oberhollenzer --- lib/sqfs/data_reader.c | 50 +++++++++++++++++++++-------------------- lib/sqfs/read_inode.c | 23 +++++++++++++++---- lib/sqfs/tree_node_from_inode.c | 11 +++++++++ 3 files changed, 56 insertions(+), 28 deletions(-) (limited to 'lib') diff --git a/lib/sqfs/data_reader.c b/lib/sqfs/data_reader.c index f8c80af..ec85412 100644 --- a/lib/sqfs/data_reader.c +++ b/lib/sqfs/data_reader.c @@ -62,13 +62,21 @@ void data_reader_destroy(data_reader_t *data) int data_reader_dump_file(data_reader_t *data, file_info_t *fi, int outfd, bool allow_sparse) { - size_t i, count, fragsz; + size_t i, count, fragsz, unpackedsz; + uint64_t filesz = 0; bool compressed; uint32_t bs; ssize_t ret; void *ptr; count = fi->size / data->block_size; + fragsz = fi->size % data->block_size; + + if (fragsz != 0 && (fi->fragment == 0xFFFFFFFF || + fi->fragment_offset == 0xFFFFFFFF)) { + fragsz = 0; + ++count; + } if (count > 0) { if (lseek(data->sqfsfd, fi->startblock, SEEK_SET) == (off_t)-1) @@ -83,8 +91,16 @@ int data_reader_dump_file(data_reader_t *data, file_info_t *fi, int outfd, if (bs > data->block_size) goto fail_bs; + if ((fi->size - filesz) < (uint64_t)data->block_size) { + unpackedsz = fi->size - filesz; + } else { + unpackedsz = data->block_size; + } + + filesz += unpackedsz; + if (bs == 0 && allow_sparse) { - if (ftruncate(outfd, i * data->block_size)) + if (ftruncate(outfd, filesz)) goto fail_sparse; if (lseek(outfd, 0, SEEK_END) == (off_t)-1) goto fail_sparse; @@ -92,8 +108,7 @@ int data_reader_dump_file(data_reader_t *data, file_info_t *fi, int outfd, } if (bs == 0) { - bs = data->block_size; - memset(data->buffer, 0, bs); + memset(data->buffer, 0, unpackedsz); compressed = false; } else { ret = read_retry(data->sqfsfd, data->buffer, bs); @@ -118,7 +133,7 @@ int data_reader_dump_file(data_reader_t *data, file_info_t *fi, int outfd, ptr = data->buffer; } - ret = write_retry(outfd, ptr, bs); + ret = write_retry(outfd, ptr, unpackedsz); if (ret < 0) goto fail_wr; @@ -127,27 +142,14 @@ int data_reader_dump_file(data_reader_t *data, file_info_t *fi, int outfd, } } - fragsz = fi->size % data->block_size; - if (fragsz > 0) { - if (fi->fragment == 0xFFFFFFFF || - fi->fragment_offset == 0xFFFFFFFF) { - if (allow_sparse) { - if (ftruncate(outfd, fragsz)) - goto fail_sparse; - return 0; - } - - memset(data->buffer, 0, fragsz); - } else { - if (data->frag == NULL) - goto fail_frag; + if (data->frag == NULL) + goto fail_frag; - if (frag_reader_read(data->frag, fi->fragment, - fi->fragment_offset, data->buffer, - fragsz)) { - return -1; - } + if (frag_reader_read(data->frag, fi->fragment, + fi->fragment_offset, data->buffer, + fragsz)) { + return -1; } ret = write_retry(outfd, data->buffer, fragsz); diff --git a/lib/sqfs/read_inode.c b/lib/sqfs/read_inode.c index 565cb5a..8230138 100644 --- a/lib/sqfs/read_inode.c +++ b/lib/sqfs/read_inode.c @@ -50,13 +50,26 @@ static int set_mode(sqfs_inode_t *inode) return 0; } +static uint64_t get_block_count(uint64_t size, uint64_t block_size, + uint32_t frag_index, uint32_t frag_offset) +{ + uint64_t count = size / block_size; + + if ((size % block_size) != 0 && + (frag_index == 0xFFFFFFFF || frag_offset == 0xFFFFFFFF)) { + ++count; + } + + return count; +} + static sqfs_inode_generic_t *read_inode_file(meta_reader_t *ir, sqfs_inode_t *base, size_t block_size) { sqfs_inode_generic_t *out; sqfs_inode_file_t file; - size_t i, count; + uint64_t i, count; if (meta_reader_read(ir, &file, sizeof(file))) return NULL; @@ -66,7 +79,8 @@ static sqfs_inode_generic_t *read_inode_file(meta_reader_t *ir, SWAB32(file.fragment_offset); SWAB32(file.file_size); - count = file.file_size / block_size; + count = get_block_count(file.file_size, block_size, + file.fragment_index, file.fragment_offset); out = calloc(1, sizeof(*out) + count * sizeof(uint32_t)); if (out == NULL) { @@ -95,7 +109,7 @@ static sqfs_inode_generic_t *read_inode_file_ext(meta_reader_t *ir, { sqfs_inode_file_ext_t file; sqfs_inode_generic_t *out; - size_t i, count; + uint64_t i, count; if (meta_reader_read(ir, &file, sizeof(file))) return NULL; @@ -108,7 +122,8 @@ static sqfs_inode_generic_t *read_inode_file_ext(meta_reader_t *ir, SWAB32(file.fragment_offset); SWAB32(file.xattr_idx); - count = file.file_size / block_size; + count = get_block_count(file.file_size, block_size, + file.fragment_idx, file.fragment_offset); out = calloc(1, sizeof(*out) + count * sizeof(uint32_t)); if (out == NULL) { diff --git a/lib/sqfs/tree_node_from_inode.c b/lib/sqfs/tree_node_from_inode.c index 9c96701..0d46eed 100644 --- a/lib/sqfs/tree_node_from_inode.c +++ b/lib/sqfs/tree_node_from_inode.c @@ -19,10 +19,14 @@ static size_t compute_size(sqfs_inode_generic_t *inode, const char *name, case SQFS_INODE_FILE: size += sizeof(file_info_t); block_count = inode->data.file.file_size / block_size; + if ((inode->data.file.file_size % block_size) != 0) + ++block_count; break; case SQFS_INODE_EXT_FILE: size += sizeof(file_info_t); block_count = inode->data.file_ext.file_size / block_size; + if ((inode->data.file_ext.file_size % block_size) != 0) + ++block_count; break; case SQFS_INODE_SLINK: case SQFS_INODE_EXT_SLINK: @@ -40,6 +44,13 @@ static void copy_block_sizes(sqfs_inode_generic_t *inode, tree_node_t *out, { size_t block_count = out->data.file->size / block_size; + if ((out->data.file->size % block_size) != 0) { + if (out->data.file->fragment == 0xFFFFFFFF || + out->data.file->fragment_offset == 0xFFFFFFFF) { + ++block_count; + } + } + out->name += block_count * sizeof(uint32_t); if (block_count) { -- cgit v1.2.3