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