Merge pull request #11596 from AdmiralCurtiss/copyany

Common/FileUtil: Migrate CopyDir() to a more clear interface.
This commit is contained in:
Admiral H. Curtiss 2023-02-27 12:02:00 +01:00 committed by GitHub
commit c730ee2de2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 114 additions and 26 deletions

View File

@ -193,7 +193,6 @@ bool Delete(const std::string& filename, IfAbsentBehavior behavior)
return true; return true;
} }
// Returns true if successful, or path already exists.
bool CreateDir(const std::string& path) bool CreateDir(const std::string& path)
{ {
DEBUG_LOG_FMT(COMMON, "{}: directory {}", __func__, path); DEBUG_LOG_FMT(COMMON, "{}: directory {}", __func__, path);
@ -202,7 +201,24 @@ bool CreateDir(const std::string& path)
auto native_path = StringToPath(path); auto native_path = StringToPath(path);
bool success = fs::create_directory(native_path, error); bool success = fs::create_directory(native_path, error);
// If the path was not created, check if it was a pre-existing directory // If the path was not created, check if it was a pre-existing directory
if (!success && fs::is_directory(native_path)) std::error_code error_ignored;
if (!success && fs::is_directory(native_path, error_ignored))
success = true;
if (!success)
ERROR_LOG_FMT(COMMON, "{}: failed on {}: {}", __func__, path, error.message());
return success;
}
bool CreateDirs(std::string_view path)
{
DEBUG_LOG_FMT(COMMON, "{}: directory {}", __func__, path);
std::error_code error;
auto native_path = StringToPath(path);
bool success = fs::create_directories(native_path, error);
// If the path was not created, check if it was a pre-existing directory
std::error_code error_ignored;
if (!success && fs::is_directory(native_path, error_ignored))
success = true; success = true;
if (!success) if (!success)
ERROR_LOG_FMT(COMMON, "{}: failed on {}: {}", __func__, path, error.message()); ERROR_LOG_FMT(COMMON, "{}: failed on {}: {}", __func__, path, error.message());
@ -217,7 +233,8 @@ bool CreateFullPath(std::string_view fullPath)
auto native_path = StringToPath(fullPath).parent_path(); auto native_path = StringToPath(fullPath).parent_path();
bool success = fs::create_directories(native_path, error); bool success = fs::create_directories(native_path, error);
// If the path was not created, check if it was a pre-existing directory // If the path was not created, check if it was a pre-existing directory
if (!success && fs::is_directory(native_path)) std::error_code error_ignored;
if (!success && fs::is_directory(native_path, error_ignored))
success = true; success = true;
if (!success) if (!success)
ERROR_LOG_FMT(COMMON, "{}: failed on {}: {}", __func__, fullPath, error.message()); ERROR_LOG_FMT(COMMON, "{}: failed on {}: {}", __func__, fullPath, error.message());
@ -516,27 +533,81 @@ bool DeleteDirRecursively(const std::string& directory)
return success; return success;
} }
// Create directory and copy contents (optionally overwrites existing files) bool Copy(std::string_view source_path, std::string_view dest_path, bool overwrite_existing)
bool CopyDir(const std::string& source_path, const std::string& dest_path, const bool destructive)
{ {
DEBUG_LOG_FMT(COMMON, "{}: {} --> {} ({})", __func__, source_path, dest_path,
overwrite_existing ? "overwrite" : "preserve");
auto src_path = StringToPath(source_path); auto src_path = StringToPath(source_path);
auto dst_path = StringToPath(dest_path); auto dst_path = StringToPath(dest_path);
if (fs::equivalent(src_path, dst_path)) std::error_code error;
auto options = fs::copy_options::recursive;
if (overwrite_existing)
options |= fs::copy_options::overwrite_existing;
fs::copy(src_path, dst_path, options, error);
if (error)
{
std::error_code error_ignored;
if (fs::equivalent(src_path, dst_path, error_ignored))
return true;
ERROR_LOG_FMT(COMMON, "{}: failed {} --> {} ({}): {}", __func__, source_path, dest_path,
overwrite_existing ? "overwrite" : "preserve", error.message());
return false;
}
return true;
}
static bool MoveWithOverwrite(const std::filesystem::path& src, const std::filesystem::path& dst,
std::error_code& error)
{
fs::rename(src, dst, error);
if (!error)
return true; return true;
DEBUG_LOG_FMT(COMMON, "{}: {} --> {}", __func__, source_path, dest_path); // rename failed, try fallbacks
auto options = fs::copy_options::recursive; if (!fs::is_directory(src))
if (destructive) {
options |= fs::copy_options::overwrite_existing; // src is not a directory (ie, probably a file), try to copy file + delete
if (!fs::copy_file(src, dst, fs::copy_options::overwrite_existing, error))
return false;
if (!fs::remove(src, error))
return false;
return true;
}
// src is a directory, recurse into it and try to move all sub-elements one by one
// this usually happens because the target is a non-empty directory
for (fs::directory_iterator it(src, error); it != fs::directory_iterator(); it.increment(error))
{
if (error)
return false;
if (!MoveWithOverwrite(it->path(), dst / it->path().filename(), error))
return false;
}
if (error)
return false;
// all sub-elements moved, remove top directory
if (!fs::remove(src, error))
return false;
return true;
}
bool MoveWithOverwrite(std::string_view source_path, std::string_view dest_path)
{
DEBUG_LOG_FMT(COMMON, "{}: {} --> {}", __func__, source_path, dest_path);
auto src_path = StringToPath(source_path);
auto dst_path = StringToPath(dest_path);
std::error_code error; std::error_code error;
bool copied = fs::copy_file(src_path, dst_path, options, error); if (!MoveWithOverwrite(src_path, dst_path, error))
if (!copied)
{ {
ERROR_LOG_FMT(COMMON, "{}: failed {} --> {}: {}", __func__, source_path, dest_path, ERROR_LOG_FMT(COMMON, "{}: failed {} --> {}: {}", __func__, source_path, dest_path,
error.message()); error.message());
} }
return copied; return true;
} }
// Returns the current directory // Returns the current directory

