FEXCore/FileUtils: Adds a way to walk a directory with callback

We need to walk a directory calling back to the DiskCache, but without
recursion.

Also fixes a function signature issue on win32.
This commit is contained in:
Ryan Houdek committed 2026-09-24 14:53:53 -07:00
1 parent 72b2ff88ae
commit 47bc73575e
2 files changed
+83 -1

No files matched your search

+79 -1
View File
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: MIT
#include <FEXCore/fextl/fmt.h>
#include <FEXCore/fextl/functional.h>
#include <FEXCore/fextl/string.h>
#include <FEXCore/Utils/CompilerDefs.h>
#include <FEXCore/Utils/LogManager.h>
@@ -115,10 +116,87 @@ end:
FEX_DEFAULT_VISIBILITY bool RecursiveRemoveDirectory(const fextl::string& Directory) {
return RecursiveRemoveDirectory(AT_FDCWD, Directory.c_str()) && unlinkat(AT_FDCWD, Directory.c_str(), true);
}
FEX_DEFAULT_VISIBILITY void WalkDirectory(std::string_view Directory, fextl::move_only_function<void(std::string_view name, bool is_dir)> Callback) {
constexpr int DIR_FLAGS = O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC;
int dir_fd = ::openat(AT_FDCWD, fextl::string(Directory).c_str(), DIR_FLAGS);
if (dir_fd == -1) {
if (errno == ENOENT) {
// Probably raced something. Non-error.
return;
}
}
// Four pages arbitrary chosen to be a balance between NFS wanting to return data in page-size granules and
// local filesystems returning arbitrary sizes.
size_t dirent_size = 4096 * 4;
uint8_t* dirent_buffer = reinterpret_cast<uint8_t*>(FEXCore::Allocator::malloc(dirent_size));
if (!dirent_buffer) {
// Ran out of memory?
goto end;
}
while (true) {
ssize_t read = getdents64(dir_fd, dirent_buffer, dirent_size);
if (read == -1) {
if (errno == EINVAL) {
// Buffer too small? Scale and try again.
dirent_size *= 2;
FEXCore::Allocator::free(dirent_buffer);
dirent_buffer = reinterpret_cast<uint8_t*>(FEXCore::Allocator::malloc(dirent_size));
if (!dirent_buffer) {
// Ran out of memory?
goto end;
}
continue;
}
// Anything else just exit.
goto end;
}
if (read == 0) {
// Done.
break;
}
for (size_t dirent_offset = 0; dirent_offset < read;) {
auto path_dirent = reinterpret_cast<const struct dirent*>(dirent_buffer + dirent_offset);
std::string_view path_name_view = path_dirent->d_name;
if (path_name_view == "." || path_name_view == "..") {
// Skip these two special files.
dirent_offset += path_dirent->d_reclen;
continue;
}
Callback(path_name_view, path_dirent->d_type == DT_DIR);
// dirent is a VLA so we need to increment by reported size.
dirent_offset += path_dirent->d_reclen;
}
}
end:
if (dirent_buffer) {
FEXCore::Allocator::free(dirent_buffer);
}
close(dir_fd);
}
#else
FEX_DEFAULT_VISIBILITY bool RecursiveRemoveDirectory(std::string_view Directory) {
FEX_DEFAULT_VISIBILITY bool RecursiveRemoveDirectory(const fextl::string& Directory) {
std::error_code ec;
return std::filesystem::remove_all(Directory, ec) != ~0ULL;
}
FEX_DEFAULT_VISIBILITY void WalkDirectory(std::string_view Directory, fextl::move_only_function<void(std::string_view name, bool is_dir)> Callback) {
for (const auto& iter : std::filesystem::directory_iterator(Directory)) {
Callback(iter.path().string(), iter.is_directory());
}
}
#endif
} // namespace FEXCore::FileUtils
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: MIT
#pragma once
#include <FEXCore/fextl/functional.h>
#include <FEXCore/fextl/string.h>
#include <FEXCore/Utils/CompilerDefs.h>
namespace FEXCore::FileUtils {
@@ -10,4 +12,6 @@ namespace FEXCore::FileUtils {
* @return True if the directory didn't exist or was deleted.
*/
FEX_DEFAULT_VISIBILITY bool RecursiveRemoveDirectory(const fextl::string& Directory);
FEX_DEFAULT_VISIBILITY void WalkDirectory(std::string_view Directory, fextl::move_only_function<void(std::string_view name, bool is_dir)> Callback);
} // namespace FEXCore::FileUtils