From bfd932fb3470fa7250359da6ed5641182a10077c Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Sat, 22 Jul 2023 22:26:21 +0200 Subject: sqfs2tar: replace hierarchy extraction with nested iterators Signed-off-by: David Oberhollenzer --- bin/sqfs2tar/src/iterator.c | 394 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 394 insertions(+) create mode 100644 bin/sqfs2tar/src/iterator.c (limited to 'bin/sqfs2tar/src/iterator.c') diff --git a/bin/sqfs2tar/src/iterator.c b/bin/sqfs2tar/src/iterator.c new file mode 100644 index 0000000..42f01e2 --- /dev/null +++ b/bin/sqfs2tar/src/iterator.c @@ -0,0 +1,394 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * iterator.c + * + * Copyright (C) 2023 David Oberhollenzer + */ +#include "sqfs2tar.h" + +enum { + STATE_INITIALIZED = 0, + STATE_ROOT = 1, + STATE_ENTRY = 2, + STATE_EOF = 3, +}; + +typedef struct { + sqfs_dir_iterator_t base; + + sqfs_super_t super; + + sqfs_dir_iterator_t *src; + sqfs_inode_generic_t *root; + sqfs_xattr_t *root_xattr; + sqfs_u32 root_uid; + sqfs_u32 root_gid; + + int state; +} iterator_t; + +static sqfs_dir_entry_t *create_root_entry(iterator_t *it) +{ + size_t nlen = strlen(root_becomes); + sqfs_dir_entry_t *ent = calloc(1, sizeof(*ent) + nlen + 2); + + if (ent == NULL) + return NULL; + + memcpy(ent->name, root_becomes, nlen); + ent->name[nlen] = '/'; + + if (it->root->base.type == SQFS_INODE_EXT_DIR) { + ent->size = it->root->data.dir_ext.size; + } else { + ent->size = it->root->data.dir.size; + } + + ent->mtime = it->root->base.mod_time; + ent->inode = it->super.root_inode_ref; + ent->mode = it->root->base.mode; + ent->uid = it->root_uid; + ent->gid = it->root_gid; + return ent; +} + +static bool keep_entry(const sqfs_dir_entry_t *ent) +{ + size_t nlen; + + if (num_subdirs == 0) + return true; + + nlen = strlen(ent->name); + + for (size_t i = 0; i < num_subdirs; ++i) { + size_t plen = strlen(subdirs[i]); + + if (nlen <= plen) { + if ((nlen == plen || subdirs[i][nlen] == '/') && + strncmp(subdirs[i], ent->name, nlen) == 0) { + return true; + } + } else if (ent->name[plen] == '/' && + strncmp(subdirs[i], ent->name, plen) == 0) { + return true; + } + } + + return false; +} + +static void destroy(sqfs_object_t *obj) +{ + iterator_t *it = (iterator_t *)obj; + + sqfs_xattr_list_free(it->root_xattr); + sqfs_free(it->root); + sqfs_drop(it->src); + free(it); +} + +static int next(sqfs_dir_iterator_t *base, sqfs_dir_entry_t **out) +{ + iterator_t *it = (iterator_t *)base; + sqfs_dir_entry_t *ent; + + *out = NULL; + + switch (it->state) { + case STATE_INITIALIZED: + *out = create_root_entry(it); + if (*out == NULL) { + it->state = SQFS_ERROR_ALLOC; + return SQFS_ERROR_ALLOC; + } + it->state = STATE_ROOT; + return 0; + case STATE_ROOT: + it->state = STATE_ENTRY; + break; + case STATE_ENTRY: + break; + case STATE_EOF: + return 1; + default: + return it->state; + } + + for (;;) { + int ret = it->src->next(it->src, &ent); + if (ret != 0) { + it->state = ret < 0 ? ret : STATE_EOF; + return ret; + } + + if (keep_entry(ent)) { + /* XXX: skip the entry, but we MUST recurse here! */ + if (num_subdirs == 1 && !keep_as_dir && + strlen(ent->name) <= strlen(subdirs[0])) { + sqfs_free(ent); + continue; + } + break; + } + + if (S_ISDIR(ent->mode)) + it->src->ignore_subdir(it->src); + + sqfs_free(ent); + } + + if (num_subdirs == 1 && !keep_as_dir) { + size_t plen = strlen(subdirs[0]) + 1; + + memmove(ent->name, ent->name + plen, + strlen(ent->name + plen) + 1); + } + + if (root_becomes != NULL) { + size_t nlen = strlen(ent->name); + size_t rlen = strlen(root_becomes); + void *new = realloc(ent, sizeof(*ent) + nlen + rlen + 2); + if (new == NULL) + goto fail_alloc; + + ent = new; + memmove(ent->name + rlen + 1, ent->name, nlen + 1); + memcpy(ent->name, root_becomes, rlen); + ent->name[rlen] = '/'; + } + + if (S_ISDIR(ent->mode)) { + size_t nlen = strlen(ent->name); + void *new = realloc(ent, sizeof(*ent) + nlen + 2); + if (new == NULL) + goto fail_alloc; + + ent = new; + ent->name[nlen++] = '/'; + ent->name[nlen ] = '\0'; + } + + *out = ent; + return 0; +fail_alloc: + sqfs_free(ent); + it->state = SQFS_ERROR_ALLOC; + return SQFS_ERROR_ALLOC; +} + +static int read_link(sqfs_dir_iterator_t *base, char **out) +{ + iterator_t *it = (iterator_t *)base; + + if (it->state != STATE_ENTRY) + return it->state < 0 ? it->state : SQFS_ERROR_NO_ENTRY; + + return it->src->read_link(it->src, out); +} + +static int open_file_ro(sqfs_dir_iterator_t *base, sqfs_istream_t **out) +{ + iterator_t *it = (iterator_t *)base; + + if (it->state != STATE_ENTRY) + return it->state < 0 ? it->state : SQFS_ERROR_NO_ENTRY; + + return it->src->open_file_ro(it->src, out); +} + +static int read_xattr(sqfs_dir_iterator_t *base, sqfs_xattr_t **out) +{ + iterator_t *it = (iterator_t *)base; + + if (it->state == STATE_ROOT) { + if (it->root_xattr != NULL) { + *out = sqfs_xattr_list_copy(it->root_xattr); + if (*out == NULL) + return SQFS_ERROR_ALLOC; + } else { + *out = NULL; + } + return 0; + } + + if (it->state != STATE_ENTRY) + return it->state < 0 ? it->state : SQFS_ERROR_NO_ENTRY; + + return it->src->read_xattr(it->src, out); +} + +sqfs_dir_iterator_t *tar_compat_iterator_create(const char *filename) +{ + sqfs_dir_iterator_t *base = NULL; + sqfs_id_table_t *idtbl = NULL; + sqfs_compressor_t *cmp = NULL; + sqfs_dir_reader_t *dr = NULL; + sqfs_data_reader_t *data = NULL; + sqfs_xattr_reader_t *xr = NULL; + sqfs_compressor_config_t cfg; + sqfs_file_t *file = NULL; + iterator_t *it = NULL; + int ret; + + it = calloc(1, sizeof(*it)); + if (it == NULL) { + perror("tar iterator"); + return NULL; + } + + /* open the file and read the super block */ + ret = sqfs_file_open(&file, filename, SQFS_FILE_OPEN_READ_ONLY); + if (ret) { + sqfs_perror(filename, NULL, ret); + goto fail; + } + + ret = sqfs_super_read(&it->super, file); + if (ret) { + sqfs_perror(filename, "reading super block", ret); + goto fail; + } + + /* create a compressor */ + sqfs_compressor_config_init(&cfg, it->super.compression_id, + it->super.block_size, + SQFS_COMP_FLAG_UNCOMPRESS); + + ret = sqfs_compressor_create(&cfg, &cmp); + +#ifdef WITH_LZO + if (it->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 fail; + } + + /* create a directory reader */ + dr = sqfs_dir_reader_create(&it->super, cmp, file, 0); + if (dr == NULL) { + sqfs_perror(filename, "creating dir reader", + SQFS_ERROR_ALLOC); + goto fail; + } + + /* load ID table */ + idtbl = sqfs_id_table_create(0); + if (idtbl == NULL) { + ret = SQFS_ERROR_ALLOC; + } else { + ret = sqfs_id_table_read(idtbl, file, &it->super, cmp); + } + + if (ret) { + sqfs_perror(filename, "loading ID table", ret); + goto fail; + } + + /* create data reader */ + data = sqfs_data_reader_create(file, it->super.block_size, cmp, 0); + if (data == NULL) { + sqfs_perror(filename, "creating data reader", + SQFS_ERROR_ALLOC); + goto fail; + } + + ret = sqfs_data_reader_load_fragment_table(data, &it->super); + if (ret) { + sqfs_perror(filename, "loading fragment table", ret); + goto fail; + } + + /* create xattr reader */ + if (!no_xattr && !(it->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 fail; + } + + ret = sqfs_xattr_reader_load(xr, &it->super, file, cmp); + if (ret) { + sqfs_perror(filename, "loading xattr table", ret); + goto fail; + } + } + + /* get the root inode */ + ret = sqfs_dir_reader_get_root_inode(dr, &it->root); + if (ret) { + sqfs_perror(filename, "reading root inode", ret); + goto fail; + } + + /* create a recursive directory iterator from there */ + ret = sqfs_dir_iterator_create(dr, idtbl, data, xr, it->root, &base); + if (ret) { + sqfs_perror(filename, "opening root directory", ret); + goto fail; + } + + ret = sqfs_dir_iterator_create_recursive(&it->src, base); + base = sqfs_drop(base); + if (ret) { + sqfs_perror(filename, "creating directory scanner", ret); + goto fail; + } + + /* get root attributes if we need them */ + if (root_becomes == NULL) { + sqfs_free(it->root); + it->root = NULL; + + it->state = STATE_ENTRY; + } else { + sqfs_u32 idx; + + ret = sqfs_id_table_index_to_id(idtbl, it->root->base.uid_idx, + &it->root_uid); + if (ret == 0) { + ret = sqfs_id_table_index_to_id(idtbl, + it->root->base.gid_idx, + &it->root_gid); + } + + if (ret == 0) + ret = sqfs_inode_get_xattr_index(it->root, &idx); + + if (xr != NULL && ret == 0) + ret = sqfs_xattr_reader_read_all(xr, idx, + &it->root_xattr); + if (ret) { + sqfs_perror(filename, "reading root inode attributes", + ret); + goto fail; + } + + it->state = STATE_INITIALIZED; + } + + /* finish up initialization */ + sqfs_object_init(it, destroy, NULL); + ((sqfs_dir_iterator_t *)it)->next = next; + ((sqfs_dir_iterator_t *)it)->read_link = read_link; + ((sqfs_dir_iterator_t *)it)->open_file_ro = open_file_ro; + ((sqfs_dir_iterator_t *)it)->read_xattr = read_xattr; +out: + xr = sqfs_drop(xr); + dr = sqfs_drop(dr); + cmp = sqfs_drop(cmp); + file = sqfs_drop(file); + idtbl = sqfs_drop(idtbl); + data = sqfs_drop(data); + return (sqfs_dir_iterator_t *)it; +fail: + sqfs_free(it->root); + sqfs_drop(it->src); + free(it); + it = NULL; + goto out; +} -- cgit v1.2.3