1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2  * Copyright by The HDF Group.                                               *
3  * Copyright by the Board of Trustees of the University of Illinois.         *
4  * All rights reserved.                                                      *
5  *                                                                           *
6  * This file is part of HDF5.  The full HDF5 copyright notice, including     *
7  * terms governing use, modification, and redistribution, is contained in    *
8  * the COPYING file, which can be found at the root of the source code       *
9  * distribution tree, or in https://support.hdfgroup.org/ftp/HDF5/releases.  *
10  * If you do not have access to either file, you may request a copy from     *
11  * help@hdfgroup.org.                                                        *
12  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
13 
14 /****************/
15 /* Module Setup */
16 /****************/
17 
18 #define H5G_FRIEND        /*suppress error about including H5Gpkg   */
19 #include "H5Lmodule.h"          /* This source code file is part of the H5L module */
20 
21 
22 /***********/
23 /* Headers */
24 /***********/
25 #include "H5private.h"          /* Generic Functions                    */
26 #include "H5ACprivate.h"        /* Metadata cache                       */
27 #include "H5CXprivate.h"        /* API Contexts                         */
28 #include "H5Eprivate.h"         /* Error handling                       */
29 #include "H5Fprivate.h"         /* Files                                */
30 #include "H5Gpkg.h"             /* Groups                               */
31 #include "H5Iprivate.h"         /* IDs                                  */
32 #include "H5Lpkg.h"             /* Links                                */
33 #include "H5MMprivate.h"        /* Memory management                    */
34 #include "H5Opublic.h"          /* File objects                         */
35 #include "H5Pprivate.h"         /* Property lists                       */
36 
37 
38 /****************/
39 /* Local Macros */
40 /****************/
41 
42 /* Version of external link format */
43 #define H5L_EXT_VERSION         0
44 
45 /* Valid flags for external links */
46 #define H5L_EXT_FLAGS_ALL       0
47 
48 /* Size of local link name buffer for traversing external links */
49 #define H5L_EXT_TRAVERSE_BUF_SIZE       256
50 
51 
52 /******************/
53 /* Local Typedefs */
54 /******************/
55 
56 
57 /********************/
58 /* Local Prototypes */
59 /********************/
60 static hid_t H5L__extern_traverse(const char *link_name, hid_t cur_group,
61     const void *udata, size_t udata_size, hid_t lapl_id, hid_t dxpl_id);
62 static ssize_t H5L__extern_query(const char *link_name, const void *udata,
63     size_t udata_size, void * buf /*out*/, size_t buf_size);
64 
65 
66 /*********************/
67 /* Package Variables */
68 /*********************/
69 
70 
71 /*****************************/
72 /* Library Private Variables */
73 /*****************************/
74 
75 
76 /*******************/
77 /* Local Variables */
78 /*******************/
79 
80 /* Default External Link link class */
81 static const H5L_class_t H5L_EXTERN_LINK_CLASS[1] = {{
82     H5L_LINK_CLASS_T_VERS,      /* H5L_class_t version            */
83     H5L_TYPE_EXTERNAL,        /* Link type id number            */
84     "external",                 /* Link name for debugging        */
85     NULL,                       /* Creation callback              */
86     NULL,                       /* Move callback                  */
87     NULL,                       /* Copy callback                  */
88     H5L__extern_traverse,       /* The actual traversal function  */
89     NULL,                       /* Deletion callback              */
90     H5L__extern_query           /* Query callback                 */
91 }};
92 
93 /*-------------------------------------------------------------------------
94  * Function:	H5L__extern_traverse
95  *
96  * Purpose:    Default traversal function for external links. This can
97  *              be overridden using H5Lregister().
98  *
99  *              Given a filename and path packed into the link udata,
100  *              attempts to open an object within an external file.
101  *              If the H5L_ELINK_PREFIX_NAME property is set in the
102  *              link access property list, appends that prefix to the
103  *              filename being opened.
104  *
105  * Return:    ID of the opened object on success/Negative on failure
106  *
107  * Programmer:    James Laird
108  *              Monday, July 10, 2006
109  *
110  *-------------------------------------------------------------------------
111  */
112 static hid_t
H5L__extern_traverse(const char H5_ATTR_UNUSED * link_name,hid_t cur_group,const void * _udata,size_t H5_ATTR_UNUSED udata_size,hid_t lapl_id,hid_t H5_ATTR_UNUSED dxpl_id)113 H5L__extern_traverse(const char H5_ATTR_UNUSED *link_name, hid_t cur_group,
114     const void *_udata, size_t H5_ATTR_UNUSED udata_size, hid_t lapl_id,
115     hid_t H5_ATTR_UNUSED dxpl_id)
116 {
117     H5P_genplist_t *plist;              /* Property list pointer */
118     H5G_loc_t   root_loc;               /* Location of root group in external file */
119     H5G_loc_t   loc;                    /* Location of object */
120     H5F_t    *ext_file = NULL;    /* File struct for external file */
121     const uint8_t *p = (const uint8_t *)_udata;  /* Pointer into external link buffer */
122     const char *file_name;              /* Name of file containing external link's object */
123     const char  *obj_name;              /* Name external link's object */
124     size_t      fname_len;              /* Length of external link file name */
125     unsigned    intent;                 /* File access permissions */
126     H5L_elink_cb_t cb_info;             /* Callback info struct */
127     hid_t       fapl_id = -1;           /* File access property list for external link's file */
128     hid_t       ext_obj = -1;           /* ID for external link's object */
129     char        *parent_group_name = NULL;/* Temporary pointer to group name */
130     char        local_group_name[H5L_EXT_TRAVERSE_BUF_SIZE];  /* Local buffer to hold group name */
131     H5P_genplist_t  *fa_plist;          /* File access property list pointer */
132     H5F_close_degree_t 	fc_degree = H5F_CLOSE_WEAK;  /* File close degree for target file */
133     char        *elink_prefix;          /* Pointer to elink prefix */
134     hid_t ret_value = H5I_INVALID_HID;  /* Return value */
135 
136     FUNC_ENTER_STATIC
137 
138     /* Sanity checks */
139     HDassert(p);
140 
141     /* Check external link version & flags */
142     if(((*p >> 4) & 0x0F) > H5L_EXT_VERSION)
143         HGOTO_ERROR(H5E_LINK, H5E_CANTDECODE, FAIL, "bad version number for external link")
144     if((*p & 0x0F) & ~H5L_EXT_FLAGS_ALL)
145         HGOTO_ERROR(H5E_LINK, H5E_CANTDECODE, FAIL, "bad flags for external link")
146     p++;
147 
148     /* Gather some information from the external link's user data */
149     file_name = (const char *)p;
150     fname_len = HDstrlen(file_name);
151     obj_name = (const char *)p + fname_len + 1;
152 
153     /* Get the plist structure */
154     if(NULL == (plist = H5P_object_verify(lapl_id, H5P_LINK_ACCESS)))
155         HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, FAIL, "can't find object for ID")
156 
157     /* Get the fapl_id set for lapl_id if any */
158     if(H5P_get(plist, H5L_ACS_ELINK_FAPL_NAME, &fapl_id) < 0)
159         HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get fapl for links")
160 
161     /* Get the location for the group holding the external link */
162     if(H5G_loc(cur_group, &loc) < 0)
163         HGOTO_ERROR(H5E_LINK, H5E_CANTGET, FAIL, "can't get object location")
164 
165     /* get the access flags set for lapl_id if any */
166     if(H5P_get(plist, H5L_ACS_ELINK_FLAGS_NAME, &intent) < 0)
167         HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get elink file access flags")
168 
169     /* get the file access mode flags for the parent file, if they were not set
170      * on lapl_id */
171     if(intent == H5F_ACC_DEFAULT)
172         intent = H5F_INTENT(loc.oloc->file);
173 
174     if((fapl_id == H5P_DEFAULT) && ((fapl_id = H5F_get_access_plist(loc.oloc->file, FALSE)) < 0))
175         HGOTO_ERROR(H5E_LINK, H5E_CANTGET, FAIL, "can't get parent's file access property list")
176 
177     /* Get callback_info */
178     if(H5P_get(plist, H5L_ACS_ELINK_CB_NAME, &cb_info) < 0)
179         HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get elink callback info")
180 
181     /* Get file access property list */
182     if(NULL == (fa_plist = H5P_object_verify(fapl_id, H5P_FILE_ACCESS)))
183         HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, FAIL, "can't find object for ID")
184 
185     /* Make callback if it exists */
186     if(cb_info.func) {
187         const char  *parent_file_name;  /* Parent file name */
188         ssize_t group_name_len;         /* Length of parent group name */
189 
190         /* Get parent file name */
191         parent_file_name = H5F_OPEN_NAME(loc.oloc->file);
192 
193         /* Query length of parent group name */
194         if((group_name_len = H5G_get_name(&loc, NULL, (size_t) 0, NULL)) < 0)
195             HGOTO_ERROR(H5E_LINK, H5E_CANTGET, FAIL, "unable to retrieve length of group name")
196 
197         /* Account for null terminator */
198         group_name_len++;
199 
200         /* Check if we need to allocate larger buffer */
201         if((size_t)group_name_len > sizeof(local_group_name)) {
202             if(NULL == (parent_group_name = (char *)H5MM_malloc((size_t)group_name_len)))
203                 HGOTO_ERROR(H5E_LINK, H5E_CANTALLOC, FAIL, "can't allocate buffer to hold group name, group_name_len = %zd", group_name_len)
204         } /* end if */
205         else
206             parent_group_name = local_group_name;
207 
208         /* Get parent group name */
209         if(H5G_get_name(&loc, parent_group_name, (size_t) group_name_len, NULL) < 0)
210             HGOTO_ERROR(H5E_LINK, H5E_CANTGET, FAIL, "unable to retrieve group name")
211 
212         /* Make callback */
213         if((cb_info.func)(parent_file_name, parent_group_name, file_name, obj_name, &intent, fapl_id, cb_info.user_data) < 0)
214             HGOTO_ERROR(H5E_LINK, H5E_CALLBACK, FAIL, "traversal operator failed")
215 
216         /* Check access flags */
217         if((intent & H5F_ACC_TRUNC) || (intent & H5F_ACC_EXCL))
218             HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "invalid file open flags")
219     } /* end if */
220 
221     /* Set file close degree for new file to "weak" */
222     if(H5P_set(fa_plist, H5F_ACS_CLOSE_DEGREE_NAME, &fc_degree) < 0)
223         HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't set file close degree")
224 
225     /* Get the current elink prefix */
226     if(H5P_peek(plist, H5L_ACS_ELINK_PREFIX_NAME, &elink_prefix) < 0)
227         HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get external link prefix")
228 
229     /* Search for the target file */
230     if(NULL == (ext_file = H5F_prefix_open_file(loc.oloc->file, H5F_PREFIX_ELINK, elink_prefix, file_name, intent, fapl_id)))
231         HGOTO_ERROR(H5E_LINK, H5E_CANTOPENFILE, FAIL, "unable to open external file, external link file name = '%s'", file_name)
232 
233     /* Retrieve the "group location" for the file's root group */
234     if(H5G_root_loc(ext_file, &root_loc) < 0)
235         HGOTO_ERROR(H5E_LINK, H5E_BADVALUE, FAIL, "unable to create location for file")
236 
237     /* Open the object referenced in the external file */
238     if((ext_obj = H5O_open_name(&root_loc, obj_name, FALSE)) < 0)
239         HGOTO_ERROR(H5E_LINK, H5E_CANTOPENOBJ, FAIL, "unable to open object")
240 
241     /* Set return value */
242     ret_value = ext_obj;
243 
244 done:
245     /* Release resources */
246     if(fapl_id > 0 && H5I_dec_ref(fapl_id) < 0)
247         HDONE_ERROR(H5E_ATOM, H5E_CANTRELEASE, FAIL, "unable to close atom for file access property list")
248     if(ext_file && H5F_efc_close(loc.oloc->file, ext_file) < 0)
249         HDONE_ERROR(H5E_LINK, H5E_CANTCLOSEFILE, FAIL, "problem closing external file")
250     if(parent_group_name && parent_group_name != local_group_name)
251         parent_group_name = (char *)H5MM_xfree(parent_group_name);
252     if(ret_value < 0) {
253         /* Close object if it's open and something failed */
254         if(ext_obj >= 0 && H5I_dec_ref(ext_obj) < 0)
255             HDONE_ERROR(H5E_ATOM, H5E_CANTRELEASE, FAIL, "unable to close atom for external object")
256     } /* end if */
257 
258     FUNC_LEAVE_NOAPI(ret_value)
259 } /* end H5L__extern_traverse() */
260 
261 
262 /*-------------------------------------------------------------------------
263  * Function:	H5L__extern_query
264  *
265  * Purpose:    Default query function for external links. This can
266  *              be overridden using H5Lregister().
267  *
268  *              Returns the size of the link's user data. If a buffer of
269  *              is provided, copies at most buf_size bytes of the udata
270  *              into it.
271  *
272  * Return:    Size of buffer on success/Negative on failure
273  *
274  * Programmer:    James Laird
275  *              Monday, July 10, 2006
276  *
277  *-------------------------------------------------------------------------
278  */
279 static ssize_t
H5L__extern_query(const char H5_ATTR_UNUSED * link_name,const void * _udata,size_t udata_size,void * buf,size_t buf_size)280 H5L__extern_query(const char H5_ATTR_UNUSED * link_name, const void *_udata, size_t udata_size,
281     void *buf /*out*/, size_t buf_size)
282 {
283     const uint8_t *udata = (const uint8_t *)_udata;      /* Pointer to external link buffer */
284     ssize_t     ret_value = SUCCEED;    /* Return value */
285 
286     FUNC_ENTER_NOAPI_NOINIT
287 
288     /* Check external link version & flags */
289     if(((*udata >> 4) & 0x0F) != H5L_EXT_VERSION)
290         HGOTO_ERROR(H5E_LINK, H5E_CANTDECODE, FAIL, "bad version number for external link")
291     if((*udata & 0x0F) & ~H5L_EXT_FLAGS_ALL)
292         HGOTO_ERROR(H5E_LINK, H5E_CANTDECODE, FAIL, "bad flags for external link")
293 
294     /* If the buffer is NULL, skip writing anything in it and just return
295      * the size needed */
296     if(buf) {
297         if(udata_size < buf_size)
298             buf_size = udata_size;
299 
300         /* Copy the udata verbatim up to buf_size */
301         HDmemcpy(buf, udata, buf_size);
302     } /* end if */
303 
304     /* Set return value */
305     ret_value = (ssize_t)udata_size;
306 
307 done:
308     FUNC_LEAVE_NOAPI(ret_value)
309 } /* end H5L__extern_query() */
310 
311 
312 /*-------------------------------------------------------------------------
313  * Function:    H5Lcreate_external
314  *
315  * Purpose:    Creates an external link from LINK_NAME to OBJ_NAME.
316  *
317  *              External links are links to objects in other HDF5 files.  They
318  *              are allowed to "dangle" like soft links internal to a file.
319  *              FILE_NAME is the name of the file that OBJ_NAME is is contained
320  *              within.  If OBJ_NAME is given as a relative path name, the
321  *              path will be relative to the root group of FILE_NAME.
322  *              LINK_NAME is interpreted relative to LINK_LOC_ID, which is
323  *              either a file ID or a group ID.
324  *
325  * Return:    Non-negative on success/Negative on failure
326  *
327  * Programmer:    Quincey Koziol
328  *              Wednesday, May 18, 2005
329  *
330  *-------------------------------------------------------------------------
331  */
332 herr_t
H5Lcreate_external(const char * file_name,const char * obj_name,hid_t link_loc_id,const char * link_name,hid_t lcpl_id,hid_t lapl_id)333 H5Lcreate_external(const char *file_name, const char *obj_name,
334     hid_t link_loc_id, const char *link_name, hid_t lcpl_id, hid_t lapl_id)
335 {
336     H5G_loc_t    link_loc;               /* Group location to create link */
337     char       *norm_obj_name = NULL;    /* Pointer to normalized current name */
338     void       *ext_link_buf = NULL;    /* Buffer to contain external link */
339     size_t      buf_size;               /* Size of buffer to hold external link */
340     size_t      file_name_len;          /* Length of file name string */
341     size_t      norm_obj_name_len;      /* Length of normalized object name string */
342     uint8_t    *p;                      /* Pointer into external link buffer */
343     herr_t      ret_value = SUCCEED;    /* Return value */
344 
345     FUNC_ENTER_API(FAIL)
346     H5TRACE6("e", "*s*si*sii", file_name, obj_name, link_loc_id, link_name,
347              lcpl_id, lapl_id);
348 
349     /* Check arguments */
350     if(!file_name || !*file_name)
351         HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no file name specified")
352     if(!obj_name || !*obj_name)
353         HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no object name specified")
354     if(H5G_loc(link_loc_id, &link_loc) < 0)
355         HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a location")
356     if(!link_name || !*link_name)
357         HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "no link name specified")
358 
359     /* Get normalized copy of the link target */
360     if(NULL == (norm_obj_name = H5G_normalize(obj_name)))
361         HGOTO_ERROR(H5E_LINK, H5E_BADVALUE, FAIL, "can't normalize object name")
362 
363     /* Combine the filename and link name into a single buffer to give to the UD link */
364     file_name_len = HDstrlen(file_name) + 1;
365     norm_obj_name_len = HDstrlen(norm_obj_name) + 1;
366     buf_size = 1 + file_name_len + norm_obj_name_len;
367     if(NULL == (ext_link_buf = H5MM_malloc(buf_size)))
368         HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate udata buffer")
369 
370     /* Encode the external link information */
371     p = (uint8_t *)ext_link_buf;
372     *p++ = (H5L_EXT_VERSION << 4) | H5L_EXT_FLAGS_ALL;  /* External link version & flags */
373     HDstrncpy((char *)p, file_name, buf_size - 1);      /* Name of file containing external link's object */
374     p += file_name_len;
375     HDstrncpy((char *)p, norm_obj_name, buf_size - (file_name_len + 1));       /* External link's object */
376 
377     /* Verify access property list and set up collective metadata if appropriate */
378     if(H5CX_set_apl(&lapl_id, H5P_CLS_LACC, link_loc_id, TRUE) < 0)
379         HGOTO_ERROR(H5E_LINK, H5E_CANTSET, FAIL, "can't set access property list info")
380 
381     /* Create an external link */
382     if(H5L__create_ud(&link_loc, link_name, ext_link_buf, buf_size, H5L_TYPE_EXTERNAL, lcpl_id) < 0)
383         HGOTO_ERROR(H5E_LINK, H5E_CANTINIT, FAIL, "unable to create link")
384 
385 done:
386     H5MM_xfree(ext_link_buf);
387     H5MM_xfree(norm_obj_name);
388 
389     FUNC_LEAVE_API(ret_value)
390 } /* end H5Lcreate_external() */
391 
392 
393 /*-------------------------------------------------------------------------
394  * Function: H5L_register_external
395  *
396  * Purpose: Registers default "External Link" link class.
397  *              Use during library initialization or to restore the default
398  *              after users change it.
399  *
400  * Return: Non-negative on success/ negative on failure
401  *
402  * Programmer:  James Laird
403  *              Monday, July 17, 2006
404  *
405  *-------------------------------------------------------------------------
406  */
407 herr_t
H5L_register_external(void)408 H5L_register_external(void)
409 {
410     herr_t      ret_value = SUCCEED;       /* Return value */
411 
412     FUNC_ENTER_NOAPI(FAIL)
413 
414     if(H5L_register(H5L_EXTERN_LINK_CLASS) < 0)
415         HGOTO_ERROR(H5E_LINK, H5E_NOTREGISTERED, FAIL, "unable to register external link class")
416 
417 done:
418     FUNC_LEAVE_NOAPI(ret_value)
419 } /* end H5L_register_external() */
420 
421 
422 /*-------------------------------------------------------------------------
423  * Function: H5Lunpack_elink_val
424  *
425  * Purpose: Given a buffer holding the "link value" from an external link,
426  *              gets pointers to the information within the link value buffer.
427  *
428  *              External link link values contain some flags and
429  *              two NULL-terminated strings, one after the other.
430  *
431  *              The FLAGS value will be filled in and FILENAME and
432  *              OBJ_PATH will be set to pointers within ext_linkval (unless
433  *              any of these values is NULL).
434  *
435  *              Using this function on strings that aren't external link
436  *              udata buffers can result in segmentation faults.
437  *
438  * Return: Non-negative on success/ Negative on failure
439  *
440  * Programmer:  James Laird
441  *              Monday, July 17, 2006
442  *
443  *-------------------------------------------------------------------------
444  */
445 herr_t
H5Lunpack_elink_val(const void * _ext_linkval,size_t link_size,unsigned * flags,const char ** filename,const char ** obj_path)446 H5Lunpack_elink_val(const void *_ext_linkval, size_t link_size,
447     unsigned *flags, const char **filename, const char **obj_path)
448 {
449     const uint8_t *ext_linkval = (const uint8_t *)_ext_linkval; /* Pointer to the link value */
450     unsigned    lnk_version;            /* External link format version */
451     unsigned    lnk_flags;              /* External link flags */
452     size_t      len;                    /* Length of the filename in the linkval*/
453     herr_t      ret_value = SUCCEED;    /* Return value */
454 
455     FUNC_ENTER_API(FAIL)
456     H5TRACE5("e", "*xz*Iu**s**s", _ext_linkval, link_size, flags, filename,
457              obj_path);
458 
459     /* Sanity check external link buffer */
460     if(ext_linkval == NULL )
461         HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "not an external link linkval buffer")
462     lnk_version = (*ext_linkval >> 4) & 0x0F;
463     lnk_flags = *ext_linkval & 0x0F;
464     if(lnk_version > H5L_EXT_VERSION)
465         HGOTO_ERROR(H5E_LINK, H5E_CANTDECODE, FAIL, "bad version number for external link")
466     if(lnk_flags & (unsigned)~H5L_EXT_FLAGS_ALL)
467         HGOTO_ERROR(H5E_LINK, H5E_CANTDECODE, FAIL, "bad flags for external link")
468     if(link_size <= 2)
469         HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "not a valid external link buffer")
470 
471     /* Try to do some error checking.  If the last character in the linkval
472      * (the last character of obj_path) isn't NULL, then something's wrong.
473      */
474     if(ext_linkval[link_size - 1] != '\0')
475         HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "linkval buffer is not NULL-terminated")
476 
477     /* We're now guaranteed that HDstrlen won't segfault, since the buffer has
478      * at least one NULL in it.
479      */
480     len = HDstrlen((const char *)ext_linkval + 1);
481 
482     /* If the first NULL we found was at the very end of the buffer, then
483      * this external link value has no object name and is invalid.
484      */
485     if((len + 1) >= (link_size - 1))
486         HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "linkval buffer doesn't contain an object path")
487 
488     /* If we got here then the buffer contains (at least) two strings packed
489      * in the correct way.  Assume it's correct and return pointers to the
490      * filename and object path.
491      */
492     if(filename)
493         *filename = (const char *)ext_linkval + 1;
494     if(obj_path)
495         *obj_path = ((const char *)ext_linkval + 1) + len + 1;  /* Add one for NULL terminator */
496 
497     /* Set the flags to return */
498     if(flags)
499         *flags = lnk_flags;
500 
501 done:
502     FUNC_LEAVE_API(ret_value)
503 } /* end H5Lunpack_elink_val() */
504 
505