NetCDF  4.4.0-rc2
nc4file.c
Go to the documentation of this file.
1 
12 #include "config.h"
13 #include <errno.h> /* netcdf functions sometimes return system errors */
14 
15 
16 #include "nc.h"
17 #include "nc4internal.h"
18 #include "nc4dispatch.h"
19 
20 /* must be after nc4internal.h */
21 #include <H5DSpublic.h>
22 
23 #ifdef USE_HDF4
24 #include <mfhdf.h>
25 #endif
26 
27 #ifdef USE_DISKLESS
28 #include <hdf5_hl.h>
29 #endif
30 
31 /* This is to track opened HDF5 objects to make sure they are
32  * closed. */
33 #ifdef EXTRA_TESTS
34 extern int num_plists;
35 extern int num_spaces;
36 #endif /* EXTRA_TESTS */
37 
38 #define MIN_DEFLATE_LEVEL 0
39 #define MAX_DEFLATE_LEVEL 9
40 
41 /* These are the special attributes added by the HDF5 dimension scale
42  * API. They will be ignored by netCDF-4. */
43 #define REFERENCE_LIST "REFERENCE_LIST"
44 #define CLASS "CLASS"
45 #define DIMENSION_LIST "DIMENSION_LIST"
46 #define NAME "NAME"
47 
48 /* Define the illegal mode flags */
49 #define ILLEGAL_OPEN_FLAGS (NC_MMAP|NC_64BIT_OFFSET)
50 
51 #define ILLEGAL_CREATE_FLAGS (NC_NOWRITE|NC_MMAP|NC_INMEMORY|NC_64BIT_OFFSET|NC_PNETCDF)
52 
59 {
60  hid_t oid; /* HDF5 object ID */
61  char oname[NC_MAX_NAME + 1]; /* Name of object */
62  H5G_stat_t statbuf; /* Information about the object */
63  struct NC4_rec_read_metadata_obj_info *next; /* Pointer to next node in list */
65 
73 {
74  NC4_rec_read_metadata_obj_info_t *grps_head, *grps_tail; /* Pointers to head & tail of list of groups */
75  NC_GRP_INFO_T *grp; /* Pointer to parent group */
77 
78 /* Forward */
79 static int NC4_enddef(int ncid);
80 static int nc4_rec_read_metadata(NC_GRP_INFO_T *grp);
81 static int close_netcdf4_file(NC_HDF5_FILE_INFO_T *h5, int abort);
82 
83 /* These are the default chunk cache sizes for HDF5 files created or
84  * opened with netCDF-4. */
85 size_t nc4_chunk_cache_size = CHUNK_CACHE_SIZE;
86 size_t nc4_chunk_cache_nelems = CHUNK_CACHE_NELEMS;
87 float nc4_chunk_cache_preemption = CHUNK_CACHE_PREEMPTION;
88 
89 /* To turn off HDF5 error messages, I have to catch an early
90  invocation of a netcdf function. */
91 static int virgin = 1;
92 
93 /* For performance, fill this array only the first time, and keep it
94  * in global memory for each further use. */
95 #define NUM_TYPES 12
96 static hid_t h5_native_type_constant_g[NUM_TYPES];
97 static const char nc_type_name_g[NUM_TYPES][NC_MAX_NAME + 1] = {"char", "byte", "short",
98  "int", "float", "double", "ubyte",
99  "ushort", "uint", "int64",
100  "uint64", "string"};
101 static const nc_type nc_type_constant_g[NUM_TYPES] = {NC_CHAR, NC_BYTE, NC_SHORT,
105 static const int nc_type_size_g[NUM_TYPES] = {sizeof(char), sizeof(char), sizeof(short),
106  sizeof(int), sizeof(float), sizeof(double), sizeof(unsigned char),
107  sizeof(unsigned short), sizeof(unsigned int), sizeof(long long),
108  sizeof(unsigned long long), sizeof(char *)};
109 
110 /* Set chunk cache size. Only affects files opened/created *after* it
111  * is called. */
112 int
113 nc_set_chunk_cache(size_t size, size_t nelems, float preemption)
114 {
115  if (preemption < 0 || preemption > 1)
116  return NC_EINVAL;
117  nc4_chunk_cache_size = size;
118  nc4_chunk_cache_nelems = nelems;
119  nc4_chunk_cache_preemption = preemption;
120  return NC_NOERR;
121 }
122 
123 /* Get chunk cache size. Only affects files opened/created *after* it
124  * is called. */
125 int
126 nc_get_chunk_cache(size_t *sizep, size_t *nelemsp, float *preemptionp)
127 {
128  if (sizep)
129  *sizep = nc4_chunk_cache_size;
130 
131  if (nelemsp)
132  *nelemsp = nc4_chunk_cache_nelems;
133 
134  if (preemptionp)
135  *preemptionp = nc4_chunk_cache_preemption;
136  return NC_NOERR;
137 }
138 
139 /* Required for fortran to avoid size_t issues. */
140 int
141 nc_set_chunk_cache_ints(int size, int nelems, int preemption)
142 {
143  if (size <= 0 || nelems <= 0 || preemption < 0 || preemption > 100)
144  return NC_EINVAL;
145  nc4_chunk_cache_size = size;
146  nc4_chunk_cache_nelems = nelems;
147  nc4_chunk_cache_preemption = (float)preemption / 100;
148  return NC_NOERR;
149 }
150 
151 int
152 nc_get_chunk_cache_ints(int *sizep, int *nelemsp, int *preemptionp)
153 {
154  if (sizep)
155  *sizep = (int)nc4_chunk_cache_size;
156  if (nelemsp)
157  *nelemsp = (int)nc4_chunk_cache_nelems;
158  if (preemptionp)
159  *preemptionp = (int)(nc4_chunk_cache_preemption * 100);
160 
161  return NC_NOERR;
162 }
163 
164 /* This will return the length of a netcdf data type in bytes. */
165 int
166 nc4typelen(nc_type type)
167 {
168  switch(type){
169  case NC_BYTE:
170  case NC_CHAR:
171  case NC_UBYTE:
172  return 1;
173  case NC_USHORT:
174  case NC_SHORT:
175  return 2;
176  case NC_FLOAT:
177  case NC_INT:
178  case NC_UINT:
179  return 4;
180  case NC_DOUBLE:
181  case NC_INT64:
182  case NC_UINT64:
183  return 8;
184  }
185  return -1;
186 }
187 
188 /* Given a filename, check to see if it is a HDF5 file. */
189 #define MAGIC_NUMBER_LEN 4
190 #define NC_HDF5_FILE 1
191 #define NC_HDF4_FILE 2
192 static int
193 nc_check_for_hdf(const char *path, int flags, void* parameters, int *hdf_file)
194 {
195  char blob[MAGIC_NUMBER_LEN];
196 #ifdef USE_PARALLEL
197  int use_parallel = ((flags & NC_MPIIO) == NC_MPIIO);
198  NC_MPI_INFO* mpiinfo = (NC_MPI_INFO*)parameters;
199  MPI_Comm comm = MPI_COMM_WORLD;
200  MPI_Info info = MPI_INFO_NULL;
201 #endif
202  int inmemory = ((flags & NC_INMEMORY) == NC_INMEMORY);
203 #ifdef USE_DISKLESS
204  NC_MEM_INFO* meminfo = (NC_MEM_INFO*)parameters;
205 #endif
206 
207 #ifdef USE_PARALLEL
208  if(use_parallel) {
209  comm = mpiinfo->comm;
210  info = mpiinfo->info;
211  }
212 #endif
213 
214  assert(hdf_file);
215  LOG((3, "%s: path %s", __func__, path));
216 
217  /* HDF5 function handles possible user block at beginning of file */
218  if(!inmemory && H5Fis_hdf5(path))
219  {
220  *hdf_file = NC_HDF5_FILE;
221  } else {
222 
223 /* Get the 4-byte blob from the beginning of the file. Don't use posix
224  * for parallel, use the MPI functions instead. */
225 #ifdef USE_PARALLEL
226  if (!inmemory && use_parallel)
227  {
228  MPI_File fh;
229  MPI_Status status;
230  int retval;
231  if ((retval = MPI_File_open(comm, (char *)path, MPI_MODE_RDONLY,
232  info, &fh)) != MPI_SUCCESS)
233  return NC_EPARINIT;
234  if ((retval = MPI_File_read(fh, blob, MAGIC_NUMBER_LEN, MPI_CHAR,
235  &status)) != MPI_SUCCESS)
236  return NC_EPARINIT;
237  if ((retval = MPI_File_close(&fh)) != MPI_SUCCESS)
238  return NC_EPARINIT;
239  }
240  else
241 #endif /* USE_PARALLEL */
242  if(!inmemory) {
243  FILE *fp;
244  if (!(fp = fopen(path, "r")) ||
245  fread(blob, MAGIC_NUMBER_LEN, 1, fp) != 1) {
246 
247  if(fp) fclose(fp);
248  return errno;
249  }
250  fclose(fp);
251  } else { /*inmemory*/
252  if(meminfo->size < MAGIC_NUMBER_LEN)
253  return NC_ENOTNC;
254  memcpy(blob,meminfo->memory,MAGIC_NUMBER_LEN);
255  }
256 
257  /* Check for HDF4. */
258  if (memcmp(blob, "\016\003\023\001", 4)==0)
259  *hdf_file = NC_HDF4_FILE;
260  else if (memcmp(blob, "HDF", 3)==0)
261  *hdf_file = NC_HDF5_FILE;
262  else
263  *hdf_file = 0;
264  }
265  return NC_NOERR;
266 }
267 
268 /* Create a HDF5/netcdf-4 file. */
269 
270 static int
271 nc4_create_file(const char *path, int cmode, MPI_Comm comm, MPI_Info info,
272  NC *nc)
273 {
274  hid_t fcpl_id, fapl_id = -1;
275  unsigned flags;
276  FILE *fp;
277  int retval = NC_NOERR;
278  NC_HDF5_FILE_INFO_T* nc4_info = NULL;
279 #ifdef USE_PARALLEL
280  int comm_duped = 0; /* Whether the MPI Communicator was duplicated */
281  int info_duped = 0; /* Whether the MPI Info object was duplicated */
282 #else /* !USE_PARALLEL */
283  int persist = 0; /* Should diskless try to persist its data into file?*/
284 #endif
285 
286  assert(nc);
287 
288  if(cmode & NC_DISKLESS)
289  flags = H5F_ACC_TRUNC;
290  else if(cmode & NC_NOCLOBBER)
291  flags = H5F_ACC_EXCL;
292  else
293  flags = H5F_ACC_TRUNC;
294 
295  LOG((3, "%s: path %s mode 0x%x", __func__, path, cmode));
296  assert(nc && path);
297 
298  /* If this file already exists, and NC_NOCLOBBER is specified,
299  return an error. */
300  if (cmode & NC_DISKLESS) {
301 #ifndef USE_PARALLEL
302  if(cmode & NC_WRITE)
303  persist = 1;
304 #endif
305  } else if ((cmode & NC_NOCLOBBER) && (fp = fopen(path, "r"))) {
306  fclose(fp);
307  return NC_EEXIST;
308  }
309 
310  /* Add necessary structs to hold netcdf-4 file data. */
311  if ((retval = nc4_nc4f_list_add(nc, path, (NC_WRITE | cmode))))
312  BAIL(retval);
313  nc4_info = NC4_DATA(nc);
314  assert(nc4_info && nc4_info->root_grp);
315 
316 #if 0 /*def USE_PNETCDF*/
317  if (cmode & NC_PNETCDF)
318  return NC_NOERR;
319 #endif
320 
321  /* Need this access plist to control how HDF5 handles open onjects
322  * on file close. (Setting H5F_CLOSE_SEMI will cause H5Fclose to
323  * fail if there are any open objects in the file. */
324  if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0)
325  BAIL(NC_EHDFERR);
326 #ifdef EXTRA_TESTS
327  num_plists++;
328 #endif
329 #ifdef EXTRA_TESTS
330  if (H5Pset_fclose_degree(fapl_id, H5F_CLOSE_SEMI))
331  BAIL(NC_EHDFERR);
332 #else
333  if (H5Pset_fclose_degree(fapl_id, H5F_CLOSE_STRONG))
334  BAIL(NC_EHDFERR);
335 #endif /* EXTRA_TESTS */
336 
337 #ifdef USE_PARALLEL
338  /* If this is a parallel file create, set up the file creation
339  property list. */
340  if ((cmode & NC_MPIIO) || (cmode & NC_MPIPOSIX))
341  {
342  nc4_info->parallel = NC_TRUE;
343  if (cmode & NC_MPIIO) /* MPI/IO */
344  {
345  LOG((4, "creating parallel file with MPI/IO"));
346  if (H5Pset_fapl_mpio(fapl_id, comm, info) < 0)
347  BAIL(NC_EPARINIT);
348  }
349 #ifdef USE_PARALLEL_POSIX
350  else /* MPI/POSIX */
351  {
352  LOG((4, "creating parallel file with MPI/posix"));
353  if (H5Pset_fapl_mpiposix(fapl_id, comm, 0) < 0)
354  BAIL(NC_EPARINIT);
355  }
356 #else /* USE_PARALLEL_POSIX */
357  /* Should not happen! Code in NC4_create/NC4_open should alias the
358  * NC_MPIPOSIX flag to NC_MPIIO, if the MPI-POSIX VFD is not
359  * available in HDF5. -QAK
360  */
361  else /* MPI/POSIX */
362  BAIL(NC_EPARINIT);
363 #endif /* USE_PARALLEL_POSIX */
364 
365  /* Keep copies of the MPI Comm & Info objects */
366  if (MPI_SUCCESS != MPI_Comm_dup(comm, &nc4_info->comm))
367  BAIL(NC_EMPI);
368  comm_duped++;
369  if (MPI_INFO_NULL != info)
370  {
371  if (MPI_SUCCESS != MPI_Info_dup(info, &nc4_info->info))
372  BAIL(NC_EMPI);
373  info_duped++;
374  }
375  else
376  {
377  /* No dup, just copy it. */
378  nc4_info->info = info;
379  }
380  }
381 #else /* only set cache for non-parallel... */
382  if(cmode & NC_DISKLESS) {
383  if (H5Pset_fapl_core(fapl_id, 4096, persist))
384  BAIL(NC_EDISKLESS);
385  }
386  if (H5Pset_cache(fapl_id, 0, nc4_chunk_cache_nelems, nc4_chunk_cache_size,
387  nc4_chunk_cache_preemption) < 0)
388  BAIL(NC_EHDFERR);
389  LOG((4, "%s: set HDF raw chunk cache to size %d nelems %d preemption %f",
390  __func__, nc4_chunk_cache_size, nc4_chunk_cache_nelems, nc4_chunk_cache_preemption));
391 #endif /* USE_PARALLEL */
392 
393  if (H5Pset_libver_bounds(fapl_id, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0)
394  BAIL(NC_EHDFERR);
395 
396  /* Create the property list. */
397  if ((fcpl_id = H5Pcreate(H5P_FILE_CREATE)) < 0)
398  BAIL(NC_EHDFERR);
399 #ifdef EXTRA_TESTS
400  num_plists++;
401 #endif
402 
403  /* RJ: this suppose to be FALSE that is defined in H5 private.h as 0 */
404  if (H5Pset_obj_track_times(fcpl_id,0)<0)
405  BAIL(NC_EHDFERR);
406 
407  /* Set latest_format in access propertly list and
408  * H5P_CRT_ORDER_TRACKED in the creation property list. This turns
409  * on HDF5 creation ordering. */
410  if (H5Pset_link_creation_order(fcpl_id, (H5P_CRT_ORDER_TRACKED |
411  H5P_CRT_ORDER_INDEXED)) < 0)
412  BAIL(NC_EHDFERR);
413  if (H5Pset_attr_creation_order(fcpl_id, (H5P_CRT_ORDER_TRACKED |
414  H5P_CRT_ORDER_INDEXED)) < 0)
415  BAIL(NC_EHDFERR);
416 
417  /* Create the file. */
418  if ((nc4_info->hdfid = H5Fcreate(path, flags, fcpl_id, fapl_id)) < 0)
419  /*Change the return error from NC_EFILEMETADATA to
420  System error EACCES because that is the more likely problem */
421  BAIL(EACCES);
422 
423  /* Open the root group. */
424  if ((nc4_info->root_grp->hdf_grpid = H5Gopen2(nc4_info->hdfid, "/",
425  H5P_DEFAULT)) < 0)
426  BAIL(NC_EFILEMETA);
427 
428  /* Release the property lists. */
429  if (H5Pclose(fapl_id) < 0 ||
430  H5Pclose(fcpl_id) < 0)
431  BAIL(NC_EHDFERR);
432 #ifdef EXTRA_TESTS
433  num_plists--;
434  num_plists--;
435 #endif
436 
437  /* Define mode gets turned on automatically on create. */
438  nc4_info->flags |= NC_INDEF;
439 
440  return NC_NOERR;
441 
442 exit: /*failure exit*/
443 #ifdef USE_PARALLEL
444  if (comm_duped) MPI_Comm_free(&nc4_info->comm);
445  if (info_duped) MPI_Info_free(&nc4_info->info);
446 #endif
447 #ifdef EXTRA_TESTS
448  num_plists--;
449 #endif
450  if (fapl_id != H5P_DEFAULT) H5Pclose(fapl_id);
451  if(!nc4_info) return retval;
452  close_netcdf4_file(nc4_info,1); /* treat like abort */
453  return retval;
454 }
455 
471 int
472 NC4_create(const char* path, int cmode, size_t initialsz, int basepe,
473  size_t *chunksizehintp, int use_parallel, void *parameters,
474  NC_Dispatch *dispatch, NC* nc_file)
475 {
476  MPI_Comm comm = MPI_COMM_WORLD;
477  MPI_Info info = MPI_INFO_NULL;
478  int res;
479 
480  assert(nc_file && path);
481 
482  LOG((1, "%s: path %s cmode 0x%x comm %d info %d",
483  __func__, path, cmode, comm, info));
484 
485 #ifdef USE_PARALLEL
486  if (parameters)
487  {
488  comm = ((NC_MPI_INFO *)parameters)->comm;
489  info = ((NC_MPI_INFO *)parameters)->info;
490  }
491 #endif /* USE_PARALLEL */
492 
493  /* If this is our first file, turn off HDF5 error messages. */
494  if (virgin)
495  {
496  if (H5Eset_auto(NULL, NULL) < 0)
497  LOG((0, "Couldn't turn off HDF5 error messages!"));
498  LOG((1, "HDF5 error messages have been turned off."));
499  virgin = 0;
500  }
501 
502  /* Check the cmode for validity. */
503  if((cmode & ILLEGAL_CREATE_FLAGS) != 0)
504  return NC_EINVAL;
505 
506  /* Cannot have both */
507  if((cmode & (NC_MPIIO|NC_MPIPOSIX)) == (NC_MPIIO|NC_MPIPOSIX))
508  return NC_EINVAL;
509 
510  /* Currently no parallel diskless io */
511  if((cmode & (NC_MPIIO | NC_MPIPOSIX)) && (cmode & NC_DISKLESS))
512  return NC_EINVAL;
513 
514 #ifndef USE_PARALLEL_POSIX
515 /* If the HDF5 library has been compiled without the MPI-POSIX VFD, alias
516  * the NC_MPIPOSIX flag to NC_MPIIO. -QAK
517  */
518  if(cmode & NC_MPIPOSIX)
519  {
520  cmode &= ~NC_MPIPOSIX;
521  cmode |= NC_MPIIO;
522  }
523 #endif /* USE_PARALLEL_POSIX */
524 
525  cmode |= NC_NETCDF4;
526 
527  /* Apply default create format. */
528  if (nc_get_default_format() == NC_FORMAT_64BIT)
529  cmode |= NC_64BIT_OFFSET;
530  else if (nc_get_default_format() == NC_FORMAT_NETCDF4_CLASSIC)
531  cmode |= NC_CLASSIC_MODEL;
532 
533  LOG((2, "cmode after applying default format: 0x%x", cmode));
534 
535  nc_file->int_ncid = nc_file->ext_ncid;
536  res = nc4_create_file(path, cmode, comm, info, nc_file);
537 
538 #if 0 /*def USE_PNETCDF*/
539  if (cmode & NC_PNETCDF)
540  {
541  NC_HDF5_FILE_INFO_T* nc4_info;
542  nc4_info = NC4_DATA(nc_file);
543  assert(nc4_info);
544 
545  nc4_info->pnetcdf_file++;
546  res = ncmpi_create(comm, path, cmode, info, &(nc_file->int_ncid));
547  }
548 #endif /* USE_PNETCDF */
549 
550  return res;
551 }
552 
553 /* This function is called by read_dataset when a dimension scale
554  * dataset is encountered. It reads in the dimension data (creating a
555  * new NC_DIM_INFO_T object), and also checks to see if this is a
556  * dimension without a variable - that is, a coordinate dimension
557  * which does not have any coordinate data. */
558 static int
559 read_scale(NC_GRP_INFO_T *grp, hid_t datasetid, const char *obj_name,
560  const H5G_stat_t *statbuf, hsize_t scale_size, hsize_t max_scale_size,
561  NC_DIM_INFO_T **dim)
562 {
563  NC_DIM_INFO_T *new_dim; /* Dimension added to group */
564  char dimscale_name_att[NC_MAX_NAME + 1] = ""; /* Dimscale name, for checking if dim without var */
565  htri_t attr_exists = -1; /* Flag indicating hidden attribute exists */
566  hid_t attid = -1; /* ID of hidden attribute (to store dim ID) */
567  int dimscale_created = 0; /* Remember if a dimension was created (for error recovery) */
568  int initial_grp_ndims = grp->ndims; /* Retain for error recovery */
569  short initial_next_dimid = grp->nc4_info->next_dimid;/* Retain for error recovery */
570  int retval;
571 
572  /* Add a dimension for this scale. */
573  if ((retval = nc4_dim_list_add(&grp->dim, &new_dim)))
574  BAIL(retval);
575  dimscale_created++;
576 
577  /* Does this dataset have a hidden attribute that tells us its
578  * dimid? If so, read it. */
579  if ((attr_exists = H5Aexists(datasetid, NC_DIMID_ATT_NAME)) < 0)
580  BAIL(NC_EHDFERR);
581  if (attr_exists)
582  {
583  if ((attid = H5Aopen_by_name(datasetid, ".", NC_DIMID_ATT_NAME,
584  H5P_DEFAULT, H5P_DEFAULT)) < 0)
585  BAIL(NC_EHDFERR);
586 
587  if (H5Aread(attid, H5T_NATIVE_INT, &new_dim->dimid) < 0)
588  BAIL(NC_EHDFERR);
589 
590  /* Check if scale's dimid should impact the group's next dimid */
591  if (new_dim->dimid >= grp->nc4_info->next_dimid)
592  grp->nc4_info->next_dimid = new_dim->dimid + 1;
593  }
594  else
595  {
596  /* Assign dimid */
597  new_dim->dimid = grp->nc4_info->next_dimid++;
598  }
599 
600  /* Increment number of dimensions. */
601  grp->ndims++;
602 
603  if (!(new_dim->name = strdup(obj_name)))
604  BAIL(NC_ENOMEM);
605  if (SIZEOF_SIZE_T < 8 && scale_size > NC_MAX_UINT)
606  {
607  new_dim->len = NC_MAX_UINT;
608  new_dim->too_long = NC_TRUE;
609  }
610  else
611  new_dim->len = scale_size;
612  new_dim->hdf5_objid.fileno[0] = statbuf->fileno[0];
613  new_dim->hdf5_objid.fileno[1] = statbuf->fileno[1];
614  new_dim->hdf5_objid.objno[0] = statbuf->objno[0];
615  new_dim->hdf5_objid.objno[1] = statbuf->objno[1];
616 
617  /* If the dimscale has an unlimited dimension, then this dimension
618  * is unlimited. */
619  if (max_scale_size == H5S_UNLIMITED)
620  new_dim->unlimited = NC_TRUE;
621 
622  /* If the scale name is set to DIM_WITHOUT_VARIABLE, then this is a
623  * dimension, but not a variable. (If get_scale_name returns an
624  * error, just move on, there's no NAME.) */
625  if (H5DSget_scale_name(datasetid, dimscale_name_att, NC_MAX_NAME) >= 0)
626  {
627  if (!strncmp(dimscale_name_att, DIM_WITHOUT_VARIABLE,
628  strlen(DIM_WITHOUT_VARIABLE)))
629  {
630  if (new_dim->unlimited)
631  {
632  size_t len = 0, *lenp = &len;
633 
634  if ((retval = nc4_find_dim_len(grp, new_dim->dimid, &lenp)))
635  BAIL(retval);
636  new_dim->len = *lenp;
637  }
638 
639  /* Hold open the dataset, since the dimension doesn't have a coordinate variable */
640  new_dim->hdf_dimscaleid = datasetid;
641  H5Iinc_ref(new_dim->hdf_dimscaleid); /* Increment number of objects using ID */
642  }
643  }
644 
645  /* Set the dimension created */
646  *dim = new_dim;
647 
648 exit:
649  /* Close the hidden attribute, if it was opened (error, or no error) */
650  if (attid > 0 && H5Aclose(attid) < 0)
651  BAIL2(NC_EHDFERR);
652 
653  /* On error, undo any dimscale creation */
654  if (retval < 0 && dimscale_created)
655  {
656  /* Delete the dimension */
657  if ((retval = nc4_dim_list_del(&grp->dim, new_dim)))
658  BAIL2(retval);
659 
660  /* Reset the group's information */
661  grp->ndims = initial_grp_ndims;
662  grp->nc4_info->next_dimid = initial_next_dimid;
663  }
664 
665  return retval;
666 }
667 
668 /* This function reads the hacked in coordinates attribute I use for
669  * multi-dimensional coordinates. */
670 static int
671 read_coord_dimids(NC_VAR_INFO_T *var)
672 {
673  hid_t coord_att_typeid = -1, coord_attid = -1, spaceid = -1;
674  hssize_t npoints;
675  int ret = 0;
676 
677  /* There is a hidden attribute telling us the ids of the
678  * dimensions that apply to this multi-dimensional coordinate
679  * variable. Read it. */
680  if ((coord_attid = H5Aopen_name(var->hdf_datasetid, COORDINATES)) < 0) ret++;
681  if (!ret && (coord_att_typeid = H5Aget_type(coord_attid)) < 0) ret++;
682 
683  /* How many dimensions are there? */
684  if (!ret && (spaceid = H5Aget_space(coord_attid)) < 0) ret++;
685 #ifdef EXTRA_TESTS
686  num_spaces++;
687 #endif
688  if (!ret && (npoints = H5Sget_simple_extent_npoints(spaceid)) < 0) ret++;
689 
690  /* Check that the number of points is the same as the number of dimensions
691  * for the variable */
692  if (!ret && npoints != var->ndims) ret++;
693 
694  if (!ret && H5Aread(coord_attid, coord_att_typeid, var->dimids) < 0) ret++;
695  LOG((4, "dimscale %s is multidimensional and has coords", var->name));
696 
697  /* Set my HDF5 IDs free! */
698  if (spaceid >= 0 && H5Sclose(spaceid) < 0) ret++;
699 #ifdef EXTRA_TESTS
700  num_spaces--;
701 #endif
702  if (coord_att_typeid >= 0 && H5Tclose(coord_att_typeid) < 0) ret++;
703  if (coord_attid >= 0 && H5Aclose(coord_attid) < 0) ret++;
704  return ret ? NC_EATTMETA : NC_NOERR;
705 }
706 
707 /* This function is called when reading a file's metadata for each
708  * dimension scale attached to a variable.*/
709 static herr_t
710 dimscale_visitor(hid_t did, unsigned dim, hid_t dsid,
711  void *dimscale_hdf5_objids)
712 {
713  H5G_stat_t statbuf;
714 
715  /* Get more info on the dimscale object.*/
716  if (H5Gget_objinfo(dsid, ".", 1, &statbuf) < 0)
717  return -1;
718 
719  /* Pass this information back to caller. */
720  (*(HDF5_OBJID_T *)dimscale_hdf5_objids).fileno[0] = statbuf.fileno[0];
721  (*(HDF5_OBJID_T *)dimscale_hdf5_objids).fileno[1] = statbuf.fileno[1];
722  (*(HDF5_OBJID_T *)dimscale_hdf5_objids).objno[0] = statbuf.objno[0];
723  (*(HDF5_OBJID_T *)dimscale_hdf5_objids).objno[1] = statbuf.objno[1];
724  return 0;
725 }
726 
727 /* Given an HDF5 type, set a pointer to netcdf type. */
728 static int
729 get_netcdf_type(NC_HDF5_FILE_INFO_T *h5, hid_t native_typeid,
730  nc_type *xtype)
731 {
732  NC_TYPE_INFO_T *type;
733  H5T_class_t class;
734  htri_t is_str, equal = 0;
735 
736  assert(h5 && xtype);
737 
738  if ((class = H5Tget_class(native_typeid)) < 0)
739  return NC_EHDFERR;
740 
741  /* H5Tequal doesn't work with H5T_C_S1 for some reason. But
742  * H5Tget_class will return H5T_STRING if this is a string. */
743  if (class == H5T_STRING)
744  {
745  if ((is_str = H5Tis_variable_str(native_typeid)) < 0)
746  return NC_EHDFERR;
747  if (is_str)
748  *xtype = NC_STRING;
749  else
750  *xtype = NC_CHAR;
751  return NC_NOERR;
752  }
753  else if (class == H5T_INTEGER || class == H5T_FLOAT)
754  {
755  /* For integers and floats, we don't have to worry about
756  * endianness if we compare native types. */
757  if ((equal = H5Tequal(native_typeid, H5T_NATIVE_SCHAR)) < 0)
758  return NC_EHDFERR;
759  if (equal)
760  {
761  *xtype = NC_BYTE;
762  return NC_NOERR;
763  }
764  if ((equal = H5Tequal(native_typeid, H5T_NATIVE_SHORT)) < 0)
765  return NC_EHDFERR;
766  if (equal)
767  {
768  *xtype = NC_SHORT;
769  return NC_NOERR;
770  }
771  if ((equal = H5Tequal(native_typeid, H5T_NATIVE_INT)) < 0)
772  return NC_EHDFERR;
773  if (equal)
774  {
775  *xtype = NC_INT;
776  return NC_NOERR;
777  }
778  if ((equal = H5Tequal(native_typeid, H5T_NATIVE_FLOAT)) < 0)
779  return NC_EHDFERR;
780  if (equal)
781  {
782  *xtype = NC_FLOAT;
783  return NC_NOERR;
784  }
785  if ((equal = H5Tequal(native_typeid, H5T_NATIVE_DOUBLE)) < 0)
786  return NC_EHDFERR;
787  if (equal)
788  {
789  *xtype = NC_DOUBLE;
790  return NC_NOERR;
791  }
792  if ((equal = H5Tequal(native_typeid, H5T_NATIVE_UCHAR)) < 0)
793  return NC_EHDFERR;
794  if (equal)
795  {
796  *xtype = NC_UBYTE;
797  return NC_NOERR;
798  }
799  if ((equal = H5Tequal(native_typeid, H5T_NATIVE_USHORT)) < 0)
800  return NC_EHDFERR;
801  if (equal)
802  {
803  *xtype = NC_USHORT;
804  return NC_NOERR;
805  }
806  if ((equal = H5Tequal(native_typeid, H5T_NATIVE_UINT)) < 0)
807  return NC_EHDFERR;
808  if (equal)
809  {
810  *xtype = NC_UINT;
811  return NC_NOERR;
812  }
813  if ((equal = H5Tequal(native_typeid, H5T_NATIVE_LLONG)) < 0)
814  return NC_EHDFERR;
815  if (equal)
816  {
817  *xtype = NC_INT64;
818  return NC_NOERR;
819  }
820  if ((equal = H5Tequal(native_typeid, H5T_NATIVE_ULLONG)) < 0)
821  return NC_EHDFERR;
822  if (equal)
823  {
824  *xtype = NC_UINT64;
825  return NC_NOERR;
826  }
827  }
828 
829  /* Maybe we already know about this type. */
830  if (!equal)
831  if((type = nc4_rec_find_hdf_type(h5->root_grp, native_typeid)))
832  {
833  *xtype = type->nc_typeid;
834  return NC_NOERR;
835  }
836 
837  *xtype = NC_NAT;
838  return NC_EBADTYPID;
839 }
840 
841 /* Given an HDF5 type, set a pointer to netcdf type_info struct,
842  * either an existing one (for user-defined types) or a newly created
843  * one. */
844 static int
845 get_type_info2(NC_HDF5_FILE_INFO_T *h5, hid_t datasetid,
846  NC_TYPE_INFO_T **type_info)
847 {
848  htri_t is_str, equal = 0;
849  H5T_class_t class;
850  hid_t native_typeid, hdf_typeid;
851  H5T_order_t order;
852  int t;
853 
854  assert(h5 && type_info);
855 
856  /* Because these N5T_NATIVE_* constants are actually function calls
857  * (!) in H5Tpublic.h, I can't initialize this array in the usual
858  * way, because at least some C compilers (like Irix) complain
859  * about calling functions when defining constants. So I have to do
860  * it like this. Note that there's no native types for char or
861  * string. Those are handled later. */
862  if (!h5_native_type_constant_g[1])
863  {
864  h5_native_type_constant_g[1] = H5T_NATIVE_SCHAR;
865  h5_native_type_constant_g[2] = H5T_NATIVE_SHORT;
866  h5_native_type_constant_g[3] = H5T_NATIVE_INT;
867  h5_native_type_constant_g[4] = H5T_NATIVE_FLOAT;
868  h5_native_type_constant_g[5] = H5T_NATIVE_DOUBLE;
869  h5_native_type_constant_g[6] = H5T_NATIVE_UCHAR;
870  h5_native_type_constant_g[7] = H5T_NATIVE_USHORT;
871  h5_native_type_constant_g[8] = H5T_NATIVE_UINT;
872  h5_native_type_constant_g[9] = H5T_NATIVE_LLONG;
873  h5_native_type_constant_g[10] = H5T_NATIVE_ULLONG;
874  }
875 
876  /* Get the HDF5 typeid - we'll need it later. */
877  if ((hdf_typeid = H5Dget_type(datasetid)) < 0)
878  return NC_EHDFERR;
879 
880  /* Get the native typeid. Will be equivalent to hdf_typeid when
881  * creating but not necessarily when reading, a variable. */
882  if ((native_typeid = H5Tget_native_type(hdf_typeid, H5T_DIR_DEFAULT)) < 0)
883  return NC_EHDFERR;
884 
885  /* Is this type an integer, string, compound, or what? */
886  if ((class = H5Tget_class(native_typeid)) < 0)
887  return NC_EHDFERR;
888 
889  /* Is this an atomic type? */
890  if (class == H5T_STRING || class == H5T_INTEGER || class == H5T_FLOAT)
891  {
892  /* Allocate a phony NC_TYPE_INFO_T struct to hold type info. */
893  if (!(*type_info = calloc(1, sizeof(NC_TYPE_INFO_T))))
894  return NC_ENOMEM;
895 
896  /* H5Tequal doesn't work with H5T_C_S1 for some reason. But
897  * H5Tget_class will return H5T_STRING if this is a string. */
898  if (class == H5T_STRING)
899  {
900  if ((is_str = H5Tis_variable_str(native_typeid)) < 0)
901  return NC_EHDFERR;
902  /* Make sure fixed-len strings will work like variable-len strings */
903  if (is_str || H5Tget_size(hdf_typeid) > 1)
904  {
905  /* Set a class for the type */
906  t = NUM_TYPES - 1;
907  (*type_info)->nc_type_class = NC_STRING;
908  }
909  else
910  {
911  /* Set a class for the type */
912  t = 0;
913  (*type_info)->nc_type_class = NC_CHAR;
914  }
915  }
916  else if (class == H5T_INTEGER || class == H5T_FLOAT)
917  {
918  for (t = 1; t < NUM_TYPES - 1; t++)
919  {
920  if ((equal = H5Tequal(native_typeid, h5_native_type_constant_g[t])) < 0)
921  return NC_EHDFERR;
922  if (equal)
923  break;
924  }
925 
926  /* Find out about endianness.
927  * As of HDF 1.8.6, this works with all data types
928  * Not just H5T_INTEGER.
929  *
930  * See https://www.hdfgroup.org/HDF5/doc/RM/RM_H5T.html#Datatype-GetOrder
931  */
932  if((order = H5Tget_order(hdf_typeid)) < 0)
933  return NC_EHDFERR;
934 
935  if(order == H5T_ORDER_LE)
936  (*type_info)->endianness = NC_ENDIAN_LITTLE;
937  else if(order == H5T_ORDER_BE)
938  (*type_info)->endianness = NC_ENDIAN_BIG;
939  else
940  return NC_EBADTYPE;
941 
942  if(class == H5T_INTEGER)
943  (*type_info)->nc_type_class = NC_INT;
944  else
945  (*type_info)->nc_type_class = NC_FLOAT;
946  }
947  (*type_info)->nc_typeid = nc_type_constant_g[t];
948  (*type_info)->size = nc_type_size_g[t];
949  if (!((*type_info)->name = strdup(nc_type_name_g[t])))
950  return NC_ENOMEM;
951  (*type_info)->hdf_typeid = hdf_typeid;
952  (*type_info)->native_hdf_typeid = native_typeid;
953  return NC_NOERR;
954  }
955  else
956  {
957  NC_TYPE_INFO_T *type;
958 
959  /* This is a user-defined type. */
960  if((type = nc4_rec_find_hdf_type(h5->root_grp, native_typeid)))
961  *type_info = type;
962 
963  /* The type entry in the array of user-defined types already has
964  * an open data typeid (and native typeid), so close the ones we
965  * opened above. */
966  if (H5Tclose(native_typeid) < 0)
967  return NC_EHDFERR;
968  if (H5Tclose(hdf_typeid) < 0)
969  return NC_EHDFERR;
970 
971  if (type)
972  return NC_NOERR;
973  }
974 
975  return NC_EBADTYPID;
976 }
977 
978 /* Read an attribute. */
979 static int
980 read_hdf5_att(NC_GRP_INFO_T *grp, hid_t attid, NC_ATT_INFO_T *att)
981 {
982  hid_t spaceid = 0, file_typeid = 0;
983  hsize_t dims[1] = {0}; /* netcdf attributes always 1-D. */
984  int retval = NC_NOERR;
985  size_t type_size;
986  int att_ndims;
987  hssize_t att_npoints;
988  H5T_class_t att_class;
989  int fixed_len_string = 0;
990  size_t fixed_size = 0;
991 
992  assert(att->name);
993  LOG((5, "%s: att->attnum %d att->name %s att->nc_typeid %d att->len %d",
994  __func__, att->attnum, att->name, (int)att->nc_typeid, att->len));
995 
996  /* Get type of attribute in file. */
997  if ((file_typeid = H5Aget_type(attid)) < 0)
998  return NC_EATTMETA;
999  if ((att->native_hdf_typeid = H5Tget_native_type(file_typeid, H5T_DIR_DEFAULT)) < 0)
1000  BAIL(NC_EHDFERR);
1001  if ((att_class = H5Tget_class(att->native_hdf_typeid)) < 0)
1002  BAIL(NC_EATTMETA);
1003  if (att_class == H5T_STRING && !H5Tis_variable_str(att->native_hdf_typeid))
1004  {
1005  fixed_len_string++;
1006  if (!(fixed_size = H5Tget_size(att->native_hdf_typeid)))
1007  BAIL(NC_EATTMETA);
1008  }
1009  if ((retval = get_netcdf_type(grp->nc4_info, att->native_hdf_typeid, &(att->nc_typeid))))
1010  BAIL(retval);
1011 
1012 
1013  /* Get len. */
1014  if ((spaceid = H5Aget_space(attid)) < 0)
1015  BAIL(NC_EATTMETA);
1016 #ifdef EXTRA_TESTS
1017  num_spaces++;
1018 #endif
1019  if ((att_ndims = H5Sget_simple_extent_ndims(spaceid)) < 0)
1020  BAIL(NC_EATTMETA);
1021  if ((att_npoints = H5Sget_simple_extent_npoints(spaceid)) < 0)
1022  BAIL(NC_EATTMETA);
1023 
1024  /* If both att_ndims and att_npoints are zero, then this is a
1025  * zero length att. */
1026  if (att_ndims == 0 && att_npoints == 0)
1027  dims[0] = 0;
1028  else if (att->nc_typeid == NC_STRING)
1029  dims[0] = att_npoints;
1030  else if (att->nc_typeid == NC_CHAR)
1031  {
1032  /* NC_CHAR attributes are written as a scalar in HDF5, of type
1033  * H5T_C_S1, of variable length. */
1034  if (att_ndims == 0)
1035  {
1036  if (!(dims[0] = H5Tget_size(file_typeid)))
1037  BAIL(NC_EATTMETA);
1038  }
1039  else
1040  {
1041  /* This is really a string type! */
1042  att->nc_typeid = NC_STRING;
1043  dims[0] = att_npoints;
1044  }
1045  }
1046  else
1047  {
1048  H5S_class_t space_class;
1049 
1050  /* All netcdf attributes are scalar or 1-D only. */
1051  if (att_ndims > 1)
1052  BAIL(NC_EATTMETA);
1053 
1054  /* Check class of HDF5 dataspace */
1055  if ((space_class = H5Sget_simple_extent_type(spaceid)) < 0)
1056  BAIL(NC_EATTMETA);
1057 
1058  /* Check for NULL HDF5 dataspace class (should be weeded out earlier) */
1059  if (H5S_NULL == space_class)
1060  BAIL(NC_EATTMETA);
1061 
1062  /* check for SCALAR HDF5 dataspace class */
1063  if (H5S_SCALAR == space_class)
1064  dims[0] = 1;
1065  else /* Must be "simple" dataspace */
1066  {
1067  /* Read the size of this attribute. */
1068  if (H5Sget_simple_extent_dims(spaceid, dims, NULL) < 0)
1069  BAIL(NC_EATTMETA);
1070  }
1071  }
1072 
1073  /* Tell the user what the length if this attribute is. */
1074  att->len = dims[0];
1075 
1076  /* Allocate some memory if the len is not zero, and read the
1077  attribute. */
1078  if (dims[0])
1079  {
1080  if ((retval = nc4_get_typelen_mem(grp->nc4_info, att->nc_typeid, 0,
1081  &type_size)))
1082  return retval;
1083  if (att_class == H5T_VLEN)
1084  {
1085  if (!(att->vldata = malloc((unsigned int)(att->len * sizeof(hvl_t)))))
1086  BAIL(NC_ENOMEM);
1087  if (H5Aread(attid, att->native_hdf_typeid, att->vldata) < 0)
1088  BAIL(NC_EATTMETA);
1089  }
1090  else if (att->nc_typeid == NC_STRING)
1091  {
1092  if (!(att->stdata = calloc(att->len, sizeof(char *))))
1093  BAIL(NC_ENOMEM);
1094  /* For a fixed length HDF5 string, the read requires
1095  * contiguous memory. Meanwhile, the netCDF API requires that
1096  * nc_free_string be called on string arrays, which would not
1097  * work if one contiguous memory block were used. So here I
1098  * convert the contiguous block of strings into an array of
1099  * malloced strings (each string with its own malloc). Then I
1100  * copy the data and free the contiguous memory. This
1101  * involves copying the data, which is bad, but this only
1102  * occurs for fixed length string attributes, and presumably
1103  * these are small. (And netCDF-4 does not create them - it
1104  * always uses variable length strings. */
1105  if (fixed_len_string)
1106  {
1107  int i;
1108  char *contig_buf, *cur;
1109 
1110  /* Alloc space for the contiguous memory read. */
1111  if (!(contig_buf = malloc(att->len * fixed_size * sizeof(char))))
1112  BAIL(NC_ENOMEM);
1113 
1114  /* Read the fixed-len strings as one big block. */
1115  if (H5Aread(attid, att->native_hdf_typeid, contig_buf) < 0) {
1116  free(contig_buf);
1117  BAIL(NC_EATTMETA);
1118  }
1119 
1120  /* Copy strings, one at a time, into their new home. Alloc
1121  space for each string. The user will later free this
1122  space with nc_free_string. */
1123  cur = contig_buf;
1124  for (i = 0; i < att->len; i++)
1125  {
1126  if (!(att->stdata[i] = malloc(fixed_size))) {
1127  free(contig_buf);
1128  BAIL(NC_ENOMEM);
1129  }
1130  strncpy(att->stdata[i], cur, fixed_size);
1131  cur += fixed_size;
1132  }
1133 
1134  /* Free contiguous memory buffer. */
1135  free(contig_buf);
1136  }
1137  else
1138  {
1139  /* Read variable-length string atts. */
1140  if (H5Aread(attid, att->native_hdf_typeid, att->stdata) < 0)
1141  BAIL(NC_EATTMETA);
1142  }
1143  }
1144  else
1145  {
1146  if (!(att->data = malloc((unsigned int)(att->len * type_size))))
1147  BAIL(NC_ENOMEM);
1148  if (H5Aread(attid, att->native_hdf_typeid, att->data) < 0)
1149  BAIL(NC_EATTMETA);
1150  }
1151  }
1152 
1153  if (H5Tclose(file_typeid) < 0)
1154  BAIL(NC_EHDFERR);
1155  if (H5Sclose(spaceid) < 0)
1156  return NC_EHDFERR;
1157 #ifdef EXTRA_TESTS
1158  num_spaces--;
1159 #endif
1160 
1161  return NC_NOERR;
1162 
1163  exit:
1164  if (H5Tclose(file_typeid) < 0)
1165  BAIL2(NC_EHDFERR);
1166  if (spaceid > 0 && H5Sclose(spaceid) < 0)
1167  BAIL2(NC_EHDFERR);
1168 #ifdef EXTRA_TESTS
1169  num_spaces--;
1170 #endif
1171  return retval;
1172 }
1173 
1174 /* Read information about a user defined type from the HDF5 file, and
1175  * stash it in the group's list of types. */
1176 static int
1177 read_type(NC_GRP_INFO_T *grp, hid_t hdf_typeid, char *type_name)
1178 {
1179  NC_TYPE_INFO_T *type;
1180  H5T_class_t class;
1181  hid_t native_typeid;
1182  size_t type_size;
1183  int retval = NC_NOERR;
1184 
1185  assert(grp && type_name);
1186 
1187  LOG((4, "%s: type_name %s grp->name %s", __func__, type_name, grp->name));
1188 
1189  /* What is the native type for this platform? */
1190  if ((native_typeid = H5Tget_native_type(hdf_typeid, H5T_DIR_DEFAULT)) < 0)
1191  return NC_EHDFERR;
1192 
1193  /* What is the size of this type on this platform. */
1194  if (!(type_size = H5Tget_size(native_typeid)))
1195  return NC_EHDFERR;
1196  LOG((5, "type_size %d", type_size));
1197 
1198  /* Add to the list for this new type, and get a local pointer to it. */
1199  if ((retval = nc4_type_list_add(grp, type_size, type_name, &type)))
1200  return retval;
1201 
1202  /* Remember common info about this type. */
1203  type->committed = NC_TRUE;
1204  type->hdf_typeid = hdf_typeid;
1205  H5Iinc_ref(type->hdf_typeid); /* Increment number of objects using ID */
1206  type->native_hdf_typeid = native_typeid;
1207 
1208  /* What is the class of this type, compound, vlen, etc. */
1209  if ((class = H5Tget_class(hdf_typeid)) < 0)
1210  return NC_EHDFERR;
1211  switch (class)
1212  {
1213  case H5T_STRING:
1214  type->nc_type_class = NC_STRING;
1215  break;
1216 
1217  case H5T_COMPOUND:
1218  {
1219  int nmembers;
1220  unsigned int m;
1221  char* member_name = NULL;
1222 #ifdef JNA
1223  char jna[1001];
1224 #endif
1225 
1226  type->nc_type_class = NC_COMPOUND;
1227 
1228  if ((nmembers = H5Tget_nmembers(hdf_typeid)) < 0)
1229  return NC_EHDFERR;
1230  LOG((5, "compound type has %d members", nmembers));
1231  for (m = 0; m < nmembers; m++)
1232  {
1233  hid_t member_hdf_typeid;
1234  hid_t member_native_typeid;
1235  size_t member_offset;
1236  H5T_class_t mem_class;
1237  nc_type member_xtype;
1238 
1239 
1240  /* Get the typeid and native typeid of this member of the
1241  * compound type. */
1242  if ((member_hdf_typeid = H5Tget_member_type(type->native_hdf_typeid, m)) < 0)
1243  return NC_EHDFERR;
1244 
1245  if ((member_native_typeid = H5Tget_native_type(member_hdf_typeid, H5T_DIR_DEFAULT)) < 0)
1246  return NC_EHDFERR;
1247 
1248  /* Get the name of the member.*/
1249  member_name = H5Tget_member_name(type->native_hdf_typeid, m);
1250  if (!member_name || strlen(member_name) > NC_MAX_NAME) {
1251  retval = NC_EBADNAME;
1252  break;
1253  }
1254 #ifdef JNA
1255  else {
1256  strncpy(jna,member_name,1000);
1257  member_name = jna;
1258  }
1259 #endif
1260 
1261  /* Offset in bytes on *this* platform. */
1262  member_offset = H5Tget_member_offset(type->native_hdf_typeid, m);
1263 
1264  /* Get dimensional data if this member is an array of something. */
1265  if ((mem_class = H5Tget_class(member_hdf_typeid)) < 0)
1266  return NC_EHDFERR;
1267  if (mem_class == H5T_ARRAY)
1268  {
1269  int ndims, dim_size[NC_MAX_VAR_DIMS];
1270  hsize_t dims[NC_MAX_VAR_DIMS];
1271  int d;
1272 
1273  if ((ndims = H5Tget_array_ndims(member_hdf_typeid)) < 0) {
1274  retval = NC_EHDFERR;
1275  break;
1276  }
1277  if (H5Tget_array_dims(member_hdf_typeid, dims, NULL) != ndims) {
1278  retval = NC_EHDFERR;
1279  break;
1280  }
1281  for (d = 0; d < ndims; d++)
1282  dim_size[d] = dims[d];
1283 
1284  /* What is the netCDF typeid of this member? */
1285  if ((retval = get_netcdf_type(grp->nc4_info, H5Tget_super(member_hdf_typeid),
1286  &member_xtype)))
1287  break;
1288 
1289  /* Add this member to our list of fields in this compound type. */
1290  if ((retval = nc4_field_list_add(&type->u.c.field, type->u.c.num_fields++, member_name,
1291  member_offset, H5Tget_super(member_hdf_typeid),
1292  H5Tget_super(member_native_typeid),
1293  member_xtype, ndims, dim_size)))
1294  break;
1295  }
1296  else
1297  {
1298  /* What is the netCDF typeid of this member? */
1299  if ((retval = get_netcdf_type(grp->nc4_info, member_native_typeid,
1300  &member_xtype)))
1301  break;
1302 
1303  /* Add this member to our list of fields in this compound type. */
1304  if ((retval = nc4_field_list_add(&type->u.c.field, type->u.c.num_fields++, member_name,
1305  member_offset, member_hdf_typeid, member_native_typeid,
1306  member_xtype, 0, NULL)))
1307  break;
1308  }
1309 
1310 #ifndef JNA
1311 
1312  /* Free the member name (which HDF5 allocated for us). */
1313  /* On Windows using the microsoft runtime, it is an error
1314  for one library to free memory allocated by a different library.
1315  IF it is available, we should use H5free_memory*/
1316 
1317 #ifdef HDF5_HAS_H5FREE
1318  if(member_name != NULL) H5free_memory(member_name);
1319 #else
1320 #ifndef _MSC_VER
1321  if(member_name != NULL) free(member_name);
1322 #endif
1323 #endif
1324 #endif
1325  member_name = NULL;
1326  }
1327 #ifndef JNA
1328  if(member_name != NULL)
1329  free(member_name);
1330 #endif
1331  if(retval) /* error exit from loop */
1332  return retval;
1333  }
1334  break;
1335 
1336  case H5T_VLEN:
1337  {
1338  htri_t ret;
1339 
1340  /* For conveninence we allow user to pass vlens of strings
1341  * with null terminated strings. This means strings are
1342  * treated slightly differently by the API, although they are
1343  * really just VLENs of characters. */
1344  if ((ret = H5Tis_variable_str(hdf_typeid)) < 0)
1345  return NC_EHDFERR;
1346  if (ret)
1347  type->nc_type_class = NC_STRING;
1348  else
1349  {
1350  hid_t base_hdf_typeid;
1351  nc_type base_nc_type = NC_NAT;
1352 
1353  type->nc_type_class = NC_VLEN;
1354 
1355  /* Find the base type of this vlen (i.e. what is this a
1356  * vlen of?) */
1357  if (!(base_hdf_typeid = H5Tget_super(native_typeid)))
1358  return NC_EHDFERR;
1359 
1360  /* What size is this type? */
1361  if (!(type_size = H5Tget_size(base_hdf_typeid)))
1362  return NC_EHDFERR;
1363 
1364  /* What is the netcdf corresponding type. */
1365  if ((retval = get_netcdf_type(grp->nc4_info, base_hdf_typeid,
1366  &base_nc_type)))
1367  return retval;
1368  LOG((5, "base_hdf_typeid 0x%x type_size %d base_nc_type %d",
1369  base_hdf_typeid, type_size, base_nc_type));
1370 
1371  /* Remember the base types for this vlen */
1372  type->u.v.base_nc_typeid = base_nc_type;
1373  type->u.v.base_hdf_typeid = base_hdf_typeid;
1374  }
1375  }
1376  break;
1377 
1378  case H5T_OPAQUE:
1379  type->nc_type_class = NC_OPAQUE;
1380  break;
1381 
1382  case H5T_ENUM:
1383  {
1384  hid_t base_hdf_typeid;
1385  nc_type base_nc_type = NC_NAT;
1386  void *value;
1387  int i;
1388  char *member_name = NULL;
1389 #ifdef JNA
1390  char jna[1001];
1391 #endif
1392 
1393  type->nc_type_class = NC_ENUM;
1394 
1395  /* Find the base type of this enum (i.e. what is this a
1396  * enum of?) */
1397  if (!(base_hdf_typeid = H5Tget_super(hdf_typeid)))
1398  return NC_EHDFERR;
1399  /* What size is this type? */
1400  if (!(type_size = H5Tget_size(base_hdf_typeid)))
1401  return NC_EHDFERR;
1402  /* What is the netcdf corresponding type. */
1403  if ((retval = get_netcdf_type(grp->nc4_info, base_hdf_typeid,
1404  &base_nc_type)))
1405  return retval;
1406  LOG((5, "base_hdf_typeid 0x%x type_size %d base_nc_type %d",
1407  base_hdf_typeid, type_size, base_nc_type));
1408 
1409  /* Remember the base types for this enum */
1410  type->u.e.base_nc_typeid = base_nc_type;
1411  type->u.e.base_hdf_typeid = base_hdf_typeid;
1412 
1413  /* Find out how many member are in the enum. */
1414  if ((type->u.e.num_members = H5Tget_nmembers(hdf_typeid)) < 0)
1415  return NC_EHDFERR;
1416 
1417  /* Allocate space for one value. */
1418  if (!(value = calloc(1, type_size)))
1419  return NC_ENOMEM;
1420 
1421  /* Read each name and value defined in the enum. */
1422  for (i = 0; i < type->u.e.num_members; i++)
1423  {
1424 
1425  /* Get the name and value from HDF5. */
1426  if (!(member_name = H5Tget_member_name(hdf_typeid, i)))
1427  {
1428  retval = NC_EHDFERR;
1429  break;
1430  }
1431 #ifdef JNA
1432  strncpy(jna,member_name,1000);
1433  member_name = jna;
1434 #endif
1435 
1436  if (strlen(member_name) > NC_MAX_NAME)
1437  {
1438  retval = NC_EBADNAME;
1439  break;
1440  }
1441  if (H5Tget_member_value(hdf_typeid, i, value) < 0)
1442  {
1443  retval = NC_EHDFERR;
1444  break;
1445  }
1446 
1447  /* Insert new field into this type's list of fields. */
1448  if ((retval = nc4_enum_member_add(&type->u.e.enum_member, type->size,
1449  member_name, value)))
1450  {
1451  break;
1452  }
1453 
1454 #ifndef JNA
1455  /* Free the member name (which HDF5 allocated for us). */
1456  if(member_name != NULL) free(member_name);
1457 #endif
1458  member_name = NULL;
1459  }
1460 
1461 #ifndef JNA
1462  if(member_name != NULL)
1463  free(member_name);
1464 #endif
1465  if(value) free(value);
1466  if(retval) /* error exit from loop */
1467  return retval;
1468  }
1469  break;
1470 
1471  default:
1472  LOG((0, "unknown class"));
1473  return NC_EBADCLASS;
1474  }
1475  return retval;
1476 }
1477 
1478 /* This function is called by read_dataset, (which is called by
1479  * nc4_rec_read_metadata) when a netCDF variable is found in the
1480  * file. This function reads in all the metadata about the var,
1481  * including the attributes. */
1482 static int
1483 read_var(NC_GRP_INFO_T *grp, hid_t datasetid, const char *obj_name,
1484  size_t ndims, NC_DIM_INFO_T *dim)
1485 {
1486  NC_VAR_INFO_T *var = NULL;
1487  hid_t access_pid = 0;
1488  int incr_id_rc = 0; /* Whether the dataset ID's ref count has been incremented */
1489  int natts, a, d;
1490 
1491  NC_ATT_INFO_T *att;
1492  hid_t attid = 0;
1493  char att_name[NC_MAX_HDF5_NAME + 1];
1494 
1495 #define CD_NELEMS_ZLIB 1
1496 #define CD_NELEMS_SZIP 4
1497  H5Z_filter_t filter;
1498  int num_filters;
1499  unsigned int cd_values[CD_NELEMS_SZIP];
1500  size_t cd_nelems = CD_NELEMS_SZIP;
1501  hid_t propid = 0;
1502  H5D_fill_value_t fill_status;
1503  H5D_layout_t layout;
1504  hsize_t chunksize[NC_MAX_VAR_DIMS] = {0};
1505  int retval = NC_NOERR;
1506  double rdcc_w0;
1507  int f;
1508 
1509  assert(obj_name && grp);
1510  LOG((4, "%s: obj_name %s", __func__, obj_name));
1511 
1512  /* Add a variable to the end of the group's var list. */
1513  if ((retval = nc4_var_list_add(&grp->var, &var)))
1514  BAIL(retval);
1515 
1516  /* Fill in what we already know. */
1517  var->hdf_datasetid = datasetid;
1518  H5Iinc_ref(var->hdf_datasetid); /* Increment number of objects using ID */
1519  incr_id_rc++; /* Indicate that we've incremented the ref. count (for errors) */
1520  var->varid = grp->nvars++;
1521  var->created = NC_TRUE;
1522  var->ndims = ndims;
1523 
1524  /* We need some room to store information about dimensions for this
1525  * var. */
1526  if (var->ndims)
1527  {
1528  if (!(var->dim = calloc(var->ndims, sizeof(NC_DIM_INFO_T *))))
1529  BAIL(NC_ENOMEM);
1530  if (!(var->dimids = calloc(var->ndims, sizeof(int))))
1531  BAIL(NC_ENOMEM);
1532  }
1533 
1534  /* Get the current chunk cache settings. */
1535  if ((access_pid = H5Dget_access_plist(datasetid)) < 0)
1536  BAIL(NC_EVARMETA);
1537 #ifdef EXTRA_TESTS
1538  num_plists++;
1539 #endif
1540 
1541  /* Learn about current chunk cache settings. */
1542  if ((H5Pget_chunk_cache(access_pid, &(var->chunk_cache_nelems),
1543  &(var->chunk_cache_size), &rdcc_w0)) < 0)
1544  BAIL(NC_EHDFERR);
1545  var->chunk_cache_preemption = rdcc_w0;
1546 
1547  /* Check for a weird case: a non-coordinate variable that has the
1548  * same name as a dimension. It's legal in netcdf, and requires
1549  * that the HDF5 dataset name be changed. */
1550  if (strlen(obj_name) > strlen(NON_COORD_PREPEND) &&
1551  !strncmp(obj_name, NON_COORD_PREPEND, strlen(NON_COORD_PREPEND)))
1552  {
1553  /* Allocate space for the name. */
1554  if (!(var->name = malloc(((strlen(obj_name) - strlen(NON_COORD_PREPEND))+ 1) * sizeof(char))))
1555  BAIL(NC_ENOMEM);
1556 
1557  strcpy(var->name, &obj_name[strlen(NON_COORD_PREPEND)]);
1558 
1559  /* Allocate space for the HDF5 name. */
1560  if (!(var->hdf5_name = malloc((strlen(obj_name) + 1) * sizeof(char))))
1561  BAIL(NC_ENOMEM);
1562 
1563  strcpy(var->hdf5_name, obj_name);
1564  }
1565  else
1566  {
1567  /* Allocate space for the name. */
1568  if (!(var->name = malloc((strlen(obj_name) + 1) * sizeof(char))))
1569  BAIL(NC_ENOMEM);
1570 
1571  strcpy(var->name, obj_name);
1572  }
1573 
1574  /* Find out what filters are applied to this HDF5 dataset,
1575  * fletcher32, deflate, and/or shuffle. All other filters are
1576  * ignored. */
1577  if ((propid = H5Dget_create_plist(datasetid)) < 0)
1578  BAIL(NC_EHDFERR);
1579 #ifdef EXTRA_TESTS
1580  num_plists++;
1581 #endif /* EXTRA_TESTS */
1582 
1583  /* Get the chunking info for non-scalar vars. */
1584  if ((layout = H5Pget_layout(propid)) < -1)
1585  BAIL(NC_EHDFERR);
1586  if (layout == H5D_CHUNKED)
1587  {
1588  if (H5Pget_chunk(propid, NC_MAX_VAR_DIMS, chunksize) < 0)
1589  BAIL(NC_EHDFERR);
1590  if (!(var->chunksizes = malloc(var->ndims * sizeof(size_t))))
1591  BAIL(NC_ENOMEM);
1592  for (d = 0; d < var->ndims; d++)
1593  var->chunksizes[d] = chunksize[d];
1594  }
1595  else if (layout == H5D_CONTIGUOUS || layout == H5D_COMPACT)
1596  var->contiguous = NC_TRUE;
1597 
1598  /* The possible values of filter (which is just an int) can be
1599  * found in H5Zpublic.h. */
1600  if ((num_filters = H5Pget_nfilters(propid)) < 0)
1601  BAIL(NC_EHDFERR);
1602  for (f = 0; f < num_filters; f++)
1603  {
1604  if ((filter = H5Pget_filter2(propid, f, NULL, &cd_nelems,
1605  cd_values, 0, NULL, NULL)) < 0)
1606  BAIL(NC_EHDFERR);
1607  switch (filter)
1608  {
1609  case H5Z_FILTER_SHUFFLE:
1610  var->shuffle = NC_TRUE;
1611  break;
1612 
1613  case H5Z_FILTER_FLETCHER32:
1614  var->fletcher32 = NC_TRUE;
1615  break;
1616 
1617  case H5Z_FILTER_DEFLATE:
1618  var->deflate = NC_TRUE;
1619  if (cd_nelems != CD_NELEMS_ZLIB || cd_values[0] > MAX_DEFLATE_LEVEL)
1620  BAIL(NC_EHDFERR);
1621  var->deflate_level = cd_values[0];
1622  break;
1623 
1624  case H5Z_FILTER_SZIP:
1625  var->szip = NC_TRUE;
1626  if (cd_nelems != CD_NELEMS_SZIP)
1627  BAIL(NC_EHDFERR);
1628  var->options_mask = cd_values[0];
1629  var->pixels_per_block = cd_values[1];
1630  break;
1631 
1632  default:
1633  LOG((1, "Yikes! Unknown filter type found on dataset!"));
1634  break;
1635  }
1636  }
1637 
1638  /* Learn all about the type of this variable. */
1639  if ((retval = get_type_info2(grp->nc4_info, datasetid,
1640  &var->type_info)))
1641  BAIL(retval);
1642 
1643  /* Indicate that the variable has a pointer to the type */
1644  var->type_info->rc++;
1645 
1646  /* Is there a fill value associated with this dataset? */
1647  if (H5Pfill_value_defined(propid, &fill_status) < 0)
1648  BAIL(NC_EHDFERR);
1649 
1650  /* Get the fill value, if there is one defined. */
1651  if (fill_status == H5D_FILL_VALUE_USER_DEFINED)
1652  {
1653  /* Allocate space to hold the fill value. */
1654  if (!var->fill_value)
1655  {
1656  if (var->type_info->nc_type_class == NC_VLEN)
1657  {
1658  if (!(var->fill_value = malloc(sizeof(nc_vlen_t))))
1659  BAIL(NC_ENOMEM);
1660  }
1661  else if (var->type_info->nc_type_class == NC_STRING)
1662  {
1663  if (!(var->fill_value = malloc(sizeof(char *))))
1664  BAIL(NC_ENOMEM);
1665  }
1666  else
1667  {
1668  assert(var->type_info->size);
1669  if (!(var->fill_value = malloc(var->type_info->size)))
1670  BAIL(NC_ENOMEM);
1671  }
1672  }
1673 
1674  /* Get the fill value from the HDF5 property lust. */
1675  if (H5Pget_fill_value(propid, var->type_info->native_hdf_typeid,
1676  var->fill_value) < 0)
1677  BAIL(NC_EHDFERR);
1678  }
1679  else
1680  var->no_fill = NC_TRUE;
1681 
1682  /* If it's a scale, mark it as such. */
1683  if (dim)
1684  {
1685  assert(ndims);
1686  var->dimscale = NC_TRUE;
1687  if (var->ndims > 1)
1688  {
1689  if ((retval = read_coord_dimids(var)))
1690  BAIL(retval);
1691  }
1692  else
1693  {
1694  /* sanity check */
1695  assert(0 == strcmp(var->name, dim->name));
1696 
1697  var->dimids[0] = dim->dimid;
1698  var->dim[0] = dim;
1699  }
1700  dim->coord_var = var;
1701  }
1702  /* If this is not a scale, but has scales, iterate
1703  * through them. (i.e. this is a variable that is not a
1704  * coordinate variable) */
1705  else
1706  {
1707  int num_scales = 0;
1708 
1709  /* Find out how many scales are attached to this
1710  * dataset. H5DSget_num_scales returns an error if there are no
1711  * scales, so convert a negative return value to zero. */
1712  num_scales = H5DSget_num_scales(datasetid, 0);
1713  if (num_scales < 0)
1714  num_scales = 0;
1715 
1716  if (num_scales && ndims)
1717  {
1718  /* Allocate space to remember whether the dimscale has been attached
1719  * for each dimension. */
1720  if (NULL == (var->dimscale_attached = calloc(ndims, sizeof(nc_bool_t))))
1721  BAIL(NC_ENOMEM);
1722 
1723  /* Store id information allowing us to match hdf5
1724  * dimscales to netcdf dimensions. */
1725  if (NULL == (var->dimscale_hdf5_objids = malloc(ndims * sizeof(struct hdf5_objid))))
1726  BAIL(NC_ENOMEM);
1727  for (d = 0; d < var->ndims; d++)
1728  {
1729  if (H5DSiterate_scales(var->hdf_datasetid, d, NULL, dimscale_visitor,
1730  &(var->dimscale_hdf5_objids[d])) < 0)
1731  BAIL(NC_EHDFERR);
1732  var->dimscale_attached[d] = NC_TRUE;
1733  }
1734  }
1735  }
1736 
1737  /* Now read all the attributes of this variable, ignoring the
1738  ones that hold HDF5 dimension scale information. */
1739  if ((natts = H5Aget_num_attrs(var->hdf_datasetid)) < 0)
1740  BAIL(NC_EATTMETA);
1741  for (a = 0; a < natts; a++)
1742  {
1743  /* Close the attribute and try to move on with our
1744  * lives. Like bits through the network port, so
1745  * flows the Days of Our Lives! */
1746  if (attid && H5Aclose(attid) < 0)
1747  BAIL(NC_EHDFERR);
1748 
1749  /* Open the att and get its name. */
1750  if ((attid = H5Aopen_idx(var->hdf_datasetid, (unsigned int)a)) < 0)
1751  BAIL(NC_EATTMETA);
1752  if (H5Aget_name(attid, NC_MAX_HDF5_NAME, att_name) < 0)
1753  BAIL(NC_EATTMETA);
1754  LOG((4, "%s:: a %d att_name %s", __func__, a, att_name));
1755 
1756  /* Should we ignore this attribute? */
1757  if (strcmp(att_name, REFERENCE_LIST) &&
1758  strcmp(att_name, CLASS) &&
1759  strcmp(att_name, DIMENSION_LIST) &&
1760  strcmp(att_name, NAME) &&
1761  strcmp(att_name, COORDINATES) &&
1762  strcmp(att_name, NC_DIMID_ATT_NAME))
1763  {
1764  /* Add to the end of the list of atts for this var. */
1765  if ((retval = nc4_att_list_add(&var->att, &att)))
1766  BAIL(retval);
1767 
1768  /* Fill in the information we know. */
1769  att->attnum = var->natts++;
1770  if (!(att->name = strdup(att_name)))
1771  BAIL(NC_ENOMEM);
1772 
1773  /* Read the rest of the info about the att,
1774  * including its values. */
1775  if ((retval = read_hdf5_att(grp, attid, att)))
1776  {
1777  if (NC_EBADTYPID == retval)
1778  {
1779  if ((retval = nc4_att_list_del(&var->att, att)))
1780  BAIL(retval);
1781  continue;
1782  }
1783  else
1784  BAIL(retval);
1785  }
1786 
1787  att->created = NC_TRUE;
1788  } /* endif not HDF5 att */
1789  } /* next attribute */
1790 
1791  /* Is this a deflated variable with a chunksize greater than the
1792  * current cache size? */
1793  if ((retval = nc4_adjust_var_cache(grp, var)))
1794  BAIL(retval);
1795 
1796 exit:
1797  if (retval)
1798  {
1799  if (incr_id_rc && H5Idec_ref(datasetid) < 0)
1800  BAIL2(NC_EHDFERR);
1801  if (var && nc4_var_list_del(&grp->var, var))
1802  BAIL2(NC_EHDFERR);
1803  }
1804  if (access_pid && H5Pclose(access_pid) < 0)
1805  BAIL2(NC_EHDFERR);
1806 #ifdef EXTRA_TESTS
1807  num_plists--;
1808 #endif
1809  if (propid > 0 && H5Pclose(propid) < 0)
1810  BAIL2(NC_EHDFERR);
1811 #ifdef EXTRA_TESTS
1812  num_plists--;
1813 #endif
1814  if (attid > 0 && H5Aclose(attid) < 0)
1815  BAIL2(NC_EHDFERR);
1816  return retval;
1817 }
1818 
1819 /* This function is called by nc4_rec_read_metadata to read all the
1820  * group level attributes (the NC_GLOBAL atts for this group). */
1821 static int
1822 read_grp_atts(NC_GRP_INFO_T *grp)
1823 {
1824  hid_t attid = 0;
1825  hsize_t num_obj, i;
1826  NC_ATT_INFO_T *att;
1827  NC_TYPE_INFO_T *type;
1828  char obj_name[NC_MAX_HDF5_NAME + 1];
1829  int max_len;
1830  int retval = NC_NOERR;
1831 
1832  num_obj = H5Aget_num_attrs(grp->hdf_grpid);
1833  for (i = 0; i < num_obj; i++)
1834  {
1835  /* Close an attribute from previous loop iteration */
1836  /* (Should be from 'continue' statement, below) */
1837  if (attid && H5Aclose(attid) < 0)
1838  BAIL(NC_EHDFERR);
1839 
1840  if ((attid = H5Aopen_idx(grp->hdf_grpid, (unsigned int)i)) < 0)
1841  BAIL(NC_EATTMETA);
1842  if (H5Aget_name(attid, NC_MAX_NAME + 1, obj_name) < 0)
1843  BAIL(NC_EATTMETA);
1844  LOG((3, "reading attribute of _netCDF group, named %s", obj_name));
1845 
1846  /* This may be an attribute telling us that strict netcdf-3
1847  * rules are in effect. If so, we will make note of the fact,
1848  * but not add this attribute to the metadata. It's not a user
1849  * attribute, but an internal netcdf-4 one. */
1850  if (!strcmp(obj_name, NC3_STRICT_ATT_NAME))
1851  grp->nc4_info->cmode |= NC_CLASSIC_MODEL;
1852  else
1853  {
1854  /* Add an att struct at the end of the list, and then go to it. */
1855  if ((retval = nc4_att_list_add(&grp->att, &att)))
1856  BAIL(retval);
1857 
1858  /* Add the info about this attribute. */
1859  max_len = strlen(obj_name) > NC_MAX_NAME ? NC_MAX_NAME : strlen(obj_name);
1860  if (!(att->name = malloc((max_len + 1) * sizeof(char))))
1861  BAIL(NC_ENOMEM);
1862  strncpy(att->name, obj_name, max_len);
1863  att->name[max_len] = 0;
1864  att->attnum = grp->natts++;
1865  if ((retval = read_hdf5_att(grp, attid, att)))
1866  {
1867  if (NC_EBADTYPID == retval)
1868  {
1869  if ((retval = nc4_att_list_del(&grp->att, att)))
1870  BAIL(retval);
1871  continue;
1872  }
1873  else
1874  BAIL(retval);
1875  }
1876  att->created = NC_TRUE;
1877  if ((retval = nc4_find_type(grp->nc4_info, att->nc_typeid, &type)))
1878  BAIL(retval);
1879  }
1880  }
1881 
1882  exit:
1883  if (attid > 0 && H5Aclose(attid) < 0)
1884  BAIL2(NC_EHDFERR);
1885  return retval;
1886 }
1887 
1888 /* This function is called when nc4_rec_read_metadata encounters an HDF5
1889  * dataset when reading a file. */
1890 static int
1891 read_dataset(NC_GRP_INFO_T *grp, hid_t datasetid, const char *obj_name,
1892  const H5G_stat_t *statbuf)
1893 {
1894  NC_DIM_INFO_T *dim = NULL; /* Dimension created for scales */
1895  hid_t spaceid = 0;
1896  int ndims;
1897  htri_t is_scale;
1898  int retval = NC_NOERR;
1899 
1900  /* Get the dimension information for this dataset. */
1901  if ((spaceid = H5Dget_space(datasetid)) < 0)
1902  BAIL(NC_EHDFERR);
1903 #ifdef EXTRA_TESTS
1904  num_spaces++;
1905 #endif
1906  if ((ndims = H5Sget_simple_extent_ndims(spaceid)) < 0)
1907  BAIL(NC_EHDFERR);
1908 
1909  /* Is this a dimscale? */
1910  if ((is_scale = H5DSis_scale(datasetid)) < 0)
1911  BAIL(NC_EHDFERR);
1912  if (is_scale)
1913  {
1914  hsize_t dims[H5S_MAX_RANK];
1915  hsize_t max_dims[H5S_MAX_RANK];
1916 
1917  /* Query the scale's size & max. size */
1918  if (H5Sget_simple_extent_dims(spaceid, dims, max_dims) < 0)
1919  BAIL(NC_EHDFERR);
1920 
1921  /* Read the scale information. */
1922  if ((retval = read_scale(grp, datasetid, obj_name, statbuf, dims[0],
1923  max_dims[0], &dim)))
1924  BAIL(retval);
1925  }
1926 
1927  /* Add a var to the linked list, and get its metadata,
1928  * unless this is one of those funny dimscales that are a
1929  * dimension in netCDF but not a variable. (Spooky!) */
1930  if (NULL == dim || (dim && !dim->hdf_dimscaleid))
1931  if ((retval = read_var(grp, datasetid, obj_name, ndims, dim)))
1932  BAIL(retval);
1933 
1934 exit:
1935  if (spaceid && H5Sclose(spaceid) <0)
1936  BAIL2(retval);
1937 #ifdef EXTRA_TESTS
1938  num_spaces--;
1939 #endif
1940 
1941  return retval;
1942 }
1943 
1944 static int
1945 nc4_rec_read_metadata_cb_list_add(NC4_rec_read_metadata_obj_info_t **head,
1947  const NC4_rec_read_metadata_obj_info_t *oinfo)
1948 {
1949  NC4_rec_read_metadata_obj_info_t *new_oinfo; /* Pointer to info for object */
1950 
1951  /* Allocate memory for the object's info */
1952  if (!(new_oinfo = calloc(1, sizeof(*new_oinfo))))
1953  return NC_ENOMEM;
1954 
1955  /* Make a copy of the object's info */
1956  memcpy(new_oinfo, oinfo, sizeof(*oinfo));
1957 
1958  if (*tail)
1959  {
1960  assert(*head);
1961  (*tail)->next = new_oinfo;
1962  *tail = new_oinfo;
1963  }
1964  else
1965  {
1966  assert(NULL == *head);
1967  *head = *tail = new_oinfo;
1968  }
1969 
1970  return (NC_NOERR);
1971 }
1972 
1973 static int
1974 nc4_rec_read_metadata_cb(hid_t grpid, const char *name, const H5L_info_t *info,
1975  void *_op_data)
1976 {
1977  NC4_rec_read_metadata_ud_t *udata = (NC4_rec_read_metadata_ud_t *)_op_data; /* Pointer to user data for callback */
1978  NC4_rec_read_metadata_obj_info_t oinfo; /* Pointer to info for object */
1979  int retval = H5_ITER_CONT;
1980 
1981  /* Reset the memory for the object's info */
1982  memset(&oinfo, 0, sizeof(oinfo));
1983 
1984  /* Open this critter. */
1985  if ((oinfo.oid = H5Oopen(grpid, name, H5P_DEFAULT)) < 0)
1986  BAIL(H5_ITER_ERROR);
1987 
1988  /* Get info about the object.*/
1989  if (H5Gget_objinfo(oinfo.oid, ".", 1, &oinfo.statbuf) < 0)
1990  BAIL(H5_ITER_ERROR);
1991 
1992  strncpy(oinfo.oname, name, NC_MAX_NAME);
1993 
1994  /* Add object to list, for later */
1995  switch(oinfo.statbuf.type)
1996  {
1997  case H5G_GROUP:
1998  LOG((3, "found group %s", oinfo.oname));
1999 
2000  /* Defer descending into child group immediately, so that the types
2001  * in the current group can be processed and be ready for use by
2002  * vars in the child group(s).
2003  */
2004  if (nc4_rec_read_metadata_cb_list_add(&udata->grps_head, &udata->grps_tail, &oinfo))
2005  BAIL(H5_ITER_ERROR);
2006  break;
2007 
2008  case H5G_DATASET:
2009  LOG((3, "found dataset %s", oinfo.oname));
2010 
2011  /* Learn all about this dataset, which may be a dimscale
2012  * (i.e. dimension metadata), or real data. */
2013  if ((retval = read_dataset(udata->grp, oinfo.oid, oinfo.oname, &oinfo.statbuf)))
2014  {
2015  /* Allow NC_EBADTYPID to transparently skip over datasets
2016  * which have a datatype that netCDF-4 doesn't undertand
2017  * (currently), but break out of iteration for other
2018  * errors.
2019  */
2020  if(NC_EBADTYPID != retval)
2021  BAIL(H5_ITER_ERROR);
2022  else
2023  retval = H5_ITER_CONT;
2024  }
2025 
2026  /* Close the object */
2027  if (H5Oclose(oinfo.oid) < 0)
2028  BAIL(H5_ITER_ERROR);
2029  break;
2030 
2031  case H5G_TYPE:
2032  LOG((3, "found datatype %s", oinfo.oname));
2033 
2034  /* Process the named datatype */
2035  if (read_type(udata->grp, oinfo.oid, oinfo.oname))
2036  BAIL(H5_ITER_ERROR);
2037 
2038  /* Close the object */
2039  if (H5Oclose(oinfo.oid) < 0)
2040  BAIL(H5_ITER_ERROR);
2041  break;
2042 
2043  default:
2044  LOG((0, "Unknown object class %d in %s!", oinfo.statbuf.type, __func__));
2045  BAIL(H5_ITER_ERROR);
2046  }
2047 
2048 exit:
2049  if (retval)
2050  {
2051  if (oinfo.oid > 0 && H5Oclose(oinfo.oid) < 0)
2052  BAIL2(H5_ITER_ERROR);
2053  }
2054 
2055  return (retval);
2056 }
2057 
2058 /* This is the main function to recursively read all the metadata for the file. */
2059 /* The links in the 'grp' are iterated over and added to the file's metadata
2060  * information. Note that child groups are not immediately processed, but
2061  * are deferred until all the other links in the group are handled (so that
2062  * vars in the child groups are guaranteed to have types that they use in
2063  * a parent group in place).
2064  */
2065 static int
2066 nc4_rec_read_metadata(NC_GRP_INFO_T *grp)
2067 {
2068  NC4_rec_read_metadata_ud_t udata; /* User data for iteration */
2069  NC4_rec_read_metadata_obj_info_t *oinfo; /* Pointer to info for object */
2070  hsize_t idx=0;
2071  hid_t pid = 0;
2072  unsigned crt_order_flags = 0;
2073  H5_index_t iter_index;
2074  int retval = NC_NOERR; /* everything worked! */
2075 
2076  assert(grp && grp->name);
2077  LOG((3, "%s: grp->name %s", __func__, grp->name));
2078 
2079  /* Portably initialize user data for later */
2080  memset(&udata, 0, sizeof(udata));
2081 
2082  /* Open this HDF5 group and retain its grpid. It will remain open
2083  * with HDF5 until this file is nc_closed. */
2084  if (!grp->hdf_grpid)
2085  {
2086  if (grp->parent)
2087  {
2088  if ((grp->hdf_grpid = H5Gopen2(grp->parent->hdf_grpid,
2089  grp->name, H5P_DEFAULT)) < 0)
2090  BAIL(NC_EHDFERR);
2091  }
2092  else
2093  {
2094  if ((grp->hdf_grpid = H5Gopen2(grp->nc4_info->hdfid,
2095  "/", H5P_DEFAULT)) < 0)
2096  BAIL(NC_EHDFERR);
2097  }
2098  }
2099  assert(grp->hdf_grpid > 0);
2100 
2101  /* Get the group creation flags, to check for creation ordering */
2102  pid = H5Gget_create_plist(grp->hdf_grpid);
2103  H5Pget_link_creation_order(pid, &crt_order_flags);
2104  if (H5Pclose(pid) < 0)
2105  BAIL(NC_EHDFERR);
2106 
2107  /* Set the iteration index to use */
2108  if (crt_order_flags & H5P_CRT_ORDER_TRACKED)
2109  iter_index = H5_INDEX_CRT_ORDER;
2110  else
2111  {
2112  NC_HDF5_FILE_INFO_T *h5 = grp->nc4_info;
2113 
2114  /* Without creation ordering, file must be read-only. */
2115  if (!h5->no_write)
2116  BAIL(NC_ECANTWRITE);
2117 
2118  iter_index = H5_INDEX_NAME;
2119  }
2120 
2121  /* Set user data for iteration */
2122  udata.grp = grp;
2123 
2124  /* Iterate over links in this group, building lists for the types,
2125  * datasets and groups encountered
2126  */
2127  if (H5Literate(grp->hdf_grpid, iter_index, H5_ITER_INC, &idx,
2128  nc4_rec_read_metadata_cb, (void *)&udata) < 0)
2129  BAIL(NC_EHDFERR);
2130 
2131  /* Process the child groups found */
2132  /* (Deferred until now, so that the types in the current group get
2133  * processed and are available for vars in the child group(s).)
2134  */
2135  for (oinfo = udata.grps_head; oinfo; oinfo = udata.grps_head)
2136  {
2137  NC_GRP_INFO_T *child_grp;
2138  NC_HDF5_FILE_INFO_T *h5 = grp->nc4_info;
2139 
2140  /* Add group to file's hierarchy */
2141  if ((retval = nc4_grp_list_add(&(grp->children), h5->next_nc_grpid++,
2142  grp, grp->nc4_info->controller, oinfo->oname, &child_grp)))
2143  BAIL(retval);
2144 
2145  /* Recursively read the child group's metadata */
2146  if ((retval = nc4_rec_read_metadata(child_grp)))
2147  BAIL(retval);
2148 
2149  /* Close the object */
2150  if (H5Oclose(oinfo->oid) < 0)
2151  BAIL(NC_EHDFERR);
2152 
2153  /* Advance to next node, free current node */
2154  udata.grps_head = oinfo->next;
2155  free(oinfo);
2156  }
2157 
2158  /* Scan the group for global (i.e. group-level) attributes. */
2159  if ((retval = read_grp_atts(grp)))
2160  BAIL(retval);
2161 
2162 exit:
2163  /* Clean up local information on error, if anything remains */
2164  if (retval)
2165  {
2166  for (oinfo = udata.grps_head; oinfo; oinfo = udata.grps_head)
2167  {
2168  /* Close the object */
2169  if (H5Oclose(oinfo->oid) < 0)
2170  BAIL2(NC_EHDFERR);
2171 
2172  /* Advance to next node, free current node */
2173  udata.grps_head = oinfo->next;
2174  free(oinfo);
2175  }
2176  }
2177 
2178  return retval;
2179 }
2180 
2181 /* Open a netcdf-4 file. Things have already been kicked off in
2182  * ncfunc.c in nc_open, but here the netCDF-4 part of opening a file
2183  * is handled. */
2184 static int
2185 nc4_open_file(const char *path, int mode, void* parameters, NC *nc)
2186 {
2187  hid_t fapl_id = H5P_DEFAULT;
2188  unsigned flags = (mode & NC_WRITE) ?
2189  H5F_ACC_RDWR : H5F_ACC_RDONLY;
2190  int retval;
2191  NC_HDF5_FILE_INFO_T* nc4_info = NULL;
2192  int inmemory = ((mode & NC_INMEMORY) == NC_INMEMORY);
2193 #ifdef USE_DISKLESS
2194  NC_MEM_INFO* meminfo = (NC_MEM_INFO*)parameters;
2195 #endif
2196 #ifdef USE_PARALLEL
2197  NC_MPI_INFO* mpiinfo = (NC_MPI_INFO*)parameters;
2198  int comm_duped = 0; /* Whether the MPI Communicator was duplicated */
2199  int info_duped = 0; /* Whether the MPI Info object was duplicated */
2200 #endif /* !USE_PARALLEL */
2201 
2202  LOG((3, "%s: path %s mode %d", __func__, path, mode));
2203  assert(path && nc);
2204 
2205  /* Add necessary structs to hold netcdf-4 file data. */
2206  if ((retval = nc4_nc4f_list_add(nc, path, mode)))
2207  BAIL(retval);
2208  nc4_info = NC4_DATA(nc);
2209  assert(nc4_info && nc4_info->root_grp);
2210 
2211  /* Need this access plist to control how HDF5 handles open objects
2212  * on file close. (Setting H5F_CLOSE_SEMI will cause H5Fclose to
2213  * fail if there are any open objects in the file. */
2214  if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0)
2215  BAIL(NC_EHDFERR);
2216 #ifdef EXTRA_TESTS
2217  num_plists++;
2218 #endif
2219 #ifdef EXTRA_TESTS
2220  if (H5Pset_fclose_degree(fapl_id, H5F_CLOSE_SEMI))
2221  BAIL(NC_EHDFERR);
2222 #else
2223  if (H5Pset_fclose_degree(fapl_id, H5F_CLOSE_STRONG))
2224  BAIL(NC_EHDFERR);
2225 #endif
2226 
2227 #ifdef USE_PARALLEL
2228  /* If this is a parallel file create, set up the file creation
2229  property list. */
2230  if (mode & NC_MPIIO || mode & NC_MPIPOSIX)
2231  {
2232  nc4_info->parallel = NC_TRUE;
2233  if (mode & NC_MPIIO) /* MPI/IO */
2234  {
2235  LOG((4, "opening parallel file with MPI/IO"));
2236  if (H5Pset_fapl_mpio(fapl_id, mpiinfo->comm, mpiinfo->info) < 0)
2237  BAIL(NC_EPARINIT);
2238  }
2239 #ifdef USE_PARALLEL_POSIX
2240  else /* MPI/POSIX */
2241  {
2242  LOG((4, "opening parallel file with MPI/posix"));
2243  if (H5Pset_fapl_mpiposix(fapl_id, mpiinfo->comm, 0) < 0)
2244  BAIL(NC_EPARINIT);
2245  }
2246 #else /* USE_PARALLEL_POSIX */
2247  /* Should not happen! Code in NC4_create/NC4_open should alias the
2248  * NC_MPIPOSIX flag to NC_MPIIO, if the MPI-POSIX VFD is not
2249  * available in HDF5. -QAK
2250  */
2251  else /* MPI/POSIX */
2252  BAIL(NC_EPARINIT);
2253 #endif /* USE_PARALLEL_POSIX */
2254 
2255  /* Keep copies of the MPI Comm & Info objects */
2256  if (MPI_SUCCESS != MPI_Comm_dup(mpiinfo->comm, &nc4_info->comm))
2257  BAIL(NC_EMPI);
2258  comm_duped++;
2259  if (MPI_INFO_NULL != mpiinfo->info)
2260  {
2261  if (MPI_SUCCESS != MPI_Info_dup(mpiinfo->info, &nc4_info->info))
2262  BAIL(NC_EMPI);
2263  info_duped++;
2264  }
2265  else
2266  {
2267  /* No dup, just copy it. */
2268  nc4_info->info = mpiinfo->info;
2269  }
2270  }
2271 #else /* only set cache for non-parallel. */
2272  if (H5Pset_cache(fapl_id, 0, nc4_chunk_cache_nelems, nc4_chunk_cache_size,
2273  nc4_chunk_cache_preemption) < 0)
2274  BAIL(NC_EHDFERR);
2275  LOG((4, "%s: set HDF raw chunk cache to size %d nelems %d preemption %f",
2276  __func__, nc4_chunk_cache_size, nc4_chunk_cache_nelems, nc4_chunk_cache_preemption));
2277 #endif /* USE_PARALLEL */
2278 
2279  /* The NetCDF-3.x prototype contains an mode option NC_SHARE for
2280  multiple processes accessing the dataset concurrently. As there
2281  is no HDF5 equivalent, NC_SHARE is treated as NC_NOWRITE. */
2282  if(inmemory) {
2283  if((nc4_info->hdfid = H5LTopen_file_image(meminfo->memory,meminfo->size,
2284  H5LT_FILE_IMAGE_DONT_COPY|H5LT_FILE_IMAGE_DONT_RELEASE
2285  )) < 0)
2286  BAIL(NC_EHDFERR);
2287  nc4_info->no_write = NC_TRUE;
2288  } else if ((nc4_info->hdfid = H5Fopen(path, flags, fapl_id)) < 0)
2289  BAIL(NC_EHDFERR);
2290 
2291  /* Does the mode specify that this file is read-only? */
2292  if ((mode & NC_WRITE) == 0)
2293  nc4_info->no_write = NC_TRUE;
2294 
2295  /* Now read in all the metadata. Some types and dimscale
2296  * information may be difficult to resolve here, if, for example, a
2297  * dataset of user-defined type is encountered before the
2298  * definition of that type. */
2299  if ((retval = nc4_rec_read_metadata(nc4_info->root_grp)))
2300  BAIL(retval);
2301 
2302  /* Now figure out which netCDF dims are indicated by the dimscale
2303  * information. */
2304  if ((retval = nc4_rec_match_dimscales(nc4_info->root_grp)))
2305  BAIL(retval);
2306 
2307 #ifdef LOGGING
2308  /* This will print out the names, types, lens, etc of the vars and
2309  atts in the file, if the logging level is 2 or greater. */
2310  log_metadata_nc(nc);
2311 #endif
2312 
2313  /* Close the property list. */
2314  if (H5Pclose(fapl_id) < 0)
2315  BAIL(NC_EHDFERR);
2316 #ifdef EXTRA_TESTS
2317  num_plists--;
2318 #endif
2319 
2320  return NC_NOERR;
2321 
2322 exit:
2323 #ifdef USE_PARALLEL
2324  if (comm_duped) MPI_Comm_free(&nc4_info->comm);
2325  if (info_duped) MPI_Info_free(&nc4_info->info);
2326 #endif
2327 #ifdef EXTRA_TESTS
2328  num_plists--;
2329 #endif
2330  if (fapl_id != H5P_DEFAULT) H5Pclose(fapl_id);
2331  if (!nc4_info) return retval;
2332  close_netcdf4_file(nc4_info,1); /* treat like abort*/
2333  return retval;
2334 }
2335 
2336 #ifdef USE_HDF4
2337 
2350 static int
2351 get_netcdf_type_from_hdf4(NC_HDF5_FILE_INFO_T *h5, int32 hdf4_typeid,
2352  nc_type *xtype, NC_TYPE_INFO_T *type_info)
2353 {
2354  int t = 0;
2355 
2356  /* Added this variable in the course of fixing NCF-332.
2357  * Prior to the fix, all data types were assigned
2358  * NC_ENDIAN_BIG, so I am preserving that here for now.
2359  * Not sure why it wouldn't be NC_ENDIAN_NATIVE, although
2360  * I can hazard a guess or two.
2361  */
2362 
2363  int endianness = NC_ENDIAN_BIG;
2364  assert(h5 && xtype);
2365 
2366  switch(hdf4_typeid)
2367  {
2368  case DFNT_CHAR:
2369  *xtype = NC_CHAR;
2370  t = 0;
2371  break;
2372  case DFNT_UCHAR:
2373  case DFNT_UINT8:
2374  *xtype = NC_UBYTE;
2375  t = 6;
2376  break;
2377  case DFNT_LUINT8:
2378  *xtype = NC_UBYTE;
2379  t = 6;
2380  endianness = NC_ENDIAN_LITTLE;
2381  break;
2382  case DFNT_INT8:
2383  *xtype = NC_BYTE;
2384  t = 1;
2385  break;
2386  case DFNT_LINT8:
2387  *xtype = NC_BYTE;
2388  t = 1;
2389  endianness = NC_ENDIAN_LITTLE;
2390  break;
2391  case DFNT_INT16:
2392  *xtype = NC_SHORT;
2393  t = 2;
2394  break;
2395  case DFNT_LINT16:
2396  *xtype = NC_SHORT;
2397  t = 2;
2398  endianness = NC_ENDIAN_LITTLE;
2399  break;
2400  case DFNT_UINT16:
2401  *xtype = NC_USHORT;
2402  t = 7;
2403  break;
2404  case DFNT_LUINT16:
2405  *xtype = NC_USHORT;
2406  t = 7;
2407  endianness = NC_ENDIAN_LITTLE;
2408  break;
2409  case DFNT_INT32:
2410  *xtype = NC_INT;
2411  t = 3;
2412  break;
2413  case DFNT_LINT32:
2414  *xtype = NC_INT;
2415  t = 3;
2416  endianness = NC_ENDIAN_LITTLE;
2417  break;
2418  case DFNT_UINT32:
2419  *xtype = NC_UINT;
2420  t = 8;
2421  break;
2422  case DFNT_LUINT32:
2423  *xtype = NC_UINT;
2424  t = 8;
2425  endianness = NC_ENDIAN_LITTLE;
2426  break;
2427  case DFNT_FLOAT32:
2428  *xtype = NC_FLOAT;
2429  t = 4;
2430  break;
2431  case DFNT_LFLOAT32:
2432  *xtype = NC_FLOAT;
2433  t = 4;
2434  endianness = NC_ENDIAN_LITTLE;
2435  break;
2436  case DFNT_FLOAT64:
2437  *xtype = NC_DOUBLE;
2438  t = 5;
2439  break;
2440  case DFNT_LFLOAT64:
2441  *xtype = NC_DOUBLE;
2442  t = 5;
2443  endianness = NC_ENDIAN_LITTLE;
2444  break;
2445  default:
2446  *xtype = NC_NAT;
2447  return NC_EBADTYPID;
2448  }
2449 
2450  if (type_info)
2451  {
2452  if (hdf4_typeid == DFNT_FLOAT32)
2453  type_info->nc_type_class = NC_FLOAT;
2454  else if (hdf4_typeid == DFNT_FLOAT64)
2455  type_info->nc_type_class = NC_DOUBLE;
2456  else if (hdf4_typeid == DFNT_CHAR)
2457  type_info->nc_type_class = NC_STRING;
2458  else
2459  type_info->nc_type_class = NC_INT;
2460  type_info->endianness = endianness;
2461  type_info->nc_typeid = *xtype;
2462  type_info->size = nc_type_size_g[t];
2463  if (!(type_info->name = strdup(nc_type_name_g[t])))
2464  return NC_ENOMEM;
2465  }
2466 
2467  return NC_NOERR;
2468 }
2469 
2470 /* Open a HDF4 file. Things have already been kicked off in nc_open,
2471  * but here the netCDF-4 part of opening a file is handled. */
2472 static int
2473 nc4_open_hdf4_file(const char *path, int mode, NC *nc)
2474 {
2475  NC_HDF5_FILE_INFO_T *h5;
2476  NC_GRP_INFO_T *grp;
2477  NC_ATT_INFO_T *att;
2478  int32 num_datasets, num_gatts;
2479  int32 rank;
2480  int v, d, a;
2481  int retval;
2482  NC_HDF5_FILE_INFO_T* nc4_info = NULL;
2483 
2484  LOG((3, "%s: path %s mode %d", __func__, path, mode));
2485  assert(path && nc);
2486 
2487  /* Must be read-only access to hdf4 files. */
2488  if (mode & NC_WRITE)
2489  return NC_EINVAL;
2490 
2491  /* Add necessary structs to hold netcdf-4 file data. */
2492  if ((retval = nc4_nc4f_list_add(nc, path, mode)))
2493  return retval;
2494  nc4_info = NC4_DATA(nc);
2495  assert(nc4_info && nc4_info->root_grp);
2496  h5 = nc4_info;
2497  h5->hdf4 = NC_TRUE;
2498  grp = h5->root_grp;
2499  h5->no_write = NC_TRUE;
2500 
2501  /* Open the file and initialize SD interface. */
2502  if ((h5->sdid = SDstart(path, DFACC_READ)) == FAIL)
2503  return NC_EHDFERR;
2504 
2505  /* Learn how many datasets and global atts we have. */
2506  if (SDfileinfo(h5->sdid, &num_datasets, &num_gatts))
2507  return NC_EHDFERR;
2508 
2509  /* Read the atts. */
2510  for (a = 0; a < num_gatts; a++)
2511  {
2512  int32 att_data_type, att_count;
2513  size_t att_type_size;
2514 
2515  /* Add to the end of the list of atts for this var. */
2516  if ((retval = nc4_att_list_add(&h5->root_grp->att, &att)))
2517  return retval;
2518  att->attnum = grp->natts++;
2519  att->created = NC_TRUE;
2520 
2521  /* Learn about this attribute. */
2522  if (!(att->name = malloc(NC_MAX_HDF4_NAME * sizeof(char))))
2523  return NC_ENOMEM;
2524  if (SDattrinfo(h5->sdid, a, att->name, &att_data_type, &att_count))
2525  return NC_EATTMETA;
2526  if ((retval = get_netcdf_type_from_hdf4(h5, att_data_type,
2527  &att->nc_typeid, NULL)))
2528  return retval;
2529  att->len = att_count;
2530 
2531  /* Allocate memory to hold the data. */
2532  if ((retval = nc4_get_typelen_mem(h5, att->nc_typeid, 0, &att_type_size)))
2533  return retval;
2534  if (!(att->data = malloc(att_type_size * att->len)))
2535  return NC_ENOMEM;
2536 
2537  /* Read the data. */
2538  if (SDreadattr(h5->sdid, a, att->data))
2539  return NC_EHDFERR;
2540  }
2541 
2542  /* Read each dataset. */
2543  for (v = 0; v < num_datasets; v++)
2544  {
2545  NC_VAR_INFO_T *var;
2546  int32 data_type, num_atts;
2547  /* Problem: Number of dims is returned by the call that requires
2548  a pre-allocated array, 'dimsize'.
2549  From SDS_SD website:
2550  http://www.hdfgroup.org/training/HDFtraining/UsersGuide/SDS_SD.fm3.html
2551  The maximum rank is 32, or MAX_VAR_DIMS (as defined in netcdf.h).
2552 
2553  int32 dimsize[MAX_VAR_DIMS];
2554  */
2555  int32 *dimsize = NULL;
2556  size_t var_type_size;
2557  int a;
2558 
2559  /* Add a variable to the end of the group's var list. */
2560  if ((retval = nc4_var_list_add(&grp->var, &var)))
2561  return retval;
2562 
2563  var->varid = grp->nvars++;
2564  var->created = NC_TRUE;
2565  var->written_to = NC_TRUE;
2566 
2567  /* Open this dataset in HDF4 file. */
2568  if ((var->sdsid = SDselect(h5->sdid, v)) == FAIL)
2569  return NC_EVARMETA;
2570 
2571  /* Get shape, name, type, and attribute info about this dataset. */
2572  if (!(var->name = malloc(NC_MAX_HDF4_NAME + 1)))
2573  return NC_ENOMEM;
2574 
2575  /* Invoke SDgetInfo with null dimsize to get rank. */
2576  if (SDgetinfo(var->sdsid, var->name, &rank, NULL, &data_type, &num_atts))
2577  return NC_EVARMETA;
2578 
2579  if(!(dimsize = (int32*)malloc(sizeof(int32)*rank)))
2580  return NC_ENOMEM;
2581 
2582  if (SDgetinfo(var->sdsid, var->name, &rank, dimsize, &data_type, &num_atts)) {
2583  if(dimsize) free(dimsize);
2584  return NC_EVARMETA;
2585  }
2586 
2587  var->ndims = rank;
2588  var->hdf4_data_type = data_type;
2589 
2590  /* Fill special type_info struct for variable type information. */
2591  if (!(var->type_info = calloc(1, sizeof(NC_TYPE_INFO_T)))) {
2592  if(dimsize) free(dimsize);
2593  return NC_ENOMEM;
2594  }
2595 
2596  if ((retval = get_netcdf_type_from_hdf4(h5, data_type, &var->type_info->nc_typeid, var->type_info))) {
2597  if(dimsize) free(dimsize);
2598  return retval;
2599  }
2600 
2601  /* Indicate that the variable has a pointer to the type */
2602  var->type_info->rc++;
2603 
2604  if ((retval = nc4_get_typelen_mem(h5, var->type_info->nc_typeid, 0, &var_type_size))) {
2605  if(dimsize) free(dimsize);
2606  return retval;
2607  }
2608 
2609  var->type_info->size = var_type_size;
2610  LOG((3, "reading HDF4 dataset %s, rank %d netCDF type %d", var->name,
2611  rank, var->type_info->nc_typeid));
2612 
2613  /* Get the fill value. */
2614  if (!(var->fill_value = malloc(var_type_size))) {
2615  if(dimsize) free(dimsize);
2616  return NC_ENOMEM;
2617  }
2618 
2619  if (SDgetfillvalue(var->sdsid, var->fill_value))
2620  {
2621  /* Whoops! No fill value! */
2622  free(var->fill_value);
2623  var->fill_value = NULL;
2624  }
2625 
2626  /* Allocate storage for dimension info in this variable. */
2627  if (var->ndims)
2628  {
2629  if (!(var->dim = malloc(sizeof(NC_DIM_INFO_T *) * var->ndims))) {
2630  if(dimsize) free(dimsize);
2631  return NC_ENOMEM;
2632  }
2633 
2634  if (!(var->dimids = malloc(sizeof(int) * var->ndims))) {
2635  if(dimsize) free(dimsize);
2636  return NC_ENOMEM;
2637  }
2638  }
2639 
2640 
2641  /* Find its dimensions. */
2642  for (d = 0; d < var->ndims; d++)
2643  {
2644  int32 dimid, dim_len, dim_data_type, dim_num_attrs;
2645  char dim_name[NC_MAX_NAME + 1];
2646  NC_DIM_INFO_T *dim;
2647 
2648  if ((dimid = SDgetdimid(var->sdsid, d)) == FAIL) {
2649  if(dimsize) free(dimsize);
2650  return NC_EDIMMETA;
2651  }
2652  if (SDdiminfo(dimid, dim_name, &dim_len, &dim_data_type,
2653  &dim_num_attrs))
2654  {
2655  if(dimsize) free(dimsize);
2656  return NC_EDIMMETA;
2657  }
2658 
2659  /* Do we already have this dimension? HDF4 explicitly uses
2660  * the name to tell. */
2661  for (dim = grp->dim; dim; dim = dim->l.next)
2662  if (!strcmp(dim->name, dim_name))
2663  break;
2664 
2665  /* If we didn't find this dimension, add one. */
2666  if (!dim)
2667  {
2668  LOG((4, "adding dimension %s for HDF4 dataset %s",
2669  dim_name, var->name));
2670  if ((retval = nc4_dim_list_add(&grp->dim, &dim)))
2671  return retval;
2672  grp->ndims++;
2673  dim->dimid = grp->nc4_info->next_dimid++;
2674  if (strlen(dim_name) > NC_MAX_HDF4_NAME)
2675  return NC_EMAXNAME;
2676  if (!(dim->name = strdup(dim_name)))
2677  return NC_ENOMEM;
2678  if (dim_len)
2679  dim->len = dim_len;
2680  else
2681  dim->len = *dimsize;
2682  }
2683 
2684  /* Tell the variable the id of this dimension. */
2685  var->dimids[d] = dim->dimid;
2686  }
2687 
2688  /* Read the atts. */
2689  for (a = 0; a < num_atts; a++)
2690  {
2691  int32 att_data_type, att_count;
2692  size_t att_type_size;
2693 
2694  /* Add to the end of the list of atts for this var. */
2695  if ((retval = nc4_att_list_add(&var->att, &att))) {
2696  if(dimsize) free(dimsize);
2697  return retval;
2698  }
2699  att->attnum = var->natts++;
2700  att->created = NC_TRUE;
2701 
2702  /* Learn about this attribute. */
2703  if (!(att->name = malloc(NC_MAX_HDF4_NAME * sizeof(char)))) {
2704  if(dimsize) free(dimsize);
2705  return NC_ENOMEM;
2706  }
2707  if (SDattrinfo(var->sdsid, a, att->name, &att_data_type, &att_count)) {
2708  if(dimsize) free(dimsize);
2709  return NC_EATTMETA;
2710  }
2711  if ((retval = get_netcdf_type_from_hdf4(h5, att_data_type,
2712  &att->nc_typeid, NULL))) {
2713  if(dimsize) free(dimsize);
2714  return retval;
2715  }
2716 
2717  att->len = att_count;
2718 
2719  /* Allocate memory to hold the data. */
2720  if ((retval = nc4_get_typelen_mem(h5, att->nc_typeid, 0, &att_type_size))) {
2721  if(dimsize) free(dimsize);
2722  return retval;
2723  }
2724  if (!(att->data = malloc(att_type_size * att->len))) {
2725  if(dimsize) free(dimsize);
2726  return NC_ENOMEM;
2727  }
2728 
2729  /* Read the data. */
2730  if (SDreadattr(var->sdsid, a, att->data)) {
2731  if(dimsize) free(dimsize);
2732  return NC_EHDFERR;
2733  }
2734  }
2735  if(dimsize) free(dimsize);
2736 
2737  {
2738  /* HDF4 files can be chunked */
2739  HDF_CHUNK_DEF chunkdefs;
2740  int flag;
2741  if(!SDgetchunkinfo(var->sdsid, &chunkdefs, &flag)) {
2742  if(flag == HDF_NONE)
2743  var->contiguous = NC_TRUE;
2744  else if((flag & HDF_CHUNK) != 0) {
2745  var->contiguous = NC_FALSE;
2746  if (!(var->chunksizes = malloc(var->ndims * sizeof(size_t))))
2747  return NC_ENOMEM;
2748  for (d = 0; d < var->ndims; d++) {
2749  var->chunksizes[d] = chunkdefs.chunk_lengths[d];
2750  }
2751  }
2752  }
2753  }
2754 
2755  } /* next var */
2756 
2757 #ifdef LOGGING
2758  /* This will print out the names, types, lens, etc of the vars and
2759  atts in the file, if the logging level is 2 or greater. */
2760  log_metadata_nc(h5->root_grp->nc4_info->controller);
2761 #endif
2762  return NC_NOERR;
2763  return NC_ENOTBUILT;
2764 }
2765 #endif /* USE_HDF4 */
2766 
2767 int
2768 NC4_open(const char *path, int mode, int basepe, size_t *chunksizehintp,
2769  int use_parallel, void *parameters, NC_Dispatch *dispatch, NC *nc_file)
2770 {
2771  int res;
2772  int hdf_file = 0;
2773 #ifdef USE_PARALLEL
2774  NC_MPI_INFO mpidfalt = {MPI_COMM_WORLD, MPI_INFO_NULL};
2775 #endif
2776 #if defined USE_PARALLEL || defined USE_HDF4
2777  int inmemory = ((mode & NC_INMEMORY) == NC_INMEMORY);
2778 #endif
2779 
2780  assert(nc_file && path);
2781 
2782  LOG((1, "%s: path %s mode %d params %x",
2783  __func__, path, mode, parameters));
2784 
2785 #ifdef USE_PARALLEL
2786  if (!inmemory && use_parallel && parameters == NULL)
2787  parameters = &mpidfalt;
2788 #endif /* USE_PARALLEL */
2789 
2790  /* If this is our first file, turn off HDF5 error messages. */
2791  if (virgin)
2792  {
2793  if (H5Eset_auto(NULL, NULL) < 0)
2794  LOG((0, "Couldn't turn off HDF5 error messages!"));
2795  LOG((1, "HDF5 error messages turned off!"));
2796  virgin = 0;
2797  }
2798 
2799 
2800  /* Check the mode for validity */
2801  if((mode & ILLEGAL_OPEN_FLAGS) != 0)
2802  return NC_EINVAL;
2803 
2804  /* Cannot have both */
2805  if((mode & (NC_MPIIO|NC_MPIPOSIX)) == (NC_MPIIO|NC_MPIPOSIX))
2806  return NC_EINVAL;
2807 
2808 #ifndef USE_PARALLEL_POSIX
2809 /* If the HDF5 library has been compiled without the MPI-POSIX VFD, alias
2810  * the NC_MPIPOSIX flag to NC_MPIIO. -QAK
2811  */
2812  if(mode & NC_MPIPOSIX)
2813  {
2814  mode &= ~NC_MPIPOSIX;
2815  mode |= NC_MPIIO;
2816  }
2817 #endif /* USE_PARALLEL_POSIX */
2818 
2819  /* Figure out if this is a hdf4 or hdf5 file. */
2820  if ((res = nc_check_for_hdf(path, use_parallel, parameters, &hdf_file)))
2821  return res;
2822 
2823  /* Depending on the type of file, open it. */
2824  nc_file->int_ncid = nc_file->ext_ncid;
2825  if (hdf_file == NC_HDF5_FILE)
2826  res = nc4_open_file(path, mode, parameters, nc_file);
2827 #ifdef USE_HDF4
2828  else if (hdf_file == NC_HDF4_FILE && inmemory)
2829  return NC_EDISKLESS;
2830  else if (hdf_file == NC_HDF4_FILE)
2831  res = nc4_open_hdf4_file(path, mode, nc_file);
2832 #endif /* USE_HDF4 */
2833  else
2834  assert(0); /* should never happen */
2835 
2836  return res;
2837 }
2838 
2839 /* Unfortunately HDF only allows specification of fill value only when
2840  a dataset is created. Whereas in netcdf, you first create the
2841  variable and then (optionally) specify the fill value. To
2842  accomplish this in HDF5 I have to delete the dataset, and recreate
2843  it, with the fill value specified. */
2844 /* QAK: This looks completely unused in the code. (?) */
2845 int
2846 NC4_set_fill(int ncid, int fillmode, int *old_modep)
2847 {
2848  NC *nc;
2849  NC_HDF5_FILE_INFO_T* nc4_info;
2850 
2851  LOG((2, "%s: ncid 0x%x fillmode %d", __func__, ncid, fillmode));
2852 
2853  if (!(nc = nc4_find_nc_file(ncid,&nc4_info)))
2854  return NC_EBADID;
2855  assert(nc4_info);
2856 
2857  /* Trying to set fill on a read-only file? You sicken me! */
2858  if (nc4_info->no_write)
2859  return NC_EPERM;
2860 
2861  /* Did you pass me some weird fillmode? */
2862  if (fillmode != NC_FILL && fillmode != NC_NOFILL)
2863  return NC_EINVAL;
2864 
2865  /* If the user wants to know, tell him what the old mode was. */
2866  if (old_modep)
2867  *old_modep = nc4_info->fill_mode;
2868 
2869  nc4_info->fill_mode = fillmode;
2870 
2871 #if 0 /*def USE_PNETCDF*/
2872  /* Take care of files created/opened with parallel-netcdf library. */
2873  if (nc4_info->pnetcdf_file)
2874  return ncmpi_set_fill(nc->int_ncid, fillmode, old_modep);
2875 #endif /* USE_PNETCDF */
2876 
2877 
2878  return NC_NOERR;
2879 }
2880 
2881 /* Put the file back in redef mode. This is done automatically for
2882  * netcdf-4 files, if the user forgets. */
2883 int
2884 NC4_redef(int ncid)
2885 {
2886  NC_HDF5_FILE_INFO_T* nc4_info;
2887 
2888  LOG((1, "%s: ncid 0x%x", __func__, ncid));
2889 
2890  /* Find this file's metadata. */
2891  if (!(nc4_find_nc_file(ncid,&nc4_info)))
2892  return NC_EBADID;
2893  assert(nc4_info);
2894 
2895 #if 0 /*def USE_PNETCDF*/
2896  /* Take care of files created/opened with parallel-netcdf library. */
2897  if (nc4_info->pnetcdf_file)
2898  return ncmpi_redef(nc->int_ncid);
2899 #endif /* USE_PNETCDF */
2900 
2901  /* If we're already in define mode, return an error. */
2902  if (nc4_info->flags & NC_INDEF)
2903  return NC_EINDEFINE;
2904 
2905  /* If the file is read-only, return an error. */
2906  if (nc4_info->no_write)
2907  return NC_EPERM;
2908 
2909  /* Set define mode. */
2910  nc4_info->flags |= NC_INDEF;
2911 
2912  /* For nc_abort, we need to remember if we're in define mode as a
2913  redef. */
2914  nc4_info->redef = NC_TRUE;
2915 
2916  return NC_NOERR;
2917 }
2918 
2919 /* For netcdf-4 files, this just calls nc_enddef, ignoring the extra
2920  * parameters. */
2921 int
2922 NC4__enddef(int ncid, size_t h_minfree, size_t v_align,
2923  size_t v_minfree, size_t r_align)
2924 {
2925  if (nc4_find_nc_file(ncid,NULL) == NULL)
2926  return NC_EBADID;
2927 
2928  return NC4_enddef(ncid);
2929 }
2930 
2931 /* Take the file out of define mode. This is called automatically for
2932  * netcdf-4 files, if the user forgets. */
2933 static int NC4_enddef(int ncid)
2934 {
2935  NC *nc;
2936  NC_HDF5_FILE_INFO_T* nc4_info;
2937 
2938  LOG((1, "%s: ncid 0x%x", __func__, ncid));
2939 
2940  if (!(nc = nc4_find_nc_file(ncid,&nc4_info)))
2941  return NC_EBADID;
2942  assert(nc4_info);
2943 
2944 #if 0 /*def USE_PNETCDF*/
2945  if (nc4_info->pnetcdf_file)
2946  {
2947  int res;
2948  res = ncmpi_enddef(nc->int_ncid);
2949  if (!res)
2950  {
2951  if (nc4_info->pnetcdf_access_mode == NC_INDEPENDENT)
2952  res = ncmpi_begin_indep_data(nc->int_ncid);
2953  }
2954  return res;
2955  }
2956 #endif /* USE_PNETCDF */
2957 
2958  return nc4_enddef_netcdf4_file(nc4_info);
2959 }
2960 
2961 /* This function will write all changed metadata, and (someday) reread
2962  * all metadata from the file. */
2963 static int
2964 sync_netcdf4_file(NC_HDF5_FILE_INFO_T *h5)
2965 {
2966  int retval;
2967 
2968  assert(h5);
2969  LOG((3, "%s", __func__));
2970 
2971  /* If we're in define mode, that's an error, for strict nc3 rules,
2972  * otherwise, end define mode. */
2973  if (h5->flags & NC_INDEF)
2974  {
2975  if (h5->cmode & NC_CLASSIC_MODEL)
2976  return NC_EINDEFINE;
2977 
2978  /* Turn define mode off. */
2979  h5->flags ^= NC_INDEF;
2980 
2981  /* Redef mode needs to be tracked seperately for nc_abort. */
2982  h5->redef = NC_FALSE;
2983  }
2984 
2985 #ifdef LOGGING
2986  /* This will print out the names, types, lens, etc of the vars and
2987  atts in the file, if the logging level is 2 or greater. */
2988  log_metadata_nc(h5->root_grp->nc4_info->controller);
2989 #endif
2990 
2991  /* Write any metadata that has changed. */
2992  if (!(h5->cmode & NC_NOWRITE))
2993  {
2994  nc_bool_t bad_coord_order = NC_FALSE; /* if detected, propagate to all groups to consistently store dimids */
2995 
2996  if ((retval = nc4_rec_write_groups_types(h5->root_grp)))
2997  return retval;
2998  if ((retval = nc4_rec_detect_need_to_preserve_dimids(h5->root_grp, &bad_coord_order)))
2999  return retval;
3000  if ((retval = nc4_rec_write_metadata(h5->root_grp, bad_coord_order)))
3001  return retval;
3002  }
3003 
3004  if (H5Fflush(h5->hdfid, H5F_SCOPE_GLOBAL) < 0)
3005  return NC_EHDFERR;
3006 
3007  return retval;
3008 }
3009 
3010 /* Flushes all buffers associated with the file, after writing all
3011  changed metadata. This may only be called in data mode. */
3012 int
3013 NC4_sync(int ncid)
3014 {
3015  NC *nc;
3016  int retval;
3017  NC_HDF5_FILE_INFO_T* nc4_info;
3018 
3019  LOG((2, "%s: ncid 0x%x", __func__, ncid));
3020 
3021  if (!(nc = nc4_find_nc_file(ncid,&nc4_info)))
3022  return NC_EBADID;
3023  assert(nc4_info);
3024 
3025 #if 0 /*def USE_PNETCDF*/
3026  /* Take care of files created/opened with parallel-netcdf library. */
3027  if (nc4_info->pnetcdf_file)
3028  return ncmpi_sync(nc->int_ncid);
3029 #endif /* USE_PNETCDF */
3030 
3031  /* If we're in define mode, we can't sync. */
3032  if (nc4_info && nc4_info->flags & NC_INDEF)
3033  {
3034  if (nc4_info->cmode & NC_CLASSIC_MODEL)
3035  return NC_EINDEFINE;
3036  if ((retval = NC4_enddef(ncid)))
3037  return retval;
3038  }
3039 
3040  return sync_netcdf4_file(nc4_info);
3041 }
3042 
3043 /* This function will free all allocated metadata memory, and close
3044  the HDF5 file. The group that is passed in must be the root group
3045  of the file. */
3046 static int
3047 close_netcdf4_file(NC_HDF5_FILE_INFO_T *h5, int abort)
3048 {
3049  int retval = NC_NOERR;
3050 
3051  assert(h5 && h5->root_grp);
3052  LOG((3, "%s: h5->path %s abort %d", __func__, h5->controller->path, abort));
3053 
3054  /* According to the docs, always end define mode on close. */
3055  if (h5->flags & NC_INDEF)
3056  h5->flags ^= NC_INDEF;
3057 
3058  /* Sync the file, unless we're aborting, or this is a read-only
3059  * file. */
3060  if (!h5->no_write && !abort)
3061  if ((retval = sync_netcdf4_file(h5)))
3062  goto exit;
3063 
3064  /* Delete all the list contents for vars, dims, and atts, in each
3065  * group. */
3066  if ((retval = nc4_rec_grp_del(&h5->root_grp, h5->root_grp)))
3067  goto exit;
3068 
3069  /* Close hdf file. */
3070 #ifdef USE_HDF4
3071  if (h5->hdf4)
3072  {
3073  if (SDend(h5->sdid))
3074  BAIL_QUIET(NC_EHDFERR);
3075  }
3076  else
3077 #endif /* USE_HDF4 */
3078  {
3079 #ifdef USE_PARALLEL
3080  /* Free the MPI Comm & Info objects, if we opened the file in parallel */
3081  if(h5->parallel)
3082  {
3083  MPI_Comm_free(&h5->comm);
3084  if(MPI_INFO_NULL != h5->info)
3085  MPI_Info_free(&h5->info);
3086  }
3087 #endif
3088  if (H5Fclose(h5->hdfid) < 0)
3089  {
3090  int nobjs;
3091 
3092  nobjs = H5Fget_obj_count(h5->hdfid, H5F_OBJ_ALL);
3093  /* Apparently we can get an error even when nobjs == 0 */
3094  if(nobjs < 0) {
3095  BAIL_QUIET(NC_EHDFERR);
3096  } else if(nobjs > 0) {
3097 #ifdef LOGGING
3098  /* If the close doesn't work, probably there are still some HDF5
3099  * objects open, which means there's a bug in the library. So
3100  * print out some info on to help the poor programmer figure it
3101  * out. */
3102  LOG((0, "There are %d HDF5 objects open!", nobjs));
3103 #endif
3104  BAIL_QUIET(NC_EHDFERR);
3105  }
3106  }
3107  }
3108 
3109 exit:
3110  /* Free the nc4_info struct; above code should have reclaimed
3111  everything else */
3112  if(h5 != NULL)
3113  free(h5);
3114 
3115  return retval;
3116 }
3117 
3118 /* From the netcdf-3 docs: The function nc_abort just closes the
3119  netCDF dataset, if not in define mode. If the dataset is being
3120  created and is still in define mode, the dataset is deleted. If
3121  define mode was entered by a call to nc_redef, the netCDF dataset
3122  is restored to its state before definition mode was entered and the
3123  dataset is closed. */
3124 int
3125 NC4_abort(int ncid)
3126 {
3127  NC *nc;
3128  int delete_file = 0;
3129  char path[NC_MAX_NAME + 1];
3130  int retval = NC_NOERR;
3131  NC_HDF5_FILE_INFO_T* nc4_info;
3132 
3133  LOG((2, "%s: ncid 0x%x", __func__, ncid));
3134 
3135  /* Find metadata for this file. */
3136  if (!(nc = nc4_find_nc_file(ncid,&nc4_info)))
3137  return NC_EBADID;
3138 
3139  assert(nc4_info);
3140 
3141 #if 0 /*def USE_PNETCDF*/
3142  /* Take care of files created/opened with parallel-netcdf library. */
3143  if (nc4_info->pnetcdf_file)
3144  return ncmpi_abort(nc->int_ncid);
3145 #endif /* USE_PNETCDF */
3146 
3147  /* If we're in define mode, but not redefing the file, delete it. */
3148  if (nc4_info->flags & NC_INDEF && !nc4_info->redef)
3149  {
3150  delete_file++;
3151  strncpy(path, nc->path,NC_MAX_NAME);
3152  }
3153 
3154  /* Free any resources the netcdf-4 library has for this file's
3155  * metadata. */
3156  if ((retval = close_netcdf4_file(nc4_info, 1)))
3157  return retval;
3158 
3159  /* Delete the file, if we should. */
3160  if (delete_file)
3161  if (remove(path) < 0)
3162  return NC_ECANTREMOVE;
3163 
3164  return retval;
3165 }
3166 
3167 /* Close the netcdf file, writing any changes first. */
3168 int
3169 NC4_close(int ncid)
3170 {
3171  NC_GRP_INFO_T *grp;
3172  NC *nc;
3173  NC_HDF5_FILE_INFO_T *h5;
3174  int retval;
3175 
3176  LOG((1, "%s: ncid 0x%x", __func__, ncid));
3177 
3178  /* Find our metadata for this file. */
3179  if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
3180  return retval;
3181 
3182  assert(nc && h5 && grp);
3183 
3184  /* This must be the root group. */
3185  if (grp->parent)
3186  return NC_EBADGRPID;
3187 
3188 #if 0 /*def USE_PNETCDF*/
3189  /* Take care of files created/opened with parallel-netcdf library. */
3190  if (h5->pnetcdf_file)
3191  return ncmpi_close(nc->int_ncid);
3192 #endif /* USE_PNETCDF */
3193 
3194  /* Call the nc4 close. */
3195  if ((retval = close_netcdf4_file(grp->nc4_info, 0)))
3196  return retval;
3197 
3198  return NC_NOERR;
3199 }
3200 
3201 /* It's possible for any of these pointers to be NULL, in which case
3202  don't try to figure out that value. */
3203 int
3204 NC4_inq(int ncid, int *ndimsp, int *nvarsp, int *nattsp, int *unlimdimidp)
3205 {
3206  NC *nc;
3207  NC_HDF5_FILE_INFO_T *h5;
3208  NC_GRP_INFO_T *grp;
3209  NC_DIM_INFO_T *dim;
3210  NC_ATT_INFO_T *att;
3211  NC_VAR_INFO_T *var;
3212  int retval;
3213 
3214  LOG((2, "%s: ncid 0x%x", __func__, ncid));
3215 
3216  /* Find file metadata. */
3217  if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
3218  return retval;
3219 
3220  assert(h5 && grp && nc);
3221 
3222 #if 0 /*def USE_PNETCDF*/
3223  /* Take care of files created/opened with parallel-netcdf library. */
3224  if (h5->pnetcdf_file)
3225  return ncmpi_inq(nc->int_ncid, ndimsp, nvarsp, nattsp, unlimdimidp);
3226 #endif /* USE_PNETCDF */
3227 
3228  /* Count the number of dims, vars, and global atts. */
3229  if (ndimsp)
3230  {
3231  *ndimsp = 0;
3232  for (dim = grp->dim; dim; dim = dim->l.next)
3233  (*ndimsp)++;
3234  }
3235  if (nvarsp)
3236  {
3237  *nvarsp = 0;
3238  for (var = grp->var; var; var= var->l.next)
3239  (*nvarsp)++;
3240  }
3241  if (nattsp)
3242  {
3243  *nattsp = 0;
3244  for (att = grp->att; att; att = att->l.next)
3245  (*nattsp)++;
3246  }
3247 
3248  if (unlimdimidp)
3249  {
3250  /* Default, no unlimited dimension */
3251  *unlimdimidp = -1;
3252 
3253  /* If there's more than one unlimited dim, which was not possible
3254  with netcdf-3, then only the last unlimited one will be reported
3255  back in xtendimp. */
3256  /* Note that this code is inconsistent with nc_inq_unlimid() */
3257  for (dim = grp->dim; dim; dim = dim->l.next)
3258  if (dim->unlimited)
3259  {
3260  *unlimdimidp = dim->dimid;
3261  break;
3262  }
3263  }
3264 
3265  return NC_NOERR;
3266 }
3267 
3268 #if 0
3269 int
3270 NC4_set_content(int ncid, size_t size, void* memory)
3271 {
3272  int retval = NC_NOERR;
3273  herr_t herr;
3274  NC *nc;
3275  NC_HDF5_FILE_INFO_T *h5;
3276  NC_GRP_INFO_T *grp;
3277 
3278  LOG((4,"%s: ncid 0x%x size %ld memory 0x%x", __func__, ncid, size, memory));
3279 
3280  /* Find file metadata. */
3281  if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5)))
3282  return retval;
3283  assert(h5 && grp && nc);
3284 
3285 #ifdef USE_DISKLESS
3286  herr = H5Pset_file_image(h5->hdfid,memory,size);
3287  if(herr)
3288  BAIL(NC_EHDFERR);
3289 #else
3290  retval = NC_EDISKLESS;
3291 #endif
3292 
3293 done:
3294  return retval;
3295 }
3296 #endif
3297 
3298 /* This function will do the enddef stuff for a netcdf-4 file. */
3299 int
3300 nc4_enddef_netcdf4_file(NC_HDF5_FILE_INFO_T *h5)
3301 {
3302  assert(h5);
3303  LOG((3, "%s", __func__));
3304 
3305  /* If we're not in define mode, return an error. */
3306  if (!(h5->flags & NC_INDEF))
3307  return NC_ENOTINDEFINE;
3308 
3309  /* Turn define mode off. */
3310  h5->flags ^= NC_INDEF;
3311 
3312  /* Redef mode needs to be tracked seperately for nc_abort. */
3313  h5->redef = NC_FALSE;
3314 
3315  return sync_netcdf4_file(h5);
3316 }
3317 
3318 #ifdef EXTRA_TESTS
3319 int
3320 nc_exit()
3321 {
3322  if (num_plists || num_spaces)
3323  return NC_EHDFERR;
3324 
3325  return NC_NOERR;
3326 }
3327 #endif /* EXTRA_TESTS */
3328 
3329 #ifdef USE_PARALLEL
3330 int
3331 nc_use_parallel_enabled()
3332 {
3333  return 0;
3334 }
3335 #endif /* USE_PARALLEL */
#define NC_PNETCDF
Use parallel-netcdf library.
Definition: netcdf.h:160
#define NC_ENOMEM
Memory allocation (malloc) failure.
Definition: netcdf.h:357
#define NC_CHAR
ISO/ASCII character.
Definition: netcdf.h:39
#define NC_ECANTWRITE
Can't write.
Definition: netcdf.h:390
int NC4_create(const char *path, int cmode, size_t initialsz, int basepe, size_t *chunksizehintp, int use_parallel, void *parameters, NC_Dispatch *dispatch, NC *nc_file)
Create a netCDF-4/HDF5 file.
Definition: nc4file.c:472
#define NC_UBYTE
unsigned 1 byte int
Definition: netcdf.h:45
#define NC_CLASSIC_MODEL
Enforce classic model.
Definition: netcdf.h:139
#define NC_MAX_VAR_DIMS
max per variable dimensions
Definition: netcdf.h:236
#define NC_UINT
unsigned 4-byte int
Definition: netcdf.h:47
#define NC_NOCLOBBER
Don't destroy existing file.
Definition: netcdf.h:133
#define NC_INMEMORY
Read from memory.
Definition: netcdf.h:137
#define NC_EHDFERR
Error at HDF5 layer.
Definition: netcdf.h:388
#define NC_OPAQUE
opaque types
Definition: netcdf.h:57
#define NC_MPIIO
Turn on MPI I/O.
Definition: netcdf.h:156
#define NC_INT64
signed 8-byte int
Definition: netcdf.h:48
#define NC_STRING
string
Definition: netcdf.h:50
#define NC_ENOTINDEFINE
Operation not allowed in data mode.
Definition: netcdf.h:298
#define NC_DOUBLE
double precision floating point number
Definition: netcdf.h:44
#define NC_EBADCLASS
Bad class.
Definition: netcdf.h:409
int nc_type
The nc_type type is just an int.
Definition: netcdf.h:28
#define NC_64BIT_OFFSET
Use large (64-bit) file offsets.
Definition: netcdf.h:140
#define NC_NOWRITE
Set read-only access for nc_open().
Definition: netcdf.h:130
#define NC_BYTE
signed 1 byte integer
Definition: netcdf.h:38
#define NC_EINDEFINE
Operation not allowed in define mode.
Definition: netcdf.h:307
#define NC_ENOTNC
Not a netcdf file.
Definition: netcdf.h:333
#define NC_ENOTBUILT
Attempt to use feature that was not turned on when netCDF was built.
Definition: netcdf.h:417
#define NC_ENDIAN_LITTLE
In HDF5 files you can set the endianness of variables with nc_def_var_endian().
Definition: netcdf.h:246
#define NC_EATTMETA
Problem with attribute metadata.
Definition: netcdf.h:394
#define NC_VLEN
vlen (variable-length) types
Definition: netcdf.h:56
#define NC_EMPI
MPI operation failed.
Definition: netcdf.h:420
#define NC_EDISKLESS
Error in using diskless access.
Definition: netcdf.h:418
#define NC_EFILEMETA
Problem with file metadata.
Definition: netcdf.h:392
#define NC_FORMAT_64BIT
Format specifier for nc_set_default_format() and returned by nc_inq_format.
Definition: netcdf.h:170
This is the type of arrays of vlens.
Definition: netcdf.h:628
#define NC_EBADTYPE
Not a netcdf data type.
Definition: netcdf.h:319
#define NC_EBADNAME
Attribute or variable name contains illegal characters.
Definition: netcdf.h:349
#define NC_EDIMMETA
Problem with dimension metadata.
Definition: netcdf.h:393
#define NC_EINVAL
Invalid Argument.
Definition: netcdf.h:292
#define NC_INT
signed 4 byte integer
Definition: netcdf.h:41
#define NC_EBADGRPID
Bad group ID.
Definition: netcdf.h:405
#define NC_NOFILL
Argument to nc_set_fill() to turn off filling of data.
Definition: netcdf.h:120
#define NC_ENDIAN_BIG
In HDF5 files you can set the endianness of variables with nc_def_var_endian().
Definition: netcdf.h:247
#define NC_MAX_NAME
Maximum for classic library.
Definition: netcdf.h:235
#define NC_ECANTREMOVE
Can't remove file.
Definition: netcdf.h:380
#define NC_NAT
Not A Type.
Definition: netcdf.h:37
#define NC_EBADTYPID
Bad type ID.
Definition: netcdf.h:406
User data struct for call to H5Literate() in nc4_rec_read_metadata()
Definition: nc4file.c:72
#define NC_USHORT
unsigned 2-byte int
Definition: netcdf.h:46
#define NC_EPARINIT
Error initializing for parallel access.
Definition: netcdf.h:404
#define NC_NETCDF4
Use netCDF-4/HDF5 format.
Definition: netcdf.h:152
#define NC_EEXIST
netcdf file exists && NC_NOCLOBBER
Definition: netcdf.h:291
#define NC_FORMAT_NETCDF4_CLASSIC
Format specifier for nc_set_default_format() and returned by nc_inq_format.
Definition: netcdf.h:172
#define NC_EBADID
Not a netcdf id.
Definition: netcdf.h:289
#define NC_EVARMETA
Problem with variable metadata.
Definition: netcdf.h:395
struct NC4_rec_read_metadata_ud NC4_rec_read_metadata_ud_t
User data struct for call to H5Literate() in nc4_rec_read_metadata()
#define NC_MAX_UINT
Max or min values for a type.
Definition: netcdf.h:104
#define NC_SHORT
signed 2 byte integer
Definition: netcdf.h:40
#define NC_WRITE
Set read-write access for nc_open().
Definition: netcdf.h:131
#define NC_EMAXNAME
NC_MAX_NAME exceeded.
Definition: netcdf.h:335
#define NC_EPERM
Write to read only.
Definition: netcdf.h:293
#define NC_MAX_HDF4_NAME
This is the max size of an SD dataset name in HDF4 (from HDF4 documentation).
Definition: netcdf.h:240
#define NC_NOERR
No Error.
Definition: netcdf.h:282
#define NC_ENUM
enum types
Definition: netcdf.h:58
#define NC_DISKLESS
Use diskless file.
Definition: netcdf.h:135
Struct to track information about objects in a group, for nc4_rec_read_metadata() ...
Definition: nc4file.c:58
struct NC4_rec_read_metadata_obj_info NC4_rec_read_metadata_obj_info_t
Struct to track information about objects in a group, for nc4_rec_read_metadata() ...
#define NC_COMPOUND
compound types
Definition: netcdf.h:59
#define NC_FILL
Argument to nc_set_fill() to clear NC_NOFILL.
Definition: netcdf.h:119
#define NC_FLOAT
single precision floating point number
Definition: netcdf.h:43
#define NC_UINT64
unsigned 8-byte int
Definition: netcdf.h:49
#define NC_MPIPOSIX
Turn on MPI POSIX I/O.
Definition: netcdf.h:159

Return to the Main Unidata NetCDF page.
Generated on Wed Aug 19 2015 17:25:41 for NetCDF. NetCDF is a Unidata library.