Coverage Report

Created: 2024-06-18 06:29

/src/hdf5/src/H5Gtraverse.c
Line
Count
Source (jump to first uncovered line)
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 COPYING 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
 *
15
 * Created:   H5Gtraverse.c
16
 *
17
 * Purpose:   Functions for traversing group hierarchy
18
 *
19
 *-------------------------------------------------------------------------
20
 */
21
22
/****************/
23
/* Module Setup */
24
/****************/
25
26
#include "H5Gmodule.h" /* This source code file is part of the H5G module */
27
28
/***********/
29
/* Headers */
30
/***********/
31
#include "H5private.h"   /* Generic Functions                        */
32
#include "H5CXprivate.h" /* API Contexts                             */
33
#include "H5Dprivate.h"  /* Datasets                                 */
34
#include "H5Eprivate.h"  /* Error handling                           */
35
#include "H5Fprivate.h"  /* File access                              */
36
#include "H5Gpkg.h"      /* Groups                                   */
37
#include "H5Iprivate.h"  /* IDs                                      */
38
#include "H5Lprivate.h"  /* Links                                    */
39
#include "H5MMprivate.h" /* Memory management                        */
40
#include "H5Ppublic.h"   /* Property Lists                           */
41
#include "H5WBprivate.h" /* Wrapped Buffers                          */
42
43
/****************/
44
/* Local Macros */
45
/****************/
46
47
/******************/
48
/* Local Typedefs */
49
/******************/
50
51
/* User data for path traversal routine */
52
typedef struct {
53
    /* down */
54
    bool chk_exists; /* Flag to indicate we are checking if object exists */
55
56
    /* up */
57
    H5G_loc_t *obj_loc; /* Object location */
58
    bool       exists;  /* Indicate if object exists */
59
} H5G_trav_slink_t;
60
61
/********************/
62
/* Package Typedefs */
63
/********************/
64
65
/********************/
66
/* Local Prototypes */
67
/********************/
68
static herr_t H5G__traverse_slink_cb(H5G_loc_t *grp_loc, const char *name, const H5O_link_t *lnk,
69
                                     H5G_loc_t *obj_loc, void *_udata /*in,out*/,
70
                                     H5G_own_loc_t *own_loc /*out*/);
71
static herr_t H5G__traverse_ud(const H5G_loc_t *grp_loc, const H5O_link_t *lnk, H5G_loc_t *obj_loc /*in,out*/,
72
                               unsigned target, bool *obj_exists);
73
static herr_t H5G__traverse_slink(const H5G_loc_t *grp_loc, const H5O_link_t *lnk,
74
                                  H5G_loc_t *obj_loc /*in,out*/, unsigned target, bool *obj_exists);
75
static herr_t H5G__traverse_real(const H5G_loc_t *loc, const char *name, unsigned target, H5G_traverse_t op,
76
                                 void *op_data);
77
78
/*********************/
79
/* Package Variables */
80
/*********************/
81
82
/*****************************/
83
/* Library Private Variables */
84
/*****************************/
85
86
/*******************/
87
/* Local Variables */
88
/*******************/
89
90
/*-------------------------------------------------------------------------
91
 * Function:  H5G__traverse_slink_cb
92
 *
93
 * Purpose: Callback for soft link traversal.  This routine sets the
94
 *              correct information for the object location.
95
 *
96
 * Return:  Non-negative on success/Negative on failure
97
 *
98
 *-------------------------------------------------------------------------
99
 */
100
static herr_t
101
H5G__traverse_slink_cb(H5G_loc_t H5_ATTR_UNUSED *grp_loc, const char H5_ATTR_UNUSED *name,
102
                       const H5O_link_t H5_ATTR_UNUSED *lnk, H5G_loc_t *obj_loc, void *_udata /*in,out*/,
103
                       H5G_own_loc_t *own_loc /*out*/)
104
0
{
105
0
    H5G_trav_slink_t *udata     = (H5G_trav_slink_t *)_udata; /* User data passed in */
106
0
    herr_t            ret_value = SUCCEED;                    /* Return value */
107
108
0
    FUNC_ENTER_PACKAGE
109
110
    /* Check for dangling soft link */
111
0
    if (obj_loc == NULL) {
112
0
        if (udata->chk_exists)
113
0
            udata->exists = false;
114
0
        else
115
0
            HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "component not found");
116
0
    } /* end if */
117
0
    else {
118
        /* Copy new location information for resolved object */
119
0
        H5O_loc_copy_deep(udata->obj_loc->oloc, obj_loc->oloc);
120
121
        /* Indicate that the object exists */
122
0
        udata->exists = true;
123
0
    } /* end else */
124
125
0
done:
126
    /* Indicate that this callback didn't take ownership of the group *
127
     * location for the object */
128
0
    *own_loc = H5G_OWN_NONE;
129
130
0
    FUNC_LEAVE_NOAPI(ret_value)
131
0
} /* end H5G__traverse_slink_cb() */
132
133
/*-------------------------------------------------------------------------
134
 * Function:  H5G__traverse_ud
135
 *
136
 * Purpose: Callback for user-defined link traversal.  Sets up a
137
 *              location ID and passes it to the user traversal callback.
138
 *
139
 * Return:  Non-negative on success/Negative on failure
140
 *
141
 *-------------------------------------------------------------------------
142
 */
