Coverage Report

Created: 2026-07-16 06:49

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