View File

@ -140,9 +140,12 @@ u64 GetSize(const std::string& path);
// Overloaded GetSize, accepts FILE* // Overloaded GetSize, accepts FILE*
u64 GetSize(FILE* f); u64 GetSize(FILE* f);
// Returns true if successful, or path already exists. // Creates a single directory. Returns true if successful or if the path already exists.
bool CreateDir(const std::string& filename); bool CreateDir(const std::string& filename);
// Creates directories recursively. Returns true if successful or if the path already exists.
bool CreateDirs(std::string_view filename);
// Creates the full path to the file given in fullPath. // Creates the full path to the file given in fullPath.
// That is, for path '/a/b/c.bin', creates folders '/a' and '/a/b'. // That is, for path '/a/b/c.bin', creates folders '/a' and '/a/b'.
// Returns true if creation is successful or if the path already exists. // Returns true if creation is successful or if the path already exists.
@ -185,9 +188,17 @@ bool DeleteDirRecursively(const std::string& directory);
// Returns the current directory // Returns the current directory
std::string GetCurrentDir(); std::string GetCurrentDir();
// Create directory and copy contents (optionally overwrites existing files) // Copies source_path to dest_path, as if by std::filesystem::copy(). Returns true on success or if
bool CopyDir(const std::string& source_path, const std::string& dest_path, // the source and destination are already the same (as determined by std::filesystem::equivalent()).
bool destructive = false); bool Copy(std::string_view source_path, std::string_view dest_path,
bool overwrite_existing = false);
// Moves source_path to dest_path. On success, the source_path will no longer exist, and the
// dest_path will contain the data previously in source_path. Files in dest_path will be overwritten
// if they match files in source_path, but files that only exist in dest_path will be kept. No
// guarantee on the state is given on failure; the move may have completely failed or partially
// completed.
bool MoveWithOverwrite(std::string_view source_path, std::string_view dest_path);
// Set the current directory to given directory // Set the current directory to given directory
bool SetCurrentDir(const std::string& directory); bool SetCurrentDir(const std::string& directory);

View File

@ -121,7 +121,7 @@ void WFSIDevice::FinalizePatchInstall()
const std::string current_title_dir = fmt::format("/vol/{}/title/{}/{}", m_device_name, const std::string current_title_dir = fmt::format("/vol/{}/title/{}/{}", m_device_name,
m_current_group_id_str, m_current_title_id_str); m_current_group_id_str, m_current_title_id_str);
const std::string patch_dir = current_title_dir + "/_patch"; const std::string patch_dir = current_title_dir + "/_patch";
File::CopyDir(WFS::NativePath(patch_dir), WFS::NativePath(current_title_dir), true); File::MoveWithOverwrite(WFS::NativePath(patch_dir), WFS::NativePath(current_title_dir));
} }
std::optional<IPCReply> WFSIDevice::IOCtl(const IOCtlRequest& request) std::optional<IPCReply> WFSIDevice::IOCtl(const IOCtlRequest& request)

