1 /*
2 Wei-keng Liao's (wkliao@eecs.northwestern.edu)
3 netcdf-3 validator program
4 (https://github.com/Parallel-NetCDF/PnetCDF/blob/master/src/utils/ncvalidator/ncvalidator.c)
5 */
6
7 /*
8 Copyright (c) 2003 Northwestern University and Argonne National Laboratory
9 All rights reserved.
10
11 Portions of this software were developed by the Unidata Program at the
12 University Corporation for Atmospheric Research.
13
14 Access and use of this software shall impose the following obligations and
15 understandings on the user. The user is granted the right, without any
16 fee or
17 cost, to use, copy, modify, alter, enhance and distribute this
18 software, and
19 any derivative works thereof, and its supporting documentation for any
20 purpose
21 whatsoever, provided that this entire notice appears in all copies of the
22 software, derivative works and supporting documentation. Further,
23 Northwestern
24 University and Argonne National Laboratory request that the user credit
25 Northwestern University and Argonne National Laboratory in any
26 publications
27 that result from the use of this software or in any product that
28 includes this
29 software. The names Northwestern University and Argonne National
30 Laboratory,
31 however, may not be used in any advertising or publicity to endorse or
32 promote
33 any products or commercial entity unless specific written permission is
34 obtained from Northwestern University and Argonne National Laboratory.
35 The user
36 also understands that Northwestern University and Argonne National
37 Laboratory
38 are not obligated to provide the user with any support, consulting,
39 training or
40 assistance of any kind with regard to the use, operation and
41 performance of
42 this software nor to provide the user with any updates, revisions, new
43 versions
44 or "bug fixes."
45
46 THIS SOFTWARE IS PROVIDED BY NORTHWESTERN UNIVERSITY AND ARGONNE NATIONAL
47 LABORATORY "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
48 BUT NOT
49 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
50 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NORTHWESTERN
51 UNIVERSITY
52 AND ARGONNE NATIONAL LABORATORY BE LIABLE FOR ANY SPECIAL, INDIRECT OR
53 CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
54 USE,
55 DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
56 TORTIOUS
57 ACTION, ARISING OUT OF OR IN CONNECTION WITH THE ACCESS, USE OR
58 PERFORMANCE OF
59 THIS SOFTWARE.
60 */
61
62 #include "config.h"
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <sys/types.h> /* open() */
66 #include <sys/stat.h> /* open() */
67 #include <fcntl.h> /* open() */
68 #include <string.h> /* strcpy(), strncpy() */
69 #include <inttypes.h> /* check for Endianness, uint32_t*/
70 #include <assert.h>
71 #include <errno.h>
72 #ifdef HAVE_UNISTD_H
73 #include <unistd.h> /* read() getopt() */
74 #endif
75
76 #ifdef _WIN32
77 #include <io.h>
78 #define snprintf _snprintf
79 #include "XGetopt.h"
80 int opterr;
81 int optind;
82 #endif
83
84 #define X_ALIGN 4
85 #define X_INT_MAX 2147483647
86 #define X_UINT_MAX 4294967295U
87 #define X_INT64_MAX 9223372036854775807LL
88
89 #ifndef EXIT_FAILURE
90 #ifndef vms
91 #define EXIT_SUCCESS 0
92 #define EXIT_FAILURE 1
93 #else
94 /* In OpenVMS, success is indicated by odd values and failure by even values. */
95 #define EXIT_SUCCESS 1
96 #define EXIT_FAILURE 0
97 #endif
98 #endif
99
100 static int verbose, trace;
101 static int repair;
102 static const char nada[4] = {0, 0, 0, 0};
103
104 #ifndef MAX
105 #define MAX(mm,nn) (((mm) > (nn)) ? (mm) : (nn))
106 #endif
107 #ifndef MIN
108 #define MIN(mm,nn) (((mm) < (nn)) ? (mm) : (nn))
109 #endif
110
111 /* useful for aligning memory */
112 #define _RNDUP(x, unit) ((((x) + (unit) - 1) / (unit)) * (unit))
113
114 #define ERR_ADDR (((size_t) gbp->pos - (size_t) gbp->base) + gbp->offset - gbp->size)
115
116 #define IS_RECVAR(vp) ((vp)->shape != NULL ? (*(vp)->shape == NC_UNLIMITED) : 0 )
117
118 #ifdef PNETCDF_DEBUG
119 #define DEBUG_RETURN_ERROR(err) { \
120 if (verbose) printf("\t(Error %s at line %d in file %s)\n", \
121 #err,__LINE__,__FILE__); \
122 return err; \
123 }
124 #define DEBUG_ASSIGN_ERROR(status, err) { \
125 if (verbose) printf("\t(Error %s at line %d in file %s)\n", \
126 #err,__LINE__,__FILE__); \
127 status = err; \
128 }
129 #else
130 #define DEBUG_RETURN_ERROR(err) return err;
131 #define DEBUG_ASSIGN_ERROR(status, err) { status = err; }
132 #endif
133
134 #define NC_UNLIMITED 0L
135 #define NC_ARRAY_GROWBY 64
136
137 #define NC_MAX_INT 2147483647
138 #define NC_MAX_DIMS NC_MAX_INT
139 #define NC_MAX_ATTRS NC_MAX_INT
140 #define NC_MAX_VARS NC_MAX_INT
141 #define NC_MAX_VAR_DIMS NC_MAX_INT /* max per-variable dimensions */
142
143 #define NC_NAT 0 /**< Not A Type */
144 #define NC_BYTE 1 /**< signed 1 byte integer */
145 #define NC_CHAR 2 /**< ISO/ASCII character */
146 #define NC_SHORT 3 /**< signed 2 byte integer */
147 #define NC_INT 4 /**< signed 4 byte integer */
148 #define NC_LONG NC_INT
149 #define NC_FLOAT 5 /**< single precision floating point number */
150 #define NC_DOUBLE 6 /**< double precision floating point number */
151 #define NC_UBYTE 7 /**< unsigned 1 byte int */
152 #define NC_USHORT 8 /**< unsigned 2-byte int */
153 #define NC_UINT 9 /**< unsigned 4-byte int */
154 #define NC_INT64 10 /**< signed 8-byte int */
155 #define NC_UINT64 11 /**< unsigned 8-byte int */
156
157 typedef int nc_type;
158
159 #define MIN_NC_XSZ 32
160 #define NC_DEFAULT_CHUNKSIZE 1048576
161
162 typedef enum {
163 NC_INVALID = -1, /* invalid */
164 NC_UNSPECIFIED = 0, /* ABSENT */
165 NC_DIMENSION = 10, /* \x00 \x00 \x00 \x0A */
166 NC_VARIABLE = 11, /* \x00 \x00 \x00 \x0B */
167 NC_ATTRIBUTE = 12 /* \x00 \x00 \x00 \x0C */
168 } NC_tag;
169
170 typedef struct {
171 char *name;
172 size_t name_len;
173 long long size;
174 } NC_dim;
175
176 typedef struct NC_dimarray {
177 int ndefined; /* number of defined dimensions */
178 int unlimited_id; /* ID of unlimited dimension */
179 NC_dim **value;
180 } NC_dimarray;
181
182 typedef struct {
183 long long xsz; /* amount of space at xvalue (4-byte aligned) */
184 char *name; /* name of the attributes */
185 size_t name_len;
186 nc_type xtype; /* the discriminant */
187 long long nelems; /* number of attribute elements */
188 void *xvalue; /* the actual data, in external representation */
189 } NC_attr;
190
191 typedef struct NC_attrarray {
192 int ndefined; /* number of defined attributes */
193 NC_attr **value;
194 } NC_attrarray;
195
196 typedef struct {
197 int xsz; /* byte size of 1 array element */
198 long long *shape; /* dim->size of each dim */
199 long long *dsizes; /* the right to left product of shape */
200 char *name; /* name of the variable */
201 size_t name_len;
202 int ndims; /* number of dimensions */
203 int *dimids; /* array of dimension IDs */
204 NC_attrarray attrs; /* attribute array */
205 nc_type xtype; /* variable's data type */
206 long long len; /* this is the "vsize" defined in header format, the
207 total size in bytes of the array variable.
208 For record variable, this is the record size */
209 long long begin; /* starting file offset of this variable */
210 } NC_var;
211
212 typedef struct NC_vararray {
213 int ndefined; /* number of defined variables */
214 int num_rec_vars;/* number of defined record variables */
215 NC_var **value;
216 } NC_vararray;
217
218 typedef struct NC {
219 int format;
220 char *path;
221 long long xsz; /* external size of this header, <= var[0].begin */
222 long long begin_var;/* file offset of the first (non-record) var */
223 long long begin_rec;/* file offset of the first 'record' */
224
225 long long recsize; /* length of 'record': sum of single record sizes
226 of all the record variables */
227 long long numrecs; /* number of 'records' allocated */
228 NC_dimarray dims; /* dimensions defined */
229 NC_attrarray attrs; /* global attributes defined */
230 NC_vararray vars; /* variables defined */
231 } NC;
232
233 typedef struct bufferinfo {
234 int is_little_endian;
235 int fd;
236 off_t offset; /* current read/write offset in the file */
237 int version; /* 1, 2, and 5 for CDF-1, 2, and 5 respectively */
238 void *base; /* beginning of read/write buffer */
239 void *pos; /* current position in buffer */
240 size_t size; /* size of the buffer */
241 } bufferinfo;
242
243 #define NC_NOERR 0 /**< No Error */
244 #define NC_EMAXDIMS (-41) /**< NC_MAX_DIMS or NC_MAX_VAR_DIMS exceeds */
245 #define NC_EMAXATTS (-44) /**< NC_MAX_ATTRS exceeded */
246 #define NC_EBADTYPE (-45) /**< Not a netcdf data type */
247 #define NC_EBADDIM (-46) /**< Invalid dimension id or name */
248 #define NC_EUNLIMPOS (-47) /**< NC_UNLIMITED in the wrong index */
249 #define NC_EMAXVARS (-48)
250 #define NC_ENOTNC (-51) /**< Not a netcdf file (file format violates CDF specification) */
251 #define NC_EUNLIMIT (-54) /**< NC_UNLIMITED size already in use */
252 #define NC_ENOMEM (-61) /**< Memory allocation (malloc) failure */
253 #define NC_EVARSIZE (-62) /**< One or more variable sizes violate format constraints */
254 #define NC_EFILE (-204) /**< Unknown error in file operation */
255 #define NC_ENOTSUPPORT (-214) /**< Feature is not yet supported */
256 #define NC_ENULLPAD (-134) /**< Header Bytes not Null-Byte padded */
257
258 /*
259 * "magic number" at beginning of file: 0x43444601 (big Endian)
260 */
261 static const char ncmagic[] = {'C', 'D', 'F', 0x01};
262
263 #define ABSENT 0
264
265 #define SWAP4B(a) ( ((a) << 24) | \
266 (((a) << 8) & 0x00ff0000) | \
267 (((a) >> 8) & 0x0000ff00) | \
268 (((a) >> 24) & 0x000000ff) )
269
270 #define SWAP8B(a) ( (((a) & 0x00000000000000FFULL) << 56) | \
271 (((a) & 0x000000000000FF00ULL) << 40) | \
272 (((a) & 0x0000000000FF0000ULL) << 24) | \
273 (((a) & 0x00000000FF000000ULL) << 8) | \
274 (((a) & 0x000000FF00000000ULL) >> 8) | \
275 (((a) & 0x0000FF0000000000ULL) >> 24) | \
276 (((a) & 0x00FF000000000000ULL) >> 40) | \
277 (((a) & 0xFF00000000000000ULL) >> 56) )
278
check_little_endian(void)279 static int check_little_endian(void)
280 {
281 /* return 0 for big endian, 1 for little endian. */
282 volatile uint32_t i=0x01234567;
283 return (*((uint8_t*)(&i))) == 0x67;
284 }
285
286 static void
swap4b(void * val)287 swap4b(void *val)
288 {
289 uint32_t *op = (uint32_t*)val;
290 *op = SWAP4B(*op);
291 }
292
293 static void
swap8b(unsigned long long * val)294 swap8b(unsigned long long *val)
295 {
296 uint64_t *op = (uint64_t*)val;
297 *op = SWAP8B(*op);
298 }
299
300 static unsigned long long
get_uint64(bufferinfo * gbp)301 get_uint64(bufferinfo *gbp)
302 {
303 /* retrieve a 64bit unsigned integer and return it as unsigned long long */
304 unsigned long long tmp;
305 memcpy(&tmp, gbp->pos, 8);
306 if (gbp->is_little_endian) swap8b(&tmp);
307 gbp->pos = (char*)gbp->pos + 8; /* advance gbp->pos 8 bytes */
308 return tmp;
309 }
310
311 static unsigned int
get_uint32(bufferinfo * gbp)312 get_uint32(bufferinfo *gbp)
313 {
314 /* retrieve a 32bit unsigned integer and return it as unsigned int */
315 unsigned int tmp;
316 memcpy(&tmp, gbp->pos, 4);
317 if (gbp->is_little_endian) swap4b(&tmp);
318 gbp->pos = (char*)gbp->pos + 4; /* advance gbp->pos 4 bytes */
319 return tmp;
320 }
321
322 static void
free_NC_dim(NC_dim * dimp)323 free_NC_dim(NC_dim *dimp)
324 {
325 if (dimp == NULL) return;
326 free(dimp->name);
327 free(dimp);
328 }
329
330 static void
free_NC_dimarray(NC_dimarray * ncap)331 free_NC_dimarray(NC_dimarray *ncap)
332 {
333 int i;
334
335 assert(ncap != NULL);
336 if (ncap->value == NULL) return;
337
338 for (i=0; i<ncap->ndefined; i++)
339 if (ncap->value[i] != NULL)
340 free_NC_dim(ncap->value[i]);
341
342 free(ncap->value);
343 ncap->value = NULL;
344 ncap->ndefined = 0;
345 }
346
347 static void
free_NC_attr(NC_attr * attrp)348 free_NC_attr(NC_attr *attrp)
349 {
350 if (attrp == NULL) return;
351 free(attrp->name);
352 if (attrp->xvalue != NULL) free(attrp->xvalue);
353 free(attrp);
354 }
355
356 static void
free_NC_attrarray(NC_attrarray * ncap)357 free_NC_attrarray(NC_attrarray *ncap)
358 {
359 int i;
360
361 assert(ncap != NULL);
362 if (ncap->value == NULL) return;
363
364 for (i=0; i<ncap->ndefined; i++)
365 free_NC_attr(ncap->value[i]);
366
367 free(ncap->value);
368 ncap->value = NULL;
369 ncap->ndefined = 0;
370 }
371
372 static void
free_NC_var(NC_var * varp)373 free_NC_var(NC_var *varp)
374 {
375 if (varp == NULL) return;
376 free_NC_attrarray(&varp->attrs);
377 free(varp->name);
378 free(varp->shape);
379 free(varp->dsizes);
380 free(varp->dimids);
381 free(varp);
382 }
383
384 static void
free_NC_vararray(NC_vararray * ncap)385 free_NC_vararray(NC_vararray *ncap)
386 {
387 int i;
388
389 assert(ncap != NULL);
390 if (ncap->value == NULL) return;
391
392 for (i=0; i<ncap->ndefined; i++) {
393 if (ncap->value[i] != NULL)
394 free_NC_var(ncap->value[i]);
395 }
396
397 free(ncap->value);
398 ncap->value = NULL;
399 ncap->ndefined = 0;
400 }
401
402 /*
403 * To compute how much space will the xdr'd header take
404 */
405
406 /*----< hdr_len_NC_name() >--------------------------------------------------*/
407 static long long
hdr_len_NC_name(size_t nchars,int sizeof_t)408 hdr_len_NC_name(size_t nchars,
409 int sizeof_t) /* NON_NEG */
410 {
411 /* netCDF file format:
412 * name = nelems namestring
413 * nelems = NON_NEG
414 * namestring = ID1 [IDN ...] padding
415 * ID1 = alphanumeric | '_'
416 * IDN = alphanumeric | special1 | special2
417 * padding = <0, 1, 2, or 3 bytes to next 4-byte boundary>
418 * NON_NEG = <non-negative INT> | // CDF-1 and CDF-2
419 * <non-negative INT64> // CDF-5
420 */
421 long long sz = sizeof_t; /* nelems */
422
423 if (nchars != 0) /* namestring */
424 sz += _RNDUP(nchars, X_ALIGN);
425
426 return sz;
427 }
428
429 /*----< hdr_len_NC_dim() >---------------------------------------------------*/
430 static long long
hdr_len_NC_dim(const NC_dim * dimp,int sizeof_t)431 hdr_len_NC_dim(const NC_dim *dimp,
432 int sizeof_t) /* NON_NEG */
433 {
434 /* netCDF file format:
435 * ...
436 * dim = name dim_length
437 * dim_length = NON_NEG
438 * NON_NEG = <non-negative INT> | // CDF-1 and CDF-2
439 * <non-negative INT64> // CDF-5
440 */
441 long long sz;
442
443 assert(dimp != NULL);
444
445 sz = hdr_len_NC_name(dimp->name_len, sizeof_t); /* name */
446 sz += sizeof_t; /* dim_length */
447
448 return sz;
449 }
450
451 /*----< hdr_len_NC_dimarray() >----------------------------------------------*/
452 static long long
hdr_len_NC_dimarray(const NC_dimarray * ncap,int sizeof_t)453 hdr_len_NC_dimarray(const NC_dimarray *ncap,
454 int sizeof_t) /* NON_NEG */
455 {
456 /* netCDF file format:
457 * ...
458 * dim_list = ABSENT | NC_DIMENSION nelems [dim ...]
459 * ABSENT = ZERO ZERO | // list is not present for CDF-1 and 2
460 * ZERO ZERO64 // for CDF-5
461 * ZERO = \x00 \x00 \x00 \x00 // 32-bit zero
462 * ZERO64 = \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 // 64-bit zero
463 * NC_DIMENSION = \x00 \x00 \x00 \x0A // tag for list of dimensions
464 * nelems = NON_NEG // number of elements in following sequence
465 * NON_NEG = <non-negative INT> | // CDF-1 and CDF-2
466 * <non-negative INT64> // CDF-5
467 */
468 int i;
469 long long xlen;
470
471 xlen = 4; /* NC_DIMENSION */
472 xlen += sizeof_t; /* nelems */
473
474 if (ncap == NULL) /* ABSENT: no dimension is defined */
475 return xlen;
476
477 /* [dim ...] */
478 for (i=0; i<ncap->ndefined; i++)
479 xlen += hdr_len_NC_dim(ncap->value[i], sizeof_t);
480
481 return xlen;
482 }
483
484 /*----< hdr_len_NC_attr() >--------------------------------------------------*/
485 static long long
hdr_len_NC_attr(const NC_attr * attrp,int sizeof_t)486 hdr_len_NC_attr(const NC_attr *attrp,
487 int sizeof_t) /* NON_NEG */
488 {
489 /* netCDF file format:
490 * ...
491 * attr = name nc_type nelems [values ...]
492 * nc_type = NC_BYTE | NC_CHAR | NC_SHORT | ...
493 * nelems = NON_NEG // number of elements in following sequence
494 * values = bytes | chars | shorts | ints | floats | doubles
495 * bytes = [BYTE ...] padding
496 * chars = [CHAR ...] padding
497 * shorts = [SHORT ...] padding
498 * ints = [INT ...]
499 * floats = [FLOAT ...]
500 * doubles = [DOUBLE ...]
501 * padding = <0, 1, 2, or 3 bytes to next 4-byte boundary>
502 * NON_NEG = <non-negative INT> | // CDF-1 and CDF-2
503 * <non-negative INT64> // CDF-5
504 */
505 long long sz;
506
507 assert(attrp != NULL);
508
509 sz = hdr_len_NC_name(attrp->name_len, sizeof_t); /* name */
510 sz += 4; /* nc_type */
511 sz += sizeof_t; /* nelems */
512 sz += attrp->xsz; /* [values ...] */
513
514 return sz;
515 }
516
517 /*----< hdr_len_NC_attrarray() >---------------------------------------------*/
518 static long long
hdr_len_NC_attrarray(const NC_attrarray * ncap,int sizeof_t)519 hdr_len_NC_attrarray(const NC_attrarray *ncap,
520 int sizeof_t) /* NON_NEG */
521 {
522 /* netCDF file format:
523 * ...
524 * att_list = ABSENT | NC_ATTRIBUTE nelems [attr ...]
525 * ABSENT = ZERO ZERO | // list is not present for CDF-1 and 2
526 * ZERO ZERO64 // for CDF-5
527 * ZERO = \x00 \x00 \x00 \x00 // 32-bit zero
528 * ZERO64 = \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 // 64-bit zero
529 * NC_ATTRIBUTE = \x00 \x00 \x00 \x0C // tag for list of attributes
530 * nelems = NON_NEG // number of elements in following sequence
531 * NON_NEG = <non-negative INT> | // CDF-1 and CDF-2
532 * <non-negative INT64> // CDF-5
533 */
534 int i;
535 long long xlen;
536
537 xlen = 4; /* NC_ATTRIBUTE */
538 xlen += sizeof_t; /* nelems */
539
540 if (ncap == NULL) /* ABSENT: no attribute is defined */
541 return xlen;
542
543 for (i=0; i<ncap->ndefined; i++) /* [attr ...] */
544 xlen += hdr_len_NC_attr(ncap->value[i], sizeof_t);
545
546 return xlen;
547 }
548
549 /*----< hdr_len_NC_var() >---------------------------------------------------*/
550 static long long
hdr_len_NC_var(const NC_var * varp,int sizeof_off_t,int sizeof_t)551 hdr_len_NC_var(const NC_var *varp,
552 int sizeof_off_t, /* OFFSET */
553 int sizeof_t) /* NON_NEG */
554 {
555 /* netCDF file format:
556 * netcdf_file = header data
557 * header = magic numrecs dim_list gatt_list var_list
558 * ...
559 * var = name nelems [dimid ...] vatt_list nc_type vsize begin
560 * nelems = NON_NEG
561 * dimid = NON_NEG
562 * vatt_list = att_list
563 * nc_type = NC_BYTE | NC_CHAR | NC_SHORT | ...
564 * vsize = NON_NEG
565 * begin = OFFSET // Variable start location.
566 * OFFSET = <non-negative INT> | // CDF-1
567 * <non-negative INT64> // CDF-2 and CDF-5
568 * NON_NEG = <non-negative INT> | // CDF-1 and CDF-2
569 * <non-negative INT64> // CDF-5
570 */
571 long long sz;
572
573 assert(varp != NULL);
574
575 /* for CDF-1, sizeof_off_t == 4 && sizeof_t == 4
576 * for CDF-2, sizeof_off_t == 8 && sizeof_t == 4
577 * for CDF-5, sizeof_off_t == 8 && sizeof_t == 8
578 */
579 sz = hdr_len_NC_name(varp->name_len, sizeof_t); /* name */
580 sz += sizeof_t; /* nelems */
581 sz += ((long long)sizeof_t) * varp->ndims; /* [dimid ...] */
582 sz += hdr_len_NC_attrarray(&varp->attrs, sizeof_t); /* vatt_list */
583 sz += 4; /* nc_type */
584 sz += sizeof_t; /* vsize */
585 sz += sizeof_off_t; /* begin */
586
587 return sz;
588 }
589
590 /*----< hdr_len_NC_vararray() >----------------------------------------------*/
591 static long long
hdr_len_NC_vararray(const NC_vararray * ncap,int sizeof_t,int sizeof_off_t)592 hdr_len_NC_vararray(const NC_vararray *ncap,
593 int sizeof_t, /* NON_NEG */
594 int sizeof_off_t) /* OFFSET */
595 {
596 /* netCDF file format:
597 * netcdf_file = header data
598 * header = magic numrecs dim_list gatt_list var_list
599 * ...
600 * var_list = ABSENT | NC_VARIABLE nelems [var ...]
601 * ABSENT = ZERO ZERO | // list is not present for CDF-1 and 2
602 * ZERO ZERO64 // for CDF-5
603 * ZERO = \x00 \x00 \x00 \x00 // 32-bit zero
604 * ZERO64 = \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 // 64-bit zero
605 * NC_VARIABLE = \x00 \x00 \x00 \x0B // tag for list of variables
606 * nelems = NON_NEG // number of elements in following sequence
607 * NON_NEG = <non-negative INT> | // CDF-1 and CDF-2
608 * <non-negative INT64> // CDF-5
609 */
610 int i;
611 long long xlen;
612
613 xlen = 4; /* NC_VARIABLE */
614 xlen += sizeof_t; /* nelems */
615
616 if (ncap == NULL) /* ABSENT: no variable is defined */
617 return xlen;
618
619 /* for CDF-1, sizeof_off_t == 4 && sizeof_t == 4
620 * for CDF-2, sizeof_off_t == 8 && sizeof_t == 4
621 * for CDF-5, sizeof_off_t == 8 && sizeof_t == 8
622 */
623 for (i=0; i<ncap->ndefined; i++) /* [var ...] */
624 xlen += hdr_len_NC_var(ncap->value[i], sizeof_off_t, sizeof_t);
625
626 return xlen;
627 }
628
629 /*----< hdr_len_NC() >-------------------------------------------------------*/
630 static long long
hdr_len_NC(const NC * ncp)631 hdr_len_NC(const NC *ncp)
632 {
633 /* netCDF file format:
634 * netcdf_file = header data
635 * header = magic numrecs dim_list gatt_list var_list
636 * ...
637 * numrecs = NON_NEG | STREAMING // length of record dimension
638 * NON_NEG = <non-negative INT> | // CDF-1 and CDF-2
639 * <non-negative INT64> // CDF-5
640 */
641
642 int sizeof_t, sizeof_off_t;
643 long long xlen;
644
645 assert(ncp != NULL);
646
647 if (ncp->format == 5) { /* CDF-5 */
648 sizeof_t = 8; /* 8-byte integer for all integers */
649 sizeof_off_t = 8; /* 8-byte integer for var begin */
650 }
651 else if (ncp->format == 2) { /* CDF-2 */
652 sizeof_t = 4; /* 4-byte integer in CDF-1 */
653 sizeof_off_t = 8; /* 8-byte integer for var begin */
654 }
655 else { /* CDF-1 */
656 sizeof_t = 4; /* 4-byte integer in CDF-1 */
657 sizeof_off_t = 4; /* 4-byte integer in CDF-1 */
658 }
659
660 xlen = sizeof(ncmagic); /* magic */
661 xlen += sizeof_t; /* numrecs */
662 xlen += hdr_len_NC_dimarray(&ncp->dims, sizeof_t); /* dim_list */
663 xlen += hdr_len_NC_attrarray(&ncp->attrs, sizeof_t); /* gatt_list */
664 xlen += hdr_len_NC_vararray(&ncp->vars, sizeof_t, sizeof_off_t); /* var_list */
665
666 return xlen; /* return the header size (not yet aligned) */
667 }
668
669 /*----< ncmpio_xlen_nc_type() >----------------------------------------------*/
670 /* return the length of external NC data type */
671 static int
xlen_nc_type(nc_type xtype)672 xlen_nc_type(nc_type xtype) {
673 switch(xtype) {
674 case NC_BYTE:
675 case NC_CHAR:
676 case NC_UBYTE: return 1;
677 case NC_SHORT:
678 case NC_USHORT: return 2;
679 case NC_INT:
680 case NC_UINT:
681 case NC_FLOAT: return 4;
682 case NC_DOUBLE:
683 case NC_INT64:
684 case NC_UINT64: return 8;
685 default: DEBUG_RETURN_ERROR(NC_EBADTYPE)
686 }
687 }
688
689 static NC_dim *
elem_NC_dimarray(const NC_dimarray * ncap,int dimid)690 elem_NC_dimarray(const NC_dimarray *ncap,
691 int dimid)
692 {
693 /* returns the dimension ID defined earlier */
694 assert(ncap != NULL);
695
696 if (dimid < 0 || ncap->ndefined == 0 || dimid >= ncap->ndefined)
697 return NULL;
698
699 assert(ncap->value != NULL);
700
701 return ncap->value[dimid];
702 }
703
704 static int
var_shape64(NC_var * varp,const NC_dimarray * dims,const char * loc)705 var_shape64(NC_var *varp,
706 const NC_dimarray *dims,
707 const char *loc)
708 {
709 int i;
710 long long product = 1;
711
712 /* set the size of 1 element */
713 varp->xsz = xlen_nc_type(varp->xtype);
714
715 if (varp->ndims == 0) goto out;
716
717 /*
718 * use the user supplied dimension indices to determine the shape
719 */
720 for (i=0; i<varp->ndims; i++) {
721 const NC_dim *dimp;
722
723 if (varp->dimids[i] < 0) {
724 if (verbose) printf("Error:\n");
725 if (verbose) printf("\t%s: dimension ID [%d] invalid (%d)\n",loc,i,varp->dimids[i]);
726 DEBUG_RETURN_ERROR(NC_EBADDIM);
727 }
728
729 if (varp->dimids[i] >= ((dims != NULL) ? dims->ndefined : 1)) {
730 if (verbose) printf("Error:\n");
731 if (verbose) printf("\t%s: dimension ID [%d] (%d) larger than defined (%d)\n",loc,i,varp->dimids[i], ((dims != NULL) ? dims->ndefined : 1));
732 DEBUG_RETURN_ERROR(NC_EBADDIM);
733 }
734
735 /* get the pointer to the dim object */
736 dimp = elem_NC_dimarray(dims, varp->dimids[i]);
737 varp->shape[i] = dimp->size;
738
739 /* check for record variable, only the highest dimension can
740 * be unlimited */
741 if (varp->shape[i] == NC_UNLIMITED && i != 0) {
742 if (verbose) printf("Error:\n");
743 if (verbose) printf("\t%s: dimension ID [%d] is NC_UNLIMITED in the wrong index\n",loc,i);
744 DEBUG_RETURN_ERROR(NC_EUNLIMPOS);
745 }
746 }
747
748 /*
749 * compute the dsizes, the right to left product of shape
750 */
751 product = 1;
752 if (varp->ndims == 1) {
753 if (varp->shape[0] == NC_UNLIMITED)
754 varp->dsizes[0] = 1;
755 else {
756 varp->dsizes[0] = varp->shape[0];
757 product = varp->shape[0];
758 }
759 }
760 else { /* varp->ndims > 1 */
761 varp->dsizes[varp->ndims-1] = varp->shape[varp->ndims-1];
762 product = varp->shape[varp->ndims-1];
763 for (i=varp->ndims-2; i>=0; i--) {
764 if (varp->shape[i] != NC_UNLIMITED)
765 product *= varp->shape[i];
766 varp->dsizes[i] = product;
767 }
768 }
769
770 out :
771 /*
772 * For CDF-1 and CDF-2 formats, the total number of array elements
773 * cannot exceed 2^32, unless this variable is the last fixed-size
774 * variable, there is no record variable, and the file starting
775 * offset of this variable is less than 2GiB.
776 * We will check this in ncmpi_enddef() which calls ncmpii_NC_enddef()
777 * which calls ncmpii_NC_check_vlens()
778 if (ncp->format != 5 && product >= X_UINT_MAX)
779 DEBUG_RETURN_ERROR(NC_EVARSIZE);
780 */
781
782 /*
783 * align variable size to 4 byte boundary, required by all netcdf file
784 * formats
785 */
786 varp->len = product * varp->xsz;
787 if (varp->len % 4 > 0)
788 varp->len += 4 - varp->len % 4; /* round up */
789
790 return NC_NOERR;
791 }
792
793 /* calculate the following
794 * ncp->begin_var first variable's offset, file header extent
795 * ncp->begin_rec first record variable's offset
796 * ncp->recsize sum of all single record size of all variables
797 * ncp->vars.value[*]->len individual variable size (record size)
798 */
799 static int
compute_var_shape(NC * ncp)800 compute_var_shape(NC *ncp)
801 {
802 int i, j, err;
803 char xloc[1024];
804 NC_var *first_var = NULL; /* first "non-record" var */
805 NC_var *first_rec = NULL; /* first "record" var */
806
807 if (ncp->vars.ndefined == 0) return NC_NOERR;
808
809 ncp->begin_var = ncp->xsz;
810 ncp->begin_rec = ncp->xsz;
811 ncp->recsize = 0;
812
813 for (i=0; i<ncp->vars.ndefined; i++) {
814 sprintf(xloc,"var %s:",ncp->vars.value[i]->name);
815 /* check if dimids are valid */
816 for (j=0; j<ncp->vars.value[i]->ndims; j++) {
817 if (ncp->vars.value[i]->dimids[j] < 0) {
818 if (verbose) printf("Error:\n");
819 if (verbose) printf("\t%s: dimension ID [%d] invalid (%d)\n",xloc,i,ncp->vars.value[i]->dimids[i]);
820 DEBUG_RETURN_ERROR(NC_EBADDIM) /* dimid is not defined */
821 }
822 else if (ncp->vars.value[i]->dimids[j] >= ncp->dims.ndefined) {
823 if (verbose) printf("Error:\n");
824 if (verbose) printf("\t%s: dimension ID [%d] (%d) larger than defined (%d)\n",xloc,i,ncp->vars.value[i]->dimids[i], ncp->dims.ndefined);
825 DEBUG_RETURN_ERROR(NC_EBADDIM);
826 }
827 }
828 /* ncp->vars.value[i]->len will be recomputed from dimensions in
829 * var_shape64() */
830 err = var_shape64(ncp->vars.value[i], &ncp->dims, xloc);
831 if (err != NC_NOERR) return err;
832
833 if (IS_RECVAR(ncp->vars.value[i])) {
834 if (first_rec == NULL) first_rec = ncp->vars.value[i];
835 ncp->recsize += ncp->vars.value[i]->len;
836 }
837 else { /* fixed-size variable */
838 if (first_var == NULL) first_var = ncp->vars.value[i];
839 ncp->begin_rec = ncp->vars.value[i]->begin
840 + ncp->vars.value[i]->len;
841 }
842 }
843
844 if (first_rec != NULL) {
845 if (ncp->begin_rec > first_rec->begin) {
846 if (verbose) printf("Error:\n");
847 if (verbose) printf("\tbegin of record section (%lld) greater than the begin of first record (%lld)\n",ncp->begin_rec, first_rec->begin);
848 DEBUG_RETURN_ERROR(NC_ENOTNC) /* not a netCDF file or corrupted */
849 }
850
851 ncp->begin_rec = first_rec->begin;
852 /*
853 * for special case of exactly one record variable, pack value
854 */
855 if (ncp->recsize == first_rec->len)
856 ncp->recsize = *first_rec->dsizes * first_rec->xsz;
857 }
858
859 if (first_var != NULL)
860 ncp->begin_var = first_var->begin;
861 else
862 ncp->begin_var = ncp->begin_rec;
863
864 if (ncp->begin_var <= 0) {
865 if (verbose) printf("Error:\n");
866 if (verbose) printf("\tbegin of variable section (%lld) is negative\n",ncp->begin_var);
867 DEBUG_RETURN_ERROR(NC_ENOTNC) /* not a netCDF file or corrupted */
868 }
869 else if (ncp->xsz > ncp->begin_var) {
870 if (verbose) printf("Error:\n");
871 if (verbose) printf("\tfile header size (%lld) is larger than the begin of data section (%lld)\n",ncp->xsz, ncp->begin_var);
872 DEBUG_RETURN_ERROR(NC_ENOTNC) /* not a netCDF file or corrupted */
873 }
874 else if (ncp->begin_rec <= 0) {
875 if (verbose) printf("Error:\n");
876 if (verbose) printf("\tbegin of record section (%lld) is zero or negative\n",ncp->begin_rec);
877 DEBUG_RETURN_ERROR(NC_ENOTNC) /* not a netCDF file or corrupted */
878 }
879 else if (ncp->begin_var > ncp->begin_rec) {
880 if (verbose) printf("Error:\n");
881 if (verbose) printf("\tbegin of data section (%lld) is larger than record section (%lld)\n",ncp->begin_var, ncp->begin_rec);
882 DEBUG_RETURN_ERROR(NC_ENOTNC) /* not a netCDF file or corrupted */
883 }
884
885 return NC_NOERR;
886 }
887
888 /*
889 * repair file contents
890 */
891 static int
val_repair(int fd,off_t offset,size_t len,void * buf)892 val_repair(int fd, off_t offset, size_t len, void *buf)
893 {
894 ssize_t nn;
895
896 if (-1 == lseek(fd, offset, SEEK_SET)) {
897 if (verbose)
898 printf("Error at line %d: lseek %s\n",__LINE__,strerror(errno));
899 return -1;
900 }
901 nn = write(fd, buf, len);
902 if (nn == -1) {
903 if (verbose)
904 printf("Error at line %d: write %s\n",__LINE__,strerror(errno));
905 return -1;
906 }
907 if (nn != len) {
908 if (verbose)
909 printf("Error at line %d: writing %zd bytes but only %zd written\n",
910 __LINE__,len, nn);
911 return -1;
912 }
913
914 return NC_NOERR;
915 }
916
917 /*
918 * Fetch the next header chunk.
919 */
920 static int
val_fetch(int fd,bufferinfo * gbp)921 val_fetch(int fd, bufferinfo *gbp) {
922 ssize_t nn = 0;
923 long long slack; /* any leftover data in the buffer */
924 size_t pos_addr, base_addr;
925
926 assert(gbp->base != NULL);
927
928 pos_addr = (size_t) gbp->pos;
929 base_addr = (size_t) gbp->base;
930
931 slack = gbp->size - (pos_addr - base_addr);
932 /* if gbp->pos and gbp->base are the same, there is no leftover buffer data
933 * to worry about.
934 * In the other extreme, where gbp->size == (gbp->pos - gbp->base), then all
935 * data in the buffer has been consumed */
936 if (slack == gbp->size) slack = 0;
937
938 memset(gbp->base, 0, gbp->size);
939 gbp->pos = gbp->base;
940
941 if (-1 == lseek(fd, gbp->offset-slack, SEEK_SET)) {
942 fprintf(stderr,"Error at line %d: lseek %s\n",__LINE__,strerror(errno));
943 return -1;
944 }
945 nn = read(fd, gbp->base, gbp->size);
946 if (nn == -1) {
947 fprintf(stderr,"Error at line %d: read %s\n",__LINE__,strerror(errno));
948 return -1;
949 }
950 gbp->offset += (gbp->size - slack);
951
952 return NC_NOERR;
953 }
954
955 /*
956 * Ensure that 'nextread' bytes are available.
957 */
958 static int
val_check_buffer(int fd,bufferinfo * gbp,long long nextread)959 val_check_buffer(int fd,
960 bufferinfo *gbp,
961 long long nextread)
962 {
963 size_t pos_addr, base_addr;
964
965 pos_addr = (size_t) gbp->pos;
966 base_addr = (size_t) gbp->base;
967
968 if (pos_addr + nextread <= base_addr + gbp->size)
969 return NC_NOERR;
970
971 return val_fetch(fd, gbp);
972 }
973
974 static int
val_get_NC_tag(int fd,bufferinfo * gbp,NC_tag * tagp,const char * loc)975 val_get_NC_tag(int fd, bufferinfo *gbp, NC_tag *tagp, const char *loc)
976 {
977 int status;
978 size_t err_addr;
979 unsigned int tag;
980
981 err_addr = ERR_ADDR;
982 status = val_check_buffer(fd, gbp, (gbp->version < 5) ? 4 : 8);
983 if (status != NC_NOERR) goto fn_exit;
984
985 tag = get_uint32(gbp);
986 switch(tag) {
987 case 0: *tagp = NC_UNSPECIFIED; break;
988 case 10: *tagp = NC_DIMENSION; break;
989 case 11: *tagp = NC_VARIABLE; break;
990 case 12: *tagp = NC_ATTRIBUTE; break;
991 default:
992 *tagp = NC_INVALID;
993 if (verbose) printf("Error @ [0x%8.8zx]:\n", err_addr);
994 if (verbose) printf("\tInvalid NC component tag (%d)\n",tag);
995 return NC_ENOTNC;
996 }
997 return NC_NOERR;
998
999 fn_exit:
1000 if (verbose) printf("Error @ [0x%8.8zx]:\n", err_addr);
1001 if (verbose) printf("\t%s: Fail to read NC component tag\n",loc);
1002 return status;
1003 }
1004
1005 static int
hdr_get_NON_NEG(int fd,bufferinfo * gbp,long long * sp)1006 hdr_get_NON_NEG(int fd, bufferinfo *gbp, long long *sp)
1007 {
1008 /* netCDF file format:
1009 * ...
1010 * NON_NEG = <non-negative INT> | // CDF-1 and CDF-2
1011 * <non-negative INT64> // CDF-5
1012 */
1013 int sizeof_NON_NEG, status;
1014
1015 sizeof_NON_NEG = (gbp->version < 5) ? 4 : 8;
1016 status = val_check_buffer(fd, gbp, sizeof_NON_NEG);
1017 if (status != NC_NOERR) {
1018 if (verbose) printf("%d-byte size is expected for ", sizeof_NON_NEG);
1019 return status;
1020 }
1021 if (gbp->version < 5)
1022 *sp = (long long) get_uint32(gbp);
1023 else
1024 *sp = (long long) get_uint64(gbp);
1025
1026 return status;
1027 }
1028
1029 static int
hdr_get_name(int fd,bufferinfo * gbp,char ** namep,const char * loc)1030 hdr_get_name(int fd,
1031 bufferinfo *gbp,
1032 char **namep,
1033 const char *loc)
1034 {
1035 /* netCDF file format:
1036 * ...
1037 * name = nelems namestring
1038 * nelems = NON_NEG
1039 * namestring = ID1 [IDN ...] padding
1040 * ID1 = alphanumeric | '_'
1041 * IDN = alphanumeric | special1 | special2
1042 * padding = <0, 1, 2, or 3 bytes to next 4-byte boundary>
1043 * NON_NEG = <non-negative INT> | // CDF-1 and CDF-2
1044 * <non-negative INT64> // CDF-5
1045 */
1046 int err=NC_NOERR;
1047 char *cpos, pad[X_ALIGN-1];
1048 long long nchars=0, padding, bufremain, strcount;
1049 size_t err_addr, pos_addr, base_addr;
1050
1051 *namep = NULL;
1052
1053 /* read nelems, string length */
1054 err_addr = ERR_ADDR;
1055 err = hdr_get_NON_NEG(fd, gbp, &nchars);
1056 if (err != NC_NOERR) {
1057 if (verbose) printf("Error @ [0x%8.8zx]:\n", err_addr);
1058 if (verbose) printf("\t%s: Failed to read name string length\n", loc);
1059 return err;
1060 }
1061
1062 *namep = (char*) malloc((size_t)nchars + 1);
1063 if (*namep == NULL) DEBUG_RETURN_ERROR(NC_ENOMEM)
1064 (*namep)[nchars] = '\0'; /* add terminal character */
1065
1066 padding = _RNDUP(nchars, X_ALIGN) - nchars;
1067 pos_addr = (size_t) gbp->pos;
1068 base_addr = (size_t) gbp->base;
1069 bufremain = gbp->size - (pos_addr - base_addr);
1070 cpos = *namep;
1071
1072 while (nchars > 0) {
1073 if (bufremain > 0) {
1074 strcount = MIN(bufremain, nchars);
1075 (void) memcpy(cpos, gbp->pos, strcount);
1076 nchars -= strcount;
1077 gbp->pos = (void *)((char *)gbp->pos + strcount);
1078 cpos += strcount;
1079 bufremain -= strcount;
1080 } else {
1081 err_addr = ERR_ADDR;
1082 err = val_fetch(fd, gbp);
1083 if (err != NC_NOERR) {
1084 if (verbose) printf("Error @ [0x%8.8zx]:\n", err_addr);
1085 if (verbose) printf("\t%s - fetching name string\n", loc);
1086 free(*namep);
1087 *namep = NULL;
1088 return err;
1089 }
1090 bufremain = gbp->size;
1091 }
1092 }
1093
1094 if (padding > 0) {
1095 err_addr = ERR_ADDR;
1096 err = val_check_buffer(fd, gbp, padding);
1097 if (err != NC_NOERR) {
1098 if (verbose) printf("Error @ [0x%8.8zx]:\n", err_addr);
1099 if (verbose) printf("\t%s - fetching name string padding\n", loc);
1100 return err;
1101 }
1102 memset(pad, 0, X_ALIGN-1);
1103 if (memcmp(gbp->pos, pad, padding) != 0) {
1104 /* This is considered not a fatal error, we continue to validate */
1105 if (verbose) printf("Error @ [0x%8.8zx]:\n", err_addr);
1106 if (verbose) printf("\t%s \"%s\": name padding is non-null byte\n", loc, *namep);
1107 /* free(*namep);
1108 *namep = NULL; */
1109 DEBUG_ASSIGN_ERROR(err, NC_ENULLPAD)
1110 if (repair) {
1111 val_repair(fd, err_addr, (size_t)padding, (void*)nada);
1112 if (verbose)
1113 printf("\t%s \"%s\": name padding error has been **repaired**\n",loc,*namep);
1114 }
1115 }
1116 gbp->pos = (void *)((char *)gbp->pos + padding);
1117 }
1118
1119 return err;
1120 }
1121
1122 static int
val_get_NC_dim(int fd,bufferinfo * gbp,NC_dim ** dimpp,NC_dimarray * ncap)1123 val_get_NC_dim(int fd, bufferinfo *gbp, NC_dim **dimpp, NC_dimarray *ncap) {
1124 int err, status=NC_NOERR;
1125 char *name=NULL;
1126 size_t err_addr;
1127 long long dim_length;
1128 NC_dim *dimp;
1129
1130 *dimpp = NULL;
1131
1132 status = hdr_get_name(fd, gbp, &name, "Dimension");
1133 if (status != NC_NOERR && status != NC_ENULLPAD) {
1134 if (name != NULL) free(name);
1135 return status;
1136 }
1137
1138 /* read dimension length */
1139 err_addr = ERR_ADDR;
1140 err = hdr_get_NON_NEG(fd, gbp, &dim_length);
1141 if (err != NC_NOERR) { /* frees dimp */
1142 if (verbose) printf("Error @ [0x%8.8zx]:\n", err_addr);
1143 if (verbose) printf("\tDimension \"%s\": Failed to read dimension size\n",name);
1144 free(name);
1145 return err;
1146 }
1147
1148 /* check if unlimited_id already set */
1149 if (ncap->unlimited_id != -1 && dim_length == 0) {
1150 if (verbose) printf("Error @ [0x%8.8zx]:\n", err_addr);
1151 if (verbose) printf("\tDimension \"%s\": NC_UNLIMITED dimension already found (\"%s\")\n",name,ncap->value[ncap->unlimited_id]->name);
1152 free(name);
1153 return NC_EUNLIMIT;
1154 }
1155
1156 dimp = (NC_dim*) malloc(sizeof(NC_dim));
1157 if (dimp == NULL) {
1158 free(name);
1159 DEBUG_RETURN_ERROR(NC_ENOMEM)
1160 }
1161 dimp->name = name;
1162 dimp->name_len = strlen(name);
1163 dimp->size = dim_length;
1164
1165 *dimpp = dimp;
1166
1167 return status;
1168 }
1169
1170 static int
val_get_NC_dimarray(int fd,bufferinfo * gbp,NC_dimarray * ncap,long long numrecs)1171 val_get_NC_dimarray(int fd, bufferinfo *gbp, NC_dimarray *ncap, long long numrecs)
1172 {
1173 /* netCDF file format:
1174 * ...
1175 * dim_list = ABSENT | NC_DIMENSION nelems [dim ...]
1176 * ABSENT = ZERO ZERO | // list is not present for CDF-1 and 2
1177 * ZERO ZERO64 // for CDF-5
1178 * ZERO = \x00 \x00 \x00 \x00 // 32-bit zero
1179 * ZERO64 = \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 // 64-bit zero
1180 * NC_DIMENSION = \x00 \x00 \x00 \x0A // tag for list of dimensions
1181 * nelems = NON_NEG // number of elements in following sequence
1182 * NON_NEG = <non-negative INT> | // CDF-1 and CDF-2
1183 * <non-negative INT64> // CDF-5
1184 */
1185 int dim, err, status=NC_NOERR;
1186 NC_tag tag = NC_UNSPECIFIED;
1187 size_t tag_err_addr, nelems_err_addr;
1188 long long tmp;
1189
1190 assert(gbp != NULL && gbp->pos != NULL);
1191 assert(ncap != NULL);
1192 assert(ncap->value == NULL);
1193
1194 /* read NC_tag (NC_DIMENSION or ZERO) from gbp buffer */
1195 tag_err_addr = ERR_ADDR;
1196 err = val_get_NC_tag(fd, gbp, &tag, "tag NC_DIMENSION");
1197 if (err != NC_NOERR) return err;
1198
1199 /* read nelems (number of dimensions) from gbp buffer */
1200 nelems_err_addr = ERR_ADDR;
1201 err = hdr_get_NON_NEG(fd, gbp, &tmp);
1202 if (err != NC_NOERR) {
1203 if (verbose) printf("Error @ [0x%8.8zx]:\n", nelems_err_addr);
1204 if (verbose) printf("\tFailed to read tag NC_DIMENSION\n");
1205 return err;
1206 }
1207 if (tmp > NC_MAX_DIMS) {
1208 /* number of allowable defined dimensions NC_MAX_DIMS */
1209 if (verbose) printf("Error @ [0x%8.8zx]:\n", nelems_err_addr);
1210 if (verbose) printf("\tNumber of dimensions (%lld) defined in file exceeds NC_MAX_DIMS (%d)\n",tmp,NC_MAX_DIMS);
1211 DEBUG_RETURN_ERROR(NC_EMAXDIMS)
1212 }
1213 ncap->ndefined = (int)tmp;
1214 if (trace) printf("\ndim_list (dimension list):\n");
1215
1216 ncap->unlimited_id = -1;
1217
1218 if (ncap->ndefined == 0) {
1219 /* no dimension defined */
1220 /* From the CDF file format specification, the tag is either
1221 * NC_DIMENSION or ABSENT (ZERO), but we follow NetCDF library to skip
1222 * checking the tag when ndefined is zero.
1223 */
1224 if (trace) printf("\ttag = ABSENT (no dimension defined)\n");
1225 return NC_NOERR;
1226 #if 0
1227 if (tag != ABSENT) {
1228 if (verbose) printf("Error @ [0x%8.8zx]:\n", err_addr);
1229 if (verbose) printf("\tInvalid NC component tag, while ABSENT is expected for ");
1230 DEBUG_RETURN_ERROR(NC_ENOTNC)
1231 }
1232 #endif
1233 } else {
1234 if (tag != NC_DIMENSION) {
1235 if (verbose) printf("Error @ [0x%8.8zx]:\n", tag_err_addr);
1236 if (verbose) printf("\tInvalid NC component tag (%d), expecting NC_DIMENSION (%d)\n",tag,NC_DIMENSION);
1237 DEBUG_RETURN_ERROR(NC_ENOTNC)
1238 }
1239 if (trace) {
1240 printf("\ttag = NC_DIMENSION\n");
1241 printf("\tnumber of dimensions defined = %d\n", ncap->ndefined);
1242 }
1243
1244 /* check each dimension */
1245 size_t alloc_size = (size_t)ncap->ndefined + NC_ARRAY_GROWBY;
1246 ncap->value = (NC_dim **) calloc(alloc_size, sizeof(NC_dim *));
1247 if (ncap->value == NULL) DEBUG_RETURN_ERROR(NC_ENOMEM)
1248
1249 for (dim=0; dim<ncap->ndefined; dim++) {
1250 err = val_get_NC_dim(fd, gbp, &ncap->value[dim], ncap);
1251 if (err != NC_NOERR && err != NC_ENULLPAD) {
1252 ncap->ndefined = dim;
1253 free_NC_dimarray(ncap);
1254 return err;
1255 }
1256 if (status == NC_NOERR) status = err;
1257 if (ncap->value[dim]->size == NC_UNLIMITED)
1258 ncap->unlimited_id = dim; /* ID of unlimited dimension */
1259
1260 if (trace) {
1261 if (ncap->unlimited_id == dim)
1262 printf("\tdimension ID %2d: name \"%s\", length = UNLIMITED (%lld currently)\n",
1263 dim,ncap->value[dim]->name, numrecs);
1264 else
1265 printf("\tdimension ID %2d: name \"%s\", length = %lld\n",
1266 dim,ncap->value[dim]->name,ncap->value[dim]->size);
1267 }
1268 }
1269 }
1270
1271 return status;
1272 }
1273
1274 static int
val_get_nc_type(int fd,bufferinfo * gbp,nc_type * xtypep,const char * loc)1275 val_get_nc_type(int fd,
1276 bufferinfo *gbp,
1277 nc_type *xtypep,
1278 const char *loc)
1279 {
1280 /* nc_type is 4-byte integer */
1281 int status;
1282 size_t err_addr;
1283 unsigned int xtype = 0;
1284
1285 err_addr = ERR_ADDR;
1286 status = val_check_buffer(fd, gbp, 4);
1287 if (status != NC_NOERR) goto read_err_exit;
1288
1289 /* get a 4-byte integer */
1290 xtype = get_uint32(gbp);
1291
1292 if (xtype < NC_BYTE) goto err_exit;
1293
1294 if (gbp->version < 5) {
1295 if (xtype > NC_DOUBLE) goto err_exit;
1296 }
1297 else if (xtype > NC_UINT64) goto err_exit;
1298
1299 *xtypep = (nc_type) xtype;
1300
1301 return NC_NOERR;
1302
1303 read_err_exit:
1304 if (verbose) printf("Error @ [0x%8.8zx]:\n", err_addr);
1305 if (verbose) printf("\t%s: Failed to read NC data type\n",loc);
1306 return status;
1307
1308 err_exit:
1309 if (verbose) printf("Error @ [0x%8.8zx]:\n", err_addr);
1310 if (verbose) printf("\t%s: Unknown NC data type (%u)\n",loc, xtype);
1311 DEBUG_RETURN_ERROR(NC_EBADTYPE)
1312 }
1313
1314 /*
1315 * Get the values of an attribute
1316 */
1317 static int
val_get_NC_attrV(int fd,bufferinfo * gbp,NC_attr * attrp,const char * loc)1318 val_get_NC_attrV(int fd,
1319 bufferinfo *gbp,
1320 NC_attr *attrp,
1321 const char *loc)
1322 {
1323 int status=NC_NOERR;
1324 void *value = attrp->xvalue;
1325 char pad[X_ALIGN-1];
1326 size_t err_addr;
1327 long long nvalues, padding, bufremain, attcount;
1328 size_t pos_addr, base_addr;
1329
1330 nvalues = attrp->nelems * xlen_nc_type(attrp->xtype);
1331 padding = attrp->xsz - nvalues;
1332 pos_addr = (size_t) gbp->pos;
1333 base_addr = (size_t) gbp->base;
1334 bufremain = gbp->size - (pos_addr - base_addr);
1335
1336 while (nvalues > 0) {
1337 if (bufremain > 0) {
1338 attcount = MIN(bufremain, nvalues);
1339 (void) memcpy(value, gbp->pos, attcount);
1340 nvalues -= attcount;
1341 gbp->pos = (void *)((char *)gbp->pos + attcount);
1342 value = (void *)((char *)value + attcount);
1343 bufremain -= attcount;
1344 } else {
1345 err_addr = ERR_ADDR;
1346 status = val_fetch(fd, gbp);
1347 if (status != NC_NOERR) {
1348 if (verbose) printf("Error @ [0x%8.8zx]:\n", err_addr);
1349 if (verbose) printf("\t%s: Failed to fetch next chunk into a buffer\n", loc);
1350 return status;
1351 }
1352 bufremain = gbp->size;
1353 }
1354 }
1355
1356 if (padding > 0) {
1357 memset(pad, 0, X_ALIGN-1);
1358 if (memcmp(gbp->pos, pad, padding) != 0) {
1359 /* This is considered not a fatal error, we continue to validate */
1360 if (verbose) printf("Error @ [0x%8.8zx]:\n", (size_t)ERR_ADDR);
1361 if (verbose) printf("\t%s: value padding is non-null byte\n", loc);
1362 DEBUG_ASSIGN_ERROR(status, NC_ENULLPAD)
1363 if (repair) {
1364 val_repair(fd, ERR_ADDR, (size_t)padding, (void*)nada);
1365 if (verbose)
1366 printf("\t%s: value padding has been **repaired**\n",loc);
1367 }
1368 }
1369 gbp->pos = (void *)((char *)gbp->pos + padding);
1370 }
1371
1372 return status;
1373 }
1374
1375 static char*
str_NC_type(nc_type xtype)1376 str_NC_type(nc_type xtype)
1377 {
1378 switch(xtype) {
1379 case NC_BYTE: return "NC_BYTE";
1380 case NC_CHAR: return "NC_CHAR";
1381 case NC_UBYTE: return "NC_UBYTE";
1382 case NC_SHORT: return "NC_SHORT";
1383 case NC_USHORT: return "NC_USHORT";
1384 case NC_INT: return "NC_INT";
1385 case NC_UINT: return "NC_UINT";
1386 case NC_FLOAT: return "NC_FLOAT";
1387 case NC_DOUBLE: return "NC_DOUBLE";
1388 case NC_INT64: return "NC_INT64";
1389 case NC_UINT64: return "NC_UINT64";
1390 default: return "";
1391 }
1392 }
1393
1394 static long long
x_len_NC_attrV(nc_type xtype,long long nelems)1395 x_len_NC_attrV(nc_type xtype,
1396 long long nelems)
1397 {
1398 switch(xtype) {
1399 case NC_BYTE:
1400 case NC_CHAR:
1401 case NC_UBYTE: return _RNDUP(nelems, 4);
1402 case NC_SHORT:
1403 case NC_USHORT: return ((nelems + (nelems)%2) * 2);
1404 case NC_INT: return (nelems * 4);
1405 case NC_UINT: return (nelems * 4);
1406 case NC_FLOAT: return (nelems * 4);
1407 case NC_DOUBLE: return (nelems * 8);
1408 case NC_INT64: return (nelems * 8);
1409 case NC_UINT64: return (nelems * 8);
1410 default: if (verbose) fprintf(stderr, "Error: bad xtype(%d) in %s\n",xtype,__func__);
1411 }
1412 return 0;
1413 }
1414
1415 static int
new_NC_attr(char * name,nc_type xtype,long long nelems,NC_attr ** attrp)1416 new_NC_attr(char *name,
1417 nc_type xtype,
1418 long long nelems,
1419 NC_attr **attrp)
1420 {
1421 *attrp = (NC_attr*) malloc(sizeof(NC_attr));
1422 if (*attrp == NULL ) DEBUG_RETURN_ERROR(NC_ENOMEM)
1423
1424 (*attrp)->xtype = xtype;
1425 (*attrp)->xsz = 0;
1426 (*attrp)->nelems = nelems;
1427 (*attrp)->xvalue = NULL;
1428 (*attrp)->name = name;
1429 (*attrp)->name_len = (name?strlen(name):0);
1430
1431 if (nelems > 0) {
1432 long long xsz = x_len_NC_attrV(xtype, nelems);
1433 (*attrp)->xsz = xsz;
1434 (*attrp)->xvalue = malloc((size_t)xsz);
1435 if ((*attrp)->xvalue == NULL) {
1436 free(*attrp);
1437 *attrp = NULL;
1438 DEBUG_RETURN_ERROR(NC_ENOMEM)
1439 }
1440 }
1441 return NC_NOERR;
1442 }
1443
1444 static int
val_get_NC_attr(int fd,bufferinfo * gbp,NC_attr ** attrpp,const char * loc)1445 val_get_NC_attr(int fd,
1446 bufferinfo *gbp,
1447 NC_attr **attrpp,
1448 const char *loc)
1449 {
1450 char *name=NULL, xloc[1024];
1451 int err, status=NC_NOERR;
1452 size_t err_addr;
1453 nc_type xtype;
1454 long long nelems;
1455 NC_attr *attrp;
1456
1457 status = hdr_get_name(fd, gbp, &name, loc);
1458 if (status != NC_NOERR && status != NC_ENULLPAD) {
1459 if (name != NULL) free(name);
1460 return status;
1461 }
1462
1463 sprintf(xloc,"%s \"%s\"",loc,name);
1464 err = val_get_nc_type(fd, gbp, &xtype, xloc);
1465 if (err != NC_NOERR) {
1466 if (name != NULL) free(name);
1467 return err;
1468 }
1469
1470 err_addr = ERR_ADDR;
1471 err = hdr_get_NON_NEG(fd, gbp, &nelems);
1472 if (err != NC_NOERR) {
1473 if (verbose) printf("Error @ [0x%8.8zx]:\n", err_addr);
1474 if (verbose) printf("\t%s: Failed to read attribute length\n",xloc);
1475 if (name != NULL) free(name);
1476 return err;
1477 }
1478
1479 err = new_NC_attr(name, xtype, nelems, &attrp);
1480 if(err != NC_NOERR) {
1481 if (name != NULL) free(name);
1482 return err;
1483 }
1484
1485 err = val_get_NC_attrV(fd, gbp, attrp, xloc);
1486 if (err != NC_NOERR && err != NC_ENULLPAD) {
1487 free(attrp->name);
1488 free(attrp->xvalue);
1489 free(attrp);
1490 return err;
1491 }
1492 if (status == NC_NOERR) status = err;
1493
1494 *attrpp = attrp;
1495
1496 return status;
1497 }
1498
1499 static int
val_get_NC_attrarray(int fd,bufferinfo * gbp,NC_attrarray * ncap,const char * loc)1500 val_get_NC_attrarray(int fd,
1501 bufferinfo *gbp,
1502 NC_attrarray *ncap,
1503 const char *loc)
1504 {
1505 /* netCDF file format:
1506 * ...
1507 * att_list = ABSENT | NC_ATTRIBUTE nelems [attr ...]
1508 * ABSENT = ZERO ZERO | // list is not present for CDF-1 and 2
1509 * ZERO ZERO64 // for CDF-5
1510 * ZERO = \x00 \x00 \x00 \x00 // 32-bit zero
1511 * ZERO64 = \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 // 64-bit zero
1512 * NC_ATTRIBUTE = \x00 \x00 \x00 \x0C // tag for list of attributes
1513 * nelems = NON_NEG // number of elements in following sequence
1514 * NON_NEG = <non-negative INT> | // CDF-1 and CDF-2
1515 * <non-negative INT64> // CDF-5
1516 */
1517 char xloc[1024];
1518 int i, err, status=NC_NOERR;
1519 NC_tag tag = NC_UNSPECIFIED;
1520 long long tmp;
1521 size_t tag_err_addr, nelems_err_addr;
1522
1523 assert(gbp != NULL && gbp->pos != NULL);
1524 assert(ncap != NULL);
1525 assert(ncap->value == NULL);
1526
1527 /* read NC_tag (NC_ATTRIBUTE or ZERO) from gbp buffer */
1528 tag_err_addr = ERR_ADDR;
1529 err = val_get_NC_tag(fd, gbp, &tag, "tag NC_ATTRIBUTE");
1530 if (err != NC_NOERR) return err;
1531
1532 /* read nelems (number of attributes) from gbp buffer */
1533 nelems_err_addr = ERR_ADDR;
1534 err = hdr_get_NON_NEG(fd, gbp, &tmp);
1535 if (err != NC_NOERR) {
1536 if (verbose) printf("Error @ [0x%8.8zx]:\n", nelems_err_addr);
1537 if (verbose) printf("\tFailed to read tag NC_ATTRIBUTE\n");
1538 return err;
1539 }
1540 if (tmp > NC_MAX_ATTRS) {
1541 /* number of allowable defined attributes NC_MAX_ATTRS */
1542 if (verbose) printf("Error @ [0x%8.8zx]:\n", nelems_err_addr);
1543 if (verbose) printf("\t%s attributes: number of attributes (%lld) exceeds NC_MAX_ATTRS (%d)\n",loc,tmp,NC_MAX_ATTRS);
1544 return NC_EMAXATTS;
1545 }
1546 ncap->ndefined = (int)tmp;
1547
1548 if (trace) {
1549 if (!strcmp(loc, "Global"))
1550 printf("\ngatt_list (global attribute list):\n");
1551 else
1552 printf("\t\tvatt_list (variable attribute list):\n");
1553 }
1554
1555 if (ncap->ndefined == 0) {
1556 /* no attribute defined */
1557 /* From the CDF file format specification, the tag is either
1558 * NC_ATTRIBUTE or ABSENT (ZERO), but we follow NetCDF library to skip
1559 * checking the tag when ndefined is zero.
1560 */
1561 if (trace) {
1562 if (strcmp(loc, "Global")) printf("\t\t");
1563 printf("\ttag = ABSENT (no attribute defined)\n");
1564 }
1565 return NC_NOERR;
1566 #if 0
1567 if (tag != ABSENT) {
1568 if (verbose) printf("Error @ [0x%8.8zx]:\n", err_addr);
1569 if (verbose) printf("\tInvalid NC component tag, while ABSENT is expected for ");
1570 DEBUG_RETURN_ERROR(NC_ENOTNC)
1571 }
1572 #endif
1573 } else {
1574 sprintf(xloc, "%s attribute", loc);
1575 if (tag != NC_ATTRIBUTE) {
1576 if (verbose) printf("Error @ [0x%8.8zx]:\n", tag_err_addr);
1577 if (verbose) printf("\t%s: Invalid NC component tag (%d), expecting NC_ATTRIBUTE (%d)\n",xloc,tag,NC_ATTRIBUTE);
1578 DEBUG_RETURN_ERROR(NC_ENOTNC)
1579 }
1580 if (trace) {
1581 if (strcmp(loc, "Global")) printf("\t\t");
1582 printf("\ttag = NC_ATTRIBUTE\n");
1583 if (strcmp(loc, "Global")) printf("\t\t");
1584 printf("\tnumber of attributes = %d\n", ncap->ndefined);
1585 }
1586
1587 size_t alloc_size = (size_t)ncap->ndefined + NC_ARRAY_GROWBY;
1588 ncap->value = (NC_attr **) calloc(alloc_size, sizeof(NC_attr *));
1589 if (ncap->value == NULL) DEBUG_RETURN_ERROR(NC_ENOMEM)
1590
1591 for (i=0; i<ncap->ndefined; i++) {
1592 err = val_get_NC_attr(fd, gbp, &ncap->value[i], xloc);
1593 if (err != NC_NOERR && err != NC_ENULLPAD) {
1594 ncap->ndefined = i;
1595 free_NC_attrarray(ncap);
1596 return err;
1597 }
1598 if (status == NC_NOERR) status = err;
1599 if (trace) {
1600 if (strcmp(loc, "Global")) printf("\t\t");
1601 printf("\tattribute name \"%s\", type = %s, length = %lld\n",
1602 ncap->value[i]->name, str_NC_type(ncap->value[i]->xtype),
1603 ncap->value[i]->nelems);
1604 }
1605 }
1606 }
1607
1608 return status;
1609 }
1610
1611 /*----< ncmpio_new_NC_var() >------------------------------------------------*/
1612 static NC_var *
val_new_NC_var(char * name,int ndims)1613 val_new_NC_var(char *name, int ndims)
1614 {
1615 NC_var *varp;
1616
1617 varp = (NC_var *) calloc(1, sizeof(NC_var));
1618 if (varp == NULL) return NULL;
1619
1620 if (ndims > 0) {
1621 varp->shape = (long long*)calloc(ndims, sizeof(long long));
1622 varp->dsizes = (long long*)calloc(ndims, sizeof(long long));
1623 varp->dimids = (int *) calloc(ndims, sizeof(int));
1624 }
1625
1626 varp->name = name;
1627 varp->name_len = strlen(name);
1628 varp->ndims = ndims;
1629 varp->xsz = 0;
1630 varp->len = 0;
1631 varp->begin = 0;
1632
1633 return varp;
1634 }
1635
1636 /*----< len_nctype() >-------------------------------------------------------*/
1637 static int
len_nctype(nc_type type)1638 len_nctype(nc_type type) {
1639 switch(type) {
1640 case NC_BYTE:
1641 case NC_CHAR:
1642 case NC_UBYTE: return 1;
1643 case NC_SHORT:
1644 case NC_USHORT: return 2;
1645 case NC_INT:
1646 case NC_UINT:
1647 case NC_FLOAT: return 4;
1648 case NC_DOUBLE:
1649 case NC_INT64:
1650 case NC_UINT64: return 8;
1651 default: assert("len_nctype bad type" == 0);
1652 }
1653 return 0;
1654 }
1655
1656 /*----< val_get_NC_var() >---------------------------------------------------*/
1657 static int
val_get_NC_var(int fd,bufferinfo * gbp,NC_var ** varpp,int f_ndims,const char * loc)1658 val_get_NC_var(int fd,
1659 bufferinfo *gbp,
1660 NC_var **varpp,
1661 int f_ndims, /* no. dimensions defined in file */
1662 const char *loc)
1663 {
1664 /* netCDF file format:
1665 * netcdf_file = header data
1666 * header = magic numrecs dim_list gatt_list var_list
1667 * ...
1668 * var = name nelems [dimid ...] vatt_list nc_type vsize begin
1669 * nelems = NON_NEG
1670 * dimid = NON_NEG
1671 * vatt_list = att_list
1672 * nc_type = NC_BYTE | NC_CHAR | NC_SHORT | ...
1673 * vsize = NON_NEG
1674 * begin = OFFSET // Variable start location.
1675 * OFFSET = <non-negative INT> | // CDF-1
1676 * <non-negative INT64> // CDF-2 and CDF-5
1677 * NON_NEG = <non-negative INT> | // CDF-1 and CDF-2
1678 * <non-negative INT64> // CDF-5
1679 */
1680 char *name=NULL, xloc[1024];
1681 int dim, dimid, err, status=NC_NOERR;
1682 size_t err_addr;
1683 long long ndims;
1684 NC_var *varp;
1685
1686 /* read variable name */
1687 err = hdr_get_name(fd, gbp, &name, loc);
1688 if (err != NC_NOERR && err != NC_ENULLPAD) {
1689 if (name != NULL) free(name);
1690 return err;
1691 }
1692 status = err;
1693
1694 if (trace) printf("\t\tname = \"%s\"\n", name);
1695
1696 /* read number of dimensions */
1697 sprintf(xloc,"%s \"%s\"",loc,name);
1698 err_addr = ERR_ADDR;
1699 err = hdr_get_NON_NEG(fd, gbp, &ndims);
1700 if (err != NC_NOERR) {
1701 if (verbose) printf("Error @ [0x%8.8zx]:\n", err_addr);
1702 if (verbose) printf("\t%s: Failed to read number of dimensions\n",xloc);
1703 if (name != NULL) free(name);
1704 return err;
1705 }
1706 if (status == NC_NOERR) status = err;
1707
1708 /* cannot be more than NC_MAX_VAR_DIMS */
1709 if (ndims > NC_MAX_VAR_DIMS) {
1710 if (verbose) printf("Error:\n");
1711 if (verbose) printf("\t%s: number of dimensions (%lld) larger than NC_MAX_VAR_DIMS (%d)\n",xloc,ndims,NC_MAX_VAR_DIMS);
1712 if (name != NULL) free(name);
1713 DEBUG_RETURN_ERROR(NC_EMAXDIMS)
1714 }
1715
1716 if (trace) printf("\t\tnumber of dimensions = %lld\n", ndims);
1717
1718 /* allocate variable object */
1719 varp = val_new_NC_var(name, ndims);
1720 if (varp == NULL) {
1721 if (name != NULL) free(name);
1722 DEBUG_RETURN_ERROR(NC_ENOMEM)
1723 }
1724
1725 if (trace && ndims > 0) printf("\t\tdimension IDs:");
1726
1727 /* read dimension IDs and check dimensions */
1728 for (dim=0; dim<ndims; dim++) {
1729 err = val_check_buffer(fd, gbp, (gbp->version < 5 ? 4 : 8));
1730 if (err != NC_NOERR) {
1731 if (trace) printf("\n");
1732 if (verbose) printf("Error @ [0x%8.8zx]:\n", (size_t)ERR_ADDR);
1733 if (verbose) printf("\t%s: Fail to read dimid[%d]\n",xloc,dim);
1734 free_NC_var(varp);
1735 return err;
1736 }
1737 err_addr = ERR_ADDR;
1738 if (gbp->version < 5)
1739 dimid = (int) get_uint32(gbp);
1740 else
1741 dimid = (int) get_uint64(gbp);
1742
1743 if (trace) printf(" %d", dimid);
1744
1745 /* dimid should be < f_ndims (num of dimensions defined in file) */
1746 if (dimid >= f_ndims) {
1747 if (trace) printf("\n");
1748 if (verbose) printf("Error @ [0x%8.8zx]:\n", err_addr);
1749 if (verbose) printf("\t%s \"%s\": dimid[%d]=%d is larger than the number of dimensions defined in file (%d)\n",loc,name,dim,dimid,f_ndims);
1750 free_NC_var(varp);
1751 DEBUG_RETURN_ERROR(NC_EBADDIM)
1752 }
1753 varp->dimids[dim] = dimid;
1754 }
1755 if (trace && ndims > 0) printf("\n");
1756
1757 /* var = name nelems [dimid ...] vatt_list nc_type vsize begin
1758 * ^^^^^^^^^ */
1759 sprintf(xloc,"%s \"%s\"",loc,name);
1760 err = val_get_NC_attrarray(fd, gbp, &varp->attrs, xloc);
1761 if (err != NC_NOERR && err != NC_ENULLPAD) {
1762 free_NC_var(varp);
1763 return err;
1764 }
1765 if (status == NC_NOERR) status = err;
1766
1767 /* var = name nelems [dimid ...] vatt_list nc_type vsize begin
1768 * ^^^^^^^ */
1769 err = val_get_nc_type(fd, gbp, &varp->xtype, xloc);
1770 if (err != NC_NOERR) {
1771 free_NC_var(varp);
1772 return err;
1773 }
1774 if (trace) printf("\t\tdata type: %s\n", str_NC_type(varp->xtype));
1775
1776 varp->xsz = len_nctype(varp->xtype);
1777
1778 /* var = name nelems [dimid ...] vatt_list nc_type vsize begin
1779 * ^^^^^
1780 * instead of use vsize from file, we recalculate it in
1781 * compute_var_shape() */
1782 err_addr = ERR_ADDR;
1783 err = hdr_get_NON_NEG(fd, gbp, &varp->len);
1784 if (err != NC_NOERR) {
1785 if (verbose) printf("Error @ [0x%8.8zx]:\n", err_addr);
1786 if (verbose) printf("\t%s: Failed to read vsize\n",xloc);
1787 free_NC_var(varp);
1788 return err;
1789 }
1790 if (trace) printf("\t\tvariable size (vsize): %lld\n", varp->len);
1791
1792 err = val_check_buffer(fd, gbp, (gbp->version == 1 ? 4 : 8));
1793 if (err != NC_NOERR) {
1794 if (verbose) printf("Error @ [0x%8.8zx]:\n", (size_t)ERR_ADDR);
1795 if (verbose) printf("\t%s: Fail to read begin\n",xloc);
1796 free_NC_var(varp);
1797 return err;
1798 }
1799 /* var = name nelems [dimid ...] vatt_list nc_type vsize begin
1800 * ^^^^^ */
1801 if (gbp->version == 1)
1802 varp->begin = (long long) get_uint32(gbp);
1803 else
1804 varp->begin = (long long) get_uint64(gbp);
1805
1806 if (trace) printf("\t\tstarting file offset (begin): %lld\n", varp->begin);
1807
1808 *varpp = varp;
1809 return status;
1810 }
1811
1812 static int
val_get_NC_vararray(int fd,bufferinfo * gbp,NC_vararray * ncap,int f_ndims)1813 val_get_NC_vararray(int fd,
1814 bufferinfo *gbp,
1815 NC_vararray *ncap,
1816 int f_ndims) /* no. dimensions defined in file */
1817
1818 {
1819 /* netCDF file format:
1820 * netcdf_file = header data
1821 * header = magic numrecs dim_list gatt_list var_list
1822 * ...
1823 * var_list = ABSENT | NC_VARIABLE nelems [var ...]
1824 * ABSENT = ZERO ZERO | // list is not present for CDF-1 and 2
1825 * ZERO ZERO64 // for CDF-5
1826 * ZERO = \x00 \x00 \x00 \x00 // 32-bit zero
1827 * ZERO64 = \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x00 // 64-bit zero
1828 * NC_VARIABLE = \x00 \x00 \x00 \x0B // tag for list of variables
1829 * nelems = NON_NEG // number of elements in following sequence
1830 * NON_NEG = <non-negative INT> | // CDF-1 and CDF-2
1831 * <non-negative INT64> // CDF-5
1832 */
1833 int var, err, status=NC_NOERR;
1834 NC_tag tag = NC_UNSPECIFIED;
1835 long long tmp;
1836 size_t tag_err_addr, nelems_err_addr;
1837
1838 assert(gbp != NULL && gbp->pos != NULL);
1839 assert(ncap != NULL);
1840 assert(ncap->value == NULL);
1841
1842 /* read NC_tag (NC_VARIABLE or ZERO) from gbp buffer */
1843 tag_err_addr = ERR_ADDR;
1844 err = val_get_NC_tag(fd, gbp, &tag, "tag NC_VARIABLE");
1845 if (err != NC_NOERR) return err;
1846
1847 /* read nelems (number of variables) from gbp buffer */
1848 nelems_err_addr = ERR_ADDR;
1849 err = hdr_get_NON_NEG(fd, gbp, &tmp);
1850 if (err != NC_NOERR) {
1851 if (verbose) printf("Error @ [0x%8.8zx]:\n", nelems_err_addr);
1852 if (verbose) printf("\tFailed to read tag NC_VARIABLE\n");
1853 return err;
1854 }
1855 if (tmp > NC_MAX_VARS) {
1856 /* number of allowable defined variables NC_MAX_VARS */
1857 if (verbose) printf("Error @ [0x%8.8zx]:\n", nelems_err_addr);
1858 if (verbose) printf("\tNumber of variables (%lld) exceeds NC_MAX_VARS (%d)\n",tmp,NC_MAX_VARS);
1859 DEBUG_RETURN_ERROR(NC_EMAXVARS);
1860 }
1861 ncap->ndefined = (int)tmp;
1862
1863 if (trace) printf("\nvar_list (variable list):\n");
1864
1865 if (ncap->ndefined == 0) {
1866 /* From the CDF file format specification, the tag is either
1867 * NC_VARIABLE or ABSENT (ZERO), but we follow NetCDF library to skip
1868 * checking the tag when ndefined is zero.
1869 */
1870 if (trace) printf("\ntag = ABSENT (no variable defined)\n");
1871 return NC_NOERR;
1872 #if 0
1873 if (tag != ABSENT) {
1874 if (verbose) printf("Error @ [0x%8.8zx]:\n", err_addr);
1875 if (verbose) printf("\tInvalid NC component tag, while ABSENT is expected for ");
1876 DEBUG_RETURN_ERROR(NC_ENOTNC)
1877 }
1878 #endif
1879 } else {
1880 if (tag != NC_VARIABLE) {
1881 if (verbose) printf("Error @ [0x%8.8zx]:\n", tag_err_addr);
1882 if (verbose) printf("\tInvalid NC component tag (%d), expecting NC_VARIABLE (%d)\n",tag,NC_VARIABLE);
1883 DEBUG_RETURN_ERROR(NC_ENOTNC)
1884 }
1885 if (trace) printf("\ttag = NC_VARIABLE\n");
1886 if (trace) printf("\tnumber of variables = %d\n", ncap->ndefined);
1887
1888 size_t alloc_size = (size_t)ncap->ndefined + NC_ARRAY_GROWBY;
1889 ncap->value = (NC_var **) calloc(alloc_size, sizeof(NC_var *));
1890 if (ncap->value == NULL) DEBUG_RETURN_ERROR(NC_ENOMEM)
1891
1892 for (var=0; var<ncap->ndefined; var++) {
1893 if (trace) printf("\n\tvariable ID = %d\n", var);
1894 err = val_get_NC_var(fd, gbp, &ncap->value[var], f_ndims, "Variable");
1895 if (err != NC_NOERR && err != NC_ENULLPAD) {
1896 ncap->ndefined = var;
1897 free_NC_vararray(ncap);
1898 return err;
1899 }
1900 if (status == NC_NOERR) status = err;
1901 }
1902 }
1903
1904 return status;
1905 }
1906
1907 /*----< NC_check_vlen() >----------------------------------------------------*/
1908 /* Check whether variable size is less than or equal to vlen_max,
1909 * without overflowing in arithmetic calculations. If OK, return 1,
1910 * else, return 0. For CDF1 format or for CDF2 format on non-LFS
1911 * platforms, vlen_max should be 2^31 - 4, but for CDF2 format on
1912 * systems with LFS it should be 2^32 - 4.
1913 */
1914 static int
NC_check_vlen(NC_var * varp,long long vlen_max)1915 NC_check_vlen(NC_var *varp,
1916 long long vlen_max)
1917 {
1918 int i;
1919 long long prod=varp->xsz; /* product of xsz and dimensions so far */
1920
1921 for (i = IS_RECVAR(varp) ? 1 : 0; i < varp->ndims; i++) {
1922 if (varp->shape[i] > vlen_max / prod) {
1923 return 0; /* size in bytes won't fit in a 32-bit int */
1924 }
1925 prod *= varp->shape[i];
1926 }
1927 return 1;
1928 }
1929
1930 /*
1931 * Given a valid ncp, check all variables for their sizes against the maximal
1932 * allowable sizes. Different CDF formation versions have different maximal
1933 * sizes. This function returns NC_EVARSIZE if any variable has a bad len
1934 * (product of non-rec dim sizes too large), else return NC_NOERR.
1935 */
1936 static int
val_NC_check_vlens(NC * ncp)1937 val_NC_check_vlens(NC *ncp)
1938 {
1939 NC_var **vpp;
1940 /* maximum permitted variable size (or size of one record's worth
1941 of a record variable) in bytes. This is different for format 1
1942 and format 2. */
1943 long long ii, vlen_max, rec_vars_count;
1944 long long large_fix_vars_count, large_rec_vars_count;
1945 int last = 0;
1946
1947 if (ncp->vars.ndefined == 0)
1948 return NC_NOERR;
1949
1950 if (ncp->format >= 5) /* CDF-5 */
1951 vlen_max = X_INT64_MAX - 3; /* "- 3" handles rounded-up size */
1952 else if (ncp->format == 2) /* CDF2 format */
1953 vlen_max = X_UINT_MAX - 3; /* "- 3" handles rounded-up size */
1954 else
1955 vlen_max = X_INT_MAX - 3; /* CDF1 format */
1956
1957 /* Loop through vars, first pass is for non-record variables */
1958 large_fix_vars_count = 0;
1959 rec_vars_count = 0;
1960 vpp = ncp->vars.value;
1961 for (ii = 0; ii < ncp->vars.ndefined; ii++, vpp++) {
1962 if (!IS_RECVAR(*vpp)) {
1963 last = 0;
1964 if (NC_check_vlen(*vpp, vlen_max) == 0) {
1965 /* check this variable's shape product against vlen_max */
1966 if (ncp->format >= 5) { /* CDF-5 */
1967 if (verbose) printf("Error:\n");
1968 if (verbose) printf("\tvar %s: variable size greater than max (%lld) allowable by CDF-%d\n",(*vpp)->name,vlen_max,ncp->format);
1969 DEBUG_RETURN_ERROR(NC_EVARSIZE)
1970 }
1971 large_fix_vars_count++;
1972 last = 1;
1973 }
1974 } else {
1975 rec_vars_count++;
1976 }
1977 }
1978 /* OK if last non-record variable size too large, since not used to
1979 compute an offset */
1980 if (large_fix_vars_count > 1) { /* only one "too-large" variable allowed */
1981 if (verbose) printf("Error:\n");
1982 if (verbose) printf("\tCDF-%d format allows only one large fixed-size variable\n",ncp->format);
1983 DEBUG_RETURN_ERROR(NC_EVARSIZE)
1984 }
1985
1986 /* The only "too-large" variable must be the last one defined */
1987 if (large_fix_vars_count == 1 && last == 0) {
1988 if (verbose) printf("Error:\n");
1989 if (verbose) printf("\tCDF-%d format allows only one large fixed-size variable\n",ncp->format);
1990 DEBUG_RETURN_ERROR(NC_EVARSIZE)
1991 }
1992
1993 if (rec_vars_count == 0) return NC_NOERR;
1994
1995 /* if there is a "too-large" fixed-size variable, no record variable is
1996 * allowed */
1997 if (large_fix_vars_count == 1) {
1998 if (verbose) printf("Error:\n");
1999 if (verbose) printf("\tCDF-%d format allows only one large fixed-size variable when there is no record variable defined\n",ncp->format);
2000 DEBUG_RETURN_ERROR(NC_EVARSIZE)
2001 }
2002
2003 /* Loop through vars, second pass is for record variables. */
2004 large_rec_vars_count = 0;
2005 vpp = ncp->vars.value;
2006 for (ii = 0; ii < ncp->vars.ndefined; ii++, vpp++) {
2007 if (IS_RECVAR(*vpp)) {
2008 last = 0;
2009 if (NC_check_vlen(*vpp, vlen_max) == 0) {
2010 /* check this variable's shape product against vlen_max */
2011 if (ncp->format >= 5) { /* CDF-5 */
2012 if (verbose) printf("Error:\n");
2013 if (verbose) printf("\tvar %s: variable size greater than max (%lld) allowable by CDF-%d\n",(*vpp)->name,vlen_max,ncp->format);
2014 DEBUG_RETURN_ERROR(NC_EVARSIZE)
2015 }
2016 large_rec_vars_count++;
2017 last = 1;
2018 }
2019 }
2020 }
2021
2022 /* For CDF-2, no record variable can require more than 2^32 - 4 bytes of
2023 * storage for each record's worth of data, unless it is the last record
2024 * variable. See
2025 * http://www.unidata.ucar.edu/software/netcdf/docs/file_structure_and_performance.html#offset_format_limitations
2026 */
2027 if (large_rec_vars_count > 1) { /* only one "too-large" variable allowed */
2028 if (verbose) printf("Error:\n");
2029 if (verbose) printf("\tCDF-%d format allows only one large record variable\n",ncp->format);
2030 DEBUG_RETURN_ERROR(NC_EVARSIZE)
2031 }
2032
2033 /* and it has to be the last one */
2034 if (large_rec_vars_count == 1 && last == 0) {
2035 if (verbose) printf("Error:\n");
2036 if (verbose) printf("\tCDF-%d format allows only one large record variable and it must be the last one defined\n",ncp->format);
2037 DEBUG_RETURN_ERROR(NC_EVARSIZE)
2038 }
2039
2040 return NC_NOERR;
2041 }
2042
2043 /*
2044 * Given a valid ncp, check all variables for their begins whether in an
2045 * increasing order.
2046 */
2047 static int
val_NC_check_voff(NC * ncp)2048 val_NC_check_voff(NC *ncp)
2049 {
2050 int nerrs=0, status=NC_NOERR;
2051 NC_var *varp;
2052 long long i, prev, prev_off;
2053
2054 if (ncp->vars.ndefined == 0) return NC_NOERR;
2055
2056 /* Loop through vars, first pass is for non-record variables */
2057 prev_off = ncp->begin_var;
2058 prev = 0;
2059 for (i=0; i<ncp->vars.ndefined; i++) {
2060 varp = ncp->vars.value[i];
2061 if (IS_RECVAR(varp)) continue;
2062
2063 if (varp->begin < prev_off) {
2064 if (verbose) {
2065 printf("Error - variable begin offset orders:\n");
2066 if (i == 0)
2067 printf("\tvar \"%s\" begin offset (%lld) is less than header extent (%lld)\n", varp->name, varp->begin, prev_off);
2068 else
2069 printf("\tvar \"%s\" begin offset (%lld) is less than previous variable \"%s\" end offset (%lld)\n", varp->name, varp->begin, ncp->vars.value[prev]->name, prev_off);
2070 }
2071 nerrs++;
2072 DEBUG_ASSIGN_ERROR(status, NC_ENOTNC)
2073 }
2074 prev_off = varp->begin + varp->len;
2075 prev = i;
2076 }
2077
2078 if (ncp->begin_rec < prev_off) {
2079 if (verbose) printf("Error:\n");
2080 if (verbose) printf("\tRecord variable section begin offset (%lld) is less than fixed-size variable section end offset (%lld)\n", varp->begin, prev_off);
2081 nerrs++;
2082 DEBUG_ASSIGN_ERROR(status, NC_ENOTNC)
2083 }
2084
2085 /* Loop through vars, second pass is for record variables */
2086 prev_off = ncp->begin_rec;
2087 prev = 0;
2088 for (i=0; i<ncp->vars.ndefined; i++) {
2089 varp = ncp->vars.value[i];
2090 if (!IS_RECVAR(varp)) continue;
2091
2092 if (varp->begin < prev_off) {
2093 if (verbose) {
2094 printf("Error:\n");
2095 if (i == 0)
2096 printf("Variable \"%s\" begin offset (%lld) is less than record variable section begin offset (%lld)\n", varp->name, varp->begin, prev_off);
2097 else
2098 printf("Variable \"%s\" begin offset (%lld) is less than previous variable \"%s\" end offset (%lld)\n", varp->name, varp->begin, ncp->vars.value[prev]->name, prev_off);
2099 }
2100 nerrs++;
2101 DEBUG_ASSIGN_ERROR(status, NC_ENOTNC)
2102 }
2103 prev_off = varp->begin + varp->len;
2104 prev = i;
2105 }
2106
2107 return status;
2108 }
2109
2110 static int
val_get_NC(int fd,NC * ncp)2111 val_get_NC(int fd, NC *ncp)
2112 {
2113 int err, status=NC_NOERR;
2114 bufferinfo getbuf;
2115 char magic[5];
2116 size_t err_addr, pos_addr, base_addr;
2117 const char *hdf5_signature="\211HDF\r\n\032\n";
2118
2119 /* find Endianness of the running machine */
2120 getbuf.is_little_endian = check_little_endian();
2121
2122 /* Initialize the get buffer that stores the header read from the file */
2123 getbuf.offset = 0; /* read from start of the file */
2124
2125 /* CDF-5's minimum header size is 4 bytes more than CDF-1 and CDF-2's */
2126 getbuf.size = NC_DEFAULT_CHUNKSIZE;
2127 getbuf.pos = getbuf.base = (void *)malloc(getbuf.size);
2128
2129 /* Fetch the next header chunk. The chunk is 'gbp->size' bytes big
2130 * netcdf_file = header data
2131 * header = magic numrecs dim_list gatt_list var_list
2132 */
2133 status = val_fetch(fd, &getbuf);
2134 if (status != NC_NOERR) goto fn_exit;
2135
2136 /* Check HDF file signature */
2137 if (memcmp(getbuf.base, hdf5_signature, 8) == 0) {
2138 if (verbose) {
2139 printf("Error: Input file is in HDF format\n");
2140 printf(" ncvalidator only validates NetCDF classic files\n");
2141 }
2142 status = NC_ENOTSUPPORT;
2143 goto fn_exit;
2144 }
2145
2146 /* Check classic CDF file signature */
2147 magic[4] = '\0';
2148 memcpy(magic, getbuf.base, 4);
2149 getbuf.pos = (char*)getbuf.pos + 4;
2150
2151 if (memcmp(magic, ncmagic, 3) != 0) {
2152 if (verbose) printf("Error: Unknown file signature\n");
2153 if (verbose) printf("\tExpecting \"CDF1\", \"CDF2\", or \"CDF5\", but got \"%4s\"\n",magic);
2154 status = NC_ENOTNC;
2155 goto fn_exit;
2156 }
2157
2158 /* check version number in last byte of magic */
2159 if (magic[3] == 0x1) {
2160 getbuf.version = 1;
2161 ncp->format = 1;
2162 } else if (magic[3] == 0x2) {
2163 getbuf.version = 2;
2164 ncp->format = 2;
2165 } else if (magic[3] == 0x5) {
2166 getbuf.version = 5;
2167 ncp->format = 5;
2168 } else {
2169 if (verbose) printf("Error: Unknown file signature\n");
2170 if (verbose) printf("\tExpecting \"CDF1\", \"CDF2\", or \"CDF5\", but got \"%4s\"\n",magic);
2171 status = NC_ENOTNC;
2172 goto fn_exit;
2173 }
2174 if (trace) printf("magic = %d (file format: CDF-%d)\n", ncp->format,ncp->format);
2175
2176 /* header = magic numrecs dim_list gatt_list var_list
2177 * Check numrecs
2178 */
2179 err_addr = 4;
2180 status = val_check_buffer(fd, &getbuf, (getbuf.version < 5) ? 4 : 8);
2181 if (status != NC_NOERR) {
2182 if (verbose) printf("Error @ [0x%8.8zx]:\n", err_addr);
2183 if (verbose) printf("\tFailed to read the number of records\n");
2184 status = NC_ENOTNC;
2185 goto fn_exit;
2186 }
2187
2188 /* get numrecs from getbuf into ncp */
2189 if (getbuf.version < 5)
2190 ncp->numrecs = (long long) get_uint32(&getbuf);
2191 else
2192 ncp->numrecs = (long long) get_uint64(&getbuf);
2193
2194 if (trace) printf("\nnumrecs = %lld (number of records)\n", ncp->numrecs);
2195
2196 pos_addr = (size_t) getbuf.pos;
2197 base_addr = (size_t) getbuf.base;
2198 assert(pos_addr < base_addr + getbuf.size);
2199
2200 /* header = magic numrecs dim_list gatt_list var_list
2201 * dim_list = ABSENT | NC_DIMENSION nelems [dim ...]
2202 * Check dim_list
2203 */
2204 err = val_get_NC_dimarray(fd, &getbuf, &ncp->dims, ncp->numrecs);
2205 if (err != NC_NOERR && err != NC_ENULLPAD) {
2206 status = err;
2207 goto fn_exit;
2208 }
2209 if (status == NC_NOERR) status = err;
2210
2211 /* header = magic numrecs dim_list gatt_list var_list
2212 * att_list = ABSENT | NC_ATTRIBUTE nelems [attr ...]
2213 * Check att_list
2214 */
2215 err = val_get_NC_attrarray(fd, &getbuf, &ncp->attrs, "Global");
2216 if (err != NC_NOERR && err != NC_ENULLPAD) {
2217 status = err;
2218 goto fn_exit;
2219 }
2220 if (status == NC_NOERR) status = err;
2221
2222 /* header = magic numrecs dim_list gatt_list var_list
2223 * var_list = ABSENT | NC_VARIABLE nelems [var ...]
2224 * Check var_list
2225 */
2226 err = val_get_NC_vararray(fd, &getbuf, &ncp->vars, ncp->dims.ndefined);
2227 if (err != NC_NOERR && err != NC_ENULLPAD) {
2228 status = err;
2229 goto fn_exit;
2230 }
2231 if (status == NC_NOERR) status = err;
2232
2233 if (trace) printf("\n");
2234
2235 /* get file header size */
2236 ncp->xsz = hdr_len_NC(ncp);
2237
2238 /* Recompute the shapes of all variables */
2239 err = compute_var_shape(ncp);
2240 if (err != NC_NOERR) {
2241 status = err;
2242 goto fn_exit;
2243 }
2244
2245 err = val_NC_check_vlens(ncp);
2246 if (err != NC_NOERR) {
2247 status = err;
2248 goto fn_exit;
2249 }
2250
2251 err = val_NC_check_voff(ncp);
2252 if (err != NC_NOERR) {
2253 status = err;
2254 goto fn_exit;
2255 }
2256
2257 fn_exit:
2258 free(getbuf.base);
2259
2260 return status;
2261 }
2262
2263 /* End Of get NC */
2264
2265 static void
usage(char * argv0)2266 usage(char *argv0)
2267 {
2268 char *help =
2269 "Usage: %s [-h] | [-t] [-x] [-q] file\n"
2270 " [-h] Print help\n"
2271 " [-t] Turn on tracing mode, printing progress of validation\n"
2272 " [-x] Repair in-place the null-byte padding in file header.\n"
2273 " [-q] Quiet mode (exit 1 when fail, 0 success)\n"
2274 " file: Input netCDF file name\n"
2275 "*PnetCDF library version PNETCDF_RELEASE_VERSION of PNETCDF_RELEASE_DATE\n";
2276 fprintf(stderr, help, argv0);
2277 }
2278
main(int argc,char ** argv)2279 int main(int argc, char **argv)
2280 {
2281 char filename[512], *path;
2282 int i, omode, fd, status=NC_NOERR;
2283 NC *ncp=NULL;
2284 struct stat ncfilestat;
2285
2286 /* get command-line arguments */
2287 verbose = 1;
2288 trace = 0;
2289 repair = 0;
2290 while ((i = getopt(argc, argv, "xthq")) != EOF)
2291 switch(i) {
2292 case 'x': repair = 1;
2293 break;
2294 case 't': trace = 1;
2295 break;
2296 case 'q': verbose = 0;
2297 break;
2298 case 'h':
2299 default: usage(argv[0]);
2300 return 1;
2301 }
2302
2303 if (argv[optind] == NULL) { /* input file name is mandatory */
2304 usage(argv[0]);
2305 return 1;
2306 }
2307
2308 snprintf(filename, 512, "%s", argv[optind]);
2309
2310 /* remove the file system type prefix name if there is any.
2311 * For example, when filename = "lustre:/home/foo/testfile.nc", remove
2312 * "lustre:" to make path = "/home/foo/testfile.nc" in open() below
2313 */
2314 path = strchr(filename, ':');
2315 if (path == NULL) path = filename; /* no prefix */
2316 else path++;
2317
2318 if (repair) omode = O_RDWR;
2319 else omode = O_RDONLY;
2320
2321 fd = open(path, omode);
2322 if (fd == -1) {
2323 fprintf(stderr, "Error on open file %s (%s)\n",
2324 filename,strerror(errno));
2325 return 1;
2326 }
2327
2328 /* Allocate NC object */
2329 ncp = (NC*) calloc(1, sizeof(NC));
2330 if (ncp == NULL) {
2331 status = NC_ENOMEM;
2332 if (verbose) printf("Error at line %d when calling calloc()\n",__LINE__);
2333 goto prog_exit;
2334 }
2335
2336 /* read and validate the header */
2337 status = val_get_NC(fd, ncp);
2338 if (status != NC_NOERR && status != NC_ENULLPAD && status != -1)
2339 goto prog_exit;
2340
2341 /* check data size */
2342 if (-1 == fstat(fd, &ncfilestat)) {
2343 if (verbose) printf("Error at line %d fstat (%s)\n",__LINE__,strerror(errno));
2344 status = NC_EFILE;
2345 goto prog_exit;
2346 }
2347 if (ncp->numrecs > 0) {
2348 long long expect_fsize;
2349 expect_fsize = ncp->begin_rec + ncp->recsize * ncp->numrecs;
2350 if (expect_fsize < ncfilestat.st_size) {
2351 if (verbose) printf("Error: file size (%lld) is larger than expected (%lld)!\n",(long long)ncfilestat.st_size, expect_fsize);
2352 if (verbose) printf("\tbegin_rec=%lld recsize=%lld numrecs=%lld ncfilestat.st_size=%lld\n",ncp->begin_rec, ncp->recsize, ncp->numrecs, (long long) ncfilestat.st_size);
2353 status = NC_EFILE;
2354 goto prog_exit;
2355 }
2356 else if (expect_fsize > ncfilestat.st_size) {
2357 /* if file header are valid and the only error is the file size
2358 * less than expected, then this is due to partial data written
2359 * to the variable while the file is in no fill mode */
2360 if (verbose) {
2361 printf("Warning:\n");
2362 printf("\tfile size (%lld) is less than expected (%lld)!\n",(long long)ncfilestat.st_size, expect_fsize);
2363 }
2364 }
2365 }
2366 else {
2367 long long expect_fsize;
2368 if (ncp->vars.ndefined == 0)
2369 expect_fsize = ncp->xsz;
2370 else
2371 /* find the size of last fixed-size variable */
2372 expect_fsize = ncp->vars.value[ncp->vars.ndefined-1]->begin +
2373 ncp->vars.value[ncp->vars.ndefined-1]->len;
2374 if (expect_fsize < ncfilestat.st_size) {
2375 if (verbose) printf("Error:\n");
2376 if (verbose) printf("\tfile size (%lld) is larger than expected (%lld)!\n",(long long)ncfilestat.st_size, expect_fsize);
2377 status = NC_EFILE;
2378 goto prog_exit;
2379 }
2380 else if (expect_fsize > ncfilestat.st_size) {
2381 /* if file header are valid and the only error is the file size
2382 * less than expected, then this is due to partial data written
2383 * to the variable while the file is in no fill mode */
2384 if (verbose) printf("Warning:\n");
2385 if (verbose) printf("\tfile size (%lld) is less than expected (%lld)!\n",(long long)ncfilestat.st_size, expect_fsize);
2386 }
2387 }
2388
2389 prog_exit:
2390 if (ncp != NULL) {
2391 free_NC_dimarray(&ncp->dims);
2392 free_NC_attrarray(&ncp->attrs);
2393 free_NC_vararray(&ncp->vars);
2394 free(ncp);
2395 }
2396 close(fd);
2397
2398 if (verbose) {
2399 if (status == NC_NOERR)
2400 printf("File \"%s\" is a valid NetCDF classic file.\n",filename);
2401 else {
2402 printf("File \"%s\" fails to conform with classic CDF file format specifications\n",filename);
2403 if (repair) {
2404 printf("and it has been repaired in place to remove the errors.\n");
2405 printf("Please run \"%s %s\" to validate again.\n",argv[0],filename);
2406 }
2407 }
2408 }
2409
2410 exit((status == NC_NOERR) ? EXIT_SUCCESS : EXIT_FAILURE);
2411 }
2412