From 0b6ca158eae90acb9af984feae5a56e529ffca15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 8 Dec 2025 16:42:14 +0800 Subject: [PATCH] Packages (Windows): improves performance for directory enumeration --- src/detection/packages/packages_windows.c | 76 ++++++++++++++++------- src/util/windows/nt.h | 16 +++++ 2 files changed, 68 insertions(+), 24 deletions(-) diff --git a/src/detection/packages/packages_windows.c b/src/detection/packages/packages_windows.c index 019b7629d..ee9abd46f 100644 --- a/src/detection/packages/packages_windows.c +++ b/src/detection/packages/packages_windows.c @@ -2,35 +2,63 @@ #include "common/processing.h" #include "util/stringUtils.h" #include "util/path.h" +#include "util/windows/unicode.h" +#include "util/mallocHelper.h" +#include "common/io/io.h" -#include -#include +#include +#include "util/windows/nt.h" +#include -static uint32_t getNumElements(const char* searchPath /* including `\*` suffix */, DWORD type, const char* ignore) +static uint32_t getNumElements(const char* searchPath, DWORD type, const wchar_t* ignore) { - uint32_t counter = 0; - bool flag = ignore == NULL; - WIN32_FIND_DATAA wfd; - HANDLE hFind = FindFirstFileA(searchPath, &wfd); + FF_AUTO_CLOSE_FD HANDLE dfd = CreateFileA(searchPath, FILE_LIST_DIRECTORY | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (dfd == INVALID_HANDLE_VALUE) return 0; - if (hFind != INVALID_HANDLE_VALUE) - { - do // Managed to locate and create an handle to that folder. + bool flag = ignore == NULL; + uint32_t counter = 0; + uint8_t buffer[64 * 1024] __attribute__((aligned(8))); // Required for WoA + BOOLEAN firstScan = TRUE; + + while (true) { + IO_STATUS_BLOCK ioStatus = {}; + NTSTATUS status = NtQueryDirectoryFile( + dfd, + NULL, NULL, NULL, + &ioStatus, + buffer, ARRAY_SIZE(buffer), + FileDirectoryInformation, + FALSE, + NULL, + firstScan + ); + firstScan = FALSE; + + if (!NT_SUCCESS(status) && status != STATUS_BUFFER_OVERFLOW) break; + + for (FILE_DIRECTORY_INFORMATION* entry = (FILE_DIRECTORY_INFORMATION*) buffer; + ; + entry = (FILE_DIRECTORY_INFORMATION*) ((uint8_t*) entry + entry->NextEntryOffset)) { - if(!(wfd.dwFileAttributes & type)) continue; - if(!flag && ffStrEqualsIgnCase(ignore, wfd.cFileName)) + if (!(entry->FileAttributes & type)) continue; + + if (!flag && _wcsicmp(entry->FileName, ignore) == 0) { flag = true; continue; } - counter++; - } while (FindNextFileA(hFind, &wfd)); - FindClose(hFind); - if(type == FILE_ATTRIBUTE_DIRECTORY && counter >= 2) - counter -= 2; // accounting for . and .. + counter++; + + if (entry->NextEntryOffset == 0) break; + } + + if (status == STATUS_SUCCESS) break; // No next page } + if(type == FILE_ATTRIBUTE_DIRECTORY && counter >= 2) + counter -= 2; // accounting for . and .. + return counter; } @@ -65,8 +93,8 @@ static void detectScoop(FFPackagesResult* result) ffStrbufSet(&scoopPath, &instance.state.platform.homeDir); ffStrbufAppendS(&scoopPath, "/scoop"); } - ffStrbufAppendS(&scoopPath, "/apps/*"); - result->scoopUser = getNumElements(scoopPath.chars, FILE_ATTRIBUTE_DIRECTORY, "scoop"); + ffStrbufAppendS(&scoopPath, "/apps/"); + result->scoopUser = getNumElements(scoopPath.chars, FILE_ATTRIBUTE_DIRECTORY, L"scoop"); } { @@ -78,8 +106,8 @@ static void detectScoop(FFPackagesResult* result) ffStrbufSetS(&scoopPath, getenv("ProgramData")); ffStrbufAppendS(&scoopPath, "/scoop"); } - ffStrbufAppendS(&scoopPath, "/apps/*"); - result->scoopGlobal = getNumElements(scoopPath.chars, FILE_ATTRIBUTE_DIRECTORY, "scoop"); + ffStrbufAppendS(&scoopPath, "/apps/"); + result->scoopGlobal = getNumElements(scoopPath.chars, FILE_ATTRIBUTE_DIRECTORY, L"scoop"); } } @@ -91,8 +119,8 @@ static void detectChoco(FF_MAYBE_UNUSED FFPackagesResult* result) char chocoPath[MAX_PATH + 3]; char* pend = ffStrCopy(chocoPath, chocoInstall, ARRAY_SIZE(chocoPath)); - ffStrCopy(pend, "/lib/*", ARRAY_SIZE(chocoPath) - (size_t) (pend - chocoPath)); - result->choco = getNumElements(chocoPath, FILE_ATTRIBUTE_DIRECTORY, "choco"); + ffStrCopy(pend, "/lib/", ARRAY_SIZE(chocoPath) - (size_t) (pend - chocoPath)); + result->choco = getNumElements(chocoPath, FILE_ATTRIBUTE_DIRECTORY, L"choco"); } static void detectPacman(FFPackagesResult* result) @@ -104,7 +132,7 @@ static void detectPacman(FFPackagesResult* result) // MSYS2 char pacmanPath[MAX_PATH + 3]; char* pend = ffStrCopy(pacmanPath, msystemPrefix, ARRAY_SIZE(pacmanPath)); - ffStrCopy(pend, "/../var/lib/pacman/local/*", ARRAY_SIZE(pacmanPath) - (size_t) (pend - pacmanPath)); + ffStrCopy(pend, "/../var/lib/pacman/local/", ARRAY_SIZE(pacmanPath) - (size_t) (pend - pacmanPath)); result->pacman = getNumElements(pacmanPath, FILE_ATTRIBUTE_DIRECTORY, NULL); } diff --git a/src/util/windows/nt.h b/src/util/windows/nt.h index a33870796..8d6af4b7e 100644 --- a/src/util/windows/nt.h +++ b/src/util/windows/nt.h @@ -232,3 +232,19 @@ typedef struct _D3DKMT_NODEMETADATA static_assert(sizeof(D3DKMT_NODEMETADATA) == 0x4E, "D3DKMT_NODEMETADATA structure size mismatch"); #endif + +NTSYSCALLAPI +NTSTATUS +NTAPI +NtQueryDirectoryFile( + IN HANDLE FileHandle, + IN HANDLE Event OPTIONAL, + IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, + IN PVOID ApcContext OPTIONAL, + OUT PIO_STATUS_BLOCK IoStatusBlock, + OUT PVOID FileInformation, + IN ULONG Length, + IN FILE_INFORMATION_CLASS FileInformationClass, + IN BOOLEAN ReturnSingleEntry, + IN PUNICODE_STRING FileName OPTIONAL, + IN BOOLEAN RestartScan);