View File

@ -186,7 +186,10 @@ static void InitializeDeterministicWiiSaves(FS::FileSystem* session_fs,
const auto& netplay_redirect_folder = boot_session_data.GetWiiSyncRedirectFolder(); const auto& netplay_redirect_folder = boot_session_data.GetWiiSyncRedirectFolder();
if (!netplay_redirect_folder.empty()) if (!netplay_redirect_folder.empty())
File::CopyDir(netplay_redirect_folder, s_temp_redirect_root + "/"); {
File::CreateDirs(s_temp_redirect_root);
File::Copy(netplay_redirect_folder, s_temp_redirect_root);
}
} }
} }
@ -194,7 +197,7 @@ static void MoveToBackupIfExists(const std::string& path)
{ {
if (File::Exists(path)) if (File::Exists(path))
{ {
const std::string backup_path = path.substr(0, path.size() - 1) + ".backup" DIR_SEP; const std::string backup_path = path.substr(0, path.size() - 1) + ".backup";
WARN_LOG_FMT(IOS_FS, "Temporary directory at {} exists, moving to backup...", path); WARN_LOG_FMT(IOS_FS, "Temporary directory at {} exists, moving to backup...", path);
// If backup exists, delete it as we don't want a mess // If backup exists, delete it as we don't want a mess
@ -204,7 +207,7 @@ static void MoveToBackupIfExists(const std::string& path)
File::DeleteDirRecursively(backup_path); File::DeleteDirRecursively(backup_path);
} }
File::CopyDir(path, backup_path, true); File::MoveWithOverwrite(path, backup_path);
} }
} }
@ -359,11 +362,11 @@ void InitializeWiiFileSystemContents(
if (!File::IsDirectory(save_redirect->m_target_path)) if (!File::IsDirectory(save_redirect->m_target_path))
{ {
File::CreateFullPath(save_redirect->m_target_path + "/"); File::CreateDirs(save_redirect->m_target_path);
if (save_redirect->m_clone) if (save_redirect->m_clone)
{ {
File::CopyDir(Common::GetTitleDataPath(title_id, Common::FROM_SESSION_ROOT), File::Copy(Common::GetTitleDataPath(title_id, Common::FROM_SESSION_ROOT),
save_redirect->m_target_path); save_redirect->m_target_path);
} }
} }
s_nand_redirects.emplace_back(IOS::HLE::FS::NandRedirect{ s_nand_redirects.emplace_back(IOS::HLE::FS::NandRedirect{
@ -396,7 +399,10 @@ void CleanUpWiiFileSystemContents(const BootSessionData& boot_session_data)
// copy back the temp nand redirected files to where they should normally be redirected to // copy back the temp nand redirected files to where they should normally be redirected to
for (const auto& redirect : s_temp_nand_redirects) for (const auto& redirect : s_temp_nand_redirects)
File::CopyDir(redirect.temp_path, redirect.real_path + "/", true); {
File::CreateFullPath(redirect.real_path);
File::MoveWithOverwrite(redirect.temp_path, redirect.real_path);
}
IOS::HLE::EmulationKernel* ios = IOS::HLE::GetIOS(); IOS::HLE::EmulationKernel* ios = IOS::HLE::GetIOS();

View File

@ -248,7 +248,7 @@ void AutoUpdateChecker::TriggerUpdate(const AutoUpdateChecker::NewVersionInforma
#ifdef __APPLE__ #ifdef __APPLE__
// Copy the updater so it can update itself if needed. // Copy the updater so it can update itself if needed.
const std::string reloc_updater_path = UpdaterPath(true); const std::string reloc_updater_path = UpdaterPath(true);
if (!File::CopyDir(UpdaterPath(), reloc_updater_path)) if (!File::Copy(UpdaterPath(), reloc_updater_path))
{ {
CriticalAlertFmtT("Unable to create updater copy."); CriticalAlertFmtT("Unable to create updater copy.");
return; return;