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