aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makemodule.am2
-rw-r--r--lib/sqfs/deserialize_fstree.c189
-rw-r--r--lib/sqfs/tree_node_from_inode.c147
3 files changed, 338 insertions, 0 deletions
diff --git a/lib/Makemodule.am b/lib/Makemodule.am
index ff63a7f..9496a9d 100644
--- a/lib/Makemodule.am
+++ b/lib/Makemodule.am
@@ -20,6 +20,8 @@ libsquashfs_a_SOURCES += lib/sqfs/id_table_read.c lib/sqfs/read_inode.c
libsquashfs_a_SOURCES += lib/sqfs/readdir.c lib/sqfs/frag_reader.c
libsquashfs_a_SOURCES += lib/sqfs/write_dir.c lib/sqfs/write_inode.c
libsquashfs_a_SOURCES += lib/sqfs/serialize_fstree.c
+libsquashfs_a_SOURCES += lib/sqfs/tree_node_from_inode.c
+libsquashfs_a_SOURCES += lib/sqfs/deserialize_fstree.c
libsquashfs_a_SOURCES += include/frag_reader.h
libutil_a_SOURCES = lib/util/canonicalize_name.c lib/util/write_retry.c
diff --git a/lib/sqfs/deserialize_fstree.c b/lib/sqfs/deserialize_fstree.c
new file mode 100644
index 0000000..95dce98
--- /dev/null
+++ b/lib/sqfs/deserialize_fstree.c
@@ -0,0 +1,189 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+#include "meta_reader.h"
+#include "highlevel.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+static int should_skip(int type, int flags)
+{
+ switch (type) {
+ case SQFS_INODE_BDEV:
+ case SQFS_INODE_CDEV:
+ case SQFS_INODE_EXT_CDEV:
+ case SQFS_INODE_EXT_BDEV:
+ return (flags & RDTREE_NO_DEVICES);
+ case SQFS_INODE_SLINK:
+ case SQFS_INODE_EXT_SLINK:
+ return (flags & RDTREE_NO_SLINKS);
+ case SQFS_INODE_SOCKET:
+ case SQFS_INODE_EXT_SOCKET:
+ return(flags & RDTREE_NO_SOCKETS);
+ case SQFS_INODE_FIFO:
+ case SQFS_INODE_EXT_FIFO:
+ return (flags & RDTREE_NO_FIFO);
+ }
+ return 0;
+}
+
+static int fill_dir(meta_reader_t *ir, meta_reader_t *dr, tree_node_t *root,
+ sqfs_super_t *super, id_table_t *idtbl, int flags)
+{
+ sqfs_inode_generic_t *inode;
+ sqfs_dir_header_t hdr;
+ sqfs_dir_entry_t *ent;
+ tree_node_t *n, *prev;
+ uint64_t block_start;
+ size_t size, diff;
+ uint32_t i;
+
+ block_start = root->data.dir->start_block;
+ block_start += super->directory_table_start;
+
+ if (meta_reader_seek(dr, block_start, root->data.dir->block_offset))
+ return -1;
+
+ size = root->data.dir->size;
+
+ while (size != 0) {
+ if (meta_reader_read_dir_header(dr, &hdr))
+ return -1;
+
+ size -= sizeof(hdr) > size ? size : sizeof(hdr);
+
+ for (i = 0; i <= hdr.count; ++i) {
+ ent = meta_reader_read_dir_ent(dr);
+ if (ent == NULL)
+ return -1;
+
+ diff = sizeof(*ent) + strlen((char *)ent->name);
+ size -= diff > size ? size : diff;
+
+ if (should_skip(ent->type, flags)) {
+ free(ent);
+ continue;
+ }
+
+ inode = meta_reader_read_inode(ir, super,
+ hdr.start_block,
+ ent->offset);
+ if (inode == NULL) {
+ free(ent);
+ return -1;
+ }
+
+ n = tree_node_from_inode(inode, idtbl,
+ (char *)ent->name,
+ super->block_size);
+ free(ent);
+ free(inode);
+
+ if (n == NULL)
+ return -1;
+
+ n->parent = root;
+ n->next = root->data.dir->children;
+ root->data.dir->children = n;
+ }
+ }
+
+ n = root->data.dir->children;
+ prev = NULL;
+
+ while (n != NULL) {
+ if (S_ISDIR(n->mode)) {
+ if (fill_dir(ir, dr, n, super, idtbl, flags))
+ return -1;
+
+ if (n->data.dir->children == NULL &&
+ (flags & RDTREE_NO_EMPTY)) {
+ if (prev == NULL) {
+ root->data.dir->children = n->next;
+ free(n);
+ n = root->data.dir->children;
+ } else {
+ prev->next = n->next;
+ free(n);
+ n = prev->next;
+ }
+ continue;
+ }
+ }
+
+ prev = n;
+ n = n->next;
+ }
+
+ return 0;
+}
+
+int deserialize_fstree(fstree_t *out, sqfs_super_t *super, compressor_t *cmp,
+ int fd, int flags)
+{
+ sqfs_inode_generic_t *root;
+ meta_reader_t *ir, *dr;
+ uint64_t block_start;
+ id_table_t idtbl;
+ int status = -1;
+ size_t offset;
+
+ ir = meta_reader_create(fd, cmp);
+ if (ir == NULL)
+ return -1;
+
+ dr = meta_reader_create(fd, cmp);
+ if (dr == NULL)
+ goto out_ir;
+
+ if (id_table_init(&idtbl))
+ goto out_dr;
+
+ if (id_table_read(&idtbl, fd, super, cmp))
+ goto out_id;
+
+ block_start = super->root_inode_ref >> 16;
+ offset = super->root_inode_ref & 0xFFFF;
+ root = meta_reader_read_inode(ir, super, block_start, offset);
+ if (root == NULL)
+ goto out_id;
+
+ if (root->base.type != SQFS_INODE_DIR &&
+ root->base.type != SQFS_INODE_EXT_DIR) {
+ free(root);
+ fputs("File system root inode is not a directory inode!\n",
+ stderr);
+ goto out_id;
+ }
+
+ memset(out, 0, sizeof(*out));
+ out->block_size = super->block_size;
+ out->default_uid = 0;
+ out->default_gid = 0;
+ out->default_mode = 0755;
+ out->default_mtime = super->modification_time;
+
+ out->root = tree_node_from_inode(root, &idtbl, "", super->block_size);
+ free(root);
+ root = NULL;
+
+ if (out->root == NULL)
+ goto out_id;
+
+ if (fill_dir(ir, dr, out->root, super, &idtbl, flags))
+ goto fail_fs;
+
+ fstree_sort(out);
+
+ status = 0;
+out_id:
+ id_table_cleanup(&idtbl);
+out_dr:
+ meta_reader_destroy(dr);
+out_ir:
+ meta_reader_destroy(ir);
+ return status;
+fail_fs:
+ fstree_cleanup(out);
+ goto out_id;
+}
diff --git a/lib/sqfs/tree_node_from_inode.c b/lib/sqfs/tree_node_from_inode.c
new file mode 100644
index 0000000..f9a6599
--- /dev/null
+++ b/lib/sqfs/tree_node_from_inode.c
@@ -0,0 +1,147 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+#include "highlevel.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+static size_t compute_size(sqfs_inode_generic_t *inode, const char *name,
+ size_t block_size)
+{
+ size_t size = sizeof(tree_node_t) + strlen(name) + 1;
+ size_t block_count = 0;
+
+ switch (inode->base.type) {
+ case SQFS_INODE_DIR:
+ case SQFS_INODE_EXT_DIR:
+ size += sizeof(dir_info_t);
+ break;
+ case SQFS_INODE_FILE:
+ size += sizeof(file_info_t);
+ block_count = inode->data.file.file_size / block_size;
+ break;
+ case SQFS_INODE_EXT_FILE:
+ size += sizeof(file_info_t);
+ block_count = inode->data.file_ext.file_size / block_size;
+ break;
+ case SQFS_INODE_SLINK:
+ case SQFS_INODE_EXT_SLINK:
+ size += strlen(inode->slink_target) + 1;
+ break;
+ default:
+ break;
+ }
+
+ return size + block_count * sizeof(uint32_t);
+}
+
+static void copy_block_sizes(sqfs_inode_generic_t *inode, tree_node_t *out,
+ size_t block_size)
+{
+ size_t block_count = out->data.file->size / block_size;
+
+ out->name += block_count * sizeof(uint32_t);
+
+ if (block_count) {
+ memcpy(out->data.file->blocksizes, inode->block_sizes,
+ block_count * sizeof(uint32_t));
+ }
+}
+
+tree_node_t *tree_node_from_inode(sqfs_inode_generic_t *inode,
+ const id_table_t *idtbl,
+ const char *name,
+ size_t block_size)
+{
+ tree_node_t *out;
+
+ if (inode->base.uid_idx >= idtbl->num_ids) {
+ fputs("converting inode to fs tree node: UID out of range\n",
+ stderr);
+ return NULL;
+ }
+
+ if (inode->base.gid_idx >= idtbl->num_ids) {
+ fputs("converting inode to fs tree node: GID out of range\n",
+ stderr);
+ return NULL;
+ }
+
+ out = calloc(1, compute_size(inode, name, block_size));
+ if (out == NULL) {
+ perror("converting inode to fs tree node");
+ return NULL;
+ }
+
+ out->uid = idtbl->ids[inode->base.uid_idx];
+ out->gid = idtbl->ids[inode->base.gid_idx];
+ out->mode = inode->base.mode;
+ out->inode_num = inode->base.inode_number;
+ out->name = (char *)out->payload;
+
+ switch (inode->base.type) {
+ case SQFS_INODE_DIR:
+ out->data.dir = (dir_info_t *)out->payload;
+ out->name += sizeof(dir_info_t);
+
+ out->data.dir->size = inode->data.dir.size;
+ out->data.dir->start_block = inode->data.dir.start_block;
+ out->data.dir->block_offset = inode->data.dir.offset;
+ break;
+ case SQFS_INODE_EXT_DIR:
+ out->data.dir = (dir_info_t *)out->payload;
+ out->name += sizeof(dir_info_t);
+
+ out->data.dir->size = inode->data.dir_ext.size;
+ out->data.dir->start_block = inode->data.dir_ext.start_block;
+ out->data.dir->block_offset = inode->data.dir_ext.offset;
+ break;
+ case SQFS_INODE_FILE:
+ out->data.file = (file_info_t *)out->payload;
+ out->name += sizeof(file_info_t);
+
+ out->data.file->size = inode->data.file.file_size;
+ out->data.file->startblock = inode->data.file.blocks_start;
+ out->data.file->fragment = inode->data.file.fragment_index;
+ out->data.file->fragment_offset =
+ inode->data.file.fragment_offset;
+
+ copy_block_sizes(inode, out, block_size);
+ break;
+ case SQFS_INODE_EXT_FILE:
+ out->data.file = (file_info_t *)out->payload;
+ out->name += sizeof(file_info_t);
+
+ out->data.file->size = inode->data.file_ext.file_size;
+ out->data.file->startblock = inode->data.file_ext.blocks_start;
+ out->data.file->fragment = inode->data.file_ext.fragment_idx;
+ out->data.file->fragment_offset =
+ inode->data.file_ext.fragment_offset;
+
+ copy_block_sizes(inode, out, block_size);
+ break;
+ case SQFS_INODE_SLINK:
+ case SQFS_INODE_EXT_SLINK:
+ out->data.slink_target = (char *)out->payload;
+ strcpy(out->data.slink_target, inode->slink_target);
+
+ out->name = (char *)out->payload +
+ strlen(inode->slink_target) + 1;
+ break;
+ case SQFS_INODE_BDEV:
+ case SQFS_INODE_CDEV:
+ out->name = (char *)out->payload;
+ out->data.devno = inode->data.dev.devno;
+ break;
+ case SQFS_INODE_EXT_BDEV:
+ case SQFS_INODE_EXT_CDEV:
+ out->name = (char *)out->payload;
+ out->data.devno = inode->data.dev_ext.devno;
+ break;
+ default:
+ break;
+ }
+
+ strcpy(out->name, name);
+ return out;
+}