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