Coverage Report

Created: 2025-07-23 09:13

/src/gdal/netcdf-c-4.7.4/libsrc/memio.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *  Copyright 2018, 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 _WIN32
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
0
#define reallocx(m,new,old) realloc(m,new)
82
#else
83
static void*
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
130
memio_new(const char* path, int ioflags, off_t initialsize, ncio** nciopp, NCMEMIO** memiop)
131
0
{
132
0
    int status = NC_NOERR;
133
0
    ncio* nciop = NULL;
134
0
    NCMEMIO* memio = NULL;
135
0
    size_t minsize = (size_t)initialsize;
136
137
    /* Unlike netcdf-4, INMEMORY and DISKLESS share code */
138
0
    if(fIsSet(ioflags,NC_DISKLESS))
139
0
  fSet(ioflags,NC_INMEMORY);    
140
141
    /* use asserts because this is an internal function */
142
0
    assert(fIsSet(ioflags,NC_INMEMORY));
143
0
    assert(memiop != NULL && nciopp != NULL);
144
0
    assert(path != NULL);
145
146
0
    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
0
      pgval = sysconf(_SC_PAGE_SIZE);
154
0
      if(pgval < 0) {
155
0
          status = NC_EIO;
156
0
          goto fail;
157
0
      }
158
0
      pagesize = (size_t)pgval;
159
#elif defined HAVE_GETPAGESIZE
160
      pagesize = (size_t)getpagesize();
161
#else
162
      pagesize = 4096; /* good guess */
163
#endif
164
0
    }
165
166
0
    errno = 0;
167
168
    /* Always force the allocated size to be a multiple of pagesize */
169
0
    if(initialsize == 0) initialsize = pagesize;
170
0
    if((initialsize % pagesize) != 0)
171
0
  initialsize += (pagesize - (initialsize % pagesize));
172
173
0
    nciop = (ncio* )calloc(1,sizeof(ncio));
174
0
    if(nciop == NULL) {status = NC_ENOMEM; goto fail;}
175
176
0
    nciop->ioflags = ioflags;
177
0
    *((int*)&nciop->fd) = -1; /* caller will fix */
178
179
0
    *((ncio_relfunc**)&nciop->rel) = memio_rel;
180
0
    *((ncio_getfunc**)&nciop->get) = memio_get;
181
0
    *((ncio_movefunc**)&nciop->move) = memio_move;
182
0
    *((ncio_syncfunc**)&nciop->sync) = memio_sync;
183
0
    *((ncio_filesizefunc**)&nciop->filesize) = memio_filesize;
184
0
    *((ncio_pad_lengthfunc**)&nciop->pad_length) = memio_pad_length;
185
0
    *((ncio_closefunc**)&nciop->close) = memio_close;
186
187
0
    memio = (NCMEMIO*)calloc(1,sizeof(NCMEMIO));
188
0
    if(memio == NULL) {status = NC_ENOMEM; goto fail;}
189
0
    *((void* *)&nciop->pvt) = memio;
190
191
0
    *((char**)&nciop->path) = strdup(path);
192
0
    if(nciop->path == NULL) {status = NC_ENOMEM; goto fail;}
193
194
0
    if(memiop && memio) *memiop = memio; else free(memio);
195
0
    if(nciopp && nciop) *nciopp = nciop;
196
0
    else {
197
0
        if(nciop->path != NULL) free((char*)nciop->path);
198
0
        free(nciop);
199
0
    }
200
0
    memio->alloc = (size_t)initialsize;
201
0
    memio->pos = 0;
202
0
    memio->size = minsize;
203
0
    memio->memory = NULL; /* filled in by caller */
204
205
0
    if(fIsSet(ioflags,NC_DISKLESS))
206
0
  memio->diskless = 1;
207
0
    if(fIsSet(ioflags,NC_INMEMORY))
208
0
  memio->inmemory = 1;
209
0
    if(fIsSet(ioflags,NC_PERSIST))
210
0
  memio->persist = 1;
211
212
0
done:
213
0
    return status;
214
215
0
fail:
216
0
    if(memio != NULL) free(memio);
217
0
    if(nciop != NULL) {
218
0
        if(nciop->path != NULL) free((char*)nciop->path);
219
0
        free(nciop);
220
0
    }
221
0
    goto done;
