summaryrefslogtreecommitdiff
path: root/lib/sqfs/dir_reader.c
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-09-16 15:50:46 +0200
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-09-19 18:22:41 +0200
commitab0c4a02ba9d1a836f62a8601699c971b72eec07 (patch)
tree4ceb7a0a5d9c36189a05750da388941c8a223ebb /lib/sqfs/dir_reader.c
parent3f4f75777b9b558e0e8c4af4408b094b6d4b1034 (diff)
Add directory reader data structure
This moves a lot of the stuff that is done manually in the tree deserializer to a generic helper in libsquashfs. Due to how the fstree is implemented, as a work around, the inode needs to be temporarily stored in the tree node, but some of the directory details could be removed. Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'lib/sqfs/dir_reader.c')
-rw-r--r--lib/sqfs/dir_reader.c266
1 files changed, 266 insertions, 0 deletions
diff --git a/lib/sqfs/dir_reader.c b/lib/sqfs/dir_reader.c
new file mode 100644
index 0000000..d4bfd27
--- /dev/null
+++ b/lib/sqfs/dir_reader.c
@@ -0,0 +1,266 @@
+/* SPDX-License-Identifier: LGPL-3.0-or-later */
+/*
+ * fs_reader.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#define SQFS_BUILDING_DLL
+#include "config.h"
+
+#include "sqfs/meta_reader.h"
+#include "sqfs/compress.h"
+#include "sqfs/super.h"
+#include "sqfs/inode.h"
+#include "sqfs/error.h"
+#include "sqfs/dir.h"
+#include "util.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+struct sqfs_dir_reader_t {
+ sqfs_meta_reader_t *meta_dir;
+ sqfs_meta_reader_t *meta_inode;
+ const sqfs_super_t *super;
+
+ sqfs_dir_header_t hdr;
+ uint64_t dir_block_start;
+ size_t entries;
+ size_t size;
+
+ size_t start_size;
+ uint16_t dir_offset;
+ uint16_t inode_offset;
+};
+
+sqfs_dir_reader_t *sqfs_dir_reader_create(const sqfs_super_t *super,
+ sqfs_compressor_t *cmp,
+ sqfs_file_t *file)
+{
+ sqfs_dir_reader_t *rd = calloc(1, sizeof(*rd));
+ uint64_t start, limit;
+
+ if (rd == NULL)
+ return NULL;
+
+ start = super->inode_table_start;
+ limit = super->directory_table_start;
+
+ rd->meta_inode = sqfs_meta_reader_create(file, cmp, start, limit);
+
+ if (rd->meta_inode == NULL) {
+ free(rd);
+ return NULL;
+ }
+
+ start = super->directory_table_start;
+ limit = super->id_table_start;
+
+ if (super->fragment_table_start < limit)
+ limit = super->fragment_table_start;
+
+ if (super->export_table_start < limit)
+ limit = super->export_table_start;
+
+ rd->meta_dir = sqfs_meta_reader_create(file, cmp, start, limit);
+
+ if (rd->meta_dir == NULL) {
+ sqfs_meta_reader_destroy(rd->meta_inode);
+ free(rd);
+ return NULL;
+ }
+
+ rd->super = super;
+ return rd;
+}
+
+void sqfs_dir_reader_destroy(sqfs_dir_reader_t *rd)
+{
+ sqfs_meta_reader_destroy(rd->meta_inode);
+ sqfs_meta_reader_destroy(rd->meta_dir);
+ free(rd);
+}
+
+int sqfs_dir_reader_open_dir(sqfs_dir_reader_t *rd,
+ const sqfs_inode_generic_t *inode)
+{
+ uint64_t block_start;
+ size_t size, offset;
+
+ if (inode->base.type == SQFS_INODE_DIR) {
+ size = inode->data.dir.size;
+ offset = inode->data.dir.offset;
+ block_start = inode->data.dir.start_block;
+ } else if (inode->base.type == SQFS_INODE_EXT_DIR) {
+ size = inode->data.dir_ext.size;
+ offset = inode->data.dir_ext.offset;
+ block_start = inode->data.dir_ext.start_block;
+ } else {
+ return SQFS_ERROR_NOT_DIR;
+ }
+
+ memset(&rd->hdr, 0, sizeof(rd->hdr));
+ rd->size = size;
+ rd->entries = 0;
+
+ if (rd->size <= sizeof(rd->hdr))
+ return 0;
+
+ block_start += rd->super->directory_table_start;
+
+ rd->dir_block_start = block_start;
+ rd->dir_offset = offset;
+ rd->start_size = size;
+
+ return sqfs_meta_reader_seek(rd->meta_dir, block_start, offset);
+}
+
+int sqfs_dir_reader_read(sqfs_dir_reader_t *rd, sqfs_dir_entry_t **out)
+{
+ sqfs_dir_entry_t *ent;
+ size_t count;
+ int err;
+
+ if (!rd->entries) {
+ if (rd->size < sizeof(rd->hdr))
+ return 1;
+
+ err = sqfs_meta_reader_read_dir_header(rd->meta_dir, &rd->hdr);
+ if (err)
+ return err;
+
+ rd->size -= sizeof(rd->hdr);
+ rd->entries = rd->hdr.count + 1;
+ }
+
+ err = sqfs_meta_reader_read_dir_ent(rd->meta_dir, &ent);
+ if (err)
+ return err;
+
+ count = sizeof(*ent) + strlen((const char *)ent->name);
+
+ if (count > rd->size) {
+ rd->size = 0;
+ rd->entries = 0;
+ } else {
+ rd->size -= count;
+ rd->entries -= 1;
+ }
+
+ rd->inode_offset = ent->offset;
+ *out = ent;
+ return 0;
+}
+
+int sqfs_dir_reader_rewind(sqfs_dir_reader_t *rd)
+{
+ memset(&rd->hdr, 0, sizeof(rd->hdr));
+ rd->size = rd->start_size;
+ rd->entries = 0;
+
+ return sqfs_meta_reader_seek(rd->meta_dir, rd->dir_block_start,
+ rd->dir_offset);
+}
+
+int sqfs_dir_reader_find(sqfs_dir_reader_t *rd, const char *name)
+{
+ sqfs_dir_entry_t *ent;
+ int ret;
+
+ if (rd->size != rd->start_size) {
+ ret = sqfs_dir_reader_rewind(rd);
+ if (ret)
+ return ret;
+ }
+
+ do {
+ ret = sqfs_dir_reader_read(rd, &ent);
+ if (ret < 0)
+ return ret;
+ if (ret > 0)
+ return SQFS_ERROR_NO_ENTRY;
+
+ ret = strcmp((const char *)ent->name, name);
+ free(ent);
+ } while (ret < 0);
+
+ return ret == 0 ? 0 : SQFS_ERROR_NO_ENTRY;
+}
+
+int sqfs_dir_reader_get_inode(sqfs_dir_reader_t *rd,
+ sqfs_inode_generic_t **inode)
+{
+ uint64_t block_start;
+
+ block_start = rd->hdr.start_block;
+
+ return sqfs_meta_reader_read_inode(rd->meta_inode, rd->super,
+ block_start, rd->inode_offset,
+ inode);
+}
+
+int sqfs_dir_reader_get_root_inode(sqfs_dir_reader_t *rd,
+ sqfs_inode_generic_t **inode)
+{
+ uint64_t block_start = rd->super->root_inode_ref >> 16;
+ uint16_t offset = rd->super->root_inode_ref & 0xFFFF;
+
+ return sqfs_meta_reader_read_inode(rd->meta_inode, rd->super,
+ block_start, offset, inode);
+}
+
+int sqfs_dir_reader_find_by_path(sqfs_dir_reader_t *rd, const char *path,
+ sqfs_inode_generic_t **out)
+{
+ sqfs_inode_generic_t *inode;
+ sqfs_dir_entry_t *ent;
+ const char *ptr;
+ int ret;
+
+ ret = sqfs_dir_reader_get_root_inode(rd, &inode);
+ if (ret)
+ return ret;
+
+ while (*path != '\0') {
+ if (*path == '/' || *path == '\\') {
+ while (*path == '/' || *path == '\\')
+ ++path;
+ continue;
+ }
+
+ ret = sqfs_dir_reader_open_dir(rd, inode);
+ free(inode);
+ if (ret)
+ return ret;
+
+ ptr = strchr(path, '/');
+ if (ptr == NULL)
+ ptr = strchrnul(path, '\\');
+
+ do {
+ ret = sqfs_dir_reader_read(rd, &ent);
+ if (ret < 0)
+ return ret;
+
+ if (ret == 0) {
+ ret = strncmp((const char *)ent->name,
+ path, ptr - path);
+ if (ret == 0)
+ ret = ent->name[ptr - path];
+ free(ent);
+ }
+ } while (ret < 0);
+
+ if (ret > 0)
+ return SQFS_ERROR_NO_ENTRY;
+
+ ret = sqfs_dir_reader_get_inode(rd, &inode);
+ if (ret)
+ return ret;
+
+ path = ptr;
+ }
+
+ *out = inode;
+ return 0;
+}