NetCDF  4.7.3
nc4info.c
Go to the documentation of this file.
1 
10 #include "config.h"
11 #include "nc4internal.h"
12 #include "hdf5internal.h"
13 #include "nc_provenance.h"
14 #include "nclist.h"
15 #include "ncbytes.h"
16 
17 /* Provide a hack to suppress the writing of _NCProperties attribute.
18  This is for creating a file without _NCProperties for testing purposes.
19 */
20 #undef SUPPRESSNCPROPS
21 
22 /* Various Constants */
23 #define NCPROPS_MAX_NAME 1024 /* max key name size */
24 #define NCPROPS_MAX_VALUE 1024 /* max value size */
25 #define HDF5_MAX_NAME 1024
27 #define ESCAPECHARS "\\=|,"
28 
30 #define NCHECK(expr) {if((expr)!=NC_NOERR) {goto done;}}
31 
33 #define HCHECK(expr) {if((expr)<0) {ncstat = NC_EHDFERR; goto done;}}
34 
35 static int NC4_read_ncproperties(NC_FILE_INFO_T* h5, char** propstring);
36 static int NC4_write_ncproperties(NC_FILE_INFO_T* h5);
37 
38 static int globalpropinitialized = 0;
39 static NC4_Provenance globalprovenance;
40 
50 int
51 NC4_provenance_init(void)
52 {
53  int stat = NC_NOERR;
54  char* name = NULL;
55  char* value = NULL;
56  unsigned major,minor,release;
57  NCbytes* buffer = NULL; /* for constructing the global _NCProperties */
58  char printbuf[1024];
59  const char* p = NULL;
60 
61  if(globalpropinitialized)
62  return stat;
63 
64  /* Build _NCProperties info */
65 
66  /* Initialize globalpropinfo */
67  memset((void*)&globalprovenance,0,sizeof(NC4_Provenance));
68  globalprovenance.version = NCPROPS_VERSION;
69 
70  buffer = ncbytesnew();
71 
72  /* Insert version as first entry */
73  ncbytescat(buffer,NCPVERSION);
74  ncbytescat(buffer,"=");
75 
76  snprintf(printbuf,sizeof(printbuf),"%d",globalprovenance.version);
77  ncbytescat(buffer,printbuf);
78 
79  /* Insert the netcdf version */
80  ncbytesappend(buffer,NCPROPSSEP2);
81  ncbytescat(buffer,NCPNCLIB2);
82  ncbytescat(buffer,"=");
83  ncbytescat(buffer,PACKAGE_VERSION);
84 
85  /* Insert the HDF5 as underlying storage format library */
86  ncbytesappend(buffer,NCPROPSSEP2);
87  ncbytescat(buffer,NCPHDF5LIB2);
88  ncbytescat(buffer,"=");
89  if((stat = NC4_hdf5get_libversion(&major,&minor,&release))) goto done;
90  snprintf(printbuf,sizeof(printbuf),"%1u.%1u.%1u",major,minor,release);
91  ncbytescat(buffer,printbuf);
92 
93 #ifdef NCPROPERTIES_EXTRA
94  /* Add any extra fields */
95  p = NCPROPERTIES_EXTRA;
96  if(p[0] == NCPROPSSEP2) p++; /* If leading separator */
97  ncbytesappend(buffer,NCPROPSSEP2);
98  ncbytescat(buffer,p);
99 #endif
100  ncbytesnull(buffer);
101  globalprovenance.ncproperties = ncbytesextract(buffer);
102 
103 done:
104  ncbytesfree(buffer);
105  if(name != NULL) free(name);
106  if(value != NULL) free(value);
107  if(stat == NC_NOERR)
108  globalpropinitialized = 1; /* avoid repeating it */
109  return stat;
110 }
111 
118 int
119 NC4_provenance_finalize(void)
120 {
121  return NC4_clear_provenance(&globalprovenance);
122 }
123 
137 int
138 NC4_new_provenance(NC_FILE_INFO_T* file)
139 {
140  int ncstat = NC_NOERR;
141  NC4_Provenance* provenance = NULL;
142  int superblock = -1;
143 
144  LOG((5, "%s: ncid 0x%x", __func__, file->root_grp->hdr.id));
145 
146  assert(file->provenance.ncproperties == NULL); /* not yet defined */
147 
148  provenance = &file->provenance;
149  memset(provenance,0,sizeof(NC4_Provenance)); /* make sure */
150 
151  /* Set the version */
152  provenance->version = globalprovenance.version;
153 
154  /* Set the superblock number */
155  if((ncstat = NC4_hdf5get_superblock(file,&superblock))) goto done;
156  provenance->superblockversion = superblock;
157 
158  if(globalprovenance.ncproperties != NULL) {
159  if((provenance->ncproperties = strdup(globalprovenance.ncproperties)) == NULL)
160  {ncstat = NC_ENOMEM; goto done;}
161  }
162 
163 done:
164  if(ncstat) {
165  LOG((0,"Could not create _NCProperties attribute"));
166  }
167  return NC_NOERR;
168 }
169 
181 int
182 NC4_read_provenance(NC_FILE_INFO_T* file)
183 {
184  int ncstat = NC_NOERR;
185  NC4_Provenance* provenance = NULL;
186  int superblock = -1;
187  char* propstring = NULL;
188 
189  LOG((5, "%s: ncid 0x%x", __func__, file->root_grp->hdr.id));
190 
191  assert(file->provenance.version == 0); /* not yet defined */
192 
193  provenance = &file->provenance;
194  memset(provenance,0,sizeof(NC4_Provenance)); /* make sure */
195 
196  /* Set the superblock number */
197  if((ncstat = NC4_hdf5get_superblock(file,&superblock))) goto done;
198  provenance->superblockversion = superblock;
199 
200  /* Read the _NCProperties value from the file */
201  if((ncstat = NC4_read_ncproperties(file,&propstring))) goto done;
202  provenance->ncproperties = propstring;
203  propstring = NULL;
204 
205 done:
206  nullfree(propstring);
207  if(ncstat) {
208  LOG((0,"Could not create _NCProperties attribute"));
209  }
210  return NC_NOERR;
211 }
212 
224 int
225 NC4_write_provenance(NC_FILE_INFO_T* file)
226 {
227  int ncstat = NC_NOERR;
228  if((ncstat = NC4_write_ncproperties(file)))
229  goto done;
230 done:
231  return ncstat;
232 }
233 
234 /* HDF5 Specific attribute read/write of _NCProperties */
235 static int
236 NC4_read_ncproperties(NC_FILE_INFO_T* h5, char** propstring)
237 {
238  int retval = NC_NOERR;
239  hid_t hdf5grpid = -1;
240  hid_t attid = -1;
241  hid_t aspace = -1;
242  hid_t atype = -1;
243  hid_t ntype = -1;
244  char* text = NULL;
245  H5T_class_t t_class;
246  hsize_t size;
247 
248  LOG((5, "%s", __func__));
249 
250  hdf5grpid = ((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid;
251 
252  if(H5Aexists(hdf5grpid,NCPROPS) <= 0) { /* Does not exist */
253  /* File did not contain a _NCProperties attribute; leave empty */
254  goto done;
255  }
256 
257  /* NCPROPS Attribute exists, make sure it is legitimate */
258  attid = H5Aopen_name(hdf5grpid, NCPROPS);
259  assert(attid > 0);
260  aspace = H5Aget_space(attid);
261  atype = H5Aget_type(attid);
262  /* Verify atype and size */
263  t_class = H5Tget_class(atype);
264  if(t_class != H5T_STRING)
265  {retval = NC_EINVAL; goto done;}
266  size = H5Tget_size(atype);
267  if(size == 0)
268  {retval = NC_EINVAL; goto done;}
269  text = (char*)malloc(1+(size_t)size);
270  if(text == NULL)
271  {retval = NC_ENOMEM; goto done;}
272  if((ntype = H5Tget_native_type(atype, H5T_DIR_DEFAULT)) < 0)
273  {retval = NC_EHDFERR; goto done;}
274  if((H5Aread(attid, ntype, text)) < 0)
275  {retval = NC_EHDFERR; goto done;}
276  /* Make sure its null terminated */
277  text[(size_t)size] = '\0';
278  if(propstring) {*propstring = text; text = NULL;}
279 
280 done:
281  if(text != NULL) free(text);
282  /* Close out the HDF5 objects */
283  if(attid > 0 && H5Aclose(attid) < 0) retval = NC_EHDFERR;
284  if(aspace > 0 && H5Sclose(aspace) < 0) retval = NC_EHDFERR;
285  if(atype > 0 && H5Tclose(atype) < 0) retval = NC_EHDFERR;
286  if(ntype > 0 && H5Tclose(ntype) < 0) retval = NC_EHDFERR;
287 
288  /* For certain errors, actually fail, else log that attribute was invalid and ignore */
289  if(retval != NC_NOERR) {
290  if(retval != NC_ENOMEM && retval != NC_EHDFERR) {
291  LOG((0,"Invalid _NCProperties attribute: ignored"));
292  retval = NC_NOERR;
293  }
294  }
295  return retval;
296 }
297 
298 static int
299 NC4_write_ncproperties(NC_FILE_INFO_T* h5)
300 {
301 #ifdef SUPPRESSNCPROPERTY
302  return NC_NOERR;
303 #else
304  int retval = NC_NOERR;
305  hid_t hdf5grpid = -1;
306  hid_t attid = -1;
307  hid_t aspace = -1;
308  hid_t atype = -1;
309  size_t len = 0;
310  NC4_Provenance* prov = &h5->provenance;
311 
312  LOG((5, "%s", __func__));
313 
314  /* If the file is read-only, return an error. */
315  if (h5->no_write)
316  {retval = NC_EPERM; goto done;}
317 
318  hdf5grpid = ((NC_HDF5_GRP_INFO_T *)(h5->root_grp->format_grp_info))->hdf_grpid;
319 
320  if(H5Aexists(hdf5grpid,NCPROPS) > 0) /* Already exists, no overwrite */
321  goto done;
322 
323  /* Build the property if we have legit value */
324  if(prov->ncproperties != NULL) {
325  /* Build the HDF5 string type */
326  if ((atype = H5Tcopy(H5T_C_S1)) < 0)
327  {retval = NC_EHDFERR; goto done;}
328  if (H5Tset_strpad(atype, H5T_STR_NULLTERM) < 0)
329  {retval = NC_EHDFERR; goto done;}
330  if(H5Tset_cset(atype, H5T_CSET_ASCII) < 0)
331  {retval = NC_EHDFERR; goto done;}
332  len = strlen(prov->ncproperties);
333  if(H5Tset_size(atype, len) < 0)
334  {retval = NC_EFILEMETA; goto done;}
335  /* Create NCPROPS attribute */
336  if((aspace = H5Screate(H5S_SCALAR)) < 0)
337  {retval = NC_EFILEMETA; goto done;}
338  if ((attid = H5Acreate(hdf5grpid, NCPROPS, atype, aspace, H5P_DEFAULT)) < 0)
339  {retval = NC_EFILEMETA; goto done;}
340  if (H5Awrite(attid, atype, prov->ncproperties) < 0)
341  {retval = NC_EFILEMETA; goto done;}
342 /* Verify */
343 #if 0
344  {
345  hid_t spacev, typev;
346  hsize_t dsize, tsize;
347  typev = H5Aget_type(attid);
348  spacev = H5Aget_space(attid);
349  dsize = H5Aget_storage_size(attid);
350  tsize = H5Tget_size(typev);
351  fprintf(stderr,"dsize=%lu tsize=%lu\n",(unsigned long)dsize,(unsigned long)tsize);
352  }
353 #endif
354  }
355 
356 done:
357  /* Close out the HDF5 objects */
358  if(attid > 0 && H5Aclose(attid) < 0) retval = NC_EHDFERR;
359  if(aspace > 0 && H5Sclose(aspace) < 0) retval = NC_EHDFERR;
360  if(atype > 0 && H5Tclose(atype) < 0) retval = NC_EHDFERR;
361 
362  /* For certain errors, actually fail, else log that attribute was invalid and ignore */
363  switch (retval) {
364  case NC_ENOMEM:
365  case NC_EHDFERR:
366  case NC_EPERM:
367  case NC_EFILEMETA:
368  case NC_NOERR:
369  break;
370  default:
371  LOG((0,"Invalid _NCProperties attribute"));
372  retval = NC_NOERR;
373  break;
374  }
375  return retval;
376 #endif
377 }
378 
379 /**************************************************/
380 /* Utilities */
381 
382 /* Debugging */
383 
384 void
385 ncprintprovenance(NC4_Provenance* info)
386 {
387  fprintf(stderr,"[%p] version=%d superblockversion=%d ncproperties=|%s|\n",
388  info,
389  info->version,
390  info->superblockversion,
391  (info->ncproperties==NULL?"":info->ncproperties));
392 }
393 
403 int
404 NC4_clear_provenance(NC4_Provenance* prov)
405 {
406  LOG((5, "%s", __func__));
407 
408  if(prov == NULL) return NC_NOERR;
409  nullfree(prov->ncproperties);
410  memset(prov,0,sizeof(NC4_Provenance));
411  return NC_NOERR;
412 }
413 
414 #if 0
415 /* Unused functions */
416 
426 static int
427 properties_parse(const char* text0, NClist* pairs)
428 {
429  int ret = NC_NOERR;
430  char* p;
431  char* q;
432  char* text = NULL;
433 
434  if(text0 == NULL || strlen(text0) == 0)
435  goto done;
436 
437  text = strdup(text0);
438  if(text == NULL) return NC_ENOMEM;
439 
440  /* For back compatibility with version 1, translate '|' -> ',' */
441  for(p=text;*p;p++) {
442  if(*p == NCPROPSSEP1)
443  *p = NCPROPSSEP2;
444  }
445 
446  /* Walk and fill in ncinfo */
447  p = text;
448  while(*p) {
449  char* name = p;
450  char* value = NULL;
451  char* next = NULL;
452 
453  /* Delimit whole (key,value) pair */
454  q = locate(p,NCPROPSSEP2);
455  if(*q != '\0') /* Never go beyond the final nul term */
456  *q++ = '\0';
457  next = q;
458  /* split key and value */
459  q = locate(p,'=');
460  name = p;
461  *q++ = '\0';
462  value = q;
463  /* Set up p for next iteration */
464  p = next;
465  nclistpush(pairs,strdup(name));
466  nclistpush(pairs,strdup(value));
467  }
468 done:
469  if(text) free(text);
470  return ret;
471 }
472 
473 /* Locate a specific character and return its pointer
474  or EOS if not found
475  take \ escapes into account */
476 static char*
477 locate(char* p, char tag)
478 {
479  char* next;
480  int c;
481  assert(p != NULL);
482  for(next = p;(c = *next);next++) {
483  if(c == tag)
484  return next;
485  else if(c == '\\' && next[1] != '\0')
486  next++; /* skip escaped char */
487  }
488  return next; /* not found */
489 }
490 
491 /* Utility to transfer a string to a buffer with escaping */
492 static void
493 escapify(NCbytes* buffer, const char* s)
494 {
495  const char* p;
496  for(p=s;*p;p++) {
497  if(strchr(ESCAPECHARS,*p) != NULL)
498  ncbytesappend(buffer,'\\');
499  ncbytesappend(buffer,*p);
500  }
501 }
502 
516 static int
517 build_propstring(int version, NClist* list, char** spropp)
518 {
519  int stat = NC_NOERR;
520  int i;
521  NCbytes* buffer = NULL;
522  char sversion[64];
523 
524  LOG((5, "%s version=%d", __func__, version));
525 
526  if(spropp != NULL) *spropp = NULL;
527 
528  if(version == 0 || version > NCPROPS_VERSION) /* unknown case */
529  goto done;
530  if(list == NULL)
531  {stat = NC_EINVAL; goto done;}
532 
533  if((buffer = ncbytesnew()) == NULL)
534  {stat = NC_ENOMEM; goto done;}
535 
536  /* start with version */
537  ncbytescat(buffer,NCPVERSION);
538  ncbytesappend(buffer,'=');
539  /* Use current version */
540  snprintf(sversion,sizeof(sversion),"%d",NCPROPS_VERSION);
541  ncbytescat(buffer,sversion);
542 
543  for(i=0;i<nclistlength(list);i+=2) {
544  char* value, *name;
545  name = nclistget(list,i);
546  if(name == NULL) continue;
547  value = nclistget(list,i+1);
548  ncbytesappend(buffer,NCPROPSSEP2); /* terminate last entry */
549  escapify(buffer,name);
550  ncbytesappend(buffer,'=');
551  escapify(buffer,value);
552  }
553  /* Force null termination */
554  ncbytesnull(buffer);
555  if(spropp) *spropp = ncbytesextract(buffer);
556 
557 done:
558  if(buffer != NULL) ncbytesfree(buffer);
559  return stat;
560 }
561 
562 static int
563 properties_getversion(const char* propstring, int* versionp)
564 {
565  int ncstat = NC_NOERR;
566  int version = 0;
567  /* propstring should begin with "version=dddd" */
568  if(propstring == NULL || strlen(propstring) < strlen("version=") + strlen("1"))
569  {ncstat = NC_EINVAL; goto done;} /* illegal version */
570  if(memcmp(propstring,"version=",strlen("version=")) != 0)
571  {ncstat = NC_EINVAL; goto done;} /* illegal version */
572  propstring += strlen("version=");
573  /* get version */
574  version = atoi(propstring);
575  if(version < 0)
576  {ncstat = NC_EINVAL; goto done;} /* illegal version */
577  if(versionp) *versionp = version;
578 done:
579  return ncstat;
580 }
581 
594 static int
595 parse_provenance(NC4_Provenance* prov)
596 {
597  int ncstat = NC_NOERR;
598  char *name = NULL;
599  char *value = NULL;
600  int version = 0;
601  NClist* list = NULL;
602 
603  LOG((5, "%s: prov 0x%x", __func__, prov));
604 
605  if(prov->ncproperty == NULL || strlen(prov->ncproperty) < strlen("version="))
606  {ncstat = NC_EINVAL; goto done;}
607  if((list = nclistnew()) == NULL)
608  {ncstat = NC_ENOMEM; goto done;}
609 
610  /* Do we understand the version? */
611  if(prov->version > 0 && prov->version <= NCPROPS_VERSION) {/* recognized version */
612  if((ncstat=properties_parse(prov->ncproperty,list)))
613  goto done;
614  /* Remove version pair from properties list*/
615  if(nclistlength(list) < 2)
616  {ncstat = NC_EINVAL; goto done;} /* bad _NCProperties attribute */
617  /* Throw away the purported version=... */
618  nclistremove(list,0); /* version key */
619  nclistremove(list,0); /* version value */
620 
621  /* Now, rebuild to the latest version */
622  switch (version) {
623  default: break; /* do nothing */
624  case 1: {
625  int i;
626  for(i=0;i<nclistlength(list);i+=2) {
627  char* newname = NULL;
628  name = nclistget(list,i);
629  if(name == NULL) continue; /* ignore */
630  if(strcmp(name,NCPNCLIB1) == 0)
631  newname = NCPNCLIB2; /* change name */
632  else if(strcmp(name,NCPHDF5LIB1) == 0)
633  newname = NCPHDF5LIB2;
634  else continue; /* ignore */
635  /* Do any rename */
636  nclistset(list,i,strdup(newname));
637  if(name) {free(name); name = NULL;}
638  }
639  } break;
640  } /*switch*/
641  }
642  prov->properties = list;
643  list = NULL;
644 
645 done:
646  nclistfreeall(list);
647  if(name != NULL) free(name);
648  if(value != NULL) free(value);
649  return ncstat;
650 }
651 
661 static int
662 NC4_free_provenance(NC4_Provenance* prov)
663 {
664  LOG((5, "%s", __func__));
665 
666  if(prov == NULL) return NC_NOERR;
667  NC4_clear_provenance(prov);
668  free(prov);
669  return NC_NOERR;
670 }
671 
672 /* Utility to copy contents of the dfalt into an NCPROPINFO object */
673 static int
674 propinfo_default(NC4_Properties* dst, const NC4_Properties* dfalt)
675 {
676  int i;
677  if(dst->properties == NULL) {
678  dst->properties = nclistnew();
679  if(dst->properties == NULL) return NC_ENOMEM;
680  }
681  dst->version = dfalt->version;
682  for(i=0;i<nclistlength(dfalt->properties);i++) {
683  char* s = nclistget(dfalt->properties,i);
684  s = strdup(s);
685  if(s == NULL) return NC_ENOMEM;
686  nclistpush(dst->properties,s);
687  }
688  return NC_NOERR;
689 }
690 
691 #endif /*0*/
NC_NOERR
#define NC_NOERR
No Error.
Definition: netcdf.h:325
NC_EINVAL
#define NC_EINVAL
Invalid Argument.
Definition: netcdf.h:335
NC_EHDFERR
#define NC_EHDFERR
Error at HDF5 layer.
Definition: netcdf.h:438
NC_EPERM
#define NC_EPERM
Write to read only.
Definition: netcdf.h:336
NC_ENOMEM
#define NC_ENOMEM
Memory allocation (malloc) failure.
Definition: netcdf.h:405
NC4_write_ncproperties
static int NC4_write_ncproperties(NC_FILE_INFO_T *h5)
Definition: nc4info.c:299
NC_EFILEMETA
#define NC_EFILEMETA
Problem with file metadata.
Definition: netcdf.h:442

Return to the Main Unidata NetCDF page.
Generated on Fri Jan 3 2020 06:58:32 for NetCDF. NetCDF is a Unidata library.