diff --git a/common/defaults/system_settings.ini b/common/defaults/system_settings.ini index 33d216373..ea59c681f 100644 --- a/common/defaults/system_settings.ini +++ b/common/defaults/system_settings.ini @@ -20,6 +20,12 @@ ; for restoration on new game launch. 1 = always save toggles, ; 0 = only save toggles if toggle file exists. ; dmnt_always_save_cheat_toggles = u8!0x0 +; Enable writing to BIS partitions for HBL. +; This is probably undesirable for normal usage. +; enable_hbl_bis_write = u8!0x0 +; Enable reading the CAL0 partition for HBL. +; This is probably undesirable for normal usage. +; enable_hbl_cal_read = u8!0x0 ; Controls whether fs.mitm should redirect save files ; to directories on the sd card. ; 0 = Do not redirect, 1 = Redirect. diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp new file mode 100644 index 000000000..b57b1da41 --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "fs_shim.h" +#include "fs_mitm_service.hpp" +#include "fsmitm_boot0storage.hpp" + +namespace ams::mitm::fs { + + using namespace ams::fs; + + namespace { + + bool GetSettingsItemBooleanValue(const char *name, const char *key) { + u8 tmp = 0; + AMS_ASSERT(settings::fwdbg::GetSettingsItemValue(&tmp, sizeof(tmp), name, key) == sizeof(tmp)); + return (tmp != 0); + } + + } + + Result FsMitmService::OpenBisStorage(sf::Out> out, u32 _bis_partition_id) { + const ::FsBisPartitionId bis_partition_id = static_cast<::FsBisPartitionId>(_bis_partition_id); + + /* Try to open a storage for the partition. */ + FsStorage bis_storage; + R_TRY(fsOpenBisStorageFwd(this->forward_service.get(), &bis_storage, bis_partition_id)); + const sf::cmif::DomainObjectId target_object_id{bis_storage.s.object_id}; + + const bool is_sysmodule = ncm::IsSystemProgramId(this->client_info.program_id); + const bool is_hbl = this->client_info.override_status.IsHbl(); + const bool can_write_bis = is_sysmodule || (is_hbl && GetSettingsItemBooleanValue("atmosphere", "enable_hbl_bis_write")); + const bool can_read_cal = is_sysmodule || (is_hbl && GetSettingsItemBooleanValue("atmosphere", "enable_hbl_cal_read")); + + /* Allow HBL to write to boot1 (safe firm) + package2. */ + /* This is needed to not break compatibility with ChoiDujourNX, which does not check for write access before beginning an update. */ + /* TODO: get fixed so that this can be turned off without causing bricks :/ */ + const bool is_package2 = (FsBisPartitionId_BootConfigAndPackage2Part1 <= bis_partition_id && bis_partition_id <= FsBisPartitionId_BootConfigAndPackage2Part6); + const bool is_boot1 = bis_partition_id == FsBisPartitionId_BootPartition2Root; + const bool can_write_bis_for_choi_support = is_hbl && (is_package2 || is_boot1); + + /* Set output storage. */ + if (bis_partition_id == FsBisPartitionId_BootPartition1Root) { + out.SetValue(std::make_shared(new Boot0Storage(bis_storage, this->client_info)), target_object_id); + } else if (bis_partition_id == FsBisPartitionId_CalibrationBinary) { + /* PRODINFO should *never* be writable. */ + /* If we have permissions, create a read only storage. */ + if (can_read_cal) { + out.SetValue(std::make_shared(new ReadOnlyStorageAdapter(new RemoteStorage(bis_storage))), target_object_id); + } else { + /* If we can't read cal, return permission denied. */ + fsStorageClose(&bis_storage); + return fs::ResultPermissionDenied(); + } + } else { + if (can_write_bis || can_write_bis_for_choi_support) { + /* We can write, so create a writable storage. */ + out.SetValue(std::make_shared(new RemoteStorage(bis_storage)), target_object_id); + } else { + /* We can only read, so create a readable storage. */ + out.SetValue(std::make_shared(new ReadOnlyStorageAdapter(new RemoteStorage(bis_storage))), target_object_id); + } + } + + return ResultSuccess(); + } + +} diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.hpp index 9e842200b..725152e34 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.hpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.hpp @@ -16,26 +16,63 @@ #pragma once #include +#include "fsmitm_istorage_interface.hpp" namespace ams::mitm::fs { + /* TODO: Consider re-enabling the mitm flag logic. */ + class FsMitmService : public sf::IMitmServiceObject { private: enum class CommandId { - /* TODO */ + OpenFileSystemDeprecated = 0, + + SetCurrentProcess = 1, + OpenFileSystemWithPatch = 7, + OpenFileSystemWithId = 8, + + OpenSdCardFileSystem = 18, + + OpenSaveDataFileSystem = 51, + + OpenBisStorage = 12, + OpenDataStorageByCurrentProcess = 200, + OpenDataStorageByDataId = 202, }; public: static bool ShouldMitm(const sm::MitmProcessInfo &client_info) { - /* TODO */ - return false; + static std::atomic_bool has_launched_qlaunch = false; + + /* TODO: intercepting everything seems to cause issues with sleep mode, for some reason. */ + /* Figure out why, and address it. */ + /* TODO: This may be because pre-rewrite code really mismanaged domain objects in a way that would cause bad things. */ + /* Need to verify if this is fixed now. */ + if (client_info.program_id == ncm::ProgramId::AppletQlaunch || client_info.program_id == ncm::ProgramId::AppletMaintenanceMenu) { + has_launched_qlaunch = true; + } + + return has_launched_qlaunch || client_info.program_id == ncm::ProgramId::Ns || !ncm::IsSystemProgramId(client_info.program_id); } public: SF_MITM_SERVICE_OBJECT_CTOR(FsMitmService) { /* ... */ } protected: - /* TODO */ + /* Overridden commands. */ + /* Result OpenFileSystemWithPatch(Out> out, u64 program_id, u32 filesystem_type); */ + /* Result OpenFileSystemWithId(Out> out, InPointer path, u64 program_id, u32 filesystem_type); */ + /* Result OpenSdCardFileSystem(Out> out); */ + /* Result OpenSaveDataFileSystem(Out> out, u8 space_id, FsSave save_struct); */ + Result OpenBisStorage(sf::Out> out, u32 bis_partition_id); + /* Result OpenDataStorageByCurrentProcess(Out> out); */ + /* Result OpenDataStorageByDataId(Out> out, u64 data_id, u8 storage_id); */ public: DEFINE_SERVICE_DISPATCH_TABLE { - /* TODO */ + /* MAKE_SERVICE_COMMAND_META(OpenFileSystemWithPatch, hos::Version_200), */ + /* MAKE_SERVICE_COMMAND_META(OpenFileSystemWithId, hos::Version_200), */ + /* MAKE_SERVICE_COMMAND_META(OpenSdCardFileSystem), */ + /* MAKE_SERVICE_COMMAND_META(OpenSaveDataFileSystem), */ + MAKE_SERVICE_COMMAND_META(OpenBisStorage), + /* MAKE_SERVICE_COMMAND_META(OpenDataStorageByCurrentProcess), */ + /* MAKE_SERVICE_COMMAND_META(OpenDataStorageByDataId), */ }; }; diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_shim.c b/stratosphere/ams_mitm/source/fs_mitm/fs_shim.c new file mode 100644 index 000000000..5cae82712 --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_shim.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "fs_shim.h" + +/* Missing fsp-srv commands. */ +Result fsOpenBisStorageFwd(Service* s, FsStorage* out, FsBisPartitionId partition_id) { + const u32 tmp = partition_id; + return serviceDispatchIn(s, 12, tmp, + .out_num_objects = 1, + .out_objects = &out->s, + ); +} diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_shim.h b/stratosphere/ams_mitm/source/fs_mitm/fs_shim.h new file mode 100644 index 000000000..2756be20b --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_shim.h @@ -0,0 +1,19 @@ +/** + * @file fs_shim.h + * @brief Filesystem Services (fs) IPC wrapper for fs.mitm. + * @author SciresM + * @copyright libnx Authors + */ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Missing fsp-srv commands. */ +Result fsOpenBisStorageFwd(Service* s, FsStorage* out, FsBisPartitionId partition_id); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp new file mode 100644 index 000000000..0b0dac800 --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "fsmitm_boot0storage.hpp" + +namespace ams::mitm::fs { + + using namespace ams::fs; + + namespace { + + os::Mutex g_boot0_access_mutex; + u8 g_boot0_bct_buffer[Boot0Storage::BctEndOffset]; + + } + + bool Boot0Storage::CanModifyBctPublicKey() { + if (exosphere::IsRcmBugPatched()) { + /* RCM bug patched. */ + /* Only allow NS to update the BCT pubks. */ + /* AutoRCM on a patched unit will cause a brick, so homebrew should NOT be allowed to write. */ + return this->client_info.program_id == ncm::ProgramId::Ns; + } else { + /* RCM bug unpatched. */ + /* Allow homebrew but not NS to update the BCT pubks. */ + return this->client_info.override_status.IsHbl(); + } + } + + Result Boot0Storage::Read(s64 offset, void *_buffer, size_t size) { + std::scoped_lock lk{g_boot0_access_mutex}; + + /* Check if we have nothing to do. */ + if (size == 0) { + return ResultSuccess(); + } + + return Base::Read(offset, _buffer, size); + } + + Result Boot0Storage::Write(s64 offset, const void *_buffer, size_t size) { + std::scoped_lock lk{g_boot0_access_mutex}; + + const u8 *buffer = static_cast(_buffer); + + /* Check if we have nothing to do. */ + if (size == 0) { + return ResultSuccess(); + } + + /* Protect the EKS region from writes. */ + if (offset <= EksStart) { + if (offset + size < EksStart) { + /* Fall through, no need to do anything here. */ + } else { + if (offset + size > EksEnd) { + /* Perform portion of write falling past end of keyblobs. */ + const s64 diff = EksEnd - offset; + R_TRY(Base::Write(EksEnd, buffer + diff, size - diff)); + } + /* Adjust size to avoid writing end of data. */ + size = EksStart - offset; + } + } else if (offset < EksEnd) { + if (offset + size <= EksEnd) { + /* Ignore writes falling strictly within the region. */ + return ResultSuccess(); + } else { + /* Only write past the end of the region. */ + const s64 diff = EksEnd - offset; + buffer += diff; + size -= diff; + offset = EksEnd; + } + } + + /* If we have nothing to write, succeed immediately. */ + if (size == 0) { + return ResultSuccess(); + } + + /* We want to protect AutoRCM from NS on ipatched units. If we can modify bct pubks or we're not touching any of them, proceed. */ + if (this->CanModifyBctPublicKey() || offset >= BctEndOffset || (util::AlignUp(offset, BctSize) >= BctEndOffset && (offset % BctSize) >= BctPubkEnd)) { + return Base::Write(offset, buffer, size); + } + + /* Handle any data written past the end of the pubk region. */ + if (offset + size > BctEndOffset) { + const u64 diff = BctEndOffset - offset; + R_TRY(Base::Write(BctEndOffset, buffer + diff, size - diff)); + size = diff; + } + + /* Read in the current BCT region. */ + R_TRY(Base::Read(0, g_boot0_bct_buffer, BctEndOffset)); + + /* Update the bct buffer. */ + for (u64 cur_offset = offset; cur_offset < BctEndOffset && cur_offset < offset + size; cur_offset++) { + const u64 cur_bct_relative_ofs = cur_offset % BctSize; + if (cur_bct_relative_ofs < BctPubkStart || BctPubkEnd <= cur_bct_relative_ofs) { + g_boot0_bct_buffer[cur_offset] = buffer[cur_offset - offset]; + } + } + + return Base::Write(0, g_boot0_bct_buffer, BctEndOffset); + } + +} diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.hpp new file mode 100644 index 000000000..f2f241c0b --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.hpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::mitm::fs { + + template + class SectoredStorageAdapter : public Base { + static_assert(std::is_base_of::value); + private: + u8 sector_buf[SectorSize]; + public: + /* Inherit constructors. */ + using Base::Base; + public: + virtual Result Read(s64 offset, void *_buffer, size_t size) override { + u8 *buffer = static_cast(_buffer); + + const s64 seek = util::AlignDown(offset, SectorSize); + const s64 sector_ofs = offset - seek; + + /* Check if we have nothing to do. */ + if (size == 0) { + return ResultSuccess(); + } + + /* Fast case. */ + if (sector_ofs == 0 && util::IsAligned(size, SectorSize)) { + return Base::Read(offset, buffer, size); + } + + R_TRY(Base::Read(seek, this->sector_buf, SectorSize)); + + if (size + sector_ofs <= SectorSize) { + /* Staying within the sector. */ + std::memcpy(buffer, this->sector_buf + sector_ofs, size); + } else { + /* Leaving the sector. */ + const size_t size_in_sector = SectorSize - sector_ofs; + std::memcpy(buffer, this->sector_buf + sector_ofs, size_in_sector); + size -= size_in_sector; + + /* Read as many guaranteed aligned sectors as we can. */ + const size_t aligned_remaining_size = util::AlignDown(size, SectorSize); + if (aligned_remaining_size) { + R_TRY(Base::Read(offset + size_in_sector, buffer + size_in_sector, aligned_remaining_size)); + size -= aligned_remaining_size; + } + + /* Read any leftover data. */ + if (size) { + R_TRY(Base::Read(offset + size_in_sector + aligned_remaining_size, this->sector_buf, SectorSize)); + std::memcpy(buffer + size_in_sector + aligned_remaining_size, this->sector_buf, size); + } + } + + return ResultSuccess(); + } + + virtual Result Write(s64 offset, const void *_buffer, size_t size) override { + const u8 *buffer = static_cast(_buffer); + + const s64 seek = util::AlignDown(offset, SectorSize); + const s64 sector_ofs = offset - seek; + + /* Check if we have nothing to do. */ + if (size == 0) { + return ResultSuccess(); + } + + /* Fast case. */ + if (sector_ofs == 0 && util::IsAligned(size, SectorSize)) { + return Base::Write(offset, buffer, size); + } + + /* Load existing sector data. */ + R_TRY(Base::Read(seek, this->sector_buf, SectorSize)); + + if (size + sector_ofs <= SectorSize) { + /* Staying within the sector. */ + std::memcpy(this->sector_buf + sector_ofs, buffer, size); + R_TRY(Base::Write(seek, this->sector_buf, SectorSize)); + } else { + /* Leaving the sector. */ + const size_t size_in_sector = SectorSize - sector_ofs; + std::memcpy(this->sector_buf + sector_ofs, buffer, size_in_sector); + R_TRY(Base::Write(seek, this->sector_buf, SectorSize)); + size -= size_in_sector; + + /* Write as many guaranteed aligned sectors as we can. */ + const size_t aligned_remaining_size = util::AlignDown(size, SectorSize); + if (aligned_remaining_size) { + R_TRY(Base::Write(offset + size_in_sector, buffer + size_in_sector, aligned_remaining_size)); + size -= aligned_remaining_size; + } + + /* Write any leftover data. */ + if (size) { + R_TRY(Base::Read(offset + size_in_sector + aligned_remaining_size, this->sector_buf, SectorSize)); + std::memcpy(this->sector_buf, buffer + size_in_sector + aligned_remaining_size, size); + R_TRY(Base::Write(offset + size_in_sector + aligned_remaining_size, this->sector_buf, SectorSize)); + } + } + + return ResultSuccess(); + } + }; + + /* Represents an RCM-preserving BOOT0 partition. */ + class Boot0Storage : public SectoredStorageAdapter { + public: + using Base = SectoredStorageAdapter; + + static constexpr s64 BctEndOffset = 0xFC000; + static constexpr s64 BctSize = static_cast(ams::updater::BctSize); + static constexpr s64 BctPubkStart = 0x210; + static constexpr s64 BctPubkSize = 0x100; + static constexpr s64 BctPubkEnd = BctPubkStart + BctPubkSize; + + static constexpr s64 EksStart = 0x180000; + static constexpr s64 EksSize = static_cast(ams::updater::EksSize); + static constexpr s64 EksEnd = EksStart + EksSize; + private: + sm::MitmProcessInfo client_info; + private: + bool CanModifyBctPublicKey(); + public: + Boot0Storage(FsStorage *s, const sm::MitmProcessInfo &c) : Base(s), client_info(c) { /* ... */ } + Boot0Storage(FsStorage s, const sm::MitmProcessInfo &c) : Base(s), client_info(c) { /* ... */ } + public: + virtual Result Read(s64 offset, void *_buffer, size_t size) override; + virtual Result Write(s64 offset, const void *_buffer, size_t size) override; + }; + +} diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_istorage_interface.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_istorage_interface.cpp new file mode 100644 index 000000000..2effb8295 --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_istorage_interface.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "fsmitm_istorage_interface.hpp" + +namespace ams::mitm::fs { + + using namespace ams::fs; + + Result IStorageInterface::Read(s64 offset, const sf::OutNonSecureBuffer &buffer, s64 size) { + /* TODO: N retries on ResultDataCorrupted, we may want to eventually. */ + R_UNLESS(offset >= 0, fs::ResultInvalidOffset()); + R_UNLESS(size >= 0, fs::ResultInvalidSize()); + return this->base_storage->Read(offset, buffer.GetPointer(), size); + } + + Result IStorageInterface::Write(s64 offset, const sf::InNonSecureBuffer &buffer, s64 size) { + /* TODO: N increases thread priority temporarily when writing. We may want to eventually. */ + R_UNLESS(offset >= 0, fs::ResultInvalidOffset()); + R_UNLESS(size >= 0, fs::ResultInvalidSize()); + return this->base_storage->Write(offset, buffer.GetPointer(), size); + } + + Result IStorageInterface::Flush() { + return this->base_storage->Flush(); + } + + Result IStorageInterface::SetSize(s64 size) { + R_UNLESS(size >= 0, fs::ResultInvalidSize()); + return this->base_storage->SetSize(size); + } + + Result IStorageInterface::GetSize(sf::Out out) { + return this->base_storage->GetSize(out.GetPointer()); + } + + Result IStorageInterface::OperateRange(sf::Out out, s32 op_id, s64 offset, s64 size) { + /* N includes this redundant check, so we will too. */ + R_UNLESS(out.GetPointer() != nullptr, fs::ResultNullptrArgument()); + + out->Clear(); + if (op_id == static_cast(fs::OperationId::QueryRange)) { + fs::StorageQueryRangeInfo info; + R_TRY(this->base_storage->OperateRange(&info, sizeof(info), fs::OperationId::QueryRange, offset, size, nullptr, 0)); + out->Merge(info); + } + + return ResultSuccess(); + } + +} diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_istorage_interface.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_istorage_interface.hpp new file mode 100644 index 000000000..3c98f1bbf --- /dev/null +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_istorage_interface.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::mitm::fs { + + class IStorageInterface : public sf::IServiceObject { + private: + enum class CommandId { + Read = 0, + Write = 1, + Flush = 2, + SetSize = 3, + GetSize = 4, + OperateRange = 5, + }; + private: + std::unique_ptr base_storage; + public: + IStorageInterface(ams::fs::IStorage *s) : base_storage(s) { /* ... */ } + IStorageInterface(std::unique_ptr s) : base_storage(std::move(s)) { /* ... */ } + private: + /* Command API. */ + virtual Result Read(s64 offset, const sf::OutNonSecureBuffer &buffer, s64 size) final; + virtual Result Write(s64 offset, const sf::InNonSecureBuffer &buffer, s64 size) final; + virtual Result Flush() final; + virtual Result SetSize(s64 size) final; + virtual Result GetSize(sf::Out out) final; + virtual Result OperateRange(sf::Out out, s32 op_id, s64 offset, s64 size) final; + public: + DEFINE_SERVICE_DISPATCH_TABLE { + /* 1.0.0- */ + MAKE_SERVICE_COMMAND_META(Read), + MAKE_SERVICE_COMMAND_META(Write), + MAKE_SERVICE_COMMAND_META(Flush), + MAKE_SERVICE_COMMAND_META(SetSize), + MAKE_SERVICE_COMMAND_META(GetSize), + + /* 4.0.0- */ + MAKE_SERVICE_COMMAND_META(OperateRange, hos::Version_400), + }; + }; + +} diff --git a/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp b/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp index 783315b17..f28fc576c 100644 --- a/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/settings_sd_kvs.cpp @@ -322,6 +322,14 @@ namespace ams::settings::fwdbg { /* Set to "normal" for normal reboot, "rcm" for rcm reboot. */ R_ASSERT(ParseSettingsItemValue("atmosphere", "power_menu_reboot_function", "str!payload")); + /* Enable writing to BIS partitions for HBL. */ + /* This is probably undesirable for normal usage. */ + R_ASSERT(ParseSettingsItemValue("atmosphere", "enable_hbl_bis_write", "u8!0x0")); + + /* Enable HBL to read the CAL0 partition. */ + /* This is probably undesirable for normal usage. */ + R_ASSERT(ParseSettingsItemValue("atmosphere", "enable_hbl_cal_read", "u8!0x0")); + /* Controls whether dmnt cheats should be toggled on or off by */ /* default. 1 = toggled on by default, 0 = toggled off by default. */ R_ASSERT(ParseSettingsItemValue("atmosphere", "dmnt_cheats_enabled_by_default", "u8!0x1")); diff --git a/stratosphere/libstratosphere/include/stratosphere.hpp b/stratosphere/libstratosphere/include/stratosphere.hpp index 0a1363660..19ba7185c 100644 --- a/stratosphere/libstratosphere/include/stratosphere.hpp +++ b/stratosphere/libstratosphere/include/stratosphere.hpp @@ -48,3 +48,6 @@ #include "stratosphere/sm.hpp" #include "stratosphere/spl.hpp" #include "stratosphere/updater.hpp" + +/* Include FS last. */ +#include "stratosphere/fs.hpp" diff --git a/stratosphere/libstratosphere/include/stratosphere/fs.hpp b/stratosphere/libstratosphere/include/stratosphere/fs.hpp new file mode 100644 index 000000000..d1347e3a5 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fs.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include "fs/fs_common.hpp" +#include "fs/fsa/fs_ifile.hpp" +#include "fs/fsa/fs_idirectory.hpp" +#include "fs/fsa/fs_ifilesystem.hpp" +#include "fs/fs_remote_filesystem.hpp" +#include "fs/fs_istorage.hpp" +#include "fs/fs_remote_storage.hpp" +#include "fs/fs_query_range.hpp" diff --git a/stratosphere/libstratosphere/include/stratosphere/fs/fs_common.hpp b/stratosphere/libstratosphere/include/stratosphere/fs/fs_common.hpp new file mode 100644 index 000000000..cbeaeeded --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fs/fs_common.hpp @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include "../os.hpp" +#include "../ncm.hpp" +#include "../sf.hpp" diff --git a/stratosphere/libstratosphere/include/stratosphere/fs/fs_directory.hpp b/stratosphere/libstratosphere/include/stratosphere/fs/fs_directory.hpp new file mode 100644 index 000000000..01c8eb2d5 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fs/fs_directory.hpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" + +namespace ams::fs { + + using DirectoryEntry = ::FsDirectoryEntry; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/fs/fs_file.hpp b/stratosphere/libstratosphere/include/stratosphere/fs/fs_file.hpp new file mode 100644 index 000000000..f7872f512 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fs/fs_file.hpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" + +namespace ams::fs { + + struct ReadOption { + u32 value; + + static const ReadOption None; + }; + + inline constexpr const ReadOption ReadOption::None = {FsReadOption_None}; + + inline constexpr bool operator==(const ReadOption &lhs, const ReadOption &rhs) { + return lhs.value == rhs.value; + } + + inline constexpr bool operator!=(const ReadOption &lhs, const ReadOption &rhs) { + return !(lhs == rhs); + } + + static_assert(std::is_pod::value && sizeof(ReadOption) == sizeof(u32)); + + struct WriteOption { + u32 value; + + constexpr inline bool HasFlushFlag() const { + return this->value & FsWriteOption_Flush; + } + + static const WriteOption None; + static const WriteOption Flush; + }; + + inline constexpr const WriteOption WriteOption::None = {FsWriteOption_None}; + inline constexpr const WriteOption WriteOption::Flush = {FsWriteOption_Flush}; + + inline constexpr bool operator==(const WriteOption &lhs, const WriteOption &rhs) { + return lhs.value == rhs.value; + } + + inline constexpr bool operator!=(const WriteOption &lhs, const WriteOption &rhs) { + return !(lhs == rhs); + } + + static_assert(std::is_pod::value && sizeof(WriteOption) == sizeof(u32)); + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/fs/fs_filesystem.hpp b/stratosphere/libstratosphere/include/stratosphere/fs/fs_filesystem.hpp new file mode 100644 index 000000000..86581f3d3 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fs/fs_filesystem.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" + +namespace ams::fs { + + enum OpenMode { + OpenMode_Read = ::FsOpenMode_Read, + OpenMode_Write = ::FsOpenMode_Write, + OpenMode_Append = ::FsOpenMode_Append, + + OpenMode_ReadWrite = (OpenMode_Read | OpenMode_Write), + OpenMode_All = (OpenMode_ReadWrite | OpenMode_Append), + }; + + enum OpenDirectoryMode { + OpenDirectoryMode_Directory = ::FsDirOpenMode_ReadDirs, + OpenDirectoryMode_File = ::FsDirOpenMode_ReadFiles, + + OpenDirectoryMode_All = (OpenDirectoryMode_Directory | OpenDirectoryMode_File), + + /* TODO: Separate enum, like N? */ + OpenDirectoryMode_NotRequireFileSize = ::FsDirOpenMode_NoFileSize, + }; + + enum DirectoryEntryType { + DirectoryEntryType_Directory = ::FsDirEntryType_Dir, + DirectoryEntryType_File = ::FsDirEntryType_File, + }; + + enum CreateOption { + CreateOption_BigFile = ::FsCreateOption_BigFile, + }; + + using FileTimeStampRaw = ::FsTimeStampRaw; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/fs/fs_istorage.hpp b/stratosphere/libstratosphere/include/stratosphere/fs/fs_istorage.hpp new file mode 100644 index 000000000..8a462582f --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fs/fs_istorage.hpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" +#include "fs_file.hpp" +#include "fs_operate_range.hpp" + +namespace ams::fs { + + class IStorage { + public: + virtual ~IStorage() { /* ... */ } + + virtual Result Read(s64 offset, void *buffer, size_t size) = 0; + + virtual Result Write(s64 offset, const void *buffer, size_t size) { + return fs::ResultUnsupportedOperation(); + } + + virtual Result Flush() = 0; + + virtual Result SetSize(s64 size) { + return fs::ResultUnsupportedOperation(); + } + + virtual Result GetSize(s64 *out) = 0; + + virtual Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) { + return fs::ResultUnsupportedOperation(); + } + + virtual Result OperateRange(OperationId op_id, s64 offset, s64 size) { + return this->OperateRange(nullptr, 0, op_id, offset, size, nullptr, 0); + } + public: + static inline bool IsRangeValid(s64 offset, s64 size, s64 total_size) { + return offset >= 0 && + size >= 0 && + size <= total_size && + offset <= (total_size - size); + } + + static inline bool IsRangeValid(s64 offset, size_t size, s64 total_size) { + return IsRangeValid(offset, static_cast(size), total_size); + } + + static inline bool IsOffsetAndSizeValid(s64 offset, s64 size) { + return offset >= 0 && + size >= 0 && + offset <= (offset + size); + } + + static inline bool IsOffsetAndSizeValid(s64 offset, size_t size) { + return IsOffsetAndSizeValid(offset, static_cast(size)); + } + }; + + class ReadOnlyStorageAdapter : public IStorage { + private: + std::shared_ptr shared_storage; + std::unique_ptr unique_storage; + IStorage *storage; + public: + ReadOnlyStorageAdapter(IStorage *s) : unique_storage(s) { + this->storage = this->unique_storage.get(); + } + ReadOnlyStorageAdapter(std::shared_ptr s) : shared_storage(s) { + this->storage = this->shared_storage.get(); + } + ReadOnlyStorageAdapter(std::unique_ptr s) : unique_storage(std::move(s)) { + this->storage = this->unique_storage.get(); + } + + virtual ~ReadOnlyStorageAdapter() { /* ... */ } + public: + virtual Result Read(s64 offset, void *buffer, size_t size) { + return this->storage->Read(offset, buffer, size); + } + + virtual Result Flush() { + return this->storage->Flush(); + } + + virtual Result GetSize(s64 *out) { + return this->storage->GetSize(out); + } + + virtual Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { + return this->storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size); + } + }; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/fs/fs_operate_range.hpp b/stratosphere/libstratosphere/include/stratosphere/fs/fs_operate_range.hpp new file mode 100644 index 000000000..3896da2e2 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fs/fs_operate_range.hpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" + +namespace ams::fs { + + enum class OperationId : u64 { + Clear = ::FsOperationId_Clear, + ClearSignature = ::FsOperationId_ClearSignature, + InvalidateCache = ::FsOperationId_InvalidateCache, + QueryRange = ::FsOperationId_QueryRange, + }; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/fs/fs_query_range.hpp b/stratosphere/libstratosphere/include/stratosphere/fs/fs_query_range.hpp new file mode 100644 index 000000000..9bab0e67d --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fs/fs_query_range.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" + +namespace ams::fs { + + struct QueryRangeInfo { + u32 aes_ctr_key_type; + u32 speed_emulation_type; + u32 reserved[0x38 / sizeof(u32)]; + + void Clear() { + this->aes_ctr_key_type = 0; + this->speed_emulation_type = 0; + std::memset(this->reserved, 0, sizeof(this->reserved)); + } + + void Merge(const QueryRangeInfo &rhs) { + this->aes_ctr_key_type |= rhs.aes_ctr_key_type; + this->speed_emulation_type |= rhs.speed_emulation_type; + } + }; + + static_assert(std::is_pod::value); + static_assert(sizeof(QueryRangeInfo) == 0x40); + static_assert(sizeof(QueryRangeInfo) == sizeof(::FsRangeInfo)); + + using FileQueryRangeInfo = QueryRangeInfo; + using StorageQueryRangeInfo = QueryRangeInfo; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/fs/fs_remote_filesystem.hpp b/stratosphere/libstratosphere/include/stratosphere/fs/fs_remote_filesystem.hpp new file mode 100644 index 000000000..8aaee50f9 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fs/fs_remote_filesystem.hpp @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" +#include "fsa/fs_ifile.hpp" +#include "fsa/fs_idirectory.hpp" +#include "fsa/fs_ifilesystem.hpp" + +namespace ams::fs { + + class RemoteFile : public fsa::IFile { + private: + std::unique_ptr<::FsFile> base_file; + public: + RemoteFile(::FsFile *f) : base_file(f) { /* ... */ } + RemoteFile(std::unique_ptr<::FsFile> f) : base_file(std::move(f)) { /* ... */ } + RemoteFile(::FsFile f) { + this->base_file = std::make_unique<::FsFile>(f); + } + + virtual ~RemoteFile() { fsFileClose(this->base_file.get()); } + public: + virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const ReadOption &option) override final { + return fsFileRead(this->base_file.get(), offset, buffer, size, option.value, out); + } + + virtual Result GetSizeImpl(s64 *out) override final { + return fsFileGetSize(this->base_file.get(), reinterpret_cast(out)); + } + + virtual Result FlushImpl() override final { + return fsFileFlush(this->base_file.get()); + } + + virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const WriteOption &option) override final { + return fsFileWrite(this->base_file.get(), offset, buffer, size, option.value); + } + + virtual Result SetSizeImpl(s64 size) override final { + return fsFileSetSize(this->base_file.get(), size); + } + + virtual Result OperateRangeImpl(void *dst, size_t dst_size, 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(); + } + }; + + class RemoteDirectory : public fsa::IDirectory { + private: + std::unique_ptr<::FsDir> base_dir; + public: + RemoteDirectory(::FsDir *d) : base_dir(d) { /* ... */ } + RemoteDirectory(std::unique_ptr<::FsDir> d) : base_dir(std::move(d)) { /* ... */ } + RemoteDirectory(::FsDir d) { + this->base_dir = std::make_unique<::FsDir>(d); + } + + virtual ~RemoteDirectory() { fsDirClose(this->base_dir.get()); } + public: + virtual Result ReadImpl(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) override final { + return fsDirRead(this->base_dir.get(), 0, reinterpret_cast(out_count), max_entries, out_entries); + } + + virtual Result GetEntryCountImpl(s64 *out) override final { + return fsDirGetEntryCount(this->base_dir.get(), reinterpret_cast(out)); + } + }; + + class RemoteFileSystem : public fsa::IFileSystem { + private: + std::unique_ptr<::FsFileSystem> base_fs; + public: + RemoteFileSystem(::FsFileSystem *fs) : base_fs(fs) { /* ... */ } + RemoteFileSystem(std::unique_ptr<::FsFileSystem> fs) : base_fs(std::move(fs)) { /* ... */ } + RemoteFileSystem(::FsFileSystem fs) { + this->base_fs = std::make_unique<::FsFileSystem>(fs); + } + + virtual ~RemoteFileSystem() { fsFsClose(this->base_fs.get()); } + public: + virtual Result CreateFileImpl(const char *path, s64 size, int flags) override final { + return fsFsCreateFile(this->base_fs.get(), path, size, flags); + } + + virtual Result DeleteFileImpl(const char *path) override final { + return fsFsDeleteFile(this->base_fs.get(), path); + } + + virtual Result CreateDirectoryImpl(const char *path) override final { + return fsFsCreateDirectory(this->base_fs.get(), path); + } + + virtual Result DeleteDirectoryImpl(const char *path) override final { + return fsFsDeleteDirectory(this->base_fs.get(), path); + } + + virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override final { + return fsFsDeleteDirectoryRecursively(this->base_fs.get(), path); + } + + virtual Result RenameFileImpl(const char *old_path, const char *new_path) override final { + return fsFsRenameFile(this->base_fs.get(), old_path, new_path); + } + + virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override final { + return fsFsRenameDirectory(this->base_fs.get(), old_path, new_path); + } + + virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const char *path) override final { + static_assert(sizeof(::FsDirEntryType) == sizeof(DirectoryEntryType)); + return fsFsGetEntryType(this->base_fs.get(), path, reinterpret_cast<::FsDirEntryType *>(out)); + } + + virtual Result OpenFileImpl(std::unique_ptr *out_file, const char *path, OpenMode mode) override final { + FsFile f; + R_TRY(fsFsOpenFile(this->base_fs.get(), path, mode, &f)); + + *out_file = std::make_unique(f); + return ResultSuccess(); + } + + virtual Result OpenDirectoryImpl(std::unique_ptr *out_dir, const char *path, OpenDirectoryMode mode) override final { + FsDir d; + R_TRY(fsFsOpenDirectory(this->base_fs.get(), path, mode, &d)); + + *out_dir = std::make_unique(d); + return ResultSuccess(); + } + + virtual Result CommitImpl() override final { + return fsFsCommit(this->base_fs.get()); + } + + + virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) { + return fsFsGetFreeSpace(this->base_fs.get(), path, reinterpret_cast(out)); + } + + virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) { + return fsFsGetTotalSpace(this->base_fs.get(), path, reinterpret_cast(out)); + } + + virtual Result CleanDirectoryRecursivelyImpl(const char *path) { + return fsFsCleanDirectoryRecursively(this->base_fs.get(), path); + } + + virtual Result GetFileTimeStampRawImpl(FileTimeStampRaw *out, const char *path) { + static_assert(sizeof(FileTimeStampRaw) == sizeof(::FsTimeStampRaw)); + return fsFsGetFileTimeStampRaw(this->base_fs.get(), path, reinterpret_cast<::FsTimeStampRaw *>(out)); + } + + virtual Result QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, fsa::QueryType query, const char *path) { + return fsFsQueryEntry(this->base_fs.get(), dst, dst_size, src, src_size, path, static_cast(query)); + } + }; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/fs/fs_remote_storage.hpp b/stratosphere/libstratosphere/include/stratosphere/fs/fs_remote_storage.hpp new file mode 100644 index 000000000..07b08c947 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fs/fs_remote_storage.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "fs_common.hpp" +#include "fs_istorage.hpp" + +namespace ams::fs { + + class RemoteStorage : public IStorage { + private: + std::unique_ptr<::FsStorage> base_storage; + public: + RemoteStorage(::FsStorage *s) : base_storage(s) { /* ... */ } + RemoteStorage(std::unique_ptr<::FsStorage> s) : base_storage(std::move(s)) { /* ... */ } + RemoteStorage(::FsStorage s) { + this->base_storage = std::make_unique<::FsStorage>(s); + } + + virtual ~RemoteStorage() { fsStorageClose(this->base_storage.get()); } + public: + virtual Result Read(s64 offset, void *buffer, size_t size) override { + return fsStorageRead(this->base_storage.get(), offset, buffer, size); + }; + + virtual Result Write(s64 offset, const void *buffer, size_t size) override { + return fsStorageWrite(this->base_storage.get(), offset, buffer, size); + }; + + virtual Result Flush() override { + return fsStorageFlush(this->base_storage.get()); + }; + + virtual Result GetSize(s64 *out_size) override { + return fsStorageGetSize(this->base_storage.get(), reinterpret_cast(out_size)); + }; + + virtual Result SetSize(s64 size) override { + return fsStorageSetSize(this->base_storage.get(), size); + }; + + virtual Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { + /* TODO: How to deal with this? */ + return fs::ResultUnsupportedOperation(); + }; + }; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/fs/fsa/fs_idirectory.hpp b/stratosphere/libstratosphere/include/stratosphere/fs/fsa/fs_idirectory.hpp new file mode 100644 index 000000000..ab3e06b48 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fs/fsa/fs_idirectory.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include "../fs_common.hpp" +#include "../fs_directory.hpp" + +namespace ams::fs::fsa { + + class IDirectory { + public: + virtual ~IDirectory() { /* ... */ } + + Result Read(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) { + R_UNLESS(out_count != nullptr, fs::ResultNullptrArgument()); + if (max_entries == 0) { + *out_count = 0; + return ResultSuccess(); + } + R_UNLESS(out_entries != nullptr, fs::ResultNullptrArgument()); + R_UNLESS(max_entries > 0, fs::ResultInvalidArgument()); + return this->ReadImpl(out_count, out_entries, max_entries); + } + + Result GetEntryCount(s64 *out) { + R_UNLESS(out != nullptr, fs::ResultNullptrArgument()); + return this->GetEntryCountImpl(out); + } + protected: + /* ...? */ + private: + virtual Result ReadImpl(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) = 0; + virtual Result GetEntryCountImpl(s64 *out) = 0; + }; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/fs/fsa/fs_ifile.hpp b/stratosphere/libstratosphere/include/stratosphere/fs/fsa/fs_ifile.hpp new file mode 100644 index 000000000..e81cae59f --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fs/fsa/fs_ifile.hpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include "../fs_common.hpp" +#include "../fs_file.hpp" +#include "../fs_operate_range.hpp" + +namespace ams::fs::fsa { + + class IFile { + public: + virtual ~IFile() { /* ... */ } + + Result Read(size_t *out, s64 offset, void *buffer, size_t size, const ReadOption &option) { + R_UNLESS(out != nullptr, fs::ResultNullptrArgument()); + if (size == 0) { + *out = 0; + return ResultSuccess(); + } + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + R_UNLESS(offset >= 0, fs::ResultOutOfRange()); + const s64 signed_size = static_cast(size); + R_UNLESS(signed_size >= 0, fs::ResultOutOfRange()); + R_UNLESS((std::numeric_limits::max() - offset) >= signed_size, fs::ResultOutOfRange()); + return this->ReadImpl(out, offset, buffer, size, option); + } + + Result Read(size_t *out, s64 offset, void *buffer, size_t size) { + return this->Read(out, offset, buffer, size, ReadOption::None); + } + + Result GetSize(s64 *out) { + R_UNLESS(out != nullptr, fs::ResultNullptrArgument()); + return this->GetSizeImpl(out); + } + + Result Flush() { + return this->FlushImpl(); + } + + Result Write(s64 offset, const void *buffer, size_t size, const WriteOption &option) { + if (size == 0) { + if (option.HasFlushFlag()) { + R_TRY(this->Flush()); + } + return ResultSuccess(); + } + R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); + R_UNLESS(offset >= 0, fs::ResultOutOfRange()); + const s64 signed_size = static_cast(size); + R_UNLESS(signed_size >= 0, fs::ResultOutOfRange()); + R_UNLESS((std::numeric_limits::max() - offset) >= signed_size, fs::ResultOutOfRange()); + return this->WriteImpl(offset, buffer, size, option); + } + + Result SetSize(s64 size) { + R_UNLESS(size >= 0, fs::ResultOutOfRange()); + return this->SetSizeImpl(size); + } + + Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) { + return this->OperateRangeImpl(dst, dst_size, op_id, offset, size, src, src_size); + } + + Result OperateRange(OperationId op_id, s64 offset, s64 size) { + return this->OperateRangeImpl(nullptr, 0, op_id, offset, size, nullptr, 0); + } + protected: + /* ...? */ + private: + virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const ReadOption &option) = 0; + virtual Result GetSizeImpl(s64 *out) = 0; + virtual Result FlushImpl() = 0; + virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const WriteOption &option) = 0; + virtual Result SetSizeImpl(s64 size) = 0; + virtual Result OperateRangeImpl(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) = 0; + }; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/fs/fsa/fs_ifilesystem.hpp b/stratosphere/libstratosphere/include/stratosphere/fs/fsa/fs_ifilesystem.hpp new file mode 100644 index 000000000..98a7a9e4b --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fs/fsa/fs_ifilesystem.hpp @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include "../fs_common.hpp" +#include "../fs_filesystem.hpp" + +namespace ams::fs::fsa { + + class IFile; + class IDirectory; + + enum class QueryType { + SetArchiveBit = FsFileSystemQueryType_SetArchiveBit + }; + + class IFileSystem { + public: + virtual ~IFileSystem() { /* ... */ } + + Result CreateFile(const char *path, s64 size, int option) { + R_UNLESS(path != nullptr, fs::ResultInvalidPath()); + R_UNLESS(size >= 0, fs::ResultOutOfRange()); + return this->CreateFileImpl(path, size, option); + } + + Result CreateFile(const char *path, s64 size) { + return this->CreateFile(path, size, 0); + } + + Result DeleteFile(const char *path) { + R_UNLESS(path != nullptr, fs::ResultInvalidPath()); + return this->DeleteFileImpl(path); + } + + Result CreateDirectory(const char *path) { + R_UNLESS(path != nullptr, fs::ResultInvalidPath()); + return this->CreateDirectoryImpl(path); + } + + Result DeleteDirectory(const char *path) { + R_UNLESS(path != nullptr, fs::ResultInvalidPath()); + return this->DeleteDirectoryImpl(path); + } + + Result DeleteDirectoryRecursively(const char *path) { + R_UNLESS(path != nullptr, fs::ResultInvalidPath()); + return this->DeleteDirectoryRecursivelyImpl(path); + } + + Result RenameFile(const char *old_path, const char *new_path) { + R_UNLESS(old_path != nullptr, fs::ResultInvalidPath()); + R_UNLESS(new_path != nullptr, fs::ResultInvalidPath()); + return this->RenameFileImpl(old_path, new_path); + } + + Result RenameDirectory(const char *old_path, const char *new_path) { + R_UNLESS(old_path != nullptr, fs::ResultInvalidPath()); + R_UNLESS(new_path != nullptr, fs::ResultInvalidPath()); + return this->RenameDirectoryImpl(old_path, new_path); + } + + Result GetEntryType(DirectoryEntryType *out, const char *path) { + R_UNLESS(path != nullptr, fs::ResultInvalidPath()); + R_UNLESS(out != nullptr, fs::ResultNullptrArgument()); + return this->GetEntryTypeImpl(out, path); + } + + Result OpenFile(std::unique_ptr *out_file, const char *path, OpenMode mode) { + R_UNLESS(path != nullptr, fs::ResultInvalidPath()); + R_UNLESS(out_file != nullptr, fs::ResultNullptrArgument()); + R_UNLESS((mode & OpenMode_ReadWrite) != 0, fs::ResultInvalidArgument()); + R_UNLESS((mode & ~OpenMode_All) == 0, fs::ResultInvalidArgument()); + return this->OpenFileImpl(out_file, path, mode); + } + + Result OpenDirectory(std::unique_ptr *out_dir, const char *path, OpenDirectoryMode mode) { + R_UNLESS(path != nullptr, fs::ResultInvalidPath()); + R_UNLESS(out_dir != nullptr, fs::ResultNullptrArgument()); + R_UNLESS((mode & OpenDirectoryMode_All) != 0, fs::ResultInvalidArgument()); + R_UNLESS((mode & ~OpenDirectoryMode_All) == 0, fs::ResultInvalidArgument()); + return this->OpenDirectoryImpl(out_dir, path, mode); + } + + Result Commit() { + return this->CommitImpl(); + } + + Result GetFreeSpaceSize(s64 *out, const char *path) { + R_UNLESS(path != nullptr, fs::ResultInvalidPath()); + R_UNLESS(out != nullptr, fs::ResultNullptrArgument()); + return this->GetFreeSpaceSizeImpl(out, path); + } + + Result GetTotalSpaceSize(s64 *out, const char *path) { + R_UNLESS(path != nullptr, fs::ResultInvalidPath()); + R_UNLESS(out != nullptr, fs::ResultNullptrArgument()); + return this->GetTotalSpaceSizeImpl(out, path); + } + + Result CleanDirectoryRecursively(const char *path) { + R_UNLESS(path != nullptr, fs::ResultInvalidPath()); + return this->CleanDirectoryRecursivelyImpl(path); + } + + Result GetFileTimeStampRaw(FileTimeStampRaw *out, const char *path) { + R_UNLESS(path != nullptr, fs::ResultInvalidPath()); + R_UNLESS(out != nullptr, fs::ResultNullptrArgument()); + return this->GetFileTimeStampRawImpl(out, path); + } + + Result QueryEntry(char *dst, size_t dst_size, const char *src, size_t src_size, QueryType query, const char *path) { + R_UNLESS(path != nullptr, fs::ResultInvalidPath()); + return this->QueryEntryImpl(dst, dst_size, src, src_size, query, path); + } + + /* These aren't accessible as commands. */ + + Result CommitProvisionally(s64 counter) { + return this->CommitProvisionallyImpl(counter); + } + + Result Rollback() { + return this->RollbackImpl(); + } + + Result Flush() { + return this->FlushImpl(); + } + + protected: + /* ...? */ + private: + virtual Result CreateFileImpl(const char *path, s64 size, int flags) = 0; + virtual Result DeleteFileImpl(const char *path) = 0; + virtual Result CreateDirectoryImpl(const char *path) = 0; + virtual Result DeleteDirectoryImpl(const char *path) = 0; + virtual Result DeleteDirectoryRecursivelyImpl(const char *path) = 0; + virtual Result RenameFileImpl(const char *old_path, const char *new_path) = 0; + virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) = 0; + virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const char *path) = 0; + virtual Result OpenFileImpl(std::unique_ptr *out_file, const char *path, OpenMode mode) = 0; + virtual Result OpenDirectoryImpl(std::unique_ptr *out_dir, const char *path, OpenDirectoryMode mode) = 0; + virtual Result CommitImpl() = 0; + + virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) { + return fs::ResultNotImplemented(); + } + + virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) { + return fs::ResultNotImplemented(); + } + + virtual Result CleanDirectoryRecursivelyImpl(const char *path) = 0; + + virtual Result GetFileTimeStampRawImpl(FileTimeStampRaw *out, const char *path) { + return fs::ResultNotImplemented(); + } + + virtual Result QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, QueryType query, const char *path) { + return fs::ResultNotImplemented(); + } + + /* These aren't accessible as commands. */ + virtual Result CommitProvisionallyImpl(s64 counter) { + return fs::ResultNotImplemented(); + } + + virtual Result RollbackImpl() { + return fs::ResultNotImplemented(); + } + + virtual Result FlushImpl() { + return fs::ResultNotImplemented(); + } + }; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp index 27d70b847..0627aed2c 100644 --- a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_manager.hpp @@ -51,6 +51,8 @@ namespace ams::sf::cmif { explicit Domain(ServerDomainManager *m) : manager(m) { /* ... */ } ~Domain(); + void DestroySelf(); + virtual ServerDomainBase *GetServerDomain() override final { return static_cast(this); } @@ -116,10 +118,9 @@ namespace ams::sf::cmif { } return new (storage) Domain(this); } - - inline void FreeDomainServiceObject(DomainServiceObject *object) { - static_cast(object)->~Domain(); - this->FreeDomain(object); + public: + static void DestroyDomainServiceObject(DomainServiceObject *obj) { + static_cast(obj)->DestroySelf(); } }; diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_service_object.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_service_object.hpp index 9dbea1009..77758190e 100644 --- a/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_service_object.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/sf/cmif/sf_cmif_domain_service_object.hpp @@ -103,6 +103,8 @@ namespace ams::sf::cmif { class MitmDomainServiceObject : public DomainServiceObject{}; + static_assert(sizeof(DomainServiceObject) == sizeof(MitmDomainServiceObject)); + template<> struct ServiceDispatchTraits { static_assert(std::is_base_of::value, "DomainServiceObject must derive from sf::IServiceObject"); diff --git a/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_domain_session_manager.hpp b/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_domain_session_manager.hpp index 6d5c45274..38cdd574c 100644 --- a/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_domain_session_manager.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/sf/hipc/sf_hipc_server_domain_session_manager.hpp @@ -32,10 +32,6 @@ namespace ams::sf::hipc { inline cmif::DomainServiceObject *AllocateDomainServiceObject() { return cmif::ServerDomainManager::AllocateDomainServiceObject(); } - - inline void FreeDomainServiceObject(cmif::DomainServiceObject *object) { - cmif::ServerDomainManager::FreeDomainServiceObject(object); - } }; } diff --git a/stratosphere/libstratosphere/source/ams/ams_exosphere_api.cpp b/stratosphere/libstratosphere/source/ams/ams_exosphere_api.cpp index 9d97f2c69..f1233ec3f 100644 --- a/stratosphere/libstratosphere/source/ams/ams_exosphere_api.cpp +++ b/stratosphere/libstratosphere/source/ams/ams_exosphere_api.cpp @@ -58,7 +58,7 @@ namespace ams::exosphere { namespace { inline Result GetRcmBugPatched(bool *out) { - u64 tmp; + u64 tmp = 0; R_TRY(spl::smc::ConvertResult(spl::smc::GetConfig(&tmp, 1, SplConfigItem_ExosphereHasRcmBugPatch))); *out = (tmp != 0); return ResultSuccess(); diff --git a/stratosphere/libstratosphere/source/sf/cmif/sf_cmif_domain_manager.cpp b/stratosphere/libstratosphere/source/sf/cmif/sf_cmif_domain_manager.cpp index 6be09d7cd..67b9ccb8e 100644 --- a/stratosphere/libstratosphere/source/sf/cmif/sf_cmif_domain_manager.cpp +++ b/stratosphere/libstratosphere/source/sf/cmif/sf_cmif_domain_manager.cpp @@ -31,6 +31,12 @@ namespace ams::sf::cmif { } } + void ServerDomainManager::Domain::DestroySelf() { + ServerDomainManager *manager = this->manager; + this->~Domain(); + manager->FreeDomain(this); + } + Result ServerDomainManager::Domain::ReserveIds(DomainObjectId *out_ids, size_t count) { for (size_t i = 0; i < count; i++) { Entry *entry = this->manager->entry_manager.AllocateEntry(); diff --git a/stratosphere/libstratosphere/source/sf/hipc/sf_hipc_server_domain_session_manager.cpp b/stratosphere/libstratosphere/source/sf/hipc/sf_hipc_server_domain_session_manager.cpp index 85c4aa419..fcf827375 100644 --- a/stratosphere/libstratosphere/source/sf/hipc/sf_hipc_server_domain_session_manager.cpp +++ b/stratosphere/libstratosphere/source/sf/hipc/sf_hipc_server_domain_session_manager.cpp @@ -63,7 +63,7 @@ namespace ams::sf::hipc { /* Allocate a domain. */ auto domain = this->manager->AllocateDomainServiceObject(); R_UNLESS(domain, sf::hipc::ResultOutOfDomains()); - auto domain_guard = SCOPE_GUARD { this->manager->FreeDomainServiceObject(domain); }; + auto domain_guard = SCOPE_GUARD { cmif::ServerDomainManager::DestroyDomainServiceObject(static_cast(domain)); }; cmif::DomainObjectId object_id = cmif::InvalidDomainObjectId; @@ -80,8 +80,8 @@ namespace ams::sf::hipc { /* Create new object. */ cmif::MitmDomainServiceObject *domain_ptr = static_cast(domain); - new_holder = cmif::ServiceObjectHolder(std::move(std::shared_ptr(domain_ptr, [&](cmif::MitmDomainServiceObject *obj) { - this->manager->FreeDomainServiceObject(domain); + new_holder = cmif::ServiceObjectHolder(std::move(std::shared_ptr(domain_ptr, [](cmif::MitmDomainServiceObject *obj) { + cmif::ServerDomainManager::DestroyDomainServiceObject(static_cast(obj)); }))); } else { /* We're not a mitm session. Reserve a new object in the domain. */ @@ -89,8 +89,8 @@ namespace ams::sf::hipc { /* Create new object. */ cmif::DomainServiceObject *domain_ptr = static_cast(domain); - new_holder = cmif::ServiceObjectHolder(std::move(std::shared_ptr(domain_ptr, [&](cmif::DomainServiceObject *obj) { - this->manager->FreeDomainServiceObject(domain); + new_holder = cmif::ServiceObjectHolder(std::move(std::shared_ptr(domain_ptr, [](cmif::DomainServiceObject *obj) { + cmif::ServerDomainManager::DestroyDomainServiceObject(static_cast(obj)); }))); }