1 /*
2 * Copyright 2018, Unuiversity Corporation for Atmospheric Research
3 * See netcdf/COPYRIGHT file for copying and redistribution conditions.
4 */
5
6 #if HAVE_CONFIG_H
7 #include <config.h>
8 #endif
9
10 #include <stdlib.h>
11 #include <string.h>
12 #include <assert.h>
13 #ifdef HAVE_UNISTD_H
14 #include <unistd.h>
15 #endif
16
17 #include "nc3internal.h"
18 #include "netcdf_mem.h"
19 #include "rnd.h"
20 #include "ncx.h"
21 #include "ncrc.h"
22
23 /* These have to do with version numbers. */
24 #define MAGIC_NUM_LEN 4
25 #define VER_CLASSIC 1
26 #define VER_64BIT_OFFSET 2
27 #define VER_HDF5 3
28
29 #define NC_NUMRECS_OFFSET 4
30
31 /* For netcdf classic */
32 #define NC_NUMRECS_EXTENT3 4
33 /* For cdf5 */
34 #define NC_NUMRECS_EXTENT5 8
35
36 /* Internal function; breaks ncio abstraction */
37 extern int memio_extract(ncio* const nciop, size_t* sizep, void** memoryp);
38
39 static void
free_NC3INFO(NC3_INFO * nc3)40 free_NC3INFO(NC3_INFO *nc3)
41 {
42 if(nc3 == NULL)
43 return;
44 free_NC_dimarrayV(&nc3->dims);
45 free_NC_attrarrayV(&nc3->attrs);
46 free_NC_vararrayV(&nc3->vars);
47 free(nc3);
48 }
49
50 static NC3_INFO *
new_NC3INFO(const size_t * chunkp)51 new_NC3INFO(const size_t *chunkp)
52 {
53 NC3_INFO *ncp;
54 ncp = (NC3_INFO*)calloc(1,sizeof(NC3_INFO));
55 if(ncp == NULL) return ncp;
56 ncp->chunk = chunkp != NULL ? *chunkp : NC_SIZEHINT_DEFAULT;
57 /* Note that ncp->xsz is not set yet because we do not know the file format */
58 return ncp;
59 }
60
61 static NC3_INFO *
dup_NC3INFO(const NC3_INFO * ref)62 dup_NC3INFO(const NC3_INFO *ref)
63 {
64 NC3_INFO *ncp;
65 ncp = (NC3_INFO*)calloc(1,sizeof(NC3_INFO));
66 if(ncp == NULL) return ncp;
67
68 if(dup_NC_dimarrayV(&ncp->dims, &ref->dims) != NC_NOERR)
69 goto err;
70 if(dup_NC_attrarrayV(&ncp->attrs, &ref->attrs) != NC_NOERR)
71 goto err;
72 if(dup_NC_vararrayV(&ncp->vars, &ref->vars) != NC_NOERR)
73 goto err;
74
75 ncp->xsz = ref->xsz;
76 ncp->begin_var = ref->begin_var;
77 ncp->begin_rec = ref->begin_rec;
78 ncp->recsize = ref->recsize;
79 NC_set_numrecs(ncp, NC_get_numrecs(ref));
80 return ncp;
81 err:
82 free_NC3INFO(ncp);
83 return NULL;
84 }
85
86
87 /*
88 * Verify that this is a user nc_type
89 * Formerly NCcktype()
90 * Sense of the return is changed.
91 */
92 int
nc3_cktype(int mode,nc_type type)93 nc3_cktype(int mode, nc_type type)
94 {
95 #ifdef ENABLE_CDF5
96 if (mode & NC_CDF5) { /* CDF-5 format */
97 if (type >= NC_BYTE && type < NC_STRING) return NC_NOERR;
98 } else
99 #endif
100 if (mode & NC_64BIT_OFFSET) { /* CDF-2 format */
101 if (type >= NC_BYTE && type <= NC_DOUBLE) return NC_NOERR;
102 } else if ((mode & NC_64BIT_OFFSET) == 0) { /* CDF-1 format */
103 if (type >= NC_BYTE && type <= NC_DOUBLE) return NC_NOERR;
104 }
105 return(NC_EBADTYPE);
106 }
107
108
109 /*
110 * How many objects of 'type'
111 * will fit into xbufsize?
112 */
113 size_t
ncx_howmany(nc_type type,size_t xbufsize)114 ncx_howmany(nc_type type, size_t xbufsize)
115 {
116 switch(type){
117 case NC_BYTE:
118 case NC_CHAR:
119 return xbufsize;
120 case NC_SHORT:
121 return xbufsize/X_SIZEOF_SHORT;
122 case NC_INT:
123 return xbufsize/X_SIZEOF_INT;
124 case NC_FLOAT:
125 return xbufsize/X_SIZEOF_FLOAT;
126 case NC_DOUBLE:
127 return xbufsize/X_SIZEOF_DOUBLE;
128 case NC_UBYTE:
129 return xbufsize;
130 case NC_USHORT:
131 return xbufsize/X_SIZEOF_USHORT;
132 case NC_UINT:
133 return xbufsize/X_SIZEOF_UINT;
134 case NC_INT64:
135 return xbufsize/X_SIZEOF_LONGLONG;
136 case NC_UINT64:
137 return xbufsize/X_SIZEOF_ULONGLONG;
138 default:
139 assert("ncx_howmany: Bad type" == 0);
140 return(0);
141 }
142 }
143
144 #define D_RNDUP(x, align) _RNDUP(x, (off_t)(align))
145
146 /*
147 * Compute each variable's 'begin' offset,
148 * update 'begin_rec' as well.
149 */
150 static int
NC_begins(NC3_INFO * ncp,size_t h_minfree,size_t v_align,size_t v_minfree,size_t r_align)151 NC_begins(NC3_INFO* ncp,
152 size_t h_minfree, size_t v_align,
153 size_t v_minfree, size_t r_align)
154 {
155 size_t ii, j;
156 int sizeof_off_t;
157 off_t index = 0;
158 off_t old_ncp_begin_var;
159 NC_var **vpp;
160 NC_var *last = NULL;
161 NC_var *first_var = NULL; /* first "non-record" var */
162
163
164 if(v_align == NC_ALIGN_CHUNK)
165 v_align = ncp->chunk;
166 if(r_align == NC_ALIGN_CHUNK)
167 r_align = ncp->chunk;
168
169 if (fIsSet(ncp->flags, NC_64BIT_OFFSET) || fIsSet(ncp->flags, NC_64BIT_DATA)) {
170 sizeof_off_t = 8;
171 } else {
172 sizeof_off_t = 4;
173 }
174
175 ncp->xsz = ncx_len_NC(ncp,sizeof_off_t);
176
177 if(ncp->vars.nelems == 0)
178 return NC_NOERR;
179
180 old_ncp_begin_var = ncp->begin_var;
181
182 /* only (re)calculate begin_var if there is not sufficient space in header
183 or start of non-record variables is not aligned as requested by valign */
184 if (ncp->begin_var < ncp->xsz + h_minfree ||
185 ncp->begin_var != D_RNDUP(ncp->begin_var, v_align) )
186 {
187 index = (off_t) ncp->xsz;
188 ncp->begin_var = D_RNDUP(index, v_align);
189 if(ncp->begin_var < index + h_minfree)
190 {
191 ncp->begin_var = D_RNDUP(index + (off_t)h_minfree, v_align);
192 }
193 }
194
195 if (ncp->old != NULL) {
196 /* check whether the new begin_var is smaller */
197 if (ncp->begin_var < ncp->old->begin_var)
198 ncp->begin_var = ncp->old->begin_var;
199 }
200
201 index = ncp->begin_var;
202
203 /* loop thru vars, first pass is for the 'non-record' vars */
204 j = 0;
205 vpp = ncp->vars.value;
206 for(ii = 0; ii < ncp->vars.nelems ; ii++, vpp++)
207 {
208 if( IS_RECVAR(*vpp) )
209 {
210 /* skip record variables on this pass */
211 continue;
212 }
213 if (first_var == NULL) first_var = *vpp;
214
215 #if 0
216 fprintf(stderr, " VAR %d %s: %ld\n", ii, (*vpp)->name->cp, (long)index);
217 #endif
218 if( sizeof_off_t == 4 && (index > X_OFF_MAX || index < 0) )
219 {
220 ncp->begin_var = old_ncp_begin_var;
221 return NC_EVARSIZE;
222 }
223 (*vpp)->begin = index;
224
225 if (ncp->old != NULL) {
226 /* move to the next fixed variable */
227 for (; j<ncp->old->vars.nelems; j++) {
228 if (!IS_RECVAR(ncp->old->vars.value[j]))
229 break;
230 }
231
232 if (j < ncp->old->vars.nelems) {
233 if ((*vpp)->begin < ncp->old->vars.value[j]->begin) {
234 /* the first ncp->vars.nelems fixed variables
235 should be the same. If the new begin is smaller,
236 reuse the old begin */
237 (*vpp)->begin = ncp->old->vars.value[j]->begin;
238 index = (*vpp)->begin;
239 }
240 j++;
241 }
242 }
243
244 index += (*vpp)->len;
245 }
246
247 if (ncp->old != NULL) {
248 /* check whether the new begin_rec is smaller */
249 if (ncp->begin_rec < ncp->old->begin_rec)
250 ncp->begin_rec = ncp->old->begin_rec;
251 }
252
253 /* only (re)calculate begin_rec if there is not sufficient
254 space at end of non-record variables or if start of record
255 variables is not aligned as requested by r_align */
256 if (ncp->begin_rec < index + v_minfree ||
257 ncp->begin_rec != D_RNDUP(ncp->begin_rec, r_align) )
258 {
259 ncp->begin_rec = D_RNDUP(index, r_align);
260 if(ncp->begin_rec < index + v_minfree)
261 {
262 ncp->begin_rec = D_RNDUP(index + (off_t)v_minfree, r_align);
263 }
264 }
265
266 if (first_var != NULL)
267 ncp->begin_var = first_var->begin;
268 else
269 ncp->begin_var = ncp->begin_rec;
270
271 index = ncp->begin_rec;
272
273 ncp->recsize = 0;
274
275 /* loop thru vars, second pass is for the 'record' vars */
276 j = 0;
277 vpp = (NC_var **)ncp->vars.value;
278 for(ii = 0; ii < ncp->vars.nelems; ii++, vpp++)
279 {
280 if( !IS_RECVAR(*vpp) )
281 {
282 /* skip non-record variables on this pass */
283 continue;
284 }
285
286 #if 0
287 fprintf(stderr, " REC %d %s: %ld\n", ii, (*vpp)->name->cp, (long)index);
288 #endif
289 if( sizeof_off_t == 4 && (index > X_OFF_MAX || index < 0) )
290 {
291 ncp->begin_var = old_ncp_begin_var;
292 return NC_EVARSIZE;
293 }
294 (*vpp)->begin = index;
295
296 if (ncp->old != NULL) {
297 /* move to the next record variable */
298 for (; j<ncp->old->vars.nelems; j++)
299 if (IS_RECVAR(ncp->old->vars.value[j]))
300 break;
301 if (j < ncp->old->vars.nelems) {
302 if ((*vpp)->begin < ncp->old->vars.value[j]->begin)
303 /* if the new begin is smaller, use the old begin */
304 (*vpp)->begin = ncp->old->vars.value[j]->begin;
305 j++;
306 }
307 }
308
309 index += (*vpp)->len;
310 /* check if record size must fit in 32-bits */
311 #if SIZEOF_OFF_T == SIZEOF_SIZE_T && SIZEOF_SIZE_T == 4
312 if( ncp->recsize > X_UINT_MAX - (*vpp)->len )
313 {
314 ncp->begin_var = old_ncp_begin_var;
315 return NC_EVARSIZE;
316 }
317 #endif
318 ncp->recsize += (*vpp)->len;
319 last = (*vpp);
320 }
321
322 /*
323 * for special case (Check CDF-1 and CDF-2 file format specifications.)
324 * "A special case: Where there is exactly one record variable, we drop the
325 * requirement that each record be four-byte aligned, so in this case there
326 * is no record padding."
327 */
328 if (last != NULL) {
329 if (ncp->recsize == last->len) {
330 /* exactly one record variable, pack value */
331 ncp->recsize = *last->dsizes * last->xsz;
332 }
333 }
334
335 if(NC_IsNew(ncp))
336 NC_set_numrecs(ncp, 0);
337 return NC_NOERR;
338 }
339
340
341 /*
342 * Read just the numrecs member.
343 * (A relatively expensive way to do things.)
344 */
345 int
read_numrecs(NC3_INFO * ncp)346 read_numrecs(NC3_INFO *ncp)
347 {
348 int status = NC_NOERR;
349 const void *xp = NULL;
350 size_t new_nrecs = 0;
351 size_t old_nrecs = NC_get_numrecs(ncp);
352 size_t nc_numrecs_extent = NC_NUMRECS_EXTENT3; /* CDF-1 and CDF-2 */
353
354 assert(!NC_indef(ncp));
355
356 if (fIsSet(ncp->flags, NC_64BIT_DATA))
357 nc_numrecs_extent = NC_NUMRECS_EXTENT5; /* CDF-5 */
358
359 status = ncio_get(ncp->nciop,
360 NC_NUMRECS_OFFSET, nc_numrecs_extent, 0, (void **)&xp);/* cast away const */
361 if(status != NC_NOERR)
362 return status;
363
364 if (fIsSet(ncp->flags, NC_64BIT_DATA)) {
365 unsigned long long tmp=0;
366 status = ncx_get_uint64(&xp, &tmp);
367 new_nrecs = (size_t)tmp;
368 } else
369 status = ncx_get_size_t(&xp, &new_nrecs);
370
371 (void) ncio_rel(ncp->nciop, NC_NUMRECS_OFFSET, 0);
372
373 if(status == NC_NOERR && old_nrecs != new_nrecs)
374 {
375 NC_set_numrecs(ncp, new_nrecs);
376 fClr(ncp->flags, NC_NDIRTY);
377 }
378
379 return status;
380 }
381
382
383 /*
384 * Write out just the numrecs member.
385 * (A relatively expensive way to do things.)
386 */
387 int
write_numrecs(NC3_INFO * ncp)388 write_numrecs(NC3_INFO *ncp)
389 {
390 int status = NC_NOERR;
391 void *xp = NULL;
392 size_t nc_numrecs_extent = NC_NUMRECS_EXTENT3; /* CDF-1 and CDF-2 */
393
394 assert(!NC_readonly(ncp));
395 assert(!NC_indef(ncp));
396
397 if (fIsSet(ncp->flags, NC_64BIT_DATA))
398 nc_numrecs_extent = NC_NUMRECS_EXTENT5; /* CDF-5 */
399
400 status = ncio_get(ncp->nciop,
401 NC_NUMRECS_OFFSET, nc_numrecs_extent, RGN_WRITE, &xp);
402 if(status != NC_NOERR)
403 return status;
404
405 {
406 const size_t nrecs = NC_get_numrecs(ncp);
407 if (fIsSet(ncp->flags, NC_64BIT_DATA))
408 status = ncx_put_uint64(&xp, (unsigned long long)nrecs);
409 else
410 status = ncx_put_size_t(&xp, &nrecs);
411 }
412
413 (void) ncio_rel(ncp->nciop, NC_NUMRECS_OFFSET, RGN_MODIFIED);
414
415 if(status == NC_NOERR)
416 fClr(ncp->flags, NC_NDIRTY);
417
418 return status;
419 }
420
421
422 /*
423 * Read in the header
424 * It is expensive.
425 */
426 static int
read_NC(NC3_INFO * ncp)427 read_NC(NC3_INFO *ncp)
428 {
429 int status = NC_NOERR;
430
431 free_NC_dimarrayV(&ncp->dims);
432 free_NC_attrarrayV(&ncp->attrs);
433 free_NC_vararrayV(&ncp->vars);
434
435 status = nc_get_NC(ncp);
436
437 if(status == NC_NOERR)
438 fClr(ncp->flags, NC_NDIRTY | NC_HDIRTY);
439
440 return status;
441 }
442
443
444 /*
445 * Write out the header
446 */
447 static int
write_NC(NC3_INFO * ncp)448 write_NC(NC3_INFO *ncp)
449 {
450 int status = NC_NOERR;
451
452 assert(!NC_readonly(ncp));
453
454 status = ncx_put_NC(ncp, NULL, 0, 0);
455
456 if(status == NC_NOERR)
457 fClr(ncp->flags, NC_NDIRTY | NC_HDIRTY);
458
459 return status;
460 }
461
462
463 /*
464 * Write the header or the numrecs if necessary.
465 */
466 int
NC_sync(NC3_INFO * ncp)467 NC_sync(NC3_INFO *ncp)
468 {
469 assert(!NC_readonly(ncp));
470
471 if(NC_hdirty(ncp))
472 {
473 return write_NC(ncp);
474 }
475 /* else */
476
477 if(NC_ndirty(ncp))
478 {
479 return write_numrecs(ncp);
480 }
481 /* else */
482
483 return NC_NOERR;
484 }
485
486
487 /*
488 * Initialize the 'non-record' variables.
489 */
490 static int
fillerup(NC3_INFO * ncp)491 fillerup(NC3_INFO *ncp)
492 {
493 int status = NC_NOERR;
494 size_t ii;
495 NC_var **varpp;
496
497 assert(!NC_readonly(ncp));
498
499 /* loop thru vars */
500 varpp = ncp->vars.value;
501 for(ii = 0; ii < ncp->vars.nelems; ii++, varpp++)
502 {
503 if ((*varpp)->no_fill) continue;
504
505 if(IS_RECVAR(*varpp))
506 {
507 /* skip record variables */
508 continue;
509 }
510
511 status = fill_NC_var(ncp, *varpp, (*varpp)->len, 0);
512 if(status != NC_NOERR)
513 break;
514 }
515 return status;
516 }
517
518 /* Begin endef */
519
520 /*
521 */
522 static int
fill_added_recs(NC3_INFO * gnu,NC3_INFO * old)523 fill_added_recs(NC3_INFO *gnu, NC3_INFO *old)
524 {
525 NC_var ** const gnu_varpp = (NC_var **)gnu->vars.value;
526
527 const int old_nrecs = (int) NC_get_numrecs(old);
528 int recno = 0;
529 NC_var **vpp = gnu_varpp;
530 NC_var *const *const end = &vpp[gnu->vars.nelems];
531 int numrecvars = 0;
532
533 /* Determine if there is only one record variable. If so, we
534 must treat as a special case because there's no record padding */
535 for(; vpp < end; vpp++) {
536 if(IS_RECVAR(*vpp)) {
537 numrecvars++;
538 }
539 }
540
541 for(; recno < old_nrecs; recno++)
542 {
543 int varid = (int)old->vars.nelems;
544 for(; varid < (int)gnu->vars.nelems; varid++)
545 {
546 const NC_var *const gnu_varp = *(gnu_varpp + varid);
547
548 if (gnu_varp->no_fill) continue;
549
550 if(!IS_RECVAR(gnu_varp))
551 {
552 /* skip non-record variables */
553 continue;
554 }
555 /* else */
556 {
557 size_t varsize = numrecvars == 1 ? gnu->recsize : gnu_varp->len;
558 const int status = fill_NC_var(gnu, gnu_varp, varsize, recno);
559 if(status != NC_NOERR)
560 return status;
561 }
562 }
563 }
564 return NC_NOERR;
565 }
566
567 /*
568 */
569 static int
fill_added(NC3_INFO * gnu,NC3_INFO * old)570 fill_added(NC3_INFO *gnu, NC3_INFO *old)
571 {
572 NC_var ** const gnu_varpp = (NC_var **)gnu->vars.value;
573 int varid = (int)old->vars.nelems;
574
575 for(; varid < (int)gnu->vars.nelems; varid++)
576 {
577 const NC_var *const gnu_varp = *(gnu_varpp + varid);
578
579 if (gnu_varp->no_fill) continue;
580
581 if(IS_RECVAR(gnu_varp))
582 {
583 /* skip record variables */
584 continue;
585 }
586 /* else */
587 {
588 const int status = fill_NC_var(gnu, gnu_varp, gnu_varp->len, 0);
589 if(status != NC_NOERR)
590 return status;
591 }
592 }
593
594 return NC_NOERR;
595 }
596
597
598 /*
599 * Move the records "out".
600 * Fill as needed.
601 */
602 static int
move_recs_r(NC3_INFO * gnu,NC3_INFO * old)603 move_recs_r(NC3_INFO *gnu, NC3_INFO *old)
604 {
605 int status;
606 int recno;
607 int varid;
608 NC_var **gnu_varpp = (NC_var **)gnu->vars.value;
609 NC_var **old_varpp = (NC_var **)old->vars.value;
610 NC_var *gnu_varp;
611 NC_var *old_varp;
612 off_t gnu_off;
613 off_t old_off;
614 const size_t old_nrecs = NC_get_numrecs(old);
615
616 /* Don't parallelize this loop */
617 for(recno = (int)old_nrecs -1; recno >= 0; recno--)
618 {
619 /* Don't parallelize this loop */
620 for(varid = (int)old->vars.nelems -1; varid >= 0; varid--)
621 {
622 gnu_varp = *(gnu_varpp + varid);
623 if(!IS_RECVAR(gnu_varp))
624 {
625 /* skip non-record variables on this pass */
626 continue;
627 }
628 /* else */
629
630 /* else, a pre-existing variable */
631 old_varp = *(old_varpp + varid);
632 gnu_off = gnu_varp->begin + (off_t)(gnu->recsize * recno);
633 old_off = old_varp->begin + (off_t)(old->recsize * recno);
634
635 if(gnu_off == old_off)
636 continue; /* nothing to do */
637
638 assert(gnu_off > old_off);
639
640 status = ncio_move(gnu->nciop, gnu_off, old_off,
641 old_varp->len, 0);
642
643 if(status != NC_NOERR)
644 return status;
645
646 }
647 }
648
649 NC_set_numrecs(gnu, old_nrecs);
650
651 return NC_NOERR;
652 }
653
654
655 /*
656 * Move the "non record" variables "out".
657 * Fill as needed.
658 */
659 static int
move_vars_r(NC3_INFO * gnu,NC3_INFO * old)660 move_vars_r(NC3_INFO *gnu, NC3_INFO *old)
661 {
662 int err, status=NC_NOERR;
663 int varid;
664 NC_var **gnu_varpp = (NC_var **)gnu->vars.value;
665 NC_var **old_varpp = (NC_var **)old->vars.value;
666 NC_var *gnu_varp;
667 NC_var *old_varp;
668 off_t gnu_off;
669 off_t old_off;
670
671 /* Don't parallelize this loop */
672 for(varid = (int)old->vars.nelems -1;
673 varid >= 0; varid--)
674 {
675 gnu_varp = *(gnu_varpp + varid);
676 if(IS_RECVAR(gnu_varp))
677 {
678 /* skip record variables on this pass */
679 continue;
680 }
681 /* else */
682
683 old_varp = *(old_varpp + varid);
684 gnu_off = gnu_varp->begin;
685 old_off = old_varp->begin;
686
687 if (gnu_off > old_off) {
688 err = ncio_move(gnu->nciop, gnu_off, old_off,
689 old_varp->len, 0);
690 if (status == NC_NOERR) status = err;
691 }
692 }
693 return status;
694 }
695
696
697 /*
698 * Given a valid ncp, return NC_EVARSIZE if any variable has a bad len
699 * (product of non-rec dim sizes too large), else return NC_NOERR.
700 */
701 int
NC_check_vlens(NC3_INFO * ncp)702 NC_check_vlens(NC3_INFO *ncp)
703 {
704 NC_var **vpp;
705 /* maximum permitted variable size (or size of one record's worth
706 of a record variable) in bytes. This is different for format 1
707 and format 2. */
708 long long vlen_max;
709 size_t ii;
710 size_t large_vars_count;
711 size_t rec_vars_count;
712 int last = 0;
713
714 if(ncp->vars.nelems == 0)
715 return NC_NOERR;
716
717 if (fIsSet(ncp->flags,NC_64BIT_DATA)) /* CDF-5 */
718 vlen_max = X_INT64_MAX - 3; /* "- 3" handles rounded-up size */
719 else if (fIsSet(ncp->flags,NC_64BIT_OFFSET) && sizeof(off_t) > 4)
720 /* CDF2 format and LFS */
721 vlen_max = X_UINT_MAX - 3; /* "- 3" handles rounded-up size */
722 else /* CDF1 format */
723 vlen_max = X_INT_MAX - 3;
724
725 /* Loop through vars, first pass is for non-record variables. */
726 large_vars_count = 0;
727 rec_vars_count = 0;
728 vpp = ncp->vars.value;
729 for (ii = 0; ii < ncp->vars.nelems; ii++, vpp++) {
730 if( !IS_RECVAR(*vpp) ) {
731 last = 0;
732 if( NC_check_vlen(*vpp, vlen_max) == 0 ) {
733 if (fIsSet(ncp->flags,NC_64BIT_DATA)) /* too big for CDF-5 */
734 return NC_EVARSIZE;
735 large_vars_count++;
736 last = 1;
737 }
738 } else {
739 rec_vars_count++;
740 }
741 }
742 /* OK if last non-record variable size too large, since not used to
743 compute an offset */
744 if( large_vars_count > 1) { /* only one "too-large" variable allowed */
745 return NC_EVARSIZE;
746 }
747 /* and it has to be the last one */
748 if( large_vars_count == 1 && last == 0) {
749 return NC_EVARSIZE;
750 }
751 if( rec_vars_count > 0 ) {
752 /* and if it's the last one, there can't be any record variables */
753 if( large_vars_count == 1 && last == 1) {
754 return NC_EVARSIZE;
755 }
756 /* Loop through vars, second pass is for record variables. */
757 large_vars_count = 0;
758 vpp = ncp->vars.value;
759 for (ii = 0; ii < ncp->vars.nelems; ii++, vpp++) {
760 if( IS_RECVAR(*vpp) ) {
761 last = 0;
762 if( NC_check_vlen(*vpp, vlen_max) == 0 ) {
763 if (fIsSet(ncp->flags,NC_64BIT_DATA)) /* too big for CDF-5 */
764 return NC_EVARSIZE;
765 large_vars_count++;
766 last = 1;
767 }
768 }
769 }
770 /* OK if last record variable size too large, since not used to
771 compute an offset */
772 if( large_vars_count > 1) { /* only one "too-large" variable allowed */
773 return NC_EVARSIZE;
774 }
775 /* and it has to be the last one */
776 if( large_vars_count == 1 && last == 0) {
777 return NC_EVARSIZE;
778 }
779 }
780 return NC_NOERR;
781 }
782
783 /*----< NC_check_voffs() >---------------------------------------------------*/
784 /*
785 * Given a valid ncp, check whether the file starting offsets (begin) of all
786 * variables follows the same increasing order as they were defined.
787 */
788 int
NC_check_voffs(NC3_INFO * ncp)789 NC_check_voffs(NC3_INFO *ncp)
790 {
791 size_t i;
792 off_t prev_off;
793 NC_var *varp;
794
795 if (ncp->vars.nelems == 0) return NC_NOERR;
796
797 /* Loop through vars, first pass is for non-record variables */
798 prev_off = ncp->begin_var;
799 for (i=0; i<ncp->vars.nelems; i++) {
800 varp = ncp->vars.value[i];
801 if (IS_RECVAR(varp)) continue;
802
803 if (varp->begin < prev_off) {
804 #if 0
805 fprintf(stderr,"Variable \"%s\" begin offset (%lld) is less than previous variable end offset (%lld)\n", varp->name->cp, varp->begin, prev_off);
806 #endif
807 return NC_ENOTNC;
808 }
809 prev_off = varp->begin + varp->len;
810 }
811
812 if (ncp->begin_rec < prev_off) {
813 #if 0
814 fprintf(stderr,"Record variable section begin offset (%lld) is less than fix-sized variable section end offset (%lld)\n", varp->begin, prev_off);
815 #endif
816 return NC_ENOTNC;
817 }
818
819 /* Loop through vars, second pass is for record variables */
820 prev_off = ncp->begin_rec;
821 for (i=0; i<ncp->vars.nelems; i++) {
822 varp = ncp->vars.value[i];
823 if (!IS_RECVAR(varp)) continue;
824
825 if (varp->begin < prev_off) {
826 #if 0
827 fprintf(stderr,"Variable \"%s\" begin offset (%lld) is less than previous variable end offset (%lld)\n", varp->name->cp, varp->begin, prev_off);
828 #endif
829 return NC_ENOTNC;
830 }
831 prev_off = varp->begin + varp->len;
832 }
833
834 return NC_NOERR;
835 }
836
837 /*
838 * End define mode.
839 * Common code for ncendef, ncclose(endef)
840 * Flushes I/O buffers.
841 */
842 static int
NC_endef(NC3_INFO * ncp,size_t h_minfree,size_t v_align,size_t v_minfree,size_t r_align)843 NC_endef(NC3_INFO *ncp,
844 size_t h_minfree, size_t v_align,
845 size_t v_minfree, size_t r_align)
846 {
847 int status = NC_NOERR;
848
849 assert(!NC_readonly(ncp));
850 assert(NC_indef(ncp));
851
852 status = NC_check_vlens(ncp);
853 if(status != NC_NOERR)
854 return status;
855 status = NC_begins(ncp, h_minfree, v_align, v_minfree, r_align);
856 if(status != NC_NOERR)
857 return status;
858 status = NC_check_voffs(ncp);
859 if(status != NC_NOERR)
860 return status;
861
862 if(ncp->old != NULL)
863 {
864 /* a plain redef, not a create */
865 assert(!NC_IsNew(ncp));
866 assert(fIsSet(ncp->flags, NC_INDEF));
867 assert(ncp->begin_rec >= ncp->old->begin_rec);
868 assert(ncp->begin_var >= ncp->old->begin_var);
869
870 if(ncp->vars.nelems != 0)
871 {
872 if(ncp->begin_rec > ncp->old->begin_rec)
873 {
874 status = move_recs_r(ncp, ncp->old);
875 if(status != NC_NOERR)
876 return status;
877 if(ncp->begin_var > ncp->old->begin_var)
878 {
879 status = move_vars_r(ncp, ncp->old);
880 if(status != NC_NOERR)
881 return status;
882 }
883 /* else if (ncp->begin_var == ncp->old->begin_var) { NOOP } */
884 }
885 else
886 {
887 /* due to fixed variable alignment, it is possible that header
888 grows but begin_rec did not change */
889 if(ncp->begin_var > ncp->old->begin_var)
890 {
891 status = move_vars_r(ncp, ncp->old);
892 if(status != NC_NOERR)
893 return status;
894 }
895 /* Even if (ncp->begin_rec == ncp->old->begin_rec)
896 and (ncp->begin_var == ncp->old->begin_var)
897 might still have added a new record variable */
898 if(ncp->recsize > ncp->old->recsize)
899 {
900 status = move_recs_r(ncp, ncp->old);
901 if(status != NC_NOERR)
902 return status;
903 }
904 }
905 }
906 }
907
908 status = write_NC(ncp);
909 if(status != NC_NOERR)
910 return status;
911
912 /* fill mode is now per variable */
913 {
914 if(NC_IsNew(ncp))
915 {
916 status = fillerup(ncp);
917 if(status != NC_NOERR)
918 return status;
919
920 }
921 else if(ncp->old == NULL ? 0
922 : (ncp->vars.nelems > ncp->old->vars.nelems))
923 {
924 status = fill_added(ncp, ncp->old);
925 if(status != NC_NOERR)
926 return status;
927 status = fill_added_recs(ncp, ncp->old);
928 if(status != NC_NOERR)
929 return status;
930 }
931 }
932
933 if(ncp->old != NULL)
934 {
935 free_NC3INFO(ncp->old);
936 ncp->old = NULL;
937 }
938
939 fClr(ncp->flags, NC_CREAT | NC_INDEF);
940
941 return ncio_sync(ncp->nciop);
942 }
943
944
945 /*
946 * Compute the expected size of the file.
947 */
948 int
NC_calcsize(const NC3_INFO * ncp,off_t * calcsizep)949 NC_calcsize(const NC3_INFO *ncp, off_t *calcsizep)
950 {
951 NC_var **vpp = (NC_var **)ncp->vars.value;
952 NC_var *const *const end = &vpp[ncp->vars.nelems];
953 NC_var *last_fix = NULL; /* last "non-record" var */
954 int numrecvars = 0; /* number of record variables */
955
956 if(ncp->vars.nelems == 0) { /* no non-record variables and
957 no record variables */
958 *calcsizep = ncp->xsz; /* size of header */
959 return NC_NOERR;
960 }
961
962 for( /*NADA*/; vpp < end; vpp++) {
963 if(IS_RECVAR(*vpp)) {
964 numrecvars++;
965 } else {
966 last_fix = *vpp;
967 }
968 }
969
970 if(numrecvars == 0) {
971 off_t varsize;
972 assert(last_fix != NULL);
973 varsize = last_fix->len;
974 if(last_fix->len == X_UINT_MAX) { /* huge last fixed var */
975 int i;
976 varsize = 1;
977 for(i = 0; i < last_fix->ndims; i++ ) {
978 varsize *= (last_fix->shape ? last_fix->shape[i] : 1);
979 }
980 }
981 *calcsizep = last_fix->begin + varsize;
982 /*last_var = last_fix;*/
983 } else { /* we have at least one record variable */
984 *calcsizep = ncp->begin_rec + ncp->numrecs * ncp->recsize;
985 }
986
987 return NC_NOERR;
988 }
989
990 /* Public */
991
992 #if 0 /* no longer needed */
993 int NC3_new_nc(NC3_INFO** ncpp)
994 {
995 NC *nc;
996 NC3_INFO* nc3;
997
998 ncp = (NC *) malloc(sizeof(NC));
999 if(ncp == NULL)
1000 return NC_ENOMEM;
1001 (void) memset(ncp, 0, sizeof(NC));
1002
1003 ncp->xsz = MIN_NC_XSZ;
1004 assert(ncp->xsz == ncx_len_NC(ncp,0));
1005
1006 if(ncpp) *ncpp = ncp;
1007 return NC_NOERR;
1008
1009 }
1010 #endif
1011
1012 /* WARNING: SIGNATURE CHANGE */
1013 int
NC3_create(const char * path,int ioflags,size_t initialsz,int basepe,size_t * chunksizehintp,void * parameters,const NC_Dispatch * dispatch,int ncid)1014 NC3_create(const char *path, int ioflags, size_t initialsz, int basepe,
1015 size_t *chunksizehintp, void *parameters,
1016 const NC_Dispatch *dispatch, int ncid)
1017 {
1018 int status = NC_NOERR;
1019 void *xp = NULL;
1020 int sizeof_off_t = 0;
1021 NC *nc;
1022 NC3_INFO* nc3 = NULL;
1023
1024 /* Find NC struct for this file. */
1025 if ((status = NC_check_id(ncid, &nc)))
1026 return status;
1027
1028 /* Create our specific NC3_INFO instance */
1029 nc3 = new_NC3INFO(chunksizehintp);
1030
1031 #if ALWAYS_NC_SHARE /* DEBUG */
1032 fSet(ioflags, NC_SHARE);
1033 #endif
1034
1035 /*
1036 * Only pe 0 is valid
1037 */
1038 if(basepe != 0) {
1039 if(nc3) free(nc3);
1040 return NC_EINVAL;
1041 }
1042 assert(nc3->flags == 0);
1043
1044 /* Now we can set min size */
1045 if (fIsSet(ioflags, NC_64BIT_DATA))
1046 nc3->xsz = MIN_NC5_XSZ; /* CDF-5 has minimum 16 extra bytes */
1047 else
1048 nc3->xsz = MIN_NC3_XSZ;
1049
1050 if (fIsSet(ioflags, NC_64BIT_OFFSET)) {
1051 fSet(nc3->flags, NC_64BIT_OFFSET);
1052 sizeof_off_t = 8;
1053 } else if (fIsSet(ioflags, NC_64BIT_DATA)) {
1054 fSet(nc3->flags, NC_64BIT_DATA);
1055 sizeof_off_t = 8;
1056 } else {
1057 sizeof_off_t = 4;
1058 }
1059
1060 assert(nc3->xsz == ncx_len_NC(nc3,sizeof_off_t));
1061
1062 status = ncio_create(path, ioflags, initialsz,
1063 0, nc3->xsz, &nc3->chunk, NULL,
1064 &nc3->nciop, &xp);
1065 if(status != NC_NOERR)
1066 {
1067 /* translate error status */
1068 if(status == EEXIST)
1069 status = NC_EEXIST;
1070 goto unwind_alloc;
1071 }
1072
1073 fSet(nc3->flags, NC_CREAT);
1074
1075 if(fIsSet(nc3->nciop->ioflags, NC_SHARE))
1076 {
1077 /*
1078 * NC_SHARE implies sync up the number of records as well.
1079 * (File format version one.)
1080 * Note that other header changes are not shared
1081 * automatically. Some sort of IPC (external to this package)
1082 * would be used to trigger a call to nc_sync().
1083 */
1084 fSet(nc3->flags, NC_NSYNC);
1085 }
1086
1087 status = ncx_put_NC(nc3, &xp, sizeof_off_t, nc3->xsz);
1088 if(status != NC_NOERR)
1089 goto unwind_ioc;
1090
1091 if(chunksizehintp != NULL)
1092 *chunksizehintp = nc3->chunk;
1093
1094 /* Link nc3 and nc */
1095 NC3_DATA_SET(nc,nc3);
1096 nc->int_ncid = nc3->nciop->fd;
1097
1098 return NC_NOERR;
1099
1100 unwind_ioc:
1101 if(nc3 != NULL) {
1102 (void) ncio_close(nc3->nciop, 1); /* N.B.: unlink */
1103 nc3->nciop = NULL;
1104 }
1105 /*FALLTHRU*/
1106 unwind_alloc:
1107 free_NC3INFO(nc3);
1108 if(nc)
1109 NC3_DATA_SET(nc,NULL);
1110 return status;
1111 }
1112
1113 #if 0
1114 /* This function sets a default create flag that will be logically
1115 or'd to whatever flags are passed into nc_create for all future
1116 calls to nc_create.
1117 Valid default create flags are NC_64BIT_OFFSET, NC_CDF5, NC_CLOBBER,
1118 NC_LOCK, NC_SHARE. */
1119 int
1120 nc_set_default_format(int format, int *old_formatp)
1121 {
1122 /* Return existing format if desired. */
1123 if (old_formatp)
1124 *old_formatp = default_create_format;
1125
1126 /* Make sure only valid format is set. */
1127 #ifdef USE_NETCDF4
1128 if (format != NC_FORMAT_CLASSIC && format != NC_FORMAT_64BIT_OFFSET &&
1129 format != NC_FORMAT_NETCDF4 && format != NC_FORMAT_NETCDF4_CLASSIC)
1130 return NC_EINVAL;
1131 #else
1132 if (format != NC_FORMAT_CLASSIC && format != NC_FORMAT_64BIT_OFFSET
1133 #ifdef ENABLE_CDF5
1134 && format != NC_FORMAT_CDF5
1135 #endif
1136 )
1137 return NC_EINVAL;
1138 #endif
1139 default_create_format = format;
1140 return NC_NOERR;
1141 }
1142 #endif
1143
1144 int
NC3_open(const char * path,int ioflags,int basepe,size_t * chunksizehintp,void * parameters,const NC_Dispatch * dispatch,int ncid)1145 NC3_open(const char *path, int ioflags, int basepe, size_t *chunksizehintp,
1146 void *parameters, const NC_Dispatch *dispatch, int ncid)
1147 {
1148 int status;
1149 NC3_INFO* nc3 = NULL;
1150 NC *nc;
1151
1152 /* Find NC struct for this file. */
1153 if ((status = NC_check_id(ncid, &nc)))
1154 return status;
1155
1156 /* Create our specific NC3_INFO instance */
1157 nc3 = new_NC3INFO(chunksizehintp);
1158
1159 #if ALWAYS_NC_SHARE /* DEBUG */
1160 fSet(ioflags, NC_SHARE);
1161 #endif
1162
1163 /*
1164 * Only pe 0 is valid.
1165 */
1166 if(basepe != 0) {
1167 if(nc3) {
1168 free(nc3);
1169 nc3 = NULL;
1170 }
1171 status = NC_EINVAL;
1172 goto unwind_alloc;
1173 }
1174
1175 #ifdef ENABLE_BYTERANGE
1176 /* If the model specified the use of byte-ranges, then signal by
1177 a temporary hack using one of the flags in the ioflags.
1178 */
1179 if(NC_testmode(path,"bytes"))
1180 ioflags |= NC_HTTP;
1181 #endif /*ENABLE_BYTERANGE*/
1182
1183 status = ncio_open(path, ioflags, 0, 0, &nc3->chunk, parameters,
1184 &nc3->nciop, NULL);
1185 if(status)
1186 goto unwind_alloc;
1187
1188 assert(nc3->flags == 0);
1189
1190 if(fIsSet(nc3->nciop->ioflags, NC_SHARE))
1191 {
1192 /*
1193 * NC_SHARE implies sync up the number of records as well.
1194 * (File format version one.)
1195 * Note that other header changes are not shared
1196 * automatically. Some sort of IPC (external to this package)
1197 * would be used to trigger a call to nc_sync().
1198 */
1199 fSet(nc3->flags, NC_NSYNC);
1200 }
1201
1202 status = nc_get_NC(nc3);
1203 if(status != NC_NOERR)
1204 goto unwind_ioc;
1205
1206 if(chunksizehintp != NULL)
1207 *chunksizehintp = nc3->chunk;
1208
1209 /* Link nc3 and nc */
1210 NC3_DATA_SET(nc,nc3);
1211 nc->int_ncid = nc3->nciop->fd;
1212
1213 return NC_NOERR;
1214
1215 unwind_ioc:
1216 if(nc3) {
1217 (void) ncio_close(nc3->nciop, 0);
1218 nc3->nciop = NULL;
1219 }
1220 /*FALLTHRU*/
1221 unwind_alloc:
1222 free_NC3INFO(nc3);
1223 if(nc)
1224 NC3_DATA_SET(nc,NULL);
1225 return status;
1226 }
1227
1228 int
NC3__enddef(int ncid,size_t h_minfree,size_t v_align,size_t v_minfree,size_t r_align)1229 NC3__enddef(int ncid,
1230 size_t h_minfree, size_t v_align,
1231 size_t v_minfree, size_t r_align)
1232 {
1233 int status;
1234 NC *nc;
1235 NC3_INFO* nc3;
1236
1237 status = NC_check_id(ncid, &nc);
1238 if(status != NC_NOERR)
1239 return status;
1240 nc3 = NC3_DATA(nc);
1241
1242 if(!NC_indef(nc3))
1243 return(NC_ENOTINDEFINE);
1244
1245 return (NC_endef(nc3, h_minfree, v_align, v_minfree, r_align));
1246 }
1247
1248 /*
1249 * In data mode, same as ncclose.
1250 * In define mode, restore previous definition.
1251 * In create, remove the file.
1252 */
1253 int
NC3_abort(int ncid)1254 NC3_abort(int ncid)
1255 {
1256 int status;
1257 NC *nc;
1258 NC3_INFO* nc3;
1259 int doUnlink = 0;
1260
1261 status = NC_check_id(ncid, &nc);
1262 if(status != NC_NOERR)
1263 return status;
1264 nc3 = NC3_DATA(nc);
1265
1266 doUnlink = NC_IsNew(nc3);
1267
1268 if(nc3->old != NULL)
1269 {
1270 /* a plain redef, not a create */
1271 assert(!NC_IsNew(nc3));
1272 assert(fIsSet(nc3->flags, NC_INDEF));
1273 free_NC3INFO(nc3->old);
1274 nc3->old = NULL;
1275 fClr(nc3->flags, NC_INDEF);
1276 }
1277 else if(!NC_readonly(nc3))
1278 {
1279 status = NC_sync(nc3);
1280 if(status != NC_NOERR)
1281 return status;
1282 }
1283
1284
1285 (void) ncio_close(nc3->nciop, doUnlink);
1286 nc3->nciop = NULL;
1287
1288 free_NC3INFO(nc3);
1289 if(nc)
1290 NC3_DATA_SET(nc,NULL);
1291
1292 return NC_NOERR;
1293 }
1294
1295 int
NC3_close(int ncid,void * params)1296 NC3_close(int ncid, void* params)
1297 {
1298 int status = NC_NOERR;
1299 NC *nc;
1300 NC3_INFO* nc3;
1301
1302 status = NC_check_id(ncid, &nc);
1303 if(status != NC_NOERR)
1304 return status;
1305 nc3 = NC3_DATA(nc);
1306
1307 if(NC_indef(nc3))
1308 {
1309 status = NC_endef(nc3, 0, 1, 0, 1); /* TODO: defaults */
1310 if(status != NC_NOERR )
1311 {
1312 (void) NC3_abort(ncid);
1313 return status;
1314 }
1315 }
1316 else if(!NC_readonly(nc3))
1317 {
1318 status = NC_sync(nc3);
1319 /* flush buffers before any filesize comparisons */
1320 (void) ncio_sync(nc3->nciop);
1321 }
1322
1323 /*
1324 * If file opened for writing and filesize is less than
1325 * what it should be (due to previous use of NOFILL mode),
1326 * pad it to correct size, as reported by NC_calcsize().
1327 */
1328 if (status == NC_NOERR) {
1329 off_t filesize; /* current size of open file */
1330 off_t calcsize; /* calculated file size, from header */
1331 status = ncio_filesize(nc3->nciop, &filesize);
1332 if(status != NC_NOERR)
1333 return status;
1334 status = NC_calcsize(nc3, &calcsize);
1335 if(status != NC_NOERR)
1336 return status;
1337 if(filesize < calcsize && !NC_readonly(nc3)) {
1338 status = ncio_pad_length(nc3->nciop, calcsize);
1339 if(status != NC_NOERR)
1340 return status;
1341 }
1342 }
1343
1344 if(params != NULL && (nc->mode & NC_INMEMORY) != 0) {
1345 NC_memio* memio = (NC_memio*)params;
1346 /* Extract the final memory size &/or contents */
1347 status = memio_extract(nc3->nciop,&memio->size,&memio->memory);
1348 }
1349
1350 (void) ncio_close(nc3->nciop, 0);
1351 nc3->nciop = NULL;
1352
1353 free_NC3INFO(nc3);
1354 NC3_DATA_SET(nc,NULL);
1355
1356 return status;
1357 }
1358
1359 int
NC3_redef(int ncid)1360 NC3_redef(int ncid)
1361 {
1362 int status;
1363 NC *nc;
1364 NC3_INFO* nc3;
1365
1366 status = NC_check_id(ncid, &nc);
1367 if(status != NC_NOERR)
1368 return status;
1369 nc3 = NC3_DATA(nc);
1370
1371 if(NC_readonly(nc3))
1372 return NC_EPERM;
1373
1374 if(NC_indef(nc3))
1375 return NC_EINDEFINE;
1376
1377
1378 if(fIsSet(nc3->nciop->ioflags, NC_SHARE))
1379 {
1380 /* read in from disk */
1381 status = read_NC(nc3);
1382 if(status != NC_NOERR)
1383 return status;
1384 }
1385
1386 nc3->old = dup_NC3INFO(nc3);
1387 if(nc3->old == NULL)
1388 return NC_ENOMEM;
1389
1390 fSet(nc3->flags, NC_INDEF);
1391
1392 return NC_NOERR;
1393 }
1394
1395
1396 int
NC3_inq(int ncid,int * ndimsp,int * nvarsp,int * nattsp,int * xtendimp)1397 NC3_inq(int ncid,
1398 int *ndimsp,
1399 int *nvarsp,
1400 int *nattsp,
1401 int *xtendimp)
1402 {
1403 int status;
1404 NC *nc;
1405 NC3_INFO* nc3;
1406
1407 status = NC_check_id(ncid, &nc);
1408 if(status != NC_NOERR)
1409 return status;
1410 nc3 = NC3_DATA(nc);
1411
1412 if(ndimsp != NULL)
1413 *ndimsp = (int) nc3->dims.nelems;
1414 if(nvarsp != NULL)
1415 *nvarsp = (int) nc3->vars.nelems;
1416 if(nattsp != NULL)
1417 *nattsp = (int) nc3->attrs.nelems;
1418 if(xtendimp != NULL)
1419 *xtendimp = find_NC_Udim(&nc3->dims, NULL);
1420
1421 return NC_NOERR;
1422 }
1423
1424 int
NC3_inq_unlimdim(int ncid,int * xtendimp)1425 NC3_inq_unlimdim(int ncid, int *xtendimp)
1426 {
1427 int status;
1428 NC *nc;
1429 NC3_INFO* nc3;
1430
1431 status = NC_check_id(ncid, &nc);
1432 if(status != NC_NOERR)
1433 return status;
1434 nc3 = NC3_DATA(nc);
1435
1436 if(xtendimp != NULL)
1437 *xtendimp = find_NC_Udim(&nc3->dims, NULL);
1438
1439 return NC_NOERR;
1440 }
1441
1442 int
NC3_sync(int ncid)1443 NC3_sync(int ncid)
1444 {
1445 int status;
1446 NC *nc;
1447 NC3_INFO* nc3;
1448
1449 status = NC_check_id(ncid, &nc);
1450 if(status != NC_NOERR)
1451 return status;
1452 nc3 = NC3_DATA(nc);
1453
1454 if(NC_indef(nc3))
1455 return NC_EINDEFINE;
1456
1457 if(NC_readonly(nc3))
1458 {
1459 return read_NC(nc3);
1460 }
1461 /* else, read/write */
1462
1463 status = NC_sync(nc3);
1464 if(status != NC_NOERR)
1465 return status;
1466
1467 status = ncio_sync(nc3->nciop);
1468 if(status != NC_NOERR)
1469 return status;
1470
1471 #ifdef USE_FSYNC
1472 /* may improve concurrent access, but slows performance if
1473 * called frequently */
1474 #ifndef _WIN32
1475 status = fsync(nc3->nciop->fd);
1476 #else
1477 status = _commit(nc3->nciop->fd);
1478 #endif /* _WIN32 */
1479 #endif /* USE_FSYNC */
1480
1481 return status;
1482 }
1483
1484
1485 int
NC3_set_fill(int ncid,int fillmode,int * old_mode_ptr)1486 NC3_set_fill(int ncid,
1487 int fillmode, int *old_mode_ptr)
1488 {
1489 int i, status;
1490 NC *nc;
1491 NC3_INFO* nc3;
1492 int oldmode;
1493
1494 status = NC_check_id(ncid, &nc);
1495 if(status != NC_NOERR)
1496 return status;
1497 nc3 = NC3_DATA(nc);
1498
1499 if(NC_readonly(nc3))
1500 return NC_EPERM;
1501
1502 oldmode = fIsSet(nc3->flags, NC_NOFILL) ? NC_NOFILL : NC_FILL;
1503
1504 if(fillmode == NC_NOFILL)
1505 {
1506 fSet(nc3->flags, NC_NOFILL);
1507 }
1508 else if(fillmode == NC_FILL)
1509 {
1510 if(fIsSet(nc3->flags, NC_NOFILL))
1511 {
1512 /*
1513 * We are changing back to fill mode
1514 * so do a sync
1515 */
1516 status = NC_sync(nc3);
1517 if(status != NC_NOERR)
1518 return status;
1519 }
1520 fClr(nc3->flags, NC_NOFILL);
1521 }
1522 else
1523 {
1524 return NC_EINVAL; /* Invalid fillmode */
1525 }
1526
1527 if(old_mode_ptr != NULL)
1528 *old_mode_ptr = oldmode;
1529
1530 /* loop thru all variables to set/overwrite its fill mode */
1531 for (i=0; i<nc3->vars.nelems; i++)
1532 nc3->vars.value[i]->no_fill = (fillmode == NC_NOFILL);
1533
1534 /* once the file's fill mode is set, any new variables defined after
1535 * this call will check NC_dofill(nc3) and set their no_fill accordingly.
1536 * See NC3_def_var() */
1537
1538 return NC_NOERR;
1539 }
1540
1541 /**
1542 * Return the file format.
1543 *
1544 * \param ncid the ID of the open file.
1545
1546 * \param formatp a pointer that gets the format. Ignored if NULL.
1547 *
1548 * \returns NC_NOERR No error.
1549 * \returns NC_EBADID Bad ncid.
1550 * \internal
1551 * \author Ed Hartnett, Dennis Heimbigner
1552 */
1553 int
NC3_inq_format(int ncid,int * formatp)1554 NC3_inq_format(int ncid, int *formatp)
1555 {
1556 int status;
1557 NC *nc;
1558 NC3_INFO* nc3;
1559
1560 status = NC_check_id(ncid, &nc);
1561 if(status != NC_NOERR)
1562 return status;
1563 nc3 = NC3_DATA(nc);
1564
1565 /* Why even call this function with no format pointer? */
1566 if (!formatp)
1567 return NC_NOERR;
1568
1569 /* only need to check for netCDF-3 variants, since this is never called for netCDF-4 files */
1570 #ifdef ENABLE_CDF5
1571 if (fIsSet(nc3->flags, NC_64BIT_DATA))
1572 *formatp = NC_FORMAT_CDF5;
1573 else
1574 #endif
1575 if (fIsSet(nc3->flags, NC_64BIT_OFFSET))
1576 *formatp = NC_FORMAT_64BIT_OFFSET;
1577 else
1578 *formatp = NC_FORMAT_CLASSIC;
1579 return NC_NOERR;
1580 }
1581
1582 /**
1583 * Return the extended format (i.e. the dispatch model), plus the mode
1584 * associated with an open file.
1585 *
1586 * \param ncid the ID of the open file.
1587 * \param formatp a pointer that gets the extended format. Note that
1588 * this is not the same as the format provided by nc_inq_format(). The
1589 * extended format indicates the dispatch layer model. Classic, 64-bit
1590 * offset, and CDF5 files all have an extended format of
1591 * ::NC_FORMATX_NC3. Ignored if NULL.
1592 * \param modep a pointer that gets the open/create mode associated with
1593 * this file. Ignored if NULL.
1594 *
1595 * \returns NC_NOERR No error.
1596 * \returns NC_EBADID Bad ncid.
1597 * \internal
1598 * \author Dennis Heimbigner
1599 */
1600 int
NC3_inq_format_extended(int ncid,int * formatp,int * modep)1601 NC3_inq_format_extended(int ncid, int *formatp, int *modep)
1602 {
1603 int status;
1604 NC *nc;
1605
1606 status = NC_check_id(ncid, &nc);
1607 if(status != NC_NOERR)
1608 return status;
1609 if(formatp) *formatp = NC_FORMATX_NC3;
1610 if(modep) *modep = nc->mode;
1611 return NC_NOERR;
1612 }
1613
1614 /**
1615 * Determine name and size of netCDF type. This netCDF-4 function
1616 * proved so popular that a netCDF-classic version is provided. You're
1617 * welcome.
1618 *
1619 * \param ncid The ID of an open file.
1620 * \param typeid The ID of a netCDF type.
1621 * \param name Pointer that will get the name of the type. Maximum
1622 * size will be NC_MAX_NAME. Ignored if NULL.
1623 * \param size Pointer that will get size of type in bytes. Ignored if
1624 * null.
1625 *
1626 * \returns NC_NOERR No error.
1627 * \returns NC_EBADID Bad ncid.
1628 * \returns NC_EBADTYPE Bad typeid.
1629 * \internal
1630 * \author Ed Hartnett
1631 */
1632 int
NC3_inq_type(int ncid,nc_type typeid,char * name,size_t * size)1633 NC3_inq_type(int ncid, nc_type typeid, char *name, size_t *size)
1634 {
1635 NC *ncp;
1636 int stat = NC_check_id(ncid, &ncp);
1637 if (stat != NC_NOERR)
1638 return stat;
1639
1640 if(typeid < NC_BYTE || typeid > NC_STRING)
1641 return NC_EBADTYPE;
1642
1643 /* Give the user the values they want. */
1644 if (name)
1645 strcpy(name, NC_atomictypename(typeid));
1646 if (size)
1647 *size = NC_atomictypelen(typeid);
1648
1649 return NC_NOERR;
1650 }
1651
1652 /**
1653 * This is an obsolete form of nc_delete(), supported for backwards
1654 * compatibility.
1655 *
1656 * @param path Filename to delete.
1657 * @param basepe Must be 0.
1658 *
1659 * @return ::NC_NOERR No error.
1660 * @return ::NC_EIO Couldn't delete file.
1661 * @return ::NC_EINVAL Invaliod basepe. Must be 0.
1662 * @author Glenn Davis, Ed Hartnett
1663 */
1664 int
nc_delete_mp(const char * path,int basepe)1665 nc_delete_mp(const char * path, int basepe)
1666 {
1667 NC *nc;
1668 int status;
1669 int ncid;
1670
1671 status = nc_open(path,NC_NOWRITE,&ncid);
1672 if(status) return status;
1673
1674 status = NC_check_id(ncid,&nc);
1675 if(status) return status;
1676
1677 /*
1678 * Only pe 0 is valid.
1679 */
1680 if(basepe != 0)
1681 return NC_EINVAL;
1682
1683 (void) nc_close(ncid);
1684 if(unlink(path) == -1) {
1685 return NC_EIO; /* No more specific error code is appropriate */
1686 }
1687 return NC_NOERR;
1688 }
1689
1690 int
nc_delete(const char * path)1691 nc_delete(const char * path)
1692 {
1693 return nc_delete_mp(path, 0);
1694 }
1695
1696 /*----< NC3_inq_default_fill_value() >---------------------------------------*/
1697 /* copy the default fill value to the memory space pointed by fillp */
1698 int
NC3_inq_default_fill_value(int xtype,void * fillp)1699 NC3_inq_default_fill_value(int xtype, void *fillp)
1700 {
1701 if (fillp == NULL) return NC_NOERR;
1702
1703 switch(xtype) {
1704 case NC_CHAR : *(char*)fillp = NC_FILL_CHAR; break;
1705 case NC_BYTE : *(signed char*)fillp = NC_FILL_BYTE; break;
1706 case NC_SHORT : *(short*)fillp = NC_FILL_SHORT; break;
1707 case NC_INT : *(int*)fillp = NC_FILL_INT; break;
1708 case NC_FLOAT : *(float*)fillp = NC_FILL_FLOAT; break;
1709 case NC_DOUBLE : *(double*)fillp = NC_FILL_DOUBLE; break;
1710 case NC_UBYTE : *(unsigned char*)fillp = NC_FILL_UBYTE; break;
1711 case NC_USHORT : *(unsigned short*)fillp = NC_FILL_USHORT; break;
1712 case NC_UINT : *(unsigned int*)fillp = NC_FILL_UINT; break;
1713 case NC_INT64 : *(long long*)fillp = NC_FILL_INT64; break;
1714 case NC_UINT64 : *(unsigned long long*)fillp = NC_FILL_UINT64; break;
1715 default : return NC_EBADTYPE;
1716 }
1717 return NC_NOERR;
1718 }
1719
1720
1721 /*----< NC3_inq_var_fill() >-------------------------------------------------*/
1722 /* inquire the fill value of a variable */
1723 int
NC3_inq_var_fill(const NC_var * varp,void * fill_value)1724 NC3_inq_var_fill(const NC_var *varp, void *fill_value)
1725 {
1726 NC_attr **attrpp = NULL;
1727
1728 if (fill_value == NULL) return NC_EINVAL;
1729
1730 /*
1731 * find fill value
1732 */
1733 attrpp = NC_findattr(&varp->attrs, _FillValue);
1734 if ( attrpp != NULL ) {
1735 const void *xp;
1736 /* User defined fill value */
1737 if ( (*attrpp)->type != varp->type || (*attrpp)->nelems != 1 )
1738 return NC_EBADTYPE;
1739
1740 xp = (*attrpp)->xvalue;
1741 /* value stored in xvalue is in external representation, may need byte-swap */
1742 switch(varp->type) {
1743 case NC_CHAR: return ncx_getn_text (&xp, 1, (char*)fill_value);
1744 case NC_BYTE: return ncx_getn_schar_schar (&xp, 1, (signed char*)fill_value);
1745 case NC_UBYTE: return ncx_getn_uchar_uchar (&xp, 1, (unsigned char*)fill_value);
1746 case NC_SHORT: return ncx_getn_short_short (&xp, 1, (short*)fill_value);
1747 case NC_USHORT: return ncx_getn_ushort_ushort (&xp, 1, (unsigned short*)fill_value);
1748 case NC_INT: return ncx_getn_int_int (&xp, 1, (int*)fill_value);
1749 case NC_UINT: return ncx_getn_uint_uint (&xp, 1, (unsigned int*)fill_value);
1750 case NC_FLOAT: return ncx_getn_float_float (&xp, 1, (float*)fill_value);
1751 case NC_DOUBLE: return ncx_getn_double_double (&xp, 1, (double*)fill_value);
1752 case NC_INT64: return ncx_getn_longlong_longlong (&xp, 1, (long long*)fill_value);
1753 case NC_UINT64: return ncx_getn_ulonglong_ulonglong(&xp, 1, (unsigned long long*)fill_value);
1754 default: return NC_EBADTYPE;
1755 }
1756 }
1757 else {
1758 /* use the default */
1759 switch(varp->type){
1760 case NC_CHAR: *(char *)fill_value = NC_FILL_CHAR;
1761 break;
1762 case NC_BYTE: *(signed char *)fill_value = NC_FILL_BYTE;
1763 break;
1764 case NC_SHORT: *(short *)fill_value = NC_FILL_SHORT;
1765 break;
1766 case NC_INT: *(int *)fill_value = NC_FILL_INT;
1767 break;
1768 case NC_UBYTE: *(unsigned char *)fill_value = NC_FILL_UBYTE;
1769 break;
1770 case NC_USHORT: *(unsigned short *)fill_value = NC_FILL_USHORT;
1771 break;
1772 case NC_UINT: *(unsigned int *)fill_value = NC_FILL_UINT;
1773 break;
1774 case NC_INT64: *(long long *)fill_value = NC_FILL_INT64;
1775 break;
1776 case NC_UINT64: *(unsigned long long *)fill_value = NC_FILL_UINT64;
1777 break;
1778 case NC_FLOAT: *(float *)fill_value = NC_FILL_FLOAT;
1779 break;
1780 case NC_DOUBLE: *(double *)fill_value = NC_FILL_DOUBLE;
1781 break;
1782 default:
1783 return NC_EINVAL;
1784 }
1785 }
1786 return NC_NOERR;
1787 }
1788