Coverage Report

Created: 2023-05-28 06:42

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