222
0
}
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
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
0
{
245
0
    ncio* nciop;
246
0
    int fd;
247
0
    int status;
248
0
    NCMEMIO* memio = NULL;
249
250
0
    if(path == NULL ||* path == 0)
251
0
        return NC_EINVAL;
252
    
253
0
    status = memio_new(path, ioflags, initialsz, &nciop, &memio);
254
0
    if(status != NC_NOERR)
255
0
        return status;
256
257
0
    if(memio->persist) {
258
  /* Verify the file is writeable or does not exist*/
259
0
  if(fileexists(path) && !fileiswriteable(path))
260
0
      {status = EPERM; goto unwind_open;}  
261
0
    }
262
263
    /* Allocate the memory for this file */
264
0
    memio->memory = (char*)malloc((size_t)memio->alloc);
265
0
    if(memio->memory == NULL) {status = NC_ENOMEM; goto unwind_open;}
266
0
    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
0
    fd = nc__pseudofd();
273
0
    *((int* )&nciop->fd) = fd;
274
275
0
    fSet(nciop->ioflags, NC_WRITE); /* Always writeable */
276
277
0
    if(igetsz != 0)
278
0
    {
279
0
        status = nciop->get(nciop,
280
0
                igeto, igetsz,
281
0
                RGN_WRITE,
282
0
                mempp);
283
0
        if(status != NC_NOERR)
284
0
            goto unwind_open;
285
0
    }
286
287
    /* Pick a default sizehint */
288
0
    if(sizehintp) *sizehintp = (size_t)pagesize;
289
290
0
    *nciopp = nciop;
291
0
    return NC_NOERR;
292
293
0
unwind_open:
294
0
    memio_close(nciop,1);
295
0
    return status;
296
0
}
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
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
0
{
319
0
    ncio* nciop = NULL;
320
0
    int fd = -1;
321
0
    int status = NC_NOERR;
322
0
    size_t sizehint = 0;
323
0
    NC_memio meminfo; /* use struct to avoid worrying about free'ing it */
324
0
    NCMEMIO* memio = NULL;
325
0
    size_t initialsize;
326
    /* Should be the case that diskless => inmemory but not converse */
327
0
    int diskless = (fIsSet(ioflags,NC_DISKLESS));
328
0
    int inmemory = fIsSet(ioflags,NC_INMEMORY);
329
0
    int locked = 0;
330
331
0
    assert(inmemory ? !diskless : 1);
332
333
0
    if(path == NULL || strlen(path) == 0)
334
0
        return NC_EINVAL;
335
336
0
    assert(sizehintp != NULL);
337
338
0
    sizehint = *sizehintp;
339
340
0
    memset(&meminfo,0,sizeof(meminfo));
341
342
0
    if(inmemory) { /* parameters provide the memory chunk */
343
0
  NC_memio* memparams = (NC_memio*)parameters;
344
0
        meminfo = *memparams;
345
0
        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
0
        if(!locked && fIsSet(ioflags,NC_WRITE)) {
349
0
      memparams->memory = NULL;     
350
0
  }  
351
0
    } else { /* read the file into a chunk of memory*/
352
0
  assert(diskless);
353
0
  status = readfile(path,&meminfo);
354
0
  if(status != NC_NOERR)
355
0
      {goto unwind_open;}
356
0
    }
357
358
    /* Fix up initial size */
359
0
    initialsize = meminfo.size;
360
361
    /* create the NCMEMIO structure */
362
0
    status = memio_new(path, ioflags, initialsize, &nciop, &memio);
363
0
    if(status != NC_NOERR)
364
0
  {goto unwind_open;}
365
0
    memio->locked = locked;
366
367
    /* Initialize the memio memory */
368
0
    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
0
    if(memio->alloc > meminfo.size) {
373
0
  if(memio->locked)
374
0
      memio->alloc = meminfo.size; /* force it back to what it was */
375
0
  else {
376
0
     void* oldmem = memio->memory;
377
0
     memio->memory = reallocx(oldmem,memio->alloc,meminfo.size);
378
0
     if(memio->memory == NULL)
379
0
         {status = NC_ENOMEM; goto unwind_open;}
380
0
  }
381
0
    }
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
0
    if(memio->persist) {
388
  /* Verify the file is writeable and exists */
389
0
  if(!fileexists(path))
390
0
      {status = ENOENT; goto unwind_open;}  
391
0
  if(!fileiswriteable(path))
392
0
      {status = EACCES; goto unwind_open;}  
393
0
    }
394
395
    /* Use half the filesize as the blocksize ; why? */
396
0
    sizehint = (size_t)(memio->alloc/2);
397
398
    /* sizehint must be multiple of 8 */
399
0
    sizehint = (sizehint / 8) * 8;
400
0
    if(sizehint < 8) sizehint = 8;
401
402
0
    fd = nc__pseudofd();
403
0
    *((int* )&nciop->fd) = fd;
404
405
0
    if(igetsz != 0)
406
0
    {
407
0
        status = nciop->get(nciop,
408
0
                igeto, igetsz,
409
0
                0,
410
0
                mempp);
411
0
        if(status != NC_NOERR)
412
0
            goto unwind_open;
413
0
    }
414
415
0
    if(sizehintp) *sizehintp = sizehint;
416
0
    if(nciopp) *nciopp = nciop; else {ncio_close(nciop,0);}
417
0
    return NC_NOERR;
418
419
0
unwind_open:
420
0
    if(fd >= 0)
421
0
      close(fd);
422
0
    memio_close(nciop,0);
423
0
    return status;
424
0
}
425
426
/*
427
 *  Get file size in bytes.
428
 */
