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