Coverage Report

Created: 2026-03-04 00:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hdf5/src/H5Lexternal.c
Line
Count
Source
1
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2
 * Copyright by The HDF Group.                                               *
3
 * All rights reserved.                                                      *
4
 *                                                                           *
5
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
6
 * terms governing use, modification, and redistribution, is contained in    *
7
 * the LICENSE file, which can be found at the root of the source code       *
8
 * distribution tree, or in https://www.hdfgroup.org/licenses.               *
9
 * If you do not have access to either file, you may request a copy from     *
10
 * help@hdfgroup.org.                                                        *
11
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12
13
/****************/
14
/* Module Setup */
15
/****************/
16
17
#define H5G_FRIEND     /*suppress error about including H5Gpkg   */
18
#include "H5Lmodule.h" /* This source code file is part of the H5L module */
19
20
/***********/
21
/* Headers */
22
/***********/
23
#include "H5private.h"   /* Generic Functions                    */
24
#include "H5Eprivate.h"  /* Error handling                       */
25
#include "H5Fprivate.h"  /* Files                                */
26
#include "H5Gpkg.h"      /* Groups                               */
27
#include "H5Iprivate.h"  /* IDs                                  */
28
#include "H5Lpkg.h"      /* Links                                */
29
#include "H5MMprivate.h" /* Memory management                    */
30
#include "H5Opublic.h"   /* File objects                         */
31
#include "H5Pprivate.h"  /* Property lists                       */
32
#include "H5VLprivate.h" /* Virtual Object Layer                 */
33
34
/****************/
35
/* Local Macros */
36
/****************/
37
38
/* Size of local link name buffer for traversing external links */
39
#define H5L_EXT_TRAVERSE_BUF_SIZE 256
40
41
/******************/
42
/* Local Typedefs */
43
/******************/
44
45
/********************/
46
/* Local Prototypes */
47
/********************/
48
static hid_t   H5L__extern_traverse(const char *link_name, hid_t cur_group, const void *udata,
49
                                    size_t udata_size, hid_t lapl_id, hid_t dxpl_id);
50
static ssize_t H5L__extern_query(const char *link_name, const void *udata, size_t udata_size,
51
                                 void *buf /*out*/, size_t buf_size);
52
53
/*********************/
54
/* Package Variables */
55
/*********************/
56
57
/*****************************/
58
/* Library Private Variables */
59
/*****************************/
60
61
/*******************/
62
/* Local Variables */
63
/*******************/
64
65
/* Default External Link link class */
66
static const H5L_class_t H5L_EXTERN_LINK_CLASS[1] = {{
67
    H5L_LINK_CLASS_T_VERS, /* H5L_class_t version            */
68
    H5L_TYPE_EXTERNAL,     /* Link type id number            */
69
    "external",            /* Link name for debugging        */
70
    NULL,                  /* Creation callback              */
71
    NULL,                  /* Move callback                  */
72
    NULL,                  /* Copy callback                  */
73
    H5L__extern_traverse,  /* The actual traversal function  */
74
    NULL,                  /* Deletion callback              */
75
    H5L__extern_query      /* Query callback                 */
76
}};
77
78
/*-------------------------------------------------------------------------
79
 * Function:  H5L__extern_traverse
80
 *
81
 * Purpose:    Default traversal function for external links. This can
82
 *              be overridden using H5Lregister().
83
 *
84
 *              Given a filename and path packed into the link udata,
85
 *              attempts to open an object within an external file.
86
 *              If the H5L_ELINK_PREFIX_NAME property is set in the
87
 *              link access property list, appends that prefix to the
88
 *              filename being opened.
89
 *
90
 * Return:    ID of the opened object on success/H5I_INVALID_HID on failure
91
 *
92
 *-------------------------------------------------------------------------
93
 */
94
static hid_t
95
H5L__extern_traverse(const char H5_ATTR_UNUSED *link_name, hid_t cur_group, const void *_udata,
96
                     size_t H5_ATTR_UNUSED udata_size, hid_t lapl_id, hid_t H5_ATTR_UNUSED dxpl_id)
