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