From dfc0542a9fbf9a23b0b8aa526ff0e8430127719f Mon Sep 17 00:00:00 2001 From: Antonino Di Guardo <64427768+digant73@users.noreply.github.com> Date: Thu, 1 Oct 2026 15:38:26 +0200 Subject: [PATCH] savedata: scoped filesystem sync --- Utilities/File.cpp | 20 +++++++++++++++++++- Utilities/File.h | 4 ++-- rpcs3/Emu/Cell/Modules/cellSaveData.cpp | 9 ++++++++- rpcs3/Emu/VFS.cpp | 14 ++++++++++++++ 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/Utilities/File.cpp b/Utilities/File.cpp index 289135b83a..e8c7ab6e9d 100644 --- a/Utilities/File.cpp +++ b/Utilities/File.cpp @@ -1892,11 +1892,29 @@ bool fs::utime(const std::string& path, s64 atime, s64 mtime) #endif } -void fs::sync() +void fs::sync(const std::string& path) { #ifdef _WIN32 + static_cast(path); fs::g_tls_error = fs::error::unknown; +#elif defined(__linux__) + // Only flush the filesystem containing the path: a global sync() also waits on every other mounted + // filesystem and may block indefinitely on a slow or stalled one (e.g. network or FUSE mounts) + const int fd = ::open(path.c_str(), O_RDONLY | O_CLOEXEC); + + if (fd == -1) + { + fs::g_tls_error = to_error(errno); + return; + } + + const int res = ::syncfs(fd); + const int err = errno; + ::close(fd); + + fs::g_tls_error = res == 0 ? fs::error::ok : to_error(err); #else + static_cast(path); ::sync(); fs::g_tls_error = fs::error::ok; #endif diff --git a/Utilities/File.h b/Utilities/File.h index f60b6bc59a..30a3f0fb0b 100644 --- a/Utilities/File.h +++ b/Utilities/File.h @@ -263,8 +263,8 @@ namespace fs // Set file access/modification time bool utime(const std::string& path, s64 atime, s64 mtime); - // Synchronize filesystems (TODO) - void sync(); + // Synchronize the filesystem containing the given path (only that filesystem where supported) + void sync(const std::string& path); class file final { diff --git a/rpcs3/Emu/Cell/Modules/cellSaveData.cpp b/rpcs3/Emu/Cell/Modules/cellSaveData.cpp index f258653491..34fc70abb0 100644 --- a/rpcs3/Emu/Cell/Modules/cellSaveData.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSaveData.cpp @@ -2189,7 +2189,14 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v // Remove old backup fs::remove_all(old_path); - fs::sync(); + fs::sync(new_path); + +#ifndef _WIN32 + if (fs::g_tls_error != fs::error::ok) + { + cellSaveData.warning("savedata_op(): Failed to sync filesystem of '%s' (%s)", new_path, fs::g_tls_error); + } +#endif // Backup old savedata if (!vfs::host::rename(dir_path, old_path, &g_mp_sys_dev_hdd0, false)) diff --git a/rpcs3/Emu/VFS.cpp b/rpcs3/Emu/VFS.cpp index 8ec7fb3341..f9ee4c96d4 100644 --- a/rpcs3/Emu/VFS.cpp +++ b/rpcs3/Emu/VFS.cpp @@ -1094,6 +1094,10 @@ bool vfs::host::rename(const std::string& from, const std::string& to, const lv2 bool res = false; + const auto retry_start = std::chrono::steady_clock::now(); + auto retry_report = retry_start; + u64 retry_count = 0; + for (;; std::this_thread::yield()) { if (fs::rename(from, to, overwrite)) @@ -1107,6 +1111,16 @@ bool vfs::host::rename(const std::string& from, const std::string& to, const lv2 res = false; break; } + + retry_count++; + + // Report a retry loop which is taking long, it may never end if the error is persistent + if (const auto now = std::chrono::steady_clock::now(); now - retry_report >= std::chrono::seconds(1)) + { + retry_report = now; + vfs_log.warning("vfs::host::rename(): Still retrying after %u attempts and %u ms (from='%s', to='%s', error=%s)", retry_count, + static_cast(std::chrono::duration_cast(now - retry_start).count()), from, to, fs::g_tls_error); + } } const auto fs_error = fs::g_tls_error;