From 4fa147024141f5800ad8ec6c66f25473f32cebe1 Mon Sep 17 00:00:00 2001 From: Dennis Heimbigner Date: Sun, 15 May 2016 18:03:04 -0600 Subject: [PATCH] re: github issue https://github.com/Unidata/netcdf-c/issues/265 Charlie Zender noted that we forgot to define what happens for various netcdf API attribute operations, notably nc_inq_att() and nc_get_att(). So, I added a list of legal and illegal api calls for the provenance attributes in docs/attribute_conventions.md. I also added more test cases to ncdump/tst_fileinfo.c to verify and fixed resultant errors. --- docs/attribute_conventions.md | 29 +++++++++++++++--- libsrc4/nc4attr.c | 32 ++++++++++++++------ ncdump/tst_fileinfo.c | 57 ++++++++++++++++++++++++++++++----- nctest/atttests.c | 3 ++ 4 files changed, 98 insertions(+), 23 deletions(-) diff --git a/docs/attribute_conventions.md b/docs/attribute_conventions.md index fab62bb54..cb05ccbc2 100644 --- a/docs/attribute_conventions.md +++ b/docs/attribute_conventions.md @@ -131,17 +131,34 @@ It is strongly recommended that applicable conventions be followed unless there These attributes can occur in netCDF enhanced (netcdf-4) files beginning with version 4.4.1. They all are associated with the root group as -global attributes. They are hidden in the sense that they can only be -accessed thru the netcdf-C api calls via the name. They have no -attribute number and will not be counted in the number of global -attributes in the root group. The simplest way to view these attributes -is to use the -s flag to the ncdump command. +global attributes. They are hidden in the sense that they have no +attribute number, so they can only be accessed thru the netcdf-C api +calls via the name. Additionally, these attributes will not be counted +in the number of global attributes in the root group. + +The simplest way to view these attributes is to use the -s flag to the +ncdump command. Alternatively, one can use the following API calls to +obtain information. +- nc_inq_att +- nc_inq_atttype +- nc_inq_attlen +- nc_get_att (and derivatives) + +Using the following API calls will fail. +- nc_inq_attid +- nc_inq_attname +- nc_copy_att +- nc_rename_att +- nc_del_att +- nc_put_att (and derivatives) + `_NCProperties` > This attribute is persistent in the file, but hidden. It is inserted in the file at creation time and is never modified after that point. It specifies the following. > - The version for the netcdf library used at creation time. > - The version for the HDF5 library used at creation time. +> - The type of this attribute is NC_CHAR. > Its format is: `name=value|name=value ...`
> Occurrences of '|' in the name or value are disallowed. @@ -155,6 +172,7 @@ is to use the -s flag to the ncdump command. > This attribute is ephemeral in that it is computed by looking at the file's HDF5 superblock. > It has this form: `_SuperBlockVersion = 0|1|2|3|...` +> The type of this attribute is NC_INT. `_IsNetcdf4` @@ -162,6 +180,7 @@ is to use the -s flag to the ncdump command. > The _IsNetcdf4 attribute has the form: `_IsNetcdf4 = 0|1` > where 1 means the file has various tags indicating it was produced thru the netcdf-4 API. +> The type of this attribute is NC_INT. > This attribute is computed by using the HDF5 API to walk the file to look for attributes specific to netcdf-4. False negatives are possible for a small subset of netcdf-4 files, especially those not containing dimensions. False positives are only possible by deliberate modifications to an existing HDF5 file thru the HDF5 API. For files with the _NCProperties attribute, this attribute is redundant. For files created prior to the introduction of the _NCProperties attribute, this may be a useful indicator of the provenance of the file. diff --git a/libsrc4/nc4attr.c b/libsrc4/nc4attr.c index 4e5e288db..8bbfa879d 100644 --- a/libsrc4/nc4attr.c +++ b/libsrc4/nc4attr.c @@ -22,7 +22,8 @@ conditions. #endif #ifdef ENABLE_FILEINFO -static int nc4_get_att_special(NC_HDF5_FILE_INFO_T*, const char*, nc_type, int, void*, size_t*, int*); +static int nc4_get_att_special(NC_HDF5_FILE_INFO_T*, const char*, + nc_type*, nc_type, size_t*, int*, int, void*); #endif int nc4typelen(nc_type type); @@ -70,7 +71,7 @@ nc4_get_att(int ncid, NC *nc, int varid, const char *name, const char** sp; for(sp = NC_RESERVED_SPECIAL_LIST;*sp;sp++) { if(strcmp(name,*sp)==0) { - return nc4_get_att_special(h5, norm_name, mem_type, is_long, data, lenp, attnum); + return nc4_get_att_special(h5, norm_name, xtype, mem_type, lenp, attnum, is_long, data); } } } @@ -546,8 +547,8 @@ nc4_put_att(int ncid, NC *nc, int varid, const char *name, return NC_NOERR; } -/* Learn about an att. All the nc4 nc_inq_ functions just call - * add_meta_get to get the metadata on an attribute. */ +/* Learn about an att. All the nc4 nc_inq_ functions just call + * nc4_get_att to get the metadata on an attribute. */ int NC4_inq_att(int ncid, int varid, const char *name, nc_type *xtypep, size_t *lenp) { @@ -578,7 +579,7 @@ NC4_inq_att(int ncid, int varid, const char *name, nc_type *xtypep, size_t *lenp #endif /* USE_PNETCDF */ /* Handle netcdf-4 files. */ - return nc4_get_att(ncid, nc, varid, name, xtypep, NC_UBYTE, lenp, NULL, 0, NULL); + return nc4_get_att(ncid, nc, varid, name, xtypep, NC_NAT, lenp, NULL, 0, NULL); } /* Learn an attnum, given a name. */ @@ -587,6 +588,7 @@ NC4_inq_attid(int ncid, int varid, const char *name, int *attnump) { NC *nc; NC_HDF5_FILE_INFO_T *h5; + int stat; LOG((2, "nc_inq_attid: ncid 0x%x varid %d name %s", ncid, varid, name)); @@ -605,8 +607,9 @@ NC4_inq_attid(int ncid, int varid, const char *name, int *attnump) #endif /* USE_PNETCDF */ /* Handle netcdf-4 files. */ - return nc4_get_att(ncid, nc, varid, name, NULL, NC_UBYTE, + stat = nc4_get_att(ncid, nc, varid, name, NULL, NC_NAT, NULL, attnump, 0, NULL); + return stat; } @@ -950,24 +953,33 @@ nc4_put_att_tc(int ncid, int varid, const char *name, nc_type file_type, #ifdef ENABLE_FILEINFO static int -nc4_get_att_special(NC_HDF5_FILE_INFO_T *h5, const char* name, nc_type mem_type, int islong, void* data, size_t* lenp, int* idp) +nc4_get_att_special(NC_HDF5_FILE_INFO_T* h5, const char* name, + nc_type* filetypep, nc_type mem_type, size_t* lenp, + int* attnump, int is_long, void* data) { - /* Always make the attr id be -1 */ - if(idp) *idp = -1; + /* Fail if asking for att id */ + if(attnump) + return NC_EATTMETA; if(strcmp(name,NCPROPS)==0) { if(h5->fileinfo->propattr.version == 0) return NC_ENOTATT; + if(mem_type == NC_NAT) mem_type = NC_CHAR; + if(mem_type != NC_CHAR) + return NC_ECHAR; + if(filetypep) *filetypep = NC_CHAR; if(lenp) *lenp = strlen(h5->fileinfo->propattr.text); if(data) strcpy((char*)data,h5->fileinfo->propattr.text); } else if(strcmp(name,ISNETCDF4ATT)==0 || strcmp(name,SUPERBLOCKATT)==0) { unsigned long long iv = 0; + if(filetypep) *filetypep = NC_INT; + if(lenp) *lenp = 1; if(strcmp(name,SUPERBLOCKATT)==0) iv = (unsigned long long)h5->fileinfo->superblockversion; else /* strcmp(name,ISNETCDF4ATT)==0 */ iv = NC4_isnetcdf4(h5); - if(lenp) *lenp = 1; + if(mem_type == NC_NAT) mem_type = NC_INT; if(data) switch (mem_type) { case NC_BYTE: *((char*)data) = (char)iv; break; diff --git a/ncdump/tst_fileinfo.c b/ncdump/tst_fileinfo.c index 452862ddb..b21face4c 100644 --- a/ncdump/tst_fileinfo.c +++ b/ncdump/tst_fileinfo.c @@ -71,14 +71,16 @@ main(int argc, char **argv) } { - int root, grpid, varid, stat, natts; + int root, grpid, varid, stat, natts, id; int data = 17; const char* sdata = "text"; char ncprops[8192]; size_t len; int dimid; + nc_type xtype; + char name[NC_MAX_NAME]; - printf("*** creating netcdf-4 test file using netCDF %s...", NC4FILE); + printf("\n*** creating netcdf-4 test file using netCDF %s...", NC4FILE); if(nc_create(NC4FILE,NC_WRITE|NC_CLOBBER|NC_NETCDF4,&root)!=0) ERR; /* Create global attribute */ @@ -107,9 +109,24 @@ main(int argc, char **argv) /* Now, fiddle with the NCPROPS attribute */ - /* Get its value */ - if(nc_inq_att(root,NC_GLOBAL,NCPROPS,NULL,&len)!=0) ERR; + /* Get its metadata */ + if(nc_inq_att(root,NC_GLOBAL,NCPROPS,&xtype,&len)!=0) ERR; + if(xtype != NC_CHAR) ERR; + + /* Read in two ways */ if(nc_get_att_text(root,NC_GLOBAL,NCPROPS,ncprops)!=0) ERR; + if(strlen(ncprops) != len) ERR; + + /* Attempt to get attribute metadata piecemeal; some will fail */ + id = -1; + stat = nc_inq_attid(root,NC_GLOBAL,NCPROPS,&id); + if(stat == NC_NOERR) ERR; + stat = nc_inq_attname(root,NC_GLOBAL,id,name); + if(stat == NC_NOERR) ERR; + if(nc_inq_atttype(root,NC_GLOBAL,NCPROPS,&xtype)!=0) ERR; + if(xtype != NC_CHAR) ERR; + if(nc_inq_attlen(root,NC_GLOBAL,NCPROPS,&len)!=0) ERR; + if(len != strlen(ncprops)) ERR; /*Overwrite _NCProperties root attribute; should fail */ stat = nc_put_att_text(root,NC_GLOBAL,NCPROPS,strlen(sdata),sdata); @@ -121,11 +138,23 @@ main(int argc, char **argv) /* Ditto _SuperblockVersion */ - /* Get its value */ - if(nc_inq_att(root,NC_GLOBAL,SUPERBLOCKATT,NULL,&len)!=0) ERR; + /* Get its metadata */ + if(nc_inq_att(root,NC_GLOBAL,SUPERBLOCKATT,&xtype,&len)!=0) ERR; + if(xtype != NC_INT) ERR; if(len != 1) ERR; + if(nc_get_att_int(root,NC_GLOBAL,SUPERBLOCKATT,&data)!=0) ERR; + /* Attempt to get attribute metadata piecemeal */ + stat = nc_inq_attid(root,NC_GLOBAL,SUPERBLOCKATT,&id); + if(stat == NC_NOERR) ERR; + stat = nc_inq_attname(root,NC_GLOBAL,id,name); + if(stat == NC_NOERR) ERR; + if(nc_inq_atttype(root,NC_GLOBAL,SUPERBLOCKATT,&xtype)!=0) ERR; + if(xtype != NC_INT) ERR; + if(nc_inq_attlen(root,NC_GLOBAL,SUPERBLOCKATT,&len)!=0) ERR; + if(len != 1) ERR; + /*Overwrite; should fail */ stat = nc_put_att_int(root,NC_GLOBAL,NCPROPS,NC_INT,1,&data); if(stat == NC_NOERR) ERR; @@ -136,11 +165,23 @@ main(int argc, char **argv) /* Ditto _IsNetcdf4 */ - /* Get its value */ - if(nc_inq_att(root,NC_GLOBAL,ISNETCDF4ATT,NULL,&len)!=0) ERR; + /* Get its metadata */ + if(nc_inq_att(root,NC_GLOBAL,ISNETCDF4ATT,&xtype,&len)!=0) ERR; + if(xtype != NC_INT) ERR; if(len != 1) ERR; + if(nc_get_att_int(root,NC_GLOBAL,ISNETCDF4ATT,&data)!=0) ERR; + /* Attempt to get attribute metadata piecemeal */ + stat = nc_inq_attid(root,NC_GLOBAL,ISNETCDF4ATT,&id); + if(stat == NC_NOERR) ERR; + stat = nc_inq_attname(root,NC_GLOBAL,id,name); + if(stat == NC_NOERR) ERR; + if(nc_inq_atttype(root,NC_GLOBAL,ISNETCDF4ATT,&xtype)!=0) ERR; + if(xtype != NC_INT) ERR; + if(nc_inq_attlen(root,NC_GLOBAL,ISNETCDF4ATT,&len)!=0) ERR; + if(len != 1) ERR; + /*Overwrite; should fail */ stat = nc_put_att_int(root,NC_GLOBAL,ISNETCDF4ATT,NC_INT,1,&data); if(stat == NC_NOERR) ERR; diff --git a/nctest/atttests.c b/nctest/atttests.c index 6cd41bcea..f9fe7f264 100644 --- a/nctest/atttests.c +++ b/nctest/atttests.c @@ -698,6 +698,9 @@ test_ncattcopy(path1, path2) error("%s: ncendef failed", pname); ncclose(cdfid); return ++nerrs; } +if(strcmp(path1, "nctest_netcdf4.nc")==0) { +int x=0; +} /* first (source) netcdf is in data mode */ /* create second netCDF to copy attributes to */ if ((cdfid2 = nccreate(path2, NC_CLOBBER)) == -1) {