diff options
-rw-r--r-- | include/util.h | 2 | ||||
-rw-r--r-- | lib/Makemodule.am | 2 | ||||
-rw-r--r-- | lib/util/mkdir_p.c | 42 | ||||
-rw-r--r-- | unpack/Makemodule.am | 1 | ||||
-rw-r--r-- | unpack/restore_fstree.c | 105 | ||||
-rw-r--r-- | unpack/unsquashfs.c | 37 | ||||
-rw-r--r-- | unpack/unsquashfs.h | 3 |
7 files changed, 184 insertions, 8 deletions
diff --git a/include/util.h b/include/util.h index b1a98c0..1ec551c 100644 --- a/include/util.h +++ b/include/util.h @@ -12,4 +12,6 @@ ssize_t read_retry(int fd, void *buffer, size_t size); void print_version(void); +int mkdir_p(const char *path); + #endif /* UTIL_H */ diff --git a/lib/Makemodule.am b/lib/Makemodule.am index f23d9b7..691d6df 100644 --- a/lib/Makemodule.am +++ b/lib/Makemodule.am @@ -19,7 +19,7 @@ libsquashfs_a_SOURCES += include/frag_reader.h libutil_a_SOURCES = lib/util/canonicalize_name.c lib/util/write_retry.c libutil_a_SOURCES += lib/util/read_retry.c include/util.h -libutil_a_SOURCES += lib/util/print_version.c +libutil_a_SOURCES += lib/util/print_version.c lib/util/mkdir_p.c if WITH_ZLIB libcompress_a_SOURCES += lib/comp/zlib.c diff --git a/lib/util/mkdir_p.c b/lib/util/mkdir_p.c new file mode 100644 index 0000000..7d1e052 --- /dev/null +++ b/lib/util/mkdir_p.c @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <alloca.h> +#include <stdio.h> +#include <errno.h> + +#include "util.h" + +int mkdir_p(const char *path) +{ + size_t i, len; + char *buffer; + + while (path[0] == '/' && path[1] == '/') + ++path; + + if (*path == '\0' || (path[0] == '/' && path[1] == '\0')) + return 0; + + len = strlen(path) + 1; + buffer = alloca(len); + + for (i = 0; i < len; ++i) { + if (i > 0 && (path[i] == '/' || path[i] == '\0')) { + buffer[i] = '\0'; + + if (mkdir(buffer, 0755) != 0) { + if (errno != EEXIST) { + fprintf(stderr, "mkdir %s: %s\n", + buffer, strerror(errno)); + return -1; + } + } + } + + buffer[i] = path[i]; + } + + return 0; +} 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] <squashfs-file>\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 <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" +" --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,\n" +" image, dump its contents to stdout.\n" +" --unpack-root <path> 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 */ |