Files
UAQ Taxi a968336825 fix(libc): remove Windows trees without the remove_all symlink hang
MinGW libstdc++ up to GCC 16 does not treat a reparse point as a link in
std::filesystem::remove_all, so a host tree that holds a symlink is walked
through the link and never returns. Add RemoveTree, which removes the tree
itself on Windows, and use it where save data, app content and the shader
disk cache removed host trees. The libstdc++ fix is in GCC 17 (88856ad855ae).
2026-10-19 14:49:49 +02:00

63 lines
2.1 KiB
C++

#ifndef CORE_LIBS_PRX_LIBC_INCLUDE_REMOVETREE_HPP
#define CORE_LIBS_PRX_LIBC_INCLUDE_REMOVETREE_HPP
#include <filesystem>
#include <string>
#include <system_error>
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#endif
inline void RemoveTree(const std::filesystem::path& target, std::error_code& error) {
error.clear();
#ifdef _WIN32
const std::wstring wide = target.wstring();
const DWORD attributes = GetFileAttributesW(wide.c_str());
if (attributes == INVALID_FILE_ATTRIBUTES) {
const DWORD missing = GetLastError();
if (missing != ERROR_FILE_NOT_FOUND && missing != ERROR_PATH_NOT_FOUND)
error = std::error_code(static_cast<int>(missing), std::system_category());
return;
}
const bool directory = (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
if (directory && (attributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) {
std::error_code listing;
{
std::filesystem::directory_iterator entries(target, listing);
while (!listing && entries != std::filesystem::directory_iterator()) {
RemoveTree(entries->path(), error);
if (error) return;
entries.increment(listing);
}
}
if (listing) {
error = listing;
return;
}
}
if (directory ? RemoveDirectoryW(wide.c_str()) != 0 : DeleteFileW(wide.c_str()) != 0) return;
const DWORD failed = GetLastError();
SetFileAttributesW(wide.c_str(), FILE_ATTRIBUTE_NORMAL);
if (directory ? RemoveDirectoryW(wide.c_str()) != 0 : DeleteFileW(wide.c_str()) != 0) return;
error = std::error_code(static_cast<int>(failed), std::system_category());
#else
std::error_code removal;
std::filesystem::remove_all(target, removal);
if (removal) error = removal;
#endif
}
inline void RemoveTree(const std::filesystem::path& target) {
std::error_code error;
RemoveTree(target, error);
if (error) throw std::filesystem::filesystem_error("RemoveTree", target, error);
}
#endif