summaryrefslogtreecommitdiffstats
path: root/src/H5Gtraverse.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/H5Gtraverse.c')
-rw-r--r--src/H5Gtraverse.c272
1 files changed, 236 insertions, 36 deletions
diff --git a/src/H5Gtraverse.c b/src/H5Gtraverse.c
index c94cd86..03db4d3 100644
--- a/src/H5Gtraverse.c
+++ b/src/H5Gtraverse.c
@@ -28,10 +28,12 @@
/* Packages needed by this file... */
#include "H5private.h" /* Generic Functions */
+#include "H5Dprivate.h" /* Datasets */
#include "H5Eprivate.h" /* Error handling */
#include "H5Fpkg.h" /* File access */
#include "H5Gpkg.h" /* Groups */
#include "H5HLprivate.h" /* Local Heaps */
+#include "H5Iprivate.h" /* IDs */
#include "H5Lprivate.h" /* Links */
#include "H5MMprivate.h" /* Memory management */
#include "H5Ppublic.h" /* Property Lists */
@@ -51,13 +53,21 @@ static size_t H5G_comp_alloc_g = 0; /*sizeof component buffer */
/* PRIVATE PROTOTYPES */
static herr_t H5G_traverse_link_cb(H5G_loc_t *grp_loc/*in*/, const char *name,
- const H5O_link_t *lnk, H5G_loc_t *obj_loc, void *_udata/*in,out*/);
+ const H5O_link_t *lnk, H5G_loc_t *obj_loc, void *_udata/*in,out*/,
+ hbool_t *own_obj_loc/*out*/);
+static herr_t H5G_traverse_ud(H5G_loc_t *grp_loc/*in,out*/, H5O_link_t *lnk,
+ H5G_loc_t *obj_loc/*in,out*/, int *nlinks/*in,out*/, hid_t lapl_id,
+ hid_t dxpl_id);
+static herr_t H5G_traverse_elink(H5G_loc_t *grp_loc/*in,out*/, H5O_link_t *lnk,
+ H5G_loc_t *obj_loc/*in,out*/, int *nlinks/*in,out*/, hid_t lapl_id,
+ hid_t dxpl_id);
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_loc_t *obj_loc/*in,out*/, int *nlinks/*in,out*/, hid_t lapl_id,
+ 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);
+ hid_t lapl_id, hid_t dxpl_id);
/*-------------------------------------------------------------------------
@@ -103,7 +113,7 @@ H5G_traverse_term_interface(void)
*/
static herr_t
H5G_traverse_link_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_loc_t *obj_loc, void *_udata/*in,out*/, hbool_t *own_obj_loc/*out*/)
{
H5G_trav_ud1_t *udata = (H5G_trav_ud1_t *)_udata; /* User data passed in */
herr_t ret_value = SUCCEED; /* Return value */
@@ -118,18 +128,149 @@ H5G_traverse_link_cb(H5G_loc_t UNUSED *grp_loc, const char UNUSED *name, const H
H5O_loc_copy(udata->obj_loc->oloc, obj_loc->oloc, H5_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);
+ /* Indicate that this callback didn't take ownership of the group *
+ * location for the object */
+ *own_obj_loc = FALSE;
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5G_traverse_link_cb() */
/*-------------------------------------------------------------------------
+ * Function: H5G_traverse_link_ud
+ *
+ * Purpose: Callback for user-defined link traversal. Sets up a
+ * location ID and passes it to the user traversal callback.
+ *
+ * Return: Non-negative on success/Negative on failure
+ *
+ * Programmer: Quincey Koziol
+ * Tuesday, September 13, 2005
+ *
+ *-------------------------------------------------------------------------
+ */
+static herr_t H5G_traverse_ud(H5G_loc_t *grp_loc/*in,out*/, H5O_link_t *lnk,
+ H5G_loc_t *obj_loc/*in,out*/, int *nlinks/*in,out*/, hid_t lapl_id,
+ hid_t dxpl_id)
+{
+ const H5L_link_class_t *link_class; /* User-defined link class */
+ hid_t cb_return = -1; /* The ID the user-defined callback returned */
+ H5O_loc_t *new_oloc=NULL;
+ H5F_t *temp_file=NULL;
+ H5F_t *new_file=NULL;
+ H5G_t *grp;
+ H5P_genplist_t *lapl_default;
+ H5P_genplist_t *lapl; /* LAPL with nlinks set */
+ hid_t cur_grp;
+ herr_t ret_value = SUCCEED; /* Return value */
+
+ FUNC_ENTER_NOAPI_NOINIT(H5G_traverse_ud)
+
+ /* Sanity check */
+ HDassert(grp_loc);
+ HDassert(lnk);
+ HDassert(lnk->type >= H5L_LINK_UD_MIN);
+ HDassert(obj_loc);
+ HDassert(nlinks);
+
+ /* Reset the object's path information, because we can't detect any changes
+ * in the "path" the user-defined callback takes */
+ H5G_name_free(obj_loc->path);
+
+ /* Get the link class for this type of link. */
+ if(NULL == (link_class = H5L_find_class(lnk->type)))
+ HGOTO_ERROR(H5E_LINK, H5E_NOTREGISTERED, FAIL, "unable to get UD link class")
+
+ /* Set up location for user-defined callback */
+ if((grp = H5G_open(grp_loc, dxpl_id)) == NULL)
+ HGOTO_ERROR(H5E_SYM, H5E_CANTOPENOBJ, FAIL, "unable to open group")
+ if((cur_grp = H5I_register(H5I_GROUP, grp)) < 0)
+ HGOTO_ERROR(H5E_ATOM, H5E_CANTREGISTER, FAIL, "unable to register group")
+
+ /* Record number of soft links left to traverse in the property list.
+ * If no property list exists yet, create one. */
+ if(lapl_id == H5P_DEFAULT)
+ {
+ HDassert(H5P_LINK_ACCESS_DEFAULT != -1);
+ if(NULL == (lapl_default = H5I_object(H5P_LINK_ACCESS_DEFAULT)))
+ HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, FAIL, "unable to get default property list")
+
+ if((lapl_id = H5P_copy_plist(lapl_default)) <0)
+ HGOTO_ERROR(H5E_PLIST, H5E_BADVALUE, FAIL, "unable to copy property list")
+ }
+
+ if(NULL == (lapl = H5I_object(lapl_id)))
+ HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, FAIL, "unable to get property list from ID")
+ if(H5P_set(lapl, H5L_NLINKS_NAME, nlinks) < 0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't set nlink info")
+
+ /* User-defined callback function */
+ if((cb_return = (link_class->trav_func)(lnk->name, cur_grp, lnk->u.ud.udata, lnk->u.ud.size, lapl_id)) < 0)
+ HGOTO_ERROR(H5E_ARGS, H5E_BADATOM, FAIL, "traversal callback returned invalid ID")
+
+ /* Get the oloc from the ID the user callback returned */
+ switch(H5I_get_type(cb_return))
+ {
+ case H5I_GROUP:
+ if((new_oloc = H5G_oloc(H5I_object(cb_return))) == NULL)
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to get object location from group ID")
+ break;
+ case H5I_DATASET:
+ if((new_oloc = H5D_oloc(H5I_object(cb_return))) ==NULL)
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to get object location from dataset ID")
+ break;
+ case H5I_DATATYPE:
+ if((new_oloc = H5T_oloc(H5I_object(cb_return))) ==NULL)
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to get object location from datatype ID")
+ break;
+ case H5I_FILE:
+ if((temp_file = H5I_object(cb_return)) == NULL)
+ HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, FAIL, "couldn't get file from ID")
+ if((new_oloc = H5G_oloc(temp_file->shared->root_grp)) ==NULL)
+ HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to get root group location from file ID")
+ break;
+ default:
+ HGOTO_ERROR(H5E_ATOM, H5E_BADTYPE, FAIL, "not a valid location or object ID")
+ }
+
+ if(H5O_loc_copy(obj_loc->oloc, new_oloc, H5_COPY_DEEP) < 0)
+ HGOTO_ERROR(H5E_FILE, H5E_CANTCOPY, FAIL, "unable to copy object location")
+
+ /* The user has given us an open object, but we only want its location. However,
+ * if the object is in another file and we close it, the file will close as well and
+ * clear its information from the location we've just copied.
+ * Thus, we play with the number of open objects in the file to close the object without
+ * closing the file. -JML 5/06*/
+ obj_loc->oloc->file->nopen_objs++;
+ H5Idec_ref(cb_return);
+ HDassert(obj_loc->oloc->file->nopen_objs > 0);
+ obj_loc->oloc->file->nopen_objs--;
+
+done:
+ /* Close location given to callback.
+ * This has the side effect of calling H5F_try_close on grp_loc's file.
+ * If we have a series of external links (file1 to file2 to file3 to
+ * file4), this closes file2 and file3 when we're done traversing
+ * through them (unless they have other IDs holding them open).
+ */
+ if(cur_grp > 0)
+ {
+ if(H5I_object_verify(cur_grp, H5I_GROUP))
+ if(H5I_dec_ref(cur_grp) < 0)
+ HDONE_ERROR(H5E_ATOM, H5E_CANTRELEASE, FAIL, "unable to close atom for current location")
+ }
+
+ if(ret_value < 0 && cb_return >= 0)
+ {
+ if(H5I_dec_ref(cb_return) < 0)
+ HDONE_ERROR(H5E_ATOM, H5E_CANTRELEASE, FAIL, "unable to close atom from UD callback")
+ }
+
+ FUNC_LEAVE_NOAPI(ret_value)
+
+}
+
+/*-------------------------------------------------------------------------
* Function: H5G_traverse_slink
*
* Purpose: Traverses symbolic link. The link head appears in the group
@@ -149,7 +290,8 @@ done:
*/
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_loc_t *obj_loc/*in,out*/, int *nlinks/*in,out*/, hid_t lapl_id,
+ 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 */
@@ -192,7 +334,7 @@ H5G_traverse_slink(H5G_loc_t *grp_loc/*in,out*/, H5O_link_t *lnk,
udata.obj_loc = obj_loc;
/* Traverse the link */
- if(H5G_traverse_real(&tmp_grp_loc, lnk->u.soft.name, H5G_TARGET_NORMAL, nlinks, H5G_traverse_link_cb, &udata, dxpl_id) < 0)
+ if(H5G_traverse_real(&tmp_grp_loc, lnk->u.soft.name, H5G_TARGET_NORMAL, nlinks, H5G_traverse_link_cb, &udata, lapl_id, dxpl_id) < 0)
HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "unable to follow symbolic link")
done:
@@ -296,7 +438,7 @@ done:
*/
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)
+ int *nlinks, H5G_traverse_t op, void *op_data, hid_t lapl_id, hid_t dxpl_id)
{
H5G_loc_t loc; /* Location of start object */
H5O_loc_t grp_oloc; /* Object loc. for current group */
@@ -305,10 +447,13 @@ H5G_traverse_real(const H5G_loc_t *_loc, const char *name, unsigned target,
H5O_loc_t obj_oloc; /* Object found */
H5G_name_t obj_path; /* Path for object found */
H5G_loc_t obj_loc; /* Location of object */
+ H5O_link_t *cb_lnk=NULL; /* Pointer to link info for callback */
+ H5G_loc_t *cb_loc=NULL; /* Pointer to object location for callback */
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 own_cb_loc=FALSE; /* Flag to indicate that callback took ownership of cb_loc */
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 */
@@ -403,7 +548,7 @@ H5G_traverse_real(const H5G_loc_t *_loc, const char *name, unsigned target,
H5O_reset(H5O_LINK_ID, &lnk);
#else /* H5_GROUP_REVISION */
/* Free information for link (but don't free link pointer) */
- if(lnk.type == H5G_LINK_SOFT)
+ if(lnk.type == H5L_LINK_SOFT)
lnk.u.soft.name = H5MM_xfree(lnk.u.soft.name);
lnk.name = H5MM_xfree(lnk.name);
#endif /* H5_GROUP_REVISION */
@@ -417,6 +562,9 @@ H5G_traverse_real(const H5G_loc_t *_loc, const char *name, unsigned target,
/* 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 */
+ HDassert(lnk.type >= H5L_LINK_HARD);
+ if(lnk.type >H5L_LINK_BUILTIN_MAX && lnk.type < H5L_LINK_UD_MIN)
+ HGOTO_ERROR(H5E_SYM, H5E_UNSUPPORTED, FAIL, "unknown link type")
link_valid = TRUE;
/* Build object's group hier. location */
@@ -437,9 +585,21 @@ H5G_traverse_real(const H5G_loc_t *_loc, const char *name, unsigned target,
if(H5L_LINK_SOFT == lnk.type &&
(0 == (target & H5G_TARGET_SLINK) || !last_comp)) {
if((*nlinks)-- <= 0)
- HGOTO_ERROR(H5E_SYM, H5E_LINK, 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_SLINK, FAIL, "symbolic link traversal failed")
+ HGOTO_ERROR(H5E_LINK, H5E_NLINKS, FAIL, "too many links")
+ if(H5G_traverse_slink(&grp_loc/*in,out*/, &lnk/*in*/, &obj_loc, nlinks, lapl_id, dxpl_id) < 0)
+ HGOTO_ERROR(H5E_LINK, H5E_TRAVERSE, FAIL, "symbolic link traversal failed")
+ } /* end if */
+
+ /*
+ * If we found an external link then we should follow it. But if this
+ * is the last component of the name and the H5G_TARGET_ELINK bit of
+ * TARGET is set then we don't follow it.
+ */
+ if( lnk.type >= H5L_LINK_UD_MIN && ((0 == (target & H5G_TARGET_UDLINK)) || !last_comp) ) {
+ if((*nlinks)-- <= 0)
+ HGOTO_ERROR(H5E_LINK, H5E_NLINKS, FAIL, "too many links")
+ if(H5G_traverse_ud(&grp_loc/*in,out*/, &lnk/*in*/, &obj_loc, nlinks, lapl_id, dxpl_id) < 0)
+ HGOTO_ERROR(H5E_LINK, H5E_TRAVERSE, FAIL, "user-defined link traversal failed")
} /* end if */
/*
@@ -449,9 +609,9 @@ H5G_traverse_real(const H5G_loc_t *_loc, const char *name, unsigned target,
*
* (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)
+ * (Note that the soft and external 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)) {
@@ -462,25 +622,23 @@ H5G_traverse_real(const H5G_loc_t *_loc, const char *name, unsigned target,
/* 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;
+ cb_lnk = NULL;
+ cb_loc = NULL;
} /* end if */
else {
- tmp_lnk = &lnk;
- tmp_loc = &obj_loc;
+ cb_lnk = &lnk;
+ cb_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)
+ if((op)(&grp_loc, H5G_comp_g, cb_lnk, cb_loc, op_data, &own_cb_loc) < 0)
HGOTO_ERROR(H5E_SYM, H5E_CALLBACK, FAIL, "traversal operator failed")
+
HGOTO_DONE(SUCCEED)
} /* end if */
@@ -533,27 +691,54 @@ H5G_traverse_real(const H5G_loc_t *_loc, const char *name, unsigned target,
/* 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) */
+ cb_loc = &grp_loc;
+
+ /* Reset "group copied" flag (cb_loc will be freed automatically unless the
+ * callback takes ownership of it) */
HDassert(group_copy);
group_copy = FALSE;
+
/* Call 'operator' routine */
- if((op)(&grp_loc, ".", NULL, &grp_loc, op_data) < 0)
+ if((op)(&grp_loc, ".", NULL, cb_loc, op_data, &own_cb_loc) < 0)
HGOTO_ERROR(H5E_SYM, H5E_CANTNEXT, FAIL, "traversal operator failed")
+
HGOTO_DONE(SUCCEED)
done:
+ /* If the operator routine didn't take ownership of the location we
+ * passed it (or if there was an error), free it. If it's in a new
+ * file, also try to close its file. */
+ if(!own_cb_loc && cb_loc)
+ {
+ if(cb_loc->oloc->file != grp_loc.oloc->file)
+ {
+ if(H5F_try_close(cb_loc->oloc->file) < 0)
+ HDONE_ERROR(H5E_FILE, H5E_CANTCLOSEFILE, FAIL, "unable to close external file opened during traversal")
+ }
+ H5G_loc_free(cb_loc);
+ }
+
/* If the object location is still valid (usually in an error situation), reset it */
if(obj_loc_valid)
+ {
+ if(ret_value < 0)
+ {
+ H5F_try_close(obj_loc.oloc->file);
+ }
H5G_loc_free(&obj_loc);
+ }
+ if(ret_value < 0 && grp_loc.oloc->file)
+ {
+ H5F_try_close(grp_loc.oloc->file);
+ }
/* If there's valid information in the link, reset it */
if(link_valid) {
#ifdef H5_GROUP_REVISION
H5O_reset(H5O_LINK_ID, &lnk);
#else /* H5_GROUP_REVISION */
/* Free information for link (but don't free link pointer) */
- if(lnk.type == H5G_LINK_SOFT)
+ if(lnk.type == H5L_LINK_SOFT)
lnk.u.soft.name = H5MM_xfree(lnk.u.soft.name);
lnk.name = H5MM_xfree(lnk.name);
#endif /* H5_GROUP_REVISION */
@@ -584,10 +769,11 @@ done:
*/
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)
+ void *op_data, hid_t lapl_id, hid_t dxpl_id)
{
- int nlinks = H5G_NLINKS; /* Link countdown value */
- herr_t ret_value = SUCCEED; /* Return value */
+ int nlinks; /* Link countdown value */
+ H5P_genplist_t *lapl; /* Property list with value for nlinks */
+ herr_t ret_value = SUCCEED; /* Return value */
FUNC_ENTER_NOAPI(H5G_traverse, FAIL)
@@ -598,9 +784,23 @@ H5G_traverse(const H5G_loc_t *loc, const char *name, unsigned target, H5G_traver
HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "no starting location")
if(!op)
HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "no operation provided")
+ HDassert(lapl_id >= 0);
+
+ /* Set nlinks value from property list, if it exists */
+ if(lapl_id == H5P_DEFAULT)
+ {
+ nlinks = H5L_NLINKS_DEF;
+ }
+ else
+ {
+ if(NULL == (lapl = H5I_object(lapl_id)))
+ HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, FAIL, "can't find object for ID")
+ if(H5P_get(lapl, H5L_NLINKS_NAME, &nlinks) < 0)
+ HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get number of links")
+ }
/* Go perform "real" traversal */
- if(H5G_traverse_real(loc, name, target, &nlinks, op, op_data, dxpl_id) < 0)
+ if(H5G_traverse_real(loc, name, target, &nlinks, op, op_data, lapl_id, dxpl_id) < 0)
HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "path traversal failed")
done: