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