143
static herr_t
144
H5G__traverse_ud(const H5G_loc_t *grp_loc /*in,out*/, const H5O_link_t *lnk, H5G_loc_t *obj_loc /*in,out*/,
145
                 unsigned target, bool *obj_exists)
146
0
{
147
0
    const H5L_class_t *link_class;     /* User-defined link class */
148
0
    hid_t              cb_return = -1; /* The ID the user-defined callback returned */
149
0
    H5G_loc_t          grp_loc_copy;
150
0
    H5G_name_t         grp_path_copy;
151
0
    H5O_loc_t          grp_oloc_copy;
152
0
    H5G_loc_t          new_loc; /* Group location for newly opened external object */
153
0
    H5G_t             *grp;
154
0
    hid_t              cur_grp   = (-1);
155
0
    herr_t             ret_value = SUCCEED; /* Return value */
156
157
0
    FUNC_ENTER_PACKAGE
158
159
    /* Sanity check */
160
0
    assert(grp_loc);
161
0
    assert(lnk);
162
0
    assert(lnk->type >= H5L_TYPE_UD_MIN);
163
0
    assert(obj_loc);
164
165
    /* Get the link class for this type of link. */
166
0
    if (NULL == (link_class = H5L_find_class(lnk->type)))
167
0
        HGOTO_ERROR(H5E_SYM, H5E_NOTREGISTERED, FAIL, "unable to get UD link class");
168
169
    /* Set up location for user-defined callback.  Use a copy of our current
170
     * grp_loc. */
171
0
    grp_loc_copy.path = &grp_path_copy;
172
0
    grp_loc_copy.oloc = &grp_oloc_copy;
173
0
    H5G_loc_reset(&grp_loc_copy);
174
0
    if (H5G_loc_copy(&grp_loc_copy, grp_loc, H5_COPY_DEEP) < 0)
175
0
        HGOTO_ERROR(H5E_SYM, H5E_CANTCOPY, FAIL, "unable to copy object location");
176
177
    /* Create a group ID to pass to the user-defined callback */
178
0
    if (NULL == (grp = H5G_open(&grp_loc_copy)))
179
0
        HGOTO_ERROR(H5E_SYM, H5E_CANTOPENOBJ, FAIL, "unable to open group");
180
0
    if ((cur_grp = H5VL_wrap_register(H5I_GROUP, grp, false)) < 0)
181
0
        HGOTO_ERROR(H5E_SYM, H5E_CANTREGISTER, FAIL, "unable to register group");
182
183
        /* User-defined callback function */
184
#ifndef H5_NO_DEPRECATED_SYMBOLS
185
    /* (Backwardly compatible with v0 H5L_class_t traversal callback) */
186
    if (link_class->version == H5L_LINK_CLASS_T_VERS_0)
187
        cb_return = (((const H5L_class_0_t *)link_class)->trav_func)(lnk->name, cur_grp, lnk->u.ud.udata,
188
                                                                     lnk->u.ud.size, H5CX_get_lapl());
189
    else
190
        cb_return = (link_class->trav_func)(lnk->name, cur_grp, lnk->u.ud.udata, lnk->u.ud.size,
191
                                            H5CX_get_lapl(), H5CX_get_dxpl());
192
#else  /* H5_NO_DEPRECATED_SYMBOLS */
193
0
    cb_return = (link_class->trav_func)(lnk->name, cur_grp, lnk->u.ud.udata, lnk->u.ud.size, H5CX_get_lapl(),
194
0
                                        H5CX_get_dxpl());
195
0
#endif /* H5_NO_DEPRECATED_SYMBOLS */
196
197
    /* Check for failing to locate the object */
198
0
    if (cb_return < 0) {
199
        /* Check if we just needed to know if the object exists */
200
0
        if (target & H5G_TARGET_EXISTS) {
201
            /* Clear any errors from the stack */
202
0
            H5E_clear_stack();
203
204
            /* Indicate that the object doesn't exist */
205
0
            *obj_exists = false;
206
207
            /* Get out now */
208
0
            HGOTO_DONE(SUCCEED);
209
0
        } /* end if */
210
        /* else, we really needed to open the object */
211
0
        else
212
0
            HGOTO_ERROR(H5E_SYM, H5E_BADID, FAIL, "traversal callback returned invalid ID");
213
0
    } /* end if */
214
215
    /* Get the object location information from the ID the user callback returned */
216
0
    if (H5G_loc(cb_return, &new_loc) < 0)
217
0
        HGOTO_ERROR(H5E_SYM, H5E_BADVALUE, FAIL, "unable to get object location from ID");
218
219
    /* Release any previous location information for the object */
220
0
    H5G_loc_free(obj_loc);
221
222
    /* Copy new object's location information */
223
0
    H5G_loc_copy(obj_loc, &new_loc, H5_COPY_DEEP);
224
225
    /* Hold the file open until we free this object header (otherwise the
226
     * object location will be invalidated when the file closes).
227
     */
228
0
    if (H5O_loc_hold_file(obj_loc->oloc) < 0)
229
0
        HGOTO_ERROR(H5E_SYM, H5E_CANTOPENOBJ, FAIL, "unable to hold file open");
230
231
    /* We have a copy of the location and we're holding the file open.
232
     * Close the open ID the user passed back.
233
     */
234
0
    if (H5I_dec_ref(cb_return) < 0)
235
0
        HGOTO_ERROR(H5E_SYM, H5E_CANTRELEASE, FAIL, "unable to close ID from UD callback");
236
0
    cb_return = (hid_t)(-1);
237
238
0
done:
239
    /* Close location given to callback. */
240
0
    if (cur_grp > 0 && H5I_dec_ref(cur_grp) < 0)
241
0
        HDONE_ERROR(H5E_SYM, H5E_CANTRELEASE, FAIL, "unable to close ID for current location");
242
243
0
    if (ret_value < 0 && cb_return > 0 && H5I_dec_ref(cb_return) < 0)
244
0
        HDONE_ERROR(H5E_SYM, H5E_CANTRELEASE, FAIL, "unable to close ID from UD callback");
245
246
0
    FUNC_LEAVE_NOAPI(ret_value)
247
0
} /* end H5G__traverse_ud() */
248
249
/*-------------------------------------------------------------------------
250
 * Function:  H5G__traverse_slink
251
 *
252
 * Purpose: Traverses symbolic link.  The link head appears in the group
253
 *    whose entry is GRP_LOC and the link tail entry is OBJ_LOC.
254
 *
255
 * Return:  Success:  Non-negative, OBJ_LOC will contain information
256
 *        about the object to which the link points
257
 *
258
 *    Failure:  Negative
259
 *
260
 *-------------------------------------------------------------------------
261
 */
