vm_native: improve sparse file implementation (Win32)

Fix sparse attribute set/check, improve logic.
Implement actual check of file data.
Ask to restart RPCS3 first time (Win7 bug).
This commit is contained in:
Nekotekina 2021-05-23 20:50:05 +03:00
parent 2491aad6f2
commit 191cb92300

View file

@ -332,26 +332,49 @@ namespace utils
#ifdef _WIN32 #ifdef _WIN32
fs::file f; fs::file f;
auto set_sparse = [](HANDLE h) -> bool // Get system version
{ [[maybe_unused]] static const DWORD version_major = *reinterpret_cast<const DWORD*>(__readgsqword(0x60) + 0x118);
// Get version
const DWORD version_major = *reinterpret_cast<const DWORD*>(__readgsqword(0x60) + 0x118);
// Disable sparse files on Windows 7 or lower auto set_sparse = [](HANDLE h, usz m_size) -> bool
if (version_major <= 7)
{ {
return true; FILE_SET_SPARSE_BUFFER arg{.SetSparse = true};
FILE_BASIC_INFO info0{};
ensure(GetFileInformationByHandleEx(h, FileBasicInfo, &info0, sizeof(info0)));
if ((info0.FileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) == 0 && version_major <= 7)
{
MessageBoxW(0, L"RPCS3 needs to be restarted to create sparse file rpcs3_vm.", L"RPCS3", MB_ICONEXCLAMATION);
} }
if (DeviceIoControl(h, FSCTL_SET_SPARSE, nullptr, 0, nullptr, 0, nullptr, nullptr)) if ((info0.FileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) || DeviceIoControl(h, FSCTL_SET_SPARSE, &arg, sizeof(arg), nullptr, 0, nullptr, nullptr))
{ {
FILE_STANDARD_INFO info; if ((info0.FileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) == 0 && version_major <= 7)
ensure(GetFileInformationByHandleEx(h, FileStandardInfo, &info, sizeof(info))); {
std::abort();
}
if (info.AllocationSize.QuadPart) FILE_STANDARD_INFO info;
{
// Make sure the file is not "dirty"
FILE_END_OF_FILE_INFO _eof{}; FILE_END_OF_FILE_INFO _eof{};
ensure(GetFileInformationByHandleEx(h, FileStandardInfo, &info, sizeof(info)));
ensure(GetFileSizeEx(h, &_eof.EndOfFile));
if (info.AllocationSize.QuadPart && _eof.EndOfFile.QuadPart == m_size)
{
// Truncate file since it may be dirty (fool-proof)
DWORD ret = 0;
FILE_ALLOCATED_RANGE_BUFFER dummy{};
dummy.Length.QuadPart = m_size;
if (!DeviceIoControl(h, FSCTL_QUERY_ALLOCATED_RANGES, &dummy, sizeof(dummy), nullptr, 0, &ret, 0) || ret)
{
_eof.EndOfFile.QuadPart = 0;
}
}
if (_eof.EndOfFile.QuadPart != m_size)
{
// Reset file size to 0 if it doesn't match
_eof.EndOfFile.QuadPart = 0;
ensure(SetFileInformationByHandle(h, FileEndOfFileInfo, &_eof, sizeof(_eof))); ensure(SetFileInformationByHandle(h, FileEndOfFileInfo, &_eof, sizeof(_eof)));
} }
@ -365,12 +388,12 @@ namespace utils
{ {
ensure(f.open(storage, fs::read + fs::write + fs::create)); ensure(f.open(storage, fs::read + fs::write + fs::create));
} }
else if (!f.open(fs::get_temp_dir() + "rpcs3_vm", fs::read + fs::write + fs::create) || !set_sparse(f.get_handle())) else if (!f.open(fs::get_cache_dir() + "rpcs3_vm", fs::read + fs::write + fs::create) || !set_sparse(f.get_handle(), m_size))
{ {
ensure(f.open(fs::get_cache_dir() + "rpcs3_vm", fs::read + fs::write + fs::create)); ensure(f.open(fs::get_temp_dir() + "rpcs3_vm", fs::read + fs::write + fs::create));
} }
if (!set_sparse(f.get_handle())) if (!set_sparse(f.get_handle(), m_size))
{ {
MessageBoxW(0, L"Failed to initialize sparse file.", L"RPCS3", MB_ICONERROR); MessageBoxW(0, L"Failed to initialize sparse file.", L"RPCS3", MB_ICONERROR);
} }