/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 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://support.hdfgroup.org/ftp/HDF5/releases.  *
 * If you do not have access to either file, you may request a copy from     *
 * help@hdfgroup.org.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/*
 * Purpose:     Link callbacks for the native VOL connector
 *
 */

#define H5L_FRIEND              /* Suppress error about including H5Lpkg    */

#include "H5private.h"          /* Generic Functions                        */
#include "H5Eprivate.h"         /* Error handling                           */
#include "H5Gprivate.h"         /* Groups                                   */
#include "H5Iprivate.h"         /* IDs                                      */
#include "H5Lpkg.h"             /* Links                                    */
#include "H5Pprivate.h"         /* Property lists                           */
#include "H5VLprivate.h"        /* Virtual Object Layer                     */

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



/*-------------------------------------------------------------------------
 * Function:    H5VL__native_link_create
 *
 * Purpose:     Handles the link create callback
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5VL__native_link_create(H5VL_link_create_type_t create_type, void *obj,
    const H5VL_loc_params_t *loc_params, hid_t lcpl_id, hid_t H5_ATTR_UNUSED lapl_id,
    hid_t H5_ATTR_UNUSED dxpl_id, void H5_ATTR_UNUSED **req)
{
    H5P_genplist_t   *plist;                     /* Property list pointer */
    herr_t           ret_value = SUCCEED;        /* Return value */

    FUNC_ENTER_PACKAGE

    /* Get the plist structure */
    if(NULL == (plist = (H5P_genplist_t *)H5I_object(lcpl_id)))
        HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, FAIL, "can't find object for ID");

    switch(create_type) {
        case H5VL_LINK_CREATE_HARD:
            {
                H5G_loc_t    cur_loc;
                H5G_loc_t    link_loc;
                void         *cur_obj;
                H5VL_loc_params_t cur_params;

                if(H5P_get(plist, H5VL_PROP_LINK_TARGET, &cur_obj) < 0)
                    HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get property value for current location id")
                if(H5P_get(plist, H5VL_PROP_LINK_TARGET_LOC_PARAMS, &cur_params) < 0)
                    HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get property value for current name")

                if(NULL != cur_obj && H5G_loc_real(cur_obj, cur_params.obj_type, &cur_loc) < 0)
                    HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file or file object")
                if(NULL != obj && H5G_loc_real(obj, loc_params->obj_type, &link_loc) < 0)
                    HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file or file object")

                /* H5Lcreate_hard */
                if(H5VL_OBJECT_BY_NAME == cur_params.type) {
                    H5G_loc_t *cur_loc_p, *link_loc_p;

                    /* Set up current & new location pointers */
                    cur_loc_p = &cur_loc;
                    link_loc_p = &link_loc;
                    if(NULL == cur_obj)
                        cur_loc_p = link_loc_p;
                    else if(NULL == obj)
                        link_loc_p = cur_loc_p;
                    else if(cur_loc_p->oloc->file != link_loc_p->oloc->file)
                        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "source and destination should be in the same file.")

                    /* Create the link */
                    if((ret_value = H5L_create_hard(cur_loc_p, cur_params.loc_data.loc_by_name.name, 
                                                    link_loc_p, loc_params->loc_data.loc_by_name.name, lcpl_id)) < 0)
                        HGOTO_ERROR(H5E_LINK, H5E_CANTINIT, FAIL, "unable to create link")
                } /* end if */
                else { /* H5Olink */
                    /* Link to the object */
                    if(H5L_link(&link_loc, loc_params->loc_data.loc_by_name.name, &cur_loc, lcpl_id) < 0)
                        HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "unable to create link")
                } /* end else */
                break;
            }

        case H5VL_LINK_CREATE_SOFT:
            {
                char        *target_name;
                H5G_loc_t   link_loc;               /* Group location for new link */

                if(H5G_loc_real(obj, loc_params->obj_type, &link_loc) < 0)
                    HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file or file object")

                if(H5P_get(plist, H5VL_PROP_LINK_TARGET_NAME, &target_name) < 0)
                    HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get property value for targe name")

                /* Create the link */
                if((ret_value = H5L_create_soft(target_name, &link_loc, loc_params->loc_data.loc_by_name.name, lcpl_id)) < 0)
                    HGOTO_ERROR(H5E_LINK, H5E_CANTINIT, FAIL, "unable to create link")
                break;
            }

        case H5VL_LINK_CREATE_UD:
            {
                H5G_loc_t   link_loc;               /* Group location for new link */
                H5L_type_t link_type;
                void *udata;
                size_t udata_size;

                if(H5G_loc_real(obj, loc_params->obj_type, &link_loc) < 0)
                    HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file or file object")

                if(H5P_get(plist, H5VL_PROP_LINK_TYPE, &link_type) < 0)
                    HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get property value for link type")
                if(H5P_get(plist, H5VL_PROP_LINK_UDATA, &udata) < 0)
                    HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get property value for udata")
                if(H5P_get(plist, H5VL_PROP_LINK_UDATA_SIZE, &udata_size) < 0)
                    HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get property value for udata size")

                /* Create link */
                if(H5L__create_ud(&link_loc, loc_params->loc_data.loc_by_name.name, udata, udata_size, 
                                 link_type, lcpl_id) < 0)
                    HGOTO_ERROR(H5E_LINK, H5E_CANTINIT, FAIL, "unable to create link")
                break;
            }

        default:
            HGOTO_ERROR(H5E_LINK, H5E_CANTINIT, FAIL, "invalid link creation call")
    } /* end switch */

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


