From d2ff0b1774689afc911da87dfa3f81bb99019506 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 9 Mar 2020 03:10:12 -0700 Subject: [PATCH] loader: refactor to use fs bindings --- libstratosphere/include/stratosphere/fs.hpp | 9 +- .../stratosphere/fs/fs_application.hpp | 23 ++ .../include/stratosphere/fs/fs_code.hpp | 3 + .../fs/fs_read_only_filesystem.hpp | 157 +++++++++ .../fs/fs_readonly_filesystem_adapter.hpp | 152 --------- .../stratosphere/fs/impl/fs_newable.hpp | 4 + .../include/stratosphere/fssystem.hpp | 1 + .../fssystem/fssystem_external_code.hpp | 28 ++ .../fssystem/fssystem_utility.hpp | 3 + .../stratosphere/os/os_managed_handle.hpp | 4 +- .../stratosphere/patcher/patcher_api.hpp | 4 +- libstratosphere/source/fs/fs_application.cpp | 40 +++ libstratosphere/source/fs/fs_code.cpp | 306 +++++++++++++++++- .../source/fs/fs_filesystem_utils.cpp | 34 +- .../fssystem/fssystem_external_code.cpp | 72 +++++ .../source/fssystem/fssystem_utility.cpp | 24 ++ .../source/patcher/patcher_api.cpp | 6 +- .../include/vapours/results/fs_results.hpp | 29 +- .../include/vapours/util/util_bounded_map.hpp | 2 + 19 files changed, 694 insertions(+), 207 deletions(-) create mode 100644 libstratosphere/include/stratosphere/fs/fs_application.hpp create mode 100644 libstratosphere/include/stratosphere/fs/fs_read_only_filesystem.hpp delete mode 100644 libstratosphere/include/stratosphere/fs/fs_readonly_filesystem_adapter.hpp create mode 100644 libstratosphere/include/stratosphere/fssystem/fssystem_external_code.hpp create mode 100644 libstratosphere/source/fs/fs_application.cpp create mode 100644 libstratosphere/source/fssystem/fssystem_external_code.cpp diff --git a/libstratosphere/include/stratosphere/fs.hpp b/libstratosphere/include/stratosphere/fs.hpp index fe2fc2ac..d66b690c 100644 --- a/libstratosphere/include/stratosphere/fs.hpp +++ b/libstratosphere/include/stratosphere/fs.hpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -36,15 +36,16 @@ #include #include #include -#include +#include #include #include #include #include #include -#include -#include #include #include #include +#include +#include +#include #include diff --git a/libstratosphere/include/stratosphere/fs/fs_application.hpp b/libstratosphere/include/stratosphere/fs/fs_application.hpp new file mode 100644 index 00000000..8745e1b6 --- /dev/null +++ b/libstratosphere/include/stratosphere/fs/fs_application.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2020 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 "fs_common.hpp" + +namespace ams::fs { + + Result MountApplicationPackage(const char *name, const char *common_path); + +} diff --git a/libstratosphere/include/stratosphere/fs/fs_code.hpp b/libstratosphere/include/stratosphere/fs/fs_code.hpp index e151f94b..cb5caf5a 100644 --- a/libstratosphere/include/stratosphere/fs/fs_code.hpp +++ b/libstratosphere/include/stratosphere/fs/fs_code.hpp @@ -21,4 +21,7 @@ namespace ams::fs { Result MountCode(const char *name, const char *path, ncm::ProgramId program_id); + Result MountCodeForAtmosphereWithRedirection(const char *name, const char *path, ncm::ProgramId program_id, bool is_hbl, bool is_specific); + Result MountCodeForAtmosphere(const char *name, const char *path, ncm::ProgramId program_id); + } diff --git a/libstratosphere/include/stratosphere/fs/fs_read_only_filesystem.hpp b/libstratosphere/include/stratosphere/fs/fs_read_only_filesystem.hpp new file mode 100644 index 00000000..d2a73ff5 --- /dev/null +++ b/libstratosphere/include/stratosphere/fs/fs_read_only_filesystem.hpp @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2018-2020 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 +#include +#include + +namespace ams::fs { + + namespace { + + class ReadOnlyFile : public fsa::IFile, public impl::Newable { + NON_COPYABLE(ReadOnlyFile); + NON_MOVEABLE(ReadOnlyFile); + private: + std::unique_ptr base_file; + public: + explicit ReadOnlyFile(std::unique_ptr &&f) : base_file(std::move(f)) { /* ... */ } + virtual ~ReadOnlyFile() { /* ... */ } + private: + virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final { + return this->base_file->Read(out, offset, buffer, size, option); + } + + virtual Result GetSizeImpl(s64 *out) override final { + return this->base_file->GetSize(out); + } + + virtual Result FlushImpl() override final { + return ResultSuccess(); + } + + virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final { + return fs::ResultUnsupportedOperationInReadOnlyFileA(); + } + + virtual Result SetSizeImpl(s64 size) override final { + return fs::ResultUnsupportedOperationInReadOnlyFileA(); + } + + virtual Result OperateRangeImpl(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final { + switch (op_id) { + case OperationId::InvalidateCache: + case OperationId::QueryRange: + return this->base_file->OperateRange(dst, dst_size, op_id, offset, size, src, src_size); + default: + return fs::ResultUnsupportedOperationInReadOnlyFileB(); + } + } + public: + virtual sf::cmif::DomainObjectId GetDomainObjectId() const override { + return this->base_file->GetDomainObjectId(); + } + }; + + } + + template + class ReadOnlyFileSystemTemplate : public fsa::IFileSystem, public impl::Newable { + NON_COPYABLE(ReadOnlyFileSystemTemplate); + NON_MOVEABLE(ReadOnlyFileSystemTemplate); + private: + T base_fs; + public: + explicit ReadOnlyFileSystemTemplate(T &&fs) : base_fs(std::move(fs)) { /* ... */ } + virtual ~ReadOnlyFileSystemTemplate() { /* ... */ } + private: + virtual Result OpenFileImpl(std::unique_ptr *out_file, const char *path, OpenMode mode) override final { + /* Only allow opening files with mode = read. */ + R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidArgument()); + + std::unique_ptr base_file; + R_TRY(this->base_fs->OpenFile(std::addressof(base_file), path, mode)); + + auto read_only_file = std::make_unique(std::move(base_file)); + R_UNLESS(read_only_file != nullptr, fs::ResultAllocationFailureInReadOnlyFileSystemA()); + + *out_file = std::move(read_only_file); + return ResultSuccess(); + } + + virtual Result OpenDirectoryImpl(std::unique_ptr *out_dir, const char *path, OpenDirectoryMode mode) override final { + return this->base_fs->OpenDirectory(out_dir, path, mode); + } + + virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const char *path) override final { + return this->base_fs->GetEntryType(out, path); + } + + virtual Result CommitImpl() override final { + return ResultSuccess(); + } + + virtual Result CreateFileImpl(const char *path, s64 size, int flags) override final { + return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA(); + } + + virtual Result DeleteFileImpl(const char *path) override final { + return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA(); + } + + virtual Result CreateDirectoryImpl(const char *path) override final { + return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA(); + } + + virtual Result DeleteDirectoryImpl(const char *path) override final { + return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA(); + } + + virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override final { + return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA(); + } + + virtual Result RenameFileImpl(const char *old_path, const char *new_path) override final { + return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA(); + } + + virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override final { + return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA(); + } + + virtual Result CleanDirectoryRecursivelyImpl(const char *path) override final { + return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA(); + } + + virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) override final { + return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateB(); + } + + virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) override final { + return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateB(); + } + + virtual Result CommitProvisionallyImpl(s64 counter) override final { + return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateC(); + } + }; + + using ReadOnlyFileSystem = ReadOnlyFileSystemTemplate>; + using ReadOnlyFileSystemShared = ReadOnlyFileSystemTemplate>; + +} diff --git a/libstratosphere/include/stratosphere/fs/fs_readonly_filesystem_adapter.hpp b/libstratosphere/include/stratosphere/fs/fs_readonly_filesystem_adapter.hpp deleted file mode 100644 index 9c3c031d..00000000 --- a/libstratosphere/include/stratosphere/fs/fs_readonly_filesystem_adapter.hpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (c) 2018-2020 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 -#include - -namespace ams::fs { - - class ReadOnlyFileAdapter : public fsa::IFile { - NON_COPYABLE(ReadOnlyFileAdapter); - private: - std::unique_ptr base_file; - public: - ReadOnlyFileAdapter(fsa::IFile *f) : base_file(f) { /* ... */ } - ReadOnlyFileAdapter(std::unique_ptr f) : base_file(std::move(f)) { /* ... */ } - virtual ~ReadOnlyFileAdapter() { /* ... */ } - public: - virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final { - /* Ensure that we can read these extents. */ - size_t read_size = 0; - R_TRY(this->DryRead(std::addressof(read_size), offset, size, option, fs::OpenMode_Read)); - - /* Validate preconditions. */ - AMS_ASSERT(offset >= 0); - AMS_ASSERT(buffer != nullptr || size == 0); - - return this->base_file->Read(out, offset, buffer, size, option); - } - - virtual Result GetSizeImpl(s64 *out) override final { - return this->base_file->GetSize(out); - } - - virtual Result FlushImpl() override final { - return ResultSuccess(); - } - - virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final { - return fs::ResultUnsupportedOperation(); - } - - virtual Result SetSizeImpl(s64 size) override final { - return fs::ResultUnsupportedOperation(); - } - - virtual Result OperateRangeImpl(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final { - /* TODO: How should this be handled? */ - return fs::ResultNotImplemented(); - } - public: - virtual sf::cmif::DomainObjectId GetDomainObjectId() const override { - return this->base_file->GetDomainObjectId(); - } - }; - - class ReadOnlyFileSystemAdapter : public fsa::IFileSystem { - NON_COPYABLE(ReadOnlyFileSystemAdapter); - private: - std::shared_ptr shared_fs; - std::unique_ptr unique_fs; - protected: - fsa::IFileSystem * const base_fs; - public: - template - explicit ReadOnlyFileSystemAdapter(std::shared_ptr fs) : shared_fs(std::move(fs)), base_fs(shared_fs.get()) { static_assert(std::is_base_of::value); } - - template - explicit ReadOnlyFileSystemAdapter(std::unique_ptr fs) : unique_fs(std::move(fs)), base_fs(unique_fs.get()) { static_assert(std::is_base_of::value); } - - virtual ~ReadOnlyFileSystemAdapter() { /* ... */ } - public: - virtual Result CreateFileImpl(const char *path, s64 size, int flags) override final { - return fs::ResultUnsupportedOperation(); - } - - virtual Result DeleteFileImpl(const char *path) override final { - return fs::ResultUnsupportedOperation(); - } - - virtual Result CreateDirectoryImpl(const char *path) override final { - return fs::ResultUnsupportedOperation(); - } - - virtual Result DeleteDirectoryImpl(const char *path) override final { - return fs::ResultUnsupportedOperation(); - } - - virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override final { - return fs::ResultUnsupportedOperation(); - } - - virtual Result RenameFileImpl(const char *old_path, const char *new_path) override final { - return fs::ResultUnsupportedOperation(); - } - - virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override final { - return fs::ResultUnsupportedOperation(); - } - - virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const char *path) override final { - return this->base_fs->GetEntryType(out, path); - } - - virtual Result OpenFileImpl(std::unique_ptr *out_file, const char *path, OpenMode mode) override final { - std::unique_ptr f; - R_TRY(this->base_fs->OpenFile(std::addressof(f), path, mode)); - - *out_file = std::make_unique(std::move(f)); - return ResultSuccess(); - } - - virtual Result OpenDirectoryImpl(std::unique_ptr *out_dir, const char *path, OpenDirectoryMode mode) override final { - return this->base_fs->OpenDirectory(out_dir, path, mode); - } - - virtual Result CommitImpl() override final { - return ResultSuccess(); - } - - virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) { - return fs::ResultUnsupportedOperation(); - } - - virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) { - return fs::ResultUnsupportedOperation(); - } - - virtual Result CleanDirectoryRecursivelyImpl(const char *path) { - return fs::ResultUnsupportedOperation(); - } - - virtual Result GetFileTimeStampRawImpl(FileTimeStampRaw *out, const char *path) { - return this->base_fs->GetFileTimeStampRaw(out, path); - } - }; - -} diff --git a/libstratosphere/include/stratosphere/fs/impl/fs_newable.hpp b/libstratosphere/include/stratosphere/fs/impl/fs_newable.hpp index 7ceb7717..c0bf8d00 100644 --- a/libstratosphere/include/stratosphere/fs/impl/fs_newable.hpp +++ b/libstratosphere/include/stratosphere/fs/impl/fs_newable.hpp @@ -24,6 +24,10 @@ namespace ams::fs::impl { return ::ams::fs::impl::Allocate(size); } + static void *operator new(size_t size, Newable *placement) { + return placement; + } + static void *operator new[](size_t size) { return ::ams::fs::impl::Allocate(size); } diff --git a/libstratosphere/include/stratosphere/fssystem.hpp b/libstratosphere/include/stratosphere/fssystem.hpp index 2c8eeff8..de899e02 100644 --- a/libstratosphere/include/stratosphere/fssystem.hpp +++ b/libstratosphere/include/stratosphere/fssystem.hpp @@ -16,6 +16,7 @@ #pragma once #include "fssystem/fssystem_utility.hpp" +#include "fssystem/fssystem_external_code.hpp" #include "fssystem/fssystem_path_tool.hpp" #include "fssystem/fssystem_subdirectory_filesystem.hpp" #include "fssystem/fssystem_directory_redirection_filesystem.hpp" diff --git a/libstratosphere/include/stratosphere/fssystem/fssystem_external_code.hpp b/libstratosphere/include/stratosphere/fssystem/fssystem_external_code.hpp new file mode 100644 index 00000000..f62417e3 --- /dev/null +++ b/libstratosphere/include/stratosphere/fssystem/fssystem_external_code.hpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018-2020 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 + +namespace ams::fssystem { + + fs::fsa::IFileSystem *GetExternalCodeFileSystem(ncm::ProgramId program_id); + + Result CreateExternalCode(Handle *out, ncm::ProgramId program_id); + void DestroyExternalCode(ncm::ProgramId program_id); + +} diff --git a/libstratosphere/include/stratosphere/fssystem/fssystem_utility.hpp b/libstratosphere/include/stratosphere/fssystem/fssystem_utility.hpp index 2e9a8b48..b982a0b8 100644 --- a/libstratosphere/include/stratosphere/fssystem/fssystem_utility.hpp +++ b/libstratosphere/include/stratosphere/fssystem/fssystem_utility.hpp @@ -142,6 +142,9 @@ namespace ams::fssystem { }; /* Other utility. */ + Result HasFile(bool *out, fs::fsa::IFileSystem *fs, const char *path); + Result HasDirectory(bool *out, fs::fsa::IFileSystem *fs, const char *path); + Result EnsureDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path); Result EnsureParentDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path); diff --git a/libstratosphere/include/stratosphere/os/os_managed_handle.hpp b/libstratosphere/include/stratosphere/os/os_managed_handle.hpp index 67399757..1219cb6e 100644 --- a/libstratosphere/include/stratosphere/os/os_managed_handle.hpp +++ b/libstratosphere/include/stratosphere/os/os_managed_handle.hpp @@ -24,8 +24,8 @@ namespace ams::os { private: Handle hnd; public: - ManagedHandle() : hnd(INVALID_HANDLE) { /* ... */ } - ManagedHandle(Handle h) : hnd(h) { /* ... */ } + constexpr ManagedHandle() : hnd(INVALID_HANDLE) { /* ... */ } + constexpr ManagedHandle(Handle h) : hnd(h) { /* ... */ } ~ManagedHandle() { if (this->hnd != INVALID_HANDLE) { R_ABORT_UNLESS(svcCloseHandle(this->hnd)); diff --git a/libstratosphere/include/stratosphere/patcher/patcher_api.hpp b/libstratosphere/include/stratosphere/patcher/patcher_api.hpp index b01e871c..9a2b6995 100644 --- a/libstratosphere/include/stratosphere/patcher/patcher_api.hpp +++ b/libstratosphere/include/stratosphere/patcher/patcher_api.hpp @@ -16,11 +16,11 @@ #pragma once -#include "../ro/ro_types.hpp" +#include namespace ams::patcher { /* Helper for applying to code binaries. */ - void LocateAndApplyIpsPatchesToModule(const char *patch_dir, size_t protected_size, size_t offset, const ro::ModuleId *module_id, u8 *mapped_module, size_t mapped_size); + void LocateAndApplyIpsPatchesToModule(const char *mount_name, const char *patch_dir, size_t protected_size, size_t offset, const ro::ModuleId *module_id, u8 *mapped_module, size_t mapped_size); } diff --git a/libstratosphere/source/fs/fs_application.cpp b/libstratosphere/source/fs/fs_application.cpp new file mode 100644 index 00000000..10451f70 --- /dev/null +++ b/libstratosphere/source/fs/fs_application.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-2020 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 "fsa/fs_mount_utils.hpp" + +namespace ams::fs { + + Result MountApplicationPackage(const char *name, const char *common_path) { + /* Validate the mount name. */ + R_TRY(impl::CheckMountName(name)); + + /* Validate the path. */ + R_UNLESS(common_path != nullptr, fs::ResultInvalidPath()); + + /* Open a filesystem using libnx bindings. */ + FsFileSystem fs; + R_TRY(fsOpenFileSystemWithId(std::addressof(fs), ncm::InvalidProgramId.value, static_cast<::FsFileSystemType>(impl::FileSystemProxyType_Package), common_path)); + + /* Allocate a new filesystem wrapper. */ + auto fsa = std::make_unique(fs); + R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInApplicationA()); + + /* Register. */ + return fsa::Register(name, std::move(fsa)); + } + +} diff --git a/libstratosphere/source/fs/fs_code.cpp b/libstratosphere/source/fs/fs_code.cpp index 83e9b5c5..6aaa7dd6 100644 --- a/libstratosphere/source/fs/fs_code.cpp +++ b/libstratosphere/source/fs/fs_code.cpp @@ -18,6 +18,261 @@ namespace ams::fs { + namespace { + + Result OpenCodeFileSystemImpl(std::unique_ptr *out, const char *path, ncm::ProgramId program_id) { + /* Print a path suitable for the remote service. */ + fssrv::sf::Path sf_path; + R_TRY(FspPathPrintf(std::addressof(sf_path), "%s", path)); + + /* Open the filesystem using libnx bindings. */ + ::FsFileSystem fs; + R_TRY(fsldrOpenCodeFileSystem(program_id.value, sf_path.str, std::addressof(fs))); + + /* Allocate a new filesystem wrapper. */ + auto fsa = std::make_unique(fs); + R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInCodeA()); + + *out = std::move(fsa); + return ResultSuccess(); + } + + Result OpenPackageFileSystemImpl(std::unique_ptr *out, const char *common_path) { + /* Open a filesystem using libnx bindings. */ + FsFileSystem fs; + R_TRY(fsOpenFileSystemWithId(std::addressof(fs), ncm::InvalidProgramId.value, static_cast<::FsFileSystemType>(impl::FileSystemProxyType_Package), common_path)); + + /* Allocate a new filesystem wrapper. */ + auto fsa = std::make_unique(fs); + R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInCodeA()); + + *out = std::move(fsa); + return ResultSuccess(); + } + + Result OpenSdCardCodeFileSystemImpl(std::unique_ptr *out, ncm::ProgramId program_id) { + /* Ensure we don't access the SD card too early. */ + R_UNLESS(cfg::IsSdCardInitialized(), fs::ResultSdCardNotPresent()); + + /* Print a path to the program's package. */ + fssrv::sf::Path sf_path; + R_TRY(FspPathPrintf(std::addressof(sf_path), "%s:/atmosphere/contents/%016lx/exefs.nsp", impl::SdCardFileSystemMountName, program_id.value)); + + return OpenPackageFileSystemImpl(out, sf_path.str); + } + + Result OpenSdCardCodeOrCodeFileSystemImpl(std::unique_ptr *out, const char *path, ncm::ProgramId program_id) { + /* If we can open an sd card code fs, use it. */ + R_SUCCEED_IF(R_SUCCEEDED(OpenSdCardCodeFileSystemImpl(out, program_id))); + + /* Otherwise, fall back to a normal code fs. */ + return OpenCodeFileSystemImpl(out, path, program_id); + } + + Result OpenHblCodeFileSystemImpl(std::unique_ptr *out) { + /* Get the HBL path. */ + const char *hbl_path = cfg::GetHblPath(); + + /* Print a path to the hbl package. */ + fssrv::sf::Path sf_path; + R_TRY(FspPathPrintf(std::addressof(sf_path), "%s:/%s", impl::SdCardFileSystemMountName, hbl_path[0] == '/' ? hbl_path + 1 : hbl_path)); + + return OpenPackageFileSystemImpl(out, sf_path.str); + } + + Result OpenSdCardFileSystemImpl(std::unique_ptr *out) { + /* Open the SD card. This uses libnx bindings. */ + FsFileSystem fs; + R_TRY(fsOpenSdCardFileSystem(std::addressof(fs))); + + /* Allocate a new filesystem wrapper. */ + auto fsa = std::make_unique(fs); + R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInCodeA()); + + *out = std::move(fsa); + return ResultSuccess(); + } + + class OpenFileOnlyFileSystem : public fsa::IFileSystem, public impl::Newable { + private: + virtual Result CommitImpl() override final { + return ResultSuccess(); + } + + virtual Result OpenDirectoryImpl(std::unique_ptr *out_dir, const char *path, OpenDirectoryMode mode) override final { + return fs::ResultUnsupportedOperation(); + } + + virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const char *path) override final { + return fs::ResultUnsupportedOperation(); + } + + virtual Result CreateFileImpl(const char *path, s64 size, int flags) override final { + return fs::ResultUnsupportedOperation(); + } + + virtual Result DeleteFileImpl(const char *path) override final { + return fs::ResultUnsupportedOperation(); + } + + virtual Result CreateDirectoryImpl(const char *path) override final { + return fs::ResultUnsupportedOperation(); + } + + virtual Result DeleteDirectoryImpl(const char *path) override final { + return fs::ResultUnsupportedOperation(); + } + + virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override final { + return fs::ResultUnsupportedOperation(); + } + + virtual Result RenameFileImpl(const char *old_path, const char *new_path) override final { + return fs::ResultUnsupportedOperation(); + } + + virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override final { + return fs::ResultUnsupportedOperation(); + } + + virtual Result CleanDirectoryRecursivelyImpl(const char *path) override final { + return fs::ResultUnsupportedOperation(); + } + + virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) override final { + return fs::ResultUnsupportedOperation(); + } + + virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) override final { + return fs::ResultUnsupportedOperation(); + } + + virtual Result CommitProvisionallyImpl(s64 counter) override final { + return fs::ResultUnsupportedOperation(); + } + }; + + class SdCardRedirectionCodeFileSystem : public OpenFileOnlyFileSystem { + private: + std::optional sd_content_fs; + ReadOnlyFileSystem code_fs; + bool is_redirect; + public: + SdCardRedirectionCodeFileSystem(std::unique_ptr &&code, ncm::ProgramId program_id, bool redirect) : code_fs(std::move(code)), is_redirect(redirect) { + if (!cfg::IsSdCardInitialized()) { + return; + } + + /* Open an SD card filesystem. */ + std::unique_ptr sd_fs; + if (R_FAILED(OpenSdCardFileSystemImpl(std::addressof(sd_fs)))) { + return; + } + + /* Create a redirection filesystem to the relevant content folder. */ + char path[fs::EntryNameLengthMax + 1]; + std::snprintf(path, sizeof(path), "/atmosphere/contents/%016lx/exefs", program_id.value); + + auto subdir_fs = std::make_unique(std::move(sd_fs), path); + if (subdir_fs == nullptr) { + return; + } + + sd_content_fs.emplace(std::move(subdir_fs)); + } + private: + bool IsFileStubbed(const char *path) { + /* If we don't have an sd content fs, nothing is stubbed. */ + if (!this->sd_content_fs) { + return false; + } + + /* Create a path representing the stub. */ + char stub_path[fs::EntryNameLengthMax + 1]; + std::snprintf(stub_path, sizeof(stub_path), "%s.stub", path); + + /* Query whether we have the file. */ + bool has_file; + if (R_FAILED(fssystem::HasFile(std::addressof(has_file), std::addressof(*this->sd_content_fs), stub_path))) { + return false; + } + + return has_file; + } + + virtual Result OpenFileImpl(std::unique_ptr *out_file, const char *path, OpenMode mode) override final { + /* Only allow opening files with mode = read. */ + R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidArgument()); + + /* If we support redirection, we'd like to prefer a file from the sd card. */ + if (this->is_redirect) { + R_SUCCEED_IF(R_SUCCEEDED(this->sd_content_fs->OpenFile(out_file, path, mode))); + } + + /* Otherwise, check if the file is stubbed. */ + R_UNLESS(!this->IsFileStubbed(path), fs::ResultPathNotFound()); + + /* Open a file from the base code fs. */ + return this->code_fs.OpenFile(out_file, path, mode); + } + }; + + class AtmosphereCodeFileSystem : public OpenFileOnlyFileSystem { + private: + std::optional code_fs; + std::optional hbl_fs; + ncm::ProgramId program_id; + bool initialized; + public: + AtmosphereCodeFileSystem() : initialized(false) { /* ... */ } + + Result Initialize(const char *path, ncm::ProgramId program_id, bool is_hbl, bool is_specific) { + AMS_ABORT_UNLESS(!this->initialized); + + /* If we're hbl, we need to open a hbl fs. */ + if (is_hbl) { + std::unique_ptr fsa; + R_TRY(OpenHblCodeFileSystemImpl(std::addressof(fsa))); + this->hbl_fs.emplace(std::move(fsa)); + } + + /* Open the code filesystem. */ + std::unique_ptr fsa; + R_TRY(OpenSdCardCodeOrCodeFileSystemImpl(std::addressof(fsa), path, program_id)); + this->code_fs.emplace(std::move(fsa), program_id, is_specific); + + this->initialized = true; + + return ResultSuccess(); + } + private: + virtual Result OpenFileImpl(std::unique_ptr *out_file, const char *path, OpenMode mode) override final { + /* Ensure that we're initialized. */ + R_UNLESS(this->initialized, fs::ResultNotInitialized()); + + /* Only allow opening files with mode = read. */ + R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidArgument()); + + /* First, check if there's an external code. */ + { + fsa::IFileSystem *ecs = fssystem::GetExternalCodeFileSystem(this->program_id); + if (ecs != nullptr) { + return ecs->OpenFile(out_file, path, mode); + } + } + + /* If we're hbl, open from the hbl fs. */ + if (this->hbl_fs) { + return this->hbl_fs->OpenFile(out_file, path, mode); + } + + /* If we're not hbl, fall back to our code filesystem. */ + return this->code_fs->OpenFile(out_file, path, mode); + } + }; + + } + Result MountCode(const char *name, const char *path, ncm::ProgramId program_id) { /* Validate the mount name. */ R_TRY(impl::CheckMountName(name)); @@ -25,20 +280,49 @@ namespace ams::fs { /* Validate the path isn't null. */ R_UNLESS(path != nullptr, fs::ResultInvalidPath()); - /* Print a path suitable for the remove service. */ - fssrv::sf::Path sf_path; - R_TRY(FspPathPrintf(std::addressof(sf_path), "%s", path)); - - /* Open the filesystem using libnx bindings. */ - ::FsFileSystem fs; - R_TRY(fsldrOpenCodeFileSystem(program_id.value, sf_path.str, std::addressof(fs))); - - /* Allocate a new filesystem wrapper. */ - auto fsa = std::make_unique(fs); - R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInCodeA()); + /* Open the code file system. */ + std::unique_ptr fsa; + R_TRY(OpenCodeFileSystemImpl(std::addressof(fsa), path, program_id)); /* Register. */ return fsa::Register(name, std::move(fsa)); } + Result MountCodeForAtmosphereWithRedirection(const char *name, const char *path, ncm::ProgramId program_id, bool is_hbl, bool is_specific) { + /* Validate the mount name. */ + R_TRY(impl::CheckMountName(name)); + + /* Validate the path isn't null. */ + R_UNLESS(path != nullptr, fs::ResultInvalidPath()); + + /* Create an AtmosphereCodeFileSystem. */ + auto ams_code_fs = std::make_unique(); + R_UNLESS(ams_code_fs != nullptr, fs::ResultAllocationFailureInCodeA()); + + /* Initialize the code file system. */ + R_TRY(ams_code_fs->Initialize(path, program_id, is_hbl, is_specific)); + + /* Register. */ + return fsa::Register(name, std::move(ams_code_fs)); + } + + Result MountCodeForAtmosphere(const char *name, const char *path, ncm::ProgramId program_id) { + /* Validate the mount name. */ + R_TRY(impl::CheckMountName(name)); + + /* Validate the path isn't null. */ + R_UNLESS(path != nullptr, fs::ResultInvalidPath()); + + /* Open the code file system. */ + std::unique_ptr fsa; + R_TRY(OpenSdCardCodeOrCodeFileSystemImpl(std::addressof(fsa), path, program_id)); + + /* Create a wrapper fs. */ + auto wrap_fsa = std::make_unique(std::move(fsa), program_id, false); + R_UNLESS(wrap_fsa != nullptr, fs::ResultAllocationFailureInCodeA()); + + /* Register. */ + return fsa::Register(name, std::move(wrap_fsa)); + } + } diff --git a/libstratosphere/source/fs/fs_filesystem_utils.cpp b/libstratosphere/source/fs/fs_filesystem_utils.cpp index 04b60e12..550ac264 100644 --- a/libstratosphere/source/fs/fs_filesystem_utils.cpp +++ b/libstratosphere/source/fs/fs_filesystem_utils.cpp @@ -19,26 +19,6 @@ namespace ams::fs { - namespace { - - Result HasEntry(bool *out, const char *path, fs::DirectoryEntryType type) { - /* Set out to false initially. */ - *out = false; - - /* Try to get the entry type. */ - fs::DirectoryEntryType entry_type; - R_TRY_CATCH(fs::GetEntryType(std::addressof(entry_type), path)) { - /* If the path doesn't exist, nothing has gone wrong. */ - R_CONVERT(fs::ResultPathNotFound, ResultSuccess()); - } R_END_TRY_CATCH; - - /* We succeeded. */ - *out = entry_type == type; - return ResultSuccess(); - } - - } - Result EnsureDirectoryRecursively(const char *path) { /* Get the filesystem accessor and sub path. */ impl::FileSystemAccessor *accessor; @@ -60,11 +40,21 @@ namespace ams::fs { } Result HasFile(bool *out, const char *path) { - return HasEntry(out, path, fs::DirectoryEntryType_File); + /* Get the filesystem accessor and sub path. */ + impl::FileSystemAccessor *accessor; + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); + + return fssystem::HasFile(out, accessor->GetRawFileSystemUnsafe(), sub_path); } Result HasDirectory(bool *out, const char *path) { - return HasEntry(out, path, fs::DirectoryEntryType_Directory); + /* Get the filesystem accessor and sub path. */ + impl::FileSystemAccessor *accessor; + const char *sub_path; + R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path)); + + return fssystem::HasDirectory(out, accessor->GetRawFileSystemUnsafe(), sub_path); } } diff --git a/libstratosphere/source/fssystem/fssystem_external_code.cpp b/libstratosphere/source/fssystem/fssystem_external_code.cpp new file mode 100644 index 00000000..c607ee3c --- /dev/null +++ b/libstratosphere/source/fssystem/fssystem_external_code.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018-2020 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 + +namespace ams::fssystem { + + namespace { + + constexpr inline size_t MaxExternalCodeFileSystem = 0x10; + + util::BoundedMap g_ecs_map; + util::BoundedMap g_hnd_map; + + } + + + fs::fsa::IFileSystem *GetExternalCodeFileSystem(ncm::ProgramId program_id) { + /* Return a fs from the map if one exists. */ + if (auto *fs = g_ecs_map.Find(program_id); fs != nullptr) { + return fs; + } + + /* Otherwise, we may have a handle. */ + if (auto *hnd = g_hnd_map.Find(program_id); hnd != nullptr) { + /* Create a service using libnx bindings. */ + Service srv; + serviceCreate(std::addressof(srv), hnd->Move()); + g_hnd_map.Remove(program_id); + + /* Create a remote filesystem. */ + const FsFileSystem fs = { srv }; + g_ecs_map.Emplace(program_id, fs); + + /* Return the created filesystem. */ + return g_ecs_map.Find(program_id); + } + + /* Otherwise, we have no filesystem. */ + return nullptr; + } + + Result CreateExternalCode(Handle *out, ncm::ProgramId program_id) { + /* Create a handle pair. */ + Handle server, client; + R_TRY(svcCreateSession(std::addressof(server), std::addressof(client), false, 0)); + + /* Insert the handle into the map. */ + g_hnd_map.Emplace(program_id, client); + + *out = server; + return ResultSuccess(); + } + + void DestroyExternalCode(ncm::ProgramId program_id) { + g_ecs_map.Remove(program_id); + g_hnd_map.Remove(program_id); + } + +} diff --git a/libstratosphere/source/fssystem/fssystem_utility.cpp b/libstratosphere/source/fssystem/fssystem_utility.cpp index 3612875f..84594b64 100644 --- a/libstratosphere/source/fssystem/fssystem_utility.cpp +++ b/libstratosphere/source/fssystem/fssystem_utility.cpp @@ -51,6 +51,22 @@ namespace ams::fssystem { return ResultSuccess(); } + Result HasEntry(bool *out, fs::fsa::IFileSystem *fsa, const char *path, fs::DirectoryEntryType type) { + /* Set out to false initially. */ + *out = false; + + /* Try to get the entry type. */ + fs::DirectoryEntryType entry_type; + R_TRY_CATCH(fsa->GetEntryType(std::addressof(entry_type), path)) { + /* If the path doesn't exist, nothing has gone wrong. */ + R_CONVERT(fs::ResultPathNotFound, ResultSuccess()); + } R_END_TRY_CATCH; + + /* We succeeded. */ + *out = entry_type == type; + return ResultSuccess(); + } + } Result CopyFile(fs::fsa::IFileSystem *dst_fs, fs::fsa::IFileSystem *src_fs, const char *dst_parent_path, const char *src_path, const fs::DirectoryEntry *entry, void *work_buf, size_t work_buf_size) { @@ -117,6 +133,14 @@ namespace ams::fssystem { ); } + Result HasFile(bool *out, fs::fsa::IFileSystem *fs, const char *path) { + return HasEntry(out, fs, path, fs::DirectoryEntryType_File); + } + + Result HasDirectory(bool *out, fs::fsa::IFileSystem *fs, const char *path) { + return HasEntry(out, fs, path, fs::DirectoryEntryType_Directory); + } + Result EnsureDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *path) { return EnsureDirectoryRecursivelyImpl(fs, path, true); } diff --git a/libstratosphere/source/patcher/patcher_api.cpp b/libstratosphere/source/patcher/patcher_api.cpp index c140e0c2..f4056190 100644 --- a/libstratosphere/source/patcher/patcher_api.cpp +++ b/libstratosphere/source/patcher/patcher_api.cpp @@ -193,7 +193,7 @@ namespace ams::patcher { } { size_t remaining = read_size; - size_t copy_offset = 0; + size_t copy_offset = patch_offset; while (remaining > 0) { const size_t cur_read = std::min(remaining, sizeof(g_patch_read_buffer)); ReadData(g_patch_read_buffer, cur_read); @@ -211,13 +211,13 @@ namespace ams::patcher { } - void LocateAndApplyIpsPatchesToModule(const char *patch_dir_name, size_t protected_size, size_t offset, const ro::ModuleId *module_id, u8 *mapped_module, size_t mapped_size) { + void LocateAndApplyIpsPatchesToModule(const char *mount_name, const char *patch_dir_name, size_t protected_size, size_t offset, const ro::ModuleId *module_id, u8 *mapped_module, size_t mapped_size) { /* Ensure only one thread tries to apply patches at a time. */ std::scoped_lock lk(apply_patch_lock); /* Inspect all patches from /atmosphere//<*>/<*>.ips */ char path[fs::EntryNameLengthMax + 1]; - std::snprintf(path, sizeof(path), "sdmc:/atmosphere/%s", patch_dir_name); + std::snprintf(path, sizeof(path), "%s:/atmosphere/%s", mount_name, patch_dir_name); const size_t patches_dir_path_len = std::strlen(path); /* Open the patch directory. */ diff --git a/libvapours/include/vapours/results/fs_results.hpp b/libvapours/include/vapours/results/fs_results.hpp index 7af09fc2..6566b85a 100644 --- a/libvapours/include/vapours/results/fs_results.hpp +++ b/libvapours/include/vapours/results/fs_results.hpp @@ -53,6 +53,7 @@ namespace ams::fs { R_DEFINE_ERROR_RANGE(AllocationFailure, 3200, 3499); R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemAccessorA, 3211); R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemAccessorB, 3212); + R_DEFINE_ERROR_RESULT(AllocationFailureInApplicationA, 3213); R_DEFINE_ERROR_RESULT(AllocationFailureInBisA, 3215); R_DEFINE_ERROR_RESULT(AllocationFailureInBisB, 3216); R_DEFINE_ERROR_RESULT(AllocationFailureInBisC, 3217); @@ -79,6 +80,7 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(AllocationFailureInRegisterB, 3366); R_DEFINE_ERROR_RESULT(AllocationFailureInPathNormalizer, 3367); R_DEFINE_ERROR_RESULT(AllocationFailureInDbmRomKeyValueStorage, 3375); + R_DEFINE_ERROR_RESULT(AllocationFailureInReadOnlyFileSystemA, 3386); R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemE, 3377); R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemInterfaceAdapter, 3407); R_DEFINE_ERROR_RESULT(AllocationFailureInNew, 3420); @@ -237,17 +239,22 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(FileExtensionWithoutOpenModeAllowAppend, 6201); R_DEFINE_ERROR_RANGE(UnsupportedOperation, 6300, 6399); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageA, 6302); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageB, 6303); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageA, 6304); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageB, 6305); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileStorageA, 6306); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileServiceObjectAdapterA, 6362); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemA, 6364); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemB, 6365); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemC, 6366); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileA, 6367); - R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileB, 6368); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageA, 6302); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageB, 6303); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageA, 6304); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInMemoryStorageB, 6305); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileStorageA, 6306); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInFileServiceObjectAdapterA, 6362); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemA, 6364); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemB, 6365); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileSystemC, 6366); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileA, 6367); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInRomFsFileB, 6368); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileSystemTemplateA, 6369); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileSystemTemplateB, 6370); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileSystemTemplateC, 6371); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileA, 6372); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileB, 6373); R_DEFINE_ERROR_RANGE(PermissionDenied, 6400, 6449); diff --git a/libvapours/include/vapours/util/util_bounded_map.hpp b/libvapours/include/vapours/util/util_bounded_map.hpp index d76f7a6d..b0d2b7c2 100644 --- a/libvapours/include/vapours/util/util_bounded_map.hpp +++ b/libvapours/include/vapours/util/util_bounded_map.hpp @@ -32,6 +32,8 @@ namespace ams::util { GetReference(this->values[i]).~Value(); } public: + constexpr BoundedMap() : keys(), values() { /* ... */ } + Value *Find(const Key &key) { for (size_t i = 0; i < N; i++) { if (this->keys[i] && this->keys[i].value() == key) {