summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/util.h2
-rw-r--r--lib/Makemodule.am2
-rw-r--r--lib/util/mkdir_p.c42
-rw-r--r--unpack/Makemodule.am1
-rw-r--r--unpack/restore_fstree.c105
-rw-r--r--unpack/unsquashfs.c37
-rw-r--r--unpack/unsquashfs.h3
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 */