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 "nc3internal.h"
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <assert.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;
307 	size_t padding, nchars = 0;
308 	NC_string *ncstrp;
309 
310 	status = v1h_get_size_t(gsp, &nchars);
311 	if(status != NC_NOERR)
312 		return status;
313 
314 	ncstrp = new_NC_string(nchars, NULL);
315 	if(ncstrp == NULL)
316 	{
317 		return NC_ENOMEM;
318 	}
319 
320 #if 0
321 /* assert(ncstrp->nchars == nchars || ncstrp->nchars - nchars < X_ALIGN); */
322 	assert(ncstrp->nchars % X_ALIGN == 0);
323 	status = check_v1hs(gsp, ncstrp->nchars);
324 #else
325 
326 	status = check_v1hs(gsp, _RNDUP(ncstrp->nchars, X_ALIGN));
327 #endif
328 	if(status != NC_NOERR)
329 		goto unwind_alloc;
330 
331 	status = ncx_pad_getn_text((const void **)(&gsp->pos),
332 		 nchars, ncstrp->cp);
333 	if(status != NC_NOERR)
334 		goto unwind_alloc;
335 
336 	padding = _RNDUP(X_SIZEOF_CHAR * ncstrp->nchars, X_ALIGN)
337 		- X_SIZEOF_CHAR * ncstrp->nchars;
338 	if (padding > 0) {
339 		/* CDF specification: Header padding uses null (\x00) bytes. */
340 		char pad[X_ALIGN-1];
341 		memset(pad, 0, X_ALIGN-1);
342 		if (memcmp((char*)gsp->pos-padding, pad, padding) != 0) {
343 			free_NC_string(ncstrp);
344 			return NC_EINVAL;
345 		}
346 	}
347 
348 	*ncstrpp = ncstrp;
349 
350 	return NC_NOERR;
351 
352 unwind_alloc:
353 	free_NC_string(ncstrp);
354 	return status;
355 }
356 
357 /* End NC_string */
358 /* Begin NC_dim */
359 
360 /*
361  * How much space will the xdr'd dim take.
362  * Formerly
363 NC_xlen_dim(dpp)
364  */
365 static size_t
ncx_len_NC_dim(const NC_dim * dimp,int version)366 ncx_len_NC_dim(const NC_dim *dimp, int version)
367 {
368 	size_t sz;
369 
370 	assert(dimp != NULL);
371 
372 	sz = ncx_len_NC_string(dimp->name, version);
373 	sz += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T;
374 
375 	return(sz);
376 }
377 
378 
379 /* Write a NC_dim to the header */
380 static int
v1h_put_NC_dim(v1hs * psp,const NC_dim * dimp)381 v1h_put_NC_dim(v1hs *psp, const NC_dim *dimp)
382 {
383 	int status;
384 
385 	status = v1h_put_NC_string(psp, dimp->name);
386     if(status != NC_NOERR)
387 		return status;
388 
389 	status = v1h_put_size_t(psp, &dimp->size);
390     if(status != NC_NOERR)
391 		return status;
392 
393     return NC_NOERR;
394 }
395 
396 /* Read a NC_dim from the header */
397 static int
v1h_get_NC_dim(v1hs * gsp,NC_dim ** dimpp)398 v1h_get_NC_dim(v1hs *gsp, NC_dim **dimpp)
399 {
400 	int status;
401 	NC_string *ncstrp;
402 	NC_dim *dimp;
403 
404 	status = v1h_get_NC_string(gsp, &ncstrp);
405     if(status != NC_NOERR)
406 		return status;
407 
408 	dimp = new_x_NC_dim(ncstrp);
409 	if(dimp == NULL)
410 	{
411 		status = NC_ENOMEM;
412 		goto unwind_name;
413 	}
414 
415 	status = v1h_get_size_t(gsp, &dimp->size);
416     if(status != NC_NOERR)
417 	{
418 		free_NC_dim(dimp); /* frees name */
419 		return status;
420 	}
421 
422 	*dimpp = dimp;
423 
424     return NC_NOERR;
425 
426 unwind_name:
427 	free_NC_string(ncstrp);
428 	return status;
429 }
430 
431 
432 /* How much space in the header is required for this NC_dimarray? */
433 static size_t
ncx_len_NC_dimarray(const NC_dimarray * ncap,int version)434 ncx_len_NC_dimarray(const NC_dimarray *ncap, int version)
435 {
436 	size_t xlen = X_SIZEOF_NCTYPE;	/* type */
437 	xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* count */
438 	if(ncap == NULL)
439 		return xlen;
440 	/* else */
441 	{
442 		const NC_dim **dpp = (const NC_dim **)ncap->value;
443 		const NC_dim *const *const end = &dpp[ncap->nelems];
444 		for(  /*NADA*/; dpp < end; dpp++)
445 		{
446 			xlen += ncx_len_NC_dim(*dpp,version);
447 		}
448 	}
449 	return xlen;
450 }
451 
452 
453 /* Write a NC_dimarray to the header */
454 static int
v1h_put_NC_dimarray(v1hs * psp,const NC_dimarray * ncap)455 v1h_put_NC_dimarray(v1hs *psp, const NC_dimarray *ncap)
456 {
457 	int status;
458 
459 	assert(psp != NULL);
460 
461 	if(ncap == NULL
462 #if 1
463 		/* Backward:
464 		 * This clause is for 'byte for byte'
465 		 * backward compatibility.
466 		 * Strickly speaking, it is 'bug for bug'.
467 		 */
468 		|| ncap->nelems == 0
469 #endif
470 		)
471 	{
472 		/*
473 		 * Handle empty netcdf
474 		 */
475 		const size_t nosz = 0;
476 
477 		status = v1h_put_NCtype(psp, NC_UNSPECIFIED);
478         if(status != NC_NOERR)
479 			return status;
480 		status = v1h_put_size_t(psp, &nosz);
481         if(status != NC_NOERR)
482 			return status;
483         return NC_NOERR;
484 	}
485 	/* else */
486 
487 	status = v1h_put_NCtype(psp, NC_DIMENSION);
488     if(status != NC_NOERR)
489 		return status;
490 	status = v1h_put_size_t(psp, &ncap->nelems);
491     if(status != NC_NOERR)
492 		return status;
493 
494 	{
495 		const NC_dim **dpp = (const NC_dim **)ncap->value;
496 		const NC_dim *const *const end = &dpp[ncap->nelems];
497 		for( /*NADA*/; dpp < end; dpp++)
498 		{
499 			status = v1h_put_NC_dim(psp, *dpp);
500 			if(status)
501 				return status;
502 		}
503 	}
504     return NC_NOERR;
505 }
506 
507 
508 /* Read a NC_dimarray from the header */
509 static int
v1h_get_NC_dimarray(v1hs * gsp,NC_dimarray * ncap)510 v1h_get_NC_dimarray(v1hs *gsp, NC_dimarray *ncap)
511 {
512 	int status;
513 	NCtype type = NC_UNSPECIFIED;
514 
515 	assert(gsp != NULL && gsp->pos != NULL);
516 	assert(ncap != NULL);
517 	assert(ncap->value == NULL);
518 
519 	status = v1h_get_NCtype(gsp, &type);
520     if(status != NC_NOERR)
521 		return status;
522 
523 	status = v1h_get_size_t(gsp, &ncap->nelems);
524     if(status != NC_NOERR)
525 		return status;
526 
527 	if(ncap->nelems == 0)
528         return NC_NOERR;
529 	/* else */
530 	if(type != NC_DIMENSION)
531 		return EINVAL;
532 
533 	ncap->value = (NC_dim **) malloc(ncap->nelems * sizeof(NC_dim *));
534 	if(ncap->value == NULL)
535 		return NC_ENOMEM;
536 	ncap->nalloc = ncap->nelems;
537 
538 	ncap->hashmap = NC_hashmapCreate(ncap->nelems);
539 
540 	{
541 		NC_dim **dpp = ncap->value;
542 		NC_dim *const *const end = &dpp[ncap->nelems];
543 		for( /*NADA*/; dpp < end; dpp++)
544 		{
545 			status = v1h_get_NC_dim(gsp, dpp);
546 			if(status)
547 			{
548 				ncap->nelems = (size_t)(dpp - ncap->value);
549 				free_NC_dimarrayV(ncap);
550 				return status;
551 			}
552 			{
553 			  int dimid = (size_t)(dpp - ncap->value);
554 			  NC_hashmapAddDim(ncap, dimid, (*dpp)->name->cp);
555 			}
556 		}
557 	}
558 
559     return NC_NOERR;
560 }
561 
562 
563 /* End NC_dim */
564 /* Begin NC_attr */
565 
566 
567 /*
568  * How much space will 'attrp' take in external representation?
569  * Formerly
570 NC_xlen_attr(app)
571  */
572 static size_t
ncx_len_NC_attr(const NC_attr * attrp,int version)573 ncx_len_NC_attr(const NC_attr *attrp, int version)
574 {
575 	size_t sz;
576 
577 	assert(attrp != NULL);
578 
579 	sz = ncx_len_NC_string(attrp->name, version);
580 	sz += X_SIZEOF_NC_TYPE; /* type */
581 	sz += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* nelems */
582 	sz += attrp->xsz;
583 
584 	return(sz);
585 }
586 
587 
588 #undef MIN
589 #define MIN(mm,nn) (((mm) < (nn)) ? (mm) : (nn))
590 
591 /*----< ncmpix_len_nctype() >------------------------------------------------*/
592 /* return the length of external data type */
593 static int
ncmpix_len_nctype(nc_type type)594 ncmpix_len_nctype(nc_type type) {
595     switch(type) {
596         case NC_BYTE:
597         case NC_CHAR:
598         case NC_UBYTE:  return X_SIZEOF_CHAR;
599         case NC_SHORT:  return X_SIZEOF_SHORT;
600         case NC_USHORT: return X_SIZEOF_USHORT;
601         case NC_INT:    return X_SIZEOF_INT;
602         case NC_UINT:   return X_SIZEOF_UINT;
603         case NC_FLOAT:  return X_SIZEOF_FLOAT;
604         case NC_DOUBLE: return X_SIZEOF_DOUBLE;
605         case NC_INT64:  return X_SIZEOF_INT64;
606         case NC_UINT64: return X_SIZEOF_UINT64;
607         default: fprintf(stderr,"ncmpix_len_nctype bad type %d\n",type);
608                  assert(0);
609     }
610     return 0;
611 }
612 
613 /*
614  * Put the values of an attribute
615  * The loop is necessary since attrp->nelems
616  * could potentially be quite large.
617  */
618 static int
v1h_put_NC_attrV(v1hs * psp,const NC_attr * attrp)619 v1h_put_NC_attrV(v1hs *psp, const NC_attr *attrp)
620 {
621 	int status;
622 	const size_t perchunk =  psp->extent;
623 	size_t remaining = attrp->xsz;
624 	void *value = attrp->xvalue;
625 	size_t nbytes, padding;
626 
627 	assert(psp->extent % X_ALIGN == 0);
628 
629 	do {
630 		nbytes = MIN(perchunk, remaining);
631 
632 		status = check_v1hs(psp, nbytes);
633 		if(status != NC_NOERR)
634 			return status;
635 
636 		(void) memcpy(psp->pos, value, nbytes);
637 
638 		psp->pos = (void *)((char *)psp->pos + nbytes);
639 		value = (void *)((char *)value + nbytes);
640         	remaining -= nbytes;
641 
642 	} while(remaining != 0);
643 
644 	padding = attrp->xsz - ncmpix_len_nctype(attrp->type) * attrp->nelems;
645 	if (padding > 0) {
646 		/* CDF specification: Header padding uses null (\x00) bytes. */
647 		memset((char*)psp->pos-padding, 0, padding);
648 	}
649 
650 	return NC_NOERR;
651 }
652 
653 /* Write a NC_attr to the header */
654 static int
v1h_put_NC_attr(v1hs * psp,const NC_attr * attrp)655 v1h_put_NC_attr(v1hs *psp, const NC_attr *attrp)
656 {
657 	int status;
658 
659 	status = v1h_put_NC_string(psp, attrp->name);
660     if(status != NC_NOERR)
661 		return status;
662 
663 	status = v1h_put_nc_type(psp, &attrp->type);
664     if(status != NC_NOERR)
665 		return status;
666 
667 	status = v1h_put_size_t(psp, &attrp->nelems);
668     if(status != NC_NOERR)
669 		return status;
670 
671 	status = v1h_put_NC_attrV(psp, attrp);
672     if(status != NC_NOERR)
673 		return status;
674 
675     return NC_NOERR;
676 }
677 
678 
679 /*
680  * Get the values of an attribute
681  * The loop is necessary since attrp->nelems
682  * could potentially be quite large.
683  */
684 static int
v1h_get_NC_attrV(v1hs * gsp,NC_attr * attrp)685 v1h_get_NC_attrV(v1hs *gsp, NC_attr *attrp)
686 {
687 	int status;
688 	const size_t perchunk =  gsp->extent;
689 	size_t remaining = attrp->xsz;
690 	void *value = attrp->xvalue;
691 	size_t nget, padding;
692 
693 	do {
694 		nget = MIN(perchunk, remaining);
695 
696 		status = check_v1hs(gsp, nget);
697 		if(status != NC_NOERR)
698 			return status;
699 
700 		(void) memcpy(value, gsp->pos, nget);
701 		gsp->pos = (void*)((unsigned char *)gsp->pos + nget);
702 
703 		value = (void *)((signed char *)value + nget);
704 
705 		remaining -= nget;
706 
707 	} while(remaining != 0);
708 
709 	padding = attrp->xsz - ncmpix_len_nctype(attrp->type) * attrp->nelems;
710 	if (padding > 0) {
711 		/* CDF specification: Header padding uses null (\x00) bytes. */
712 		char pad[X_ALIGN-1];
713 		memset(pad, 0, X_ALIGN-1);
714 		if (memcmp((char*)gsp->pos-padding, pad, (size_t)padding) != 0)
715 			return NC_EINVAL;
716 	}
717 
718 	return NC_NOERR;
719 }
720 
721 
722 /* Read a NC_attr from the header */
723 static int
v1h_get_NC_attr(v1hs * gsp,NC_attr ** attrpp)724 v1h_get_NC_attr(v1hs *gsp, NC_attr **attrpp)
725 {
726 	NC_string *strp;
727 	int status;
728 	nc_type type;
729 	size_t nelems;
730 	NC_attr *attrp;
731 
732 	status = v1h_get_NC_string(gsp, &strp);
733     if(status != NC_NOERR)
734 		return status;
735 
736 	status = v1h_get_nc_type(gsp, &type);
737     if(status != NC_NOERR)
738 		goto unwind_name;
739 
740 	status = v1h_get_size_t(gsp, &nelems);
741     if(status != NC_NOERR)
742 		goto unwind_name;
743 
744 	attrp = new_x_NC_attr(strp, type, nelems);
745 	if(attrp == NULL)
746 	{
747 		status = NC_ENOMEM;
748 		goto unwind_name;
749 	}
750 
751 	status = v1h_get_NC_attrV(gsp, attrp);
752     if(status != NC_NOERR)
753 	{
754 		free_NC_attr(attrp); /* frees strp */
755 		return status;
756 	}
757 
758 	*attrpp = attrp;
759 
760     return NC_NOERR;
761 
762 unwind_name:
763 	free_NC_string(strp);
764 	return status;
765 }
766 
767 
768 /* How much space in the header is required for this NC_attrarray? */
769 static size_t
ncx_len_NC_attrarray(const NC_attrarray * ncap,int version)770 ncx_len_NC_attrarray(const NC_attrarray *ncap, int version)
771 {
772 	size_t xlen = X_SIZEOF_NCTYPE;	/* type */
773 	xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* count */
774 	if(ncap == NULL)
775 		return xlen;
776 	/* else */
777 	{
778 		const NC_attr **app = (const NC_attr **)ncap->value;
779 		const NC_attr *const *const end = &app[ncap->nelems];
780 		for( /*NADA*/; app < end; app++)
781 		{
782 			xlen += ncx_len_NC_attr(*app,version);
783 		}
784 	}
785 	return xlen;
786 }
787 
788 
789 /* Write a NC_attrarray to the header */
790 static int
v1h_put_NC_attrarray(v1hs * psp,const NC_attrarray * ncap)791 v1h_put_NC_attrarray(v1hs *psp, const NC_attrarray *ncap)
792 {
793 	int status;
794 
795 	assert(psp != NULL);
796 
797 	if(ncap == NULL
798 #if 1
799 		/* Backward:
800 		 * This clause is for 'byte for byte'
801 		 * backward compatibility.
802 		 * Strickly speaking, it is 'bug for bug'.
803 		 */
804 		|| ncap->nelems == 0
805 #endif
806 		)
807 	{
808 		/*
809 		 * Handle empty netcdf
810 		 */
811 		const size_t nosz = 0;
812 
813 		status = v1h_put_NCtype(psp, NC_UNSPECIFIED);
814         if(status != NC_NOERR)
815 			return status;
816 		status = v1h_put_size_t(psp, &nosz);
817         if(status != NC_NOERR)
818 			return status;
819         return NC_NOERR;
820 	}
821 	/* else */
822 
823 	status = v1h_put_NCtype(psp, NC_ATTRIBUTE);
824     if(status != NC_NOERR)
825 		return status;
826 	status = v1h_put_size_t(psp, &ncap->nelems);
827     if(status != NC_NOERR)
828 		return status;
829 
830 	{
831 		const NC_attr **app = (const NC_attr **)ncap->value;
832 		const NC_attr *const *const end = &app[ncap->nelems];
833 		for( /*NADA*/; app < end; app++)
834 		{
835 			status = v1h_put_NC_attr(psp, *app);
836 			if(status)
837 				return status;
838 		}
839 	}
840     return NC_NOERR;
841 }
842 
843 
844 /* Read a NC_attrarray from the header */
845 static int
v1h_get_NC_attrarray(v1hs * gsp,NC_attrarray * ncap)846 v1h_get_NC_attrarray(v1hs *gsp, NC_attrarray *ncap)
847 {
848 	int status;
849 	NCtype type = NC_UNSPECIFIED;
850 
851 	assert(gsp != NULL && gsp->pos != NULL);
852 	assert(ncap != NULL);
853 	assert(ncap->value == NULL);
854 
855 	status = v1h_get_NCtype(gsp, &type);
856     if(status != NC_NOERR)
857 		return status;
858 	status = v1h_get_size_t(gsp, &ncap->nelems);
859     if(status != NC_NOERR)
860 		return status;
861 
862 	if(ncap->nelems == 0)
863         return NC_NOERR;
864 	/* else */
865 	if(type != NC_ATTRIBUTE)
866 		return EINVAL;
867 
868 	ncap->value = (NC_attr **) malloc(ncap->nelems * sizeof(NC_attr *));
869 	if(ncap->value == NULL)
870 		return NC_ENOMEM;
871 	ncap->nalloc = ncap->nelems;
872 
873 	{
874 		NC_attr **app = ncap->value;
875 		NC_attr *const *const end = &app[ncap->nelems];
876 		for( /*NADA*/; app < end; app++)
877 		{
878 			status = v1h_get_NC_attr(gsp, app);
879 			if(status)
880 			{
881 				ncap->nelems = (size_t)(app - ncap->value);
882 				free_NC_attrarrayV(ncap);
883 				return status;
884 			}
885 		}
886 	}
887 
888     return NC_NOERR;
889 }
890 
891 /* End NC_attr */
892 /* Begin NC_var */
893 
894 /*
895  * How much space will the xdr'd var take.
896  * Formerly
897 NC_xlen_var(vpp)
898  */
899 static size_t
ncx_len_NC_var(const NC_var * varp,size_t sizeof_off_t,int version)900 ncx_len_NC_var(const NC_var *varp, size_t sizeof_off_t, int version)
901 {
902 	size_t sz;
903 
904 	assert(varp != NULL);
905 	assert(sizeof_off_t != 0);
906 
907 	sz = ncx_len_NC_string(varp->name, version);
908         if (version == 5) {
909 	    sz += X_SIZEOF_INT64; /* ndims */
910 	    sz += ncx_len_int64(varp->ndims); /* dimids */
911         }
912         else {
913 	    sz += X_SIZEOF_SIZE_T; /* ndims */
914 	    sz += ncx_len_int(varp->ndims); /* dimids */
915 	}
916 	sz += ncx_len_NC_attrarray(&varp->attrs, version);
917 	sz += X_SIZEOF_NC_TYPE; /* nc_type */
918 	sz += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* vsize */
919 	sz += sizeof_off_t; /* begin */
920 
921 	return(sz);
922 }
923 
924 
925 /* Write a NC_var to the header */
926 static int
v1h_put_NC_var(v1hs * psp,const NC_var * varp)927 v1h_put_NC_var(v1hs *psp, const NC_var *varp)
928 {
929 	int status;
930 
931 	status = v1h_put_NC_string(psp, varp->name);
932     if(status != NC_NOERR)
933 		return status;
934 
935 	status = v1h_put_size_t(psp, &varp->ndims);
936     if(status != NC_NOERR)
937 		return status;
938 
939 	if (psp->version == 5) {
940 		status = check_v1hs(psp, ncx_len_int64(varp->ndims));
941         if(status != NC_NOERR)
942 			return status;
943 		status = ncx_putn_longlong_int(&psp->pos,
944 				varp->ndims, varp->dimids, NULL);
945         if(status != NC_NOERR)
946 			return status;
947 	}
948 	else {
949   	    status = check_v1hs(psp, ncx_len_int(varp->ndims));
950         if(status != NC_NOERR)
951 		return status;
952 	    status = ncx_putn_int_int(&psp->pos,
953 			varp->ndims, varp->dimids, NULL);
954         if(status != NC_NOERR)
955 		return status;
956 	}
957 
958 	status = v1h_put_NC_attrarray(psp, &varp->attrs);
959     if(status != NC_NOERR)
960 		return status;
961 
962 	status = v1h_put_nc_type(psp, &varp->type);
963     if(status != NC_NOERR)
964 		return status;
965 
966 	status = v1h_put_size_t(psp, &varp->len);
967     if(status != NC_NOERR)
968 		return status;
969 
970 	status = check_v1hs(psp, psp->version == 1 ? 4 : 8); /*begin*/
971     if(status != NC_NOERR)
972 		 return status;
973 	status = ncx_put_off_t(&psp->pos, &varp->begin, psp->version == 1 ? 4 : 8);
974     if(status != NC_NOERR)
975 		return status;
976 
977     return NC_NOERR;
978 }
979 
980 
981 /* Read a NC_var from the header */
982 static int
v1h_get_NC_var(v1hs * gsp,NC_var ** varpp)983 v1h_get_NC_var(v1hs *gsp, NC_var **varpp)
984 {
985 	NC_string *strp;
986 	int status;
987 	size_t ndims;
988 	NC_var *varp;
989 
990 	status = v1h_get_NC_string(gsp, &strp);
991     if(status != NC_NOERR)
992 		return status;
993 
994 	status = v1h_get_size_t(gsp, &ndims);
995     if(status != NC_NOERR)
996 		goto unwind_name;
997 
998 	varp = new_x_NC_var(strp, ndims);
999 	if(varp == NULL)
1000 	{
1001 		status = NC_ENOMEM;
1002 		goto unwind_name;
1003 	}
1004 
1005 	if (gsp->version == 5) {
1006 		status = check_v1hs(gsp, ncx_len_int64(ndims));
1007         if(status != NC_NOERR)
1008 			goto unwind_alloc;
1009 		status = ncx_getn_longlong_int((const void **)(&gsp->pos),
1010 				ndims, varp->dimids);
1011         if(status != NC_NOERR)
1012 			goto unwind_alloc;
1013 	}
1014 	else {
1015 	    status = check_v1hs(gsp, ncx_len_int(ndims));
1016         if(status != NC_NOERR)
1017 		goto unwind_alloc;
1018 	    status = ncx_getn_int_int((const void **)(&gsp->pos),
1019 			ndims, varp->dimids);
1020         if(status != NC_NOERR)
1021 		goto unwind_alloc;
1022 	}
1023 	status = v1h_get_NC_attrarray(gsp, &varp->attrs);
1024     if(status != NC_NOERR)
1025 		goto unwind_alloc;
1026 	status = v1h_get_nc_type(gsp, &varp->type);
1027     if(status != NC_NOERR)
1028 		 goto unwind_alloc;
1029 
1030 	status = v1h_get_size_t(gsp, &varp->len);
1031     if(status != NC_NOERR)
1032 		 goto unwind_alloc;
1033 
1034 	status = check_v1hs(gsp, gsp->version == 1 ? 4 : 8);
1035     if(status != NC_NOERR)
1036 		 goto unwind_alloc;
1037 	status = ncx_get_off_t((const void **)&gsp->pos,
1038 			       &varp->begin, gsp->version == 1 ? 4 : 8);
1039     if(status != NC_NOERR)
1040 		 goto unwind_alloc;
1041 
1042 	*varpp = varp;
1043     return NC_NOERR;
1044 
1045 unwind_alloc:
1046 	free_NC_var(varp); /* frees name */
1047 	return status;
1048 
1049 unwind_name:
1050 	free_NC_string(strp);
1051 	return status;
1052 }
1053 
1054 
1055 /* How much space in the header is required for this NC_vararray? */
1056 static size_t
ncx_len_NC_vararray(const NC_vararray * ncap,size_t sizeof_off_t,int version)1057 ncx_len_NC_vararray(const NC_vararray *ncap, size_t sizeof_off_t, int version)
1058 {
1059 	size_t xlen = X_SIZEOF_NCTYPE;	/* type */
1060 	xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* count */
1061 	if(ncap == NULL)
1062 		return xlen;
1063 	/* else */
1064 	{
1065 		const NC_var **vpp = (const NC_var **)ncap->value;
1066 		const NC_var *const *const end = &vpp[ncap->nelems];
1067 		for( /*NADA*/; vpp < end; vpp++)
1068 		{
1069 			xlen += ncx_len_NC_var(*vpp, sizeof_off_t, version);
1070 		}
1071 	}
1072 	return xlen;
1073 }
1074 
1075 
1076 /* Write a NC_vararray to the header */
1077 static int
v1h_put_NC_vararray(v1hs * psp,const NC_vararray * ncap)1078 v1h_put_NC_vararray(v1hs *psp, const NC_vararray *ncap)
1079 {
1080 	int status;
1081 
1082 	assert(psp != NULL);
1083 
1084 	if(ncap == NULL
1085 #if 1
1086 		/* Backward:
1087 		 * This clause is for 'byte for byte'
1088 		 * backward compatibility.
1089 		 * Strickly speaking, it is 'bug for bug'.
1090 		 */
1091 		|| ncap->nelems == 0
1092 #endif
1093 		)
1094 	{
1095 		/*
1096 		 * Handle empty netcdf
1097 		 */
1098 		const size_t nosz = 0;
1099 
1100 		status = v1h_put_NCtype(psp, NC_UNSPECIFIED);
1101         if(status != NC_NOERR)
1102 			return status;
1103 		status = v1h_put_size_t(psp, &nosz);
1104         if(status != NC_NOERR)
1105 			return status;
1106         return NC_NOERR;
1107 	}
1108 	/* else */
1109 
1110 	status = v1h_put_NCtype(psp, NC_VARIABLE);
1111     if(status != NC_NOERR)
1112 		return status;
1113 	status = v1h_put_size_t(psp, &ncap->nelems);
1114     if(status != NC_NOERR)
1115 		return status;
1116 
1117 	{
1118 		const NC_var **vpp = (const NC_var **)ncap->value;
1119 		const NC_var *const *const end = &vpp[ncap->nelems];
1120 		for( /*NADA*/; vpp < end; vpp++)
1121 		{
1122 			status = v1h_put_NC_var(psp, *vpp);
1123 			if(status)
1124 				return status;
1125 		}
1126 	}
1127     return NC_NOERR;
1128 }
1129 
1130 
1131 /* Read a NC_vararray from the header */
1132 static int
v1h_get_NC_vararray(v1hs * gsp,NC_vararray * ncap)1133 v1h_get_NC_vararray(v1hs *gsp, NC_vararray *ncap)
1134 {
1135 	int status;
1136 	NCtype type = NC_UNSPECIFIED;
1137 
1138 	assert(gsp != NULL && gsp->pos != NULL);
1139 	assert(ncap != NULL);
1140 	assert(ncap->value == NULL);
1141 
1142 	status = v1h_get_NCtype(gsp, &type);
1143     if(status != NC_NOERR)
1144 		return status;
1145 
1146 	status = v1h_get_size_t(gsp, &ncap->nelems);
1147     if(status != NC_NOERR)
1148 		return status;
1149 
1150 	if(ncap->nelems == 0)
1151         return NC_NOERR;
1152 	/* else */
1153 	if(type != NC_VARIABLE)
1154 		return EINVAL;
1155 
1156 	ncap->value = (NC_var **) malloc(ncap->nelems * sizeof(NC_var *));
1157 	if(ncap->value == NULL)
1158 		return NC_ENOMEM;
1159 	ncap->nalloc = ncap->nelems;
1160 
1161 	ncap->hashmap = NC_hashmapCreate(ncap->nelems);
1162 	{
1163 		NC_var **vpp = ncap->value;
1164 		NC_var *const *const end = &vpp[ncap->nelems];
1165 		for( /*NADA*/; vpp < end; vpp++)
1166 		{
1167 			status = v1h_get_NC_var(gsp, vpp);
1168 			if(status)
1169 			{
1170 				ncap->nelems = (size_t)(vpp - ncap->value);
1171 				free_NC_vararrayV(ncap);
1172 				return status;
1173 			}
1174 			{
1175 			  int varid = (size_t)(vpp - ncap->value);
1176 			  NC_hashmapAddVar(ncap, varid, (*vpp)->name->cp);
1177 			}
1178 		}
1179 	}
1180 
1181     return NC_NOERR;
1182 }
1183 
1184 
1185 /* End NC_var */
1186 /* Begin NC */
1187 
1188 /*
1189  * Recompute the shapes of all variables
1190  * Sets ncp->begin_var to start of first variable.
1191  * Sets ncp->begin_rec to start of first record variable.
1192  * Returns -1 on error. The only possible error is a reference
1193  * to a non existent dimension, which could occur for a corrupted
1194  * netcdf file.
1195  */
1196 static int
NC_computeshapes(NC3_INFO * ncp)1197 NC_computeshapes(NC3_INFO* ncp)
1198 {
1199 	NC_var **vpp = (NC_var **)ncp->vars.value;
1200 	NC_var *const *const end = &vpp[ncp->vars.nelems];
1201 	NC_var *first_var = NULL;	/* first "non-record" var */
1202 	NC_var *first_rec = NULL;	/* first "record" var */
1203 	int status;
1204 
1205 	ncp->begin_var = (off_t) ncp->xsz;
1206 	ncp->begin_rec = (off_t) ncp->xsz;
1207 	ncp->recsize = 0;
1208 
1209 	if(ncp->vars.nelems == 0)
1210 		return(0);
1211 
1212 	for( /*NADA*/; vpp < end; vpp++)
1213 	{
1214 		status = NC_var_shape(*vpp, &ncp->dims);
1215         if(status != NC_NOERR)
1216 			return(status);
1217 
1218 	  	if(IS_RECVAR(*vpp))
1219 		{
1220 	  		if(first_rec == NULL)
1221 				first_rec = *vpp;
1222 			if((*vpp)->len == UINT32_MAX &&
1223                            (fIsSet(ncp->flags, NC_64BIT_OFFSET) ||
1224                             fIsSet(ncp->flags, NC_64BIT_DATA))) /* Flag for large last record */
1225                             ncp->recsize += (*vpp)->dsizes[0] * (*vpp)->xsz;
1226 			else
1227 			    ncp->recsize += (*vpp)->len;
1228 		}
1229 		else
1230 		{
1231 		        if(first_var == NULL)
1232 			        first_var = *vpp;
1233 			/*
1234 			 * Overwritten each time thru.
1235 			 * Usually overwritten in first_rec != NULL clause below.
1236 			 */
1237 			ncp->begin_rec = (*vpp)->begin + (off_t)(*vpp)->len;
1238 		}
1239 	}
1240 
1241 	if(first_rec != NULL)
1242 	{
1243 		if(ncp->begin_rec > first_rec->begin)
1244 		    return(NC_ENOTNC); /* not a netCDF file or corrupted */
1245 		ncp->begin_rec = first_rec->begin;
1246 		/*
1247 	 	 * for special case of exactly one record variable, pack value
1248 	 	 */
1249 		if(ncp->recsize == first_rec->len)
1250 			ncp->recsize = *first_rec->dsizes * first_rec->xsz;
1251 	}
1252 
1253 	if(first_var != NULL)
1254 	{
1255 		ncp->begin_var = first_var->begin;
1256 	}
1257 	else
1258 	{
1259 		ncp->begin_var = ncp->begin_rec;
1260 	}
1261 
1262 	if(ncp->begin_var <= 0 ||
1263 	   ncp->xsz > (size_t)ncp->begin_var ||
1264 	   ncp->begin_rec <= 0 ||
1265 	   ncp->begin_var > ncp->begin_rec)
1266 	    return(NC_ENOTNC); /* not a netCDF file or corrupted */
1267 
1268     return(NC_NOERR);
1269 }
1270 
1271 /* How much space in the header is required for the NC data structure? */
1272 size_t
ncx_len_NC(const NC3_INFO * ncp,size_t sizeof_off_t)1273 ncx_len_NC(const NC3_INFO* ncp, size_t sizeof_off_t)
1274 {
1275 	int version=1;
1276 	size_t xlen = sizeof(ncmagic);
1277 
1278 	assert(ncp != NULL);
1279 	if (fIsSet(ncp->flags, NC_64BIT_DATA)) /* CDF-5 */
1280 		version = 5;
1281     	else if (fIsSet(ncp->flags, NC_64BIT_OFFSET)) /* CDF-2 */
1282 		version = 2;
1283 
1284 	xlen += (version == 5) ? X_SIZEOF_INT64 : X_SIZEOF_SIZE_T; /* numrecs */
1285 	xlen += ncx_len_NC_dimarray(&ncp->dims, version);
1286 	xlen += ncx_len_NC_attrarray(&ncp->attrs, version);
1287 	xlen += ncx_len_NC_vararray(&ncp->vars, sizeof_off_t, version);
1288 
1289 	return xlen;
1290 }
1291 
1292 
1293 /* Write the file header */
1294 int
ncx_put_NC(const NC3_INFO * ncp,void ** xpp,off_t offset,size_t extent)1295 ncx_put_NC(const NC3_INFO* ncp, void **xpp, off_t offset, size_t extent)
1296 {
1297     int status = NC_NOERR;
1298 	v1hs ps; /* the get stream */
1299 
1300 	assert(ncp != NULL);
1301 
1302 	/* Initialize stream ps */
1303 
1304 	ps.nciop = ncp->nciop;
1305 	ps.flags = RGN_WRITE;
1306 
1307 	if (ncp->flags & NC_64BIT_DATA)
1308 	  ps.version = 5;
1309 	else if (ncp->flags & NC_64BIT_OFFSET)
1310 	  ps.version = 2;
1311 	else
1312 	  ps.version = 1;
1313 
1314 	if(xpp == NULL)
1315 	{
1316 		/*
1317 		 * Come up with a reasonable stream read size.
1318 		 */
1319 		extent = ncp->xsz;
1320 		if(extent <= ((ps.version==5)?MIN_NC5_XSZ:MIN_NC3_XSZ))
1321 		{
1322 			/* first time read */
1323 			extent = ncp->chunk;
1324 			/* Protection for when ncp->chunk is huge;
1325 			 * no need to read hugely. */
1326 	      		if(extent > 4096)
1327 				extent = 4096;
1328 		}
1329 		else if(extent > ncp->chunk)
1330 		    extent = ncp->chunk;
1331 
1332 		ps.offset = 0;
1333 		ps.extent = extent;
1334 		ps.base = NULL;
1335 		ps.pos = ps.base;
1336 
1337 		status = fault_v1hs(&ps, extent);
1338 		if(status)
1339 			return status;
1340 	}
1341 	else
1342 	{
1343 		ps.offset = offset;
1344 		ps.extent = extent;
1345 		ps.base = *xpp;
1346 		ps.pos = ps.base;
1347 		ps.end = (char *)ps.base + ps.extent;
1348 	}
1349 
1350 	if (ps.version == 5)
1351 	  status = ncx_putn_schar_schar(&ps.pos, sizeof(ncmagic5), ncmagic5, NULL);
1352 	else if (ps.version == 2)
1353 	  status = ncx_putn_schar_schar(&ps.pos, sizeof(ncmagic), ncmagic, NULL);
1354 	else
1355 	  status = ncx_putn_schar_schar(&ps.pos, sizeof(ncmagic1), ncmagic1, NULL);
1356 	if(status != NC_NOERR)
1357 		goto release;
1358 
1359 	{
1360 	const size_t nrecs = NC_get_numrecs(ncp);
1361 	if (ps.version == 5) {
1362             unsigned long long tmp = (unsigned long long) nrecs;
1363 	    status = ncx_put_uint64(&ps.pos, tmp);
1364         }
1365        	else
1366 	    status = ncx_put_size_t(&ps.pos, &nrecs);
1367 	if(status != NC_NOERR)
1368 		goto release;
1369 	}
1370 
1371 	assert((char *)ps.pos < (char *)ps.end);
1372 
1373 	status = v1h_put_NC_dimarray(&ps, &ncp->dims);
1374     if(status != NC_NOERR)
1375 		goto release;
1376 
1377 	status = v1h_put_NC_attrarray(&ps, &ncp->attrs);
1378     if(status != NC_NOERR)
1379 		goto release;
1380 
1381 	status = v1h_put_NC_vararray(&ps, &ncp->vars);
1382     if(status != NC_NOERR)
1383 		goto release;
1384 
1385 release:
1386 	(void) rel_v1hs(&ps);
1387 
1388 	return status;
1389 }
1390 
1391 
1392 /* Make the in-memory NC structure from reading the file header */
1393 int
nc_get_NC(NC3_INFO * ncp)1394 nc_get_NC(NC3_INFO* ncp)
1395 {
1396 	int status;
1397 	v1hs gs; /* the get stream */
1398 
1399 	assert(ncp != NULL);
1400 
1401 	/* Initialize stream gs */
1402 
1403 	gs.nciop = ncp->nciop;
1404 	gs.offset = 0; /* beginning of file */
1405 	gs.extent = 0;
1406 	gs.flags = 0;
1407 	gs.version = 0;
1408 	gs.base = NULL;
1409 	gs.pos = gs.base;
1410 
1411 	{
1412 		/*
1413 		 * Come up with a reasonable stream read size.
1414 		 */
1415 	        off_t filesize;
1416 		size_t extent = ncp->xsz;
1417 
1418 		if(extent <= ((fIsSet(ncp->flags, NC_64BIT_DATA))?MIN_NC5_XSZ:MIN_NC3_XSZ))
1419 		{
1420 		        status = ncio_filesize(ncp->nciop, &filesize);
1421 			if(status)
1422 			    return status;
1423 			if(filesize < sizeof(ncmagic)) { /* too small, not netcdf */
1424 
1425 			    status = NC_ENOTNC;
1426 			    return status;
1427 			}
1428 			/* first time read */
1429 			extent = ncp->chunk;
1430 			/* Protection for when ncp->chunk is huge;
1431 			 * no need to read hugely. */
1432 	      		if(extent > 4096)
1433 				extent = 4096;
1434 			if(extent > filesize)
1435 			        extent = (size_t)filesize;
1436 		}
1437 		else if(extent > ncp->chunk)
1438 		    extent = ncp->chunk;
1439 
1440 		/*
1441 		 * Invalidate the I/O buffers to force a read of the header
1442 		 * region.
1443 		 */
1444 		status = ncio_sync(gs.nciop);
1445 		if(status)
1446 			return status;
1447 
1448 		status = fault_v1hs(&gs, extent);
1449 		if(status)
1450 			return status;
1451 	}
1452 
1453 	/* get the header from the stream gs */
1454 
1455 	{
1456 		/* Get & check magic number */
1457 		schar magic[sizeof(ncmagic)];
1458 		(void) memset(magic, 0, sizeof(magic));
1459 
1460 		status = ncx_getn_schar_schar(
1461 			(const void **)(&gs.pos), sizeof(magic), magic);
1462         if(status != NC_NOERR)
1463 			goto unwind_get;
1464 
1465 		if(memcmp(magic, ncmagic, sizeof(ncmagic)-1) != 0)
1466 		{
1467 			status = NC_ENOTNC;
1468 			goto unwind_get;
1469 		}
1470 		/* Check version number in last byte of magic */
1471 		if (magic[sizeof(ncmagic)-1] == 0x1) {
1472 		  gs.version = 1;
1473 		} else if (magic[sizeof(ncmagic)-1] == 0x2) {
1474 		  gs.version = 2;
1475 		  fSet(ncp->flags, NC_64BIT_OFFSET);
1476 		  /* Now we support version 2 file access on non-LFS systems -- rkr */
1477 #if 0
1478 		  if (sizeof(off_t) != 8) {
1479 		    fprintf(stderr, "NETCDF WARNING: Version 2 file on 32-bit system.\n");
1480 		  }
1481 #endif
1482 		} else if (magic[sizeof(ncmagic)-1] == 0x5) {
1483 		  gs.version = 5;
1484 		  fSet(ncp->flags, NC_64BIT_DATA);
1485 		} else {
1486 			status = NC_ENOTNC;
1487 			goto unwind_get;
1488 		}
1489 	}
1490 
1491 	{
1492 	size_t nrecs = 0;
1493        	if (gs.version == 5) {
1494 		unsigned long long tmp = 0;
1495 		status = ncx_get_uint64((const void **)(&gs.pos), &tmp);
1496 		nrecs = (size_t)tmp;
1497        	}
1498        	else
1499 	    status = ncx_get_size_t((const void **)(&gs.pos), &nrecs);
1500     if(status != NC_NOERR)
1501 		goto unwind_get;
1502 	NC_set_numrecs(ncp, nrecs);
1503 	}
1504 
1505 	assert((char *)gs.pos < (char *)gs.end);
1506 
1507 	status = v1h_get_NC_dimarray(&gs, &ncp->dims);
1508     if(status != NC_NOERR)
1509 		goto unwind_get;
1510 
1511 	status = v1h_get_NC_attrarray(&gs, &ncp->attrs);
1512     if(status != NC_NOERR)
1513 		goto unwind_get;
1514 
1515 	status = v1h_get_NC_vararray(&gs, &ncp->vars);
1516     if(status != NC_NOERR)
1517 		goto unwind_get;
1518 
1519 	ncp->xsz = ncx_len_NC(ncp, (gs.version == 1) ? 4 : 8);
1520 
1521 	status = NC_computeshapes(ncp);
1522     if(status != NC_NOERR)
1523 		goto unwind_get;
1524 
1525 	status = NC_check_vlens(ncp);
1526     if(status != NC_NOERR)
1527 		goto unwind_get;
1528 
1529 unwind_get:
1530 	(void) rel_v1hs(&gs);
1531 	return status;
1532 }
1533