summaryrefslogtreecommitdiff
path: root/tar/sqfs2tar.c
diff options
context:
space:
mode:
Diffstat (limited to 'tar/sqfs2tar.c')
-rw-r--r--tar/sqfs2tar.c208
1 files changed, 208 insertions, 0 deletions
diff --git a/tar/sqfs2tar.c b/tar/sqfs2tar.c
new file mode 100644
index 0000000..f7f7e71
--- /dev/null
+++ b/tar/sqfs2tar.c
@@ -0,0 +1,208 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+#include "meta_reader.h"
+#include "data_reader.h"
+#include "highlevel.h"
+#include "compress.h"
+#include "fstree.h"
+#include "util.h"
+#include "tar.h"
+
+#include <getopt.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+static struct option long_opts[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+};
+
+static const char *short_opts = "hV";
+
+static const char *usagestr =
+"Usage: sqfs2tar [OPTIONS...] <sqfsfile>\n"
+"\n"
+"Read an input squashfs archive and turn it into a tar archive, written\n"
+"to stdout.\n"
+"\n"
+"Possible options:\n"
+"\n"
+" --help, -h Print help text and exit.\n"
+" --version, -V Print version information and exit.\n"
+"\n"
+"Examples:\n"
+"\n"
+"\tsqfs2tar rootfs.sqfs > rootfs.tar\n"
+"\tsqfs2tar rootfs.sqfs | gzip > rootfs.tar.gz\n"
+"\tsqfs2tar rootfs.sqfs | xz > rootfs.tar.xz\n"
+"\n";
+
+static const char *filename;
+
+static void process_args(int argc, char **argv)
+{
+ int i;
+
+ for (;;) {
+ i = getopt_long(argc, argv, short_opts, long_opts, NULL);
+ if (i == -1)
+ break;
+
+ switch (i) {
+ case 'h':
+ fputs(usagestr, stdout);
+ exit(EXIT_SUCCESS);
+ case 'V':
+ print_version();
+ exit(EXIT_SUCCESS);
+ default:
+ goto fail_arg;
+ }
+ }
+
+ if (optind >= argc) {
+ fputs("Missing argument: squashfs image\n", stderr);
+ goto fail_arg;
+ }
+
+ filename = argv[optind++];
+
+ if (optind < argc) {
+ fputs("Unknown extra arguments\n", stderr);
+ goto fail_arg;
+ }
+ return;
+fail_arg:
+ fputs("Try `sqfs2tar --help' for more information.\n", stderr);
+ exit(EXIT_FAILURE);
+}
+
+static int terminate_archive(void)
+{
+ char buffer[1024];
+ ssize_t ret;
+
+ memset(buffer, '\0', sizeof(buffer));
+
+ ret = write_retry(STDOUT_FILENO, buffer, sizeof(buffer));
+
+ if (ret < 0) {
+ perror("adding archive terminator");
+ return -1;
+ }
+
+ if ((size_t)ret < sizeof(buffer)) {
+ fputs("adding archive terminator: truncated write\n", stderr);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int write_tree_dfs(fstree_t *fs, tree_node_t *n, data_reader_t *data)
+{
+ char *name;
+ int ret;
+
+ if (n->parent != NULL || !S_ISDIR(n->mode)) {
+ name = fstree_get_path(n);
+ if (name == NULL) {
+ perror("resolving tree node path");
+ return -1;
+ }
+
+ assert(canonicalize_name(name) == 0);
+
+ ret = write_tar_header(STDOUT_FILENO, fs, n, name);
+ free(name);
+
+ if (ret < 0)
+ return -1;
+ if (ret > 0)
+ return 0;
+
+ if (S_ISREG(n->mode)) {
+ if (data_reader_dump_file(data, n->data.file,
+ STDOUT_FILENO)) {
+ return -1;
+ }
+
+ if (padd_file(STDOUT_FILENO, n->data.file->size, 512))
+ return -1;
+ }
+ }
+
+ if (S_ISDIR(n->mode)) {
+ for (n = n->data.dir->children; n != NULL; n = n->next) {
+ if (write_tree_dfs(fs, n, data))
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ data_reader_t *data = NULL;
+ int status = EXIT_FAILURE;
+ sqfs_super_t super;
+ compressor_t *cmp;
+ fstree_t fs;
+ int sqfsfd;
+
+ process_args(argc, argv);
+
+ sqfsfd = open(filename, O_RDONLY);
+ if (sqfsfd < 0) {
+ perror(filename);
+ return EXIT_FAILURE;
+ }
+
+ if (sqfs_super_read(&super, sqfsfd))
+ goto out_fd;
+
+ if (!compressor_exists(super.compression_id)) {
+ fputs("Image uses a compressor that has not been built in\n",
+ stderr);
+ goto out_fd;
+ }
+
+ cmp = compressor_create(super.compression_id, false,
+ super.block_size, NULL);
+ if (cmp == NULL)
+ goto out_fd;
+
+ if (super.flags & SQFS_FLAG_COMPRESSOR_OPTIONS) {
+ if (cmp->read_options(cmp, sqfsfd))
+ goto out_cmp;
+ }
+
+ if (deserialize_fstree(&fs, &super, cmp, sqfsfd, 0))
+ goto out_cmp;
+
+ data = data_reader_create(sqfsfd, &super, cmp);
+ if (data == NULL)
+ goto out_fs;
+
+ if (write_tree_dfs(&fs, fs.root, data))
+ goto out_data;
+
+ if (terminate_archive())
+ goto out_data;
+
+ status = EXIT_SUCCESS;
+out_data:
+ data_reader_destroy(data);
+out_fs:
+ fstree_cleanup(&fs);
+out_cmp:
+ cmp->destroy(cmp);
+out_fd:
+ close(sqfsfd);
+ return status;
+}