Merged with atmosphere 0.16.1

This commit is contained in:
rashevskyv 2020-12-11 13:53:16 +02:00
commit 20c76dcb67
121 changed files with 3616 additions and 2759 deletions

View File

@ -35,6 +35,17 @@
# mmc space, encrypted to prevent detection. This backup can be used # mmc space, encrypted to prevent detection. This backup can be used
# to prevent unrecoverable edits in emergencies. # to prevent unrecoverable edits in emergencies.
# Key: log_port, default: 0.
# Desc: Controls what uart port exosphere will set up for logging.
# NOTE: 0 = UART-A, 1 = UART-B, 2 = UART-C, 3 = UART-D
# Key: log_baud_rate, default: 115200
# Desc: Controls the baud rate exosphere will set up for logging.
# NOTE: 0 is treated as equivalent to 115200.
# Key: log_inverted, default: 0.
# Desc: Controls whether the logging uart port is inverted.
[exosphere] [exosphere]
debugmode=1 debugmode=1
debugmode_user=0 debugmode_user=0
@ -43,3 +54,6 @@ enable_user_pmu_access=0
blank_prodinfo_sysmmc=0 blank_prodinfo_sysmmc=0
blank_prodinfo_emummc=0 blank_prodinfo_emummc=0
allow_writing_to_cal_sysmmc=0 allow_writing_to_cal_sysmmc=0
log_port=0
log_baud_rate=115200
log_inverted=0

View File

@ -1,4 +1,16 @@
# Changelog # Changelog
## 0.16.1
+ Support was added for 11.0.1.
+ `mesosphère` was updated to reflect the latest official kernel behavior.
+ A new svc::InfoType added in 11.0.0 was implemented (it wasn't discovered before 0.16.0 released).
+ The new Control Flow Integrity (CFI) logic added in 11.0.0 kernel was implemented.
+ `fs` logic was refactored and cleaned up to reflect some newer sysmodule behavioral and structural changes.
+ `exosphère` was updated to allow dynamic control of what uart port is used for logging.
+ This can be controlled by editing the `log_port`, `log_baud_rate`, and `log_inverted` fields in `exosphere.ini`.
+ `mesosphère` was updated to improve debugging capabilities ().
+ This is still a work in progress, but developers may be interested.
+ A bug was fixed that caused `fatal` to fatal error if the fatal process was already being debugged.
+ Several issues were fixed, and usability and stability were improved.
## 0.16.0 ## 0.16.0
+ Support was added for 11.0.0. + Support was added for 11.0.0.
+ `exosphère` was updated to reflect the latest official secure monitor behavior. + `exosphère` was updated to reflect the latest official secure monitor behavior.

View File

@ -3,13 +3,13 @@ export SEPT_00_ENC_PATH="~/sept/sept-secondary_00.enc"
export SEPT_DEV_00_ENC_PATH="~/sept/sept-secondary_dev_00.enc" export SEPT_DEV_00_ENC_PATH="~/sept/sept-secondary_dev_00.enc"
export SEPT_DEV_01_ENC_PATH="~/sept/sept-secondary_dev_01.enc" export SEPT_DEV_01_ENC_PATH="~/sept/sept-secondary_dev_01.enc"
git remote add atmo https://github.com/Atmosphere-NX/Atmosphere.git git remote add atmo https://github.com/Atmosphere-NX/Atmosphere.git
git fetch atmo git fetch atmo
git pull atmo master git pull atmo master
git checkout tags/0.15.0 -b 0.15.0 git checkout tags/0.15.0 -b 0.15.0
git clean -xdf git clean -xdf
git remote add fspusb https://github.com/XorTroll/Atmosphere.git git remote add fspusb https://github.com/XorTroll/Atmosphere.git
git fetch fspusb git fetch fspusb

View File

@ -960,7 +960,7 @@ namespace ams::secmon {
} }
void SetupLogForBoot() { void SetupLogForBoot() {
log::Initialize(); log::Initialize(secmon::GetLogPort(), secmon::GetLogBaudRate(), secmon::GetLogFlags());
log::SendText("OHAYO\n", 6); log::SendText("OHAYO\n", 6);
log::Flush(); log::Flush();
} }

View File

@ -282,6 +282,10 @@ namespace ams::secmon::smc {
return SmcResult::NotInitialized; return SmcResult::NotInitialized;
} }
break; break;
case ConfigItem::ExosphereLogConfiguration:
/* Get the log configuration. */
args.r[1] = (static_cast<u64>(static_cast<u8>(secmon::GetLogPort())) << 32) | static_cast<u64>(secmon::GetLogBaudRate());
break;
default: default:
return SmcResult::InvalidArgument; return SmcResult::InvalidArgument;
} }

View File

@ -49,6 +49,7 @@ namespace ams::secmon::smc {
ExosphereAllowCalWrites = 65006, ExosphereAllowCalWrites = 65006,
ExosphereEmummcType = 65007, ExosphereEmummcType = 65007,
ExospherePayloadAddress = 65008, ExospherePayloadAddress = 65008,
ExosphereLogConfiguration = 65009,
}; };
SmcResult SmcGetConfigUser(SmcArguments &args); SmcResult SmcGetConfigUser(SmcArguments &args);

View File

@ -409,6 +409,7 @@ namespace ams::secmon::smc {
/* NOTE: Nintendo only does this on dev, but we will always do it. */ /* NOTE: Nintendo only does this on dev, but we will always do it. */
if (true /* !pkg1::IsProduction() */) { if (true /* !pkg1::IsProduction() */) {
log::SendText("OYASUMI\n", 8); log::SendText("OYASUMI\n", 8);
log::Flush();
} }
/* If we're on erista, configure the bootrom to allow our custom warmboot firmware. */ /* If we're on erista, configure the bootrom to allow our custom warmboot firmware. */

View File

@ -34,11 +34,17 @@
#define EXOSPHERE_FLAG_BLANK_PRODINFO (1 << 5u) #define EXOSPHERE_FLAG_BLANK_PRODINFO (1 << 5u)
#define EXOSPHERE_FLAG_ALLOW_WRITING_TO_CAL_SYSMMC (1 << 6u) #define EXOSPHERE_FLAG_ALLOW_WRITING_TO_CAL_SYSMMC (1 << 6u)
#define EXOSPHERE_LOG_FLAG_INVERTED (1 << 0u)
typedef struct { typedef struct {
uint32_t magic; uint32_t magic;
uint32_t target_firmware; uint32_t target_firmware;
uint32_t flags; uint32_t flags[2];
uint32_t reserved[5]; uint16_t lcd_vendor;
uint8_t log_port;
uint8_t log_flags;
uint32_t log_baud_rate;
uint32_t reserved1[2];
exo_emummc_config_t emummc_cfg; exo_emummc_config_t emummc_cfg;
} exosphere_config_t; } exosphere_config_t;
@ -54,6 +60,9 @@ _Static_assert(sizeof(exosphere_config_t) == 0x20 + sizeof(exo_emummc_config_t),
#define EXOSPHERE_BLANK_PRODINFO_SYSMMC_KEY "blank_prodinfo_sysmmc" #define EXOSPHERE_BLANK_PRODINFO_SYSMMC_KEY "blank_prodinfo_sysmmc"
#define EXOSPHERE_BLANK_PRODINFO_EMUMMC_KEY "blank_prodinfo_emummc" #define EXOSPHERE_BLANK_PRODINFO_EMUMMC_KEY "blank_prodinfo_emummc"
#define EXOSPHERE_ALLOW_WRITING_TO_CAL_SYSMMC_KEY "allow_writing_to_cal_sysmmc" #define EXOSPHERE_ALLOW_WRITING_TO_CAL_SYSMMC_KEY "allow_writing_to_cal_sysmmc"
#define EXOSPHERE_LOG_PORT_KEY "log_port"
#define EXOSPHERE_LOG_BAUD_RATE_KEY "log_baud_rate"
#define EXOSPHERE_LOG_INVERTED_KEY "log_inverted"
typedef struct { typedef struct {
int debugmode; int debugmode;
@ -63,6 +72,9 @@ typedef struct {
int blank_prodinfo_sysmmc; int blank_prodinfo_sysmmc;
int blank_prodinfo_emummc; int blank_prodinfo_emummc;
int allow_writing_to_cal_sysmmc; int allow_writing_to_cal_sysmmc;
int log_port;
int log_baud_rate;
int log_inverted;
} exosphere_parse_cfg_t; } exosphere_parse_cfg_t;
#endif #endif

View File

@ -906,6 +906,35 @@ static const kernel_patch_t g_kernel_patches_1100[] = {
} }
}; };
static const kernel_patch_t g_kernel_patches_1101[] = {
{ /* Send Message Process ID Patch. */
.pattern_size = 0x1C,
.pattern = MAKE_KERNEL_PATTERN_NAME(1100, proc_id_send),
.pattern_hook_offset = 0x0,
.payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(1100, proc_id_send))/sizeof(instruction_t),
.branch_back_offset = 0x10,
.payload = MAKE_KERNEL_PATCH_NAME(1100, proc_id_send)
},
{ /* Receive Message Process ID Patch. */
.pattern_size = 0x1C,
.pattern = MAKE_KERNEL_PATTERN_NAME(1100, proc_id_recv),
.pattern_hook_offset = 0x0,
.payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(1100, proc_id_recv))/sizeof(instruction_t),
.branch_back_offset = 0x10,
.payload = MAKE_KERNEL_PATCH_NAME(1100, proc_id_recv)
},
{ /* svcControlCodeMemory Patch. */
.payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(1100, svc_control_codememory))/sizeof(instruction_t),
.payload = MAKE_KERNEL_PATCH_NAME(1100, svc_control_codememory),
.patch_offset = 0x2FD04,
},
{ /* System Memory Increase Patch. */
.payload_num_instructions = sizeof(MAKE_KERNEL_PATCH_NAME(1100, system_memory_increase))/sizeof(instruction_t),
.payload = MAKE_KERNEL_PATCH_NAME(1100, system_memory_increase),
.patch_offset = 0x490C4,
}
};
#define KERNEL_PATCHES(vers) .num_patches = sizeof(g_kernel_patches_##vers)/sizeof(kernel_patch_t), .patches = g_kernel_patches_##vers, #define KERNEL_PATCHES(vers) .num_patches = sizeof(g_kernel_patches_##vers)/sizeof(kernel_patch_t), .patches = g_kernel_patches_##vers,
/* Kernel Infos. */ /* Kernel Infos. */
@ -1000,6 +1029,15 @@ static const kernel_info_t g_kernel_infos[] = {
.embedded_ini_ptr = 0x180, .embedded_ini_ptr = 0x180,
.free_code_space_offset = 0x49EE8, .free_code_space_offset = 0x49EE8,
KERNEL_PATCHES(1100) KERNEL_PATCHES(1100)
},
{ /* 11.0.1. */
.hash = {0x68, 0xB9, 0x72, 0xB7, 0x97, 0x55, 0x87, 0x5E, 0x24, 0x95, 0x8D, 0x99, 0x0A, 0x77, 0xAB, 0xF1, 0xC5, 0xC1, 0x32, 0x80, 0x67, 0xF0, 0xA2, 0xEC, 0x9C, 0xEF, 0xC3, 0x22, 0xE3, 0x42, 0xC0, 0x4D, },
.hash_offset = 0x1C4,
.hash_size = 0x69000 - 0x1C4,
.embedded_ini_offset = 0x69000,
.embedded_ini_ptr = 0x180,
.free_code_space_offset = 0x49EE8,
KERNEL_PATCHES(1101)
} }
}; };

View File

@ -196,6 +196,27 @@ static int exosphere_ini_handler(void *user, const char *section, const char *na
} else if (tmp == 0) { } else if (tmp == 0) {
parse_cfg->allow_writing_to_cal_sysmmc = 0; parse_cfg->allow_writing_to_cal_sysmmc = 0;
} }
} else if (strcmp(name, EXOSPHERE_LOG_PORT_KEY) == 0) {
sscanf(value, "%d", &tmp);
if (0 <= tmp && tmp < 4) {
parse_cfg->log_port = tmp;
} else {
parse_cfg->log_port = 0;
}
} else if (strcmp(name, EXOSPHERE_LOG_BAUD_RATE_KEY) == 0) {
sscanf(value, "%d", &tmp);
if (tmp > 0) {
parse_cfg->log_baud_rate = tmp;
} else {
parse_cfg->log_baud_rate = 115200;
}
} else if (strcmp(name, EXOSPHERE_LOG_INVERTED_KEY) == 0) {
sscanf(value, "%d", &tmp);
if (tmp == 1) {
parse_cfg->log_inverted = 1;
} else if (tmp == 0) {
parse_cfg->log_inverted = 0;
}
} else { } else {
return 0; return 0;
} }
@ -240,6 +261,7 @@ static uint32_t nxboot_get_specific_target_firmware(uint32_t target_firmware){
#define CHECK_NCA(NCA_ID, VERSION) do { if (is_nca_present(NCA_ID)) { return ATMOSPHERE_TARGET_FIRMWARE_##VERSION; } } while(0) #define CHECK_NCA(NCA_ID, VERSION) do { if (is_nca_present(NCA_ID)) { return ATMOSPHERE_TARGET_FIRMWARE_##VERSION; } } while(0)
if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_11_0_0) { if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_11_0_0) {
CHECK_NCA("56211c7a5ed20a5332f5cdda67121e37", 11_0_1);
CHECK_NCA("594c90bcdbcccad6b062eadba0cd0e7e", 11_0_0); CHECK_NCA("594c90bcdbcccad6b062eadba0cd0e7e", 11_0_0);
} else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_10_0_0) { } else if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_10_0_0) {
CHECK_NCA("26325de4db3909e0ef2379787c7e671d", 10_2_0); CHECK_NCA("26325de4db3909e0ef2379787c7e671d", 10_2_0);
@ -464,9 +486,9 @@ static void nxboot_configure_exosphere(uint32_t target_firmware, unsigned int ke
const bool is_emummc = exo_emummc_cfg->base_cfg.magic == MAGIC_EMUMMC_CONFIG && exo_emummc_cfg->base_cfg.type != EMUMMC_TYPE_NONE; const bool is_emummc = exo_emummc_cfg->base_cfg.magic == MAGIC_EMUMMC_CONFIG && exo_emummc_cfg->base_cfg.type != EMUMMC_TYPE_NONE;
if (keygen_type) { if (keygen_type) {
exo_cfg.flags = EXOSPHERE_FLAG_PERFORM_620_KEYGEN; exo_cfg.flags[0] = EXOSPHERE_FLAG_PERFORM_620_KEYGEN;
} else { } else {
exo_cfg.flags = 0; exo_cfg.flags[0] = 0;
} }
/* Setup exosphere parse configuration with defaults. */ /* Setup exosphere parse configuration with defaults. */
@ -478,6 +500,9 @@ static void nxboot_configure_exosphere(uint32_t target_firmware, unsigned int ke
.blank_prodinfo_sysmmc = 0, .blank_prodinfo_sysmmc = 0,
.blank_prodinfo_emummc = 0, .blank_prodinfo_emummc = 0,
.allow_writing_to_cal_sysmmc = 0, .allow_writing_to_cal_sysmmc = 0,
.log_port = 0,
.log_baud_rate = 115200,
.log_inverted = 0,
}; };
/* If we have an ini to read, parse it. */ /* If we have an ini to read, parse it. */
@ -490,13 +515,17 @@ static void nxboot_configure_exosphere(uint32_t target_firmware, unsigned int ke
free(exosphere_ini); free(exosphere_ini);
/* Apply parse config. */ /* Apply parse config. */
if (parse_cfg.debugmode) exo_cfg.flags |= EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV; if (parse_cfg.debugmode) exo_cfg.flags[0] |= EXOSPHERE_FLAG_IS_DEBUGMODE_PRIV;
if (parse_cfg.debugmode_user) exo_cfg.flags |= EXOSPHERE_FLAG_IS_DEBUGMODE_USER; if (parse_cfg.debugmode_user) exo_cfg.flags[0] |= EXOSPHERE_FLAG_IS_DEBUGMODE_USER;
if (parse_cfg.disable_user_exception_handlers) exo_cfg.flags |= EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS; if (parse_cfg.disable_user_exception_handlers) exo_cfg.flags[0] |= EXOSPHERE_FLAG_DISABLE_USERMODE_EXCEPTION_HANDLERS;
if (parse_cfg.enable_user_pmu_access) exo_cfg.flags |= EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS; if (parse_cfg.enable_user_pmu_access) exo_cfg.flags[0] |= EXOSPHERE_FLAG_ENABLE_USERMODE_PMU_ACCESS;
if (parse_cfg.blank_prodinfo_sysmmc && !is_emummc) exo_cfg.flags |= EXOSPHERE_FLAG_BLANK_PRODINFO; if (parse_cfg.blank_prodinfo_sysmmc && !is_emummc) exo_cfg.flags[0] |= EXOSPHERE_FLAG_BLANK_PRODINFO;
if (parse_cfg.blank_prodinfo_emummc && is_emummc) exo_cfg.flags |= EXOSPHERE_FLAG_BLANK_PRODINFO; if (parse_cfg.blank_prodinfo_emummc && is_emummc) exo_cfg.flags[0] |= EXOSPHERE_FLAG_BLANK_PRODINFO;
if (parse_cfg.allow_writing_to_cal_sysmmc) exo_cfg.flags |= EXOSPHERE_FLAG_ALLOW_WRITING_TO_CAL_SYSMMC; if (parse_cfg.allow_writing_to_cal_sysmmc) exo_cfg.flags[0] |= EXOSPHERE_FLAG_ALLOW_WRITING_TO_CAL_SYSMMC;
exo_cfg.log_port = parse_cfg.log_port;
exo_cfg.log_baud_rate = parse_cfg.log_baud_rate;
if (parse_cfg.log_inverted) exo_cfg.log_flags |= EXOSPHERE_LOG_FLAG_INVERTED;
if ((exo_cfg.target_firmware < ATMOSPHERE_TARGET_FIRMWARE_MIN) || (exo_cfg.target_firmware > ATMOSPHERE_TARGET_FIRMWARE_MAX)) { if ((exo_cfg.target_firmware < ATMOSPHERE_TARGET_FIRMWARE_MIN) || (exo_cfg.target_firmware > ATMOSPHERE_TARGET_FIRMWARE_MAX)) {
fatal_error("[NXBOOT] Invalid Exosphere target firmware!\n"); fatal_error("[NXBOOT] Invalid Exosphere target firmware!\n");

View File

@ -6,7 +6,7 @@
[subrepo] [subrepo]
remote = https://github.com/Atmosphere-NX/Atmosphere-libs remote = https://github.com/Atmosphere-NX/Atmosphere-libs
branch = master branch = master
commit = 2c3ccef17e9b267a5d9d232f1aba689f2c591b95 commit = 5a18bea64545105c52d642d7789029b5ca875864
parent = d2f48d5e36cb2ba4e8cc014238457bd75df81797 parent = 17c8c390fc84d059b89f563a8fae6936649d0d45
method = merge method = merge
cmdver = 0.4.1 cmdver = 0.4.1

View File

@ -35,6 +35,7 @@ namespace ams::log {
#endif #endif
void Initialize(); void Initialize();
void Initialize(uart::Port port, u32 baud_rate, u32 flags);
void Finalize(); void Finalize();
void Printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); void Printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));

View File

@ -116,6 +116,18 @@ namespace ams::secmon {
return GetSecmonConfiguration().GetLcdVendor(); return GetSecmonConfiguration().GetLcdVendor();
} }
ALWAYS_INLINE uart::Port GetLogPort() {
return GetSecmonConfiguration().GetLogPort();
}
ALWAYS_INLINE u8 GetLogFlags() {
return GetSecmonConfiguration().GetLogFlags();
}
ALWAYS_INLINE u32 GetLogBaudRate() {
return GetSecmonConfiguration().GetLogBaudRate();
}
ALWAYS_INLINE bool IsProduction() { ALWAYS_INLINE bool IsProduction() {
return GetSecmonConfiguration().IsProduction(); return GetSecmonConfiguration().IsProduction();
} }

View File

@ -16,6 +16,7 @@
#pragma once #pragma once
#include <vapours.hpp> #include <vapours.hpp>
#include <exosphere/fuse.hpp> #include <exosphere/fuse.hpp>
#include <exosphere/uart.hpp>
#include <exosphere/secmon/secmon_emummc_context.hpp> #include <exosphere/secmon/secmon_emummc_context.hpp>
namespace ams::secmon { namespace ams::secmon {
@ -39,8 +40,10 @@ namespace ams::secmon {
ams::TargetFirmware target_firmware; ams::TargetFirmware target_firmware;
u32 flags[2]; u32 flags[2];
u16 lcd_vendor; u16 lcd_vendor;
u16 reserved0; u8 log_port;
u32 reserved1[3]; u8 log_flags;
u32 log_baud_rate;
u32 reserved1[2];
EmummcConfiguration emummc_cfg; EmummcConfiguration emummc_cfg;
constexpr bool IsValid() const { return this->magic == Magic; } constexpr bool IsValid() const { return this->magic == Magic; }
@ -54,17 +57,22 @@ namespace ams::secmon {
u8 hardware_type; u8 hardware_type;
u8 soc_type; u8 soc_type;
u8 hardware_state; u8 hardware_state;
u8 pad_0B[1]; u8 log_port;
u32 flags[2]; u32 flags[2];
u16 lcd_vendor; u16 lcd_vendor;
u16 reserved0; u8 log_flags;
u32 reserved1[(0x80 - 0x18) / sizeof(u32)]; u8 reserved0;
u32 log_baud_rate;
u32 reserved1[(0x80 - 0x1C) / sizeof(u32)];
constexpr void CopyFrom(const SecureMonitorStorageConfiguration &storage) { constexpr void CopyFrom(const SecureMonitorStorageConfiguration &storage) {
this->target_firmware = storage.target_firmware; this->target_firmware = storage.target_firmware;
this->flags[0] = storage.flags[0]; this->flags[0] = storage.flags[0];
this->flags[1] = storage.flags[1]; this->flags[1] = storage.flags[1];
this->lcd_vendor = storage.lcd_vendor; this->lcd_vendor = storage.lcd_vendor;
this->log_port = storage.log_port;
this->log_flags = storage.log_flags;
this->log_baud_rate = storage.log_baud_rate != 0 ? storage.log_baud_rate : 115200;
} }
void SetFuseInfo() { void SetFuseInfo() {
@ -78,9 +86,13 @@ namespace ams::secmon {
constexpr fuse::HardwareType GetHardwareType() const { return static_cast<fuse::HardwareType>(this->hardware_type); } constexpr fuse::HardwareType GetHardwareType() const { return static_cast<fuse::HardwareType>(this->hardware_type); }
constexpr fuse::SocType GetSocType() const { return static_cast<fuse::SocType>(this->soc_type); } constexpr fuse::SocType GetSocType() const { return static_cast<fuse::SocType>(this->soc_type); }
constexpr fuse::HardwareState GetHardwareState() const { return static_cast<fuse::HardwareState>(this->hardware_state); } constexpr fuse::HardwareState GetHardwareState() const { return static_cast<fuse::HardwareState>(this->hardware_state); }
constexpr uart::Port GetLogPort() const { return static_cast<uart::Port>(this->log_port); }
constexpr u8 GetLogFlags() const { return this->log_flags; }
constexpr u16 GetLcdVendor() const { return this->lcd_vendor; } constexpr u16 GetLcdVendor() const { return this->lcd_vendor; }
constexpr u32 GetLogBaudRate() const { return this->log_baud_rate; }
constexpr bool IsProduction() const { return this->GetHardwareState() != fuse::HardwareState_Development; } constexpr bool IsProduction() const { return this->GetHardwareState() != fuse::HardwareState_Development; }
constexpr bool IsDevelopmentFunctionEnabledForKernel() const { return (this->flags[0] & SecureMonitorConfigurationFlag_IsDevelopmentFunctionEnabledForKernel) != 0; } constexpr bool IsDevelopmentFunctionEnabledForKernel() const { return (this->flags[0] & SecureMonitorConfigurationFlag_IsDevelopmentFunctionEnabledForKernel) != 0; }
@ -101,10 +113,12 @@ namespace ams::secmon {
.hardware_type = {}, .hardware_type = {},
.soc_type = {}, .soc_type = {},
.hardware_state = {}, .hardware_state = {},
.pad_0B = {}, .log_port = uart::Port_ReservedDebug,
.flags = { SecureMonitorConfigurationFlag_Default, SecureMonitorConfigurationFlag_None }, .flags = { SecureMonitorConfigurationFlag_Default, SecureMonitorConfigurationFlag_None },
.lcd_vendor = {}, .lcd_vendor = {},
.log_flags = {},
.reserved0 = {}, .reserved0 = {},
.log_baud_rate = 115200,
.reserved1 = {}, .reserved1 = {},
}; };

View File

@ -19,58 +19,46 @@ namespace ams::log {
namespace { namespace {
constexpr inline uart::Port UartLogPort = uart::Port_ReservedDebug; constexpr inline uart::Port DefaultLogPort = uart::Port_ReservedDebug;
constexpr inline int UartBaudRate = 115200; constexpr inline u32 DefaultLogFlags = static_cast<u32>(uart::Flag_None);
constexpr inline int DefaultBaudRate = 115200;
constinit uart::Port g_log_port = DefaultLogPort;
constinit bool g_initialized_uart = false; constinit bool g_initialized_uart = false;
constexpr inline u32 UartPortFlags = [] { ALWAYS_INLINE void SetupUartClock(uart::Port port) {
if constexpr (UartLogPort == uart::Port_ReservedDebug) { /* The debug port must always be set up, for compatibility with official hos. */
/* Logging to the debug port. */
/* Don't invert transactions. */
return uart::Flag_None;
} else if constexpr (UartLogPort == uart::Port_LeftJoyCon) {
/* Logging to left joy-con (e.g. with Joyless). */
/* Invert transactions. */
return uart::Flag_Inverted;
} else if constexpr (UartLogPort == uart::Port_RightJoyCon) {
/* Logging to right joy-con (e.g. with Joyless). */
/* Invert transactions. */
return uart::Flag_Inverted;
} else {
__builtin_unreachable();
}
}();
ALWAYS_INLINE void SetupUart() {
if constexpr (UartLogPort == uart::Port_ReservedDebug) {
/* Logging to the debug port. */
pinmux::SetupUartA(); pinmux::SetupUartA();
clkrst::EnableUartAClock(); clkrst::EnableUartAClock();
} else if constexpr (UartLogPort == uart::Port_LeftJoyCon) {
/* If logging to a joy-con port, configure appropriately. */
if (port == uart::Port_LeftJoyCon) {
/* Logging to left joy-con (e.g. with Joyless). */ /* Logging to left joy-con (e.g. with Joyless). */
static_assert(uart::Port_LeftJoyCon == uart::Port_C); static_assert(uart::Port_LeftJoyCon == uart::Port_C);
pinmux::SetupUartC(); pinmux::SetupUartC();
clkrst::EnableUartCClock(); clkrst::EnableUartCClock();
} else if constexpr (UartLogPort == uart::Port_RightJoyCon) { } else if (port == uart::Port_RightJoyCon) {
/* Logging to right joy-con (e.g. with Joyless). */ /* Logging to right joy-con (e.g. with Joyless). */
static_assert(uart::Port_RightJoyCon == uart::Port_B); static_assert(uart::Port_RightJoyCon == uart::Port_B);
pinmux::SetupUartB(); pinmux::SetupUartB();
clkrst::EnableUartBClock(); clkrst::EnableUartBClock();
} else {
__builtin_unreachable();
} }
} }
} }
void Initialize() { void Initialize() {
return Initialize(DefaultLogPort, DefaultBaudRate, DefaultLogFlags);
}
void Initialize(uart::Port port, u32 baud_rate, u32 flags) {
/* Initialize pinmux and clock for the target uart port. */ /* Initialize pinmux and clock for the target uart port. */
SetupUart(); SetupUartClock(port);
/* Initialize the target uart port. */ /* Initialize the target uart port. */
uart::Initialize(UartLogPort, UartBaudRate, UartPortFlags); uart::Initialize(port, baud_rate, flags);
/* Note that we've initialized. */ /* Note that we've initialized. */
g_log_port = port;
g_initialized_uart = true; g_initialized_uart = true;
} }
@ -84,7 +72,7 @@ namespace ams::log {
const auto len = util::TVSNPrintf(log_buf, sizeof(log_buf), fmt, vl); const auto len = util::TVSNPrintf(log_buf, sizeof(log_buf), fmt, vl);
if (g_initialized_uart) { if (g_initialized_uart) {
uart::SendText(UartLogPort, log_buf, len); uart::SendText(g_log_port, log_buf, len);
} }
} }
@ -115,13 +103,13 @@ namespace ams::log {
void SendText(const void *text, size_t size) { void SendText(const void *text, size_t size) {
if (g_initialized_uart) { if (g_initialized_uart) {
uart::SendText(UartLogPort, text, size); uart::SendText(g_log_port, text, size);
} }
} }
void Flush() { void Flush() {
if (g_initialized_uart) { if (g_initialized_uart) {
uart::WaitFlush(UartLogPort); uart::WaitFlush(g_log_port);
} }
} }

View File

@ -54,6 +54,7 @@
#include <mesosphere/kern_kernel.hpp> #include <mesosphere/kern_kernel.hpp>
#include <mesosphere/kern_k_page_table_manager.hpp> #include <mesosphere/kern_k_page_table_manager.hpp>
#include <mesosphere/kern_select_page_table.hpp> #include <mesosphere/kern_select_page_table.hpp>
#include <mesosphere/kern_k_dump_object.hpp>
/* Miscellaneous objects. */ /* Miscellaneous objects. */
#include <mesosphere/kern_k_shared_memory_info.hpp> #include <mesosphere/kern_k_shared_memory_info.hpp>

View File

@ -44,6 +44,9 @@ namespace ams::kern::arch::arm64 {
static uintptr_t GetProgramCounter(const KThread &thread); static uintptr_t GetProgramCounter(const KThread &thread);
static void SetPreviousProgramCounter(); static void SetPreviousProgramCounter();
static void PrintRegister(KThread *thread = nullptr);
static void PrintBacktrace(KThread *thread = nullptr);
static Result BreakIfAttached(ams::svc::BreakReason break_reason, uintptr_t address, size_t size); static Result BreakIfAttached(ams::svc::BreakReason break_reason, uintptr_t address, size_t size);
static Result SetHardwareBreakPoint(ams::svc::HardwareBreakPointRegisterName name, u64 flags, u64 value); static Result SetHardwareBreakPoint(ams::svc::HardwareBreakPointRegisterName name, u64 flags, u64 value);
@ -61,8 +64,6 @@ namespace ams::kern::arch::arm64 {
} }
} }
} }
/* TODO: This is a placeholder definition. */
}; };
} }

View File

@ -112,6 +112,7 @@ namespace ams::kern::arch::arm64 {
L1PageTableEntry *Finalize(); L1PageTableEntry *Finalize();
void Dump(uintptr_t start, size_t size) const; void Dump(uintptr_t start, size_t size) const;
size_t CountPageTables() const;
bool BeginTraversal(TraversalEntry *out_entry, TraversalContext *out_context, KProcessAddress address) const; bool BeginTraversal(TraversalEntry *out_entry, TraversalContext *out_context, KProcessAddress address) const;
bool ContinueTraversal(TraversalEntry *out_entry, TraversalContext *context) const; bool ContinueTraversal(TraversalEntry *out_entry, TraversalContext *context) const;

View File

@ -232,14 +232,18 @@ namespace ams::kern::arch::arm64 {
return this->page_table.UnmapPhysicalMemoryUnsafe(address, size); return this->page_table.UnmapPhysicalMemoryUnsafe(address, size);
} }
void DumpTable() const {
return this->page_table.DumpTable();
}
void DumpMemoryBlocks() const { void DumpMemoryBlocks() const {
return this->page_table.DumpMemoryBlocks(); return this->page_table.DumpMemoryBlocks();
} }
void DumpPageTable() const {
return this->page_table.DumpPageTable();
}
size_t CountPageTables() const {
return this->page_table.CountPageTables();
}
bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress address) const { bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress address) const {
return this->page_table.GetPhysicalAddress(out, address); return this->page_table.GetPhysicalAddress(out, address);
} }
@ -267,12 +271,22 @@ namespace ams::kern::arch::arm64 {
size_t GetNormalMemorySize() const { return this->page_table.GetNormalMemorySize(); } size_t GetNormalMemorySize() const { return this->page_table.GetNormalMemorySize(); }
size_t GetCodeSize() const { return this->page_table.GetCodeSize(); }
size_t GetCodeDataSize() const { return this->page_table.GetCodeDataSize(); }
size_t GetAliasCodeSize() const { return this->page_table.GetAliasCodeSize(); }
size_t GetAliasCodeDataSize() const { return this->page_table.GetAliasCodeDataSize(); }
u32 GetAllocateOption() const { return this->page_table.GetAllocateOption(); } u32 GetAllocateOption() const { return this->page_table.GetAllocateOption(); }
KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress address) const { KPhysicalAddress GetHeapPhysicalAddress(KVirtualAddress address) const {
return this->page_table.GetHeapPhysicalAddress(address); return this->page_table.GetHeapPhysicalAddress(address);
} }
KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress address) const {
return this->page_table.GetHeapVirtualAddress(address);
}
KBlockInfoManager *GetBlockInfoManager() { KBlockInfoManager *GetBlockInfoManager() {
return this->page_table.GetBlockInfoManager(); return this->page_table.GetBlockInfoManager();
} }

View File

@ -62,6 +62,18 @@ namespace ams::kern::arch::arm64 {
} }
constexpr u64 GetIdentityMapTtbr0(s32 core_id) const { return this->ttbr0_identity[core_id]; } constexpr u64 GetIdentityMapTtbr0(s32 core_id) const { return this->ttbr0_identity[core_id]; }
void DumpMemoryBlocks() const {
return this->page_table.DumpMemoryBlocks();
}
void DumpPageTable() const {
return this->page_table.DumpPageTable();
}
size_t CountPageTables() const {
return this->page_table.CountPageTables();
}
}; };
} }

View File

@ -30,6 +30,7 @@ namespace ams::kern::board::nintendo::nx {
static size_t GetApplicationPoolSize(); static size_t GetApplicationPoolSize();
static size_t GetAppletPoolSize(); static size_t GetAppletPoolSize();
static size_t GetMinimumNonSecureSystemPoolSize(); static size_t GetMinimumNonSecureSystemPoolSize();
static u8 GetDebugLogUartPort();
/* Randomness. */ /* Randomness. */
static void GenerateRandomBytes(void *dst, size_t size); static void GenerateRandomBytes(void *dst, size_t size);

View File

@ -26,6 +26,7 @@
#ifdef MESOSPHERE_BUILD_FOR_DEBUGGING #ifdef MESOSPHERE_BUILD_FOR_DEBUGGING
#define MESOSPHERE_ENABLE_ASSERTIONS #define MESOSPHERE_ENABLE_ASSERTIONS
#define MESOSPHERE_ENABLE_DEBUG_PRINT #define MESOSPHERE_ENABLE_DEBUG_PRINT
#define MESOSPHERE_ENABLE_KERNEL_STACK_USAGE
#endif #endif
//#define MESOSPHERE_BUILD_FOR_TRACING //#define MESOSPHERE_BUILD_FOR_TRACING

View File

@ -40,7 +40,7 @@ namespace ams::kern {
#ifndef MESOSPHERE_DEBUG_LOG_SELECTED #ifndef MESOSPHERE_DEBUG_LOG_SELECTED
#ifdef ATMOSPHERE_BOARD_NINTENDO_NX #ifdef ATMOSPHERE_BOARD_NINTENDO_NX
#define MESOSPHERE_DEBUG_LOG_USE_UART_A #define MESOSPHERE_DEBUG_LOG_USE_UART
#else #else
#error "Unknown board for Default Debug Log Source" #error "Unknown board for Default Debug Log Source"
#endif #endif

View File

@ -42,6 +42,10 @@ namespace ams::kern {
constexpr const KPort *GetParent() const { return this->parent; } constexpr const KPort *GetParent() const { return this->parent; }
ALWAYS_INLINE s32 GetNumSessions() const { return this->num_sessions; }
ALWAYS_INLINE s32 GetPeakSessions() const { return this->peak_sessions; }
ALWAYS_INLINE s32 GetMaxSessions() const { return this->max_sessions; }
bool IsLight() const; bool IsLight() const;
/* Overridden virtual functions. */ /* Overridden virtual functions. */

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2018-2020 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 <mesosphere/kern_common.hpp>
#include <mesosphere/kern_select_cpu.hpp>
namespace ams::kern::KDumpObject {
void DumpThread();
void DumpThread(u64 thread_id);
void DumpThreadCallStack();
void DumpThreadCallStack(u64 thread_id);
void DumpKernelObject();
void DumpHandle();
void DumpHandle(u64 process_id);
void DumpKernelMemory();
void DumpMemory();
void DumpMemory(u64 process_id);
void DumpProcess();
void DumpProcess(u64 process_id);
void DumpPort();
void DumpPort(u64 process_id);
}

View File

@ -90,6 +90,8 @@ namespace ams::kern {
size_t GetFreeSize() const { return this->heap.GetFreeSize(); } size_t GetFreeSize() const { return this->heap.GetFreeSize(); }
void DumpFreeList() const { return this->heap.DumpFreeList(); }
constexpr size_t GetPageOffset(KVirtualAddress address) const { return this->heap.GetPageOffset(address); } constexpr size_t GetPageOffset(KVirtualAddress address) const { return this->heap.GetPageOffset(address); }
constexpr size_t GetPageOffsetToEnd(KVirtualAddress address) const { return this->heap.GetPageOffsetToEnd(address); } constexpr size_t GetPageOffsetToEnd(KVirtualAddress address) const { return this->heap.GetPageOffsetToEnd(address); }
@ -247,12 +249,15 @@ namespace ams::kern {
size_t GetFreeSize() { size_t GetFreeSize() {
size_t total = 0; size_t total = 0;
for (size_t i = 0; i < this->num_managers; i++) { for (size_t i = 0; i < this->num_managers; i++) {
KScopedLightLock lk(this->pool_locks[this->managers[i].GetPool()]);
total += this->managers[i].GetFreeSize(); total += this->managers[i].GetFreeSize();
} }
return total; return total;
} }
size_t GetFreeSize(Pool pool) { size_t GetFreeSize(Pool pool) {
KScopedLightLock lk(this->pool_locks[pool]);
constexpr Direction GetSizeDirection = Direction_FromFront; constexpr Direction GetSizeDirection = Direction_FromFront;
size_t total = 0; size_t total = 0;
for (auto *manager = this->GetFirstManager(pool, GetSizeDirection); manager != nullptr; manager = this->GetNextManager(manager, GetSizeDirection)) { for (auto *manager = this->GetFirstManager(pool, GetSizeDirection); manager != nullptr; manager = this->GetNextManager(manager, GetSizeDirection)) {
@ -260,6 +265,15 @@ namespace ams::kern {
} }
return total; return total;
} }
void DumpFreeList(Pool pool) {
KScopedLightLock lk(this->pool_locks[pool]);
constexpr Direction DumpDirection = Direction_FromFront;
for (auto *manager = this->GetFirstManager(pool, DumpDirection); manager != nullptr; manager = this->GetNextManager(manager, DumpDirection)) {
manager->DumpFreeList();
}
}
public: public:
static size_t CalculateManagementOverheadSize(size_t region_size) { static size_t CalculateManagementOverheadSize(size_t region_size) {
return Impl::CalculateManagementOverheadSize(region_size); return Impl::CalculateManagementOverheadSize(region_size);

View File

@ -147,6 +147,7 @@ namespace ams::kern {
} }
size_t GetFreeSize() const { return this->GetNumFreePages() * PageSize; } size_t GetFreeSize() const { return this->GetNumFreePages() * PageSize; }
void DumpFreeList() const;
void UpdateUsedSize() { void UpdateUsedSize() {
this->used_size = this->heap_size - (this->GetNumFreePages() * PageSize); this->used_size = this->heap_size - (this->GetNumFreePages() * PageSize);

View File

@ -301,6 +301,8 @@ namespace ams::kern {
Result SetupForIpcClient(PageLinkedList *page_list, size_t *out_blocks_needed, KProcessAddress address, size_t size, KMemoryPermission test_perm, KMemoryState dst_state); Result SetupForIpcClient(PageLinkedList *page_list, size_t *out_blocks_needed, KProcessAddress address, size_t size, KMemoryPermission test_perm, KMemoryState dst_state);
Result SetupForIpcServer(KProcessAddress *out_addr, size_t size, KProcessAddress src_addr, KMemoryPermission test_perm, KMemoryState dst_state, KPageTableBase &src_page_table, bool send); Result SetupForIpcServer(KProcessAddress *out_addr, size_t size, KProcessAddress src_addr, KMemoryPermission test_perm, KMemoryState dst_state, KPageTableBase &src_page_table, bool send);
void CleanupForIpcClientOnServerSetupFailure(PageLinkedList *page_list, KProcessAddress address, size_t size, KMemoryPermission prot_perm); void CleanupForIpcClientOnServerSetupFailure(PageLinkedList *page_list, KProcessAddress address, size_t size, KMemoryPermission prot_perm);
size_t GetSize(KMemoryState state) const;
public: public:
bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress virt_addr) const { bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress virt_addr) const {
return this->GetImpl().GetPhysicalAddress(out, virt_addr); return this->GetImpl().GetPhysicalAddress(out, virt_addr);
@ -382,9 +384,9 @@ namespace ams::kern {
Result MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size); Result MapPhysicalMemoryUnsafe(KProcessAddress address, size_t size);
Result UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size); Result UnmapPhysicalMemoryUnsafe(KProcessAddress address, size_t size);
void DumpTable() const { void DumpMemoryBlocksLocked() const {
KScopedLightLock lk(this->general_lock); MESOSPHERE_ASSERT(this->IsLockedByCurrentThread());
this->GetImpl().Dump(GetInteger(this->address_space_start), this->address_space_end - this->address_space_start); this->memory_block_manager.DumpBlocks();
} }
void DumpMemoryBlocks() const { void DumpMemoryBlocks() const {
@ -392,9 +394,14 @@ namespace ams::kern {
this->DumpMemoryBlocksLocked(); this->DumpMemoryBlocksLocked();
} }
void DumpMemoryBlocksLocked() const { void DumpPageTable() const {
MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); KScopedLightLock lk(this->general_lock);
this->memory_block_manager.DumpBlocks(); this->GetImpl().Dump(GetInteger(this->address_space_start), this->address_space_end - this->address_space_start);
}
size_t CountPageTables() const {
KScopedLightLock lk(this->general_lock);
return this->GetImpl().CountPageTables();
} }
public: public:
KProcessAddress GetAddressSpaceStart() const { return this->address_space_start; } KProcessAddress GetAddressSpaceStart() const { return this->address_space_start; }
@ -418,6 +425,11 @@ namespace ams::kern {
return (this->current_heap_end - this->heap_region_start) + this->mapped_physical_memory_size; return (this->current_heap_end - this->heap_region_start) + this->mapped_physical_memory_size;
} }
size_t GetCodeSize() const;
size_t GetCodeDataSize() const;
size_t GetAliasCodeSize() const;
size_t GetAliasCodeDataSize() const;
u32 GetAllocateOption() const { return this->allocate_option; } u32 GetAllocateOption() const { return this->allocate_option; }
public: public:
static ALWAYS_INLINE KVirtualAddress GetLinearMappedVirtualAddress(KPhysicalAddress addr) { static ALWAYS_INLINE KVirtualAddress GetLinearMappedVirtualAddress(KPhysicalAddress addr) {

View File

@ -170,6 +170,8 @@ namespace ams::kern {
constexpr KProcessAddress GetEntryPoint() const { return this->code_address; } constexpr KProcessAddress GetEntryPoint() const { return this->code_address; }
constexpr size_t GetMainStackSize() const { return this->main_thread_stack_size; }
constexpr KMemoryManager::Pool GetMemoryPool() const { return this->memory_pool; } constexpr KMemoryManager::Pool GetMemoryPool() const { return this->memory_pool; }
constexpr u64 GetRandomEntropy(size_t i) const { return this->entropy[i]; } constexpr u64 GetRandomEntropy(size_t i) const { return this->entropy[i]; }
@ -305,6 +307,11 @@ namespace ams::kern {
} }
} }
const KDynamicPageManager &GetDynamicPageManager() const { return this->dynamic_page_manager; }
const KMemoryBlockSlabManager &GetMemoryBlockSlabManager() const { return this->memory_block_slab_manager; }
const KBlockInfoManager &GetBlockInfoManager() const { return this->block_info_manager; }
const KPageTableManager &GetPageTableManager() const { return this->page_table_manager; }
constexpr KThread *GetRunningThread(s32 core) const { return this->running_threads[core]; } constexpr KThread *GetRunningThread(s32 core) const { return this->running_threads[core]; }
constexpr u64 GetRunningThreadIdleCount(s32 core) const { return this->running_thread_idle_counts[core]; } constexpr u64 GetRunningThreadIdleCount(s32 core) const { return this->running_thread_idle_counts[core]; }

View File

@ -50,6 +50,8 @@ namespace ams::kern {
Result SendReply(uintptr_t message, uintptr_t buffer_size, KPhysicalAddress message_paddr); Result SendReply(uintptr_t message, uintptr_t buffer_size, KPhysicalAddress message_paddr);
void OnClientClosed(); void OnClientClosed();
void Dump();
private: private:
bool IsSignaledImpl() const; bool IsSignaledImpl() const;
void CleanupRequests(); void CleanupRequests();

View File

@ -71,6 +71,8 @@ namespace ams::kern {
KServerSession &GetServerSession() { return this->server; } KServerSession &GetServerSession() { return this->server; }
const KClientSession &GetClientSession() const { return this->client; } const KClientSession &GetClientSession() const { return this->client; }
const KServerSession &GetServerSession() const { return this->server; } const KServerSession &GetServerSession() const { return this->server; }
const KClientPort *GetParent() const { return this->port; }
}; };
} }

View File

@ -140,7 +140,21 @@ namespace ams::kern {
void *obj = this->GetImpl()->Allocate(); void *obj = this->GetImpl()->Allocate();
/* TODO: under some debug define, track the peak for statistics, as N does? */ /* Track the allocated peak. */
#if defined(MESOSPHERE_BUILD_FOR_DEBUGGING)
if (AMS_LIKELY(obj != nullptr)) {
static_assert(std::atomic_ref<uintptr_t>::is_always_lock_free);
std::atomic_ref<uintptr_t> peak_ref(this->peak);
const uintptr_t alloc_peak = reinterpret_cast<uintptr_t>(obj) + this->GetObjectSize();
uintptr_t cur_peak = this->peak;
do {
if (alloc_peak <= cur_peak) {
break;
}
} while (!peak_ref.compare_exchange_strong(cur_peak, alloc_peak));
}
#endif
return obj; return obj;
} }
@ -165,6 +179,29 @@ namespace ams::kern {
uintptr_t GetSlabHeapAddress() const { uintptr_t GetSlabHeapAddress() const {
return this->start; return this->start;
} }
size_t GetNumRemaining() const {
size_t remaining = 0;
/* Only calculate the number of remaining objects under debug configuration. */
#if defined(MESOSPHERE_BUILD_FOR_DEBUGGING)
while (true) {
auto *cur = this->GetImpl()->GetHead();
remaining = 0;
while (this->Contains(reinterpret_cast<uintptr_t>(cur))) {
++remaining;
cur = cur->next;
}
if (cur == nullptr) {
break;
}
}
#endif
return remaining;
}
}; };
template<typename T> template<typename T>

View File

@ -30,9 +30,10 @@ namespace ams::kern {
KThread *thread; KThread *thread;
}; };
private: private:
ThreadListNode *thread_list_root; ThreadListNode *thread_list_head;
ThreadListNode *thread_list_tail;
protected: protected:
constexpr ALWAYS_INLINE explicit KSynchronizationObject() : KAutoObjectWithList(), thread_list_root() { MESOSPHERE_ASSERT_THIS(); } constexpr ALWAYS_INLINE explicit KSynchronizationObject() : KAutoObjectWithList(), thread_list_head(), thread_list_tail() { MESOSPHERE_ASSERT_THIS(); }
virtual ~KSynchronizationObject() { MESOSPHERE_ASSERT_THIS(); } virtual ~KSynchronizationObject() { MESOSPHERE_ASSERT_THIS(); }
virtual void OnFinalizeSynchronizationObject() { MESOSPHERE_ASSERT_THIS(); } virtual void OnFinalizeSynchronizationObject() { MESOSPHERE_ASSERT_THIS(); }
@ -46,7 +47,7 @@ namespace ams::kern {
public: public:
virtual void Finalize() override; virtual void Finalize() override;
virtual bool IsSignaled() const = 0; virtual bool IsSignaled() const = 0;
virtual void DebugWaiters(); virtual void DumpWaiters();
}; };
} }

View File

@ -52,7 +52,7 @@ namespace ams::kern {
SuspendType_Process = 0, SuspendType_Process = 0,
SuspendType_Thread = 1, SuspendType_Thread = 1,
SuspendType_Debug = 2, SuspendType_Debug = 2,
SuspendType_Unk3 = 3, SuspendType_Backtrace = 3,
SuspendType_Init = 4, SuspendType_Init = 4,
SuspendType_Count, SuspendType_Count,
@ -70,7 +70,7 @@ namespace ams::kern {
ThreadState_ProcessSuspended = (1 << (SuspendType_Process + ThreadState_SuspendShift)), ThreadState_ProcessSuspended = (1 << (SuspendType_Process + ThreadState_SuspendShift)),
ThreadState_ThreadSuspended = (1 << (SuspendType_Thread + ThreadState_SuspendShift)), ThreadState_ThreadSuspended = (1 << (SuspendType_Thread + ThreadState_SuspendShift)),
ThreadState_DebugSuspended = (1 << (SuspendType_Debug + ThreadState_SuspendShift)), ThreadState_DebugSuspended = (1 << (SuspendType_Debug + ThreadState_SuspendShift)),
ThreadState_Unk3Suspended = (1 << (SuspendType_Unk3 + ThreadState_SuspendShift)), ThreadState_BacktraceSuspended = (1 << (SuspendType_Backtrace + ThreadState_SuspendShift)),
ThreadState_InitSuspended = (1 << (SuspendType_Init + ThreadState_SuspendShift)), ThreadState_InitSuspended = (1 << (SuspendType_Init + ThreadState_SuspendShift)),
ThreadState_SuspendFlagMask = ((1 << SuspendType_Count) - 1) << ThreadState_SuspendShift, ThreadState_SuspendFlagMask = ((1 << SuspendType_Count) - 1) << ThreadState_SuspendShift,
@ -533,6 +533,7 @@ namespace ams::kern {
return this->termination_requested || this->GetRawState() == ThreadState_Terminated; return this->termination_requested || this->GetRawState() == ThreadState_Terminated;
} }
size_t GetKernelStackUsage() const;
public: public:
/* Overridden parent functions. */ /* Overridden parent functions. */
virtual u64 GetId() const override final { return this->GetThreadId(); } virtual u64 GetId() const override final { return this->GetThreadId(); }

View File

@ -48,6 +48,8 @@ namespace ams::kern {
static size_t GetSlabHeapSize() { return s_slab_heap.GetSlabHeapSize(); } static size_t GetSlabHeapSize() { return s_slab_heap.GetSlabHeapSize(); }
static size_t GetPeakIndex() { return s_slab_heap.GetPeakIndex(); } static size_t GetPeakIndex() { return s_slab_heap.GetPeakIndex(); }
static uintptr_t GetSlabHeapAddress() { return s_slab_heap.GetSlabHeapAddress(); } static uintptr_t GetSlabHeapAddress() { return s_slab_heap.GetSlabHeapAddress(); }
static size_t GetNumRemaining() { return s_slab_heap.GetNumRemaining(); }
}; };
template<typename Derived, typename Base> template<typename Derived, typename Base>
@ -92,7 +94,7 @@ namespace ams::kern {
virtual uintptr_t GetPostDestroyArgument() const { return 0; } virtual uintptr_t GetPostDestroyArgument() const { return 0; }
size_t GetSlabIndex() const { size_t GetSlabIndex() const {
return s_slab_heap.GetIndex(static_cast<const Derived *>(this)); return s_slab_heap.GetObjectIndex(static_cast<const Derived *>(this));
} }
public: public:
static void InitializeSlabHeap(void *memory, size_t memory_size) { static void InitializeSlabHeap(void *memory, size_t memory_size) {
@ -116,6 +118,8 @@ namespace ams::kern {
static size_t GetSlabHeapSize() { return s_slab_heap.GetSlabHeapSize(); } static size_t GetSlabHeapSize() { return s_slab_heap.GetSlabHeapSize(); }
static size_t GetPeakIndex() { return s_slab_heap.GetPeakIndex(); } static size_t GetPeakIndex() { return s_slab_heap.GetPeakIndex(); }
static uintptr_t GetSlabHeapAddress() { return s_slab_heap.GetSlabHeapAddress(); } static uintptr_t GetSlabHeapAddress() { return s_slab_heap.GetSlabHeapAddress(); }
static size_t GetNumRemaining() { return s_slab_heap.GetNumRemaining(); }
}; };
} }

View File

@ -384,4 +384,541 @@ namespace ams::kern::arch::arm64 {
#undef MESOSPHERE_SET_HW_WATCH_POINT #undef MESOSPHERE_SET_HW_WATCH_POINT
#undef MESOSPHERE_SET_HW_BREAK_POINT #undef MESOSPHERE_SET_HW_BREAK_POINT
void KDebug::PrintRegister(KThread *thread) {
#if defined(MESOSPHERE_BUILD_FOR_DEBUGGING)
{
/* Treat no thread as current thread. */
if (thread == nullptr) {
thread = GetCurrentThreadPointer();
}
/* Get the exception context. */
KExceptionContext *e_ctx = GetExceptionContext(thread);
/* Get the owner process. */
if (auto *process = thread->GetOwnerProcess(); process != nullptr) {
/* Lock the owner process. */
KScopedLightLock state_lk(process->GetStateLock());
KScopedLightLock list_lk(process->GetListLock());
/* Suspend all the process's threads. */
{
KScopedSchedulerLock sl;
auto end = process->GetThreadList().end();
for (auto it = process->GetThreadList().begin(); it != end; ++it) {
if (std::addressof(*it) != GetCurrentThreadPointer()) {
it->RequestSuspend(KThread::SuspendType_Backtrace);
}
}
}
/* Print the registers. */
MESOSPHERE_RELEASE_LOG("Registers\n");
if ((e_ctx->psr & 0x10) == 0) {
/* 64-bit thread. */
for (auto i = 0; i < 31; ++i) {
MESOSPHERE_RELEASE_LOG(" X[%2d]: 0x%016lx\n", i, e_ctx->x[i]);
}
MESOSPHERE_RELEASE_LOG(" SP: 0x%016lx\n", e_ctx->sp);
MESOSPHERE_RELEASE_LOG(" PC: 0x%016lx\n", e_ctx->pc - sizeof(u32));
MESOSPHERE_RELEASE_LOG(" PSR: 0x%08x\n", e_ctx->psr);
MESOSPHERE_RELEASE_LOG(" TPIDR_EL0: 0x%016lx\n", e_ctx->tpidr);
} else {
/* 32-bit thread. */
for (auto i = 0; i < 13; ++i) {
MESOSPHERE_RELEASE_LOG(" R[%2d]: 0x%08x\n", i, static_cast<u32>(e_ctx->x[i]));
}
MESOSPHERE_RELEASE_LOG(" SP: 0x%08x\n", static_cast<u32>(e_ctx->x[13]));
MESOSPHERE_RELEASE_LOG(" LR: 0x%08x\n", static_cast<u32>(e_ctx->x[14]));
MESOSPHERE_RELEASE_LOG(" PC: 0x%08x\n", static_cast<u32>(e_ctx->pc) - static_cast<u32>((e_ctx->psr & 0x20) ? sizeof(u16) : sizeof(u32)));
MESOSPHERE_RELEASE_LOG(" PSR: 0x%08x\n", e_ctx->psr);
MESOSPHERE_RELEASE_LOG(" TPIDR: 0x%08x\n", static_cast<u32>(e_ctx->tpidr));
}
/* Resume the threads that we suspended. */
{
KScopedSchedulerLock sl;
auto end = process->GetThreadList().end();
for (auto it = process->GetThreadList().begin(); it != end; ++it) {
if (std::addressof(*it) != GetCurrentThreadPointer()) {
it->Resume(KThread::SuspendType_Backtrace);
}
}
}
}
}
#else
MESOSPHERE_UNUSED(thread);
#endif
}
#if defined(MESOSPHERE_BUILD_FOR_DEBUGGING)
namespace {
bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr) {
const KMemoryRegion *cached = nullptr;
return KMemoryLayout::IsHeapPhysicalAddress(cached, phys_addr);
}
template<typename T>
bool ReadValue(T *out, KProcess *process, uintptr_t address) {
KPhysicalAddress phys_addr;
KMemoryInfo mem_info;
ams::svc::PageInfo page_info;
if (!util::IsAligned(address, sizeof(T))) {
return false;
}
if (R_FAILED(process->GetPageTable().QueryInfo(std::addressof(mem_info), std::addressof(page_info), address))) {
return false;
}
if ((mem_info.GetPermission() & KMemoryPermission_UserRead) != KMemoryPermission_UserRead) {
return false;
}
if (!process->GetPageTable().GetPhysicalAddress(std::addressof(phys_addr), address)) {
return false;
}
if (!IsHeapPhysicalAddress(phys_addr)) {
return false;
}
*out = *GetPointer<T>(process->GetPageTable().GetHeapVirtualAddress(phys_addr));
return true;
}
bool GetModuleName(char *dst, size_t dst_size, KProcess *process, uintptr_t base_address) {
/* Locate .rodata. */
KMemoryInfo mem_info;
ams::svc::PageInfo page_info;
KMemoryState mem_state = KMemoryState_None;
while (true) {
if (R_FAILED(process->GetPageTable().QueryInfo(std::addressof(mem_info), std::addressof(page_info), base_address))) {
return false;
}
if (mem_state == KMemoryState_None) {
mem_state = mem_info.GetState();
if (mem_state != KMemoryState_Code && mem_state != KMemoryState_AliasCode) {
return false;
}
}
if (mem_info.GetState() != mem_state) {
return false;
}
if (mem_info.GetPermission() == KMemoryPermission_UserRead) {
break;
}
base_address = mem_info.GetEndAddress();
}
/* Check that first value is 0. */
u32 val;
if (!ReadValue(std::addressof(val), process, base_address)) {
return false;
}
if (val != 0) {
return false;
}
/* Read the name length. */
if (!ReadValue(std::addressof(val), process, base_address + sizeof(u32))) {
return false;
}
if (!(0 < val && val < dst_size)) {
return false;
}
const size_t name_len = val;
/* Read the name, one character at a time. */
for (size_t i = 0; i < name_len; ++i) {
if (!ReadValue(dst + i, process, base_address + 2 * sizeof(u32) + i)) {
return false;
}
if (!(0 < dst[i] && dst[i] <= 0x7F)) {
return false;
}
}
/* NULL-terminate. */
dst[name_len] = 0;
return true;
}
void PrintAddress(uintptr_t address) {
MESOSPHERE_RELEASE_LOG(" %p\n", reinterpret_cast<void *>(address));
}
void PrintAddressWithModuleName(uintptr_t address, bool has_module_name, const char *module_name, uintptr_t base_address) {
if (has_module_name) {
MESOSPHERE_RELEASE_LOG(" %p [%10s + %8lx]\n", reinterpret_cast<void *>(address), module_name, address - base_address);
} else {
MESOSPHERE_RELEASE_LOG(" %p [%10lx + %8lx]\n", reinterpret_cast<void *>(address), base_address, address - base_address);
}
}
void PrintAddressWithSymbol(uintptr_t address, bool has_module_name, const char *module_name, uintptr_t base_address, const char *symbol_name, uintptr_t func_address) {
if (has_module_name) {
MESOSPHERE_RELEASE_LOG(" %p [%10s + %8lx] (%s + %lx)\n", reinterpret_cast<void *>(address), module_name, address - base_address, symbol_name, address - func_address);
} else {
MESOSPHERE_RELEASE_LOG(" %p [%10lx + %8lx] (%s + %lx)\n", reinterpret_cast<void *>(address), base_address, address - base_address, symbol_name, address - func_address);
}
}
void PrintCodeAddress(KProcess *process, uintptr_t address, bool is_lr = true) {
/* Prepare to parse + print the address. */
uintptr_t test_address = is_lr ? address - sizeof(u32) : address;
uintptr_t base_address = address;
uintptr_t dyn_address = 0;
uintptr_t sym_tab = 0;
uintptr_t str_tab = 0;
size_t num_sym = 0;
u64 temp_64;
u32 temp_32;
/* Locate the start of .text. */
KMemoryInfo mem_info;
ams::svc::PageInfo page_info;
KMemoryState mem_state = KMemoryState_None;
while (true) {
if (R_FAILED(process->GetPageTable().QueryInfo(std::addressof(mem_info), std::addressof(page_info), base_address))) {
return PrintAddress(address);
}
if (mem_state == KMemoryState_None) {
mem_state = mem_info.GetState();
if (mem_state != KMemoryState_Code && mem_state != KMemoryState_AliasCode) {
return PrintAddress(address);
}
} else if (mem_info.GetState() != mem_state) {
return PrintAddress(address);
}
if (mem_info.GetPermission() != KMemoryPermission_UserReadExecute) {
return PrintAddress(address);
}
base_address = mem_info.GetAddress();
if (R_FAILED(process->GetPageTable().QueryInfo(std::addressof(mem_info), std::addressof(page_info), base_address - 1))) {
return PrintAddress(address);
}
if (mem_info.GetState() != mem_state) {
break;
}
if (mem_info.GetPermission() != KMemoryPermission_UserReadExecute) {
break;
}
}
/* Read the first instruction. */
if (!ReadValue(std::addressof(temp_32), process, base_address)) {
return PrintAddress(address);
}
/* Get the module name. */
char module_name[0x20];
const bool has_module_name = GetModuleName(module_name, sizeof(module_name), process, base_address);
/* If the process is 32-bit, just print the module. */
if (!process->Is64Bit()) {
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
}
if (temp_32 == 0) {
/* Module is dynamically loaded by rtld. */
u32 mod_offset;
if (!ReadValue(std::addressof(mod_offset), process, base_address + sizeof(u32))) {
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
}
if (!ReadValue(std::addressof(temp_32), process, base_address + mod_offset)) {
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
}
if (temp_32 != 0x30444F4D) { /* MOD0 */
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
}
if (!ReadValue(std::addressof(temp_32), process, base_address + mod_offset + sizeof(u32))) {
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
}
dyn_address = base_address + mod_offset + temp_32;
} else if (temp_32 == 0x14000002) {
/* Module embeds rtld. */
if (!ReadValue(std::addressof(temp_32), process, base_address + 0x5C)) {
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
}
if (temp_32 != 0x94000002) {
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
}
if (!ReadValue(std::addressof(temp_32), process, base_address + 0x60)) {
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
}
dyn_address = base_address + 0x60 + temp_32;
} else {
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
}
/* Locate tables inside .dyn. */
for (size_t ofs = 0; /* ... */; ofs += 0x10) {
/* Read the DynamicTag. */
if (!ReadValue(std::addressof(temp_64), process, dyn_address + ofs)) {
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
}
if (temp_64 == 0) {
/* We're done parsing .dyn. */
break;
} else if (temp_64 == 4) {
/* We found DT_HASH */
if (!ReadValue(std::addressof(temp_64), process, dyn_address + ofs + sizeof(u64))) {
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
}
/* Read nchain, to get the number of symbols. */
if (!ReadValue(std::addressof(temp_32), process, base_address + temp_64 + sizeof(u32))) {
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
}
num_sym = temp_32;
} else if (temp_64 == 5) {
/* We found DT_STRTAB */
if (!ReadValue(std::addressof(temp_64), process, dyn_address + ofs + sizeof(u64))) {
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
}
str_tab = base_address + temp_64;
} else if (temp_64 == 6) {
/* We found DT_SYMTAB */
if (!ReadValue(std::addressof(temp_64), process, dyn_address + ofs + sizeof(u64))) {
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
}
sym_tab = base_address + temp_64;
}
}
/* Check that we found all the tables. */
if (!(sym_tab != 0 && str_tab != 0 && num_sym != 0)) {
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
}
/* Try to locate an appropriate symbol. */
for (size_t i = 0; i < num_sym; ++i) {
/* Read the symbol from userspace. */
struct {
u32 st_name;
u8 st_info;
u8 st_other;
u16 st_shndx;
u64 st_value;
u64 st_size;
} sym;
{
u64 x[sizeof(sym) / sizeof(u64)];
for (size_t j = 0; j < util::size(x); ++j) {
if (!ReadValue(x + j, process, sym_tab + sizeof(sym) * i + sizeof(u64) * j)) {
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
}
}
std::memcpy(std::addressof(sym), x, sizeof(sym));
}
/* Check the symbol is valid/STT_FUNC. */
if (sym.st_shndx == 0 || ((sym.st_shndx & 0xFF00) == 0xFF00)) {
continue;
}
if ((sym.st_info & 0xF) != 2) {
continue;
}
/* Check the address. */
const uintptr_t func_start = base_address + sym.st_value;
if (func_start <= test_address && test_address < func_start + sym.st_size) {
/* Read the symbol name. */
const uintptr_t sym_address = str_tab + sym.st_name;
char sym_name[0x80];
sym_name[util::size(sym_name) - 1] = 0;
for (size_t j = 0; j < util::size(sym_name) - 1; ++j) {
if (!ReadValue(sym_name + j, process, sym_address + j)) {
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
}
if (sym_name[j] == 0) {
break;
}
}
/* Print the symbol. */
return PrintAddressWithSymbol(address, has_module_name, module_name, base_address, sym_name, func_start);
}
}
/* Fall back to printing the module. */
return PrintAddressWithModuleName(address, has_module_name, module_name, base_address);
}
}
#endif
void KDebug::PrintBacktrace(KThread *thread) {
#if defined(MESOSPHERE_BUILD_FOR_DEBUGGING)
{
/* Treat no thread as current thread. */
if (thread == nullptr) {
thread = GetCurrentThreadPointer();
}
/* Get the exception context. */
KExceptionContext *e_ctx = GetExceptionContext(thread);
/* Get the owner process. */
if (auto *process = thread->GetOwnerProcess(); process != nullptr) {
/* Lock the owner process. */
KScopedLightLock state_lk(process->GetStateLock());
KScopedLightLock list_lk(process->GetListLock());
/* Suspend all the process's threads. */
{
KScopedSchedulerLock sl;
auto end = process->GetThreadList().end();
for (auto it = process->GetThreadList().begin(); it != end; ++it) {
if (std::addressof(*it) != GetCurrentThreadPointer()) {
it->RequestSuspend(KThread::SuspendType_Backtrace);
}
}
}
/* Print the backtrace. */
MESOSPHERE_RELEASE_LOG("User Backtrace\n");
if ((e_ctx->psr & 0x10) == 0) {
/* 64-bit thread. */
PrintCodeAddress(process, e_ctx->pc, false);
PrintCodeAddress(process, e_ctx->x[30]);
/* Walk the stack frames. */
uintptr_t fp = static_cast<uintptr_t>(e_ctx->x[29]);
for (auto i = 0; i < 0x20 && fp != 0 && util::IsAligned(fp, 0x10); ++i) {
/* Read the next frame. */
struct {
u64 fp;
u64 lr;
} stack_frame;
{
KMemoryInfo mem_info;
ams::svc::PageInfo page_info;
KPhysicalAddress phys_addr;
if (R_FAILED(process->GetPageTable().QueryInfo(std::addressof(mem_info), std::addressof(page_info), fp))) {
break;
}
if ((mem_info.GetState() & KMemoryState_FlagReferenceCounted) == 0) {
break;
}
if ((mem_info.GetAttribute() & KMemoryAttribute_Uncached) != 0) {
break;
}
if ((mem_info.GetPermission() & KMemoryPermission_UserRead) != KMemoryPermission_UserRead) {
break;
}
if (!process->GetPageTable().GetPhysicalAddress(std::addressof(phys_addr), fp)) {
break;
}
if (!IsHeapPhysicalAddress(phys_addr)) {
break;
}
u64 *frame_ptr = GetPointer<u64>(process->GetPageTable().GetHeapVirtualAddress(phys_addr));
stack_frame.fp = frame_ptr[0];
stack_frame.lr = frame_ptr[1];
}
/* Print and advance. */
PrintCodeAddress(process, stack_frame.lr);
fp = stack_frame.fp;
}
} else {
/* 32-bit thread. */
PrintCodeAddress(process, e_ctx->pc, false);
PrintCodeAddress(process, e_ctx->x[14]);
/* Walk the stack frames. */
uintptr_t fp = static_cast<uintptr_t>(e_ctx->x[11]);
for (auto i = 0; i < 0x20 && fp != 0 && util::IsAligned(fp, 4); ++i) {
/* Read the next frame. */
struct {
u32 fp;
u32 lr;
} stack_frame;
{
KMemoryInfo mem_info;
ams::svc::PageInfo page_info;
KPhysicalAddress phys_addr;
/* Read FP */
if (R_FAILED(process->GetPageTable().QueryInfo(std::addressof(mem_info), std::addressof(page_info), fp))) {
break;
}
if ((mem_info.GetState() & KMemoryState_FlagReferenceCounted) == 0) {
break;
}
if ((mem_info.GetAttribute() & KMemoryAttribute_Uncached) != 0) {
break;
}
if ((mem_info.GetPermission() & KMemoryPermission_UserRead) != KMemoryPermission_UserRead) {
break;
}
if (!process->GetPageTable().GetPhysicalAddress(std::addressof(phys_addr), fp)) {
break;
}
if (!IsHeapPhysicalAddress(phys_addr)) {
break;
}
stack_frame.fp = *GetPointer<u32>(process->GetPageTable().GetHeapVirtualAddress(phys_addr));
/* Read LR. */
uintptr_t lr_ptr = (e_ctx->x[13] <= stack_frame.fp && stack_frame.fp < e_ctx->x[13] + PageSize) ? fp + 4 : fp - 4;
if (R_FAILED(process->GetPageTable().QueryInfo(std::addressof(mem_info), std::addressof(page_info), lr_ptr))) {
break;
}
if ((mem_info.GetState() & KMemoryState_FlagReferenceCounted) == 0) {
break;
}
if ((mem_info.GetAttribute() & KMemoryAttribute_Uncached) != 0) {
break;
}
if ((mem_info.GetPermission() & KMemoryPermission_UserRead) != KMemoryPermission_UserRead) {
break;
}
if (!process->GetPageTable().GetPhysicalAddress(std::addressof(phys_addr), lr_ptr)) {
break;
}
if (!IsHeapPhysicalAddress(phys_addr)) {
break;
}
stack_frame.lr = *GetPointer<u32>(process->GetPageTable().GetHeapVirtualAddress(phys_addr));
}
/* Print and advance. */
PrintCodeAddress(process, stack_frame.lr);
fp = stack_frame.fp;
}
}
/* Resume the threads that we suspended. */
{
KScopedSchedulerLock sl;
auto end = process->GetThreadList().end();
for (auto it = process->GetThreadList().begin(); it != end; ++it) {
if (std::addressof(*it) != GetCurrentThreadPointer()) {
it->Resume(KThread::SuspendType_Backtrace);
}
}
}
}
}
#else
MESOSPHERE_UNUSED(thread);
#endif
}
} }

View File

@ -431,5 +431,28 @@ namespace ams::kern::arch::arm64 {
} }
} }
size_t KPageTableImpl::CountPageTables() const {
size_t num_tables = 0;
#if defined(MESOSPHERE_BUILD_FOR_DEBUGGING)
{
++num_tables;
for (size_t l1_index = 0; l1_index < this->num_entries; ++l1_index) {
auto &l1_entry = this->table[l1_index];
if (l1_entry.IsTable()) {
++num_tables;
for (size_t l2_index = 0; l2_index < MaxPageTableEntries; ++l2_index) {
auto *l2_entry = GetPointer<L2PageTableEntry>(GetTableEntry(KMemoryLayout::GetLinearVirtualAddress(l1_entry.GetTable()), l2_index));
if (l2_entry->IsTable()) {
++num_tables;
}
}
}
}
}
#endif
return num_tables;
}
} }

View File

@ -51,7 +51,7 @@ namespace ams::kern::arch::arm64 {
cpu::InstructionMemoryBarrier(); cpu::InstructionMemoryBarrier();
} }
uintptr_t SetupStackForUserModeThreadStarter(KVirtualAddress pc, KVirtualAddress k_sp, KVirtualAddress u_sp, uintptr_t arg, bool is_64_bit) { uintptr_t SetupStackForUserModeThreadStarter(KVirtualAddress pc, KVirtualAddress k_sp, KVirtualAddress u_sp, uintptr_t arg, const bool is_64_bit) {
/* NOTE: Stack layout on entry looks like following: */ /* NOTE: Stack layout on entry looks like following: */
/* SP */ /* SP */
/* | */ /* | */
@ -76,6 +76,11 @@ namespace ams::kern::arch::arm64 {
MESOSPHERE_LOG("Creating User 32-Thread, %016lx\n", GetInteger(pc)); MESOSPHERE_LOG("Creating User 32-Thread, %016lx\n", GetInteger(pc));
} }
/* Set CFI-value. */
if (is_64_bit) {
ctx->x[18] = KSystemControl::GenerateRandomU64() | 1;
}
/* Set stack pointer. */ /* Set stack pointer. */
if (is_64_bit) { if (is_64_bit) {
ctx->sp = GetInteger(u_sp); ctx->sp = GetInteger(u_sp);

View File

@ -409,6 +409,15 @@ namespace ams::kern::board::nintendo::nx {
return MinimumSize; return MinimumSize;
} }
u8 KSystemControl::Init::GetDebugLogUartPort() {
/* Get the log configuration. */
u64 value = 0;
smc::init::GetConfig(std::addressof(value), 1, smc::ConfigItem::ExosphereLogConfiguration);
/* Extract the port. */
return static_cast<u8>((value >> 32) & 0xFF);
}
void KSystemControl::Init::CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) { void KSystemControl::Init::CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) {
smc::init::CpuOn(core_id, entrypoint, arg); smc::init::CpuOn(core_id, entrypoint, arg);
} }

View File

@ -64,6 +64,7 @@ namespace ams::kern::board::nintendo::nx::smc {
ExosphereAllowCalWrites = 65006, ExosphereAllowCalWrites = 65006,
ExosphereEmummcType = 65007, ExosphereEmummcType = 65007,
ExospherePayloadAddress = 65008, ExospherePayloadAddress = 65008,
ExosphereLogConfiguration = 65009,
}; };
enum class SmcResult { enum class SmcResult {

View File

@ -22,7 +22,7 @@ namespace ams::kern::init {
#define FOREACH_SLAB_TYPE(HANDLER, ...) \ #define FOREACH_SLAB_TYPE(HANDLER, ...) \
HANDLER(KProcess, (SLAB_COUNT(KProcess)), ## __VA_ARGS__) \ HANDLER(KProcess, (SLAB_COUNT(KProcess)), ## __VA_ARGS__) \
HANDLER(KThread, (SLAB_COUNT(KThread)), ## __VA_ARGS__) \ HANDLER(KThread, (SLAB_COUNT(KThread)), ## __VA_ARGS__) \
HANDLER(KLinkedListNode, (SLAB_COUNT(KThread) * 17), ## __VA_ARGS__) \ HANDLER(KLinkedListNode, (SLAB_COUNT(KThread)), ## __VA_ARGS__) \
HANDLER(KEvent, (SLAB_COUNT(KEvent)), ## __VA_ARGS__) \ HANDLER(KEvent, (SLAB_COUNT(KEvent)), ## __VA_ARGS__) \
HANDLER(KInterruptEvent, (SLAB_COUNT(KInterruptEvent)), ## __VA_ARGS__) \ HANDLER(KInterruptEvent, (SLAB_COUNT(KInterruptEvent)), ## __VA_ARGS__) \
HANDLER(KInterruptEventTask, (SLAB_COUNT(KInterruptEvent)), ## __VA_ARGS__) \ HANDLER(KInterruptEventTask, (SLAB_COUNT(KInterruptEvent)), ## __VA_ARGS__) \
@ -77,7 +77,7 @@ namespace ams::kern::init {
namespace test { namespace test {
constexpr size_t RequiredSizeForExtraThreadCount = SlabCountExtraKThread * (sizeof(KThread) + (sizeof(KLinkedListNode) * 17) + (sizeof(KThreadLocalPage) / 8) + sizeof(KEventInfo)); constexpr size_t RequiredSizeForExtraThreadCount = SlabCountExtraKThread * (sizeof(KThread) + sizeof(KLinkedListNode) + (sizeof(KThreadLocalPage) / 8) + sizeof(KEventInfo));
static_assert(RequiredSizeForExtraThreadCount <= KernelSlabHeapAdditionalSize); static_assert(RequiredSizeForExtraThreadCount <= KernelSlabHeapAdditionalSize);
} }

View File

@ -18,10 +18,12 @@
namespace ams::kern { namespace ams::kern {
#if defined(MESOSPHERE_DEBUG_LOG_USE_UART_A) || defined(MESOSPHERE_DEBUG_LOG_USE_UART_B) || defined(MESOSPHERE_DEBUG_LOG_USE_UART_C) || defined(MESOSPHERE_DEBUG_LOG_USE_UART_D) #if defined(MESOSPHERE_DEBUG_LOG_USE_UART)
namespace { namespace {
constexpr bool DoSaveAndRestore = false;
enum UartRegister { enum UartRegister {
UartRegister_THR = 0, UartRegister_THR = 0,
UartRegister_IER = 1, UartRegister_IER = 1,
@ -38,13 +40,13 @@ namespace ams::kern {
KVirtualAddress g_uart_address = 0; KVirtualAddress g_uart_address = 0;
constinit u32 g_saved_registers[5]; [[maybe_unused]] constinit u32 g_saved_registers[5];
NOINLINE u32 ReadUartRegister(UartRegister which) { ALWAYS_INLINE u32 ReadUartRegister(UartRegister which) {
return GetPointer<volatile u32>(g_uart_address)[which]; return GetPointer<volatile u32>(g_uart_address)[which];
} }
NOINLINE void WriteUartRegister(UartRegister which, u32 value) { ALWAYS_INLINE void WriteUartRegister(UartRegister which, u32 value) {
GetPointer<volatile u32>(g_uart_address)[which] = value; GetPointer<volatile u32>(g_uart_address)[which] = value;
} }
@ -86,6 +88,7 @@ namespace ams::kern {
} }
void KDebugLogImpl::Save() { void KDebugLogImpl::Save() {
if constexpr (DoSaveAndRestore) {
/* Save LCR, IER, FCR. */ /* Save LCR, IER, FCR. */
g_saved_registers[0] = ReadUartRegister(UartRegister_LCR); g_saved_registers[0] = ReadUartRegister(UartRegister_LCR);
g_saved_registers[1] = ReadUartRegister(UartRegister_IER); g_saved_registers[1] = ReadUartRegister(UartRegister_IER);
@ -103,8 +106,10 @@ namespace ams::kern {
WriteUartRegister(UartRegister_LCR, g_saved_registers[0]); WriteUartRegister(UartRegister_LCR, g_saved_registers[0]);
ReadUartRegister(UartRegister_LCR); ReadUartRegister(UartRegister_LCR);
} }
}
void KDebugLogImpl::Restore() { void KDebugLogImpl::Restore() {
if constexpr (DoSaveAndRestore) {
/* Set Divisor Latch Access bit, to allow access to DLL/DLH */ /* Set Divisor Latch Access bit, to allow access to DLL/DLH */
WriteUartRegister(UartRegister_LCR, 0x80); WriteUartRegister(UartRegister_LCR, 0x80);
ReadUartRegister(UartRegister_LCR); ReadUartRegister(UartRegister_LCR);
@ -124,6 +129,7 @@ namespace ams::kern {
WriteUartRegister(UartRegister_IRDA_CSR, 0x02); WriteUartRegister(UartRegister_IRDA_CSR, 0x02);
ReadUartRegister(UartRegister_FCR); ReadUartRegister(UartRegister_FCR);
} }
}
#elif defined(MESOSPHERE_DEBUG_LOG_USE_IRAM_RINGBUFFER) #elif defined(MESOSPHERE_DEBUG_LOG_USE_IRAM_RINGBUFFER)

View File

@ -0,0 +1,651 @@
/*
* Copyright (c) 2018-2020 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 <mesosphere.hpp>
namespace ams::kern::KDumpObject {
namespace {
constexpr const char * const ThreadStates[] = {
[KThread::ThreadState_Initialized] = "Initialized",
[KThread::ThreadState_Waiting] = "Waiting",
[KThread::ThreadState_Runnable] = "Runnable",
[KThread::ThreadState_Terminated] = "Terminated",
};
void DumpThread(KThread *thread) {
if (KProcess *process = thread->GetOwnerProcess(); process != nullptr) {
MESOSPHERE_RELEASE_LOG("Thread ID=%5lu pid=%3lu %-11s Pri=%2d %-11s KernelStack=%4zu/%4zu Run=%d Ideal=%d (%d) Affinity=%016lx (%016lx)\n",
thread->GetId(), process->GetId(), process->GetName(), thread->GetPriority(), ThreadStates[thread->GetState()],
thread->GetKernelStackUsage(), PageSize, thread->GetActiveCore(), thread->GetIdealVirtualCore(), thread->GetIdealPhysicalCore(),
thread->GetVirtualAffinityMask(), thread->GetAffinityMask().GetAffinityMask());
MESOSPHERE_RELEASE_LOG(" State: 0x%04x Suspend: 0x%04x Dpc: 0x%x\n", thread->GetRawState(), thread->GetSuspendFlags(), thread->GetDpc());
MESOSPHERE_RELEASE_LOG(" TLS: %p (%p)\n", GetVoidPointer(thread->GetThreadLocalRegionAddress()), thread->GetThreadLocalRegionHeapAddress());
} else {
MESOSPHERE_RELEASE_LOG("Thread ID=%5lu pid=%3d %-11s Pri=%2d %-11s KernelStack=%4zu/%4zu Run=%d Ideal=%d (%d) Affinity=%016lx (%016lx)\n",
thread->GetId(), -1, "(kernel)", thread->GetPriority(), ThreadStates[thread->GetState()],
thread->GetKernelStackUsage(), PageSize, thread->GetActiveCore(), thread->GetIdealVirtualCore(), thread->GetIdealPhysicalCore(),
thread->GetVirtualAffinityMask(), thread->GetAffinityMask().GetAffinityMask());
}
}
void DumpThreadCallStack(KThread *thread) {
if (KProcess *process = thread->GetOwnerProcess(); process != nullptr) {
MESOSPHERE_RELEASE_LOG("Thread ID=%5lu pid=%3lu %-11s Pri=%2d %-11s KernelStack=%4zu/%4zu\n",
thread->GetId(), process->GetId(), process->GetName(), thread->GetPriority(), ThreadStates[thread->GetState()], thread->GetKernelStackUsage(), PageSize);
KDebug::PrintRegister(thread);
KDebug::PrintBacktrace(thread);
} else {
MESOSPHERE_RELEASE_LOG("Thread ID=%5lu pid=%3d %-11s Pri=%2d %-11s KernelStack=%4zu/%4zu\n",
thread->GetId(), -1, "(kernel)", thread->GetPriority(), ThreadStates[thread->GetState()], thread->GetKernelStackUsage(), PageSize);
}
}
void DumpHandle(const KProcess::ListAccessor &accessor, KProcess *process) {
MESOSPHERE_RELEASE_LOG("Process ID=%lu (%s)\n", process->GetId(), process->GetName());
const auto end = accessor.end();
const auto &handle_table = process->GetHandleTable();
const size_t max_handles = handle_table.GetMaxCount();
for (size_t i = 0; i < max_handles; ++i) {
/* Get the object + handle. */
ams::svc::Handle handle = ams::svc::InvalidHandle;
KScopedAutoObject obj = handle_table.GetObjectByIndex(std::addressof(handle), i);
if (obj.IsNotNull()) {
if (auto *target = obj->DynamicCast<KServerSession *>(); target != nullptr) {
MESOSPHERE_RELEASE_LOG("Handle %08x Obj=%p Ref=%d Type=%s Client=%p\n", handle, obj.GetPointerUnsafe(), obj->GetReferenceCount() - 1, obj->GetTypeName(), std::addressof(target->GetParent()->GetClientSession()));
target->Dump();
} else if (auto *target = obj->DynamicCast<KClientSession *>(); target != nullptr) {
MESOSPHERE_RELEASE_LOG("Handle %08x Obj=%p Ref=%d Type=%s Server=%p\n", handle, obj.GetPointerUnsafe(), obj->GetReferenceCount() - 1, obj->GetTypeName(), std::addressof(target->GetParent()->GetServerSession()));
} else if (auto *target = obj->DynamicCast<KThread *>(); target != nullptr) {
KProcess *target_owner = target->GetOwnerProcess();
const s32 owner_pid = target_owner != nullptr ? static_cast<s32>(target_owner->GetId()) : -1;
MESOSPHERE_RELEASE_LOG("Handle %08x Obj=%p Ref=%d Type=%s ID=%d PID=%d\n", handle, obj.GetPointerUnsafe(), obj->GetReferenceCount() - 1, obj->GetTypeName(), static_cast<s32>(target->GetId()), owner_pid);
} else if (auto *target = obj->DynamicCast<KProcess *>(); target != nullptr) {
MESOSPHERE_RELEASE_LOG("Handle %08x Obj=%p Ref=%d Type=%s ID=%d\n", handle, obj.GetPointerUnsafe(), obj->GetReferenceCount() - 1, obj->GetTypeName(), static_cast<s32>(target->GetId()));
} else if (auto *target = obj->DynamicCast<KSharedMemory *>(); target != nullptr) {
/* Find the owner. */
KProcess *target_owner = nullptr;
for (auto it = accessor.begin(); it != end; ++it) {
if (static_cast<KProcess *>(std::addressof(*it))->GetId() == target->GetOwnerProcessId()) {
target_owner = static_cast<KProcess *>(std::addressof(*it));
break;
}
}
MESOSPHERE_ASSERT(target_owner != nullptr);
MESOSPHERE_RELEASE_LOG("Handle %08x Obj=%p Ref=%d Type=%s Size=%zu KB OwnerPID=%d (%s)\n", handle, obj.GetPointerUnsafe(), obj->GetReferenceCount() - 1, obj->GetTypeName(), target->GetSize() / 1_KB, static_cast<s32>(target_owner->GetId()), target_owner->GetName());
} else if (auto *target = obj->DynamicCast<KTransferMemory *>(); target != nullptr) {
KProcess *target_owner = target->GetOwner();
MESOSPHERE_RELEASE_LOG("Handle %08x Obj=%p Ref=%d Type=%s OwnerPID=%d (%s) OwnerAddress=%lx Size=%zu KB\n", handle, obj.GetPointerUnsafe(), obj->GetReferenceCount() - 1, obj->GetTypeName(), static_cast<s32>(target_owner->GetId()), target_owner->GetName(), GetInteger(target->GetSourceAddress()), target->GetSize() / 1_KB);
} else if (auto *target = obj->DynamicCast<KCodeMemory *>(); target != nullptr) {
KProcess *target_owner = target->GetOwner();
MESOSPHERE_RELEASE_LOG("Handle %08x Obj=%p Ref=%d Type=%s OwnerPID=%d (%s) OwnerAddress=%lx Size=%zu KB\n", handle, obj.GetPointerUnsafe(), obj->GetReferenceCount() - 1, obj->GetTypeName(), static_cast<s32>(target_owner->GetId()), target_owner->GetName(), GetInteger(target->GetSourceAddress()), target->GetSize() / 1_KB);
} else if (auto *target = obj->DynamicCast<KInterruptEvent *>(); target != nullptr) {
MESOSPHERE_RELEASE_LOG("Handle %08x Obj=%p Ref=%d Type=%s irq=%d\n", handle, obj.GetPointerUnsafe(), obj->GetReferenceCount() - 1, obj->GetTypeName(), target->GetInterruptId());
} else if (auto *target = obj->DynamicCast<KWritableEvent *>(); target != nullptr) {
if (KEvent *event = target->GetParent(); event != nullptr) {
MESOSPHERE_RELEASE_LOG("Handle %08x Obj=%p Ref=%d Type=%s Pair=%p\n", handle, obj.GetPointerUnsafe(), obj->GetReferenceCount() - 1, obj->GetTypeName(), std::addressof(event->GetReadableEvent()));
} else {
MESOSPHERE_RELEASE_LOG("Handle %08x Obj=%p Ref=%d Type=%s\n", handle, obj.GetPointerUnsafe(), obj->GetReferenceCount() - 1, obj->GetTypeName());
}
} else if (auto *target = obj->DynamicCast<KReadableEvent *>(); target != nullptr) {
if (KEvent *event = target->GetParent(); event != nullptr) {
MESOSPHERE_RELEASE_LOG("Handle %08x Obj=%p Ref=%d Type=%s Pair=%p\n", handle, obj.GetPointerUnsafe(), obj->GetReferenceCount() - 1, obj->GetTypeName(), std::addressof(event->GetWritableEvent()));
} else {
MESOSPHERE_RELEASE_LOG("Handle %08x Obj=%p Ref=%d Type=%s\n", handle, obj.GetPointerUnsafe(), obj->GetReferenceCount() - 1, obj->GetTypeName());
}
} else {
MESOSPHERE_RELEASE_LOG("Handle %08x Obj=%p Ref=%d Type=%s\n", handle, obj.GetPointerUnsafe(), obj->GetReferenceCount() - 1, obj->GetTypeName());
}
if (auto *sync = obj->DynamicCast<KSynchronizationObject *>(); sync != nullptr) {
sync->DumpWaiters();
}
}
}
MESOSPHERE_RELEASE_LOG("%zu(max %zu)/%zu used.\n", handle_table.GetCount(), max_handles, handle_table.GetTableSize());
MESOSPHERE_RELEASE_LOG("\n\n");
}
void DumpMemory(KProcess *process) {
const auto process_id = process->GetId();
MESOSPHERE_RELEASE_LOG("Process ID=%3lu (%s)\n", process_id, process->GetName());
/* Dump the memory blocks. */
process->GetPageTable().DumpMemoryBlocks();
/* Collect information about memory totals. */
const size_t code = process->GetPageTable().GetCodeSize();
const size_t code_data = process->GetPageTable().GetCodeDataSize();
const size_t alias_code = process->GetPageTable().GetAliasCodeSize();
const size_t alias_code_data = process->GetPageTable().GetAliasCodeDataSize();
const size_t normal = process->GetPageTable().GetNormalMemorySize();
const size_t main_stack = process->GetMainStackSize();
size_t shared = 0;
{
KSharedMemory::ListAccessor accessor;
const auto end = accessor.end();
for (auto it = accessor.begin(); it != end; ++it) {
KSharedMemory *shared_mem = static_cast<KSharedMemory *>(std::addressof(*it));
if (shared_mem->GetOwnerProcessId() == process_id) {
shared += shared_mem->GetSize();
}
}
}
/* Dump the totals. */
MESOSPHERE_RELEASE_LOG("---\n");
MESOSPHERE_RELEASE_LOG("Code %8zu KB\n", code / 1_KB);
MESOSPHERE_RELEASE_LOG("CodeData %8zu KB\n", code_data / 1_KB);
MESOSPHERE_RELEASE_LOG("AliasCode %8zu KB\n", alias_code / 1_KB);
MESOSPHERE_RELEASE_LOG("AliasCodeData %8zu KB\n", alias_code_data / 1_KB);
MESOSPHERE_RELEASE_LOG("Heap %8zu KB\n", normal / 1_KB);
MESOSPHERE_RELEASE_LOG("SharedMemory %8zu KB\n", shared / 1_KB);
MESOSPHERE_RELEASE_LOG("InitialStack %8zu KB\n", main_stack / 1_KB);
MESOSPHERE_RELEASE_LOG("---\n");
MESOSPHERE_RELEASE_LOG("TOTAL %8zu KB\n", (code + code_data + alias_code + alias_code_data + normal + main_stack + shared) / 1_KB);
MESOSPHERE_RELEASE_LOG("\n\n");
}
void DumpProcess(KProcess *process) {
MESOSPHERE_RELEASE_LOG("Process ID=%3lu index=%3zu State=%d (%s)\n", process->GetId(), process->GetSlabIndex(), process->GetState(), process->GetName());
}
void DumpPort(const KProcess::ListAccessor &accessor, KProcess *process) {
MESOSPHERE_RELEASE_LOG("Dump Port Process ID=%lu (%s)\n", process->GetId(), process->GetName());
const auto end = accessor.end();
const auto &handle_table = process->GetHandleTable();
const size_t max_handles = handle_table.GetMaxCount();
for (size_t i = 0; i < max_handles; ++i) {
/* Get the object + handle. */
ams::svc::Handle handle = ams::svc::InvalidHandle;
KScopedAutoObject obj = handle_table.GetObjectByIndex(std::addressof(handle), i);
if (obj.IsNull()) {
continue;
}
/* Process the object as a port. */
if (auto *server = obj->DynamicCast<KServerPort *>(); server != nullptr) {
const KClientPort *client = std::addressof(server->GetParent()->GetClientPort());
const uintptr_t port_name = server->GetParent()->GetName();
/* Get the port name. */
char name[9] = {};
{
/* Find the client port process. */
KScopedAutoObject<KProcess> client_port_process;
{
for (auto it = accessor.begin(); it != end && client_port_process.IsNull(); ++it) {
KProcess *cur = static_cast<KProcess *>(std::addressof(*it));
for (size_t j = 0; j < cur->GetHandleTable().GetMaxCount(); ++j) {
ams::svc::Handle cur_h = ams::svc::InvalidHandle;
KScopedAutoObject cur_o = cur->GetHandleTable().GetObjectByIndex(std::addressof(cur_h), j);
if (cur_o.IsNotNull()) {
if (cur_o.GetPointerUnsafe() == client) {
client_port_process = cur;
break;
}
}
}
}
}
/* Read the port name. */
if (client_port_process.IsNotNull()) {
if (R_FAILED(client_port_process->GetPageTable().CopyMemoryFromLinearToKernel(KProcessAddress(name), 8, port_name, KMemoryState_None, KMemoryState_None, KMemoryPermission_UserRead, KMemoryAttribute_None, KMemoryAttribute_None))) {
std::memset(name, 0, sizeof(name));
}
for (size_t i = 0; i < 8 && name[i] != 0; i++) {
if (name[i] > 0x7F) {
std::memset(name, 0, sizeof(name));
break;
}
}
}
}
MESOSPHERE_RELEASE_LOG("%-9s: Handle %08x Obj=%p Cur=%3d Peak=%3d Max=%3d\n", name, handle, obj.GetPointerUnsafe(), client->GetNumSessions(), client->GetPeakSessions(), client->GetMaxSessions());
/* Identify any sessions. */
{
for (auto it = accessor.begin(); it != end; ++it) {
KProcess *cur = static_cast<KProcess *>(std::addressof(*it));
for (size_t j = 0; j < cur->GetHandleTable().GetMaxCount(); ++j) {
ams::svc::Handle cur_h = ams::svc::InvalidHandle;
KScopedAutoObject cur_o = cur->GetHandleTable().GetObjectByIndex(std::addressof(cur_h), j);
if (cur_o.IsNull()) {
continue;
}
if (auto *session = cur_o->DynamicCast<KClientSession *>(); session != nullptr && session->GetParent()->GetParent() == client) {
MESOSPHERE_RELEASE_LOG(" Client %p Server %p %-12s: PID=%3lu\n", session, std::addressof(session->GetParent()->GetServerSession()), cur->GetName(), cur->GetId());
}
}
}
}
}
}
}
}
void DumpThread() {
MESOSPHERE_RELEASE_LOG("Dump Thread\n");
{
/* Lock the list. */
KThread::ListAccessor accessor;
const auto end = accessor.end();
/* Dump each thread. */
for (auto it = accessor.begin(); it != end; ++it) {
DumpThread(static_cast<KThread *>(std::addressof(*it)));
}
}
MESOSPHERE_RELEASE_LOG("\n");
}
void DumpThread(u64 thread_id) {
MESOSPHERE_RELEASE_LOG("Dump Thread\n");
{
/* Find and dump the target thread. */
if (KThread *thread = KThread::GetThreadFromId(thread_id); thread != nullptr) {
ON_SCOPE_EXIT { thread->Close(); };
DumpThread(thread);
}
}
MESOSPHERE_RELEASE_LOG("\n");
}
void DumpThreadCallStack() {
MESOSPHERE_RELEASE_LOG("Dump Thread\n");
{
/* Lock the list. */
KThread::ListAccessor accessor;
const auto end = accessor.end();
/* Dump each thread. */
for (auto it = accessor.begin(); it != end; ++it) {
DumpThreadCallStack(static_cast<KThread *>(std::addressof(*it)));
}
}
MESOSPHERE_RELEASE_LOG("\n");
}
void DumpThreadCallStack(u64 thread_id) {
MESOSPHERE_RELEASE_LOG("Dump Thread\n");
{
/* Find and dump the target thread. */
if (KThread *thread = KThread::GetThreadFromId(thread_id); thread != nullptr) {
ON_SCOPE_EXIT { thread->Close(); };
DumpThreadCallStack(thread);
}
}
MESOSPHERE_RELEASE_LOG("\n");
}
void DumpKernelObject() {
MESOSPHERE_LOG("Dump Kernel Object\n");
{
/* Static slab heaps. */
{
#define DUMP_KSLABOBJ(__OBJECT__) \
MESOSPHERE_RELEASE_LOG(#__OBJECT__ "\n"); \
MESOSPHERE_RELEASE_LOG(" Cur=%3zu Peak=%3zu Max=%3zu\n", __OBJECT__::GetSlabHeapSize() - __OBJECT__::GetNumRemaining(), __OBJECT__::GetPeakIndex(), __OBJECT__::GetSlabHeapSize())
DUMP_KSLABOBJ(KPageBuffer);
DUMP_KSLABOBJ(KEvent);
DUMP_KSLABOBJ(KInterruptEvent);
DUMP_KSLABOBJ(KProcess);
DUMP_KSLABOBJ(KThread);
DUMP_KSLABOBJ(KPort);
DUMP_KSLABOBJ(KSharedMemory);
DUMP_KSLABOBJ(KTransferMemory);
DUMP_KSLABOBJ(KDeviceAddressSpace);
DUMP_KSLABOBJ(KDebug);
DUMP_KSLABOBJ(KSession);
DUMP_KSLABOBJ(KLightSession);
DUMP_KSLABOBJ(KLinkedListNode);
DUMP_KSLABOBJ(KThreadLocalPage);
DUMP_KSLABOBJ(KObjectName);
DUMP_KSLABOBJ(KEventInfo);
DUMP_KSLABOBJ(KSessionRequest);
DUMP_KSLABOBJ(KResourceLimit);
DUMP_KSLABOBJ(KAlpha);
DUMP_KSLABOBJ(KBeta);
#undef DUMP_KSLABOBJ
}
MESOSPHERE_RELEASE_LOG("\n");
/* Dynamic slab heaps. */
{
/* Memory block slabs. */
{
MESOSPHERE_RELEASE_LOG("App Memory Block\n");
auto &app = Kernel::GetApplicationMemoryBlockManager();
MESOSPHERE_RELEASE_LOG(" Cur=%6zu Peak=%6zu Max=%6zu\n", app.GetUsed(), app.GetPeak(), app.GetCount());
MESOSPHERE_RELEASE_LOG("Sys Memory Block\n");
auto &sys = Kernel::GetSystemMemoryBlockManager();
MESOSPHERE_RELEASE_LOG(" Cur=%6zu Peak=%6zu Max=%6zu\n", sys.GetUsed(), sys.GetPeak(), sys.GetCount());
}
/* KBlockInfo slab. */
{
MESOSPHERE_RELEASE_LOG("KBlockInfo\n");
auto &manager = Kernel::GetBlockInfoManager();
MESOSPHERE_RELEASE_LOG(" Cur=%6zu Peak=%6zu Max=%6zu\n", manager.GetUsed(), manager.GetPeak(), manager.GetCount());
}
/* Page Table slab. */
{
MESOSPHERE_RELEASE_LOG("Page Table\n");
auto &manager = Kernel::GetPageTableManager();
MESOSPHERE_RELEASE_LOG(" Cur=%6zu Peak=%6zu Max=%6zu\n", manager.GetUsed(), manager.GetPeak(), manager.GetCount());
}
}
MESOSPHERE_RELEASE_LOG("\n");
/* Process resources. */
{
KProcess::ListAccessor accessor;
size_t process_pts = 0;
const auto end = accessor.end();
for (auto it = accessor.begin(); it != end; ++it) {
KProcess *process = static_cast<KProcess *>(std::addressof(*it));
/* Count the number of threads. */
int threads = 0;
{
KThread::ListAccessor thr_accessor;
const auto thr_end = thr_accessor.end();
for (auto thr_it = thr_accessor.begin(); thr_it != thr_end; ++thr_it) {
KThread *thread = static_cast<KThread *>(std::addressof(*thr_it));
if (thread->GetOwnerProcess() == process) {
++threads;
}
}
}
/* Count the number of events. */
int events = 0;
{
KEvent::ListAccessor ev_accessor;
const auto ev_end = ev_accessor.end();
for (auto ev_it = ev_accessor.begin(); ev_it != ev_end; ++ev_it) {
KEvent *event = static_cast<KEvent *>(std::addressof(*ev_it));
if (event->GetOwner() == process) {
++events;
}
}
}
size_t pts = process->GetPageTable().CountPageTables();
process_pts += pts;
MESOSPHERE_RELEASE_LOG("%-12s: PID=%3lu Thread %4d / Event %4d / PageTable %5zu\n", process->GetName(), process->GetId(), threads, events, pts);
if (process->GetTotalSystemResourceSize() != 0) {
MESOSPHERE_RELEASE_LOG(" System Resource\n");
MESOSPHERE_RELEASE_LOG(" Cur=%6zu Peak=%6zu Max=%6zu\n", process->GetDynamicPageManager().GetUsed(), process->GetDynamicPageManager().GetPeak(), process->GetDynamicPageManager().GetCount());
MESOSPHERE_RELEASE_LOG(" Memory Block\n");
MESOSPHERE_RELEASE_LOG(" Cur=%6zu Peak=%6zu Max=%6zu\n", process->GetMemoryBlockSlabManager().GetUsed(), process->GetMemoryBlockSlabManager().GetPeak(), process->GetMemoryBlockSlabManager().GetCount());
MESOSPHERE_RELEASE_LOG(" Page Table\n");
MESOSPHERE_RELEASE_LOG(" Cur=%6zu Peak=%6zu Max=%6zu\n", process->GetPageTableManager().GetUsed(), process->GetPageTableManager().GetPeak(), process->GetPageTableManager().GetCount());
MESOSPHERE_RELEASE_LOG(" Block Info\n");
MESOSPHERE_RELEASE_LOG(" Cur=%6zu Peak=%6zu Max=%6zu\n", process->GetBlockInfoManager().GetUsed(), process->GetBlockInfoManager().GetPeak(), process->GetBlockInfoManager().GetCount());
}
}
MESOSPHERE_RELEASE_LOG("Process Page Table %zu\n", process_pts);
MESOSPHERE_RELEASE_LOG("Kernel Page Table %zu\n", Kernel::GetKernelPageTable().CountPageTables());
}
MESOSPHERE_RELEASE_LOG("\n");
/* Resource limits. */
{
auto &sys_rl = Kernel::GetSystemResourceLimit();
u64 cur = sys_rl.GetCurrentValue(ams::svc::LimitableResource_PhysicalMemoryMax);
u64 lim = sys_rl.GetLimitValue(ams::svc::LimitableResource_PhysicalMemoryMax);
MESOSPHERE_RELEASE_LOG("System ResourceLimit PhysicalMemory 0x%01x_%08x / 0x%01x_%08x\n", static_cast<u32>(cur >> 32), static_cast<u32>(cur), static_cast<u32>(lim >> 32), static_cast<u32>(lim));
cur = sys_rl.GetCurrentValue(ams::svc::LimitableResource_ThreadCountMax);
lim = sys_rl.GetLimitValue(ams::svc::LimitableResource_ThreadCountMax);
MESOSPHERE_RELEASE_LOG("System ResourceLimit Thread %4lu / %4lu\n", cur, lim);
cur = sys_rl.GetCurrentValue(ams::svc::LimitableResource_EventCountMax);
lim = sys_rl.GetLimitValue(ams::svc::LimitableResource_EventCountMax);
MESOSPHERE_RELEASE_LOG("System ResourceLimit Event %4lu / %4lu\n", cur, lim);
cur = sys_rl.GetCurrentValue(ams::svc::LimitableResource_TransferMemoryCountMax);
lim = sys_rl.GetLimitValue(ams::svc::LimitableResource_TransferMemoryCountMax);
MESOSPHERE_RELEASE_LOG("System ResourceLimit TransferMemory %4lu / %4lu\n", cur, lim);
cur = sys_rl.GetCurrentValue(ams::svc::LimitableResource_SessionCountMax);
lim = sys_rl.GetLimitValue(ams::svc::LimitableResource_SessionCountMax);
MESOSPHERE_RELEASE_LOG("System ResourceLimit Session %4lu / %4lu\n", cur, lim);
{
KResourceLimit::ListAccessor accessor;
const auto end = accessor.end();
for (auto it = accessor.begin(); it != end; ++it) {
KResourceLimit *rl = static_cast<KResourceLimit *>(std::addressof(*it));
cur = rl->GetCurrentValue(ams::svc::LimitableResource_PhysicalMemoryMax);
lim = rl->GetLimitValue(ams::svc::LimitableResource_PhysicalMemoryMax);
MESOSPHERE_RELEASE_LOG("ResourceLimit %zu PhysicalMemory 0x%01x_%08x / 0x%01x_%08x\n", rl->GetSlabIndex(), static_cast<u32>(cur >> 32), static_cast<u32>(cur), static_cast<u32>(lim >> 32), static_cast<u32>(lim));
}
}
}
MESOSPHERE_RELEASE_LOG("\n");
/* Memory Manager. */
{
auto &mm = Kernel::GetMemoryManager();
u64 max = mm.GetSize();
u64 cur = max - mm.GetFreeSize();
MESOSPHERE_RELEASE_LOG("Kernel Heap Size 0x%01x_%08x / 0x%01x_%08x\n", static_cast<u32>(cur >> 32), static_cast<u32>(cur), static_cast<u32>(max >> 32), static_cast<u32>(max));
MESOSPHERE_RELEASE_LOG("\n");
max = mm.GetSize(KMemoryManager::Pool_Application);
cur = max - mm.GetFreeSize(KMemoryManager::Pool_Application);
MESOSPHERE_RELEASE_LOG("Application 0x%01x_%08x / 0x%01x_%08x\n", static_cast<u32>(cur >> 32), static_cast<u32>(cur), static_cast<u32>(max >> 32), static_cast<u32>(max));
mm.DumpFreeList(KMemoryManager::Pool_Application);
MESOSPHERE_RELEASE_LOG("\n");
max = mm.GetSize(KMemoryManager::Pool_Applet);
cur = max - mm.GetFreeSize(KMemoryManager::Pool_Applet);
MESOSPHERE_RELEASE_LOG("Applet 0x%01x_%08x / 0x%01x_%08x\n", static_cast<u32>(cur >> 32), static_cast<u32>(cur), static_cast<u32>(max >> 32), static_cast<u32>(max));
mm.DumpFreeList(KMemoryManager::Pool_Applet);
MESOSPHERE_RELEASE_LOG("\n");
max = mm.GetSize(KMemoryManager::Pool_System);
cur = max - mm.GetFreeSize(KMemoryManager::Pool_System);
MESOSPHERE_RELEASE_LOG("System 0x%01x_%08x / 0x%01x_%08x\n", static_cast<u32>(cur >> 32), static_cast<u32>(cur), static_cast<u32>(max >> 32), static_cast<u32>(max));
mm.DumpFreeList(KMemoryManager::Pool_System);
MESOSPHERE_RELEASE_LOG("\n");
max = mm.GetSize(KMemoryManager::Pool_SystemNonSecure);
cur = max - mm.GetFreeSize(KMemoryManager::Pool_SystemNonSecure);
MESOSPHERE_RELEASE_LOG("SystemNonSecure 0x%01x_%08x / 0x%01x_%08x\n", static_cast<u32>(cur >> 32), static_cast<u32>(cur), static_cast<u32>(max >> 32), static_cast<u32>(max));
mm.DumpFreeList(KMemoryManager::Pool_SystemNonSecure);
MESOSPHERE_RELEASE_LOG("\n");
}
MESOSPHERE_RELEASE_LOG("\n");
}
MESOSPHERE_RELEASE_LOG("\n");
}
void DumpHandle() {
MESOSPHERE_RELEASE_LOG("Dump Handle\n");
{
/* Lock the list. */
KProcess::ListAccessor accessor;
const auto end = accessor.end();
/* Dump each process. */
for (auto it = accessor.begin(); it != end; ++it) {
DumpHandle(accessor, static_cast<KProcess *>(std::addressof(*it)));
}
}
MESOSPHERE_RELEASE_LOG("\n");
}
void DumpHandle(u64 process_id) {
MESOSPHERE_RELEASE_LOG("Dump Handle\n");
{
/* Find and dump the target process. */
if (KProcess *process = KProcess::GetProcessFromId(process_id); process != nullptr) {
ON_SCOPE_EXIT { process->Close(); };
/* Lock the list. */
KProcess::ListAccessor accessor;
DumpHandle(accessor, process);
}
}
MESOSPHERE_RELEASE_LOG("\n");
}
void DumpKernelMemory() {
MESOSPHERE_RELEASE_LOG("Dump Kernel Memory Info\n");
{
Kernel::GetKernelPageTable().DumpMemoryBlocks();
}
MESOSPHERE_RELEASE_LOG("\n");
}
void DumpMemory() {
MESOSPHERE_RELEASE_LOG("Dump Memory Info\n");
{
/* Lock the list. */
KProcess::ListAccessor accessor;
const auto end = accessor.end();
/* Dump each process. */
for (auto it = accessor.begin(); it != end; ++it) {
DumpMemory(static_cast<KProcess *>(std::addressof(*it)));
}
}
MESOSPHERE_RELEASE_LOG("\n");
}
void DumpMemory(u64 process_id) {
MESOSPHERE_RELEASE_LOG("Dump Memory Info\n");
{
/* Find and dump the target process. */
if (KProcess *process = KProcess::GetProcessFromId(process_id); process != nullptr) {
ON_SCOPE_EXIT { process->Close(); };
DumpMemory(process);
}
}
MESOSPHERE_RELEASE_LOG("\n");
}
void DumpProcess() {
MESOSPHERE_RELEASE_LOG("Dump Process\n");
{
/* Lock the list. */
KProcess::ListAccessor accessor;
const auto end = accessor.end();
/* Dump each process. */
for (auto it = accessor.begin(); it != end; ++it) {
DumpProcess(static_cast<KProcess *>(std::addressof(*it)));
}
}
MESOSPHERE_RELEASE_LOG("\n");
}
void DumpProcess(u64 process_id) {
MESOSPHERE_RELEASE_LOG("Dump Process\n");
{
/* Find and dump the target process. */
if (KProcess *process = KProcess::GetProcessFromId(process_id); process != nullptr) {
ON_SCOPE_EXIT { process->Close(); };
DumpProcess(process);
}
}
MESOSPHERE_RELEASE_LOG("\n");
}
void DumpPort() {
MESOSPHERE_RELEASE_LOG("Dump Port\n");
{
/* Lock the list. */
KProcess::ListAccessor accessor;
const auto end = accessor.end();
/* Dump each process. */
for (auto it = accessor.begin(); it != end; ++it) {
DumpPort(accessor, static_cast<KProcess *>(std::addressof(*it)));
}
}
MESOSPHERE_RELEASE_LOG("\n");
}
void DumpPort(u64 process_id) {
MESOSPHERE_RELEASE_LOG("Dump Port\n");
{
/* Find and dump the target process. */
if (KProcess *process = KProcess::GetProcessFromId(process_id); process != nullptr) {
ON_SCOPE_EXIT { process->Close(); };
/* Lock the list. */
KProcess::ListAccessor accessor;
DumpPort(accessor, process);
}
}
MESOSPHERE_RELEASE_LOG("\n");
}
}

View File

@ -72,8 +72,8 @@ namespace ams::kern {
void DumpMemoryInfo(const KMemoryInfo &info) { void DumpMemoryInfo(const KMemoryInfo &info) {
const char *state = GetMemoryStateName(info.state); const char *state = GetMemoryStateName(info.state);
const char *perm = GetMemoryPermissionString(info); const char *perm = GetMemoryPermissionString(info);
const void *start = reinterpret_cast<void *>(info.GetAddress()); const uintptr_t start = info.GetAddress();
const void *end = reinterpret_cast<void *>(info.GetLastAddress()); const uintptr_t end = info.GetLastAddress();
const size_t kb = info.GetSize() / 1_KB; const size_t kb = info.GetSize() / 1_KB;
const char l = (info.attribute & KMemoryAttribute_Locked) ? 'L' : '-'; const char l = (info.attribute & KMemoryAttribute_Locked) ? 'L' : '-';
@ -81,7 +81,7 @@ namespace ams::kern {
const char d = (info.attribute & KMemoryAttribute_DeviceShared) ? 'D' : '-'; const char d = (info.attribute & KMemoryAttribute_DeviceShared) ? 'D' : '-';
const char u = (info.attribute & KMemoryAttribute_Uncached) ? 'U' : '-'; const char u = (info.attribute & KMemoryAttribute_Uncached) ? 'U' : '-';
MESOSPHERE_LOG("%p - %p (%9zu KB) %s %s %c%c%c%c [%d, %d]\n", start, end, kb, perm, state, l, i, d, u, info.ipc_lock_count, info.device_use_count); MESOSPHERE_LOG("0x%10lx - 0x%10lx (%9zu KB) %s %s %c%c%c%c [%d, %d]\n", start, end, kb, perm, state, l, i, d, u, info.ipc_lock_count, info.device_use_count);
} }
} }

View File

@ -26,14 +26,14 @@ namespace ams::kern {
constexpr size_t CarveoutSizeMax = 512_MB - CarveoutAlignment; constexpr size_t CarveoutSizeMax = 512_MB - CarveoutAlignment;
ALWAYS_INLINE bool SetupUartPhysicalMemoryRegion() { ALWAYS_INLINE bool SetupUartPhysicalMemoryRegion() {
#if defined(MESOSPHERE_DEBUG_LOG_USE_UART_A) #if defined(MESOSPHERE_DEBUG_LOG_USE_UART)
return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006000, 0x40, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap); switch (KSystemControl::Init::GetDebugLogUartPort()) {
#elif defined(MESOSPHERE_DEBUG_LOG_USE_UART_B) case 0: return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006000, 0x40, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap);
return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006040, 0x40, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap); case 1: return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006040, 0x40, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap);
#elif defined(MESOSPHERE_DEBUG_LOG_USE_UART_C) case 2: return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006200, 0x100, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap);
return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006200, 0x100, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap); case 3: return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006300, 0x100, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap);
#elif defined(MESOSPHERE_DEBUG_LOG_USE_UART_D) default: return false;
return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006300, 0x100, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap); }
#elif defined(MESOSPHERE_DEBUG_LOG_USE_IRAM_RINGBUFFER) #elif defined(MESOSPHERE_DEBUG_LOG_USE_IRAM_RINGBUFFER)
return true; return true;
#else #else

View File

@ -132,4 +132,29 @@ namespace ams::kern {
return util::AlignUp(overhead_size, PageSize); return util::AlignUp(overhead_size, PageSize);
} }
void KPageHeap::DumpFreeList() const {
MESOSPHERE_RELEASE_LOG("KPageHeap::DumpFreeList %p\n", this);
for (size_t i = 0; i < this->num_blocks; ++i) {
const size_t block_size = this->blocks[i].GetSize();
const char *suffix;
size_t size;
if (block_size >= 1_GB) {
suffix = "GiB";
size = block_size / 1_GB;
} else if (block_size >= 1_MB) {
suffix = "MiB";
size = block_size / 1_MB;
} else if (block_size >= 1_KB) {
suffix = "KiB";
size = block_size / 1_KB;
} else {
suffix = "B";
size = block_size;
}
MESOSPHERE_RELEASE_LOG(" %4zu %s block x %zu\n", size, suffix, this->blocks[i].GetNumFreeBlocks());
}
}
} }

View File

@ -1018,6 +1018,39 @@ namespace ams::kern {
return address; return address;
} }
size_t KPageTableBase::GetSize(KMemoryState state) const {
/* Lock the table. */
KScopedLightLock lk(this->general_lock);
/* Iterate, counting blocks with the desired state. */
size_t total_size = 0;
for (KMemoryBlockManager::const_iterator it = this->memory_block_manager.FindIterator(this->address_space_start); it != this->memory_block_manager.end(); ++it) {
/* Get the memory info. */
const KMemoryInfo info = it->GetMemoryInfo();
if (info.GetState() == state) {
total_size += info.GetSize();
}
}
return total_size;
}
size_t KPageTableBase::GetCodeSize() const {
return this->GetSize(KMemoryState_Code);
}
size_t KPageTableBase::GetCodeDataSize() const {
return this->GetSize(KMemoryState_CodeData);
}
size_t KPageTableBase::GetAliasCodeSize() const {
return this->GetSize(KMemoryState_AliasCode);
}
size_t KPageTableBase::GetAliasCodeDataSize() const {
return this->GetSize(KMemoryState_AliasCodeData);
}
Result KPageTableBase::AllocateAndMapPagesImpl(PageLinkedList *page_list, KProcessAddress address, size_t num_pages, KMemoryPermission perm) { Result KPageTableBase::AllocateAndMapPagesImpl(PageLinkedList *page_list, KProcessAddress address, size_t num_pages, KMemoryPermission perm) {
MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); MESOSPHERE_ASSERT(this->IsLockedByCurrentThread());

View File

@ -1367,4 +1367,36 @@ namespace ams::kern {
#pragma GCC pop_options #pragma GCC pop_options
void KServerSession::Dump() {
MESOSPHERE_ASSERT_THIS();
KScopedLightLock lk(this->lock);
{
KScopedSchedulerLock sl;
MESOSPHERE_RELEASE_LOG("Dump Session %p\n", this);
/* Dump current request. */
bool has_request = false;
if (this->current_request != nullptr) {
KThread *thread = this->current_request->GetThread();
const s32 thread_id = thread != nullptr ? static_cast<s32>(thread->GetId()) : -1;
MESOSPHERE_RELEASE_LOG(" CurrentReq %p Thread=%p ID=%d\n", this->current_request, thread, thread_id);
has_request = true;
}
/* Dump all rqeuests in list. */
for (auto it = this->request_list.begin(); it != this->request_list.end(); ++it) {
KThread *thread = it->GetThread();
const s32 thread_id = thread != nullptr ? static_cast<s32>(thread->GetId()) : -1;
MESOSPHERE_RELEASE_LOG(" Req %p Thread=%p ID=%d\n", this->current_request, thread, thread_id);
has_request = true;
}
/* If we didn't have any requests, print so. */
if (!has_request) {
MESOSPHERE_RELEASE_LOG(" None\n");
}
}
}
} }

View File

@ -81,8 +81,15 @@ namespace ams::kern {
/* Add the waiters. */ /* Add the waiters. */
for (auto i = 0; i < num_objects; ++i) { for (auto i = 0; i < num_objects; ++i) {
thread_nodes[i].thread = thread; thread_nodes[i].thread = thread;
thread_nodes[i].next = objects[i]->thread_list_root; thread_nodes[i].next = nullptr;
objects[i]->thread_list_root = std::addressof(thread_nodes[i]);
if (objects[i]->thread_list_tail == nullptr) {
objects[i]->thread_list_head = std::addressof(thread_nodes[i]);
} else {
objects[i]->thread_list_tail->next = std::addressof(thread_nodes[i]);
}
objects[i]->thread_list_tail = std::addressof(thread_nodes[i]);
} }
/* Mark the thread as waiting. */ /* Mark the thread as waiting. */
@ -111,11 +118,22 @@ namespace ams::kern {
for (auto i = 0; i < num_objects; ++i) { for (auto i = 0; i < num_objects; ++i) {
/* Unlink the object from the list. */ /* Unlink the object from the list. */
ThreadListNode **link = std::addressof(objects[i]->thread_list_root); ThreadListNode *prev_ptr = reinterpret_cast<ThreadListNode *>(std::addressof(objects[i]->thread_list_head));
while (*link != std::addressof(thread_nodes[i])) { ThreadListNode *prev_val = nullptr;
link = std::addressof((*link)->next); ThreadListNode *prev, *tail_prev;
do {
prev = prev_ptr;
prev_ptr = prev_ptr->next;
tail_prev = prev_val;
prev_val = prev_ptr;
} while (prev_ptr != std::addressof(thread_nodes[i]));
if (objects[i]->thread_list_tail == std::addressof(thread_nodes[i])) {
objects[i]->thread_list_tail = tail_prev;
} }
*link = thread_nodes[i].next;
prev->next = thread_nodes[i].next;
if (objects[i] == synced_obj) { if (objects[i] == synced_obj) {
sync_index = i; sync_index = i;
@ -139,7 +157,7 @@ namespace ams::kern {
} }
/* Iterate over each thread. */ /* Iterate over each thread. */
for (auto *cur_node = this->thread_list_root; cur_node != nullptr; cur_node = cur_node->next) { for (auto *cur_node = this->thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
KThread *thread = cur_node->thread; KThread *thread = cur_node->thread;
if (thread->GetState() == KThread::ThreadState_Waiting) { if (thread->GetState() == KThread::ThreadState_Waiting) {
thread->SetSyncedObject(this, result); thread->SetSyncedObject(this, result);
@ -148,7 +166,7 @@ namespace ams::kern {
} }
} }
void KSynchronizationObject::DebugWaiters() { void KSynchronizationObject::DumpWaiters() {
MESOSPHERE_ASSERT_THIS(); MESOSPHERE_ASSERT_THIS();
/* If debugging, dump the list of waiters. */ /* If debugging, dump the list of waiters. */
@ -158,8 +176,7 @@ namespace ams::kern {
MESOSPHERE_RELEASE_LOG("Threads waiting on %p:\n", this); MESOSPHERE_RELEASE_LOG("Threads waiting on %p:\n", this);
bool has_waiters = false; for (auto *cur_node = this->thread_list_head; cur_node != nullptr; cur_node = cur_node->next) {
for (auto *cur_node = this->thread_list_root; cur_node != nullptr; cur_node = cur_node->next) {
KThread *thread = cur_node->thread; KThread *thread = cur_node->thread;
if (KProcess *process = thread->GetOwnerProcess(); process != nullptr) { if (KProcess *process = thread->GetOwnerProcess(); process != nullptr) {
@ -167,12 +184,10 @@ namespace ams::kern {
} else { } else {
MESOSPHERE_RELEASE_LOG(" %p tid=%ld (Kernel)\n", thread, thread->GetId()); MESOSPHERE_RELEASE_LOG(" %p tid=%ld (Kernel)\n", thread, thread->GetId());
} }
has_waiters = true;
} }
/* If we didn't have any waiters, print so. */ /* If we didn't have any waiters, print so. */
if (!has_waiters) { if (this->thread_list_head != nullptr) {
MESOSPHERE_RELEASE_LOG(" None\n"); MESOSPHERE_RELEASE_LOG(" None\n");
} }
} }

View File

@ -24,6 +24,15 @@ namespace ams::kern {
return KernelVirtualAddressSpaceBase <= key_uptr && key_uptr <= KernelVirtualAddressSpaceLast; return KernelVirtualAddressSpaceBase <= key_uptr && key_uptr <= KernelVirtualAddressSpaceLast;
} }
void InitializeKernelStack(uintptr_t stack_top) {
#if defined(MESOSPHERE_ENABLE_KERNEL_STACK_USAGE)
const uintptr_t stack_bottom = stack_top - PageSize;
std::memset(reinterpret_cast<void *>(stack_bottom), 0xCC, PageSize - sizeof(KThread::StackParameters));
#else
MESOSPHERE_UNUSED(stack_top);
#endif
}
void CleanupKernelStack(uintptr_t stack_top) { void CleanupKernelStack(uintptr_t stack_top) {
const uintptr_t stack_bottom = stack_top - PageSize; const uintptr_t stack_bottom = stack_top - PageSize;
@ -153,6 +162,11 @@ namespace ams::kern {
this->resource_limit_release_hint = 0; this->resource_limit_release_hint = 0;
this->cpu_time = 0; this->cpu_time = 0;
/* Setup our kernel stack. */
if (type != ThreadType_Main) {
InitializeKernelStack(reinterpret_cast<uintptr_t>(kern_stack_top));
}
/* Clear our stack parameters. */ /* Clear our stack parameters. */
std::memset(static_cast<void *>(std::addressof(this->GetStackParameters())), 0, sizeof(StackParameters)); std::memset(static_cast<void *>(std::addressof(this->GetStackParameters())), 0, sizeof(StackParameters));
@ -803,6 +817,26 @@ namespace ams::kern {
KScheduler::OnThreadStateChanged(this, old_state); KScheduler::OnThreadStateChanged(this, old_state);
} }
size_t KThread::GetKernelStackUsage() const {
MESOSPHERE_ASSERT_THIS();
MESOSPHERE_ASSERT(this->kernel_stack_top != nullptr);
#if defined(MESOSPHERE_ENABLE_KERNEL_STACK_USAGE)
const u8 *stack = static_cast<const u8 *>(this->kernel_stack_top) - PageSize;
size_t i;
for (i = 0; i < PageSize; ++i) {
if (stack[i] != 0xCC) {
break;
}
}
return PageSize - i;
#else
return 0;
#endif
}
Result KThread::SetActivity(ams::svc::ThreadActivity activity) { Result KThread::SetActivity(ams::svc::ThreadActivity activity) {
/* Lock ourselves. */ /* Lock ourselves. */
KScopedLightLock lk(this->activity_pause_lock); KScopedLightLock lk(this->activity_pause_lock);

View File

@ -97,18 +97,13 @@ namespace ams::kern {
/* Print the state. */ /* Print the state. */
MESOSPHERE_RELEASE_LOG("Core[%d] Current State:\n", core_id); MESOSPHERE_RELEASE_LOG("Core[%d] Current State:\n", core_id);
#ifdef ATMOSPHERE_ARCH_ARM64 /* Print registers and user backtrace. */
/* Print registers. */ KDebug::PrintRegister();
if (core_ctx != nullptr) { KDebug::PrintBacktrace();
MESOSPHERE_RELEASE_LOG(" Registers:\n");
for (size_t i = 0; i < util::size(core_ctx->x); ++i) {
MESOSPHERE_RELEASE_LOG(" X[%02zx]: %p\n", i, reinterpret_cast<void *>(core_ctx->x[i]));
}
MESOSPHERE_RELEASE_LOG(" SP: %p\n", reinterpret_cast<void *>(core_ctx->x[30]));
}
/* Print backtrace. */ #ifdef ATMOSPHERE_ARCH_ARM64
MESOSPHERE_RELEASE_LOG(" Backtrace:\n"); /* Print kernel backtrace. */
MESOSPHERE_RELEASE_LOG("Backtrace:\n");
uintptr_t fp = core_ctx != nullptr ? core_ctx->x[29] : reinterpret_cast<uintptr_t>(__builtin_frame_address(0)); uintptr_t fp = core_ctx != nullptr ? core_ctx->x[29] : reinterpret_cast<uintptr_t>(__builtin_frame_address(0));
for (size_t i = 0; i < 32 && fp && util::IsAligned(fp, 0x10) && cpu::GetPhysicalAddressWritable(nullptr, fp, true); i++) { for (size_t i = 0; i < 32 && fp && util::IsAligned(fp, 0x10) && cpu::GetPhysicalAddressWritable(nullptr, fp, true); i++) {
struct { struct {
@ -137,7 +132,7 @@ namespace ams::kern {
} }
NORETURN void PanicImpl(const char *file, int line, const char *format, ...) { NORETURN WEAK_SYMBOL void PanicImpl(const char *file, int line, const char *format, ...) {
#ifdef MESOSPHERE_BUILD_FOR_DEBUGGING #ifdef MESOSPHERE_BUILD_FOR_DEBUGGING
/* Wait for it to be our turn to print. */ /* Wait for it to be our turn to print. */
WaitCoreTicket(); WaitCoreTicket();
@ -158,7 +153,7 @@ namespace ams::kern {
StopSystem(); StopSystem();
} }
NORETURN void PanicImpl() { NORETURN WEAK_SYMBOL void PanicImpl() {
StopSystem(); StopSystem();
} }

View File

@ -21,16 +21,18 @@ namespace ams::kern::svc {
namespace { namespace {
[[maybe_unused]] void PrintBreak(ams::svc::BreakReason break_reason) { #if defined(MESOSPHERE_BUILD_FOR_DEBUGGING)
void PrintBreak(ams::svc::BreakReason break_reason) {
/* Print that break was called. */ /* Print that break was called. */
MESOSPHERE_RELEASE_LOG("%s: svc::Break(%d) was called, pid=%ld, tid=%ld\n", GetCurrentProcess().GetName(), static_cast<s32>(break_reason), GetCurrentProcess().GetId(), GetCurrentThread().GetId()); MESOSPHERE_RELEASE_LOG("%s: svc::Break(%d) was called, pid=%ld, tid=%ld\n", GetCurrentProcess().GetName(), static_cast<s32>(break_reason), GetCurrentProcess().GetId(), GetCurrentThread().GetId());
/* Print the current thread's registers. */ /* Print the current thread's registers. */
/* TODO: KDebug::PrintRegisters(); */ KDebug::PrintRegister();
/* Print a backtrace. */ /* Print a backtrace. */
/* TODO: KDebug::PrintBacktrace(); */ KDebug::PrintBacktrace();
} }
#endif
void Break(ams::svc::BreakReason break_reason, uintptr_t address, size_t size) { void Break(ams::svc::BreakReason break_reason, uintptr_t address, size_t size) {
/* Determine whether the break is only a notification. */ /* Determine whether the break is only a notification. */
@ -38,7 +40,7 @@ namespace ams::kern::svc {
/* If the break isn't a notification, print it. */ /* If the break isn't a notification, print it. */
if (!is_notification) { if (!is_notification) {
#ifdef MESOSPHERE_BUILD_FOR_DEBUGGING #if defined(MESOSPHERE_BUILD_FOR_DEBUGGING)
PrintBreak(break_reason); PrintBreak(break_reason);
#endif #endif
} }

View File

@ -59,6 +59,7 @@ namespace ams::kern::svc {
case ams::svc::InfoType_TotalNonSystemMemorySize: case ams::svc::InfoType_TotalNonSystemMemorySize:
case ams::svc::InfoType_UsedNonSystemMemorySize: case ams::svc::InfoType_UsedNonSystemMemorySize:
case ams::svc::InfoType_IsApplication: case ams::svc::InfoType_IsApplication:
case ams::svc::InfoType_FreeThreadCount:
{ {
/* These info types don't support non-zero subtypes. */ /* These info types don't support non-zero subtypes. */
R_UNLESS(info_subtype == 0, svc::ResultInvalidCombination()); R_UNLESS(info_subtype == 0, svc::ResultInvalidCombination());
@ -125,6 +126,15 @@ namespace ams::kern::svc {
case ams::svc::InfoType_IsApplication: case ams::svc::InfoType_IsApplication:
*out = process->IsApplication(); *out = process->IsApplication();
break; break;
case ams::svc::InfoType_FreeThreadCount:
if (KResourceLimit *resource_limit = process->GetResourceLimit(); resource_limit != nullptr) {
const auto current_value = resource_limit->GetCurrentValue(ams::svc::LimitableResource_ThreadCountMax);
const auto limit_value = resource_limit->GetLimitValue(ams::svc::LimitableResource_ThreadCountMax);
*out = limit_value - current_value;
} else {
*out = 0;
}
break;
MESOSPHERE_UNREACHABLE_DEFAULT_CASE(); MESOSPHERE_UNREACHABLE_DEFAULT_CASE();
} }
} }

View File

@ -22,19 +22,85 @@ namespace ams::kern::svc {
namespace { namespace {
void KernelDebug(ams::svc::KernelDebugType kern_debug_type, uint64_t arg0, uint64_t arg1, uint64_t arg2) { void KernelDebug(ams::svc::KernelDebugType kern_debug_type, uint64_t arg0, uint64_t arg1, uint64_t arg2) {
#ifdef ATMOSPHERE_BUILD_FOR_DEBUGGING
{
/* TODO: Implement Kernel Debugging. */
}
#else
{
MESOSPHERE_UNUSED(kern_debug_type, arg0, arg1, arg2); MESOSPHERE_UNUSED(kern_debug_type, arg0, arg1, arg2);
#ifdef MESOSPHERE_BUILD_FOR_DEBUGGING
{
switch (kern_debug_type) {
case ams::svc::KernelDebugType_Thread:
if (arg0 == static_cast<u64>(-1)) {
KDumpObject::DumpThread();
} else {
KDumpObject::DumpThread(arg0);
}
break;
case ams::svc::KernelDebugType_ThreadCallStack:
if (arg0 == static_cast<u64>(-1)) {
KDumpObject::DumpThreadCallStack();
} else {
KDumpObject::DumpThreadCallStack(arg0);
}
break;
case ams::svc::KernelDebugType_KernelObject:
KDumpObject::DumpKernelObject();
break;
case ams::svc::KernelDebugType_Handle:
if (arg0 == static_cast<u64>(-1)) {
KDumpObject::DumpHandle();
} else {
KDumpObject::DumpHandle(arg0);
}
break;
case ams::svc::KernelDebugType_Memory:
if (arg0 == static_cast<u64>(-2)) {
KDumpObject::DumpKernelMemory();
} else if (arg0 == static_cast<u64>(-1)) {
KDumpObject::DumpMemory();
} else {
KDumpObject::DumpMemory(arg0);
}
break;
case ams::svc::KernelDebugType_Process:
if (arg0 == static_cast<u64>(-1)) {
KDumpObject::DumpProcess();
} else {
KDumpObject::DumpProcess(arg0);
}
break;
case ams::svc::KernelDebugType_SuspendProcess:
if (KProcess *process = KProcess::GetProcessFromId(arg0); process != nullptr) {
ON_SCOPE_EXIT { process->Close(); };
if (R_SUCCEEDED(process->SetActivity(ams::svc::ProcessActivity_Paused))) {
MESOSPHERE_RELEASE_LOG("Suspend Process ID=%3lu\n", process->GetId());
}
}
break;
case ams::svc::KernelDebugType_ResumeProcess:
if (KProcess *process = KProcess::GetProcessFromId(arg0); process != nullptr) {
ON_SCOPE_EXIT { process->Close(); };
if (R_SUCCEEDED(process->SetActivity(ams::svc::ProcessActivity_Runnable))) {
MESOSPHERE_RELEASE_LOG("Resume Process ID=%3lu\n", process->GetId());
}
}
break;
case ams::svc::KernelDebugType_Port:
if (arg0 == static_cast<u64>(-1)) {
KDumpObject::DumpPort();
} else {
KDumpObject::DumpPort(arg0);
}
break;
default:
break;
}
} }
#endif #endif
} }
void ChangeKernelTraceState(ams::svc::KernelTraceState kern_trace_state) { void ChangeKernelTraceState(ams::svc::KernelTraceState kern_trace_state) {
#ifdef ATMOSPHERE_BUILD_FOR_DEBUGGING #ifdef MESOSPHERE_BUILD_FOR_DEBUGGING
{ {
switch (kern_trace_state) { switch (kern_trace_state) {
case ams::svc::KernelTraceState_Enabled: case ams::svc::KernelTraceState_Enabled:

View File

@ -35,12 +35,11 @@
#include <stratosphere/fs/fs_substorage.hpp> #include <stratosphere/fs/fs_substorage.hpp>
#include <stratosphere/fs/fs_memory_storage.hpp> #include <stratosphere/fs/fs_memory_storage.hpp>
#include <stratosphere/fs/fs_remote_storage.hpp> #include <stratosphere/fs/fs_remote_storage.hpp>
#include <stratosphere/fs/fs_file_storage.hpp> #include <stratosphere/fs/common/fs_file_storage.hpp>
#include <stratosphere/fs/fs_query_range.hpp> #include <stratosphere/fs/fs_query_range.hpp>
#include <stratosphere/fs/fs_speed_emulation.hpp> #include <stratosphere/fs/fs_speed_emulation.hpp>
#include <stratosphere/fs/impl/fs_common_mount_name.hpp> #include <stratosphere/fs/impl/fs_common_mount_name.hpp>
#include <stratosphere/fs/fs_mount.hpp> #include <stratosphere/fs/fs_mount.hpp>
#include <stratosphere/fs/fs_path_tool.hpp>
#include <stratosphere/fs/fs_path_utils.hpp> #include <stratosphere/fs/fs_path_utils.hpp>
#include <stratosphere/fs/fs_filesystem_utils.hpp> #include <stratosphere/fs/fs_filesystem_utils.hpp>
#include <stratosphere/fs/fs_romfs_filesystem.hpp> #include <stratosphere/fs/fs_romfs_filesystem.hpp>
@ -60,4 +59,5 @@
#include <stratosphere/fs/fs_sd_card.hpp> #include <stratosphere/fs/fs_sd_card.hpp>
#include <stratosphere/fs/fs_signed_system_partition.hpp> #include <stratosphere/fs/fs_signed_system_partition.hpp>
#include <stratosphere/fs/fs_system_data.hpp> #include <stratosphere/fs/fs_system_data.hpp>
#include <stratosphere/fs/fs_program_index_map_info.hpp>
#include <stratosphere/fs/impl/fs_access_log_impl.hpp> #include <stratosphere/fs/impl/fs_access_log_impl.hpp>

View File

@ -14,9 +14,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "fs_dbm_rom_types.hpp" #include <stratosphere/fs/common/fs_dbm_rom_types.hpp>
#include "fs_dbm_rom_path_tool.hpp" #include <stratosphere/fs/common/fs_dbm_rom_path_tool.hpp>
#include "fs_dbm_rom_key_value_storage.hpp" #include <stratosphere/fs/common/fs_dbm_rom_key_value_storage.hpp>
namespace ams::fs { namespace ams::fs {
@ -33,19 +33,23 @@ namespace ams::fs {
using DirectoryInfo = RomDirectoryInfo; using DirectoryInfo = RomDirectoryInfo;
using FileInfo = RomFileInfo; using FileInfo = RomFileInfo;
static constexpr RomFileId ConvertToFileId(Position pos) { static constexpr RomFileId PositionToFileId(Position pos) {
return static_cast<RomFileId>(pos); return static_cast<RomFileId>(pos);
} }
static constexpr Position FileIdToPosition(RomFileId id) {
return static_cast<Position>(id);
}
private: private:
static constexpr inline Position InvalidPosition = ~Position(); static constexpr inline Position InvalidPosition = ~Position();
static constexpr inline Position RootPosition = 0; static constexpr inline Position RootPosition = 0;
static constexpr inline size_t ReservedDirectoryCount = 1; static constexpr inline size_t ReservedDirectoryCount = 1;
static constexpr RomDirectoryId ConvertToDirectoryId(Position pos) { static constexpr RomDirectoryId PositionToDirectoryId(Position pos) {
return static_cast<RomDirectoryId>(pos); return static_cast<RomDirectoryId>(pos);
} }
static constexpr Position ConvertToPosition(RomDirectoryId id) { static constexpr Position DirectoryIdToPosition(RomDirectoryId id) {
return static_cast<Position>(id); return static_cast<Position>(id);
} }
@ -67,20 +71,20 @@ namespace ams::fs {
static constexpr inline u32 MaxKeyLength = RomPathTool::MaxPathLength; static constexpr inline u32 MaxKeyLength = RomPathTool::MaxPathLength;
template<typename ImplKeyType, typename ClientKeyType, typename ValueType> template<typename ImplKeyType, typename ClientKeyType, typename ValueType>
class EntryMapTable : public RomKeyValueStorage<ImplKeyType, ValueType, MaxKeyLength> { class EntryMapTable : public KeyValueRomStorageTemplate<ImplKeyType, ValueType, MaxKeyLength> {
public: public:
using ImplKey = ImplKeyType; using ImplKey = ImplKeyType;
using ClientKey = ClientKeyType; using ClientKey = ClientKeyType;
using Value = ValueType; using Value = ValueType;
using Position = HierarchicalRomFileTable::Position; using Position = HierarchicalRomFileTable::Position;
using Base = RomKeyValueStorage<ImplKeyType, ValueType, MaxKeyLength>; using Base = KeyValueRomStorageTemplate<ImplKeyType, ValueType, MaxKeyLength>;
public: public:
Result Add(Position *out, const ClientKeyType &key, const Value &value) { Result Add(Position *out, const ClientKeyType &key, const Value &value) {
return Base::AddImpl(out, key.key, key.Hash(), key.name.path, key.name.length * sizeof(RomPathChar), value); return Base::AddInternal(out, key.key, key.Hash(), key.name.path, key.name.length * sizeof(RomPathChar), value);
} }
Result Get(Position *out_pos, Value *out_val, const ClientKeyType &key) { Result Get(Position *out_pos, Value *out_val, const ClientKeyType &key) {
return Base::GetImpl(out_pos, out_val, key.key, key.Hash(), key.name.path, key.name.length * sizeof(RomPathChar)); return Base::GetInternal(out_pos, out_val, key.key, key.Hash(), key.name.path, key.name.length * sizeof(RomPathChar));
} }
Result GetByPosition(ImplKey *out_key, Value *out_val, Position pos) { Result GetByPosition(ImplKey *out_key, Value *out_val, Position pos) {
@ -117,8 +121,8 @@ namespace ams::fs {
constexpr u32 Hash() const { constexpr u32 Hash() const {
u32 hash = this->key.parent ^ 123456789; u32 hash = this->key.parent ^ 123456789;
const RomPathChar *name = this->name.path; const RomPathChar * name = this->name.path;
const RomPathChar *end = name + this->name.length; const RomPathChar * const end = name + this->name.length;
while (name < end) { while (name < end) {
const u32 cur = static_cast<u32>(static_cast<std::make_unsigned<RomPathChar>::type>(*(name++))); const u32 cur = static_cast<u32>(static_cast<std::make_unsigned<RomPathChar>::type>(*(name++)));
hash = ((hash >> 5) | (hash << 27)) ^ cur; hash = ((hash >> 5) | (hash << 27)) ^ cur;
@ -134,10 +138,10 @@ namespace ams::fs {
DirectoryEntryMapTable dir_table; DirectoryEntryMapTable dir_table;
FileEntryMapTable file_table; FileEntryMapTable file_table;
public: public:
static s64 QueryDirectoryEntryStorageSize(u32 count);
static s64 QueryDirectoryEntryBucketStorageSize(s64 count); static s64 QueryDirectoryEntryBucketStorageSize(s64 count);
static s64 QueryFileEntryStorageSize(u32 count); static size_t QueryDirectoryEntrySize(size_t aux_size);
static s64 QueryFileEntryBucketStorageSize(s64 count); static s64 QueryFileEntryBucketStorageSize(s64 count);
static size_t QueryFileEntrySize(size_t aux_size);
static Result Format(SubStorage dir_bucket, SubStorage file_bucket); static Result Format(SubStorage dir_bucket, SubStorage file_bucket);
public: public:

View File

@ -14,13 +14,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "fs_dbm_rom_types.hpp" #include <stratosphere/fs/common/fs_dbm_rom_types.hpp>
#include "fs_substorage.hpp" #include <stratosphere/fs/fs_substorage.hpp>
namespace ams::fs { namespace ams::fs {
template<typename KeyType, typename ValueType, size_t MaxAuxiliarySize> template<typename KeyType, typename ValueType, size_t MaxAuxiliarySize>
class RomKeyValueStorage { class KeyValueRomStorageTemplate {
public: public:
using Key = KeyType; using Key = KeyType;
using Value = ValueType; using Value = ValueType;
@ -57,8 +57,8 @@ namespace ams::fs {
return size / sizeof(Position); return size / sizeof(Position);
} }
static constexpr s64 QueryKeyValueStorageSize(u32 num) { static constexpr size_t QueryEntrySize(size_t aux_size) {
return num * sizeof(Element); return util::AlignUp(sizeof(Element) + aux_size, alignof(Element));
} }
static Result Format(SubStorage bucket, s64 count) { static Result Format(SubStorage bucket, s64 count) {
@ -69,13 +69,13 @@ namespace ams::fs {
return ResultSuccess(); return ResultSuccess();
} }
public: public:
RomKeyValueStorage() : bucket_count(), bucket_storage(), kv_storage(), total_entry_size(), entry_count() { /* ... */ } KeyValueRomStorageTemplate() : bucket_count(), bucket_storage(), kv_storage(), total_entry_size(), entry_count() { /* ... */ }
Result Initialize(const SubStorage &bucket, s64 count, const SubStorage &kv) { Result Initialize(const SubStorage &bucket, s64 count, const SubStorage &kv) {
AMS_ASSERT(count > 0); AMS_ASSERT(count > 0);
this->bucket_storage = bucket; this->bucket_storage = bucket;
this->kv_storage = kv;
this->bucket_count = count; this->bucket_count = count;
this->kv_storage = kv;
return ResultSuccess(); return ResultSuccess();
} }
@ -100,82 +100,17 @@ namespace ams::fs {
constexpr u32 GetEntryCount() const { constexpr u32 GetEntryCount() const {
return this->entry_count; return this->entry_count;
} }
Result Add(const Key &key, u32 hash_key, const void *aux, size_t aux_size, const Value &value) {
AMS_ASSERT(aux != nullptr);
AMS_ASSERT(aux_size <= MaxAuxiliarySize);
Position pos;
return this->AddImpl(std::addressof(pos), key, hash_key, aux, aux_size, value);
}
Result Get(Value *out, const Key &key, u32 hash_key, const void *aux, size_t aux_size) {
AMS_ASSERT(aux != nullptr);
AMS_ASSERT(aux_size <= MaxAuxiliarySize);
Position pos;
return this->GetImpl(std::addressof(pos), out, key, hash_key, aux, aux_size);
}
void FindOpen(FindIndex *out) const {
AMS_ASSERT(out != nullptr);
out->ind = static_cast<BucketIndex>(-1);
out->pos = InvalidPosition;
}
Result FindNext(Key *out_key, Value *out_val, FindIndex *find) {
AMS_ASSERT(out_key != nullptr);
AMS_ASSERT(out_val != nullptr);
AMS_ASSERT(find != nullptr);
BucketIndex ind = find->ind;
R_UNLESS((ind < this->bucket_count) || ind == static_cast<BucketIndex>(-1), fs::ResultDbmFindKeyFinished());
s64 kv_size;
R_TRY(this->kv_storage.GetSize(std::addressof(kv_size)));
while (true) {
if (find->pos != InvalidPosition) {
Element elem;
R_TRY(this->ReadKeyValue(std::addressof(elem), find->pos));
AMS_ASSERT(elem.next == InvalidPosition || elem.next < kv_size);
find->pos = elem.next;
*out_key = elem.key;
*out_val = elem.val;
return ResultSuccess();
}
while (true) {
ind++;
if (ind == this->bucket_count) {
find->ind = ind;
find->pos = InvalidPosition;
return fs::ResultDbmFindKeyFinished();
}
Position pos;
R_TRY(this->ReadBucket(std::addressof(pos), ind));
AMS_ASSERT(pos == InvalidPosition || pos < kv_size);
if (pos != InvalidPosition) {
find->ind = ind;
find->pos = pos;
break;
}
}
}
}
protected: protected:
Result AddImpl(Position *out, const Key &key, u32 hash_key, const void *aux, size_t aux_size, const Value &value) { Result AddInternal(Position *out, const Key &key, u32 hash_key, const void *aux, size_t aux_size, const Value &value) {
AMS_ASSERT(out != nullptr); AMS_ASSERT(out != nullptr);
AMS_ASSERT(aux != nullptr); AMS_ASSERT(aux != nullptr || aux_size == 0);
AMS_ASSERT(this->bucket_count > 0); AMS_ASSERT(this->bucket_count > 0);
{ {
Position pos, prev_pos; Position pos, prev_pos;
Element elem; Element elem;
const Result find_res = this->FindImpl(std::addressof(pos), std::addressof(prev_pos), std::addressof(elem), key, hash_key, aux, aux_size); const Result find_res = this->FindInternal(std::addressof(pos), std::addressof(prev_pos), std::addressof(elem), key, hash_key, aux, aux_size);
R_UNLESS(R_FAILED(find_res), fs::ResultDbmAlreadyExists()); R_UNLESS(R_FAILED(find_res), fs::ResultDbmAlreadyExists());
R_UNLESS(fs::ResultDbmKeyNotFound::Includes(find_res), find_res); R_UNLESS(fs::ResultDbmKeyNotFound::Includes(find_res), find_res);
} }
@ -195,14 +130,14 @@ namespace ams::fs {
return ResultSuccess(); return ResultSuccess();
} }
Result GetImpl(Position *out_pos, Value *out_val, const Key &key, u32 hash_key, const void *aux, size_t aux_size) { Result GetInternal(Position *out_pos, Value *out_val, const Key &key, u32 hash_key, const void *aux, size_t aux_size) {
AMS_ASSERT(out_pos != nullptr); AMS_ASSERT(out_pos != nullptr);
AMS_ASSERT(out_val != nullptr); AMS_ASSERT(out_val != nullptr);
AMS_ASSERT(aux != nullptr); AMS_ASSERT(aux != nullptr);
Position pos, prev_pos; Position pos, prev_pos;
Element elem; Element elem;
R_TRY(this->FindImpl(std::addressof(pos), std::addressof(prev_pos), std::addressof(elem), key, hash_key, aux, aux_size)); R_TRY(this->FindInternal(std::addressof(pos), std::addressof(prev_pos), std::addressof(elem), key, hash_key, aux, aux_size));
*out_pos = pos; *out_pos = pos;
*out_val = elem.value; *out_val = elem.value;
@ -246,11 +181,11 @@ namespace ams::fs {
return hash_key % this->bucket_count; return hash_key % this->bucket_count;
} }
Result FindImpl(Position *out_pos, Position *out_prev, Element *out_elem, const Key &key, u32 hash_key, const void *aux, size_t aux_size) { Result FindInternal(Position *out_pos, Position *out_prev, Element *out_elem, const Key &key, u32 hash_key, const void *aux, size_t aux_size) {
AMS_ASSERT(out_pos != nullptr); AMS_ASSERT(out_pos != nullptr);
AMS_ASSERT(out_prev != nullptr); AMS_ASSERT(out_prev != nullptr);
AMS_ASSERT(out_elem != nullptr); AMS_ASSERT(out_elem != nullptr);
AMS_ASSERT(aux != nullptr); AMS_ASSERT(aux != nullptr || aux_size == 0);
AMS_ASSERT(this->bucket_count > 0); AMS_ASSERT(this->bucket_count > 0);
*out_pos = 0; *out_pos = 0;
@ -296,7 +231,7 @@ namespace ams::fs {
*out = static_cast<Position>(this->total_entry_size); *out = static_cast<Position>(this->total_entry_size);
this->total_entry_size = util::AlignUp(static_cast<s64>(end_pos), s64(4)); this->total_entry_size = util::AlignUp(static_cast<s64>(end_pos), alignof(Position));
return ResultSuccess(); return ResultSuccess();
} }

View File

@ -0,0 +1,103 @@
/*
* Copyright (c) 2018-2020 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/fs/common/fs_dbm_rom_types.hpp>
namespace ams::fs::RomPathTool {
constexpr inline u32 MaxPathLength = 0x300;
struct RomEntryName {
size_t length;
const RomPathChar *path;
};
static_assert(util::is_pod<RomEntryName>::value);
constexpr void InitEntryName(RomEntryName *entry) {
AMS_ASSERT(entry != nullptr);
entry->length = 0;
}
constexpr inline bool IsSeparator(RomPathChar c) {
return c == RomStringTraits::DirectorySeparator;
}
constexpr inline bool IsNullTerminator(RomPathChar c) {
return c == RomStringTraits::NullTerminator;
}
constexpr inline bool IsDot(RomPathChar c) {
return c == RomStringTraits::Dot;
}
constexpr inline bool IsCurrentDirectory(const RomEntryName &name) {
return name.length == 1 && IsDot(name.path[0]);
}
constexpr inline bool IsCurrentDirectory(const RomPathChar *p, size_t length) {
AMS_ASSERT(p != nullptr);
return length == 1 && IsDot(p[0]);
}
constexpr inline bool IsCurrentDirectory(const RomPathChar *p) {
AMS_ASSERT(p != nullptr);
return IsDot(p[0]) && IsNullTerminator(p[1]);
}
constexpr inline bool IsParentDirectory(const RomEntryName &name) {
return name.length == 2 && IsDot(name.path[0]) && IsDot(name.path[1]);
}
constexpr inline bool IsParentDirectory(const RomPathChar *p) {
AMS_ASSERT(p != nullptr);
return IsDot(p[0]) && IsDot(p[1]) && IsNullTerminator(p[2]);
}
constexpr inline bool IsParentDirectory(const RomPathChar *p, size_t length) {
AMS_ASSERT(p != nullptr);
return length == 2 && IsDot(p[0]) && IsDot(p[1]);
}
constexpr inline bool IsEqualPath(const RomPathChar *lhs, const RomPathChar *rhs, size_t length) {
AMS_ASSERT(lhs != nullptr);
AMS_ASSERT(rhs != nullptr);
return std::strncmp(lhs, rhs, length) == 0;
}
Result GetParentDirectoryName(RomEntryName *out, const RomEntryName &cur, const RomPathChar *p);
class PathParser {
private:
const RomPathChar *prev_path_start;
const RomPathChar *prev_path_end;
const RomPathChar *next_path;
bool finished;
public:
constexpr PathParser() : prev_path_start(), prev_path_end(), next_path(), finished() { /* ... */ }
Result Initialize(const RomPathChar *path);
void Finalize();
bool IsParseFinished() const;
bool IsDirectoryPath() const;
Result GetAsDirectoryName(RomEntryName *out) const;
Result GetAsFileName(RomEntryName *out) const;
Result GetNextDirectoryName(RomEntryName *out);
};
}

View File

@ -14,7 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "fs_common.hpp" #include <stratosphere/fs/fs_common.hpp>
namespace ams::fs { namespace ams::fs {

View File

@ -1,122 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "fs_dbm_rom_types.hpp"
namespace ams::fs {
namespace RomPathTool {
constexpr inline u32 MaxPathLength = 0x300;
struct RomEntryName {
size_t length;
const RomPathChar *path;
};
static_assert(util::is_pod<RomEntryName>::value);
constexpr void InitializeRomEntryName(RomEntryName *entry) {
AMS_ABORT_UNLESS(entry != nullptr);
entry->length = 0;
}
constexpr inline bool IsSeparator(RomPathChar c) {
return c == RomStringTraits::DirectorySeparator;
}
constexpr inline bool IsNullTerminator(RomPathChar c) {
return c == RomStringTraits::NullTerminator;
}
constexpr inline bool IsDot(RomPathChar c) {
return c == RomStringTraits::Dot;
}
constexpr inline bool IsCurrentDirectory(const RomEntryName &name) {
return name.length == 1 && IsDot(name.path[0]);
}
constexpr inline bool IsCurrentDirectory(const RomPathChar *p, size_t length) {
AMS_ABORT_UNLESS(p != nullptr);
return length == 1 && IsDot(p[0]);
}
constexpr inline bool IsCurrentDirectory(const RomPathChar *p) {
AMS_ABORT_UNLESS(p != nullptr);
return IsDot(p[0]) && IsNullTerminator(p[1]);
}
constexpr inline bool IsParentDirectory(const RomEntryName &name) {
return name.length == 2 && IsDot(name.path[0]) && IsDot(name.path[1]);
}
constexpr inline bool IsParentDirectory(const RomPathChar *p) {
AMS_ABORT_UNLESS(p != nullptr);
return IsDot(p[0]) && IsDot(p[1]) && IsNullTerminator(p[2]);
}
constexpr inline bool IsParentDirectory(const RomPathChar *p, size_t length) {
AMS_ABORT_UNLESS(p != nullptr);
return length == 2 && IsDot(p[0]) && IsDot(p[1]);
}
constexpr inline bool IsEqualPath(const RomPathChar *lhs, const RomPathChar *rhs, size_t length) {
AMS_ABORT_UNLESS(lhs != nullptr);
AMS_ABORT_UNLESS(rhs != nullptr);
return std::strncmp(lhs, rhs, length) == 0;
}
constexpr inline bool IsEqualName(const RomEntryName &lhs, const RomPathChar *rhs) {
AMS_ABORT_UNLESS(rhs != nullptr);
if (strnlen(rhs, MaxPathLength) != lhs.length) {
return false;
}
return IsEqualPath(lhs.path, rhs, lhs.length);
}
constexpr inline bool IsEqualName(const RomEntryName &lhs, const RomEntryName &rhs) {
if (lhs.length != rhs.length) {
return false;
}
return IsEqualPath(lhs.path, rhs.path, lhs.length);
}
Result GetParentDirectoryName(RomEntryName *out, const RomEntryName &cur, const RomPathChar *p);
class PathParser {
private:
const RomPathChar *prev_path_start;
const RomPathChar *prev_path_end;
const RomPathChar *next_path;
bool finished;
public:
constexpr PathParser() : prev_path_start(), prev_path_end(), next_path(), finished() { /* ... */ }
Result Initialize(const RomPathChar *path);
void Finalize();
bool IsFinished() const;
bool IsDirectoryPath() const;
Result GetAsDirectoryName(RomEntryName *out) const;
Result GetAsFileName(RomEntryName *out) const;
Result GetNextDirectoryName(RomEntryName *out);
};
}
}

View File

@ -40,25 +40,25 @@ namespace ams::fs {
return this->OperateRange(nullptr, 0, op_id, offset, size, nullptr, 0); return this->OperateRange(nullptr, 0, op_id, offset, size, nullptr, 0);
} }
public: public:
static inline bool IsRangeValid(s64 offset, s64 size, s64 total_size) { static inline bool CheckAccessRange(s64 offset, s64 size, s64 total_size) {
return offset >= 0 && return offset >= 0 &&
size >= 0 && size >= 0 &&
size <= total_size && size <= total_size &&
offset <= (total_size - size); offset <= (total_size - size);
} }
static inline bool IsRangeValid(s64 offset, size_t size, s64 total_size) { static inline bool CheckAccessRange(s64 offset, size_t size, s64 total_size) {
return IsRangeValid(offset, static_cast<s64>(size), total_size); return CheckAccessRange(offset, static_cast<s64>(size), total_size);
} }
static inline bool IsOffsetAndSizeValid(s64 offset, s64 size) { static inline bool CheckOffsetAndSize(s64 offset, s64 size) {
return offset >= 0 && return offset >= 0 &&
size >= 0 && size >= 0 &&
offset <= (offset + size); offset <= (offset + size);
} }
static inline bool IsOffsetAndSizeValid(s64 offset, size_t size) { static inline bool CheckOffsetAndSize(s64 offset, size_t size) {
return IsOffsetAndSizeValid(offset, static_cast<s64>(size)); return CheckOffsetAndSize(offset, static_cast<s64>(size));
} }
}; };

View File

@ -33,7 +33,7 @@ namespace ams::fs {
/* Validate arguments. */ /* Validate arguments. */
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange()); R_UNLESS(IStorage::CheckAccessRange(offset, size, this->size), fs::ResultOutOfRange());
/* Copy from memory. */ /* Copy from memory. */
std::memcpy(buffer, this->buf + offset, size); std::memcpy(buffer, this->buf + offset, size);
@ -46,7 +46,7 @@ namespace ams::fs {
/* Validate arguments. */ /* Validate arguments. */
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange()); R_UNLESS(IStorage::CheckAccessRange(offset, size, this->size), fs::ResultOutOfRange());
/* Copy to memory. */ /* Copy to memory. */
std::memcpy(this->buf + offset, buffer, size); std::memcpy(this->buf + offset, buffer, size);

View File

@ -1,86 +0,0 @@
/*
* Copyright (c) 2018-2020 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/fs/fs_common.hpp>
#include <stratosphere/fssrv/sf/fssrv_sf_path.hpp>
namespace ams::fs {
namespace StringTraits {
constexpr inline char DirectorySeparator = '/';
constexpr inline char DriveSeparator = ':';
constexpr inline char Dot = '.';
constexpr inline char NullTerminator = '\x00';
constexpr inline char AlternateDirectorySeparator = '\\';
}
class PathTool {
public:
static constexpr const char RootPath[] = "/";
public:
static constexpr inline bool IsAlternateSeparator(char c) {
return c == StringTraits::AlternateDirectorySeparator;
}
static constexpr inline bool IsSeparator(char c) {
return c == StringTraits::DirectorySeparator;
}
static constexpr inline bool IsAnySeparator(char c) {
return IsSeparator(c) || IsAlternateSeparator(c);
}
static constexpr inline bool IsNullTerminator(char c) {
return c == StringTraits::NullTerminator;
}
static constexpr inline bool IsDot(char c) {
return c == StringTraits::Dot;
}
static constexpr inline bool IsWindowsDriveCharacter(char c) {
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
}
static constexpr inline bool IsDriveSeparator(char c) {
return c == StringTraits::DriveSeparator;
}
static constexpr inline bool IsWindowsAbsolutePath(const char *p) {
return IsWindowsDriveCharacter(p[0]) && IsDriveSeparator(p[1]);
}
static constexpr inline bool IsUnc(const char *p) {
return (IsSeparator(p[0]) && IsSeparator(p[1])) || (IsAlternateSeparator(p[0]) && IsAlternateSeparator(p[1]));
}
static constexpr inline bool IsCurrentDirectory(const char *p) {
return IsDot(p[0]) && (IsSeparator(p[1]) || IsNullTerminator(p[1]));
}
static constexpr inline bool IsParentDirectory(const char *p) {
return IsDot(p[0]) && IsDot(p[1]) && (IsSeparator(p[2]) || IsNullTerminator(p[2]));
}
static Result Normalize(char *out, size_t *out_len, const char *src, size_t max_out_size, bool unc_preserved = false);
static Result IsNormalized(bool *out, const char *path);
static bool IsSubPath(const char *lhs, const char *rhs);
};
}

View File

@ -19,15 +19,59 @@
namespace ams::fs { namespace ams::fs {
namespace StringTraits {
constexpr inline char DirectorySeparator = '/';
constexpr inline char DriveSeparator = ':';
constexpr inline char Dot = '.';
constexpr inline char NullTerminator = '\x00';
constexpr inline char AlternateDirectorySeparator = '\\';
}
/* Windows path utilities. */
constexpr inline bool IsWindowsDrive(const char *path) {
AMS_ASSERT(path != nullptr);
const char c = path[0];
return (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) && path[1] == StringTraits::DriveSeparator;
}
constexpr inline bool IsUnc(const char *path) {
return (path[0] == StringTraits::DirectorySeparator && path[1] == StringTraits::DirectorySeparator) ||
(path[0] == StringTraits::AlternateDirectorySeparator && path[1] == StringTraits::AlternateDirectorySeparator);
}
constexpr inline s64 GetWindowsPathSkipLength(const char *path) {
if (IsWindowsDrive(path)) {
return 2;
}
if (IsUnc(path)) {
for (s64 i = 2; path[i] != StringTraits::NullTerminator; ++i) {
if (path[i] == '$' || path[i] == ':') {
return i + 1;
}
}
}
return 0;
}
/* Path utilities. */
inline void Replace(char *dst, size_t dst_size, char old_char, char new_char) { inline void Replace(char *dst, size_t dst_size, char old_char, char new_char) {
for (char *cur = dst; cur < dst + dst_size && *cur != '\x00'; cur++) { AMS_ASSERT(dst != nullptr);
for (char *cur = dst; cur < dst + dst_size && *cur != StringTraits::NullTerminator; ++cur) {
if (*cur == old_char) { if (*cur == old_char) {
*cur = new_char; *cur = new_char;
} }
} }
} }
Result FspPathPrintf(fssrv::sf::FspPath *dst, const char *format, ...) __attribute__((format(printf, 2, 3)));
inline Result FspPathPrintf(fssrv::sf::FspPath *dst, const char *format, ...) { inline Result FspPathPrintf(fssrv::sf::FspPath *dst, const char *format, ...) {
AMS_ASSERT(dst != nullptr);
/* Format the path. */ /* Format the path. */
std::va_list va_list; std::va_list va_list;
va_start(va_list, format); va_start(va_list, format);
@ -43,6 +87,37 @@ namespace ams::fs {
return ResultSuccess(); return ResultSuccess();
} }
Result VerifyPath(const char *path, size_t max_path_len, size_t max_name_len); Result VerifyPath(const char *path, int max_path_len, int max_name_len);
bool IsSubPath(const char *lhs, const char *rhs);
/* Path normalization. */
class PathNormalizer {
public:
static constexpr const char RootPath[] = "/";
public:
static constexpr inline bool IsSeparator(char c) {
return c == StringTraits::DirectorySeparator;
}
static constexpr inline bool IsAnySeparator(char c) {
return c == StringTraits::DirectorySeparator || c == StringTraits::AlternateDirectorySeparator;
}
static constexpr inline bool IsNullTerminator(char c) {
return c == StringTraits::NullTerminator;
}
static constexpr inline bool IsCurrentDirectory(const char *p) {
return p[0] == StringTraits::Dot && (IsSeparator(p[1]) || IsNullTerminator(p[1]));
}
static constexpr inline bool IsParentDirectory(const char *p) {
return p[0] == StringTraits::Dot && p[1] == StringTraits::Dot && (IsSeparator(p[2]) || IsNullTerminator(p[2]));
}
static Result Normalize(char *out, size_t *out_len, const char *src, size_t max_out_size, bool unc_preserved = false, bool has_mount_name = false);
static Result IsNormalized(bool *out, const char *path, bool unc_preserved = false, bool has_mount_name = false);
};
} }

View File

@ -14,13 +14,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "../fs/fs_common.hpp" #include <stratosphere/fs/fs_common.hpp>
#include "../fs/fs_path_tool.hpp"
namespace ams::fssystem { namespace ams::fs {
namespace StringTraits = ::ams::fs::StringTraits; struct ProgramIndexMapInfo {
ncm::ProgramId program_id;
using PathTool = ::ams::fs::PathTool; ncm::ProgramId base_program_id;
u8 program_index;
u8 pad[0xF];
};
static_assert(util::is_pod<ProgramIndexMapInfo>::value);
static_assert(sizeof(ProgramIndexMapInfo) == 0x20);
} }

View File

@ -33,27 +33,27 @@ namespace ams::fs {
explicit ReadOnlyFile(std::unique_ptr<fsa::IFile> &&f) : base_file(std::move(f)) { /* ... */ } explicit ReadOnlyFile(std::unique_ptr<fsa::IFile> &&f) : base_file(std::move(f)) { /* ... */ }
virtual ~ReadOnlyFile() { /* ... */ } virtual ~ReadOnlyFile() { /* ... */ }
private: private:
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final { virtual Result DoRead(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final {
return this->base_file->Read(out, offset, buffer, size, option); return this->base_file->Read(out, offset, buffer, size, option);
} }
virtual Result GetSizeImpl(s64 *out) override final { virtual Result DoGetSize(s64 *out) override final {
return this->base_file->GetSize(out); return this->base_file->GetSize(out);
} }
virtual Result FlushImpl() override final { virtual Result DoFlush() override final {
return ResultSuccess(); return ResultSuccess();
} }
virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final { virtual Result DoWrite(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final {
return fs::ResultUnsupportedOperationInReadOnlyFileA(); return fs::ResultUnsupportedOperationInReadOnlyFileA();
} }
virtual Result SetSizeImpl(s64 size) override final { virtual Result DoSetSize(s64 size) override final {
return fs::ResultUnsupportedOperationInReadOnlyFileA(); return fs::ResultUnsupportedOperationInReadOnlyFileA();
} }
virtual Result OperateRangeImpl(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final { 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) { switch (op_id) {
case OperationId::InvalidateCache: case OperationId::InvalidateCache:
case OperationId::QueryRange: case OperationId::QueryRange:
@ -80,7 +80,7 @@ namespace ams::fs {
explicit ReadOnlyFileSystemTemplate(T &&fs) : base_fs(std::move(fs)) { /* ... */ } explicit ReadOnlyFileSystemTemplate(T &&fs) : base_fs(std::move(fs)) { /* ... */ }
virtual ~ReadOnlyFileSystemTemplate() { /* ... */ } virtual ~ReadOnlyFileSystemTemplate() { /* ... */ }
private: private:
virtual Result OpenFileImpl(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 char *path, OpenMode mode) override final {
/* Only allow opening files with mode = read. */ /* Only allow opening files with mode = read. */
R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidOpenMode()); R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidOpenMode());
@ -94,59 +94,59 @@ namespace ams::fs {
return ResultSuccess(); return ResultSuccess();
} }
virtual Result OpenDirectoryImpl(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 char *path, OpenDirectoryMode mode) override final {
return this->base_fs->OpenDirectory(out_dir, path, mode); return this->base_fs->OpenDirectory(out_dir, path, mode);
} }
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const char *path) override final { virtual Result DoGetEntryType(DirectoryEntryType *out, const char *path) override final {
return this->base_fs->GetEntryType(out, path); return this->base_fs->GetEntryType(out, path);
} }
virtual Result CommitImpl() override final { virtual Result DoCommit() override final {
return ResultSuccess(); return ResultSuccess();
} }
virtual Result CreateFileImpl(const char *path, s64 size, int flags) override final { virtual Result DoCreateFile(const char *path, s64 size, int flags) override final {
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA(); return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
} }
virtual Result DeleteFileImpl(const char *path) override final { virtual Result DoDeleteFile(const char *path) override final {
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA(); return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
} }
virtual Result CreateDirectoryImpl(const char *path) override final { virtual Result DoCreateDirectory(const char *path) override final {
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA(); return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
} }
virtual Result DeleteDirectoryImpl(const char *path) override final { virtual Result DoDeleteDirectory(const char *path) override final {
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA(); return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
} }
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override final { virtual Result DoDeleteDirectoryRecursively(const char *path) override final {
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA(); return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
} }
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override final { virtual Result DoRenameFile(const char *old_path, const char *new_path) override final {
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA(); return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
} }
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override final { virtual Result DoRenameDirectory(const char *old_path, const char *new_path) override final {
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA(); return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
} }
virtual Result CleanDirectoryRecursivelyImpl(const char *path) override final { virtual Result DoCleanDirectoryRecursively(const char *path) override final {
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA(); return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateA();
} }
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) override final { virtual Result DoGetFreeSpaceSize(s64 *out, const char *path) override final {
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateB(); return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateB();
} }
virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) override final { virtual Result DoGetTotalSpaceSize(s64 *out, const char *path) override final {
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateB(); return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateB();
} }
virtual Result CommitProvisionallyImpl(s64 counter) override final { virtual Result DoCommitProvisionally(s64 counter) override final {
return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateC(); return fs::ResultUnsupportedOperationInReadOnlyFileSystemTemplateC();
} }
}; };

View File

@ -20,7 +20,6 @@
#include <stratosphere/fs/fsa/fs_idirectory.hpp> #include <stratosphere/fs/fsa/fs_idirectory.hpp>
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp> #include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
#include <stratosphere/fs/fs_query_range.hpp> #include <stratosphere/fs/fs_query_range.hpp>
#include <stratosphere/fs/fs_path_tool.hpp>
#include <stratosphere/fs/fs_path_utils.hpp> #include <stratosphere/fs/fs_path_utils.hpp>
namespace ams::fs { namespace ams::fs {
@ -33,27 +32,27 @@ namespace ams::fs {
virtual ~RemoteFile() { fsFileClose(std::addressof(this->base_file)); } virtual ~RemoteFile() { fsFileClose(std::addressof(this->base_file)); }
public: public:
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final { virtual Result DoRead(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final {
return fsFileRead(std::addressof(this->base_file), offset, buffer, size, option.value, out); return fsFileRead(std::addressof(this->base_file), offset, buffer, size, option.value, out);
} }
virtual Result GetSizeImpl(s64 *out) override final { virtual Result DoGetSize(s64 *out) override final {
return fsFileGetSize(std::addressof(this->base_file), out); return fsFileGetSize(std::addressof(this->base_file), out);
} }
virtual Result FlushImpl() override final { virtual Result DoFlush() override final {
return fsFileFlush(std::addressof(this->base_file)); return fsFileFlush(std::addressof(this->base_file));
} }
virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final { virtual Result DoWrite(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final {
return fsFileWrite(std::addressof(this->base_file), offset, buffer, size, option.value); return fsFileWrite(std::addressof(this->base_file), offset, buffer, size, option.value);
} }
virtual Result SetSizeImpl(s64 size) override final { virtual Result DoSetSize(s64 size) override final {
return fsFileSetSize(std::addressof(this->base_file), size); return fsFileSetSize(std::addressof(this->base_file), size);
} }
virtual Result OperateRangeImpl(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final { 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 {
R_UNLESS(op_id == OperationId::QueryRange, fs::ResultUnsupportedOperationInFileServiceObjectAdapterA()); R_UNLESS(op_id == OperationId::QueryRange, fs::ResultUnsupportedOperationInFileServiceObjectAdapterA());
R_UNLESS(dst_size == sizeof(FileQueryRangeInfo), fs::ResultInvalidSize()); R_UNLESS(dst_size == sizeof(FileQueryRangeInfo), fs::ResultInvalidSize());
@ -73,11 +72,11 @@ namespace ams::fs {
virtual ~RemoteDirectory() { fsDirClose(std::addressof(this->base_dir)); } virtual ~RemoteDirectory() { fsDirClose(std::addressof(this->base_dir)); }
public: public:
virtual Result ReadImpl(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) override final { virtual Result DoRead(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) override final {
return fsDirRead(std::addressof(this->base_dir), out_count, max_entries, out_entries); return fsDirRead(std::addressof(this->base_dir), out_count, max_entries, out_entries);
} }
virtual Result GetEntryCountImpl(s64 *out) override final { virtual Result DoGetEntryCount(s64 *out) override final {
return fsDirGetEntryCount(std::addressof(this->base_dir), out); return fsDirGetEntryCount(std::addressof(this->base_dir), out);
} }
public: public:
@ -100,46 +99,46 @@ namespace ams::fs {
out_path->str[sizeof(out_path->str) - 1] = '\x00'; out_path->str[sizeof(out_path->str) - 1] = '\x00';
/* Replace directory separators. */ /* Replace directory separators. */
Replace(out_path->str, sizeof(out_path->str) - 1, StringTraits::AlternateDirectorySeparator, StringTraits::DirectorySeparator); fs::Replace(out_path->str, sizeof(out_path->str) - 1, StringTraits::AlternateDirectorySeparator, StringTraits::DirectorySeparator);
/* Get lengths. */ /* Get lengths. */
const auto mount_name_len = PathTool::IsWindowsAbsolutePath(path) ? 2 : 0; const auto skip_len = fs::GetWindowsPathSkipLength(path);
const auto rel_path = out_path->str + mount_name_len; const auto rel_path = out_path->str + skip_len;
const auto max_len = fs::EntryNameLengthMax - mount_name_len; const auto max_len = fs::EntryNameLengthMax - skip_len;
return VerifyPath(rel_path, max_len, max_len); return VerifyPath(rel_path, max_len, max_len);
} }
public: public:
virtual Result CreateFileImpl(const char *path, s64 size, int flags) override final { virtual Result DoCreateFile(const char *path, s64 size, int flags) override final {
fssrv::sf::Path sf_path; fssrv::sf::Path sf_path;
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
return fsFsCreateFile(std::addressof(this->base_fs), sf_path.str, size, flags); return fsFsCreateFile(std::addressof(this->base_fs), sf_path.str, size, flags);
} }
virtual Result DeleteFileImpl(const char *path) override final { virtual Result DoDeleteFile(const char *path) override final {
fssrv::sf::Path sf_path; fssrv::sf::Path sf_path;
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
return fsFsDeleteFile(std::addressof(this->base_fs), sf_path.str); return fsFsDeleteFile(std::addressof(this->base_fs), sf_path.str);
} }
virtual Result CreateDirectoryImpl(const char *path) override final { virtual Result DoCreateDirectory(const char *path) override final {
fssrv::sf::Path sf_path; fssrv::sf::Path sf_path;
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
return fsFsCreateDirectory(std::addressof(this->base_fs), sf_path.str); return fsFsCreateDirectory(std::addressof(this->base_fs), sf_path.str);
} }
virtual Result DeleteDirectoryImpl(const char *path) override final { virtual Result DoDeleteDirectory(const char *path) override final {
fssrv::sf::Path sf_path; fssrv::sf::Path sf_path;
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
return fsFsDeleteDirectory(std::addressof(this->base_fs), sf_path.str); return fsFsDeleteDirectory(std::addressof(this->base_fs), sf_path.str);
} }
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override final { virtual Result DoDeleteDirectoryRecursively(const char *path) override final {
fssrv::sf::Path sf_path; fssrv::sf::Path sf_path;
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
return fsFsDeleteDirectoryRecursively(std::addressof(this->base_fs), sf_path.str); return fsFsDeleteDirectoryRecursively(std::addressof(this->base_fs), sf_path.str);
} }
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override final { virtual Result DoRenameFile(const char *old_path, const char *new_path) override final {
fssrv::sf::Path old_sf_path; fssrv::sf::Path old_sf_path;
fssrv::sf::Path new_sf_path; fssrv::sf::Path new_sf_path;
R_TRY(GetPathForServiceObject(std::addressof(old_sf_path), old_path)); R_TRY(GetPathForServiceObject(std::addressof(old_sf_path), old_path));
@ -147,7 +146,7 @@ namespace ams::fs {
return fsFsRenameFile(std::addressof(this->base_fs), old_sf_path.str, new_sf_path.str); return fsFsRenameFile(std::addressof(this->base_fs), old_sf_path.str, new_sf_path.str);
} }
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override final { virtual Result DoRenameDirectory(const char *old_path, const char *new_path) override final {
fssrv::sf::Path old_sf_path; fssrv::sf::Path old_sf_path;
fssrv::sf::Path new_sf_path; fssrv::sf::Path new_sf_path;
R_TRY(GetPathForServiceObject(std::addressof(old_sf_path), old_path)); R_TRY(GetPathForServiceObject(std::addressof(old_sf_path), old_path));
@ -155,7 +154,7 @@ namespace ams::fs {
return fsFsRenameDirectory(std::addressof(this->base_fs), old_sf_path.str, new_sf_path.str); return fsFsRenameDirectory(std::addressof(this->base_fs), old_sf_path.str, new_sf_path.str);
} }
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const char *path) override final { virtual Result DoGetEntryType(DirectoryEntryType *out, const char *path) override final {
fssrv::sf::Path sf_path; fssrv::sf::Path sf_path;
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
@ -163,7 +162,7 @@ namespace ams::fs {
return fsFsGetEntryType(std::addressof(this->base_fs), sf_path.str, reinterpret_cast<::FsDirEntryType *>(out)); return fsFsGetEntryType(std::addressof(this->base_fs), sf_path.str, reinterpret_cast<::FsDirEntryType *>(out));
} }
virtual Result OpenFileImpl(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 char *path, OpenMode mode) override final {
fssrv::sf::Path sf_path; fssrv::sf::Path sf_path;
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
@ -177,7 +176,7 @@ namespace ams::fs {
return ResultSuccess(); return ResultSuccess();
} }
virtual Result OpenDirectoryImpl(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 char *path, OpenDirectoryMode mode) override final {
fssrv::sf::Path sf_path; fssrv::sf::Path sf_path;
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
@ -191,37 +190,37 @@ namespace ams::fs {
return ResultSuccess(); return ResultSuccess();
} }
virtual Result CommitImpl() override final { virtual Result DoCommit() override final {
return fsFsCommit(std::addressof(this->base_fs)); return fsFsCommit(std::addressof(this->base_fs));
} }
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) { virtual Result DoGetFreeSpaceSize(s64 *out, const char *path) {
fssrv::sf::Path sf_path; fssrv::sf::Path sf_path;
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
return fsFsGetFreeSpace(std::addressof(this->base_fs), sf_path.str, out); return fsFsGetFreeSpace(std::addressof(this->base_fs), sf_path.str, out);
} }
virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) { virtual Result DoGetTotalSpaceSize(s64 *out, const char *path) {
fssrv::sf::Path sf_path; fssrv::sf::Path sf_path;
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
return fsFsGetTotalSpace(std::addressof(this->base_fs), sf_path.str, out); return fsFsGetTotalSpace(std::addressof(this->base_fs), sf_path.str, out);
} }
virtual Result CleanDirectoryRecursivelyImpl(const char *path) { virtual Result DoCleanDirectoryRecursively(const char *path) {
fssrv::sf::Path sf_path; fssrv::sf::Path sf_path;
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
return fsFsCleanDirectoryRecursively(std::addressof(this->base_fs), sf_path.str); return fsFsCleanDirectoryRecursively(std::addressof(this->base_fs), sf_path.str);
} }
virtual Result GetFileTimeStampRawImpl(FileTimeStampRaw *out, const char *path) { virtual Result DoGetFileTimeStampRaw(FileTimeStampRaw *out, const char *path) {
fssrv::sf::Path sf_path; fssrv::sf::Path sf_path;
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
static_assert(sizeof(FileTimeStampRaw) == sizeof(::FsTimeStampRaw)); static_assert(sizeof(FileTimeStampRaw) == sizeof(::FsTimeStampRaw));
return fsFsGetFileTimeStampRaw(std::addressof(this->base_fs), sf_path.str, reinterpret_cast<::FsTimeStampRaw *>(out)); return fsFsGetFileTimeStampRaw(std::addressof(this->base_fs), sf_path.str, reinterpret_cast<::FsTimeStampRaw *>(out));
} }
virtual Result QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, fsa::QueryId query, const char *path) { virtual Result DoQueryEntry(char *dst, size_t dst_size, const char *src, size_t src_size, fsa::QueryId query, const char *path) {
fssrv::sf::Path sf_path; fssrv::sf::Path sf_path;
R_TRY(GetPathForServiceObject(std::addressof(sf_path), path)); R_TRY(GetPathForServiceObject(std::addressof(sf_path), path));
return fsFsQueryEntry(std::addressof(this->base_fs), dst, dst_size, src, src_size, sf_path.str, static_cast<FsFileSystemQueryId>(query)); return fsFsQueryEntry(std::addressof(this->base_fs), dst, dst_size, src, src_size, sf_path.str, static_cast<FsFileSystemQueryId>(query));

View File

@ -14,12 +14,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "fs_common.hpp" #include <stratosphere/fs/fs_common.hpp>
#include "impl/fs_newable.hpp" #include <stratosphere/fs/impl/fs_newable.hpp>
#include "fsa/fs_ifile.hpp" #include <stratosphere/fs/fsa/fs_ifile.hpp>
#include "fsa/fs_idirectory.hpp" #include <stratosphere/fs/fsa/fs_idirectory.hpp>
#include "fsa/fs_ifilesystem.hpp" #include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
#include "fs_dbm_hierarchical_rom_file_table.hpp" #include <stratosphere/fs/common/fs_dbm_hierarchical_rom_file_table.hpp>
namespace ams::fs { namespace ams::fs {
@ -51,24 +51,24 @@ namespace ams::fs {
RomFileTable *GetRomFileTable(); RomFileTable *GetRomFileTable();
Result GetFileBaseOffset(s64 *out, const char *path); Result GetFileBaseOffset(s64 *out, const char *path);
public: public:
virtual Result CreateFileImpl(const char *path, s64 size, int flags) override; virtual Result DoCreateFile(const char *path, s64 size, int flags) override;
virtual Result DeleteFileImpl(const char *path) override; virtual Result DoDeleteFile(const char *path) override;
virtual Result CreateDirectoryImpl(const char *path) override; virtual Result DoCreateDirectory(const char *path) override;
virtual Result DeleteDirectoryImpl(const char *path) override; virtual Result DoDeleteDirectory(const char *path) override;
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override; virtual Result DoDeleteDirectoryRecursively(const char *path) override;
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override; virtual Result DoRenameFile(const char *old_path, const char *new_path) override;
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override; virtual Result DoRenameDirectory(const char *old_path, const char *new_path) override;
virtual Result GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) override; virtual Result DoGetEntryType(fs::DirectoryEntryType *out, const char *path) override;
virtual Result OpenFileImpl(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) override; virtual Result DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) override;
virtual Result OpenDirectoryImpl(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) override; virtual Result DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) override;
virtual Result CommitImpl() override; virtual Result DoCommit() override;
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) override; virtual Result DoGetFreeSpaceSize(s64 *out, const char *path) override;
virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) override; virtual Result DoGetTotalSpaceSize(s64 *out, const char *path) override;
virtual Result CleanDirectoryRecursivelyImpl(const char *path) override; virtual Result DoCleanDirectoryRecursively(const char *path) override;
/* These aren't accessible as commands. */ /* These aren't accessible as commands. */
virtual Result CommitProvisionallyImpl(s64 counter) override; virtual Result DoCommitProvisionally(s64 counter) override;
virtual Result RollbackImpl() override; virtual Result DoRollback() override;
}; };
} }

View File

@ -30,25 +30,25 @@ namespace ams::fs {
public: public:
SharedFileSystemHolder(std::shared_ptr<fsa::IFileSystem> f) : fs(std::move(f)) { /* ... */ } SharedFileSystemHolder(std::shared_ptr<fsa::IFileSystem> f) : fs(std::move(f)) { /* ... */ }
public: public:
virtual Result CreateFileImpl(const char *path, s64 size, int flags) override { return this->fs->CreateFile(path, size, flags); } virtual Result DoCreateFile(const char *path, s64 size, int flags) override { return this->fs->CreateFile(path, size, flags); }
virtual Result DeleteFileImpl(const char *path) override { return this->fs->DeleteFile(path); } virtual Result DoDeleteFile(const char *path) override { return this->fs->DeleteFile(path); }
virtual Result CreateDirectoryImpl(const char *path) override { return this->fs->CreateDirectory(path); } virtual Result DoCreateDirectory(const char *path) override { return this->fs->CreateDirectory(path); }
virtual Result DeleteDirectoryImpl(const char *path) override { return this->fs->DeleteDirectory(path); } virtual Result DoDeleteDirectory(const char *path) override { return this->fs->DeleteDirectory(path); }
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override { return this->fs->DeleteDirectoryRecursively(path); } virtual Result DoDeleteDirectoryRecursively(const char *path) override { return this->fs->DeleteDirectoryRecursively(path); }
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override { return this->fs->RenameFile(old_path, new_path); } virtual Result DoRenameFile(const char *old_path, const char *new_path) override { return this->fs->RenameFile(old_path, new_path); }
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override { return this->fs->RenameDirectory(old_path, new_path); } virtual Result DoRenameDirectory(const char *old_path, const char *new_path) override { return this->fs->RenameDirectory(old_path, new_path); }
virtual Result GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) override { return this->fs->GetEntryType(out, path); } virtual Result DoGetEntryType(fs::DirectoryEntryType *out, const char *path) override { return this->fs->GetEntryType(out, path); }
virtual Result OpenFileImpl(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) override { return this->fs->OpenFile(out_file, path, mode); } virtual Result DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) override { return this->fs->OpenFile(out_file, path, mode); }
virtual Result OpenDirectoryImpl(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) override { return this->fs->OpenDirectory(out_dir, path, mode); } virtual Result DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) override { return this->fs->OpenDirectory(out_dir, path, mode); }
virtual Result CommitImpl() override { return this->fs->Commit(); } virtual Result DoCommit() override { return this->fs->Commit(); }
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) override { return this->fs->GetFreeSpaceSize(out, path); } virtual Result DoGetFreeSpaceSize(s64 *out, const char *path) override { return this->fs->GetFreeSpaceSize(out, path); }
virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) override { return this->fs->GetTotalSpaceSize(out, path); } virtual Result DoGetTotalSpaceSize(s64 *out, const char *path) override { return this->fs->GetTotalSpaceSize(out, path); }
virtual Result CleanDirectoryRecursivelyImpl(const char *path) override { return this->fs->CleanDirectoryRecursively(path); } virtual Result DoCleanDirectoryRecursively(const char *path) override { return this->fs->CleanDirectoryRecursively(path); }
/* These aren't accessible as commands. */ /* These aren't accessible as commands. */
virtual Result CommitProvisionallyImpl(s64 counter) override { return this->fs->CommitProvisionally(counter); } virtual Result DoCommitProvisionally(s64 counter) override { return this->fs->CommitProvisionally(counter); }
virtual Result RollbackImpl() override { return this->fs->Rollback(); } virtual Result DoRollback() override { return this->fs->Rollback(); }
virtual Result FlushImpl() override { return this->fs->Flush(); } virtual Result DoFlush() override { return this->fs->Flush(); }
}; };
} }

View File

@ -78,7 +78,7 @@ namespace ams::fs {
/* Validate arguments and read. */ /* Validate arguments and read. */
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange()); R_UNLESS(IStorage::CheckAccessRange(offset, size, this->size), fs::ResultOutOfRange());
return this->base_storage->Read(this->offset + offset, buffer, size); return this->base_storage->Read(this->offset + offset, buffer, size);
} }
@ -91,7 +91,7 @@ namespace ams::fs {
/* Validate arguments and write. */ /* Validate arguments and write. */
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange()); R_UNLESS(IStorage::CheckAccessRange(offset, size, this->size), fs::ResultOutOfRange());
return this->base_storage->Write(this->offset + offset, buffer, size); return this->base_storage->Write(this->offset + offset, buffer, size);
} }
@ -104,7 +104,7 @@ namespace ams::fs {
/* Ensure we're initialized and validate arguments. */ /* Ensure we're initialized and validate arguments. */
R_UNLESS(this->IsValid(), fs::ResultNotInitialized()); R_UNLESS(this->IsValid(), fs::ResultNotInitialized());
R_UNLESS(this->resizable, fs::ResultUnsupportedOperationInSubStorageA()); R_UNLESS(this->resizable, fs::ResultUnsupportedOperationInSubStorageA());
R_UNLESS(IStorage::IsOffsetAndSizeValid(this->offset, size), fs::ResultInvalidSize()); R_UNLESS(IStorage::CheckOffsetAndSize(this->offset, size), fs::ResultInvalidSize());
/* Ensure that we're allowed to set size. */ /* Ensure that we're allowed to set size. */
s64 cur_size; s64 cur_size;
@ -134,7 +134,7 @@ namespace ams::fs {
R_SUCCEED_IF(size == 0); R_SUCCEED_IF(size == 0);
/* Validate arguments and operate. */ /* Validate arguments and operate. */
R_UNLESS(IStorage::IsOffsetAndSizeValid(offset, size), fs::ResultOutOfRange()); R_UNLESS(IStorage::CheckOffsetAndSize(offset, size), fs::ResultOutOfRange());
return this->base_storage->OperateRange(dst, dst_size, op_id, this->offset + offset, size, src, src_size); return this->base_storage->OperateRange(dst, dst_size, op_id, this->offset + offset, size, src, src_size);
} }

View File

@ -32,12 +32,12 @@ namespace ams::fs::fsa {
} }
R_UNLESS(out_entries != nullptr, fs::ResultNullptrArgument()); R_UNLESS(out_entries != nullptr, fs::ResultNullptrArgument());
R_UNLESS(max_entries > 0, fs::ResultInvalidArgument()); R_UNLESS(max_entries > 0, fs::ResultInvalidArgument());
return this->ReadImpl(out_count, out_entries, max_entries); return this->DoRead(out_count, out_entries, max_entries);
} }
Result GetEntryCount(s64 *out) { Result GetEntryCount(s64 *out) {
R_UNLESS(out != nullptr, fs::ResultNullptrArgument()); R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
return this->GetEntryCountImpl(out); return this->DoGetEntryCount(out);
} }
public: public:
/* TODO: This is a hack to allow the mitm API to work. Find a better way? */ /* TODO: This is a hack to allow the mitm API to work. Find a better way? */
@ -45,8 +45,8 @@ namespace ams::fs::fsa {
protected: protected:
/* ...? */ /* ...? */
private: private:
virtual Result ReadImpl(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) = 0; virtual Result DoRead(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) = 0;
virtual Result GetEntryCountImpl(s64 *out) = 0; virtual Result DoGetEntryCount(s64 *out) = 0;
}; };
} }

View File

@ -36,7 +36,7 @@ namespace ams::fs::fsa {
const s64 signed_size = static_cast<s64>(size); const s64 signed_size = static_cast<s64>(size);
R_UNLESS(signed_size >= 0, fs::ResultOutOfRange()); R_UNLESS(signed_size >= 0, fs::ResultOutOfRange());
R_UNLESS((std::numeric_limits<s64>::max() - offset) >= signed_size, fs::ResultOutOfRange()); R_UNLESS((std::numeric_limits<s64>::max() - offset) >= signed_size, fs::ResultOutOfRange());
return this->ReadImpl(out, offset, buffer, size, option); return this->DoRead(out, offset, buffer, size, option);
} }
Result Read(size_t *out, s64 offset, void *buffer, size_t size) { Result Read(size_t *out, s64 offset, void *buffer, size_t size) {
@ -45,11 +45,11 @@ namespace ams::fs::fsa {
Result GetSize(s64 *out) { Result GetSize(s64 *out) {
R_UNLESS(out != nullptr, fs::ResultNullptrArgument()); R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
return this->GetSizeImpl(out); return this->DoGetSize(out);
} }
Result Flush() { Result Flush() {
return this->FlushImpl(); return this->DoFlush();
} }
Result Write(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) { Result Write(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) {
@ -64,20 +64,20 @@ namespace ams::fs::fsa {
const s64 signed_size = static_cast<s64>(size); const s64 signed_size = static_cast<s64>(size);
R_UNLESS(signed_size >= 0, fs::ResultOutOfRange()); R_UNLESS(signed_size >= 0, fs::ResultOutOfRange());
R_UNLESS((std::numeric_limits<s64>::max() - offset) >= signed_size, fs::ResultOutOfRange()); R_UNLESS((std::numeric_limits<s64>::max() - offset) >= signed_size, fs::ResultOutOfRange());
return this->WriteImpl(offset, buffer, size, option); return this->DoWrite(offset, buffer, size, option);
} }
Result SetSize(s64 size) { Result SetSize(s64 size) {
R_UNLESS(size >= 0, fs::ResultOutOfRange()); R_UNLESS(size >= 0, fs::ResultOutOfRange());
return this->SetSizeImpl(size); return this->DoSetSize(size);
} }
Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) { Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
return this->OperateRangeImpl(dst, dst_size, op_id, offset, size, src, src_size); return this->DoOperateRange(dst, dst_size, op_id, offset, size, src, src_size);
} }
Result OperateRange(fs::OperationId op_id, s64 offset, s64 size) { Result OperateRange(fs::OperationId op_id, s64 offset, s64 size) {
return this->OperateRangeImpl(nullptr, 0, op_id, offset, size, nullptr, 0); return this->DoOperateRange(nullptr, 0, op_id, offset, size, nullptr, 0);
} }
public: public:
/* TODO: This is a hack to allow the mitm API to work. Find a better way? */ /* TODO: This is a hack to allow the mitm API to work. Find a better way? */
@ -124,12 +124,12 @@ namespace ams::fs::fsa {
return ResultSuccess(); return ResultSuccess();
} }
private: private:
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) = 0; virtual Result DoRead(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) = 0;
virtual Result GetSizeImpl(s64 *out) = 0; virtual Result DoGetSize(s64 *out) = 0;
virtual Result FlushImpl() = 0; virtual Result DoFlush() = 0;
virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) = 0; virtual Result DoWrite(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) = 0;
virtual Result SetSizeImpl(s64 size) = 0; virtual Result DoSetSize(s64 size) = 0;
virtual Result OperateRangeImpl(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) = 0; virtual Result DoOperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) = 0;
}; };
} }

View File

@ -36,7 +36,7 @@ namespace ams::fs::fsa {
Result CreateFile(const char *path, s64 size, int option) { Result CreateFile(const char *path, s64 size, int option) {
R_UNLESS(path != nullptr, fs::ResultInvalidPath()); R_UNLESS(path != nullptr, fs::ResultInvalidPath());
R_UNLESS(size >= 0, fs::ResultOutOfRange()); R_UNLESS(size >= 0, fs::ResultOutOfRange());
return this->CreateFileImpl(path, size, option); return this->DoCreateFile(path, size, option);
} }
Result CreateFile(const char *path, s64 size) { Result CreateFile(const char *path, s64 size) {
@ -45,40 +45,40 @@ namespace ams::fs::fsa {
Result DeleteFile(const char *path) { Result DeleteFile(const char *path) {
R_UNLESS(path != nullptr, fs::ResultInvalidPath()); R_UNLESS(path != nullptr, fs::ResultInvalidPath());
return this->DeleteFileImpl(path); return this->DoDeleteFile(path);
} }
Result CreateDirectory(const char *path) { Result CreateDirectory(const char *path) {
R_UNLESS(path != nullptr, fs::ResultInvalidPath()); R_UNLESS(path != nullptr, fs::ResultInvalidPath());
return this->CreateDirectoryImpl(path); return this->DoCreateDirectory(path);
} }
Result DeleteDirectory(const char *path) { Result DeleteDirectory(const char *path) {
R_UNLESS(path != nullptr, fs::ResultInvalidPath()); R_UNLESS(path != nullptr, fs::ResultInvalidPath());
return this->DeleteDirectoryImpl(path); return this->DoDeleteDirectory(path);
} }
Result DeleteDirectoryRecursively(const char *path) { Result DeleteDirectoryRecursively(const char *path) {
R_UNLESS(path != nullptr, fs::ResultInvalidPath()); R_UNLESS(path != nullptr, fs::ResultInvalidPath());
return this->DeleteDirectoryRecursivelyImpl(path); return this->DoDeleteDirectoryRecursively(path);
} }
Result RenameFile(const char *old_path, const char *new_path) { Result RenameFile(const char *old_path, const char *new_path) {
R_UNLESS(old_path != nullptr, fs::ResultInvalidPath()); R_UNLESS(old_path != nullptr, fs::ResultInvalidPath());
R_UNLESS(new_path != nullptr, fs::ResultInvalidPath()); R_UNLESS(new_path != nullptr, fs::ResultInvalidPath());
return this->RenameFileImpl(old_path, new_path); return this->DoRenameFile(old_path, new_path);
} }
Result RenameDirectory(const char *old_path, const char *new_path) { Result RenameDirectory(const char *old_path, const char *new_path) {
R_UNLESS(old_path != nullptr, fs::ResultInvalidPath()); R_UNLESS(old_path != nullptr, fs::ResultInvalidPath());
R_UNLESS(new_path != nullptr, fs::ResultInvalidPath()); R_UNLESS(new_path != nullptr, fs::ResultInvalidPath());
return this->RenameDirectoryImpl(old_path, new_path); return this->DoRenameDirectory(old_path, new_path);
} }
Result GetEntryType(DirectoryEntryType *out, const char *path) { Result GetEntryType(DirectoryEntryType *out, const char *path) {
R_UNLESS(path != nullptr, fs::ResultInvalidPath()); R_UNLESS(path != nullptr, fs::ResultInvalidPath());
R_UNLESS(out != nullptr, fs::ResultNullptrArgument()); R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
return this->GetEntryTypeImpl(out, path); return this->DoGetEntryType(out, path);
} }
Result OpenFile(std::unique_ptr<IFile> *out_file, const char *path, OpenMode mode) { Result OpenFile(std::unique_ptr<IFile> *out_file, const char *path, OpenMode mode) {
@ -86,7 +86,7 @@ namespace ams::fs::fsa {
R_UNLESS(out_file != nullptr, fs::ResultNullptrArgument()); R_UNLESS(out_file != nullptr, fs::ResultNullptrArgument());
R_UNLESS((mode & OpenMode_ReadWrite) != 0, fs::ResultInvalidOpenMode()); R_UNLESS((mode & OpenMode_ReadWrite) != 0, fs::ResultInvalidOpenMode());
R_UNLESS((mode & ~OpenMode_All) == 0, fs::ResultInvalidOpenMode()); R_UNLESS((mode & ~OpenMode_All) == 0, fs::ResultInvalidOpenMode());
return this->OpenFileImpl(out_file, path, mode); return this->DoOpenFile(out_file, path, mode);
} }
Result OpenDirectory(std::unique_ptr<IDirectory> *out_dir, const char *path, OpenDirectoryMode mode) { Result OpenDirectory(std::unique_ptr<IDirectory> *out_dir, const char *path, OpenDirectoryMode mode) {
@ -94,98 +94,98 @@ namespace ams::fs::fsa {
R_UNLESS(out_dir != nullptr, fs::ResultNullptrArgument()); R_UNLESS(out_dir != nullptr, fs::ResultNullptrArgument());
R_UNLESS((mode & OpenDirectoryMode_All) != 0, fs::ResultInvalidOpenMode()); R_UNLESS((mode & OpenDirectoryMode_All) != 0, fs::ResultInvalidOpenMode());
R_UNLESS((mode & ~(OpenDirectoryMode_All | OpenDirectoryMode_NotRequireFileSize)) == 0, fs::ResultInvalidOpenMode()); R_UNLESS((mode & ~(OpenDirectoryMode_All | OpenDirectoryMode_NotRequireFileSize)) == 0, fs::ResultInvalidOpenMode());
return this->OpenDirectoryImpl(out_dir, path, mode); return this->DoOpenDirectory(out_dir, path, mode);
} }
Result Commit() { Result Commit() {
return this->CommitImpl(); return this->DoCommit();
} }
Result GetFreeSpaceSize(s64 *out, const char *path) { Result GetFreeSpaceSize(s64 *out, const char *path) {
R_UNLESS(path != nullptr, fs::ResultInvalidPath()); R_UNLESS(path != nullptr, fs::ResultInvalidPath());
R_UNLESS(out != nullptr, fs::ResultNullptrArgument()); R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
return this->GetFreeSpaceSizeImpl(out, path); return this->DoGetFreeSpaceSize(out, path);
} }
Result GetTotalSpaceSize(s64 *out, const char *path) { Result GetTotalSpaceSize(s64 *out, const char *path) {
R_UNLESS(path != nullptr, fs::ResultInvalidPath()); R_UNLESS(path != nullptr, fs::ResultInvalidPath());
R_UNLESS(out != nullptr, fs::ResultNullptrArgument()); R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
return this->GetTotalSpaceSizeImpl(out, path); return this->DoGetTotalSpaceSize(out, path);
} }
Result CleanDirectoryRecursively(const char *path) { Result CleanDirectoryRecursively(const char *path) {
R_UNLESS(path != nullptr, fs::ResultInvalidPath()); R_UNLESS(path != nullptr, fs::ResultInvalidPath());
return this->CleanDirectoryRecursivelyImpl(path); return this->DoCleanDirectoryRecursively(path);
} }
Result GetFileTimeStampRaw(FileTimeStampRaw *out, const char *path) { Result GetFileTimeStampRaw(FileTimeStampRaw *out, const char *path) {
R_UNLESS(path != nullptr, fs::ResultInvalidPath()); R_UNLESS(path != nullptr, fs::ResultInvalidPath());
R_UNLESS(out != nullptr, fs::ResultNullptrArgument()); R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
return this->GetFileTimeStampRawImpl(out, path); return this->DoGetFileTimeStampRaw(out, path);
} }
Result QueryEntry(char *dst, size_t dst_size, const char *src, size_t src_size, QueryId query, const char *path) { Result QueryEntry(char *dst, size_t dst_size, const char *src, size_t src_size, QueryId query, const char *path) {
R_UNLESS(path != nullptr, fs::ResultInvalidPath()); R_UNLESS(path != nullptr, fs::ResultInvalidPath());
return this->QueryEntryImpl(dst, dst_size, src, src_size, query, path); return this->DoQueryEntry(dst, dst_size, src, src_size, query, path);
} }
/* These aren't accessible as commands. */ /* These aren't accessible as commands. */
Result CommitProvisionally(s64 counter) { Result CommitProvisionally(s64 counter) {
return this->CommitProvisionallyImpl(counter); return this->DoCommitProvisionally(counter);
} }
Result Rollback() { Result Rollback() {
return this->RollbackImpl(); return this->DoRollback();
} }
Result Flush() { Result Flush() {
return this->FlushImpl(); return this->DoFlush();
} }
protected: protected:
/* ...? */ /* ...? */
private: private:
virtual Result CreateFileImpl(const char *path, s64 size, int flags) = 0; virtual Result DoCreateFile(const char *path, s64 size, int flags) = 0;
virtual Result DeleteFileImpl(const char *path) = 0; virtual Result DoDeleteFile(const char *path) = 0;
virtual Result CreateDirectoryImpl(const char *path) = 0; virtual Result DoCreateDirectory(const char *path) = 0;
virtual Result DeleteDirectoryImpl(const char *path) = 0; virtual Result DoDeleteDirectory(const char *path) = 0;
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) = 0; virtual Result DoDeleteDirectoryRecursively(const char *path) = 0;
virtual Result RenameFileImpl(const char *old_path, const char *new_path) = 0; virtual Result DoRenameFile(const char *old_path, const char *new_path) = 0;
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) = 0; virtual Result DoRenameDirectory(const char *old_path, const char *new_path) = 0;
virtual Result GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) = 0; virtual Result DoGetEntryType(fs::DirectoryEntryType *out, const char *path) = 0;
virtual Result OpenFileImpl(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) = 0; virtual Result DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) = 0;
virtual Result OpenDirectoryImpl(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) = 0; virtual Result DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) = 0;
virtual Result CommitImpl() = 0; virtual Result DoCommit() = 0;
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) { virtual Result DoGetFreeSpaceSize(s64 *out, const char *path) {
return fs::ResultNotImplemented(); return fs::ResultNotImplemented();
} }
virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) { virtual Result DoGetTotalSpaceSize(s64 *out, const char *path) {
return fs::ResultNotImplemented(); return fs::ResultNotImplemented();
} }
virtual Result CleanDirectoryRecursivelyImpl(const char *path) = 0; virtual Result DoCleanDirectoryRecursively(const char *path) = 0;
virtual Result GetFileTimeStampRawImpl(fs::FileTimeStampRaw *out, const char *path) { virtual Result DoGetFileTimeStampRaw(fs::FileTimeStampRaw *out, const char *path) {
return fs::ResultNotImplemented(); return fs::ResultNotImplemented();
} }
virtual Result QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, fs::fsa::QueryId query, const char *path) { virtual Result DoQueryEntry(char *dst, size_t dst_size, const char *src, size_t src_size, fs::fsa::QueryId query, const char *path) {
return fs::ResultNotImplemented(); return fs::ResultNotImplemented();
} }
/* These aren't accessible as commands. */ /* These aren't accessible as commands. */
virtual Result CommitProvisionallyImpl(s64 counter) { virtual Result DoCommitProvisionally(s64 counter) {
return fs::ResultNotImplemented(); return fs::ResultNotImplemented();
} }
virtual Result RollbackImpl() { virtual Result DoRollback() {
return fs::ResultNotImplemented(); return fs::ResultNotImplemented();
} }
virtual Result FlushImpl() { virtual Result DoFlush() {
return fs::ResultNotImplemented(); return fs::ResultNotImplemented();
} }
}; };

View File

@ -31,7 +31,7 @@ namespace ams::fssrv {
Option_AcceptEmpty = BIT(4), Option_AcceptEmpty = BIT(4),
}; };
private: private:
using Buffer = std::unique_ptr<char[]>; using Buffer = std::unique_ptr<char[], fs::impl::Deleter>;
private: private:
Buffer buffer; Buffer buffer;
const char *path; const char *path;
@ -39,8 +39,9 @@ namespace ams::fssrv {
private: private:
static Result Normalize(const char **out_path, Buffer *out_buf, const char *path, bool preserve_unc, bool preserve_tail_sep, bool has_mount_name); static Result Normalize(const char **out_path, Buffer *out_buf, const char *path, bool preserve_unc, bool preserve_tail_sep, bool has_mount_name);
public: public:
/* TODO: Remove non-option constructor. */
explicit PathNormalizer(const char *p) : buffer(), path(nullptr), result(ResultSuccess()) { explicit PathNormalizer(const char *p) : buffer(), path(nullptr), result(ResultSuccess()) {
this->result = Normalize(&this->path, &this->buffer, p, false, false, false); this->result = Normalize(std::addressof(this->path), std::addressof(this->buffer), p, false, false, false);
} }
PathNormalizer(const char *p, u32 option) : buffer(), path(nullptr), result(ResultSuccess()) { PathNormalizer(const char *p, u32 option) : buffer(), path(nullptr), result(ResultSuccess()) {
@ -50,15 +51,15 @@ namespace ams::fssrv {
const bool preserve_unc = (option & Option_PreserveUnc); const bool preserve_unc = (option & Option_PreserveUnc);
const bool preserve_tail_sep = (option & Option_PreserveTailSeparator); const bool preserve_tail_sep = (option & Option_PreserveTailSeparator);
const bool has_mount_name = (option & Option_HasMountName); const bool has_mount_name = (option & Option_HasMountName);
this->result = Normalize(&this->path, &this->buffer, p, preserve_unc, preserve_tail_sep, has_mount_name); this->result = Normalize(std::addressof(this->path), std::addressof(this->buffer), p, preserve_unc, preserve_tail_sep, has_mount_name);
} }
} }
inline Result GetResult() const { Result GetResult() const {
return this->result; return this->result;
} }
inline const char * GetPath() const { const char *GetPath() const {
return this->path; return this->path;
} }
}; };

View File

@ -21,7 +21,6 @@
#include <stratosphere/fssystem/fssystem_external_code.hpp> #include <stratosphere/fssystem/fssystem_external_code.hpp>
#include <stratosphere/fssystem/fssystem_partition_file_system.hpp> #include <stratosphere/fssystem/fssystem_partition_file_system.hpp>
#include <stratosphere/fssystem/fssystem_partition_file_system_meta.hpp> #include <stratosphere/fssystem/fssystem_partition_file_system_meta.hpp>
#include <stratosphere/fssystem/fssystem_path_tool.hpp>
#include <stratosphere/fssystem/fssystem_thread_priority_changer.hpp> #include <stratosphere/fssystem/fssystem_thread_priority_changer.hpp>
#include <stratosphere/fssystem/fssystem_aes_ctr_storage.hpp> #include <stratosphere/fssystem/fssystem_aes_ctr_storage.hpp>
#include <stratosphere/fssystem/fssystem_aes_xts_storage.hpp> #include <stratosphere/fssystem/fssystem_aes_xts_storage.hpp>

View File

@ -61,7 +61,7 @@ namespace ams::fssystem {
s64 bs_size = 0; s64 bs_size = 0;
R_TRY(this->GetSize(std::addressof(bs_size))); R_TRY(this->GetSize(std::addressof(bs_size)));
R_UNLESS(fs::IStorage::IsRangeValid(offset, size, bs_size), fs::ResultOutOfRange()); R_UNLESS(fs::IStorage::CheckAccessRange(offset, size, bs_size), fs::ResultOutOfRange());
return AlignmentMatchingStorageImpl::Read(this->base_storage, work_buf, sizeof(work_buf), DataAlign, BufferAlign, offset, static_cast<char *>(buffer), size); return AlignmentMatchingStorageImpl::Read(this->base_storage, work_buf, sizeof(work_buf), DataAlign, BufferAlign, offset, static_cast<char *>(buffer), size);
} }
@ -79,7 +79,7 @@ namespace ams::fssystem {
s64 bs_size = 0; s64 bs_size = 0;
R_TRY(this->GetSize(std::addressof(bs_size))); R_TRY(this->GetSize(std::addressof(bs_size)));
R_UNLESS(fs::IStorage::IsRangeValid(offset, size, bs_size), fs::ResultOutOfRange()); R_UNLESS(fs::IStorage::CheckAccessRange(offset, size, bs_size), fs::ResultOutOfRange());
return AlignmentMatchingStorageImpl::Write(this->base_storage, work_buf, sizeof(work_buf), DataAlign, BufferAlign, offset, static_cast<const char *>(buffer), size); return AlignmentMatchingStorageImpl::Write(this->base_storage, work_buf, sizeof(work_buf), DataAlign, BufferAlign, offset, static_cast<const char *>(buffer), size);
} }
@ -115,7 +115,7 @@ namespace ams::fssystem {
/* Get the base storage size. */ /* Get the base storage size. */
s64 bs_size = 0; s64 bs_size = 0;
R_TRY(this->GetSize(std::addressof(bs_size))); R_TRY(this->GetSize(std::addressof(bs_size)));
R_UNLESS(fs::IStorage::IsOffsetAndSizeValid(offset, size), fs::ResultOutOfRange()); R_UNLESS(fs::IStorage::CheckOffsetAndSize(offset, size), fs::ResultOutOfRange());
/* Operate on the base storage. */ /* Operate on the base storage. */
const auto valid_size = std::min(size, bs_size - offset); const auto valid_size = std::min(size, bs_size - offset);
@ -154,7 +154,7 @@ namespace ams::fssystem {
s64 bs_size = 0; s64 bs_size = 0;
R_TRY(this->GetSize(std::addressof(bs_size))); R_TRY(this->GetSize(std::addressof(bs_size)));
R_UNLESS(fs::IStorage::IsRangeValid(offset, size, bs_size), fs::ResultOutOfRange()); R_UNLESS(fs::IStorage::CheckAccessRange(offset, size, bs_size), fs::ResultOutOfRange());
/* Allocate a pooled buffer. */ /* Allocate a pooled buffer. */
PooledBuffer pooled_buffer; PooledBuffer pooled_buffer;
@ -172,7 +172,7 @@ namespace ams::fssystem {
s64 bs_size = 0; s64 bs_size = 0;
R_TRY(this->GetSize(std::addressof(bs_size))); R_TRY(this->GetSize(std::addressof(bs_size)));
R_UNLESS(fs::IStorage::IsRangeValid(offset, size, bs_size), fs::ResultOutOfRange()); R_UNLESS(fs::IStorage::CheckAccessRange(offset, size, bs_size), fs::ResultOutOfRange());
/* Allocate a pooled buffer. */ /* Allocate a pooled buffer. */
PooledBuffer pooled_buffer; PooledBuffer pooled_buffer;
@ -212,7 +212,7 @@ namespace ams::fssystem {
/* Get the base storage size. */ /* Get the base storage size. */
s64 bs_size = 0; s64 bs_size = 0;
R_TRY(this->GetSize(std::addressof(bs_size))); R_TRY(this->GetSize(std::addressof(bs_size)));
R_UNLESS(fs::IStorage::IsOffsetAndSizeValid(offset, size), fs::ResultOutOfRange()); R_UNLESS(fs::IStorage::CheckOffsetAndSize(offset, size), fs::ResultOutOfRange());
/* Operate on the base storage. */ /* Operate on the base storage. */
const auto valid_size = std::min(size, bs_size - offset); const auto valid_size = std::min(size, bs_size - offset);
@ -257,7 +257,7 @@ namespace ams::fssystem {
s64 bs_size = 0; s64 bs_size = 0;
R_TRY(this->GetSize(std::addressof(bs_size))); R_TRY(this->GetSize(std::addressof(bs_size)));
R_UNLESS(fs::IStorage::IsRangeValid(offset, size, bs_size), fs::ResultOutOfRange()); R_UNLESS(fs::IStorage::CheckAccessRange(offset, size, bs_size), fs::ResultOutOfRange());
/* Allocate a pooled buffer. */ /* Allocate a pooled buffer. */
PooledBuffer pooled_buffer(this->data_align, this->data_align); PooledBuffer pooled_buffer(this->data_align, this->data_align);
@ -294,7 +294,7 @@ namespace ams::fssystem {
/* Get the base storage size. */ /* Get the base storage size. */
s64 bs_size = 0; s64 bs_size = 0;
R_TRY(this->GetSize(std::addressof(bs_size))); R_TRY(this->GetSize(std::addressof(bs_size)));
R_UNLESS(fs::IStorage::IsOffsetAndSizeValid(offset, size), fs::ResultOutOfRange()); R_UNLESS(fs::IStorage::CheckOffsetAndSize(offset, size), fs::ResultOutOfRange());
/* Operate on the base storage. */ /* Operate on the base storage. */
const auto valid_size = std::min(size, bs_size - offset); const auto valid_size = std::min(size, bs_size - offset);

View File

@ -1,684 +0,0 @@
/*
* Copyright (c) 2018-2020 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 <vapours.hpp>
#include <stratosphere/fssystem/fssystem_dbm_rom_types.hpp>
#include <stratosphere/fssystem/fssystem_dbm_rom_path_tool.hpp>
#include <stratosphere/fssystem/fssystem_dbm_rom_key_value_storage.hpp>
namespace ams::fssystem {
template<typename DBS, typename DES, typename FBS, typename FES>
class HierarchicalRomFileTable {
private:
using DirectoryBucketStorage = DBS;
using DirectoryEntryStorage = DES;
using FileBucketStorage = FBS;
using FileEntryStorage = FES;
public:
using Position = u32;
struct FindPosition {
Position next_dir;
Position next_file;
};
static_assert(util::is_pod<FindPosition>::value);
using DirectoryInfo = RomDirectoryInfo;
using FileInfo = RomFileInfo;
static constexpr RomFileId ConvertToFileId(Position pos) {
return static_cast<RomFileId>(pos);
}
private:
static constexpr inline Position InvalidPosition = ~Position();
static constexpr inline Position RootPosition = 0;
static constexpr inline size_t ReservedDirectoryCount = 1;
static constexpr RomDirectoryId ConvertToDirectoryId(Position pos) {
return static_cast<RomDirectoryId>(pos);
}
static constexpr Position ConvertToPosition(RomDirectoryId id) {
return static_cast<Position>(id);
}
static_assert(std::is_same<RomDirectoryId, RomFileId>::value);
struct RomDirectoryEntry {
Position next;
Position dir;
Position file;
};
static_assert(util::is_pod<RomDirectoryEntry>::value);
struct RomFileEntry {
Position next;
FileInfo info;
};
static_assert(util::is_pod<RomFileEntry>::value);
static constexpr inline u32 MaxKeyLength = RomPathTool::MaxPathLength;
template<typename BucketStorageType, typename EntryStorageType, typename ImplKeyType, typename ClientKeyType, typename ValueType>
class EntryMapTable : public RomKeyValueStorage<BucketStorageType, EntryStorageType, ImplKeyType, ValueType, MaxKeyLength> {
private:
using BucketStorage = BucketStorageType;
using EntryStorage = EntryStorageType;
public:
using ImplKey = ImplKeyType;
using ClientKey = ClientKeyType;
using Value = ValueType;
using Position = HierarchicalRomFileTable::Position;
using Base = RomKeyValueStorage<BucketStorageType, EntryStorageType, ImplKeyType, ValueType, MaxKeyLength>;
public:
Result Add(Position *out, const ClientKeyType &key, const Value &value) {
return Base::AddImpl(out, key.key, key.Hash(), key.name.path, key.name.length * sizeof(RomPathChar), value);
}
Result Get(Position *out_pos, Value *out_val, const ClientKeyType &key) const {
return Base::GetImpl(out_pos, out_val, key.key, key.Hash(), key.name.path, key.name.length * sizeof(RomPathChar));
}
Result GetByPosition(ImplKey *out_key, Value *out_val, void *out_aux, size_t *out_aux_size, Position pos) const {
return Base::GetByPosition(out_key, out_val, out_aux, out_aux_size, pos);
}
Result SetByPosition(Position pos, const Value &value, fs::WriteOption option) const {
return Base::SetByPosition(pos, value, option);
}
};
struct RomEntryKey {
Position parent;
bool IsEqual(const RomEntryKey &rhs, const void *aux_lhs, size_t aux_lhs_size, const void *aux_rhs, size_t aux_rhs_size) const {
if (this->parent != rhs.parent) {
return false;
}
if (aux_lhs_size != aux_rhs_size) {
return false;
}
return RomPathTool::IsEqualPath(reinterpret_cast<const RomPathChar *>(aux_lhs), reinterpret_cast<const RomPathChar *>(aux_rhs), aux_lhs_size / sizeof(RomPathChar));
}
};
static_assert(util::is_pod<RomEntryKey>::value);
struct EntryKey {
RomEntryKey key;
RomPathTool::RomEntryName name;
constexpr u32 Hash() const {
u32 hash = this->key.parent ^ 123456789;
const RomPathChar *name = this->name.path;
const RomPathChar *end = name + this->name.length;
while (name < end) {
const u32 cur = static_cast<u32>(static_cast<std::make_unsigned<RomPathChar>::type>(*(name++)));
hash = ((hash >> 5) | (hash << 27)) ^ cur;
}
return hash;
}
};
static_assert(util::is_pod<EntryKey>::value);
using DirectoryEntryMapTable = EntryMapTable<DirectoryBucketStorage, DirectoryEntryStorage, RomEntryKey, EntryKey, RomDirectoryEntry>;
using FileEntryMapTable = EntryMapTable<FileBucketStorage, FileEntryStorage, RomEntryKey, EntryKey, RomFileEntry>;
private:
DirectoryEntryMapTable dir_table;
FileEntryMapTable file_table;
public:
static u32 QueryDirectoryEntrySize(u32 name_len) {
AMS_ABORT_UNLESS(name_len <= RomPathTool::MaxPathLength);
return DirectoryEntryMapTable::QueryEntrySize(name_len * sizeof(RomPathChar));
}
static u32 QueryFileEntrySize(u32 name_len) {
AMS_ABORT_UNLESS(name_len <= RomPathTool::MaxPathLength);
return FileEntryMapTable::QueryEntrySize(name_len * sizeof(RomPathChar));
}
static u32 QueryDirectoryEntryBucketStorageSize(u32 count) { return DirectoryEntryMapTable::QueryBucketStorageSize(count); }
static u32 QueryFileEntryBucketStorageSize(u32 count) { return FileEntryMapTable::QueryBucketStorageSize(count); }
static Result Format(DirectoryBucketStorage *dir_bucket, s64 dir_bucket_ofs, u32 dir_bucket_size, DirectoryEntryStorage *dir_entry, s64 dir_entry_ofs, u32 dir_entry_size, FileBucketStorage *file_bucket, s64 file_bucket_ofs, u32 file_bucket_size, FileEntryStorage *file_entry, s64 file_entry_ofs, u32 file_entry_size) {
R_TRY(DirectoryEntryMapTable::Format(dir_bucket, dir_bucket_ofs, DirectoryEntryMapTable::QueryBucketCount(dir_bucket_size), dir_entry, dir_entry_ofs, dir_entry_size));
R_TRY(FileEntryMapTable::Format(file_bucket, file_bucket_ofs, FileEntryMapTable::QueryBucketCount(file_bucket_size), file_entry, file_entry_ofs, file_entry_size));
return ResultSuccess();
}
public:
HierarchicalRomFileTable() { /* ... */ }
constexpr u32 GetDirectoryEntryCount() const {
return this->dir_table.GetEntryCount();
}
constexpr u32 GetFileEntryCount() const {
return this->file_table.GetEntryCount();
}
Result Initialize(DirectoryBucketStorage *dir_bucket, s64 dir_bucket_ofs, u32 dir_bucket_size, DirectoryEntryStorage *dir_entry, s64 dir_entry_ofs, u32 dir_entry_size, FileBucketStorage *file_bucket, s64 file_bucket_ofs, u32 file_bucket_size, FileEntryStorage *file_entry, s64 file_entry_ofs, u32 file_entry_size) {
AMS_ASSERT(dir_bucket != nullptr);
AMS_ASSERT(dir_entry != nullptr);
AMS_ASSERT(file_bucket != nullptr);
AMS_ASSERT(file_entry != nullptr);
R_TRY(this->dir_table.Initialize(dir_bucket, dir_bucket_ofs, DirectoryEntryMapTable::QueryBucketCount(dir_bucket_size), dir_entry, dir_entry_ofs, dir_entry_size));
R_TRY(this->file_table.Initialize(file_bucket, file_bucket_ofs, FileEntryMapTable::QueryBucketCount(file_bucket_size), file_entry, file_entry_ofs, file_entry_size));
return ResultSuccess();
}
void Finalize() {
this->dir_table.Finalize();
this->file_table.Finalize();
}
Result CreateRootDirectory() {
Position root_pos = RootPosition;
EntryKey root_key = {};
root_key.key.parent = root_pos;
RomPathTool::InitializeRomEntryName(std::addressof(root_key.name));
RomDirectoryEntry root_entry = {
.next = InvalidPosition,
.dir = InvalidPosition,
.file = InvalidPosition,
};
return this->dir_table.Add(std::addressof(root_pos), root_key, root_entry);
}
Result CreateDirectory(RomDirectoryId *out, const RomPathChar *path, const DirectoryInfo &info) {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
EntryKey parent_key = {};
RomDirectoryEntry parent_entry = {};
EntryKey new_key = {};
R_TRY(this->FindDirectoryRecursive(std::addressof(parent_key), std::addressof(parent_entry), std::addressof(new_key), path));
R_TRY(this->CheckSameEntryExists(new_key, fs::ResultDbmAlreadyExists()));
RomDirectoryEntry new_entry = {
.next = InvalidPosition,
.dir = InvalidPosition,
.file = InvalidPosition,
};
Position new_pos = 0;
R_TRY_CATCH(this->dir_table.Add(std::addressof(new_pos), new_key, new_entry)) {
R_CONVERT(fs::ResultDbmKeyFull, fs::ResultDbmDirectoryEntryFull())
} R_END_TRY_CATCH;
*out = ConvertToDirectoryId(new_pos);
if (parent_entry.dir == InvalidPosition) {
parent_entry.dir = new_pos;
R_TRY(this->dir_table.SetByPosition(new_key.key.parent, parent_entry, fs::WriteOption::None));
} else {
Position cur_pos = parent_entry.dir;
while (true) {
RomEntryKey cur_key = {};
RomDirectoryEntry cur_entry = {};
R_TRY(this->dir_table.GetByPosition(std::addressof(cur_key), std::addressof(cur_entry), nullptr, nullptr, cur_pos));
if (cur_entry.next == InvalidPosition) {
cur_entry.next = new_pos;
R_TRY(this->dir_table.SetByPosition(cur_pos, cur_entry, fs::WriteOption::None));
break;
}
cur_pos = cur_entry.next;
}
}
return ResultSuccess();
}
Result CreateFile(RomFileId *out, const RomPathChar *path, const FileInfo &info) {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
EntryKey parent_key = {};
RomDirectoryEntry parent_entry = {};
EntryKey new_key = {};
R_TRY(this->FindFileRecursive(std::addressof(parent_key), std::addressof(parent_entry), std::addressof(new_key), path));
R_TRY(this->CheckSameEntryExists(new_key, fs::ResultDbmAlreadyExists()));
RomFileEntry new_entry = {
.next = InvalidPosition,
.info = info,
};
Position new_pos = 0;
R_TRY_CATCH(this->file_table.Add(std::addressof(new_pos), new_key, new_entry)) {
R_CONVERT(fs::ResultDbmKeyFull, fs::ResultDbmFileEntryFull())
} R_END_TRY_CATCH;
*out = ConvertToFileId(new_pos);
if (parent_entry.file == InvalidPosition) {
parent_entry.file = new_pos;
R_TRY(this->dir_table.SetByPosition(new_key.key.parent, parent_entry, fs::WriteOption::None));
} else {
Position cur_pos = parent_entry.file;
while (true) {
RomEntryKey cur_key = {};
RomFileEntry cur_entry = {};
R_TRY(this->file_table.GetByPosition(std::addressof(cur_key), std::addressof(cur_entry), nullptr, nullptr, cur_pos));
if (cur_entry.next == InvalidPosition) {
cur_entry.next = new_pos;
R_TRY(this->file_table.SetByPosition(cur_pos, cur_entry, fs::WriteOption::None));
break;
}
cur_pos = cur_entry.next;
}
}
return ResultSuccess();
}
Result ConvertPathToDirectoryId(RomDirectoryId *out, const RomPathChar *path) const {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
EntryKey parent_key = {};
RomDirectoryEntry parent_entry = {};
EntryKey key = {};
R_TRY(this->FindDirectoryRecursive(std::addressof(parent_key), std::addressof(parent_entry), std::addressof(key), path));
Position pos = 0;
RomDirectoryEntry entry = {};
R_TRY(this->GetDirectoryEntry(std::addressof(pos), std::addressof(entry), key));
*out = ConvertToDirectoryId(pos);
return ResultSuccess();
}
Result ConvertPathToFileId(RomFileId *out, const RomPathChar *path) const {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
EntryKey parent_key = {};
RomDirectoryEntry parent_entry = {};
EntryKey key = {};
R_TRY(this->FindDirectoryRecursive(std::addressof(parent_key), std::addressof(parent_entry), std::addressof(key), path));
Position pos = 0;
RomFileEntry entry = {};
R_TRY(this->GetFileEntry(std::addressof(pos), std::addressof(entry), key));
*out = ConvertToFileId(pos);
return ResultSuccess();
}
Result GetDirectoryInformation(DirectoryInfo *out, const RomPathChar *path) const {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
EntryKey parent_key = {};
RomDirectoryEntry parent_entry = {};
EntryKey key = {};
R_TRY(this->FindDirectoryRecursive(std::addressof(parent_key), std::addressof(parent_entry), std::addressof(key), path));
return this->GetDirectoryInformation(out, key);
}
Result GetDirectoryInformation(DirectoryInfo *out, RomDirectoryId id) const {
AMS_ASSERT(out != nullptr);
RomDirectoryEntry entry = {};
R_TRY(this->GetDirectoryEntry(std::addressof(entry), id));
return ResultSuccess();
}
Result OpenFile(FileInfo *out, const RomPathChar *path) const {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
EntryKey parent_key = {};
RomDirectoryEntry parent_entry = {};
EntryKey key = {};
R_TRY(this->FindFileRecursive(std::addressof(parent_key), std::addressof(parent_entry), std::addressof(key), path));
return this->OpenFile(out, key);
}
Result OpenFile(FileInfo *out, RomFileId id) const {
AMS_ASSERT(out != nullptr);
RomFileEntry entry = {};
R_TRY(this->GetFileEntry(std::addressof(entry), id));
*out = entry.info;
return ResultSuccess();
}
Result FindOpen(FindPosition *out, const RomPathChar *path) const {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
EntryKey parent_key = {};
RomDirectoryEntry parent_entry = {};
EntryKey key = {};
R_TRY(this->FindDirectoryRecursive(std::addressof(parent_key), std::addressof(parent_entry), std::addressof(key), path));
return this->FindOpen(out, key);
}
Result FindOpen(FindPosition *out, RomDirectoryId id) const {
AMS_ASSERT(out != nullptr);
out->next_dir = InvalidPosition;
out->next_file = InvalidPosition;
RomDirectoryEntry entry = {};
R_TRY(this->GetDirectoryEntry(std::addressof(entry), id));
out->next_dir = entry.dir;
out->next_file = entry.file;
return ResultSuccess();
}
Result FindNextDirectory(RomPathChar *out, FindPosition *find) const {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(find != nullptr);
R_UNLESS(find->next_dir != InvalidPosition, fs::ResultDbmFindFinished());
RomEntryKey key = {};
RomDirectoryEntry entry = {};
size_t aux_size = 0;
R_TRY(this->dir_table.GetByPosition(std::addressof(key), std::addressof(entry), out, std::addressof(aux_size), find->next_dir));
out[aux_size / sizeof(RomPathChar)] = RomStringTraits::NullTerminator;
find->next_dir = entry.next;
return ResultSuccess();
}
Result FindNextFile(RomPathChar *out, FindPosition *find) const {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(find != nullptr);
R_UNLESS(find->next_file != InvalidPosition, fs::ResultDbmFindFinished());
RomEntryKey key = {};
RomFileEntry entry = {};
size_t aux_size = 0;
R_TRY(this->file_table.GetByPosition(std::addressof(key), std::addressof(entry), out, std::addressof(aux_size), find->next_file));
out[aux_size / sizeof(RomPathChar)] = RomStringTraits::NullTerminator;
find->next_file = entry.next;
return ResultSuccess();
}
Result QueryRomFileSystemSize(u32 *out_dir_entry_size, u32 *out_file_entry_size) {
AMS_ASSERT(out_dir_entry_size != nullptr);
AMS_ASSERT(out_file_entry_size != nullptr);
*out_dir_entry_size = this->dir_table.GetTotalEntrySize();
*out_file_entry_size = this->file_table.GetTotalEntrySize();
return ResultSuccess();
}
private:
Result GetGrandParent(Position *out_pos, EntryKey *out_dir_key, RomDirectoryEntry *out_dir_entry, Position pos, RomPathTool::RomEntryName name, const RomPathChar *path) const {
AMS_ASSERT(out_pos != nullptr);
AMS_ASSERT(out_dir_key != nullptr);
AMS_ASSERT(out_dir_entry != nullptr);
RomEntryKey gp_key = {};
RomDirectoryEntry gp_entry = {};
R_TRY(this->dir_table.GetByPosition(std::addressof(gp_key), std::addressof(gp_entry), nullptr, nullptr, pos));
out_dir_key->key.parent = gp_key.parent;
R_TRY(RomPathTool::GetParentDirectoryName(std::addressof(out_dir_key->name), name, path));
R_TRY(this->GetDirectoryEntry(out_pos, out_dir_entry, *out_dir_key));
return ResultSuccess();
}
Result FindParentDirectoryRecursive(Position *out_pos, EntryKey *out_dir_key, RomDirectoryEntry *out_dir_entry, RomPathTool::PathParser &parser, const RomPathChar *path) const {
AMS_ASSERT(out_pos != nullptr);
AMS_ASSERT(out_dir_key != nullptr);
AMS_ASSERT(out_dir_entry != nullptr);
Position dir_pos = RootPosition;
EntryKey dir_key = {};
RomDirectoryEntry dir_entry = {};
dir_key.key.parent = RootPosition;
R_TRY(parser.GetNextDirectoryName(std::addressof(dir_key.name)));
R_TRY(this->GetDirectoryEntry(std::addressof(dir_pos), std::addressof(dir_entry), dir_key));
Position parent_pos = dir_pos;
while (!parser.IsFinished()) {
EntryKey old_key = dir_key;
R_TRY(parser.GetNextDirectoryName(std::addressof(dir_key.name)));
if (RomPathTool::IsCurrentDirectory(dir_key.name)) {
dir_key = old_key;
continue;
} else if (RomPathTool::IsParentDirectory(dir_key.name)) {
R_UNLESS(parent_pos != RootPosition, fs::ResultDbmInvalidOperation());
R_TRY(this->GetGrandParent(std::addressof(parent_pos), std::addressof(dir_key), std::addressof(dir_entry), dir_key.key.parent, dir_key.name, path));
} else {
dir_key.key.parent = parent_pos;
R_TRY(this->GetDirectoryEntry(std::addressof(dir_pos), std::addressof(dir_entry), dir_key));
parent_pos = dir_pos;
}
}
*out_pos = parent_pos;
*out_dir_key = dir_key;
*out_dir_entry = dir_entry;
return ResultSuccess();
}
Result FindPathRecursive(EntryKey *out_parent_key, RomDirectoryEntry *out_parent_dir_entry, EntryKey *out_key, bool is_dir, const RomPathChar *path) const {
AMS_ASSERT(out_parent_key != nullptr);
AMS_ASSERT(out_parent_dir_entry != nullptr);
AMS_ASSERT(out_key != nullptr);
AMS_ASSERT(path != nullptr);
RomPathTool::PathParser parser;
R_TRY(parser.Initialize(path));
Position parent_pos = 0;
R_TRY(this->FindParentDirectoryRecursive(std::addressof(parent_pos), out_parent_key, out_parent_dir_entry, parser, path));
if (is_dir) {
RomPathTool::RomEntryName name = {};
R_TRY(parser.GetAsDirectoryName(std::addressof(name)));
if (RomPathTool::IsCurrentDirectory(name)) {
*out_key = *out_parent_key;
if (out_key->key.parent != RootPosition) {
Position pos = 0;
R_TRY(this->GetGrandParent(std::addressof(pos), out_parent_key, out_parent_dir_entry, out_key->key.parent, out_key->name, path));
}
} else if (RomPathTool::IsParentDirectory(name)) {
R_UNLESS(parent_pos != RootPosition, fs::ResultDbmInvalidOperation());
Position pos = 0;
RomDirectoryEntry cur_entry = {};
R_TRY(this->GetGrandParent(std::addressof(pos), out_key, std::addressof(cur_entry), out_parent_key->key.parent, out_parent_key->name, path));
if (out_key->key.parent != RootPosition) {
R_TRY(this->GetGrandParent(std::addressof(pos), out_parent_key, out_parent_dir_entry, out_key->key.parent, out_key->name, path));
}
} else {
out_key->name = name;
out_key->key.parent = (out_key->name.length > 0) ? parent_pos : RootPosition;
}
} else {
R_UNLESS(!parser.IsDirectoryPath(), fs::ResultDbmInvalidOperation());
out_key->key.parent = parent_pos;
R_TRY(parser.GetAsFileName(std::addressof(out_key->name)));
}
return ResultSuccess();
}
Result FindDirectoryRecursive(EntryKey *out_parent_key, RomDirectoryEntry *out_parent_dir_entry, EntryKey *out_key, const RomPathChar *path) const {
return this->FindPathRecursive(out_parent_key, out_parent_dir_entry, out_key, true, path);
}
Result FindFileRecursive(EntryKey *out_parent_key, RomDirectoryEntry *out_parent_dir_entry, EntryKey *out_key, const RomPathChar *path) const {
return this->FindPathRecursive(out_parent_key, out_parent_dir_entry, out_key, false, path);
}
Result CheckSameEntryExists(const EntryKey &key, Result if_exists) const {
/* Check dir */
{
Position pos = InvalidPosition;
RomDirectoryEntry entry = {};
const Result get_res = this->dir_table.Get(std::addressof(pos), std::addressof(entry), key);
if (!fs::ResultDbmKeyNotFound::Includes(get_res)) {
R_TRY(get_res);
return if_exists;
}
}
/* Check file */
{
Position pos = InvalidPosition;
RomFileEntry entry = {};
const Result get_res = this->file_table.Get(std::addressof(pos), std::addressof(entry), key);
if (!fs::ResultDbmKeyNotFound::Includes(get_res)) {
R_TRY(get_res);
return if_exists;
}
}
return ResultSuccess();
}
Result GetDirectoryEntry(Position *out_pos, RomDirectoryEntry *out_entry, const EntryKey &key) const {
AMS_ASSERT(out_pos != nullptr);
AMS_ASSERT(out_entry != nullptr);
const Result dir_res = this->dir_table.Get(out_pos, out_entry, key);
R_UNLESS(R_FAILED(dir_res), dir_res);
R_UNLESS(fs::ResultDbmKeyNotFound::Includes(dir_res), dir_res);
Position pos = 0;
RomFileEntry entry = {};
const Result file_res = this->file_table.Get(std::addressof(pos), std::addressof(entry), key);
R_UNLESS(R_FAILED(file_res), fs::ResultDbmInvalidOperation());
R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(file_res), fs::ResultDbmDirectoryNotFound());
return file_res;
}
Result GetDirectoryEntry(RomDirectoryEntry *out_entry, RomDirectoryId id) const {
AMS_ASSERT(out_entry != nullptr);
Position pos = ConvertToPosition(id);
RomEntryKey key = {};
const Result dir_res = this->dir_table.GetByPosition(std::addressof(key), out_entry, nullptr, nullptr, pos);
R_UNLESS(R_FAILED(dir_res), dir_res);
R_UNLESS(fs::ResultDbmKeyNotFound::Includes(dir_res), dir_res);
RomFileEntry entry = {};
const Result file_res = this->file_table.GetByPosition(std::addressof(key), std::addressof(entry), nullptr, nullptr, pos);
R_UNLESS(R_FAILED(file_res), fs::ResultDbmInvalidOperation());
R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(file_res), fs::ResultDbmDirectoryNotFound());
return file_res;
}
Result GetFileEntry(Position *out_pos, RomFileEntry *out_entry, const EntryKey &key) const {
AMS_ASSERT(out_pos != nullptr);
AMS_ASSERT(out_entry != nullptr);
const Result file_res = this->file_table.Get(out_pos, out_entry, key);
R_UNLESS(R_FAILED(file_res), file_res);
R_UNLESS(fs::ResultDbmKeyNotFound::Includes(file_res), file_res);
Position pos = 0;
RomDirectoryEntry entry = {};
const Result dir_res = this->dir_table.Get(std::addressof(pos), std::addressof(entry), key);
R_UNLESS(R_FAILED(dir_res), fs::ResultDbmInvalidOperation());
R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(dir_res), fs::ResultDbmFileNotFound());
return dir_res;
}
Result GetFileEntry(RomFileEntry *out_entry, RomFileId id) const {
AMS_ASSERT(out_entry != nullptr);
Position pos = ConvertToPosition(id);
RomEntryKey key = {};
const Result file_res = this->file_table.GetByPosition(std::addressof(key), out_entry, nullptr, nullptr, pos);
R_UNLESS(R_FAILED(file_res), file_res);
R_UNLESS(fs::ResultDbmKeyNotFound::Includes(file_res), file_res);
RomDirectoryEntry entry = {};
const Result dir_res = this->dir_table.GetByPosition(std::addressof(key), std::addressof(entry), nullptr, nullptr, pos);
R_UNLESS(R_FAILED(dir_res), fs::ResultDbmInvalidOperation());
R_UNLESS(!fs::ResultDbmKeyNotFound::Includes(dir_res), fs::ResultDbmFileNotFound());
return dir_res;
}
Result GetDirectoryInformation(DirectoryInfo *out, const EntryKey &key) const {
AMS_ASSERT(out != nullptr);
Position pos = 0;
RomDirectoryEntry entry = {};
R_TRY(this->GetDirectoryEntry(std::addressof(pos), std::addressof(entry), key));
return ResultSuccess();
}
Result OpenFile(FileInfo *out, const EntryKey &key) const {
AMS_ASSERT(out != nullptr);
Position pos = 0;
RomFileEntry entry = {};
R_TRY(this->GetFileEntry(std::addressof(pos), std::addressof(entry), key));
*out = entry.info;
return ResultSuccess();
}
Result FindOpen(FindPosition *out, const EntryKey &key) const {
AMS_ASSERT(out != nullptr);
out->next_dir = InvalidPosition;
out->next_file = InvalidPosition;
Position pos = 0;
RomDirectoryEntry entry = {};
R_TRY(this->GetDirectoryEntry(std::addressof(pos), std::addressof(entry), key));
out->next_dir = entry.dir;
out->next_file = entry.file;
return ResultSuccess();
}
};
}

View File

@ -1,367 +0,0 @@
/*
* Copyright (c) 2018-2020 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 <vapours.hpp>
#include <stratosphere/fssystem/fssystem_dbm_rom_types.hpp>
namespace ams::fssystem {
constexpr ALWAYS_INLINE u32 AlignRomAddress(u32 addr) {
return util::AlignUp(addr, sizeof(addr));
}
template<typename BucketStorageType, typename EntryStorageType, typename KeyType, typename ValueType, size_t MaxAuxiliarySize>
class RomKeyValueStorage {
public:
using BucketStorage = BucketStorageType;
using EntryStorage = EntryStorageType;
using Key = KeyType;
using Value = ValueType;
using Position = u32;
using BucketIndex = u32;
struct FindIndex {
BucketIndex ind;
Position pos;
};
static_assert(util::is_pod<FindIndex>::value);
private:
static constexpr inline Position InvalidPosition = ~Position();
struct Element {
Key key;
Value value;
Position next;
u32 size;
};
static_assert(util::is_pod<Element>::value);
private:
s64 bucket_offset;
u32 bucket_count;
BucketStorage *bucket_storage;
s64 kv_offset;
u32 kv_size;
EntryStorage *kv_storage;
u32 total_entry_size;
u32 entry_count;
public:
static constexpr u32 QueryEntrySize(u32 aux_size) {
return AlignRomAddress(sizeof(Element) + aux_size);
}
static constexpr u32 QueryBucketStorageSize(u32 num) {
return num * sizeof(Position);
}
static constexpr u32 QueryBucketCount(u32 size) {
return size / sizeof(Position);
}
static constexpr u32 QueryKeyValueStorageSize(u32 num) {
return num * sizeof(Element);
}
static Result Format(BucketStorage *bucket, s64 bucket_ofs, u32 bucket_count, EntryStorage *kv, s64 kv_ofs, u32 kv_size) {
AMS_ASSERT(bucket != nullptr);
AMS_ASSERT(kv != nullptr);
AMS_ASSERT(kv_size >= 0);
const Position pos = InvalidPosition;
for (s64 i = 0; i < bucket_count; i++) {
R_TRY(bucket->Write(bucket_ofs + i * sizeof(pos), std::addressof(pos), sizeof(pos)));
}
return ResultSuccess();
}
public:
RomKeyValueStorage() : bucket_offset(), bucket_count(), bucket_storage(), kv_offset(), kv_size(), kv_storage(), total_entry_size(), entry_count() { /* ... */ }
Result Initialize(BucketStorage *bucket, s64 bucket_ofs, u32 bucket_count, EntryStorage *kv, s64 kv_ofs, u32 kv_size) {
AMS_ASSERT(bucket != nullptr);
AMS_ASSERT(kv != nullptr);
AMS_ASSERT(bucket_count > 0);
this->bucket_storage = bucket;
this->bucket_offset = bucket_ofs;
this->bucket_count = bucket_count;
this->kv_storage = kv;
this->kv_offset = kv_ofs;
this->kv_size = kv_size;
return ResultSuccess();
}
void Finalize() {
this->bucket_storage = nullptr;
this->bucket_offset = 0;
this->bucket_count = 0;
this->kv_storage = nullptr;
this->kv_offset = 0;
this->kv_size = 0;
}
constexpr u32 GetTotalEntrySize() const {
return this->total_entry_size;
}
constexpr u32 GetFreeSize() const {
return (this->kv_size - this->total_entry_size);
}
constexpr u32 GetEntryCount() const {
return this->entry_count;
}
Result Add(const Key &key, u32 hash_key, const void *aux, size_t aux_size, const Value &value) {
AMS_ASSERT(aux != nullptr);
AMS_ASSERT(aux_size <= MaxAuxiliarySize);
Position pos;
return this->AddImpl(std::addressof(pos), key, hash_key, aux, aux_size, value);
}
Result Get(Value *out, const Key &key, u32 hash_key, const void *aux, size_t aux_size) {
AMS_ASSERT(aux != nullptr);
AMS_ASSERT(aux_size <= MaxAuxiliarySize);
Position pos;
return this->GetImpl(std::addressof(pos), out, key, hash_key, aux, aux_size);
}
void FindOpen(FindIndex *out) const {
AMS_ASSERT(out != nullptr);
out->ind = static_cast<BucketIndex>(-1);
out->pos = InvalidPosition;
}
Result FindNext(Key *out_key, Value *out_val, FindIndex *find) {
AMS_ASSERT(out_key != nullptr);
AMS_ASSERT(out_val != nullptr);
AMS_ASSERT(find != nullptr);
Element elem;
BucketIndex ind = find->ind;
R_UNLESS((ind < this->bucket_count) || ind == static_cast<BucketIndex>(-1), fs::ResultDbmFindKeyFinished());
while (true) {
if (find->pos != InvalidPosition) {
R_TRY(this->ReadKeyValue(std::addressof(elem), nullptr, nullptr, find->pos));
AMS_ASSERT(elem.next == InvalidPosition || elem.next < this->kv_size);
find->pos = elem.next;
*out_key = elem.key;
*out_val = elem.val;
return ResultSuccess();
}
while (true) {
ind++;
if (ind == this->bucket_count) {
find->ind = ind;
find->pos = InvalidPosition;
return fs::ResultDbmFindKeyFinished();
}
Position pos;
R_TRY(this->ReadBucket(std::addressof(pos), ind));
AMS_ASSERT(pos == InvalidPosition || pos < this->kv_size);
if (pos != InvalidPosition) {
find->ind = ind;
find->pos = pos;
break;
}
}
}
}
protected:
Result AddImpl(Position *out, const Key &key, u32 hash_key, const void *aux, size_t aux_size, const Value &value) {
Position pos, prev_pos;
Element elem;
AMS_ASSERT(out != nullptr);
AMS_ASSERT(this->bucket_count > 0);
AMS_ASSERT(this->kv_size >= 0);
const Result find_res = this->FindImpl(std::addressof(pos), std::addressof(prev_pos), std::addressof(elem), key, hash_key, aux, aux_size);
R_UNLESS(R_FAILED(find_res), fs::ResultDbmAlreadyExists());
R_UNLESS(fs::ResultDbmKeyNotFound::Includes(find_res), find_res);
R_TRY(this->AllocateEntry(std::addressof(pos), aux_size));
Position next_pos;
R_TRY(this->LinkEntry(std::addressof(next_pos), pos, hash_key));
elem = { key, value, next_pos, static_cast<u32>(aux_size) };
*out = pos;
R_TRY(this->WriteKeyValue(std::addressof(elem), pos, aux, aux_size, fs::WriteOption::None));
this->entry_count++;
return ResultSuccess();
}
Result GetImpl(Position *out_pos, Value *out_val, const Key &key, u32 hash_key, const void *aux, size_t aux_size) const {
Position pos, prev_pos;
Element elem;
AMS_ASSERT(out_pos != nullptr);
AMS_ASSERT(out_val != nullptr);
R_TRY(this->FindImpl(std::addressof(pos), std::addressof(prev_pos), std::addressof(elem), key, hash_key, aux, aux_size));
*out_pos = pos;
*out_val = elem.value;
return ResultSuccess();
}
Result GetByPosition(Key *out_key, Value *out_val, void *out_aux, size_t *out_aux_size, Position pos) const {
AMS_ASSERT(out_key != nullptr);
AMS_ASSERT(out_val != nullptr);
Element elem;
R_TRY(this->ReadKeyValue(std::addressof(elem), out_aux, out_aux_size, pos));
*out_key = elem.key;
*out_val = elem.value;
return ResultSuccess();
}
Result SetByPosition(Position pos, const Value &value, fs::WriteOption option) const {
Element elem;
R_TRY(this->ReadKeyValue(std::addressof(elem), nullptr, nullptr, pos));
elem.value = value;
return this->WriteKeyValue(std::addressof(elem), pos, nullptr, 0, option);
}
private:
BucketIndex HashToBucket(u32 hash_key) const {
return hash_key % this->bucket_count;
}
Result FindImpl(Position *out_pos, Position *out_prev, Element *out_elem, const Key &key, u32 hash_key, const void *aux, size_t aux_size) const {
AMS_ASSERT(out_pos != nullptr);
AMS_ASSERT(out_prev != nullptr);
AMS_ASSERT(out_elem != nullptr);
AMS_ASSERT(this->bucket_count > 0);
AMS_ASSERT(this->kv_size >= 0);
*out_pos = 0;
*out_prev = 0;
const BucketIndex ind = HashToBucket(hash_key);
Position cur;
R_TRY(this->ReadBucket(std::addressof(cur), ind));
AMS_ASSERT(cur == InvalidPosition || cur < this->kv_size);
R_UNLESS(cur != InvalidPosition, fs::ResultDbmKeyNotFound());
u8 buf[MaxAuxiliarySize];
while (true) {
size_t cur_aux_size;
R_TRY(this->ReadKeyValue(out_elem, buf, std::addressof(cur_aux_size), cur));
if (key.IsEqual(out_elem->key, aux, aux_size, buf, cur_aux_size)) {
*out_pos = cur;
return ResultSuccess();
}
*out_prev = cur;
cur = out_elem->next;
R_UNLESS(cur != InvalidPosition, fs::ResultDbmKeyNotFound());
}
}
Result AllocateEntry(Position *out, size_t aux_size) {
AMS_ASSERT(out != nullptr);
R_UNLESS(this->total_entry_size + sizeof(Element) + aux_size <= this->kv_size, fs::ResultDbmKeyFull());
*out = static_cast<Position>(this->total_entry_size);
this->total_entry_size = AlignRomAddress(this->total_entry_size + sizeof(Element) + static_cast<u32>(aux_size));
return ResultSuccess();
}
Result LinkEntry(Position *out, Position pos, u32 hash_key) {
AMS_ASSERT(out != nullptr);
const BucketIndex ind = HashToBucket(hash_key);
Position next;
R_TRY(this->ReadBucket(std::addressof(next), ind));
AMS_ASSERT(next == InvalidPosition || next < this->kv_size);
R_TRY(this->WriteBucket(pos, ind, fs::WriteOption::None));
*out = next;
return ResultSuccess();
}
Result ReadBucket(Position *out, BucketIndex ind) const {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(this->bucket_storage != nullptr);
AMS_ASSERT(ind < this->bucket_count);
const s64 offset = this->bucket_offset + ind * sizeof(Position);
return this->bucket_storage->Read(offset, out, sizeof(*out));
}
Result WriteBucket(Position pos, BucketIndex ind, fs::WriteOption option) const {
AMS_ASSERT(this->bucket_storage != nullptr);
AMS_ASSERT(ind < this->bucket_count);
const s64 offset = this->bucket_offset + ind * sizeof(Position);
return this->bucket_storage.Write(offset, std::addressof(pos), sizeof(pos));
}
Result ReadKeyValue(Element *out, void *out_aux, size_t *out_aux_size, Position pos) const {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(this->kv_storage != nullptr);
AMS_ASSERT(pos < this->kv_size);
const s64 offset = this->kv_offset + pos;
R_TRY(this->kv_storage->Read(offset, out, sizeof(*out)));
if (out_aux != nullptr && out_aux_size != nullptr) {
*out_aux_size = out->size;
if (out->size > 0) {
R_TRY(this->kv_storage->Read(offset + sizeof(*out), out_aux, out->size));
}
}
return ResultSuccess();
}
Result WriteKeyValue(const Element *elem, Position pos, const void *aux, size_t aux_size, fs::WriteOption option) const {
AMS_ASSERT(elem != nullptr);
AMS_ASSERT(this->kv_storage != nullptr);
AMS_ASSERT(pos < this->kv_size);
const s64 offset = this->kv_offset + pos;
R_TRY(this->kv_storage->Write(offset, elem, sizeof(*elem)));
if (aux != nullptr && aux_size > 0) {
R_TRY(this->kv_storage->Write(offset + sizeof(*elem), aux, aux_size));
}
return ResultSuccess();
}
};
}

View File

@ -1,114 +0,0 @@
/*
* Copyright (c) 2018-2020 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/fssystem/fssystem_dbm_rom_types.hpp>
namespace ams::fssystem {
namespace RomPathTool {
constexpr inline u32 MaxPathLength = 0x300;
struct RomEntryName {
size_t length;
const RomPathChar *path;
};
static_assert(util::is_pod<RomEntryName>::value);
constexpr void InitializeRomEntryName(RomEntryName *entry) {
entry->length = 0;
}
constexpr inline bool IsSeparator(RomPathChar c) {
return c == RomStringTraits::DirectorySeparator;
}
constexpr inline bool IsNullTerminator(RomPathChar c) {
return c == RomStringTraits::NullTerminator;
}
constexpr inline bool IsDot(RomPathChar c) {
return c == RomStringTraits::Dot;
}
constexpr inline bool IsCurrentDirectory(const RomEntryName &name) {
return name.length == 1 && IsDot(name.path[0]);
}
constexpr inline bool IsCurrentDirectory(const RomPathChar *p, size_t length) {
return length == 1 && IsDot(p[0]);
}
constexpr inline bool IsCurrentDirectory(const RomPathChar *p) {
return IsDot(p[0]) && IsNullTerminator(p[1]);
}
constexpr inline bool IsParentDirectory(const RomEntryName &name) {
return name.length == 2 && IsDot(name.path[0]) && IsDot(name.path[1]);
}
constexpr inline bool IsParentDirectory(const RomPathChar *p) {
return IsDot(p[0]) && IsDot(p[1]) && IsNullTerminator(p[2]);
}
constexpr inline bool IsParentDirectory(const RomPathChar *p, size_t length) {
return length == 2 && IsDot(p[0]) && IsDot(p[1]);
}
constexpr inline bool IsEqualPath(const RomPathChar *lhs, const RomPathChar *rhs, size_t length) {
return std::strncmp(lhs, rhs, length) == 0;
}
constexpr inline bool IsEqualName(const RomEntryName &lhs, const RomPathChar *rhs) {
if (strnlen(rhs, MaxPathLength) != lhs.length) {
return false;
}
return IsEqualPath(lhs.path, rhs, lhs.length);
}
constexpr inline bool IsEqualName(const RomEntryName &lhs, const RomEntryName &rhs) {
if (lhs.length != rhs.length) {
return false;
}
return IsEqualPath(lhs.path, rhs.path, lhs.length);
}
Result GetParentDirectoryName(RomEntryName *out, const RomEntryName &cur, const RomPathChar *p);
class PathParser {
private:
const RomPathChar *prev_path_start;
const RomPathChar *prev_path_end;
const RomPathChar *next_path;
bool finished;
public:
constexpr PathParser() : prev_path_start(), prev_path_end(), next_path(), finished() { /* ... */ }
Result Initialize(const RomPathChar *path);
void Finalize();
bool IsFinished() const;
bool IsDirectoryPath() const;
Result GetAsDirectoryName(RomEntryName *out) const;
Result GetAsFileName(RomEntryName *out) const;
Result GetNextDirectoryName(RomEntryName *out);
};
}
}

View File

@ -1,59 +0,0 @@
/*
* Copyright (c) 2018-2020 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/fs/fs_common.hpp>
namespace ams::fssystem {
using RomPathChar = char;
using RomFileId = s32;
using RomDirectoryId = s32;
struct RomFileSystemInformation {
s64 size;
s64 directory_bucket_offset;
s64 directory_bucket_size;
s64 directory_entry_offset;
s64 directory_entry_size;
s64 file_bucket_offset;
s64 file_bucket_size;
s64 file_entry_offset;
s64 file_entry_size;
s64 body_offset;
};
static_assert(util::is_pod<RomFileSystemInformation>::value);
static_assert(sizeof(RomFileSystemInformation) == 0x50);
struct RomDirectoryInfo {
/* ... */
};
static_assert(util::is_pod<RomDirectoryInfo>::value);
struct RomFileInfo {
fs::Int64 offset;
fs::Int64 size;
};
static_assert(util::is_pod<RomFileInfo>::value);
namespace RomStringTraits {
constexpr inline char DirectorySeparator = '/';
constexpr inline char NullTerminator = '\x00';
constexpr inline char Dot = '.';
}
}

View File

@ -46,19 +46,19 @@ namespace ams::fssystem {
Result CopySaveFromFileSystem(fs::fsa::IFileSystem *save_fs); Result CopySaveFromFileSystem(fs::fsa::IFileSystem *save_fs);
public: public:
/* Overridden from IPathResolutionFileSystem */ /* Overridden from IPathResolutionFileSystem */
virtual Result OpenFileImpl(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) override; virtual Result DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) override;
virtual Result CommitImpl() override; virtual Result DoCommit() override;
/* Overridden from IPathResolutionFileSystem but not commands. */ /* Overridden from IPathResolutionFileSystem but not commands. */
virtual Result CommitProvisionallyImpl(s64 counter) override; virtual Result DoCommitProvisionally(s64 counter) override;
virtual Result RollbackImpl() override; virtual Result DoRollback() override;
/* Explicitly overridden to be not implemented. */ /* Explicitly overridden to be not implemented. */
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) override; virtual Result DoGetFreeSpaceSize(s64 *out, const char *path) override;
virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) override; virtual Result DoGetTotalSpaceSize(s64 *out, const char *path) override;
virtual Result GetFileTimeStampRawImpl(fs::FileTimeStampRaw *out, const char *path) override; virtual Result DoGetFileTimeStampRaw(fs::FileTimeStampRaw *out, const char *path) override;
virtual Result QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, fs::fsa::QueryId query, const char *path) override; virtual Result DoQueryEntry(char *dst, size_t dst_size, const char *src, size_t src_size, fs::fsa::QueryId query, const char *path) override;
virtual Result FlushImpl() override; virtual Result DoFlush() override;
}; };
} }

View File

@ -49,21 +49,21 @@ namespace ams::fssystem {
Result GetFileBaseOffset(s64 *out_offset, const char *path); Result GetFileBaseOffset(s64 *out_offset, const char *path);
virtual Result CreateFileImpl(const char *path, s64 size, int option) override; virtual Result DoCreateFile(const char *path, s64 size, int option) override;
virtual Result DeleteFileImpl(const char *path) override; virtual Result DoDeleteFile(const char *path) override;
virtual Result CreateDirectoryImpl(const char *path) override; virtual Result DoCreateDirectory(const char *path) override;
virtual Result DeleteDirectoryImpl(const char *path) override; virtual Result DoDeleteDirectory(const char *path) override;
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override; virtual Result DoDeleteDirectoryRecursively(const char *path) override;
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override; virtual Result DoRenameFile(const char *old_path, const char *new_path) override;
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override; virtual Result DoRenameDirectory(const char *old_path, const char *new_path) override;
virtual Result GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) override; virtual Result DoGetEntryType(fs::DirectoryEntryType *out, const char *path) override;
virtual Result OpenFileImpl(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) override; virtual Result DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) override;
virtual Result OpenDirectoryImpl(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) override; virtual Result DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) override;
virtual Result CommitImpl() override; virtual Result DoCommit() override;
virtual Result CleanDirectoryRecursivelyImpl(const char *path) override; virtual Result DoCleanDirectoryRecursively(const char *path) override;
/* These aren't accessible as commands. */ /* These aren't accessible as commands. */
virtual Result CommitProvisionallyImpl(s64 counter) override; virtual Result DoCommitProvisionally(s64 counter) override;
}; };
using PartitionFileSystem = PartitionFileSystemCore<PartitionFileSystemMeta>; using PartitionFileSystem = PartitionFileSystemCore<PartitionFileSystemMeta>;

View File

@ -17,7 +17,7 @@
#include <vapours.hpp> #include <vapours.hpp>
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp> #include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
#include <stratosphere/fs/impl/fs_newable.hpp> #include <stratosphere/fs/impl/fs_newable.hpp>
#include <stratosphere/fssystem/fssystem_dbm_hierarchical_rom_file_table.hpp> #include <stratosphere/fs/common/fs_dbm_hierarchical_rom_file_table.hpp>
#include <stratosphere/fs/fs_istorage.hpp> #include <stratosphere/fs/fs_istorage.hpp>
namespace ams::fssystem { namespace ams::fssystem {
@ -25,7 +25,7 @@ namespace ams::fssystem {
class RomFsFileSystem : public fs::fsa::IFileSystem, public fs::impl::Newable { class RomFsFileSystem : public fs::fsa::IFileSystem, public fs::impl::Newable {
NON_COPYABLE(RomFsFileSystem); NON_COPYABLE(RomFsFileSystem);
public: public:
using RomFileTable = HierarchicalRomFileTable<fs::IStorage, fs::IStorage, fs::IStorage, fs::IStorage>; using RomFileTable = fs::HierarchicalRomFileTable;
private: private:
RomFileTable rom_file_table; RomFileTable rom_file_table;
fs::IStorage *base_storage; fs::IStorage *base_storage;
@ -50,22 +50,22 @@ namespace ams::fssystem {
RomFileTable *GetRomFileTable(); RomFileTable *GetRomFileTable();
Result GetFileBaseOffset(s64 *out, const char *path); Result GetFileBaseOffset(s64 *out, const char *path);
public: public:
virtual Result CreateFileImpl(const char *path, s64 size, int flags) override; virtual Result DoCreateFile(const char *path, s64 size, int flags) override;
virtual Result DeleteFileImpl(const char *path) override; virtual Result DoDeleteFile(const char *path) override;
virtual Result CreateDirectoryImpl(const char *path) override; virtual Result DoCreateDirectory(const char *path) override;
virtual Result DeleteDirectoryImpl(const char *path) override; virtual Result DoDeleteDirectory(const char *path) override;
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override; virtual Result DoDeleteDirectoryRecursively(const char *path) override;
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override; virtual Result DoRenameFile(const char *old_path, const char *new_path) override;
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override; virtual Result DoRenameDirectory(const char *old_path, const char *new_path) override;
virtual Result GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) override; virtual Result DoGetEntryType(fs::DirectoryEntryType *out, const char *path) override;
virtual Result OpenFileImpl(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) override; virtual Result DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) override;
virtual Result OpenDirectoryImpl(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) override; virtual Result DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) override;
virtual Result CommitImpl() override; virtual Result DoCommit() override;
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) override; virtual Result DoGetFreeSpaceSize(s64 *out, const char *path) override;
virtual Result CleanDirectoryRecursivelyImpl(const char *path) override; virtual Result DoCleanDirectoryRecursively(const char *path) override;
/* These aren't accessible as commands. */ /* These aren't accessible as commands. */
virtual Result CommitProvisionallyImpl(s64 counter) override; virtual Result DoCommitProvisionally(s64 counter) override;
}; };
} }

View File

@ -14,11 +14,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "../fs/fs_common.hpp" #include <stratosphere/fs/fs_common.hpp>
#include "../fs/fs_file.hpp" #include <stratosphere/fs/fs_file.hpp>
#include "../fs/fs_directory.hpp" #include <stratosphere/fs/fs_directory.hpp>
#include "../fs/fs_filesystem.hpp" #include <stratosphere/fs/fs_filesystem.hpp>
#include "fssystem_path_tool.hpp" #include <stratosphere/fs/fs_path_utils.hpp>
namespace ams::fssystem { namespace ams::fssystem {
@ -70,7 +70,7 @@ namespace ams::fssystem {
} }
/* Restore parent path. */ /* Restore parent path. */
work_path[parent_len] = StringTraits::NullTerminator; work_path[parent_len] = fs::StringTraits::NullTerminator;
} }
return ResultSuccess(); return ResultSuccess();
@ -91,13 +91,13 @@ namespace ams::fssystem {
/* Copy root path in, add a / if necessary. */ /* Copy root path in, add a / if necessary. */
std::memcpy(work_path, root_path, root_path_len); std::memcpy(work_path, root_path, root_path_len);
if (!PathTool::IsSeparator(work_path[root_path_len - 1])) { if (!fs::PathNormalizer::IsSeparator(work_path[root_path_len - 1])) {
work_path[root_path_len++] = StringTraits::DirectorySeparator; work_path[root_path_len++] = fs::StringTraits::DirectorySeparator;
} }
/* Make sure the result path is still valid. */ /* Make sure the result path is still valid. */
R_UNLESS(root_path_len <= fs::EntryNameLengthMax, fs::ResultTooLongPath()); R_UNLESS(root_path_len <= fs::EntryNameLengthMax, fs::ResultTooLongPath());
work_path[root_path_len] = StringTraits::NullTerminator; work_path[root_path_len] = fs::StringTraits::NullTerminator;
return impl::IterateDirectoryRecursivelyImpl(fs, work_path, work_path_size, dir_ent_buf, on_enter_dir, on_exit_dir, on_file); return impl::IterateDirectoryRecursivelyImpl(fs, work_path, work_path_size, dir_ent_buf, on_enter_dir, on_exit_dir, on_file);
} }
@ -111,7 +111,7 @@ namespace ams::fssystem {
template<typename OnEnterDir, typename OnExitDir, typename OnFile> template<typename OnEnterDir, typename OnExitDir, typename OnFile>
Result IterateDirectoryRecursively(fs::fsa::IFileSystem *fs, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) { Result IterateDirectoryRecursively(fs::fsa::IFileSystem *fs, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
return IterateDirectoryRecursively(fs, PathTool::RootPath, on_enter_dir, on_exit_dir, on_file); return IterateDirectoryRecursively(fs, fs::PathNormalizer::RootPath, on_enter_dir, on_exit_dir, on_file);
} }
/* TODO: Cleanup API */ /* TODO: Cleanup API */

View File

@ -46,7 +46,7 @@ namespace ams::fssystem::impl {
return this->unc_preserved; return this->unc_preserved;
} }
public: public:
virtual Result CreateFileImpl(const char *path, s64 size, int option) override { virtual Result DoCreateFile(const char *path, s64 size, int option) override {
char full_path[fs::EntryNameLengthMax + 1]; char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path)); R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path));
@ -54,7 +54,7 @@ namespace ams::fssystem::impl {
return this->base_fs->CreateFile(full_path, size, option); return this->base_fs->CreateFile(full_path, size, option);
} }
virtual Result DeleteFileImpl(const char *path) override { virtual Result DoDeleteFile(const char *path) override {
char full_path[fs::EntryNameLengthMax + 1]; char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path)); R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path));
@ -62,7 +62,7 @@ namespace ams::fssystem::impl {
return this->base_fs->DeleteFile(full_path); return this->base_fs->DeleteFile(full_path);
} }
virtual Result CreateDirectoryImpl(const char *path) override { virtual Result DoCreateDirectory(const char *path) override {
char full_path[fs::EntryNameLengthMax + 1]; char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path)); R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path));
@ -70,7 +70,7 @@ namespace ams::fssystem::impl {
return this->base_fs->CreateDirectory(full_path); return this->base_fs->CreateDirectory(full_path);
} }
virtual Result DeleteDirectoryImpl(const char *path) override { virtual Result DoDeleteDirectory(const char *path) override {
char full_path[fs::EntryNameLengthMax + 1]; char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path)); R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path));
@ -78,7 +78,7 @@ namespace ams::fssystem::impl {
return this->base_fs->DeleteDirectory(full_path); return this->base_fs->DeleteDirectory(full_path);
} }
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override { virtual Result DoDeleteDirectoryRecursively(const char *path) override {
char full_path[fs::EntryNameLengthMax + 1]; char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path)); R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path));
@ -86,7 +86,7 @@ namespace ams::fssystem::impl {
return this->base_fs->DeleteDirectoryRecursively(full_path); return this->base_fs->DeleteDirectoryRecursively(full_path);
} }
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override { virtual Result DoRenameFile(const char *old_path, const char *new_path) override {
char old_full_path[fs::EntryNameLengthMax + 1]; char old_full_path[fs::EntryNameLengthMax + 1];
char new_full_path[fs::EntryNameLengthMax + 1]; char new_full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast<Impl*>(this)->ResolveFullPath(old_full_path, sizeof(old_full_path), old_path)); R_TRY(static_cast<Impl*>(this)->ResolveFullPath(old_full_path, sizeof(old_full_path), old_path));
@ -96,7 +96,7 @@ namespace ams::fssystem::impl {
return this->base_fs->RenameFile(old_full_path, new_full_path); return this->base_fs->RenameFile(old_full_path, new_full_path);
} }
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override { virtual Result DoRenameDirectory(const char *old_path, const char *new_path) override {
char old_full_path[fs::EntryNameLengthMax + 1]; char old_full_path[fs::EntryNameLengthMax + 1];
char new_full_path[fs::EntryNameLengthMax + 1]; char new_full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast<Impl*>(this)->ResolveFullPath(old_full_path, sizeof(old_full_path), old_path)); R_TRY(static_cast<Impl*>(this)->ResolveFullPath(old_full_path, sizeof(old_full_path), old_path));
@ -106,7 +106,7 @@ namespace ams::fssystem::impl {
return this->base_fs->RenameDirectory(old_full_path, new_full_path); return this->base_fs->RenameDirectory(old_full_path, new_full_path);
} }
virtual Result GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) override { virtual Result DoGetEntryType(fs::DirectoryEntryType *out, const char *path) override {
char full_path[fs::EntryNameLengthMax + 1]; char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path)); R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path));
@ -114,7 +114,7 @@ namespace ams::fssystem::impl {
return this->base_fs->GetEntryType(out, full_path); return this->base_fs->GetEntryType(out, full_path);
} }
virtual Result OpenFileImpl(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) override { virtual Result DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) override {
char full_path[fs::EntryNameLengthMax + 1]; char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path)); R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path));
@ -122,7 +122,7 @@ namespace ams::fssystem::impl {
return this->base_fs->OpenFile(out_file, full_path, mode); return this->base_fs->OpenFile(out_file, full_path, mode);
} }
virtual Result OpenDirectoryImpl(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) override { virtual Result DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) override {
char full_path[fs::EntryNameLengthMax + 1]; char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path)); R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path));
@ -130,12 +130,12 @@ namespace ams::fssystem::impl {
return this->base_fs->OpenDirectory(out_dir, full_path, mode); return this->base_fs->OpenDirectory(out_dir, full_path, mode);
} }
virtual Result CommitImpl() override { virtual Result DoCommit() override {
std::optional optional_lock = static_cast<Impl*>(this)->GetAccessorLock(); std::optional optional_lock = static_cast<Impl*>(this)->GetAccessorLock();
return this->base_fs->Commit(); return this->base_fs->Commit();
} }
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) override { virtual Result DoGetFreeSpaceSize(s64 *out, const char *path) override {
char full_path[fs::EntryNameLengthMax + 1]; char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path)); R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path));
@ -143,7 +143,7 @@ namespace ams::fssystem::impl {
return this->base_fs->GetFreeSpaceSize(out, full_path); return this->base_fs->GetFreeSpaceSize(out, full_path);
} }
virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) override { virtual Result DoGetTotalSpaceSize(s64 *out, const char *path) override {
char full_path[fs::EntryNameLengthMax + 1]; char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path)); R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path));
@ -151,7 +151,7 @@ namespace ams::fssystem::impl {
return this->base_fs->GetTotalSpaceSize(out, full_path); return this->base_fs->GetTotalSpaceSize(out, full_path);
} }
virtual Result CleanDirectoryRecursivelyImpl(const char *path) override { virtual Result DoCleanDirectoryRecursively(const char *path) override {
char full_path[fs::EntryNameLengthMax + 1]; char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path)); R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path));
@ -159,7 +159,7 @@ namespace ams::fssystem::impl {
return this->base_fs->CleanDirectoryRecursively(full_path); return this->base_fs->CleanDirectoryRecursively(full_path);
} }
virtual Result GetFileTimeStampRawImpl(fs::FileTimeStampRaw *out, const char *path) override { virtual Result DoGetFileTimeStampRaw(fs::FileTimeStampRaw *out, const char *path) override {
char full_path[fs::EntryNameLengthMax + 1]; char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path)); R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path));
@ -167,7 +167,7 @@ namespace ams::fssystem::impl {
return this->base_fs->GetFileTimeStampRaw(out, full_path); return this->base_fs->GetFileTimeStampRaw(out, full_path);
} }
virtual Result QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, fs::fsa::QueryId query, const char *path) override { virtual Result DoQueryEntry(char *dst, size_t dst_size, const char *src, size_t src_size, fs::fsa::QueryId query, const char *path) override {
char full_path[fs::EntryNameLengthMax + 1]; char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path)); R_TRY(static_cast<Impl*>(this)->ResolveFullPath(full_path, sizeof(full_path), path));
@ -176,17 +176,17 @@ namespace ams::fssystem::impl {
} }
/* These aren't accessible as commands. */ /* These aren't accessible as commands. */
virtual Result CommitProvisionallyImpl(s64 counter) override { virtual Result DoCommitProvisionally(s64 counter) override {
std::optional optional_lock = static_cast<Impl*>(this)->GetAccessorLock(); std::optional optional_lock = static_cast<Impl*>(this)->GetAccessorLock();
return this->base_fs->CommitProvisionally(counter); return this->base_fs->CommitProvisionally(counter);
} }
virtual Result RollbackImpl() override { virtual Result DoRollback() override {
std::optional optional_lock = static_cast<Impl*>(this)->GetAccessorLock(); std::optional optional_lock = static_cast<Impl*>(this)->GetAccessorLock();
return this->base_fs->Rollback(); return this->base_fs->Rollback();
} }
virtual Result FlushImpl() override { virtual Result DoFlush() override {
std::optional optional_lock = static_cast<Impl*>(this)->GetAccessorLock(); std::optional optional_lock = static_cast<Impl*>(this)->GetAccessorLock();
return this->base_fs->Flush(); return this->base_fs->Flush();
} }

View File

@ -59,6 +59,7 @@ namespace ams::hos {
Version_10_1_0 = ::ams::TargetFirmware_10_1_0, Version_10_1_0 = ::ams::TargetFirmware_10_1_0,
Version_10_2_0 = ::ams::TargetFirmware_10_2_0, Version_10_2_0 = ::ams::TargetFirmware_10_2_0,
Version_11_0_0 = ::ams::TargetFirmware_11_0_0, Version_11_0_0 = ::ams::TargetFirmware_11_0_0,
Version_11_0_1 = ::ams::TargetFirmware_11_0_1,
Version_Current = ::ams::TargetFirmware_Current, Version_Current = ::ams::TargetFirmware_Current,

View File

@ -14,7 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <stratosphere/fs/fs_file_storage.hpp> #include <stratosphere/fs/common/fs_file_storage.hpp>
#include <stratosphere/ncm/ncm_package_install_task.hpp> #include <stratosphere/ncm/ncm_package_install_task.hpp>
namespace ams::ncm { namespace ams::ncm {

View File

@ -17,23 +17,22 @@
namespace ams::fs { namespace ams::fs {
s64 HierarchicalRomFileTable::QueryDirectoryEntryStorageSize(u32 count) {
const size_t real_count = count + ReservedDirectoryCount;
return DirectoryEntryMapTable::QueryKeyValueStorageSize(real_count) + real_count * (RomPathTool::MaxPathLength + 1) * sizeof(RomPathChar);
}
s64 HierarchicalRomFileTable::QueryDirectoryEntryBucketStorageSize(s64 count) { s64 HierarchicalRomFileTable::QueryDirectoryEntryBucketStorageSize(s64 count) {
return DirectoryEntryMapTable::QueryBucketStorageSize(count); return DirectoryEntryMapTable::QueryBucketStorageSize(count);
} }
s64 HierarchicalRomFileTable::QueryFileEntryStorageSize(u32 count) { size_t HierarchicalRomFileTable::QueryDirectoryEntrySize(size_t aux_size) {
return FileEntryMapTable::QueryKeyValueStorageSize(count) + count * (RomPathTool::MaxPathLength + 1) * sizeof(RomPathChar); return DirectoryEntryMapTable::QueryEntrySize(aux_size);
} }
s64 HierarchicalRomFileTable::QueryFileEntryBucketStorageSize(s64 count) { s64 HierarchicalRomFileTable::QueryFileEntryBucketStorageSize(s64 count) {
return FileEntryMapTable::QueryBucketStorageSize(count); return FileEntryMapTable::QueryBucketStorageSize(count);
} }
size_t HierarchicalRomFileTable::QueryFileEntrySize(size_t aux_size) {
return FileEntryMapTable::QueryEntrySize(aux_size);
}
Result HierarchicalRomFileTable::Format(SubStorage dir_bucket, SubStorage file_bucket) { Result HierarchicalRomFileTable::Format(SubStorage dir_bucket, SubStorage file_bucket) {
s64 dir_bucket_size; s64 dir_bucket_size;
R_TRY(dir_bucket.GetSize(std::addressof(dir_bucket_size))); R_TRY(dir_bucket.GetSize(std::addressof(dir_bucket_size)));
@ -69,7 +68,7 @@ namespace ams::fs {
Position root_pos = RootPosition; Position root_pos = RootPosition;
EntryKey root_key = {}; EntryKey root_key = {};
root_key.key.parent = root_pos; root_key.key.parent = root_pos;
RomPathTool::InitializeRomEntryName(std::addressof(root_key.name)); RomPathTool::InitEntryName(std::addressof(root_key.name));
RomDirectoryEntry root_entry = { RomDirectoryEntry root_entry = {
.next = InvalidPosition, .next = InvalidPosition,
.dir = InvalidPosition, .dir = InvalidPosition,
@ -99,7 +98,7 @@ namespace ams::fs {
R_CONVERT(fs::ResultDbmKeyFull, fs::ResultDbmDirectoryEntryFull()) R_CONVERT(fs::ResultDbmKeyFull, fs::ResultDbmDirectoryEntryFull())
} R_END_TRY_CATCH; } R_END_TRY_CATCH;
*out = ConvertToDirectoryId(new_pos); *out = PositionToDirectoryId(new_pos);
if (parent_entry.dir == InvalidPosition) { if (parent_entry.dir == InvalidPosition) {
parent_entry.dir = new_pos; parent_entry.dir = new_pos;
@ -146,7 +145,7 @@ namespace ams::fs {
R_CONVERT(fs::ResultDbmKeyFull, fs::ResultDbmFileEntryFull()) R_CONVERT(fs::ResultDbmKeyFull, fs::ResultDbmFileEntryFull())
} R_END_TRY_CATCH; } R_END_TRY_CATCH;
*out = ConvertToFileId(new_pos); *out = PositionToFileId(new_pos);
if (parent_entry.file == InvalidPosition) { if (parent_entry.file == InvalidPosition) {
parent_entry.file = new_pos; parent_entry.file = new_pos;
@ -185,7 +184,7 @@ namespace ams::fs {
RomDirectoryEntry entry = {}; RomDirectoryEntry entry = {};
R_TRY(this->GetDirectoryEntry(std::addressof(pos), std::addressof(entry), key)); R_TRY(this->GetDirectoryEntry(std::addressof(pos), std::addressof(entry), key));
*out = ConvertToDirectoryId(pos); *out = PositionToDirectoryId(pos);
return ResultSuccess(); return ResultSuccess();
} }
@ -201,7 +200,7 @@ namespace ams::fs {
RomFileEntry entry = {}; RomFileEntry entry = {};
R_TRY(this->GetFileEntry(std::addressof(pos), std::addressof(entry), key)); R_TRY(this->GetFileEntry(std::addressof(pos), std::addressof(entry), key));
*out = ConvertToFileId(pos); *out = PositionToFileId(pos);
return ResultSuccess(); return ResultSuccess();
} }
@ -353,7 +352,7 @@ namespace ams::fs {
R_TRY(this->GetDirectoryEntry(std::addressof(dir_pos), std::addressof(dir_entry), dir_key)); R_TRY(this->GetDirectoryEntry(std::addressof(dir_pos), std::addressof(dir_entry), dir_key));
Position parent_pos = dir_pos; Position parent_pos = dir_pos;
while (!parser->IsFinished()) { while (!parser->IsParseFinished()) {
EntryKey old_key = dir_key; EntryKey old_key = dir_key;
R_TRY(parser->GetNextDirectoryName(std::addressof(dir_key.name))); R_TRY(parser->GetNextDirectoryName(std::addressof(dir_key.name)));
@ -492,7 +491,7 @@ namespace ams::fs {
Result HierarchicalRomFileTable::GetDirectoryEntry(RomDirectoryEntry *out_entry, RomDirectoryId id) { Result HierarchicalRomFileTable::GetDirectoryEntry(RomDirectoryEntry *out_entry, RomDirectoryId id) {
AMS_ASSERT(out_entry != nullptr); AMS_ASSERT(out_entry != nullptr);
Position pos = ConvertToPosition(id); Position pos = DirectoryIdToPosition(id);
RomEntryKey key = {}; RomEntryKey key = {};
const Result dir_res = this->dir_table.GetByPosition(std::addressof(key), out_entry, pos); const Result dir_res = this->dir_table.GetByPosition(std::addressof(key), out_entry, pos);
@ -524,7 +523,7 @@ namespace ams::fs {
Result HierarchicalRomFileTable::GetFileEntry(RomFileEntry *out_entry, RomFileId id) { Result HierarchicalRomFileTable::GetFileEntry(RomFileEntry *out_entry, RomFileId id) {
AMS_ASSERT(out_entry != nullptr); AMS_ASSERT(out_entry != nullptr);
Position pos = ConvertToPosition(id); Position pos = FileIdToPosition(id);
RomEntryKey key = {}; RomEntryKey key = {};
const Result file_res = this->file_table.GetByPosition(std::addressof(key), out_entry, pos); const Result file_res = this->file_table.GetByPosition(std::addressof(key), out_entry, pos);

View File

@ -28,9 +28,8 @@ namespace ams::fs::RomPathTool {
this->prev_path_start = path; this->prev_path_start = path;
this->prev_path_end = path; this->prev_path_end = path;
this->next_path = path + 1; for (this->next_path = path + 1; IsSeparator(this->next_path[0]); ++this->next_path) {
while (IsSeparator(this->next_path[0])) { /* ... */
this->next_path++;
} }
return ResultSuccess(); return ResultSuccess();
@ -43,12 +42,13 @@ namespace ams::fs::RomPathTool {
this->finished = false; this->finished = false;
} }
bool PathParser::IsFinished() const { bool PathParser::IsParseFinished() const {
return this->finished; return this->finished;
} }
bool PathParser::IsDirectoryPath() const { bool PathParser::IsDirectoryPath() const {
AMS_ASSERT(this->next_path != nullptr); AMS_ASSERT(this->next_path != nullptr);
if (IsNullTerminator(this->next_path[0]) && IsSeparator(this->next_path[-1])) { if (IsNullTerminator(this->next_path[0]) && IsSeparator(this->next_path[-1])) {
return true; return true;
} }
@ -57,11 +57,47 @@ namespace ams::fs::RomPathTool {
return true; return true;
} }
if (IsParentDirectory(this->next_path)) { return IsParentDirectory(this->next_path);
return true;
} }
return false; Result PathParser::GetNextDirectoryName(RomEntryName *out) {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(this->prev_path_start != nullptr);
AMS_ASSERT(this->prev_path_end != nullptr);
AMS_ASSERT(this->next_path != nullptr);
/* Set the current path to output. */
out->length = this->prev_path_end - this->prev_path_start;
out->path = this->prev_path_start;
/* Parse the next path. */
this->prev_path_start = this->next_path;
const RomPathChar *cur = this->next_path;
for (size_t name_len = 0; true; name_len++) {
if (IsSeparator(cur[name_len])) {
R_UNLESS(name_len < MaxPathLength, fs::ResultDbmDirectoryNameTooLong());
this->prev_path_end = cur + name_len;
this->next_path = this->prev_path_end + 1;
while (IsSeparator(this->next_path[0])) {
++this->next_path;
}
if (IsNullTerminator(this->next_path[0])) {
this->finished = true;
}
break;
}
if (IsNullTerminator(cur[name_len])) {
this->finished = true;
this->next_path = cur + name_len;
this->prev_path_end = cur + name_len;
break;
}
}
return ResultSuccess();
} }
Result PathParser::GetAsDirectoryName(RomEntryName *out) const { Result PathParser::GetAsDirectoryName(RomEntryName *out) const {
@ -92,45 +128,6 @@ namespace ams::fs::RomPathTool {
return ResultSuccess(); return ResultSuccess();
} }
Result PathParser::GetNextDirectoryName(RomEntryName *out) {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(this->prev_path_start != nullptr);
AMS_ASSERT(this->prev_path_end != nullptr);
AMS_ASSERT(this->next_path != nullptr);
/* Set the current path to output. */
out->length = this->prev_path_end - this->prev_path_start;
out->path = this->prev_path_start;
/* Parse the next path. */
this->prev_path_start = this->next_path;
const RomPathChar *cur = this->next_path;
for (size_t name_len = 0; true; name_len++) {
if (IsSeparator(cur[name_len])) {
R_UNLESS(name_len < MaxPathLength, fs::ResultDbmDirectoryNameTooLong());
this->prev_path_end = cur + name_len;
this->next_path = this->prev_path_end + 1;
while (IsSeparator(this->next_path[0])) {
this->next_path++;
}
if (IsNullTerminator(this->next_path[0])) {
this->finished = true;
}
break;
}
if (IsNullTerminator(cur[name_len])) {
this->finished = true;
this->prev_path_end = this->next_path = cur + name_len;
break;
}
}
return ResultSuccess();
}
Result GetParentDirectoryName(RomEntryName *out, const RomEntryName &cur, const RomPathChar *p) { Result GetParentDirectoryName(RomEntryName *out, const RomEntryName &cur, const RomPathChar *p) {
AMS_ASSERT(out != nullptr); AMS_ASSERT(out != nullptr);
AMS_ASSERT(p != nullptr); AMS_ASSERT(p != nullptr);
@ -140,7 +137,7 @@ namespace ams::fs::RomPathTool {
s32 depth = 1; s32 depth = 1;
if (IsParentDirectory(cur)) { if (IsParentDirectory(cur)) {
depth++; ++depth;
} }
if (cur.path > p) { if (cur.path > p) {
@ -149,7 +146,7 @@ namespace ams::fs::RomPathTool {
while (head >= p) { while (head >= p) {
if (IsSeparator(*head)) { if (IsSeparator(*head)) {
if (IsCurrentDirectory(head + 1, len)) { if (IsCurrentDirectory(head + 1, len)) {
depth++; ++depth;
} }
if (IsParentDirectory(head + 1, len)) { if (IsParentDirectory(head + 1, len)) {
@ -162,16 +159,16 @@ namespace ams::fs::RomPathTool {
} }
while (IsSeparator(*head)) { while (IsSeparator(*head)) {
head--; --head;
} }
end = head; end = head;
len = 0; len = 0;
depth--; --depth;
} }
len++; ++len;
head--; --head;
} }
R_UNLESS(depth == 0, fs::ResultDirectoryUnobtainable()); R_UNLESS(depth == 0, fs::ResultDirectoryUnobtainable());

View File

@ -19,7 +19,7 @@ namespace ams::fs {
Result FileStorage::UpdateSize() { Result FileStorage::UpdateSize() {
R_SUCCEED_IF(this->size != InvalidSize); R_SUCCEED_IF(this->size != InvalidSize);
return this->base_file->GetSize(&this->size); return this->base_file->GetSize(std::addressof(this->size));
} }
Result FileStorage::Read(s64 offset, void *buffer, size_t size) { Result FileStorage::Read(s64 offset, void *buffer, size_t size) {
@ -33,10 +33,10 @@ namespace ams::fs {
R_TRY(this->UpdateSize()); R_TRY(this->UpdateSize());
/* Ensure our access is valid. */ /* Ensure our access is valid. */
R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange()); R_UNLESS(IStorage::CheckAccessRange(offset, size, this->size), fs::ResultOutOfRange());
size_t read_size; size_t read_size;
return this->base_file->Read(&read_size, offset, buffer, size); return this->base_file->Read(std::addressof(read_size), offset, buffer, size);
} }
Result FileStorage::Write(s64 offset, const void *buffer, size_t size) { Result FileStorage::Write(s64 offset, const void *buffer, size_t size) {
@ -50,7 +50,7 @@ namespace ams::fs {
R_TRY(this->UpdateSize()); R_TRY(this->UpdateSize());
/* Ensure our access is valid. */ /* Ensure our access is valid. */
R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange()); R_UNLESS(IStorage::CheckAccessRange(offset, size, this->size), fs::ResultOutOfRange());
return this->base_file->Write(offset, buffer, size, fs::WriteOption()); return this->base_file->Write(offset, buffer, size, fs::WriteOption());
} }
@ -83,7 +83,7 @@ namespace ams::fs {
return ResultSuccess(); return ResultSuccess();
} }
R_TRY(this->UpdateSize()); R_TRY(this->UpdateSize());
R_UNLESS(IStorage::IsOffsetAndSizeValid(offset, size), fs::ResultOutOfRange()); R_UNLESS(IStorage::CheckOffsetAndSize(offset, size), fs::ResultOutOfRange());
return this->base_file->OperateRange(dst, dst_size, op_id, offset, size, src, src_size); return this->base_file->OperateRange(dst, dst_size, op_id, offset, size, src, src_size);
default: default:
return fs::ResultUnsupportedOperationInFileStorageA(); return fs::ResultUnsupportedOperationInFileStorageA();
@ -121,7 +121,7 @@ namespace ams::fs {
R_TRY(this->UpdateSize()); R_TRY(this->UpdateSize());
/* Ensure our access is valid. */ /* Ensure our access is valid. */
R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange()); R_UNLESS(IStorage::CheckAccessRange(offset, size, this->size), fs::ResultOutOfRange());
return ReadFile(this->handle, offset, buffer, size, fs::ReadOption()); return ReadFile(this->handle, offset, buffer, size, fs::ReadOption());
} }
@ -140,7 +140,7 @@ namespace ams::fs {
R_TRY(this->UpdateSize()); R_TRY(this->UpdateSize());
/* Ensure our access is valid. */ /* Ensure our access is valid. */
R_UNLESS(IStorage::IsRangeValid(offset, size, this->size), fs::ResultOutOfRange()); R_UNLESS(IStorage::CheckAccessRange(offset, size, this->size), fs::ResultOutOfRange());
return WriteFile(this->handle, offset, buffer, size, fs::WriteOption()); return WriteFile(this->handle, offset, buffer, size, fs::WriteOption());
} }

View File

@ -72,7 +72,7 @@ namespace ams::fs {
fssrv::sf::Path sf_path; fssrv::sf::Path sf_path;
if (len > 0) { if (len > 0) {
const bool ending_sep = PathTool::IsSeparator(root_path[len - 1]); const bool ending_sep = PathNormalizer::IsSeparator(root_path[len - 1]);
FspPathPrintf(std::addressof(sf_path), "%s%s", root_path, ending_sep ? "" : "/"); FspPathPrintf(std::addressof(sf_path), "%s%s", root_path, ending_sep ? "" : "/");
} else { } else {
sf_path.str[0] = '\x00'; sf_path.str[0] = '\x00';

View File

@ -96,59 +96,59 @@ namespace ams::fs {
class OpenFileOnlyFileSystem : public fsa::IFileSystem, public impl::Newable { class OpenFileOnlyFileSystem : public fsa::IFileSystem, public impl::Newable {
private: private:
virtual Result CommitImpl() override final { virtual Result DoCommit() override final {
return ResultSuccess(); return ResultSuccess();
} }
virtual Result OpenDirectoryImpl(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 char *path, OpenDirectoryMode mode) override final {
return fs::ResultUnsupportedOperation(); return fs::ResultUnsupportedOperation();
} }
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const char *path) override final { virtual Result DoGetEntryType(DirectoryEntryType *out, const char *path) override final {
return fs::ResultUnsupportedOperation(); return fs::ResultUnsupportedOperation();
} }
virtual Result CreateFileImpl(const char *path, s64 size, int flags) override final { virtual Result DoCreateFile(const char *path, s64 size, int flags) override final {
return fs::ResultUnsupportedOperation(); return fs::ResultUnsupportedOperation();
} }
virtual Result DeleteFileImpl(const char *path) override final { virtual Result DoDeleteFile(const char *path) override final {
return fs::ResultUnsupportedOperation(); return fs::ResultUnsupportedOperation();
} }
virtual Result CreateDirectoryImpl(const char *path) override final { virtual Result DoCreateDirectory(const char *path) override final {
return fs::ResultUnsupportedOperation(); return fs::ResultUnsupportedOperation();
} }
virtual Result DeleteDirectoryImpl(const char *path) override final { virtual Result DoDeleteDirectory(const char *path) override final {
return fs::ResultUnsupportedOperation(); return fs::ResultUnsupportedOperation();
} }
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override final { virtual Result DoDeleteDirectoryRecursively(const char *path) override final {
return fs::ResultUnsupportedOperation(); return fs::ResultUnsupportedOperation();
} }
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override final { virtual Result DoRenameFile(const char *old_path, const char *new_path) override final {
return fs::ResultUnsupportedOperation(); return fs::ResultUnsupportedOperation();
} }
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override final { virtual Result DoRenameDirectory(const char *old_path, const char *new_path) override final {
return fs::ResultUnsupportedOperation(); return fs::ResultUnsupportedOperation();
} }
virtual Result CleanDirectoryRecursivelyImpl(const char *path) override final { virtual Result DoCleanDirectoryRecursively(const char *path) override final {
return fs::ResultUnsupportedOperation(); return fs::ResultUnsupportedOperation();
} }
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) override final { virtual Result DoGetFreeSpaceSize(s64 *out, const char *path) override final {
return fs::ResultUnsupportedOperation(); return fs::ResultUnsupportedOperation();
} }
virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) override final { virtual Result DoGetTotalSpaceSize(s64 *out, const char *path) override final {
return fs::ResultUnsupportedOperation(); return fs::ResultUnsupportedOperation();
} }
virtual Result CommitProvisionallyImpl(s64 counter) override final { virtual Result DoCommitProvisionally(s64 counter) override final {
return fs::ResultUnsupportedOperation(); return fs::ResultUnsupportedOperation();
} }
}; };
@ -201,7 +201,7 @@ namespace ams::fs {
return has_file; return has_file;
} }
virtual Result OpenFileImpl(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 char *path, OpenMode mode) override final {
/* Only allow opening files with mode = read. */ /* Only allow opening files with mode = read. */
R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidOpenMode()); R_UNLESS((mode & fs::OpenMode_All) == fs::OpenMode_Read, fs::ResultInvalidOpenMode());
@ -248,7 +248,7 @@ namespace ams::fs {
return ResultSuccess(); return ResultSuccess();
} }
private: private:
virtual Result OpenFileImpl(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 char *path, OpenMode mode) override final {
/* Ensure that we're initialized. */ /* Ensure that we're initialized. */
R_UNLESS(this->initialized, fs::ResultNotInitialized()); R_UNLESS(this->initialized, fs::ResultNotInitialized());

View File

@ -0,0 +1,569 @@
/*
* Copyright (c) 2018-2020 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);
AMS_ASSERT(R_SUCCEEDED(is_norm_result));
AMS_ASSERT(normalized);
}
return ResultSuccess();
}
Result PathNormalizer::IsNormalized(bool *out, const char *path, bool unc_preserved, bool has_mount_name) {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(path != nullptr);
/* Save the start of the path. */
const char *path_start = path;
/* If we should, skip the mount name. */
if (has_mount_name) {
R_TRY(SkipMountName(std::addressof(path), path));
R_UNLESS(IsSeparator(*path), fs::ResultInvalidPathFormat());
}
/* If we should, handle unc. */
bool is_unc_path = false;
if (unc_preserved) {
path_start = path;
/* Skip the windows path. */
bool normalized_windows = false;
R_TRY(SkipWindowsPath(std::addressof(path), std::addressof(normalized_windows), path, has_mount_name));
/* If we're not windows-normalized, we're not normalized. */
if (!normalized_windows) {
*out = false;
return ResultSuccess();
}
/* Handle the case where we're dealing with a unc path. */
if (path != path_start) {
is_unc_path = true;
if (IsSeparator(path_start[0]) && IsSeparator(path_start[1])) {
*out = false;
return ResultSuccess();
}
if (IsNullTerminator(path[0])) {
*out = true;
return ResultSuccess();
}
}
}
/* Check if parent directory path replacement is needed. */
if (IsParentDirectoryPathReplacementNeeded(path)) {
*out = false;
return ResultSuccess();
}
/* Nintendo uses a state machine here. */
enum class PathState {
Start,
Normal,
FirstSeparator,
Separator,
CurrentDir,
ParentDir,
};
PathState state = PathState::Start;
for (const char *cur = path; *cur != StringTraits::NullTerminator; ++cur) {
const char c = *cur;
switch (state) {
case PathState::Start:
if (IsSeparator(c)) {
state = PathState::FirstSeparator;
} else {
R_UNLESS(path != path_start, fs::ResultInvalidPathFormat());
if (c == StringTraits::Dot) {
state = PathState::CurrentDir;
} else {
state = PathState::Normal;
}
}
break;
case PathState::Normal:
if (IsSeparator(c)) {
state = PathState::Separator;
}
break;
case PathState::FirstSeparator:
case PathState::Separator:
if (IsSeparator(c)) {
*out = false;
return ResultSuccess();
} else if (c == StringTraits::Dot) {
state = PathState::CurrentDir;
} else {
state = PathState::Normal;
}
break;
case PathState::CurrentDir:
if (IsSeparator(c)) {
*out = false;
return ResultSuccess();
} else if (c == StringTraits::Dot) {
state = PathState::ParentDir;
} else {
state = PathState::Normal;
}
break;
case PathState::ParentDir:
if (IsSeparator(c)) {
*out = false;
return ResultSuccess();
} else {
state = PathState::Normal;
}
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
switch (state) {
case PathState::Start:
return fs::ResultInvalidPathFormat();
case PathState::Normal:
*out = true;
break;
case PathState::FirstSeparator:
*out = !is_unc_path;
break;
case PathState::CurrentDir:
case PathState::ParentDir:
case PathState::Separator:
*out = false;
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
return ResultSuccess();
}
}

View File

@ -1,248 +0,0 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
namespace ams::fs {
Result PathTool::Normalize(char *out, size_t *out_len, const char *src, size_t max_out_size, bool unc_preserved) {
/* Paths must start with / */
R_UNLESS(IsSeparator(src[0]), fs::ResultInvalidPathFormat());
bool skip_next_sep = false;
size_t i = 0;
size_t len = 0;
while (!IsNullTerminator(src[i])) {
if (IsSeparator(src[i])) {
/* Swallow separators. */
while (IsSeparator(src[++i])) { }
if (IsNullTerminator(src[i])) {
break;
}
/* Handle skip if needed */
if (!skip_next_sep) {
if (len + 1 == max_out_size) {
out[len] = StringTraits::NullTerminator;
if (out_len != nullptr) {
*out_len = len;
}
return fs::ResultTooLongPath();
}
out[len++] = StringTraits::DirectorySeparator;
if (unc_preserved && len == 1) {
while (len < i) {
if (len + 1 == max_out_size) {
out[len] = StringTraits::NullTerminator;
if (out_len != nullptr) {
*out_len = len;
}
return fs::ResultTooLongPath();
}
out[len++] = StringTraits::DirectorySeparator;
}
}
}
skip_next_sep = false;
}
/* See length of current dir. */
size_t dir_len = 0;
while (!IsSeparator(src[i+dir_len]) && !IsNullTerminator(src[i+dir_len])) {
dir_len++;
}
if (IsCurrentDirectory(&src[i])) {
skip_next_sep = true;
} else if (IsParentDirectory(&src[i])) {
AMS_ABORT_UNLESS(IsSeparator(out[0]));
AMS_ABORT_UNLESS(IsSeparator(out[len - 1]));
R_UNLESS(len != 1, fs::ResultDirectoryUnobtainable());
/* Walk up a directory. */
len -= 2;
while (!IsSeparator(out[len])) {
len--;
}
} else {
/* Copy, possibly truncating. */
if (len + dir_len + 1 <= max_out_size) {
for (size_t j = 0; j < dir_len; j++) {
out[len++] = src[i+j];
}
} else {
const size_t copy_len = max_out_size - 1 - len;
for (size_t j = 0; j < copy_len; j++) {
out[len++] = src[i+j];
}
out[len] = StringTraits::NullTerminator;
if (out_len != nullptr) {
*out_len = len;
}
return fs::ResultTooLongPath();
}
}
i += dir_len;
}
if (skip_next_sep) {
len--;
}
if (len == 0 && max_out_size) {
out[len++] = StringTraits::DirectorySeparator;
}
R_UNLESS(max_out_size >= len - 1, fs::ResultTooLongPath());
/* Null terminate. */
out[len] = StringTraits::NullTerminator;
if (out_len != nullptr) {
*out_len = len;
}
/* Assert normalized. */
bool normalized = false;
AMS_ABORT_UNLESS(unc_preserved || (R_SUCCEEDED(IsNormalized(&normalized, out)) && normalized));
return ResultSuccess();
}
Result PathTool::IsNormalized(bool *out, const char *path) {
/* Nintendo uses a state machine here. */
enum class PathState {
Start,
Normal,
FirstSeparator,
Separator,
CurrentDir,
ParentDir,
WindowsDriveLetter,
};
PathState state = PathState::Start;
for (const char *cur = path; *cur != StringTraits::NullTerminator; cur++) {
const char c = *cur;
switch (state) {
case PathState::Start:
if (IsWindowsDriveCharacter(c)) {
state = PathState::WindowsDriveLetter;
} else if (IsSeparator(c)) {
state = PathState::FirstSeparator;
} else {
return fs::ResultInvalidPathFormat();
}
break;
case PathState::Normal:
if (IsSeparator(c)) {
state = PathState::Separator;
}
break;
case PathState::FirstSeparator:
case PathState::Separator:
if (IsSeparator(c)) {
*out = false;
return ResultSuccess();
} else if (IsDot(c)) {
state = PathState::CurrentDir;
} else {
state = PathState::Normal;
}
break;
case PathState::CurrentDir:
if (IsSeparator(c)) {
*out = false;
return ResultSuccess();
} else if (IsDot(c)) {
state = PathState::ParentDir;
} else {
state = PathState::Normal;
}
break;
case PathState::ParentDir:
if (IsSeparator(c)) {
*out = false;
return ResultSuccess();
} else {
state = PathState::Normal;
}
break;
case PathState::WindowsDriveLetter:
if (IsDriveSeparator(c)) {
*out = true;
return ResultSuccess();
} else {
return fs::ResultInvalidPathFormat();
}
break;
}
}
switch (state) {
case PathState::Start:
case PathState::WindowsDriveLetter:
return fs::ResultInvalidPathFormat();
case PathState::FirstSeparator:
case PathState::Normal:
*out = true;
break;
case PathState::CurrentDir:
case PathState::ParentDir:
case PathState::Separator:
*out = false;
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
return ResultSuccess();
}
bool PathTool::IsSubPath(const char *lhs, const char *rhs) {
AMS_ABORT_UNLESS(lhs != nullptr);
AMS_ABORT_UNLESS(rhs != nullptr);
/* Special case certain paths. */
if (IsSeparator(lhs[0]) && !IsSeparator(lhs[1]) && IsSeparator(rhs[0]) && IsSeparator(rhs[1])) {
return false;
}
if (IsSeparator(rhs[0]) && !IsSeparator(rhs[1]) && IsSeparator(lhs[0]) && IsSeparator(lhs[1])) {
return false;
}
if (IsSeparator(lhs[0]) && IsNullTerminator(lhs[1]) && IsSeparator(rhs[0]) && !IsNullTerminator(rhs[1])) {
return true;
}
if (IsSeparator(rhs[0]) && IsNullTerminator(rhs[1]) && IsSeparator(lhs[0]) && !IsNullTerminator(lhs[1])) {
return true;
}
/* Check subpath. */
for (size_t i = 0; /* No terminating condition */; i++) {
if (IsNullTerminator(lhs[i])) {
return IsSeparator(rhs[i]);
} else if (IsNullTerminator(rhs[i])) {
return IsSeparator(lhs[i]);
} else if (lhs[i] != rhs[i]) {
return false;
}
}
}
}

View File

@ -17,31 +17,114 @@
namespace ams::fs { namespace ams::fs {
Result VerifyPath(const char *path, size_t max_path_len, size_t max_name_len) { namespace {
const char *cur = path;
size_t name_len = 0;
for (size_t path_len = 0; path_len <= max_path_len && name_len <= max_name_len; path_len++) { class PathVerifier {
const char c = *(cur++); private:
u32 invalid_chars[6];
u32 separators[2];
public:
PathVerifier() {
/* Convert all invalid characters. */
u32 *dst_invalid = this->invalid_chars;
for (const char *cur = ":*?<>|"; *cur != '\x00'; ++cur) {
AMS_ASSERT(dst_invalid < std::end(this->invalid_chars));
const auto result = util::ConvertCharacterUtf8ToUtf32(dst_invalid, cur);
AMS_ASSERT(result == util::CharacterEncodingResult_Success);
++dst_invalid;
}
AMS_ASSERT(dst_invalid == std::end(this->invalid_chars));
/* If terminated, we're done. */ /* Convert all separators. */
R_SUCCEED_IF(PathTool::IsNullTerminator(c)); u32 *dst_sep = this->separators;
for (const char *cur = "/\\"; *cur != '\x00'; ++cur) {
AMS_ASSERT(dst_sep < std::end(this->separators));
const auto result = util::ConvertCharacterUtf8ToUtf32(dst_sep, cur);
AMS_ASSERT(result == util::CharacterEncodingResult_Success);
++dst_sep;
}
AMS_ASSERT(dst_sep == std::end(this->separators));
}
/* TODO: Nintendo converts the path from utf-8 to utf-32, one character at a time. */ Result Verify(const char *path, int max_path_len, int max_name_len) const {
/* We should do this. */ AMS_ASSERT(path != nullptr);
/* Banned characters: :*?<>| */ auto cur = path;
R_UNLESS((c != ':' && c != '*' && c != '?' && c != '<' && c != '>' && c != '|'), fs::ResultInvalidCharacter()); 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 : this->invalid_chars) {
R_UNLESS(path_char != invalid, fs::ResultInvalidCharacter());
}
/* Increment name length. */
++name_len;
name_len++;
/* Check for separator. */ /* Check for separator. */
if (c == '\\' || c == '/') { for (const auto sep : this->separators) {
if (path_char == sep) {
name_len = 0; name_len = 0;
break;
}
}
} }
} /* The path was too long. */
return fs::ResultTooLongPath(); return fs::ResultTooLongPath();
} }
};
PathVerifier g_path_verifier;
}
Result VerifyPath(const char *path, int max_path_len, int max_name_len) {
return g_path_verifier.Verify(path, max_path_len, max_name_len);
}
bool IsSubPath(const char *lhs, const char *rhs) {
AMS_ASSERT(lhs != nullptr);
AMS_ASSERT(rhs != nullptr);
/* Special case certain paths. */
if (IsUnc(lhs) && !IsUnc(rhs)) {
return false;
}
if (!IsUnc(lhs) && IsUnc(rhs)) {
return false;
}
if (PathNormalizer::IsSeparator(lhs[0]) && PathNormalizer::IsNullTerminator(lhs[1]) && PathNormalizer::IsSeparator(rhs[0]) && !PathNormalizer::IsNullTerminator(rhs[1])) {
return true;
}
if (PathNormalizer::IsSeparator(rhs[0]) && PathNormalizer::IsNullTerminator(rhs[1]) && PathNormalizer::IsSeparator(lhs[0]) && !PathNormalizer::IsNullTerminator(lhs[1])) {
return true;
}
/* Check subpath. */
for (size_t i = 0; /* No terminating condition */; i++) {
if (PathNormalizer::IsNullTerminator(lhs[i])) {
return PathNormalizer::IsSeparator(rhs[i]);
} else if (PathNormalizer::IsNullTerminator(rhs[i])) {
return PathNormalizer::IsSeparator(lhs[i]);
} else if (lhs[i] != rhs[i]) {
return false;
}
}
}
} }

View File

@ -199,7 +199,7 @@ namespace ams::fs {
return this->parent->GetBaseStorage(); return this->parent->GetBaseStorage();
} }
public: public:
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override { virtual Result DoRead(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override {
size_t read_size = 0; size_t read_size = 0;
R_TRY(this->VerifyArguments(std::addressof(read_size), offset, buffer, size, option)); R_TRY(this->VerifyArguments(std::addressof(read_size), offset, buffer, size, option));
@ -209,24 +209,24 @@ namespace ams::fs {
return ResultSuccess(); return ResultSuccess();
} }
virtual Result GetSizeImpl(s64 *out) override { virtual Result DoGetSize(s64 *out) override {
*out = this->GetSize(); *out = this->GetSize();
return ResultSuccess(); return ResultSuccess();
} }
virtual Result FlushImpl() override { virtual Result DoFlush() override {
return ResultSuccess(); return ResultSuccess();
} }
virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override { virtual Result DoWrite(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override {
return fs::ResultUnsupportedOperationInRomFsFileA(); return fs::ResultUnsupportedOperationInRomFsFileA();
} }
virtual Result SetSizeImpl(s64 size) override { virtual Result DoSetSize(s64 size) override {
return fs::ResultUnsupportedOperationInRomFsFileA(); return fs::ResultUnsupportedOperationInRomFsFileA();
} }
virtual Result OperateRangeImpl(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { 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 {
switch (op_id) { switch (op_id) {
case OperationId::InvalidateCache: case OperationId::InvalidateCache:
case OperationId::QueryRange: case OperationId::QueryRange:
@ -263,16 +263,16 @@ namespace ams::fs {
RomFsDirectory(RomFsFileSystem *p, const FindPosition &f, fs::OpenDirectoryMode m) : parent(p), current_find(f), first_find(f), mode(m) { /* ... */ } RomFsDirectory(RomFsFileSystem *p, const FindPosition &f, fs::OpenDirectoryMode m) : parent(p), current_find(f), first_find(f), mode(m) { /* ... */ }
virtual ~RomFsDirectory() override { /* ... */ } virtual ~RomFsDirectory() override { /* ... */ }
public: public:
virtual Result ReadImpl(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) { virtual Result DoRead(s64 *out_count, DirectoryEntry *out_entries, s64 max_entries) {
return this->ReadImpl(out_count, std::addressof(this->current_find), out_entries, max_entries); return this->ReadInternal(out_count, std::addressof(this->current_find), out_entries, max_entries);
} }
virtual Result GetEntryCountImpl(s64 *out) { virtual Result DoGetEntryCount(s64 *out) {
FindPosition find = this->first_find; FindPosition find = this->first_find;
return this->ReadImpl(out, std::addressof(find), nullptr, 0); return this->ReadInternal(out, std::addressof(find), nullptr, 0);
} }
private: private:
Result ReadImpl(s64 *out_count, FindPosition *find, DirectoryEntry *out_entries, s64 max_entries) { Result ReadInternal(s64 *out_count, FindPosition *find, DirectoryEntry *out_entries, s64 max_entries) {
AMS_ASSERT(out_count != nullptr); AMS_ASSERT(out_count != nullptr);
AMS_ASSERT(find != nullptr); AMS_ASSERT(find != nullptr);
@ -316,7 +316,7 @@ namespace ams::fs {
out_entries[i].type = fs::DirectoryEntryType_File; out_entries[i].type = fs::DirectoryEntryType_File;
RomFsFileSystem::RomFileTable::FileInfo file_info; RomFsFileSystem::RomFileTable::FileInfo file_info;
R_TRY(this->parent->GetRomFileTable()->OpenFile(std::addressof(file_info), this->parent->GetRomFileTable()->ConvertToFileId(file_pos))); R_TRY(this->parent->GetRomFileTable()->OpenFile(std::addressof(file_info), this->parent->GetRomFileTable()->PositionToFileId(file_pos)));
out_entries[i].file_size = file_info.size.Get(); out_entries[i].file_size = file_info.size.Get();
} }
@ -440,35 +440,35 @@ namespace ams::fs {
return ResultSuccess(); return ResultSuccess();
} }
Result RomFsFileSystem::CreateFileImpl(const char *path, s64 size, int flags) { Result RomFsFileSystem::DoCreateFile(const char *path, s64 size, int flags) {
return fs::ResultUnsupportedOperationInRomFsFileSystemA(); return fs::ResultUnsupportedOperationInRomFsFileSystemA();
} }
Result RomFsFileSystem::DeleteFileImpl(const char *path) { Result RomFsFileSystem::DoDeleteFile(const char *path) {
return fs::ResultUnsupportedOperationInRomFsFileSystemA(); return fs::ResultUnsupportedOperationInRomFsFileSystemA();
} }
Result RomFsFileSystem::CreateDirectoryImpl(const char *path) { Result RomFsFileSystem::DoCreateDirectory(const char *path) {
return fs::ResultUnsupportedOperationInRomFsFileSystemA(); return fs::ResultUnsupportedOperationInRomFsFileSystemA();
} }
Result RomFsFileSystem::DeleteDirectoryImpl(const char *path) { Result RomFsFileSystem::DoDeleteDirectory(const char *path) {
return fs::ResultUnsupportedOperationInRomFsFileSystemA(); return fs::ResultUnsupportedOperationInRomFsFileSystemA();
} }
Result RomFsFileSystem::DeleteDirectoryRecursivelyImpl(const char *path) { Result RomFsFileSystem::DoDeleteDirectoryRecursively(const char *path) {
return fs::ResultUnsupportedOperationInRomFsFileSystemA(); return fs::ResultUnsupportedOperationInRomFsFileSystemA();
} }
Result RomFsFileSystem::RenameFileImpl(const char *old_path, const char *new_path) { Result RomFsFileSystem::DoRenameFile(const char *old_path, const char *new_path) {
return fs::ResultUnsupportedOperationInRomFsFileSystemA(); return fs::ResultUnsupportedOperationInRomFsFileSystemA();
} }
Result RomFsFileSystem::RenameDirectoryImpl(const char *old_path, const char *new_path) { Result RomFsFileSystem::DoRenameDirectory(const char *old_path, const char *new_path) {
return fs::ResultUnsupportedOperationInRomFsFileSystemA(); return fs::ResultUnsupportedOperationInRomFsFileSystemA();
} }
Result RomFsFileSystem::GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) { Result RomFsFileSystem::DoGetEntryType(fs::DirectoryEntryType *out, const char *path) {
RomDirectoryInfo dir_info; RomDirectoryInfo dir_info;
R_TRY_CATCH(this->rom_file_table.GetDirectoryInformation(std::addressof(dir_info), path)) { R_TRY_CATCH(this->rom_file_table.GetDirectoryInformation(std::addressof(dir_info), path)) {
R_CONVERT(fs::ResultDbmNotFound, fs::ResultPathNotFound()) R_CONVERT(fs::ResultDbmNotFound, fs::ResultPathNotFound())
@ -484,7 +484,7 @@ namespace ams::fs {
return ResultSuccess(); return ResultSuccess();
} }
Result RomFsFileSystem::OpenFileImpl(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 char *path, fs::OpenMode mode) {
AMS_ASSERT(out_file != nullptr); AMS_ASSERT(out_file != nullptr);
AMS_ASSERT(path != nullptr); AMS_ASSERT(path != nullptr);
@ -500,7 +500,7 @@ namespace ams::fs {
return ResultSuccess(); return ResultSuccess();
} }
Result RomFsFileSystem::OpenDirectoryImpl(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 char *path, fs::OpenDirectoryMode mode) {
AMS_ASSERT(out_dir != nullptr); AMS_ASSERT(out_dir != nullptr);
AMS_ASSERT(path != nullptr); AMS_ASSERT(path != nullptr);
@ -517,28 +517,28 @@ namespace ams::fs {
return ResultSuccess(); return ResultSuccess();
} }
Result RomFsFileSystem::CommitImpl() { Result RomFsFileSystem::DoCommit() {
return ResultSuccess(); return ResultSuccess();
} }
Result RomFsFileSystem::GetFreeSpaceSizeImpl(s64 *out, const char *path) { Result RomFsFileSystem::DoGetFreeSpaceSize(s64 *out, const char *path) {
*out = 0; *out = 0;
return ResultSuccess(); return ResultSuccess();
} }
Result RomFsFileSystem::GetTotalSpaceSizeImpl(s64 *out, const char *path) { Result RomFsFileSystem::DoGetTotalSpaceSize(s64 *out, const char *path) {
return fs::ResultUnsupportedOperationInRomFsFileSystemC(); return fs::ResultUnsupportedOperationInRomFsFileSystemC();
} }
Result RomFsFileSystem::CleanDirectoryRecursivelyImpl(const char *path) { Result RomFsFileSystem::DoCleanDirectoryRecursively(const char *path) {
return fs::ResultUnsupportedOperationInRomFsFileSystemA(); return fs::ResultUnsupportedOperationInRomFsFileSystemA();
} }
Result RomFsFileSystem::CommitProvisionallyImpl(s64 counter) { Result RomFsFileSystem::DoCommitProvisionally(s64 counter) {
return fs::ResultUnsupportedOperationInRomFsFileSystemB(); return fs::ResultUnsupportedOperationInRomFsFileSystemB();
} }
Result RomFsFileSystem::RollbackImpl() { Result RomFsFileSystem::DoRollback() {
return ResultSuccess(); return ResultSuccess();
} }
} }

View File

@ -24,9 +24,9 @@ namespace ams::fs::impl {
const char *FindMountNameDriveSeparator(const char *path) { 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++) {
if (PathTool::IsDriveSeparator(*cur)) { if (*cur == StringTraits::DriveSeparator) {
return cur; return cur;
} else if (PathTool::IsNullTerminator(*cur)) { } else if (PathNormalizer::IsNullTerminator(*cur)) {
break; break;
} }
} }
@ -35,7 +35,7 @@ namespace ams::fs::impl {
Result GetMountNameAndSubPath(MountName *out_mount_name, const char **out_sub_path, const char *path) { Result GetMountNameAndSubPath(MountName *out_mount_name, const char **out_sub_path, const char *path) {
/* Handle the Host-path case. */ /* Handle the Host-path case. */
if (PathTool::IsWindowsAbsolutePath(path) || PathTool::IsUnc(path)) { if (fs::IsWindowsDrive(path) || fs::IsUnc(path)) {
std::strncpy(out_mount_name->str, HostRootFileSystemMountName, MountNameLengthMax); std::strncpy(out_mount_name->str, HostRootFileSystemMountName, MountNameLengthMax);
out_mount_name->str[MountNameLengthMax] = '\x00'; out_mount_name->str[MountNameLengthMax] = '\x00';
return ResultSuccess(); return ResultSuccess();
@ -51,8 +51,8 @@ namespace ams::fs::impl {
/* Ensure the result sub-path is valid. */ /* Ensure the result sub-path is valid. */
const char *sub_path = drive_separator + 1; const char *sub_path = drive_separator + 1;
R_UNLESS(!PathTool::IsNullTerminator(sub_path[0]), fs::ResultInvalidMountName()); R_UNLESS(!PathNormalizer::IsNullTerminator(sub_path[0]), fs::ResultInvalidMountName());
R_UNLESS(PathTool::IsAnySeparator(sub_path[0]), fs::ResultInvalidPathFormat()); R_UNLESS(PathNormalizer::IsAnySeparator(sub_path[0]), fs::ResultInvalidPathFormat());
/* Set output. */ /* Set output. */
std::memcpy(out_mount_name->str, path, len); std::memcpy(out_mount_name->str, path, len);
@ -64,17 +64,17 @@ namespace ams::fs::impl {
} }
bool IsValidMountName(const char *name) { bool IsValidMountName(const char *name) {
if (PathTool::IsNullTerminator(*name)) { if (PathNormalizer::IsNullTerminator(name[0])) {
return false; return false;
} }
if (PathTool::IsWindowsDriveCharacter(name[0]) && PathTool::IsNullTerminator(name[1])) { if ((('a' <= name[0] && name[0] <= 'z') || ('A' <= name[0] && name[0] <= 'Z')) && PathNormalizer::IsNullTerminator(name[1])) {
return false; return false;
} }
size_t len = 0; size_t len = 0;
for (const char *cur = name; !PathTool::IsNullTerminator(*cur); cur++) { for (const char *cur = name; !PathNormalizer::IsNullTerminator(*cur); cur++) {
if (PathTool::IsDriveSeparator(*cur) || PathTool::IsSeparator(*cur)) { if (*cur == StringTraits::DriveSeparator || PathNormalizer::IsSeparator(*cur)) {
return false; return false;
} }
@ -87,10 +87,6 @@ namespace ams::fs::impl {
return true; return true;
} }
bool IsWindowsDrive(const char *name) {
return PathTool::IsWindowsAbsolutePath(name);
}
bool IsReservedMountName(const char *name) { bool IsReservedMountName(const char *name) {
return name[0] == ReservedMountNamePrefixCharacter; return name[0] == ReservedMountNamePrefixCharacter;
} }

View File

@ -216,7 +216,7 @@ namespace ams::fssrv::impl {
R_UNLESS(old_normalizer.GetPath() != nullptr, old_normalizer.GetResult()); R_UNLESS(old_normalizer.GetPath() != nullptr, old_normalizer.GetResult());
R_UNLESS(new_normalizer.GetPath() != nullptr, new_normalizer.GetResult()); R_UNLESS(new_normalizer.GetPath() != nullptr, new_normalizer.GetResult());
const bool is_subpath = fssystem::PathTool::IsSubPath(old_normalizer.GetPath(), new_normalizer.GetPath()); const bool is_subpath = fs::IsSubPath(old_normalizer.GetPath(), new_normalizer.GetPath());
R_UNLESS(!is_subpath, fs::ResultDirectoryNotRenamable()); R_UNLESS(!is_subpath, fs::ResultDirectoryNotRenamable());
return this->base_fs->RenameFile(old_normalizer.GetPath(), new_normalizer.GetPath()); return this->base_fs->RenameFile(old_normalizer.GetPath(), new_normalizer.GetPath());

View File

@ -18,51 +18,36 @@
namespace ams::fssrv { 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) { 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. */ /* Clear output. */
*out_path = nullptr; *out_path = nullptr;
*out_buf = Buffer(); *out_buf = Buffer();
/* Find start of path. */
const char *path_start = path;
if (has_mount_name) {
while (path_start < path + fs::MountNameLengthMax + 1) {
if (fssystem::PathTool::IsNullTerminator(*path_start)) {
break;
} else if (fssystem::PathTool::IsDriveSeparator(*(path_start++))) {
break;
}
}
R_UNLESS(path < path_start - 1, fs::ResultInvalidPath());
R_UNLESS(fssystem::PathTool::IsDriveSeparator(*(path_start - 1)), fs::ResultInvalidPath());
}
/* Check if we're normalized. */ /* Check if we're normalized. */
bool normalized = false; bool normalized = false;
R_TRY(fssystem::PathTool::IsNormalized(&normalized, path_start)); R_TRY(fs::PathNormalizer::IsNormalized(std::addressof(normalized), path, preserve_unc, has_mount_name));
if (normalized) { if (normalized) {
/* If we're already normalized, no allocation is needed. */ /* If we're already normalized, no allocation is needed. */
*out_path = path; *out_path = path;
} else { } else {
/* Allocate a new buffer. */ /* Allocate a new buffer. */
auto buffer = std::make_unique<char[]>(fs::EntryNameLengthMax + 1); auto buffer = fs::impl::MakeUnique<char[]>(fs::EntryNameLengthMax + 1);
R_UNLESS(buffer != nullptr, fs::ResultAllocationFailureInPathNormalizer()); R_UNLESS(buffer != nullptr, fs::ResultAllocationFailureInPathNormalizer());
/* Copy in mount name. */
const size_t mount_name_len = path_start - path;
std::memcpy(buffer.get(), path, mount_name_len);
/* Generate normalized path. */ /* Generate normalized path. */
size_t normalized_len = 0; size_t normalized_len = 0;
R_TRY(fssystem::PathTool::Normalize(buffer.get() + mount_name_len, &normalized_len, path_start, fs::EntryNameLengthMax + 1 - mount_name_len, preserve_unc)); 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. */ /* Preserve the tail separator, if we should. */
if (preserve_tail_sep) { if (preserve_tail_sep) {
if (fssystem::PathTool::IsSeparator(path[strnlen(path, fs::EntryNameLengthMax) - 1])) { if (fs::PathNormalizer::IsSeparator(path[strnlen(path, fs::EntryNameLengthMax) - 1]) && !fs::PathNormalizer::IsSeparator(buffer[normalized_len - 1])) {
/* Nintendo doesn't actually validate this. */ AMS_ASSERT(normalized_len < fs::EntryNameLengthMax);
R_UNLESS(mount_name_len + normalized_len < fs::EntryNameLengthMax, fs::ResultTooLongPath()); buffer[normalized_len] = fs::StringTraits::DirectorySeparator;
buffer[mount_name_len + normalized_len] = fssystem::StringTraits::DirectorySeparator; buffer[normalized_len + 1] = fs::StringTraits::NullTerminator;
buffer[mount_name_len + normalized_len + 1] = fssystem::StringTraits::NullTerminator;
} }
} }

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