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);
}
bool FileSystem::RecursiveDeleteDirectory(const char* path)
bool FileSystem::RecursiveDeleteDirectory(const char* path, Error* error)
{
FindResultsArray results;
if (FindFiles(path, "*", FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_FOLDERS | FILESYSTEM_FIND_HIDDEN_FILES, &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;
}
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 DeleteDirectory(path);
return DeleteDirectory(path, error);
}
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)
{
const std::wstring wpath = GetWin32Path(path);
// Need to handle both links/junctions and files as per unix.
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.");
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());
return false;
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;
@ -2186,10 +2206,23 @@ bool FileSystem::RenamePath(const char* old_path, const char* new_path, Error* e
return true;
}
bool FileSystem::DeleteDirectory(const char* path)
bool FileSystem::DeleteDirectory(const char* path, Error* error)
{
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()
@ -2273,10 +2306,10 @@ bool FileSystem::SetPathCompression(const char* path, bool enable)
#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) |
(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,
@ -2330,12 +2363,12 @@ static u32 RecursiveFindFiles(const char* OriginPath, const char* ParentPath, co
else
full_path = fmt::format("{}/{}", OriginPath, pDirEnt->d_name);
struct stat sDir;
if (stat(full_path.c_str(), &sDir) < 0)
struct stat sDir, sDirLink;
if (stat(full_path.c_str(), &sDir) < 0 || lstat(full_path.c_str(), &sDirLink) < 0)
continue;
FILESYSTEM_FIND_DATA outData;
outData.Attributes = TranslateStatAttributes(sDir);
outData.Attributes = TranslateStatAttributes(sDir, sDirLink);
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)
{
// stat file
struct stat sysStatData;
if (stat(path, &sysStatData) < 0)
struct stat ssd, ssd_link;
if (stat(path, &ssd) < 0 || lstat(path, &ssd_link) < 0)
{
Error::SetErrno(error, "stat() failed: ", errno);
return false;
}
// parse attributes
sd->CreationTime = sysStatData.st_ctime;
sd->ModificationTime = sysStatData.st_mtime;
sd->Attributes = TranslateStatAttributes(sysStatData);
sd->Size = S_ISREG(sysStatData.st_mode) ? sysStatData.st_size : 0;
sd->CreationTime = ssd.st_ctime;
sd->ModificationTime = ssd.st_mtime;
sd->Attributes = TranslateStatAttributes(ssd, ssd_link);
sd->Size = S_ISREG(ssd.st_mode) ? ssd.st_size : 0;
// ok
return true;
@ -2505,18 +2538,18 @@ bool FileSystem::StatFile(std::FILE* fp, FILESYSTEM_STAT_DATA* sd, Error* error)
}
// stat file
struct stat sysStatData;
if (fstat(fd, &sysStatData) != 0)
struct stat ssd;
if (fstat(fd, &ssd) != 0)
{
Error::SetErrno(error, "stat() failed: ", errno);
return false;
}
// parse attributes
sd->CreationTime = sysStatData.st_ctime;
sd->ModificationTime = sysStatData.st_mtime;
sd->Attributes = TranslateStatAttributes(sysStatData);
sd->Size = S_ISREG(sysStatData.st_mode) ? sysStatData.st_size : 0;
sd->CreationTime = ssd.st_ctime;
sd->ModificationTime = ssd.st_mtime;
sd->Attributes = TranslateStatAttributes(ssd, ssd);
sd->Size = S_ISREG(ssd.st_mode) ? ssd.st_size : 0;
return true;
}
@ -2655,8 +2688,8 @@ bool FileSystem::CreateDirectory(const char* path, bool recursive, Error* error)
bool FileSystem::DeleteFile(const char* path, Error* error)
{
struct stat sysStatData;
if (stat(path, &sysStatData) != 0 || S_ISDIR(sysStatData.st_mode))
struct stat sd;
if (lstat(path, &sd) != 0 || (S_ISDIR(sd.st_mode) && !S_ISLNK(sd.st_mode)))
{
Error::SetStringView(error, "File does not exist.");
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)
{
const int err = errno;
Error::SetErrno(error, "rename() failed: ", err);
Error::SetErrno(error, "rename() failed: ", errno);
return false;
}
return true;
}
bool FileSystem::DeleteDirectory(const char* path)
bool FileSystem::DeleteDirectory(const char* path, Error* error)
{
struct stat sysStatData;
if (stat(path, &sysStatData) != 0 || !S_ISDIR(sysStatData.st_mode))
struct stat sd;
if (stat(path, &sd) != 0 || !S_ISDIR(sd.st_mode))
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()

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);
/// Removes a directory.
bool DeleteDirectory(const char* path);
bool DeleteDirectory(const char* path, Error* error = nullptr);
/// 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.
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;
#else
Error error;
FileSystem::FindResultsArray results;
if (FileSystem::FindFiles(path, "*", FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_FOLDERS | FILESYSTEM_FIND_HIDDEN_FILES,
&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))
return false;
}
else
{
m_progress->FormatInformation("Removing directory '{}'.", fd.FileName);
if (!FileSystem::DeleteFile(fd.FileName.c_str()))
m_progress->FormatInformation("Removing file '{}'.", fd.FileName);
if (!FileSystem::DeleteFile(fd.FileName.c_str(), &error))
{
m_progress->FormatModalError("DeleteFile({}) failed: {}", fd.FileName, error.GetDescription());
return false;
}
}
}
}
@ -157,7 +162,13 @@ bool Updater::RecursiveDeleteDirectory(const char* path, bool remove_dir)
return true;
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
}