NetCDF  4.7.0
hdf5file.c
Go to the documentation of this file.
1 /* Copyright 2003-2018, University Corporation for Atmospheric
2  * Research. See COPYRIGHT file for copying and redistribution
3  * conditions. */
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 
24 #define LOGOPEN 1
25 
29 #define NRESERVED 11 /*|NC_reservedatt|*/
30 
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 
58 const NC_reservedatt*
59 NC_findreserved(const char* name)
60 {
61  int n = NRESERVED;
62  int L = 0;
63  int R = (n - 1);
64  for(;;) {
65  if(L > R) break;
66  int m = (L + R) / 2;
67  const NC_reservedatt* p = &NC_reserved[m];
68  int cmp = strcmp(p->name,name);
69  if(cmp == 0) return p;
70  if(cmp < 0)
71  L = (m + 1);
72  else /*cmp > 0*/
73  R = (m - 1);
74  }
75  return NULL;
76 }
77 
94 static int
95 detect_preserve_dimids(NC_GRP_INFO_T *grp, nc_bool_t *bad_coord_orderp)
96 {
97  NC_VAR_INFO_T *var;
98  NC_GRP_INFO_T *child_grp;
99  int last_dimid = -1;
100  int retval;
101  int i;
102 
103  /* Iterate over variables in this group */
104  for (i=0; i < ncindexsize(grp->vars); i++)
105  {
106  var = (NC_VAR_INFO_T*)ncindexith(grp->vars,i);
107  if (var == NULL) continue;
108  /* Only matters for dimension scale variables, with non-scalar dimensionality */
109  if (var->dimscale && var->ndims)
110  {
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  if (var->dimids[0] < last_dimid)
116  {
117  LOG((5, "%s: %s is out of order coord var", __func__, var->hdr.name));
118  *bad_coord_orderp = NC_TRUE;
119  return NC_NOERR;
120  }
121  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  if (var->ndims > 1)
127  {
128  LOG((5, "%s: %s is multidimensional coord var", __func__, var->hdr.name));
129  *bad_coord_orderp = NC_TRUE;
130  return NC_NOERR;
131  }
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  if (var->is_new_var || var->became_coord_var)
137  {
138  LOG((5, "%s: coord var defined after enddef/redef", __func__));
139  *bad_coord_orderp = NC_TRUE;
140  return NC_NOERR;
141  }
142  }
143  }
144 
145  /* If there are any child groups, check them also for this condition. */
146  for (i = 0; i < ncindexsize(grp->children); i++)
147  {
148  if (!(child_grp = (NC_GRP_INFO_T *)ncindexith(grp->children, i)))
149  continue;
150  if ((retval = detect_preserve_dimids(child_grp, bad_coord_orderp)))
151  return retval;
152  }
153  return NC_NOERR;
154 }
155 
167 static int
168 sync_netcdf4_file(NC_FILE_INFO_T *h5)
169 {
170  NC_HDF5_FILE_INFO_T *hdf5_info;
171  int retval;
172 
173  assert(h5 && h5->format_file_info);
174  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  if (h5->flags & NC_INDEF)
179  {
180  if (h5->cmode & NC_CLASSIC_MODEL)
181  return NC_EINDEFINE;
182 
183  /* Turn define mode off. */
184  h5->flags ^= NC_INDEF;
185 
186  /* Redef mode needs to be tracked separately for nc_abort. */
187  h5->redef = NC_FALSE;
188  }
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  if (!h5->no_write)
198  {
199  nc_bool_t bad_coord_order = NC_FALSE;
200 
201  /* Write any user-defined types. */
202  if ((retval = nc4_rec_write_groups_types(h5->root_grp)))
203  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  if ((retval = detect_preserve_dimids(h5->root_grp, &bad_coord_order)))
209  return retval;
210 
211  /* Write all the metadata. */
212  if ((retval = nc4_rec_write_metadata(h5->root_grp, bad_coord_order)))
213  return retval;
214 
215  /* Write out provenance*/
216  if((retval = NC4_write_provenance(h5)))
217  return retval;
218  }
219 
220  /* Tell HDF5 to flush all changes to the file. */
221  hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info;
222  if (H5Fflush(hdf5_info->hdfid, H5F_SCOPE_GLOBAL) < 0)
223  return NC_EHDFERR;
224 
225  return NC_NOERR;
226 }
227 
243 int
244 nc4_close_netcdf4_file(NC_FILE_INFO_T *h5, int abort, NC_memio *memio)
245 {
246  NC_HDF5_FILE_INFO_T *hdf5_info;
247  int retval;
248 
249  assert(h5 && h5->root_grp && h5->format_file_info);
250  LOG((3, "%s: h5->path %s abort %d", __func__, h5->controller->path, abort));
251 
252  /* Get HDF5 specific info. */
253  hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info;
254 
255  /* Delete all the list contents for vars, dims, and atts, in each
256  * group. */
257  if ((retval = nc4_rec_grp_del(h5->root_grp)))
258  return retval;
259 
260  /* Free lists of dims, groups, and types in the root group. */
261  nclistfree(h5->alldims);
262  nclistfree(h5->allgroups);
263  nclistfree(h5->alltypes);
264 
265 #ifdef USE_PARALLEL4
266  /* Free the MPI Comm & Info objects, if we opened the file in
267  * parallel. */
268  if (h5->parallel)
269  {
270  if (h5->comm != MPI_COMM_NULL)
271  MPI_Comm_free(&h5->comm);
272  if (h5->info != MPI_INFO_NULL)
273  MPI_Info_free(&h5->info);
274  }
275 #endif
276 
277  /* Free the fileinfo struct, which holds info from the fileinfo
278  * hidden attribute. */
279  NC4_clear_provenance(&h5->provenance);
280 
281  /* Close hdf file. It may not be open, since this function is also
282  * called by NC_create() when a file opening is aborted. */
283  if (hdf5_info->hdfid > 0 && H5Fclose(hdf5_info->hdfid) < 0)
284  {
285  dumpopenobjects(h5);
286  return NC_EHDFERR;
287  }
288 
289  /* If inmemory is used and user wants the final memory block,
290  then capture and return the final memory block else free it */
291  if(h5->mem.inmemory) {
292  /* Pull out the final memory */
293  (void)NC4_extract_file_image(h5);
294  if(!abort && memio != NULL) {
295  *memio = h5->mem.memio; /* capture it */
296  h5->mem.memio.memory = NULL; /* avoid duplicate free */
297  }
298  /* If needed, reclaim extraneous memory */
299  if(h5->mem.memio.memory != NULL) {
300  /* If the original block of memory is not resizeable, then
301  it belongs to the caller and we should not free it. */
302  if(!h5->mem.locked)
303  free(h5->mem.memio.memory);
304  }
305  h5->mem.memio.memory = NULL;
306  h5->mem.memio.size = 0;
307  NC4_image_finalize(h5->mem.udata);
308  }
309 
310  /* Free the HDF5-specific info. */
311  if (h5->format_file_info)
312  free(h5->format_file_info);
313 
314  /* Free the nc4_info struct; above code should have reclaimed
315  everything else */
316  free(h5);
317 
318  return NC_NOERR;
319 }
320 
334 int
335 nc4_close_hdf5_file(NC_FILE_INFO_T *h5, int abort, NC_memio *memio)
336 {
337  int retval;
338 
339  assert(h5 && h5->root_grp && h5->format_file_info);
340  LOG((3, "%s: h5->path %s abort %d", __func__, h5->controller->path, abort));
341 
342  /* According to the docs, always end define mode on close. */
343  if (h5->flags & NC_INDEF)
344  h5->flags ^= NC_INDEF;
345 
346  /* Sync the file, unless we're aborting, or this is a read-only
347  * file. */
348  if (!h5->no_write && !abort)
349  if ((retval = sync_netcdf4_file(h5)))
350  return retval;
351 
352  /* Close all open HDF5 objects within the file. */
353  if ((retval = nc4_rec_grp_HDF5_del(h5->root_grp)))
354  return retval;
355 
356  /* Release all intarnal lists and metadata associated with this
357  * file. All HDF5 objects have already been released. */
358  if ((retval = nc4_close_netcdf4_file(h5, abort, memio)))
359  return retval;
360 
361  return NC_NOERR;
362 }
363 
372 static void
373 dumpopenobjects(NC_FILE_INFO_T* h5)
374 {
375  NC_HDF5_FILE_INFO_T *hdf5_info;
376  int nobjs;
377 
378  assert(h5 && h5->format_file_info);
379  hdf5_info = (NC_HDF5_FILE_INFO_T *)h5->format_file_info;
380 
381  if(hdf5_info->hdfid <= 0)
382  return; /* File was never opened */
383 
384  nobjs = H5Fget_obj_count(hdf5_info->hdfid, H5F_OBJ_ALL);
385 
386  /* Apparently we can get an error even when nobjs == 0 */
387  if(nobjs < 0) {
388  return;
389  } else if(nobjs > 0) {
390  char msg[1024];
391  int logit = 0;
392  /* If the close doesn't work, probably there are still some HDF5
393  * objects open, which means there's a bug in the library. So
394  * print out some info on to help the poor programmer figure it
395  * out. */
396  snprintf(msg,sizeof(msg),"There are %d HDF5 objects open!", nobjs);
397 #ifdef LOGGING
398 #ifdef LOGOPEN
399  LOG((0, msg));
400  logit = 1;
401 #endif
402 #else
403  fprintf(stdout,"%s\n",msg);
404  logit = 0;
405 #endif
406  reportopenobjects(logit,hdf5_info->hdfid);
407  fflush(stderr);
408  }
409 
410  return;
411 }
412 
427 int
428 NC4_set_fill(int ncid, int fillmode, int *old_modep)
429 {
430  NC_FILE_INFO_T *nc4_info;
431  int retval;
432 
433  LOG((2, "%s: ncid 0x%x fillmode %d", __func__, ncid, fillmode));
434 
435  /* Get pointer to file info. */
436  if ((retval = nc4_find_grp_h5(ncid, NULL, &nc4_info)))
437  return retval;
438  assert(nc4_info);
439 
440  /* Trying to set fill on a read-only file? You sicken me! */
441  if (nc4_info->no_write)
442  return NC_EPERM;
443 
444  /* Did you pass me some weird fillmode? */
445  if (fillmode != NC_FILL && fillmode != NC_NOFILL)
446  return NC_EINVAL;
447 
448  /* If the user wants to know, tell him what the old mode was. */
449  if (old_modep)
450  *old_modep = nc4_info->fill_mode;
451 
452  nc4_info->fill_mode = fillmode;
453 
454  return NC_NOERR;
455 }
456 
466 int
467 NC4_redef(int ncid)
468 {
469  NC_FILE_INFO_T *nc4_info;
470  int retval;
471 
472  LOG((1, "%s: ncid 0x%x", __func__, ncid));
473 
474  /* Find this file's metadata. */
475  if ((retval = nc4_find_grp_h5(ncid, NULL, &nc4_info)))
476  return retval;
477  assert(nc4_info);
478 
479  /* If we're already in define mode, return an error. */
480  if (nc4_info->flags & NC_INDEF)
481  return NC_EINDEFINE;
482 
483  /* If the file is read-only, return an error. */
484  if (nc4_info->no_write)
485  return NC_EPERM;
486 
487  /* Set define mode. */
488  nc4_info->flags |= NC_INDEF;
489 
490  /* For nc_abort, we need to remember if we're in define mode as a
491  redef. */
492  nc4_info->redef = NC_TRUE;
493 
494  return NC_NOERR;
495 }
496 
510 int
511 NC4__enddef(int ncid, size_t h_minfree, size_t v_align,
512  size_t v_minfree, size_t r_align)
513 {
514  return NC4_enddef(ncid);
515 }
516 
528 static int
529 NC4_enddef(int ncid)
530 {
531  NC_FILE_INFO_T *nc4_info;
532  NC_GRP_INFO_T *grp;
533  NC_VAR_INFO_T *var;
534  int i;
535  int retval;
536 
537  LOG((1, "%s: ncid 0x%x", __func__, ncid));
538 
539  /* Find pointer to group and nc4_info. */
540  if ((retval = nc4_find_grp_h5(ncid, &grp, &nc4_info)))
541  return retval;
542 
543  /* When exiting define mode, mark all variable written. */
544  for (i = 0; i < ncindexsize(grp->vars); i++)
545  {
546  var = (NC_VAR_INFO_T *)ncindexith(grp->vars, i);
547  assert(var);
548  var->written_to = NC_TRUE;
549  }
550 
551  return nc4_enddef_netcdf4_file(nc4_info);
552 }
553 
565 int
566 NC4_sync(int ncid)
567 {
568  NC_FILE_INFO_T *nc4_info;
569  int retval;
570 
571  LOG((2, "%s: ncid 0x%x", __func__, ncid));
572 
573  if ((retval = nc4_find_grp_h5(ncid, NULL, &nc4_info)))
574  return retval;
575  assert(nc4_info);
576 
577  /* If we're in define mode, we can't sync. */
578  if (nc4_info->flags & NC_INDEF)
579  {
580  if (nc4_info->cmode & NC_CLASSIC_MODEL)
581  return NC_EINDEFINE;
582  if ((retval = NC4_enddef(ncid)))
583  return retval;
584  }
585 
586  return sync_netcdf4_file(nc4_info);
587 }
588 
602 int
603 NC4_abort(int ncid)
604 {
605  NC *nc;
606  NC_FILE_INFO_T *nc4_info;
607  int delete_file = 0;
608  char path[NC_MAX_NAME + 1];
609  int retval;
610 
611  LOG((2, "%s: ncid 0x%x", __func__, ncid));
612 
613  /* Find metadata for this file. */
614  if ((retval = nc4_find_nc_grp_h5(ncid, &nc, NULL, &nc4_info)))
615  return retval;
616  assert(nc4_info);
617 
618  /* If we're in define mode, but not redefing the file, delete it. */
619  if (nc4_info->flags & NC_INDEF && !nc4_info->redef)
620  {
621  delete_file++;
622  strncpy(path, nc->path, NC_MAX_NAME);
623  }
624 
625  /* Free any resources the netcdf-4 library has for this file's
626  * metadata. */
627  if ((retval = nc4_close_hdf5_file(nc4_info, 1, NULL)))
628  return retval;
629 
630  /* Delete the file, if we should. */
631  if (delete_file)
632  if (remove(path) < 0)
633  return NC_ECANTREMOVE;
634 
635  return NC_NOERR;
636 }
637 
647 int
648 NC4_close(int ncid, void* params)
649 {
650  NC_GRP_INFO_T *grp;
651  NC_FILE_INFO_T *h5;
652  int retval;
653  int inmemory;
654  NC_memio* memio = NULL;
655 
656  LOG((1, "%s: ncid 0x%x", __func__, ncid));
657 
658  /* Find our metadata for this file. */
659  if ((retval = nc4_find_grp_h5(ncid, &grp, &h5)))
660  return retval;
661 
662  assert(h5 && grp);
663 
664  /* This must be the root group. */
665  if (grp->parent)
666  return NC_EBADGRPID;
667 
668  inmemory = ((h5->cmode & NC_INMEMORY) == NC_INMEMORY);
669 
670  if(inmemory && params != NULL) {
671  memio = (NC_memio*)params;
672  }
673 
674  /* Call the nc4 close. */
675  if ((retval = nc4_close_hdf5_file(grp->nc4_info, 0, memio)))
676  return retval;
677 
678  return NC_NOERR;
679 }
680 
698 int
699 NC4_inq(int ncid, int *ndimsp, int *nvarsp, int *nattsp, int *unlimdimidp)
700 {
701  NC *nc;
702  NC_FILE_INFO_T *h5;
703  NC_GRP_INFO_T *grp;
704  int retval;
705  int i;
706 
707  LOG((2, "%s: ncid 0x%x", __func__, ncid));
708 
709  /* Find file metadata. */
710  if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
711  return retval;
712 
713  assert(h5 && grp && nc);
714 
715  /* Count the number of dims, vars, and global atts; need to iterate
716  * because of possible nulls. */
717  if (ndimsp)
718  {
719  *ndimsp = ncindexcount(grp->dim);
720  }
721  if (nvarsp)
722  {
723  *nvarsp = ncindexcount(grp->vars);
724  }
725  if (nattsp)
726  {
727  /* Do we need to read the atts? */
728  if (!grp->atts_read)
729  if ((retval = nc4_read_atts(grp, NULL)))
730  return retval;
731 
732  *nattsp = ncindexcount(grp->att);
733  }
734 
735  if (unlimdimidp)
736  {
737  /* Default, no unlimited dimension */
738  *unlimdimidp = -1;
739 
740  /* If there's more than one unlimited dim, which was not possible
741  with netcdf-3, then only the last unlimited one will be reported
742  back in xtendimp. */
743  /* Note that this code is inconsistent with nc_inq_unlimid() */
744  for(i=0;i<ncindexsize(grp->dim);i++) {
745  NC_DIM_INFO_T* d = (NC_DIM_INFO_T*)ncindexith(grp->dim,i);
746  if(d == NULL) continue;
747  if(d->unlimited) {
748  *unlimdimidp = d->hdr.id;
749  break;
750  }
751  }
752  }
753 
754  return NC_NOERR;
755 }
756 
766 int
767 nc4_enddef_netcdf4_file(NC_FILE_INFO_T *h5)
768 {
769  assert(h5);
770  LOG((3, "%s", __func__));
771 
772  /* If we're not in define mode, return an error. */
773  if (!(h5->flags & NC_INDEF))
774  return NC_ENOTINDEFINE;
775 
776  /* Turn define mode off. */
777  h5->flags ^= NC_INDEF;
778 
779  /* Redef mode needs to be tracked separately for nc_abort. */
780  h5->redef = NC_FALSE;
781 
782  return sync_netcdf4_file(h5);
783 }
#define NC_CLASSIC_MODEL
Enforce classic model on netCDF-4.
Definition: netcdf.h:138
#define NC_INMEMORY
Read from memory.
Definition: netcdf.h:161
#define NC_EHDFERR
Error at HDF5 layer.
Definition: netcdf.h:438
#define NC_ENOTINDEFINE
Operation not allowed in data mode.
Definition: netcdf.h:341
#define NC_EINDEFINE
Operation not allowed in define mode.
Definition: netcdf.h:350
#define NC_EINVAL
Invalid Argument.
Definition: netcdf.h:335
#define NC_EBADGRPID
Bad group ID.
Definition: netcdf.h:453
#define NC_NOFILL
Argument to nc_set_fill() to turn off filling of data.
Definition: netcdf.h:115
#define NC_MAX_NAME
Maximum for classic library.
Definition: netcdf.h:275
#define NC_ECANTREMOVE
Can&#39;t remove file.
Definition: netcdf.h:430
#define NC_EPERM
Write to read only.
Definition: netcdf.h:336
#define NC_NOERR
No Error.
Definition: netcdf.h:325
#define NC_FILL
Argument to nc_set_fill() to clear NC_NOFILL.
Definition: netcdf.h:114

Return to the Main Unidata NetCDF page.
Generated on Tue Apr 30 2019 05:08:56 for NetCDF. NetCDF is a Unidata library.