From 9de5c93986bc6a4b33f3f50ed2a94c9aa3a33c7f Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Fri, 3 May 2019 21:44:59 +0200 Subject: unsquashfs: add code to unpack the entire file system Signed-off-by: David Oberhollenzer --- unpack/Makemodule.am | 1 + unpack/restore_fstree.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++ unpack/unsquashfs.c | 37 +++++++++++++---- unpack/unsquashfs.h | 3 ++ 4 files changed, 139 insertions(+), 7 deletions(-) create mode 100644 unpack/restore_fstree.c (limited to 'unpack') diff --git a/unpack/Makemodule.am b/unpack/Makemodule.am index a8fd2e7..7d71bf5 100644 --- a/unpack/Makemodule.am +++ b/unpack/Makemodule.am @@ -1,6 +1,7 @@ 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 unpack/extract_file.c +unsquashfs_SOURCES += unpack/restore_fstree.c unsquashfs_LDADD = libsquashfs.a libfstree.a libcompress.a libutil.a if WITH_LZMA diff --git a/unpack/restore_fstree.c b/unpack/restore_fstree.c new file mode 100644 index 0000000..56c3cc5 --- /dev/null +++ b/unpack/restore_fstree.c @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "unsquashfs.h" + +static int restore_directory(int dirfd, tree_node_t *n, compressor_t *cmp, + size_t block_size, frag_reader_t *frag, + int sqfsfd) +{ + int fd; + + for (n = n->data.dir->children; n != NULL; n = n->next) { + switch (n->mode & S_IFMT) { + case S_IFDIR: + if (mkdirat(dirfd, n->name, n->mode) && + errno != EEXIST) { + fprintf(stderr, "mkdir %s: %s\n", + n->name, strerror(errno)); + return -1; + } + + fd = openat(dirfd, n->name, O_RDONLY | O_DIRECTORY); + if (fd < 0) { + fprintf(stderr, "open dir %s: %s\n", + n->name, strerror(errno)); + return -1; + } + + if (restore_directory(fd, n, cmp, block_size, + frag, sqfsfd)) { + close(fd); + return -1; + } + + close(fd); + break; + case S_IFLNK: + if (symlinkat(n->data.slink_target, dirfd, n->name)) { + fprintf(stderr, "ln -s %s %s: %s\n", + n->data.slink_target, n->name, + strerror(errno)); + return -1; + } + break; + case S_IFSOCK: + case S_IFIFO: + if (mknodat(dirfd, n->name, n->mode, 0)) { + fprintf(stderr, "creating %s: %s\n", + n->name, strerror(errno)); + return -1; + } + break; + case S_IFBLK: + case S_IFCHR: + if (mknodat(dirfd, n->name, n->mode, n->data.devno)) { + fprintf(stderr, "creating device %s: %s\n", + n->name, strerror(errno)); + return -1; + } + break; + case S_IFREG: + fd = openat(dirfd, n->name, + O_WRONLY | O_CREAT | O_EXCL, n->mode); + if (fd < 0) { + fprintf(stderr, "creating %s: %s\n", + n->name, strerror(errno)); + return -1; + } + + if (extract_file(n->data.file, cmp, block_size, + frag, sqfsfd, fd)) { + close(fd); + return -1; + } + + close(fd); + break; + default: + break; + } + } + + return 0; +} + +int restore_fstree(const char *rootdir, tree_node_t *root, compressor_t *cmp, + size_t block_size, frag_reader_t *frag, int sqfsfd) +{ + int dirfd; + + if (mkdir_p(rootdir)) + return -1; + + dirfd = open(rootdir, O_RDONLY | O_DIRECTORY); + if (dirfd < 0) { + perror(rootdir); + return -1; + } + + if (restore_directory(dirfd, root, cmp, block_size, frag, sqfsfd)) { + close(dirfd); + return -1; + } + + close(dirfd); + return 0; +} diff --git a/unpack/unsquashfs.c b/unpack/unsquashfs.c index 90a7583..178def4 100644 --- a/unpack/unsquashfs.c +++ b/unpack/unsquashfs.c @@ -5,16 +5,18 @@ enum { OP_NONE = 0, OP_LS, OP_CAT, + OP_UNPACK, }; static struct option long_opts[] = { { "list", required_argument, NULL, 'l' }, { "cat", required_argument, NULL, 'c' }, + { "unpack-root", required_argument, NULL, 'u' }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, }; -static const char *short_opts = "l:hV"; +static const char *short_opts = "l:c:u:hV"; static const char *help_string = "Usage: %s [OPTIONS] \n" @@ -22,12 +24,14 @@ static const char *help_string = "View or extract the contents of a squashfs image.\n" "\n" "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" +" --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,\n" +" image, dump its contents to stdout.\n" +" --unpack-root Unpack the contents of the filesystem into the\n" +" specified path\n" +" --help, -h Print help text and exit.\n" +" --version, -V Print version information and exit.\n" "\n"; extern const char *__progname; @@ -89,6 +93,7 @@ static char *get_path(char *old, const char *arg) int main(int argc, char **argv) { int i, fd, status = EXIT_FAILURE, op = OP_NONE; + const char *unpack_root = NULL; frag_reader_t *frag = NULL; char *cmdpath = NULL; sqfs_super_t super; @@ -110,6 +115,10 @@ int main(int argc, char **argv) op = OP_LS; cmdpath = get_path(cmdpath, optarg); break; + case 'u': + op = OP_UNPACK; + unpack_root = optarg; + break; case 'h': printf(help_string, __progname); status = EXIT_SUCCESS; @@ -207,6 +216,20 @@ int main(int argc, char **argv) goto out_fs; } break; + case OP_UNPACK: + 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 (restore_fstree(unpack_root, fs.root, cmp, super.block_size, + frag, fd)) { + goto out_fs; + } + break; } status = EXIT_SUCCESS; diff --git a/unpack/unsquashfs.h b/unpack/unsquashfs.h index 0e8854b..5895fb8 100644 --- a/unpack/unsquashfs.h +++ b/unpack/unsquashfs.h @@ -31,4 +31,7 @@ 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); +int restore_fstree(const char *rootdir, tree_node_t *root, compressor_t *cmp, + size_t block_size, frag_reader_t *frag, int sqfsfd); + #endif /* UNSQUASHFS_H */ -- cgit v1.2.3