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


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