|
|
|
|
@ -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()
|
|
|
|
|
|