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

/****************/
/* Module Setup */
/****************/

#include "H5Lmodule.h" /* This source code file is part of the H5L module */

/***********/
/* Headers */
/***********/
#include "H5private.h"          /* Generic Functions                        */
#include "H5CXprivate.h"        /* API Contexts                             */
#include "H5Eprivate.h"         /* Error handling                           */
#include "H5ESprivate.h"        /* Event Sets                               */
#include "H5Gprivate.h"         /* Groups                                   */
#include "H5Iprivate.h"         /* IDs                                      */
#include "H5Lpkg.h"             /* Links                                    */
#include "H5MMprivate.h"        /* Memory management                        */
#include "H5Pprivate.h"         /* Property lists                           */
#include "H5VLprivate.h"        /* Virtual Object Layer                     */
#include "H5VLnative_private.h" /* Native VOL                               */

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

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

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

static herr_t H5L__create_soft_api_common(const char *link_target, hid_t link_loc_id, const char *link_name,
                                          hid_t lcpl_id, hid_t lapl_id, void **token_ptr,
                                          H5VL_object_t **_vol_obj_ptr);
static herr_t H5L__create_hard_api_common(hid_t cur_loc_id, const char *cur_name, hid_t new_loc_id,
                                          const char *new_name, hid_t lcpl_id, hid_t lapl_id,
                                          void **token_ptr, H5VL_connector_t **conn);
static herr_t H5L__delete_api_common(hid_t loc_id, const char *name, hid_t lapl_id, void **token_ptr,
                                     H5VL_object_t **_vol_obj_ptr);
static herr_t H5L__delete_by_idx_api_common(hid_t loc_id, const char *group_name, H5_index_t idx_type,
                                            H5_iter_order_t order, hsize_t n, hid_t lapl_id, void **token_ptr,
                                            H5VL_object_t **_vol_obj_ptr);
static herr_t H5L__exists_api_common(hid_t loc_id, const char *name, bool *exists, hid_t lapl_id,
                                     void **token_ptr, H5VL_object_t **_vol_obj_ptr);
static herr_t H5L__iterate_api_common(hid_t group_id, H5_index_t idx_type, H5_iter_order_t order,
                                      hsize_t *idx_p, H5L_iterate2_t op, void *op_data, void **token_ptr,
                                      H5VL_object_t **_vol_obj_ptr);

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

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

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

