diff --git a/Include/Aurora/IO/FS/FS.hpp b/Include/Aurora/IO/FS/FS.hpp index def876a8..88c8d436 100644 --- a/Include/Aurora/IO/FS/FS.hpp +++ b/Include/Aurora/IO/FS/FS.hpp @@ -9,18 +9,24 @@ namespace Aurora::IO::FS { + struct IReadDir; + /** Lists files with respect to a given partial or full path of a directory @param files relative address of an entry + @deprecate use ReadDir */ AUKN_SYM bool FilesInDirectory(const AuString &string, AuList &files); /** Lists directories with respect to a given partial or full path of a directory @param files relative address of an entry + @deprecate use ReadDir */ AUKN_SYM bool DirsInDirectory(const AuString &string, AuList &dirs); + AUKN_SYM AuSPtr ReadDir(const AuString &string); + AUKN_SYM bool WriteFile(const AuString &path, const Memory::MemoryViewRead &blob); AUKN_SYM bool WriteString(const AuString &path, const AuString &str); @@ -55,4 +61,5 @@ namespace Aurora::IO::FS #include "Stat.hpp" #include "IAsyncFileStream.hpp" #include "Async.hpp" -#include "Watcher.hpp" \ No newline at end of file +#include "Watcher.hpp" +#include "IReadDir.hpp" \ No newline at end of file diff --git a/Include/Aurora/IO/FS/IReadDir.hpp b/Include/Aurora/IO/FS/IReadDir.hpp new file mode 100644 index 00000000..bd712c9a --- /dev/null +++ b/Include/Aurora/IO/FS/IReadDir.hpp @@ -0,0 +1,22 @@ +/*** + Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: IReadDir.hpp + Date: 2022-07-22 + Author: Reece +***/ +#pragma once + +namespace Aurora::IO::FS +{ + struct StatEx : Stat + { + AuString path; + AuString fileName; + }; + + struct IReadDir + { + virtual StatEx *Next() = 0; + }; +} \ No newline at end of file diff --git a/Include/Aurora/IO/IIOWaitableItem.hpp b/Include/Aurora/IO/IIOWaitableItem.hpp index 5500fd75..09c781ad 100644 --- a/Include/Aurora/IO/IIOWaitableItem.hpp +++ b/Include/Aurora/IO/IIOWaitableItem.hpp @@ -30,6 +30,4 @@ namespace Aurora::IO AUI_METHOD(bool, ApplyRateLimit, ()) ); - - } \ No newline at end of file diff --git a/Source/IO/FS/FS.NT.cpp b/Source/IO/FS/FS.NT.cpp index c753498e..9cfdea1e 100644 --- a/Source/IO/FS/FS.NT.cpp +++ b/Source/IO/FS/FS.NT.cpp @@ -20,28 +20,150 @@ namespace Aurora::IO::FS return CreateDirectoryW(Locale::ConvertFromUTF8(str).c_str(), NULL); } - AUKN_SYM bool FilesInDirectory(const AuString &string, AuList &files) + struct ReadDirStructure : IReadDir { - if (IterateDirEntriesSTL(string, true, files)) + HANDLE hFind {INVALID_HANDLE_VALUE}; + WIN32_FIND_DATAW ffd; + bool bFirstTick {true}; + bool bDead {false}; + StatEx stat; + AuString sPath; + + ~ReadDirStructure() { - return true; + ::FindClose(this->hFind); } - // TODO: Implement a fast path for NT. std::filesystem is unusably slow + virtual StatEx *Next() override + { + if (!AuExchange(this->bFirstTick, false)) + { + if (::FindNextFileW(this->hFind, &this->ffd) == 0) + { + return {}; + } + } - return false; + if (this->bDead) + { + return {}; + } + + if (this->ffd.cFileName == std::wstring(L".") || + this->ffd.cFileName == std::wstring(L"..")) + { + return Next(); + } + + stat.exists = 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.existsDirectory = this->ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + stat.existsFile = !stat.existsDirectory; + stat.symLink = this->ffd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT; + + stat.size = 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->existsFile) + { + continue; + } + + if (!AuTryInsert(files, stat->fileName)) + { + return false; + } + } + + return true; } AUKN_SYM bool DirsInDirectory(const AuString &string, AuList &dirs) { - if (IterateDirEntriesSTL(string, false, dirs)) + auto itr = ReadDir(string); + if (!itr) { - return true; + return false; } - // TODO: Implement a fast path for NT. std::filesystem is unusably slow + // 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->existsDirectory) + { + continue; + } - return false; + if (!AuTryInsert(dirs, stat->fileName)) + { + return false; + } + } + + return true; } AUKN_SYM bool WriteFile(const AuString &path, const Memory::MemoryViewRead &blob) @@ -212,10 +334,16 @@ namespace Aurora::IO::FS try { auto translatedPath = Locale::ConvertFromUTF8(NormalizePathRet(path)); - if (!DeleteFileW(translatedPath.c_str())) + if (::DeleteFileW(translatedPath.c_str())) { - return RemoveDirectoryW(translatedPath.c_str()); + return true; } + + if (::RemoveDirectoryW(translatedPath.c_str())) + { + return true; + } + return false; } catch (...)