Coverage Report

Created: 2025-11-15 08:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/netcdf-c-4.7.4/libsrc/posixio.c
Line
Count
Source
1
/*
2
 *  Copyright 2018, University Corporation for Atmospheric Research
3
 *  See netcdf/COPYRIGHT file for copying and redistribution conditions.
4
 */
5
/* $Id: posixio.c,v 1.89 2010/05/22 21:59:08 dmh Exp $ */
6
7
/* For MinGW Build */
8
9
#if HAVE_CONFIG_H
10
#include <config.h>
11
#endif
12
13
#include <stdio.h>
14
#include <assert.h>
15
#include <stdlib.h>
16
#include <errno.h>
17
#include <string.h>
18
19
#ifdef HAVE_FCNTL_H
20
#include <fcntl.h>
21
#endif
22
#ifdef HAVE_SYS_TYPES_H
23
#include <sys/types.h>
24
#endif
25
#ifdef HAVE_SYS_STAT_H
26
#include <sys/stat.h>
27
#endif
28
29
/* Windows platforms, including MinGW, Cygwin, Visual Studio */
30
#if defined(_WIN32) || defined(_WIN64)
31
#include <windows.h>
32
#include <winbase.h>
33
#include <io.h>
34
#endif
35
36
#ifdef HAVE_UNISTD_H
37
#include <unistd.h>
38
#endif
39
40
#ifndef NC_NOERR
41
#define NC_NOERR 0
42
#endif
43
44
#ifndef SEEK_SET
45
#define SEEK_SET 0
46
#define SEEK_CUR 1
47
#define SEEK_END 2
48
#endif
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
#undef MIN  /* system may define MIN somewhere and complain */
62
0
#define MIN(mm,nn) (((mm) < (nn)) ? (mm) : (nn))
63
64
#if !defined(NDEBUG) && !defined(X_INT_MAX)
65
#define  X_INT_MAX 2147483647
66
#endif
67
68
#if 0 /* !defined(NDEBUG) && !defined(X_ALIGN) */
69
#define  X_ALIGN 4
70
#else
71
#undef X_ALIGN
72
#endif
73
74
/* These are needed on mingw to get a dll to compile. They really
75
 * should be provided in sys/stats.h, but what the heck. Let's not be
76
 * too picky! */
77
#ifndef S_IRGRP
78
#define S_IRGRP   0000040
79
#endif
80
#ifndef S_IROTH
81
#define S_IROTH   0000004
82
#endif
83
#ifndef S_IWGRP
84
#define S_IWGRP   0000020
85
#endif
86
#ifndef S_IWOTH
87
#define S_IWOTH   0000002
88
#endif
89
90
/*Forward*/
91
static int ncio_px_filesize(ncio *nciop, off_t *filesizep);
92
static int ncio_px_pad_length(ncio *nciop, off_t length);
93
static int ncio_px_close(ncio *nciop, int doUnlink);
94
static int ncio_spx_close(ncio *nciop, int doUnlink);
95
96
97
/*
98
 * Define the following for debugging.
99
 */
100
/* #define ALWAYS_NC_SHARE 1 */
101
102
/* Begin OS */
103
104
#ifndef POSIXIO_DEFAULT_PAGESIZE
105
0
#define POSIXIO_DEFAULT_PAGESIZE 4096
106
#endif
107
108
/*! Cross-platform file length.
109
 *
110
 * Some versions of Visual Studio are throwing errno 132
111
 * when fstat is used on large files.  This function is
112
 * an attempt to get around that.
113
 *
114
 * @par fd File Descriptor.
115
 * @return -1 on error, length of file (in bytes) otherwise.
116
 */
117
0
static off_t nc_get_filelen(const int fd) {
118
119
0
  off_t flen;
120
121
#ifdef HAVE_FILE_LENGTH_I64
122
  __int64 file_len = 0;
123
  if ((file_len = _filelengthi64(fd)) < 0) {
124
    return file_len;
125
  }
126
  flen = (off_t)file_len;
127
128
#else
129
0
  int res = 0;
130
0
  struct stat sb;
131
0
  if((res = fstat(fd,&sb)) <0)
132
0
    return res;
133
134
0
  flen = sb.st_size;
135
0
#endif
136
137
0
  return flen;
138
139
0
}
140
141
142
/*
143
 * What is the system pagesize?
144
 */
145
static size_t
146
pagesize(void)
147
21.7k
{
148
21.7k
  size_t pgsz;
149
#if defined(_WIN32) || defined(_WIN64)
150
  SYSTEM_INFO info;
151
#endif
152
/* Hmm, aren't standards great? */
153
#if defined(_SC_PAGE_SIZE) && !defined(_SC_PAGESIZE)
154
#define _SC_PAGESIZE _SC_PAGE_SIZE
155
#endif
156
157
  /* For MinGW Builds */
158
#if defined(_WIN32) || defined(_WIN64)
159
  GetSystemInfo(&info);
160
  pgsz = (size_t)info.dwPageSize;
161
#elif defined(_SC_PAGESIZE)
162
21.7k
  pgsz = (size_t)sysconf(_SC_PAGESIZE);
163
#elif defined(HAVE_GETPAGESIZE)
164
  pgsz = (size_t) getpagesize();
165
#endif
166
21.7k
  if(pgsz > 0)
167
21.7k
    return (size_t) pgsz;
168
0
   return (size_t)POSIXIO_DEFAULT_PAGESIZE;
169
21.7k
}
170
171
/*
172
 * What is the preferred I/O block size?
173
 */
174
static size_t
175
blksize(int fd)
176
21.7k
{
177
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
178
#ifdef HAVE_SYS_STAT_H
179
  struct stat sb;
180
  if (fstat(fd, &sb) > -1)
181
  {
182
    if(sb.st_blksize >= 8192)
183
      return (size_t) sb.st_blksize;
184
    return 8192;
185
  }
186
  /* else, silent in the face of error */
187
#else
188
  NC_UNUSED(fd);
189
#endif
190
#else
191
21.7k
  NC_UNUSED(fd);
192
21.7k
#endif
193
21.7k
  return (size_t) 2 * pagesize();
194
21.7k
}
195
196
197
/*
198
 * Sortof like ftruncate, except won't make the
199
 * file shorter.
200
 */
201
static int
202
fgrow(const int fd, const off_t len)
203
10.8k
{
204
10.8k
  struct stat sb;
205
10.8k
  if (fstat(fd, &sb) < 0)
206
0
    return errno;
207
10.8k
  if (len < sb.st_size)
208
0
    return NC_NOERR;
209
10.8k
  {
210
10.8k
      const long dumb = 0;
211
      /* we don't use ftruncate() due to problem with FAT32 file systems */
212
      /* cache current position */
213
10.8k
      const off_t pos = lseek(fd, 0, SEEK_CUR);
214
10.8k
      if(pos < 0)
215
0
    return errno;
216
10.8k
      if (lseek(fd, len-sizeof(dumb), SEEK_SET) < 0)
217
0
    return errno;
218
10.8k
      if(write(fd, &dumb, sizeof(dumb)) < 0)
219
0
    return errno;
220
10.8k
      if (lseek(fd, pos, SEEK_SET) < 0)
221
0
    return errno;
222
10.8k
  }
223
10.8k
  return NC_NOERR;
224
10.8k
}
225
226
227
/*
228
 * Sortof like ftruncate, except won't make the file shorter.  Differs
229
 * from fgrow by only writing one byte at designated seek position, if
230
 * needed.
231
 */
232
static int
233
fgrow2(const int fd, const off_t len)
234
0
{
235
236
237
  /* There is a problem with fstat on Windows based systems
238
     which manifests (so far) when Config RELEASE is built.
239
     Use _filelengthi64 isntead.
240
241
     See https://github.com/Unidata/netcdf-c/issues/188
242
243
  */
244
245
246
0
  off_t file_len = nc_get_filelen(fd);
247
0
  if(file_len < 0) return errno;
248
0
  if(len <= file_len)
249
0
    return NC_NOERR;
250
0
  {
251
0
    const char dumb = 0;
252
      /* we don't use ftruncate() due to problem with FAT32 file systems */
253
      /* cache current position */
254
0
      const off_t pos = lseek(fd, 0, SEEK_CUR);
255
0
      if(pos < 0)
256
0
    return errno;
257
0
      if (lseek(fd, len-1, SEEK_SET) < 0)
258
0
    return errno;
259
0
      if(write(fd, &dumb, sizeof(dumb)) < 0)
260
0
    return errno;
261
0
      if (lseek(fd, pos, SEEK_SET) < 0)
262
0
    return errno;
263
0
  }
264
0
  return NC_NOERR;
265
0
}
266
/* End OS */
267
/* Begin px */
268
269
/* The px_ functions are for posix systems, when NC_SHARE is not in
270
   effect. */
271
272
/* Write out a "page" of data to the file. The size of the page
273
   (i.e. the extent) varies.
274
275
   nciop - pointer to the file metadata.
276
   offset - where in the file should this page be written.
277
   extent - how many bytes should be written.
278
   vp - pointer to the data to write.
279
   posp - pointer to current position in file, updated after write.
280
*/
281
static int
282
px_pgout(ncio *const nciop,
283
  off_t const offset,  const size_t extent,
284
  void *const vp, off_t *posp)
