ams_mitm: Implement emummc Nintendo folder redirection

This commit is contained in:
Michael Scire 2019-12-05 23:41:33 -08:00 committed by SciresM
parent 733f2b3cdd
commit 746dbfe018
78 changed files with 2190 additions and 187 deletions

View file

@ -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);
}

View file

@ -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 *); \
}

View file

@ -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. */

View file

@ -62,13 +62,29 @@ namespace ams::mitm::fs {
}
Result FsMitmService::OpenSdCardFileSystem(sf::Out<std::shared_ptr<IFileSystemInterface>> 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<fs::fsa::IFileSystem> redir_fs = std::make_shared<fssystem::DirectoryRedirectionFileSystem>(std::make_shared<RemoteFileSystem>(sd_fs), "/Nintendo", emummc::GetNintendoDirPath());
out.SetValue(std::make_shared<IFileSystemInterface>(std::move(redir_fs), false), target_object_id);
return ResultSuccess();
}
Result FsMitmService::OpenBisStorage(sf::Out<std::shared_ptr<IStorageInterface>> 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<u64>(data_id), static_cast<NcmStorageId>(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. */
{

View file

@ -16,10 +16,13 @@
#pragma once
#include <stratosphere.hpp>
#include "fsmitm_istorage_interface.hpp"
#include <stratosphere/fssrv/fssrv_interface_adapters.hpp>
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<std::shared_ptr<IFileSystemInterface>> out, u64 program_id, u32 filesystem_type); */
/* Result OpenFileSystemWithId(Out<std::shared_ptr<IFileSystemInterface>> out, InPointer<char> path, u64 program_id, u32 filesystem_type); */
/* Result OpenSdCardFileSystem(Out<std::shared_ptr<IFileSystemInterface>> out); */
Result OpenSdCardFileSystem(sf::Out<std::shared_ptr<IFileSystemInterface>> out);
/* Result OpenSaveDataFileSystem(Out<std::shared_ptr<IFileSystemInterface>> out, u8 space_id, FsSave save_struct); */
Result OpenBisStorage(sf::Out<std::shared_ptr<IStorageInterface>> out, u32 bis_partition_id);
Result OpenDataStorageByCurrentProcess(sf::Out<std::shared_ptr<IStorageInterface>> 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),

View file

@ -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) {

View file

@ -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);

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
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<ams::fs::IStorage> base_storage;
public:
IStorageInterface(ams::fs::IStorage *s) : base_storage(s) { /* ... */ }
IStorageInterface(std::unique_ptr<ams::fs::IStorage> 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<s64> out) final;
virtual Result OperateRange(sf::Out<ams::fs::StorageQueryRangeInfo> 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),
};
};
}

View file

@ -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);

View file

@ -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. */

View file

@ -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);
}

View file

@ -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<size_t>(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<size_t>(fusee_primary_bin_size - ofs), sizeof(g_work_page)));
exosphere::CopyToIram(IramPayloadBase + ofs, g_work_page, sizeof(g_work_page));
}

View file

@ -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];
}

View file

@ -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;

View file

@ -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 *>(registers);
}

View file

@ -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);
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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;

View file

@ -50,9 +50,9 @@ namespace ams::dmnt::cheat::impl {
CheatEntry cheat_entries[MaxCheatCount] = {};
std::map<u64, FrozenAddressValue> 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);

View file

@ -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<os::MessageQueue, NumCores> message_queues;
std::array<os::Thread, NumCores> 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. */

View file

@ -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];

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;

View file

@ -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

View file

@ -35,6 +35,7 @@
#include <optional>
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <algorithm>
#include <functional>
#include <tuple>

View file

@ -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);

View file

@ -51,3 +51,5 @@
/* Include FS last. */
#include "stratosphere/fs.hpp"
#include "stratosphere/fssrv.hpp"
#include "stratosphere/fssystem.hpp"

View file

@ -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"

View file

@ -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;
}

View file

@ -18,6 +18,8 @@
namespace ams::fs {
constexpr inline size_t EntryNameLengthMax = 0x300;
using DirectoryEntry = ::FsDirectoryEntry;
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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);
};
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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);
}

View file

