1 /*
2  *	Copyright 1996, University 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 <stdio.h>
12 #include <string.h>
13 #include <assert.h>
14 #include "nc3internal.h"
15 #include "rnd.h"
16 #include "ncx.h"
17 
18 /*
19  * This module defines the external representation
20  * of the "header" of a netcdf version one file and
21  * the version two variant that uses 64-bit file
22  * offsets instead of the 32-bit file offsets in version
23  * one files.
24  * For each of the components of the NC structure,
25  * There are (static) ncx_len_XXX(), v1h_put_XXX()
26  * and v1h_get_XXX() functions. These define the
27  * external representation of the components.
28  * The exported entry points for the whole NC structure
29  * are built up from these.
30  */
31 
32 
33 /*
34  * "magic number" at beginning of file: 0x43444601 (big endian)
35  * assert(sizeof(ncmagic) % X_ALIGN == 0);
36  */
37 static const schar ncmagic[] = {'C', 'D', 'F', 0x02};
38 static const schar ncmagic1[] = {'C', 'D', 'F', 0x01};
39 static const schar ncmagic5[] = {'C', 'D', 'F', 0x05};
40 
41 /*
42  * v1hs == "Version 1 Header Stream"
43  *
44  * The netcdf file version 1 header is
45  * of unknown and potentially unlimited size.
46  * So, we don't know how much to get() on
47  * the initial read. We build a stream, 'v1hs'
48  * on top of ncio to do the header get.
49  */
50 typedef struct v1hs {
51 	ncio *nciop;
52 	off_t offset;	/* argument to nciop->get() */
53 	size_t extent;	/* argument to nciop->get() */
54 	int flags;	/* set to RGN_WRITE for write */
55         int version;    /* format variant: NC_FORMAT_CLASSIC, NC_FORMAT_64BIT_OFFSET or NC_FORMAT_CDF5 */
56 	void *base;	/* beginning of current buffer */
57 	void *pos;	/* current position in buffer */
58 	void *end;	/* end of current buffer = base + extent */
59 } v1hs;
60 
61 
62 /*
63  * Release the stream, invalidate buffer
64  */
65 static int
rel_v1hs(v1hs * gsp)66 rel_v1hs(v1hs *gsp)
67 {
68 	int status;
69 	if(gsp->offset == OFF_NONE || gsp->base == NULL)
70         return NC_NOERR;
71 	status = ncio_rel(gsp->nciop, gsp->offset,
72 			 gsp->flags == RGN_WRITE ? RGN_MODIFIED : 0);
73 	gsp->end = NULL;
74 	gsp->pos = NULL;
75 	gsp->base = NULL;
76 	return status;
77 }
78 
79 
80 /*
81  * Release the current chunk and get the next one.
82  * Also used for initialization when gsp->base == NULL.
83  */
84 static int
fault_v1hs(v1hs * gsp,size_t extent)85 fault_v1hs(v1hs *gsp, size_t extent)
86 {
87 	int status;
88 
89 	if(gsp->base != NULL)
90 	{
91 		const ptrdiff_t incr = (char *)gsp->pos - (char *)gsp->base;
92 		status = rel_v1hs(gsp);
93 		if(status)
94 			return status;
95 		gsp->offset += incr;
96 	}
97 
98 	if(extent > gsp->extent)
99 		gsp->extent = extent;
100 
101 	status = ncio_get(gsp->nciop,
102 		 	gsp->offset, gsp->extent,
103 			gsp->flags, &gsp->base);
104 	if(status)
105 		return status;
106 
107 	gsp->pos = gsp->base;
108 
109 	gsp->end = (char *)gsp->base + gsp->extent;
110     return NC_NOERR;
111 }
112 
113 
114 /*
115  * Ensure that 'nextread' bytes are available.
116  */
117 static int
check_v1hs(v1hs * gsp,size_t nextread)118 check_v1hs(v1hs *gsp, size_t nextread)
119 {
120 
121 #if 0 /* DEBUG */
122 fprintf(stderr, "nextread %lu, remaining %lu\n",
123 	(unsigned long)nextread,
124 	(unsigned long)((char *)gsp->end - (char *)gsp->pos));
125 #endif
126     if((char *)gsp->pos + nextread <= (char *)gsp->end)
127 	return NC_NOERR;
128 
129     return fault_v1hs(gsp, nextread);
130 }
131 
132 /* End v1hs */
133 
134 /* Write a size_t to the header */
135 static int
v1h_put_size_t(v1hs * psp,const size_t * sp)136 v1h_put_size_t(v1hs *psp, const size_t *sp)
137 {
138 	int status;
139 	if (psp->version == 5) /* all integers in CDF-5 are 64 bits */
140 		status = check_v1hs(psp, X_SIZEOF_INT64);
141 	else
142 		status = check_v1hs(psp, X_SIZEOF_SIZE_T);
143 	if(status != NC_NOERR)
144  		return status;
145         if (psp->version == 5) {
146                 unsigned long long tmp = (unsigned long long) (*sp);
147 		return ncx_put_uint64(&psp->pos, tmp);
148         }
149         else
150 	    return ncx_put_size_t(&psp->pos, sp);
151 }
152 
153 /* Read a size_t from the header */
154 static int
v1h_get_size_t(v1hs * gsp,size_t * sp)155 v1h_get_size_t(v1hs *gsp, size_t *sp)
156 {
157 	int status;
158 	if (gsp->version == 5) /* all integers in CDF-5 are 64 bits */
159 		status = check_v1hs(gsp, X_SIZEOF_INT64);
160 	else
161 		status = check_v1hs(gsp, X_SIZEOF_SIZE_T);
162 	if(status != NC_NOERR)
163 		return status;
164         if (gsp->version == 5) {
165 		unsigned long long tmp=0;
166 		status = ncx_get_uint64((const void **)(&gsp->pos), &tmp);
167 		*sp = (size_t)tmp;
168 		return status;
169         }
170         else
171 	    return ncx_get_size_t((const void **)(&gsp->pos), sp);
172 }
173 
174 /* Begin nc_type */
175 
176 #define X_SIZEOF_NC_TYPE X_SIZEOF_INT
177 
178 /* Write a nc_type to the header */
179 static int
v1h_put_nc_type(v1hs * psp,const nc_type * typep)180 v1h_put_nc_type(v1hs *psp, const nc_type *typep)
181 {
182     const unsigned int itype = (unsigned int) *typep;
183     int status = check_v1hs(psp, X_SIZEOF_INT);
184     if(status != NC_NOERR) return status;
185     status =  ncx_put_uint32(&psp->pos, itype);
186     return status;
187 }
188 
189 
190 /* Read a nc_type from the header */
191 static int
v1h_get_nc_type(v1hs * gsp,nc_type * typep)192 v1h_get_nc_type(v1hs *gsp, nc_type *typep)
193 {
194     unsigned int type = 0;
195     int status = check_v1hs(gsp, X_SIZEOF_INT);
196     if(status != NC_NOERR) return status;
197     status =  ncx_get_uint32((const void**)(&gsp->pos), &type);
198     if(status != NC_NOERR)
199 		return status;
200 
201 	assert(type == NC_BYTE
202 		|| type == NC_CHAR
203 		|| type == NC_SHORT
204 		|| type == NC_INT
205 		|| type == NC_FLOAT
206 		|| type == NC_DOUBLE
207 		|| type == NC_UBYTE
208 		|| type == NC_USHORT
209 		|| type == NC_UINT
210 		|| type == NC_INT64
211 		|| type == NC_UINT64
212 		|| type == NC_STRING);
213 
214 	/* else */
215 	*typep = (nc_type) type;
216 
217     return NC_NOERR;
218 }
219 
220 /* End nc_type */
221 /* Begin NCtype (internal tags) */
222 
223 #define X_SIZEOF_NCTYPE X_SIZEOF_INT
224 
225 /* Write a NCtype to the header */
226 static int
v1h_put_NCtype(v1hs * psp,NCtype type)227 v1h_put_NCtype(v1hs *psp, NCtype type)
228 {
229     const unsigned int itype = (unsigned int) type;
230     int status = check_v1hs(psp, X_SIZEOF_INT);
231     if(status != NC_NOERR) return status;
232     status = ncx_put_uint32(&psp->pos, itype);
233     return status;
234 }
235 
236 /* Read a NCtype from the header */
237 static int
v1h_get_NCtype(v1hs * gsp,NCtype * typep)238 v1h_get_NCtype(v1hs *gsp, NCtype *typep)
239 {
240     unsigned int type = 0;
241     int status = check_v1hs(gsp, X_SIZEOF_INT);
242     if(status != NC_NOERR) return status;
243     status =  ncx_get_uint32((const void**)(&gsp->pos), &type);
244     if(status != NC_NOERR) return status;
245     /* else */
246     *typep = (NCtype) type;
247     return NC_NOERR;
248 }
249 
250 /* End NCtype */
251 /* Begin NC_string */
252 
253 /*
254  * How much space will the xdr'd string take.
255  * Formerly
256 NC_xlen_string(cdfstr)
257  */
258 static size_t
ncx_len_NC_string(const NC_string * ncstrp,int version)259 ncx_len_NC_string(const NC_string *ncstrp, int version)
260 {
261 	size_t sz = (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_INT; /* nchars */
262 
263 	assert(ncstrp != NULL);
264 
265 	if(ncstrp->nchars != 0)
266 	{
267 #if 0
268 		assert(ncstrp->nchars % X_ALIGN == 0);
269 		sz += ncstrp->nchars;
270 #else
271 		sz += _RNDUP(ncstrp->nchars, X_ALIGN);
272 #endif
273 	}
274 	return sz;
275 }
276 
277 
278 /* Write a NC_string to the header */
279 static int
v1h_put_NC_string(v1hs * psp,const NC_string * ncstrp)280 v1h_put_NC_string(v1hs *psp, const NC_string *ncstrp)
281 {
282 	int status;
283 
284 #if 0
285 	assert(ncstrp->nchars % X_ALIGN == 0);
286 #endif
287 
288 	status = v1h_put_size_t(psp, &ncstrp->nchars);
289     if(status != NC_NOERR)
290 		return status;
291 	status = check_v1hs(psp, _RNDUP(ncstrp->nchars, X_ALIGN));
292     if(status != NC_NOERR)
293 		return status;
294 	status = ncx_pad_putn_text(&psp->pos, ncstrp->nchars, ncstrp->cp);
295     if(status != NC_NOERR)
296 		return status;
297 
298     return NC_NOERR;
299 }
300 
301 
302 /* Read a NC_string from the header */
303 static int
v1h_get_NC_string(v1hs * gsp,NC_string ** ncstrpp)304 v1h_get_NC_string(v1hs *gsp, NC_string **ncstrpp)
305 {
306 	int status = 0;
307 	size_t nchars = 0;
308 	NC_string *ncstrp = NULL;
309 #if USE_STRICT_NULL_BYTE_HEADER_PADDING
310         size_t padding = 0;
311 #endif /* USE_STRICT_NULL_BYTE_HEADER_PADDING */
312 
313 	status = v1h_get_size_t(gsp, &nchars);
314 	if(status != NC_NOERR)
315 		return status;
316 
317 	ncstrp = new_NC_string(nchars, NULL);
318 	if(ncstrp == NULL)
319 	{
320 		return NC_ENOMEM;
321 	}
322 
323 #if 0
324 /* assert(ncstrp->nchars == nchars || ncstrp->nchars - nchars < X_ALIGN); */
325 	assert(ncstrp->nchars % X_ALIGN == 0);
326 	status = check_v1hs(gsp, ncstrp->nchars);
327 #else
328 
329 	status = check_v1hs(gsp, _RNDUP(ncstrp->nchars, X_ALIGN));
330 #endif
331 	if(status != NC_NOERR)
332 		goto unwind_alloc;
333 
334 	status = ncx_pad_getn_text((const void **)(&gsp->pos),
335 		 nchars, ncstrp->cp);
336 	if(status != NC_NOERR)
337 		goto unwind_alloc;
338 
339 #if USE_STRICT_NULL_BYTE_HEADER_PADDING
340 	padding = _RNDUP(X_SIZEOF_CHAR * ncstrp->nchars, X_ALIGN)
341 		- X_SIZEOF_CHAR * ncstrp->nchars;
342 
343 	if (padding > 0) {
344 		/* CDF specification: Header padding uses null (\x00) bytes. */
345 		char pad[X_ALIGN-1];
346 		memset(pad, 0, X_ALIGN-1);
347 		if (memcmp((char*)gsp->pos-padding, pad, padding) != 0) {
348 			free_NC_string(ncstrp);
349 			return NC_ENULLPAD;
350 		}
351 	}
352 #endif
353 
354 	*ncstrpp = ncstrp;
355 
356 	return NC_NOERR;
357 
358 unwind_alloc:
359 	free_NC_string(ncstrp);
360 	return status;
361 }
362 
363 /* End NC_string */
364 /* Begin NC_dim */
365 
366 /*
367  * How much space will the xdr'd dim take.
368  * Formerly
369 NC_xlen_dim(dpp)
370  */
371 static size_t
ncx_len_NC_dim(const NC_dim * dimp,int version)372 ncx_len_NC_dim(const NC_dim *dimp, int version)
373 {
374 	size_t sz;
375 
376 	assert(dimp != NULL);
377 
378 	sz = ncx_len_NC_string(dimp->name, version);
379 	sz += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T;
380 
381 	return(sz);
382 }
383 
384 
385 /* Write a NC_dim to the header */
386 static int
v1h_put_NC_dim(v1hs * psp,const NC_dim * dimp)387 v1h_put_NC_dim(v1hs *psp, const NC_dim *dimp)
388 {
389 	int status;
390 
391 	status = v1h_put_NC_string(psp, dimp->name);
392     if(status != NC_NOERR)
393 		return status;
394 
395 	status = v1h_put_size_t(psp, &dimp->size);
396     if(status != NC_NOERR)
397 		return status;
398 
399     return NC_NOERR;
400 }
401 
402 /* Read a NC_dim from the header */
403 static int
v1h_get_NC_dim(v1hs * gsp,NC_dim ** dimpp)404 v1h_get_NC_dim(v1hs *gsp, NC_dim **dimpp)
405 {
406 	int status;
407 	NC_string *ncstrp;
408 	NC_dim *dimp;
409 
410 	status = v1h_get_NC_string(gsp, &ncstrp);
411     if(status != NC_NOERR)
412 		return status;
413 
414 	dimp = new_x_NC_dim(ncstrp);
415 	if(dimp == NULL)
416 	{
417 		status = NC_ENOMEM;
418 		goto unwind_name;
419 	}
420 
421 	status = v1h_get_size_t(gsp, &dimp->size);
422     if(status != NC_NOERR)
423 	{
424 		free_NC_dim(dimp); /* frees name */
425 		return status;
426 	}
427 
428 	*dimpp = dimp;
429 
430     return NC_NOERR;
431 
432 unwind_name:
433 	free_NC_string(ncstrp);
434 	return status;
435 }
436 
437 
438 /* How much space in the header is required for this NC_dimarray? */
439 static size_t
ncx_len_NC_dimarray(const NC_dimarray * ncap,int version)440 ncx_len_NC_dimarray(const NC_dimarray *ncap, int version)
441 {
442 	size_t xlen = X_SIZEOF_NCTYPE;	/* type */
443 	xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* count */
444 	if(ncap == NULL)
445 		return xlen;
446 	/* else */
447 	{
448 		const NC_dim **dpp = (const NC_dim **)ncap->value;
449 		const NC_dim *const *const end = &dpp[ncap->nelems];
450 		for(  /*NADA*/; dpp < end; dpp++)
451 		{
452 			xlen += ncx_len_NC_dim(*dpp,version);
453 		}
454 	}
455 	return xlen;
456 }
457 
458 
459 /* Write a NC_dimarray to the header */
460 static int
v1h_put_NC_dimarray(v1hs * psp,const NC_dimarray * ncap)461 v1h_put_NC_dimarray(v1hs *psp, const NC_dimarray *ncap)
462 {
463 	int status;
464 
465 	assert(psp != NULL);
466 
467 	if(ncap == NULL
468 #if 1
469 		/* Backward:
470 		 * This clause is for 'byte for byte'
471 		 * backward compatibility.
472 		 * Strickly speaking, it is 'bug for bug'.
473 		 */
474 		|| ncap->nelems == 0
475 #endif
476 		)
477 	{
478 		/*
479 		 * Handle empty netcdf
480 		 */
481 		const size_t nosz = 0;
482 
483 		status = v1h_put_NCtype(psp, NC_UNSPECIFIED);
484         if(status != NC_NOERR)
485 			return status;
486 		status = v1h_put_size_t(psp, &nosz);
487         if(status != NC_NOERR)
488 			return status;
489         return NC_NOERR;
490 	}
491 	/* else */
492 
493 	status = v1h_put_NCtype(psp, NC_DIMENSION);
494     if(status != NC_NOERR)
495 		return status;
496 	status = v1h_put_size_t(psp, &ncap->nelems);
497     if(status != NC_NOERR)
498 		return status;
499 
500 	{
501 		const NC_dim **dpp = (const NC_dim **)ncap->value;
502 		const NC_dim *const *const end = &dpp[ncap->nelems];
503 		for( /*NADA*/; dpp < end; dpp++)
504 		{
505 			status = v1h_put_NC_dim(psp, *dpp);
506 			if(status)
507 				return status;
508 		}
509 	}
510     return NC_NOERR;
511 }
512 
513 
514 /* Read a NC_dimarray from the header */
515 static int
v1h_get_NC_dimarray(v1hs * gsp,NC_dimarray * ncap)516 v1h_get_NC_dimarray(v1hs *gsp, NC_dimarray *ncap)
517 {
518 	int status;
519 	NCtype type = NC_UNSPECIFIED;
520 
521 	assert(gsp != NULL && gsp->pos != NULL);
522 	assert(ncap != NULL);
523 	assert(ncap->value == NULL);
524 
525 	status = v1h_get_NCtype(gsp, &type);
526     if(status != NC_NOERR)
527 		return status;
528 
529 	status = v1h_get_size_t(gsp, &ncap->nelems);
530     if(status != NC_NOERR)
531 		return status;
532 
533 	if(ncap->nelems == 0)
534         return NC_NOERR;
535 	/* else */
536 	if(type != NC_DIMENSION)
537 		return EINVAL;
538 
539 	ncap->value = (NC_dim **) calloc(1,ncap->nelems * sizeof(NC_dim *));
540 	if(ncap->value == NULL)
541 		return NC_ENOMEM;
542 	ncap->nalloc = ncap->nelems;
543 
544 	ncap->hashmap = NC_hashmapnew(ncap->nelems);
545 
546 	{
547 		NC_dim **dpp = ncap->value;
548 		NC_dim *const *const end = &dpp[ncap->nelems];
549 		for( /*NADA*/; dpp < end; dpp++)
550 		{
551 			status = v1h_get_NC_dim(gsp, dpp);
552 			if(status)
553 			{
554 				ncap->nelems = (size_t)(dpp - ncap->value);
555 				free_NC_dimarrayV(ncap);
556 				return status;
557 			}
558 			{
559 			  int dimid = (size_t)(dpp - ncap->value);
560 			  NC_hashmapadd(ncap->hashmap, (uintptr_t)dimid, (*dpp)->name->cp,strlen((*dpp)->name->cp));
561 			}
562 		}
563 	}
564 
565     return NC_NOERR;
566 }
567 
568 
569 /* End NC_dim */
570 /* Begin NC_attr */
571 
572 
573 /*
574  * How much space will 'attrp' take in external representation?
575  * Formerly
576 NC_xlen_attr(app)
577  */
578 static size_t
ncx_len_NC_attr(const NC_attr * attrp,int version)579 ncx_len_NC_attr(const NC_attr *attrp, int version)
580 {
581 	size_t sz;
582 
583 	assert(attrp != NULL);
584 
585 	sz = ncx_len_NC_string(attrp->name, version);
586 	sz += X_SIZEOF_NC_TYPE; /* type */
587 	sz += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* nelems */
588 	sz += attrp->xsz;
589 
590 	return(sz);
591 }
592 
593 
594 #undef MIN
595 #define MIN(mm,nn) (((mm) < (nn)) ? (mm) : (nn))
596 
597 /*----< ncmpix_len_nctype() >------------------------------------------------*/
598 /* return the length of external data type */
599 static int
ncmpix_len_nctype(nc_type type)600 ncmpix_len_nctype(nc_type type) {
601     switch(type) {
602         case NC_BYTE:
603         case NC_CHAR:
604         case NC_UBYTE:  return X_SIZEOF_CHAR;
605         case NC_SHORT:  return X_SIZEOF_SHORT;
606         case NC_USHORT: return X_SIZEOF_USHORT;
607         case NC_INT:    return X_SIZEOF_INT;
608         case NC_UINT:   return X_SIZEOF_UINT;
609         case NC_FLOAT:  return X_SIZEOF_FLOAT;
610         case NC_DOUBLE: return X_SIZEOF_DOUBLE;
611         case NC_INT64:  return X_SIZEOF_INT64;
612         case NC_UINT64: return X_SIZEOF_UINT64;
613         default: fprintf(stderr,"ncmpix_len_nctype bad type %d\n",type);
614                  assert(0);
615     }
616     return 0;
617 }
618 
619 /*
620  * Put the values of an attribute
621  * The loop is necessary since attrp->nelems
622  * could potentially be quite large.
623  */
624 static int
v1h_put_NC_attrV(v1hs * psp,const NC_attr * attrp)625 v1h_put_NC_attrV(v1hs *psp, const NC_attr *attrp)
626 {
627 	int status = 0;
628 	const size_t perchunk =  psp->extent;
629 	size_t remaining = attrp->xsz;
630 	void *value = attrp->xvalue;
631 	size_t nbytes = 0, padding = 0;
632 
633 	assert(psp->extent % X_ALIGN == 0);
634 
635 	do {
636 		nbytes = MIN(perchunk, remaining);
637 
638 		status = check_v1hs(psp, nbytes);
639 		if(status != NC_NOERR)
640 			return status;
641 
642 		(void) memcpy(psp->pos, value, nbytes);
643 
644 		psp->pos = (void *)((char *)psp->pos + nbytes);
645 		value = (void *)((char *)value + nbytes);
646         	remaining -= nbytes;
647 
648 	} while(remaining != 0);
649 
650 
651 	padding = attrp->xsz - ncmpix_len_nctype(attrp->type) * attrp->nelems;
652 	if (padding > 0) {
653 		/* CDF specification: Header padding uses null (\x00) bytes. */
654 		memset((char*)psp->pos-padding, 0, padding);
655 	}
656 
657 	return NC_NOERR;
658 }
659 
660 /* Write a NC_attr to the header */
661 static int
v1h_put_NC_attr(v1hs * psp,const NC_attr * attrp)662 v1h_put_NC_attr(v1hs *psp, const NC_attr *attrp)
663 {
664 	int status;
665 
666 	status = v1h_put_NC_string(psp, attrp->name);
667     if(status != NC_NOERR)
668 		return status;
669 
670 	status = v1h_put_nc_type(psp, &attrp->type);
671     if(status != NC_NOERR)
672 		return status;
673 
674 	status = v1h_put_size_t(psp, &attrp->nelems);
675     if(status != NC_NOERR)
676 		return status;
677 
678 	status = v1h_put_NC_attrV(psp, attrp);
679     if(status != NC_NOERR)
680 		return status;
681 
682     return NC_NOERR;
683 }
684 
685 
686 /*
687  * Get the values of an attribute
688  * The loop is necessary since attrp->nelems
689  * could potentially be quite large.
690  */
691 static int
v1h_get_NC_attrV(v1hs * gsp,NC_attr * attrp)692 v1h_get_NC_attrV(v1hs *gsp, NC_attr *attrp)
693 {
694 	int status;
695 	const size_t perchunk =  gsp->extent;
696 	size_t remaining = attrp->xsz;
697 	void *value = attrp->xvalue;
698 	size_t nget;
699 #if USE_STRICT_NULL_BYTE_HEADER_PADDING
700 	size_t padding;
701 #endif /* USE_STRICT_NULL_BYTE_HEADER_PADDING */
702 
703 	do {
704 		nget = MIN(perchunk, remaining);
705 
706 		status = check_v1hs(gsp, nget);
707 		if(status != NC_NOERR)
708 			return status;
709 
710 		(void) memcpy(value, gsp->pos, nget);
711 		gsp->pos = (void*)((unsigned char *)gsp->pos + nget);
712 
713 		value = (void *)((signed char *)value + nget);
714 
715 		remaining -= nget;
716 
717 	} while(remaining != 0);
718 
719 #if USE_STRICT_NULL_BYTE_HEADER_PADDING
720 	padding = attrp->xsz - ncmpix_len_nctype(attrp->type) * attrp->nelems;
721 	if (padding > 0) {
722 		/* CDF specification: Header padding uses null (\x00) bytes. */
723 		char pad[X_ALIGN-1];
724 		memset(pad, 0, X_ALIGN-1);
725 		if (memcmp((char*)gsp->pos-padding, pad, (size_t)padding) != 0)
726 			return NC_ENULLPAD;
727 	}
728 #endif
729 
730 	return NC_NOERR;
731 }
732 
733 
734 /* Read a NC_attr from the header */
735 static int
v1h_get_NC_attr(v1hs * gsp,NC_attr ** attrpp)736 v1h_get_NC_attr(v1hs *gsp, NC_attr **attrpp)
737 {
738 	NC_string *strp;
739 	int status;
740 	nc_type type;
741 	size_t nelems;
742 	NC_attr *attrp;
743 
744 	status = v1h_get_NC_string(gsp, &strp);
745     if(status != NC_NOERR)
746 		return status;
747 
748 	status = v1h_get_nc_type(gsp, &type);
749     if(status != NC_NOERR)
750 		goto unwind_name;
751 
752 	status = v1h_get_size_t(gsp, &nelems);
753     if(status != NC_NOERR)
754 		goto unwind_name;
755 
756 	attrp = new_x_NC_attr(strp, type, nelems);
757 	if(attrp == NULL)
758 	{
759 		status = NC_ENOMEM;
760 		goto unwind_name;
761 	}
762 
763 	status = v1h_get_NC_attrV(gsp, attrp);
764     if(status != NC_NOERR)
765 	{
766 		free_NC_attr(attrp); /* frees strp */
767 		return status;
768 	}
769 
770 	*attrpp = attrp;
771 
772     return NC_NOERR;
773 
774 unwind_name:
775 	free_NC_string(strp);
776 	return status;
777 }
778 
779 
780 /* How much space in the header is required for this NC_attrarray? */
781 static size_t
ncx_len_NC_attrarray(const NC_attrarray * ncap,int version)782 ncx_len_NC_attrarray(const NC_attrarray *ncap, int version)
783 {
784 	size_t xlen = X_SIZEOF_NCTYPE;	/* type */
785 	xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* count */
786 	if(ncap == NULL)
787 		return xlen;
788 	/* else */
789 	{
790 		const NC_attr **app = (const NC_attr **)ncap->value;
791 		const NC_attr *const *const end = &app[ncap->nelems];
792 		for( /*NADA*/; app < end; app++)
793 		{
794 			xlen += ncx_len_NC_attr(*app,version);
795 		}
796 	}
797 	return xlen;
798 }
799 
800 
801 /* Write a NC_attrarray to the header */
802 static int
v1h_put_NC_attrarray(v1hs * psp,const NC_attrarray * ncap)803 v1h_put_NC_attrarray(v1hs *psp, const NC_attrarray *ncap)
804 {
805 	int status;
806 
807 	assert(psp != NULL);
808 
809 	if(ncap == NULL
810 #if 1
811 		/* Backward:
812 		 * This clause is for 'byte for byte'
813 		 * backward compatibility.
814 		 * Strickly speaking, it is 'bug for bug'.
815 		 */
816 		|| ncap->nelems == 0
817 #endif
818 		)
819 	{
820 		/*
821 		 * Handle empty netcdf
822 		 */
823 		const size_t nosz = 0;
824 
825 		status = v1h_put_NCtype(psp, NC_UNSPECIFIED);
826         if(status != NC_NOERR)
827 			return status;
828 		status = v1h_put_size_t(psp, &nosz);
829         if(status != NC_NOERR)
830 			return status;
831         return NC_NOERR;
832 	}
833 	/* else */
834 
835 	status = v1h_put_NCtype(psp, NC_ATTRIBUTE);
836     if(status != NC_NOERR)
837 		return status;
838 	status = v1h_put_size_t(psp, &ncap->nelems);
839     if(status != NC_NOERR)
840 		return status;
841 
842 	{
843 		const NC_attr **app = (const NC_attr **)ncap->value;
844 		const NC_attr *const *const end = &app[ncap->nelems];
845 		for( /*NADA*/; app < end; app++)
846 		{
847 			status = v1h_put_NC_attr(psp, *app);
848 			if(status)
849 				return status;
850 		}
851 	}
852     return NC_NOERR;
853 }
854 
855 
856 /* Read a NC_attrarray from the header */
857 static int
v1h_get_NC_attrarray(v1hs * gsp,NC_attrarray * ncap)858 v1h_get_NC_attrarray(v1hs *gsp, NC_attrarray *ncap)
859 {
860 	int status;
861 	NCtype type = NC_UNSPECIFIED;
862 
863 	assert(gsp != NULL && gsp->pos != NULL);
864 	assert(ncap != NULL);
865 	assert(ncap->value == NULL);
866 
867 	status = v1h_get_NCtype(gsp, &type);
868     if(status != NC_NOERR)
869 		return status;
870 	status = v1h_get_size_t(gsp, &ncap->nelems);
871     if(status != NC_NOERR)
872 		return status;
873 
874 	if(ncap->nelems == 0)
875         return NC_NOERR;
876 	/* else */
877 	if(type != NC_ATTRIBUTE)
878 		return EINVAL;
879 
880 	ncap->value = (NC_attr **) malloc(ncap->nelems * sizeof(NC_attr *));
881 	if(ncap->value == NULL)
882 		return NC_ENOMEM;
883 	ncap->nalloc = ncap->nelems;
884 
885 	{
886 		NC_attr **app = ncap->value;
887 		NC_attr *const *const end = &app[ncap->nelems];
888 		for( /*NADA*/; app < end; app++)
889 		{
890 			status = v1h_get_NC_attr(gsp, app);
891 			if(status)
892 			{
893 				ncap->nelems = (size_t)(app - ncap->value);
894 				free_NC_attrarrayV(ncap);
895 				return status;
896 			}
897 		}
898 	}
899 
900     return NC_NOERR;
901 }
902 
903 /* End NC_attr */
904 /* Begin NC_var */
905 
906 /*
907  * How much space will the xdr'd var take.
908  * Formerly
909 NC_xlen_var(vpp)
910  */
911 static size_t
ncx_len_NC_var(const NC_var * varp,size_t sizeof_off_t,int version)912 ncx_len_NC_var(const NC_var *varp, size_t sizeof_off_t, int version)
913 {
914 	size_t sz;
915 
916 	assert(varp != NULL);
917 	assert(sizeof_off_t != 0);
918 
919 	sz = ncx_len_NC_string(varp->name, version);
920         if (version == 5) {
921 	    sz += X_SIZEOF_INT64; /* ndims */
922 	    sz += ncx_len_int64(varp->ndims); /* dimids */
923         }
924         else {
925 	    sz += X_SIZEOF_SIZE_T; /* ndims */
926 	    sz += ncx_len_int(varp->ndims); /* dimids */
927 	}
928 	sz += ncx_len_NC_attrarray(&varp->attrs, version);
929 	sz += X_SIZEOF_NC_TYPE; /* nc_type */
930 	sz += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* vsize */
931 	sz += sizeof_off_t; /* begin */
932 
933 	return(sz);
934 }
935 
936 
937 /* Write a NC_var to the header */
938 static int
v1h_put_NC_var(v1hs * psp,const NC_var * varp)939 v1h_put_NC_var(v1hs *psp, const NC_var *varp)
940 {
941 	int status;
942     size_t vsize;
943 
944 	status = v1h_put_NC_string(psp, varp->name);
945     if(status != NC_NOERR)
946 		return status;
947 
948 	status = v1h_put_size_t(psp, &varp->ndims);
949     if(status != NC_NOERR)
950 		return status;
951 
952 	if (psp->version == 5) {
953 		status = check_v1hs(psp, ncx_len_int64(varp->ndims));
954         if(status != NC_NOERR)
955 			return status;
956 		status = ncx_putn_longlong_int(&psp->pos,
957 				varp->ndims, varp->dimids, NULL);
958         if(status != NC_NOERR)
959 			return status;
960 	}
961 	else {
962   	    status = check_v1hs(psp, ncx_len_int(varp->ndims));
963         if(status != NC_NOERR)
964 		return status;
965 	    status = ncx_putn_int_int(&psp->pos,
966 			varp->ndims, varp->dimids, NULL);
967         if(status != NC_NOERR)
968 		return status;
969 	}
970 
971 	status = v1h_put_NC_attrarray(psp, &varp->attrs);
972     if(status != NC_NOERR)
973 		return status;
974 
975 	status = v1h_put_nc_type(psp, &varp->type);
976     if(status != NC_NOERR)
977 		return status;
978 
979     /* write vsize to header.
980      * CDF format specification: The vsize field is actually redundant, because
981      * its value may be computed from other information in the header. The
982      * 32-bit vsize field is not large enough to contain the size of variables
983      * that require more than 2^32 - 4 bytes, so 2^32 - 1 is used in the vsize
984      * field for such variables.
985      */
986     vsize = varp->len;
987     if (varp->len > 4294967292UL && (psp->version == NC_FORMAT_CLASSIC ||
988                                      psp->version == NC_FORMAT_64BIT_OFFSET))
989         vsize = 4294967295UL; /* 2^32-1 */
990     status = v1h_put_size_t(psp, &vsize);
991     if(status != NC_NOERR) return status;
992 
993 	status = check_v1hs(psp, psp->version == 1 ? 4 : 8); /*begin*/
994     if(status != NC_NOERR)
995 		 return status;
996 	status = ncx_put_off_t(&psp->pos, &varp->begin, psp->version == 1 ? 4 : 8);
997     if(status != NC_NOERR)
998 		return status;
999 
1000     return NC_NOERR;
1001 }
1002 
1003 
1004 /* Read a NC_var from the header */
1005 static int
v1h_get_NC_var(v1hs * gsp,NC_var ** varpp)1006 v1h_get_NC_var(v1hs *gsp, NC_var **varpp)
1007 {
1008 	NC_string *strp;
1009 	int status;
1010 	size_t ndims;
1011 	NC_var *varp;
1012 
1013 	status = v1h_get_NC_string(gsp, &strp);
1014     if(status != NC_NOERR)
1015 		return status;
1016 
1017 	status = v1h_get_size_t(gsp, &ndims);
1018     if(status != NC_NOERR)
1019 		goto unwind_name;
1020 
1021 	varp = new_x_NC_var(strp, ndims);
1022 	if(varp == NULL)
1023 	{
1024 		status = NC_ENOMEM;
1025 		goto unwind_name;
1026 	}
1027 
1028 	if (gsp->version == 5) {
1029 		status = check_v1hs(gsp, ncx_len_int64(ndims));
1030         if(status != NC_NOERR)
1031 			goto unwind_alloc;
1032 		status = ncx_getn_longlong_int((const void **)(&gsp->pos),
1033 				ndims, varp->dimids);
1034         if(status != NC_NOERR)
1035 			goto unwind_alloc;
1036 	}
1037 	else {
1038 	    status = check_v1hs(gsp, ncx_len_int(ndims));
1039         if(status != NC_NOERR)
1040 		goto unwind_alloc;
1041 	    status = ncx_getn_int_int((const void **)(&gsp->pos),
1042 			ndims, varp->dimids);
1043         if(status != NC_NOERR)
1044 		goto unwind_alloc;
1045 	}
1046 	status = v1h_get_NC_attrarray(gsp, &varp->attrs);
1047     if(status != NC_NOERR)
1048 		goto unwind_alloc;
1049 	status = v1h_get_nc_type(gsp, &varp->type);
1050     if(status != NC_NOERR)
1051 		 goto unwind_alloc;
1052 
1053     size_t tmp;
1054     status = v1h_get_size_t(gsp, &tmp);
1055     varp->len = tmp;
1056     if(status != NC_NOERR)
1057 		 goto unwind_alloc;
1058 
1059 	status = check_v1hs(gsp, gsp->version == 1 ? 4 : 8);
1060     if(status != NC_NOERR)
1061 		 goto unwind_alloc;
1062 	status = ncx_get_off_t((const void **)&gsp->pos,
1063 			       &varp->begin, gsp->version == 1 ? 4 : 8);
1064     if(status != NC_NOERR)
1065 		 goto unwind_alloc;
1066 
1067 	*varpp = varp;
1068     return NC_NOERR;
1069 
1070 unwind_alloc:
1071 	free_NC_var(varp); /* frees name */
1072 	return status;
1073 
1074 unwind_name:
1075 	free_NC_string(strp);
1076 	return status;
1077 }
1078 
1079 
1080 /* How much space in the header is required for this NC_vararray? */
1081 static size_t
ncx_len_NC_vararray(const NC_vararray * ncap,size_t sizeof_off_t,int version)1082 ncx_len_NC_vararray(const NC_vararray *ncap, size_t sizeof_off_t, int version)
1083 {
1084 	size_t xlen = X_SIZEOF_NCTYPE;	/* type */
1085 	xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* count */
1086 	if(ncap == NULL)
1087 		return xlen;
1088 	/* else */
1089 	{
1090 		const NC_var **vpp = (const NC_var **)ncap->value;
1091 		const NC_var *const *const end = &vpp[ncap->nelems];
1092 		for( /*NADA*/; vpp < end; vpp++)
1093 		{
1094 			xlen += ncx_len_NC_var(*vpp, sizeof_off_t, version);
1095 		}
1096 	}
1097 	return xlen;
1098 }
1099 
1100 
1101 /* Write a NC_vararray to the header */
1102 static int
v1h_put_NC_vararray(v1hs * psp,const NC_vararray * ncap)1103 v1h_put_NC_vararray(v1hs *psp, const NC_vararray *ncap)
1104 {
1105 	int status;
1106 
1107 	assert(psp != NULL);
1108 
1109 	if(ncap == NULL
1110 #if 1
1111 		/* Backward:
1112 		 * This clause is for 'byte for byte'
1113 		 * backward compatibility.
1114 		 * Strickly speaking, it is 'bug for bug'.
1115 		 */
1116 		|| ncap->nelems == 0
1117 #endif
1118 		)
1119 	{
1120 		/*
1121 		 * Handle empty netcdf
1122 		 */
1123 		const size_t nosz = 0;
1124 
1125 		status = v1h_put_NCtype(psp, NC_UNSPECIFIED);
1126         if(status != NC_NOERR)
1127 			return status;
1128 		status = v1h_put_size_t(psp, &nosz);
1129         if(status != NC_NOERR)
1130 			return status;
1131         return NC_NOERR;
1132 	}
1133 	/* else */
1134 
1135 	status = v1h_put_NCtype(psp, NC_VARIABLE);
1136     if(status != NC_NOERR)
1137 		return status;
1138 	status = v1h_put_size_t(psp, &ncap->nelems);
1139     if(status != NC_NOERR)
1140 		return status;
1141 
1142 	{
1143 		const NC_var **vpp = (const NC_var **)ncap->value;
1144 		const NC_var *const *const end = &vpp[ncap->nelems];
1145 		for( /*NADA*/; vpp < end; vpp++)
1146 		{
1147 			status = v1h_put_NC_var(psp, *vpp);
1148 			if(status)
1149 				return status;
1150 		}
1151 	}
1152     return NC_NOERR;
1153 }
1154 
1155 
1156 /* Read a NC_vararray from the header */
1157 static int
v1h_get_NC_vararray(v1hs * gsp,NC_vararray * ncap)1158 v1h_get_NC_vararray(v1hs *gsp, NC_vararray *ncap)
1159 {
1160 	int status;
1161 	NCtype type = NC_UNSPECIFIED;
1162 
1163 	assert(gsp != NULL && gsp->pos != NULL);
1164 	assert(ncap != NULL);
1165 	assert(ncap->value == NULL);
1166 
1167 	status = v1h_get_NCtype(gsp, &type);
1168     if(status != NC_NOERR)
1169 		return status;
1170 
1171 	status = v1h_get_size_t(gsp, &ncap->nelems);
1172     if(status != NC_NOERR)
1173 		return status;
1174 
1175 	if(ncap->nelems == 0)
1176         return NC_NOERR;
1177 	/* else */
1178 	if(type != NC_VARIABLE)
1179 		return EINVAL;
1180 
1181 	ncap->value = (NC_var **) calloc(1,ncap->nelems * sizeof(NC_var *));
1182 	if(ncap->value == NULL)
1183 		return NC_ENOMEM;
1184 	ncap->nalloc = ncap->nelems;
1185 
1186 	ncap->hashmap = NC_hashmapnew(ncap->nelems);
1187 	{
1188 		NC_var **vpp = ncap->value;
1189 		NC_var *const *const end = &vpp[ncap->nelems];
1190 		for( /*NADA*/; vpp < end; vpp++)
1191 		{
1192 			status = v1h_get_NC_var(gsp, vpp);
1193 			if(status)
1194 			{
1195 				ncap->nelems = (size_t)(vpp - ncap->value);
1196 				free_NC_vararrayV(ncap);
1197 				return status;
1198 			}
1199 			{
1200 			  int varid = (size_t)(vpp - ncap->value);
1201 			  NC_hashmapadd(ncap->hashmap, (uintptr_t)varid, (*vpp)->name->cp,strlen((*vpp)->name->cp));
1202 			}
1203 		}
1204 	}
1205 
1206     return NC_NOERR;
1207 }
1208 
1209 
1210 /* End NC_var */
1211 /* Begin NC */
1212 
1213 /*
1214  * Recompute the shapes of all variables
1215  * Sets ncp->begin_var to start of first variable.
1216  * Sets ncp->begin_rec to start of first record variable.
1217  * Returns -1 on error. The only possible error is a reference
1218  * to a non existent dimension, which could occur for a corrupted
1219  * netcdf file.
1220  */
1221 static int
NC_computeshapes(NC3_INFO * ncp)1222 NC_computeshapes(NC3_INFO* ncp)
1223 {
1224 	NC_var **vpp = (NC_var **)ncp->vars.value;
1225 	NC_var *const *const end = &vpp[ncp->vars.nelems];
1226 	NC_var *first_var = NULL;	/* first "non-record" var */
1227 	NC_var *first_rec = NULL;	/* first "record" var */
1228 	int status;
1229 
1230 	ncp->begin_var = (off_t) ncp->xsz;
1231 	ncp->begin_rec = (off_t) ncp->xsz;
1232 	ncp->recsize = 0;
1233 
1234 	if(ncp->vars.nelems == 0)
1235 		return(0);
1236 
1237 	for( /*NADA*/; vpp < end; vpp++)
1238 	{
1239 		status = NC_var_shape(*vpp, &ncp->dims);
1240         if(status != NC_NOERR)
1241 			return(status);
1242 
1243 	  	if(IS_RECVAR(*vpp))
1244 		{
1245 	  		if(first_rec == NULL)
1246 				first_rec = *vpp;
1247 			    ncp->recsize += (*vpp)->len;
1248 		}
1249 		else
1250 		{
1251 		        if(first_var == NULL)
1252 			        first_var = *vpp;
1253 			/*
1254 			 * Overwritten each time thru.
1255 			 * Usually overwritten in first_rec != NULL clause below.
1256 			 */
1257 			ncp->begin_rec = (*vpp)->begin + (off_t)(*vpp)->len;
1258 		}
1259 	}
1260 
1261 	if(first_rec != NULL)
1262 	{
1263 		if(ncp->begin_rec > first_rec->begin)
1264 		    return(NC_ENOTNC); /* not a netCDF file or corrupted */
1265 		ncp->begin_rec = first_rec->begin;
1266 		/*
1267 	 	 * for special case of exactly one record variable, pack value
1268 	 	 */
1269 		if(ncp->recsize == first_rec->len)
1270 			ncp->recsize = *first_rec->dsizes * first_rec->xsz;
1271 	}
1272 
1273 	if(first_var != NULL)
1274 	{
1275 		ncp->begin_var = first_var->begin;
1276 	}
1277 	else
1278 	{
1279 		ncp->begin_var = ncp->begin_rec;
1280 	}
1281 
1282 	if(ncp->begin_var <= 0 ||
1283 	   ncp->xsz > (size_t)ncp->begin_var ||
1284 	   ncp->begin_rec <= 0 ||
1285 	   ncp->begin_var > ncp->begin_rec)
1286 	    return(NC_ENOTNC); /* not a netCDF file or corrupted */
1287 
1288     return(NC_NOERR);
1289 }
1290 
1291 /* How much space in the header is required for the NC data structure? */
1292 size_t
ncx_len_NC(const NC3_INFO * ncp,size_t sizeof_off_t)1293 ncx_len_NC(const NC3_INFO* ncp, size_t sizeof_off_t)
1294 {
1295 	int version=1;
1296 	size_t xlen = sizeof(ncmagic);
1297 
1298 	assert(ncp != NULL);
1299 	if (fIsSet(ncp->flags, NC_64BIT_DATA)) /* CDF-5 */
1300 		version = 5;
1301     	else if (fIsSet(ncp->flags, NC_64BIT_OFFSET)) /* CDF-2 */
1302 		version = 2;
1303 
1304 	xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* numrecs */
1305 	xlen += ncx_len_NC_dimarray(&ncp->dims, version);
1306 	xlen += ncx_len_NC_attrarray(&ncp->attrs, version);
1307 	xlen += ncx_len_NC_vararray(&ncp->vars, sizeof_off_t, version);
1308 
1309 	return xlen;
1310 }
1311 
1312 
1313 /* Write the file header */
1314 int
ncx_put_NC(const NC3_INFO * ncp,void ** xpp,off_t offset,size_t extent)1315 ncx_put_NC(const NC3_INFO* ncp, void **xpp, off_t offset, size_t extent)
1316 {
1317     int status = NC_NOERR;
1318 	v1hs ps; /* the get stream */
1319 
1320 	assert(ncp != NULL);
1321 
1322 	/* Initialize stream ps */
1323 
1324 	ps.nciop = ncp->nciop;
1325 	ps.flags = RGN_WRITE;
1326 
1327 	if (ncp->flags & NC_64BIT_DATA)
1328 	  ps.version = 5;
1329 	else if (ncp->flags & NC_64BIT_OFFSET)
1330 	  ps.version = 2;
1331 	else
1332 	  ps.version = 1;
1333 
1334 	if(xpp == NULL)
1335 	{
1336 		/*
1337 		 * Come up with a reasonable stream read size.
1338 		 */
1339 		extent = ncp->xsz;
1340 		if(extent <= ((ps.version==5)?MIN_NC5_XSZ:MIN_NC3_XSZ))
1341 		{
1342 			/* first time read */
1343 			extent = ncp->chunk;
1344 			/* Protection for when ncp->chunk is huge;
1345 			 * no need to read hugely. */
1346 	      		if(extent > 4096)
1347 				extent = 4096;
1348 		}
1349 		else if(extent > ncp->chunk)
1350 		    extent = ncp->chunk;
1351 
1352 		ps.offset = 0;
1353 		ps.extent = extent;
1354 		ps.base = NULL;
1355 		ps.pos = ps.base;
1356 
1357 		status = fault_v1hs(&ps, extent);
1358 		if(status)
1359 			return status;
1360 	}
1361 	else
1362 	{
1363 		ps.offset = offset;
1364 		ps.extent = extent;
1365 		ps.base = *xpp;
1366 		ps.pos = ps.base;
1367 		ps.end = (char *)ps.base + ps.extent;
1368 	}
1369 
1370 	if (ps.version == 5)
1371 	  status = ncx_putn_schar_schar(&ps.pos, sizeof(ncmagic5), ncmagic5, NULL);
1372 	else if (ps.version == 2)
1373 	  status = ncx_putn_schar_schar(&ps.pos, sizeof(ncmagic), ncmagic, NULL);
1374 	else
1375 	  status = ncx_putn_schar_schar(&ps.pos, sizeof(ncmagic1), ncmagic1, NULL);
1376 	if(status != NC_NOERR)
1377 		goto release;
1378 
1379 	{
1380 	const size_t nrecs = NC_get_numrecs(ncp);
1381 	if (ps.version == 5) {
1382             unsigned long long tmp = (unsigned long long) nrecs;
1383 	    status = ncx_put_uint64(&ps.pos, tmp);
1384         }
1385        	else
1386 	    status = ncx_put_size_t(&ps.pos, &nrecs);
1387 	if(status != NC_NOERR)
1388 		goto release;
1389 	}
1390 
1391 	assert((char *)ps.pos < (char *)ps.end);
1392 
1393 	status = v1h_put_NC_dimarray(&ps, &ncp->dims);
1394     if(status != NC_NOERR)
1395 		goto release;
1396 
1397 	status = v1h_put_NC_attrarray(&ps, &ncp->attrs);
1398     if(status != NC_NOERR)
1399 		goto release;
1400 
1401 	status = v1h_put_NC_vararray(&ps, &ncp->vars);
1402     if(status != NC_NOERR)
1403 		goto release;
1404 
1405 release:
1406 	(void) rel_v1hs(&ps);
1407 
1408 	return status;
1409 }
1410 
1411 
1412 /* Make the in-memory NC structure from reading the file header */
1413 int
nc_get_NC(NC3_INFO * ncp)1414 nc_get_NC(NC3_INFO* ncp)
1415 {
1416 	int status;
1417 	v1hs gs; /* the get stream */
1418 
1419 	assert(ncp != NULL);
1420 
1421 	/* Initialize stream gs */
1422 
1423 	gs.nciop = ncp->nciop;
1424 	gs.offset = 0; /* beginning of file */
1425 	gs.extent = 0;
1426 	gs.flags = 0;
1427 	gs.version = 0;
1428 	gs.base = NULL;
1429 	gs.pos = gs.base;
1430 
1431 	{
1432 		/*
1433 		 * Come up with a reasonable stream read size.
1434 		 */
1435 	        off_t filesize;
1436 		size_t extent = ncp->xsz;
1437 
1438 		if(extent <= ((fIsSet(ncp->flags, NC_64BIT_DATA))?MIN_NC5_XSZ:MIN_NC3_XSZ))
1439 		{
1440 		        status = ncio_filesize(ncp->nciop, &filesize);
1441 			if(status)
1442 			    return status;
1443 			if(filesize < sizeof(ncmagic)) { /* too small, not netcdf */
1444 
1445 			    status = NC_ENOTNC;
1446 			    return status;
1447 			}
1448 			/* first time read */
1449 			extent = ncp->chunk;
1450 			/* Protection for when ncp->chunk is huge;
1451 			 * no need to read hugely. */
1452 	      		if(extent > 4096)
1453 				extent = 4096;
1454 			if(extent > filesize)
1455 			        extent = filesize;
1456 		}
1457 		else if(extent > ncp->chunk)
1458 		    extent = ncp->chunk;
1459 
1460 		/*
1461 		 * Invalidate the I/O buffers to force a read of the header
1462 		 * region.
1463 		 */
1464 		status = ncio_sync(gs.nciop);
1465 		if(status)
1466 			return status;
1467 
1468 		status = fault_v1hs(&gs, extent);
1469 		if(status)
1470 			return status;
1471 	}
1472 
1473 	/* get the header from the stream gs */
1474 
1475 	{
1476 		/* Get & check magic number */
1477 		schar magic[sizeof(ncmagic)];
1478 		(void) memset(magic, 0, sizeof(magic));
1479 
1480 		status = ncx_getn_schar_schar(
1481 			(const void **)(&gs.pos), sizeof(magic), magic);
1482         if(status != NC_NOERR)
1483 			goto unwind_get;
1484 
1485 		if(memcmp(magic, ncmagic, sizeof(ncmagic)-1) != 0)
1486 		{
1487 			status = NC_ENOTNC;
1488 			goto unwind_get;
1489 		}
1490 		/* Check version number in last byte of magic */
1491 		if (magic[sizeof(ncmagic)-1] == 0x1) {
1492 		  gs.version = 1;
1493 		} else if (magic[sizeof(ncmagic)-1] == 0x2) {
1494 		  gs.version = 2;
1495 		  fSet(ncp->flags, NC_64BIT_OFFSET);
1496 		  /* Now we support version 2 file access on non-LFS systems -- rkr */
1497 #if 0
1498 		  if (sizeof(off_t) != 8) {
1499 		    fprintf(stderr, "NETCDF WARNING: Version 2 file on 32-bit system.\n");
1500 		  }
1501 #endif
1502 		} else if (magic[sizeof(ncmagic)-1] == 0x5) {
1503 		  gs.version = 5;
1504 		  fSet(ncp->flags, NC_64BIT_DATA);
1505 		} else {
1506 			status = NC_ENOTNC;
1507 			goto unwind_get;
1508 		}
1509 	}
1510 
1511 	{
1512 	size_t nrecs = 0;
1513        	if (gs.version == 5) {
1514 		unsigned long long tmp = 0;
1515 		status = ncx_get_uint64((const void **)(&gs.pos), &tmp);
1516 		nrecs = (size_t)tmp;
1517        	}
1518        	else
1519 	    status = ncx_get_size_t((const void **)(&gs.pos), &nrecs);
1520     if(status != NC_NOERR)
1521 		goto unwind_get;
1522 	NC_set_numrecs(ncp, nrecs);
1523 	}
1524 
1525 	assert((char *)gs.pos < (char *)gs.end);
1526 
1527 	status = v1h_get_NC_dimarray(&gs, &ncp->dims);
1528     if(status != NC_NOERR)
1529 		goto unwind_get;
1530 
1531 	status = v1h_get_NC_attrarray(&gs, &ncp->attrs);
1532     if(status != NC_NOERR)
1533 		goto unwind_get;
1534 
1535 	status = v1h_get_NC_vararray(&gs, &ncp->vars);
1536     if(status != NC_NOERR)
1537 		goto unwind_get;
1538 
1539 	ncp->xsz = ncx_len_NC(ncp, (gs.version == 1) ? 4 : 8);
1540 
1541 	status = NC_computeshapes(ncp);
1542     if(status != NC_NOERR)
1543 		goto unwind_get;
1544 
1545 	status = NC_check_vlens(ncp);
1546     if(status != NC_NOERR)
1547 		goto unwind_get;
1548 
1549 	status = NC_check_voffs(ncp);
1550     if(status != NC_NOERR)
1551 		goto unwind_get;
1552 
1553 unwind_get:
1554 	(void) rel_v1hs(&gs);
1555 	return status;
1556 }
1557