From 020698f3a92195eb371e806a0b3ed0649565046f Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Sat, 9 Apr 2022 23:20:09 +0200 Subject: Add support for '.' and '..' entries in sqfs_dir_reader_t Two flags are added to the dir reader API, one for the create function that the dir reader should report those entries and one to the open function to suppress that if it was enabled. To implement the feature, a mapping of visited directory inodes is maintained internally, that mapps inode numbers to inode references. When opening a directory, state is maintained to generate the fake entries for '.' and '..'. Since all the other functions are based on the open/read/rewind API, no alterations need to be made. The tree scan function is modified, to use the suppress flag, so it does not accidentally catch those entries. Signed-off-by: David Oberhollenzer --- lib/sqfs/dir_reader/dir_reader.c | 166 ++++++++++++++++++++++++++++++++++----- 1 file changed, 146 insertions(+), 20 deletions(-) (limited to 'lib/sqfs/dir_reader/dir_reader.c') diff --git a/lib/sqfs/dir_reader/dir_reader.c b/lib/sqfs/dir_reader/dir_reader.c index 969b71d..8a074aa 100644 --- a/lib/sqfs/dir_reader/dir_reader.c +++ b/lib/sqfs/dir_reader/dir_reader.c @@ -11,6 +11,7 @@ static void dir_reader_destroy(sqfs_object_t *obj) { sqfs_dir_reader_t *rd = (sqfs_dir_reader_t *)obj; + sqfs_dir_reader_dcache_cleanup(rd); sqfs_destroy(rd->meta_inode); sqfs_destroy(rd->meta_dir); free(rd); @@ -26,6 +27,9 @@ static sqfs_object_t *dir_reader_copy(const sqfs_object_t *obj) memcpy(copy, rd, sizeof(*copy)); + if (sqfs_dir_reader_dcache_init_copy(copy, rd)) + goto fail_cache; + copy->meta_inode = sqfs_copy(rd->meta_inode); if (copy->meta_inode == NULL) goto fail_mino; @@ -38,6 +42,8 @@ static sqfs_object_t *dir_reader_copy(const sqfs_object_t *obj) fail_mdir: sqfs_destroy(copy->meta_inode); fail_mino: + sqfs_dir_reader_dcache_cleanup(copy); +fail_cache: free(copy); return NULL; } @@ -50,22 +56,22 @@ sqfs_dir_reader_t *sqfs_dir_reader_create(const sqfs_super_t *super, sqfs_dir_reader_t *rd; sqfs_u64 start, limit; - if (flags != 0) + if (flags & ~SQFS_DIR_READER_ALL_FLAGS) return NULL; rd = calloc(1, sizeof(*rd)); if (rd == NULL) return NULL; + if (sqfs_dir_reader_dcache_init(rd, flags)) + goto fail_dcache; + 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; - } + if (rd->meta_inode == NULL) + goto fail_mino; start = super->directory_table_start; limit = super->id_table_start; @@ -77,17 +83,22 @@ sqfs_dir_reader_t *sqfs_dir_reader_create(const sqfs_super_t *super, limit = super->export_table_start; rd->meta_dir = sqfs_meta_reader_create(file, cmp, start, limit); - - if (rd->meta_dir == NULL) { - sqfs_destroy(rd->meta_inode); - free(rd); - return NULL; - } + if (rd->meta_dir == NULL) + goto fail_mdir; ((sqfs_object_t *)rd)->destroy = dir_reader_destroy; ((sqfs_object_t *)rd)->copy = dir_reader_copy; rd->super = super; + rd->flags = flags; + rd->state = DIR_STATE_NONE; return rd; +fail_mdir: + sqfs_destroy(rd->meta_inode); +fail_mino: + sqfs_dir_reader_dcache_cleanup(rd); +fail_dcache: + free(rd); + return NULL; } int sqfs_dir_reader_open_dir(sqfs_dir_reader_t *rd, @@ -96,15 +107,18 @@ int sqfs_dir_reader_open_dir(sqfs_dir_reader_t *rd, { sqfs_u64 block_start; size_t size, offset; + sqfs_u32 parent; - if (flags != 0) + 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; @@ -112,6 +126,27 @@ int sqfs_dir_reader_open_dir(sqfs_dir_reader_t *rd, return SQFS_ERROR_NOT_DIR; } + if ((rd->flags & SQFS_DIR_READER_DOT_ENTRIES) && + !(flags & SQFS_DIR_OPEN_NO_DOT_ENTRIES)) { + if (sqfs_dir_reader_dcache_find(rd, inode->base.inode_number, + &rd->cur_ref)) { + return SQFS_ERROR_NO_ENTRY; + } + + if (rd->cur_ref == rd->super->root_inode_ref) { + rd->parent_ref = rd->cur_ref; + } else if (sqfs_dir_reader_dcache_find(rd, parent, + &rd->parent_ref)) { + return SQFS_ERROR_NO_ENTRY; + } + + rd->state = DIR_STATE_OPENED; + } else { + rd->state = DIR_STATE_ENTRIES; + } + + rd->start_state = rd->state; + memset(&rd->hdr, 0, sizeof(rd->hdr)); rd->size = size; rd->entries = 0; @@ -128,12 +163,50 @@ int sqfs_dir_reader_open_dir(sqfs_dir_reader_t *rd, return sqfs_meta_reader_seek(rd->meta_dir, block_start, offset); } +static int mk_dummy_entry(const char *str, sqfs_dir_entry_t **out) +{ + size_t len = strlen(str); + sqfs_dir_entry_t *ent; + + ent = calloc(1, sizeof(sqfs_dir_entry_t) + len + 1); + if (ent == NULL) + return SQFS_ERROR_ALLOC; + + ent->type = SQFS_INODE_DIR; + ent->size = len - 1; + + strcpy((char *)ent->name, str); + + *out = ent; + return 0; +} + 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) { + case DIR_STATE_OPENED: + err = mk_dummy_entry(".", out); + if (err == 0) + rd->state = DIR_STATE_DOT; + return err; + case DIR_STATE_DOT: + err = mk_dummy_entry("..", out); + if (err == 0) + rd->state = DIR_STATE_DOT_DOT; + return err; + case DIR_STATE_DOT_DOT: + rd->state = DIR_STATE_ENTRIES; + break; + case DIR_STATE_ENTRIES: + break; + default: + return SQFS_ERROR_SEQUENCE; + } + if (!rd->entries) { if (rd->size <= sizeof(rd->hdr)) return 1; @@ -173,9 +246,13 @@ int sqfs_dir_reader_read(sqfs_dir_reader_t *rd, sqfs_dir_entry_t **out) 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; + rd->state = rd->start_state; if (rd->size <= sizeof(rd->hdr)) return 0; @@ -189,7 +266,10 @@ 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) { + 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; @@ -213,12 +293,42 @@ int sqfs_dir_reader_get_inode(sqfs_dir_reader_t *rd, sqfs_inode_generic_t **inode) { sqfs_u64 block_start; + sqfs_u16 offset; + 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; + default: + return SQFS_ERROR_SEQUENCE; + } - block_start = rd->hdr.start_block; + ret = sqfs_meta_reader_read_inode(rd->meta_inode, rd->super, + block_start, offset, inode); + if (ret != 0) + return ret; - return sqfs_meta_reader_read_inode(rd->meta_inode, rd->super, - block_start, rd->inode_offset, - inode); + 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) + return ret; + } + + return 0; } int sqfs_dir_reader_get_root_inode(sqfs_dir_reader_t *rd, @@ -226,7 +336,23 @@ int sqfs_dir_reader_get_root_inode(sqfs_dir_reader_t *rd, { sqfs_u64 block_start = rd->super->root_inode_ref >> 16; sqfs_u16 offset = rd->super->root_inode_ref & 0xFFFF; + int ret; + + ret = sqfs_meta_reader_read_inode(rd->meta_inode, rd->super, + block_start, offset, inode); - return sqfs_meta_reader_read_inode(rd->meta_inode, rd->super, - block_start, offset, 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 = rd->super->root_inode_ref; + + ret = sqfs_dir_reader_dcache_add(rd, inum, ref); + if (ret != 0) + return ret; + } + + return 0; } -- cgit v1.2.3