From 4dc4c6bf50155550c0b83e433fef8c7c19462853 Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Fri, 3 May 2019 15:59:35 +0200 Subject: unsquashfs: add file data extraction Signed-off-by: David Oberhollenzer --- unpack/Makemodule.am | 2 +- unpack/extract_file.c | 97 +++++++++++++++++++++++++++++++++++++++++++++ unpack/unsquashfs.c | 107 ++++++++++++++++++++++++++++++++++++++------------ unpack/unsquashfs.h | 4 ++ 4 files changed, 183 insertions(+), 27 deletions(-) create mode 100644 unpack/extract_file.c (limited to 'unpack') diff --git a/unpack/Makemodule.am b/unpack/Makemodule.am index a199375..a8fd2e7 100644 --- a/unpack/Makemodule.am +++ b/unpack/Makemodule.am @@ -1,6 +1,6 @@ unsquashfs_SOURCES = unpack/unsquashfs.c unpack/tree_node_from_inode.c unsquashfs_SOURCES += unpack/unsquashfs.h unpack/read_fstree.c -unsquashfs_SOURCES += unpack/list_files.c +unsquashfs_SOURCES += unpack/list_files.c unpack/extract_file.c unsquashfs_LDADD = libsquashfs.a libfstree.a libcompress.a libutil.a if WITH_LZMA diff --git a/unpack/extract_file.c b/unpack/extract_file.c new file mode 100644 index 0000000..c18ace8 --- /dev/null +++ b/unpack/extract_file.c @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "unsquashfs.h" + +int extract_file(file_info_t *fi, compressor_t *cmp, size_t block_size, + frag_reader_t *frag, int sqfsfd, int outfd) +{ + size_t i, count, fragsz; + bool compressed; + void *buffer; + uint32_t bs; + ssize_t ret; + + buffer = malloc(block_size); + if (buffer == NULL) { + perror("allocating scratch buffer"); + return -1; + } + + count = fi->size / block_size; + + if (count > 0) { + if (lseek(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 > block_size) + goto fail_bs; + + ret = read_retry(sqfsfd, buffer, bs); + if (ret < 0) + goto fail_rd; + + if ((size_t)ret < bs) + goto fail_trunc; + + if (compressed) { + ret = cmp->do_block(cmp, buffer, bs); + if (ret <= 0) + goto fail; + + bs = ret; + } + + ret = write_retry(outfd, buffer, bs); + if (ret < 0) + goto fail_wr; + + if ((size_t)ret < bs) + goto fail_wr_trunc; + } + } + + fragsz = fi->size % block_size; + + if (fragsz > 0) { + if (frag_reader_read(frag, fi->fragment, fi->fragment_offset, + buffer, fragsz)) { + goto fail; + } + + ret = write_retry(outfd, buffer, fragsz); + if (ret < 0) + goto fail_wr; + + if ((size_t)ret < fragsz) + goto fail_wr_trunc; + } + + free(buffer); + return 0; +fail_seek: + perror("seek on squashfs"); + goto fail; +fail_wr: + perror("writing uncompressed block"); + goto fail; +fail_wr_trunc: + fputs("writing uncompressed block: truncated write\n", stderr); + goto fail; +fail_rd: + perror("reading from squashfs"); + goto fail; +fail_trunc: + fputs("reading from squashfs: unexpected end of file\n", stderr); + goto fail; +fail_bs: + fputs("found compressed block larger than block size\n", stderr); + goto fail; +fail: + free(buffer); + return -1; +} diff --git a/unpack/unsquashfs.c b/unpack/unsquashfs.c index b253bae..90a7583 100644 --- a/unpack/unsquashfs.c +++ b/unpack/unsquashfs.c @@ -1,8 +1,15 @@ /* SPDX-License-Identifier: GPL-3.0-or-later */ #include "unsquashfs.h" +enum { + OP_NONE = 0, + OP_LS, + OP_CAT, +}; + static struct option long_opts[] = { { "list", required_argument, NULL, 'l' }, + { "cat", required_argument, NULL, 'c' }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, }; @@ -17,6 +24,8 @@ static const char *help_string = "Possible options:\n" " --list, -l Produce a directory listing for a given path in the\n" " squashfs image.\n" +" --cat, -c If the specified path is a regular file in the image,\n" +" dump its contents to stdout.\n" " --help, -h Print help text and exit.\n" " --version, -V Print version information and exit.\n" "\n"; @@ -56,12 +65,35 @@ static tree_node_t *find_node(tree_node_t *n, const char *path) return n; } +static char *get_path(char *old, const char *arg) +{ + char *path; + + free(old); + + path = strdup(arg); + if (path == NULL) { + perror("processing arguments"); + exit(EXIT_FAILURE); + } + + if (canonicalize_name(path)) { + fprintf(stderr, "Invalid path: %s\n", arg); + free(path); + exit(EXIT_FAILURE); + } + + return path; +} + int main(int argc, char **argv) { - int i, fd, status = EXIT_FAILURE; - char *lspath = NULL; + int i, fd, status = EXIT_FAILURE, op = OP_NONE; + frag_reader_t *frag = NULL; + char *cmdpath = NULL; sqfs_super_t super; compressor_t *cmp; + tree_node_t *n; fstree_t fs; for (;;) { @@ -70,22 +102,13 @@ int main(int argc, char **argv) break; switch (i) { + case 'c': + op = OP_CAT; + cmdpath = get_path(cmdpath, optarg); + break; case 'l': - if (optarg != NULL) { - free(lspath); - - lspath = strdup(optarg ? optarg : ""); - if (lspath == NULL) { - perror("processing arguments"); - return EXIT_FAILURE; - } - - if (canonicalize_name(lspath)) { - fprintf(stderr, "Invalid pth: %s\n", - optarg); - goto out_cmd; - } - } + op = OP_LS; + cmdpath = get_path(cmdpath, optarg); break; case 'h': printf(help_string, __progname); @@ -100,10 +123,14 @@ int main(int argc, char **argv) } } + if (op == OP_NONE) { + fputs("No opteration specified\n", stderr); + goto fail_arg; + } + if (optind >= argc) { - fprintf(stderr, "Usage: %s [OPTIONS] \n", - __progname); - goto out_cmd; + fputs("Missing image argument\n", stderr); + goto fail_arg; } fd = open(argv[optind], O_RDONLY); @@ -145,29 +172,57 @@ int main(int argc, char **argv) if (read_fstree(&fs, fd, &super, cmp)) goto out_cmp; - if (lspath != NULL) { - tree_node_t *n = find_node(fs.root, lspath); - + switch (op) { + case OP_LS: + n = find_node(fs.root, cmdpath); if (n == NULL) { - perror(lspath); + perror(cmdpath); goto out_fs; } list_files(n); + break; + case OP_CAT: + n = find_node(fs.root, cmdpath); + if (n == NULL) { + perror(cmdpath); + goto out_fs; + } + + if (!S_ISREG(n->mode)) { + fprintf(stderr, "/%s: not a regular file\n", cmdpath); + goto out_fs; + } + + if (super.fragment_entry_count > 0 && + super.fragment_table_start < super.bytes_used && + !(super.flags & SQFS_FLAG_NO_FRAGMENTS)) { + frag = frag_reader_create(&super, fd, cmp); + if (frag == NULL) + goto out_fs; + } + + if (extract_file(n->data.file, cmp, super.block_size, + frag, fd, STDOUT_FILENO)) { + goto out_fs; + } + break; } status = EXIT_SUCCESS; out_fs: + if (frag != NULL) + frag_reader_destroy(frag); fstree_cleanup(&fs); out_cmp: cmp->destroy(cmp); out_fd: close(fd); out_cmd: - free(lspath); + free(cmdpath); return status; fail_arg: fprintf(stderr, "Try `%s --help' for more information.\n", __progname); - free(lspath); + free(cmdpath); return EXIT_FAILURE; } diff --git a/unpack/unsquashfs.h b/unpack/unsquashfs.h index 52b3889..0e8854b 100644 --- a/unpack/unsquashfs.h +++ b/unpack/unsquashfs.h @@ -3,6 +3,7 @@ #define UNSQUASHFS_H #include "meta_reader.h" +#include "frag_reader.h" #include "squashfs.h" #include "compress.h" #include "id_table.h" @@ -27,4 +28,7 @@ int read_fstree(fstree_t *out, int fd, sqfs_super_t *super, compressor_t *cmp); void list_files(tree_node_t *node); +int extract_file(file_info_t *fi, compressor_t *cmp, size_t block_size, + frag_reader_t *frag, int sqfsfd, int outfd); + #endif /* UNSQUASHFS_H */ -- cgit v1.2.3