285
1.38M
{
286
1.38M
    ssize_t partial;
287
1.38M
    size_t nextent;
288
1.38M
    char *nvp;
289
#ifdef X_ALIGN
290
  assert(offset % X_ALIGN == 0);
291
#endif
292
293
1.38M
  assert(*posp == OFF_NONE || *posp == lseek(nciop->fd, 0, SEEK_CUR));
294
295
1.38M
  if(*posp != offset)
296
1.37M
  {
297
1.37M
    if(lseek(nciop->fd, offset, SEEK_SET) != offset)
298
0
    {
299
0
      return errno;
300
0
    }
301
1.37M
    *posp = offset;
302
1.37M
  }
303
  /* Old write, didn't handle partial writes correctly */
304
  /* if(write(nciop->fd, vp, extent) != (ssize_t) extent) */
305
  /* { */
306
  /*  return errno; */
307
  /* } */
308
1.38M
  nextent = extent;
309
1.38M
        nvp = vp;
310
1.38M
  while((partial = write(nciop->fd, nvp, nextent)) != -1) {
311
1.38M
      if(partial == nextent)
312
1.38M
    break;
313
0
      nvp += partial;
314
0
      nextent -= partial;
315
0
  }
316
1.38M
  if(partial == -1)
317
0
      return errno;
318
1.38M
  *posp += extent;
319
320
1.38M
  return NC_NOERR;
321
1.38M
}
322
323
/*! Read in a page of data.
324
325
  @param[in] nciop  A pointer to the ncio struct for this file.
326
  @param[in] offset The byte offset in file where read starts.
327
  @param[in] extent The size of the page that will be read.
328
  @param[in] vp     A pointer to where the data will end up.
329
330
  @param[in,out] nreadp Returned number of bytes actually read (may be less than extent).
331
  @param[in,out] posp The pointer to current position in file, updated after read.
332
  @return Return 0 on success, otherwise an error code.
333
*/
334
static int
335
px_pgin(ncio *const nciop,
336
  off_t const offset, const size_t extent,
337
  void *const vp, size_t *nreadp, off_t *posp)
338
2.91M
{
339
2.91M
  int status;
340
2.91M
  ssize_t nread;
341
#ifdef X_ALIGN
342
  assert(offset % X_ALIGN == 0);
343
  assert(extent % X_ALIGN == 0);
344
#endif
345
    /* *posp == OFF_NONE (-1) on first call. This
346
       is problematic because lseek also returns -1
347
       on error. Use errno instead. */
348
2.91M
    if(*posp != OFF_NONE && *posp != lseek(nciop->fd, 0, SEEK_CUR)) {
349
0
      if(errno) {
350
0
        status = errno;
351
0
        printf("Error %d: %s\n",errno,strerror(errno));
352
0
        return status;
353
0
      }
354
0
    }
355
356
2.91M
  if(*posp != offset)
357
2.73M
  {
358
2.73M
    if(lseek(nciop->fd, offset, SEEK_SET) != offset)
359
0
    {
360
0
      status = errno;
361
0
      return status;
362
0
    }
363
2.73M
    *posp = offset;
364
2.73M
  }
365
366
2.91M
  errno = 0;
367
    /* Handle the case where the read is interrupted
368
       by a signal (see NCF-337,
369
       http://pubs.opengroup.org/onlinepubs/009695399/functions/read.html)
370
371
       When this happens, nread will (should) be the bytes read, and
372
       errno will be set to EINTR.  On older systems nread might be -1.
373
       If this is the case, there's not a whole lot we can do about it
374
       as we can't compute any offsets, so we will attempt to read again.
375
       This *feels* like it could lead to an infinite loop, but it shouldn't
376
       unless the read is being constantly interrupted by a signal, and is
377
       on an older system which returns -1 instead of bytexs read.
378
379
       The case where it's a short read is already handled by the function
380
       (according to the comment below, at least). */
381
2.91M
    do {
382
2.91M
      nread = read(nciop->fd,vp,extent);
383
2.91M
    } while (nread == -1 && errno == EINTR);
384
385
386
2.91M
    if(nread != (ssize_t)extent) {
387
1.46M
      status = errno;
388
1.46M
      if( nread == -1 || (status != EINTR && status != NC_NOERR))
389
0
        return status;
390
      /* else it's okay we read less than asked for */
391
1.46M
      (void) memset((char *)vp + nread, 0, (ssize_t)extent - nread);
392
1.46M
    }
393
394
2.91M
    *nreadp = nread;
395
2.91M
  *posp += nread;
396
397
2.91M
  return NC_NOERR;
398
2.91M
}
399
400
/* This struct is for POSIX systems, with NC_SHARE not in effect. If
401
   NC_SHARE is used, see ncio_spx.
402
403
   blksz - block size for reads and writes to file.
404
   pos - current read/write position in file.
405
   bf_offset - file offset corresponding to start of memory buffer
406
   bf_extent - number of bytes in I/O request
407
   bf_cnt - number of bytes available in buffer
408
   bf_base - pointer to beginning of buffer.
409
   bf_rflags - buffer region flags (defined in ncio.h) tell the lock
410
   status, read/write permissions, and modification status of regions
411
   of data in the buffer.
412
   bf_refcount - buffer reference count.
413
   slave - used in moves.
414
*/
415
typedef struct ncio_px {
416
  size_t blksz;
417
  off_t pos;
418
  /* buffer */
419
  off_t bf_offset;
420
  size_t  bf_extent;
421
  size_t  bf_cnt;
422
  void  *bf_base;
423
  int bf_rflags;
424
  int bf_refcount;
425
  /* chain for double buffering in px_move */
426
  struct ncio_px *slave;
427
} ncio_px;
428
429
430
/*ARGSUSED*/
431
/* This function indicates the file region starting at offset may be
432
   released.
433
434
   This is for POSIX, without NC_SHARE.  If called with RGN_MODIFIED
435
   flag, sets the modified flag in pxp->bf_rflags and decrements the
436
   reference count.
437
438
   pxp - pointer to posix non-share ncio_px struct.
439
440
   offset - file offset for beginning of to region to be
441
   released.
442
443
   rflags - only RGN_MODIFIED is relevant to this function, others ignored
444
*/
445
static int
446
px_rel(ncio_px *const pxp, off_t offset, int rflags)
447
4.56M
{
448
4.56M
  assert(pxp->bf_offset <= offset
449
4.56M
     && offset < pxp->bf_offset + (off_t) pxp->bf_extent);
450
4.56M
  assert(pIf(fIsSet(rflags, RGN_MODIFIED),
451
4.56M
    fIsSet(pxp->bf_rflags, RGN_WRITE)));
452
4.56M
  NC_UNUSED(offset);
453
454
4.56M
  if(fIsSet(rflags, RGN_MODIFIED))
455
1.61M
  {
456
1.61M
    fSet(pxp->bf_rflags, RGN_MODIFIED);
457
1.61M
  }
458
4.56M
  pxp->bf_refcount--;
459
460
4.56M
  return NC_NOERR;
461
4.56M
}
462
463
/* This function indicates the file region starting at offset may be
464
   released.  Each read or write to the file is bracketed by a call to
465
   the "get" region function and a call to the "rel" region function.
466
   If you only read from the memory region, release it with a flag of
467
   0, if you modify the region, release it with a flag of
468
   RGN_MODIFIED.
469
470
   For POSIX system, without NC_SHARE, this becomes the rel function
471
   pointed to by the ncio rel function pointer. It merely checks for
472
   file write permission, then calls px_rel to do everything.
473
474
   nciop - pointer to ncio struct.
475
   offset - num bytes from beginning of buffer to region to be
476
   released.
477
   rflags - only RGN_MODIFIED is relevant to this function, others ignored
478
*/
479
static int
480
ncio_px_rel(ncio *const nciop, off_t offset, int rflags)
481
4.56M
{
482
4.56M
  ncio_px *const pxp = (ncio_px *)nciop->pvt;
483
484
4.56M
  if(fIsSet(rflags, RGN_MODIFIED) && !fIsSet(nciop->ioflags, NC_WRITE))
485
0
    return EPERM; /* attempt to write readonly file */
486
487
4.56M
  return px_rel(pxp, offset, rflags);
488
4.56M
}
489
490
/* POSIX get. This will "make a region available." Since we're using
491
   buffered IO, this means that if needed, we'll fetch a new page from
492
   the file, otherwise, just return a pointer to what's in memory
493
   already.
494
495
   nciop - pointer to ncio struct, containing file info.
496
   pxp - pointer to ncio_px struct, which contains special metadate
497
   for posix files without NC_SHARE.
498
   offset - start byte of region to get.
499
   extent - how many bytes to read.
500
   rflags - One of the RGN_* flags defined in ncio.h.
501
   vpp - pointer to pointer that will receive data.
502
503
   NOTES:
504
505
   * For blkoffset round offset down to the nearest pxp->blksz. This
506
   provides the offset (in bytes) to the beginning of the block that
507
   holds the current offset.
508
509
   * diff tells how far into the current block we are.
510
511
   * For blkextent round up to the number of bytes at the beginning of
512
   the next block, after the one that holds our current position, plus
513
   whatever extra (i.e. the extent) that we are about to grab.
514
515
   * The blkextent can't be more than twice the pxp->blksz. That's
516
   because the pxp->blksize is the sizehint, and in ncio_px_init2 the
517
   buffer (pointed to by pxp->bf-base) is allocated with 2 *
518
   *sizehintp. This is checked (unnecessarily) more than once in
519
   asserts.
520
521
   * If this is called on a newly opened file, pxp->bf_offset will be
522
   OFF_NONE and we'll jump to label pgin to immediately read in a
523
   page.
524
*/
525
static int
526
px_get(ncio *const nciop, ncio_px *const pxp,
527
    off_t offset, size_t extent,
528
    int rflags,
529
    void **const vpp)
530
4.56M
{
531
4.56M
  int status = NC_NOERR;
532
533
4.56M
  const off_t blkoffset = _RNDDOWN(offset, (off_t)pxp->blksz);
534
4.56M
  off_t diff = (size_t)(offset - blkoffset);
535
4.56M
  off_t blkextent = _RNDUP(diff + extent, pxp->blksz);
536
537
4.56M
  assert(extent != 0);
538
4.56M
  assert(extent < X_INT_MAX); /* sanity check */
539
4.56M
  assert(offset >= 0); /* sanity check */
540
541
4.56M
  if(2 * pxp->blksz < blkextent)
542
0
    return E2BIG; /* TODO: temporary kludge */
543
4.56M
  if(pxp->bf_offset == OFF_NONE)
544
10.8k
  {
545
    /* Uninitialized */
546
10.8k
    if(pxp->bf_base == NULL)
547
0
    {
548
0
      assert(pxp->bf_extent == 0);
549
0
      assert(blkextent <= 2 * pxp->blksz);
550
0
      pxp->bf_base = malloc(2 * pxp->blksz);
551
0
      if(pxp->bf_base == NULL)
552
0
        return ENOMEM;
553
0
    }
554
10.8k
    goto pgin;
555
10.8k
  }
556
  /* else */
557
4.56M
  assert(blkextent <= 2 * pxp->blksz);
558
559
4.55M
  if(blkoffset == pxp->bf_offset)
560
1.27M
  {
561
    /* hit */
562
1.27M
    if(blkextent > pxp->bf_extent)
563
2.41k
    {
564
      /* page in upper */
565
2.41k
      void *const middle =
566
2.41k
        (void *)((char *)pxp->bf_base + pxp->blksz);
567
2.41k
      assert(pxp->bf_extent == pxp->blksz);
568
2.41k
      status = px_pgin(nciop,
569
2.41k
         pxp->bf_offset + (off_t)pxp->blksz,
570
2.41k
         pxp->blksz,
571
2.41k
         middle,
572
2.41k
         &pxp->bf_cnt,
573
2.41k
         &pxp->pos);
574
2.41k
      if(status != NC_NOERR)
575
0
        return status;
576
2.41k
      pxp->bf_extent = 2 * pxp->blksz;
577
2.41k
      pxp->bf_cnt += pxp->blksz;
578
2.41k
    }
579
1.27M
    goto done;
580
1.27M
  }
581
  /* else */
582
583
3.27M
  if(pxp->bf_extent > pxp->blksz
584
1.82M
     && blkoffset == pxp->bf_offset + (off_t)pxp->blksz)
585
1.76M
  {
586
    /* hit in upper half */
587
1.76M
    if(blkextent == pxp->blksz)
588
372k
    {
589
      /* all in upper half, no fault needed */
590
372k
      diff += pxp->blksz;
591
372k
      goto done;
592
372k
    }
593
    /* else */
594
1.39M
    if(pxp->bf_cnt > pxp->blksz)
595
1.39M
    {
596
      /* data in upper half */
597
1.39M
      void *const middle =
598
1.39M
        (void *)((char *)pxp->bf_base + pxp->blksz);
599
1.39M
      assert(pxp->bf_extent == 2 * pxp->blksz);
600
1.39M
      if(fIsSet(pxp->bf_rflags, RGN_MODIFIED))
601
1.35M
      {
602
        /* page out lower half */
603
1.35M
        assert(pxp->bf_refcount <= 0);
604
1.35M
        status = px_pgout(nciop,
605
1.35M
          pxp->bf_offset,
606
1.35M
          pxp->blksz,
607
1.35M
          pxp->bf_base,
608
1.35M
          &pxp->pos);
609
1.35M
        if(status != NC_NOERR)
610
0
          return status;
611
1.35M
      }
612
1.39M
      pxp->bf_cnt -= pxp->blksz;
613
      /* copy upper half into lower half */
614
1.39M
      (void) memcpy(pxp->bf_base, middle, pxp->bf_cnt);
615
1.39M
    }
616
0
    else    /* added to fix nofill bug */
617
0
    {
618
0
      assert(pxp->bf_extent == 2 * pxp->blksz);
619
      /* still have to page out lower half, if modified */
620
0
      if(fIsSet(pxp->bf_rflags, RGN_MODIFIED))
621
0
      {
622
0
        assert(pxp->bf_refcount <= 0);
623
0
        status = px_pgout(nciop,
624
0
          pxp->bf_offset,
625
0
          pxp->blksz,
626
0
          pxp->bf_base,
627
0
          &pxp->pos);
628
0
        if(status != NC_NOERR)
629
0
          return status;
630
0
      }
631
0
    }
632
1.39M
    pxp->bf_offset = blkoffset;
633
    /* pxp->bf_extent = pxp->blksz; */
634
635
1.39M
    assert(blkextent == 2 * pxp->blksz);
636
1.39M
    {
637
      /* page in upper */
638
1.39M
      void *const middle =
639
1.39M
        (void *)((char *)pxp->bf_base + pxp->blksz);
640
1.39M
      status = px_pgin(nciop,
641
1.39M
         pxp->bf_offset + (off_t)pxp->blksz,
642
1.39M
         pxp->blksz,
643
1.39M
         middle,
644
1.39M
         &pxp->bf_cnt,
645
1.39M
         &pxp->pos);
646
1.39M
      if(status != NC_NOERR)
647
0
        return status;
648
1.39M
      pxp->bf_extent = 2 * pxp->blksz;
649
1.39M
      pxp->bf_cnt += pxp->blksz;
650
1.39M
    }
651
0
    goto done;
652
1.39M
  }
653
  /* else */
654
655
1.50M
  if(blkoffset == pxp->bf_offset - (off_t)pxp->blksz)
656
3.41k
  {
657
    /* wants the page below */
658
3.41k
    void *const middle =
659
3.41k
      (void *)((char *)pxp->bf_base + pxp->blksz);
660
3.41k
    size_t upper_cnt = 0;
661
3.41k
    if(pxp->bf_cnt > pxp->blksz)
662
1.35k
    {
663
      /* data in upper half */
664
1.35k
      assert(pxp->bf_extent == 2 * pxp->blksz);
665
1.35k
      if(fIsSet(pxp->bf_rflags, RGN_MODIFIED))
666
1.22k
      {
667
        /* page out upper half */
668
1.22k
        assert(pxp->bf_refcount <= 0);
669
1.22k
        status = px_pgout(nciop,
670
1.22k
          pxp->bf_offset + (off_t)pxp->blksz,
671
1.22k
          pxp->bf_cnt - pxp->blksz,
672
1.22k
          middle,
673
1.22k
          &pxp->pos);
674
1.22k
        if(status != NC_NOERR)
675
0
          return status;
676
1.22k
      }
677
1.35k
      pxp->bf_cnt = pxp->blksz;
678
1.35k
      pxp->bf_extent = pxp->blksz;
679
1.35k
    }
680
3.41k
    if(pxp->bf_cnt > 0)
681
3.41k
    {
682
      /* copy lower half into upper half */
683
3.41k
      (void) memcpy(middle, pxp->bf_base, pxp->blksz);
684
3.41k
      upper_cnt = pxp->bf_cnt;
685
3.41k
    }
686
    /* read page below into lower half */
687
3.41k
    status = px_pgin(nciop,
688
3.41k
       blkoffset,
689
3.41k
       pxp->blksz,
690
3.41k
       pxp->bf_base,
691
3.41k
       &pxp->bf_cnt,
692
3.41k
       &pxp->pos);
693
3.41k
    if(status != NC_NOERR)
694
0
      return status;
695
3.41k
    pxp->bf_offset = blkoffset;
696
3.41k
    if(upper_cnt != 0)
697
3.41k
    {
698
3.41k
      pxp->bf_extent = 2 * pxp->blksz;
699
3.41k
      pxp->bf_cnt = pxp->blksz + upper_cnt;
700
3.41k
    }
701
0
    else
702
0
    {
703
0
      pxp->bf_extent = pxp->blksz;
704
0
    }
705
3.41k
    goto done;
706
3.41k
  }
707
  /* else */
708
709
  /* no overlap */
710
1.50M
  if(fIsSet(pxp->bf_rflags, RGN_MODIFIED))
711
21.5k
  {
712
21.5k
    assert(pxp->bf_refcount <= 0);
713
21.5k
    status = px_pgout(nciop,
714
21.5k
      pxp->bf_offset,
715
21.5k
      pxp->bf_cnt,
716
21.5k
      pxp->bf_base,
717
21.5k
      &pxp->pos);
718
21.5k
    if(status != NC_NOERR)
719
0
      return status;
720
21.5k
    pxp->bf_rflags = 0;
721
21.5k
  }
722
723
1.51M
pgin:
724
1.51M
  status = px_pgin(nciop,
725
1.51M
     blkoffset,
726
1.51M
     blkextent,
727
1.51M
     pxp->bf_base,
728
1.51M
     &pxp->bf_cnt,
729
1.51M
     &pxp->pos);
730
1.51M
  if(status != NC_NOERR)
731
0
    return status;
732
1.51M
   pxp->bf_offset = blkoffset;
733
1.51M
   pxp->bf_extent = blkextent;
734
735
4.56M
done:
736
4.56M
  extent += diff;
737
4.56M
  if(pxp->bf_cnt < extent)
738
1.43M
    pxp->bf_cnt = extent;
739
4.56M
  assert(pxp->bf_cnt <= pxp->bf_extent);
740
741
4.56M
  pxp->bf_rflags |= rflags;
742
4.56M
  pxp->bf_refcount++;
743
744
4.56M
    *vpp = (void *)((signed char*)pxp->bf_base + diff);
745
4.56M
  return NC_NOERR;
746
4.56M
}
747
748
/* Request that the region (offset, extent) be made available through
749
   *vpp.
750
751
   This function converts a file region specified by an offset and
752
   extent to a memory pointer. The region may be locked until the
753
   corresponding call to rel().
754
755
   For POSIX systems, without NC_SHARE. This function gets a page of
756
   size extent?
757
758
   This is a wrapper for the function px_get, which does all the heavy
759
   lifting.
760
761
   nciop - pointer to ncio struct for this file.
762
   offset - offset (from beginning of file?) to the data we want to
763
   read.
764
   extent - the number of bytes to read from the file.
765
   rflags - One of the RGN_* flags defined in ncio.h.
766
   vpp - handle to point at data when it's been read.
767
*/
768
static int
769
ncio_px_get(ncio *const nciop,
770
    off_t offset, size_t extent,
771
    int rflags,
772
    void **const vpp)
