diff options
Diffstat (limited to 'tar/sqfs2tar.c')
-rw-r--r-- | tar/sqfs2tar.c | 208 |
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; +} |