/src/gdal/netcdf-c-4.7.4/libhdf5/hdf5file.c
Line | Count | Source |
1 | | /* Copyright 2003-2018, University Corporation for Atmospheric |
2 | | * Research. See COPYRIGHT file for copying and redistribution |
3 | | * conditions. */ |
4 | | /** |
5 | | * @file |
6 | | * @internal The netCDF-4 file functions. |
7 | | * |
8 | | * This file is part of netcdf-4, a netCDF-like interface for HDF5, or |
9 | | * a HDF5 backend for netCDF, depending on your point of view. |
10 | | * |
11 | | * @author Ed Hartnett |
12 | | */ |
13 | | |
14 | | #include "config.h" |
15 | | #include "hdf5internal.h" |
16 | | #include "ncrc.h" |
17 | | |
18 | | extern int NC4_extract_file_image(NC_FILE_INFO_T* h5); /* In nc4memcb.c */ |
19 | | |
20 | | static void dumpopenobjects(NC_FILE_INFO_T* h5); |
21 | | |
22 | | /** @internal When we have open objects at file close, should |
23 | | we log them or print to stdout. Default is to log. */ |
24 | | #define LOGOPEN 1 |
25 | | |
26 | | /** @internal Number of reserved attributes. These attributes are |
27 | | * hidden from the netcdf user, but exist in the HDF5 file to help |
28 | | * netcdf read the file. */ |
29 | 340k | #define NRESERVED 11 /*|NC_reservedatt|*/ |
30 | | |
31 | | /** @internal List of reserved attributes. This list must be in sorted |
32 | | * order for binary search. */ |
33 | | static const NC_reservedatt NC_reserved[NRESERVED] = { |
34 | | {NC_ATT_CLASS, READONLYFLAG|DIMSCALEFLAG}, /*CLASS*/ |
35 | | {NC_ATT_DIMENSION_LIST, READONLYFLAG|DIMSCALEFLAG}, /*DIMENSION_LIST*/ |
36 | | {NC_ATT_NAME, READONLYFLAG|DIMSCALEFLAG}, /*NAME*/ |
37 | | {NC_ATT_REFERENCE_LIST, READONLYFLAG|DIMSCALEFLAG}, /*REFERENCE_LIST*/ |
38 | | {NC_ATT_FORMAT, READONLYFLAG}, /*_Format*/ |
39 | | {ISNETCDF4ATT, READONLYFLAG|NAMEONLYFLAG}, /*_IsNetcdf4*/ |
40 | | {NCPROPS, READONLYFLAG|NAMEONLYFLAG|MATERIALIZEDFLAG},/*_NCProperties*/ |
41 | | {NC_ATT_COORDINATES, READONLYFLAG|DIMSCALEFLAG|MATERIALIZEDFLAG},/*_Netcdf4Coordinates*/ |
42 | | {NC_DIMID_ATT_NAME, READONLYFLAG|DIMSCALEFLAG|MATERIALIZEDFLAG},/*_Netcdf4Dimid*/ |
43 | | {SUPERBLOCKATT, READONLYFLAG|NAMEONLYFLAG},/*_SuperblockVersion*/ |
44 | | {NC3_STRICT_ATT_NAME, READONLYFLAG|MATERIALIZEDFLAG}, /*_nc3_strict*/ |
45 | | }; |
46 | | |
47 | | /* Forward */ |
48 | | static int NC4_enddef(int ncid); |
49 | | static void dumpopenobjects(NC_FILE_INFO_T* h5); |
50 | | |
51 | | /** |
52 | | * @internal Define a binary searcher for reserved attributes |
53 | | * @param name for which to search |
54 | | * @return pointer to the matchig NC_reservedatt structure. |
55 | | * @return NULL if not found. |
56 | | * @author Dennis Heimbigner |
57 | | */ |
58 | | const NC_reservedatt* |
59 | | NC_findreserved(const char* name) |
60 | 340k | { |
61 | 340k | int n = NRESERVED; |
62 | 340k | int L = 0; |
63 | 340k | int R = (n - 1); |
64 | 1.55M | for(;;) { |
65 | 1.55M | if(L > R) break; |
66 | 1.27M | int m = (L + R) / 2; |
67 | 1.27M | const NC_reservedatt* p = &NC_reserved[m]; |
68 | 1.27M | int cmp = strcmp(p->name,name); |
69 | 1.27M | if(cmp == 0) return p; |
70 | 1.21M | if(cmp < 0) |
71 | 650k | L = (m + 1); |
72 | 560k | else /*cmp > 0*/ |
73 | 560k | R = (m - 1); |
74 | 1.21M | } |
75 | 279k | return NULL; |
76 | 340k | } |
77 | | |
78 | | /** |
79 | | * @internal Recursively determine if there is a mismatch between |
80 | | * order of coordinate creation and associated dimensions in this |
81 | | * group or any subgroups, to find out if we have to handle that |
82 | | * situation. Also check if there are any multidimensional coordinate |
83 | | * variables defined, which require the same treatment to fix a |
84 | | * potential bug when such variables occur in subgroups. |
85 | | * |
86 | | * @param grp Pointer to group info struct. |
87 | | * @param bad_coord_orderp Pointer that gets 1 if there is a bad |
88 | | * coordinate order. |
89 | | * |
90 | | * @returns NC_NOERR No error. |
91 | | * @returns NC_EHDFERR HDF5 returned an error. |
92 | | * @author Ed Hartnett |
93 | | */ |
94 | | static int |
95 | | detect_preserve_dimids(NC_GRP_INFO_T *grp, nc_bool_t *bad_coord_orderp) |
96 | 7.56k | { |
97 | 7.56k | NC_VAR_INFO_T *var; |
98 | 7.56k | NC_GRP_INFO_T *child_grp; |
99 | 7.56k | int last_dimid = -1; |
100 | 7.56k | int retval; |
101 | 7.56k | int i; |
102 | | |
103 | | /* Iterate over variables in this group */ |
104 | 67.4k | for (i=0; i < ncindexsize(grp->vars); i++) |
105 | 61.3k | { |
106 | 61.3k | var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i); |
107 | 61.3k | if (var == NULL) continue; |
108 | | /* Only matters for dimension scale variables, with non-scalar dimensionality */ |
109 | 61.3k | if (var->dimscale && var->ndims) |
110 | 1.91k | { |
111 | | /* If the user writes coord vars in a different order then he |
112 | | * defined their dimensions, then, when the file is reopened, the |
113 | | * order of the dimids will change to match the order of the coord |
114 | | * vars. Detect if this is about to happen. */ |
115 | 1.91k | if (var->dimids[0] < last_dimid) |
116 | 37 | { |
117 | 37 | LOG((5, "%s: %s is out of order coord var", __func__, var->hdr.name)); |
118 | 37 | *bad_coord_orderp = NC_TRUE; |
119 | 37 | return NC_NOERR; |
120 | 37 | } |
121 | 1.87k | last_dimid = var->dimids[0]; |
122 | | |
123 | | /* If there are multidimensional coordinate variables defined, then |
124 | | * it's also necessary to preserve dimension IDs when the file is |
125 | | * reopened ... */ |
126 | 1.87k | if (var->ndims > 1) |
127 | 701 | { |
128 | 701 | LOG((5, "%s: %s is multidimensional coord var", __func__, var->hdr.name)); |
129 | 701 | *bad_coord_orderp = NC_TRUE; |
130 | 701 | return NC_NOERR; |
131 | 701 | } |
132 | | |
133 | | /* Did the user define a dimension, end define mode, reenter define |
134 | | * mode, and then define a coordinate variable for that dimension? |
135 | | * If so, dimensions will be out of order. */ |
136 | 1.17k | if (var->is_new_var || var->became_coord_var) |
137 | 747 | { |
138 | 747 | LOG((5, "%s: coord var defined after enddef/redef", __func__)); |
139 | 747 | *bad_coord_orderp = NC_TRUE; |
140 | 747 | return NC_NOERR; |
141 | 747 | } |
142 | 1.17k | } |
143 | 61.3k | } |
144 | | |
145 | | /* If there are any child groups, check them also for this condition. */ |
146 | 6.07k | for (i = 0; i < ncindexsize(grp->children); i++) |
147 | 0 | { |
148 | 0 | if (!(child_grp = (NC_GRP_INFO_T *)ncindexith(grp->children, i))) |
149 | 0 | continue; |
150 | 0 | if ((retval = detect_preserve_dimids(child_grp, bad_coord_orderp))) |
151 | 0 | return retval; |
152 | 0 | } |
153 | 6.07k | return NC_NOERR; |
154 | 6.07k | } |
155 | | |
156 | | /** |
157 | | * @internal This function will write all changed metadata and flush |
158 | | * HDF5 file to disk. |
159 | | * |
160 | | * @param h5 Pointer to HDF5 file info struct. |
161 | | * |
162 | | * @return ::NC_NOERR No error. |
163 | | * @return ::NC_EINDEFINE Classic model file in define mode. |
164 | | * @return ::NC_EHDFERR HDF5 error. |
165 | | * @author Ed Hartnett |
166 | | */ |
167 | | static int |
168 | | sync_netcdf4_file(NC_FILE_INFO_T *h5) |
169 | 7.56k | { |
170 | 7.56k | NC_HDF5_FILE_INFO_T *hdf5_info; |
171 | 7.56k | int retval; |
172 | | |
173 | 7.56k | assert(h5 && h5->format_file_info); |
174 | 7.56k | LOG((3, "%s", __func__)); |
175 | | |
176 | | /* If we're in define mode, that's an error, for strict nc3 rules, |
177 | | * otherwise, end define mode. */ |
178 | 7.56k | if (h5->flags & NC_INDEF) |
179 | 0 | { |
180 | 0 | if (h5->cmode & NC_CLASSIC_MODEL) |
181 | 0 | return NC_EINDEFINE; |
182 | | |
183 | | /* Turn define mode off. */ |
184 | 0 | h5->flags ^= NC_INDEF; |
185 | | |
186 | | /* Redef mode needs to be tracked separately for nc_abort. */ |
187 | 0 | h5->redef = NC_FALSE; |
188 | 0 | } |
189 | | |
190 | | #ifdef LOGGING |
191 | | /* This will print out the names, types, lens, etc of the vars and |
192 | | atts in the file, if the logging level is 2 or greater. */ |
193 | | log_metadata_nc(h5); |
194 | | #endif |
195 | | |
196 | | /* Write any metadata that has changed. */ |
197 | 7.56k | if (!h5->no_write) |
198 | 7.56k | { |
199 | 7.56k | nc_bool_t bad_coord_order = NC_FALSE; |
200 | | |
201 | | /* Write any user-defined types. */ |
202 | 7.56k | if ((retval = nc4_rec_write_groups_types(h5->root_grp))) |
203 | 0 | return retval; |
204 | | |
205 | | /* Check to see if the coordinate order is messed up. If |
206 | | * detected, propagate to all groups to consistently store |
207 | | * dimids. */ |
208 | 7.56k | if ((retval = detect_preserve_dimids(h5->root_grp, &bad_coord_order))) |
209 | 0 | return retval; |
210 | | |
211 | | /* Write all the metadata. */ |
212 | 7.56k | if ((retval = nc4_rec_write_metadata(h5->root_grp, bad_coord_order))) |
213 | 0 | return retval; |
214 | | |
215 | | /* Write out provenance*/ |
216 | 7.56k | if((retval = NC4_write_provenance(h5))) |
217 | 0 | return retval; |
218 | 7.56k | } |
219 | | |
220 | | /* Tell HDF5 to flush all changes to the file. */ |
221 | 7.56k | hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info; |
222 | 7.56k | if (H5Fflush(hdf5_info->hdfid, H5F_SCOPE_GLOBAL) < 0) |
223 | 0 | return NC_EHDFERR; |
224 | | |
225 | 7.56k | return NC_NOERR; |
226 | 7.56k | } |
227 | | |
228 | | /** |
229 | | * @internal This function will free all allocated metadata memory, |
230 | | * and close the HDF5 file. The group that is passed in must be the |
231 | | * root group of the file. If inmemory is used, then save |
232 | | * the final memory in mem.memio. |
233 | | * |
234 | | * @param h5 Pointer to HDF5 file info struct. |
235 | | * @param abort True if this is an abort. |
236 | | * @param memio the place to return a core image if not NULL |
237 | | * |
238 | | * @return ::NC_NOERR No error. |
239 | | * @return ::NC_EHDFERR HDF5 could not close the file. |
240 | | * @return ::NC_EINDEFINE Classic model file is in define mode. |
241 | | * @author Ed Hartnett, Dennis Heimbigner |
242 | | */ |
243 | | int |
244 | | nc4_close_netcdf4_file(NC_FILE_INFO_T *h5, int abort, NC_memio *memio) |
245 | 12.5k | { |
246 | 12.5k | NC_HDF5_FILE_INFO_T *hdf5_info; |
247 | 12.5k | int retval; |
248 | | |
249 | 12.5k | assert(h5 && h5->root_grp && h5->format_file_info); |
250 | 12.5k | LOG((3, "%s: h5->path %s abort %d", __func__, h5->controller->path, abort)); |
251 | | |
252 | | /* Get HDF5 specific info. */ |
253 | 12.5k | hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info; |
254 | | |
255 | | #ifdef USE_PARALLEL4 |
256 | | /* Free the MPI Comm & Info objects, if we opened the file in |
257 | | * parallel. */ |
258 | | if (h5->parallel) |
259 | | { |
260 | | if (h5->comm != MPI_COMM_NULL) |
261 | | MPI_Comm_free(&h5->comm); |
262 | | if (h5->info != MPI_INFO_NULL) |
263 | | MPI_Info_free(&h5->info); |
264 | | } |
265 | | #endif |
266 | | |
267 | | /* Free the fileinfo struct, which holds info from the fileinfo |
268 | | * hidden attribute. */ |
269 | 12.5k | NC4_clear_provenance(&h5->provenance); |
270 | | |
271 | | /* Close hdf file. It may not be open, since this function is also |
272 | | * called by NC_create() when a file opening is aborted. */ |
273 | 12.5k | if (hdf5_info->hdfid > 0 && H5Fclose(hdf5_info->hdfid) < 0) |
274 | 0 | { |
275 | 0 | dumpopenobjects(h5); |
276 | 0 | return NC_EHDFERR; |
277 | 0 | } |
278 | | |
279 | | /* If inmemory is used and user wants the final memory block, |
280 | | then capture and return the final memory block else free it */ |
281 | 12.5k | if (h5->mem.inmemory) |
282 | 0 | { |
283 | | /* Pull out the final memory */ |
284 | 0 | (void)NC4_extract_file_image(h5); |
285 | 0 | if (!abort && memio != NULL) |
286 | 0 | { |
287 | 0 | *memio = h5->mem.memio; /* capture it */ |
288 | 0 | h5->mem.memio.memory = NULL; /* avoid duplicate free */ |
289 | 0 | } |
290 | | /* If needed, reclaim extraneous memory */ |
291 | 0 | if (h5->mem.memio.memory != NULL) |
292 | 0 | { |
293 | | /* If the original block of memory is not resizeable, then |
294 | | it belongs to the caller and we should not free it. */ |
295 | 0 | if(!h5->mem.locked) |
296 | 0 | free(h5->mem.memio.memory); |
297 | 0 | } |
298 | 0 | h5->mem.memio.memory = NULL; |
299 | 0 | h5->mem.memio.size = 0; |
300 | 0 | NC4_image_finalize(h5->mem.udata); |
301 | 0 | } |
302 | | |
303 | | /* Free the HDF5-specific info. */ |
304 | 12.5k | if (h5->format_file_info) |
305 | 12.5k | free(h5->format_file_info); |
306 | | |
307 | | /* Free the NC_FILE_INFO_T struct. */ |
308 | 12.5k | if ((retval = nc4_nc4f_list_del(h5))) |
309 | 0 | return retval; |
310 | | |
311 | 12.5k | return NC_NOERR; |
312 | 12.5k | } |
313 | | |
314 | | /** |
315 | | * @internal This function will recurse through an open HDF5 file and |
316 | | * release resources. All open HDF5 objects in the file will be |
317 | | * closed. |
318 | | * |
319 | | * @param h5 Pointer to HDF5 file info struct. |
320 | | * @param abort True if this is an abort. |
321 | | * @param memio the place to return a core image if not NULL |
322 | | * |
323 | | * @return ::NC_NOERR No error. |
324 | | * @return ::NC_EHDFERR HDF5 could not close the file. |
325 | | * @author Ed Hartnett |
326 | | */ |
327 | | int |
328 | | nc4_close_hdf5_file(NC_FILE_INFO_T *h5, int abort, NC_memio *memio) |
329 | 12.5k | { |
330 | 12.5k | int retval; |
331 | | |
332 | 12.5k | assert(h5 && h5->root_grp && h5->format_file_info); |
333 | 12.5k | LOG((3, "%s: h5->path %s abort %d", __func__, h5->controller->path, abort)); |
334 | | |
335 | | /* According to the docs, always end define mode on close. */ |
336 | 12.5k | if (h5->flags & NC_INDEF) |
337 | 5.01k | h5->flags ^= NC_INDEF; |
338 | | |
339 | | /* Sync the file, unless we're aborting, or this is a read-only |
340 | | * file. */ |
341 | 12.5k | if (!h5->no_write && !abort) |
342 | 6.28k | if ((retval = sync_netcdf4_file(h5))) |
343 | 0 | return retval; |
344 | | |
345 | | /* Close all open HDF5 objects within the file. */ |
346 | 12.5k | if ((retval = nc4_rec_grp_HDF5_del(h5->root_grp))) |
347 | 0 | return retval; |
348 | | |
349 | | /* Release all intarnal lists and metadata associated with this |
350 | | * file. All HDF5 objects have already been released. */ |
351 | 12.5k | if ((retval = nc4_close_netcdf4_file(h5, abort, memio))) |
352 | 0 | return retval; |
353 | | |
354 | 12.5k | return NC_NOERR; |
355 | 12.5k | } |
356 | | |
357 | | /** |
358 | | * @internal Output a list of still-open objects in the HDF5 |
359 | | * file. This is only called if the file fails to close cleanly. |
360 | | * |
361 | | * @param h5 Pointer to file info. |
362 | | * |
363 | | * @author Dennis Heimbigner |
364 | | */ |
365 | | static void |
366 | | dumpopenobjects(NC_FILE_INFO_T* h5) |
367 | 0 | { |
368 | 0 | NC_HDF5_FILE_INFO_T *hdf5_info; |
369 | 0 | int nobjs; |
370 | |
|
371 | 0 | assert(h5 && h5->format_file_info); |
372 | 0 | hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info; |
373 | |
|
374 | 0 | if(hdf5_info->hdfid <= 0) |
375 | 0 | return; /* File was never opened */ |
376 | | |
377 | 0 | nobjs = H5Fget_obj_count(hdf5_info->hdfid, H5F_OBJ_ALL); |
378 | | |
379 | | /* Apparently we can get an error even when nobjs == 0 */ |
380 | 0 | if(nobjs < 0) { |
381 | 0 | return; |
382 | 0 | } else if(nobjs > 0) { |
383 | 0 | char msg[1024]; |
384 | 0 | int logit = 0; |
385 | | /* If the close doesn't work, probably there are still some HDF5 |
386 | | * objects open, which means there's a bug in the library. So |
387 | | * print out some info on to help the poor programmer figure it |
388 | | * out. */ |
389 | 0 | snprintf(msg,sizeof(msg),"There are %d HDF5 objects open!", nobjs); |
390 | | #ifdef LOGGING |
391 | | #ifdef LOGOPEN |
392 | | LOG((0, msg)); |
393 | | logit = 1; |
394 | | #endif |
395 | | #else |
396 | 0 | fprintf(stdout,"%s\n",msg); |
397 | 0 | logit = 0; |
398 | 0 | #endif |
399 | 0 | reportopenobjects(logit,hdf5_info->hdfid); |
400 | 0 | fflush(stderr); |
401 | 0 | } |
402 | | |
403 | 0 | return; |
404 | 0 | } |
405 | | |
406 | | /** |
407 | | * @internal Unfortunately HDF only allows specification of fill value |
408 | | * only when a dataset is created. Whereas in netcdf, you first create |
409 | | * the variable and then (optionally) specify the fill value. To |
410 | | * accomplish this in HDF5 I have to delete the dataset, and recreate |
411 | | * it, with the fill value specified. |
412 | | * |
413 | | * @param ncid File and group ID. |
414 | | * @param fillmode File mode. |
415 | | * @param old_modep Pointer that gets old mode. Ignored if NULL. |
416 | | * |
417 | | * @return ::NC_NOERR No error. |
418 | | * @author Ed Hartnett |
419 | | */ |
420 | | int |
421 | | NC4_set_fill(int ncid, int fillmode, int *old_modep) |
422 | 0 | { |
423 | 0 | NC_FILE_INFO_T *nc4_info; |
424 | 0 | int retval; |
425 | |
|
426 | 0 | LOG((2, "%s: ncid 0x%x fillmode %d", __func__, ncid, fillmode)); |
427 | | |
428 | | /* Get pointer to file info. */ |
429 | 0 | if ((retval = nc4_find_grp_h5(ncid, NULL, &nc4_info))) |
430 | 0 | return retval; |
431 | 0 | assert(nc4_info); |
432 | | |
433 | | /* Trying to set fill on a read-only file? You sicken me! */ |
434 | 0 | if (nc4_info->no_write) |
435 | 0 | return NC_EPERM; |
436 | | |
437 | | /* Did you pass me some weird fillmode? */ |
438 | 0 | if (fillmode != NC_FILL && fillmode != NC_NOFILL) |
439 | 0 | return NC_EINVAL; |
440 | | |
441 | | /* If the user wants to know, tell him what the old mode was. */ |
442 | 0 | if (old_modep) |
443 | 0 | *old_modep = nc4_info->fill_mode; |
444 | |
|
445 | 0 | nc4_info->fill_mode = fillmode; |
446 | |
|
447 | 0 | return NC_NOERR; |
448 | 0 | } |
449 | | |
450 | | /** |
451 | | * @internal Put the file back in redef mode. This is done |
452 | | * automatically for netcdf-4 files, if the user forgets. |
453 | | * |
454 | | * @param ncid File and group ID. |
455 | | * |
456 | | * @return ::NC_NOERR No error. |
457 | | * @author Ed Hartnett |
458 | | */ |
459 | | int |
460 | | NC4_redef(int ncid) |
461 | 0 | { |
462 | 0 | NC_FILE_INFO_T *nc4_info; |
463 | 0 | int retval; |
464 | |
|
465 | 0 | LOG((1, "%s: ncid 0x%x", __func__, ncid)); |
466 | | |
467 | | /* Find this file's metadata. */ |
468 | 0 | if ((retval = nc4_find_grp_h5(ncid, NULL, &nc4_info))) |
469 | 0 | return retval; |
470 | 0 | assert(nc4_info); |
471 | | |
472 | | /* If we're already in define mode, return an error. */ |
473 | 0 | if (nc4_info->flags & NC_INDEF) |
474 | 0 | return NC_EINDEFINE; |
475 | | |
476 | | /* If the file is read-only, return an error. */ |
477 | 0 | if (nc4_info->no_write) |
478 | 0 | return NC_EPERM; |
479 | | |
480 | | /* Set define mode. */ |
481 | 0 | nc4_info->flags |= NC_INDEF; |
482 | | |
483 | | /* For nc_abort, we need to remember if we're in define mode as a |
484 | | redef. */ |
485 | 0 | nc4_info->redef = NC_TRUE; |
486 | |
|
487 | 0 | return NC_NOERR; |
488 | 0 | } |
489 | | |
490 | | /** |
491 | | * @internal For netcdf-4 files, this just calls nc_enddef, ignoring |
492 | | * the extra parameters. |
493 | | * |
494 | | * @param ncid File and group ID. |
495 | | * @param h_minfree Ignored for netCDF-4 files. |
496 | | * @param v_align Ignored for netCDF-4 files. |
497 | | * @param v_minfree Ignored for netCDF-4 files. |
498 | | * @param r_align Ignored for netCDF-4 files. |
499 | | * |
500 | | * @return ::NC_NOERR No error. |
501 | | * @author Ed Hartnett |
502 | | */ |
503 | | int |
504 | | NC4__enddef(int ncid, size_t h_minfree, size_t v_align, |
505 | | size_t v_minfree, size_t r_align) |
506 | 1.27k | { |
507 | 1.27k | return NC4_enddef(ncid); |
508 | 1.27k | } |
509 | | |
510 | | /** |
511 | | * @internal Take the file out of define mode. This is called |
512 | | * automatically for netcdf-4 files, if the user forgets. |
513 | | * |
514 | | * @param ncid File and group ID. |
515 | | * |
516 | | * @return ::NC_NOERR No error. |
517 | | * @return ::NC_EBADID Bad ncid. |
518 | | * @return ::NC_EBADGRPID Bad group ID. |
519 | | * @author Ed Hartnett |
520 | | */ |
521 | | static int |
522 | | NC4_enddef(int ncid) |
523 | 1.27k | { |
524 | 1.27k | NC_FILE_INFO_T *nc4_info; |
525 | 1.27k | NC_GRP_INFO_T *grp; |
526 | 1.27k | NC_VAR_INFO_T *var; |
527 | 1.27k | int i; |
528 | 1.27k | int retval; |
529 | | |
530 | 1.27k | LOG((1, "%s: ncid 0x%x", __func__, ncid)); |
531 | | |
532 | | /* Find pointer to group and nc4_info. */ |
533 | 1.27k | if ((retval = nc4_find_grp_h5(ncid, &grp, &nc4_info))) |
534 | 0 | return retval; |
535 | | |
536 | | /* When exiting define mode, mark all variable written. */ |
537 | 13.8k | for (i = 0; i < ncindexsize(grp->vars); i++) |
538 | 12.5k | { |
539 | 12.5k | var = (NC_VAR_INFO_T *)ncindexith(grp->vars, i); |
540 | 12.5k | assert(var); |
541 | 12.5k | var->written_to = NC_TRUE; |
542 | 12.5k | } |
543 | | |
544 | 1.27k | return nc4_enddef_netcdf4_file(nc4_info); |
545 | 1.27k | } |
546 | | |
547 | | /** |
548 | | * @internal Flushes all buffers associated with the file, after |
549 | | * writing all changed metadata. This may only be called in data mode. |
550 | | * |
551 | | * @param ncid File and group ID. |
552 | | * |
553 | | * @return ::NC_NOERR No error. |
554 | | * @return ::NC_EBADID Bad ncid. |
555 | | * @return ::NC_EINDEFINE Classic model file is in define mode. |
556 | | * @author Ed Hartnett |
557 | | */ |
558 | | int |
559 | | NC4_sync(int ncid) |
560 | 0 | { |
561 | 0 | NC_FILE_INFO_T *nc4_info; |
562 | 0 | int retval; |
563 | |
|
564 | 0 | LOG((2, "%s: ncid 0x%x", __func__, ncid)); |
565 | |
|
566 | 0 | if ((retval = nc4_find_grp_h5(ncid, NULL, &nc4_info))) |
567 | 0 | return retval; |
568 | 0 | assert(nc4_info); |
569 | | |
570 | | /* If we're in define mode, we can't sync. */ |
571 | 0 | if (nc4_info->flags & NC_INDEF) |
572 | 0 | { |
573 | 0 | if (nc4_info->cmode & NC_CLASSIC_MODEL) |
574 | 0 | return NC_EINDEFINE; |
575 | 0 | if ((retval = NC4_enddef(ncid))) |
576 | 0 | return retval; |
577 | 0 | } |
578 | | |
579 | 0 | return sync_netcdf4_file(nc4_info); |
580 | 0 | } |
581 | | |
582 | | /** |
583 | | * @internal From the netcdf-3 docs: The function nc_abort just closes |
584 | | * the netCDF dataset, if not in define mode. If the dataset is being |
585 | | * created and is still in define mode, the dataset is deleted. If |
586 | | * define mode was entered by a call to nc_redef, the netCDF dataset |
587 | | * is restored to its state before definition mode was entered and the |
588 | | * dataset is closed. |
589 | | * |
590 | | * @param ncid File and group ID. |
591 | | * |
592 | | * @return ::NC_NOERR No error. |
593 | | * @author Ed Hartnett |
594 | | */ |
595 | | int |
596 | | NC4_abort(int ncid) |
597 | 0 | { |
598 | 0 | NC *nc; |
599 | 0 | NC_FILE_INFO_T *nc4_info; |
600 | 0 | int delete_file = 0; |
601 | 0 | char path[NC_MAX_NAME + 1]; |
602 | 0 | int retval; |
603 | |
|
604 | 0 | LOG((2, "%s: ncid 0x%x", __func__, ncid)); |
605 | | |
606 | | /* Find metadata for this file. */ |
607 | 0 | if ((retval = nc4_find_nc_grp_h5(ncid, &nc, NULL, &nc4_info))) |
608 | 0 | return retval; |
609 | 0 | assert(nc4_info); |
610 | | |
611 | | /* If we're in define mode, but not redefing the file, delete it. */ |
612 | 0 | if (nc4_info->flags & NC_INDEF && !nc4_info->redef) |
613 | 0 | { |
614 | 0 | delete_file++; |
615 | 0 | strncpy(path, nc->path, NC_MAX_NAME); |
616 | 0 | } |
617 | | |
618 | | /* Free any resources the netcdf-4 library has for this file's |
619 | | * metadata. */ |
620 | 0 | if ((retval = nc4_close_hdf5_file(nc4_info, 1, NULL))) |
621 | 0 | return retval; |
622 | | |
623 | | /* Delete the file, if we should. */ |
624 | 0 | if (delete_file) |
625 | 0 | if (remove(path) < 0) |
626 | 0 | return NC_ECANTREMOVE; |
627 | | |
628 | 0 | return NC_NOERR; |
629 | 0 | } |
630 | | |
631 | | /** |
632 | | * @internal Close the netcdf file, writing any changes first. |
633 | | * |
634 | | * @param ncid File and group ID. |
635 | | * @param params any extra parameters in/out of close |
636 | | * |
637 | | * @return ::NC_NOERR No error. |
638 | | * @author Ed Hartnett |
639 | | */ |
640 | | int |
641 | | NC4_close(int ncid, void* params) |
642 | 12.5k | { |
643 | 12.5k | NC_GRP_INFO_T *grp; |
644 | 12.5k | NC_FILE_INFO_T *h5; |
645 | 12.5k | int retval; |
646 | 12.5k | int inmemory; |
647 | 12.5k | NC_memio* memio = NULL; |
648 | | |
649 | 12.5k | LOG((1, "%s: ncid 0x%x", __func__, ncid)); |
650 | | |
651 | | /* Find our metadata for this file. */ |
652 | 12.5k | if ((retval = nc4_find_grp_h5(ncid, &grp, &h5))) |
653 | 0 | return retval; |
654 | | |
655 | 12.5k | assert(h5 && grp); |
656 | | |
657 | | /* This must be the root group. */ |
658 | 12.5k | if (grp->parent) |
659 | 0 | return NC_EBADGRPID; |
660 | | |
661 | 12.5k | inmemory = ((h5->cmode & NC_INMEMORY) == NC_INMEMORY); |
662 | | |
663 | 12.5k | if(inmemory && params != NULL) { |
664 | 0 | memio = (NC_memio*)params; |
665 | 0 | } |
666 | | |
667 | | /* Call the nc4 close. */ |
668 | 12.5k | if ((retval = nc4_close_hdf5_file(grp->nc4_info, 0, memio))) |
669 | 0 | return retval; |
670 | | |
671 | 12.5k | return NC_NOERR; |
672 | 12.5k | } |
673 | | |
674 | | /** |
675 | | * @internal Learn number of dimensions, variables, global attributes, |
676 | | * and the ID of the first unlimited dimension (if any). |
677 | | * |
678 | | * @note It's possible for any of these pointers to be NULL, in which |
679 | | * case don't try to figure out that value. |
680 | | * |
681 | | * @param ncid File and group ID. |
682 | | * @param ndimsp Pointer that gets number of dimensions. |
683 | | * @param nvarsp Pointer that gets number of variables. |
684 | | * @param nattsp Pointer that gets number of global attributes. |
685 | | * @param unlimdimidp Pointer that gets first unlimited dimension ID, |
686 | | * or -1 if there are no unlimied dimensions. |
687 | | * |
688 | | * @return ::NC_NOERR No error. |
689 | | * @author Ed Hartnett |
690 | | */ |
691 | | int |
692 | | NC4_inq(int ncid, int *ndimsp, int *nvarsp, int *nattsp, int *unlimdimidp) |
693 | 31.2k | { |
694 | 31.2k | NC *nc; |
695 | 31.2k | NC_FILE_INFO_T *h5; |
696 | 31.2k | NC_GRP_INFO_T *grp; |
697 | 31.2k | int retval; |
698 | 31.2k | int i; |
699 | | |
700 | 31.2k | LOG((2, "%s: ncid 0x%x", __func__, ncid)); |
701 | | |
702 | | /* Find file metadata. */ |
703 | 31.2k | if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) |
704 | 0 | return retval; |
705 | | |
706 | 31.2k | assert(h5 && grp && nc); |
707 | | |
708 | | /* Count the number of dims, vars, and global atts; need to iterate |
709 | | * because of possible nulls. */ |
710 | 31.2k | if (ndimsp) |
711 | 8.62k | { |
712 | 8.62k | *ndimsp = ncindexcount(grp->dim); |
713 | 8.62k | } |
714 | 31.2k | if (nvarsp) |
715 | 22.5k | { |
716 | 22.5k | *nvarsp = ncindexcount(grp->vars); |
717 | 22.5k | } |
718 | 31.2k | if (nattsp) |
719 | 12.5k | { |
720 | | /* Do we need to read the atts? */ |
721 | 12.5k | if (!grp->atts_read) |
722 | 6.28k | if ((retval = nc4_read_atts(grp, NULL))) |
723 | 0 | return retval; |
724 | | |
725 | 12.5k | *nattsp = ncindexcount(grp->att); |
726 | 12.5k | } |
727 | | |
728 | 31.2k | if (unlimdimidp) |
729 | 6.28k | { |
730 | | /* Default, no unlimited dimension */ |
731 | 6.28k | *unlimdimidp = -1; |
732 | | |
733 | | /* If there's more than one unlimited dim, which was not possible |
734 | | with netcdf-3, then only the last unlimited one will be reported |
735 | | back in xtendimp. */ |
736 | | /* Note that this code is inconsistent with nc_inq_unlimid() */ |
737 | 10.3k | for(i=0;i<ncindexsize(grp->dim);i++) { |
738 | 8.26k | NC_DIM_INFO_T* d = (NC_DIM_INFO_T*)ncindexith(grp->dim,i); |
739 | 8.26k | if(d == NULL) continue; |
740 | 8.26k | if(d->unlimited) { |
741 | 4.16k | *unlimdimidp = d->hdr.id; |
742 | 4.16k | break; |
743 | 4.16k | } |
744 | 8.26k | } |
745 | 6.28k | } |
746 | | |
747 | 31.2k | return NC_NOERR; |
748 | 31.2k | } |
749 | | |
750 | | /** |
751 | | * @internal This function will do the enddef stuff for a netcdf-4 file. |
752 | | * |
753 | | * @param h5 Pointer to HDF5 file info struct. |
754 | | * |
755 | | * @return ::NC_NOERR No error. |
756 | | * @return ::NC_ENOTINDEFINE Not in define mode. |
757 | | * @author Ed Hartnett |
758 | | */ |
759 | | int |
760 | | nc4_enddef_netcdf4_file(NC_FILE_INFO_T *h5) |
761 | 1.27k | { |
762 | 1.27k | assert(h5); |
763 | 1.27k | LOG((3, "%s", __func__)); |
764 | | |
765 | | /* If we're not in define mode, return an error. */ |
766 | 1.27k | if (!(h5->flags & NC_INDEF)) |
767 | 0 | return NC_ENOTINDEFINE; |
768 | | |
769 | | /* Turn define mode off. */ |
770 | 1.27k | h5->flags ^= NC_INDEF; |
771 | | |
772 | | /* Redef mode needs to be tracked separately for nc_abort. */ |
773 | 1.27k | h5->redef = NC_FALSE; |
774 | | |
775 | 1.27k | return sync_netcdf4_file(h5); |
776 | 1.27k | } |