/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 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. *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#define H5F_PACKAGE		/*suppress error about including H5Fpkg	  */
#define H5G_PACKAGE		/*suppress error about including H5Gpkg   */
#define H5L_PACKAGE		/*suppress error about including H5Gpkg   */

/* Interface initialization */
#define H5_INTERFACE_INIT_FUNC	H5L_init_interface

#include "H5private.h"          /* Generic Functions                    */
#include "H5Lpkg.h"             /* Links                                */
#include "H5Fpkg.h"             /* File access                          */
#include "H5Eprivate.h"         /* Error handling                       */
#include "H5Iprivate.h"         /* IDs                                  */
#include "H5MMprivate.h"        /* Memory management                    */
#include "H5Oprivate.h"         /* File objects                         */
#include "H5Dprivate.h"         /* Datasets                             */
#include "H5Pprivate.h"         /* Property lists                       */
#include "H5Gpkg.h"             /* Groups                               */

/* Local typedefs */
#define H5L_MOVE_OP 1
#define H5L_RENAME_OP 2

/* User data for path traversal routine for getting link metadata */
typedef struct {
    H5L_linkinfo_t  *linfo;                  /* Buffer to return to user */
    hid_t           dxpl_id;                   /* dxpl to use in callback */
} H5L_trav_ud1_t;

/* User data for path traversal callback to creating a link */
typedef struct {
    H5F_t *file;                                /* Pointer to the file */
    hid_t dxpl_id;                              /* Dataset transfer property list */
    H5G_name_t *path;                           /* Path to object being linked */
    H5O_link_t *lnk;                            /* Pointer to link information to insert */
} H5L_trav_ud3_t;

/* User data for path traversal routine for moving and renaming a link */
typedef struct {
    const char *dst_name;                       /* Destination name for moving object */
    H5T_cset_t cset;                            /* Char set for new name */
    H5G_loc_t  *dst_loc;			/* Destination location for moving object */
    hbool_t copy;                               /* TRUE if this is a copy operation */
    hid_t dxpl_id;                              /* dxpl to use in callback */
} H5L_trav_ud4_t;

/* User data for path traversal routine for getting soft link value */
typedef struct {
    size_t size;                                /* Size of user buffer */
    char *buf;                                  /* User buffer */
} H5L_trav_ud5_t;

/* User data for path traversal routine for removing link (i.e. unlink) */
typedef struct {
    hid_t dxpl_id;                              /* Dataset transfer property list */
} H5L_trav_ud6_t;

/* User data for path traversal routine for retrieving link creation property list */
typedef struct {
    H5P_genplist_t *lcpl;                     /* Copy of default property list to be set */
} H5L_trav_ud8_t;

/* User data for path traversal routine for moving and renaming an object */
typedef struct {
    H5F_t *file;                                /* Pointer to the file */
    H5O_link_t *lnk;                            /* Pointer to link information to insert */
    hid_t dxpl_id;                              /* Dataset transfer property list */
} H5L_trav_ud10_t;

/* Package variables */

/* Local variables */

/* Private prototypes */
static herr_t H5L_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*/);
static herr_t H5L_create_real(H5G_loc_t *link_loc, const char *link_name,
    H5G_name_t *obj_path, H5F_t *obj_file, H5O_link_t *lnk, hid_t dxpl_id,
    hid_t lcpl_id);
static herr_t H5L_create_hard(H5G_loc_t *cur_loc, const char *cur_name,
    H5G_loc_t *link_loc, const char *link_name, hid_t dxpl_id, hid_t lcpl_id);
static herr_t H5L_create_soft(const char *target_path, H5G_loc_t *loc,
    const char *name, hid_t dxpl_id, hid_t lcpl_id);
static herr_t H5L_linkval_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 H5L_linkval(H5G_loc_t *loc, const char *name, size_t size,
    char *buf/*out*/, hid_t dxpl_id);
static herr_t H5L_unlink_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 H5L_unlink(H5G_loc_t *loc, const char *name, hid_t dxpl_id);
static herr_t H5L_move(H5G_loc_t *src_loc, const char *src_name,
    H5G_loc_t *dst_loc, const char *dst_name, hbool_t copy_flag,
    hid_t lcpl_id, hid_t dxpl_id);
static herr_t H5L_move_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 H5L_move_dest_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 H5L_get_linkinfo(H5G_loc_t *loc, const char *name,
    H5L_linkinfo_t *linkbuf/*out*/, hid_t dxpl_id);
static herr_t H5L_get_linfo_cb(H5G_loc_t UNUSED *grp_loc/*in*/, const char UNUSED *name,
    const H5O_link_t *lnk, H5G_loc_t UNUSED *obj_loc, void *_udata/*in,out*/);


/*-------------------------------------------------------------------------
 * Function:	H5L_init_interface
 *
 * Purpose:	Initialize information specific to H5L interface.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	James Laird
 *              Tuesday, January 24, 2006
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5L_init_interface(void)
{
    H5P_genclass_t  *crt_pclass;
    size_t          nprops;                 /* Number of properties */
    unsigned        intmd_group = H5L_CRT_INTERMEDIATE_GROUP_DEF;
    herr_t ret_value = SUCCEED;   /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5L_init_interface)

    /* =========Link Creation Property Class Initialization========= */
    /* Register the default attribute creation properties */
    assert(H5P_CLS_LINK_CREATE_g!=(-1));

    /* Get the pointer to the link creation class */
    if (NULL == (crt_pclass = H5I_object(H5P_CLS_LINK_CREATE_g)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a property list class")

    /* Get the number of properties in the class */
    if(H5P_get_nprops_pclass(crt_pclass,&nprops,FALSE)<0)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "can't query number of properties")

    /* Assume that if there are properties in the class, they are the default ones */
    if(nprops==0) {
        /* Register create intermediate groups property */
        if(H5P_register(crt_pclass,H5L_CRT_INTERMEDIATE_GROUP_NAME,H5L_CRT_INTERMEDIATE_GROUP_SIZE,
                 &intmd_group,NULL,NULL,NULL,NULL,NULL,NULL,NULL)<0)
             HGOTO_ERROR(H5E_PLIST, H5E_CANTINSERT, FAIL, "can't insert property into class")
    }

    /* Only register the default property list if it hasn't been created yet */
    if(H5P_LST_LINK_CREATE_g==(-1)) {
        /* Register the default link creation property list */
        if ((H5P_LST_LINK_CREATE_g = H5P_create_id (crt_pclass))<0)
            HGOTO_ERROR (H5E_PLIST, H5E_CANTREGISTER, FAIL, "can't register default property list")
    } /* end if */

done:
    FUNC_LEAVE_NOAPI(ret_value)
}