773
4.56M
{
774
4.56M
  ncio_px *const pxp = (ncio_px *)nciop->pvt;
775
776
4.56M
  if(fIsSet(rflags, RGN_WRITE) && !fIsSet(nciop->ioflags, NC_WRITE))
777
0
    return EPERM; /* attempt to write readonly file */
778
779
  /* reclaim space used in move */
780
4.56M
  if(pxp->slave != NULL)
781
0
  {
782
0
    if(pxp->slave->bf_base != NULL)
783
0
    {
784
0
      free(pxp->slave->bf_base);
785
0
      pxp->slave->bf_base = NULL;
786
0
      pxp->slave->bf_extent = 0;
787
0
      pxp->slave->bf_offset = OFF_NONE;
788
0
    }
789
0
    free(pxp->slave);
790
0
    pxp->slave = NULL;
791
0
  }
792
4.56M
  return px_get(nciop, pxp, offset, extent, rflags, vpp);
793
4.56M
}
794
795
796
/* ARGSUSED */
797
static int
798
px_double_buffer(ncio *const nciop, off_t to, off_t from,
799
      size_t nbytes, int rflags)
800
0
{
801
0
  ncio_px *const pxp = (ncio_px *)nciop->pvt;
802
0
  int status = NC_NOERR;
803
0
  void *src;
804
0
  void *dest;
805
0
  NC_UNUSED(rflags);
806
807
#if INSTRUMENT
808
fprintf(stderr, "\tdouble_buffr %ld %ld %ld\n",
809
     (long)to, (long)from, (long)nbytes);
810
#endif
811
0
  status = px_get(nciop, pxp, to, nbytes, RGN_WRITE,
812
0
      &dest);
813
0
  if(status != NC_NOERR)
814
0
    return status;
815
816
0
  if(pxp->slave == NULL)
817
0
  {
818
0
    pxp->slave = (ncio_px *) malloc(sizeof(ncio_px));
819
0
    if(pxp->slave == NULL)
820
0
      return ENOMEM;
821
822
0
    pxp->slave->blksz = pxp->blksz;
823
    /* pos done below */
824
0
    pxp->slave->bf_offset = pxp->bf_offset;
825
0
    pxp->slave->bf_extent = pxp->bf_extent;
826
0
    pxp->slave->bf_cnt = pxp->bf_cnt;
827
0
    pxp->slave->bf_base = malloc(2 * pxp->blksz);
828
0
    if(pxp->slave->bf_base == NULL)
829
0
      return ENOMEM;
830
0
    (void) memcpy(pxp->slave->bf_base, pxp->bf_base,
831
0
       pxp->bf_extent);
832
0
    pxp->slave->bf_rflags = 0;
833
0
    pxp->slave->bf_refcount = 0;
834
0
    pxp->slave->slave = NULL;
835
0
  }
836
837
0
  pxp->slave->pos = pxp->pos;
838
0
  status = px_get(nciop, pxp->slave, from, nbytes, 0,
839
0
      &src);
840
0
  if(status != NC_NOERR)
841
0
    return status;
842
0
  if(pxp->pos != pxp->slave->pos)
843
0
  {
844
    /* position changed, sync */
845
0
    pxp->pos = pxp->slave->pos;
846
0
  }
847
848
0
  (void) memcpy(dest, src, nbytes);
849
850
0
  (void)px_rel(pxp->slave, from, 0);
851
0
  (void)px_rel(pxp, to, RGN_MODIFIED);
852
853
0
  return status;
854
0
}
855
856
/* Like memmove(), safely move possibly overlapping data.
857
858
   Copy one region to another without making anything available to
859
   higher layers. May be just implemented in terms of get() and rel(),
860
   or may be tricky to be efficient. Only used in by nc_enddef()
861
   after redefinition.
862
863
   nciop - pointer to ncio struct with file info.
864
   to - src for move?
865
   from - dest for move?
866
   nbytes - number of bytes to move.
867
   rflags - One of the RGN_* flags defined in ncio.h. The only
868
   reasonable flag value is RGN_NOLOCK.
869
*/
870
static int
871
ncio_px_move(ncio *const nciop, off_t to, off_t from,
872
      size_t nbytes, int rflags)
