ams: support building unit test programs on windows/linux/macos

This commit is contained in:
Michael Scire
2022-03-06 12:08:20 -08:00
committed by SciresM
parent 9a38be201a
commit 64a97576d0
756 changed files with 33359 additions and 9372 deletions

View File

@@ -15,7 +15,7 @@
*/
#define NX_SERVICE_ASSUME_NON_DOMAIN
#include "../service_guard.h"
#include "ams_bpc.h"
#include "ams_bpc.os.horizon.h"
static Service g_amsBpcSrv;

View File

@@ -58,7 +58,7 @@ namespace ams::emummc {
/* Globals. */
constinit os::SdkMutex g_lock;
constinit ExosphereConfig g_exo_config;
constinit ExosphereConfig g_exo_config = {};
constinit bool g_is_emummc;
constinit bool g_has_cached;
@@ -87,15 +87,23 @@ namespace ams::emummc {
g_is_emummc = g_exo_config.base_cfg.magic == StorageMagic && storage != Storage_Emmc;
/* Format paths. */
if (storage == Storage_SdFile) {
util::SNPrintf(g_exo_config.file_cfg.path, sizeof(g_exo_config.file_cfg.path), "/%s", paths->file_path);
}
{
char tmp_path[MaxDirLen + 1];
util::SNPrintf(g_exo_config.emu_dir_path, sizeof(g_exo_config.emu_dir_path), "/%s", paths->nintendo_path);
/* Format paths. */
if (storage == Storage_SdFile) {
util::TSNPrintf(tmp_path, sizeof(tmp_path), "/%s", paths->file_path);
R_ABORT_UNLESS(fs::PathFormatter::Normalize(g_exo_config.file_cfg.path, sizeof(g_exo_config.file_cfg.path), tmp_path, std::strlen(tmp_path) + 1, fs::PathFlags{}));
}
/* If we're emummc, implement default nintendo redirection path. */
if (g_is_emummc && std::strcmp(g_exo_config.emu_dir_path, "/") == 0) {
util::SNPrintf(g_exo_config.emu_dir_path, sizeof(g_exo_config.emu_dir_path), "/emummc/Nintendo_%04x", g_exo_config.base_cfg.id);
util::TSNPrintf(tmp_path, sizeof(tmp_path), "/%s", paths->nintendo_path);
R_ABORT_UNLESS(fs::PathFormatter::Normalize(g_exo_config.emu_dir_path, sizeof(g_exo_config.emu_dir_path), tmp_path, std::strlen(tmp_path) + 1, fs::PathFlags{}));
/* If we're emummc, implement default nintendo redirection path. */
if (g_is_emummc && std::strcmp(g_exo_config.emu_dir_path, "/") == 0) {
util::TSNPrintf(tmp_path, sizeof(tmp_path), "/emummc/Nintendo_%04x", g_exo_config.base_cfg.id);
R_ABORT_UNLESS(fs::PathFormatter::Normalize(g_exo_config.emu_dir_path, sizeof(g_exo_config.emu_dir_path), tmp_path, std::strlen(tmp_path) + 1, fs::PathFlags{}));
}
}
}

View File

@@ -14,12 +14,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "ams_bpc.h"
#include "ams_bpc.os.horizon.h"
namespace ams {
namespace {
#if defined(ATMOSPHERE_ARCH_ARM64)
inline u64 GetPc() {
u64 pc;
__asm__ __volatile__ ("adr %[pc], ." : [pc]"=&r"(pc) :: );
@@ -30,6 +31,9 @@ namespace ams {
u64 fp;
u64 lr;
};
#else
#error "Unknown architecture for os Horizon"
#endif
}

View File

@@ -45,6 +45,7 @@ namespace ams {
}
NOINLINE NORETURN void AbortImpl() {
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
/* Just perform a data abort. */
register u64 addr __asm__("x27") = FatalErrorContext::StdAbortMagicAddress;
register u64 val __asm__("x28") = FatalErrorContext::StdAbortMagicValue;
@@ -55,6 +56,10 @@ namespace ams {
: [val]"r"(val), [addr]"r"(addr)
);
}
#else
/* What should be done here? */
AMS_INFINITE_LOOP();
#endif
__builtin_unreachable();
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 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 {
namespace os {
void Initialize();
}
void *Malloc(size_t size) {
return std::malloc(size);
}
void Free(void *ptr) {
return std::free(ptr);
}
void *MallocForRapidJson(size_t size) {
return std::malloc(size);
}
void *ReallocForRapidJson(void *ptr, size_t size) {
return std::realloc(ptr, size);
}
void FreeForRapidJson(void *ptr) {
return std::free(ptr);
}
NORETURN void AbortImpl() {
std::abort();
}
}
extern "C" {
/* Redefine C++ exception handlers. Requires wrap linker flag. */
#define WRAP_ABORT_FUNC(func) void NORETURN __wrap_##func(void) { std::abort(); __builtin_unreachable(); }
WRAP_ABORT_FUNC(__cxa_pure_virtual)
#undef WRAP_ABORT_FUNC
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 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 {
namespace os {
void Initialize();
}
void *Malloc(size_t size) {
return std::malloc(size);
}
void Free(void *ptr) {
return std::free(ptr);
}
void *MallocForRapidJson(size_t size) {
return std::malloc(size);
}
void *ReallocForRapidJson(void *ptr, size_t size) {
return std::realloc(ptr, size);
}
void FreeForRapidJson(void *ptr) {
return std::free(ptr);
}
NORETURN void AbortImpl() {
std::abort();
}
}
extern "C" {
/* Redefine C++ exception handlers. Requires wrap linker flag. */
#define WRAP_ABORT_FUNC(func) void NORETURN __wrap_##func(void) { std::abort(); __builtin_unreachable(); }
WRAP_ABORT_FUNC(__cxa_pure_virtual)
#undef WRAP_ABORT_FUNC
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 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>
extern "C" char **__real___p__acmdln(void);
namespace ams {
namespace os {
void Initialize();
}
void *Malloc(size_t size) {
return std::malloc(size);
}
void Free(void *ptr) {
return std::free(ptr);
}
void *MallocForRapidJson(size_t size) {
return std::malloc(size);
}
void *ReallocForRapidJson(void *ptr, size_t size) {
return std::realloc(ptr, size);
}
void FreeForRapidJson(void *ptr) {
return std::free(ptr);
}
NORETURN void AbortImpl() {
std::abort();
}
}
extern "C" {
/* Redefine C++ exception handlers. Requires wrap linker flag. */
#define WRAP_ABORT_FUNC(func) void NORETURN __wrap_##func(void) { std::abort(); __builtin_unreachable(); }
WRAP_ABORT_FUNC(__cxa_pure_virtual)
#undef WRAP_ABORT_FUNC
/* On windows, mingw may attempt to call malloc before we've initialized globals to set up the command line. */
/* We perform some critical init here, to make that work. */
char **__wrap___p__acmdln(void) {
::ams::os::Initialize();
return __real___p__acmdln();
}
}

View File

@@ -26,6 +26,7 @@ namespace ams::exosphere {
return ApiInfo{ util::BitPack64{exosphere_cfg} };
}
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
void ForceRebootToRcm() {
R_ABORT_UNLESS(spl::impl::SetConfig(spl::ConfigItem::ExosphereNeedsReboot, 1));
}
@@ -81,5 +82,6 @@ namespace ams::exosphere {
R_ABORT_UNLESS(spl::impl::GetConfig(std::addressof(device_id), spl::ConfigItem::DeviceId));
return device_id;
}
#endif
}

View File

@@ -162,7 +162,7 @@ namespace ams::boot2 {
}
bool GetGpioPadLow(DeviceCode device_code) {
gpio::GpioPadSession button;
gpio::GpioPadSession button{};
if (R_FAILED(gpio::OpenSession(std::addressof(button), device_code))) {
return false;
}
@@ -255,7 +255,7 @@ namespace ams::boot2 {
/* Read the mitm list off the SD card. */
{
char path[fs::EntryNameLengthMax];
util::SNPrintf(path, sizeof(path), "sdmc:/atmosphere/contents/%016lx/mitm.lst", static_cast<u64>(program_id));
util::SNPrintf(path, sizeof(path), "sdmc:/atmosphere/contents/%016" PRIx64 "/mitm.lst", static_cast<u64>(program_id));
fs::FileHandle f;
if (R_FAILED(fs::OpenFile(std::addressof(f), path, fs::OpenMode_Read))) {

View File

@@ -18,18 +18,32 @@
namespace ams::capsrv {
Result InitializeScreenShotControl() {
#if defined(ATMOSPHERE_OS_HORIZON)
return ::capsscInitialize();
#else
AMS_ABORT("TODO");
#endif
}
void FinalizeScreenShotControl() {
#if defined(ATMOSPHERE_OS_HORIZON)
return ::capsscExit();
#else
AMS_ABORT("TODO");
#endif
}
Result CaptureJpegScreenshot(u64 *out_size, void *dst, size_t dst_size, vi::LayerStack layer_stack, TimeSpan timeout) {
#if defined(ATMOSPHERE_OS_HORIZON)
return ::capsscCaptureJpegScreenShot(out_size, dst, dst_size, static_cast<::ViLayerStack>(layer_stack), timeout.GetNanoSeconds());
#else
AMS_UNUSED(out_size, dst, dst_size, layer_stack, timeout);
AMS_ABORT("TODO");
#endif
}
Result OpenRawScreenShotReadStreamForDevelop(size_t *out_data_size, s32 *out_width, s32 *out_height, vi::LayerStack layer_stack, TimeSpan timeout) {
#if defined(ATMOSPHERE_OS_HORIZON)
u64 data_size, width, height;
R_TRY(::capsscOpenRawScreenShotReadStream(std::addressof(data_size), std::addressof(width), std::addressof(height), static_cast<::ViLayerStack>(layer_stack), timeout.GetNanoSeconds()));
@@ -38,18 +52,32 @@ namespace ams::capsrv {
*out_height = static_cast<s32>(height);
return ResultSuccess();
#else
AMS_UNUSED(out_data_size, out_width, out_height, layer_stack, timeout);
AMS_ABORT("TODO");
#endif
}
Result ReadRawScreenShotReadStreamForDevelop(size_t *out_read_size, void *dst, size_t dst_size, std::ptrdiff_t offset) {
#if defined(ATMOSPHERE_OS_HORIZON)
u64 read_size;
R_TRY(::capsscReadRawScreenShotReadStream(std::addressof(read_size), dst, dst_size, static_cast<u64>(offset)));
*out_read_size = static_cast<size_t>(read_size);
return ResultSuccess();
#else
AMS_UNUSED(out_read_size, dst, dst_size, offset);
AMS_ABORT("TODO");
#endif
}
void CloseRawScreenShotReadStreamForDevelop() {
#if defined(ATMOSPHERE_OS_HORIZON)
::capsscCloseRawScreenShotReadStream();
#else
AMS_UNUSED();
AMS_ABORT("TODO");
#endif
}
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright (c) 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::cfg {
namespace {
std::atomic<u32> g_flag_mount_count;
/* Helper. */
void GetFlagMountName(char *dst) {
util::SNPrintf(dst, fs::MountNameLengthMax + 1, "#flag%08x", g_flag_mount_count.fetch_add(1));
}
bool HasFlagFile(const char *flag_path) {
/* All flags are not present until the SD card is. */
if (!IsSdCardInitialized()) {
return false;
}
/* Mount the SD card. */
char mount_name[fs::MountNameLengthMax + 1];
GetFlagMountName(mount_name);
if (R_FAILED(fs::MountSdCard(mount_name))) {
return false;
}
ON_SCOPE_EXIT { fs::Unmount(mount_name); };
/* Check if the entry exists. */
char full_path[fs::EntryNameLengthMax + 1];
util::SNPrintf(full_path, sizeof(full_path), "%s:/%s", mount_name, flag_path[0] == '/' ? flag_path + 1 : flag_path);
bool has_file;
if (R_FAILED(fs::HasFile(std::addressof(has_file), full_path))) {
return false;
}
return has_file;
}
Result DeleteFlagFile(const char *flag_path) {
/* We need the SD card to be available to delete anything. */
AMS_ABORT_UNLESS(IsSdCardInitialized());
/* Mount the sd card. */
char mount_name[fs::MountNameLengthMax + 1];
GetFlagMountName(mount_name);
R_TRY(fs::MountSdCard(mount_name));
ON_SCOPE_EXIT { fs::Unmount(mount_name); };
/* Get the flag path. */
char full_path[fs::EntryNameLengthMax + 1];
util::SNPrintf(full_path, sizeof(full_path), "%s:/%s", mount_name, flag_path[0] == '/' ? flag_path + 1 : flag_path);
/* Delete the file. */
R_TRY(fs::DeleteFile(full_path));
return ResultSuccess();
}
}
/* Flag utilities. */
bool HasFlag(const sm::MitmProcessInfo &process_info, const char *flag) {
return HasContentSpecificFlag(process_info.program_id, flag) || (process_info.override_status.IsHbl() && HasHblFlag(flag));
}
bool HasContentSpecificFlag(ncm::ProgramId program_id, const char *flag) {
char content_flag[fs::EntryNameLengthMax + 1];
util::SNPrintf(content_flag, sizeof(content_flag) - 1, "/atmosphere/contents/%016" PRIx64 "/flags/%s.flag", program_id.value, flag);
return HasFlagFile(content_flag);
}
bool HasGlobalFlag(const char *flag) {
char global_flag[fs::EntryNameLengthMax + 1];
util::SNPrintf(global_flag, sizeof(global_flag) - 1, "/atmosphere/flags/%s.flag", flag);
return HasFlagFile(global_flag);
}
bool HasHblFlag(const char *flag) {
char hbl_flag[0x100];
util::SNPrintf(hbl_flag, sizeof(hbl_flag) - 1, "hbl_%s", flag);
return HasGlobalFlag(hbl_flag);
}
Result DeleteGlobalFlag(const char *flag) {
char global_flag[fs::EntryNameLengthMax + 1];
util::SNPrintf(global_flag, sizeof(global_flag) - 1, "/atmosphere/flags/%s.flag", flag);
return DeleteFlagFile(global_flag);
}
}

View File

@@ -15,92 +15,8 @@
*/
#include <stratosphere.hpp>
namespace ams::cfg {
namespace {
std::atomic<u32> g_flag_mount_count;
/* Helper. */
void GetFlagMountName(char *dst) {
util::SNPrintf(dst, fs::MountNameLengthMax + 1, "#flag%08x", g_flag_mount_count.fetch_add(1));
}
bool HasFlagFile(const char *flag_path) {
/* All flags are not present until the SD card is. */
if (!IsSdCardInitialized()) {
return false;
}
/* Mount the SD card. */
char mount_name[fs::MountNameLengthMax + 1];
GetFlagMountName(mount_name);
if (R_FAILED(fs::MountSdCard(mount_name))) {
return false;
}
ON_SCOPE_EXIT { fs::Unmount(mount_name); };
/* Check if the entry exists. */
char full_path[fs::EntryNameLengthMax + 1];
util::SNPrintf(full_path, sizeof(full_path), "%s:/%s", mount_name, flag_path[0] == '/' ? flag_path + 1 : flag_path);
bool has_file;
if (R_FAILED(fs::HasFile(std::addressof(has_file), full_path))) {
return false;
}
return has_file;
}
Result DeleteFlagFile(const char *flag_path) {
/* We need the SD card to be available to delete anything. */
AMS_ABORT_UNLESS(IsSdCardInitialized());
/* Mount the sd card. */
char mount_name[fs::MountNameLengthMax + 1];
GetFlagMountName(mount_name);
R_TRY(fs::MountSdCard(mount_name));
ON_SCOPE_EXIT { fs::Unmount(mount_name); };
/* Get the flag path. */
char full_path[fs::EntryNameLengthMax + 1];
util::SNPrintf(full_path, sizeof(full_path), "%s:/%s", mount_name, flag_path[0] == '/' ? flag_path + 1 : flag_path);
/* Delete the file. */
R_TRY(fs::DeleteFile(full_path));
return ResultSuccess();
}
}
/* Flag utilities. */
bool HasFlag(const sm::MitmProcessInfo &process_info, const char *flag) {
return HasContentSpecificFlag(process_info.program_id, flag) || (process_info.override_status.IsHbl() && HasHblFlag(flag));
}
bool HasContentSpecificFlag(ncm::ProgramId program_id, const char *flag) {
char content_flag[fs::EntryNameLengthMax + 1];
util::SNPrintf(content_flag, sizeof(content_flag) - 1, "/atmosphere/contents/%016lx/flags/%s.flag", static_cast<u64>(program_id), flag);
return HasFlagFile(content_flag);
}
bool HasGlobalFlag(const char *flag) {
char global_flag[fs::EntryNameLengthMax + 1];
util::SNPrintf(global_flag, sizeof(global_flag) - 1, "/atmosphere/flags/%s.flag", flag);
return HasFlagFile(global_flag);
}
bool HasHblFlag(const char *flag) {
char hbl_flag[0x100];
util::SNPrintf(hbl_flag, sizeof(hbl_flag) - 1, "hbl_%s", flag);
return HasGlobalFlag(hbl_flag);
}
Result DeleteGlobalFlag(const char *flag) {
char global_flag[fs::EntryNameLengthMax + 1];
util::SNPrintf(global_flag, sizeof(global_flag) - 1, "/atmosphere/flags/%s.flag", flag);
return DeleteFlagFile(global_flag);
}
}
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
#include "cfg_flags.board.nintendo_nx.inc"
#else
#include "cfg_flags.generic.inc"
#endif

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 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::cfg {
/* Flag utilities. */
bool HasFlag(const sm::MitmProcessInfo &process_info, const char *flag) {
AMS_UNUSED(process_info, flag);
return false;
}
bool HasContentSpecificFlag(ncm::ProgramId program_id, const char *flag) {
AMS_UNUSED(program_id, flag);
return false;
}
bool HasGlobalFlag(const char *flag) {
AMS_UNUSED(flag);
return false;
}
bool HasHblFlag(const char *flag) {
AMS_UNUSED(flag);
return false;
}
Result DeleteGlobalFlag(const char *flag) {
AMS_UNUSED(flag);
return false;
}
}

View File

@@ -0,0 +1,443 @@
/*
* Copyright (c) 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::cfg {
namespace {
/* Types. */
struct OverrideKey {
u64 key_combination;
bool override_by_default;
};
struct ProgramOverrideKey {
OverrideKey override_key;
ncm::ProgramId program_id;
};
constexpr ProgramOverrideKey InvalidProgramOverrideKey = {};
constexpr ProgramOverrideKey DefaultAppletPhotoViewerOverrideKey = {
.override_key = {
.key_combination = HidNpadButton_R,
.override_by_default = true,
},
.program_id = ncm::SystemAppletId::PhotoViewer,
};
constexpr size_t MaxProgramOverrideKeys = 8;
struct HblOverrideConfig {
ProgramOverrideKey program_configs[MaxProgramOverrideKeys];
impl::OverrideStatusFlag program_as_flags[MaxProgramOverrideKeys];
OverrideKey override_any_app_key;
impl::OverrideStatusFlag override_any_app_as_flag;
bool override_any_app;
};
struct ContentSpecificOverrideConfig {
OverrideKey override_key;
OverrideKey cheat_enable_key;
OverrideLocale locale;
};
/* Override globals. */
OverrideKey g_default_override_key = {
.key_combination = HidNpadButton_L,
.override_by_default = true,
};
OverrideKey g_default_cheat_enable_key = {
.key_combination = HidNpadButton_L,
.override_by_default = true,
};
HblOverrideConfig g_hbl_override_config = {
.program_configs = {
DefaultAppletPhotoViewerOverrideKey,
InvalidProgramOverrideKey,
InvalidProgramOverrideKey,
InvalidProgramOverrideKey,
InvalidProgramOverrideKey,
InvalidProgramOverrideKey,
InvalidProgramOverrideKey,
InvalidProgramOverrideKey,
},
.program_as_flags = {
impl::OverrideStatusFlag_AddressSpace64Bit,
impl::OverrideStatusFlag_AddressSpace64Bit,
impl::OverrideStatusFlag_AddressSpace64Bit,
impl::OverrideStatusFlag_AddressSpace64Bit,
impl::OverrideStatusFlag_AddressSpace64Bit,
impl::OverrideStatusFlag_AddressSpace64Bit,
impl::OverrideStatusFlag_AddressSpace64Bit,
impl::OverrideStatusFlag_AddressSpace64Bit,
},
.override_any_app_key = {
.key_combination = HidNpadButton_R,
.override_by_default = false,
},
.override_any_app_as_flag = impl::OverrideStatusFlag_AddressSpace64Bit,
.override_any_app = true,
};
char g_hbl_sd_path[0x100] = "/atmosphere/hbl.nsp";
/* Helpers. */
OverrideKey ParseOverrideKey(const char *value) {
OverrideKey cfg = {};
/* Parse on by default. */
if (value[0] == '!') {
cfg.override_by_default = true;
value++;
}
/* Parse key combination. */
if (strcasecmp(value, "A") == 0) {
cfg.key_combination = HidNpadButton_A;
} else if (strcasecmp(value, "B") == 0) {
cfg.key_combination = HidNpadButton_B;
} else if (strcasecmp(value, "X") == 0) {
cfg.key_combination = HidNpadButton_X;
} else if (strcasecmp(value, "Y") == 0) {
cfg.key_combination = HidNpadButton_Y;
} else if (strcasecmp(value, "LS") == 0) {
cfg.key_combination = HidNpadButton_StickL;
} else if (strcasecmp(value, "RS") == 0) {
cfg.key_combination = HidNpadButton_StickR;
} else if (strcasecmp(value, "L") == 0) {
cfg.key_combination = HidNpadButton_L;
} else if (strcasecmp(value, "R") == 0) {
cfg.key_combination = HidNpadButton_R;
} else if (strcasecmp(value, "ZL") == 0) {
cfg.key_combination = HidNpadButton_ZL;
} else if (strcasecmp(value, "ZR") == 0) {
cfg.key_combination = HidNpadButton_ZR;
} else if (strcasecmp(value, "PLUS") == 0) {
cfg.key_combination = HidNpadButton_Plus;
} else if (strcasecmp(value, "MINUS") == 0) {
cfg.key_combination = HidNpadButton_Minus;
} else if (strcasecmp(value, "DLEFT") == 0) {
cfg.key_combination = HidNpadButton_Left;
} else if (strcasecmp(value, "DUP") == 0) {
cfg.key_combination = HidNpadButton_Up;
} else if (strcasecmp(value, "DRIGHT") == 0) {
cfg.key_combination = HidNpadButton_Right;
} else if (strcasecmp(value, "DDOWN") == 0) {
cfg.key_combination = HidNpadButton_Down;
} else if (strcasecmp(value, "SL") == 0) {
cfg.key_combination = HidNpadButton_AnySL;
} else if (strcasecmp(value, "SR") == 0) {
cfg.key_combination = HidNpadButton_AnySR;
}
return cfg;
}
impl::OverrideStatusFlag ParseOverrideAddressSpace(const char *value) {
if (strcasecmp(value, "39_bit") == 0 || strcasecmp(value, "39") == 0) {
return impl::OverrideStatusFlag_AddressSpace64Bit;
} else if (strcasecmp(value, "36_bit") == 0 || strcasecmp(value, "36") == 0) {
return impl::OverrideStatusFlag_AddressSpace64BitDeprecated;
} else if (strcasecmp(value, "32_bit") == 0 || strcasecmp(value, "32") == 0) {
return impl::OverrideStatusFlag_AddressSpace32Bit;
} else if (strcasecmp(value, "32_bit_without_alias") == 0 ||
strcasecmp(value, "32_bit_no_alias") == 0 ||
strcasecmp(value, "32_without_alias") == 0 ||
strcasecmp(value, "32_no_alias") ||
strcasecmp(value, "32_bit_without_map") == 0 ||
strcasecmp(value, "32_bit_no_map") == 0 ||
strcasecmp(value, "32_without_map") == 0 ||
strcasecmp(value, "32_no_map") == 0)
{
return impl::OverrideStatusFlag_AddressSpace32BitWithoutAlias;
} else {
/* Default to 39-bit. */
return impl::OverrideStatusFlag_AddressSpace64Bit;
}
}
inline void SetHblSpecificProgramId(size_t i, const char *value) {
g_hbl_override_config.program_configs[i].program_id = {strtoul(value, nullptr, 16)};
}
inline void SetHblSpecificOverrideKey(size_t i, const char *value) {
g_hbl_override_config.program_configs[i].override_key = ParseOverrideKey(value);
}
inline void SetHblSpecificAddressSpace(size_t i, const char *value) {
g_hbl_override_config.program_as_flags[i] = ParseOverrideAddressSpace(value);
}
int OverrideConfigIniHandler(void *user, const char *section, const char *name, const char *value) {
AMS_UNUSED(user);
/* Taken and modified, with love, from Rajkosto's implementation. */
if (strcasecmp(section, "hbl_config") == 0) {
if (strcasecmp(name, "program_id") == 0 || strcasecmp(name, "program_id_0") == 0) {
SetHblSpecificProgramId(0, value);
} else if (strcasecmp(name, "program_id_1") == 0) {
SetHblSpecificProgramId(1, value);
} else if (strcasecmp(name, "program_id_2") == 0) {
SetHblSpecificProgramId(2, value);
} else if (strcasecmp(name, "program_id_3") == 0) {
SetHblSpecificProgramId(3, value);
} else if (strcasecmp(name, "program_id_4") == 0) {
SetHblSpecificProgramId(4, value);
} else if (strcasecmp(name, "program_id_5") == 0) {
SetHblSpecificProgramId(5, value);
} else if (strcasecmp(name, "program_id_6") == 0) {
SetHblSpecificProgramId(6, value);
} else if (strcasecmp(name, "program_id_7") == 0) {
SetHblSpecificProgramId(7, value);
} else if (strcasecmp(name, "override_key") == 0 || strcasecmp(name, "override_key_0") == 0) {
SetHblSpecificOverrideKey(0, value);
} else if (strcasecmp(name, "override_key_1") == 0) {
SetHblSpecificOverrideKey(1, value);
} else if (strcasecmp(name, "override_key_2") == 0) {
SetHblSpecificOverrideKey(2, value);
} else if (strcasecmp(name, "override_key_3") == 0) {
SetHblSpecificOverrideKey(3, value);
} else if (strcasecmp(name, "override_key_4") == 0) {
SetHblSpecificOverrideKey(4, value);
} else if (strcasecmp(name, "override_key_5") == 0) {
SetHblSpecificOverrideKey(5, value);
} else if (strcasecmp(name, "override_key_6") == 0) {
SetHblSpecificOverrideKey(6, value);
} else if (strcasecmp(name, "override_key_7") == 0) {
SetHblSpecificOverrideKey(7, value);
} else if (strcasecmp(name, "override_address_space") == 0 || strcasecmp(name, "override_address_space_0") == 0) {
SetHblSpecificAddressSpace(0, value);
} else if (strcasecmp(name, "override_address_space_1") == 0) {
SetHblSpecificAddressSpace(1, value);
} else if (strcasecmp(name, "override_address_space_2") == 0) {
SetHblSpecificAddressSpace(2, value);
} else if (strcasecmp(name, "override_address_space_3") == 0) {
SetHblSpecificAddressSpace(3, value);
} else if (strcasecmp(name, "override_address_space_4") == 0) {
SetHblSpecificAddressSpace(4, value);
} else if (strcasecmp(name, "override_address_space_5") == 0) {
SetHblSpecificAddressSpace(5, value);
} else if (strcasecmp(name, "override_address_space_6") == 0) {
SetHblSpecificAddressSpace(6, value);
} else if (strcasecmp(name, "override_address_space_7") == 0) {
SetHblSpecificAddressSpace(7, value);
} else if (strcasecmp(name, "override_any_app") == 0) {
if (strcasecmp(value, "true") == 0 || strcasecmp(value, "1") == 0) {
g_hbl_override_config.override_any_app = true;
} else if (strcasecmp(value, "false") == 0 || strcasecmp(value, "0") == 0) {
g_hbl_override_config.override_any_app = false;
} else {
/* I guess we default to not changing the value? */
}
} else if (strcasecmp(name, "override_any_app_key") == 0) {
g_hbl_override_config.override_any_app_key = ParseOverrideKey(value);
} else if (strcasecmp(name, "override_any_app_address_space") == 0) {
g_hbl_override_config.override_any_app_as_flag = ParseOverrideAddressSpace(value);
} else if (strcasecmp(name, "path") == 0) {
while (*value == '/' || *value == '\\') {
value++;
}
util::SNPrintf(g_hbl_sd_path, sizeof(g_hbl_sd_path) - 1, "/%s", value);
g_hbl_sd_path[sizeof(g_hbl_sd_path) - 1] = '\0';
for (size_t i = 0; i < sizeof(g_hbl_sd_path); i++) {
if (g_hbl_sd_path[i] == '\\') {
g_hbl_sd_path[i] = '/';
}
}
}
} else if (strcasecmp(section, "default_config") == 0) {
if (strcasecmp(name, "override_key") == 0) {
g_default_override_key = ParseOverrideKey(value);
} else if (strcasecmp(name, "cheat_enable_key") == 0) {
g_default_cheat_enable_key = ParseOverrideKey(value);
}
} else {
return 0;
}
return 1;
}
int ContentSpecificIniHandler(void *user, const char *section, const char *name, const char *value) {
ContentSpecificOverrideConfig *config = reinterpret_cast<ContentSpecificOverrideConfig *>(user);
if (strcasecmp(section, "override_config") == 0) {
if (strcasecmp(name, "override_key") == 0) {
config->override_key = ParseOverrideKey(value);
} else if (strcasecmp(name, "cheat_enable_key") == 0) {
config->cheat_enable_key = ParseOverrideKey(value);
} else if (strcasecmp(name, "override_language") == 0) {
config->locale.language_code = settings::LanguageCode::Encode(value);
} else if (strcasecmp(name, "override_region") == 0) {
if (strcasecmp(value, "jpn") == 0) {
config->locale.region_code = settings::RegionCode_Japan;
} else if (strcasecmp(value, "usa") == 0) {
config->locale.region_code = settings::RegionCode_America;
} else if (strcasecmp(value, "eur") == 0) {
config->locale.region_code = settings::RegionCode_Europe;
} else if (strcasecmp(value, "aus") == 0) {
config->locale.region_code = settings::RegionCode_Australia;
} else if (strcasecmp(value, "chn") == 0) {
config->locale.region_code = settings::RegionCode_China;
} else if (strcasecmp(value, "kor") == 0) {
config->locale.region_code = settings::RegionCode_Korea;
} else if (strcasecmp(value, "twn") == 0) {
config->locale.region_code = settings::RegionCode_Taiwan;
}
}
} else {
return 0;
}
return 1;
}
constexpr inline bool IsOverrideMatch(const OverrideStatus &status, const OverrideKey &cfg) {
bool keys_triggered = ((status.keys_held & cfg.key_combination) != 0);
return (cfg.override_by_default ^ keys_triggered);
}
inline bool IsAnySpecificHblProgramId(ncm::ProgramId program_id) {
for (size_t i = 0; i < MaxProgramOverrideKeys; i++) {
if (program_id == g_hbl_override_config.program_configs[i].program_id) {
return true;
}
}
return false;
}
inline bool IsSpecificHblProgramId(size_t i, ncm::ProgramId program_id) {
return program_id == g_hbl_override_config.program_configs[i].program_id;
}
inline bool IsAnyApplicationHblProgramId(ncm::ProgramId program_id) {
return g_hbl_override_config.override_any_app && ncm::IsApplicationId(program_id) && !IsAnySpecificHblProgramId(program_id);
}
std::atomic<u32> g_ini_mount_count;
void GetIniMountName(char *dst) {
util::SNPrintf(dst, fs::MountNameLengthMax + 1, "#ini%08x", g_ini_mount_count.fetch_add(1));
}
void ParseIniFile(util::ini::Handler handler, const char *path, void *user_ctx) {
/* Mount the SD card. */
char mount_name[fs::MountNameLengthMax + 1];
GetIniMountName(mount_name);
if (R_FAILED(fs::MountSdCard(mount_name))) {
return;
}
ON_SCOPE_EXIT { fs::Unmount(mount_name); };
/* Open the file. */
fs::FileHandle file;
{
char full_path[fs::EntryNameLengthMax + 1];
util::SNPrintf(full_path, sizeof(full_path), "%s:/%s", mount_name, path[0] == '/' ? path + 1 : path);
if (R_FAILED(fs::OpenFile(std::addressof(file), full_path, fs::OpenMode_Read))) {
return;
}
}
ON_SCOPE_EXIT { fs::CloseFile(file); };
/* Parse the config. */
util::ini::ParseFile(file, user_ctx, handler);
}
void RefreshOverrideConfiguration() {
ParseIniFile(OverrideConfigIniHandler, "/atmosphere/config/override_config.ini", nullptr);
}
ContentSpecificOverrideConfig GetContentOverrideConfig(ncm::ProgramId program_id) {
char path[fs::EntryNameLengthMax + 1];
util::SNPrintf(path, sizeof(path), "/atmosphere/contents/%016" PRIx64 "/config.ini", program_id.value);
ContentSpecificOverrideConfig config = {
.override_key = g_default_override_key,
.cheat_enable_key = g_default_cheat_enable_key,
};
std::memset(std::addressof(config.locale), 0xCC, sizeof(config.locale));
ParseIniFile(ContentSpecificIniHandler, path, std::addressof(config));
return config;
}
}
OverrideStatus CaptureOverrideStatus(ncm::ProgramId program_id) {
OverrideStatus status = {};
/* If the SD card isn't initialized, we can't override. */
if (!IsSdCardInitialized()) {
return status;
}
/* For system modules and anything launched before the home menu, always override. */
if (program_id < ncm::SystemAppletId::Start || !pm::info::HasLaunchedBootProgram(ncm::SystemAppletId::Qlaunch)) {
status.SetProgramSpecific();
return status;
}
/* Unconditionally refresh override_config.ini contents. */
RefreshOverrideConfiguration();
/* If we can't read the key state, don't override anything. */
if (R_FAILED(hid::GetKeysHeld(std::addressof(status.keys_held)))) {
return status;
}
/* Detect Hbl. */
if (IsAnyApplicationHblProgramId(program_id) && IsOverrideMatch(status, g_hbl_override_config.override_any_app_key)) {
status.SetHbl();
status.flags &= ~impl::OverrideStatusFlag_AddressSpaceMask;
status.flags |= g_hbl_override_config.override_any_app_as_flag;
}
for (size_t i = 0; i < MaxProgramOverrideKeys; i++) {
if (IsSpecificHblProgramId(i, program_id) && IsOverrideMatch(status, g_hbl_override_config.program_configs[i].override_key)) {
status.SetHbl();
status.flags &= ~impl::OverrideStatusFlag_AddressSpaceMask;
status.flags |= g_hbl_override_config.program_as_flags[i];
}
}
/* Detect content specific keys. */
const auto content_cfg = GetContentOverrideConfig(program_id);
if (IsOverrideMatch(status, content_cfg.override_key)) {
status.SetProgramSpecific();
}
/* Only allow cheat enable if not HBL. */
if (!status.IsHbl() && IsOverrideMatch(status, content_cfg.cheat_enable_key)) {
status.SetCheatEnabled();
}
return status;
}
OverrideLocale GetOverrideLocale(ncm::ProgramId program_id) {
return GetContentOverrideConfig(program_id).locale;
}
/* HBL Configuration utilities. */
const char *GetHblPath() {
return g_hbl_sd_path;
}
}

View File

@@ -15,429 +15,8 @@
*/
#include <stratosphere.hpp>
namespace ams::cfg {
namespace {
/* Types. */
struct OverrideKey {
u64 key_combination;
bool override_by_default;
};
struct ProgramOverrideKey {
OverrideKey override_key;
ncm::ProgramId program_id;
};
constexpr ProgramOverrideKey InvalidProgramOverrideKey = {};
constexpr ProgramOverrideKey DefaultAppletPhotoViewerOverrideKey = {
.override_key = {
.key_combination = HidNpadButton_R,
.override_by_default = true,
},
.program_id = ncm::SystemAppletId::PhotoViewer,
};
constexpr size_t MaxProgramOverrideKeys = 8;
struct HblOverrideConfig {
ProgramOverrideKey program_configs[MaxProgramOverrideKeys];
impl::OverrideStatusFlag program_as_flags[MaxProgramOverrideKeys];
OverrideKey override_any_app_key;
impl::OverrideStatusFlag override_any_app_as_flag;
bool override_any_app;
};
struct ContentSpecificOverrideConfig {
OverrideKey override_key;
OverrideKey cheat_enable_key;
OverrideLocale locale;
};
/* Override globals. */
OverrideKey g_default_override_key = {
.key_combination = HidNpadButton_L,
.override_by_default = true,
};
OverrideKey g_default_cheat_enable_key = {
.key_combination = HidNpadButton_L,
.override_by_default = true,
};
HblOverrideConfig g_hbl_override_config = {
.program_configs = {
DefaultAppletPhotoViewerOverrideKey,
InvalidProgramOverrideKey,
InvalidProgramOverrideKey,
InvalidProgramOverrideKey,
InvalidProgramOverrideKey,
InvalidProgramOverrideKey,
InvalidProgramOverrideKey,
InvalidProgramOverrideKey,
},
.program_as_flags = {
impl::OverrideStatusFlag_AddressSpace64Bit,
impl::OverrideStatusFlag_AddressSpace64Bit,
impl::OverrideStatusFlag_AddressSpace64Bit,
impl::OverrideStatusFlag_AddressSpace64Bit,
impl::OverrideStatusFlag_AddressSpace64Bit,
impl::OverrideStatusFlag_AddressSpace64Bit,
impl::OverrideStatusFlag_AddressSpace64Bit,
impl::OverrideStatusFlag_AddressSpace64Bit,
},
.override_any_app_key = {
.key_combination = HidNpadButton_R,
.override_by_default = false,
},
.override_any_app_as_flag = impl::OverrideStatusFlag_AddressSpace64Bit,
.override_any_app = true,
};
char g_hbl_sd_path[0x100] = "/atmosphere/hbl.nsp";
/* Helpers. */
OverrideKey ParseOverrideKey(const char *value) {
OverrideKey cfg = {};
/* Parse on by default. */
if (value[0] == '!') {
cfg.override_by_default = true;
value++;
}
/* Parse key combination. */
if (strcasecmp(value, "A") == 0) {
cfg.key_combination = HidNpadButton_A;
} else if (strcasecmp(value, "B") == 0) {
cfg.key_combination = HidNpadButton_B;
} else if (strcasecmp(value, "X") == 0) {
cfg.key_combination = HidNpadButton_X;
} else if (strcasecmp(value, "Y") == 0) {
cfg.key_combination = HidNpadButton_Y;
} else if (strcasecmp(value, "LS") == 0) {
cfg.key_combination = HidNpadButton_StickL;
} else if (strcasecmp(value, "RS") == 0) {
cfg.key_combination = HidNpadButton_StickR;
} else if (strcasecmp(value, "L") == 0) {
cfg.key_combination = HidNpadButton_L;
} else if (strcasecmp(value, "R") == 0) {
cfg.key_combination = HidNpadButton_R;
} else if (strcasecmp(value, "ZL") == 0) {
cfg.key_combination = HidNpadButton_ZL;
} else if (strcasecmp(value, "ZR") == 0) {
cfg.key_combination = HidNpadButton_ZR;
} else if (strcasecmp(value, "PLUS") == 0) {
cfg.key_combination = HidNpadButton_Plus;
} else if (strcasecmp(value, "MINUS") == 0) {
cfg.key_combination = HidNpadButton_Minus;
} else if (strcasecmp(value, "DLEFT") == 0) {
cfg.key_combination = HidNpadButton_Left;
} else if (strcasecmp(value, "DUP") == 0) {
cfg.key_combination = HidNpadButton_Up;
} else if (strcasecmp(value, "DRIGHT") == 0) {
cfg.key_combination = HidNpadButton_Right;
} else if (strcasecmp(value, "DDOWN") == 0) {
cfg.key_combination = HidNpadButton_Down;
} else if (strcasecmp(value, "SL") == 0) {
cfg.key_combination = HidNpadButton_AnySL;
} else if (strcasecmp(value, "SR") == 0) {
cfg.key_combination = HidNpadButton_AnySR;
}
return cfg;
}
impl::OverrideStatusFlag ParseOverrideAddressSpace(const char *value) {
if (strcasecmp(value, "39_bit") == 0 || strcasecmp(value, "39") == 0) {
return impl::OverrideStatusFlag_AddressSpace64Bit;
} else if (strcasecmp(value, "36_bit") == 0 || strcasecmp(value, "36") == 0) {
return impl::OverrideStatusFlag_AddressSpace64BitDeprecated;
} else if (strcasecmp(value, "32_bit") == 0 || strcasecmp(value, "32") == 0) {
return impl::OverrideStatusFlag_AddressSpace32Bit;
} else if (strcasecmp(value, "32_bit_without_alias") == 0 ||
strcasecmp(value, "32_bit_no_alias") == 0 ||
strcasecmp(value, "32_without_alias") == 0 ||
strcasecmp(value, "32_no_alias") ||
strcasecmp(value, "32_bit_without_map") == 0 ||
strcasecmp(value, "32_bit_no_map") == 0 ||
strcasecmp(value, "32_without_map") == 0 ||
strcasecmp(value, "32_no_map") == 0)
{
return impl::OverrideStatusFlag_AddressSpace32BitWithoutAlias;
} else {
/* Default to 39-bit. */
return impl::OverrideStatusFlag_AddressSpace64Bit;
}
}
inline void SetHblSpecificProgramId(size_t i, const char *value) {
g_hbl_override_config.program_configs[i].program_id = {strtoul(value, nullptr, 16)};
}
inline void SetHblSpecificOverrideKey(size_t i, const char *value) {
g_hbl_override_config.program_configs[i].override_key = ParseOverrideKey(value);
}
inline void SetHblSpecificAddressSpace(size_t i, const char *value) {
g_hbl_override_config.program_as_flags[i] = ParseOverrideAddressSpace(value);
}
int OverrideConfigIniHandler(void *user, const char *section, const char *name, const char *value) {
AMS_UNUSED(user);
/* Taken and modified, with love, from Rajkosto's implementation. */
if (strcasecmp(section, "hbl_config") == 0) {
if (strcasecmp(name, "program_id") == 0 || strcasecmp(name, "program_id_0") == 0) {
SetHblSpecificProgramId(0, value);
} else if (strcasecmp(name, "program_id_1") == 0) {
SetHblSpecificProgramId(1, value);
} else if (strcasecmp(name, "program_id_2") == 0) {
SetHblSpecificProgramId(2, value);
} else if (strcasecmp(name, "program_id_3") == 0) {
SetHblSpecificProgramId(3, value);
} else if (strcasecmp(name, "program_id_4") == 0) {
SetHblSpecificProgramId(4, value);
} else if (strcasecmp(name, "program_id_5") == 0) {
SetHblSpecificProgramId(5, value);
} else if (strcasecmp(name, "program_id_6") == 0) {
SetHblSpecificProgramId(6, value);
} else if (strcasecmp(name, "program_id_7") == 0) {
SetHblSpecificProgramId(7, value);
} else if (strcasecmp(name, "override_key") == 0 || strcasecmp(name, "override_key_0") == 0) {
SetHblSpecificOverrideKey(0, value);
} else if (strcasecmp(name, "override_key_1") == 0) {
SetHblSpecificOverrideKey(1, value);
} else if (strcasecmp(name, "override_key_2") == 0) {
SetHblSpecificOverrideKey(2, value);
} else if (strcasecmp(name, "override_key_3") == 0) {
SetHblSpecificOverrideKey(3, value);
} else if (strcasecmp(name, "override_key_4") == 0) {
SetHblSpecificOverrideKey(4, value);
} else if (strcasecmp(name, "override_key_5") == 0) {
SetHblSpecificOverrideKey(5, value);
} else if (strcasecmp(name, "override_key_6") == 0) {
SetHblSpecificOverrideKey(6, value);
} else if (strcasecmp(name, "override_key_7") == 0) {
SetHblSpecificOverrideKey(7, value);
} else if (strcasecmp(name, "override_address_space") == 0 || strcasecmp(name, "override_address_space_0") == 0) {
SetHblSpecificAddressSpace(0, value);
} else if (strcasecmp(name, "override_address_space_1") == 0) {
SetHblSpecificAddressSpace(1, value);
} else if (strcasecmp(name, "override_address_space_2") == 0) {
SetHblSpecificAddressSpace(2, value);
} else if (strcasecmp(name, "override_address_space_3") == 0) {
SetHblSpecificAddressSpace(3, value);
} else if (strcasecmp(name, "override_address_space_4") == 0) {
SetHblSpecificAddressSpace(4, value);
} else if (strcasecmp(name, "override_address_space_5") == 0) {
SetHblSpecificAddressSpace(5, value);
} else if (strcasecmp(name, "override_address_space_6") == 0) {
SetHblSpecificAddressSpace(6, value);
} else if (strcasecmp(name, "override_address_space_7") == 0) {
SetHblSpecificAddressSpace(7, value);
} else if (strcasecmp(name, "override_any_app") == 0) {
if (strcasecmp(value, "true") == 0 || strcasecmp(value, "1") == 0) {
g_hbl_override_config.override_any_app = true;
} else if (strcasecmp(value, "false") == 0 || strcasecmp(value, "0") == 0) {
g_hbl_override_config.override_any_app = false;
} else {
/* I guess we default to not changing the value? */
}
} else if (strcasecmp(name, "override_any_app_key") == 0) {
g_hbl_override_config.override_any_app_key = ParseOverrideKey(value);
} else if (strcasecmp(name, "override_any_app_address_space") == 0) {
g_hbl_override_config.override_any_app_as_flag = ParseOverrideAddressSpace(value);
} else if (strcasecmp(name, "path") == 0) {
while (*value == '/' || *value == '\\') {
value++;
}
util::SNPrintf(g_hbl_sd_path, sizeof(g_hbl_sd_path) - 1, "/%s", value);
g_hbl_sd_path[sizeof(g_hbl_sd_path) - 1] = '\0';
for (size_t i = 0; i < sizeof(g_hbl_sd_path); i++) {
if (g_hbl_sd_path[i] == '\\') {
g_hbl_sd_path[i] = '/';
}
}
}
} else if (strcasecmp(section, "default_config") == 0) {
if (strcasecmp(name, "override_key") == 0) {
g_default_override_key = ParseOverrideKey(value);
} else if (strcasecmp(name, "cheat_enable_key") == 0) {
g_default_cheat_enable_key = ParseOverrideKey(value);
}
} else {
return 0;
}
return 1;
}
int ContentSpecificIniHandler(void *user, const char *section, const char *name, const char *value) {
ContentSpecificOverrideConfig *config = reinterpret_cast<ContentSpecificOverrideConfig *>(user);
if (strcasecmp(section, "override_config") == 0) {
if (strcasecmp(name, "override_key") == 0) {
config->override_key = ParseOverrideKey(value);
} else if (strcasecmp(name, "cheat_enable_key") == 0) {
config->cheat_enable_key = ParseOverrideKey(value);
} else if (strcasecmp(name, "override_language") == 0) {
config->locale.language_code = settings::LanguageCode::Encode(value);
} else if (strcasecmp(name, "override_region") == 0) {
if (strcasecmp(value, "jpn") == 0) {
config->locale.region_code = settings::RegionCode_Japan;
} else if (strcasecmp(value, "usa") == 0) {
config->locale.region_code = settings::RegionCode_America;
} else if (strcasecmp(value, "eur") == 0) {
config->locale.region_code = settings::RegionCode_Europe;
} else if (strcasecmp(value, "aus") == 0) {
config->locale.region_code = settings::RegionCode_Australia;
} else if (strcasecmp(value, "chn") == 0) {
config->locale.region_code = settings::RegionCode_China;
} else if (strcasecmp(value, "kor") == 0) {
config->locale.region_code = settings::RegionCode_Korea;
} else if (strcasecmp(value, "twn") == 0) {
config->locale.region_code = settings::RegionCode_Taiwan;
}
}
} else {
return 0;
}
return 1;
}
constexpr inline bool IsOverrideMatch(const OverrideStatus &status, const OverrideKey &cfg) {
bool keys_triggered = ((status.keys_held & cfg.key_combination) != 0);
return (cfg.override_by_default ^ keys_triggered);
}
inline bool IsAnySpecificHblProgramId(ncm::ProgramId program_id) {
for (size_t i = 0; i < MaxProgramOverrideKeys; i++) {
if (program_id == g_hbl_override_config.program_configs[i].program_id) {
return true;
}
}
return false;
}
inline bool IsSpecificHblProgramId(size_t i, ncm::ProgramId program_id) {
return program_id == g_hbl_override_config.program_configs[i].program_id;
}
inline bool IsAnyApplicationHblProgramId(ncm::ProgramId program_id) {
return g_hbl_override_config.override_any_app && ncm::IsApplicationId(program_id) && !IsAnySpecificHblProgramId(program_id);
}
std::atomic<u32> g_ini_mount_count;
void GetIniMountName(char *dst) {
util::SNPrintf(dst, fs::MountNameLengthMax + 1, "#ini%08x", g_ini_mount_count.fetch_add(1));
}
void ParseIniFile(util::ini::Handler handler, const char *path, void *user_ctx) {
/* Mount the SD card. */
char mount_name[fs::MountNameLengthMax + 1];
GetIniMountName(mount_name);
if (R_FAILED(fs::MountSdCard(mount_name))) {
return;
}
ON_SCOPE_EXIT { fs::Unmount(mount_name); };
/* Open the file. */
fs::FileHandle file;
{
char full_path[fs::EntryNameLengthMax + 1];
util::SNPrintf(full_path, sizeof(full_path), "%s:/%s", mount_name, path[0] == '/' ? path + 1 : path);
if (R_FAILED(fs::OpenFile(std::addressof(file), full_path, fs::OpenMode_Read))) {
return;
}
}
ON_SCOPE_EXIT { fs::CloseFile(file); };
/* Parse the config. */
util::ini::ParseFile(file, user_ctx, handler);
}
void RefreshOverrideConfiguration() {
ParseIniFile(OverrideConfigIniHandler, "/atmosphere/config/override_config.ini", nullptr);
}
ContentSpecificOverrideConfig GetContentOverrideConfig(ncm::ProgramId program_id) {
char path[fs::EntryNameLengthMax + 1];
util::SNPrintf(path, sizeof(path), "/atmosphere/contents/%016lx/config.ini", static_cast<u64>(program_id));
ContentSpecificOverrideConfig config = {
.override_key = g_default_override_key,
.cheat_enable_key = g_default_cheat_enable_key,
};
std::memset(std::addressof(config.locale), 0xCC, sizeof(config.locale));
ParseIniFile(ContentSpecificIniHandler, path, std::addressof(config));
return config;
}
}
OverrideStatus CaptureOverrideStatus(ncm::ProgramId program_id) {
OverrideStatus status = {};
/* If the SD card isn't initialized, we can't override. */
if (!IsSdCardInitialized()) {
return status;
}
/* For system modules and anything launched before the home menu, always override. */
if (program_id < ncm::SystemAppletId::Start || !pm::info::HasLaunchedBootProgram(ncm::SystemAppletId::Qlaunch)) {
status.SetProgramSpecific();
return status;
}
/* Unconditionally refresh override_config.ini contents. */
RefreshOverrideConfiguration();
/* If we can't read the key state, don't override anything. */
if (R_FAILED(hid::GetKeysHeld(std::addressof(status.keys_held)))) {
return status;
}
/* Detect Hbl. */
if (IsAnyApplicationHblProgramId(program_id) && IsOverrideMatch(status, g_hbl_override_config.override_any_app_key)) {
status.SetHbl();
status.flags &= ~impl::OverrideStatusFlag_AddressSpaceMask;
status.flags |= g_hbl_override_config.override_any_app_as_flag;
}
for (size_t i = 0; i < MaxProgramOverrideKeys; i++) {
if (IsSpecificHblProgramId(i, program_id) && IsOverrideMatch(status, g_hbl_override_config.program_configs[i].override_key)) {
status.SetHbl();
status.flags &= ~impl::OverrideStatusFlag_AddressSpaceMask;
status.flags |= g_hbl_override_config.program_as_flags[i];
}
}
/* Detect content specific keys. */
const auto content_cfg = GetContentOverrideConfig(program_id);
if (IsOverrideMatch(status, content_cfg.override_key)) {
status.SetProgramSpecific();
}
/* Only allow cheat enable if not HBL. */
if (!status.IsHbl() && IsOverrideMatch(status, content_cfg.cheat_enable_key)) {
status.SetCheatEnabled();
}
return status;
}
OverrideLocale GetOverrideLocale(ncm::ProgramId program_id) {
return GetContentOverrideConfig(program_id).locale;
}
/* HBL Configuration utilities. */
const char *GetHblPath() {
return g_hbl_sd_path;
}
}
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
#include "cfg_override.board.nintendo_nx.inc"
#else
#include "cfg_override.generic.inc"
#endif

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 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::cfg {
OverrideStatus CaptureOverrideStatus(ncm::ProgramId program_id) {
AMS_UNUSED(program_id);
AMS_ABORT("TODO: How should this work?");
}
OverrideLocale GetOverrideLocale(ncm::ProgramId program_id) {
AMS_UNUSED(program_id);
AMS_ABORT("TODO: How should this work?");
}
/* HBL Configuration utilities. */
const char *GetHblPath() {
AMS_ABORT("TODO: How should this work?");
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright (c) 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::cfg {
namespace {
/* Convenience definitions. */
constexpr sm::ServiceName RequiredServicesForSdCardAccess[] = {
sm::ServiceName::Encode("pcv"),
sm::ServiceName::Encode("gpio"),
sm::ServiceName::Encode("pinmux"),
sm::ServiceName::Encode("psc:m"),
};
constexpr size_t NumRequiredServicesForSdCardAccess = util::size(RequiredServicesForSdCardAccess);
/* SD card globals. */
constinit os::SdkMutex g_sd_card_lock;
constinit bool g_sd_card_initialized = false;
constinit FsFileSystem g_sd_card_filesystem = {};
/* SD card helpers. */
Result CheckSdCardServicesReady() {
for (size_t i = 0; i < NumRequiredServicesForSdCardAccess; i++) {
bool service_present = false;
R_TRY(sm::HasService(std::addressof(service_present), RequiredServicesForSdCardAccess[i]));
if (!service_present) {
return fs::ResultSdCardNotPresent();
}
}
return ResultSuccess();
}
void WaitSdCardServicesReadyImpl() {
for (size_t i = 0; i < NumRequiredServicesForSdCardAccess; i++) {
R_ABORT_UNLESS(sm::WaitService(RequiredServicesForSdCardAccess[i]));
}
}
Result TryInitializeSdCard() {
R_TRY(CheckSdCardServicesReady());
R_ABORT_UNLESS(fsOpenSdCardFileSystem(std::addressof(g_sd_card_filesystem)));
g_sd_card_initialized = true;
return ResultSuccess();
}
void InitializeSdCard() {
WaitSdCardServicesReadyImpl();
R_ABORT_UNLESS(fsOpenSdCardFileSystem(std::addressof(g_sd_card_filesystem)));
g_sd_card_initialized = true;
}
}
/* SD card utilities. */
bool IsSdCardRequiredServicesReady() {
return R_SUCCEEDED(CheckSdCardServicesReady());
}
void WaitSdCardRequiredServicesReady() {
WaitSdCardServicesReadyImpl();
}
bool IsSdCardInitialized() {
std::scoped_lock lk(g_sd_card_lock);
if (!g_sd_card_initialized) {
if (R_SUCCEEDED(TryInitializeSdCard())) {
g_sd_card_initialized = true;
}
}
return g_sd_card_initialized;
}
void WaitSdCardInitialized() {
std::scoped_lock lk(g_sd_card_lock);
InitializeSdCard();
}
}

View File

@@ -15,82 +15,8 @@
*/
#include <stratosphere.hpp>
namespace ams::cfg {
namespace {
/* Convenience definitions. */
constexpr sm::ServiceName RequiredServicesForSdCardAccess[] = {
sm::ServiceName::Encode("pcv"),
sm::ServiceName::Encode("gpio"),
sm::ServiceName::Encode("pinmux"),
sm::ServiceName::Encode("psc:m"),
};
constexpr size_t NumRequiredServicesForSdCardAccess = util::size(RequiredServicesForSdCardAccess);
/* SD card globals. */
constinit os::SdkMutex g_sd_card_lock;
constinit bool g_sd_card_initialized = false;
constinit FsFileSystem g_sd_card_filesystem = {};
/* SD card helpers. */
Result CheckSdCardServicesReady() {
for (size_t i = 0; i < NumRequiredServicesForSdCardAccess; i++) {
bool service_present = false;
R_TRY(sm::HasService(std::addressof(service_present), RequiredServicesForSdCardAccess[i]));
if (!service_present) {
return fs::ResultSdCardNotPresent();
}
}
return ResultSuccess();
}
void WaitSdCardServicesReadyImpl() {
for (size_t i = 0; i < NumRequiredServicesForSdCardAccess; i++) {
R_ABORT_UNLESS(sm::WaitService(RequiredServicesForSdCardAccess[i]));
}
}
Result TryInitializeSdCard() {
R_TRY(CheckSdCardServicesReady());
R_ABORT_UNLESS(fsOpenSdCardFileSystem(std::addressof(g_sd_card_filesystem)));
g_sd_card_initialized = true;
return ResultSuccess();
}
void InitializeSdCard() {
WaitSdCardServicesReadyImpl();
R_ABORT_UNLESS(fsOpenSdCardFileSystem(std::addressof(g_sd_card_filesystem)));
g_sd_card_initialized = true;
}
}
/* SD card utilities. */
bool IsSdCardRequiredServicesReady() {
return R_SUCCEEDED(CheckSdCardServicesReady());
}
void WaitSdCardRequiredServicesReady() {
WaitSdCardServicesReadyImpl();
}
bool IsSdCardInitialized() {
std::scoped_lock lk(g_sd_card_lock);
if (!g_sd_card_initialized) {
if (R_SUCCEEDED(TryInitializeSdCard())) {
g_sd_card_initialized = true;
}
}
return g_sd_card_initialized;
}
void WaitSdCardInitialized() {
std::scoped_lock lk(g_sd_card_lock);
InitializeSdCard();
}
}
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
#include "cfg_sd_card.board.nintendo_nx.inc"
#else
#include "cfg_sd_card.generic.inc"
#endif

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 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::cfg {
bool IsSdCardRequiredServicesReady() {
return true;
}
void WaitSdCardRequiredServicesReady() {
/* ... */
}
bool IsSdCardInitialized() {
return true;
}
void WaitSdCardInitialized() {
/* ... */
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) 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::dd::impl {
class DeviceAddressSpaceImplByWindows {
public:
static Result Create(DeviceAddressSpaceHandle *, u64, u64) {
R_THROW(dd::ResultNotSupported());
}
static void Close(DeviceAddressSpaceHandle) {
/* ... */
}
static Result MapAligned(DeviceAddressSpaceHandle, ProcessHandle, u64, size_t, DeviceVirtualAddress, dd::MemoryPermission) {
R_THROW(dd::ResultNotSupported());
}
static Result MapNotAligned(DeviceAddressSpaceHandle, ProcessHandle, u64, size_t, DeviceVirtualAddress, dd::MemoryPermission) {
R_THROW(dd::ResultNotSupported());
}
static void Unmap(DeviceAddressSpaceHandle, ProcessHandle, u64, size_t, DeviceVirtualAddress) {
/* ... */
}
static Result Attach(DeviceAddressSpaceType *, DeviceName) {
R_THROW(dd::ResultNotSupported());
}
static void Detach(DeviceAddressSpaceType *, DeviceName) {
/* ... */
}
};
using DeviceAddressSpaceImpl = DeviceAddressSpaceImplByWindows;
}

View File

@@ -19,5 +19,5 @@
#if defined(ATMOSPHERE_OS_HORIZON)
#include "dd_device_address_space_impl.os.horizon.hpp"
#else
#error "Unknown os for dd::DeviceAddressSpaceImpl"
#include "dd_device_address_space_impl.generic.hpp"
#endif

View File

@@ -14,12 +14,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/diag_print_debug_string.hpp"
namespace ams::diag {
namespace {
inline NORETURN void AbortWithValue(u64 debug) {
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
/* Just perform a data abort. */
register u64 addr __asm__("x27") = FatalErrorContext::StdAbortMagicAddress;
register u64 val __asm__("x28") = FatalErrorContext::StdAbortMagicValue;
@@ -32,6 +34,10 @@ namespace ams::diag {
: "x0"
);
}
#else
AMS_UNUSED(debug);
std::abort();
#endif
__builtin_unreachable();
}
@@ -46,7 +52,7 @@ namespace ams::diag {
util::VSNPrintf(g_debug_buffer, sizeof(g_debug_buffer), format, vl);
svc::OutputDebugString(g_debug_buffer, strlen(g_debug_buffer));
diag::impl::PrintDebugString(g_debug_buffer, strlen(g_debug_buffer));
}
void DebugLog(const char *format, ...) {
@@ -62,8 +68,12 @@ namespace ams::diag {
}
NORETURN WEAK_SYMBOL void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
DebugLog("%016lx: Assertion Failure\n", os::GetCurrentProgramId().value);
NORETURN NOINLINE WEAK_SYMBOL void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
#if defined(ATMOSPHERE_OS_HORIZON)
DebugLog("%016" PRIx64 ": Assertion Failure\n", os::GetCurrentProgramId().value);
#else
DebugLog("0100000000000000: Assertion Failure\n");
#endif
DebugLog(" Location: %s:%d\n", file, line);
DebugLog(" Function: %s\n", func);
DebugLog(" Expression: %s\n", expr);
@@ -84,8 +94,12 @@ namespace ams::diag {
AbortWithValue(value);
}
NORETURN WEAK_SYMBOL void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
DebugLog("%016lx: Assertion Failure\n", os::GetCurrentProgramId().value);
NORETURN NOINLINE WEAK_SYMBOL void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
#if defined(ATMOSPHERE_OS_HORIZON)
DebugLog("%016" PRIx64 ": Assertion Failure\n", os::GetCurrentProgramId().value);
#else
DebugLog("0100000000000000: Assertion Failure\n");
#endif
DebugLog(" Location: %s:%d\n", file, line);
DebugLog(" Function: %s\n", func);
DebugLog(" Expression: %s\n", expr);
@@ -96,8 +110,12 @@ namespace ams::diag {
AbortWithValue(value);
}
NORETURN WEAK_SYMBOL void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
DebugLog("%016lx: Abort Called\n", os::GetCurrentProgramId().value);
NORETURN NOINLINE WEAK_SYMBOL void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) {
#if defined(ATMOSPHERE_OS_HORIZON)
DebugLog("%016" PRIx64 ": Abort Called\n", os::GetCurrentProgramId().value);
#else
DebugLog("0100000000000000: Abort Called\n");
#endif
DebugLog(" Location: %s:%d\n", file, line);
DebugLog(" Function: %s\n", func);
DebugLog(" Expression: %s\n", expr);
@@ -118,8 +136,12 @@ namespace ams::diag {
AbortWithValue(value);
}
NORETURN WEAK_SYMBOL void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
DebugLog("%016lx: Abort Called\n", os::GetCurrentProgramId().value);
NORETURN NOINLINE WEAK_SYMBOL void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value) {
#if defined(ATMOSPHERE_OS_HORIZON)
DebugLog("%016" PRIx64 ": Abort Called\n", os::GetCurrentProgramId().value);
#else
DebugLog("0100000000000000: Abort Called\n");
#endif
DebugLog(" Location: %s:%d\n", file, line);
DebugLog(" Function: %s\n", func);
DebugLog(" Expression: %s\n", expr);
@@ -130,7 +152,7 @@ namespace ams::diag {
AbortWithValue(value);
}
NORETURN WEAK_SYMBOL void AbortImpl() {
NORETURN NOINLINE WEAK_SYMBOL void AbortImpl() {
AbortWithValue(0);
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 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/>.
*/
/* ams::diag::impl::FatalErrorByResultForNx(Result value) */
.section .text._ZN3ams4diag4impl23FatalErrorByResultForNxENS_6ResultE, "ax", %progbits
.global _ZN3ams4diag4impl23FatalErrorByResultForNxENS_6ResultE
.type _ZN3ams4diag4impl23FatalErrorByResultForNxENS_6ResultE, %function
.balign 0x10
_ZN3ams4diag4impl23FatalErrorByResultForNxENS_6ResultE:
/* Save x27/x28. */
stp x27, x28, [sp, #0x10]
/* Put magic std::abort values into x27/x28. */
mov x28, #0xcafe
movk x28, #0xdead, lsl#16
movk x28, #0xf00d, lsl#32
movk x28, #0xa55a, lsl#48
mov x27, #8
/* Abort */
0:
str x28, [x27]
nop
b 0b

View File

@@ -19,6 +19,11 @@
namespace ams::diag::impl {
void PrintDebugString(const char *msg, size_t size);
void PrintDebugString(const char *msg);
inline void PrintDebugString(const char *msg) {
AMS_AUDIT(msg != nullptr);
PrintDebugString(msg, std::strlen(msg));
}
}

View File

@@ -26,10 +26,4 @@ namespace ams::diag::impl {
}
}
void PrintDebugString(const char *msg) {
AMS_AUDIT(msg != nullptr);
PrintDebugString(msg, std::strlen(msg));
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 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 "diag_print_debug_string.hpp"
namespace ams::diag::impl {
NOINLINE void PrintDebugString(const char *msg, size_t size) {
AMS_AUDIT(msg != nullptr || size == 0);
if (size == 0) {
return;
}
/* TODO: Printf? */
printf("%s", msg);
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 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 "diag_print_debug_string.hpp"
namespace ams::diag::impl {
NOINLINE void PrintDebugString(const char *msg, size_t size) {
AMS_AUDIT(msg != nullptr || size == 0);
if (size == 0) {
return;
}
/* TODO: Printf? */
printf("%s", msg);
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 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 "diag_print_debug_string.hpp"
namespace ams::diag::impl {
NOINLINE void PrintDebugString(const char *msg, size_t size) {
AMS_AUDIT(msg != nullptr || size == 0);
if (size == 0) {
return;
}
/* TODO: OutputDebugString? Printf? */
printf("%s", msg);
}
}

View File

@@ -18,7 +18,7 @@
namespace ams::erpt::srv {
constinit JournalMeta JournalForMeta::s_journal_meta;
constinit JournalMeta JournalForMeta::s_journal_meta = {};
void JournalForMeta::InitializeJournal() {
std::memset(std::addressof(s_journal_meta), 0, sizeof(s_journal_meta));

View File

@@ -25,7 +25,7 @@
namespace ams::erpt::srv {
constinit lmem::HeapHandle g_heap_handle;
constinit ams::sf::ExpHeapAllocator g_sf_allocator;
constinit ams::sf::ExpHeapAllocator g_sf_allocator = {};
namespace {

View File

@@ -33,9 +33,6 @@ namespace ams::erpt::srv {
namespace {
constinit os::SdkMutex g_limit_mutex;
constinit bool g_submitted_limit = false;
class AppletActiveTimeInfoList {
private:
struct AppletActiveTimeInfo {
@@ -91,6 +88,7 @@ namespace ams::erpt::srv {
constinit AppletActiveTimeInfoList g_applet_active_time_info_list;
#if defined(ATMOSPHERE_OS_HORIZON)
Result PullErrorContext(size_t *out_total_size, size_t *out_size, void *dst, size_t dst_size, const err::ContextDescriptor &descriptor, Result result) {
s32 unk0;
u32 total_size, size;
@@ -135,6 +133,9 @@ namespace ams::erpt::srv {
record->Add(FieldId_ErrorContext, error_context, error_context_size);
}
constinit os::SdkMutex g_limit_mutex;
constinit bool g_submitted_limit = false;
void SubmitResourceLimitLimitContext() {
std::scoped_lock lk(g_limit_mutex);
if (g_submitted_limit) {
@@ -222,6 +223,11 @@ namespace ams::erpt::srv {
SubmitResourceLimitLimitContext();
SubmitResourceLimitPeakContext();
}
#else
void SubmitErrorContext(ContextRecord *record, Result result) {
AMS_UNUSED(record, result);
}
#endif
Result ValidateCreateReportContext(const ContextEntry *ctx) {
R_UNLESS(ctx->category == CategoryId_ErrorInfo, erpt::ResultRequiredContextMissing());
@@ -520,7 +526,9 @@ namespace ams::erpt::srv {
R_TRY(Context::SubmitContextRecord(std::move(record)));
/* Submit context for resource limits. */
#if defined(ATMOSPHERE_OS_HORIZON)
SubmitResourceLimitContexts();
#endif
return ResultSuccess();
}

View File

@@ -138,7 +138,7 @@ namespace ams::erpt::srv {
}
}
constinit util::TypedStorage<ErrorReportServiceManager> g_erpt_server_manager;
constinit util::TypedStorage<ErrorReportServiceManager> g_erpt_server_manager = {};
}

View File

@@ -31,7 +31,7 @@ namespace ams::err {
};
}
ALWAYS_INLINE Result ConvertErrorCodeToResult(const ErrorCode &error_code) {
[[maybe_unused]] ALWAYS_INLINE Result ConvertErrorCodeToResult(const ErrorCode &error_code) {
const auto result_value = ::ams::result::impl::ResultTraits::MakeValue(error_code.category - ErrorCodeCategoryPlatformPrefixForResultModule, error_code.number);
return ::ams::result::impl::MakeResult(result_value);
}

View File

@@ -90,7 +90,7 @@ namespace ams::fs {
}
}
Result FileStorageBasedFileSystem::Initialize(std::shared_ptr<fs::fsa::IFileSystem> base_file_system, const char *path, fs::OpenMode mode) {
Result FileStorageBasedFileSystem::Initialize(std::shared_ptr<fs::fsa::IFileSystem> base_file_system, const fs::Path &path, fs::OpenMode mode) {
/* Open the file. */
std::unique_ptr<fs::fsa::IFile> base_file;
R_TRY(base_file_system->OpenFile(std::addressof(base_file), path, mode));

View File

@@ -14,6 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/fs_file_system_proxy_service_object.hpp"
#include "fsa/fs_user_mount_table.hpp"
#include "fsa/fs_directory_accessor.hpp"
#include "fsa/fs_file_accessor.hpp"
@@ -21,7 +22,7 @@
#define AMS_FS_IMPL_ACCESS_LOG_AMS_API_VERSION "ams_version: " STRINGIZE(ATMOSPHERE_RELEASE_VERSION_MAJOR) "." STRINGIZE(ATMOSPHERE_RELEASE_VERSION_MINOR) "." STRINGIZE(ATMOSPHERE_RELEASE_VERSION_MICRO)
/* TODO: Other boards? */
/* TODO: Other specs? */
#define AMS_FS_IMPL_ACCESS_LOG_SPEC "spec: NX"
namespace ams::fs {
@@ -48,13 +49,15 @@ namespace ams::fs {
}
Result GetGlobalAccessLogMode(u32 *out) {
/* Use libnx bindings. */
return ::fsGetGlobalAccessLogMode(out);
const auto fsp = impl::GetFileSystemProxyServiceObject();
AMS_FS_R_TRY(fsp->GetGlobalAccessLogMode(out));
R_SUCCEED();
}
Result SetGlobalAccessLogMode(u32 mode) {
/* Use libnx bindings. */
return ::fsSetGlobalAccessLogMode(mode);
const auto fsp = impl::GetFileSystemProxyServiceObject();
AMS_FS_R_TRY(fsp->SetGlobalAccessLogMode(mode));
R_SUCCEED();
}
void SetLocalAccessLog(bool enabled) {
@@ -167,6 +170,15 @@ namespace ams::fs::impl {
}
}
template<> const char *IdString::ToString<fs::GameCardPartition>(fs::GameCardPartition id) {
switch (id) {
case fs::GameCardPartition::Update: return "Update";
case fs::GameCardPartition::Normal: return "Normal";
case fs::GameCardPartition::Secure: return "Secure";
default: return ToValueString(static_cast<int>(id));
}
}
namespace {
class AccessLogPrinterCallbackManager {
@@ -199,8 +211,9 @@ namespace ams::fs::impl {
}
Result OutputAccessLogToSdCardImpl(const char *log, size_t size) {
/* Use libnx bindings. */
return ::fsOutputAccessLogToSdCard(log, size);
const auto fsp = impl::GetFileSystemProxyServiceObject();
AMS_FS_R_TRY(fsp->OutputAccessLogToSdCard(sf::InBuffer(log, size)));
R_SUCCEED();
}
void OutputAccessLogToSdCard(const char *format, std::va_list vl) {
@@ -291,10 +304,11 @@ namespace ams::fs::impl {
OutputAccessLogImpl(log_buffer.get(), log_buffer_size);
}
void GetProgramIndexFortAccessLog(u32 *out_index, u32 *out_count) {
void GetProgramIndexForAccessLog(u32 *out_index, u32 *out_count) {
if (hos::GetVersion() >= hos::Version_7_0_0) {
/* Use libnx bindings if available. */
R_ABORT_UNLESS(::fsGetProgramIndexForAccessLog(out_index, out_count));
const auto fsp = impl::GetFileSystemProxyServiceObject();
R_ABORT_UNLESS(fsp->GetProgramIndexForAccessLog(out_index, out_count));
} else {
/* Use hardcoded defaults. */
*out_index = 0;
@@ -305,7 +319,7 @@ namespace ams::fs::impl {
void OutputAccessLogStart() {
/* Get the program index. */
u32 program_index = 0, program_count = 0;
GetProgramIndexFortAccessLog(std::addressof(program_index), std::addressof(program_count));
GetProgramIndexForAccessLog(std::addressof(program_index), std::addressof(program_count));
/* Print the log buffer. */
if (program_count < 2) {

View File

@@ -14,25 +14,182 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
extern "C" {
extern u32 __nx_fs_num_sessions;
}
#include "fs_remote_file_system_proxy.hpp"
#include "fs_remote_file_system_proxy_for_loader.hpp"
#include "impl/fs_library.hpp"
namespace ams::fs {
/* TODO: FileSystemProxySessionSetting */
#if defined(ATMOSPHERE_OS_HORIZON)
namespace {
constinit std::aligned_storage_t<0x80> g_fsp_service_object_buffer;
constinit std::aligned_storage_t<0x80> g_fsp_ldr_service_object_buffer;
constinit bool g_use_static_fsp_service_object_buffer = false;
constinit bool g_use_static_fsp_ldr_service_object_buffer = false;
class HipcClientAllocator {
public:
using Policy = sf::StatelessAllocationPolicy<HipcClientAllocator>;
public:
constexpr HipcClientAllocator() = default;
void *Allocate(size_t size) {
if (g_use_static_fsp_service_object_buffer) {
return std::addressof(g_fsp_service_object_buffer);
} else if (g_use_static_fsp_ldr_service_object_buffer) {
return std::addressof(g_fsp_ldr_service_object_buffer);
} else {
return ::ams::fs::impl::Allocate(size);
}
}
void Deallocate(void *ptr, size_t size) {
if (ptr == std::addressof(g_fsp_service_object_buffer)) {
return;
} else if (ptr == std::addressof(g_fsp_ldr_service_object_buffer)) {
return;
} else {
return ::ams::fs::impl::Deallocate(ptr, size);
}
}
};
enum class FileSystemProxySessionSetting {
SystemNormal = 0,
Application = 1,
SystemMulti = 2,
};
constexpr ALWAYS_INLINE int GetSessionCount(FileSystemProxySessionSetting setting) {
switch (setting) {
case FileSystemProxySessionSetting::Application: return 3;
case FileSystemProxySessionSetting::SystemNormal: return 1;
case FileSystemProxySessionSetting::SystemMulti: return 2;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
constinit bool g_is_fsp_object_initialized = false;
constinit FileSystemProxySessionSetting g_fsp_session_setting = FileSystemProxySessionSetting::SystemNormal;
/* TODO: SessionResourceManager */
}
#endif
namespace {
constinit sf::SharedPointer<fssrv::sf::IFileSystemProxy> g_fsp_service_object;
sf::SharedPointer<fssrv::sf::IFileSystemProxy> GetFileSystemProxyServiceObjectImpl() {
/* Ensure the library is initialized. */
::ams::fs::impl::InitializeFileSystemLibrary();
sf::SharedPointer<fssrv::sf::IFileSystemProxy> fsp_object;
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) && defined(ATMOSPHERE_OS_HORIZON)
/* Try to use the custom object. */
fsp_object = g_fsp_service_object;
/* If we don't have one, create a remote object. */
if (fsp_object == nullptr) {
/* Make our next allocation use our static reserved buffer for the service object. */
g_use_static_fsp_service_object_buffer = true;
ON_SCOPE_EXIT { g_use_static_fsp_service_object_buffer = false; };
using ObjectFactory = sf::ObjectFactory<HipcClientAllocator::Policy>;
/* Create the object. */
fsp_object = ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystemProxy, RemoteFileSystemProxy>(GetSessionCount(g_fsp_session_setting));
AMS_ABORT_UNLESS(fsp_object != nullptr);
/* Set the current process. */
fsp_object->SetCurrentProcess({});
}
#else
/* On non-horizon, use the system object. */
fsp_object = fssrv::impl::GetFileSystemProxyServiceObject();
AMS_ABORT_UNLESS(fsp_object != nullptr);
/* Set the current process. */
fsp_object->SetCurrentProcess({});
#endif
/* Return the object. */
return fsp_object;
}
sf::SharedPointer<fssrv::sf::IFileSystemProxyForLoader> GetFileSystemProxyForLoaderServiceObjectImpl() {
/* Ensure the library is initialized. */
::ams::fs::impl::InitializeFileSystemLibrary();
sf::SharedPointer<fssrv::sf::IFileSystemProxyForLoader> fsp_ldr_object;
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) && defined(ATMOSPHERE_OS_HORIZON)
/* Make our next allocation use our static reserved buffer for the service object. */
g_use_static_fsp_ldr_service_object_buffer = true;
ON_SCOPE_EXIT { g_use_static_fsp_ldr_service_object_buffer = false; };
using ObjectFactory = sf::ObjectFactory<HipcClientAllocator::Policy>;
/* Create the object. */
fsp_ldr_object = ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystemProxyForLoader, RemoteFileSystemProxyForLoader>();
AMS_ABORT_UNLESS(fsp_ldr_object != nullptr);
#else
/* On non-horizon, use the system object. */
fsp_ldr_object = fssrv::impl::GetFileSystemProxyForLoaderServiceObject();
AMS_ABORT_UNLESS(fsp_ldr_object != nullptr);
#endif
/* Return the object. */
return fsp_ldr_object;
}
}
namespace impl {
sf::SharedPointer<fssrv::sf::IFileSystemProxy> GetFileSystemProxyServiceObject() {
AMS_FUNCTION_LOCAL_STATIC(sf::SharedPointer<fssrv::sf::IFileSystemProxy>, s_fsp_service_object, GetFileSystemProxyServiceObjectImpl());
return s_fsp_service_object;
}
sf::SharedPointer<fssrv::sf::IFileSystemProxyForLoader> GetFileSystemProxyForLoaderServiceObject() {
AMS_FUNCTION_LOCAL_STATIC(sf::SharedPointer<fssrv::sf::IFileSystemProxyForLoader>, s_fsp_ldr_service_object, GetFileSystemProxyForLoaderServiceObjectImpl());
return s_fsp_ldr_service_object;
}
}
void InitializeForHostTool() {
#if !defined(ATMOSPHERE_OS_HORIZON)
AMS_ABORT_UNLESS(impl::GetFileSystemProxyServiceObject() != nullptr);
R_ABORT_UNLESS(::ams::fs::MountHostRoot());
#endif
}
void InitializeForSystem() {
__nx_fs_num_sessions = 1;
R_ABORT_UNLESS(::fsInitialize());
#if defined(ATMOSPHERE_OS_HORIZON)
AMS_ABORT_UNLESS(!g_is_fsp_object_initialized);
AMS_ABORT_UNLESS(g_fsp_session_setting == FileSystemProxySessionSetting::SystemNormal);
g_fsp_session_setting = FileSystemProxySessionSetting::SystemNormal;
/* Nintendo doesn't do this, but we have to for timing reasons. */
AMS_ABORT_UNLESS(impl::GetFileSystemProxyServiceObject() != nullptr);
#endif
}
void InitializeWithMultiSessionForSystem() {
__nx_fs_num_sessions = 2;
R_ABORT_UNLESS(::fsInitialize());
#if defined(ATMOSPHERE_OS_HORIZON)
AMS_ABORT_UNLESS(!g_is_fsp_object_initialized);
AMS_ABORT_UNLESS(g_fsp_session_setting == FileSystemProxySessionSetting::SystemNormal);
g_fsp_session_setting = FileSystemProxySessionSetting::SystemMulti;
/* Nintendo doesn't do this, but we have to for timing reasons. */
AMS_ABORT_UNLESS(impl::GetFileSystemProxyServiceObject() != nullptr);
#endif
}
}

View File

@@ -15,26 +15,43 @@
*/
#include <stratosphere.hpp>
#include "fsa/fs_mount_utils.hpp"
#include "impl/fs_file_system_service_object_adapter.hpp"
#include "impl/fs_file_system_proxy_service_object.hpp"
namespace ams::fs {
Result MountApplicationPackage(const char *name, const char *common_path) {
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
auto mount_impl = [=]() -> Result {
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
/* Validate the path. */
R_UNLESS(common_path != nullptr, fs::ResultInvalidPath());
/* Validate the path. */
R_UNLESS(common_path != nullptr, fs::ResultInvalidPath());
/* Open a filesystem using libnx bindings. */
FsFileSystem fs;
R_TRY(fsOpenFileSystemWithId(std::addressof(fs), ncm::InvalidProgramId.value, static_cast<::FsFileSystemType>(impl::FileSystemProxyType_Package), common_path));
/* Convert the path for ipc. */
fssrv::sf::FspPath sf_path;
R_TRY(fs::ConvertToFspPath(std::addressof(sf_path), common_path));
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<RemoteFileSystem>(fs);
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInApplicationA());
/* Open the filesystem. */
auto fsp = impl::GetFileSystemProxyServiceObject();
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
R_TRY(fsp->OpenFileSystemWithId(std::addressof(fs), sf_path, ncm::InvalidProgramId.value, impl::FileSystemProxyType_Package));
/* Register. */
return fsa::Register(name, std::move(fsa));
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInApplicationA());
/* Register. */
R_RETURN(fsa::Register(name, std::move(fsa)));
};
/* Perform the mount. */
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_APPLICATION_PACKAGE(name, common_path)));
/* Enable access logging. */
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
R_SUCCEED();
}
}

View File

@@ -15,6 +15,9 @@
*/
#include <stratosphere.hpp>
#include "fsa/fs_mount_utils.hpp"
#include "impl/fs_file_system_service_object_adapter.hpp"
#include "impl/fs_storage_service_object_adapter.hpp"
#include "impl/fs_file_system_proxy_service_object.hpp"
namespace ams::fs {
@@ -46,43 +49,45 @@ namespace ams::fs {
namespace impl {
Result MountBisImpl(const char *name, BisPartitionId id, const char *root_path) {
/* Validate the mount name. */
R_TRY(impl::CheckMountNameAllowingReserved(name));
auto mount_impl = [=]() -> Result {
/* Validate the mount name. */
R_TRY(impl::CheckMountNameAllowingReserved(name));
/* Open the partition. This uses libnx bindings. */
/* NOTE: Nintendo ignores the root_path here. */
AMS_UNUSED(root_path);
FsFileSystem fs;
R_TRY(fsOpenBisFileSystem(std::addressof(fs), static_cast<::FsBisPartitionId>(id), ""));
/* Convert the path for ipc. */
/* NOTE: Nintendo ignores the root_path here. */
fssrv::sf::FspPath sf_path;
sf_path.str[0] = '\x00';
AMS_UNUSED(root_path);
/* Allocate a new mountname generator. */
auto generator = std::make_unique<BisCommonMountNameGenerator>(id);
R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInBisA());
/* Open the filesystem. */
auto fsp = impl::GetFileSystemProxyServiceObject();
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
R_TRY(fsp->OpenBisFileSystem(std::addressof(fs), sf_path, static_cast<u32>(id)));
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<RemoteFileSystem>(fs);
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInBisB());
/* Allocate a new mountname generator. */
auto generator = std::make_unique<BisCommonMountNameGenerator>(id);
R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInBisA());
/* Register. */
return fsa::Register(name, std::move(fsa), std::move(generator));
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInBisB());
/* Register. */
R_RETURN(fsa::Register(name, std::move(fsa), std::move(generator)));
};
/* Perform the mount. */
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_BIS(name, id, root_path)));
/* Enable access logging. */
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
R_SUCCEED();
}
Result SetBisRootForHostImpl(BisPartitionId id, const char *root_path) {
/* Ensure the path isn't too long. */
size_t len = strnlen(root_path, fs::EntryNameLengthMax + 1);
R_UNLESS(len <= fs::EntryNameLengthMax, fs::ResultTooLongPath());
fssrv::sf::Path sf_path;
if (len > 0) {
const bool ending_sep = PathNormalizer::IsSeparator(root_path[len - 1]);
FspPathPrintf(std::addressof(sf_path), "%s%s", root_path, ending_sep ? "" : "/");
} else {
sf_path.str[0] = '\x00';
}
/* TODO: Libnx binding for fsSetBisRootForHost */
AMS_UNUSED(id);
AMS_ABORT();
AMS_UNUSED(id, root_path);
AMS_ABORT("TODO");
}
}
@@ -110,21 +115,23 @@ namespace ams::fs {
}
Result OpenBisPartition(std::unique_ptr<fs::IStorage> *out, BisPartitionId id) {
/* Open the partition. This uses libnx bindings. */
FsStorage s;
R_TRY(fsOpenBisStorage(std::addressof(s), static_cast<::FsBisPartitionId>(id)));
/* Open the partition. */
auto fsp = impl::GetFileSystemProxyServiceObject();
sf::SharedPointer<fssrv::sf::IStorage> s;
AMS_FS_R_TRY(fsp->OpenBisStorage(std::addressof(s), static_cast<u32>(id)));
/* Allocate a new storage wrapper. */
auto storage = std::make_unique<RemoteStorage>(s);
R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInBisC());
auto storage = std::make_unique<impl::StorageServiceObjectAdapter>(std::move(s));
AMS_FS_R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInBisC());
*out = std::move(storage);
return ResultSuccess();
R_SUCCEED();
}
Result InvalidateBisCache() {
/* TODO: Libnx binding for this command. */
AMS_ABORT();
auto fsp = impl::GetFileSystemProxyServiceObject();
AMS_FS_R_ABORT_UNLESS(fsp->InvalidateBisCache());
R_SUCCEED();
}
}

View File

@@ -15,6 +15,8 @@
*/
#include <stratosphere.hpp>
#include "fsa/fs_mount_utils.hpp"
#include "impl/fs_file_system_service_object_adapter.hpp"
#include "impl/fs_file_system_proxy_service_object.hpp"
namespace ams::fs {
@@ -56,7 +58,7 @@ namespace ams::fs {
g_mounted_stratosphere_romfs = true;
}
return ResultSuccess();
R_SUCCEED();
}
fsa::IFileSystem &GetStratosphereRomFsFileSystem() {
@@ -70,32 +72,35 @@ namespace ams::fs {
Result OpenCodeFileSystemImpl(CodeVerificationData *out_verification_data, std::unique_ptr<fsa::IFileSystem> *out, const char *path, ncm::ProgramId program_id) {
/* Print a path suitable for the remote service. */
fssrv::sf::Path sf_path;
R_TRY(FspPathPrintf(std::addressof(sf_path), "%s", path));
R_TRY(FormatToFspPath(std::addressof(sf_path), "%s", path));
/* Open the filesystem using libnx bindings. */
static_assert(sizeof(CodeVerificationData) == sizeof(::FsCodeInfo));
::FsFileSystem fs;
R_TRY(fsldrOpenCodeFileSystem(reinterpret_cast<::FsCodeInfo *>(out_verification_data), program_id.value, sf_path.str, std::addressof(fs)));
/* Open the filesystem. */
auto fsp = impl::GetFileSystemProxyForLoaderServiceObject();
R_TRY(fsp->SetCurrentProcess({}));
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
R_TRY(fsp->OpenCodeFileSystem(std::addressof(fs), out_verification_data, sf_path, program_id));
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<RemoteFileSystem>(fs);
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInCodeA());
*out = std::move(fsa);
return ResultSuccess();
R_SUCCEED();
}
Result OpenPackageFileSystemImpl(std::unique_ptr<fsa::IFileSystem> *out, const char *common_path) {
/* Open a filesystem using libnx bindings. */
FsFileSystem fs;
R_TRY(fsOpenFileSystemWithId(std::addressof(fs), ncm::InvalidProgramId.value, static_cast<::FsFileSystemType>(impl::FileSystemProxyType_Package), common_path));
Result OpenPackageFileSystemImpl(std::unique_ptr<fsa::IFileSystem> *out, const fssrv::sf::FspPath &path) {
/* Open the filesystem. */
auto fsp = impl::GetFileSystemProxyServiceObject();
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
R_TRY(fsp->OpenFileSystemWithId(std::addressof(fs), path, ncm::InvalidProgramId.value, impl::FileSystemProxyType_Package));
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<RemoteFileSystem>(fs);
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInCodeA());
*out = std::move(fsa);
return ResultSuccess();
R_SUCCEED();
}
Result OpenSdCardCodeFileSystemImpl(std::unique_ptr<fsa::IFileSystem> *out, ncm::ProgramId program_id) {
@@ -104,9 +109,9 @@ namespace ams::fs {
/* Print a path to the program's package. */
fssrv::sf::Path sf_path;
R_TRY(FspPathPrintf(std::addressof(sf_path), "%s:/atmosphere/contents/%016lx/exefs.nsp", impl::SdCardFileSystemMountName, program_id.value));
R_TRY(FormatToFspPath(std::addressof(sf_path), "%s:/atmosphere/contents/%016" PRIx64 "/exefs.nsp", impl::SdCardFileSystemMountName, program_id.value));
return OpenPackageFileSystemImpl(out, sf_path.str);
R_RETURN(OpenPackageFileSystemImpl(out, sf_path));
}
Result OpenStratosphereCodeFileSystemImpl(std::unique_ptr<fsa::IFileSystem> *out, ncm::ProgramId program_id) {
@@ -120,11 +125,12 @@ namespace ams::fs {
auto &romfs_fs = GetStratosphereRomFsFileSystem();
/* Print a path to the program's package. */
fssrv::sf::Path sf_path;
R_TRY(FspPathPrintf(std::addressof(sf_path), "/atmosphere/contents/%016lX/exefs.nsp", program_id.value));
fs::Path path;
R_TRY(path.InitializeWithFormat("/atmosphere/contents/%016" PRIx64 "/exefs.nsp", program_id.value));
R_TRY(path.Normalize(fs::PathFlags{}));
/* Open the package within stratosphere.romfs. */
R_TRY(romfs_fs.OpenFile(std::addressof(package_file), sf_path.str, fs::OpenMode_Read));
R_TRY(romfs_fs.OpenFile(std::addressof(package_file), path, fs::OpenMode_Read));
}
/* Create a file storage for the program's package. */
@@ -139,7 +145,7 @@ namespace ams::fs {
R_TRY(package_fs->Initialize(package_storage));
*out = std::move(package_fs);
return ResultSuccess();
R_SUCCEED();
}
Result OpenSdCardCodeOrStratosphereCodeOrCodeFileSystemImpl(CodeVerificationData *out_verification_data, std::unique_ptr<fsa::IFileSystem> *out, const char *path, ncm::ProgramId program_id) {
@@ -150,7 +156,7 @@ namespace ams::fs {
R_SUCCEED_IF(R_SUCCEEDED(OpenStratosphereCodeFileSystemImpl(out, program_id)));
/* Otherwise, fall back to a normal code fs. */
return OpenCodeFileSystemImpl(out_verification_data, out, path, program_id);
R_RETURN(OpenCodeFileSystemImpl(out_verification_data, out, path, program_id));
}
Result OpenHblCodeFileSystemImpl(std::unique_ptr<fsa::IFileSystem> *out) {
@@ -159,93 +165,94 @@ namespace ams::fs {
/* Print a path to the hbl package. */
fssrv::sf::Path sf_path;
R_TRY(FspPathPrintf(std::addressof(sf_path), "%s:/%s", impl::SdCardFileSystemMountName, hbl_path[0] == '/' ? hbl_path + 1 : hbl_path));
R_TRY(FormatToFspPath(std::addressof(sf_path), "%s:/%s", impl::SdCardFileSystemMountName, hbl_path[0] == '/' ? hbl_path + 1 : hbl_path));
return OpenPackageFileSystemImpl(out, sf_path.str);
R_RETURN(OpenPackageFileSystemImpl(out, sf_path));
}
Result OpenSdCardFileSystemImpl(std::unique_ptr<fsa::IFileSystem> *out) {
/* Open the SD card. This uses libnx bindings. */
FsFileSystem fs;
R_TRY(fsOpenSdCardFileSystem(std::addressof(fs)));
Result OpenSdCardFileSystemImpl(std::shared_ptr<fsa::IFileSystem> *out) {
/* Open the SD card filesystem. */
auto fsp = impl::GetFileSystemProxyServiceObject();
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
R_TRY(fsp->OpenSdCardFileSystem(std::addressof(fs)));
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<RemoteFileSystem>(fs);
auto fsa = std::make_shared<impl::FileSystemServiceObjectAdapter>(std::move(fs));
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInCodeA());
*out = std::move(fsa);
return ResultSuccess();
R_SUCCEED();
}
class OpenFileOnlyFileSystem : public fsa::IFileSystem, public impl::Newable {
private:
virtual Result DoCommit() override final {
return ResultSuccess();
R_SUCCEED();
}
virtual Result DoOpenDirectory(std::unique_ptr<fsa::IDirectory> *out_dir, const char *path, OpenDirectoryMode mode) override final {
virtual Result DoOpenDirectory(std::unique_ptr<fsa::IDirectory> *out_dir, const fs::Path &path, OpenDirectoryMode mode) override final {
AMS_UNUSED(out_dir, path, mode);
return fs::ResultUnsupportedOperation();
R_THROW(fs::ResultUnsupportedOperation());
}
virtual Result DoGetEntryType(DirectoryEntryType *out, const char *path) override final {
virtual Result DoGetEntryType(DirectoryEntryType *out, const fs::Path &path) override final {
AMS_UNUSED(out, path);
return fs::ResultUnsupportedOperation();
R_THROW(fs::ResultUnsupportedOperation());
}
virtual Result DoCreateFile(const char *path, s64 size, int flags) override final {
virtual Result DoCreateFile(const fs::Path &path, s64 size, int flags) override final {
AMS_UNUSED(path, size, flags);
return fs::ResultUnsupportedOperation();
R_THROW(fs::ResultUnsupportedOperation());
}
virtual Result DoDeleteFile(const char *path) override final {
virtual Result DoDeleteFile(const fs::Path &path) override final {
AMS_UNUSED(path);
return fs::ResultUnsupportedOperation();
R_THROW(fs::ResultUnsupportedOperation());
}
virtual Result DoCreateDirectory(const char *path) override final {
virtual Result DoCreateDirectory(const fs::Path &path) override final {
AMS_UNUSED(path);
return fs::ResultUnsupportedOperation();
R_THROW(fs::ResultUnsupportedOperation());
}
virtual Result DoDeleteDirectory(const char *path) override final {
virtual Result DoDeleteDirectory(const fs::Path &path) override final {
AMS_UNUSED(path);
return fs::ResultUnsupportedOperation();
R_THROW(fs::ResultUnsupportedOperation());
}
virtual Result DoDeleteDirectoryRecursively(const char *path) override final {
virtual Result DoDeleteDirectoryRecursively(const fs::Path &path) override final {
AMS_UNUSED(path);
return fs::ResultUnsupportedOperation();
R_THROW(fs::ResultUnsupportedOperation());
}
virtual Result DoRenameFile(const char *old_path, const char *new_path) override final {
virtual Result DoRenameFile(const fs::Path &old_path, const fs::Path &new_path) override final {
AMS_UNUSED(old_path, new_path);
return fs::ResultUnsupportedOperation();
R_THROW(fs::ResultUnsupportedOperation());
}
virtual Result DoRenameDirectory(const char *old_path, const char *new_path) override final {
virtual Result DoRenameDirectory(const fs::Path &old_path, const fs::Path &new_path) override final {
AMS_UNUSED(old_path, new_path);
return fs::ResultUnsupportedOperation();
R_THROW(fs::ResultUnsupportedOperation());
}
virtual Result DoCleanDirectoryRecursively(const char *path) override final {
virtual Result DoCleanDirectoryRecursively(const fs::Path &path) override final {
AMS_UNUSED(path);
return fs::ResultUnsupportedOperation();
R_THROW(fs::ResultUnsupportedOperation());
}
virtual Result DoGetFreeSpaceSize(s64 *out, const char *path) override final {
virtual Result DoGetFreeSpaceSize(s64 *out, const fs::Path &path) override final {
AMS_UNUSED(out, path);
return fs::ResultUnsupportedOperation();
R_THROW(fs::ResultUnsupportedOperation());
}
virtual Result DoGetTotalSpaceSize(s64 *out, const char *path) override final {
virtual Result DoGetTotalSpaceSize(s64 *out, const fs::Path &path) override final {
AMS_UNUSED(out, path);
return fs::ResultUnsupportedOperation();
R_THROW(fs::ResultUnsupportedOperation());
}
virtual Result DoCommitProvisionally(s64 counter) override final {
AMS_UNUSED(counter);
return fs::ResultUnsupportedOperation();
R_THROW(fs::ResultUnsupportedOperation());
}
};
@@ -261,32 +268,40 @@ namespace ams::fs {
}
/* Open an SD card filesystem. */
std::unique_ptr<fsa::IFileSystem> sd_fs;
std::shared_ptr<fsa::IFileSystem> sd_fs;
if (R_FAILED(OpenSdCardFileSystemImpl(std::addressof(sd_fs)))) {
return;
}
/* Create a redirection filesystem to the relevant content folder. */
char path[fs::EntryNameLengthMax + 1];
util::SNPrintf(path, sizeof(path), "/atmosphere/contents/%016lx/exefs", program_id.value);
auto subdir_fs = std::make_unique<fssystem::SubDirectoryFileSystem>(std::move(sd_fs), path);
auto subdir_fs = std::make_unique<fssystem::SubDirectoryFileSystem>(std::move(sd_fs));
if (subdir_fs == nullptr) {
return;
}
fs::Path path;
R_ABORT_UNLESS(path.InitializeWithFormat("/atmosphere/contents/%016" PRIx64 "/exefs", program_id.value));
R_ABORT_UNLESS(path.Normalize(fs::PathFlags{}));
R_ABORT_UNLESS(subdir_fs->Initialize(path));
m_sd_content_fs.emplace(std::move(subdir_fs));
}
private:
bool IsFileStubbed(const char *path) {
bool IsFileStubbed(const fs::Path &path) {
/* If we don't have an sd content fs, nothing is stubbed. */
if (!m_sd_content_fs) {
return false;
}
/* Create a path representing the stub. */
char stub_path[fs::EntryNameLengthMax + 1];
util::SNPrintf(stub_path, sizeof(stub_path), "%s.stub", path);
fs::Path stub_path;
if (R_FAILED(stub_path.InitializeWithFormat("%s.stub", path.GetString()))) {
return false;
}
if (R_FAILED(stub_path.Normalize(fs::PathFlags{}))) {
return false;
}
/* Query whether we have the file. */
bool has_file;
@@ -297,7 +312,7 @@ namespace ams::fs {
return has_file;
}
virtual Result DoOpenFile(std::unique_ptr<fsa::IFile> *out_file, const char *path, OpenMode mode) override final {
virtual Result DoOpenFile(std::unique_ptr<fsa::IFile> *out_file, const fs::Path &path, OpenMode mode) override final {
/* Only allow opening files with mode = read. */
R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidOpenMode());
@@ -310,7 +325,7 @@ namespace ams::fs {
R_UNLESS(!this->IsFileStubbed(path), fs::ResultPathNotFound());
/* Open a file from the base code fs. */
return m_code_fs.OpenFile(out_file, path, mode);
R_RETURN(m_code_fs.OpenFile(out_file, path, mode));
}
};
@@ -341,10 +356,10 @@ namespace ams::fs {
m_program_id = program_id;
m_initialized = true;
return ResultSuccess();
R_SUCCEED();
}
private:
virtual Result DoOpenFile(std::unique_ptr<fsa::IFile> *out_file, const char *path, OpenMode mode) override final {
virtual Result DoOpenFile(std::unique_ptr<fsa::IFile> *out_file, const fs::Path &path, OpenMode mode) override final {
/* Ensure that we're initialized. */
R_UNLESS(m_initialized, fs::ResultNotInitialized());
@@ -355,81 +370,111 @@ namespace ams::fs {
{
fsa::IFileSystem *ecs = fssystem::GetExternalCodeFileSystem(m_program_id);
if (ecs != nullptr) {
return ecs->OpenFile(out_file, path, mode);
R_RETURN(ecs->OpenFile(out_file, path, mode));
}
}
/* If we're hbl, open from the hbl fs. */
if (m_hbl_fs) {
return m_hbl_fs->OpenFile(out_file, path, mode);
R_RETURN(m_hbl_fs->OpenFile(out_file, path, mode));
}
/* If we're not hbl, fall back to our code filesystem. */
return m_code_fs->OpenFile(out_file, path, mode);
R_RETURN(m_code_fs->OpenFile(out_file, path, mode));
}
};
}
Result MountCode(CodeVerificationData *out, const char *name, const char *path, ncm::ProgramId program_id) {
/* Clear the output. */
std::memset(out, 0, sizeof(*out));
auto mount_impl = [=]() -> Result {
/* Clear the output. */
std::memset(out, 0, sizeof(*out));
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
/* Validate the path isn't null. */
R_UNLESS(path != nullptr, fs::ResultInvalidPath());
/* Validate the path isn't null. */
R_UNLESS(path != nullptr, fs::ResultInvalidPath());
/* Open the code file system. */
std::unique_ptr<fsa::IFileSystem> fsa;
R_TRY(OpenCodeFileSystemImpl(out, std::addressof(fsa), path, program_id));
/* Open the code file system. */
std::unique_ptr<fsa::IFileSystem> fsa;
R_TRY(OpenCodeFileSystemImpl(out, std::addressof(fsa), path, program_id));
/* Register. */
return fsa::Register(name, std::move(fsa));
/* Register. */
R_RETURN(fsa::Register(name, std::move(fsa)));
};
/* Perform the mount. */
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_CODE(name, path, program_id)));
/* Enable access logging. */
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
R_SUCCEED();
}
Result MountCodeForAtmosphereWithRedirection(CodeVerificationData *out, const char *name, const char *path, ncm::ProgramId program_id, bool is_hbl, bool is_specific) {
/* Clear the output. */
std::memset(out, 0, sizeof(*out));
auto mount_impl = [=]() -> Result {
/* Clear the output. */
std::memset(out, 0, sizeof(*out));
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
/* Validate the path isn't null. */
R_UNLESS(path != nullptr, fs::ResultInvalidPath());
/* Validate the path isn't null. */
R_UNLESS(path != nullptr, fs::ResultInvalidPath());
/* Create an AtmosphereCodeFileSystem. */
auto ams_code_fs = std::make_unique<AtmosphereCodeFileSystem>();
R_UNLESS(ams_code_fs != nullptr, fs::ResultAllocationFailureInCodeA());
/* Create an AtmosphereCodeFileSystem. */
auto ams_code_fs = std::make_unique<AtmosphereCodeFileSystem>();
R_UNLESS(ams_code_fs != nullptr, fs::ResultAllocationFailureInCodeA());
/* Initialize the code file system. */
R_TRY(ams_code_fs->Initialize(out, path, program_id, is_hbl, is_specific));
/* Initialize the code file system. */
R_TRY(ams_code_fs->Initialize(out, path, program_id, is_hbl, is_specific));
/* Register. */
return fsa::Register(name, std::move(ams_code_fs));
/* Register. */
R_RETURN(fsa::Register(name, std::move(ams_code_fs)));
};
/* Perform the mount. */
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_CODE(name, path, program_id)));
/* Enable access logging. */
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
R_SUCCEED();
}
Result MountCodeForAtmosphere(CodeVerificationData *out, const char *name, const char *path, ncm::ProgramId program_id) {
/* Clear the output. */
std::memset(out, 0, sizeof(*out));
auto mount_impl = [=]() -> Result {
/* Clear the output. */
std::memset(out, 0, sizeof(*out));
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
/* Validate the path isn't null. */
R_UNLESS(path != nullptr, fs::ResultInvalidPath());
/* Validate the path isn't null. */
R_UNLESS(path != nullptr, fs::ResultInvalidPath());
/* Open the code file system. */
std::unique_ptr<fsa::IFileSystem> fsa;
R_TRY(OpenSdCardCodeOrStratosphereCodeOrCodeFileSystemImpl(out, std::addressof(fsa), path, program_id));
/* Open the code file system. */
std::unique_ptr<fsa::IFileSystem> fsa;
R_TRY(OpenSdCardCodeOrStratosphereCodeOrCodeFileSystemImpl(out, std::addressof(fsa), path, program_id));
/* Create a wrapper fs. */
auto wrap_fsa = std::make_unique<SdCardRedirectionCodeFileSystem>(std::move(fsa), program_id, false);
R_UNLESS(wrap_fsa != nullptr, fs::ResultAllocationFailureInCodeA());
/* Create a wrapper fs. */
auto wrap_fsa = std::make_unique<SdCardRedirectionCodeFileSystem>(std::move(fsa), program_id, false);
R_UNLESS(wrap_fsa != nullptr, fs::ResultAllocationFailureInCodeA());
/* Register. */
return fsa::Register(name, std::move(wrap_fsa));
/* Register. */
R_RETURN(fsa::Register(name, std::move(wrap_fsa)));
};
/* Perform the mount. */
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_CODE(name, path, program_id)));
/* Enable access logging. */
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
R_SUCCEED();
}
}

View File

@@ -15,6 +15,8 @@
*/
#include <stratosphere.hpp>
#include "fsa/fs_mount_utils.hpp"
#include "impl/fs_file_system_proxy_service_object.hpp"
#include "impl/fs_file_system_service_object_adapter.hpp"
namespace ams::fs {
@@ -30,45 +32,75 @@ namespace ams::fs {
}
}
Result MountContentImpl(const char *name, const char *path, u64 id, impl::FileSystemProxyType type) {
/* Open a filesystem using libnx bindings. */
FsFileSystem fs;
R_TRY(fsOpenFileSystemWithId(std::addressof(fs), id, static_cast<::FsFileSystemType>(type), path));
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<RemoteFileSystem>(fs);
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInContentA());
/* Register. */
return fsa::Register(name, std::move(fsa));
}
Result MountContent(const char *name, const char *path, u64 id, ContentType content_type) {
Result MountContentImpl(const char *name, const char *path, u64 id, ContentType type) {
/* Validate the mount name. */
R_TRY(impl::CheckMountNameAllowingReserved(name));
/* Validate the path. */
R_UNLESS(path != nullptr, fs::ResultInvalidPath());
/* Mount the content. */
return MountContentImpl(name, path, id, ConvertToFileSystemProxyType(content_type));
/* Convert the path for fsp. */
fssrv::sf::FspPath sf_path;
R_TRY(fs::ConvertToFspPath(std::addressof(sf_path), path));
/* Open the filesystem. */
auto fsp = impl::GetFileSystemProxyServiceObject();
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
R_TRY(fsp->OpenFileSystemWithId(std::addressof(fs), sf_path, id, ConvertToFileSystemProxyType(type)));
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInContentA());
/* Register. */
R_RETURN(fsa::Register(name, std::move(fsa)));
}
}
Result MountContent(const char *name, const char *path, ContentType content_type) {
/* This API only supports mounting Meta content. */
AMS_ABORT_UNLESS(content_type == ContentType_Meta);
auto mount_impl = [=]() -> Result {
/* This API only supports mounting Meta content. */
R_UNLESS(content_type == ContentType_Meta, fs::ResultInvalidArgument());
return MountContent(name, path, ncm::InvalidProgramId, content_type);
R_RETURN(MountContentImpl(name, path, ncm::InvalidProgramId.value, content_type));
};
/* Perform the mount. */
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_CONTENT_PATH(name, path, content_type)));
/* Enable access logging. */
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
R_SUCCEED();
}
Result MountContent(const char *name, const char *path, ncm::ProgramId id, ContentType content_type) {
return MountContent(name, path, id.value, content_type);
auto mount_impl = [=]() -> Result {
R_RETURN(MountContentImpl(name, path, id.value, content_type));
};
/* Perform the mount. */
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_CONTENT_PATH_AND_PROGRAM_ID(name, path, id, content_type)));
/* Enable access logging. */
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
R_SUCCEED();
}
Result MountContent(const char *name, const char *path, ncm::DataId id, ContentType content_type) {
return MountContent(name, path, id.value, content_type);
auto mount_impl = [=]() -> Result {
R_RETURN(MountContentImpl(name, path, id.value, content_type));
};
/* Perform the mount. */
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_CONTENT_PATH_AND_DATA_ID(name, path, id, content_type)));
/* Enable access logging. */
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
R_SUCCEED();
}
}

View File

@@ -15,6 +15,8 @@
*/
#include <stratosphere.hpp>
#include "fsa/fs_mount_utils.hpp"
#include "impl/fs_file_system_service_object_adapter.hpp"
#include "impl/fs_file_system_proxy_service_object.hpp"
namespace ams::fs {
@@ -28,7 +30,7 @@ namespace ams::fs {
virtual Result GenerateCommonMountName(char *dst, size_t dst_size) override {
/* Determine how much space we need. */
const size_t needed_size = strnlen(GetContentStorageMountName(id), MountNameLengthMax) + 2;
const size_t needed_size = util::Strnlen(GetContentStorageMountName(id), MountNameLengthMax) + 2;
AMS_ABORT_UNLESS(dst_size >= needed_size);
/* Generate the name. */
@@ -56,43 +58,55 @@ namespace ams::fs {
}
Result MountContentStorage(const char *name, ContentStorageId id) {
/* Validate the mount name. */
R_TRY(impl::CheckMountNameAllowingReserved(name));
auto mount_impl = [=]() -> Result {
/* Validate the mount name. */
R_TRY(impl::CheckMountNameAllowingReserved(name));
/* It can take some time for the system partition to be ready (if it's on the SD card). */
/* Thus, we will retry up to 10 times, waiting one second each time. */
constexpr size_t MaxRetries = 10;
constexpr auto RetryInterval = TimeSpan::FromSeconds(1);
/* It can take some time for the system partition to be ready (if it's on the SD card). */
/* Thus, we will retry up to 10 times, waiting one second each time. */
constexpr size_t MaxRetries = 10;
constexpr auto RetryInterval = TimeSpan::FromSeconds(1);
/* Mount the content storage, use libnx bindings. */
::FsFileSystem fs;
for (size_t i = 0; i < MaxRetries; i++) {
/* Try to open the filesystem. */
R_TRY_CATCH(fsOpenContentStorageFileSystem(std::addressof(fs), static_cast<::FsContentStorageId>(id))) {
R_CATCH(fs::ResultSystemPartitionNotReady) {
if (i < MaxRetries - 1) {
os::SleepThread(RetryInterval);
continue;
} else {
return fs::ResultSystemPartitionNotReady();
/* Mount the content storage */
auto fsp = impl::GetFileSystemProxyServiceObject();
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
for (size_t i = 0; i < MaxRetries; i++) {
/* Try to open the filesystem. */
R_TRY_CATCH(fsp->OpenContentStorageFileSystem(std::addressof(fs), static_cast<u32>(id))) {
R_CATCH(fs::ResultSystemPartitionNotReady) {
if (i < MaxRetries - 1) {
os::SleepThread(RetryInterval);
continue;
} else {
R_THROW(fs::ResultSystemPartitionNotReady());
}
}
}
} R_END_TRY_CATCH;
} R_END_TRY_CATCH;
/* The filesystem was opened successfully. */
break;
}
/* The filesystem was opened successfully. */
break;
}
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<RemoteFileSystem>(fs);
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInContentStorageA());
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInContentStorageA());
/* Allocate a new mountname generator. */
auto generator = std::make_unique<ContentStorageCommonMountNameGenerator>(id);
R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInContentStorageB());
/* Allocate a new mountname generator. */
auto generator = std::make_unique<ContentStorageCommonMountNameGenerator>(id);
R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInContentStorageB());
/* Register. */
return fsa::Register(name, std::move(fsa), std::move(generator));
/* Register. */
R_RETURN(fsa::Register(name, std::move(fsa), std::move(generator)));
};
/* Perform the mount. */
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_CONTENT_STORAGE(name, id)));
/* Enable access logging. */
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
R_SUCCEED();
}
}

View File

@@ -15,19 +15,27 @@
*/
#include <stratosphere.hpp>
#include "fsa/fs_mount_utils.hpp"
#include "impl/fs_file_system_proxy_service_object.hpp"
#include "impl/fs_storage_service_object_adapter.hpp"
namespace ams::fs::impl {
namespace {
Result OpenDataStorageByDataId(std::unique_ptr<ams::fs::IStorage> *out, ncm::DataId data_id, ncm::StorageId storage_id) {
/* Open storage using libnx bindings. */
::FsStorage s;
R_TRY_CATCH(fsOpenDataStorageByDataId(std::addressof(s), data_id.value, static_cast<::NcmStorageId>(storage_id))) {
Result OpenDataStorageByDataIdImpl(sf::SharedPointer<fssrv::sf::IStorage> *out, ncm::DataId data_id, ncm::StorageId storage_id) {
auto fsp = impl::GetFileSystemProxyServiceObject();
R_TRY_CATCH(fsp->OpenDataStorageByDataId(out, data_id, static_cast<u8>(storage_id))) {
R_CONVERT(ncm::ResultContentMetaNotFound, fs::ResultTargetNotFound());
} R_END_TRY_CATCH;
R_SUCCEED();
}
auto storage = std::make_unique<RemoteStorage>(s);
Result OpenDataStorageByDataId(std::unique_ptr<ams::fs::IStorage> *out, ncm::DataId data_id, ncm::StorageId storage_id) {
/* Open storage. */
sf::SharedPointer<fssrv::sf::IStorage> s;
AMS_FS_R_TRY(OpenDataStorageByDataIdImpl(std::addressof(s), data_id, storage_id));
auto storage = std::make_unique<impl::StorageServiceObjectAdapter>(std::move(s));
R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInDataA());
*out = std::move(storage);
@@ -48,13 +56,13 @@ namespace ams::fs::impl {
}
Result QueryMountDataCacheSize(size_t *out, ncm::DataId data_id, ncm::StorageId storage_id) {
R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
AMS_FS_R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
std::unique_ptr<fs::IStorage> storage;
R_TRY(OpenDataStorageByDataId(std::addressof(storage), data_id, storage_id));
AMS_FS_R_TRY(OpenDataStorageByDataId(std::addressof(storage), data_id, storage_id));
size_t size = 0;
R_TRY(RomFsFileSystem::GetRequiredWorkingMemorySize(std::addressof(size), storage.get()));
AMS_FS_R_TRY(RomFsFileSystem::GetRequiredWorkingMemorySize(std::addressof(size), storage.get()));
constexpr size_t MinimumCacheSize = 32;
*out = std::max(size, MinimumCacheSize);
@@ -63,25 +71,25 @@ namespace ams::fs::impl {
Result MountData(const char *name, ncm::DataId data_id, ncm::StorageId storage_id) {
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
AMS_FS_R_TRY(impl::CheckMountName(name));
return MountDataImpl(name, data_id, storage_id, nullptr, 0, false, false, false);
}
Result MountData(const char *name, ncm::DataId data_id, ncm::StorageId storage_id, void *cache_buffer, size_t cache_size) {
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
AMS_FS_R_TRY(impl::CheckMountName(name));
R_UNLESS(cache_buffer != nullptr, fs::ResultNullptrArgument());
AMS_FS_R_UNLESS(cache_buffer != nullptr, fs::ResultNullptrArgument());
return MountDataImpl(name, data_id, storage_id, cache_buffer, cache_size, true, false, false);
}
Result MountData(const char *name, ncm::DataId data_id, ncm::StorageId storage_id, void *cache_buffer, size_t cache_size, bool use_data_cache, bool use_path_cache) {
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
AMS_FS_R_TRY(impl::CheckMountName(name));
R_UNLESS(cache_buffer != nullptr, fs::ResultNullptrArgument());
AMS_FS_R_UNLESS(cache_buffer != nullptr, fs::ResultNullptrArgument());
return MountDataImpl(name, data_id, storage_id, cache_buffer, cache_size, true, use_data_cache, use_path_cache);
}

View File

@@ -15,6 +15,8 @@
*/
#include <stratosphere.hpp>
#include "fsa/fs_mount_utils.hpp"
#include "impl/fs_file_system_proxy_service_object.hpp"
#include "impl/fs_file_system_service_object_adapter.hpp"
namespace ams::fs {
@@ -26,12 +28,13 @@ namespace ams::fs {
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
/* Open the filesystem, use libnx bindings. */
::FsFileSystem fs;
R_TRY(fsOpenSaveDataFileSystem(std::addressof(fs), static_cast<::FsSaveDataSpaceId>(DeviceSaveDataSpaceId), reinterpret_cast<const ::FsSaveDataAttribute *>(std::addressof(attribute))));
/* Open the filesystem. */
auto fsp = impl::GetFileSystemProxyServiceObject();
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
R_TRY(fsp->OpenSaveDataFileSystem(std::addressof(fs), static_cast<u8>(DeviceSaveDataSpaceId), attribute));
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<RemoteFileSystem>(fs);
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInDeviceSaveDataA());
/* Register. */
@@ -41,11 +44,27 @@ namespace ams::fs {
}
Result MountDeviceSaveData(const char *name) {
return MountDeviceSaveDataImpl(name, SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::Device, InvalidUserId, InvalidSystemSaveDataId));
const auto attr = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::Device, InvalidUserId, InvalidSystemSaveDataId);
/* Perform the mount. */
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT(MountDeviceSaveDataImpl(name, attr), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT, name));
/* Enable access logging. */
AMS_FS_IMPL_ACCESS_LOG_FS_ACCESSOR_ENABLE(name);
R_SUCCEED();
}
Result MountDeviceSaveData(const char *name, const ncm::ApplicationId application_id) {
return MountDeviceSaveDataImpl(name, SaveDataAttribute::Make(application_id, SaveDataType::Device, InvalidUserId, InvalidSystemSaveDataId));
const auto attr = SaveDataAttribute::Make(application_id, SaveDataType::Device, InvalidUserId, InvalidSystemSaveDataId);
/* Perform the mount. */
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT(MountDeviceSaveDataImpl(name, attr), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_DEVICE_SAVE_DATA_APPLICATION_ID(name, application_id)));
/* Enable access logging. */
AMS_FS_IMPL_ACCESS_LOG_FS_ACCESSOR_ENABLE(name);
R_SUCCEED();
}
}

View File

@@ -19,24 +19,35 @@
namespace ams::fs {
Result EnsureDirectoryRecursively(const char *path) {
Result EnsureDirectory(const char *path) {
/* Get the filesystem accessor and sub path. */
impl::FileSystemAccessor *accessor;
const char *sub_path;
R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path));
/* Set up the true sub path. */
fs::Path sub_fs_path;
R_TRY(accessor->SetUpPath(std::addressof(sub_fs_path), sub_path));
/* Use the system implementation. */
return fssystem::EnsureDirectoryRecursively(accessor->GetRawFileSystemUnsafe(), sub_path);
return fssystem::EnsureDirectory(accessor->GetRawFileSystemUnsafe(), sub_fs_path);
}
Result EnsureParentDirectoryRecursively(const char *path) {
Result EnsureParentDirectory(const char *path) {
/* Get the filesystem accessor and sub path. */
impl::FileSystemAccessor *accessor;
const char *sub_path;
R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path));
/* Set up the true sub path. */
fs::Path sub_fs_path;
R_TRY(accessor->SetUpPath(std::addressof(sub_fs_path), sub_path));
/* Convert the path to the parent directory path. */
R_TRY(sub_fs_path.RemoveChild());
/* Use the system implementation. */
return fssystem::EnsureParentDirectoryRecursively(accessor->GetRawFileSystemUnsafe(), sub_path);
return fssystem::EnsureDirectory(accessor->GetRawFileSystemUnsafe(), sub_fs_path);
}
Result HasFile(bool *out, const char *path) {
@@ -45,7 +56,11 @@ namespace ams::fs {
const char *sub_path;
R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path));
return fssystem::HasFile(out, accessor->GetRawFileSystemUnsafe(), sub_path);
/* Set up the true sub path. */
fs::Path sub_fs_path;
R_TRY(accessor->SetUpPath(std::addressof(sub_fs_path), sub_path));
return fssystem::HasFile(out, accessor->GetRawFileSystemUnsafe(), sub_fs_path);
}
Result HasDirectory(bool *out, const char *path) {
@@ -54,7 +69,11 @@ namespace ams::fs {
const char *sub_path;
R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path));
return fssystem::HasDirectory(out, accessor->GetRawFileSystemUnsafe(), sub_path);
/* Set up the true sub path. */
fs::Path sub_fs_path;
R_TRY(accessor->SetUpPath(std::addressof(sub_fs_path), sub_path));
return fssystem::HasDirectory(out, accessor->GetRawFileSystemUnsafe(), sub_fs_path);
}
}

View File

@@ -15,6 +15,8 @@
*/
#include <stratosphere.hpp>
#include "fsa/fs_mount_utils.hpp"
#include "impl/fs_file_system_proxy_service_object.hpp"
#include "impl/fs_file_system_service_object_adapter.hpp"
namespace ams::fs {
@@ -38,7 +40,7 @@ namespace ams::fs {
virtual Result GenerateCommonMountName(char *dst, size_t dst_size) override {
/* Determine how much space we need. */
const size_t needed_size = strnlen(impl::GameCardFileSystemMountName, MountNameLengthMax) + strnlen(GetGameCardMountNameSuffix(m_partition), MountNameLengthMax) + sizeof(GameCardHandle) * 2 + 2;
const size_t needed_size = util::Strnlen(impl::GameCardFileSystemMountName, MountNameLengthMax) + util::Strnlen(GetGameCardMountNameSuffix(m_partition), MountNameLengthMax) + sizeof(GameCardHandle) * 2 + 2;
AMS_ABORT_UNLESS(dst_size >= needed_size);
/* Generate the name. */
@@ -53,36 +55,49 @@ namespace ams::fs {
}
Result GetGameCardHandle(GameCardHandle *out) {
/* TODO: fs::DeviceOperator */
/* Open a DeviceOperator. */
::FsDeviceOperator d;
R_TRY(fsOpenDeviceOperator(std::addressof(d)));
ON_SCOPE_EXIT { fsDeviceOperatorClose(std::addressof(d)); };
auto fsp = impl::GetFileSystemProxyServiceObject();
/* Open a device operator. */
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
/* Get the handle. */
static_assert(sizeof(GameCardHandle) == sizeof(::FsGameCardHandle));
return fsDeviceOperatorGetGameCardHandle(std::addressof(d), reinterpret_cast<::FsGameCardHandle *>(out));
u32 handle;
AMS_FS_R_TRY(device_operator->GetGameCardHandle(std::addressof(handle)));
*out = handle;
R_SUCCEED();
}
Result MountGameCardPartition(const char *name, GameCardHandle handle, GameCardPartition partition) {
/* Validate the mount name. */
R_TRY(impl::CheckMountNameAllowingReserved(name));
auto mount_impl = [=]() -> Result {
/* Validate the mount name. */
R_TRY(impl::CheckMountNameAllowingReserved(name));
/* Open gamecard filesystem. This uses libnx bindings. */
::FsFileSystem fs;
const ::FsGameCardHandle _hnd = {handle};
R_TRY(fsOpenGameCardFileSystem(std::addressof(fs), std::addressof(_hnd), static_cast<::FsGameCardPartition>(partition)));
/* Open the gamecard filesystem. */
auto fsp = impl::GetFileSystemProxyServiceObject();
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
R_TRY(fsp->OpenGameCardFileSystem(std::addressof(fs), static_cast<u32>(handle), static_cast<u32>(partition)));
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<RemoteFileSystem>(fs);
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInGameCardC());
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInGameCardC());
/* Allocate a new mountname generator. */
auto generator = std::make_unique<GameCardCommonMountNameGenerator>(handle, partition);
R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInGameCardD());
/* Allocate a new mountname generator. */
auto generator = std::make_unique<GameCardCommonMountNameGenerator>(handle, partition);
R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInGameCardD());
/* Register. */
return fsa::Register(name, std::move(fsa), std::move(generator));
/* Register. */
R_RETURN(fsa::Register(name, std::move(fsa), std::move(generator)));
};
/* Perform the mount. */
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_GAME_CARD_PARTITION(name, handle, partition)));
/* Enable access logging. */
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
R_SUCCEED();
}
}

View File

@@ -0,0 +1,241 @@
/*
* Copyright (c) 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 "fsa/fs_mount_utils.hpp"
#include "impl/fs_file_system_proxy_service_object.hpp"
#include "impl/fs_file_system_service_object_adapter.hpp"
namespace ams::fs {
namespace {
class HostRootCommonMountNameGenerator : public fsa::ICommonMountNameGenerator, public impl::Newable {
public:
explicit HostRootCommonMountNameGenerator() { /* ... */ }
virtual Result GenerateCommonMountName(char *dst, size_t dst_size) override {
/* Determine how much space we need. */
constexpr size_t RequiredNameBufferSizeSize = AMS_FS_IMPL_HOST_ROOT_FILE_SYSTEM_MOUNT_NAME_LEN + 1 + 1;
AMS_ASSERT(dst_size >= RequiredNameBufferSizeSize);
AMS_UNUSED(RequiredNameBufferSizeSize);
/* Generate the name. */
const auto size = util::SNPrintf(dst, dst_size, AMS_FS_IMPL_HOST_ROOT_FILE_SYSTEM_MOUNT_NAME ":");
AMS_ASSERT(static_cast<size_t>(size) == RequiredNameBufferSizeSize - 1);
AMS_UNUSED(size);
R_SUCCEED();
}
};
class HostCommonMountNameGenerator : public fsa::ICommonMountNameGenerator, public impl::Newable {
private:
char m_path[fs::EntryNameLengthMax + 1];
public:
HostCommonMountNameGenerator(const char *path) {
util::Strlcpy<char>(m_path, path, sizeof(m_path));
}
virtual Result GenerateCommonMountName(char *dst, size_t dst_size) override {
/* Determine how much space we need. */
const size_t required_size = AMS_FS_IMPL_HOST_ROOT_FILE_SYSTEM_MOUNT_NAME_LEN + 1 + util::Strnlen<char>(m_path, sizeof(m_path)) + 1; /* @Host:%s */
R_UNLESS(dst_size >= required_size, fs::ResultTooLongPath());
/* Generate the name. */
const auto size = util::SNPrintf(dst, dst_size, AMS_FS_IMPL_HOST_ROOT_FILE_SYSTEM_MOUNT_NAME ":%s", m_path);
AMS_ASSERT(static_cast<size_t>(size) == required_size - 1);
AMS_UNUSED(size);
R_SUCCEED();
}
};
Result OpenHostFileSystemImpl(std::unique_ptr<fs::fsa::IFileSystem> *out, const fssrv::sf::FspPath &path, const MountHostOption &option) {
/* Open the filesystem. */
auto fsp = impl::GetFileSystemProxyServiceObject();
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
if (option != MountHostOption::None) {
R_TRY(fsp->OpenHostFileSystemWithOption(std::addressof(fs), path, option._value));
} else {
R_TRY(fsp->OpenHostFileSystem(std::addressof(fs), path));
}
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInHostA());
/* Set the output. */
*out = std::move(fsa);
R_SUCCEED();
}
Result PreMountHost(std::unique_ptr<fs::HostCommonMountNameGenerator> *out, const char *name, const char *path) {
/* Check pre-conditions. */
AMS_ASSERT(out != nullptr);
/* Check the mount name. */
R_TRY(impl::CheckMountName(name));
/* Check that path is valid. */
R_UNLESS(path != nullptr, fs::ResultNullptrArgument());
/* Create a new HostCommonMountNameGenerator. */
*out = std::make_unique<HostCommonMountNameGenerator>(path);
R_UNLESS(out->get() != nullptr, fs::ResultAllocationFailureInHostB());
R_SUCCEED();
}
}
namespace impl {
Result OpenHostFileSystem(std::unique_ptr<fs::fsa::IFileSystem> *out, const char *name, const char *path, const fs::MountHostOption &option) {
/* Validate arguments. */
R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
R_UNLESS(name != nullptr, fs::ResultNullptrArgument());
R_UNLESS(path != nullptr, fs::ResultNullptrArgument());
/* Check mount name isn't windows path or reserved. */
R_UNLESS(!fs::IsWindowsDrive(name), fs::ResultInvalidMountName());
R_UNLESS(!fs::impl::IsReservedMountName(name), fs::ResultInvalidMountName());
/* Convert the path for fsp. */
fssrv::sf::FspPath sf_path;
R_TRY(fs::ConvertToFspPath(std::addressof(sf_path), path));
/* Ensure that the path doesn't correspond to the root. */
if (sf_path.str[0] == 0) {
sf_path.str[0] = '.';
sf_path.str[1] = 0;
}
/* Open the host file system. */
R_RETURN(OpenHostFileSystemImpl(out, sf_path, option));
}
}
Result MountHost(const char *name, const char *root_path) {
/* Pre-mount host. */
std::unique_ptr<fs::HostCommonMountNameGenerator> generator;
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT_UNLESS_R_SUCCEEDED(PreMountHost(std::addressof(generator), name, root_path), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_HOST(name, root_path)));
/* Open the filesystem. */
std::unique_ptr<fs::fsa::IFileSystem> fsa;
R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT_UNLESS_R_SUCCEEDED(impl::OpenHostFileSystem(std::addressof(fsa), name, root_path, MountHostOption::None), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_HOST(name, root_path)));
/* Declare registration helper. */
auto register_impl = [&]() -> Result {
/* Register. */
R_RETURN(fsa::Register(name, std::move(fsa), std::move(generator)));
};
/* Mount the filesystem. */
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT(register_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_HOST(name, root_path)));
/* Enable access logging. */
AMS_FS_IMPL_ACCESS_LOG_FS_ACCESSOR_ENABLE(name);
R_SUCCEED();
}
Result MountHost(const char *name, const char *root_path, const MountHostOption &option) {
/* Pre-mount host. */
std::unique_ptr<fs::HostCommonMountNameGenerator> generator;
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT_UNLESS_R_SUCCEEDED(PreMountHost(std::addressof(generator), name, root_path), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_HOST_WITH_OPTION(name, root_path, option)));
/* Open the filesystem. */
std::unique_ptr<fs::fsa::IFileSystem> fsa;
R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT_UNLESS_R_SUCCEEDED(impl::OpenHostFileSystem(std::addressof(fsa), name, root_path, option), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_HOST_WITH_OPTION(name, root_path, option)));
/* Declare registration helper. */
auto register_impl = [&]() -> Result {
/* Register. */
R_RETURN(fsa::Register(name, std::move(fsa), std::move(generator)));
};
/* Mount the filesystem. */
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT(register_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_HOST_WITH_OPTION(name, root_path, option)));
/* Enable access logging. */
AMS_FS_IMPL_ACCESS_LOG_FS_ACCESSOR_ENABLE(name);
R_SUCCEED();
}
Result MountHostRoot() {
/* Create host root path. */
fssrv::sf::FspPath sf_path;
sf_path.str[0] = 0;
/* Open the filesystem. */
std::unique_ptr<fs::fsa::IFileSystem> fsa;
R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT_UNLESS_R_SUCCEEDED(OpenHostFileSystemImpl(std::addressof(fsa), sf_path, MountHostOption::None), impl::HostRootFileSystemMountName, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_HOST_ROOT()));
/* Declare registration helper. */
auto register_impl = [&]() -> Result {
/* Allocate a new mountname generator. */
auto generator = std::make_unique<HostRootCommonMountNameGenerator>();
R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInHostC());
/* Register. */
R_RETURN(fsa::Register(impl::HostRootFileSystemMountName, std::move(fsa), std::move(generator)));
};
/* Mount the filesystem. */
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT(register_impl(), impl::HostRootFileSystemMountName, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_HOST_ROOT()));
/* Enable access logging. */
AMS_FS_IMPL_ACCESS_LOG_FS_ACCESSOR_ENABLE(impl::HostRootFileSystemMountName);
R_SUCCEED();
}
Result MountHostRoot(const MountHostOption &option) {
/* Create host root path. */
fssrv::sf::FspPath sf_path;
sf_path.str[0] = 0;
/* Open the filesystem. */
std::unique_ptr<fs::fsa::IFileSystem> fsa;
R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT_UNLESS_R_SUCCEEDED(OpenHostFileSystemImpl(std::addressof(fsa), sf_path, option), impl::HostRootFileSystemMountName, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_HOST_ROOT_WITH_OPTION(option)));
/* Declare registration helper. */
auto register_impl = [&]() -> Result {
/* Allocate a new mountname generator. */
auto generator = std::make_unique<HostRootCommonMountNameGenerator>();
R_UNLESS(generator != nullptr, fs::ResultAllocationFailureInHostC());
/* Register. */
R_RETURN(fsa::Register(impl::HostRootFileSystemMountName, std::move(fsa), std::move(generator)));
};
/* Mount the filesystem. */
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT(register_impl(), impl::HostRootFileSystemMountName, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_HOST_ROOT_WITH_OPTION(option)));
/* Enable access logging. */
AMS_FS_IMPL_ACCESS_LOG_FS_ACCESSOR_ENABLE(impl::HostRootFileSystemMountName);
R_SUCCEED();
}
void UnmountHostRoot() {
return Unmount(impl::HostRootFileSystemMountName);
}
}

View File

@@ -15,23 +15,36 @@
*/
#include <stratosphere.hpp>
#include "fsa/fs_mount_utils.hpp"
#include "impl/fs_file_system_proxy_service_object.hpp"
#include "impl/fs_file_system_service_object_adapter.hpp"
namespace ams::fs {
Result MountImageDirectory(const char *name, ImageDirectoryId id) {
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
auto mount_impl = [=]() -> Result {
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
/* Open the image directory. This uses libnx bindings. */
FsFileSystem fs;
R_TRY(fsOpenImageDirectoryFileSystem(std::addressof(fs), static_cast<::FsImageDirectoryId>(id)));
/* Open the image directory. */
auto fsp = impl::GetFileSystemProxyServiceObject();
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
R_TRY(fsp->OpenImageDirectoryFileSystem(std::addressof(fs), static_cast<u32>(id)));
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<RemoteFileSystem>(fs);
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInImageDirectoryA());
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInImageDirectoryA());
/* Register. */
return fsa::Register(name, std::move(fsa));
/* Register. */
R_RETURN(fsa::Register(name, std::move(fsa)));
};
/* Perform the mount. */
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_IMAGE_DIRECTORY(name, id)));
/* Enable access logging. */
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
R_SUCCEED();
}
}

View File

@@ -1,570 +0,0 @@
/*
* Copyright (c) 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 {
namespace {
Result CheckSharedName(const char *path, int len) {
if (len == 1) {
R_UNLESS(path[0] != StringTraits::Dot, fs::ResultInvalidPathFormat());
} else if (len == 2) {
R_UNLESS(path[0] != StringTraits::Dot || path[1] != StringTraits::Dot, fs::ResultInvalidPathFormat());
}
return ResultSuccess();
}
Result ParseWindowsPath(const char **out_path, char *out, size_t *out_windows_path_len, bool *out_normalized, const char *path, size_t max_out_size, bool has_mount_name) {
/* Prepare to parse. */
const char * const path_start = path;
if (out_normalized != nullptr) {
*out_normalized = true;
}
/* Handle start of path. */
bool skipped_mount = false;
auto prefix_len = 0;
if (has_mount_name) {
if (PathNormalizer::IsSeparator(path[0]) && path[1] == StringTraits::AlternateDirectorySeparator && path[2] == StringTraits::AlternateDirectorySeparator) {
path += 1;
prefix_len = 1;
} else {
/* Advance past separators. */
while (PathNormalizer::IsSeparator(path[0])) {
++path;
}
if (path != path_start) {
if (path - path_start == 1 || IsWindowsDrive(path)) {
prefix_len = 1;
} else {
if (path - path_start > 2 && out_normalized != nullptr) {
*out_normalized = false;
return ResultSuccess();
}
path -= 2;
skipped_mount = true;
}
}
}
} else if (PathNormalizer::IsSeparator(path[0]) && !IsUnc(path)) {
path += 1;
prefix_len = 1;
}
/* Parse the path. */
const char *trimmed_path = path_start;
if (IsWindowsDrive(path)) {
/* Find the first separator. */
int i;
for (i = 2; !PathNormalizer::IsNullTerminator(path[i]); ++i) {
if (PathNormalizer::IsAnySeparator(path[i])) {
break;
}
}
trimmed_path = path + i;
const size_t win_path_len = trimmed_path - path_start;
if (out != nullptr) {
R_UNLESS(win_path_len <= max_out_size, fs::ResultTooLongPath());
std::memcpy(out, path_start, win_path_len);
}
*out_path = trimmed_path;
*out_windows_path_len = win_path_len;
} else if (IsUnc(path)) {
if (PathNormalizer::IsAnySeparator(path[2])) {
AMS_ASSERT(!has_mount_name);
return fs::ResultInvalidPathFormat();
}
int cur_part_ofs = 0;
bool needs_sep_fix = false;
for (auto i = 2; !PathNormalizer::IsNullTerminator(path[i]); ++i) {
if (cur_part_ofs == 0 && path[i] == StringTraits::AlternateDirectorySeparator) {
needs_sep_fix = true;
if (out_normalized != nullptr) {
*out_normalized = false;
return ResultSuccess();
}
}
if (PathNormalizer::IsAnySeparator(path[i])) {
if (path[i] == StringTraits::AlternateDirectorySeparator) {
needs_sep_fix = true;
}
if (cur_part_ofs != 0) {
break;
}
R_UNLESS(!PathNormalizer::IsSeparator(path[i + 1]), fs::ResultInvalidPathFormat());
R_TRY(CheckSharedName(path + 2, i - 2));
cur_part_ofs = i + 1;
}
if (path[i] == '$' || path[i] == StringTraits::DriveSeparator) {
R_UNLESS(cur_part_ofs != 0, fs::ResultInvalidCharacter());
R_UNLESS(PathNormalizer::IsAnySeparator(path[i + 1]) || PathNormalizer::IsNullTerminator(path[i + 1]), fs::ResultInvalidPathFormat());
trimmed_path = path + i + 1;
break;
}
}
if (trimmed_path == path_start) {
int tr_part_ofs = 0;
int i;
for (i = 2; !PathNormalizer::IsNullTerminator(path[i]); ++i) {
if (PathNormalizer::IsAnySeparator(path[i])) {
if (tr_part_ofs != 0) {
R_TRY(CheckSharedName(path + tr_part_ofs, i - tr_part_ofs));
trimmed_path = path + i;
break;
}
R_UNLESS(!PathNormalizer::IsSeparator(path[i + 1]), fs::ResultInvalidPathFormat());
R_TRY(CheckSharedName(path + 2, i - 2));
cur_part_ofs = i + 1;
}
}
if (tr_part_ofs != 0 && trimmed_path == path_start) {
R_TRY(CheckSharedName(path + tr_part_ofs, i - tr_part_ofs));
trimmed_path = path + i;
}
}
const size_t win_path_len = trimmed_path - path;
const bool prepend_sep = prefix_len != 0 || skipped_mount;
if (out != nullptr) {
R_UNLESS(win_path_len <= max_out_size, fs::ResultTooLongPath());
if (prepend_sep) {
*(out++) = StringTraits::DirectorySeparator;
}
std::memcpy(out, path, win_path_len);
out[0] = StringTraits::AlternateDirectorySeparator;
out[1] = StringTraits::AlternateDirectorySeparator;
if (needs_sep_fix) {
for (size_t i = 2; i < win_path_len; ++i) {
if (PathNormalizer::IsSeparator(out[i])) {
out[i] = StringTraits::AlternateDirectorySeparator;
}
}
}
}
*out_path = trimmed_path;
*out_windows_path_len = win_path_len + (prepend_sep ? 1 : 0);
} else {
*out_path = trimmed_path;
}
return ResultSuccess();
}
Result SkipWindowsPath(const char **out_path, bool *out_normalized, const char *path, bool has_mount_name) {
size_t windows_path_len;
return ParseWindowsPath(out_path, nullptr, std::addressof(windows_path_len), out_normalized, path, 0, has_mount_name);
}
Result ParseMountName(const char **out_path, char *out, size_t *out_mount_name_len, const char *path, size_t max_out_size) {
/* Decide on a start. */
const char *start = PathNormalizer::IsSeparator(path[0]) ? path + 1 : path;
/* Find the end of the mount name. */
const char *cur;
for (cur = start; cur < start + MountNameLengthMax + 1; ++cur) {
if (*cur == StringTraits::DriveSeparator) {
++cur;
break;
}
if (PathNormalizer::IsSeparator(*cur)) {
*out_path = path;
*out_mount_name_len = 0;
return ResultSuccess();
}
}
R_UNLESS(start < cur - 1, fs::ResultInvalidPathFormat());
R_UNLESS(cur[-1] == StringTraits::DriveSeparator, fs::ResultInvalidPathFormat());
/* Check the mount name doesn't contain a dot. */
if (cur != start) {
for (const char *p = start; p < cur; ++p) {
R_UNLESS(*p != StringTraits::Dot, fs::ResultInvalidCharacter());
}
}
const size_t mount_name_len = cur - path;
if (out != nullptr) {
R_UNLESS(mount_name_len <= max_out_size, fs::ResultTooLongPath());
std::memcpy(out, path, mount_name_len);
}
*out_path = cur;
*out_mount_name_len = mount_name_len;
return ResultSuccess();
}
Result SkipMountName(const char **out_path, const char *path) {
size_t mount_name_len;
return ParseMountName(out_path, nullptr, std::addressof(mount_name_len), path, 0);
}
bool IsParentDirectoryPathReplacementNeeded(const char *path) {
if (!PathNormalizer::IsAnySeparator(path[0])) {
return false;
}
for (auto i = 0; !PathNormalizer::IsNullTerminator(path[i]); ++i) {
if (path[i + 0] == StringTraits::AlternateDirectorySeparator &&
path[i + 1] == StringTraits::Dot &&
path[i + 2] == StringTraits::Dot &&
(PathNormalizer::IsAnySeparator(path[i + 3]) || PathNormalizer::IsNullTerminator(path[i + 3])))
{
return true;
}
if (PathNormalizer::IsAnySeparator(path[i + 0]) &&
path[i + 1] == StringTraits::Dot &&
path[i + 2] == StringTraits::Dot &&
path[i + 3] == StringTraits::AlternateDirectorySeparator)
{
return true;
}
}
return false;
}
void ReplaceParentDirectoryPath(char *dst, const char *src) {
dst[0] = StringTraits::DirectorySeparator;
int i = 1;
while (!PathNormalizer::IsNullTerminator(src[i])) {
if (PathNormalizer::IsAnySeparator(src[i - 1]) &&
src[i + 0] == StringTraits::Dot &&
src[i + 1] == StringTraits::Dot &&
PathNormalizer::IsAnySeparator(src[i + 2]))
{
dst[i - 1] = StringTraits::DirectorySeparator;
dst[i + 0] = StringTraits::Dot;
dst[i + 1] = StringTraits::Dot;
dst[i - 2] = StringTraits::DirectorySeparator;
i += 3;
} else {
if (src[i - 1] == StringTraits::AlternateDirectorySeparator &&
src[i + 0] == StringTraits::Dot &&
src[i + 1] == StringTraits::Dot &&
PathNormalizer::IsNullTerminator(src[i + 2]))
{
dst[i - 1] = StringTraits::DirectorySeparator;
dst[i + 0] = StringTraits::Dot;
dst[i + 1] = StringTraits::Dot;
i += 2;
break;
}
dst[i] = src[i];
++i;
}
}
dst[i] = StringTraits::NullTerminator;
}
}
Result PathNormalizer::Normalize(char *out, size_t *out_len, const char *path, size_t max_out_size, bool unc_preserved, bool has_mount_name) {
/* Check pre-conditions. */
AMS_ASSERT(out != nullptr);
AMS_ASSERT(out_len != nullptr);
AMS_ASSERT(path != nullptr);
/* If we should, handle the mount name. */
size_t prefix_len = 0;
if (has_mount_name) {
size_t mount_name_len = 0;
R_TRY(ParseMountName(std::addressof(path), out, std::addressof(mount_name_len), path, max_out_size));
prefix_len += mount_name_len;
}
/* Deal with unc. */
bool is_unc_path = false;
if (unc_preserved) {
const char * const path_start = path;
size_t windows_path_len = 0;
R_TRY(ParseWindowsPath(std::addressof(path), out + prefix_len, std::addressof(windows_path_len), nullptr, path, max_out_size, has_mount_name));
prefix_len += windows_path_len;
is_unc_path = path != path_start;
}
/* Paths must start with / */
R_UNLESS(prefix_len != 0 || IsSeparator(path[0]), fs::ResultInvalidPathFormat());
/* Check if parent directory path replacement is needed. */
std::unique_ptr<char[], fs::impl::Deleter> replacement_path;
if (IsParentDirectoryPathReplacementNeeded(path)) {
/* Allocate a buffer to hold the replacement path. */
replacement_path = fs::impl::MakeUnique<char[]>(EntryNameLengthMax + 1);
R_UNLESS(replacement_path != nullptr, fs::ResultAllocationFailureInNew());
/* Replace the path. */
ReplaceParentDirectoryPath(replacement_path.get(), path);
/* Set path to be the replacement path. */
path = replacement_path.get();
}
bool skip_next_sep = false;
size_t i = 0;
size_t len = prefix_len;
while (!IsNullTerminator(path[i])) {
if (IsSeparator(path[i])) {
/* Swallow separators. */
while (IsSeparator(path[++i])) { }
if (IsNullTerminator(path[i])) {
break;
}
/* Handle skip if needed */
if (!skip_next_sep) {
if (len + 1 == max_out_size) {
out[len] = StringTraits::NullTerminator;
*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(path[i + dir_len]) && !IsNullTerminator(path[i + dir_len])) {
++dir_len;
}
if (IsCurrentDirectory(path + i)) {
skip_next_sep = true;
} else if (IsParentDirectory(path + i)) {
AMS_ASSERT(IsSeparator(out[len - 1]));
if (!is_unc_path) {
AMS_ASSERT(IsSeparator(out[prefix_len]));
}
/* Walk up a directory. */
if (len == prefix_len + 1) {
R_UNLESS(is_unc_path, fs::ResultDirectoryUnobtainable());
--len;
} else {
len -= 2;
do {
if (IsSeparator(out[len])) {
break;
}
--len;
} while (len != prefix_len);
}
if (!is_unc_path) {
AMS_ASSERT(IsSeparator(out[prefix_len]));
}
AMS_ASSERT(len < max_out_size);
} else {
/* Copy, possibly truncating. */
if (len + dir_len + 1 <= max_out_size) {
for (size_t j = 0; j < dir_len; ++j) {
out[len++] = path[i+j];
}
} else {
const size_t copy_len = max_out_size - 1 - len;
for (size_t j = 0; j < copy_len; ++j) {
out[len++] = path[i+j];
}
out[len] = StringTraits::NullTerminator;
*out_len = len;
return fs::ResultTooLongPath();
}
}
i += dir_len;
}
if (skip_next_sep) {
--len;
}
if (!is_unc_path && len == prefix_len && max_out_size > len) {
out[len++] = StringTraits::DirectorySeparator;
}
R_UNLESS(max_out_size >= len - 1, fs::ResultTooLongPath());
/* Null terminate. */
out[len] = StringTraits::NullTerminator;
*out_len = len;
/* Assert normalized. */
{
bool normalized = false;
const auto is_norm_result = IsNormalized(std::addressof(normalized), out, unc_preserved, has_mount_name);
R_ASSERT(is_norm_result);
AMS_ASSERT(normalized);
AMS_UNUSED(is_norm_result, normalized);
}
return ResultSuccess();
}
Result PathNormalizer::IsNormalized(bool *out, const char *path, bool unc_preserved, bool has_mount_name) {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
/* Save the start of the path. */
const char *path_start = path;
/* If we should, skip the mount name. */
if (has_mount_name) {
R_TRY(SkipMountName(std::addressof(path), path));
R_UNLESS(IsSeparator(*path), fs::ResultInvalidPathFormat());
}
/* If we should, handle unc. */
bool is_unc_path = false;
if (unc_preserved) {
path_start = path;
/* Skip the windows path. */
bool normalized_windows = false;
R_TRY(SkipWindowsPath(std::addressof(path), std::addressof(normalized_windows), path, has_mount_name));
/* If we're not windows-normalized, we're not normalized. */
if (!normalized_windows) {
*out = false;
return ResultSuccess();
}
/* Handle the case where we're dealing with a unc path. */
if (path != path_start) {
is_unc_path = true;
if (IsSeparator(path_start[0]) && IsSeparator(path_start[1])) {
*out = false;
return ResultSuccess();
}
if (IsNullTerminator(path[0])) {
*out = true;
return ResultSuccess();
}
}
}
/* Check if parent directory path replacement is needed. */
if (IsParentDirectoryPathReplacementNeeded(path)) {
*out = false;
return ResultSuccess();
}
/* Nintendo uses a state machine here. */
enum class PathState {
Start,
Normal,
FirstSeparator,
Separator,
CurrentDir,
ParentDir,
};
PathState state = PathState::Start;
for (const char *cur = path; *cur != StringTraits::NullTerminator; ++cur) {
const char c = *cur;
switch (state) {
case PathState::Start:
if (IsSeparator(c)) {
state = PathState::FirstSeparator;
} else {
R_UNLESS(path != path_start, fs::ResultInvalidPathFormat());
if (c == StringTraits::Dot) {
state = PathState::CurrentDir;
} else {
state = PathState::Normal;
}
}
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 (c == StringTraits::Dot) {
state = PathState::CurrentDir;
} else {
state = PathState::Normal;
}
break;
case PathState::CurrentDir:
if (IsSeparator(c)) {
*out = false;
return ResultSuccess();
} else if (c == StringTraits::Dot) {
state = PathState::ParentDir;
} else {
state = PathState::Normal;
}
break;
case PathState::ParentDir:
if (IsSeparator(c)) {
*out = false;
return ResultSuccess();
} else {
state = PathState::Normal;
}
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
switch (state) {
case PathState::Start:
return fs::ResultInvalidPathFormat();
case PathState::Normal:
*out = true;
break;
case PathState::FirstSeparator:
*out = !is_unc_path;
break;
case PathState::CurrentDir:
case PathState::ParentDir:
case PathState::Separator:
*out = false;
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
return ResultSuccess();
}
}

View File

@@ -1,136 +0,0 @@
/*
* Copyright (c) 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 {
namespace {
class PathVerifier {
private:
u32 m_invalid_chars[6];
u32 m_separators[2];
public:
PathVerifier() {
/* Convert all invalid characters. */
u32 *dst_invalid = m_invalid_chars;
for (const char *cur = ":*?<>|"; *cur != '\x00'; ++cur) {
AMS_ASSERT(dst_invalid < std::end(m_invalid_chars));
const auto result = util::ConvertCharacterUtf8ToUtf32(dst_invalid, cur);
AMS_ASSERT(result == util::CharacterEncodingResult_Success);
AMS_UNUSED(result);
++dst_invalid;
}
AMS_ASSERT(dst_invalid == std::end(m_invalid_chars));
/* Convert all separators. */
u32 *dst_sep = m_separators;
for (const char *cur = "/\\"; *cur != '\x00'; ++cur) {
AMS_ASSERT(dst_sep < std::end(m_separators));
const auto result = util::ConvertCharacterUtf8ToUtf32(dst_sep, cur);
AMS_ASSERT(result == util::CharacterEncodingResult_Success);
AMS_UNUSED(result);
++dst_sep;
}
AMS_ASSERT(dst_sep == std::end(m_separators));
}
Result Verify(const char *path, int max_path_len, int max_name_len) const {
AMS_ASSERT(path != nullptr);
auto cur = path;
auto name_len = 0;
for (auto path_len = 0; path_len <= max_path_len && name_len <= max_name_len; ++path_len) {
/* We're done, if the path is terminated. */
R_SUCCEED_IF(*cur == '\x00');
/* Get the current utf-8 character. */
util::CharacterEncodingResult result;
char char_buf[4] = {};
result = util::PickOutCharacterFromUtf8String(char_buf, std::addressof(cur));
R_UNLESS(result == util::CharacterEncodingResult_Success, fs::ResultInvalidCharacter());
/* Convert the current utf-8 character to utf-32. */
u32 path_char = 0;
result = util::ConvertCharacterUtf8ToUtf32(std::addressof(path_char), char_buf);
R_UNLESS(result == util::CharacterEncodingResult_Success, fs::ResultInvalidCharacter());
/* Check if the character is invalid. */
for (const auto invalid : m_invalid_chars) {
R_UNLESS(path_char != invalid, fs::ResultInvalidCharacter());
}
/* Increment name length. */
++name_len;
/* Check for separator. */
for (const auto sep : m_separators) {
if (path_char == sep) {
name_len = 0;
break;
}
}
}
/* The path was too long. */
return fs::ResultTooLongPath();
}
};
PathVerifier g_path_verifier;
}
Result VerifyPath(const char *path, int max_path_len, int max_name_len) {
return g_path_verifier.Verify(path, max_path_len, max_name_len);
}
bool IsSubPath(const char *lhs, const char *rhs) {
AMS_ASSERT(lhs != nullptr);
AMS_ASSERT(rhs != nullptr);
/* Special case certain paths. */
if (IsUnc(lhs) && !IsUnc(rhs)) {
return false;
}
if (!IsUnc(lhs) && IsUnc(rhs)) {
return false;
}
if (PathNormalizer::IsSeparator(lhs[0]) && PathNormalizer::IsNullTerminator(lhs[1]) && PathNormalizer::IsSeparator(rhs[0]) && !PathNormalizer::IsNullTerminator(rhs[1])) {
return true;
}
if (PathNormalizer::IsSeparator(rhs[0]) && PathNormalizer::IsNullTerminator(rhs[1]) && PathNormalizer::IsSeparator(lhs[0]) && !PathNormalizer::IsNullTerminator(lhs[1])) {
return true;
}
/* Check subpath. */
for (size_t i = 0; /* No terminating condition */; i++) {
if (PathNormalizer::IsNullTerminator(lhs[i])) {
return PathNormalizer::IsSeparator(rhs[i]);
} else if (PathNormalizer::IsNullTerminator(rhs[i])) {
return PathNormalizer::IsSeparator(lhs[i]);
} else if (lhs[i] != rhs[i]) {
return false;
}
}
}
}

View File

@@ -118,7 +118,7 @@ namespace ams::fs {
Priority GetPriority(os::ThreadType *thread) {
fs::Priority priority;
AMS_FS_R_ABORT_UNLESS(AMS_FS_IMPL_ACCESS_LOG(GetPriorityImpl(std::addressof(priority), thread), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_THREAD_ID, thread != nullptr ? os::GetThreadId(thread) : static_cast<os::ThreadId>(0)));
AMS_FS_R_ABORT_UNLESS(AMS_FS_IMPL_ACCESS_LOG(GetPriorityImpl(std::addressof(priority), thread), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_THREAD_ID, reinterpret_cast<u64>(thread != nullptr ? os::GetThreadId(thread) : os::ThreadId{})));
return priority;
}
@@ -137,7 +137,7 @@ namespace ams::fs {
PriorityRaw GetPriorityRaw(os::ThreadType *thread) {
fs::PriorityRaw priority_raw;
AMS_FS_R_ABORT_UNLESS(AMS_FS_IMPL_ACCESS_LOG(GetPriorityRawImpl(std::addressof(priority_raw), thread), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_THREAD_ID, thread != nullptr ? os::GetThreadId(thread) : static_cast<os::ThreadId>(0)));
AMS_FS_R_ABORT_UNLESS(AMS_FS_IMPL_ACCESS_LOG(GetPriorityRawImpl(std::addressof(priority_raw), thread), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_THREAD_ID, reinterpret_cast<u64>(thread != nullptr ? os::GetThreadId(thread) : os::ThreadId{})));
return priority_raw;
}
@@ -146,7 +146,7 @@ namespace ams::fs {
}
void SetPriority(os::ThreadType *thread, Priority priority) {
AMS_FS_R_ABORT_UNLESS(AMS_FS_IMPL_ACCESS_LOG(SetPriorityImpl(os::GetCurrentThread(), priority), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_THREAD_ID, thread != nullptr ? os::GetThreadId(thread) : static_cast<os::ThreadId>(0)));
AMS_FS_R_ABORT_UNLESS(AMS_FS_IMPL_ACCESS_LOG(SetPriorityImpl(os::GetCurrentThread(), priority), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_THREAD_ID, reinterpret_cast<u64>(thread != nullptr ? os::GetThreadId(thread) : os::ThreadId{})));
}
void SetPriorityRawOnCurrentThread(PriorityRaw priority_raw) {
@@ -154,7 +154,7 @@ namespace ams::fs {
}
void SetPriorityRaw(os::ThreadType *thread, PriorityRaw priority_raw) {
AMS_FS_R_ABORT_UNLESS(AMS_FS_IMPL_ACCESS_LOG(SetPriorityRawImpl(os::GetCurrentThread(), priority_raw), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_THREAD_ID, thread != nullptr ? os::GetThreadId(thread) : static_cast<os::ThreadId>(0)));
AMS_FS_R_ABORT_UNLESS(AMS_FS_IMPL_ACCESS_LOG(SetPriorityRawImpl(os::GetCurrentThread(), priority_raw), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_THREAD_ID, reinterpret_cast<u64>(thread != nullptr ? os::GetThreadId(thread) : os::ThreadId{})));
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 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 "fs_remote_file_system_proxy.hpp"
#include "fs_remote_file_system_proxy_for_loader.hpp"
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
extern "C" {
extern u32 __nx_fs_num_sessions;
}
#endif
namespace ams::fs {
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
RemoteFileSystemProxy::RemoteFileSystemProxy(int session_count) {
/* Ensure we can connect to sm. */
R_ABORT_UNLESS(sm::Initialize());
ON_SCOPE_EXIT { R_ABORT_UNLESS(sm::Finalize()); };
/* Initialize libnx. */
__nx_fs_num_sessions = static_cast<u32>(session_count);
R_ABORT_UNLESS(::fsInitialize());
}
RemoteFileSystemProxy::~RemoteFileSystemProxy() {
::fsExit();
}
RemoteFileSystemProxyForLoader::RemoteFileSystemProxyForLoader() {
/* Ensure we can connect to sm. */
R_ABORT_UNLESS(sm::Initialize());
ON_SCOPE_EXIT { R_ABORT_UNLESS(sm::Finalize()); };
R_ABORT_UNLESS(::fsldrInitialize());
}
RemoteFileSystemProxyForLoader::~RemoteFileSystemProxyForLoader() {
::fsldrExit();
}
#endif
}

View File

@@ -0,0 +1,467 @@
/*
* Copyright (c) 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>
#include <stratosphere/fssrv/fssrv_interface_adapters.hpp>
#include "../fssrv/impl/fssrv_allocator_for_service_framework.hpp"
#include "impl/fs_remote_event_notifier.hpp"
#include "impl/fs_remote_device_operator.hpp"
namespace ams::fs {
#if defined(ATMOSPHERE_OS_HORIZON)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
class RemoteFileSystemProxy {
NON_COPYABLE(RemoteFileSystemProxy);
NON_MOVEABLE(RemoteFileSystemProxy);
private:
using ObjectFactory = fssrv::impl::FileSystemObjectFactory;
public:
RemoteFileSystemProxy(int session_count);
~RemoteFileSystemProxy();
public:
/* Command interface */
Result OpenFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u32 type) {
AMS_ABORT("TODO");
}
Result SetCurrentProcess(const ams::sf::ClientProcessId &client_pid) {
/* Libnx does this for us automatically. */
AMS_UNUSED(client_pid);
R_SUCCEED();
}
Result OpenDataFileSystemByCurrentProcess(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out) {
AMS_ABORT("TODO");
}
Result OpenFileSystemWithPatch(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, ncm::ProgramId program_id, u32 type) {
AMS_ABORT("TODO");
}
Result OpenFileSystemWithId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u64 program_id, u32 type) {
::FsFileSystem fs;
R_TRY(fsOpenFileSystemWithId(std::addressof(fs), program_id, static_cast<::FsFileSystemType>(type), path.str));
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystem, fssrv::impl::RemoteFileSystem>(fs));
R_SUCCEED();
}
Result OpenDataFileSystemByProgramId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, ncm::ProgramId program_id) {
AMS_ABORT("TODO");
}
Result OpenBisFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u32 id) {
AMS_ABORT("TODO");
}
Result OpenBisStorage(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, u32 id) {
FsStorage s;
R_TRY(fsOpenBisStorage(std::addressof(s), static_cast<::FsBisPartitionId>(id)));
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IStorage, fssrv::impl::RemoteStorage>(s));
R_SUCCEED();
}
Result InvalidateBisCache() {
AMS_ABORT("TODO");
}
Result OpenHostFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path) {
AMS_ABORT("TODO");
}
Result OpenSdCardFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out) {
::FsFileSystem fs;
R_TRY(fsOpenSdCardFileSystem(std::addressof(fs)));
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystem, fssrv::impl::RemoteFileSystem>(fs));
R_SUCCEED();
}
Result FormatSdCardFileSystem() {
AMS_ABORT("TODO");
}
Result DeleteSaveDataFileSystem(u64 save_data_id) {
AMS_UNUSED(save_data_id);
AMS_ABORT("TODO: libnx binding");
}
Result CreateSaveDataFileSystem(const fs::SaveDataAttribute &attribute, const fs::SaveDataCreationInfo &creation_info, const fs::SaveDataMetaInfo &meta_info) {
AMS_ABORT("TODO");
}
Result CreateSaveDataFileSystemBySystemSaveDataId(const fs::SaveDataAttribute &attribute, const fs::SaveDataCreationInfo &creation_info) {
static_assert(sizeof(attribute) == sizeof(::FsSaveDataAttribute));
static_assert(sizeof(creation_info) == sizeof(::FsSaveDataCreationInfo));
R_RETURN(fsCreateSaveDataFileSystemBySystemSaveDataId(reinterpret_cast<const ::FsSaveDataAttribute *>(std::addressof(attribute)), reinterpret_cast<const ::FsSaveDataCreationInfo *>(std::addressof(creation_info))));
}
Result RegisterSaveDataFileSystemAtomicDeletion(const ams::sf::InBuffer &save_data_ids) {
AMS_ABORT("TODO");
}
Result DeleteSaveDataFileSystemBySaveDataSpaceId(u8 indexer_space_id, u64 save_data_id) {
R_RETURN(fsDeleteSaveDataFileSystemBySaveDataSpaceId(static_cast<::FsSaveDataSpaceId>(indexer_space_id), save_data_id));
}
Result FormatSdCardDryRun() {
AMS_ABORT("TODO");
}
Result IsExFatSupported(ams::sf::Out<bool> out) {
AMS_ABORT("TODO");
}
Result DeleteSaveDataFileSystemBySaveDataAttribute(u8 space_id, const fs::SaveDataAttribute &attribute) {
static_assert(sizeof(attribute) == sizeof(::FsSaveDataAttribute));
R_RETURN(fsDeleteSaveDataFileSystemBySaveDataAttribute(static_cast<::FsSaveDataSpaceId>(space_id), reinterpret_cast<const ::FsSaveDataAttribute *>(std::addressof(attribute))));
}
Result OpenGameCardStorage(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, u32 handle, u32 partition) {
AMS_ABORT("TODO");
}
Result OpenGameCardFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u32 handle, u32 partition) {
::FsFileSystem fs;
const ::FsGameCardHandle _hnd = {handle};
R_TRY(fsOpenGameCardFileSystem(std::addressof(fs), std::addressof(_hnd), static_cast<::FsGameCardPartition>(partition)));
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystem, fssrv::impl::RemoteFileSystem>(fs));
R_SUCCEED();
}
Result ExtendSaveDataFileSystem(u8 space_id, u64 save_data_id, s64 available_size, s64 journal_size) {
R_RETURN(::fsExtendSaveDataFileSystem(static_cast<::FsSaveDataSpaceId>(space_id), save_data_id, available_size, journal_size));
}
Result DeleteCacheStorage(u16 index) {
AMS_ABORT("TODO");
}
Result GetCacheStorageSize(ams::sf::Out<s64> out_size, ams::sf::Out<s64> out_journal_size, u16 index) {
AMS_ABORT("TODO");
}
Result CreateSaveDataFileSystemWithHashSalt(const fs::SaveDataAttribute &attribute, const fs::SaveDataCreationInfo &creation_info, const fs::SaveDataMetaInfo &meta_info, const fs::HashSalt &salt) {
AMS_ABORT("TODO");
}
Result OpenHostFileSystemWithOption(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u32 option) {
AMS_ABORT("TODO");
}
Result OpenSaveDataFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u8 space_id, const fs::SaveDataAttribute &attribute) {
::FsFileSystem fs;
R_TRY(fsOpenSaveDataFileSystem(std::addressof(fs), static_cast<::FsSaveDataSpaceId>(space_id), reinterpret_cast<const ::FsSaveDataAttribute *>(std::addressof(attribute))));
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystem, fssrv::impl::RemoteFileSystem>(fs));
R_SUCCEED();
}
Result OpenSaveDataFileSystemBySystemSaveDataId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u8 space_id, const fs::SaveDataAttribute &attribute) {
::FsFileSystem fs;
R_TRY(fsOpenSaveDataFileSystemBySystemSaveDataId(std::addressof(fs), static_cast<::FsSaveDataSpaceId>(space_id), reinterpret_cast<const ::FsSaveDataAttribute *>(std::addressof(attribute))));
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystem, fssrv::impl::RemoteFileSystem>(fs));
R_SUCCEED();
}
Result OpenReadOnlySaveDataFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u8 space_id, const fs::SaveDataAttribute &attribute) {
AMS_ABORT("TODO");
}
Result ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(const ams::sf::OutBuffer &buffer, u8 space_id, u64 save_data_id) {
R_RETURN(fsReadSaveDataFileSystemExtraDataBySaveDataSpaceId(buffer.GetPointer(), buffer.GetSize(), static_cast<::FsSaveDataSpaceId>(space_id), save_data_id));
}
Result ReadSaveDataFileSystemExtraData(const ams::sf::OutBuffer &buffer, u64 save_data_id) {
R_RETURN(fsReadSaveDataFileSystemExtraData(buffer.GetPointer(), buffer.GetSize(), save_data_id));
}
Result WriteSaveDataFileSystemExtraData(u64 save_data_id, u8 space_id, const ams::sf::InBuffer &buffer) {
R_RETURN(fsWriteSaveDataFileSystemExtraData(buffer.GetPointer(), buffer.GetSize(), static_cast<::FsSaveDataSpaceId>(space_id), save_data_id));
}
/* ... */
Result OpenImageDirectoryFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u32 id) {
::FsFileSystem fs;
R_TRY(fsOpenImageDirectoryFileSystem(std::addressof(fs), static_cast<::FsImageDirectoryId>(id)));
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystem, fssrv::impl::RemoteFileSystem>(fs));
R_SUCCEED();
}
/* ... */
Result OpenContentStorageFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u32 id) {
::FsFileSystem fs;
R_TRY(fsOpenContentStorageFileSystem(std::addressof(fs), static_cast<::FsContentStorageId>(id)));
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystem, fssrv::impl::RemoteFileSystem>(fs));
R_SUCCEED();
}
/* ... */
Result OpenDataStorageByCurrentProcess(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out) {
AMS_ABORT("TODO");
}
Result OpenDataStorageByProgramId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, ncm::ProgramId program_id) {
AMS_ABORT("TODO");
}
Result OpenDataStorageByDataId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, ncm::DataId data_id, u8 storage_id) {
::FsStorage s;
R_TRY(fsOpenDataStorageByDataId(std::addressof(s), data_id.value, static_cast<::NcmStorageId>(storage_id)));
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IStorage, fssrv::impl::RemoteStorage>(s));
R_SUCCEED();
}
Result OpenPatchDataStorageByCurrentProcess(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out) {
AMS_ABORT("TODO");
}
Result OpenDataFileSystemWithProgramIndex(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u8 index) {
AMS_ABORT("TODO");
}
Result OpenDataStorageWithProgramIndex(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, u8 index) {
AMS_ABORT("TODO");
}
Result OpenDataStorageByPath(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, const fssrv::sf::FspPath &path, u32 type) {
AMS_ABORT("TODO");
}
Result OpenDeviceOperator(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IDeviceOperator>> out) {
::FsDeviceOperator d;
R_TRY(fsOpenDeviceOperator(std::addressof(d)));
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IDeviceOperator, impl::RemoteDeviceOperator>(d));
R_SUCCEED();
}
Result OpenSdCardDetectionEventNotifier(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IEventNotifier>> out) {
::FsEventNotifier e;
R_TRY(fsOpenSdCardDetectionEventNotifier(std::addressof(e)));
out.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IEventNotifier, impl::RemoteEventNotifier>(e));
R_SUCCEED();
}
Result OpenGameCardDetectionEventNotifier(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IEventNotifier>> out) {
AMS_ABORT("TODO");
}
Result OpenSystemDataUpdateEventNotifier(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IEventNotifier>> out) {
AMS_ABORT("TODO");
}
Result NotifySystemDataUpdateEvent() {
AMS_ABORT("TODO");
}
/* ... */
Result SetCurrentPosixTime(s64 posix_time) {
AMS_ABORT("TODO");
}
/* ... */
Result GetRightsId(ams::sf::Out<fs::RightsId> out, ncm::ProgramId program_id, ncm::StorageId storage_id) {
AMS_ABORT("TODO");
}
Result RegisterExternalKey(const fs::RightsId &rights_id, const spl::AccessKey &access_key) {
AMS_ABORT("TODO");
}
Result UnregisterAllExternalKey() {
AMS_ABORT("TODO");
}
Result GetRightsIdByPath(ams::sf::Out<fs::RightsId> out, const fssrv::sf::FspPath &path) {
static_assert(sizeof(RightsId) == sizeof(::FsRightsId));
R_RETURN(fsGetRightsIdByPath(path.str, reinterpret_cast<::FsRightsId *>(out.GetPointer())));
}
Result GetRightsIdAndKeyGenerationByPath(ams::sf::Out<fs::RightsId> out, ams::sf::Out<u8> out_key_generation, const fssrv::sf::FspPath &path) {
static_assert(sizeof(RightsId) == sizeof(::FsRightsId));
R_RETURN(fsGetRightsIdAndKeyGenerationByPath(path.str, out_key_generation.GetPointer(), reinterpret_cast<::FsRightsId *>(out.GetPointer())));
}
Result SetCurrentPosixTimeWithTimeDifference(s64 posix_time, s32 time_difference) {
AMS_ABORT("TODO");
}
Result GetFreeSpaceSizeForSaveData(ams::sf::Out<s64> out, u8 space_id) {
AMS_ABORT("TODO");
}
Result VerifySaveDataFileSystemBySaveDataSpaceId() {
AMS_ABORT("TODO");
}
Result CorruptSaveDataFileSystemBySaveDataSpaceId() {
AMS_ABORT("TODO");
}
Result QuerySaveDataInternalStorageTotalSize() {
AMS_ABORT("TODO");
}
Result GetSaveDataCommitId() {
AMS_ABORT("TODO");
}
Result UnregisterExternalKey(const fs::RightsId &rights_id) {
AMS_ABORT("TODO");
}
Result SetSdCardEncryptionSeed(const fs::EncryptionSeed &seed) {
AMS_ABORT("TODO");
}
Result SetSdCardAccessibility(bool accessible) {
AMS_ABORT("TODO");
}
Result IsSdCardAccessible(ams::sf::Out<bool> out) {
AMS_ABORT("TODO");
}
Result IsSignedSystemPartitionOnSdCardValid(ams::sf::Out<bool> out) {
R_RETURN(fsIsSignedSystemPartitionOnSdCardValid(out.GetPointer()));
}
Result OpenAccessFailureDetectionEventNotifier() {
AMS_ABORT("TODO");
}
/* ... */
Result RegisterProgramIndexMapInfo(const ams::sf::InBuffer &buffer, s32 count) {
AMS_ABORT("TODO");
}
Result SetBisRootForHost(u32 id, const fssrv::sf::FspPath &path) {
AMS_ABORT("TODO");
}
Result SetSaveDataSize(s64 size, s64 journal_size) {
AMS_ABORT("TODO");
}
Result SetSaveDataRootPath(const fssrv::sf::FspPath &path) {
AMS_ABORT("TODO");
}
Result DisableAutoSaveDataCreation() {
R_RETURN(::fsDisableAutoSaveDataCreation());
}
Result SetGlobalAccessLogMode(u32 mode) {
R_RETURN(::fsSetGlobalAccessLogMode(mode));
}
Result GetGlobalAccessLogMode(sf::Out<u32> out) {
R_RETURN(::fsGetGlobalAccessLogMode(out.GetPointer()));
}
Result OutputAccessLogToSdCard(const ams::sf::InBuffer &buf) {
R_RETURN(::fsOutputAccessLogToSdCard(reinterpret_cast<const char *>(buf.GetPointer()), buf.GetSize()));
}
Result RegisterUpdatePartition() {
AMS_ABORT("TODO");
}
Result OpenRegisteredUpdatePartition(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out) {
AMS_ABORT("TODO");
}
/* ... */
Result GetProgramIndexForAccessLog(ams::sf::Out<u32> out_idx, ams::sf::Out<u32> out_count) {
R_RETURN(::fsGetProgramIndexForAccessLog(out_idx.GetPointer(), out_count.GetPointer()));
}
Result GetFsStackUsage(ams::sf::Out<u32> out, u32 type) {
AMS_ABORT("TODO");
}
Result UnsetSaveDataRootPath() {
AMS_ABORT("TODO");
}
Result OutputMultiProgramTagAccessLog() {
AMS_ABORT("TODO");
}
Result FlushAccessLogOnSdCard() {
AMS_ABORT("TODO");
}
Result OutputApplicationInfoAccessLog() {
AMS_ABORT("TODO");
}
Result RegisterDebugConfiguration(u32 key, s64 value) {
AMS_ABORT("TODO");
}
Result UnregisterDebugConfiguration(u32 key) {
AMS_ABORT("TODO");
}
Result OverrideSaveDataTransferTokenSignVerificationKey(const ams::sf::InBuffer &buf) {
AMS_ABORT("TODO");
}
Result CorruptSaveDataFileSystemByOffset(u8 space_id, u64 save_data_id, s64 offset) {
AMS_ABORT("TODO");
}
/* ... */
};
static_assert(fssrv::sf::IsIFileSystemProxy<RemoteFileSystemProxy>);
#pragma GCC diagnostic pop
#endif
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (c) 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>
#include <stratosphere/fssrv/fssrv_interface_adapters.hpp>
#include "../fssrv/impl/fssrv_allocator_for_service_framework.hpp"
namespace ams::fs {
#if defined(ATMOSPHERE_OS_HORIZON)
class RemoteFileSystemProxyForLoader {
NON_COPYABLE(RemoteFileSystemProxyForLoader);
NON_MOVEABLE(RemoteFileSystemProxyForLoader);
private:
using ObjectFactory = fssrv::impl::FileSystemObjectFactory;
public:
RemoteFileSystemProxyForLoader();
~RemoteFileSystemProxyForLoader();
public:
Result OpenCodeFileSystemDeprecated(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out_fs, const fssrv::sf::Path &path, ncm::ProgramId program_id) {
::FsCodeInfo dummy;
::FsFileSystem fs;
R_TRY(fsldrOpenCodeFileSystem(std::addressof(dummy), program_id.value, path.str, std::addressof(fs)));
out_fs.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystem, fssrv::impl::RemoteFileSystem>(fs));
R_SUCCEED();
}
Result OpenCodeFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out_fs, ams::sf::Out<fs::CodeVerificationData> out_verif, const fssrv::sf::Path &path, ncm::ProgramId program_id) {
::FsFileSystem fs;
R_TRY(fsldrOpenCodeFileSystem(reinterpret_cast<::FsCodeInfo *>(out_verif.GetPointer()), program_id.value, path.str, std::addressof(fs)));
out_fs.SetValue(ObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystem, fssrv::impl::RemoteFileSystem>(fs));
R_SUCCEED();
}
Result IsArchivedProgram(ams::sf::Out<bool> out, u64 process_id) {
R_RETURN(fsldrIsArchivedProgram(process_id, out.GetPointer()));
}
Result SetCurrentProcess(const ams::sf::ClientProcessId &client_pid) {
/* Libnx does this for us automatically. */
AMS_UNUSED(client_pid);
R_SUCCEED();
}
};
static_assert(fssrv::sf::IsIFileSystemProxyForLoader<RemoteFileSystemProxyForLoader>);
#endif
}

View File

@@ -15,17 +15,37 @@
*/
#include <stratosphere.hpp>
#include <stratosphere/fs/fs_rights_id.hpp>
#include "impl/fs_file_system_proxy_service_object.hpp"
namespace ams::fs {
Result GetRightsId(RightsId *out, const char *path) {
static_assert(sizeof(RightsId) == sizeof(::FsRightsId));
return fsGetRightsIdByPath(path, reinterpret_cast<::FsRightsId *>(out));
AMS_FS_R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
AMS_FS_R_UNLESS(path != nullptr, fs::ResultNullptrArgument());
/* Convert the path for fsp. */
fssrv::sf::FspPath sf_path;
R_TRY(fs::ConvertToFspPath(std::addressof(sf_path), path));
auto fsp = impl::GetFileSystemProxyServiceObject();
AMS_FS_R_TRY(fsp->GetRightsIdByPath(out, sf_path));
R_SUCCEED();
}
Result GetRightsId(RightsId *out, u8 *out_key_generation, const char *path) {
static_assert(sizeof(RightsId) == sizeof(::FsRightsId));
return fsGetRightsIdAndKeyGenerationByPath(path, out_key_generation, reinterpret_cast<::FsRightsId *>(out));
AMS_FS_R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
AMS_FS_R_UNLESS(out_key_generation != nullptr, fs::ResultNullptrArgument());
AMS_FS_R_UNLESS(path != nullptr, fs::ResultNullptrArgument());
/* Convert the path for fsp. */
fssrv::sf::FspPath sf_path;
R_TRY(fs::ConvertToFspPath(std::addressof(sf_path), path));
auto fsp = impl::GetFileSystemProxyServiceObject();
AMS_FS_R_TRY(fsp->GetRightsIdAndKeyGenerationByPath(out, out_key_generation, sf_path));
R_SUCCEED();
}
}

View File

@@ -266,11 +266,11 @@ namespace ams::fs {
RomFsDirectory(RomFsFileSystem *p, const FindPosition &f, fs::OpenDirectoryMode m) : m_parent(p), m_current_find(f), m_first_find(f), m_mode(m) { /* ... */ }
virtual ~RomFsDirectory() override { /* ... */ }
public:
virtual Result DoRead(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) {
virtual Result DoRead(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) override {
return this->ReadInternal(out_count, std::addressof(m_current_find), out_entries, max_entries);
}
virtual Result DoGetEntryCount(s64 *out) {
virtual Result DoGetEntryCount(s64 *out) override {
FindPosition find = m_first_find;
return this->ReadInternal(out, std::addressof(find), nullptr, 0);
}
@@ -293,9 +293,10 @@ namespace ams::fs {
} R_END_TRY_CATCH;
if (out_entries) {
R_UNLESS(strnlen(name_buf, NameBufferSize) < NameBufferSize, fs::ResultTooLongPath());
strncpy(out_entries[i].name, name_buf, fs::EntryNameLengthMax);
out_entries[i].name[fs::EntryNameLengthMax] = '\x00';
const size_t name_len = util::Strnlen(name_buf, NameBufferSize);
R_UNLESS(name_len < NameBufferSize, fs::ResultTooLongPath());
std::memcpy(out_entries[i].name, name_buf, name_len);
out_entries[i].name[name_len] = '\x00';
out_entries[i].type = fs::DirectoryEntryType_Directory;
out_entries[i].file_size = 0;
}
@@ -313,9 +314,10 @@ namespace ams::fs {
} R_END_TRY_CATCH;
if (out_entries) {
R_UNLESS(strnlen(name_buf, NameBufferSize) < NameBufferSize, fs::ResultTooLongPath());
strncpy(out_entries[i].name, name_buf, fs::EntryNameLengthMax);
out_entries[i].name[fs::EntryNameLengthMax] = '\x00';
const size_t name_len = util::Strnlen(name_buf, NameBufferSize);
R_UNLESS(name_len < NameBufferSize, fs::ResultTooLongPath());
std::memcpy(out_entries[i].name, name_buf, name_len);
out_entries[i].name[name_len] = '\x00';
out_entries[i].type = fs::DirectoryEntryType_File;
RomFsFileSystem::RomFileTable::FileInfo file_info;
@@ -443,48 +445,48 @@ namespace ams::fs {
return ResultSuccess();
}
Result RomFsFileSystem::DoCreateFile(const char *path, s64 size, int flags) {
Result RomFsFileSystem::DoCreateFile(const fs::Path &path, s64 size, int flags) {
AMS_UNUSED(path, size, flags);
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
}
Result RomFsFileSystem::DoDeleteFile(const char *path) {
Result RomFsFileSystem::DoDeleteFile(const fs::Path &path) {
AMS_UNUSED(path);
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
}
Result RomFsFileSystem::DoCreateDirectory(const char *path) {
Result RomFsFileSystem::DoCreateDirectory(const fs::Path &path) {
AMS_UNUSED(path);
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
}
Result RomFsFileSystem::DoDeleteDirectory(const char *path) {
Result RomFsFileSystem::DoDeleteDirectory(const fs::Path &path) {
AMS_UNUSED(path);
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
}
Result RomFsFileSystem::DoDeleteDirectoryRecursively(const char *path) {
Result RomFsFileSystem::DoDeleteDirectoryRecursively(const fs::Path &path) {
AMS_UNUSED(path);
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
}
Result RomFsFileSystem::DoRenameFile(const char *old_path, const char *new_path) {
Result RomFsFileSystem::DoRenameFile(const fs::Path &old_path, const fs::Path &new_path) {
AMS_UNUSED(old_path, new_path);
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
}
Result RomFsFileSystem::DoRenameDirectory(const char *old_path, const char *new_path) {
Result RomFsFileSystem::DoRenameDirectory(const fs::Path &old_path, const fs::Path &new_path) {
AMS_UNUSED(old_path, new_path);
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
}
Result RomFsFileSystem::DoGetEntryType(fs::DirectoryEntryType *out, const char *path) {
Result RomFsFileSystem::DoGetEntryType(fs::DirectoryEntryType *out, const fs::Path &path) {
RomDirectoryInfo dir_info;
R_TRY_CATCH(m_rom_file_table.GetDirectoryInformation(std::addressof(dir_info), path)) {
R_TRY_CATCH(m_rom_file_table.GetDirectoryInformation(std::addressof(dir_info), path.GetString())) {
R_CONVERT(fs::ResultDbmNotFound, fs::ResultPathNotFound())
R_CATCH(fs::ResultDbmInvalidOperation) {
RomFileTable::FileInfo file_info;
R_TRY(this->GetFileInfo(std::addressof(file_info), path));
R_TRY(this->GetFileInfo(std::addressof(file_info), path.GetString()));
*out = fs::DirectoryEntryType_File;
return ResultSuccess();
}
@@ -494,14 +496,13 @@ namespace ams::fs {
return ResultSuccess();
}
Result RomFsFileSystem::DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) {
Result RomFsFileSystem::DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const fs::Path &path, fs::OpenMode mode) {
AMS_ASSERT(out_file != nullptr);
AMS_ASSERT(path != nullptr);
R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidOpenMode());
RomFileTable::FileInfo file_info;
R_TRY(this->GetFileInfo(std::addressof(file_info), path));
R_TRY(this->GetFileInfo(std::addressof(file_info), path.GetString()));
auto file = std::make_unique<RomFsFile>(this, m_entry_size + file_info.offset.Get(), m_entry_size + file_info.offset.Get() + file_info.size.Get());
R_UNLESS(file != nullptr, fs::ResultAllocationFailureInRomFsFileSystemB());
@@ -510,12 +511,11 @@ namespace ams::fs {
return ResultSuccess();
}
Result RomFsFileSystem::DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) {
Result RomFsFileSystem::DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const fs::Path &path, fs::OpenDirectoryMode mode) {
AMS_ASSERT(out_dir != nullptr);
AMS_ASSERT(path != nullptr);
RomFileTable::FindPosition find;
R_TRY_CATCH(m_rom_file_table.FindOpen(std::addressof(find), path)) {
R_TRY_CATCH(m_rom_file_table.FindOpen(std::addressof(find), path.GetString())) {
R_CONVERT(fs::ResultDbmNotFound, fs::ResultPathNotFound())
R_CONVERT(fs::ResultDbmInvalidOperation, fs::ResultPathNotFound())
} R_END_TRY_CATCH;
@@ -531,19 +531,19 @@ namespace ams::fs {
return ResultSuccess();
}
Result RomFsFileSystem::DoGetFreeSpaceSize(s64 *out, const char *path) {
Result RomFsFileSystem::DoGetFreeSpaceSize(s64 *out, const fs::Path &path) {
AMS_UNUSED(path);
*out = 0;
return ResultSuccess();
}
Result RomFsFileSystem::DoGetTotalSpaceSize(s64 *out, const char *path) {
Result RomFsFileSystem::DoGetTotalSpaceSize(s64 *out, const fs::Path &path) {
AMS_UNUSED(out, path);
return fs::ResultUnsupportedOperationInRomFsFileSystemC();
}
Result RomFsFileSystem::DoCleanDirectoryRecursively(const char *path) {
Result RomFsFileSystem::DoCleanDirectoryRecursively(const fs::Path &path) {
AMS_UNUSED(path);
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
}

View File

@@ -15,45 +15,56 @@
*/
#include <stratosphere.hpp>
#include "fsa/fs_mount_utils.hpp"
#include "impl/fs_file_system_proxy_service_object.hpp"
namespace ams::fs {
namespace impl {
Result ReadSaveDataFileSystemExtraData(SaveDataExtraData *out, SaveDataId id) {
return fsReadSaveDataFileSystemExtraData(out, sizeof(*out), id);
auto fsp = impl::GetFileSystemProxyServiceObject();
AMS_FS_R_TRY(fsp->ReadSaveDataFileSystemExtraData(sf::OutBuffer(out, sizeof(*out)), id));
R_SUCCEED();
}
Result ReadSaveDataFileSystemExtraData(SaveDataExtraData *out, SaveDataSpaceId space_id, SaveDataId id) {
return fsReadSaveDataFileSystemExtraDataBySaveDataSpaceId(out, sizeof(*out), static_cast<::FsSaveDataSpaceId>(space_id), id);
auto fsp = impl::GetFileSystemProxyServiceObject();
AMS_FS_R_TRY(fsp->ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(sf::OutBuffer(out, sizeof(*out)), static_cast<u8>(space_id), id));
R_SUCCEED();
}
Result WriteSaveDataFileSystemExtraData(SaveDataSpaceId space_id, SaveDataId id, const SaveDataExtraData &extra_data) {
return fsWriteSaveDataFileSystemExtraData(std::addressof(extra_data), sizeof(extra_data), static_cast<::FsSaveDataSpaceId>(space_id), id);
auto fsp = impl::GetFileSystemProxyServiceObject();
AMS_FS_R_TRY(fsp->WriteSaveDataFileSystemExtraData(id, static_cast<u8>(space_id), sf::InBuffer(std::addressof(extra_data), sizeof(extra_data))));
R_SUCCEED();
}
}
void DisableAutoSaveDataCreation() {
/* Use libnx binding. */
R_ABORT_UNLESS(fsDisableAutoSaveDataCreation());
auto fsp = impl::GetFileSystemProxyServiceObject();
AMS_FS_R_ABORT_UNLESS(fsp->DisableAutoSaveDataCreation());
}
Result CreateSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId save_id, UserId user_id, u64 owner_id, s64 size, s64 journal_size, u32 flags) {
const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, save_id);
const SaveDataCreationInfo info = {
.size = size,
.journal_size = journal_size,
.block_size = DefaultSaveDataBlockSize,
.owner_id = owner_id,
.flags = flags,
.space_id = space_id,
.pseudo = false,
auto create_impl = [=]() -> Result {
const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, save_id);
const SaveDataCreationInfo info = {
.size = size,
.journal_size = journal_size,
.block_size = DefaultSaveDataBlockSize,
.owner_id = owner_id,
.flags = flags,
.space_id = space_id,
.pseudo = false,
};
auto fsp = impl::GetFileSystemProxyServiceObject();
R_RETURN(fsp->CreateSaveDataFileSystemBySystemSaveDataId(attribute, info));
};
static_assert(sizeof(SaveDataAttribute) == sizeof(::FsSaveDataAttribute));
static_assert(sizeof(SaveDataCreationInfo) == sizeof(::FsSaveDataCreationInfo));
return fsCreateSaveDataFileSystemBySystemSaveDataId(reinterpret_cast<const ::FsSaveDataAttribute *>(std::addressof(attribute)), reinterpret_cast<const ::FsSaveDataCreationInfo *>(std::addressof(info)));
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM(create_impl(), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_CREATE_SYSTEM_SAVE_DATA(space_id, save_id, user_id, owner_id, size, journal_size, flags)));
R_SUCCEED();
}
Result CreateSystemSaveData(SystemSaveDataId save_id, s64 size, s64 journal_size, u32 flags) {
@@ -77,20 +88,35 @@ namespace ams::fs {
}
Result DeleteSaveData(SaveDataId id) {
/* TODO: Libnx binding for DeleteSaveDataFileSystem */
AMS_UNUSED(id);
AMS_ABORT();
auto delete_impl = [=]() -> Result {
auto fsp = impl::GetFileSystemProxyServiceObject();
R_RETURN(fsp->DeleteSaveDataFileSystem(id));
};
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM(delete_impl(), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_SAVE_DATA_ID, id));
R_SUCCEED();
}
Result DeleteSaveData(SaveDataSpaceId space_id, SaveDataId id) {
return fsDeleteSaveDataFileSystemBySaveDataSpaceId(static_cast<::FsSaveDataSpaceId>(space_id), id);
auto delete_impl = [=]() -> Result {
auto fsp = impl::GetFileSystemProxyServiceObject();
R_RETURN(fsp->DeleteSaveDataFileSystemBySaveDataSpaceId(static_cast<u8>(space_id), id));
};
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM(delete_impl(), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_DELETE_SAVE_DATA(space_id, id)));
R_SUCCEED();
}
Result DeleteSystemSaveData(SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id) {
const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, id);
auto delete_impl = [=]() -> Result {
const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, id);
static_assert(sizeof(attribute) == sizeof(::FsSaveDataAttribute));
return fsDeleteSaveDataFileSystemBySaveDataAttribute(static_cast<::FsSaveDataSpaceId>(space_id), reinterpret_cast<const ::FsSaveDataAttribute *>(std::addressof(attribute)));
auto fsp = impl::GetFileSystemProxyServiceObject();
R_RETURN(fsp->DeleteSaveDataFileSystemBySaveDataAttribute(static_cast<u8>(space_id), attribute));
};
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM(delete_impl(), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_DELETE_SYSTEM_SAVE_DATA(space_id, id, user_id)));
R_SUCCEED();
}
Result GetSaveDataFlags(u32 *out, SaveDataId id) {
@@ -133,7 +159,13 @@ namespace ams::fs {
}
Result ExtendSaveData(SaveDataSpaceId space_id, SaveDataId id, s64 available_size, s64 journal_size) {
return ::fsExtendSaveDataFileSystem(static_cast<::FsSaveDataSpaceId>(space_id), static_cast<u64>(id), available_size, journal_size);
auto extend_impl = [=]() -> Result {
auto fsp = impl::GetFileSystemProxyServiceObject();
R_RETURN(fsp->ExtendSaveDataFileSystem(static_cast<u8>(space_id), id, available_size, journal_size));
};
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM(extend_impl(), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_EXTEND_SAVE_DATA(space_id, id, available_size, journal_size)));
R_SUCCEED();
}
}

View File

@@ -15,7 +15,9 @@
*/
#include <stratosphere.hpp>
#include "fsa/fs_mount_utils.hpp"
#include "impl/fs_event_notifier_object_adapter.hpp"
#include "impl/fs_file_system_proxy_service_object.hpp"
#include "impl/fs_file_system_service_object_adapter.hpp"
#include "impl/fs_event_notifier_service_object_adapter.hpp"
namespace ams::fs {
@@ -31,7 +33,7 @@ namespace ams::fs {
virtual Result GenerateCommonMountName(char *dst, size_t dst_size) override {
/* Determine how much space we need. */
const size_t needed_size = strnlen(impl::SdCardFileSystemMountName, MountNameLengthMax) + 2;
const size_t needed_size = util::Strnlen(impl::SdCardFileSystemMountName, MountNameLengthMax) + 2;
AMS_ABORT_UNLESS(dst_size >= needed_size);
/* Generate the name. */
@@ -47,14 +49,15 @@ namespace ams::fs {
Result MountSdCard(const char *name) {
/* Validate the mount name. */
R_TRY(impl::CheckMountNameAllowingReserved(name));
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_MOUNT_UNLESS_R_SUCCEEDED(impl::CheckMountNameAllowingReserved(name), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT, name));
/* Open the SD card. This uses libnx bindings. */
FsFileSystem fs;
R_TRY(fsOpenSdCardFileSystem(std::addressof(fs)));
/* Open the SD card filesystem. */
auto fsp = impl::GetFileSystemProxyServiceObject();
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
R_TRY(fsp->OpenSdCardFileSystem(std::addressof(fs)));
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<RemoteFileSystem>(fs);
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInSdCardA());
/* Allocate a new mountname generator. */
@@ -70,47 +73,53 @@ namespace ams::fs {
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
/* Open the SD card. This uses libnx bindings. */
FsFileSystem fs;
R_TRY(fsOpenSdCardFileSystem(std::addressof(fs)));
/* Open the SD card filesystem. */
auto fsp = impl::GetFileSystemProxyServiceObject();
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
R_TRY(fsp->OpenSdCardFileSystem(std::addressof(fs)));
/* Allocate a new filesystem wrapper. */
std::unique_ptr<fsa::IFileSystem> fsa = std::make_unique<RemoteFileSystem>(fs);
auto fsa = std::make_shared<impl::FileSystemServiceObjectAdapter>(std::move(fs));
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInSdCardA());
/* Ensure that the error report directory exists. */
R_TRY(fssystem::EnsureDirectoryRecursively(fsa.get(), AtmosphereErrorReportDirectory));
constexpr fs::Path fs_path = fs::MakeConstantPath(AtmosphereErrorReportDirectory);
R_TRY(fssystem::EnsureDirectory(fsa.get(), fs_path));
/* Create a subdirectory filesystem. */
auto subdir_fs = std::make_unique<fssystem::SubDirectoryFileSystem>(std::move(fsa), AtmosphereErrorReportDirectory);
auto subdir_fs = std::make_unique<fssystem::SubDirectoryFileSystem>(std::move(fsa));
R_UNLESS(subdir_fs != nullptr, fs::ResultAllocationFailureInSdCardA());
R_TRY(subdir_fs->Initialize(fs_path));
/* Register. */
return fsa::Register(name, std::move(subdir_fs));
}
Result OpenSdCardDetectionEventNotifier(std::unique_ptr<IEventNotifier> *out) {
auto fsp = impl::GetFileSystemProxyServiceObject();
/* Try to open an event notifier. */
FsEventNotifier notifier;
AMS_FS_R_TRY(fsOpenSdCardDetectionEventNotifier(std::addressof(notifier)));
sf::SharedPointer<fssrv::sf::IEventNotifier> notifier;
AMS_FS_R_TRY(fsp->OpenSdCardDetectionEventNotifier(std::addressof(notifier)));
/* Create an event notifier adapter. */
auto adapter = std::make_unique<impl::RemoteEventNotifierObjectAdapter>(notifier);
R_UNLESS(adapter != nullptr, fs::ResultAllocationFailureInSdCardB());
auto adapter = std::make_unique<impl::EventNotifierObjectAdapter>(std::move(notifier));
AMS_FS_R_UNLESS(adapter != nullptr, fs::ResultAllocationFailureInSdCardB());
*out = std::move(adapter);
return ResultSuccess();
}
bool IsSdCardInserted() {
/* Open device operator. */
FsDeviceOperator device_operator;
AMS_FS_R_ABORT_UNLESS(::fsOpenDeviceOperator(std::addressof(device_operator)));
ON_SCOPE_EXIT { ::fsDeviceOperatorClose(std::addressof(device_operator)); };
auto fsp = impl::GetFileSystemProxyServiceObject();
/* Get SD card inserted. */
/* Open a device operator. */
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
AMS_FS_R_ABORT_UNLESS(fsp->OpenDeviceOperator(std::addressof(device_operator)));
/* Get insertion status. */
bool inserted;
AMS_FS_R_ABORT_UNLESS(::fsDeviceOperatorIsSdCardInserted(std::addressof(device_operator), std::addressof(inserted)));
AMS_FS_R_ABORT_UNLESS(device_operator->IsSdCardInserted(std::addressof(inserted)));
return inserted;
}

View File

@@ -16,21 +16,30 @@
#include <stratosphere.hpp>
#include "fsa/fs_filesystem_accessor.hpp"
#include "fsa/fs_mount_utils.hpp"
#include "impl/fs_file_system_proxy_service_object.hpp"
namespace ams::fs {
namespace {
Result GetSignedSystemPartitionOnSdCardValid(char *out, impl::FileSystemAccessor *accessor) {
R_TRY_CATCH(accessor->QueryEntry(out, sizeof(*out), nullptr, 0, fsa::QueryId::IsSignedSystemPartitionOnSdCardValid, "/")) {
/* If querying isn't supported, then the partition isn't valid. */
R_CATCH(fs::ResultUnsupportedOperation) { *out = false; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
R_SUCCEED();
}
}
bool IsSignedSystemPartitionOnSdCardValid(const char *system_root_path) {
/* Get the accessor for the system filesystem. */
impl::FileSystemAccessor *accessor;
const char *sub_path;
R_ABORT_UNLESS(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), system_root_path));
AMS_FS_R_ABORT_UNLESS(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), system_root_path));
char is_valid;
R_TRY_CATCH(accessor->QueryEntry(std::addressof(is_valid), 1, nullptr, 0, fsa::QueryId::IsSignedSystemPartitionOnSdCardValid, "/")) {
/* If querying isn't supported, then the partition isn't valid. */
R_CATCH(fs::ResultUnsupportedOperation) { is_valid = false; }
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
AMS_FS_R_ABORT_UNLESS(GetSignedSystemPartitionOnSdCardValid(std::addressof(is_valid), accessor));
return is_valid;
}
@@ -40,8 +49,9 @@ namespace ams::fs {
AMS_ABORT_UNLESS(hos::Version_4_0_0 <= version && version < hos::Version_8_0_0);
/* Check that the partition is valid. */
auto fsp = impl::GetFileSystemProxyServiceObject();
bool is_valid;
R_ABORT_UNLESS(fsIsSignedSystemPartitionOnSdCardValid(std::addressof(is_valid)));
AMS_FS_R_ABORT_UNLESS(fsp->IsSignedSystemPartitionOnSdCardValid(std::addressof(is_valid)));
return is_valid;
}

View File

@@ -19,15 +19,20 @@
namespace ams::fs {
Result QueryMountSystemDataCacheSize(size_t *out, ncm::SystemDataId data_id) {
return impl::QueryMountDataCacheSize(out, data_id, ncm::StorageId::BuiltInSystem);
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM(impl::QueryMountDataCacheSize(out, data_id, ncm::StorageId::BuiltInSystem), nullptr, AMS_FS_IMPL_ACCESS_LOG_FORMAT_QUERY_MOUNT_SYSTEM_DATA_CACHE_SIZE(data_id, out)));
R_SUCCEED();
}
Result MountSystemData(const char *name, ncm::SystemDataId data_id) {
return impl::MountData(name, data_id, ncm::StorageId::BuiltInSystem);
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(impl::MountData(name, data_id, ncm::StorageId::BuiltInSystem), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_SYSTEM_DATA(name, data_id)));
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
R_SUCCEED();
}
Result MountSystemData(const char *name, ncm::SystemDataId data_id, void *cache_buffer, size_t cache_size) {
return impl::MountData(name, data_id, ncm::StorageId::BuiltInSystem, cache_buffer, cache_size);
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(impl::MountData(name, data_id, ncm::StorageId::BuiltInSystem, cache_buffer, cache_size), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_SYSTEM_DATA(name, data_id)));
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
R_SUCCEED();
}
}

View File

@@ -15,33 +15,11 @@
*/
#include <stratosphere.hpp>
#include "fsa/fs_mount_utils.hpp"
#include "impl/fs_file_system_proxy_service_object.hpp"
#include "impl/fs_file_system_service_object_adapter.hpp"
namespace ams::fs {
namespace {
Result MountSystemSaveDataImpl(const char *name, SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id, SaveDataType type) {
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
/* Create the attribute. */
const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, type, user_id, id);
static_assert(sizeof(attribute) == sizeof(::FsSaveDataAttribute));
/* Open the filesystem, use libnx bindings. */
::FsFileSystem fs;
R_TRY(fsOpenSaveDataFileSystemBySystemSaveDataId(std::addressof(fs), static_cast<::FsSaveDataSpaceId>(space_id), reinterpret_cast<const ::FsSaveDataAttribute *>(std::addressof(attribute))));
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<RemoteFileSystem>(fs);
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInSystemSaveDataA());
/* Register. */
return fsa::Register(name, std::move(fsa));
}
}
Result MountSystemSaveData(const char *name, SystemSaveDataId id) {
return MountSystemSaveData(name, id, InvalidUserId);
}
@@ -55,7 +33,29 @@ namespace ams::fs {
}
Result MountSystemSaveData(const char *name, SaveDataSpaceId space_id, SystemSaveDataId id, UserId user_id) {
return MountSystemSaveDataImpl(name, space_id, id, user_id, SaveDataType::System);
auto mount_impl = [=]() -> Result {
/* Validate the mount name. */
R_TRY(impl::CheckMountName(name));
/* Create the attribute. */
const auto attribute = SaveDataAttribute::Make(ncm::InvalidProgramId, SaveDataType::System, user_id, id);
/* Open the filesystem. */
auto fsp = impl::GetFileSystemProxyServiceObject();
sf::SharedPointer<fssrv::sf::IFileSystem> fs;
R_TRY(fsp->OpenSaveDataFileSystemBySystemSaveDataId(std::addressof(fs), static_cast<u8>(space_id), attribute));
/* Allocate a new filesystem wrapper. */
auto fsa = std::make_unique<impl::FileSystemServiceObjectAdapter>(std::move(fs));
R_UNLESS(fsa != nullptr, fs::ResultAllocationFailureInSystemSaveDataA());
/* Register. */
return fsa::Register(name, std::move(fsa));
};
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_SYSTEM_MOUNT(mount_impl(), name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT_SYSTEM_SAVE_DATA(name, space_id, id, user_id)));
AMS_FS_IMPL_ACCESS_LOG_SYSTEM_FS_ACCESSOR_ENABLE(name);
R_SUCCEED();
}
}

View File

@@ -24,7 +24,7 @@ namespace ams::fs::impl {
template<typename List, typename Iter>
void Remove(List &list, Iter *desired) {
for (auto it = list.cbegin(); it != list.cend(); it++) {
for (auto it = list.cbegin(); it != list.cend(); ++it) {
if (it.operator->() == desired) {
list.erase(it);
return;
@@ -35,18 +35,13 @@ namespace ams::fs::impl {
AMS_ABORT();
}
Result ValidatePath(const char *mount_name, const char *path) {
const size_t mount_name_len = strnlen(mount_name, MountNameLengthMax);
const size_t path_len = strnlen(path, EntryNameLengthMax);
R_UNLESS(mount_name_len + 1 + path_len <= EntryNameLengthMax, fs::ResultTooLongPath());
return ResultSuccess();
}
Result ValidateMountName(const char *name) {
Result SetMountName(char *dst, const char *name) {
R_UNLESS(name[0] != 0, fs::ResultInvalidMountName());
R_UNLESS(strnlen(name, sizeof(MountName)) < sizeof(MountName), fs::ResultInvalidMountName());
return ResultSuccess();
const size_t n_len = util::Strlcpy<char>(dst, name, MountNameLengthMax + 1);
R_UNLESS(n_len <= MountNameLengthMax, fs::ResultInvalidMountName());
R_SUCCEED();
}
template<typename List>
@@ -54,18 +49,21 @@ namespace ams::fs::impl {
for (auto it = list.cbegin(); it != list.cend(); it++) {
R_UNLESS((it->GetOpenMode() & OpenMode_Write) == 0, fs::ResultWriteModeFileNotClosed());
}
return ResultSuccess();
R_SUCCEED();
}
}
FileSystemAccessor::FileSystemAccessor(const char *n, std::unique_ptr<fsa::IFileSystem> &&fs, std::unique_ptr<fsa::ICommonMountNameGenerator> &&generator)
: m_impl(std::move(fs)), m_open_list_lock(), m_mount_name_generator(std::move(generator)),
m_access_log_enabled(false), m_data_cache_attachable(false), m_path_cache_attachable(false), m_path_cache_attached(false), m_multi_commit_supported(false)
m_access_log_enabled(false), m_data_cache_attachable(false), m_path_cache_attachable(false), m_path_cache_attached(false), m_multi_commit_supported(false),
m_path_flags()
{
R_ABORT_UNLESS(ValidateMountName(n));
std::strncpy(m_name.str, n, MountNameLengthMax);
m_name.str[MountNameLengthMax] = 0;
R_ABORT_UNLESS(SetMountName(m_name.str, n));
if (std::strcmp(m_name.str, AMS_FS_IMPL_HOST_ROOT_FILE_SYSTEM_MOUNT_NAME) == 0) {
m_path_flags.AllowWindowsPath();
}
}
FileSystemAccessor::~FileSystemAccessor() {
@@ -83,13 +81,12 @@ namespace ams::fs::impl {
Result FileSystemAccessor::GetCommonMountName(char *dst, size_t dst_size) const {
R_UNLESS(m_mount_name_generator != nullptr, fs::ResultPreconditionViolation());
return m_mount_name_generator->GenerateCommonMountName(dst, dst_size);
R_RETURN(m_mount_name_generator->GenerateCommonMountName(dst, dst_size));
}
std::shared_ptr<fssrv::impl::FileSystemInterfaceAdapter> FileSystemAccessor::GetMultiCommitTarget() {
if (m_multi_commit_supported) {
/* TODO: Support multi commit. */
AMS_ABORT();
AMS_ABORT("TODO: Support multi commit");
}
return nullptr;
}
@@ -104,71 +101,126 @@ namespace ams::fs::impl {
Remove(m_open_dir_list, d);
}
Result FileSystemAccessor::SetUpPath(fs::Path *out, const char *p) {
/* Initialize the path appropriately. */
bool normalized;
size_t len;
if (R_SUCCEEDED(PathFormatter::IsNormalized(std::addressof(normalized), std::addressof(len), p, m_path_flags)) && normalized) {
/* We can use the input buffer directly. */
out->SetShallowBuffer(p);
} else {
/* Initialize with appropriate slash replacement. */
if (m_path_flags.IsWindowsPathAllowed()) {
R_TRY(out->InitializeWithReplaceForwardSlashes(p));
} else {
R_TRY(out->InitializeWithReplaceBackslash(p));
}
/* Ensure we're normalized. */
R_TRY(out->Normalize(m_path_flags));
}
/* Check the path isn't too long. */
R_UNLESS(out->GetLength() <= fs::EntryNameLengthMax, fs::ResultTooLongPath());
R_SUCCEED();
}
Result FileSystemAccessor::CreateFile(const char *path, s64 size, int option) {
R_TRY(ValidatePath(m_name.str, path));
/* Create path. */
fs::Path normalized_path;
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
if (m_path_cache_attached) {
/* TODO: Path cache */
R_TRY(m_impl->CreateFile(path, size, option));
R_TRY(m_impl->CreateFile(normalized_path, size, option));
} else {
R_TRY(m_impl->CreateFile(path, size, option));
R_TRY(m_impl->CreateFile(normalized_path, size, option));
}
return ResultSuccess();
R_SUCCEED();
}
Result FileSystemAccessor::DeleteFile(const char *path) {
R_TRY(ValidatePath(m_name.str, path));
return m_impl->DeleteFile(path);
/* Create path. */
fs::Path normalized_path;
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
R_RETURN(m_impl->DeleteFile(normalized_path));
}
Result FileSystemAccessor::CreateDirectory(const char *path) {
R_TRY(ValidatePath(m_name.str, path));
return m_impl->CreateDirectory(path);
/* Create path. */
fs::Path normalized_path;
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
R_RETURN(m_impl->CreateDirectory(normalized_path));
}
Result FileSystemAccessor::DeleteDirectory(const char *path) {
R_TRY(ValidatePath(m_name.str, path));
return m_impl->DeleteDirectory(path);
/* Create path. */
fs::Path normalized_path;
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
R_RETURN(m_impl->DeleteDirectory(normalized_path));
}
Result FileSystemAccessor::DeleteDirectoryRecursively(const char *path) {
R_TRY(ValidatePath(m_name.str, path));
return m_impl->DeleteDirectoryRecursively(path);
/* Create path. */
fs::Path normalized_path;
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
R_RETURN(m_impl->DeleteDirectoryRecursively(normalized_path));
}
Result FileSystemAccessor::RenameFile(const char *old_path, const char *new_path) {
R_TRY(ValidatePath(m_name.str, old_path));
R_TRY(ValidatePath(m_name.str, new_path));
/* Create path. */
fs::Path normalized_old_path;
fs::Path normalized_new_path;
R_TRY(this->SetUpPath(std::addressof(normalized_old_path), old_path));
R_TRY(this->SetUpPath(std::addressof(normalized_new_path), new_path));
if (m_path_cache_attached) {
/* TODO: Path cache */
R_TRY(m_impl->RenameFile(old_path, new_path));
R_TRY(m_impl->RenameFile(normalized_old_path, normalized_new_path));
} else {
R_TRY(m_impl->RenameFile(old_path, new_path));
R_TRY(m_impl->RenameFile(normalized_old_path, normalized_new_path));
}
return ResultSuccess();
R_SUCCEED();
}
Result FileSystemAccessor::RenameDirectory(const char *old_path, const char *new_path) {
R_TRY(ValidatePath(m_name.str, old_path));
R_TRY(ValidatePath(m_name.str, new_path));
/* Create path. */
fs::Path normalized_old_path;
fs::Path normalized_new_path;
R_TRY(this->SetUpPath(std::addressof(normalized_old_path), old_path));
R_TRY(this->SetUpPath(std::addressof(normalized_new_path), new_path));
if (m_path_cache_attached) {
/* TODO: Path cache */
R_TRY(m_impl->RenameDirectory(old_path, new_path));
R_TRY(m_impl->RenameDirectory(normalized_old_path, normalized_new_path));
} else {
R_TRY(m_impl->RenameDirectory(old_path, new_path));
R_TRY(m_impl->RenameDirectory(normalized_old_path, normalized_new_path));
}
return ResultSuccess();
R_SUCCEED();
}
Result FileSystemAccessor::GetEntryType(DirectoryEntryType *out, const char *path) {
R_TRY(ValidatePath(m_name.str, path));
return m_impl->GetEntryType(out, path);
/* Create path. */
fs::Path normalized_path;
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
R_RETURN(m_impl->GetEntryType(out, normalized_path));
}
Result FileSystemAccessor::OpenFile(std::unique_ptr<FileAccessor> *out_file, const char *path, OpenMode mode) {
R_TRY(ValidatePath(m_name.str, path));
/* Create path. */
fs::Path normalized_path;
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
std::unique_ptr<fsa::IFile> file;
R_TRY(m_impl->OpenFile(std::addressof(file), path, mode));
R_TRY(m_impl->OpenFile(std::addressof(file), normalized_path, mode));
auto accessor = new FileAccessor(std::move(file), this, mode);
R_UNLESS(accessor != nullptr, fs::ResultAllocationFailureInFileSystemAccessorA());
@@ -187,14 +239,16 @@ namespace ams::fs::impl {
}
out_file->reset(accessor);
return ResultSuccess();
R_SUCCEED();
}
Result FileSystemAccessor::OpenDirectory(std::unique_ptr<DirectoryAccessor> *out_dir, const char *path, OpenDirectoryMode mode) {
R_TRY(ValidatePath(m_name.str, path));
/* Create path. */
fs::Path normalized_path;
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
std::unique_ptr<fsa::IDirectory> dir;
R_TRY(m_impl->OpenDirectory(std::addressof(dir), path, mode));
R_TRY(m_impl->OpenDirectory(std::addressof(dir), normalized_path, mode));
auto accessor = new DirectoryAccessor(std::move(dir), *this);
R_UNLESS(accessor != nullptr, fs::ResultAllocationFailureInFileSystemAccessorB());
@@ -205,7 +259,7 @@ namespace ams::fs::impl {
}
out_dir->reset(accessor);
return ResultSuccess();
R_SUCCEED();
}
Result FileSystemAccessor::Commit() {
@@ -213,30 +267,47 @@ namespace ams::fs::impl {
std::scoped_lock lk(m_open_list_lock);
R_ABORT_UNLESS(ValidateNoOpenWriteModeFiles(m_open_file_list));
}
return m_impl->Commit();
R_RETURN(m_impl->Commit());
}
Result FileSystemAccessor::GetFreeSpaceSize(s64 *out, const char *path) {
R_TRY(ValidatePath(m_name.str, path));
return m_impl->GetFreeSpaceSize(out, path);
/* Create path. */
fs::Path normalized_path;
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
R_RETURN(m_impl->GetFreeSpaceSize(out, normalized_path));
}
Result FileSystemAccessor::GetTotalSpaceSize(s64 *out, const char *path) {
R_TRY(ValidatePath(m_name.str, path));
return m_impl->GetTotalSpaceSize(out, path);
/* Create path. */
fs::Path normalized_path;
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
R_RETURN(m_impl->GetTotalSpaceSize(out, normalized_path));
}
Result FileSystemAccessor::CleanDirectoryRecursively(const char *path) {
R_TRY(ValidatePath(m_name.str, path));
return m_impl->CleanDirectoryRecursively(path);
/* Create path. */
fs::Path normalized_path;
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
R_RETURN(m_impl->CleanDirectoryRecursively(normalized_path));
}
Result FileSystemAccessor::GetFileTimeStampRaw(FileTimeStampRaw *out, const char *path) {
return m_impl->GetFileTimeStampRaw(out, path);
/* Create path. */
fs::Path normalized_path;
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
R_RETURN(m_impl->GetFileTimeStampRaw(out, normalized_path));
}
Result FileSystemAccessor::QueryEntry(char *dst, size_t dst_size, const char *src, size_t src_size, fsa::QueryId query, const char *path) {
return m_impl->QueryEntry(dst, dst_size, src, src_size, query, path);
/* Create path. */
fs::Path normalized_path;
R_TRY(this->SetUpPath(std::addressof(normalized_path), path));
R_RETURN(m_impl->QueryEntry(dst, dst_size, src, src_size, query, normalized_path));
}

View File

@@ -42,6 +42,7 @@ namespace ams::fs::impl {
bool m_path_cache_attachable;
bool m_path_cache_attached;
bool m_multi_commit_supported;
PathFlags m_path_flags;
public:
FileSystemAccessor(const char *name, std::unique_ptr<fsa::IFileSystem> &&fs, std::unique_ptr<fsa::ICommonMountNameGenerator> &&generator = nullptr);
virtual ~FileSystemAccessor();
@@ -93,6 +94,8 @@ namespace ams::fs::impl {
private:
void NotifyCloseFile(FileAccessor *f);
void NotifyCloseDirectory(DirectoryAccessor *d);
public:
Result SetUpPath(fs::Path *out, const char *p);
};
}

View File

@@ -23,22 +23,32 @@ namespace ams::fs::impl {
namespace {
const char *FindMountNameDriveSeparator(const char *path) {
for (const char *cur = path; cur < path + MountNameLengthMax + 1; cur++) {
for (const char *cur = path; cur < path + MountNameLengthMax + 1 && *cur != StringTraits::NullTerminator; ++cur) {
if (*cur == StringTraits::DriveSeparator) {
return cur;
} else if (PathNormalizer::IsNullTerminator(*cur)) {
break;
}
}
return nullptr;
}
constexpr bool IsHostRootPath(const char *path) {
#if defined(ATMOSPHERE_OS_HORIZON) || defined(ATMOSPHERE_OS_WINDOWS)
return fs::IsWindowsDrive(path) || fs::IsUncPath(path);
#elif defined(ATMOSPHERE_OS_LINUX) || defined(ATMOSPHERE_OS_MACOS)
return fs::IsPathAbsolute(path);
#else
#error "Unknown OS for host path identification"
#endif
}
Result GetMountNameAndSubPath(MountName *out_mount_name, const char **out_sub_path, const char *path) {
/* Handle the Host-path case. */
if (fs::IsWindowsDrive(path) || fs::IsUnc(path)) {
if (IsHostRootPath(path)) {
std::strncpy(out_mount_name->str, HostRootFileSystemMountName, MountNameLengthMax);
out_mount_name->str[MountNameLengthMax] = '\x00';
return ResultSuccess();
*out_sub_path = path;
R_SUCCEED();
}
/* Locate the drive separator. */
@@ -47,34 +57,37 @@ namespace ams::fs::impl {
/* Ensure the mount name isn't too long. */
const size_t len = drive_separator - path;
R_UNLESS(0 < len, fs::ResultInvalidMountName());
R_UNLESS(len <= MountNameLengthMax, fs::ResultInvalidMountName());
/* Ensure the result sub-path is valid. */
const char *sub_path = drive_separator + 1;
R_UNLESS(!PathNormalizer::IsNullTerminator(sub_path[0]), fs::ResultInvalidMountName());
R_UNLESS(PathNormalizer::IsAnySeparator(sub_path[0]), fs::ResultInvalidPathFormat());
const auto starts_with_dir = (sub_path[0] == StringTraits::DirectorySeparator) || (sub_path[0] == StringTraits::AlternateDirectorySeparator);
R_UNLESS(starts_with_dir, fs::ResultInvalidPathFormat());
/* Set output. */
std::memcpy(out_mount_name->str, path, len);
out_mount_name->str[len] = StringTraits::NullTerminator;
*out_sub_path = sub_path;
return ResultSuccess();
R_SUCCEED();
}
}
bool IsValidMountName(const char *name) {
if (PathNormalizer::IsNullTerminator(name[0])) {
if (name[0] == StringTraits::NullTerminator) {
return false;
}
if ((('a' <= name[0] && name[0] <= 'z') || ('A' <= name[0] && name[0] <= 'Z')) && PathNormalizer::IsNullTerminator(name[1])) {
if ((('a' <= name[0] && name[0] <= 'z') || ('A' <= name[0] && name[0] <= 'Z')) && name[1] == StringTraits::NullTerminator) {
return false;
}
size_t len = 0;
for (const char *cur = name; !PathNormalizer::IsNullTerminator(*cur); cur++) {
if (*cur == StringTraits::DriveSeparator || PathNormalizer::IsSeparator(*cur)) {
for (const char *cur = name; *cur != StringTraits::NullTerminator; ++cur) {
if (*cur == StringTraits::DriveSeparator || *cur == StringTraits::DirectorySeparator) {
return false;
}
@@ -83,8 +96,7 @@ namespace ams::fs::impl {
}
}
/* TODO: N validates that the mount name decodes via utf-8 here. */
return true;
return util::VerifyUtf8String(name, len);
}
bool IsReservedMountName(const char *name) {
@@ -108,7 +120,7 @@ namespace ams::fs::impl {
R_UNLESS(out_sub_path != nullptr, fs::ResultUnexpectedInFindFileSystemA());
R_UNLESS(path != nullptr, fs::ResultNullptrArgument());
R_UNLESS(strncmp(path, HostRootFileSystemMountName, strnlen(HostRootFileSystemMountName, sizeof(MountName))) != 0, fs::ResultNotMounted());
R_UNLESS(strncmp(path, HostRootFileSystemMountName, util::Strnlen(HostRootFileSystemMountName, sizeof(MountName))) != 0, fs::ResultNotMounted());
MountName mount_name;
R_TRY(GetMountNameAndSubPath(std::addressof(mount_name), out_sub_path, path));
@@ -132,10 +144,6 @@ namespace ams::fs::impl {
namespace ams::fs {
namespace {
}
Result ConvertToFsCommonPath(char *dst, size_t dst_size, const char *src) {
/* Ensure neither argument is nullptr. */
AMS_FS_R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
@@ -150,7 +158,7 @@ namespace ams::fs {
AMS_FS_R_TRY(impl::Find(std::addressof(accessor), mount_name.str));
AMS_FS_R_TRY(accessor->GetCommonMountName(dst, dst_size));
const auto mount_name_len = strnlen(dst, dst_size);
const auto mount_name_len = util::Strnlen(dst, dst_size);
const auto common_path_len = util::SNPrintf(dst + mount_name_len, dst_size - mount_name_len, "%s", sub_path);
AMS_FS_R_UNLESS(static_cast<size_t>(common_path_len) < dst_size - mount_name_len, fs::ResultTooLongPath());

View File

@@ -164,62 +164,27 @@ namespace ams::fs {
}
Result GetFreeSpaceSize(s64 *out, const char *path) {
/* Find the filesystem without access logging. */
impl::FileSystemAccessor *accessor;
const char *sub_path = nullptr;
const char *sub_path;
AMS_FS_R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path));
/* Get the accessor. */
auto find_impl = [&]() -> Result {
R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
R_UNLESS(path != nullptr, fs::ResultNullptrArgument());
if (impl::IsValidMountName(path)) {
R_TRY(impl::Find(std::addressof(accessor), path));
} else {
R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path));
}
return ResultSuccess();
};
/* Get the total space size. */
AMS_FS_R_TRY(accessor->GetFreeSpaceSize(out, sub_path));
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_UNLESS_R_SUCCEEDED(find_impl(), AMS_FS_IMPL_ACCESS_LOG_FORMAT_GET_SPACE_SIZE(out, path)));
/* Get the space size. */
auto get_size_impl = [&]() -> Result {
R_UNLESS(sub_path == nullptr || std::strcmp(sub_path, "/") == 0, fs::ResultInvalidMountName());
R_TRY(accessor->GetFreeSpaceSize(out, "/"));
return ResultSuccess();
};
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_FILESYSTEM(get_size_impl(), nullptr, accessor, AMS_FS_IMPL_ACCESS_LOG_FORMAT_GET_SPACE_SIZE(out, path)));
return ResultSuccess();
R_SUCCEED();
}
Result GetTotalSpaceSize(s64 *out, const char *path) {
/* NOTE: Nintendo does not do access logging here, and does not support mount-name instead of path. */
/* Find the filesystem without access logging. */
impl::FileSystemAccessor *accessor;
const char *sub_path = nullptr;
const char *sub_path;
AMS_FS_R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path));
/* Get the accessor. */
auto find_impl = [&]() -> Result {
R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
R_UNLESS(path != nullptr, fs::ResultNullptrArgument());
if (impl::IsValidMountName(path)) {
R_TRY(impl::Find(std::addressof(accessor), path));
} else {
R_TRY(impl::FindFileSystem(std::addressof(accessor), std::addressof(sub_path), path));
}
return ResultSuccess();
};
/* Get the total space size. */
AMS_FS_R_TRY(accessor->GetTotalSpaceSize(out, sub_path));
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_UNLESS_R_SUCCEEDED(find_impl(), AMS_FS_IMPL_ACCESS_LOG_FORMAT_GET_SPACE_SIZE(out, path)));
/* Get the space size. */
auto get_size_impl = [&]() -> Result {
R_UNLESS(sub_path == nullptr || std::strcmp(sub_path, "/") == 0, fs::ResultInvalidMountName());
R_TRY(accessor->GetTotalSpaceSize(out, "/"));
return ResultSuccess();
};
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_FILESYSTEM(get_size_impl(), nullptr, accessor, AMS_FS_IMPL_ACCESS_LOG_FORMAT_GET_SPACE_SIZE(out, path)));
return ResultSuccess();
R_SUCCEED();
}
Result SetConcatenationFileAttribute(const char *path) {
@@ -229,7 +194,7 @@ namespace ams::fs {
AMS_FS_R_TRY(accessor->QueryEntry(nullptr, 0, nullptr, 0, fsa::QueryId::SetConcatenationFileAttribute, sub_path));
return ResultSuccess();
R_SUCCEED();
}
Result OpenFile(FileHandle *out, std::unique_ptr<fsa::IFile> &&file, int mode) {
@@ -239,7 +204,7 @@ namespace ams::fs {
AMS_FS_R_UNLESS(file_accessor != nullptr, fs::ResultAllocationFailureInNew());
out->handle = file_accessor.release();
return ResultSuccess();
R_SUCCEED();
}
namespace {
@@ -249,7 +214,7 @@ namespace ams::fs {
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_UNLESS_R_SUCCEEDED(impl::Find(std::addressof(accessor), mount_name), AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT, mount_name));
AMS_FS_R_TRY(AMS_FS_IMPL_ACCESS_LOG_FILESYSTEM_WITH_NAME(accessor->Commit(), nullptr, accessor, func_name, AMS_FS_IMPL_ACCESS_LOG_FORMAT_MOUNT, mount_name));
return ResultSuccess();
R_SUCCEED();
}
}

View File

@@ -36,9 +36,13 @@ namespace ams::fs {
}
Result GetFileTimeStampRawForDebug(FileTimeStampRaw *out, const char *path) {
AMS_FS_R_TRY(impl::GetFileTimeStampRawForDebug(out, path));
return ResultSuccess();
Result GetFileTimeStamp(FileTimeStamp *out, const char *path) {
fs::FileTimeStampRaw raw;
AMS_FS_R_TRY(impl::GetFileTimeStampRawForDebug(std::addressof(raw), path));
static_assert(sizeof(raw) == sizeof(*out));
std::memcpy(out, std::addressof(raw), sizeof(raw));
R_SUCCEED();
}
}

View File

@@ -38,23 +38,4 @@ namespace ams::fs::impl {
}
};
class RemoteEventNotifierObjectAdapter final : public ::ams::fs::IEventNotifier, public ::ams::fs::impl::Newable {
private:
::FsEventNotifier m_notifier;
public:
RemoteEventNotifierObjectAdapter(::FsEventNotifier &n) : m_notifier(n) { /* ... */ }
virtual ~RemoteEventNotifierObjectAdapter() { fsEventNotifierClose(std::addressof(m_notifier)); }
private:
virtual Result DoBindEvent(os::SystemEventType *out, os::EventClearMode clear_mode) override {
/* Get the handle. */
::Event e;
R_TRY(fsEventNotifierGetEventHandle(std::addressof(m_notifier), std::addressof(e), false));
/* Create the system event. */
os::AttachReadableHandleToSystemEvent(out, e.revent, true, clear_mode);
return ResultSuccess();
}
};
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 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::fs::impl {
sf::SharedPointer<fssrv::sf::IFileSystemProxy> GetFileSystemProxyServiceObject();
sf::SharedPointer<fssrv::sf::IFileSystemProxyForLoader> GetFileSystemProxyForLoaderServiceObject();
}

View File

@@ -0,0 +1,263 @@
/*
* Copyright (c) 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::fs::impl {
class FileServiceObjectAdapter : public ::ams::fs::impl::Newable, public ::ams::fs::fsa::IFile {
NON_COPYABLE(FileServiceObjectAdapter);
NON_MOVEABLE(FileServiceObjectAdapter);
private:
sf::SharedPointer<fssrv::sf::IFile> m_x;
public:
explicit FileServiceObjectAdapter(sf::SharedPointer<fssrv::sf::IFile> &&o) : m_x(o) { /* ... */}
virtual ~FileServiceObjectAdapter() { /* ... */ }
public:
virtual Result DoRead(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final {
s64 read_size = 0;
R_TRY(m_x->Read(std::addressof(read_size), offset, sf::OutNonSecureBuffer(buffer, size), static_cast<s64>(size), option));
*out = static_cast<size_t>(read_size);
R_SUCCEED();
}
virtual Result DoGetSize(s64 *out) override final {
R_RETURN(m_x->GetSize(out));
}
virtual Result DoFlush() override final {
R_RETURN(m_x->Flush());
}
virtual Result DoWrite(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final {
R_RETURN(m_x->Write(offset, sf::InNonSecureBuffer(buffer, size), static_cast<s64>(size), option));
}
virtual Result DoSetSize(s64 size) override final {
R_RETURN(m_x->SetSize(size));
}
virtual Result DoOperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final {
switch (op_id) {
case OperationId::Invalidate:
{
fs::QueryRangeInfo dummy_range_info;
R_RETURN(m_x->OperateRange(std::addressof(dummy_range_info), static_cast<s32>(op_id), offset, size));
}
case OperationId::QueryRange:
{
R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
R_UNLESS(dst_size == sizeof(fs::QueryRangeInfo), fs::ResultInvalidSize());
R_RETURN(m_x->OperateRange(reinterpret_cast<fs::QueryRangeInfo *>(dst), static_cast<s32>(op_id), offset, size));
}
default:
{
R_RETURN(m_x->OperateRangeWithBuffer(sf::OutNonSecureBuffer(dst, dst_size), sf::InNonSecureBuffer(src, src_size), static_cast<s32>(op_id), offset, size));
}
}
}
public:
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override final {
AMS_ABORT("Invalid GetDomainObjectId call");
}
};
class DirectoryServiceObjectAdapter : public ::ams::fs::impl::Newable, public ::ams::fs::fsa::IDirectory {
NON_COPYABLE(DirectoryServiceObjectAdapter);
NON_MOVEABLE(DirectoryServiceObjectAdapter);
private:
sf::SharedPointer<fssrv::sf::IDirectory> m_x;
public:
explicit DirectoryServiceObjectAdapter(sf::SharedPointer<fssrv::sf::IDirectory> &&o) : m_x(o) { /* ... */}
virtual ~DirectoryServiceObjectAdapter() { /* ... */ }
public:
virtual Result DoRead(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) override final {
R_RETURN(m_x->Read(out_count, sf::OutBuffer(out_entries, max_entries * sizeof(*out_entries))));
}
virtual Result DoGetEntryCount(s64 *out) override final {
R_RETURN(m_x->GetEntryCount(out));
}
public:
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override final {
AMS_ABORT("Invalid GetDomainObjectId call");
}
};
class FileSystemServiceObjectAdapter : public ::ams::fs::impl::Newable, public ::ams::fs::fsa::IFileSystem {
NON_COPYABLE(FileSystemServiceObjectAdapter);
NON_MOVEABLE(FileSystemServiceObjectAdapter);
private:
sf::SharedPointer<fssrv::sf::IFileSystem> m_x;
public:
explicit FileSystemServiceObjectAdapter(sf::SharedPointer<fssrv::sf::IFileSystem> &&o) : m_x(o) { /* ... */}
virtual ~FileSystemServiceObjectAdapter() { /* ... */ }
sf::SharedPointer<fssrv::sf::IFileSystem> GetFileSystem() const { return m_x; }
private:
static Result GetPathForServiceObject(fssrv::sf::Path *out, const fs::Path &path) {
const size_t len = util::Strlcpy<char>(out->str, path.GetString(), sizeof(out->str));
R_UNLESS(len < sizeof(out->str), fs::ResultTooLongPath());
R_SUCCEED();
}
private:
virtual Result DoOpenFile(std::unique_ptr<fsa::IFile> *out_file, const fs::Path &path, OpenMode mode) override final {
/* Convert the path. */
fssrv::sf::Path fsp_path;
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
/* Open the file. */
sf::SharedPointer<fssrv::sf::IFile> file;
R_TRY(m_x->OpenFile(std::addressof(file), fsp_path, mode));
/* Create the output fsa file. */
out_file->reset(new FileServiceObjectAdapter(std::move(file)));
R_UNLESS(out_file != nullptr, fs::ResultAllocationFailureInNew());
R_SUCCEED();
}
virtual Result DoOpenDirectory(std::unique_ptr<fsa::IDirectory> *out_dir, const fs::Path &path, OpenDirectoryMode mode) override final {
/* Convert the path. */
fssrv::sf::Path fsp_path;
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
/* Open the directory. */
sf::SharedPointer<fssrv::sf::IDirectory> dir;
R_TRY(m_x->OpenDirectory(std::addressof(dir), fsp_path, mode));
/* Create the output fsa directory. */
out_dir->reset(new DirectoryServiceObjectAdapter(std::move(dir)));
R_UNLESS(out_dir != nullptr, fs::ResultAllocationFailureInNew());
R_SUCCEED();
}
virtual Result DoGetEntryType(DirectoryEntryType *out, const fs::Path &path) override final {
/* Convert the path. */
fssrv::sf::Path fsp_path;
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
R_RETURN(m_x->GetEntryType(out, fsp_path));
}
virtual Result DoCommit() override final {
R_RETURN(m_x->Commit());
}
virtual Result DoCreateFile(const fs::Path &path, s64 size, int flags) override final {
/* Convert the path. */
fssrv::sf::Path fsp_path;
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
R_RETURN(m_x->CreateFile(fsp_path, size, flags));
}
virtual Result DoDeleteFile(const fs::Path &path) override final {
/* Convert the path. */
fssrv::sf::Path fsp_path;
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
R_RETURN(m_x->DeleteFile(fsp_path));
}
virtual Result DoCreateDirectory(const fs::Path &path) override final {
/* Convert the path. */
fssrv::sf::Path fsp_path;
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
R_RETURN(m_x->CreateDirectory(fsp_path));
}
virtual Result DoDeleteDirectory(const fs::Path &path) override final {
/* Convert the path. */
fssrv::sf::Path fsp_path;
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
R_RETURN(m_x->DeleteDirectory(fsp_path));
}
virtual Result DoDeleteDirectoryRecursively(const fs::Path &path) override final {
/* Convert the path. */
fssrv::sf::Path fsp_path;
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
R_RETURN(m_x->DeleteDirectoryRecursively(fsp_path));
}
virtual Result DoRenameFile(const fs::Path &old_path, const fs::Path &new_path) override final {
/* Convert the path. */
fssrv::sf::Path fsp_old_path;
fssrv::sf::Path fsp_new_path;
R_TRY(GetPathForServiceObject(std::addressof(fsp_old_path), old_path));
R_TRY(GetPathForServiceObject(std::addressof(fsp_new_path), new_path));
R_RETURN(m_x->RenameFile(fsp_old_path, fsp_new_path));
}
virtual Result DoRenameDirectory(const fs::Path &old_path, const fs::Path &new_path) override final {
/* Convert the path. */
fssrv::sf::Path fsp_old_path;
fssrv::sf::Path fsp_new_path;
R_TRY(GetPathForServiceObject(std::addressof(fsp_old_path), old_path));
R_TRY(GetPathForServiceObject(std::addressof(fsp_new_path), new_path));
R_RETURN(m_x->RenameDirectory(fsp_old_path, fsp_new_path));
}
virtual Result DoCleanDirectoryRecursively(const fs::Path &path) override final {
/* Convert the path. */
fssrv::sf::Path fsp_path;
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
R_RETURN(m_x->CleanDirectoryRecursively(fsp_path));
}
virtual Result DoGetFreeSpaceSize(s64 *out, const fs::Path &path) override final {
/* Convert the path. */
fssrv::sf::Path fsp_path;
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
R_RETURN(m_x->GetFreeSpaceSize(out, fsp_path));
}
virtual Result DoGetTotalSpaceSize(s64 *out, const fs::Path &path) override final {
/* Convert the path. */
fssrv::sf::Path fsp_path;
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
R_RETURN(m_x->GetTotalSpaceSize(out, fsp_path));
}
virtual Result DoGetFileTimeStampRaw(fs::FileTimeStampRaw *out, const fs::Path &path) override final {
/* Convert the path. */
fssrv::sf::Path fsp_path;
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
R_RETURN(m_x->GetFileTimeStampRaw(out, fsp_path));
}
virtual Result DoQueryEntry(char *dst, size_t dst_size, const char *src, size_t src_size, fs::fsa::QueryId query, const fs::Path &path) override {
fssrv::sf::Path fsp_path;
R_TRY(GetPathForServiceObject(std::addressof(fsp_path), path));
R_RETURN(m_x->QueryEntry(sf::OutBuffer(dst, dst_size), sf::InBuffer(src, src_size), static_cast<s32>(query), fsp_path));
}
};
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright (c) 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>
#include "fs_library.hpp"
#include "fs_file_system_service_object_adapter.hpp"
#include "../../fssrv/impl/fssrv_allocator_for_service_framework.hpp"
namespace ams::fs::impl {
#if !defined(ATMOSPHERE_OS_HORIZON)
namespace {
constexpr size_t SystemHeapSize = 8_MB;
alignas(os::MemoryPageSize) constinit u8 g_system_heap[SystemHeapSize];
ALWAYS_INLINE auto &GetSystemHeapAllocator() {
AMS_FUNCTION_LOCAL_STATIC(mem::StandardAllocator, s_system_heap_allocator, g_system_heap, sizeof(g_system_heap));
return s_system_heap_allocator;
}
constinit util::optional<fssrv::MemoryResourceFromStandardAllocator> g_system_heap_memory_resource;
void *AllocateForSystem(size_t size) { return g_system_heap_memory_resource->Allocate(size); }
void DeallocateForSystem(void *p, size_t size) { return g_system_heap_memory_resource->Deallocate(p, size); }
[[maybe_unused]] constexpr size_t BufferPoolSize = 6_MB;
[[maybe_unused]] constexpr size_t DeviceBufferSize = 8_MB;
[[maybe_unused]] constexpr size_t BufferManagerHeapSize = 14_MB;
static_assert(util::IsAligned(BufferManagerHeapSize, os::MemoryBlockUnitSize));
//alignas(os::MemoryPageSize) u8 g_buffer_pool[BufferPoolSize];
//alignas(os::MemoryPageSize) u8 g_device_buffer[DeviceBufferSize];
//alignas(os::MemoryPageSize) u8 g_buffer_manager_heap[BufferManagerHeapSize];
//
//alignas(os::MemoryPageSize) u8 g_buffer_manager_work_buffer[64_KB];
/* TODO: Other work buffers. */
/* TODO: Implement pooled threads. */
// constexpr int PooledThreadCount = 12;
// constexpr size_t PooledThreadStackSize = 32_KB;
// fssystem::PooledThread g_pooled_threads[PooledThreadCount];
/* FileSystem creators. */
constinit util::optional<fssrv::fscreator::LocalFileSystemCreator> g_local_fs_creator;
constinit util::optional<fssrv::fscreator::SubDirectoryFileSystemCreator> g_subdir_fs_creator;
constinit fssrv::fscreator::FileSystemCreatorInterfaces g_fs_creator_interfaces = {};
Result InitializeFileSystemLibraryImpl() {
/* Set system allocator. */
fssystem::InitializeAllocator(::ams::fs::impl::Allocate, ::ams::fs::impl::Deallocate);
fssystem::InitializeAllocatorForSystem(::ams::fs::impl::AllocateForSystem, ::ams::fs::impl::DeallocateForSystem);
/* TODO: Many things. */
g_system_heap_memory_resource.emplace(std::addressof(GetSystemHeapAllocator()));
/* Setup fscreators/interfaces. */
g_local_fs_creator.emplace(true);
g_subdir_fs_creator.emplace();
g_fs_creator_interfaces.local_fs_creator = std::addressof(*g_local_fs_creator);
g_fs_creator_interfaces.subdir_fs_creator = std::addressof(*g_subdir_fs_creator);
/* Initialize fssrv. */
const fssrv::FileSystemProxyConfiguration config = {
.m_fs_creator_interfaces = std::addressof(g_fs_creator_interfaces),
.m_base_storage_service_impl = nullptr /* TODO */,
.m_base_file_system_service_impl = nullptr /* TODO */,
.m_nca_file_system_service_impl = nullptr /* TODO */,
.m_save_data_file_system_service_impl = nullptr /* TODO */,
.m_access_failure_management_service_impl = nullptr /* TODO */,
.m_time_service_impl = nullptr /* TODO */,
.m_status_report_service_impl = nullptr /* TODO */,
.m_program_registry_service_impl = nullptr /* TODO */,
.m_access_log_service_impl = nullptr /* TODO */,
.m_debug_configuration_service_impl = nullptr /* TODO */,
};
fssrv::InitializeForFileSystemProxy(config);
R_SUCCEED();
}
class FileSystemLibraryInitializer {
public:
FileSystemLibraryInitializer() {
R_ABORT_UNLESS(InitializeFileSystemLibraryImpl());
}
};
}
#endif
void InitializeFileSystemLibrary() {
#if !defined(ATMOSPHERE_OS_HORIZON)
AMS_FUNCTION_LOCAL_STATIC(FileSystemLibraryInitializer, s_library_initializer);
AMS_UNUSED(s_library_initializer);
#endif
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 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::fs::impl {
void InitializeFileSystemLibrary();
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 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::fs::impl {
#if defined(ATMOSPHERE_OS_HORIZON)
class RemoteDeviceOperator {
private:
::FsDeviceOperator m_operator;
public:
RemoteDeviceOperator(::FsDeviceOperator &o) : m_operator(o) { /* ... */ }
virtual ~RemoteDeviceOperator() {
fsDeviceOperatorClose(std::addressof(m_operator));
}
public:
Result IsSdCardInserted(ams::sf::Out<bool> out) {
R_RETURN(fsDeviceOperatorIsSdCardInserted(std::addressof(m_operator), out.GetPointer()));
}
Result IsGameCardInserted(ams::sf::Out<bool> out) {
R_RETURN(fsDeviceOperatorIsGameCardInserted(std::addressof(m_operator), out.GetPointer()));
}
Result GetGameCardHandle(ams::sf::Out<u32> out) {
static_assert(sizeof(::FsGameCardHandle) == sizeof(u32));
R_RETURN(fsDeviceOperatorGetGameCardHandle(std::addressof(m_operator), reinterpret_cast<::FsGameCardHandle *>(out.GetPointer())));
}
};
static_assert(fssrv::sf::IsIDeviceOperator<RemoteDeviceOperator>);
#endif
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 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::fs::impl {
#if defined(ATMOSPHERE_OS_HORIZON)
class RemoteEventNotifier {
private:
::FsEventNotifier m_notifier;
public:
RemoteEventNotifier(::FsEventNotifier &n) : m_notifier(n) { /* ... */ }
virtual ~RemoteEventNotifier() {
fsEventNotifierClose(std::addressof(m_notifier));
}
public:
Result GetEventHandle(ams::sf::OutCopyHandle out) {
::Event e;
R_TRY(fsEventNotifierGetEventHandle(std::addressof(m_notifier), std::addressof(e), false));
out.SetValue(e.revent, true);
R_SUCCEED();
}
};
static_assert(fssrv::sf::IsIEventNotifier<RemoteEventNotifier>);
#endif
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 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::fs::impl {
class StorageServiceObjectAdapter : public ::ams::fs::impl::Newable, public ::ams::fs::IStorage {
NON_COPYABLE(StorageServiceObjectAdapter);
NON_MOVEABLE(StorageServiceObjectAdapter);
private:
sf::SharedPointer<fssrv::sf::IStorage> m_x;
public:
explicit StorageServiceObjectAdapter(sf::SharedPointer<fssrv::sf::IStorage> &&o) : m_x(o) { /* ... */}
virtual ~StorageServiceObjectAdapter() { /* ... */ }
public:
virtual Result Read(s64 offset, void *buffer, size_t size) override final {
R_RETURN(m_x->Read(offset, sf::OutNonSecureBuffer(buffer, size), static_cast<s64>(size)));
}
virtual Result GetSize(s64 *out) override final {
R_RETURN(m_x->GetSize(out));
}
virtual Result Flush() override final {
R_RETURN(m_x->Flush());
}
virtual Result Write(s64 offset, const void *buffer, size_t size) override final {
R_RETURN(m_x->Write(offset, sf::InNonSecureBuffer(buffer, size), static_cast<s64>(size)));
}
virtual Result SetSize(s64 size) override final {
R_RETURN(m_x->SetSize(size));
}
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final {
AMS_UNUSED(src, src_size);
switch (op_id) {
case OperationId::Invalidate:
{
fs::QueryRangeInfo dummy_range_info;
R_RETURN(m_x->OperateRange(std::addressof(dummy_range_info), static_cast<s32>(op_id), offset, size));
}
case OperationId::QueryRange:
{
R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
R_UNLESS(dst_size == sizeof(fs::QueryRangeInfo), fs::ResultInvalidSize());
R_RETURN(m_x->OperateRange(reinterpret_cast<fs::QueryRangeInfo *>(dst), static_cast<s32>(op_id), offset, size));
}
default:
{
R_THROW(fs::ResultUnsupportedOperationInStorageServiceObjectAdapterA());
}
}
}
};
}

View File

@@ -0,0 +1,623 @@
/*
* Copyright (c) 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 {
namespace {
constexpr size_t DefaultPathBufferSize = fs::EntryNameLengthMax + 1;
consteval PathFlags DecodeFlags(const char *desc) {
PathFlags flags{};
while (*desc) {
switch (*(desc++)) {
case 'B':
flags.AllowBackslash();
break;
case 'E':
flags.AllowEmptyPath();
break;
case 'M':
flags.AllowMountName();
break;
case 'R':
flags.AllowRelativePath();
break;
case 'W':
flags.AllowWindowsPath();
break;
case 'C':
flags.AllowAllCharacters();
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
return flags;
}
consteval size_t Strlen(const char *p) {
size_t len = 0;
while (p[len] != StringTraits::NullTerminator) {
++len;
}
return len;
}
//#define ENABLE_PRINT_FORMAT_TEST_DEBUGGING
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
template<u32 Expected, u32 Actual>
struct Print {
constexpr Print() {
if (std::is_constant_evaluated()) {
__builtin_unreachable();
}
}
};
template<u32 E = 0, u32 A = 0, size_t N = 0>
consteval void PrintResultMismatchImpl(u32 e, u32 a) {
if constexpr (N == 32) {
Print<E, A>{};
} else {
const bool is_e = (e & (1 << N)) != 0;
const bool is_a = (a & (1 << N)) != 0;
if (is_e) {
if (is_a) {
PrintResultMismatchImpl<E | (1 << N), A | (1 << N), N + 1>(e, a);
} else {
PrintResultMismatchImpl<E | (1 << N), A | (0 << N), N + 1>(e, a);
}
} else {
if (is_a) {
PrintResultMismatchImpl<E | (0 << N), A | (1 << N), N + 1>(e, a);
} else {
PrintResultMismatchImpl<E | (0 << N), A | (0 << N), N + 1>(e, a);
}
}
}
}
consteval void PrintResultMismatch(const Result &lhs, const Result &rhs) {
PrintResultMismatchImpl(lhs.GetDescription(), rhs.GetDescription());
}
template<size_t Index, char Expected, char Actual>
struct PrintMismatchChar {
constexpr PrintMismatchChar() {
if (std::is_constant_evaluated()) {
__builtin_unreachable();
}
}
};
template<size_t Ix, char Expected, size_t C = 0>
consteval void PrintCharacterMismatch(char c) {
if (c == static_cast<char>(C)) {
PrintMismatchChar<Ix, Expected, static_cast<char>(C)>{};
return;
}
if constexpr (C < std::numeric_limits<unsigned char>::max()) {
PrintCharacterMismatch<Ix, Expected, C + 1>(c);
}
}
template<size_t Ix, char C = 0>
consteval void PrintCharacterMismatch(char c, char c2) {
if (c == static_cast<char>(C)) {
PrintCharacterMismatch<Ix, static_cast<char>(C)>(c2);
return;
}
if constexpr (C < std::numeric_limits<unsigned char>::max()) {
PrintCharacterMismatch<Ix, C + 1>(c, c2);
}
}
template<size_t Ix = 0>
consteval void PrintCharacterMismatch(size_t ix, char c, char c2) {
if (Ix == ix) {
PrintCharacterMismatch<Ix>(c, c2);
return;
}
if constexpr (Ix <= DefaultPathBufferSize) {
PrintCharacterMismatch<Ix + 1>(ix, c, c2);
}
}
#endif
consteval bool TestNormalizedImpl(const char *path, const PathFlags &flags, size_t buffer_size, const char *normalized, Result expected_result) {
/* Allocate a buffer to normalize into. */
char *buffer = new char[buffer_size];
ON_SCOPE_EXIT { delete[] buffer; };
buffer[buffer_size - 1] = '\xcc';
/* Perform normalization. */
const Result actual_result = PathFormatter::Normalize(buffer, buffer_size, path, Strlen(path) + 1, flags);
/* Check that the expected result matches the actual. */
if (actual_result.GetValue() != expected_result.GetValue()) {
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
PrintResultMismatch(expected_result.GetValue(), actual_result.GetValue());
#endif
return false;
}
/* Check that the expected string matches the actual. */
for (size_t i = 0; i < buffer_size; ++i) {
if (normalized[i] != StringTraits::NullTerminator || R_SUCCEEDED(expected_result)) {
if (buffer[i] != normalized[i]) {
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
PrintCharacterMismatch(i, normalized[i], buffer[i]);
#endif
return false;
}
}
if (normalized[i] == StringTraits::NullTerminator || buffer[i] == StringTraits::NullTerminator) {
break;
}
}
return true;
}
struct NormalizeTestData {
const char *path;
const char *flag_desc;
const char *normalized;
Result result;
size_t buffer_size = DefaultPathBufferSize;
};
template<size_t N, size_t Ix = 0>
consteval bool DoNormalizeTests(const NormalizeTestData (&tests)[N]) {
if constexpr (Ix >= N) {
return true;
}
const auto &test = tests[Ix];
if (!TestNormalizedImpl(test.path, DecodeFlags(test.flag_desc), test.buffer_size, test.normalized, test.result)) {
return false;
}
if constexpr (Ix < N) {
return DoNormalizeTests<N, Ix + 1>(tests);
} else {
AMS_ASSUME(false);
}
}
consteval bool TestNormalizeEmptyPath() {
constexpr NormalizeTestData Tests[] = {
{ "", "", "", fs::ResultInvalidPathFormat() },
{ "", "E", "", ResultSuccess() },
{ "/aa/bb/../cc", "E", "/aa/cc", ResultSuccess() },
};
return DoNormalizeTests(Tests);
}
static_assert(TestNormalizeEmptyPath());
consteval bool TestNormalizeMountName() {
constexpr NormalizeTestData Tests[] = {
{ "mount:/aa/bb", "", "", fs::ResultInvalidPathFormat() },
{ "mount:/aa/bb", "W", "", fs::ResultInvalidPathFormat() },
{ "mount:/aa/bb", "M", "mount:/aa/bb", ResultSuccess() },
{ "mount:/aa/./bb", "M", "mount:/aa/bb", ResultSuccess() },
{ "mount:\\aa\\bb", "M", "mount:", fs::ResultInvalidPathFormat() },
{ "m:/aa/bb", "M", "", fs::ResultInvalidPathFormat() },
{ "mo>unt:/aa/bb", "M", "", fs::ResultInvalidCharacter() },
{ "moun?t:/aa/bb", "M", "", fs::ResultInvalidCharacter() },
{ "mo&unt:/aa/bb", "M", "mo&unt:/aa/bb", ResultSuccess() },
{ "/aa/./bb", "M", "/aa/bb", ResultSuccess() },
{ "mount/aa/./bb", "M", "", fs::ResultInvalidPathFormat() }
};
return DoNormalizeTests(Tests);
}
static_assert(TestNormalizeMountName());
consteval bool TestNormalizeWindowsPath() {
constexpr NormalizeTestData Tests[] = {
{ "c:/aa/bb", "", "", fs::ResultInvalidPathFormat() },
{ "c:\\aa\\bb", "", "", fs::ResultInvalidCharacter() },
{ "\\\\host\\share", "", "", fs::ResultInvalidCharacter() },
{ "\\\\.\\c:\\", "", "", fs::ResultInvalidCharacter() },
{ "\\\\.\\c:/aa/bb/.", "", "", fs::ResultInvalidCharacter() },
{ "\\\\?\\c:\\", "", "", fs::ResultInvalidCharacter() },
{ "mount:\\\\host\\share\\aa\\bb", "M", "mount:", fs::ResultInvalidCharacter() },
{ "mount:\\\\host/share\\aa\\bb", "M", "mount:", fs::ResultInvalidCharacter() },
{ "c:\\aa\\..\\..\\..\\bb", "W", "c:/bb", ResultSuccess() },
{ "mount:/\\\\aa\\..\\bb", "MW", "mount:", fs::ResultInvalidPathFormat() },
{ "mount:/c:\\aa\\..\\bb", "MW", "mount:c:/bb", ResultSuccess() },
{ "mount:/aa/bb", "MW", "mount:/aa/bb", ResultSuccess() },
{ "/mount:/aa/bb", "MW", "/", fs::ResultInvalidCharacter() },
{ "/mount:/aa/bb", "W", "/", fs::ResultInvalidCharacter() },
{ "a:aa/../bb", "MW", "a:aa/bb", ResultSuccess() },
{ "a:aa\\..\\bb", "MW", "a:aa/bb", ResultSuccess() },
{ "/a:aa\\..\\bb", "W", "/", fs::ResultInvalidCharacter() },
{ "\\\\?\\c:\\.\\aa", "W", "\\\\?\\c:/aa", ResultSuccess() },
{ "\\\\.\\c:\\.\\aa", "W", "\\\\.\\c:/aa", ResultSuccess() },
{ "\\\\.\\mount:\\.\\aa", "W", "\\\\./", fs::ResultInvalidCharacter() },
{ "\\\\./.\\aa", "W", "\\\\./aa", ResultSuccess() },
{ "\\\\/aa", "W", "", fs::ResultInvalidPathFormat() },
{ "\\\\\\aa", "W", "", fs::ResultInvalidPathFormat() },
{ "\\\\", "W", "/", ResultSuccess() },
{ "\\\\host\\share", "W", "\\\\host\\share/", ResultSuccess() },
{ "\\\\host\\share\\path", "W", "\\\\host\\share/path", ResultSuccess() },
{ "\\\\host\\share\\path\\aa\\bb\\..\\cc\\.", "W", "\\\\host\\share/path/aa/cc", ResultSuccess() },
{ "\\\\host\\", "W", "", fs::ResultInvalidPathFormat() },
{ "\\\\ho$st\\share\\path", "W", "", fs::ResultInvalidCharacter() },
{ "\\\\host:\\share\\path", "W", "", fs::ResultInvalidCharacter() },
{ "\\\\..\\share\\path", "W", "", fs::ResultInvalidPathFormat() },
{ "\\\\host\\s:hare\\path", "W", "", fs::ResultInvalidCharacter() },
{ "\\\\host\\.\\path", "W", "", fs::ResultInvalidPathFormat() },
{ "\\\\host\\..\\path", "W", "", fs::ResultInvalidPathFormat() },
{ "\\\\host\\sha:re", "W", "", fs::ResultInvalidCharacter() },
{ ".\\\\host\\share", "RW", "..\\\\host\\share/", ResultSuccess() }
};
return DoNormalizeTests(Tests);
}
static_assert(TestNormalizeWindowsPath());
consteval bool TestNormalizeRelativePath() {
constexpr NormalizeTestData Tests[] = {
{ "./aa/bb", "", "", fs::ResultInvalidPathFormat() },
{ "./aa/bb/../cc", "R", "./aa/cc", ResultSuccess() },
{ ".\\aa/bb/../cc", "R", "..", fs::ResultInvalidCharacter() },
{ ".", "R", ".", ResultSuccess() },
{ "../aa/bb", "R", "", fs::ResultDirectoryUnobtainable() },
{ "/aa/./bb", "R", "/aa/bb", ResultSuccess() },
{ "mount:./aa/bb", "MR", "mount:./aa/bb", ResultSuccess() },
{ "mount:./aa/./bb", "MR", "mount:./aa/bb", ResultSuccess() },
{ "mount:./aa/bb", "M", "mount:", fs::ResultInvalidPathFormat() }
};
return DoNormalizeTests(Tests);
}
static_assert(TestNormalizeRelativePath());
consteval bool TestNormalizeBackslash() {
constexpr NormalizeTestData Tests[] = {
{ "\\aa\\bb\\..\\cc", "", "", fs::ResultInvalidPathFormat() },
{ "\\aa\\bb\\..\\cc", "B", "", fs::ResultInvalidPathFormat() },
{ "/aa\\bb\\..\\cc", "", "", fs::ResultInvalidCharacter() },
{ "/aa\\bb\\..\\cc", "B", "/cc", ResultSuccess() },
{ "/aa\\bb\\cc", "", "", fs::ResultInvalidCharacter() },
{ "/aa\\bb\\cc", "B", "/aa\\bb\\cc", ResultSuccess() },
{ "\\\\host\\share\\path\\aa\\bb\\cc", "W", "\\\\host\\share/path/aa/bb/cc", ResultSuccess() },
{ "\\\\host\\share\\path\\aa\\bb\\cc", "WB", "\\\\host\\share/path/aa/bb/cc", ResultSuccess() },
{ "/aa/bb\\../cc/..\\dd\\..\\ee/..", "", "", fs::ResultInvalidCharacter() },
{ "/aa/bb\\../cc/..\\dd\\..\\ee/..", "B", "/aa", ResultSuccess() }
};
return DoNormalizeTests(Tests);
}
static_assert(TestNormalizeBackslash());
consteval bool TestNormalizeAllowAllChars() {
constexpr NormalizeTestData Tests[] = {
{ "/aa/b:b/cc", "", "/aa/", fs::ResultInvalidCharacter() },
{ "/aa/b*b/cc", "", "/aa/", fs::ResultInvalidCharacter() },
{ "/aa/b?b/cc", "", "/aa/", fs::ResultInvalidCharacter() },
{ "/aa/b<b/cc", "", "/aa/", fs::ResultInvalidCharacter() },
{ "/aa/b>b/cc", "", "/aa/", fs::ResultInvalidCharacter() },
{ "/aa/b|b/cc", "", "/aa/", fs::ResultInvalidCharacter() },
{ "/aa/b:b/cc", "C", "/aa/b:b/cc", ResultSuccess() },
{ "/aa/b*b/cc", "C", "/aa/b*b/cc", ResultSuccess() },
{ "/aa/b?b/cc", "C", "/aa/b?b/cc", ResultSuccess() },
{ "/aa/b<b/cc", "C", "/aa/b<b/cc", ResultSuccess() },
{ "/aa/b>b/cc", "C", "/aa/b>b/cc", ResultSuccess() },
{ "/aa/b|b/cc", "C", "/aa/b|b/cc", ResultSuccess() },
{ "/aa/b'b/cc", "", "/aa/b'b/cc", ResultSuccess() },
{ "/aa/b\"b/cc", "", "/aa/b\"b/cc", ResultSuccess() },
{ "/aa/b(b/cc", "", "/aa/b(b/cc", ResultSuccess() },
{ "/aa/b)b/cc", "", "/aa/b)b/cc", ResultSuccess() },
{ "/aa/b'b/cc", "C", "/aa/b'b/cc", ResultSuccess() },
{ "/aa/b\"b/cc", "C", "/aa/b\"b/cc", ResultSuccess() },
{ "/aa/b(b/cc", "C", "/aa/b(b/cc", ResultSuccess() },
{ "/aa/b)b/cc", "C", "/aa/b)b/cc", ResultSuccess() },
{ "mount:/aa/b<b/cc", "MC", "mount:/aa/b<b/cc", ResultSuccess() },
{ "mo>unt:/aa/bb/cc", "MC", "", fs::ResultInvalidCharacter() }
};
return DoNormalizeTests(Tests);
}
static_assert(TestNormalizeAllowAllChars());
consteval bool TestNormalizeAll() {
constexpr NormalizeTestData Tests[] = {
{ "mount:./aa/bb", "WRM", "mount:./aa/bb", ResultSuccess() },
{ "mount:./aa/bb\\cc/dd", "WRM", "mount:./aa/bb/cc/dd", ResultSuccess() },
{ "mount:./aa/bb\\cc/dd", "WRMB", "mount:./aa/bb/cc/dd", ResultSuccess() },
{ "mount:./.c:/aa/bb", "RM", "mount:./", fs::ResultInvalidCharacter() },
{ "mount:.c:/aa/bb", "WRM", "mount:./", fs::ResultInvalidCharacter() },
{ "mount:./cc:/aa/bb", "WRM", "mount:./", fs::ResultInvalidCharacter() },
{ "mount:./\\\\host\\share/aa/bb", "MW", "mount:", fs::ResultInvalidPathFormat() },
{ "mount:./\\\\host\\share/aa/bb", "WRM", "mount:.\\\\host\\share/aa/bb", ResultSuccess() },
{ "mount:.\\\\host\\share/aa/bb", "WRM", "mount:..\\\\host\\share/aa/bb", ResultSuccess() },
{ "mount:..\\\\host\\share/aa/bb", "WRM", "mount:.", fs::ResultDirectoryUnobtainable() },
{ ".\\\\host\\share/aa/bb", "WRM", "..\\\\host\\share/aa/bb", ResultSuccess() },
{ "..\\\\host\\share/aa/bb", "WRM", ".", fs::ResultDirectoryUnobtainable() },
{ "mount:\\\\host\\share/aa/bb", "MW", "mount:\\\\host\\share/aa/bb", ResultSuccess() },
{ "mount:\\aa\\bb", "BM", "mount:", fs::ResultInvalidPathFormat() },
{ "mount:/aa\\bb", "BM", "mount:/aa\\bb", ResultSuccess() },
{ ".//aa/bb", "RW", "./aa/bb", ResultSuccess() },
{ "./aa/bb", "R", "./aa/bb", ResultSuccess() },
{ "./c:/aa/bb", "RW", "./", fs::ResultInvalidCharacter() },
{ "mount:./aa/b:b\\cc/dd", "WRMBC", "mount:./aa/b:b/cc/dd", ResultSuccess() }
};
return DoNormalizeTests(Tests);
}
static_assert(TestNormalizeAll());
consteval bool TestNormalizeSmallBuffer() {
constexpr NormalizeTestData Tests[] = {
{ "/aa/bb", "M", "", fs::ResultTooLongPath(), 1},
{ "mount:/aa/bb", "MR", "", fs::ResultTooLongPath(), 6 },
{ "mount:/aa/bb", "MR", "mount:", fs::ResultTooLongPath(), 7 },
{ "aa/bb", "MR", "./", fs::ResultTooLongPath(), 3 },
{ "\\\\host\\share", "W", "\\\\host\\share", fs::ResultTooLongPath(), 13 }
};
return DoNormalizeTests(Tests);
}
static_assert(TestNormalizeSmallBuffer());
consteval bool TestIsNormalizedImpl(const char *path, const PathFlags &flags, bool expected_normalized, size_t expected_size, Result expected_result) {
/* Perform normalization checking. */
bool actual_normalized;
size_t actual_size;
const Result actual_result = PathFormatter::IsNormalized(std::addressof(actual_normalized), std::addressof(actual_size), path, flags);
/* Check that the expected result matches the actual. */
if (actual_result.GetValue() != expected_result.GetValue()) {
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
PrintResultMismatch(expected_result.GetValue(), actual_result.GetValue());
#endif
return false;
}
/* Check that the expected output matches the actual. */
if (R_SUCCEEDED(expected_result)) {
if (expected_normalized != actual_normalized) {
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
PrintResultMismatchImpl(static_cast<u32>(expected_normalized), static_cast<u32>(actual_normalized));
#endif
return false;
}
if (expected_normalized) {
if (expected_size != actual_size) {
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
PrintResultMismatchImpl(static_cast<u32>(expected_size), static_cast<u32>(actual_size));
#endif
return false;
}
}
}
return true;
}
struct IsNormalizedTestData {
const char *path;
const char *flag_desc;
bool normalized;
size_t len;
Result result;
};
template<size_t N, size_t Ix = 0>
consteval bool DoIsNormalizedTests(const IsNormalizedTestData (&tests)[N]) {
if constexpr (Ix >= N) {
return true;
}
const auto &test = tests[Ix];
if (!TestIsNormalizedImpl(test.path, DecodeFlags(test.flag_desc), test.normalized, test.len, test.result)) {
return false;
}
if constexpr (Ix < N) {
return DoIsNormalizedTests<N, Ix + 1>(tests);
} else {
AMS_ASSUME(false);
}
}
consteval bool TestIsNormalizedEmptyPath() {
constexpr IsNormalizedTestData Tests[] = {
{ "", "", false, 0, fs::ResultInvalidPathFormat() },
{ "", "E", true, 0, ResultSuccess() },
{ "/aa/bb/../cc", "E", false, 0, ResultSuccess() }
};
return DoIsNormalizedTests(Tests);
}
static_assert(TestIsNormalizedEmptyPath());
consteval bool TestIsNormalizedMountName() {
constexpr IsNormalizedTestData Tests[] = {
{ "mount:/aa/bb", "", false, 0, fs::ResultInvalidPathFormat() },
{ "mount:/aa/bb", "W", false, 0, fs::ResultInvalidPathFormat() },
{ "mount:/aa/bb", "M", true, 12, ResultSuccess() },
{ "mount:/aa/./bb", "M", false, 6, ResultSuccess() },
{ "mount:\\aa\\bb", "M", false, 0, fs::ResultInvalidPathFormat() },
{ "m:/aa/bb", "M", false, 0, fs::ResultInvalidPathFormat() },
{ "mo>unt:/aa/bb", "M", false, 0, fs::ResultInvalidCharacter() },
{ "moun?t:/aa/bb", "M", false, 0, fs::ResultInvalidCharacter() },
{ "mo&unt:/aa/bb", "M", true, 13, ResultSuccess() },
{ "/aa/./bb", "M", false, 0, ResultSuccess() },
{ "mount/aa/./bb", "M", false, 0, fs::ResultInvalidPathFormat() }
};
return DoIsNormalizedTests(Tests);
}
static_assert(TestIsNormalizedMountName());
consteval bool TestIsNormalizedWindowsPath() {
constexpr IsNormalizedTestData Tests[] = {
{ "c:/aa/bb", "", false, 0, fs::ResultInvalidPathFormat() },
{ "c:\\aa\\bb", "", false, 0, fs::ResultInvalidPathFormat() },
{ "\\\\host\\share", "", false, 0, fs::ResultInvalidPathFormat() },
{ "\\\\.\\c:\\", "", false, 0, fs::ResultInvalidPathFormat() },
{ "\\\\.\\c:/aa/bb/.", "", false, 0, fs::ResultInvalidPathFormat() },
{ "\\\\?\\c:\\", "", false, 0, fs::ResultInvalidPathFormat() },
{ "mount:\\\\host\\share\\aa\\bb", "M", false, 0, fs::ResultInvalidPathFormat() },
{ "mount:\\\\host/share\\aa\\bb", "M", false, 0, fs::ResultInvalidPathFormat() },
{ "c:\\aa\\..\\..\\..\\bb", "W", false, 0, ResultSuccess() },
{ "mount:/\\\\aa\\..\\bb", "MW", false, 0, ResultSuccess() },
{ "mount:/c:\\aa\\..\\bb", "MW", false, 0, ResultSuccess() },
{ "mount:/aa/bb", "MW", true, 12, ResultSuccess() },
{ "/mount:/aa/bb", "MW", false, 0, fs::ResultInvalidCharacter() },
{ "/mount:/aa/bb", "W", false, 0, fs::ResultInvalidCharacter() },
{ "a:aa/../bb", "MW", false, 8, ResultSuccess() },
{ "a:aa\\..\\bb", "MW", false, 0, ResultSuccess() },
{ "/a:aa\\..\\bb", "W", false, 0, ResultSuccess() },
{ "\\\\?\\c:\\.\\aa", "W", false, 0, ResultSuccess() },
{ "\\\\.\\c:\\.\\aa", "W", false, 0, ResultSuccess() },
{ "\\\\.\\mount:\\.\\aa", "W", false, 0, ResultSuccess() },
{ "\\\\./.\\aa", "W", false, 0, ResultSuccess() },
{ "\\\\/aa", "W", false, 0, fs::ResultInvalidPathFormat() },
{ "\\\\\\aa", "W", false, 0, fs::ResultInvalidPathFormat() },
{ "\\\\", "W", false, 0, ResultSuccess() },
{ "\\\\host\\share", "W", false, 0, ResultSuccess() },
{ "\\\\host\\share\\path", "W", false, 0, ResultSuccess() },
{ "\\\\host\\share\\path\\aa\\bb\\..\\cc\\.", "W", false, 0, ResultSuccess() },
{ "\\\\host\\", "W", false, 0, fs::ResultInvalidPathFormat() },
{ "\\\\ho$st\\share\\path", "W", false, 0, fs::ResultInvalidCharacter() },
{ "\\\\host:\\share\\path", "W", false, 0, fs::ResultInvalidCharacter() },
{ "\\\\..\\share\\path", "W", false, 0, fs::ResultInvalidPathFormat() },
{ "\\\\host\\s:hare\\path", "W", false, 0, fs::ResultInvalidCharacter() },
{ "\\\\host\\.\\path", "W", false, 0, fs::ResultInvalidPathFormat() },
{ "\\\\host\\..\\path", "W", false, 0, fs::ResultInvalidPathFormat() },
{ "\\\\host\\sha:re", "W", false, 0, fs::ResultInvalidCharacter() },
{ ".\\\\host\\share", "RW", false, 0, ResultSuccess() }
};
return DoIsNormalizedTests(Tests);
}
static_assert(TestIsNormalizedWindowsPath());
consteval bool TestIsNormalizedRelativePath() {
constexpr IsNormalizedTestData Tests[] = {
{ "./aa/bb", "", false, 0, fs::ResultInvalidPathFormat() },
{ "./aa/bb/../cc", "R", false, 1, ResultSuccess() },
{ ".\\aa/bb/../cc", "R", false, 0, ResultSuccess() },
{ ".", "R", true, 1, ResultSuccess() },
{ "../aa/bb", "R", false, 0, fs::ResultDirectoryUnobtainable() },
{ "/aa/./bb", "R", false, 0, ResultSuccess() },
{ "mount:./aa/bb", "MR", true, 13, ResultSuccess() },
{ "mount:./aa/./bb", "MR", false, 7, ResultSuccess() },
{ "mount:./aa/bb", "M", false, 0, fs::ResultInvalidPathFormat() }
};
return DoIsNormalizedTests(Tests);
}
static_assert(TestIsNormalizedRelativePath());
consteval bool TestIsNormalizedBackslash() {
constexpr IsNormalizedTestData Tests[] = {
{ "\\aa\\bb\\..\\cc", "", false, 0, fs::ResultInvalidPathFormat() },
{ "\\aa\\bb\\..\\cc", "B", false, 0, fs::ResultInvalidPathFormat() },
{ "/aa\\bb\\..\\cc", "", false, 0, fs::ResultInvalidCharacter() },
{ "/aa\\bb\\..\\cc", "B", false, 0, ResultSuccess() },
{ "/aa\\bb\\cc", "", false, 0, fs::ResultInvalidCharacter() },
{ "/aa\\bb\\cc", "B", true, 9, ResultSuccess() },
{ "\\\\host\\share\\path\\aa\\bb\\cc", "W", false, 0, ResultSuccess() },
{ "\\\\host\\share\\path\\aa\\bb\\cc", "WB", false, 0, ResultSuccess() },
{ "/aa/bb\\../cc/..\\dd\\..\\ee/..", "", false, 0, fs::ResultInvalidCharacter() },
{ "/aa/bb\\../cc/..\\dd\\..\\ee/..", "B", false, 0, ResultSuccess() }
};
return DoIsNormalizedTests(Tests);
}
static_assert(TestIsNormalizedBackslash());
consteval bool TestIsNormalizedAllowAllCharacters() {
constexpr IsNormalizedTestData Tests[] = {
{ "/aa/b:b/cc", "", false, 0, fs::ResultInvalidCharacter() },
{ "/aa/b*b/cc", "", false, 0, fs::ResultInvalidCharacter() },
{ "/aa/b?b/cc", "", false, 0, fs::ResultInvalidCharacter() },
{ "/aa/b<b/cc", "", false, 0, fs::ResultInvalidCharacter() },
{ "/aa/b>b/cc", "", false, 0, fs::ResultInvalidCharacter() },
{ "/aa/b|b/cc", "", false, 0, fs::ResultInvalidCharacter() },
{ "/aa/b:b/cc", "C", true, 10, ResultSuccess() },
{ "/aa/b*b/cc", "C", true, 10, ResultSuccess() },
{ "/aa/b?b/cc", "C", true, 10, ResultSuccess() },
{ "/aa/b<b/cc", "C", true, 10, ResultSuccess() },
{ "/aa/b>b/cc", "C", true, 10, ResultSuccess() },
{ "/aa/b|b/cc", "C", true, 10, ResultSuccess() },
{ "/aa/b'b/cc", "", true, 10, ResultSuccess() },
{ "/aa/b\"b/cc", "", true, 10, ResultSuccess() },
{ "/aa/b(b/cc", "", true, 10, ResultSuccess() },
{ "/aa/b)b/cc", "", true, 10, ResultSuccess() },
{ "/aa/b'b/cc", "C", true, 10, ResultSuccess() },
{ "/aa/b\"b/cc", "C", true, 10, ResultSuccess() },
{ "/aa/b(b/cc", "C", true, 10, ResultSuccess() },
{ "/aa/b)b/cc", "C", true, 10, ResultSuccess() },
{ "mount:/aa/b<b/cc", "MC", true, 16, ResultSuccess() },
{ "mo>unt:/aa/bb/cc", "MC", false, 0, fs::ResultInvalidCharacter() }
};
return DoIsNormalizedTests(Tests);
}
static_assert(TestIsNormalizedAllowAllCharacters());
consteval bool TestIsNormalizedAll() {
constexpr IsNormalizedTestData Tests[] = {
{ "mount:./aa/bb", "WRM", true, 13, ResultSuccess() },
{ "mount:./aa/bb\\cc/dd", "WRM", false, 0, ResultSuccess() },
{ "mount:./aa/bb\\cc/dd", "WRMB", true, 19, ResultSuccess() },
{ "mount:./.c:/aa/bb", "RM", false, 0, fs::ResultInvalidCharacter() },
{ "mount:.c:/aa/bb", "WRM", false, 0, ResultSuccess() },
{ "mount:./cc:/aa/bb", "WRM", false, 0, fs::ResultInvalidCharacter() },
{ "mount:./\\\\host\\share/aa/bb", "MW", false, 0, fs::ResultInvalidPathFormat() },
{ "mount:./\\\\host\\share/aa/bb", "WRM", false, 0, ResultSuccess() },
{ "mount:.\\\\host\\share/aa/bb", "WRM", false, 0, ResultSuccess() },
{ "mount:..\\\\host\\share/aa/bb", "WRM", false, 0, ResultSuccess() },
{ ".\\\\host\\share/aa/bb", "WRM", false, 0, ResultSuccess() },
{ "..\\\\host\\share/aa/bb", "WRM", false, 0, ResultSuccess() },
{ "mount:\\\\host\\share/aa/bb", "MW", true, 24, ResultSuccess() },
{ "mount:\\aa\\bb", "BM", false, 0, fs::ResultInvalidPathFormat() },
{ "mount:/aa\\bb", "BM", true, 12, ResultSuccess() },
{ ".//aa/bb", "RW", false, 1, ResultSuccess() },
{ "./aa/bb", "R", true, 7, ResultSuccess() },
{ "./c:/aa/bb", "RW", false, 0, fs::ResultInvalidCharacter() },
{ "mount:./aa/b:b\\cc/dd", "WRMBC", true, 20, ResultSuccess() }
};
return DoIsNormalizedTests(Tests);
}
static_assert(TestIsNormalizedAll());
}
}

View File

@@ -0,0 +1,394 @@
/*
* Copyright (c) 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 {
namespace {
constexpr size_t DefaultPathBufferSize = fs::EntryNameLengthMax + 1;
//#define ENABLE_PRINT_FORMAT_TEST_DEBUGGING
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
template<u32 Expected, u32 Actual>
struct Print {
constexpr Print() {
if (std::is_constant_evaluated()) {
__builtin_unreachable();
}
}
};
template<u32 E = 0, u32 A = 0, size_t N = 0>
consteval void PrintResultMismatchImpl(u32 e, u32 a) {
if constexpr (N == 32) {
Print<E, A>{};
} else {
const bool is_e = (e & (1 << N)) != 0;
const bool is_a = (a & (1 << N)) != 0;
if (is_e) {
if (is_a) {
PrintResultMismatchImpl<E | (1 << N), A | (1 << N), N + 1>(e, a);
} else {
PrintResultMismatchImpl<E | (1 << N), A | (0 << N), N + 1>(e, a);
}
} else {
if (is_a) {
PrintResultMismatchImpl<E | (0 << N), A | (1 << N), N + 1>(e, a);
} else {
PrintResultMismatchImpl<E | (0 << N), A | (0 << N), N + 1>(e, a);
}
}
}
}
consteval void PrintResultMismatch(const Result &lhs, const Result &rhs) {
PrintResultMismatchImpl(lhs.GetDescription(), rhs.GetDescription());
}
template<size_t Index, char Expected, char Actual>
struct PrintMismatchChar {
constexpr PrintMismatchChar() {
if (std::is_constant_evaluated()) {
__builtin_unreachable();
}
}
};
template<size_t Ix, char Expected, size_t C = 0>
consteval void PrintCharacterMismatch(char c) {
if (c == static_cast<char>(C)) {
PrintMismatchChar<Ix, Expected, static_cast<char>(C)>{};
return;
}
if constexpr (C < std::numeric_limits<unsigned char>::max()) {
PrintCharacterMismatch<Ix, Expected, C + 1>(c);
}
}
template<size_t Ix, char C = 0>
consteval void PrintCharacterMismatch(char c, char c2) {
if (c == static_cast<char>(C)) {
PrintCharacterMismatch<Ix, static_cast<char>(C)>(c2);
return;
}
if constexpr (C < std::numeric_limits<unsigned char>::max()) {
PrintCharacterMismatch<Ix, C + 1>(c, c2);
}
}
template<size_t Ix = 0>
consteval void PrintCharacterMismatch(size_t ix, char c, char c2) {
if (Ix == ix) {
PrintCharacterMismatch<Ix>(c, c2);
return;
}
if constexpr (Ix <= DefaultPathBufferSize) {
PrintCharacterMismatch<Ix + 1>(ix, c, c2);
}
}
#endif
consteval bool TestNormalizeImpl(const char *path, size_t buffer_size, const char *normalized, bool windows_path, bool drive_relative, bool all_chars, size_t expected_length, Result expected_result) {
/* Allocate a buffer to normalize into. */
char *buffer = new char[buffer_size];
ON_SCOPE_EXIT { delete[] buffer; };
buffer[buffer_size - 1] = '\xcc';
/* Perform normalization. */
size_t actual_length;
const Result actual_result = PathNormalizer::Normalize(buffer, std::addressof(actual_length), path, buffer_size, windows_path, drive_relative, all_chars);
/* Check that the expected result matches the actual. */
if (actual_result.GetValue() != expected_result.GetValue()) {
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
PrintResultMismatch(expected_result.GetValue(), actual_result.GetValue());
#endif
return false;
}
/* Check that the expected string matches the actual. */
for (size_t i = 0; i < buffer_size; ++i) {
if (normalized[i] != StringTraits::NullTerminator || R_SUCCEEDED(expected_result)) {
if (buffer[i] != normalized[i]) {
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
PrintCharacterMismatch(i, normalized[i], buffer[i]);
#endif
return false;
}
}
if (normalized[i] == StringTraits::NullTerminator || buffer[i] == StringTraits::NullTerminator) {
break;
}
}
/* Check that the expected length matches the actual. */
if (R_SUCCEEDED(expected_result) || fs::ResultTooLongPath::Includes(expected_result)) {
if (expected_length != actual_length) {
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
PrintResultMismatchImpl(static_cast<u32>(expected_length), static_cast<u32>(actual_length));
#endif
return false;
}
}
return true;
}
struct NormalizeTestData {
const char *path;
bool windows;
bool rel;
bool allow_all;
const char *normalized;
size_t len;
Result result;
};
template<size_t N, size_t Ix = 0>
consteval bool DoNormalizeTests(const NormalizeTestData (&tests)[N]) {
if constexpr (Ix >= N) {
return true;
}
const auto &test = tests[Ix];
if (!TestNormalizeImpl(test.path, DefaultPathBufferSize, test.normalized, test.windows, test.rel, test.allow_all, test.len, test.result)) {
return false;
}
if constexpr (Ix < N) {
return DoNormalizeTests<N, Ix + 1>(tests);
} else {
AMS_ASSUME(false);
}
}
consteval bool TestNormalized() {
constexpr NormalizeTestData Tests[] = {
{ "/aa/bb/c/", false, true, false, "/aa/bb/c", 8, ResultSuccess() },
{ "aa/bb/c/", false, false, false, "", 0, fs::ResultInvalidPathFormat() },
{ "aa/bb/c/", false, true, false, "/aa/bb/c", 8, ResultSuccess() },
{ "mount:a/b", false, true, false, "/", 0, fs::ResultInvalidCharacter() },
{ "mo|unt:a/b", false, true, true, "/mo|unt:a/b", 11, ResultSuccess() },
{ "/aa/bb/../..", true, false, false, "/", 1, ResultSuccess() },
{ "/aa/bb/../../..", true, false, false, "/", 1, ResultSuccess() },
{ "/aa/bb/../../..", false, false, false, "/aa/bb/", 0, fs::ResultDirectoryUnobtainable() },
{ "aa/bb/../../..", true, true, false, "/", 1, ResultSuccess() },
{ "aa/bb/../../..", false, true, false, "/aa/bb/", 0, fs::ResultDirectoryUnobtainable() },
{ "mount:a/b", false, true, true, "/mount:a/b", 10, ResultSuccess() },
{ "/a|/bb/cc", false, false, true, "/a|/bb/cc", 9, ResultSuccess() },
{ "/>a/bb/cc", false, false, true, "/>a/bb/cc", 9, ResultSuccess() },
{ "/aa/.</cc", false, false, true, "/aa/.</cc", 9, ResultSuccess() },
{ "/aa/..</cc", false, false, true, "/aa/..</cc", 10, ResultSuccess() },
{ "", false, false, false, "", 0, fs::ResultInvalidPathFormat() },
{ "/", false, false, false, "/", 1, ResultSuccess() },
{ "/.", false, false, false, "/", 1, ResultSuccess() },
{ "/./", false, false, false, "/", 1, ResultSuccess() },
{ "/..", false, false, false, "/", 0, fs::ResultDirectoryUnobtainable() },
{ "//.", false, false, false, "/", 1, ResultSuccess() },
{ "/ ..", false, false, false, "/ ..", 4, ResultSuccess() },
{ "/.. /", false, false, false, "/.. ", 4, ResultSuccess() },
{ "/. /.", false, false, false, "/. ", 3, ResultSuccess() },
{ "/aa/bb/cc/dd/./.././../..", false, false, false, "/aa", 3, ResultSuccess() },
{ "/aa/bb/cc/dd/./.././../../..", false, false, false, "/", 1, ResultSuccess() },
{ "/./aa/./bb/./cc/./dd/.", false, false, false, "/aa/bb/cc/dd", 12, ResultSuccess() },
{ "/aa\\bb/cc", false, false, false, "/aa\\bb/cc", 9, ResultSuccess() },
{ "/aa\\bb/cc", false, false, false, "/aa\\bb/cc", 9, ResultSuccess() },
{ "/a|/bb/cc", false, false, false, "/", 0, fs::ResultInvalidCharacter() },
{ "/>a/bb/cc", false, false, false, "/", 0, fs::ResultInvalidCharacter() },
{ "/aa/.</cc", false, false, false, "/aa/", 0, fs::ResultInvalidCharacter() },
{ "/aa/..</cc", false, false, false, "/aa/", 0, fs::ResultInvalidCharacter() },
{ "\\\\aa/bb/cc", false, false, false, "", 0, fs::ResultInvalidPathFormat() },
{ "\\\\aa\\bb\\cc", false, false, false, "", 0, fs::ResultInvalidPathFormat() },
{ "/aa/bb/..\\cc", false, false, false, "/aa/cc", 6, ResultSuccess() },
{ "/aa/bb\\..\\cc", false, false, false, "/aa/cc", 6, ResultSuccess() },
{ "/aa/bb\\..", false, false, false, "/aa", 3, ResultSuccess() },
{ "/aa\\bb/../cc", false, false, false, "/cc", 3, ResultSuccess() }
};
return DoNormalizeTests(Tests);
}
static_assert(TestNormalized());
struct NormalizeTestDataSmallBuffer {
const char *path;
size_t buffer_size;
const char *normalized;
size_t len;
Result result;
};
template<size_t N, size_t Ix = 0>
consteval bool DoNormalizeTests(const NormalizeTestDataSmallBuffer (&tests)[N]) {
if constexpr (Ix >= N) {
return true;
}
const auto &test = tests[Ix];
if (!TestNormalizeImpl(test.path, test.buffer_size, test.normalized, false, false, false, test.len, test.result)) {
return false;
}
if constexpr (Ix < N) {
return DoNormalizeTests<N, Ix + 1>(tests);
} else {
AMS_ASSUME(false);
}
}
consteval bool TestNormalizedSmallBuffer() {
constexpr NormalizeTestDataSmallBuffer Tests[] = {
{ "/aa/bb/cc/", 7, "/aa/bb", 6, fs::ResultTooLongPath() },
{ "/aa/bb/cc/", 8, "/aa/bb/", 7, fs::ResultTooLongPath() },
{ "/aa/bb/cc/", 9, "/aa/bb/c", 8, fs::ResultTooLongPath() },
{ "/aa/bb/cc/", 10, "/aa/bb/cc", 9, ResultSuccess() },
{ "/aa/bb/cc", 9, "/aa/bb/c", 8, fs::ResultTooLongPath() },
{ "/aa/bb/cc", 10, "/aa/bb/cc", 9, ResultSuccess() },
{ "/./aa/./bb/./cc", 9, "/aa/bb/c", 8, fs::ResultTooLongPath() },
{ "/./aa/./bb/./cc", 10, "/aa/bb/cc", 9, ResultSuccess() },
{ "/aa/bb/cc/../../..", 9, "/aa/bb/c", 8, fs::ResultTooLongPath() },
{ "/aa/bb/cc/../../..", 10, "/aa/bb/cc", 9, fs::ResultTooLongPath() },
{ "/aa/bb/.", 7, "/aa/bb", 6, fs::ResultTooLongPath() },
{ "/aa/bb/./", 7, "/aa/bb", 6, fs::ResultTooLongPath() },
{ "/aa/bb/..", 8, "/aa", 3, ResultSuccess() },
{ "/aa/bb", 1, "", 0, fs::ResultTooLongPath() },
{ "/aa/bb", 2, "/", 1, fs::ResultTooLongPath() },
{ "/aa/bb", 3, "/a", 2, fs::ResultTooLongPath() },
{ "aa/bb", 1, "", 0, fs::ResultInvalidPathFormat() }
};
return DoNormalizeTests(Tests);
}
static_assert(TestNormalizedSmallBuffer());
consteval bool TestIsNormalizedImpl(const char *path, bool allow_all, bool expected_normalized, size_t expected_size, Result expected_result) {
/* Perform normalization checking. */
bool actual_normalized;
size_t actual_size = 0;
const Result actual_result = PathNormalizer::IsNormalized(std::addressof(actual_normalized), std::addressof(actual_size), path, allow_all);
/* Check that the expected result matches the actual. */
if (actual_result.GetValue() != expected_result.GetValue()) {
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
PrintResultMismatch(expected_result.GetValue(), actual_result.GetValue());
#endif
return false;
}
if (expected_size != actual_size) {
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
PrintResultMismatchImpl(static_cast<u32>(expected_size), static_cast<u32>(actual_size));
#endif
return false;
}
/* Check that the expected output matches the actual. */
if (R_SUCCEEDED(expected_result)) {
if (expected_normalized != actual_normalized) {
#if defined(ENABLE_PRINT_FORMAT_TEST_DEBUGGING)
PrintResultMismatchImpl(static_cast<u32>(expected_normalized), static_cast<u32>(actual_normalized));
#endif
return false;
}
}
return true;
}
struct IsNormalizedTestData {
const char *path;
bool allow_all;
bool normalized;
size_t len;
Result result;
};
template<size_t N, size_t Ix = 0>
consteval bool DoIsNormalizedTests(const IsNormalizedTestData (&tests)[N]) {
if constexpr (Ix >= N) {
return true;
}
const auto &test = tests[Ix];
if (!TestIsNormalizedImpl(test.path, test.allow_all, test.normalized, test.len, test.result)) {
return false;
}
if constexpr (Ix < N) {
return DoIsNormalizedTests<N, Ix + 1>(tests);
} else {
AMS_ASSUME(false);
}
}
consteval bool TestIsNormalized() {
constexpr IsNormalizedTestData Tests[] = {
{ "/aa/bb/c/", false, false, 9, ResultSuccess() },
{ "aa/bb/c/", false, false, 0, fs::ResultInvalidPathFormat() },
{ "aa/bb/c/", false, false, 0, fs::ResultInvalidPathFormat() },
{ "mount:a/b", false, false, 0, fs::ResultInvalidPathFormat() },
{ "mo|unt:a/b", true, false, 0, fs::ResultInvalidPathFormat() },
{ "/aa/bb/../..", false, false, 0, ResultSuccess() },
{ "/aa/bb/../../..", false, false, 0, ResultSuccess() },
{ "/aa/bb/../../..", false, false, 0, ResultSuccess() },
{ "aa/bb/../../..", false, false, 0, fs::ResultInvalidPathFormat() },
{ "aa/bb/../../..", false, false, 0, fs::ResultInvalidPathFormat() },
{ "mount:a/b", true, false, 0, fs::ResultInvalidPathFormat() },
{ "/a|/bb/cc", true, true, 9, ResultSuccess() },
{ "/>a/bb/cc", true, true, 9, ResultSuccess() },
{ "/aa/.</cc", true, true, 9, ResultSuccess() },
{ "/aa/..</cc", true, true, 10, ResultSuccess() },
{ "", false, false, 0, fs::ResultInvalidPathFormat() },
{ "/", false, true, 1, ResultSuccess() },
{ "/.", false, false, 2, ResultSuccess() },
{ "/./", false, false, 0, ResultSuccess() },
{ "/..", false, false, 3, ResultSuccess() },
{ "//.", false, false, 0, ResultSuccess() },
{ "/ ..", false, true, 4, ResultSuccess() },
{ "/.. /", false, false, 5, ResultSuccess() },
{ "/. /.", false, false, 5, ResultSuccess() },
{ "/aa/bb/cc/dd/./.././../..", false, false, 0, ResultSuccess() },
{ "/aa/bb/cc/dd/./.././../../..", false, false, 0, ResultSuccess() },
{ "/./aa/./bb/./cc/./dd/.", false, false, 0, ResultSuccess() },
{ "/aa\\bb/cc", false, true, 9, ResultSuccess() },
{ "/aa\\bb/cc", false, true, 9, ResultSuccess() },
{ "/a|/bb/cc", false, false, 0, fs::ResultInvalidCharacter() },
{ "/>a/bb/cc", false, false, 0, fs::ResultInvalidCharacter() },
{ "/aa/.</cc", false, false, 0, fs::ResultInvalidCharacter() },
{ "/aa/..</cc", false, false, 0, fs::ResultInvalidCharacter() },
{ "\\\\aa/bb/cc", false, false, 0, fs::ResultInvalidPathFormat() },
{ "\\\\aa\\bb\\cc", false, false, 0, fs::ResultInvalidPathFormat() },
{ "/aa/bb/..\\cc", false, true, 12, ResultSuccess() },
{ "/aa/bb\\..\\cc", false, true, 12, ResultSuccess() },
{ "/aa/bb\\..", false, true, 9, ResultSuccess() },
{ "/aa\\bb/../cc", false, false, 0, ResultSuccess() }
};
return DoIsNormalizedTests(Tests);
}
static_assert(TestIsNormalized());
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 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 {
namespace {
struct IsSubPathTestData {
const char *lhs;
const char *rhs;
bool is_sub_path;
};
template<size_t N, size_t Ix = 0>
consteval bool DoIsSubPathTests(const IsSubPathTestData (&tests)[N]) {
if constexpr (Ix >= N) {
return true;
}
const auto &test = tests[Ix];
if (::ams::fs::IsSubPath(test.lhs, test.rhs) != test.is_sub_path) {
return false;
}
if constexpr (Ix < N) {
return DoIsSubPathTests<N, Ix + 1>(tests);
} else {
AMS_ASSUME(false);
}
}
consteval bool TestIsSubPath() {
constexpr IsSubPathTestData Tests[] = {
{ "//a/b", "/a", false },
{ "/a", "//a/b", false },
{ "//a/b", "\\\\a", false },
{ "//a/b", "//a", true },
{ "/", "/a", true },
{ "/a", "/", true },
{ "/", "/", false },
{ "", "", false },
{ "/", "", true },
{ "/", "mount:/a", false },
{ "mount:/", "mount:/", false },
{ "mount:/a/b", "mount:/a/b", false },
{ "mount:/a/b", "mount:/a/b/c", true },
{ "/a/b", "/a/b/c", true },
{ "/a/b/c", "/a/b", true },
{ "/a/b", "/a/b", false },
{ "/a/b", "/a/b\\c", false }
};
return DoIsSubPathTests(Tests);
}
static_assert(TestIsSubPath());
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 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::fscreator {
Result LocalFileSystemCreator::Create(std::shared_ptr<fs::fsa::IFileSystem> *out, const fs::Path &path, bool case_sensitive, bool ensure_root, Result on_path_not_found) {
/* Check that we're configured for development. */
R_UNLESS(m_is_development, fs::ResultPermissionDeniedForCreateHostFileSystem());
/* Allocate a local filesystem. */
auto local_fs = fs::AllocateShared<fssystem::LocalFileSystem>();
R_UNLESS(local_fs != nullptr, fs::ResultAllocationFailureInLocalFileSystemCreatorA());
/* If we're supposed to make sure the root path exists, do so. */
if (ensure_root) {
/* Sanity check that the path will be possible to create. */
AMS_ASSERT(!path.IsEmpty());
/* Initialize the local fs with an empty path. */
fs::Path empty_path;
R_TRY(empty_path.InitializeAsEmpty());
R_TRY(local_fs->Initialize(empty_path, fssystem::PathCaseSensitiveMode_CaseInsensitive));
/* Ensure the directory exists. */
if (const Result ensure_result = fssystem::EnsureDirectory(local_fs.get(), path); R_FAILED(ensure_result)) {
if (R_SUCCEEDED(on_path_not_found)) {
R_THROW(ensure_result);
} else {
R_THROW(on_path_not_found);
}
}
}
/* Initialize the local filesystem. */
R_TRY_CATCH(local_fs->Initialize(path, case_sensitive ? fssystem::PathCaseSensitiveMode_CaseSensitive : fssystem::PathCaseSensitiveMode_CaseInsensitive)) {
R_CATCH(fs::ResultPathNotFound) {
if (R_SUCCEEDED(on_path_not_found)) {
R_THROW(R_CURRENT_RESULT);
} else {
R_THROW(on_path_not_found);
}
}
} R_END_TRY_CATCH;
/* Set the output fs. */
*out = std::move(local_fs);
R_SUCCEED();
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 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::fscreator {
Result SubDirectoryFileSystemCreator::Create(std::shared_ptr<fs::fsa::IFileSystem> *out, std::shared_ptr<fs::fsa::IFileSystem> base_fs, const fs::Path &path) {
/* Verify that we can the directory on the base filesystem. */
{
std::unique_ptr<fs::fsa::IDirectory> sub_dir;
R_TRY(base_fs->OpenDirectory(std::addressof(sub_dir), path, fs::OpenDirectoryMode_Directory));
}
/* Allocate a SubDirectoryFileSystem. */
auto sub_dir_fs = fs::AllocateShared<fssystem::SubDirectoryFileSystem>(std::move(base_fs));
R_UNLESS(sub_dir_fs != nullptr, fs::ResultAllocationFailureInSubDirectoryFileSystemCreatorA());
/* Initialize the new filesystem. */
R_TRY(sub_dir_fs->Initialize(path));
/* Return the new filesystem. */
*out = std::move(sub_dir_fs);
R_SUCCEED();
}
}

View File

@@ -42,8 +42,8 @@ namespace ams::fssrv {
constexpr size_t NumSessions = FileSystemProxyMaxSessions + ProgramRegistryMaxSessions + FileSystemProxyForLoaderMaxSessions;
constinit os::SemaphoreType g_semaphore_for_file_system_proxy_for_loader;
constinit os::SemaphoreType g_semaphore_for_program_registry;
constinit os::SemaphoreType g_semaphore_for_file_system_proxy_for_loader = {};
constinit os::SemaphoreType g_semaphore_for_program_registry = {};
class FileSystemProxyServerManager final : public ams::sf::hipc::ServerManager<PortIndex_Count, FileSystemProxyServerOptions, NumSessions> {
private:
@@ -89,11 +89,11 @@ namespace ams::fssrv {
}
};
constinit util::TypedStorage<FileSystemProxyServerManager> g_server_manager_storage;
constinit util::TypedStorage<FileSystemProxyServerManager> g_server_manager_storage = {};
constinit FileSystemProxyServerManager *g_server_manager = nullptr;
constinit os::BarrierType g_server_loop_barrier;
constinit os::EventType g_resume_wait_event;
constinit os::BarrierType g_server_loop_barrier = {};
constinit os::EventType g_resume_wait_event = {};
constinit bool g_is_suspended = false;
@@ -103,9 +103,9 @@ namespace ams::fssrv {
}
void InitializeForFileSystemProxy(fscreator::FileSystemCreatorInterfaces *fs_creator_interfaces, fssystem::IBufferManager *buffer_manager, bool is_development_function_enabled) {
void InitializeForFileSystemProxy(const FileSystemProxyConfiguration &config) {
/* TODO FS-REIMPL */
AMS_UNUSED(fs_creator_interfaces, buffer_manager, is_development_function_enabled);
AMS_UNUSED(config);
}
void InitializeFileSystemProxyServer(int threads) {

View File

@@ -14,6 +14,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include <stratosphere/fssrv/fssrv_interface_adapters.hpp>
#include "impl/fssrv_allocator_for_service_framework.hpp"
#include "impl/fssrv_program_info.hpp"
namespace ams::fssrv {
@@ -27,6 +29,420 @@ namespace ams::fssrv {
/* ... */
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
Result FileSystemProxyImpl::OpenFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u32 type) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::FileSystemProxyImpl::SetCurrentProcess(const ams::sf::ClientProcessId &client_pid) {
/* Set current process. */
m_process_id = client_pid.GetValue().value;
/* TODO: Allocate NcaFileSystemService. */
/* TODO: Allocate SaveDataFileSystemService. */
R_SUCCEED();
}
Result FileSystemProxyImpl::OpenDataFileSystemByCurrentProcess(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenFileSystemWithPatch(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, ncm::ProgramId program_id, u32 type) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenFileSystemWithId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u64 program_id, u32 type) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenDataFileSystemByProgramId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, ncm::ProgramId program_id) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenBisFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u32 id) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenBisStorage(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, u32 id) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::InvalidateBisCache() {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenHostFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path) {
/* Invoke the modern API from the legacy API. */
R_RETURN(this->OpenHostFileSystemWithOption(out, path, fs::MountHostOption::None._value));
}
Result FileSystemProxyImpl::OpenSdCardFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::FormatSdCardFileSystem() {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::DeleteSaveDataFileSystem(u64 save_data_id) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::CreateSaveDataFileSystem(const fs::SaveDataAttribute &attribute, const fs::SaveDataCreationInfo &creation_info, const fs::SaveDataMetaInfo &meta_info) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::CreateSaveDataFileSystemBySystemSaveDataId(const fs::SaveDataAttribute &attribute, const fs::SaveDataCreationInfo &creation_info) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::RegisterSaveDataFileSystemAtomicDeletion(const ams::sf::InBuffer &save_data_ids) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::DeleteSaveDataFileSystemBySaveDataSpaceId(u8 indexer_space_id, u64 save_data_id) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::FormatSdCardDryRun() {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::IsExFatSupported(ams::sf::Out<bool> out) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::DeleteSaveDataFileSystemBySaveDataAttribute(u8 space_id, const fs::SaveDataAttribute &attribute) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenGameCardStorage(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, u32 handle, u32 partition) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenGameCardFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u32 handle, u32 partition) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::ExtendSaveDataFileSystem(u8 space_id, u64 save_data_id, s64 available_size, s64 journal_size) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::DeleteCacheStorage(u16 index) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::GetCacheStorageSize(ams::sf::Out<s64> out_size, ams::sf::Out<s64> out_journal_size, u16 index) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::CreateSaveDataFileSystemWithHashSalt(const fs::SaveDataAttribute &attribute, const fs::SaveDataCreationInfo &creation_info, const fs::SaveDataMetaInfo &meta_info, const fs::HashSalt &salt) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenHostFileSystemWithOption(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, const fssrv::sf::FspPath &path, u32 _option) {
/* TODO: GetProgramInfo */
/* TODO: GetAccessibility. */
/* TODO: Check Accessibility CanRead/CanWrite */
/* Convert the path. */
fs::Path normalized_path;
#if defined(ATMOSPHERE_OS_WINDOWS)
R_TRY(normalized_path.Initialize(path.str));
#else
if (path.str[0] == '/' && path.str[1] == '/') {
R_TRY(normalized_path.Initialize(path.str));
} else {
R_TRY(normalized_path.InitializeWithReplaceUnc(path.str));
}
#endif
/* Normalize the path. */
fs::PathFlags path_flags;
path_flags.AllowWindowsPath();
path_flags.AllowRelativePath();
path_flags.AllowEmptyPath();
R_TRY(normalized_path.Normalize(path_flags));
/* Parse option. */
const fs::MountHostOption option{ _option };
/* TODO: FileSystemProxyCoreImpl::OpenHostFileSystem */
/* TODO: use creator interfaces */
std::shared_ptr<fs::fsa::IFileSystem> fs;
{
fssrv::fscreator::LocalFileSystemCreator local_fs_creator(true);
R_TRY(static_cast<fscreator::ILocalFileSystemCreator &>(local_fs_creator).Create(std::addressof(fs), normalized_path, option.HasPseudoCaseSensitiveFlag()));
}
/* Determine path flags for the result fs. */
fs::PathFlags host_path_flags;
if (path.str[0] == 0) {
host_path_flags.AllowWindowsPath();
}
/* Create an interface adapter. */
auto sf_fs = impl::FileSystemObjectFactory::CreateSharedEmplaced<fssrv::sf::IFileSystem, impl::FileSystemInterfaceAdapter>(std::move(fs), host_path_flags, false);
R_UNLESS(sf_fs != nullptr, fs::ResultAllocationFailureInFileSystemProxyImplA());
/* Set the output. */
*out = std::move(sf_fs);
R_SUCCEED();
}
Result FileSystemProxyImpl::OpenSaveDataFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u8 space_id, const fs::SaveDataAttribute &attribute) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenSaveDataFileSystemBySystemSaveDataId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u8 space_id, const fs::SaveDataAttribute &attribute) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenReadOnlySaveDataFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u8 space_id, const fs::SaveDataAttribute &attribute) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(const ams::sf::OutBuffer &buffer, u8 space_id, u64 save_data_id) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::ReadSaveDataFileSystemExtraData(const ams::sf::OutBuffer &buffer, u64 save_data_id) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::WriteSaveDataFileSystemExtraData(u64 save_data_id, u8 space_id, const ams::sf::InBuffer &buffer) {
AMS_ABORT("TODO");
}
/* ... */
Result FileSystemProxyImpl::OpenImageDirectoryFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u32 id) {
AMS_ABORT("TODO");
}
/* ... */
Result FileSystemProxyImpl::OpenContentStorageFileSystem(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u32 id) {
AMS_ABORT("TODO");
}
/* ... */
Result FileSystemProxyImpl::OpenDataStorageByCurrentProcess(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenDataStorageByProgramId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, ncm::ProgramId program_id) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenDataStorageByDataId(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, ncm::DataId data_id, u8 storage_id) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenPatchDataStorageByCurrentProcess(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenDataFileSystemWithProgramIndex(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out, u8 index) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenDataStorageWithProgramIndex(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, u8 index) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenDataStorageByPath(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IStorage>> out, const fssrv::sf::FspPath &path, u32 type) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenDeviceOperator(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IDeviceOperator>> out) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenSdCardDetectionEventNotifier(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IEventNotifier>> out) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenGameCardDetectionEventNotifier(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IEventNotifier>> out) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenSystemDataUpdateEventNotifier(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IEventNotifier>> out) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::NotifySystemDataUpdateEvent() {
AMS_ABORT("TODO");
}
/* ... */
Result FileSystemProxyImpl::SetCurrentPosixTime(s64 posix_time) {
AMS_ABORT("TODO");
}
/* ... */
Result FileSystemProxyImpl::GetRightsId(ams::sf::Out<fs::RightsId> out, ncm::ProgramId program_id, ncm::StorageId storage_id) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::RegisterExternalKey(const fs::RightsId &rights_id, const spl::AccessKey &access_key) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::UnregisterAllExternalKey() {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::GetRightsIdByPath(ams::sf::Out<fs::RightsId> out, const fssrv::sf::FspPath &path) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::GetRightsIdAndKeyGenerationByPath(ams::sf::Out<fs::RightsId> out, ams::sf::Out<u8> out_key_generation, const fssrv::sf::FspPath &path) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::SetCurrentPosixTimeWithTimeDifference(s64 posix_time, s32 time_difference) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::GetFreeSpaceSizeForSaveData(ams::sf::Out<s64> out, u8 space_id) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::VerifySaveDataFileSystemBySaveDataSpaceId() {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::CorruptSaveDataFileSystemBySaveDataSpaceId() {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::QuerySaveDataInternalStorageTotalSize() {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::GetSaveDataCommitId() {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::UnregisterExternalKey(const fs::RightsId &rights_id) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::SetSdCardEncryptionSeed(const fs::EncryptionSeed &seed) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::SetSdCardAccessibility(bool accessible) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::IsSdCardAccessible(ams::sf::Out<bool> out) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::IsSignedSystemPartitionOnSdCardValid(ams::sf::Out<bool> out) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenAccessFailureDetectionEventNotifier() {
AMS_ABORT("TODO");
}
/* ... */
Result FileSystemProxyImpl::RegisterProgramIndexMapInfo(const ams::sf::InBuffer &buffer, s32 count) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::SetBisRootForHost(u32 id, const fssrv::sf::FspPath &path) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::SetSaveDataSize(s64 size, s64 journal_size) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::SetSaveDataRootPath(const fssrv::sf::FspPath &path) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::DisableAutoSaveDataCreation() {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::SetGlobalAccessLogMode(u32 mode) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::GetGlobalAccessLogMode(ams::sf::Out<u32> out) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OutputAccessLogToSdCard(const ams::sf::InBuffer &buf) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::RegisterUpdatePartition() {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OpenRegisteredUpdatePartition(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out) {
AMS_ABORT("TODO");
}
/* ... */
Result FileSystemProxyImpl::GetProgramIndexForAccessLog(ams::sf::Out<u32> out_idx, ams::sf::Out<u32> out_count) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::GetFsStackUsage(ams::sf::Out<u32> out, u32 type) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::UnsetSaveDataRootPath() {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OutputMultiProgramTagAccessLog() {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::FlushAccessLogOnSdCard() {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OutputApplicationInfoAccessLog() {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::RegisterDebugConfiguration(u32 key, s64 value) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::UnregisterDebugConfiguration(u32 key) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::OverrideSaveDataTransferTokenSignVerificationKey(const ams::sf::InBuffer &buf) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::CorruptSaveDataFileSystemByOffset(u8 space_id, u64 save_data_id, s64 offset) {
AMS_ABORT("TODO");
}
/* ... */
#pragma GCC diagnostic pop
Result FileSystemProxyImpl::OpenCodeFileSystemDeprecated(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out_fs, const fssrv::sf::Path &path, ncm::ProgramId program_id) {
AMS_ABORT("TODO");
AMS_UNUSED(out_fs, path, program_id);
@@ -42,12 +458,4 @@ namespace ams::fssrv {
AMS_UNUSED(out, process_id);
}
Result FileSystemProxyImpl::SetCurrentProcess(const ams::sf::ClientProcessId &client_pid) {
/* Set current process. */
m_process_id = client_pid.GetValue().value;
/* TODO: Allocate NcaFileSystemService. */
AMS_ABORT("TODO");
}
}

View File

@@ -16,77 +16,83 @@
#include <stratosphere.hpp>
#include <stratosphere/fssrv/fssrv_interface_adapters.hpp>
#include "impl/fssrv_allocator_for_service_framework.hpp"
#include "fssrv_retry_utility.hpp"
namespace ams::fssrv::impl {
FileInterfaceAdapter::FileInterfaceAdapter(std::unique_ptr<fs::fsa::IFile> &&file, FileSystemInterfaceAdapter *parent, util::unique_lock<fssystem::SemaphoreAdapter> &&sema)
: m_parent_filesystem(parent, true), m_base_file(std::move(file)), m_open_count_semaphore(std::move(sema))
namespace {
constexpr const char *RootDirectory = "/";
}
FileInterfaceAdapter::FileInterfaceAdapter(std::unique_ptr<fs::fsa::IFile> &&file, FileSystemInterfaceAdapter *parent, bool allow_all)
: m_parent_filesystem(parent, true), m_base_file(std::move(file)), m_allow_all_operations(allow_all)
{
/* ... */
}
FileInterfaceAdapter::~FileInterfaceAdapter() {
/* ... */
}
void FileInterfaceAdapter::InvalidateCache() {
AMS_ABORT_UNLESS(m_parent_filesystem->IsDeepRetryEnabled());
std::scoped_lock<os::ReaderWriterLock> scoped_write_lock(m_parent_filesystem->GetReaderWriterLockForCacheInvalidation());
m_base_file->OperateRange(nullptr, 0, fs::OperationId::Invalidate, 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 fs::ResultDataCorrupted, we may want to eventually. */
/* TODO: Deep retry */
R_UNLESS(offset >= 0, fs::ResultInvalidOffset());
R_UNLESS(size >= 0, fs::ResultInvalidSize());
/* Check pre-conditions. */
R_UNLESS(0 <= offset, fs::ResultInvalidOffset());
R_UNLESS(0 <= size, fs::ResultInvalidSize());
R_UNLESS(size <= static_cast<s64>(buffer.GetSize()), fs::ResultInvalidSize());
/* Read the data, retrying on corruption. */
size_t read_size = 0;
R_TRY(m_base_file->Read(std::addressof(read_size), offset, buffer.GetPointer(), static_cast<size_t>(size), option));
R_TRY(RetryFinitelyForDataCorrupted([&] () ALWAYS_INLINE_LAMBDA {
R_RETURN(m_base_file->Read(std::addressof(read_size), offset, buffer.GetPointer(), static_cast<size_t>(size), option));
}));
out.SetValue(read_size);
return ResultSuccess();
/* Set the output size. */
*out = read_size;
R_SUCCEED();
}
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());
/* Check pre-conditions. */
R_UNLESS(0 <= offset, fs::ResultInvalidOffset());
R_UNLESS(0 <= size, fs::ResultInvalidSize());
R_UNLESS(size <= static_cast<s64>(buffer.GetSize()), fs::ResultInvalidSize());
auto read_lock = m_parent_filesystem->AcquireCacheInvalidationReadLock();
return m_base_file->Write(offset, buffer.GetPointer(), size, option);
/* Temporarily increase our thread's priority. */
fssystem::ScopedThreadPriorityChangerByAccessPriority cp(fssystem::ScopedThreadPriorityChangerByAccessPriority::AccessMode::Write);
R_RETURN(m_base_file->Write(offset, buffer.GetPointer(), size, option));
}
Result FileInterfaceAdapter::Flush() {
auto read_lock = m_parent_filesystem->AcquireCacheInvalidationReadLock();
return m_base_file->Flush();
R_RETURN(m_base_file->Flush());
}
Result FileInterfaceAdapter::SetSize(s64 size) {
R_UNLESS(size >= 0, fs::ResultInvalidSize());
auto read_lock = m_parent_filesystem->AcquireCacheInvalidationReadLock();
return m_base_file->SetSize(size);
R_RETURN(m_base_file->SetSize(size));
}
Result FileInterfaceAdapter::GetSize(ams::sf::Out<s64> out) {
auto read_lock = m_parent_filesystem->AcquireCacheInvalidationReadLock();
return m_base_file->GetSize(out.GetPointer());
/* Get the size, retrying on corruption. */
R_RETURN(RetryFinitelyForDataCorrupted([&] () ALWAYS_INLINE_LAMBDA {
R_RETURN(m_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());
/* Clear the range info. */
out->Clear();
if (op_id == static_cast<s32>(fs::OperationId::QueryRange)) {
auto read_lock = m_parent_filesystem->AcquireCacheInvalidationReadLock();
if (op_id == static_cast<s32>(fs::OperationId::QueryRange)) {
fs::FileQueryRangeInfo info;
R_TRY(m_base_file->OperateRange(std::addressof(info), sizeof(info), fs::OperationId::QueryRange, offset, size, nullptr, 0));
out->Merge(info);
} else if (op_id == static_cast<s32>(fs::OperationId::Invalidate)) {
R_TRY(m_base_file->OperateRange(nullptr, 0, fs::OperationId::Invalidate, offset, size, nullptr, 0));
}
return ResultSuccess();
R_SUCCEED();
}
Result FileInterfaceAdapter::OperateRangeWithBuffer(const ams::sf::OutNonSecureBuffer &out_buf, const ams::sf::InNonSecureBuffer &in_buf, s32 op_id, s64 offset, s64 size) {
@@ -99,265 +105,275 @@ namespace ams::fssrv::impl {
/* Lazy load/unprepared operations are always allowed to be performed with buffer. */
break;
default:
/* TODO: Nintendo requires that a class member here be true here, but this class member seems to always be false. */
/* If this changes (or reverse engineering is wrong), this should be updated. */
return fs::ResultPermissionDenied();
R_UNLESS(m_allow_all_operations, fs::ResultPermissionDenied());
}
/* Perform the operation. */
R_TRY(m_base_file->OperateRange(out_buf.GetPointer(), out_buf.GetSize(), static_cast<fs::OperationId>(op_id), offset, size, in_buf.GetPointer(), in_buf.GetSize()));
return ResultSuccess();
R_RETURN(m_base_file->OperateRange(out_buf.GetPointer(), out_buf.GetSize(), static_cast<fs::OperationId>(op_id), offset, size, in_buf.GetPointer(), in_buf.GetSize()));
}
DirectoryInterfaceAdapter::DirectoryInterfaceAdapter(std::unique_ptr<fs::fsa::IDirectory> &&dir, FileSystemInterfaceAdapter *parent, util::unique_lock<fssystem::SemaphoreAdapter> &&sema)
: m_parent_filesystem(parent, true), m_base_dir(std::move(dir)), m_open_count_semaphore(std::move(sema))
DirectoryInterfaceAdapter::DirectoryInterfaceAdapter(std::unique_ptr<fs::fsa::IDirectory> &&dir, FileSystemInterfaceAdapter *parent, bool allow_all)
: m_parent_filesystem(parent, true), m_base_dir(std::move(dir)), m_allow_all_operations(allow_all)
{
/* ... */
}
DirectoryInterfaceAdapter::~DirectoryInterfaceAdapter() {
/* ... */
}
Result DirectoryInterfaceAdapter::Read(ams::sf::Out<s64> out, const ams::sf::OutBuffer &out_entries) {
auto read_lock = m_parent_filesystem->AcquireCacheInvalidationReadLock();
/* Get the maximum number of entries we can read into the buffer. */
const s64 max_num_entries = out_entries.GetSize() / sizeof(fs::DirectoryEntry);
R_UNLESS(max_num_entries >= 0, fs::ResultInvalidSize());
/* TODO: N retries on fs::ResultDataCorrupted, we may want to eventually. */
return m_base_dir->Read(out.GetPointer(), reinterpret_cast<fs::DirectoryEntry *>(out_entries.GetPointer()), max_num_entries);
/* Get the size, retrying on corruption. */
s64 num_read = 0;
R_TRY(RetryFinitelyForDataCorrupted([&] () ALWAYS_INLINE_LAMBDA {
R_RETURN(m_base_dir->Read(std::addressof(num_read), reinterpret_cast<fs::DirectoryEntry *>(out_entries.GetPointer()), max_num_entries));
}));
/* Set the output. */
*out = num_read;
R_SUCCEED();
}
Result DirectoryInterfaceAdapter::GetEntryCount(ams::sf::Out<s64> out) {
auto read_lock = m_parent_filesystem->AcquireCacheInvalidationReadLock();
return m_base_dir->GetEntryCount(out.GetPointer());
R_RETURN(m_base_dir->GetEntryCount(out.GetPointer()));
}
FileSystemInterfaceAdapter::FileSystemInterfaceAdapter(std::shared_ptr<fs::fsa::IFileSystem> &&fs, bool open_limited)
: m_base_fs(std::move(fs)), m_open_count_limited(open_limited), m_deep_retry_enabled(false)
{
/* ... */
}
FileSystemInterfaceAdapter::~FileSystemInterfaceAdapter() {
/* ... */
}
bool FileSystemInterfaceAdapter::IsDeepRetryEnabled() const {
return m_deep_retry_enabled;
}
bool FileSystemInterfaceAdapter::IsAccessFailureDetectionObserved() const {
/* TODO: This calls into fssrv::FileSystemProxyImpl, which we don't have yet. */
AMS_ABORT_UNLESS(false);
}
util::optional<std::shared_lock<os::ReaderWriterLock>> FileSystemInterfaceAdapter::AcquireCacheInvalidationReadLock() {
util::optional<std::shared_lock<os::ReaderWriterLock>> lock;
if (m_deep_retry_enabled) {
lock.emplace(m_invalidation_lock);
Result FileSystemInterfaceAdapter::SetUpPath(fs::Path *out, const fssrv::sf::Path &sf_path) {
/* Initialize the fs path. */
if (m_path_flags.IsWindowsPathAllowed()) {
R_TRY(out->InitializeWithReplaceUnc(sf_path.str));
} else {
R_TRY(out->Initialize(sf_path.str));
}
return lock;
}
os::ReaderWriterLock &FileSystemInterfaceAdapter::GetReaderWriterLockForCacheInvalidation() {
return m_invalidation_lock;
/* Ensure the path is normalized. */
R_RETURN(out->Normalize(m_path_flags));
}
Result FileSystemInterfaceAdapter::CreateFile(const fssrv::sf::Path &path, s64 size, s32 option) {
auto read_lock = this->AcquireCacheInvalidationReadLock();
/* Check pre-conditions. */
R_UNLESS(size >= 0, fs::ResultInvalidSize());
PathNormalizer normalizer(path.str);
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
/* Normalize the input path. */
fs::Path fs_path;
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
return m_base_fs->CreateFile(normalizer.GetPath(), size, option);
R_RETURN(m_base_fs->CreateFile(fs_path, size, option));
}
Result FileSystemInterfaceAdapter::DeleteFile(const fssrv::sf::Path &path) {
auto read_lock = this->AcquireCacheInvalidationReadLock();
/* Normalize the input path. */
fs::Path fs_path;
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
PathNormalizer normalizer(path.str);
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
return m_base_fs->DeleteFile(normalizer.GetPath());
R_RETURN(m_base_fs->DeleteFile(fs_path));
}
Result FileSystemInterfaceAdapter::CreateDirectory(const fssrv::sf::Path &path) {
auto read_lock = this->AcquireCacheInvalidationReadLock();
/* Normalize the input path. */
fs::Path fs_path;
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
PathNormalizer normalizer(path.str);
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
/* Sanity check that the directory isn't the root. */
R_UNLESS(fs_path != RootDirectory, fs::ResultPathAlreadyExists());
R_UNLESS(strncmp(normalizer.GetPath(), "/", 2) != 0, fs::ResultPathAlreadyExists());
return m_base_fs->CreateDirectory(normalizer.GetPath());
R_RETURN(m_base_fs->CreateDirectory(fs_path));
}
Result FileSystemInterfaceAdapter::DeleteDirectory(const fssrv::sf::Path &path) {
auto read_lock = this->AcquireCacheInvalidationReadLock();
/* Normalize the input path. */
fs::Path fs_path;
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
PathNormalizer normalizer(path.str);
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
/* Sanity check that the directory isn't the root. */
R_UNLESS(fs_path != RootDirectory, fs::ResultDirectoryNotDeletable());
R_UNLESS(strncmp(normalizer.GetPath(), "/", 2) != 0, fs::ResultDirectoryNotDeletable());
return m_base_fs->DeleteDirectory(normalizer.GetPath());
R_RETURN(m_base_fs->DeleteDirectory(fs_path));
}
Result FileSystemInterfaceAdapter::DeleteDirectoryRecursively(const fssrv::sf::Path &path) {
auto read_lock = this->AcquireCacheInvalidationReadLock();
/* Normalize the input path. */
fs::Path fs_path;
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
PathNormalizer normalizer(path.str);
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
/* Sanity check that the directory isn't the root. */
R_UNLESS(fs_path != RootDirectory, fs::ResultDirectoryNotDeletable());
R_UNLESS(strncmp(normalizer.GetPath(), "/", 2) != 0, fs::ResultDirectoryNotDeletable());
return m_base_fs->DeleteDirectoryRecursively(normalizer.GetPath());
R_RETURN(m_base_fs->DeleteDirectoryRecursively(fs_path));
}
Result FileSystemInterfaceAdapter::RenameFile(const fssrv::sf::Path &old_path, const fssrv::sf::Path &new_path) {
auto read_lock = this->AcquireCacheInvalidationReadLock();
/* Normalize the input paths. */
fs::Path fs_old_path;
fs::Path fs_new_path;
R_TRY(this->SetUpPath(std::addressof(fs_old_path), old_path));
R_TRY(this->SetUpPath(std::addressof(fs_new_path), new_path));
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 m_base_fs->RenameFile(old_normalizer.GetPath(), new_normalizer.GetPath());
R_RETURN(m_base_fs->RenameFile(fs_old_path, fs_new_path));
}
Result FileSystemInterfaceAdapter::RenameDirectory(const fssrv::sf::Path &old_path, const fssrv::sf::Path &new_path) {
auto read_lock = this->AcquireCacheInvalidationReadLock();
/* Normalize the input paths. */
fs::Path fs_old_path;
fs::Path fs_new_path;
R_TRY(this->SetUpPath(std::addressof(fs_old_path), old_path));
R_TRY(this->SetUpPath(std::addressof(fs_new_path), new_path));
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());
R_UNLESS(!fs::IsSubPath(fs_old_path.GetString(), fs_new_path.GetString()), fs::ResultDirectoryNotRenamable());
const bool is_subpath = fs::IsSubPath(old_normalizer.GetPath(), new_normalizer.GetPath());
R_UNLESS(!is_subpath, fs::ResultDirectoryNotRenamable());
return m_base_fs->RenameFile(old_normalizer.GetPath(), new_normalizer.GetPath());
R_RETURN(m_base_fs->RenameDirectory(fs_old_path, fs_new_path));
}
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());
/* Normalize the input path. */
fs::Path fs_path;
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
static_assert(sizeof(*out.GetPointer()) == sizeof(fs::DirectoryEntryType));
return m_base_fs->GetEntryType(reinterpret_cast<fs::DirectoryEntryType *>(out.GetPointer()), normalizer.GetPath());
R_RETURN(m_base_fs->GetEntryType(reinterpret_cast<fs::DirectoryEntryType *>(out.GetPointer()), fs_path));
}
Result FileSystemInterfaceAdapter::OpenFile(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFile>> out, const fssrv::sf::Path &path, u32 mode) {
auto read_lock = this->AcquireCacheInvalidationReadLock();
/* Normalize the input path. */
fs::Path fs_path;
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
util::unique_lock<fssystem::SemaphoreAdapter> open_count_semaphore;
if (m_open_count_limited) {
/* TODO: This calls into fssrv::FileSystemProxyImpl, which we don't have yet. */
AMS_ABORT_UNLESS(false);
/* Open the file, retrying on corruption. */
std::unique_ptr<fs::fsa::IFile> file;
R_TRY(RetryFinitelyForDataCorrupted([&] () ALWAYS_INLINE_LAMBDA {
R_RETURN(m_base_fs->OpenFile(std::addressof(file), fs_path, static_cast<fs::OpenMode>(mode)));
}));
/* If we're a mitm interface, we should preserve the resulting target object id. */
if (m_is_mitm_interface) {
/* TODO: This is a hack to get the mitm API to work. Better solution? */
const auto target_object_id = file->GetDomainObjectId();
ams::sf::SharedPointer<fssrv::sf::IFile> file_intf = FileSystemObjectFactory::CreateSharedEmplaced<fssrv::sf::IFile, FileInterfaceAdapter>(std::move(file), this, m_allow_all_operations);
R_UNLESS(file_intf != nullptr, fs::ResultAllocationFailureInFileSystemInterfaceAdapter());
out.SetValue(std::move(file_intf), target_object_id);
} else {
ams::sf::SharedPointer<fssrv::sf::IFile> file_intf = FileSystemObjectFactory::CreateSharedEmplaced<fssrv::sf::IFile, FileInterfaceAdapter>(std::move(file), this, m_allow_all_operations);
R_UNLESS(file_intf != nullptr, fs::ResultAllocationFailureInFileSystemInterfaceAdapter());
out.SetValue(std::move(file_intf));
}
PathNormalizer normalizer(path.str);
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
/* TODO: N retries on fs::ResultDataCorrupted, we may want to eventually. */
std::unique_ptr<fs::fsa::IFile> file;
R_TRY(m_base_fs->OpenFile(std::addressof(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. */
ams::sf::SharedPointer<fssrv::sf::IFile> file_intf = FileSystemObjectFactory::CreateSharedEmplaced<fssrv::sf::IFile, FileInterfaceAdapter>(std::move(file), this, std::move(open_count_semaphore));
R_UNLESS(file_intf != nullptr, fs::ResultAllocationFailureInFileSystemInterfaceAdapter());
out.SetValue(std::move(file_intf), target_object_id);
return ResultSuccess();
R_SUCCEED();
}
Result FileSystemInterfaceAdapter::OpenDirectory(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IDirectory>> out, const fssrv::sf::Path &path, u32 mode) {
auto read_lock = this->AcquireCacheInvalidationReadLock();
/* Normalize the input path. */
fs::Path fs_path;
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
util::unique_lock<fssystem::SemaphoreAdapter> open_count_semaphore;
if (m_open_count_limited) {
/* TODO: This calls into fssrv::FileSystemProxyImpl, which we don't have yet. */
AMS_ABORT_UNLESS(false);
/* Open the directory, retrying on corruption. */
std::unique_ptr<fs::fsa::IDirectory> dir;
R_TRY(RetryFinitelyForDataCorrupted([&] () ALWAYS_INLINE_LAMBDA {
R_RETURN(m_base_fs->OpenDirectory(std::addressof(dir), fs_path, static_cast<fs::OpenDirectoryMode>(mode)));
}));
/* If we're a mitm interface, we should preserve the resulting target object id. */
if (m_is_mitm_interface) {
/* TODO: This is a hack to get the mitm API to work. Better solution? */
const auto target_object_id = dir->GetDomainObjectId();
ams::sf::SharedPointer<fssrv::sf::IDirectory> dir_intf = FileSystemObjectFactory::CreateSharedEmplaced<fssrv::sf::IDirectory, DirectoryInterfaceAdapter>(std::move(dir), this, m_allow_all_operations);
R_UNLESS(dir_intf != nullptr, fs::ResultAllocationFailureInFileSystemInterfaceAdapter());
out.SetValue(std::move(dir_intf), target_object_id);
} else {
ams::sf::SharedPointer<fssrv::sf::IDirectory> dir_intf = FileSystemObjectFactory::CreateSharedEmplaced<fssrv::sf::IDirectory, DirectoryInterfaceAdapter>(std::move(dir), this, m_allow_all_operations);
R_UNLESS(dir_intf != nullptr, fs::ResultAllocationFailureInFileSystemInterfaceAdapter());
out.SetValue(std::move(dir_intf));
}
PathNormalizer normalizer(path.str);
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
/* TODO: N retries on fs::ResultDataCorrupted, we may want to eventually. */
std::unique_ptr<fs::fsa::IDirectory> dir;
R_TRY(m_base_fs->OpenDirectory(std::addressof(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();
ams::sf::SharedPointer<fssrv::sf::IDirectory> dir_intf = FileSystemObjectFactory::CreateSharedEmplaced<fssrv::sf::IDirectory, DirectoryInterfaceAdapter>(std::move(dir), this, std::move(open_count_semaphore));
R_UNLESS(dir_intf != nullptr, fs::ResultAllocationFailureInFileSystemInterfaceAdapter());
out.SetValue(std::move(dir_intf), target_object_id);
return ResultSuccess();
R_SUCCEED();
}
Result FileSystemInterfaceAdapter::Commit() {
auto read_lock = this->AcquireCacheInvalidationReadLock();
return m_base_fs->Commit();
R_RETURN(m_base_fs->Commit());
}
Result FileSystemInterfaceAdapter::GetFreeSpaceSize(ams::sf::Out<s64> out, const fssrv::sf::Path &path) {
auto read_lock = this->AcquireCacheInvalidationReadLock();
/* Normalize the input path. */
fs::Path fs_path;
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
PathNormalizer normalizer(path.str);
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
return m_base_fs->GetFreeSpaceSize(out.GetPointer(), normalizer.GetPath());
R_RETURN(m_base_fs->GetFreeSpaceSize(out.GetPointer(), fs_path));
}
Result FileSystemInterfaceAdapter::GetTotalSpaceSize(ams::sf::Out<s64> out, const fssrv::sf::Path &path) {
auto read_lock = this->AcquireCacheInvalidationReadLock();
/* Normalize the input path. */
fs::Path fs_path;
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
PathNormalizer normalizer(path.str);
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
return m_base_fs->GetTotalSpaceSize(out.GetPointer(), normalizer.GetPath());
R_RETURN(m_base_fs->GetTotalSpaceSize(out.GetPointer(), fs_path));
}
Result FileSystemInterfaceAdapter::CleanDirectoryRecursively(const fssrv::sf::Path &path) {
auto read_lock = this->AcquireCacheInvalidationReadLock();
/* Normalize the input path. */
fs::Path fs_path;
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
PathNormalizer normalizer(path.str);
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
return m_base_fs->CleanDirectoryRecursively(normalizer.GetPath());
R_RETURN(m_base_fs->CleanDirectoryRecursively(fs_path));
}
Result FileSystemInterfaceAdapter::GetFileTimeStampRaw(ams::sf::Out<fs::FileTimeStampRaw> out, const fssrv::sf::Path &path) {
auto read_lock = this->AcquireCacheInvalidationReadLock();
/* Normalize the input path. */
fs::Path fs_path;
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
PathNormalizer normalizer(path.str);
R_UNLESS(normalizer.GetPath() != nullptr, normalizer.GetResult());
return m_base_fs->GetFileTimeStampRaw(out.GetPointer(), normalizer.GetPath());
R_RETURN(m_base_fs->GetFileTimeStampRaw(out.GetPointer(), fs_path));
}
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();
/* Check that we have permission to perform the operation. */
switch (static_cast<fs::fsa::QueryId>(query_id)) {
using enum fs::fsa::QueryId;
case SetConcatenationFileAttribute:
case IsSignedSystemPartitionOnSdCardValid:
case QueryUnpreparedFileInformation:
/* Only certain operations are unconditionally allowable. */
break;
default:
R_UNLESS(m_allow_all_operations, fs::ResultPermissionDenied());
}
/* TODO: Nintendo does not normalize the path. Should we? */
/* Normalize the input path. */
fs::Path fs_path;
R_TRY(this->SetUpPath(std::addressof(fs_path), path));
char *dst = reinterpret_cast< char *>(out_buf.GetPointer());
char *dst = reinterpret_cast<char *>(out_buf.GetPointer());
const char *src = reinterpret_cast<const char *>(in_buf.GetPointer());
return m_base_fs->QueryEntry(dst, out_buf.GetSize(), src, in_buf.GetSize(), static_cast<fs::fsa::QueryId>(query_id), path.str);
R_RETURN(m_base_fs->QueryEntry(dst, out_buf.GetSize(), src, in_buf.GetSize(), static_cast<fs::fsa::QueryId>(query_id), fs_path));
}
#if defined(ATMOSPHERE_OS_HORIZON)
Result RemoteFileSystem::OpenFile(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFile>> out, const fssrv::sf::Path &path, u32 mode) {
FsFile f;
R_TRY(fsFsOpenFile(std::addressof(m_base_fs), path.str, mode, std::addressof(f)));
auto intf = FileSystemObjectFactory::CreateSharedEmplaced<fssrv::sf::IFile, RemoteFile>(f);
R_UNLESS(intf != nullptr, fs::ResultAllocationFailureInFileSystemInterfaceAdapter());
out.SetValue(std::move(intf));
R_SUCCEED();
}
Result RemoteFileSystem::OpenDirectory(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IDirectory>> out, const fssrv::sf::Path &path, u32 mode) {
FsDir d;
R_TRY(fsFsOpenDirectory(std::addressof(m_base_fs), path.str, mode, std::addressof(d)));
auto intf = FileSystemObjectFactory::CreateSharedEmplaced<fssrv::sf::IDirectory, RemoteDirectory>(d);
R_UNLESS(intf != nullptr, fs::ResultAllocationFailureInFileSystemInterfaceAdapter());
out.SetValue(std::move(intf));
R_SUCCEED();
}
#endif
}

View File

@@ -1,62 +0,0 @@
/*
* Copyright (c) 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) {
/* Check pre-conditions. */
AMS_ASSERT(out_path != nullptr);
AMS_ASSERT(out_buf != nullptr);
/* Clear output. */
*out_path = nullptr;
*out_buf = Buffer();
/* Check if we're normalized. */
bool normalized = false;
R_TRY(fs::PathNormalizer::IsNormalized(std::addressof(normalized), path, preserve_unc, has_mount_name));
if (normalized) {
/* If we're already normalized, no allocation is needed. */
*out_path = path;
} else {
/* Allocate a new buffer. */
auto buffer = fs::impl::MakeUnique<char[]>(fs::EntryNameLengthMax + 1);
R_UNLESS(buffer != nullptr, fs::ResultAllocationFailureInPathNormalizer());
/* Generate normalized path. */
size_t normalized_len = 0;
R_TRY(fs::PathNormalizer::Normalize(buffer.get(), std::addressof(normalized_len), path, fs::EntryNameLengthMax + 1, preserve_unc, has_mount_name));
/* Preserve the tail separator, if we should. */
if (preserve_tail_sep) {
if (fs::PathNormalizer::IsSeparator(path[strnlen(path, fs::EntryNameLengthMax) - 1]) && !fs::PathNormalizer::IsSeparator(buffer[normalized_len - 1])) {
AMS_ASSERT(normalized_len < fs::EntryNameLengthMax);
buffer[normalized_len] = fs::StringTraits::DirectorySeparator;
buffer[normalized_len + 1] = fs::StringTraits::NullTerminator;
}
}
/* Save output. */
*out_path = buffer.get();
*out_buf = std::move(buffer);
}
return ResultSuccess();
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 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::fssrv::impl {
template<typename F>
ALWAYS_INLINE Result RetryFinitelyForDataCorrupted(F f) {
/* All official uses of this retry once, for two tries total. */
constexpr auto MaxTryCount = 2;
/* Perform the operation, retrying on fs::ResultDataCorrupted. */
auto tries = 0;
while (true) {
/* Try to perform the operation. */
const auto rc = f();
/* If we should, retry. */
if (fs::ResultDataCorrupted::Includes(rc)) {
if ((++tries) < MaxTryCount) {
continue;
}
}
/* Ensure the current attempt succeeded. */
R_TRY(rc);
/* Return success. */
R_SUCCEED();
}
}
}

View File

@@ -15,80 +15,62 @@
*/
#include <stratosphere.hpp>
#include <stratosphere/fssrv/fssrv_interface_adapters.hpp>
#include "fssrv_retry_utility.hpp"
namespace ams::fssrv::impl {
StorageInterfaceAdapter::StorageInterfaceAdapter(fs::IStorage *storage) : m_base_storage(storage) {
/* ... */
}
StorageInterfaceAdapter::StorageInterfaceAdapter(std::unique_ptr<fs::IStorage> storage) : m_base_storage(storage.release()) {
/* ... */
}
StorageInterfaceAdapter::StorageInterfaceAdapter(std::shared_ptr<fs::IStorage> storage) : m_base_storage(std::move(storage)) {
/* ... */
}
StorageInterfaceAdapter::~StorageInterfaceAdapter() {
/* ... */
}
util::optional<std::shared_lock<os::ReaderWriterLock>> StorageInterfaceAdapter::AcquireCacheInvalidationReadLock() {
util::optional<std::shared_lock<os::ReaderWriterLock>> lock;
if (m_deep_retry_enabled) {
lock.emplace(m_invalidation_lock);
}
return lock;
}
Result StorageInterfaceAdapter::Read(s64 offset, const ams::sf::OutNonSecureBuffer &buffer, s64 size) {
/* TODO: N retries on fs::ResultDataCorrupted, we may want to eventually. */
/* TODO: Deep retry */
R_UNLESS(offset >= 0, fs::ResultInvalidOffset());
R_UNLESS(size >= 0, fs::ResultInvalidSize());
return m_base_storage->Read(offset, buffer.GetPointer(), size);
/* Check pre-conditions. */
R_UNLESS(0 <= offset, fs::ResultInvalidOffset());
R_UNLESS(0 <= size, fs::ResultInvalidSize());
R_UNLESS(size <= static_cast<s64>(buffer.GetSize()), fs::ResultInvalidSize());
R_RETURN(RetryFinitelyForDataCorrupted([&] () ALWAYS_INLINE_LAMBDA {
R_RETURN(m_base_storage->Read(offset, buffer.GetPointer(), 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());
/* Check pre-conditions. */
R_UNLESS(0 <= offset, fs::ResultInvalidOffset());
R_UNLESS(0 <= size, fs::ResultInvalidSize());
R_UNLESS(size <= static_cast<s64>(buffer.GetSize()), fs::ResultInvalidSize());
auto read_lock = this->AcquireCacheInvalidationReadLock();
return m_base_storage->Write(offset, buffer.GetPointer(), size);
/* Temporarily increase our thread's priority. */
fssystem::ScopedThreadPriorityChangerByAccessPriority cp(fssystem::ScopedThreadPriorityChangerByAccessPriority::AccessMode::Write);
R_RETURN(m_base_storage->Write(offset, buffer.GetPointer(), size));
}
Result StorageInterfaceAdapter::Flush() {
auto read_lock = this->AcquireCacheInvalidationReadLock();
return m_base_storage->Flush();
R_RETURN(m_base_storage->Flush());
}
Result StorageInterfaceAdapter::SetSize(s64 size) {
R_UNLESS(size >= 0, fs::ResultInvalidSize());
auto read_lock = this->AcquireCacheInvalidationReadLock();
return m_base_storage->SetSize(size);
R_RETURN(m_base_storage->SetSize(size));
}
Result StorageInterfaceAdapter::GetSize(ams::sf::Out<s64> out) {
auto read_lock = this->AcquireCacheInvalidationReadLock();
return m_base_storage->GetSize(out.GetPointer());
R_RETURN(m_base_storage->GetSize(out.GetPointer()));
}
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());
/* Clear the range info. */
out->Clear();
if (op_id == static_cast<s32>(fs::OperationId::QueryRange)) {
auto read_lock = this->AcquireCacheInvalidationReadLock();
fs::StorageQueryRangeInfo info;
if (op_id == static_cast<s32>(fs::OperationId::QueryRange)) {
fs::FileQueryRangeInfo info;
R_TRY(m_base_storage->OperateRange(std::addressof(info), sizeof(info), fs::OperationId::QueryRange, offset, size, nullptr, 0));
out->Merge(info);
} else if (op_id == static_cast<s32>(fs::OperationId::Invalidate)) {
R_TRY(m_base_storage->OperateRange(nullptr, 0, fs::OperationId::Invalidate, offset, size, nullptr, 0));
}
return ResultSuccess();
R_SUCCEED();
}
}

View File

@@ -20,15 +20,7 @@ namespace ams::fssrv::impl {
namespace {
constinit os::SdkMutex g_mutex;
constinit bool g_initialized = false;
constinit u64 g_initial_process_id_min = 0;
constinit u64 g_initial_process_id_max = 0;
constinit u64 g_current_process_id = 0;
constinit std::aligned_storage<0x80>::type g_static_buffer_for_program_info_for_initial_process;
constinit std::aligned_storage<0x80>::type g_static_buffer_for_program_info_for_initial_process = {};
template<typename T>
class StaticAllocatorForProgramInfoForInitialProcess : public std::allocator<T> {
@@ -56,6 +48,16 @@ namespace ams::fssrv::impl {
constexpr const u32 FileAccessControlForInitialProgram[0x1C / sizeof(u32)] = {0x00000001, 0x00000000, 0x80000000, 0x0000001C, 0x00000000, 0x0000001C, 0x00000000};
constexpr const u32 FileAccessControlDescForInitialProgram[0x2C / sizeof(u32)] = {0x00000001, 0x00000000, 0x80000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF};
#if defined(ATMOSPHERE_OS_HORIZON)
constinit os::SdkMutex g_mutex;
constinit bool g_initialized = false;
constinit u64 g_initial_process_id_min = 0;
constinit u64 g_initial_process_id_max = 0;
constinit u64 g_current_process_id = 0;
ALWAYS_INLINE void InitializeInitialAndCurrentProcessId() {
if (AMS_UNLIKELY(!g_initialized)) {
std::scoped_lock lk(g_mutex);
@@ -75,45 +77,49 @@ namespace ams::fssrv::impl {
}
}
}
#endif
}
std::shared_ptr<ProgramInfo> ProgramInfo::GetProgramInfoForInitialProcess() {
static constinit os::SdkMutex s_mutex;
static constinit bool s_initialized = false;
static constinit std::shared_ptr<ProgramInfo> s_initial_program_info = nullptr;
class ProgramInfoHelper : public ProgramInfo {
public:
ProgramInfoHelper(const void *data, s64 data_size, const void *desc, s64 desc_size) : ProgramInfo(data, data_size, desc, desc_size) { /* ... */ }
};
/* Ensure we've initialized the program info. */
if (AMS_UNLIKELY(!s_initialized)) {
std::scoped_lock lk(s_mutex);
if (AMS_LIKELY(!s_initialized)) {
class ProgramInfoHelper : public ProgramInfo {
public:
ProgramInfoHelper(const void *data, s64 data_size, const void *desc, s64 desc_size) : ProgramInfo(data, data_size, desc, desc_size) { /* ... */ }
};
s_initial_program_info = std::allocate_shared<ProgramInfoHelper>(StaticAllocatorForProgramInfoForInitialProcess<char>{}, FileAccessControlForInitialProgram, sizeof(FileAccessControlForInitialProgram), FileAccessControlDescForInitialProgram, sizeof(FileAccessControlDescForInitialProgram));
s_initialized = true;
}
}
AMS_FUNCTION_LOCAL_STATIC(std::shared_ptr<ProgramInfo>, s_initial_program_info, std::allocate_shared<ProgramInfoHelper>(StaticAllocatorForProgramInfoForInitialProcess<char>{}, FileAccessControlForInitialProgram, sizeof(FileAccessControlForInitialProgram), FileAccessControlDescForInitialProgram, sizeof(FileAccessControlDescForInitialProgram)));
return s_initial_program_info;
}
bool IsInitialProgram(u64 process_id) {
#if defined(ATMOSPHERE_OS_HORIZON)
/* Initialize/sanity check. */
InitializeInitialAndCurrentProcessId();
AMS_ABORT_UNLESS(g_initial_process_id_min > 0);
/* Check process id in range. */
return g_initial_process_id_min <= process_id && process_id <= g_initial_process_id_max;
#elif defined(ATMOSPHERE_OS_WINDOWS) || defined(ATMOSPHERE_OS_LINUX) || defined(ATMOSPHERE_OS_MACOS)
AMS_UNUSED(process_id);
return true;
#else
#error "Unknown os for fssrv::impl::IsInitialProgram"
#endif
}
bool IsCurrentProcess(u64 process_id) {
#if defined(ATMOSPHERE_OS_HORIZON)
/* Initialize. */
InitializeInitialAndCurrentProcessId();
return process_id == g_current_process_id;
#elif defined(ATMOSPHERE_OS_WINDOWS) || defined(ATMOSPHERE_OS_LINUX) || defined(ATMOSPHERE_OS_MACOS)
AMS_UNUSED(process_id);
return true;
#else
#error "Unknown os for fssrv::impl::IsCurrentProcess"
#endif
}
}

View File

@@ -41,7 +41,7 @@ namespace ams::fssrv::impl {
public:
static std::shared_ptr<ProgramInfo> GetProgramInfoForInitialProcess();
private:
ProgramInfo(const void *data, s64 data_size, const void *desc, s64 desc_size) : m_process_id(0), m_program_id(0), m_storage_id(static_cast<ncm::StorageId>(0)) /* TODO: m_access_control */ {
ProgramInfo(const void *data, s64 data_size, const void *desc, s64 desc_size) : m_process_id(os::InvalidProcessId), m_program_id(ncm::InvalidProgramId), m_storage_id(static_cast<ncm::StorageId>(0)) /* TODO: m_access_control */ {
/* TODO */
AMS_UNUSED(data, data_size, desc, desc_size);
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright (c) 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 "fssrv_utility.hpp"
#if defined(ATMOSPHERE_OS_LINUX)
#include <unistd.h>
#elif defined(ATMOSPHERE_OS_MACOS)
#include <mach-o/dyld.h>
#endif
namespace ams::fssystem {
class PathOnExecutionDirectory {
private:
char m_path[fs::EntryNameLengthMax + 1];
public:
PathOnExecutionDirectory() {
#if defined(ATMOSPHERE_OS_WINDOWS)
{
/* Get the module file name. */
wchar_t module_file_name[fs::EntryNameLengthMax + 1];
if (::GetModuleFileNameW(0, module_file_name, util::size(module_file_name)) == 0) {
AMS_FS_R_ABORT_UNLESS(fs::ResultUnexpectedInPathOnExecutionDirectoryA());
}
/* Split the path. */
wchar_t drive_name[3];
wchar_t dir_name[fs::EntryNameLengthMax + 1];
::_wsplitpath_s(module_file_name, drive_name, util::size(drive_name), dir_name, util::size(dir_name), nullptr, 0, nullptr, 0);
/* Print the drive and directory. */
wchar_t path[fs::EntryNameLengthMax + 1];
::swprintf_s(path, util::size(path), L"%s%s", drive_name, dir_name);
/* Convert to utf-8. */
const auto res = ::WideCharToMultiByte(CP_UTF8, 0, path, -1, m_path, util::size(m_path), nullptr, nullptr);
if (res == 0) {
AMS_FS_R_ABORT_UNLESS(fs::ResultUnexpectedInPathOnExecutionDirectoryB());
}
}
#elif defined(ATMOSPHERE_OS_LINUX)
{
char full_path[PATH_MAX] = {};
if (::readlink("/proc/self/exe", full_path, sizeof(full_path)) == -1) {
AMS_FS_R_ABORT_UNLESS(fs::ResultUnexpectedInPathOnExecutionDirectoryA());
}
const int len = std::strlen(full_path);
std::memcpy(m_path, full_path, len + 1);
for (int i = len - 1; i >= 0; --i) {
if (m_path[i] == '/') {
m_path[i + 1] = 0;
break;
}
}
}
#elif defined(ATMOSPHERE_OS_MACOS)
{
char full_path[PATH_MAX + 1] = {};
uint32_t size = sizeof(full_path);
AMS_ABORT_UNLESS(_NSGetExecutablePath(full_path, std::addressof(size)) == 0);
const int len = std::strlen(full_path);
std::memcpy(m_path, full_path, len + 1);
for (int i = len - 1; i >= 0; --i) {
if (m_path[i] == '/') {
m_path[i + 1] = 0;
break;
}
}
}
#else
AMS_ABORT("TODO: Unknown OS for PathOnExecutionDirectory");
#endif
}
const char *Get() const {
return m_path;
}
};
}
namespace ams::fssrv::impl {
const char *GetExecutionDirectoryPath() {
AMS_FUNCTION_LOCAL_STATIC(fssystem::PathOnExecutionDirectory, s_path_on_execution_directory);
return s_path_on_execution_directory.Get();
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 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::fssrv::impl {
const char *GetExecutionDirectoryPath();
}

View File

@@ -206,7 +206,7 @@ namespace ams::fssystem {
/* Setup the keyslot cache. */
for (s32 i = 0; i < KeySlotCacheEntryCount; i++) {
s32 slot_index;
s32 slot_index = -1;
R_ABORT_UNLESS(spl::AllocateAesKeySlot(std::addressof(slot_index)));
g_key_slot_cache_entry[i].emplace(slot_index);
g_key_slot_cache.AddEntry(std::addressof(g_key_slot_cache_entry[i].value()));

View File

@@ -1,129 +0,0 @@
/*
* Copyright (c) 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, bool unc)
: PathResolutionFileSystem(fs, unc)
{
m_before_dir = nullptr;
m_after_dir = nullptr;
R_ABORT_UNLESS(this->Initialize(before, after));
}
DirectoryRedirectionFileSystem::DirectoryRedirectionFileSystem(std::unique_ptr<fs::fsa::IFileSystem> fs, const char *before, const char *after, bool unc)
: PathResolutionFileSystem(std::move(fs), unc)
{
m_before_dir = nullptr;
m_after_dir = nullptr;
R_ABORT_UNLESS(this->Initialize(before, after));
}
DirectoryRedirectionFileSystem::~DirectoryRedirectionFileSystem() {
if (m_before_dir != nullptr) {
fs::impl::Deallocate(m_before_dir, m_before_dir_len);
}
if (m_after_dir != nullptr) {
fs::impl::Deallocate(m_after_dir, m_after_dir_len);
}
}
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(fs::PathNormalizer::Normalize(normalized_path, &normalized_path_len, dir, sizeof(normalized_path), this->IsUncPreserved(), false));
/* Ensure terminating '/' */
if (!fs::PathNormalizer::IsSeparator(normalized_path[normalized_path_len - 1])) {
AMS_ASSERT(normalized_path_len + 2 < sizeof(normalized_path));
normalized_path[normalized_path_len] = fs::StringTraits::DirectorySeparator;
normalized_path[normalized_path_len + 1] = fs::StringTraits::NullTerminator;
++normalized_path_len;
}
/* Allocate new path. */
const size_t size = normalized_path_len + 1;
char *new_dir = static_cast<char *>(fs::impl::Allocate(size));
AMS_ABORT_UNLESS(new_dir != nullptr);
/* TODO: custom fs::ResultAllocationFailure? */
/* Copy path in. */
std::memcpy(new_dir, normalized_path, normalized_path_len);
new_dir[normalized_path_len] = fs::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(std::addressof(m_before_dir), std::addressof(m_before_dir_len), before);
this->GetNormalizedDirectoryPath(std::addressof(m_after_dir), std::addressof(m_after_dir_len), after);
return ResultSuccess();
}
Result DirectoryRedirectionFileSystem::ResolveFullPath(char *out, size_t out_size, const char *relative_path) {
/* Check pre-conditions. */
AMS_ASSERT(relative_path[0] == '/');
AMS_ASSERT(m_before_dir_len >= 2);
AMS_ASSERT(m_after_dir_len >= 2);
AMS_ASSERT(m_after_dir_len <= out_size);
AMS_ASSERT(fs::PathNormalizer::IsNullTerminator(m_before_dir[m_before_dir_len - 1]));
AMS_ASSERT(fs::PathNormalizer::IsSeparator(m_before_dir[m_before_dir_len - 2]));
AMS_ASSERT(fs::PathNormalizer::IsNullTerminator(m_after_dir[m_after_dir_len - 1]));
AMS_ASSERT(fs::PathNormalizer::IsSeparator(m_after_dir[m_after_dir_len - 2]));
/* Normalize the relative path. */
char normalized_rel_path[fs::EntryNameLengthMax + 1];
size_t normalized_rel_path_len;
R_TRY(fs::PathNormalizer::Normalize(normalized_rel_path, std::addressof(normalized_rel_path_len), relative_path, sizeof(normalized_rel_path), this->IsUncPreserved(), false));
const bool is_prefixed = std::memcmp(normalized_rel_path, m_before_dir, m_before_dir_len - 2) == 0 &&
(fs::PathNormalizer::IsSeparator(normalized_rel_path[m_before_dir_len - 2]) || fs::PathNormalizer::IsNullTerminator(normalized_rel_path[m_before_dir_len - 2]));
if (is_prefixed) {
const size_t before_prefix_len = m_before_dir_len - 2;
const size_t after_prefix_len = m_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, m_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] = fs::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] = fs::StringTraits::NullTerminator;
}
return ResultSuccess();
}
}

View File

@@ -19,13 +19,15 @@ namespace ams::fssystem {
namespace {
constexpr size_t IdealWorkBufferSize = 0x100000; /* 1 MiB */
constexpr size_t IdealWorkBufferSize = 1_MB;
constexpr size_t MinimumWorkBufferSize = 1_KB;
constexpr const char CommittedDirectoryPath[] = "/0/";
constexpr const char WorkingDirectoryPath[] = "/1/";
constexpr const char SynchronizingDirectoryPath[] = "/_/";
constexpr const fs::Path CommittedDirectoryPath = fs::MakeConstantPath("/0");
constexpr const fs::Path WorkingDirectoryPath = fs::MakeConstantPath("/1");
constexpr const fs::Path SynchronizingDirectoryPath = fs::MakeConstantPath("/_");
constexpr const fs::Path LockFilePath = fs::MakeConstantPath("/.lock");
class DirectorySaveDataFile : public fs::fsa::IFile {
class DirectorySaveDataFile : public fs::fsa::IFile, public fs::impl::Newable {
private:
std::unique_ptr<fs::fsa::IFile> m_base_file;
DirectorySaveDataFileSystem *m_parent_fs;
@@ -38,7 +40,7 @@ namespace ams::fssystem {
virtual ~DirectorySaveDataFile() {
/* Observe closing of writable file. */
if (m_open_mode & fs::OpenMode_Write) {
m_parent_fs->OnWritableFileClose();
m_parent_fs->DecrementWriteOpenFileCount();
}
}
public:
@@ -73,25 +75,14 @@ namespace ams::fssystem {
}
DirectorySaveDataFileSystem::DirectorySaveDataFileSystem(std::shared_ptr<fs::fsa::IFileSystem> fs)
: PathResolutionFileSystem(fs), m_accessor_mutex(), m_open_writable_files(0)
{
/* ... */
}
Result DirectorySaveDataFileSystem::Initialize(bool journaling_supported, bool multi_commit_supported, bool journaling_enabled) {
/* Configure ourselves. */
m_is_journaling_supported = journaling_supported;
m_is_multi_commit_supported = multi_commit_supported;
m_is_journaling_enabled = journaling_enabled;
DirectorySaveDataFileSystem::DirectorySaveDataFileSystem(std::unique_ptr<fs::fsa::IFileSystem> fs)
: PathResolutionFileSystem(std::move(fs)), m_accessor_mutex(), m_open_writable_files(0)
{
/* ... */
}
DirectorySaveDataFileSystem::~DirectorySaveDataFileSystem() {
/* ... */
}
Result DirectorySaveDataFileSystem::Initialize() {
/* Nintendo does not acquire the lock here, but I think we probably should. */
std::scoped_lock lk(m_accessor_mutex);
/* Ensure that we can initialize further by acquiring a lock on the filesystem. */
R_TRY(this->AcquireLockFile());
fs::DirectoryEntryType type;
@@ -100,44 +91,35 @@ namespace ams::fssystem {
/* If path isn't found, create working directory and committed directory. */
R_CATCH(fs::ResultPathNotFound) {
R_TRY(m_base_fs->CreateDirectory(WorkingDirectoryPath));
R_TRY(m_base_fs->CreateDirectory(CommittedDirectoryPath));
if (m_is_journaling_supported) {
R_TRY(m_base_fs->CreateDirectory(CommittedDirectoryPath));
}
}
} R_END_TRY_CATCH;
/* Now check for the committed directory. */
R_TRY_CATCH(m_base_fs->GetEntryType(std::addressof(type), CommittedDirectoryPath)) {
/* Committed doesn't exist, so synchronize and rename. */
R_CATCH(fs::ResultPathNotFound) {
R_TRY(this->SynchronizeDirectory(SynchronizingDirectoryPath, WorkingDirectoryPath));
R_TRY(m_base_fs->RenameDirectory(SynchronizingDirectoryPath, CommittedDirectoryPath));
return ResultSuccess();
}
} R_END_TRY_CATCH;
/* If we support journaling, we need to set up the committed directory. */
if (m_is_journaling_supported) {
/* Now check for the committed directory. */
R_TRY_CATCH(m_base_fs->GetEntryType(std::addressof(type), CommittedDirectoryPath)) {
/* Committed doesn't exist, so synchronize and rename. */
R_CATCH(fs::ResultPathNotFound) {
R_TRY(this->SynchronizeDirectory(SynchronizingDirectoryPath, WorkingDirectoryPath));
R_TRY(m_base_fs->RenameDirectory(SynchronizingDirectoryPath, CommittedDirectoryPath));
R_SUCCEED();
}
} R_END_TRY_CATCH;
/* The committed directory exists, so synchronize it to the working directory. */
return this->SynchronizeDirectory(WorkingDirectoryPath, CommittedDirectoryPath);
}
Result DirectorySaveDataFileSystem::AllocateWorkBuffer(std::unique_ptr<u8[]> *out, size_t *out_size, size_t size) {
/* Repeatedly try to allocate until success. */
while (size > 0x200) {
/* Allocate the buffer. */
if (auto mem = new (std::nothrow) u8[size]; mem != nullptr) {
out->reset(mem);
*out_size = size;
return ResultSuccess();
} else {
/* Divide size by two. */
size >>= 1;
/* The committed directory exists, so if we should, synchronize it to the working directory. */
if (m_is_journaling_enabled) {
R_TRY(this->SynchronizeDirectory(WorkingDirectoryPath, CommittedDirectoryPath));
}
}
/* TODO: Return a result here? Nintendo does not, but they have other allocation failed results. */
/* Consider returning fs::ResultFsAllocationFailureInDirectorySaveDataFileSystem? */
AMS_ABORT_UNLESS(false);
R_SUCCEED();
}
Result DirectorySaveDataFileSystem::SynchronizeDirectory(const char *dst, const char *src) {
Result DirectorySaveDataFileSystem::SynchronizeDirectory(const fs::Path &dst, const fs::Path &src) {
/* Delete destination dir and recreate it. */
R_TRY_CATCH(m_base_fs->DeleteDirectoryRecursively(dst)) {
R_CATCH(fs::ResultPathNotFound) { /* Nintendo returns error unconditionally, but I think that's a bug in their code. */}
@@ -146,132 +128,239 @@ namespace ams::fssystem {
R_TRY(m_base_fs->CreateDirectory(dst));
/* Get a work buffer to work with. */
std::unique_ptr<u8[]> work_buf;
size_t work_buf_size;
R_TRY(this->AllocateWorkBuffer(std::addressof(work_buf), std::addressof(work_buf_size), IdealWorkBufferSize));
fssystem::PooledBuffer buffer;
buffer.AllocateParticularlyLarge(IdealWorkBufferSize, MinimumWorkBufferSize);
/* Copy the directory recursively. */
return fssystem::CopyDirectoryRecursively(m_base_fs, dst, src, work_buf.get(), work_buf_size);
fs::DirectoryEntry dir_entry_buffer = {};
R_RETURN(fssystem::CopyDirectoryRecursively(m_base_fs, dst, src, std::addressof(dir_entry_buffer), buffer.GetBuffer(), buffer.GetSize()));
}
Result DirectorySaveDataFileSystem::ResolveFullPath(char *out, size_t out_size, const char *relative_path) {
R_UNLESS(strnlen(relative_path, fs::EntryNameLengthMax + 1) < fs::EntryNameLengthMax + 1, fs::ResultTooLongPath());
R_UNLESS(fs::PathNormalizer::IsSeparator(relative_path[0]), fs::ResultInvalidPath());
/* Copy working directory path. */
std::strncpy(out, WorkingDirectoryPath, out_size);
out[out_size - 1] = fs::StringTraits::NullTerminator;
/* Normalize it. */
constexpr size_t WorkingDirectoryPathLength = sizeof(WorkingDirectoryPath) - 1;
size_t normalized_length;
return fs::PathNormalizer::Normalize(out + WorkingDirectoryPathLength - 1, std::addressof(normalized_length), relative_path, out_size - (WorkingDirectoryPathLength - 1));
Result DirectorySaveDataFileSystem::ResolvePath(fs::Path *out, const fs::Path &path) {
const fs::Path &directory = (m_is_journaling_supported && !m_is_journaling_enabled) ? CommittedDirectoryPath : WorkingDirectoryPath;
R_RETURN(out->Combine(directory, path));
}
void DirectorySaveDataFileSystem::OnWritableFileClose() {
Result DirectorySaveDataFileSystem::AcquireLockFile() {
/* If we already have a lock file, we don't need to lock again. */
R_SUCCEED_IF(m_lock_file != nullptr);
/* Open the lock file. */
std::unique_ptr<fs::fsa::IFile> file;
R_TRY_CATCH(m_base_fs->OpenFile(std::addressof(file), LockFilePath, fs::OpenMode_ReadWrite)) {
/* If the lock file doesn't yet exist, we may need to create it. */
R_CATCH(fs::ResultPathNotFound) {
R_TRY(m_base_fs->CreateFile(LockFilePath, 0));
R_TRY(m_base_fs->OpenFile(std::addressof(file), LockFilePath, fs::OpenMode_ReadWrite));
}
} R_END_TRY_CATCH;
/* Set our lock file. */
m_lock_file = std::move(file);
R_SUCCEED();
}
void DirectorySaveDataFileSystem::DecrementWriteOpenFileCount() {
std::scoped_lock lk(m_accessor_mutex);
m_open_writable_files--;
/* Nintendo does not check this, but I think it's sensible to do so. */
AMS_ABORT_UNLESS(m_open_writable_files >= 0);
--m_open_writable_files;
}
Result DirectorySaveDataFileSystem::CopySaveFromFileSystem(fs::fsa::IFileSystem *save_fs) {
/* If the input save is null, there's nothing to copy. */
R_SUCCEED_IF(save_fs == nullptr);
/* Get a work buffer to work with. */
std::unique_ptr<u8[]> work_buf;
size_t work_buf_size;
R_TRY(this->AllocateWorkBuffer(std::addressof(work_buf), std::addressof(work_buf_size), IdealWorkBufferSize));
/* Copy the directory recursively. */
R_TRY(fssystem::CopyDirectoryRecursively(m_base_fs, save_fs, fs::PathNormalizer::RootPath, fs::PathNormalizer::RootPath, work_buf.get(), work_buf_size));
return this->Commit();
}
/* Overridden from IPathResolutionFileSystem */
Result DirectorySaveDataFileSystem::DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) {
char full_path[fs::EntryNameLengthMax + 1];
R_TRY(this->ResolveFullPath(full_path, sizeof(full_path), path));
Result DirectorySaveDataFileSystem::DoCreateFile(const fs::Path &path, s64 size, int option) {
/* Resolve the final path. */
fs::Path resolved;
R_TRY(this->ResolvePath(std::addressof(resolved), path));
/* Lock ourselves. */
std::scoped_lock lk(m_accessor_mutex);
R_RETURN(m_base_fs->CreateFile(resolved, size, option));
}
Result DirectorySaveDataFileSystem::DoDeleteFile(const fs::Path &path) {
/* Resolve the final path. */
fs::Path resolved;
R_TRY(this->ResolvePath(std::addressof(resolved), path));
/* Lock ourselves. */
std::scoped_lock lk(m_accessor_mutex);
R_RETURN(m_base_fs->DeleteFile(resolved));
}
Result DirectorySaveDataFileSystem::DoCreateDirectory(const fs::Path &path) {
/* Resolve the final path. */
fs::Path resolved;
R_TRY(this->ResolvePath(std::addressof(resolved), path));
/* Lock ourselves. */
std::scoped_lock lk(m_accessor_mutex);
R_RETURN(m_base_fs->CreateDirectory(resolved));
}
Result DirectorySaveDataFileSystem::DoDeleteDirectory(const fs::Path &path) {
/* Resolve the final path. */
fs::Path resolved;
R_TRY(this->ResolvePath(std::addressof(resolved), path));
/* Lock ourselves. */
std::scoped_lock lk(m_accessor_mutex);
R_RETURN(m_base_fs->DeleteDirectory(resolved));
}
Result DirectorySaveDataFileSystem::DoDeleteDirectoryRecursively(const fs::Path &path) {
/* Resolve the final path. */
fs::Path resolved;
R_TRY(this->ResolvePath(std::addressof(resolved), path));
/* Lock ourselves. */
std::scoped_lock lk(m_accessor_mutex);
R_RETURN(m_base_fs->DeleteDirectoryRecursively(resolved));
}
Result DirectorySaveDataFileSystem::DoRenameFile(const fs::Path &old_path, const fs::Path &new_path) {
/* Resolve the final paths. */
fs::Path old_resolved;
fs::Path new_resolved;
R_TRY(this->ResolvePath(std::addressof(old_resolved), old_path));
R_TRY(this->ResolvePath(std::addressof(new_resolved), new_path));
/* Lock ourselves. */
std::scoped_lock lk(m_accessor_mutex);
R_RETURN(m_base_fs->RenameFile(old_resolved, new_resolved));
}
Result DirectorySaveDataFileSystem::DoRenameDirectory(const fs::Path &old_path, const fs::Path &new_path) {
/* Resolve the final paths. */
fs::Path old_resolved;
fs::Path new_resolved;
R_TRY(this->ResolvePath(std::addressof(old_resolved), old_path));
R_TRY(this->ResolvePath(std::addressof(new_resolved), new_path));
/* Lock ourselves. */
std::scoped_lock lk(m_accessor_mutex);
R_RETURN(m_base_fs->RenameDirectory(old_resolved, new_resolved));
}
Result DirectorySaveDataFileSystem::DoGetEntryType(fs::DirectoryEntryType *out, const fs::Path &path) {
/* Resolve the final path. */
fs::Path resolved;
R_TRY(this->ResolvePath(std::addressof(resolved), path));
/* Lock ourselves. */
std::scoped_lock lk(m_accessor_mutex);
R_RETURN(m_base_fs->GetEntryType(out, resolved));
}
Result DirectorySaveDataFileSystem::DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const fs::Path &path, fs::OpenMode mode) {
/* Resolve the final path. */
fs::Path resolved;
R_TRY(this->ResolvePath(std::addressof(resolved), path));
/* Lock ourselves. */
std::scoped_lock lk(m_accessor_mutex);
/* Open base file. */
std::unique_ptr<fs::fsa::IFile> base_file;
R_TRY(m_base_fs->OpenFile(std::addressof(base_file), full_path, mode));
R_TRY(m_base_fs->OpenFile(std::addressof(base_file), resolved, mode));
std::unique_ptr<DirectorySaveDataFile> file(new (std::nothrow) DirectorySaveDataFile(std::move(base_file), this, mode));
/* Make DirectorySaveDataFile. */
std::unique_ptr<fs::fsa::IFile> file = std::make_unique<DirectorySaveDataFile>(std::move(base_file), this, mode);
R_UNLESS(file != nullptr, fs::ResultAllocationFailureInDirectorySaveDataFileSystem());
/* Increment our open writable files, if the file is writable. */
if (mode & fs::OpenMode_Write) {
m_open_writable_files++;
++m_open_writable_files;
}
/* Set the output. */
*out_file = std::move(file);
return ResultSuccess();
R_SUCCEED();
}
Result DirectorySaveDataFileSystem::DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const fs::Path &path, fs::OpenDirectoryMode mode) {
/* Resolve the final path. */
fs::Path resolved;
R_TRY(this->ResolvePath(std::addressof(resolved), path));
/* Lock ourselves. */
std::scoped_lock lk(m_accessor_mutex);
R_RETURN(m_base_fs->OpenDirectory(out_dir, resolved, mode));
}
Result DirectorySaveDataFileSystem::DoCommit() {
/* Here, Nintendo does the following (with retries): */
/* - Rename Committed -> Synchronizing. */
/* - Synchronize Working -> Synchronizing (deleting Synchronizing). */
/* - Rename Synchronizing -> Committed. */
/* Lock ourselves. */
std::scoped_lock lk(m_accessor_mutex);
R_UNLESS(m_open_writable_files == 0, fs::ResultPreconditionViolation());
/* If we aren't journaling, we don't need to do anything. */
R_SUCCEED_IF(!m_is_journaling_enabled);
R_SUCCEED_IF(!m_is_journaling_supported);
const auto RenameCommitedDir = [&]() { return m_base_fs->RenameDirectory(CommittedDirectoryPath, SynchronizingDirectoryPath); };
const auto SynchronizeWorkingDir = [&]() { return this->SynchronizeDirectory(SynchronizingDirectoryPath, WorkingDirectoryPath); };
const auto RenameSynchronizingDir = [&]() { return m_base_fs->RenameDirectory(SynchronizingDirectoryPath, CommittedDirectoryPath); };
/* Check that there are no open files blocking the commit. */
R_UNLESS(m_open_writable_files == 0, fs::ResultWriteModeFileNotClosed());
/* Rename Committed -> Synchronizing. */
R_TRY(fssystem::RetryFinitelyForTargetLocked(std::move(RenameCommitedDir)));
/* Remove the previous commit by renaming the folder. */
R_TRY(fssystem::RetryFinitelyForTargetLocked([&] () ALWAYS_INLINE_LAMBDA { R_RETURN(m_base_fs->RenameDirectory(CommittedDirectoryPath, SynchronizingDirectoryPath)); }));
/* - Synchronize Working -> Synchronizing (deleting Synchronizing). */
R_TRY(fssystem::RetryFinitelyForTargetLocked(std::move(SynchronizeWorkingDir)));
/* Synchronize the working directory to the synchronizing directory. */
R_TRY(fssystem::RetryFinitelyForTargetLocked([&] () ALWAYS_INLINE_LAMBDA { R_RETURN(this->SynchronizeDirectory(SynchronizingDirectoryPath, WorkingDirectoryPath)); }));
/* - Rename Synchronizing -> Committed. */
R_TRY(fssystem::RetryFinitelyForTargetLocked(std::move(RenameSynchronizingDir)));
/* Rename the synchronized directory to commit it. */
R_TRY(fssystem::RetryFinitelyForTargetLocked([&] () ALWAYS_INLINE_LAMBDA { R_RETURN(m_base_fs->RenameDirectory(SynchronizingDirectoryPath, CommittedDirectoryPath)); }));
/* TODO: Should I call m_base_fs->Commit()? Nintendo does not. */
return ResultSuccess();
R_SUCCEED();
}
Result DirectorySaveDataFileSystem::DoGetFreeSpaceSize(s64 *out, const fs::Path &path) {
/* Lock ourselves. */
std::scoped_lock lk(m_accessor_mutex);
/* Get the free space size in our working directory. */
AMS_UNUSED(path);
R_RETURN(m_base_fs->GetFreeSpaceSize(out, WorkingDirectoryPath));
}
Result DirectorySaveDataFileSystem::DoGetTotalSpaceSize(s64 *out, const fs::Path &path) {
/* Lock ourselves. */
std::scoped_lock lk(m_accessor_mutex);
/* Get the free space size in our working directory. */
AMS_UNUSED(path);
R_RETURN(m_base_fs->GetTotalSpaceSize(out, WorkingDirectoryPath));
}
Result DirectorySaveDataFileSystem::DoCleanDirectoryRecursively(const fs::Path &path) {
/* Resolve the final path. */
fs::Path resolved;
R_TRY(this->ResolvePath(std::addressof(resolved), path));
/* Lock ourselves. */
std::scoped_lock lk(m_accessor_mutex);
R_RETURN(m_base_fs->CleanDirectoryRecursively(resolved));
}
/* Overridden from IPathResolutionFileSystem but not commands. */
Result DirectorySaveDataFileSystem::DoCommitProvisionally(s64 counter) {
/* Nintendo does nothing here. */
/* Check that we support multi-commit. */
R_UNLESS(m_is_multi_commit_supported, fs::ResultUnsupportedOperationInDirectorySaveDataFileSystemA());
/* Do nothing. */
AMS_UNUSED(counter);
return ResultSuccess();
R_SUCCEED();
}
Result DirectorySaveDataFileSystem::DoRollback() {
/* Initialize overwrites the working directory with the committed directory. */
return this->Initialize();
}
/* On non-journaled savedata, there's nothing to roll back to. */
R_SUCCEED_IF(!m_is_journaling_supported);
/* Explicitly overridden to be not implemented. */
Result DirectorySaveDataFileSystem::DoGetFreeSpaceSize(s64 *out, const char *path) {
AMS_UNUSED(out, path);
return fs::ResultNotImplemented();
}
Result DirectorySaveDataFileSystem::DoGetTotalSpaceSize(s64 *out, const char *path) {
AMS_UNUSED(out, path);
return fs::ResultNotImplemented();
}
Result DirectorySaveDataFileSystem::DoGetFileTimeStampRaw(fs::FileTimeStampRaw *out, const char *path) {
AMS_UNUSED(out, path);
return fs::ResultNotImplemented();
}
Result DirectorySaveDataFileSystem::DoQueryEntry(char *dst, size_t dst_size, const char *src, size_t src_size, fs::fsa::QueryId query, const char *path) {
AMS_UNUSED(dst, dst_size, src, src_size, query, path);
return fs::ResultNotImplemented();
}
Result DirectorySaveDataFileSystem::DoFlush() {
return fs::ResultNotImplemented();
/* Perform a re-initialize. */
R_RETURN(this->Initialize(m_is_journaling_supported, m_is_multi_commit_supported, m_is_journaling_enabled));
}
}

View File

@@ -17,6 +17,7 @@
namespace ams::fssystem {
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
namespace {
constexpr inline size_t MaxExternalCodeFileSystem = 0x10;
@@ -72,5 +73,21 @@ namespace ams::fssystem {
g_hnd_map.Remove(program_id);
}
}
#else
fs::fsa::IFileSystem *GetExternalCodeFileSystem(ncm::ProgramId program_id) {
AMS_UNUSED(program_id);
return nullptr;
}
Result CreateExternalCode(os::NativeHandle *out, ncm::ProgramId program_id) {
AMS_UNUSED(out, program_id);
R_THROW(fs::ResultNotImplemented());
}
void DestroyExternalCode(ncm::ProgramId program_id) {
AMS_UNUSED(program_id);
}
#endif
}

View File

@@ -39,9 +39,9 @@ namespace ams::fssystem {
constexpr size_t MaxCacheCount = 1024;
constexpr size_t BlockSize = 16_KB;
alignas(os::MemoryPageSize) u8 g_exp_heap_buffer[ExpHeapSize];
lmem::HeapHandle g_exp_heap_handle = nullptr;
fssrv::PeakCheckableMemoryResourceFromExpHeap g_exp_allocator(ExpHeapSize);
alignas(os::MemoryPageSize) constinit u8 g_exp_heap_buffer[ExpHeapSize];
constinit lmem::HeapHandle g_exp_heap_handle = nullptr;
constinit fssrv::PeakCheckableMemoryResourceFromExpHeap g_exp_allocator(ExpHeapSize);
void InitializeExpHeap() {
if (g_exp_heap_handle == nullptr) {
@@ -70,25 +70,25 @@ namespace ams::fssystem {
lmem::FreeToExpHeap(g_exp_heap_handle, p);
}
alignas(os::MemoryPageSize) u8 g_device_buffer[DeviceBufferSize];
alignas(os::MemoryPageSize) constinit u8 g_device_buffer[DeviceBufferSize] = {};
alignas(os::MemoryPageSize) u8 g_buffer_pool[BufferPoolSize];
alignas(os::MemoryPageSize) constinit u8 g_buffer_pool[BufferPoolSize] = {};
util::TypedStorage<mem::StandardAllocator> g_buffer_allocator;
util::TypedStorage<fssrv::MemoryResourceFromStandardAllocator> g_allocator;
constinit util::TypedStorage<mem::StandardAllocator> g_buffer_allocator = {};
constinit util::TypedStorage<fssrv::MemoryResourceFromStandardAllocator> g_allocator = {};
/* TODO: Nintendo uses os::SetMemoryHeapSize (svc::SetHeapSize) and os::AllocateMemoryBlock for the BufferManager heap. */
/* It's unclear how we should handle this in ams.mitm (especially hoping to reuse some logic for fs reimpl). */
/* Should we be doing the same(?) */
util::TypedStorage<fssystem::FileSystemBufferManager> g_buffer_manager;
alignas(os::MemoryPageSize) u8 g_buffer_manager_heap[BufferManagerHeapSize];
constinit util::TypedStorage<fssystem::FileSystemBufferManager> g_buffer_manager = {};
alignas(os::MemoryPageSize) constinit u8 g_buffer_manager_heap[BufferManagerHeapSize] = {};
/* FileSystem creators. */
util::TypedStorage<fssrv::fscreator::RomFileSystemCreator> g_rom_fs_creator;
util::TypedStorage<fssrv::fscreator::PartitionFileSystemCreator> g_partition_fs_creator;
util::TypedStorage<fssrv::fscreator::StorageOnNcaCreator> g_storage_on_nca_creator;
constinit util::TypedStorage<fssrv::fscreator::RomFileSystemCreator> g_rom_fs_creator = {};
constinit util::TypedStorage<fssrv::fscreator::PartitionFileSystemCreator> g_partition_fs_creator = {};
constinit util::TypedStorage<fssrv::fscreator::StorageOnNcaCreator> g_storage_on_nca_creator = {};
fssrv::fscreator::FileSystemCreatorInterfaces g_fs_creator_interfaces = {};
constinit fssrv::fscreator::FileSystemCreatorInterfaces g_fs_creator_interfaces = {};
}
@@ -162,7 +162,21 @@ namespace ams::fssystem {
/* TODO FS-REIMPL: Sd Card detection, speed emulation. */
/* Initialize fssrv. TODO FS-REIMPL: More arguments, more actions taken. */
fssrv::InitializeForFileSystemProxy(std::addressof(g_fs_creator_interfaces), GetPointer(g_buffer_manager), is_development_function_enabled);
const fssrv::FileSystemProxyConfiguration config = {
.m_fs_creator_interfaces = std::addressof(g_fs_creator_interfaces),
.m_base_storage_service_impl = nullptr /* TODO */,
.m_base_file_system_service_impl = nullptr /* TODO */,
.m_nca_file_system_service_impl = nullptr /* TODO */,
.m_save_data_file_system_service_impl = nullptr /* TODO */,
.m_access_failure_management_service_impl = nullptr /* TODO */,
.m_time_service_impl = nullptr /* TODO */,
.m_status_report_service_impl = nullptr /* TODO */,
.m_program_registry_service_impl = std::addressof(program_registry_service),
.m_access_log_service_impl = nullptr /* TODO */,
.m_debug_configuration_service_impl = nullptr /* TODO */,
};
fssrv::InitializeForFileSystemProxy(config);
/* TODO FS-REIMPL: GetFileSystemProxyServiceObject(), set current process, initialize global service object. */
@@ -241,7 +255,21 @@ namespace ams::fssystem {
};
/* Initialize fssrv. TODO FS-REIMPL: More arguments, more actions taken. */
fssrv::InitializeForFileSystemProxy(std::addressof(g_fs_creator_interfaces), GetPointer(g_buffer_manager), is_development_function_enabled);
const fssrv::FileSystemProxyConfiguration config = {
.m_fs_creator_interfaces = std::addressof(g_fs_creator_interfaces),
.m_base_storage_service_impl = nullptr /* TODO */,
.m_base_file_system_service_impl = nullptr /* TODO */,
.m_nca_file_system_service_impl = nullptr /* TODO */,
.m_save_data_file_system_service_impl = nullptr /* TODO */,
.m_access_failure_management_service_impl = nullptr /* TODO */,
.m_time_service_impl = nullptr /* TODO */,
.m_status_report_service_impl = nullptr /* TODO */,
.m_program_registry_service_impl = nullptr /* TODO */,
.m_access_log_service_impl = nullptr /* TODO */,
.m_debug_configuration_service_impl = nullptr /* TODO */,
};
fssrv::InitializeForFileSystemProxy(config);
/* Disable auto-abort in fs library code. */
fs::SetEnabledAutoAbort(false);

File diff suppressed because it is too large Load Diff

View File

@@ -292,13 +292,13 @@ namespace ams::fssystem {
using IntegrityLevelInfo = NcaFsHeader::HashData::IntegrityMetaInfo::LevelHashInfo;
using IntegrityDataInfo = IntegrityLevelInfo::HierarchicalIntegrityVerificationLevelInformation;
inline const Sha256DataRegion &GetSha256DataRegion(const NcaFsHeader::HashData &hash_data) {
return hash_data.hierarchical_sha256_data.hash_layer_region[1];
}
// inline const Sha256DataRegion &GetSha256DataRegion(const NcaFsHeader::HashData &hash_data) {
// return hash_data.hierarchical_sha256_data.hash_layer_region[1];
// }
inline const IntegrityDataInfo &GetIntegrityDataInfo(const NcaFsHeader::HashData &hash_data) {
return hash_data.integrity_meta_info.level_hash_info.info[hash_data.integrity_meta_info.level_hash_info.max_layers - 2];
}
// inline const IntegrityDataInfo &GetIntegrityDataInfo(const NcaFsHeader::HashData &hash_data) {
// return hash_data.integrity_meta_info.level_hash_info.info[hash_data.integrity_meta_info.level_hash_info.max_layers - 2];
// }
}
@@ -706,7 +706,7 @@ namespace ams::fssystem {
Result NcaFileSystemDriver::CreateSparseStorage(std::shared_ptr<fs::IStorage> *out, s64 *out_fs_data_offset, std::shared_ptr<fssystem::SparseStorage> *out_sparse_storage, std::shared_ptr<fs::IStorage> *out_meta_storage, s32 index, const NcaAesCtrUpperIv &upper_iv, const NcaSparseInfo &sparse_info) {
/* Validate preconditions. */
AMS_ASSERT(out != nullptr);
AMS_ASSERT(base_storage != nullptr);
AMS_ASSERT(out_fs_data_offset != nullptr);
/* Check the sparse info generation. */
R_UNLESS(sparse_info.generation != 0, fs::ResultInvalidNcaHeader());

View File

@@ -375,7 +375,7 @@ namespace ams::fssystem {
/* Generate the hash. */
Hash hash;
crypto::GenerateSha256Hash(std::addressof(hash), sizeof(hash), std::addressof(m_data), sizeof(NcaFsHeader));
crypto::GenerateSha256(std::addressof(hash), sizeof(hash), std::addressof(m_data), sizeof(NcaFsHeader));
/* Validate the hash. */
R_UNLESS(crypto::IsSameBytes(std::addressof(reader.GetFsHeaderHash(index)), std::addressof(hash), sizeof(Hash)), fs::ResultNcaFsHeaderHashVerificationFailed());

View File

@@ -19,6 +19,8 @@ namespace ams::fssystem {
namespace {
constexpr const char RootPath[] = "/";
class PartitionFileSystemDefaultAllocator : public MemoryResource {
private:
virtual void *AllocateImpl(size_t size, size_t alignment) override {
@@ -53,7 +55,7 @@ namespace ams::fssystem {
virtual Result DoGetSize(s64 *out) override final {
*out = m_partition_entry->size;
return ResultSuccess();
R_SUCCEED();
}
virtual Result DoFlush() override final {
@@ -61,7 +63,7 @@ namespace ams::fssystem {
R_SUCCEED_IF((m_mode & fs::OpenMode_Write) == 0);
/* Flush base storage. */
return m_parent->m_base_storage->Flush();
R_RETURN(m_parent->m_base_storage->Flush());
}
virtual Result DoWrite(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final {
@@ -78,12 +80,12 @@ namespace ams::fssystem {
R_UNLESS(static_cast<s64>(offset + size) <= static_cast<s64>(m_partition_entry->size), fs::ResultInvalidSize());
/* Write to the base storage. */
return m_parent->m_base_storage->Write(m_parent->m_meta_data_size + m_partition_entry->offset + offset, buffer, size);
R_RETURN(m_parent->m_base_storage->Write(m_parent->m_meta_data_size + m_partition_entry->offset + offset, buffer, size));
}
virtual Result DoSetSize(s64 size) override final {
R_TRY(this->DrySetSize(size, m_mode));
return fs::ResultUnsupportedOperationInPartitionFileA();
R_RETURN(fs::ResultUnsupportedOperationInPartitionFileA());
}
virtual Result DoOperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final {
@@ -96,7 +98,7 @@ namespace ams::fssystem {
case fs::OperationId::QueryRange:
break;
default:
return fs::ResultUnsupportedOperationInPartitionFileB();
R_THROW(fs::ResultUnsupportedOperationInPartitionFileB());
}
/* Validate offset and size. */
@@ -105,7 +107,7 @@ namespace ams::fssystem {
R_UNLESS(static_cast<s64>(offset + size) <= static_cast<s64>(m_partition_entry->size), fs::ResultInvalidSize());
R_UNLESS(static_cast<s64>(offset + size) >= offset, fs::ResultInvalidSize());
return m_parent->m_base_storage->OperateRange(dst, dst_size, op_id, m_parent->m_meta_data_size + m_partition_entry->offset + offset, size, src, src_size);
R_RETURN(m_parent->m_base_storage->OperateRange(dst, dst_size, op_id, m_parent->m_meta_data_size + m_partition_entry->offset + offset, size, src, src_size));
}
public:
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
@@ -125,7 +127,7 @@ namespace ams::fssystem {
/* Set output size. */
*out = read_size;
return ResultSuccess();
R_SUCCEED();
}
template<>
@@ -213,7 +215,7 @@ namespace ams::fssystem {
/* Set output size. */
*out = read_size;
return ResultSuccess();
R_SUCCEED();
}
template <typename MetaType>
@@ -229,7 +231,7 @@ namespace ams::fssystem {
/* There are no subdirectories. */
if ((m_mode & fs::OpenDirectoryMode_File) == 0) {
*out_count = 0;
return ResultSuccess();
R_SUCCEED();
}
/* Calculate number of entries. */
@@ -247,7 +249,7 @@ namespace ams::fssystem {
}
*out_count = entry_count;
return ResultSuccess();
R_SUCCEED();
}
virtual Result DoGetEntryCount(s64 *out) override final {
@@ -258,7 +260,7 @@ namespace ams::fssystem {
*out = 0;
}
return ResultSuccess();
R_SUCCEED();
}
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
@@ -294,13 +296,13 @@ namespace ams::fssystem {
m_base_storage = base_storage;
m_meta_data_size = m_meta_data->GetMetaDataSize();
m_initialized = true;
return ResultSuccess();
R_SUCCEED();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::Initialize(std::unique_ptr<MetaType> &&meta_data, std::shared_ptr<fs::IStorage> base_storage) {
m_unique_meta_data = std::move(meta_data);
return this->Initialize(m_unique_meta_data.get(), base_storage);
R_RETURN(this->Initialize(m_unique_meta_data.get(), base_storage));
}
template <typename MetaType>
@@ -314,24 +316,24 @@ namespace ams::fssystem {
m_meta_data = meta_data;
m_meta_data_size = m_meta_data->GetMetaDataSize();
m_initialized = true;
return ResultSuccess();
R_SUCCEED();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::Initialize(fs::IStorage *base_storage) {
return this->Initialize(base_storage, std::addressof(g_partition_filesystem_default_allocator));
R_RETURN(this->Initialize(base_storage, std::addressof(g_partition_filesystem_default_allocator)));
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::Initialize(std::shared_ptr<fs::IStorage> base_storage) {
m_shared_storage = std::move(base_storage);
return this->Initialize(m_shared_storage.get());
R_RETURN(this->Initialize(m_shared_storage.get()));
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::Initialize(std::shared_ptr<fs::IStorage> base_storage, MemoryResource *allocator) {
m_shared_storage = std::move(base_storage);
return this->Initialize(m_shared_storage.get(), allocator);
R_RETURN(this->Initialize(m_shared_storage.get(), allocator));
}
template <typename MetaType>
@@ -345,114 +347,116 @@ namespace ams::fssystem {
/* Output offset. */
*out_offset = m_meta_data_size + m_meta_data->GetEntry(entry_index)->offset;
return ResultSuccess();
R_SUCCEED();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::DoGetEntryType(fs::DirectoryEntryType *out, const char *path) {
Result PartitionFileSystemCore<MetaType>::DoGetEntryType(fs::DirectoryEntryType *out, const fs::Path &path) {
/* Validate preconditions. */
R_UNLESS(m_initialized, fs::ResultPreconditionViolation());
R_UNLESS(fs::PathNormalizer::IsSeparator(path[0]), fs::ResultInvalidPathFormat());
const char * const p = path.GetString();
R_UNLESS(p[0] == RootPath[0], fs::ResultInvalidPathFormat());
/* Check if the path is for a directory. */
if (std::strncmp(path, fs::PathNormalizer::RootPath, sizeof(fs::PathNormalizer::RootPath)) == 0) {
if (util::Strncmp(p, RootPath, sizeof(RootPath))) {
*out = fs::DirectoryEntryType_Directory;
return ResultSuccess();
R_SUCCEED();
}
/* Ensure that path is for a file. */
R_UNLESS(m_meta_data->GetEntryIndex(path + 1) >= 0, fs::ResultPathNotFound());
R_UNLESS(m_meta_data->GetEntryIndex(p + 1) >= 0, fs::ResultPathNotFound());
*out = fs::DirectoryEntryType_File;
return ResultSuccess();
R_SUCCEED();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) {
Result PartitionFileSystemCore<MetaType>::DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const fs::Path &path, fs::OpenMode mode) {
/* Validate preconditions. */
R_UNLESS(m_initialized, fs::ResultPreconditionViolation());
/* Obtain and validate the entry index. */
const s32 entry_index = m_meta_data->GetEntryIndex(path + 1);
const s32 entry_index = m_meta_data->GetEntryIndex(path.GetString() + 1);
R_UNLESS(entry_index >= 0, fs::ResultPathNotFound());
/* Create and output the file directory. */
std::unique_ptr file = std::make_unique<PartitionFile>(this, m_meta_data->GetEntry(entry_index), mode);
R_UNLESS(file != nullptr, fs::ResultAllocationFailureInPartitionFileSystemB());
*out_file = std::move(file);
return ResultSuccess();
R_SUCCEED();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) {
Result PartitionFileSystemCore<MetaType>::DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const fs::Path &path, fs::OpenDirectoryMode mode) {
/* Validate preconditions. */
R_UNLESS(m_initialized, fs::ResultPreconditionViolation());
R_UNLESS(std::strncmp(path, fs::PathNormalizer::RootPath, sizeof(fs::PathNormalizer::RootPath)) == 0, fs::ResultPathNotFound());
R_UNLESS(m_initialized, fs::ResultPreconditionViolation());
R_UNLESS(path == RootPath, fs::ResultPathNotFound());
/* Create and output the partition directory. */
std::unique_ptr directory = std::make_unique<PartitionDirectory>(this, mode);
R_UNLESS(directory != nullptr, fs::ResultAllocationFailureInPartitionFileSystemC());
*out_dir = std::move(directory);
return ResultSuccess();
R_SUCCEED();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::DoCommit() {
return ResultSuccess();
R_SUCCEED();
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::DoCleanDirectoryRecursively(const char *path) {
Result PartitionFileSystemCore<MetaType>::DoCleanDirectoryRecursively(const fs::Path &path) {
AMS_UNUSED(path);
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
R_THROW(fs::ResultUnsupportedOperationInPartitionFileSystemA());
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::DoCreateDirectory(const char *path) {
Result PartitionFileSystemCore<MetaType>::DoCreateDirectory(const fs::Path &path) {
AMS_UNUSED(path);
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
R_THROW(fs::ResultUnsupportedOperationInPartitionFileSystemA());
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::DoCreateFile(const char *path, s64 size, int option) {
Result PartitionFileSystemCore<MetaType>::DoCreateFile(const fs::Path &path, s64 size, int option) {
AMS_UNUSED(path, size, option);
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
R_THROW(fs::ResultUnsupportedOperationInPartitionFileSystemA());
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::DoDeleteDirectory(const char *path) {
Result PartitionFileSystemCore<MetaType>::DoDeleteDirectory(const fs::Path &path) {
AMS_UNUSED(path);
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
R_THROW(fs::ResultUnsupportedOperationInPartitionFileSystemA());
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::DoDeleteDirectoryRecursively(const char *path) {
Result PartitionFileSystemCore<MetaType>::DoDeleteDirectoryRecursively(const fs::Path &path) {
AMS_UNUSED(path);
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
R_THROW(fs::ResultUnsupportedOperationInPartitionFileSystemA());
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::DoDeleteFile(const char *path) {
Result PartitionFileSystemCore<MetaType>::DoDeleteFile(const fs::Path &path) {
AMS_UNUSED(path);
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
R_THROW(fs::ResultUnsupportedOperationInPartitionFileSystemA());
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::DoRenameDirectory(const char *old_path, const char *new_path) {
Result PartitionFileSystemCore<MetaType>::DoRenameDirectory(const fs::Path &old_path, const fs::Path &new_path) {
AMS_UNUSED(old_path, new_path);
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
R_THROW(fs::ResultUnsupportedOperationInPartitionFileSystemA());
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::DoRenameFile(const char *old_path, const char *new_path) {
Result PartitionFileSystemCore<MetaType>::DoRenameFile(const fs::Path &old_path, const fs::Path &new_path) {
AMS_UNUSED(old_path, new_path);
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
R_THROW(fs::ResultUnsupportedOperationInPartitionFileSystemA());
}
template <typename MetaType>
Result PartitionFileSystemCore<MetaType>::DoCommitProvisionally(s64 counter) {
AMS_UNUSED(counter);
return fs::ResultUnsupportedOperationInPartitionFileSystemB();
R_THROW(fs::ResultUnsupportedOperationInPartitionFileSystemB());
}
template class PartitionFileSystemCore<PartitionFileSystemMeta>;

View File

@@ -37,26 +37,26 @@ namespace ams::fssystem {
virtual ~RomFsFile() { /* ... */ }
public:
virtual Result DoRead(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override {
R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([=, this]() -> Result {
R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([&]() -> Result {
size_t read_size = 0;
R_TRY(this->DryRead(std::addressof(read_size), offset, size, option, fs::OpenMode_Read));
R_TRY(m_parent->GetBaseStorage()->Read(offset + m_start, buffer, read_size));
*out = read_size;
return ResultSuccess();
R_SUCCEED();
}, AMS_CURRENT_FUNCTION_NAME));
return ResultSuccess();
R_SUCCEED();
}
virtual Result DoGetSize(s64 *out) override {
*out = this->GetSize();
return ResultSuccess();
R_SUCCEED();
}
virtual Result DoFlush() override {
return ResultSuccess();
R_SUCCEED();
}
virtual Result DoWrite(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override {
@@ -66,12 +66,12 @@ namespace ams::fssystem {
R_TRY(this->DryWrite(std::addressof(needs_append), offset, size, option, fs::OpenMode_Read));
AMS_ASSERT(needs_append == false);
return fs::ResultUnsupportedOperationInRomFsFileA();
R_THROW(fs::ResultUnsupportedOperationInRomFsFileA());
}
virtual Result DoSetSize(s64 size) override {
R_TRY(this->DrySetSize(size, fs::OpenMode_Read));
return fs::ResultUnsupportedOperationInRomFsFileA();
R_THROW(fs::ResultUnsupportedOperationInRomFsFileA());
}
virtual Result DoOperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
@@ -87,14 +87,14 @@ namespace ams::fssystem {
operate_size = this->GetSize() - offset;
}
R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([=, this]() -> Result {
R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([&]() -> Result {
R_TRY(m_parent->GetBaseStorage()->OperateRange(dst, dst_size, op_id, m_start + offset, operate_size, src, src_size));
return ResultSuccess();
R_SUCCEED();
}, AMS_CURRENT_FUNCTION_NAME));
return ResultSuccess();
R_SUCCEED();
}
default:
return fs::ResultUnsupportedOperationInRomFsFileB();
R_THROW(fs::ResultUnsupportedOperationInRomFsFileB());
}
}
public:
@@ -115,21 +115,21 @@ namespace ams::fssystem {
RomFsDirectory(RomFsFileSystem *p, const FindPosition &f, fs::OpenDirectoryMode m) : m_parent(p), m_current_find(f), m_first_find(f), m_mode(m) { /* ... */ }
virtual ~RomFsDirectory() override { /* ... */ }
public:
virtual Result DoRead(s64 *out_count, fs::DirectoryEntry *out_entries, s64 max_entries) {
R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([=, this]() -> Result {
return this->ReadInternal(out_count, std::addressof(m_current_find), out_entries, max_entries);
virtual Result DoRead(s64 *out_count, fs::DirectoryEntry *out_entries, s64 max_entries) override {
R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([&]() -> Result {
R_RETURN(this->ReadInternal(out_count, std::addressof(m_current_find), out_entries, max_entries));
}, AMS_CURRENT_FUNCTION_NAME));
return ResultSuccess();
R_SUCCEED();
}
virtual Result DoGetEntryCount(s64 *out) {
virtual Result DoGetEntryCount(s64 *out) override {
FindPosition find = m_first_find;
R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([&]() -> Result {
R_TRY(this->ReadInternal(out, std::addressof(find), nullptr, 0));
return ResultSuccess();
R_SUCCEED();
}, AMS_CURRENT_FUNCTION_NAME));
return ResultSuccess();
R_SUCCEED();
}
private:
Result ReadInternal(s64 *out_count, FindPosition *find, fs::DirectoryEntry *out_entries, s64 max_entries) {
@@ -179,7 +179,7 @@ namespace ams::fssystem {
}
*out_count = i;
return ResultSuccess();
R_SUCCEED();
}
public:
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
@@ -211,11 +211,11 @@ namespace ams::fssystem {
R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([&]() -> Result {
R_TRY(storage->Read(0, std::addressof(header), sizeof(header)));
return ResultSuccess();
R_SUCCEED();
}, AMS_CURRENT_FUNCTION_NAME));
*out = CalculateRequiredWorkingMemorySize(header);
return ResultSuccess();
R_SUCCEED();
}
Result RomFsFileSystem::Initialize(fs::IStorage *base, void *work, size_t work_size, bool use_cache) {
@@ -272,93 +272,99 @@ namespace ams::fssystem {
/* Set members. */
m_entry_size = header.body_offset;
m_base_storage = base;
return ResultSuccess();
R_SUCCEED();
}
Result RomFsFileSystem::Initialize(std::shared_ptr<fs::IStorage> base, void *work, size_t work_size, bool use_cache) {
m_shared_storage = std::move(base);
return this->Initialize(m_shared_storage.get(), work, work_size, use_cache);
R_RETURN(this->Initialize(m_shared_storage.get(), work, work_size, use_cache));
}
Result RomFsFileSystem::GetFileInfo(RomFileTable::FileInfo *out, const char *path) {
R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([=, this]() -> Result {
R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([&]() -> Result {
R_TRY_CATCH(m_rom_file_table.OpenFile(out, path)) {
R_CONVERT(fs::ResultDbmNotFound, fs::ResultPathNotFound());
R_CONVERT(fs::ResultDbmNotFound, fs::ResultPathNotFound());
} R_END_TRY_CATCH;
return ResultSuccess();
R_SUCCEED();
}, AMS_CURRENT_FUNCTION_NAME));
return ResultSuccess();
R_SUCCEED();
}
Result RomFsFileSystem::GetFileBaseOffset(s64 *out, const char *path) {
Result RomFsFileSystem::GetFileBaseOffset(s64 *out, const fs::Path &path) {
R_TRY(this->CheckPathFormat(path));
RomFileTable::FileInfo info;
R_TRY(this->GetFileInfo(std::addressof(info), path));
*out = m_entry_size + info.offset.Get();
return ResultSuccess();
R_SUCCEED();
}
Result RomFsFileSystem::DoCreateFile(const char *path, s64 size, int flags) {
Result RomFsFileSystem::DoCreateFile(const fs::Path &path, s64 size, int flags) {
AMS_UNUSED(path, size, flags);
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
R_THROW(fs::ResultUnsupportedOperationInRomFsFileSystemA());
}
Result RomFsFileSystem::DoDeleteFile(const char *path) {
Result RomFsFileSystem::DoDeleteFile(const fs::Path &path) {
AMS_UNUSED(path);
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
R_THROW(fs::ResultUnsupportedOperationInRomFsFileSystemA());
}
Result RomFsFileSystem::DoCreateDirectory(const char *path) {
Result RomFsFileSystem::DoCreateDirectory(const fs::Path &path) {
AMS_UNUSED(path);
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
R_THROW(fs::ResultUnsupportedOperationInRomFsFileSystemA());
}
Result RomFsFileSystem::DoDeleteDirectory(const char *path) {
Result RomFsFileSystem::DoDeleteDirectory(const fs::Path &path) {
AMS_UNUSED(path);
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
R_THROW(fs::ResultUnsupportedOperationInRomFsFileSystemA());
}
Result RomFsFileSystem::DoDeleteDirectoryRecursively(const char *path) {
Result RomFsFileSystem::DoDeleteDirectoryRecursively(const fs::Path &path) {
AMS_UNUSED(path);
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
R_THROW(fs::ResultUnsupportedOperationInRomFsFileSystemA());
}
Result RomFsFileSystem::DoRenameFile(const char *old_path, const char *new_path) {
Result RomFsFileSystem::DoRenameFile(const fs::Path &old_path, const fs::Path &new_path) {
AMS_UNUSED(old_path, new_path);
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
R_THROW(fs::ResultUnsupportedOperationInRomFsFileSystemA());
}
Result RomFsFileSystem::DoRenameDirectory(const char *old_path, const char *new_path) {
Result RomFsFileSystem::DoRenameDirectory(const fs::Path &old_path, const fs::Path &new_path) {
AMS_UNUSED(old_path, new_path);
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
R_THROW(fs::ResultUnsupportedOperationInRomFsFileSystemA());
}
Result RomFsFileSystem::DoGetEntryType(fs::DirectoryEntryType *out, const char *path) {
R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([=, this]() -> Result {
Result RomFsFileSystem::DoGetEntryType(fs::DirectoryEntryType *out, const fs::Path &path) {
R_TRY(this->CheckPathFormat(path));
R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([&]() -> Result {
fs::RomDirectoryInfo dir_info;
R_TRY_CATCH(m_rom_file_table.GetDirectoryInformation(std::addressof(dir_info), path)) {
R_TRY_CATCH(m_rom_file_table.GetDirectoryInformation(std::addressof(dir_info), path.GetString())) {
R_CONVERT(fs::ResultDbmNotFound, fs::ResultPathNotFound())
R_CATCH(fs::ResultDbmInvalidOperation) {
RomFileTable::FileInfo file_info;
R_TRY(this->GetFileInfo(std::addressof(file_info), path));
*out = fs::DirectoryEntryType_File;
return ResultSuccess();
R_SUCCEED();
}
} R_END_TRY_CATCH;
*out = fs::DirectoryEntryType_Directory;
return ResultSuccess();
R_SUCCEED();
}, AMS_CURRENT_FUNCTION_NAME));
return ResultSuccess();
R_SUCCEED();
}
Result RomFsFileSystem::DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) {
Result RomFsFileSystem::DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const fs::Path &path, fs::OpenMode mode) {
R_UNLESS(mode == fs::OpenMode_Read, fs::ResultInvalidOpenMode());
R_TRY(this->CheckPathFormat(path));
RomFileTable::FileInfo file_info;
R_TRY(this->GetFileInfo(std::addressof(file_info), path));
@@ -366,45 +372,47 @@ namespace ams::fssystem {
R_UNLESS(file != nullptr, fs::ResultAllocationFailureInRomFsFileSystemC());
*out_file = std::move(file);
return ResultSuccess();
R_SUCCEED();
}
Result RomFsFileSystem::DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) {
Result RomFsFileSystem::DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const fs::Path &path, fs::OpenDirectoryMode mode) {
R_TRY(this->CheckPathFormat(path));
RomFileTable::FindPosition find;
R_TRY(buffers::DoContinuouslyUntilBufferIsAllocated([&]() -> Result {
R_TRY_CATCH(m_rom_file_table.FindOpen(std::addressof(find), path)) {
R_CONVERT(fs::ResultDbmNotFound, fs::ResultPathNotFound())
R_TRY_CATCH(m_rom_file_table.FindOpen(std::addressof(find), path.GetString())) {
R_CONVERT(fs::ResultDbmNotFound, fs::ResultPathNotFound())
} R_END_TRY_CATCH;
return ResultSuccess();
R_SUCCEED();
}, AMS_CURRENT_FUNCTION_NAME));
auto dir = std::make_unique<RomFsDirectory>(this, find, mode);
R_UNLESS(dir != nullptr, fs::ResultAllocationFailureInRomFsFileSystemD());
*out_dir = std::move(dir);
return ResultSuccess();
R_SUCCEED();
}
Result RomFsFileSystem::DoCommit() {
return ResultSuccess();
R_SUCCEED();
}
Result RomFsFileSystem::DoGetFreeSpaceSize(s64 *out, const char *path) {
Result RomFsFileSystem::DoGetFreeSpaceSize(s64 *out, const fs::Path &path) {
AMS_UNUSED(path);
*out = 0;
return ResultSuccess();
R_SUCCEED();
}
Result RomFsFileSystem::DoCleanDirectoryRecursively(const char *path) {
Result RomFsFileSystem::DoCleanDirectoryRecursively(const fs::Path &path) {
AMS_UNUSED(path);
return fs::ResultUnsupportedOperationInRomFsFileSystemA();
R_THROW(fs::ResultUnsupportedOperationInRomFsFileSystemA());
}
Result RomFsFileSystem::DoCommitProvisionally(s64 counter) {
AMS_UNUSED(counter);
return fs::ResultUnsupportedOperationInRomFsFileSystemB();
R_THROW(fs::ResultUnsupportedOperationInRomFsFileSystemB());
}
}

Some files were not shown because too many files have changed in this diff Show More