/*-------------------------------------------------------------------------
 * Function:	H5Lmove
 *
 * Purpose:	Renames an object within an HDF5 file and moves it to a new
 *              group.  The original name SRC is unlinked from the group graph
 *              and the inserted with the new name DST (which can specify a
 *              new path for the object) as an atomic operation. The names
 *              are interpreted relative to SRC_LOC_ID and
 *              DST_LOC_ID, which are either file IDs or group ID.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	James Laird
 *              Wednesday, March 29, 2006
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lmove(hid_t src_loc_id, const char *src_name, hid_t dst_loc_id,
              const char *dst_name, hid_t lcpl_id)
{
    H5G_loc_t	src_loc, *src_loc_p;
    H5G_loc_t	dst_loc, *dst_loc_p;
    herr_t      ret_value=SUCCEED;              /* Return value */

    FUNC_ENTER_API(H5Lmove, FAIL)
    H5TRACE5("e","isisi",src_loc_id,src_name,dst_loc_id,dst_name,lcpl_id);

    /* Check arguments */
    if(src_loc_id != H5L_SAME_LOC && H5G_loc(src_loc_id, &src_loc) < 0)
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a location")
    if(dst_loc_id != H5L_SAME_LOC && H5G_loc(dst_loc_id, &dst_loc) < 0)
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a location")
    if(!src_name || !*src_name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no current name specified")
    if(!dst_name || !*dst_name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no destination name specified")

    /* Set up src & dst location pointers */
    src_loc_p = &src_loc;
    dst_loc_p = &dst_loc;
    if(src_loc_id == H5L_SAME_LOC && dst_loc_id == H5L_SAME_LOC)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "source and destination should not both be H5L_SAME_LOC")
    else if(src_loc_id == H5L_SAME_LOC)
        src_loc_p = dst_loc_p;
    else if(dst_loc_id == H5L_SAME_LOC)
        dst_loc_p = src_loc_p;

    if(lcpl_id != H5P_DEFAULT && (TRUE != H5P_isa_class(lcpl_id, H5P_LINK_CREATE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a link creation property list")

    if(H5L_move(src_loc_p, src_name, dst_loc_p, dst_name,
                                FALSE, lcpl_id, H5AC_dxpl_id) < 0)
	HGOTO_ERROR(H5E_SYM, H5E_CANTINIT, FAIL, "unable to move link")

done:    
    FUNC_LEAVE_API(ret_value)
}


/*-------------------------------------------------------------------------
 * Function:	H5Lcopy
 *
 * Purpose:	Creates an identical copy of a link with the same creation
 *              time and target.  The new link can have a different name
 *              and be in a different location than the original.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	James Laird
 *              Wednesday, March 29, 2006
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lcopy(hid_t src_loc_id, const char *src_name, hid_t dst_loc_id,
              const char *dst_name, hid_t lcpl_id)
{
    H5G_loc_t	src_loc, *src_loc_p;
    H5G_loc_t	dst_loc, *dst_loc_p;
    herr_t      ret_value=SUCCEED;              /* Return value */

    FUNC_ENTER_API(H5Lcopy, FAIL)
    H5TRACE5("e","isisi",src_loc_id,src_name,dst_loc_id,dst_name,lcpl_id);

    /* Check arguments */
    if(src_loc_id != H5L_SAME_LOC && H5G_loc(src_loc_id, &src_loc) < 0)
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a location")
    if(dst_loc_id != H5L_SAME_LOC && H5G_loc(dst_loc_id, &dst_loc) < 0)
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a location")
    if(!src_name || !*src_name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no current name specified")
    if(!dst_name || !*dst_name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no destination name specified")

    /* Set up src & dst location pointers */
    src_loc_p = &src_loc;
    dst_loc_p = &dst_loc;
    if(src_loc_id == H5L_SAME_LOC && dst_loc_id == H5L_SAME_LOC)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "source and destination should not both be H5L_SAME_LOC")
    else if(src_loc_id == H5L_SAME_LOC)
        src_loc_p = dst_loc_p;
    else if(dst_loc_id == H5L_SAME_LOC)
        dst_loc_p = src_loc_p;

    if(lcpl_id != H5P_DEFAULT && (TRUE != H5P_isa_class(lcpl_id, H5P_LINK_CREATE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a link creation property list")

    if(H5L_move(src_loc_p, src_name, dst_loc_p, dst_name, TRUE, lcpl_id, H5AC_dxpl_id) < 0)
	HGOTO_ERROR(H5E_SYM, H5E_CANTINIT, FAIL, "unable to move link")

done:    
    FUNC_LEAVE_API(ret_value)
}


/*-------------------------------------------------------------------------
 * Function:	H5Llink
 *
 * Purpose:	Creates a hard link from NEW_NAME to the object specified
 *		by OBJ_ID using properties defined in the Link Creation
 *              Property List LCPL.
 *
 *		This function should be used to link objects that have just
 *              been created.
 *
 *		CUR_NAME and NEW_NAME are interpreted relative to
 *		CUR_LOC_ID and NEW_LOC_ID, which is either a file ID or a
 *		group ID.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	James Laird
 *              Tuesday, December 13, 2005
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Llink(hid_t new_loc_id, const char *new_name, hid_t obj_id, hid_t lcpl_id)
{
    H5G_loc_t	new_loc;
    H5G_loc_t	obj_loc;
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_API(H5Llink, FAIL)
    H5TRACE4("e","isii",new_loc_id,new_name,obj_id,lcpl_id);

    /* Check arguments */
    if(new_loc_id == H5L_SAME_LOC)
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "cannot use H5L_SAME_LOC when only one location is specified")
    if(H5G_loc(new_loc_id, &new_loc) < 0)
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a location")
    if(H5G_loc(obj_id, &obj_loc) < 0)
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a location")
    if(!new_name || !*new_name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no name specified")

    if(lcpl_id != H5P_DEFAULT && (TRUE != H5P_isa_class(lcpl_id, H5P_LINK_CREATE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a link creation property list")

    if(H5L_link(&new_loc, new_name, &obj_loc, H5AC_dxpl_id, lcpl_id ) < 0)
        HGOTO_ERROR(H5E_SYM, H5E_LINK, FAIL, "unable to create link")

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Llink() */


/*-------------------------------------------------------------------------
 * Function:	H5Lcreate_soft
 *
 * Purpose:	Creates a soft link from NEW_NAME to TARGET_PATH.
 *
 * 		TARGET_PATH can be anything and is interpreted at lookup
 *              time relative to the group which contains the final component
 *              of NEW_NAME.  For instance, if TARGET_PATH is `./foo' and
 *              NEW_NAME is `./x/y/bar' and a request is made for `./x/y/bar'
 *              then the actual object looked up is `./x/y/./foo'.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Robb Matzke
 *              Monday, April  6, 1998
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lcreate_soft(const char *target_path,
	 hid_t loc_id, const char *name, hid_t lcpl_id)
{
    H5G_loc_t	new_loc, *new_loc_p;
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_API(H5Lcreate_soft, FAIL)
    H5TRACE4("e","sisi",target_path,loc_id,name,lcpl_id);

    /* Check arguments */
    if(H5G_loc(loc_id, &new_loc) < 0)
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a location")
    if(!target_path || !*target_path)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no target specified")
    if(!name || !*name)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no new name specified")

    if(lcpl_id != H5P_DEFAULT && (TRUE != H5P_isa_class(lcpl_id, H5P_LINK_CREATE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a link creation property list")

    new_loc_p = &new_loc;

    if(H5L_create_soft(target_path, new_loc_p, name, H5AC_dxpl_id, lcpl_id) < 0)
	HGOTO_ERROR(H5E_SYM, H5E_LINK, FAIL, "unable to create link")

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Lcreate_soft() */


/*-------------------------------------------------------------------------
 * Function:	H5Lcreate_hard
 *
 * Purpose:	Creates a hard link from NEW_NAME to CUR_NAME.
 *
 *		CUR_NAME must name an existing object.  CUR_NAME and
 *              NEW_NAME are interpreted relative to CUR_LOC_ID and
 *              NEW_LOC_ID, which are either file IDs or group IDs.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Robb Matzke
 *              Monday, April  6, 1998
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lcreate_hard(hid_t cur_loc_id, const char *cur_name,
	 hid_t new_loc_id, const char *new_name, hid_t lcpl_id)
{
    H5G_loc_t	cur_loc, *cur_loc_p;
    H5G_loc_t	new_loc, *new_loc_p;
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_API(H5Lcreate_hard, FAIL)
    H5TRACE5("e","isisi",cur_loc_id,cur_name,new_loc_id,new_name,lcpl_id);

    /* Check arguments */
    if(cur_loc_id != H5L_SAME_LOC && H5G_loc(cur_loc_id, &cur_loc) < 0)
	HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a location")
    if(new_loc_id != H5L_SAME_LOC && H5G_loc(new_loc_id, &new_loc) < 0)
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a location")
    if(!cur_name || !*cur_name)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no current name specified")
    if(!new_name || !*new_name)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no new name specified")

    if(lcpl_id != H5P_DEFAULT && (TRUE != H5P_isa_class(lcpl_id, H5P_LINK_CREATE)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a link creation property list")

    /* Set up current & new location pointers */
    cur_loc_p = &cur_loc;
    new_loc_p = &new_loc;
    if(cur_loc_id == H5L_SAME_LOC && new_loc_id == H5L_SAME_LOC)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "source and destination should not be both H5L_SAME_LOC")
    else if(cur_loc_id == H5L_SAME_LOC)
        cur_loc_p = new_loc_p;
    else if(new_loc_id == H5L_SAME_LOC)
   	new_loc_p = cur_loc_p;
    else if(cur_loc_p->oloc->file != new_loc_p->oloc->file)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "source and destination should be in the same file.")

    if(H5L_create_hard(cur_loc_p, cur_name, new_loc_p, new_name, H5AC_dxpl_id, lcpl_id) < 0)
	HGOTO_ERROR(H5E_SYM, H5E_LINK, FAIL, "unable to create link")

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Lcreate_hard() */


/*-------------------------------------------------------------------------
 * Function:	H5Lunlink
 *
 * Purpose:	Removes the specified NAME from the group graph and
 *		decrements the link count for the object to which NAME
 *		points.  If the link count reaches zero then all file-space
 *		associated with the object will be reclaimed (but if the
 *		object is open, then the reclamation of the file space is
 *		delayed until all handles to the object are closed).
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Robb Matzke
 *              Monday, April  6, 1998
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lunlink(hid_t loc_id, const char *name)
{
    H5G_loc_t	loc;
    herr_t      ret_value=SUCCEED;       /* Return value */

    FUNC_ENTER_API(H5Lunlink, FAIL)
    H5TRACE2("e","is",loc_id,name);

    /* Check arguments */
    if(H5G_loc(loc_id, &loc) < 0)
	HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a location")
    if(!name || !*name)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no name")

    /* Unlink */
    if(H5L_unlink(&loc, name, H5AC_dxpl_id) < 0)
	HGOTO_ERROR(H5E_SYM, H5E_CANTINIT, FAIL, "unable to unlink object")

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Lunlink() */


/*-------------------------------------------------------------------------
 * Function:	H5Lget_linkval
 *
 * Purpose:	Returns the link value of a link whose name is NAME.  For
 *              symbolic links, this is the path to which the link points,
 *              including the null terminator.  For user-defined links, it
 *              is the link buffer.
 *
 *              At most SIZE bytes are copied to the BUF result buffer.
 *
 * Return:	Success:	Non-negative with the link value in BUF.
 *
 * 		Failure:	Negative
 *
 * Programmer:	Robb Matzke
 *              Monday, April 13, 1998
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lget_linkval(hid_t loc_id, const char *name, size_t size, char *buf/*out*/)
{
    H5G_loc_t	loc;
    herr_t      ret_value = SUCCEED;       /* Return value */

    FUNC_ENTER_API(H5Lget_linkval, FAIL)
    H5TRACE4("e","iszx",loc_id,name,size,buf);

    /* Check arguments */
    if(H5G_loc(loc_id, &loc))
	HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a location")
    if(!name || !*name)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no name specified")

    /* Get the link value */
    if(H5L_linkval(&loc, name, size, buf, H5AC_ind_dxpl_id) < 0)
	HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "unable to get link value")

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5Lget_linkval() */


/*-------------------------------------------------------------------------
 * Function:	H5Lget_linkinfo
 *
 * Purpose:	Gets metadata for a link.
 *
 * Return:	Success:	Non-negative with information in LINKBUF
 *
 * 		Failure:	Negative
 *
 * Programmer:	James Laird
 *              Wednesday, June 21, 2006
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lget_linkinfo(hid_t loc_id, const char *name, H5L_linkinfo_t *linkbuf /*out*/)
{
    H5G_loc_t	loc;
    herr_t ret_value = SUCCEED;
    FUNC_ENTER_API(H5Lget_linkinfo, FAIL)
    H5TRACE3("e","isx",loc_id,name,linkbuf);

    /* Check arguments */
    if(H5G_loc(loc_id, &loc))
	HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a location")
    if(!name || !*name)
	HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no name specified")

    /* Get the creation time */
    if(H5L_get_linkinfo(&loc, name, linkbuf, H5AC_ind_dxpl_id) < 0)
	HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "unable to get link info")

done:
    FUNC_LEAVE_API(ret_value)
}



/*
 *-------------------------------------------------------------------------
 *-------------------------------------------------------------------------
 *   N O   A P I   F U N C T I O N S   B E Y O N D   T H I S   P O I N T
 *-------------------------------------------------------------------------
 *-------------------------------------------------------------------------
 */

/*-------------------------------------------------------------------------
 * Function:	H5L_link
 *
 * Purpose:	Creates a link from OBJ_ID to CUR_NAME.  See H5Llink() for
 *		full documentation.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	James Laird
 *              Tuesday, December 13, 2005
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5L_link(H5G_loc_t *new_loc, const char *new_name, H5G_loc_t *obj_loc,
           hid_t dxpl_id, hid_t lcpl_id)
{
    H5F_t *file = NULL;                 /* File link will be in */
    H5O_link_t lnk;                     /* Link to insert */
    herr_t ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5L_link)

    /* Check args */
    HDassert(new_loc);
    HDassert(obj_loc);
    HDassert(new_name && *new_name);

    /* Check that the object is not being hard linked into a different file */
    if(NULL == (file = H5G_insertion_file(new_loc, new_name, dxpl_id)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to identify insertion file")
    if(obj_loc->oloc->file != file)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "cannot link an object from another file")

    /* Construct link information for eventual insertion */
    lnk.type = H5L_LINK_HARD;
    lnk.u.hard.addr = obj_loc->oloc->addr;

    /* Create the link */
    if( H5L_create_real(new_loc, new_name, obj_loc->path, obj_loc->oloc->file, &lnk, dxpl_id, lcpl_id) <0) 
        HGOTO_ERROR(H5E_SYM, H5E_CANTINIT, FAIL, "unable to register new name for object")

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5L_link() */


/*-------------------------------------------------------------------------
 * Function:	H5L_link_cb
 *
 * Purpose:	Callback for creating a link to an object.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Quincey Koziol
 *              Monday, September 19, 2005
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5L_link_cb(H5G_loc_t *grp_loc/*in*/, const char *name, const H5O_link_t UNUSED *lnk,
    H5G_loc_t *obj_loc, void *_udata/*in,out*/)
{
    H5L_trav_ud3_t *udata = (H5L_trav_ud3_t *)_udata;   /* User data passed in */
    herr_t ret_value = SUCCEED;              /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5L_link_cb)

    /* Check if the name in this group resolved to a valid location */
    /* (which is not what we want) */
    if(obj_loc != NULL)
        HGOTO_ERROR(H5E_SYM, H5E_EXISTS, FAIL, "name already exists")

    /* Check for crossing file boundaries with a new hard link */
    if(udata->lnk->type == H5L_LINK_HARD) {
        /* Check that both objects are in same file */
        if(grp_loc->oloc->file->shared != udata->file->shared)
            HGOTO_ERROR(H5E_SYM, H5E_LINK, FAIL, "interfile hard links are not allowed")
    } /* end if */

    /* Set the link's name correctly */
    /* Casting away const OK -QAK */
    udata->lnk->name = name;

    /* Insert link into group */
    if(H5G_obj_insert(grp_loc->oloc, name, udata->lnk, (hbool_t)(udata->lnk->type == H5L_LINK_HARD ? TRUE : FALSE), udata->dxpl_id) < 0)
        HGOTO_ERROR(H5E_SYM, H5E_CANTINIT, FAIL, "unable to create new name/link for object")

    /* Set object's path if it has been passed in and is not set */
    if(udata->path != NULL && udata->path->user_path_r == NULL)
    {
      if(H5G_name_set(grp_loc->path, udata->path, name) < 0)
         HGOTO_ERROR(H5E_SYM, H5E_CANTINIT, FAIL, "cannot set name")
    }

done:
    if(ret_value < 0) {
        /* 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);
    } /* end if */

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5L_link_cb() */


/*-------------------------------------------------------------------------
 * Function:    H5L_create_real
 *
 * Purpose:     Creates a link at a path location
 *
 *              lnk should have linkclass-specific information already
 *              set, but this function will take care of setting
 *              creation time and name.
 *
 *              obj_path can be NULL if the object's path doesn't need to
 *              be set, and obj_file can be NULL if the object is not a
 *              hard link.
 *
 * Return:      Non-negative on success/Negative on failure
 *
 * Programmer:  Quincey Koziol
 *              Monday, December  5, 2005
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5L_create_real(H5G_loc_t *link_loc, const char *link_name, H5G_name_t *obj_path,
    H5F_t *obj_file, H5O_link_t *lnk, hid_t dxpl_id, hid_t lcpl_id)
{
    char *norm_link_name = NULL;        /* Pointer to normalized link name */
    unsigned target_flags = H5G_TARGET_NORMAL; /* Flags to pass to group traversal function */
    H5T_cset_t char_encoding = H5F_CRT_DEFAULT_CSET; /* Character encoding for link */
    H5P_genplist_t* lc_plist;           /* Link creation property list */
    H5L_trav_ud3_t udata;               /* User data for callback */
    herr_t ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5L_create_real)

    /* Check args */
    HDassert(link_loc);
    HDassert(link_name && *link_name);
    HDassert(lnk);

    /* Get normalized link name */
    if((norm_link_name = H5G_normalize(link_name)) == NULL)
        HGOTO_ERROR(H5E_SYM, H5E_BADVALUE, FAIL, "can't normalize name")

    /* Check for flags present in creation property list */
    if(lcpl_id != H5P_DEFAULT)
    {
      unsigned crt_intmd_group;

      if(NULL == (lc_plist = H5I_object(lcpl_id)))
          HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a property list")

      /* Get intermediate group creation property */
      if(H5P_get(lc_plist, H5L_CRT_INTERMEDIATE_GROUP_NAME, &crt_intmd_group) < 0)
          HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get property value for creating missing groups")

      if (crt_intmd_group > 0)
          target_flags |= H5G_CRT_INTMD_GROUP;

      /* Get character encoding property */
      if(H5P_get(lc_plist, H5P_CHAR_ENCODING_NAME, &char_encoding) < 0)
          HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get property value for character encoding")
    } /* end if */

    /* Fill in common data for the link struct */
    lnk->cset = char_encoding;
#ifdef H5_HAVE_GETTIMEOFDAY
    {
        struct timeval now_tv;

        HDgettimeofday(&now_tv, NULL);
        lnk->ctime = now_tv.tv_sec;
    }
#else /* H5_HAVE_GETTIMEOFDAY */
    lnk->ctime = HDtime(NULL);
#endif /* H5_HAVE_GETTIMEOFDAY */

    /* Set up user data
     * file is used to make sure that hard links don't cross files, and
     * should be NULL for other link types.
     * lnk is the link struct passed into this function.  At this point all
     * of its fields should be populated except for name, which is set when
     * inserting it in the callback.
     * dxpl_id is the dxpl ID that needs to be used during writes and reads.
     * path is a pointer to the path of the object being inserted if this is
     * a hard link; this is used to set the paths to objects when they are
     * created.  For other link types, this is NULL.
     */
    udata.file = obj_file;
    udata.lnk = lnk;
    udata.dxpl_id = dxpl_id;
    udata.path = obj_path;

    /* Traverse the destination path & create new link */
    if(H5G_traverse(link_loc, link_name, target_flags, H5L_link_cb, &udata, dxpl_id) < 0)
        HGOTO_ERROR(H5E_SYM, H5E_CANTINSERT, FAIL, "can't insert link")

done:
    /* Free the normalized path name */
    if(norm_link_name)
        H5MM_xfree(norm_link_name);

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5L_create_real() */


/*-------------------------------------------------------------------------
 * Function:	H5L_create_hard
 *
 * Purpose:	Creates a hard link from NEW_NAME to CUR_NAME.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Robb Matzke
 *              Monday, April  6, 1998
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5L_create_hard(H5G_loc_t *cur_loc, const char *cur_name,
    H5G_loc_t *link_loc, const char *link_name, hid_t dxpl_id, hid_t lcpl_id)
{
    char *norm_cur_name = NULL;	        /* Pointer to normalized current name */
    H5F_t *link_file = NULL;            /* Pointer to file to link to */
    H5O_link_t lnk;                     /* Link to insert */
    H5O_loc_t obj_oloc;                 /* Location of object to link to */
    herr_t ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5L_create_hard)

    /* Check args */
    HDassert(cur_loc);
    HDassert(link_loc);
    HDassert(cur_name && *cur_name);
    HDassert(link_name && *link_name);

    /* Get normalized copy of the current name */
    if((norm_cur_name = H5G_normalize(cur_name)) == NULL)
        HGOTO_ERROR(H5E_SYM, H5E_BADVALUE, FAIL, "can't normalize name")

    /* Set up link data specific to hard links */
    lnk.type = H5L_LINK_HARD;

    /* Get object location for object pointed to */
    if(H5G_obj_find(cur_loc, norm_cur_name, H5G_TARGET_NORMAL, NULL, &obj_oloc, dxpl_id) < 0)
        HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "source object not found")

    /* Construct link information for eventual insertion */
    lnk.u.hard.addr = obj_oloc.addr;

    /* Set destination's file information */
    link_file = obj_oloc.file;

    /* Create actual link to the object.  Pass in NULL for the path, since this
     * function shouldn't change an object's user path. */
    if(H5L_create_real(link_loc, link_name, NULL, link_file, &lnk, dxpl_id, lcpl_id) < 0)
        HGOTO_ERROR(H5E_SYM, H5E_CANTINIT, FAIL, "unable to register new name for object")

done:
    /* Free the normalized path name */
    if(norm_cur_name)
        H5MM_xfree(norm_cur_name);

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5L_create_hard() */


/*-------------------------------------------------------------------------
 * Function:	H5L_create_soft
 *
 * Purpose:	Creates a soft link from NEW_NAME to CUR_NAME.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Robb Matzke
 *              Monday, April  6, 1998
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5L_create_soft( const char *target_path, H5G_loc_t *link_loc,
                const char *link_name, hid_t dxpl_id, hid_t lcpl_id)
{
    char *norm_target = NULL;	        /* Pointer to normalized current name */
    H5O_link_t lnk;                     /* Link to insert */
    herr_t ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5L_create_soft)

    /* Check args */
    HDassert(link_loc);
    HDassert(target_path && *target_path);
    HDassert(link_name && *link_name);

    /* Get normalized copy of the link target */
    if((norm_target = H5G_normalize(target_path)) == NULL)
        HGOTO_ERROR(H5E_SYM, H5E_BADVALUE, FAIL, "can't normalize name")

    /* Set up link data specific to soft links */
    lnk.type = H5L_LINK_SOFT;
    lnk.u.soft.name = norm_target;

    /* Create actual link to the object */
    if(H5L_create_real(link_loc, link_name, NULL, NULL, &lnk, dxpl_id, lcpl_id) < 0)
        HGOTO_ERROR(H5E_SYM, H5E_CANTINIT, FAIL, "unable to register new name for object")

done:
    /* Free the normalized target name */
    if(norm_target)
        H5MM_xfree(norm_target);

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5L_create_soft() */


/*-------------------------------------------------------------------------
 * Function:	H5L_linkval_cb
 *
 * Purpose:	Callback for retrieving soft link value for an object.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Quincey Koziol
 *              Tuesday, September 20, 2005
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5L_linkval_cb(H5G_loc_t UNUSED *grp_loc/*in*/, const char UNUSED *name, const H5O_link_t *lnk,
    H5G_loc_t UNUSED *obj_loc, void *_udata/*in,out*/)
{
    H5L_trav_ud5_t *udata = (H5L_trav_ud5_t *)_udata;   /* User data passed in */
    herr_t ret_value = SUCCEED;              /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5L_linkval_cb)

    /* Check if the name in this group resolved to a valid link */
    if(lnk == NULL)
        HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "name doesn't exist")

    if(H5L_LINK_SOFT != lnk->type)
        HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "object is not a symbolic link")

    /* Copy to output buffer */
    if(udata->size > 0 && udata->buf) {
        HDstrncpy(udata->buf, lnk->u.soft.name, udata->size);
        if(HDstrlen(lnk->u.soft.name) >= udata->size)
            udata->buf[udata->size - 1] = '\0';
    } /* end if */

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 H5L_linkval_cb() */


/*-------------------------------------------------------------------------
 * Function:	H5L_linkval
 *
 * Purpose:	Returns the value of a symbolic link.
 *
 * Return:	Success:	Non-negative, with at most SIZE bytes of the
 *				link value copied into the BUF buffer.  If the
 *				link value is larger than SIZE characters
 *				counting the null terminator then the BUF
 *				result will not be null terminated.
 *
 *		Failure:	Negative
 *
 * Programmer:	Robb Matzke
 *              Monday, April 13, 1998
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5L_linkval(H5G_loc_t *loc, const char *name, size_t size, char *buf/*out*/, hid_t dxpl_id)
{
    H5L_trav_ud5_t udata;           /* User data for callback */
    herr_t ret_value = SUCCEED;       /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5L_linkval)

    /* Set up user data for retrieving information */
    udata.size = size;
    udata.buf = buf;

    /* Traverse the group hierarchy to locate the object to get info about */
    if(H5G_traverse(loc, name, H5G_TARGET_SLINK, H5L_linkval_cb, &udata, dxpl_id) < 0)
        HGOTO_ERROR(H5E_SYM, H5E_EXISTS, FAIL, "name doesn't exist")

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* H5L_linkval() */


/*-------------------------------------------------------------------------
 * Function:	H5L_unlink_cb
 *
 * Purpose:	Callback for unlinking an object.  This routine
 *              deletes the link
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Quincey Koziol
 *              Monday, September 19, 2005
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5L_unlink_cb(H5G_loc_t *grp_loc/*in*/, const char *name, const H5O_link_t UNUSED *lnk,
    H5G_loc_t *obj_loc, void *_udata/*in,out*/)
{
    H5L_trav_ud6_t *udata = (H5L_trav_ud6_t *)_udata;   /* User data passed in */
    herr_t ret_value = SUCCEED;              /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5L_unlink_cb)

    /* Check if the name in this group resolved to a valid link */
    if(obj_loc == NULL)
        HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "name doesn't exist")

    /* Check for removing '.' */
    if(lnk == NULL)
        HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "can't delete self")

    /* Remove the link from the group */
    if(H5G_loc_remove(grp_loc, name, obj_loc, udata->dxpl_id) < 0)
	HGOTO_ERROR(H5E_SYM, H5E_CANTDELETE, FAIL, "unable to unlink name from group")

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 H5L_unlink_cb() */