97
0
{
98
0
    H5P_genplist_t    *plist;                              /* Property list pointer */
99
0
    H5G_loc_t          root_loc;                           /* Location of root group in external file */
100
0
    H5G_loc_t          loc;                                /* Location of object */
101
0
    H5F_t             *ext_file = NULL;                    /* File struct for external file */
102
0
    const uint8_t     *p        = (const uint8_t *)_udata; /* Pointer into external link buffer */
103
0
    const char        *file_name;                    /* Name of file containing external link's object */
104
0
    const char        *obj_name;                     /* Name external link's object */
105
0
    size_t             fname_len;                    /* Length of external link file name */
106
0
    unsigned           intent;                       /* File access permissions */
107
0
    H5L_elink_cb_t     cb_info;                      /* Callback info struct */
108
0
    hid_t              fapl_id    = H5I_INVALID_HID; /* File access property list for external link's file */
109
0
    void              *ext_obj    = NULL;            /* External link's object */
110
0
    hid_t              ext_obj_id = H5I_INVALID_HID; /* ID for external link's object */
111
0
    H5I_type_t         opened_type;                  /* ID type of external link's object */
112
0
    char              *parent_group_name = NULL;     /* Temporary pointer to group name */
113
0
    char               local_group_name[H5L_EXT_TRAVERSE_BUF_SIZE]; /* Local buffer to hold group name */
114
0
    H5P_genplist_t    *fa_plist;                                    /* File access property list pointer */
115
0
    H5F_close_degree_t fc_degree    = H5F_CLOSE_WEAK;               /* File close degree for target file */
116
0
    char              *elink_prefix = NULL;                         /* Pointer to elink prefix */
117
0
    hid_t              ret_value    = H5I_INVALID_HID;              /* Return value */
118
119
0
    FUNC_ENTER_PACKAGE
120
121
    /* Sanity checks */
122
0
    assert(p);
123
124
    /* Check external link version & flags */
125
0
    if (((*p >> 4) & 0x0F) > H5L_EXT_VERSION)
126
0
        HGOTO_ERROR(H5E_LINK, H5E_CANTDECODE, H5I_INVALID_HID, "bad version number for external link");
127
0
    if ((*p & 0x0F) & ~H5L_EXT_FLAGS_ALL)
128
0
        HGOTO_ERROR(H5E_LINK, H5E_CANTDECODE, H5I_INVALID_HID, "bad flags for external link");
129
0
    p++;
130
131
    /* Gather some information from the external link's user data */
132
0
    file_name = (const char *)p;
133
0
    fname_len = strlen(file_name);
134
0
    obj_name  = (const char *)p + fname_len + 1;
135
136
    /* Get the plist structure */
137
0
    if (NULL == (plist = H5P_object_verify(lapl_id, H5P_LINK_ACCESS, true)))
138
0
        HGOTO_ERROR(H5E_ID, H5E_BADID, H5I_INVALID_HID, "can't find object for ID");
139
140
    /* Get the fapl_id set for lapl_id if any */
141
0
    if (H5P_get(plist, H5L_ACS_ELINK_FAPL_NAME, &fapl_id) < 0)
142
0
        HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, H5I_INVALID_HID, "can't get fapl for links");
143
144
    /* Get the location for the group holding the external link */
145
0
    if (H5G_loc(cur_group, &loc) < 0)
146
0
        HGOTO_ERROR(H5E_LINK, H5E_CANTGET, H5I_INVALID_HID, "can't get object location");
147
148
    /* get the access flags set for lapl_id if any */
149
0
    if (H5P_get(plist, H5L_ACS_ELINK_FLAGS_NAME, &intent) < 0)
150
0
        HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, H5I_INVALID_HID, "can't get elink file access flags");
151
152
    /* get the file access mode flags for the parent file, if they were not set
153
     * on lapl_id */
154
0
    if (intent == H5F_ACC_DEFAULT)
155
0
        intent = H5F_INTENT(loc.oloc->file);
156
157
0
    if ((fapl_id == H5P_DEFAULT) && ((fapl_id = H5F_get_access_plist(loc.oloc->file, false)) < 0))
158
0
        HGOTO_ERROR(H5E_LINK, H5E_CANTGET, H5I_INVALID_HID, "can't get parent's file access property list");
159
160
    /* Get callback_info */
161
0
    if (H5P_get(plist, H5L_ACS_ELINK_CB_NAME, &cb_info) < 0)
162
0
        HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, H5I_INVALID_HID, "can't get elink callback info");
163
164
    /* Get file access property list */
165
0
    if (NULL == (fa_plist = H5P_object_verify(fapl_id, H5P_FILE_ACCESS, true)))
