1 /*
2  *	Copyright 1996, University Corporation for Atmospheric Research
3  *	See netcdf/COPYRIGHT file for copying and redistribution conditions.
4  */
5 
6 #if HAVE_CONFIG_H
7 #include <config.h>
8 #endif
9 #include <assert.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <errno.h>
13 #include <string.h>
14 #ifdef HAVE_UNISTD_H
15 #include <unistd.h>
16 #endif
17 #ifdef HAVE_FCNTL_H
18 #include <fcntl.h>
19 #endif
20 #ifdef _MSC_VER /* Microsoft Compilers */
21 #include <windows.h>
22 #include <winbase.h>
23 #include <io.h>
24 #endif
25 
26 #include "ncdispatch.h"
27 #include "nc3internal.h"
28 #include "netcdf_mem.h"
29 #include "ncwinpath.h"
30 
31 #undef DEBUG
32 
33 #ifndef HAVE_SSIZE_T
34 typedef int ssize_t;
35 #endif
36 
37 #ifdef DEBUG
38 #include <stdio.h>
39 #endif
40 
41 #ifndef SEEK_SET
42 #define SEEK_SET 0
43 #define SEEK_CUR 1
44 #define SEEK_END 2
45 #endif
46 
47 /* Define the mode flags for create: let umask rule */
48 #define OPENMODE 0666
49 
50 #include "ncio.h"
51 #include "fbits.h"
52 #include "rnd.h"
53 
54 /* #define INSTRUMENT 1 */
55 #if INSTRUMENT /* debugging */
56 #undef NDEBUG
57 #include <stdio.h>
58 #include "instr.h"
59 #endif
60 
61 #ifndef MEMIO_MAXBLOCKSIZE
62 #define MEMIO_MAXBLOCKSIZE 268435456 /* sanity check, about X_SIZE_T_MAX/8 */
63 #endif
64 
65 #undef MIN  /* system may define MIN somewhere and complain */
66 #define MIN(mm,nn) (((mm) < (nn)) ? (mm) : (nn))
67 
68 #if !defined(NDEBUG) && !defined(X_INT_MAX)
69 #define  X_INT_MAX 2147483647
70 #endif
71 
72 #if 0 /* !defined(NDEBUG) && !defined(X_ALIGN) */
73 #define  X_ALIGN 4
74 #else
75 #undef X_ALIGN
76 #endif
77 
78 #define REALLOCBUG
79 #ifdef REALLOCBUG
80 /* There is some kind of realloc bug that I cannot solve yet */
81 #define reallocx(m,new,old) realloc(m,new)
82 #else
83 static void*
reallocx(void * mem,size_t newsize,size_t oldsize)84 reallocx(void* mem, size_t newsize, size_t oldsize)
85 {
86     void* m = malloc(newsize);
87     if(m != NULL) {
88         memcpy(m,mem,oldsize);
89 	free(mem);
90     }
91     return m;
92 }
93 #endif
94 
95 /* Private data for memio */
96 
97 typedef struct NCMEMIO {
98     int locked; /* => we cannot realloc or free*/
99     int modified; /* => we realloc'd memory at least once */
100     int persist; /* => save to a file; triggered by NC_PERSIST*/
101     char* memory;
102     size_t alloc;
103     size_t size;
104     size_t pos;
105     /* Convenience flags */
106     int diskless;
107     int inmemory; /* assert(inmemory iff !diskless */
108 } NCMEMIO;
109 
110 /* Forward */
111 static int memio_rel(ncio *const nciop, off_t offset, int rflags);
112 static int memio_get(ncio *const nciop, off_t offset, size_t extent, int rflags, void **const vpp);
113 static int memio_move(ncio *const nciop, off_t to, off_t from, size_t nbytes, int rflags);
114 static int memio_sync(ncio *const nciop);
115 static int memio_filesize(ncio* nciop, off_t* filesizep);
116 static int memio_pad_length(ncio* nciop, off_t length);
117 static int memio_close(ncio* nciop, int);
118 static int readfile(const char* path, NC_memio*);
119 static int writefile(const char* path, NCMEMIO*);
120 static int fileiswriteable(const char* path);
121 static int fileexists(const char* path);
122 
123 /* Mnemonic */
124 #define DOOPEN 1
125 
126 static size_t pagesize = 0;
127 
128 /*! Create a new ncio struct to hold info about the file. */
129 static int
memio_new(const char * path,int ioflags,off_t initialsize,ncio ** nciopp,NCMEMIO ** memiop)130 memio_new(const char* path, int ioflags, off_t initialsize, ncio** nciopp, NCMEMIO** memiop)
131 {
132     int status = NC_NOERR;
133     ncio* nciop = NULL;
134     NCMEMIO* memio = NULL;
135     size_t minsize = (size_t)initialsize;
136 
137     /* Unlike netcdf-4, INMEMORY and DISKLESS share code */
138     if(fIsSet(ioflags,NC_DISKLESS))
139 	fSet(ioflags,NC_INMEMORY);
140 
141     /* use asserts because this is an internal function */
142     assert(fIsSet(ioflags,NC_INMEMORY));
143     assert(memiop != NULL && nciopp != NULL);
144     assert(path != NULL);
145 
146     if(pagesize == 0) {
147 #if defined (_WIN32) || defined(_WIN64)
148       SYSTEM_INFO info;
149       GetSystemInfo (&info);
150       pagesize = info.dwPageSize;
151 #elif defined HAVE_SYSCONF
152       long pgval = -1;
153       pgval = sysconf(_SC_PAGE_SIZE);
154       if(pgval < 0) {
155           status = NC_EIO;
156           goto fail;
157       }
158       pagesize = (size_t)pgval;
159 #elif defined HAVE_GETPAGESIZE
160       pagesize = (size_t)getpagesize();
161 #else
162       pagesize = 4096; /* good guess */
163 #endif
164     }
165 
166     errno = 0;
167 
168     /* Always force the allocated size to be a multiple of pagesize */
169     if(initialsize == 0) initialsize = pagesize;
170     if((initialsize % pagesize) != 0)
171 	initialsize += (pagesize - (initialsize % pagesize));
172 
173     nciop = (ncio* )calloc(1,sizeof(ncio));
174     if(nciop == NULL) {status = NC_ENOMEM; goto fail;}
175 
176     nciop->ioflags = ioflags;
177     *((int*)&nciop->fd) = -1; /* caller will fix */
178 
179     *((ncio_relfunc**)&nciop->rel) = memio_rel;
180     *((ncio_getfunc**)&nciop->get) = memio_get;
181     *((ncio_movefunc**)&nciop->move) = memio_move;
182     *((ncio_syncfunc**)&nciop->sync) = memio_sync;
183     *((ncio_filesizefunc**)&nciop->filesize) = memio_filesize;
184     *((ncio_pad_lengthfunc**)&nciop->pad_length) = memio_pad_length;
185     *((ncio_closefunc**)&nciop->close) = memio_close;
186 
187     memio = (NCMEMIO*)calloc(1,sizeof(NCMEMIO));
188     if(memio == NULL) {status = NC_ENOMEM; goto fail;}
189     *((void* *)&nciop->pvt) = memio;
190 
191     *((char**)&nciop->path) = strdup(path);
192     if(nciop->path == NULL) {status = NC_ENOMEM; goto fail;}
193 
194     if(memiop && memio) *memiop = memio; else free(memio);
195     if(nciopp && nciop) *nciopp = nciop;
196     else {
197         if(nciop->path != NULL) free((char*)nciop->path);
198         free(nciop);
199     }
200     memio->alloc = (size_t)initialsize;
201     memio->pos = 0;
202     memio->size = minsize;
203     memio->memory = NULL; /* filled in by caller */
204 
205     if(fIsSet(ioflags,NC_DISKLESS))
206 	memio->diskless = 1;
207     if(fIsSet(ioflags,NC_INMEMORY))
208 	memio->inmemory = 1;
209     if(fIsSet(ioflags,NC_PERSIST))
210 	memio->persist = 1;
211 
212 done:
213     return status;
214 
215 fail:
216     if(memio != NULL) free(memio);
217     if(nciop != NULL) {
218         if(nciop->path != NULL) free((char*)nciop->path);
219         free(nciop);
220     }
221     goto done;
222 }
223 
224 /* Create a file, and the ncio struct to go with it.
225 
226    path - path of file to create.
227    ioflags - flags from nc_create
228    initialsz - From the netcdf man page: "The argument
229                initialsize sets the initial size of the file at creation time."
230    igeto -
231    igetsz -
232    sizehintp - the size of a page of data for buffered reads and writes.
233    parameters - arbitrary data
234    nciopp - pointer to a pointer that will get location of newly
235    created and inited ncio struct.
236    mempp - pointer to pointer to the initial memory read.
237 */
238 int
memio_create(const char * path,int ioflags,size_t initialsz,off_t igeto,size_t igetsz,size_t * sizehintp,void * parameters,ncio ** nciopp,void ** const mempp)239 memio_create(const char* path, int ioflags,
240     size_t initialsz,
241     off_t igeto, size_t igetsz, size_t* sizehintp,
242     void* parameters /*ignored*/,
243     ncio* *nciopp, void** const mempp)
244 {
245     ncio* nciop;
246     int fd;
247     int status;
248     NCMEMIO* memio = NULL;
249 
250     if(path == NULL ||* path == 0)
251         return NC_EINVAL;
252 
253     status = memio_new(path, ioflags, initialsz, &nciop, &memio);
254     if(status != NC_NOERR)
255         return status;
256 
257     if(memio->persist) {
258 	/* Verify the file is writeable or does not exist*/
259 	if(fileexists(path) && !fileiswriteable(path))
260 	    {status = EPERM; goto unwind_open;}
261     }
262 
263     /* Allocate the memory for this file */
264     memio->memory = (char*)malloc((size_t)memio->alloc);
265     if(memio->memory == NULL) {status = NC_ENOMEM; goto unwind_open;}
266     memio->locked = 0;
267 
268 #ifdef DEBUG
269 fprintf(stderr,"memio_create: initial memory: %lu/%lu\n",(unsigned long)memio->memory,(unsigned long)memio->alloc);
270 #endif
271 
272     fd = nc__pseudofd();
273     *((int* )&nciop->fd) = fd;
274 
275     fSet(nciop->ioflags, NC_WRITE); /* Always writeable */
276 
277     if(igetsz != 0)
278     {
279         status = nciop->get(nciop,
280                 igeto, igetsz,
281                 RGN_WRITE,
282                 mempp);
283         if(status != NC_NOERR)
284             goto unwind_open;
285     }
286 
287     /* Pick a default sizehint */
288     if(sizehintp) *sizehintp = (size_t)pagesize;
289 
290     *nciopp = nciop;
291     return NC_NOERR;
292 
293 unwind_open:
294     memio_close(nciop,1);
295     return status;
296 }
297 
298 /* This function opens the data file or inmemory data
299    path - path of data file.
300    ioflags - flags passed into nc_open.
301    igeto - looks like this function can do an initial page get, and
302    igeto is going to be the offset for that. But it appears to be
303    unused
304    igetsz - the size in bytes of initial page get (a.k.a. extent). Not
305    ever used in the library.
306    sizehintp - the size of a page of data for buffered reads and writes.
307    parameters - arbitrary data
308    nciopp - pointer to pointer that will get address of newly created
309    and inited ncio struct.
310    mempp - pointer to pointer to the initial memory read.
311 */
312 int
memio_open(const char * path,int ioflags,off_t igeto,size_t igetsz,size_t * sizehintp,void * parameters,ncio ** nciopp,void ** const mempp)313 memio_open(const char* path,
314     int ioflags,
315     off_t igeto, size_t igetsz, size_t* sizehintp,
316     void* parameters,
317     ncio* *nciopp, void** const mempp)
318 {
319     ncio* nciop = NULL;
320     int fd = -1;
321     int status = NC_NOERR;
322     size_t sizehint = 0;
323     NC_memio meminfo; /* use struct to avoid worrying about free'ing it */
324     NCMEMIO* memio = NULL;
325     size_t initialsize;
326     /* Should be the case that diskless => inmemory but not converse */
327     int diskless = (fIsSet(ioflags,NC_DISKLESS));
328     int inmemory = fIsSet(ioflags,NC_INMEMORY);
329     int locked = 0;
330 
331     assert(inmemory ? !diskless : 1);
332 
333     if(path == NULL || strlen(path) == 0)
334         return NC_EINVAL;
335 
336     assert(sizehintp != NULL);
337 
338     sizehint = *sizehintp;
339 
340     memset(&meminfo,0,sizeof(meminfo));
341 
342     if(inmemory) { /* parameters provide the memory chunk */
343 	NC_memio* memparams = (NC_memio*)parameters;
344         meminfo = *memparams;
345         locked = fIsSet(meminfo.flags,NC_MEMIO_LOCKED);
346 	/* As a safeguard, if !locked and NC_WRITE is set,
347            then we must take control of the incoming memory */
348         if(!locked && fIsSet(ioflags,NC_WRITE)) {
349 	    memparams->memory = NULL;
350 	}
351     } else { /* read the file into a chunk of memory*/
352 	assert(diskless);
353 	status = readfile(path,&meminfo);
354 	if(status != NC_NOERR)
355 	    {goto unwind_open;}
356     }
357 
358     /* Fix up initial size */
359     initialsize = meminfo.size;
360 
361     /* create the NCMEMIO structure */
362     status = memio_new(path, ioflags, initialsize, &nciop, &memio);
363     if(status != NC_NOERR)
364 	{goto unwind_open;}
365     memio->locked = locked;
366 
367     /* Initialize the memio memory */
368     memio->memory = meminfo.memory;
369 
370     /* memio_new may have modified the allocated size, in which case,
371        reallocate the memory unless the memory is locked. */
372     if(memio->alloc > meminfo.size) {
373 	if(memio->locked)
374 	    memio->alloc = meminfo.size; /* force it back to what it was */
375 	else {
376 	   void* oldmem = memio->memory;
377 	   memio->memory = reallocx(oldmem,memio->alloc,meminfo.size);
378 	   if(memio->memory == NULL)
379 	       {status = NC_ENOMEM; goto unwind_open;}
380 	}
381     }
382 
383 #ifdef DEBUG
384 fprintf(stderr,"memio_open: initial memory: %lu/%lu\n",(unsigned long)memio->memory,(unsigned long)memio->alloc);
385 #endif
386 
387     if(memio->persist) {
388 	/* Verify the file is writeable and exists */
389 	if(!fileexists(path))
390 	    {status = ENOENT; goto unwind_open;}
391 	if(!fileiswriteable(path))
392 	    {status = EACCES; goto unwind_open;}
393     }
394 
395     /* Use half the filesize as the blocksize ; why? */
396     sizehint = (size_t)(memio->alloc/2);
397 
398     /* sizehint must be multiple of 8 */
399     sizehint = (sizehint / 8) * 8;
400     if(sizehint < 8) sizehint = 8;
401 
402     fd = nc__pseudofd();
403     *((int* )&nciop->fd) = fd;
404 
405     if(igetsz != 0)
406     {
407         status = nciop->get(nciop,
408                 igeto, igetsz,
409                 0,
410                 mempp);
411         if(status != NC_NOERR)
412             goto unwind_open;
413     }
414 
415     if(sizehintp) *sizehintp = sizehint;
416     if(nciopp) *nciopp = nciop; else {ncio_close(nciop,0);}
417     return NC_NOERR;
418 
419 unwind_open:
420     if(fd >= 0)
421       close(fd);
422     memio_close(nciop,0);
423     return status;
424 }
425 
426 /*
427  *  Get file size in bytes.
428  */
429 static int
memio_filesize(ncio * nciop,off_t * filesizep)430 memio_filesize(ncio* nciop, off_t* filesizep)
431 {
432     NCMEMIO* memio;
433     if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
434     memio = (NCMEMIO*)nciop->pvt;
435     if(filesizep != NULL) *filesizep = memio->size;
436     return NC_NOERR;
437 }
438 
439 /*
440  *  Sync any changes to disk, then truncate or extend file so its size
441  *  is length.  This is only intended to be called before close, if the
442  *  file is open for writing and the actual size does not match the
443  *  calculated size, perhaps as the result of having been previously
444  *  written in NOFILL mode.
445  */
446 static int
memio_pad_length(ncio * nciop,off_t length)447 memio_pad_length(ncio* nciop, off_t length)
448 {
449     NCMEMIO* memio;
450     size_t len = (size_t)length;
451     if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
452     memio = (NCMEMIO*)nciop->pvt;
453 
454     if(!fIsSet(nciop->ioflags,NC_WRITE))
455         return EPERM; /* attempt to write readonly file*/
456     if(memio->locked)
457 	return NC_EINMEMORY;
458 
459     if(len > memio->alloc) {
460         /* Realloc the allocated memory to a multiple of the pagesize*/
461 	size_t newsize = (size_t)len;
462 	void* newmem = NULL;
463 	/* Round to a multiple of pagesize */
464 	if((newsize % pagesize) != 0)
465 	    newsize += (pagesize - (newsize % pagesize));
466 
467         newmem = (char*)reallocx(memio->memory,newsize,memio->alloc);
468         if(newmem == NULL) return NC_ENOMEM;
469 	/* If not copy is set, then fail if the newmem address is different
470            from old address */
471 	if(newmem != memio->memory) {
472 	    memio->modified++;
473 	    if(memio->locked) {
474 		free(newmem);
475 		return NC_EINMEMORY;
476 	    }
477         }
478 	/* zero out the extra memory */
479         memset((void*)((char*)newmem+memio->alloc),0,(size_t)(newsize - memio->alloc));
480 
481 #ifdef DEBUG
482 fprintf(stderr,"realloc: %lu/%lu -> %lu/%lu\n",
483 (unsigned long)memio->memory,(unsigned long)memio->alloc,
484 (unsigned long)newmem,(unsigned long)newsize);
485 #endif
486 	if(memio->memory != NULL && (!memio->locked || memio->modified))
487 	    free(memio->memory);
488 	memio->memory = newmem;
489 	memio->alloc = newsize;
490 	memio->modified = 1;
491     }
492     memio->size = len;
493     return NC_NOERR;
494 }
495 
496 /*! Write out any dirty buffers to disk.
497 
498   Write out any dirty buffers to disk and ensure that next read will get data from disk.
499   Sync any changes, then close the open file associated with the ncio struct, and free its memory.
500 
501   @param[in] nciop pointer to ncio to close.
502   @param[in] doUnlink if true, unlink file
503   @return NC_NOERR on success, error code on failure.
504 */
505 
506 static int
memio_close(ncio * nciop,int doUnlink)507 memio_close(ncio* nciop, int doUnlink)
508 {
509     int status = NC_NOERR;
510     NCMEMIO* memio ;
511 
512     if(nciop == NULL || nciop->pvt == NULL) return NC_NOERR;
513 
514     memio = (NCMEMIO*)nciop->pvt;
515     assert(memio != NULL);
516 
517     /* See if the user wants the contents persisted to a file */
518     if(memio->persist && memio->memory != NULL) {
519 	status = writefile(nciop->path,memio);
520     }
521 
522     /* We only free the memio memory if file is not locked or has been modified */
523     if(memio->memory != NULL && (!memio->locked || memio->modified)) {
524 	free(memio->memory);
525 	memio->memory = NULL;
526     }
527     /* do cleanup  */
528     if(memio != NULL) free(memio);
529     if(nciop->path != NULL) free((char*)nciop->path);
530     free(nciop);
531     return status;
532 }
533 
534 static int
guarantee(ncio * nciop,off_t endpoint0)535 guarantee(ncio* nciop, off_t endpoint0)
536 {
537     NCMEMIO* memio = (NCMEMIO*)nciop->pvt;
538     size_t endpoint = (size_t)endpoint0;
539     if(endpoint > memio->alloc) {
540 	/* extend the allocated memory and size */
541 	int status = memio_pad_length(nciop,endpoint);
542 	if(status != NC_NOERR) return status;
543     }
544     if(memio->size < endpoint)
545 	memio->size = endpoint;
546     return NC_NOERR;
547 }
548 
549 /*
550  * Request that the region (offset, extent)
551  * be made available through *vpp.
552  */
553 static int
memio_get(ncio * const nciop,off_t offset,size_t extent,int rflags,void ** const vpp)554 memio_get(ncio* const nciop, off_t offset, size_t extent, int rflags, void** const vpp)
555 {
556     int status = NC_NOERR;
557     NCMEMIO* memio;
558     if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
559     memio = (NCMEMIO*)nciop->pvt;
560     status = guarantee(nciop, offset+(off_t)extent);
561     memio->locked++;
562     if(status != NC_NOERR) return status;
563     if(vpp) *vpp = memio->memory+offset;
564     return NC_NOERR;
565 }
566 
567 /*
568  * Like memmove(), safely move possibly overlapping data.
569  */
570 static int
memio_move(ncio * const nciop,off_t to,off_t from,size_t nbytes,int ignored)571 memio_move(ncio* const nciop, off_t to, off_t from, size_t nbytes, int ignored)
572 {
573     int status = NC_NOERR;
574     NCMEMIO* memio;
575 
576     if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
577     memio = (NCMEMIO*)nciop->pvt;
578     if(from < to) {
579        /* extend if "to" is not currently allocated */
580        status = guarantee(nciop,to+(off_t)nbytes);
581        if(status != NC_NOERR) return status;
582     }
583     /* check for overlap */
584     if((to + (off_t)nbytes) > from || (from + (off_t)nbytes) > to) {
585 	/* Ranges overlap */
586 #ifdef HAVE_MEMMOVE
587         memmove((void*)(memio->memory+to),(void*)(memio->memory+from),nbytes);
588 #else
589         off_t overlap;
590 	off_t nbytes1;
591         if((from + nbytes) > to) {
592 	    overlap = ((from + nbytes) - to); /* # bytes of overlap */
593 	    nbytes1 = (nbytes - overlap); /* # bytes of non-overlap */
594 	    /* move the non-overlapping part */
595             memcpy((void*)(memio->memory+(to+overlap)),
596                    (void*)(memio->memory+(from+overlap)),
597 		   nbytes1);
598 	    /* move the overlapping part */
599 	    memcpy((void*)(memio->memory+to),
600                    (void*)(memio->memory+from),
601 		   overlap);
602 	} else { /*((to + nbytes) > from) */
603 	    overlap = ((to + nbytes) - from); /* # bytes of overlap */
604 	    nbytes1 = (nbytes - overlap); /* # bytes of non-overlap */
605 	    /* move the non-overlapping part */
606             memcpy((void*)(memio->memory+to),
607                    (void*)(memio->memory+from),
608 		   nbytes1);
609 	    /* move the overlapping part */
610 	    memcpy((void*)(memio->memory+(to+nbytes1)),
611                    (void*)(memio->memory+(from+nbytes1)),
612 		   overlap);
613 	}
614 #endif
615     } else {/* no overlap */
616 	memcpy((void*)(memio->memory+to),(void*)(memio->memory+from),nbytes);
617     }
618     return status;
619 }
620 
621 static int
memio_rel(ncio * const nciop,off_t offset,int rflags)622 memio_rel(ncio* const nciop, off_t offset, int rflags)
623 {
624     NCMEMIO* memio;
625     if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
626     memio = (NCMEMIO*)nciop->pvt;
627     memio->locked--;
628     return NC_NOERR; /* do nothing */
629 }
630 
631 /*
632  * Write out any dirty buffers to disk and
633  * ensure that next read will get data from disk.
634  */
635 static int
memio_sync(ncio * const nciop)636 memio_sync(ncio* const nciop)
637 {
638     return NC_NOERR; /* do nothing */
639 }
640 
641 /* "Hidden" Internal function to extract the
642    the size and/or contents of the memory.
643 */
644 int
memio_extract(ncio * const nciop,size_t * sizep,void ** memoryp)645 memio_extract(ncio* const nciop, size_t* sizep, void** memoryp)
646 {
647     int status = NC_NOERR;
648     NCMEMIO* memio = NULL;
649 
650     if(nciop == NULL || nciop->pvt == NULL) return NC_NOERR;
651     memio = (NCMEMIO*)nciop->pvt;
652     assert(memio != NULL);
653     if(sizep) *sizep = memio->size;
654 
655     if(memoryp && memio->memory != NULL) {
656 	*memoryp = memio->memory;
657 	memio->memory = NULL; /* make sure it does not get free'd */
658     }
659     return status;
660 }
661 
662 /* Return 1 if file exists, 0 otherwise */
663 static int
fileexists(const char * path)664 fileexists(const char* path)
665 {
666     int ok;
667     /* See if the file exists at all */
668     ok = NCaccess(path,ACCESS_MODE_EXISTS);
669     if(ok < 0) /* file does not exist */
670       return 0;
671     return 1;
672 }
673 
674 /* Return 1 if file is writeable, return 0 otherwise;
675    assumes fileexists has been checked already */
676 static int
fileiswriteable(const char * path)677 fileiswriteable(const char* path)
678 {
679     int ok;
680     /* if W is ok */
681     ok = NCaccess(path,ACCESS_MODE_W);
682     if(ok < 0)
683 	return 0;
684     return 1;
685 }
686 
687 #if 0 /* not used */
688 /* Return 1 if file is READABLE, return 0 otherwise;
689    assumes fileexists has been checked already */
690 static int
691 fileisreadable(const char* path)
692 {
693     int ok;
694     /* if RW is ok */
695     ok = NCaccess(path,ACCESS_MODE_R);
696     if(ok < 0)
697 	return 0;
698     return 1;
699 }
700 #endif
701 
702 /* Read contents of a disk file into a memory chunk */
703 static int
readfile(const char * path,NC_memio * memio)704 readfile(const char* path, NC_memio* memio)
705 {
706     int status = NC_NOERR;
707     FILE* f = NULL;
708     size_t filesize = 0;
709     size_t count = 0;
710     char* memory = NULL;
711     char* p = NULL;
712 
713     /* Open the file for reading */
714 #ifdef _MSC_VER
715     f = NCfopen(path,"rb");
716 #else
717     f = NCfopen(path,"r");
718 #endif
719     if(f == NULL)
720 	{status = errno; goto done;}
721     /* get current filesize */
722     if(fseek(f,0,SEEK_END) < 0)
723 	{status = errno; goto done;}
724     filesize = (size_t)ftell(f);
725     /* allocate memory */
726     memory = malloc((size_t)filesize);
727     if(memory == NULL)
728 	{status = NC_ENOMEM; goto done;}
729     /* move pointer back to beginning of file */
730     rewind(f);
731     count = filesize;
732     p = memory;
733     while(count > 0) {
734         size_t actual;
735         actual = fread(p,1,count,f);
736 	if(actual == 0 || ferror(f))
737 	    {status = NC_EIO; goto done;}
738 	count -= actual;
739 	p += actual;
740     }
741     if(memio) {
742 	memio->size = (size_t)filesize;
743 	memio->memory = memory;
744 	memory = NULL;
745     }
746 
747 done:
748     if(memory != NULL)
749 	free(memory);
750     if(f != NULL) fclose(f);
751     return status;
752 }
753 
754 /* write contents of a memory chunk back into a disk file */
755 static int
writefile(const char * path,NCMEMIO * memio)756 writefile(const char* path, NCMEMIO* memio)
757 {
758     int status = NC_NOERR;
759     FILE* f = NULL;
760     size_t count = 0;
761     char* p = NULL;
762 
763     /* Open/create the file for writing*/
764 #ifdef _MSC_VER
765     f = NCfopen(path,"wb");
766 #else
767     f = NCfopen(path,"w");
768 #endif
769     if(f == NULL)
770         {status = errno; goto done;}
771     rewind(f);
772     count = memio->size;
773     p = memio->memory;
774     while(count > 0) {
775         size_t actual;
776         actual = fwrite(p,1,count,f);
777 	if(actual == 0 || ferror(f))
778 	    {status = NC_EIO; goto done;}
779 	count -= actual;
780 	p += actual;
781     }
782 done:
783     if(f != NULL) fclose(f);
784     return status;
785 }
786