/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() */ |