166
0
        HGOTO_ERROR(H5E_ID, H5E_BADID, H5I_INVALID_HID, "can't find object for ID");
167
168
    /* Make callback if it exists */
169
0
    if (cb_info.func) {
170
0
        const char *parent_file_name;   /* Parent file name */
171
0
        size_t      group_name_len = 0; /* Length of parent group name */
172
173
        /* Get parent file name */
174
0
        parent_file_name = H5F_OPEN_NAME(loc.oloc->file);
175
176
        /* Query length of parent group name */
177
0
        if (H5G_get_name(&loc, NULL, (size_t)0, &group_name_len, NULL) < 0)
178
0
            HGOTO_ERROR(H5E_LINK, H5E_CANTGET, H5I_INVALID_HID, "unable to retrieve length of group name");
179
180
        /* Account for null terminator */
181
0
        group_name_len++;
182
183
        /* Check if we need to allocate larger buffer */
184
0
        if (group_name_len > sizeof(local_group_name)) {
185
0
            if (NULL == (parent_group_name = (char *)H5MM_malloc(group_name_len)))
186
0
                HGOTO_ERROR(H5E_LINK, H5E_CANTALLOC, H5I_INVALID_HID,
187
0
                            "can't allocate buffer to hold group name, group_name_len = %zu", group_name_len);
188
0
        } /* end if */
189
0
        else
190
0
            parent_group_name = local_group_name;
191
192
        /* Get parent group name */
193
0
        if (H5G_get_name(&loc, parent_group_name, group_name_len, NULL, NULL) < 0)
194
0
            HGOTO_ERROR(H5E_LINK, H5E_CANTGET, H5I_INVALID_HID, "unable to retrieve group name");
195
196
        /* Prepare & restore library for user callback */
197
0
        H5_BEFORE_USER_CB(FAIL)
198
0
            {
199
0
                ret_value = (cb_info.func)(parent_file_name, parent_group_name, file_name, obj_name, &intent,
200
0
                                           fapl_id, cb_info.user_data);
201
0
            }
202
0
        H5_AFTER_USER_CB(FAIL)
203
0
        if (ret_value < 0)
204
0
            HGOTO_ERROR(H5E_LINK, H5E_CALLBACK, H5I_INVALID_HID, "traversal operator failed");
205
206
        /* Check access flags */
207
0
        if ((intent & H5F_ACC_TRUNC) || (intent & H5F_ACC_EXCL))
208
0
            HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, H5I_INVALID_HID, "invalid file open flags");
209
0
    } /* end if */
210
211
    /* Set file close degree for new file to "weak" */
212
0
    if (H5P_set(fa_plist, H5F_ACS_CLOSE_DEGREE_NAME, &fc_degree) < 0)
213
0
        HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, H5I_INVALID_HID, "can't set file close degree");
214
215
    /* Get the current elink prefix */
216
0
    if (H5P_peek(plist, H5L_ACS_ELINK_PREFIX_NAME, &elink_prefix) < 0)
217
0
        HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, H5I_INVALID_HID, "can't get external link prefix");
218
219
    /* Search for the target file */
220
0
    if (H5F_prefix_open_file(false, &ext_file, loc.oloc->file, H5F_PREFIX_ELINK, elink_prefix, file_name,
221
0
                             intent, fapl_id) < 0)
222
0
        HGOTO_ERROR(H5E_LINK, H5E_CANTOPENFILE, H5I_INVALID_HID,
223
0
                    "unable to open external file, external link file name = '%s'", file_name);
224
225
    /* Retrieve the "group location" for the file's root group */
226
0
    if (H5G_root_loc(ext_file, &root_loc) < 0)
227
0
        HGOTO_ERROR(H5E_LINK, H5E_BADVALUE, H5I_INVALID_HID, "unable to create location for file");
228
229
    /* Open the object referenced in the external file */
230
0
    if (NULL == (ext_obj = H5O_open_name(&root_loc, obj_name, &opened_type)))
231
0
        HGOTO_ERROR(H5E_LINK, H5E_CANTOPENOBJ, H5I_INVALID_HID, "unable to open object");
232
233
    /* Get an ID for the external link's object */
234
0
    if ((ext_obj_id = H5VL_wrap_register(opened_type, ext_obj, true)) < 0)
235
0
        HGOTO_ERROR(H5E_ID, H5E_CANTREGISTER, H5I_INVALID_HID, "unable to register external link object");
236
237
    /* Set return value */
