/other/netcdf_write_matrix/src/libsrc/ffio.c
C | 831 lines | 597 code | 132 blank | 102 comment | 154 complexity | f85bcee25474c004d6fde331bcbb1363 MD5 | raw file
Possible License(s): AGPL-1.0
- /*
- * Copyright 1996, University Corporation for Atmospheric Research
- * See netcdf/COPYRIGHT file for copying and redistribution conditions.
- */
- /* $Id: ffio.c,v 1.55 2006/01/03 04:54:00 russ Exp $ */
- /* addition by O. Heudecker, AWI-Bremerhaven, 12.3.1998 */
- /* added correction by John Sheldon and Hans Vahlenkamp 15.4.1998*/
- #include "ncconfig.h"
- #include <assert.h>
- #include <stdlib.h>
- #include <stdio.h> /* DEBUG */
- #include <errno.h>
- #ifndef ENOERR
- #define ENOERR 0
- #endif
- #include <fcntl.h>
- #include <ffio.h>
- #include <unistd.h>
- #include <string.h>
- /* Insertion by O. R. Heudecker, AWI-Bremerhaven 12.3.98 (1 line)*/
- #include <fortran.h>
- #include "ncio.h"
- #include "fbits.h"
- #include "rnd.h"
- #if !defined(NDEBUG) && !defined(X_INT_MAX)
- #define X_INT_MAX 2147483647
- #endif
- #if 0 /* !defined(NDEBUG) && !defined(X_ALIGN) */
- #define X_ALIGN 4
- #endif
- #define ALWAYS_NC_SHARE 0 /* DEBUG */
- /* Begin OS */
- /*
- * What is the preferred I/O block size?
- * (This becomes the default *sizehint == ncp->chunk in the higher layers.)
- * TODO: What is the the best answer here?
- */
- static size_t
- blksize(int fd)
- {
- struct ffc_stat_s sb;
- struct ffsw sw;
- if (fffcntl(fd, FC_STAT, &sb, &sw) > -1)
- {
- if(sb.st_oblksize > 0)
- return (size_t) sb.st_oblksize;
- }
- /* else, silent in the face of error */
- return (size_t) 32768;
- }
- /*
- * Sortof like ftruncate, except won't make the
- * file shorter.
- */
- static int
- fgrow(const int fd, const off_t len)
- {
- struct ffc_stat_s sb;
- struct ffsw sw;
- if (fffcntl(fd, FC_STAT, &sb, &sw) < 0)
- return errno;
- if (len < sb.st_size)
- return ENOERR;
- {
- const long dumb = 0;
- /* cache current position */
- const off_t pos = ffseek(fd, 0, SEEK_CUR);
- if(pos < 0)
- return errno;
- if (ffseek(fd, len-sizeof(dumb), SEEK_SET) < 0)
- return errno;
- if(ffwrite(fd, (void *)&dumb, sizeof(dumb)) < 0)
- return errno;
- if (ffseek(fd, pos, SEEK_SET) < 0)
- return errno;
- }
- /* else */
- return ENOERR;
- }
- /*
- * Sortof like ftruncate, except won't make the file shorter. Differs
- * from fgrow by only writing one byte at designated seek position, if
- * needed.
- */
- static int
- fgrow2(const int fd, const off_t len)
- {
- struct ffc_stat_s sb;
- struct ffsw sw;
- if (fffcntl(fd, FC_STAT, &sb, &sw) < 0)
- return errno;
- if (len <= sb.st_size)
- return ENOERR;
- {
- const char dumb = 0;
- /* we don't use ftruncate() due to problem with FAT32 file systems */
- /* cache current position */
- const off_t pos = lseek(fd, 0, SEEK_CUR);
- if(pos < 0)
- return errno;
- if (lseek(fd, len-1, SEEK_SET) < 0)
- return errno;
- if(write(fd, &dumb, sizeof(dumb)) < 0)
- return errno;
- if (lseek(fd, pos, SEEK_SET) < 0)
- return errno;
- }
- return ENOERR;
- }
- /* End OS */
- /* Begin ffio */
- static int
- ffio_pgout(ncio *const nciop,
- off_t const offset, const size_t extent,
- const void *const vp, off_t *posp)
- {
- #ifdef X_ALIGN
- assert(offset % X_ALIGN == 0);
- assert(extent % X_ALIGN == 0);
- #endif
- if(*posp != offset)
- {
- if(ffseek(nciop->fd, offset, SEEK_SET) != offset)
- {
- return errno;
- }
- *posp = offset;
- }
- if(ffwrite(nciop->fd, vp, extent) != extent)
- {
- return errno;
- }
- *posp += extent;
- return ENOERR;
- }
- static int
- ffio_pgin(ncio *const nciop,
- off_t const offset, const size_t extent,
- void *const vp, size_t *nreadp, off_t *posp)
- {
- int status;
- ssize_t nread;
- #ifdef X_ALIGN
- assert(offset % X_ALIGN == 0);
- assert(extent % X_ALIGN == 0);
- #endif
- if(*posp != offset)
- {
- if(ffseek(nciop->fd, offset, SEEK_SET) != offset)
- {
- status = errno;
- return status;
- }
- *posp = offset;
- }
- errno = 0;
- nread = ffread(nciop->fd, vp, extent);
- if(nread != extent)
- {
- status = errno;
- if(nread == -1 || status != ENOERR)
- return status;
- /* else it's okay we read 0. */
- }
- *nreadp = nread;
- *posp += nread;
- return ENOERR;
- }
- /* */
- typedef struct ncio_ffio {
- off_t pos;
- /* buffer */
- off_t bf_offset;
- size_t bf_extent;
- size_t bf_cnt;
- void *bf_base;
- } ncio_ffio;
- static int
- ncio_ffio_rel(ncio *const nciop, off_t offset, int rflags)
- {
- ncio_ffio *ffp = (ncio_ffio *)nciop->pvt;
- int status = ENOERR;
- assert(ffp->bf_offset <= offset);
- assert(ffp->bf_cnt != 0);
- assert(ffp->bf_cnt <= ffp->bf_extent);
- #ifdef X_ALIGN
- assert(offset < ffp->bf_offset + X_ALIGN);
- assert(ffp->bf_cnt % X_ALIGN == 0 );
- #endif
- if(fIsSet(rflags, RGN_MODIFIED))
- {
- if(!fIsSet(nciop->ioflags, NC_WRITE))
- return EPERM; /* attempt to write readonly file */
- status = ffio_pgout(nciop, ffp->bf_offset,
- ffp->bf_cnt,
- ffp->bf_base, &ffp->pos);
- /* if error, invalidate buffer anyway */
- }
- ffp->bf_offset = OFF_NONE;
- ffp->bf_cnt = 0;
- return status;
- }
- static int
- ncio_ffio_get(ncio *const nciop,
- off_t offset, size_t extent,
- int rflags,
- void **const vpp)
- {
- ncio_ffio *ffp = (ncio_ffio *)nciop->pvt;
- int status = ENOERR;
- #ifdef X_ALIGN
- size_t rem;
- #endif
-
- if(fIsSet(rflags, RGN_WRITE) && !fIsSet(nciop->ioflags, NC_WRITE))
- return EPERM; /* attempt to write readonly file */
- assert(extent != 0);
- assert(extent < X_INT_MAX); /* sanity check */
- assert(ffp->bf_cnt == 0);
- #ifdef X_ALIGN
- /* round to seekable boundaries */
- rem = offset % X_ALIGN;
- if(rem != 0)
- {
- offset -= rem;
- extent += rem;
- }
- {
- const size_t rndup = extent % X_ALIGN;
- if(rndup != 0)
- extent += X_ALIGN - rndup;
- }
- assert(offset % X_ALIGN == 0);
- assert(extent % X_ALIGN == 0);
- #endif
- if(ffp->bf_extent < extent)
- {
- if(ffp->bf_base != NULL)
- {
- free(ffp->bf_base);
- ffp->bf_base = NULL;
- ffp->bf_extent = 0;
- }
- assert(ffp->bf_extent == 0);
- ffp->bf_base = malloc(extent);
- if(ffp->bf_base == NULL)
- return ENOMEM;
- ffp->bf_extent = extent;
- }
- status = ffio_pgin(nciop, offset,
- extent,
- ffp->bf_base,
- &ffp->bf_cnt, &ffp->pos);
- if(status != ENOERR)
- return status;
- ffp->bf_offset = offset;
- if(ffp->bf_cnt < extent)
- {
- (void) memset((char *)ffp->bf_base + ffp->bf_cnt, 0,
- extent - ffp->bf_cnt);
- ffp->bf_cnt = extent;
- }
- #ifdef X_ALIGN
- *vpp = (char *)ffp->bf_base + rem;
- #else
- *vpp = (char *)ffp->bf_base;
- #endif
- return ENOERR;
- }
- static int
- ncio_ffio_move(ncio *const nciop, off_t to, off_t from,
- size_t nbytes, int rflags)
- {
- int status = ENOERR;
- off_t lower = from;
- off_t upper = to;
- char *base;
- size_t diff = upper - lower;
- size_t extent = diff + nbytes;
- rflags &= RGN_NOLOCK; /* filter unwanted flags */
- if(to == from)
- return ENOERR; /* NOOP */
-
- if(to > from)
- {
- /* growing */
- lower = from;
- upper = to;
- }
- else
- {
- /* shrinking */
- lower = to;
- upper = from;
- }
- diff = upper - lower;
- extent = diff + nbytes;
- status = ncio_ffio_get(nciop, lower, extent, RGN_WRITE|rflags,
- (void **)&base);
- if(status != ENOERR)
- return status;
- if(to > from)
- (void) memmove(base + diff, base, nbytes);
- else
- (void) memmove(base, base + diff, nbytes);
-
- (void) ncio_ffio_rel(nciop, lower, RGN_MODIFIED);
- return status;
- }
- #ifdef NOFFFLUSH
- /* ncio_ffio_sync_noffflush is only needed if the FFIO global layer is
- * used, because it currently has a bug that causes the PEs to hang
- * RKO 06/26/98
- */
- static int
- ncio_ffio_sync_noffflush(ncio *const nciop)
- {
- struct ffc_stat_s si; /* for call to fffcntl() */
- struct ffsw ffstatus; /* to return ffsw.sw_error */
- /* run some innocuous ffio routine to get if any errno */
- if(fffcntl(nciop->fd, FC_STAT, &si, &ffstatus) < 0)
- return ffstatus.sw_error;
- return ENOERR;
- }
- /* this tests to see if the global FFIO layer is being called for
- * returns ~0 if it is, else returns 0
- */
- static int
- ncio_ffio_global_test(const char *ControlString)
- {
- if (strstr(ControlString,"global") != (char *) NULL) {
- return ~0;
- } else {
- return 0;
- }
- }
- #endif
- static int
- ncio_ffio_sync(ncio *const nciop)
- {
- if(ffflush(nciop->fd) < 0)
- return errno;
- return ENOERR;
- }
- static void
- ncio_ffio_free(void *const pvt)
- {
- ncio_ffio *ffp = (ncio_ffio *)pvt;
- if(ffp == NULL)
- return;
- if(ffp->bf_base != NULL)
- {
- free(ffp->bf_base);
- ffp->bf_base = NULL;
- ffp->bf_offset = OFF_NONE;
- ffp->bf_extent = 0;
- ffp->bf_cnt = 0;
- }
- }
- static int
- ncio_ffio_init2(ncio *const nciop, size_t *sizehintp)
- {
- ncio_ffio *ffp = (ncio_ffio *)nciop->pvt;
- assert(nciop->fd >= 0);
- ffp->bf_extent = *sizehintp;
- assert(ffp->bf_base == NULL);
- /* this is separate allocation because it may grow */
- ffp->bf_base = malloc(ffp->bf_extent);
- if(ffp->bf_base == NULL)
- {
- ffp->bf_extent = 0;
- return ENOMEM;
- }
- /* else */
- return ENOERR;
- }
- static void
- ncio_ffio_init(ncio *const nciop)
- {
- ncio_ffio *ffp = (ncio_ffio *)nciop->pvt;
- *((ncio_relfunc **)&nciop->rel) = ncio_ffio_rel; /* cast away const */
- *((ncio_getfunc **)&nciop->get) = ncio_ffio_get; /* cast away const */
- *((ncio_movefunc **)&nciop->move) = ncio_ffio_move; /* cast away const */
- *((ncio_syncfunc **)&nciop->sync) = ncio_ffio_sync; /* cast away const */
- *((ncio_freefunc **)&nciop->free) = ncio_ffio_free; /* cast away const */
- ffp->pos = -1;
- ffp->bf_offset = OFF_NONE;
- ffp->bf_extent = 0;
- ffp->bf_cnt = 0;
- ffp->bf_base = NULL;
- }
- /* */
- static void
- ncio_free(ncio *nciop)
- {
- if(nciop == NULL)
- return;
- if(nciop->free != NULL)
- nciop->free(nciop->pvt);
-
- free(nciop);
- }
- static ncio *
- ncio_new(const char *path, int ioflags)
- {
- size_t sz_ncio = M_RNDUP(sizeof(ncio));
- size_t sz_path = M_RNDUP(strlen(path) +1);
- size_t sz_ncio_pvt;
- ncio *nciop;
-
- #if ALWAYS_NC_SHARE /* DEBUG */
- fSet(ioflags, NC_SHARE);
- #endif
- if(fIsSet(ioflags, NC_SHARE))
- fprintf(stderr, "NC_SHARE not implemented for ffio\n");
- sz_ncio_pvt = sizeof(ncio_ffio);
- nciop = (ncio *) malloc(sz_ncio + sz_path + sz_ncio_pvt);
- if(nciop == NULL)
- return NULL;
-
- nciop->ioflags = ioflags;
- *((int *)&nciop->fd) = -1; /* cast away const */
- nciop->path = (char *) ((char *)nciop + sz_ncio);
- (void) strcpy((char *)nciop->path, path); /* cast away const */
- /* cast away const */
- *((void **)&nciop->pvt) = (void *)(nciop->path + sz_path);
- ncio_ffio_init(nciop);
- return nciop;
- }
- /* put all the FFIO assign specific code here
- * returns a pointer to an internal static char location
- * which may change when the function is called again
- * if the returned pointer is NULL this indicates that an error occured
- * check errno for the netCDF error value
- */
- /* prototype fortran subroutines */
- void ASNQFILE(_fcd filename, _fcd attribute, int *istat);
- void ASNFILE(_fcd filename, _fcd attribute, int *istat);
- #define BUFLEN 256
- static const char *
- ncio_ffio_assign(const char *filename) {
- static char buffer[BUFLEN];
- int istat;
- _fcd fnp, fbp;
- char *envstr;
- char *xtra_assign;
- char emptystr='\0';
- /* put things into known states */
- memset(buffer,'\0',BUFLEN);
- errno = ENOERR;
- /* set up Fortran character pointers */
- fnp = _cptofcd((char *)filename, strlen(filename));
- fbp = _cptofcd(buffer, BUFLEN);
- /* see if the user has "assigned" to this file */
- ASNQFILE(fnp, fbp, &istat);
- if (istat == 0) { /* user has already specified an assign */
- return buffer;
- } else if (istat > 0 || istat < -1) { /* error occured */
- errno = EINVAL;
- return (const char *) NULL;
- } /* istat = -1 -> no assign for file */
- envstr = getenv("NETCDF_FFIOSPEC");
- if(envstr == (char *) NULL) {
- envstr = "bufa:336:2"; /* this should be macroized */
- }
-
- /* Insertion by Olaf Heudecker, AWI-Bremerhaven, 12.8.1998
- to allow more versatile FFIO-assigns */
- /* this is unnecessary and could have been included
- * into the NETCDF_FFIOSPEC environment variable */
- xtra_assign = getenv("NETCDF_XFFIOSPEC");
- if(xtra_assign == (char *) NULL) {
- xtra_assign=&emptystr;
- }
- if (strlen(envstr)+strlen(xtra_assign) + 4 > BUFLEN) {
- /* Error: AssignCommand too long */
- errno=E2BIG;
- return (const char *) NULL;
- }
- (void) sprintf(buffer,"-F %s %s", envstr,xtra_assign);
- fbp = _cptofcd(buffer, strlen(buffer));
- ASNFILE(fnp, fbp, &istat);
- if (istat == 0) { /* success */
- return buffer;
- } else { /* error */
- errno = EINVAL;
- return (const char *) NULL;
- }
- }
- /* Public below this point */
- /* TODO: Is this reasonable for this platform? */
- static const size_t NCIO_MINBLOCKSIZE = 256;
- static const size_t NCIO_MAXBLOCKSIZE = 268435456; /* sanity check, about X_SIZE_T_MAX/8 */
- 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)
- {
- ncio *nciop;
- const char *ControlString;
- int oflags = (O_RDWR|O_CREAT|O_TRUNC);
- int fd;
- int status;
- struct ffsw stat;
- if(initialsz < (size_t)igeto + igetsz)
- initialsz = (size_t)igeto + igetsz;
- fSet(ioflags, NC_WRITE);
- if(path == NULL || *path == 0)
- return EINVAL;
- nciop = ncio_new(path, ioflags);
- if(nciop == NULL)
- return ENOMEM;
- if ((ControlString = ncio_ffio_assign(path)) == (const char *)NULL) {
- /* an error occured - just punt */
- status = errno;
- goto unwind_new;
- }
- #ifdef NOFFFLUSH
- /* test whether the global layer is being called for
- * this file ... if so then can't call FFIO ffflush()
- * RKO 06/26/98
- */
- if (strstr(ControlString,"global") != (char *) NULL) {
- /* use no ffflush version */
- *((ncio_syncfunc **)&nciop->sync)
- = ncio_ffio_sync_noffflush;
- }
- #endif
- if(fIsSet(ioflags, NC_NOCLOBBER))
- fSet(oflags, O_EXCL);
- /* Orig: fd = ffopens(path, oflags, 0666, 0, &stat, ControlString); */
- fd = ffopen(path, oflags, 0666, 0, &stat);
- if(fd < 0)
- {
- status = errno;
- goto unwind_new;
- }
- *((int *)&nciop->fd) = fd; /* cast away const */
- if(*sizehintp < NCIO_MINBLOCKSIZE || *sizehintp > NCIO_MAXBLOCKSIZE)
- {
- /* Use default */
- *sizehintp = blksize(fd);
- }
- else
- {
- *sizehintp = M_RNDUP(*sizehintp);
- }
- status = ncio_ffio_init2(nciop, sizehintp);
- if(status != ENOERR)
- goto unwind_open;
- if(initialsz != 0)
- {
- status = fgrow(fd, (off_t)initialsz);
- if(status != ENOERR)
- goto unwind_open;
- }
- if(igetsz != 0)
- {
- status = nciop->get(nciop,
- igeto, igetsz,
- RGN_WRITE,
- igetvpp);
- if(status != ENOERR)
- goto unwind_open;
- }
- *nciopp = nciop;
- return ENOERR;
- unwind_open:
- (void) ffclose(fd);
- /* ?? unlink */
- /*FALLTHRU*/
- unwind_new:
- ncio_free(nciop);
- return status;
- }
- int
- ncio_open(const char *path,
- int ioflags,
- off_t igeto, size_t igetsz, size_t *sizehintp,
- ncio **nciopp, void **const igetvpp)
- {
- ncio *nciop;
- const char *ControlString;
- int oflags = fIsSet(ioflags, NC_WRITE) ? O_RDWR : O_RDONLY;
- int fd;
- int status;
- struct ffsw stat;
- if(path == NULL || *path == 0)
- return EINVAL;
- nciop = ncio_new(path, ioflags);
- if(nciop == NULL)
- return ENOMEM;
- if ((ControlString = ncio_ffio_assign(path)) == (const char *)NULL) {
- /* an error occured - just punt */
- status = errno;
- goto unwind_new;
- }
- #ifdef NOFFFLUSH
- /* test whether the global layer is being called for
- * this file ... if so then can't call FFIO ffflush()
- * RKO 06/26/98
- */
- if (strstr(ControlString,"global") != (char *) NULL) {
- /* use no ffflush version */
- *((ncio_syncfunc **)&nciop->sync)
- = ncio_ffio_sync_noffflush;
- }
- #endif
- /* Orig: fd = ffopens(path, oflags, 0, 0, &stat, ControlString); */
- fd = ffopen(path, oflags, 0, 0, &stat);
- if(fd < 0)
- {
- status = errno;
- goto unwind_new;
- }
- *((int *)&nciop->fd) = fd; /* cast away const */
- if(*sizehintp < NCIO_MINBLOCKSIZE || *sizehintp > NCIO_MAXBLOCKSIZE)
- {
- /* Use default */
- *sizehintp = blksize(fd);
- }
- else
- {
- *sizehintp = M_RNDUP(*sizehintp);
- }
- status = ncio_ffio_init2(nciop, sizehintp);
- if(status != ENOERR)
- goto unwind_open;
- if(igetsz != 0)
- {
- status = nciop->get(nciop,
- igeto, igetsz,
- 0,
- igetvpp);
- if(status != ENOERR)
- goto unwind_open;
- }
- *nciopp = nciop;
- return ENOERR;
- unwind_open:
- (void) ffclose(fd);
- /*FALLTHRU*/
- unwind_new:
- ncio_free(nciop);
- return status;
- }
- /*
- * Get file size in bytes.
- * Is use of ffseek() really necessary, or could we use standard fstat() call
- * and get st_size member?
- */
- int
- ncio_filesize(ncio *nciop, off_t *filesizep)
- {
- off_t filesize, current, reset;
- if(nciop == NULL)
- return EINVAL;
- current = ffseek(nciop->fd, 0, SEEK_CUR); /* save current */
- *filesizep = ffseek(nciop->fd, 0, SEEK_END); /* get size */
- reset = ffseek(nciop->fd, current, SEEK_SET); /* reset */
- if(reset != current)
- return EINVAL;
- return ENOERR;
- }
- /*
- * Sync any changes to disk, then extend file so its size is length.
- * This is only intended to be called before close, if the file is
- * open for writing and the actual size does not match the calculated
- * size, perhaps as the result of having been previously written in
- * NOFILL mode.
- */
- int
- ncio_pad_length(ncio *nciop, off_t length)
- {
- int status = ENOERR;
- if(nciop == NULL)
- return EINVAL;
- if(!fIsSet(nciop->ioflags, NC_WRITE))
- return EPERM; /* attempt to write readonly file */
- status = nciop->sync(nciop);
- if(status != ENOERR)
- return status;
- status = fgrow2(nciop->fd, length);
- if(status != ENOERR)
- return errno;
- return ENOERR;
- }
- int
- ncio_close(ncio *nciop, int doUnlink)
- {
- /*
- * TODO: I believe this function is lacking the de-assignment of the
- * Fortran LUN assigned by ASNFILE in ncio_ffio_assign(...) -- SRE
- * 2002-07-10.
- */
- int status = ENOERR;
- if(nciop == NULL)
- return EINVAL;
- status = nciop->sync(nciop);
- (void) ffclose(nciop->fd);
-
- if(doUnlink)
- (void) unlink(nciop->path);
- ncio_free(nciop);
- return status;
- }