diff options
author | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2023-01-31 11:21:30 +0100 |
---|---|---|
committer | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2023-01-31 13:51:49 +0100 |
commit | cdccc69c62579b0c13b35fad0728079652b8f3c9 (patch) | |
tree | 9fa54c710f73c5e08a9c8466e7a712eb63ee07ac /bin/sqfs2tar/src/write_tree.c | |
parent | 2182129c8f359c4fa1390eaba7a65b595ccd4182 (diff) |
Move library source into src sub-directory
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'bin/sqfs2tar/src/write_tree.c')
-rw-r--r-- | bin/sqfs2tar/src/write_tree.c | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/bin/sqfs2tar/src/write_tree.c b/bin/sqfs2tar/src/write_tree.c new file mode 100644 index 0000000..354ec21 --- /dev/null +++ b/bin/sqfs2tar/src/write_tree.c @@ -0,0 +1,209 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * write_tree.c + * + * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at> + */ +#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; + tar_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++); + sqfs_free(name); + return ret; + } + } + + if (!no_xattr) { + if (get_xattrs(name, n->inode, &xattr)) { + 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++); + free_xattr_list(xattr); + + if (ret > 0) + goto out_skip; + + if (ret < 0) { + sqfs_free(name); + return -1; + } + + if (S_ISREG(sb.st_mode)) { + if (sqfs_data_reader_dump(name, data, n->inode, out_file, + super.block_size)) { + sqfs_free(name); + return -1; + } + + if (padd_file(out_file, sb.st_size)) { + 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; +} |