summaryrefslogtreecommitdiffstats
path: root/src/H5Gtraverse.c
diff options
context:
space:
mode:
authorJames Laird <jlaird@hdfgroup.org>2006-08-02 23:41:53 (GMT)
committerJames Laird <jlaird@hdfgroup.org>2006-08-02 23:41:53 (GMT)
commit3e755623cb24eb37c19fa645d74dc46948318253 (patch)
tree66e0a3807f37d50a8d6e5f3469864c604cd837c6 /src/H5Gtraverse.c
parent71a4d0e9c48c4e02e5384cd3f6e38a2a530e9d22 (diff)
downloadhdf5-3e755623cb24eb37c19fa645d74dc46948318253.zip
hdf5-3e755623cb24eb37c19fa645d74dc46948318253.tar.gz
hdf5-3e755623cb24eb37c19fa645d74dc46948318253.tar.bz2
[svn-r12528] Added User-Defined links to the library.
Users can create external links using H5L_create_external(). These links point to an object in another HDF5 file. Users can alter the behavior of external links or create new kinds of links by registering callbacks using the H5L interface. Added tests, tools support, etc. Also a number of other, minor changes have been made (some restructuring of the H5L interface, for instance). Additional documentation and examples are forthcoming.
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: