mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-18 22:23:35 +00:00
[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.
This commit is contained in:
+62
-1
@@ -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));
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user