aboutsummaryrefslogtreecommitdiff
path: root/unpack
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-05-03 15:59:35 +0200
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-05-03 21:45:46 +0200
commit4dc4c6bf50155550c0b83e433fef8c7c19462853 (patch)
treec46dabc7c757fac27b506194ce4d236c244ac938 /unpack
parent12c8196ad46808dc9d0e84b3a798509dcf1a41e7 (diff)
unsquashfs: add file data extraction
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'unpack')
-rw-r--r--unpack/Makemodule.am2
-rw-r--r--unpack/extract_file.c97
-rw-r--r--unpack/unsquashfs.c107
-rw-r--r--unpack/unsquashfs.h4
4 files changed, 183 insertions, 27 deletions
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 <path> Produce a directory listing for a given path in the\n"
" squashfs image.\n"
+" --cat, -c <path> 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] <filename>\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 */