/*-------------------------------------------------------------------------
 * Function:	H5L_unlink
 *
 * Purpose:	Unlink a name from a group.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	Robb Matzke
 *              Thursday, September 17, 1998
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5L_unlink(H5G_loc_t *loc, const char *name, hid_t dxpl_id)
{
    H5L_trav_ud6_t      udata;                  /* User data for callback */
    char		*norm_name = NULL;	/* Pointer to normalized name */
    herr_t              ret_value = SUCCEED;    /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5L_unlink)

    /* Sanity check */
    HDassert(loc);
    HDassert(name && *name);

    /* Get normalized copy of the name */
    if((norm_name = H5G_normalize(name)) == NULL)
        HGOTO_ERROR(H5E_SYM, H5E_BADVALUE, FAIL, "can't normalize name")

    /* Set up user data for unlink operation */
    udata.dxpl_id = dxpl_id;

    if(H5G_traverse(loc, norm_name, H5G_TARGET_SLINK|H5G_TARGET_MOUNT, H5L_unlink_cb, &udata, dxpl_id) < 0)
        HGOTO_ERROR(H5E_SYM, H5E_EXISTS, FAIL, "name doesn't exist")

done:
    /* Free the normalized path name */
    if(norm_name)
        H5MM_xfree(norm_name);

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5L_unlink() */


