1/*
2 * Copyright 1996, University Corporation for Atmospheric Research
3 * See netcdf/COPYRIGHT file for copying and redistribution conditions.
4 */
5#if defined (_WIN32) || defined (_WIN64)
6#include <windows.h>
7#include <winbase.h>
8#include <io.h>
9#define lseek64 lseek
10#endif
11
12#include "config.h"
13#include <assert.h>
14#include <stdlib.h>
15#include <errno.h>
16#include <string.h>
17#ifdef _MSC_VER /* Microsoft Compilers */
18#include <io.h>
19#else
20#include <unistd.h>
21#endif
22#ifdef HAVE_FCNTL_H
23#include <fcntl.h>
24#endif
25#include "ncdispatch.h"
26#include "nc3internal.h"
27
28#undef DEBUG
29
30#ifdef DEBUG
31#include <stdio.h>
32#endif
33
34#ifndef HAVE_SSIZE_T
35#define ssize_t int
36#endif
37
38#ifndef SEEK_SET
39#define SEEK_SET 0
40#define SEEK_CUR 1
41#define SEEK_END 2
42#endif
43
44/* Define the mode flags for create: let umask rule */
45#define OPENMODE 0666
46
47#include "ncio.h"
48#include "fbits.h"
49#include "rnd.h"
50
51/* #define INSTRUMENT 1 */
52#if INSTRUMENT /* debugging */
53#undef NDEBUG
54#include <stdio.h>
55#include "instr.h"
56#endif
57
58#ifndef MEMIO_MAXBLOCKSIZE
59#define MEMIO_MAXBLOCKSIZE 268435456 /* sanity check, about X_SIZE_T_MAX/8 */
60#endif
61
62#undef MIN  /* system may define MIN somewhere and complain */
63#define MIN(mm,nn) (((mm) < (nn)) ? (mm) : (nn))
64
65#if !defined(NDEBUG) && !defined(X_INT_MAX)
66#define  X_INT_MAX 2147483647
67#endif
68
69#if 0 /* !defined(NDEBUG) && !defined(X_ALIGN) */
70#define  X_ALIGN 4
71#else
72#undef X_ALIGN
73#endif
74
75/* Private data for memio */
76
77typedef struct NCMEMIO {
78    int locked; /* => we cannot realloc */
79    int persist; /* => save to a file; triggered by NC_WRITE */
80    char* memory;
81    off_t alloc;
82    off_t size;
83    off_t pos;
84NCMEMIO;
85
86/* Forward */
87static int memio_rel(ncio *const nciopoff_t offset, int rflags);
88static int memio_get(ncio *const nciopoff_t offset, size_t extent, int rflags, void **const vpp);
89static int memio_move(ncio *const nciopoff_t tooff_t from, size_t nbytes, int rflags);
90static int memio_sync(ncio *const nciop);
91static int memio_filesize(ncionciopoff_tfilesizep);
92static int memio_pad_length(ncionciopoff_t length);
93static int memio_close(ncionciop, int);
94
95/* Mnemonic */
96#define DOOPEN 1
97
98static long pagesize = 0;
99
100/*! Create a new ncio struct to hold info about the file. */
101static int memio_new(const char* path, int ioflagsoff_t initialsize, void* memoryncio** ncioppNCMEMIO** memiop)
102{
103    int status = NC_NOERR;
104    ncionciop = NULL;
105    NCMEMIOmemio = NULL;
106    off_t minsize = initialsize;
107    int inmemory = (fIsSet(ioflags,NC_INMEMORY));
108
109    /* use asserts because this is an internal function */
110    assert(memiop != NULL && nciopp != NULL);
111    assert(path != NULL || (memory != NULL && initialsize > 0));
112    assert(!inmemory || (memory != NULL && initialsize > 0));
113
114    if(pagesize == 0) {
115#if defined (_WIN32) || defined(_WIN64)
116      SYSTEM_INFO info;
117      GetSystemInfo (&info);
118      pagesize = info.dwPageSize;
119#elif defined HAVE_SYSCONF
120        pagesize = sysconf(_SC_PAGE_SIZE);
121#elif defined HAVE_GETPAGESIZE
122        pagesize = getpagesize();
123#else
124        pagesize = 4096; /* good guess */
125#endif
126    }
127
128    /* We need to catch errors.
129       sysconf, at least, can return a negative value
130       when there is an error. */
131    if(pagesize < 0) {
132      status = NC_EIO;
133      goto fail;
134    }
135
136    errno = 0;
137
138    /* Always force the allocated size to be a multiple of pagesize */
139    if(initialsize == 0) initialsize = pagesize;
140    if((initialsize % pagesize) != 0)
141 initialsize += (pagesize - (initialsize % pagesize));
142
143    nciop = (ncio* )calloc(1,sizeof(ncio));
144    if(nciop == NULL) {status = NC_ENOMEM; goto fail;}
145
146    nciop->ioflags = ioflags;
147    *((int*)&nciop->fd) = -1; /* caller will fix */
148
149    *((ncio_relfunc**)&nciop->rel) = memio_rel;
150    *((ncio_getfunc**)&nciop->get) = memio_get;
151    *((ncio_movefunc**)&nciop->move) = memio_move;
152    *((ncio_syncfunc**)&nciop->sync) = memio_sync;
153    *((ncio_filesizefunc**)&nciop->filesize) = memio_filesize;
154    *((ncio_pad_lengthfunc**)&nciop->pad_length) = memio_pad_length;
155    *((ncio_closefunc**)&nciop->close) = memio_close;
156
157    memio = (NCMEMIO*)calloc(1,sizeof(NCMEMIO));
158    if(memio == NULL) {status = NC_ENOMEM; goto fail;}
159    *((void* *)&nciop->pvt) = memio;
160
161    *((char**)&nciop->path) = strdup(path);
162    if(nciop->path == NULL) {status = NC_ENOMEM; goto fail;}
163    memio->alloc = initialsize;
164    memio->pos = 0;
165    memio->size = minsize;
166    memio->memory = NULL;
167    memio->persist = fIsSet(ioflags,NC_WRITE);
168    if(memiop && memio) *memiop = memio; else free(memio);
169    if(nciopp && nciop) *nciopp = nciop;
170    else {
171        if(nciop->path != NULL) free((char*)nciop->path);
172        free(nciop);
173    }
174    if(inmemory) {
175      memio->memory = memory;
176    } else {
177        /* malloc memory */
178        memio->memory = (char*)malloc(memio->alloc);
179        if(memio->memory == NULL) {status = NC_ENOMEM; goto fail;}
180    }
181
182done:
183    return status;
184
185fail:
186    if(memio != NULL) free(memio);
187    if(nciop != NULL) {
188        if(nciop->path != NULL) free((char*)nciop->path);
189        free(nciop);
190    }
191    goto done;
192}
193
194/* Create a file, and the ncio struct to go with it.
195
196   path - path of file to create.
197   ioflags - flags from nc_create
198   initialsz - From the netcdf man page: "The argument
199               initialsize sets the initial size of the file at creation time."
200   igeto -
201   igetsz -
202   sizehintp - the size of a page of data for buffered reads and writes.
203   parameters - arbitrary data
204   nciopp - pointer to a pointer that will get location of newly
205   created and inited ncio struct.
206   mempp - pointer to pointer to the initial memory read.
207*/
208int
209memio_create(const char* path, int ioflags,
210    size_t initialsz,
211    off_t igeto, size_t igetsz, size_t* sizehintp,
212    void* parameters,
213    ncio* *nciopp, void** const mempp)
214{
215    ncionciop;
216    int fd;
217    int status;
218    NCMEMIOmemio = NULL;
219    int persist = (ioflags & NC_WRITE?1:0);
220    int oflags;
221
222    if(path == NULL ||* path == 0)
223        return NC_EINVAL;
224
225    status = memio_new(pathioflagsinitialszNULL, &nciop, &memio);
226    if(status != NC_NOERR)
227        return status;
228
229    if(persist) {
230        /* Open the file just tomake sure we can write it if needed */
231        oflags = (persist ? O_RDWR : O_RDONLY);
232#ifdef O_BINARY
233        fSet(oflagsO_BINARY);
234#endif
235     oflags |= (O_CREAT|O_TRUNC);
236        if(fIsSet(ioflags,NC_NOCLOBBER))
237     oflags |= O_EXCL;
238#ifdef vms
239        fd = open(pathoflags, 0, "ctx=stm");
240#else
241        fd  = open(pathoflagsOPENMODE);
242#endif
243        if(fd < 0) {status = errno; goto unwind_open;}
244
245        (void)close(fd); /* will reopen at nc_close */
246    } /*!persist*/
247
248#ifdef DEBUG
249fprintf(stderr,"memio_create: initial memory: %lu/%lu\n",(unsigned long)memio->memory,(unsigned long)memio->alloc);
250#endif
251
252    fd = nc__pseudofd();
253    *((int* )&nciop->fd) = fd;
254
255    fSet(nciop->ioflagsNC_WRITE);
256
257    if(igetsz != 0)
258    {
259        status = nciop->get(nciop,
260                igetoigetsz,
261                RGN_WRITE,
262                mempp);
263        if(status != NC_NOERR)
264            goto unwind_open;
265    }
266
267    /* Pick a default sizehint */
268    if(sizehintp) *sizehintp = pagesize;
269
270    *nciopp = nciop;
271    return NC_NOERR;
272
273unwind_open:
274    memio_close(nciop,1);
275    return status;
276}
277
278/* This function opens the data file.
279
280   path - path of data file.
281   ioflags - flags passed into nc_open.
282   igeto - looks like this function can do an initial page get, and
283   igeto is going to be the offset for that. But it appears to be
284   unused
285   igetsz - the size in bytes of initial page get (a.k.a. extent). Not
286   ever used in the library.
287   sizehintp - the size of a page of data for buffered reads and writes.
288   parameters - arbitrary data
289   nciopp - pointer to pointer that will get address of newly created
290   and inited ncio struct.
291   mempp - pointer to pointer to the initial memory read.
292*/
293int
294memio_open(const char* path,
295    int ioflags,
296    off_t igeto, size_t igetsz, size_t* sizehintp,
297    void* parameters,
298    ncio* *nciopp, void** const mempp)
299{
300    ncionciop = NULL;
301    int fd = -1;
302    int status = NC_NOERR;
303    int persist = (fIsSet(ioflags,NC_WRITE)?1:0);
304    int inmemory = (fIsSet(ioflags,NC_INMEMORY));
305    int oflags = 0;
306    NCMEMIOmemio = NULL;
307    size_t sizehint = 0;
308    off_t filesize = 0;
309    off_t red = 0;
310    char* pos = NULL;
311    NC_MEM_INFOmeminfo = (NC_MEM_INFO*)parameters;
312
313    if(path == NULL || strlen(path) == 0)
314        return NC_EINVAL;
315
316    assert(sizehintp != NULL);
317    sizehint = *sizehintp;
318
319    if(inmemory) {
320 filesize = meminfo->size;
321    } else {
322        /* Open the file,and make sure we can write it if needed */
323        oflags = (persist ? O_RDWR : O_RDONLY);
324#ifdef O_BINARY
325        fSet(oflagsO_BINARY);
326#endif
327        oflags |= O_EXCL;
328#ifdef vms
329        fd = open(pathoflags, 0, "ctx=stm");
330#else
331        fd  = open(pathoflagsOPENMODE);
332#endif
333#ifdef DEBUG
334        if(fd < 0) {
335            fprintf(stderr,"open failed: file=%s err=",path);
336            perror("");
337 }
338#endif
339        if(fd < 0) {status = errno; goto unwind_open;}
340
341        /* get current filesize  = max(|file|,initialize)*/
342        filesize = lseek(fd,0,SEEK_END);
343        if(filesize < 0) {status = errno; goto unwind_open;}
344        /* move pointer back to beginning of file */
345        (void)lseek(fd,0,SEEK_SET);
346        if(filesize < (off_t)sizehint)
347            filesize = (off_t)sizehint;
348    }
349
350    if(inmemory)
351        status = memio_new(pathioflagsfilesizememinfo->memory, &nciop, &memio);
352    else
353        status = memio_new(pathioflagsfilesizeNULL, &nciop, &memio);
354    if(status != NC_NOERR) {
355 if(fd >= 0)
356     close(fd);
357      return status;
358    }
359
360#ifdef DEBUG
361fprintf(stderr,"memio_open: initial memory: %lu/%lu\n",(unsigned long)memio->memory,(unsigned long)memio->alloc);
362#endif
363
364    if(!inmemory) {
365        /* Read the file into the memio memory */
366        /* We need to do multiple reads because there is no
367           guarantee that the amount read will be the full amount */
368        red = memio->size;
369        pos = memio->memory;
370        while(red > 0) {
371            ssize_t count = read(fdposred);
372            if(count < 0) {status = errno; goto unwind_open;}
373            if(count == 0) {status = NC_ENOTNC; goto unwind_open;}
374            red -= count;
375            pos += count;
376        }
377        (void)close(fd);
378    }
379
380    /* Use half the filesize as the blocksize ; why? */
381    sizehint = filesize/2;
382
383    fd = nc__pseudofd();
384    *((int* )&nciop->fd) = fd;
385
386    if(igetsz != 0)
387    {
388        status = nciop->get(nciop,
389                igetoigetsz,
390                0,
391                mempp);
392        if(status != NC_NOERR)
393            goto unwind_open;
394    }
395
396    if(sizehintp) *sizehintp = sizehint;
397    if(nciopp) *nciopp = nciop; else {ncio_close(nciop,0);}
398    return NC_NOERR;
399
400unwind_open:
401    if(fd >= 0)
402      close(fd);
403    memio_close(nciop,0);
404    return status;
405}
406
407/*
408 *  Get file size in bytes.
409 */
410static int
411memio_filesize(ncionciopoff_tfilesizep)
412{
413    NCMEMIOmemio;
414    if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
415    memio = (NCMEMIO*)nciop->pvt;
416    if(filesizep != NULL) *filesizep = memio->size;
417    return NC_NOERR;
418}
419
420/*
421 *  Sync any changes to disk, then truncate or extend file so its size
422 *  is length.  This is only intended to be called before close, if the
423 *  file is open for writing and the actual size does not match the
424 *  calculated size, perhaps as the result of having been previously
425 *  written in NOFILL mode.
426 */
427static int
428memio_pad_length(ncionciopoff_t length)
429{
430    NCMEMIOmemio;
431    if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
432    memio = (NCMEMIO*)nciop->pvt;
433
434    if(!fIsSet(nciop->ioflagsNC_WRITE))
435        return EPERM; /* attempt to write readonly file*/
436
437    if(memio->locked > 0)
438 return NC_EDISKLESS;
439
440    if(length > memio->alloc) {
441        /* Realloc the allocated memory to a multiple of the pagesize*/
442 off_t newsize = length;
443 void* newmem = NULL;
444 /* Round to a multiple of pagesize */
445 if((newsize % pagesize) != 0)
446     newsize += (pagesize - (newsize % pagesize));
447
448        newmem = (char*)realloc(memio->memory,newsize);
449        if(newmem == NULL) return NC_ENOMEM;
450
451 /* zero out the extra memory */
452        memset((void*)((char*)newmem+memio->alloc),0,(newsize - memio->alloc));
453
454#ifdef DEBUG
455fprintf(stderr,"realloc: %lu/%lu -> %lu/%lu\n",
456(unsigned long)memio->memory,(unsigned long)memio->alloc,
457(unsigned long)newmem,(unsigned long)newsize);
458#endif
459 memio->memory = newmem;
460 memio->alloc = newsize;
461    }
462    memio->size = length;
463    return NC_NOERR;
464}
465
466/*! Write out any dirty buffers to disk.
467
468  Write out any dirty buffers to disk and ensure that next read will get data from disk. Sync any changes, then close the open file associated with the ncio struct, and free its memory.
469
470  @param[in] nciop pointer to ncio to close.
471  @param[in] doUnlink if true, unlink file
472  @return NC_NOERR on success, error code on failure.
473*/
474
475static int
476memio_close(ncionciop, int doUnlink)
477{
478    int status = NC_NOERR;
479    NCMEMIOmemio ;
480    int fd = -1;
481    int inmemory = 0;
482
483    if(nciop == NULL || nciop->pvt == NULL) return NC_NOERR;
484
485    inmemory = (fIsSet(nciop->ioflags,NC_INMEMORY));
486    memio = (NCMEMIO*)nciop->pvt;
487    assert(memio != NULL);
488
489    /* See if the user wants the contents persisted to a file */
490    if(!inmemory && memio->persist) {
491        /* Try to open the file for writing */
492 int oflags = O_WRONLY|O_CREAT|O_TRUNC;
493#ifdef O_BINARY
494        fSet(oflagsO_BINARY);
495#endif
496 fd = open(nciop->pathoflagsOPENMODE);
497 if(fd >= 0) {
498     /* We need to do multiple writes because there is no
499               guarantee that the amount written will be the full amount */
500     off_t written = memio->size;
501     char* pos = memio->memory;
502     while(written > 0) {
503         ssize_t count = write(fdposwritten);
504         if(count < 0)
505             {status = errno; goto done;}
506         if(count == 0)
507             {status = NC_ENOTNC; goto done;}
508 written -= count;
509 pos += count;
510     }
511 } else
512     status = errno;
513     }
514
515done:
516    if(!inmemory && memio->memory != NULL)
517 free(memio->memory);
518    /* do cleanup  */
519    if(fd >= 0) (void)close(fd);
520    if(memio != NULL) free(memio);
521    if(nciop->path != NULL) free((char*)nciop->path);
522    free(nciop);
523    return status;
524}
525
526static int
527guarantee(ncionciopoff_t endpoint)
528{
529    NCMEMIOmemio = (NCMEMIO*)nciop->pvt;
530    if(endpoint > memio->alloc) {
531 /* extend the allocated memory and size */
532 int status = memio_pad_length(nciop,endpoint);
533 if(status != NC_NOERR) return status;
534    }
535    if(memio->size < endpoint)
536 memio->size = endpoint;
537    return NC_NOERR;
538}
539
540/*
541 * Request that the region (offset, extent)
542 * be made available through *vpp.
543 */
544static int
545memio_get(ncio* const nciopoff_t offset, size_t extent, int rflags, void** const vpp)
546{
547    int status = NC_NOERR;
548    NCMEMIOmemio;
549    if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
550    memio = (NCMEMIO*)nciop->pvt;
551    status = guarantee(nciopoffset+extent);
552    memio->locked++;
553    if(status != NC_NOERR) return status;
554    if(vpp) *vpp = memio->memory+offset;
555    return NC_NOERR;
556}
557
558/*
559 * Like memmove(), safely move possibly overlapping data.
560 */
561static int
562memio_move(ncio* const nciopoff_t tooff_t from, size_t nbytes, int ignored)
563{
564    int status = NC_NOERR;
565    NCMEMIOmemio;
566
567    if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
568    memio = (NCMEMIO*)nciop->pvt;
569    if(from < to) {
570       /* extend if "to" is not currently allocated */
571       status = guarantee(nciop,to+nbytes);
572       if(status != NC_NOERR) return status;
573    }
574    /* check for overlap */
575    if((to + nbytes) > from || (from + nbytes) > to) {
576 /* Ranges overlap */
577#ifdef HAVE_MEMMOVE
578        memmove((void*)(memio->memory+to),(void*)(memio->memory+from),nbytes);
579#else
580        off_t overlap;
581 off_t nbytes1;
582        if((from + nbytes) > to) {
583     overlap = ((from + nbytes) - to); /* # bytes of overlap */
584     nbytes1 = (nbytes - overlap); /* # bytes of non-overlap */
585     /* move the non-overlapping part */
586            memcpy((void*)(memio->memory+(to+overlap)),
587                   (void*)(memio->memory+(from+overlap)),
588    nbytes1);
589     /* move the overlapping part */
590     memcpy((void*)(memio->memory+to),
591                   (void*)(memio->memory+from),
592    overlap);
593 } else { /*((to + nbytes) > from) */
594     overlap = ((to + nbytes) - from); /* # bytes of overlap */
595     nbytes1 = (nbytes - overlap); /* # bytes of non-overlap */
596     /* move the non-overlapping part */
597            memcpy((void*)(memio->memory+to),
598                   (void*)(memio->memory+from),
599    nbytes1);
600     /* move the overlapping part */
601     memcpy((void*)(memio->memory+(to+nbytes1)),
602                   (void*)(memio->memory+(from+nbytes1)),
603    overlap);
604 }
605#endif
606    } else {/* no overlap */
607 memcpy((void*)(memio->memory+to),(void*)(memio->memory+from),nbytes);
608    }
609    return status;
610}
611
612static int
613memio_rel(ncio* const nciopoff_t offset, int rflags)
614{
615    NCMEMIOmemio;
616    if(nciop == NULL || nciop->pvt == NULL) return NC_EINVAL;
617    memio = (NCMEMIO*)nciop->pvt;
618    memio->locked--;
619    return NC_NOERR; /* do nothing */
620}
621
622/*
623 * Write out any dirty buffers to disk and
624 * ensure that next read will get data from disk.
625 */
626static int
627memio_sync(ncio* const nciop)
628{
629    return NC_NOERR; /* do nothing */
630}


HyperKWIC - Version 7.20DA executed at 11:37 on 27 Oct 2017 | Polyhedron Solutions - INTERNAL USE | COMMERCIAL (Any O/S) SN 4AKIed