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/Makemodule.am | 2 +- bin/sqfs2tar/src/iterator.c | 394 ++++++++++++++++++++++++++++++++++++++++++ bin/sqfs2tar/src/sqfs2tar.c | 287 +++++++++++------------------- bin/sqfs2tar/src/sqfs2tar.h | 12 +- bin/sqfs2tar/src/write_tree.c | 236 ------------------------- include/common.h | 9 - lib/common/Makemodule.am | 2 +- lib/common/src/hardlink.c | 101 ----------- 8 files changed, 501 insertions(+), 542 deletions(-) create mode 100644 bin/sqfs2tar/src/iterator.c delete mode 100644 bin/sqfs2tar/src/write_tree.c delete mode 100644 lib/common/src/hardlink.c diff --git a/bin/sqfs2tar/Makemodule.am b/bin/sqfs2tar/Makemodule.am index beb373e..abd1c3b 100644 --- a/bin/sqfs2tar/Makemodule.am +++ b/bin/sqfs2tar/Makemodule.am @@ -1,5 +1,5 @@ sqfs2tar_SOURCES = bin/sqfs2tar/src/sqfs2tar.c bin/sqfs2tar/src/sqfs2tar.h \ - bin/sqfs2tar/src/options.c bin/sqfs2tar/src/write_tree.c + bin/sqfs2tar/src/options.c bin/sqfs2tar/src/iterator.c sqfs2tar_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) sqfs2tar_LDADD = libcommon.a libutil.a libtar.a libio.a libsquashfs.la sqfs2tar_LDADD += libxfrm.a libcompat.a libfstree.a 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; +} diff --git a/bin/sqfs2tar/src/sqfs2tar.c b/bin/sqfs2tar/src/sqfs2tar.c index e21cd7a..786182c 100644 --- a/bin/sqfs2tar/src/sqfs2tar.c +++ b/bin/sqfs2tar/src/sqfs2tar.c @@ -6,113 +6,96 @@ */ #include "sqfs2tar.h" -sqfs_xattr_reader_t *xr; -sqfs_data_reader_t *data; -sqfs_super_t super; -sqfs_ostream_t *out_file = NULL; +static sqfs_ostream_t *out_file = NULL; -static sqfs_file_t *file; - -char *assemble_tar_path(char *name, bool is_dir) +static int terminate_archive(void) { - 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; + char buffer[1024]; - if (root_becomes != NULL) { - len = strlen(root_becomes); + memset(buffer, '\0', sizeof(buffer)); - memmove(name + len + 1, name, strlen(name) + 1); - memcpy(name, root_becomes, len); - name[len] = '/'; - } + return out_file->append(out_file, buffer, sizeof(buffer)); +} - if (is_dir) { - len = strlen(name); +static int write_file_data(sqfs_dir_iterator_t *it, const sqfs_dir_entry_t *ent) +{ + sqfs_istream_t *in; + int ret; - if (len == 0 || name[len - 1] != '/') { - name[len++] = '/'; - name[len] = '\0'; - } - } + ret = it->open_file_ro(it, &in); + if (ret) + return ret; - return name; -} + do { + ret = sqfs_istream_splice(in, out_file, + SQFS_DEFAULT_BLOCK_SIZE); + } while (ret > 0); -static int terminate_archive(void) -{ - char buffer[1024]; + in = sqfs_drop(in); - memset(buffer, '\0', sizeof(buffer)); + if (ret == 0) + ret = padd_file(out_file, ent->size); - return out_file->append(out_file, buffer, sizeof(buffer)); + return ret; } -static sqfs_tree_node_t *tree_merge(sqfs_tree_node_t *lhs, - sqfs_tree_node_t *rhs) +static int write_entry(sqfs_dir_iterator_t *it, const sqfs_dir_entry_t *ent) { - 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); + static unsigned int record_counter; + sqfs_xattr_t *xattr = NULL; + char *target = NULL; + struct stat sb; + int ret; + + if (S_ISLNK(ent->mode) || + (ent->flags & SQFS_DIR_ENTRY_FLAG_HARD_LINK)) { + ret = it->read_link(it, &target); + + if (ret != 0) { + sqfs_perror(ent->name, "reading link target", ret); + return ret; } + } - *next_ptr = it; - next_ptr = &it->next; + ret = it->read_xattr(it, &xattr); + if (ret != 0) { + sqfs_perror(ent->name, "reading xattr data", ret); + sqfs_free(target); + return ret; + } + + memset(&sb, 0, sizeof(sb)); + sb.st_mode = ent->mode; + sb.st_uid = ent->uid; + sb.st_gid = ent->gid; + sb.st_mtime = ent->mtime; + sb.st_rdev = ent->rdev; + sb.st_size = ent->size; + + if (ent->flags & SQFS_DIR_ENTRY_FLAG_HARD_LINK) { + ret = write_hard_link(out_file, &sb, ent->name, target, + record_counter++); + if (ret) + sqfs_perror(ent->name, "writing tar hard link", ret); + } else { + ret = write_tar_header(out_file, &sb, ent->name, target, + xattr, record_counter++); + if (ret) + sqfs_perror(ent->name, "writing tar header", ret); } - it = (lhs->children != NULL ? lhs->children : rhs->children); - *next_ptr = it; + if (S_ISREG(ent->mode) && ret == 0) + ret = write_file_data(it, ent); - sqfs_dir_tree_destroy(rhs); - lhs->children = head; - return lhs; + sqfs_xattr_list_free(xattr); + sqfs_free(target); + return ret; } 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; + int ret, status = EXIT_FAILURE; + sqfs_dir_iterator_t *it = NULL; size_t i; process_args(argc, argv); @@ -139,117 +122,59 @@ int main(int argc, char **argv) goto out; } - ret = sqfs_file_open(&file, filename, SQFS_FILE_OPEN_READ_ONLY); - if (ret) { - sqfs_perror(filename, "open", ret); - 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); + it = tar_compat_iterator_create(filename); + if (it == NULL) goto out; - } - idtbl = sqfs_id_table_create(0); + if (!no_links) { + sqfs_dir_iterator_t *hl; - if (idtbl == NULL) { - perror("creating ID table"); - goto out; - } + ret = sqfs_hard_link_filter_create(&hl, it); + it = sqfs_drop(it); - 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; - } + if (ret != 0) { + sqfs_perror(filename, "creating hard link filter", ret); + goto out; + } - ret = sqfs_data_reader_load_fragment_table(data, &super); - if (ret) { - sqfs_perror(filename, "loading fragment table", ret); - goto out; + it = hl; } - dr = sqfs_dir_reader_create(&super, cmp, file, 0); - if (dr == NULL) { - sqfs_perror(filename, "creating dir reader", - SQFS_ERROR_ALLOC); - goto out; - } + for (;;) { + sqfs_dir_entry_t *ent; - 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); + ret = it->next(it, &ent); + if (ret > 0) + break; + if (ret < 0) { + sqfs_perror(filename, "reading directory entry", ret); goto out; } - ret = sqfs_xattr_reader_load(xr, &super, file, cmp); - if (ret) { - sqfs_perror(filename, "loading xattr table", ret); - goto out; + ret = write_entry(it, ent); + if (ret == SQFS_ERROR_UNSUPPORTED) { + fprintf(stderr, "WARNING: %s: unsupported file type\n", + ent->name); + if (dont_skip) { + fputs("Not allowed to skip files, aborting!\n", + stderr); + sqfs_free(ent); + goto out; + } + fprintf(stderr, "Skipping %s\n", ent->name); + sqfs_free(ent); + continue; } - } - 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); + sqfs_perror(ent->name, NULL, ret); + sqfs_free(ent); 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); - } - } + sqfs_free(ent); } - if (write_tree(root)) - goto out; - if (terminate_archive()) goto out; @@ -261,13 +186,7 @@ int main(int argc, char **argv) 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(it); sqfs_drop(out_file); for (i = 0; i < num_subdirs; ++i) free(subdirs[i]); diff --git a/bin/sqfs2tar/src/sqfs2tar.h b/bin/sqfs2tar/src/sqfs2tar.h index bb07092..a1a5927 100644 --- a/bin/sqfs2tar/src/sqfs2tar.h +++ b/bin/sqfs2tar/src/sqfs2tar.h @@ -38,15 +38,7 @@ extern const char *filename; void process_args(int argc, char **argv); -/* tar2sqfs.c */ -extern sqfs_xattr_reader_t *xr; -extern sqfs_data_reader_t *data; -extern sqfs_super_t super; -extern sqfs_ostream_t *out_file; - -char *assemble_tar_path(char *name, bool is_dir); - -/* write_tree.c */ -int write_tree(const sqfs_tree_node_t *n); +/* iterator.c */ +sqfs_dir_iterator_t *tar_compat_iterator_create(const char *filename); #endif /* SQFS2TAR_H */ diff --git a/bin/sqfs2tar/src/write_tree.c b/bin/sqfs2tar/src/write_tree.c deleted file mode 100644 index ed82173..0000000 --- a/bin/sqfs2tar/src/write_tree.c +++ /dev/null @@ -1,236 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * write_tree.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "sqfs2tar.h" - -static sqfs_hard_link_t *links = NULL; -static unsigned int record_counter; - -static sqfs_hard_link_t *find_hard_link(const char *name, sqfs_u32 inum) -{ - sqfs_hard_link_t *lnk = NULL; - - for (lnk = links; lnk != NULL; lnk = lnk->next) { - if (lnk->inode_number == inum) { - if (strcmp(name, lnk->target) == 0) - lnk = NULL; - break; - } - } - - return lnk; -} - -static void inode_stat(const sqfs_tree_node_t *node, struct stat *sb) -{ - memset(sb, 0, sizeof(*sb)); - - sb->st_mode = node->inode->base.mode; - sb->st_uid = node->uid; - sb->st_gid = node->gid; - sb->st_mtime = node->inode->base.mod_time; - - switch (node->inode->base.type) { - case SQFS_INODE_BDEV: - case SQFS_INODE_CDEV: - sb->st_rdev = node->inode->data.dev.devno; - break; - case SQFS_INODE_EXT_BDEV: - case SQFS_INODE_EXT_CDEV: - sb->st_rdev = node->inode->data.dev_ext.devno; - break; - case SQFS_INODE_SLINK: - sb->st_size = node->inode->data.slink.target_size; - break; - case SQFS_INODE_EXT_SLINK: - sb->st_size = node->inode->data.slink_ext.target_size; - break; - case SQFS_INODE_FILE: - sb->st_size = node->inode->data.file.file_size; - break; - case SQFS_INODE_EXT_FILE: - sb->st_size = node->inode->data.file_ext.file_size; - break; - case SQFS_INODE_DIR: - sb->st_size = node->inode->data.dir.size; - break; - case SQFS_INODE_EXT_DIR: - sb->st_size = node->inode->data.dir_ext.size; - break; - default: - break; - } -} - -static int write_tree_dfs(const sqfs_tree_node_t *n) -{ - sqfs_hard_link_t *lnk = NULL; - sqfs_xattr_t *xattr = NULL; - char *name, *target; - struct stat sb; - size_t len; - int ret; - - inode_stat(n, &sb); - - if (n->parent == NULL) { - if (root_becomes == NULL) - goto skip_hdr; - - len = strlen(root_becomes); - name = malloc(len + 2); - if (name == NULL) { - perror("creating root directory"); - return -1; - } - - memcpy(name, root_becomes, len); - name[len] = '/'; - name[len + 1] = '\0'; - } else { - if (!is_filename_sane((const char *)n->name, false)) { - fprintf(stderr, "Found a file named '%s', skipping.\n", - n->name); - if (dont_skip) { - fputs("Not allowed to skip files, aborting!\n", - stderr); - return -1; - } - return 0; - } - - ret = sqfs_tree_node_get_path(n, &name); - if (ret != 0) { - sqfs_perror(NULL, "resolving tree node path", ret); - return -1; - } - - if (canonicalize_name(name)) - goto out_skip; - - name = assemble_tar_path(name, S_ISDIR(sb.st_mode)); - if (name == NULL) - return -1; - - lnk = find_hard_link(name, n->inode->base.inode_number); - if (lnk != NULL) { - ret = write_hard_link(out_file, &sb, name, lnk->target, - record_counter++); - if (ret != 0) - sqfs_perror(name, "writing hard link", ret); - sqfs_free(name); - return ret; - } - } - - if (!no_xattr && xr != NULL) { - sqfs_u32 index; - int ret; - - sqfs_inode_get_xattr_index(n->inode, &index); - - ret = sqfs_xattr_reader_read_all(xr, index, &xattr); - if (ret) { - sqfs_perror(name, "resolving xattr index", ret); - sqfs_free(name); - return -1; - } - } - - target = S_ISLNK(sb.st_mode) ? (char *)n->inode->extra : NULL; - ret = write_tar_header(out_file, &sb, name, target, xattr, - record_counter++); - sqfs_xattr_list_free(xattr); - - if (ret == SQFS_ERROR_UNSUPPORTED) { - fprintf(stderr, "WARNING: %s: unsupported file type\n", name); - goto out_skip; - } - - if (ret < 0) { - sqfs_perror(name, "writing tar header", ret); - sqfs_free(name); - return -1; - } - - if (S_ISREG(sb.st_mode)) { - sqfs_istream_t *in; - int ret; - - ret = sqfs_data_reader_create_stream(data, n->inode, name, &in); - if (ret) { - sqfs_perror(name, NULL, ret); - sqfs_free(name); - return -1; - } - - do { - ret = sqfs_istream_splice(in, out_file, - super.block_size); - } while (ret > 0); - - sqfs_drop(in); - - if (ret == 0) - ret = padd_file(out_file, sb.st_size); - - if (ret) { - sqfs_perror(name, NULL, ret); - sqfs_free(name); - return -1; - } - } - - sqfs_free(name); -skip_hdr: - for (n = n->children; n != NULL; n = n->next) { - if (write_tree_dfs(n)) - return -1; - } - return 0; -out_skip: - if (dont_skip) { - fputs("Not allowed to skip files, aborting!\n", stderr); - ret = -1; - } else { - fprintf(stderr, "Skipping %s\n", name); - ret = 0; - } - sqfs_free(name); - return ret; -} - -int write_tree(const sqfs_tree_node_t *n) -{ - sqfs_hard_link_t *lnk; - int status = -1; - - if (!no_links) { - int ret = sqfs_tree_find_hard_links(n, &links); - if (ret) { - sqfs_perror(NULL, "detecting hard links in " - "file system tree", ret); - return -1; - } - - for (lnk = links; lnk != NULL; lnk = lnk->next) { - lnk->target = assemble_tar_path(lnk->target, false); - - if (lnk->target == NULL) - goto out_links; - } - } - - status = write_tree_dfs(n); -out_links: - while (links != NULL) { - lnk = links; - links = links->next; - sqfs_free(lnk->target); - free(lnk); - } - return status; -} diff --git a/include/common.h b/include/common.h index ca95ba6..ab6fccf 100644 --- a/include/common.h +++ b/include/common.h @@ -28,17 +28,8 @@ #include -typedef struct sqfs_hard_link_t { - struct sqfs_hard_link_t *next; - sqfs_u32 inode_number; - char *target; -} sqfs_hard_link_t; - void sqfs_perror(const char *file, const char *action, int error_code); -int sqfs_tree_find_hard_links(const sqfs_tree_node_t *root, - sqfs_hard_link_t **out); - /* A common implementation of the '--version' command line flag. */ void print_version(const char *progname); diff --git a/lib/common/Makemodule.am b/lib/common/Makemodule.am index 5549cc3..7c07d48 100644 --- a/lib/common/Makemodule.am +++ b/lib/common/Makemodule.am @@ -1,6 +1,6 @@ libcommon_a_SOURCES = include/common.h include/simple_writer.h \ include/compress_cli.h include/dir_tree.h \ - lib/common/src/hardlink.c lib/common/src/print_version.c \ + lib/common/src/print_version.c \ lib/common/src/compress.c lib/common/src/comp_opt.c \ lib/common/src/parse_size.c lib/common/src/print_size.c \ lib/common/src/writer/init.c lib/common/src/writer/cleanup.c \ diff --git a/lib/common/src/hardlink.c b/lib/common/src/hardlink.c deleted file mode 100644 index e43df33..0000000 --- a/lib/common/src/hardlink.c +++ /dev/null @@ -1,101 +0,0 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * hardlink.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "common.h" -#include "util/rbtree.h" -#include "util/util.h" - -#include -#include -#include - -static int map_nodes(rbtree_t *inumtree, sqfs_hard_link_t **out, - const sqfs_tree_node_t *n) -{ - const sqfs_tree_node_t *target; - sqfs_hard_link_t *lnk; - rbtree_node_t *tn; - sqfs_u32 idx; - int ret; - - /* XXX: refuse to generate hard links to directories */ - if (n->children != NULL) { - for (n = n->children; n != NULL; n = n->next) { - ret = map_nodes(inumtree, out, n); - if (ret != 0) - return ret; - } - return 0; - } - - if (!is_filename_sane((const char *)n->name, false)) - return SQFS_ERROR_CORRUPTED; - - idx = n->inode->base.inode_number; - tn = rbtree_lookup(inumtree, &idx); - - if (tn == NULL) - return rbtree_insert(inumtree, &idx, &n); - - target = *((const sqfs_tree_node_t **)rbtree_node_value(tn)); - - lnk = calloc(1, sizeof(*lnk)); - if (lnk == NULL) - return SQFS_ERROR_ALLOC; - - lnk->inode_number = idx; - ret = sqfs_tree_node_get_path(target, &lnk->target); - if (ret != 0) { - free(lnk); - return ret; - } - - if (canonicalize_name(lnk->target) != 0) { - sqfs_free(lnk->target); - free(lnk); - return SQFS_ERROR_CORRUPTED; - } - - lnk->next = (*out); - (*out) = lnk; - return 0; -} - -static int compare_inum(const void *ctx, const void *lhs, const void *rhs) -{ - sqfs_u32 l = *((const sqfs_u32 *)lhs), r = *((const sqfs_u32 *)rhs); - (void)ctx; - - return l < r ? -1 : (l > r ? 1 : 0); -} - -int sqfs_tree_find_hard_links(const sqfs_tree_node_t *root, - sqfs_hard_link_t **out) -{ - sqfs_hard_link_t *lnk = NULL; - rbtree_t inumtree; - int ret; - - ret = rbtree_init(&inumtree, sizeof(sqfs_u32), - sizeof(sqfs_tree_node_t *), - compare_inum); - if (ret != 0) - return ret; - - ret = map_nodes(&inumtree, out, root); - rbtree_cleanup(&inumtree); - - if (ret != 0) { - while ((*out) != NULL) { - lnk = (*out); - (*out) = lnk->next; - free(lnk->target); - free(lnk); - } - } - - return ret; -} -- cgit v1.2.3