/*-------------------------------------------------------------------------
 * Function:	H5L_move_dest_cb
 *
 * Purpose:	Second callback for moving and renaming an object.  This routine
 *              inserts a new link into the group returned by the traversal.
 *              It is called by H5L_move_cb.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	James Laird
 *              Monday, April 3, 2006
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5L_move_dest_cb(H5G_loc_t *grp_loc/*in*/, const char *name, const H5O_link_t *lnk,
    H5G_loc_t *obj_loc, void *_udata/*in,out*/)
{
    H5L_trav_ud10_t *udata = (H5L_trav_ud10_t *)_udata;   /* User data passed in */
    H5RS_str_t *dst_name_r = NULL;      /* Ref-counted version of dest name */
    herr_t ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5L_move_dest_cb)

    /* Make sure an object with this name doesn't already exist */
    if(obj_loc != NULL)
        HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "an object with that name already exists")

    /* Check for crossing file boundaries with a new hard link */
    if(udata->lnk->type == H5L_LINK_HARD) {
        /* Check that both objects are in same file */
        if(grp_loc->oloc->file->shared != udata->file->shared)
            HGOTO_ERROR(H5E_SYM, H5E_LINK, FAIL, "moving a link across files is not allowed")
    } /* end if */

    /* Give the object its new name */
    /* Casting away const okay -JML */
    udata->lnk->name = H5MM_xfree(udata->lnk->name);
    udata->lnk->name=name;

    /* Insert the link into the group */
    if(H5G_obj_insert(grp_loc->oloc, name, udata->lnk, (hbool_t)(udata->lnk->type == H5L_LINK_HARD ? TRUE : FALSE), udata->dxpl_id) < 0)
        HGOTO_ERROR(H5E_SYM, H5E_CANTINIT, FAIL, "unable to create new name/link for object")

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);
    if(dst_name_r)
        H5RS_decr(dst_name_r);

    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5L_move_dest_cb() */


