/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: FileStream.Unix.cpp Date: 2021-6-12 Author: Reece ***/ #include #include "FS.hpp" #include "FileStream.Generic.hpp" #include "FileAdvisory.Unix.hpp" #include #include #include #include #include #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 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(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(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 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 &pIOHandle) { SysCheckArgNotNull(pIOHandle, {}); auto pStream = _new PosixFileStream(); SysCheckNotNullMemory(pStream, {}); if (!AuStaticCast(pIOHandle)->pIPCPipe && AuStaticCast(pIOHandle)->ShouldClone()) { auto pIOHandle2 = AuMakeShared(); 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(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(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(that); } AUKN_SYM IFileStream *OpenReadNew(const AuROString &path, EFileAdvisoryLockLevel level) { return OpenNewEx(path, EFileOpenMode::eRead, level, false); } AUKN_SYM void OpenReadRelease(IFileStream * that) { AuSafeDelete(that); } AUKN_SYM IFileStream *OpenWriteNew(const AuROString &path, EFileAdvisoryLockLevel level) { return OpenNewEx(path, EFileOpenMode::eWrite, level, false); } AUKN_SYM void OpenWriteRelease(IFileStream *that) { AuSafeDelete(that); } } #endif