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 | | * |
15 | | * Created: H5Ocopy.c |
16 | | * |
17 | | * Purpose: Object copying routines. |
18 | | * |
19 | | *------------------------------------------------------------------------- |
20 | | */ |
21 | | |
22 | | /****************/ |
23 | | /* Module Setup */ |
24 | | /****************/ |
25 | | |
26 | | #include "H5Omodule.h" /* This source code file is part of the H5O module */ |
27 | | |
28 | | /***********/ |
29 | | /* Headers */ |
30 | | /***********/ |
31 | | #include "H5private.h" /* Generic Functions */ |
32 | | #include "H5Aprivate.h" /* Attributes */ |
33 | | #include "H5Eprivate.h" /* Error handling */ |
34 | | #include "H5FLprivate.h" /* Free lists */ |
35 | | #include "H5Iprivate.h" /* IDs */ |
36 | | #include "H5FOprivate.h" /* File objects */ |
37 | | #include "H5Lprivate.h" /* Links */ |
38 | | #include "H5MFprivate.h" /* File memory management */ |
39 | | #include "H5MMprivate.h" /* Memory management */ |
40 | | #include "H5Opkg.h" /* Object headers */ |
41 | | #include "H5Pprivate.h" /* Property lists */ |
42 | | #include "H5SLprivate.h" /* Skip Lists */ |
43 | | |
44 | | /****************/ |
45 | | /* Local Macros */ |
46 | | /****************/ |
47 | | |
48 | | /******************/ |
49 | | /* Local Typedefs */ |
50 | | /******************/ |
51 | | |
52 | | /* Key object for skiplist of committed datatypes */ |
53 | | typedef struct H5O_copy_search_comm_dt_key_t { |
54 | | H5T_t *dt; /* Datatype */ |
55 | | unsigned long fileno; /* File number */ |
56 | | } H5O_copy_search_comm_dt_key_t; |
57 | | |
58 | | /* Callback struct for building a list of committed datatypes */ |
59 | | typedef struct H5O_copy_search_comm_dt_ud_t { |
60 | | H5SL_t *dst_dt_list; /* Skip list of committed datatypes */ |
61 | | H5G_loc_t *dst_root_loc; /* Starting location for iteration */ |
62 | | H5O_loc_t obj_oloc; /* Object location (for attribute iteration callback) */ |
63 | | } H5O_copy_search_comm_dt_ud_t; |
64 | | |
65 | | /********************/ |
66 | | /* Package Typedefs */ |
67 | | /********************/ |
68 | | |
69 | | /********************/ |
70 | | /* Local Prototypes */ |
71 | | /********************/ |
72 | | |
73 | | static herr_t H5O__copy_free_addrmap_cb(void *item, void *key, void *op_data); |
74 | | static herr_t H5O__copy_header_real(const H5O_loc_t *oloc_src, H5O_loc_t *oloc_dst /*out*/, |
75 | | H5O_copy_t *cpy_info, H5O_type_t *obj_type, void **udata); |
76 | | static herr_t H5O__copy_header(const H5O_loc_t *oloc_src, H5O_loc_t *oloc_dst /*out*/, hid_t ocpypl_id, |
77 | | hid_t lcpl_id); |
78 | | static herr_t H5O__copy_obj(H5G_loc_t *src_loc, H5G_loc_t *dst_loc, const char *dst_name, hid_t ocpypl_id, |
79 | | hid_t lcpl_id); |
80 | | static herr_t H5O__copy_free_comm_dt_cb(void *item, void *key, void *op_data); |
81 | | static int H5O__copy_comm_dt_cmp(const void *dt1, const void *dt2); |
82 | | static herr_t H5O__copy_search_comm_dt_cb(hid_t group, const char *name, const H5O_loc_t *obj_oloc, |
83 | | void *udata); |
84 | | static htri_t H5O__copy_search_comm_dt(H5F_t *file_src, H5O_t *oh_src, H5O_loc_t *oloc_dst /*in, out*/, |
85 | | H5O_copy_t *cpy_info); |
86 | | static herr_t H5O__copy_insert_comm_dt(H5F_t *file_src, H5O_t *oh_src, H5O_loc_t *oloc_dst, |
87 | | H5O_copy_t *cpy_info); |
88 | | |
89 | | /*********************/ |
90 | | /* Package Variables */ |
91 | | /*********************/ |
92 | | |
93 | | /* Declare a free list to manage the H5O_addr_map_t struct */ |
94 | | H5FL_DEFINE(H5O_addr_map_t); |
95 | | |
96 | | /* Declare a free list to manage the H5O_copy_search_comm_dt_key_t struct */ |
97 | | H5FL_DEFINE(H5O_copy_search_comm_dt_key_t); |
98 | | |
99 | | /* Declare a free list to manage haddr_t variables */ |
100 | | H5FL_DEFINE(haddr_t); |
101 | | |
102 | | /*****************************/ |
103 | | /* Library Private Variables */ |
104 | | /*****************************/ |
105 | | |
106 | | /*******************/ |
107 | | /* Local Variables */ |
108 | | /*******************/ |
109 | | |
110 | | /*------------------------------------------------------------------------- |
111 | | * Function: H5O__copy |
112 | | * |
113 | | * Purpose: Private version of H5Ocopy |
114 | | * |
115 | | * Return: SUCCEED/FAIL |
116 | | * |
117 | | *------------------------------------------------------------------------- |
118 | | */ |
119 | | herr_t |
120 | | H5O__copy(const H5G_loc_t *loc, const char *src_name, H5G_loc_t *dst_loc, const char *dst_name, |
121 | | hid_t ocpypl_id, hid_t lcpl_id) |
122 | 0 | { |
123 | 0 | H5G_loc_t src_loc; /* Source object group location */ |
124 | 0 | H5G_name_t src_path; /* Opened source object hier. path */ |
125 | 0 | H5O_loc_t src_oloc; /* Opened source object object location */ |
126 | 0 | bool dst_exists; /* Does destination name exist already? */ |
127 | 0 | bool loc_found = false; /* Location at 'name' found */ |
128 | 0 | bool obj_open = false; /* Entry at 'name' found */ |
129 | 0 | herr_t ret_value = SUCCEED; /* Return value */ |
130 | |
|
131 | 0 | FUNC_ENTER_PACKAGE |
132 | | |
133 | | /* Check arguments */ |
134 | 0 | assert(loc); |
135 | 0 | assert(src_name && *src_name); |
136 | 0 | assert(dst_loc); |
137 | 0 | assert(dst_name && *dst_name); |
138 | | |
139 | | /* Check if destination name already exists */ |
140 | 0 | dst_exists = false; |
141 | 0 | if (H5L_exists_tolerant(dst_loc, dst_name, &dst_exists) < 0) |
142 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "unable to check if destination name exists"); |
143 | 0 | if (dst_exists) |
144 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_EXISTS, FAIL, "destination object already exists"); |
145 | | |
146 | | /* Set up opened group location to fill in */ |
147 | 0 | src_loc.oloc = &src_oloc; |
148 | 0 | src_loc.path = &src_path; |
149 | 0 | H5G_loc_reset(&src_loc); |
150 | | |
151 | | /* Find the source object to copy */ |
152 | 0 | if (H5G_loc_find(loc, src_name, &src_loc /*out*/) < 0) |
153 | 0 | HGOTO_ERROR(H5E_SYM, H5E_NOTFOUND, FAIL, "source object not found"); |
154 | 0 | loc_found = true; |
155 | | |
156 | | /* Open source object's object header */ |
157 | 0 | if (H5O_open(&src_oloc) < 0) |
158 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTOPENOBJ, FAIL, "unable to open object"); |
159 | 0 | obj_open = true; |
160 | | |
161 | | /* Do the actual copying of the object */ |
162 | 0 | if (H5O__copy_obj(&src_loc, dst_loc, dst_name, ocpypl_id, lcpl_id) < 0) |
163 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTCOPY, FAIL, "unable to copy object"); |
164 | | |
165 | 0 | done: |
166 | 0 | if (loc_found && H5G_loc_free(&src_loc) < 0) |
167 | 0 | HDONE_ERROR(H5E_OHDR, H5E_CANTRELEASE, FAIL, "can't free location"); |
168 | 0 | if (obj_open && H5O_close(&src_oloc, NULL) < 0) |
169 | 0 | HDONE_ERROR(H5E_OHDR, H5E_CLOSEERROR, FAIL, "unable to release object header"); |
170 | |
|
171 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
172 | 0 | } /* end H5O__copy() */ |
173 | | |
174 | | /*------------------------------------------------------------------------- |
175 | | * Function: H5O__copy_header_real |
176 | | * |
177 | | * Purpose: Copy header object from one location to another using |
178 | | * pre-copy, copy, and post-copy callbacks for each message |
179 | | * type. |
180 | | * |
181 | | * The source header object is compressed into a single chunk |
182 | | * (since we know how big it is) and any continuation messages |
183 | | * are converted into NULL messages. |
184 | | * |
185 | | * By default, NULL messages are not copied. |
186 | | * |
187 | | * Return: Non-negative on success/Negative on failure |
188 | | * |
189 | | *------------------------------------------------------------------------- |
190 | | */ |
191 | | static herr_t |
192 | | H5O__copy_header_real(const H5O_loc_t *oloc_src, H5O_loc_t *oloc_dst /*out*/, H5O_copy_t *cpy_info, |
193 | | H5O_type_t *obj_type, void **udata /*out*/) |
194 | 0 | { |
195 | 0 | H5O_addr_map_t *addr_map = NULL; /* Address mapping of object copied */ |
196 | 0 | H5O_t *oh_src = NULL; /* Object header for source object */ |
197 | 0 | H5O_t *oh_dst = NULL; /* Object header for destination object */ |
198 | 0 | unsigned mesgno = 0; |
199 | 0 | haddr_t addr_new = HADDR_UNDEF; |
200 | 0 | bool *deleted = NULL; /* Array of flags indicating whether messages should be copied */ |
201 | 0 | bool inserted = false; /* Whether the destination object header has been inserted into the cache */ |
202 | 0 | size_t null_msgs; /* Number of NULL messages found in each loop */ |
203 | 0 | size_t orig_dst_msgs; /* Original # of messages in dest. object */ |
204 | 0 | H5O_mesg_t *mesg_src; /* Message in source object header */ |
205 | 0 | H5O_mesg_t *mesg_dst; /* Message in destination object header */ |
206 | 0 | const H5O_msg_class_t *copy_type; /* Type of message to use for copying */ |
207 | 0 | const H5O_obj_class_t *obj_class = NULL; /* Type of object we are copying */ |
208 | 0 | void *cpy_udata = NULL; /* User data for passing to message callbacks */ |
209 | 0 | uint64_t dst_oh_size; /* Total size of the destination OH */ |
210 | 0 | size_t dst_oh_null; /* Size of the null message to add to destination OH */ |
211 | 0 | size_t dst_oh_gap; /* Size of the gap in chunk #0 of destination OH */ |
212 | 0 | uint8_t *current_pos; /* Current position in destination image */ |
213 | 0 | size_t msghdr_size; |
214 | 0 | herr_t ret_value = SUCCEED; |
215 | |
|
216 | 0 | FUNC_ENTER_PACKAGE_TAG(oloc_src->addr) |
217 | |
|
218 | 0 | assert(oloc_src); |
219 | 0 | assert(oloc_src->file); |
220 | 0 | assert(H5_addr_defined(oloc_src->addr)); |
221 | 0 | assert(oloc_dst->file); |
222 | 0 | assert(cpy_info); |
223 | | |
224 | | /* Get pointer to object class for this object */ |
225 | 0 | if (NULL == (obj_class = H5O__obj_class(oloc_src))) |
226 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "unable to determine object type"); |
227 | | |
228 | | /* Set the pointer to the shared struct for the object if opened in the file */ |
229 | 0 | cpy_info->shared_fo = H5FO_opened(oloc_src->file, oloc_src->addr); |
230 | | |
231 | | /* Get source object header */ |
232 | 0 | if (NULL == (oh_src = H5O_protect(oloc_src, H5AC__READ_ONLY_FLAG, false))) |
233 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTPROTECT, FAIL, "unable to load object header"); |
234 | | |
235 | | /* Retrieve user data for particular type of object to copy */ |
236 | 0 | if (obj_class->get_copy_file_udata && (NULL == (cpy_udata = (obj_class->get_copy_file_udata)()))) |
237 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "unable to retrieve copy user data"); |
238 | | |
239 | | /* If we are merging committed datatypes, check for a match in the destination |
240 | | * file now */ |
241 | 0 | if (cpy_info->merge_comm_dt && obj_class->type == H5O_TYPE_NAMED_DATATYPE) { |
242 | 0 | unsigned long fileno_src; /* fileno for source file */ |
243 | 0 | unsigned long fileno_dst; /* fileno for destination file */ |
244 | 0 | htri_t merge; /* Whether we found a match in the destination file */ |
245 | | |
246 | | /* Check if the source and dest file are the same. If so, just return |
247 | | * the source object address */ |
248 | 0 | H5F_GET_FILENO(oloc_src->file, fileno_src); |
249 | 0 | H5F_GET_FILENO(oloc_dst->file, fileno_dst); |
250 | 0 | if (fileno_src == fileno_dst) { |
251 | 0 | merge = true; |
252 | 0 | oloc_dst->addr = oloc_src->addr; |
253 | 0 | } /* end if */ |
254 | 0 | else |
255 | | /* Search for a matching committed datatype, building the list if |
256 | | * necessary */ |
257 | 0 | if ((merge = H5O__copy_search_comm_dt(oloc_src->file, oh_src, oloc_dst, cpy_info)) < 0) |
258 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "can't search for matching committed datatype"); |
259 | | |
260 | 0 | if (merge) { |
261 | | /* Found a match, add to skip list and exit */ |
262 | | /* Allocate space for the address mapping of the object copied */ |
263 | 0 | if (NULL == (addr_map = H5FL_MALLOC(H5O_addr_map_t))) |
264 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed"); |
265 | | |
266 | | /* Insert the address mapping for the found object into the copied |
267 | | * list */ |
268 | 0 | addr_map->src_obj_pos.fileno = fileno_src; |
269 | 0 | addr_map->src_obj_pos.addr = oloc_src->addr; |
270 | 0 | addr_map->dst_addr = oloc_dst->addr; |
271 | 0 | addr_map->is_locked = true; /* We've locked the object currently */ |
272 | 0 | addr_map->inc_ref_count = 0; /* Start with no additional ref counts to add */ |
273 | 0 | addr_map->obj_class = obj_class; |
274 | 0 | addr_map->udata = cpy_udata; |
275 | | |
276 | | /* Insert into skip list */ |
277 | 0 | if (H5SL_insert(cpy_info->map_list, addr_map, &(addr_map->src_obj_pos)) < 0) { |
278 | 0 | addr_map = H5FL_FREE(H5O_addr_map_t, addr_map); |
279 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTINSERT, FAIL, "can't insert object into skip list"); |
280 | 0 | } /* end if */ |
281 | | |
282 | 0 | HGOTO_DONE(SUCCEED); |
283 | 0 | } /* end if */ |
284 | 0 | } /* end if */ |
285 | | |
286 | | /* Flush any dirty messages in source object header to update the header chunks */ |
287 | 0 | if (H5O__flush_msgs(oloc_src->file, oh_src) < 0) |
288 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTFLUSH, FAIL, "unable to flush object header messages"); |
289 | | |
290 | | /* Allocate the destination object header and fill in header fields */ |
291 | 0 | if (NULL == (oh_dst = H5FL_CALLOC(H5O_t))) |
292 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed"); |
293 | | |
294 | | /* Initialize header information */ |
295 | 0 | oh_dst->version = oh_src->version; |
296 | | |
297 | | /* Version bounds check for destination object header */ |
298 | 0 | if (oh_dst->version > H5O_obj_ver_bounds[H5F_HIGH_BOUND(oloc_dst->file)]) |
299 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_BADRANGE, FAIL, "destination object header version out of bounds"); |
300 | | |
301 | 0 | oh_dst->flags = oh_src->flags; |
302 | 0 | oh_dst->link_msgs_seen = oh_src->link_msgs_seen; |
303 | 0 | oh_dst->attr_msgs_seen = oh_src->attr_msgs_seen; |
304 | 0 | oh_dst->sizeof_size = H5F_SIZEOF_SIZE(oloc_dst->file); |
305 | 0 | oh_dst->sizeof_addr = H5F_SIZEOF_ADDR(oloc_dst->file); |
306 | 0 | oh_dst->swmr_write = !!(H5F_INTENT(oloc_dst->file) & H5F_ACC_SWMR_WRITE); |
307 | | |
308 | | /* Copy time fields */ |
309 | 0 | oh_dst->atime = oh_src->atime; |
310 | 0 | oh_dst->mtime = oh_src->mtime; |
311 | 0 | oh_dst->ctime = oh_src->ctime; |
312 | 0 | oh_dst->btime = oh_src->btime; |
313 | | |
314 | | /* Copy attribute storage information */ |
315 | 0 | oh_dst->max_compact = oh_src->max_compact; |
316 | 0 | oh_dst->min_dense = oh_src->min_dense; |
317 | | |
318 | | /* Create object header proxy if doing SWMR writes */ |
319 | 0 | if (oh_dst->swmr_write) { |
320 | | /* Create virtual entry, for use as proxy */ |
321 | 0 | if (NULL == (oh_dst->proxy = H5AC_proxy_entry_create())) |
322 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTCREATE, FAIL, "can't create object header proxy"); |
323 | 0 | } /* end if */ |
324 | 0 | else |
325 | 0 | oh_dst->proxy = NULL; |
326 | | |
327 | | /* Initialize size of chunk array. Start off with zero chunks so this field |
328 | | * is consistent with the current state of the chunk array. This is |
329 | | * important if an error occurs. |
330 | | */ |
331 | 0 | oh_dst->alloc_nchunks = oh_dst->nchunks = 0; |
332 | | |
333 | | /* Allocate memory for the chunk array - always start with 1 chunk */ |
334 | 0 | if (NULL == (oh_dst->chunk = H5FL_SEQ_MALLOC(H5O_chunk_t, (size_t)1))) |
335 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed"); |
336 | | |
337 | | /* Update number of allocated chunks. There are still no chunks used. */ |
338 | 0 | oh_dst->alloc_nchunks = 1; |
339 | | |
340 | | /* Allocate memory for "deleted" array. This array marks the message in |
341 | | * the source that shouldn't be copied to the destination. |
342 | | */ |
343 | 0 | if (NULL == (deleted = (bool *)H5MM_malloc(sizeof(bool) * oh_src->nmesgs))) |
344 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed"); |
345 | 0 | memset(deleted, false, sizeof(bool) * oh_src->nmesgs); |
346 | | |
347 | | /* "pre copy" pass over messages, to gather information for actual message copy operation |
348 | | * (for messages which depend on information from other messages) |
349 | | * Keep track of how many NULL or deleted messages we find (or create) |
350 | | */ |
351 | 0 | null_msgs = 0; |
352 | 0 | for (mesgno = 0; mesgno < oh_src->nmesgs; mesgno++) { |
353 | | /* Set up convenience variables */ |
354 | 0 | mesg_src = &(oh_src->mesg[mesgno]); |
355 | | |
356 | | /* Sanity check */ |
357 | 0 | assert(!mesg_src->dirty); /* Should be cleared by earlier call to flush messages */ |
358 | | |
359 | | /* Get message class to operate on */ |
360 | 0 | copy_type = mesg_src->type; |
361 | | |
362 | | /* Sanity check message for possible corruption */ |
363 | 0 | if (H5O_UNKNOWN_ID != mesg_src->type->id && H5O_NULL_ID != mesg_src->type->id) |
364 | 0 | if (0 == mesg_src->raw_size) |
365 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, FAIL, "message of type '%s' has zero size", |
366 | 0 | mesg_src->type->name); |
367 | | |
368 | | /* Check for continuation message; these are converted to NULL |
369 | | * messages because the destination OH will have only one chunk |
370 | | */ |
371 | 0 | if (H5O_CONT_ID == mesg_src->type->id || H5O_NULL_ID == mesg_src->type->id) { |
372 | 0 | deleted[mesgno] = true; |
373 | 0 | ++null_msgs; |
374 | 0 | copy_type = H5O_MSG_NULL; |
375 | 0 | } /* end if */ |
376 | 0 | assert(copy_type); |
377 | |
|
378 | 0 | if (copy_type->pre_copy_file) { |
379 | | /* Decode the message if necessary. */ |
380 | 0 | H5O_LOAD_NATIVE(oloc_src->file, 0, oh_src, mesg_src, FAIL) |
381 | | |
382 | | /* Save destination file pointer in cpy_info so that it can be used |
383 | | in the pre_copy_file callback to obtain the destination file's |
384 | | high bound. The high bound is used to index into the corresponding |
385 | | message's array of versions for doing version bounds check. */ |
386 | 0 | cpy_info->file_dst = oloc_dst->file; |
387 | | |
388 | | /* Perform "pre copy" operation on message */ |
389 | 0 | if ((copy_type->pre_copy_file)(oloc_src->file, mesg_src->native, &(deleted[mesgno]), cpy_info, |
390 | 0 | cpy_udata) < 0) |
391 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, |
392 | 0 | "unable to perform 'pre copy' operation on message"); |
393 | | |
394 | | /* Check if the message should be deleted in the destination */ |
395 | 0 | if (deleted[mesgno]) |
396 | | /* Mark message as deleted */ |
397 | 0 | ++null_msgs; |
398 | 0 | } /* end if(copy_type->pre_copy_file) */ |
399 | 0 | } /* end for */ |
400 | | |
401 | | /* Initialize size of message list. It may or may not include the NULL messages |
402 | | * detected above. |
403 | | */ |
404 | 0 | if (cpy_info->preserve_null) |
405 | 0 | oh_dst->alloc_nmesgs = oh_dst->nmesgs = oh_src->nmesgs; |
406 | 0 | else |
407 | 0 | oh_dst->alloc_nmesgs = oh_dst->nmesgs = (oh_src->nmesgs - null_msgs); |
408 | | |
409 | | /* Allocate memory for destination message array */ |
410 | 0 | if (oh_dst->alloc_nmesgs > 0) |
411 | 0 | if (NULL == (oh_dst->mesg = H5FL_SEQ_CALLOC(H5O_mesg_t, oh_dst->alloc_nmesgs))) |
412 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed"); |
413 | | |
414 | | /* "copy" pass over messages, to perform main message copying */ |
415 | 0 | null_msgs = 0; |
416 | 0 | for (mesgno = 0; mesgno < oh_dst->nmesgs; mesgno++) { |
417 | | /* Skip any deleted or NULL messages in the source unless the |
418 | | * preserve_null flag is set |
419 | | */ |
420 | 0 | if (false == cpy_info->preserve_null) { |
421 | 0 | while (deleted[mesgno + null_msgs]) { |
422 | 0 | ++null_msgs; |
423 | 0 | assert(mesgno + null_msgs < oh_src->nmesgs); |
424 | 0 | } /* end while */ |
425 | 0 | } /* end if */ |
426 | | |
427 | | /* Set up convenience variables */ |
428 | 0 | mesg_src = &(oh_src->mesg[mesgno + null_msgs]); |
429 | 0 | mesg_dst = &(oh_dst->mesg[mesgno]); |
430 | | |
431 | | /* Initialize non-zero components of destination message */ |
432 | 0 | mesg_dst->crt_idx = mesg_src->crt_idx; |
433 | 0 | mesg_dst->flags = mesg_src->flags; |
434 | 0 | mesg_dst->raw_size = mesg_src->raw_size; |
435 | 0 | mesg_dst->type = mesg_src->type; |
436 | | |
437 | | /* If we're preserving deleted messages, set their types to 'NULL' |
438 | | * in the destination. |
439 | | */ |
440 | 0 | if (cpy_info->preserve_null && deleted[mesgno]) { |
441 | 0 | mesg_dst->type = H5O_MSG_NULL; |
442 | 0 | mesg_dst->flags = 0; |
443 | 0 | mesg_dst->dirty = true; |
444 | 0 | } /* end if */ |
445 | | |
446 | | /* Check for message class to operate on */ |
447 | | /* (Use destination message, in case the message has been removed (i.e |
448 | | * converted to a nil message) in the destination -QAK) |
449 | | */ |
450 | 0 | copy_type = mesg_dst->type; |
451 | 0 | assert(copy_type); |
452 | | |
453 | | /* copy this message into destination file */ |
454 | 0 | if (copy_type->copy_file) { |
455 | 0 | bool recompute_size; /* Whether copy_file callback created a shared message */ |
456 | 0 | unsigned mesg_flags; /* Message flags */ |
457 | | |
458 | | /* Decode the message if necessary. */ |
459 | 0 | H5O_LOAD_NATIVE(oloc_src->file, 0, oh_src, mesg_src, FAIL) |
460 | | |
461 | | /* Get destination message flags, and unset shared and shareable |
462 | | * flags. mesg_dst->flags will contain the original flags for now. |
463 | | */ |
464 | 0 | mesg_flags = (unsigned)mesg_dst->flags & ~H5O_MSG_FLAG_SHARED & ~H5O_MSG_FLAG_SHAREABLE; |
465 | | |
466 | | /* Copy the source message */ |
467 | 0 | recompute_size = false; |
468 | 0 | if (NULL == (mesg_dst->native = |
469 | 0 | H5O__msg_copy_file(copy_type, oloc_src->file, mesg_src->native, oloc_dst->file, |
470 | 0 | &recompute_size, &mesg_flags, cpy_info, cpy_udata))) |
471 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTCOPY, FAIL, "unable to copy object header message"); |
472 | | |
473 | | /* Check if the sharing state changed, and recompute the size if so |
474 | | */ |
475 | 0 | if (!(mesg_flags & H5O_MSG_FLAG_SHARED) != !(mesg_dst->flags & H5O_MSG_FLAG_SHARED)) |
476 | 0 | recompute_size = true; |
477 | | |
478 | | /* Set destination message flags */ |
479 | 0 | mesg_dst->flags = (uint8_t)mesg_flags; |
480 | | |
481 | | /* Recompute message's size */ |
482 | | /* (its sharing status or one of its components (for attributes) |
483 | | * could have changed) |
484 | | */ |
485 | 0 | if (recompute_size) |
486 | 0 | mesg_dst->raw_size = H5O_ALIGN_OH( |
487 | 0 | oh_dst, H5O_msg_raw_size(oloc_dst->file, mesg_dst->type->id, false, mesg_dst->native)); |
488 | | |
489 | | /* Mark the message in the destination as dirty, so it'll get encoded when the object header is |
490 | | * flushed */ |
491 | 0 | mesg_dst->dirty = true; |
492 | 0 | } /* end if (mesg_src->type->copy_file) */ |
493 | 0 | } /* end of mesgno loop */ |
494 | | |
495 | | /* Allocate the destination header and copy any messages that didn't have |
496 | | * copy callbacks. They get copied directly from the source image to the |
497 | | * destination image. |
498 | | */ |
499 | | |
500 | | /* Calculate how big the destination object header will be on disk. |
501 | | * This isn't necessarily the same size as the original. |
502 | | */ |
503 | | |
504 | | /* Compute space for messages. */ |
505 | 0 | dst_oh_size = 0; |
506 | 0 | for (mesgno = 0; mesgno < oh_dst->nmesgs; mesgno++) { |
507 | 0 | dst_oh_size += (uint64_t)H5O_SIZEOF_MSGHDR_OH(oh_dst); |
508 | 0 | dst_oh_size += oh_dst->mesg[mesgno].raw_size; |
509 | 0 | } /* end for */ |
510 | | |
511 | | /* Check if we need to determine correct value for chunk #0 size bits */ |
512 | 0 | if (oh_dst->version > H5O_VERSION_1) { |
513 | | /* Reset destination object header's "chunk 0 size" flags */ |
514 | 0 | oh_dst->flags = (uint8_t)(oh_dst->flags & ~H5O_HDR_CHUNK0_SIZE); |
515 | | |
516 | | /* Determine correct value for chunk #0 size bits */ |
517 | 0 | if (dst_oh_size > 4294967295) |
518 | 0 | oh_dst->flags |= H5O_HDR_CHUNK0_8; |
519 | 0 | else if (dst_oh_size > 65535) |
520 | 0 | oh_dst->flags |= H5O_HDR_CHUNK0_4; |
521 | 0 | else if (dst_oh_size > 255) |
522 | 0 | oh_dst->flags |= H5O_HDR_CHUNK0_2; |
523 | 0 | } /* end if */ |
524 | | |
525 | | /* Check if the chunk's data portion is too small */ |
526 | 0 | dst_oh_gap = dst_oh_null = 0; |
527 | 0 | if (dst_oh_size < H5O_MIN_SIZE) { |
528 | 0 | size_t delta = (size_t)(H5O_MIN_SIZE - dst_oh_size); /* Delta in chunk size needed */ |
529 | | |
530 | | /* Sanity check */ |
531 | 0 | assert((oh_dst->flags & H5O_HDR_CHUNK0_SIZE) == H5O_HDR_CHUNK0_1); |
532 | | |
533 | | /* Determine whether to create gap or NULL message */ |
534 | 0 | if ((oh_dst->version > H5O_VERSION_1) && (delta < H5O_SIZEOF_MSGHDR_OH(oh_dst))) |
535 | 0 | dst_oh_gap = delta; |
536 | 0 | else { |
537 | | /* NULL message must be at least size of message header */ |
538 | 0 | if (delta < H5O_SIZEOF_MSGHDR_OH(oh_dst)) |
539 | 0 | delta = H5O_SIZEOF_MSGHDR_OH(oh_dst); |
540 | |
|
541 | 0 | dst_oh_null = delta; |
542 | 0 | } |
543 | | |
544 | | /* Increase destination object header size */ |
545 | 0 | dst_oh_size += delta; |
546 | | |
547 | | /* Sanity check */ |
548 | 0 | assert(dst_oh_size <= 255); |
549 | 0 | } /* end if */ |
550 | | |
551 | | /* Add in destination's object header size now */ |
552 | 0 | dst_oh_size += (uint64_t)H5O_SIZEOF_HDR(oh_dst); |
553 | | |
554 | | /* Allocate space for chunk in destination file */ |
555 | 0 | if (HADDR_UNDEF == |
556 | 0 | (oh_dst->chunk[0].addr = H5MF_alloc(oloc_dst->file, H5FD_MEM_OHDR, (hsize_t)dst_oh_size))) |
557 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "file allocation failed for object header"); |
558 | 0 | addr_new = oh_dst->chunk[0].addr; |
559 | | |
560 | | /* Create memory image for the new chunk */ |
561 | | /* Note: we use calloc() instead of malloc() here because older versions of |
562 | | * some messages don't initialize "unused" bytes and because we want to |
563 | | * write out the same version of the object header and older versions of |
564 | | * object headers aligned messages. In both those situations, it's |
565 | | * complex and error-prone to determine all the proper ways/places to |
566 | | * clear to zero bytes, so we just set the buffer to zero's here. |
567 | | * (QAK - 2010/08/17) |
568 | | */ |
569 | 0 | if (NULL == (oh_dst->chunk[0].image = H5FL_BLK_CALLOC(chunk_image, (size_t)dst_oh_size))) |
570 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed"); |
571 | | |
572 | | /* Set dest. chunk information */ |
573 | 0 | oh_dst->chunk[0].size = (size_t)dst_oh_size; |
574 | 0 | oh_dst->chunk[0].gap = dst_oh_gap; |
575 | | |
576 | | /* Update size of chunk array. The destination now has one chunk. */ |
577 | 0 | oh_dst->nchunks = 1; |
578 | | |
579 | | /* Set up raw pointers and copy messages that didn't need special |
580 | | * treatment. This has to happen after the destination header has been |
581 | | * allocated. |
582 | | */ |
583 | 0 | assert(H5O_SIZEOF_MSGHDR_OH(oh_src) == H5O_SIZEOF_MSGHDR_OH(oh_dst)); |
584 | 0 | msghdr_size = H5O_SIZEOF_MSGHDR_OH(oh_dst); |
585 | |
|
586 | 0 | current_pos = oh_dst->chunk[0].image; |
587 | | |
588 | | /* Write the magic number for versions > 1 and skip the rest of the |
589 | | * header. This will be written when the header is flushed to disk. |
590 | | */ |
591 | 0 | if (oh_dst->version > H5O_VERSION_1) |
592 | 0 | H5MM_memcpy(current_pos, H5O_HDR_MAGIC, (size_t)H5_SIZEOF_MAGIC); |
593 | 0 | current_pos += H5O_SIZEOF_HDR(oh_dst) - H5O_SIZEOF_CHKSUM_OH(oh_dst); |
594 | | |
595 | | /* Loop through destination messages, updating their "raw" info */ |
596 | 0 | null_msgs = 0; |
597 | 0 | for (mesgno = 0; mesgno < oh_dst->nmesgs; mesgno++) { |
598 | | /* Skip any deleted or NULL messages in the source unless the |
599 | | * preserve_null flag is set. |
600 | | */ |
601 | 0 | if (false == cpy_info->preserve_null) { |
602 | 0 | while (deleted[mesgno + null_msgs]) { |
603 | 0 | ++null_msgs; |
604 | 0 | assert(mesgno + null_msgs < oh_src->nmesgs); |
605 | 0 | } /* end while */ |
606 | 0 | } /* end if */ |
607 | | |
608 | | /* Set up convenience variables */ |
609 | 0 | mesg_src = &(oh_src->mesg[mesgno + null_msgs]); |
610 | 0 | mesg_dst = &(oh_dst->mesg[mesgno]); |
611 | | |
612 | | /* Copy each message that wasn't dirtied above */ |
613 | 0 | if (!mesg_dst->dirty) |
614 | | /* Copy the message header plus the message's raw data. */ |
615 | 0 | H5MM_memcpy(current_pos, mesg_src->raw - msghdr_size, msghdr_size + mesg_src->raw_size); |
616 | | |
617 | | /* Set message's raw pointer to destination chunk's new "image" */ |
618 | 0 | mesg_dst->raw = current_pos + msghdr_size; |
619 | | |
620 | | /* Move to location where next message should go */ |
621 | 0 | current_pos += mesg_dst->raw_size + msghdr_size; |
622 | 0 | } /* end for */ |
623 | | |
624 | | /* Save this in case more messages are added during NULL message checking */ |
625 | 0 | orig_dst_msgs = oh_dst->nmesgs; |
626 | | |
627 | | /* Check if we need to add a NULL message to this header */ |
628 | 0 | if (dst_oh_null > 0) { |
629 | 0 | size_t null_idx; /* Index of new NULL message */ |
630 | | |
631 | | /* Make sure we have enough space for new NULL message */ |
632 | 0 | if (oh_dst->nmesgs + 1 > oh_dst->alloc_nmesgs) |
633 | 0 | if (H5O__alloc_msgs(oh_dst, (size_t)1) < 0) |
634 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "can't allocate more space for messages"); |
635 | | |
636 | | /* Create null message for [rest of] space in new chunk */ |
637 | | /* (account for chunk's magic # & checksum) */ |
638 | 0 | null_idx = oh_dst->nmesgs++; |
639 | 0 | oh_dst->mesg[null_idx].type = H5O_MSG_NULL; |
640 | 0 | oh_dst->mesg[null_idx].dirty = true; |
641 | 0 | oh_dst->mesg[null_idx].native = NULL; |
642 | 0 | oh_dst->mesg[null_idx].raw = current_pos + msghdr_size; |
643 | 0 | oh_dst->mesg[null_idx].raw_size = dst_oh_null - msghdr_size; |
644 | 0 | oh_dst->mesg[null_idx].chunkno = 0; |
645 | 0 | } /* end if */ |
646 | | |
647 | | /* Make sure we filled the chunk, except for room at the end for a checksum */ |
648 | 0 | assert(current_pos + dst_oh_gap + dst_oh_null + H5O_SIZEOF_CHKSUM_OH(oh_dst) == |
649 | 0 | (size_t)dst_oh_size + oh_dst->chunk[0].image); |
650 | | |
651 | | /* Set the dest. object location to the first chunk address */ |
652 | 0 | assert(H5_addr_defined(addr_new)); |
653 | 0 | oloc_dst->addr = addr_new; |
654 | | |
655 | | /* If we are merging committed datatypes and this is a committed datatype, insert |
656 | | * the copied datatype into the list of committed datatypes in the target file. |
657 | | */ |
658 | 0 | if (cpy_info->merge_comm_dt && obj_class->type == H5O_TYPE_NAMED_DATATYPE) |
659 | 0 | if (H5O__copy_insert_comm_dt(oloc_src->file, oh_src, oloc_dst, cpy_info) < 0) |
660 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "can't insert committed datatype into destination list"); |
661 | | |
662 | | /* Allocate space for the address mapping of the object copied */ |
663 | 0 | if (NULL == (addr_map = H5FL_MALLOC(H5O_addr_map_t))) |
664 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed"); |
665 | | |
666 | | /* Insert the address mapping for the new object into the copied list */ |
667 | | /* (Do this here, because "post copy" possibly checks it) */ |
668 | 0 | H5F_GET_FILENO(oloc_src->file, addr_map->src_obj_pos.fileno); |
669 | 0 | addr_map->src_obj_pos.addr = oloc_src->addr; |
670 | 0 | addr_map->dst_addr = oloc_dst->addr; |
671 | 0 | addr_map->is_locked = true; /* We've locked the object currently */ |
672 | 0 | addr_map->inc_ref_count = 0; /* Start with no additional ref counts to add */ |
673 | 0 | addr_map->obj_class = obj_class; |
674 | 0 | addr_map->udata = cpy_udata; |
675 | | |
676 | | /* Insert into skip list */ |
677 | 0 | if (H5SL_insert(cpy_info->map_list, addr_map, &(addr_map->src_obj_pos)) < 0) { |
678 | 0 | addr_map = H5FL_FREE(H5O_addr_map_t, addr_map); |
679 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTINSERT, FAIL, "can't insert object into skip list"); |
680 | 0 | } /* end if */ |
681 | | |
682 | | /* "post copy" loop over messages, to fix up any messages which require a complete |
683 | | * object header for destination object |
684 | | */ |
685 | 0 | null_msgs = 0; |
686 | 0 | for (mesgno = 0; mesgno < orig_dst_msgs; mesgno++) { |
687 | | /* Skip any deleted or NULL messages in the source unless the |
688 | | * preserve_null flag is set |
689 | | */ |
690 | 0 | if (false == cpy_info->preserve_null) { |
691 | 0 | while (deleted[mesgno + null_msgs]) { |
692 | 0 | ++null_msgs; |
693 | 0 | assert(mesgno + null_msgs < oh_src->nmesgs); |
694 | 0 | } /* end while */ |
695 | 0 | } /* end if */ |
696 | | |
697 | | /* Set up convenience variables */ |
698 | 0 | mesg_src = &(oh_src->mesg[mesgno + null_msgs]); |
699 | 0 | mesg_dst = &(oh_dst->mesg[mesgno]); |
700 | | |
701 | | /* Check for message class to operate on */ |
702 | | /* (Use destination message, in case the message has been removed (i.e |
703 | | * converted to a nil message) in the destination -QAK) |
704 | | */ |
705 | 0 | copy_type = mesg_dst->type; |
706 | 0 | assert(copy_type); |
707 | |
|
708 | 0 | if (copy_type->post_copy_file && mesg_src->native) { |
709 | 0 | unsigned mesg_flags; /* Message flags */ |
710 | | |
711 | | /* Sanity check destination message */ |
712 | 0 | assert(mesg_dst->type == mesg_src->type); |
713 | 0 | assert(mesg_dst->native); |
714 | | |
715 | | /* Get destination message flags. mesg_dst->flags will contain the |
716 | | * original flags for now. */ |
717 | 0 | mesg_flags = (unsigned)mesg_dst->flags; |
718 | | |
719 | | /* the object header is needed in the post copy for shared message */ |
720 | 0 | cpy_info->oh_dst = oh_dst; |
721 | | |
722 | | /* Perform "post copy" operation on message */ |
723 | 0 | if ((copy_type->post_copy_file)(oloc_src, mesg_src->native, oloc_dst, mesg_dst->native, |
724 | 0 | &mesg_flags, cpy_info) < 0) |
725 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, |
726 | 0 | "unable to perform 'post copy' operation on message"); |
727 | | |
728 | | /* Verify that the flags did not change */ |
729 | 0 | assert(mesg_flags == (unsigned)mesg_dst->flags); |
730 | 0 | } /* end if */ |
731 | 0 | } /* end for */ |
732 | | |
733 | | /* Indicate that the destination address will no longer be locked */ |
734 | 0 | addr_map->is_locked = false; |
735 | | |
736 | | /* Increment object header's reference count, if any descendents have created links to this object */ |
737 | 0 | if (addr_map->inc_ref_count) { |
738 | 0 | H5_CHECK_OVERFLOW(addr_map->inc_ref_count, hsize_t, unsigned); |
739 | 0 | oh_dst->nlink += (unsigned)addr_map->inc_ref_count; |
740 | 0 | } /* end if */ |
741 | | |
742 | | /* Retag all copied metadata to apply the destination object's tag */ |
743 | 0 | if (H5AC_retag_copied_metadata(oloc_dst->file, oloc_dst->addr) < 0) |
744 | 0 | HGOTO_ERROR(H5E_CACHE, H5E_CANTTAG, FAIL, "unable to re-tag metadata entries"); |
745 | | |
746 | | /* Set metadata tag for destination object's object header */ |
747 | 0 | H5_BEGIN_TAG(oloc_dst->addr) |
748 | | |
749 | | /* Insert destination object header in cache */ |
750 | 0 | if (H5AC_insert_entry(oloc_dst->file, H5AC_OHDR, oloc_dst->addr, oh_dst, H5AC__NO_FLAGS_SET) < 0) |
751 | 0 | HGOTO_ERROR_TAG(H5E_OHDR, H5E_CANTINSERT, FAIL, "unable to cache object header"); |
752 | 0 | oh_dst = NULL; |
753 | 0 | inserted = true; |
754 | | |
755 | | /* Reset metadata tag */ |
756 | 0 | H5_END_TAG |
757 | | |
758 | | /* Set obj_type and udata, if requested */ |
759 | 0 | if (obj_type) { |
760 | 0 | assert(udata); |
761 | 0 | *obj_type = obj_class->type; |
762 | 0 | *udata = cpy_udata; |
763 | 0 | } /* end if */ |
764 | |
|
765 | 0 | done: |
766 | | /* Free deleted array */ |
767 | 0 | if (deleted) |
768 | 0 | H5MM_free(deleted); |
769 | | |
770 | | /* Release pointer to source object header and its derived objects */ |
771 | 0 | if (oh_src && H5O_unprotect(oloc_src, oh_src, H5AC__NO_FLAGS_SET) < 0) |
772 | 0 | HDONE_ERROR(H5E_OHDR, H5E_CANTUNPROTECT, FAIL, "unable to release object header"); |
773 | | |
774 | | /* Free destination object header on failure */ |
775 | 0 | if (ret_value < 0) { |
776 | 0 | if (oh_dst && !inserted) { |
777 | 0 | if (H5O__free(oh_dst, true) < 0) |
778 | 0 | HDONE_ERROR(H5E_OHDR, H5E_CANTFREE, FAIL, "unable to destroy object header data"); |
779 | 0 | if (H5O_loc_reset(oloc_dst) < 0) |
780 | 0 | HDONE_ERROR(H5E_OHDR, H5E_CANTFREE, FAIL, "unable to destroy object header data"); |
781 | 0 | } /* end if */ |
782 | |
|
783 | 0 | if (addr_map == NULL && cpy_udata) { |
784 | 0 | if (obj_class && obj_class->free_copy_file_udata) |
785 | 0 | obj_class->free_copy_file_udata(cpy_udata); |
786 | 0 | } /* end if */ |
787 | 0 | } |
788 | |
|
789 | 0 | FUNC_LEAVE_NOAPI_TAG(ret_value) |
790 | 0 | } /* end H5O__copy_header_real() */ |
791 | | |
792 | | /*------------------------------------------------------------------------- |
793 | | * Function: H5O_copy_header_map |
794 | | * |
795 | | * Purpose: Copy header object from one location to another, detecting |
796 | | * already mapped objects, etc. |
797 | | * |
798 | | * Return: Non-negative on success/Negative on failure |
799 | | * |
800 | | *------------------------------------------------------------------------- |
801 | | */ |
802 | | herr_t |
803 | | H5O_copy_header_map(const H5O_loc_t *oloc_src, H5O_loc_t *oloc_dst /*out*/, H5O_copy_t *cpy_info, |
804 | | bool inc_depth, H5O_type_t *obj_type, void **udata /*out*/) |
805 | 0 | { |
806 | 0 | H5O_addr_map_t *addr_map = NULL; /* Address mapping of object copied */ |
807 | 0 | H5_obj_t src_obj_pos; /* Position of source object */ |
808 | 0 | bool inc_link; /* Whether to increment the link count for the object */ |
809 | 0 | herr_t ret_value = SUCCEED; |
810 | |
|
811 | 0 | FUNC_ENTER_NOAPI(FAIL) |
812 | | |
813 | | /* Sanity check */ |
814 | 0 | assert(oloc_src); |
815 | 0 | assert(oloc_src->file); |
816 | 0 | assert(oloc_dst); |
817 | 0 | assert(oloc_dst->file); |
818 | 0 | assert(cpy_info); |
819 | | |
820 | | /* Create object "position" struct */ |
821 | 0 | H5F_GET_FILENO(oloc_src->file, src_obj_pos.fileno); |
822 | 0 | src_obj_pos.addr = oloc_src->addr; |
823 | | |
824 | | /* Search for the object in the skip list of copied objects */ |
825 | 0 | addr_map = (H5O_addr_map_t *)H5SL_search(cpy_info->map_list, &src_obj_pos); |
826 | | |
827 | | /* Check if address is already in list of objects copied */ |
828 | 0 | if (addr_map == NULL) { |
829 | | /* Copy object for the first time */ |
830 | | |
831 | | /* Check for incrementing the depth of copy */ |
832 | | /* (Can't do this for all copies, since committed datatypes should always be copied) */ |
833 | 0 | if (inc_depth) |
834 | 0 | cpy_info->curr_depth++; |
835 | | |
836 | | /* Copy object referred to */ |
837 | 0 | if (H5O__copy_header_real(oloc_src, oloc_dst, cpy_info, obj_type, udata) < 0) |
838 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTCOPY, FAIL, "unable to copy object"); |
839 | | |
840 | | /* Check for incrementing the depth of copy */ |
841 | 0 | if (inc_depth) |
842 | 0 | cpy_info->curr_depth--; |
843 | | |
844 | | /* When an object is copied for the first time, increment it's link */ |
845 | 0 | inc_link = true; |
846 | | |
847 | | /* indicate that a new object is created */ |
848 | 0 | ret_value++; |
849 | 0 | } /* end if */ |
850 | 0 | else { |
851 | | /* Object has already been copied, set its address in destination file */ |
852 | 0 | oloc_dst->addr = addr_map->dst_addr; |
853 | | |
854 | | /* Return saved obj_type and udata, if requested */ |
855 | 0 | if (obj_type) { |
856 | 0 | assert(udata); |
857 | 0 | *obj_type = addr_map->obj_class->type; |
858 | 0 | *udata = addr_map->udata; |
859 | 0 | } /* end if */ |
860 | | |
861 | | /* If the object is locked currently (because we are copying a group |
862 | | * hierarchy and this is a link to a group higher in the hierarchy), |
863 | | * increment it's deferred reference count instead of incrementing the |
864 | | * reference count now. |
865 | | */ |
866 | 0 | if (addr_map->is_locked) { |
867 | 0 | addr_map->inc_ref_count++; |
868 | 0 | inc_link = false; |
869 | 0 | } /* end if */ |
870 | 0 | else |
871 | 0 | inc_link = true; |
872 | 0 | } /* end else */ |
873 | | |
874 | | /* Increment destination object's link count, if allowed */ |
875 | 0 | if (inc_link) |
876 | 0 | if (H5O_link(oloc_dst, 1) < 0) |
877 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "unable to increment object link count"); |
878 | | |
879 | 0 | done: |
880 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
881 | 0 | } /* end H5O_copy_header_map() */ |
882 | | |
883 | | /*-------------------------------------------------------------------------- |
884 | | NAME |
885 | | H5O__copy_free_addrmap_cb |
886 | | PURPOSE |
887 | | Internal routine to free address maps from the skip list for copying objects |
888 | | USAGE |
889 | | herr_t H5O__copy_free_addrmap_cb(item, key, op_data) |
890 | | void *item; IN/OUT: Pointer to addr |
891 | | void *key; IN/OUT: (unused) |
892 | | void *op_data; IN: (unused) |
893 | | RETURNS |
894 | | Returns zero on success, negative on failure. |
895 | | DESCRIPTION |
896 | | Releases the memory for the address. |
897 | | GLOBAL VARIABLES |
898 | | COMMENTS, BUGS, ASSUMPTIONS |
899 | | EXAMPLES |
900 | | REVISION LOG |
901 | | --------------------------------------------------------------------------*/ |
902 | | static herr_t |
903 | | H5O__copy_free_addrmap_cb(void *_item, void H5_ATTR_UNUSED *key, void H5_ATTR_UNUSED *op_data) |
904 | 0 | { |
905 | 0 | H5O_addr_map_t *item = (H5O_addr_map_t *)_item; |
906 | |
|
907 | 0 | FUNC_ENTER_PACKAGE_NOERR |
908 | |
|
909 | 0 | assert(item); |
910 | | |
911 | | /* Release user data for particular type of object */ |
912 | 0 | if (item->udata) { |
913 | 0 | assert(item->obj_class); |
914 | 0 | assert(item->obj_class->free_copy_file_udata); |
915 | 0 | (item->obj_class->free_copy_file_udata)(item->udata); |
916 | 0 | } /* end if */ |
917 | | |
918 | | /* Release the item */ |
919 | 0 | item = H5FL_FREE(H5O_addr_map_t, item); |
920 | |
|
921 | 0 | FUNC_LEAVE_NOAPI(0) |
922 | 0 | } /* H5O__copy_free_addrmap_cb() */ |
923 | | |
924 | | /*------------------------------------------------------------------------- |
925 | | * Function: H5O__copy_header |
926 | | * |
927 | | * Purpose: copy header object from one location to another. |
928 | | * |
929 | | * Return: Non-negative on success/Negative on failure |
930 | | * |
931 | | *------------------------------------------------------------------------- |
932 | | */ |
933 | | static herr_t |
934 | | H5O__copy_header(const H5O_loc_t *oloc_src, H5O_loc_t *oloc_dst /*out */, hid_t ocpypl_id, hid_t lcpl_id) |
935 | 0 | { |
936 | 0 | H5O_copy_t cpy_info; /* Information for copying object */ |
937 | 0 | H5P_genplist_t *ocpy_plist; /* Object copy property list created */ |
938 | 0 | H5O_copy_dtype_merge_list_t *dt_list = NULL; /* List of datatype merge suggestions */ |
939 | 0 | H5O_mcdt_cb_info_t cb_info; /* Callback info struct */ |
940 | 0 | unsigned cpy_option = 0; /* Copy options */ |
941 | 0 | herr_t ret_value = SUCCEED; |
942 | |
|
943 | 0 | FUNC_ENTER_PACKAGE |
944 | | |
945 | | /* Sanity check */ |
946 | 0 | assert(oloc_src); |
947 | 0 | assert(oloc_src->file); |
948 | 0 | assert(H5_addr_defined(oloc_src->addr)); |
949 | 0 | assert(oloc_dst->file); |
950 | | |
951 | | /* Initialize copy info before errors can be thrown */ |
952 | 0 | memset(&cpy_info, 0, sizeof(H5O_copy_t)); |
953 | | |
954 | | /* Get the copy property list */ |
955 | 0 | if (NULL == (ocpy_plist = (H5P_genplist_t *)H5I_object(ocpypl_id))) |
956 | 0 | HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "not a property list"); |
957 | | |
958 | | /* Retrieve the copy parameters */ |
959 | 0 | if (H5P_get(ocpy_plist, H5O_CPY_OPTION_NAME, &cpy_option) < 0) |
960 | 0 | HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get object copy flag"); |
961 | | |
962 | | /* Retrieve the merge committed datatype list */ |
963 | 0 | if (H5P_peek(ocpy_plist, H5O_CPY_MERGE_COMM_DT_LIST_NAME, &dt_list) < 0) |
964 | 0 | HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get merge committed datatype list"); |
965 | | |
966 | | /* Get callback info */ |
967 | 0 | if (H5P_get(ocpy_plist, H5O_CPY_MCDT_SEARCH_CB_NAME, &cb_info) < 0) |
968 | 0 | HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get callback info"); |
969 | | |
970 | | /* Convert copy flags into copy struct */ |
971 | 0 | if ((cpy_option & H5O_COPY_SHALLOW_HIERARCHY_FLAG) > 0) { |
972 | 0 | cpy_info.copy_shallow = true; |
973 | 0 | cpy_info.max_depth = 1; |
974 | 0 | } /* end if */ |
975 | 0 | else |
976 | 0 | cpy_info.max_depth = -1; /* Current default is for full, recursive hier. copy */ |
977 | 0 | cpy_info.curr_depth = 0; |
978 | 0 | if ((cpy_option & H5O_COPY_EXPAND_SOFT_LINK_FLAG) > 0) |
979 | 0 | cpy_info.expand_soft_link = true; |
980 | 0 | if ((cpy_option & H5O_COPY_EXPAND_EXT_LINK_FLAG) > 0) |
981 | 0 | cpy_info.expand_ext_link = true; |
982 | 0 | if ((cpy_option & H5O_COPY_EXPAND_REFERENCE_FLAG) > 0) |
983 | 0 | cpy_info.expand_ref = true; |
984 | 0 | if ((cpy_option & H5O_COPY_WITHOUT_ATTR_FLAG) > 0) |
985 | 0 | cpy_info.copy_without_attr = true; |
986 | 0 | if ((cpy_option & H5O_COPY_PRESERVE_NULL_FLAG) > 0) |
987 | 0 | cpy_info.preserve_null = true; |
988 | 0 | if ((cpy_option & H5O_COPY_MERGE_COMMITTED_DTYPE_FLAG) > 0) |
989 | 0 | cpy_info.merge_comm_dt = true; |
990 | | |
991 | | /* Add dt_list to copy struct */ |
992 | 0 | cpy_info.dst_dt_suggestion_list = dt_list; |
993 | | |
994 | | /* Add set callback information */ |
995 | 0 | cpy_info.mcdt_cb = cb_info.func; |
996 | 0 | cpy_info.mcdt_ud = cb_info.user_data; |
997 | | |
998 | | /* Add property lists needed by callbacks */ |
999 | 0 | cpy_info.lcpl_id = lcpl_id; |
1000 | | |
1001 | | /* Create a skip list to keep track of which objects are copied */ |
1002 | 0 | if (NULL == (cpy_info.map_list = H5SL_create(H5SL_TYPE_OBJ, NULL))) |
1003 | 0 | HGOTO_ERROR(H5E_SLIST, H5E_CANTCREATE, FAIL, "cannot make skip list"); |
1004 | | |
1005 | | /* copy the object from the source file to the destination file */ |
1006 | 0 | if (H5O__copy_header_real(oloc_src, oloc_dst, &cpy_info, NULL, NULL) < 0) |
1007 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTCOPY, FAIL, "unable to copy object"); |
1008 | | |
1009 | 0 | done: |
1010 | 0 | if (cpy_info.map_list) |
1011 | 0 | H5SL_destroy(cpy_info.map_list, H5O__copy_free_addrmap_cb, NULL); |
1012 | 0 | if (cpy_info.dst_dt_list) |
1013 | 0 | H5SL_destroy(cpy_info.dst_dt_list, H5O__copy_free_comm_dt_cb, NULL); |
1014 | |
|
1015 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
1016 | 0 | } /* end H5O__copy_header() */ |
1017 | | |
1018 | | /*------------------------------------------------------------------------- |
1019 | | * Function: H5O__copy_obj |
1020 | | * |
1021 | | * Purpose: Copy an object to destination location |
1022 | | * |
1023 | | * Return: Non-negative on success/Negative on failure |
1024 | | * |
1025 | | *------------------------------------------------------------------------- |
1026 | | */ |
1027 | | static herr_t |
1028 | | H5O__copy_obj(H5G_loc_t *src_loc, H5G_loc_t *dst_loc, const char *dst_name, hid_t ocpypl_id, hid_t lcpl_id) |
1029 | 0 | { |
1030 | 0 | H5G_name_t new_path; /* Copied object group hier. path */ |
1031 | 0 | H5O_loc_t new_oloc; /* Copied object object location */ |
1032 | 0 | H5G_loc_t new_loc; /* Group location of object copied */ |
1033 | 0 | H5F_t *cached_dst_file; /* Cached destination file */ |
1034 | 0 | bool entry_inserted = false; /* Flag to indicate that the new entry was inserted into a group */ |
1035 | 0 | herr_t ret_value = SUCCEED; /* Return value */ |
1036 | |
|
1037 | 0 | FUNC_ENTER_PACKAGE |
1038 | |
|
1039 | 0 | assert(src_loc); |
1040 | 0 | assert(src_loc->oloc->file); |
1041 | 0 | assert(dst_loc); |
1042 | 0 | assert(dst_loc->oloc->file); |
1043 | 0 | assert(dst_name); |
1044 | | |
1045 | | /* Set up copied object location to fill in */ |
1046 | 0 | new_loc.oloc = &new_oloc; |
1047 | 0 | new_loc.path = &new_path; |
1048 | 0 | H5G_loc_reset(&new_loc); |
1049 | 0 | new_oloc.file = dst_loc->oloc->file; |
1050 | | |
1051 | | /* Make a copy of the destination file, in case the original is changed by |
1052 | | * H5O__copy_header. If and when oloc's point to the shared file struct, |
1053 | | * this will no longer be necessary, so this code can be removed. */ |
1054 | 0 | cached_dst_file = dst_loc->oloc->file; |
1055 | | |
1056 | | /* Copy the object from the source file to the destination file */ |
1057 | 0 | if (H5O__copy_header(src_loc->oloc, &new_oloc, ocpypl_id, lcpl_id) < 0) |
1058 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTCOPY, FAIL, "unable to copy object"); |
1059 | | |
1060 | | /* Patch dst_loc. Again, this can be removed once oloc's point to shared |
1061 | | * file structs. */ |
1062 | 0 | dst_loc->oloc->file = cached_dst_file; |
1063 | | |
1064 | | /* Insert the new object in the destination file's group */ |
1065 | 0 | if (H5L_link(dst_loc, dst_name, &new_loc, lcpl_id) < 0) |
1066 | 0 | HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "unable to insert link"); |
1067 | 0 | entry_inserted = true; |
1068 | |
|
1069 | 0 | done: |
1070 | | /* Free the ID to name buffers */ |
1071 | 0 | if (entry_inserted) |
1072 | 0 | H5G_loc_free(&new_loc); |
1073 | |
|
1074 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
1075 | 0 | } /* end H5O__copy_obj() */ |
1076 | | |
1077 | | /*------------------------------------------------------------------------- |
1078 | | * Function: H5O__copy_free_comm_dt_cb |
1079 | | * |
1080 | | * Purpose: Frees the merge committed dt skip list key and object. |
1081 | | * |
1082 | | * Return: SUCCEED (never fails) |
1083 | | * |
1084 | | *------------------------------------------------------------------------- |
1085 | | */ |
1086 | | static herr_t |
1087 | | H5O__copy_free_comm_dt_cb(void *item, void *_key, void H5_ATTR_UNUSED *_op_data) |
1088 | 0 | { |
1089 | 0 | haddr_t *addr = (haddr_t *)item; |
1090 | 0 | H5O_copy_search_comm_dt_key_t *key = (H5O_copy_search_comm_dt_key_t *)_key; |
1091 | |
|
1092 | 0 | FUNC_ENTER_PACKAGE_NOERR |
1093 | |
|
1094 | 0 | assert(addr); |
1095 | 0 | assert(key); |
1096 | 0 | assert(key->dt); |
1097 | |
|
1098 | 0 | key->dt = (H5T_t *)H5O_msg_free(H5O_DTYPE_ID, key->dt); |
1099 | 0 | key = H5FL_FREE(H5O_copy_search_comm_dt_key_t, key); |
1100 | 0 | addr = H5FL_FREE(haddr_t, addr); |
1101 | |
|
1102 | 0 | FUNC_LEAVE_NOAPI(SUCCEED) |
1103 | 0 | } /* end H5O__copy_free_comm_dt_cb */ |
1104 | | |
1105 | | /*------------------------------------------------------------------------- |
1106 | | * Function: H5O__copy_comm_dt_cmp |
1107 | | * |
1108 | | * Purpose: Skiplist callback used to compare 2 keys for the merge |
1109 | | * committed dt list. Mostly a wrapper for H5T_cmp. |
1110 | | * |
1111 | | * Return: 0 if key1 and key2 are equal. |
1112 | | * <0 if key1 is less than key2. |
1113 | | * >0 if key1 is greater than key2. |
1114 | | * |
1115 | | *------------------------------------------------------------------------- |
1116 | | */ |
1117 | | static int |
1118 | | H5O__copy_comm_dt_cmp(const void *_key1, const void *_key2) |
1119 | 0 | { |
1120 | 0 | const H5O_copy_search_comm_dt_key_t *key1 = (const H5O_copy_search_comm_dt_key_t *)_key1; |
1121 | 0 | const H5O_copy_search_comm_dt_key_t *key2 = (const H5O_copy_search_comm_dt_key_t *)_key2; |
1122 | 0 | int ret_value = 0; |
1123 | |
|
1124 | 0 | FUNC_ENTER_PACKAGE_NOERR |
1125 | | |
1126 | | /* Check fileno. It is unlikely to be different so check if they are equal |
1127 | | * first so only one comparison needs to be made. */ |
1128 | 0 | if (key1->fileno != key2->fileno) { |
1129 | 0 | if (key1->fileno < key2->fileno) |
1130 | 0 | HGOTO_DONE(-1); |
1131 | 0 | if (key1->fileno > key2->fileno) |
1132 | 0 | HGOTO_DONE(1); |
1133 | 0 | } /* end if */ |
1134 | | |
1135 | 0 | ret_value = H5T_cmp(key1->dt, key2->dt, false); |
1136 | |
|
1137 | 0 | done: |
1138 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
1139 | 0 | } /* end H5O__copy_comm_dt_cmp */ |
1140 | | |
1141 | | /*------------------------------------------------------------------------- |
1142 | | * Function: H5O__copy_search_comm_dt_attr_cb |
1143 | | * |
1144 | | * Purpose: Callback for H5O_attr_iterate_real from |
1145 | | * H5O__copy_search_comm_dt_check. Checks if the attribute's |
1146 | | * datatype is committed. If it is, adds it to the merge |
1147 | | * committed dt skiplist present in udata if it does not match |
1148 | | * any already present. |
1149 | | * |
1150 | | * Return: Non-negative on success/Negative on failure |
1151 | | * |
1152 | | *------------------------------------------------------------------------- |
1153 | | */ |
1154 | | static herr_t |
1155 | | H5O__copy_search_comm_dt_attr_cb(const H5A_t *attr, void *_udata) |
1156 | 0 | { |
1157 | 0 | H5O_copy_search_comm_dt_ud_t *udata = (H5O_copy_search_comm_dt_ud_t *)_udata; |
1158 | 0 | H5T_t *dt = NULL; /* Datatype */ |
1159 | 0 | H5O_copy_search_comm_dt_key_t *key = NULL; /* Skiplist key */ |
1160 | 0 | haddr_t *addr = NULL; /* Destination address */ |
1161 | 0 | bool obj_inserted = false; /* Object inserted into skip list */ |
1162 | 0 | herr_t ret_value = SUCCEED; /* Return value */ |
1163 | |
|
1164 | 0 | FUNC_ENTER_PACKAGE |
1165 | | |
1166 | | /* Sanity checks */ |
1167 | 0 | assert(attr); |
1168 | 0 | assert(udata); |
1169 | 0 | assert(udata->dst_dt_list); |
1170 | 0 | assert(H5_addr_defined(udata->obj_oloc.addr)); |
1171 | | |
1172 | | /* Get attribute datatype */ |
1173 | 0 | if (NULL == (dt = H5A_type(attr))) |
1174 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "can't get attribute datatype"); |
1175 | | |
1176 | | /* Check if the datatype is committed and search the skip list if so */ |
1177 | 0 | if (H5T_is_named(dt)) { |
1178 | | /* Allocate key */ |
1179 | 0 | if (NULL == (key = H5FL_MALLOC(H5O_copy_search_comm_dt_key_t))) |
1180 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed"); |
1181 | | |
1182 | | /* Copy datatype into key */ |
1183 | 0 | if (NULL == (key->dt = (H5T_t *)H5O_msg_copy(H5O_DTYPE_ID, dt, NULL))) |
1184 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "unable to copy datatype message"); |
1185 | | |
1186 | | /* Get datatype object fileno */ |
1187 | 0 | H5F_GET_FILENO(udata->obj_oloc.file, key->fileno); |
1188 | |
|
1189 | 0 | if (!H5SL_search(udata->dst_dt_list, key)) { |
1190 | | /* Allocate destination address */ |
1191 | 0 | if (NULL == (addr = H5FL_MALLOC(haddr_t))) |
1192 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed"); |
1193 | | |
1194 | | /* Add the destination datatype to the skip list */ |
1195 | 0 | *addr = ((H5O_shared_t *)(key->dt))->u.loc.oh_addr; |
1196 | 0 | if (H5SL_insert(udata->dst_dt_list, addr, key) < 0) |
1197 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTINSERT, FAIL, "can't insert object into skip list"); |
1198 | 0 | obj_inserted = true; |
1199 | 0 | } /* end if */ |
1200 | 0 | } /* end if */ |
1201 | | |
1202 | 0 | done: |
1203 | | /* Release resources */ |
1204 | 0 | if (!obj_inserted) { |
1205 | 0 | if (key) { |
1206 | 0 | if (key->dt) |
1207 | 0 | key->dt = (H5T_t *)H5O_msg_free(H5O_DTYPE_ID, key->dt); |
1208 | 0 | key = H5FL_FREE(H5O_copy_search_comm_dt_key_t, key); |
1209 | 0 | } /* end if */ |
1210 | 0 | if (addr) { |
1211 | 0 | assert(ret_value < 0); |
1212 | 0 | addr = H5FL_FREE(haddr_t, addr); |
1213 | 0 | } /* end if */ |
1214 | 0 | } /* end if */ |
1215 | |
|
1216 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
1217 | 0 | } /* end H5O__copy_search_comm_dt_attr_cb */ |
1218 | | |
1219 | | /*------------------------------------------------------------------------- |
1220 | | * Function: H5O__copy_search_comm_dt_check |
1221 | | * |
1222 | | * Purpose: Check if the object at obj_oloc is or contains a reference |
1223 | | * to a committed datatype. If it does, adds it to the merge |
1224 | | * committed dt skiplist present in udata if it does not match |
1225 | | * any already present. |
1226 | | * |
1227 | | * Return: Non-negative on success/Negative on failure |
1228 | | * |
1229 | | *------------------------------------------------------------------------- |
1230 | | */ |
1231 | | static herr_t |
1232 | | H5O__copy_search_comm_dt_check(const H5O_loc_t *obj_oloc, H5O_copy_search_comm_dt_ud_t *udata) |
1233 | 0 | { |
1234 | 0 | H5O_copy_search_comm_dt_key_t *key = NULL; /* Skiplist key */ |
1235 | 0 | haddr_t *addr = NULL; /* Destination address */ |
1236 | 0 | bool obj_inserted = false; /* Object inserted into skip list */ |
1237 | 0 | H5A_attr_iter_op_t attr_op; /* Attribute iteration operator */ |
1238 | 0 | const H5O_obj_class_t *obj_class = NULL; /* Type of object */ |
1239 | 0 | herr_t ret_value = SUCCEED; /* Return value */ |
1240 | |
|
1241 | 0 | FUNC_ENTER_PACKAGE |
1242 | | |
1243 | | /* Sanity checks */ |
1244 | 0 | assert(obj_oloc); |
1245 | 0 | assert(udata); |
1246 | 0 | assert(udata->dst_dt_list); |
1247 | 0 | assert(udata->dst_root_loc); |
1248 | | |
1249 | | /* Get pointer to object class for this object */ |
1250 | 0 | if ((obj_class = H5O__obj_class(obj_oloc)) == NULL) |
1251 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, FAIL, "unable to determine object type"); |
1252 | | |
1253 | | /* Check if the object is a datatype, a dataset using a committed |
1254 | | * datatype, or contains an attribute using a committed datatype */ |
1255 | 0 | if (obj_class->type == H5O_TYPE_NAMED_DATATYPE) { |
1256 | | /* Allocate key */ |
1257 | 0 | if (NULL == (key = H5FL_MALLOC(H5O_copy_search_comm_dt_key_t))) |
1258 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed"); |
1259 | | |
1260 | | /* Read the destination datatype */ |
1261 | 0 | if (NULL == (key->dt = (H5T_t *)H5O_msg_read(obj_oloc, H5O_DTYPE_ID, NULL))) |
1262 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "can't read DTYPE message"); |
1263 | | |
1264 | | /* Get destination object fileno */ |
1265 | 0 | H5F_GET_FILENO(obj_oloc->file, key->fileno); |
1266 | | |
1267 | | /* Check if the datatype is already present in the skip list */ |
1268 | 0 | if (!H5SL_search(udata->dst_dt_list, key)) { |
1269 | | /* Allocate destination address */ |
1270 | 0 | if (NULL == (addr = H5FL_MALLOC(haddr_t))) |
1271 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed"); |
1272 | | |
1273 | | /* Add the destination datatype to the skip list */ |
1274 | 0 | *addr = obj_oloc->addr; |
1275 | 0 | if (H5SL_insert(udata->dst_dt_list, addr, key) < 0) |
1276 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTINSERT, FAIL, "can't insert object into skip list"); |
1277 | 0 | obj_inserted = true; |
1278 | 0 | } /* end if */ |
1279 | 0 | } /* end if */ |
1280 | 0 | else if (obj_class->type == H5O_TYPE_DATASET) { |
1281 | | /* Allocate key */ |
1282 | 0 | if (NULL == (key = H5FL_MALLOC(H5O_copy_search_comm_dt_key_t))) |
1283 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed"); |
1284 | | |
1285 | | /* Read the destination datatype */ |
1286 | 0 | if (NULL == (key->dt = (H5T_t *)H5O_msg_read(obj_oloc, H5O_DTYPE_ID, NULL))) |
1287 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "can't read DTYPE message"); |
1288 | | |
1289 | | /* Check if the datatype is committed and search the skip list if so */ |
1290 | 0 | if (H5T_is_named(key->dt)) { |
1291 | | /* Get datatype object fileno */ |
1292 | 0 | H5F_GET_FILENO(obj_oloc->file, key->fileno); |
1293 | |
|
1294 | 0 | if (!H5SL_search(udata->dst_dt_list, key)) { |
1295 | | /* Allocate destination address */ |
1296 | 0 | if (NULL == (addr = H5FL_MALLOC(haddr_t))) |
1297 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed"); |
1298 | | |
1299 | | /* Add the destination datatype to the skip list */ |
1300 | 0 | *addr = ((H5O_shared_t *)(key->dt))->u.loc.oh_addr; |
1301 | 0 | if (H5SL_insert(udata->dst_dt_list, addr, key) < 0) |
1302 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTINSERT, FAIL, "can't insert object into skip list"); |
1303 | 0 | obj_inserted = true; |
1304 | 0 | } /* end if */ |
1305 | 0 | } /* end if */ |
1306 | 0 | } /* end else */ |
1307 | | |
1308 | | /* Search within attributes */ |
1309 | 0 | attr_op.op_type = H5A_ATTR_OP_LIB; |
1310 | 0 | attr_op.u.lib_op = H5O__copy_search_comm_dt_attr_cb; |
1311 | 0 | udata->obj_oloc.file = obj_oloc->file; |
1312 | 0 | udata->obj_oloc.addr = obj_oloc->addr; |
1313 | 0 | if (H5O_attr_iterate_real((hid_t)-1, obj_oloc, H5_INDEX_NAME, H5_ITER_NATIVE, (hsize_t)0, NULL, &attr_op, |
1314 | 0 | udata) < 0) |
1315 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_BADITER, FAIL, "error iterating over attributes"); |
1316 | | |
1317 | 0 | done: |
1318 | | /* Release resources */ |
1319 | 0 | if (!obj_inserted) { |
1320 | 0 | if (key) { |
1321 | 0 | if (key->dt) |
1322 | 0 | key->dt = (H5T_t *)H5O_msg_free(H5O_DTYPE_ID, key->dt); |
1323 | 0 | key = H5FL_FREE(H5O_copy_search_comm_dt_key_t, key); |
1324 | 0 | } /* end if */ |
1325 | 0 | if (addr) { |
1326 | 0 | assert(ret_value < 0); |
1327 | 0 | addr = H5FL_FREE(haddr_t, addr); |
1328 | 0 | } /* end if */ |
1329 | 0 | } /* end if */ |
1330 | |
|
1331 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
1332 | 0 | } /* end H5O__copy_search_comm_dt_check */ |
1333 | | |
1334 | | /*------------------------------------------------------------------------- |
1335 | | * Function: H5O__copy_search_comm_dt_cb |
1336 | | * |
1337 | | * Purpose: H5G_visit callback to add committed datatypes to the merge |
1338 | | * committed dt skiplist. Mostly a wrapper for |
1339 | | * H5O__copy_search_comm_dt_check. |
1340 | | * |
1341 | | * Return: Non-negative on success/Negative on failure |
1342 | | * |
1343 | | *------------------------------------------------------------------------- |
1344 | | */ |
1345 | | static herr_t |
1346 | | H5O__copy_search_comm_dt_cb(hid_t H5_ATTR_UNUSED group, const char H5_ATTR_UNUSED *name, |
1347 | | const H5O_loc_t *obj_oloc, void *_udata) |
1348 | 0 | { |
1349 | 0 | H5O_copy_search_comm_dt_ud_t *udata = |
1350 | 0 | (H5O_copy_search_comm_dt_ud_t *)_udata; /* Skip list of dtypes in dest file */ |
1351 | 0 | herr_t ret_value = H5_ITER_CONT; /* Return value */ |
1352 | |
|
1353 | 0 | FUNC_ENTER_PACKAGE |
1354 | | |
1355 | | /* Sanity checks */ |
1356 | 0 | assert(name); |
1357 | 0 | assert(obj_oloc); |
1358 | 0 | assert(udata); |
1359 | 0 | assert(udata->dst_dt_list); |
1360 | 0 | assert(udata->dst_root_loc); |
1361 | | |
1362 | | /* Check object and add to skip list if appropriate */ |
1363 | 0 | if (H5O__copy_search_comm_dt_check(obj_oloc, udata) < 0) |
1364 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, H5_ITER_ERROR, "can't check object"); |
1365 | | |
1366 | 0 | done: |
1367 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
1368 | 0 | } /* end H5O__copy_search_comm_dt_cb */ |
1369 | | |
1370 | | /*------------------------------------------------------------------------- |
1371 | | * Function: H5O__copy_search_comm_dt |
1372 | | * |
1373 | | * Purpose: Checks if the committed datatype present in oh_src matches any |
1374 | | * in the destination file, building the destination file |
1375 | | * skiplist as necessary. |
1376 | | * |
1377 | | * Return: true if a match is found in the destination file |
1378 | | * - oloc_dst will contain the address |
1379 | | * false if a match is not found |
1380 | | * Negative on failure |
1381 | | * |
1382 | | *------------------------------------------------------------------------- |
1383 | | */ |
1384 | | static htri_t |
1385 | | H5O__copy_search_comm_dt(H5F_t *file_src, H5O_t *oh_src, H5O_loc_t *oloc_dst /*in, out*/, |
1386 | | H5O_copy_t *cpy_info) |
1387 | 0 | { |
1388 | 0 | H5O_copy_search_comm_dt_key_t *key = NULL; /* Skiplist key */ |
1389 | 0 | haddr_t *dst_addr; /* Destination datatype address */ |
1390 | 0 | H5G_loc_t dst_root_loc = {NULL, NULL}; /* Destination root group location */ |
1391 | 0 | H5O_copy_search_comm_dt_ud_t udata; /* Group iteration user data */ |
1392 | 0 | herr_t ret_value = false; /* Return value */ |
1393 | |
|
1394 | 0 | FUNC_ENTER_PACKAGE |
1395 | | |
1396 | | /* Sanity checks */ |
1397 | 0 | assert(oh_src); |
1398 | 0 | assert(oloc_dst); |
1399 | 0 | assert(oloc_dst->file); |
1400 | 0 | assert(H5F_ID_EXISTS(oloc_dst->file)); |
1401 | 0 | assert(cpy_info); |
1402 | | |
1403 | | /* Allocate key */ |
1404 | 0 | if (NULL == (key = H5FL_MALLOC(H5O_copy_search_comm_dt_key_t))) |
1405 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed"); |
1406 | | |
1407 | | /* Read the source datatype */ |
1408 | 0 | if (NULL == (key->dt = (H5T_t *)H5O_msg_read_oh(file_src, oh_src, H5O_DTYPE_ID, NULL))) |
1409 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "can't read DTYPE message"); |
1410 | | |
1411 | | /* Get destination object fileno */ |
1412 | 0 | H5F_GET_FILENO(oloc_dst->file, key->fileno); |
1413 | | |
1414 | | /* Check if the destination dtype list exists, create it if it does not */ |
1415 | 0 | if (!cpy_info->dst_dt_list) { |
1416 | | /* Create the skip list */ |
1417 | 0 | if (NULL == (cpy_info->dst_dt_list = H5SL_create(H5SL_TYPE_GENERIC, H5O__copy_comm_dt_cmp))) |
1418 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTCREATE, FAIL, "can't create skip list for committed datatypes"); |
1419 | | |
1420 | | /* Add suggested types to list, if they are present */ |
1421 | 0 | if (cpy_info->dst_dt_suggestion_list) { |
1422 | 0 | H5O_copy_dtype_merge_list_t *suggestion = cpy_info->dst_dt_suggestion_list; |
1423 | 0 | H5G_loc_t obj_loc; /* Location of object */ |
1424 | 0 | H5O_loc_t obj_oloc; /* Object's object location */ |
1425 | 0 | H5G_name_t obj_path; /* Object's group hier. path */ |
1426 | | |
1427 | | /* Set up the root group in the destination file */ |
1428 | 0 | if (NULL == (dst_root_loc.oloc = H5G_oloc(H5G_rootof(oloc_dst->file)))) |
1429 | 0 | HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to get object location for root group"); |
1430 | 0 | if (NULL == (dst_root_loc.path = H5G_nameof(H5G_rootof(oloc_dst->file)))) |
1431 | 0 | HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to get path for root group"); |
1432 | | |
1433 | | /* Set up opened group location to fill in */ |
1434 | 0 | obj_loc.oloc = &obj_oloc; |
1435 | 0 | obj_loc.path = &obj_path; |
1436 | 0 | H5G_loc_reset(&obj_loc); |
1437 | | |
1438 | | /* Build udata */ |
1439 | 0 | udata.dst_dt_list = cpy_info->dst_dt_list; |
1440 | 0 | udata.dst_root_loc = &dst_root_loc; |
1441 | 0 | udata.obj_oloc.file = NULL; |
1442 | 0 | udata.obj_oloc.addr = HADDR_UNDEF; |
1443 | | |
1444 | | /* Walk through the list of datatype suggestions */ |
1445 | 0 | while (suggestion) { |
1446 | 0 | bool exists = false; |
1447 | | |
1448 | | /* Find the object */ |
1449 | 0 | if (H5G_loc_exists(&dst_root_loc, suggestion->path, &exists /*out*/) < 0) |
1450 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTFIND, FAIL, "can't check object's existence"); |
1451 | | |
1452 | 0 | if (exists) { |
1453 | | /* Retrieve the object location info */ |
1454 | 0 | if (H5G_loc_find(&dst_root_loc, suggestion->path, &obj_loc /*out*/) < 0) |
1455 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "can't retrieve object location"); |
1456 | | |
1457 | | /* Check object and add to skip list if appropriate */ |
1458 | 0 | if (H5O__copy_search_comm_dt_check(&obj_oloc, &udata) < 0) { |
1459 | 0 | if (H5G_loc_free(&obj_loc) < 0) |
1460 | 0 | HERROR(H5E_OHDR, H5E_CANTRELEASE, "can't free location"); |
1461 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "can't check object"); |
1462 | 0 | } /* end if */ |
1463 | | |
1464 | | /* Free location */ |
1465 | 0 | if (H5G_loc_free(&obj_loc) < 0) |
1466 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTRELEASE, FAIL, "can't free location"); |
1467 | 0 | } /* end if */ |
1468 | | |
1469 | | /* Advance the suggestion pointer */ |
1470 | 0 | suggestion = suggestion->next; |
1471 | 0 | } /* end while */ |
1472 | 0 | } /* end if */ |
1473 | 0 | } |
1474 | | |
1475 | 0 | if (!cpy_info->dst_dt_list_complete) { |
1476 | | /* Search for the type in the destination file, and return its address |
1477 | | * if found, but only if the list is populated with and only with |
1478 | | * suggested types. We will search complete lists later. */ |
1479 | 0 | if (cpy_info->dst_dt_suggestion_list && |
1480 | 0 | NULL != (dst_addr = (haddr_t *)H5SL_search(cpy_info->dst_dt_list, key))) { |
1481 | 0 | oloc_dst->addr = *dst_addr; |
1482 | 0 | ret_value = true; |
1483 | 0 | } /* end if */ |
1484 | 0 | else { |
1485 | 0 | H5O_mcdt_search_ret_t search_cb_ret = H5O_MCDT_SEARCH_CONT; |
1486 | | |
1487 | | /* Make callback to see if we should search destination file */ |
1488 | 0 | if (cpy_info->mcdt_cb) { |
1489 | | /* Prepare & restore library for user callback */ |
1490 | 0 | H5_BEFORE_USER_CB(FAIL) |
1491 | 0 | { |
1492 | 0 | search_cb_ret = cpy_info->mcdt_cb(cpy_info->mcdt_ud); |
1493 | 0 | } |
1494 | 0 | H5_AFTER_USER_CB(FAIL) |
1495 | 0 | if (H5O_MCDT_SEARCH_ERROR == search_cb_ret) |
1496 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CALLBACK, FAIL, "callback returned error"); |
1497 | 0 | } |
1498 | | |
1499 | 0 | if (search_cb_ret == H5O_MCDT_SEARCH_CONT) { |
1500 | | /* Build the complete dst dt list */ |
1501 | | /* Set up the root group in the destination file, if necessary */ |
1502 | 0 | if (!dst_root_loc.oloc) { |
1503 | 0 | assert(!dst_root_loc.path); |
1504 | 0 | if (NULL == (dst_root_loc.oloc = H5G_oloc(H5G_rootof(oloc_dst->file)))) |
1505 | 0 | HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, |
1506 | 0 | "unable to get object location for root group"); |
1507 | 0 | if (NULL == (dst_root_loc.path = H5G_nameof(H5G_rootof(oloc_dst->file)))) |
1508 | 0 | HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unable to get path for root group"); |
1509 | 0 | } /* end if */ |
1510 | 0 | else |
1511 | 0 | assert(dst_root_loc.path); |
1512 | | |
1513 | | /* Build udata. Note that this may be done twice in some cases, but |
1514 | | * it should be rare and should be cheaper on average than trying to |
1515 | | * keep track of whether it was done before. */ |
1516 | 0 | udata.dst_dt_list = cpy_info->dst_dt_list; |
1517 | 0 | udata.dst_root_loc = &dst_root_loc; |
1518 | 0 | udata.obj_oloc.file = NULL; |
1519 | 0 | udata.obj_oloc.addr = HADDR_UNDEF; |
1520 | | |
1521 | | /* Traverse the destination file, adding committed datatypes to the skip |
1522 | | * list */ |
1523 | 0 | if (H5G_visit(&dst_root_loc, "/", H5_INDEX_NAME, H5_ITER_NATIVE, NULL, |
1524 | 0 | H5O__copy_search_comm_dt_cb, &udata) < 0) |
1525 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_BADITER, FAIL, "object visitation failed"); |
1526 | 0 | cpy_info->dst_dt_list_complete = true; |
1527 | 0 | } /* end if */ |
1528 | 0 | else if (search_cb_ret != H5O_MCDT_SEARCH_STOP) |
1529 | 0 | HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "unknown return value for callback"); |
1530 | 0 | } /* end if */ |
1531 | 0 | } /* end if */ |
1532 | | |
1533 | | /* Search for the type in the destination file, and return its address if |
1534 | | * found, but only if the list is complete */ |
1535 | 0 | if (cpy_info->dst_dt_list_complete) { |
1536 | 0 | if (NULL != (dst_addr = (haddr_t *)H5SL_search(cpy_info->dst_dt_list, key))) { |
1537 | 0 | oloc_dst->addr = *dst_addr; |
1538 | 0 | ret_value = true; |
1539 | 0 | } /* end if */ |
1540 | 0 | } /* end if */ |
1541 | |
|
1542 | 0 | done: |
1543 | 0 | if (key) { |
1544 | 0 | if (key->dt) |
1545 | 0 | key->dt = (H5T_t *)H5O_msg_free(H5O_DTYPE_ID, key->dt); |
1546 | 0 | key = H5FL_FREE(H5O_copy_search_comm_dt_key_t, key); |
1547 | 0 | } /* end if */ |
1548 | |
|
1549 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
1550 | 0 | } /* end H5O__copy_search_comm_dt */ |
1551 | | |
1552 | | /*------------------------------------------------------------------------- |
1553 | | * Function: H5O__copy_insert_comm_dt |
1554 | | * |
1555 | | * Purpose: Insert the committed datatype at oloc_dst into the merge committed |
1556 | | * dt skiplist. The datatype must not be present already. |
1557 | | * |
1558 | | * Return: Non-negative on success/Negative on failure |
1559 | | * |
1560 | | *------------------------------------------------------------------------- |
1561 | | */ |
1562 | | static herr_t |
1563 | | H5O__copy_insert_comm_dt(H5F_t *file_src, H5O_t *oh_src, H5O_loc_t *oloc_dst, H5O_copy_t *cpy_info) |
1564 | 0 | { |
1565 | 0 | H5O_copy_search_comm_dt_key_t *key = NULL; /* Skiplist key */ |
1566 | 0 | haddr_t *addr = NULL; /* Destination object address */ |
1567 | 0 | herr_t ret_value = SUCCEED; /* Return value */ |
1568 | |
|
1569 | 0 | FUNC_ENTER_PACKAGE |
1570 | | |
1571 | | /* Sanity checks */ |
1572 | 0 | assert(oh_src); |
1573 | 0 | assert(oloc_dst); |
1574 | 0 | assert(oloc_dst->file); |
1575 | 0 | assert(oloc_dst->addr != HADDR_UNDEF); |
1576 | 0 | assert(cpy_info); |
1577 | 0 | assert(cpy_info->dst_dt_list); |
1578 | | |
1579 | | /* Allocate key */ |
1580 | 0 | if (NULL == (key = H5FL_MALLOC(H5O_copy_search_comm_dt_key_t))) |
1581 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed"); |
1582 | | |
1583 | | /* Read the datatype. Read from the source file because the destination |
1584 | | * object could be changed in the post-copy. */ |
1585 | 0 | if (NULL == (key->dt = (H5T_t *)H5O_msg_read_oh(file_src, oh_src, H5O_DTYPE_ID, NULL))) |
1586 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTGET, FAIL, "can't read DTYPE message"); |
1587 | | |
1588 | | /* Get destination object fileno */ |
1589 | 0 | H5F_GET_FILENO(oloc_dst->file, key->fileno); |
1590 | | |
1591 | | /* Allocate destination address */ |
1592 | 0 | if (NULL == (addr = H5FL_MALLOC(haddr_t))) |
1593 | 0 | HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed"); |
1594 | | |
1595 | | /* Add the destination datatype to the skip list */ |
1596 | 0 | *addr = oloc_dst->addr; |
1597 | 0 | if (H5SL_insert(cpy_info->dst_dt_list, addr, key) < 0) |
1598 | 0 | HGOTO_ERROR(H5E_OHDR, H5E_CANTINSERT, FAIL, "can't insert object into skip list"); |
1599 | | |
1600 | 0 | done: |
1601 | 0 | if (ret_value < 0) { |
1602 | 0 | if (key) { |
1603 | 0 | if (key->dt) |
1604 | 0 | key->dt = (H5T_t *)H5O_msg_free(H5O_DTYPE_ID, key->dt); |
1605 | 0 | key = H5FL_FREE(H5O_copy_search_comm_dt_key_t, key); |
1606 | 0 | } /* end if */ |
1607 | 0 | if (addr) |
1608 | 0 | addr = H5FL_FREE(haddr_t, addr); |
1609 | 0 | } /* end if */ |
1610 | |
|
1611 | 0 | FUNC_LEAVE_NOAPI(ret_value) |
1612 | 0 | } /* end H5O__copy_insert_comm_dt */ |