summaryrefslogtreecommitdiffstats
path: root/src/H5Gtraverse.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/H5Gtraverse.c')
-rw-r--r--src/H5Gtraverse.c589
1 files changed, 589 insertions, 0 deletions
diff --git a/src/H5Gtraverse.c b/src/H5Gtraverse.c
new file mode 100644
index 0000000..c43ffbb
--- /dev/null
+++ b/src/H5Gtraverse.c
@@ -0,0 +1,589 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Copyright by the Board of Trustees of the University of Illinois. *
+ * All rights reserved. *
+ * *
+ * This file is part of HDF5. The full HDF5 copyright notice, including *
+ * terms governing use, modification, and redistribution, is contained in *
+ * the files COPYING and Copyright.html. COPYING can be found at the root *
+ * of the source code distribution tree; Copyright.html can be found at the *
+ * root level of an installed copy of the electronic HDF5 document set and *
+ * is linked from the top-level documents page. It can also be found at *
+ * http://hdf.ncsa.uiuc.edu/HDF5/doc/Copyright.html. If you do not have *
+ * access to either file, you may request a copy from hdfhelp@ncsa.uiuc.edu. *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*-------------------------------------------------------------------------
+ *
+ * Created: H5Gtraverse.c
+ * Sep 13 2005
+ * Quincey Koziol <koziol@ncsa.uiuc.edu>
+ *
+ * Purpose: Functions for traversing group hierarchy
+ *
+ *-------------------------------------------------------------------------
+ */
+#define H5F_PACKAGE /*suppress error about including H5Fpkg */
+#define H5G_PACKAGE /*suppress error about including H5Gpkg */
+
+
+/* Packages needed by this file... */
+#include "H5private.h" /* Generic Functions */
+#include "H5Eprivate.h" /* Error handling */
+#include "H5Fpkg.h" /* File access */
+#include "H5Gpkg.h" /* Groups */
+#include "H5HLprivate.h" /* Local Heaps */
+#include "H5MMprivate.h" /* Memory management */
+
+/* Private typedefs */
+
+/* User data for path traversal routine */
+typedef struct {
+ H5G_loc_t *obj_loc; /* Object location */
+} H5G_trav_ud1_t;
+
+/* Private macros */
+
+/* Local variables */
+static char *H5G_comp_g = NULL; /*component buffer */
+static size_t H5G_comp_alloc_g = 0; /*sizeof component buffer */
+
+/* PRIVATE PROTOTYPES */
+static herr_t H5G_traverse_slink_cb(H5G_loc_t *grp_loc/*in*/, const char *name,
+ const H5O_link_t *lnk, H5G_loc_t *obj_loc, void *_udata/*in,out*/);
+static herr_t H5G_traverse_slink(H5G_loc_t *grp_loc/*in,out*/, H5O_link_t *lnk,
+ H5G_loc_t *obj_loc/*in,out*/, int *nlinks/*in,out*/, hid_t dxpl_id);
+static herr_t H5G_traverse_mount(H5G_loc_t *loc/*in,out*/);
+static herr_t H5G_traverse_real(const H5G_loc_t *loc, const char *name,
+ unsigned target, int *nlinks, H5G_traverse_t op, void *op_data,
+ hid_t dxpl_id);
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5G_traverse_term_interface
+ *
+ * Purpose: Terminates part of the H5G interface - free the global
+ * component buffer.
+ *
+ * Return: Success: Non-negative.
+ *
+ * Failure: Negative.
+ *
+ * Programmer: Quincey Koziol
+ * Monday, September 26, 2005
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5G_traverse_term_interface(void)
+{
+ FUNC_ENTER_NOAPI_NOINIT_NOFUNC(H5G_traverse_term_interface)
+
+ /* Free the global component buffer */
+ H5G_comp_g = H5MM_xfree(H5G_comp_g);
+ H5G_comp_alloc_g = 0;
+
+ FUNC_LEAVE_NOAPI(SUCCEED)
+} /* end H5G_traverse_term_interface() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5G_traverse_slink_cb
+ *
+ * Purpose: Callback for symbolic link traversal. This routine sets the
+ * correct information for the object location.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * Tuesday, September 13, 2005
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5G_traverse_slink_cb(H5G_loc_t UNUSED *grp_loc, const char UNUSED *name, const H5O_link_t UNUSED *lnk,
+ H5G_loc_t *obj_loc, void *_udata/*in,out*/)
+{
+ H5G_trav_ud1_t *udata = (H5G_trav_ud1_t *)_udata; /* User data passed in */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI_NOINIT(H5G_traverse_slink_cb)
+
+ /* Check for dangling soft link */
+ if(obj_loc == NULL)
+ HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "component not found")
+
+ /* Copy new location information for resolved object */
+ H5O_loc_copy(udata->obj_loc->oloc, obj_loc->oloc, H5O_COPY_DEEP);
+
+done:
+ /* Release the group location for the object */
+ /* (Group traversal callbacks are responsible for either taking ownership
+ * of the group location for the object, or freeing it. - QAK)
+ */
+ if(obj_loc)
+ H5G_loc_free(obj_loc);
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5G_traverse_slink_cb() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5G_traverse_slink
+ *
+ * Purpose: Traverses symbolic link. The link head appears in the group
+ * whose entry is GRP_LOC and the link tail entry is OBJ_LOC.
+ *
+ * Return: Success: Non-negative, OBJ_LOC will contain information
+ * about the object to which the link points and
+ * GRP_LOC will contain the information about
+ * the group in which the link tail appears.
+ *
+ * Failure: Negative
+ *
+ * Programmer: Robb Matzke
+ * Friday, April 10, 1998
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5G_traverse_slink(H5G_loc_t *grp_loc/*in,out*/, H5O_link_t *lnk,
+ H5G_loc_t *obj_loc/*in,out*/, int *nlinks/*in,out*/, hid_t dxpl_id)
+{
+ H5G_trav_ud1_t udata; /* User data to pass to link traversal callback */
+ H5G_name_t tmp_obj_path; /* Temporary copy of object's path */
+ hbool_t tmp_obj_path_set = FALSE; /* Flag to indicate that tmp object path is initialized */
+ H5O_loc_t tmp_grp_oloc; /* Temporary copy of group entry */
+ H5G_name_t tmp_grp_path; /* Temporary copy of group's path */
+ H5G_loc_t tmp_grp_loc; /* Temporary copy of group's location */
+ hbool_t tmp_grp_path_set = FALSE; /* Flag to indicate that tmp group path is initialized */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI_NOINIT(H5G_traverse_slink)
+
+ /* Sanity check */
+ HDassert(grp_loc);
+ HDassert(lnk);
+ HDassert(lnk->type == H5G_LINK_SOFT);
+ HDassert(nlinks);
+
+ /* Set up temporary location */
+ tmp_grp_loc.oloc = &tmp_grp_oloc;
+ tmp_grp_loc.path = &tmp_grp_path;
+
+ /* Portably initialize the temporary objects */
+ H5G_loc_reset(&tmp_grp_loc);
+
+ /* Clone the group location, so we can track the names properly */
+ /* ("tracking the names properly" means to ignore the effects of the
+ * soft link traversal on the object's & group's paths - QAK)
+ */
+ H5G_loc_copy(&tmp_grp_loc, grp_loc, H5G_COPY_DEEP);
+ tmp_grp_path_set = TRUE;
+
+ /* Hold the object's group hier. path to restore later */
+ /* (Part of "tracking the names properly") */
+ H5G_name_reset(&tmp_obj_path);
+ H5G_name_copy(&tmp_obj_path, obj_loc->path, H5G_COPY_SHALLOW);
+ tmp_obj_path_set = TRUE;
+
+ /* Set up user data for traversal callback */
+ udata.obj_loc = obj_loc;
+
+ /* Traverse to the link's last component */
+ if(H5G_traverse_real(&tmp_grp_loc, lnk->u.soft.name, H5G_TARGET_NORMAL, nlinks, H5G_traverse_slink_cb, &udata, dxpl_id) < 0)
+ HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "unable to follow symbolic link")
+
+done:
+ /* Restore object's group hier. path */
+ if(tmp_obj_path_set) {
+ H5G_name_free(obj_loc->path);
+ H5G_name_copy(obj_loc->path, &tmp_obj_path, H5G_COPY_SHALLOW);
+ } /* end if */
+
+ /* Release cloned copy of group path */
+ if(tmp_grp_path_set)
+ H5G_name_free(&tmp_grp_path);
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5G_traverse_slink() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5G_traverse_mount
+ *
+ * Purpose: If LNK is a mount point then copy the entry for the root
+ * group of the mounted file into LNK.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Robb Matzke
+ * Tuesday, October 6, 1998
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5G_traverse_mount(H5G_loc_t *obj_loc/*in,out*/)
+{
+ H5F_t *parent = obj_loc->oloc->file; /* File of object */
+ unsigned lt, rt, md = 0; /* Binary search indices */
+ int cmp;
+ H5O_loc_t *oloc = NULL; /* Object location for mount points */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5G_traverse_mount, FAIL)
+
+ /* Sanity check */
+ HDassert(obj_loc);
+
+ /*
+ * The loop is necessary because we might have file1 mounted at the root
+ * of file2, which is mounted somewhere in file3.
+ */
+ do {
+ /*
+ * Use a binary search to find the potential mount point in the mount
+ * table for the parent
+ */
+ lt = 0;
+ rt = parent->mtab.nmounts;
+ cmp = -1;
+ while(lt < rt && cmp) {
+ md = (lt + rt) / 2;
+ oloc = H5G_oloc(parent->mtab.child[md].group);
+ cmp = H5F_addr_cmp(obj_loc->oloc->addr, oloc->addr);
+ if(cmp < 0)
+ rt = md;
+ else
+ lt = md + 1;
+ } /* end while */
+
+ /* Copy root info over to ENT */
+ if(0 == cmp) {
+ H5G_name_t *root_path; /* Path of root group */
+
+ /* Get the location for the root group in the child's file */
+ oloc = H5G_oloc(parent->mtab.child[md].file->shared->root_grp);
+ root_path = H5G_nameof(parent->mtab.child[md].file->shared->root_grp);
+
+ /* Copy the entry for the root group */
+ if(H5O_loc_copy(obj_loc->oloc, oloc, H5O_COPY_DEEP) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTCOPY, FAIL, "unable to copy object location")
+
+ /* Don't lose the user path of the group when we copy the root group's path */
+ if(H5G_name_copy(obj_loc->path, root_path, H5G_COPY_CANON) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTCOPY, FAIL, "unable to copy object path")
+
+ /* Switch to child's file */
+ parent = oloc->file;
+ } /* end if */
+ } while(!cmp);
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5G_traverse_mount() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5G_traverse_real
+ *
+ * Purpose: Internal version of path traversal routine
+ *
+ * Return: Success: Non-negative if name can be fully resolved.
+ *
+ * Failure: Negative if the name could not be fully
+ * resolved.
+ *
+ * Programmer: Robb Matzke
+ * matzke@llnl.gov
+ * Aug 11 1997
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t
+H5G_traverse_real(const H5G_loc_t *_loc, const char *name, unsigned target,
+ int *nlinks, H5G_traverse_t op, void *op_data, hid_t dxpl_id)
+{
+ H5G_loc_t loc; /* Location of start object */
+ H5O_loc_t grp_oloc; /* Object loc. for current group */
+ H5G_name_t grp_path; /* Path for current group */
+ H5G_loc_t grp_loc; /* Location of group */
+ H5O_loc_t obj_oloc; /* Object found */
+ H5G_name_t obj_path; /* Path for object found */
+ H5G_loc_t obj_loc; /* Location of object */
+ size_t nchars; /* component name length */
+ H5O_link_t lnk; /* Link information for object */
+ hbool_t link_valid = FALSE; /* Flag to indicate that the link information is valid */
+ hbool_t obj_loc_valid = FALSE; /* Flag to indicate that the object location is valid */
+ hbool_t group_copy = FALSE; /* Flag to indicate that the group entry is copied */
+ hbool_t last_comp = FALSE; /* Flag to indicate that a component is the last component in the name */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI_NOINIT(H5G_traverse_real)
+
+ /* Check parameters */
+ HDassert(_loc);
+ HDassert(name);
+ HDassert(nlinks);
+ HDassert(op);
+
+ /*
+ * Where does the searching start? For absolute names it starts at the
+ * root of the file; for relative names it starts at CWG.
+ */
+ /* Check if we need to get the root group's entry */
+ if('/' == *name) {
+ H5G_t *root_grp; /* Temporary pointer to root group of file */
+
+ /* Look up root group for starting location */
+ root_grp = H5G_rootof(_loc->oloc->file);
+ HDassert(root_grp);
+
+ /* Set the location entry to the root group's info */
+ loc.oloc=&(root_grp->oloc);
+ loc.path=&(root_grp->path);
+ } /* end if */
+ else {
+ loc.oloc = _loc->oloc;
+ loc.path = _loc->path;
+ } /* end else */
+
+ /* Set up group & object locations */
+ grp_loc.oloc = &grp_oloc;
+ grp_loc.path = &grp_path;
+ obj_loc.oloc = &obj_oloc;
+ obj_loc.path = &obj_path;
+
+ /* Deep copy of the starting location to group location */
+ if(H5G_loc_copy(&grp_loc, &loc, H5G_COPY_DEEP) < 0)
+ HGOTO_ERROR(H5E_SYM, H5E_CANTOPENOBJ, FAIL, "unable to copy location")
+ group_copy = TRUE;
+
+ /* Clear object location */
+ if(H5G_loc_reset(&obj_loc) < 0)
+ HGOTO_ERROR(H5E_SYM, H5E_CANTOPENOBJ, FAIL, "unable to reset location")
+
+ /* Check for needing a larger buffer for the individual path name components */
+ if(HDstrlen(name) + 1 > H5G_comp_alloc_g) {
+ H5G_comp_alloc_g = MAX3(1024, 2 * H5G_comp_alloc_g, HDstrlen(name) + 1);
+ H5G_comp_g = H5MM_realloc(H5G_comp_g, H5G_comp_alloc_g);
+ if(!H5G_comp_g) {
+ H5G_comp_alloc_g = 0;
+ HGOTO_ERROR(H5E_SYM, H5E_NOSPACE, FAIL, "unable to allocate component buffer")
+ } /* end if */
+ } /* end if */
+
+ /* Traverse the path */
+ while((name = H5G_component(name, &nchars)) && *name) {
+ const char *s; /* Temporary string pointer */
+ herr_t lookup_status; /* Status from object lookup */
+
+ /*
+ * Copy the component name into a null-terminated buffer so
+ * we can pass it down to the other symbol table functions.
+ */
+ HDmemcpy(H5G_comp_g, name, nchars);
+ H5G_comp_g[nchars] = '\0';
+
+ /*
+ * The special name `.' is a no-op.
+ */
+ if('.' == H5G_comp_g[0] && !H5G_comp_g[1]) {
+ name += nchars;
+ continue;
+ } /* end if */
+
+ /* Check if this is the last component of the name */
+ if(!((s = H5G_component(name + nchars, NULL)) && *s))
+ last_comp = TRUE;
+
+ /* If there's valid information in the link, reset it */
+ if(link_valid) {
+ H5O_reset(H5O_LINK_ID, &lnk);
+ link_valid = FALSE;
+ } /* end if */
+
+ /* Get information for object in current group */
+ /* (Defer issuing error for back lookup until later) */
+ lookup_status = H5G_obj_lookup(grp_loc.oloc, H5G_comp_g, &lnk/*out*/, dxpl_id);
+
+ /* If the lookup was OK, try traversing soft links and mount points, if allowed */
+ if(lookup_status >= 0) {
+ /* Indicate that the link info is valid */
+ link_valid = TRUE;
+
+ /* Build object's group hier. location */
+ if(H5G_name_set(grp_loc.path, obj_loc.path, H5G_comp_g) < 0)
+ HGOTO_ERROR(H5E_SYM, H5E_CANTINIT, FAIL, "cannot set name")
+
+ /* Set the object location, if it's a hard link set the address also */
+ obj_loc.oloc->file = grp_loc.oloc->file;
+ if(lnk.type == H5G_LINK_HARD) {
+ obj_loc.oloc->addr = lnk.u.hard.addr;
+ } /* end if */
+ obj_loc_valid = TRUE;
+
+ /*
+ * If we found a symbolic link then we should follow it. But if this
+ * is the last component of the name and the H5G_TARGET_SLINK bit of
+ * TARGET is set then we don't follow it.
+ */
+ if(H5G_LINK_SOFT == lnk.type &&
+ (0 == (target & H5G_TARGET_SLINK) || !last_comp)) {
+ if((*nlinks)-- <= 0)
+ HGOTO_ERROR(H5E_SYM, H5E_SLINK, FAIL, "too many links")
+ if(H5G_traverse_slink(&grp_loc/*in,out*/, &lnk/*in*/, &obj_loc, nlinks, dxpl_id) < 0)
+ HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "symbolic link traversal failed")
+ } /* end if */
+
+ /*
+ * Resolve mount points to the mounted group. Do not do this step if
+ * the H5G_TARGET_MOUNT bit of TARGET is set and this is the last
+ * component of the name.
+ *
+ * (If this link is a hard link, try to perform mount point traversal)
+ *
+ * (Note that the soft link traversal above can change the status of
+ * the object (into a hard link), so don't use an 'else' statement
+ * here. -QAK)
+ */
+ if(H5F_addr_defined(obj_loc.oloc->addr) &&
+ (0 == (target & H5G_TARGET_MOUNT) || !last_comp)) {
+ if(H5G_traverse_mount(&obj_loc/*in,out*/) < 0)
+ HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "mount point traversal failed")
+ } /* end if */
+ } /* end if */
+
+ /* Check for last component in name provided */
+ if(last_comp) {
+ H5O_link_t *tmp_lnk; /* Pointer to link info for callback */
+ H5G_loc_t *tmp_loc; /* Pointer to object location for callback */
+
+ /* Set callback parameters appropriately, based on link being found */
+ if(lookup_status < 0) {
+ tmp_lnk = NULL;
+ tmp_loc = NULL;
+ } /* end if */
+ else {
+ tmp_lnk = &lnk;
+ tmp_loc = &obj_loc;
+ } /* end else */
+
+ /* Operator routine will take care of object location, succeed or fail */
+ obj_loc_valid = FALSE;
+
+ /* Call 'operator' routine */
+ if((op)(&grp_loc, H5G_comp_g, tmp_lnk, tmp_loc, op_data) < 0)
+ HGOTO_ERROR(H5E_SYM, H5E_CALLBACK, FAIL, "traversal operator failed")
+ HGOTO_DONE(SUCCEED)
+ } /* end if */
+
+ /* Handle lookup failures now */
+ if(lookup_status < 0) {
+ /* If an intermediate group doesn't exist & flag is set, create the group */
+ if(target & H5G_CRT_INTMD_GROUP) {
+ H5O_ginfo_t ginfo; /* Group info message for parent group */
+
+ /* Get the group info for parent group */
+ if(NULL == H5O_read(grp_loc.oloc, H5O_GINFO_ID, 0, &ginfo, dxpl_id))
+ HGOTO_ERROR(H5E_SYM, H5E_BADMESG, FAIL, "can't get group info")
+
+ /* Create the intermediate group */
+/* XXX: Should we allow user to control the group creation params here? -QAK */
+ if(H5G_obj_create(grp_oloc.file, dxpl_id, &ginfo, obj_loc.oloc/*out*/) < 0)
+ HGOTO_ERROR(H5E_SYM, H5E_CANTINIT, FAIL, "unable to create group entry")
+
+ /* Insert new group into current group's symbol table */
+ if(H5G_loc_insert(&grp_loc, H5G_comp_g, &obj_loc, TRUE, dxpl_id) < 0)
+ HGOTO_ERROR(H5E_SYM, H5E_CANTINSERT, FAIL, "unable to insert intermediate group")
+
+ /* Close new group */
+ if(H5O_close(obj_loc.oloc) < 0)
+ HGOTO_ERROR(H5E_SYM, H5E_CANTINIT, FAIL, "unable to close")
+ } /* end if */
+ else
+ HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "component not found")
+ } /* end if */
+
+ /*
+ * Advance to the next component of the path.
+ */
+
+ /* Transfer "ownership" of the object's information to the group object */
+ H5G_loc_free(&grp_loc);
+ H5G_loc_copy(&grp_loc, &obj_loc, H5G_COPY_SHALLOW);
+ H5G_loc_reset(&obj_loc);
+ obj_loc_valid = FALSE;
+
+ /* Advance to next component in string */
+ name += nchars;
+ } /* end while */
+
+ /* If we've fallen through to here, the name must be something like just '.'
+ * and we should issue the callback on that. -QAK
+ */
+ /* Reset "group copied" flag */
+ /* (callback will take ownership of group location, succeed or fail) */
+ HDassert(group_copy);
+ group_copy = FALSE;
+
+ /* Call 'operator' routine */
+ if((op)(&grp_loc, ".", NULL, &grp_loc, op_data) < 0)
+ HGOTO_ERROR(H5E_SYM, H5E_CANTNEXT, FAIL, "traversal operator failed")
+ HGOTO_DONE(SUCCEED)
+
+done:
+ /* If the object location is still valid (usually in an error situation), reset it */
+ if(obj_loc_valid)
+ H5G_loc_free(&obj_loc);
+ /* If there's valid information in the link, reset it */
+ if(link_valid)
+ H5O_reset(H5O_LINK_ID, &lnk);
+ /* If we copied something into the group location, free it */
+ if(group_copy)
+ H5G_loc_free(&grp_loc);
+
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5G_traverse_real() */
+
+
+/*-------------------------------------------------------------------------
+ * Function: H5G_traverse
+ *
+ * Purpose: Traverse a path from a location & perform an operation when
+ * the last component of the name is reached.
+ *
+ * Return: Success: Non-negative if path can be fully traversed.
+ * Failure: Negative if the path could not be fully
+ * traversed.
+ *
+ * Programmer: Quincey Koziol
+ * koziol@ncsa.uiuc.edu
+ * Sep 13 2005
+ *
+ *-------------------------------------------------------------------------
+ */
+herr_t
+H5G_traverse(const H5G_loc_t *loc, const char *name, unsigned target, H5G_traverse_t op,
+ void *op_data, hid_t dxpl_id)
+{
+ int nlinks = H5G_NLINKS; /* Link countdown value */
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI(H5G_traverse, FAIL)
+
+ /* Check args */
+ if(!name || !*name)
+ HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "no name given")
+ if(!loc)
+ HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "no starting location")
+ if(!op)
+ HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "no operation provided")
+
+ /* Go perform "real" traversal */
+ if(H5G_traverse_real(loc, name, target, &nlinks, op, op_data, dxpl_id) < 0)
+ HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "path traversal failed")
+
+done:
+ FUNC_LEAVE_NOAPI(ret_value)
+} /* end H5G_traverse() */
+