262
static herr_t
263
H5G__traverse_slink(const H5G_loc_t *grp_loc, const H5O_link_t *lnk, H5G_loc_t *obj_loc /*in,out*/,
264
                    unsigned target, bool *obj_exists)
265
0
{
266
0
    H5G_trav_slink_t udata;                     /* User data to pass to link traversal callback */
267
0
    H5G_name_t       tmp_obj_path;              /* Temporary copy of object's path */
268
0
    bool             tmp_obj_path_set = false;  /* Flag to indicate that tmp object path is initialized */
269
0
    H5O_loc_t        tmp_grp_oloc;              /* Temporary copy of group entry */
270
0
    H5G_name_t       tmp_grp_path;              /* Temporary copy of group's path */
271
0
    H5G_loc_t        tmp_grp_loc;               /* Temporary copy of group's location */
272
0
    bool             tmp_grp_loc_set = false;   /* Flag to indicate that tmp group location is initialized */
273
0
    herr_t           ret_value       = SUCCEED; /* Return value */
274
275
0
    FUNC_ENTER_PACKAGE
276
277
    /* Sanity check */
278
0
    assert(grp_loc);
279
0
    assert(lnk);
280
0
    assert(lnk->type == H5L_TYPE_SOFT);
281
282
    /* Set up temporary location */
283
0
    tmp_grp_loc.oloc = &tmp_grp_oloc;
284
0
    tmp_grp_loc.path = &tmp_grp_path;
285
286
    /* Portably initialize the temporary objects */
287
0
    H5G_loc_reset(&tmp_grp_loc);
288
0
    H5G_name_reset(&tmp_obj_path);
289
290
    /* Clone the group location, so we can track the names properly */
291
    /* ("tracking the names properly" means to ignore the effects of the
292
     *  link traversal on the object's & group's paths - QAK)
293
     */
294
0
    H5G_loc_copy(&tmp_grp_loc, grp_loc, H5_COPY_DEEP);
295
0
    tmp_grp_loc_set = true;
296
297
    /* Hold the object's group hier. path to restore later */
298
    /* (Part of "tracking the names properly") */
299
0
    H5G_name_copy(&tmp_obj_path, obj_loc->path, H5_COPY_SHALLOW);
300
0
    tmp_obj_path_set = true;
301
302
    /* Set up user data for traversal callback */
303
0
    udata.chk_exists = (target & H5G_TARGET_EXISTS) ? true : false;
304
0
    udata.exists     = false;
305
0
    udata.obj_loc    = obj_loc;
306
307
    /* Traverse the link */
308
0
    if (H5G__traverse_real(&tmp_grp_loc, lnk->u.soft.name, target, H5G__traverse_slink_cb, &udata) < 0)
309
0
        HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "unable to follow symbolic link");
310
311
    /* Pass back information about whether the object exists */
312
0
    *obj_exists = udata.exists;
313
314
0
done:
315
    /* Restore object's group hier. path */
316
0
    if (tmp_obj_path_set) {
317
0
        H5G_name_free(obj_loc->path);
318
0
        H5G_name_copy(obj_loc->path, &tmp_obj_path, H5_COPY_SHALLOW);
319
0
    } /* end if */
320
321
    /* Release cloned copy of group location */
322
0
    if (tmp_grp_loc_set)
323
0
        H5G_loc_free(&tmp_grp_loc);
324
325
0
    FUNC_LEAVE_NOAPI(ret_value)
326
0
} /* end H5G__traverse_slink() */
327
328
/*-------------------------------------------------------------------------
329
 * Function:  H5G__traverse_special
330
 *
331
 * Purpose: Handle traversing special link situations
332
 *
333
 * Return:  Non-negative on success/Negative on failure
334
 *
335
 *-------------------------------------------------------------------------
336
 */
337
herr_t
338
H5G__traverse_special(const H5G_loc_t *grp_loc, const H5O_link_t *lnk, unsigned target, bool last_comp,
339
                      H5G_loc_t *obj_loc, bool *obj_exists)