/*-------------------------------------------------------------------------
 * 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 then 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
 *
 *-------------------------------------------------------------------------
 */
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,
        hid_t lapl_id)
{
    H5VL_object_t    *vol_obj1 = NULL; /* Object of src_id */
    H5VL_object_t    *vol_obj2 = NULL; /* Object of dst_id */
    H5VL_loc_params_t loc_params1;
    H5VL_loc_params_t loc_params2;
    H5I_type_t        src_id_type = H5I_BADID, dst_id_type = H5I_BADID;
    herr_t            ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check arguments */
    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");
    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");

    /* reset an ID in the case of H5L_SAME_LOC */
    if (src_loc_id == H5L_SAME_LOC)
        src_loc_id = dst_loc_id;
    else if (dst_loc_id == H5L_SAME_LOC)
        dst_loc_id = src_loc_id;

    /* verify that src and dst IDs are either a file or a group ID */
    src_id_type = H5I_get_type(src_loc_id);
    if (!(H5I_GROUP == src_id_type || H5I_FILE == src_id_type))
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid group (or file) ID, src_loc_id");
    dst_id_type = H5I_get_type(dst_loc_id);
    if (!(H5I_GROUP == dst_id_type || H5I_FILE == dst_id_type))
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid group (or file) ID, dst_loc_id");

    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");

    /* Check the link create property list */
    if (H5P_DEFAULT == lcpl_id)
        lcpl_id = H5P_LINK_CREATE_DEFAULT;

    /* Set the LCPL for the API context */
    H5CX_set_lcpl(lcpl_id);

    /* Verify access property list and set up collective metadata if appropriate */
    if (H5CX_set_apl(&lapl_id, H5P_CLS_LACC, dst_loc_id, true) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTSET, FAIL, "can't set access property list info");

    /* Set location parameter for source object */
    loc_params1.type                         = H5VL_OBJECT_BY_NAME;
    loc_params1.loc_data.loc_by_name.name    = src_name;
    loc_params1.loc_data.loc_by_name.lapl_id = lapl_id;
    loc_params1.obj_type                     = src_id_type;

    /* Set location parameter for destination object */
    loc_params2.type                         = H5VL_OBJECT_BY_NAME;
    loc_params2.loc_data.loc_by_name.name    = dst_name;
    loc_params2.loc_data.loc_by_name.lapl_id = lapl_id;
    loc_params2.obj_type                     = dst_id_type;

    /* Get the location object */
    if (NULL == (vol_obj1 = H5VL_vol_object(src_loc_id)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid location identifier");
    /* Get the location object */
    if (NULL == (vol_obj2 = H5VL_vol_object(dst_loc_id)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid location identifier");

    /* Make sure that the VOL connectors are the same */
    if (vol_obj1 && vol_obj2) {
        htri_t same_connector;

        /* Check if both objects are associated with the same VOL connector */
        if ((same_connector =
                 H5VL_conn_same_class(H5VL_OBJ_CONNECTOR(vol_obj1), H5VL_OBJ_CONNECTOR(vol_obj2))) < 0)
            HGOTO_ERROR(H5E_LINK, H5E_CANTCOMPARE, FAIL, "can't compare connector classes");
        if (!same_connector)
            HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL,
                        "Objects are accessed through different VOL connectors and can't be linked");
    }

    /* Move the link */
    if (H5VL_link_move(vol_obj1, &loc_params1, vol_obj2, &loc_params2, lcpl_id, lapl_id,
                       H5P_DATASET_XFER_DEFAULT, H5_REQUEST_NULL) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTMOVE, FAIL, "unable to move link");

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

/*-------------------------------------------------------------------------
 * 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
 *
 *-------------------------------------------------------------------------
 */
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,
        hid_t lapl_id)
{
    H5VL_object_t    *vol_obj1 = NULL; /* Object of src_id */
    H5VL_loc_params_t loc_params1;
    H5VL_object_t    *vol_obj2 = NULL; /* Object of dst_id */
    H5VL_loc_params_t loc_params2;
    H5I_type_t        src_id_type = H5I_BADID, dst_id_type = H5I_BADID;
    herr_t            ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check arguments */
    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");
    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");
    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");

    /* reset an ID in the case of H5L_SAME_LOC */
    if (src_loc_id == H5L_SAME_LOC)
        src_loc_id = dst_loc_id;
    else if (dst_loc_id == H5L_SAME_LOC)
        dst_loc_id = src_loc_id;

    /* verify that src and dst IDs are either a file or a group ID */
    src_id_type = H5I_get_type(src_loc_id);
    if (!(H5I_GROUP == src_id_type || H5I_FILE == src_id_type) && src_loc_id != H5L_SAME_LOC)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid group (or file) ID, src_loc_id");
    dst_id_type = H5I_get_type(dst_loc_id);
    if (!(H5I_GROUP == dst_id_type || H5I_FILE == dst_id_type) && dst_loc_id != H5L_SAME_LOC)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid group (or file) ID, dst_loc_id");

    /* Check the link create property list */
    if (H5P_DEFAULT == lcpl_id)
        lcpl_id = H5P_LINK_CREATE_DEFAULT;

    /* Set the LCPL for the API context */
    H5CX_set_lcpl(lcpl_id);

    /* Verify access property list and set up collective metadata if appropriate */
    if (H5CX_set_apl(&lapl_id, H5P_CLS_LACC, ((src_loc_id != H5L_SAME_LOC) ? src_loc_id : dst_loc_id), true) <
        0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTSET, FAIL, "can't set access property list info");

    /* Set location parameter for source object */
    loc_params1.type                         = H5VL_OBJECT_BY_NAME;
    loc_params1.loc_data.loc_by_name.name    = src_name;
    loc_params1.loc_data.loc_by_name.lapl_id = lapl_id;
    loc_params1.obj_type                     = src_id_type;

    /* Set location parameter for destination object */
    loc_params2.type                         = H5VL_OBJECT_BY_NAME;
    loc_params2.loc_data.loc_by_name.name    = dst_name;
    loc_params2.loc_data.loc_by_name.lapl_id = lapl_id;
    loc_params2.obj_type                     = dst_id_type;

    /* Get the location object */
    if (NULL == (vol_obj1 = H5VL_vol_object(src_loc_id)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid location identifier");
    /* Get the location object */
    if (NULL == (vol_obj2 = H5VL_vol_object(dst_loc_id)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid location identifier");

    /* Make sure that the VOL connectors are the same */
    if (vol_obj1 && vol_obj2) {
        htri_t same_connector;

        /* Check if both objects are associated with the same VOL connector */
        if ((same_connector =
                 H5VL_conn_same_class(H5VL_OBJ_CONNECTOR(vol_obj1), H5VL_OBJ_CONNECTOR(vol_obj2))) < 0)
            HGOTO_ERROR(H5E_LINK, H5E_CANTCOMPARE, FAIL, "can't compare connector classes");
        if (!same_connector)
            HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL,
                        "Objects are accessed through different VOL connectors and can't be linked");
    } /* end if */

    /* Copy the link */
    if (H5VL_link_copy(vol_obj1, &loc_params1, vol_obj2, &loc_params2, lcpl_id, lapl_id,
                       H5P_DATASET_XFER_DEFAULT, H5_REQUEST_NULL) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTMOVE, FAIL, "unable to copy link");

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

/*-------------------------------------------------------------------------
 * Function:    H5L__create_soft_api_common
 *
 * Purpose:     This is the common function for creating a soft link
 *
 * Return:      Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5L__create_soft_api_common(const char *link_target, hid_t link_loc_id, const char *link_name, hid_t lcpl_id,
                            hid_t lapl_id, void **token_ptr, H5VL_object_t **_vol_obj_ptr)
{
    H5VL_object_t  *tmp_vol_obj = NULL; /* Object for loc_id */
    H5VL_object_t **vol_obj_ptr =
        (_vol_obj_ptr ? _vol_obj_ptr : &tmp_vol_obj); /* Ptr to object ptr for loc_id */
    H5VL_link_create_args_t vol_cb_args;              /* Arguments to VOL callback */
    H5VL_loc_params_t       loc_params;               /* Location parameters for object access */
    herr_t                  ret_value = SUCCEED;      /* Return value */

    FUNC_ENTER_PACKAGE

    /* Check arguments */
    if (link_loc_id == H5L_SAME_LOC)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "link location id should not be H5L_SAME_LOC");
    if (!link_target)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "link_target parameter cannot be NULL");
    if (!*link_target)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "link_target parameter cannot be an empty string");
    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");
    /* link_name is verified in H5VL_setup_name_args() */

    /* Get the link creation property list */
    if (H5P_DEFAULT == lcpl_id)
        lcpl_id = H5P_LINK_CREATE_DEFAULT;

    /* Set the LCPL for the API context */
    H5CX_set_lcpl(lcpl_id);

    /* Verify access property list and set up collective metadata if appropriate */
    if (H5CX_set_apl(&lapl_id, H5P_CLS_LACC, link_loc_id, true) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTSET, FAIL, "can't set access property list info");

    /* Set up object access arguments */
    if (H5VL_setup_name_args(link_loc_id, link_name, true, lapl_id, vol_obj_ptr, &loc_params) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTSET, FAIL, "can't set object access arguments");

    /* Set up VOL callback arguments */
    vol_cb_args.op_type          = H5VL_LINK_CREATE_SOFT;
    vol_cb_args.args.soft.target = link_target;

    /* Create the link */
    if (H5VL_link_create(&vol_cb_args, *vol_obj_ptr, &loc_params, lcpl_id, lapl_id, H5P_DATASET_XFER_DEFAULT,
                         token_ptr) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTCREATE, FAIL, "unable to create soft link");

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

/*-------------------------------------------------------------------------
 * Function:    H5Lcreate_soft
 *
 * Purpose:     Creates a soft link from LINK_NAME to LINK_TARGET.
 *
 *              LINK_TARGET can be anything and is interpreted at lookup
 *              time relative to the group which contains the final component
 *              of LINK_NAME.  For instance, if LINK_TARGET is `./foo' and
 *              LINK_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
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lcreate_soft(const char *link_target, hid_t link_loc_id, const char *link_name, hid_t lcpl_id,
               hid_t lapl_id)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Creates a soft link synchronously */
    if (H5L__create_soft_api_common(link_target, link_loc_id, link_name, lcpl_id, lapl_id, NULL, NULL) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTCREATE, FAIL, "unable to synchronously create soft link");

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

/*-------------------------------------------------------------------------
 * Function:    H5Lcreate_soft_async
 *
 * Purpose:     Asynchronous version of H5Lcreate_soft
 *
 * Return:      Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lcreate_soft_async(const char *app_file, const char *app_func, unsigned app_line, const char *link_target,
                     hid_t link_loc_id, const char *link_name, hid_t lcpl_id, hid_t lapl_id, hid_t es_id)
{
    H5VL_object_t *vol_obj   = NULL;            /* Object for loc_id */
    void          *token     = NULL;            /* Request token for async operation        */
    void         **token_ptr = H5_REQUEST_NULL; /* Pointer to request token for async operation        */
    herr_t         ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Set up request token pointer for asynchronous operation */
    if (H5ES_NONE != es_id)
        token_ptr = &token; /* Point at token for VOL connector to set up */

    /* Creates a soft link asynchronously */
    if (H5L__create_soft_api_common(link_target, link_loc_id, link_name, lcpl_id, lapl_id, token_ptr,
                                    &vol_obj) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTCREATE, FAIL, "unable to asynchronously create soft link");

    /* If a token was created, add the token to the event set */
    if (NULL != token)
        /* clang-format off */
        if (H5ES_insert(es_id, H5VL_OBJ_CONNECTOR(vol_obj), token,
                        H5ARG_TRACE9(__func__, "*s*sIu*si*siii", app_file, app_func, app_line, link_target, link_loc_id, link_name, lcpl_id, lapl_id, es_id)) < 0)
            /* clang-format on */
            HGOTO_ERROR(H5E_LINK, H5E_CANTINSERT, FAIL, "can't insert token into event set");

done:
    FUNC_LEAVE_API(ret_value)
} /* H5Lcreate_soft_async() */

/*-------------------------------------------------------------------------
 * Function:    H5L__create_hard_api_common
 *
 * Purpose:     This is the common function for creating a hard link
 *
 * Return:      Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5L__create_hard_api_common(hid_t cur_loc_id, const char *cur_name, hid_t link_loc_id, const char *link_name,
                            hid_t lcpl_id, hid_t lapl_id, void **token_ptr, H5VL_connector_t **connector)
{
    H5VL_object_t          *curr_vol_obj = NULL; /* Object of cur_loc_id */
    H5VL_object_t          *link_vol_obj = NULL; /* Object of link_loc_id */
    H5VL_link_create_args_t vol_cb_args;         /* Arguments to VOL callback */
    H5VL_loc_params_t       link_loc_params;     /* Location parameters for link_loc_id object access */
    herr_t                  ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_PACKAGE

    /* Check arguments */
    if (cur_loc_id == H5L_SAME_LOC && link_loc_id == H5L_SAME_LOC)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "source and destination should not be both H5L_SAME_LOC");
    if (!cur_name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "cur_name parameter cannot be NULL");
    if (!*cur_name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "cur_name parameter cannot be an empty string");
    if (!link_name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "new_name parameter cannot be NULL");
    if (!*link_name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "new_name parameter cannot be an empty string");
    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");

    /* Check the link create property list */
    if (H5P_DEFAULT == lcpl_id)
        lcpl_id = H5P_LINK_CREATE_DEFAULT;

    /* Set the LCPL for the API context */
    H5CX_set_lcpl(lcpl_id);

    /* Verify access property list and set up collective metadata if appropriate */
    if (H5CX_set_apl(&lapl_id, H5P_CLS_LACC, cur_loc_id, true) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTSET, FAIL, "can't set access property list info");

    if (H5L_SAME_LOC != cur_loc_id)
        /* Get the current location object */
        if (NULL == (curr_vol_obj = H5VL_vol_object(cur_loc_id)))
            HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid location identifier");
    if (H5L_SAME_LOC != link_loc_id)
        /* Get the new location object */
        if (NULL == (link_vol_obj = H5VL_vol_object(link_loc_id)))
            HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid location identifier");

    /* Make sure that the VOL connectors are the same */
    if (curr_vol_obj && link_vol_obj) {
        htri_t same_connector;

        /* Check if both objects are associated with the same VOL connector */
        if ((same_connector = H5VL_conn_same_class(H5VL_OBJ_CONNECTOR(curr_vol_obj),
                                                   H5VL_OBJ_CONNECTOR(link_vol_obj))) < 0)
            HGOTO_ERROR(H5E_LINK, H5E_CANTCOMPARE, FAIL, "can't compare connector classes");
        if (!same_connector)
            HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL,
                        "Objects are accessed through different VOL connectors and can't be linked");
    } /* end if */

    /* Set up new location struct */
    link_loc_params.type     = H5VL_OBJECT_BY_NAME;
    link_loc_params.obj_type = (link_vol_obj ? H5I_get_type(link_loc_id) : H5I_get_type(cur_loc_id));
    link_loc_params.loc_data.loc_by_name.name    = link_name;
    link_loc_params.loc_data.loc_by_name.lapl_id = lapl_id;

    /* Set up VOL callback arguments */
    vol_cb_args.op_type                        = H5VL_LINK_CREATE_HARD;
    vol_cb_args.args.hard.curr_obj             = (curr_vol_obj ? H5VL_OBJ_DATA(curr_vol_obj) : NULL);
    vol_cb_args.args.hard.curr_loc_params.type = H5VL_OBJECT_BY_NAME;
    vol_cb_args.args.hard.curr_loc_params.obj_type =
        (H5L_SAME_LOC != cur_loc_id ? H5I_get_type(cur_loc_id) : H5I_BADID);
    vol_cb_args.args.hard.curr_loc_params.loc_data.loc_by_name.name    = cur_name;
    vol_cb_args.args.hard.curr_loc_params.loc_data.loc_by_name.lapl_id = lapl_id;

    /* Create the link */
    if (H5VL_link_create(&vol_cb_args, (link_vol_obj ? link_vol_obj : curr_vol_obj), &link_loc_params,
                         lcpl_id, lapl_id, H5P_DATASET_XFER_DEFAULT, token_ptr) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTCREATE, FAIL, "unable to create hard link");

    /* Set the connector to use for async operations */
    if (connector)
        *connector = (link_vol_obj ? H5VL_OBJ_CONNECTOR(link_vol_obj) : H5VL_OBJ_CONNECTOR(curr_vol_obj));

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

/*-------------------------------------------------------------------------
 * 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
 *
 *-------------------------------------------------------------------------
 */
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,
               hid_t lapl_id)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Creates a hard link synchronously */
    if (H5L__create_hard_api_common(cur_loc_id, cur_name, new_loc_id, new_name, lcpl_id, lapl_id, NULL,
                                    NULL) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTCREATE, FAIL, "unable to synchronously create hard link");

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

