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