/*-------------------------------------------------------------------------
 * Function:    H5VL__native_link_copy
 *
 * Purpose:     Handles the link copy callback
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5VL__native_link_copy(void *src_obj, const H5VL_loc_params_t *loc_params1, 
    void *dst_obj, const H5VL_loc_params_t *loc_params2, hid_t lcpl_id,
    hid_t H5_ATTR_UNUSED lapl_id, hid_t H5_ATTR_UNUSED dxpl_id,
    void H5_ATTR_UNUSED **req)
{
    H5G_loc_t   src_loc, *src_loc_p;
    H5G_loc_t   dst_loc, *dst_loc_p;
    herr_t      ret_value = SUCCEED;        /* Return value */

    FUNC_ENTER_PACKAGE

    if(NULL != src_obj && H5G_loc_real(src_obj, loc_params1->obj_type, &src_loc) < 0)
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file or file object")
    if(NULL != dst_obj && H5G_loc_real(dst_obj, loc_params2->obj_type, &dst_loc) < 0)
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file or file object")

    /* Set up src & dst location pointers */
    src_loc_p = &src_loc;
    dst_loc_p = &dst_loc;
    if(NULL == src_obj)
        src_loc_p = dst_loc_p;
    else if(NULL == dst_obj)
        dst_loc_p = src_loc_p;

    /* Copy the link */
    if(H5L_move(src_loc_p, loc_params1->loc_data.loc_by_name.name, 
                dst_loc_p, loc_params2->loc_data.loc_by_name.name, 
                TRUE, lcpl_id) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTCOPY, FAIL, "unable to copy link")

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


/*-------------------------------------------------------------------------
 * Function:    H5VL__native_link_move
 *
 * Purpose:     Handles the link move callback
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5VL__native_link_move(void *src_obj, const H5VL_loc_params_t *loc_params1, 
    void *dst_obj, const H5VL_loc_params_t *loc_params2, hid_t lcpl_id,
    hid_t H5_ATTR_UNUSED lapl_id, hid_t H5_ATTR_UNUSED dxpl_id,
    void H5_ATTR_UNUSED **req)
{
    H5G_loc_t   src_loc, *src_loc_p;
    H5G_loc_t   dst_loc, *dst_loc_p;
    herr_t      ret_value = SUCCEED;        /* Return value */

    FUNC_ENTER_PACKAGE

    if(NULL != src_obj && H5G_loc_real(src_obj, loc_params1->obj_type, &src_loc) < 0)
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file or file object")
    if(NULL != dst_obj && H5G_loc_real(dst_obj, loc_params2->obj_type, &dst_loc) < 0)
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file or file object")

    /* Set up src & dst location pointers */
    src_loc_p = &src_loc;
    dst_loc_p = &dst_loc;
    if(NULL == src_obj)
        src_loc_p = dst_loc_p;
    else if(NULL == dst_obj)
        dst_loc_p = src_loc_p;

    /* Move the link */
    if(H5L_move(src_loc_p, loc_params1->loc_data.loc_by_name.name, 
                dst_loc_p, loc_params2->loc_data.loc_by_name.name, 
                FALSE, lcpl_id) < 0)
        HGOTO_ERROR(H5E_LINK, H5E_CANTMOVE, FAIL, "unable to move link")

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


