diff options
Diffstat (limited to 'lib/sqfs/dir_reader')
| -rw-r--r-- | lib/sqfs/dir_reader/dcache.c | 73 | ||||
| -rw-r--r-- | lib/sqfs/dir_reader/dir_reader.c | 166 | ||||
| -rw-r--r-- | lib/sqfs/dir_reader/internal.h | 31 | ||||
| -rw-r--r-- | lib/sqfs/dir_reader/read_tree.c | 9 | 
4 files changed, 256 insertions, 23 deletions
| diff --git a/lib/sqfs/dir_reader/dcache.c b/lib/sqfs/dir_reader/dcache.c new file mode 100644 index 0000000..47fbf73 --- /dev/null +++ b/lib/sqfs/dir_reader/dcache.c @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * dcache.c + * + * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at> + */ +#define SQFS_BUILDING_DLL +#include "internal.h" + +static int dcache_key_compare(const void *ctx, const void *l, const void *r) +{ +	sqfs_u32 lhs = *((const sqfs_u32 *)l), rhs = *((const sqfs_u32 *)r); +	(void)ctx; + +	return lhs < rhs ? -1 : (lhs > rhs ? 1 : 0); +} + +int sqfs_dir_reader_dcache_init(sqfs_dir_reader_t *rd, sqfs_u32 flags) +{ +	if (!(flags & SQFS_DIR_READER_DOT_ENTRIES)) +		return 0; + +	return rbtree_init(&rd->dcache, sizeof(sqfs_u32), sizeof(sqfs_u64), +			   dcache_key_compare); +} + +int sqfs_dir_reader_dcache_init_copy(sqfs_dir_reader_t *copy, +				     const sqfs_dir_reader_t *rd) +{ +	if (!(rd->flags & SQFS_DIR_READER_DOT_ENTRIES)) +		return 0; + +	return rbtree_copy(&rd->dcache, ©->dcache); +} + +void sqfs_dir_reader_dcache_cleanup(sqfs_dir_reader_t *rd) +{ +	if (!(rd->flags & SQFS_DIR_READER_DOT_ENTRIES)) +		return; + +	rbtree_cleanup(&rd->dcache); +} + +int sqfs_dir_reader_dcache_add(sqfs_dir_reader_t *rd, +			       sqfs_u32 inode, sqfs_u64 ref) +{ +	rbtree_node_t *node; + +	if (!(rd->flags & SQFS_DIR_READER_DOT_ENTRIES)) +		return 0; + +	node = rbtree_lookup(&rd->dcache, &inode); +	if (node != NULL) +		return 0; + +	return rbtree_insert(&rd->dcache, &inode, &ref); +} + +int sqfs_dir_reader_dcache_find(sqfs_dir_reader_t *rd, +				sqfs_u32 inode, sqfs_u64 *ref) +{ +	rbtree_node_t *node; + +	if (!(rd->flags & SQFS_DIR_READER_DOT_ENTRIES)) +		return SQFS_ERROR_NO_ENTRY; + +	node = rbtree_lookup(&rd->dcache, &inode); +	if (node == NULL) +		return SQFS_ERROR_NO_ENTRY; + +	*ref = *((sqfs_u64 *)rbtree_node_value(node)); +	return 0; +} 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;  } diff --git a/lib/sqfs/dir_reader/internal.h b/lib/sqfs/dir_reader/internal.h index ff162ff..cd20b69 100644 --- a/lib/sqfs/dir_reader/internal.h +++ b/lib/sqfs/dir_reader/internal.h @@ -17,11 +17,20 @@  #include "sqfs/inode.h"  #include "sqfs/error.h"  #include "sqfs/dir.h" +#include "rbtree.h"  #include "util.h"  #include <string.h>  #include <stdlib.h> +enum { +	DIR_STATE_NONE = 0, +	DIR_STATE_OPENED = 1, +	DIR_STATE_DOT = 2, +	DIR_STATE_DOT_DOT = 3, +	DIR_STATE_ENTRIES = 4, +}; +  struct sqfs_dir_reader_t {  	sqfs_object_t base; @@ -37,6 +46,28 @@ struct sqfs_dir_reader_t {  	size_t start_size;  	sqfs_u16 dir_offset;  	sqfs_u16 inode_offset; + +	sqfs_u32 flags; + +	int start_state; +	int state; +	sqfs_u64 parent_ref; +	sqfs_u64 cur_ref; +	rbtree_t dcache;  }; +SQFS_INTERNAL int sqfs_dir_reader_dcache_init(sqfs_dir_reader_t *rd, +					      sqfs_u32 flags); + +SQFS_INTERNAL int sqfs_dir_reader_dcache_init_copy(sqfs_dir_reader_t *copy, +						   const sqfs_dir_reader_t *rd); + +SQFS_INTERNAL int sqfs_dir_reader_dcache_add(sqfs_dir_reader_t *rd, +					     sqfs_u32 inode, sqfs_u64 ref); + +SQFS_INTERNAL int sqfs_dir_reader_dcache_find(sqfs_dir_reader_t *rd, +					      sqfs_u32 inode, sqfs_u64 *ref); + +SQFS_INTERNAL void sqfs_dir_reader_dcache_cleanup(sqfs_dir_reader_t *rd); +  #endif /* DIR_READER_INTERNAL_H */ diff --git a/lib/sqfs/dir_reader/read_tree.c b/lib/sqfs/dir_reader/read_tree.c index d173ef7..7fa944a 100644 --- a/lib/sqfs/dir_reader/read_tree.c +++ b/lib/sqfs/dir_reader/read_tree.c @@ -113,7 +113,8 @@ static int fill_dir(sqfs_dir_reader_t *dr, sqfs_tree_node_t *root,  		if (n->inode->base.type == SQFS_INODE_DIR ||  		    n->inode->base.type == SQFS_INODE_EXT_DIR) {  			if (!(flags & SQFS_TREE_NO_RECURSE)) { -				err = sqfs_dir_reader_open_dir(dr, n->inode, 0); +				err = sqfs_dir_reader_open_dir(dr, n->inode, +					       SQFS_DIR_OPEN_NO_DOT_ENTRIES);  				if (err)  					return err; @@ -212,7 +213,8 @@ int sqfs_dir_reader_get_full_hierarchy(sqfs_dir_reader_t *rd,  			continue;  		} -		ret = sqfs_dir_reader_open_dir(rd, tail->inode, 0); +		ret = sqfs_dir_reader_open_dir(rd, tail->inode, +					       SQFS_DIR_OPEN_NO_DOT_ENTRIES);  		if (ret)  			goto fail; @@ -271,7 +273,8 @@ int sqfs_dir_reader_get_full_hierarchy(sqfs_dir_reader_t *rd,  	if (tail->inode->base.type == SQFS_INODE_DIR ||  	    tail->inode->base.type == SQFS_INODE_EXT_DIR) { -		ret = sqfs_dir_reader_open_dir(rd, tail->inode, 0); +		ret = sqfs_dir_reader_open_dir(rd, tail->inode, +					       SQFS_DIR_OPEN_NO_DOT_ENTRIES);  		if (ret)  			goto fail; | 
