diff options
Diffstat (limited to 'src/H5FDstdio.c')
-rw-r--r-- | src/H5FDstdio.c | 706 |
1 files changed, 706 insertions, 0 deletions
diff --git a/src/H5FDstdio.c b/src/H5FDstdio.c new file mode 100644 index 0000000..ac8cdc2 --- /dev/null +++ b/src/H5FDstdio.c @@ -0,0 +1,706 @@ +/* + * Copyright © 1999 NCSA + * All rights reserved. + * + * Programmer: Robb Matzke <matzke@llnl.gov> + * Wednesday, October 22, 1997 + * + * Purpose: This is the Posix stdio.h I/O subclass of H5Flow. + * + * Notes: Ported to the new H5FD architecture on 10/18/99 - QAK + * + */ +#include "hdf5.h" +#include <assert.h> +#include <stdlib.h> +#include <sys/stat.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef WIN32 +#include <windows.h> +#endif + +#ifdef MAX +#undef MAX +#endif /* MAX */ +#define MAX(X,Y) ((X)>(Y)?(X):(Y)) + +/* The driver identification number, initialized at runtime */ +static hid_t H5FD_STDIO_g = 0; + +/* File operations */ +typedef enum { + H5FD_STDIO_OP_UNKNOWN=0, + H5FD_STDIO_OP_READ=1, + H5FD_STDIO_OP_WRITE=2, + H5FD_STDIO_OP_SEEK=3 +} H5FD_stdio_file_op; + +/* + * The description of a file belonging to this driver. The `eoa' and `eof' + * determine the amount of hdf5 address space in use and the high-water mark + * of the file (the current size of the underlying Unix file). The `pos' + * value is used to eliminate file position updates when they would be a + * no-op. Unfortunately we've found systems that use separate file position + * indicators for reading and writing so the lseek can only be eliminated if + * the current operation is the same as the previous operation. When opening + * a file the `eof' will be set to the current file size, `eoa' will be set + * to zero, `pos' will be set to H5F_ADDR_UNDEF (as it is when an error + * occurs), and `op' will be set to H5F_OP_UNKNOWN. + */ +typedef struct H5FD_stdio_t { + H5FD_t pub; /*public stuff, must be first */ + FILE * fp; /*the file handle */ + haddr_t eoa; /*end of allocated region */ + haddr_t eof; /*end of file; current file size*/ + haddr_t pos; /*current file I/O position */ + H5FD_stdio_file_op op; /*last operation */ + unsigned write_access; /* Flag to indicate the file was opened with write access */ +#ifndef WIN32 + /* + * On most systems the combination of device and i-node number uniquely + * identify a file. + */ + dev_t device; /*file device number */ + ino_t inode; /*file i-node number */ +#else + /* + * On WIN32 the low-order word of a unique identifier associated with the + * file and the volume serial number uniquely identify a file. This number + * (which, both? -rpm) may change when the system is restarted or when the + * file is opened. After a process opens a file, the identifier is + * constant until the file is closed. An application can use this + * identifier and the volume serial number to determine whether two + * handles refer to the same file. + */ + int fileindexlo; + int fileindexhi; +#endif +} H5FD_stdio_t; + +/* + * These macros check for overflow of various quantities. These macros + * assume that file_offset_t is signed and haddr_t and size_t are unsigned. + * + * ADDR_OVERFLOW: Checks whether a file address of type `haddr_t' + * is too large to be represented by the second argument + * of the file seek function. + * + * SIZE_OVERFLOW: Checks whether a buffer size of type `hsize_t' is too + * large to be represented by the `size_t' type. + * + * REGION_OVERFLOW: Checks whether an address and size pair describe data + * which can be addressed entirely by the second + * argument of the file seek function. + */ +#define MAXADDR (((haddr_t)1<<(8*sizeof(long)-1))-1) +#define ADDR_OVERFLOW(A) (HADDR_UNDEF==(A) || ((A) & ~(haddr_t)MAXADDR)) +#define SIZE_OVERFLOW(Z) ((Z) & ~(hsize_t)MAXADDR) +#define REGION_OVERFLOW(A,Z) (ADDR_OVERFLOW(A) || SIZE_OVERFLOW(Z) || \ + sizeof(long)<sizeof(size_t) || HADDR_UNDEF==(A)+(Z) || (long)((A)+(Z))<(long)(A)) + +/* Prototypes */ +static H5FD_t *H5FD_stdio_open(const char *name, unsigned flags, + hid_t fapl_id, haddr_t maxaddr); +static herr_t H5FD_stdio_close(H5FD_t *lf); +static int H5FD_stdio_cmp(const H5FD_t *_f1, const H5FD_t *_f2); +static haddr_t H5FD_stdio_get_eoa(H5FD_t *_file); +static herr_t H5FD_stdio_set_eoa(H5FD_t *_file, haddr_t addr); +static haddr_t H5FD_stdio_get_eof(H5FD_t *_file); +static herr_t H5FD_stdio_read(H5FD_t *lf, hid_t fapl_id, haddr_t addr, + hsize_t size, void *buf); +static herr_t H5FD_stdio_write(H5FD_t *lf, hid_t fapl_id, haddr_t addr, + hsize_t size, const void *buf); +static herr_t H5FD_stdio_flush(H5FD_t *_file); + +static const H5FD_class_t H5FD_stdio_g = { + "stdio", /*name */ + MAXADDR, /*maxaddr */ + NULL, /*sb_size */ + NULL, /*sb_encode */ + NULL, /*sb_decode */ + 0, /*fapl_size */ + NULL, /*fapl_get */ + NULL, /*fapl_copy */ + NULL, /*fapl_free */ + 0, /*dxpl_size */ + NULL, /*dxpl_copy */ + NULL, /*dxpl_free */ + H5FD_stdio_open, /*open */ + H5FD_stdio_close, /*close */ + H5FD_stdio_cmp, /*cmp */ + NULL, /*alloc */ + NULL, /*free */ + H5FD_stdio_get_eoa, /*get_eoa */ + H5FD_stdio_set_eoa, /*set_eoa */ + H5FD_stdio_get_eof, /*get_eof */ + H5FD_stdio_read, /*read */ + H5FD_stdio_write, /*write */ + H5FD_stdio_flush, /*flush */ + H5FD_FLMAP_SINGLE, /*fl_map */ +}; + + +/*------------------------------------------------------------------------- + * Function: H5FD_stdio_init + * + * Purpose: Initialize this driver by registering the driver with the + * library. + * + * Return: Success: The driver ID for the stdio driver. + * + * Failure: Negative. + * + * Programmer: Robb Matzke + * Thursday, July 29, 1999 + * + * Modifications: + * Stolen from the sec2 driver - QAK, 10/18/99 + * + *------------------------------------------------------------------------- + */ +hid_t +H5FD_stdio_init(void) +{ + if (H5I_VFL!=H5Iget_type(H5FD_STDIO_g)) + H5FD_STDIO_g = H5FDregister(&H5FD_stdio_g); + return(H5FD_STDIO_g); +} + + +/*------------------------------------------------------------------------- + * Function: H5Pset_fapl_stdio + * + * Purpose: Modify the file access property list to use the H5FD_STDIO + * driver defined in this source file. There are no driver + * specific properties. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Robb Matzke + * Thursday, February 19, 1998 + * + * Modifications: + * Stolen from the sec2 driver - QAK, 10/18/99 + * + *------------------------------------------------------------------------- + */ +herr_t +H5Pset_fapl_stdio(hid_t fapl_id) +{ + if (H5P_FILE_ACCESS!=H5Pget_class(fapl_id)) + return -1; + + return(H5Pset_driver(fapl_id, H5FD_STDIO, NULL)); +} + + +/*------------------------------------------------------------------------- + * Function: H5FD_stdio_open + * + * Purpose: Create and/or opens a Standard C file as an HDF5 file. + * + * Bugs: H5F_ACC_EXCL has a race condition. (? -QAK) + * + * Errors: + * IO CANTOPENFILE File doesn't exist and CREAT wasn't + * specified. + * IO CANTOPENFILE Fopen failed. + * IO FILEEXISTS File exists but CREAT and EXCL were + * specified. + * + * Return: Success: A pointer to a new file data structure. The + * public fields will be initialized by the + * caller, which is always H5FD_open(). + * + * Failure: NULL + * + * Programmer: Robb Matzke + * Wednesday, October 22, 1997 + * + * Modifications: + * Ported to VFL/H5FD layer - QAK, 10/18/99 + * + *------------------------------------------------------------------------- + */ +static H5FD_t * +H5FD_stdio_open( const char *name, unsigned flags, hid_t fapl_id, + haddr_t maxaddr) +{ + FILE *f = NULL; + unsigned write_access=0; /* File opened with write access? */ + H5FD_stdio_t *file=NULL; + static const char *func="H5FD_stdio_open"; /* Function Name for error reporting */ +#ifdef WIN32 + HFILE filehandle; + struct _BY_HANDLE_FILE_INFORMATION fileinfo; + int results; +#else /* WIN32 */ + struct stat sb; +#endif /* WIN32 */ + + /* Shut compiler up */ + fapl_id=fapl_id; + + /* Check arguments */ + if (!name || !*name) return NULL; + if (0==maxaddr || HADDR_UNDEF==maxaddr) return NULL; + if (ADDR_OVERFLOW(maxaddr)) return NULL; + + if (access(name, F_OK) < 0) { + if ((flags & H5F_ACC_CREAT) && (flags & H5F_ACC_RDWR)) { + f = fopen(name, "wb+"); + write_access=1; /* Note the write access */ + } else { + H5Epush_ret(func, H5E_IO, H5E_CANTOPENFILE, + "file doesn't exist and CREAT wasn't specified", NULL); + } + } else if ((flags & H5F_ACC_CREAT) && (flags & H5F_ACC_EXCL)) { + H5Epush_ret(func, H5E_IO, H5E_FILEEXISTS, + "file exists but CREAT and EXCL were specified", NULL); + + } else if (flags & H5F_ACC_RDWR) { + if (flags & H5F_ACC_TRUNC) + f = fopen(name, "wb+"); + else + f = fopen(name, "rb+"); + write_access=1; /* Note the write access */ + } else { + f = fopen(name, "rb"); + } + if (!f) + H5Epush_ret(func, H5E_IO, H5E_CANTOPENFILE, "fopen failed", NULL); + + /* Build the return value */ + if (NULL==(file = calloc(1,sizeof(H5FD_stdio_t)))) { + H5Epush_ret(func, H5E_RESOURCE, H5E_NOSPACE, + "memory allocation failed", NULL); + } + file->fp = f; + file->op = H5FD_STDIO_OP_SEEK; + file->pos = HADDR_UNDEF; + file->write_access=write_access; /* Note the write_access for later */ + if (fseek(file->fp, 0, SEEK_END) < 0) { + file->op = H5FD_STDIO_OP_UNKNOWN; + } else { + long x = ftell (file->fp); + assert (x>=0); + file->eof = x; + } + + /* The unique key */ +#ifdef WIN32 +#error "Needs correct fileindexhi & fileindexlo, code below is from sec2 driver" + filehandle = _get_osfhandle(fd); + results = GetFileInformationByHandle(filehandle, &fileinfo); + file->fileindexhi = fileinfo.nFileIndexHigh; + file->fileindexlo = fileinfo.nFileIndexLow; +#else + fstat(fileno(file->fp), &sb); + file->device = sb.st_dev; + file->inode = sb.st_ino; +#endif + return((H5FD_t*)file); +} /* end H5FD_stdio_open() */ + + +/*------------------------------------------------------------------------- + * Function: H5F_stdio_close + * + * Purpose: Closes a file. + * + * Errors: + * IO CLOSEERROR Fclose failed. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Robb Matzke + * Wednesday, October 22, 1997 + * + * Modifications: + * Ported to VFL/H5FD layer - QAK, 10/18/99 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5FD_stdio_close(H5FD_t *_file) +{ + H5FD_stdio_t *file = (H5FD_stdio_t*)_file; + static const char *func="H5FD_stdio_close"; /* Function Name for error reporting */ + + if (H5FD_stdio_flush(_file)<0) + H5Epush_ret(func, H5E_IO, H5E_WRITEERROR, "flush failed", -1); + if (fclose(file->fp) < 0) + H5Epush_ret(func, H5E_IO, H5E_CLOSEERROR, "fclose failed", -1); + + free(file); + + return(0); +} + + +/*------------------------------------------------------------------------- + * Function: H5FD_stdio_cmp + * + * Purpose: Compares two files belonging to this driver using an + * arbitrary (but consistent) ordering. + * + * Return: Success: A value like strcmp() + * + * Failure: never fails (arguments were checked by the + * caller). + * + * Programmer: Robb Matzke + * Thursday, July 29, 1999 + * + * Modifications: + * Stolen from the sec2 driver - QAK, 10/18/99 + * + *------------------------------------------------------------------------- + */ +static int +H5FD_stdio_cmp(const H5FD_t *_f1, const H5FD_t *_f2) +{ + const H5FD_stdio_t *f1 = (const H5FD_stdio_t*)_f1; + const H5FD_stdio_t *f2 = (const H5FD_stdio_t*)_f2; + +#ifdef WIN32 + if (f1->fileindexhi < f2->fileindexhi) return -1; + if (f1->fileindexhi > f2->fileindexhi) return 1; + + if (f1->fileindexlo < f2->fileindexlo) return -1; + if (f1->fileindexlo > f2->fileindexlo) return 1; + +#else + if (f1->device < f2->device) return -1; + if (f1->device > f2->device) return 1; + + if (f1->inode < f2->inode) return -1; + if (f1->inode > f2->inode) return 1; +#endif + return 0; +} + + +/*------------------------------------------------------------------------- + * Function: H5FD_stdio_get_eoa + * + * Purpose: Gets the end-of-address marker for the file. The EOA marker + * is the first address past the last byte allocated in the + * format address space. + * + * Return: Success: The end-of-address marker. + * + * Failure: HADDR_UNDEF + * + * Programmer: Robb Matzke + * Monday, August 2, 1999 + * + * Modifications: + * Stolen from the sec2 driver - QAK, 10/18/99 + * + *------------------------------------------------------------------------- + */ +static haddr_t +H5FD_stdio_get_eoa(H5FD_t *_file) +{ + H5FD_stdio_t *file = (H5FD_stdio_t*)_file; + + return(file->eoa); +} + + +/*------------------------------------------------------------------------- + * Function: H5FD_stdio_set_eoa + * + * Purpose: Set the end-of-address marker for the file. This function is + * called shortly after an existing HDF5 file is opened in order + * to tell the driver where the end of the HDF5 data is located. + * + * Return: Success: 0 + * + * Failure: -1 + * + * Programmer: Robb Matzke + * Thursday, July 29, 1999 + * + * Modifications: + * Stolen from the sec2 driver - QAK, 10/18/99 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5FD_stdio_set_eoa(H5FD_t *_file, haddr_t addr) +{ + H5FD_stdio_t *file = (H5FD_stdio_t*)_file; + + file->eoa = addr; + + return(0); +} + + +/*------------------------------------------------------------------------- + * Function: H5FD_stdio_get_eof + * + * Purpose: Returns the end-of-file marker, which is the greater of + * either the Unix end-of-file or the HDF5 end-of-address + * markers. + * + * Return: Success: End of file address, the first address past + * the end of the "file", either the Unix file + * or the HDF5 file. + * + * Failure: HADDR_UNDEF + * + * Programmer: Robb Matzke + * Thursday, July 29, 1999 + * + * Modifications: + * Stolen from the sec2 driver - QAK, 10/18/99 + * + *------------------------------------------------------------------------- + */ +static haddr_t +H5FD_stdio_get_eof(H5FD_t *_file) +{ + H5FD_stdio_t *file = (H5FD_stdio_t*)_file; + + return(MAX(file->eof, file->eoa)); +} + + +/*------------------------------------------------------------------------- + * Function: H5F_stdio_read + * + * Purpose: Reads SIZE bytes beginning at address ADDR in file LF and + * places them in buffer BUF. Reading past the logical or + * physical end of file returns zeros instead of failing. + * + * Errors: + * IO READERROR Fread failed. + * IO SEEKERROR Fseek failed. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Robb Matzke + * Wednesday, October 22, 1997 + * + * Modifications: + * June 2, 1998 Albert Cheng + * Added xfer_mode argument + * + * Ported to VFL/H5FD layer - QAK, 10/18/99 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5FD_stdio_read(H5FD_t *_file, hid_t dxpl_id, haddr_t addr, hsize_t size, + void *buf/*out*/) +{ + size_t n; + H5FD_stdio_t *file = (H5FD_stdio_t*)_file; + static const char *func="H5FD_stdio_read"; /* Function Name for error reporting */ + + /* Shut compiler up */ + dxpl_id=dxpl_id; + + /* Check for overflow */ + if (HADDR_UNDEF==addr) + H5Epush_ret (func, H5E_IO, H5E_OVERFLOW, "file address overflowed", -1); + if (REGION_OVERFLOW(addr, size)) + H5Epush_ret (func, H5E_IO, H5E_OVERFLOW, "file address overflowed", -1); + if (addr+size>file->eoa) + H5Epush_ret (func, H5E_IO, H5E_OVERFLOW, "file address overflowed", -1); + + /* Check easy cases */ + if (0 == size) + return(0); + if ((uint64_t)addr >= file->eof) { + memset(buf, 0, size); + return(0); + } + + /* + * Seek to the correct file position. + */ + if (!(file->op == H5FD_STDIO_OP_READ || file->op==H5FD_STDIO_OP_SEEK) || + file->pos != addr) { + if (fseek(file->fp, addr, SEEK_SET) < 0) { + file->op = H5FD_STDIO_OP_UNKNOWN; + file->pos = HADDR_UNDEF; + H5Epush_ret(func, H5E_IO, H5E_SEEKERROR, "fseek failed", -1); + } + file->pos = addr; + } + + /* + * Read zeros past the logical end of file (physical is handled below) + */ + if ((size_t) addr + size > file->eof) { + size_t nbytes = (size_t) addr + size - file->eof; + memset((unsigned char *)buf + size - nbytes, 0, nbytes); + size -= nbytes; + } + + /* + * Read the data. Since we're reading single-byte values, a partial read + * will advance the file position by N. If N is negative or an error + * occurs then the file position is undefined. + */ + n = fread(buf, 1, size, file->fp); + if (n <= 0 && ferror(file->fp)) { + file->op = H5FD_STDIO_OP_UNKNOWN; + file->pos = HADDR_UNDEF; + H5Epush_ret(func, H5E_IO, H5E_READERROR, "fread failed", -1); + } else if (n < size) { + memset((unsigned char *)buf + n, 0, size - n); + } + + /* + * Update the file position data. + */ + file->op = H5FD_STDIO_OP_READ; + file->pos = addr+n; /*checked for overflow above*/ + return(0); +} + + +/*------------------------------------------------------------------------- + * Function: H5F_stdio_write + * + * Purpose: Writes SIZE bytes from the beginning of BUF into file LF at + * file address ADDR. + * + * Errors: + * IO SEEKERROR Fseek failed. + * IO WRITEERROR Fwrite failed. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Robb Matzke + * Wednesday, October 22, 1997 + * + * Modifications: + * June 2, 1998 Albert Cheng + * Added xfer_mode argument + * + * Ported to VFL/H5FD layer - QAK, 10/18/99 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5FD_stdio_write(H5FD_t *_file, hid_t dxpl_id, haddr_t addr, + hsize_t size, const void *buf) +{ + H5FD_stdio_t *file = (H5FD_stdio_t*)_file; + static const char *func="H5FD_stdio_write"; /* Function Name for error reporting */ + + /* Shut compiler up */ + dxpl_id=dxpl_id; + + /* Check for overflow conditions */ + if (HADDR_UNDEF==addr) + H5Epush_ret (func, H5E_IO, H5E_OVERFLOW, "file address overflowed", -1); + if (REGION_OVERFLOW(addr, size)) + H5Epush_ret (func, H5E_IO, H5E_OVERFLOW, "file address overflowed", -1); + if (addr+size>file->eoa) + H5Epush_ret (func, H5E_IO, H5E_OVERFLOW, "file address overflowed", -1); + + /* + * Seek to the correct file position. + */ + if (!(file->op == H5FD_STDIO_OP_WRITE || file->op==H5FD_STDIO_OP_SEEK) || + file->pos != addr) { + if (fseek(file->fp, addr, SEEK_SET) < 0) { + file->op = H5FD_STDIO_OP_UNKNOWN; + file->pos = HADDR_UNDEF; + H5Epush_ret(func, H5E_IO, H5E_SEEKERROR, "fseek failed", -1); + } + file->pos = addr; + } + + /* + * Write the buffer. On successful return, the file position will be + * advanced by the number of bytes read. Otherwise nobody knows where it + * is. + */ + if (size != fwrite(buf, 1, size, file->fp)) { + file->op = H5FD_STDIO_OP_UNKNOWN; + file->pos = HADDR_UNDEF; + H5Epush_ret(func, H5E_IO, H5E_WRITEERROR, "fwrite failed", -1); + } + + /* + * Update seek optimizing data. + */ + file->op = H5FD_STDIO_OP_WRITE; + file->pos = addr + size; + + /* Update EOF if necessary */ + if (file->pos>file->eof) + file->eof = file->pos; + + return(0); +} + + +/*------------------------------------------------------------------------- + * Function: H5F_stdio_flush + * + * Purpose: Makes sure that all data is on disk. + * + * Errors: + * IO SEEKERROR fseek failed. + * IO WRITEERROR fflush or fwrite failed. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Robb Matzke + * Wednesday, October 22, 1997 + * + * Modifications: + * Ported to VFL/H5FD layer - QAK, 10/18/99 + * + *------------------------------------------------------------------------- + */ +static herr_t +H5FD_stdio_flush(H5FD_t *_file) +{ + H5FD_stdio_t *file = (H5FD_stdio_t*)_file; + static const char *func="H5FD_stdio_flush"; /* Function Name for error reporting */ + + /* Only try to flush the file if we have write access */ + if(file->write_access) { + /* Makes sure that the true file size is the same (or larger) than the end-of-address. */ + if (file->eoa>file->eof) { + if (fseek(file->fp, file->eoa-1, SEEK_SET)<0) + H5Epush_ret(func, H5E_IO, H5E_SEEKERROR, "fseek failed", -1); + if (fwrite("", 1, 1, file->fp)!=1) + H5Epush_ret(func, H5E_IO, H5E_SEEKERROR, "EOF fwrite failed", -1); + file->eof = file->eoa; + file->pos = file->eoa; + /* fall through to set the IO operation */ + } + + /* + * What happens to the file position? Is it guaranteed to be the same + * after the fflush() as it was before? + */ + file->op = H5FD_STDIO_OP_UNKNOWN; + + /* + * Flush + */ + if (fflush(file->fp) < 0) + H5Epush_ret(func, H5E_IO, H5E_WRITEERROR, "fflush failed", -1); + } /* end if */ + else { + /* Double-check for problems */ + if (file->eoa>file->eof) + H5Epush_ret(func, H5E_IO, H5E_TRUNCATED, "eoa>eof!", -1); + } /* end else */ + + return(0); +} |