549 lines
15 KiB
C++
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 ¶meters)
|
|
{
|
|
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 ¶meters)
|
|
{
|
|
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 |