1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2  * Copyright 1993, University Corporation for Atmospheric Research           *
3  * See netcdf/COPYRIGHT file for copying and redistribution conditions.      *
4  *                                                                           *
5  * Copyright by The HDF Group.                                               *
6  * Copyright by the Board of Trustees of the University of Illinois.         *
7  * All rights reserved.                                                      *
8  *                                                                           *
9  * This file is part of HDF.  The full HDF copyright notice, including       *
10  * terms governing use, modification, and redistribution, is contained in    *
11  * the COPYING file, which can be found at the root of the source code       *
12  * distribution tree, or in https://support.hdfgroup.org/ftp/HDF/releases/.  *
13  * If you do not have access to either file, you may request a copy from     *
14  * help@hdfgroup.org.                                                        *
15  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16 
17 /*    $Id$ */
18 
19 #ifdef DEBUG
20 #include <assert.h>
21 #endif /* DEBUG */
22 
23 #include    <string.h>
24 #include    <errno.h>
25 #include    <unistd.h> /* for access() */
26 #include    "local_nc.h"
27 #include    "alloc.h"
28 #include    "herr.h"
29 
30 /* obtain the maximum number of open files allowed, at the same time,
31    on the current system */
32 #if defined _WIN32
33 #define MAX_SYS_OPENFILES    _getmaxstdio()
34 #else
35 #include <sys/resource.h>
36 struct rlimit rlim;
37 #define MAX_SYS_OPENFILES (                                     \
38         getrlimit((RLIMIT_NOFILE), (&rlim)),                    \
39         rlim.rlim_cur)
40 #endif
41 
42 /* Maximum number of files can be opened at one time; subtract 3 from
43    the system allowed to account for stdin, stdout, and stderr */
44 /* On AIX 6.1 system the limit is 2GB-1; it caused our library to choke.
45    For now we will use a cap H4_MAX_AVAIL_OPENFILES on the maximum number
46    of files can be open at one time. This limit should probably
47    be in hlimits.h file in the future. EIP 2010-02-01*/
48 
49 #define H4_MAX_AVAIL_OPENFILES 20000
50 #define MAX_AVAIL_OPENFILES  (((MAX_SYS_OPENFILES - 3) > H4_MAX_AVAIL_OPENFILES) ? H4_MAX_AVAIL_OPENFILES : (MAX_SYS_OPENFILES - 3))
51 
52 static int _curr_opened = 0 ; /* the number of files currently opened */
53 /* NOTE: _ncdf might have been the number of files currently opened, yet it
54    is not decremented when ANY file is closed but only when the file that
55    has the same index as _ncdf-1 is closed.  Thus, it indicates the last
56    index in _cdfs intead of the number of files currently opened.  So, I
57    added _curr_opened to keep track of the number of files currently opened.
58    QAK suggested to use atom as in other interfaces and that would eliminate
59    similar issues.  - BMR - 11/03/07 */
60 static int _ncdf = 0 ; /*  high water mark on open cdf's */
61 static NC **_cdfs;
62 
63 #define HNDLE(id) (((id) >= 0 && (id) < _ncdf) ? _cdfs[(id)] : NULL)
64 #define STASH(id) (((id) >= 0 && (id) < _ncdf) ?  \
65         HNDLE(_cdfs[(id)]->redefid) : NULL)
66 
67 #ifdef NO_STDC_REMOVE
68 /* try unix 'unlink' */
69 #define remove(fpath) unlink((fpath))
70 #endif
71 
72 #ifdef DOS_FS
73 #define SEP '\\' /* this separates path components on DOS */
74 #endif
75 #ifndef SEP
76 #define SEP '/' /* default, unix */
77 #endif
78 
79 static intn max_NC_open = H4_MAX_NC_OPEN;    /* current netCDF default */
80 
81 /*
82  * Resets _cdfs
83  */
84 static void
ncreset_cdflist()85 ncreset_cdflist()
86 {
87     if (_cdfs != NULL)
88     {
89     HDfree((VOIDP)_cdfs);
90     _cdfs = NULL;
91     }
92 }
93 
94 /*
95  *  Allocates _cdfs and returns the allocated size if succeeds;
96  *  otherwise return FAIL(-1).
97  */
98 intn
NC_reset_maxopenfiles(req_max)99 NC_reset_maxopenfiles(req_max)
100 intn req_max;    /* requested max to allocate */
101 {
102     intn sys_limit = MAX_AVAIL_OPENFILES;
103     intn alloc_size;
104     NC **newlist;
105     intn i;
106     int ret_value = SUCCEED;
107 
108         /* Verify arguments */
109         if (req_max < 0)
110         {
111             NCadvise(NC_EINVAL, "Invalid request: %d for maximum files", req_max);
112             HGOTO_DONE(-1) ;
113         }
114 
115 
116     /* If requested max is 0, allocate _cdfs with the default,
117     max_NC_open, if _cdfs is not yet allocated, otherwise, keep
118     _cdfs as is and return the current max */
119     if (req_max == 0)
120     {
121         if (!_cdfs)
122         {
123         _cdfs = (NC **)HDmalloc(sizeof(NC *) * (max_NC_open));
124 
125         /* If allocation fails, return 0 for no allocation */
126         if (_cdfs == NULL)
127         {
128             /* NC_EINVAL is Invalid Argument, but must decide if
129             we just want to return 0 without error or not */
130             NCadvise(NC_EINVAL, "Unable to allocate a cdf list of %d elements", max_NC_open);
131             HGOTO_DONE(-1) ;
132         }
133         else
134             HGOTO_DONE(max_NC_open);
135         }
136         else  /* return the current limit */
137         HGOTO_DONE (max_NC_open);
138     } /* if req_max == 0 */
139 
140     /* If the requested max is less than the current max but there are
141     more than the requested max number of files opened, do not reset
142     the current max, since this will cause information lost. */
143     if (req_max < max_NC_open && req_max <= _ncdf)
144         HGOTO_DONE(max_NC_open);
145 
146     /* If the requested max exceeds system limit, only allocate up
147     to system limit */
148     if (req_max > sys_limit)
149         alloc_size = sys_limit;
150     else
151         alloc_size = req_max;
152 
153     /* Allocate a new list */
154     newlist = (NC **)HDmalloc(sizeof(NC *) * alloc_size);
155 
156     /* If allocation fails, return 0 for no allocation */
157     if (newlist == NULL)
158     {
159         /* NC_EINVAL is Invalid Argument, but must decide if
160         we just want to return 0 without error or not */
161         NCadvise(NC_EINVAL, "Unable to allocate a cdf list of %d elements", alloc_size) ;
162         HGOTO_DONE(-1) ;
163     }
164 
165     /* If _cdfs is already allocated, transfer pointers over to the
166     new list and deallocate the old list of pointers */
167     if (_cdfs != NULL)
168     {
169         for (i=0; i < _ncdf; i++)
170         newlist[i] = _cdfs[i];
171         HDfree(_cdfs);
172     }
173 
174     /* Set _cdfs to the new list */
175     _cdfs = newlist;
176     newlist = NULL;
177 
178     /* Reset current max files opened allowed in HDF to the new max */
179     max_NC_open = alloc_size;
180 
181     HGOTO_DONE(max_NC_open);
182 
183 done:
184     if (ret_value == FAIL)
185       { /* Failure cleanup */
186     /* Nothing yet. */
187       }
188      /* Normal cleanup */
189 
190     return ret_value;
191 } /* NC_reset_maxopenfiles */
192 
193 /*
194  *  Returns the current # of open files allowed
195  */
196 intn
NC_get_maxopenfiles()197 NC_get_maxopenfiles()
198 {
199     return(max_NC_open);
200 } /* NC_get_maxopenfiles */
201 
202 /*
203  *  Returns the maximum number of open files the system allows.
204  */
205 intn
NC_get_systemlimit()206 NC_get_systemlimit()
207 {
208     return(MAX_AVAIL_OPENFILES);
209 } /* NC_get_systemlimit */
210 
211 /*
212  *  Returns the number of files currently being opened.
213  */
214 int
NC_get_numopencdfs()215 NC_get_numopencdfs()
216 {
217     return(_curr_opened);
218 } /* NC_get_numopencdfs */
219 
220 /*
221  *  Check validity of cdf handle, return pointer to NC struct or
222  * NULL on error.
223  */
224 NC *
NC_check_id(cdfid)225 NC_check_id(cdfid)
226 int cdfid ;
227 {
228     NC *handle ;
229 
230     handle = ( cdfid >= 0 && cdfid < _ncdf) ? _cdfs[cdfid] : NULL ;
231     if(handle == NULL)
232     {
233         NCadvise(NC_EBADID, "%d is not a valid cdfid", cdfid) ;
234         return(NULL) ;
235     }
236     return(handle) ;
237 }
238 
239 
240 /*
241  *  Check to see if in define mode.
242  * If 'iserr' arg is true, advise.
243  */
244 bool_t
NC_indefine(cdfid,iserr)245 NC_indefine(cdfid, iserr) /* Should be a Macro ? */
246 int cdfid ;
247 bool_t iserr ;
248 {
249     bool_t ret  ;
250     ret = (cdfid >= 0 && cdfid < _ncdf) ?
251         (bool_t)(_cdfs[cdfid]->flags & NC_INDEF) : FALSE ;
252     if(!ret && iserr)
253     {
254         if(cdfid < 0 || cdfid >= _ncdf)
255             NCadvise(NC_EBADID, "%d is not a valid cdfid", cdfid) ;
256         else
257             NCadvise(NC_ENOTINDEFINE, "%s Not in define mode",
258                 _cdfs[cdfid]->path) ;
259     }
260     return(ret) ;
261 }
262 
263 
264 /*
265  *  Common code for ncopen and nccreate.
266  */
267 static int
NC_open(path,mode)268 NC_open(path, mode )
269 const char    *path ;    /* file name */
270 int mode ;
271 {
272     NC *handle ;
273     int cdfid;
274     intn cdfs_size;
275 
276     /* Allocate _cdfs, if it is already allocated, nothing will be done */
277     if (_cdfs == NULL){
278         if (FAIL == (cdfs_size = NC_reset_maxopenfiles(0)))
279         {
280         NCadvise(NC_ENFILE, "Could not reset max open files limit");
281         return(-1);
282         }
283     }
284 
285     /* find first available id */
286     for(cdfid = 0 ; cdfid < _ncdf; cdfid++)
287         if( _cdfs[cdfid] == NULL) break ;
288 
289     /* if application attempts to open more files than the current max
290     allows, increase the current max to the system limit, if it's
291     not at the system limit yet */
292     if(cdfid == _ncdf && _ncdf >= max_NC_open)
293     {
294         /* if the current max already reaches the system limit, fail */
295         if (max_NC_open == MAX_AVAIL_OPENFILES)
296         {
297         NCadvise(NC_ENFILE, "maximum number of open cdfs allowed already reaches system limit %d", MAX_AVAIL_OPENFILES) ;
298         return(-1);
299         }
300         /* otherwise, increase the current max to the system limit */
301         if (FAIL == NC_reset_maxopenfiles(MAX_AVAIL_OPENFILES))
302         {
303         NCadvise(NC_ENFILE, "Could not reset max open files limit");
304         return(-1);
305         }
306     }
307 
308     handle = NC_new_cdf(path, mode) ;
309     if( handle == NULL)
310     {
311     /* if the failure was due to "too many open files," simply return */
312         if(errno == EMFILE)
313         {
314             nc_serror("maximum number of open files allowed has been reached\"%s\"", path) ;
315             return(-1);
316         }
317 
318         if((mode & 0x0f) == NC_CLOBBER)
319         {
320         /* only attempt to remove the file if it's not currently
321         in use - bugzilla #376 */
322             if(!HPisfile_in_use(path))
323                 if( remove(path) != 0 )
324                     nc_serror("couldn't remove filename \"%s\"", path) ;
325         }
326         return(-1) ;
327     }
328 
329     (void) strncpy(handle->path, path, FILENAME_MAX) ;
330     _cdfs[cdfid] = handle ;
331     if(cdfid == _ncdf)
332         _ncdf++ ;
333     _curr_opened++;
334     return(cdfid) ;
335 }   /* NC_open */
336 
337 
nccreate(path,cmode)338 int nccreate(path, cmode)
339 const char    *path ;    /* file name */
340 int         cmode ;
341 {
342     cdf_routine_name = "nccreate" ;
343 
344     if(cmode & NC_CREAT)
345     {
346         return(NC_open(path, cmode)) ;
347     }
348     NCadvise(NC_EINVAL, "Bad Flag") ;
349     return(-1) ;
350 }
351 
352 
ncopen(path,mode)353 int ncopen(path,mode)
354 const char    *path ;    /* file name */
355 int         mode ;
356 {
357     cdf_routine_name = "ncopen" ;
358     if(mode & NC_CREAT)
359     {
360         NCadvise(NC_EINVAL, "Bad Flag") ;
361         return(-1) ;
362     }
363     return(NC_open(path, mode)) ;
364 }
365 
366 
ncsync(cdfid)367 int ncsync(cdfid)
368 int cdfid ;
369 {
370     NC *handle ;
371 
372     cdf_routine_name = "ncsync" ;
373 
374     handle = NC_check_id(cdfid) ;
375     if(handle == NULL)
376         return(-1) ;
377 
378     if( handle->flags & NC_INDEF )
379       {
380           NCadvise(NC_EINDEFINE, "Unfinished definition") ;
381           return(-1) ;
382       }
383 
384     if(handle->flags & NC_RDWR)
385       {
386           handle->xdrs->x_op = XDR_ENCODE ;
387           if(handle->flags & NC_HDIRTY)
388             {
389                 if(!xdr_cdf(handle->xdrs, &handle) )
390                     return(-1) ;
391                 handle->flags &= ~(NC_NDIRTY | NC_HDIRTY) ;
392             }
393           else if(handle->flags & NC_NDIRTY)
394             {
395                 if(!xdr_numrecs(handle->xdrs, handle) )
396                     return(-1) ;
397 #ifdef HDF
398                 if (handle->file_type != HDF_FILE)
399 #endif
400                     handle->flags &= ~(NC_NDIRTY) ;
401             }
402       }
403     else /* read only */
404       {
405           /* assert(handle->xdrs->x_op == XDR_DECODE) ; */
406           /* free the stuff in handle that xdr_cdf allocates */
407           handle->xdrs->x_op = XDR_FREE ;
408           (void) xdr_cdf(handle->xdrs, &handle) ;
409           handle->xdrs->x_op = XDR_DECODE ;
410 
411           if(!xdr_cdf(handle->xdrs, &handle) )
412             {
413                 nc_serror("xdr_cdf") ;
414                 NC_free_cdf(handle) ; /* ?? what should we do now? */
415 
416 #if 0 /* not sure if we need this here, will check again - 1/26/08 BMR */
417         /* if the _cdf list is empty, deallocate and reset it to NULL */
418         if (_ncdf == 0)
419             ncreset_cdflist();
420 #endif
421                 return(-1) ;
422             }
423           if( NC_computeshapes(handle) == -1)
424               return(-1) ;
425       }
426 
427     (void) NCxdrfile_sync(handle->xdrs) ;
428 
429     return(0) ;
430 }
431 
432 
433 /*
434  *  In data mode, same as ncclose ;
435  * In define mode, restore previous definition ;
436  * In create, remove the file ;
437  */
ncabort(cdfid)438 int ncabort(cdfid)
439 int cdfid ;
440 {
441     NC *handle ;
442     char path[FILENAME_MAX + 1] ;
443     unsigned flags ;
444 #ifdef HDF
445     intn   file_type;
446 #endif
447 
448     cdf_routine_name = "ncabort" ;
449 
450 
451     handle = NC_check_id(cdfid) ;
452     if(handle == NULL)
453         return(-1) ;
454 
455     flags = handle->flags ; /* need to save past free_cdf */
456 
457     /* NC_CREAT implies NC_INDEF, in both cases need to remove handle->path */
458     if(flags & (NC_INDEF | NC_CREAT))
459         {
460           (void)strncpy(path, handle->path, FILENAME_MAX) ; /* stash path */
461           if(!(flags & NC_CREAT)) /* redef */
462             {
463                 NC_free_cdf(STASH(cdfid)) ;
464 
465                 _cdfs[handle->redefid] = NULL ;
466                 if(handle->redefid == _ncdf - 1)
467                     _ncdf-- ;
468                 handle->redefid = -1 ;
469         _curr_opened--;    /* one less file currently opened */
470 
471         /* if the _cdf list is empty, deallocate and reset it to NULL */
472         if (_ncdf == 0)
473             ncreset_cdflist();
474             }
475         }
476     else if(handle->flags & NC_RDWR)
477         {
478           handle->xdrs->x_op = XDR_ENCODE ;
479           if(handle->flags & NC_HDIRTY)
480             {
481                 if(!xdr_cdf(handle->xdrs, &handle) )
482                     return(-1) ;
483             }
484           else if(handle->flags & NC_NDIRTY)
485             {
486                 if(!xdr_numrecs(handle->xdrs, handle) )
487                     return(-1) ;
488             }
489         }
490 
491 #ifdef HDF
492     file_type = handle->file_type;
493 #endif
494     NC_free_cdf(handle) ; /* calls fclose */
495 
496 #ifdef HDF
497     switch(file_type)
498       {
499       case netCDF_FILE:
500           if(flags & (NC_INDEF | NC_CREAT))
501             {
502                 if( remove(path) != 0 )
503                     nc_serror("couldn't remove filename \"%s\"", path) ;
504             }
505           break;
506       case HDF_FILE:
507           if(flags & NC_CREAT)
508             {
509                 if( remove(path) != 0 )
510                     nc_serror("couldn't remove filename \"%s\"", path) ;
511             }
512           break;
513       }
514 #else
515     if(flags & (NC_INDEF | NC_CREAT))
516       {
517           if( remove(path) != 0 )
518               nc_serror("couldn't remove filename \"%s\"", path) ;
519       }
520 #endif
521 
522     _cdfs[cdfid] = NULL ; /* reset pointer */
523 
524     /* if current file is at the top of the list, adjust the water mark */
525     if(cdfid == _ncdf - 1)
526         _ncdf-- ;
527     _curr_opened--;    /* one less file currently being opened */
528 
529     /* if the _cdf list is empty, deallocate and reset it to NULL */
530     if (_ncdf == 0)
531         ncreset_cdflist();
532 
533     return(0) ;
534 } /* ncabort */
535 
536 
537 /*
538  * Deprecated function ;
539  */
ncnobuf(cdfid)540 int ncnobuf(cdfid)
541 int cdfid ;
542 {
543     NC *handle ;
544 
545     cdf_routine_name = "ncnobuf" ;
546 
547     handle = NC_check_id(cdfid) ;
548     if(handle == NULL)
549         return(-1) ;
550 /* NOOP */
551     return(0);
552 }
553 
554 
555 /*
556  * Given the path to a file "proto",
557  * we replace the filename component with
558  * a name like one would get from tmpnam(3S).
559  * (Many implementations of tmpnam insist on giving us
560  * a directory like /usr/tmp as well. Since we are making a copy which we
561  * will eventually rename() back to proto, we want the return of NCtempname
562  * and proto to dwell on the same filesystem.)
563  */
564 static char *
NCtempname(proto)565 NCtempname(proto)
566 const char *proto ;
567 {
568 /* NO_ACCESS defined if the OS lacks the access() function */
569 #ifndef NO_ACCESS
570 #    define TN_NACCES 1
571 #else
572 #    define TN_NACCES 0
573 #endif /* !NO_ACCESS */
574 /* NO_GETPID defined if the OS lacks the getpid() function */
575 #ifndef NO_GETPID
576 #    define TN_NDIGITS 4
577 #if defined _WIN32
578     typedef int pid_t;
579 #endif
580     pid_t getpid(void);
581     unsigned int pid ; /* OS/2 DOS (MicroSoft Lib) allows "negative" int pids */
582 #else
583 #    define TN_NDIGITS 0
584 #endif /* !NO_GETPID */
585 
586     static char seed[] = {'a','a','a', '\0'} ;
587 #define TN_NSEED (sizeof(seed) -1)
588     static char tnbuf[FILENAME_MAX +1] ;
589     char *begin, *cp, *sp ;
590 
591     /* assert(TN_NSEED > 0) ; */
592     strcpy(tnbuf,proto) ;
593 
594 #ifdef SEP
595     if ((begin = strrchr(tnbuf, SEP)) == NULL)
596         begin = tnbuf ;
597     else
598         begin++ ;
599 
600     if(&tnbuf[FILENAME_MAX] - begin <= TN_NSEED + TN_NACCES + TN_NDIGITS)
601       {
602           /* not big enough */
603           tnbuf[0] = '\0' ;
604           return tnbuf ;
605       }
606 #else
607     begin = tnbuf ;
608 #endif /* SEP */
609 
610     *begin = '\0' ;
611     (void) strcat(begin, seed) ;
612 
613     cp = begin + TN_NSEED + TN_NACCES + TN_NDIGITS ;
614 #ifndef NO_GETPID
615     *cp = '\0' ;
616     pid = getpid() ;
617     while(--cp >= begin + TN_NSEED + TN_NACCES)
618       {
619           *cp = (pid % 10) + '0' ;
620           pid /= 10 ;
621       }
622 #else
623     *cp-- = '\0' ;
624 #endif /* !NO_GETPID */
625 
626     /* update seed for next call */
627     sp = seed ;
628     while(*sp == 'z')
629         *sp++ = 'a' ;
630     if(*sp != '\0')
631         ++*sp ;
632 
633 #ifndef NO_ACCESS
634     for(*cp = 'a' ; access(tnbuf, 0) == 0 ; )
635       {
636           if(++*cp > 'z')
637             {
638                 /* ran out of tries */
639                 tnbuf[0] = '\0' ;
640                 return tnbuf ;
641             }
642       }
643 #endif /* !NO_ACCESS */
644 
645     return tnbuf ;
646 }
647 
648 
ncredef(cdfid)649 int ncredef(cdfid)
650 int cdfid ;
651 {
652     NC *handle ;
653     NC *new ;
654     int id ;
655     char *scratchfile ;
656 
657     cdf_routine_name = "ncredef" ;
658 
659     handle = NC_check_id(cdfid) ;
660     if(handle == NULL)
661         return(-1) ;
662     if( handle->flags & NC_INDEF) /* in define mode already */
663       {
664           NC *stash = STASH(cdfid) ;
665           if(stash) NCadvise(NC_EINDEFINE, "%s: in define mode aleady",
666                              stash->path) ;
667           return(-1) ;
668       }
669     if(!(handle->flags & NC_RDWR))
670       {
671           NCadvise(NC_EPERM, "%s: NC_NOWRITE", handle->path) ;
672           return(-1) ;
673       }
674 
675 
676 #ifdef HDF
677     if(handle->file_type == HDF_FILE)
678       {
679           handle->flags |= NC_INDEF ;
680           handle->redefid = TRUE;
681           return(0);
682       }
683 #endif
684 
685     /* find first available id */
686     for(id = 0 ; id < _ncdf; id++)
687         if( _cdfs[id] == NULL) break ;
688 
689     if(id == _ncdf && _ncdf >= max_NC_open) /* will need a new one */
690       {
691           NCadvise(NC_ENFILE, "maximum number of open cdfs %d exceeded",
692                    _ncdf) ;
693           return(-1) ;
694       }
695 
696     if( ncopts & NC_NOFILL )
697       {
698           /* fill last record */
699           handle->xdrs->x_op = XDR_ENCODE ;
700           if(handle->flags & NC_NDIRTY)
701             {
702                 if(!xdr_numrecs(handle->xdrs, handle) )
703                     return(-1) ;
704                 handle->flags &= ~(NC_NDIRTY) ;
705             }
706       }
707 
708     scratchfile = NCtempname(handle->path) ;
709 
710     new = NC_dup_cdf(scratchfile, NC_NOCLOBBER, handle) ;
711     if(new == NULL)
712       {
713           return(-1) ;
714       }
715 
716     handle->flags |= NC_INDEF ;
717     (void) strncpy(new->path, scratchfile, FILENAME_MAX) ;
718 
719     /* put the old handle in the new id */
720     _cdfs[id] = handle ;
721     if(id == _ncdf)
722         _ncdf++ ;
723     _curr_opened++;
724 
725     /* put the new handle in old id */
726     _cdfs[cdfid] = new ;
727 
728     new->redefid = id ;
729 
730     return(0) ;
731 }
732 
733 
734 /*
735  * Compute offsets and put into the header
736  */
737 static void
NC_begins(handle)738 NC_begins(handle)
739 NC *handle ;
740 {
741     unsigned ii ;
742     u_long index = 0 ;
743     NC_var **vpp ;
744     NC_var *last = NULL ;
745 
746     if(handle->vars == NULL)
747         return ;
748 
749     index = NC_xlen_cdf(handle) ;
750 
751     /* loop thru vars, first pass is for the 'non-record' vars */
752     vpp = (NC_var **)handle->vars->values ;
753     for(ii = 0 ; ii < handle->vars->count ; ii++, vpp++)
754       {
755           if( IS_RECVAR(*vpp) )
756             {
757                 continue ;    /* skip record variables on this pass */
758             }
759 
760           (*vpp)->begin = index ;
761           index += (*vpp)->len ;
762 #ifdef EDEBUG
763           NCadvise(NC_NOERR, "%s pass 1 begin %d, length %d",
764                    (*vpp)->name->values, (*vpp)->begin, (*vpp)->len) ;
765 #endif /* EDEBUG */
766       }
767 
768     handle->begin_rec = index ;
769     handle->recsize = 0 ;
770 
771     /* loop thru vars, second pass is for the 'non-record' vars */
772     vpp = (NC_var **)handle->vars->values ;
773     for(ii = 0 ; ii < handle->vars->count ; ii++, vpp++)
774       {
775           if( !IS_RECVAR(*vpp) )
776             {
777                 continue ;    /* skip non-record variables on this pass */
778             }
779 
780           (*vpp)->begin = index ;
781 #ifdef EDEBUG
782           NCadvise(NC_NOERR, "%s pass 2 begin %d, len %d, *dsizes %d",
783                    (*vpp)->name->values, (*vpp)->begin, index, (*vpp)->len, *(*vpp)->dsizes ) ;
784 #endif /* EDEBUG */
785           index += (*vpp)->len ;
786           handle->recsize += (*vpp)->len ;
787           last = (*vpp) ;
788       }
789     /*
790     * for special case of exactly one record variable, pack values
791     */
792     if(last != NULL && handle->recsize == last->len)
793         handle->recsize = *last->dsizes ;
794     handle->numrecs = 0 ;
795 }
796 
797 
798 /*
799  * Copy nbytes bytes from source to target.
800  * Streams target and source should be positioned before the call.
801  * opaque I/O, no XDR conversion performed (or needed).
802  * The Macros XDR_GETBYTES and XDR_PUTBYTES may not be
803  * supported on your xdr implementation. If not, calls
804  * to xdr_opaque may be used.
805  */
806 bool_t
NC_dcpy(target,source,nbytes)807 NC_dcpy( target, source, nbytes)
808 XDR *target ;
809 XDR *source ;
810 long nbytes ;
811 {
812 /* you may wish to tune this: big on a cray, small on a PC? */
813 #define NC_DCP_BUFSIZE 8192
814     char buf[NC_DCP_BUFSIZE] ;
815 
816     while(nbytes > sizeof(buf))
817     {
818         if(!XDR_GETBYTES(source, buf, sizeof(buf)))
819             goto err ;
820         if(!XDR_PUTBYTES(target, buf, sizeof(buf)))
821             goto err ;
822         nbytes -= sizeof(buf) ;
823     }
824     /* we know nbytes <= sizeof(buf) at this point */
825     if(!XDR_GETBYTES(source, buf, nbytes))
826         goto err ;
827     if(!XDR_PUTBYTES(target, buf, nbytes))
828         goto err ;
829     return(TRUE) ;
830 err:
831     NCadvise(NC_EXDR, "NC_dcpy") ;
832     return(FALSE) ;
833 }
834 
835 
836 /*
837  * XDR the data of varid in old, target is the new xdr strm
838  */
839 static bool_t
NC_vcpy(target,old,varid)840 NC_vcpy( target, old, varid)
841 XDR *target ;
842 NC *old ;
843 int varid ;
844 {
845     NC_var **vpp ;
846     vpp = (NC_var **)old->vars->values ;
847     vpp += varid ;
848 
849     if( !xdr_setpos(old->xdrs, (*vpp)->begin) )
850       {
851           NCadvise(NC_EXDR, "NC_vcpy: xdr_setpos") ;
852           return(FALSE) ;
853       }
854 
855     return(NC_dcpy(target, old->xdrs, (*vpp)->len)) ;
856 }
857 
858 
859 /*
860  * XDR the data of (varid, recnum) in old, target is the new xdr strm
861  */
862 static bool_t
NC_reccpy(target,old,varid,recnum)863 NC_reccpy( target, old, varid, recnum)
864 XDR *target ;
865 NC *old ;
866 int varid ;
867 int recnum ;
868 {
869     NC_var **vpp ;
870     vpp = (NC_var **)old->vars->values ;
871     vpp += varid ;
872 
873     if( !xdr_setpos(old->xdrs,
874                     (*vpp)->begin + old->recsize*recnum )){
875         NCadvise(NC_EXDR, "NC_reccpy: xdr_setpos") ;
876         return(FALSE) ;
877     }
878 
879     return(NC_dcpy(target, old->xdrs, (*vpp)->len)) ;
880 }
881 
882 
883 /*
884  *  Common code for ncendef, ncclose(endef)
885  */
886 static
NC_endef(cdfid,handle)887 int NC_endef( cdfid, handle )
888 int cdfid ;
889 NC *handle ;
890 {
891     XDR *xdrs ;
892     unsigned ii ;
893     unsigned jj = 0 ;
894     NC_var **vpp ;
895     NC *stash = STASH(cdfid) ; /* faster rvalue */
896 
897 #ifdef HDF
898     if(handle->file_type != HDF_FILE)
899 #endif
900         NC_begins(handle) ;
901 
902     xdrs = handle->xdrs ;
903     xdrs->x_op = XDR_ENCODE ;
904 
905     if(!xdr_cdf(xdrs, &handle) )
906       {
907           nc_serror("xdr_cdf") ;
908           return(-1) ;
909       }
910 
911 #ifdef HDF
912     /* Get rid of the temporary buffer allocated for I/O */
913     SDPfreebuf();
914 
915     if(handle->file_type == HDF_FILE)
916       {
917           handle->flags &= ~(NC_CREAT | NC_INDEF | NC_NDIRTY | NC_HDIRTY) ;
918           return(0) ;
919       }
920 #endif
921 
922     if(handle->vars == NULL)
923         goto done ;
924 
925     /* loop thru vars, first pass is for the 'non-record' vars */
926     vpp = (NC_var **)handle->vars->values ;
927     for(ii = 0 ; ii < handle->vars->count ; ii++, vpp++)
928       {
929           if( IS_RECVAR(*vpp) )
930             {
931                 continue ;    /* skip record variables on this pass */
932             }
933 
934 #ifdef DEBUG
935           assert( (*vpp)->begin == xdr_getpos(xdrs) ) ;
936 #endif /* DEBUG */
937 
938           if( !(handle->flags & NC_CREAT) && stash->vars != NULL
939               && ii < stash->vars->count)
940             {
941                 /* copy data */
942                 if( !NC_vcpy(xdrs, stash, ii) )
943                     return(-1) ;
944                 continue ;
945             } /* else */
946 
947           if( !(handle->flags & NC_NOFILL) )
948               if( !xdr_NC_fill(xdrs, *vpp) )
949                   return(-1) ;
950       }
951 
952     if(!(handle->flags & NC_CREAT)) /* after redefinition */
953       {
954           for(jj = 0 ; jj < stash->numrecs ; jj++)
955             {
956                 vpp = (NC_var **)handle->vars->values ;
957                 for(ii = 0 ; ii < handle->vars->count ; ii++, vpp++)
958                   {
959                       if( !IS_RECVAR(*vpp) )
960                         {
961                             continue ;    /* skip non-record variables on this pass */
962                         }
963                       if( stash->vars != NULL && ii < stash->vars->count)
964                         {
965                             /* copy data */
966                             if( !NC_reccpy(xdrs, stash, ii, jj) )
967                                 return(-1) ;
968                             continue ;
969                         } /* else */
970                       if( !(handle->flags & NC_NOFILL) )
971                           if( !xdr_NC_fill(xdrs, *vpp) )
972                               return(-1) ;
973                   }
974             }
975           handle->numrecs = stash->numrecs ;
976           if(!xdr_numrecs(handle->xdrs, handle) )
977               return(-1) ;
978       }
979 
980 #ifdef EDEBUG
981     NCadvise(NC_NOERR, "begin %d, recsize %d, numrecs %d",
982              handle->begin_rec, handle->recsize, handle->numrecs) ;
983 #endif /* EDEBUG */
984 
985     if(!(handle->flags & NC_CREAT)) /* redefine */
986       {
987           char realpath[FILENAME_MAX + 1] ;
988           strcpy(realpath, stash->path) ;
989 
990           /* close stash */
991 /*                NC_free_cdf(stash) ; */
992 #ifdef DOS_FS
993           xdr_destroy(handle->xdrs) ; /* close handle */
994           if( remove(realpath) != 0 )
995               nc_serror("couldn't remove filename \"%s\"", realpath) ;
996 #endif
997           if( rename(handle->path, realpath) != 0)
998             {
999                 nc_serror("rename %s -> %s failed", handle->path, realpath) ;
1000                 /* try to restore state prior to redef */
1001                 _cdfs[cdfid] = stash ;
1002                 _cdfs[handle->redefid] = NULL ;
1003                 if(handle->redefid == _ncdf - 1)
1004                     _ncdf-- ;
1005         _curr_opened--;    /* one less file currently opened */
1006                 NC_free_cdf(handle) ;
1007 
1008         /* if the _cdf list is empty, deallocate and reset it to NULL */
1009         if (_ncdf == 0)
1010             ncreset_cdflist();
1011 
1012                 return(-1) ;
1013             }
1014           (void) strncpy(handle->path, realpath, FILENAME_MAX) ;
1015 #ifdef DOS_FS
1016           if( NCxdrfile_create( handle->xdrs, handle->path, NC_WRITE ) < 0)
1017               return -1 ;
1018 #endif
1019           NC_free_cdf(stash) ;
1020           _cdfs[handle->redefid] = NULL ;
1021           if(handle->redefid == _ncdf - 1)
1022               _ncdf-- ;
1023     _curr_opened--;    /* one less file currently opened */
1024           handle->redefid = -1 ;
1025 
1026     /* if the _cdf list is empty, deallocate and reset it to NULL */
1027     if (_ncdf == 0)
1028         ncreset_cdflist();
1029       }
1030 
1031 done:
1032     handle->flags &= ~(NC_CREAT | NC_INDEF | NC_NDIRTY | NC_HDIRTY) ;
1033     return(0) ;
1034 }
1035 
1036 
ncendef(cdfid)1037 int ncendef( cdfid )
1038 int cdfid ;
1039 {
1040     NC *handle ;
1041 
1042     cdf_routine_name = "ncendef" ;
1043 
1044     handle = NC_check_id(cdfid) ;
1045     if(handle == NULL)
1046         return(-1) ;
1047     if( !NC_indefine(cdfid,TRUE) )
1048         return(-1) ;
1049     return( NC_endef(cdfid, handle) ) ;
1050 }
1051 
1052 /*
1053  * This routine is called by SDend()? -GV
1054  */
ncclose(cdfid)1055 int ncclose( cdfid )
1056 int cdfid ;
1057 {
1058     NC *handle ;
1059 
1060     cdf_routine_name = "ncclose" ;
1061 
1062     handle = NC_check_id(cdfid) ;
1063     if(handle == NULL)
1064         return(-1) ;
1065 
1066     if( handle->flags & NC_INDEF)
1067         {
1068         if( NC_endef(cdfid, handle) == -1 )
1069             {
1070                 return( ncabort(cdfid) ) ;
1071             }
1072         }
1073     else if(handle->flags & NC_RDWR)
1074         {
1075         handle->xdrs->x_op = XDR_ENCODE ;
1076         if(handle->flags & NC_HDIRTY)
1077             {
1078                 if(!xdr_cdf(handle->xdrs, &handle) )
1079                     return(-1) ;
1080             }
1081         else if(handle->flags & NC_NDIRTY)
1082             {
1083                 if(!xdr_numrecs(handle->xdrs, handle) )
1084                     return(-1) ;
1085             }
1086     }
1087 
1088 #ifdef HDF
1089     if(handle->file_type == HDF_FILE)
1090         hdf_close(handle);
1091 #endif
1092 
1093     NC_free_cdf(handle) ; /* calls fclose */
1094 
1095     _cdfs[cdfid] = NULL ;    /* reset pointer */
1096 
1097     if(cdfid == _ncdf - 1)
1098         _ncdf-- ;
1099     _curr_opened--;    /* one less file currently opened */
1100 
1101     /* if the _cdf list is empty, deallocate and reset it to NULL */
1102     if (_ncdf == 0)
1103         ncreset_cdflist();
1104     return(0) ;
1105 }
1106 
1107 int
ncsetfill(id,fillmode)1108 ncsetfill(id, fillmode)
1109 int id ;
1110 int fillmode ;
1111 {
1112     NC *handle ;
1113     int ret = 0 ;
1114 
1115     cdf_routine_name = "ncsetfill" ;
1116 
1117     handle = NC_check_id(id) ;
1118     if(handle == NULL)
1119         return(-1) ;
1120 
1121     if(!(handle->flags & NC_RDWR))
1122       {
1123           /* file isn't writable */
1124           NCadvise(NC_EPERM, "%s is not writable", handle->path) ;
1125           return -1 ;
1126       }
1127 
1128     ret = (handle->flags & NC_NOFILL) ? NC_NOFILL : NC_FILL ;
1129 
1130     if(fillmode == NC_NOFILL)
1131         handle->flags |= NC_NOFILL ;
1132     else if(fillmode == NC_FILL)
1133       {
1134           if(handle->flags & NC_NOFILL)
1135             {
1136                 /*
1137                  * We are changing back to fill mode
1138                  * so do a sync
1139                  */
1140 #ifdef HDF       /* save the original x_op  */
1141                 enum xdr_op  xdr_op = handle->xdrs->x_op;
1142 
1143                 if (handle->flags & NC_RDWR)   /* make sure we can write */
1144                     handle->xdrs->x_op = XDR_ENCODE; /*  to the file */
1145 #endif
1146                 if(handle->flags & NC_HDIRTY)
1147                   {
1148                       if(!xdr_cdf(handle->xdrs, &handle) )
1149                           return(-1) ;
1150                       handle->flags &= ~(NC_NDIRTY | NC_HDIRTY) ;
1151                   }
1152                 else if(handle->flags & NC_NDIRTY)
1153                   {
1154                       if(!xdr_numrecs(handle->xdrs, handle) )
1155                           return(-1) ;
1156 #ifdef HDF
1157                       if (handle->file_type != HDF_FILE)
1158                           handle->flags &= ~(NC_NDIRTY) ;
1159 #else
1160                       handle->flags &= ~(NC_NDIRTY) ;
1161 #endif
1162                   }
1163                 handle->flags &= ~NC_NOFILL ;
1164 #ifdef HDF                /* re-store the x_op  */
1165                 handle->xdrs->x_op = xdr_op;
1166 #endif
1167             }
1168       }
1169     else
1170       {
1171           NCadvise(NC_EINVAL, "Bad fillmode") ;
1172           return -1 ;
1173       }
1174 
1175 
1176     return ret ;
1177 }
1178