Files
libsst/libsst-os/SST_File_POSIX.c
2026-04-03 00:22:39 -05:00

351 lines
8.6 KiB
C

/*
SST_File_POSIX.c
Author: Patrick Baggett <ptb1@762studios.com>
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 <SST/SST_File.h>
#include <SST/SST_Assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/******************************************************************************/
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;
}
/******************************************************************************/