/*-------------------------------------------------------------------------
 * Function:	H5L_move_cb
 *
 * Purpose:	Callback for moving and renaming an object.  This routine
 *              replaces the names of open objects with the moved object
 *              in the path
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	James Laird
 *              Friday, April 3, 2006
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5L_move_cb(H5G_loc_t *grp_loc/*in*/, const char *name, const H5O_link_t *lnk,
    H5G_loc_t *obj_loc, void *_udata/*in,out*/)
{
    H5L_trav_ud4_t *udata = (H5L_trav_ud4_t *)_udata;   /* User data passed in */
    H5L_trav_ud10_t udata_out;    /* User data for H5L_move_dest_cb traversal */
    H5G_obj_t type;               /* Type of object being moved */
    H5RS_str_t *dst_name_r = NULL;      /* Ref-counted version of dest name */
    char * orig_name = NULL;            /* The name of the link in this group */
    herr_t ret_value = SUCCEED;              /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5L_move_cb)

    /* Check if the name in this group resolved to a valid link */
    if(obj_loc == NULL)
        HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "name doesn't exist")

    /* Check for operations on '.' */
    if(lnk == NULL)
        HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "the name of a link must be supplied to move or copy")

    /* Get object type */
    switch(lnk->type) {
        case H5L_LINK_HARD:
          if(H5G_UNKNOWN == (type = H5O_obj_type(obj_loc->oloc, udata->dxpl_id)))
                HGOTO_ERROR(H5E_SYM, H5E_CANTGET, FAIL, "unable to get object type to move")
            break;

        case H5L_LINK_SOFT:
            type = H5G_LINK;
            break;

        default:
            HGOTO_ERROR(H5E_SYM, H5E_BADVALUE, FAIL, "unrecognized link type")
    } /* end switch */

    /* Set up user data for move_dest_cb */
    if((udata_out.lnk = H5O_link_copy(lnk, NULL, 0)) == NULL)
        HGOTO_ERROR(H5E_SYM, H5E_CANTCOPY, FAIL, "unable to copy link to be moved");
    udata_out.lnk->cset = udata->cset;
    udata_out.file = grp_loc->oloc->file;
    udata_out.dxpl_id = udata->dxpl_id;

    /* Remember the link's original name (in case it's changed by H5G_name_replace) */
    orig_name = H5MM_xstrdup(name);

    /* Insert the link into its new location */
    if(H5G_traverse(udata->dst_loc, udata->dst_name, H5G_TARGET_NORMAL, H5L_move_dest_cb, &udata_out, udata->dxpl_id) < 0)
	HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "unable to follow symbolic link")

    /* If this is a move and not a copy operation, change the object's name and remove the old link */
    if(!udata->copy)
    {
        /* Fix names up */
        dst_name_r = H5RS_wrap(udata->dst_name);
        HDassert(dst_name_r);
        if(H5G_name_replace(type, obj_loc, dst_name_r, udata->dst_loc, H5G_NAME_MOVE) < 0)
            HGOTO_ERROR(H5E_SYM, H5E_CANTINIT, FAIL, "unable to replace name ")

        /* Remove the old link */
        if(H5G_obj_remove(grp_loc->oloc, orig_name, &type, udata->dxpl_id) < 0)
            HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "unable to remove old name")
    }

