diff --git a/src/core/file_sys/fs_filesystem.h b/src/core/file_sys/fs_filesystem.h index e895502e9a..6f77d7972b 100644 --- a/src/core/file_sys/fs_filesystem.h +++ b/src/core/file_sys/fs_filesystem.h @@ -12,6 +12,7 @@ namespace FileSys { enum class OpenMode : u32 { + Default = 0, Read = (1 << 0), Write = (1 << 1), AllowAppend = (1 << 2), diff --git a/src/core/file_sys/vfs/vfs.cpp b/src/core/file_sys/vfs/vfs.cpp index b85b843e3e..afd602017d 100644 --- a/src/core/file_sys/vfs/vfs.cpp +++ b/src/core/file_sys/vfs/vfs.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project @@ -40,7 +40,7 @@ VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const { VirtualFile VfsFilesystem::OpenFile(std::string_view path_, OpenMode perms) { const auto path = Common::FS::SanitizePath(path_); - return root->GetFileRelative(path); + return root->GetFileRelative(path, perms); } VirtualFile VfsFilesystem::CreateFile(std::string_view path_, OpenMode perms) { @@ -201,7 +201,7 @@ std::string VfsFile::GetFullPath() const { return GetContainingDirectory()->GetFullPath() + '/' + GetName(); } -VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const { +VirtualFile VfsDirectory::GetFileRelative(std::string_view path, OpenMode perms) const { auto vec = Common::FS::SplitPathComponents(path); if (vec.empty()) { return nullptr; @@ -224,7 +224,10 @@ VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const { return nullptr; } - return dir->GetFile(vec.back()); + if (perms == OpenMode::Default) { + return dir->GetFile(vec.back()); + } + return dir->GetFileRelative(vec.back(), perms); } VirtualFile VfsDirectory::GetFileAbsolute(std::string_view path) const { diff --git a/src/core/file_sys/vfs/vfs.h b/src/core/file_sys/vfs/vfs.h index c3d73364bc..4b8e7d7ba0 100644 --- a/src/core/file_sys/vfs/vfs.h +++ b/src/core/file_sys/vfs/vfs.h @@ -201,7 +201,8 @@ public: // Retrieves the file located at path as if the current directory was root. Returns nullptr if // not found. - virtual VirtualFile GetFileRelative(std::string_view path) const; + virtual VirtualFile GetFileRelative(std::string_view path, + OpenMode perms = OpenMode::Default) const; // Calls GetFileRelative(path) on the root of the current directory. virtual VirtualFile GetFileAbsolute(std::string_view path) const; diff --git a/src/core/file_sys/vfs/vfs_layered.cpp b/src/core/file_sys/vfs/vfs_layered.cpp index 8dabe03e16..b63a35ba1b 100644 --- a/src/core/file_sys/vfs/vfs_layered.cpp +++ b/src/core/file_sys/vfs/vfs_layered.cpp @@ -27,9 +27,9 @@ VirtualDir LayeredVfsDirectory::MakeLayeredDirectory(std::vector dir return VirtualDir(new LayeredVfsDirectory(std::move(dirs), std::move(name))); } -VirtualFile LayeredVfsDirectory::GetFileRelative(std::string_view path) const { +VirtualFile LayeredVfsDirectory::GetFileRelative(std::string_view path, OpenMode perms) const { for (const auto& layer : dirs) { - const auto file = layer->GetFileRelative(path); + const auto file = layer->GetFileRelative(path, perms); if (file != nullptr) return file; } diff --git a/src/core/file_sys/vfs/vfs_layered.h b/src/core/file_sys/vfs/vfs_layered.h index 0027ffa9a9..e9854f3601 100644 --- a/src/core/file_sys/vfs/vfs_layered.h +++ b/src/core/file_sys/vfs/vfs_layered.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -20,7 +23,8 @@ public: /// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases. static VirtualDir MakeLayeredDirectory(std::vector dirs, std::string name = ""); - VirtualFile GetFileRelative(std::string_view path) const override; + VirtualFile GetFileRelative(std::string_view path, + OpenMode perms = OpenMode::Default) const override; VirtualDir GetDirectoryRelative(std::string_view path) const override; VirtualFile GetFile(std::string_view file_name) const override; VirtualDir GetSubdirectory(std::string_view subdir_name) const override; diff --git a/src/core/file_sys/vfs/vfs_real.cpp b/src/core/file_sys/vfs/vfs_real.cpp index b47b053bfb..f0f9d3358a 100644 --- a/src/core/file_sys/vfs/vfs_real.cpp +++ b/src/core/file_sys/vfs/vfs_real.cpp @@ -45,17 +45,11 @@ bool IsWithinRoot(std::string_view root, std::string_view full_path) { } constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(OpenMode mode) { - switch (mode) { - case OpenMode::Read: - return FS::FileAccessMode::Read; - case OpenMode::Write: - case OpenMode::ReadWrite: - case OpenMode::AllowAppend: - case OpenMode::All: + if (True(mode & OpenMode::Write) || True(mode & OpenMode::AllowAppend)) { return FS::FileAccessMode::ReadWrite; - default: - return {}; } + + return FS::FileAccessMode::Read; } } // Anonymous namespace @@ -93,9 +87,11 @@ VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::op std::optional parent_path, OpenMode perms) { const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); + const auto open_perms = perms == OpenMode::Default ? OpenMode::Read : perms; std::scoped_lock lk{list_lock}; - if (auto it = cache.find(path); it != cache.end()) { + const CacheKey cache_key{path, open_perms}; + if (auto it = cache.find(cache_key); it != cache.end()) { if (auto file = it->second.lock(); file) { return file; } @@ -109,8 +105,9 @@ VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::op this->InsertReferenceIntoListLocked(*reference); auto file = std::shared_ptr( - new RealVfsFile(*this, std::move(reference), path, perms, size, std::move(parent_path))); - cache[path] = file; + new RealVfsFile(*this, std::move(reference), path, open_perms, size, + std::move(parent_path))); + cache[cache_key] = file; return file; } @@ -124,7 +121,6 @@ VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, OpenMode perms { std::scoped_lock lk{list_lock}; CloseCachedFileReferenceLocked(path); - cache.erase(path); } // Current usages of CreateFile expect to delete the contents of an existing file. @@ -159,8 +155,6 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_ std::scoped_lock lk{list_lock}; CloseCachedFileReferenceLocked(old_path); CloseCachedFileReferenceLocked(new_path); - cache.erase(old_path); - cache.erase(new_path); } if (!FS::RenameFile(old_path, new_path)) { return nullptr; @@ -173,14 +167,14 @@ bool RealVfsFilesystem::DeleteFile(std::string_view path_) { { std::scoped_lock lk{list_lock}; CloseCachedFileReferenceLocked(path); - cache.erase(path); } return FS::RemoveFile(path); } VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) { const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); - return std::shared_ptr(new RealVfsDirectory(*this, path, perms)); + return std::shared_ptr( + new RealVfsDirectory(*this, path, perms == OpenMode::Default ? OpenMode::Read : perms)); } VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) { @@ -301,26 +295,21 @@ RealVfsFile::~RealVfsFile() { } void RealVfsFilesystem::CloseCachedFileReferenceLocked(const std::string& path) { - const auto it = cache.find(path); - if (it == cache.end()) { - return; - } - - const auto cached_file = it->second.lock(); - if (!cached_file) { - return; - } - - auto* real_file = static_cast(cached_file.get()); - auto& reference = real_file->reference; - if (!reference || !reference->file) { - return; + for (auto it = cache.lower_bound(CacheKey{path, OpenMode::Default}); + it != cache.end() && it->first.first == path;) { + const auto cached_file = it->second.lock(); + if (cached_file) { + auto* const real_file = static_cast(cached_file.get()); + auto& reference = real_file->reference; + if (reference && reference->file) { + RemoveReferenceFromListLocked(*reference); + reference->file.reset(); + num_open_files--; + InsertReferenceIntoListLocked(*reference); + } + } + it = cache.erase(it); } - - RemoveReferenceFromListLocked(*reference); - reference->file.reset(); - num_open_files--; - InsertReferenceIntoListLocked(*reference); } std::string RealVfsFile::GetName() const { @@ -449,13 +438,14 @@ RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& RealVfsDirectory::~RealVfsDirectory() = default; -VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) const { +VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path, + OpenMode open_perms) const { const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); if (!FS::Exists(full_path) || FS::IsDir(full_path) || !IsWithinRoot(FS::SanitizePath(path), full_path)) { return nullptr; } - return base.OpenFile(full_path, perms); + return base.OpenFile(full_path, open_perms == OpenMode::Default ? perms : open_perms); } VirtualDir RealVfsDirectory::GetDirectoryRelative(std::string_view relative_path) const { diff --git a/src/core/file_sys/vfs/vfs_real.h b/src/core/file_sys/vfs/vfs_real.h index 2dad801bbf..9b102f4ce9 100644 --- a/src/core/file_sys/vfs/vfs_real.h +++ b/src/core/file_sys/vfs/vfs_real.h @@ -10,6 +10,7 @@ #include #include #include +#include #include "common/intrusive_list.h" #include "core/file_sys/fs_filesystem.h" #include "core/file_sys/vfs/vfs.h" @@ -49,8 +50,9 @@ public: bool DeleteDirectory(std::string_view path) override; private: + using CacheKey = std::pair; using ReferenceListType = Common::IntrusiveListBaseTraits::ListType; - std::map, std::less<>> cache; + std::map, std::less<>> cache; ReferenceListType open_references; ReferenceListType closed_references; std::mutex list_lock; @@ -117,7 +119,8 @@ class RealVfsDirectory : public VfsDirectory { public: ~RealVfsDirectory() override; - VirtualFile GetFileRelative(std::string_view relative_path) const override; + VirtualFile GetFileRelative(std::string_view relative_path, + OpenMode perms = OpenMode::Default) const override; VirtualDir GetDirectoryRelative(std::string_view relative_path) const override; VirtualFile GetFile(std::string_view name) const override; VirtualDir GetSubdirectory(std::string_view name) const override; diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index a7a45ca8b2..6d5e5f43b9 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -258,7 +258,7 @@ Result VfsDirectoryServiceWrapper::OpenFile(FileSys::VirtualFile* out_file, npath.remove_prefix(1); } - auto file = backing->GetFileRelative(npath); + auto file = backing->GetFileRelative(npath, mode); if (file == nullptr) { return FileSys::ResultPathNotFound; }