Browse Source

[fs] Allow real VFS files to be replaced while open

Add a Windows share-delete file open mode and use it for cached real VFS files. Close cached references before create, move, and delete so guest-side self-update flows can rename or replace files that Eden previously opened.

Also preserve Android real VFS full paths so homebrew path derivation does not lose the original file path.
xbzk/hb-tico-support-part-2
xbzk 5 days ago
parent
commit
c87f6202d3
  1. 63
      src/common/fs/file.cpp
  2. 11
      src/common/fs/fs_types.h
  3. 46
      src/core/file_sys/vfs/vfs_real.cpp
  4. 2
      src/core/file_sys/vfs/vfs_real.h

63
src/common/fs/file.cpp

@ -4,6 +4,8 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cerrno>
#include <cstdint>
#include <vector>
#include "common/assert.h"
@ -15,8 +17,10 @@
#include "common/logging.h"
#ifdef _WIN32
#include <fcntl.h>
#include <io.h>
#include <share.h>
#include <windows.h>
#else
#include <unistd.h>
#endif
@ -95,10 +99,65 @@ namespace {
case FileShareFlag::ShareWriteOnly:
return _SH_DENYRD;
case FileShareFlag::ShareReadWrite:
case FileShareFlag::ShareReadWriteDelete:
return _SH_DENYNO;
}
}
[[nodiscard]] std::FILE* OpenWithWindowsShareDelete(const fs::path& path, FileAccessMode mode,
FileType type) {
DWORD desired_access{};
DWORD creation_disposition{OPEN_EXISTING};
int open_flags = type == FileType::BinaryFile ? _O_BINARY : _O_TEXT;
switch (mode) {
case FileAccessMode::Read:
desired_access = GENERIC_READ;
open_flags |= _O_RDONLY;
break;
case FileAccessMode::Write:
desired_access = GENERIC_WRITE;
creation_disposition = CREATE_ALWAYS;
open_flags |= _O_WRONLY;
break;
case FileAccessMode::Append:
desired_access = GENERIC_WRITE;
creation_disposition = OPEN_ALWAYS;
open_flags |= _O_WRONLY | _O_APPEND;
break;
case FileAccessMode::ReadWrite:
desired_access = GENERIC_READ | GENERIC_WRITE;
open_flags |= _O_RDWR;
break;
case FileAccessMode::ReadAppend:
desired_access = GENERIC_READ | GENERIC_WRITE;
creation_disposition = OPEN_ALWAYS;
open_flags |= _O_RDWR | _O_APPEND;
break;
}
const auto handle =
CreateFileW(path.c_str(), desired_access,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
creation_disposition, FILE_ATTRIBUTE_NORMAL, nullptr);
if (handle == INVALID_HANDLE_VALUE) {
errno = EACCES;
return nullptr;
}
const auto fd = _open_osfhandle(reinterpret_cast<intptr_t>(handle), open_flags);
if (fd == -1) {
CloseHandle(handle);
return nullptr;
}
auto* const file = _wfdopen(fd, AccessModeToWStr(mode, type));
if (file == nullptr) {
_close(fd);
}
return file;
}
#else
/**
@ -254,7 +313,9 @@ void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, File
errno = 0;
#ifdef _WIN32
if (flag != FileShareFlag::ShareNone) {
if (flag == FileShareFlag::ShareReadWriteDelete) {
file = OpenWithWindowsShareDelete(path, mode, type);
} else if (flag != FileShareFlag::ShareNone) {
file = _wfsopen(path.c_str(), AccessModeToWStr(mode, type), ToWindowsFileShareFlag(flag));
} else {
_wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type));

11
src/common/fs/fs_types.h

@ -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 2021 yuzu Emulator Project
@ -49,10 +49,11 @@ enum class FileType {
};
enum class FileShareFlag {
ShareNone, // Provides exclusive access to the file.
ShareReadOnly, // Provides read only shared access to the file.
ShareWriteOnly, // Provides write only shared access to the file.
ShareReadWrite, // Provides read and write shared access to the file.
ShareNone, // Provides exclusive access to the file.
ShareReadOnly, // Provides read only shared access to the file.
ShareWriteOnly, // Provides write only shared access to the file.
ShareReadWrite, // Provides read and write shared access to the file.
ShareReadWriteDelete, // Provides read, write, and delete shared access to the file.
};
enum class DirEntryFilter {

46
src/core/file_sys/vfs/vfs_real.cpp

@ -12,7 +12,6 @@
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/file_sys/vfs/vfs_real.h"
@ -124,6 +123,7 @@ VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, OpenMode perms
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
{
std::scoped_lock lk{list_lock};
CloseCachedFileReferenceLocked(path);
cache.erase(path);
}
@ -157,6 +157,8 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
{
std::scoped_lock lk{list_lock};
CloseCachedFileReferenceLocked(old_path);
CloseCachedFileReferenceLocked(new_path);
cache.erase(old_path);
cache.erase(new_path);
}
@ -170,6 +172,7 @@ bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
{
std::scoped_lock lk{list_lock};
CloseCachedFileReferenceLocked(path);
cache.erase(path);
}
return FS::RemoveFile(path);
@ -222,8 +225,8 @@ std::unique_lock<std::mutex> RealVfsFilesystem::RefreshReference(const std::stri
if (!reference.file) {
this->EvictSingleReferenceLocked();
reference.file =
FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile);
reference.file = FS::FileOpen(path, ModeFlagsToFileAccessMode(perms),
FS::FileType::BinaryFile, FS::FileShareFlag::ShareReadWriteDelete);
if (reference.file) {
num_open_files++;
}
@ -297,15 +300,50 @@ RealVfsFile::~RealVfsFile() {
base.DropReference(std::move(reference));
}
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;
}
RemoveReferenceFromListLocked(*reference);
reference->file.reset();
num_open_files--;
InsertReferenceIntoListLocked(*reference);
}
std::string RealVfsFile::GetName() const {
#ifdef __ANDROID__
if (path[0] != '/') {
if (!path.empty() && path[0] != '/') {
return FS::Android::GetFilename(path);
}
#endif
return path_components.empty() ? "" : std::string(path_components.back());
}
std::string RealVfsFile::GetFullPath() const {
#ifdef __ANDROID__
if (!path.empty() && path[0] != '/') {
auto out = path;
std::replace(out.begin(), out.end(), '\\', '/');
return out;
}
#endif
return VfsFile::GetFullPath();
}
std::size_t RealVfsFile::GetSize() const {
if (size) {
return *size;

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

@ -63,6 +63,7 @@ private:
std::unique_lock<std::mutex> RefreshReference(const std::string& path, OpenMode perms,
FileReference& reference);
void DropReference(std::unique_ptr<FileReference>&& reference);
void CloseCachedFileReferenceLocked(const std::string& path);
private:
friend class RealVfsDirectory;
@ -85,6 +86,7 @@ public:
~RealVfsFile() override;
std::string GetName() const override;
std::string GetFullPath() const override;
std::size_t GetSize() const override;
bool Resize(std::size_t new_size) override;
VirtualDir GetContainingDirectory() const override;

Loading…
Cancel
Save