Browse Source

[fsp] Preserve homebrew cwd for SDMC root aliases

Carry the initial homebrew working directory through filesystem process registration and FSP current-process state.

Use that cwd to resolve the homebrew cwd-plus-double-slash alias back to the SDMC root, allowing file browsers to navigate above their launch directory.
xbzk 5 days ago
parent
commit
dc91963c7b
  1. 21
      src/core/hle/service/filesystem/filesystem.cpp
  2. 11
      src/core/hle/service/filesystem/filesystem.h
  3. 42
      src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp
  4. 9
      src/core/hle/service/filesystem/fsp/fs_i_filesystem.h
  5. 8
      src/core/hle/service/filesystem/fsp/fsp_srv.cpp
  6. 2
      src/core/hle/service/filesystem/fsp/fsp_srv.h

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

@ -4,6 +4,7 @@
// 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
#include <string_view>
#include <utility> #include <utility>
#include "common/assert.h" #include "common/assert.h"
@ -333,14 +334,16 @@ FileSystemController::~FileSystemController() = default;
Result FileSystemController::RegisterProcess( Result FileSystemController::RegisterProcess(
ProcessId process_id, ProgramId program_id, ProcessId process_id, ProgramId program_id,
std::shared_ptr<FileSys::RomFSFactory>&& romfs_factory) {
std::shared_ptr<FileSys::RomFSFactory>&& romfs_factory, std::string homebrew_initial_cwd) {
std::scoped_lock lk{registration_lock}; 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); LOG_DEBUG(Service_FS, "Registered for process {}", process_id);
return ResultSuccess; return ResultSuccess;
@ -348,7 +351,8 @@ Result FileSystemController::RegisterProcess(
Result FileSystemController::OpenProcess( Result FileSystemController::OpenProcess(
ProgramId* out_program_id, std::shared_ptr<SaveDataController>* out_save_data_controller, ProgramId* out_program_id, std::shared_ptr<SaveDataController>* out_save_data_controller,
std::shared_ptr<RomFsController>* out_romfs_controller, ProcessId process_id) {
std::shared_ptr<RomFsController>* out_romfs_controller, ProcessId process_id,
std::string* out_homebrew_initial_cwd) {
std::scoped_lock lk{registration_lock}; std::scoped_lock lk{registration_lock};
const auto it = registrations.find(process_id); const auto it = registrations.find(process_id);
@ -361,6 +365,9 @@ Result FileSystemController::OpenProcess(
std::make_shared<SaveDataController>(system, it->second.save_data_factory); std::make_shared<SaveDataController>(system, it->second.save_data_factory);
*out_romfs_controller = *out_romfs_controller =
std::make_shared<RomFsController>(it->second.romfs_factory, it->second.program_id); std::make_shared<RomFsController>(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; return ResultSuccess;
} }

11
src/core/hle/service/filesystem/filesystem.h

@ -8,6 +8,7 @@
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <string>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/file_sys/fs_directory.h" #include "core/file_sys/fs_directory.h"
#include "core/file_sys/fs_filesystem.h" #include "core/file_sys/fs_filesystem.h"
@ -71,11 +72,12 @@ public:
~FileSystemController(); ~FileSystemController();
Result RegisterProcess(ProcessId process_id, ProgramId program_id, Result RegisterProcess(ProcessId process_id, ProgramId program_id,
std::shared_ptr<FileSys::RomFSFactory>&& factory);
std::shared_ptr<FileSys::RomFSFactory>&& factory,
std::string homebrew_initial_cwd = {});
Result OpenProcess(ProgramId* out_program_id, Result OpenProcess(ProgramId* out_program_id,
std::shared_ptr<SaveDataController>* out_save_data_controller,
std::shared_ptr<RomFsController>* out_romfs_controller,
ProcessId process_id);
std::shared_ptr<SaveDataController>* out_save_data_controller,
std::shared_ptr<RomFsController>* out_romfs_controller,
ProcessId process_id, std::string* out_homebrew_initial_cwd = nullptr);
void SetPackedUpdate(ProcessId process_id, FileSys::VirtualFile update_raw); void SetPackedUpdate(ProcessId process_id, FileSys::VirtualFile update_raw);
std::shared_ptr<SaveDataController> OpenSaveDataController(); std::shared_ptr<SaveDataController> OpenSaveDataController();
@ -136,6 +138,7 @@ private:
ProgramId program_id; ProgramId program_id;
std::shared_ptr<FileSys::RomFSFactory> romfs_factory; std::shared_ptr<FileSys::RomFSFactory> romfs_factory;
std::shared_ptr<FileSys::SaveDataFactory> save_data_factory; std::shared_ptr<FileSys::SaveDataFactory> save_data_factory;
std::string homebrew_initial_cwd;
}; };
std::mutex registration_lock; std::mutex registration_lock;

42
src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp

@ -4,6 +4,9 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <string_view>
#include "common/fs/path_util.h"
#include "common/string_util.h" #include "common/string_util.h"
#include "core/file_sys/fssrv/fssrv_sf_path.h" #include "core/file_sys/fssrv/fssrv_sf_path.h"
#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/cmif_serialization.h"
@ -13,10 +16,29 @@
namespace Service::FileSystem { namespace Service::FileSystem {
IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_)
: ServiceFramework{system_, "IFileSystem"}, backend{std::make_unique<FileSys::Fsa::IFileSystem>(
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<FileSys::Fsa::IFileSystem>(dir_)},
size_getter{std::move(size_getter_)}, homebrew_initial_cwd{std::move(homebrew_initial_cwd)} {
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, D<&IFileSystem::CreateFile>, "CreateFile"}, {0, D<&IFileSystem::CreateFile>, "CreateFile"},
{1, D<&IFileSystem::DeleteFile>, "DeleteFile"}, {1, D<&IFileSystem::DeleteFile>, "DeleteFile"},
@ -43,7 +65,8 @@ Result IFileSystem::CreateFile(const InLargeData<FileSys::Sf::Path, BufferAttr_H
s32 option, s64 size) { s32 option, s64 size) {
LOG_DEBUG(Service_FS, "called. file={}, option={:#x}, size={:#08x}", path->str, option, size); LOG_DEBUG(Service_FS, "called. file={}, option={:#x}, size={:#08x}", path->str, 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<FileSys::Sf::Path, BufferAttr_HipcPointer> path) { Result IFileSystem::DeleteFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
@ -94,7 +117,8 @@ Result IFileSystem::OpenFile(OutInterface<IFile> out_interface,
LOG_DEBUG(Service_FS, "called. file={}, mode={}", path->str, mode); LOG_DEBUG(Service_FS, "called. file={}, mode={}", path->str, mode);
FileSys::VirtualFile vfs_file{}; 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<FileSys::OpenMode>(mode))); static_cast<FileSys::OpenMode>(mode)));
*out_interface = std::make_shared<IFile>(system, vfs_file); *out_interface = std::make_shared<IFile>(system, vfs_file);
@ -107,7 +131,8 @@ Result IFileSystem::OpenDirectory(OutInterface<IDirectory> out_interface,
LOG_DEBUG(Service_FS, "called. directory={}, mode={}", path->str, mode); LOG_DEBUG(Service_FS, "called. directory={}, mode={}", path->str, mode);
FileSys::VirtualDir vfs_dir{}; 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<FileSys::OpenDirectoryMode>(mode))); static_cast<FileSys::OpenDirectoryMode>(mode)));
*out_interface = std::make_shared<IDirectory>(system, vfs_dir, *out_interface = std::make_shared<IDirectory>(system, vfs_dir,
@ -120,7 +145,8 @@ Result IFileSystem::GetEntryType(
LOG_DEBUG(Service_FS, "called. file={}", path->str); LOG_DEBUG(Service_FS, "called. file={}", path->str);
FileSys::DirectoryEntryType vfs_entry_type{}; 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<u32>(vfs_entry_type); *out_type = static_cast<u32>(vfs_entry_type);
R_SUCCEED(); R_SUCCEED();

9
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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <string>
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "core/file_sys/fs_filesystem.h" #include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/fsa/fs_i_filesystem.h" #include "core/file_sys/fsa/fs_i_filesystem.h"
@ -23,7 +28,8 @@ class IDirectory;
class IFileSystem final : public ServiceFramework<IFileSystem> { class IFileSystem final : public ServiceFramework<IFileSystem> {
public: 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<FileSys::Sf::Path, BufferAttr_HipcPointer> path, s32 option, Result CreateFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, s32 option,
s64 size); s64 size);
@ -55,6 +61,7 @@ public:
private: private:
std::unique_ptr<FileSys::Fsa::IFileSystem> backend; std::unique_ptr<FileSys::Fsa::IFileSystem> backend;
SizeGetter size_getter; SizeGetter size_getter;
std::string homebrew_initial_cwd;
}; };
} // namespace Service::FileSystem } // namespace Service::FileSystem

