From 746dbfe01888f7c73987e259b8ad464a989fea61 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 5 Dec 2019 23:41:33 -0800 Subject: [PATCH] ams_mitm: Implement emummc Nintendo folder redirection --- stratosphere/ams_mitm/source/amsmitm_main.cpp | 2 +- .../ams_mitm/source/amsmitm_module.hpp | 2 +- .../source/bpc_mitm/bpc_ams_power_utils.cpp | 10 +- .../source/fs_mitm/fs_mitm_service.cpp | 22 +- .../source/fs_mitm/fs_mitm_service.hpp | 9 +- .../ams_mitm/source/fs_mitm/fs_shim.c | 16 +- .../ams_mitm/source/fs_mitm/fs_shim.h | 1 + .../fs_mitm/fsmitm_istorage_interface.hpp | 58 --- stratosphere/boot/source/boot_check_clock.cpp | 2 +- stratosphere/boot/source/boot_display.cpp | 21 +- stratosphere/boot/source/boot_main.cpp | 2 +- stratosphere/boot/source/boot_power_utils.cpp | 8 +- .../boot/source/boot_repair_boot_images.cpp | 2 +- stratosphere/boot/source/gpio/gpio_utils.cpp | 2 +- .../source/i2c/driver/impl/i2c_registers.hpp | 4 +- stratosphere/boot2/source/boot2_main.cpp | 2 +- .../creport/source/creport_crash_report.hpp | 2 +- stratosphere/creport/source/creport_main.cpp | 2 +- .../creport/source/creport_modules.cpp | 2 +- .../dmnt/source/cheat/impl/dmnt_cheat_api.cpp | 6 +- .../impl/dmnt_cheat_debug_events_manager.cpp | 4 +- stratosphere/dmnt/source/dmnt_main.cpp | 2 +- stratosphere/eclct.stub/source/eclct_stub.cpp | 2 +- stratosphere/fatal/source/fatal_main.cpp | 2 +- stratosphere/fatal/source/fatal_task.hpp | 4 +- stratosphere/libstratosphere/Makefile | 2 +- .../include/atmosphere/common_includes.hpp | 1 + .../include/atmosphere/results/fs_results.hpp | 8 + .../libstratosphere/include/stratosphere.hpp | 2 + .../include/stratosphere/fs.hpp | 2 + .../include/stratosphere/fs/fs_common.hpp | 7 + .../include/stratosphere/fs/fs_directory.hpp | 2 + .../include/stratosphere/fs/fs_path_tool.hpp | 73 ++++ .../include/stratosphere/fs/fs_path_utils.hpp | 48 +++ .../stratosphere/fs/fs_remote_filesystem.hpp | 8 + .../stratosphere/fs/fsa/fs_idirectory.hpp | 3 + .../include/stratosphere/fs/fsa/fs_ifile.hpp | 3 + .../stratosphere/fs/fsa/fs_ifilesystem.hpp | 10 +- .../include/stratosphere/fssrv.hpp | 19 + .../fssrv/fssrv_interface_adapters.hpp | 18 + .../fssrv/fssrv_path_normalizer.hpp | 66 ++++ .../stratosphere/fssrv/fssrv_sf_path.hpp | 50 +++ .../fssrv_filesystem_interface_adapter.hpp | 192 ++++++++++ .../fssrv_storage_interface_adapter.hpp | 78 ++++ .../include/stratosphere/fssystem.hpp | 21 ++ ...ystem_directory_redirection_filesystem.hpp | 43 +++ .../fssystem_path_resolution_filesystem.hpp | 173 +++++++++ .../fssystem/fssystem_path_tool.hpp | 26 ++ .../fssystem_subdirectory_filesystem.hpp | 40 ++ .../fssystem/fssystem_utility.hpp | 34 ++ .../include/stratosphere/os.hpp | 1 + .../stratosphere/os/os_memory_common.hpp | 25 ++ .../include/stratosphere/os/os_semaphore.hpp | 41 ++- .../include/stratosphere/os/os_thread.hpp | 5 +- .../stratosphere/os/os_waitable_holder.hpp | 2 + .../source/ams/ams_emummc_api.cpp | 2 +- .../source/dd/dd_io_mappings.cpp | 2 +- .../source/fs/fs_path_tool.cpp | 248 +++++++++++++ .../source/fs/fs_path_utils.cpp | 47 +++ .../fssrv_filesystem_interface_adapter.cpp | 343 ++++++++++++++++++ .../source/fssrv/fssrv_path_normalizer.cpp | 77 ++++ .../fssrv_storage_interface_adapter.cpp} | 49 ++- ...ystem_directory_redirection_filesystem.cpp | 115 ++++++ .../fssystem_subdirectory_filesystem.cpp | 78 ++++ .../os/impl/os_waitable_holder_impl.hpp | 3 + .../impl/os_waitable_holder_of_semaphore.hpp | 52 +++ .../source/os/os_semaphore.cpp | 85 +++++ .../source/os/os_waitable_holder.cpp | 8 + .../source/spl/smc/spl_smc.cpp | 2 +- .../source/updater/updater_api.cpp | 12 +- .../source/updater/updater_bis_save.cpp | 2 +- stratosphere/loader/source/ldr_main.cpp | 2 +- .../loader/source/ldr_process_creation.cpp | 6 +- stratosphere/pm/source/pm_main.cpp | 2 +- .../ro/source/impl/ro_service_impl.cpp | 36 +- stratosphere/sm/source/sm_main.cpp | 2 +- stratosphere/spl/source/spl_api_impl.cpp | 10 +- stratosphere/spl/source/spl_main.cpp | 2 +- 78 files changed, 2190 insertions(+), 187 deletions(-) delete mode 100644 stratosphere/ams_mitm/source/fs_mitm/fsmitm_istorage_interface.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/fs/fs_path_tool.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/fs/fs_path_utils.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/fssrv.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/fssrv/fssrv_interface_adapters.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/fssrv/fssrv_path_normalizer.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/fssrv/fssrv_sf_path.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/fssrv/interface_adapters/fssrv_filesystem_interface_adapter.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/fssrv/interface_adapters/fssrv_storage_interface_adapter.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/fssystem.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/fssystem/fssystem_directory_redirection_filesystem.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/fssystem/fssystem_path_resolution_filesystem.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/fssystem/fssystem_path_tool.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/fssystem/fssystem_subdirectory_filesystem.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/fssystem/fssystem_utility.hpp create mode 100644 stratosphere/libstratosphere/include/stratosphere/os/os_memory_common.hpp create mode 100644 stratosphere/libstratosphere/source/fs/fs_path_tool.cpp create mode 100644 stratosphere/libstratosphere/source/fs/fs_path_utils.cpp create mode 100644 stratosphere/libstratosphere/source/fssrv/fssrv_filesystem_interface_adapter.cpp create mode 100644 stratosphere/libstratosphere/source/fssrv/fssrv_path_normalizer.cpp rename stratosphere/{ams_mitm/source/fs_mitm/fsmitm_istorage_interface.cpp => libstratosphere/source/fssrv/fssrv_storage_interface_adapter.cpp} (51%) create mode 100644 stratosphere/libstratosphere/source/fssystem/fssystem_directory_redirection_filesystem.cpp create mode 100644 stratosphere/libstratosphere/source/fssystem/fssystem_subdirectory_filesystem.cpp create mode 100644 stratosphere/libstratosphere/source/os/impl/os_waitable_holder_of_semaphore.hpp create mode 100644 stratosphere/libstratosphere/source/os/os_semaphore.cpp diff --git a/stratosphere/ams_mitm/source/amsmitm_main.cpp b/stratosphere/ams_mitm/source/amsmitm_main.cpp index 1da942444..f27f47bae 100644 --- a/stratosphere/ams_mitm/source/amsmitm_main.cpp +++ b/stratosphere/ams_mitm/source/amsmitm_main.cpp @@ -33,7 +33,7 @@ extern "C" { void __appExit(void); /* Exception handling. */ - alignas(16) u8 __nx_exception_stack[0x1000]; + alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize]; u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); void __libnx_exception_handler(ThreadExceptionDump *ctx); } diff --git a/stratosphere/ams_mitm/source/amsmitm_module.hpp b/stratosphere/ams_mitm/source/amsmitm_module.hpp index 3cda6e8c7..63f1edb53 100644 --- a/stratosphere/ams_mitm/source/amsmitm_module.hpp +++ b/stratosphere/ams_mitm/source/amsmitm_module.hpp @@ -25,7 +25,7 @@ namespace ams::mitm { public: \ static constexpr size_t ThreadPriority = prio; \ static constexpr size_t StackSize = ss; \ - alignas(0x1000) static inline u8 Stack[StackSize]; \ + alignas(os::MemoryPageSize) static inline u8 Stack[StackSize]; \ public: \ static void ThreadFunction(void *); \ } diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp index 2584d195a..cf693e3b0 100644 --- a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp @@ -34,8 +34,8 @@ namespace ams::mitm::bpc { }; /* Globals. */ - alignas(0x1000) u8 g_work_page[0x1000]; - alignas(0x1000) u8 g_reboot_payload[IramPayloadMaxSize]; + alignas(os::MemoryPageSize) u8 g_work_page[os::MemoryPageSize]; + alignas(os::MemoryPageSize) u8 g_reboot_payload[IramPayloadMaxSize]; RebootType g_reboot_type = RebootType::ToRcm; /* Helpers. */ @@ -54,9 +54,9 @@ namespace ams::mitm::bpc { ClearIram(); /* Copy in payload. */ - for (size_t ofs = 0; ofs < sizeof(g_reboot_payload); ofs += 0x1000) { - std::memcpy(g_work_page, &g_reboot_payload[ofs], std::min(sizeof(g_reboot_payload) - ofs, size_t(0x1000))); - exosphere::CopyToIram(IramPayloadBase + ofs, g_work_page, 0x1000); + for (size_t ofs = 0; ofs < sizeof(g_reboot_payload); ofs += sizeof(g_work_page)) { + std::memcpy(g_work_page, &g_reboot_payload[ofs], std::min(sizeof(g_reboot_payload) - ofs, sizeof(g_work_page))); + exosphere::CopyToIram(IramPayloadBase + ofs, g_work_page, sizeof(g_work_page)); } /* Copy in fatal error context, if relevant. */ diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp index 7ae9f5a6e..409548f84 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp @@ -62,13 +62,29 @@ namespace ams::mitm::fs { } + Result FsMitmService::OpenSdCardFileSystem(sf::Out> out) { + /* We only care about redirecting this for NS/emummc. */ + R_UNLESS(this->client_info.program_id == ncm::ProgramId::Ns, sm::mitm::ResultShouldForwardToSession()); + R_UNLESS(emummc::IsActive(), sm::mitm::ResultShouldForwardToSession()); + + /* Create a new SD card filesystem. */ + FsFileSystem sd_fs; + R_TRY(fsOpenSdCardFileSystemFwd(this->forward_service.get(), &sd_fs)); + const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(&sd_fs.s)}; + + /* Return output filesystem. */ + std::shared_ptr redir_fs = std::make_shared(std::make_shared(sd_fs), "/Nintendo", emummc::GetNintendoDirPath()); + out.SetValue(std::make_shared(std::move(redir_fs), false), target_object_id); + return ResultSuccess(); + } + 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 sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(&bis_storage.s)}; const bool is_sysmodule = ncm::IsSystemProgramId(this->client_info.program_id); const bool is_hbl = this->client_info.override_status.IsHbl(); @@ -118,7 +134,7 @@ namespace ams::mitm::fs { /* Try to open the process romfs. */ FsStorage data_storage; R_TRY(fsOpenDataStorageByCurrentProcessFwd(this->forward_service.get(), &data_storage)); - const sf::cmif::DomainObjectId target_object_id{data_storage.s.object_id}; + const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(&data_storage.s)}; /* Try to get a storage from the cache. */ { @@ -160,7 +176,7 @@ namespace ams::mitm::fs { /* Try to open the process romfs. */ FsStorage data_storage; R_TRY(fsOpenDataStorageByDataIdFwd(this->forward_service.get(), &data_storage, static_cast(data_id), static_cast(storage_id))); - const sf::cmif::DomainObjectId target_object_id{data_storage.s.object_id}; + const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(&data_storage.s)}; /* Try to get a storage from the cache. */ { 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 c7d5c9c4d..cd68beabd 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.hpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.hpp @@ -16,10 +16,13 @@ #pragma once #include -#include "fsmitm_istorage_interface.hpp" +#include namespace ams::mitm::fs { + using IStorageInterface = ams::fssrv::impl::StorageInterfaceAdapter; + using IFileSystemInterface = ams::fssrv::impl::FileSystemInterfaceAdapter; + /* TODO: Consider re-enabling the mitm flag logic. */ class FsMitmService : public sf::IMitmServiceObject { @@ -59,7 +62,7 @@ namespace ams::mitm::fs { /* 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 OpenSdCardFileSystem(sf::Out> out); /* Result OpenSaveDataFileSystem(Out> out, u8 space_id, FsSave save_struct); */ Result OpenBisStorage(sf::Out> out, u32 bis_partition_id); Result OpenDataStorageByCurrentProcess(sf::Out> out); @@ -68,7 +71,7 @@ namespace ams::mitm::fs { DEFINE_SERVICE_DISPATCH_TABLE { /* 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(OpenSdCardFileSystem), /* MAKE_SERVICE_COMMAND_META(OpenSaveDataFileSystem), */ MAKE_SERVICE_COMMAND_META(OpenBisStorage), MAKE_SERVICE_COMMAND_META(OpenDataStorageByCurrentProcess), diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_shim.c b/stratosphere/ams_mitm/source/fs_mitm/fs_shim.c index 65ad02a26..e836b31ae 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fs_shim.c +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_shim.c @@ -16,6 +16,17 @@ #include "fs_shim.h" /* Missing fsp-srv commands. */ +static Result _fsOpenSession(Service *s, Service* out, u32 cmd_id) { + return serviceDispatch(s, cmd_id, + .out_num_objects = 1, + .out_objects = out, + ); +} + +Result fsOpenSdCardFileSystemFwd(Service* s, FsFileSystem* out) { + return _fsOpenSession(s, &out->s, 18); +} + Result fsOpenBisStorageFwd(Service* s, FsStorage* out, FsBisPartitionId partition_id) { const u32 tmp = partition_id; return serviceDispatchIn(s, 12, tmp, @@ -25,10 +36,7 @@ Result fsOpenBisStorageFwd(Service* s, FsStorage* out, FsBisPartitionId partitio } Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out) { - return serviceDispatch(s, 200, - .out_num_objects = 1, - .out_objects = &out->s, - ); + return _fsOpenSession(s, &out->s, 200); } Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorage* out, u64 data_id, NcmStorageId storage_id) { diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_shim.h b/stratosphere/ams_mitm/source/fs_mitm/fs_shim.h index 7591b922f..910ee0075 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fs_shim.h +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_shim.h @@ -12,6 +12,7 @@ extern "C" { #endif /* Missing fsp-srv commands. */ +Result fsOpenSdCardFileSystemFwd(Service* s, FsFileSystem* out); Result fsOpenBisStorageFwd(Service* s, FsStorage* out, FsBisPartitionId partition_id); Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out); Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorage* out, u64 data_id, NcmStorageId storage_id); diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_istorage_interface.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_istorage_interface.hpp deleted file mode 100644 index 3c98f1bbf..000000000 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_istorage_interface.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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/boot/source/boot_check_clock.cpp b/stratosphere/boot/source/boot_check_clock.cpp index b80796293..637a1e789 100644 --- a/stratosphere/boot/source/boot_check_clock.cpp +++ b/stratosphere/boot/source/boot_check_clock.cpp @@ -34,7 +34,7 @@ namespace ams::boot { /* Helpers. */ bool IsUsbClockValid() { - uintptr_t car_regs = dd::GetIoMapping(0x60006000ul, 0x1000); + uintptr_t car_regs = dd::GetIoMapping(0x60006000ul, os::MemoryPageSize); const u32 pllu = reg::Read(car_regs + 0xC0); const u32 utmip = reg::Read(car_regs + 0x480); diff --git a/stratosphere/boot/source/boot_display.cpp b/stratosphere/boot/source/boot_display.cpp index 1c70ecb60..63cc5ed04 100644 --- a/stratosphere/boot/source/boot_display.cpp +++ b/stratosphere/boot/source/boot_display.cpp @@ -42,18 +42,19 @@ namespace ams::boot { constexpr size_t FrameBufferHeight = 1280; constexpr size_t FrameBufferSize = FrameBufferHeight * FrameBufferWidth * sizeof(u32); - constexpr uintptr_t Disp1Base = 0x54200000ul; - constexpr uintptr_t DsiBase = 0x54300000ul; - constexpr uintptr_t ClkRstBase = 0x60006000ul; - constexpr uintptr_t GpioBase = 0x6000D000ul; + constexpr uintptr_t Disp1Base = 0x54200000ul; + constexpr uintptr_t DsiBase = 0x54300000ul; + constexpr uintptr_t ClkRstBase = 0x60006000ul; + constexpr uintptr_t GpioBase = 0x6000D000ul; constexpr uintptr_t ApbMiscBase = 0x70000000ul; constexpr uintptr_t MipiCalBase = 0x700E3000ul; - constexpr size_t Disp1Size = 0x3000; - constexpr size_t DsiSize = 0x1000; - constexpr size_t ClkRstSize = 0x1000; - constexpr size_t GpioSize = 0x1000; - constexpr size_t ApbMiscSize = 0x1000; - constexpr size_t MipiCalSize = 0x1000; + + constexpr size_t Disp1Size = 3 * os::MemoryPageSize; + constexpr size_t DsiSize = os::MemoryPageSize; + constexpr size_t ClkRstSize = os::MemoryPageSize; + constexpr size_t GpioSize = os::MemoryPageSize; + constexpr size_t ApbMiscSize = os::MemoryPageSize; + constexpr size_t MipiCalSize = os::MemoryPageSize; /* Types. */ diff --git a/stratosphere/boot/source/boot_main.cpp b/stratosphere/boot/source/boot_main.cpp index 509726e41..922649ac2 100644 --- a/stratosphere/boot/source/boot_main.cpp +++ b/stratosphere/boot/source/boot_main.cpp @@ -45,7 +45,7 @@ extern "C" { void __appExit(void); /* Exception handling. */ - alignas(16) u8 __nx_exception_stack[0x1000]; + alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize]; u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); void __libnx_exception_handler(ThreadExceptionDump *ctx); } diff --git a/stratosphere/boot/source/boot_power_utils.cpp b/stratosphere/boot/source/boot_power_utils.cpp index d600962bc..08f29eb97 100644 --- a/stratosphere/boot/source/boot_power_utils.cpp +++ b/stratosphere/boot/source/boot_power_utils.cpp @@ -27,7 +27,7 @@ namespace ams::boot { constexpr size_t IramPayloadMaxSize = 0x2E000; /* Globals. */ - u8 __attribute__ ((aligned (0x1000))) g_work_page[0x1000]; + alignas(os::MemoryPageSize) u8 g_work_page[os::MemoryPageSize]; /* Helpers. */ void ClearIram() { @@ -45,9 +45,9 @@ namespace ams::boot { ClearIram(); /* Copy in payload. */ - for (size_t ofs = 0; ofs < fusee_primary_bin_size; ofs += 0x1000) { - std::memcpy(g_work_page, &fusee_primary_bin[ofs], std::min(static_cast(fusee_primary_bin_size - ofs), size_t(0x1000))); - exosphere::CopyToIram(IramPayloadBase + ofs, g_work_page, 0x1000); + for (size_t ofs = 0; ofs < fusee_primary_bin_size; ofs += sizeof(g_work_page)) { + std::memcpy(g_work_page, &fusee_primary_bin[ofs], std::min(static_cast(fusee_primary_bin_size - ofs), sizeof(g_work_page))); + exosphere::CopyToIram(IramPayloadBase + ofs, g_work_page, sizeof(g_work_page)); } diff --git a/stratosphere/boot/source/boot_repair_boot_images.cpp b/stratosphere/boot/source/boot_repair_boot_images.cpp index 95bf67ff5..e56ba52b6 100644 --- a/stratosphere/boot/source/boot_repair_boot_images.cpp +++ b/stratosphere/boot/source/boot_repair_boot_images.cpp @@ -21,7 +21,7 @@ namespace ams::boot { namespace { /* Globals. */ - alignas(0x1000) u8 g_boot_image_work_buffer[0x10000]; + alignas(os::MemoryPageSize) u8 g_boot_image_work_buffer[0x10000]; } diff --git a/stratosphere/boot/source/gpio/gpio_utils.cpp b/stratosphere/boot/source/gpio/gpio_utils.cpp index 1b642293f..b29fb684e 100644 --- a/stratosphere/boot/source/gpio/gpio_utils.cpp +++ b/stratosphere/boot/source/gpio/gpio_utils.cpp @@ -39,7 +39,7 @@ namespace ams::gpio { uintptr_t GetBaseAddress() { if (!g_initialized_gpio_vaddr) { - g_gpio_vaddr = dd::GetIoMapping(PhysicalBase, 0x1000); + g_gpio_vaddr = dd::GetIoMapping(PhysicalBase, os::MemoryPageSize); g_initialized_gpio_vaddr = true; } return g_gpio_vaddr; diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_registers.hpp b/stratosphere/boot/source/i2c/driver/impl/i2c_registers.hpp index c699cd955..3a16ceeba 100644 --- a/stratosphere/boot/source/i2c/driver/impl/i2c_registers.hpp +++ b/stratosphere/boot/source/i2c/driver/impl/i2c_registers.hpp @@ -83,7 +83,7 @@ namespace ams::i2c::driver::impl { 12, 22, 3, 7, 15, 6 }; - const uintptr_t registers = dd::GetIoMapping(0x60006000ul, 0x1000); + const uintptr_t registers = dd::GetIoMapping(0x60006000ul, os::MemoryPageSize); const size_t idx = ConvertToIndex(bus); this->clk_src_reg = registers + s_clk_src_offsets[idx]; this->clk_en_reg = registers + s_clk_en_offsets[idx]; @@ -97,7 +97,7 @@ namespace ams::i2c::driver::impl { 0x0000, 0x0400, 0x0500, 0x0700, 0x1000, 0x1100 }; - const uintptr_t registers = dd::GetIoMapping(0x7000c000ul, 0x2000) + s_offsets[ConvertToIndex(bus)]; + const uintptr_t registers = dd::GetIoMapping(0x7000c000ul, 2 * os::MemoryPageSize) + s_offsets[ConvertToIndex(bus)]; return reinterpret_cast(registers); } diff --git a/stratosphere/boot2/source/boot2_main.cpp b/stratosphere/boot2/source/boot2_main.cpp index 657261782..755598d4f 100644 --- a/stratosphere/boot2/source/boot2_main.cpp +++ b/stratosphere/boot2/source/boot2_main.cpp @@ -31,7 +31,7 @@ extern "C" { void __appExit(void); /* Exception handling. */ - alignas(16) u8 __nx_exception_stack[0x1000]; + alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize]; u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); void __libnx_exception_handler(ThreadExceptionDump *ctx); } diff --git a/stratosphere/creport/source/creport_crash_report.hpp b/stratosphere/creport/source/creport_crash_report.hpp index 7f1081f9f..2cfb88418 100644 --- a/stratosphere/creport/source/creport_crash_report.hpp +++ b/stratosphere/creport/source/creport_crash_report.hpp @@ -21,7 +21,7 @@ namespace ams::creport { class CrashReport { private: - static constexpr size_t DyingMessageSizeMax = 0x1000; + static constexpr size_t DyingMessageSizeMax = os::MemoryPageSize; private: Handle debug_handle = INVALID_HANDLE; bool has_extra_info = true; diff --git a/stratosphere/creport/source/creport_main.cpp b/stratosphere/creport/source/creport_main.cpp index e1310b11e..a83249afe 100644 --- a/stratosphere/creport/source/creport_main.cpp +++ b/stratosphere/creport/source/creport_main.cpp @@ -33,7 +33,7 @@ extern "C" { void __appExit(void); /* Exception handling. */ - alignas(16) u8 __nx_exception_stack[0x1000]; + alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize]; u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); void __libnx_exception_handler(ThreadExceptionDump *ctx); } diff --git a/stratosphere/creport/source/creport_modules.cpp b/stratosphere/creport/source/creport_modules.cpp index 6997c862d..d20744954 100644 --- a/stratosphere/creport/source/creport_modules.cpp +++ b/stratosphere/creport/source/creport_modules.cpp @@ -229,7 +229,7 @@ namespace ams::creport { } /* We want to read the last two pages of .rodata. */ - static u8 s_last_rodata_pages[2 * 0x1000]; + static u8 s_last_rodata_pages[2 * os::MemoryPageSize]; const size_t read_size = mi.size >= sizeof(s_last_rodata_pages) ? sizeof(s_last_rodata_pages) : (sizeof(s_last_rodata_pages) / 2); if (R_FAILED(svcReadDebugProcessMemory(s_last_rodata_pages, this->debug_handle, mi.addr + mi.size - read_size, read_size))) { return; diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp index 7a7a695bc..23e291681 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_api.cpp @@ -50,9 +50,9 @@ namespace ams::dmnt::cheat::impl { CheatEntry cheat_entries[MaxCheatCount] = {}; std::map frozen_addresses_map; - alignas(0x1000) u8 detect_thread_stack[ThreadStackSize] = {}; - alignas(0x1000) u8 debug_events_thread_stack[ThreadStackSize] = {}; - alignas(0x1000) u8 vm_thread_stack[ThreadStackSize] = {}; + alignas(os::MemoryPageSize) u8 detect_thread_stack[ThreadStackSize] = {}; + alignas(os::MemoryPageSize) u8 debug_events_thread_stack[ThreadStackSize] = {}; + alignas(os::MemoryPageSize) u8 vm_thread_stack[ThreadStackSize] = {}; private: static void DetectLaunchThread(void *_this); static void VirtualMachineThread(void *_this); diff --git a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp index 5c1d3b941..10390f5d2 100644 --- a/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp +++ b/stratosphere/dmnt/source/cheat/impl/dmnt_cheat_debug_events_manager.cpp @@ -24,14 +24,14 @@ namespace ams::dmnt::cheat::impl { class DebugEventsManager { public: static constexpr size_t NumCores = 4; - static constexpr size_t ThreadStackSize = 0x1000; + static constexpr size_t ThreadStackSize = os::MemoryPageSize; static constexpr size_t ThreadPriority = 24; private: std::array message_queues; std::array threads; os::Event continued_event; - alignas(0x1000) u8 thread_stacks[NumCores][ThreadStackSize]; + alignas(os::MemoryPageSize) u8 thread_stacks[NumCores][ThreadStackSize]; private: static void PerCoreThreadFunction(void *_this) { /* This thread will wait on the appropriate message queue. */ diff --git a/stratosphere/dmnt/source/dmnt_main.cpp b/stratosphere/dmnt/source/dmnt_main.cpp index 3f6fddecc..c6dec00a3 100644 --- a/stratosphere/dmnt/source/dmnt_main.cpp +++ b/stratosphere/dmnt/source/dmnt_main.cpp @@ -122,7 +122,7 @@ namespace { static_assert(TotalThreads >= 1, "TotalThreads"); constexpr size_t NumExtraThreads = TotalThreads - 1; constexpr size_t ThreadStackSize = 0x4000; - alignas(0x1000) u8 g_extra_thread_stacks[NumExtraThreads][ThreadStackSize]; + alignas(os::MemoryPageSize) u8 g_extra_thread_stacks[NumExtraThreads][ThreadStackSize]; os::Thread g_extra_threads[NumExtraThreads]; diff --git a/stratosphere/eclct.stub/source/eclct_stub.cpp b/stratosphere/eclct.stub/source/eclct_stub.cpp index da998fb82..7e92913f8 100644 --- a/stratosphere/eclct.stub/source/eclct_stub.cpp +++ b/stratosphere/eclct.stub/source/eclct_stub.cpp @@ -29,7 +29,7 @@ extern "C" { void __appExit(void); /* Exception handling. */ - alignas(16) u8 __nx_exception_stack[0x1000]; + alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize]; u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); void __libnx_exception_handler(ThreadExceptionDump *ctx); } diff --git a/stratosphere/fatal/source/fatal_main.cpp b/stratosphere/fatal/source/fatal_main.cpp index 771ce02fa..a4351dc85 100644 --- a/stratosphere/fatal/source/fatal_main.cpp +++ b/stratosphere/fatal/source/fatal_main.cpp @@ -37,7 +37,7 @@ extern "C" { void __appExit(void); /* Exception handling. */ - alignas(16) u8 __nx_exception_stack[0x1000]; + alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize]; u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); void __libnx_exception_handler(ThreadExceptionDump *ctx); } diff --git a/stratosphere/fatal/source/fatal_task.hpp b/stratosphere/fatal/source/fatal_task.hpp index fa656f92a..82a51720a 100644 --- a/stratosphere/fatal/source/fatal_task.hpp +++ b/stratosphere/fatal/source/fatal_task.hpp @@ -36,9 +36,9 @@ namespace ams::fatal::srv { class ITaskWithStack : public ITask { public: static constexpr size_t StackSize = _StackSize; - static_assert(util::IsAligned(StackSize, 0x1000), "StackSize alignment"); + static_assert(util::IsAligned(StackSize, os::MemoryPageSize), "StackSize alignment"); protected: - alignas(0x1000) u8 stack_mem[StackSize] = {}; + alignas(os::MemoryPageSize) u8 stack_mem[StackSize] = {}; public: virtual u8 *GetStack() override final { return this->stack_mem; diff --git a/stratosphere/libstratosphere/Makefile b/stratosphere/libstratosphere/Makefile index 7a616fecb..08d494fe5 100644 --- a/stratosphere/libstratosphere/Makefile +++ b/stratosphere/libstratosphere/Makefile @@ -16,7 +16,7 @@ include $(DEVKITPRO)/libnx/switch_rules # INCLUDES is a list of directories containing header files #--------------------------------------------------------------------------------- TARGET := $(notdir $(CURDIR)) -SOURCES := source source/ams source/hos source/result source/os source/os/impl source/dd source/fs source/sf source/sf/cmif source/sf/hipc source/dmnt source/spl source/spl/smc source/updater source/patcher source/map source/rnd source/util source/sm source/cfg source/pm source/hid source/ldr source/kvdb source/settings source/boot2 +SOURCES ?= $(shell find source -type d) DATA := data INCLUDES := include diff --git a/stratosphere/libstratosphere/include/atmosphere/common_includes.hpp b/stratosphere/libstratosphere/include/atmosphere/common_includes.hpp index fec9205ca..82887c8aa 100644 --- a/stratosphere/libstratosphere/include/atmosphere/common_includes.hpp +++ b/stratosphere/libstratosphere/include/atmosphere/common_includes.hpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include diff --git a/stratosphere/libstratosphere/include/atmosphere/results/fs_results.hpp b/stratosphere/libstratosphere/include/atmosphere/results/fs_results.hpp index b3877c337..f7fe264a1 100644 --- a/stratosphere/libstratosphere/include/atmosphere/results/fs_results.hpp +++ b/stratosphere/libstratosphere/include/atmosphere/results/fs_results.hpp @@ -50,6 +50,8 @@ namespace ams::fs { R_DEFINE_ERROR_RANGE(AllocationFailure, 3200, 3499); R_DEFINE_ERROR_RESULT(AllocationFailureInDirectorySaveDataFileSystem, 3321); R_DEFINE_ERROR_RESULT(AllocationFailureInSubDirectoryFileSystem, 3355); + R_DEFINE_ERROR_RESULT(AllocationFailureInPathNormalizer, 3367); + R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemInterfaceAdapter, 3407); R_DEFINE_ERROR_RANGE(MmcAccessFailed, 3500, 3999); @@ -79,6 +81,12 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(DirectoryUnobtainable, 6006); R_DEFINE_ERROR_RESULT(NotNormalized, 6007); + R_DEFINE_ERROR_RANGE(InvalidPathForOperation, 6030, 6059); + R_DEFINE_ERROR_RESULT(DirectoryNotDeletable, 6031); + R_DEFINE_ERROR_RESULT(DirectoryNotRenamable, 6032); + R_DEFINE_ERROR_RESULT(IncompatiblePath, 6033); + R_DEFINE_ERROR_RESULT(RenameToOtherFileSystem, 6034); + R_DEFINE_ERROR_RESULT(InvalidOffset, 6061); R_DEFINE_ERROR_RESULT(InvalidSize, 6062); R_DEFINE_ERROR_RESULT(NullptrArgument, 6063); diff --git a/stratosphere/libstratosphere/include/stratosphere.hpp b/stratosphere/libstratosphere/include/stratosphere.hpp index 19ba7185c..a432ace74 100644 --- a/stratosphere/libstratosphere/include/stratosphere.hpp +++ b/stratosphere/libstratosphere/include/stratosphere.hpp @@ -51,3 +51,5 @@ /* Include FS last. */ #include "stratosphere/fs.hpp" +#include "stratosphere/fssrv.hpp" +#include "stratosphere/fssystem.hpp" diff --git a/stratosphere/libstratosphere/include/stratosphere/fs.hpp b/stratosphere/libstratosphere/include/stratosphere/fs.hpp index 08a38d897..2e417f1a4 100644 --- a/stratosphere/libstratosphere/include/stratosphere/fs.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/fs.hpp @@ -24,3 +24,5 @@ #include "fs/fs_remote_storage.hpp" #include "fs/fs_file_storage.hpp" #include "fs/fs_query_range.hpp" +#include "fs/fs_path_tool.hpp" +#include "fs/fs_path_utils.hpp" diff --git a/stratosphere/libstratosphere/include/stratosphere/fs/fs_common.hpp b/stratosphere/libstratosphere/include/stratosphere/fs/fs_common.hpp index cbeaeeded..7e0eaa651 100644 --- a/stratosphere/libstratosphere/include/stratosphere/fs/fs_common.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/fs/fs_common.hpp @@ -18,3 +18,10 @@ #include "../os.hpp" #include "../ncm.hpp" #include "../sf.hpp" + +namespace ams::fs { + + /* TODO: Better place for this? */ + constexpr inline size_t MountNameLengthMax = 15; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/fs/fs_directory.hpp b/stratosphere/libstratosphere/include/stratosphere/fs/fs_directory.hpp index 01c8eb2d5..723f0c9f9 100644 --- a/stratosphere/libstratosphere/include/stratosphere/fs/fs_directory.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/fs/fs_directory.hpp @@ -18,6 +18,8 @@ namespace ams::fs { + constexpr inline size_t EntryNameLengthMax = 0x300; + using DirectoryEntry = ::FsDirectoryEntry; } diff --git a/stratosphere/libstratosphere/include/stratosphere/fs/fs_path_tool.hpp b/stratosphere/libstratosphere/include/stratosphere/fs/fs_path_tool.hpp new file mode 100644 index 000000000..c74c68316 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fs/fs_path_tool.hpp @@ -0,0 +1,73 @@ +/* + * 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 "../fssrv/fssrv_sf_path.hpp" + +namespace ams::fs { + + namespace StringTraits { + + constexpr inline char DirectorySeparator = '/'; + constexpr inline char DriveSeparator = ':'; + constexpr inline char Dot = '.'; + constexpr inline char NullTerminator = '\x00'; + + } + + class PathTool { + public: + static constexpr fssrv::sf::Path RootPath = fssrv::sf::FspPath::Encode("/"); + public: + static constexpr inline bool IsSeparator(char c) { + return c == StringTraits::DirectorySeparator; + } + + static constexpr inline bool IsNullTerminator(char c) { + return c == StringTraits::NullTerminator; + } + + static constexpr inline bool IsDot(char c) { + return c == StringTraits::Dot; + } + + static constexpr inline bool IsWindowsDriveCharacter(char c) { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); + } + + static constexpr inline bool IsDriveSeparator(char c) { + return c == StringTraits::DriveSeparator; + } + + static constexpr inline bool IsWindowsAbsolutePath(const char *p) { + return IsWindowsDriveCharacter(p[0]) && IsDriveSeparator(p[1]); + } + + static constexpr inline bool IsCurrentDirectory(const char *p) { + return IsDot(p[0]) && (IsSeparator(p[1]) || IsNullTerminator(p[1])); + } + + static constexpr inline bool IsParentDirectory(const char *p) { + return IsDot(p[0]) && IsDot(p[1]) && (IsSeparator(p[2]) || IsNullTerminator(p[2])); + } + + static Result Normalize(char *out, size_t *out_len, const char *src, size_t max_out_size, bool unc_preserved = false); + static Result IsNormalized(bool *out, const char *path); + + static bool IsSubPath(const char *lhs, const char *rhs); + }; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/fs/fs_path_utils.hpp b/stratosphere/libstratosphere/include/stratosphere/fs/fs_path_utils.hpp new file mode 100644 index 000000000..f4598eb9d --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fs/fs_path_utils.hpp @@ -0,0 +1,48 @@ +/* + * 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 "../fssrv/fssrv_sf_path.hpp" + +namespace ams::fs { + + inline void Replace(char *dst, size_t dst_size, char old_char, char new_char) { + for (char *cur = dst; cur < dst + dst_size && *cur != '\x00'; cur++) { + if (*cur == old_char) { + *cur = new_char; + } + } + } + + inline Result FspPathPrintf(fssrv::sf::FspPath *dst, const char *format, ...) { + /* Format the path. */ + std::va_list va_list; + va_start(va_list, format); + const size_t len = std::vsnprintf(dst->str, sizeof(dst->str), format, va_list); + va_end(va_list); + + /* Validate length. */ + R_UNLESS(len < sizeof(dst->str), fs::ResultTooLongPath()); + + /* Fix slashes. */ + Replace(dst->str, sizeof(dst->str) - 1, '\\', '/'); + + return ResultSuccess(); + } + + Result VerifyPath(const char *path, size_t max_path_len, size_t max_name_len); + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/fs/fs_remote_filesystem.hpp b/stratosphere/libstratosphere/include/stratosphere/fs/fs_remote_filesystem.hpp index d797d44a5..8c39b4618 100644 --- a/stratosphere/libstratosphere/include/stratosphere/fs/fs_remote_filesystem.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/fs/fs_remote_filesystem.hpp @@ -57,6 +57,10 @@ namespace ams::fs { /* TODO: How should this be handled? */ return fs::ResultNotImplemented(); } + public: + virtual sf::cmif::DomainObjectId GetDomainObjectId() const override { + return sf::cmif::DomainObjectId{serviceGetObjectId(&this->base_file->s)}; + } }; class RemoteDirectory : public fsa::IDirectory { @@ -78,6 +82,10 @@ namespace ams::fs { virtual Result GetEntryCountImpl(s64 *out) override final { return fsDirGetEntryCount(this->base_dir.get(), out); } + public: + virtual sf::cmif::DomainObjectId GetDomainObjectId() const override { + return sf::cmif::DomainObjectId{serviceGetObjectId(&this->base_dir->s)}; + } }; class RemoteFileSystem : public fsa::IFileSystem { diff --git a/stratosphere/libstratosphere/include/stratosphere/fs/fsa/fs_idirectory.hpp b/stratosphere/libstratosphere/include/stratosphere/fs/fsa/fs_idirectory.hpp index ab3e06b48..335d7717f 100644 --- a/stratosphere/libstratosphere/include/stratosphere/fs/fsa/fs_idirectory.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/fs/fsa/fs_idirectory.hpp @@ -39,6 +39,9 @@ namespace ams::fs::fsa { R_UNLESS(out != nullptr, fs::ResultNullptrArgument()); return this->GetEntryCountImpl(out); } + public: + /* TODO: This is a hack to allow the mitm API to work. Find a better way? */ + virtual sf::cmif::DomainObjectId GetDomainObjectId() const = 0; protected: /* ...? */ private: diff --git a/stratosphere/libstratosphere/include/stratosphere/fs/fsa/fs_ifile.hpp b/stratosphere/libstratosphere/include/stratosphere/fs/fsa/fs_ifile.hpp index e81cae59f..9ecc920aa 100644 --- a/stratosphere/libstratosphere/include/stratosphere/fs/fsa/fs_ifile.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/fs/fsa/fs_ifile.hpp @@ -78,6 +78,9 @@ namespace ams::fs::fsa { Result OperateRange(OperationId op_id, s64 offset, s64 size) { return this->OperateRangeImpl(nullptr, 0, op_id, offset, size, nullptr, 0); } + public: + /* TODO: This is a hack to allow the mitm API to work. Find a better way? */ + virtual sf::cmif::DomainObjectId GetDomainObjectId() const = 0; protected: /* ...? */ private: diff --git a/stratosphere/libstratosphere/include/stratosphere/fs/fsa/fs_ifilesystem.hpp b/stratosphere/libstratosphere/include/stratosphere/fs/fsa/fs_ifilesystem.hpp index 7d5ac04e7..815cc159b 100644 --- a/stratosphere/libstratosphere/include/stratosphere/fs/fsa/fs_ifilesystem.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/fs/fsa/fs_ifilesystem.hpp @@ -151,9 +151,9 @@ namespace ams::fs::fsa { 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 GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) = 0; + virtual Result OpenFileImpl(std::unique_ptr *out_file, const char *path, fs::OpenMode mode) = 0; + virtual Result OpenDirectoryImpl(std::unique_ptr *out_dir, const char *path, fs::OpenDirectoryMode mode) = 0; virtual Result CommitImpl() = 0; virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) { @@ -166,11 +166,11 @@ namespace ams::fs::fsa { virtual Result CleanDirectoryRecursivelyImpl(const char *path) = 0; - virtual Result GetFileTimeStampRawImpl(FileTimeStampRaw *out, const char *path) { + virtual Result GetFileTimeStampRawImpl(fs::FileTimeStampRaw *out, const char *path) { return fs::ResultNotImplemented(); } - virtual Result QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, QueryId query, const char *path) { + virtual Result QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, fs::fsa::QueryId query, const char *path) { return fs::ResultNotImplemented(); } diff --git a/stratosphere/libstratosphere/include/stratosphere/fssrv.hpp b/stratosphere/libstratosphere/include/stratosphere/fssrv.hpp new file mode 100644 index 000000000..3f72bb4a9 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fssrv.hpp @@ -0,0 +1,19 @@ +/* + * 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 "fssrv/fssrv_sf_path.hpp" +#include "fssrv/fssrv_path_normalizer.hpp" diff --git a/stratosphere/libstratosphere/include/stratosphere/fssrv/fssrv_interface_adapters.hpp b/stratosphere/libstratosphere/include/stratosphere/fssrv/fssrv_interface_adapters.hpp new file mode 100644 index 000000000..52fbb7180 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fssrv/fssrv_interface_adapters.hpp @@ -0,0 +1,18 @@ +/* + * 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 "interface_adapters/fssrv_storage_interface_adapter.hpp" +#include "interface_adapters/fssrv_filesystem_interface_adapter.hpp" diff --git a/stratosphere/libstratosphere/include/stratosphere/fssrv/fssrv_path_normalizer.hpp b/stratosphere/libstratosphere/include/stratosphere/fssrv/fssrv_path_normalizer.hpp new file mode 100644 index 000000000..3411d9611 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fssrv/fssrv_path_normalizer.hpp @@ -0,0 +1,66 @@ +/* + * 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" + +namespace ams::fssrv { + + /* This is in fssrv::detail in official code. */ + /* TODO: Consider moving to ::impl? */ + + class PathNormalizer { + public: + enum Option : u32 { + Option_None = BIT(0), + Option_PreserveUnc = BIT(1), + Option_PreserveTailSeparator = BIT(2), + Option_HasMountName = BIT(3), + Option_AcceptEmpty = BIT(4), + }; + private: + using Buffer = std::unique_ptr; + private: + Buffer buffer; + const char *path; + Result result; + private: + static Result Normalize(const char **out_path, Buffer *out_buf, const char *path, bool preserve_unc, bool preserve_tail_sep, bool has_mount_name); + public: + explicit PathNormalizer(const char *p) : buffer(), path(nullptr), result(ResultSuccess()) { + this->result = Normalize(&this->path, &this->buffer, p, false, false, false); + } + + PathNormalizer(const char *p, u32 option) : buffer(), path(nullptr), result(ResultSuccess()) { + if ((option & Option_AcceptEmpty) && p[0] == '\x00') { + this->path = path; + } else { + const bool preserve_unc = (option & Option_PreserveUnc); + const bool preserve_tail_sep = (option & Option_PreserveTailSeparator); + const bool has_mount_name = (option & Option_HasMountName); + this->result = Normalize(&this->path, &this->buffer, p, preserve_unc, preserve_tail_sep, has_mount_name); + } + } + + inline Result GetResult() const { + return this->result; + } + + inline const char * GetPath() const { + return this->path; + } + }; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/fssrv/fssrv_sf_path.hpp b/stratosphere/libstratosphere/include/stratosphere/fssrv/fssrv_sf_path.hpp new file mode 100644 index 000000000..a15c3fa55 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fssrv/fssrv_sf_path.hpp @@ -0,0 +1,50 @@ +/* + * 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/fs_directory.hpp" +#include "../sf/sf_buffer_tags.hpp" + +namespace ams::fssrv::sf { + + struct Path : ams::sf::LargeData { + char str[fs::EntryNameLengthMax + 1]; + + static constexpr Path Encode(const char *p) { + Path path = {}; + for (size_t i = 0; i < sizeof(path) - 1; i++) { + path.str[i] = p[i]; + if (p[i] == '\x00') { + break; + } + } + return path; + } + + static constexpr size_t GetPathLength(const Path &path) { + size_t len = 0; + for (size_t i = 0; i < sizeof(path) - 1 && path.str[i] != '\x00'; i++) { + len++; + } + return len; + } + }; + + static_assert(std::is_pod::value && sizeof(Path) == FS_MAX_PATH); + + using FspPath = Path; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/fssrv/interface_adapters/fssrv_filesystem_interface_adapter.hpp b/stratosphere/libstratosphere/include/stratosphere/fssrv/interface_adapters/fssrv_filesystem_interface_adapter.hpp new file mode 100644 index 000000000..970f24bb2 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fssrv/interface_adapters/fssrv_filesystem_interface_adapter.hpp @@ -0,0 +1,192 @@ +/* + * 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/fs_file.hpp" +#include "../../fs/fs_directory.hpp" +#include "../../fs/fs_filesystem.hpp" +#include "../../fs/fs_query_range.hpp" +#include "../../fssrv/fssrv_sf_path.hpp" +#include "../../fssystem/fssystem_utility.hpp" + +namespace ams::fs::fsa { + + class IFile; + class IDirectory; + class IFileSystem; + +} + +namespace ams::fssrv::impl { + + class FileSystemInterfaceAdapter; + + class FileInterfaceAdapter final : public ams::sf::IServiceObject { + NON_COPYABLE(FileInterfaceAdapter); + public: + enum class CommandId { + Read = 0, + Write = 1, + Flush = 2, + SetSize = 3, + GetSize = 4, + OperateRange = 5, + }; + private: + std::shared_ptr parent_filesystem; + std::unique_ptr base_file; + std::unique_lock open_count_semaphore; + public: + FileInterfaceAdapter(std::unique_ptr &&file, std::shared_ptr &&parent, std::unique_lock &&sema); + ~FileInterfaceAdapter(); + private: + void InvalidateCache(); + public: + /* Command API. */ + Result Read(ams::sf::Out out, s64 offset, const ams::sf::OutNonSecureBuffer &buffer, s64 size, fs::ReadOption option); + Result Write(s64 offset, const ams::sf::InNonSecureBuffer &buffer, s64 size, fs::WriteOption option); + Result Flush(); + Result SetSize(s64 size); + Result GetSize(ams::sf::Out out); + Result OperateRange(ams::sf::Out out, s32 op_id, s64 offset, s64 size); + 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), + }; + }; + + class DirectoryInterfaceAdapter final : public ams::sf::IServiceObject { + NON_COPYABLE(DirectoryInterfaceAdapter); + public: + enum class CommandId { + Read = 0, + GetEntryCount = 1, + }; + private: + std::shared_ptr parent_filesystem; + std::unique_ptr base_dir; + std::unique_lock open_count_semaphore; + public: + DirectoryInterfaceAdapter(std::unique_ptr &&dir, std::shared_ptr &&parent, std::unique_lock &&sema); + ~DirectoryInterfaceAdapter(); + public: + /* Command API */ + Result Read(ams::sf::Out out, const ams::sf::OutBuffer &out_entries); + Result GetEntryCount(ams::sf::Out out); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(Read), + MAKE_SERVICE_COMMAND_META(GetEntryCount), + }; + }; + + class FileSystemInterfaceAdapter final : std::enable_shared_from_this, public ams::sf::IServiceObject { + NON_COPYABLE(FileSystemInterfaceAdapter); + public: + enum class CommandId { + /* 1.0.0+ */ + CreateFile = 0, + DeleteFile = 1, + CreateDirectory = 2, + DeleteDirectory = 3, + DeleteDirectoryRecursively = 4, + RenameFile = 5, + RenameDirectory = 6, + GetEntryType = 7, + OpenFile = 8, + OpenDirectory = 9, + Commit = 10, + GetFreeSpaceSize = 11, + GetTotalSpaceSize = 12, + + /* 3.0.0+ */ + CleanDirectoryRecursively = 13, + GetFileTimeStampRaw = 14, + + /* 4.0.0+ */ + QueryEntry = 15, + }; + private: + std::shared_ptr base_fs; + std::unique_lock mount_count_semaphore; + os::ReadWriteLock invalidation_lock; + bool open_count_limited; + bool deep_retry_enabled = false; + public: + FileSystemInterfaceAdapter(std::shared_ptr &&fs, bool open_limited); + /* TODO: Other constructors. */ + + ~FileSystemInterfaceAdapter(); + public: + bool IsDeepRetryEnabled() const; + bool IsAccessFailureDetectionObserved() const; + std::optional> AcquireCacheInvalidationReadLock(); + os::ReadWriteLock &GetReadWriteLockForCacheInvalidation(); + public: + /* Command API. */ + Result CreateFile(const fssrv::sf::Path &path, s64 size, s32 option); + Result DeleteFile(const fssrv::sf::Path &path); + Result CreateDirectory(const fssrv::sf::Path &path); + Result DeleteDirectory(const fssrv::sf::Path &path); + Result DeleteDirectoryRecursively(const fssrv::sf::Path &path); + Result RenameFile(const fssrv::sf::Path &old_path, const fssrv::sf::Path &new_path); + Result RenameDirectory(const fssrv::sf::Path &old_path, const fssrv::sf::Path &new_path); + Result GetEntryType(ams::sf::Out out, const fssrv::sf::Path &path); + Result OpenFile(ams::sf::Out> out, const fssrv::sf::Path &path, u32 mode); + Result OpenDirectory(ams::sf::Out> out, const fssrv::sf::Path &path, u32 mode); + Result Commit(); + Result GetFreeSpaceSize(ams::sf::Out out, const fssrv::sf::Path &path); + Result GetTotalSpaceSize(ams::sf::Out out, const fssrv::sf::Path &path); + + Result CleanDirectoryRecursively(const fssrv::sf::Path &path); + Result GetFileTimeStampRaw(ams::sf::Out out, const fssrv::sf::Path &path); + + Result QueryEntry(const ams::sf::OutBuffer &out_buf, const ams::sf::InBuffer &in_buf, s32 query_id, const fssrv::sf::Path &path); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + /* 1.0.0- */ + MAKE_SERVICE_COMMAND_META(CreateFile), + MAKE_SERVICE_COMMAND_META(DeleteFile), + MAKE_SERVICE_COMMAND_META(CreateDirectory), + MAKE_SERVICE_COMMAND_META(DeleteDirectory), + MAKE_SERVICE_COMMAND_META(DeleteDirectoryRecursively), + MAKE_SERVICE_COMMAND_META(RenameFile), + MAKE_SERVICE_COMMAND_META(RenameDirectory), + MAKE_SERVICE_COMMAND_META(GetEntryType), + MAKE_SERVICE_COMMAND_META(OpenFile), + MAKE_SERVICE_COMMAND_META(OpenDirectory), + MAKE_SERVICE_COMMAND_META(Commit), + MAKE_SERVICE_COMMAND_META(GetFreeSpaceSize), + MAKE_SERVICE_COMMAND_META(GetTotalSpaceSize), + + /* 3.0.0- */ + MAKE_SERVICE_COMMAND_META(CleanDirectoryRecursively, hos::Version_300), + MAKE_SERVICE_COMMAND_META(GetFileTimeStampRaw, hos::Version_300), + + /* 4.0.0- */ + MAKE_SERVICE_COMMAND_META(QueryEntry, hos::Version_400), + }; + }; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/fssrv/interface_adapters/fssrv_storage_interface_adapter.hpp b/stratosphere/libstratosphere/include/stratosphere/fssrv/interface_adapters/fssrv_storage_interface_adapter.hpp new file mode 100644 index 000000000..dc404085e --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fssrv/interface_adapters/fssrv_storage_interface_adapter.hpp @@ -0,0 +1,78 @@ +/* + * 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/fs_query_range.hpp" +#include "../../fssystem/fssystem_utility.hpp" + +namespace ams::fs { + + class IStorage; + +} + +namespace ams::fssrv::impl { + + class StorageInterfaceAdapter final : public ams::sf::IServiceObject { + NON_COPYABLE(StorageInterfaceAdapter); + public: + enum class CommandId { + Read = 0, + Write = 1, + Flush = 2, + SetSize = 3, + GetSize = 4, + OperateRange = 5, + }; + private: + /* TODO: Nintendo uses fssystem::AsynchronousAccessStorage here. */ + std::shared_ptr base_storage; + std::unique_lock open_count_semaphore; + os::ReadWriteLock invalidation_lock; + /* TODO: DataStorageContext. */ + bool deep_retry_enabled = false; + public: + StorageInterfaceAdapter(fs::IStorage *storage); + StorageInterfaceAdapter(std::unique_ptr storage); + explicit StorageInterfaceAdapter(std::shared_ptr &&storage); + /* TODO: Other constructors. */ + + ~StorageInterfaceAdapter(); + private: + std::optional> AcquireCacheInvalidationReadLock(); + private: + /* Command API. */ + Result Read(s64 offset, const ams::sf::OutNonSecureBuffer &buffer, s64 size); + Result Write(s64 offset, const ams::sf::InNonSecureBuffer &buffer, s64 size); + Result Flush(); + Result SetSize(s64 size); + Result GetSize(ams::sf::Out out); + Result OperateRange(ams::sf::Out out, s32 op_id, s64 offset, s64 size); + 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/libstratosphere/include/stratosphere/fssystem.hpp b/stratosphere/libstratosphere/include/stratosphere/fssystem.hpp new file mode 100644 index 000000000..90d84444a --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fssystem.hpp @@ -0,0 +1,21 @@ +/* + * 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 "fssystem/fssystem_utility.hpp" +#include "fssystem/fssystem_path_tool.hpp" +#include "fssystem/fssystem_subdirectory_filesystem.hpp" +#include "fssystem/fssystem_directory_redirection_filesystem.hpp" diff --git a/stratosphere/libstratosphere/include/stratosphere/fssystem/fssystem_directory_redirection_filesystem.hpp b/stratosphere/libstratosphere/include/stratosphere/fssystem/fssystem_directory_redirection_filesystem.hpp new file mode 100644 index 000000000..3d56f3e6c --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fssystem/fssystem_directory_redirection_filesystem.hpp @@ -0,0 +1,43 @@ +/* + * 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 "fssystem_path_resolution_filesystem.hpp" + +namespace ams::fssystem { + + class DirectoryRedirectionFileSystem : public IPathResolutionFileSystem { + NON_COPYABLE(DirectoryRedirectionFileSystem); + private: + using PathResolutionFileSystem = IPathResolutionFileSystem; + private: + char *before_dir; + size_t before_dir_len; + char *after_dir; + size_t after_dir_len; + bool unc_preserved; + public: + DirectoryRedirectionFileSystem(std::shared_ptr fs, const char *before, const char *after); + DirectoryRedirectionFileSystem(std::shared_ptr fs, const char *before, const char *after, bool unc); + + virtual ~DirectoryRedirectionFileSystem(); + private: + Result GetNormalizedDirectoryPath(char **out, size_t *out_size, const char *dir); + Result Initialize(const char *before, const char *after); + public: + Result ResolveFullPath(char *out, size_t out_size, const char *relative_path); + }; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/fssystem/fssystem_path_resolution_filesystem.hpp b/stratosphere/libstratosphere/include/stratosphere/fssystem/fssystem_path_resolution_filesystem.hpp new file mode 100644 index 000000000..d0d99e2b4 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fssystem/fssystem_path_resolution_filesystem.hpp @@ -0,0 +1,173 @@ +/* + * 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" + +namespace ams::fssystem { + + template + class IPathResolutionFileSystem : public fs::fsa::IFileSystem { + NON_COPYABLE(IPathResolutionFileSystem); + private: + std::shared_ptr shared_fs; + fs::fsa::IFileSystem *base_fs; + bool unc_preserved; + public: + IPathResolutionFileSystem(std::shared_ptr fs) : shared_fs(std::move(fs)), unc_preserved(false) { + this->base_fs = this->shared_fs.get(); + } + + IPathResolutionFileSystem(std::shared_ptr fs, bool unc) : shared_fs(std::move(fs)), unc_preserved(unc) { + this->base_fs = this->shared_fs.get(); + } + + virtual ~IPathResolutionFileSystem() { /* ... */ } + protected: + constexpr inline bool IsUncPreserved() const { + return this->unc_preserved; + } + public: + virtual Result CreateFileImpl(const char *path, s64 size, int option) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->CreateFile(full_path, size, option); + } + + virtual Result DeleteFileImpl(const char *path) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->DeleteFile(full_path); + } + + virtual Result CreateDirectoryImpl(const char *path) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->CreateDirectory(full_path); + } + + virtual Result DeleteDirectoryImpl(const char *path) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->DeleteDirectory(full_path); + } + + virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->DeleteDirectoryRecursively(full_path); + } + + virtual Result RenameFileImpl(const char *old_path, const char *new_path) override { + char old_full_path[fs::EntryNameLengthMax + 1]; + char new_full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(old_full_path, sizeof(old_full_path), old_path)); + R_TRY(static_cast(this)->ResolveFullPath(new_full_path, sizeof(new_full_path), new_path)); + + return this->base_fs->RenameFile(old_path, new_path); + } + + virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override { + char old_full_path[fs::EntryNameLengthMax + 1]; + char new_full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(old_full_path, sizeof(old_full_path), old_path)); + R_TRY(static_cast(this)->ResolveFullPath(new_full_path, sizeof(new_full_path), new_path)); + + return this->base_fs->RenameDirectory(old_path, new_path); + } + + virtual Result GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->GetEntryType(out, full_path); + } + + virtual Result OpenFileImpl(std::unique_ptr *out_file, const char *path, fs::OpenMode mode) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->OpenFile(out_file, full_path, mode); + } + + virtual Result OpenDirectoryImpl(std::unique_ptr *out_dir, const char *path, fs::OpenDirectoryMode mode) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->OpenDirectory(out_dir, full_path, mode); + } + + virtual Result CommitImpl() override { + return this->base_fs->Rollback(); + } + + virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->GetFreeSpaceSize(out, full_path); + } + + virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->GetTotalSpaceSize(out, full_path); + } + + virtual Result CleanDirectoryRecursivelyImpl(const char *path) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->CleanDirectoryRecursively(full_path); + } + + virtual Result GetFileTimeStampRawImpl(fs::FileTimeStampRaw *out, const char *path) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->GetFileTimeStampRaw(out, full_path); + } + + virtual Result QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, fs::fsa::QueryId query, const char *path) override { + char full_path[fs::EntryNameLengthMax + 1]; + R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path)); + + return this->base_fs->QueryEntry(dst, dst_size, src, src_size, query, full_path); + } + + /* These aren't accessible as commands. */ + virtual Result CommitProvisionallyImpl(s64 counter) override { + return this->base_fs->CommitProvisionally(counter); + } + + virtual Result RollbackImpl() override { + return this->base_fs->Rollback(); + } + + virtual Result FlushImpl() override { + return this->base_fs->Flush(); + } + }; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/fssystem/fssystem_path_tool.hpp b/stratosphere/libstratosphere/include/stratosphere/fssystem/fssystem_path_tool.hpp new file mode 100644 index 000000000..2e50cce9b --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fssystem/fssystem_path_tool.hpp @@ -0,0 +1,26 @@ +/* + * 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/fs_path_tool.hpp" + +namespace ams::fssystem { + + namespace StringTraits = ::ams::fs::StringTraits; + + using PathTool = ::ams::fs::PathTool; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/fssystem/fssystem_subdirectory_filesystem.hpp b/stratosphere/libstratosphere/include/stratosphere/fssystem/fssystem_subdirectory_filesystem.hpp new file mode 100644 index 000000000..0f827e060 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fssystem/fssystem_subdirectory_filesystem.hpp @@ -0,0 +1,40 @@ +/* + * 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 "fssystem_path_resolution_filesystem.hpp" + +namespace ams::fssystem { + + class SubDirectoryFileSystem : public IPathResolutionFileSystem { + NON_COPYABLE(SubDirectoryFileSystem); + private: + using PathResolutionFileSystem = IPathResolutionFileSystem; + private: + char *base_path; + size_t base_path_len; + bool unc_preserved; + public: + SubDirectoryFileSystem(std::shared_ptr fs, const char *bp); + SubDirectoryFileSystem(std::shared_ptr fs, const char *bp, bool unc); + + virtual ~SubDirectoryFileSystem(); + private: + Result Initialize(const char *bp); + public: + Result ResolveFullPath(char *out, size_t out_size, const char *relative_path); + }; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/fssystem/fssystem_utility.hpp b/stratosphere/libstratosphere/include/stratosphere/fssystem/fssystem_utility.hpp new file mode 100644 index 000000000..4376c6011 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fssystem/fssystem_utility.hpp @@ -0,0 +1,34 @@ +/* + * 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" + +namespace ams::fssystem { + + class SemaphoreAdapter : public os::Semaphore { + public: + SemaphoreAdapter(int c, int mc) : os::Semaphore(c, mc) { /* ... */ } + + bool try_lock() { + return this->TryAcquire(); + } + + void unlock() { + this->Release(); + } + }; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/os.hpp b/stratosphere/libstratosphere/include/stratosphere/os.hpp index d899759b6..21e54e43d 100644 --- a/stratosphere/libstratosphere/include/stratosphere/os.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/os.hpp @@ -17,6 +17,7 @@ #pragma once #include "os/os_common_types.hpp" +#include "os/os_memory_common.hpp" #include "os/os_managed_handle.hpp" #include "os/os_process_handle.hpp" #include "os/os_mutex.hpp" diff --git a/stratosphere/libstratosphere/include/stratosphere/os/os_memory_common.hpp b/stratosphere/libstratosphere/include/stratosphere/os/os_memory_common.hpp new file mode 100644 index 000000000..d267cbb65 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/os/os_memory_common.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 + +namespace ams::os { + + constexpr inline size_t MemoryPageSize = 0x1000; + + constexpr inline size_t MemoryBlockUnitSize = 0x200000; + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/os/os_semaphore.hpp b/stratosphere/libstratosphere/include/stratosphere/os/os_semaphore.hpp index 7cda2c86e..0941ce5cb 100644 --- a/stratosphere/libstratosphere/include/stratosphere/os/os_semaphore.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/os/os_semaphore.hpp @@ -15,34 +15,41 @@ */ #pragma once -#include "os_common_types.hpp" +#include "os_mutex.hpp" +#include "os_condvar.hpp" namespace ams::os { + namespace impl { + + class WaitableObjectList; + class WaitableHolderOfSemaphore; + + } + class Semaphore { + friend class impl::WaitableHolderOfSemaphore; NON_COPYABLE(Semaphore); NON_MOVEABLE(Semaphore); private: - ::Semaphore s; + util::TypedStorage waitlist; + os::Mutex mutex; + os::ConditionVariable condvar; + int count; + int max_count; public: - Semaphore() { - semaphoreInit(&s, 0); - } + explicit Semaphore(int c, int mc); + ~Semaphore(); - Semaphore(u64 c) { - semaphoreInit(&s, c); - } + void Acquire(); + bool TryAcquire(); + bool TimedAcquire(u64 timeout); - void Signal() { - semaphoreSignal(&s); - } + void Release(); + void Release(int count); - void Wait() { - semaphoreWait(&s); - } - - bool TryWait() { - return semaphoreTryWait(&s); + constexpr inline int GetCurrentCount() const { + return this->count; } }; diff --git a/stratosphere/libstratosphere/include/stratosphere/os/os_thread.hpp b/stratosphere/libstratosphere/include/stratosphere/os/os_thread.hpp index d1b35d7c8..a3e809e0b 100644 --- a/stratosphere/libstratosphere/include/stratosphere/os/os_thread.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/os/os_thread.hpp @@ -16,6 +16,7 @@ #pragma once #include "os_common_types.hpp" +#include "os_memory_common.hpp" namespace ams::os { @@ -62,9 +63,9 @@ namespace ams::os { class StaticThread { NON_COPYABLE(StaticThread); NON_MOVEABLE(StaticThread); - static_assert(util::IsAligned(StackSize, 0x1000), "StaticThread must have aligned resource size"); + static_assert(util::IsAligned(StackSize, os::MemoryPageSize), "StaticThread must have aligned resource size"); private: - alignas(0x1000) u8 stack_mem[StackSize]; + alignas(os::MemoryPageSize) u8 stack_mem[StackSize]; ::Thread thr; public: constexpr StaticThread() : stack_mem{}, thr{} { /* ... */ } diff --git a/stratosphere/libstratosphere/include/stratosphere/os/os_waitable_holder.hpp b/stratosphere/libstratosphere/include/stratosphere/os/os_waitable_holder.hpp index ea3216b46..03c9dcf0e 100644 --- a/stratosphere/libstratosphere/include/stratosphere/os/os_waitable_holder.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/os/os_waitable_holder.hpp @@ -25,6 +25,7 @@ namespace ams::os { class InterruptEvent; class Thread; class MessageQueue; + class Semaphore; namespace impl { @@ -47,6 +48,7 @@ namespace ams::os { WaitableHolder(SystemEvent *event); WaitableHolder(InterruptEvent *event); WaitableHolder(Thread *thread); + WaitableHolder(Semaphore *semaphore); WaitableHolder(MessageQueue *message_queue, MessageQueueWaitKind wait_kind); ~WaitableHolder(); diff --git a/stratosphere/libstratosphere/source/ams/ams_emummc_api.cpp b/stratosphere/libstratosphere/source/ams/ams_emummc_api.cpp index 1eb304f9e..4cb1f126d 100644 --- a/stratosphere/libstratosphere/source/ams/ams_emummc_api.cpp +++ b/stratosphere/libstratosphere/source/ams/ams_emummc_api.cpp @@ -74,7 +74,7 @@ namespace ams::emummc { /* Retrieve and cache values. */ { - typename std::aligned_storage<2 * (MaxDirLen + 1), 0x1000>::type path_storage; + typename std::aligned_storage<2 * (MaxDirLen + 1), os::MemoryPageSize>::type path_storage; struct { char file_path[MaxDirLen + 1]; diff --git a/stratosphere/libstratosphere/source/dd/dd_io_mappings.cpp b/stratosphere/libstratosphere/source/dd/dd_io_mappings.cpp index c423fd4aa..4376374a0 100644 --- a/stratosphere/libstratosphere/source/dd/dd_io_mappings.cpp +++ b/stratosphere/libstratosphere/source/dd/dd_io_mappings.cpp @@ -19,7 +19,7 @@ namespace ams::dd { uintptr_t QueryIoMapping(uintptr_t phys_addr, size_t size) { u64 virtual_addr; - const u64 aligned_addr = util::AlignDown(phys_addr, 0x1000); + const u64 aligned_addr = util::AlignDown(phys_addr, os::MemoryPageSize); const size_t offset = phys_addr - aligned_addr; const u64 aligned_size = size + offset; R_TRY_CATCH(svcQueryIoMapping(&virtual_addr, aligned_addr, aligned_size)) { diff --git a/stratosphere/libstratosphere/source/fs/fs_path_tool.cpp b/stratosphere/libstratosphere/source/fs/fs_path_tool.cpp new file mode 100644 index 000000000..74eac03a0 --- /dev/null +++ b/stratosphere/libstratosphere/source/fs/fs_path_tool.cpp @@ -0,0 +1,248 @@ +/* + * 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 + +namespace ams::fs { + + + Result PathTool::Normalize(char *out, size_t *out_len, const char *src, size_t max_out_size, bool unc_preserved) { + /* Paths must start with / */ + R_UNLESS(IsSeparator(src[0]), fs::ResultInvalidPathFormat()); + + bool skip_next_sep = false; + size_t i = 0; + size_t len = 0; + + while (!IsNullTerminator(src[i])) { + if (IsSeparator(src[i])) { + /* Swallow separators. */ + while (IsSeparator(src[++i])) { } + if (IsNullTerminator(src[i])) { + break; + } + + /* Handle skip if needed */ + if (!skip_next_sep) { + if (len + 1 == max_out_size) { + out[len] = StringTraits::NullTerminator; + if (out_len != nullptr) { + *out_len = len; + } + return fs::ResultTooLongPath(); + } + + out[len++] = StringTraits::DirectorySeparator; + + if (unc_preserved && len == 1) { + while (len < i) { + if (len + 1 == max_out_size) { + out[len] = StringTraits::NullTerminator; + if (out_len != nullptr) { + *out_len = len; + } + return fs::ResultTooLongPath(); + } + out[len++] = StringTraits::DirectorySeparator; + } + } + } + skip_next_sep = false; + } + + /* See length of current dir. */ + size_t dir_len = 0; + while (!IsSeparator(src[i+dir_len]) && !IsNullTerminator(src[i+dir_len])) { + dir_len++; + } + + if (IsCurrentDirectory(&src[i])) { + skip_next_sep = true; + } else if (IsParentDirectory(&src[i])) { + AMS_ASSERT(IsSeparator(out[0])); + AMS_ASSERT(IsSeparator(out[len - 1])); + R_UNLESS(len != 1, fs::ResultDirectoryUnobtainable()); + + /* Walk up a directory. */ + len -= 2; + while (!IsSeparator(out[len])) { + len--; + } + } else { + /* Copy, possibly truncating. */ + if (len + dir_len + 1 <= max_out_size) { + for (size_t j = 0; j < dir_len; j++) { + out[len++] = src[i+j]; + } + } else { + const size_t copy_len = max_out_size - 1 - len; + for (size_t j = 0; j < copy_len; j++) { + out[len++] = src[i+j]; + } + out[len] = StringTraits::NullTerminator; + if (out_len != nullptr) { + *out_len = len; + } + return fs::ResultTooLongPath(); + } + } + + i += dir_len; + } + + if (skip_next_sep) { + len--; + } + + if (len == 0 && max_out_size) { + out[len++] = StringTraits::DirectorySeparator; + } + + R_UNLESS(max_out_size >= len - 1, fs::ResultTooLongPath()); + + /* Null terminate. */ + out[len] = StringTraits::NullTerminator; + if (out_len != nullptr) { + *out_len = len; + } + + /* Assert normalized. */ + bool normalized = false; + AMS_ASSERT(unc_preserved || (R_SUCCEEDED(IsNormalized(&normalized, out)) && normalized)); + + return ResultSuccess(); + } + + Result PathTool::IsNormalized(bool *out, const char *path) { + /* Nintendo uses a state machine here. */ + enum class PathState { + Start, + Normal, + FirstSeparator, + Separator, + CurrentDir, + ParentDir, + WindowsDriveLetter, + }; + + PathState state = PathState::Start; + + for (const char *cur = path; *cur != StringTraits::NullTerminator; cur++) { + const char c = *cur; + switch (state) { + case PathState::Start: + if (IsWindowsDriveCharacter(c)) { + state = PathState::WindowsDriveLetter; + } else if (IsSeparator(c)) { + state = PathState::FirstSeparator; + } else { + return fs::ResultInvalidPathFormat(); + } + break; + case PathState::Normal: + if (IsSeparator(c)) { + state = PathState::Separator; + } + break; + case PathState::FirstSeparator: + case PathState::Separator: + if (IsSeparator(c)) { + *out = false; + return ResultSuccess(); + } else if (IsDot(c)) { + state = PathState::CurrentDir; + } else { + state = PathState::Normal; + } + break; + case PathState::CurrentDir: + if (IsSeparator(c)) { + *out = false; + return ResultSuccess(); + } else if (IsDot(c)) { + state = PathState::ParentDir; + } else { + state = PathState::Normal; + } + break; + case PathState::ParentDir: + if (IsSeparator(c)) { + *out = false; + return ResultSuccess(); + } else { + state = PathState::Normal; + } + break; + case PathState::WindowsDriveLetter: + if (IsDriveSeparator(c)) { + *out = true; + return ResultSuccess(); + } else { + return fs::ResultInvalidPathFormat(); + } + break; + } + } + + switch (state) { + case PathState::Start: + case PathState::WindowsDriveLetter: + return fs::ResultInvalidPathFormat(); + case PathState::FirstSeparator: + case PathState::Normal: + *out = true; + break; + case PathState::CurrentDir: + case PathState::ParentDir: + case PathState::Separator: + *out = false; + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + return ResultSuccess(); + } + + bool PathTool::IsSubPath(const char *lhs, const char *rhs) { + AMS_ASSERT(lhs != nullptr); + AMS_ASSERT(rhs != nullptr); + + /* Special case certain paths. */ + if (IsSeparator(lhs[0]) && !IsSeparator(lhs[1]) && IsSeparator(rhs[0]) && IsSeparator(rhs[1])) { + return false; + } + if (IsSeparator(rhs[0]) && !IsSeparator(rhs[1]) && IsSeparator(lhs[0]) && IsSeparator(lhs[1])) { + return false; + } + if (IsSeparator(lhs[0]) && IsNullTerminator(lhs[1]) && IsSeparator(rhs[0]) && !IsNullTerminator(rhs[1])) { + return true; + } + if (IsSeparator(rhs[0]) && IsNullTerminator(rhs[1]) && IsSeparator(lhs[0]) && !IsNullTerminator(lhs[1])) { + return true; + } + + /* Check subpath. */ + for (size_t i = 0; /* No terminating condition */; i++) { + if (IsNullTerminator(lhs[i])) { + return IsSeparator(rhs[i]); + } else if (IsNullTerminator(rhs[i])) { + return IsSeparator(lhs[i]); + } else if (lhs[i] != rhs[i]) { + return false; + } + } + } + +} diff --git a/stratosphere/libstratosphere/source/fs/fs_path_utils.cpp b/stratosphere/libstratosphere/source/fs/fs_path_utils.cpp new file mode 100644 index 000000000..ba2495240 --- /dev/null +++ b/stratosphere/libstratosphere/source/fs/fs_path_utils.cpp @@ -0,0 +1,47 @@ +/* + * 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 + +namespace ams::fs { + + Result VerifyPath(const char *path, size_t max_path_len, size_t max_name_len) { + const char *cur = path; + size_t name_len = 0; + + for (size_t path_len = 0; path_len <= max_path_len && name_len <= max_name_len; path_len++) { + const char c = *(cur++); + + /* If terminated, we're done. */ + R_UNLESS(c != StringTraits::NullTerminator, ResultSuccess()); + + /* TODO: Nintendo converts the path from utf-8 to utf-32, one character at a time. */ + /* We should do this. */ + + /* Banned characters: :*?<>| */ + R_UNLESS((c != ':' && c != '*' && c != '?' && c != '<' && c != '>' && c != '|'), fs::ResultInvalidCharacter()); + + name_len++; + /* Check for separator. */ + if (c == '\\' || c == '/') { + name_len = 0; + } + + } + + return fs::ResultTooLongPath(); + } + +} diff --git a/stratosphere/libstratosphere/source/fssrv/fssrv_filesystem_interface_adapter.cpp b/stratosphere/libstratosphere/source/fssrv/fssrv_filesystem_interface_adapter.cpp new file mode 100644 index 000000000..1b6677025 --- /dev/null +++ b/stratosphere/libstratosphere/source/fssrv/fssrv_filesystem_interface_adapter.cpp @@ -0,0 +1,343 @@ +/* + * 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 +#include + +namespace ams::fssrv::impl { + + FileInterfaceAdapter::FileInterfaceAdapter(std::unique_ptr &&file, std::shared_ptr &&parent, std::unique_lock &&sema) + : parent_filesystem(std::move(parent)), base_file(std::move(file)), open_count_semaphore(std::move(sema)) + { + /* ... */ + } + + FileInterfaceAdapter::~FileInterfaceAdapter() { + /* ... */ + } + + void FileInterfaceAdapter::InvalidateCache() { + AMS_ASSERT(this->parent_filesystem->IsDeepRetryEnabled()); + std::scoped_lock scoped_write_lock(this->parent_filesystem->GetReadWriteLockForCacheInvalidation()); + this->base_file->OperateRange(nullptr, 0, fs::OperationId::InvalidateCache, 0, std::numeric_limits::max(), nullptr, 0); + } + + Result FileInterfaceAdapter::Read(ams::sf::Out out, s64 offset, const ams::sf::OutNonSecureBuffer &buffer, s64 size, fs::ReadOption option) { + /* TODO: N retries on ResultDataCorrupted, we may want to eventually. */ + /* TODO: Deep retry */ + R_UNLESS(offset >= 0, fs::ResultInvalidOffset()); + R_UNLESS(size >= 0, fs::ResultInvalidSize()); + + size_t read_size = 0; + R_TRY(this->base_file->Read(&read_size, offset, buffer.GetPointer(), static_cast(size), option)); + + out.SetValue(read_size); + return ResultSuccess(); + } + + Result FileInterfaceAdapter::Write(s64 offset, const ams::sf::InNonSecureBuffer &buffer, s64 size, fs::WriteOption option) { + /* 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()); + + auto read_lock = this->parent_filesystem->AcquireCacheInvalidationReadLock(); + return this->base_file->Write(offset, buffer.GetPointer(), size, option); + } + + Result FileInterfaceAdapter::Flush() { + auto read_lock = this->parent_filesystem->AcquireCacheInvalidationReadLock(); + return this->base_file->Flush(); + } + + Result FileInterfaceAdapter::SetSize(s64 size) { + R_UNLESS(size >= 0, fs::ResultInvalidSize()); + auto read_lock = this->parent_filesystem->AcquireCacheInvalidationReadLock(); + return this->base_file->SetSize(size); + } + + Result FileInterfaceAdapter::GetSize(ams::sf::Out out) { + auto read_lock = this->parent_filesystem->AcquireCacheInvalidationReadLock(); + return this->base_file->GetSize(out.GetPointer()); + } + + Result FileInterfaceAdapter::OperateRange(ams::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)) { + auto read_lock = this->parent_filesystem->AcquireCacheInvalidationReadLock(); + + fs::FileQueryRangeInfo info; + R_TRY(this->base_file->OperateRange(&info, sizeof(info), fs::OperationId::QueryRange, offset, size, nullptr, 0)); + out->Merge(info); + } + + return ResultSuccess(); + } + + DirectoryInterfaceAdapter::DirectoryInterfaceAdapter(std::unique_ptr &&dir, std::shared_ptr &&parent, std::unique_lock &&sema) + : parent_filesystem(std::move(parent)), base_dir(std::move(dir)), open_count_semaphore(std::move(sema)) + { + /* ... */ + } + + DirectoryInterfaceAdapter::~DirectoryInterfaceAdapter() { + /* ... */ + } + + Result DirectoryInterfaceAdapter::Read(ams::sf::Out out, const ams::sf::OutBuffer &out_entries) { + auto read_lock = this->parent_filesystem->AcquireCacheInvalidationReadLock(); + + const size_t max_num_entries = out_entries.GetSize() / sizeof(fs::DirectoryEntry); + R_UNLESS(max_num_entries >= 0, fs::ResultInvalidSize()); + + /* TODO: N retries on ResultDataCorrupted, we may want to eventually. */ + return this->base_dir->Read(out.GetPointer(), reinterpret_cast(out_entries.GetPointer()), max_num_entries); + } + + Result DirectoryInterfaceAdapter::GetEntryCount(ams::sf::Out out) { + auto read_lock = this->parent_filesystem->AcquireCacheInvalidationReadLock(); + return this->base_dir->GetEntryCount(out.GetPointer()); + } + + FileSystemInterfaceAdapter::FileSystemInterfaceAdapter(std::shared_ptr &&fs, bool open_limited) + : base_fs(std::move(fs)), open_count_limited(open_limited), deep_retry_enabled(false) + { + /* ... */ + } + + FileSystemInterfaceAdapter::~FileSystemInterfaceAdapter() { + /* ... */ + } + + bool FileSystemInterfaceAdapter::IsDeepRetryEnabled() const { + return this->deep_retry_enabled; + } + + bool FileSystemInterfaceAdapter::IsAccessFailureDetectionObserved() const { + /* TODO: This calls into fssrv::FileSystemProxyImpl, which we don't have yet. */ + AMS_ASSERT(false); + } + + std::optional> FileSystemInterfaceAdapter::AcquireCacheInvalidationReadLock() { + std::optional> lock; + if (this->deep_retry_enabled) { + lock.emplace(this->invalidation_lock); + } + return lock; + } + + os::ReadWriteLock &FileSystemInterfaceAdapter::GetReadWriteLockForCacheInvalidation() { + return this->invalidation_lock; + } + + Result FileSystemInterfaceAdapter::CreateFile(const fssrv::sf::Path &path, s64 size, s32 option) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + R_UNLESS(size >= 0, fs::ResultInvalidSize()); + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + return this->base_fs->CreateFile(normalizer.GetPath(), size, option); + } + + Result FileSystemInterfaceAdapter::DeleteFile(const fssrv::sf::Path &path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + return this->base_fs->DeleteFile(normalizer.GetPath()); + } + + Result FileSystemInterfaceAdapter::CreateDirectory(const fssrv::sf::Path &path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + R_UNLESS(strncmp(normalizer.GetPath(), "/", 2) != 0, fs::ResultPathAlreadyExists()); + + return this->base_fs->CreateDirectory(normalizer.GetPath()); + } + + Result FileSystemInterfaceAdapter::DeleteDirectory(const fssrv::sf::Path &path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + R_UNLESS(strncmp(normalizer.GetPath(), "/", 2) != 0, fs::ResultDirectoryNotDeletable()); + + return this->base_fs->DeleteDirectory(normalizer.GetPath()); + } + + Result FileSystemInterfaceAdapter::DeleteDirectoryRecursively(const fssrv::sf::Path &path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + R_UNLESS(strncmp(normalizer.GetPath(), "/", 2) != 0, fs::ResultDirectoryNotDeletable()); + + return this->base_fs->DeleteDirectoryRecursively(normalizer.GetPath()); + } + + Result FileSystemInterfaceAdapter::RenameFile(const fssrv::sf::Path &old_path, const fssrv::sf::Path &new_path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + PathNormalizer old_normalizer(old_path.str); + PathNormalizer new_normalizer(new_path.str); + R_UNLESS(old_normalizer.GetPath() != nullptr, old_normalizer.GetResult()); + R_UNLESS(new_normalizer.GetPath() != nullptr, new_normalizer.GetResult()); + + return this->base_fs->RenameFile(old_normalizer.GetPath(), new_normalizer.GetPath()); + } + + Result FileSystemInterfaceAdapter::RenameDirectory(const fssrv::sf::Path &old_path, const fssrv::sf::Path &new_path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + PathNormalizer old_normalizer(old_path.str); + PathNormalizer new_normalizer(new_path.str); + R_UNLESS(old_normalizer.GetPath() != nullptr, old_normalizer.GetResult()); + R_UNLESS(new_normalizer.GetPath() != nullptr, new_normalizer.GetResult()); + + const bool is_subpath = fssystem::PathTool::IsSubPath(old_normalizer.GetPath(), new_normalizer.GetPath()); + R_UNLESS(!is_subpath, fs::ResultDirectoryNotRenamable()); + + return this->base_fs->RenameFile(old_normalizer.GetPath(), new_normalizer.GetPath()); + } + + Result FileSystemInterfaceAdapter::GetEntryType(ams::sf::Out out, const fssrv::sf::Path &path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + static_assert(sizeof(*out.GetPointer()) == sizeof(fs::DirectoryEntryType)); + return this->base_fs->GetEntryType(reinterpret_cast(out.GetPointer()), normalizer.GetPath()); + } + + Result FileSystemInterfaceAdapter::OpenFile(ams::sf::Out> out, const fssrv::sf::Path &path, u32 mode) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + std::unique_lock open_count_semaphore; + if (this->open_count_limited) { + /* TODO: This calls into fssrv::FileSystemProxyImpl, which we don't have yet. */ + AMS_ASSERT(false); + } + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + /* TODO: N retries on ResultDataCorrupted, we may want to eventually. */ + std::unique_ptr file; + R_TRY(this->base_fs->OpenFile(&file, normalizer.GetPath(), static_cast(mode))); + + /* TODO: This is a hack to get the mitm API to work. Better solution? */ + const auto target_object_id = file->GetDomainObjectId(); + + /* TODO: N creates an nn::fssystem::AsynchronousAccessFile here. */ + + std::shared_ptr shared_this = this->shared_from_this(); + std::shared_ptr file_intf = std::make_shared(std::move(file), std::move(shared_this), std::move(open_count_semaphore)); + R_UNLESS(file_intf != nullptr, fs::ResultAllocationFailureInFileSystemInterfaceAdapter()); + + out.SetValue(std::move(file_intf), target_object_id); + return ResultSuccess(); + } + + Result FileSystemInterfaceAdapter::OpenDirectory(ams::sf::Out> out, const fssrv::sf::Path &path, u32 mode) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + std::unique_lock open_count_semaphore; + if (this->open_count_limited) { + /* TODO: This calls into fssrv::FileSystemProxyImpl, which we don't have yet. */ + AMS_ASSERT(false); + } + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + /* TODO: N retries on ResultDataCorrupted, we may want to eventually. */ + std::unique_ptr dir; + R_TRY(this->base_fs->OpenDirectory(&dir, normalizer.GetPath(), static_cast(mode))); + + /* TODO: This is a hack to get the mitm API to work. Better solution? */ + const auto target_object_id = dir->GetDomainObjectId(); + + std::shared_ptr shared_this = this->shared_from_this(); + std::shared_ptr dir_intf = std::make_shared(std::move(dir), std::move(shared_this), std::move(open_count_semaphore)); + R_UNLESS(dir_intf != nullptr, fs::ResultAllocationFailureInFileSystemInterfaceAdapter()); + + out.SetValue(std::move(dir_intf), target_object_id); + return ResultSuccess(); + } + + Result FileSystemInterfaceAdapter::Commit() { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + return this->base_fs->Commit(); + } + + Result FileSystemInterfaceAdapter::GetFreeSpaceSize(ams::sf::Out out, const fssrv::sf::Path &path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + return this->base_fs->GetFreeSpaceSize(out.GetPointer(), normalizer.GetPath()); + } + + Result FileSystemInterfaceAdapter::GetTotalSpaceSize(ams::sf::Out out, const fssrv::sf::Path &path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + return this->base_fs->GetTotalSpaceSize(out.GetPointer(), normalizer.GetPath()); + } + + Result FileSystemInterfaceAdapter::CleanDirectoryRecursively(const fssrv::sf::Path &path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + return this->base_fs->CleanDirectoryRecursively(normalizer.GetPath()); + } + + Result FileSystemInterfaceAdapter::GetFileTimeStampRaw(ams::sf::Out out, const fssrv::sf::Path &path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + PathNormalizer normalizer(path.str); + R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult()); + + return this->base_fs->GetFileTimeStampRaw(out.GetPointer(), normalizer.GetPath()); + } + + Result FileSystemInterfaceAdapter::QueryEntry(const ams::sf::OutBuffer &out_buf, const ams::sf::InBuffer &in_buf, s32 query_id, const fssrv::sf::Path &path) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + + /* TODO: Nintendo does not normalize the path. Should we? */ + + char *dst = reinterpret_cast< char *>(out_buf.GetPointer()); + const char *src = reinterpret_cast(in_buf.GetPointer()); + return this->base_fs->QueryEntry(dst, out_buf.GetSize(), src, in_buf.GetSize(), static_cast(query_id), path.str); + } + +} diff --git a/stratosphere/libstratosphere/source/fssrv/fssrv_path_normalizer.cpp b/stratosphere/libstratosphere/source/fssrv/fssrv_path_normalizer.cpp new file mode 100644 index 000000000..3de434732 --- /dev/null +++ b/stratosphere/libstratosphere/source/fssrv/fssrv_path_normalizer.cpp @@ -0,0 +1,77 @@ +/* + * 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 + +namespace ams::fssrv { + + Result PathNormalizer::Normalize(const char **out_path, Buffer *out_buf, const char *path, bool preserve_unc, bool preserve_tail_sep, bool has_mount_name) { + /* Clear output. */ + *out_path = nullptr; + *out_buf = Buffer(); + + /* Find start of path. */ + const char *path_start = path; + if (has_mount_name) { + while (path_start < path + fs::MountNameLengthMax + 1) { + if (fssystem::PathTool::IsNullTerminator(*path_start)) { + break; + } else if (fssystem::PathTool::IsDriveSeparator(*(path_start++))) { + break; + } + } + R_UNLESS(path < path_start - 1, fs::ResultInvalidPath()); + R_UNLESS(fssystem::PathTool::IsDriveSeparator(*(path_start - 1)), fs::ResultInvalidPath()); + } + + /* Check if we're normalized. */ + bool normalized = false; + R_TRY(fssystem::PathTool::IsNormalized(&normalized, path_start)); + + if (normalized) { + /* If we're already normalized, no allocation is needed. */ + *out_path = path; + } else { + /* Allocate a new buffer. */ + auto buffer = std::make_unique(fs::EntryNameLengthMax + 1); + R_UNLESS(buffer != nullptr, fs::ResultAllocationFailureInPathNormalizer()); + + /* Copy in mount name. */ + const size_t mount_name_len = path_start - path; + std::memcpy(buffer.get(), path, mount_name_len); + + /* Generate normalized path. */ + size_t normalized_len = 0; + R_TRY(fssystem::PathTool::Normalize(buffer.get() + mount_name_len, &normalized_len, path_start, fs::EntryNameLengthMax + 1 - mount_name_len, preserve_unc)); + + /* Preserve the tail separator, if we should. */ + if (preserve_tail_sep) { + if (fssystem::PathTool::IsSeparator(path[strnlen(path, fs::EntryNameLengthMax) - 1])) { + /* Nintendo doesn't actually validate this. */ + R_UNLESS(mount_name_len + normalized_len < fs::EntryNameLengthMax, fs::ResultTooLongPath()); + buffer[mount_name_len + normalized_len] = fssystem::StringTraits::DirectorySeparator; + buffer[mount_name_len + normalized_len + 1] = fssystem::StringTraits::NullTerminator; + } + } + + /* Save output. */ + *out_path = buffer.get(); + *out_buf = std::move(buffer); + } + + return ResultSuccess(); + } + +} diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_istorage_interface.cpp b/stratosphere/libstratosphere/source/fssrv/fssrv_storage_interface_adapter.cpp similarity index 51% rename from stratosphere/ams_mitm/source/fs_mitm/fsmitm_istorage_interface.cpp rename to stratosphere/libstratosphere/source/fssrv/fssrv_storage_interface_adapter.cpp index 2effb8295..95ecb2724 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_istorage_interface.cpp +++ b/stratosphere/libstratosphere/source/fssrv/fssrv_storage_interface_adapter.cpp @@ -13,45 +13,76 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include "fsmitm_istorage_interface.hpp" +#include +#include -namespace ams::mitm::fs { +namespace ams::fssrv::impl { - using namespace ams::fs; + StorageInterfaceAdapter::StorageInterfaceAdapter(fs::IStorage *storage) : base_storage(storage) { + /* ... */ + } - Result IStorageInterface::Read(s64 offset, const sf::OutNonSecureBuffer &buffer, s64 size) { + StorageInterfaceAdapter::StorageInterfaceAdapter(std::unique_ptr storage) : base_storage(storage.release()) { + /* ... */ + } + + StorageInterfaceAdapter::StorageInterfaceAdapter(std::shared_ptr &&storage) : base_storage(std::move(storage)) { + /* ... */ + } + + StorageInterfaceAdapter::~StorageInterfaceAdapter() { + /* ... */ + } + + std::optional> StorageInterfaceAdapter::AcquireCacheInvalidationReadLock() { + std::optional> lock; + if (this->deep_retry_enabled) { + lock.emplace(this->invalidation_lock); + } + return lock; + } + + Result StorageInterfaceAdapter::Read(s64 offset, const ams::sf::OutNonSecureBuffer &buffer, s64 size) { /* TODO: N retries on ResultDataCorrupted, we may want to eventually. */ + /* TODO: Deep retry */ 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) { + Result StorageInterfaceAdapter::Write(s64 offset, const ams::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()); + + auto read_lock = this->AcquireCacheInvalidationReadLock(); return this->base_storage->Write(offset, buffer.GetPointer(), size); } - Result IStorageInterface::Flush() { + Result StorageInterfaceAdapter::Flush() { + auto read_lock = this->AcquireCacheInvalidationReadLock(); return this->base_storage->Flush(); } - Result IStorageInterface::SetSize(s64 size) { + Result StorageInterfaceAdapter::SetSize(s64 size) { R_UNLESS(size >= 0, fs::ResultInvalidSize()); + auto read_lock = this->AcquireCacheInvalidationReadLock(); return this->base_storage->SetSize(size); } - Result IStorageInterface::GetSize(sf::Out out) { + Result StorageInterfaceAdapter::GetSize(ams::sf::Out out) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); return this->base_storage->GetSize(out.GetPointer()); } - Result IStorageInterface::OperateRange(sf::Out out, s32 op_id, s64 offset, s64 size) { + Result StorageInterfaceAdapter::OperateRange(ams::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)) { + auto read_lock = this->AcquireCacheInvalidationReadLock(); + fs::StorageQueryRangeInfo info; R_TRY(this->base_storage->OperateRange(&info, sizeof(info), fs::OperationId::QueryRange, offset, size, nullptr, 0)); out->Merge(info); diff --git a/stratosphere/libstratosphere/source/fssystem/fssystem_directory_redirection_filesystem.cpp b/stratosphere/libstratosphere/source/fssystem/fssystem_directory_redirection_filesystem.cpp new file mode 100644 index 000000000..24d17928b --- /dev/null +++ b/stratosphere/libstratosphere/source/fssystem/fssystem_directory_redirection_filesystem.cpp @@ -0,0 +1,115 @@ +/* + * 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 + +namespace ams::fssystem { + + DirectoryRedirectionFileSystem::DirectoryRedirectionFileSystem(std::shared_ptr fs, const char *before, const char *after) : PathResolutionFileSystem(fs) { + this->before_dir = nullptr; + this->after_dir = nullptr; + R_ASSERT(this->Initialize(before, after)); + } + + DirectoryRedirectionFileSystem::DirectoryRedirectionFileSystem(std::shared_ptr fs, const char *before, const char *after, bool unc) : PathResolutionFileSystem(fs, unc) { + this->before_dir = nullptr; + this->after_dir = nullptr; + R_ASSERT(this->Initialize(before, after)); + } + + DirectoryRedirectionFileSystem::~DirectoryRedirectionFileSystem() { + if (this->before_dir != nullptr) { + std::free(this->before_dir); + } + if (this->after_dir != nullptr) { + std::free(this->after_dir); + } + } + + Result DirectoryRedirectionFileSystem::GetNormalizedDirectoryPath(char **out, size_t *out_size, const char *dir) { + /* Clear output. */ + *out = nullptr; + *out_size = 0; + + /* Make sure the path isn't too long. */ + R_UNLESS(strnlen(dir, fs::EntryNameLengthMax + 1) <= fs::EntryNameLengthMax, fs::ResultTooLongPath()); + + /* Normalize the path. */ + char normalized_path[fs::EntryNameLengthMax + 2]; + size_t normalized_path_len; + R_TRY(PathTool::Normalize(normalized_path, &normalized_path_len, dir, sizeof(normalized_path), this->IsUncPreserved())); + + /* Ensure terminating '/' */ + if (!PathTool::IsSeparator(normalized_path[normalized_path_len - 1])) { + AMS_ASSERT(normalized_path_len + 2 <= sizeof(normalized_path)); + normalized_path[normalized_path_len] = StringTraits::DirectorySeparator; + normalized_path[normalized_path_len + 1] = StringTraits::NullTerminator; + + normalized_path_len++; + } + + /* Allocate new path. */ + const size_t size = normalized_path_len + 1; + char *new_dir = static_cast(std::malloc(size)); + AMS_ASSERT(new_dir != nullptr); + /* TODO: custom ResultAllocationFailure? */ + + /* Copy path in. */ + std::memcpy(new_dir, normalized_path, normalized_path_len); + new_dir[normalized_path_len] = StringTraits::NullTerminator; + + /* Set output. */ + *out = new_dir; + *out_size = size; + return ResultSuccess(); + } + + Result DirectoryRedirectionFileSystem::Initialize(const char *before, const char *after) { + /* Normalize both directories. */ + this->GetNormalizedDirectoryPath(&this->before_dir, &this->before_dir_len, before); + this->GetNormalizedDirectoryPath(&this->after_dir, &this->after_dir_len, after); + + return ResultSuccess(); + } + + Result DirectoryRedirectionFileSystem::ResolveFullPath(char *out, size_t out_size, const char *relative_path) { + /* Normalize the relative path. */ + char normalized_rel_path[fs::EntryNameLengthMax + 1]; + size_t normalized_rel_path_len; + R_TRY(PathTool::Normalize(normalized_rel_path, &normalized_rel_path_len, relative_path, sizeof(normalized_rel_path), this->IsUncPreserved())); + + const bool is_prefixed = std::memcmp(normalized_rel_path, this->before_dir, this->before_dir_len - 2) == 0 && + (PathTool::IsSeparator(normalized_rel_path[this->before_dir_len - 2]) || PathTool::IsNullTerminator(normalized_rel_path[this->before_dir_len - 2])); + if (is_prefixed) { + const size_t before_prefix_len = this->before_dir_len - 2; + const size_t after_prefix_len = this->after_dir_len - 2; + const size_t final_str_len = after_prefix_len + normalized_rel_path_len - before_prefix_len; + R_UNLESS(final_str_len < out_size, fs::ResultTooLongPath()); + + /* Copy normalized path. */ + std::memcpy(out, this->after_dir, after_prefix_len); + std::memcpy(out + after_prefix_len, normalized_rel_path + before_prefix_len, normalized_rel_path_len - before_prefix_len); + out[final_str_len] = StringTraits::NullTerminator; + } else { + /* Path is not prefixed. */ + R_UNLESS(normalized_rel_path_len + 1 <= out_size, fs::ResultTooLongPath()); + std::memcpy(out, normalized_rel_path, normalized_rel_path_len); + out[normalized_rel_path_len] = StringTraits::NullTerminator; + } + + return ResultSuccess(); + } + +} diff --git a/stratosphere/libstratosphere/source/fssystem/fssystem_subdirectory_filesystem.cpp b/stratosphere/libstratosphere/source/fssystem/fssystem_subdirectory_filesystem.cpp new file mode 100644 index 000000000..bcfafcac4 --- /dev/null +++ b/stratosphere/libstratosphere/source/fssystem/fssystem_subdirectory_filesystem.cpp @@ -0,0 +1,78 @@ +/* + * 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 + +namespace ams::fssystem { + + SubDirectoryFileSystem::SubDirectoryFileSystem(std::shared_ptr fs, const char *bp) : PathResolutionFileSystem(fs) { + this->base_path = nullptr; + R_ASSERT(this->Initialize(bp)); + } + + SubDirectoryFileSystem::SubDirectoryFileSystem(std::shared_ptr fs, const char *bp, bool unc) : PathResolutionFileSystem(fs, unc) { + this->base_path = nullptr; + R_ASSERT(this->Initialize(bp)); + } + + SubDirectoryFileSystem::~SubDirectoryFileSystem() { + if (this->base_path != nullptr) { + std::free(this->base_path); + } + } + + Result SubDirectoryFileSystem::Initialize(const char *bp) { + /* Make sure the path isn't too long. */ + R_UNLESS(strnlen(bp, fs::EntryNameLengthMax + 1) <= fs::EntryNameLengthMax, fs::ResultTooLongPath()); + + /* Normalize the path. */ + char normalized_path[fs::EntryNameLengthMax + 2]; + size_t normalized_path_len; + R_TRY(PathTool::Normalize(normalized_path, &normalized_path_len, bp, sizeof(normalized_path), this->IsUncPreserved())); + + /* Ensure terminating '/' */ + if (!PathTool::IsSeparator(normalized_path[normalized_path_len - 1])) { + AMS_ASSERT(normalized_path_len + 2 <= sizeof(normalized_path)); + normalized_path[normalized_path_len] = StringTraits::DirectorySeparator; + normalized_path[normalized_path_len + 1] = StringTraits::NullTerminator; + + normalized_path_len++; + } + + /* Allocate new path. */ + this->base_path_len = normalized_path_len + 1; + this->base_path = static_cast(std::malloc(this->base_path_len)); + R_UNLESS(this->base_path != nullptr, fs::ResultAllocationFailureInSubDirectoryFileSystem()); + + /* Copy path in. */ + std::memcpy(this->base_path, normalized_path, normalized_path_len); + this->base_path[normalized_path_len] = StringTraits::NullTerminator; + return ResultSuccess(); + } + + Result SubDirectoryFileSystem::ResolveFullPath(char *out, size_t out_size, const char *relative_path) { + /* Ensure path will fit. */ + R_UNLESS(this->base_path_len + strnlen(relative_path, fs::EntryNameLengthMax + 1) <= out_size, fs::ResultTooLongPath()); + + /* Copy base path. */ + std::memcpy(out, this->base_path, this->base_path_len); + + /* Normalize it. */ + const size_t prefix_size = this->base_path_len - 2; + size_t normalized_len; + return PathTool::Normalize(out + prefix_size, &normalized_len, relative_path, out_size - prefix_size, this->IsUncPreserved()); + } + +} diff --git a/stratosphere/libstratosphere/source/os/impl/os_waitable_holder_impl.hpp b/stratosphere/libstratosphere/source/os/impl/os_waitable_holder_impl.hpp index ccd7a335f..d87044097 100644 --- a/stratosphere/libstratosphere/source/os/impl/os_waitable_holder_impl.hpp +++ b/stratosphere/libstratosphere/source/os/impl/os_waitable_holder_impl.hpp @@ -19,6 +19,7 @@ #include "os_waitable_holder_of_inter_process_event.hpp" #include "os_waitable_holder_of_interrupt_event.hpp" #include "os_waitable_holder_of_thread.hpp" +#include "os_waitable_holder_of_semaphore.hpp" #include "os_waitable_holder_of_message_queue.hpp" namespace ams::os::impl { @@ -30,6 +31,7 @@ namespace ams::os::impl { TYPED_STORAGE(WaitableHolderOfInterProcessEvent) holder_of_inter_process_event_storage; TYPED_STORAGE(WaitableHolderOfInterruptEvent) holder_of_interrupt_event_storage; TYPED_STORAGE(WaitableHolderOfThread) holder_of_thread_storage; + TYPED_STORAGE(WaitableHolderOfSemaphore) holder_of_semaphore_storage; TYPED_STORAGE(WaitableHolderOfMessageQueueForNotFull) holder_of_mq_for_not_full_storage; TYPED_STORAGE(WaitableHolderOfMessageQueueForNotEmpty) holder_of_mq_for_not_empty_storage; }; @@ -43,6 +45,7 @@ namespace ams::os::impl { CHECK_HOLDER(WaitableHolderOfInterProcessEvent); CHECK_HOLDER(WaitableHolderOfInterruptEvent); CHECK_HOLDER(WaitableHolderOfThread); + CHECK_HOLDER(WaitableHolderOfSemaphore); CHECK_HOLDER(WaitableHolderOfMessageQueueForNotFull); CHECK_HOLDER(WaitableHolderOfMessageQueueForNotEmpty); diff --git a/stratosphere/libstratosphere/source/os/impl/os_waitable_holder_of_semaphore.hpp b/stratosphere/libstratosphere/source/os/impl/os_waitable_holder_of_semaphore.hpp new file mode 100644 index 000000000..36939ef7e --- /dev/null +++ b/stratosphere/libstratosphere/source/os/impl/os_waitable_holder_of_semaphore.hpp @@ -0,0 +1,52 @@ +/* + * 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 "os_waitable_holder_base.hpp" +#include "os_waitable_object_list.hpp" + +namespace ams::os::impl { + + class WaitableHolderOfSemaphore : public WaitableHolderOfUserObject { + private: + Semaphore *semaphore; + private: + TriBool IsSignaledImpl() const { + return this->semaphore->count > 0 ? TriBool::True : TriBool::False; + } + public: + explicit WaitableHolderOfSemaphore(Semaphore *s) : semaphore(s) { /* ... */ } + + /* IsSignaled, Link, Unlink implemented. */ + virtual TriBool IsSignaled() const override { + std::scoped_lock lk(this->semaphore->mutex); + return this->IsSignaledImpl(); + } + + virtual TriBool LinkToObjectList() override { + std::scoped_lock lk(this->semaphore->mutex); + + GetReference(this->semaphore->waitlist).LinkWaitableHolder(*this); + return this->IsSignaledImpl(); + } + + virtual void UnlinkFromObjectList() override { + std::scoped_lock lk(this->semaphore->mutex); + + GetReference(this->semaphore->waitlist).UnlinkWaitableHolder(*this); + } + }; + +} diff --git a/stratosphere/libstratosphere/source/os/os_semaphore.cpp b/stratosphere/libstratosphere/source/os/os_semaphore.cpp new file mode 100644 index 000000000..12c7c532b --- /dev/null +++ b/stratosphere/libstratosphere/source/os/os_semaphore.cpp @@ -0,0 +1,85 @@ +/* + * 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 "impl/os_waitable_object_list.hpp" + +namespace ams::os { + + Semaphore::Semaphore(int c, int mc) : count(c), max_count(mc) { + new (GetPointer(this->waitlist)) impl::WaitableObjectList(); + } + + Semaphore::~Semaphore() { + GetReference(this->waitlist).~WaitableObjectList(); + } + + void Semaphore::Acquire() { + std::scoped_lock lk(this->mutex); + + while (this->count == 0) { + this->condvar.Wait(&this->mutex); + } + + this->count--; + } + + bool Semaphore::TryAcquire() { + std::scoped_lock lk(this->mutex); + + if (this->count == 0) { + return false; + } + + this->count--; + return true; + } + + bool Semaphore::TimedAcquire(u64 timeout) { + std::scoped_lock lk(this->mutex); + TimeoutHelper timeout_helper(timeout); + + while (this->count == 0) { + if (timeout_helper.TimedOut()) { + return false; + } + + this->condvar.TimedWait(&this->mutex, timeout_helper.NsUntilTimeout()); + } + + this->count--; + return true; + } + + void Semaphore::Release() { + std::scoped_lock lk(this->mutex); + + AMS_ASSERT(this->count + 1 <= this->max_count); + this->count++; + + this->condvar.Signal(); + GetReference(this->waitlist).SignalAllThreads(); + } + + void Semaphore::Release(int count) { + std::scoped_lock lk(this->mutex); + + AMS_ASSERT(this->count + count <= this->max_count); + this->count += count; + + this->condvar.Broadcast(); + GetReference(this->waitlist).SignalAllThreads(); + } + +} diff --git a/stratosphere/libstratosphere/source/os/os_waitable_holder.cpp b/stratosphere/libstratosphere/source/os/os_waitable_holder.cpp index 1221e4469..d653cd2af 100644 --- a/stratosphere/libstratosphere/source/os/os_waitable_holder.cpp +++ b/stratosphere/libstratosphere/source/os/os_waitable_holder.cpp @@ -70,6 +70,14 @@ namespace ams::os { this->user_data = 0; } + WaitableHolder::WaitableHolder(Semaphore *semaphore) { + /* Initialize appropriate holder. */ + new (GetPointer(this->impl_storage)) impl::WaitableHolderOfSemaphore(semaphore); + + /* Set user-data. */ + this->user_data = 0; + } + WaitableHolder::WaitableHolder(MessageQueue *message_queue, MessageQueueWaitKind wait_kind) { /* Initialize appropriate holder. */ switch (wait_kind) { diff --git a/stratosphere/libstratosphere/source/spl/smc/spl_smc.cpp b/stratosphere/libstratosphere/source/spl/smc/spl_smc.cpp index 03668251c..bfd4f0e56 100644 --- a/stratosphere/libstratosphere/source/spl/smc/spl_smc.cpp +++ b/stratosphere/libstratosphere/source/spl/smc/spl_smc.cpp @@ -363,7 +363,7 @@ namespace ams::spl::smc { Result AtmosphereGetEmummcConfig(void *out_config, void *out_paths, u32 storage_id) { const u64 paths = reinterpret_cast(out_paths); - AMS_ASSERT(util::IsAligned(paths, 0x1000)); + AMS_ASSERT(util::IsAligned(paths, os::MemoryPageSize)); SecmonArgs args = {}; args.X[0] = static_cast(FunctionId::AtmosphereGetEmummcConfig); diff --git a/stratosphere/libstratosphere/source/updater/updater_api.cpp b/stratosphere/libstratosphere/source/updater/updater_api.cpp index cb7a7ddbf..510a366ec 100644 --- a/stratosphere/libstratosphere/source/updater/updater_api.cpp +++ b/stratosphere/libstratosphere/source/updater/updater_api.cpp @@ -54,15 +54,9 @@ namespace ams::updater { /* Implementations. */ Result ValidateWorkBuffer(const void *work_buffer, size_t work_buffer_size) { - if (work_buffer_size < BctSize + EksSize) { - return ResultTooSmallWorkBuffer(); - } - if (!util::IsAligned(work_buffer, 0x1000)) { - return ResultNotAlignedWorkBuffer(); - } - if (util::IsAligned(work_buffer_size, 0x200)) { - return ResultNotAlignedWorkBuffer(); - } + R_UNLESS(work_buffer_size >= BctSize + EksSize, ResultTooSmallWorkBuffer()); + R_UNLESS(util::IsAligned(work_buffer, os::MemoryPageSize), ResultNotAlignedWorkBuffer()); + R_UNLESS(util::IsAligned(work_buffer_size, 0x200), ResultNotAlignedWorkBuffer()); return ResultSuccess(); } diff --git a/stratosphere/libstratosphere/source/updater/updater_bis_save.cpp b/stratosphere/libstratosphere/source/updater/updater_bis_save.cpp index fba422673..30af92cdc 100644 --- a/stratosphere/libstratosphere/source/updater/updater_bis_save.cpp +++ b/stratosphere/libstratosphere/source/updater/updater_bis_save.cpp @@ -30,7 +30,7 @@ namespace ams::updater { Result BisSave::Initialize(void *work_buffer, size_t work_buffer_size) { AMS_ASSERT(work_buffer_size >= SaveSize); - AMS_ASSERT(util::IsAligned(reinterpret_cast(work_buffer), 0x1000)); + AMS_ASSERT(util::IsAligned(reinterpret_cast(work_buffer), os::MemoryPageSize)); AMS_ASSERT(util::IsAligned(work_buffer_size, 0x200)); R_TRY(this->accessor.Initialize()); diff --git a/stratosphere/loader/source/ldr_main.cpp b/stratosphere/loader/source/ldr_main.cpp index c37698aa7..6881960da 100644 --- a/stratosphere/loader/source/ldr_main.cpp +++ b/stratosphere/loader/source/ldr_main.cpp @@ -32,7 +32,7 @@ extern "C" { void __appExit(void); /* Exception handling. */ - alignas(16) u8 __nx_exception_stack[0x1000]; + alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize]; u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); void __libnx_exception_handler(ThreadExceptionDump *ctx); } diff --git a/stratosphere/loader/source/ldr_process_creation.cpp b/stratosphere/loader/source/ldr_process_creation.cpp index 70281d892..1534280ec 100644 --- a/stratosphere/loader/source/ldr_process_creation.cpp +++ b/stratosphere/loader/source/ldr_process_creation.cpp @@ -27,8 +27,6 @@ namespace ams::ldr { namespace { /* Convenience defines. */ - constexpr size_t BaseAddressAlignment = 0x200000; - constexpr size_t SystemResourceSizeAlignment = 0x200000; constexpr size_t SystemResourceSizeMax = 0x1FE00000; /* Types. */ @@ -416,7 +414,7 @@ namespace ams::ldr { /* 3.0.0+ System Resource Size. */ if (hos::GetVersion() >= hos::Version_300) { /* Validate size is aligned. */ - R_UNLESS(util::IsAligned(meta->npdm->system_resource_size, SystemResourceSizeAlignment), ResultInvalidSize()); + R_UNLESS(util::IsAligned(meta->npdm->system_resource_size, os::MemoryBlockUnitSize), ResultInvalidSize()); /* Validate system resource usage. */ if (meta->npdm->system_resource_size) { @@ -503,7 +501,7 @@ namespace ams::ldr { uintptr_t aslr_slide = 0; uintptr_t unused_size = (aslr_size - total_size); if (out_cpi->flags & svc::CreateProcessFlag_EnableAslr) { - aslr_slide = ams::rnd::GenerateRandomU64(unused_size / BaseAddressAlignment) * BaseAddressAlignment; + aslr_slide = ams::rnd::GenerateRandomU64(unused_size / os::MemoryBlockUnitSize) * os::MemoryBlockUnitSize; } /* Set out. */ diff --git a/stratosphere/pm/source/pm_main.cpp b/stratosphere/pm/source/pm_main.cpp index 3c83ec525..9d89df62f 100644 --- a/stratosphere/pm/source/pm_main.cpp +++ b/stratosphere/pm/source/pm_main.cpp @@ -34,7 +34,7 @@ extern "C" { void __appExit(void); /* Exception handling. */ - alignas(16) u8 __nx_exception_stack[0x1000]; + alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize]; u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); void __libnx_exception_handler(ThreadExceptionDump *ctx); } diff --git a/stratosphere/ro/source/impl/ro_service_impl.cpp b/stratosphere/ro/source/impl/ro_service_impl.cpp index 9190eae4c..1742b7516 100644 --- a/stratosphere/ro/source/impl/ro_service_impl.cpp +++ b/stratosphere/ro/source/impl/ro_service_impl.cpp @@ -191,24 +191,24 @@ namespace ams::ro::impl { const u64 bss_size = header->GetBssSize(); /* Validate sizes meet expected. */ - R_UNLESS(nro_size == expected_nro_size, ResultInvalidNro()); - R_UNLESS(bss_size == expected_bss_size, ResultInvalidNro()); + R_UNLESS(nro_size == expected_nro_size, ResultInvalidNro()); + R_UNLESS(bss_size == expected_bss_size, ResultInvalidNro()); /* Validate all sizes are aligned. */ - R_UNLESS(util::IsAligned(text_size, 0x1000), ResultInvalidNro()); - R_UNLESS(util::IsAligned(ro_size, 0x1000), ResultInvalidNro()); - R_UNLESS(util::IsAligned(rw_size, 0x1000), ResultInvalidNro()); - R_UNLESS(util::IsAligned(bss_size, 0x1000), ResultInvalidNro()); + R_UNLESS(util::IsAligned(text_size, os::MemoryPageSize), ResultInvalidNro()); + R_UNLESS(util::IsAligned(ro_size, os::MemoryPageSize), ResultInvalidNro()); + R_UNLESS(util::IsAligned(rw_size, os::MemoryPageSize), ResultInvalidNro()); + R_UNLESS(util::IsAligned(bss_size, os::MemoryPageSize), ResultInvalidNro()); /* Validate sections are in order. */ - R_UNLESS(text_ofs <= ro_ofs, ResultInvalidNro()); - R_UNLESS(ro_ofs <= rw_ofs, ResultInvalidNro()); + R_UNLESS(text_ofs <= ro_ofs, ResultInvalidNro()); + R_UNLESS(ro_ofs <= rw_ofs, ResultInvalidNro()); /* Validate sections are sequential and contiguous. */ - R_UNLESS(text_ofs == 0, ResultInvalidNro()); - R_UNLESS(text_ofs + text_size == ro_ofs, ResultInvalidNro()); - R_UNLESS(ro_ofs + ro_size == rw_ofs, ResultInvalidNro()); - R_UNLESS(rw_ofs + rw_size == nro_size, ResultInvalidNro()); + R_UNLESS(text_ofs == 0, ResultInvalidNro()); + R_UNLESS(text_ofs + text_size == ro_ofs, ResultInvalidNro()); + R_UNLESS(ro_ofs + ro_size == rw_ofs, ResultInvalidNro()); + R_UNLESS(rw_ofs + rw_size == nro_size, ResultInvalidNro()); /* Verify NRO hash. */ R_TRY(this->ValidateHasNroHash(header)); @@ -287,10 +287,10 @@ namespace ams::ro::impl { } constexpr inline Result ValidateAddressAndSize(u64 address, u64 size) { - R_UNLESS(util::IsAligned(address, 0x1000), ResultInvalidAddress()); - R_UNLESS(size != 0, ResultInvalidSize()); - R_UNLESS(util::IsAligned(size, 0x1000), ResultInvalidSize()); - R_UNLESS(address < address + size, ResultInvalidSize()); + R_UNLESS(util::IsAligned(address, os::MemoryPageSize), ResultInvalidAddress()); + R_UNLESS(size != 0, ResultInvalidSize()); + R_UNLESS(util::IsAligned(size, os::MemoryPageSize), ResultInvalidSize()); + R_UNLESS(address < address + size, ResultInvalidSize()); return ResultSuccess(); } @@ -393,7 +393,7 @@ namespace ams::ro::impl { AMS_ASSERT(context != nullptr); /* Validate address. */ - R_UNLESS(util::IsAligned(nrr_address, 0x1000), ResultInvalidAddress()); + R_UNLESS(util::IsAligned(nrr_address, os::MemoryPageSize), ResultInvalidAddress()); /* Check the NRR is loaded. */ NrrInfo *nrr_info = nullptr; @@ -461,7 +461,7 @@ namespace ams::ro::impl { AMS_ASSERT(context != nullptr); /* Validate address. */ - R_UNLESS(util::IsAligned(nro_address, 0x1000), ResultInvalidAddress()); + R_UNLESS(util::IsAligned(nro_address, os::MemoryPageSize), ResultInvalidAddress()); /* Check the NRO is loaded. */ NroInfo *nro_info = nullptr; diff --git a/stratosphere/sm/source/sm_main.cpp b/stratosphere/sm/source/sm_main.cpp index 1569b20a0..80390a3e1 100644 --- a/stratosphere/sm/source/sm_main.cpp +++ b/stratosphere/sm/source/sm_main.cpp @@ -32,7 +32,7 @@ extern "C" { void __appExit(void); /* Exception handling. */ - alignas(16) u8 __nx_exception_stack[0x1000]; + alignas(16) u8 __nx_exception_stack[ams::os::MemoryBlockUnitSize]; u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); void __libnx_exception_handler(ThreadExceptionDump *ctx); diff --git a/stratosphere/spl/source/spl_api_impl.cpp b/stratosphere/spl/source/spl_api_impl.cpp index 0b92c5c71..7da353065 100644 --- a/stratosphere/spl/source/spl_api_impl.cpp +++ b/stratosphere/spl/source/spl_api_impl.cpp @@ -102,7 +102,7 @@ namespace ams::spl::impl { Handle g_se_das_hnd; u32 g_se_mapped_work_buffer_addr; - u8 __attribute__((aligned(0x1000))) g_work_buffer[2 * WorkBufferSizeMax]; + alignas(os::MemoryPageSize) u8 g_work_buffer[2 * WorkBufferSizeMax]; os::Mutex g_async_op_lock; @@ -597,10 +597,10 @@ namespace ams::spl::impl { /* We can only map 0x400000 aligned buffers for the SE. With that in mind, we have some math to do. */ const uintptr_t src_addr = reinterpret_cast(src); const uintptr_t dst_addr = reinterpret_cast(dst); - const uintptr_t src_addr_page_aligned = util::AlignDown(src_addr, 0x1000); - const uintptr_t dst_addr_page_aligned = util::AlignDown(dst_addr, 0x1000); - const size_t src_size_page_aligned = util::AlignUp(src_addr + src_size, 0x1000) - src_addr_page_aligned; - const size_t dst_size_page_aligned = util::AlignUp(dst_addr + dst_size, 0x1000) - dst_addr_page_aligned; + const uintptr_t src_addr_page_aligned = util::AlignDown(src_addr, os::MemoryPageSize); + const uintptr_t dst_addr_page_aligned = util::AlignDown(dst_addr, os::MemoryPageSize); + const size_t src_size_page_aligned = util::AlignUp(src_addr + src_size, os::MemoryPageSize) - src_addr_page_aligned; + const size_t dst_size_page_aligned = util::AlignUp(dst_addr + dst_size, os::MemoryPageSize) - dst_addr_page_aligned; const u32 src_se_map_addr = CryptAesInMapBase + (src_addr_page_aligned % DeviceAddressSpaceAlign); const u32 dst_se_map_addr = CryptAesOutMapBase + (dst_addr_page_aligned % DeviceAddressSpaceAlign); const u32 src_se_addr = CryptAesInMapBase + (src_addr % DeviceAddressSpaceAlign); diff --git a/stratosphere/spl/source/spl_main.cpp b/stratosphere/spl/source/spl_main.cpp index bd1390480..1d353b7a5 100644 --- a/stratosphere/spl/source/spl_main.cpp +++ b/stratosphere/spl/source/spl_main.cpp @@ -39,7 +39,7 @@ extern "C" { void __appExit(void); /* Exception handling. */ - alignas(16) u8 __nx_exception_stack[0x1000]; + alignas(16) u8 __nx_exception_stack[ams::os::MemoryPageSize]; u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); void __libnx_exception_handler(ThreadExceptionDump *ctx); }