mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-21 15:28:19 +00:00
[fs] Preserve guest file open modes in real VFS
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
namespace FileSys {
|
||||
|
||||
enum class OpenMode : u32 {
|
||||
Default = 0,
|
||||
Read = (1 << 0),
|
||||
Write = (1 << 1),
|
||||
AllowAppend = (1 << 2),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -27,9 +27,9 @@ VirtualDir LayeredVfsDirectory::MakeLayeredDirectory(std::vector<VirtualDir> 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;
|
||||
}
|
||||
|
||||
@@ -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<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;
|
||||
VirtualFile GetFile(std::string_view file_name) const override;
|
||||
VirtualDir GetSubdirectory(std::string_view subdir_name) const override;
|
||||
|
||||
@@ -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<std::string> 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<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;
|
||||
}
|
||||
@@ -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<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) {
|
||||
@@ -301,26 +295,21 @@ RealVfsFile::~RealVfsFile() {
|
||||
}
|
||||
|
||||
void RealVfsFilesystem::CloseCachedFileReferenceLocked(const std::string& path) {
|
||||
const auto it = cache.find(path);
|
||||
if (it == cache.end()) {
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#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<std::string, OpenMode>;
|
||||
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 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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user