fs.mitm: add Nintendo directory redirection code

This commit is contained in:
Michael Scire 2019-05-27 11:48:17 -07:00
parent cb532e3fb3
commit 4dac80df75
5 changed files with 401 additions and 1 deletions

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <cstring>
#include <switch.h>
#include <stratosphere.hpp>
#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<char *>(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<IFile> &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<IDirectory> &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);
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "fs_ifilesystem.hpp"
#include "fs_path_utils.hpp"
class DirectoryRedirectionFileSystem : public IFileSystem {
private:
std::shared_ptr<IFileSystem> 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<IFileSystem> 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<IFile> &out_file, const FsPath &path, OpenMode mode) override;
virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> &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;
};

View file

@ -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(Out<std::shared_ptr<IFileSystemInterf
return this->OpenHblWebContentFileSystem(out_fs);
}
Result FsMitmService::OpenSdCardFileSystem(Out<std::shared_ptr<IFileSystemInterface>> 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<IFileSystemInterface> 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<IFileSystem> redir_fs = std::make_shared<DirectoryRedirectionFileSystem>(new ProxyFileSystem(sd_fs), "/Nintendo", "/Emu/0000" /* TODO: Real Path */);
fs = std::make_shared<IFileSystemInterface>(redir_fs);
if (out_fs.IsDomain()) {
out_domain_id = sd_fs.s.object_id;
}
return rc;
}
Result FsMitmService::OpenSaveDataFileSystem(Out<std::shared_ptr<IFileSystemInterface>> 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");

View file

@ -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<std::shared_ptr<IFileSystemInterface>> out, u64 title_id, u32 filesystem_type);
Result OpenFileSystemWithId(Out<std::shared_ptr<IFileSystemInterface>> out, InPointer<char> path, u64 title_id, u32 filesystem_type);
Result OpenSdCardFileSystem(Out<std::shared_ptr<IFileSystemInterface>> out);
Result OpenSaveDataFileSystem(Out<std::shared_ptr<IFileSystemInterface>> out, u8 space_id, FsSave save_struct);
Result OpenBisStorage(Out<std::shared_ptr<IStorageInterface>> out, u32 bis_partition_id);
Result OpenDataStorageByCurrentProcess(Out<std::shared_ptr<IStorageInterface>> out);
@ -84,6 +87,7 @@ class FsMitmService : public IMitmServiceObject {
/* TODO MakeServiceCommandMeta<FspSrvCmd_OpenFileSystemDeprecated, &FsMitmService::OpenFileSystemDeprecated>(), */
MakeServiceCommandMeta<FspSrvCmd_OpenFileSystemWithPatch, &FsMitmService::OpenFileSystemWithPatch, FirmwareVersion_200>(),
MakeServiceCommandMeta<FspSrvCmd_OpenFileSystemWithId, &FsMitmService::OpenFileSystemWithId, FirmwareVersion_200>(),
MakeServiceCommandMeta<FspSrvCmd_OpenSdCardFileSystem, &FsMitmService::OpenSdCardFileSystem>(),
MakeServiceCommandMeta<FspSrvCmd_OpenSaveDataFileSystem, &FsMitmService::OpenSaveDataFileSystem>(),
MakeServiceCommandMeta<FspSrvCmd_OpenBisStorage, &FsMitmService::OpenBisStorage>(),
MakeServiceCommandMeta<FspSrvCmd_OpenDataStorageByCurrentProcess, &FsMitmService::OpenDataStorageByCurrentProcess>(),

@ -1 +1 @@
Subproject commit 8f2328975a66519275ada907cbc0000377ce5ede
Subproject commit 44f52b445e9154a18fc0e99b9efc6be78b859495