/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * 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://www.hdfgroup.org/licenses.               *
 * If you do not have access to either file, you may request a copy from     *
 * help@hdfgroup.org.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*-------------------------------------------------------------------------
 *
 * Created:		H5Gname.c
 *
 * Purpose:		Functions for handling group hierarchy paths.
 *
 *-------------------------------------------------------------------------
 */

/****************/
/* 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 "H5Iprivate.h"  /* IDs			  		*/
#include "H5Lprivate.h"  /* Links                                */
#include "H5MMprivate.h" /* Memory wrappers			*/

#include "H5VLnative_private.h" /* Native VOL connector                     */

/****************/
/* Local Macros */
/****************/

/******************/
/* Local Typedefs */
/******************/

/* Struct used by change name callback function */
typedef struct H5G_names_t {
    H5G_names_op_t op;              /* Operation performed on file */
    H5F_t         *src_file;        /* Top file in src location's mounted file hier. */
    H5RS_str_t    *src_full_path_r; /* Source location's full path */
    H5F_t         *dst_file;        /* Destination location's file */
    H5RS_str_t    *dst_full_path_r; /* Destination location's full path */
} H5G_names_t;

/* Info to pass to the iteration function when building name */
typedef struct H5G_gnba_iter_t {
    /* In */
    const H5O_loc_t *loc; /* The location of the object we're looking for */

    /* Out */
    char *path; /* Name of the object */
} H5G_gnba_iter_t;

/********************/
/* Package Typedefs */
/********************/

/********************/
/* Local Prototypes */
/********************/

static htri_t      H5G__common_path(const H5RS_str_t *fullpath_r, const H5RS_str_t *prefix_r);
static H5RS_str_t *H5G__build_fullpath(const char *prefix, const char *name);
static herr_t      H5G__name_move_path(H5RS_str_t **path_r_ptr, const char *full_suffix, const char *src_path,
                                       const char *dst_path);
static int         H5G__name_replace_cb(void *obj_ptr, hid_t obj_id, void *key);

/*********************/
/* Package Variables */
/*********************/

/*****************************/
/* Library Private Variables */
/*****************************/

/*******************/
/* Local Variables */
/*******************/

/*-------------------------------------------------------------------------
 * Function:	H5G__component
 *
 * Purpose:	Returns the pointer to the first component of the
 *		specified name by skipping leading slashes.  Returns
 *		the size in characters of the component through SIZE_P not
 *		counting leading slashes or the null terminator.
 *
 * Return:	Success:	Ptr into NAME.
 *
 *		Failure:	Ptr to the null terminator of NAME.
 *
 *-------------------------------------------------------------------------
 */
const char *
H5G__component(const char *name, size_t *size_p)
{
    FUNC_ENTER_PACKAGE_NOERR

    assert(name);

    while ('/' == *name)
        name++;
    if (size_p)
        *size_p = strcspn(name, "/");

    FUNC_LEAVE_NOAPI(name)
} /* end H5G__component() */

/*-------------------------------------------------------------------------
 * Function:	H5G_normalize
 *
 * Purpose:	Returns a pointer to a new string which has duplicate and
 *              trailing slashes removed from it.
 *
 * Return:	Success:	Ptr to normalized name.
 *		Failure:	NULL
 *
 *-------------------------------------------------------------------------
 */
