From 7a8995f322408e4090f7eae6db775b56baeb250b Mon Sep 17 00:00:00 2001 From: Thomas Schmitt Date: Sun, 1 Jul 2012 18:41:54 +0200 Subject: [PATCH] New API call iso_tree_resolve_symlink(). --- libisofs/libisofs.h | 37 ++++++++++++++++++++ libisofs/libisofs.ver | 1 + libisofs/messages.c | 4 +++ libisofs/tree.c | 81 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+) diff --git a/libisofs/libisofs.h b/libisofs/libisofs.h index 3f4f73f..c6ac7e2 100644 --- a/libisofs/libisofs.h +++ b/libisofs/libisofs.h @@ -5059,6 +5059,37 @@ int iso_tree_path_to_node(IsoImage *image, const char *path, IsoNode **node); */ char *iso_tree_get_node_path(IsoNode *node); +/** + * Get the destination node of a symbolic link within the IsoImage. + * + * @param img + * The image wherein to try resolving the link. + * @param sym + * The symbolic link node which to resolve. + * @param res + * Will return the found destination node, in case of success. + * Call iso_node_ref() / iso_node_unref() if you intend to use the node + * over API calls which might in any event delete it. + * @param depth + * Prevents endless loops. Submit as 0. + * @param flag + * Bitfield for control purposes. Submit 0 for now. + * @return + * 1 on success, + * < 0 on failure, especially ISO_DEEP_SYMLINK and ISO_DEAD_SYMLINK + * + * @since 1.2.4 + */ +int iso_tree_resolve_symlink(IsoImage *img, IsoSymlink *sym, IsoNode **res, + int *depth, int flag); + +/* Maximum number link resolution steps before ISO_DEEP_SYMLINK gets + * returned by iso_tree_resolve_symlink(). + * + * @since 1.2.4 +*/ +#define LIBISO_MAX_LINK_DEPTH 100 + /** * Increments the reference counting of the given IsoDataSource. * @@ -7373,6 +7404,12 @@ int iso_image_hfsplus_get_blessed(IsoImage *img, IsoNode ***blessed_nodes, /** Name collision in HFS+, mangling not possible (FAILURE, HIGH, -393) */ #define ISO_HFSP_NO_MANGLE 0xE830FE77 +/** Symbolic link cannot be resolved (FAILURE, HIGH, -394) */ +#define ISO_DEAD_SYMLINK 0xE830FE76 + +/** Too many chained symbolic links (FAILURE, HIGH, -395) */ +#define ISO_DEEP_SYMLINK 0xE830FE75 + /* Internal developer note: Place new error codes directly above this comment. diff --git a/libisofs/libisofs.ver b/libisofs/libisofs.ver index f921b19..b9df3eb 100644 --- a/libisofs/libisofs.ver +++ b/libisofs/libisofs.ver @@ -257,6 +257,7 @@ iso_tree_get_node_path; iso_tree_get_replace_mode; iso_tree_path_to_node; iso_tree_remove_exclude; +iso_tree_resolve_symlink; iso_tree_set_follow_symlinks; iso_tree_set_ignore_hidden; iso_tree_set_ignore_special; diff --git a/libisofs/messages.c b/libisofs/messages.c index 19b8c8d..a9ce81a 100644 --- a/libisofs/messages.c +++ b/libisofs/messages.c @@ -496,6 +496,10 @@ const char *iso_error_to_msg(int errcode) return "APM block size prevents coexistence with GPT"; case ISO_HFSP_NO_MANGLE: return "Name collision in HFS+, mangling not possible"; + case ISO_DEAD_SYMLINK: + return "Symbolic link cannot be resolved"; + case ISO_DEEP_SYMLINK: + return "Too many chained symbolic links"; default: return "Unknown error"; } diff --git a/libisofs/tree.c b/libisofs/tree.c index cc04d43..dd6dc0c 100644 --- a/libisofs/tree.c +++ b/libisofs/tree.c @@ -1196,3 +1196,84 @@ int iso_tree_clone(IsoNode *node, return ret; } + +int iso_tree_resolve_symlink(IsoImage *img, IsoSymlink *sym, IsoNode **res, + int *depth, int flag) +{ + IsoDir *cur_dir = NULL; + IsoNode *n, *resolved_node; + char *dest, *dest_start, *dest_end; + int ret = 0; + unsigned int comp_len, dest_len; + + dest = sym->dest; + dest_len = strlen(dest); + + if (dest[0] == '/') { + + /* >>> ??? How to resolve absolute links without knowing the + path of the future mount point ? + ??? Would it be better to throw error ? + I can only assume that it gets mounted at / during some stage + of booting. + */; + + cur_dir = img->root; + dest_end = dest; + } else { + cur_dir = sym->node.parent; + if (cur_dir == NULL) + cur_dir = img->root; + dest_end = dest - 1; + } + + while (dest_end < dest + dest_len) { + dest_start = dest_end + 1; + dest_end = strchr(dest_start, '/'); + if (dest_end == NULL) + dest_end = dest_start + strlen(dest_start); + comp_len = dest_end - dest_start; + if (comp_len == 0 || (comp_len == 1 && dest_start[0] == '.')) + continue; + if (comp_len == 2 && dest_start[0] == '.' && dest_start[1] == '.') { + cur_dir = cur_dir->node.parent; + if (cur_dir == NULL) /* link shoots over root */ + return ISO_DEAD_SYMLINK; + continue; + } + + /* Search node in cur_dir */ + for (n = cur_dir->children; n != NULL; n = n->next) + if (strncmp(dest_start, n->name, comp_len) == 0 && + strlen(n->name) == comp_len) + break; + if (n == NULL) + return ISO_DEAD_SYMLINK; + + if (n->type == LIBISO_DIR) { + cur_dir = (IsoDir *) n; + } else if (n->type == LIBISO_SYMLINK) { + if (*depth >= LIBISO_MAX_LINK_DEPTH) + return ISO_DEEP_SYMLINK; + (*depth)++; + ret = iso_tree_resolve_symlink(img, (IsoSymlink *) n, + &resolved_node, depth, 0); + if (ret < 0) + return ret; + if (resolved_node->type != LIBISO_DIR) { + n = resolved_node; + goto leaf_type; + } + cur_dir = (IsoDir *) resolved_node; + } else { +leaf_type:; + if (dest_end < dest + dest_len) /* attempt to dive into file */ + return ISO_DEAD_SYMLINK; + *res = n; + return ISO_SUCCESS; + } + } + *res = cur_dir; + return ISO_SUCCESS; +} +