Browse Source

[fs] Preserve guest file open modes in real VFS

xbzk 4 days ago
parent
commit
f3e4fadba2
  1. 1
      src/core/file_sys/fs_filesystem.h
  2. 11
      src/core/file_sys/vfs/vfs.cpp
  3. 3
      src/core/file_sys/vfs/vfs.h
  4. 4
      src/core/file_sys/vfs/vfs_layered.cpp
  5. 6
      src/core/file_sys/vfs/vfs_layered.h
  6. 66
      src/core/file_sys/vfs/vfs_real.cpp
  7. 7
      src/core/file_sys/vfs/vfs_real.h
  8. 2
      src/core/hle/service/filesystem/filesystem.cpp

1
src/core/file_sys/fs_filesystem.h

@ -12,6 +12,7 @@
namespace FileSys { namespace FileSys {
enum class OpenMode : u32 { enum class OpenMode : u32 {
Default = 0,
Read = (1 << 0), Read = (1 << 0),
Write = (1 << 1), Write = (1 << 1),
AllowAppend = (1 << 2), AllowAppend = (1 << 2),

11
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-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // 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) { VirtualFile VfsFilesystem::OpenFile(std::string_view path_, OpenMode perms) {
const auto path = Common::FS::SanitizePath(path_); 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) { VirtualFile VfsFilesystem::CreateFile(std::string_view path_, OpenMode perms) {
@ -201,7 +201,7 @@ std::string VfsFile::GetFullPath() const {
return GetContainingDirectory()->GetFullPath() + '/' + GetName(); 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); auto vec = Common::FS::SplitPathComponents(path);
if (vec.empty()) { if (vec.empty()) {
return nullptr; return nullptr;
@ -224,7 +224,10 @@ VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const {
return nullptr; 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 { VirtualFile VfsDirectory::GetFileAbsolute(std::string_view path) const {

3
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 // Retrieves the file located at path as if the current directory was root. Returns nullptr if
// not found. // 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. // Calls GetFileRelative(path) on the root of the current directory.
virtual VirtualFile GetFileAbsolute(std::string_view path) const; virtual VirtualFile GetFileAbsolute(std::string_view path) const;

4
src/core/file_sys/vfs/vfs_layered.cpp

@ -27,9 +27,9 @@ VirtualDir LayeredVfsDirectory::MakeLayeredDirectory(std::vector<VirtualDir> dir
return VirtualDir(new LayeredVfsDirectory(std::move(dirs), std::move(name))); 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) { for (const auto& layer : dirs) {
const auto file = layer->GetFileRelative(path);
const auto file = layer->GetFileRelative(path, perms);
if (file != nullptr) if (file != nullptr)
return file; return file;
} }

6
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-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // 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. /// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases.
static VirtualDir MakeLayeredDirectory(std::vector<VirtualDir> dirs, std::string name = ""); static VirtualDir MakeLayeredDirectory(std::vector<VirtualDir> 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; VirtualDir GetDirectoryRelative(std::string_view path) const override;
VirtualFile GetFile(std::string_view file_name) const override; VirtualFile GetFile(std::string_view file_name) const override;
VirtualDir GetSubdirectory(std::string_view subdir_name) const override; VirtualDir GetSubdirectory(std::string_view subdir_name) const override;

66
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) { 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; return FS::FileAccessMode::ReadWrite;
default:
return {};
} }
return FS::FileAccessMode::Read;
} }
} // Anonymous namespace } // Anonymous namespace
@ -93,9 +87,11 @@ VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::op
std::optional<std::string> parent_path, std::optional<std::string> parent_path,
OpenMode perms) { OpenMode perms) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); 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}; 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) { if (auto file = it->second.lock(); file) {
return file; return file;
} }
@ -109,8 +105,9 @@ VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::op
this->InsertReferenceIntoListLocked(*reference); this->InsertReferenceIntoListLocked(*reference);
auto file = std::shared_ptr<RealVfsFile>( auto file = std::shared_ptr<RealVfsFile>(
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; return file;
} }
@ -124,7 +121,6 @@ VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, OpenMode perms
{ {
std::scoped_lock lk{list_lock}; std::scoped_lock lk{list_lock};
CloseCachedFileReferenceLocked(path); CloseCachedFileReferenceLocked(path);
cache.erase(path);
} }
// Current usages of CreateFile expect to delete the contents of an existing file. // 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}; std::scoped_lock lk{list_lock};
CloseCachedFileReferenceLocked(old_path); CloseCachedFileReferenceLocked(old_path);
CloseCachedFileReferenceLocked(new_path); CloseCachedFileReferenceLocked(new_path);
cache.erase(old_path);
cache.erase(new_path);
} }
if (!FS::RenameFile(old_path, new_path)) { if (!FS::RenameFile(old_path, new_path)) {
return nullptr; return nullptr;
@ -173,14 +167,14 @@ bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
{ {
std::scoped_lock lk{list_lock}; std::scoped_lock lk{list_lock};
CloseCachedFileReferenceLocked(path); CloseCachedFileReferenceLocked(path);
cache.erase(path);
} }
return FS::RemoveFile(path); return FS::RemoveFile(path);
} }
VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) { VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
return std::shared_ptr<RealVfsDirectory>(
new RealVfsDirectory(*this, path, perms == OpenMode::Default ? OpenMode::Read : perms));
} }
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) { VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) {
@ -301,26 +295,21 @@ RealVfsFile::~RealVfsFile() {
} }
void RealVfsFilesystem::CloseCachedFileReferenceLocked(const std::string& path) { 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<RealVfsFile*>(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<RealVfsFile*>(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 { std::string RealVfsFile::GetName() const {
@ -449,13 +438,14 @@ RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string&
RealVfsDirectory::~RealVfsDirectory() = default; 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)); const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
if (!FS::Exists(full_path) || FS::IsDir(full_path) if (!FS::Exists(full_path) || FS::IsDir(full_path)
|| !IsWithinRoot(FS::SanitizePath(path), full_path)) { || !IsWithinRoot(FS::SanitizePath(path), full_path)) {
return nullptr; 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 { VirtualDir RealVfsDirectory::GetDirectoryRelative(std::string_view relative_path) const {

7
src/core/file_sys/vfs/vfs_real.h

@ -10,6 +10,7 @@
#include <mutex> #include <mutex>
#include <optional> #include <optional>
#include <string_view> #include <string_view>
#include <utility>
#include "common/intrusive_list.h" #include "common/intrusive_list.h"
#include "core/file_sys/fs_filesystem.h" #include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/vfs/vfs.h" #include "core/file_sys/vfs/vfs.h"
@ -49,8 +50,9 @@ public:
bool DeleteDirectory(std::string_view path) override; bool DeleteDirectory(std::string_view path) override;
private: private:
using CacheKey = std::pair<std::string, OpenMode>;
using ReferenceListType = Common::IntrusiveListBaseTraits<FileReference>::ListType; using ReferenceListType = Common::IntrusiveListBaseTraits<FileReference>::ListType;
std::map<std::string, std::weak_ptr<VfsFile>, std::less<>> cache;
std::map<CacheKey, std::weak_ptr<VfsFile>, std::less<>> cache;
ReferenceListType open_references; ReferenceListType open_references;
ReferenceListType closed_references; ReferenceListType closed_references;
std::mutex list_lock; std::mutex list_lock;
@ -117,7 +119,8 @@ class RealVfsDirectory : public VfsDirectory {
public: public:
~RealVfsDirectory() override; ~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; VirtualDir GetDirectoryRelative(std::string_view relative_path) const override;
VirtualFile GetFile(std::string_view name) const override; VirtualFile GetFile(std::string_view name) const override;
VirtualDir GetSubdirectory(std::string_view name) const override; VirtualDir GetSubdirectory(std::string_view name) const override;

2
src/core/hle/service/filesystem/filesystem.cpp

@ -258,7 +258,7 @@ Result VfsDirectoryServiceWrapper::OpenFile(FileSys::VirtualFile* out_file,
npath.remove_prefix(1); npath.remove_prefix(1);
} }
auto file = backing->GetFileRelative(npath);
auto file = backing->GetFileRelative(npath, mode);
if (file == nullptr) { if (file == nullptr) {
return FileSys::ResultPathNotFound; return FileSys::ResultPathNotFound;
} }

Loading…
Cancel
Save