From 634ce933be7dd5dc664fc85ddc48a8404cafb30c Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 22 Mar 2019 08:49:10 -0700 Subject: [PATCH] fs.mitm: start implementation of IFileSystem api. --- .../source/fs_mitm/fs_filesystem_types.hpp | 38 +++ .../ams_mitm/source/fs_mitm/fs_idirectory.hpp | 85 ++++++ .../ams_mitm/source/fs_mitm/fs_ifile.hpp | 142 +++++++++ .../source/fs_mitm/fs_ifilesystem.hpp | 285 ++++++++++++++++++ .../ams_mitm/source/fs_mitm/fs_istorage.hpp | 14 +- .../ams_mitm/source/fs_mitm/fs_results.hpp | 32 ++ .../source/fs_mitm/fsmitm_layeredrom.cpp | 4 +- .../source/fs_mitm/fsmitm_service.cpp | 2 + 8 files changed, 597 insertions(+), 5 deletions(-) create mode 100644 stratosphere/ams_mitm/source/fs_mitm/fs_filesystem_types.hpp create mode 100644 stratosphere/ams_mitm/source/fs_mitm/fs_idirectory.hpp create mode 100644 stratosphere/ams_mitm/source/fs_mitm/fs_ifile.hpp create mode 100644 stratosphere/ams_mitm/source/fs_mitm/fs_ifilesystem.hpp create mode 100644 stratosphere/ams_mitm/source/fs_mitm/fs_results.hpp diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_filesystem_types.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_filesystem_types.hpp new file mode 100644 index 000000000..08d056c29 --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_filesystem_types.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 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 + +enum OpenMode { + OpenMode_Read = (1 << 0), + OpenMode_Write = (1 << 1), + OpenMode_Append = (1 << 2), + + OpenMode_ReadWrite = OpenMode_Read | OpenMode_Write, + OpenMode_All = OpenMode_ReadWrite | OpenMode_Append, +}; + +enum DirectoryOpenMode { + DirectoryOpenMode_Directories = (1 << 0), + DirectoryOpenMode_Files = (1 << 1), + + DirectoryOpenMode_All = (DirectoryOpenMode_Directories | DirectoryOpenMode_Files), +}; + +enum DirectoryEntryType { + DirectoryEntryType_Directory, + DirectoryEntryType_File, +}; diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_idirectory.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_idirectory.hpp new file mode 100644 index 000000000..020b67dde --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_idirectory.hpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018 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_results.hpp" + +enum FsIDirectoryCmd : u32 { + FsIDirectoryCmd_Read = 0, + FsIDirectoryCmd_GetEntryCount = 1, +}; + +class IDirectory { + public: + virtual ~IDirectory() {} + + Result Read(uint64_t *out_count, FsDirectoryEntry *out_entries, uint64_t max_entries) { + if (out_count == nullptr) { + return ResultFsNullptrArgument; + } + if (max_entries == 0) { + *out_count = 0; + return 0; + } + if (out_entries == nullptr) { + return ResultFsNullptrArgument; + } + return ReadImpl(out_count, out_entries, max_entries); + } + + Result GetEntryCount(uint64_t *count) { + if (count == nullptr) { + return ResultFsNullptrArgument; + } + return GetEntryCountImpl(count); + } + + protected: + /* ...? */ + private: + virtual Result ReadImpl(uint64_t *out_count, FsDirectoryEntry *out_entries, uint64_t max_entries) = 0; + virtual Result GetEntryCountImpl(uint64_t *count) = 0; +}; + +class IDirectoryInterface : public IServiceObject { + private: + std::unique_ptr base_dir; + public: + IDirectoryInterface(IDirectory *d) : base_dir(d) { + /* ... */ + }; + IDirectoryInterface(std::unique_ptr d) : base_dir(std::move(d)) { + /* ... */ + }; + + private: + /* Actual command API. */ + virtual Result Read(OutBuffer buffer, Out out_count) final { + return this->base_dir->Read(out_count.GetPointer(), buffer.buffer, buffer.num_elements); + }; + virtual Result GetEntryCount(Out out_count) final { + return this->base_dir->GetEntryCount(out_count.GetPointer()); + }; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + /* 1.0.0- */ + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + }; +}; diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_ifile.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_ifile.hpp new file mode 100644 index 000000000..2a6057179 --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_ifile.hpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2018 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_results.hpp" + +enum FsIFileCmd : u32 { + FsIFileCmd_Read = 0, + FsIFileCmd_Write = 1, + FsIFileCmd_Flush = 2, + FsIFileCmd_SetSize = 3, + FsIFileCmd_GetSize = 4, + FsIFileCmd_OperateRange = 5, +}; + +class IFile { + public: + virtual ~IFile() {} + + Result Read(uint64_t *out, uint64_t offset, void *buffer, uint64_t size, uint32_t flags) { + (void)(flags); + if (out == nullptr) { + return ResultFsNullptrArgument; + } + if (size == 0) { + *out = 0; + return 0; + } + if (buffer == nullptr) { + return ResultFsNullptrArgument; + } + return ReadImpl(out, offset, buffer, size); + } + + Result GetSize(uint64_t *out) { + if (out == nullptr) { + return ResultFsNullptrArgument; + } + return GetSizeImpl(out); + } + + Result Flush() { + return FlushImpl(); + } + + Result Write(uint64_t offset, void *buffer, uint64_t size, uint32_t flags) { + if (size == 0) { + return 0; + } + if (buffer == nullptr) { + return ResultFsNullptrArgument; + } + const bool flush = (flags & 1) != 0; + return WriteImpl(offset, buffer, size, flush); + } + + Result Write(uint64_t offset, void *buffer, uint64_t size) { + return Write(offset, buffer, size, false); + } + + Result SetSize(uint64_t size) { + return SetSizeImpl(size); + } + + Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) { + if (operation_type != 3) { + return OperateRangeImpl(operation_type, offset, size, out_range_info); + } + return ResultFsUnsupportedOperation; + } + + protected: + /* ...? */ + private: + virtual Result ReadImpl(u64 *out, u64 offset, void *buffer, u64 size) = 0; + virtual Result GetSizeImpl(u64 *out) = 0; + virtual Result FlushImpl() = 0; + virtual Result WriteImpl(u64 offset, void *buffer, u64 size, bool flush) = 0; + virtual Result SetSizeImpl(u64 size) = 0; + virtual Result OperateRangeImpl(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0; +}; + +class IFileInterface : public IServiceObject { + private: + std::unique_ptr base_file; + public: + IFileInterface(IFile *f) : base_file(f) { + /* ... */ + }; + IFileInterface(std::unique_ptr f) : base_file(std::move(f)) { + /* ... */ + }; + + private: + /* Actual command API. */ + virtual Result Read(OutBuffer buffer, Out out_read, u64 offset, u64 size, u32 read_flags) final { + return this->base_file->Read(out_read.GetPointer(), offset, buffer.buffer, std::min(buffer.num_elements, size), read_flags); + }; + virtual Result Write(InBuffer buffer, u64 offset, u64 size, u32 write_flags) final { + return this->base_file->Write(offset, buffer.buffer, std::min(buffer.num_elements, size), write_flags); + }; + virtual Result Flush() final { + return this->base_file->Flush(); + }; + virtual Result SetSize(u64 size) final { + return this->base_file->SetSize(size); + }; + virtual Result GetSize(Out size) final { + return this->base_file->GetSize(size.GetPointer()); + }; + virtual Result OperateRange(Out range_info, u32 operation_type, u64 offset, u64 size) final { + return this->base_file->OperateRange(operation_type, offset, size, range_info.GetPointer()); + }; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + /* 1.0.0- */ + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + + /* 4.0.0- */ + MakeServiceCommandMeta(), + }; +}; diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_ifilesystem.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_ifilesystem.hpp new file mode 100644 index 000000000..bc855a4c1 --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_ifilesystem.hpp @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2018 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_results.hpp" +#include "fs_filesystem_types.hpp" +#include "fs_ifile.hpp" +#include "fs_idirectory.hpp" + +enum FsIFileSystemCmd : u32 { + /* 1.0.0+ */ + FsIFileSystemCmd_CreateFile = 0, + FsIFileSystemCmd_DeleteFile = 1, + FsIFileSystemCmd_CreateDirectory = 2, + FsIFileSystemCmd_DeleteDirectory = 3, + FsIFileSystemCmd_DeleteDirectoryRecursively = 4, + FsIFileSystemCmd_RenameFile = 5, + FsIFileSystemCmd_RenameDirectory = 6, + FsIFileSystemCmd_GetEntryType = 7, + FsIFileSystemCmd_OpenFile = 8, + FsIFileSystemCmd_OpenDirectory = 9, + FsIFileSystemCmd_Commit = 10, + FsIFileSystemCmd_GetFreeSpaceSize = 11, + FsIFileSystemCmd_GetTotalSpaceSize = 12, + + /* 3.0.0+ */ + FsIFileSystemCmd_CleanDirectoryRecursively = 13, + FsIFileSystemCmd_GetFileTimeStampRaw = 14, + + /* 4.0.0+ */ + FsIFileSystemCmd_QueryEntry = 15, +}; + +class IFile; +class IDirectory; + +class IFileSystem { + public: + virtual ~IFileSystem() {} + + Result CreateFile(const char *path, uint64_t size, int flags) { + if (path == nullptr) { + return ResultFsInvalidPath; + } + return CreateFileImpl(path, size, flags); + } + + Result DeleteFile(const char *path) { + if (path == nullptr) { + return ResultFsInvalidPath; + } + return DeleteFileImpl(path); + } + + Result CreateDirectory(const char *path) { + if (path == nullptr) { + return ResultFsInvalidPath; + } + return CreateDirectoryImpl(path); + } + + Result DeleteDirectory(const char *path) { + if (path == nullptr) { + return ResultFsInvalidPath; + } + return DeleteDirectoryImpl(path); + } + + Result DeleteDirectoryRecursively(const char *path) { + if (path == nullptr) { + return ResultFsInvalidPath; + } + return DeleteDirectoryRecursivelyImpl(path); + } + + Result RenameFile(const char *old_path, const char *new_path) { + if (old_path == nullptr || new_path == nullptr) { + return ResultFsInvalidPath; + } + return RenameFileImpl(old_path, new_path); + } + + Result RenameDirectory(const char *old_path, const char *new_path) { + if (old_path == nullptr || new_path == nullptr) { + return ResultFsInvalidPath; + } + return RenameDirectoryImpl(old_path, new_path); + } + + Result GetEntryType(DirectoryEntryType *out, const char *path) { + if (out == nullptr) { + return ResultFsNullptrArgument; + } + if (path == nullptr) { + return ResultFsInvalidPath; + } + return GetEntryTypeImpl(out, path); + } + + Result OpenFile(std::unique_ptr *out_file, const char *path, OpenMode mode) { + if (path == nullptr) { + return ResultFsInvalidPath; + } + if (out_file == nullptr) { + return ResultFsNullptrArgument; + } + if (!(mode & OpenMode_ReadWrite)) { + return ResultFsInvalidArgument; + } + if (mode & ~OpenMode_All) { + return ResultFsInvalidArgument; + } + return OpenFileImpl(out_file, path, mode); + } + + Result OpenDirectory(std::unique_ptr *out_dir, const char *path, DirectoryOpenMode mode) { + if (path == nullptr) { + return ResultFsInvalidPath; + } + if (out_dir == nullptr) { + return ResultFsNullptrArgument; + } + if (!(mode & DirectoryOpenMode_All)) { + return ResultFsInvalidArgument; + } + if (mode & ~DirectoryOpenMode_All) { + return ResultFsInvalidArgument; + } + return OpenDirectory(out_dir, path, mode); + } + + Result Commit() { + return CommitImpl(); + } + + Result GetFreeSpaceSize(uint64_t *out, const char *path) { + if (path == nullptr) { + return ResultFsInvalidPath; + } + if (out == nullptr) { + return ResultFsNullptrArgument; + } + return GetFreeSpaceSizeImpl(out, path); + } + + Result GetTotalSpaceSize(uint64_t *out, const char *path) { + if (path == nullptr) { + return ResultFsInvalidPath; + } + if (out == nullptr) { + return ResultFsNullptrArgument; + } + return GetTotalSpaceSizeImpl(out, path); + } + + Result CleanDirectoryRecursively(const char *path) { + if (path == nullptr) { + return ResultFsInvalidPath; + } + return CleanDirectoryRecursivelyImpl(path); + } + + Result GetFileTimeStampRaw(FsTimeStampRaw *out, const char *path) { + if (path == nullptr) { + return ResultFsInvalidPath; + } + if (out == nullptr) { + return ResultFsNullptrArgument; + } + return GetFileTimeStampRawImpl(out, path); + } + + Result QueryEntry(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const char *path) { + if (path == nullptr) { + return ResultFsInvalidPath; + } + return QueryEntryImpl(out, out_size, in, in_size, query, path); + } + + + protected: + /* ...? */ + private: + virtual Result CreateFileImpl(const char *path, uint64_t size, int flags) = 0; + virtual Result DeleteFileImpl(const char *path) = 0; + virtual Result CreateDirectoryImpl(const char *path) = 0; + virtual Result DeleteDirectoryImpl(const char *path) = 0; + virtual Result DeleteDirectoryRecursivelyImpl(const char *path) = 0; + virtual Result RenameFileImpl(const char *old_path, const char *new_path) = 0; + virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) = 0; + virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const char *path) = 0; + virtual Result OpenFileImpl(std::unique_ptr *out_file, const char *path, OpenMode mode) = 0; + virtual Result OpenDirectoryImpl(std::unique_ptr *out_dir, const char *path, DirectoryOpenMode mode) = 0; + virtual Result CommitImpl() = 0; + + virtual Result GetFreeSpaceSizeImpl(uint64_t *out, const char *path) { + (void)(out); + (void)(path); + return ResultFsNotImplemented; + } + + virtual Result GetTotalSpaceSizeImpl(uint64_t *out, const char *path) { + (void)(out); + (void)(path); + return ResultFsNotImplemented; + } + + virtual Result CleanDirectoryRecursivelyImpl(const char *path) = 0; + + virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, const char *path) { + (void)(out); + (void)(path); + return ResultFsNotImplemented; + } + + virtual Result QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const char *path) { + (void)(out); + (void)(out_size); + (void)(in); + (void)(in_size); + (void)(query); + (void)(path); + return ResultFsNotImplemented; + } +}; + +class IFileSystemInterface : public IServiceObject { + private: + std::unique_ptr base_fs; + public: + IFileSystemInterface(IFileSystem *fs) : base_fs(fs) { + /* ... */ + }; + IFileSystemInterface(std::unique_ptr fs) : base_fs(std::move(fs)) { + /* ... */ + }; + + private: + /* Actual command API. */ + /* TODO.... */ + public: + DEFINE_SERVICE_DISPATCH_TABLE { + /* 1.0.0- */ + /* + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + */ + + /* 3.0.0- */ + /* + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + */ + + /* 4.0.0- */ + /* (), */ + }; +}; diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_istorage.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_istorage.hpp index 77e58302d..be019ae12 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fs_istorage.hpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_istorage.hpp @@ -19,6 +19,8 @@ #include #include "fs_shim.h" +#include "fs_results.hpp" + #include "../debug.hpp" enum FsIStorageCmd : u32 { @@ -33,13 +35,17 @@ enum FsIStorageCmd : u32 { class IStorage { public: virtual ~IStorage(); - + virtual Result Read(void *buffer, size_t size, u64 offset) = 0; virtual Result Write(void *buffer, size_t size, u64 offset) = 0; virtual Result Flush() = 0; virtual Result SetSize(u64 size) = 0; virtual Result GetSize(u64 *out_size) = 0; virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0; + + static inline bool IsRangeValid(uint64_t offset, uint64_t size, uint64_t total_size) { + return size <= total_size && offset <= total_size - size; + } }; class IStorageInterface : public IServiceObject { @@ -49,7 +55,7 @@ class IStorageInterface : public IServiceObject { IStorageInterface(IStorage *s) : base_storage(s) { /* ... */ }; - + ~IStorageInterface() { delete base_storage; }; @@ -95,14 +101,14 @@ class IROStorage : public IStorage { (void)(buffer); (void)(offset); (void)(size); - return 0x313802; + return ResultFsUnsupportedOperation; }; virtual Result Flush() final { return 0x0; }; virtual Result SetSize(u64 size) final { (void)(size); - return 0x313802; + return ResultFsUnsupportedOperation; }; virtual Result GetSize(u64 *out_size) = 0; virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0; diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_results.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_results.hpp new file mode 100644 index 000000000..de45e90f9 --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_results.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 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 + +static constexpr u32 Module_Fs = 2; + +static constexpr Result ResultFsNotImplemented = MAKERESULT(Module_Fs, 3001); +static constexpr Result ResultFsOutOfRange = MAKERESULT(Module_Fs, 3005); + +static constexpr Result ResultFsInvalidArgument = MAKERESULT(Module_Fs, 6001); +static constexpr Result ResultFsInvalidPath = MAKERESULT(Module_Fs, 6002); + +static constexpr Result ResultFsInvalidOffset = MAKERESULT(Module_Fs, 6061); +static constexpr Result ResultFsInvalidSize = MAKERESULT(Module_Fs, 6062); +static constexpr Result ResultFsNullptrArgument = MAKERESULT(Module_Fs, 6063); + +static constexpr Result ResultFsUnsupportedOperation = MAKERESULT(Module_Fs, 6300); diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layeredrom.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layeredrom.cpp index fe81b7eeb..a82c733de 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layeredrom.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_layeredrom.cpp @@ -17,6 +17,8 @@ #include #include +#include "fs_results.hpp" + #include "fsmitm_layeredrom.hpp" #include "../utils.hpp" #include "../debug.hpp" @@ -54,7 +56,7 @@ Result LayeredRomFS::Read(void *buffer, size_t size, u64 offset) { /* Validate size. */ u64 virt_size = (*this->p_source_infos)[this->p_source_infos->size() - 1].virtual_offset + (*this->p_source_infos)[this->p_source_infos->size() - 1].size; if (offset >= virt_size) { - return 0x2F5A02; + return ResultFsInvalidOffset; } if (virt_size - offset < size) { size = virt_size - offset; diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp index 40b4b70f6..79d490981 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp @@ -28,6 +28,8 @@ #include "fsmitm_romstorage.hpp" #include "fsmitm_layeredrom.hpp" +#include "fs_ifilesystem.hpp" + #include "../debug.hpp" static HosMutex g_StorageCacheLock;