873
0
{
874
0
  ncio_px *const pxp = (ncio_px *)nciop->pvt;
875
0
  int status = NC_NOERR;
876
0
  off_t lower;
877
0
  off_t upper;
878
0
  char *base;
879
0
  size_t diff;
880
0
  size_t extent;
881
882
0
  if(to == from)
883
0
    return NC_NOERR; /* NOOP */
884
885
0
  if(fIsSet(rflags, RGN_WRITE) && !fIsSet(nciop->ioflags, NC_WRITE))
886
0
    return EPERM; /* attempt to write readonly file */
887
888
0
  rflags &= RGN_NOLOCK; /* filter unwanted flags */
889
890
0
  if(to > from)
891
0
  {
892
    /* growing */
893
0
    lower = from;
894
0
    upper = to;
895
0
  }
896
0
  else
897
0
  {
898
    /* shrinking */
899
0
    lower = to;
900
0
    upper = from;
901
0
  }
902
0
  diff = (size_t)(upper - lower);
903
0
  extent = diff + nbytes;
904
905
#if INSTRUMENT
906
fprintf(stderr, "ncio_px_move %ld %ld %ld %ld %ld\n",
907
     (long)to, (long)from, (long)nbytes, (long)lower, (long)extent);
908
#endif
909
0
  if(extent > pxp->blksz)
910
0
  {
911
0
    size_t remaining = nbytes;
912
913
0
if(to > from)
914
0
{
915
0
    off_t frm = from + nbytes;
916
0
    off_t toh = to + nbytes;
917
0
    for(;;)
918
0
    {
919
0
      size_t loopextent = MIN(remaining, pxp->blksz);
920
0
      frm -= loopextent;
921
0
      toh -= loopextent;
922
923
0
      status = px_double_buffer(nciop, toh, frm,
924
0
          loopextent, rflags) ;
925
0
      if(status != NC_NOERR)
926
0
        return status;
927
0
      remaining -= loopextent;
928
929
0
      if(remaining == 0)
930
0
        break; /* normal loop exit */
931
0
    }
932
0
}
933
0
else
934
0
{
935
0
    for(;;)
936
0
    {
937
0
      size_t loopextent = MIN(remaining, pxp->blksz);
938
939
0
      status = px_double_buffer(nciop, to, from,
940
0
          loopextent, rflags) ;
941
0
      if(status != NC_NOERR)
942
0
        return status;
943
0
      remaining -= loopextent;
944
945
0
      if(remaining == 0)
946
0
        break; /* normal loop exit */
947
0
      to += loopextent;
948
0
      from += loopextent;
949
0
    }
950
0
}
951
0
    return NC_NOERR;
952
0
  }
953
954
#if INSTRUMENT
955
fprintf(stderr, "\tncio_px_move small\n");
956
#endif
957
0
  status = px_get(nciop, pxp, lower, extent, RGN_WRITE|rflags,
958
0
      (void **)&base);
959
960
0
  if(status != NC_NOERR)
961
0
    return status;
962
963
0
  if(to > from)
964
0
    (void) memmove(base + diff, base, nbytes);
965
0
  else
966
0
    (void) memmove(base, base + diff, nbytes);
967
968
0
  (void) px_rel(pxp, lower, RGN_MODIFIED);
969
970
0
  return status;
971
0
}
972
973
974
/* Flush any buffers to disk. May be a no-op on if I/O is unbuffered.
975
   This function is used when NC_SHARE is NOT used.
976
*/
977
static int
978
ncio_px_sync(ncio *const nciop)
979
45.5k
{
980
45.5k
  ncio_px *const pxp = (ncio_px *)nciop->pvt;
981
45.5k
  int status = NC_NOERR;
982
45.5k
  if(fIsSet(pxp->bf_rflags, RGN_MODIFIED))
983
12.0k
  {
984
12.0k
    assert(pxp->bf_refcount <= 0);
985
12.0k
    status = px_pgout(nciop, pxp->bf_offset,
986
12.0k
      pxp->bf_cnt,
987
12.0k
      pxp->bf_base, &pxp->pos);
988
12.0k
    if(status != NC_NOERR)
989
0
      return status;
990
12.0k
    pxp->bf_rflags = 0;
991
12.0k
  }
992
33.4k
  else if (!fIsSet(pxp->bf_rflags, RGN_WRITE))
993
33.4k
  {
994
      /*
995
       * The dataset is readonly.  Invalidate the buffers so
996
       * that the next ncio_px_get() will actually read data.
997
       */
998
33.4k
      pxp->bf_offset = OFF_NONE;
999
33.4k
      pxp->bf_cnt = 0;
1000
33.4k
  }
1001
45.5k
  return status;
1002
45.5k
}
1003
1004
/* Internal function called at close to
1005
   free up anything hanging off pvt.
1006
*/
1007
static void
1008
ncio_px_freepvt(void *const pvt)
1009
21.7k
{
1010
21.7k
  ncio_px *const pxp = (ncio_px *)pvt;
1011
21.7k
  if(pxp == NULL)
1012
0
    return;
1013
1014
21.7k
  if(pxp->slave != NULL)
1015
0
  {
1016
0
    if(pxp->slave->bf_base != NULL)
1017
0
    {
1018
0
      free(pxp->slave->bf_base);
1019
0
      pxp->slave->bf_base = NULL;
1020
0
      pxp->slave->bf_extent = 0;
1021
0
      pxp->slave->bf_offset = OFF_NONE;
1022
0
    }
1023
0
    free(pxp->slave);
1024
0
    pxp->slave = NULL;
1025
0
  }
1026
1027
21.7k
  if(pxp->bf_base != NULL)
1028
21.7k
  {
1029
21.7k
    free(pxp->bf_base);
1030
21.7k
    pxp->bf_base = NULL;
1031
21.7k
    pxp->bf_extent = 0;
1032
21.7k
    pxp->bf_offset = OFF_NONE;
1033
21.7k
  }
1034
21.7k
}
1035
1036
1037
/* This is the second half of the ncio initialization. This is called
1038
   after the file has actually been opened.
1039
1040
   The most important thing that happens is the allocation of a block
1041
   of memory at pxp->bf_base. This is going to be twice the size of
1042
   the chunksizehint (rounded up to the nearest sizeof(double)) passed
1043
   in from nc__create or nc__open. The rounded chunksizehint (passed
1044
   in here in sizehintp) is going to be stored as pxp->blksize.
1045
1046
   According to our "contract" we are not allowed to ask for an extent
1047
   larger than this chunksize/sizehint/blksize from the ncio get
1048
   function.
1049
1050
   nciop - pointer to the ncio struct
1051
   sizehintp - pointer to a size hint that will be rounded up and
1052
   passed back to the caller.
1053
   isNew - true if this is being called from ncio_create for a new
1054
   file.
1055
*/
1056
static int
1057
ncio_px_init2(ncio *const nciop, size_t *sizehintp, int isNew)
1058
21.7k
{
1059
21.7k
  ncio_px *const pxp = (ncio_px *)nciop->pvt;
1060
21.7k
  const size_t bufsz = 2 * *sizehintp;
1061
1062
21.7k
  assert(nciop->fd >= 0);
1063
1064
21.7k
  pxp->blksz = *sizehintp;
1065
1066
21.7k
  assert(pxp->bf_base == NULL);
1067
1068
  /* this is separate allocation because it may grow */
1069
21.7k
  pxp->bf_base = malloc(bufsz);
1070
21.7k
  if(pxp->bf_base == NULL)
1071
0
    return ENOMEM;
1072
  /* else */
1073
21.7k
  pxp->bf_cnt = 0;
1074
21.7k
  if(isNew)
1075
10.8k
  {
1076
    /* save a read */
1077
10.8k
    pxp->pos = 0;
1078
10.8k
    pxp->bf_offset = 0;
1079
10.8k
    pxp->bf_extent = bufsz;
1080
10.8k
    (void) memset(pxp->bf_base, 0, pxp->bf_extent);
1081
10.8k
  }
1082
21.7k
  return NC_NOERR;
1083
21.7k
}
1084
1085
1086
/* This is the first of a two-part initialization of the ncio struct.
1087
   Here the rel, get, move, sync, and free function pointers are set
1088
   to their POSIX non-NC_SHARE functions (ncio_px_*).
1089
1090
   The ncio_px struct is also partially initialized.
1091
*/
1092
static void
1093
ncio_px_init(ncio *const nciop)
1094
21.7k
{
1095
21.7k
  ncio_px *const pxp = (ncio_px *)nciop->pvt;
1096
1097
21.7k
  *((ncio_relfunc **)&nciop->rel) = ncio_px_rel; /* cast away const */
1098
21.7k
  *((ncio_getfunc **)&nciop->get) = ncio_px_get; /* cast away const */
1099
21.7k
  *((ncio_movefunc **)&nciop->move) = ncio_px_move; /* cast away const */
1100
21.7k
  *((ncio_syncfunc **)&nciop->sync) = ncio_px_sync; /* cast away const */
1101
21.7k
  *((ncio_filesizefunc **)&nciop->filesize) = ncio_px_filesize; /* cast away const */
1102
21.7k
  *((ncio_pad_lengthfunc **)&nciop->pad_length) = ncio_px_pad_length; /* cast away const */
1103
21.7k
  *((ncio_closefunc **)&nciop->close) = ncio_px_close; /* cast away const */
1104
1105
21.7k
  pxp->blksz = 0;
1106
21.7k
  pxp->pos = -1;
1107
21.7k
  pxp->bf_offset = OFF_NONE;
1108
21.7k
  pxp->bf_extent = 0;
1109
21.7k
  pxp->bf_rflags = 0;
1110
21.7k
  pxp->bf_refcount = 0;
1111
21.7k
  pxp->bf_base = NULL;
1112
21.7k
  pxp->slave = NULL;
1113
1114
21.7k
}
1115
1116
/* Begin spx */
1117
1118
/* This is the struct that gets hung of ncio->pvt(?) when the NC_SHARE
1119
   flag is used.
1120
*/
1121
typedef struct ncio_spx {
1122
  off_t pos;
1123
  /* buffer */
1124
  off_t bf_offset;
1125
  size_t  bf_extent;
1126
  size_t  bf_cnt;
1127
  void  *bf_base;
1128
} ncio_spx;
1129
1130
1131
/*ARGSUSED*/
1132
/* This function releases the region specified by offset.
1133
1134
   For POSIX system, with NC_SHARE, this becomes the rel function
1135
   pointed to by the ncio rel function pointer. It merely checks for
1136
   file write permission, then calls px_rel to do everything.
1137
1138
   nciop - pointer to ncio struct.
1139
1140
   offset - beginning of region.
1141
1142
   rflags - One of the RGN_* flags defined in ncio.h. If set to
1143
   RGN_MODIFIED it means that the data in this region were modified,
1144
   and it needs to be written out to the disk immediately (since we
1145
   are not buffering with NC_SHARE on).
1146
1147
*/
1148
static int
1149
ncio_spx_rel(ncio *const nciop, off_t offset, int rflags)
1150
0
{
1151
0
  ncio_spx *const pxp = (ncio_spx *)nciop->pvt;
1152
0
  int status = NC_NOERR;
1153
1154
0
  assert(pxp->bf_offset <= offset);
1155
0
  assert(pxp->bf_cnt != 0);
1156
0
  assert(pxp->bf_cnt <= pxp->bf_extent);
1157
#ifdef X_ALIGN
1158
  assert(offset < pxp->bf_offset + X_ALIGN);
1159
  assert(pxp->bf_cnt % X_ALIGN == 0 );
1160
#endif
1161
0
  NC_UNUSED(offset);
1162
1163
0
  if(fIsSet(rflags, RGN_MODIFIED))
1164
0
  {
1165
0
    if(!fIsSet(nciop->ioflags, NC_WRITE))
1166
0
      return EPERM; /* attempt to write readonly file */
1167
1168
0
    status = px_pgout(nciop, pxp->bf_offset,
1169
0
      pxp->bf_cnt,
1170
0
      pxp->bf_base, &pxp->pos);
1171
    /* if error, invalidate buffer anyway */
1172
0
  }
1173
0
  pxp->bf_offset = OFF_NONE;
1174
0
  pxp->bf_cnt = 0;
1175
0
  return status;
1176
0
}
1177
1178
1179
/* Request that the region (offset, extent) be made available through
1180
   *vpp.
1181
1182
   This function converts a file region specified by an offset and
1183
   extent to a memory pointer. The region may be locked until the
1184
   corresponding call to rel().
1185
1186
   For POSIX systems, with NC_SHARE.
1187
1188
   nciop - pointer to ncio struct for this file.
1189
   offset - offset (from beginning of file?) to the data we want to
1190
   read.
1191
   extent - the number of bytes we want.
1192
   rflags - One of the RGN_* flags defined in ncio.h. May be RGN_NOLOCK.
1193
   vpp - handle to point at data when it's been read.
1194
*/
1195
static int
1196
ncio_spx_get(ncio *const nciop,
1197
    off_t offset, size_t extent,
1198
    int rflags,
1199
    void **const vpp)
