diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index d5984f560b..a7a45ca8b2 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -4,6 +4,7 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include "common/assert.h" @@ -333,14 +334,16 @@ FileSystemController::~FileSystemController() = default; Result FileSystemController::RegisterProcess( ProcessId process_id, ProgramId program_id, - std::shared_ptr&& romfs_factory) { + std::shared_ptr&& romfs_factory, std::string homebrew_initial_cwd) { std::scoped_lock lk{registration_lock}; - registrations.emplace(process_id, Registration{ - .program_id = program_id, - .romfs_factory = std::move(romfs_factory), - .save_data_factory = CreateSaveDataFactory(program_id), - }); + registrations.insert_or_assign(process_id, + Registration{ + .program_id = program_id, + .romfs_factory = std::move(romfs_factory), + .save_data_factory = CreateSaveDataFactory(program_id), + .homebrew_initial_cwd = std::move(homebrew_initial_cwd), + }); LOG_DEBUG(Service_FS, "Registered for process {}", process_id); return ResultSuccess; @@ -348,7 +351,8 @@ Result FileSystemController::RegisterProcess( Result FileSystemController::OpenProcess( ProgramId* out_program_id, std::shared_ptr* out_save_data_controller, - std::shared_ptr* out_romfs_controller, ProcessId process_id) { + std::shared_ptr* out_romfs_controller, ProcessId process_id, + std::string* out_homebrew_initial_cwd) { std::scoped_lock lk{registration_lock}; const auto it = registrations.find(process_id); @@ -361,6 +365,9 @@ Result FileSystemController::OpenProcess( std::make_shared(system, it->second.save_data_factory); *out_romfs_controller = std::make_shared(it->second.romfs_factory, it->second.program_id); + if (out_homebrew_initial_cwd != nullptr) { + *out_homebrew_initial_cwd = it->second.homebrew_initial_cwd; + } return ResultSuccess; } diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index ef45aec627..72721f3192 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -8,6 +8,7 @@ #include #include +#include #include "common/common_types.h" #include "core/file_sys/fs_directory.h" #include "core/file_sys/fs_filesystem.h" @@ -71,11 +72,12 @@ public: ~FileSystemController(); Result RegisterProcess(ProcessId process_id, ProgramId program_id, - std::shared_ptr&& factory); + std::shared_ptr&& factory, + std::string homebrew_initial_cwd = {}); Result OpenProcess(ProgramId* out_program_id, - std::shared_ptr* out_save_data_controller, - std::shared_ptr* out_romfs_controller, - ProcessId process_id); + std::shared_ptr* out_save_data_controller, + std::shared_ptr* out_romfs_controller, + ProcessId process_id, std::string* out_homebrew_initial_cwd = nullptr); void SetPackedUpdate(ProcessId process_id, FileSys::VirtualFile update_raw); std::shared_ptr OpenSaveDataController(); @@ -136,6 +138,7 @@ private: ProgramId program_id; std::shared_ptr romfs_factory; std::shared_ptr save_data_factory; + std::string homebrew_initial_cwd; }; std::mutex registration_lock; diff --git a/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp index d16602022a..18c62ac935 100644 --- a/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp +++ b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp @@ -4,6 +4,9 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + +#include "common/fs/path_util.h" #include "common/string_util.h" #include "core/file_sys/fssrv/fssrv_sf_path.h" #include "core/hle/service/cmif_serialization.h" @@ -13,10 +16,29 @@ namespace Service::FileSystem { -IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_) - : ServiceFramework{system_, "IFileSystem"}, backend{std::make_unique( - dir_)}, - size_getter{std::move(size_getter_)} { +static std::string ResolveHomebrewCwdRootAlias(std::string path, + std::string_view homebrew_initial_cwd) { + if (homebrew_initial_cwd.empty()) { + return path; + } + + const std::string normalized_path = Common::FS::SanitizePath(path); + if (normalized_path.empty() || normalized_path == "/" || + normalized_path != homebrew_initial_cwd || + path.size() != normalized_path.size() + 2 || + path.substr(0, normalized_path.size()) != normalized_path || + path.substr(normalized_path.size()) != "//") { + return path; + } + + return "/"; +} + +IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_, + std::string homebrew_initial_cwd) + : ServiceFramework{system_, "IFileSystem"}, + backend{std::make_unique(dir_)}, + size_getter{std::move(size_getter_)}, homebrew_initial_cwd{std::move(homebrew_initial_cwd)} { static const FunctionInfo functions[] = { {0, D<&IFileSystem::CreateFile>, "CreateFile"}, {1, D<&IFileSystem::DeleteFile>, "DeleteFile"}, @@ -43,7 +65,8 @@ Result IFileSystem::CreateFile(const InLargeDatastr, option, size); - R_RETURN(backend->CreateFile(FileSys::Path(path->str), size)); + const auto fs_path = ResolveHomebrewCwdRootAlias(path->str, homebrew_initial_cwd); + R_RETURN(backend->CreateFile(FileSys::Path(fs_path.c_str()), size)); } Result IFileSystem::DeleteFile(const InLargeData path) { @@ -94,7 +117,8 @@ Result IFileSystem::OpenFile(OutInterface out_interface, LOG_DEBUG(Service_FS, "called. file={}, mode={}", path->str, mode); FileSys::VirtualFile vfs_file{}; - R_TRY(backend->OpenFile(&vfs_file, FileSys::Path(path->str), + const auto fs_path = ResolveHomebrewCwdRootAlias(path->str, homebrew_initial_cwd); + R_TRY(backend->OpenFile(&vfs_file, FileSys::Path(fs_path.c_str()), static_cast(mode))); *out_interface = std::make_shared(system, vfs_file); @@ -107,7 +131,8 @@ Result IFileSystem::OpenDirectory(OutInterface out_interface, LOG_DEBUG(Service_FS, "called. directory={}, mode={}", path->str, mode); FileSys::VirtualDir vfs_dir{}; - R_TRY(backend->OpenDirectory(&vfs_dir, FileSys::Path(path->str), + const auto fs_path = ResolveHomebrewCwdRootAlias(path->str, homebrew_initial_cwd); + R_TRY(backend->OpenDirectory(&vfs_dir, FileSys::Path(fs_path.c_str()), static_cast(mode))); *out_interface = std::make_shared(system, vfs_dir, @@ -120,7 +145,8 @@ Result IFileSystem::GetEntryType( LOG_DEBUG(Service_FS, "called. file={}", path->str); FileSys::DirectoryEntryType vfs_entry_type{}; - R_TRY(backend->GetEntryType(&vfs_entry_type, FileSys::Path(path->str))); + const auto fs_path = ResolveHomebrewCwdRootAlias(path->str, homebrew_initial_cwd); + R_TRY(backend->GetEntryType(&vfs_entry_type, FileSys::Path(fs_path.c_str()))); *out_type = static_cast(vfs_entry_type); R_SUCCEED(); diff --git a/src/core/hle/service/filesystem/fsp/fs_i_filesystem.h b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.h index dd069f36f3..123680012f 100644 --- a/src/core/hle/service/filesystem/fsp/fs_i_filesystem.h +++ b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.h @@ -1,8 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include + #include "common/common_funcs.h" #include "core/file_sys/fs_filesystem.h" #include "core/file_sys/fsa/fs_i_filesystem.h" @@ -23,7 +28,8 @@ class IDirectory; class IFileSystem final : public ServiceFramework { public: - explicit IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_); + explicit IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_, + std::string homebrew_initial_cwd = {}); Result CreateFile(const InLargeData path, s32 option, s64 size); @@ -55,6 +61,7 @@ public: private: std::unique_ptr backend; SizeGetter size_getter; + std::string homebrew_initial_cwd; }; } // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp index fdbd50ed7a..8536ab3730 100644 --- a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp @@ -192,8 +192,9 @@ Result FSP_SRV::SetCurrentProcess(ClientProcessId pid) { LOG_DEBUG(Service_FS, "called. current_process_id={:#016x}", current_process_id); - R_RETURN( - fsc.OpenProcess(&program_id, &save_data_controller, &romfs_controller, current_process_id)); + homebrew_initial_cwd.clear(); + R_RETURN(fsc.OpenProcess(&program_id, &save_data_controller, &romfs_controller, + current_process_id, &homebrew_initial_cwd)); } Result FSP_SRV::OpenFileSystemWithPatch(OutInterface out_interface, @@ -224,7 +225,8 @@ Result FSP_SRV::OpenSdCardFileSystem(OutInterface out_interface) { fsc.OpenSDMC(&sdmc_dir); *out_interface = std::make_shared( - system, sdmc_dir, SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard)); + system, sdmc_dir, SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard), + homebrew_initial_cwd); R_SUCCEED(); } diff --git a/src/core/hle/service/filesystem/fsp/fsp_srv.h b/src/core/hle/service/filesystem/fsp/fsp_srv.h index 0ea41903e4..9e310b4252 100644 --- a/src/core/hle/service/filesystem/fsp/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp/fsp_srv.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include "core/file_sys/fs_save_data_types.h" #include "core/hle/service/cmif_types.h" #include "core/hle/service/filesystem/fsp/fsp_types.h" @@ -123,6 +124,7 @@ private: u32 access_log_program_index = 0; AccessLogMode access_log_mode = AccessLogMode::None; u64 program_id = 0; + std::string homebrew_initial_cwd; std::shared_ptr save_data_controller; std::shared_ptr romfs_controller; };