340
140
{
341
140
    size_t nlinks;              /* # of soft / UD links left to traverse */
342
140
    herr_t ret_value = SUCCEED; /* Return value */
343
344
140
    FUNC_ENTER_PACKAGE
345
346
    /* Sanity check */
347
140
    assert(grp_loc);
348
140
    assert(lnk);
349
140
    assert(obj_loc);
350
351
    /* If we found a symbolic link then we should follow it.  But if this
352
     * is the last component of the name and the H5G_TARGET_SLINK bit of
353
     * TARGET is set then we don't follow it.
354
     */
355
140
    if (H5L_TYPE_SOFT == lnk->type && (0 == (target & H5G_TARGET_SLINK) || !last_comp)) {
356
357
        /* Get the # of soft / UD links left to traverse */
358
0
        if (H5CX_get_nlinks(&nlinks) < 0)
359
0
            HGOTO_ERROR(H5E_LINK, H5E_CANTGET, FAIL, "unable to retrieve # of soft / UD links to traverse");
360
361
        /* Decrement # of links and range check */
362
0
        if ((nlinks)-- <= 0)
363
0
            HGOTO_ERROR(H5E_LINK, H5E_NLINKS, FAIL, "too many links");
364
365
        /* Update the # of links in the API context */
366
0
        if (H5CX_set_nlinks(nlinks) < 0)
367
0
            HGOTO_ERROR(H5E_LINK, H5E_CANTSET, FAIL, "can't update # of soft / UD links to traverse");
368
369
        /* Traverse soft link */
370
0
        if (H5G__traverse_slink(grp_loc, lnk, obj_loc, (target & H5G_TARGET_EXISTS), obj_exists) < 0)
371
0
            HGOTO_ERROR(H5E_LINK, H5E_TRAVERSE, FAIL, "symbolic link traversal failed");
372
0
    } /* end if */
373
374
    /*
375
     * If we found a user-defined link then we should follow it.  But if this
376
     * is the last component of the name and the H5G_TARGET_UDLINK bit of
377
     * TARGET is set then we don't follow it.
378
     */
379
140
    if (lnk->type >= H5L_TYPE_UD_MIN && (0 == (target & H5G_TARGET_UDLINK) || !last_comp)) {
380
381
        /* Get the # of soft / UD links left to traverse */
382
0
        if (H5CX_get_nlinks(&nlinks) < 0)
383
0
            HGOTO_ERROR(H5E_LINK, H5E_CANTGET, FAIL, "unable to retrieve # of soft / UD links to traverse");
384
385
        /* Decrement # of links and range check */
386
0
        if ((nlinks)-- <= 0)
387
0
            HGOTO_ERROR(H5E_LINK, H5E_NLINKS, FAIL, "too many links");
388
389
        /* Update the # of links in the API context */
390
0
        if (H5CX_set_nlinks(nlinks) < 0)
391
0
            HGOTO_ERROR(H5E_LINK, H5E_CANTSET, FAIL, "can't update # of soft / UD links to traverse");
392
393
        /* Traverse user-defined link */
394
0
        if (H5G__traverse_ud(grp_loc, lnk, obj_loc, (target & H5G_TARGET_EXISTS), obj_exists) < 0)
395
0
            HGOTO_ERROR(H5E_LINK, H5E_TRAVERSE, FAIL, "user-defined link traversal failed");
396
0
    } /* end if */
397
398
    /*
399
     * Resolve mount points to the mounted group.  Do not do this step if
400
     * the H5G_TARGET_MOUNT bit of TARGET is set and this is the last
401
     * component of the name.
402
     *
403
     * (If this link is a hard link, try to perform mount point traversal)
404
     *
405
     * (Note that the soft and external link traversal above can change
406
     *  the status of the object (into a hard link), so don't use an 'else'
407
     *  statement here. -QAK)
408
     */
409
140
    if (H5_addr_defined(obj_loc->oloc->addr) && (0 == (target & H5G_TARGET_MOUNT) || !last_comp)) {
410
140
        if (H5F_traverse_mount(obj_loc->oloc /*in,out*/) < 0)
411
0
            HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "mount point traversal failed");
412
140
    } /* end if */
413
414
    /* If the grp_loc is the only thing holding an external file open
415
     * and obj_loc is in the same file, obj_loc should also hold the
416
     * file open so that closing the grp_loc doesn't close the file.
417
     */
418
140
    if (grp_loc->oloc->holding_file && grp_loc->oloc->file == obj_loc->oloc->file)
419
0
        if (H5O_loc_hold_file(obj_loc->oloc) < 0)
420
0
            HGOTO_ERROR(H5E_SYM, H5E_CANTOPENOBJ, FAIL, "unable to hold file open");
421
422
140
done:
423
140
    FUNC_LEAVE_NOAPI(ret_value)
424
140
} /* end H5G__traverse_special() */
425
426
/*-------------------------------------------------------------------------
427
 * Function:  H5G__traverse_real
428
 *
429
 * Purpose: Internal version of path traversal routine
430
 *
431
 * Return:  Success:  Non-negative if name can be fully resolved.
432
 *
433
 *    Failure:  Negative if the name could not be fully
434
 *        resolved.
435
 *
436
 *-------------------------------------------------------------------------
437
 */