1200
0
{
1201
0
  ncio_spx *const pxp = (ncio_spx *)nciop->pvt;
1202
0
  int status = NC_NOERR;
1203
#ifdef X_ALIGN
1204
  size_t rem;
1205
#endif
1206
1207
0
  if(fIsSet(rflags, RGN_WRITE) && !fIsSet(nciop->ioflags, NC_WRITE))
1208
0
    return EPERM; /* attempt to write readonly file */
1209
1210
0
  assert(extent != 0);
1211
0
  assert(extent < X_INT_MAX); /* sanity check */
1212
1213
0
  assert(pxp->bf_cnt == 0);
1214
1215
#ifdef X_ALIGN
1216
  rem = (size_t)(offset % X_ALIGN);
1217
  if(rem != 0)
1218
  {
1219
    offset -= rem;
1220
    extent += rem;
1221
  }
1222
1223
  {
1224
      const size_t rndup = extent % X_ALIGN;
1225
      if(rndup != 0)
1226
        extent += X_ALIGN - rndup;
1227
  }
1228
1229
  assert(offset % X_ALIGN == 0);
1230
  assert(extent % X_ALIGN == 0);
1231
#endif
1232
1233
0
  if(pxp->bf_extent < extent)
1234
0
  {
1235
0
    if(pxp->bf_base != NULL)
1236
0
    {
1237
0
      free(pxp->bf_base);
1238
0
      pxp->bf_base = NULL;
1239
0
      pxp->bf_extent = 0;
1240
0
    }
1241
0
    assert(pxp->bf_extent == 0);
1242
0
    pxp->bf_base = malloc(extent+1);
1243
0
    if(pxp->bf_base == NULL)
1244
0
      return ENOMEM;
1245
0
    pxp->bf_extent = extent;
1246
0
  }
1247
1248
0
  status = px_pgin(nciop, offset,
1249
0
     extent,
1250
0
     pxp->bf_base,
1251
0
     &pxp->bf_cnt, &pxp->pos);
1252
0
  if(status != NC_NOERR)
1253
0
    return status;
1254
1255
0
  pxp->bf_offset = offset;
1256
1257
0
  if(pxp->bf_cnt < extent)
1258
0
    pxp->bf_cnt = extent;
1259
1260
#ifdef X_ALIGN
1261
  *vpp = (char *)pxp->bf_base + rem;
1262
#else
1263
0
  *vpp = pxp->bf_base;
1264
0
#endif
1265
0
  return NC_NOERR;
1266
0
}
1267
1268
1269
#if 0
1270
/*ARGSUSED*/
1271
static int
1272
strategy(ncio *const nciop, off_t to, off_t offset,
1273
      size_t extent, int rflags)
1274
{
1275
  static ncio_spx pxp[1];
1276
  int status = NC_NOERR;
1277
#ifdef X_ALIGN
1278
  size_t rem;
1279
#endif
1280
1281
  assert(extent != 0);
1282
  assert(extent < X_INT_MAX); /* sanity check */
1283
#if INSTRUMENT
1284
fprintf(stderr, "strategy %ld at %ld to %ld\n",
1285
   (long)extent, (long)offset, (long)to);
1286
#endif
1287
1288
1289
#ifdef X_ALIGN
1290
  rem = (size_t)(offset % X_ALIGN);
1291
  if(rem != 0)
1292
  {
1293
    offset -= rem;
1294
    extent += rem;
1295
  }
1296
1297
  {
1298
    const size_t rndup = extent % X_ALIGN;
1299
    if(rndup != 0)
1300
      extent += X_ALIGN - rndup;
1301
  }
1302
1303
  assert(offset % X_ALIGN == 0);
1304
  assert(extent % X_ALIGN == 0);
1305
#endif
1306
1307
  if(pxp->bf_extent < extent)
1308
  {
1309
    if(pxp->bf_base != NULL)
1310
    {
1311
      free(pxp->bf_base);
1312
      pxp->bf_base = NULL;
1313
      pxp->bf_extent = 0;
1314
    }
1315
    assert(pxp->bf_extent == 0);
1316
    pxp->bf_base = malloc(extent);
1317
    if(pxp->bf_base == NULL)
1318
      return ENOMEM;
1319
    pxp->bf_extent = extent;
1320
  }
1321
1322
  status = px_pgin(nciop, offset,
1323
     extent,
1324
     pxp->bf_base,
1325
     &pxp->bf_cnt, &pxp->pos);
1326
  if(status != NC_NOERR)
1327
    return status;
1328
1329
  pxp->bf_offset = to; /* TODO: XALIGN */
1330
1331
  if(pxp->bf_cnt < extent)
1332
    pxp->bf_cnt = extent;
1333
1334
  status = px_pgout(nciop, pxp->bf_offset,
1335
    pxp->bf_cnt,
1336
    pxp->bf_base, &pxp->pos);
1337
  /* if error, invalidate buffer anyway */
1338
  pxp->bf_offset = OFF_NONE;
1339
  pxp->bf_cnt = 0;
1340
  return status;
1341
}
1342
#endif
1343
1344
/* Copy one region to another without making anything available to
1345
   higher layers. May be just implemented in terms of get() and rel(),
1346
   or may be tricky to be efficient.  Only used in by nc_enddef()
1347
   after redefinition.
1348
1349
   nciop - pointer to ncio struct for this file.
1350
   to - dest for move?
1351
   from - src for move?
1352
   nbytes - number of bytes to move.
1353
   rflags - One of the RGN_* flags defined in ncio.h.
1354
*/
1355
static int
1356
ncio_spx_move(ncio *const nciop, off_t to, off_t from,
1357
      size_t nbytes, int rflags)
