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

Return to the Main Unidata NetCDF page.
Generated on Thu Aug 4 2016 17:52:18 for NetCDF. NetCDF is a Unidata library.