From 3e345109ded8df8d99cd944397c5ce811dddca0f Mon Sep 17 00:00:00 2001 From: chaoticgd <43898262+chaoticgd@users.noreply.github.com> Date: Sun, 1 Dec 2024 23:40:58 +0000 Subject: [PATCH] FileSystem: Don't follow symlinks when recursively deleting directories --- common/FileSystem.cpp | 97 ++++++++++++++++++++++++++++++++----------- common/FileSystem.h | 7 +++- 2 files changed, 78 insertions(+), 26 deletions(-) diff --git a/common/FileSystem.cpp b/common/FileSystem.cpp index 826aee13ba8b9..66cc4f57c208c 100644 --- a/common/FileSystem.cpp +++ b/common/FileSystem.cpp @@ -1225,7 +1225,12 @@ bool FileSystem::RecursiveDeleteDirectory(const char* path) { for (const FILESYSTEM_FIND_DATA& fd : results) { - if (fd.Attributes & FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY) + if (IsSymbolicLink(fd.FileName.c_str())) + { + if (!DeleteSymbolicLink(fd.FileName.c_str())) + return false; + } + else if ((fd.Attributes & FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY)) { if (!RecursiveDeleteDirectory(fd.FileName.c_str())) return false; @@ -1650,21 +1655,6 @@ bool FileSystem::DirectoryExists(const char* path) return false; } -bool FileSystem::IsRealDirectory(const char* path) -{ - // convert to wide string - const std::wstring wpath = GetWin32Path(path); - if (wpath.empty()) - return false; - - // determine attributes for the path. if it's a directory, things have to be handled differently.. - const DWORD fileAttributes = GetFileAttributesW(wpath.c_str()); - if (fileAttributes == INVALID_FILE_ATTRIBUTES) - return false; - - return ((fileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) != FILE_ATTRIBUTE_DIRECTORY); -} - bool FileSystem::DirectoryIsEmpty(const char* path) { std::wstring wpath = GetWin32Path(path); @@ -1935,6 +1925,52 @@ bool FileSystem::SetPathCompression(const char* path, bool enable) return result; } +bool FileSystem::IsSymbolicLink(const char* path) +{ + // convert to wide string + const std::wstring wpath = GetWin32Path(path); + if (wpath.empty()) + return false; + + // determine attributes for the path + const DWORD fileAttributes = GetFileAttributesW(wpath.c_str()); + if (fileAttributes == INVALID_FILE_ATTRIBUTES) + return false; + + return fileAttributes & FILE_ATTRIBUTE_REPARSE_POINT; +} + +bool FileSystem::DeleteSymbolicLink(const char* path, Error* error) +{ + // convert to wide string + const std::wstring wpath = GetWin32Path(path); + if (wpath.empty()) + { + Error::SetStringView(error, "Invalid path."); + return false; + } + + // delete the symbolic link + if (DirectoryExists(path)) + { + if (!RemoveDirectoryW(wpath.c_str())) + { + Error::SetWin32(error, "RemoveDirectoryW() failed: ", GetLastError()); + return false; + } + } + else + { + if (!DeleteFileW(wpath.c_str())) + { + Error::SetWin32(error, "DeleteFileW() failed: ", GetLastError()); + return false; + } + } + + return true; +} + #else // No 32-bit file offsets breaking stuff please. @@ -2216,15 +2252,6 @@ bool FileSystem::DirectoryExists(const char* path) return false; } -bool FileSystem::IsRealDirectory(const char* path) -{ - struct stat sysStatData; - if (lstat(path, &sysStatData) < 0) - return false; - - return (S_ISDIR(sysStatData.st_mode) && !S_ISLNK(sysStatData.st_mode)); -} - bool FileSystem::DirectoryIsEmpty(const char* path) { DIR* pDir = opendir(path); @@ -2478,6 +2505,26 @@ bool FileSystem::SetPathCompression(const char* path, bool enable) return false; } +bool FileSystem::IsSymbolicLink(const char* path) +{ + struct stat sysStatData; + if (lstat(path, &sysStatData) < 0) + return false; + + return S_ISLNK(sysStatData.st_mode); +} + +bool FileSystem::DeleteSymbolicLink(const char* path, Error* error) +{ + if (unlink(path) != 0) + { + Error::SetErrno(error, "unlink() failed: ", errno); + return false; + } + + return true; +} + FileSystem::POSIXLock::POSIXLock(int fd) { if (lockf(fd, F_LOCK, 0) == 0) diff --git a/common/FileSystem.h b/common/FileSystem.h index a9bdee5a3bff8..e1f73229dbcac 100644 --- a/common/FileSystem.h +++ b/common/FileSystem.h @@ -84,7 +84,6 @@ namespace FileSystem /// Directory exists? bool DirectoryExists(const char* path); - bool IsRealDirectory(const char* path); /// Directory does not contain any files? bool DirectoryIsEmpty(const char* path); @@ -170,6 +169,12 @@ namespace FileSystem /// Does nothing and returns false on non-Windows platforms. bool SetPathCompression(const char* path, bool enable); + /// Checks if a file or directory is a symbolic link. + bool IsSymbolicLink(const char* path); + + /// Deletes a symbolic link (either a file or directory). + bool DeleteSymbolicLink(const char* path, Error* error = nullptr); + #ifdef _WIN32 // Path limit remover, but also converts to a wide string at the same time. bool GetWin32Path(std::wstring* dest, std::string_view str);