8
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); 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<IFileSystem> out_interface, Result FSP_SRV::OpenFileSystemWithPatch(OutInterface<IFileSystem> out_interface,
@ -224,7 +225,8 @@ Result FSP_SRV::OpenSdCardFileSystem(OutInterface<IFileSystem> out_interface) {
fsc.OpenSDMC(&sdmc_dir); fsc.OpenSDMC(&sdmc_dir);
*out_interface = std::make_shared<IFileSystem>( *out_interface = std::make_shared<IFileSystem>(
system, sdmc_dir, SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard));
system, sdmc_dir, SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard),
homebrew_initial_cwd);
R_SUCCEED(); R_SUCCEED();
} }

2
src/core/hle/service/filesystem/fsp/fsp_srv.h

@ -7,6 +7,7 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <string>
#include "core/file_sys/fs_save_data_types.h" #include "core/file_sys/fs_save_data_types.h"
#include "core/hle/service/cmif_types.h" #include "core/hle/service/cmif_types.h"
#include "core/hle/service/filesystem/fsp/fsp_types.h" #include "core/hle/service/filesystem/fsp/fsp_types.h"
@ -123,6 +124,7 @@ private:
u32 access_log_program_index = 0; u32 access_log_program_index = 0;
AccessLogMode access_log_mode = AccessLogMode::None; AccessLogMode access_log_mode = AccessLogMode::None;
u64 program_id = 0; u64 program_id = 0;
std::string homebrew_initial_cwd;
std::shared_ptr<SaveDataController> save_data_controller; std::shared_ptr<SaveDataController> save_data_controller;
std::shared_ptr<RomFsController> romfs_controller; std::shared_ptr<RomFsController> romfs_controller;
}; };

Loading…
Cancel
Save