429
static int
430
memio_filesize(ncio* nciop, off_t* filesizep)
431
0
{
432
0
    NCMEMIO* memio;
433
0
    if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
434
0
    memio = (NCMEMIO*)nciop->pvt;
435
0
    if(filesizep != NULL) *filesizep = memio->size;
436
0
    return NC_NOERR;
437
0
}
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
447
memio_pad_length(ncio* nciop, off_t length)
448
0
{
449
0
    NCMEMIO* memio;
450
0
    size_t len = (size_t)length;
451
0
    if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
452
0
    memio = (NCMEMIO*)nciop->pvt;
453
454
0
    if(!fIsSet(nciop->ioflags,NC_WRITE))
455
0
        return EPERM; /* attempt to write readonly file*/
456
0
    if(memio->locked)
457
0
  return NC_EINMEMORY;
458
459
0
    if(len > memio->alloc) {
460
        /* Realloc the allocated memory to a multiple of the pagesize*/
461
0
  size_t newsize = (size_t)len;
462
0
  void* newmem = NULL;
463
  /* Round to a multiple of pagesize */
464
0
  if((newsize % pagesize) != 0)
465
0
      newsize += (pagesize - (newsize % pagesize));
466
467
0
        newmem = (char*)reallocx(memio->memory,newsize,memio->alloc);
468
0
        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
0
  if(newmem != memio->memory) {
472
0
      memio->modified++;
473
0
      if(memio->locked) {
474
0
    free(newmem);
475
0
    return NC_EINMEMORY;
476
0
      }
477
0
        }
478
  /* zero out the extra memory */
479
0
        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
0
  memio->memory = newmem;
487
0
  memio->alloc = newsize;
488
0
  memio->modified = 1;
489
0
    }
490
0
    memio->size = len;
491
0
    return NC_NOERR;
492
0
}
493
494
/*! Write out any dirty buffers to disk.
495
496
  Write out any dirty buffers to disk and ensure that next read will get data from disk.
497
  Sync any changes, then close the open file associated with the ncio struct, and free its memory.
498
499
  @param[in] nciop pointer to ncio to close.
500
  @param[in] doUnlink if true, unlink file
501
  @return NC_NOERR on success, error code on failure.
502
*/
503
504
static int
505
memio_close(ncio* nciop, int doUnlink)
506
0
{
507
0
    int status = NC_NOERR;
508
0
    NCMEMIO* memio ;
509
510
0
    if(nciop == NULL || nciop->pvt == NULL) return NC_NOERR;
511
512
0
    memio = (NCMEMIO*)nciop->pvt;
513
0
    assert(memio != NULL);
514
515
    /* See if the user wants the contents persisted to a file */
516
0
    if(memio->persist && memio->memory != NULL) {
517
0
  status = writefile(nciop->path,memio);    
518
0
    }
519
520
    /* We only free the memio memory if file is not locked or has been modified */
521
0
    if(memio->memory != NULL && (!memio->locked || memio->modified)) {
522
0
  free(memio->memory);
523
0
  memio->memory = NULL;
524
0
    }
525
    /* do cleanup  */
526
0
    if(memio != NULL) free(memio);
527
0
    if(nciop->path != NULL) free((char*)nciop->path);
528
0
    free(nciop);
529
0
    return status;
530
0
}
531
532
static int
533
guarantee(ncio* nciop, off_t endpoint0)
534
0
{
535
0
    NCMEMIO* memio = (NCMEMIO*)nciop->pvt;
536
0
    size_t endpoint = (size_t)endpoint0;
537
0
    if(endpoint > memio->alloc) {
538
  /* extend the allocated memory and size */
539
0
  int status = memio_pad_length(nciop,endpoint);
540
0
  if(status != NC_NOERR) return status;
541
0
    }
542
0
    if(memio->size < endpoint)
543
0
  memio->size = endpoint;
544
0
    return NC_NOERR;
545
0
}
546
547
/*
548
 * Request that the region (offset, extent)
549
 * be made available through *vpp.
550
 */
