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