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


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