/*-------------------------------------------------------------------------
 * Function:    H5VL__native_link_get
 *
 * Purpose:     Handles the link get callback
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5VL__native_link_get(void *obj, const H5VL_loc_params_t *loc_params, H5VL_link_get_t get_type, 
    hid_t H5_ATTR_UNUSED dxpl_id, void H5_ATTR_UNUSED **req, va_list arguments)
{
    H5G_loc_t   loc;
    herr_t      ret_value = SUCCEED;    /* Return value */

    FUNC_ENTER_PACKAGE

    if(H5G_loc_real(obj, loc_params->obj_type, &loc) < 0)
        HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file or file object")

    switch(get_type) {
        /* H5Lget_info/H5Lget_info_by_idx */
        case H5VL_LINK_GET_INFO:
            {
                H5L_info_t *linfo  = HDva_arg(arguments, H5L_info_t *);

                /* Get the link information */
                if(loc_params->type == H5VL_OBJECT_BY_NAME) { /* H5Lget_info */
                    if(H5L_get_info(&loc, loc_params->loc_data.loc_by_name.name, linfo) < 0)
                        HGOTO_ERROR(H5E_LINK, H5E_NOTFOUND, FAIL, "unable to get link info")
                } /* end if */
                else if(loc_params->type == H5VL_OBJECT_BY_IDX) { /* H5Lget_info_by_idx */
                    if(H5L_get_info_by_idx(&loc, loc_params->loc_data.loc_by_idx.name, loc_params->loc_data.loc_by_idx.idx_type,
                            loc_params->loc_data.loc_by_idx.order, loc_params->loc_data.loc_by_idx.n, linfo) < 0)
                        HGOTO_ERROR(H5E_LINK, H5E_NOTFOUND, FAIL, "unable to get link info")
                } /* end else-if */
                else
                    HGOTO_ERROR(H5E_LINK, H5E_NOTFOUND, FAIL, "unable to get link info")
                break;
            }

        /* H5Lget_name_by_idx */
        case H5VL_LINK_GET_NAME:
            {
                char       *name   = HDva_arg(arguments, char *);
                size_t      size   = HDva_arg(arguments, size_t);
                ssize_t    *ret    = HDva_arg(arguments, ssize_t *);

                /* Get the link name */
                if((*ret = H5L_get_name_by_idx(&loc, loc_params->loc_data.loc_by_idx.name, loc_params->loc_data.loc_by_idx.idx_type,
                            loc_params->loc_data.loc_by_idx.order, loc_params->loc_data.loc_by_idx.n, name, size)) < 0)
                    HGOTO_ERROR(H5E_LINK, H5E_NOTFOUND, FAIL, "unable to get link info")

                break;
            }

        /* H5Lget_val/H5Lget_val_by_idx */
        case H5VL_LINK_GET_VAL:
            {
                void       *buf    = HDva_arg(arguments, void *);
                size_t     size    = HDva_arg(arguments, size_t);

                /* Get the link information */
                if(loc_params->type == H5VL_OBJECT_BY_NAME) { /* H5Lget_val */
                    if(H5L_get_val(&loc, loc_params->loc_data.loc_by_name.name, buf, size) < 0)
                        HGOTO_ERROR(H5E_LINK, H5E_NOTFOUND, FAIL, "unable to get link value")
                }
                else if(loc_params->type == H5VL_OBJECT_BY_IDX) { /* H5Lget_val_by_idx */

                    if(H5L_get_val_by_idx(&loc, loc_params->loc_data.loc_by_idx.name, loc_params->loc_data.loc_by_idx.idx_type,
                            loc_params->loc_data.loc_by_idx.order, loc_params->loc_data.loc_by_idx.n, buf, size) < 0)
                        HGOTO_ERROR(H5E_LINK, H5E_NOTFOUND, FAIL, "unable to get link val")                    
                }
                else
                    HGOTO_ERROR(H5E_LINK, H5E_NOTFOUND, FAIL, "unable to get link val")

                break;
            }

        default:
            HGOTO_ERROR(H5E_VOL, H5E_CANTGET, FAIL, "can't get this type of information from link")
    } /* end switch */

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


