diff options
Diffstat (limited to 'unpack/fill_files.c')
-rw-r--r-- | unpack/fill_files.c | 186 |
1 files changed, 168 insertions, 18 deletions
diff --git a/unpack/fill_files.c b/unpack/fill_files.c index df6ca7e..de0b676 100644 --- a/unpack/fill_files.c +++ b/unpack/fill_files.c @@ -7,27 +7,173 @@ #include "config.h" #include "rdsquashfs.h" -static int fill_files(data_reader_t *data, file_info_t *list, int flags) +static struct file_ent { + char *path; + const sqfs_inode_generic_t *inode; +} *files = NULL; + +static size_t num_files = 0, max_files = 0; +static size_t block_size = 0; + +static uint32_t get_frag_idx(const sqfs_inode_generic_t *inode) { - file_info_t *fi; - int fd; + if (inode->base.type == SQFS_INODE_EXT_FILE) + return inode->data.file_ext.fragment_idx; + + return inode->data.file.fragment_index; +} + +static uint32_t get_frag_off(const sqfs_inode_generic_t *inode) +{ + if (inode->base.type == SQFS_INODE_EXT_FILE) + return inode->data.file_ext.fragment_offset; + + return inode->data.file.fragment_offset; +} + +static uint64_t get_size(const sqfs_inode_generic_t *inode) +{ + if (inode->base.type == SQFS_INODE_EXT_FILE) + return inode->data.file_ext.file_size; + + return inode->data.file.file_size; +} + +static uint64_t get_start(const sqfs_inode_generic_t *inode) +{ + if (inode->base.type == SQFS_INODE_EXT_FILE) + return inode->data.file_ext.blocks_start; + + return inode->data.file.blocks_start; +} + +static bool has_fragment(const struct file_ent *ent) +{ + if (get_size(ent->inode) % block_size == 0) + return false; + + return get_frag_off(ent->inode) < block_size && + (get_frag_idx(ent->inode) != 0xFFFFFFFF); +} + +static int compare_files(const void *l, const void *r) +{ + const struct file_ent *lhs = l, *rhs = r; + + /* Files with fragments come first, ordered by ID. + In case of tie, files without data blocks come first, + and the others are ordered by start block. */ + if (has_fragment(lhs)) { + if (!(has_fragment(rhs))) + return -1; + + if (get_frag_idx(lhs->inode) < get_frag_idx(rhs->inode)) + return -1; + + if (get_frag_idx(lhs->inode) > get_frag_idx(rhs->inode)) + return 1; + + if (get_size(lhs->inode) < block_size) + return (get_size(rhs->inode) < block_size) ? 0 : -1; + + if (get_size(rhs->inode) < block_size) + return 1; + + goto order_by_start; + } + + if (has_fragment(rhs)) + return 1; - for (fi = list; fi != NULL; fi = fi->next) { - if (fi->input_file == NULL) - continue; + /* order the rest by start block */ +order_by_start: + return get_start(lhs->inode) < get_start(rhs->inode) ? -1 : + get_start(lhs->inode) > get_start(rhs->inode) ? 1 : 0; +} + +static int add_file(const sqfs_tree_node_t *node) +{ + size_t new_sz; + char *path; + void *new; + + if (num_files == max_files) { + new_sz = max_files ? max_files * 2 : 256; + new = realloc(files, sizeof(files[0]) * new_sz); + + if (new == NULL) { + perror("expanding file list"); + return -1; + } + + files = new; + max_files = new_sz; + } - fd = open(fi->input_file, O_WRONLY); + path = sqfs_tree_node_get_path(node); + if (path == NULL) { + perror("assembling file path"); + return -1; + } + + if (canonicalize_name(path)) { + fprintf(stderr, "Invalid file path '%s'\n", path); + free(path); + return -1; + } + + files[num_files].path = path; + files[num_files].inode = node->inode; + num_files++; + return 0; +} + +static void clear_file_list(void) +{ + size_t i; + + for (i = 0; i < num_files; ++i) + free(files[i].path); + + free(files); + files = NULL; + num_files = 0; + max_files = 0; +} + +static int gen_file_list_dfs(const sqfs_tree_node_t *n) +{ + if (S_ISREG(n->inode->base.mode)) + return add_file(n); + + if (S_ISDIR(n->inode->base.mode)) { + for (n = n->children; n != NULL; n = n->next) { + if (gen_file_list_dfs(n)) + return -1; + } + } + + return 0; +} + +static int fill_files(data_reader_t *data, int flags) +{ + size_t i; + int fd; + + for (i = 0; i < num_files; ++i) { + fd = open(files[i].path, O_WRONLY); if (fd < 0) { fprintf(stderr, "unpacking %s: %s\n", - fi->input_file, strerror(errno)); + files[i].path, strerror(errno)); return -1; } if (!(flags & UNPACK_QUIET)) - printf("unpacking %s\n", fi->input_file); + printf("unpacking %s\n", files[i].path); - if (data_reader_dump_file(data, fi, fd, - (flags & UNPACK_NO_SPARSE) == 0)) { + if (data_reader_dump(data, files[i].inode, fd, + (flags & UNPACK_NO_SPARSE) == 0)) { close(fd); return -1; } @@ -38,17 +184,21 @@ static int fill_files(data_reader_t *data, file_info_t *list, int flags) return 0; } -int fill_unpacked_files(fstree_t *fs, data_reader_t *data, int flags) +int fill_unpacked_files(size_t blk_sz, const sqfs_tree_node_t *root, + data_reader_t *data, int flags) { - file_info_t *list, *it; - int status = 0; + int status; - list = optimize_unpack_order(fs); + block_size = blk_sz; - status = fill_files(data, list, flags); + if (gen_file_list_dfs(root)) { + clear_file_list(); + return -1; + } - for (it = list; it != NULL; it = it->next) - free(it->input_file); + qsort(files, num_files, sizeof(files[0]), compare_files); + status = fill_files(data, flags); + clear_file_list(); return status; } |