diff options
-rw-r--r-- | difftool/extract.c | 10 | ||||
-rw-r--r-- | include/data_reader.h | 10 | ||||
-rw-r--r-- | include/sqfs/error.h | 2 | ||||
-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 | ||||
-rw-r--r-- | tar/sqfs2tar.c | 3 | ||||
-rw-r--r-- | unpack/fill_files.c | 2 | ||||
-rw-r--r-- | unpack/rdsquashfs.c | 3 |
9 files changed, 216 insertions, 62 deletions
diff --git a/difftool/extract.c b/difftool/extract.c index 45c5560..94477b1 100644 --- a/difftool/extract.c +++ b/difftool/extract.c @@ -7,7 +7,7 @@ #include "sqfsdiff.h" static int extract(data_reader_t *data, const sqfs_inode_generic_t *inode, - const char *prefix, const char *path) + const char *prefix, const char *path, size_t block_size) { char *ptr, *temp; int fd; @@ -27,7 +27,7 @@ static int extract(data_reader_t *data, const sqfs_inode_generic_t *inode, return -1; } - if (data_reader_dump(data, inode, fd, true)) { + if (data_reader_dump(data, inode, fd, block_size, true)) { close(fd); return -1; } @@ -41,12 +41,14 @@ int extract_files(sqfsdiff_t *sd, const sqfs_inode_generic_t *old, const char *path) { if (old != NULL) { - if (extract(sd->sqfs_old.data, old, "old", path)) + if (extract(sd->sqfs_old.data, old, "old", + path, sd->sqfs_old.super.block_size)) return -1; } if (new != NULL) { - if (extract(sd->sqfs_new.data, new, "new", path)) + if (extract(sd->sqfs_new.data, new, "new", + path, sd->sqfs_new.super.block_size)) return -1; } diff --git a/include/data_reader.h b/include/data_reader.h index 77bdd82..324cdc8 100644 --- a/include/data_reader.h +++ b/include/data_reader.h @@ -28,8 +28,16 @@ data_reader_t *data_reader_create(sqfs_file_t *file, sqfs_super_t *super, void data_reader_destroy(data_reader_t *data); +int data_reader_get_fragment(data_reader_t *data, + const sqfs_inode_generic_t *inode, + sqfs_block_t **out); + +int data_reader_get_block(data_reader_t *data, + const sqfs_inode_generic_t *inode, + size_t index, sqfs_block_t **out); + int data_reader_dump(data_reader_t *data, const sqfs_inode_generic_t *inode, - int outfd, bool allow_sparse); + int outfd, size_t block_size, bool allow_sparse); /* Read a chunk of data from a file. Starting from 'offset' into the diff --git a/include/sqfs/error.h b/include/sqfs/error.h index 31ebb47..7277a3d 100644 --- a/include/sqfs/error.h +++ b/include/sqfs/error.h @@ -109,6 +109,8 @@ typedef enum { SQFS_ERROR_NO_ENTRY = -13, SQFS_ERROR_LINK_LOOP = -14, + + SQFS_ERROR_NOT_FILE = -15, } E_SQFS_ERROR; #endif /* SQFS_ERROR_H */ 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; +} diff --git a/tar/sqfs2tar.c b/tar/sqfs2tar.c index 43df40b..c624934 100644 --- a/tar/sqfs2tar.c +++ b/tar/sqfs2tar.c @@ -317,7 +317,8 @@ static int write_tree_dfs(const sqfs_tree_node_t *n) return -1; if (S_ISREG(sb.st_mode)) { - if (data_reader_dump(data, n->inode, STDOUT_FILENO, false)) + if (data_reader_dump(data, n->inode, STDOUT_FILENO, + super.block_size, false)) return -1; if (padd_file(STDOUT_FILENO, sb.st_size, 512)) diff --git a/unpack/fill_files.c b/unpack/fill_files.c index de0b676..f98a801 100644 --- a/unpack/fill_files.c +++ b/unpack/fill_files.c @@ -172,7 +172,7 @@ static int fill_files(data_reader_t *data, int flags) if (!(flags & UNPACK_QUIET)) printf("unpacking %s\n", files[i].path); - if (data_reader_dump(data, files[i].inode, fd, + if (data_reader_dump(data, files[i].inode, fd, block_size, (flags & UNPACK_NO_SPARSE) == 0)) { close(fd); return -1; diff --git a/unpack/rdsquashfs.c b/unpack/rdsquashfs.c index 60bfd19..e8ec3ec 100644 --- a/unpack/rdsquashfs.c +++ b/unpack/rdsquashfs.c @@ -102,7 +102,8 @@ int main(int argc, char **argv) goto out; } - if (data_reader_dump(data, n->inode, STDOUT_FILENO, false)) + if (data_reader_dump(data, n->inode, STDOUT_FILENO, + super.block_size, false)) goto out; break; case OP_UNPACK: |