/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: FileStream.NT.cpp Date: 2021-6-12 Author: Reece ***/ #include #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 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) { if (!this->pHandle_) { SysPushErrorUninitialized(); return 0; } auto hHandle = this->GetWin32Handle(true); HANDLE hEventHandle { INVALID_HANDLE_VALUE }; if (hHandle == INVALID_HANDLE_VALUE) { SysPushErrorIOResourceRejected(); return {}; } bool bIsAsync = this->pHandle_->IsAsync(); if (bIsAsync) { hEventHandle = ::CreateEventA(nullptr, false, false, nullptr); } parameters.outVariable = 0; AuUInt64 uLength = parameters.length; AuUInt64 uOffset {}; while (uLength) { DWORD read {}; auto blockSize = AuMin(AuUInt(kFileCopyBlock), uLength); if (this->pHandle_->IsAsync()) { OVERLAPPED overlapped {}; overlapped.hEvent = hEventHandle; if (!::ReadFile(hHandle, reinterpret_cast(parameters.ptr) + uOffset, blockSize, NULL, &overlapped) && ::GetLastError() != ERROR_IO_PENDING) { SysPushErrorIO("ReadFile IO Error: 0x{:x}, {}", GetLastError(), this->pHandle_ ? this->pHandle_->GetPath() : ""); AuWin32CloseHandle(hEventHandle); parameters.outVariable = uOffset; return false; } ::WaitForSingleObject(hEventHandle, 0); if (!::GetOverlappedResult(hHandle, &overlapped, &read, true)) { if (::GetLastError() != ERROR_HANDLE_EOF) { AuWin32CloseHandle(hEventHandle); parameters.outVariable = uOffset; return false; } } } else { if (!::ReadFile(hHandle, reinterpret_cast(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) { 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); } parameters.outVariable = 0; AuUInt64 uLength = parameters.length; AuUInt64 uOffset {}; while (uLength) { DWORD written {}; auto uBlockSize = AuMin(AuUInt(kFileCopyBlock), uLength); if (bIsAsync) { OVERLAPPED overlapped {}; overlapped.hEvent = hEventHandle; if (!::WriteFile(hHandle, reinterpret_cast(parameters.ptr) + uOffset, uBlockSize, NULL, &overlapped) && ::GetLastError() != ERROR_IO_PENDING) { SysPushErrorIO("WriteFile IO Error: 0x{:x}, {}", GetLastError(), this->pHandle_ ? this->pHandle_->GetPath() : ""); AuWin32CloseHandle(hEventHandle); parameters.outVariable = uOffset; return false; } ::WaitForSingleObject(hEventHandle, 0); if (!::GetOverlappedResult(hHandle, &overlapped, &written, true)) { AuWin32CloseHandle(hEventHandle); parameters.outVariable = uOffset; return false; } } else { if (!::WriteFile(hHandle, reinterpret_cast(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 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 OpenBlockingFileStreamFromHandle(AuSPtr pIOHandle) { auto pStream = AuMakeShared(); 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(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(that); } AUKN_SYM IFileStream *OpenReadNew(const AuString &path, EFileAdvisoryLockLevel level) { return OpenNew(path, EFileOpenMode::eRead, level); } AUKN_SYM void OpenReadRelease(IFileStream * that) { AuSafeDelete(that); } AUKN_SYM IFileStream *OpenWriteNew(const AuString &path, EFileAdvisoryLockLevel level) { return OpenNew(path, EFileOpenMode::eWrite, level); } AUKN_SYM void OpenWriteRelease(IFileStream *that) { AuSafeDelete(that); } } #endif