diff --git a/libraries/libstratosphere/include/stratosphere/fs.hpp b/libraries/libstratosphere/include/stratosphere/fs.hpp index fe2fc2aca..d66b690c2 100644 --- a/libraries/libstratosphere/include/stratosphere/fs.hpp +++ b/libraries/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/stratosphere/loader/source/ldr_ecs.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_application.hpp similarity index 73% rename from stratosphere/loader/source/ldr_ecs.hpp rename to libraries/libstratosphere/include/stratosphere/fs/fs_application.hpp index 7e49f3574..8745e1b67 100644 --- a/stratosphere/loader/source/ldr_ecs.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_application.hpp @@ -14,13 +14,10 @@ * along with this program. If not, see . */ #pragma once -#include +#include "fs_common.hpp" -namespace ams::ldr::ecs { +namespace ams::fs { - /* External Content Source API. */ - const char *Get(ncm::ProgramId program_id); - Result Set(Handle *out, ncm::ProgramId program_id); - Result Clear(ncm::ProgramId program_id); + Result MountApplicationPackage(const char *name, const char *common_path); } diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_code.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_code.hpp index e151f94ba..cb5caf5a3 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_code.hpp +++ b/libraries/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/libraries/libstratosphere/include/stratosphere/fs/fs_read_only_filesystem.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_read_only_filesystem.hpp new file mode 100644 index 000000000..d2a73ff58 --- /dev/null +++ b/libraries/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/libraries/libstratosphere/include/stratosphere/fs/fs_readonly_filesystem_adapter.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_readonly_filesystem_adapter.hpp deleted file mode 100644 index 9c3c031d3..000000000 --- a/libraries/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/libraries/libstratosphere/include/stratosphere/fs/impl/fs_newable.hpp b/libraries/libstratosphere/include/stratosphere/fs/impl/fs_newable.hpp index 7ceb7717f..c0bf8d001 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/impl/fs_newable.hpp +++ b/libraries/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/libraries/libstratosphere/include/stratosphere/fssystem.hpp b/libraries/libstratosphere/include/stratosphere/fssystem.hpp index 2c8eeff85..de899e024 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem.hpp +++ b/libraries/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/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_external_code.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_external_code.hpp new file mode 100644 index 000000000..f62417e38 --- /dev/null +++ b/libraries/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/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_utility.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_utility.hpp index 2e9a8b48c..b982a0b88 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_utility.hpp +++ b/libraries/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/libraries/libstratosphere/include/stratosphere/os/os_managed_handle.hpp b/libraries/libstratosphere/include/stratosphere/os/os_managed_handle.hpp index 673997578..1219cb6e5 100644 --- a/libraries/libstratosphere/include/stratosphere/os/os_managed_handle.hpp +++ b/libraries/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/libraries/libstratosphere/include/stratosphere/patcher/patcher_api.hpp b/libraries/libstratosphere/include/stratosphere/patcher/patcher_api.hpp index b01e871c6..9a2b69950 100644 --- a/libraries/libstratosphere/include/stratosphere/patcher/patcher_api.hpp +++ b/libraries/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/libraries/libstratosphere/source/fs/fs_application.cpp b/libraries/libstratosphere/source/fs/fs_application.cpp new file mode 100644 index 000000000..10451f702 --- /dev/null +++ b/libraries/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/libraries/libstratosphere/source/fs/fs_code.cpp b/libraries/libstratosphere/source/fs/fs_code.cpp index 83e9b5c5d..6aaa7dd6f 100644 --- a/libraries/libstratosphere/source/fs/fs_code.cpp +++ b/libraries/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/libraries/libstratosphere/source/fs/fs_filesystem_utils.cpp b/libraries/libstratosphere/source/fs/fs_filesystem_utils.cpp index 04b60e123..550ac2640 100644 --- a/libraries/libstratosphere/source/fs/fs_filesystem_utils.cpp +++ b/libraries/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/libraries/libstratosphere/source/fssystem/fssystem_external_code.cpp b/libraries/libstratosphere/source/fssystem/fssystem_external_code.cpp new file mode 100644 index 000000000..c607ee3c5 --- /dev/null +++ b/libraries/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/libraries/libstratosphere/source/fssystem/fssystem_utility.cpp b/libraries/libstratosphere/source/fssystem/fssystem_utility.cpp index 3612875f0..84594b646 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_utility.cpp +++ b/libraries/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/libraries/libstratosphere/source/patcher/patcher_api.cpp b/libraries/libstratosphere/source/patcher/patcher_api.cpp index c140e0c23..f4056190a 100644 --- a/libraries/libstratosphere/source/patcher/patcher_api.cpp +++ b/libraries/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/libraries/libvapours/include/vapours/results/fs_results.hpp b/libraries/libvapours/include/vapours/results/fs_results.hpp index 7af09fc21..6566b85ac 100644 --- a/libraries/libvapours/include/vapours/results/fs_results.hpp +++ b/libraries/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/libraries/libvapours/include/vapours/util/util_bounded_map.hpp b/libraries/libvapours/include/vapours/util/util_bounded_map.hpp index d76f7a6d7..b0d2b7c27 100644 --- a/libraries/libvapours/include/vapours/util/util_bounded_map.hpp +++ b/libraries/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) { diff --git a/stratosphere/eclct.stub/eclct.stub.json b/stratosphere/eclct.stub/eclct.stub.json index 5c86a71a8..9d2404787 100644 --- a/stratosphere/eclct.stub/eclct.stub.json +++ b/stratosphere/eclct.stub/eclct.stub.json @@ -15,7 +15,7 @@ "permissions": "0xFFFFFFFFFFFFFFFF" }, "service_host": [], - "service_access": [], + "service_access": ["fatal:u"], "kernel_capabilities": [ { "type": "kernel_flags", diff --git a/stratosphere/loader/source/ldr_content_management.cpp b/stratosphere/loader/source/ldr_content_management.cpp index a837941e3..343cd8040 100644 --- a/stratosphere/loader/source/ldr_content_management.cpp +++ b/stratosphere/loader/source/ldr_content_management.cpp @@ -13,255 +13,65 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include #include "ldr_content_management.hpp" -#include "ldr_ecs.hpp" namespace ams::ldr { namespace { - /* DeviceNames. */ - constexpr const char *CodeFileSystemDeviceName = "code"; - constexpr const char *HblFileSystemDeviceName = "hbl"; - constexpr const char *SdCardFileSystemDeviceName = "sdmc"; - - constexpr const char *SdCardStorageMountPoint = "@Sdcard"; - - /* Globals. */ - bool g_has_mounted_sd_card = false; - - /* Helpers. */ - inline void FixFileSystemPath(char *path) { - /* Paths will fail when passed to FS if they use the wrong kinds of slashes. */ - for (size_t i = 0; i < FS_MAX_PATH && path[i]; i++) { - if (path[i] == '\\') { - path[i] = '/'; - } - } - } - - inline const char *GetRelativePathStart(const char *relative_path) { - /* We assume filenames don't start with slashes when formatting. */ - while (*relative_path == '/' || *relative_path == '\\') { - relative_path++; - } - return relative_path; - } - - Result MountSdCardFileSystem() { - return fsdevMountSdmc(); - } - - Result MountNspFileSystem(const char *device_name, const char *path) { - FsFileSystem fs; - R_TRY(fsOpenFileSystemWithId(&fs, 0, FsFileSystemType_ApplicationPackage, path)); - AMS_ABORT_UNLESS(fsdevMountDevice(device_name, fs) >= 0); - return ResultSuccess(); - } - - FILE *OpenFile(const char *device_name, const char *relative_path) { - /* Allow nullptr device_name/relative path -- those are simply not openable. */ - if (device_name == nullptr || relative_path == nullptr) { - return nullptr; - } - - char path[FS_MAX_PATH]; - std::snprintf(path, FS_MAX_PATH, "%s:/%s", device_name, GetRelativePathStart(relative_path)); - FixFileSystemPath(path); - return fopen(path, "rb"); - } - - FILE *OpenLooseSdFile(ncm::ProgramId program_id, const char *relative_path) { - /* Allow nullptr relative path -- those are simply not openable. */ - if (relative_path == nullptr) { - return nullptr; - } - - char path[FS_MAX_PATH]; - std::snprintf(path, FS_MAX_PATH, "/atmosphere/contents/%016lx/exefs/%s", static_cast(program_id), GetRelativePathStart(relative_path)); - FixFileSystemPath(path); - return OpenFile(SdCardFileSystemDeviceName, path); - } - - bool IsFileStubbed(ncm::ProgramId program_id, const cfg::OverrideStatus &status, const char *relative_path) { - /* Allow nullptr relative path -- those are simply not openable. */ - if (relative_path == nullptr) { - return true; - } - - /* Only allow stubbing in the case where we're considering SD card content. */ - if (!status.IsProgramSpecific()) { - return false; - } - - char path[FS_MAX_PATH]; - std::snprintf(path, FS_MAX_PATH, "/atmosphere/contents/%016lx/exefs/%s.stub", static_cast(program_id), GetRelativePathStart(relative_path)); - FixFileSystemPath(path); - FILE *f = OpenFile(SdCardFileSystemDeviceName, path); - if (f == nullptr) { - return false; - } - fclose(f); - return true; - } - - FILE *OpenBaseExefsFile(ncm::ProgramId program_id, const cfg::OverrideStatus &status, const char *relative_path) { - /* Allow nullptr relative path -- those are simply not openable. */ - if (relative_path == nullptr) { - return nullptr; - } - - /* Check if stubbed. */ - if (IsFileStubbed(program_id, status, relative_path)) { - return nullptr; - } - - return OpenFile(CodeFileSystemDeviceName, relative_path); - } + os::Mutex g_scoped_code_mount_lock; } /* ScopedCodeMount functionality. */ - ScopedCodeMount::ScopedCodeMount(const ncm::ProgramLocation &loc) : has_status(false), is_code_mounted(false), is_hbl_mounted(false) { + ScopedCodeMount::ScopedCodeMount(const ncm::ProgramLocation &loc) : lk(g_scoped_code_mount_lock), has_status(false), mounted_ams(false), mounted_code(false) { this->result = this->Initialize(loc); } - ScopedCodeMount::ScopedCodeMount(const ncm::ProgramLocation &loc, const cfg::OverrideStatus &o) : override_status(o), has_status(true), is_code_mounted(false), is_hbl_mounted(false) { + ScopedCodeMount::ScopedCodeMount(const ncm::ProgramLocation &loc, const cfg::OverrideStatus &o) : lk(g_scoped_code_mount_lock), override_status(o), has_status(true), mounted_ams(false), mounted_code(false) { this->result = this->Initialize(loc); } ScopedCodeMount::~ScopedCodeMount() { - /* Unmount devices. */ - if (this->is_code_mounted) { - fsdevUnmountDevice(CodeFileSystemDeviceName); + /* Unmount filesystems. */ + if (this->mounted_ams) { + fs::Unmount(AtmosphereCodeMountName); } - if (this->is_hbl_mounted) { - fsdevUnmountDevice(HblFileSystemDeviceName); + if (this->mounted_code) { + fs::Unmount(CodeMountName); } } - Result ScopedCodeMount::MountCodeFileSystem(const ncm::ProgramLocation &loc) { - char path[FS_MAX_PATH]; - - /* Try to get the content path. */ - R_TRY(ResolveContentPath(path, loc)); - - /* Try to mount the content path. */ - FsFileSystem fs; - R_TRY(fsldrOpenCodeFileSystem(static_cast(loc.program_id), path, &fs)); - AMS_ABORT_UNLESS(fsdevMountDevice(CodeFileSystemDeviceName, fs) != -1); - - /* Note that we mounted code. */ - this->is_code_mounted = true; - return ResultSuccess(); - } - - Result ScopedCodeMount::MountSdCardCodeFileSystem(const ncm::ProgramLocation &loc) { - char path[FS_MAX_PATH]; - - /* Print and fix path. */ - std::snprintf(path, FS_MAX_PATH, "%s:/atmosphere/contents/%016lx/exefs.nsp", SdCardStorageMountPoint, static_cast(loc.program_id)); - FixFileSystemPath(path); - R_TRY(MountNspFileSystem(CodeFileSystemDeviceName, path)); - - /* Note that we mounted code. */ - this->is_code_mounted = true; - return ResultSuccess(); - } - - Result ScopedCodeMount::MountHblFileSystem() { - char path[FS_MAX_PATH]; - - /* Print and fix path. */ - std::snprintf(path, FS_MAX_PATH, "%s:/%s", SdCardStorageMountPoint, GetRelativePathStart(cfg::GetHblPath())); - FixFileSystemPath(path); - R_TRY(MountNspFileSystem(HblFileSystemDeviceName, path)); - - /* Note that we mounted HBL. */ - this->is_hbl_mounted = true; - return ResultSuccess(); - } - - Result ScopedCodeMount::Initialize(const ncm::ProgramLocation &loc) { - bool is_sd_initialized = cfg::IsSdCardInitialized(); - - /* Check if we're ready to mount the SD card. */ - if (!g_has_mounted_sd_card) { - if (is_sd_initialized) { - R_ABORT_UNLESS(MountSdCardFileSystem()); - g_has_mounted_sd_card = true; - } - } - /* Capture override status, if necessary. */ - if (!this->has_status) { - this->InitializeOverrideStatus(loc); + this->EnsureOverrideStatus(loc); + AMS_ABORT_UNLESS(this->has_status); + + /* Get the content path. */ + char content_path[fs::EntryNameLengthMax + 1] = "/"; + if (static_cast(loc.storage_id) != ncm::StorageId::None) { + R_TRY(ResolveContentPath(content_path, loc)); } - /* Check if we should override contents. */ - if (this->override_status.IsHbl()) { - /* Try to mount HBL. */ - this->MountHblFileSystem(); - } - if (this->override_status.IsProgramSpecific()) { - /* Try to mount Code NSP on SD. */ - this->MountSdCardCodeFileSystem(loc); - } + /* Mount the atmosphere code file system. */ + R_TRY(fs::MountCodeForAtmosphereWithRedirection(AtmosphereCodeMountName, content_path, loc.program_id, this->override_status.IsHbl(), this->override_status.IsProgramSpecific())); + this->mounted_ams = true; - /* If we haven't already mounted code, mount it. */ - if (!this->IsCodeMounted()) { - R_TRY(this->MountCodeFileSystem(loc)); - } + /* Mount the base code file system. */ + R_TRY(fs::MountCodeForAtmosphere(CodeMountName, content_path, loc.program_id)); + this->mounted_code = true; return ResultSuccess(); } - void ScopedCodeMount::InitializeOverrideStatus(const ncm::ProgramLocation &loc) { - AMS_ABORT_UNLESS(!this->has_status); + void ScopedCodeMount::EnsureOverrideStatus(const ncm::ProgramLocation &loc) { + if (this->has_status) { + return; + } this->override_status = cfg::CaptureOverrideStatus(loc.program_id); this->has_status = true; } - Result OpenCodeFile(FILE *&out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, const char *relative_path) { - FILE *f = nullptr; - const char *ecs_device_name = ecs::Get(program_id); - - if (ecs_device_name != nullptr) { - /* First priority: Open from external content. */ - f = OpenFile(ecs_device_name, relative_path); - } else if (status.IsHbl()) { - /* Next, try to open from HBL. */ - f = OpenFile(HblFileSystemDeviceName, relative_path); - } else { - /* If not ECS or HBL, try a loose file on the SD. */ - if (status.IsProgramSpecific()) { - f = OpenLooseSdFile(program_id, relative_path); - } - - /* If we fail, try the original exefs. */ - if (f == nullptr) { - f = OpenBaseExefsFile(program_id, status, relative_path); - } - } - - /* If nothing worked, we failed to find the path. */ - R_UNLESS(f != nullptr, fs::ResultPathNotFound()); - - out = f; - return ResultSuccess(); - } - - Result OpenCodeFileFromBaseExefs(FILE *&out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, const char *relative_path) { - /* Open the file. */ - FILE *f = OpenBaseExefsFile(program_id, status, relative_path); - R_UNLESS(f != nullptr, fs::ResultPathNotFound()); - - out = f; - return ResultSuccess(); - } - /* Redirection API. */ Result ResolveContentPath(char *out_path, const ncm::ProgramLocation &loc) { lr::Path path; @@ -281,7 +91,9 @@ namespace ams::ldr { std::strncpy(out_path, path.str, fs::EntryNameLengthMax); out_path[fs::EntryNameLengthMax - 1] = '\0'; - FixFileSystemPath(out_path); + + fs::Replace(out_path, fs::EntryNameLengthMax + 1, fs::StringTraits::AlternateDirectorySeparator, fs::StringTraits::DirectorySeparator); + return ResultSuccess(); } diff --git a/stratosphere/loader/source/ldr_content_management.hpp b/stratosphere/loader/source/ldr_content_management.hpp index 9aa0f0a96..7fe67ad9f 100644 --- a/stratosphere/loader/source/ldr_content_management.hpp +++ b/stratosphere/loader/source/ldr_content_management.hpp @@ -21,12 +21,14 @@ namespace ams::ldr { /* Utility reference to make code mounting automatic. */ class ScopedCodeMount { NON_COPYABLE(ScopedCodeMount); + NON_MOVEABLE(ScopedCodeMount); private: - Result result; + std::scoped_lock lk; cfg::OverrideStatus override_status; + Result result; bool has_status; - bool is_code_mounted; - bool is_hbl_mounted; + bool mounted_ams; + bool mounted_code; public: ScopedCodeMount(const ncm::ProgramLocation &loc); ScopedCodeMount(const ncm::ProgramLocation &loc, const cfg::OverrideStatus &override_status); @@ -36,32 +38,20 @@ namespace ams::ldr { return this->result; } - bool IsCodeMounted() const { - return this->is_code_mounted; - } - - bool IsHblMounted() const { - return this->is_hbl_mounted; - } - const cfg::OverrideStatus &GetOverrideStatus() const { AMS_ABORT_UNLESS(this->has_status); return this->override_status; } - private: Result Initialize(const ncm::ProgramLocation &loc); - - void InitializeOverrideStatus(const ncm::ProgramLocation &loc); - - Result MountCodeFileSystem(const ncm::ProgramLocation &loc); - Result MountSdCardCodeFileSystem(const ncm::ProgramLocation &loc); - Result MountHblFileSystem(); + void EnsureOverrideStatus(const ncm::ProgramLocation &loc); }; - /* Content Management API. */ - Result OpenCodeFile(FILE *&out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, const char *relative_path); - Result OpenCodeFileFromBaseExefs(FILE *&out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, const char *relative_path); + constexpr inline const char * const AtmosphereCodeMountName = "ams-code"; + constexpr inline const char * const CodeMountName = "code"; + + #define ENCODE_ATMOSPHERE_CODE_PATH(relative) "ams-code:" relative + #define ENCODE_CODE_PATH(relative) "ams:" relative /* Redirection API. */ Result ResolveContentPath(char *out_path, const ncm::ProgramLocation &loc); diff --git a/stratosphere/loader/source/ldr_ecs.cpp b/stratosphere/loader/source/ldr_ecs.cpp deleted file mode 100644 index 29127e870..000000000 --- a/stratosphere/loader/source/ldr_ecs.cpp +++ /dev/null @@ -1,122 +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 . - */ -#include "ldr_ecs.hpp" - -namespace ams::ldr::ecs { - - namespace { - - /* Convenience definition. */ - constexpr size_t DeviceNameSizeMax = 0x20; - constexpr size_t MaxExternalContentSourceCount = 0x10; - - /* Types. */ - class ExternalContentSource { - NON_COPYABLE(ExternalContentSource); - NON_MOVEABLE(ExternalContentSource); - private: - bool has_mounted = false; - char device_name[DeviceNameSizeMax]; - os::ManagedHandle client; - - Result Mount() { - /* Create service. */ - Service srv; - serviceCreate(&srv, client.Move()); - FsFileSystem fs = { srv }; - auto fs_guard = SCOPE_GUARD { fsFsClose(&fs); }; - - /* Try to mount. */ - R_UNLESS(fsdevMountDevice(device_name, fs) >= 0, fs::ResultMountNameAlreadyExists()); - fs_guard.Cancel(); - - this->has_mounted = true; - return ResultSuccess(); - } - public: - ExternalContentSource(const char *dn, os::ManagedHandle client) : client(std::move(client)) { - std::strncpy(this->device_name, dn, sizeof(this->device_name)); - this->device_name[sizeof(this->device_name) - 1] = '\0'; - } - - Result EnsureMounted() { - if (!this->has_mounted) { - return Mount(); - } - return ResultSuccess(); - } - - ~ExternalContentSource() { - if (this->has_mounted) { - fsdevUnmountDevice(this->device_name); - } - } - - const char *GetDeviceName() const { - return this->device_name; - } - }; - - /* Global storage. */ - std::unordered_map g_map; - } - - /* API. */ - const char *Get(ncm::ProgramId program_id) { - auto it = g_map.find(static_cast(program_id)); - if (it == g_map.end()) { - return nullptr; - } - if (R_FAILED(it->second.EnsureMounted())) { - g_map.erase(it); - return nullptr; - } - return it->second.GetDeviceName(); - } - - Result Set(Handle *out, ncm::ProgramId program_id) { - /* TODO: Is this an appropriate error? */ - R_UNLESS(g_map.size() < MaxExternalContentSourceCount, ldr::ResultTooManyArguments()); - - /* Clear any sources. */ - R_ABORT_UNLESS(Clear(program_id)); - - /* Generate mountpoint. */ - char device_name[DeviceNameSizeMax]; - std::snprintf(device_name, DeviceNameSizeMax, "ecs-%016lx", static_cast(program_id)); - - /* Create session. */ - os::ManagedHandle server, client; - R_TRY(svcCreateSession(server.GetPointer(), client.GetPointer(), 0, 0)); - - /* Do not create service yet. */ - /* Defer until we've handed the server side back so we don't deadlock on querying pointer buffer size. */ - - /* Add to map. */ - g_map.emplace(std::piecewise_construct, - std::make_tuple(static_cast(program_id)), - std::make_tuple(device_name, std::move(client))); - *out = server.Move(); - return ResultSuccess(); - } - - Result Clear(ncm::ProgramId program_id) { - /* Delete if present. */ - g_map.erase(static_cast(program_id)); - return ResultSuccess(); - } - -} diff --git a/stratosphere/loader/source/ldr_launch_record.cpp b/stratosphere/loader/source/ldr_launch_record.cpp index 4135d4f6e..53ade01af 100644 --- a/stratosphere/loader/source/ldr_launch_record.cpp +++ b/stratosphere/loader/source/ldr_launch_record.cpp @@ -49,7 +49,7 @@ namespace ams::ldr { if (IsTrackableSystemProgramId(program_id)) { return HasLaunchedSystemProgram(ncm::SystemProgramId{program_id.value}); } else { - return g_launched_programs.find(static_cast(program_id)) != g_launched_programs.end(); + return g_launched_programs.find(program_id.value) != g_launched_programs.end(); } } diff --git a/stratosphere/loader/source/ldr_loader_service.cpp b/stratosphere/loader/source/ldr_loader_service.cpp index 2f4405a6c..58d3658f7 100644 --- a/stratosphere/loader/source/ldr_loader_service.cpp +++ b/stratosphere/loader/source/ldr_loader_service.cpp @@ -15,7 +15,6 @@ */ #include "ldr_arguments.hpp" #include "ldr_content_management.hpp" -#include "ldr_ecs.hpp" #include "ldr_process_creation.hpp" #include "ldr_launch_record.hpp" #include "ldr_loader_service.hpp" @@ -97,12 +96,12 @@ namespace ams::ldr { } /* Atmosphere commands. */ - Result LoaderService::AtmosphereSetExternalContentSource(sf::OutMoveHandle out, ncm::ProgramId program_id) { - return ecs::Set(out.GetHandlePointer(), program_id); + Result LoaderService::AtmosphereRegisterExternalCode(sf::OutMoveHandle out, ncm::ProgramId program_id) { + return fssystem::CreateExternalCode(out.GetHandlePointer(), program_id); } - void LoaderService::AtmosphereClearExternalContentSource(ncm::ProgramId program_id) { - R_ABORT_UNLESS(ecs::Clear(program_id)); + void LoaderService::AtmosphereUnregisterExternalCode(ncm::ProgramId program_id) { + fssystem::DestroyExternalCode(program_id); } void LoaderService::AtmosphereHasLaunchedProgram(sf::Out out, ncm::ProgramId program_id) { diff --git a/stratosphere/loader/source/ldr_loader_service.hpp b/stratosphere/loader/source/ldr_loader_service.hpp index 249a235b3..f750bf1bd 100644 --- a/stratosphere/loader/source/ldr_loader_service.hpp +++ b/stratosphere/loader/source/ldr_loader_service.hpp @@ -30,8 +30,8 @@ namespace ams::ldr { virtual Result GetProcessModuleInfo(sf::Out count, const sf::OutPointerArray &out, os::ProcessId process_id); /* Atmosphere commands. */ - virtual Result AtmosphereSetExternalContentSource(sf::OutMoveHandle out, ncm::ProgramId program_id); - virtual void AtmosphereClearExternalContentSource(ncm::ProgramId program_id); + virtual Result AtmosphereRegisterExternalCode(sf::OutMoveHandle out, ncm::ProgramId program_id); + virtual void AtmosphereUnregisterExternalCode(ncm::ProgramId program_id); virtual void AtmosphereHasLaunchedProgram(sf::Out out, ncm::ProgramId program_id); virtual Result AtmosphereGetProgramInfo(sf::Out out_program_info, sf::Out out_status, const ncm::ProgramLocation &loc); virtual Result AtmospherePinProgram(sf::Out out_id, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &override_status); @@ -97,16 +97,16 @@ namespace ams::ldr { SetProgramArguments = 0, FlushArguments = 1, - AtmosphereSetExternalContentSource = 65000, - AtmosphereClearExternalContentSource = 65001, + AtmosphereRegisterExternalCode = 65000, + AtmosphereUnregisterExternalCode = 65001, }; public: DEFINE_SERVICE_DISPATCH_TABLE { MAKE_SERVICE_COMMAND_META(SetProgramArguments), MAKE_SERVICE_COMMAND_META(FlushArguments), - MAKE_SERVICE_COMMAND_META(AtmosphereSetExternalContentSource), - MAKE_SERVICE_COMMAND_META(AtmosphereClearExternalContentSource), + MAKE_SERVICE_COMMAND_META(AtmosphereRegisterExternalCode), + MAKE_SERVICE_COMMAND_META(AtmosphereUnregisterExternalCode), }; }; diff --git a/stratosphere/loader/source/ldr_meta.cpp b/stratosphere/loader/source/ldr_meta.cpp index cb873dc00..62ba7a174 100644 --- a/stratosphere/loader/source/ldr_meta.cpp +++ b/stratosphere/loader/source/ldr_meta.cpp @@ -23,7 +23,8 @@ namespace ams::ldr { /* Convenience definitions. */ constexpr size_t MetaCacheBufferSize = 0x8000; - constexpr const char *MetaFilePath = "/main.npdm"; + constexpr inline const char AtmosphereMetaPath[] = ENCODE_ATMOSPHERE_CODE_PATH("/main.npdm"); + constexpr inline const char BaseMetaPath[] = ENCODE_CODE_PATH("/main.npdm"); /* Types. */ struct MetaCache { @@ -93,25 +94,23 @@ namespace ams::ldr { return ResultSuccess(); } - Result LoadMetaFromFile(FILE *f, MetaCache *cache) { + Result LoadMetaFromFile(fs::FileHandle file, MetaCache *cache) { /* Reset cache. */ cache->meta = {}; /* Read from file. */ - size_t npdm_size = 0; + s64 npdm_size = 0; { /* Get file size. */ - fseek(f, 0, SEEK_END); - npdm_size = ftell(f); - fseek(f, 0, SEEK_SET); + R_TRY(fs::GetFileSize(std::addressof(npdm_size), file)); /* Read data into cache buffer. */ - R_UNLESS(npdm_size <= MetaCacheBufferSize, ResultTooLargeMeta()); - R_UNLESS(fread(cache->buffer, npdm_size, 1, f) == 1, ResultTooLargeMeta()); + R_UNLESS(npdm_size <= static_cast(MetaCacheBufferSize), ResultTooLargeMeta()); + R_TRY(fs::ReadFile(file, 0, cache->buffer, npdm_size)); } /* Ensure size is big enough. */ - R_UNLESS(npdm_size >= sizeof(Npdm), ResultInvalidMeta()); + R_UNLESS(npdm_size >= static_cast(sizeof(Npdm)), ResultInvalidMeta()); /* Validate the meta. */ { @@ -146,13 +145,12 @@ namespace ams::ldr { /* API. */ Result LoadMeta(Meta *out_meta, ncm::ProgramId program_id, const cfg::OverrideStatus &status) { - FILE *f = nullptr; - /* Try to load meta from file. */ - R_TRY(OpenCodeFile(f, program_id, status, MetaFilePath)); + fs::FileHandle file; + R_TRY(fs::OpenFile(std::addressof(file), AtmosphereMetaPath, fs::OpenMode_Read)); { - ON_SCOPE_EXIT { fclose(f); }; - R_TRY(LoadMetaFromFile(f, &g_meta_cache)); + ON_SCOPE_EXIT { fs::CloseFile(file); }; + R_TRY(LoadMetaFromFile(file, &g_meta_cache)); } /* Patch meta. Start by setting all program ids to the current program id. */ @@ -163,9 +161,9 @@ namespace ams::ldr { /* For HBL, we need to copy some information from the base meta. */ if (status.IsHbl()) { - if (R_SUCCEEDED(OpenCodeFileFromBaseExefs(f, program_id, status, MetaFilePath))) { - ON_SCOPE_EXIT { fclose(f); }; - if (R_SUCCEEDED(LoadMetaFromFile(f, &g_original_meta_cache))) { + if (R_SUCCEEDED(fs::OpenFile(std::addressof(file), BaseMetaPath, fs::OpenMode_Read))) { + ON_SCOPE_EXIT { fs::CloseFile(file); }; + if (R_SUCCEEDED(LoadMetaFromFile(file, &g_original_meta_cache))) { Meta *o_meta = &g_original_meta_cache.meta; /* Fix pool partition. */ diff --git a/stratosphere/loader/source/ldr_patcher.cpp b/stratosphere/loader/source/ldr_patcher.cpp index 7d11e2439..bf711f549 100644 --- a/stratosphere/loader/source/ldr_patcher.cpp +++ b/stratosphere/loader/source/ldr_patcher.cpp @@ -26,13 +26,42 @@ namespace ams::ldr { constexpr size_t NsoPatchesProtectedSize = sizeof(NsoHeader); constexpr size_t NsoPatchesProtectedOffset = sizeof(NsoHeader); + constexpr const char * const LoaderSdMountName = "#amsldr-sdpatch"; + static_assert(sizeof(LoaderSdMountName) <= fs::MountNameLengthMax); + + os::Mutex g_ldr_sd_lock; + bool g_mounted_sd; + + bool EnsureSdCardMounted() { + std::scoped_lock lk(g_ldr_sd_lock); + + if (g_mounted_sd) { + return true; + } + + if (!cfg::IsSdCardInitialized()) { + return false; + } + + if (R_FAILED(fs::MountSdCard(LoaderSdMountName))) { + return false; + } + + return (g_mounted_sd = true); + } + + } /* Apply IPS patches. */ void LocateAndApplyIpsPatchesToModule(const u8 *build_id, uintptr_t mapped_nso, size_t mapped_size) { + if (!EnsureSdCardMounted()) { + return; + } + ro::ModuleId module_id; std::memcpy(&module_id.build_id, build_id, sizeof(module_id.build_id)); - ams::patcher::LocateAndApplyIpsPatchesToModule(NsoPatchesDirectory, NsoPatchesProtectedSize, NsoPatchesProtectedOffset, &module_id, reinterpret_cast(mapped_nso), mapped_size); + ams::patcher::LocateAndApplyIpsPatchesToModule(LoaderSdMountName, NsoPatchesDirectory, NsoPatchesProtectedSize, NsoPatchesProtectedOffset, &module_id, reinterpret_cast(mapped_nso), mapped_size); } } \ No newline at end of file diff --git a/stratosphere/loader/source/ldr_process_creation.cpp b/stratosphere/loader/source/ldr_process_creation.cpp index 2ef0b4146..468340365 100644 --- a/stratosphere/loader/source/ldr_process_creation.cpp +++ b/stratosphere/loader/source/ldr_process_creation.cpp @@ -15,7 +15,6 @@ */ #include "ldr_capabilities.hpp" #include "ldr_content_management.hpp" -#include "ldr_ecs.hpp" #include "ldr_launch_record.hpp" #include "ldr_meta.hpp" #include "ldr_patcher.hpp" @@ -47,25 +46,25 @@ namespace ams::ldr { Nso_Count, }; - constexpr const char *GetNsoName(size_t idx) { - AMS_ABORT_UNLESS(idx < Nso_Count); + constexpr inline const char *NsoPaths[Nso_Count] = { + ENCODE_ATMOSPHERE_CODE_PATH("/rtld"), + ENCODE_ATMOSPHERE_CODE_PATH("/main"), + ENCODE_ATMOSPHERE_CODE_PATH("/subsdk0"), + ENCODE_ATMOSPHERE_CODE_PATH("/subsdk1"), + ENCODE_ATMOSPHERE_CODE_PATH("/subsdk2"), + ENCODE_ATMOSPHERE_CODE_PATH("/subsdk3"), + ENCODE_ATMOSPHERE_CODE_PATH("/subsdk4"), + ENCODE_ATMOSPHERE_CODE_PATH("/subsdk5"), + ENCODE_ATMOSPHERE_CODE_PATH("/subsdk6"), + ENCODE_ATMOSPHERE_CODE_PATH("/subsdk7"), + ENCODE_ATMOSPHERE_CODE_PATH("/subsdk8"), + ENCODE_ATMOSPHERE_CODE_PATH("/subsdk9"), + ENCODE_ATMOSPHERE_CODE_PATH("/sdk"), + }; - constexpr const char *NsoNames[Nso_Count] = { - "rtld", - "main", - "subsdk0", - "subsdk1", - "subsdk2", - "subsdk3", - "subsdk4", - "subsdk5", - "subsdk6", - "subsdk7", - "subsdk8", - "subsdk9", - "sdk", - }; - return NsoNames[idx]; + constexpr const char *GetNsoPath(size_t idx) { + AMS_ABORT_UNLESS(idx < Nso_Count); + return NsoPaths[idx]; } struct ProcessInfo { @@ -166,17 +165,21 @@ namespace ams::ldr { return static_cast((meta->acid->flags & Acid::AcidFlag_PoolPartitionMask) >> Acid::AcidFlag_PoolPartitionShift); } - Result LoadNsoHeaders(ncm::ProgramId program_id, const cfg::OverrideStatus &override_status, NsoHeader *nso_headers, bool *has_nso) { + Result LoadNsoHeaders(NsoHeader *nso_headers, bool *has_nso) { /* Clear NSOs. */ std::memset(nso_headers, 0, sizeof(*nso_headers) * Nso_Count); std::memset(has_nso, 0, sizeof(*has_nso) * Nso_Count); for (size_t i = 0; i < Nso_Count; i++) { - FILE *f = nullptr; - if (R_SUCCEEDED(OpenCodeFile(f, program_id, override_status, GetNsoName(i)))) { - ON_SCOPE_EXIT { fclose(f); }; + fs::FileHandle file; + if (R_SUCCEEDED(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read))) { + ON_SCOPE_EXIT { fs::CloseFile(file); }; + /* Read NSO header. */ - R_UNLESS(fread(nso_headers + i, sizeof(*nso_headers), 1, f) == 1, ResultInvalidNso()); + size_t read_size; + R_TRY(fs::ReadFile(std::addressof(read_size), file, 0, nso_headers + i, sizeof(*nso_headers))); + R_UNLESS(read_size == sizeof(*nso_headers), ResultInvalidNso()); + has_nso[i] = true; } } @@ -437,7 +440,7 @@ namespace ams::ldr { return svcCreateProcess(out->process_handle.GetPointer(), ¶m, reinterpret_cast(meta->aci_kac), meta->aci->kac_size / sizeof(u32)); } - Result LoadNsoSegment(FILE *f, const NsoHeader::SegmentInfo *segment, size_t file_size, const u8 *file_hash, bool is_compressed, bool check_hash, uintptr_t map_base, uintptr_t map_end) { + Result LoadNsoSegment(fs::FileHandle file, const NsoHeader::SegmentInfo *segment, size_t file_size, const u8 *file_hash, bool is_compressed, bool check_hash, uintptr_t map_base, uintptr_t map_end) { /* Select read size based on compression. */ if (!is_compressed) { file_size = segment->size; @@ -449,8 +452,9 @@ namespace ams::ldr { /* Load data from file. */ uintptr_t load_address = is_compressed ? map_end - file_size : map_base; - fseek(f, segment->file_offset, SEEK_SET); - R_UNLESS(fread(reinterpret_cast(load_address), file_size, 1, f) == 1, ResultInvalidNso()); + size_t read_size; + R_TRY(fs::ReadFile(std::addressof(read_size), file, segment->file_offset, reinterpret_cast(load_address), file_size)); + R_UNLESS(read_size == file_size, ResultInvalidNso()); /* Uncompress if necessary. */ if (is_compressed) { @@ -460,8 +464,8 @@ namespace ams::ldr { /* Check hash if necessary. */ if (check_hash) { - u8 hash[SHA256_HASH_SIZE]; - sha256CalculateHash(hash, reinterpret_cast(map_base), segment->size); + u8 hash[crypto::Sha256Generator::HashSize]; + crypto::GenerateSha256Hash(hash, sizeof(hash), reinterpret_cast(map_base), segment->size); R_UNLESS(std::memcmp(hash, file_hash, sizeof(hash)) == 0, ResultInvalidNso()); } @@ -469,19 +473,19 @@ namespace ams::ldr { return ResultSuccess(); } - Result LoadNsoIntoProcessMemory(Handle process_handle, FILE *f, uintptr_t map_address, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size) { + Result LoadNsoIntoProcessMemory(Handle process_handle, fs::FileHandle file, uintptr_t map_address, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size) { /* Map and read data from file. */ { map::AutoCloseMap mapper(map_address, process_handle, nso_address, nso_size); R_TRY(mapper.GetResult()); /* Load NSO segments. */ - R_TRY(LoadNsoSegment(f, &nso_header->segments[NsoHeader::Segment_Text], nso_header->text_compressed_size, nso_header->text_hash, (nso_header->flags & NsoHeader::Flag_CompressedText) != 0, - (nso_header->flags & NsoHeader::Flag_CheckHashText) != 0, map_address + nso_header->text_dst_offset, map_address + nso_size)); - R_TRY(LoadNsoSegment(f, &nso_header->segments[NsoHeader::Segment_Ro], nso_header->ro_compressed_size, nso_header->ro_hash, (nso_header->flags & NsoHeader::Flag_CompressedRo) != 0, - (nso_header->flags & NsoHeader::Flag_CheckHashRo) != 0, map_address + nso_header->ro_dst_offset, map_address + nso_size)); - R_TRY(LoadNsoSegment(f, &nso_header->segments[NsoHeader::Segment_Rw], nso_header->rw_compressed_size, nso_header->rw_hash, (nso_header->flags & NsoHeader::Flag_CompressedRw) != 0, - (nso_header->flags & NsoHeader::Flag_CheckHashRw) != 0, map_address + nso_header->rw_dst_offset, map_address + nso_size)); + R_TRY(LoadNsoSegment(file, &nso_header->segments[NsoHeader::Segment_Text], nso_header->text_compressed_size, nso_header->text_hash, (nso_header->flags & NsoHeader::Flag_CompressedText) != 0, + (nso_header->flags & NsoHeader::Flag_CheckHashText) != 0, map_address + nso_header->text_dst_offset, map_address + nso_size)); + R_TRY(LoadNsoSegment(file, &nso_header->segments[NsoHeader::Segment_Ro], nso_header->ro_compressed_size, nso_header->ro_hash, (nso_header->flags & NsoHeader::Flag_CompressedRo) != 0, + (nso_header->flags & NsoHeader::Flag_CheckHashRo) != 0, map_address + nso_header->ro_dst_offset, map_address + nso_size)); + R_TRY(LoadNsoSegment(file, &nso_header->segments[NsoHeader::Segment_Rw], nso_header->rw_compressed_size, nso_header->rw_hash, (nso_header->flags & NsoHeader::Flag_CompressedRw) != 0, + (nso_header->flags & NsoHeader::Flag_CheckHashRw) != 0, map_address + nso_header->rw_dst_offset, map_address + nso_size)); /* Clear unused space to zero. */ const size_t text_end = nso_header->text_dst_offset + nso_header->text_size; @@ -513,20 +517,20 @@ namespace ams::ldr { return ResultSuccess(); } - Result LoadNsosIntoProcessMemory(const ProcessInfo *process_info, const ncm::ProgramId program_id, const cfg::OverrideStatus &override_status, const NsoHeader *nso_headers, const bool *has_nso, const args::ArgumentInfo *arg_info) { + Result LoadNsosIntoProcessMemory(const ProcessInfo *process_info, const NsoHeader *nso_headers, const bool *has_nso, const args::ArgumentInfo *arg_info) { const Handle process_handle = process_info->process_handle.Get(); /* Load each NSO. */ for (size_t i = 0; i < Nso_Count; i++) { if (has_nso[i]) { - FILE *f = nullptr; - R_TRY(OpenCodeFile(f, program_id, override_status, GetNsoName(i))); - ON_SCOPE_EXIT { fclose(f); }; + fs::FileHandle file; + R_TRY(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read)); + ON_SCOPE_EXIT { fs::CloseFile(file); }; uintptr_t map_address = 0; R_TRY(map::LocateMappableSpace(&map_address, process_info->nso_size[i])); - R_TRY(LoadNsoIntoProcessMemory(process_handle, f, map_address, nso_headers + i, process_info->nso_address[i], process_info->nso_size[i])); + R_TRY(LoadNsoIntoProcessMemory(process_handle, file, map_address, nso_headers + i, process_info->nso_address[i], process_info->nso_size[i])); } } @@ -576,7 +580,7 @@ namespace ams::ldr { R_TRY(ValidateMeta(&meta, loc)); /* Load, validate NSOs. */ - R_TRY(LoadNsoHeaders(loc.program_id, override_status, nso_headers, has_nso)); + R_TRY(LoadNsoHeaders(nso_headers, has_nso)); R_TRY(ValidateNsoHeaders(nso_headers, has_nso)); /* Actually create process. */ @@ -584,7 +588,7 @@ namespace ams::ldr { R_TRY(CreateProcessImpl(&info, &meta, nso_headers, has_nso, arg_info, flags, reslimit_h)); /* Load NSOs into process memory. */ - R_TRY(LoadNsosIntoProcessMemory(&info, loc.program_id, override_status, nso_headers, has_nso, arg_info)); + R_TRY(LoadNsosIntoProcessMemory(&info, nso_headers, has_nso, arg_info)); /* Register NSOs with ro manager. */ { @@ -603,13 +607,13 @@ namespace ams::ldr { } /* If we're overriding for HBL, perform HTML document redirection. */ - if (mount.IsHblMounted()) { + if (override_status.IsHbl()) { /* Don't validate result, failure is okay. */ RedirectHtmlDocumentPathForHbl(loc); } - /* Clear the ECS entry for the program. */ - R_ABORT_UNLESS(ecs::Clear(loc.program_id)); + /* Clear the external code for the program. */ + fssystem::DestroyExternalCode(loc.program_id); /* Note that we've created the program. */ SetLaunchedProgram(loc.program_id); diff --git a/stratosphere/ro/source/impl/ro_patcher.cpp b/stratosphere/ro/source/impl/ro_patcher.cpp index 23aed3280..992d27f4a 100644 --- a/stratosphere/ro/source/impl/ro_patcher.cpp +++ b/stratosphere/ro/source/impl/ro_patcher.cpp @@ -30,7 +30,7 @@ namespace ams::ro::impl { /* Apply IPS patches. */ void LocateAndApplyIpsPatchesToModule(const ModuleId *module_id, u8 *mapped_nro, size_t mapped_size) { - ams::patcher::LocateAndApplyIpsPatchesToModule(NroPatchesDirectory, NroPatchesProtectedSize, NroPatchesProtectedOffset, module_id, mapped_nro, mapped_size); + ams::patcher::LocateAndApplyIpsPatchesToModule("sdmc", NroPatchesDirectory, NroPatchesProtectedSize, NroPatchesProtectedOffset, module_id, mapped_nro, mapped_size); } } \ No newline at end of file