ams: support building unit test programs on windows/linux/macos
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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{}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
@@ -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))) {
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
46
libraries/libstratosphere/source/cfg/cfg_flags.generic.inc
Normal file
46
libraries/libstratosphere/source/cfg/cfg_flags.generic.inc
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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?");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
36
libraries/libstratosphere/source/cfg/cfg_sd_card.generic.inc
Normal file
36
libraries/libstratosphere/source/cfg/cfg_sd_card.generic.inc
Normal 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() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,10 +26,4 @@ namespace ams::diag::impl {
|
||||
}
|
||||
}
|
||||
|
||||
void PrintDebugString(const char *msg) {
|
||||
AMS_AUDIT(msg != nullptr);
|
||||
|
||||
PrintDebugString(msg, std::strlen(msg));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ namespace ams::erpt::srv {
|
||||
}
|
||||
}
|
||||
|
||||
constinit util::TypedStorage<ErrorReportServiceManager> g_erpt_server_manager;
|
||||
constinit util::TypedStorage<ErrorReportServiceManager> g_erpt_server_manager = {};
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
241
libraries/libstratosphere/source/fs/fs_host.cpp
Normal file
241
libraries/libstratosphere/source/fs/fs_host.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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{})));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
115
libraries/libstratosphere/source/fs/impl/fs_library.cpp
Normal file
115
libraries/libstratosphere/source/fs/impl/fs_library.cpp
Normal 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
|
||||
}
|
||||
|
||||
}
|
||||
23
libraries/libstratosphere/source/fs/impl/fs_library.hpp
Normal file
23
libraries/libstratosphere/source/fs/impl/fs_library.hpp
Normal 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();
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
107
libraries/libstratosphere/source/fssrv/impl/fssrv_utility.cpp
Normal file
107
libraries/libstratosphere/source/fssrv/impl/fssrv_utility.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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()));
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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
@@ -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());
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user