mirror of
https://github.com/Unidata/netcdf-c.git
synced 2024-11-27 07:30:33 +08:00
887 lines
18 KiB
C
887 lines
18 KiB
C
/*
|
|
* Copyright 2018, University Corporation for Atmospheric Research
|
|
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
|
|
*/
|
|
/* $Id: ffio.c,v 1.56 2006/09/15 20:40:30 ed Exp $ */
|
|
/* addition by O. Heudecker, AWI-Bremerhaven, 12.3.1998 */
|
|
/* added correction by John Sheldon and Hans Vahlenkamp 15.4.1998*/
|
|
|
|
|
|
#if HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h> /* DEBUG */
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#ifndef NC_NOERR
|
|
#define NC_NOERR 0
|
|
#endif
|
|
#ifdef HAVE_FCNTL_H
|
|
#include <fcntl.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_STAT_H
|
|
#include <sys/stat.h>
|
|
#endif
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#if 0
|
|
/* Insertion by O. R. Heudecker, AWI-Bremerhaven 12.3.98 (1 line)*/
|
|
#include <ffio.h>
|
|
#include <fortran.h>
|
|
#endif
|
|
|
|
#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 */
|
|
|
|
/* Forward */
|
|
static int ncio_ffio_filesize(ncio *nciop, off_t *filesizep);
|
|
static int ncio_ffio_pad_length(ncio *nciop, off_t length);
|
|
static int ncio_ffio_close(ncio *nciop, int doUnlink);
|
|
|
|
|
|
/* 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;
|
|
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
|
|
#ifdef HAVE_FCNTL_H
|
|
if (fffcntl(fd, FC_STAT, &sb, &sw) > -1)
|
|
{
|
|
#ifdef __crayx1
|
|
if(sb.st_blksize > 0)
|
|
return (size_t) sb.st_blksize;
|
|
#else
|
|
if(sb.st_oblksize > 0)
|
|
return (size_t) sb.st_oblksize;
|
|
#endif
|
|
}
|
|
#endif
|
|
#endif
|
|
/* 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 NC_NOERR;
|
|
{
|
|
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 NC_NOERR;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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 NC_NOERR;
|
|
{
|
|
const char dumb = 0;
|
|
/* we don't use ftruncate() due to problem with FAT32 file systems */
|
|
/* cache current position */
|
|
const off_t pos = ffseek(fd, 0, SEEK_CUR);
|
|
if(pos < 0)
|
|
return errno;
|
|
if (ffseek(fd, len-1, SEEK_SET) < 0)
|
|
return errno;
|
|
if(ffwrite(fd, (void *)&dumb, sizeof(dumb)) < 0)
|
|
return errno;
|
|
if (ffseek(fd, pos, SEEK_SET) < 0)
|
|
return errno;
|
|
}
|
|
return NC_NOERR;
|
|
}
|
|
/* 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 NC_NOERR;
|
|
}
|
|
|
|
|
|
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 != NC_NOERR)
|
|
return status;
|
|
/* else it's okay we read 0. */
|
|
}
|
|
*nreadp = nread;
|
|
*posp += nread;
|
|
|
|
return NC_NOERR;
|
|
}
|
|
|
|
/* */
|
|
|
|
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 = NC_NOERR;
|
|
|
|
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 = NC_NOERR;
|
|
#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 != NC_NOERR)
|
|
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 NC_NOERR;
|
|
}
|
|
|
|
|
|
static int
|
|
ncio_ffio_move(ncio *const nciop, off_t to, off_t from,
|
|
size_t nbytes, int rflags)
|
|
{
|
|
int status = NC_NOERR;
|
|
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 NC_NOERR; /* 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 != NC_NOERR)
|
|
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 NC_NOERR;
|
|
}
|
|
/* 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)
|
|
{
|
|
int test_flush;
|
|
#ifdef __crayx1
|
|
struct ffsw stat;
|
|
test_flush = ffflush(nciop->fd,&stat) < 0;
|
|
#else
|
|
test_flush = ffflush(nciop->fd) < 0;
|
|
#endif
|
|
if(test_flush)
|
|
return errno;
|
|
return NC_NOERR;
|
|
}
|
|
|
|
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 NC_NOERR;
|
|
}
|
|
|
|
|
|
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_filesizefunc **)&nciop->filesize) = ncio_ffio_filesize; /* cast away const */
|
|
*((ncio_pad_lengthfunc **)&nciop->pad_length) = ncio_ffio_pad_length; /* cast away const */
|
|
*((ncio_closefunc **)&nciop->close) = ncio_ffio_close; /* 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_ffio_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 occurred
|
|
* check errno for the netCDF error value
|
|
*/
|
|
/* prototype fortran subroutines */
|
|
#ifdef __crayx1
|
|
void ASNQFILE(const char *filename, const char *attribute, int *istat, int flen, int alen);
|
|
void ASNFILE(const char *filename, const char *attribute, int *istat, int flen, int alen);
|
|
#else
|
|
void ASNQFILE(_fcd filename, _fcd attribute, int *istat);
|
|
void ASNFILE(_fcd filename, _fcd attribute, int *istat);
|
|
#endif
|
|
|
|
#define BUFLEN 256
|
|
static const char *
|
|
ncio_ffio_assign(const char *filename) {
|
|
static char buffer[BUFLEN];
|
|
int istat;
|
|
#ifndef __crayx1
|
|
_fcd fnp, fbp;
|
|
#endif
|
|
char *envstr;
|
|
char *xtra_assign;
|
|
char emptystr='\0';
|
|
|
|
/* put things into known states */
|
|
memset(buffer,'\0',BUFLEN);
|
|
errno = NC_NOERR;
|
|
|
|
/* set up Fortran character pointers */
|
|
#ifdef __crayx1
|
|
ASNQFILE(filename, buffer, &istat, strlen(filename)+1, BUFLEN);
|
|
#else
|
|
fnp = _cptofcd((char *)filename, strlen(filename));
|
|
fbp = _cptofcd(buffer, BUFLEN);
|
|
|
|
/* see if the user has "assigned" to this file */
|
|
ASNQFILE(fnp, fbp, &istat);
|
|
#endif
|
|
if (istat == 0) { /* user has already specified an assign */
|
|
return buffer;
|
|
} else if (istat > 0 || istat < -1) { /* error occurred */
|
|
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);
|
|
#ifdef __crayx1
|
|
ASNFILE(filename, buffer, &istat, strlen(filename)+1, strlen(buffer)+1);
|
|
#else
|
|
fbp = _cptofcd(buffer, strlen(buffer));
|
|
ASNFILE(fnp, fbp, &istat);
|
|
#endif
|
|
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
|
|
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)
|
|
{
|
|
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_ffio_new(path, ioflags);
|
|
if(nciop == NULL)
|
|
return ENOMEM;
|
|
|
|
if ((ControlString = ncio_ffio_assign(path)) == (const char *)NULL) {
|
|
/* an error occurred - 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 != NC_NOERR)
|
|
goto unwind_open;
|
|
|
|
if(initialsz != 0)
|
|
{
|
|
status = fgrow(fd, (off_t)initialsz);
|
|
if(status != NC_NOERR)
|
|
goto unwind_open;
|
|
}
|
|
|
|
if(igetsz != 0)
|
|
{
|
|
status = nciop->get(nciop,
|
|
igeto, igetsz,
|
|
RGN_WRITE,
|
|
igetvpp);
|
|
if(status != NC_NOERR)
|
|
goto unwind_open;
|
|
}
|
|
|
|
*nciopp = nciop;
|
|
return NC_NOERR;
|
|
|
|
unwind_open:
|
|
(void) ffclose(fd);
|
|
/* ?? unlink */
|
|
/*FALLTHRU*/
|
|
unwind_new:
|
|
ncio_close(nciop,!fIsSet(ioflags, NC_NOCLOBBER));
|
|
return status;
|
|
}
|
|
|
|
|
|
int
|
|
ffio_open(const char *path,
|
|
int ioflags,
|
|
off_t igeto, size_t igetsz, size_t *sizehintp,
|
|
void* parameters,
|
|
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_ffio_new(path, ioflags);
|
|
if(nciop == NULL)
|
|
return ENOMEM;
|
|
|
|
if ((ControlString = ncio_ffio_assign(path)) == (const char *)NULL) {
|
|
/* an error occurred - 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 != NC_NOERR)
|
|
goto unwind_open;
|
|
|
|
if(igetsz != 0)
|
|
{
|
|
status = nciop->get(nciop,
|
|
igeto, igetsz,
|
|
0,
|
|
igetvpp);
|
|
if(status != NC_NOERR)
|
|
goto unwind_open;
|
|
}
|
|
|
|
*nciopp = nciop;
|
|
return NC_NOERR;
|
|
|
|
unwind_open:
|
|
(void) ffclose(fd);
|
|
/*FALLTHRU*/
|
|
unwind_new:
|
|
ncio_close(nciop,0);
|
|
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?
|
|
*/
|
|
static int
|
|
ncio_ffio_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 NC_NOERR;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
static int
|
|
ncio_ffio_pad_length(ncio *nciop, off_t length)
|
|
{
|
|
int status = NC_NOERR;
|
|
|
|
if(nciop == NULL)
|
|
return EINVAL;
|
|
|
|
if(!fIsSet(nciop->ioflags, NC_WRITE))
|
|
return EPERM; /* attempt to write readonly file */
|
|
|
|
status = nciop->sync(nciop);
|
|
if(status != NC_NOERR)
|
|
return status;
|
|
|
|
status = fgrow2(nciop->fd, length);
|
|
if(status != NC_NOERR)
|
|
return errno;
|
|
return NC_NOERR;
|
|
}
|
|
|
|
|
|
static int
|
|
ncio_ffio_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 = NC_NOERR;
|
|
|
|
if(nciop == NULL)
|
|
return EINVAL;
|
|
|
|
status = nciop->sync(nciop);
|
|
|
|
(void) ffclose(nciop->fd);
|
|
|
|
if(doUnlink)
|
|
(void) unlink(nciop->path);
|
|
|
|
ncio__ffio_free(nciop);
|
|
|
|
return status;
|
|
}
|