/*-------------------------------------------------------------------------
 * Function:    H5VL__native_link_specific
 *
 * Purpose:     Handles the link specific callback
 *
 * Return:      SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5VL__native_link_specific(void *obj, const H5VL_loc_params_t *loc_params, H5VL_link_specific_t specific_type, 
    hid_t H5_ATTR_UNUSED dxpl_id, void H5_ATTR_UNUSED **req, va_list arguments)
{
    herr_t      ret_value = SUCCEED;    /* Return value */

    FUNC_ENTER_PACKAGE

    switch(specific_type) {
        case H5VL_LINK_EXISTS:
            {
                htri_t *ret = HDva_arg(arguments, htri_t *);
                H5G_loc_t loc;

                if(H5G_loc_real(obj, loc_params->obj_type, &loc) < 0)
                    HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file or file object")

                /* Check for the existence of the link */
                if((*ret = H5L_exists(&loc, loc_params->loc_data.loc_by_name.name)) < 0)
                    HGOTO_ERROR(H5E_LINK, H5E_NOTFOUND, FAIL, "unable to specific link info")
                break;
            }

        case H5VL_LINK_ITER:
            {
                H5G_loc_t loc;
                hbool_t recursive       = (hbool_t)HDva_arg(arguments, unsigned);
                H5_index_t idx_type     = (H5_index_t)HDva_arg(arguments, int); /* enum work-around */
                H5_iter_order_t order   = (H5_iter_order_t)HDva_arg(arguments, int); /* enum work-around */
                hsize_t *idx_p          = HDva_arg(arguments, hsize_t *);
                H5L_iterate_t op        = HDva_arg(arguments, H5L_iterate_t);
                void *op_data           = HDva_arg(arguments, void *);

                /* Get the location */
                if(H5G_loc_real(obj, loc_params->obj_type, &loc) < 0)
                    HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a location")

                /* Visit or iterate over the links */
                if(loc_params->type == H5VL_OBJECT_BY_SELF) {
                    if(recursive) {
                        /* H5Lvisit */
                        if((ret_value = H5G_visit(&loc, ".", idx_type, order, op, op_data)) < 0)
                            HGOTO_ERROR(H5E_LINK, H5E_BADITER, FAIL, "link visitation failed")
                    } /* end if */
                    else {
                        /* H5Literate */
                        if((ret_value = H5L_iterate(&loc, ".", idx_type, order, idx_p, op, op_data)) < 0)
                            HGOTO_ERROR(H5E_LINK, H5E_BADITER, FAIL, "error iterating over links")
                    } /* end else */
                } /* end if */
                else if(loc_params->type == H5VL_OBJECT_BY_NAME) {
                    if(recursive) {
                        /* H5Lvisit_by_name */
                        if((ret_value = H5G_visit(&loc, loc_params->loc_data.loc_by_name.name, idx_type, order, op, op_data)) < 0)
                            HGOTO_ERROR(H5E_LINK, H5E_BADITER, FAIL, "link visitation failed")
                    } /* end if */
                    else {
                        /* H5Literate_by_name */
                        if((ret_value = H5L_iterate(&loc, loc_params->loc_data.loc_by_name.name, idx_type, order, idx_p, op, op_data)) < 0)
                            HGOTO_ERROR(H5E_LINK, H5E_BADITER, FAIL, "error iterating over links")
                    } /* end else */
                } /* end else-if */
                else
                    HGOTO_ERROR(H5E_LINK, H5E_UNSUPPORTED, FAIL, "unknown link iterate params")

                break;
            }

        case H5VL_LINK_DELETE:
            {
                H5G_loc_t loc;

                if(H5G_loc_real(obj, loc_params->obj_type, &loc) < 0)
                    HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a file or file object")

                /* Unlink */
                if(loc_params->type == H5VL_OBJECT_BY_NAME) { /* H5Ldelete */
                    if(H5L_delete(&loc, loc_params->loc_data.loc_by_name.name) < 0)
                        HGOTO_ERROR(H5E_LINK, H5E_CANTDELETE, FAIL, "unable to delete link")
                } /* end if */
                else if(loc_params->type == H5VL_OBJECT_BY_IDX) { /* H5Ldelete_by_idx */
                    if(H5L_delete_by_idx(&loc, loc_params->loc_data.loc_by_idx.name, loc_params->loc_data.loc_by_idx.idx_type,
                            loc_params->loc_data.loc_by_idx.order, loc_params->loc_data.loc_by_idx.n) < 0)
                        HGOTO_ERROR(H5E_LINK, H5E_CANTDELETE, FAIL, "unable to delete link")
                } /* end else-if */
                else
                    HGOTO_ERROR(H5E_LINK, H5E_CANTDELETE, FAIL, "unable to delete link")
                break;
            }

        default:
            HGOTO_ERROR(H5E_VOL, H5E_UNSUPPORTED, FAIL, "invalid specific operation")
    } /* end switch */

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