438
static herr_t
439
H5G__traverse_real(const H5G_loc_t *_loc, const char *name, unsigned target, H5G_traverse_t op, void *op_data)
440
541
{
441
541
    H5G_loc_t     loc;                    /* Location of start object     */
442
541
    H5O_loc_t     grp_oloc;               /* Object loc. for current group */
443
541
    H5G_name_t    grp_path;               /* Path for current group */
444
541
    H5G_loc_t     grp_loc;                /* Location of group            */
445
541
    H5O_loc_t     obj_oloc;               /* Object found     */
446
541
    H5G_name_t    obj_path;               /* Path for object found  */
447
541
    H5G_loc_t     obj_loc;                /* Location of object           */
448
541
    size_t        nchars;                 /* component name length  */
449
541
    H5O_link_t    lnk;                    /* Link information for object  */
450
541
    bool          link_valid    = false;  /* Flag to indicate that the link information is valid */
451
541
    bool          obj_loc_valid = false;  /* Flag to indicate that the object location is valid */
452
541
    H5G_own_loc_t own_loc = H5G_OWN_NONE; /* Enum to indicate whether callback took ownership of locations*/
453
541
    bool          group_copy = false;     /* Flag to indicate that the group entry is copied */
454
541
    char          comp_buf[1024];         /* Temporary buffer for path components */
455
541
    char         *comp;                   /* Pointer to buffer for path components */
456
541
    H5WB_t       *wb        = NULL;       /* Wrapped buffer for temporary buffer */
457
541
    bool          last_comp = false; /* Flag to indicate that a component is the last component in the name */
458
541
    herr_t        ret_value = SUCCEED; /* Return value */
459
460
541
    FUNC_ENTER_PACKAGE
461
462
    /* Check parameters */
463
541
    assert(_loc);
464
541
    assert(name);
465
541
    assert(op);
466
467
    /*
468
     * Where does the searching start?  For absolute names it starts at the
469
     * root of the file; for relative names it starts at CWG.
470
     */
471
    /* Check if we need to get the root group's entry */
472
541
    if ('/' == *name) {
473
138
        H5G_t *root_grp; /* Temporary pointer to root group of file */
474
475
        /* Look up root group for starting location */
476
138
        root_grp = H5G_rootof(_loc->oloc->file);
477
138
        assert(root_grp);
478
479
        /* Set the location entry to the root group's info */
480
138
        loc.oloc = &(root_grp->oloc);
481
138
        loc.path = &(root_grp->path);
482
138
    } /* end if */
483
403
    else {
484
403
        loc.oloc = _loc->oloc;
485
403
        loc.path = _loc->path;
486
403
    } /* end else */
487
488
    /* Set up group & object locations */
489
541
    grp_loc.oloc = &grp_oloc;
490
541
    grp_loc.path = &grp_path;
491
541
    obj_loc.oloc = &obj_oloc;
492
541
    obj_loc.path = &obj_path;
493
494
#if defined(H5_USING_MEMCHECKER) || !defined(NDEBUG)
495
    /* Clear group location */
496
    if (H5G_loc_reset(&grp_loc) < 0)
497
        HGOTO_ERROR(H5E_SYM, H5E_CANTOPENOBJ, FAIL, "unable to reset location");
498
#endif /* H5_USING_MEMCHECKER */
499
500
    /* Deep copy of the starting location to group location */
501
541
    if (H5G_loc_copy(&grp_loc, &loc, H5_COPY_DEEP) < 0)
502
0
        HGOTO_ERROR(H5E_SYM, H5E_CANTOPENOBJ, FAIL, "unable to copy location");
503
541
    group_copy = true;
504
505
    /* Clear object location */
506
541
    if (H5G_loc_reset(&obj_loc) < 0)
507
0
        HGOTO_ERROR(H5E_SYM, H5E_CANTOPENOBJ, FAIL, "unable to reset location");
508
509
    /* Wrap the local buffer for serialized header info */
510
541
    if (NULL == (wb = H5WB_wrap(comp_buf, sizeof(comp_buf))))
511
0
        HGOTO_ERROR(H5E_SYM, H5E_CANTINIT, FAIL, "can't wrap buffer");
512
513
    /* Get a pointer to a buffer that's large enough  */
514
541
    if (NULL == (comp = (char *)H5WB_actual(wb, (strlen(name) + 1))))
515
0
        HGOTO_ERROR(H5E_SYM, H5E_NOSPACE, FAIL, "can't get actual buffer");
516
517
    /* Traverse the path */
518
802
    while ((name = H5G__component(name, &nchars)) && *name) {
519
403
        const char *s;             /* Temporary string pointer */
520
403
        bool        lookup_status; /* Status from object lookup */
521
403
        bool        obj_exists;    /* Whether the object exists */
522
523
        /*
524
         * Copy the component name into a null-terminated buffer so
525
         * we can pass it down to the other symbol table functions.
526
         */
527
403
        H5MM_memcpy(comp, name, nchars);
528
403
        comp[nchars] = '\0';
529
530
        /*
531
         * The special name `.' is a no-op.
532
         */
533
403
        if ('.' == comp[0] && !comp[1]) {
534
261
            name += nchars;
535
261
            continue;
536
261
        } /* end if */
537
538
        /* Check if this is the last component of the name */
539
142
        if (!((s = H5G__component(name + nchars, NULL)) && *s))
540
142
            last_comp = true;
541
542
        /* If there's valid information in the link, reset it */
543
142
        if (link_valid) {
544
0
            H5O_msg_reset(H5O_LINK_ID, &lnk);
545
0
            link_valid = false;
546
0
        } /* end if */
547
548
        /* Get information for object in current group */
549
142
        lookup_status = false;
550
142
        if (H5G__obj_lookup(grp_loc.oloc, comp, &lookup_status, &lnk /*out*/) < 0)
551
0
            HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "can't look up component");
552
142
        obj_exists = false;
553
554
        /* If the lookup was OK, build object location and traverse special links, etc. */
555
142
        if (lookup_status) {
556
            /* Sanity check link and indicate it's valid */
557
140
            assert(lnk.type >= H5L_TYPE_HARD);
558
140
            assert(!strcmp(comp, lnk.name));
559
140
            link_valid = true;
560
561
            /* Build object location from the link */
562
140
            if (H5G__link_to_loc(&grp_loc, &lnk, &obj_loc) < 0)
563
0
                HGOTO_ERROR(H5E_SYM, H5E_CANTINIT, FAIL, "cannot initialize object location");
564
140
            obj_loc_valid = true;
565
566
            /* Assume object exists */
567
140
            obj_exists = true;
568
569
            /* Perform any special traversals that the link needs */
570
            /* (soft links, user-defined links, file mounting, etc.) */
571
140
            if (H5G__traverse_special(&grp_loc, &lnk, target, last_comp, &obj_loc, &obj_exists) < 0)
572
0
                HGOTO_ERROR(H5E_LINK, H5E_TRAVERSE, FAIL, "special link traversal failed");
573
140
        } /* end if */
574
575
        /* Check for last component in name provided */
576
142
        if (last_comp) {
577
142
            H5O_link_t *cb_lnk; /* Pointer to link info for callback */
578
142
            H5G_loc_t  *cb_loc; /* Pointer to object location for callback */
579
580
            /* Set callback parameters appropriately, based on link being found */
581
142
            if (lookup_status) {
582
140
                cb_lnk = &lnk;
583
140
                if (obj_exists)
584
140
                    cb_loc = &obj_loc;
585
0
                else
586
0
                    cb_loc = NULL;
587
140
            } /* end if */
588
2
            else {
589
2
                assert(!obj_loc_valid);
590
2
                cb_lnk = NULL;
591
2
                cb_loc = NULL;
592
2
            } /* end else */
593
594
            /* Call 'operator' routine */
595
142
            if ((op)(&grp_loc, comp, cb_lnk, cb_loc, op_data, &own_loc) < 0)
596
24
                HGOTO_ERROR(H5E_SYM, H5E_CALLBACK, FAIL, "traversal operator failed");
597
598
118
            HGOTO_DONE(SUCCEED);
599
118
        } /* end if */
600
601
        /* Handle lookup failures now */
602
0
        if (!lookup_status) {
603
            /* If an intermediate group doesn't exist & flag is set, create the group */
604
0
            if (target & H5G_CRT_INTMD_GROUP) {
605
0
                const H5O_ginfo_t  def_ginfo = H5G_CRT_GROUP_INFO_DEF; /* Default group info settings */
606
0
                const H5O_linfo_t  def_linfo = H5G_CRT_LINK_INFO_DEF;  /* Default link info settings */
607
0
                const H5O_pline_t  def_pline = H5O_CRT_PIPELINE_DEF;   /* Default filter pipeline settings */
608
0
                H5O_ginfo_t        par_ginfo; /* Group info settings for parent group */
609
0
                H5O_linfo_t        par_linfo; /* Link info settings for parent group */
610
0
                H5O_pline_t        par_pline; /* Filter pipeline settings for parent group */
611
0
                H5O_linfo_t        tmp_linfo; /* Temporary link info settings */
612
0
                htri_t             exists;    /* Whether a group or link info message exists */
613
0
                const H5O_ginfo_t *ginfo;     /* Group info settings for new group */
614
0
                const H5O_linfo_t *linfo;     /* Link info settings for new group */
615
0
                const H5O_pline_t *pline;     /* Filter pipeline settings for new group */
616
0
                H5G_obj_create_t   gcrt_info; /* Group creation info */
617
0
                H5O_obj_create_t  *ocrt_info; /* Object creation info in op_data */
618
619
                /* Check for the parent group having a group info message */
620
                /* (OK if not found) */
621
0
                if ((exists = H5O_msg_exists(grp_loc.oloc, H5O_GINFO_ID)) < 0)
622
0
                    HGOTO_ERROR(H5E_SYM, H5E_CANTGET, FAIL, "unable to read object header");
623
0
                if (exists) {
624
                    /* Get the group info for parent group */
625
0
                    if (NULL == H5O_msg_read(grp_loc.oloc, H5O_GINFO_ID, &par_ginfo))
626
0
                        HGOTO_ERROR(H5E_SYM, H5E_CANTGET, FAIL, "group info message not present");
627
628
                    /* Use parent group info settings */
629
0
                    ginfo = &par_ginfo;
630
0
                } /* end if */
631
0
                else
632
                    /* Use default group info settings */
633
0
                    ginfo = &def_ginfo;
634
635
                /* Check for the parent group having a link info message */
636
                /* (OK if not found) */
637
                /* Get the link info for parent group */
638
0
                if ((exists = H5G__obj_get_linfo(grp_loc.oloc, &par_linfo)) < 0)
639
0
                    HGOTO_ERROR(H5E_SYM, H5E_CANTGET, FAIL, "unable to read object header");
640
0
                if (exists) {
641
                    /* Only keep the creation order information from the parent
642
                     *  group's link info
643
                     */
644
0
                    H5MM_memcpy(&tmp_linfo, &def_linfo, sizeof(H5O_linfo_t));
645
0
                    tmp_linfo.track_corder = par_linfo.track_corder;
646
0
                    tmp_linfo.index_corder = par_linfo.index_corder;
647
0
                    linfo                  = &tmp_linfo;
648
0
                } /* end if */
649
0
                else
650
                    /* Use default link info settings */
651
0
                    linfo = &def_linfo;
652
653
                /* Check for the parent group having a filter pipeline message */
654
                /* (OK if not found) */
655
0
                if ((exists = H5O_msg_exists(grp_loc.oloc, H5O_PLINE_ID)) < 0)
656
0
                    HGOTO_ERROR(H5E_SYM, H5E_CANTGET, FAIL, "unable to read object header");
657
0
                if (exists) {
658
                    /* Get the filter pipeline for parent group */
659
0
                    if (NULL == H5O_msg_read(grp_loc.oloc, H5O_PLINE_ID, &par_pline))
660
0
                        HGOTO_ERROR(H5E_SYM, H5E_CANTGET, FAIL, "filter pipeline message not present");
661
662
                    /* Use parent filter pipeline settings */
663
0
                    pline = &par_pline;
664
0
                } /* end if */
665
0
                else
666
                    /* Use default filter pipeline settings */
667
0
                    pline = &def_pline;
668
669
                /* Create the intermediate group */
670
0
                gcrt_info.gcpl_id = H5P_GROUP_CREATE_DEFAULT;
671
                /* Propagate the object creation properties when creating intermedidate groups */
672
0
                if ((target & H5G_CRT_OBJ) && (ocrt_info = H5L_OCRT_INFO(op_data)) != NULL) {
673
0
                    if (ocrt_info->obj_type == H5O_TYPE_GROUP)
674
0
                        gcrt_info.gcpl_id = H5G_OBJ_ID(ocrt_info->crt_info);
675
0
                    else if (ocrt_info->obj_type == H5O_TYPE_DATASET)
676
0
                        gcrt_info.gcpl_id = H5D_OBJ_ID(ocrt_info->crt_info);
677
0
                }
678
679
0
                gcrt_info.cache_type = H5G_NOTHING_CACHED;
680
0
                memset(&gcrt_info.cache, 0, sizeof(gcrt_info.cache));
681
0
                if (H5G__obj_create_real(grp_oloc.file, ginfo, linfo, pline, &gcrt_info,
682
0
                                         obj_loc.oloc /*out*/) < 0)
683
0
                    HGOTO_ERROR(H5E_SYM, H5E_CANTINIT, FAIL, "unable to create group entry");
684
685
                /* Insert new group into current group's symbol table */
686
0
                if (H5G__loc_insert(&grp_loc, comp, &obj_loc, H5O_TYPE_GROUP, &gcrt_info) < 0)
687
0
                    HGOTO_ERROR(H5E_SYM, H5E_CANTINSERT, FAIL, "unable to insert intermediate group");
688
689
                /* Decrement refcount on intermediate group's object header in memory */
690
0
                if (H5O_dec_rc_by_loc(obj_loc.oloc) < 0)
691
0
                    HGOTO_ERROR(H5E_SYM, H5E_CANTDEC, FAIL,
692
0
                                "unable to decrement refcount on newly created object");
693
694
                /* Close new group */
695
0
                if (H5O_close(obj_loc.oloc, NULL) < 0)
696
0
                    HGOTO_ERROR(H5E_SYM, H5E_CANTINIT, FAIL, "unable to close");
697
698
                /* If the parent group was holding the file open, the
699
                 * newly-created group should, as well.
700
                 */
701
0
                if (grp_loc.oloc->holding_file)
702
0
                    if (H5O_loc_hold_file(obj_loc.oloc) < 0)
703
0
                        HGOTO_ERROR(H5E_SYM, H5E_CANTOPENOBJ, FAIL, "unable to hold file open");
704
705
                /* Reset any non-default object header messages */
706
0
                H5_GCC_CLANG_DIAG_OFF("cast-qual")
707
0
                if (ginfo != &def_ginfo)
708
0
                    if (H5O_msg_reset(H5O_GINFO_ID, (void *)ginfo) < 0)
709
0
                        HGOTO_ERROR(H5E_SYM, H5E_CANTRELEASE, FAIL, "unable to reset group info message");
710
0
                if (linfo != &def_linfo)
711
0
                    if (H5O_msg_reset(H5O_LINFO_ID, (void *)linfo) < 0)
712
0
                        HGOTO_ERROR(H5E_SYM, H5E_CANTRELEASE, FAIL, "unable to reset link info message");
713
0
                if (pline != &def_pline)
714
0
                    if (H5O_msg_reset(H5O_PLINE_ID, (void *)pline) < 0)
715
0
                        HGOTO_ERROR(H5E_SYM, H5E_CANTRELEASE, FAIL, "unable to reset I/O pipeline message");
716
0
                H5_GCC_CLANG_DIAG_ON("cast-qual")
717
0
            } /* end if */
718
0
            else
719
0
                HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "component not found");
720
0
        } /* end if */
