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