diff --git a/Utilities/File.h b/Utilities/File.h index cd635c72b0..8aef67986c 100644 --- a/Utilities/File.h +++ b/Utilities/File.h @@ -628,7 +628,8 @@ namespace fs template bool write_file(const std::string& path, bs_t mode, const Args&... args) { - if (fs::file f{path, mode}) + // Always use write flag, remove read flag + if (fs::file f{path, mode + fs::write - fs::read}) { // Write args sequentially (f.write(args), ...); diff --git a/rpcs3/Emu/VFS.cpp b/rpcs3/Emu/VFS.cpp index 3d4adaef44..cb08d6d0c9 100644 --- a/rpcs3/Emu/VFS.cpp +++ b/rpcs3/Emu/VFS.cpp @@ -701,6 +701,11 @@ std::string vfs::unescape(std::string_view name) return result; } +std::string vfs::host::hash_path(const std::string& path, const std::string& dev_root) +{ + return fmt::format(u8"%s/$%s%s", dev_root, fmt::base57(std::hash()(path)), fmt::base57(__rdtsc())); +} + bool vfs::host::rename(const std::string& from, const std::string& to, bool overwrite) { while (!fs::rename(from, to, overwrite)) @@ -725,7 +730,7 @@ bool vfs::host::unlink(const std::string& path, const std::string& dev_root) else { // Rename to special dummy name which will be ignored by VFS (but opened file handles can still read or write it) - const std::string dummy = fmt::format(u8"%s/$%s%s", dev_root, fmt::base57(std::hash()(path)), fmt::base57(__rdtsc())); + const std::string dummy = hash_path(path, dev_root); if (!fs::rename(path, dummy, true)) { @@ -755,7 +760,7 @@ bool vfs::host::remove_all(const std::string& path, const std::string& dev_root, if (remove_root) { // Rename to special dummy folder which will be ignored by VFS (but opened file handles can still read or write it) - const std::string dummy = fmt::format(u8"%s/$%s%s", dev_root, fmt::base57(std::hash()(path)), fmt::base57(__rdtsc())); + const std::string dummy = hash_path(path, dev_root); if (!vfs::host::rename(path, dummy, false)) { diff --git a/rpcs3/Emu/VFS.h b/rpcs3/Emu/VFS.h index 85b4011f28..7e9bc793ac 100644 --- a/rpcs3/Emu/VFS.h +++ b/rpcs3/Emu/VFS.h @@ -21,6 +21,8 @@ namespace vfs // Functions in this namespace operate on host filepaths, similar to fs:: namespace host { + std::string hash_path(const std::string& path, const std::string& dev_root); + // Call fs::rename with retry on access error bool rename(const std::string& from, const std::string& to, bool overwrite); diff --git a/rpcs3/Loader/TRP.cpp b/rpcs3/Loader/TRP.cpp index 02dc8c2302..be5cce139f 100644 --- a/rpcs3/Loader/TRP.cpp +++ b/rpcs3/Loader/TRP.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "Emu/VFS.h" +#include "Emu/System.h" #include "TRP.h" #include "Crypto/sha1.h" #include "Utilities/StrUtil.h" @@ -15,27 +16,58 @@ bool TRPLoader::Install(const std::string& dest, bool show) { if (!trp_f) { + fs::g_tls_error = fs::error::noent; return false; } + fs::g_tls_error = {}; + const std::string& local_path = vfs::get(dest); - if (!fs::is_dir(local_path) && !fs::create_dir(local_path)) + const auto temp = vfs::host::hash_path(local_path, Emu.GetHddDir()) + '/'; + + if (!fs::create_dir(temp)) { return false; } std::vector buffer(65536); + bool success = true; for (const TRPEntry& entry : m_entries) { trp_f.seek(entry.offset); buffer.resize(entry.size); if (!trp_f.read(buffer)) continue; // ??? - fs::file(local_path + '/' + entry.name, fs::rewrite).write(buffer); + + // Create the file in the temporary directory + success = fs::write_file(temp + vfs::escape(entry.name), fs::create + fs::excl, buffer); + if (!success) + { + break; + } } - return true; + if (success) + { + success = vfs::host::remove_all(local_path, Emu.GetHddDir(), true); + + if (success) + { + // Atomically create trophy data (overwrite existing data) + success = fs::rename(temp, local_path, false); + } + } + + if (!success) + { + // Remove temporary directory manually on failure (removed automatically on success) + auto old_error = fs::g_tls_error; + fs::remove_all(temp); + fs::g_tls_error = old_error; + } + + return success; } bool TRPLoader::LoadHeader(bool show)