done:
        /* Cleanup */
    if(orig_name)
        H5MM_xfree(orig_name);

    /* 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 H5L_move_cb() */


/*-------------------------------------------------------------------------
 * Function:	H5L_move
 *
 * Purpose:	Atomically move or copy a link.
 *
 *              Creates a copy of a link in a new destination with a new name.
 *              SRC_LOC and SRC_NAME together define the link's original
 *              location, while DST_LOC and DST_NAME together define its
 *              final location.
 *
 *              If copy_flag is FALSE, the original link is removed
 *              (effectively moving the link).
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	James Laird
 *              Monday, May 1, 2006
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5L_move(H5G_loc_t *src_loc, const char *src_name, H5G_loc_t *dst_loc,
                const char *dst_name, hbool_t copy_flag, hid_t lcpl_id, hid_t dxpl_id)
{
    unsigned target_flags = H5G_TARGET_MOUNT|H5G_TARGET_SLINK; /* Flags to pass to group traversal function */
    H5T_cset_t char_encoding = H5F_CRT_DEFAULT_CSET; /* Character encoding for link */
    H5P_genplist_t* lc_plist;           /* Link creation property list */
    H5L_trav_ud4_t      udata;          /* User data for traversal */
    herr_t              ret_value = SUCCEED;      /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5L_move)

    /* Sanity check */
    HDassert(src_loc);
    HDassert(dst_loc);
    HDassert(src_name && *src_name);
    HDassert(dst_name && *dst_name);

    /* Check for flags present in creation property list */
    if(lcpl_id != H5P_DEFAULT)
    {
      unsigned crt_intmd_group;

      if(NULL == (lc_plist = H5I_object(lcpl_id)))
          HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a property list")

      /* Get intermediate group creation property */
      if(H5P_get(lc_plist, H5L_CRT_INTERMEDIATE_GROUP_NAME, &crt_intmd_group) < 
0)
          HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get property value for creating missing groups")

      if (crt_intmd_group > 0)
          target_flags |= H5G_CRT_INTMD_GROUP;

      /* Get character encoding property */
      if(H5P_get(lc_plist, H5P_CHAR_ENCODING_NAME, &char_encoding) < 0)
          HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get property value for character encoding")
    } /* end if */

    /* Set up user data */
    udata.dst_loc = dst_loc;
    udata.dst_name= dst_name;
    udata.cset = char_encoding;
    udata.copy = copy_flag;
    udata.dxpl_id = dxpl_id;

    /* Do the move */
    if(H5G_traverse(src_loc, src_name, H5G_TARGET_MOUNT|H5G_TARGET_SLINK, H5L_move_cb, &udata, dxpl_id) < 0)
	HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "unable to find link")    

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5L_move() */


