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

549 lines
15 KiB
C++

/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: FileStream.NT.cpp
Date: 2021-6-12
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "FS.hpp"
#include "FileStream.NT.hpp"
#include "FileAdvisory.NT.hpp"
#if !defined(_AURUNTIME_GENERICFILESTREAM)
namespace Aurora::IO::FS
{
static const AuUInt64 kFileCopyBlock = 0xFFFF * 128;
WinFileStream::~WinFileStream()
{
Close();
}
void WinFileStream::Init(AuSPtr<IIOHandle> pHandle)
{
this->pHandle_ = pHandle;
}
AuUInt64 WinFileStream::GetOffset()
{
LARGE_INTEGER distance {};
LARGE_INTEGER pos {};
HANDLE hHandle {};
if (auto pHandle = this->pHandle_)
{
if (!pHandle->IsFile())
{
SysPushErrorIOResourceRejected();
return 0;
}
hHandle = this->GetWin32Handle();
if (hHandle == INVALID_HANDLE_VALUE)
{
SysPushErrorInvalidFd();
return 0;
}
}
else
{
SysPushErrorUninitialized();
return 0;
}
if (::SetFilePointerEx(hHandle,
distance,
&pos,
FILE_CURRENT) == INVALID_SET_FILE_POINTER)
{
SysPushErrorIO("SetFilePointerEx IO Error: 0x{:x}, {}", GetLastError(), this->pHandle_ ? this->pHandle_->GetPath() : "");
return 0;
}
return pos.QuadPart;
}
bool WinFileStream::SetOffset(AuUInt64 offset)
{
LARGE_INTEGER distance {};
LARGE_INTEGER pos {};
HANDLE hHandle {};
if (auto pHandle = this->pHandle_)
{
if (!pHandle->IsFile())
{
SysPushErrorIOResourceRejected();
return 0;
}
hHandle = this->GetWin32Handle();
if (hHandle == INVALID_HANDLE_VALUE)
{
SysPushErrorInvalidFd();
return 0;
}
}
else
{
SysPushErrorUninitialized();
return 0;
}
distance.QuadPart = offset;
if (::SetFilePointerEx(hHandle, distance, &pos, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
{
SysPushErrorIO("SetFilePointerEx IO Error: 0x{:x}, {}", GetLastError(), this->pHandle_ ? this->pHandle_->GetPath() : "");
return false;
}
return true;
}
AuUInt64 WinFileStream::GetLength()
{
LARGE_INTEGER length;
HANDLE hHandle {};
if (auto pHandle = this->pHandle_)
{
if (!pHandle->IsFile())
{
SysPushErrorIOResourceRejected();
return 0;
}
hHandle = this->GetWin32Handle();
if (hHandle == INVALID_HANDLE_VALUE)
{
SysPushErrorInvalidFd();
return 0;
}
}
else
{
SysPushErrorUninitialized();
return 0;
}
if (!::GetFileSizeEx(hHandle, &length))
{
SysPushErrorIO();
return 0;
}
return length.QuadPart;
}
bool WinFileStream::Read(const Memory::MemoryViewStreamWrite &parameters)
{
OVERLAPPED overlapped {};
AuUInt64 uOffset {};
parameters.outVariable = 0;
if (!this->pHandle_)
{
SysPushErrorUninitialized();
return 0;
}
auto hHandle = this->GetWin32Handle(true);
HANDLE hEventHandle { INVALID_HANDLE_VALUE };
if (hHandle == INVALID_HANDLE_VALUE)
{
SysPushErrorIOResourceRejected();
return {};
}
AuUInt64 uLength = parameters.length;
if (this->pHandle_->IsPipe())
{
DWORD uAvail {};
if (!PeekNamedPipe(hHandle, NULL, NULL, NULL, &uAvail, NULL))
{
return false;
}
uLength = AuMin(uLength, uAvail);
if (!uLength)
{
return true;
}
}
bool bIsAsync = this->pHandle_->IsAsync();
if (bIsAsync)
{
hEventHandle = ::CreateEventA(nullptr, false, false, nullptr);
if (!hEventHandle)
{
SysPushErrorGeneric();
return false;
}
}
while (uLength)
{
DWORD read {};
auto blockSize = AuMin<AuUInt64>(AuUInt(kFileCopyBlock), uLength);
if (this->pHandle_->IsAsync())
{
overlapped.hEvent = hEventHandle;
if (!::ReadFile(hHandle,
reinterpret_cast<char *>(parameters.ptr) + uOffset,
AuUInt32(blockSize),
NULL,
&overlapped))
{
if (::GetLastError() == ERROR_IO_PENDING)
{
::WaitForSingleObject(hEventHandle, 0);
}
else
{
SysPushErrorIO("ReadFile IO Error: 0x{:x}, {}", GetLastError(), this->pHandle_ ? this->pHandle_->GetPath() : "");
AuWin32CloseHandle(hEventHandle);
parameters.outVariable = uOffset;
return false;
}
}
if (!::GetOverlappedResult(hHandle, &overlapped, &read, true))
{
if (::GetLastError() != ERROR_HANDLE_EOF)
{
AuWin32CloseHandle(hEventHandle);
parameters.outVariable = uOffset;
return false;
}
}
}
else
{
if (!::ReadFile(hHandle,
reinterpret_cast<char *>(parameters.ptr) + uOffset,
blockSize,
&read,
NULL))
{
SysPushErrorIO("ReadFile IO Error: 0x{:x}, {}", GetLastError(), this->pHandle_ ? this->pHandle_->GetPath() : "");
AuWin32CloseHandle(hEventHandle);
parameters.outVariable = uOffset;
return false;
}
}
if (read == 0)
{
break;
}
uOffset += read;
uLength -= read;
}
AuWin32CloseHandle(hEventHandle);
parameters.outVariable = uOffset;
return true;
}
bool WinFileStream::Write(const Memory::MemoryViewStreamRead &parameters)
{
OVERLAPPED overlapped {};
AuUInt64 uOffset {};
parameters.outVariable = 0;
auto optHandle = this->GetHandle()->GetOSWriteHandleSafe();
if (!optHandle)
{
SysPushErrorIOResourceRejected();
return false;
}
auto hHandle = (HANDLE)optHandle.value();
HANDLE hEventHandle { INVALID_HANDLE_VALUE };
if (hHandle == INVALID_HANDLE_VALUE)
{
SysPushErrorUninitialized();
return 0;
}
bool bIsAsync = this->pHandle_->IsAsync();
if (bIsAsync)
{
hEventHandle = ::CreateEventA(nullptr, false, false, nullptr);
if (!hEventHandle)
{
SysPushErrorGeneric();
return false;
}
}
AuUInt64 uLength = parameters.length;
while (uLength)
{
DWORD written {};
auto uBlockSize = AuMin<AuUInt64>(AuUInt(kFileCopyBlock), uLength);
if (bIsAsync)
{
overlapped.hEvent = hEventHandle;
if (!::WriteFile(hHandle,
reinterpret_cast<const char *>(parameters.ptr) + uOffset,
AuUInt32(uBlockSize),
NULL,
&overlapped))
{
if (GetLastError() == ERROR_IO_PENDING)
{
::WaitForSingleObject(hEventHandle, 0);
}
else
{
SysPushErrorIO("WriteFile IO Error: 0x{:x}, {}", GetLastError(), this->pHandle_ ? this->pHandle_->GetPath() : "");
AuWin32CloseHandle(hEventHandle);
parameters.outVariable = uOffset;
return false;
}
}
if (!::GetOverlappedResult(hHandle, &overlapped, &written, true))
{
AuWin32CloseHandle(hEventHandle);
parameters.outVariable = uOffset;
return false;
}
}
else
{
if (!::WriteFile(hHandle, reinterpret_cast<const char *>(parameters.ptr) + uOffset, uBlockSize, &written, NULL))
{
SysPushErrorIO("WriteFile IO Error: 0x{:x}, {}", GetLastError(), this->pHandle_ ? this->pHandle_->GetPath() : "");
parameters.outVariable = uOffset;
return false;
}
}
if (!written)
{
SysPushErrorIO();
parameters.outVariable = uOffset;
AuWin32CloseHandle(hEventHandle);
return true;
}
uOffset += written;
uLength -= written;
}
AuWin32CloseHandle(hEventHandle);
if (!uOffset)
{
return false;
}
parameters.outVariable = uOffset;
return true;
}
void WinFileStream::WriteEoS()
{
auto hHandle = this->GetWin32Handle();
if (hHandle == INVALID_HANDLE_VALUE)
{
SysPushErrorUninitialized();
return;
}
SetEndOfFile(hHandle);
}
void WinFileStream::Close()
{
auto hHandle = this->GetWin32Handle();
bool bDeleteFailed {};
AuString path;
if ((hHandle != INVALID_HANDLE_VALUE) &&
(this->bShouldDelete))
{
FILE_DISPOSITION_INFO rm {};
rm.DeleteFile = true;
if (!(pSetFileInformationByHandle &&
pSetFileInformationByHandle(hHandle, _FILE_INFO_BY_HANDLE_CLASS::FileDispositionInfo, &rm, sizeof(rm))))
{
path = this->pHandle_ ? this->pHandle_->GetPath() : "";
SysPushErrorIO("Couldn't delete temporary file {}", path);
if (path.length())
{
bDeleteFailed = true;
}
}
else
{
bDeleteFailed = true;
}
}
AuResetMember(this->pHandle_);
// a bit of a hack for problematic NT-like platforms and Windows XP
if (bDeleteFailed)
{
AuFS::Remove(path);
}
}
void WinFileStream::Flush()
{
auto hHandle = this->GetWin32Handle();
::FlushFileBuffers(hHandle);
}
void WinFileStream::MakeTemporary()
{
this->bShouldDelete = true;
}
AuSPtr<IIOHandle> WinFileStream::GetHandle()
{
return this->pHandle_;
}
HANDLE WinFileStream::GetWin32Handle(bool bReadOnly)
{
if (auto pHandle = this->pHandle_)
{
if (bReadOnly)
{
return (HANDLE)pHandle->GetOSReadHandleSafe().ValueOr((AuUInt)INVALID_HANDLE_VALUE);
}
else
{
return (HANDLE)pHandle->GetOSWriteHandleSafe().ValueOr(pHandle->GetOSReadHandleSafe().ValueOr((AuUInt)INVALID_HANDLE_VALUE));
}
}
return INVALID_HANDLE_VALUE;
}
AUKN_SYM AuSPtr<IFileStream> OpenBlockingFileStreamFromHandle(AuSPtr<IIOHandle> pIOHandle)
{
auto pStream = AuMakeShared<WinFileStream>();
if (!pStream)
{
SysPushErrorMemory();
return nullptr;
}
pStream->Init(pIOHandle);
return pStream;
}
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 WinFileStream();
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<WinFileStream *>(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<WinFileStream *>(that);
}
AUKN_SYM IFileStream *OpenReadNew(const AuString &path, EFileAdvisoryLockLevel level)
{
return OpenNew(path, EFileOpenMode::eRead, level);
}
AUKN_SYM void OpenReadRelease(IFileStream * that)
{
AuSafeDelete<WinFileStream *>(that);
}
AUKN_SYM IFileStream *OpenWriteNew(const AuString &path, EFileAdvisoryLockLevel level)
{
return OpenNew(path, EFileOpenMode::eWrite, level);
}
AUKN_SYM void OpenWriteRelease(IFileStream *that)
{
AuSafeDelete<WinFileStream *>(that);
}
}
#endif