/* * Copyright © 1999 NCSA * All rights reserved. * * Programmer: Robb Matzke * 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 #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef WIN32 #include #endif #ifdef MAX #undef MAX #endif /* MAX */ #define MAX(X,Y) ((X)>(Y)?(X):(Y)) #ifndef F_OK #define F_OK 00 #define W_OK 02 #define R_OK 04 #endif /* 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)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(f); 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 */ /* Clear the error stack */ H5Eclear(); 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; /* Clear the error stack */ H5Eclear(); #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; /* Clear the error stack */ H5Eclear(); 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; /* Clear the error stack */ H5Eclear(); 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; /* Clear the error stack */ H5Eclear(); 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; /* Clear the error stack */ H5Eclear(); /* 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 ((haddr_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; /* Clear the error stack */ H5Eclear(); /* 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 */ /* Clear the error stack */ H5Eclear(); /* 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); }