@ -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 {

View file

@ -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:

View file

@ -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:

View file

@ -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<IFile> *out_file, const char *path, OpenMode mode) = 0;
virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> *out_dir, const char *path, OpenDirectoryMode mode) = 0;
virtual Result GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) = 0;
virtual Result OpenFileImpl(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) = 0;
virtual Result OpenDirectoryImpl(std::unique_ptr<fs::fsa::IDirectory> *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();
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "fssrv/fssrv_sf_path.hpp"
#include "fssrv/fssrv_path_normalizer.hpp"

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "interface_adapters/fssrv_storage_interface_adapter.hpp"
#include "interface_adapters/fssrv_filesystem_interface_adapter.hpp"

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<char[]>;
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;
}
};
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<Path>::value && sizeof(Path) == FS_MAX_PATH);
using FspPath = Path;
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<FileSystemInterfaceAdapter> parent_filesystem;
std::unique_ptr<fs::fsa::IFile> base_file;
std::unique_lock<fssystem::SemaphoreAdapter> open_count_semaphore;
public:
FileInterfaceAdapter(std::unique_ptr<fs::fsa::IFile> &&file, std::shared_ptr<FileSystemInterfaceAdapter> &&parent, std::unique_lock<fssystem::SemaphoreAdapter> &&sema);
~FileInterfaceAdapter();
private:
void InvalidateCache();
public:
/* Command API. */
Result Read(ams::sf::Out<s64> 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<s64> out);
Result OperateRange(ams::sf::Out<fs::FileQueryRangeInfo> 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<FileSystemInterfaceAdapter> parent_filesystem;
std::unique_ptr<fs::fsa::IDirectory> base_dir;
std::unique_lock<fssystem::SemaphoreAdapter> open_count_semaphore;
public:
DirectoryInterfaceAdapter(std::unique_ptr<fs::fsa::IDirectory> &&dir, std::shared_ptr<FileSystemInterfaceAdapter> &&parent, std::unique_lock<fssystem::SemaphoreAdapter> &&sema);
~DirectoryInterfaceAdapter();
public:
/* Command API */
Result Read(ams::sf::Out<s64> out, const ams::sf::OutBuffer &out_entries);
Result GetEntryCount(ams::sf::Out<s64> out);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(Read),
MAKE_SERVICE_COMMAND_META(GetEntryCount),
};
};
class FileSystemInterfaceAdapter final : std::enable_shared_from_this<FileSystemInterfaceAdapter>, 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<fs::fsa::IFileSystem> base_fs;
std::unique_lock<fssystem::SemaphoreAdapter> mount_count_semaphore;
os::ReadWriteLock invalidation_lock;
bool open_count_limited;
bool deep_retry_enabled = false;
public:
FileSystemInterfaceAdapter(std::shared_ptr<fs::fsa::IFileSystem> &&fs, bool open_limited);
/* TODO: Other constructors. */
~FileSystemInterfaceAdapter();
public:
bool IsDeepRetryEnabled() const;
bool IsAccessFailureDetectionObserved() const;
std::optional<std::shared_lock<os::ReadWriteLock>> 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<u32> out, const fssrv::sf::Path &path);
Result OpenFile(ams::sf::Out<std::shared_ptr<FileInterfaceAdapter>> out, const fssrv::sf::Path &path, u32 mode);
Result OpenDirectory(ams::sf::Out<std::shared_ptr<DirectoryInterfaceAdapter>> out, const fssrv::sf::Path &path, u32 mode);
Result Commit();
Result GetFreeSpaceSize(ams::sf::Out<s64> out, const fssrv::sf::Path &path);
Result GetTotalSpaceSize(ams::sf::Out<s64> out, const fssrv::sf::Path &path);
Result CleanDirectoryRecursively(const fssrv::sf::Path &path);
Result GetFileTimeStampRaw(ams::sf::Out<fs::FileTimeStampRaw> 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),
};
};
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<fs::IStorage> base_storage;
std::unique_lock<fssystem::SemaphoreAdapter> open_count_semaphore;
os::ReadWriteLock invalidation_lock;
/* TODO: DataStorageContext. */
bool deep_retry_enabled = false;
public:
StorageInterfaceAdapter(fs::IStorage *storage);
StorageInterfaceAdapter(std::unique_ptr<fs::IStorage> storage);
explicit StorageInterfaceAdapter(std::shared_ptr<fs::IStorage> &&storage);
/* TODO: Other constructors. */
~StorageInterfaceAdapter();
private:
std::optional<std::shared_lock<os::ReadWriteLock>> 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<s64> out);
Result OperateRange(ams::sf::Out<fs::StorageQueryRangeInfo> 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),
};
};
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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"

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "fssystem_path_resolution_filesystem.hpp"
namespace ams::fssystem {
class DirectoryRedirectionFileSystem : public IPathResolutionFileSystem<DirectoryRedirectionFileSystem> {
NON_COPYABLE(DirectoryRedirectionFileSystem);
private:
using PathResolutionFileSystem = IPathResolutionFileSystem<DirectoryRedirectionFileSystem>;
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::fsa::IFileSystem> fs, const char *before, const char *after);
DirectoryRedirectionFileSystem(std::shared_ptr<fs::fsa::IFileSystem> 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);
};
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<typename Impl>
class IPathResolutionFileSystem : public fs::fsa::IFileSystem {
NON_COPYABLE(IPathResolutionFileSystem);
private:
std::shared_ptr<fs::fsa::IFileSystem> shared_fs;
fs::fsa::IFileSystem *base_fs;
bool unc_preserved;
public:
IPathResolutionFileSystem(std::shared_ptr<fs::fsa::IFileSystem> fs) : shared_fs(std::move(fs)), unc_preserved(false) {
this->base_fs = this->shared_fs.get();
}
IPathResolutionFileSystem(std::shared_ptr<fs::fsa::IFileSystem> 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<Impl*>(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<Impl*>(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<Impl*>(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<Impl*>(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<Impl*>(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<Impl*>(this)->ResolveFullPath(old_full_path, sizeof(old_full_path), old_path));
R_TRY(static_cast<Impl*>(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<Impl*>(this)->ResolveFullPath(old_full_path, sizeof(old_full_path), old_path));
R_TRY(static_cast<Impl*>(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<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path));
return this->base_fs->GetEntryType(out, full_path);
}
virtual Result OpenFileImpl(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) override {
char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path));
return this->base_fs->OpenFile(out_file, full_path, mode);
}
virtual Result OpenDirectoryImpl(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) override {
char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast<Impl*>(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<Impl*>(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<Impl*>(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<Impl*>(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<Impl*>(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<Impl*>(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();
}
};
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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;
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "fssystem_path_resolution_filesystem.hpp"
namespace ams::fssystem {
class SubDirectoryFileSystem : public IPathResolutionFileSystem<SubDirectoryFileSystem> {
NON_COPYABLE(SubDirectoryFileSystem);
private:
using PathResolutionFileSystem = IPathResolutionFileSystem<SubDirectoryFileSystem>;
private:
char *base_path;
size_t base_path_len;
bool unc_preserved;
public:
SubDirectoryFileSystem(std::shared_ptr<fs::fsa::IFileSystem> fs, const char *bp);
SubDirectoryFileSystem(std::shared_ptr<fs::fsa::IFileSystem> 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);
};
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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();
}
};
}

View file

@ -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"

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <atmosphere/common.hpp>
namespace ams::os {
constexpr inline size_t MemoryPageSize = 0x1000;
constexpr inline size_t MemoryBlockUnitSize = 0x200000;
}

View file

@ -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<impl::WaitableObjectList, sizeof(util::IntrusiveListNode), alignof(util::IntrusiveListNode)> 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;
}
};

View file

@ -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{} { /* ... */ }

View file

@ -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();

View file

@ -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];

View file

@ -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)) {

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
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;
}
}
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
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();
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include <stratosphere/fssrv/fssrv_interface_adapters.hpp>
namespace ams::fssrv::impl {
FileInterfaceAdapter::FileInterfaceAdapter(std::unique_ptr<fs::fsa::IFile> &&file, std::shared_ptr<FileSystemInterfaceAdapter> &&parent, std::unique_lock<fssystem::SemaphoreAdapter> &&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<os::ReadWriteLock> scoped_write_lock(this->parent_filesystem->GetReadWriteLockForCacheInvalidation());
this->base_file->OperateRange(nullptr, 0, fs::OperationId::InvalidateCache, 0, std::numeric_limits<s64>::max(), nullptr, 0);
}
Result FileInterfaceAdapter::Read(ams::sf::Out<s64> 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_t>(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<s64> out) {
auto read_lock = this->parent_filesystem->AcquireCacheInvalidationReadLock();
return this->base_file->GetSize(out.GetPointer());
}
Result FileInterfaceAdapter::OperateRange(ams::sf::Out<fs::FileQueryRangeInfo> 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<s32>(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<fs::fsa::IDirectory> &&dir, std::shared_ptr<FileSystemInterfaceAdapter> &&parent, std::unique_lock<fssystem::SemaphoreAdapter> &&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<s64> 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<fs::DirectoryEntry *>(out_entries.GetPointer()), max_num_entries);
}
Result DirectoryInterfaceAdapter::GetEntryCount(ams::sf::Out<s64> out) {
auto read_lock = this->parent_filesystem->AcquireCacheInvalidationReadLock();
return this->base_dir->GetEntryCount(out.GetPointer());
}
FileSystemInterfaceAdapter::FileSystemInterfaceAdapter(std::shared_ptr<fs::fsa::IFileSystem> &&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<std::shared_lock<os::ReadWriteLock>> FileSystemInterfaceAdapter::AcquireCacheInvalidationReadLock() {
std::optional<std::shared_lock<os::ReadWriteLock>> 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<u32> 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<fs::DirectoryEntryType *>(out.GetPointer()), normalizer.GetPath());
}
Result FileSystemInterfaceAdapter::OpenFile(ams::sf::Out<std::shared_ptr<FileInterfaceAdapter>> out, const fssrv::sf::Path &path, u32 mode) {
auto read_lock = this->AcquireCacheInvalidationReadLock();
std::unique_lock<fssystem::SemaphoreAdapter> 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<fs::fsa::IFile> file;
R_TRY(this->base_fs->OpenFile(&file, normalizer.GetPath(), static_cast<fs::OpenMode>(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<FileSystemInterfaceAdapter> shared_this = this->shared_from_this();
std::shared_ptr<FileInterfaceAdapter> file_intf = std::make_shared<FileInterfaceAdapter>(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<std::shared_ptr<DirectoryInterfaceAdapter>> out, const fssrv::sf::Path &path, u32 mode) {
auto read_lock = this->AcquireCacheInvalidationReadLock();
std::unique_lock<fssystem::SemaphoreAdapter> 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<fs::fsa::IDirectory> dir;
R_TRY(this->base_fs->OpenDirectory(&dir, normalizer.GetPath(), static_cast<fs::OpenDirectoryMode>(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<FileSystemInterfaceAdapter> shared_this = this->shared_from_this();
std::shared_ptr<DirectoryInterfaceAdapter> dir_intf = std::make_shared<DirectoryInterfaceAdapter>(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<s64> 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<s64> 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<fs::FileTimeStampRaw> 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<const char *>(in_buf.GetPointer());
return this->base_fs->QueryEntry(dst, out_buf.GetSize(), src, in_buf.GetSize(), static_cast<fs::fsa::QueryId>(query_id), path.str);
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
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<char[]>(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();
}
}

View file

@ -13,45 +13,76 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "fsmitm_istorage_interface.hpp"
#include <stratosphere.hpp>
#include <stratosphere/fssrv/fssrv_interface_adapters.hpp>
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<fs::IStorage> storage) : base_storage(storage.release()) {
/* ... */
}
StorageInterfaceAdapter::StorageInterfaceAdapter(std::shared_ptr<fs::IStorage> &&storage) : base_storage(std::move(storage)) {
/* ... */
}
StorageInterfaceAdapter::~StorageInterfaceAdapter() {
/* ... */
}
std::optional<std::shared_lock<os::ReadWriteLock>> StorageInterfaceAdapter::AcquireCacheInvalidationReadLock() {
std::optional<std::shared_lock<os::ReadWriteLock>> 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<s64> out) {
Result StorageInterfaceAdapter::GetSize(ams::sf::Out<s64> out) {
auto read_lock = this->AcquireCacheInvalidationReadLock();
return this->base_storage->GetSize(out.GetPointer());
}
Result IStorageInterface::OperateRange(sf::Out<StorageQueryRangeInfo> out, s32 op_id, s64 offset, s64 size) {
Result StorageInterfaceAdapter::OperateRange(ams::sf::Out<fs::StorageQueryRangeInfo> 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<s32>(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);

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::fssystem {
DirectoryRedirectionFileSystem::DirectoryRedirectionFileSystem(std::shared_ptr<fs::fsa::IFileSystem> 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::fsa::IFileSystem> 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<char *>(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();
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::fssystem {
SubDirectoryFileSystem::SubDirectoryFileSystem(std::shared_ptr<fs::fsa::IFileSystem> fs, const char *bp) : PathResolutionFileSystem(fs) {
this->base_path = nullptr;
R_ASSERT(this->Initialize(bp));
}
SubDirectoryFileSystem::SubDirectoryFileSystem(std::shared_ptr<fs::fsa::IFileSystem> 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<char *>(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());
}
}

View file

@ -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);

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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);
}
};
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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();
}
}

View file

@ -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) {

View file

@ -363,7 +363,7 @@ namespace ams::spl::smc {
Result AtmosphereGetEmummcConfig(void *out_config, void *out_paths, u32 storage_id) {
const u64 paths = reinterpret_cast<u64>(out_paths);
AMS_ASSERT(util::IsAligned(paths, 0x1000));
AMS_ASSERT(util::IsAligned(paths, os::MemoryPageSize));
SecmonArgs args = {};
args.X[0] = static_cast<u64>(FunctionId::AtmosphereGetEmummcConfig);

View file

@ -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();
}

View file

@ -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<uintptr_t>(work_buffer), 0x1000));
AMS_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(work_buffer), os::MemoryPageSize));
AMS_ASSERT(util::IsAligned(work_buffer_size, 0x200));
R_TRY(this->accessor.Initialize());

View file

@ -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);
}

View file

@ -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. */

View file

@ -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);
}

View file

@ -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;

View file

@ -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);

View file

@ -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<uintptr_t>(src);
const uintptr_t dst_addr = reinterpret_cast<uintptr_t>(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);

View file

@ -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);
}