diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_ifilesystem.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_ifilesystem.hpp index bc855a4c1..8c5ebebb7 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fs_ifilesystem.hpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_ifilesystem.hpp @@ -20,6 +20,8 @@ #include "fs_results.hpp" #include "fs_filesystem_types.hpp" +#include "fs_path_utils.hpp" + #include "fs_ifile.hpp" #include "fs_idirectory.hpp" @@ -38,85 +40,58 @@ enum FsIFileSystemCmd : u32 { 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; - } + + Result CreateFile(FsPath &path, uint64_t size, int flags) { return CreateFileImpl(path, size, flags); } - - Result DeleteFile(const char *path) { - if (path == nullptr) { - return ResultFsInvalidPath; - } - return DeleteFileImpl(path); + + Result DeleteFile(FsPath &path) { + return DeleteFileImpl(path); } - - Result CreateDirectory(const char *path) { - if (path == nullptr) { - return ResultFsInvalidPath; - } - return CreateDirectoryImpl(path); + + Result CreateDirectory(FsPath &path) { + return CreateDirectoryImpl(path); } - - Result DeleteDirectory(const char *path) { - if (path == nullptr) { - return ResultFsInvalidPath; - } - return DeleteDirectoryImpl(path); + + Result DeleteDirectory(FsPath &path) { + return DeleteDirectoryImpl(path); } - - Result DeleteDirectoryRecursively(const char *path) { - if (path == nullptr) { - return ResultFsInvalidPath; - } - return DeleteDirectoryRecursivelyImpl(path); + + Result DeleteDirectoryRecursively(FsPath &path) { + return DeleteDirectoryRecursivelyImpl(path); } - - Result RenameFile(const char *old_path, const char *new_path) { - if (old_path == nullptr || new_path == nullptr) { - return ResultFsInvalidPath; - } + + Result RenameFile(FsPath &old_path, FsPath &new_path) { 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; - } + + Result RenameDirectory(FsPath &old_path, FsPath &new_path) { return RenameDirectoryImpl(old_path, new_path); } - - Result GetEntryType(DirectoryEntryType *out, const char *path) { + + Result GetEntryType(DirectoryEntryType *out, FsPath &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; - } + + Result OpenFile(std::unique_ptr &out_file, FsPath &path, OpenMode mode) { if (out_file == nullptr) { return ResultFsNullptrArgument; } @@ -128,11 +103,8 @@ class IFileSystem { } return OpenFileImpl(out_file, path, mode); } - - Result OpenDirectory(std::unique_ptr *out_dir, const char *path, DirectoryOpenMode mode) { - if (path == nullptr) { - return ResultFsInvalidPath; - } + + Result OpenDirectory(std::unique_ptr &out_dir, FsPath &path, DirectoryOpenMode mode) { if (out_dir == nullptr) { return ResultFsNullptrArgument; } @@ -144,92 +116,77 @@ class IFileSystem { } return OpenDirectory(out_dir, path, mode); } - + Result Commit() { return CommitImpl(); } - - Result GetFreeSpaceSize(uint64_t *out, const char *path) { - if (path == nullptr) { - return ResultFsInvalidPath; - } + + Result GetFreeSpaceSize(uint64_t *out, FsPath &path) { if (out == nullptr) { return ResultFsNullptrArgument; } return GetFreeSpaceSizeImpl(out, path); } - - Result GetTotalSpaceSize(uint64_t *out, const char *path) { - if (path == nullptr) { - return ResultFsInvalidPath; - } + + Result GetTotalSpaceSize(uint64_t *out, FsPath &path) { if (out == nullptr) { return ResultFsNullptrArgument; } return GetTotalSpaceSizeImpl(out, path); } - - Result CleanDirectoryRecursively(const char *path) { - if (path == nullptr) { - return ResultFsInvalidPath; - } - return CleanDirectoryRecursivelyImpl(path); + + Result CleanDirectoryRecursively(FsPath &path) { + return CleanDirectoryRecursivelyImpl(path); } - - Result GetFileTimeStampRaw(FsTimeStampRaw *out, const char *path) { - if (path == nullptr) { - return ResultFsInvalidPath; - } + + Result GetFileTimeStampRaw(FsTimeStampRaw *out, FsPath &path) { 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; - } + + Result QueryEntry(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, FsPath &path) { 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 CreateFileImpl(FsPath &path, uint64_t size, int flags) = 0; + virtual Result DeleteFileImpl(FsPath &path) = 0; + virtual Result CreateDirectoryImpl(FsPath &path) = 0; + virtual Result DeleteDirectoryImpl(FsPath &path) = 0; + virtual Result DeleteDirectoryRecursivelyImpl(FsPath &path) = 0; + virtual Result RenameFileImpl(FsPath &old_path, FsPath &new_path) = 0; + virtual Result RenameDirectoryImpl(FsPath &old_path, FsPath &new_path) = 0; + virtual Result GetEntryTypeImpl(DirectoryEntryType *out, FsPath &path) = 0; + virtual Result OpenFileImpl(std::unique_ptr &out_file, FsPath &path, OpenMode mode) = 0; + virtual Result OpenDirectoryImpl(std::unique_ptr &out_dir, FsPath &path, DirectoryOpenMode mode) = 0; virtual Result CommitImpl() = 0; - - virtual Result GetFreeSpaceSizeImpl(uint64_t *out, const char *path) { + + virtual Result GetFreeSpaceSizeImpl(uint64_t *out, FsPath &path) { (void)(out); (void)(path); return ResultFsNotImplemented; } - - virtual Result GetTotalSpaceSizeImpl(uint64_t *out, const char *path) { + + virtual Result GetTotalSpaceSizeImpl(uint64_t *out, FsPath &path) { (void)(out); (void)(path); return ResultFsNotImplemented; } - - virtual Result CleanDirectoryRecursivelyImpl(const char *path) = 0; - - virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, const char *path) { + + virtual Result CleanDirectoryRecursivelyImpl(FsPath &path) = 0; + + virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, FsPath &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) { + + virtual Result QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, FsPath &path) { (void)(out); (void)(out_size); (void)(in); @@ -250,36 +207,225 @@ class IFileSystemInterface : public IServiceObject { IFileSystemInterface(std::unique_ptr fs) : base_fs(std::move(fs)) { /* ... */ }; - + private: /* Actual command API. */ - /* TODO.... */ + virtual Result CreateFile(InPointer in_path, uint64_t size, int flags) final { + FsPath path; + + Result rc; + if (R_FAILED((rc = FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer)))) { + return rc; + } + + return this->base_fs->CreateFile(path, size, flags); + } + + virtual Result DeleteFile(InPointer in_path) final { + FsPath path; + + Result rc; + if (R_FAILED((rc = FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer)))) { + return rc; + } + + return this->base_fs->DeleteFile(path); + } + + virtual Result CreateDirectory(InPointer in_path) final { + FsPath path; + + Result rc; + if (R_FAILED((rc = FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer)))) { + return rc; + } + + return this->base_fs->CreateDirectory(path); + } + + virtual Result DeleteDirectory(InPointer in_path) final { + FsPath path; + + Result rc; + if (R_FAILED((rc = FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer)))) { + return rc; + } + + return this->base_fs->DeleteDirectory(path); + } + + virtual Result DeleteDirectoryRecursively(InPointer in_path) final { + FsPath path; + + Result rc; + if (R_FAILED((rc = FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer)))) { + return rc; + } + + return this->base_fs->DeleteDirectoryRecursively(path); + } + + virtual Result RenameFile(InPointer in_old_path, InPointer in_new_path) final { + FsPath old_path; + FsPath new_path; + + Result rc; + if (R_FAILED((rc = FsPathUtils::ConvertPathForServiceObject(&old_path, in_old_path.pointer)))) { + return rc; + } + if (R_FAILED((rc = FsPathUtils::ConvertPathForServiceObject(&new_path, in_new_path.pointer)))) { + return rc; + } + + return this->base_fs->RenameFile(old_path, new_path); + } + + virtual Result RenameDirectory(InPointer in_old_path, InPointer in_new_path) final { + FsPath old_path; + FsPath new_path; + + Result rc; + if (R_FAILED((rc = FsPathUtils::ConvertPathForServiceObject(&old_path, in_old_path.pointer)))) { + return rc; + } + if (R_FAILED((rc = FsPathUtils::ConvertPathForServiceObject(&new_path, in_new_path.pointer)))) { + return rc; + } + + return this->base_fs->RenameDirectory(old_path, new_path); + } + + + virtual Result GetEntryType(Out out_type, InPointer in_path) final { + FsPath path; + + Result rc; + if (R_FAILED((rc = FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer)))) { + return rc; + } + + DirectoryEntryType type; + rc = this->base_fs->GetEntryType(&type, path); + if (R_SUCCEEDED(rc)) { + out_type.SetValue(type); + } + return rc; + } + + virtual Result OpenFile(Out> out_intf, InPointer in_path, uint32_t mode) final { + FsPath path; + std::unique_ptr out_file; + + Result rc; + if (R_FAILED((rc = FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer)))) { + return rc; + } + + rc = this->base_fs->OpenFile(out_file, path, static_cast(mode)); + if (R_SUCCEEDED(rc)) { + out_intf.SetValue(std::make_shared(std::move(out_file))); + /* TODO: Nintendo checks allocation success here, should we?. */ + } + return rc; + } + + virtual Result OpenDirectory(Out> out_intf, InPointer in_path, uint32_t mode) final { + FsPath path; + std::unique_ptr out_dir; + + Result rc; + if (R_FAILED((rc = FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer)))) { + return rc; + } + + rc = this->base_fs->OpenDirectory(out_dir, path, static_cast(mode)); + if (R_SUCCEEDED(rc)) { + out_intf.SetValue(std::make_shared(std::move(out_dir))); + /* TODO: Nintendo checks allocation success here, should we?. */ + } + return rc; + } + + virtual Result Commit() final { + return this->base_fs->Commit(); + } + + virtual Result GetFreeSpaceSize(Out out_size, InPointer in_path) final { + FsPath path; + + Result rc; + if (R_FAILED((rc = FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer)))) { + return rc; + } + + return this->base_fs->GetFreeSpaceSize(out_size.GetPointer(), path); + } + + virtual Result GetTotalSpaceSize(Out out_size, InPointer in_path) final { + FsPath path; + + Result rc; + if (R_FAILED((rc = FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer)))) { + return rc; + } + + return this->base_fs->GetTotalSpaceSize(out_size.GetPointer(), path); + } + + virtual Result CleanDirectoryRecursively(InPointer in_path) final { + FsPath path; + + Result rc; + if (R_FAILED((rc = FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer)))) { + return rc; + } + + return this->base_fs->CleanDirectoryRecursively(path); + } + + virtual Result GetFileTimeStampRaw(Out out_timestamp, InPointer in_path) final { + FsPath path; + + Result rc; + if (R_FAILED((rc = FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer)))) { + return rc; + } + + return this->base_fs->GetFileTimeStampRaw(out_timestamp.GetPointer(), path); + } + + virtual Result QueryEntry(OutBuffer out_buffer, InBuffer in_buffer, int query, InPointer in_path) final { + FsPath path; + + Result rc; + if (R_FAILED((rc = FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer)))) { + return rc; + } + + return this->base_fs->QueryEntry(out_buffer.buffer, out_buffer.num_elements, in_buffer.buffer, in_buffer.num_elements, query, path); + } public: DEFINE_SERVICE_DISPATCH_TABLE { /* 1.0.0- */ - /* - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - */ + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), /* 3.0.0- */ - /* - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - */ + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), /* 4.0.0- */ - /* (), */ + MakeServiceCommandMeta(), }; }; diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_path_utils.cpp b/stratosphere/ams_mitm/source/fs_mitm/fs_path_utils.cpp new file mode 100644 index 000000000..0faa04fcd --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_path_utils.cpp @@ -0,0 +1,73 @@ +/* + * 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 . + */ +#include +#include +#include "fs_path_utils.hpp" +#include "fs_results.hpp" + +Result FsPathUtils::VerifyPath(const char *path, size_t max_path_len, size_t max_name_len) { + const char *cur = path; + size_t name_len = 0; + + for (size_t path_len = 0; path_len <= max_path_len && name_len <= max_name_len; path_len++) { + const char c = *cur; + /* If terminated, we're done. */ + if (c == 0) { + return 0; + } + + /* TODO: Nintendo converts the path from utf-8 to utf-32, one character at a time. */ + /* We should do this. */ + + /* Banned characters: :*?<>| */ + if (c == ':' || c == '*' || c == '?' || c == '<' || c == '>' || c == '|') { + return ResultFsInvalidCharacter; + } + + name_len++; + /* Check for separator. */ + if (c == '/' || c == '\\') { + name_len = 0; + } + } + + return ResultFsTooLongPath; +} + +Result FsPathUtils::ConvertPathForServiceObject(FsPath *out, const char *path) { + /* Check for nullptr. */ + if (out == nullptr || path == nullptr) { + return ResultFsInvalidPath; + } + + /* Copy string, NULL terminate. */ + /* NOTE: Nintendo adds an extra char at 0x301 for NULL terminator */ + /* But then forces 0x300 to NULL anyway... */ + std::strncpy(out->str, path, sizeof(out->str) - 1); + out->str[sizeof(out->str)-1] = 0; + + /* Replace any instances of \ with / */ + for (size_t i = 0; i < sizeof(out->str); i++) { + if (out->str[i] == '\\') { + out->str[i] = '/'; + } + } + + /* Nintendo allows some liberties if the path is a windows path. Who knows why... */ + const auto prefix_len = FsPathUtils::IsWindowsAbsolutePath(path) ? 2 : 0; + const size_t max_len = (FS_MAX_PATH-1) - prefix_len; + return FsPathUtils::VerifyPath(out->str + prefix_len, max_len, max_len); +} diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_path_utils.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_path_utils.hpp new file mode 100644 index 000000000..975fe0337 --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_path_utils.hpp @@ -0,0 +1,34 @@ +/* + * 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 + +struct FsPath { + char str[FS_MAX_PATH]; +}; + +class FsPathUtils { + public: + static Result VerifyPath(const char *path, size_t max_path_len, size_t max_name_len); + static Result ConvertPathForServiceObject(FsPath *out, const char *path); + + static bool IsWindowsAbsolutePath(const char *path) { + /* Nintendo uses this in path comparisons... */ + return (('a' <= path[0] && path[0] <= 'z') || (('A' <= path[0] && path[0] <= 'Z'))) && + path[0] != 0 && path[1] == ':'; + } +}; diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_results.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_results.hpp index de45e90f9..6efecd17d 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fs_results.hpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_results.hpp @@ -24,6 +24,8 @@ static constexpr Result ResultFsOutOfRange = MAKERESULT(Module_Fs, 300 static constexpr Result ResultFsInvalidArgument = MAKERESULT(Module_Fs, 6001); static constexpr Result ResultFsInvalidPath = MAKERESULT(Module_Fs, 6002); +static constexpr Result ResultFsTooLongPath = MAKERESULT(Module_Fs, 6003); +static constexpr Result ResultFsInvalidCharacter = MAKERESULT(Module_Fs, 6004); static constexpr Result ResultFsInvalidOffset = MAKERESULT(Module_Fs, 6061); static constexpr Result ResultFsInvalidSize = MAKERESULT(Module_Fs, 6062);