/* SST_File_POSIX.c Author: Patrick Baggett Created: 12/23/2011 Purpose: libsst-os file I/O for POSIX platforms License: This program is free software. It comes without any warranty, to the extent permitted by applicable law. You can redistribute it and/or modify it under the terms of the Do What The Fuck You Want To Public License, Version 2, as published by Sam Hocevar. See http://sam.zoy.org/wtfpl/COPYING for more details. */ #include "POSIXPrivate.h" #include #include #include #include #include /******************************************************************************/ SST_File SST_OS_OpenFile(const char* path, uint32_t mode) { int flags; int fd; SST_File_POSIX* file; mode_t openMode = 0; SST_OS_DebugAssert(path != NULL, "File path may not be NULL"); /* Async I/O is not yet suppported */ if(mode & SST_OPEN_ASYNC) return NULL; /* Only one of SST_OPEN_HINTRAND or SST_OPEN_HINTSEQ may be set. */ if((mode & (SST_OPEN_HINTRAND|SST_OPEN_HINTSEQ)) == (SST_OPEN_HINTRAND|SST_OPEN_HINTSEQ)) return NULL; switch(mode & 0xF) /* Lower byte has the open mode, remaining 24 bits are flags */ { /* In each of the three cases (r/w/a), we set the execute bit so that memory mapped I/O can specify 'execute' a permission too -- apparently the mapped view must be a subset of the permissions the file was originally opened with. */ case SST_OPEN_READ: flags = O_RDONLY; break; case SST_OPEN_WRITE: flags = O_WRONLY | O_CREAT | O_TRUNC; openMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; /* User/Group: RW, Other: R */ break; case SST_OPEN_APPEND: flags = O_WRONLY | O_APPEND; break; /* Invalid open mode */ default: return NULL; } fd = open(path, flags, openMode); if(fd < 0) return NULL; #if _XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L if(mode & SST_OPEN_HINTRAND) posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM); else if(mode & SST_OPEN_HINTSEQ) posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL); #endif file = (SST_File_POSIX*)malloc(sizeof(SST_File_POSIX)); if(file == NULL) { close(fd); return NULL; } file->fd = fd; file->isAsync = (int)(mode & SST_OPEN_ASYNC); /* Not yet used */ #ifdef _DEBUG file->nrMmaps = 0; #endif return (SST_File)file; } /******************************************************************************/ uint64_t SST_OS_WriteFile(SST_File file, const void* buf, uint64_t bytes) { SST_File_POSIX* fp = (SST_File_POSIX*)file; uint64_t bytesLeft = bytes; uint64_t totalWritten; const char* writePtr = (const char*)buf; SST_OS_DebugAssert(file != NULL, "File handle may not be NULL"); SST_OS_DebugAssert(buf != NULL, "Buffer may not be NULL"); SST_OS_DebugAssert(!fp->isAsync, "Files opened for asynchronous I/O may not use the synchronous file I/O functions"); #ifdef _DEBUG SST_OS_DebugAssert(fp->nrMmaps == 0, "Cannot use synchronous file I/O while memory maps exist!"); #endif /* Do nothing? */ if(bytes == 0) return 0; totalWritten = 0; do { size_t nrBytesToWrite; ssize_t nrWritten; /* Determine how many bytes to write */ if(bytesLeft > SIZE_MAX) nrBytesToWrite = SIZE_MAX; else nrBytesToWrite = (size_t)bytesLeft; /* Actually write data to the file */ nrWritten = write(fp->fd, writePtr, nrBytesToWrite); if(nrWritten < 0) /* error */ { /* OK, but some of the data may have been written, so stop now but return how far we did get */ break; } /* Advance pointers and counters */ writePtr += nrWritten; totalWritten += (uint64_t)nrWritten; bytesLeft -= (uint64_t)nrWritten; } while(bytesLeft > 0); return totalWritten; } /******************************************************************************/ uint64_t SST_OS_ReadFile(SST_File file, void* buf, uint64_t bytes) { SST_File_POSIX* fp = (SST_File_POSIX*)file; uint64_t bytesLeft = bytes; uint64_t totalRead; char* readPtr = (char*)buf; SST_OS_DebugAssert(file != NULL, "File handle may not be NULL"); SST_OS_DebugAssert(buf != NULL, "Buffer may not be NULL"); SST_OS_DebugAssert(!fp->isAsync, "Files opened for asynchronous I/O may not use the synchronous file I/O functions"); #ifdef _DEBUG SST_OS_DebugAssert(fp->nrMmaps == 0, "Cannot use synchronous file I/O while memory maps exist!"); #endif /* Do nothing? */ if(bytes == 0) return 0; totalRead = 0; do { size_t nrBytesToRead; ssize_t nrRead; /* Determine how many bytes to read */ if(bytesLeft > SIZE_MAX) nrBytesToRead = SIZE_MAX; else nrBytesToRead = (size_t)bytesLeft; nrRead = read(fp->fd, readPtr, nrBytesToRead); if(nrRead <= 0) { /* Error or EOF, but some of the reads could have succeeded, so return how much was read */ break; } /* Advance pointers and counters */ readPtr += nrRead; totalRead += (uint64_t)nrRead; bytesLeft -= (uint64_t)nrRead; } while(bytesLeft > 0); return totalRead; } /******************************************************************************/ int SST_OS_GetFilePointer(SST_File file, uint64_t* fpReturn) { SST_File_POSIX* fp = (SST_File_POSIX*)file; off_t off; SST_OS_DebugAssert(file != NULL, "File handle may not be NULL"); SST_OS_DebugAssert(fpReturn != NULL, "File pointer return may not be NULL"); #ifdef _DEBUG SST_OS_DebugAssert(fp->nrMmaps == 0, "Cannot use synchronous file I/O while memory maps exist!"); #endif off = lseek(fp->fd, 0, SEEK_CUR); if(off == (off_t)-1) return 0; *fpReturn = (uint64_t)off; return 1; } /******************************************************************************/ int SST_OS_SetFilePointer(SST_File file, uint64_t ptr) { SST_File_POSIX* fp = (SST_File_POSIX*)file; off_t off; SST_OS_DebugAssert(file != NULL, "File handle may not be NULL"); #ifdef _DEBUG SST_OS_DebugAssert(fp->nrMmaps == 0, "Cannot use synchronous file I/O while memory maps exist!"); #endif /* Trying to seek > 4GB but underlying OS doesn't support it -> error */ if(sizeof(off_t) == sizeof(uint32_t) && ptr > UINT32_MAX) return 0; off = (off_t)ptr; if(lseek(fp->fd, off, SEEK_SET) != (off_t)-1) return 0; return 1; } /******************************************************************************/ int SST_OS_FileEOF(SST_File file) { SST_File_POSIX* fp = (SST_File_POSIX*)file; off_t here, eof; SST_OS_DebugAssert(file != NULL, "File handle may not be NULL"); #ifdef _DEBUG SST_OS_DebugAssert(fp->nrMmaps == 0, "Cannot use synchronous file I/O while memory maps exist!"); #endif /* Step 1: Get current file position */ here = lseek(fp->fd, 0, SEEK_CUR); if(here == (off_t)-1) return -1; /* Step 2: Seek to EOF */ eof = lseek(fp->fd, 0, SEEK_END); if(eof == (off_t)-1) return -2; /* Step 3: Put it back! */ lseek(fp->fd, here, SEEK_SET); /* Step 4: Compare and return */ return (here == eof); } /******************************************************************************/ void SST_OS_FlushFile(SST_File file) { SST_File_POSIX* fp = (SST_File_POSIX*)file; SST_OS_DebugAssert(file != NULL, "File handle may not be NULL"); (void)fsync(fp->fd); } /******************************************************************************/ void SST_OS_CloseFile(SST_File file) { SST_File_POSIX* fp = (SST_File_POSIX*)file; SST_OS_DebugAssert(file != NULL, "File handle may not be NULL"); if(fp->fd > 0) { close(fp->fd); #ifdef _DEBUG fp->fd = 0; #endif } #ifdef _DEBUG SST_OS_DebugAssert(fp->nrMmaps == 0, "Closing file with open memory maps means leaked OS resources"); #endif free(fp); } /******************************************************************************/ int SST_OS_SeekFile(SST_File file, int64_t offset, int fromWhere) { SST_File_POSIX* fp = (SST_File_POSIX*)file; int method; off_t off; SST_OS_DebugAssert(file != NULL, "File handle may not be NULL"); #ifdef _DEBUG SST_OS_DebugAssert(fp->nrMmaps == 0, "Cannot use synchronous file I/O while memory maps exist!"); #endif off = (off_t)offset; /* Map SST to POSIX constants (pretty sure they are isomorphic) */ switch(fromWhere) { case SST_SEEK_START: method = SEEK_SET; break; case SST_SEEK_CUR: method = SEEK_CUR; break; case SST_SEEK_END: method = SEEK_END; break; /* Invalid parameter */ default: return 0; } if(lseek(fp->fd, off, method) == (off_t)-1) return 0; return 1; } /******************************************************************************/ uint64_t SST_OS_GetFileSize(SST_File file) { SST_File_POSIX* fp = (SST_File_POSIX*)file; struct stat info; if(fstat(fp->fd, &info) == 0) /* i.e. success */ return (uint64_t)info.st_size; /* Failure */ return UINT64_MAX; } /******************************************************************************/