char *
H5G_normalize(const char *name)
{
    char  *norm;             /* Pointer to the normalized string */
    size_t s, d;             /* Positions within the strings */
    bool   last_slash;       /* Flag to indicate last character was a slash */
    char  *ret_value = NULL; /* Return value */

    FUNC_ENTER_NOAPI_NOINIT

    /* Sanity check */
    assert(name);

    /* Duplicate the name, to return */
    if (NULL == (norm = H5MM_strdup(name)))
        HGOTO_ERROR(H5E_SYM, H5E_CANTALLOC, NULL, "memory allocation failed for normalized string");

    /* Walk through the characters, omitting duplicated '/'s */
    s = d      = 0;
    last_slash = false;
    while (name[s] != '\0') {
        if (name[s] == '/')
            if (last_slash)
                ;
            else {
                norm[d++]  = name[s];
                last_slash = true;
            } /* end else */
        else {
            norm[d++]  = name[s];
            last_slash = false;
        } /* end else */
        s++;
    } /* end while */

    /* Terminate normalized string */
    norm[d] = '\0';

    /* Check for final '/' on normalized name & eliminate it */
    if (d > 1 && last_slash)
        norm[d - 1] = '\0';

    /* Set return value */
    ret_value = norm;

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

/*-------------------------------------------------------------------------
 * Function: H5G__common_path
 *
 * Purpose: Determine if one path is a valid prefix of another path
 *
 * Return: true for valid prefix, false for not a valid prefix, FAIL
 *              on error
 *
 *
 *-------------------------------------------------------------------------
 */
static htri_t
H5G__common_path(const H5RS_str_t *fullpath_r, const H5RS_str_t *prefix_r)
{
    const char *fullpath;          /* Pointer to actual fullpath string */
    const char *prefix;            /* Pointer to actual prefix string */
    size_t      nchars1, nchars2;  /* Number of characters in components */
    htri_t      ret_value = false; /* Return value */

    FUNC_ENTER_PACKAGE_NOERR

    /* Get component of each name */
    fullpath = H5RS_get_str(fullpath_r);
    assert(fullpath);
    fullpath = H5G__component(fullpath, &nchars1);
    assert(fullpath);
    prefix = H5RS_get_str(prefix_r);
    assert(prefix);
    prefix = H5G__component(prefix, &nchars2);
    assert(prefix);

    /* Check if we have a real string for each component */
    while (*fullpath && *prefix) {
        /* Check that the components we found are the same length */
        if (nchars1 == nchars2) {
            /* Check that the two components are equal */
            if (strncmp(fullpath, prefix, nchars1) == 0) {
                /* Advance the pointers in the names */
                fullpath += nchars1;
                prefix += nchars2;

                /* Get next component of each name */
                fullpath = H5G__component(fullpath, &nchars1);
                assert(fullpath);
                prefix = H5G__component(prefix, &nchars2);
                assert(prefix);
            } /* end if */
            else
                HGOTO_DONE(false);
        } /* end if */
        else
            HGOTO_DONE(false);
    } /* end while */

    /* If we reached the end of the prefix path to check, it must be a valid prefix */
    if (*prefix == '\0')
        ret_value = true;

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

/*-------------------------------------------------------------------------
 * Function: H5G__build_fullpath
 *
 * Purpose: Build a full path from a prefix & base pair of strings
 *
 * Return: Pointer to reference counted string on success, NULL on error
 *
 *-------------------------------------------------------------------------
 */
static H5RS_str_t *
H5G__build_fullpath(const char *prefix, const char *name)
{
    H5RS_str_t *ret_value = NULL; /* Return value */

    FUNC_ENTER_PACKAGE

    /* Sanity check */
    assert(prefix);
    assert(name);

    /* Create full path */
    if (NULL == (ret_value = H5RS_create(prefix)))
        HGOTO_ERROR(H5E_SYM, H5E_CANTCREATE, NULL, "can't create ref-counted string");
    if (prefix[strlen(prefix) - 1] != '/')
        H5RS_aputc(ret_value, '/'); /* Add separator, if the prefix doesn't end in one */
    H5RS_acat(ret_value, name);

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

/*-------------------------------------------------------------------------
 * Function:	H5G_build_fullpath_refstr_str
 *
 * Purpose:     Append an object path to an existing ref-counted path
 *
 * Return:	Success:	Non-NULL, combined path
 *		Failure:	NULL
 *
 *-------------------------------------------------------------------------
 */
H5RS_str_t *
H5G_build_fullpath_refstr_str(H5RS_str_t *prefix_r, const char *name)
{
    const char *prefix;           /* Pointer to raw string for path */
    H5RS_str_t *ret_value = NULL; /* Return value */

    FUNC_ENTER_NOAPI_NOINIT_NOERR

    assert(prefix_r);
    assert(name);

    /* Get the raw string for the user path */
    prefix = H5RS_get_str(prefix_r);
    assert(prefix);

    /* Create reference counted string for path */
    ret_value = H5G__build_fullpath(prefix, name);

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

/*-------------------------------------------------------------------------
 * Function:    H5G__name_init
 *
 * Purpose:     Set the initial path for a group hierarchy name
 *
 * Return:	Success:	Non-negative
 *		Failure:	Negative
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5G__name_init(H5G_name_t *name, const char *path)
{
    FUNC_ENTER_PACKAGE_NOERR

    /* Check arguments */
    assert(name);

    /* Set the initial paths for a name object */
    name->full_path_r = H5RS_create(path);
    assert(name->full_path_r);
    name->user_path_r = H5RS_create(path);
    assert(name->user_path_r);
    name->obj_hidden = 0;

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* end H5G__name_init() */

/*-------------------------------------------------------------------------
 * Function:	H5G_name_set
 *
 * Purpose:     Set the name of a symbol entry OBJ, located at LOC
 *
 * Return:	Success:	Non-negative
 *		Failure:	Negative
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5G_name_set(const H5G_name_t *loc, H5G_name_t *obj, const char *name)
{
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_NOAPI(FAIL)

    assert(loc);
    assert(obj);
    assert(name);

    /* Free & reset the object's previous paths info (if they exist) */
    H5G_name_free(obj);

    /* Create the object's full path, if a full path exists in the location */
    if (loc->full_path_r) {
        /* Go build the new full path */
        if ((obj->full_path_r = H5G_build_fullpath_refstr_str(loc->full_path_r, name)) == NULL)
            HGOTO_ERROR(H5E_SYM, H5E_PATH, FAIL, "can't build user path name");
    } /* end if */

    /* Create the object's user path, if a user path exists in the location */
    if (loc->user_path_r) {
        /* Go build the new user path */
        if ((obj->user_path_r = H5G_build_fullpath_refstr_str(loc->user_path_r, name)) == NULL)
            HGOTO_ERROR(H5E_SYM, H5E_PATH, FAIL, "can't build user path name");
    } /* end if */

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

/*-------------------------------------------------------------------------
 * Function:    H5G_name_copy
 *
 * Purpose:     Do a copy of group hier. names
 *
 * Return:	Success:	Non-negative
 *		Failure:	Negative
 *
 * Notes:       'depth' parameter determines how much of the group entry
 *              structure we want to copy.  The depths are:
 *                  H5_COPY_SHALLOW - Copy all the fields from the source
 *                      to the destination, including the user path and
 *                      canonical path. (Destination "takes ownership" of
 *                      user and canonical paths)
 *                  H5_COPY_DEEP - Copy all the fields from the source to
 *                      the destination, deep copying the user and canonical
 *                      paths.
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5G_name_copy(H5G_name_t *dst, const H5G_name_t *src, H5_copy_depth_t depth)
{
    FUNC_ENTER_NOAPI_NOINIT_NOERR

    /* Check arguments */
    assert(src);
    assert(dst);
#if defined(H5_USING_MEMCHECKER) || !defined(NDEBUG)
    assert(dst->full_path_r == NULL);
    assert(dst->user_path_r == NULL);
#endif /* H5_USING_MEMCHECKER */
    assert(depth == H5_COPY_SHALLOW || depth == H5_COPY_DEEP);

    /* Copy the top level information */
    H5MM_memcpy(dst, src, sizeof(H5G_name_t));

    /* Deep copy the names */
    if (depth == H5_COPY_DEEP) {
        dst->full_path_r = H5RS_dup(src->full_path_r);
        dst->user_path_r = H5RS_dup(src->user_path_r);
    }
    else {
        H5_GCC_CLANG_DIAG_OFF("cast-qual")
        H5G_name_reset((H5G_name_t *)src);
        H5_GCC_CLANG_DIAG_ON("cast-qual")
    }

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* end H5G_name_copy() */

/*-------------------------------------------------------------------------
 * Function:    H5G_get_name
 *
 * Purpose:     Gets a name of an object from its ID.
 *
 * Notes:	Internal routine for H5Iget_name().

 * Return:	Success:	Non-negative, length of name
 *		Failure:	Negative
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5G_get_name(const H5G_loc_t *loc, char *name /*out*/, size_t size, size_t *name_len, bool *cached)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_NOAPI(FAIL)

    /* Sanity check */
    assert(loc);

    /* If the user path is available and it's not "hidden", use it */
    if (loc->path->user_path_r != NULL && loc->path->obj_hidden == 0) {
        size_t len; /* Length of object's name */

        len = H5RS_len(loc->path->user_path_r);

        if (name) {
            strncpy(name, H5RS_get_str(loc->path->user_path_r), MIN((len + 1), size));
            if (len >= size)
                name[size - 1] = '\0';
        } /* end if */

        /* Set name length, if requested */
        if (name_len)
            *name_len = len;

        /* Indicate that the name is cached, if requested */
        /* (Currently only used for testing - QAK, 2010/07/26) */
        if (cached)
            *cached = true;
    } /* end if */
    else if (!loc->path->obj_hidden) {
        /* Search for name of object */
        if (H5G_get_name_by_addr(loc->oloc->file, loc->oloc, name, size, name_len) < 0)
            HGOTO_ERROR(H5E_SYM, H5E_CANTGET, FAIL, "can't determine name");

        /* Indicate that the name is _not_ cached, if requested */
        /* (Currently only used for testing - QAK, 2010/07/26) */
        if (cached)
            *cached = false;
    } /* end else */

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

/*-------------------------------------------------------------------------
 * Function:	H5G_name_reset
 *
 * Purpose:	Reset a group hierarchy name to an empty state
 *
 * Return:	Success:	Non-negative
 *		Failure:	Negative
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5G_name_reset(H5G_name_t *name)
{
    FUNC_ENTER_NOAPI_NOINIT_NOERR

    /* Check arguments */
    assert(name);

    /* Clear the group hier. name to an empty state */
    memset(name, 0, sizeof(H5G_name_t));

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* end H5G_name_reset() */

/*-------------------------------------------------------------------------
 * Function:	H5G_name_free
 *
 * Purpose:	Free the 'ID to name' buffers.
 *
 * Return:	Success
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5G_name_free(H5G_name_t *name)
{
    FUNC_ENTER_NOAPI_NOINIT_NOERR

    /* Check args */
    assert(name);

    if (name->full_path_r) {
        H5RS_decr(name->full_path_r);
        name->full_path_r = NULL;
    } /* end if */
    if (name->user_path_r) {
        H5RS_decr(name->user_path_r);
        name->user_path_r = NULL;
    } /* end if */
    name->obj_hidden = 0;

    FUNC_LEAVE_NOAPI(SUCCEED)
} /* end H5G_name_free() */

/*-------------------------------------------------------------------------
 * Function:    H5G__name_move_path
 *
 * Purpose:     Update a user or canonical path after an object moves
 *
 * Return:	Success:	Non-negative
 *		Failure:	Negative
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5G__name_move_path(H5RS_str_t **path_r_ptr, const char *full_suffix, const char *src_path,
                    const char *dst_path)
{
    const char *path;                /* Path to update */
    size_t      path_len;            /* Length of path */
    size_t      full_suffix_len;     /* Length of full suffix */
    herr_t      ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_PACKAGE

    /* Check arguments */
    assert(path_r_ptr && *path_r_ptr);
    assert(full_suffix);
    assert(src_path);
    assert(dst_path);

    /* Get pointer to path to update */
    path = H5RS_get_str(*path_r_ptr);
    assert(path);

    /* Check if path needs to be updated */
    full_suffix_len = strlen(full_suffix);
    path_len        = strlen(path);
    if (full_suffix_len < path_len) {
        const char *dst_suffix;        /* Destination suffix that changes */
        const char *src_suffix;        /* Source suffix that changes */
        size_t      path_prefix_len;   /* Length of path prefix */
        const char *path_prefix2;      /* 2nd prefix for path */
        size_t      path_prefix2_len;  /* Length of 2nd path prefix */
        size_t      common_prefix_len; /* Length of common prefix */
        H5RS_str_t *rs;                /* Ref-counted string for new path */

        /* Compute path prefix before full suffix */
        path_prefix_len = path_len - full_suffix_len;

        /* Determine the common prefix for src & dst paths */
        common_prefix_len = 0;
        /* Find first character that is different */
        while (*(src_path + common_prefix_len) == *(dst_path + common_prefix_len))
            common_prefix_len++;
        /* Back up to previous '/' */
        while (*(src_path + common_prefix_len) != '/')
            common_prefix_len--;
        /* Include '/' */
        common_prefix_len++;

        /* Determine source suffix */
        src_suffix = src_path + (common_prefix_len - 1);

        /* Determine destination suffix */
        dst_suffix = dst_path + (common_prefix_len - 1);

        /* Compute path prefix before src suffix */
        path_prefix2     = path;
        path_prefix2_len = path_prefix_len - strlen(src_suffix);

        /* Allocate new ref-counted string */
        if (NULL == (rs = H5RS_create(NULL)))
            HGOTO_ERROR(H5E_SYM, H5E_CANTCREATE, FAIL, "can't create ref-counted string");

        /* Create the new path */
        if (path_prefix2_len > 0)
            H5RS_ancat(rs, path_prefix2, path_prefix2_len);
        H5RS_acat(rs, dst_suffix);
        if (full_suffix_len > 0)
            H5RS_acat(rs, full_suffix);

        /* Release previous path */
        H5RS_decr(*path_r_ptr);

        /* Take ownership of the new full path */
        *path_r_ptr = rs;
    } /* end if */

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

/*-------------------------------------------------------------------------
 * Function: H5G__name_replace_cb
 *
 * Purpose: H5I_iterate callback function to replace group entry names
 *
 * Return: Success: 0, Failure: -1
 *
 *-------------------------------------------------------------------------
 */
static int
H5G__name_replace_cb(void *obj_ptr, hid_t obj_id, void *key)
{
    const H5G_names_t *names = (const H5G_names_t *)key; /* Get operation's information */
    H5O_loc_t         *oloc;         /* Object location for object that the ID refers to */
    H5G_name_t        *obj_path;     /* Pointer to group hier. path for obj */
    H5F_t             *top_obj_file; /* Top file in object's mounted file hier. */
    bool   obj_in_child = false;     /* Flag to indicate that the object is in the child mount hier. */
    herr_t ret_value    = SUCCEED;   /* Return value */

    FUNC_ENTER_PACKAGE

    assert(obj_ptr);

    /* Get the symbol table entry */
    switch (H5I_get_type(obj_id)) {
        case H5I_GROUP:
            oloc     = H5G_oloc((H5G_t *)obj_ptr);
            obj_path = H5G_nameof((H5G_t *)obj_ptr);
            break;

        case H5I_DATASET:
            oloc     = H5D_oloc((H5D_t *)obj_ptr);
            obj_path = H5D_nameof((H5D_t *)obj_ptr);
            break;

        case H5I_DATATYPE:
            /* Avoid non-named datatypes */
            if (!H5T_is_named((H5T_t *)obj_ptr))
                HGOTO_DONE(SUCCEED); /* Do not exit search over IDs */

            oloc     = H5T_oloc((H5T_t *)obj_ptr);
            obj_path = H5T_nameof((H5T_t *)obj_ptr);
            break;

        case H5I_MAP:
            HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "maps not supported in native VOL connector");

        case H5I_UNINIT:
        case H5I_BADID:
        case H5I_FILE:
        case H5I_DATASPACE:
        case H5I_ATTR:
        case H5I_VFL:
        case H5I_VOL:
        case H5I_GENPROP_CLS:
        case H5I_GENPROP_LST:
        case H5I_ERROR_CLASS:
        case H5I_ERROR_MSG:
        case H5I_ERROR_STACK:
        case H5I_SPACE_SEL_ITER:
        case H5I_EVENTSET:
        case H5I_NTYPES:
        default:
            HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "unknown data object");
    } /* end switch */
    assert(oloc);
    assert(obj_path);

    /* Check if the object has a full path still */
    if (!obj_path->full_path_r)
        HGOTO_DONE(SUCCEED); /* No need to look at object, it's path is already invalid */

    /* Find the top file in object's mount hier. */
    if (H5F_PARENT(oloc->file)) {
        /* Check if object is in child file (for mount & unmount operations) */
        if (names->dst_file && H5F_SAME_SHARED(oloc->file, names->dst_file))
            obj_in_child = true;

        /* Find the "top" file in the chain of mounted files */
        top_obj_file = H5F_PARENT(oloc->file);
        while (H5F_PARENT(top_obj_file) != NULL) {
            /* Check if object is in child mount hier. (for mount & unmount operations) */
            if (names->dst_file && H5F_SAME_SHARED(top_obj_file, names->dst_file))
                obj_in_child = true;

            top_obj_file = H5F_PARENT(top_obj_file);
        } /* end while */
    }     /* end if */
    else
        top_obj_file = oloc->file;

    /* Check if object is in top of child mount hier. (for mount & unmount operations) */
    if (names->dst_file && H5F_SAME_SHARED(top_obj_file, names->dst_file))
        obj_in_child = true;

    /* Check if the object is in same file mount hier. */
    if (!H5F_SAME_SHARED(top_obj_file, names->src_file))
        HGOTO_DONE(SUCCEED); /* No need to look at object, it's path is already invalid */

    switch (names->op) {
        /*-------------------------------------------------------------------------
         * H5G_NAME_MOUNT
         *-------------------------------------------------------------------------
         */
        case H5G_NAME_MOUNT:
            /* Check if object is in child mount hier. */
            if (obj_in_child) {
                const char *full_path; /* Full path of current object */
                const char *src_path;  /* Full path of source object */
                H5RS_str_t *rs;        /* Ref-counted string for new path */

                /* Get pointers to paths of interest */
                full_path = H5RS_get_str(obj_path->full_path_r);
                src_path  = H5RS_get_str(names->src_full_path_r);

                /* Create new full path */
                if (NULL == (rs = H5RS_create(src_path)))
                    HGOTO_ERROR(H5E_SYM, H5E_CANTCREATE, FAIL, "can't create ref-counted string");
                H5RS_acat(rs, full_path);

                /* Release previous full path */
                H5RS_decr(obj_path->full_path_r);

                /* Take ownership of the new full path */
                obj_path->full_path_r = rs;
            } /* end if */
            /* Object must be in parent mount file hier. */
            else {
                /* Check if the source is along the entry's path */
                /* (But not actually the entry itself) */
                if (H5G__common_path(obj_path->full_path_r, names->src_full_path_r) &&
                    H5RS_cmp(obj_path->full_path_r, names->src_full_path_r)) {
                    /* Hide the user path */
                    (obj_path->obj_hidden)++;
                } /* end if */
            }     /* end else */
            break;

        /*-------------------------------------------------------------------------
         * H5G_NAME_UNMOUNT
         *-------------------------------------------------------------------------
         */
        case H5G_NAME_UNMOUNT:
            if (obj_in_child) {
                const char *full_path;   /* Full path of current object */
                const char *full_suffix; /* Full path after source path */
                const char *src_path;    /* Full path of source object */
                H5RS_str_t *rs;          /* Ref-counted string for new path */

                /* Get pointers to paths of interest */
                full_path = H5RS_get_str(obj_path->full_path_r);
                src_path  = H5RS_get_str(names->src_full_path_r);

                /* Construct full path suffix */
                full_suffix = full_path + strlen(src_path);

                /* Create new full path suffix */
                if (NULL == (rs = H5RS_create(full_suffix)))
                    HGOTO_ERROR(H5E_SYM, H5E_CANTCREATE, FAIL, "can't create ref-counted string");

                /* Release previous full path */
                H5RS_decr(obj_path->full_path_r);

                /* Take ownership of the new full path */
                obj_path->full_path_r = rs;

                /* Check if the object's user path should be invalidated */
                if (obj_path->user_path_r && H5RS_len(rs) < H5RS_len(obj_path->user_path_r)) {
                    /* Free user path */
                    H5RS_decr(obj_path->user_path_r);
                    obj_path->user_path_r = NULL;
                } /* end if */
            }     /* end if */
            else {
                /* Check if file being unmounted was hiding the object */
                if (H5G__common_path(obj_path->full_path_r, names->src_full_path_r) &&
                    H5RS_cmp(obj_path->full_path_r, names->src_full_path_r)) {
                    /* Un-hide the user path */
                    (obj_path->obj_hidden)--;
                } /* end if */
            }     /* end else */
            break;

        /*-------------------------------------------------------------------------
         * H5G_NAME_DELETE
         *-------------------------------------------------------------------------
         */
        case H5G_NAME_DELETE:
            /* Check if the location being unlinked is in the path for the current object */
            if (H5G__common_path(obj_path->full_path_r, names->src_full_path_r)) {
                /* Free paths for object */
                H5G_name_free(obj_path);
            } /* end if */
            break;

        /*-------------------------------------------------------------------------
         * H5G_NAME_MOVE
         *-------------------------------------------------------------------------
         */
        case H5G_NAME_MOVE: /* Link move case, check for relative names case */
            /* Check if the src object moved is in the current object's path */
            if (H5G__common_path(obj_path->full_path_r, names->src_full_path_r)) {
                const char *full_path;   /* Full path of current object */
                const char *full_suffix; /* Suffix of full path, after src_path */
                const char *src_path;    /* Full path of source object */
                const char *dst_path;    /* Full path of destination object */
                H5RS_str_t *rs;          /* Ref-counted string for new path */

                /* Sanity check */
                assert(names->dst_full_path_r);

                /* Get pointers to paths of interest */
                full_path = H5RS_get_str(obj_path->full_path_r);
                src_path  = H5RS_get_str(names->src_full_path_r);
                dst_path  = H5RS_get_str(names->dst_full_path_r);

                /* Make certain that the source and destination names are full (not relative) paths */
                assert(*src_path == '/');
                assert(*dst_path == '/');

                /* Get pointer to "full suffix" */
                full_suffix = full_path + strlen(src_path);

                /* Update the user path, if one exists */
                if (obj_path->user_path_r)
                    if (H5G__name_move_path(&(obj_path->user_path_r), full_suffix, src_path, dst_path) < 0)
                        HGOTO_ERROR(H5E_SYM, H5E_PATH, FAIL, "can't build user path name");

                /* Create new full path */
                if (NULL == (rs = H5RS_create(dst_path)))
                    HGOTO_ERROR(H5E_SYM, H5E_CANTCREATE, FAIL, "can't create ref-counted string");
                H5RS_acat(rs, full_suffix);

                /* Release previous full path */
                H5RS_decr(obj_path->full_path_r);

                /* Take ownership of the new full path */
                obj_path->full_path_r = rs;
            } /* end if */
            break;

        default:
            HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid operation");
    } /* end switch */

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

/*-------------------------------------------------------------------------
 * Function: H5G_name_replace
 *
 * Purpose: Search the list of open IDs and replace names according to a
 *              particular operation.  The operation occurred on the
 *              SRC_FILE/SRC_FULL_PATH_R object.  The new name (if there is
 *              one) is NEW_NAME_R.  Additional entry location information
 *              (currently only needed for the 'move' operation) is passed in
 *              DST_FILE/DST_FULL_PATH_R.
 *
 * Return: Success: 0, Failure: -1
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5G_name_replace(const H5O_link_t *lnk, H5G_names_op_t op, H5F_t *src_file, H5RS_str_t *src_full_path_r,
                 H5F_t *dst_file, H5RS_str_t *dst_full_path_r)
{
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_NOAPI(FAIL)

    /* Check arguments */
    assert(src_file);

    /* Check if the object we are manipulating has a path */
    if (src_full_path_r) {
        bool search_group    = false; /* Flag to indicate that groups are to be searched */
        bool search_dataset  = false; /* Flag to indicate that datasets are to be searched */
        bool search_datatype = false; /* Flag to indicate that datatypes are to be searched */

        /* Check for particular link to operate on */
        if (lnk) {
            /* Look up the object type for each type of link */
            switch (lnk->type) {
                case H5L_TYPE_HARD: {
                    H5O_loc_t  tmp_oloc; /* Temporary object location */
                    H5O_type_t obj_type; /* Type of object at location */

                    /* Build temporary object location */
                    tmp_oloc.file = src_file;
                    tmp_oloc.addr = lnk->u.hard.addr;

                    /* Get the type of the object */
                    if (H5O_obj_type(&tmp_oloc, &obj_type) < 0)
                        HGOTO_ERROR(H5E_SYM, H5E_CANTGET, FAIL, "can't get object type");

                    /* Determine which type of objects to operate on */
                    switch (obj_type) {
                        case H5O_TYPE_GROUP:
                            /* Search and replace names through group IDs */
                            search_group = true;
                            break;

                        case H5O_TYPE_DATASET:
                            /* Search and replace names through dataset IDs */
                            search_dataset = true;
                            break;

                        case H5O_TYPE_NAMED_DATATYPE:
                            /* Search and replace names through datatype IDs */
                            search_datatype = true;
                            break;

                        case H5O_TYPE_MAP:
                            HGOTO_ERROR(H5E_SYM, H5E_BADTYPE, FAIL,
                                        "maps not supported in native VOL connector");

                        case H5O_TYPE_UNKNOWN:
                        case H5O_TYPE_NTYPES:
                            /* Search and replace names through datatype IDs */
                        default:
                            HGOTO_ERROR(H5E_SYM, H5E_BADTYPE, FAIL, "not valid object type");
                    } /* end switch */
                }     /* end case */
                break;

                case H5L_TYPE_SOFT:
                    /* Symbolic links might resolve to any object, so we need to search all IDs */
                    search_group = search_dataset = search_datatype = true;
                    break;

                case H5L_TYPE_ERROR:
                case H5L_TYPE_EXTERNAL:
                case H5L_TYPE_MAX:
                default: /* User-defined link */
                    /* Check for unknown library-defined link type */
                    if (lnk->type < H5L_TYPE_UD_MIN)
                        HGOTO_ERROR(H5E_SYM, H5E_BADVALUE, FAIL, "unknown link type");

                    /* User-defined & external links automatically wipe out
                     * names (because it would be too much work to track them),
                     * so there's no point in searching them.
                     */
                    break;
            } /* end switch */
        }     /* end if */
        else {
            /* We pass NULL as link pointer when we need to search all IDs */
            search_group = search_dataset = search_datatype = true;
        }

        /* Check if we need to operate on the objects affected */
        if (search_group || search_dataset || search_datatype) {
            H5G_names_t names; /* Structure to hold operation information for callback */

            /* Find top file in src location's mount hierarchy */
            while (H5F_PARENT(src_file))
                src_file = H5F_PARENT(src_file);

            /* Set up common information for callback */
            names.src_file        = src_file;
            names.src_full_path_r = src_full_path_r;
            names.dst_file        = dst_file;
            names.dst_full_path_r = dst_full_path_r;
            names.op              = op;

            /* Search through group IDs */
            if (search_group)
                if (H5I_iterate(H5I_GROUP, H5G__name_replace_cb, &names, false) < 0)
                    HGOTO_ERROR(H5E_SYM, H5E_BADITER, FAIL, "can't iterate over groups");

            /* Search through dataset IDs */
            if (search_dataset)
                if (H5I_iterate(H5I_DATASET, H5G__name_replace_cb, &names, false) < 0)
                    HGOTO_ERROR(H5E_SYM, H5E_BADITER, FAIL, "can't iterate over datasets");

            /* Search through datatype IDs */
            if (search_datatype)
                if (H5I_iterate(H5I_DATATYPE, H5G__name_replace_cb, &names, false) < 0)
                    HGOTO_ERROR(H5E_SYM, H5E_BADITER, FAIL, "can't iterate over datatypes");
        } /* end if */
    }     /* end if */

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

/*-------------------------------------------------------------------------
 * Function:    H5G__get_name_by_addr_cb
 *
 * Purpose:     Callback for retrieving object's name by address
 *
 * Return:      Positive if path is for object desired
 * 	            0 if not correct object
 * 	            negative on failure.
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5G__get_name_by_addr_cb(hid_t gid, const char *path, const H5L_info2_t *linfo, void *_udata)
{
    H5G_gnba_iter_t *udata = (H5G_gnba_iter_t *)_udata; /* User data for iteration */
    H5G_loc_t        obj_loc;                           /* Location of object */
    H5G_name_t       obj_path;                          /* Object's group hier. path */
    H5O_loc_t        obj_oloc;                          /* Object's object location */
    bool             obj_found = false;                 /* Object at 'path' found */
    herr_t           ret_value = H5_ITER_CONT;          /* Return value */

    FUNC_ENTER_PACKAGE

    /* Sanity check */
    assert(path);
    assert(linfo);
    assert(udata->loc);
    assert(udata->path == NULL);

    /* Check for hard link with correct address */
    if (linfo->type == H5L_TYPE_HARD) {
        haddr_t link_addr;

        /* Retrieve hard link address from VOL token */
        if (H5VL_native_token_to_addr(udata->loc->file, H5I_FILE, linfo->u.token, &link_addr) < 0)
            HGOTO_ERROR(H5E_SYM, H5E_CANTUNSERIALIZE, FAIL, "can't deserialize object token into address");

        if (udata->loc->addr == link_addr) {
            H5G_loc_t grp_loc; /* Location of group */

            /* Get group's location */
            if (H5G_loc(gid, &grp_loc) < 0)
                HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, H5_ITER_ERROR, "bad group location");

            /* Set up opened object location to fill in */
            obj_loc.oloc = &obj_oloc;
            obj_loc.path = &obj_path;
            H5G_loc_reset(&obj_loc);

            /* Find the object */
            if (H5G_loc_find(&grp_loc, path, &obj_loc /*out*/) < 0)
                HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, H5_ITER_ERROR, "object not found");
            obj_found = true;

            /* Check for object in same file (handles mounted files) */
            /* (re-verify address, in case we traversed a file mount) */
            if (udata->loc->addr == obj_loc.oloc->addr && udata->loc->file == obj_loc.oloc->file) {
                if (NULL == (udata->path = H5MM_strdup(path)))
                    HGOTO_ERROR(H5E_SYM, H5E_CANTALLOC, H5_ITER_ERROR, "can't duplicate path string");

                /* We found a match so we return immediately */
                HGOTO_DONE(H5_ITER_STOP);
            } /* end if */
        }     /* end if */
    }         /* end if */