238
0
    ret_value = ext_obj_id;
239
240
0
done:
241
    /* XXX (VOL MERGE): Probably also want to consider closing ext_obj here on failures */
242
    /* Release resources */
243
0
    if (fapl_id > 0 && H5I_dec_ref(fapl_id) < 0)
244
0
        HDONE_ERROR(H5E_ID, H5E_CANTRELEASE, H5I_INVALID_HID,
245
0
                    "unable to close ID for file access property list");
246
0
    if (ext_file && H5F_efc_close(loc.oloc->file, ext_file) < 0)
247
0
        HDONE_ERROR(H5E_LINK, H5E_CANTCLOSEFILE, H5I_INVALID_HID, "problem closing external file");
248
0
    if (parent_group_name && parent_group_name != local_group_name)
249
0
        parent_group_name = (char *)H5MM_xfree(parent_group_name);
250
0
    if (ret_value < 0) {
251
        /* Close object if it's open and something failed */
252
0
        if (ext_obj_id >= 0 && H5I_dec_ref(ext_obj_id) < 0)
253
0
            HDONE_ERROR(H5E_ID, H5E_CANTRELEASE, H5I_INVALID_HID, "unable to close ID for external object");
254
0
    } /* end if */
255
256
0
    FUNC_LEAVE_NOAPI(ret_value)
257
0
} /* end H5L__extern_traverse() */
258
259
/*-------------------------------------------------------------------------
260
 * Function:  H5L__extern_query
261
 *
262
 * Purpose:    Default query function for external links. This can
263
 *              be overridden using H5Lregister().
264
 *
265
 *              Returns the size of the link's user data. If a buffer of
266
 *              is provided, copies at most buf_size bytes of the udata
267
 *              into it.
268
 *
269
 * Return:    Size of buffer on success/Negative on failure
270
 *
271
 *-------------------------------------------------------------------------
272
 */
273
static ssize_t
274
H5L__extern_query(const char H5_ATTR_UNUSED *link_name, const void *_udata, size_t udata_size,
275
                  void *buf /*out*/, size_t buf_size)
276
0
{
277
0
    const uint8_t *udata     = (const uint8_t *)_udata; /* Pointer to external link buffer */
278
0
    ssize_t        ret_value = SUCCEED;                 /* Return value */
279
280
0
    FUNC_ENTER_PACKAGE
281
282
    /* Check external link version & flags */
283
0
    if (((*udata >> 4) & 0x0F) != H5L_EXT_VERSION)
284
0
        HGOTO_ERROR(H5E_LINK, H5E_CANTDECODE, FAIL, "bad version number for external link");
285
0
    if ((*udata & 0x0F) & ~H5L_EXT_FLAGS_ALL)
286
0
        HGOTO_ERROR(H5E_LINK, H5E_CANTDECODE, FAIL, "bad flags for external link");
287
288
    /* If the buffer is NULL, skip writing anything in it and just return
289
     * the size needed */
290
0
    if (buf) {
291
0
        if (udata_size < buf_size)
292
0
            buf_size = udata_size;
293
294
        /* Copy the udata verbatim up to buf_size */
295
0
        H5MM_memcpy(buf, udata, buf_size);
296
0
    } /* end if */
297
298
    /* Set return value */
299
0
    ret_value = (ssize_t)udata_size;
300
301
0
done:
302
0
    FUNC_LEAVE_NOAPI(ret_value)
303
0
} /* end H5L__extern_query() */
304
305
/*-------------------------------------------------------------------------
306
 * Function: H5L_register_external
307
 *
308
 * Purpose: Registers default "External Link" link class.
309
 *              Use during library initialization or to restore the default
310
 *              after users change it.
311
 *
312
 * Return: Non-negative on success/ negative on failure
313
 *
314
 *-------------------------------------------------------------------------
315
 */
316
herr_t
317
H5L_register_external(void)
318
1
{
319
1
    herr_t ret_value = SUCCEED; /* Return value */
320
321
1
    FUNC_ENTER_NOAPI(FAIL)
322
323
1
    if (H5L_register(H5L_EXTERN_LINK_CLASS) < 0)
324
0
        HGOTO_ERROR(H5E_LINK, H5E_NOTREGISTERED, FAIL, "unable to register external link class");
325
326
1
done:
327
1
    FUNC_LEAVE_NOAPI(ret_value)
328
1
} /* end H5L_register_external() */