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