FileSystem: Add error reporting to DeleteDirectory()

This commit is contained in:
Stenzek 2024-12-29 15:56:22 +10:00
parent 5c3abb490d
commit 0e6ade067c
No known key found for this signature in database
3 changed files with 103 additions and 42 deletions

View File

@ -1611,27 +1611,32 @@ bool FileSystem::EnsureDirectoryExists(const char* path, bool recursive, Error*
return FileSystem::CreateDirectory(path, recursive, error); return FileSystem::CreateDirectory(path, recursive, error);
} }
bool FileSystem::RecursiveDeleteDirectory(const char* path) bool FileSystem::RecursiveDeleteDirectory(const char* path, Error* error)
{ {
FindResultsArray results; FindResultsArray results;
if (FindFiles(path, "*", FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_FOLDERS | FILESYSTEM_FIND_HIDDEN_FILES, &results)) if (FindFiles(path, "*", FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_FOLDERS | FILESYSTEM_FIND_HIDDEN_FILES, &results))
{ {
for (const FILESYSTEM_FIND_DATA& fd : results) for (const FILESYSTEM_FIND_DATA& fd : results)
{ {
if (fd.Attributes & FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY) // don't recurse into symlinked directories, just remove the link itself
if ((fd.Attributes & (FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY | FILESYSTEM_FILE_ATTRIBUTE_LINK)) ==
FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY)
{ {
if (!RecursiveDeleteDirectory(fd.FileName.c_str())) if (!RecursiveDeleteDirectory(fd.FileName.c_str(), error))
return false; return false;
} }
else else
{ {
if (!DeleteFile(fd.FileName.c_str())) if (!DeleteFile(fd.FileName.c_str(), error))
{
Error::AddPrefixFmt(error, "Failed to delete {}: ", fd.FileName);
return false; return false;
}
} }
} }
} }
return DeleteDirectory(path); return DeleteDirectory(path, error);
} }
bool FileSystem::CopyFilePath(const char* source, const char* destination, bool replace) bool FileSystem::CopyFilePath(const char* source, const char* destination, bool replace)
@ -2156,17 +2161,32 @@ bool FileSystem::CreateDirectory(const char* Path, bool Recursive, Error* error)
bool FileSystem::DeleteFile(const char* path, Error* error) bool FileSystem::DeleteFile(const char* path, Error* error)
{ {
const std::wstring wpath = GetWin32Path(path); const std::wstring wpath = GetWin32Path(path);
// Need to handle both links/junctions and files as per unix.
const DWORD fileAttributes = GetFileAttributesW(wpath.c_str()); const DWORD fileAttributes = GetFileAttributesW(wpath.c_str());
if (fileAttributes == INVALID_FILE_ATTRIBUTES || fileAttributes & FILE_ATTRIBUTE_DIRECTORY) if (fileAttributes == INVALID_FILE_ATTRIBUTES ||
((fileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT)) == FILE_ATTRIBUTE_DIRECTORY))
{ {
Error::SetStringView(error, "File does not exist."); Error::SetStringView(error, "File does not exist.");
return false; return false;
} }
if (!DeleteFileW(wpath.c_str())) // if it's a junction/symlink, we need to use RemoveDirectory() instead
if (fileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{ {
Error::SetWin32(error, "DeleteFileW() failed: ", GetLastError()); if (!RemoveDirectoryW(wpath.c_str()))
return false; {
Error::SetWin32(error, "RemoveDirectoryW() failed: ", GetLastError());
return false;
}
}
else
{
if (!DeleteFileW(wpath.c_str()))
{
Error::SetWin32(error, "DeleteFileW() failed: ", GetLastError());
return false;
}
} }
return true; return true;
@ -2186,10 +2206,23 @@ bool FileSystem::RenamePath(const char* old_path, const char* new_path, Error* e
return true; return true;
} }
bool FileSystem::DeleteDirectory(const char* path) bool FileSystem::DeleteDirectory(const char* path, Error* error)
{ {
const std::wstring wpath = GetWin32Path(path); const std::wstring wpath = GetWin32Path(path);
return RemoveDirectoryW(wpath.c_str()); const DWORD fileAttributes = GetFileAttributesW(wpath.c_str());
if (fileAttributes == INVALID_FILE_ATTRIBUTES || !(fileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
Error::SetStringView(error, "File does not exist.");
return false;
}
if (!RemoveDirectoryW(wpath.c_str()))
{
Error::SetWin32(error, "RemoveDirectoryW() failed: ", GetLastError());
return false;
}
return true;
} }
std::string FileSystem::GetProgramPath() std::string FileSystem::GetProgramPath()
@ -2273,10 +2306,10 @@ bool FileSystem::SetPathCompression(const char* path, bool enable)
#elif !defined(__ANDROID__) #elif !defined(__ANDROID__)
static u32 TranslateStatAttributes(struct stat& st) static u32 TranslateStatAttributes(struct stat& st, struct stat& st_link)
{ {
return (S_ISDIR(st.st_mode) ? FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY : 0) | return (S_ISDIR(st.st_mode) ? FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY : 0) |
(S_ISLNK(st.st_mode) ? FILESYSTEM_FILE_ATTRIBUTE_LINK : 0); (S_ISLNK(st_link.st_mode) ? FILESYSTEM_FILE_ATTRIBUTE_LINK : 0);
} }
static u32 RecursiveFindFiles(const char* OriginPath, const char* ParentPath, const char* Path, const char* Pattern, static u32 RecursiveFindFiles(const char* OriginPath, const char* ParentPath, const char* Path, const char* Pattern,
@ -2330,12 +2363,12 @@ static u32 RecursiveFindFiles(const char* OriginPath, const char* ParentPath, co
else else
full_path = fmt::format("{}/{}", OriginPath, pDirEnt->d_name); full_path = fmt::format("{}/{}", OriginPath, pDirEnt->d_name);
struct stat sDir; struct stat sDir, sDirLink;
if (stat(full_path.c_str(), &sDir) < 0) if (stat(full_path.c_str(), &sDir) < 0 || lstat(full_path.c_str(), &sDirLink) < 0)
continue; continue;
FILESYSTEM_FIND_DATA outData; FILESYSTEM_FIND_DATA outData;
outData.Attributes = TranslateStatAttributes(sDir); outData.Attributes = TranslateStatAttributes(sDir, sDirLink);
if (S_ISDIR(sDir.st_mode)) if (S_ISDIR(sDir.st_mode))
{ {
@ -2478,18 +2511,18 @@ bool FileSystem::StatFile(std::FILE* fp, struct stat* st, Error* error)
bool FileSystem::StatFile(const char* path, FILESYSTEM_STAT_DATA* sd, Error* error) bool FileSystem::StatFile(const char* path, FILESYSTEM_STAT_DATA* sd, Error* error)
{ {
// stat file // stat file
struct stat sysStatData; struct stat ssd, ssd_link;
if (stat(path, &sysStatData) < 0) if (stat(path, &ssd) < 0 || lstat(path, &ssd_link) < 0)
{ {
Error::SetErrno(error, "stat() failed: ", errno); Error::SetErrno(error, "stat() failed: ", errno);
return false; return false;
} }
// parse attributes // parse attributes
sd->CreationTime = sysStatData.st_ctime; sd->CreationTime = ssd.st_ctime;
sd->ModificationTime = sysStatData.st_mtime; sd->ModificationTime = ssd.st_mtime;
sd->Attributes = TranslateStatAttributes(sysStatData); sd->Attributes = TranslateStatAttributes(ssd, ssd_link);
sd->Size = S_ISREG(sysStatData.st_mode) ? sysStatData.st_size : 0; sd->Size = S_ISREG(ssd.st_mode) ? ssd.st_size : 0;
// ok // ok
return true; return true;
@ -2505,18 +2538,18 @@ bool FileSystem::StatFile(std::FILE* fp, FILESYSTEM_STAT_DATA* sd, Error* error)
} }
// stat file // stat file
struct stat sysStatData; struct stat ssd;
if (fstat(fd, &sysStatData) != 0) if (fstat(fd, &ssd) != 0)
{ {
Error::SetErrno(error, "stat() failed: ", errno); Error::SetErrno(error, "stat() failed: ", errno);
return false; return false;
} }
// parse attributes // parse attributes
sd->CreationTime = sysStatData.st_ctime; sd->CreationTime = ssd.st_ctime;
sd->ModificationTime = sysStatData.st_mtime; sd->ModificationTime = ssd.st_mtime;
sd->Attributes = TranslateStatAttributes(sysStatData); sd->Attributes = TranslateStatAttributes(ssd, ssd);
sd->Size = S_ISREG(sysStatData.st_mode) ? sysStatData.st_size : 0; sd->Size = S_ISREG(ssd.st_mode) ? ssd.st_size : 0;
return true; return true;
} }
@ -2655,8 +2688,8 @@ bool FileSystem::CreateDirectory(const char* path, bool recursive, Error* error)
bool FileSystem::DeleteFile(const char* path, Error* error) bool FileSystem::DeleteFile(const char* path, Error* error)
{ {
struct stat sysStatData; struct stat sd;
if (stat(path, &sysStatData) != 0 || S_ISDIR(sysStatData.st_mode)) if (lstat(path, &sd) != 0 || (S_ISDIR(sd.st_mode) && !S_ISLNK(sd.st_mode)))
{ {
Error::SetStringView(error, "File does not exist."); Error::SetStringView(error, "File does not exist.");
return false; return false;
@ -2675,21 +2708,38 @@ bool FileSystem::RenamePath(const char* old_path, const char* new_path, Error* e
{ {
if (rename(old_path, new_path) != 0) if (rename(old_path, new_path) != 0)
{ {
const int err = errno; Error::SetErrno(error, "rename() failed: ", errno);
Error::SetErrno(error, "rename() failed: ", err);
return false; return false;
} }
return true; return true;
} }
bool FileSystem::DeleteDirectory(const char* path) bool FileSystem::DeleteDirectory(const char* path, Error* error)
{ {
struct stat sysStatData; struct stat sd;
if (stat(path, &sysStatData) != 0 || !S_ISDIR(sysStatData.st_mode)) if (stat(path, &sd) != 0 || !S_ISDIR(sd.st_mode))
return false; return false;
return (rmdir(path) == 0); // if it's a symlink, use unlink() instead
if (S_ISLNK(sd.st_mode))
{
if (unlink(path) != 0)
{
Error::SetErrno(error, "unlink() failed: ", errno);
return false;
}
}
else
{
if (rmdir(path) != 0)
{
Error::SetErrno(error, "rmdir() failed: ", errno);
return false;
}
}
return true;
} }
std::string FileSystem::GetProgramPath() std::string FileSystem::GetProgramPath()

View File

@ -202,10 +202,10 @@ bool CreateDirectory(const char* path, bool recursive, Error* error = nullptr);
bool EnsureDirectoryExists(const char* path, bool recursive, Error* error = nullptr); bool EnsureDirectoryExists(const char* path, bool recursive, Error* error = nullptr);
/// Removes a directory. /// Removes a directory.
bool DeleteDirectory(const char* path); bool DeleteDirectory(const char* path, Error* error = nullptr);
/// Recursively removes a directory and all subdirectories/files. /// Recursively removes a directory and all subdirectories/files.
bool RecursiveDeleteDirectory(const char* path); bool RecursiveDeleteDirectory(const char* path, Error* error = nullptr);
/// Copies one file to another, optionally replacing it if it already exists. /// Copies one file to another, optionally replacing it if it already exists.
bool CopyFilePath(const char* source, const char* destination, bool replace); bool CopyFilePath(const char* source, const char* destination, bool replace);

View File

@ -133,22 +133,27 @@ bool Updater::RecursiveDeleteDirectory(const char* path, bool remove_dir)
return true; return true;
#else #else
Error error;
FileSystem::FindResultsArray results; FileSystem::FindResultsArray results;
if (FileSystem::FindFiles(path, "*", FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_FOLDERS | FILESYSTEM_FIND_HIDDEN_FILES, if (FileSystem::FindFiles(path, "*", FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_FOLDERS | FILESYSTEM_FIND_HIDDEN_FILES,
&results)) &results))
{ {
for (const FILESYSTEM_FIND_DATA& fd : results) for (const FILESYSTEM_FIND_DATA& fd : results)
{ {
if (fd.Attributes & FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY) if ((fd.Attributes & (FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY | FILESYSTEM_FILE_ATTRIBUTE_LINK)) ==
FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY)
{ {
if (!RecursiveDeleteDirectory(fd.FileName.c_str(), true)) if (!RecursiveDeleteDirectory(fd.FileName.c_str(), true))
return false; return false;
} }
else else
{ {
m_progress->FormatInformation("Removing directory '{}'.", fd.FileName); m_progress->FormatInformation("Removing file '{}'.", fd.FileName);
if (!FileSystem::DeleteFile(fd.FileName.c_str())) if (!FileSystem::DeleteFile(fd.FileName.c_str(), &error))
{
m_progress->FormatModalError("DeleteFile({}) failed: {}", fd.FileName, error.GetDescription());
return false; return false;
}
} }
} }
} }
@ -157,7 +162,13 @@ bool Updater::RecursiveDeleteDirectory(const char* path, bool remove_dir)
return true; return true;
m_progress->FormatInformation("Removing directory '{}'.", path); m_progress->FormatInformation("Removing directory '{}'.", path);
return FileSystem::DeleteDirectory(path); if (!FileSystem::DeleteDirectory(path, &error))
{
m_progress->FormatModalError("DeleteDirectory({}) failed: {}", path, error.GetDescription());
return false;
}
return true;
#endif #endif
} }