551
static int
552
memio_get(ncio* const nciop, off_t offset, size_t extent, int rflags, void** const vpp)
553
0
{
554
0
    int status = NC_NOERR;
555
0
    NCMEMIO* memio;
556
0
    if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
557
0
    memio = (NCMEMIO*)nciop->pvt;
558
0
    status = guarantee(nciop, offset+(off_t)extent);
559
0
    memio->locked++;
560
0
    if(status != NC_NOERR) return status;
561
0
    if(vpp) *vpp = memio->memory+offset;
562
0
    return NC_NOERR;
563
0
}
564
565
/*
566
 * Like memmove(), safely move possibly overlapping data.
567
 */
568
static int
569
memio_move(ncio* const nciop, off_t to, off_t from, size_t nbytes, int ignored)
570
0
{
571
0
    int status = NC_NOERR;
572
0
    NCMEMIO* memio;
573
574
0
    if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
575
0
    memio = (NCMEMIO*)nciop->pvt;
576
0
    if(from < to) {
577
       /* extend if "to" is not currently allocated */
578
0
       status = guarantee(nciop,to+(off_t)nbytes);
579
0
       if(status != NC_NOERR) return status;
580
0
    }
581
    /* check for overlap */
582
0
    if((to + (off_t)nbytes) > from || (from + (off_t)nbytes) > to) {
583
  /* Ranges overlap */
584
0
#ifdef HAVE_MEMMOVE
585
0
        memmove((void*)(memio->memory+to),(void*)(memio->memory+from),nbytes);
586
#else
587
        off_t overlap;
588
  off_t nbytes1;
589
        if((from + nbytes) > to) {
590
      overlap = ((from + nbytes) - to); /* # bytes of overlap */
591
      nbytes1 = (nbytes - overlap); /* # bytes of non-overlap */
592
      /* move the non-overlapping part */
593
            memcpy((void*)(memio->memory+(to+overlap)),
594
                   (void*)(memio->memory+(from+overlap)),
595
       nbytes1);
596
      /* move the overlapping part */
597
      memcpy((void*)(memio->memory+to),
598
                   (void*)(memio->memory+from),
599
       overlap);
600
  } else { /*((to + nbytes) > from) */
601
      overlap = ((to + nbytes) - from); /* # bytes of overlap */
602
      nbytes1 = (nbytes - overlap); /* # bytes of non-overlap */
603
      /* move the non-overlapping part */
604
            memcpy((void*)(memio->memory+to),
605
                   (void*)(memio->memory+from),
606
       nbytes1);
607
      /* move the overlapping part */
608
      memcpy((void*)(memio->memory+(to+nbytes1)),
609
                   (void*)(memio->memory+(from+nbytes1)),
610
       overlap);
611
  }
612
#endif
613
0
    } else {/* no overlap */
614
0
  memcpy((void*)(memio->memory+to),(void*)(memio->memory+from),nbytes);
615
0
    }
616
0
    return status;
617
0
}
618
619
static int
620
memio_rel(ncio* const nciop, off_t offset, int rflags)
621
0
{
622
0
    NCMEMIO* memio;
623
0
    if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
624
0
    memio = (NCMEMIO*)nciop->pvt;
625
0
    memio->locked--;
626
0
    return NC_NOERR; /* do nothing */
627
0
}
628
629
/*
630
 * Write out any dirty buffers to disk and
631
 * ensure that next read will get data from disk.
632
 */
