aboutsummaryrefslogtreecommitdiff
path: root/unpack/fill_files.c
diff options
context:
space:
mode:
Diffstat (limited to 'unpack/fill_files.c')
-rw-r--r--unpack/fill_files.c186
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;
}