1358
0
{
1359
0
  int status = NC_NOERR;
1360
0
  off_t lower = from;
1361
0
  off_t upper = to;
1362
0
  char *base;
1363
0
  size_t diff;
1364
0
  size_t extent;
1365
1366
0
  rflags &= RGN_NOLOCK; /* filter unwanted flags */
1367
1368
0
  if(to == from)
1369
0
    return NC_NOERR; /* NOOP */
1370
1371
0
  if(to > from)
1372
0
  {
1373
    /* growing */
1374
0
    lower = from;
1375
0
    upper = to;
1376
0
  }
1377
0
  else
1378
0
  {
1379
    /* shrinking */
1380
0
    lower = to;
1381
0
    upper = from;
1382
0
  }
1383
1384
0
  diff = (size_t)(upper - lower);
1385
0
  extent = diff + nbytes;
1386
1387
0
  status = ncio_spx_get(nciop, lower, extent, RGN_WRITE|rflags,
1388
0
      (void **)&base);
1389
1390
0
  if(status != NC_NOERR)
1391
0
    return status;
1392
1393
0
  if(to > from)
1394
0
    (void) memmove(base + diff, base, nbytes);
1395
0
  else
1396
0
    (void) memmove(base, base + diff, nbytes);
1397
1398
0
  (void) ncio_spx_rel(nciop, lower, RGN_MODIFIED);
1399
1400
0
  return status;
1401
0
}
1402
1403
1404
/*ARGSUSED*/
1405
/* Flush any buffers to disk. May be a no-op on if I/O is unbuffered.
1406
*/
1407
static int
1408
ncio_spx_sync(ncio *const nciop)
1409
0
{
1410
0
  NC_UNUSED(nciop);
1411
  /* NOOP */
1412
0
  return NC_NOERR;
1413
0
}
1414
1415
static void
1416
ncio_spx_freepvt(void *const pvt)
1417
0
{
1418
0
  ncio_spx *const pxp = (ncio_spx *)pvt;
1419
0
  if(pxp == NULL)
1420
0
    return;
1421
1422
0
  if(pxp->bf_base != NULL)
1423
0
  {
1424
0
    free(pxp->bf_base);
1425
0
    pxp->bf_base = NULL;
1426
0
    pxp->bf_offset = OFF_NONE;
1427
0
    pxp->bf_extent = 0;
1428
0
    pxp->bf_cnt = 0;
1429
0
  }
1430
0
}
1431
1432
1433
/* This does the second half of the ncio_spx struct initialization for
1434
   POSIX systems, with NC_SHARE on.
1435
1436
   nciop - pointer to ncio struct for this file. File has been opened.
1437
   sizehintp - pointer to a size which will be rounded up to the
1438
   nearest 8-byt boundary and then used as the max size "chunk" (or
1439
   page) to read from the file.
1440
*/
1441
static int
1442
ncio_spx_init2(ncio *const nciop, const size_t *const sizehintp)
1443
0
{
1444
0
  ncio_spx *const pxp = (ncio_spx *)nciop->pvt;
1445
1446
0
  assert(nciop->fd >= 0);
1447
1448
0
  pxp->bf_extent = *sizehintp;
1449
1450
0
  assert(pxp->bf_base == NULL);
1451
1452
  /* this is separate allocation because it may grow */
1453
0
  pxp->bf_base = malloc(pxp->bf_extent);
1454
0
  if(pxp->bf_base == NULL)
1455
0
  {
1456
0
    pxp->bf_extent = 0;
1457
0
    return ENOMEM;
1458
0
  }
1459
  /* else */
1460
0
  return NC_NOERR;
1461
0
}
1462
1463
1464
/* First half of init for ncio_spx struct, setting the rel, get, move,
1465
   sync, and free function pointers to the NC_SHARE versions of these
1466
   functions (i.e. the ncio_spx_* functions).
1467
*/
1468
static void
1469
ncio_spx_init(ncio *const nciop)
1470
0
{
1471
0
  ncio_spx *const pxp = (ncio_spx *)nciop->pvt;
1472
1473
0
  *((ncio_relfunc **)&nciop->rel) = ncio_spx_rel; /* cast away const */
1474
0
  *((ncio_getfunc **)&nciop->get) = ncio_spx_get; /* cast away const */
1475
0
  *((ncio_movefunc **)&nciop->move) = ncio_spx_move; /* cast away const */
1476
0
  *((ncio_syncfunc **)&nciop->sync) = ncio_spx_sync; /* cast away const */
1477
  /* shared with _px_ */
1478
0
  *((ncio_filesizefunc **)&nciop->filesize) = ncio_px_filesize; /* cast away const */
1479
0
  *((ncio_pad_lengthfunc **)&nciop->pad_length) = ncio_px_pad_length; /* cast away const */
1480
0
  *((ncio_closefunc **)&nciop->close) = ncio_spx_close; /* cast away const */
1481
1482
0
  pxp->pos = -1;
1483
0
  pxp->bf_offset = OFF_NONE;
1484
0
  pxp->bf_extent = 0;
1485
0
  pxp->bf_cnt = 0;
1486
0
  pxp->bf_base = NULL;
1487
0
}
1488
1489
1490
/* */
1491
1492
/* This will call whatever free function is attached to the free
1493
   function pointer in ncio. It's called from ncio_close, and from
1494
   ncio_open and ncio_create when an error occurs that the file
1495
   metadata must be freed.
1496
*/
1497
static void
1498
ncio_px_free(ncio *nciop)
1499
21.7k
{
1500
21.7k
  if(nciop == NULL)
1501
0
    return;
1502
21.7k
  if(nciop->pvt != NULL)
1503
21.7k
    ncio_px_freepvt(nciop->pvt);
1504
21.7k
  free(nciop);
1505
21.7k
}
1506
1507
static void
1508
ncio_spx_free(ncio *nciop)
1509
0
{
1510
0
  if(nciop == NULL)
1511
0
    return;
1512
0
  if(nciop->pvt != NULL)
1513
0
    ncio_spx_freepvt(nciop->pvt);
1514
0
  free(nciop);
1515
0
}
1516
1517
1518
/* Create a new ncio struct to hold info about the file. This will
1519
   create and init the ncio_px or ncio_spx struct (the latter if
1520
   NC_SHARE is used.)
1521
*/
1522
static ncio *
1523
ncio_px_new(const char *path, int ioflags)
1524
21.7k
{
1525
21.7k
  size_t sz_ncio = M_RNDUP(sizeof(ncio));
1526
21.7k
  size_t sz_path = M_RNDUP(strlen(path) +1);
1527
21.7k
  size_t sz_ncio_pvt;
1528
21.7k
  ncio *nciop;
1529
1530
#if ALWAYS_NC_SHARE /* DEBUG */
1531
  fSet(ioflags, NC_SHARE);
1532
#endif
1533
1534
21.7k
  if(fIsSet(ioflags, NC_SHARE))
1535
0
    sz_ncio_pvt = sizeof(ncio_spx);
1536
21.7k
  else
1537
21.7k
    sz_ncio_pvt = sizeof(ncio_px);
1538
1539
21.7k
  nciop = (ncio *) malloc(sz_ncio + sz_path + sz_ncio_pvt);
1540
21.7k
  if(nciop == NULL)
1541
0
    return NULL;
1542
1543
21.7k
  nciop->ioflags = ioflags;
1544
21.7k
  *((int *)&nciop->fd) = -1; /* cast away const */
1545
1546
21.7k
  nciop->path = (char *) ((char *)nciop + sz_ncio);
1547
21.7k
  (void) strcpy((char *)nciop->path, path); /* cast away const */
1548
1549
        /* cast away const */
1550
21.7k
  *((void **)&nciop->pvt) = (void *)(nciop->path + sz_path);
1551
1552
21.7k
  if(fIsSet(ioflags, NC_SHARE))
1553
0
    ncio_spx_init(nciop);
1554
21.7k
  else
1555
21.7k
    ncio_px_init(nciop);
1556
1557
21.7k
  return nciop;
1558
21.7k
}
1559
1560
1561
/* Public below this point */
1562
#ifndef NCIO_MINBLOCKSIZE
1563
#define NCIO_MINBLOCKSIZE 256
1564
#endif
1565
#ifndef NCIO_MAXBLOCKSIZE
1566
0
#define NCIO_MAXBLOCKSIZE 268435456 /* sanity check, about X_SIZE_T_MAX/8 */
1567
#endif
1568
1569
#ifdef S_IRUSR
1570
#define NC_DEFAULT_CREAT_MODE \
1571
10.8k
        (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) /* 0666 */
1572
1573
#else
1574
#define NC_DEFAULT_CREAT_MODE 0666
1575
#endif
1576
1577
/* Create a file, and the ncio struct to go with it. This function is
1578
   only called from nc__create_mp.
1579
1580
   path - path of file to create.
1581
   ioflags - flags from nc_create
1582
   initialsz - From the netcdf man page: "The argument
1583
   Iinitialsize sets the initial size of the file at creation time."
1584
   igeto -
1585
   igetsz -
1586
   sizehintp - this eventually goes into pxp->blksz and is the size of
1587
   a page of data for buffered reads and writes.
1588
   nciopp - pointer to a pointer that will get location of newly
1589
   created and inited ncio struct.
1590
   igetvpp - pointer to pointer which will get the location of ?
1591
*/
1592
int
1593
posixio_create(const char *path, int ioflags,
1594
  size_t initialsz,
1595
  off_t igeto, size_t igetsz, size_t *sizehintp,
1596
  void* parameters,
1597
  ncio **nciopp, void **const igetvpp)
1598
10.8k
{
1599
10.8k
  ncio *nciop;
1600
10.8k
  int oflags = (O_RDWR|O_CREAT);
1601
10.8k
  int fd;
1602
10.8k
  int status;
1603
10.8k
  NC_UNUSED(parameters);
1604
1605
10.8k
  if(initialsz < (size_t)igeto + igetsz)
1606
10.8k
    initialsz = (size_t)igeto + igetsz;
1607
1608
10.8k
  fSet(ioflags, NC_WRITE);
1609
1610
10.8k
  if(path == NULL || *path == 0)
1611
0
    return EINVAL;
1612
1613
10.8k
  nciop = ncio_px_new(path, ioflags);
1614
10.8k
  if(nciop == NULL)
1615
0
    return ENOMEM;
1616
1617
10.8k
  if(fIsSet(ioflags, NC_NOCLOBBER))
1618
0
    fSet(oflags, O_EXCL);
1619
10.8k
  else
1620
10.8k
    fSet(oflags, O_TRUNC);
1621
#ifdef O_BINARY
1622
  fSet(oflags, O_BINARY);
1623
#endif
1624
#ifdef vms
1625
  fd = open(path, oflags, NC_DEFAULT_CREAT_MODE, "ctx=stm");
1626
#else
1627
  /* Should we mess with the mode based on NC_SHARE ?? */
1628
10.8k
  fd = open(path, oflags, NC_DEFAULT_CREAT_MODE);
1629
10.8k
#endif
1630
#if 0
1631
  (void) fprintf(stderr, "ncio_create(): path=\"%s\"\n", path);
1632
  (void) fprintf(stderr, "ncio_create(): oflags=0x%x\n", oflags);
1633
#endif
1634
10.8k
  if(fd < 0)
1635
0
  {
1636
0
    status = errno;
1637
0
    goto unwind_new;
1638
0
  }
1639
10.8k
  *((int *)&nciop->fd) = fd; /* cast away const */
1640
1641
10.8k
  if(*sizehintp < NCIO_MINBLOCKSIZE)
1642
10.8k
  {
1643
    /* Use default */
1644
10.8k
    *sizehintp = blksize(fd);
1645
10.8k
  }
1646
0
  else if(*sizehintp >= NCIO_MAXBLOCKSIZE)
1647
0
  {
1648
    /* Use maximum allowed value */
1649
0
    *sizehintp = NCIO_MAXBLOCKSIZE;
1650
0
  }
1651
0
  else
1652
0
  {
1653
0
    *sizehintp = M_RNDUP(*sizehintp);
1654
0
  }
1655
1656
10.8k
  if(fIsSet(nciop->ioflags, NC_SHARE))
1657
0
    status = ncio_spx_init2(nciop, sizehintp);
1658
10.8k
  else
1659
10.8k
    status = ncio_px_init2(nciop, sizehintp, 1);
1660
1661
10.8k
  if(status != NC_NOERR)
1662
0
    goto unwind_open;
1663
1664
10.8k
  if(initialsz != 0)
1665
10.8k
  {
1666
10.8k
    status = fgrow(fd, (off_t)initialsz);
1667
10.8k
    if(status != NC_NOERR)
1668
0
      goto unwind_open;
1669
10.8k
  }
1670
1671
10.8k
  if(igetsz != 0)
1672
10.8k
  {
1673
10.8k
    status = nciop->get(nciop,
1674
10.8k
        igeto, igetsz,
1675
10.8k
                          RGN_WRITE,
1676
10.8k
                          igetvpp);
1677
10.8k
    if(status != NC_NOERR)
1678
0
      goto unwind_open;
1679
10.8k
  }
1680
1681
10.8k
  *nciopp = nciop;
1682
10.8k
  return NC_NOERR;
1683
1684
0
unwind_open:
1685
0
  (void) close(fd);
1686
  /* ?? unlink */
1687
  /*FALLTHRU*/
1688
0
unwind_new:
1689
0
  ncio_close(nciop,!fIsSet(ioflags, NC_NOCLOBBER));
1690
0
  return status;
1691
0
}
1692
1693
1694
/* This function opens the data file. It is only called from nc.c,
1695
   from nc__open_mp and nc_delete_mp.
1696
1697
   path - path of data file.
1698
1699
   ioflags - flags passed into nc_open.
1700
1701
   igeto - looks like this function can do an initial page get, and
1702
   igeto is going to be the offset for that. But it appears to be
1703
   unused
1704
1705
   igetsz - the size in bytes of initial page get (a.k.a. extent). Not
1706
   ever used in the library.
1707
1708
   sizehintp - pointer to sizehint parameter from nc__open or
1709
   nc__create. This is used to set pxp->blksz.
1710
1711
   Here's what the man page has to say:
1712
1713
   "The argument referenced by chunksize controls a space versus time
1714
   tradeoff, memory allocated in the netcdf library versus number of
1715
   system calls.
1716
1717
   Because of internal requirements, the value may not be set to
1718
   exactly the value requested. The actual value chosen is returned by reference.
1719
1720
   Using the value NC_SIZEHINT_DEFAULT causes the library to choose a
1721
   default. How the system choses the default depends on the
1722
   system. On many systems, the "preferred I/O block size" is
1723
   available from the stat() system call, struct stat member
1724
   st_blksize. If this is available it is used. Lacking that, twice
1725
   the system pagesize is used. Lacking a call to discover the system
1726
   pagesize, we just set default chunksize to 8192.
1727
1728
   The chunksize is a property of a given open netcdf descriptor ncid,
1729
   it is not a persistent property of the netcdf dataset."
1730
1731
   nciopp - pointer to pointer that will get address of newly created
1732
   and inited ncio struct.
1733
1734
   igetvpp - handle to pass back pointer to data from initial page
1735
   read, if this were ever used, which it isn't.
1736
*/
1737
int
1738
posixio_open(const char *path,
1739
  int ioflags,
1740
  off_t igeto, size_t igetsz, size_t *sizehintp,
1741
        void* parameters,
1742
  ncio **nciopp, void **const igetvpp)
