520 lines
12 KiB
C++
Executable File
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 ¶meters)
|
|
{
|
|
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 ¶meters)
|
|
{
|
|
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 AuROString &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 AuROString &path)
|
|
{
|
|
return OpenNewEx(path, EFileOpenMode::eWrite, EFileAdvisoryLockLevel::eBlockReadWrite, true);
|
|
}
|
|
|
|
AUKN_SYM void CreateRelease(IFileStream *that)
|
|
{
|
|
AuSafeDelete<PosixFileStream *>(that);
|
|
}
|
|
|
|
AUKN_SYM IFileStream *OpenNew(const AuROString &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 AuROString &path, EFileAdvisoryLockLevel level)
|
|
{
|
|
return OpenNewEx(path, EFileOpenMode::eRead, level, false);
|
|
}
|
|
|
|
AUKN_SYM void OpenReadRelease(IFileStream * that)
|
|
{
|
|
AuSafeDelete<PosixFileStream *>(that);
|
|
}
|
|
|
|
AUKN_SYM IFileStream *OpenWriteNew(const AuROString &path, EFileAdvisoryLockLevel level)
|
|
{
|
|
return OpenNewEx(path, EFileOpenMode::eWrite, level, false);
|
|
}
|
|
|
|
AUKN_SYM void OpenWriteRelease(IFileStream *that)
|
|
{
|
|
AuSafeDelete<PosixFileStream *>(that);
|
|
}
|
|
}
|
|
#endif
|