/*-------------------------------------------------------------------------
 * Function:	H5L_get_lcpl_cb
 *
 * Purpose:	Callback for getting a link's creation property list.  This
 *              routine gets properties from the link and sets them on the
 *              copy of the default property list passed in.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	James Laird
 *              Friday, January 27, 2006
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5L_get_lcpl_cb(H5G_loc_t UNUSED *grp_loc/*in*/, const char UNUSED *name, const H5O_link_t *lnk,
    H5G_loc_t UNUSED *obj_loc, void *_udata/*in,out*/)
{
    H5L_trav_ud8_t *udata = (H5L_trav_ud8_t *)_udata;   /* User data passed in */
    herr_t ret_value = SUCCEED;              /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5L_get_lcpl_cb)

    /* Check if the name in this group resolved to a valid link */
    if(lnk == NULL)
        HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "name doesn't exist")

    /* Set appropriate character encoding */
    if(H5P_set(udata->lcpl, H5P_CHAR_ENCODING_NAME, &(lnk->cset)) < 0)
          HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't set property value for character encoding")

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 H5L_get_lcpl_cb() */


/*-------------------------------------------------------------------------
 * Function:	H5L_get_create_plist
 *
 * Purpose:	Returns a copy of the link's creation property list given
 *              given a link's location and name.
 *
 * Return:	Success:	ID of the property list
 *
 * 		Failure:	Negative
 *
 * Programmer:	James Laird
 *              Friday, January 27, 2006
 *
 *-------------------------------------------------------------------------
 */