/*-------------------------------------------------------------------------
 * Function:    H5Lcreate_hard_async
 *
 * Purpose:     Asynchronous version of H5Lcreate_hard
 *
 * Note:        The implementation for this routine is different from other
 *              _async operations, as the 'api_common' routine needs a "real"
 *              H5VL_object_t to point at, which is usually provided by the
 *              loc_id, but isn't here.
 *
 * Return:      Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lcreate_hard_async(const char *app_file, const char *app_func, unsigned app_line, hid_t cur_loc_id,
                     const char *cur_name, hid_t new_loc_id, const char *new_name, hid_t lcpl_id,
                     hid_t lapl_id, hid_t es_id)
{
    H5VL_connector_t *connector = NULL;            /* Connector for operation */
    void             *token     = NULL;            /* Request token for async operation        */
    void            **token_ptr = H5_REQUEST_NULL; /* Pointer to request token for async operation        */
    herr_t            ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Set up request token pointer for asynchronous operation */
    if (H5ES_NONE != es_id)
        token_ptr = &token; /* Point at token for VOL connector to set up */

    /* Creates a hard link asynchronously */
    if (H5L__create_hard_api_common(cur_loc_id, cur_name, new_loc_id, new_name, lcpl_id, lapl_id, token_ptr,
                                    &connector) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTCREATE, FAIL, "unable to asynchronously create hard link");
    assert(connector);

    /* If a token was created, add the token to the event set */
    if (NULL != token)
        /* clang-format off */
        if (H5ES_insert(es_id, connector, token,
                        H5ARG_TRACE10(__func__, "*s*sIui*si*siii", app_file, app_func, app_line, cur_loc_id, cur_name, new_loc_id, new_name, lcpl_id, lapl_id, es_id)) < 0)
            /* clang-format on */
            HGOTO_ERROR(H5E_LINK, H5E_CANTINSERT, FAIL, "can't insert token into event set");

done:
    FUNC_LEAVE_API(ret_value)
} /* H5Lcreate_hard_async() */

