AuroraRuntime/Source/IO/FS/FileStream.Unix.cpp

365 lines
8.4 KiB
C++
Executable File

/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: FileStream.Unix.cpp
Date: 2021-6-12
Author: Reece
***/
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE
#include <Source/RuntimeInternal.hpp>
#include "FS.hpp"
#include "FileStream.Generic.hpp"
#include "FileAdvisory.Unix.hpp"
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#if !defined(_AURUNTIME_GENERICFILESTREAM)
namespace Aurora::IO::FS
{
static const AuUInt64 kFileCopyBlock = 0x4000; // 16KiB
static bool PosixLseek63(int fd, AuUInt64 offset, int whence, AuUInt64 *pOffset)
{
if (offset > std::numeric_limits<off_t>::max())
{
SysPushErrorIO("int overflow exploit?");
return false;
}
#if defined(AURORA_IS_LINUX_DERIVED)
auto ret = lseek64(fd, offset, whence);
#elif defined(AURORA_IS_64BIT)
static_assert(std::numeric_limits<off_t>::max() >= std::numeric_limits<AuInt64>::max(), "unsupported posix os");
auto ret = lseek(fd, offset, whence);
#else
#error accient 32-bit posix operating systems require 64-bit file awareness
#endif
if (ret < 0)
{
//SysPushErrorIO("PosixLseek63 IO Error: %i", ret);
return false;
}
if (pOffset)
{
*pOffset = ret;
}
return true;
}
static AuUInt64 PosixGetOffset(int fd)
{
AuUInt64 ret;
if (!PosixLseek63(fd, 0, SEEK_SET, &ret))
{
return 0;
}
return ret;
}
bool PosixSetOffset(int fd, AuUInt64 offset)
{
return PosixLseek63(fd, offset, SEEK_SET, nullptr);
}
static AuUInt64 PosixGetLength(int fd)
{
AuUInt64 ret {}, old {};
bool status {};
if (!PosixLseek63(fd, 0, SEEK_SET, &old))
{
return 0;
}
status = PosixLseek63(fd, 0, SEEK_END, &ret);
status &= PosixLseek63(fd, old, SEEK_SET, nullptr);
return status ? ret : 0;
}
bool PosixRead(int fd, void *buf, AuUInt32 count, AuUInt32 *pRead)
{
auto ret = read(fd, buf, count);
if (ret < 0)
{
SysPushErrorIO("PosixRead IO Error: %i", ret);
return false;
}
if (pRead)
{
*pRead = ret;
}
return true;
}
bool PosixWrite(int fd, const void *buf, AuUInt32 count, AuUInt32 *pWritten)
{
auto ret = write(fd, buf, count);
if (ret < 0)
{
SysPushErrorIO("PosixWrite IO Error: %i", ret);
return false;
}
if (pWritten)
{
*pWritten = ret;
}
return true;
}
PosixFileStream::~PosixFileStream()
{
Close();
}
bool PosixFileStream::Init(int handle, const AuString &path)
{
AuCtorCode_t code;
this->handle_ = handle;
this->path_ = AuTryConstruct<AuString>(code, path);
if (!code)
{
return false;
}
return true;
}
AuUInt64 PosixFileStream::GetOffset()
{
AU_LOCK_GUARD(this->spinlock_);
if (this->handle_ == -1)
{
SysPushErrorUninitialized();
return 0;
}
return PosixGetOffset(this->handle_);
}
bool PosixFileStream::SetOffset(AuUInt64 offset)
{
AU_LOCK_GUARD(this->spinlock_);
if (this->handle_ == -1)
{
SysPushErrorUninitialized();
return 0;
}
return PosixSetOffset(this->handle_, offset);
}
AuUInt64 PosixFileStream::GetLength()
{
AU_LOCK_GUARD(this->spinlock_);
if (this->handle_ == -1)
{
SysPushErrorUninitialized();
return 0;
}
return PosixGetLength(this->handle_);
}
bool PosixFileStream::Read(const Memory::MemoryViewStreamWrite &parameters)
{
AU_LOCK_GUARD(this->spinlock_);
if (this->handle_ == -1)
{
SysPushErrorUninitialized();
return 0;
}
auto length = parameters.length;
AuUInt offset {0};
while (length)
{
AuUInt32 read;
int blockSize = AuMin(kFileCopyBlock, length);
if (!PosixRead(handle_, &reinterpret_cast<char *>(parameters.ptr)[offset], blockSize, &read))
{
SysPushErrorNested("File Error: {}", path_);
break;
}
if (read == 0)
{
break;
}
offset += read;
length -= read;
}
if (!offset)
{
return false;
}
parameters.outVariable = offset;
return true;
}
bool PosixFileStream::Write(const Memory::MemoryViewStreamRead &parameters)
{
AU_LOCK_GUARD(this->spinlock_);
if (this->handle_ == -1)
{
SysPushErrorUninitialized();
return 0;
}
AuUInt length = parameters.length;
AuUInt offset {0};
while (length)
{
AuUInt32 written;
int blockSize = AuMin(AuUInt(kFileCopyBlock), length);
if (!PosixWrite(this->handle_, &reinterpret_cast<const char *>(parameters.ptr)[offset], blockSize, &written))
{
SysPushErrorNested("File Error: {}", path_);
return false;
}
if (written != blockSize)
{
SysPushErrorNested("File Error: {}", path_);
break;
}
offset += written;
length -= written;
}
if (!offset)
{
return false;
}
parameters.outVariable = offset;
return true;
}
void PosixFileStream::Close()
{
int handle = AuExchange(this->handle_, -1);
if (handle != -1)
{
::close(handle);
}
if (AuExchange(this->bMadeTemporary, false))
{
::unlink(this->path_.c_str());
}
}
void PosixFileStream::Flush()
{
::fsync(this->handle_);
}
void PosixFileStream::WriteEoS()
{
::ftruncate(this->handle_, GetOffset());
}
void PosixFileStream::MakeTemporary()
{
this->bMadeTemporary = true;
}
static IFileStream *OpenNew(const AuString &path, EFileOpenMode openMode, EFileAdvisoryLockLevel lock)
{
auto pathex = NormalizePathRet(path);
if (openMode != EFileOpenMode::eRead)
{
CreateDirectories(pathex, true);
}
auto fileHandle = ::open(pathex.c_str(),
openMode == EFileOpenMode::eRead ? O_RDONLY : (O_RDWR | O_CREAT),
0664);
if (fileHandle < 0)
{
SysPushErrorIO("Couldn't open file: {} ({}) {}", path, pathex, errno);
return nullptr;
}
if (!ApplyDumbAdvisoryLock(fileHandle, lock))
{
SysPushErrorIO("Couldn't open file: {}. File node (not section) is locked.", path);
return nullptr;
}
auto stream = _new PosixFileStream();
if (!stream)
{
::close(fileHandle);
SysPushErrorMem("{}", path);
return nullptr;
}
if (!stream->Init(fileHandle, pathex))
{
delete stream;
SysPushErrorGeneric("{}", path);
return nullptr;
}
return stream;
}
AUKN_SYM void OpenRelease(IFileStream *that)
{
AuSafeDelete<PosixFileStream *>(that);
}
AUKN_SYM IFileStream *OpenReadNew(const AuString &path, EFileAdvisoryLockLevel level)
{
return OpenNew(path, EFileOpenMode::eRead, level);
}
AUKN_SYM void OpenReadRelease(IFileStream * that)
{
AuSafeDelete<PosixFileStream *>(that);
}
AUKN_SYM IFileStream *OpenWriteNew(const AuString &path, EFileAdvisoryLockLevel level)
{
return OpenNew(path, EFileOpenMode::eWrite, level);
}
AUKN_SYM void OpenWriteRelease(IFileStream *that)
{
AuSafeDelete<PosixFileStream *>(that);
}
}
#endif