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

520 lines
12 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
***/
#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>
#include <sys/ioctl.h>
#include <Source/IO/AuIOHandle.hpp>
#if !defined(_AURUNTIME_GENERICFILESTREAM)
namespace Aurora::IO::FS
{
static const AuUInt64 kFileCopyBlock = 0x4000; // 16KiB
PosixFileStream::PosixFileStream() :
reader_(AuUnsafeRaiiToShared(this)),
writer_(AuUnsafeRaiiToShared(this)),
sreader_(AuUnsafeRaiiToShared(this)),
swriter_(AuUnsafeRaiiToShared(this))
{
}
PosixFileStream::~PosixFileStream()
{
Close();
}
bool PosixFileStream::Init(AuSPtr<IIOHandle> pHandle)
{
AuCtorCode_t code;
this->pHandle_ = pHandle;
return true;
}
AuUInt64 PosixFileStream::GetOffset()
{
AU_LOCK_GUARD(this->mutex_);
int fd {};
if (auto pHandle = this->pHandle_)
{
if (!pHandle->IsFile())
{
SysPushErrorIOResourceRejected();
return 0;
}
if (auto opt = pHandle->GetOSHandleSafe())
{
fd = opt.Value();
}
else
{
SysPushErrorInvalidFd();
return 0;
}
}
else
{
SysPushErrorUninitialized();
return 0;
}
return PosixGetOffset(fd);
}
bool PosixFileStream::SetOffset(AuUInt64 offset)
{
AU_LOCK_GUARD(this->mutex_);
int fd {};
if (auto pHandle = this->pHandle_)
{
if (!pHandle->IsFile())
{
SysPushErrorIOResourceRejected();
return 0;
}
if (auto opt = pHandle->GetOSHandleSafe())
{
fd = opt.Value();
}
else
{
SysPushErrorInvalidFd();
return false;
}
}
else
{
SysPushErrorUninitialized();
return false;
}
return PosixSetOffset(fd, offset);
}
AuUInt64 PosixFileStream::GetLength()
{
AU_LOCK_GUARD(this->mutex_);
int fd {};
if (auto pHandle = this->pHandle_)
{
if (!pHandle->IsFile())
{
// Intentionally suppressed (for the moment)
// I don't want to fill error stacks up under paths that can handle char-devs by reading until EoS
// If we do, we could hit an always-throw on AuErrorStack-catch
// Some users are completely unaware they can access/test the underlying handle
// Returning zero will do
return 0;
}
if (auto opt = pHandle->GetOSHandleSafe())
{
fd = opt.Value();
}
else
{
SysPushErrorInvalidFd();
return 0;
}
}
else
{
SysPushErrorUninitialized();
return 0;
}
return PosixGetLength(fd);
}
bool PosixFileStream::Read(const Memory::MemoryViewStreamWrite &parameters)
{
AU_LOCK_GUARD(this->mutex_);
parameters.outVariable = 0;
if (!this->pHandle_)
{
SysPushErrorUninitialized();
return 0;
}
int fd = (int)this->pHandle_->GetOSReadHandleSafe().ValueOr((AuUInt)-1);
if (fd == -1)
{
SysPushErrorIOResourceRejected();
return 0;
}
AuUInt uOffset {};
AuUInt64 uLength = parameters.length;
if (this->pHandle_->IsPipe())
{
#if defined(FIONREAD)
int available {};
if (::ioctl(fd, FIONREAD, &available) < 0)
{
return 0;
}
uLength = AuMin(uLength, available);
if (!uLength)
{
return 0;
}
#endif
}
while (uLength)
{
AuUInt32 uRead;
if (!PosixRead(fd,
&reinterpret_cast<char *>(parameters.ptr)[uOffset],
AuMin(kFileCopyBlock, uLength),
&uRead))
{
SysPushErrorNested("File Error: {}", this->path_);
break;
}
if (uRead == 0)
{
break;
}
uOffset += uRead;
uLength -= uRead;
}
parameters.outVariable = uOffset;
return true;
}
bool PosixFileStream::Write(const Memory::MemoryViewStreamRead &parameters)
{
AU_LOCK_GUARD(this->mutex_);
parameters.outVariable = 0;
if (!this->pHandle_)
{
SysPushErrorUninitialized();
return 0;
}
int fd = this->pHandle_->GetOSWriteHandleSafe().ValueOr((AuUInt)-1);
if (fd == -1)
{
SysPushErrorUninitialized();
return 0;
}
AuUInt length = parameters.length;
AuUInt offset {0};
while (length)
{
AuUInt32 written;
int blockSize = AuMin(AuUInt(kFileCopyBlock), length);
if (!PosixWrite(fd, &reinterpret_cast<const char *>(parameters.ptr)[offset], blockSize, &written))
{
SysPushErrorNested("File Error: {}", this->pHandle_->GetPath());
return false;
}
if (!written)
{
break;
}
offset += written;
length -= written;
}
parameters.outVariable = offset;
return true;
}
void PosixFileStream::Close()
{
AU_DEBUG_MEMCRUNCH;
AU_LOCK_GUARD(this->mutex_);
AuString path;
if (AuExchange(this->bMadeTemporary, false) &&
this->pHandle_)
{
path = this->pHandle_->GetPath();
}
AuResetMember(this->pHandle_);
if (path.size())
{
Aurora::RuntimeWaitForSecondaryTick();
PosixUnlink(path.c_str());
}
}
bool PosixFileStream::IsFlushOnClose() const
{
AU_LOCK_GUARD(this->mutex_);
if (auto &pHandle = this->pHandle_)
{
return pHandle->IsFlushOnClose();
}
else
{
return false;
}
}
void PosixFileStream::SetFlushOnClose(bool bFlushOnClose)
{
AU_LOCK_GUARD(this->mutex_);
if (auto &pHandle = this->pHandle_)
{
pHandle->SetFlushOnClose(bFlushOnClose);
}
}
bool PosixFileStream::IsWriteEoSOnClose() const
{
AU_LOCK_GUARD(this->mutex_);
if (auto &pHandle = this->pHandle_)
{
return pHandle->IsWriteEoSOnClose();
}
else
{
return false;
}
}
void PosixFileStream::SetWriteEoSOnClose(bool bShouldWriteEoS)
{
AU_LOCK_GUARD(this->mutex_);
if (auto &pHandle = this->pHandle_)
{
pHandle->SetWriteEoSOnClose(bShouldWriteEoS);
}
}
void PosixFileStream::Flush()
{
AU_LOCK_GUARD(this->mutex_);
if (!this->pHandle_)
{
SysPushErrorUninitialized();
return;
}
int fd = this->pHandle_->GetOSWriteHandleSafe().ValueOr((AuUInt)-1);
if (fd == -1)
{
SysPushErrorIOResourceRejected();
return;
}
SysFlushHandle(fd);
}
void PosixFileStream::WriteEoS()
{
AU_LOCK_GUARD(this->mutex_);
if (!this->pHandle_)
{
SysPushErrorUninitialized();
return;
}
int fd = this->pHandle_->GetOSWriteHandleSafe().ValueOr((AuUInt)-1);
if (fd == -1)
{
SysPushErrorIOResourceRejected();
return;
}
PosixWriteEoS(fd);
}
void PosixFileStream::MakeTemporary()
{
this->bMadeTemporary = true;
}
int PosixFileStream::GetUnixHandle()
{
return this->pHandle_ ?
(int)this->pHandle_->GetOSWriteHandleSafe().ValueOr(this->pHandle_->GetOSReadHandleSafe().ValueOr((AuUInt)-1)) :
-1;
}
AuSPtr<IIOHandle> PosixFileStream::GetHandle()
{
return this->pHandle_;
}
IO::IStreamReader *PosixFileStream::ToStreamReader()
{
return &this->reader_;
}
IO::IStreamWriter *PosixFileStream::ToStreamWriter()
{
return &this->writer_;
}
IO::ISeekingReader *PosixFileStream::ToStreamSeekingReader()
{
return &this->sreader_;
}
IO::ISeekingWriter *PosixFileStream::ToStreamSeekingWriter()
{
return &this->swriter_;
}
AUKN_SYM IFileStream *OpenBlockingFileStreamFromHandleNew(const AuSPtr<IIOHandle> &pIOHandle)
{
SysCheckArgNotNull(pIOHandle, {});
auto pStream = _new PosixFileStream();
SysCheckNotNullMemory(pStream, {});
if (!AuStaticCast<AFileHandle>(pIOHandle)->pIPCPipe &&
AuStaticCast<AFileHandle>(pIOHandle)->ShouldClone())
{
auto pIOHandle2 = AuMakeShared<AFileHandle>();
SysCheckNotNullMemory(pIOHandle2, {});
if (!pIOHandle2->InitFromPair(pIOHandle->GetOSReadHandleSafe(), pIOHandle->GetOSWriteHandle()))
{
SysPushErrorIOResourceFailure();
return {};
}
pStream->Init(pIOHandle2);
}
else
{
pStream->Init(pIOHandle);
}
return pStream;
}
AUKN_SYM void OpenBlockingFileStreamFromHandleRelease(IFileStream *that)
{
AuSafeDelete<PosixFileStream *>(that);
}
static IFileStream *OpenNewEx(const AuString &path,
EFileOpenMode openMode,
EFileAdvisoryLockLevel lock,
bool bCheck)
{
try
{
auto pHandle = AuIO::IOHandleShared();
if (!pHandle)
{
SysPushErrorMemory();
return nullptr;
}
AuIO::IIOHandle::HandleCreate createhandle(path);
createhandle.eAdvisoryLevel = lock;
createhandle.eMode = openMode;
createhandle.bFailIfNonEmptyFile = bCheck;
createhandle.bDirectIOMode = false;
createhandle.bAsyncHandle = false;
if (!pHandle->InitFromPath(createhandle))
{
SysPushErrorNested();
return nullptr;
}
auto pStream = _new PosixFileStream();
if (!pStream)
{
SysPushErrorMemory();
return nullptr;
}
pStream->Init(pHandle);
return pStream;
}
catch (...)
{
return nullptr;
}
}
AUKN_SYM IFileStream *CreateNew(const AuString &path)
{
return OpenNewEx(path, EFileOpenMode::eWrite, EFileAdvisoryLockLevel::eBlockReadWrite, true);
}
AUKN_SYM void CreateRelease(IFileStream *that)
{
AuSafeDelete<PosixFileStream *>(that);
}
AUKN_SYM IFileStream *OpenNew(const AuString &path, EFileOpenMode openMode, EFileAdvisoryLockLevel lock)
{
return OpenNewEx(path, openMode, lock, false);
}
AUKN_SYM void OpenRelease(IFileStream *that)
{
AuSafeDelete<PosixFileStream *>(that);
}
AUKN_SYM IFileStream *OpenReadNew(const AuString &path, EFileAdvisoryLockLevel level)
{
return OpenNewEx(path, EFileOpenMode::eRead, level, false);
}
AUKN_SYM void OpenReadRelease(IFileStream * that)
{
AuSafeDelete<PosixFileStream *>(that);
}
AUKN_SYM IFileStream *OpenWriteNew(const AuString &path, EFileAdvisoryLockLevel level)
{
return OpenNewEx(path, EFileOpenMode::eWrite, level, false);
}
AUKN_SYM void OpenWriteRelease(IFileStream *that)
{
AuSafeDelete<PosixFileStream *>(that);
}
}
#endif