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

515 lines
14 KiB
C++

/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: FS.NT.cpp
Date: 2021-6-12
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "FS.hpp"
#include "FS.Generic.hpp"
#include <Source/Time/Time.hpp>
#if !defined(_AURUNTIME_GENERICFS)
namespace Aurora::IO::FS
{
static const AuUInt64 kFileCopyBlock = 0xFFFF; // 64KiB
bool _MkDir(const AuString &str)
{
return CreateDirectoryW(Locale::ConvertFromUTF8(str).c_str(), NULL);
}
struct ReadDirStructure : IReadDir
{
HANDLE hFind {INVALID_HANDLE_VALUE};
WIN32_FIND_DATAW ffd;
bool bFirstTick {true};
bool bDead {false};
StatEx stat;
AuString sPath;
~ReadDirStructure()
{
::FindClose(this->hFind);
}
virtual StatEx *Next() override
{
if (!AuExchange(this->bFirstTick, false))
{
if (::FindNextFileW(this->hFind, &this->ffd) == 0)
{
return {};
}
}
if (this->bDead)
{
return {};
}
if (this->ffd.cFileName == std::wstring(L".") ||
this->ffd.cFileName == std::wstring(L".."))
{
return Next();
}
stat.bExists = true;
stat.fileName = Locale::ConvertFromWChar(this->ffd.cFileName);
if (stat.fileName.empty())
{
this->bDead = true;
return {};
}
stat.path = NormalizePathRet(this->sPath + stat.fileName);
if (stat.path.empty())
{
this->bDead = true;
return {};
}
stat.bExistsDirectory = this->ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
stat.bExistsFile = !stat.bExistsDirectory;
stat.bSymLink = this->ffd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT;
stat.uSize = AuUInt64(this->ffd.nFileSizeLow) | (AuUInt64(this->ffd.nFileSizeHigh) << 32);
stat.createdNs = Time::ConvertTimestampNs(this->ffd.ftCreationTime);
stat.modifiedNs = Time::ConvertTimestampNs(this->ffd.ftLastWriteTime);
stat.accessedNs = Time::ConvertTimestampNs(this->ffd.ftLastAccessTime);
return &stat;
}
};
AUKN_SYM AuSPtr<IReadDir> ReadDir(const AuString &string)
{
if (string.empty())
{
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
return {};
}
auto pObj = AuMakeShared<ReadDirStructure>();
if (!pObj)
{
SysPushErrorMem();
return {};
}
if (!AuIOFS::NormalizePath(pObj->sPath, string))
{
SysPushErrorMem();
return {};
}
if (!AuTryInsert(pObj->sPath, '\\'))
{
SysPushErrorMem();
return {};
}
pObj->hFind = ::FindFirstFileW(AuLocale::ConvertFromUTF8(pObj->sPath + "*").c_str(), &pObj->ffd);
if (INVALID_HANDLE_VALUE == pObj->hFind)
{
SysPushErrorIO();
return {};
}
return pObj;
}
AUKN_SYM bool FilesInDirectory(const AuString &string, AuList<AuString> &files)
{
auto itr = ReadDir(string);
if (!itr)
{
return false;
}
// SECURITY(): if next fails, its indistinguishable from end of file list, and will return true. it kinda sucks
while (auto stat = itr->Next())
{
if (!stat->bExistsFile)
{
continue;
}
if (!AuTryInsert(files, stat->fileName))
{
return false;
}
}
return true;
}
AUKN_SYM bool DirsInDirectory(const AuString &string, AuList<AuString> &dirs)
{
auto itr = ReadDir(string);
if (!itr)
{
return false;
}
// SECURITY(): if next fails, its indistinguishable from end of file list, and will return true. it kinda sucks
while (auto stat = itr->Next())
{
if (!stat->bExistsDirectory)
{
continue;
}
if (!AuTryInsert(dirs, stat->fileName))
{
return false;
}
}
return true;
}
AUKN_SYM bool ReadFile(const AuString &path, AuByteBuffer &buffer)
{
std::wstring win32Path;
LARGE_INTEGER length;
bool status;
AuMemoryViewWrite writeView;
size_t offset;
if (path.empty())
{
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
return false;
}
status = false;
offset = 0;
win32Path = Locale::ConvertFromUTF8(NormalizePathRet(path));
if (win32Path.empty())
{
SysPushErrorMemory();
return false;
}
auto fileHandle = Win32Open(win32Path.c_str(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
false,
OPEN_EXISTING,
0,
0);
if (fileHandle == INVALID_HANDLE_VALUE &&
GetLastError() == ERROR_SHARING_VIOLATION)
{
RuntimeWaitForSecondaryTick();
fileHandle = Win32Open(win32Path.c_str(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
false,
OPEN_EXISTING,
0,
0);
}
if (fileHandle == INVALID_HANDLE_VALUE)
{
SysPushErrorIO("Couldn't open handle: {}", path);
return false;
}
if (!::GetFileSizeEx(fileHandle, &length))
{
SysPushErrorIO();
goto out;
}
if (length.QuadPart == 0)
{
return true;
}
writeView = buffer.GetOrAllocateLinearWriteable(length.QuadPart);
if (!writeView)
{
SysPushErrorMem();
goto out;
}
while (length.QuadPart)
{
DWORD read;
int blockSize = AuMin(static_cast<AuUInt64>(kFileCopyBlock), static_cast<AuUInt64>(length.QuadPart));
if (!::ReadFile(fileHandle, buffer.writePtr, blockSize, &read, NULL))
{
AuLogWarn("ReadFile IO Error: 0x{:x} {}", GetLastError(), path);
SysPushErrorIO();
goto out;
}
buffer.writePtr += read;
offset += read;
length.QuadPart -= read;
}
status = true;
out:
AuWin32CloseHandle(fileHandle);
return status;
}
AUKN_SYM bool FileExists(const AuString &path)
{
try
{
if (path.empty())
{
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
return false;
}
auto translatedPath = Locale::ConvertFromUTF8(NormalizePathRet(path));
if (translatedPath.empty())
{
SysPushErrorMemory();
return false;
}
DWORD dwAttrib = ::GetFileAttributesW(translatedPath.c_str());
return ((dwAttrib != INVALID_FILE_ATTRIBUTES) &&
!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
catch (...)
{
SysPushErrorCatch();
return false;
}
}
AUKN_SYM bool DirExists(const AuString &path)
{
try
{
if (path.empty())
{
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
return false;
}
auto translatedPath = Locale::ConvertFromUTF8(NormalizePathRet(path));
if (translatedPath.empty())
{
SysPushErrorMemory();
return false;
}
DWORD dwAttrib = ::GetFileAttributesW(translatedPath.c_str());
return ((dwAttrib != INVALID_FILE_ATTRIBUTES) &&
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
catch (...)
{
SysPushErrorCatch();
return false;
}
}
AUKN_SYM bool DirMk(const AuString &path)
{
try
{
if (path.empty())
{
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
return false;
}
auto translatedPath = NormalizePathRet(path);
if (translatedPath.empty())
{
SysPushErrorMemory();
return false;
}
return CreateDirectories(translatedPath, false);
}
catch (...)
{
SysPushErrorCatch();
return false;
}
}
AUKN_SYM bool Remove(const AuString &path)
{
try
{
if (path.empty())
{
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
return false;
}
Aurora::RuntimeWaitForSecondaryTick();
auto translatedPath = Locale::ConvertFromUTF8(NormalizePathRet(path));
if (translatedPath.empty())
{
SysPushErrorMemory();
return false;
}
DWORD dwAttrib = ::GetFileAttributesW(translatedPath.c_str());
if (dwAttrib == INVALID_FILE_ATTRIBUTES)
{
SysPushErrorIO("Couldn't open file ({}) for removal", path);
return false;
}
if (dwAttrib & FILE_ATTRIBUTE_READONLY)
{
SysPushErrorIO("Couldn't open file ({}) for removal (read-only)", path);
return false;
}
if (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)
{
if (::RemoveDirectoryW(translatedPath.c_str()))
{
return true;
}
}
else
{
if (::DeleteFileW(translatedPath.c_str()))
{
return true;
}
}
return false;
}
catch (...)
{
SysPushErrorCatch();
return false;
}
}
AUKN_SYM bool Relink(const AuString &src, const AuString &dest)
{
try
{
if (src.empty() ||
dest.empty())
{
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
return false;
}
auto pathSrcExpanded = Locale::ConvertFromUTF8(NormalizePathRet(src));
auto pathExpandedA = NormalizePathRet(dest);
auto pathExpanded = Locale::ConvertFromUTF8(pathExpandedA);
if (pathSrcExpanded.empty() ||
pathExpanded.empty())
{
SysPushErrorMemory();
return false;
}
CreateDirectories(pathExpandedA, true);
Aurora::RuntimeWaitForSecondaryTick();
return ::MoveFileW(pathSrcExpanded.c_str(),
pathExpanded.c_str());
}
catch (...)
{
return false;
}
}
AUKN_SYM bool Copy(const AuString &src, const AuString &dest)
{
try
{
if (src.empty() ||
dest.empty())
{
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
return false;
}
auto pathSrcExpanded = Locale::ConvertFromUTF8(NormalizePathRet(src));
auto pathExpandedA = NormalizePathRet(dest);
auto pathExpanded = Locale::ConvertFromUTF8(pathExpandedA);
if (pathSrcExpanded.empty() ||
pathExpanded.empty())
{
SysPushErrorMemory();
return false;
}
CreateDirectories(pathExpandedA, true);
return ::CopyFileW(pathSrcExpanded.c_str(),
pathExpanded.c_str(), true);
}
catch (...)
{
SysPushErrorCatch();
return false;
}
}
AUKN_SYM bool StatFile(const AuString &path, Stat &stat)
{
WIN32_FILE_ATTRIBUTE_DATA data;
stat = {};
if (path.empty())
{
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
return false;
}
auto translatedPath = Locale::ConvertFromUTF8(NormalizePathRet(path));
if (translatedPath.empty())
{
SysPushErrorMemory();
return false;
}
if (!::GetFileAttributesExW(translatedPath.c_str(), GetFileExInfoStandard, &data))
{
stat.bExists = false;
return false;
}
stat.bExists = true;
stat.bExistsDirectory = data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
stat.bExistsFile = !stat.bExistsDirectory;
stat.bSymLink = data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT;
stat.uSize = AuUInt64(data.nFileSizeLow) | (AuUInt64(data.nFileSizeHigh) << 32);
stat.createdNs = Time::ConvertTimestampNs(data.ftCreationTime);
stat.modifiedNs = Time::ConvertTimestampNs(data.ftLastWriteTime);
stat.accessedNs = Time::ConvertTimestampNs(data.ftLastAccessTime);
return true;
}
}
#endif