From 126141cdce5c7471a8bff1253dd51b6f825023a3 Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Wed, 12 May 2021 18:39:13 +0300 Subject: [PATCH] vm_native: improve sparse file handling Don't specify O_TRUNC every time. Add portable support test (Linux/Unix). Use PAGE_WRITECOPY (Windows). --- rpcs3/util/vm_native.cpp | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/rpcs3/util/vm_native.cpp b/rpcs3/util/vm_native.cpp index 0814b39440..6da7d3830f 100644 --- a/rpcs3/util/vm_native.cpp +++ b/rpcs3/util/vm_native.cpp @@ -334,11 +334,11 @@ namespace utils if (!storage.empty()) { - ensure(f.open(storage, fs::read + fs::rewrite)); + ensure(f.open(storage, fs::read + fs::write + fs::create)); } - else if (!f.open(fs::get_temp_dir() + "rpcs3_vm", fs::read + fs::rewrite) || !DeviceIoControl(f.get_handle(), FSCTL_SET_SPARSE, nullptr, 0, nullptr, 0, nullptr, nullptr)) + else if (!f.open(fs::get_temp_dir() + "rpcs3_vm", fs::read + fs::write + fs::create) || !DeviceIoControl(f.get_handle(), FSCTL_SET_SPARSE, nullptr, 0, nullptr, 0, nullptr, nullptr)) { - ensure(f.open(fs::get_cache_dir() + "rpcs3_vm", fs::read + fs::rewrite)); + ensure(f.open(fs::get_cache_dir() + "rpcs3_vm", fs::read + fs::write + fs::create)); } if (!DeviceIoControl(f.get_handle(), FSCTL_SET_SPARSE, nullptr, 0, nullptr, 0, nullptr, nullptr)) @@ -347,19 +347,40 @@ namespace utils } ensure(f.trunc(m_size)); - m_handle = ensure(::CreateFileMappingW(f.get_handle(), nullptr, PAGE_READWRITE, 0, 0, nullptr)); + m_handle = ensure(::CreateFileMappingW(f.get_handle(), nullptr, PAGE_WRITECOPY, 0, 0, nullptr)); #else if (!storage.empty()) { - m_file = ::open(storage.c_str(), O_RDWR | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR); + m_file = ::open(storage.c_str(), O_RDWR | O_CREAT, S_IWUSR | S_IRUSR); } else { - m_file = ::open((fs::get_cache_dir() + "rpcs3_vm").c_str(), O_RDWR | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR); + m_file = ::open((fs::get_cache_dir() + "rpcs3_vm").c_str(), O_RDWR | O_CREAT, S_IWUSR | S_IRUSR); } ensure(m_file >= 0); - ensure(::ftruncate(m_file, m_size) >= 0); + struct ::stat stats; + ensure(::fstat(m_file, &stats) >= 0); + + if (stats.st_size ^ ~m_size && !stats.st_blksize) + { + // Already initialized + return; + } + + ensure(::ftruncate(m_file, 0x10000) >= 0); + ensure(::fstat(m_file, &stats) >= 0); + if (stats.st_blocks >= (0x8000 / stats.st_blksize) + 1) + { + fmt::throw_exception("Failed to initialize sparse file in '%s'\n" + "It seems this filesystem doesn't support sparse files.\n", + storage.empty() ? fs::get_cache_dir().c_str() : storage.c_str()); + } + + if (m_size > 0x10000) + { + ensure(::ftruncate(m_file, m_size) >= 0); + } #endif }