diff options
Diffstat (limited to 'src/H5Gtraverse.c')
-rw-r--r-- | src/H5Gtraverse.c | 875 |
1 files changed, 875 insertions, 0 deletions
diff --git a/src/H5Gtraverse.c b/src/H5Gtraverse.c new file mode 100644 index 0000000..ff1206e --- /dev/null +++ b/src/H5Gtraverse.c @@ -0,0 +1,875 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * 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 COPYING file, which can be found at the root of the source code * + * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases. * + * If you do not have access to either file, you may request a copy from * + * help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/*------------------------------------------------------------------------- + * + * Created: H5Gtraverse.c + * Sep 13 2005 + * Quincey Koziol <koziol@ncsa.uiuc.edu> + * + * Purpose: Functions for traversing group hierarchy + * + *------------------------------------------------------------------------- + */ + +/****************/ +/* Module Setup */ +/****************/ + +#include "H5Gmodule.h" /* This source code file is part of the H5G module */ + + +/***********/ +/* Headers */ +/***********/ +#include "H5private.h" /* Generic Functions */ +#include "H5Dprivate.h" /* Datasets */ +#include "H5Eprivate.h" /* Error handling */ +#include "H5Fprivate.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 */ +#include "H5WBprivate.h" /* Wrapped Buffers */ + + +/****************/ +/* Local Macros */ +/****************/ + + +/******************/ +/* Local Typedefs */ +/******************/ + +/* User data for path traversal routine */ +typedef struct { + /* down */ + hbool_t chk_exists; /* Flag to indicate we are checking if object exists */ + + /* up */ + H5G_loc_t *obj_loc; /* Object location */ + hbool_t exists; /* Indicate if object exists */ +} H5G_trav_slink_t; + + +/********************/ +/* Package Typedefs */ +/********************/ + + +/********************/ +/* Local Prototypes */ +/********************/ +static herr_t H5G_traverse_slink_cb(H5G_loc_t *grp_loc, const char *name, + const H5O_link_t *lnk, H5G_loc_t *obj_loc, void *_udata/*in,out*/, + H5G_own_loc_t *own_loc/*out*/); +static herr_t H5G_traverse_ud(const H5G_loc_t *grp_loc, const H5O_link_t *lnk, + H5G_loc_t *obj_loc/*in,out*/, unsigned target, size_t *nlinks/*in,out*/, + hbool_t *obj_exists, hid_t lapl_id, hid_t dxpl_id); +static herr_t H5G_traverse_slink(const H5G_loc_t *grp_loc, const H5O_link_t *lnk, + H5G_loc_t *obj_loc/*in,out*/, unsigned target, size_t *nlinks/*in,out*/, + hbool_t *obj_exists, hid_t lapl_id, hid_t dxpl_id); +static herr_t H5G_traverse_real(const H5G_loc_t *loc, const char *name, + unsigned target, size_t *nlinks, H5G_traverse_t op, void *op_data, + hid_t lapl_id, hid_t dxpl_id); + + +/*********************/ +/* Package Variables */ +/*********************/ + + +/*****************************/ +/* Library Private Variables */ +/*****************************/ + + +/*******************/ +/* Local Variables */ +/*******************/ + + + +/*------------------------------------------------------------------------- + * Function: H5G_traverse_slink_cb + * + * Purpose: Callback for soft 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 H5_ATTR_UNUSED *grp_loc, const char H5_ATTR_UNUSED *name, + const H5O_link_t H5_ATTR_UNUSED *lnk, H5G_loc_t *obj_loc, void *_udata/*in,out*/, + H5G_own_loc_t *own_loc/*out*/) +{ + H5G_trav_slink_t *udata = (H5G_trav_slink_t *)_udata; /* User data passed in */ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI_NOINIT + + /* Check for dangling soft link */ + if(obj_loc == NULL) { + if(udata->chk_exists) + udata->exists = FALSE; + else + HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "component not found") + } /* end if */ + else { + /* Copy new location information for resolved object */ + H5O_loc_copy(udata->obj_loc->oloc, obj_loc->oloc, H5_COPY_DEEP); + + /* Indicate that the object exists */ + udata->exists = TRUE; + } /* end else */ + +done: + /* Indicate that this callback didn't take ownership of the group * + * location for the object */ + *own_loc = H5G_OWN_NONE; + + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5G_traverse_slink_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(const H5G_loc_t *grp_loc/*in,out*/, const H5O_link_t *lnk, + H5G_loc_t *obj_loc/*in,out*/, unsigned target, size_t *nlinks/*in,out*/, + hbool_t *obj_exists, hid_t _lapl_id, hid_t dxpl_id) +{ + const H5L_class_t *link_class; /* User-defined link class */ + hid_t cb_return = -1; /* The ID the user-defined callback returned */ + H5G_loc_t grp_loc_copy; + H5G_name_t grp_path_copy; + H5O_loc_t grp_oloc_copy; + H5G_loc_t new_loc; /* Group location for newly opened external object */ + H5G_t *grp; + hid_t lapl_id = (-1); /* LAPL local to this routine */ + H5P_genplist_t *lapl; /* LAPL with nlinks set */ + hid_t cur_grp = (-1); + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI_NOINIT + + /* Sanity check */ + HDassert(grp_loc); + HDassert(lnk); + HDassert(lnk->type >= H5L_TYPE_UD_MIN); + HDassert(obj_loc); + HDassert(nlinks); + HDassert(_lapl_id >= 0); + + /* Get the link class for this type of link. */ + if(NULL == (link_class = H5L_find_class(lnk->type))) + HGOTO_ERROR(H5E_SYM, H5E_NOTREGISTERED, FAIL, "unable to get UD link class") + + /* Set up location for user-defined callback. Use a copy of our current + * grp_loc. */ + grp_loc_copy.path = &grp_path_copy; + grp_loc_copy.oloc = &grp_oloc_copy; + H5G_loc_reset(&grp_loc_copy); + if(H5G_loc_copy(&grp_loc_copy, grp_loc, H5_COPY_DEEP) < 0) + HGOTO_ERROR(H5E_SYM, H5E_CANTCOPY, FAIL, "unable to copy object location") + + /* Create a group ID to pass to the user-defined callback */ + if(NULL == (grp = H5G_open(&grp_loc_copy, dxpl_id))) + HGOTO_ERROR(H5E_SYM, H5E_CANTOPENOBJ, FAIL, "unable to open group") + if((cur_grp = H5I_register(H5I_GROUP, grp, FALSE)) < 0) + HGOTO_ERROR(H5E_SYM, H5E_CANTREGISTER, FAIL, "unable to register group") + + /* Check for generic default property list and use link access default if so */ + if(_lapl_id == H5P_DEFAULT) { + HDassert(H5P_LINK_ACCESS_DEFAULT != -1); + if(NULL == (lapl = (H5P_genplist_t *)H5I_object(H5P_LINK_ACCESS_DEFAULT))) + HGOTO_ERROR(H5E_SYM, H5E_BADATOM, FAIL, "unable to get default property list") + } /* end if */ + else { + /* Get the underlying property list passed in */ + if(NULL == (lapl = (H5P_genplist_t *)H5I_object(_lapl_id))) + HGOTO_ERROR(H5E_SYM, H5E_BADATOM, FAIL, "unable to get property list from ID") + } /* end else */ + + /* Copy the property list passed in */ + if((lapl_id = H5P_copy_plist(lapl, FALSE)) < 0) + HGOTO_ERROR(H5E_SYM, H5E_CANTCOPY, FAIL, "unable to copy property list") + + /* Get the underlying property list copy */ + if(NULL == (lapl = (H5P_genplist_t *)H5I_object(lapl_id))) + HGOTO_ERROR(H5E_SYM, H5E_BADATOM, FAIL, "unable to get property list from ID") + + /* Record number of soft links left to traverse in the property list. */ + if(H5P_set(lapl, H5L_ACS_NLINKS_NAME, nlinks) < 0) + HGOTO_ERROR(H5E_SYM, H5E_CANTSET, FAIL, "can't set nlink info") + + /* User-defined callback function */ + cb_return = (link_class->trav_func)(lnk->name, cur_grp, lnk->u.ud.udata, lnk->u.ud.size, lapl_id); + + /* Check for failing to locate the object */ + if(cb_return < 0) { + /* Check if we just needed to know if the object exists */ + if(target & H5G_TARGET_EXISTS) { + /* Clear any errors from the stack */ + H5E_clear_stack(NULL); + + /* Indicate that the object doesn't exist */ + *obj_exists = FALSE; + + /* Get out now */ + HGOTO_DONE(SUCCEED); + } /* end if */ + /* else, we really needed to open the object */ + else + HGOTO_ERROR(H5E_SYM, H5E_BADATOM, FAIL, "traversal callback returned invalid ID") + } /* end if */ + + /* Get the object location information from the ID the user callback returned */ + if(H5G_loc(cb_return, &new_loc) < 0) + HGOTO_ERROR(H5E_SYM, H5E_BADVALUE, FAIL, "unable to get object location from ID") + + /* Release any previous location information for the object */ + H5G_loc_free(obj_loc); + + /* Copy new object's location information */ + H5G_loc_copy(obj_loc, &new_loc, H5_COPY_DEEP); + + /* Hold the file open until we free this object header (otherwise the + * object location will be invalidated when the file closes). + */ + if(H5O_loc_hold_file(obj_loc->oloc) < 0) + HGOTO_ERROR(H5E_SYM, H5E_CANTOPENOBJ, FAIL, "unable to hold file open") + + /* We have a copy of the location and we're holding the file open. + * Close the open ID the user passed back. + */ + if(H5I_dec_ref(cb_return) < 0) + HGOTO_ERROR(H5E_SYM, H5E_CANTRELEASE, FAIL, "unable to close atom from UD callback") + cb_return = (hid_t)(-1); + +done: + /* Close location given to callback. */ + if(cur_grp > 0 && H5I_dec_ref(cur_grp) < 0) + HDONE_ERROR(H5E_SYM, H5E_CANTRELEASE, FAIL, "unable to close atom for current location") + + if(ret_value < 0 && cb_return > 0 && H5I_dec_ref(cb_return) < 0) + HDONE_ERROR(H5E_SYM, H5E_CANTRELEASE, FAIL, "unable to close atom from UD callback") + + /* Close the LAPL, if we copied one */ + if(lapl_id > 0 && H5I_dec_ref(lapl_id) < 0) + HDONE_ERROR(H5E_SYM, H5E_CANTRELEASE, FAIL, "unable to close copied link access property list") + + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5G_traverse_ud() */ + + +/*------------------------------------------------------------------------- + * 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 + * + * Failure: Negative + * + * Programmer: Robb Matzke + * Friday, April 10, 1998 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5G_traverse_slink(const H5G_loc_t *grp_loc, const H5O_link_t *lnk, + H5G_loc_t *obj_loc/*in,out*/, unsigned target, size_t *nlinks/*in,out*/, + hbool_t *obj_exists, hid_t lapl_id, hid_t dxpl_id) +{ + H5G_trav_slink_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_loc_set = FALSE; /* Flag to indicate that tmp group location is initialized */ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI_NOINIT + + /* Sanity check */ + HDassert(grp_loc); + HDassert(lnk); + HDassert(lnk->type == H5L_TYPE_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); + H5G_name_reset(&tmp_obj_path); + + /* Clone the group location, so we can track the names properly */ + /* ("tracking the names properly" means to ignore the effects of the + * link traversal on the object's & group's paths - QAK) + */ + H5G_loc_copy(&tmp_grp_loc, grp_loc, H5_COPY_DEEP); + tmp_grp_loc_set = TRUE; + + /* Hold the object's group hier. path to restore later */ + /* (Part of "tracking the names properly") */ + H5G_name_copy(&tmp_obj_path, obj_loc->path, H5_COPY_SHALLOW); + tmp_obj_path_set = TRUE; + + /* Set up user data for traversal callback */ + udata.chk_exists = (target & H5G_TARGET_EXISTS) ? TRUE : FALSE; + udata.exists = FALSE; + udata.obj_loc = obj_loc; + + /* Traverse the link */ + if(H5G_traverse_real(&tmp_grp_loc, lnk->u.soft.name, target, nlinks, H5G_traverse_slink_cb, &udata, lapl_id, dxpl_id) < 0) + HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "unable to follow symbolic link") + + /* Pass back information about whether the object exists */ + *obj_exists = udata.exists; + +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, H5_COPY_SHALLOW); + } /* end if */ + + /* Release cloned copy of group location */ + if(tmp_grp_loc_set) + H5G_loc_free(&tmp_grp_loc); + + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5G_traverse_slink() */ + + +/*------------------------------------------------------------------------- + * Function: H5G__traverse_special + * + * Purpose: Handle traversing special link situations + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Quincey Koziol + * koziol@hdfgroup.org + * Nov 20 2006 + * + *------------------------------------------------------------------------- + */ +herr_t +H5G__traverse_special(const H5G_loc_t *grp_loc, const H5O_link_t *lnk, + unsigned target, size_t *nlinks, hbool_t last_comp, + H5G_loc_t *obj_loc, hbool_t *obj_exists, hid_t lapl_id, hid_t dxpl_id) +{ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_PACKAGE + + /* Sanity check */ + HDassert(grp_loc); + HDassert(lnk); + HDassert(obj_loc); + HDassert(nlinks); + + /* + * 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(H5L_TYPE_SOFT == lnk->type && + (0 == (target & H5G_TARGET_SLINK) || !last_comp)) { + if((*nlinks)-- <= 0) + HGOTO_ERROR(H5E_LINK, H5E_NLINKS, FAIL, "too many links") + if(H5G_traverse_slink(grp_loc, lnk, obj_loc, (target & H5G_TARGET_EXISTS), nlinks, obj_exists, lapl_id, dxpl_id) < 0) + HGOTO_ERROR(H5E_LINK, H5E_TRAVERSE, FAIL, "symbolic link traversal failed") + } /* end if */ + + /* + * If we found a user-defined link then we should follow it. But if this + * is the last component of the name and the H5G_TARGET_UDLINK bit of + * TARGET is set then we don't follow it. + */ + if(lnk->type >= H5L_TYPE_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, lnk, obj_loc, (target & H5G_TARGET_EXISTS), nlinks, obj_exists, lapl_id, dxpl_id) < 0) + HGOTO_ERROR(H5E_LINK, H5E_TRAVERSE, FAIL, "user-defined 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 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)) { + if(H5F_traverse_mount(obj_loc->oloc/*in,out*/) < 0) + HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "mount point traversal failed") + } /* end if */ + + /* If the grp_loc is the only thing holding an external file open + * and obj_loc is in the same file, obj_loc should also hold the + * file open so that closing the grp_loc doesn't close the file. + */ + if(grp_loc->oloc->holding_file && grp_loc->oloc->file == obj_loc->oloc->file) + if(H5O_loc_hold_file(obj_loc->oloc) < 0) + HGOTO_ERROR(H5E_SYM, H5E_CANTOPENOBJ, FAIL, "unable to hold file open") + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5G__traverse_special() */ + + +/*------------------------------------------------------------------------- + * 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, + size_t *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 */ + 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 */ + H5G_own_loc_t own_loc = H5G_OWN_NONE; /* Enum to indicate whether callback took ownership of locations*/ + hbool_t group_copy = FALSE; /* Flag to indicate that the group entry is copied */ + char comp_buf[1024]; /* Temporary buffer for path components */ + char *comp; /* Pointer to buffer for path components */ + H5WB_t *wb = NULL; /* Wrapped buffer for temporary buffer */ + 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 + + /* 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; + +#if defined(H5_USING_MEMCHECKER) || !defined(NDEBUG) + /* Clear group location */ + if(H5G_loc_reset(&grp_loc) < 0) + HGOTO_ERROR(H5E_SYM, H5E_CANTOPENOBJ, FAIL, "unable to reset location") +#endif /* H5_USING_MEMCHECKER */ + + /* Deep copy of the starting location to group location */ + if(H5G_loc_copy(&grp_loc, &loc, H5_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") + + /* Wrap the local buffer for serialized header info */ + if(NULL == (wb = H5WB_wrap(comp_buf, sizeof(comp_buf)))) + HGOTO_ERROR(H5E_SYM, H5E_CANTINIT, FAIL, "can't wrap buffer") + + /* Get a pointer to a buffer that's large enough */ + if(NULL == (comp = (char *)H5WB_actual(wb, (HDstrlen(name) + 1)))) + HGOTO_ERROR(H5E_SYM, H5E_NOSPACE, FAIL, "can't get actual buffer") + + /* Traverse the path */ + while((name = H5G__component(name, &nchars)) && *name) { + const char *s; /* Temporary string pointer */ + htri_t lookup_status; /* Status from object lookup */ + hbool_t obj_exists; /* Whether the object exists */ + + /* + * Copy the component name into a null-terminated buffer so + * we can pass it down to the other symbol table functions. + */ + HDmemcpy(comp, name, nchars); + comp[nchars] = '\0'; + + /* + * The special name `.' is a no-op. + */ + if('.' == comp[0] && !comp[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_msg_reset(H5O_LINK_ID, &lnk); + link_valid = FALSE; + } /* end if */ + + /* Get information for object in current group */ + if((lookup_status = H5G__obj_lookup(grp_loc.oloc, comp, &lnk/*out*/, dxpl_id)) < 0) + HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "can't look up component") + obj_exists = FALSE; + + /* If the lookup was OK, build object location and traverse special links, etc. */ + if(lookup_status) { + /* Sanity check link and indicate it's valid */ + HDassert(lnk.type >= H5L_TYPE_HARD); + HDassert(!HDstrcmp(comp, lnk.name)); + link_valid = TRUE; + + /* Build object location from the link */ + if(H5G__link_to_loc(&grp_loc, &lnk, &obj_loc) < 0) + HGOTO_ERROR(H5E_SYM, H5E_CANTINIT, FAIL, "cannot initialize object location") + obj_loc_valid = TRUE; + + /* Assume object exists */ + obj_exists = TRUE; + + /* Perform any special traversals that the link needs */ + /* (soft links, user-defined links, file mounting, etc.) */ + if(H5G__traverse_special(&grp_loc, &lnk, target, nlinks, last_comp, &obj_loc, &obj_exists, lapl_id, dxpl_id) < 0) + HGOTO_ERROR(H5E_LINK, H5E_TRAVERSE, FAIL, "special link traversal failed") + } /* end if */ + + /* Check for last component in name provided */ + if(last_comp) { + H5O_link_t *cb_lnk; /* Pointer to link info for callback */ + H5G_loc_t *cb_loc; /* Pointer to object location for callback */ + + /* Set callback parameters appropriately, based on link being found */ + if(lookup_status) { + cb_lnk = &lnk; + if(obj_exists) + cb_loc = &obj_loc; + else + cb_loc = NULL; + } /* end if */ + else { + HDassert(!obj_loc_valid); + cb_lnk = NULL; + cb_loc = NULL; + } /* end else */ + + /* Call 'operator' routine */ + if((op)(&grp_loc, comp, cb_lnk, cb_loc, op_data, &own_loc) < 0) + HGOTO_ERROR(H5E_SYM, H5E_CALLBACK, FAIL, "traversal operator failed") + + HGOTO_DONE(SUCCEED) + } /* end if */ + + /* Handle lookup failures now */ + if(!lookup_status) { + /* If an intermediate group doesn't exist & flag is set, create the group */ + if(target & H5G_CRT_INTMD_GROUP) { + const H5O_ginfo_t def_ginfo = H5G_CRT_GROUP_INFO_DEF; /* Default group info settings */ + const H5O_linfo_t def_linfo = H5G_CRT_LINK_INFO_DEF; /* Default link info settings */ + const H5O_pline_t def_pline = H5O_CRT_PIPELINE_DEF; /* Default filter pipeline settings */ + H5O_ginfo_t par_ginfo; /* Group info settings for parent group */ + H5O_linfo_t par_linfo; /* Link info settings for parent group */ + H5O_pline_t par_pline; /* Filter pipeline settings for parent group */ + H5O_linfo_t tmp_linfo; /* Temporary link info settings */ + htri_t exists; /* Whether a group or link info message exists */ + const H5O_ginfo_t *ginfo; /* Group info settings for new group */ + const H5O_linfo_t *linfo; /* Link info settings for new group */ + const H5O_pline_t *pline; /* Filter pipeline settings for new group */ + H5G_obj_create_t gcrt_info; /* Group creation info */ + + /* Check for the parent group having a group info message */ + /* (OK if not found) */ + if((exists = H5O_msg_exists(grp_loc.oloc, H5O_GINFO_ID, dxpl_id)) < 0) + HGOTO_ERROR(H5E_SYM, H5E_CANTGET, FAIL, "unable to read object header") + if(exists) { + /* Get the group info for parent group */ + if(NULL == H5O_msg_read(grp_loc.oloc, H5O_GINFO_ID, &par_ginfo, dxpl_id)) + HGOTO_ERROR(H5E_SYM, H5E_CANTGET, FAIL, "group info message not present") + + /* Use parent group info settings */ + ginfo = &par_ginfo; + } /* end if */ + else + /* Use default group info settings */ + ginfo = &def_ginfo; + + /* Check for the parent group having a link info message */ + /* (OK if not found) */ + /* Get the link info for parent group */ + if((exists = H5G__obj_get_linfo(grp_loc.oloc, &par_linfo, dxpl_id)) < 0) + HGOTO_ERROR(H5E_SYM, H5E_CANTGET, FAIL, "unable to read object header") + if(exists) { + /* Only keep the creation order information from the parent + * group's link info + */ + HDmemcpy(&tmp_linfo, &def_linfo, sizeof(H5O_linfo_t)); + tmp_linfo.track_corder = par_linfo.track_corder; + tmp_linfo.index_corder = par_linfo.index_corder; + linfo = &tmp_linfo; + } /* end if */ + else + /* Use default link info settings */ + linfo = &def_linfo; + + /* Check for the parent group having a filter pipeline message */ + /* (OK if not found) */ + if((exists = H5O_msg_exists(grp_loc.oloc, H5O_PLINE_ID, dxpl_id)) < 0) + HGOTO_ERROR(H5E_SYM, H5E_CANTGET, FAIL, "unable to read object header") + if(exists) { + /* Get the filter pipeline for parent group */ + if(NULL == H5O_msg_read(grp_loc.oloc, H5O_PLINE_ID, &par_pline, dxpl_id)) + HGOTO_ERROR(H5E_SYM, H5E_CANTGET, FAIL, "filter pipeline message not present") + + /* Use parent filter pipeline settings */ + pline = &par_pline; + } /* end if */ + else + /* Use default filter pipeline settings */ + pline = &def_pline; + + /* Create the intermediate group */ +/* XXX: Should we allow user to control the group creation params here? -QAK */ + gcrt_info.gcpl_id = H5P_GROUP_CREATE_DEFAULT; + gcrt_info.cache_type = H5G_NOTHING_CACHED; + HDmemset(&gcrt_info.cache, 0, sizeof(gcrt_info.cache)); + if(H5G__obj_create_real(grp_oloc.file, dxpl_id, ginfo, linfo, pline, &gcrt_info, 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, comp, &obj_loc, H5O_TYPE_GROUP, &gcrt_info, dxpl_id) < 0) + HGOTO_ERROR(H5E_SYM, H5E_CANTINSERT, FAIL, "unable to insert intermediate group") + + /* Decrement refcount on intermediate group's object header in memory */ + if(H5O_dec_rc_by_loc(obj_loc.oloc, dxpl_id) < 0) + HGOTO_ERROR(H5E_SYM, H5E_CANTDEC, FAIL, "unable to decrement refcount on newly created object") + + /* Close new group */ + if(H5O_close(obj_loc.oloc, NULL) < 0) + HGOTO_ERROR(H5E_SYM, H5E_CANTINIT, FAIL, "unable to close") + + /* If the parent group was holding the file open, the + * newly-created group should, as well. + */ + if(grp_loc.oloc->holding_file) + if(H5O_loc_hold_file(obj_loc.oloc) < 0) + HGOTO_ERROR(H5E_SYM, H5E_CANTOPENOBJ, FAIL, "unable to hold file open") + + /* Reset any non-default object header messages */ + if(ginfo != &def_ginfo) + /* (Casting away const OK - QAK) */ + if(H5O_msg_reset(H5O_GINFO_ID, (void *)ginfo) < 0) + HGOTO_ERROR(H5E_SYM, H5E_CANTRELEASE, FAIL, "unable to reset group info message") + if(linfo != &def_linfo) + /* (Casting away const OK - QAK) */ + if(H5O_msg_reset(H5O_LINFO_ID, (void *)linfo) < 0) + HGOTO_ERROR(H5E_SYM, H5E_CANTRELEASE, FAIL, "unable to reset link info message") + if(pline != &def_pline) + /* (Casting away const OK - QAK) */ + if(H5O_msg_reset(H5O_PLINE_ID, (void *)pline) < 0) + HGOTO_ERROR(H5E_SYM, H5E_CANTRELEASE, FAIL, "unable to reset I/O pipeline message") + } /* 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, H5_COPY_SHALLOW); + H5G_loc_reset(&obj_loc); + obj_loc_valid = FALSE; + + /* Advance to next component in string */ + name += nchars; + } /* end while */ + + /* Call 'operator' routine */ + /* If we've fallen through to here, the name must be something like just '.' + * and we should issue the callback on that. -QAK + * Since we don't have a group location or a link to the object we pass in + * NULL. + */ + HDassert(group_copy); + if((op)(NULL, ".", NULL, &grp_loc, op_data, &own_loc) < 0) + HGOTO_ERROR(H5E_SYM, H5E_CANTNEXT, FAIL, "traversal operator failed") + + /* If the callback took ownership of the object location, it actually has + * ownership of grp_loc. It shouldn't have tried to take ownership of + * the "group location", which was NULL. */ + HDassert(!(own_loc & H5G_OWN_GRP_LOC)); + if(own_loc & H5G_OWN_OBJ_LOC) + own_loc |= H5G_OWN_GRP_LOC; + +done: + /* If there's been an error, the callback doesn't really get ownership of + * any location and we should close them both */ + if(ret_value < 0) + own_loc = H5G_OWN_NONE; + + /* Free all open locations. This also closes any open external files. */ + if(obj_loc_valid && !(own_loc & H5G_OWN_OBJ_LOC)) + H5G_loc_free(&obj_loc); + if(group_copy && !(own_loc & H5G_OWN_GRP_LOC)) + H5G_loc_free(&grp_loc); + + /* If there's valid information in the link, reset it */ + if(link_valid) + if(H5O_msg_reset(H5O_LINK_ID, &lnk) < 0) + HDONE_ERROR(H5E_SYM, H5E_CANTRELEASE, FAIL, "unable to reset link message") + + /* Release temporary component buffer */ + if(wb && H5WB_unwrap(wb) < 0) + HDONE_ERROR(H5E_SYM, H5E_CANTRELEASE, FAIL, "can't release wrapped buffer") + + 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 lapl_id, hid_t dxpl_id) +{ + size_t nlinks; /* Link countdown value */ + H5P_genplist_t *lapl; /* Property list with value for nlinks */ + herr_t ret_value = SUCCEED; /* Return value */ + + FUNC_ENTER_NOAPI(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") + HDassert(lapl_id >= 0); + + /* Set nlinks value from property list, if it exists */ + if(lapl_id == H5P_DEFAULT) + nlinks = H5L_NUM_LINKS; + else { + if(NULL == (lapl = (H5P_genplist_t *)H5I_object(lapl_id))) + HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, FAIL, "can't find object for ID") + if(H5P_get(lapl, H5L_ACS_NLINKS_NAME, &nlinks) < 0) + HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get number of links") + } /* end else */ + + /* Set up invalid tag. This is a precautionary step only. Setting an invalid + tag here will ensure that no metadata accessed while doing the traversal + is given an improper tag, unless another one is specifically set up + first. This will ensure we're not accidentally tagging something we + shouldn't be during the traversal. Note that for best tagging assertion + coverage, setting H5C_DO_TAGGING_SANITY_CHECKS is advised. */ + H5_BEGIN_TAG(dxpl_id, H5AC__INVALID_TAG, FAIL); + + /* Go perform "real" traversal */ + if(H5G_traverse_real(loc, name, target, &nlinks, op, op_data, lapl_id, dxpl_id) < 0) + HGOTO_ERROR_TAG(H5E_SYM, H5E_NOTFOUND, FAIL, "internal path traversal failed") + + /* Reset tag after traversal */ + H5_END_TAG(FAIL); + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5G_traverse() */ + |