diff options
author | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2022-06-01 00:10:22 +0200 |
---|---|---|
committer | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2022-06-02 17:26:53 +0200 |
commit | a36cf2824c74a3ba7333801268d0ba4194684423 (patch) | |
tree | f1fd950ea37bf6c91af7a154f90f5e6eb8b70e01 | |
parent | fa7110a9c4bd0ece4be33998b6f9e7d4f528acc7 (diff) |
Cleanup: libsqfs: move directory iteration out of the directory reader
Add a simple directory state object to the meta data reader and use
that to iterate directory entries. The code for reading the directory
listing is movde to readdir.c
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
-rw-r--r-- | include/sqfs/meta_reader.h | 83 | ||||
-rw-r--r-- | include/sqfs/predef.h | 1 | ||||
-rw-r--r-- | lib/sqfs/dir_reader/dir_reader.c | 123 | ||||
-rw-r--r-- | lib/sqfs/dir_reader/internal.h | 10 | ||||
-rw-r--r-- | lib/sqfs/readdir.c | 95 |
5 files changed, 204 insertions, 108 deletions
diff --git a/include/sqfs/meta_reader.h b/include/sqfs/meta_reader.h index e0dc186..149672b 100644 --- a/include/sqfs/meta_reader.h +++ b/include/sqfs/meta_reader.h @@ -21,6 +21,7 @@ #define SQFS_META_READER_H #include "sqfs/predef.h" +#include "sqfs/super.h" /** * @file meta_reader.h @@ -47,6 +48,37 @@ * from disk and reading transparently across block boarders if required. */ +/** + * @struct sqfs_readdir_state_t + * + * @brief Encapsulates state for simple directory reading + */ +struct sqfs_readdir_state_t { + struct { + sqfs_u64 block; + size_t offset; + size_t size; + } init, current; + + size_t entries; + + sqfs_u32 inum_base; + sqfs_u64 inode_block; +}; + +/** + * @brief Rewind a directory state object back to its starting location + * + * @memberof sqfs_readdir_state_t + * + * @param it A pointer to the directory state. + */ +static SQFS_INLINE void sqfs_readdir_state_reset(sqfs_readdir_state_t *s) +{ + s->current = s->init; + s->entries = 0; +} + #ifdef __cplusplus extern "C" { #endif @@ -153,6 +185,57 @@ SQFS_API int sqfs_meta_reader_read_dir_ent(sqfs_meta_reader_t *m, sqfs_dir_entry_t **ent); /** + * @brief Initialize a state object for reading a directory + * + * @memberof sqfs_readdir_state_t + * + * This function initializes a simple state object to point to the + * location of a directory header and store the total, uncompressed + * size of the directory. + * + * The state object can be passed to @ref sqfs_meta_reader_readdir + * to read entries one-by-one. + * + * @param s A pointer to the state object to initialize. + * @param super A pointer to the super block, telling us where + * the directory table starts. + * @param inode A pointer to a directory inode from which to get the + * directory location. + * + * @return Zero on success, an @ref SQFS_ERROR value on + * failure (e.g. the inode is not a directory inode). + */ +SQFS_API +int sqfs_readdir_state_init(sqfs_readdir_state_t *s, const sqfs_super_t *super, + const sqfs_inode_generic_t *inode); + +/** + * @brief Simple directory reading interface + * + * @memberof sqfs_meta_reader_t + * + * This function successively reads directory entries, transparently + * parsing and skipping across headers. The state is encapsulated in + * an external object that is passed in and the function seeks to the + * location, so one can swap between multiple states and read several + * directories interchangeably. + * + * @param m A pointer to a meta data reader. + * @param s A pointer to a directory state that is used and updated. + * @param ent Returns a pointer to a directory entry. Can be + * released with a single @ref sqfs_free call. + * @param inum If not NULL, returns the decoded inode number. + * @param iref If not NULL, returns a reference to the inode. + * + * @return Zero on success, a negative @ref SQFS_ERROR number on failure, + * a positive number if the end of the directory is reached. + */ +SQFS_API int sqfs_meta_reader_readdir(sqfs_meta_reader_t *m, + sqfs_readdir_state_t *s, + sqfs_dir_entry_t **ent, + sqfs_u32 *inum, sqfs_u64 *iref); + +/** * @brief Read and decode an inode from a meta data reader. * * @memberof sqfs_meta_reader_t diff --git a/include/sqfs/predef.h b/include/sqfs/predef.h index a976cd4..3c13ef5 100644 --- a/include/sqfs/predef.h +++ b/include/sqfs/predef.h @@ -94,6 +94,7 @@ typedef struct sqfs_block_writer_t sqfs_block_writer_t; typedef struct sqfs_block_writer_stats_t sqfs_block_writer_stats_t; typedef struct sqfs_block_processor_stats_t sqfs_block_processor_stats_t; typedef struct sqfs_block_processor_desc_t sqfs_block_processor_desc_t; +typedef struct sqfs_readdir_state_t sqfs_readdir_state_t; typedef struct sqfs_fragment_t sqfs_fragment_t; typedef struct sqfs_dir_header_t sqfs_dir_header_t; diff --git a/lib/sqfs/dir_reader/dir_reader.c b/lib/sqfs/dir_reader/dir_reader.c index 8a074aa..5ed0eff 100644 --- a/lib/sqfs/dir_reader/dir_reader.c +++ b/lib/sqfs/dir_reader/dir_reader.c @@ -105,29 +105,24 @@ int sqfs_dir_reader_open_dir(sqfs_dir_reader_t *rd, const sqfs_inode_generic_t *inode, sqfs_u32 flags) { - sqfs_u64 block_start; - size_t size, offset; sqfs_u32 parent; + int ret; if (flags & (~SQFS_DIR_OPEN_ALL_FLAGS)) return SQFS_ERROR_UNSUPPORTED; - if (inode->base.type == SQFS_INODE_DIR) { - parent = inode->data.dir.parent_inode; - 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) { - parent = inode->data.dir_ext.parent_inode; - 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; - } + ret = sqfs_readdir_state_init(&rd->it, rd->super, inode); + if (ret) + return ret; if ((rd->flags & SQFS_DIR_READER_DOT_ENTRIES) && !(flags & SQFS_DIR_OPEN_NO_DOT_ENTRIES)) { + if (inode->base.type == SQFS_INODE_EXT_DIR) { + parent = inode->data.dir_ext.parent_inode; + } else { + parent = inode->data.dir.parent_inode; + } + if (sqfs_dir_reader_dcache_find(rd, inode->base.inode_number, &rd->cur_ref)) { return SQFS_ERROR_NO_ENTRY; @@ -146,21 +141,7 @@ int sqfs_dir_reader_open_dir(sqfs_dir_reader_t *rd, } rd->start_state = rd->state; - - memset(&rd->hdr, 0, sizeof(rd->hdr)); - rd->size = size; - rd->entries = 0; - - block_start += rd->super->directory_table_start; - - rd->dir_block_start = block_start; - rd->dir_offset = offset; - rd->start_size = size; - - if (rd->size <= sizeof(rd->hdr)) - return 0; - - return sqfs_meta_reader_seek(rd->meta_dir, block_start, offset); + return 0; } static int mk_dummy_entry(const char *str, sqfs_dir_entry_t **out) @@ -183,8 +164,6 @@ static int mk_dummy_entry(const char *str, sqfs_dir_entry_t **out) 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; switch (rd->state) { @@ -207,41 +186,8 @@ int sqfs_dir_reader_read(sqfs_dir_reader_t *rd, sqfs_dir_entry_t **out) return SQFS_ERROR_SEQUENCE; } - 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; - } - - if (rd->size <= sizeof(*ent)) { - rd->size = 0; - rd->entries = 0; - return 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; + return sqfs_meta_reader_readdir(rd->meta_dir, &rd->it, + out, NULL, &rd->ent_ref); } int sqfs_dir_reader_rewind(sqfs_dir_reader_t *rd) @@ -249,16 +195,9 @@ int sqfs_dir_reader_rewind(sqfs_dir_reader_t *rd) if (rd->state == DIR_STATE_NONE) return SQFS_ERROR_SEQUENCE; - memset(&rd->hdr, 0, sizeof(rd->hdr)); - rd->size = rd->start_size; - rd->entries = 0; + sqfs_readdir_state_reset(&rd->it); rd->state = rd->start_state; - - if (rd->size <= sizeof(rd->hdr)) - return 0; - - return sqfs_meta_reader_seek(rd->meta_dir, rd->dir_block_start, - rd->dir_offset); + return 0; } int sqfs_dir_reader_find(sqfs_dir_reader_t *rd, const char *name) @@ -266,14 +205,9 @@ int sqfs_dir_reader_find(sqfs_dir_reader_t *rd, const char *name) sqfs_dir_entry_t *ent; int ret; - if (rd->state == DIR_STATE_NONE) - return SQFS_ERROR_SEQUENCE; - - if (rd->size != rd->start_size || rd->state != rd->start_state) { - ret = sqfs_dir_reader_rewind(rd); - if (ret) - return ret; - } + ret = sqfs_dir_reader_rewind(rd); + if (ret != 0) + return ret; do { ret = sqfs_dir_reader_read(rd, &ent); @@ -292,36 +226,25 @@ int sqfs_dir_reader_find(sqfs_dir_reader_t *rd, const char *name) int sqfs_dir_reader_get_inode(sqfs_dir_reader_t *rd, sqfs_inode_generic_t **inode) { - sqfs_u64 block_start; - sqfs_u16 offset; + sqfs_u64 ref; int ret; switch (rd->state) { - case DIR_STATE_DOT: - block_start = rd->cur_ref >> 16; - offset = rd->cur_ref & 0x0FFFF; - break; - case DIR_STATE_DOT_DOT: - block_start = rd->parent_ref >> 16; - offset = rd->parent_ref & 0x0FFFF; - break; - case DIR_STATE_ENTRIES: - block_start = rd->hdr.start_block; - offset = rd->inode_offset; - break; + case DIR_STATE_DOT: ref = rd->cur_ref; break; + case DIR_STATE_DOT_DOT: ref = rd->parent_ref; break; + case DIR_STATE_ENTRIES: ref = rd->ent_ref; break; default: return SQFS_ERROR_SEQUENCE; } ret = sqfs_meta_reader_read_inode(rd->meta_inode, rd->super, - block_start, offset, inode); + ref >> 16, ref & 0x0FFFF, inode); if (ret != 0) return ret; if ((*inode)->base.type == SQFS_INODE_DIR || (*inode)->base.type == SQFS_INODE_EXT_DIR) { sqfs_u32 inum = (*inode)->base.inode_number; - sqfs_u64 ref = (block_start << 16) | rd->inode_offset; ret = sqfs_dir_reader_dcache_add(rd, inum, ref); if (ret != 0) diff --git a/lib/sqfs/dir_reader/internal.h b/lib/sqfs/dir_reader/internal.h index cd20b69..4dbe728 100644 --- a/lib/sqfs/dir_reader/internal.h +++ b/lib/sqfs/dir_reader/internal.h @@ -38,14 +38,7 @@ struct sqfs_dir_reader_t { sqfs_meta_reader_t *meta_inode; const sqfs_super_t *super; - sqfs_dir_header_t hdr; - sqfs_u64 dir_block_start; - size_t entries; - size_t size; - - size_t start_size; - sqfs_u16 dir_offset; - sqfs_u16 inode_offset; + sqfs_readdir_state_t it; sqfs_u32 flags; @@ -53,6 +46,7 @@ struct sqfs_dir_reader_t { int state; sqfs_u64 parent_ref; sqfs_u64 cur_ref; + sqfs_u64 ent_ref; rbtree_t dcache; }; diff --git a/lib/sqfs/readdir.c b/lib/sqfs/readdir.c index 8899475..e2dbcd4 100644 --- a/lib/sqfs/readdir.c +++ b/lib/sqfs/readdir.c @@ -9,6 +9,8 @@ #include "sqfs/meta_reader.h" #include "sqfs/error.h" +#include "sqfs/super.h" +#include "sqfs/inode.h" #include "sqfs/dir.h" #include "compat.h" @@ -64,3 +66,96 @@ int sqfs_meta_reader_read_dir_ent(sqfs_meta_reader_t *m, *result = out; return 0; } + +int sqfs_readdir_state_init(sqfs_readdir_state_t *s, const sqfs_super_t *super, + const sqfs_inode_generic_t *inode) +{ + memset(s, 0, sizeof(*s)); + + if (inode->base.type == SQFS_INODE_DIR) { + s->init.block = inode->data.dir.start_block; + s->init.offset = inode->data.dir.offset; + s->init.size = inode->data.dir.size; + } else if (inode->base.type == SQFS_INODE_EXT_DIR) { + s->init.block = inode->data.dir_ext.start_block; + s->init.offset = inode->data.dir_ext.offset; + s->init.size = inode->data.dir_ext.size; + } else { + return SQFS_ERROR_NOT_DIR; + } + + s->init.block += super->directory_table_start; + s->current = s->init; + return 0; +} + +int sqfs_meta_reader_readdir(sqfs_meta_reader_t *m, sqfs_readdir_state_t *it, + sqfs_dir_entry_t **ent, + sqfs_u32 *inum, sqfs_u64 *iref) +{ + size_t count; + int ret; + + if (it->entries == 0) { + sqfs_dir_header_t hdr; + + if (it->current.size <= sizeof(hdr)) + goto out_eof; + + ret = sqfs_meta_reader_seek(m, it->current.block, + it->current.offset); + if (ret != 0) + return ret; + + ret = sqfs_meta_reader_read_dir_header(m, &hdr); + if (ret != 0) + return ret; + + sqfs_meta_reader_get_position(m, &it->current.block, + &it->current.offset); + + it->current.size -= sizeof(hdr); + it->entries = hdr.count + 1; + it->inum_base = hdr.inode_number; + it->inode_block = hdr.start_block; + } + + if (it->current.size <= sizeof(**ent)) + goto out_eof; + + ret = sqfs_meta_reader_seek(m, it->current.block, it->current.offset); + if (ret != 0) + return ret; + + ret = sqfs_meta_reader_read_dir_ent(m, ent); + if (ret) + return ret; + + sqfs_meta_reader_get_position(m, &it->current.block, + &it->current.offset); + + it->current.size -= sizeof(**ent); + it->entries -= 1; + + count = (*ent)->size + 1; + + if (count >= it->current.size) { + it->current.size = 0; + } else { + it->current.size -= count; + } + + if (inum != NULL) + *inum = it->inum_base + (*ent)->inode_diff; + + if (iref != NULL) { + *iref = (sqfs_u64)it->inode_block << 16UL; + *iref |= (*ent)->offset; + } + + return 0; +out_eof: + it->current.size = 0; + it->entries = 0; + return 1; +} |