721
722
        /*
723
         * Advance to the next component of the path.
724
         */
725
726
        /* Transfer "ownership" of the object's information to the group object */
727
0
        H5G_loc_free(&grp_loc);
728
0
        H5G_loc_copy(&grp_loc, &obj_loc, H5_COPY_SHALLOW);
729
0
        H5G_loc_reset(&obj_loc);
730
0
        obj_loc_valid = false;
731
732
        /* Advance to next component in string */
733
0
        name += nchars;
734
0
    } /* end while */
735
736
    /* Call 'operator' routine */
737
    /* If we've fallen through to here, the name must be something like just '.'
738
     * and we should issue the callback on that. -QAK
739
     * Since we don't have a group location or a link to the object we pass in
740
     * NULL.
741
     */
742
399
    assert(group_copy);
743
399
    if ((op)(NULL, ".", NULL, &grp_loc, op_data, &own_loc) < 0)
744
2
        HGOTO_ERROR(H5E_SYM, H5E_CANTNEXT, FAIL, "traversal operator failed");
745
746
    /* If the callback took ownership of the object location, it actually has
747
     * ownership of grp_loc.  It shouldn't have tried to take ownership of
748
     * the "group location", which was NULL. */
749
397
    assert(!(own_loc & H5G_OWN_GRP_LOC));
