1 /*
2  *	Copyright 2018, Unuiversity Corporation for Atmospheric Research
3  *      See netcdf/COPYRIGHT file for copying and redistribution conditions.
4  */
5 
6 #if HAVE_CONFIG_H
7 #include <config.h>
8 #endif
9 
10 #include <stdlib.h>
11 #include <string.h>
12 #include <assert.h>
13 #ifdef HAVE_UNISTD_H
14 #include <unistd.h>
15 #endif
16 
17 #include "nc3internal.h"
18 #include "netcdf_mem.h"
19 #include "rnd.h"
20 #include "ncx.h"
21 #include "ncrc.h"
22 
23 /* These have to do with version numbers. */
24 #define MAGIC_NUM_LEN 4
25 #define VER_CLASSIC 1
26 #define VER_64BIT_OFFSET 2
27 #define VER_HDF5 3
28 
29 #define NC_NUMRECS_OFFSET 4
30 
31 /* For netcdf classic */
32 #define NC_NUMRECS_EXTENT3 4
33 /* For cdf5 */
34 #define NC_NUMRECS_EXTENT5 8
35 
36 /* Internal function; breaks ncio abstraction */
37 extern int memio_extract(ncio* const nciop, size_t* sizep, void** memoryp);
38 
39 static void
free_NC3INFO(NC3_INFO * nc3)40 free_NC3INFO(NC3_INFO *nc3)
41 {
42 	if(nc3 == NULL)
43 		return;
44 	free_NC_dimarrayV(&nc3->dims);
45 	free_NC_attrarrayV(&nc3->attrs);
46 	free_NC_vararrayV(&nc3->vars);
47 	free(nc3);
48 }
49 
50 static NC3_INFO *
new_NC3INFO(const size_t * chunkp)51 new_NC3INFO(const size_t *chunkp)
52 {
53 	NC3_INFO *ncp;
54 	ncp = (NC3_INFO*)calloc(1,sizeof(NC3_INFO));
55 	if(ncp == NULL) return ncp;
56         ncp->chunk = chunkp != NULL ? *chunkp : NC_SIZEHINT_DEFAULT;
57 	/* Note that ncp->xsz is not set yet because we do not know the file format */
58 	return ncp;
59 }
60 
61 static NC3_INFO *
dup_NC3INFO(const NC3_INFO * ref)62 dup_NC3INFO(const NC3_INFO *ref)
63 {
64 	NC3_INFO *ncp;
65 	ncp = (NC3_INFO*)calloc(1,sizeof(NC3_INFO));
66 	if(ncp == NULL) return ncp;
67 
68 	if(dup_NC_dimarrayV(&ncp->dims, &ref->dims) != NC_NOERR)
69 		goto err;
70 	if(dup_NC_attrarrayV(&ncp->attrs, &ref->attrs) != NC_NOERR)
71 		goto err;
72 	if(dup_NC_vararrayV(&ncp->vars, &ref->vars) != NC_NOERR)
73 		goto err;
74 
75 	ncp->xsz = ref->xsz;
76 	ncp->begin_var = ref->begin_var;
77 	ncp->begin_rec = ref->begin_rec;
78 	ncp->recsize = ref->recsize;
79 	NC_set_numrecs(ncp, NC_get_numrecs(ref));
80 	return ncp;
81 err:
82 	free_NC3INFO(ncp);
83 	return NULL;
84 }
85 
86 
87 /*
88  *  Verify that this is a user nc_type
89  * Formerly NCcktype()
90  * Sense of the return is changed.
91  */
92 int
nc3_cktype(int mode,nc_type type)93 nc3_cktype(int mode, nc_type type)
94 {
95 #ifdef ENABLE_CDF5
96     if (mode & NC_CDF5) { /* CDF-5 format */
97         if (type >= NC_BYTE && type < NC_STRING) return NC_NOERR;
98     } else
99 #endif
100       if (mode & NC_64BIT_OFFSET) { /* CDF-2 format */
101         if (type >= NC_BYTE && type <= NC_DOUBLE) return NC_NOERR;
102     } else if ((mode & NC_64BIT_OFFSET) == 0) { /* CDF-1 format */
103         if (type >= NC_BYTE && type <= NC_DOUBLE) return NC_NOERR;
104     }
105     return(NC_EBADTYPE);
106 }
107 
108 
109 /*
110  * How many objects of 'type'
111  * will fit into xbufsize?
112  */
113 size_t
ncx_howmany(nc_type type,size_t xbufsize)114 ncx_howmany(nc_type type, size_t xbufsize)
115 {
116 	switch(type){
117 	case NC_BYTE:
118 	case NC_CHAR:
119 		return xbufsize;
120 	case NC_SHORT:
121 		return xbufsize/X_SIZEOF_SHORT;
122 	case NC_INT:
123 		return xbufsize/X_SIZEOF_INT;
124 	case NC_FLOAT:
125 		return xbufsize/X_SIZEOF_FLOAT;
126 	case NC_DOUBLE:
127 		return xbufsize/X_SIZEOF_DOUBLE;
128 	case NC_UBYTE:
129  		return xbufsize;
130 	case NC_USHORT:
131 		return xbufsize/X_SIZEOF_USHORT;
132 	case NC_UINT:
133 		return xbufsize/X_SIZEOF_UINT;
134 	case NC_INT64:
135 		return xbufsize/X_SIZEOF_LONGLONG;
136 	case NC_UINT64:
137 		return xbufsize/X_SIZEOF_ULONGLONG;
138 	default:
139 	        assert("ncx_howmany: Bad type" == 0);
140 		return(0);
141 	}
142 }
143 
144 #define	D_RNDUP(x, align) _RNDUP(x, (off_t)(align))
145 
146 /*
147  * Compute each variable's 'begin' offset,
148  * update 'begin_rec' as well.
149  */
150 static int
NC_begins(NC3_INFO * ncp,size_t h_minfree,size_t v_align,size_t v_minfree,size_t r_align)151 NC_begins(NC3_INFO* ncp,
152 	size_t h_minfree, size_t v_align,
153 	size_t v_minfree, size_t r_align)
154 {
155 	size_t ii, j;
156 	int sizeof_off_t;
157 	off_t index = 0;
158 	off_t old_ncp_begin_var;
159 	NC_var **vpp;
160 	NC_var *last = NULL;
161 	NC_var *first_var = NULL;       /* first "non-record" var */
162 
163 
164 	if(v_align == NC_ALIGN_CHUNK)
165 		v_align = ncp->chunk;
166 	if(r_align == NC_ALIGN_CHUNK)
167 		r_align = ncp->chunk;
168 
169 	if (fIsSet(ncp->flags, NC_64BIT_OFFSET) || fIsSet(ncp->flags, NC_64BIT_DATA)) {
170 	  sizeof_off_t = 8;
171 	} else {
172 	  sizeof_off_t = 4;
173 	}
174 
175 	ncp->xsz = ncx_len_NC(ncp,sizeof_off_t);
176 
177 	if(ncp->vars.nelems == 0)
178 		return NC_NOERR;
179 
180         old_ncp_begin_var = ncp->begin_var;
181 
182 	/* only (re)calculate begin_var if there is not sufficient space in header
183 	   or start of non-record variables is not aligned as requested by valign */
184 	if (ncp->begin_var < ncp->xsz + h_minfree ||
185 	    ncp->begin_var != D_RNDUP(ncp->begin_var, v_align) )
186 	{
187 	  index = (off_t) ncp->xsz;
188 	  ncp->begin_var = D_RNDUP(index, v_align);
189 	  if(ncp->begin_var < index + h_minfree)
190 	  {
191 	    ncp->begin_var = D_RNDUP(index + (off_t)h_minfree, v_align);
192 	  }
193 	}
194 
195 	if (ncp->old != NULL) {
196             /* check whether the new begin_var is smaller */
197             if (ncp->begin_var < ncp->old->begin_var)
198                 ncp->begin_var = ncp->old->begin_var;
199 	}
200 
201 	index = ncp->begin_var;
202 
203 	/* loop thru vars, first pass is for the 'non-record' vars */
204 	j = 0;
205 	vpp = ncp->vars.value;
206 	for(ii = 0; ii < ncp->vars.nelems ; ii++, vpp++)
207 	{
208 		if( IS_RECVAR(*vpp) )
209 		{
210 			/* skip record variables on this pass */
211 			continue;
212 		}
213 		if (first_var == NULL) first_var = *vpp;
214 
215 #if 0
216 fprintf(stderr, "    VAR %d %s: %ld\n", ii, (*vpp)->name->cp, (long)index);
217 #endif
218                 if( sizeof_off_t == 4 && (index > X_OFF_MAX || index < 0) )
219 		{
220                     ncp->begin_var = old_ncp_begin_var;
221 		    return NC_EVARSIZE;
222                 }
223 		(*vpp)->begin = index;
224 
225 		if (ncp->old != NULL) {
226           /* move to the next fixed variable */
227           for (; j<ncp->old->vars.nelems; j++) {
228             if (!IS_RECVAR(ncp->old->vars.value[j]))
229               break;
230           }
231 
232           if (j < ncp->old->vars.nelems) {
233             if ((*vpp)->begin < ncp->old->vars.value[j]->begin) {
234               /* the first ncp->vars.nelems fixed variables
235                  should be the same. If the new begin is smaller,
236                  reuse the old begin */
237               (*vpp)->begin = ncp->old->vars.value[j]->begin;
238               index = (*vpp)->begin;
239             }
240             j++;
241           }
242 		}
243 
244 		index += (*vpp)->len;
245 	}
246 
247 	if (ncp->old != NULL) {
248 	    /* check whether the new begin_rec is smaller */
249 	    if (ncp->begin_rec < ncp->old->begin_rec)
250 	        ncp->begin_rec = ncp->old->begin_rec;
251 	}
252 
253 	/* only (re)calculate begin_rec if there is not sufficient
254 	   space at end of non-record variables or if start of record
255 	   variables is not aligned as requested by r_align */
256 	if (ncp->begin_rec < index + v_minfree ||
257 	    ncp->begin_rec != D_RNDUP(ncp->begin_rec, r_align) )
258 	{
259 	  ncp->begin_rec = D_RNDUP(index, r_align);
260 	  if(ncp->begin_rec < index + v_minfree)
261 	  {
262 	    ncp->begin_rec = D_RNDUP(index + (off_t)v_minfree, r_align);
263 	  }
264 	}
265 
266 	if (first_var != NULL)
267 	    ncp->begin_var = first_var->begin;
268 	else
269 	    ncp->begin_var = ncp->begin_rec;
270 
271 	index = ncp->begin_rec;
272 
273 	ncp->recsize = 0;
274 
275 	/* loop thru vars, second pass is for the 'record' vars */
276 	j = 0;
277 	vpp = (NC_var **)ncp->vars.value;
278 	for(ii = 0; ii < ncp->vars.nelems; ii++, vpp++)
279 	{
280 		if( !IS_RECVAR(*vpp) )
281 		{
282 			/* skip non-record variables on this pass */
283 			continue;
284 		}
285 
286 #if 0
287 fprintf(stderr, "    REC %d %s: %ld\n", ii, (*vpp)->name->cp, (long)index);
288 #endif
289                 if( sizeof_off_t == 4 && (index > X_OFF_MAX || index < 0) )
290 		{
291                     ncp->begin_var = old_ncp_begin_var;
292 		    return NC_EVARSIZE;
293                 }
294 		(*vpp)->begin = index;
295 
296                 if (ncp->old != NULL) {
297                     /* move to the next record variable */
298                     for (; j<ncp->old->vars.nelems; j++)
299                         if (IS_RECVAR(ncp->old->vars.value[j]))
300                             break;
301                     if (j < ncp->old->vars.nelems) {
302                         if ((*vpp)->begin < ncp->old->vars.value[j]->begin)
303                             /* if the new begin is smaller, use the old begin */
304                             (*vpp)->begin = ncp->old->vars.value[j]->begin;
305                         j++;
306                     }
307                 }
308 
309 		index += (*vpp)->len;
310 		/* check if record size must fit in 32-bits */
311 #if SIZEOF_OFF_T == SIZEOF_SIZE_T && SIZEOF_SIZE_T == 4
312 		if( ncp->recsize > X_UINT_MAX - (*vpp)->len )
313 		{
314                     ncp->begin_var = old_ncp_begin_var;
315 		    return NC_EVARSIZE;
316 		}
317 #endif
318 		ncp->recsize += (*vpp)->len;
319 		last = (*vpp);
320 	}
321 
322     /*
323      * for special case (Check CDF-1 and CDF-2 file format specifications.)
324      * "A special case: Where there is exactly one record variable, we drop the
325      * requirement that each record be four-byte aligned, so in this case there
326      * is no record padding."
327      */
328     if (last != NULL) {
329         if (ncp->recsize == last->len) {
330             /* exactly one record variable, pack value */
331             ncp->recsize = *last->dsizes * last->xsz;
332         }
333     }
334 
335 	if(NC_IsNew(ncp))
336 		NC_set_numrecs(ncp, 0);
337 	return NC_NOERR;
338 }
339 
340 
341 /*
342  * Read just the numrecs member.
343  * (A relatively expensive way to do things.)
344  */
345 int
read_numrecs(NC3_INFO * ncp)346 read_numrecs(NC3_INFO *ncp)
347 {
348 	int status = NC_NOERR;
349 	const void *xp = NULL;
350 	size_t new_nrecs = 0;
351 	size_t  old_nrecs = NC_get_numrecs(ncp);
352 	size_t nc_numrecs_extent = NC_NUMRECS_EXTENT3; /* CDF-1 and CDF-2 */
353 
354 	assert(!NC_indef(ncp));
355 
356 	if (fIsSet(ncp->flags, NC_64BIT_DATA))
357 		nc_numrecs_extent = NC_NUMRECS_EXTENT5; /* CDF-5 */
358 
359 	status = ncio_get(ncp->nciop,
360 		 NC_NUMRECS_OFFSET, nc_numrecs_extent, 0, (void **)&xp);/* cast away const */
361 	if(status != NC_NOERR)
362 		return status;
363 
364 	if (fIsSet(ncp->flags, NC_64BIT_DATA)) {
365 	    unsigned long long tmp=0;
366 	    status = ncx_get_uint64(&xp, &tmp);
367 	    new_nrecs = (size_t)tmp;
368         } else
369 	    status = ncx_get_size_t(&xp, &new_nrecs);
370 
371 	(void) ncio_rel(ncp->nciop, NC_NUMRECS_OFFSET, 0);
372 
373 	if(status == NC_NOERR && old_nrecs != new_nrecs)
374 	{
375 		NC_set_numrecs(ncp, new_nrecs);
376 		fClr(ncp->flags, NC_NDIRTY);
377 	}
378 
379 	return status;
380 }
381 
382 
383 /*
384  * Write out just the numrecs member.
385  * (A relatively expensive way to do things.)
386  */
387 int
write_numrecs(NC3_INFO * ncp)388 write_numrecs(NC3_INFO *ncp)
389 {
390 	int status = NC_NOERR;
391 	void *xp = NULL;
392 	size_t nc_numrecs_extent = NC_NUMRECS_EXTENT3; /* CDF-1 and CDF-2 */
393 
394 	assert(!NC_readonly(ncp));
395 	assert(!NC_indef(ncp));
396 
397 	if (fIsSet(ncp->flags, NC_64BIT_DATA))
398 	    nc_numrecs_extent = NC_NUMRECS_EXTENT5; /* CDF-5 */
399 
400 	status = ncio_get(ncp->nciop,
401 		 NC_NUMRECS_OFFSET, nc_numrecs_extent, RGN_WRITE, &xp);
402 	if(status != NC_NOERR)
403 		return status;
404 
405 	{
406 		const size_t nrecs = NC_get_numrecs(ncp);
407 		if (fIsSet(ncp->flags, NC_64BIT_DATA))
408 		    status = ncx_put_uint64(&xp, (unsigned long long)nrecs);
409 		else
410  		    status = ncx_put_size_t(&xp, &nrecs);
411 	}
412 
413 	(void) ncio_rel(ncp->nciop, NC_NUMRECS_OFFSET, RGN_MODIFIED);
414 
415 	if(status == NC_NOERR)
416 		fClr(ncp->flags, NC_NDIRTY);
417 
418 	return status;
419 }
420 
421 
422 /*
423  * Read in the header
424  * It is expensive.
425  */
426 static int
read_NC(NC3_INFO * ncp)427 read_NC(NC3_INFO *ncp)
428 {
429 	int status = NC_NOERR;
430 
431 	free_NC_dimarrayV(&ncp->dims);
432 	free_NC_attrarrayV(&ncp->attrs);
433 	free_NC_vararrayV(&ncp->vars);
434 
435 	status = nc_get_NC(ncp);
436 
437 	if(status == NC_NOERR)
438 		fClr(ncp->flags, NC_NDIRTY | NC_HDIRTY);
439 
440 	return status;
441 }
442 
443 
444 /*
445  * Write out the header
446  */
447 static int
write_NC(NC3_INFO * ncp)448 write_NC(NC3_INFO *ncp)
449 {
450 	int status = NC_NOERR;
451 
452 	assert(!NC_readonly(ncp));
453 
454 	status = ncx_put_NC(ncp, NULL, 0, 0);
455 
456 	if(status == NC_NOERR)
457 		fClr(ncp->flags, NC_NDIRTY | NC_HDIRTY);
458 
459 	return status;
460 }
461 
462 
463 /*
464  * Write the header or the numrecs if necessary.
465  */
466 int
NC_sync(NC3_INFO * ncp)467 NC_sync(NC3_INFO *ncp)
468 {
469 	assert(!NC_readonly(ncp));
470 
471 	if(NC_hdirty(ncp))
472 	{
473 		return write_NC(ncp);
474 	}
475 	/* else */
476 
477 	if(NC_ndirty(ncp))
478 	{
479 		return write_numrecs(ncp);
480 	}
481 	/* else */
482 
483 	return NC_NOERR;
484 }
485 
486 
487 /*
488  * Initialize the 'non-record' variables.
489  */
490 static int
fillerup(NC3_INFO * ncp)491 fillerup(NC3_INFO *ncp)
492 {
493 	int status = NC_NOERR;
494 	size_t ii;
495 	NC_var **varpp;
496 
497 	assert(!NC_readonly(ncp));
498 
499 	/* loop thru vars */
500 	varpp = ncp->vars.value;
501 	for(ii = 0; ii < ncp->vars.nelems; ii++, varpp++)
502 	{
503 		if ((*varpp)->no_fill) continue;
504 
505 		if(IS_RECVAR(*varpp))
506 		{
507 			/* skip record variables */
508 			continue;
509 		}
510 
511 		status = fill_NC_var(ncp, *varpp, (*varpp)->len, 0);
512 		if(status != NC_NOERR)
513 			break;
514 	}
515 	return status;
516 }
517 
518 /* Begin endef */
519 
520 /*
521  */
522 static int
fill_added_recs(NC3_INFO * gnu,NC3_INFO * old)523 fill_added_recs(NC3_INFO *gnu, NC3_INFO *old)
524 {
525 	NC_var ** const gnu_varpp = (NC_var **)gnu->vars.value;
526 
527 	const int old_nrecs = (int) NC_get_numrecs(old);
528 	int recno = 0;
529 	NC_var **vpp = gnu_varpp;
530 	NC_var *const *const end = &vpp[gnu->vars.nelems];
531 	int numrecvars = 0;
532 
533 	/* Determine if there is only one record variable.  If so, we
534 	   must treat as a special case because there's no record padding */
535 	for(; vpp < end; vpp++) {
536 	    if(IS_RECVAR(*vpp)) {
537 		numrecvars++;
538 	    }
539 	}
540 
541 	for(; recno < old_nrecs; recno++)
542 	    {
543 		int varid = (int)old->vars.nelems;
544 		for(; varid < (int)gnu->vars.nelems; varid++)
545 		    {
546 			const NC_var *const gnu_varp = *(gnu_varpp + varid);
547 
548 			if (gnu_varp->no_fill) continue;
549 
550 			if(!IS_RECVAR(gnu_varp))
551 			    {
552 				/* skip non-record variables */
553 				continue;
554 			    }
555 			/* else */
556 			{
557 			    size_t varsize = numrecvars == 1 ? gnu->recsize :  gnu_varp->len;
558 			    const int status = fill_NC_var(gnu, gnu_varp, varsize, recno);
559 			    if(status != NC_NOERR)
560 				return status;
561 			}
562 		    }
563 	    }
564 	return NC_NOERR;
565 }
566 
567 /*
568  */
569 static int
fill_added(NC3_INFO * gnu,NC3_INFO * old)570 fill_added(NC3_INFO *gnu, NC3_INFO *old)
571 {
572 	NC_var ** const gnu_varpp = (NC_var **)gnu->vars.value;
573 	int varid = (int)old->vars.nelems;
574 
575 	for(; varid < (int)gnu->vars.nelems; varid++)
576 	{
577 		const NC_var *const gnu_varp = *(gnu_varpp + varid);
578 
579 		if (gnu_varp->no_fill) continue;
580 
581 		if(IS_RECVAR(gnu_varp))
582 		{
583 			/* skip record variables */
584 			continue;
585 		}
586 		/* else */
587 		{
588 		const int status = fill_NC_var(gnu, gnu_varp, gnu_varp->len, 0);
589 		if(status != NC_NOERR)
590 			return status;
591 		}
592 	}
593 
594 	return NC_NOERR;
595 }
596 
597 
598 /*
599  * Move the records "out".
600  * Fill as needed.
601  */
602 static int
move_recs_r(NC3_INFO * gnu,NC3_INFO * old)603 move_recs_r(NC3_INFO *gnu, NC3_INFO *old)
604 {
605 	int status;
606 	int recno;
607 	int varid;
608 	NC_var **gnu_varpp = (NC_var **)gnu->vars.value;
609 	NC_var **old_varpp = (NC_var **)old->vars.value;
610 	NC_var *gnu_varp;
611 	NC_var *old_varp;
612 	off_t gnu_off;
613 	off_t old_off;
614 	const size_t old_nrecs = NC_get_numrecs(old);
615 
616 	/* Don't parallelize this loop */
617 	for(recno = (int)old_nrecs -1; recno >= 0; recno--)
618 	{
619 	/* Don't parallelize this loop */
620 	for(varid = (int)old->vars.nelems -1; varid >= 0; varid--)
621 	{
622 		gnu_varp = *(gnu_varpp + varid);
623 		if(!IS_RECVAR(gnu_varp))
624 		{
625 			/* skip non-record variables on this pass */
626 			continue;
627 		}
628 		/* else */
629 
630 		/* else, a pre-existing variable */
631 		old_varp = *(old_varpp + varid);
632 		gnu_off = gnu_varp->begin + (off_t)(gnu->recsize * recno);
633 		old_off = old_varp->begin + (off_t)(old->recsize * recno);
634 
635 		if(gnu_off == old_off)
636 			continue; 	/* nothing to do */
637 
638 		assert(gnu_off > old_off);
639 
640 		status = ncio_move(gnu->nciop, gnu_off, old_off,
641 			 old_varp->len, 0);
642 
643 		if(status != NC_NOERR)
644 			return status;
645 
646 	}
647 	}
648 
649 	NC_set_numrecs(gnu, old_nrecs);
650 
651 	return NC_NOERR;
652 }
653 
654 
655 /*
656  * Move the "non record" variables "out".
657  * Fill as needed.
658  */
659 static int
move_vars_r(NC3_INFO * gnu,NC3_INFO * old)660 move_vars_r(NC3_INFO *gnu, NC3_INFO *old)
661 {
662 	int err, status=NC_NOERR;
663 	int varid;
664 	NC_var **gnu_varpp = (NC_var **)gnu->vars.value;
665 	NC_var **old_varpp = (NC_var **)old->vars.value;
666 	NC_var *gnu_varp;
667 	NC_var *old_varp;
668 	off_t gnu_off;
669 	off_t old_off;
670 
671 	/* Don't parallelize this loop */
672 	for(varid = (int)old->vars.nelems -1;
673 		 varid >= 0; varid--)
674 	{
675 		gnu_varp = *(gnu_varpp + varid);
676 		if(IS_RECVAR(gnu_varp))
677 		{
678 			/* skip record variables on this pass */
679 			continue;
680 		}
681 		/* else */
682 
683 		old_varp = *(old_varpp + varid);
684 		gnu_off = gnu_varp->begin;
685 		old_off = old_varp->begin;
686 
687 		if (gnu_off > old_off) {
688 		    err = ncio_move(gnu->nciop, gnu_off, old_off,
689 			               old_varp->len, 0);
690 		    if (status == NC_NOERR) status = err;
691 		}
692 	}
693 	return status;
694 }
695 
696 
697 /*
698  * Given a valid ncp, return NC_EVARSIZE if any variable has a bad len
699  * (product of non-rec dim sizes too large), else return NC_NOERR.
700  */
701 int
NC_check_vlens(NC3_INFO * ncp)702 NC_check_vlens(NC3_INFO *ncp)
703 {
704     NC_var **vpp;
705     /* maximum permitted variable size (or size of one record's worth
706        of a record variable) in bytes.  This is different for format 1
707        and format 2. */
708     long long vlen_max;
709     size_t ii;
710     size_t large_vars_count;
711     size_t rec_vars_count;
712     int last = 0;
713 
714     if(ncp->vars.nelems == 0)
715 	return NC_NOERR;
716 
717     if (fIsSet(ncp->flags,NC_64BIT_DATA)) /* CDF-5 */
718 	vlen_max = X_INT64_MAX - 3; /* "- 3" handles rounded-up size */
719     else if (fIsSet(ncp->flags,NC_64BIT_OFFSET) && sizeof(off_t) > 4)
720 	/* CDF2 format and LFS */
721 	vlen_max = X_UINT_MAX - 3; /* "- 3" handles rounded-up size */
722     else /* CDF1 format */
723 	vlen_max = X_INT_MAX - 3;
724 
725     /* Loop through vars, first pass is for non-record variables.   */
726     large_vars_count = 0;
727     rec_vars_count = 0;
728     vpp = ncp->vars.value;
729     for (ii = 0; ii < ncp->vars.nelems; ii++, vpp++) {
730 	if( !IS_RECVAR(*vpp) ) {
731 	    last = 0;
732 	    if( NC_check_vlen(*vpp, vlen_max) == 0 ) {
733                 if (fIsSet(ncp->flags,NC_64BIT_DATA)) /* too big for CDF-5 */
734                     return NC_EVARSIZE;
735 		large_vars_count++;
736 		last = 1;
737 	    }
738 	} else {
739 	  rec_vars_count++;
740 	}
741     }
742     /* OK if last non-record variable size too large, since not used to
743        compute an offset */
744     if( large_vars_count > 1) { /* only one "too-large" variable allowed */
745       return NC_EVARSIZE;
746     }
747     /* and it has to be the last one */
748     if( large_vars_count == 1 && last == 0) {
749       return NC_EVARSIZE;
750     }
751     if( rec_vars_count > 0 ) {
752 	/* and if it's the last one, there can't be any record variables */
753 	if( large_vars_count == 1 && last == 1) {
754 	    return NC_EVARSIZE;
755 	}
756 	/* Loop through vars, second pass is for record variables.   */
757 	large_vars_count = 0;
758 	vpp = ncp->vars.value;
759 	for (ii = 0; ii < ncp->vars.nelems; ii++, vpp++) {
760 	    if( IS_RECVAR(*vpp) ) {
761 		last = 0;
762 		if( NC_check_vlen(*vpp, vlen_max) == 0 ) {
763                     if (fIsSet(ncp->flags,NC_64BIT_DATA)) /* too big for CDF-5 */
764                         return NC_EVARSIZE;
765 		    large_vars_count++;
766 		    last = 1;
767 		}
768 	    }
769 	}
770 	/* OK if last record variable size too large, since not used to
771 	   compute an offset */
772 	if( large_vars_count > 1) { /* only one "too-large" variable allowed */
773 	    return NC_EVARSIZE;
774 	}
775 	/* and it has to be the last one */
776 	if( large_vars_count == 1 && last == 0) {
777 	    return NC_EVARSIZE;
778 	}
779     }
780     return NC_NOERR;
781 }
782 
783 /*----< NC_check_voffs() >---------------------------------------------------*/
784 /*
785  * Given a valid ncp, check whether the file starting offsets (begin) of all
786  * variables follows the same increasing order as they were defined.
787  */
788 int
NC_check_voffs(NC3_INFO * ncp)789 NC_check_voffs(NC3_INFO *ncp)
790 {
791     size_t i;
792     off_t prev_off;
793     NC_var *varp;
794 
795     if (ncp->vars.nelems == 0) return NC_NOERR;
796 
797     /* Loop through vars, first pass is for non-record variables */
798     prev_off = ncp->begin_var;
799     for (i=0; i<ncp->vars.nelems; i++) {
800         varp = ncp->vars.value[i];
801         if (IS_RECVAR(varp)) continue;
802 
803         if (varp->begin < prev_off) {
804 #if 0
805             fprintf(stderr,"Variable \"%s\" begin offset (%lld) is less than previous variable end offset (%lld)\n", varp->name->cp, varp->begin, prev_off);
806 #endif
807             return NC_ENOTNC;
808         }
809         prev_off = varp->begin + varp->len;
810     }
811 
812     if (ncp->begin_rec < prev_off) {
813 #if 0
814         fprintf(stderr,"Record variable section begin offset (%lld) is less than fix-sized variable section end offset (%lld)\n", varp->begin, prev_off);
815 #endif
816         return NC_ENOTNC;
817     }
818 
819     /* Loop through vars, second pass is for record variables */
820     prev_off = ncp->begin_rec;
821     for (i=0; i<ncp->vars.nelems; i++) {
822         varp = ncp->vars.value[i];
823         if (!IS_RECVAR(varp)) continue;
824 
825         if (varp->begin < prev_off) {
826 #if 0
827             fprintf(stderr,"Variable \"%s\" begin offset (%lld) is less than previous variable end offset (%lld)\n", varp->name->cp, varp->begin, prev_off);
828 #endif
829             return NC_ENOTNC;
830         }
831         prev_off = varp->begin + varp->len;
832     }
833 
834     return NC_NOERR;
835 }
836 
837 /*
838  *  End define mode.
839  *  Common code for ncendef, ncclose(endef)
840  *  Flushes I/O buffers.
841  */
842 static int
NC_endef(NC3_INFO * ncp,size_t h_minfree,size_t v_align,size_t v_minfree,size_t r_align)843 NC_endef(NC3_INFO *ncp,
844 	size_t h_minfree, size_t v_align,
845 	size_t v_minfree, size_t r_align)
846 {
847 	int status = NC_NOERR;
848 
849 	assert(!NC_readonly(ncp));
850 	assert(NC_indef(ncp));
851 
852 	status = NC_check_vlens(ncp);
853 	if(status != NC_NOERR)
854 	    return status;
855 	status = NC_begins(ncp, h_minfree, v_align, v_minfree, r_align);
856 	if(status != NC_NOERR)
857 	    return status;
858 	status = NC_check_voffs(ncp);
859 	if(status != NC_NOERR)
860 	    return status;
861 
862 	if(ncp->old != NULL)
863 	{
864 		/* a plain redef, not a create */
865 		assert(!NC_IsNew(ncp));
866 		assert(fIsSet(ncp->flags, NC_INDEF));
867 		assert(ncp->begin_rec >= ncp->old->begin_rec);
868 		assert(ncp->begin_var >= ncp->old->begin_var);
869 
870 		if(ncp->vars.nelems != 0)
871 		{
872 		if(ncp->begin_rec > ncp->old->begin_rec)
873 		{
874 			status = move_recs_r(ncp, ncp->old);
875 			if(status != NC_NOERR)
876 				return status;
877 			if(ncp->begin_var > ncp->old->begin_var)
878 			{
879 				status = move_vars_r(ncp, ncp->old);
880 				if(status != NC_NOERR)
881 					return status;
882 			}
883 			/* else if (ncp->begin_var == ncp->old->begin_var) { NOOP } */
884 		}
885 		else
886                 {
887 			/* due to fixed variable alignment, it is possible that header
888                            grows but begin_rec did not change */
889 			if(ncp->begin_var > ncp->old->begin_var)
890 			{
891 				status = move_vars_r(ncp, ncp->old);
892 				if(status != NC_NOERR)
893 					return status;
894 			}
895 		 	/* Even if (ncp->begin_rec == ncp->old->begin_rec)
896 			   and     (ncp->begin_var == ncp->old->begin_var)
897 			   might still have added a new record variable */
898 		        if(ncp->recsize > ncp->old->recsize)
899 			{
900 			        status = move_recs_r(ncp, ncp->old);
901 				if(status != NC_NOERR)
902 				      return status;
903 			}
904 		}
905 		}
906 	}
907 
908 	status = write_NC(ncp);
909 	if(status != NC_NOERR)
910 		return status;
911 
912 	/* fill mode is now per variable */
913 	{
914 		if(NC_IsNew(ncp))
915 		{
916 			status = fillerup(ncp);
917 			if(status != NC_NOERR)
918 				return status;
919 
920 		}
921 		else if(ncp->old == NULL ? 0
922                                          : (ncp->vars.nelems > ncp->old->vars.nelems))
923           {
924             status = fill_added(ncp, ncp->old);
925             if(status != NC_NOERR)
926               return status;
927             status = fill_added_recs(ncp, ncp->old);
928             if(status != NC_NOERR)
929               return status;
930           }
931 	}
932 
933 	if(ncp->old != NULL)
934 	{
935 		free_NC3INFO(ncp->old);
936 		ncp->old = NULL;
937 	}
938 
939 	fClr(ncp->flags, NC_CREAT | NC_INDEF);
940 
941 	return ncio_sync(ncp->nciop);
942 }
943 
944 
945 /*
946  * Compute the expected size of the file.
947  */
948 int
NC_calcsize(const NC3_INFO * ncp,off_t * calcsizep)949 NC_calcsize(const NC3_INFO *ncp, off_t *calcsizep)
950 {
951 	NC_var **vpp = (NC_var **)ncp->vars.value;
952 	NC_var *const *const end = &vpp[ncp->vars.nelems];
953 	NC_var *last_fix = NULL;	/* last "non-record" var */
954 	int numrecvars = 0;	/* number of record variables */
955 
956 	if(ncp->vars.nelems == 0) { /* no non-record variables and
957 				       no record variables */
958 	    *calcsizep = ncp->xsz; /* size of header */
959 	    return NC_NOERR;
960 	}
961 
962 	for( /*NADA*/; vpp < end; vpp++) {
963 	    if(IS_RECVAR(*vpp)) {
964 		numrecvars++;
965 	    } else {
966 		last_fix = *vpp;
967 	    }
968 	}
969 
970 	if(numrecvars == 0) {
971 	    off_t varsize;
972 	    assert(last_fix != NULL);
973 	    varsize = last_fix->len;
974 	    if(last_fix->len == X_UINT_MAX) { /* huge last fixed var */
975 		int i;
976 		varsize = 1;
977   	        for(i = 0; i < last_fix->ndims; i++ ) {
978                 varsize *= (last_fix->shape ? last_fix->shape[i] : 1);
979 		    }
980 	    }
981 	    *calcsizep = last_fix->begin + varsize;
982 	    /*last_var = last_fix;*/
983 	} else {       /* we have at least one record variable */
984 	    *calcsizep = ncp->begin_rec + ncp->numrecs * ncp->recsize;
985 	}
986 
987 	return NC_NOERR;
988 }
989 
990 /* Public */
991 
992 #if 0 /* no longer needed */
993 int NC3_new_nc(NC3_INFO** ncpp)
994 {
995 	NC *nc;
996 	NC3_INFO* nc3;
997 
998 	ncp = (NC *) malloc(sizeof(NC));
999 	if(ncp == NULL)
1000 		return NC_ENOMEM;
1001 	(void) memset(ncp, 0, sizeof(NC));
1002 
1003 	ncp->xsz = MIN_NC_XSZ;
1004 	assert(ncp->xsz == ncx_len_NC(ncp,0));
1005 
1006         if(ncpp) *ncpp = ncp;
1007         return NC_NOERR;
1008 
1009 }
1010 #endif
1011 
1012 /* WARNING: SIGNATURE CHANGE */
1013 int
NC3_create(const char * path,int ioflags,size_t initialsz,int basepe,size_t * chunksizehintp,void * parameters,const NC_Dispatch * dispatch,int ncid)1014 NC3_create(const char *path, int ioflags, size_t initialsz, int basepe,
1015            size_t *chunksizehintp, void *parameters,
1016            const NC_Dispatch *dispatch, int ncid)
1017 {
1018 	int status = NC_NOERR;
1019 	void *xp = NULL;
1020 	int sizeof_off_t = 0;
1021         NC *nc;
1022 	NC3_INFO* nc3 = NULL;
1023 
1024         /* Find NC struct for this file. */
1025         if ((status = NC_check_id(ncid, &nc)))
1026             return status;
1027 
1028 	/* Create our specific NC3_INFO instance */
1029 	nc3 = new_NC3INFO(chunksizehintp);
1030 
1031 #if ALWAYS_NC_SHARE /* DEBUG */
1032 	fSet(ioflags, NC_SHARE);
1033 #endif
1034 
1035 	/*
1036 	 * Only pe 0 is valid
1037 	 */
1038 	if(basepe != 0) {
1039             if(nc3) free(nc3);
1040             return NC_EINVAL;
1041         }
1042 	assert(nc3->flags == 0);
1043 
1044 	/* Now we can set min size */
1045 	if (fIsSet(ioflags, NC_64BIT_DATA))
1046 	    nc3->xsz = MIN_NC5_XSZ; /* CDF-5 has minimum 16 extra bytes */
1047 	else
1048 	    nc3->xsz = MIN_NC3_XSZ;
1049 
1050 	if (fIsSet(ioflags, NC_64BIT_OFFSET)) {
1051 	    fSet(nc3->flags, NC_64BIT_OFFSET);
1052 	    sizeof_off_t = 8;
1053 	} else if (fIsSet(ioflags, NC_64BIT_DATA)) {
1054 	    fSet(nc3->flags, NC_64BIT_DATA);
1055 	    sizeof_off_t = 8;
1056 	} else {
1057 	  sizeof_off_t = 4;
1058 	}
1059 
1060 	assert(nc3->xsz == ncx_len_NC(nc3,sizeof_off_t));
1061 
1062         status =  ncio_create(path, ioflags, initialsz,
1063 			      0, nc3->xsz, &nc3->chunk, NULL,
1064 			      &nc3->nciop, &xp);
1065 	if(status != NC_NOERR)
1066 	{
1067 		/* translate error status */
1068 		if(status == EEXIST)
1069 			status = NC_EEXIST;
1070 		goto unwind_alloc;
1071 	}
1072 
1073 	fSet(nc3->flags, NC_CREAT);
1074 
1075 	if(fIsSet(nc3->nciop->ioflags, NC_SHARE))
1076 	{
1077 		/*
1078 		 * NC_SHARE implies sync up the number of records as well.
1079 		 * (File format version one.)
1080 		 * Note that other header changes are not shared
1081 		 * automatically.  Some sort of IPC (external to this package)
1082 		 * would be used to trigger a call to nc_sync().
1083 		 */
1084 		fSet(nc3->flags, NC_NSYNC);
1085 	}
1086 
1087 	status = ncx_put_NC(nc3, &xp, sizeof_off_t, nc3->xsz);
1088 	if(status != NC_NOERR)
1089 		goto unwind_ioc;
1090 
1091 	if(chunksizehintp != NULL)
1092 		*chunksizehintp = nc3->chunk;
1093 
1094 	/* Link nc3 and nc */
1095         NC3_DATA_SET(nc,nc3);
1096 	nc->int_ncid = nc3->nciop->fd;
1097 
1098 	return NC_NOERR;
1099 
1100 unwind_ioc:
1101 	if(nc3 != NULL) {
1102 	    (void) ncio_close(nc3->nciop, 1); /* N.B.: unlink */
1103 	    nc3->nciop = NULL;
1104 	}
1105 	/*FALLTHRU*/
1106 unwind_alloc:
1107 	free_NC3INFO(nc3);
1108 	if(nc)
1109             NC3_DATA_SET(nc,NULL);
1110 	return status;
1111 }
1112 
1113 #if 0
1114 /* This function sets a default create flag that will be logically
1115    or'd to whatever flags are passed into nc_create for all future
1116    calls to nc_create.
1117    Valid default create flags are NC_64BIT_OFFSET, NC_CDF5, NC_CLOBBER,
1118    NC_LOCK, NC_SHARE. */
1119 int
1120 nc_set_default_format(int format, int *old_formatp)
1121 {
1122     /* Return existing format if desired. */
1123     if (old_formatp)
1124       *old_formatp = default_create_format;
1125 
1126     /* Make sure only valid format is set. */
1127 #ifdef USE_NETCDF4
1128     if (format != NC_FORMAT_CLASSIC && format != NC_FORMAT_64BIT_OFFSET &&
1129 	format != NC_FORMAT_NETCDF4 && format != NC_FORMAT_NETCDF4_CLASSIC)
1130       return NC_EINVAL;
1131 #else
1132     if (format != NC_FORMAT_CLASSIC && format != NC_FORMAT_64BIT_OFFSET
1133 #ifdef ENABLE_CDF5
1134         && format != NC_FORMAT_CDF5
1135 #endif
1136         )
1137       return NC_EINVAL;
1138 #endif
1139     default_create_format = format;
1140     return NC_NOERR;
1141 }
1142 #endif
1143 
1144 int
NC3_open(const char * path,int ioflags,int basepe,size_t * chunksizehintp,void * parameters,const NC_Dispatch * dispatch,int ncid)1145 NC3_open(const char *path, int ioflags, int basepe, size_t *chunksizehintp,
1146          void *parameters, const NC_Dispatch *dispatch, int ncid)
1147 {
1148 	int status;
1149 	NC3_INFO* nc3 = NULL;
1150         NC *nc;
1151 
1152         /* Find NC struct for this file. */
1153         if ((status = NC_check_id(ncid, &nc)))
1154             return status;
1155 
1156 	/* Create our specific NC3_INFO instance */
1157 	nc3 = new_NC3INFO(chunksizehintp);
1158 
1159 #if ALWAYS_NC_SHARE /* DEBUG */
1160 	fSet(ioflags, NC_SHARE);
1161 #endif
1162 
1163 	/*
1164 	 * Only pe 0 is valid.
1165 	 */
1166 	if(basepe != 0) {
1167             if(nc3) {
1168                 free(nc3);
1169                 nc3 = NULL;
1170             }
1171             status = NC_EINVAL;
1172             goto unwind_alloc;
1173         }
1174 
1175 #ifdef ENABLE_BYTERANGE
1176     /* If the model specified the use of byte-ranges, then signal by
1177        a temporary hack using one of the flags in the ioflags.
1178     */
1179     if(NC_testmode(path,"bytes"))
1180         ioflags |= NC_HTTP;
1181 #endif /*ENABLE_BYTERANGE*/
1182 
1183         status = ncio_open(path, ioflags, 0, 0, &nc3->chunk, parameters,
1184 			       &nc3->nciop, NULL);
1185 	if(status)
1186 		goto unwind_alloc;
1187 
1188 	assert(nc3->flags == 0);
1189 
1190 	if(fIsSet(nc3->nciop->ioflags, NC_SHARE))
1191 	{
1192 		/*
1193 		 * NC_SHARE implies sync up the number of records as well.
1194 		 * (File format version one.)
1195 		 * Note that other header changes are not shared
1196 		 * automatically.  Some sort of IPC (external to this package)
1197 		 * would be used to trigger a call to nc_sync().
1198 		 */
1199 		fSet(nc3->flags, NC_NSYNC);
1200 	}
1201 
1202 	status = nc_get_NC(nc3);
1203 	if(status != NC_NOERR)
1204 		goto unwind_ioc;
1205 
1206 	if(chunksizehintp != NULL)
1207 		*chunksizehintp = nc3->chunk;
1208 
1209 	/* Link nc3 and nc */
1210         NC3_DATA_SET(nc,nc3);
1211 	nc->int_ncid = nc3->nciop->fd;
1212 
1213 	return NC_NOERR;
1214 
1215 unwind_ioc:
1216 	if(nc3) {
1217     	    (void) ncio_close(nc3->nciop, 0);
1218 	    nc3->nciop = NULL;
1219 	}
1220 	/*FALLTHRU*/
1221 unwind_alloc:
1222 	free_NC3INFO(nc3);
1223 	if(nc)
1224             NC3_DATA_SET(nc,NULL);
1225 	return status;
1226 }
1227 
1228 int
NC3__enddef(int ncid,size_t h_minfree,size_t v_align,size_t v_minfree,size_t r_align)1229 NC3__enddef(int ncid,
1230 	size_t h_minfree, size_t v_align,
1231 	size_t v_minfree, size_t r_align)
1232 {
1233 	int status;
1234 	NC *nc;
1235 	NC3_INFO* nc3;
1236 
1237 	status = NC_check_id(ncid, &nc);
1238 	if(status != NC_NOERR)
1239 	  return status;
1240 	nc3 = NC3_DATA(nc);
1241 
1242 	if(!NC_indef(nc3))
1243 		return(NC_ENOTINDEFINE);
1244 
1245 	return (NC_endef(nc3, h_minfree, v_align, v_minfree, r_align));
1246 }
1247 
1248 /*
1249  * In data mode, same as ncclose.
1250  * In define mode, restore previous definition.
1251  * In create, remove the file.
1252  */
1253 int
NC3_abort(int ncid)1254 NC3_abort(int ncid)
1255 {
1256 	int status;
1257 	NC *nc;
1258 	NC3_INFO* nc3;
1259 	int doUnlink = 0;
1260 
1261 	status = NC_check_id(ncid, &nc);
1262 	if(status != NC_NOERR)
1263 	    return status;
1264 	nc3 = NC3_DATA(nc);
1265 
1266 	doUnlink = NC_IsNew(nc3);
1267 
1268 	if(nc3->old != NULL)
1269 	{
1270 		/* a plain redef, not a create */
1271 		assert(!NC_IsNew(nc3));
1272 		assert(fIsSet(nc3->flags, NC_INDEF));
1273 		free_NC3INFO(nc3->old);
1274 		nc3->old = NULL;
1275 		fClr(nc3->flags, NC_INDEF);
1276 	}
1277 	else if(!NC_readonly(nc3))
1278 	{
1279 		status = NC_sync(nc3);
1280 		if(status != NC_NOERR)
1281 			return status;
1282 	}
1283 
1284 
1285 	(void) ncio_close(nc3->nciop, doUnlink);
1286 	nc3->nciop = NULL;
1287 
1288 	free_NC3INFO(nc3);
1289 	if(nc)
1290             NC3_DATA_SET(nc,NULL);
1291 
1292 	return NC_NOERR;
1293 }
1294 
1295 int
NC3_close(int ncid,void * params)1296 NC3_close(int ncid, void* params)
1297 {
1298 	int status = NC_NOERR;
1299 	NC *nc;
1300 	NC3_INFO* nc3;
1301 
1302 	status = NC_check_id(ncid, &nc);
1303 	if(status != NC_NOERR)
1304 	    return status;
1305 	nc3 = NC3_DATA(nc);
1306 
1307 	if(NC_indef(nc3))
1308 	{
1309 		status = NC_endef(nc3, 0, 1, 0, 1); /* TODO: defaults */
1310 		if(status != NC_NOERR )
1311 		{
1312 			(void) NC3_abort(ncid);
1313 			return status;
1314 		}
1315 	}
1316 	else if(!NC_readonly(nc3))
1317 	{
1318 		status = NC_sync(nc3);
1319 		/* flush buffers before any filesize comparisons */
1320 		(void) ncio_sync(nc3->nciop);
1321 	}
1322 
1323 	/*
1324 	 * If file opened for writing and filesize is less than
1325 	 * what it should be (due to previous use of NOFILL mode),
1326 	 * pad it to correct size, as reported by NC_calcsize().
1327 	 */
1328 	if (status == NC_NOERR) {
1329 	    off_t filesize; 	/* current size of open file */
1330 	    off_t calcsize;	/* calculated file size, from header */
1331 	    status = ncio_filesize(nc3->nciop, &filesize);
1332 	    if(status != NC_NOERR)
1333 		return status;
1334 	    status = NC_calcsize(nc3, &calcsize);
1335 	    if(status != NC_NOERR)
1336 		return status;
1337 	    if(filesize < calcsize && !NC_readonly(nc3)) {
1338 		status = ncio_pad_length(nc3->nciop, calcsize);
1339 		if(status != NC_NOERR)
1340 		    return status;
1341 	    }
1342 	}
1343 
1344 	if(params != NULL && (nc->mode & NC_INMEMORY) != 0) {
1345 	    NC_memio* memio = (NC_memio*)params;
1346             /* Extract the final memory size &/or contents */
1347             status = memio_extract(nc3->nciop,&memio->size,&memio->memory);
1348         }
1349 
1350 	(void) ncio_close(nc3->nciop, 0);
1351 	nc3->nciop = NULL;
1352 
1353 	free_NC3INFO(nc3);
1354         NC3_DATA_SET(nc,NULL);
1355 
1356 	return status;
1357 }
1358 
1359 int
NC3_redef(int ncid)1360 NC3_redef(int ncid)
1361 {
1362 	int status;
1363 	NC *nc;
1364 	NC3_INFO* nc3;
1365 
1366 	status = NC_check_id(ncid, &nc);
1367 	if(status != NC_NOERR)
1368 		return status;
1369 	nc3 = NC3_DATA(nc);
1370 
1371 	if(NC_readonly(nc3))
1372 		return NC_EPERM;
1373 
1374 	if(NC_indef(nc3))
1375 		return NC_EINDEFINE;
1376 
1377 
1378 	if(fIsSet(nc3->nciop->ioflags, NC_SHARE))
1379 	{
1380 		/* read in from disk */
1381 		status = read_NC(nc3);
1382 		if(status != NC_NOERR)
1383 			return status;
1384 	}
1385 
1386 	nc3->old = dup_NC3INFO(nc3);
1387 	if(nc3->old == NULL)
1388 		return NC_ENOMEM;
1389 
1390 	fSet(nc3->flags, NC_INDEF);
1391 
1392 	return NC_NOERR;
1393 }
1394 
1395 
1396 int
NC3_inq(int ncid,int * ndimsp,int * nvarsp,int * nattsp,int * xtendimp)1397 NC3_inq(int ncid,
1398 	int *ndimsp,
1399 	int *nvarsp,
1400 	int *nattsp,
1401 	int *xtendimp)
1402 {
1403 	int status;
1404 	NC *nc;
1405 	NC3_INFO* nc3;
1406 
1407 	status = NC_check_id(ncid, &nc);
1408 	if(status != NC_NOERR)
1409 		return status;
1410 	nc3 = NC3_DATA(nc);
1411 
1412 	if(ndimsp != NULL)
1413 		*ndimsp = (int) nc3->dims.nelems;
1414 	if(nvarsp != NULL)
1415 		*nvarsp = (int) nc3->vars.nelems;
1416 	if(nattsp != NULL)
1417 		*nattsp = (int) nc3->attrs.nelems;
1418 	if(xtendimp != NULL)
1419 		*xtendimp = find_NC_Udim(&nc3->dims, NULL);
1420 
1421 	return NC_NOERR;
1422 }
1423 
1424 int
NC3_inq_unlimdim(int ncid,int * xtendimp)1425 NC3_inq_unlimdim(int ncid, int *xtendimp)
1426 {
1427 	int status;
1428 	NC *nc;
1429 	NC3_INFO* nc3;
1430 
1431 	status = NC_check_id(ncid, &nc);
1432 	if(status != NC_NOERR)
1433 		return status;
1434 	nc3 = NC3_DATA(nc);
1435 
1436 	if(xtendimp != NULL)
1437 		*xtendimp = find_NC_Udim(&nc3->dims, NULL);
1438 
1439 	return NC_NOERR;
1440 }
1441 
1442 int
NC3_sync(int ncid)1443 NC3_sync(int ncid)
1444 {
1445 	int status;
1446 	NC *nc;
1447 	NC3_INFO* nc3;
1448 
1449 	status = NC_check_id(ncid, &nc);
1450 	if(status != NC_NOERR)
1451 		return status;
1452 	nc3 = NC3_DATA(nc);
1453 
1454 	if(NC_indef(nc3))
1455 		return NC_EINDEFINE;
1456 
1457 	if(NC_readonly(nc3))
1458 	{
1459 		return read_NC(nc3);
1460 	}
1461 	/* else, read/write */
1462 
1463 	status = NC_sync(nc3);
1464 	if(status != NC_NOERR)
1465 		return status;
1466 
1467 	status = ncio_sync(nc3->nciop);
1468 	if(status != NC_NOERR)
1469 		return status;
1470 
1471 #ifdef USE_FSYNC
1472 	/* may improve concurrent access, but slows performance if
1473 	 * called frequently */
1474 #ifndef _WIN32
1475 	status = fsync(nc3->nciop->fd);
1476 #else
1477 	status = _commit(nc3->nciop->fd);
1478 #endif	/* _WIN32 */
1479 #endif	/* USE_FSYNC */
1480 
1481 	return status;
1482 }
1483 
1484 
1485 int
NC3_set_fill(int ncid,int fillmode,int * old_mode_ptr)1486 NC3_set_fill(int ncid,
1487 	int fillmode, int *old_mode_ptr)
1488 {
1489 	int i, status;
1490 	NC *nc;
1491 	NC3_INFO* nc3;
1492 	int oldmode;
1493 
1494 	status = NC_check_id(ncid, &nc);
1495 	if(status != NC_NOERR)
1496 		return status;
1497 	nc3 = NC3_DATA(nc);
1498 
1499 	if(NC_readonly(nc3))
1500 		return NC_EPERM;
1501 
1502 	oldmode = fIsSet(nc3->flags, NC_NOFILL) ? NC_NOFILL : NC_FILL;
1503 
1504 	if(fillmode == NC_NOFILL)
1505 	{
1506 		fSet(nc3->flags, NC_NOFILL);
1507 	}
1508 	else if(fillmode == NC_FILL)
1509 	{
1510 		if(fIsSet(nc3->flags, NC_NOFILL))
1511 		{
1512 			/*
1513 			 * We are changing back to fill mode
1514 			 * so do a sync
1515 			 */
1516 			status = NC_sync(nc3);
1517 			if(status != NC_NOERR)
1518 				return status;
1519 		}
1520 		fClr(nc3->flags, NC_NOFILL);
1521 	}
1522 	else
1523 	{
1524 		return NC_EINVAL; /* Invalid fillmode */
1525 	}
1526 
1527 	if(old_mode_ptr != NULL)
1528 		*old_mode_ptr = oldmode;
1529 
1530 	/* loop thru all variables to set/overwrite its fill mode */
1531 	for (i=0; i<nc3->vars.nelems; i++)
1532 		nc3->vars.value[i]->no_fill = (fillmode == NC_NOFILL);
1533 
1534 	/* once the file's fill mode is set, any new variables defined after
1535 	 * this call will check NC_dofill(nc3) and set their no_fill accordingly.
1536 	 * See NC3_def_var() */
1537 
1538 	return NC_NOERR;
1539 }
1540 
1541 /**
1542  * Return the file format.
1543  *
1544  * \param ncid the ID of the open file.
1545 
1546  * \param formatp a pointer that gets the format. Ignored if NULL.
1547  *
1548  * \returns NC_NOERR No error.
1549  * \returns NC_EBADID Bad ncid.
1550  * \internal
1551  * \author Ed Hartnett, Dennis Heimbigner
1552  */
1553 int
NC3_inq_format(int ncid,int * formatp)1554 NC3_inq_format(int ncid, int *formatp)
1555 {
1556    int status;
1557    NC *nc;
1558    NC3_INFO* nc3;
1559 
1560    status = NC_check_id(ncid, &nc);
1561    if(status != NC_NOERR)
1562       return status;
1563    nc3 = NC3_DATA(nc);
1564 
1565    /* Why even call this function with no format pointer? */
1566    if (!formatp)
1567       return NC_NOERR;
1568 
1569    /* only need to check for netCDF-3 variants, since this is never called for netCDF-4 files */
1570 #ifdef ENABLE_CDF5
1571    if (fIsSet(nc3->flags, NC_64BIT_DATA))
1572       *formatp = NC_FORMAT_CDF5;
1573    else
1574 #endif
1575       if (fIsSet(nc3->flags, NC_64BIT_OFFSET))
1576          *formatp = NC_FORMAT_64BIT_OFFSET;
1577       else
1578          *formatp = NC_FORMAT_CLASSIC;
1579    return NC_NOERR;
1580 }
1581 
1582 /**
1583  * Return the extended format (i.e. the dispatch model), plus the mode
1584  * associated with an open file.
1585  *
1586  * \param ncid the ID of the open file.
1587  * \param formatp a pointer that gets the extended format. Note that
1588  * this is not the same as the format provided by nc_inq_format(). The
1589  * extended format indicates the dispatch layer model. Classic, 64-bit
1590  * offset, and CDF5 files all have an extended format of
1591  * ::NC_FORMATX_NC3. Ignored if NULL.
1592  * \param modep a pointer that gets the open/create mode associated with
1593  * this file. Ignored if NULL.
1594  *
1595  * \returns NC_NOERR No error.
1596  * \returns NC_EBADID Bad ncid.
1597  * \internal
1598  * \author Dennis Heimbigner
1599  */
1600 int
NC3_inq_format_extended(int ncid,int * formatp,int * modep)1601 NC3_inq_format_extended(int ncid, int *formatp, int *modep)
1602 {
1603    int status;
1604    NC *nc;
1605 
1606    status = NC_check_id(ncid, &nc);
1607    if(status != NC_NOERR)
1608       return status;
1609    if(formatp) *formatp = NC_FORMATX_NC3;
1610    if(modep) *modep = nc->mode;
1611    return NC_NOERR;
1612 }
1613 
1614 /**
1615  * Determine name and size of netCDF type. This netCDF-4 function
1616  * proved so popular that a netCDF-classic version is provided. You're
1617  * welcome.
1618  *
1619  * \param ncid The ID of an open file.
1620  * \param typeid The ID of a netCDF type.
1621  * \param name Pointer that will get the name of the type. Maximum
1622  * size will be NC_MAX_NAME. Ignored if NULL.
1623  * \param size Pointer that will get size of type in bytes. Ignored if
1624  * null.
1625  *
1626  * \returns NC_NOERR No error.
1627  * \returns NC_EBADID Bad ncid.
1628  * \returns NC_EBADTYPE Bad typeid.
1629  * \internal
1630  * \author Ed Hartnett
1631  */
1632 int
NC3_inq_type(int ncid,nc_type typeid,char * name,size_t * size)1633 NC3_inq_type(int ncid, nc_type typeid, char *name, size_t *size)
1634 {
1635    NC *ncp;
1636    int stat = NC_check_id(ncid, &ncp);
1637    if (stat != NC_NOERR)
1638       return stat;
1639 
1640    if(typeid < NC_BYTE || typeid > NC_STRING)
1641       return NC_EBADTYPE;
1642 
1643    /* Give the user the values they want. */
1644    if (name)
1645       strcpy(name, NC_atomictypename(typeid));
1646    if (size)
1647       *size = NC_atomictypelen(typeid);
1648 
1649    return NC_NOERR;
1650 }
1651 
1652 /**
1653  * This is an obsolete form of nc_delete(), supported for backwards
1654  * compatibility.
1655  *
1656  * @param path Filename to delete.
1657  * @param basepe Must be 0.
1658  *
1659  * @return ::NC_NOERR No error.
1660  * @return ::NC_EIO Couldn't delete file.
1661  * @return ::NC_EINVAL Invaliod basepe. Must be 0.
1662  * @author Glenn Davis, Ed Hartnett
1663  */
1664 int
nc_delete_mp(const char * path,int basepe)1665 nc_delete_mp(const char * path, int basepe)
1666 {
1667 	NC *nc;
1668 	int status;
1669 	int ncid;
1670 
1671 	status = nc_open(path,NC_NOWRITE,&ncid);
1672         if(status) return status;
1673 
1674 	status = NC_check_id(ncid,&nc);
1675         if(status) return status;
1676 
1677 	/*
1678 	 * Only pe 0 is valid.
1679 	 */
1680 	if(basepe != 0)
1681 		return NC_EINVAL;
1682 
1683 	(void) nc_close(ncid);
1684 	if(unlink(path) == -1) {
1685 	    return NC_EIO;	/* No more specific error code is appropriate */
1686 	}
1687 	return NC_NOERR;
1688 }
1689 
1690 int
nc_delete(const char * path)1691 nc_delete(const char * path)
1692 {
1693         return nc_delete_mp(path, 0);
1694 }
1695 
1696 /*----< NC3_inq_default_fill_value() >---------------------------------------*/
1697 /* copy the default fill value to the memory space pointed by fillp */
1698 int
NC3_inq_default_fill_value(int xtype,void * fillp)1699 NC3_inq_default_fill_value(int xtype, void *fillp)
1700 {
1701     if (fillp == NULL) return NC_NOERR;
1702 
1703     switch(xtype) {
1704         case NC_CHAR   :               *(char*)fillp = NC_FILL_CHAR;   break;
1705         case NC_BYTE   :        *(signed char*)fillp = NC_FILL_BYTE;   break;
1706         case NC_SHORT  :              *(short*)fillp = NC_FILL_SHORT;  break;
1707         case NC_INT    :                *(int*)fillp = NC_FILL_INT;    break;
1708         case NC_FLOAT  :              *(float*)fillp = NC_FILL_FLOAT;  break;
1709         case NC_DOUBLE :             *(double*)fillp = NC_FILL_DOUBLE; break;
1710         case NC_UBYTE  :      *(unsigned char*)fillp = NC_FILL_UBYTE;  break;
1711         case NC_USHORT :     *(unsigned short*)fillp = NC_FILL_USHORT; break;
1712         case NC_UINT   :       *(unsigned int*)fillp = NC_FILL_UINT;   break;
1713         case NC_INT64  :          *(long long*)fillp = NC_FILL_INT64;  break;
1714         case NC_UINT64 : *(unsigned long long*)fillp = NC_FILL_UINT64; break;
1715         default : return NC_EBADTYPE;
1716     }
1717     return NC_NOERR;
1718 }
1719 
1720 
1721 /*----< NC3_inq_var_fill() >-------------------------------------------------*/
1722 /* inquire the fill value of a variable */
1723 int
NC3_inq_var_fill(const NC_var * varp,void * fill_value)1724 NC3_inq_var_fill(const NC_var *varp, void *fill_value)
1725 {
1726     NC_attr **attrpp = NULL;
1727 
1728     if (fill_value == NULL) return NC_EINVAL;
1729 
1730     /*
1731      * find fill value
1732      */
1733     attrpp = NC_findattr(&varp->attrs, _FillValue);
1734     if ( attrpp != NULL ) {
1735         const void *xp;
1736         /* User defined fill value */
1737         if ( (*attrpp)->type != varp->type || (*attrpp)->nelems != 1 )
1738             return NC_EBADTYPE;
1739 
1740         xp = (*attrpp)->xvalue;
1741         /* value stored in xvalue is in external representation, may need byte-swap */
1742         switch(varp->type) {
1743             case NC_CHAR:   return ncx_getn_text               (&xp, 1,               (char*)fill_value);
1744             case NC_BYTE:   return ncx_getn_schar_schar        (&xp, 1,        (signed char*)fill_value);
1745             case NC_UBYTE:  return ncx_getn_uchar_uchar        (&xp, 1,      (unsigned char*)fill_value);
1746             case NC_SHORT:  return ncx_getn_short_short        (&xp, 1,              (short*)fill_value);
1747             case NC_USHORT: return ncx_getn_ushort_ushort      (&xp, 1,     (unsigned short*)fill_value);
1748             case NC_INT:    return ncx_getn_int_int            (&xp, 1,                (int*)fill_value);
1749             case NC_UINT:   return ncx_getn_uint_uint          (&xp, 1,       (unsigned int*)fill_value);
1750             case NC_FLOAT:  return ncx_getn_float_float        (&xp, 1,              (float*)fill_value);
1751             case NC_DOUBLE: return ncx_getn_double_double      (&xp, 1,             (double*)fill_value);
1752             case NC_INT64:  return ncx_getn_longlong_longlong  (&xp, 1,          (long long*)fill_value);
1753             case NC_UINT64: return ncx_getn_ulonglong_ulonglong(&xp, 1, (unsigned long long*)fill_value);
1754             default: return NC_EBADTYPE;
1755         }
1756     }
1757     else {
1758         /* use the default */
1759         switch(varp->type){
1760             case NC_CHAR:                *(char *)fill_value = NC_FILL_CHAR;
1761                  break;
1762             case NC_BYTE:          *(signed char *)fill_value = NC_FILL_BYTE;
1763                  break;
1764             case NC_SHORT:               *(short *)fill_value = NC_FILL_SHORT;
1765                  break;
1766             case NC_INT:                   *(int *)fill_value = NC_FILL_INT;
1767                  break;
1768             case NC_UBYTE:       *(unsigned char *)fill_value = NC_FILL_UBYTE;
1769                  break;
1770             case NC_USHORT:     *(unsigned short *)fill_value = NC_FILL_USHORT;
1771                  break;
1772             case NC_UINT:         *(unsigned int *)fill_value = NC_FILL_UINT;
1773                  break;
1774             case NC_INT64:           *(long long *)fill_value = NC_FILL_INT64;
1775                  break;
1776             case NC_UINT64: *(unsigned long long *)fill_value = NC_FILL_UINT64;
1777                  break;
1778             case NC_FLOAT:               *(float *)fill_value = NC_FILL_FLOAT;
1779                  break;
1780             case NC_DOUBLE:             *(double *)fill_value = NC_FILL_DOUBLE;
1781                  break;
1782             default:
1783                  return NC_EINVAL;
1784         }
1785     }
1786     return NC_NOERR;
1787 }
1788