/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: FS.NT.cpp Date: 2021-6-12 Author: Reece ***/ #include #include "FS.hpp" #include "FS.Generic.hpp" #include #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.created = Time::ConvertTimestamp(this->ffd.ftCreationTime); stat.modified = Time::ConvertTimestamp(this->ffd.ftLastWriteTime); stat.accessed = Time::ConvertTimestamp(this->ffd.ftLastAccessTime); return &stat; } }; AUKN_SYM AuSPtr ReadDir(const AuString &string) { auto pObj = AuMakeShared(); 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 &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 &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 WriteFile(const AuString &path, const Memory::MemoryViewRead &blob) { bool status; size_t offset; HANDLE fileHandle; std::wstring win32Path; AuString pathNormalized; AuUInt length; status = false; pathNormalized = NormalizePathRet(path); if (pathNormalized.empty()) { return false; } win32Path = Locale::ConvertFromUTF8(pathNormalized); if (win32Path.empty()) { return false; } CreateDirectories(pathNormalized, true); fileHandle = CreateFileW(win32Path.c_str(), GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (fileHandle == INVALID_HANDLE_VALUE) { SysPushErrorIO("Couldn't open handle: {}", path); return false; } length = blob.length; offset = 0; while (length) { DWORD written; int blockSize = AuMin(static_cast(kFileCopyBlock), static_cast(length)); if (!::WriteFile(fileHandle, &reinterpret_cast(blob.ptr)[offset], blockSize, &written, NULL)) { SysPushErrorIO(); goto out; } if (!written) { SysPushErrorIO(); goto out; } offset += written; length -= written; } status = true; out: AuWin32CloseHandle(fileHandle); return status; } AUKN_SYM bool ReadFile(const AuString &path, AuByteBuffer &buffer) { std::wstring win32Path; LARGE_INTEGER length; bool status; AuMemoryViewWrite writeView; size_t offset; status = false; offset = 0; win32Path = Locale::ConvertFromUTF8(NormalizePathRet(path)); if (win32Path.empty()) { return false; } auto fileHandle = CreateFileW(win32Path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (fileHandle == INVALID_HANDLE_VALUE) { SysPushErrorIO("Couldn't open handle: {}", path); return false; } if (!GetFileSizeEx(fileHandle, &length)) { SysPushErrorIO(); goto out; } writeView = buffer.GetOrAllocateLinearWriteable(length.QuadPart); if (!writeView) { SysPushErrorMem(); goto out; } while (length.QuadPart) { DWORD read; int blockSize = AuMin(static_cast(kFileCopyBlock), static_cast(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 { DWORD dwAttrib = GetFileAttributesW(Locale::ConvertFromUTF8(NormalizePathRet(path)).c_str()); return ((dwAttrib != INVALID_FILE_ATTRIBUTES) && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); } catch (...) { return false; } } AUKN_SYM bool DirExists(const AuString &path) { try { DWORD dwAttrib = GetFileAttributesW(Locale::ConvertFromUTF8(NormalizePathRet(path)).c_str()); return ((dwAttrib != INVALID_FILE_ATTRIBUTES) && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); } catch (...) { return false; } } AUKN_SYM bool DirMk(const AuString &path) { try { return CreateDirectories(NormalizePathRet(path), false); } catch (...) { return false; } } AUKN_SYM bool Remove(const AuString &path) { try { auto translatedPath = Locale::ConvertFromUTF8(NormalizePathRet(path)); if (::DeleteFileW(translatedPath.c_str())) { return true; } if (::RemoveDirectoryW(translatedPath.c_str())) { return true; } return false; } catch (...) { return false; } } AUKN_SYM bool Relink(const AuString &src, const AuString &dest) { try { auto srcPathNormalized = NormalizePathRet(src); auto destPathNormalized = NormalizePathRet(dest); CreateDirectories(destPathNormalized, true); return MoveFileW(Locale::ConvertFromUTF8(srcPathNormalized).c_str(), Locale::ConvertFromUTF8(destPathNormalized).c_str()); } catch (...) { return false; } } AUKN_SYM bool Copy(const AuString &src, const AuString &dest) { try { auto srcPathNormalized = NormalizePathRet(src); auto destPathNormalized = NormalizePathRet(dest); CreateDirectories(destPathNormalized, true); return CopyFileW(Locale::ConvertFromUTF8(srcPathNormalized).c_str(), Locale::ConvertFromUTF8(destPathNormalized).c_str(), true); } catch (...) { return false; } } AUKN_SYM bool StatFile(const AuString &path, Stat &stat) { WIN32_FILE_ATTRIBUTE_DATA data; stat = {}; if (!GetFileAttributesExW(Locale::ConvertFromUTF8(NormalizePathRet(path)).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.created = Time::ConvertTimestamp(data.ftCreationTime); stat.modified = Time::ConvertTimestamp(data.ftLastWriteTime); stat.accessed = Time::ConvertTimestamp(data.ftLastAccessTime); return true; } } #endif