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