633
static int
634
memio_sync(ncio* const nciop)
635
0
{
636
0
    return NC_NOERR; /* do nothing */
637
0
}
638
639
/* "Hidden" Internal function to extract the 
640
   the size and/or contents of the memory.
641
*/
642
int
643
memio_extract(ncio* const nciop, size_t* sizep, void** memoryp)
644
0
{
645
0
    int status = NC_NOERR;
646
0
    NCMEMIO* memio = NULL;
647
648
0
    if(nciop == NULL || nciop->pvt == NULL) return NC_NOERR;
649
0
    memio = (NCMEMIO*)nciop->pvt;
650
0
    assert(memio != NULL);
651
0
    if(sizep) *sizep = memio->size;
652
653
0
    if(memoryp && memio->memory != NULL) {
654
0
  *memoryp = memio->memory;
655
0
  memio->memory = NULL; /* make sure it does not get free'd */
656
0
    }
657
0
    return status;
658
0
}
659
660
/* Return 1 if file exists, 0 otherwise */
661
static int
662
fileexists(const char* path)
663
0
{
664
0
    int ok;
665
    /* See if the file exists at all */
666
0
    ok = NCaccess(path,ACCESS_MODE_EXISTS);
667
0
    if(ok < 0) /* file does not exist */
668
0
      return 0;
669
0
    return 1;
670
0
}
671
672
/* Return 1 if file is writeable, return 0 otherwise;
673
   assumes fileexists has been checked already */
674
static int
675
fileiswriteable(const char* path)
676
0
{
677
0
    int ok;
678
    /* if W is ok */
679
0
    ok = NCaccess(path,ACCESS_MODE_W);
680
0
    if(ok < 0)
681
0
  return 0;
682
0
    return 1;
683
0
}
684
685
#if 0 /* not used */
686
/* Return 1 if file is READABLE, return 0 otherwise;
687
   assumes fileexists has been checked already */
688
static int
689
fileisreadable(const char* path)
690
{
691
    int ok;
692
    /* if RW is ok */
693
    ok = NCaccess(path,ACCESS_MODE_R);
694
    if(ok < 0)
695
  return 0;
696
    return 1;
697
}
698
#endif
699
700
/* Read contents of a disk file into a memory chunk */
701
static int
702
readfile(const char* path, NC_memio* memio)
703
0
{
704
0
    int status = NC_NOERR;
705
0
    FILE* f = NULL;
706
0
    size_t filesize = 0;
707
0
    size_t count = 0;
708
0
    char* memory = NULL;
709
0
    char* p = NULL;
710
711
    /* Open the file for reading */
712
#ifdef _MSC_VER
713
    f = NCfopen(path,"rb");
714
#else
715
0
    f = NCfopen(path,"r");
716
0
#endif
717
0
    if(f == NULL)
718
0
  {status = errno; goto done;}
719
    /* get current filesize */
720
0
    if(fseek(f,0,SEEK_END) < 0)
721
0
  {status = errno; goto done;}
722
0
    filesize = (size_t)ftell(f);
723
    /* allocate memory */
724
0
    memory = malloc((size_t)filesize);
725
0
    if(memory == NULL)
726
0
  {status = NC_ENOMEM; goto done;}
727
    /* move pointer back to beginning of file */
728
0
    rewind(f);
729
0
    count = filesize;
730
0
    p = memory;
731
0
    while(count > 0) {
732
0
        size_t actual;
733
0
        actual = fread(p,1,count,f);
734
0
  if(actual == 0 || ferror(f))
735
0
      {status = NC_EIO; goto done;}  
736
0
  count -= actual;
737
0
  p += actual;
738
0
    }
739
0
    if(memio) {
740
0
  memio->size = (size_t)filesize;
741
0
  memio->memory = memory;
742
0
  memory = NULL;
743
0
    }
744
745
0
done:
746
0
    if(memory != NULL)
747
0
  free(memory);
748
0
    if(f != NULL) fclose(f);
749
0
    return status;    
750
0
}
751
752
/* write contents of a memory chunk back into a disk file */
753
static int
754
writefile(const char* path, NCMEMIO* memio)
755
0
{
756
0
    int status = NC_NOERR;
757
0
    FILE* f = NULL;
758
0
    size_t count = 0;
759
0
    char* p = NULL;
760
761
    /* Open/create the file for writing*/
762
#ifdef _MSC_VER
763
    f = NCfopen(path,"wb");
764
#else
765
0
    f = NCfopen(path,"w");
766
0
#endif
767
0
    if(f == NULL)
768
0
        {status = errno; goto done;}
769
0
    rewind(f);
770
0
    count = memio->size;
771
0
    p = memio->memory;
772
0
    while(count > 0) {
773
0
        size_t actual;
774
0
        actual = fwrite(p,1,count,f);
775
0
  if(actual == 0 || ferror(f))
776
0
      {status = NC_EIO; goto done;}  
777
0
  count -= actual;
778
0
  p += actual;
779
0
    }
780
0
done:
781
0
    if(f != NULL) fclose(f);
782
0
    return status;    
783
0
}