done:
    if (obj_found && H5G_loc_free(&obj_loc) < 0)
        HDONE_ERROR(H5E_SYM, H5E_CANTRELEASE, H5_ITER_ERROR, "can't free location");

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

/*-------------------------------------------------------------------------
 * Function:    H5G_get_name_by_addr
 *
 * Purpose:     Tries to figure out the path to an object from it's address
 *
 * Return:      Success:    Returns size of path name, and copies it into buffer
 *                          pointed to by name if that buffer is big enough.
 *                          0 if it cannot find the path
 *
 *              Failure:    -1
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5G_get_name_by_addr(H5F_t *f, const H5O_loc_t *loc, char *name, size_t size, size_t *name_len)
{
    H5G_gnba_iter_t udata;               /* User data for iteration  */
    size_t          len;                 /* Length of path name */
    H5G_loc_t       root_loc;            /* Root group's location    */
    bool            found_obj = false;   /* If we found the object   */
    herr_t          status;              /* Status from iteration    */
    herr_t          ret_value = SUCCEED; /* Return value             */

    /* Portably clear udata struct (before FUNC_ENTER) */
    memset(&udata, 0, sizeof(udata));

    FUNC_ENTER_NOAPI(FAIL)

    /* Construct a group location for root group of the file */
    if (H5G_root_loc(f, &root_loc) < 0)
        HGOTO_ERROR(H5E_SYM, H5E_CANTGET, FAIL, "can't get root group's location");

    /* Check for root group being the object looked for */
    if (root_loc.oloc->addr == loc->addr && root_loc.oloc->file == loc->file) {
        if (NULL == (udata.path = H5MM_strdup("")))
            HGOTO_ERROR(H5E_SYM, H5E_CANTALLOC, FAIL, "can't duplicate path string");
        found_obj = true;
    } /* end if */
    else {
        /* Set up user data for iterator */
        udata.loc  = loc;
        udata.path = NULL;

        /* Visit all the links in the file */
        if ((status = H5G_visit(&root_loc, "/", H5_INDEX_NAME, H5_ITER_NATIVE, H5G__get_name_by_addr_cb,
                                &udata)) < 0)
            HGOTO_ERROR(H5E_SYM, H5E_BADITER, FAIL, "group traversal failed while looking for object name");
        else if (status > 0)
            found_obj = true;
    } /* end else */

    /* Check for finding the object */
    if (found_obj) {
        /* Set the length of the full path */
        len = strlen(udata.path) + 1; /* Length of path + 1 (for "/") */

        /* If there's a buffer provided, copy into it, up to the limit of its size */
        if (name) {
            /* Copy the initial path separator */
            strncpy(name, "/", (size_t)2);

            /* Append the rest of the path */
            /* (less one character, for the initial path separator) */
            strncat(name, udata.path, (size - 2));
            if (len >= size)
                name[size - 1] = '\0';
        } /* end if */
    }     /* end if */
    else
        len = 0;

    /* Set path name length, if given */
    if (name_len)
        *name_len = len;

done:
    /* Release resources */
    H5MM_xfree(udata.path);

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