Skip to content

Commit

Permalink
FileSystem: Don't follow symlinks when recursively deleting directories
Browse files Browse the repository at this point in the history
  • Loading branch information
chaoticgd committed Dec 2, 2024
1 parent 0522694 commit 3e34510
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 26 deletions.
97 changes: 72 additions & 25 deletions common/FileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down
7 changes: 6 additions & 1 deletion common/FileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 3e34510

Please sign in to comment.