/* * Copyright 2018, University Corporation for Atmospheric Research * See netcdf/COPYRIGHT file for copying and redistribution conditions. */ /* $Id: winceio.c,v 1.2 2010/05/04 17:30:04 dmh Exp $ */ /* Dennis Heimbigner 2010-3-04 */ #if HAVE_CONFIG_H #include #endif #include #include #include /* DEBUG */ #ifndef _WIN32_WCE #include #else #define EPERM NC_EPERM #define ENOMEM NC_ENOMEM #define EINVAL NC_EINVAL #define EIO NC_EINVAL #define EEXIST NC_EEXIST #endif #ifndef NC_NOERR #define NC_NOERR 0 #endif #include #include #include "ncpathmgr.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 */ #define DEFAULTBLKSIZE 32768 static FILE* descriptors[1024]; static int fdmax = 1; /* never use zero */ /* 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) { return (size_t) DEFAULTBLKSIZE; } /* * Sortof like ftruncate, except won't make the * file shorter. */ static int fgrow(FILE* f, const off_t len) { int status = NC_NOERR; long pos = ftell(f); long size; pos = ftell(f); status = fseek(f,0,SEEK_END); if(ferror(f)) return EIO; size = ftell(f); status = fseek(f,pos,SEEK_SET); if(ferror(f)) return EIO; if(len < size) return NC_NOERR; else { const long dumb = 0; status = fseek(f, len-sizeof(dumb), SEEK_SET); if(ferror(f)) return EIO; fwrite((const void *)&dumb, 1, sizeof(dumb), f); if(ferror(f)) return EIO; status = fseek(f, pos, SEEK_SET); if(ferror(f)) return EIO; } 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(FILE* f, const off_t len) { int status = NC_NOERR; long pos = ftell(f); long size; pos = ftell(f); status = fseek(f,0,SEEK_END); if(ferror(f)) return EIO; size = ftell(f); status = fseek(f,pos,SEEK_SET); if(ferror(f)) return EIO; if(len < size) return NC_NOERR; else { const char dumb = 0; status = fseek(f, len-sizeof(dumb), SEEK_SET); if(ferror(f)) return EIO; fwrite((const void *)&dumb, 1, sizeof(dumb), f); if(ferror(f)) return EIO; status = fseek(f, pos, SEEK_SET); if(ferror(f)) return EIO; } return NC_NOERR; } /* End OS */ /* Begin ffio */ static int fileio_pgout(ncio *const nciop, off_t const offset, const size_t extent, const void *const vp, off_t *posp) { int status = NC_NOERR; FILE* f = descriptors[nciop->fd]; #ifdef X_ALIGN assert(offset % X_ALIGN == 0); assert(extent % X_ALIGN == 0); #endif if(*posp != offset) { status = fseek(f, offset, SEEK_SET); if(ferror(f)) return EIO; *posp = offset; } fwrite(vp,1,extent,f); if(ferror(f)) return EIO; *posp += extent; return NC_NOERR; } static int fileio_pgin(ncio *const nciop, off_t const offset, const size_t extent, void *const vp, size_t *nreadp, off_t *posp) { int status = NC_NOERR; ssize_t nread; int count; FILE* f = descriptors[nciop->fd]; #ifdef X_ALIGN assert(offset % X_ALIGN == 0); assert(extent % X_ALIGN == 0); #endif if(*posp != offset) { status = fseek(f, offset, SEEK_SET); if(ferror(f)) return EIO; *posp = offset; } nread = fread(vp,1,extent,f); if(ferror(f)) return EIO; *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_fileio_rel(ncio *const nciop, off_t offset, int rflags) { int status = NC_NOERR; FILE* f = descriptors[nciop->fd]; ncio_ffio *ffp = (ncio_ffio *)nciop->pvt; 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 = fileio_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_fileio_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; FILE* f = descriptors[nciop->fd]; #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 = fileio_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_fileio_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; FILE* f = descriptors[nciop->fd]; 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_fileio_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_fileio_rel(nciop, lower, RGN_MODIFIED); return status; } static int ncio_fileio_sync(ncio *const nciop) { FILE* f = descriptors[nciop->fd]; fflush(f); return NC_NOERR; } static void ncio_fileio_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_fileio_init2(ncio *const nciop, size_t *sizehintp) { FILE* f = descriptors[nciop->fd]; ncio_ffio *ffp = (ncio_ffio *)nciop->pvt; assert(f != NULL); 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_fileio_init(ncio *const nciop) { ncio_ffio *ffp = (ncio_ffio *)nciop->pvt; p *((ncio_relfunc **)&nciop->rel) = ncio_fileio_rel; /* cast away const */ *((ncio_getfunc **)&nciop->get) = ncio_fileio_get; /* cast away const */ *((ncio_movefunc **)&nciop->move) = ncio_fileio_move; /* cast away const */ *((ncio_syncfunc **)&nciop->sync) = ncio_fileio_sync; /* cast away const */ *((ncio_freefunc **)&nciop->free) = ncio_fileio_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 fileio\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_fileio_init(nciop); return nciop; } /* Public below this point */ /* TODO: Is this reasonable for this platform? */ static const size_t NCIO_MINBLOCKSIZE = 0x100; static const size_t NCIO_MAXBLOCKSIZE = 0x100000; int ncio_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; #ifdef _WIN32_WCE char* oflags = "bw+"; /*==?(O_RDWR|O_CREAT|O_TRUNC) && binary*/ #else char* oflags = "w+"; /*==?(O_RDWR|O_CREAT|O_TRUNC);*/ #endif FILE* f; int i,fd; int status = NC_NOERR; 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(fIsSet(ioflags, NC_NOCLOBBER)) { /* Since we do not have use of the O_EXCL flag, we need to fake it */ #ifdef WINCE f = NCfopen(path,"rb"); #else f = NCfopen(path,"r"); #endif if(f != NULL) { /* do not overwrite */ (void)fclose(f); return EEXIST; } } f = NCfopen(path, oflags); if(f == NULL) { status = errno; goto unwind_new; } /* Locate an open pseudo file descriptor */ fd = -1; for(i=1;ifd) = fd; /* cast away const */ if(*sizehintp < NCIO_MINBLOCKSIZE || *sizehintp > NCIO_MAXBLOCKSIZE) { /* Use default */ *sizehintp = blksize(fd); } else { *sizehintp = M_RNDUP(*sizehintp); } status = ncio_fileio_init2(nciop, sizehintp); if(status != NC_NOERR) goto unwind_open; if(initialsz != 0) { status = fgrow(f, (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) fclose(descriptors[fd]); descriptors[fd] = NULL; /* ?? 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, void* parameters, ncio **nciopp, void **const igetvpp) { ncio *nciop; char* oflags = fIsSet(ioflags, NC_WRITE) ? "r+" #ifdef WINCE : "rb"; #else : "r"; #endif FILE* f; int i,fd; int status = NC_NOERR; if(path == NULL || *path == 0) return EINVAL; nciop = ncio_new(path, ioflags); if(nciop == NULL) return ENOMEM; f = NCfopen(path, oflags); if(f == NULL) { status = errno; goto unwind_new; } /* Locate an open pseudo file descriptor */ fd = -1; for(i=1;ifd) = fd; /* cast away const */ if(*sizehintp < NCIO_MINBLOCKSIZE || *sizehintp > NCIO_MAXBLOCKSIZE) { /* Use default */ *sizehintp = blksize(fd); } else { *sizehintp = M_RNDUP(*sizehintp); } status = ncio_fileio_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) fclose(descriptors[fd]); descriptors[fd] = NULL; /*FALLTHRU*/ unwind_new: ncio_free(nciop); return status; } /* * Get file size in bytes. * Is use of fstatus = fseek() really necessary, or could we use standard fstat() call * and get st_size member? */ int ncio_filesize(ncio *nciop, off_t *filesizep) { int status = NC_NOERR; off_t filesize, current, reset; FILE* f; if(nciop == NULL) return EINVAL; f = descriptors[nciop->fd]; current = ftell(f); status = fseek(f, 0, SEEK_END); /* get size */ if(ferror(f)) return EIO; *filesizep = ftell(f); status = fseek(f, current, SEEK_SET); /* reset */ if(ferror(f)) return EIO; 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. */ int ncio_pad_length(ncio *nciop, off_t length) { int status = NC_NOERR; FILE* f; if(nciop == NULL) return EINVAL; f = descriptors[nciop->fd]; 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(f, length); if(status != NC_NOERR) return errno; return NC_NOERR; } int ncio_close(ncio *nciop, int doUnlink) { int status = NC_NOERR; FILE* f; if(nciop == NULL) return EINVAL; f = descriptors[nciop->fd]; nciop->sync(nciop); (void) fclose(f); descriptors[nciop->fd] = NULL; if(doUnlink) (void) unlink(nciop->path); ncio_free(nciop); return status; }