750
397
    if (own_loc & H5G_OWN_OBJ_LOC)
751
261
        own_loc |= H5G_OWN_GRP_LOC;
752
753
541
done:
754
    /* If there's been an error, the callback doesn't really get ownership of
755
     * any location and we should close them both */
756
541
    if (ret_value < 0)
757
26
        own_loc = H5G_OWN_NONE;
758
759
    /* Free all open locations.  This also closes any open external files. */
760
541
    if (obj_loc_valid && !(own_loc & H5G_OWN_OBJ_LOC))
761
80
        H5G_loc_free(&obj_loc);
762
541
    if (group_copy && !(own_loc & H5G_OWN_GRP_LOC))
763
280
        H5G_loc_free(&grp_loc);
764
765
    /* If there's valid information in the link, reset it */
766
541
    if (link_valid)
767
140
        if (H5O_msg_reset(H5O_LINK_ID, &lnk) < 0)
768
0
            HDONE_ERROR(H5E_SYM, H5E_CANTRELEASE, FAIL, "unable to reset link message");
769
770
    /* Release temporary component buffer */
771
541
    if (wb && H5WB_unwrap(wb) < 0)
772
0
        HDONE_ERROR(H5E_SYM, H5E_CANTRELEASE, FAIL, "can't release wrapped buffer");
