diff options
Diffstat (limited to 'bin/sqfs2tar/src/sqfs2tar.c')
-rw-r--r-- | bin/sqfs2tar/src/sqfs2tar.c | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/bin/sqfs2tar/src/sqfs2tar.c b/bin/sqfs2tar/src/sqfs2tar.c new file mode 100644 index 0000000..43f9e78 --- /dev/null +++ b/bin/sqfs2tar/src/sqfs2tar.c @@ -0,0 +1,274 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * sqfs2tar.c + * + * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at> + */ +#include "sqfs2tar.h" + +sqfs_xattr_reader_t *xr; +sqfs_data_reader_t *data; +sqfs_super_t super; +ostream_t *out_file = NULL; + +static sqfs_file_t *file; + +char *assemble_tar_path(char *name, bool is_dir) +{ + size_t len, new_len; + char *temp; + (void)is_dir; + + if (root_becomes == NULL && !is_dir) + return name; + + new_len = strlen(name); + if (root_becomes != NULL) + new_len += strlen(root_becomes) + 1; + if (is_dir) + new_len += 1; + + temp = realloc(name, new_len + 1); + if (temp == NULL) { + perror("assembling tar entry filename"); + free(name); + return NULL; + } + + name = temp; + + if (root_becomes != NULL) { + len = strlen(root_becomes); + + memmove(name + len + 1, name, strlen(name) + 1); + memcpy(name, root_becomes, len); + name[len] = '/'; + } + + if (is_dir) { + len = strlen(name); + + if (len == 0 || name[len - 1] != '/') { + name[len++] = '/'; + name[len] = '\0'; + } + } + + return name; +} + +static int terminate_archive(void) +{ + char buffer[1024]; + + memset(buffer, '\0', sizeof(buffer)); + + return ostream_append(out_file, buffer, sizeof(buffer)); +} + +static sqfs_tree_node_t *tree_merge(sqfs_tree_node_t *lhs, + sqfs_tree_node_t *rhs) +{ + sqfs_tree_node_t *head = NULL, **next_ptr = &head; + sqfs_tree_node_t *it, *l, *r; + int diff; + + while (lhs->children != NULL && rhs->children != NULL) { + diff = strcmp((const char *)lhs->children->name, + (const char *)rhs->children->name); + + if (diff < 0) { + it = lhs->children; + lhs->children = lhs->children->next; + } else if (diff > 0) { + it = rhs->children; + rhs->children = rhs->children->next; + } else { + l = lhs->children; + lhs->children = lhs->children->next; + + r = rhs->children; + rhs->children = rhs->children->next; + + it = tree_merge(l, r); + } + + *next_ptr = it; + next_ptr = &it->next; + } + + it = (lhs->children != NULL ? lhs->children : rhs->children); + *next_ptr = it; + + sqfs_dir_tree_destroy(rhs); + lhs->children = head; + return lhs; +} + +int main(int argc, char **argv) +{ + sqfs_tree_node_t *root = NULL, *subtree = NULL; + int flags, ret, status = EXIT_FAILURE; + sqfs_compressor_t *cmp = NULL; + sqfs_id_table_t *idtbl = NULL; + sqfs_dir_reader_t *dr = NULL; + sqfs_compressor_config_t cfg; + size_t i; + + process_args(argc, argv); + + out_file = ostream_open_stdout(); + if (out_file == NULL) { + perror("changing stdout to binary mode"); + goto out; + } + + if (compressor > 0) { + xfrm_stream_t *xfrm = compressor_stream_create(compressor,NULL); + ostream_t *strm; + + if (xfrm == NULL) + goto out; + + strm = ostream_xfrm_create(out_file, xfrm); + sqfs_drop(out_file); + sqfs_drop(xfrm); + out_file = strm; + + if (out_file == NULL) + goto out; + } + + file = sqfs_open_file(filename, SQFS_FILE_OPEN_READ_ONLY); + if (file == NULL) { + perror(filename); + goto out; + } + + ret = sqfs_super_read(&super, file); + if (ret) { + sqfs_perror(filename, "reading super block", ret); + goto out; + } + + sqfs_compressor_config_init(&cfg, super.compression_id, + super.block_size, + SQFS_COMP_FLAG_UNCOMPRESS); + + ret = sqfs_compressor_create(&cfg, &cmp); + +#ifdef WITH_LZO + if (super.compression_id == SQFS_COMP_LZO && ret != 0) + ret = lzo_compressor_create(&cfg, &cmp); +#endif + + if (ret != 0) { + sqfs_perror(filename, "creating compressor", ret); + goto out; + } + + idtbl = sqfs_id_table_create(0); + + if (idtbl == NULL) { + perror("creating ID table"); + goto out; + } + + ret = sqfs_id_table_read(idtbl, file, &super, cmp); + if (ret) { + sqfs_perror(filename, "loading ID table", ret); + goto out; + } + + data = sqfs_data_reader_create(file, super.block_size, cmp, 0); + if (data == NULL) { + sqfs_perror(filename, "creating data reader", + SQFS_ERROR_ALLOC); + goto out; + } + + ret = sqfs_data_reader_load_fragment_table(data, &super); + if (ret) { + sqfs_perror(filename, "loading fragment table", ret); + goto out; + } + + dr = sqfs_dir_reader_create(&super, cmp, file, 0); + if (dr == NULL) { + sqfs_perror(filename, "creating dir reader", + SQFS_ERROR_ALLOC); + goto out; + } + + if (!no_xattr && !(super.flags & SQFS_FLAG_NO_XATTRS)) { + xr = sqfs_xattr_reader_create(0); + if (xr == NULL) { + sqfs_perror(filename, "creating xattr reader", + SQFS_ERROR_ALLOC); + goto out; + } + + ret = sqfs_xattr_reader_load(xr, &super, file, cmp); + if (ret) { + sqfs_perror(filename, "loading xattr table", ret); + goto out; + } + } + + if (num_subdirs == 0) { + ret = sqfs_dir_reader_get_full_hierarchy(dr, idtbl, NULL, + 0, &root); + if (ret) { + sqfs_perror(filename, "loading filesystem tree", ret); + goto out; + } + } else { + flags = 0; + + if (keep_as_dir || num_subdirs > 1) + flags = SQFS_TREE_STORE_PARENTS; + + for (i = 0; i < num_subdirs; ++i) { + ret = sqfs_dir_reader_get_full_hierarchy(dr, idtbl, + subdirs[i], + flags, + &subtree); + if (ret) { + sqfs_perror(subdirs[i], "loading filesystem " + "tree", ret); + goto out; + } + + if (root == NULL) { + root = subtree; + } else { + root = tree_merge(root, subtree); + } + } + } + + if (write_tree(root)) + goto out; + + if (terminate_archive()) + goto out; + + if (ostream_flush(out_file)) + goto out; + + status = EXIT_SUCCESS; +out: + sqfs_dir_tree_destroy(root); + sqfs_drop(xr); + sqfs_drop(dr); + sqfs_drop(data); + sqfs_drop(idtbl); + sqfs_drop(cmp); + sqfs_drop(file); + sqfs_drop(out_file); + for (i = 0; i < num_subdirs; ++i) + free(subdirs[i]); + free(subdirs); + free(root_becomes); + return status; +} |