/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright by the Board of Trustees of the University of Illinois. * * All rights reserved. * * * * This file is part of HDF5. The full HDF5 copyright notice, including * * terms governing use, modification, and redistribution, is contained in * * the files COPYING and Copyright.html. COPYING can be found at the root * * of the source code distribution tree; Copyright.html can be found at the * * root level of an installed copy of the electronic HDF5 document set and * * is linked from the top-level documents page. It can also be found at * * http://hdf.ncsa.uiuc.edu/HDF5/doc/Copyright.html. If you do not have * * access to either file, you may request a copy from hdfhelp@ncsa.uiuc.edu. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * Programmer: Robb Matzke * Wednesday, October 22, 1997 * * Purpose: This is the Posix stdio.h I/O subclass of H5Flow. * It also serves as an example of coding a simple file driver, * therefore, it should not use any non-public definitions. * * Notes: Ported to the new H5FD architecture on 10/18/99 - QAK * */ #include #include #include #include "hdf5.h" #ifdef H5_HAVE_STDIO_H #include #endif #ifdef H5_HAVE_UNISTD_H #include #endif #ifdef WIN32 #include #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. */ /* adding for windows NT filesystem support. */ #ifdef WIN32 #define MAXADDR (((haddr_t)1<<(8*sizeof(LONGLONG)-1))-1) #else #define MAXADDR (((haddr_t)1<<(8*sizeof(long)-1))-1) #endif #define ADDR_OVERFLOW(A) (HADDR_UNDEF==(A) || ((A) & ~(haddr_t)MAXADDR)) #define SIZE_OVERFLOW(Z) ((Z) & ~(hsize_t)MAXADDR) #ifdef WIN32 #define REGION_OVERFLOW(A,Z) (ADDR_OVERFLOW(A) || SIZE_OVERFLOW(Z) || \ sizeof(LONGLONG)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"*/ fd = _fileno(f); filehandle = _get_osfhandle(fd); results = GetFileInformationByHandle((HANDLE)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 (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 #ifdef H5_DEV_T_IS_SCALAR if (f1->device < f2->device) return -1; if (f1->device > f2->device) return 1; #else /* H5_DEV_T_IS_SCALAR */ /* If dev_t isn't a scalar value on this system, just use memcmp to * determine if the values are the same or not. The actual return value * shouldn't really matter... */ if(memcmp(&(f1->device),&(f2->device),sizeof(dev_t))<0) return -1; if(memcmp(&(f1->device),&(f2->device),sizeof(dev_t))>0) return 1; #endif /* H5_DEV_T_IS_SCALAR */ if (f1->inode < f2->inode) return -1; if (f1->inode > f2->inode) return 1; #endif return 0; } /*------------------------------------------------------------------------- * Function: H5FD_stdio_query * * Purpose: Set the flags that this VFL driver is capable of supporting. * (listed in H5FDpublic.h) * * Return: Success: non-negative * * Failure: negative * * Programmer: Quincey Koziol * Friday, August 25, 2000 * * Modifications: * *------------------------------------------------------------------------- */ static herr_t H5FD_stdio_query(const H5FD_t *_f, unsigned long *flags /* out */) { /* Shut compiler up */ _f=_f; /* Set the VFL feature flags that this driver supports */ if(flags) { *flags = 0; *flags|=H5FD_FEAT_AGGREGATE_METADATA; /* OK to aggregate metadata allocations */ *flags|=H5FD_FEAT_ACCUMULATE_METADATA; /* OK to accumulate metadata for faster writes */ *flags|=H5FD_FEAT_DATA_SIEVE; /* OK to perform data sieving for faster raw data reads & writes */ *flags|=H5FD_FEAT_AGGREGATE_SMALLDATA; /* OK to aggregate "small" raw data allocations */ } 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: H5FD_stdio_get_handle * * Purpose: Returns the file handle of stdio file driver. * * Returns: Non-negative if succeed or negative if fails. * * Programmer: Raymond Lu * Sept. 16, 2002 * * Modifications: * *------------------------------------------------------------------------- */ static herr_t H5FD_stdio_get_handle(H5FD_t *_file, hid_t fapl, void** file_handle) { H5FD_stdio_t *file = (H5FD_stdio_t *)_file; static const char *func="H5FD_stdio_get_handle"; /* Function Name for error reporting */ /* Shut compiler up */ fapl=fapl; /* Clear the error stack */ H5Eclear(); *file_handle = &(file->fp); if(*file_handle==NULL) H5Epush_ret(func, H5E_IO, H5E_WRITEERROR, "get handle failed", -1); return(0); } /*------------------------------------------------------------------------- * 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, H5FD_mem_t type, hid_t dxpl_id, haddr_t addr, size_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 */ type=type; 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) { #ifdef WIN32 fpos_t tempos =(fpos_t)(addr+SEEK_SET); if (fsetpos(file->fp,&tempos)!=0) { file->op = H5FD_STDIO_OP_UNKNOWN; file->pos = HADDR_UNDEF; H5Epush_ret(func, H5E_IO, H5E_SEEKERROR, "fsetpos failed", -1); } #else if (fseek(file->fp, (long)addr, SEEK_SET) < 0) { file->op = H5FD_STDIO_OP_UNKNOWN; file->pos = HADDR_UNDEF; H5Epush_ret(func, H5E_IO, H5E_SEEKERROR, "fseek failed", -1); } #endif 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, H5FD_mem_t type, hid_t dxpl_id, haddr_t addr, size_t size, const void *buf) { #ifdef WIN32 fpos_t tempos; #endif 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; type=type; /* 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) { #ifdef WIN32 tempos =(fpos_t)(addr+SEEK_SET); if (fsetpos(file->fp,&tempos) != 0) { file->op = H5FD_STDIO_OP_UNKNOWN; file->pos = HADDR_UNDEF; H5Epush_ret(func, H5E_IO, H5E_SEEKERROR, "fsetpos failed", -1); } #else if (fseek(file->fp, (long)addr, SEEK_SET) < 0) { file->op = H5FD_STDIO_OP_UNKNOWN; file->pos = HADDR_UNDEF; H5Epush_ret(func, H5E_IO, H5E_SEEKERROR, "fseek failed", -1); } #endif /* WIN32 */ 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; /* The following code needs to be added for windows VC 6.0. This should be a VC++ compiler bug. When not using ftell and fseek, although you reset the position to the starting of the file, fwrite will somehow to go to the end of the file and add contents. It seems they used a circular seeking algorithm, the starting point overlaps with the ending point and windows doesn't handle correctly for the case when file was written to the disk close to the end of the file and rewrite from the beginning of the file. This is how HDF5 signature was written for some failing cases. */ #ifdef WIN32 tempos = ftell(file->fp); fseek(file->fp,tempos,SEEK_SET); #endif /* 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, hid_t dxpl_id, unsigned closing) { H5FD_stdio_t *file = (H5FD_stdio_t*)_file; static const char *func="H5FD_stdio_flush"; /* Function Name for error reporting */ /* Shut compiler up */ dxpl_id=dxpl_id; /* 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 as the end-of-address. */ if (file->eoa!=file->eof) { int fd=fileno(file->fp); /* File descriptor for HDF5 file */ #ifdef WIN32 HFILE filehandle; /* Windows file handle */ LARGE_INTEGER li; /* 64-bit integer for SetFilePointer() call */ fd = _fileno(file->fp); /* Map the posix file handle to a Windows file handle */ filehandle = _get_osfhandle(fd); /* Translate 64-bit integers into form Windows wants */ /* [This algorithm is from the Windows documentation for SetFilePointer()] */ li.QuadPart = file->eoa; SetFilePointer((HANDLE)filehandle,li.LowPart,&li.HighPart,FILE_BEGIN); if(SetEndOfFile((HANDLE)filehandle)==0) H5Epush_ret(func, H5E_IO, H5E_SEEKERROR, "unable to extend file properly", -1); #else /* WIN32 */ if (-1==file_truncate(fd, (file_offset_t)file->eoa)) H5Epush_ret(func, H5E_IO, H5E_SEEKERROR, "unable to extend file properly", -1); #endif /* WIN32 */ /* Update the eof value */ file->eof = file->eoa; /* Reset last file I/O information */ file->pos = HADDR_UNDEF; file->op = H5FD_STDIO_OP_UNKNOWN; } /* end if */ /* * Flush */ if(!closing) { if (fflush(file->fp) < 0) H5Epush_ret(func, H5E_IO, H5E_WRITEERROR, "fflush failed", -1); } /* end if */ } /* 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); } #ifdef _H5private_H /* * This is not related to the functionality of the driver code. * It is added here to trigger warning if HDF5 private definitions are included * by mistake. The code should use only HDF5 public API and definitions. */ #error "Do not use HDF5 private definitions" #endif