From 4dac80df752d7b5a1880d8110fbc66d95a74abee Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 27 May 2019 11:48:17 -0700 Subject: [PATCH] fs.mitm: add Nintendo directory redirection code --- .../fs_directory_redirection_filesystem.cpp | 276 ++++++++++++++++++ .../fs_directory_redirection_filesystem.hpp | 82 ++++++ .../source/fs_mitm/fsmitm_service.cpp | 38 +++ .../source/fs_mitm/fsmitm_service.hpp | 4 + stratosphere/libstratosphere | 2 +- 5 files changed, 401 insertions(+), 1 deletion(-) create mode 100644 stratosphere/ams_mitm/source/fs_mitm/fs_directory_redirection_filesystem.cpp create mode 100644 stratosphere/ams_mitm/source/fs_mitm/fs_directory_redirection_filesystem.hpp diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_directory_redirection_filesystem.cpp b/stratosphere/ams_mitm/source/fs_mitm/fs_directory_redirection_filesystem.cpp new file mode 100644 index 000000000..52f0a71ca --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_directory_redirection_filesystem.cpp @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "../utils.hpp" +#include "fs_directory_redirection_filesystem.hpp" +#include "fs_path_utils.hpp" + +static char *GetNormalizedDirectory(const char *dir_prefix) { + /* Normalize the path. */ + char normal_path[FS_MAX_PATH + 1]; + size_t normal_path_len; + Result rc = FsPathUtils::Normalize(normal_path, sizeof(normal_path), dir_prefix, &normal_path_len); + if (R_FAILED(rc)) { + /* N calls svcBreak here. */ + std::abort(); + } + + /* Ensure terminating '/' */ + if (normal_path[normal_path_len-1] != '/') { + if (normal_path_len + 2 > sizeof(normal_path)) { + std::abort(); + } + + strncat(normal_path, "/", 2); + normal_path[sizeof(normal_path)-1] = 0; + normal_path_len++; + } + + char *output = static_cast(malloc(normal_path_len + 1)); + if (output == nullptr) { + std::abort(); + /* TODO: Result error code? */ + } + + std::strncpy(output, normal_path, normal_path_len + 1); + output[normal_path_len] = 0; + return output; +} + +Result DirectoryRedirectionFileSystem::Initialize(const char *before, const char *after) { + if (strnlen(before, FS_MAX_PATH) >= FS_MAX_PATH || strnlen(after, FS_MAX_PATH) >= FS_MAX_PATH) { + return ResultFsTooLongPath; + } + + this->before_dir = GetNormalizedDirectory(before); + this->after_dir = GetNormalizedDirectory(after); + this->before_dir_len = strlen(this->before_dir); + this->after_dir_len = strlen(this->after_dir); + + return ResultSuccess; +} + +Result DirectoryRedirectionFileSystem::GetFullPath(char *out, size_t out_size, const char *relative_path) { + FsPath tmp_rel_path; + Result rc = FsPathUtils::Normalize(tmp_rel_path.str, sizeof(tmp_rel_path), relative_path, nullptr); + if (R_FAILED(rc)) { + return rc; + } + + if (std::strncmp(tmp_rel_path.str, this->before_dir, this->before_dir_len) == 0) { + if (this->after_dir_len + strnlen(tmp_rel_path.str, FS_MAX_PATH) - this->before_dir_len > out_size) { + return ResultFsTooLongPath; + } + + /* Copy after path. */ + std::strncpy(out, this->after_dir, out_size); + out[out_size - 1] = 0; + + /* Normalize it. */ + return FsPathUtils::Normalize(out + this->after_dir_len - 1, out_size - (this->after_dir_len - 1), relative_path + this->before_dir_len - 1, nullptr); + } else if (std::memcmp(tmp_rel_path.str, this->before_dir, this->before_dir_len - 1) == 0) { + /* Handling raw directory. */ + if (this->after_dir_len + 1 > out_size) { + return ResultFsTooLongPath; + } + std::strncpy(out, this->after_dir, out_size); + out[out_size - 1] = 0; + return ResultSuccess; + } else { + return FsPathUtils::Normalize(out, out_size, relative_path, nullptr); + } +} + +Result DirectoryRedirectionFileSystem::CreateFileImpl(const FsPath &path, uint64_t size, int flags) { + Result rc; + FsPath full_path; + + if (R_FAILED((rc = GetFullPath(full_path, path)))) { + return rc; + } + + return this->base_fs->CreateFile(full_path, size, flags); +} + +Result DirectoryRedirectionFileSystem::DeleteFileImpl(const FsPath &path) { + Result rc; + FsPath full_path; + + if (R_FAILED((rc = GetFullPath(full_path, path)))) { + return rc; + } + + return this->base_fs->DeleteFile(full_path); +} + +Result DirectoryRedirectionFileSystem::CreateDirectoryImpl(const FsPath &path) { + Result rc; + FsPath full_path; + + if (R_FAILED((rc = GetFullPath(full_path, path)))) { + return rc; + } + + return this->base_fs->CreateDirectory(full_path); +} + +Result DirectoryRedirectionFileSystem::DeleteDirectoryImpl(const FsPath &path) { + Result rc; + FsPath full_path; + + if (R_FAILED((rc = GetFullPath(full_path, path)))) { + return rc; + } + + return this->base_fs->DeleteDirectory(full_path); +} + +Result DirectoryRedirectionFileSystem::DeleteDirectoryRecursivelyImpl(const FsPath &path) { + Result rc; + FsPath full_path; + + if (R_FAILED((rc = GetFullPath(full_path, path)))) { + return rc; + } + + return this->base_fs->DeleteDirectoryRecursively(full_path); +} + +Result DirectoryRedirectionFileSystem::RenameFileImpl(const FsPath &old_path, const FsPath &new_path) { + Result rc; + FsPath full_old_path, full_new_path; + + if (R_FAILED((rc = GetFullPath(full_old_path, old_path)))) { + return rc; + } + + if (R_FAILED((rc = GetFullPath(full_new_path, new_path)))) { + return rc; + } + + return this->base_fs->RenameFile(full_old_path, full_new_path); +} + +Result DirectoryRedirectionFileSystem::RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) { + Result rc; + FsPath full_old_path, full_new_path; + + if (R_FAILED((rc = GetFullPath(full_old_path, old_path)))) { + return rc; + } + + if (R_FAILED((rc = GetFullPath(full_new_path, new_path)))) { + return rc; + } + + return this->base_fs->RenameDirectory(full_old_path, full_new_path); +} + +Result DirectoryRedirectionFileSystem::GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) { + Result rc; + FsPath full_path; + + if (R_FAILED((rc = GetFullPath(full_path, path)))) { + return rc; + } + + return this->base_fs->GetEntryType(out, full_path); +} + +Result DirectoryRedirectionFileSystem::OpenFileImpl(std::unique_ptr &out_file, const FsPath &path, OpenMode mode) { + Result rc; + FsPath full_path; + + if (R_FAILED((rc = GetFullPath(full_path, path)))) { + return rc; + } + + return this->base_fs->OpenFile(out_file, full_path, mode); +} + +Result DirectoryRedirectionFileSystem::OpenDirectoryImpl(std::unique_ptr &out_dir, const FsPath &path, DirectoryOpenMode mode) { + Result rc; + FsPath full_path; + + if (R_FAILED((rc = GetFullPath(full_path, path)))) { + return rc; + } + + return this->base_fs->OpenDirectory(out_dir, full_path, mode); +} + +Result DirectoryRedirectionFileSystem::CommitImpl() { + return this->base_fs->Commit(); +} + +Result DirectoryRedirectionFileSystem::GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) { + Result rc; + FsPath full_path; + + if (R_FAILED((rc = GetFullPath(full_path, path)))) { + return rc; + } + + return this->base_fs->GetFreeSpaceSize(out, full_path); +} + +Result DirectoryRedirectionFileSystem::GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) { + Result rc; + FsPath full_path; + + if (R_FAILED((rc = GetFullPath(full_path, path)))) { + return rc; + } + + return this->base_fs->GetTotalSpaceSize(out, full_path); +} + +Result DirectoryRedirectionFileSystem::CleanDirectoryRecursivelyImpl(const FsPath &path) { + Result rc; + FsPath full_path; + + if (R_FAILED((rc = GetFullPath(full_path, path)))) { + return rc; + } + + return this->base_fs->CleanDirectoryRecursively(full_path); +} + +Result DirectoryRedirectionFileSystem::GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) { + Result rc; + FsPath full_path; + + if (R_FAILED((rc = GetFullPath(full_path, path)))) { + return rc; + } + + return this->base_fs->GetFileTimeStampRaw(out, full_path); +} + +Result DirectoryRedirectionFileSystem::QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) { + Result rc; + FsPath full_path; + + if (R_FAILED((rc = GetFullPath(full_path, path)))) { + return rc; + } + + return this->base_fs->QueryEntry(out, out_size, in, in_size, query, full_path); +} diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_directory_redirection_filesystem.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_directory_redirection_filesystem.hpp new file mode 100644 index 000000000..95d7b342b --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_directory_redirection_filesystem.hpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "fs_ifilesystem.hpp" +#include "fs_path_utils.hpp" + +class DirectoryRedirectionFileSystem : public IFileSystem { + private: + std::shared_ptr base_fs; + char *before_dir = nullptr; + size_t before_dir_len = 0; + char *after_dir = nullptr; + size_t after_dir_len = 0; + + public: + DirectoryRedirectionFileSystem(IFileSystem *fs, const char *before, const char *after) : base_fs(fs) { + Result rc = this->Initialize(before, after); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + } + + DirectoryRedirectionFileSystem(std::shared_ptr fs, const char *before, const char *after) : base_fs(fs) { + Result rc = this->Initialize(before, after); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + } + + + virtual ~DirectoryRedirectionFileSystem() { + if (this->before_dir != nullptr) { + free(this->before_dir); + } + if (this->after_dir != nullptr) { + free(this->after_dir); + } + } + + private: + Result Initialize(const char *before, const char *after); + protected: + Result GetFullPath(char *out, size_t out_size, const char *relative_path); + Result GetFullPath(FsPath &full_path, const FsPath &relative_path) { + return GetFullPath(full_path.str, sizeof(full_path.str), relative_path.str); + } + + public: + virtual Result CreateFileImpl(const FsPath &path, uint64_t size, int flags) override; + virtual Result DeleteFileImpl(const FsPath &path) override; + virtual Result CreateDirectoryImpl(const FsPath &path) override; + virtual Result DeleteDirectoryImpl(const FsPath &path) override; + virtual Result DeleteDirectoryRecursivelyImpl(const FsPath &path) override; + virtual Result RenameFileImpl(const FsPath &old_path, const FsPath &new_path) override; + virtual Result RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) override; + virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) override; + virtual Result OpenFileImpl(std::unique_ptr &out_file, const FsPath &path, OpenMode mode) override; + virtual Result OpenDirectoryImpl(std::unique_ptr &out_dir, const FsPath &path, DirectoryOpenMode mode) override; + virtual Result CommitImpl() override; + virtual Result GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) override; + virtual Result GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) override; + virtual Result CleanDirectoryRecursivelyImpl(const FsPath &path) override; + virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) override; + virtual Result QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) override; +}; \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp index 3ce545193..991d4113f 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp @@ -31,6 +31,7 @@ #include "fs_dir_utils.hpp" #include "fs_save_utils.hpp" #include "fs_subdirectory_filesystem.hpp" +#include "fs_directory_redirection_filesystem.hpp" #include "fs_directory_savedata_filesystem.hpp" #include "../debug.hpp" @@ -161,6 +162,43 @@ Result FsMitmService::OpenFileSystemWithId(OutOpenHblWebContentFileSystem(out_fs); } +Result FsMitmService::OpenSdCardFileSystem(Out> out_fs) { + /* We only care about redirecting this for NS/Emunand. */ + if (!IsEmunand()) { + return ResultAtmosphereMitmShouldForwardToSession; + } + if (this->title_id != TitleId_Ns) { + return ResultAtmosphereMitmShouldForwardToSession; + } + + std::shared_ptr fs = nullptr; + u32 out_domain_id = 0; + Result rc = ResultSuccess; + + ON_SCOPE_EXIT { + if (R_SUCCEEDED(rc)) { + out_fs.SetValue(std::move(fs)); + if (out_fs.IsDomain()) { + out_fs.ChangeObjectId(out_domain_id); + } + } + }; + + /* Mount the SD card. */ + FsFileSystem sd_fs; + if (R_FAILED((rc = fsMountSdcard(&sd_fs)))) { + return rc; + } + + std::shared_ptr redir_fs = std::make_shared(new ProxyFileSystem(sd_fs), "/Nintendo", "/Emu/0000" /* TODO: Real Path */); + fs = std::make_shared(redir_fs); + if (out_fs.IsDomain()) { + out_domain_id = sd_fs.s.object_id; + } + + return rc; +} + Result FsMitmService::OpenSaveDataFileSystem(Out> out_fs, u8 space_id, FsSave save_struct) { bool should_redirect_saves = false; const bool has_redirect_save_flags = Utils::HasFlag(this->title_id, "redirect_save"); diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.hpp index 1e818aba2..54f8b6ff6 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.hpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.hpp @@ -29,6 +29,8 @@ enum FspSrvCmd : u32 { FspSrvCmd_OpenFileSystemWithPatch = 7, FspSrvCmd_OpenFileSystemWithId = 8, + FspSrvCmd_OpenSdCardFileSystem = 18, + FspSrvCmd_OpenSaveDataFileSystem = 51, FspSrvCmd_OpenBisStorage = 12, @@ -75,6 +77,7 @@ class FsMitmService : public IMitmServiceObject { /* Overridden commands. */ Result OpenFileSystemWithPatch(Out> out, u64 title_id, u32 filesystem_type); Result OpenFileSystemWithId(Out> out, InPointer path, u64 title_id, u32 filesystem_type); + Result OpenSdCardFileSystem(Out> out); Result OpenSaveDataFileSystem(Out> out, u8 space_id, FsSave save_struct); Result OpenBisStorage(Out> out, u32 bis_partition_id); Result OpenDataStorageByCurrentProcess(Out> out); @@ -84,6 +87,7 @@ class FsMitmService : public IMitmServiceObject { /* TODO MakeServiceCommandMeta(), */ MakeServiceCommandMeta(), MakeServiceCommandMeta(), + MakeServiceCommandMeta(), MakeServiceCommandMeta(), MakeServiceCommandMeta(), MakeServiceCommandMeta(), diff --git a/stratosphere/libstratosphere b/stratosphere/libstratosphere index 8f2328975..44f52b445 160000 --- a/stratosphere/libstratosphere +++ b/stratosphere/libstratosphere @@ -1 +1 @@ -Subproject commit 8f2328975a66519275ada907cbc0000377ce5ede +Subproject commit 44f52b445e9154a18fc0e99b9efc6be78b859495