From d4d5f967b36f828dc4fc1ad07262a25712c90f1c Mon Sep 17 00:00:00 2001 From: Enno Boland Date: Sun, 26 May 2024 22:02:53 +0200 Subject: [PATCH 1/4] tree: rewrite the path resolution code --- .Mk.yaml | 2 +- include/sqsh_file.h | 14 + libsqsh/include/sqsh_file_private.h | 12 + libsqsh/include/sqsh_tree_private.h | 87 ++--- libsqsh/src/easy/file.c | 2 - libsqsh/src/file/file.c | 102 ++++-- libsqsh/src/tree/path_resolver.c | 543 +++++++++++++++------------- libsqsh/src/tree/walker.c | 6 +- test/libsqsh/file/file.c | 14 +- test/libsqsh/integration.c | 18 + test/libsqsh/tree/path_resolver.c | 18 + test/libsqsh/tree/walker.c | 38 +- 12 files changed, 478 insertions(+), 378 deletions(-) diff --git a/.Mk.yaml b/.Mk.yaml index cf2f4147..a9d168d9 100644 --- a/.Mk.yaml +++ b/.Mk.yaml @@ -15,7 +15,7 @@ configure: - -Dfuse-old=enabled - -Db_coverage=true - -Db_sanitize=address,undefined - - -Dtest=extended + #- -Dtest=extended #- -Dfuzzer=true #- -Dfuzzer_timeout=10 default: diff --git a/include/sqsh_file.h b/include/sqsh_file.h index c3700d86..6d212e2f 100644 --- a/include/sqsh_file.h +++ b/include/sqsh_file.h @@ -265,6 +265,20 @@ enum SqshFileType { SQSH_NO_UNUSED struct SqshFile * sqsh_open(struct SqshArchive *archive, const char *path, int *err); +/** + * @memberof SqshFile + * @brief Initialize the file context from a path. This function is identical to + * `sqsh_open()` but if the path is a symlink, the symlink target not resolved. + * + * @param[in] archive The sqsh archive context. + * @param[in] path The path the file or directory. + * @param[out] err Pointer to an int where the error code will be stored. + * + * @return 0 on success, less than 0 on error. + */ +SQSH_NO_UNUSED struct SqshFile * +sqsh_lopen(struct SqshArchive *archive, const char *path, int *err); + /** * @memberof SqshFile * @brief Initializes a file context in heap diff --git a/libsqsh/include/sqsh_file_private.h b/libsqsh/include/sqsh_file_private.h index 17cc8bae..a0c8fab9 100644 --- a/libsqsh/include/sqsh_file_private.h +++ b/libsqsh/include/sqsh_file_private.h @@ -207,6 +207,8 @@ SQSH_NO_EXPORT int sqsh__file_reader_cleanup(struct SqshFileReader *reader); * file/file.c */ +#define SQSH_AUTO_DIR_INODE UINT32_MAX + /** * @brief The file type implementation */ @@ -270,6 +272,16 @@ SQSH_NO_EXPORT SQSH_NO_UNUSED int sqsh__file_init( struct SqshFile *context, struct SqshArchive *sqsh, uint64_t inode_ref, uint32_t dir_inode); +/** + * @internal + * @memberof SqshFile + * @brief Retrieves the inode of the parent directory. + * + * @param context The file context. + * @return uint32_t The inode number. + */ +SQSH_NO_EXPORT uint32_t sqsh__file_dir_inode(const struct SqshFile *context); + /** * @internal * @memberof SqshFile diff --git a/libsqsh/include/sqsh_tree_private.h b/libsqsh/include/sqsh_tree_private.h index 7e1fec22..1e2a6cb0 100644 --- a/libsqsh/include/sqsh_tree_private.h +++ b/libsqsh/include/sqsh_tree_private.h @@ -54,76 +54,43 @@ struct SqshPathResolver { /** * @privatesection */ - uint64_t root_inode_ref; - uint64_t current_inode_ref; - struct SqshArchive *archive; - struct SqshInodeMap *inode_map; + size_t max_symlink_depth; + size_t current_symlink_depth; struct SqshFile cwd; struct SqshDirectoryIterator iterator; - size_t max_symlink_depth; + struct SqshArchive *archive; + struct SqshInodeMap *inode_map; + uint64_t current_inode_ref; + uint64_t root_inode_ref; }; -/** - * @internal - * @memberof SqshPathResolver - * @brief Initializes a SqshPathResolver struct using the inode_ref as the start - * directory. - * - * @param[out] walker The file walker to initialize. - * @param[in] archive The archive to used - * @param[in] inode_ref The inode reference to start at - * - * @return 0 on success, less than 0 on error. - */ -SQSH_NO_EXPORT SQSH_NO_UNUSED int sqsh__path_resolver_init_from_inode_ref( - struct SqshPathResolver *walker, struct SqshArchive *archive, - uint64_t inode_ref); - -/** - * @internal - * @memberof SqshPathResolver - * @brief Initializes a SqshPathResolver struct. - * - * @param[out] walker The file walker to initialize. - * @param[in] archive The archive to use - * - * @return 0 on success, less than 0 on error. - */ SQSH_NO_EXPORT SQSH_NO_UNUSED int sqsh__path_resolver_init( - struct SqshPathResolver *walker, struct SqshArchive *archive); + struct SqshPathResolver *resolver, struct SqshArchive *archive); -/** - * @internal - * @memberof SqshPathResolver - * @brief Resolve a non-zero terminated path with the tree walker. - * - * This function will resolve the given path with the tree walker. The base is - * the current directory. - * - * @param[in,out] walker The walker to use - * @param[in] path The path to resolve. - * @param[in] path_len The length of the path. - * @param[in] follow_symlinks Whether to follow symlinks. - * - * @return the inode of the current entry. - */ -SQSH_NO_UNUSED int sqsh__path_resolver_resolve_len( - struct SqshPathResolver *walker, const char *path, size_t path_len, +SQSH_NO_EXPORT SQSH_NO_UNUSED int sqsh__path_resolver_to_ref( + struct SqshPathResolver *resolver, uint64_t inode_ref); + +SQSH_NO_EXPORT SQSH_NO_UNUSED int sqsh__path_resolver_to_inode( + struct SqshPathResolver *resolver, uint32_t inode_number); + +SQSH_NO_EXPORT enum SqshFileType +sqsh__path_resolver_type(const struct SqshPathResolver *resolver); + +SQSH_NO_EXPORT SQSH_NO_UNUSED int +sqsh__path_resolver_follow_symlink(struct SqshPathResolver *resolver); + +SQSH_NO_EXPORT SQSH_NO_UNUSED int +sqsh__path_resolver_follow_all_symlinks(struct SqshPathResolver *resolver); + +SQSH_NO_EXPORT SQSH_NO_UNUSED int sqsh__path_resolver_resolve_nt( + struct SqshPathResolver *resolver, const char *path, size_t path_len, bool follow_symlinks); -/** - * @internal - * @memberof SqshPathResolver - * @brief Frees the resources used by the file walker. - * - * @param walker The file walker to clean up. - * - * @return 0 on success, less than 0 on error. - */ -SQSH_NO_EXPORT int sqsh__path_resolver_cleanup(struct SqshPathResolver *walker); +SQSH_NO_EXPORT int +sqsh__path_resolver_cleanup(struct SqshPathResolver *resolver); /*************************************** - * tree/path_resolver.c + * tree/traversal.c */ /** diff --git a/libsqsh/src/easy/file.c b/libsqsh/src/easy/file.c index 3ff1db4a..3ef36f1e 100644 --- a/libsqsh/src/easy/file.c +++ b/libsqsh/src/easy/file.c @@ -35,9 +35,7 @@ #include -#include #include -#include #include #include diff --git a/libsqsh/src/file/file.c b/libsqsh/src/file/file.c index de277b4a..a658b909 100644 --- a/libsqsh/src/file/file.c +++ b/libsqsh/src/file/file.c @@ -43,6 +43,8 @@ #include #include +#define SQSH_DEFAULT_MAX_SYMLINKS_FOLLOWED 100 + static const struct SqshDataInode * get_inode(const struct SqshFile *inode) { return (const struct SqshDataInode *)sqsh__metablock_reader_data( @@ -160,6 +162,14 @@ check_file_consistency(struct SqshFile *file) { return -SQSH_ERROR_INODE_PARENT_MISMATCH; } + if (sqsh__file_dir_inode(file) == 0) { + const struct SqshSuperblock *superblock = + sqsh_archive_superblock(file->archive); + if (file->inode_ref != sqsh_superblock_inode_root_ref(superblock)) { + return -SQSH_ERROR_INODE_PARENT_MISMATCH; + } + } + return 0; } @@ -204,11 +214,15 @@ sqsh__file_init( goto out; } - if (dir_inode == 0 && sqsh_file_type(inode) == SQSH_FILE_TYPE_DIRECTORY) { - inode->dir_inode = sqsh_file_directory_parent_inode(inode); - } else { - inode->dir_inode = dir_inode; + if (dir_inode == SQSH_AUTO_DIR_INODE) { + if (sqsh_file_type(inode) == SQSH_FILE_TYPE_DIRECTORY) { + dir_inode = sqsh_file_directory_parent_inode(inode); + } else { + rv = -SQSH_ERROR_NOT_A_DIRECTORY; + goto out; + } } + inode->dir_inode = dir_inode; rv = sqsh_archive_inode_map(archive, &inode_map); if (rv < 0) { @@ -245,6 +259,11 @@ sqsh_open_by_ref2( sqsh__file_init, struct SqshFile, archive, inode_ref, dir_inode); } +uint32_t +sqsh__file_dir_inode(const struct SqshFile *context) { + return context->dir_inode; +} + bool sqsh_file_is_extended(const struct SqshFile *context) { // Only extended inodes have pointers to the xattr table. Instead of relying @@ -359,40 +378,40 @@ sqsh_file_type(const struct SqshFile *context) { } static int -symlink_resolve_inode_ref( - const struct SqshFile *context, uint64_t *inode_ref, - uint32_t *dir_inode) { +symlink_resolve(struct SqshFile *context, bool follow_symlinks) { int rv = 0; - struct SqshPathResolver resolver = {0}; - uint32_t old_dir_inode = context->dir_inode; if (sqsh_file_type(context) != SQSH_FILE_TYPE_SYMLINK) { - rv = -SQSH_ERROR_NOT_A_SYMLINK; - goto out; + return -SQSH_ERROR_NOT_A_SYMLINK; } - struct SqshInodeMap *inode_map = NULL; - rv = sqsh_archive_inode_map(context->archive, &inode_map); + struct SqshPathResolver resolver = {0}; + rv = sqsh__path_resolver_init(&resolver, context->archive); if (rv < 0) { goto out; } - uint64_t dir_inode_ref = sqsh_inode_map_get2(inode_map, old_dir_inode, &rv); - - rv = sqsh__path_resolver_init_from_inode_ref( - &resolver, context->archive, dir_inode_ref); + uint32_t old_dir_inode = sqsh__file_dir_inode(context); + rv = sqsh__path_resolver_to_inode(&resolver, old_dir_inode); if (rv < 0) { goto out; } const char *target = sqsh_file_symlink(context); - size_t target_len = sqsh_file_symlink_size(context); - rv = sqsh__path_resolver_resolve_len(&resolver, target, target_len, true); + const size_t target_size = sqsh_file_symlink_size(context); + rv = sqsh__path_resolver_resolve_nt( + &resolver, target, target_size, follow_symlinks); + if (rv < 0) { + goto out; + } + + const uint64_t inode_ref = sqsh_path_resolver_inode_ref(&resolver); + const uint32_t dir_inode = sqsh_path_resolver_dir_inode(&resolver); + sqsh__file_cleanup(context); + rv = sqsh__file_init(context, context->archive, inode_ref, dir_inode); if (rv < 0) { goto out; } - *inode_ref = sqsh_path_resolver_inode_ref(&resolver); - *dir_inode = sqsh_path_resolver_dir_inode(&resolver); out: sqsh__path_resolver_cleanup(&resolver); return rv; @@ -400,20 +419,12 @@ symlink_resolve_inode_ref( int sqsh_file_symlink_resolve(struct SqshFile *context) { - int rv = 0; - struct SqshArchive *archive = context->archive; - uint32_t dir_inode = 0; - uint64_t inode_ref = 0; - rv = symlink_resolve_inode_ref(context, &inode_ref, &dir_inode); - if (rv < 0) { - goto out; - } - - sqsh__file_cleanup(context); - rv = sqsh__file_init(context, archive, inode_ref, dir_inode); + return symlink_resolve(context, false); +} -out: - return rv; +int +sqsh_file_symlink_resolve_all(struct SqshFile *context) { + return symlink_resolve(context, true); } const char * @@ -482,8 +493,10 @@ sqsh__file_cleanup(struct SqshFile *inode) { return sqsh__metablock_reader_cleanup(&inode->metablock); } -struct SqshFile * -sqsh_open(struct SqshArchive *archive, const char *path, int *err) { +static struct SqshFile * +open_file( + struct SqshArchive *archive, const char *path, int *err, + bool follow_symlink) { int rv; struct SqshPathResolver resolver = {0}; struct SqshFile *inode = NULL; @@ -492,7 +505,12 @@ sqsh_open(struct SqshArchive *archive, const char *path, int *err) { goto out; } - rv = sqsh_path_resolver_resolve(&resolver, path, true); + rv = sqsh_path_resolver_to_root(&resolver); + if (rv < 0) { + goto out; + } + + rv = sqsh_path_resolver_resolve(&resolver, path, follow_symlink); if (rv < 0) { goto out; } @@ -510,6 +528,16 @@ sqsh_open(struct SqshArchive *archive, const char *path, int *err) { return inode; } +struct SqshFile * +sqsh_open(struct SqshArchive *archive, const char *path, int *err) { + return open_file(archive, path, err, true); +} + +struct SqshFile * +sqsh_lopen(struct SqshArchive *archive, const char *path, int *err) { + return open_file(archive, path, err, false); +} + int sqsh_close(struct SqshFile *file) { SQSH_FREE_IMPL(sqsh__file_cleanup, file); diff --git a/libsqsh/src/tree/path_resolver.c b/libsqsh/src/tree/path_resolver.c index 5656e8a8..aaaefd8c 100644 --- a/libsqsh/src/tree/path_resolver.c +++ b/libsqsh/src/tree/path_resolver.c @@ -31,6 +31,8 @@ * @file path_resolver.c */ +#include "sqsh_archive.h" +#include "sqsh_file.h" #include #include @@ -40,223 +42,222 @@ #include #include -#include #define SQSH_DEFAULT_MAX_SYMLINKS_FOLLOWED 100 -SQSH_NO_UNUSED static int path_resolve( - struct SqshPathResolver *walker, const char *path, size_t path_len, - int recursion); +static int path_resolve( + struct SqshPathResolver *resolver, const char *path, size_t path_len, + bool follow_symlinks); -SQSH_NO_UNUSED static bool -is_beginning(const struct SqshPathResolver *walker) { - return walker->current_inode_ref == sqsh_file_inode_ref(&walker->cwd); +static size_t +path_segment_len(const char *path, size_t path_len) { + sqsh_index_t len = 0; + for (; len < path_len && path[len] != '/'; len++) { + } + return len; +} + +static const char * +path_next_segment(const char *path, size_t path_len) { + sqsh_index_t current_segment_len = path_segment_len(path, path_len); + if (current_segment_len == path_len) { + return NULL; + } else { + return &path[current_segment_len] + 1; + } +} + +static bool +is_beginning(const struct SqshPathResolver *resolver) { + return resolver->current_inode_ref == sqsh_file_inode_ref(&resolver->cwd); } SQSH_NO_UNUSED static int -update_inode_from_iterator(struct SqshPathResolver *walker) { - struct SqshDirectoryIterator *iterator = &walker->iterator; +update_inode_from_iterator(struct SqshPathResolver *resolver) { + struct SqshDirectoryIterator *iterator = &resolver->iterator; const uint32_t inode_number = sqsh_directory_iterator_inode(iterator); const uint64_t inode_ref = sqsh_directory_iterator_inode_ref(iterator); - if (sqsh_file_inode(&walker->cwd) == inode_number) { + if (sqsh_file_inode(&resolver->cwd) == inode_number) { return -SQSH_ERROR_CORRUPTED_INODE; } - if (sqsh_file_inode_ref(&walker->cwd) == inode_ref) { + if (sqsh_file_inode_ref(&resolver->cwd) == inode_ref) { return -SQSH_ERROR_CORRUPTED_INODE; } - walker->current_inode_ref = inode_ref; + + resolver->current_inode_ref = inode_ref; return 0; } SQSH_NO_UNUSED static int -update_inode_from_cwd(struct SqshPathResolver *walker) { +update_inode_from_cwd(struct SqshPathResolver *resolver) { int rv = 0; - struct SqshDirectoryIterator *iterator = &walker->iterator; - struct SqshFile *cwd = &walker->cwd; + struct SqshDirectoryIterator *iterator = &resolver->iterator; + struct SqshFile *cwd = &resolver->cwd; const uint64_t inode_ref = sqsh_file_inode_ref(cwd); rv = sqsh__directory_iterator_cleanup(iterator); if (rv < 0) { - goto out; + return rv; } rv = sqsh__directory_iterator_init(iterator, cwd); if (rv < 0) { - goto out; + return rv; } - walker->current_inode_ref = inode_ref; -out: - return rv; + resolver->current_inode_ref = inode_ref; + return 0; } -SQSH_NO_UNUSED static int -enter_directory(struct SqshPathResolver *walker, uint64_t inode_ref) { +static uint64_t +inode_number_to_ref( + const struct SqshPathResolver *resolver, uint32_t inode_number, + int *err) { int rv = 0; - struct SqshFile *cwd = &walker->cwd; + uint64_t inode_ref = 0; + const struct SqshInodeMap *inode_map = resolver->inode_map; - rv = sqsh__file_cleanup(cwd); - if (rv < 0) { - goto out; - } - - rv = sqsh__file_init(cwd, walker->archive, inode_ref, /* TODO */ 0); - if (rv < 0) { - goto out; - } - - if (sqsh_file_type(cwd) != SQSH_FILE_TYPE_DIRECTORY) { - rv = -SQSH_ERROR_NOT_A_DIRECTORY; - goto out; - } - - rv = update_inode_from_cwd(walker); + inode_ref = sqsh_inode_map_get2(inode_map, inode_number, &rv); if (rv < 0) { goto out; } out: - return rv; + if (err != NULL) { + *err = rv; + } + return inode_ref; } int -sqsh__path_resolver_init_from_inode_ref( - struct SqshPathResolver *walker, struct SqshArchive *archive, - uint64_t inode_ref) { +sqsh__path_resolver_init( + struct SqshPathResolver *resolver, struct SqshArchive *archive) { int rv = 0; - const struct SqshSuperblock *superblock = sqsh_archive_superblock(archive); - const struct SqshConfig *config = sqsh_archive_config(archive); - - walker->max_symlink_depth = SQSH_CONFIG_DEFAULT( - config->max_symlink_depth, SQSH_DEFAULT_MAX_SYMLINKS_FOLLOWED); - walker->archive = archive; - walker->root_inode_ref = sqsh_superblock_inode_root_ref(superblock); - rv = sqsh_archive_inode_map(archive, &walker->inode_map); + resolver->archive = archive; + rv = sqsh_archive_inode_map(archive, &resolver->inode_map); if (rv < 0) { goto out; } - rv = enter_directory(walker, inode_ref); + const struct SqshConfig *config = sqsh_archive_config(archive); + resolver->max_symlink_depth = SQSH_CONFIG_DEFAULT( + config->max_symlink_depth, SQSH_DEFAULT_MAX_SYMLINKS_FOLLOWED); + + const struct SqshSuperblock *superblock = + sqsh_archive_superblock(resolver->archive); + resolver->root_inode_ref = sqsh_superblock_inode_root_ref(superblock); out: return rv; } -int -sqsh__path_resolver_init( - struct SqshPathResolver *walker, struct SqshArchive *archive) { - const struct SqshSuperblock *superblock = sqsh_archive_superblock(archive); - const uint64_t inode_root_ref = sqsh_superblock_inode_root_ref(superblock); - - return sqsh__path_resolver_init_from_inode_ref( - walker, archive, inode_root_ref); +static struct SqshPathResolver * +new_resolver(struct SqshArchive *archive, int *err) { + SQSH_NEW_IMPL(sqsh__path_resolver_init, struct SqshPathResolver, archive); } +// TODO: To keep the API consistent, we need to move the resolver to the +// root_inode_ref in the public API. Internally we let the user take care of +// this. struct SqshPathResolver * sqsh_path_resolver_new(struct SqshArchive *archive, int *err) { - SQSH_NEW_IMPL(sqsh__path_resolver_init, struct SqshPathResolver, archive); -} - -int -sqsh_path_resolver_up(struct SqshPathResolver *walker) { int rv = 0; - const struct SqshFile *cwd = &walker->cwd; - /* We do not use the parent inode to check if it is the root node. - * According to the documentationen it *should* be zero. That's vague. - */ - - if (sqsh_file_inode_ref(cwd) == walker->root_inode_ref) { - return -SQSH_ERROR_WALKER_CANNOT_GO_UP; - } - const uint32_t parent_inode = sqsh_file_directory_parent_inode(cwd); - if (parent_inode <= 0) { - rv = -SQSH_ERROR_CORRUPTED_INODE; + struct SqshPathResolver *resolver = new_resolver(archive, &rv); + if (rv < 0) { goto out; } - const uint64_t parent_inode_ref = - sqsh_inode_map_get2(walker->inode_map, parent_inode, &rv); + rv = sqsh_path_resolver_to_root(resolver); if (rv < 0) { goto out; } - rv = enter_directory(walker, parent_inode_ref); +out: + if (err != NULL) { + *err = rv; + } + if (rv < 0) { + sqsh_path_resolver_free(resolver); + return NULL; + } + return resolver; +} + +int +sqsh__path_resolver_to_ref( + struct SqshPathResolver *resolver, uint64_t inode_ref) { + int rv = 0; + rv = sqsh__file_cleanup(&resolver->cwd); + + rv = sqsh__file_init(&resolver->cwd, resolver->archive, inode_ref, 0); if (rv < 0) { goto out; } + rv = update_inode_from_cwd(resolver); out: return rv; } -bool -sqsh_path_resolver_next(struct SqshPathResolver *walker, int *err) { - struct SqshDirectoryIterator *iterator = &walker->iterator; +int +sqsh__path_resolver_to_inode( + struct SqshPathResolver *resolver, uint32_t inode_number) { int rv = 0; - - bool has_next = sqsh_directory_iterator_next(iterator, &rv); + const uint64_t inode_ref = inode_number_to_ref(resolver, inode_number, &rv); if (rv < 0) { goto out; } - if (has_next != false) { - rv = update_inode_from_iterator(walker); - if (rv < 0) { - goto out; - } - } + rv = sqsh__path_resolver_to_ref(resolver, inode_ref); out: - if (err != NULL) { - *err = rv; - } - if (rv < 0) { - return false; - } else { - return has_next; - } + return rv; } -enum SqshFileType -sqsh_path_resolver_type(const struct SqshPathResolver *walker) { - if (is_beginning(walker)) { - return sqsh_file_type(&walker->cwd); - } else { - return sqsh_directory_iterator_file_type(&walker->iterator); - } +int +sqsh_path_resolver_to_root(struct SqshPathResolver *resolver) { + return sqsh__path_resolver_to_ref(resolver, resolver->root_inode_ref); } -const char * -sqsh_path_resolver_name(const struct SqshPathResolver *walker) { - size_t size; - if (is_beginning(walker)) { - return NULL; - } else { - return sqsh_directory_iterator_name2(&walker->iterator, &size); +int +sqsh_path_resolver_down(struct SqshPathResolver *resolver) { + if (is_beginning(resolver)) { + return -SQSH_ERROR_WALKER_CANNOT_GO_DOWN; } + const uint64_t child_inode_ref = + sqsh_directory_iterator_inode_ref(&resolver->iterator); + + return sqsh__path_resolver_to_ref(resolver, child_inode_ref); } -uint16_t -sqsh_path_resolver_name_size(const struct SqshPathResolver *walker) { - size_t size; - if (is_beginning(walker)) { - return 0; - } else { - sqsh_directory_iterator_name2(&walker->iterator, &size); - return (uint16_t)size; +int +sqsh_path_resolver_up(struct SqshPathResolver *resolver) { + int rv = 0; + if (!is_beginning(resolver)) { + return update_inode_from_cwd(resolver); } -} -char * -sqsh_path_resolver_name_dup(const struct SqshPathResolver *walker) { - if (is_beginning(walker)) { - return NULL; - } else { - return sqsh_directory_iterator_name_dup(&walker->iterator); + if (sqsh_file_inode_ref(&resolver->cwd) == resolver->root_inode_ref) { + return -SQSH_ERROR_WALKER_CANNOT_GO_UP; + } + const uint32_t parent_inode = + sqsh_file_directory_parent_inode(&resolver->cwd); + if (parent_inode <= 0) { + rv = -SQSH_ERROR_CORRUPTED_INODE; + goto out; } + + rv = sqsh__path_resolver_to_inode(resolver, parent_inode); +out: + return rv; } int -sqsh_path_resolver_revert(struct SqshPathResolver *walker) { - return update_inode_from_cwd(walker); +sqsh_path_resolver_revert(struct SqshPathResolver *resolver) { + return update_inode_from_cwd(resolver); +} + +uint64_t +sqsh_path_resolver_inode_ref(const struct SqshPathResolver *resolver) { + return resolver->current_inode_ref; } int @@ -264,197 +265,243 @@ sqsh_path_resolver_lookup( struct SqshPathResolver *walker, const char *name, const size_t name_size) { int rv = 0; - struct SqshDirectoryIterator *iterator = &walker->iterator; - rv = sqsh_path_resolver_revert(walker); - if (rv < 0) { - return rv; - } - rv = sqsh_directory_iterator_lookup(iterator, name, name_size); + // revert to the beginning of the directory + rv = update_inode_from_cwd(walker); if (rv < 0) { - return rv; + goto out; } - return update_inode_from_iterator(walker); -} - -int -sqsh_path_resolver_down(struct SqshPathResolver *walker) { - if (is_beginning(walker)) { - return -SQSH_ERROR_WALKER_CANNOT_GO_DOWN; + rv = sqsh_directory_iterator_lookup(&walker->iterator, name, name_size); + if (rv < 0) { + goto out; } - const uint64_t child_inode_ref = - sqsh_directory_iterator_inode_ref(&walker->iterator); - - return enter_directory(walker, child_inode_ref); -} -int -sqsh_path_resolver_to_root(struct SqshPathResolver *walker) { - return enter_directory(walker, walker->root_inode_ref); -} - -struct SqshFile * -sqsh_path_resolver_open_file(const struct SqshPathResolver *walker, int *err) { - uint32_t dir_inode = sqsh_path_resolver_dir_inode(walker); - return sqsh_open_by_ref2( - walker->archive, walker->current_inode_ref, dir_inode, err); + rv = update_inode_from_iterator(walker); +out: + return rv; } uint32_t -sqsh_path_resolver_dir_inode(const struct SqshPathResolver *walker) { - if (is_beginning(walker)) { - return sqsh_file_directory_parent_inode(&walker->cwd); +sqsh_path_resolver_dir_inode(const struct SqshPathResolver *resolver) { + if (is_beginning(resolver)) { + return sqsh_file_directory_parent_inode(&resolver->cwd); } else { - return sqsh_file_inode(&walker->cwd); + return sqsh_file_inode(&resolver->cwd); } } -uint64_t -sqsh_path_resolver_inode_ref(const struct SqshPathResolver *walker) { - return walker->current_inode_ref; -} - -static size_t -path_segment_len(const char *path, size_t path_len) { - sqsh_index_t len = 0; - for (; len < path_len && path[len] != '/'; len++) { - } - return len; +struct SqshFile * +sqsh_path_resolver_open_file( + const struct SqshPathResolver *resolver, int *err) { + uint32_t dir_inode = sqsh_path_resolver_dir_inode(resolver); + uint64_t inode_ref = resolver->current_inode_ref; + return sqsh_open_by_ref2(resolver->archive, inode_ref, dir_inode, err); } -static const char * -path_next_segment(const char *path, size_t path_len) { - sqsh_index_t current_segment_len = path_segment_len(path, path_len); - if (current_segment_len == path_len) { - return NULL; +enum SqshFileType +sqsh__path_resolver_type(const struct SqshPathResolver *resolver) { + if (is_beginning(resolver)) { + return SQSH_FILE_TYPE_DIRECTORY; } else { - return &path[current_segment_len] + 1; + return sqsh_directory_iterator_file_type(&resolver->iterator); } } - -static int -path_resolver_follow_symlink(struct SqshPathResolver *walker, int recursion) { - struct SqshFile inode = {0}; +int +sqsh__path_resolver_follow_symlink(struct SqshPathResolver *resolver) { int rv = 0; - // if symlinks_followed is smaller than zero, the caller intends to - // disable symlink following - if (recursion < 0) { - return 0; - } - if (recursion == 0) { + if (resolver->current_symlink_depth > resolver->max_symlink_depth) { return -SQSH_ERROR_TOO_MANY_SYMLINKS_FOLLOWED; + } else if (sqsh__path_resolver_type(resolver) != SQSH_FILE_TYPE_SYMLINK) { + return -SQSH_ERROR_NOT_A_SYMLINK; } + resolver->current_symlink_depth++; - rv = sqsh__file_init( - &inode, walker->archive, walker->current_inode_ref, - /* TODO */ 0); + uint64_t inode_ref = sqsh_directory_iterator_inode_ref(&resolver->iterator); + uint32_t dir_inode = sqsh_path_resolver_dir_inode(resolver); + + rv = update_inode_from_cwd(resolver); if (rv < 0) { goto out; } - rv = sqsh_path_resolver_revert(walker); + struct SqshFile file = {0}; + rv = sqsh__file_init(&file, resolver->archive, inode_ref, dir_inode); if (rv < 0) { goto out; } - const char *symlink = sqsh_file_symlink(&inode); - size_t symlink_len = sqsh_file_symlink_size(&inode); - rv = path_resolve(walker, symlink, symlink_len, recursion - 1); + const char *target = sqsh_file_symlink(&file); + size_t target_size = sqsh_file_symlink_size(&file); + + rv = path_resolve(resolver, target, target_size, true); out: - sqsh__file_cleanup(&inode); + sqsh__file_cleanup(&file); return rv; } +int +sqsh__path_resolver_follow_all_symlinks(struct SqshPathResolver *resolver) { + while (1) { + int rv = sqsh__path_resolver_follow_symlink(resolver); + if (rv == -SQSH_ERROR_NOT_A_SYMLINK) { + return 0; + } else if (rv < 0) { + return rv; + } + } +} + +enum SqshFileType +sqsh_path_resolver_type(const struct SqshPathResolver *walker) { + if (is_beginning(walker)) { + return sqsh_file_type(&walker->cwd); + } else { + return sqsh_directory_iterator_file_type(&walker->iterator); + } +} + static int path_resolve( - struct SqshPathResolver *walker, const char *path, size_t path_len, - int recursion) { + struct SqshPathResolver *resolver, const char *path, size_t path_len, + bool follow_symlinks) { int rv = 0; - const char *segment = path, *next_segment; + const char *segment = path; + const char *next_segment = NULL; size_t remaining_path_len = path_len; - bool is_dir = true; if (segment[0] == '/') { - rv = sqsh_path_resolver_to_root(walker); + rv = sqsh_path_resolver_to_root(resolver); if (rv < 0) { goto out; } } + bool is_dir = true; do { + if (!is_dir) { + rv = -SQSH_ERROR_NOT_A_DIRECTORY; + goto out; + } const size_t segment_len = path_segment_len(segment, remaining_path_len); next_segment = path_next_segment(segment, remaining_path_len); remaining_path_len -= segment_len + 1; - if (strncmp(".", segment, segment_len) == 0 || segment_len == 0) { - is_dir = true; + if (segment_len == 0) { + continue; + } else if (segment_len == 1 && segment[0] == '.') { continue; - } else if (strncmp("..", segment, segment_len) == 0) { - is_dir = true; - rv = sqsh_path_resolver_up(walker); - if (rv == -SQSH_ERROR_WALKER_CANNOT_GO_UP) { - // To mimic the behaviour of the unix file tree, we ignore - // attempts to go up from the root node. - rv = 0; + } else if (segment_len == 2 && segment[0] == '.' && segment[1] == '.') { + rv = sqsh_path_resolver_up(resolver); + if (rv < 0) { + goto out; } + continue; } else { - rv = sqsh_path_resolver_lookup(walker, segment, segment_len); + rv = sqsh_path_resolver_lookup(resolver, segment, segment_len); if (rv < 0) { goto out; } - switch (sqsh_path_resolver_type(walker)) { - case SQSH_FILE_TYPE_SYMLINK: - rv = path_resolver_follow_symlink(walker, recursion); - if (rv < 0) { - goto out; - } - is_dir = sqsh_path_resolver_type(walker) == - SQSH_FILE_TYPE_DIRECTORY; - break; - case SQSH_FILE_TYPE_DIRECTORY: - is_dir = true; - rv = sqsh_path_resolver_down(walker); - break; - default: - is_dir = false; - break; + } + + if (next_segment != NULL || follow_symlinks) { + rv = sqsh__path_resolver_follow_all_symlinks(resolver); + if (rv < 0) { + goto out; } } - if (rv < 0) { - goto out; + is_dir = sqsh_path_resolver_type(resolver) == SQSH_FILE_TYPE_DIRECTORY; + if (is_dir && !is_beginning(resolver)) { + rv = sqsh_path_resolver_down(resolver); } - } while ((segment = next_segment) && is_dir); - if (segment != NULL && is_dir == false) { - rv = -SQSH_ERROR_NOT_A_DIRECTORY; - } - + } while ((segment = next_segment) != NULL); out: return rv; } int -sqsh__path_resolver_resolve_len( - struct SqshPathResolver *walker, const char *path, size_t path_len, - bool follow_links) { - int recursion = follow_links ? (int)walker->max_symlink_depth : -1; - return path_resolve(walker, path, path_len, recursion); +sqsh__path_resolver_resolve_nt( + struct SqshPathResolver *resolver, const char *path, size_t path_len, + bool follow_symlinks) { + resolver->current_symlink_depth = 0; + return path_resolve(resolver, path, path_len, follow_symlinks); } int sqsh_path_resolver_resolve( - struct SqshPathResolver *walker, const char *path, bool follow_links) { - return sqsh__path_resolver_resolve_len( - walker, path, strlen(path), follow_links); + struct SqshPathResolver *resolver, const char *path, + bool follow_symlinks) { + return sqsh__path_resolver_resolve_nt( + resolver, path, strlen(path), follow_symlinks); } int -sqsh__path_resolver_cleanup(struct SqshPathResolver *walker) { - sqsh__file_cleanup(&walker->cwd); - sqsh__directory_iterator_cleanup(&walker->iterator); +sqsh__path_resolver_cleanup(struct SqshPathResolver *resolver) { + sqsh__file_cleanup(&resolver->cwd); + sqsh__directory_iterator_cleanup(&resolver->iterator); + return 0; } int -sqsh_path_resolver_free(struct SqshPathResolver *walker) { - SQSH_FREE_IMPL(sqsh__path_resolver_cleanup, walker); +sqsh_path_resolver_free(struct SqshPathResolver *resolver) { + SQSH_FREE_IMPL(sqsh__path_resolver_cleanup, resolver); +} + +/* TO BE DEPRECATED FUNCTIONS */ + +bool +sqsh_path_resolver_next(struct SqshPathResolver *walker, int *err) { + struct SqshDirectoryIterator *iterator = &walker->iterator; + int rv = 0; + + bool has_next = sqsh_directory_iterator_next(iterator, &rv); + if (rv < 0) { + goto out; + } + if (has_next != false) { + rv = update_inode_from_iterator(walker); + if (rv < 0) { + goto out; + } + } + +out: + if (err != NULL) { + *err = rv; + } + if (rv < 0) { + return false; + } else { + return has_next; + } +} + +const char * +sqsh_path_resolver_name(const struct SqshPathResolver *walker) { + size_t size; + if (is_beginning(walker)) { + return NULL; + } else { + return sqsh_directory_iterator_name2(&walker->iterator, &size); + } +} + +uint16_t +sqsh_path_resolver_name_size(const struct SqshPathResolver *walker) { + size_t size; + if (is_beginning(walker)) { + return 0; + } else { + sqsh_directory_iterator_name2(&walker->iterator, &size); + return (uint16_t)size; + } +} + +char * +sqsh_path_resolver_name_dup(const struct SqshPathResolver *walker) { + if (is_beginning(walker)) { + return NULL; + } else { + return sqsh_directory_iterator_name_dup(&walker->iterator); + } } diff --git a/libsqsh/src/tree/walker.c b/libsqsh/src/tree/walker.c index 155343c0..4a223036 100644 --- a/libsqsh/src/tree/walker.c +++ b/libsqsh/src/tree/walker.c @@ -48,7 +48,11 @@ static int tree_walker_init(struct SqshTreeWalker *walker, struct SqshArchive *archive) { - return sqsh__path_resolver_init(&walker->inner, archive); + int rv = sqsh__path_resolver_init(&walker->inner, archive); + if (rv < 0) { + return rv; + } + return sqsh_path_resolver_to_root(&walker->inner); } struct SqshTreeWalker * diff --git a/test/libsqsh/file/file.c b/test/libsqsh/file/file.c index d3d51c3c..52e052ea 100644 --- a/test/libsqsh/file/file.c +++ b/test/libsqsh/file/file.c @@ -105,18 +105,16 @@ UTEST(file, resolve_file) { }; mk_stub(&archive, payload, sizeof(payload)); - uint64_t ref = sqsh_address_ref_create(0, 128); - struct SqshFile symlink = {0}; - rv = sqsh__file_init(&symlink, &archive, ref, /* TODO */ 0); + struct SqshFile *symlink = sqsh_lopen(&archive, "/src", &rv); ASSERT_EQ(0, rv); - ASSERT_EQ(SQSH_FILE_TYPE_SYMLINK, sqsh_file_type(&symlink)); + ASSERT_EQ(SQSH_FILE_TYPE_SYMLINK, sqsh_file_type(symlink)); - rv = sqsh_file_symlink_resolve(&symlink); + rv = sqsh_file_symlink_resolve(symlink); ASSERT_EQ(0, rv); - ASSERT_EQ(SQSH_FILE_TYPE_FILE, sqsh_file_type(&symlink)); - ASSERT_EQ((uint32_t)3, sqsh_file_inode(&symlink)); + ASSERT_EQ(SQSH_FILE_TYPE_FILE, sqsh_file_type(symlink)); + ASSERT_EQ((uint32_t)3, sqsh_file_inode(symlink)); - sqsh__file_cleanup(&symlink); + sqsh_close(symlink); sqsh__archive_cleanup(&archive); } diff --git a/test/libsqsh/integration.c b/test/libsqsh/integration.c index 0cc046ba..9546eadd 100644 --- a/test/libsqsh/integration.c +++ b/test/libsqsh/integration.c @@ -68,6 +68,9 @@ UTEST(integration, sqsh_get_nonexistant) { rv = sqsh__path_resolver_init(&resolver, &sqsh); ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_to_root(&resolver); + ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_resolve(&resolver, "/nonexistant", false); ASSERT_GT(0, rv); @@ -91,6 +94,9 @@ UTEST(integration, path_resolver) { rv = sqsh__path_resolver_init(&resolver, &sqsh); ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_to_root(&resolver); + ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_resolve(&resolver, "/large_dir", false); ASSERT_EQ(0, rv); @@ -205,6 +211,9 @@ UTEST(integration, sqsh_cat_fragment) { rv = sqsh__path_resolver_init(&resolver, &sqsh); ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_to_root(&resolver); + ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_resolve(&resolver, "a", false); ASSERT_EQ(0, rv); @@ -256,6 +265,9 @@ UTEST(integration, sqsh_cat_datablock_and_fragment) { rv = sqsh__path_resolver_init(&resolver, &sqsh); ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_to_root(&resolver); + ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_resolve(&resolver, "b", false); ASSERT_EQ(0, rv); @@ -309,6 +321,9 @@ UTEST(integration, sqsh_cat_size_overflow) { rv = sqsh__path_resolver_init(&resolver, &sqsh); ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_to_root(&resolver); + ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_resolve(&resolver, "b", false); ASSERT_EQ(0, rv); @@ -385,6 +400,9 @@ UTEST(integration, sqsh_test_extended_dir) { rv = sqsh__path_resolver_init(&resolver, &sqsh); ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_to_root(&resolver); + ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_resolve(&resolver, "/large_dir/999", false); ASSERT_EQ(0, rv); diff --git a/test/libsqsh/tree/path_resolver.c b/test/libsqsh/tree/path_resolver.c index ae6e94a6..082fdb09 100644 --- a/test/libsqsh/tree/path_resolver.c +++ b/test/libsqsh/tree/path_resolver.c @@ -66,6 +66,9 @@ UTEST(path_resolver, resolver_symlink_recursion) { rv = sqsh__path_resolver_init(&resolver, &archive); ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_to_root(&resolver); + ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_resolve(&resolver, "src", true); ASSERT_EQ(-SQSH_ERROR_TOO_MANY_SYMLINKS_FOLLOWED, rv); @@ -105,6 +108,9 @@ UTEST(path_resolver, resolver_symlink_alternating_recursion) { rv = sqsh__path_resolver_init(&resolver, &archive); ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_to_root(&resolver); + ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_resolve(&resolver, "src1", true); ASSERT_EQ(-SQSH_ERROR_TOO_MANY_SYMLINKS_FOLLOWED, rv); @@ -144,6 +150,9 @@ UTEST(path_resolver, resolver_symlink_open) { rv = sqsh__path_resolver_init(&resolver, &archive); ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_to_root(&resolver); + ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_resolve(&resolver, "src", true); ASSERT_EQ(0, rv); @@ -199,6 +208,9 @@ UTEST(path_resolver, resolver_directory_enter) { rv = sqsh__path_resolver_init(&resolver, &archive); ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_to_root(&resolver); + ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_resolve(&resolver, "dir", true); ASSERT_EQ(0, rv); expect_inode(&resolver, 2); @@ -234,6 +246,9 @@ UTEST(path_resolver, resolver_uninitialized_up) { rv = sqsh__path_resolver_init(&resolver, &archive); ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_to_root(&resolver); + ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_up(&resolver); ASSERT_EQ(-SQSH_ERROR_WALKER_CANNOT_GO_UP, rv); @@ -264,6 +279,9 @@ UTEST(path_resolver, resolver_uninitialized_down) { rv = sqsh__path_resolver_init(&resolver, &archive); ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_to_root(&resolver); + ASSERT_EQ(0, rv); + rv = sqsh_path_resolver_down(&resolver); ASSERT_EQ(-SQSH_ERROR_WALKER_CANNOT_GO_DOWN, rv); diff --git a/test/libsqsh/tree/walker.c b/test/libsqsh/tree/walker.c index bf55fdba..43054c68 100644 --- a/test/libsqsh/tree/walker.c +++ b/test/libsqsh/tree/walker.c @@ -62,14 +62,13 @@ UTEST(tree_walker, walker_symlink_recursion) { }; mk_stub(&archive, payload, sizeof(payload)); - struct SqshTreeWalker walker = {0}; - rv = sqsh__path_resolver_init(&walker.inner, &archive); + struct SqshTreeWalker *walker = sqsh_tree_walker_new(&archive, &rv); ASSERT_EQ(0, rv); - rv = sqsh_tree_walker_resolve(&walker, "src", true); + rv = sqsh_tree_walker_resolve(walker, "src", true); ASSERT_EQ(-SQSH_ERROR_TOO_MANY_SYMLINKS_FOLLOWED, rv); - sqsh__path_resolver_cleanup(&walker.inner); + sqsh_tree_walker_free(walker); sqsh__archive_cleanup(&archive); } @@ -101,14 +100,13 @@ UTEST(tree_walker, walker_symlink_alternating_recursion) { }; mk_stub(&archive, payload, sizeof(payload)); - struct SqshTreeWalker walker = {0}; - rv = sqsh__path_resolver_init(&walker.inner, &archive); + struct SqshTreeWalker *walker = sqsh_tree_walker_new(&archive, &rv); ASSERT_EQ(0, rv); - rv = sqsh_tree_walker_resolve(&walker, "src1", true); + rv = sqsh_tree_walker_resolve(walker, "src1", true); ASSERT_EQ(-SQSH_ERROR_TOO_MANY_SYMLINKS_FOLLOWED, rv); - sqsh__path_resolver_cleanup(&walker.inner); + sqsh_tree_walker_free(walker); sqsh__archive_cleanup(&archive); } @@ -140,19 +138,18 @@ UTEST(tree_walker, walker_symlink_open) { }; mk_stub(&archive, payload, sizeof(payload)); - struct SqshTreeWalker walker = {0}; - rv = sqsh__path_resolver_init(&walker.inner, &archive); + struct SqshTreeWalker *walker = sqsh_tree_walker_new(&archive, &rv); ASSERT_EQ(0, rv); - rv = sqsh_tree_walker_resolve(&walker, "src", true); + rv = sqsh_tree_walker_resolve(walker, "src", true); ASSERT_EQ(0, rv); - const char *name = sqsh_tree_walker_name(&walker); - size_t name_size = sqsh_tree_walker_name_size(&walker); + const char *name = sqsh_tree_walker_name(walker); + size_t name_size = sqsh_tree_walker_name_size(walker); ASSERT_EQ((size_t)3, name_size); ASSERT_EQ(0, memcmp(name, "tgt", name_size)); - sqsh__path_resolver_cleanup(&walker.inner); + sqsh_tree_walker_free(walker); sqsh__archive_cleanup(&archive); } @@ -195,19 +192,18 @@ UTEST(tree_walker, walker_directory_enter) { }; mk_stub(&archive, payload, sizeof(payload)); - struct SqshTreeWalker walker = {0}; - rv = sqsh__path_resolver_init(&walker.inner, &archive); + struct SqshTreeWalker *walker = sqsh_tree_walker_new(&archive, &rv); ASSERT_EQ(0, rv); - rv = sqsh_tree_walker_resolve(&walker, "dir", true); + rv = sqsh_tree_walker_resolve(walker, "dir", true); ASSERT_EQ(0, rv); - expect_inode(&walker, 2); + expect_inode(walker, 2); - rv = sqsh_tree_walker_up(&walker); + rv = sqsh_tree_walker_up(walker); ASSERT_EQ(0, rv); - expect_inode(&walker, 1); + expect_inode(walker, 1); - sqsh__path_resolver_cleanup(&walker.inner); + sqsh_tree_walker_free(walker); sqsh__archive_cleanup(&archive); } From 733e1182eb8a99cf61d8eb453e3fd200e1cdc0c0 Mon Sep 17 00:00:00 2001 From: Enno Boland Date: Tue, 28 May 2024 11:47:04 +0200 Subject: [PATCH 2/4] path_resolver: check for inconsistent directory/inode information --- libsqsh/src/tree/path_resolver.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/libsqsh/src/tree/path_resolver.c b/libsqsh/src/tree/path_resolver.c index aaaefd8c..992fdbb2 100644 --- a/libsqsh/src/tree/path_resolver.c +++ b/libsqsh/src/tree/path_resolver.c @@ -31,8 +31,6 @@ * @file path_resolver.c */ -#include "sqsh_archive.h" -#include "sqsh_file.h" #include #include @@ -193,6 +191,11 @@ sqsh__path_resolver_to_ref( goto out; } + if (sqsh_file_type(&resolver->cwd) != SQSH_FILE_TYPE_DIRECTORY) { + rv = -SQSH_ERROR_NOT_A_DIRECTORY; + goto out; + } + rv = update_inode_from_cwd(resolver); out: return rv; @@ -219,13 +222,28 @@ sqsh_path_resolver_to_root(struct SqshPathResolver *resolver) { int sqsh_path_resolver_down(struct SqshPathResolver *resolver) { + int rv = 0; if (is_beginning(resolver)) { return -SQSH_ERROR_WALKER_CANNOT_GO_DOWN; + } else if (sqsh__path_resolver_type(resolver) != SQSH_FILE_TYPE_DIRECTORY) { + return -SQSH_ERROR_NOT_A_DIRECTORY; } const uint64_t child_inode_ref = sqsh_directory_iterator_inode_ref(&resolver->iterator); - return sqsh__path_resolver_to_ref(resolver, child_inode_ref); + rv = sqsh__path_resolver_to_ref(resolver, child_inode_ref); + + if (rv == -SQSH_ERROR_NOT_A_DIRECTORY) { + // We checked above if the directory iterator was pointing to a + // directory if we get this error here, it means that either the inode + // or the directory is corrupted. + rv = -SQSH_ERROR_CORRUPTED_INODE; + } else if (rv < 0) { + goto out; + } + +out: + return rv; } int From 753fd4757de7e2707af8169c8707b1cc9f13a5a5 Mon Sep 17 00:00:00 2001 From: Enno Boland Date: Tue, 28 May 2024 11:46:13 +0200 Subject: [PATCH 3/4] test: add tests for inconsistent directory/inode informations. --- test/libsqsh/tree/path_resolver.c | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/libsqsh/tree/path_resolver.c b/test/libsqsh/tree/path_resolver.c index 082fdb09..2d2b4df6 100644 --- a/test/libsqsh/tree/path_resolver.c +++ b/test/libsqsh/tree/path_resolver.c @@ -176,6 +176,45 @@ expect_inode(struct SqshPathResolver *resolver, uint32_t inode_number) { sqsh_close(file); } +UTEST(path_resolver, resolver_inconsistent_file_type) { + int rv; + struct SqshArchive archive = {0}; + uint8_t payload[] = { + /* clang-format off */ + SQSH_HEADER, + /* inode */ + [INODE_TABLE_OFFSET] = METABLOCK_HEADER(0, 1024), + INODE_HEADER(1, 0, 0, 0, 0, 1), + INODE_BASIC_DIR(0, 1024, 0, 0), + + [INODE_TABLE_OFFSET+2+128] = + INODE_HEADER(2, 0, 0, 0, 0, 3), + INODE_BASIC_FILE(0, 0xFFFFFFFF, 0, 0), + + [DIRECTORY_TABLE_OFFSET] = METABLOCK_HEADER(0, 128), + DIRECTORY_HEADER(1, 0, 0), + DIRECTORY_ENTRY(128, 2, /* wrong file type */ 1, 4), + 'f', 'i', 'l', 'e', + + [FRAGMENT_TABLE_OFFSET] = 0, + /* clang-format on */ + }; + mk_stub(&archive, payload, sizeof(payload)); + + struct SqshPathResolver resolver = {0}; + rv = sqsh__path_resolver_init(&resolver, &archive); + ASSERT_EQ(0, rv); + + rv = sqsh_path_resolver_to_root(&resolver); + ASSERT_EQ(0, rv); + + rv = sqsh_path_resolver_resolve(&resolver, "file", true); + ASSERT_EQ(-SQSH_ERROR_CORRUPTED_INODE, rv); + + sqsh__path_resolver_cleanup(&resolver); + sqsh__archive_cleanup(&archive); +} + UTEST(path_resolver, resolver_directory_enter) { int rv; struct SqshArchive archive = {0}; From 3c87326e8317f72803da650da5f0e41ae1a98ab1 Mon Sep 17 00:00:00 2001 From: Enno Boland Date: Tue, 28 May 2024 11:46:45 +0200 Subject: [PATCH 4/4] test: add test trying to set a file as cwd. --- test/libsqsh/tree/path_resolver.c | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/libsqsh/tree/path_resolver.c b/test/libsqsh/tree/path_resolver.c index 2d2b4df6..3e35c6d2 100644 --- a/test/libsqsh/tree/path_resolver.c +++ b/test/libsqsh/tree/path_resolver.c @@ -215,6 +215,45 @@ UTEST(path_resolver, resolver_inconsistent_file_type) { sqsh__archive_cleanup(&archive); } +UTEST(path_resolver, resolver_file_enter) { + int rv; + struct SqshArchive archive = {0}; + uint8_t payload[] = { + /* clang-format off */ + SQSH_HEADER, + /* inode */ + [INODE_TABLE_OFFSET] = METABLOCK_HEADER(0, 1024), + INODE_HEADER(1, 0, 0, 0, 0, 1), + INODE_BASIC_DIR(0, 1024, 0, 0), + + [INODE_TABLE_OFFSET+2+128] = + INODE_HEADER(2, 0, 0, 0, 0, 3), + INODE_BASIC_FILE(0, 0xFFFFFFFF, 0, 0), + + [DIRECTORY_TABLE_OFFSET] = METABLOCK_HEADER(0, 128), + DIRECTORY_HEADER(1, 0, 0), + DIRECTORY_ENTRY(128, 2, 2, 4), + 'f', 'i', 'l', 'e', + + [FRAGMENT_TABLE_OFFSET] = 0, + /* clang-format on */ + }; + mk_stub(&archive, payload, sizeof(payload)); + + struct SqshPathResolver resolver = {0}; + rv = sqsh__path_resolver_init(&resolver, &archive); + ASSERT_EQ(0, rv); + + rv = sqsh_path_resolver_to_root(&resolver); + ASSERT_EQ(0, rv); + + rv = sqsh_path_resolver_resolve(&resolver, "file/", true); + ASSERT_EQ(-SQSH_ERROR_NOT_A_DIRECTORY, rv); + + sqsh__path_resolver_cleanup(&resolver); + sqsh__archive_cleanup(&archive); +} + UTEST(path_resolver, resolver_directory_enter) { int rv; struct SqshArchive archive = {0};