/*-------------------------------------------------------------------------
 * Function:    H5Lcreate_external
 *
 * Purpose:     Creates an external link from LINK_NAME to OBJ_NAME.
 *
 *              External links are links to objects in other HDF5 files.  They
 *              are allowed to "dangle" like soft links internal to a file.
 *              FILE_NAME is the name of the file that OBJ_NAME is contained
 *              within.  If OBJ_NAME is given as a relative path name, the
 *              path will be relative to the root group of FILE_NAME.
 *              LINK_NAME is interpreted relative to LINK_LOC_ID, which is
 *              either a file ID or a group ID.
 *
 * Return:      Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lcreate_external(const char *file_name, const char *obj_name, hid_t link_loc_id, const char *link_name,
                   hid_t lcpl_id, hid_t lapl_id)
{
    H5VL_object_t          *vol_obj = NULL;       /* Object of loc_id */
    H5VL_link_create_args_t vol_cb_args;          /* Arguments to VOL callback */
    H5VL_loc_params_t       loc_params;           /* Location parameters for object access */
    char                   *norm_obj_name = NULL; /* Pointer to normalized current name */
    void                   *ext_link_buf  = NULL; /* Buffer to contain external link */
    size_t                  buf_size;             /* Size of buffer to hold external link */
    size_t                  file_name_len;        /* Length of file name string */
    size_t                  norm_obj_name_len;    /* Length of normalized object name string */
    uint8_t                *p;                    /* Pointer into external link buffer */
    herr_t                  ret_value = SUCCEED;  /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check arguments */
    if (!file_name || !*file_name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no file name specified");
    if (!obj_name || !*obj_name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no object name specified");
    if (!link_name || !*link_name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no link name specified");

    /* Get the link creation property list */
    if (H5P_DEFAULT == lcpl_id)
        lcpl_id = H5P_LINK_CREATE_DEFAULT;

    /* Set the LCPL for the API context */
    H5CX_set_lcpl(lcpl_id);

    /* Verify access property list and set up collective metadata if appropriate */
    if (H5CX_set_apl(&lapl_id, H5P_CLS_LACC, link_loc_id, true) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTSET, FAIL, "can't set access property list info");

    /* Get normalized copy of the link target */
    if (NULL == (norm_obj_name = H5G_normalize(obj_name)))
        HGOTO_ERROR(H5E_LINK, H5E_BADVALUE, FAIL, "can't normalize object name");

    /* Combine the filename and link name into a single buffer to give to the UD link */
    file_name_len     = strlen(file_name) + 1;
    norm_obj_name_len = strlen(norm_obj_name) + 1;
    buf_size          = 1 + file_name_len + norm_obj_name_len;
    if (NULL == (ext_link_buf = H5MM_malloc(buf_size)))
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate udata buffer");

    /* Encode the external link information */
    p    = (uint8_t *)ext_link_buf;
    *p++ = (H5L_EXT_VERSION << 4) | H5L_EXT_FLAGS_ALL; /* External link version & flags */
    strncpy((char *)p, file_name, buf_size - 1);       /* Name of file containing external link's object */
    p += file_name_len;
    strncpy((char *)p, norm_obj_name, buf_size - (file_name_len + 1)); /* External link's object */

    loc_params.type                         = H5VL_OBJECT_BY_NAME;
    loc_params.loc_data.loc_by_name.name    = link_name;
    loc_params.loc_data.loc_by_name.lapl_id = lapl_id;
    loc_params.obj_type                     = H5I_get_type(link_loc_id);

    /* get the location object */
    if (NULL == (vol_obj = H5VL_vol_object(link_loc_id)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid object identifier");

    /* Set up VOL callback arguments */
    vol_cb_args.op_type          = H5VL_LINK_CREATE_UD;
    vol_cb_args.args.ud.type     = H5L_TYPE_EXTERNAL;
    vol_cb_args.args.ud.buf      = ext_link_buf;
    vol_cb_args.args.ud.buf_size = buf_size;

    /* Create an external link */
    if (H5VL_link_create(&vol_cb_args, vol_obj, &loc_params, lcpl_id, lapl_id, H5P_DATASET_XFER_DEFAULT,
                         H5_REQUEST_NULL) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTINIT, FAIL, "unable to create external link");

done:
    H5MM_xfree(ext_link_buf);
    H5MM_xfree(norm_obj_name);

    FUNC_LEAVE_API(ret_value)
} /* end H5Lcreate_external() */

/*-------------------------------------------------------------------------
 * Function:    H5Lcreate_ud
 *
 * Purpose:     Creates a user-defined link of type LINK_TYPE named LINK_NAME
 *              with user-specified data UDATA.
 *
 *              The format of the information pointed to by UDATA is
 *              defined by the user. UDATA_SIZE holds the size of this buffer.
 *
 *              LINK_NAME is interpreted relative to LINK_LOC_ID.
 *
 *              The property list specified by LCPL_ID holds properties used
 *              to create the link.
 *
 *              The link class of the new link must already be registered
 *              with the library.
 *
 * Return:      Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lcreate_ud(hid_t link_loc_id, const char *link_name, H5L_type_t link_type, const void *udata,
             size_t udata_size, hid_t lcpl_id, hid_t lapl_id)
{
    H5VL_object_t          *vol_obj = NULL;      /* Object of loc_id */
    H5VL_link_create_args_t vol_cb_args;         /* Arguments to VOL callback */
    H5VL_loc_params_t       loc_params;          /* Location parameters for object access */
    herr_t                  ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check arguments */
    if (!link_name || !*link_name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no link name specified");
    if (link_type < H5L_TYPE_UD_MIN || link_type > H5L_TYPE_MAX)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid link class");
    if (!udata && udata_size)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "udata cannot be NULL if udata_size is non-zero");

    /* Get the link creation property list */
    if (H5P_DEFAULT == lcpl_id)
        lcpl_id = H5P_LINK_CREATE_DEFAULT;

    /* Set the LCPL for the API context */
    H5CX_set_lcpl(lcpl_id);

    /* Verify access property list and set up collective metadata if appropriate */
    if (H5CX_set_apl(&lapl_id, H5P_CLS_LACC, link_loc_id, true) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTSET, FAIL, "can't set access property list info");

    loc_params.type                         = H5VL_OBJECT_BY_NAME;
    loc_params.loc_data.loc_by_name.name    = link_name;
    loc_params.loc_data.loc_by_name.lapl_id = lapl_id;
    loc_params.obj_type                     = H5I_get_type(link_loc_id);

    /* get the location object */
    if (NULL == (vol_obj = H5VL_vol_object(link_loc_id)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid location identifier");

    /* Set up VOL callback arguments */
    vol_cb_args.op_type          = H5VL_LINK_CREATE_UD;
    vol_cb_args.args.ud.type     = link_type;
    vol_cb_args.args.ud.buf      = udata;
    vol_cb_args.args.ud.buf_size = udata_size;

    /* Create user-defined link */
    if (H5VL_link_create(&vol_cb_args, vol_obj, &loc_params, lcpl_id, lapl_id, H5P_DATASET_XFER_DEFAULT,
                         H5_REQUEST_NULL) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTINIT, FAIL, "unable to create link");

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

/*-------------------------------------------------------------------------
 * Function:    H5L__delete_api_common
 *
 * Purpose:     This is the common function for deleting a link
 *
 * Return:      Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5L__delete_api_common(hid_t loc_id, const char *name, hid_t lapl_id, void **token_ptr,
                       H5VL_object_t **_vol_obj_ptr)
{
    H5VL_object_t  *tmp_vol_obj = NULL; /* Object for loc_id */
    H5VL_object_t **vol_obj_ptr =
        (_vol_obj_ptr ? _vol_obj_ptr : &tmp_vol_obj); /* Ptr to object ptr for loc_id */
    H5VL_link_specific_args_t vol_cb_args;            /* Arguments to VOL callback */
    H5VL_loc_params_t         loc_params;             /* Location parameters for object access */
    herr_t                    ret_value = SUCCEED;    /* Return value */

    FUNC_ENTER_PACKAGE

    /* Check arguments */
    /* name is verified in H5VL_setup_name_args() */

    /* Set up object access arguments */
    if (H5VL_setup_name_args(loc_id, name, true, lapl_id, vol_obj_ptr, &loc_params) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTSET, FAIL, "can't set object access arguments");

    /* Set up VOL callback arguments */
    vol_cb_args.op_type = H5VL_LINK_DELETE;

    /* Delete link */
    if (H5VL_link_specific(*vol_obj_ptr, &loc_params, &vol_cb_args, H5P_DATASET_XFER_DEFAULT, token_ptr) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTDELETE, FAIL, "unable to delete link");

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

/*-------------------------------------------------------------------------
 * Function:    H5Ldelete
 *
 * 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
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Ldelete(hid_t loc_id, const char *name, hid_t lapl_id)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Delete a link synchronously */
    if (H5L__delete_api_common(loc_id, name, lapl_id, NULL, NULL) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTDELETE, FAIL, "unable to synchronously delete link");

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

/*-------------------------------------------------------------------------
 * Function:    H5Ldelete_async
 *
 * Purpose:     Asynchronous version of H5Ldelete
 *
 * Return:      Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Ldelete_async(const char *app_file, const char *app_func, unsigned app_line, hid_t loc_id, const char *name,
                hid_t lapl_id, hid_t es_id)
{
    H5VL_object_t *vol_obj   = NULL;            /* Object for loc_id */
    void          *token     = NULL;            /* Request token for async operation        */
    void         **token_ptr = H5_REQUEST_NULL; /* Pointer to request token for async operation        */
    herr_t         ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Set up request token pointer for asynchronous operation */
    if (H5ES_NONE != es_id)
        token_ptr = &token; /* Point at token for VOL connector to set up */

    /* Delete a link asynchronously */
    if (H5L__delete_api_common(loc_id, name, lapl_id, token_ptr, &vol_obj) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTDELETE, FAIL, "unable to asynchronously delete link");

    /* If a token was created, add the token to the event set */
    if (NULL != token)
        /* clang-format off */
        if (H5ES_insert(es_id, H5VL_OBJ_CONNECTOR(vol_obj), token,
                        H5ARG_TRACE7(__func__, "*s*sIui*sii", app_file, app_func, app_line, loc_id, name, lapl_id, es_id)) < 0)
            /* clang-format on */
            HGOTO_ERROR(H5E_LINK, H5E_CANTINSERT, FAIL, "can't insert token into event set");

done:
    FUNC_LEAVE_API(ret_value)
} /* H5Ldelete_async() */

/*-------------------------------------------------------------------------
 * Function:    H5L__delete_by_idx_api_common
 *
 * Purpose:     This is the common function for deleting a link
 *		        according to the order within an index.
 *
 * Return:      Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5L__delete_by_idx_api_common(hid_t loc_id, const char *group_name, H5_index_t idx_type,
                              H5_iter_order_t order, hsize_t n, hid_t lapl_id, void **token_ptr,
                              H5VL_object_t **_vol_obj_ptr)
{
    H5VL_object_t  *tmp_vol_obj = NULL; /* Object for loc_id */
    H5VL_object_t **vol_obj_ptr =
        (_vol_obj_ptr ? _vol_obj_ptr : &tmp_vol_obj); /* Ptr to object ptr for loc_id */
    H5VL_link_specific_args_t vol_cb_args;            /* Arguments to VOL callback */
    H5VL_loc_params_t         loc_params;             /* Location parameters for object access */
    herr_t                    ret_value = SUCCEED;    /* Return value */

    FUNC_ENTER_PACKAGE

    /* Check arguments */
    if (!group_name || !*group_name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no name specified");
    if (idx_type <= H5_INDEX_UNKNOWN || idx_type >= H5_INDEX_N)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid index type specified");
    if (order <= H5_ITER_UNKNOWN || order >= H5_ITER_N)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid iteration order specified");

    /* Set up object access arguments */
    if (H5VL_setup_idx_args(loc_id, group_name, idx_type, order, n, true, lapl_id, vol_obj_ptr, &loc_params) <
        0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTSET, FAIL, "can't set object access arguments");

    /* Set up VOL callback arguments */
    vol_cb_args.op_type = H5VL_LINK_DELETE;

    /* Delete the link */
    if (H5VL_link_specific(*vol_obj_ptr, &loc_params, &vol_cb_args, H5P_DATASET_XFER_DEFAULT, token_ptr) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTDELETE, FAIL, "unable to delete link");

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

/*-------------------------------------------------------------------------
 * Function:	H5Ldelete_by_idx
 *
 * Purpose:	Removes the specified link from the group graph and
 *		decrements the link count for the object to which it
 *		points, according to the order within an index.
 *
 *		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
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Ldelete_by_idx(hid_t loc_id, const char *group_name, H5_index_t idx_type, H5_iter_order_t order, hsize_t n,
                 hid_t lapl_id)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Delete a link synchronously */
    if (H5L__delete_by_idx_api_common(loc_id, group_name, idx_type, order, n, lapl_id, NULL, NULL) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTDELETE, FAIL, "unable to synchronously delete link");

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

/*-------------------------------------------------------------------------
 * Function:    H5Ldelete_by_idx_async
 *
 * Purpose:     Asynchronous version of H5Ldelete_by_idx
 *
 * Return:      Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Ldelete_by_idx_async(const char *app_file, const char *app_func, unsigned app_line, hid_t loc_id,
                       const char *group_name, H5_index_t idx_type, H5_iter_order_t order, hsize_t n,
                       hid_t lapl_id, hid_t es_id)
{
    H5VL_object_t *vol_obj   = NULL;            /* Object for loc_id */
    void          *token     = NULL;            /* Request token for async operation        */
    void         **token_ptr = H5_REQUEST_NULL; /* Pointer to request token for async operation        */
    herr_t         ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Set up request token pointer for asynchronous operation */
    if (H5ES_NONE != es_id)
        token_ptr = &token; /* Point at token for VOL connector to set up */

    /* Delete a link asynchronously */
    if (H5L__delete_by_idx_api_common(loc_id, group_name, idx_type, order, n, lapl_id, token_ptr, &vol_obj) <
        0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTDELETE, FAIL, "unable to asynchronously delete link");

    /* If a token was created, add the token to the event set */
    if (NULL != token)
        /* clang-format off */
        if (H5ES_insert(es_id, H5VL_OBJ_CONNECTOR(vol_obj), token,
                        H5ARG_TRACE10(__func__, "*s*sIui*sIiIohii", app_file, app_func, app_line, loc_id, group_name, idx_type, order, n, lapl_id, es_id)) < 0)
            /* clang-format on */
            HGOTO_ERROR(H5E_LINK, H5E_CANTINSERT, FAIL, "can't insert token into event set");

done:
    FUNC_LEAVE_API(ret_value)
} /* H5Ldelete_by_idx_async() */

/*-------------------------------------------------------------------------
 * Function:	H5Lget_val
 *
 * 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
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lget_val(hid_t loc_id, const char *name, void *buf /*out*/, size_t size, hid_t lapl_id)
{
    H5VL_object_t       *vol_obj = NULL;      /* object of loc_id */
    H5VL_link_get_args_t vol_cb_args;         /* Arguments to VOL callback */
    H5VL_loc_params_t    loc_params;          /* Location parameters for object access */
    herr_t               ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check arguments */
    if (!name || !*name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no name specified");

    /* Verify access property list and set up collective metadata if appropriate */
    if (H5CX_set_apl(&lapl_id, H5P_CLS_LACC, loc_id, false) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTSET, FAIL, "can't set access property list info");

    /* Set up location struct */
    loc_params.type                         = H5VL_OBJECT_BY_NAME;
    loc_params.obj_type                     = H5I_get_type(loc_id);
    loc_params.loc_data.loc_by_name.name    = name;
    loc_params.loc_data.loc_by_name.lapl_id = lapl_id;

    /* Get the VOL object */
    if (NULL == (vol_obj = H5VL_vol_object(loc_id)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid location identifier");

    /* Set up VOL callback arguments */
    vol_cb_args.op_type               = H5VL_LINK_GET_VAL;
    vol_cb_args.args.get_val.buf      = buf;
    vol_cb_args.args.get_val.buf_size = size;

    /* Get the link value */
    if (H5VL_link_get(vol_obj, &loc_params, &vol_cb_args, H5P_DATASET_XFER_DEFAULT, H5_REQUEST_NULL) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTGET, FAIL, "unable to get link value for '%s'", name);

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

/*-------------------------------------------------------------------------
 * Function:	H5Lget_val_by_idx
 *
 * Purpose:	Returns the link value of a link, according to the order of
 *              an index.  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
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lget_val_by_idx(hid_t loc_id, const char *group_name, H5_index_t idx_type, H5_iter_order_t order, hsize_t n,
                  void *buf /*out*/, size_t size, hid_t lapl_id)
{
    H5VL_object_t       *vol_obj = NULL;      /* object of loc_id */
    H5VL_link_get_args_t vol_cb_args;         /* Arguments to VOL callback */
    H5VL_loc_params_t    loc_params;          /* Location parameters for object access */
    herr_t               ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check arguments */
    if (!group_name || !*group_name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no name specified");
    if (idx_type <= H5_INDEX_UNKNOWN || idx_type >= H5_INDEX_N)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid index type specified");
    if (order <= H5_ITER_UNKNOWN || order >= H5_ITER_N)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid iteration order specified");

    /* Verify access property list and set up collective metadata if appropriate */
    if (H5CX_set_apl(&lapl_id, H5P_CLS_LACC, loc_id, false) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTSET, FAIL, "can't set access property list info");

    /* Set up location struct */
    loc_params.type                         = H5VL_OBJECT_BY_IDX;
    loc_params.loc_data.loc_by_idx.name     = group_name;
    loc_params.loc_data.loc_by_idx.idx_type = idx_type;
    loc_params.loc_data.loc_by_idx.order    = order;
    loc_params.loc_data.loc_by_idx.n        = n;
    loc_params.loc_data.loc_by_idx.lapl_id  = lapl_id;
    loc_params.obj_type                     = H5I_get_type(loc_id);

    /* Get the VOL object */
    if (NULL == (vol_obj = H5VL_vol_object(loc_id)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid location identifier");

    /* Set up VOL callback arguments */
    vol_cb_args.op_type               = H5VL_LINK_GET_VAL;
    vol_cb_args.args.get_val.buf      = buf;
    vol_cb_args.args.get_val.buf_size = size;

    /* Get the link value */
    if (H5VL_link_get(vol_obj, &loc_params, &vol_cb_args, H5P_DATASET_XFER_DEFAULT, H5_REQUEST_NULL) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTGET, FAIL, "unable to get link value");

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

/*--------------------------------------------------------------------------
 *  NAME
 *      H5L__exists_api_common
 *  PURPOSE
 *      Common helper routine for sync/async check if an attribute exists
 *  RETURNS
 *      Non-negative on success/Negative on failure
 *
 *--------------------------------------------------------------------------*/
static herr_t
H5L__exists_api_common(hid_t loc_id, const char *name, bool *exists, hid_t lapl_id, void **token_ptr,
                       H5VL_object_t **_vol_obj_ptr)
{
    H5VL_object_t  *tmp_vol_obj = NULL; /* Object for loc_id */
    H5VL_object_t **vol_obj_ptr =
        (_vol_obj_ptr ? _vol_obj_ptr : &tmp_vol_obj); /* Ptr to object ptr for loc_id */
    H5VL_link_specific_args_t vol_cb_args;            /* Arguments to VOL callback */
    H5VL_loc_params_t         loc_params;             /* Location parameters for object access */
    herr_t                    ret_value = SUCCEED;    /* Return value */

    FUNC_ENTER_PACKAGE

    /* Check arguments */
    /* name is verified in H5VL_setup_name_args() */
    if (NULL == exists)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid pointer for link existence");

    /* Set up object access arguments */
    if (H5VL_setup_name_args(loc_id, name, false, lapl_id, vol_obj_ptr, &loc_params) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTSET, FAIL, "can't set object access arguments");

    /* Set up VOL callback arguments */
    vol_cb_args.op_type            = H5VL_LINK_EXISTS;
    vol_cb_args.args.exists.exists = exists;

    /* Check for the existence of the link */
    if (H5VL_link_specific(*vol_obj_ptr, &loc_params, &vol_cb_args, H5P_DATASET_XFER_DEFAULT, token_ptr) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTGET, FAIL, "unable to get link info");

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

/*-------------------------------------------------------------------------
 * Function:    H5Lexists
 *
 * Purpose:     Checks if a link of a given name exists in a group
 *
 * Return:      Success:    true/false/FAIL
 *
 *-------------------------------------------------------------------------
 */
htri_t
H5Lexists(hid_t loc_id, const char *name, hid_t lapl_id)
{
    bool   exists;           /* Flag to indicate if link exists */
    htri_t ret_value = FAIL; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Synchronously check if a link exists */
    exists = false;
    if (H5L__exists_api_common(loc_id, name, &exists, lapl_id, NULL, NULL) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTGET, FAIL, "unable to synchronously check link existence");

    /* Set return value */
    ret_value = (htri_t)exists;

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

/*--------------------------------------------------------------------------
 * Function:    H5Lexists_async
 *
 * Purpose:     Asynchronous version of H5Lexists
 *
 * Return:      Success:    true/false/FAIL
 *
 *--------------------------------------------------------------------------*/
herr_t
H5Lexists_async(const char *app_file, const char *app_func, unsigned app_line, hid_t loc_id, const char *name,
                bool *exists, hid_t lapl_id, hid_t es_id)
{
    H5VL_object_t *vol_obj   = NULL;            /* Object for loc_id */
    void          *token     = NULL;            /* Request token for async operation        */
    void         **token_ptr = H5_REQUEST_NULL; /* Pointer to request token for async operation        */
    herr_t         ret_value = SUCCEED;         /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Set up request token pointer for asynchronous operation */
    if (H5ES_NONE != es_id)
        token_ptr = &token; /* Point at token for VOL connector to set up */

    /* Asynchronously check if a link exists */
    if (H5L__exists_api_common(loc_id, name, exists, lapl_id, token_ptr, &vol_obj) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTGET, FAIL, "unable to asynchronously check link existence");

    /* If a token was created, add the token to the event set */
    if (NULL != token)
        if (H5ES_insert(es_id, H5VL_OBJ_CONNECTOR(vol_obj), token,
                        /* clang-format off */
                        H5ARG_TRACE8(__func__, "*s*sIui*s*bii", app_file, app_func, app_line, loc_id, name, exists, lapl_id, es_id)) < 0)
            /* clang-format on */
            HGOTO_ERROR(H5E_LINK, H5E_CANTINSERT, FAIL, "can't insert token into event set");

done:
    FUNC_LEAVE_API(ret_value)
} /* H5Lexists_async() */

/*-------------------------------------------------------------------------
 * Function:    H5Lget_info2
 *
 * Purpose:     Gets metadata for a link.
 *
 * Return:      Success:    Non-negative with information in LINFO
 *              Failure:    Negative
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lget_info2(hid_t loc_id, const char *name, H5L_info2_t *linfo /*out*/, hid_t lapl_id)
{
    H5VL_object_t       *vol_obj = NULL;      /* object of loc_id */
    H5VL_link_get_args_t vol_cb_args;         /* Arguments to VOL callback */
    H5VL_loc_params_t    loc_params;          /* Location parameters for object access */
    herr_t               ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check arguments */
    if (!name || !*name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no name specified");

    /* Verify access property list and set up collective metadata if appropriate */
    if (H5CX_set_apl(&lapl_id, H5P_CLS_LACC, loc_id, true) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTSET, FAIL, "can't set access property list info");

    /* Set up location struct */
    loc_params.type                         = H5VL_OBJECT_BY_NAME;
    loc_params.obj_type                     = H5I_get_type(loc_id);
    loc_params.loc_data.loc_by_name.name    = name;
    loc_params.loc_data.loc_by_name.lapl_id = lapl_id;

    /* Get the location object */
    if (NULL == (vol_obj = H5VL_vol_object(loc_id)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid location identifier");

    /* Set up VOL callback arguments */
    vol_cb_args.op_type             = H5VL_LINK_GET_INFO;
    vol_cb_args.args.get_info.linfo = linfo;

    /* Get the link information */
    if (H5VL_link_get(vol_obj, &loc_params, &vol_cb_args, H5P_DATASET_XFER_DEFAULT, H5_REQUEST_NULL) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTGET, FAIL, "unable to get link info");

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

/*-------------------------------------------------------------------------
 * Function:    H5Lget_info_by_idx2
 *
 * Purpose:     Gets metadata for a link, according to the order within an
 *              index.
 *
 * Return:      Success:    Non-negative with information in LINFO
 *              Failure:    Negative
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lget_info_by_idx2(hid_t loc_id, const char *group_name, H5_index_t idx_type, H5_iter_order_t order,
                    hsize_t n, H5L_info2_t *linfo /*out*/, hid_t lapl_id)
{
    H5VL_object_t       *vol_obj = NULL;      /* object of loc_id */
    H5VL_link_get_args_t vol_cb_args;         /* Arguments to VOL callback */
    H5VL_loc_params_t    loc_params;          /* Location parameters for object access */
    herr_t               ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check arguments */
    if (!group_name || !*group_name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no name specified");
    if (idx_type <= H5_INDEX_UNKNOWN || idx_type >= H5_INDEX_N)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid index type specified");
    if (order <= H5_ITER_UNKNOWN || order >= H5_ITER_N)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid iteration order specified");

    /* Verify access property list and set up collective metadata if appropriate */
    if (H5CX_set_apl(&lapl_id, H5P_CLS_LACC, loc_id, false) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTSET, FAIL, "can't set access property list info");

    /* Set up location struct */
    loc_params.type                         = H5VL_OBJECT_BY_IDX;
    loc_params.loc_data.loc_by_idx.name     = group_name;
    loc_params.loc_data.loc_by_idx.idx_type = idx_type;
    loc_params.loc_data.loc_by_idx.order    = order;
    loc_params.loc_data.loc_by_idx.n        = n;
    loc_params.loc_data.loc_by_idx.lapl_id  = lapl_id;
    loc_params.obj_type                     = H5I_get_type(loc_id);

    /* Get the location object */
    if (NULL == (vol_obj = H5VL_vol_object(loc_id)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid location identifier");

    /* Set up VOL callback arguments */
    vol_cb_args.op_type             = H5VL_LINK_GET_INFO;
    vol_cb_args.args.get_info.linfo = linfo;

    /* Get the link information */
    if (H5VL_link_get(vol_obj, &loc_params, &vol_cb_args, H5P_DATASET_XFER_DEFAULT, H5_REQUEST_NULL) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTGET, FAIL, "unable to get link info");

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

/*-------------------------------------------------------------------------
 * Function:	H5Lregister
 *
 * Purpose:	Registers a class of user-defined links, or changes the
 *              behavior of an existing class.
 *
 *              The link class passed in will override any existing link
 *              class for the specified link class ID. It must at least
 *              include a H5L_class_t version (which should be
 *              H5L_LINK_CLASS_T_VERS), a link class ID, and a traversal
 *              function.
 *
 * Return:	Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lregister(const H5L_class_t *cls)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check args */
    if (cls == NULL)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid link class");

    /* Check H5L_class_t version number; this is where a function to convert
     * from an outdated version should be called.
     *
     * v0 of the H5L_class_t is only different in the parameters to the
     * traversal callback, which is handled in H5G_traverse_ud()
     * (in src/H5Gtraverse.c), so it's allowed to to pass through here. - QAK, 2018/02/06
     */
    if (cls->version > H5L_LINK_CLASS_T_VERS)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid H5L_class_t version number");
#ifdef H5_NO_DEPRECATED_SYMBOLS
    if (cls->version < H5L_LINK_CLASS_T_VERS)
        HGOTO_ERROR(
            H5E_ARGS, H5E_BADVALUE, FAIL,
            "deprecated H5L_class_t version number (%d) and library built without deprecated symbol support",
            cls->version);
#endif /* H5_NO_DEPRECATED_SYMBOLS */

    if (cls->id < H5L_TYPE_UD_MIN || cls->id > H5L_TYPE_MAX)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid link identification number");
    if (cls->trav_func == NULL)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no traversal function specified");

    /* Do it */
    if (H5L_register(cls) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_NOTREGISTERED, FAIL, "unable to register link type");

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

/*-------------------------------------------------------------------------
 * Function:	H5Lunregister
 *
 * Purpose:	Unregisters a class of user-defined links, preventing them
 *              from being traversed, queried, moved, etc.
 *
 *              A link class can be re-registered using H5Lregister().
 *
 * Return:	Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lunregister(H5L_type_t id)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check args */
    if (id < 0 || id > H5L_TYPE_MAX)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid link type");

    /* Do it */
    if (H5L_unregister(id) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_NOTREGISTERED, FAIL, "unable to unregister link type");

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

/*-------------------------------------------------------------------------
 * Function:    H5Lis_registered
 *
 * Purpose:     Tests whether a user-defined link class has been registered
 *              or not.
 *
 * Return:      true if the link class has been registered
 *              false if it is unregistered
 *              FAIL on error (if the class is not a valid UD class ID)
 *
 *-------------------------------------------------------------------------
 */
htri_t
H5Lis_registered(H5L_type_t id)
{
    bool   is_registered = false;
    htri_t ret_value     = false; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check args */
    if (id < 0 || id > H5L_TYPE_MAX)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid link type id number");

    /* Is the link class already registered? */
    if (H5L_is_registered(id, &is_registered) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_BADTYPE, FAIL, "could not determine registration status of UD link type");

    ret_value = is_registered ? true : false;

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

/*-------------------------------------------------------------------------
 * Function:    H5Lget_name_by_idx
 *
 * Purpose:     Gets name for a link, according to the order within an
 *              index.
 *
 *              Same pattern of behavior as H5Iget_name.
 *
 * Return:      Success:    Non-negative length of name, with information
 *                          in NAME buffer
 *
 *              Failure:    -1
 *
 *-------------------------------------------------------------------------
 */
ssize_t
H5Lget_name_by_idx(hid_t loc_id, const char *group_name, H5_index_t idx_type, H5_iter_order_t order,
                   hsize_t n, char *name /*out*/, size_t size, hid_t lapl_id)
{
    H5VL_object_t       *vol_obj = NULL;     /* object of loc_id */
    H5VL_link_get_args_t vol_cb_args;        /* Arguments to VOL callback */
    H5VL_loc_params_t    loc_params;         /* Location parameters for object access */
    size_t               link_name_len = 0;  /* Length of the link name string */
    ssize_t              ret_value     = -1; /* Return value */

    FUNC_ENTER_API((-1))

    /* If name size is zero, treat as length query and do not write, even a '\0' */
    if (name && size == 0)
        name = NULL;

    /* Check arguments */
    if (!group_name || !*group_name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, (-1), "no name specified");
    if (idx_type <= H5_INDEX_UNKNOWN || idx_type >= H5_INDEX_N)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, (-1), "invalid index type specified");
    if (order <= H5_ITER_UNKNOWN || order >= H5_ITER_N)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, (-1), "invalid iteration order specified");

    /* Verify access property list and set up collective metadata if appropriate */
    if (H5CX_set_apl(&lapl_id, H5P_CLS_LACC, loc_id, true) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTSET, (-1), "can't set access property list info");

    /* Set up location struct */
    loc_params.type                         = H5VL_OBJECT_BY_IDX;
    loc_params.loc_data.loc_by_idx.name     = group_name;
    loc_params.loc_data.loc_by_idx.idx_type = idx_type;
    loc_params.loc_data.loc_by_idx.order    = order;
    loc_params.loc_data.loc_by_idx.n        = n;
    loc_params.loc_data.loc_by_idx.lapl_id  = lapl_id;
    loc_params.obj_type                     = H5I_get_type(loc_id);

    /* Get the VOL object */
    if (NULL == (vol_obj = H5VL_vol_object(loc_id)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, (-1), "invalid location identifier");

    /* Set up VOL callback arguments */
    vol_cb_args.op_type                 = H5VL_LINK_GET_NAME;
    vol_cb_args.args.get_name.name_size = size;
    vol_cb_args.args.get_name.name      = name;
    vol_cb_args.args.get_name.name_len  = &link_name_len;

    /* Get the link information */
    if (H5VL_link_get(vol_obj, &loc_params, &vol_cb_args, H5P_DATASET_XFER_DEFAULT, H5_REQUEST_NULL) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTGET, (-1), "unable to get link name");

    /* Set the return value */
    ret_value = (ssize_t)link_name_len;

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

/*-------------------------------------------------------------------------
 * Function:    H5L__iterate_api_common
 *
 * Purpose:     This is the common function for iterating over links
 *
 * Return:      Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
static herr_t
H5L__iterate_api_common(hid_t group_id, H5_index_t idx_type, H5_iter_order_t order, hsize_t *idx_p,
                        H5L_iterate2_t op, void *op_data, void **token_ptr, H5VL_object_t **_vol_obj_ptr)
{
    H5VL_object_t  *tmp_vol_obj = NULL; /* Object for loc_id */
    H5VL_object_t **vol_obj_ptr =
        (_vol_obj_ptr ? _vol_obj_ptr : &tmp_vol_obj);   /* Ptr to object ptr for loc_id */
    H5VL_link_specific_args_t vol_cb_args;              /* Arguments to VOL callback */
    H5VL_loc_params_t         loc_params;               /* Location parameters for object access */
    H5I_type_t                id_type;                  /* Type of ID */
    herr_t                    ret_value = H5_ITER_CONT; /* Return value */

    FUNC_ENTER_PACKAGE

    /* Check arguments */
    id_type = H5I_get_type(group_id);
    if (!(H5I_GROUP == id_type || H5I_FILE == id_type))
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid argument");
    if (idx_type <= H5_INDEX_UNKNOWN || idx_type >= H5_INDEX_N)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid index type specified");
    if (order <= H5_ITER_UNKNOWN || order >= H5_ITER_N)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid iteration order specified");
    if (!op)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no operator specified");

    /* Set up object access arguments */
    if (H5VL_setup_self_args(group_id, vol_obj_ptr, &loc_params) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTSET, FAIL, "can't set object access arguments");

    /* Set up VOL callback arguments */
    vol_cb_args.op_type                = H5VL_LINK_ITER;
    vol_cb_args.args.iterate.recursive = false;
    vol_cb_args.args.iterate.idx_type  = idx_type;
    vol_cb_args.args.iterate.order     = order;
    vol_cb_args.args.iterate.idx_p     = idx_p;
    vol_cb_args.args.iterate.op        = op;
    vol_cb_args.args.iterate.op_data   = op_data;

    /* Iterate over the links */
    if ((ret_value = H5VL_link_specific(*vol_obj_ptr, &loc_params, &vol_cb_args, H5P_DATASET_XFER_DEFAULT,
                                        token_ptr)) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_BADITER, FAIL, "link iteration failed");

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

/*-------------------------------------------------------------------------
 * Function:    H5Literate2
 *
 * Purpose:     Iterates over links in a group, with user callback routine,
 *              according to the order within an index.
 *
 *              Same pattern of behavior as H5Giterate.
 *
 * Return:      Success:    The return value of the first operator that
 *                          returns non-zero, or zero if all members were
 *                          processed with no operator returning non-zero.
 *
 *              Failure:    Negative if something goes wrong within the
 *                          library, or the negative value returned by one
 *                          of the operators.
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Literate2(hid_t group_id, H5_index_t idx_type, H5_iter_order_t order, hsize_t *idx_p, H5L_iterate2_t op,
            void *op_data)
{
    herr_t ret_value; /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Iterate over links synchronously */
    if ((ret_value = H5L__iterate_api_common(group_id, idx_type, order, idx_p, op, op_data, NULL, NULL)) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_BADITER, FAIL, "synchronous link iteration failed");

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

/*-------------------------------------------------------------------------
 * Function:    H5Literate_async
 *
 * Purpose:     Asynchronous version of H5Literate2
 *
 * Return:      Success:    The return value of the first operator that
 *                          returns non-zero, or zero if all members were
 *                          processed with no operator returning non-zero.
 *
 *              Failure:    Negative if something goes wrong within the
 *                          library, or the negative value returned by one
 *                          of the operators.
 *
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Literate_async(const char *app_file, const char *app_func, unsigned app_line, hid_t group_id,
                 H5_index_t idx_type, H5_iter_order_t order, hsize_t *idx_p, H5L_iterate2_t op, void *op_data,
                 hid_t es_id)
{
    H5VL_object_t *vol_obj   = NULL;            /* Object for loc_id */
    void          *token     = NULL;            /* Request token for async operation        */
    void         **token_ptr = H5_REQUEST_NULL; /* Pointer to request token for async operation        */
    herr_t         ret_value;                   /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Set up request token pointer for asynchronous operation */
    if (H5ES_NONE != es_id)
        token_ptr = &token; /* Point at token for VOL connector to set up */

    /* Iterate over links asynchronously */
    if ((ret_value =
             H5L__iterate_api_common(group_id, idx_type, order, idx_p, op, op_data, token_ptr, &vol_obj)) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_BADITER, FAIL, "asynchronous link iteration failed");

    /* If a token was created, add the token to the event set */
    if (NULL != token)
        /* clang-format off */
        if (H5ES_insert(es_id, H5VL_OBJ_CONNECTOR(vol_obj), token,
                        H5ARG_TRACE10(__func__, "*s*sIuiIiIo*hLI*xi", app_file, app_func, app_line, group_id, idx_type, order, idx_p, op, op_data, es_id)) < 0)
            /* clang-format on */
            HGOTO_ERROR(H5E_LINK, H5E_CANTINSERT, FAIL, "can't insert token into event set");

done:
    FUNC_LEAVE_API(ret_value)
} /* H5Literate_async() */

/*-------------------------------------------------------------------------
 * Function:    H5Literate_by_name2
 *
 * Purpose:     Iterates over links in a group, with user callback routine,
 *              according to the order within an index.
 *
 *              Same pattern of behavior as H5Giterate.
 *
 * Return:      Success:    The return value of the first operator that
 *                          returns non-zero, or zero if all members were
 *                          processed with no operator returning non-zero.
 *
 *              Failure:    Negative if something goes wrong within the
 *                          library, or the negative value returned by one
 *                          of the operators.
 *
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Literate_by_name2(hid_t loc_id, const char *group_name, H5_index_t idx_type, H5_iter_order_t order,
                    hsize_t *idx_p, H5L_iterate2_t op, void *op_data, hid_t lapl_id)
{
    H5VL_object_t            *vol_obj = NULL; /* Object of loc_id */
    H5VL_link_specific_args_t vol_cb_args;    /* Arguments to VOL callback */
    H5VL_loc_params_t         loc_params;     /* Location parameters for object access */
    herr_t                    ret_value;      /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check arguments */
    if (!group_name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "group_name parameter cannot be NULL");
    if (!*group_name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "group_name parameter cannot be an empty string");
    if (idx_type <= H5_INDEX_UNKNOWN || idx_type >= H5_INDEX_N)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid index type specified");
    if (order <= H5_ITER_UNKNOWN || order >= H5_ITER_N)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid iteration order specified");
    if (!op)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no operator specified");

    /* Verify access property list and set up collective metadata if appropriate */
    if (H5CX_set_apl(&lapl_id, H5P_CLS_LACC, loc_id, false) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTSET, FAIL, "can't set access property list info");

    /* Get the location object */
    if (NULL == (vol_obj = H5VL_vol_object(loc_id)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid location identifier");

    /* Set location struct fields */
    loc_params.type                         = H5VL_OBJECT_BY_NAME;
    loc_params.obj_type                     = H5I_get_type(loc_id);
    loc_params.loc_data.loc_by_name.name    = group_name;
    loc_params.loc_data.loc_by_name.lapl_id = lapl_id;

    /* Set up VOL callback arguments */
    vol_cb_args.op_type                = H5VL_LINK_ITER;
    vol_cb_args.args.iterate.recursive = false;
    vol_cb_args.args.iterate.idx_type  = idx_type;
    vol_cb_args.args.iterate.order     = order;
    vol_cb_args.args.iterate.idx_p     = idx_p;
    vol_cb_args.args.iterate.op        = op;
    vol_cb_args.args.iterate.op_data   = op_data;

    /* Iterate over the links */
    if ((ret_value = H5VL_link_specific(vol_obj, &loc_params, &vol_cb_args, H5P_DATASET_XFER_DEFAULT,
                                        H5_REQUEST_NULL)) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_BADITER, FAIL, "link iteration failed");

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

/*-------------------------------------------------------------------------
 * Function:    H5Lvisit2
 *
 * Purpose:     Recursively visit all the links in a group and all
 *              the groups that are linked to from that group.  Links within
 *              each group are visited according to the order within the
 *              specified index (unless the specified index does not exist for
 *              a particular group, then the "name" index is used).
 *
 *              NOTE: Each _link_ reachable from the initial group will only be
 *              visited once.  However, because an object may be reached from
 *              more than one link, the visitation may call the application's
 *              callback with more than one link that points to a particular
 *              _object_.
 *
 * Return:      Success:    The return value of the first operator that
 *                          returns non-zero, or zero if all members were
 *                          processed with no operator returning non-zero.
 *
 *              Failure:    Negative if something goes wrong within the
 *                          library, or the negative value returned by one
 *                          of the operators.
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lvisit2(hid_t group_id, H5_index_t idx_type, H5_iter_order_t order, H5L_iterate2_t op, void *op_data)
{
    H5VL_object_t            *vol_obj = NULL; /* Object of loc_id */
    H5VL_link_specific_args_t vol_cb_args;    /* Arguments to VOL callback */
    H5VL_loc_params_t         loc_params;     /* Location parameters for object access */
    H5I_type_t                id_type;        /* Type of ID */
    herr_t                    ret_value;      /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check args */
    id_type = H5I_get_type(group_id);
    if (!(H5I_GROUP == id_type || H5I_FILE == id_type))
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid argument");
    if (idx_type <= H5_INDEX_UNKNOWN || idx_type >= H5_INDEX_N)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid index type specified");
    if (order <= H5_ITER_UNKNOWN || order >= H5_ITER_N)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid iteration order specified");
    if (!op)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no callback operator specified");

    /* Set location struct fields */
    loc_params.type     = H5VL_OBJECT_BY_SELF;
    loc_params.obj_type = H5I_get_type(group_id);

    /* Get the location object */
    if (NULL == (vol_obj = H5VL_vol_object(group_id)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid location identifier");

    /* Set up VOL callback arguments */
    vol_cb_args.op_type                = H5VL_LINK_ITER;
    vol_cb_args.args.iterate.recursive = true;
    vol_cb_args.args.iterate.idx_type  = idx_type;
    vol_cb_args.args.iterate.order     = order;
    vol_cb_args.args.iterate.idx_p     = NULL;
    vol_cb_args.args.iterate.op        = op;
    vol_cb_args.args.iterate.op_data   = op_data;

    /* Iterate over the links */
    if ((ret_value = H5VL_link_specific(vol_obj, &loc_params, &vol_cb_args, H5P_DATASET_XFER_DEFAULT,
                                        H5_REQUEST_NULL)) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_BADITER, FAIL, "link visitation failed");

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

/*-------------------------------------------------------------------------
 * Function:    H5Lvisit_by_name2
 *
 * Purpose:     Recursively visit all the links in a group and all
 *              the groups that are linked to from that group.  Links within
 *              each group are visited according to the order within the
 *              specified index (unless the specified index does not exist for
 *              a particular group, then the "name" index is used).
 *
 *              NOTE: Each _link_ reachable from the initial group will only be
 *              visited once.  However, because an object may be reached from
 *              more than one link, the visitation may call the application's
 *              callback with more than one link that points to a particular
 *              _object_.
 *
 * Return:      Success:    The return value of the first operator that
 *                          returns non-zero, or zero if all members were
 *                          processed with no operator returning non-zero.
 *
 *              Failure:    Negative if something goes wrong within the
 *                          library, or the negative value returned by one
 *                          of the operators.
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lvisit_by_name2(hid_t loc_id, const char *group_name, H5_index_t idx_type, H5_iter_order_t order,
                  H5L_iterate2_t op, void *op_data, hid_t lapl_id)
{
    H5VL_object_t            *vol_obj = NULL; /* Object of loc_id */
    H5VL_link_specific_args_t vol_cb_args;    /* Arguments to VOL callback */
    H5VL_loc_params_t         loc_params;     /* Location parameters for object access */
    herr_t                    ret_value;      /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Check args */
    if (!group_name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "group_name parameter cannot be NULL");
    if (!*group_name)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "group_name parameter cannot be an empty string");
    if (idx_type <= H5_INDEX_UNKNOWN || idx_type >= H5_INDEX_N)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid index type specified");
    if (order <= H5_ITER_UNKNOWN || order >= H5_ITER_N)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid iteration order specified");
    if (!op)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no callback operator specified");

    /* Verify access property list and set up collective metadata if appropriate */
    if (H5CX_set_apl(&lapl_id, H5P_CLS_LACC, loc_id, false) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTSET, FAIL, "can't set access property list info");

    /* get the location object */
    if (NULL == (vol_obj = H5VL_vol_object(loc_id)))
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "invalid location identifier");

    /* Set location struct fields */
    loc_params.type                         = H5VL_OBJECT_BY_NAME;
    loc_params.obj_type                     = H5I_get_type(loc_id);
    loc_params.loc_data.loc_by_name.name    = group_name;
    loc_params.loc_data.loc_by_name.lapl_id = lapl_id;

    /* Set up VOL callback arguments */
    vol_cb_args.op_type                = H5VL_LINK_ITER;
    vol_cb_args.args.iterate.recursive = true;
    vol_cb_args.args.iterate.idx_type  = idx_type;
    vol_cb_args.args.iterate.order     = order;
    vol_cb_args.args.iterate.idx_p     = NULL;
    vol_cb_args.args.iterate.op        = op;
    vol_cb_args.args.iterate.op_data   = op_data;

    /* Visit the links */
    if ((ret_value = H5VL_link_specific(vol_obj, &loc_params, &vol_cb_args, H5P_DATASET_XFER_DEFAULT,
                                        H5_REQUEST_NULL)) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_BADITER, FAIL, "link visitation failed");

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

/*-------------------------------------------------------------------------
 * Function: H5Lunpack_elink_val
 *
 * Purpose: Given a buffer holding the "link value" from an external link,
 *              gets pointers to the information within the link value buffer.
 *
 *              External link link values contain some flags and
 *              two NULL-terminated strings, one after the other.
 *
 *              The FLAGS value will be filled in and FILENAME and
 *              OBJ_PATH will be set to pointers within ext_linkval (unless
 *              any of these values is NULL).
 *
 *              Using this function on strings that aren't external link
 *              udata buffers can result in segmentation faults.
 *
 * Return: Non-negative on success/ Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5Lunpack_elink_val(const void *_ext_linkval, size_t link_size, unsigned *flags, const char **filename,
                    const char **obj_path)
{
    const uint8_t *ext_linkval = (const uint8_t *)_ext_linkval; /* Pointer to the link value */
    unsigned       lnk_version;                                 /* External link format version */
    unsigned       lnk_flags;                                   /* External link flags */
    size_t         len;                                         /* Length of the filename in the linkval*/
    herr_t         ret_value = SUCCEED;                         /* Return value */

    FUNC_ENTER_API(FAIL)

    /* Sanity check external link buffer */
    if (ext_linkval == NULL)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "not an external link linkval buffer");
    lnk_version = (*ext_linkval >> 4) & 0x0F;
    lnk_flags   = *ext_linkval & 0x0F;
    if (lnk_version > H5L_EXT_VERSION)
        HGOTO_ERROR(H5E_LINK, H5E_CANTDECODE, FAIL, "bad version number for external link");
    if (lnk_flags & (unsigned)~H5L_EXT_FLAGS_ALL)
        HGOTO_ERROR(H5E_LINK, H5E_CANTDECODE, FAIL, "bad flags for external link");
    if (link_size <= 2)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "not a valid external link buffer");

    /* Try to do some error checking.  If the last character in the linkval
     * (the last character of obj_path) isn't NULL, then something's wrong.
     */
    if (ext_linkval[link_size - 1] != '\0')
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "linkval buffer is not NULL-terminated");

    /* We're now guaranteed that strlen won't segfault, since the buffer has
     * at least one NULL in it.
     */
    len = strlen((const char *)ext_linkval + 1);

    /* If the first NULL we found was at the very end of the buffer, then
     * this external link value has no object name and is invalid.
     */
    if ((len + 1) >= (link_size - 1))
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "linkval buffer doesn't contain an object path");

    /* If we got here then the buffer contains (at least) two strings packed
     * in the correct way.  Assume it's correct and return pointers to the
     * filename and object path.
     */
    if (filename)
        *filename = (const char *)ext_linkval + 1;
    if (obj_path)
        *obj_path = ((const char *)ext_linkval + 1) + len + 1; /* Add one for NULL terminator */

    /* Set the flags to return */
    if (flags)
        *flags = lnk_flags;

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