773
774
541
    FUNC_LEAVE_NOAPI(ret_value)
775
397
} /* end H5G__traverse_real() */
776
777
/*-------------------------------------------------------------------------
778
 * Function:  H5G_traverse
779
 *
780
 * Purpose: Traverse a path from a location & perform an operation when
781
 *              the last component of the name is reached.
782
 *
783
 * Return:  Success:  Non-negative if path can be fully traversed.
784
 *    Failure:  Negative if the path could not be fully
785
 *        traversed.
786
 *
787
 *-------------------------------------------------------------------------
788
 */
789
herr_t
790
H5G_traverse(const H5G_loc_t *loc, const char *name, unsigned target, H5G_traverse_t op, void *op_data)
791
541
{
792
541
    size_t orig_nlinks;         /* Original value for # of soft / UD links able to traverse */
793
541
    herr_t ret_value = SUCCEED; /* Return value */
794
795
541
    FUNC_ENTER_NOAPI(FAIL)
796
797
    /* Check args */
798
541
    if (!name || !*name)
799
0
        HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "no name given");
800
541
    if (!loc)
801
0
        HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "no starting location");
802
541
    if (!op)
803
0
        HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "no operation provided");
804
805
    /* Retrieve the original # of soft / UD links that are able to be traversed
806
     * (So that multiple calls to H5G_traverse don't incorrectly look
807
     *  like they've traversed too many.  Nested calls, like in H5L__move(),
808
     *  may need their own mechanism to set & reset the # of links to traverse)
809
     */
810
541
    if (H5CX_get_nlinks(&orig_nlinks) < 0)
811
0
        HGOTO_ERROR(H5E_SYM, H5E_CANTGET, FAIL, "unable to retrieve # of soft / UD links to traverse");
812
813
    /* Set up invalid tag. This is a precautionary step only. Setting an invalid
814
     * tag here will ensure that no metadata accessed while doing the traversal
815
     * is given an improper tag, unless another one is specifically set up
816
     * first. This will ensure we're not accidentally tagging something we
817
     * shouldn't be during the traversal. Note that for best tagging assertion
818
     * coverage, setting H5C_DO_TAGGING_SANITY_CHECKS is advised.
819
     */
820
541
    H5_BEGIN_TAG(H5AC__INVALID_TAG)
821
822
    /* Go perform "real" traversal */
823
541
    if (H5G__traverse_real(loc, name, target, op, op_data) < 0)
824
26
        HGOTO_ERROR_TAG(H5E_SYM, H5E_NOTFOUND, FAIL, "internal path traversal failed");
825
826
    /* Reset tag after traversal */
827
515
    H5_END_TAG
828
829
    /* Reset the # of soft / UD links that can be traversed */
830
515
    if (H5CX_set_nlinks(orig_nlinks) < 0)
831
0
        HGOTO_ERROR(H5E_SYM, H5E_CANTSET, FAIL, "can't reset # of soft / UD links to traverse");
832
833
541
done:
834
541
    FUNC_LEAVE_NOAPI(ret_value)
835
515
} /* end H5G_traverse() */