1 /*
2  *	Copyright 2018, University Corporation for Atmospheric Research
3  *	See netcdf/COPYRIGHT file for copying and redistribution conditions.
4  */
5 /* $Id: ffio.c,v 1.56 2006/09/15 20:40:30 ed Exp $ */
6 /* addition by O. Heudecker, AWI-Bremerhaven, 12.3.1998 */
7 /* added correction by John Sheldon and Hans Vahlenkamp 15.4.1998*/
8 
9 
10 #if HAVE_CONFIG_H
11 #include <config.h>
12 #endif
13 
14 #include <assert.h>
15 #include <stdlib.h>
16 #include <stdio.h>	/* DEBUG */
17 #include <string.h>
18 #include <assert.h>
19 #include <errno.h>
20 #ifndef NC_NOERR
21 #define NC_NOERR 0
22 #endif
23 #ifdef HAVE_FCNTL_H
24 #include <fcntl.h>
25 #endif
26 #ifdef HAVE_SYS_STAT_H
27 #include <sys/stat.h>
28 #endif
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 #if 0
33 /* Insertion by O. R. Heudecker, AWI-Bremerhaven 12.3.98 (1 line)*/
34 #include <ffio.h>
35 #include <fortran.h>
36 #endif
37 
38 #include "ncio.h"
39 #include "fbits.h"
40 #include "rnd.h"
41 
42 #if !defined(NDEBUG) && !defined(X_INT_MAX)
43 #define  X_INT_MAX 2147483647
44 #endif
45 #if 0 /* !defined(NDEBUG) && !defined(X_ALIGN) */
46 #define  X_ALIGN 4
47 #endif
48 
49 #define ALWAYS_NC_SHARE 0 /* DEBUG */
50 
51 /* Forward */
52 static int ncio_ffio_filesize(ncio *nciop, off_t *filesizep);
53 static int ncio_ffio_pad_length(ncio *nciop, off_t length);
54 static int ncio_ffio_close(ncio *nciop, int doUnlink);
55 
56 
57 /* Begin OS */
58 
59 /*
60  * What is the preferred I/O block size?
61  * (This becomes the default *sizehint == ncp->chunk in the higher layers.)
62  * TODO: What is the the best answer here?
63  */
64 static size_t
blksize(int fd)65 blksize(int fd)
66 {
67 	struct ffc_stat_s sb;
68 	struct ffsw sw;
69 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
70 #ifdef HAVE_FCNTL_H
71 	if (fffcntl(fd, FC_STAT, &sb, &sw) > -1)
72 	{
73 #ifdef __crayx1
74 		if(sb.st_blksize > 0)
75 			return (size_t) sb.st_blksize;
76 #else
77 		if(sb.st_oblksize > 0)
78 			return (size_t) sb.st_oblksize;
79 #endif
80 	}
81 #endif
82 #endif
83 	/* else, silent in the face of error */
84 	return (size_t) 32768;
85 }
86 
87 /*
88  * Sortof like ftruncate, except won't make the
89  * file shorter.
90  */
91 static int
fgrow(const int fd,const off_t len)92 fgrow(const int fd, const off_t len)
93 {
94 	struct ffc_stat_s sb;
95 	struct ffsw sw;
96 	if (fffcntl(fd, FC_STAT, &sb, &sw) < 0)
97 		return errno;
98 	if (len < sb.st_size)
99 		return NC_NOERR;
100 	{
101 		const long dumb = 0;
102 			/* cache current position */
103 		const off_t pos = ffseek(fd, 0, SEEK_CUR);
104 		if(pos < 0)
105 			return errno;
106 		if (ffseek(fd, len-sizeof(dumb), SEEK_SET) < 0)
107 			return errno;
108 		if(ffwrite(fd, (void *)&dumb, sizeof(dumb)) < 0)
109 			return errno;
110 		if (ffseek(fd, pos, SEEK_SET) < 0)
111 			return errno;
112 	}
113 	/* else */
114 	return NC_NOERR;
115 }
116 
117 
118 /*
119  * Sortof like ftruncate, except won't make the file shorter.  Differs
120  * from fgrow by only writing one byte at designated seek position, if
121  * needed.
122  */
123 static int
fgrow2(const int fd,const off_t len)124 fgrow2(const int fd, const off_t len)
125 {
126 	struct ffc_stat_s sb;
127 	struct ffsw sw;
128 	if (fffcntl(fd, FC_STAT, &sb, &sw) < 0)
129 		return errno;
130 	if (len <= sb.st_size)
131 		return NC_NOERR;
132 	{
133 	    const char dumb = 0;
134 	    /* we don't use ftruncate() due to problem with FAT32 file systems */
135 	    /* cache current position */
136 	    const off_t pos = ffseek(fd, 0, SEEK_CUR);
137 	    if(pos < 0)
138 		return errno;
139 	    if (ffseek(fd, len-1, SEEK_SET) < 0)
140 		return errno;
141 	    if(ffwrite(fd, (void *)&dumb, sizeof(dumb)) < 0)
142 		return errno;
143 	    if (ffseek(fd, pos, SEEK_SET) < 0)
144 		return errno;
145 	}
146 	return NC_NOERR;
147 }
148 /* End OS */
149 /* Begin ffio */
150 
151 static int
ffio_pgout(ncio * const nciop,off_t const offset,const size_t extent,const void * const vp,off_t * posp)152 ffio_pgout(ncio *const nciop,
153 	off_t const offset,  const size_t extent,
154 	const void *const vp, off_t *posp)
155 {
156 #ifdef X_ALIGN
157 	assert(offset % X_ALIGN == 0);
158 	assert(extent % X_ALIGN == 0);
159 #endif
160 
161 	if(*posp != offset)
162 	{
163 		if(ffseek(nciop->fd, offset, SEEK_SET) != offset)
164 		{
165 			return errno;
166 		}
167 		*posp = offset;
168 	}
169 	if(ffwrite(nciop->fd, vp, extent) != extent)
170 	{
171 		return errno;
172 	}
173 	*posp += extent;
174 
175 	return NC_NOERR;
176 }
177 
178 
179 static int
ffio_pgin(ncio * const nciop,off_t const offset,const size_t extent,void * const vp,size_t * nreadp,off_t * posp)180 ffio_pgin(ncio *const nciop,
181 	off_t const offset, const size_t extent,
182 	void *const vp, size_t *nreadp, off_t *posp)
183 {
184 	int status;
185 	ssize_t nread;
186 
187 #ifdef X_ALIGN
188 	assert(offset % X_ALIGN == 0);
189 	assert(extent % X_ALIGN == 0);
190 #endif
191 
192 	if(*posp != offset)
193 	{
194 		if(ffseek(nciop->fd, offset, SEEK_SET) != offset)
195 		{
196 			status = errno;
197 			return status;
198 		}
199 		*posp = offset;
200 	}
201 
202 	errno = 0;
203 	nread = ffread(nciop->fd, vp, extent);
204 	if(nread != extent)
205 	{
206 		status = errno;
207 		if(nread == -1 || status != NC_NOERR)
208 			return status;
209 		/* else it's okay we read 0. */
210 	}
211 	*nreadp = nread;
212 	*posp += nread;
213 
214 	return NC_NOERR;
215 }
216 
217 /* */
218 
219 typedef struct ncio_ffio {
220 	off_t pos;
221 	/* buffer */
222 	off_t	bf_offset;
223 	size_t	bf_extent;
224 	size_t	bf_cnt;
225 	void	*bf_base;
226 } ncio_ffio;
227 
228 
229 static int
ncio_ffio_rel(ncio * const nciop,off_t offset,int rflags)230 ncio_ffio_rel(ncio *const nciop, off_t offset, int rflags)
231 {
232 	ncio_ffio *ffp = (ncio_ffio *)nciop->pvt;
233 	int status = NC_NOERR;
234 
235 	assert(ffp->bf_offset <= offset);
236 	assert(ffp->bf_cnt != 0);
237 	assert(ffp->bf_cnt <= ffp->bf_extent);
238 #ifdef X_ALIGN
239 	assert(offset < ffp->bf_offset + X_ALIGN);
240 	assert(ffp->bf_cnt % X_ALIGN == 0 );
241 #endif
242 
243 	if(fIsSet(rflags, RGN_MODIFIED))
244 	{
245 		if(!fIsSet(nciop->ioflags, NC_WRITE))
246 			return EPERM; /* attempt to write readonly file */
247 
248 		status = ffio_pgout(nciop, ffp->bf_offset,
249 			ffp->bf_cnt,
250 			ffp->bf_base, &ffp->pos);
251 		/* if error, invalidate buffer anyway */
252 	}
253 	ffp->bf_offset = OFF_NONE;
254 	ffp->bf_cnt = 0;
255 	return status;
256 }
257 
258 
259 static int
ncio_ffio_get(ncio * const nciop,off_t offset,size_t extent,int rflags,void ** const vpp)260 ncio_ffio_get(ncio *const nciop,
261 		off_t offset, size_t extent,
262 		int rflags,
263 		void **const vpp)
264 {
265 	ncio_ffio *ffp = (ncio_ffio *)nciop->pvt;
266 	int status = NC_NOERR;
267 #ifdef X_ALIGN
268 	size_t rem;
269 #endif
270 
271 	if(fIsSet(rflags, RGN_WRITE) && !fIsSet(nciop->ioflags, NC_WRITE))
272 		return EPERM; /* attempt to write readonly file */
273 
274 	assert(extent != 0);
275 	assert(extent < X_INT_MAX); /* sanity check */
276 
277 	assert(ffp->bf_cnt == 0);
278 
279 #ifdef X_ALIGN
280 	/* round to seekable boundaries */
281 	rem = offset % X_ALIGN;
282 	if(rem != 0)
283 	{
284 		offset -= rem;
285 		extent += rem;
286 	}
287 
288 	{
289 		const size_t rndup = extent % X_ALIGN;
290 		if(rndup != 0)
291 			extent += X_ALIGN - rndup;
292 	}
293 
294 	assert(offset % X_ALIGN == 0);
295 	assert(extent % X_ALIGN == 0);
296 #endif
297 
298 	if(ffp->bf_extent < extent)
299 	{
300 		if(ffp->bf_base != NULL)
301 		{
302 			free(ffp->bf_base);
303 			ffp->bf_base = NULL;
304 			ffp->bf_extent = 0;
305 		}
306 		assert(ffp->bf_extent == 0);
307 		ffp->bf_base = malloc(extent);
308 		if(ffp->bf_base == NULL)
309 			return ENOMEM;
310 		ffp->bf_extent = extent;
311 	}
312 
313 	status = ffio_pgin(nciop, offset,
314 		 extent,
315 		 ffp->bf_base,
316 		 &ffp->bf_cnt, &ffp->pos);
317 	if(status != NC_NOERR)
318 		return status;
319 
320 	ffp->bf_offset = offset;
321 
322 	if(ffp->bf_cnt < extent)
323 	{
324 		(void) memset((char *)ffp->bf_base + ffp->bf_cnt, 0,
325 			extent - ffp->bf_cnt);
326 		ffp->bf_cnt = extent;
327 	}
328 
329 
330 #ifdef X_ALIGN
331 	*vpp = (char *)ffp->bf_base + rem;
332 #else
333 	*vpp = (char *)ffp->bf_base;
334 #endif
335 	return NC_NOERR;
336 }
337 
338 
339 static int
ncio_ffio_move(ncio * const nciop,off_t to,off_t from,size_t nbytes,int rflags)340 ncio_ffio_move(ncio *const nciop, off_t to, off_t from,
341 			size_t nbytes, int rflags)
342 {
343 	int status = NC_NOERR;
344 	off_t lower = from;
345 	off_t upper = to;
346 	char *base;
347 	size_t diff = upper - lower;
348 	size_t extent = diff + nbytes;
349 
350 	rflags &= RGN_NOLOCK; /* filter unwanted flags */
351 
352 	if(to == from)
353 		return NC_NOERR; /* NOOP */
354 
355 	if(to > from)
356 	{
357 		/* growing */
358 		lower = from;
359 		upper = to;
360 	}
361 	else
362 	{
363 		/* shrinking */
364 		lower = to;
365 		upper = from;
366 	}
367 
368 	diff = upper - lower;
369 	extent = diff + nbytes;
370 
371 	status = ncio_ffio_get(nciop, lower, extent, RGN_WRITE|rflags,
372 			(void **)&base);
373 
374 	if(status != NC_NOERR)
375 		return status;
376 
377 	if(to > from)
378 		(void) memmove(base + diff, base, nbytes);
379 	else
380 		(void) memmove(base, base + diff, nbytes);
381 
382 	(void) ncio_ffio_rel(nciop, lower, RGN_MODIFIED);
383 
384 	return status;
385 }
386 
387 #ifdef NOFFFLUSH
388 /* ncio_ffio_sync_noffflush is only needed if the FFIO global layer is
389  * used, because it currently has a bug that causes the PEs to hang
390  * RKO 06/26/98
391  */
392 static int
ncio_ffio_sync_noffflush(ncio * const nciop)393 ncio_ffio_sync_noffflush(ncio *const nciop)
394 {
395 	struct ffc_stat_s si;	/* for call to fffcntl() */
396 	struct ffsw ffstatus;	/* to return ffsw.sw_error */
397 	/* run some innocuous ffio routine to get if any errno */
398 	if(fffcntl(nciop->fd, FC_STAT, &si, &ffstatus) < 0)
399 		return ffstatus.sw_error;
400 	return NC_NOERR;
401 }
402 /* this tests to see if the global FFIO layer is being called for
403  * returns ~0 if it is, else returns 0
404  */
405 static int
ncio_ffio_global_test(const char * ControlString)406 ncio_ffio_global_test(const char *ControlString)
407 {
408 	if (strstr(ControlString,"global") != (char *) NULL) {
409 		return ~0;
410 	} else {
411 		return 0;
412 	}
413 }
414 #endif
415 
416 static int
ncio_ffio_sync(ncio * const nciop)417 ncio_ffio_sync(ncio *const nciop)
418 {
419 	int test_flush;
420 #ifdef __crayx1
421 	struct ffsw stat;
422 	test_flush = ffflush(nciop->fd,&stat) < 0;
423 #else
424 	test_flush = ffflush(nciop->fd) < 0;
425 #endif
426 	if(test_flush)
427 		return errno;
428 	return NC_NOERR;
429 }
430 
431 static void
ncio_ffio_free(void * const pvt)432 ncio_ffio_free(void *const pvt)
433 {
434 	ncio_ffio *ffp = (ncio_ffio *)pvt;
435 	if(ffp == NULL)
436 		return;
437 
438 	if(ffp->bf_base != NULL)
439 	{
440 		free(ffp->bf_base);
441 		ffp->bf_base = NULL;
442 		ffp->bf_offset = OFF_NONE;
443 		ffp->bf_extent = 0;
444 		ffp->bf_cnt = 0;
445 	}
446 }
447 
448 
449 static int
ncio_ffio_init2(ncio * const nciop,size_t * sizehintp)450 ncio_ffio_init2(ncio *const nciop, size_t *sizehintp)
451 {
452 	ncio_ffio *ffp = (ncio_ffio *)nciop->pvt;
453 
454 	assert(nciop->fd >= 0);
455 
456 	ffp->bf_extent = *sizehintp;
457 
458 	assert(ffp->bf_base == NULL);
459 
460 	/* this is separate allocation because it may grow */
461 	ffp->bf_base = malloc(ffp->bf_extent);
462 	if(ffp->bf_base == NULL)
463 	{
464 		ffp->bf_extent = 0;
465 		return ENOMEM;
466 	}
467 	/* else */
468 	return NC_NOERR;
469 }
470 
471 
472 static void
ncio_ffio_init(ncio * const nciop)473 ncio_ffio_init(ncio *const nciop)
474 {
475 	ncio_ffio *ffp = (ncio_ffio *)nciop->pvt;
476 
477 	*((ncio_relfunc **)&nciop->rel) = ncio_ffio_rel; /* cast away const */
478 	*((ncio_getfunc **)&nciop->get) = ncio_ffio_get; /* cast away const */
479 	*((ncio_movefunc **)&nciop->move) = ncio_ffio_move; /* cast away const */
480 	*((ncio_syncfunc **)&nciop->sync) = ncio_ffio_sync; /* cast away const */
481 	*((ncio_filesizefunc **)&nciop->filesize) = ncio_ffio_filesize; /* cast away const */
482 	*((ncio_pad_lengthfunc **)&nciop->pad_length) = ncio_ffio_pad_length; /* cast away const */
483 	*((ncio_closefunc **)&nciop->close) = ncio_ffio_close; /* cast away const */
484 
485 	ffp->pos = -1;
486 	ffp->bf_offset = OFF_NONE;
487 	ffp->bf_extent = 0;
488 	ffp->bf_cnt = 0;
489 	ffp->bf_base = NULL;
490 }
491 
492 /* */
493 
494 static void
ncio_free(ncio * nciop)495 ncio_free(ncio *nciop)
496 {
497 	if(nciop == NULL)
498 		return;
499 
500 	if(nciop->free != NULL)
501 		nciop->free(nciop->pvt);
502 
503 	free(nciop);
504 }
505 
506 
507 static ncio *
ncio_ffio_new(const char * path,int ioflags)508 ncio_ffio_new(const char *path, int ioflags)
509 {
510 	size_t sz_ncio = M_RNDUP(sizeof(ncio));
511 	size_t sz_path = M_RNDUP(strlen(path) +1);
512 	size_t sz_ncio_pvt;
513 	ncio *nciop;
514 
515 #if ALWAYS_NC_SHARE /* DEBUG */
516 	fSet(ioflags, NC_SHARE);
517 #endif
518 
519 	if(fIsSet(ioflags, NC_SHARE))
520 		fprintf(stderr, "NC_SHARE not implemented for ffio\n");
521 
522 	sz_ncio_pvt = sizeof(ncio_ffio);
523 
524 	nciop = (ncio *) malloc(sz_ncio + sz_path + sz_ncio_pvt);
525 	if(nciop == NULL)
526 		return NULL;
527 
528 	nciop->ioflags = ioflags;
529 	*((int *)&nciop->fd) = -1; /* cast away const */
530 
531 	nciop->path = (char *) ((char *)nciop + sz_ncio);
532 	(void) strcpy((char *)nciop->path, path); /* cast away const */
533 
534 				/* cast away const */
535 	*((void **)&nciop->pvt) = (void *)(nciop->path + sz_path);
536 
537 	ncio_ffio_init(nciop);
538 
539 	return nciop;
540 }
541 
542 /* put all the FFIO assign specific code here
543  * returns a pointer to an internal static char location
544  * which may change when the function is called again
545  * if the returned pointer is NULL this indicates that an error occurred
546  * check errno for the netCDF error value
547  */
548 /* prototype fortran subroutines */
549 #ifdef __crayx1
550 void ASNQFILE(const char *filename, const char *attribute, int *istat, int flen, int alen);
551 void ASNFILE(const char *filename, const char *attribute, int *istat, int flen, int alen);
552 #else
553 void ASNQFILE(_fcd filename, _fcd attribute, int *istat);
554 void ASNFILE(_fcd filename, _fcd attribute, int *istat);
555 #endif
556 
557 #define BUFLEN 256
558 static const char *
ncio_ffio_assign(const char * filename)559 ncio_ffio_assign(const char *filename) {
560 	static char buffer[BUFLEN];
561 	int istat;
562 #ifndef __crayx1
563 	_fcd fnp, fbp;
564 #endif
565 	char *envstr;
566 	char *xtra_assign;
567 	char emptystr='\0';
568 
569 /* put things into known states */
570 	memset(buffer,'\0',BUFLEN);
571 	errno = NC_NOERR;
572 
573 /* set up Fortran character pointers */
574 #ifdef __crayx1
575 	ASNQFILE(filename, buffer, &istat, strlen(filename)+1, BUFLEN);
576 #else
577 	fnp = _cptofcd((char *)filename, strlen(filename));
578 	fbp = _cptofcd(buffer, BUFLEN);
579 
580 /* see if the user has "assigned" to this file */
581 	ASNQFILE(fnp, fbp, &istat);
582 #endif
583 	if (istat == 0) {	/* user has already specified an assign */
584 		return buffer;
585 	} else if (istat > 0 || istat < -1) {	/* error occurred */
586 		errno = EINVAL;
587 		return (const char *) NULL;
588 	} /* istat = -1 -> no assign for file */
589 	envstr = getenv("NETCDF_FFIOSPEC");
590 	if(envstr == (char *) NULL) {
591 		 envstr = "bufa:336:2";		/* this should be macroized */
592 	}
593 
594 	/* Insertion by Olaf Heudecker, AWI-Bremerhaven, 12.8.1998
595 	   to allow more versatile FFIO-assigns */
596 	/* this is unnecessary and could have been included
597 	 * into the NETCDF_FFIOSPEC environment variable */
598 	xtra_assign = getenv("NETCDF_XFFIOSPEC");
599 	if(xtra_assign == (char *) NULL) {
600 		xtra_assign=&emptystr;
601 	}
602 	if (strlen(envstr)+strlen(xtra_assign) + 4 > BUFLEN) {
603 	/* Error: AssignCommand too long */
604 		errno=E2BIG;
605 		return (const char *) NULL;
606 	}
607 	(void) sprintf(buffer,"-F %s %s", envstr,xtra_assign);
608 #ifdef __crayx1
609 	ASNFILE(filename, buffer, &istat, strlen(filename)+1, strlen(buffer)+1);
610 #else
611 	fbp = _cptofcd(buffer, strlen(buffer));
612 	ASNFILE(fnp, fbp, &istat);
613 #endif
614 	if (istat == 0) {	/* success */
615 		return buffer;
616 	} else {		/* error */
617 		errno = EINVAL;
618 		return (const char *) NULL;
619 	}
620 }
621 
622 /* Public below this point */
623 
624 /* TODO: Is this reasonable for this platform? */
625 static const size_t NCIO_MINBLOCKSIZE = 256;
626 static const size_t NCIO_MAXBLOCKSIZE = 268435456; /* sanity check, about X_SIZE_T_MAX/8 */
627 
628 int
ffio_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)629 ffio_create(const char *path, int ioflags,
630 	size_t initialsz,
631 	off_t igeto, size_t igetsz, size_t *sizehintp,
632 	void* parameters,
633 	ncio **nciopp, void **const igetvpp)
634 {
635 	ncio *nciop;
636 	const char *ControlString;
637 	int oflags = (O_RDWR|O_CREAT|O_TRUNC);
638 	int fd;
639 	int status;
640 	struct ffsw stat;
641 
642 	if(initialsz < (size_t)igeto + igetsz)
643 		initialsz = (size_t)igeto + igetsz;
644 
645 	fSet(ioflags, NC_WRITE);
646 
647 	if(path == NULL || *path == 0)
648 		return EINVAL;
649 
650 	nciop = ncio_ffio_new(path, ioflags);
651 	if(nciop == NULL)
652 		return ENOMEM;
653 
654 	if ((ControlString = ncio_ffio_assign(path)) == (const char *)NULL) {
655 		/* an error occurred - just punt */
656 		status = errno;
657 		goto unwind_new;
658 	}
659 #ifdef NOFFFLUSH
660 	/* test whether the global layer is being called for
661 	 * this file ... if so then can't call FFIO ffflush()
662 	 * RKO 06/26/98
663 	 */
664 	if (strstr(ControlString,"global") != (char *) NULL) {
665 		/* use no ffflush version */
666 		*((ncio_syncfunc **)&nciop->sync)
667 			= ncio_ffio_sync_noffflush;
668 	}
669 #endif
670 	if(fIsSet(ioflags, NC_NOCLOBBER))
671 		fSet(oflags, O_EXCL);
672 
673 	/* Orig: fd = ffopens(path, oflags, 0666, 0, &stat, ControlString); */
674 	fd = ffopen(path, oflags, 0666, 0, &stat);
675 	if(fd < 0)
676 	{
677 		status = errno;
678 		goto unwind_new;
679 	}
680 	*((int *)&nciop->fd) = fd; /* cast away const */
681 
682 	if(*sizehintp < NCIO_MINBLOCKSIZE || *sizehintp > NCIO_MAXBLOCKSIZE)
683 	{
684 		/* Use default */
685 		*sizehintp = blksize(fd);
686 	}
687 	else
688 	{
689 		*sizehintp = M_RNDUP(*sizehintp);
690 	}
691 
692 	status = ncio_ffio_init2(nciop, sizehintp);
693 	if(status != NC_NOERR)
694 		goto unwind_open;
695 
696 	if(initialsz != 0)
697 	{
698 		status = fgrow(fd, (off_t)initialsz);
699 		if(status != NC_NOERR)
700 			goto unwind_open;
701 	}
702 
703 	if(igetsz != 0)
704 	{
705 		status = nciop->get(nciop,
706 				igeto, igetsz,
707                         	RGN_WRITE,
708                         	igetvpp);
709 		if(status != NC_NOERR)
710 			goto unwind_open;
711 	}
712 
713 	*nciopp = nciop;
714 	return NC_NOERR;
715 
716 unwind_open:
717 	(void) ffclose(fd);
718 	/* ?? unlink */
719 	/*FALLTHRU*/
720 unwind_new:
721 	ncio_close(nciop,!fIsSet(ioflags, NC_NOCLOBBER));
722 	return status;
723 }
724 
725 
726 int
ffio_open(const char * path,int ioflags,off_t igeto,size_t igetsz,size_t * sizehintp,void * parameters,ncio ** nciopp,void ** const igetvpp)727 ffio_open(const char *path,
728 	int ioflags,
729 	off_t igeto, size_t igetsz, size_t *sizehintp,
730 	void* parameters,
731 	ncio **nciopp, void **const igetvpp)
732 {
733 	ncio *nciop;
734 	const char *ControlString;
735 	int oflags = fIsSet(ioflags, NC_WRITE) ? O_RDWR : O_RDONLY;
736 	int fd;
737 	int status;
738 	struct ffsw stat;
739 
740 	if(path == NULL || *path == 0)
741 		return EINVAL;
742 
743 	nciop = ncio_ffio_new(path, ioflags);
744 	if(nciop == NULL)
745 		return ENOMEM;
746 
747 	if ((ControlString = ncio_ffio_assign(path)) == (const char *)NULL) {
748 		/* an error occurred - just punt */
749 		status = errno;
750 		goto unwind_new;
751 	}
752 #ifdef NOFFFLUSH
753 	/* test whether the global layer is being called for
754 	 * this file ... if so then can't call FFIO ffflush()
755 	 * RKO 06/26/98
756 	 */
757 	if (strstr(ControlString,"global") != (char *) NULL) {
758 		/* use no ffflush version */
759 		*((ncio_syncfunc **)&nciop->sync)
760 			= ncio_ffio_sync_noffflush;
761 	}
762 #endif
763 
764 	/* Orig: fd = ffopens(path, oflags, 0, 0, &stat, ControlString); */
765 	fd = ffopen(path, oflags, 0, 0, &stat);
766 
767 	if(fd < 0)
768 	{
769 		status = errno;
770 		goto unwind_new;
771 	}
772 	*((int *)&nciop->fd) = fd; /* cast away const */
773 
774 	if(*sizehintp < NCIO_MINBLOCKSIZE || *sizehintp > NCIO_MAXBLOCKSIZE)
775 	{
776 		/* Use default */
777 		*sizehintp = blksize(fd);
778 	}
779 	else
780 	{
781 		*sizehintp = M_RNDUP(*sizehintp);
782 	}
783 
784 	status = ncio_ffio_init2(nciop, sizehintp);
785 	if(status != NC_NOERR)
786 		goto unwind_open;
787 
788 	if(igetsz != 0)
789 	{
790 		status = nciop->get(nciop,
791 				igeto, igetsz,
792                         	0,
793                         	igetvpp);
794 		if(status != NC_NOERR)
795 			goto unwind_open;
796 	}
797 
798 	*nciopp = nciop;
799 	return NC_NOERR;
800 
801 unwind_open:
802 	(void) ffclose(fd);
803 	/*FALLTHRU*/
804 unwind_new:
805 	ncio_close(nciop,0);
806 	return status;
807 }
808 
809 
810 /*
811  * Get file size in bytes.
812  * Is use of ffseek() really necessary, or could we use standard fstat() call
813  * and get st_size member?
814  */
815 static int
ncio_ffio_filesize(ncio * nciop,off_t * filesizep)816 ncio_ffio_filesize(ncio *nciop, off_t *filesizep)
817 {
818     off_t filesize, current, reset;
819 
820     if(nciop == NULL)
821 	return EINVAL;
822 
823     current = ffseek(nciop->fd, 0, SEEK_CUR);  /* save current */
824     *filesizep = ffseek(nciop->fd, 0, SEEK_END); /* get size */
825     reset = ffseek(nciop->fd, current, SEEK_SET); /* reset */
826 
827     if(reset != current)
828 	return EINVAL;
829     return NC_NOERR;
830 }
831 
832 
833 /*
834  * Sync any changes to disk, then extend file so its size is length.
835  * This is only intended to be called before close, if the file is
836  * open for writing and the actual size does not match the calculated
837  * size, perhaps as the result of having been previously written in
838  * NOFILL mode.
839  */
840 static int
ncio_ffio_pad_length(ncio * nciop,off_t length)841 ncio_ffio_pad_length(ncio *nciop, off_t length)
842 {
843 	int status = NC_NOERR;
844 
845 	if(nciop == NULL)
846 		return EINVAL;
847 
848 	if(!fIsSet(nciop->ioflags, NC_WRITE))
849 	        return EPERM; /* attempt to write readonly file */
850 
851 	status = nciop->sync(nciop);
852 	if(status != NC_NOERR)
853 	        return status;
854 
855 	status = fgrow2(nciop->fd, length);
856 	if(status != NC_NOERR)
857 	        return errno;
858 	return NC_NOERR;
859 }
860 
861 
862 static int
ncio_ffio_close(ncio * nciop,int doUnlink)863 ncio_ffio_close(ncio *nciop, int doUnlink)
864 {
865 	/*
866          * TODO: I believe this function is lacking the de-assignment of the
867          * Fortran LUN assigned by ASNFILE in ncio_ffio_assign(...) -- SRE
868          * 2002-07-10.
869 	 */
870 
871 	int status = NC_NOERR;
872 
873 	if(nciop == NULL)
874 		return EINVAL;
875 
876 	status = nciop->sync(nciop);
877 
878 	(void) ffclose(nciop->fd);
879 
880 	if(doUnlink)
881 		(void) unlink(nciop->path);
882 
883 	ncio__ffio_free(nciop);
884 
885 	return status;
886 }
887