1 /*
2  *	Copyright 2018, University Corporation for Atmospheric Research
3  *	See netcdf/COPYRIGHT file for copying and redistribution conditions.
4  */
5 /* $Id: posixio.c,v 1.89 2010/05/22 21:59:08 dmh Exp $ */
6 
7 /* For MinGW Build */
8 
9 #if HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12 
13 #include <stdio.h>
14 #include <assert.h>
15 #include <stdlib.h>
16 #include <errno.h>
17 #include <string.h>
18 
19 #ifdef HAVE_FCNTL_H
20 #include <fcntl.h>
21 #endif
22 #ifdef HAVE_SYS_TYPES_H
23 #include <sys/types.h>
24 #endif
25 #ifdef HAVE_SYS_STAT_H
26 #include <sys/stat.h>
27 #endif
28 
29 /* Windows platforms, including MinGW, Cygwin, Visual Studio */
30 #if defined(_WIN32) || defined(_WIN64)
31 #include <windows.h>
32 #include <winbase.h>
33 #include <io.h>
34 #endif
35 
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39 
40 #ifndef NC_NOERR
41 #define NC_NOERR 0
42 #endif
43 
44 #ifndef SEEK_SET
45 #define SEEK_SET 0
46 #define SEEK_CUR 1
47 #define SEEK_END 2
48 #endif
49 
50 #include "ncio.h"
51 #include "fbits.h"
52 #include "rnd.h"
53 
54 /* #define INSTRUMENT 1 */
55 #if INSTRUMENT /* debugging */
56 #undef NDEBUG
57 #include <stdio.h>
58 #include "instr.h"
59 #endif
60 
61 #undef MIN  /* system may define MIN somewhere and complain */
62 #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 #else
188 	NC_UNUSED(fd);
189 #endif
190 #else
191 	NC_UNUSED(fd);
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 #ifdef X_ALIGN
342 	assert(offset % X_ALIGN == 0);
343 	assert(extent % X_ALIGN == 0);
344 #endif
345     /* *posp == OFF_NONE (-1) on first call. This
346        is problematic because lseek also returns -1
347        on error. Use errno instead. */
348     if(*posp != OFF_NONE && *posp != lseek(nciop->fd, 0, SEEK_CUR)) {
349       if(errno) {
350         status = errno;
351         printf("Error %d: %s\n",errno,strerror(errno));
352         return status;
353       }
354     }
355 
356 	if(*posp != offset)
357 	{
358 		if(lseek(nciop->fd, offset, SEEK_SET) != offset)
359 		{
360 			status = errno;
361 			return status;
362 		}
363 		*posp = offset;
364 	}
365 
366 	errno = 0;
367     /* Handle the case where the read is interrupted
368        by a signal (see NCF-337,
369        http://pubs.opengroup.org/onlinepubs/009695399/functions/read.html)
370 
371        When this happens, nread will (should) be the bytes read, and
372        errno will be set to EINTR.  On older systems nread might be -1.
373        If this is the case, there's not a whole lot we can do about it
374        as we can't compute any offsets, so we will attempt to read again.
375        This *feels* like it could lead to an infinite loop, but it shouldn't
376        unless the read is being constantly interrupted by a signal, and is
377        on an older system which returns -1 instead of bytexs read.
378 
379        The case where it's a short read is already handled by the function
380        (according to the comment below, at least). */
381     do {
382       nread = read(nciop->fd,vp,extent);
383     } while (nread == -1 && errno == EINTR);
384 
385 
386     if(nread != (ssize_t)extent) {
387       status = errno;
388       if( nread == -1 || (status != EINTR && status != NC_NOERR))
389         return status;
390       /* else it's okay we read less than asked for */
391       (void) memset((char *)vp + nread, 0, (ssize_t)extent - nread);
392     }
393 
394     *nreadp = nread;
395 	*posp += nread;
396 
397 	return NC_NOERR;
398 }
399 
400 /* This struct is for POSIX systems, with NC_SHARE not in effect. If
401    NC_SHARE is used, see ncio_spx.
402 
403    blksz - block size for reads and writes to file.
404    pos - current read/write position in file.
405    bf_offset - file offset corresponding to start of memory buffer
406    bf_extent - number of bytes in I/O request
407    bf_cnt - number of bytes available in buffer
408    bf_base - pointer to beginning of buffer.
409    bf_rflags - buffer region flags (defined in ncio.h) tell the lock
410    status, read/write permissions, and modification status of regions
411    of data in the buffer.
412    bf_refcount - buffer reference count.
413    slave - used in moves.
414 */
415 typedef struct ncio_px {
416 	size_t blksz;
417 	off_t pos;
418 	/* buffer */
419 	off_t	bf_offset;
420 	size_t	bf_extent;
421 	size_t	bf_cnt;
422 	void	*bf_base;
423 	int	bf_rflags;
424 	int	bf_refcount;
425 	/* chain for double buffering in px_move */
426 	struct ncio_px *slave;
427 } ncio_px;
428 
429 
430 /*ARGSUSED*/
431 /* This function indicates the file region starting at offset may be
432    released.
433 
434    This is for POSIX, without NC_SHARE.  If called with RGN_MODIFIED
435    flag, sets the modified flag in pxp->bf_rflags and decrements the
436    reference count.
437 
438    pxp - pointer to posix non-share ncio_px struct.
439 
440    offset - file offset for beginning of to region to be
441    released.
442 
443    rflags - only RGN_MODIFIED is relevant to this function, others ignored
444 */
445 static int
px_rel(ncio_px * const pxp,off_t offset,int rflags)446 px_rel(ncio_px *const pxp, off_t offset, int rflags)
447 {
448 	assert(pxp->bf_offset <= offset
449 		 && offset < pxp->bf_offset + (off_t) pxp->bf_extent);
450 	assert(pIf(fIsSet(rflags, RGN_MODIFIED),
451 		fIsSet(pxp->bf_rflags, RGN_WRITE)));
452 	NC_UNUSED(offset);
453 
454 	if(fIsSet(rflags, RGN_MODIFIED))
455 	{
456 		fSet(pxp->bf_rflags, RGN_MODIFIED);
457 	}
458 	pxp->bf_refcount--;
459 
460 	return NC_NOERR;
461 }
462 
463 /* This function indicates the file region starting at offset may be
464    released.  Each read or write to the file is bracketed by a call to
465    the "get" region function and a call to the "rel" region function.
466    If you only read from the memory region, release it with a flag of
467    0, if you modify the region, release it with a flag of
468    RGN_MODIFIED.
469 
470    For POSIX system, without NC_SHARE, this becomes the rel function
471    pointed to by the ncio rel function pointer. It merely checks for
472    file write permission, then calls px_rel to do everything.
473 
474    nciop - pointer to ncio struct.
475    offset - num bytes from beginning of buffer to region to be
476    released.
477    rflags - only RGN_MODIFIED is relevant to this function, others ignored
478 */
479 static int
ncio_px_rel(ncio * const nciop,off_t offset,int rflags)480 ncio_px_rel(ncio *const nciop, off_t offset, int rflags)
481 {
482 	ncio_px *const pxp = (ncio_px *)nciop->pvt;
483 
484 	if(fIsSet(rflags, RGN_MODIFIED) && !fIsSet(nciop->ioflags, NC_WRITE))
485 		return EPERM; /* attempt to write readonly file */
486 
487 	return px_rel(pxp, offset, rflags);
488 }
489 
490 /* POSIX get. This will "make a region available." Since we're using
491    buffered IO, this means that if needed, we'll fetch a new page from
492    the file, otherwise, just return a pointer to what's in memory
493    already.
494 
495    nciop - pointer to ncio struct, containing file info.
496    pxp - pointer to ncio_px struct, which contains special metadate
497    for posix files without NC_SHARE.
498    offset - start byte of region to get.
499    extent - how many bytes to read.
500    rflags - One of the RGN_* flags defined in ncio.h.
501    vpp - pointer to pointer that will receive data.
502 
503    NOTES:
504 
505    * For blkoffset round offset down to the nearest pxp->blksz. This
506    provides the offset (in bytes) to the beginning of the block that
507    holds the current offset.
508 
509    * diff tells how far into the current block we are.
510 
511    * For blkextent round up to the number of bytes at the beginning of
512    the next block, after the one that holds our current position, plus
513    whatever extra (i.e. the extent) that we are about to grab.
514 
515    * The blkextent can't be more than twice the pxp->blksz. That's
516    because the pxp->blksize is the sizehint, and in ncio_px_init2 the
517    buffer (pointed to by pxp->bf-base) is allocated with 2 *
518    *sizehintp. This is checked (unnecessarily) more than once in
519    asserts.
520 
521    * If this is called on a newly opened file, pxp->bf_offset will be
522    OFF_NONE and we'll jump to label pgin to immediately read in a
523    page.
524 */
525 static int
px_get(ncio * const nciop,ncio_px * const pxp,off_t offset,size_t extent,int rflags,void ** const vpp)526 px_get(ncio *const nciop, ncio_px *const pxp,
527 		off_t offset, size_t extent,
528 		int rflags,
529 		void **const vpp)
530 {
531 	int status = NC_NOERR;
532 
533 	const off_t blkoffset = _RNDDOWN(offset, (off_t)pxp->blksz);
534 	off_t diff = (size_t)(offset - blkoffset);
535 	off_t blkextent = _RNDUP(diff + extent, pxp->blksz);
536 
537 	assert(extent != 0);
538 	assert(extent < X_INT_MAX); /* sanity check */
539 	assert(offset >= 0); /* sanity check */
540 
541 	if(2 * pxp->blksz < blkextent)
542 		return E2BIG; /* TODO: temporary kludge */
543 	if(pxp->bf_offset == OFF_NONE)
544 	{
545 		/* Uninitialized */
546 		if(pxp->bf_base == NULL)
547 		{
548 			assert(pxp->bf_extent == 0);
549 			assert(blkextent <= 2 * pxp->blksz);
550 			pxp->bf_base = malloc(2 * pxp->blksz);
551 			if(pxp->bf_base == NULL)
552 				return ENOMEM;
553 		}
554 		goto pgin;
555 	}
556 	/* else */
557 	assert(blkextent <= 2 * pxp->blksz);
558 
559 	if(blkoffset == pxp->bf_offset)
560 	{
561 		/* hit */
562  		if(blkextent > pxp->bf_extent)
563 		{
564 			/* page in upper */
565 			void *const middle =
566 			 	(void *)((char *)pxp->bf_base + pxp->blksz);
567 			assert(pxp->bf_extent == pxp->blksz);
568 			status = px_pgin(nciop,
569 				 pxp->bf_offset + (off_t)pxp->blksz,
570 				 pxp->blksz,
571 				 middle,
572 				 &pxp->bf_cnt,
573 				 &pxp->pos);
574 			if(status != NC_NOERR)
575 				return status;
576 			pxp->bf_extent = 2 * pxp->blksz;
577 			pxp->bf_cnt += pxp->blksz;
578 		}
579 		goto done;
580 	}
581 	/* else */
582 
583 	if(pxp->bf_extent > pxp->blksz
584 		 && blkoffset == pxp->bf_offset + (off_t)pxp->blksz)
585 	{
586 		/* hit in upper half */
587 		if(blkextent == pxp->blksz)
588 		{
589 			/* all in upper half, no fault needed */
590 			diff += pxp->blksz;
591 			goto done;
592 		}
593 		/* else */
594 		if(pxp->bf_cnt > pxp->blksz)
595 		{
596 			/* data in upper half */
597 			void *const middle =
598 				(void *)((char *)pxp->bf_base + pxp->blksz);
599 			assert(pxp->bf_extent == 2 * pxp->blksz);
600 			if(fIsSet(pxp->bf_rflags, RGN_MODIFIED))
601 			{
602 				/* page out lower half */
603 				assert(pxp->bf_refcount <= 0);
604 				status = px_pgout(nciop,
605 					pxp->bf_offset,
606 					pxp->blksz,
607 					pxp->bf_base,
608 					&pxp->pos);
609 				if(status != NC_NOERR)
610 					return status;
611 			}
612 			pxp->bf_cnt -= pxp->blksz;
613 			/* copy upper half into lower half */
614 			(void) memcpy(pxp->bf_base, middle, pxp->bf_cnt);
615 		}
616 		else		/* added to fix nofill bug */
617 		{
618 			assert(pxp->bf_extent == 2 * pxp->blksz);
619 			/* still have to page out lower half, if modified */
620 			if(fIsSet(pxp->bf_rflags, RGN_MODIFIED))
621 			{
622 				assert(pxp->bf_refcount <= 0);
623 				status = px_pgout(nciop,
624 					pxp->bf_offset,
625 					pxp->blksz,
626 					pxp->bf_base,
627 					&pxp->pos);
628 				if(status != NC_NOERR)
629 					return status;
630 			}
631 		}
632 		pxp->bf_offset = blkoffset;
633 		/* pxp->bf_extent = pxp->blksz; */
634 
635  		assert(blkextent == 2 * pxp->blksz);
636 		{
637 			/* page in upper */
638 			void *const middle =
639 			 	(void *)((char *)pxp->bf_base + pxp->blksz);
640 			status = px_pgin(nciop,
641 				 pxp->bf_offset + (off_t)pxp->blksz,
642 				 pxp->blksz,
643 				 middle,
644 				 &pxp->bf_cnt,
645 				 &pxp->pos);
646 			if(status != NC_NOERR)
647 				return status;
648 			pxp->bf_extent = 2 * pxp->blksz;
649 			pxp->bf_cnt += pxp->blksz;
650 		}
651 		goto done;
652 	}
653 	/* else */
654 
655 	if(blkoffset == pxp->bf_offset - (off_t)pxp->blksz)
656 	{
657 		/* wants the page below */
658 		void *const middle =
659 			(void *)((char *)pxp->bf_base + pxp->blksz);
660 		size_t upper_cnt = 0;
661 		if(pxp->bf_cnt > pxp->blksz)
662 		{
663 			/* data in upper half */
664 			assert(pxp->bf_extent == 2 * pxp->blksz);
665 			if(fIsSet(pxp->bf_rflags, RGN_MODIFIED))
666 			{
667 				/* page out upper half */
668 				assert(pxp->bf_refcount <= 0);
669 				status = px_pgout(nciop,
670 					pxp->bf_offset + (off_t)pxp->blksz,
671 					pxp->bf_cnt - pxp->blksz,
672 					middle,
673 					&pxp->pos);
674 				if(status != NC_NOERR)
675 					return status;
676 			}
677 			pxp->bf_cnt = pxp->blksz;
678 			pxp->bf_extent = pxp->blksz;
679 		}
680 		if(pxp->bf_cnt > 0)
681 		{
682 			/* copy lower half into upper half */
683 			(void) memcpy(middle, pxp->bf_base, pxp->blksz);
684 			upper_cnt = pxp->bf_cnt;
685 		}
686 		/* read page below into lower half */
687 		status = px_pgin(nciop,
688 			 blkoffset,
689 			 pxp->blksz,
690 			 pxp->bf_base,
691 			 &pxp->bf_cnt,
692 			 &pxp->pos);
693 		if(status != NC_NOERR)
694 			return status;
695 		pxp->bf_offset = blkoffset;
696 		if(upper_cnt != 0)
697 		{
698 			pxp->bf_extent = 2 * pxp->blksz;
699 			pxp->bf_cnt = pxp->blksz + upper_cnt;
700 		}
701 		else
702 		{
703 			pxp->bf_extent = pxp->blksz;
704 		}
705 		goto done;
706 	}
707 	/* else */
708 
709 	/* no overlap */
710 	if(fIsSet(pxp->bf_rflags, RGN_MODIFIED))
711 	{
712 		assert(pxp->bf_refcount <= 0);
713 		status = px_pgout(nciop,
714 			pxp->bf_offset,
715 			pxp->bf_cnt,
716 			pxp->bf_base,
717 			&pxp->pos);
718 		if(status != NC_NOERR)
719 			return status;
720 		pxp->bf_rflags = 0;
721 	}
722 
723 pgin:
724 	status = px_pgin(nciop,
725 		 blkoffset,
726 		 blkextent,
727 		 pxp->bf_base,
728 		 &pxp->bf_cnt,
729 		 &pxp->pos);
730 	if(status != NC_NOERR)
731 		return status;
732 	 pxp->bf_offset = blkoffset;
733 	 pxp->bf_extent = blkextent;
734 
735 done:
736 	extent += diff;
737 	if(pxp->bf_cnt < extent)
738 		pxp->bf_cnt = extent;
739 	assert(pxp->bf_cnt <= pxp->bf_extent);
740 
741 	pxp->bf_rflags |= rflags;
742 	pxp->bf_refcount++;
743 
744     *vpp = (void *)((signed char*)pxp->bf_base + diff);
745 	return NC_NOERR;
746 }
747 
748 /* Request that the region (offset, extent) be made available through
749    *vpp.
750 
751    This function converts a file region specified by an offset and
752    extent to a memory pointer. The region may be locked until the
753    corresponding call to rel().
754 
755    For POSIX systems, without NC_SHARE. This function gets a page of
756    size extent?
757 
758    This is a wrapper for the function px_get, which does all the heavy
759    lifting.
760 
761    nciop - pointer to ncio struct for this file.
762    offset - offset (from beginning of file?) to the data we want to
763    read.
764    extent - the number of bytes to read from the file.
765    rflags - One of the RGN_* flags defined in ncio.h.
766    vpp - handle to point at data when it's been read.
767 */
768 static int
ncio_px_get(ncio * const nciop,off_t offset,size_t extent,int rflags,void ** const vpp)769 ncio_px_get(ncio *const nciop,
770 		off_t offset, size_t extent,
771 		int rflags,
772 		void **const vpp)
773 {
774 	ncio_px *const pxp = (ncio_px *)nciop->pvt;
775 
776 	if(fIsSet(rflags, RGN_WRITE) && !fIsSet(nciop->ioflags, NC_WRITE))
777 		return EPERM; /* attempt to write readonly file */
778 
779 	/* reclaim space used in move */
780 	if(pxp->slave != NULL)
781 	{
782 		if(pxp->slave->bf_base != NULL)
783 		{
784 			free(pxp->slave->bf_base);
785 			pxp->slave->bf_base = NULL;
786 			pxp->slave->bf_extent = 0;
787 			pxp->slave->bf_offset = OFF_NONE;
788 		}
789 		free(pxp->slave);
790 		pxp->slave = NULL;
791 	}
792 	return px_get(nciop, pxp, offset, extent, rflags, vpp);
793 }
794 
795 
796 /* ARGSUSED */
797 static int
px_double_buffer(ncio * const nciop,off_t to,off_t from,size_t nbytes,int rflags)798 px_double_buffer(ncio *const nciop, off_t to, off_t from,
799 			size_t nbytes, int rflags)
800 {
801 	ncio_px *const pxp = (ncio_px *)nciop->pvt;
802 	int status = NC_NOERR;
803 	void *src;
804 	void *dest;
805 	NC_UNUSED(rflags);
806 
807 #if INSTRUMENT
808 fprintf(stderr, "\tdouble_buffr %ld %ld %ld\n",
809 		 (long)to, (long)from, (long)nbytes);
810 #endif
811 	status = px_get(nciop, pxp, to, nbytes, RGN_WRITE,
812 			&dest);
813 	if(status != NC_NOERR)
814 		return status;
815 
816 	if(pxp->slave == NULL)
817 	{
818 		pxp->slave = (ncio_px *) malloc(sizeof(ncio_px));
819 		if(pxp->slave == NULL)
820 			return ENOMEM;
821 
822 		pxp->slave->blksz = pxp->blksz;
823 		/* pos done below */
824 		pxp->slave->bf_offset = pxp->bf_offset;
825 		pxp->slave->bf_extent = pxp->bf_extent;
826 		pxp->slave->bf_cnt = pxp->bf_cnt;
827 		pxp->slave->bf_base = malloc(2 * pxp->blksz);
828 		if(pxp->slave->bf_base == NULL)
829 			return ENOMEM;
830 		(void) memcpy(pxp->slave->bf_base, pxp->bf_base,
831 			 pxp->bf_extent);
832 		pxp->slave->bf_rflags = 0;
833 		pxp->slave->bf_refcount = 0;
834 		pxp->slave->slave = NULL;
835 	}
836 
837 	pxp->slave->pos = pxp->pos;
838 	status = px_get(nciop, pxp->slave, from, nbytes, 0,
839 			&src);
840 	if(status != NC_NOERR)
841 		return status;
842 	if(pxp->pos != pxp->slave->pos)
843 	{
844 		/* position changed, sync */
845 		pxp->pos = pxp->slave->pos;
846 	}
847 
848 	(void) memcpy(dest, src, nbytes);
849 
850 	(void)px_rel(pxp->slave, from, 0);
851 	(void)px_rel(pxp, to, RGN_MODIFIED);
852 
853 	return status;
854 }
855 
856 /* Like memmove(), safely move possibly overlapping data.
857 
858    Copy one region to another without making anything available to
859    higher layers. May be just implemented in terms of get() and rel(),
860    or may be tricky to be efficient. Only used in by nc_enddef()
861    after redefinition.
862 
863    nciop - pointer to ncio struct with file info.
864    to - src for move?
865    from - dest for move?
866    nbytes - number of bytes to move.
867    rflags - One of the RGN_* flags defined in ncio.h. The only
868    reasonable flag value is RGN_NOLOCK.
869 */
870 static int
ncio_px_move(ncio * const nciop,off_t to,off_t from,size_t nbytes,int rflags)871 ncio_px_move(ncio *const nciop, off_t to, off_t from,
872 			size_t nbytes, int rflags)
873 {
874 	ncio_px *const pxp = (ncio_px *)nciop->pvt;
875 	int status = NC_NOERR;
876 	off_t lower;
877 	off_t upper;
878 	char *base;
879 	size_t diff;
880 	size_t extent;
881 
882 	if(to == from)
883 		return NC_NOERR; /* NOOP */
884 
885 	if(fIsSet(rflags, RGN_WRITE) && !fIsSet(nciop->ioflags, NC_WRITE))
886 		return EPERM; /* attempt to write readonly file */
887 
888 	rflags &= RGN_NOLOCK; /* filter unwanted flags */
889 
890 	if(to > from)
891 	{
892 		/* growing */
893 		lower = from;
894 		upper = to;
895 	}
896 	else
897 	{
898 		/* shrinking */
899 		lower = to;
900 		upper = from;
901 	}
902 	diff = (size_t)(upper - lower);
903 	extent = diff + nbytes;
904 
905 #if INSTRUMENT
906 fprintf(stderr, "ncio_px_move %ld %ld %ld %ld %ld\n",
907 		 (long)to, (long)from, (long)nbytes, (long)lower, (long)extent);
908 #endif
909 	if(extent > pxp->blksz)
910 	{
911 		size_t remaining = nbytes;
912 
913 if(to > from)
914 {
915 		off_t frm = from + nbytes;
916 		off_t toh = to + nbytes;
917 		for(;;)
918 		{
919 			size_t loopextent = MIN(remaining, pxp->blksz);
920 			frm -= loopextent;
921 			toh -= loopextent;
922 
923 			status = px_double_buffer(nciop, toh, frm,
924 				 	loopextent, rflags) ;
925 			if(status != NC_NOERR)
926 				return status;
927 			remaining -= loopextent;
928 
929 			if(remaining == 0)
930 				break; /* normal loop exit */
931 		}
932 }
933 else
934 {
935 		for(;;)
936 		{
937 			size_t loopextent = MIN(remaining, pxp->blksz);
938 
939 			status = px_double_buffer(nciop, to, from,
940 				 	loopextent, rflags) ;
941 			if(status != NC_NOERR)
942 				return status;
943 			remaining -= loopextent;
944 
945 			if(remaining == 0)
946 				break; /* normal loop exit */
947 			to += loopextent;
948 			from += loopextent;
949 		}
950 }
951 		return NC_NOERR;
952 	}
953 
954 #if INSTRUMENT
955 fprintf(stderr, "\tncio_px_move small\n");
956 #endif
957 	status = px_get(nciop, pxp, lower, extent, RGN_WRITE|rflags,
958 			(void **)&base);
959 
960 	if(status != NC_NOERR)
961 		return status;
962 
963 	if(to > from)
964 		(void) memmove(base + diff, base, nbytes);
965 	else
966 		(void) memmove(base, base + diff, nbytes);
967 
968 	(void) px_rel(pxp, lower, RGN_MODIFIED);
969 
970 	return status;
971 }
972 
973 
974 /* Flush any buffers to disk. May be a no-op on if I/O is unbuffered.
975    This function is used when NC_SHARE is NOT used.
976 */
977 static int
ncio_px_sync(ncio * const nciop)978 ncio_px_sync(ncio *const nciop)
979 {
980 	ncio_px *const pxp = (ncio_px *)nciop->pvt;
981 	int status = NC_NOERR;
982 	if(fIsSet(pxp->bf_rflags, RGN_MODIFIED))
983 	{
984 		assert(pxp->bf_refcount <= 0);
985 		status = px_pgout(nciop, pxp->bf_offset,
986 			pxp->bf_cnt,
987 			pxp->bf_base, &pxp->pos);
988 		if(status != NC_NOERR)
989 			return status;
990 		pxp->bf_rflags = 0;
991 	}
992 	else if (!fIsSet(pxp->bf_rflags, RGN_WRITE))
993 	{
994 	    /*
995 	     * The dataset is readonly.  Invalidate the buffers so
996 	     * that the next ncio_px_get() will actually read data.
997 	     */
998 	    pxp->bf_offset = OFF_NONE;
999 	    pxp->bf_cnt = 0;
1000 	}
1001 	return status;
1002 }
1003 
1004 /* Internal function called at close to
1005    free up anything hanging off pvt.
1006 */
1007 static void
ncio_px_freepvt(void * const pvt)1008 ncio_px_freepvt(void *const pvt)
1009 {
1010 	ncio_px *const pxp = (ncio_px *)pvt;
1011 	if(pxp == NULL)
1012 		return;
1013 
1014 	if(pxp->slave != NULL)
1015 	{
1016 		if(pxp->slave->bf_base != NULL)
1017 		{
1018 			free(pxp->slave->bf_base);
1019 			pxp->slave->bf_base = NULL;
1020 			pxp->slave->bf_extent = 0;
1021 			pxp->slave->bf_offset = OFF_NONE;
1022 		}
1023 		free(pxp->slave);
1024 		pxp->slave = NULL;
1025 	}
1026 
1027 	if(pxp->bf_base != NULL)
1028 	{
1029 		free(pxp->bf_base);
1030 		pxp->bf_base = NULL;
1031 		pxp->bf_extent = 0;
1032 		pxp->bf_offset = OFF_NONE;
1033 	}
1034 }
1035 
1036 
1037 /* This is the second half of the ncio initialization. This is called
1038    after the file has actually been opened.
1039 
1040    The most important thing that happens is the allocation of a block
1041    of memory at pxp->bf_base. This is going to be twice the size of
1042    the chunksizehint (rounded up to the nearest sizeof(double)) passed
1043    in from nc__create or nc__open. The rounded chunksizehint (passed
1044    in here in sizehintp) is going to be stored as pxp->blksize.
1045 
1046    According to our "contract" we are not allowed to ask for an extent
1047    larger than this chunksize/sizehint/blksize from the ncio get
1048    function.
1049 
1050    nciop - pointer to the ncio struct
1051    sizehintp - pointer to a size hint that will be rounded up and
1052    passed back to the caller.
1053    isNew - true if this is being called from ncio_create for a new
1054    file.
1055 */
1056 static int
ncio_px_init2(ncio * const nciop,size_t * sizehintp,int isNew)1057 ncio_px_init2(ncio *const nciop, size_t *sizehintp, int isNew)
1058 {
1059 	ncio_px *const pxp = (ncio_px *)nciop->pvt;
1060 	const size_t bufsz = 2 * *sizehintp;
1061 
1062 	assert(nciop->fd >= 0);
1063 
1064 	pxp->blksz = *sizehintp;
1065 
1066 	assert(pxp->bf_base == NULL);
1067 
1068 	/* this is separate allocation because it may grow */
1069 	pxp->bf_base = malloc(bufsz);
1070 	if(pxp->bf_base == NULL)
1071 		return ENOMEM;
1072 	/* else */
1073 	pxp->bf_cnt = 0;
1074 	if(isNew)
1075 	{
1076 		/* save a read */
1077 		pxp->pos = 0;
1078 		pxp->bf_offset = 0;
1079 		pxp->bf_extent = bufsz;
1080 		(void) memset(pxp->bf_base, 0, pxp->bf_extent);
1081 	}
1082 	return NC_NOERR;
1083 }
1084 
1085 
1086 /* This is the first of a two-part initialization of the ncio struct.
1087    Here the rel, get, move, sync, and free function pointers are set
1088    to their POSIX non-NC_SHARE functions (ncio_px_*).
1089 
1090    The ncio_px struct is also partially initialized.
1091 */
1092 static void
ncio_px_init(ncio * const nciop)1093 ncio_px_init(ncio *const nciop)
1094 {
1095 	ncio_px *const pxp = (ncio_px *)nciop->pvt;
1096 
1097 	*((ncio_relfunc **)&nciop->rel) = ncio_px_rel; /* cast away const */
1098 	*((ncio_getfunc **)&nciop->get) = ncio_px_get; /* cast away const */
1099 	*((ncio_movefunc **)&nciop->move) = ncio_px_move; /* cast away const */
1100 	*((ncio_syncfunc **)&nciop->sync) = ncio_px_sync; /* cast away const */
1101 	*((ncio_filesizefunc **)&nciop->filesize) = ncio_px_filesize; /* cast away const */
1102 	*((ncio_pad_lengthfunc **)&nciop->pad_length) = ncio_px_pad_length; /* cast away const */
1103 	*((ncio_closefunc **)&nciop->close) = ncio_px_close; /* cast away const */
1104 
1105 	pxp->blksz = 0;
1106 	pxp->pos = -1;
1107 	pxp->bf_offset = OFF_NONE;
1108 	pxp->bf_extent = 0;
1109 	pxp->bf_rflags = 0;
1110 	pxp->bf_refcount = 0;
1111 	pxp->bf_base = NULL;
1112 	pxp->slave = NULL;
1113 
1114 }
1115 
1116 /* Begin spx */
1117 
1118 /* This is the struct that gets hung of ncio->pvt(?) when the NC_SHARE
1119    flag is used.
1120 */
1121 typedef struct ncio_spx {
1122 	off_t pos;
1123 	/* buffer */
1124 	off_t	bf_offset;
1125 	size_t	bf_extent;
1126 	size_t	bf_cnt;
1127 	void	*bf_base;
1128 } ncio_spx;
1129 
1130 
1131 /*ARGSUSED*/
1132 /* This function releases the region specified by offset.
1133 
1134    For POSIX system, with NC_SHARE, this becomes the rel function
1135    pointed to by the ncio rel function pointer. It merely checks for
1136    file write permission, then calls px_rel to do everything.
1137 
1138    nciop - pointer to ncio struct.
1139 
1140    offset - beginning of region.
1141 
1142    rflags - One of the RGN_* flags defined in ncio.h. If set to
1143    RGN_MODIFIED it means that the data in this region were modified,
1144    and it needs to be written out to the disk immediately (since we
1145    are not buffering with NC_SHARE on).
1146 
1147 */
1148 static int
ncio_spx_rel(ncio * const nciop,off_t offset,int rflags)1149 ncio_spx_rel(ncio *const nciop, off_t offset, int rflags)
1150 {
1151 	ncio_spx *const pxp = (ncio_spx *)nciop->pvt;
1152 	int status = NC_NOERR;
1153 
1154 	assert(pxp->bf_offset <= offset);
1155 	assert(pxp->bf_cnt != 0);
1156 	assert(pxp->bf_cnt <= pxp->bf_extent);
1157 #ifdef X_ALIGN
1158 	assert(offset < pxp->bf_offset + X_ALIGN);
1159 	assert(pxp->bf_cnt % X_ALIGN == 0 );
1160 #endif
1161 	NC_UNUSED(offset);
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 	NC_UNUSED(nciop);
1411 	/* NOOP */
1412 	return NC_NOERR;
1413 }
1414 
1415 static void
ncio_spx_freepvt(void * const pvt)1416 ncio_spx_freepvt(void *const pvt)
1417 {
1418 	ncio_spx *const pxp = (ncio_spx *)pvt;
1419 	if(pxp == NULL)
1420 		return;
1421 
1422 	if(pxp->bf_base != NULL)
1423 	{
1424 		free(pxp->bf_base);
1425 		pxp->bf_base = NULL;
1426 		pxp->bf_offset = OFF_NONE;
1427 		pxp->bf_extent = 0;
1428 		pxp->bf_cnt = 0;
1429 	}
1430 }
1431 
1432 
1433 /* This does the second half of the ncio_spx struct initialization for
1434    POSIX systems, with NC_SHARE on.
1435 
1436    nciop - pointer to ncio struct for this file. File has been opened.
1437    sizehintp - pointer to a size which will be rounded up to the
1438    nearest 8-byt boundary and then used as the max size "chunk" (or
1439    page) to read from the file.
1440 */
1441 static int
ncio_spx_init2(ncio * const nciop,const size_t * const sizehintp)1442 ncio_spx_init2(ncio *const nciop, const size_t *const sizehintp)
1443 {
1444 	ncio_spx *const pxp = (ncio_spx *)nciop->pvt;
1445 
1446 	assert(nciop->fd >= 0);
1447 
1448 	pxp->bf_extent = *sizehintp;
1449 
1450 	assert(pxp->bf_base == NULL);
1451 
1452 	/* this is separate allocation because it may grow */
1453 	pxp->bf_base = malloc(pxp->bf_extent);
1454 	if(pxp->bf_base == NULL)
1455 	{
1456 		pxp->bf_extent = 0;
1457 		return ENOMEM;
1458 	}
1459 	/* else */
1460 	return NC_NOERR;
1461 }
1462 
1463 
1464 /* First half of init for ncio_spx struct, setting the rel, get, move,
1465    sync, and free function pointers to the NC_SHARE versions of these
1466    functions (i.e. the ncio_spx_* functions).
1467 */
1468 static void
ncio_spx_init(ncio * const nciop)1469 ncio_spx_init(ncio *const nciop)
1470 {
1471 	ncio_spx *const pxp = (ncio_spx *)nciop->pvt;
1472 
1473 	*((ncio_relfunc **)&nciop->rel) = ncio_spx_rel; /* cast away const */
1474 	*((ncio_getfunc **)&nciop->get) = ncio_spx_get; /* cast away const */
1475 	*((ncio_movefunc **)&nciop->move) = ncio_spx_move; /* cast away const */
1476 	*((ncio_syncfunc **)&nciop->sync) = ncio_spx_sync; /* cast away const */
1477 	/* shared with _px_ */
1478 	*((ncio_filesizefunc **)&nciop->filesize) = ncio_px_filesize; /* cast away const */
1479 	*((ncio_pad_lengthfunc **)&nciop->pad_length) = ncio_px_pad_length; /* cast away const */
1480 	*((ncio_closefunc **)&nciop->close) = ncio_spx_close; /* cast away const */
1481 
1482 	pxp->pos = -1;
1483 	pxp->bf_offset = OFF_NONE;
1484 	pxp->bf_extent = 0;
1485 	pxp->bf_cnt = 0;
1486 	pxp->bf_base = NULL;
1487 }
1488 
1489 
1490 /* */
1491 
1492 /* This will call whatever free function is attached to the free
1493    function pointer in ncio. It's called from ncio_close, and from
1494    ncio_open and ncio_create when an error occurs that the file
1495    metadata must be freed.
1496 */
1497 static void
ncio_px_free(ncio * nciop)1498 ncio_px_free(ncio *nciop)
1499 {
1500 	if(nciop == NULL)
1501 		return;
1502 	if(nciop->pvt != NULL)
1503 		ncio_px_freepvt(nciop->pvt);
1504 	free(nciop);
1505 }
1506 
1507 static void
ncio_spx_free(ncio * nciop)1508 ncio_spx_free(ncio *nciop)
1509 {
1510 	if(nciop == NULL)
1511 		return;
1512 	if(nciop->pvt != NULL)
1513 		ncio_spx_freepvt(nciop->pvt);
1514 	free(nciop);
1515 }
1516 
1517 
1518 /* Create a new ncio struct to hold info about the file. This will
1519    create and init the ncio_px or ncio_spx struct (the latter if
1520    NC_SHARE is used.)
1521 */
1522 static ncio *
ncio_px_new(const char * path,int ioflags)1523 ncio_px_new(const char *path, int ioflags)
1524 {
1525 	size_t sz_ncio = M_RNDUP(sizeof(ncio));
1526 	size_t sz_path = M_RNDUP(strlen(path) +1);
1527 	size_t sz_ncio_pvt;
1528 	ncio *nciop;
1529 
1530 #if ALWAYS_NC_SHARE /* DEBUG */
1531 	fSet(ioflags, NC_SHARE);
1532 #endif
1533 
1534 	if(fIsSet(ioflags, NC_SHARE))
1535 		sz_ncio_pvt = sizeof(ncio_spx);
1536 	else
1537 		sz_ncio_pvt = sizeof(ncio_px);
1538 
1539 	nciop = (ncio *) malloc(sz_ncio + sz_path + sz_ncio_pvt);
1540 	if(nciop == NULL)
1541 		return NULL;
1542 
1543 	nciop->ioflags = ioflags;
1544 	*((int *)&nciop->fd) = -1; /* cast away const */
1545 
1546 	nciop->path = (char *) ((char *)nciop + sz_ncio);
1547 	(void) strcpy((char *)nciop->path, path); /* cast away const */
1548 
1549 				/* cast away const */
1550 	*((void **)&nciop->pvt) = (void *)(nciop->path + sz_path);
1551 
1552 	if(fIsSet(ioflags, NC_SHARE))
1553 		ncio_spx_init(nciop);
1554 	else
1555 		ncio_px_init(nciop);
1556 
1557 	return nciop;
1558 }
1559 
1560 
1561 /* Public below this point */
1562 #ifndef NCIO_MINBLOCKSIZE
1563 #define NCIO_MINBLOCKSIZE 256
1564 #endif
1565 #ifndef NCIO_MAXBLOCKSIZE
1566 #define NCIO_MAXBLOCKSIZE 268435456 /* sanity check, about X_SIZE_T_MAX/8 */
1567 #endif
1568 
1569 #ifdef S_IRUSR
1570 #define NC_DEFAULT_CREAT_MODE \
1571         (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) /* 0666 */
1572 
1573 #else
1574 #define NC_DEFAULT_CREAT_MODE 0666
1575 #endif
1576 
1577 /* Create a file, and the ncio struct to go with it. This function is
1578    only called from nc__create_mp.
1579 
1580    path - path of file to create.
1581    ioflags - flags from nc_create
1582    initialsz - From the netcdf man page: "The argument
1583    Iinitialsize sets the initial size of the file at creation time."
1584    igeto -
1585    igetsz -
1586    sizehintp - this eventually goes into pxp->blksz and is the size of
1587    a page of data for buffered reads and writes.
1588    nciopp - pointer to a pointer that will get location of newly
1589    created and inited ncio struct.
1590    igetvpp - pointer to pointer which will get the location of ?
1591 */
1592 int
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)1593 posixio_create(const char *path, int ioflags,
1594 	size_t initialsz,
1595 	off_t igeto, size_t igetsz, size_t *sizehintp,
1596 	void* parameters,
1597 	ncio **nciopp, void **const igetvpp)
1598 {
1599 	ncio *nciop;
1600 	int oflags = (O_RDWR|O_CREAT);
1601 	int fd;
1602 	int status;
1603 	NC_UNUSED(parameters);
1604 
1605 	if(initialsz < (size_t)igeto + igetsz)
1606 		initialsz = (size_t)igeto + igetsz;
1607 
1608 	fSet(ioflags, NC_WRITE);
1609 
1610 	if(path == NULL || *path == 0)
1611 		return EINVAL;
1612 
1613 	nciop = ncio_px_new(path, ioflags);
1614 	if(nciop == NULL)
1615 		return ENOMEM;
1616 
1617 	if(fIsSet(ioflags, NC_NOCLOBBER))
1618 		fSet(oflags, O_EXCL);
1619 	else
1620 		fSet(oflags, O_TRUNC);
1621 #ifdef O_BINARY
1622 	fSet(oflags, O_BINARY);
1623 #endif
1624 #ifdef vms
1625 	fd = open(path, oflags, NC_DEFAULT_CREAT_MODE, "ctx=stm");
1626 #else
1627 	/* Should we mess with the mode based on NC_SHARE ?? */
1628 	fd = open(path, oflags, NC_DEFAULT_CREAT_MODE);
1629 #endif
1630 #if 0
1631 	(void) fprintf(stderr, "ncio_create(): path=\"%s\"\n", path);
1632 	(void) fprintf(stderr, "ncio_create(): oflags=0x%x\n", oflags);
1633 #endif
1634 	if(fd < 0)
1635 	{
1636 		status = errno;
1637 		goto unwind_new;
1638 	}
1639 	*((int *)&nciop->fd) = fd; /* cast away const */
1640 
1641 	if(*sizehintp < NCIO_MINBLOCKSIZE)
1642 	{
1643 		/* Use default */
1644 		*sizehintp = blksize(fd);
1645 	}
1646 	else if(*sizehintp >= NCIO_MAXBLOCKSIZE)
1647 	{
1648 		/* Use maximum allowed value */
1649 		*sizehintp = NCIO_MAXBLOCKSIZE;
1650 	}
1651 	else
1652 	{
1653 		*sizehintp = M_RNDUP(*sizehintp);
1654 	}
1655 
1656 	if(fIsSet(nciop->ioflags, NC_SHARE))
1657 		status = ncio_spx_init2(nciop, sizehintp);
1658 	else
1659 		status = ncio_px_init2(nciop, sizehintp, 1);
1660 
1661 	if(status != NC_NOERR)
1662 		goto unwind_open;
1663 
1664 	if(initialsz != 0)
1665 	{
1666 		status = fgrow(fd, (off_t)initialsz);
1667 		if(status != NC_NOERR)
1668 			goto unwind_open;
1669 	}
1670 
1671 	if(igetsz != 0)
1672 	{
1673 		status = nciop->get(nciop,
1674 				igeto, igetsz,
1675                         	RGN_WRITE,
1676                         	igetvpp);
1677 		if(status != NC_NOERR)
1678 			goto unwind_open;
1679 	}
1680 
1681 	*nciopp = nciop;
1682 	return NC_NOERR;
1683 
1684 unwind_open:
1685 	(void) close(fd);
1686 	/* ?? unlink */
1687 	/*FALLTHRU*/
1688 unwind_new:
1689 	ncio_close(nciop,!fIsSet(ioflags, NC_NOCLOBBER));
1690 	return status;
1691 }
1692 
1693 
1694 /* This function opens the data file. It is only called from nc.c,
1695    from nc__open_mp and nc_delete_mp.
1696 
1697    path - path of data file.
1698 
1699    ioflags - flags passed into nc_open.
1700 
1701    igeto - looks like this function can do an initial page get, and
1702    igeto is going to be the offset for that. But it appears to be
1703    unused
1704 
1705    igetsz - the size in bytes of initial page get (a.k.a. extent). Not
1706    ever used in the library.
1707 
1708    sizehintp - pointer to sizehint parameter from nc__open or
1709    nc__create. This is used to set pxp->blksz.
1710 
1711    Here's what the man page has to say:
1712 
1713    "The argument referenced by chunksize controls a space versus time
1714    tradeoff, memory allocated in the netcdf library versus number of
1715    system calls.
1716 
1717    Because of internal requirements, the value may not be set to
1718    exactly the value requested. The actual value chosen is returned by reference.
1719 
1720    Using the value NC_SIZEHINT_DEFAULT causes the library to choose a
1721    default. How the system choses the default depends on the
1722    system. On many systems, the "preferred I/O block size" is
1723    available from the stat() system call, struct stat member
1724    st_blksize. If this is available it is used. Lacking that, twice
1725    the system pagesize is used. Lacking a call to discover the system
1726    pagesize, we just set default chunksize to 8192.
1727 
1728    The chunksize is a property of a given open netcdf descriptor ncid,
1729    it is not a persistent property of the netcdf dataset."
1730 
1731    nciopp - pointer to pointer that will get address of newly created
1732    and inited ncio struct.
1733 
1734    igetvpp - handle to pass back pointer to data from initial page
1735    read, if this were ever used, which it isn't.
1736 */
1737 int
posixio_open(const char * path,int ioflags,off_t igeto,size_t igetsz,size_t * sizehintp,void * parameters,ncio ** nciopp,void ** const igetvpp)1738 posixio_open(const char *path,
1739 	int ioflags,
1740 	off_t igeto, size_t igetsz, size_t *sizehintp,
1741         void* parameters,
1742 	ncio **nciopp, void **const igetvpp)
1743 {
1744 	ncio *nciop;
1745 	int oflags = fIsSet(ioflags, NC_WRITE) ? O_RDWR : O_RDONLY;
1746 	int fd = -1;
1747 	int status = 0;
1748 	NC_UNUSED(parameters);
1749 
1750 	if(path == NULL || *path == 0)
1751 		return EINVAL;
1752 
1753 	nciop = ncio_px_new(path, ioflags);
1754 	if(nciop == NULL)
1755 		return ENOMEM;
1756 
1757 #ifdef O_BINARY
1758 	/*#if _MSC_VER*/
1759 	fSet(oflags, O_BINARY);
1760 #endif
1761 
1762 #ifdef vms
1763 	fd = open(path, oflags, 0, "ctx=stm");
1764 #else
1765 	fd = open(path, oflags, 0);
1766 #endif
1767 	if(fd < 0)
1768 	{
1769 		status = errno;
1770 		goto unwind_new;
1771 	}
1772 	*((int *)&nciop->fd) = fd; /* cast away const */
1773 
1774 	if(*sizehintp < NCIO_MINBLOCKSIZE)
1775 	{
1776 		/* Use default */
1777 		*sizehintp = blksize(fd);
1778 	}
1779 	else if(*sizehintp >= NCIO_MAXBLOCKSIZE)
1780 	{
1781 		/* Use maximum allowed value */
1782 		*sizehintp = NCIO_MAXBLOCKSIZE;
1783 	}
1784 	else
1785 	{
1786 		*sizehintp = M_RNDUP(*sizehintp);
1787 	}
1788 
1789 	if(fIsSet(nciop->ioflags, NC_SHARE))
1790 		status = ncio_spx_init2(nciop, sizehintp);
1791 	else
1792 		status = ncio_px_init2(nciop, sizehintp, 0);
1793 
1794 	if(status != NC_NOERR)
1795 		goto unwind_open;
1796 
1797 	if(igetsz != 0)
1798 	{
1799 		status = nciop->get(nciop,
1800 				igeto, igetsz,
1801                         	0,
1802                         	igetvpp);
1803 		if(status != NC_NOERR)
1804 			goto unwind_open;
1805 	}
1806 
1807 	*nciopp = nciop;
1808 	return NC_NOERR;
1809 
1810 unwind_open:
1811 	(void) close(fd); /* assert fd >= 0 */
1812 	/*FALLTHRU*/
1813 unwind_new:
1814 	ncio_close(nciop,0);
1815 	return status;
1816 }
1817 
1818 /*
1819  * Get file size in bytes.
1820  */
1821 static int
ncio_px_filesize(ncio * nciop,off_t * filesizep)1822 ncio_px_filesize(ncio *nciop, off_t *filesizep)
1823 {
1824 
1825 
1826 	/* There is a problem with fstat on Windows based systems
1827 		which manifests (so far) when Config RELEASE is built.
1828 		Use _filelengthi64 isntead. */
1829 #ifdef HAVE_FILE_LENGTH_I64
1830 
1831 	__int64 file_len = 0;
1832 	if( (file_len = _filelengthi64(nciop->fd)) < 0) {
1833 		return errno;
1834 	}
1835 
1836 	*filesizep = file_len;
1837 
1838 #else
1839     struct stat sb;
1840     assert(nciop != NULL);
1841     if (fstat(nciop->fd, &sb) < 0)
1842 	return errno;
1843     *filesizep = sb.st_size;
1844 #endif
1845 	return NC_NOERR;
1846 }
1847 
1848 /*
1849  * Sync any changes to disk, then truncate or extend file so its size
1850  * is length.  This is only intended to be called before close, if the
1851  * file is open for writing and the actual size does not match the
1852  * calculated size, perhaps as the result of having been previously
1853  * written in NOFILL mode.
1854  */
1855 static int
ncio_px_pad_length(ncio * nciop,off_t length)1856 ncio_px_pad_length(ncio *nciop, off_t length)
1857 {
1858 
1859 	int status = NC_NOERR;
1860 
1861 	if(nciop == NULL)
1862 		return EINVAL;
1863 
1864 	if(!fIsSet(nciop->ioflags, NC_WRITE))
1865 	        return EPERM; /* attempt to write readonly file */
1866 
1867 	status = nciop->sync(nciop);
1868 	if(status != NC_NOERR)
1869 	        return status;
1870 
1871  	status = fgrow2(nciop->fd, length);
1872  	if(status != NC_NOERR)
1873 	        return status;
1874 	return NC_NOERR;
1875 }
1876 
1877 
1878 /* Write out any dirty buffers to disk and
1879    ensure that next read will get data from disk.
1880 
1881    Sync any changes, then close the open file associated with the ncio
1882    struct, and free its memory.
1883 
1884    nciop - pointer to ncio to close.
1885 
1886    doUnlink - if true, unlink file
1887 */
1888 static int
ncio_px_close(ncio * nciop,int doUnlink)1889 ncio_px_close(ncio *nciop, int doUnlink)
1890 {
1891 	int status = NC_NOERR;
1892 	if(nciop == NULL)
1893 		return EINVAL;
1894 	if(nciop->fd > 0) {
1895 	    status = nciop->sync(nciop);
1896 	    (void) close(nciop->fd);
1897 	}
1898 	if(doUnlink)
1899 		(void) unlink(nciop->path);
1900 	ncio_px_free(nciop);
1901 	return status;
1902 }
1903 
1904 static int
ncio_spx_close(ncio * nciop,int doUnlink)1905 ncio_spx_close(ncio *nciop, int doUnlink)
1906 {
1907 	int status = NC_NOERR;
1908 	if(nciop == NULL)
1909 		return EINVAL;
1910 	if(nciop->fd > 0) {
1911 	    status = nciop->sync(nciop);
1912 	    (void) close(nciop->fd);
1913 	}
1914 	if(doUnlink)
1915 		(void) unlink(nciop->path);
1916 	ncio_spx_free(nciop);
1917 	return status;
1918 }
1919