hid_t H5L_get_create_plist(H5G_loc_t *loc, const char* name)
{
    H5P_genplist_t      *plist;                 /* Default property list */
    H5P_genplist_t      *plist_copy;            /* Copy of list to be modified */
    hid_t                lcpl_id=-1;
    H5L_trav_ud8_t       udata;                 /* User data for traversal */
    char		*norm_name = NULL;	/* Pointer to normalized name */
    hid_t                ret_value;

    FUNC_ENTER_NOAPI(H5L_get_create_plist, FAIL)

    /* Check arguments */
    HDassert(loc);
    HDassert(name && *name);

    /* Get normalized copy of the name */
    if((norm_name = H5G_normalize(name)) == NULL)
        HGOTO_ERROR(H5E_SYM, H5E_BADVALUE, FAIL, "can't normalize name")

    /* Get copy of default lcpl */
    if (NULL==(plist=H5I_object(H5P_LST_LINK_CREATE_g)))
        HGOTO_ERROR(H5E_PLIST, H5E_BADTYPE, FAIL, "can't get default LCPL")
    if((lcpl_id=H5P_copy_plist(plist)) < 0)
	HGOTO_ERROR(H5E_PLIST, H5E_CANTINIT, FAIL, "unable to copy attribute creation properties")
    if (NULL==(plist_copy=H5I_object(lcpl_id)))
        HGOTO_ERROR(H5E_PLIST, H5E_BADTYPE, FAIL, "can't get copy of LCPL")

    /* Set up user data */
    udata.lcpl = plist_copy;

    if(H5G_traverse(loc, norm_name, H5G_TARGET_SLINK|H5G_TARGET_MOUNT, H5L_get_lcpl_cb, &udata, H5AC_dxpl_id) < 0)
        HGOTO_ERROR(H5E_SYM, H5E_EXISTS, FAIL, "name doesn't exist")

    ret_value = lcpl_id;

done:
    /* Free the normalized path name */
    if(norm_name)
        H5MM_xfree(norm_name);
    /* If we've created a new lcpl, close it */
    if(ret_value <0 && lcpl_id >= 0)
        H5P_close(H5I_object(lcpl_id));
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5L_get_create_plist */


/*-------------------------------------------------------------------------
 * Function:	H5L_get_linfo_cb
 *
 * Purpose:	Callback for retrieving a link's metadata
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	James Laird
 *              Monday, April 17 2006
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5L_get_linfo_cb(H5G_loc_t UNUSED *grp_loc/*in*/, const char UNUSED *name, const H5O_link_t *lnk,
    H5G_loc_t UNUSED *obj_loc, void *_udata/*in,out*/)
{
    H5L_trav_ud1_t *udata = (H5L_trav_ud1_t *)_udata;   /* User data passed in */
    H5L_linkinfo_t *linfo = udata->linfo;
    herr_t ret_value = SUCCEED;              /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5L_get_linfo_cb)

    /* Check if the name in this group resolved to a valid link */
    if(lnk == NULL)
        HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "name doesn't exist")

    /* Get information from the link */
    linfo->cset = lnk->cset;
    linfo->ctime = lnk->ctime;
    linfo->linkclass = lnk->type;
    
    switch(lnk->type)
    {
      case H5L_LINK_HARD:
          linfo->u.objno = lnk->u.hard.addr;
          break;

      case H5L_LINK_SOFT:
          linfo->u.link_size = HDstrlen(lnk->u.soft.name) + 1; /*count the null terminator*/
          break;

      default:
          HGOTO_ERROR(H5E_SYM, H5E_BADTYPE, FAIL, "unknown link type");
    }

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 H5L_get_linfo_cb() */


/*-------------------------------------------------------------------------
 * Function:	H5L_get_linkinfo
 *
 * Purpose:	Returns metadata about a link.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 * Programmer:	James Laird
 *              Monday, April 17 2006
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5L_get_linkinfo(H5G_loc_t *loc, const char *name, H5L_linkinfo_t *linkbuf/*out*/, hid_t dxpl_id)
{
    H5L_trav_ud1_t udata;
    herr_t ret_value = SUCCEED;       /* Return value */

    FUNC_ENTER_NOAPI_NOINIT(H5L_get_linkinfo)

    udata.linfo = linkbuf;
    udata.dxpl_id = dxpl_id;

    /* Traverse the group hierarchy to locate the object to get info about */
    if(H5G_traverse(loc, name, H5G_TARGET_SLINK, H5L_get_linfo_cb, &udata, dxpl_id) < 0)
        HGOTO_ERROR(H5E_SYM, H5E_EXISTS, FAIL, "name doesn't exist")

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* H5L_get_linkinfo() */


/*-------------------------------------------------------------------------
 * Function:	H5L get_default_lcpl
 *
 * Purpose:	Accessor for the default Link Creation Property List
 *
 * Return:	Success:	ID of the deafult lcpl
 *
 * 		Failure:	Negative
 *
 * Programmer:	James Laird
 *              Tuesday, July 4, 2006
 *
 *-------------------------------------------------------------------------
 */
hid_t
H5L_get_default_lcpl()
{
    hid_t ret_value = FAIL;       /* Return value */

    FUNC_ENTER_NOAPI(H5L_get_default_lcpl, FAIL)

    ret_value = H5P_LINK_CREATE_DEFAULT;

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* H5L_get_default_lcpl */