mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 10:00:16 +02:00
Merge pull request #5989 from Sonicadvance1/260
FEXCore/DiskCache: Prune old entries automatically
This commit is contained in:
7 files changed
+375
-28
No files matched your search
@@ -262,6 +262,10 @@ endfunction()
|
||||
# Build FEXCore_Base static library
|
||||
add_library(FEXCore_Base STATIC ${FEXCORE_BASE_SRCS})
|
||||
target_link_libraries(FEXCore_Base PUBLIC ${LIBS})
|
||||
if (MINGW)
|
||||
target_link_libraries(FEXCore_Base PUBLIC ntdll)
|
||||
endif()
|
||||
|
||||
AddDefaultOptionsToTarget(FEXCore_Base)
|
||||
|
||||
if (ENABLE_FEXCORE_PROFILER AND FEXCORE_PROFILER_BACKEND STREQUAL "TRACY")
|
||||
|
||||
@@ -229,6 +229,15 @@
|
||||
"Desc": [
|
||||
"Size limit on the main cache file - index not included. Default 1G"
|
||||
]
|
||||
},
|
||||
"DiskCachePruneStaleEntries": {
|
||||
"Type": "bool",
|
||||
"Default": "true",
|
||||
"AffectsCodeGen": "false",
|
||||
"Desc": [
|
||||
"Allows FEX to prune stale disk cache entries on startup.",
|
||||
"Frees space by removing cache entries associated with old FEX versions."
|
||||
]
|
||||
}
|
||||
},
|
||||
"Emulation": {
|
||||
|
||||
@@ -2,18 +2,22 @@
|
||||
|
||||
#define XXH_STATIC_LINKING_ONLY
|
||||
|
||||
#include "FEXCore/Utils/TypeDefines.h"
|
||||
#include "Interface/Core/Frontend.h"
|
||||
#include "FEXCore/Config/Config.h"
|
||||
#include "FEXCore/fextl/string.h"
|
||||
#include "FEXHeaderUtils/Filesystem.h"
|
||||
#include "FEXCore/Core/DiskCache.h"
|
||||
#include "FEXCore/Core/DiskCacheFileMapper.h"
|
||||
#include "FEXCore/Utils/LogManager.h"
|
||||
#include "Interface/Context/Context.h"
|
||||
#include "FEXCore/HLE/SyscallHandler.h"
|
||||
#include "FEXCore/Utils/File.h"
|
||||
#include "FEXCore/fextl/memory.h"
|
||||
|
||||
#include <FEXCore/Config/Config.h>
|
||||
#include <FEXCore/Core/DiskCache.h>
|
||||
#include <FEXCore/Core/DiskCacheFileMapper.h>
|
||||
#include <FEXCore/fextl/memory.h>
|
||||
#include <FEXCore/fextl/string.h>
|
||||
#include <FEXCore/HLE/SyscallHandler.h>
|
||||
#include <FEXCore/Utils/File.h>
|
||||
#include <FEXCore/Utils/FileUtils.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <FEXCore/Utils/Profiler.h>
|
||||
#include <FEXCore/Utils/TypeDefines.h>
|
||||
#include <FEXHeaderUtils/Filesystem.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <atomic>
|
||||
@@ -356,6 +360,39 @@ namespace DiskCache {
|
||||
FileMapper = Func;
|
||||
}
|
||||
|
||||
static inline void PruneStaleEntries(std::string_view CacheBase, std::string_view MachineBucketHash) {
|
||||
FEXCORE_PROFILE_SCOPED("DiskCache::PruneStaleEntries");
|
||||
FEX_CONFIG_OPT(DiskCachePruneStaleEntries, DISKCACHEPRUNESTALEENTRIES);
|
||||
|
||||
if (!DiskCachePruneStaleEntries) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct SimpleCapture {
|
||||
std::string_view CacheBase, MachineBucketHash;
|
||||
} const SimpleCapture {
|
||||
.CacheBase = CacheBase,
|
||||
.MachineBucketHash = MachineBucketHash,
|
||||
};
|
||||
|
||||
FEXCore::FileUtils::WalkDirectory(
|
||||
CacheBase,
|
||||
[](std::string_view name, bool is_dir, const void* user_data) {
|
||||
if (!is_dir) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto capture = reinterpret_cast<const struct SimpleCapture*>(user_data);
|
||||
|
||||
// Current behaviour is to remove entries that no longer match the MachineBucketHash.
|
||||
// This means that if the Disk Cache version no longer matches, or the FEXCore::HostFeatures differ, then they get removed.
|
||||
if (name != capture->MachineBucketHash) {
|
||||
FEXCore::FileUtils::RecursiveRemoveDirectory(fextl::fmt::format("{}/{}", capture->CacheBase, name));
|
||||
}
|
||||
},
|
||||
&SimpleCapture);
|
||||
}
|
||||
|
||||
void DiskCache::Init(FEXCore::Context::ContextImpl* CTX) {
|
||||
this->CTX = CTX;
|
||||
|
||||
@@ -365,18 +402,24 @@ namespace DiskCache {
|
||||
|
||||
fextl::string SerializedConfig = FEXCore::Config::SerializeForCache();
|
||||
|
||||
const auto HostFeatureHash = CTX->HostFeatures.HashForCaching();
|
||||
|
||||
struct __attribute__((packed)) {
|
||||
uint16_t FormatVersion;
|
||||
uint64_t HostFeaturesHash;
|
||||
} MachineBucketData = {FormatVersion, CTX->HostFeatures.HashForCaching()};
|
||||
} MachineBucketData = {FormatVersion, HostFeatureHash.HostFeaturesHash};
|
||||
|
||||
fextl::vector<uint8_t> BucketBytes(sizeof(MachineBucketData) + sizeof(uint8_t) + SerializedConfig.size());
|
||||
fextl::vector<uint8_t> BucketBytes(sizeof(MachineBucketData) + (sizeof(uint8_t) * 2) + SerializedConfig.size());
|
||||
memcpy(BucketBytes.data(), &MachineBucketData, sizeof(MachineBucketData));
|
||||
|
||||
// 64-bit mode and HostType is in the ProcessBucket hash only instead of the MachineBucketHash
|
||||
// These effect code-gen, but they aren't part of the MachineBucketHash, as it comes from Process state.
|
||||
BucketBytes[sizeof(MachineBucketData)] = CTX->Config.Is64BitMode;
|
||||
memcpy(BucketBytes.data() + sizeof(MachineBucketData) + 1, SerializedConfig.data(), SerializedConfig.size());
|
||||
BucketBytes[sizeof(MachineBucketData) + 1] = FEXCore::ToUnderlying(HostFeatureHash.HostType);
|
||||
memcpy(BucketBytes.data() + sizeof(MachineBucketData) + 2, SerializedConfig.data(), SerializedConfig.size());
|
||||
|
||||
uint64_t MachineBucketHash = XXH3_64bits(BucketBytes.data(), sizeof(MachineBucketData));
|
||||
uint64_t ProcessBucketHash = XXH3_64bits(BucketBytes.data() + sizeof(MachineBucketData), 1 + SerializedConfig.size());
|
||||
uint64_t ProcessBucketHash = XXH3_64bits(BucketBytes.data() + sizeof(MachineBucketData), 2 + SerializedConfig.size());
|
||||
BucketHash.high64 = MachineBucketHash;
|
||||
BucketHash.low64 = ProcessBucketHash;
|
||||
|
||||
@@ -384,9 +427,12 @@ namespace DiskCache {
|
||||
if (BasePath.empty()) {
|
||||
BasePath = FEXCore::Config::GetCacheDirectory() + "DiskCache/";
|
||||
}
|
||||
BasePath += fextl::fmt::format("{:016x}", MachineBucketHash) + "/";
|
||||
|
||||
const auto MachineBucketHashAsString = fextl::fmt::format("{:016x}", MachineBucketHash);
|
||||
PruneStaleEntries(BasePath, MachineBucketHashAsString);
|
||||
|
||||
BasePath += MachineBucketHashAsString + "/";
|
||||
FHU::Filesystem::CreateDirectories(BasePath);
|
||||
// todo could kick off clean up of leftover MachineBucketHash sibling directories here
|
||||
|
||||
if (!MapDiskCacheFiles) {
|
||||
FileMapper = nullptr;
|
||||
|
||||
@@ -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>
|
||||
@@ -8,6 +9,18 @@
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <windows.h>
|
||||
#include <winnt.h>
|
||||
#include <winternl.h>
|
||||
#include <ntstatus.h>
|
||||
|
||||
// FEX today doesn't have Windows include in FEXCore.
|
||||
extern "C" NTSTATUS WINAPI NtQueryDirectoryFile(HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID, PIO_STATUS_BLOCK, PVOID, ULONG,
|
||||
FILE_INFORMATION_CLASS, BOOLEAN, PUNICODE_STRING, BOOLEAN);
|
||||
|
||||
extern "C" NTSTATUS RtlUnicodeToUTF8N(OUT PCHAR UTF8StringDestination, IN ULONG UTF8StringMaxByteCount,
|
||||
OUT PULONG UTF8StringActualByteCount, IN PCWCH UnicodeStringSource, IN ULONG UnicodeStringByteCount);
|
||||
#endif
|
||||
|
||||
namespace FEXCore::FileUtils {
|
||||
@@ -115,10 +128,239 @@ end:
|
||||
FEX_DEFAULT_VISIBILITY bool RecursiveRemoveDirectory(const fextl::string& Directory) {
|
||||
return RecursiveRemoveDirectory(AT_FDCWD, Directory.c_str()) && unlinkat(AT_FDCWD, Directory.c_str(), true);
|
||||
}
|
||||
#else
|
||||
FEX_DEFAULT_VISIBILITY bool RecursiveRemoveDirectory(std::string_view 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, const void* user_data)> Callback,
|
||||
const void* user_data) {
|
||||
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, user_data);
|
||||
|
||||
// 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
|
||||
static inline std::optional<UNICODE_STRING> PathToNTPath(std::string_view Path) {
|
||||
UNICODE_STRING PathW;
|
||||
if (!RtlCreateUnicodeStringFromAsciiz(&PathW, fextl::string(Path).c_str())) {
|
||||
return std::nullopt;
|
||||
}
|
||||
UNICODE_STRING NTPath;
|
||||
bool Success = RtlDosPathNameToNtPathName_U(PathW.Buffer, &NTPath, nullptr, nullptr);
|
||||
RtlFreeUnicodeString(&PathW);
|
||||
if (!Success) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return NTPath;
|
||||
}
|
||||
|
||||
static inline void FreeNTPath(UNICODE_STRING Path) {
|
||||
RtlFreeUnicodeString(&Path);
|
||||
}
|
||||
|
||||
FEX_DEFAULT_VISIBILITY void WalkDirectory(std::string_view Directory,
|
||||
fextl::move_only_function<void(std::string_view name, bool is_dir, const void* user_data)> Callback,
|
||||
const void* user_data) {
|
||||
auto NTPath = PathToNTPath(Directory);
|
||||
if (!NTPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
NTSTATUS Status {};
|
||||
|
||||
HANDLE dir_fd {};
|
||||
OBJECT_ATTRIBUTES attr {};
|
||||
IO_STATUS_BLOCK io {};
|
||||
|
||||
InitializeObjectAttributes(&attr, &*NTPath, OBJ_CASE_INSENSITIVE, nullptr, nullptr);
|
||||
Status = NtOpenFile(&dir_fd, FILE_LIST_DIRECTORY | SYNCHRONIZE, &attr, &io, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
|
||||
|
||||
FreeNTPath(*NTPath);
|
||||
|
||||
if (!NT_SUCCESS(Status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
BOOLEAN FirstQuery = TRUE;
|
||||
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) {
|
||||
Status = NtQueryDirectoryFile(dir_fd,
|
||||
nullptr, // Event
|
||||
nullptr, // ApcRoutine
|
||||
nullptr, // ApcContext
|
||||
&io, dirent_buffer, dirent_size, FileDirectoryInformation,
|
||||
FALSE, // ReturnSingleEntry
|
||||
nullptr, // FileName
|
||||
FirstQuery);
|
||||
FirstQuery = FALSE;
|
||||
|
||||
if (Status == STATUS_NO_MORE_FILES) {
|
||||
// No more files
|
||||
break;
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(Status)) {
|
||||
if (Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_INFO_LENGTH_MISMATCH) {
|
||||
// Buffer too small? Scale and try again.
|
||||
dirent_size *= 2;
|
||||
FEXCore::Allocator::free(dirent_buffer);
|
||||
uint8_t* dirent_buffer = reinterpret_cast<uint8_t*>(FEXCore::Allocator::malloc(dirent_size));
|
||||
|
||||
if (!dirent_buffer) {
|
||||
// Ran out of memory?
|
||||
goto end;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Any other failure, exit loop.
|
||||
break;
|
||||
}
|
||||
|
||||
// Iterate the entries returned.
|
||||
for (size_t dirent_offset = 0;;) {
|
||||
auto Info = reinterpret_cast<FILE_DIRECTORY_INFORMATION*>(dirent_buffer + dirent_offset);
|
||||
|
||||
std::wstring_view EntryName(Info->FileName, Info->FileNameLength / sizeof(wchar_t));
|
||||
|
||||
if (EntryName != L"." && EntryName != L"..") {
|
||||
ULONG utf8_bytes_needed = 0;
|
||||
ULONG actual_utf8_bytes = 0;
|
||||
RtlUnicodeToUTF8N(nullptr, 0, &utf8_bytes_needed, Info->FileName, Info->FileNameLength);
|
||||
char* dynamic_buf = reinterpret_cast<char*>(FEXCore::Allocator::malloc(utf8_bytes_needed + 1));
|
||||
if (dynamic_buf) {
|
||||
RtlUnicodeToUTF8N(dynamic_buf, utf8_bytes_needed + 1, &actual_utf8_bytes, Info->FileName, Info->FileNameLength);
|
||||
std::string_view name_view(dynamic_buf, actual_utf8_bytes);
|
||||
bool is_dir = (Info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
Callback(name_view, is_dir, user_data);
|
||||
FEXCore::Allocator::free(dynamic_buf);
|
||||
}
|
||||
}
|
||||
|
||||
if (Info->NextEntryOffset == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
dirent_offset += Info->NextEntryOffset;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
if (dirent_buffer) {
|
||||
FEXCore::Allocator::free(dirent_buffer);
|
||||
}
|
||||
|
||||
NtClose(dir_fd);
|
||||
}
|
||||
|
||||
FEX_DEFAULT_VISIBILITY bool RecursiveRemoveDirectory(const fextl::string& Directory) {
|
||||
using CallbackType = void (*)(std::string_view name, bool is_dir, const void* user_data);
|
||||
struct UserData {
|
||||
CallbackType RemoveFile {};
|
||||
std::string_view base_path;
|
||||
};
|
||||
|
||||
CallbackType RemoveFile = [](std::string_view name, bool is_dir, const void* user_data) {
|
||||
auto Data = reinterpret_cast<const UserData*>(user_data);
|
||||
|
||||
auto full_path = std::format("{}/{}", Data->base_path, name);
|
||||
if (is_dir) {
|
||||
UserData NewData {
|
||||
.RemoveFile = Data->RemoveFile,
|
||||
.base_path = full_path,
|
||||
};
|
||||
|
||||
WalkDirectory(full_path, Data->RemoveFile, &NewData);
|
||||
}
|
||||
|
||||
if (is_dir) {
|
||||
RemoveDirectoryA(full_path.c_str());
|
||||
} else {
|
||||
DeleteFileA(full_path.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
UserData Data {
|
||||
.RemoveFile = RemoveFile,
|
||||
.base_path = Directory,
|
||||
};
|
||||
|
||||
WalkDirectory(Directory, RemoveFile, &Data);
|
||||
|
||||
return RemoveDirectoryA(Directory.c_str()) != 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
} // namespace FEXCore::FileUtils
|
||||
@@ -32,16 +32,23 @@ struct HostFeatures {
|
||||
return 4 << DCacheLineLog2;
|
||||
}
|
||||
|
||||
struct CacheHash {
|
||||
uint64_t HostFeaturesHash;
|
||||
HostTypeEnum HostType;
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
uint64_t HashForCaching() const {
|
||||
// As long as the number of options is 64-bit or below, we can just return it.
|
||||
// Skip CPUMIDRs as it doesn't affect codegen.
|
||||
static_assert(offsetof(HostFeatures, CPUMIDRs) == 8);
|
||||
uint64_t Result {};
|
||||
memcpy(&Result, this, sizeof(Result));
|
||||
CacheHash HashForCaching() const {
|
||||
static_assert(offsetof(HostFeatures, HostType) == 8);
|
||||
|
||||
CacheHash Result {};
|
||||
memcpy(&Result.HostFeaturesHash, this, sizeof(uint64_t));
|
||||
Result.HostType = HostType;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Affects codegen and is basically machine description.
|
||||
uint32_t DCacheLineLog2 : 4 {};
|
||||
uint32_t SupportsCacheMaintenanceOps : 1 {};
|
||||
uint32_t SupportsAES : 1 {};
|
||||
@@ -78,8 +85,10 @@ struct HostFeatures {
|
||||
uint32_t SupportsFloatExceptions : 1 {};
|
||||
// Flag if this is InstCountCI
|
||||
uint32_t IsInstCountCI : 1 {};
|
||||
HostTypeEnum HostType : 2 {};
|
||||
uint32_t pad : 24 {};
|
||||
uint32_t pad : 26 {};
|
||||
|
||||
// This affects codegen, but it isn't machine state
|
||||
HostTypeEnum HostType {};
|
||||
|
||||
// MIDR information
|
||||
// Also used for determining number of CPU cores for CPUID
|
||||
|
||||
@@ -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,8 @@ 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, const void* user_data)> Callback,
|
||||
const void* user_data);
|
||||
} // namespace FEXCore::FileUtils
|
||||
@@ -45,6 +45,37 @@ FILE_INFORMATION_CLASS FileInfoClassToNT(FILE_INFO_BY_HANDLE_CLASS InformationCl
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DLLEXPORT_FUNC(BOOL, RemoveDirectoryA, (LPCSTR lpFileName)) {
|
||||
ScopedUnicodeString FileName {lpFileName};
|
||||
return RemoveDirectoryW(FileName->Buffer);
|
||||
}
|
||||
|
||||
DLLEXPORT_FUNC(BOOL, RemoveDirectoryW, (LPCWSTR lpFileName)) {
|
||||
UNICODE_STRING PathW;
|
||||
RtlInitUnicodeString(&PathW, lpFileName);
|
||||
|
||||
ScopedUnicodeString NTPath;
|
||||
if (!RtlDosPathNameToNtPathName_U(PathW.Buffer, &*NTPath, nullptr, nullptr)) {
|
||||
SetLastError(ERROR_PATH_NOT_FOUND);
|
||||
return false;
|
||||
}
|
||||
|
||||
OBJECT_ATTRIBUTES ObjAttributes;
|
||||
InitializeObjectAttributes(&ObjAttributes, &*NTPath, OBJ_CASE_INSENSITIVE, nullptr, nullptr);
|
||||
|
||||
HANDLE Handle;
|
||||
IO_STATUS_BLOCK IOSB;
|
||||
|
||||
NTSTATUS Status =
|
||||
NtCreateFile(&Handle, SYNCHRONIZE | DELETE, &ObjAttributes, &IOSB, nullptr, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
FILE_OPEN, FILE_DELETE_ON_CLOSE | FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT, nullptr, 0);
|
||||
if (WinAPIReturn(Status)) {
|
||||
Status = NtClose(Handle);
|
||||
}
|
||||
|
||||
return WinAPIReturn(Status);
|
||||
}
|
||||
|
||||
DLLEXPORT_FUNC(BOOL, DeleteFileA, (LPCSTR lpFileName)) {
|
||||
ScopedUnicodeString FileName {lpFileName};
|
||||
return DeleteFileW(FileName->Buffer);
|
||||
|
||||
Reference in new issue
Block a user