1743
10.8k
{
1744
10.8k
  ncio *nciop;
1745
10.8k
  int oflags = fIsSet(ioflags, NC_WRITE) ? O_RDWR : O_RDONLY;
1746
10.8k
  int fd = -1;
1747
10.8k
  int status = 0;
1748
10.8k
  NC_UNUSED(parameters);
1749
1750
10.8k
  if(path == NULL || *path == 0)
1751
0
    return EINVAL;
1752
1753
10.8k
  nciop = ncio_px_new(path, ioflags);
1754
10.8k
  if(nciop == NULL)
1755
0
    return ENOMEM;
1756
1757
#ifdef O_BINARY
1758
  /*#if _MSC_VER*/
1759
  fSet(oflags, O_BINARY);
1760
#endif
1761
1762
#ifdef vms
1763
  fd = open(path, oflags, 0, "ctx=stm");
1764
#else
1765
10.8k
  fd = open(path, oflags, 0);
1766
10.8k
#endif
1767
10.8k
  if(fd < 0)
1768
0
  {
1769
0
    status = errno;
1770
0
    goto unwind_new;
1771
0
  }
1772
10.8k
  *((int *)&nciop->fd) = fd; /* cast away const */
1773
1774
10.8k
  if(*sizehintp < NCIO_MINBLOCKSIZE)
1775
10.8k
  {
1776
    /* Use default */
1777
10.8k
    *sizehintp = blksize(fd);
1778
10.8k
  }
1779
0
  else if(*sizehintp >= NCIO_MAXBLOCKSIZE)
1780
0
  {
1781
    /* Use maximum allowed value */
1782
0
    *sizehintp = NCIO_MAXBLOCKSIZE;
1783
0
  }
1784
0
  else
1785
0
  {
1786
0
    *sizehintp = M_RNDUP(*sizehintp);
1787
0
  }
1788
1789
10.8k
  if(fIsSet(nciop->ioflags, NC_SHARE))
1790
0
    status = ncio_spx_init2(nciop, sizehintp);
1791
10.8k
  else
1792
10.8k
    status = ncio_px_init2(nciop, sizehintp, 0);
1793
1794
10.8k
  if(status != NC_NOERR)
1795
0
    goto unwind_open;
1796
1797
10.8k
  if(igetsz != 0)
1798
0
  {
1799
0
    status = nciop->get(nciop,
1800
0
        igeto, igetsz,
1801
0
                          0,
1802
0
                          igetvpp);
1803
0
    if(status != NC_NOERR)
1804
0
      goto unwind_open;
1805
0
  }
1806
1807
10.8k
  *nciopp = nciop;
1808
10.8k
  return NC_NOERR;
1809
1810
0
unwind_open:
1811
0
  (void) close(fd); /* assert fd >= 0 */
1812
  /*FALLTHRU*/
1813
0
unwind_new:
1814
0
  ncio_close(nciop,0);
1815
0
  return status;
1816
0
}
1817
1818
/*
1819
 * Get file size in bytes.
1820
 */
1821
static int
1822
ncio_px_filesize(ncio *nciop, off_t *filesizep)
1823
32.6k
{
1824
1825
1826
  /* There is a problem with fstat on Windows based systems
1827
    which manifests (so far) when Config RELEASE is built.
1828
    Use _filelengthi64 isntead. */
1829
#ifdef HAVE_FILE_LENGTH_I64
1830
1831
  __int64 file_len = 0;
1832
  if( (file_len = _filelengthi64(nciop->fd)) < 0) {
1833
    return errno;
1834
  }
1835
1836
  *filesizep = file_len;
1837
1838
#else
1839
32.6k
    struct stat sb;
1840
32.6k
    assert(nciop != NULL);
1841
32.6k
    if (fstat(nciop->fd, &sb) < 0)
1842
0
  return errno;
1843
32.6k
    *filesizep = sb.st_size;
1844
32.6k
#endif
1845
32.6k
  return NC_NOERR;
1846
32.6k
}
1847
1848
/*
1849
 * Sync any changes to disk, then truncate or extend file so its size
1850
 * is length.  This is only intended to be called before close, if the
1851
 * file is open for writing and the actual size does not match the
1852
 * calculated size, perhaps as the result of having been previously
1853
 * written in NOFILL mode.
1854
 */
1855
static int
1856
ncio_px_pad_length(ncio *nciop, off_t length)
1857
0
{
1858
1859
0
  int status = NC_NOERR;
1860
1861
0
  if(nciop == NULL)
1862
0
    return EINVAL;
1863
1864
0
  if(!fIsSet(nciop->ioflags, NC_WRITE))
1865
0
          return EPERM; /* attempt to write readonly file */
1866
1867
0
  status = nciop->sync(nciop);
1868
0
  if(status != NC_NOERR)
1869
0
          return status;
1870
1871
0
  status = fgrow2(nciop->fd, length);
1872
0
  if(status != NC_NOERR)
1873
0
          return status;
1874
0
  return NC_NOERR;
1875
0
}
1876
1877
1878
/* Write out any dirty buffers to disk and
1879
   ensure that next read will get data from disk.
1880
1881
   Sync any changes, then close the open file associated with the ncio
1882
   struct, and free its memory.
1883
1884
   nciop - pointer to ncio to close.
1885
1886
   doUnlink - if true, unlink file
1887
*/
1888
static int
1889
ncio_px_close(ncio *nciop, int doUnlink)
1890
21.7k
{
1891
21.7k
  int status = NC_NOERR;
1892
21.7k
  if(nciop == NULL)
1893
0
    return EINVAL;
1894
21.7k
  if(nciop->fd > 0) {
1895
21.7k
      status = nciop->sync(nciop);
1896
21.7k
      (void) close(nciop->fd);
1897
21.7k
  }
1898
21.7k
  if(doUnlink)
1899
0
    (void) unlink(nciop->path);
1900
21.7k
  ncio_px_free(nciop);
1901
21.7k
  return status;
1902
21.7k
}
1903
1904
static int
1905
ncio_spx_close(ncio *nciop, int doUnlink)
1906
0
{
1907
0
  int status = NC_NOERR;
1908
0
  if(nciop == NULL)
1909
0
    return EINVAL;
1910
0
  if(nciop->fd > 0) {
1911
0
      status = nciop->sync(nciop);
1912
0
      (void) close(nciop->fd);
1913
0
  }
1914
0
  if(doUnlink)
1915
0
    (void) unlink(nciop->path);
1916
0
  ncio_spx_free(nciop);
1917
0
  return status;
1918
0
}