diff options
-rw-r--r-- | include/data_reader.h | 33 | ||||
-rw-r--r-- | lib/Makemodule.am | 4 | ||||
-rw-r--r-- | lib/sqfs/data_reader.c | 157 | ||||
-rw-r--r-- | unpack/Makemodule.am | 2 | ||||
-rw-r--r-- | unpack/extract_file.c | 93 | ||||
-rw-r--r-- | unpack/rdsquashfs.c | 61 | ||||
-rw-r--r-- | unpack/rdsquashfs.h | 10 | ||||
-rw-r--r-- | unpack/restore_fstree.c | 2 |
8 files changed, 205 insertions, 157 deletions
diff --git a/include/data_reader.h b/include/data_reader.h new file mode 100644 index 0000000..68788b4 --- /dev/null +++ b/include/data_reader.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#ifndef DATA_READER_H +#define DATA_READER_H + +#include "squashfs.h" +#include "compress.h" +#include "fstree.h" + +typedef struct data_reader_t data_reader_t; + +/* + Create a data reader for accessing data blocks in a squashfs image. + + Internally creates a fragment_reader_t (if applicable) to resolve + fragment indices. + + Prints error messsages to stderr on failure. + */ +data_reader_t *data_reader_create(int fd, sqfs_super_t *super, + compressor_t *cmp); + +void data_reader_destroy(data_reader_t *data); + +/* + Use a file_info_t to locate and extract all blocks of the coresponding + file and its fragment, if it has one. The entire data is dumped to the + given file descriptor. + + Returns 0 on success, prints error messages to stderr on failure. + */ +int data_reader_dump_file(data_reader_t *data, file_info_t *fi, int outfd); + +#endif /* DATA_READER_H */ diff --git a/lib/Makemodule.am b/lib/Makemodule.am index 3c1ef12..ddc78f6 100644 --- a/lib/Makemodule.am +++ b/lib/Makemodule.am @@ -23,8 +23,8 @@ libsquashfs_a_SOURCES += lib/sqfs/serialize_fstree.c libsquashfs_a_SOURCES += lib/sqfs/tree_node_from_inode.c libsquashfs_a_SOURCES += lib/sqfs/deserialize_fstree.c libsquashfs_a_SOURCES += lib/sqfs/data_writer.c lib/sqfs/write_xattr.c -libsquashfs_a_SOURCES += include/data_writer.h -libsquashfs_a_SOURCES += include/frag_reader.h +libsquashfs_a_SOURCES += include/data_writer.h include/frag_reader.h +libsquashfs_a_SOURCES += include/data_reader.h lib/sqfs/data_reader.c libutil_a_SOURCES = lib/util/canonicalize_name.c lib/util/write_retry.c libutil_a_SOURCES += lib/util/read_retry.c include/util.h diff --git a/lib/sqfs/data_reader.c b/lib/sqfs/data_reader.c new file mode 100644 index 0000000..22413f2 --- /dev/null +++ b/lib/sqfs/data_reader.c @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "data_reader.h" +#include "frag_reader.h" +#include "util.h" + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +struct data_reader_t { + compressor_t *cmp; + size_t block_size; + frag_reader_t *frag; + int sqfsfd; + + void *buffer; + void *scratch; +}; + +data_reader_t *data_reader_create(int fd, sqfs_super_t *super, + compressor_t *cmp) +{ + data_reader_t *data = calloc(1, sizeof(*data) + 2 * super->block_size); + + if (data == NULL) { + perror("creating data reader"); + return data; + } + + if (super->fragment_entry_count > 0 && + !(super->flags & SQFS_FLAG_NO_FRAGMENTS)) { + if (super->fragment_table_start >= super->bytes_used) { + fputs("Fragment table start is past end of file\n", + stderr); + free(data); + return NULL; + } + + data->frag = frag_reader_create(super, fd, cmp); + if (data->frag == NULL) { + free(data); + return NULL; + } + } + + data->buffer = (char *)data + sizeof(*data); + data->scratch = (char *)data->buffer + super->block_size; + data->sqfsfd = fd; + data->block_size = super->block_size; + data->cmp = cmp; + return data; +} + +void data_reader_destroy(data_reader_t *data) +{ + if (data->frag != NULL) + frag_reader_destroy(data->frag); + free(data); +} + +int data_reader_dump_file(data_reader_t *data, file_info_t *fi, int outfd) +{ + size_t i, count, fragsz; + bool compressed; + uint32_t bs; + ssize_t ret; + void *ptr; + + count = fi->size / data->block_size; + + if (count > 0) { + if (lseek(data->sqfsfd, fi->startblock, SEEK_SET) == (off_t)-1) + goto fail_seek; + + for (i = 0; i < count; ++i) { + bs = fi->blocksizes[i]; + + compressed = (bs & (1 << 24)) == 0; + bs &= (1 << 24) - 1; + + if (bs > data->block_size) + goto fail_bs; + + ret = read_retry(data->sqfsfd, data->buffer, bs); + if (ret < 0) + goto fail_rd; + + if ((size_t)ret < bs) + goto fail_trunc; + + if (compressed) { + ret = data->cmp->do_block(data->cmp, + data->buffer, bs, + data->scratch, + data->block_size); + if (ret <= 0) + return -1; + + bs = ret; + ptr = data->scratch; + } else { + ptr = data->buffer; + } + + ret = write_retry(outfd, ptr, bs); + if (ret < 0) + goto fail_wr; + + if ((size_t)ret < bs) + goto fail_wr_trunc; + } + } + + fragsz = fi->size % data->block_size; + + if (fragsz > 0) { + if (data->frag == NULL) + goto fail_frag; + + if (frag_reader_read(data->frag, fi->fragment, + fi->fragment_offset, data->buffer, + fragsz)) { + return -1; + } + + ret = write_retry(outfd, data->buffer, fragsz); + if (ret < 0) + goto fail_wr; + + if ((size_t)ret < fragsz) + goto fail_wr_trunc; + } + + return 0; +fail_seek: + perror("seek on squashfs"); + return -1; +fail_wr: + perror("writing uncompressed block"); + return -1; +fail_wr_trunc: + fputs("writing uncompressed block: truncated write\n", stderr); + return -1; +fail_rd: + perror("reading from squashfs"); + return -1; +fail_trunc: + fputs("reading from squashfs: unexpected end of file\n", stderr); + return -1; +fail_bs: + fputs("found compressed block larger than block size\n", stderr); + return -1; +fail_frag: + fputs("file not a multiple of block size but no fragments available\n", + stderr); + return -1; +} diff --git a/unpack/Makemodule.am b/unpack/Makemodule.am index 5f4269e..c70026d 100644 --- a/unpack/Makemodule.am +++ b/unpack/Makemodule.am @@ -1,5 +1,5 @@ rdsquashfs_SOURCES = unpack/rdsquashfs.c unpack/rdsquashfs.h -rdsquashfs_SOURCES += unpack/list_files.c unpack/extract_file.c +rdsquashfs_SOURCES += unpack/list_files.c rdsquashfs_SOURCES += unpack/restore_fstree.c unpack/describe.c rdsquashfs_LDADD = libsquashfs.a libfstree.a libcompress.a libutil.a diff --git a/unpack/extract_file.c b/unpack/extract_file.c deleted file mode 100644 index 62d1086..0000000 --- a/unpack/extract_file.c +++ /dev/null @@ -1,93 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -#include "rdsquashfs.h" - -int extract_file(file_info_t *fi, unsqfs_info_t *info, int outfd) -{ - size_t i, count, fragsz; - bool compressed; - uint32_t bs; - ssize_t ret; - void *ptr; - - count = fi->size / info->block_size; - - if (count > 0) { - if (lseek(info->sqfsfd, fi->startblock, SEEK_SET) == (off_t)-1) - goto fail_seek; - - for (i = 0; i < count; ++i) { - bs = fi->blocksizes[i]; - - compressed = (bs & (1 << 24)) == 0; - bs &= (1 << 24) - 1; - - if (bs > info->block_size) - goto fail_bs; - - ret = read_retry(info->sqfsfd, info->buffer, bs); - if (ret < 0) - goto fail_rd; - - if ((size_t)ret < bs) - goto fail_trunc; - - if (compressed) { - ret = info->cmp->do_block(info->cmp, - info->buffer, bs, - info->scratch, - info->block_size); - if (ret <= 0) - return -1; - - bs = ret; - ptr = info->scratch; - } else { - ptr = info->buffer; - } - - ret = write_retry(outfd, ptr, bs); - if (ret < 0) - goto fail_wr; - - if ((size_t)ret < bs) - goto fail_wr_trunc; - } - } - - fragsz = fi->size % info->block_size; - - if (fragsz > 0) { - if (frag_reader_read(info->frag, fi->fragment, - fi->fragment_offset, info->buffer, - fragsz)) { - return -1; - } - - ret = write_retry(outfd, info->buffer, fragsz); - if (ret < 0) - goto fail_wr; - - if ((size_t)ret < fragsz) - goto fail_wr_trunc; - } - - return 0; -fail_seek: - perror("seek on squashfs"); - return -1; -fail_wr: - perror("writing uncompressed block"); - return -1; -fail_wr_trunc: - fputs("writing uncompressed block: truncated write\n", stderr); - return -1; -fail_rd: - perror("reading from squashfs"); - return -1; -fail_trunc: - fputs("reading from squashfs: unexpected end of file\n", stderr); - return -1; -fail_bs: - fputs("found compressed block larger than block size\n", stderr); - return -1; -} diff --git a/unpack/rdsquashfs.c b/unpack/rdsquashfs.c index 81da226..7fd2947 100644 --- a/unpack/rdsquashfs.c +++ b/unpack/rdsquashfs.c @@ -121,43 +121,6 @@ static char *get_path(char *old, const char *arg) return path; } -static int alloc_buffers(unsqfs_info_t *info, sqfs_super_t *super) -{ - info->buffer = malloc(super->block_size); - if (info->buffer == NULL) { - perror("allocating block buffer"); - return -1; - } - - info->scratch = malloc(super->block_size); - if (info->scratch == NULL) { - perror("allocating scrtach buffer for extracting blocks"); - return -1; - } - - return 0; -} - -static int load_fragment_table(unsqfs_info_t *info, sqfs_super_t *super) -{ - if (super->fragment_entry_count == 0) - return 0; - - if (super->flags & SQFS_FLAG_NO_FRAGMENTS) - return 0; - - if (super->fragment_table_start >= super->bytes_used) { - fputs("Fragment table start is past end of file\n", stderr); - return -1; - } - - info->frag = frag_reader_create(super, info->sqfsfd, info->cmp); - if (info->frag == NULL) - return -1; - - return 0; -} - int main(int argc, char **argv) { int i, status = EXIT_FAILURE, op = OP_NONE; @@ -281,8 +244,6 @@ int main(int argc, char **argv) goto out_cmp; } - info.block_size = super.block_size; - switch (op) { case OP_LS: n = find_node(fs.root, cmdpath); @@ -305,14 +266,14 @@ int main(int argc, char **argv) goto out_fs; } - if (load_fragment_table(&info, &super)) + info.data = data_reader_create(info.sqfsfd, &super, info.cmp); + if (info.data == NULL) goto out_fs; - if (alloc_buffers(&info, &super)) - goto out_fs; - - if (extract_file(n->data.file, &info, STDOUT_FILENO)) + if (data_reader_dump_file(info.data, n->data.file, + STDOUT_FILENO)) { goto out_fs; + } break; case OP_UNPACK: n = find_node(fs.root, cmdpath); @@ -321,10 +282,8 @@ int main(int argc, char **argv) goto out_fs; } - if (load_fragment_table(&info, &super)) - goto out_fs; - - if (alloc_buffers(&info, &super)) + info.data = data_reader_create(info.sqfsfd, &super, info.cmp); + if (info.data == NULL) goto out_fs; if (restore_fstree(unpack_root, n, &info)) @@ -337,8 +296,8 @@ int main(int argc, char **argv) status = EXIT_SUCCESS; out_fs: - if (info.frag != NULL) - frag_reader_destroy(info.frag); + if (info.data != NULL) + data_reader_destroy(info.data); fstree_cleanup(&fs); out_cmp: info.cmp->destroy(info.cmp); @@ -346,8 +305,6 @@ out_fd: close(info.sqfsfd); out_cmd: free(cmdpath); - free(info.buffer); - free(info.scratch); return status; fail_arg: fprintf(stderr, "Try `%s --help' for more information.\n", __progname); diff --git a/unpack/rdsquashfs.h b/unpack/rdsquashfs.h index a615bb5..b927bab 100644 --- a/unpack/rdsquashfs.h +++ b/unpack/rdsquashfs.h @@ -3,7 +3,7 @@ #define RDSQUASHFS_H #include "meta_reader.h" -#include "frag_reader.h" +#include "data_reader.h" #include "highlevel.h" #include "squashfs.h" #include "compress.h" @@ -27,21 +27,15 @@ enum UNPACK_FLAGS { }; typedef struct { + data_reader_t *data; compressor_t *cmp; - size_t block_size; - frag_reader_t *frag; int rdtree_flags; int sqfsfd; int flags; - - void *buffer; - void *scratch; } unsqfs_info_t; void list_files(tree_node_t *node); -int extract_file(file_info_t *fi, unsqfs_info_t *info, int outfd); - int restore_fstree(const char *rootdir, tree_node_t *root, unsqfs_info_t *info); diff --git a/unpack/restore_fstree.c b/unpack/restore_fstree.c index 355bbb5..a39ec97 100644 --- a/unpack/restore_fstree.c +++ b/unpack/restore_fstree.c @@ -63,7 +63,7 @@ static int create_node(tree_node_t *n, unsqfs_info_t *info) return -1; } - if (extract_file(n->data.file, info, fd)) { + if (data_reader_dump_file(info->data, n->data.file, fd)) { close(fd); return -1; } |