From f46e3c7e3973fb4c9bf9620cc0e59c0302143351 Mon Sep 17 00:00:00 2001 From: emoose Date: Sun, 18 Aug 2019 05:18:16 +0100 Subject: [PATCH] [Kernel/VFS] Ensure vfs::Entry is up-to-date before retrieving file information Games like Forza use NtQueryInformationFile to get the size of the file, to make sure that there's enough room inside it to write data. Previously, updating the file size (via SetInfoFile(EndOfFile) or WriteFile) wouldn't update the vfs::Entry size field, which NtQueryInfo uses to return the size of the file. This resulted in the game thinking that the file was smaller than it actually is, and trying to correct that by using SetInfoFile(EndOfFile), which then truncated the file and deleted important data that was written to it. --- src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc | 8 ++++++++ src/xenia/vfs/devices/host_path_entry.cc | 12 ++++++++++++ src/xenia/vfs/devices/host_path_entry.h | 1 + src/xenia/vfs/entry.h | 1 + 4 files changed, 22 insertions(+) diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc index cda1a9a48..ece9a8cfd 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc @@ -402,6 +402,9 @@ dword_result_t NtSetInformationFile( assert_true(length == 8); auto eof = xe::load_and_swap(file_info); result = file->SetLength(eof); + + // Update the files vfs::Entry information + file->entry()->update(); break; } case XFileCompletionInformation: { @@ -485,6 +488,11 @@ dword_result_t NtQueryInformationFile( // }; assert_true(length == 56); + // Make sure we're working with up-to-date information, just in case the + // file size has changed via something other than NtSetInfoFile + // (eg. seems NtWriteFile might extend the file in some cases) + file->entry()->update(); + auto file_info = file_info_ptr.as(); file_info->creation_time = file->entry()->create_timestamp(); file_info->last_access_time = file->entry()->access_timestamp(); diff --git a/src/xenia/vfs/devices/host_path_entry.cc b/src/xenia/vfs/devices/host_path_entry.cc index 3d0ce2057..10b19fb03 100644 --- a/src/xenia/vfs/devices/host_path_entry.cc +++ b/src/xenia/vfs/devices/host_path_entry.cc @@ -104,5 +104,17 @@ bool HostPathEntry::DeleteEntryInternal(Entry* entry) { } } +void HostPathEntry::update() { + xe::filesystem::FileInfo file_info; + if (!xe::filesystem::GetInfo(local_path_, &file_info)) { + return; + } + if (file_info.type == xe::filesystem::FileInfo::Type::kFile) { + size_ = file_info.total_size; + allocation_size_ = + xe::round_up(file_info.total_size, device()->bytes_per_sector()); + } +} + } // namespace vfs } // namespace xe diff --git a/src/xenia/vfs/devices/host_path_entry.h b/src/xenia/vfs/devices/host_path_entry.h index 50e0aea82..a3d01e0e3 100644 --- a/src/xenia/vfs/devices/host_path_entry.h +++ b/src/xenia/vfs/devices/host_path_entry.h @@ -38,6 +38,7 @@ class HostPathEntry : public Entry { std::unique_ptr OpenMapped(MappedMemory::Mode mode, size_t offset, size_t length) override; + void update() override; private: friend class HostPathDevice; diff --git a/src/xenia/vfs/entry.h b/src/xenia/vfs/entry.h index 6e6e85dc4..bdbf7de3a 100644 --- a/src/xenia/vfs/entry.h +++ b/src/xenia/vfs/entry.h @@ -120,6 +120,7 @@ class Entry { size_t length = 0) { return nullptr; } + virtual void update() { return; } protected: Entry(Device* device, Entry* parent, const std::string& path);