#include "ncdap4.h" #ifdef HAVE_GETRLIMIT #include #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "dapdebug.h" #include "dapalign.h" #include "netcdf.h" #include "nc4dispatch.h" #include "ncd4dispatch.h" #ifdef DEBUG #include "dapdump.h" #endif #define DFALTMODELFLAGS (NCF_NC3|NCF_NCDAP) /* Mnemonic */ #define getgrpid(nfi) ((nfi)->ext_ncid) #define getncid(drno) (((NC_FILE_INFO_T*)((drno)->controller))->ext_ncid) ptrdiff_t dapsinglestride4[NC_MAX_VAR_DIMS]; extern NC_FILE_INFO_T* nc_file; extern NCerror freeNCDRNO4(NCDRNO* drno); static void nc4dinitialize(void); static NCerror buildnc4(NC_FILE_INFO_T* nfi); static NCerror builddims4(NCDRNO*); static NCerror buildtypes4(NCDRNO*); static NCerror buildtypes4r(NCDRNO* drno, CDFnode* tnode); static NCerror buildvars4(NCDRNO*); static NCerror buildglobalattrs4(NCDRNO*, int, CDFnode* root); static NCerror buildattribute4a(NCDRNO* drno, NCattribute* att, int varid, int ncid); static NCerror showprojection4(NCDRNO* drno, CDFnode* var); static size_t estimatesizes4r(NCDRNO* drno, CDFnode* node); static void estimatesizes4(NCDRNO* drno); static NCerror fixzerodims4(NCDRNO* drno); static NCerror fixzerodims4r(NCDRNO* drno, CDFnode* node); static NCerror cvtunlimiteddim(NCDRNO* drno, CDFnode* dim); static void applyclientparamcontrols4(NCDRNO* drno); static int nc4dinitialized = 0; /* See ncd4dispatch.c for other version */ int NCD4_open(const char * path, int mode, int basepe, size_t *chunksizehintp, int useparallel, void* mpidata, NC_Dispatch* dispatch, NC** ncpp) { NCerror ncstat = NC_NOERR; OCerror ocstat = OC_NOERR; DAPURL tmpurl; NCDRNO* drno = NULL; /* reuse the ncdap3 structure*/ NC_HDF5_FILE_INFO_T* h5 = NULL; NC_FILE_INFO_T* nc = NULL; NC_GRP_INFO_T *grp = NULL; int ncid = -1; char* modifiedpath = NULL; const char* value; #ifdef PSEUDOFILE int fd; char* tmpname = NULL; #else short fileid; #endif LOG((1, "nc_open_file: path %s mode %d", path, mode)); if(!nc4dinitialized) nc4dinitialize(); if(!dapurlparse(path,&tmpurl)) PANIC("libncdap4: non-url path"); dapurlclear(&tmpurl); /* no longer needed */ /* Check for legal mode flags */ if((mode & NC_WRITE) != 0) ncstat = NC_EINVAL; else if(mode & (NC_WRITE|NC_CLOBBER)) ncstat = NC_EPERM; if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;} mode = (mode & ~(NC_MPIIO | NC_MPIPOSIX)); /* Despite the above check, we want the file to be initially writable */ mode |= (NC_WRITE|NC_CLOBBER); #ifdef DEBUG extern int ocdebug; ocdebug = 1; #endif #ifdef OCCOMPILEBYDEFAULT /* set the compile flag by default */ modifiedpath = (char*)emalloc(strlen(path)+strlen("[compile]")+1); strcpy(modifiedpath,"[compile]"); strcat(modifiedpath,path); #else modifiedpath = nulldup(path); #endif /* Setup tentative DRNO state*/ drno = (NCDRNO*)emalloc(sizeof(NCDRNO)); MEMCHECK(drno,NC_ENOMEM); drno->dap.urltext = modifiedpath; drno->cdf.separator = "."; dapurlparse(drno->dap.urltext,&drno->dap.url); if(!constrainable34(&drno->dap.url)) SETFLAG(drno,NCF_UNCONSTRAINABLE); drno->cdf.smallsizelimit = DFALTSMALLLIMIT; drno->cdf.smallsizelimit = DFALTSMALLLIMIT; drno->cdf.cache.cachelimit = DFALTCACHELIMIT; drno->cdf.cache.cachesize = 0; drno->cdf.cache.nodes = nclistnew(); drno->cdf.cache.cachecount = DFALTCACHECOUNT; #ifdef HAVE_GETRLIMIT { struct rlimit rl; if(getrlimit(RLIMIT_NOFILE, &rl) >= 0) { drno->cdf.cache.cachecount = (size_t)(rl.rlim_cur / 2); } } #endif /* Re-scan the client parameters */ applyclientparamcontrols4(drno); #ifdef PSEUDOFILE tmpname = nulldup(PSEUDOFILE); fd = mkstemp(tmpname); if(fd < 0) {THROWCHK(errno); goto fail;} /* Now, use the file to create the hdf5 file */ ncstat = NC4_create(tmpname,NC_NETCDF4|NC_CLOBBER, 0,0,NULL,0,NULL,dispatch,(NC**)&nc); ncid = nc->ext_ncid; /* unlink the temp file so it will automatically be reclaimed */ unlink(tmpname); efree(tmpname); /* Avoid fill */ dispatch->set_fill(ncid,NC_NOFILL,NULL); if(ncstat) {THROWCHK(ncstat); goto fail;} /* Find our metadata for this file. */ ncstat = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5); if(ncstat) {THROWCHK(ncstat); goto fail;} #else /*!PSEUDOFILE*/ /* Allocate the storage for this file info struct, and fill it with zeros*/ /* WARNING: this stores struct ptr at front of global list "nc_file" */ fileid = drno_delta_file_id(1); if((ncstat = nc4_file_list_add(fileid << ID_SHIFT))) goto fail; nc = nc_file; nc->int_ncid = nc->ext_ncid; ncid = nc->int_ncid; /* Add necessary structs to hold netcdf-4 file data. */ if((ncstat = nc4_nc4f_list_add(nc, path, mode))) {THROWCHK(ncstat); goto fail;} ASSERT((nc->nc4_info && nc->nc4_info->root_grp)); /* get the h5 structure*/ h5 = nc->nc4_info; /* Mark as indef state and writable */ h5->flags |= NC_INDEF; h5->no_write = 0; #endif nc->drno = drno; drno->controller = (void*)nc; /* cross link*/ nc->dispatch = dispatch; if(ncpp) *ncpp = (NC*)nc; ocstat = oc_open(drno->dap.urltext,&drno->dap.conn); if(ocstat != OC_NOERR) {THROWCHK(ocstat); goto fail;} if(paramcheck34(drno,"show","fetch")) drno->controls.flags |= NCF_SHOWFETCH; /* Turn on logging */ value = oc_clientparam_get(drno->dap.conn,"log"); if(value != NULL) { oc_loginit(); oc_setlogging(1); oc_logopen(value); } /* fetch and build the unconstrained DDS */ ncstat = fetchtemplatemetadata3(drno); if(ncstat != NC_NOERR) goto fail; /* Processing the constraints is a multi-step action. 1. retrieve the dds 2. convert the nc constraint names to be dap constraint names 3. parse and merge the dap and nc constraints */ ncstat = buildconstraints3(drno); if(ncstat != NC_NOERR) goto fail; /* fetch and build the constrained DDS */ ncstat = fetchconstrainedmetadata3(drno); if(ncstat != NC_NOERR) goto fail; /* The following actions are WRT to the constrained tree */ /* Accumulate useful nodes sets */ ncstat = computecdfnodesets4(drno); if(ncstat) {THROWCHK(ncstat); goto fail;} /* Fix grids */ ncstat = fixgrids4(drno); if(ncstat) {THROWCHK(ncstat); goto fail;} /* apply client parameters (after computcdfinfo and computecdfvars)*/ ncstat = applyclientparams34(drno); if(ncstat) {THROWCHK(ncstat); goto fail;} /* Accumulate the nodes representing user types*/ ncstat = computeusertypes4(drno); if(ncstat) {THROWCHK(ncstat); goto fail;} /* Re-compute the type names*/ ncstat = shortentypenames4(drno); if(ncstat) {THROWCHK(ncstat); goto fail;} /* Re-compute the dimension names*/ ncstat = computecdfdimnames34(drno); if(ncstat) {THROWCHK(ncstat); goto fail;} /* deal with zero-size dimensions */ ncstat = fixzerodims4(drno); if(ncstat) {THROWCHK(ncstat); goto fail;} /* Estimate the variable sizes */ estimatesizes4(drno); ncstat = buildnc4(nc); if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;} /* Do any necessary data prefetch */ ncstat = prefetchdata3(drno); if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;} /* Mark as no longer indef and no longer writable*/ h5->flags &= ~(NC_INDEF); h5->no_write = 1; return ncstat; fail: #ifdef PSEUDOFILE if(nc != NULL) NC4_abort(nc->ext_ncid); #else if(nc != NULL) { nc4_file_list_del(nc); } #endif freeNCDRNO4(drno); if(ocstat != OC_NOERR) ncstat = ocerrtoncerr(ocstat); return THROW(ncstat); } int NCD4_close(int ncid) { NC_FILE_INFO_T *nc; NC_GRP_INFO_T *grp; NC_HDF5_FILE_INFO_T *h5; NCDRNO* drno; int ncstat = NC_NOERR; LOG((1, "nc_close: ncid 0x%x", ncid)); /* Find our metadata for this file. */ ncstat = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5); if(ncstat != NC_NOERR) return THROW(ncstat); nc = grp->file; ASSERT((nc)); /* This must be the root group. */ if (grp->parent) ncstat = NC_EBADGRPID; oc_logclose(); /* Destroy/close the NCDRNO state */ drno = nc->drno; freeNCDRNO4(drno); nc->drno = NULL; #ifdef PSEUDOFILE NC4_abort(ncid); #else /* Invoke the relevant parts of close_netcdf4_file() */ nc4_rec_grp_del(&h5->root_grp, h5->root_grp); /* Free the nc4_info struct. */ nc_free(h5); /* Delete this entry from our list of open files. */ nc4_file_list_del(nc); drno_delta_numfiles(-1); #endif return THROW(ncstat); } /**************************************************/ /* Auxilliary routines */ /**************************************************/ static void nc4dinitialize() { nc3dinitialize(); nc4dinitialized = 1; } NCerror freeNCDRNO4(NCDRNO* drno) { return freeNCDRNO3(drno); } /* Note: never use any of the libncdap3 code to call netcdf API functions because it will use the netcdf-3 API. */ static NCerror buildnc4(NC_FILE_INFO_T* nfi) { NCerror ncstat = NC_NOERR; NCDRNO* drno = nfi->drno; CDFnode* dds = drno->cdf.ddsroot; ncstat = buildglobalattrs4(drno,getncid(drno),dds); if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;} ncstat = builddims4(drno); if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;} ncstat = buildtypes4(drno); if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;} ncstat = buildvars4(drno); if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;} fail: return THROW(ncstat); } /* Define dim info for top-level dims */ static NCerror builddims4(NCDRNO* drno) { unsigned int i,j; NCerror ncstat = NC_NOERR; int dimid; NC_FILE_INFO_T* nfi = (NC_FILE_INFO_T*)drno->controller; int ncid = getgrpid(nfi); NClist* dimset = nclistnew(); /* collect all dimensions from variables, including duplicates; note we use array.dimensions not array.ncdimensions. */ for(i=0;icdf.varnodes);i++) { CDFnode* var = (CDFnode*)nclistget(drno->cdf.varnodes,i); if(!var->visible) continue; nclistextend(dimset,nclistlength(var->array.dimensions)); for(j=0;jarray.dimensions);j++) { CDFnode* dim = (CDFnode*)nclistget(var->array.dimensions,j); int k,inserted = 0; /* Sort by fullname just for the fun of it */ for(k=0;kncfullname,dim->ncfullname) > 0) { nclistinsert(dimset,k,(ncelem)dim); inserted = 1; break; } } if(!inserted) nclistpush(dimset,(ncelem)dim); } } /* Define those top-level dims */ for(i=0;idim.basedim != NULL) continue; ncstat = nc_def_dim(ncid,dim->ncfullname,dim->dim.declsize,&dimid); if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;} dim->ncid = dimid; } /* Make all duplicate dims have same dimid as basedim*/ /* (see computecdfdimnames)*/ for(i=0;idim.basedim != NULL) { dim->ncid = dim->dim.basedim->ncid; } } /*ok:*/ fail: nclistfree(dimset); return THROW(ncstat); } static NCerror buildtypes4(NCDRNO* drno) { unsigned int i; NCerror ncstat = NC_NOERR; /* Define user types in postorder */ for(i=0;icdf.usertypes);i++) { CDFnode* node = (CDFnode*)nclistget(drno->cdf.usertypes,i); if(!node->visible) continue; ncstat = buildtypes4r(drno,node); if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;} } fail: return THROW(ncstat); } static NCerror buildtypes4r(NCDRNO* drno, CDFnode* tnode) { unsigned int i,j; int typeid; NCerror ncstat = NC_NOERR; if(!tnode->visible) goto done; switch (tnode->nctype) { case NC_Sequence: /* Look for sequences that have a single field whose type is primitive; for these, we will not generate the compound type. */ if(tnode->singleton) { /* ok, just generate the vlen type using the sequence's singleton field */ /* Find the first primitive visible field */ CDFnode* prim = getsingletonfield(tnode->subnodes); ASSERT((prim != NULL)); ncstat = nc_def_vlen(getncid(drno),tnode->vlenname, prim->etype,&typeid); if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;} tnode->basetypeid = prim->etype; tnode->typeid = typeid; break; } /* fall thru */ case NC_Grid: case NC_Structure: ncstat = nc_def_compound(getncid(drno),tnode->typesize.instance.size, tnode->typename,&typeid); if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;} tnode->typeid = typeid; for(i=0;isubnodes);i++) { CDFnode* field = (CDFnode*)nclistget(tnode->subnodes,i); if(!field->visible) continue; if(nclistlength(field->array.dimensions) == 0) { ncstat = nc_insert_compound(getncid(drno),typeid, field->ncbasename, field->typesize.field.offset, field->typeid); if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;} } else { int dimsizes[NC_MAX_VAR_DIMS]; for(j=0;jarray.dimensions);j++) { CDFnode* dim=(CDFnode*)nclistget(field->array.dimensions,j); dimsizes[j] = dim->dim.declsize; } ncstat = nc_insert_array_compound(getncid(drno),typeid, field->ncbasename, field->typesize.field.offset, field->typeid, nclistlength(field->array.dimensions), dimsizes); if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;} } } /* If the node is a sequence, also define the corresponding vlen type*/ if(tnode->nctype == NC_Sequence) { ncstat = nc_def_vlen(getncid(drno),tnode->vlenname,tnode->typeid,&typeid); if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;} tnode->basetypeid = typeid; tnode->typeid = typeid; } break; case NC_Primitive: break; default: PANIC1("unexpected nctype: %d",tnode->nctype); } done: return THROW(ncstat); } /* Simultaneously build any associated attributes */ static NCerror buildvars4(NCDRNO* drno) { /* Variables (in this translation) are (mostly) the direct fields of the Dataset*/ unsigned int i,j; NCerror ncstat = NC_NOERR; int varid; NC_FILE_INFO_T* nfi = (NC_FILE_INFO_T*)drno->controller; int ncid = getgrpid(nfi); for(i=0;icdf.varnodes);i++) { CDFnode* var = (CDFnode*)nclistget(drno->cdf.varnodes,i); NClist* vardims = var->array.dimensions; int dimids[NC_MAX_VAR_DIMS]; int ncrank,dimindex=0; if(!var->visible) continue; ncrank = nclistlength(vardims); if(ncrank > 0) { dimindex = 0; for(j=0;jncid; } } setvarbasetype(drno,var); ASSERT((var->typeid > 0)); ncstat = nc_def_var(getncid(drno),var->ncfullname, var->typeid, nclistlength(var->array.dimensions), (ncrank==0?NULL:dimids), &varid); if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;} var->ncid = varid; if(var->attributes != NULL) { for(j=0;jattributes);j++) { NCattribute* att = (NCattribute*)nclistget(var->attributes,j); ncstat = buildattribute4a(drno,att,varid,ncid); if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;} } } /* Tag the variable with its DAP path */ if(paramcheck34(drno,"show","projection")) showprojection4(drno,var); } fail: return THROW(ncstat); } static NCerror buildglobalattrs4(NCDRNO* drno, int ncid, CDFnode* root) { int i; const char* txt; char *nltxt, *p; NCerror ncstat = NC_NOERR; if(root->attributes != NULL) { for(i=0;iattributes);i++) { NCattribute* att = (NCattribute*)nclistget(root->attributes,i); ncstat = buildattribute4a(drno,att,NC_GLOBAL,ncid); if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;} } } /* Define some additional system global attributes depending on show= clientparams*/ /* Ignore failures*/ if(paramcheck34(drno,"show","translate")) { /* Add a global attribute to show the translation */ ncstat = nc_put_att_text(ncid,NC_GLOBAL,"_translate", strlen("netcdf-4"),"netcdf-4"); } if(paramcheck34(drno,"show","url")) { if(drno->dap.urltext != NULL) ncstat = nc_put_att_text(ncid,NC_GLOBAL,"_url", strlen(drno->dap.urltext),drno->dap.urltext); } if(paramcheck34(drno,"show","dds")) { txt = NULL; if(drno->cdf.ddsroot != NULL) txt = oc_inq_text(drno->dap.conn,drno->cdf.ddsroot->dds); if(txt != NULL) { /* replace newlines with spaces*/ nltxt = nulldup(txt); for(p=nltxt;*p;p++) {if(*p == '\n' || *p == '\r' || *p == '\t') {*p = ' ';}}; ncstat = nc_put_att_text(ncid,NC_GLOBAL,"_DDS",strlen(nltxt),nltxt); efree(nltxt); } } if(paramcheck34(drno,"show","das")) { txt = NULL; if(drno->dap.ocdasroot != OCNULL) txt = oc_inq_text(drno->dap.conn,drno->dap.ocdasroot); if(txt != NULL) { nltxt = nulldup(txt); for(p=nltxt;*p;p++) {if(*p == '\n' || *p == '\r' || *p == '\t') {*p = ' ';}}; ncstat = nc_put_att_text(ncid,NC_GLOBAL,"_DAS",strlen(nltxt),nltxt); efree(nltxt); } } fail: return THROW(ncstat); } static NCerror buildattribute4a(NCDRNO* drno, NCattribute* att, int varid, int ncid) { NCerror ncstat = NC_NOERR; char* cname = cdflegalname3(att->name); unsigned int nvalues = nclistlength(att->values); unsigned int typesize = nctypesizeof(att->etype); void* mem = emalloc(typesize * nvalues); ncstat = dapcvtattrval3(att->etype,mem,att->values); ncstat = nc_put_att(ncid,varid,cname,att->etype,nvalues,mem); if(att->etype == NC_STRING) { int i; for(i=0;inctype == NC_Sequence) return NC_NOERR; /* Collect the set of DDS node name forming the xpath */ collectnodepath3(var,path,WITHOUTDATASET); for(i=0;i 0) ncbytescat(projection,"."); ncbytescat(projection,node->name); } /* Now, add the dimension info */ rank = nclistlength(var->array.dimensions); for(i=0;iarray.dimensions,i); char tmp[32]; ncbytescat(projection,"["); snprintf(tmp,sizeof(tmp),"%lu",(unsigned long)dim->dim.declsize); ncbytescat(projection,tmp); ncbytescat(projection,"]"); } /* Define the attribute */ ncstat = nc_put_att_text(getncid(drno),var->ncid, "_projection", ncbyteslength(projection), ncbytescontents(projection)); return ncstat; } static unsigned long cdftotalsize4(NClist* dimensions) { unsigned int i; unsigned long total = 1; if(dimensions != NULL) { for(i=0;idim.declsize; } } return total; } static size_t estimatesizes4r(NCDRNO* drno, CDFnode* node) { int i; size_t size = 0; size_t totalsize = 0; NClist* ncdims; unsigned int rank; unsigned long dimsize; for(i=0;isubnodes);i++) { CDFnode* subnode = (CDFnode*)nclistget(node->subnodes,i); size += estimatesizes4r(drno,subnode); } switch (node->nctype) { case NC_Primitive: node->estimatedsize = nctypesizeof(node->etype); break; case NC_Dataset: case NC_Structure: case NC_Sequence: case NC_Grid: node->estimatedsize = size; break; default: PANIC1("Estimatesize: Unexpected node type: %d",node->nctype); break; } /* Now, take dimensions into account */ ncdims = node->array.dimensions; rank = nclistlength(ncdims); dimsize = cdftotalsize4(ncdims); totalsize = (dimsize * node->estimatedsize); if(!node->visible) totalsize = 0; #ifdef DEBUG fprintf(stderr,"estimatedsize: %s%s/%u = %lu (= %lu = %lu * %lu)\n", (node->visible?"":"*"), node->name,rank, totalsize, node->estimatedsize, size,dimsize); #endif node->estimatedsize = totalsize; return totalsize; } static void estimatesizes4(NCDRNO* drno) { size_t totalsize; CDFnode* root = drno->cdf.ddsroot; /* Recursively compute the sizes of each node */ totalsize = estimatesizes4r(drno,root); } /* For variables which have a zero size dimension, either use unlimited, or make them invisible. */ static NCerror fixzerodims4(NCDRNO* drno) { int i; NCerror ncstat = NC_NOERR; for(i=0;icdf.varnodes);i++) { CDFnode* var = (CDFnode*)nclistget(drno->cdf.varnodes,i); ncstat = fixzerodims4r(drno,var); } return ncstat; } static NCerror fixzerodims4r(NCDRNO* drno, CDFnode* node) { int i; NCerror ncstat = NC_NOERR; if(nclistlength(node->array.dimensions) > 0) { for(i=0;iarray.dimensions);i++) { CDFnode* dim = (CDFnode*)nclistget(node->array.dimensions,i); if(dim->dim.declsize == 0) { if(node->container->nctype == NC_Dataset) { /* use unlimited */ ncstat = cvtunlimiteddim(drno,dim); } else { /* make node invisible */ node->visible = 0; node->zerodim = 1; } } } } /* walk the subnodes */ for(i=0;isubnodes);i++) { CDFnode* subnode = (CDFnode*)nclistget(node->subnodes,i); ncstat = fixzerodims4r(drno,subnode); } return ncstat; } /* Convert a dimension to unlimited */ static NCerror cvtunlimiteddim(NCDRNO* drno, CDFnode* dim) { DIMFLAGSET(dim,CDFDIMUNLIM); drno->cdf.unlimited = dim; return NC_NOERR; } static void applyclientparamcontrols4(NCDRNO* drno) { NClist* params = NULL; const char* value; /* Get client parameters */ params = dapparamdecode(drno->dap.url.params); /* enable/disable caching */ value = dapparamlookup(params,"cache"); if(value == NULL) drno->controls.flags |= DFALTCACHEFLAG; else if(strlen(value) == 0) drno->controls.flags |= NCF_CACHE; else if(strcmp(value,"1")==0 || value[0] == 'y') drno->controls.flags |= NCF_CACHE; /* Set the translation base */ drno->controls.flags |= (NCF_NC4); /* No longer need params */ dapparamfree(params); }