Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"
This reverts commit 15b7df8ef1.
This commit is contained in:
@@ -1,89 +0,0 @@
|
||||
{
|
||||
"name": "LogManager",
|
||||
"title_id": "0x0100000000000420",
|
||||
"title_id_range_min": "0x0100000000000420",
|
||||
"title_id_range_max": "0x0100000000000420",
|
||||
"main_thread_stack_size": "0x00003000",
|
||||
"main_thread_priority": 38,
|
||||
"default_cpu_id": 3,
|
||||
"process_category": 0,
|
||||
"is_retail": true,
|
||||
"pool_partition": 2,
|
||||
"is_64_bit": true,
|
||||
"address_space_type": 1,
|
||||
"filesystem_access": {
|
||||
"permissions": "0xFFFFFFFFFFFFFFFF"
|
||||
},
|
||||
"service_access": ["fsp-srv", "htc", "htcs", "psc:m", "set", "set:sys", "srepo:u", "time:su", "time:u", "tma_log"],
|
||||
"service_host": ["lm", "lm:get"],
|
||||
"kernel_capabilities": [{
|
||||
"type": "kernel_flags",
|
||||
"value": {
|
||||
"highest_thread_priority": 63,
|
||||
"lowest_thread_priority": 24,
|
||||
"lowest_cpu_id": 3,
|
||||
"highest_cpu_id": 3
|
||||
}
|
||||
}, {
|
||||
"type": "syscalls",
|
||||
"value": {
|
||||
"svcSetHeapSize": "0x01",
|
||||
"svcSetMemoryPermission": "0x02",
|
||||
"svcSetMemoryAttribute": "0x03",
|
||||
"svcMapMemory": "0x04",
|
||||
"svcUnmapMemory": "0x05",
|
||||
"svcQueryMemory": "0x06",
|
||||
"svcExitProcess": "0x07",
|
||||
"svcCreateThread": "0x08",
|
||||
"svcStartThread": "0x09",
|
||||
"svcExitThread": "0x0a",
|
||||
"svcSleepThread": "0x0b",
|
||||
"svcGetThreadPriority": "0x0c",
|
||||
"svcSetThreadPriority": "0x0d",
|
||||
"svcGetThreadCoreMask": "0x0e",
|
||||
"svcSetThreadCoreMask": "0x0f",
|
||||
"svcGetCurrentProcessorNumber": "0x10",
|
||||
"svcSignalEvent": "0x11",
|
||||
"svcClearEvent": "0x12",
|
||||
"svcMapSharedMemory": "0x13",
|
||||
"svcUnmapSharedMemory": "0x14",
|
||||
"svcCreateTransferMemory": "0x15",
|
||||
"svcCloseHandle": "0x16",
|
||||
"svcResetSignal": "0x17",
|
||||
"svcWaitSynchronization": "0x18",
|
||||
"svcCancelSynchronization": "0x19",
|
||||
"svcArbitrateLock": "0x1a",
|
||||
"svcArbitrateUnlock": "0x1b",
|
||||
"svcWaitProcessWideKeyAtomic": "0x1c",
|
||||
"svcSignalProcessWideKey": "0x1d",
|
||||
"svcGetSystemTick": "0x1e",
|
||||
"svcConnectToNamedPort": "0x1f",
|
||||
"svcSendSyncRequestLight": "0x20",
|
||||
"svcSendSyncRequest": "0x21",
|
||||
"svcSendSyncRequestWithUserBuffer": "0x22",
|
||||
"svcSendAsyncRequestWithUserBuffer": "0x23",
|
||||
"svcGetProcessId": "0x24",
|
||||
"svcGetThreadId": "0x25",
|
||||
"svcBreak": "0x26",
|
||||
"svcOutputDebugString": "0x27",
|
||||
"svcReturnFromException": "0x28",
|
||||
"svcGetInfo": "0x29",
|
||||
"svcWaitForAddress": "0x34",
|
||||
"svcSignalToAddress": "0x35",
|
||||
"svcSynchronizePreemptionState": "0x36",
|
||||
"svcCreateSession": "0x40",
|
||||
"svcAcceptSession": "0x41",
|
||||
"svcReplyAndReceiveLight": "0x42",
|
||||
"svcReplyAndReceive": "0x43",
|
||||
"svcReplyAndReceiveWithUserBuffer": "0x44",
|
||||
"svcCreateEvent": "0x45",
|
||||
"svcCallSecureMonitor": "0x7f"
|
||||
}
|
||||
}, {
|
||||
"type": "min_kernel_version",
|
||||
"value": "0x0030"
|
||||
}, {
|
||||
"type": "handle_table_size",
|
||||
"value": 64
|
||||
}]
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
ATMOSPHERE_BUILD_CONFIGS :=
|
||||
all: nx_release
|
||||
|
||||
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE)))
|
||||
|
||||
define ATMOSPHERE_ADD_TARGET
|
||||
|
||||
ATMOSPHERE_BUILD_CONFIGS += $(strip $1)
|
||||
|
||||
$(strip $1):
|
||||
@echo "Building $(strip $1)"
|
||||
@$$(MAKE) -f $(CURRENT_DIRECTORY)/system_module.mk ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
|
||||
|
||||
clean-$(strip $1):
|
||||
@echo "Cleaning $(strip $1)"
|
||||
@$$(MAKE) -f $(CURRENT_DIRECTORY)/system_module.mk clean ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
|
||||
|
||||
endef
|
||||
|
||||
define ATMOSPHERE_ADD_TARGETS
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_release, release, $(strip $2), $(strip $3), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $4)" \
|
||||
))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_debug, debug, $(strip $2), $(strip $3), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $4) -DAMS_BUILD_FOR_DEBUGGING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 \
|
||||
))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_audit, audit, $(strip $2), $(strip $3), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $4) -DAMS_BUILD_FOR_AUDITING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 ATMOSPHERE_BUILD_FOR_AUDITING=1 \
|
||||
))
|
||||
|
||||
endef
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, nx, nx-hac-001, arm-cortex-a57,))
|
||||
|
||||
clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config))
|
||||
|
||||
.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS), $(config) clean-$(config))
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams {
|
||||
|
||||
namespace lm::srv {
|
||||
|
||||
void StartLogServerProxy();
|
||||
void StopLogServerProxy();
|
||||
|
||||
void InitializeFlushThread();
|
||||
void FinalizeFlushThread();
|
||||
|
||||
void InitializeIpcServer();
|
||||
void LoopIpcServer();
|
||||
void FinalizeIpcServer();
|
||||
|
||||
}
|
||||
|
||||
namespace init {
|
||||
|
||||
void InitializeSystemModule() {
|
||||
/* Initialize our connection to sm. */
|
||||
R_ABORT_UNLESS(sm::Initialize());
|
||||
|
||||
/* Initialize services we need. */
|
||||
R_ABORT_UNLESS(::setsysInitialize());
|
||||
R_ABORT_UNLESS(::pscmInitialize());
|
||||
|
||||
/* Verify that we can sanely execute. */
|
||||
ams::CheckApiVersion();
|
||||
}
|
||||
|
||||
void FinalizeSystemModule() { /* ... */ }
|
||||
|
||||
void Startup() { /* ... */ }
|
||||
|
||||
}
|
||||
|
||||
void Main() {
|
||||
/* Check thread priority. */
|
||||
AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(LogManager, MainThread));
|
||||
|
||||
/* Set thread name. */
|
||||
os::ChangeThreadPriority(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_PRIORITY(lm, IpcServer));
|
||||
os::SetThreadNamePointer(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_NAME(lm, IpcServer));
|
||||
|
||||
/* Start log server proxy. */
|
||||
lm::srv::StartLogServerProxy();
|
||||
|
||||
/* Initialize flush thread. */
|
||||
lm::srv::InitializeFlushThread();
|
||||
|
||||
/* Process IPC server. */
|
||||
lm::srv::InitializeIpcServer();
|
||||
lm::srv::LoopIpcServer();
|
||||
lm::srv::FinalizeIpcServer();
|
||||
|
||||
/* Finalize flush thread. */
|
||||
lm::srv::FinalizeFlushThread();
|
||||
|
||||
/* Stop log server proxy. */
|
||||
lm::srv::StopLogServerProxy();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
# pull in common stratosphere sysmodule configuration
|
||||
#---------------------------------------------------------------------------------
|
||||
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE)))
|
||||
include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk
|
||||
|
||||
ATMOSPHERE_SYSTEM_MODULE_TARGETS := nsp
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(__RECURSIVE__),1)
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c)
|
||||
CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp)
|
||||
SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s)
|
||||
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
$(foreach dir,$(AMS_LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(ATMOSPHERE_BUILD_DIR)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) $(foreach dir,$(AMS_LIBDIRS),-L$(dir)/$(ATMOSPHERE_LIBRARY_DIR))
|
||||
|
||||
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
|
||||
|
||||
ifeq ($(strip $(CONFIG_JSON)),)
|
||||
jsons := $(wildcard *.json)
|
||||
ifneq (,$(findstring $(TARGET).json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/$(TARGET).json
|
||||
else
|
||||
ifneq (,$(findstring config.json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/config.json
|
||||
endif
|
||||
endif
|
||||
else
|
||||
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
|
||||
endif
|
||||
|
||||
.PHONY: clean all check_lib
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||
@$(MAKE) __RECURSIVE__=1 OUTPUT=$(CURDIR)/$(ATMOSPHERE_OUT_DIR)/$(TARGET) \
|
||||
DEPSDIR=$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) \
|
||||
--no-print-directory -C $(ATMOSPHERE_BUILD_DIR) \
|
||||
-f $(THIS_MAKEFILE)
|
||||
|
||||
$(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a: check_lib
|
||||
@$(SILENTCMD)echo "Checked library."
|
||||
|
||||
ifeq ($(ATMOSPHERE_CHECKED_LIBSTRATOSPHERE),1)
|
||||
check_lib:
|
||||
else
|
||||
check_lib:
|
||||
@$(MAKE) --no-print-directory -C $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere -f $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/libstratosphere.mk
|
||||
endif
|
||||
|
||||
$(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all : $(foreach target,$(ATMOSPHERE_SYSTEM_MODULE_TARGETS),$(OUTPUT).$(target))
|
||||
|
||||
$(OUTPUT).kip : $(OUTPUT).elf
|
||||
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
|
||||
$(OUTPUT).nso : $(OUTPUT).elf
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
$(OFILES) : $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||
|
||||
%.npdm : %.npdm.json
|
||||
@echo built ... $< $@
|
||||
@npdmtool $< $@
|
||||
@echo built ... $(notdir $@)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
||||
@@ -1,41 +0,0 @@
|
||||
ATMOSPHERE_BUILD_CONFIGS :=
|
||||
all: nx_release
|
||||
|
||||
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE)))
|
||||
|
||||
define ATMOSPHERE_ADD_TARGET
|
||||
|
||||
ATMOSPHERE_BUILD_CONFIGS += $(strip $1)
|
||||
|
||||
$(strip $1):
|
||||
@echo "Building $(strip $1)"
|
||||
@$$(MAKE) -f $(CURRENT_DIRECTORY)/stratosphere.mk ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
|
||||
|
||||
clean-$(strip $1):
|
||||
@echo "Cleaning $(strip $1)"
|
||||
@$$(MAKE) -f $(CURRENT_DIRECTORY)/stratosphere.mk clean ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
|
||||
|
||||
endef
|
||||
|
||||
define ATMOSPHERE_ADD_TARGETS
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_release, release, $(strip $2), $(strip $3), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $4)" \
|
||||
))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_debug, debug, $(strip $2), $(strip $3), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $4) -DAMS_BUILD_FOR_DEBUGGING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 \
|
||||
))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_audit, audit, $(strip $2), $(strip $3), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $4) -DAMS_BUILD_FOR_AUDITING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 ATMOSPHERE_BUILD_FOR_AUDITING=1 \
|
||||
))
|
||||
|
||||
endef
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, nx, nx-hac-001, arm-cortex-a57,))
|
||||
|
||||
clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config))
|
||||
|
||||
.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS), $(config) clean-$(config))
|
||||
@@ -1,41 +0,0 @@
|
||||
ATMOSPHERE_BUILD_CONFIGS :=
|
||||
all: nx_release
|
||||
|
||||
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE)))
|
||||
|
||||
define ATMOSPHERE_ADD_TARGET
|
||||
|
||||
ATMOSPHERE_BUILD_CONFIGS += $(strip $1)
|
||||
|
||||
$(strip $1):
|
||||
@echo "Building $(strip $1)"
|
||||
@$$(MAKE) -f $(CURRENT_DIRECTORY)/system_module.mk ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
|
||||
|
||||
clean-$(strip $1):
|
||||
@echo "Cleaning $(strip $1)"
|
||||
@$$(MAKE) -f $(CURRENT_DIRECTORY)/system_module.mk clean ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
|
||||
|
||||
endef
|
||||
|
||||
define ATMOSPHERE_ADD_TARGETS
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_release, release, $(strip $2), $(strip $3), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $4)" \
|
||||
))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_debug, debug, $(strip $2), $(strip $3), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $4) -DAMS_BUILD_FOR_DEBUGGING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 \
|
||||
))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_audit, audit, $(strip $2), $(strip $3), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $4) -DAMS_BUILD_FOR_AUDITING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 ATMOSPHERE_BUILD_FOR_AUDITING=1 \
|
||||
))
|
||||
|
||||
endef
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, nx, nx-hac-001, arm-cortex-a57,))
|
||||
|
||||
clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config))
|
||||
|
||||
.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS), $(config) clean-$(config))
|
||||
@@ -1,84 +0,0 @@
|
||||
{
|
||||
"name": "TioServer",
|
||||
"title_id": "0x010000000000D623",
|
||||
"title_id_range_min": "0x010000000000D623",
|
||||
"title_id_range_max": "0x010000000000D623",
|
||||
"main_thread_stack_size": "0x00004000",
|
||||
"main_thread_priority": 49,
|
||||
"default_cpu_id": 3,
|
||||
"process_category": 0,
|
||||
"is_retail": true,
|
||||
"pool_partition": 2,
|
||||
"is_64_bit": true,
|
||||
"address_space_type": 3,
|
||||
"disable_device_address_space_merge": true,
|
||||
"filesystem_access": {
|
||||
"permissions": "0xFFFFFFFFFFFFFFFF"
|
||||
},
|
||||
"service_access": ["fatal:u", "fsp-srv", "htcs"],
|
||||
"service_host": [],
|
||||
"kernel_capabilities": [{
|
||||
"type": "kernel_flags",
|
||||
"value": {
|
||||
"highest_thread_priority": 63,
|
||||
"lowest_thread_priority": 24,
|
||||
"lowest_cpu_id": 3,
|
||||
"highest_cpu_id": 3
|
||||
}
|
||||
}, {
|
||||
"type": "syscalls",
|
||||
"value": {
|
||||
"svcSetHeapSize": "0x01",
|
||||
"svcSetMemoryPermission": "0x02",
|
||||
"svcSetMemoryAttribute": "0x03",
|
||||
"svcMapMemory": "0x04",
|
||||
"svcUnmapMemory": "0x05",
|
||||
"svcQueryMemory": "0x06",
|
||||
"svcExitProcess": "0x07",
|
||||
"svcCreateThread": "0x08",
|
||||
"svcStartThread": "0x09",
|
||||
"svcExitThread": "0x0a",
|
||||
"svcSleepThread": "0x0b",
|
||||
"svcGetThreadPriority": "0x0c",
|
||||
"svcSetThreadPriority": "0x0d",
|
||||
"svcGetThreadCoreMask": "0x0e",
|
||||
"svcSetThreadCoreMask": "0x0f",
|
||||
"svcGetCurrentProcessorNumber": "0x10",
|
||||
"svcSignalEvent": "0x11",
|
||||
"svcClearEvent": "0x12",
|
||||
"svcMapSharedMemory": "0x13",
|
||||
"svcUnmapSharedMemory": "0x14",
|
||||
"svcCreateTransferMemory": "0x15",
|
||||
"svcCloseHandle": "0x16",
|
||||
"svcResetSignal": "0x17",
|
||||
"svcWaitSynchronization": "0x18",
|
||||
"svcCancelSynchronization": "0x19",
|
||||
"svcArbitrateLock": "0x1a",
|
||||
"svcArbitrateUnlock": "0x1b",
|
||||
"svcWaitProcessWideKeyAtomic": "0x1c",
|
||||
"svcSignalProcessWideKey": "0x1d",
|
||||
"svcGetSystemTick": "0x1e",
|
||||
"svcConnectToNamedPort": "0x1f",
|
||||
"svcSendSyncRequestLight": "0x20",
|
||||
"svcSendSyncRequest": "0x21",
|
||||
"svcSendSyncRequestWithUserBuffer": "0x22",
|
||||
"svcSendAsyncRequestWithUserBuffer": "0x23",
|
||||
"svcGetProcessId": "0x24",
|
||||
"svcGetThreadId": "0x25",
|
||||
"svcBreak": "0x26",
|
||||
"svcOutputDebugString": "0x27",
|
||||
"svcReturnFromException": "0x28",
|
||||
"svcGetInfo": "0x29",
|
||||
"svcWaitForAddress": "0x34",
|
||||
"svcSignalToAddress": "0x35",
|
||||
"svcSynchronizePreemptionState": "0x36",
|
||||
"svcCallSecureMonitor": "0x7f"
|
||||
}
|
||||
}, {
|
||||
"type": "min_kernel_version",
|
||||
"value": "0x0030"
|
||||
}, {
|
||||
"type": "handle_table_size",
|
||||
"value": 0
|
||||
}]
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "tio_file_server.hpp"
|
||||
#include "tio_file_server_packet.hpp"
|
||||
#include "tio_file_server_htcs_server.hpp"
|
||||
#include "tio_file_server_processor.hpp"
|
||||
#include "tio_sd_card_observer.hpp"
|
||||
|
||||
namespace ams::tio {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline auto NumDispatchThreads = 2;
|
||||
constexpr inline auto DispatchThreadPriority = 21;
|
||||
constexpr inline size_t RequestBufferSize = 1_MB + util::AlignUp(0x40 + fs::EntryNameLengthMax, 1_KB);
|
||||
|
||||
struct FileServerRequest {
|
||||
int socket;
|
||||
FileServerRequestHeader header;
|
||||
u8 body[RequestBufferSize];
|
||||
};
|
||||
|
||||
constexpr const char HtcsPortName[] = "iywys@$TioServer_FileServer";
|
||||
|
||||
alignas(os::ThreadStackAlignment) u8 g_server_stack[os::MemoryPageSize];
|
||||
alignas(os::ThreadStackAlignment) u8 g_observer_stack[os::MemoryPageSize];
|
||||
alignas(os::ThreadStackAlignment) u8 g_dispatch_stacks[NumDispatchThreads][os::MemoryPageSize];
|
||||
|
||||
constinit FileServerHtcsServer g_file_server_htcs_server;
|
||||
constinit FileServerProcessor g_file_server_processor(g_file_server_htcs_server);
|
||||
constinit SdCardObserver g_sd_card_observer;
|
||||
|
||||
constinit os::ThreadType g_file_server_dispatch_threads[NumDispatchThreads];
|
||||
|
||||
constinit FileServerRequest g_requests[NumDispatchThreads];
|
||||
|
||||
constinit os::MessageQueueType g_free_mq;
|
||||
constinit os::MessageQueueType g_dispatch_mq;
|
||||
|
||||
constinit uintptr_t g_free_mq_storage[NumDispatchThreads];
|
||||
constinit uintptr_t g_dispatch_mq_storage[NumDispatchThreads];
|
||||
|
||||
void OnSdCardInsertionChanged(bool inserted) {
|
||||
g_file_server_processor.SetInserted(inserted);
|
||||
}
|
||||
|
||||
void OnFileServerHtcsSocketAccepted(int fd) {
|
||||
/* Service requests, while we can. */
|
||||
while (true) {
|
||||
/* Receive a free request. */
|
||||
uintptr_t request_address;
|
||||
os::ReceiveMessageQueue(std::addressof(request_address), std::addressof(g_free_mq));
|
||||
|
||||
/* Ensure we manage our request properly. */
|
||||
auto req_guard = SCOPE_GUARD { os::SendMessageQueue(std::addressof(g_free_mq), request_address); };
|
||||
|
||||
/* Receive the request header. */
|
||||
FileServerRequest *request = reinterpret_cast<FileServerRequest *>(request_address);
|
||||
if (htcs::Recv(fd, std::addressof(request->header), sizeof(request->header), htcs::HTCS_MSG_WAITALL) != sizeof(request->header)) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Receive the request body, if necessary. */
|
||||
if (request->header.body_size > 0) {
|
||||
if (htcs::Recv(fd, request->body, request->header.body_size, htcs::HTCS_MSG_WAITALL) != request->header.body_size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dispatch the request. */
|
||||
req_guard.Cancel();
|
||||
request->socket = fd;
|
||||
os::SendMessageQueue(std::addressof(g_dispatch_mq), request_address);
|
||||
}
|
||||
|
||||
/* Our socket is no longer making requests, so close it. */
|
||||
htcs::Close(fd);
|
||||
|
||||
/* Clean up any server resources. */
|
||||
g_file_server_processor.Unmount();
|
||||
}
|
||||
|
||||
void FileServerDispatchThreadFunction(void *) {
|
||||
while (true) {
|
||||
/* Receive a request. */
|
||||
uintptr_t request_address;
|
||||
os::ReceiveMessageQueue(std::addressof(request_address), std::addressof(g_dispatch_mq));
|
||||
|
||||
/* Process the request. */
|
||||
FileServerRequest *request = reinterpret_cast<FileServerRequest *>(request_address);
|
||||
if (!g_file_server_processor.ProcessRequest(std::addressof(request->header), request->body, request->socket)) {
|
||||
htcs::Close(request->socket);
|
||||
}
|
||||
|
||||
/* Free the request. */
|
||||
os::SendMessageQueue(std::addressof(g_free_mq), request_address);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InitializeFileServer() {
|
||||
/* Initialize the htcs server. */
|
||||
g_file_server_htcs_server.Initialize(HtcsPortName, g_server_stack, sizeof(g_server_stack), OnFileServerHtcsSocketAccepted);
|
||||
|
||||
/* Initialize SD card observer. */
|
||||
g_sd_card_observer.Initialize(g_observer_stack, sizeof(g_observer_stack));
|
||||
g_sd_card_observer.SetCallback(OnSdCardInsertionChanged);
|
||||
|
||||
/* Initialize the command processor. */
|
||||
g_file_server_processor.SetInserted(g_sd_card_observer.IsSdCardInserted());
|
||||
g_file_server_processor.SetRequestBufferSize(RequestBufferSize);
|
||||
|
||||
/* Initialize the dispatch message queues. */
|
||||
os::InitializeMessageQueue(std::addressof(g_free_mq), g_free_mq_storage, util::size(g_free_mq_storage));
|
||||
os::InitializeMessageQueue(std::addressof(g_dispatch_mq), g_dispatch_mq_storage, util::size(g_dispatch_mq_storage));
|
||||
|
||||
/* Begin with all requests free. */
|
||||
for (auto i = 0; i < NumDispatchThreads; ++i) {
|
||||
os::SendMessageQueue(std::addressof(g_free_mq), reinterpret_cast<uintptr_t>(g_requests + i));
|
||||
}
|
||||
|
||||
/* Initialize the dispatch threads. */
|
||||
/* NOTE: Nintendo does not name these threads. */
|
||||
for (auto i = 0; i < NumDispatchThreads; ++i) {
|
||||
R_ABORT_UNLESS(os::CreateThread(g_file_server_dispatch_threads + i, FileServerDispatchThreadFunction, nullptr, g_dispatch_stacks + i, sizeof(g_dispatch_stacks[i]), DispatchThreadPriority));
|
||||
}
|
||||
}
|
||||
|
||||
void StartFileServer() {
|
||||
/* Start the htcs server. */
|
||||
g_file_server_htcs_server.Start();
|
||||
|
||||
/* Start the dispatch threads. */
|
||||
for (auto i = 0; i < NumDispatchThreads; ++i) {
|
||||
os::StartThread(g_file_server_dispatch_threads + i);
|
||||
}
|
||||
}
|
||||
|
||||
void WaitFileServer() {
|
||||
/* Wait for the htcs server to finish. */
|
||||
g_file_server_htcs_server.Wait();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::tio {
|
||||
|
||||
void InitializeFileServer();
|
||||
void StartFileServer();
|
||||
void WaitFileServer();
|
||||
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "tio_file_server_htcs_server.hpp"
|
||||
|
||||
namespace ams::tio {
|
||||
|
||||
void FileServerHtcsServer::Initialize(const char *port_name, void *thread_stack, size_t thread_stack_size, SocketAcceptedCallback on_socket_accepted) {
|
||||
/* Set our port name. */
|
||||
std::strcpy(m_port_name.name, port_name);
|
||||
|
||||
/* Set our callback. */
|
||||
m_on_socket_accepted = on_socket_accepted;
|
||||
|
||||
/* Setup our thread. */
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(m_thread), ThreadEntry, this, thread_stack, thread_stack_size, AMS_GET_SYSTEM_THREAD_PRIORITY(TioServer, FileServerHtcsServer)));
|
||||
|
||||
/* Set our thread name pointer. */
|
||||
os::SetThreadNamePointer(std::addressof(m_thread), AMS_GET_SYSTEM_THREAD_NAME(TioServer, FileServerHtcsServer));
|
||||
}
|
||||
|
||||
void FileServerHtcsServer::Start() {
|
||||
os::StartThread(std::addressof(m_thread));
|
||||
}
|
||||
void FileServerHtcsServer::Wait() {
|
||||
os::WaitThread(std::addressof(m_thread));
|
||||
}
|
||||
|
||||
void FileServerHtcsServer::ThreadFunc() {
|
||||
/* Loop forever, servicing sockets. */
|
||||
while (true) {
|
||||
/* Get a socket. */
|
||||
int fd;
|
||||
while ((fd = htcs::Socket()) == -1) {
|
||||
os::SleepThread(TimeSpan::FromSeconds(1));
|
||||
}
|
||||
|
||||
/* Ensure we cleanup the socket when we're done with it. */
|
||||
ON_SCOPE_EXIT {
|
||||
htcs::Close(fd);
|
||||
os::SleepThread(TimeSpan::FromSeconds(1));
|
||||
};
|
||||
|
||||
/* Create a sock addr for our server. */
|
||||
htcs::SockAddrHtcs addr;
|
||||
addr.family = htcs::HTCS_AF_HTCS;
|
||||
addr.peer_name = htcs::GetPeerNameAny();
|
||||
addr.port_name = m_port_name;
|
||||
|
||||
/* Bind. */
|
||||
if (htcs::Bind(fd, std::addressof(addr)) == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Listen on our port. */
|
||||
while (htcs::Listen(fd, 0) == 0) {
|
||||
/* Continue accepting clients, so long as we can. */
|
||||
int client_fd;
|
||||
while (true) {
|
||||
/* Try to accept a client. */
|
||||
if (client_fd = htcs::Accept(fd, std::addressof(addr)); client_fd < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Handle the client. */
|
||||
m_on_socket_accepted(client_fd);
|
||||
}
|
||||
|
||||
/* NOTE: This seems unnecessary (client_fd guaranteed < 0 here), but Nintendo does it. */
|
||||
htcs::Close(client_fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t FileServerHtcsServer::Send(s32 desc, const void *buffer, size_t buffer_size, s32 flags) {
|
||||
AMS_ASSERT(m_mutex.IsLockedByCurrentThread());
|
||||
|
||||
return htcs::Send(desc, buffer, buffer_size, flags);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::tio {
|
||||
|
||||
using SocketAcceptedCallback = void(*)(s32 desc);
|
||||
|
||||
class FileServerHtcsServer {
|
||||
private:
|
||||
SocketAcceptedCallback m_on_socket_accepted;
|
||||
htcs::HtcsPortName m_port_name;
|
||||
os::ThreadType m_thread;
|
||||
os::SdkMutex m_mutex;
|
||||
public:
|
||||
constexpr FileServerHtcsServer() : m_on_socket_accepted(nullptr), m_port_name{}, m_thread{}, m_mutex{} { /* ... */ }
|
||||
private:
|
||||
static void ThreadEntry(void *arg) {
|
||||
static_cast<FileServerHtcsServer *>(arg)->ThreadFunc();
|
||||
}
|
||||
|
||||
void ThreadFunc();
|
||||
public:
|
||||
os::SdkMutex &GetMutex() { return m_mutex; }
|
||||
public:
|
||||
void Initialize(const char *port_name, void *thread_stack, size_t thread_stack_size, SocketAcceptedCallback on_socket_accepted);
|
||||
void Start();
|
||||
void Wait();
|
||||
|
||||
ssize_t Send(s32 desc, const void *buffer, size_t buffer_size, s32 flags);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::tio {
|
||||
|
||||
enum class PacketType : u32 {
|
||||
/* Control commands. */
|
||||
Connect = 0,
|
||||
Disconnect = 1,
|
||||
|
||||
/* Direct filesystem access. */
|
||||
CreateDirectory = 500,
|
||||
DeleteDirectory = 501,
|
||||
DeleteDirectoryRecursively = 502,
|
||||
OpenDirectory = 503,
|
||||
CloseDirectory = 504,
|
||||
RenameDirectory = 505,
|
||||
CreateFile = 506,
|
||||
DeleteFile = 507,
|
||||
OpenFile = 508,
|
||||
FlushFile = 509,
|
||||
CloseFile = 510,
|
||||
RenameFile = 511,
|
||||
ReadFile = 512,
|
||||
WriteFile = 513,
|
||||
GetEntryType = 514,
|
||||
ReadDirectory = 515,
|
||||
GetFileSize = 516,
|
||||
SetFileSize = 517,
|
||||
GetTotalSpaceSize = 518,
|
||||
GetFreeSpaceSize = 519,
|
||||
|
||||
/* Utilities. */
|
||||
Stat = 1000,
|
||||
ListDirectory = 1001,
|
||||
};
|
||||
|
||||
struct FileServerRequestHeader {
|
||||
u64 request_id;
|
||||
PacketType packet_type;
|
||||
u32 body_size;
|
||||
};
|
||||
|
||||
struct FileServerResponseHeader {
|
||||
u64 request_id;
|
||||
Result result;
|
||||
u32 body_size;
|
||||
};
|
||||
static_assert(sizeof(FileServerRequestHeader) == sizeof(FileServerResponseHeader));
|
||||
|
||||
struct CreateDirectoryParam {
|
||||
u32 path_len;
|
||||
char path[];
|
||||
};
|
||||
|
||||
struct DeleteDirectoryParam {
|
||||
u32 path_len;
|
||||
char path[];
|
||||
};
|
||||
|
||||
struct DeleteDirectoryRecursivelyParam {
|
||||
u32 path_len;
|
||||
char path[];
|
||||
};
|
||||
|
||||
struct OpenDirectoryParam {
|
||||
u32 path_len;
|
||||
fs::OpenDirectoryMode open_mode;
|
||||
char path[];
|
||||
};
|
||||
static_assert(sizeof(OpenDirectoryParam) == 0x8);
|
||||
|
||||
struct CloseDirectoryParam {
|
||||
u64 handle;
|
||||
};
|
||||
|
||||
struct RenameDirectoryParam {
|
||||
u32 old_len;
|
||||
u32 new_len;
|
||||
char data[];
|
||||
};
|
||||
|
||||
struct CreateFileParam {
|
||||
s64 size;
|
||||
u32 path_len;
|
||||
fs::CreateOption option;
|
||||
char path[];
|
||||
};
|
||||
static_assert(sizeof(CreateFileParam) == 0x10);
|
||||
|
||||
struct DeleteFileParam {
|
||||
u32 path_len;
|
||||
char path[];
|
||||
};
|
||||
|
||||
struct OpenFileParam {
|
||||
u32 path_len;
|
||||
fs::OpenMode mode;
|
||||
char path[];
|
||||
};
|
||||
static_assert(sizeof(OpenFileParam) == 0x8);
|
||||
|
||||
struct FlushFileParam {
|
||||
u64 handle;
|
||||
};
|
||||
|
||||
struct CloseFileParam {
|
||||
u64 handle;
|
||||
};
|
||||
|
||||
struct RenameFileParam {
|
||||
u32 old_len;
|
||||
u32 new_len;
|
||||
char data[];
|
||||
};
|
||||
|
||||
struct ReadFileParam {
|
||||
u64 handle;
|
||||
s64 offset;
|
||||
u64 size;
|
||||
fs::ReadOption option;
|
||||
};
|
||||
static_assert(sizeof(ReadFileParam) == 0x20);
|
||||
|
||||
struct WriteFileParam {
|
||||
u64 handle;
|
||||
s64 offset;
|
||||
u64 size;
|
||||
fs::WriteOption option;
|
||||
};
|
||||
static_assert(sizeof(WriteFileParam) == 0x20);
|
||||
|
||||
struct GetEntryTypeParam {
|
||||
u32 path_len;
|
||||
char path[];
|
||||
};
|
||||
|
||||
struct ReadDirectoryParam {
|
||||
u64 handle;
|
||||
s64 count;
|
||||
};
|
||||
|
||||
struct GetFileSizeParam {
|
||||
u64 handle;
|
||||
};
|
||||
|
||||
struct SetFileSizeParam {
|
||||
u64 handle;
|
||||
s64 size;
|
||||
};
|
||||
|
||||
struct GetTotalSpaceSizeParam {
|
||||
u32 path_len;
|
||||
char path[];
|
||||
};
|
||||
|
||||
struct GetFreeSpaceSizeParam {
|
||||
u32 path_len;
|
||||
char path[];
|
||||
};
|
||||
|
||||
struct StatParam {
|
||||
u32 path_len;
|
||||
char path[];
|
||||
};
|
||||
|
||||
struct ListDirectoryParam {
|
||||
u32 path_len;
|
||||
char path[];
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,735 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "tio_file_server_processor.hpp"
|
||||
|
||||
namespace ams::tio {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline int ProtocolVersion = 1;
|
||||
|
||||
}
|
||||
|
||||
void FileServerProcessor::Unmount() {
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Close all our directories. */
|
||||
if (m_open_directory_count > 0) {
|
||||
for (size_t i = 0; i < util::size(m_directories); ++i) {
|
||||
if (m_directories[i].handle != nullptr) {
|
||||
fs::CloseDirectory(m_directories[i]);
|
||||
m_directories[i] = {};
|
||||
--m_open_directory_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
AMS_ABORT_UNLESS(m_open_directory_count == 0);
|
||||
|
||||
/* Close all our files. */
|
||||
if (m_open_file_count > 0) {
|
||||
for (size_t i = 0; i < util::size(m_files); ++i) {
|
||||
if (m_files[i].handle != nullptr) {
|
||||
fs::CloseFile(m_files[i]);
|
||||
m_files[i] = {};
|
||||
--m_open_file_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
AMS_ABORT_UNLESS(m_open_file_count == 0);
|
||||
|
||||
/* If we're mounted, unmount the sd card. */
|
||||
if (m_is_mounted) {
|
||||
m_is_mounted = false;
|
||||
fs::Unmount("sd");
|
||||
}
|
||||
}
|
||||
|
||||
bool FileServerProcessor::ProcessRequest(FileServerRequestHeader *header, u8 *body, int socket) {
|
||||
/* Declare a response header for us to use. */
|
||||
FileServerResponseHeader response_header = {
|
||||
.request_id = header->request_id,
|
||||
.result = ResultSuccess(),
|
||||
.body_size = 0,
|
||||
};
|
||||
|
||||
/* Handle the special control commands. */
|
||||
if (header->packet_type == PacketType::Connect) {
|
||||
/* If the SD card isn't already mounted, try to mount it. */
|
||||
if (!m_is_mounted) {
|
||||
/* Mount the sd card. */
|
||||
m_is_mounted = !fs::ResultSdCardAccessFailed::Includes(fs::MountSdCard("sd"));
|
||||
|
||||
/* Prepare the response. */
|
||||
char *response_body = reinterpret_cast<char *>(body);
|
||||
util::SNPrintf(response_body, 0x100, "{\"bufferSize\":%zu, \"sdcardMounted\":%s, \"sdcardInserted\":%s, \"version\":%d}",
|
||||
m_request_buffer_size,
|
||||
m_is_mounted ? "true" : "false",
|
||||
m_is_inserted ? "true" : "false",
|
||||
ProtocolVersion);
|
||||
|
||||
/* Get the response length. */
|
||||
response_header.body_size = std::strlen(response_body);
|
||||
}
|
||||
|
||||
return this->SendResponse(response_header, body, socket);
|
||||
} else if (header->packet_type == PacketType::Disconnect) {
|
||||
/* If we need to, unmount the sd card. */
|
||||
if (m_is_mounted) {
|
||||
this->Unmount();
|
||||
}
|
||||
|
||||
/* Send the response. */
|
||||
return this->SendResponse(response_header, body, socket);
|
||||
}
|
||||
|
||||
/* The SD card must be inserted and mounted for us to process requests. */
|
||||
if (m_is_inserted && m_is_mounted) {
|
||||
switch (header->packet_type) {
|
||||
case PacketType::CreateDirectory:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto *param = reinterpret_cast<const CreateDirectoryParam *>(body);
|
||||
if (header->body_size != sizeof(*param) + param->path_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Create the directory. */
|
||||
response_header.result = fs::CreateDirectory(param->path);
|
||||
}
|
||||
break;
|
||||
case PacketType::DeleteDirectory:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto *param = reinterpret_cast<const DeleteDirectoryParam *>(body);
|
||||
if (header->body_size != sizeof(*param) + param->path_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Delete the directory. */
|
||||
response_header.result = fs::DeleteDirectory(param->path);
|
||||
}
|
||||
break;
|
||||
case PacketType::DeleteDirectoryRecursively:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto *param = reinterpret_cast<const DeleteDirectoryRecursivelyParam *>(body);
|
||||
if (header->body_size != sizeof(*param) + param->path_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Delete the directory. */
|
||||
response_header.result = fs::DeleteDirectoryRecursively(param->path);
|
||||
}
|
||||
break;
|
||||
case PacketType::OpenDirectory:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto *param = reinterpret_cast<const OpenDirectoryParam *>(body);
|
||||
if (header->body_size != sizeof(*param) + param->path_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Open the directory. */
|
||||
fs::DirectoryHandle handle;
|
||||
response_header.result = fs::OpenDirectory(std::addressof(handle), param->path, param->open_mode);
|
||||
if (R_SUCCEEDED(response_header.result)) {
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
if (m_open_directory_count < util::size(m_directories)) {
|
||||
/* Insert the directory into our table. */
|
||||
u64 index = std::numeric_limits<u64>::max();
|
||||
for (size_t i = 0; i < util::size(m_directories); ++i) {
|
||||
if (m_directories[i].handle == nullptr) {
|
||||
m_directories[i] = handle;
|
||||
index = i;
|
||||
++m_open_directory_count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
AMS_ABORT_UNLESS(index < util::size(m_directories));
|
||||
|
||||
/* Return the index. */
|
||||
response_header.body_size = sizeof(index);
|
||||
std::memcpy(body, std::addressof(index), sizeof(index));
|
||||
} else {
|
||||
/* We can't actually open the directory. */
|
||||
fs::CloseDirectory(handle);
|
||||
|
||||
response_header.result = fs::ResultOpenCountLimit();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PacketType::CloseDirectory:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto *param = reinterpret_cast<const CloseDirectoryParam *>(body);
|
||||
if (header->body_size != sizeof(*param)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Check that the directory handle is valid. */
|
||||
if (param->handle >= util::size(m_directories) || m_directories[param->handle].handle == nullptr) {
|
||||
response_header.result = fs::ResultDataCorrupted();
|
||||
break;
|
||||
}
|
||||
|
||||
/* Lock the filesystem. */
|
||||
std::scoped_lock lk2(m_fs_mutex);
|
||||
|
||||
/* Close the directory. */
|
||||
fs::CloseDirectory(m_directories[param->handle]);
|
||||
m_directories[param->handle].handle = {};
|
||||
--m_open_directory_count;
|
||||
}
|
||||
break;
|
||||
case PacketType::RenameDirectory:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto *param = reinterpret_cast<const RenameDirectoryParam *>(body);
|
||||
if (header->body_size != sizeof(*param) + param->old_len + param->new_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Delete the directory. */
|
||||
const char *old_path = param->data + 0;
|
||||
const char *new_path = param->data + param->old_len;
|
||||
response_header.result = fs::RenameDirectory(old_path, new_path);
|
||||
}
|
||||
break;
|
||||
case PacketType::CreateFile:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto *param = reinterpret_cast<const CreateFileParam *>(body);
|
||||
if (header->body_size != sizeof(*param) + param->path_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Create the file. */
|
||||
response_header.result = fs::CreateFile(param->path, param->size, static_cast<int>(param->option));
|
||||
}
|
||||
break;
|
||||
case PacketType::DeleteFile:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto *param = reinterpret_cast<const DeleteFileParam *>(body);
|
||||
if (header->body_size != sizeof(*param) + param->path_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Delete the file. */
|
||||
response_header.result = fs::DeleteFile(param->path);
|
||||
}
|
||||
break;
|
||||
case PacketType::OpenFile:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto *param = reinterpret_cast<const OpenFileParam *>(body);
|
||||
if (header->body_size != sizeof(*param) + param->path_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Open the file. */
|
||||
fs::FileHandle handle;
|
||||
response_header.result = fs::OpenFile(std::addressof(handle), param->path, param->mode);
|
||||
if (R_SUCCEEDED(response_header.result)) {
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
if (m_open_file_count < util::size(m_files)) {
|
||||
/* Insert the file into our table. */
|
||||
u64 index = std::numeric_limits<u64>::max();
|
||||
for (size_t i = 0; i < util::size(m_files); ++i) {
|
||||
if (m_files[i].handle == nullptr) {
|
||||
m_files[i] = handle;
|
||||
index = i;
|
||||
++m_open_file_count;
|
||||
break;
|
||||
}
|
||||
}
|
||||
AMS_ABORT_UNLESS(index < util::size(m_files));
|
||||
|
||||
/* Return the index. */
|
||||
response_header.body_size = sizeof(index);
|
||||
std::memcpy(body, std::addressof(index), sizeof(index));
|
||||
} else {
|
||||
/* We can't actually open the file. */
|
||||
fs::CloseFile(handle);
|
||||
|
||||
response_header.result = fs::ResultOpenCountLimit();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PacketType::FlushFile:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto *param = reinterpret_cast<const FlushFileParam *>(body);
|
||||
if (header->body_size != sizeof(*param)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Check that the file handle is valid. */
|
||||
if (param->handle >= util::size(m_files) || m_files[param->handle].handle == nullptr) {
|
||||
response_header.result = fs::ResultDataCorrupted();
|
||||
break;
|
||||
}
|
||||
|
||||
/* Lock the filesystem. */
|
||||
std::scoped_lock lk2(m_fs_mutex);
|
||||
|
||||
/* Flush the file. */
|
||||
response_header.result = fs::FlushFile(m_files[param->handle]);
|
||||
}
|
||||
break;
|
||||
case PacketType::CloseFile:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto *param = reinterpret_cast<const CloseFileParam *>(body);
|
||||
if (header->body_size != sizeof(*param)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Check that the file handle is valid. */
|
||||
if (param->handle >= util::size(m_files) || m_files[param->handle].handle == nullptr) {
|
||||
response_header.result = fs::ResultDataCorrupted();
|
||||
break;
|
||||
}
|
||||
|
||||
/* Lock the filesystem. */
|
||||
std::scoped_lock lk2(m_fs_mutex);
|
||||
|
||||
/* Close the directory. */
|
||||
fs::CloseFile(m_files[param->handle]);
|
||||
m_files[param->handle].handle = {};
|
||||
--m_open_file_count;
|
||||
}
|
||||
break;
|
||||
case PacketType::RenameFile:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto *param = reinterpret_cast<const RenameFileParam *>(body);
|
||||
if (header->body_size != sizeof(*param) + param->old_len + param->new_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Delete the directory. */
|
||||
const char *old_path = param->data + 0;
|
||||
const char *new_path = param->data + param->old_len;
|
||||
response_header.result = fs::RenameFile(old_path, new_path);
|
||||
}
|
||||
break;
|
||||
case PacketType::ReadFile:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto param = *reinterpret_cast<const ReadFileParam *>(body);
|
||||
if (header->body_size != sizeof(param)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check that the read is valid. */
|
||||
if (param.size + sizeof(u64) > m_request_buffer_size) {
|
||||
response_header.result = fs::ResultDataCorrupted();
|
||||
break;
|
||||
}
|
||||
|
||||
/* Prepare response variables. */
|
||||
u64 *out_size = reinterpret_cast<u64 *>(body);
|
||||
void *dst = out_size + 1;
|
||||
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Check that the file handle is valid. */
|
||||
if (param.handle >= util::size(m_files) || m_files[param.handle].handle == nullptr) {
|
||||
response_header.result = fs::ResultDataCorrupted();
|
||||
break;
|
||||
}
|
||||
|
||||
/* Read the file. */
|
||||
size_t read_size;
|
||||
response_header.result = fs::ReadFile(std::addressof(read_size), m_files[param.handle], param.offset, dst, param.size);
|
||||
|
||||
if (R_SUCCEEDED(response_header.result)) {
|
||||
*out_size = read_size;
|
||||
response_header.body_size = sizeof(u64) + read_size;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PacketType::WriteFile:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto *param = reinterpret_cast<const WriteFileParam *>(body);
|
||||
if (header->body_size != sizeof(*param) + param->size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Check that the file handle is valid. */
|
||||
if (param->handle >= util::size(m_files) || m_files[param->handle].handle == nullptr) {
|
||||
response_header.result = fs::ResultDataCorrupted();
|
||||
break;
|
||||
}
|
||||
|
||||
/* Lock the filesystem. */
|
||||
std::scoped_lock lk2(m_fs_mutex);
|
||||
|
||||
/* Write the file. */
|
||||
response_header.result = fs::WriteFile(m_files[param->handle], param->offset, body + sizeof(*param), param->size, param->option);
|
||||
}
|
||||
break;
|
||||
case PacketType::GetEntryType:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto *param = reinterpret_cast<const GetEntryTypeParam *>(body);
|
||||
if (header->body_size != sizeof(*param) + param->path_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Get the entry type. */
|
||||
fs::DirectoryEntryType type;
|
||||
response_header.result = fs::GetEntryType(std::addressof(type), param->path);
|
||||
|
||||
if (R_SUCCEEDED(response_header.result)) {
|
||||
/* Return the type. */
|
||||
response_header.body_size = sizeof(type);
|
||||
std::memcpy(body, std::addressof(type), sizeof(type));
|
||||
|
||||
static_assert(sizeof(type) == sizeof(u32));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PacketType::ReadDirectory:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto param = *reinterpret_cast<const ReadDirectoryParam *>(body);
|
||||
if (header->body_size != sizeof(param)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check that the read is valid. */
|
||||
if (sizeof(s64) + param.count * sizeof(fs::DirectoryEntry) > m_request_buffer_size) {
|
||||
response_header.result = fs::ResultDataCorrupted();
|
||||
break;
|
||||
}
|
||||
|
||||
/* Prepare response variables. */
|
||||
s64 *out_count = reinterpret_cast<s64 *>(body);
|
||||
fs::DirectoryEntry *dst = reinterpret_cast<fs::DirectoryEntry *>(out_count + 1);
|
||||
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Check that the directory handle is valid. */
|
||||
if (param.handle >= util::size(m_directories) || m_directories[param.handle].handle == nullptr) {
|
||||
response_header.result = fs::ResultDataCorrupted();
|
||||
break;
|
||||
}
|
||||
|
||||
/* Read the directory. */
|
||||
response_header.result = fs::ReadDirectory(out_count, dst, m_directories[param.handle], param.count);
|
||||
|
||||
if (R_SUCCEEDED(response_header.result)) {
|
||||
response_header.body_size = sizeof(s64) + *out_count * sizeof(fs::DirectoryEntry);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PacketType::GetFileSize:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto *param = reinterpret_cast<const GetFileSizeParam *>(body);
|
||||
if (header->body_size != sizeof(*param)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Check that the file handle is valid. */
|
||||
if (param->handle >= util::size(m_files) || m_files[param->handle].handle == nullptr) {
|
||||
response_header.result = fs::ResultDataCorrupted();
|
||||
break;
|
||||
}
|
||||
|
||||
/* Get the file size. */
|
||||
response_header.result = fs::GetFileSize(reinterpret_cast<s64 *>(body), m_files[param->handle]);
|
||||
|
||||
if (R_SUCCEEDED(response_header.result)) {
|
||||
response_header.body_size = sizeof(s64);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PacketType::SetFileSize:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto *param = reinterpret_cast<const SetFileSizeParam *>(body);
|
||||
if (header->body_size != sizeof(*param)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Lock ourselves. */
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
/* Check that the file handle is valid. */
|
||||
if (param->handle >= util::size(m_files) || m_files[param->handle].handle == nullptr) {
|
||||
response_header.result = fs::ResultDataCorrupted();
|
||||
break;
|
||||
}
|
||||
|
||||
/* Lock the filesystem. */
|
||||
std::scoped_lock lk2(m_fs_mutex);
|
||||
|
||||
/* Get the file size. */
|
||||
response_header.result = fs::SetFileSize(m_files[param->handle], param->size);
|
||||
}
|
||||
break;
|
||||
case PacketType::GetTotalSpaceSize:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto *param = reinterpret_cast<const GetTotalSpaceSizeParam *>(body);
|
||||
if (header->body_size != sizeof(*param) + param->path_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Get the total space size. */
|
||||
s64 size;
|
||||
response_header.result = fs::GetTotalSpaceSize(std::addressof(size), param->path);
|
||||
|
||||
if (R_SUCCEEDED(response_header.result)) {
|
||||
/* Return the size. */
|
||||
response_header.body_size = sizeof(size);
|
||||
std::memcpy(body, std::addressof(size), sizeof(size));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PacketType::GetFreeSpaceSize:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto *param = reinterpret_cast<const GetFreeSpaceSizeParam *>(body);
|
||||
if (header->body_size != sizeof(*param) + param->path_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Get the free space size. */
|
||||
s64 size;
|
||||
response_header.result = fs::GetFreeSpaceSize(std::addressof(size), param->path);
|
||||
|
||||
if (R_SUCCEEDED(response_header.result)) {
|
||||
/* Return the size. */
|
||||
response_header.body_size = sizeof(size);
|
||||
std::memcpy(body, std::addressof(size), sizeof(size));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PacketType::Stat:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto *param = reinterpret_cast<const StatParam *>(body);
|
||||
if (header->body_size != sizeof(*param) + param->path_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Prepare a response stat structure. */
|
||||
struct {
|
||||
fs::DirectoryEntryType type;
|
||||
s64 file_size;
|
||||
fs::FileTimeStampRaw file_timestamp;
|
||||
} out = {};
|
||||
static_assert(sizeof(out) == 0x30);
|
||||
|
||||
/* Get the entry type. */
|
||||
response_header.result = fs::GetEntryType(std::addressof(out.type), param->path);
|
||||
if (R_FAILED(response_header.result)) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* If the path is a file, get further information. */
|
||||
if (out.type == fs::DirectoryEntryType_File) {
|
||||
/* Try to get the file size. */
|
||||
{
|
||||
fs::FileHandle handle;
|
||||
const auto open_result = fs::OpenFile(std::addressof(handle), param->path, fs::OpenMode_Read);
|
||||
if (R_SUCCEEDED(open_result)) {
|
||||
ON_SCOPE_EXIT { fs::CloseFile(handle); };
|
||||
|
||||
response_header.result = fs::GetFileSize(std::addressof(out.file_size), handle);
|
||||
if (R_FAILED(response_header.result)) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (fs::ResultTargetLocked::Includes(open_result)) {
|
||||
out.file_size = 0;
|
||||
} else {
|
||||
response_header.result = open_result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the file timestamp. */
|
||||
response_header.result = fs::impl::GetFileTimeStampRawForDebug(std::addressof(out.file_timestamp), param->path);
|
||||
if (R_FAILED(response_header.result)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we successfully got the stat information, send it as response. */
|
||||
if (R_SUCCEEDED(response_header.result)) {
|
||||
response_header.body_size = sizeof(out);
|
||||
std::memcpy(body, std::addressof(out), sizeof(out));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PacketType::ListDirectory:
|
||||
{
|
||||
/* Get the parameters. */
|
||||
const auto *param = reinterpret_cast<const ListDirectoryParam *>(body);
|
||||
if (header->body_size != sizeof(*param) + param->path_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Open the directory. */
|
||||
fs::DirectoryHandle handle;
|
||||
response_header.result = fs::OpenDirectory(std::addressof(handle), param->path, fs::OpenDirectoryMode_All);
|
||||
if (R_FAILED(response_header.result)) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* When we're done, close the handle. */
|
||||
ON_SCOPE_EXIT { fs::CloseDirectory(handle); };
|
||||
|
||||
/* Get the directory entry count. */
|
||||
s64 count;
|
||||
response_header.result = fs::GetDirectoryEntryCount(std::addressof(count), handle);
|
||||
if (R_FAILED(response_header.result)) {
|
||||
break;
|
||||
}
|
||||
/* Determine whether we can send the response in one go. */
|
||||
const size_t needed_size = sizeof(s64) + sizeof(u64) + sizeof(fs::DirectoryEntry) * count;
|
||||
if (needed_size <= m_request_buffer_size) {
|
||||
/* We can perform the entire read in one send. */
|
||||
struct {
|
||||
s64 count;
|
||||
u64 size;
|
||||
fs::DirectoryEntry entries[];
|
||||
} *out = reinterpret_cast<decltype(out)>(body);
|
||||
|
||||
s64 read_count;
|
||||
response_header.result = fs::ReadDirectory(std::addressof(read_count), out->entries, handle, count);
|
||||
if (R_FAILED(response_header.result)) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set the output. */
|
||||
out->count = read_count;
|
||||
out->size = read_count * sizeof(fs::DirectoryEntry);
|
||||
|
||||
/* Set the response body size. */
|
||||
response_header.body_size = sizeof(*out) + out->size;
|
||||
} else {
|
||||
/* We have to use multiple sends. */
|
||||
/* Lock our server. */
|
||||
std::scoped_lock lk(m_htcs_server.GetMutex());
|
||||
|
||||
/* Send the response header. */
|
||||
response_header.body_size = needed_size;
|
||||
if (m_htcs_server.Send(socket, std::addressof(header), sizeof(header), 0) != sizeof(header)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Send the body header. */
|
||||
struct {
|
||||
s64 count;
|
||||
u64 size;
|
||||
} out = { count, count * sizeof(fs::DirectoryEntry) };
|
||||
if (m_htcs_server.Send(socket, std::addressof(out), sizeof(out), 0) != sizeof(out)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Loop sending entries. */
|
||||
s64 remaining = count;
|
||||
do {
|
||||
/* Determine how many entries we can read. */
|
||||
const s64 cur = std::min<s64>(remaining, static_cast<s64>(m_request_buffer_size / sizeof(fs::DirectoryEntry)));
|
||||
|
||||
/* NOTE: Nintendo does not check the output of this call. */
|
||||
s64 read_count = 0;
|
||||
fs::ReadDirectory(std::addressof(read_count), reinterpret_cast<fs::DirectoryEntry *>(body), handle, cur);
|
||||
|
||||
/* Send the current entries. */
|
||||
const ssize_t cur_size = read_count * sizeof(fs::DirectoryEntry);
|
||||
if (m_htcs_server.Send(socket, body, cur_size, 0) != cur_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Advance. */
|
||||
remaining -= read_count;
|
||||
} while (remaining > 0);
|
||||
|
||||
/* We've sent the entirety of our response, so early return. */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* Unsupported packet. */
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Send the response. */
|
||||
return this->SendResponse(response_header, body, socket);
|
||||
} else if (m_is_mounted) {
|
||||
/* The SD card is mounted but not inserted, so we should unmount it. */
|
||||
this->Unmount();
|
||||
}
|
||||
|
||||
/* We failed to process the request due to SD card not being inserted or mounted. */
|
||||
response_header.result = fs::ResultSdCardAccessFailed();
|
||||
|
||||
return this->SendResponse(response_header, body, socket);
|
||||
}
|
||||
|
||||
bool FileServerProcessor::SendResponse(const FileServerResponseHeader &header, const void *body, int socket) {
|
||||
/* Lock our server. */
|
||||
std::scoped_lock lk(m_htcs_server.GetMutex());
|
||||
|
||||
/* Send the response header. */
|
||||
if (m_htcs_server.Send(socket, std::addressof(header), sizeof(header), 0) != sizeof(header)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If we don't have a body, we're done. */
|
||||
if (header.body_size == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Send the body. */
|
||||
return m_htcs_server.Send(socket, body, header.body_size, 0) == header.body_size;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "tio_file_server_htcs_server.hpp"
|
||||
#include "tio_file_server_packet.hpp"
|
||||
|
||||
namespace ams::tio {
|
||||
|
||||
class FileServerProcessor {
|
||||
private:
|
||||
bool m_is_inserted{};
|
||||
bool m_is_mounted{};
|
||||
size_t m_request_buffer_size{};
|
||||
FileServerHtcsServer &m_htcs_server;
|
||||
size_t m_open_file_count{};
|
||||
size_t m_open_directory_count{};
|
||||
fs::FileHandle m_files[0x80]{};
|
||||
fs::DirectoryHandle m_directories[0x80]{};
|
||||
os::SdkMutex m_fs_mutex{};
|
||||
os::SdkMutex m_mutex{};
|
||||
public:
|
||||
constexpr FileServerProcessor(FileServerHtcsServer &htcs_server) : m_htcs_server(htcs_server) { /* ... */ }
|
||||
|
||||
void SetInserted(bool ins) { m_is_inserted = ins; }
|
||||
void SetRequestBufferSize(size_t size) { m_request_buffer_size = size; }
|
||||
public:
|
||||
bool ProcessRequest(FileServerRequestHeader *header, u8 *body, int socket);
|
||||
|
||||
void Unmount();
|
||||
private:
|
||||
bool SendResponse(const FileServerResponseHeader &header, const void *body, int socket);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "tio_file_server.hpp"
|
||||
|
||||
namespace ams {
|
||||
|
||||
namespace tio {
|
||||
|
||||
namespace {
|
||||
|
||||
alignas(0x40) constinit u8 g_fs_heap_buffer[64_KB];
|
||||
alignas(0x40) constinit u8 g_htcs_buffer[1_KB];
|
||||
lmem::HeapHandle g_fs_heap_handle;
|
||||
|
||||
void *AllocateForFs(size_t size) {
|
||||
return lmem::AllocateFromExpHeap(g_fs_heap_handle, size);
|
||||
}
|
||||
|
||||
void DeallocateForFs(void *p, size_t size) {
|
||||
AMS_UNUSED(size);
|
||||
|
||||
return lmem::FreeToExpHeap(g_fs_heap_handle, p);
|
||||
}
|
||||
|
||||
void InitializeFsHeap() {
|
||||
/* Setup fs allocator. */
|
||||
g_fs_heap_handle = lmem::CreateExpHeap(g_fs_heap_buffer, sizeof(g_fs_heap_buffer), lmem::CreateOption_ThreadSafe);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace init {
|
||||
|
||||
void InitializeSystemModule() {
|
||||
/* Initialize heap. */
|
||||
tio::InitializeFsHeap();
|
||||
|
||||
/* Initialize our connection to sm. */
|
||||
R_ABORT_UNLESS(sm::Initialize());
|
||||
|
||||
/* Initialize fs. */
|
||||
fs::InitializeForSystem();
|
||||
fs::SetAllocator(tio::AllocateForFs, tio::DeallocateForFs);
|
||||
fs::SetEnabledAutoAbort(false);
|
||||
|
||||
/* Verify that we can sanely execute. */
|
||||
ams::CheckApiVersion();
|
||||
}
|
||||
|
||||
void FinalizeSystemModule() { /* ... */ }
|
||||
|
||||
void Startup() { /* ... */ }
|
||||
|
||||
}
|
||||
|
||||
void Main() {
|
||||
/* Set thread name. */
|
||||
os::SetThreadNamePointer(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_NAME(TioServer, Main));
|
||||
AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(TioServer, Main));
|
||||
|
||||
/* Initialize htcs. */
|
||||
constexpr auto HtcsSocketCountMax = 2;
|
||||
const size_t buffer_size = htcs::GetWorkingMemorySize(HtcsSocketCountMax);
|
||||
AMS_ABORT_UNLESS(sizeof(tio::g_htcs_buffer) >= buffer_size);
|
||||
htcs::InitializeForSystem(tio::g_htcs_buffer, buffer_size, HtcsSocketCountMax);
|
||||
|
||||
/* Initialize the file server. */
|
||||
tio::InitializeFileServer();
|
||||
|
||||
/* Start the file server. */
|
||||
tio::StartFileServer();
|
||||
|
||||
/* Wait for the file server to finish. */
|
||||
tio::WaitFileServer();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "tio_sd_card_observer.hpp"
|
||||
|
||||
namespace ams::tio {
|
||||
|
||||
void SdCardObserver::Initialize(void *thread_stack, size_t thread_stack_size) {
|
||||
/* Setup our thread. */
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(m_thread), ThreadEntry, this, thread_stack, thread_stack_size, AMS_GET_SYSTEM_THREAD_PRIORITY(TioServer, SdCardObserver)));
|
||||
|
||||
/* Set our thread name pointer. */
|
||||
os::SetThreadNamePointer(std::addressof(m_thread), AMS_GET_SYSTEM_THREAD_NAME(TioServer, SdCardObserver));
|
||||
|
||||
/* Set our initial insertion state. */
|
||||
m_inserted = fs::IsSdCardInserted();
|
||||
}
|
||||
|
||||
void SdCardObserver::SetCallback(SdCardInsertionCallback callback) {
|
||||
/* Check that we don't already have a callback. */
|
||||
AMS_ABORT_UNLESS(m_callback == nullptr);
|
||||
|
||||
/* Set our callback. */
|
||||
m_callback = callback;
|
||||
}
|
||||
|
||||
void SdCardObserver::ThreadFunc() {
|
||||
/* Open detection event notifier. */
|
||||
std::unique_ptr<fs::IEventNotifier> notifier;
|
||||
R_ABORT_UNLESS(fs::OpenSdCardDetectionEventNotifier(std::addressof(notifier)));
|
||||
|
||||
/* Bind the detection event. */
|
||||
os::SystemEventType event;
|
||||
R_ABORT_UNLESS(notifier->BindEvent(std::addressof(event), os::EventClearMode_AutoClear));
|
||||
|
||||
/* Loop, waiting for insertion events. */
|
||||
while (true) {
|
||||
/* Wait for an event. */
|
||||
os::WaitSystemEvent(std::addressof(event));
|
||||
|
||||
/* Update our insertion state. */
|
||||
m_inserted = fs::IsSdCardInserted();
|
||||
|
||||
/* Invoke our callback. */
|
||||
if (m_callback) {
|
||||
m_callback(m_inserted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::tio {
|
||||
|
||||
using SdCardInsertionCallback = void(*)(bool inserted);
|
||||
|
||||
class SdCardObserver {
|
||||
private:
|
||||
bool m_inserted;
|
||||
SdCardInsertionCallback m_callback;
|
||||
os::ThreadType m_thread;
|
||||
public:
|
||||
constexpr SdCardObserver() : m_inserted(false), m_callback(nullptr), m_thread{} { /* ... */ }
|
||||
|
||||
bool IsSdCardInserted() const { return m_inserted; }
|
||||
private:
|
||||
static void ThreadEntry(void *arg) {
|
||||
static_cast<SdCardObserver *>(arg)->ThreadFunc();
|
||||
}
|
||||
|
||||
void ThreadFunc();
|
||||
public:
|
||||
void Initialize(void *thread_stack, size_t thread_stack_size);
|
||||
void SetCallback(SdCardInsertionCallback callback);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
# pull in common stratosphere sysmodule configuration
|
||||
#---------------------------------------------------------------------------------
|
||||
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE)))
|
||||
include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk
|
||||
|
||||
ATMOSPHERE_SYSTEM_MODULE_TARGETS := nsp
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(__RECURSIVE__),1)
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c)
|
||||
CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp)
|
||||
SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s)
|
||||
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
$(foreach dir,$(AMS_LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(ATMOSPHERE_BUILD_DIR)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) $(foreach dir,$(AMS_LIBDIRS),-L$(dir)/$(ATMOSPHERE_LIBRARY_DIR))
|
||||
|
||||
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
|
||||
|
||||
ifeq ($(strip $(CONFIG_JSON)),)
|
||||
jsons := $(wildcard *.json)
|
||||
ifneq (,$(findstring $(TARGET).json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/$(TARGET).json
|
||||
else
|
||||
ifneq (,$(findstring config.json,$(jsons)))
|
||||
export APP_JSON := $(TOPDIR)/config.json
|
||||
endif
|
||||
endif
|
||||
else
|
||||
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
|
||||
endif
|
||||
|
||||
.PHONY: clean all check_lib
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||
@$(MAKE) __RECURSIVE__=1 OUTPUT=$(CURDIR)/$(ATMOSPHERE_OUT_DIR)/$(TARGET) \
|
||||
DEPSDIR=$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) \
|
||||
--no-print-directory -C $(ATMOSPHERE_BUILD_DIR) \
|
||||
-f $(THIS_MAKEFILE)
|
||||
|
||||
$(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a: check_lib
|
||||
@$(SILENTCMD)echo "Checked library."
|
||||
|
||||
ifeq ($(ATMOSPHERE_CHECKED_LIBSTRATOSPHERE),1)
|
||||
check_lib:
|
||||
else
|
||||
check_lib:
|
||||
@$(MAKE) --no-print-directory -C $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere -f $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/libstratosphere.mk
|
||||
endif
|
||||
|
||||
$(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all : $(foreach target,$(ATMOSPHERE_SYSTEM_MODULE_TARGETS),$(OUTPUT).$(target))
|
||||
|
||||
$(OUTPUT).kip : $(OUTPUT).elf
|
||||
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
|
||||
$(OUTPUT).nso : $(OUTPUT).elf
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
$(OFILES) : $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||
|
||||
%.npdm : %.npdm.json
|
||||
@echo built ... $< $@
|
||||
@npdmtool $< $@
|
||||
@echo built ... $(notdir $@)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o : %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
||||
@@ -1,41 +0,0 @@
|
||||
ATMOSPHERE_BUILD_CONFIGS :=
|
||||
all: nx_release
|
||||
|
||||
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE)))
|
||||
|
||||
define ATMOSPHERE_ADD_TARGET
|
||||
|
||||
ATMOSPHERE_BUILD_CONFIGS += $(strip $1)
|
||||
|
||||
$(strip $1):
|
||||
@echo "Building $(strip $1)"
|
||||
@$$(MAKE) -f $(CURRENT_DIRECTORY)/system_module.mk ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
|
||||
|
||||
clean-$(strip $1):
|
||||
@echo "Cleaning $(strip $1)"
|
||||
@$$(MAKE) -f $(CURRENT_DIRECTORY)/system_module.mk clean ATMOSPHERE_MAKEFILE_TARGET="$(strip $1)" ATMOSPHERE_BUILD_NAME="$(strip $2)" ATMOSPHERE_BOARD="$(strip $3)" ATMOSPHERE_CPU="$(strip $4)" $(strip $5)
|
||||
|
||||
endef
|
||||
|
||||
define ATMOSPHERE_ADD_TARGETS
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_release, release, $(strip $2), $(strip $3), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $4)" \
|
||||
))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_debug, debug, $(strip $2), $(strip $3), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $4) -DAMS_BUILD_FOR_DEBUGGING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 \
|
||||
))
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGET, $(strip $1)_audit, audit, $(strip $2), $(strip $3), \
|
||||
ATMOSPHERE_BUILD_SETTINGS="$(strip $4) -DAMS_BUILD_FOR_AUDITING" ATMOSPHERE_BUILD_FOR_DEBUGGING=1 ATMOSPHERE_BUILD_FOR_AUDITING=1 \
|
||||
))
|
||||
|
||||
endef
|
||||
|
||||
$(eval $(call ATMOSPHERE_ADD_TARGETS, nx, nx-hac-001, arm-cortex-a57,))
|
||||
|
||||
clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config))
|
||||
|
||||
.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS), $(config) clean-$(config))
|
||||
@@ -1,86 +0,0 @@
|
||||
{
|
||||
"name": "ams.mitm",
|
||||
"title_id": "0x010041544D530000",
|
||||
"main_thread_stack_size": "0x20000",
|
||||
"main_thread_priority": 43,
|
||||
"default_cpu_id": 3,
|
||||
"process_category": 1,
|
||||
"use_secure_memory": true,
|
||||
"immortal": true,
|
||||
"kernel_capabilities": [
|
||||
{
|
||||
"type": "handle_table_size",
|
||||
"value": 512
|
||||
},
|
||||
{
|
||||
"type": "syscalls",
|
||||
"value": {
|
||||
"svcSetHeapSize": "0x01",
|
||||
"svcSetMemoryPermission": "0x02",
|
||||
"svcSetMemoryAttribute": "0x03",
|
||||
"svcMapMemory": "0x04",
|
||||
"svcUnmapMemory": "0x05",
|
||||
"svcQueryMemory": "0x06",
|
||||
"svcExitProcess": "0x07",
|
||||
"svcCreateThread": "0x08",
|
||||
"svcStartThread": "0x09",
|
||||
"svcExitThread": "0x0a",
|
||||
"svcSleepThread": "0x0b",
|
||||
"svcGetThreadPriority": "0x0c",
|
||||
"svcSetThreadPriority": "0x0d",
|
||||
"svcGetThreadCoreMask": "0x0e",
|
||||
"svcSetThreadCoreMask": "0x0f",
|
||||
"svcGetCurrentProcessorNumber": "0x10",
|
||||
"svcSignalEvent": "0x11",
|
||||
"svcClearEvent": "0x12",
|
||||
"svcMapSharedMemory": "0x13",
|
||||
"svcUnmapSharedMemory": "0x14",
|
||||
"svcCreateTransferMemory": "0x15",
|
||||
"svcCloseHandle": "0x16",
|
||||
"svcResetSignal": "0x17",
|
||||
"svcWaitSynchronization": "0x18",
|
||||
"svcCancelSynchronization": "0x19",
|
||||
"svcArbitrateLock": "0x1a",
|
||||
"svcArbitrateUnlock": "0x1b",
|
||||
"svcWaitProcessWideKeyAtomic": "0x1c",
|
||||
"svcSignalProcessWideKey": "0x1d",
|
||||
"svcGetSystemTick": "0x1e",
|
||||
"svcConnectToNamedPort": "0x1f",
|
||||
"svcSendSyncRequestLight": "0x20",
|
||||
"svcSendSyncRequest": "0x21",
|
||||
"svcSendSyncRequestWithUserBuffer": "0x22",
|
||||
"svcSendAsyncRequestWithUserBuffer": "0x23",
|
||||
"svcGetProcessId": "0x24",
|
||||
"svcGetThreadId": "0x25",
|
||||
"svcBreak": "0x26",
|
||||
"svcOutputDebugString": "0x27",
|
||||
"svcReturnFromException": "0x28",
|
||||
"svcGetInfo": "0x29",
|
||||
"svcWaitForAddress": "0x34",
|
||||
"svcSignalToAddress": "0x35",
|
||||
"svcSynchronizePreemptionState": "0x36",
|
||||
"svcCreateSession": "0x40",
|
||||
"svcAcceptSession": "0x41",
|
||||
"svcReplyAndReceiveLight": "0x42",
|
||||
"svcReplyAndReceive": "0x43",
|
||||
"svcReplyAndReceiveWithUserBuffer": "0x44",
|
||||
"svcCreateEvent": "0x45",
|
||||
"svcMapPhysicalMemoryUnsafe": "0x48",
|
||||
"svcUnmapPhysicalMemoryUnsafe": "0x49",
|
||||
"svcMapTransferMemory": "0x51",
|
||||
"svcUnmapTransferMemory": "0x52",
|
||||
"svcCreateInterruptEvent": "0x53",
|
||||
"svcReadWriteRegister": "0x4E",
|
||||
"svcQueryIoMapping": "0x55",
|
||||
"svcCreateDeviceAddressSpace": "0x56",
|
||||
"svcAttachDeviceAddressSpace": "0x57",
|
||||
"svcDetachDeviceAddressSpace": "0x58",
|
||||
"svcMapDeviceAddressSpaceAligned": "0x5a",
|
||||
"svcUnmapDeviceAddressSpace": "0x5c",
|
||||
"svcGetSystemInfo": "0x6f",
|
||||
"svcManageNamedPort": "0x71",
|
||||
"svcCallSecureMonitor": "0x7F"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "amsmitm_initialization.hpp"
|
||||
#include "amsmitm_fs_utils.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
using namespace ams::fs;
|
||||
|
||||
namespace {
|
||||
|
||||
/* Globals. */
|
||||
FsFileSystem g_sd_filesystem;
|
||||
|
||||
/* Helpers. */
|
||||
Result EnsureSdInitialized() {
|
||||
R_UNLESS(serviceIsActive(std::addressof(g_sd_filesystem.s)), ams::fs::ResultSdCardNotPresent());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void FormatAtmosphereRomfsPath(char *dst_path, size_t dst_path_size, ncm::ProgramId program_id, const char *src_path) {
|
||||
return FormatAtmosphereSdPath(dst_path, dst_path_size, program_id, "romfs", src_path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void OpenGlobalSdCardFileSystem() {
|
||||
R_ABORT_UNLESS(fsOpenSdCardFileSystem(std::addressof(g_sd_filesystem)));
|
||||
}
|
||||
|
||||
Result CreateSdFile(const char *path, s64 size, s32 option) {
|
||||
R_TRY(EnsureSdInitialized());
|
||||
R_RETURN(fsFsCreateFile(std::addressof(g_sd_filesystem), path, size, option));
|
||||
}
|
||||
|
||||
Result DeleteSdFile(const char *path) {
|
||||
R_TRY(EnsureSdInitialized());
|
||||
R_RETURN(fsFsDeleteFile(std::addressof(g_sd_filesystem), path));
|
||||
}
|
||||
|
||||
bool HasSdFile(const char *path) {
|
||||
if (R_FAILED(EnsureSdInitialized())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FsDirEntryType type;
|
||||
if (R_FAILED(fsFsGetEntryType(std::addressof(g_sd_filesystem), path, std::addressof(type)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return type == FsDirEntryType_File;
|
||||
}
|
||||
|
||||
bool HasAtmosphereSdFile(const char *path) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
|
||||
return HasSdFile(fixed_path);
|
||||
}
|
||||
|
||||
Result DeleteAtmosphereSdFile(const char *path) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
|
||||
R_RETURN(DeleteSdFile(fixed_path));
|
||||
}
|
||||
|
||||
Result CreateAtmosphereSdFile(const char *path, s64 size, s32 option) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
|
||||
R_RETURN(CreateSdFile(fixed_path, size, option));
|
||||
}
|
||||
|
||||
Result OpenSdFile(FsFile *out, const char *path, u32 mode) {
|
||||
R_TRY(EnsureSdInitialized());
|
||||
R_RETURN(fsFsOpenFile(std::addressof(g_sd_filesystem), path, mode, out));
|
||||
}
|
||||
|
||||
Result OpenAtmosphereSdFile(FsFile *out, const char *path, u32 mode) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
|
||||
R_RETURN(OpenSdFile(out, fixed_path, mode));
|
||||
}
|
||||
|
||||
Result OpenAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||
R_RETURN(OpenSdFile(out, fixed_path, mode));
|
||||
}
|
||||
|
||||
Result OpenAtmosphereSdRomfsFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereRomfsPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||
R_RETURN(OpenSdFile(out, fixed_path, mode));
|
||||
}
|
||||
|
||||
Result OpenAtmosphereRomfsFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode, FsFileSystem *fs) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereRomfsPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||
R_RETURN(fsFsOpenFile(fs, fixed_path, mode, out));
|
||||
}
|
||||
|
||||
Result CreateSdDirectory(const char *path) {
|
||||
R_TRY(EnsureSdInitialized());
|
||||
R_RETURN(fsFsCreateDirectory(std::addressof(g_sd_filesystem), path));
|
||||
}
|
||||
|
||||
Result CreateAtmosphereSdDirectory(const char *path) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
|
||||
R_RETURN(CreateSdDirectory(fixed_path));
|
||||
}
|
||||
|
||||
Result OpenSdDirectory(FsDir *out, const char *path, u32 mode) {
|
||||
R_TRY(EnsureSdInitialized());
|
||||
R_RETURN(fsFsOpenDirectory(std::addressof(g_sd_filesystem), path, mode, out));
|
||||
}
|
||||
|
||||
Result OpenAtmosphereSdDirectory(FsDir *out, const char *path, u32 mode) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
|
||||
R_RETURN(OpenSdDirectory(out, fixed_path, mode));
|
||||
}
|
||||
|
||||
Result OpenAtmosphereSdDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||
R_RETURN(OpenSdDirectory(out, fixed_path, mode));
|
||||
}
|
||||
|
||||
Result OpenAtmosphereSdRomfsDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereRomfsPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||
R_RETURN(OpenSdDirectory(out, fixed_path, mode));
|
||||
}
|
||||
|
||||
Result OpenAtmosphereRomfsDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode, FsFileSystem *fs) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereRomfsPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||
R_RETURN(fsFsOpenDirectory(fs, fixed_path, mode, out));
|
||||
}
|
||||
|
||||
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, const char *src_path) {
|
||||
if (src_path[0] == '/') {
|
||||
util::SNPrintf(dst_path, dst_path_size, "/atmosphere%s", src_path);
|
||||
} else {
|
||||
util::SNPrintf(dst_path, dst_path_size, "/atmosphere/%s", src_path);
|
||||
}
|
||||
}
|
||||
|
||||
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, const char *subdir, const char *src_path) {
|
||||
if (src_path[0] == '/') {
|
||||
util::SNPrintf(dst_path, dst_path_size, "/atmosphere/%s%s", subdir, src_path);
|
||||
} else {
|
||||
util::SNPrintf(dst_path, dst_path_size, "/atmosphere/%s/%s", subdir, src_path);
|
||||
}
|
||||
}
|
||||
|
||||
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, ncm::ProgramId program_id, const char *src_path) {
|
||||
if (src_path[0] == '/') {
|
||||
util::SNPrintf(dst_path, dst_path_size, "/atmosphere/contents/%016lx%s", static_cast<u64>(program_id), src_path);
|
||||
} else {
|
||||
util::SNPrintf(dst_path, dst_path_size, "/atmosphere/contents/%016lx/%s", static_cast<u64>(program_id), src_path);
|
||||
}
|
||||
}
|
||||
|
||||
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, ncm::ProgramId program_id, const char *subdir, const char *src_path) {
|
||||
if (src_path[0] == '/') {
|
||||
util::SNPrintf(dst_path, dst_path_size, "/atmosphere/contents/%016lx/%s%s", static_cast<u64>(program_id), subdir, src_path);
|
||||
} else {
|
||||
util::SNPrintf(dst_path, dst_path_size, "/atmosphere/contents/%016lx/%s/%s", static_cast<u64>(program_id), subdir, src_path);
|
||||
}
|
||||
}
|
||||
|
||||
bool HasSdRomfsContent(ncm::ProgramId program_id) {
|
||||
/* Check if romfs.bin is present. */
|
||||
{
|
||||
FsFile romfs_file;
|
||||
if (R_SUCCEEDED(OpenAtmosphereSdFile(std::addressof(romfs_file), program_id, "romfs.bin", OpenMode_Read))) {
|
||||
fsFileClose(std::addressof(romfs_file));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for romfs folder with content. */
|
||||
FsDir romfs_dir;
|
||||
if (R_FAILED(OpenAtmosphereSdRomfsDirectory(std::addressof(romfs_dir), program_id, "", fs::OpenDirectoryMode_All))) {
|
||||
return false;
|
||||
}
|
||||
ON_SCOPE_EXIT { fsDirClose(std::addressof(romfs_dir)); };
|
||||
|
||||
/* Verify the folder has at least one entry. */
|
||||
s64 num_entries = 0;
|
||||
return R_SUCCEEDED(fsDirGetEntryCount(std::addressof(romfs_dir), std::addressof(num_entries))) && num_entries > 0;
|
||||
}
|
||||
|
||||
Result SaveAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, void *data, size_t size) {
|
||||
R_TRY(EnsureSdInitialized());
|
||||
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||
|
||||
/* Unconditionally create. */
|
||||
/* Don't check error, as a failure here should be okay. */
|
||||
FsFile f;
|
||||
fsFsCreateFile(std::addressof(g_sd_filesystem), fixed_path, size, 0);
|
||||
|
||||
/* Try to open. */
|
||||
R_TRY(fsFsOpenFile(std::addressof(g_sd_filesystem), fixed_path, OpenMode_ReadWrite, std::addressof(f)));
|
||||
auto file_guard = SCOPE_GUARD { fsFileClose(std::addressof(f)); };
|
||||
|
||||
/* Try to set the size. */
|
||||
R_TRY(fsFileSetSize(std::addressof(f), static_cast<s64>(size)));
|
||||
|
||||
/* Try to write data. */
|
||||
R_TRY(fsFileWrite(std::addressof(f), 0, data, size, FsWriteOption_Flush));
|
||||
|
||||
/* Set output. */
|
||||
file_guard.Cancel();
|
||||
*out = f;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CreateAndOpenAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, size_t size) {
|
||||
R_TRY(EnsureSdInitialized());
|
||||
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||
|
||||
/* Unconditionally create. */
|
||||
/* Don't check error, as a failure here should be okay. */
|
||||
FsFile f;
|
||||
fsFsCreateFile(std::addressof(g_sd_filesystem), fixed_path, size, 0);
|
||||
|
||||
/* Try to open. */
|
||||
R_TRY(fsFsOpenFile(std::addressof(g_sd_filesystem), fixed_path, OpenMode_ReadWrite, std::addressof(f)));
|
||||
auto file_guard = SCOPE_GUARD { fsFileClose(std::addressof(f)); };
|
||||
|
||||
/* Try to set the size. */
|
||||
R_TRY(fsFileSetSize(std::addressof(f), static_cast<s64>(size)));
|
||||
|
||||
/* Set output. */
|
||||
file_guard.Cancel();
|
||||
*out = f;
|
||||
R_SUCCEED();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
/* Initialization. */
|
||||
void OpenGlobalSdCardFileSystem();
|
||||
|
||||
/* Utilities. */
|
||||
Result DeleteAtmosphereSdFile(const char *path);
|
||||
Result CreateSdFile(const char *path, s64 size, s32 option);
|
||||
Result CreateAtmosphereSdFile(const char *path, s64 size, s32 option);
|
||||
Result OpenSdFile(FsFile *out, const char *path, u32 mode);
|
||||
Result OpenAtmosphereSdFile(FsFile *out, const char *path, u32 mode);
|
||||
Result OpenAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode);
|
||||
Result OpenAtmosphereSdRomfsFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode);
|
||||
Result OpenAtmosphereRomfsFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode, FsFileSystem *fs);
|
||||
|
||||
bool HasSdFile(const char *path);
|
||||
bool HasAtmosphereSdFile(const char *path);
|
||||
|
||||
Result CreateSdDirectory(const char *path);
|
||||
Result CreateAtmosphereSdDirectory(const char *path);
|
||||
Result OpenSdDirectory(FsDir *out, const char *path, u32 mode);
|
||||
Result OpenAtmosphereSdDirectory(FsDir *out, const char *path, u32 mode);
|
||||
Result OpenAtmosphereSdDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode);
|
||||
Result OpenAtmosphereSdRomfsDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode);
|
||||
Result OpenAtmosphereRomfsDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode, FsFileSystem *fs);
|
||||
|
||||
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, const char *src_path);
|
||||
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, const char *subdir, const char *src_path);
|
||||
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, ncm::ProgramId program_id, const char *src_path);
|
||||
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, ncm::ProgramId program_id, const char *subdir, const char *src_path);
|
||||
|
||||
bool HasSdRomfsContent(ncm::ProgramId program_id);
|
||||
|
||||
Result SaveAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, void *data, size_t size);
|
||||
Result CreateAndOpenAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, size_t size);
|
||||
|
||||
/* NOTE: Implemented in fs.mitm logic. */
|
||||
bool HasSdManualHtmlContent(ncm::ProgramId program_id);
|
||||
|
||||
}
|
||||
@@ -1,245 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "amsmitm_initialization.hpp"
|
||||
#include "amsmitm_fs_utils.hpp"
|
||||
#include "amsmitm_prodinfo_utils.hpp"
|
||||
#include "bpc_mitm/bpc_ams_power_utils.hpp"
|
||||
#include "set_mitm/settings_sd_kvs.hpp"
|
||||
|
||||
namespace ams::mitm {
|
||||
|
||||
namespace {
|
||||
|
||||
/* BIS key sources. */
|
||||
constexpr u8 BisKeySources[4][2][0x10] = {
|
||||
{
|
||||
{0xF8, 0x3F, 0x38, 0x6E, 0x2C, 0xD2, 0xCA, 0x32, 0xA8, 0x9A, 0xB9, 0xAA, 0x29, 0xBF, 0xC7, 0x48},
|
||||
{0x7D, 0x92, 0xB0, 0x3A, 0xA8, 0xBF, 0xDE, 0xE1, 0xA7, 0x4C, 0x3B, 0x6E, 0x35, 0xCB, 0x71, 0x06}
|
||||
},
|
||||
{
|
||||
{0x41, 0x00, 0x30, 0x49, 0xDD, 0xCC, 0xC0, 0x65, 0x64, 0x7A, 0x7E, 0xB4, 0x1E, 0xED, 0x9C, 0x5F},
|
||||
{0x44, 0x42, 0x4E, 0xDA, 0xB4, 0x9D, 0xFC, 0xD9, 0x87, 0x77, 0x24, 0x9A, 0xDC, 0x9F, 0x7C, 0xA4}
|
||||
},
|
||||
{
|
||||
{0x52, 0xC2, 0xE9, 0xEB, 0x09, 0xE3, 0xEE, 0x29, 0x32, 0xA1, 0x0C, 0x1F, 0xB6, 0xA0, 0x92, 0x6C},
|
||||
{0x4D, 0x12, 0xE1, 0x4B, 0x2A, 0x47, 0x4C, 0x1C, 0x09, 0xCB, 0x03, 0x59, 0xF0, 0x15, 0xF4, 0xE4}
|
||||
},
|
||||
{
|
||||
{0x52, 0xC2, 0xE9, 0xEB, 0x09, 0xE3, 0xEE, 0x29, 0x32, 0xA1, 0x0C, 0x1F, 0xB6, 0xA0, 0x92, 0x6C},
|
||||
{0x4D, 0x12, 0xE1, 0x4B, 0x2A, 0x47, 0x4C, 0x1C, 0x09, 0xCB, 0x03, 0x59, 0xF0, 0x15, 0xF4, 0xE4}
|
||||
}
|
||||
};
|
||||
|
||||
constexpr u8 BisKekSource[0x10] = {
|
||||
0x34, 0xC1, 0xA0, 0xC4, 0x82, 0x58, 0xF8, 0xB4, 0xFA, 0x9E, 0x5E, 0x6A, 0xDA, 0xFC, 0x7E, 0x4F,
|
||||
};
|
||||
|
||||
void InitializeThreadFunc(void *arg);
|
||||
|
||||
constexpr size_t InitializeThreadStackSize = 0x4000;
|
||||
|
||||
/* Globals. */
|
||||
os::Event g_init_event(os::EventClearMode_ManualClear);
|
||||
|
||||
os::ThreadType g_initialize_thread;
|
||||
alignas(os::ThreadStackAlignment) u8 g_initialize_thread_stack[InitializeThreadStackSize];
|
||||
|
||||
/* Console-unique data backup and protection. */
|
||||
FsFile g_bis_key_file;
|
||||
|
||||
/* Emummc file protection. */
|
||||
FsFile g_emummc_file;
|
||||
|
||||
/* Maintain exclusive access to the fusee external package. */
|
||||
FsFile g_stratosphere_file;
|
||||
FsFile g_package3_file;
|
||||
|
||||
constexpr inline bool IsHexadecimal(const char *str) {
|
||||
while (*str) {
|
||||
if (std::isxdigit(static_cast<unsigned char>(*str))) {
|
||||
str++;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GetBackupFileName(char *dst, size_t dst_size, const char *serial_number, const char *fn) {
|
||||
if (strlen(serial_number) > 0) {
|
||||
util::SNPrintf(dst, dst_size, "automatic_backups/%s_%s", serial_number, fn);
|
||||
} else {
|
||||
util::SNPrintf(dst, dst_size, "automatic_backups/%s", fn);
|
||||
}
|
||||
}
|
||||
|
||||
void CreateAutomaticBackups() {
|
||||
/* Create a backup directory, if one doesn't exist. */
|
||||
mitm::fs::CreateAtmosphereSdDirectory("/automatic_backups");
|
||||
|
||||
/* Initialize PRODINFO and get a reference for the device. */
|
||||
char device_reference[0x40] = {};
|
||||
ON_SCOPE_EXIT { std::memset(device_reference, 0, sizeof(device_reference)); };
|
||||
mitm::SaveProdInfoBackupsAndWipeMemory(device_reference, sizeof(device_reference));
|
||||
|
||||
/* Backup BIS keys. */
|
||||
{
|
||||
u64 key_generation = 0;
|
||||
if (hos::GetVersion() >= hos::Version_5_0_0) {
|
||||
R_ABORT_UNLESS(spl::GetConfig(std::addressof(key_generation), spl::ConfigItem::DeviceUniqueKeyGeneration));
|
||||
}
|
||||
|
||||
u8 bis_keys[4][2][0x10];
|
||||
std::memset(bis_keys, 0xCC, sizeof(bis_keys));
|
||||
ON_SCOPE_EXIT { std::memset(bis_keys, 0xCC, sizeof(bis_keys)); };
|
||||
|
||||
/* TODO: Clean this up. */
|
||||
for (size_t partition = 0; partition < 4; partition++) {
|
||||
if (partition == 0) {
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
R_ABORT_UNLESS(spl::GenerateSpecificAesKey(bis_keys[partition][i], 0x10, BisKeySources[partition][i], 0x10, key_generation, i));
|
||||
}
|
||||
} else {
|
||||
const u32 option = (partition == 3 && spl::IsRecoveryBoot()) ? 0x4 : 0x1;
|
||||
|
||||
spl::AccessKey access_key;
|
||||
R_ABORT_UNLESS(spl::GenerateAesKek(std::addressof(access_key), BisKekSource, 0x10, key_generation, option));
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
R_ABORT_UNLESS(spl::GenerateAesKey(bis_keys[partition][i], 0x10, access_key, BisKeySources[partition][i], 0x10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char bis_keys_backup_name[ams::fs::EntryNameLengthMax + 1];
|
||||
GetBackupFileName(bis_keys_backup_name, sizeof(bis_keys_backup_name), device_reference, "BISKEYS.bin");
|
||||
|
||||
mitm::fs::CreateAtmosphereSdFile(bis_keys_backup_name, sizeof(bis_keys), ams::fs::CreateOption_None);
|
||||
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(g_bis_key_file), bis_keys_backup_name, ams::fs::OpenMode_ReadWrite));
|
||||
R_ABORT_UNLESS(fsFileSetSize(std::addressof(g_bis_key_file), sizeof(bis_keys)));
|
||||
R_ABORT_UNLESS(fsFileWrite(std::addressof(g_bis_key_file), 0, bis_keys, sizeof(bis_keys), FsWriteOption_Flush));
|
||||
/* NOTE: g_bis_key_file is intentionally not closed here. This prevents any other process from opening it. */
|
||||
}
|
||||
|
||||
/* Open a reference to the fusee external package. */
|
||||
/* As upcoming/current atmosphere releases may contain more than one zip which users much choose between, */
|
||||
/* maintaining an open reference prevents cleanly the issue of "automatic" updaters selecting the incorrect */
|
||||
/* zip, and encourages good updating hygiene -- atmosphere should not be updated on SD while HOS is alive. */
|
||||
{
|
||||
R_ABORT_UNLESS(mitm::fs::OpenSdFile(std::addressof(g_package3_file), "/atmosphere/package3", ams::fs::OpenMode_Read));
|
||||
R_ABORT_UNLESS(mitm::fs::OpenSdFile(std::addressof(g_stratosphere_file), "/atmosphere/stratosphere.romfs", ams::fs::OpenMode_Read));
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialization implementation */
|
||||
void InitializeThreadFunc(void *) {
|
||||
/* Wait for the SD card to be ready. */
|
||||
cfg::WaitSdCardInitialized();
|
||||
|
||||
/* Open global SD card file system, so that other threads can begin using the SD. */
|
||||
mitm::fs::OpenGlobalSdCardFileSystem();
|
||||
|
||||
/* Mount the sd card at a convenient mountpoint. */
|
||||
ams::fs::MountSdCard(ams::fs::impl::SdCardFileSystemMountName);
|
||||
|
||||
/* Initialize the reboot manager (load a payload off the SD). */
|
||||
/* Discard result, since it doesn't need to succeed. */
|
||||
mitm::bpc::LoadRebootPayload();
|
||||
|
||||
/* Backup Calibration Binary and BIS keys. */
|
||||
CreateAutomaticBackups();
|
||||
|
||||
/* If we're emummc, persist a write-handle to prevent other processes from touching the image. */
|
||||
if (emummc::IsActive()) {
|
||||
if (const char *emummc_file_path = emummc::GetFilePath(); emummc_file_path != nullptr) {
|
||||
char emummc_path[ams::fs::EntryNameLengthMax + 1];
|
||||
util::SNPrintf(emummc_path, sizeof(emummc_path), "%s/eMMC", emummc_file_path);
|
||||
mitm::fs::OpenSdFile(std::addressof(g_emummc_file), emummc_path, ams::fs::OpenMode_Read);
|
||||
}
|
||||
|
||||
/* NOTE: due to an Atmosphere bug, NS accesses to the Nintendo dir accessed /Nintendo/Nintendo */
|
||||
/* instead of /Nintendo. This logic is potentially temporary, and fixes the case where this would have happened. */
|
||||
{
|
||||
auto HasDir = [](const char *p) -> bool { bool res{}; R_ABORT_UNLESS(ams::fs::HasDirectory(std::addressof(res), p)); return res; };
|
||||
auto HasFile = [](const char *p) -> bool { bool res{}; R_ABORT_UNLESS(ams::fs::HasFile(std::addressof(res), p)); return res; };
|
||||
|
||||
char emummc_path[ams::fs::EntryNameLengthMax + 1];
|
||||
char emummc_bug_path[ams::fs::EntryNameLengthMax + 1];
|
||||
util::SNPrintf(emummc_path, sizeof(emummc_path), "%s:/%s", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
|
||||
util::SNPrintf(emummc_bug_path, sizeof(emummc_bug_path), "%s:/%s/Nintendo", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
|
||||
|
||||
if (HasDir(emummc_bug_path)) {
|
||||
/* Ensure Contents directory exists for normal emummc. */
|
||||
/* NOTE: Allowed to fail on already-exists. */
|
||||
util::SNPrintf(emummc_path, sizeof(emummc_path), "%s:/%s/Contents", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
|
||||
ams::fs::CreateDirectory(emummc_path);
|
||||
|
||||
/* Fix Contents/private */
|
||||
util::SNPrintf(emummc_path, sizeof(emummc_path), "%s:/%s/Contents/private", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
|
||||
util::SNPrintf(emummc_bug_path, sizeof(emummc_bug_path), "%s:/%s/Nintendo/Contents/private", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
|
||||
if (HasFile(emummc_bug_path) && !HasFile(emummc_path)) {
|
||||
R_ABORT_UNLESS(ams::fs::RenameFile(emummc_bug_path, emummc_path));
|
||||
}
|
||||
|
||||
/* Fix Contents/private1 */
|
||||
util::SNPrintf(emummc_path, sizeof(emummc_path), "%s:/%s/Contents/private1", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
|
||||
util::SNPrintf(emummc_bug_path, sizeof(emummc_bug_path), "%s:/%s/Nintendo/Contents/private1", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
|
||||
if (HasFile(emummc_bug_path) && !HasFile(emummc_path)) {
|
||||
R_ABORT_UNLESS(ams::fs::RenameFile(emummc_bug_path, emummc_path));
|
||||
}
|
||||
|
||||
/* Delete bug directory. */
|
||||
util::SNPrintf(emummc_bug_path, sizeof(emummc_bug_path), "%s:/%s/Nintendo", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
|
||||
R_ABORT_UNLESS(ams::fs::DeleteDirectoryRecursively(emummc_bug_path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Connect to set:sys. */
|
||||
R_ABORT_UNLESS(setInitialize());
|
||||
R_ABORT_UNLESS(setsysInitialize());
|
||||
|
||||
/* Load settings off the SD card. */
|
||||
settings::fwdbg::InitializeSdCardKeyValueStore();
|
||||
|
||||
/* Ensure that we reboot using the user's preferred method. */
|
||||
R_ABORT_UNLESS(mitm::bpc::DetectPreferredRebootFunctionality());
|
||||
|
||||
/* Signal to waiters that we are ready. */
|
||||
g_init_event.Signal();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void StartInitialize() {
|
||||
/* Initialize prodinfo. */
|
||||
mitm::InitializeProdInfoManagement();
|
||||
|
||||
/* Launch initialize thread. */
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_initialize_thread), InitializeThreadFunc, nullptr, g_initialize_thread_stack, sizeof(g_initialize_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(mitm, InitializeThread)));
|
||||
os::SetThreadNamePointer(std::addressof(g_initialize_thread), AMS_GET_SYSTEM_THREAD_NAME(mitm, InitializeThread));
|
||||
os::StartThread(std::addressof(g_initialize_thread));
|
||||
}
|
||||
|
||||
bool IsInitialized() {
|
||||
return g_init_event.TryWait();
|
||||
}
|
||||
|
||||
void WaitInitialized() {
|
||||
g_init_event.Wait();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm {
|
||||
|
||||
void StartInitialize();
|
||||
|
||||
bool IsInitialized();
|
||||
void WaitInitialized();
|
||||
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "amsmitm_initialization.hpp"
|
||||
#include "amsmitm_module_management.hpp"
|
||||
#include "bpc_mitm/bpc_ams_power_utils.hpp"
|
||||
#include "sysupdater/sysupdater_fs_utils.hpp"
|
||||
|
||||
namespace ams {
|
||||
|
||||
namespace {
|
||||
|
||||
/* TODO: we really shouldn't be using malloc just to avoid dealing with real allocator separation. */
|
||||
constexpr size_t MallocBufferSize = 12_MB;
|
||||
alignas(os::MemoryPageSize) constinit u8 g_malloc_buffer[MallocBufferSize];
|
||||
|
||||
}
|
||||
|
||||
namespace init {
|
||||
|
||||
void InitializeSystemModule() {
|
||||
/* Initialize our connection to sm. */
|
||||
R_ABORT_UNLESS(sm::Initialize());
|
||||
|
||||
/* Initialize fs. */
|
||||
fs::InitializeForSystem();
|
||||
fs::SetEnabledAutoAbort(false);
|
||||
|
||||
/* Initialize other services. */
|
||||
R_ABORT_UNLESS(pmdmntInitialize());
|
||||
R_ABORT_UNLESS(pminfoInitialize());
|
||||
ncm::Initialize();
|
||||
|
||||
/* Verify that we can sanely execute. */
|
||||
ams::CheckApiVersion();
|
||||
}
|
||||
|
||||
void FinalizeSystemModule() { /* ... */ }
|
||||
|
||||
void Startup() {
|
||||
/* Initialize the global malloc allocator. */
|
||||
init::InitializeAllocator(g_malloc_buffer, sizeof(g_malloc_buffer));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ExceptionHandler(FatalErrorContext *ctx) {
|
||||
/* We're bpc-mitm (or ams_mitm, anyway), so manually reboot to fatal error. */
|
||||
mitm::bpc::RebootForFatalError(ctx);
|
||||
}
|
||||
|
||||
void NORETURN Exit(int rc) {
|
||||
AMS_UNUSED(rc);
|
||||
AMS_ABORT("Exit called by immortal process");
|
||||
}
|
||||
|
||||
void Main() {
|
||||
/* Register "ams" port, use up its session. */
|
||||
{
|
||||
svc::Handle ams_port;
|
||||
R_ABORT_UNLESS(svc::ManageNamedPort(std::addressof(ams_port), "ams", 1));
|
||||
|
||||
svc::Handle ams_session;
|
||||
R_ABORT_UNLESS(svc::ConnectToNamedPort(std::addressof(ams_session), "ams"));
|
||||
}
|
||||
|
||||
/* Initialize fssystem library. */
|
||||
fssystem::InitializeForAtmosphereMitm();
|
||||
|
||||
/* Configure ncm to use fssystem library to mount content from the sd card. */
|
||||
ncm::SetMountContentMetaFunction(mitm::sysupdater::MountSdCardContentMeta);
|
||||
|
||||
/* Launch all mitm modules in sequence. */
|
||||
mitm::LaunchAllModules();
|
||||
|
||||
/* Wait for all mitm modules to end. */
|
||||
mitm::WaitAllModules();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm {
|
||||
|
||||
template<typename T>
|
||||
concept IsModule = requires(T, void *arg) {
|
||||
{ T::ThreadPriority } -> std::convertible_to<s32>;
|
||||
{ T::StackSize } -> std::convertible_to<size_t>;
|
||||
{ T::Stack } -> std::convertible_to<void *>;
|
||||
{ T::ThreadFunction(arg) } -> std::same_as<void>;
|
||||
};
|
||||
|
||||
#define DEFINE_MITM_MODULE_CLASS(ss, prio) class MitmModule { \
|
||||
public: \
|
||||
static constexpr s32 ThreadPriority = prio; \
|
||||
static constexpr size_t StackSize = ss; \
|
||||
alignas(os::ThreadStackAlignment) static inline u8 Stack[StackSize]; \
|
||||
public: \
|
||||
static void ThreadFunction(void *); \
|
||||
}
|
||||
|
||||
template<class M> requires IsModule<M>
|
||||
struct ModuleTraits {
|
||||
static constexpr void *Stack = &M::Stack[0];
|
||||
static constexpr size_t StackSize = M::StackSize;
|
||||
|
||||
static constexpr s32 ThreadPriority = M::ThreadPriority;
|
||||
|
||||
static constexpr ::ThreadFunc ThreadFunction = &M::ThreadFunction;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "amsmitm_module_management.hpp"
|
||||
#include "amsmitm_module.hpp"
|
||||
|
||||
#include "fs_mitm/fsmitm_module.hpp"
|
||||
#include "set_mitm/setmitm_module.hpp"
|
||||
#include "bpc_mitm/bpcmitm_module.hpp"
|
||||
#include "bpc_mitm/bpc_ams_module.hpp"
|
||||
#include "ns_mitm/nsmitm_module.hpp"
|
||||
#include "dns_mitm/dnsmitm_module.hpp"
|
||||
#include "sysupdater/sysupdater_module.hpp"
|
||||
#include "mitm_pm/mitm_pm_module.hpp"
|
||||
|
||||
namespace ams::mitm {
|
||||
|
||||
namespace {
|
||||
|
||||
enum ModuleId : u32 {
|
||||
ModuleId_FsMitm,
|
||||
ModuleId_SetMitm,
|
||||
ModuleId_BpcMitm,
|
||||
ModuleId_BpcAms,
|
||||
ModuleId_NsMitm,
|
||||
ModuleId_DnsMitm,
|
||||
ModuleId_Sysupdater,
|
||||
ModuleId_PmService,
|
||||
|
||||
ModuleId_Count,
|
||||
};
|
||||
|
||||
struct ModuleDefinition {
|
||||
ThreadFunc main;
|
||||
void *stack_mem;
|
||||
s32 priority;
|
||||
u32 stack_size;
|
||||
};
|
||||
|
||||
template<class M>
|
||||
constexpr ModuleDefinition GetModuleDefinition() {
|
||||
using Traits = ModuleTraits<M>;
|
||||
|
||||
return ModuleDefinition {
|
||||
.main = Traits::ThreadFunction,
|
||||
.stack_mem = Traits::Stack,
|
||||
.priority = Traits::ThreadPriority,
|
||||
.stack_size = static_cast<u32>(Traits::StackSize),
|
||||
};
|
||||
}
|
||||
|
||||
ams::os::ThreadType g_module_threads[ModuleId_Count];
|
||||
|
||||
constexpr ModuleDefinition g_module_definitions[ModuleId_Count] = {
|
||||
GetModuleDefinition<fs::MitmModule>(),
|
||||
GetModuleDefinition<settings::MitmModule>(),
|
||||
GetModuleDefinition<bpc::MitmModule>(),
|
||||
GetModuleDefinition<bpc_ams::MitmModule>(),
|
||||
GetModuleDefinition<ns::MitmModule>(),
|
||||
GetModuleDefinition<socket::resolver::MitmModule>(),
|
||||
GetModuleDefinition<sysupdater::MitmModule>(),
|
||||
GetModuleDefinition<pm::MitmModule>(),
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void LaunchAllModules() {
|
||||
/* Create thread for each module. */
|
||||
for (u32 i = 0; i < static_cast<u32>(ModuleId_Count); i++) {
|
||||
const ModuleDefinition &cur_module = g_module_definitions[i];
|
||||
R_ABORT_UNLESS(os::CreateThread(g_module_threads + i, cur_module.main, nullptr, cur_module.stack_mem, cur_module.stack_size, cur_module.priority));
|
||||
}
|
||||
|
||||
/* Start thread for each module. */
|
||||
for (u32 i = 0; i < static_cast<u32>(ModuleId_Count); i++) {
|
||||
os::StartThread(g_module_threads + i);
|
||||
}
|
||||
}
|
||||
|
||||
void WaitAllModules() {
|
||||
/* Wait on thread for each module. */
|
||||
for (u32 i = 0; i < static_cast<u32>(ModuleId_Count); i++) {
|
||||
os::WaitThread(g_module_threads + i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm {
|
||||
|
||||
void LaunchAllModules();
|
||||
void WaitAllModules();
|
||||
|
||||
}
|
||||
@@ -1,637 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "amsmitm_fs_utils.hpp"
|
||||
#include "amsmitm_prodinfo_utils.hpp"
|
||||
|
||||
namespace ams::mitm {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline u16 Crc16InitialValue = 0x55AA;
|
||||
|
||||
constexpr inline u16 Crc16Table[] = {
|
||||
0x0000, 0xCC01, 0xD801, 0x1400,
|
||||
0xF001, 0x3C00, 0x2800, 0xE401,
|
||||
0xA001, 0x6C00, 0x7800, 0xB401,
|
||||
0x5000, 0x9C01, 0x8801, 0x4400,
|
||||
};
|
||||
|
||||
u16 GetCrc16(const void *data, size_t size) {
|
||||
AMS_ASSERT(data != nullptr);
|
||||
AMS_ASSERT(size > 0);
|
||||
|
||||
const u8 *src = static_cast<const u8 *>(data);
|
||||
|
||||
u16 crc = Crc16InitialValue;
|
||||
|
||||
u16 tmp = 0;
|
||||
while ((size--) > 0) {
|
||||
tmp = Crc16Table[crc & 0xF];
|
||||
crc = ((crc >> 4) & 0x0FFF) ^ tmp ^ Crc16Table[*src & 0xF];
|
||||
tmp = Crc16Table[crc & 0xF];
|
||||
crc = ((crc >> 4) & 0x0FFF) ^ tmp ^ Crc16Table[(*(src++) >> 4) & 0xF];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
bool IsBlank(const void *data, size_t size) {
|
||||
AMS_ASSERT(data != nullptr);
|
||||
AMS_ASSERT(size > 0);
|
||||
|
||||
const u8 *src = static_cast<const u8 *>(data);
|
||||
while ((size--) > 0) {
|
||||
if (*(src++) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr inline u32 CalibrationMagic = util::FourCC<'C','A','L','0'>::Code;
|
||||
|
||||
struct Sha256Hash {
|
||||
u8 data[crypto::Sha256Generator::HashSize];
|
||||
};
|
||||
|
||||
struct CalibrationInfoHeader {
|
||||
u32 magic;
|
||||
u32 version;
|
||||
u32 body_size;
|
||||
u16 model;
|
||||
u16 update_count;
|
||||
u8 pad[0xE];
|
||||
u16 crc;
|
||||
Sha256Hash body_hash;
|
||||
};
|
||||
static_assert(sizeof(CalibrationInfoHeader) == 0x40);
|
||||
|
||||
constexpr inline size_t CalibrationInfoBodySizeMax = CalibrationBinarySize - sizeof(CalibrationInfoHeader);
|
||||
|
||||
struct CalibrationInfo {
|
||||
CalibrationInfoHeader header;
|
||||
u8 body[CalibrationInfoBodySizeMax]; /* TODO: CalibrationInfoBody body; */
|
||||
|
||||
template<typename Block>
|
||||
Block &GetBlock() {
|
||||
static_assert(Block::Offset >= sizeof(CalibrationInfoHeader));
|
||||
static_assert(Block::Offset < sizeof(CalibrationInfo));
|
||||
static_assert(Block::Offset + Block::Size <= sizeof(CalibrationInfo));
|
||||
return *static_cast<Block *>(static_cast<void *>(std::addressof(this->body[Block::Offset - sizeof(this->header)])));
|
||||
}
|
||||
|
||||
template<typename Block>
|
||||
const Block &GetBlock() const {
|
||||
static_assert(Block::Offset >= sizeof(CalibrationInfoHeader));
|
||||
static_assert(Block::Offset < sizeof(CalibrationInfo));
|
||||
static_assert(Block::Offset + Block::Size <= sizeof(CalibrationInfo));
|
||||
return *static_cast<const Block *>(static_cast<const void *>(std::addressof(this->body[Block::Offset - sizeof(this->header)])));
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(CalibrationInfo) == CalibrationBinarySize);
|
||||
|
||||
struct SecureCalibrationInfoBackup {
|
||||
CalibrationInfo info;
|
||||
Sha256Hash hash;
|
||||
u8 pad[SecureCalibrationBinaryBackupSize - sizeof(info) - sizeof(hash)];
|
||||
};
|
||||
static_assert(sizeof(SecureCalibrationInfoBackup) == SecureCalibrationBinaryBackupSize);
|
||||
|
||||
bool IsValidSha256Hash(const Sha256Hash &hash, const void *data, size_t data_size) {
|
||||
Sha256Hash calc_hash;
|
||||
ON_SCOPE_EXIT { ::ams::crypto::ClearMemory(std::addressof(calc_hash), sizeof(calc_hash)); };
|
||||
|
||||
::ams::crypto::GenerateSha256(std::addressof(calc_hash), sizeof(calc_hash), data, data_size);
|
||||
return ::ams::crypto::IsSameBytes(std::addressof(calc_hash), std::addressof(hash), sizeof(Sha256Hash));
|
||||
}
|
||||
|
||||
bool IsValid(const CalibrationInfoHeader &header) {
|
||||
return header.magic == CalibrationMagic && GetCrc16(std::addressof(header), AMS_OFFSETOF(CalibrationInfoHeader, crc)) == header.crc;
|
||||
}
|
||||
|
||||
bool IsValid(const CalibrationInfoHeader &header, const void *body) {
|
||||
return IsValid(header) && IsValidSha256Hash(header.body_hash, body, header.body_size);
|
||||
}
|
||||
|
||||
#define DEFINE_CALIBRATION_CRC_BLOCK(_TypeName, _Offset, _Size, _Decl, _MemberName) \
|
||||
struct _TypeName { \
|
||||
static constexpr size_t Offset = _Offset; \
|
||||
static constexpr size_t Size = _Size; \
|
||||
static constexpr bool IsCrcBlock = true; \
|
||||
static constexpr bool IsShaBlock = false; \
|
||||
_Decl; \
|
||||
static_assert(Size >= sizeof(_MemberName) + sizeof(u16)); \
|
||||
u8 pad[Size - sizeof(_MemberName) - sizeof(u16)]; \
|
||||
u16 crc; \
|
||||
}; \
|
||||
static_assert(sizeof(_TypeName) == _TypeName::Size)
|
||||
|
||||
#define DEFINE_CALIBRATION_SHA_BLOCK(_TypeName, _Offset, _Size, _Decl, _MemberName) \
|
||||
struct _TypeName { \
|
||||
static constexpr size_t Offset = _Offset; \
|
||||
static constexpr size_t Size = _Size; \
|
||||
static constexpr bool IsCrcBlock = false; \
|
||||
static constexpr bool IsShaBlock = true; \
|
||||
_Decl; \
|
||||
static_assert(Size == sizeof(_MemberName) + sizeof(Sha256Hash)); \
|
||||
Sha256Hash sha256_hash; \
|
||||
}; \
|
||||
static_assert(sizeof(_TypeName) == _TypeName::Size)
|
||||
|
||||
DEFINE_CALIBRATION_CRC_BLOCK(SerialNumberBlock, 0x0250, 0x020, ::ams::settings::factory::SerialNumber serial_number, serial_number);
|
||||
DEFINE_CALIBRATION_CRC_BLOCK(EccB233DeviceCertificateBlock, 0x0480, 0x190, ::ams::settings::factory::EccB233DeviceCertificate device_certificate, device_certificate);
|
||||
DEFINE_CALIBRATION_CRC_BLOCK(SslKeyBlock, 0x09B0, 0x120, u8 ssl_key[0x110], ssl_key);
|
||||
DEFINE_CALIBRATION_CRC_BLOCK(SslCertificateSizeBlock, 0x0AD0, 0x010, u64 ssl_certificate_size, ssl_certificate_size);
|
||||
DEFINE_CALIBRATION_SHA_BLOCK(SslCertificateBlock, 0x0AE0, 0x820, u8 ssl_certificate[0x800], ssl_certificate);
|
||||
DEFINE_CALIBRATION_CRC_BLOCK(EcqvEcdsaAmiiboRootCertificateBlock, 0x35A0, 0x080, u8 data[0x70], data);
|
||||
DEFINE_CALIBRATION_CRC_BLOCK(EcqvBlsAmiiboRootCertificateBlock, 0x36A0, 0x0A0, u8 data[0x90], data);
|
||||
DEFINE_CALIBRATION_CRC_BLOCK(ExtendedSslKeyBlock, 0x3AE0, 0x140, u8 ssl_key[0x134], ssl_key);
|
||||
DEFINE_CALIBRATION_CRC_BLOCK(Rsa2048DeviceKeyBlock, 0x3D70, 0x250, u8 device_key[0x240], device_key);
|
||||
DEFINE_CALIBRATION_CRC_BLOCK(Rsa2048DeviceCertificateBlock, 0x3FC0, 0x250, ::ams::settings::factory::Rsa2048DeviceCertificate device_certificate, device_certificate);
|
||||
|
||||
#undef DEFINE_CALIBRATION_CRC_BLOCK
|
||||
#undef DEFINE_CALIBRATION_SHA_BLOCK
|
||||
|
||||
constexpr inline const char BlankSerialNumberString[] = "XAW00000000000";
|
||||
|
||||
template<typename Block>
|
||||
void Blank(Block &block) {
|
||||
if constexpr (std::is_same<Block, SerialNumberBlock>::value) {
|
||||
static_assert(sizeof(BlankSerialNumberString) <= sizeof(SerialNumberBlock::serial_number));
|
||||
std::memset(std::addressof(block), 0, Block::Size - sizeof(block.crc));
|
||||
std::memcpy(block.serial_number.str, BlankSerialNumberString, sizeof(BlankSerialNumberString));
|
||||
block.crc = GetCrc16(std::addressof(block), Block::Size - sizeof(block.crc));
|
||||
} else if constexpr (std::is_same<Block, SslCertificateBlock>::value) {
|
||||
std::memset(std::addressof(block), 0, sizeof(block.ssl_certificate));
|
||||
} else if constexpr (Block::IsCrcBlock) {
|
||||
std::memset(std::addressof(block), 0, Block::Size - sizeof(block.crc));
|
||||
block.crc = GetCrc16(std::addressof(block), Block::Size - sizeof(block.crc));
|
||||
} else {
|
||||
static_assert(Block::IsShaBlock);
|
||||
std::memset(std::addressof(block), 0, Block::Size);
|
||||
::ams::crypto::GenerateSha256(std::addressof(block.sha256_hash), sizeof(block.sha256_hash), std::addressof(block), Block::Size - sizeof(block.sha256_hash));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Block>
|
||||
bool IsBlank(const Block &block) {
|
||||
if constexpr (std::is_same<Block, SerialNumberBlock>::value) {
|
||||
static_assert(sizeof(BlankSerialNumberString) <= sizeof(SerialNumberBlock::serial_number));
|
||||
return std::memcmp(block.serial_number.str, BlankSerialNumberString, sizeof(BlankSerialNumberString) - 1) == 0 || IsBlank(std::addressof(block), Block::Size - sizeof(block.crc));
|
||||
} else if constexpr (Block::IsCrcBlock) {
|
||||
return IsBlank(std::addressof(block), Block::Size - sizeof(block.crc));
|
||||
} else {
|
||||
return IsBlank(std::addressof(block), Block::Size - sizeof(block.sha256_hash));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Block>
|
||||
bool IsValid(const Block &block, size_t size = 0) {
|
||||
if constexpr (Block::IsCrcBlock) {
|
||||
return GetCrc16(std::addressof(block), Block::Size - sizeof(block.crc)) == block.crc;
|
||||
} else {
|
||||
static_assert(Block::IsShaBlock);
|
||||
return IsValidSha256Hash(block.sha256_hash, std::addressof(block), size != 0 ? size : Block::Size - sizeof(block.sha256_hash));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Blank(CalibrationInfo &info) {
|
||||
/* Set header. */
|
||||
info.header.magic = CalibrationMagic;
|
||||
info.header.body_size = sizeof(info.body);
|
||||
info.header.crc = GetCrc16(std::addressof(info.header), AMS_OFFSETOF(CalibrationInfoHeader, crc));
|
||||
|
||||
/* Set blocks. */
|
||||
Blank(info.GetBlock<SerialNumberBlock>());
|
||||
Blank(info.GetBlock<SslCertificateSizeBlock>());
|
||||
Blank(info.GetBlock<SslCertificateBlock>());
|
||||
Blank(info.GetBlock<EcqvEcdsaAmiiboRootCertificateBlock>());
|
||||
Blank(info.GetBlock<EcqvBlsAmiiboRootCertificateBlock>());
|
||||
Blank(info.GetBlock<ExtendedSslKeyBlock>());
|
||||
|
||||
/* Set header hash. */
|
||||
crypto::GenerateSha256(std::addressof(info.header.body_hash), sizeof(info.header.body_hash), std::addressof(info.body), sizeof(info.body));
|
||||
}
|
||||
|
||||
bool IsValidHeader(const CalibrationInfo &cal) {
|
||||
return IsValid(cal.header) && cal.header.body_size <= CalibrationInfoBodySizeMax && IsValid(cal.header, cal.body);
|
||||
}
|
||||
|
||||
bool IsValidSerialNumber(const char *sn) {
|
||||
for (size_t i = 0; i < std::strlen(sn); i++) {
|
||||
if (!std::isalnum(static_cast<unsigned char>(sn[i]))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GetSerialNumber(char *dst, const CalibrationInfo &info) {
|
||||
std::memcpy(dst, std::addressof(info.GetBlock<SerialNumberBlock>()), sizeof(info.GetBlock<SerialNumberBlock>().serial_number));
|
||||
dst[sizeof(info.GetBlock<SerialNumberBlock>().serial_number) + 1] = '\x00';
|
||||
}
|
||||
|
||||
bool IsValidSerialNumber(const CalibrationInfo &cal) {
|
||||
char sn[0x20] = {};
|
||||
ON_SCOPE_EXIT { std::memset(sn, 0, sizeof(sn)); };
|
||||
|
||||
GetSerialNumber(sn, cal);
|
||||
return IsValidSerialNumber(sn);
|
||||
}
|
||||
|
||||
bool IsValid(const CalibrationInfo &cal) {
|
||||
return IsValidHeader(cal) &&
|
||||
IsValid(cal.GetBlock<SerialNumberBlock>()) &&
|
||||
IsValid(cal.GetBlock<EccB233DeviceCertificateBlock>()) &&
|
||||
IsValid(cal.GetBlock<SslKeyBlock>()) &&
|
||||
IsValid(cal.GetBlock<SslCertificateSizeBlock>()) &&
|
||||
cal.GetBlock<SslCertificateSizeBlock>().ssl_certificate_size <= sizeof(cal.GetBlock<SslCertificateBlock>().ssl_certificate) &&
|
||||
IsValid(cal.GetBlock<SslCertificateBlock>(), cal.GetBlock<SslCertificateSizeBlock>().ssl_certificate_size) &&
|
||||
IsValid(cal.GetBlock<EcqvEcdsaAmiiboRootCertificateBlock>()) &&
|
||||
IsValid(cal.GetBlock<EcqvBlsAmiiboRootCertificateBlock>()) &&
|
||||
IsValid(cal.GetBlock<ExtendedSslKeyBlock>()) &&
|
||||
IsValidSerialNumber(cal);
|
||||
}
|
||||
|
||||
bool ContainsCorrectDeviceId(const EccB233DeviceCertificateBlock &block, u64 device_id) {
|
||||
static constexpr size_t DeviceIdOffset = 0xC6;
|
||||
char found_device_id_str[sizeof("0011223344556677")] = {};
|
||||
ON_SCOPE_EXIT { std::memset(found_device_id_str, 0, sizeof(found_device_id_str)); };
|
||||
std::memcpy(found_device_id_str, std::addressof(block.device_certificate.data[DeviceIdOffset]), sizeof(found_device_id_str) - 1);
|
||||
|
||||
static constexpr u64 DeviceIdLowMask = 0x00FFFFFFFFFFFFFFul;
|
||||
|
||||
return (std::strtoul(found_device_id_str, nullptr, 16) & DeviceIdLowMask) == (device_id & DeviceIdLowMask);
|
||||
}
|
||||
|
||||
bool ContainsCorrectDeviceId(const CalibrationInfo &cal) {
|
||||
return ContainsCorrectDeviceId(cal.GetBlock<EccB233DeviceCertificateBlock>(), exosphere::GetDeviceId());
|
||||
}
|
||||
|
||||
bool IsValidForSecureBackup(const CalibrationInfo &cal) {
|
||||
return IsValid(cal) && ContainsCorrectDeviceId(cal);
|
||||
}
|
||||
|
||||
bool IsBlank(const CalibrationInfo &cal) {
|
||||
return IsBlank(cal.GetBlock<SerialNumberBlock>()) ||
|
||||
IsBlank(cal.GetBlock<SslCertificateSizeBlock>()) ||
|
||||
IsBlank(cal.GetBlock<SslCertificateBlock>()) ||
|
||||
IsBlank(cal.GetBlock<EcqvEcdsaAmiiboRootCertificateBlock>()) ||
|
||||
IsBlank(cal.GetBlock<EcqvBlsAmiiboRootCertificateBlock>()) ||
|
||||
IsBlank(cal.GetBlock<ExtendedSslKeyBlock>());
|
||||
}
|
||||
|
||||
void ReadStorageCalibrationBinary(CalibrationInfo *out) {
|
||||
FsStorage calibration_binary_storage;
|
||||
R_ABORT_UNLESS(fsOpenBisStorage(std::addressof(calibration_binary_storage), FsBisPartitionId_CalibrationBinary));
|
||||
ON_SCOPE_EXIT { fsStorageClose(std::addressof(calibration_binary_storage)); };
|
||||
|
||||
R_ABORT_UNLESS(fsStorageRead(std::addressof(calibration_binary_storage), 0, out, sizeof(*out)));
|
||||
}
|
||||
|
||||
constexpr inline const u8 SecureCalibrationBinaryBackupIv[crypto::Aes128CtrDecryptor::IvSize] = {};
|
||||
|
||||
void ReadStorageEncryptedSecureCalibrationBinaryBackupUnsafe(SecureCalibrationInfoBackup *out) {
|
||||
FsStorage calibration_binary_storage;
|
||||
R_ABORT_UNLESS(fsOpenBisStorage(std::addressof(calibration_binary_storage), FsBisPartitionId_CalibrationBinary));
|
||||
ON_SCOPE_EXIT { fsStorageClose(std::addressof(calibration_binary_storage)); };
|
||||
|
||||
R_ABORT_UNLESS(fsStorageRead(std::addressof(calibration_binary_storage), SecureCalibrationInfoBackupOffset, out, sizeof(*out)));
|
||||
}
|
||||
|
||||
void WriteStorageEncryptedSecureCalibrationBinaryBackupUnsafe(const SecureCalibrationInfoBackup *src) {
|
||||
FsStorage calibration_binary_storage;
|
||||
R_ABORT_UNLESS(fsOpenBisStorage(std::addressof(calibration_binary_storage), FsBisPartitionId_CalibrationBinary));
|
||||
ON_SCOPE_EXIT { fsStorageClose(std::addressof(calibration_binary_storage)); };
|
||||
|
||||
R_ABORT_UNLESS(fsStorageWrite(std::addressof(calibration_binary_storage), SecureCalibrationInfoBackupOffset, src, sizeof(*src)));
|
||||
}
|
||||
|
||||
void GenerateSecureCalibrationBinaryBackupKey(void *dst, size_t dst_size) {
|
||||
static constexpr const u8 SecureCalibrationBinaryBackupKeySource[crypto::Aes128CtrDecryptor::KeySize] = { '|', '-', 'A', 'M', 'S', '-', 'C', 'A', 'L', '0', '-', 'K', 'E', 'Y', '-', '|' };
|
||||
spl::AccessKey access_key;
|
||||
ON_SCOPE_EXIT { crypto::ClearMemory(std::addressof(access_key), sizeof(access_key)); };
|
||||
|
||||
/* Generate a personalized kek. */
|
||||
R_ABORT_UNLESS(spl::GenerateAesKek(std::addressof(access_key), SecureCalibrationBinaryBackupKeySource, sizeof(SecureCalibrationBinaryBackupKeySource), 0, 1));
|
||||
|
||||
/* Generate a personalized key. */
|
||||
R_ABORT_UNLESS(spl::GenerateAesKey(dst, dst_size, access_key, SecureCalibrationBinaryBackupKeySource, sizeof(SecureCalibrationBinaryBackupKeySource)));
|
||||
}
|
||||
|
||||
bool ReadStorageSecureCalibrationBinaryBackup(SecureCalibrationInfoBackup *out) {
|
||||
/* Read the data. */
|
||||
ReadStorageEncryptedSecureCalibrationBinaryBackupUnsafe(out);
|
||||
|
||||
/* Don't leak any data unless we validate. */
|
||||
auto clear_guard = SCOPE_GUARD { std::memset(out, 0, sizeof(*out)); };
|
||||
|
||||
{
|
||||
/* Create a buffer to hold our key. */
|
||||
u8 key[crypto::Aes128CtrDecryptor::KeySize];
|
||||
ON_SCOPE_EXIT { crypto::ClearMemory(key, sizeof(key)); };
|
||||
|
||||
/* Generate the key. */
|
||||
GenerateSecureCalibrationBinaryBackupKey(key, sizeof(key));
|
||||
|
||||
/* Decrypt the data in place. */
|
||||
crypto::DecryptAes128Ctr(out, sizeof(*out), key, sizeof(key), SecureCalibrationBinaryBackupIv, sizeof(SecureCalibrationBinaryBackupIv), out, sizeof(*out));
|
||||
}
|
||||
|
||||
/* Generate a hash for the data. */
|
||||
if (!IsValidSha256Hash(out->hash, std::addressof(out->info), sizeof(out->info))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Validate the backup. */
|
||||
if (!IsValidForSecureBackup(out->info)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Our backup is valid. */
|
||||
clear_guard.Cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
void WriteStorageSecureCalibrationBinaryBackup(SecureCalibrationInfoBackup *src) {
|
||||
/* Clear the input once we've written it. */
|
||||
ON_SCOPE_EXIT { std::memset(src, 0, sizeof(*src)); };
|
||||
|
||||
/* Ensure that the input is valid. */
|
||||
AMS_ABORT_UNLESS(IsValidForSecureBackup(src->info));
|
||||
|
||||
/* Set the Sha256 hash. */
|
||||
crypto::GenerateSha256(std::addressof(src->hash), sizeof(src->hash), std::addressof(src->info), sizeof(src->info));
|
||||
|
||||
/* Validate the hash. */
|
||||
AMS_ABORT_UNLESS(IsValidSha256Hash(src->hash, std::addressof(src->info), sizeof(src->info)));
|
||||
|
||||
/* Encrypt the data. */
|
||||
{
|
||||
/* Create a buffer to hold our key. */
|
||||
u8 key[crypto::Aes128CtrDecryptor::KeySize];
|
||||
ON_SCOPE_EXIT { crypto::ClearMemory(key, sizeof(key)); };
|
||||
|
||||
/* Generate the key. */
|
||||
GenerateSecureCalibrationBinaryBackupKey(key, sizeof(key));
|
||||
|
||||
/* Encrypt the data in place. */
|
||||
crypto::EncryptAes128Ctr(src, sizeof(*src), key, sizeof(key), SecureCalibrationBinaryBackupIv, sizeof(SecureCalibrationBinaryBackupIv), src, sizeof(*src));
|
||||
}
|
||||
|
||||
/* Write the encrypted data. */
|
||||
WriteStorageEncryptedSecureCalibrationBinaryBackupUnsafe(src);
|
||||
}
|
||||
|
||||
void GetBackupFileName(char *dst, size_t dst_size, const CalibrationInfo &info) {
|
||||
char sn[0x20] = {};
|
||||
ON_SCOPE_EXIT { std::memset(sn, 0, sizeof(sn)); };
|
||||
|
||||
|
||||
if (IsValidForSecureBackup(info)) {
|
||||
GetSerialNumber(sn, info);
|
||||
util::SNPrintf(dst, dst_size, "automatic_backups/%s_PRODINFO.bin", sn);
|
||||
} else {
|
||||
Sha256Hash hash;
|
||||
crypto::GenerateSha256(std::addressof(hash), sizeof(hash), std::addressof(info), sizeof(info));
|
||||
ON_SCOPE_EXIT { crypto::ClearMemory(std::addressof(hash), sizeof(hash)); };
|
||||
|
||||
if (IsValid(info)) {
|
||||
if (IsBlank(info)) {
|
||||
util::SNPrintf(dst, dst_size, "automatic_backups/BLANK_PRODINFO_%02X%02X%02X%02X.bin", hash.data[0], hash.data[1], hash.data[2], hash.data[3]);
|
||||
} else {
|
||||
GetSerialNumber(sn, info);
|
||||
util::SNPrintf(dst, dst_size, "automatic_backups/%s_PRODINFO_%02X%02X%02X%02X.bin", sn, hash.data[0], hash.data[1], hash.data[2], hash.data[3]);
|
||||
}
|
||||
} else {
|
||||
util::SNPrintf(dst, dst_size, "automatic_backups/INVALID_PRODINFO_%02X%02X%02X%02X.bin", hash.data[0], hash.data[1], hash.data[2], hash.data[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SafeRead(ams::fs::fsa::IFile *file, s64 offset, void *dst, size_t size) {
|
||||
size_t read_size = 0;
|
||||
R_ABORT_UNLESS(file->Read(std::addressof(read_size), offset, dst, size));
|
||||
AMS_ABORT_UNLESS(read_size == size);
|
||||
}
|
||||
|
||||
alignas(os::MemoryPageSize) CalibrationInfo g_temp_calibration_info = {};
|
||||
|
||||
void SaveProdInfoBackup(util::optional<ams::fs::FileStorage> *dst, const CalibrationInfo &info) {
|
||||
char backup_fn[0x100];
|
||||
GetBackupFileName(backup_fn, sizeof(backup_fn), info);
|
||||
|
||||
/* Create the file, in case it does not exist. */
|
||||
mitm::fs::CreateAtmosphereSdFile(backup_fn, sizeof(CalibrationInfo), ams::fs::CreateOption_None);
|
||||
|
||||
/* Open the file. */
|
||||
FsFile libnx_file;
|
||||
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(libnx_file), backup_fn, ams::fs::OpenMode_ReadWrite));
|
||||
|
||||
/* Create our accessor. */
|
||||
std::unique_ptr<ams::fs::fsa::IFile> file = std::make_unique<ams::fs::RemoteFile>(libnx_file);
|
||||
AMS_ABORT_UNLESS(file != nullptr);
|
||||
|
||||
/* Check if we're valid already. */
|
||||
bool valid = false;
|
||||
s64 size;
|
||||
R_ABORT_UNLESS(file->GetSize(std::addressof(size)));
|
||||
if (size == sizeof(CalibrationInfo)) {
|
||||
SafeRead(file.get(), 0, std::addressof(g_temp_calibration_info), sizeof(g_temp_calibration_info));
|
||||
ON_SCOPE_EXIT { std::memset(std::addressof(g_temp_calibration_info), 0, sizeof(g_temp_calibration_info)); };
|
||||
|
||||
if (std::memcmp(std::addressof(info), std::addressof(g_temp_calibration_info), sizeof(CalibrationInfo)) == 0) {
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we're not valid, we need to save. */
|
||||
if (!valid) {
|
||||
R_ABORT_UNLESS(file->Write(0, std::addressof(info), sizeof(info), ams::fs::WriteOption::Flush));
|
||||
}
|
||||
|
||||
/* Save our storage to output. */
|
||||
if (dst != nullptr) {
|
||||
dst->emplace(std::move(file));
|
||||
}
|
||||
}
|
||||
|
||||
void GetRandomEntropy(Sha256Hash *dst) {
|
||||
AMS_ASSERT(dst != nullptr);
|
||||
|
||||
u64 data_buffer[3] = {};
|
||||
ON_SCOPE_EXIT { crypto::ClearMemory(data_buffer, sizeof(data_buffer)); };
|
||||
|
||||
data_buffer[0] = os::GetSystemTick().GetInt64Value();
|
||||
R_ABORT_UNLESS(svc::GetInfo(data_buffer + 1, svc::InfoType_AliasRegionAddress, svc::PseudoHandle::CurrentProcess, 0));
|
||||
if (hos::GetVersion() >= hos::Version_2_0_0) {
|
||||
R_ABORT_UNLESS(svc::GetInfo(data_buffer + 2, svc::InfoType_RandomEntropy, svc::InvalidHandle, (data_buffer[0] ^ (data_buffer[1] >> 24)) & 3));
|
||||
} else {
|
||||
data_buffer[2] = os::GetSystemTick().GetInt64Value();
|
||||
}
|
||||
|
||||
return crypto::GenerateSha256(dst, sizeof(*dst), data_buffer, sizeof(data_buffer));
|
||||
}
|
||||
|
||||
void FillWithGarbage(void *dst, size_t dst_size) {
|
||||
/* Get random entropy. */
|
||||
Sha256Hash entropy;
|
||||
ON_SCOPE_EXIT { crypto::ClearMemory(std::addressof(entropy), sizeof(entropy)); };
|
||||
GetRandomEntropy(std::addressof(entropy));
|
||||
|
||||
/* Clear dst. */
|
||||
std::memset(dst, 0xCC, dst_size);
|
||||
|
||||
/* Encrypt dst. */
|
||||
static_assert(sizeof(entropy) == crypto::Aes128CtrEncryptor::KeySize + crypto::Aes128CtrEncryptor::IvSize);
|
||||
crypto::EncryptAes128Ctr(dst, dst_size, entropy.data, crypto::Aes128CtrEncryptor::KeySize, entropy.data + crypto::Aes128CtrEncryptor::KeySize, crypto::Aes128CtrEncryptor::IvSize, dst, dst_size);
|
||||
}
|
||||
|
||||
alignas(os::MemoryPageSize) constinit CalibrationInfo g_calibration_info = {};
|
||||
alignas(os::MemoryPageSize) constinit CalibrationInfo g_blank_calibration_info = {};
|
||||
alignas(os::MemoryPageSize) constinit SecureCalibrationInfoBackup g_secure_calibration_info_backup = {};
|
||||
|
||||
constinit util::optional<ams::fs::FileStorage> g_prodinfo_backup_file;
|
||||
constinit util::optional<ams::fs::MemoryStorage> g_blank_prodinfo_storage;
|
||||
constinit util::optional<ams::fs::MemoryStorage> g_fake_secure_backup_storage;
|
||||
|
||||
constinit bool g_allow_writes = false;
|
||||
constinit bool g_has_secure_backup = false;
|
||||
|
||||
constinit os::SdkMutex g_prodinfo_management_lock;
|
||||
|
||||
}
|
||||
|
||||
void InitializeProdInfoManagement() {
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
|
||||
/* First, get our options. */
|
||||
const bool should_blank = exosphere::ShouldBlankProdInfo();
|
||||
bool allow_writes = exosphere::ShouldAllowWritesToProdInfo();
|
||||
|
||||
/* Next, read our prodinfo. */
|
||||
ReadStorageCalibrationBinary(std::addressof(g_calibration_info));
|
||||
|
||||
/* Next, check if we have a secure backup. */
|
||||
bool has_secure_backup = ReadStorageSecureCalibrationBinaryBackup(std::addressof(g_secure_calibration_info_backup));
|
||||
|
||||
/* Only allow writes if we have a secure backup. */
|
||||
if (allow_writes && !has_secure_backup) {
|
||||
/* If we can make a secure backup, great. */
|
||||
if (IsValidForSecureBackup(g_calibration_info)) {
|
||||
g_secure_calibration_info_backup.info = g_calibration_info;
|
||||
WriteStorageSecureCalibrationBinaryBackup(std::addressof(g_secure_calibration_info_backup));
|
||||
g_secure_calibration_info_backup.info = g_calibration_info;
|
||||
has_secure_backup = true;
|
||||
} else {
|
||||
/* Don't allow writes if we can't make a secure backup. */
|
||||
allow_writes = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure our preconditions are met. */
|
||||
AMS_ABORT_UNLESS(!allow_writes || has_secure_backup);
|
||||
|
||||
/* Set globals. */
|
||||
g_allow_writes = allow_writes;
|
||||
g_has_secure_backup = has_secure_backup;
|
||||
|
||||
/* If we should blank, do so. */
|
||||
if (should_blank) {
|
||||
g_blank_calibration_info = g_calibration_info;
|
||||
Blank(g_blank_calibration_info);
|
||||
g_blank_prodinfo_storage.emplace(std::addressof(g_blank_calibration_info), sizeof(g_blank_calibration_info));
|
||||
}
|
||||
|
||||
/* Ensure that we have a blank file only if we need one. */
|
||||
AMS_ABORT_UNLESS(should_blank == static_cast<bool>(g_blank_prodinfo_storage));
|
||||
}
|
||||
|
||||
void SaveProdInfoBackupsAndWipeMemory(char *out_name, size_t out_name_size) {
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
|
||||
ON_SCOPE_EXIT {
|
||||
FillWithGarbage(std::addressof(g_calibration_info), sizeof(g_calibration_info));
|
||||
FillWithGarbage(std::addressof(g_secure_calibration_info_backup), sizeof(g_secure_calibration_info_backup));
|
||||
};
|
||||
|
||||
/* Save our backup. We always prefer to save a secure copy of data over a non-secure one. */
|
||||
if (g_has_secure_backup) {
|
||||
GetSerialNumber(out_name, g_secure_calibration_info_backup.info);
|
||||
SaveProdInfoBackup(std::addressof(g_prodinfo_backup_file), g_secure_calibration_info_backup.info);
|
||||
} else {
|
||||
if (IsValid(g_calibration_info) && !IsBlank(g_calibration_info)) {
|
||||
GetSerialNumber(out_name, g_calibration_info);
|
||||
} else {
|
||||
Sha256Hash hash;
|
||||
ON_SCOPE_EXIT { crypto::ClearMemory(std::addressof(hash), sizeof(hash)); };
|
||||
crypto::GenerateSha256(std::addressof(hash), sizeof(hash), std::addressof(g_calibration_info), sizeof(g_calibration_info));
|
||||
|
||||
util::SNPrintf(out_name, out_name_size, "%02X%02X%02X%02X", hash.data[0], hash.data[1], hash.data[2], hash.data[3]);
|
||||
}
|
||||
SaveProdInfoBackup(std::addressof(g_prodinfo_backup_file), g_calibration_info);
|
||||
}
|
||||
|
||||
/* Ensure we made our backup. */
|
||||
AMS_ABORT_UNLESS(g_prodinfo_backup_file);
|
||||
|
||||
/* Setup our memory storage. */
|
||||
g_fake_secure_backup_storage.emplace(std::addressof(g_secure_calibration_info_backup), sizeof(g_secure_calibration_info_backup));
|
||||
|
||||
/* Ensure that we have a fake storage. */
|
||||
AMS_ABORT_UNLESS(static_cast<bool>(g_fake_secure_backup_storage));
|
||||
}
|
||||
|
||||
bool ShouldReadBlankCalibrationBinary() {
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
return static_cast<bool>(g_blank_prodinfo_storage);
|
||||
}
|
||||
|
||||
bool IsWriteToCalibrationBinaryAllowed() {
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
return g_allow_writes;
|
||||
}
|
||||
|
||||
void ReadFromBlankCalibrationBinary(s64 offset, void *dst, size_t size) {
|
||||
AMS_ABORT_UNLESS(ShouldReadBlankCalibrationBinary());
|
||||
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
R_ABORT_UNLESS(g_blank_prodinfo_storage->Read(offset, dst, size));
|
||||
}
|
||||
|
||||
void WriteToBlankCalibrationBinary(s64 offset, const void *src, size_t size) {
|
||||
AMS_ABORT_UNLESS(ShouldReadBlankCalibrationBinary());
|
||||
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
R_ABORT_UNLESS(g_blank_prodinfo_storage->Write(offset, src, size));
|
||||
}
|
||||
|
||||
void ReadFromFakeSecureBackupStorage(s64 offset, void *dst, size_t size) {
|
||||
AMS_ABORT_UNLESS(IsWriteToCalibrationBinaryAllowed());
|
||||
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
R_ABORT_UNLESS(g_fake_secure_backup_storage->Read(offset, dst, size));
|
||||
}
|
||||
|
||||
void WriteToFakeSecureBackupStorage(s64 offset, const void *src, size_t size) {
|
||||
AMS_ABORT_UNLESS(IsWriteToCalibrationBinaryAllowed());
|
||||
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
R_ABORT_UNLESS(g_fake_secure_backup_storage->Write(offset, src, size));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm {
|
||||
|
||||
constexpr inline size_t CalibrationBinarySize = 0x8000;
|
||||
|
||||
constexpr inline s64 SecureCalibrationInfoBackupOffset = 3_MB;
|
||||
constexpr inline size_t SecureCalibrationBinaryBackupSize = 0xC000;
|
||||
|
||||
void InitializeProdInfoManagement();
|
||||
|
||||
void SaveProdInfoBackupsAndWipeMemory(char *out_name, size_t out_name_size);
|
||||
|
||||
bool ShouldReadBlankCalibrationBinary();
|
||||
bool IsWriteToCalibrationBinaryAllowed();
|
||||
|
||||
void ReadFromBlankCalibrationBinary(s64 offset, void *dst, size_t size);
|
||||
void WriteToBlankCalibrationBinary(s64 offset, const void *src, size_t size);
|
||||
|
||||
void ReadFromFakeSecureBackupStorage(s64 offset, void *dst, size_t size);
|
||||
void WriteToFakeSecureBackupStorage(s64 offset, const void *src, size_t size);
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "bpc_ams_module.hpp"
|
||||
#include "bpc_ams_service.hpp"
|
||||
|
||||
namespace ams::mitm::bpc_ams {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr sm::ServiceName AtmosphereServiceName = sm::ServiceName::Encode("bpc:ams");
|
||||
constexpr size_t AtmosphereMaxSessions = 4;
|
||||
|
||||
constexpr size_t MaxServers = 1;
|
||||
constexpr size_t MaxSessions = AtmosphereMaxSessions;
|
||||
using ServerOptions = sf::hipc::DefaultServerManagerOptions;
|
||||
sf::hipc::ServerManager<MaxServers, ServerOptions, MaxSessions> g_server_manager;
|
||||
|
||||
constinit sf::UnmanagedServiceObject<bpc::impl::IAtmosphereInterface, bpc::AtmosphereService> g_ams_service_object;
|
||||
|
||||
}
|
||||
|
||||
void MitmModule::ThreadFunction(void *) {
|
||||
/* Create bpc:ams. */
|
||||
{
|
||||
os::NativeHandle bpcams_h;
|
||||
R_ABORT_UNLESS(svc::ManageNamedPort(&bpcams_h, AtmosphereServiceName.name, AtmosphereMaxSessions));
|
||||
g_server_manager.RegisterObjectForServer(g_ams_service_object.GetShared(), bpcams_h);
|
||||
}
|
||||
|
||||
/* Loop forever, servicing our services. */
|
||||
g_server_manager.LoopProcess();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_module.hpp"
|
||||
|
||||
namespace ams::mitm::bpc_ams {
|
||||
|
||||
DEFINE_MITM_MODULE_CLASS(0x8000, AMS_GET_SYSTEM_THREAD_PRIORITY(bpc, IpcServer));
|
||||
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "bpc_ams_power_utils.hpp"
|
||||
#include "../amsmitm_fs_utils.hpp"
|
||||
|
||||
namespace ams::mitm::bpc {
|
||||
|
||||
namespace {
|
||||
|
||||
/* Convenience definitions. */
|
||||
constexpr uintptr_t IramBase = 0x40000000ull;
|
||||
constexpr uintptr_t IramPayloadBase = 0x40010000ull;
|
||||
constexpr size_t IramSize = 0x40000;
|
||||
constexpr size_t IramPayloadMaxSize = 0x24000;
|
||||
constexpr size_t IramFatalErrorContextOffset = 0x2E000;
|
||||
|
||||
/* Helper enum. */
|
||||
enum class RebootType : u32 {
|
||||
Standard,
|
||||
ToRcm,
|
||||
ToPayload,
|
||||
ByPmic,
|
||||
};
|
||||
|
||||
/* Globals. */
|
||||
alignas(os::MemoryPageSize) u8 g_work_page[os::MemoryPageSize];
|
||||
alignas(os::MemoryPageSize) u8 g_reboot_payload[IramPayloadMaxSize];
|
||||
RebootType g_reboot_type = RebootType::ToRcm;
|
||||
|
||||
/* Helpers. */
|
||||
void ClearIram() {
|
||||
/* Make page CCs. */
|
||||
std::memset(g_work_page, 0xCC, sizeof(g_work_page));
|
||||
|
||||
/* Overwrite all of IRAM with CCs. */
|
||||
for (size_t ofs = 0; ofs < IramSize; ofs += sizeof(g_work_page)) {
|
||||
exosphere::CopyToIram(IramBase + ofs, g_work_page, sizeof(g_work_page));
|
||||
}
|
||||
}
|
||||
|
||||
void DoRebootToPayload() {
|
||||
/* Ensure clean IRAM state. */
|
||||
ClearIram();
|
||||
|
||||
/* Copy in payload. */
|
||||
for (size_t ofs = 0; ofs < sizeof(g_reboot_payload); ofs += sizeof(g_work_page)) {
|
||||
std::memcpy(g_work_page, g_reboot_payload + ofs, std::min(sizeof(g_reboot_payload) - ofs, sizeof(g_work_page)));
|
||||
exosphere::CopyToIram(IramPayloadBase + ofs, g_work_page, sizeof(g_work_page));
|
||||
}
|
||||
|
||||
exosphere::ForceRebootToIramPayload();
|
||||
}
|
||||
|
||||
void DoRebootToFatalError(const ams::FatalErrorContext *ctx) {
|
||||
/* Ensure clean IRAM state. */
|
||||
ClearIram();
|
||||
|
||||
/* Copy in payload. */
|
||||
for (size_t ofs = 0; ofs < sizeof(g_reboot_payload); ofs += sizeof(g_work_page)) {
|
||||
std::memcpy(g_work_page, g_reboot_payload + ofs, std::min(sizeof(g_reboot_payload) - ofs, sizeof(g_work_page)));
|
||||
exosphere::CopyToIram(IramPayloadBase + ofs, g_work_page, sizeof(g_work_page));
|
||||
}
|
||||
|
||||
/* Copy in fatal error context, if relevant. */
|
||||
if (ctx != nullptr) {
|
||||
std::memset(g_work_page, 0xCC, sizeof(g_work_page));
|
||||
std::memcpy(g_work_page, ctx, sizeof(*ctx));
|
||||
exosphere::CopyToIram(IramPayloadBase + IramFatalErrorContextOffset, g_work_page, sizeof(g_work_page));
|
||||
}
|
||||
|
||||
exosphere::ForceRebootToFatalError();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Power utilities. */
|
||||
bool IsRebootManaged() {
|
||||
return g_reboot_type != RebootType::Standard;
|
||||
}
|
||||
|
||||
void RebootSystem() {
|
||||
switch (g_reboot_type) {
|
||||
case RebootType::ByPmic:
|
||||
exosphere::ForceRebootByPmic();
|
||||
break;
|
||||
case RebootType::ToRcm:
|
||||
exosphere::ForceRebootToRcm();
|
||||
break;
|
||||
case RebootType::ToPayload:
|
||||
default: /* This should never be called with ::Standard */
|
||||
DoRebootToPayload();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ShutdownSystem() {
|
||||
exosphere::ForceShutdown();
|
||||
}
|
||||
|
||||
/* Atmosphere power utilities. */
|
||||
void RebootForFatalError(const ams::FatalErrorContext *ctx) {
|
||||
DoRebootToFatalError(ctx);
|
||||
}
|
||||
|
||||
void SetRebootPayload(const void *payload, size_t payload_size) {
|
||||
/* Mariko does not support reboot-to-payload. */
|
||||
if (spl::GetSocType() == spl::SocType_Mariko) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Clear payload buffer */
|
||||
std::memset(g_reboot_payload, 0xCC, sizeof(g_reboot_payload));
|
||||
|
||||
/* Ensure valid. */
|
||||
AMS_ABORT_UNLESS(payload != nullptr && payload_size <= sizeof(g_reboot_payload));
|
||||
|
||||
/* Copy in payload. */
|
||||
std::memcpy(g_reboot_payload, payload, payload_size);
|
||||
|
||||
/* Note to the secure monitor that we have a payload. */
|
||||
spl::smc::AsyncOperationKey dummy;
|
||||
spl::smc::SetConfig(std::addressof(dummy), spl::ConfigItem::ExospherePayloadAddress, nullptr, 0, g_reboot_payload);
|
||||
|
||||
/* NOTE: Preferred reboot type may be overrwritten when parsed from settings during boot. */
|
||||
g_reboot_type = RebootType::ToPayload;
|
||||
}
|
||||
|
||||
Result LoadRebootPayload() {
|
||||
/* Mariko does not support reboot-to-payload. */
|
||||
R_SUCCEED_IF(spl::GetSocType() == spl::SocType_Mariko)
|
||||
|
||||
/* Clear payload buffer */
|
||||
std::memset(g_reboot_payload, 0xCC, sizeof(g_reboot_payload));
|
||||
|
||||
/* Open payload file. */
|
||||
FsFile payload_file;
|
||||
R_TRY(fs::OpenAtmosphereSdFile(std::addressof(payload_file), "/reboot_payload.bin", ams::fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fsFileClose(std::addressof(payload_file)); };
|
||||
|
||||
/* Read payload file. Discard result. */
|
||||
{
|
||||
size_t actual_size;
|
||||
fsFileRead(std::addressof(payload_file), 0, g_reboot_payload, sizeof(g_reboot_payload), FsReadOption_None, std::addressof(actual_size));
|
||||
}
|
||||
|
||||
/* NOTE: Preferred reboot type will be parsed from settings later on. */
|
||||
g_reboot_type = RebootType::ToPayload;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result DetectPreferredRebootFunctionality() {
|
||||
char reboot_type[0x40] = {};
|
||||
settings::fwdbg::GetSettingsItemValue(reboot_type, sizeof(reboot_type) - 1, "atmosphere", "power_menu_reboot_function");
|
||||
|
||||
if (strcasecmp(reboot_type, "stock") == 0 || strcasecmp(reboot_type, "normal") == 0 || strcasecmp(reboot_type, "standard") == 0) {
|
||||
g_reboot_type = RebootType::Standard;
|
||||
} else if (strcasecmp(reboot_type, "rcm") == 0) {
|
||||
g_reboot_type = RebootType::ToRcm;
|
||||
} else if (strcasecmp(reboot_type, "payload") == 0) {
|
||||
g_reboot_type = RebootType::ToPayload;
|
||||
}
|
||||
|
||||
/* TODO: Should we actually allow control over this on mariko? */
|
||||
if (spl::GetSocType() == spl::SocType_Mariko) {
|
||||
g_reboot_type = RebootType::ByPmic;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::bpc {
|
||||
|
||||
/* Power utilities. */
|
||||
bool IsRebootManaged();
|
||||
void RebootSystem();
|
||||
void ShutdownSystem();
|
||||
|
||||
/* Atmosphere power utilities. */
|
||||
void SetRebootPayload(const void *payload, size_t payload_size);
|
||||
Result LoadRebootPayload();
|
||||
Result DetectPreferredRebootFunctionality();
|
||||
void RebootForFatalError(const ams::FatalErrorContext *ctx);
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "bpc_ams_service.hpp"
|
||||
#include "bpc_ams_power_utils.hpp"
|
||||
|
||||
namespace ams::mitm::bpc {
|
||||
|
||||
namespace {
|
||||
|
||||
bool g_set_initial_payload = false;
|
||||
|
||||
}
|
||||
|
||||
void AtmosphereService::RebootToFatalError(const ams::FatalErrorContext &ctx) {
|
||||
bpc::RebootForFatalError(std::addressof(ctx));
|
||||
}
|
||||
|
||||
void AtmosphereService::SetRebootPayload(const ams::sf::InBuffer &payload) {
|
||||
/* Set the reboot payload. */
|
||||
bpc::SetRebootPayload(payload.GetPointer(), payload.GetSize());
|
||||
|
||||
/* If this is being called for the first time (by boot sysmodule), */
|
||||
/* Then we should kick off the rest of init. */
|
||||
if (!g_set_initial_payload) {
|
||||
g_set_initial_payload = true;
|
||||
|
||||
/* Start the initialization process. */
|
||||
::ams::mitm::StartInitialize();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#define AMS_BPC_MITM_ATMOSPHERE_INTERFACE_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 65000, void, RebootToFatalError, (const ams::FatalErrorContext &ctx), (ctx)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 65001, void, SetRebootPayload, (const ams::sf::InBuffer &payload), (payload))
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::mitm::bpc::impl, IAtmosphereInterface, AMS_BPC_MITM_ATMOSPHERE_INTERFACE_INTERFACE_INFO, 0x00000000)
|
||||
|
||||
namespace ams::mitm::bpc {
|
||||
|
||||
class AtmosphereService {
|
||||
public:
|
||||
void RebootToFatalError(const ams::FatalErrorContext &ctx);
|
||||
void SetRebootPayload(const ams::sf::InBuffer &payload);
|
||||
};
|
||||
static_assert(impl::IsIAtmosphereInterface<AtmosphereService>);
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "bpc_mitm_service.hpp"
|
||||
#include "bpc_ams_power_utils.hpp"
|
||||
|
||||
namespace ams::mitm::bpc {
|
||||
|
||||
Result BpcMitmService::RebootSystem() {
|
||||
R_UNLESS(bpc::IsRebootManaged(), sm::mitm::ResultShouldForwardToSession());
|
||||
bpc::RebootSystem();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result BpcMitmService::ShutdownSystem() {
|
||||
bpc::ShutdownSystem();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#define AMS_BPC_MITM_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, ShutdownSystem, (), ()) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, RebootSystem, (), ())
|
||||
|
||||
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::bpc::impl, IBpcMitmInterface, AMS_BPC_MITM_INTERFACE_INFO, 0xF6C277FD)
|
||||
|
||||
namespace ams::mitm::bpc {
|
||||
|
||||
class BpcMitmService : public sf::MitmServiceImplBase {
|
||||
public:
|
||||
using MitmServiceImplBase::MitmServiceImplBase;
|
||||
public:
|
||||
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
|
||||
/* We will mitm:
|
||||
* - am (omm on 14.0.0+), to intercept the Reboot/Power buttons in the overlay menu.
|
||||
* - fatal, to simplify payload reboot logic significantly
|
||||
* - hbl, to allow homebrew to take advantage of the feature.
|
||||
*/
|
||||
return client_info.program_id == ncm::SystemProgramId::Am ||
|
||||
client_info.program_id == ncm::SystemProgramId::Omm ||
|
||||
client_info.program_id == ncm::SystemProgramId::Fatal ||
|
||||
client_info.override_status.IsHbl();
|
||||
}
|
||||
public:
|
||||
/* Overridden commands. */
|
||||
Result ShutdownSystem();
|
||||
Result RebootSystem();
|
||||
};
|
||||
static_assert(impl::IsIBpcMitmInterface<BpcMitmService>);
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "bpcmitm_module.hpp"
|
||||
#include "bpc_mitm_service.hpp"
|
||||
|
||||
namespace ams::mitm::bpc {
|
||||
|
||||
namespace {
|
||||
|
||||
enum PortIndex {
|
||||
PortIndex_Mitm,
|
||||
PortIndex_Count,
|
||||
};
|
||||
|
||||
constexpr sm::ServiceName MitmServiceName = sm::ServiceName::Encode("bpc");
|
||||
constexpr sm::ServiceName DeprecatedMitmServiceName = sm::ServiceName::Encode("bpc:c");
|
||||
constexpr size_t MitmServiceMaxSessions = 13;
|
||||
|
||||
constexpr size_t MaxSessions = MitmServiceMaxSessions;
|
||||
|
||||
struct ServerOptions {
|
||||
static constexpr size_t PointerBufferSize = sf::hipc::DefaultServerManagerOptions::PointerBufferSize;
|
||||
static constexpr size_t MaxDomains = sf::hipc::DefaultServerManagerOptions::MaxDomains;
|
||||
static constexpr size_t MaxDomainObjects = sf::hipc::DefaultServerManagerOptions::MaxDomainObjects;
|
||||
static constexpr bool CanDeferInvokeRequest = sf::hipc::DefaultServerManagerOptions::CanDeferInvokeRequest;
|
||||
static constexpr bool CanManageMitmServers = true;
|
||||
};
|
||||
|
||||
class ServerManager final : public sf::hipc::ServerManager<PortIndex_Count, ServerOptions, MaxSessions> {
|
||||
private:
|
||||
virtual Result OnNeedsToAccept(int port_index, Server *server) override;
|
||||
};
|
||||
|
||||
ServerManager g_server_manager;
|
||||
|
||||
Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
|
||||
/* Acknowledge the mitm session. */
|
||||
std::shared_ptr<::Service> fsrv;
|
||||
sm::MitmProcessInfo client_info;
|
||||
server->AcknowledgeMitmSession(std::addressof(fsrv), std::addressof(client_info));
|
||||
|
||||
switch (port_index) {
|
||||
case PortIndex_Mitm:
|
||||
R_RETURN(this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<impl::IBpcMitmInterface, BpcMitmService>(decltype(fsrv)(fsrv), client_info), fsrv));
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MitmModule::ThreadFunction(void *) {
|
||||
/* Wait until initialization is complete. */
|
||||
mitm::WaitInitialized();
|
||||
|
||||
/* Create bpc mitm. */
|
||||
const sm::ServiceName service_name = (hos::GetVersion() >= hos::Version_2_0_0) ? MitmServiceName : DeprecatedMitmServiceName;
|
||||
|
||||
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<BpcMitmService>(PortIndex_Mitm, service_name)));
|
||||
|
||||
/* Loop forever, servicing our services. */
|
||||
g_server_manager.LoopProcess();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_module.hpp"
|
||||
|
||||
namespace ams::mitm::bpc {
|
||||
|
||||
DEFINE_MITM_MODULE_CLASS(0x8000, AMS_GET_SYSTEM_THREAD_PRIORITY(bpc, IpcServer));
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_fs_utils.hpp"
|
||||
#include "dnsmitm_debug.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver {
|
||||
|
||||
namespace {
|
||||
|
||||
constinit os::SdkMutex g_log_mutex;
|
||||
constinit bool g_log_enabled;
|
||||
|
||||
constinit ::FsFile g_log_file;
|
||||
constinit s64 g_log_ofs;
|
||||
|
||||
|
||||
constinit char g_log_buf[0x400];
|
||||
|
||||
}
|
||||
|
||||
void InitializeDebug(bool enable_log) {
|
||||
{
|
||||
std::scoped_lock lk(g_log_mutex);
|
||||
|
||||
g_log_enabled = enable_log;
|
||||
|
||||
if (g_log_enabled) {
|
||||
/* Create the logs directory. */
|
||||
mitm::fs::CreateAtmosphereSdDirectory("/logs");
|
||||
|
||||
/* Create the log file. */
|
||||
mitm::fs::CreateAtmosphereSdFile("/logs/dns_mitm_debug.log", 0, ams::fs::CreateOption_None);
|
||||
|
||||
/* Open the log file. */
|
||||
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(g_log_file), "/logs/dns_mitm_debug.log", ams::fs::OpenMode_ReadWrite | ams::fs::OpenMode_AllowAppend));
|
||||
|
||||
/* Get the current log offset. */
|
||||
R_ABORT_UNLESS(::fsFileGetSize(std::addressof(g_log_file), std::addressof(g_log_ofs)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Start a new log. */
|
||||
LogDebug("\n---\n");
|
||||
}
|
||||
|
||||
void LogDebug(const char *fmt, ...) {
|
||||
std::scoped_lock lk(g_log_mutex);
|
||||
|
||||
if (g_log_enabled) {
|
||||
int len = 0;
|
||||
{
|
||||
std::va_list vl;
|
||||
va_start(vl, fmt);
|
||||
len = util::VSNPrintf(g_log_buf, sizeof(g_log_buf), fmt, vl);
|
||||
va_end(vl);
|
||||
}
|
||||
|
||||
R_ABORT_UNLESS(::fsFileWrite(std::addressof(g_log_file), g_log_ofs, g_log_buf, len, FsWriteOption_Flush));
|
||||
g_log_ofs += len;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::socket::resolver {
|
||||
|
||||
void InitializeDebug(bool enable_log);
|
||||
|
||||
void LogDebug(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||
|
||||
}
|
||||
@@ -1,399 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_fs_utils.hpp"
|
||||
#include "dnsmitm_debug.hpp"
|
||||
#include "dnsmitm_host_redirection.hpp"
|
||||
#include "socket_allocator.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver {
|
||||
|
||||
namespace {
|
||||
|
||||
/* https://github.com/clibs/wildcardcmp */
|
||||
constexpr int wildcardcmp(const char *pattern, const char *string) {
|
||||
const char *w = nullptr; /* last `*` */
|
||||
const char *s = nullptr; /* last checked char */
|
||||
|
||||
/* malformed */
|
||||
if (!pattern || !string) return 0;
|
||||
|
||||
/* loop 1 char at a time */
|
||||
while (1) {
|
||||
if (!*string) {
|
||||
if (!*pattern) return 1;
|
||||
if ('*' == *pattern) return 1;
|
||||
if (!s || !*s) return 0;
|
||||
string = s++;
|
||||
pattern = w;
|
||||
continue;
|
||||
} else {
|
||||
if (*pattern != *string) {
|
||||
if ('*' == *pattern) {
|
||||
w = ++pattern;
|
||||
s = string;
|
||||
/* "*" -> "foobar" */
|
||||
if (*pattern) continue;
|
||||
return 1;
|
||||
} else if (w) {
|
||||
string++;
|
||||
/* "*ooba*" -> "foobar" */
|
||||
continue;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
string++;
|
||||
pattern++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
constexpr const char DefaultHostsFile[] =
|
||||
"# Nintendo telemetry servers\n"
|
||||
"127.0.0.1 receive-%.dg.srv.nintendo.net receive-%.er.srv.nintendo.net\n";
|
||||
|
||||
constinit os::SdkMutex g_redirection_lock;
|
||||
std::vector<std::pair<std::string, ams::socket::InAddrT>> g_redirection_list;
|
||||
|
||||
void RemoveRedirection(const char *hostname) {
|
||||
for (auto it = g_redirection_list.begin(); it != g_redirection_list.end(); ++it) {
|
||||
if (std::strcmp(it->first.c_str(), hostname) == 0) {
|
||||
g_redirection_list.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddRedirection(const char *hostname, ams::socket::InAddrT addr) {
|
||||
RemoveRedirection(hostname);
|
||||
g_redirection_list.emplace(g_redirection_list.begin(), std::string(hostname), addr);
|
||||
}
|
||||
|
||||
constinit char g_specific_emummc_hosts_path[0x40] = {};
|
||||
|
||||
void ParseHostsFile(const char *file_data) {
|
||||
/* Get the environment identifier from settings. */
|
||||
const auto env = ams::nsd::impl::device::GetEnvironmentIdentifierFromSettings();
|
||||
const auto env_len = std::strlen(env.value);
|
||||
|
||||
/* Parse the file. */
|
||||
enum class State {
|
||||
IgnoredLine,
|
||||
BeginLine,
|
||||
Ip1,
|
||||
IpDot1,
|
||||
Ip2,
|
||||
IpDot2,
|
||||
Ip3,
|
||||
IpDot3,
|
||||
Ip4,
|
||||
WhiteSpace,
|
||||
HostName,
|
||||
};
|
||||
|
||||
ams::socket::InAddrT current_address{};
|
||||
char current_hostname[0x200];
|
||||
u32 work{};
|
||||
|
||||
State state = State::BeginLine;
|
||||
for (const char *cur = file_data; *cur != '\x00'; ++cur) {
|
||||
const char c = *cur;
|
||||
switch (state) {
|
||||
case State::IgnoredLine:
|
||||
if (c == '\n') {
|
||||
state = State::BeginLine;
|
||||
}
|
||||
break;
|
||||
case State::BeginLine:
|
||||
if (std::isdigit(static_cast<unsigned char>(c))) {
|
||||
current_address = 0;
|
||||
work = static_cast<u32>(c - '0');
|
||||
state = State::Ip1;
|
||||
} else if (c == '\n') {
|
||||
state = State::BeginLine;
|
||||
} else {
|
||||
state = State::IgnoredLine;
|
||||
}
|
||||
break;
|
||||
case State::Ip1:
|
||||
if (std::isdigit(static_cast<unsigned char>(c))) {
|
||||
work *= 10;
|
||||
work += static_cast<u32>(c - '0');
|
||||
} else if (c == '.') {
|
||||
current_address |= (work & 0xFF) << 0;
|
||||
work = 0;
|
||||
state = State::IpDot1;
|
||||
} else {
|
||||
state = State::IgnoredLine;
|
||||
}
|
||||
break;
|
||||
case State::IpDot1:
|
||||
if (std::isdigit(static_cast<unsigned char>(c))) {
|
||||
work = static_cast<u32>(c - '0');
|
||||
state = State::Ip2;
|
||||
} else {
|
||||
state = State::IgnoredLine;
|
||||
}
|
||||
break;
|
||||
case State::Ip2:
|
||||
if (std::isdigit(static_cast<unsigned char>(c))) {
|
||||
work *= 10;
|
||||
work += static_cast<u32>(c - '0');
|
||||
} else if (c == '.') {
|
||||
current_address |= (work & 0xFF) << 8;
|
||||
work = 0;
|
||||
state = State::IpDot2;
|
||||
} else {
|
||||
state = State::IgnoredLine;
|
||||
}
|
||||
break;
|
||||
case State::IpDot2:
|
||||
if (std::isdigit(static_cast<unsigned char>(c))) {
|
||||
work = static_cast<u32>(c - '0');
|
||||
state = State::Ip3;
|
||||
} else {
|
||||
state = State::IgnoredLine;
|
||||
}
|
||||
break;
|
||||
case State::Ip3:
|
||||
if (std::isdigit(static_cast<unsigned char>(c))) {
|
||||
work *= 10;
|
||||
work += static_cast<u32>(c - '0');
|
||||
} else if (c == '.') {
|
||||
current_address |= (work & 0xFF) << 16;
|
||||
work = 0;
|
||||
state = State::IpDot3;
|
||||
} else {
|
||||
state = State::IgnoredLine;
|
||||
}
|
||||
break;
|
||||
case State::IpDot3:
|
||||
if (std::isdigit(static_cast<unsigned char>(c))) {
|
||||
work = static_cast<u32>(c - '0');
|
||||
state = State::Ip4;
|
||||
} else {
|
||||
state = State::IgnoredLine;
|
||||
}
|
||||
break;
|
||||
case State::Ip4:
|
||||
if (std::isdigit(static_cast<unsigned char>(c))) {
|
||||
work *= 10;
|
||||
work += static_cast<u32>(c - '0');
|
||||
} else if (c == ' ' || c == '\t') {
|
||||
current_address |= (work & 0xFF) << 24;
|
||||
work = 0;
|
||||
state = State::WhiteSpace;
|
||||
} else {
|
||||
state = State::IgnoredLine;
|
||||
}
|
||||
break;
|
||||
case State::WhiteSpace:
|
||||
if (c == '\n') {
|
||||
state = State::BeginLine;
|
||||
} else if (c != ' ' && c != '\r' && c != '\t') {
|
||||
if (c == '%') {
|
||||
std::memcpy(current_hostname, env.value, env_len);
|
||||
work = env_len;
|
||||
} else {
|
||||
current_hostname[0] = c;
|
||||
work = 1;
|
||||
}
|
||||
state = State::HostName;
|
||||
}
|
||||
break;
|
||||
case State::HostName:
|
||||
if (c == ' ' || c == '\r' || c == '\n' || c == '\t') {
|
||||
AMS_ABORT_UNLESS(work < sizeof(current_hostname));
|
||||
current_hostname[work] = '\x00';
|
||||
|
||||
AddRedirection(current_hostname, current_address);
|
||||
work = 0;
|
||||
|
||||
if (c == '\n') {
|
||||
state = State::BeginLine;
|
||||
} else {
|
||||
state = State::WhiteSpace;
|
||||
}
|
||||
} else if (c == '%') {
|
||||
AMS_ABORT_UNLESS(work < sizeof(current_hostname) - env_len);
|
||||
std::memcpy(current_hostname + work, env.value, env_len);
|
||||
work += env_len;
|
||||
} else {
|
||||
AMS_ABORT_UNLESS(work < sizeof(current_hostname) - 1);
|
||||
current_hostname[work++] = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state == State::HostName) {
|
||||
AMS_ABORT_UNLESS(work < sizeof(current_hostname));
|
||||
current_hostname[work] = '\x00';
|
||||
|
||||
AddRedirection(current_hostname, current_address);
|
||||
}
|
||||
}
|
||||
|
||||
void Log(::FsFile &f, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
|
||||
void Log(::FsFile &f, const char *fmt, ...) {
|
||||
char log_buf[0x100];
|
||||
int len = 0;
|
||||
{
|
||||
std::va_list vl;
|
||||
va_start(vl, fmt);
|
||||
len = util::VSNPrintf(log_buf, sizeof(log_buf), fmt, vl);
|
||||
va_end(vl);
|
||||
}
|
||||
|
||||
s64 ofs;
|
||||
R_ABORT_UNLESS(::fsFileGetSize(std::addressof(f), std::addressof(ofs)));
|
||||
R_ABORT_UNLESS(::fsFileWrite(std::addressof(f), ofs, log_buf, len, FsWriteOption_Flush));
|
||||
}
|
||||
|
||||
const char *SelectHostsFile(::FsFile &log_file) {
|
||||
Log(log_file, "Selecting hosts file...\n");
|
||||
const bool is_emummc = emummc::IsActive();
|
||||
const u32 emummc_id = emummc::GetActiveId();
|
||||
util::SNPrintf(g_specific_emummc_hosts_path, sizeof(g_specific_emummc_hosts_path), "/hosts/emummc_%04x.txt", emummc_id);
|
||||
|
||||
if (is_emummc) {
|
||||
if (mitm::fs::HasAtmosphereSdFile(g_specific_emummc_hosts_path)) {
|
||||
return g_specific_emummc_hosts_path;
|
||||
}
|
||||
Log(log_file, "Skipping %s because it does not exist...\n", g_specific_emummc_hosts_path);
|
||||
|
||||
if (mitm::fs::HasAtmosphereSdFile("/hosts/emummc.txt")) {
|
||||
return "/hosts/emummc.txt";
|
||||
}
|
||||
Log(log_file, "Skipping %s because it does not exist...\n", "/hosts/emummc.txt");
|
||||
} else {
|
||||
if (mitm::fs::HasAtmosphereSdFile("/hosts/sysmmc.txt")) {
|
||||
return "/hosts/sysmmc.txt";
|
||||
}
|
||||
Log(log_file, "Skipping %s because it does not exist...\n", "/hosts/sysmmc.txt");
|
||||
}
|
||||
|
||||
return "/hosts/default.txt";
|
||||
}
|
||||
|
||||
bool ShouldAddDefaultResolverRedirections() {
|
||||
u8 en = 0;
|
||||
if (settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "add_defaults_to_dns_hosts") == sizeof(en)) {
|
||||
return (en != 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InitializeResolverRedirections() {
|
||||
/* Get whether we should add defaults. */
|
||||
const bool add_defaults = ShouldAddDefaultResolverRedirections();
|
||||
|
||||
/* Acquire exclusive access to the map. */
|
||||
std::scoped_lock lk(g_redirection_lock);
|
||||
|
||||
/* Clear the redirections map. */
|
||||
g_redirection_list.clear();
|
||||
|
||||
/* Open log file. */
|
||||
::FsFile log_file;
|
||||
mitm::fs::DeleteAtmosphereSdFile("/logs/dns_mitm_startup.log");
|
||||
mitm::fs::CreateAtmosphereSdDirectory("/logs");
|
||||
R_ABORT_UNLESS(mitm::fs::CreateAtmosphereSdFile("/logs/dns_mitm_startup.log", 0, ams::fs::CreateOption_None));
|
||||
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(log_file), "/logs/dns_mitm_startup.log", ams::fs::OpenMode_ReadWrite | ams::fs::OpenMode_AllowAppend));
|
||||
ON_SCOPE_EXIT { ::fsFileClose(std::addressof(log_file)); };
|
||||
|
||||
Log(log_file, "DNS Mitm:\n");
|
||||
|
||||
/* If a default hosts file doesn't exist on the sd card, create one. */
|
||||
if (!mitm::fs::HasAtmosphereSdFile("/hosts/default.txt")) {
|
||||
Log(log_file, "Creating /hosts/default.txt because it does not exist.\n");
|
||||
|
||||
mitm::fs::CreateAtmosphereSdDirectory("/hosts");
|
||||
R_ABORT_UNLESS(mitm::fs::CreateAtmosphereSdFile("/hosts/default.txt", sizeof(DefaultHostsFile) - 1, ams::fs::CreateOption_None));
|
||||
|
||||
::FsFile default_file;
|
||||
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(default_file), "/hosts/default.txt", ams::fs::OpenMode_ReadWrite));
|
||||
R_ABORT_UNLESS(::fsFileWrite(std::addressof(default_file), 0, DefaultHostsFile, sizeof(DefaultHostsFile) - 1, ::FsWriteOption_Flush));
|
||||
::fsFileClose(std::addressof(default_file));
|
||||
}
|
||||
|
||||
/* If we should, add the defaults. */
|
||||
if (add_defaults) {
|
||||
Log(log_file, "Adding defaults to redirection list.\n");
|
||||
ParseHostsFile(DefaultHostsFile);
|
||||
}
|
||||
|
||||
/* Select the hosts file. */
|
||||
const char *hosts_path = SelectHostsFile(log_file);
|
||||
Log(log_file, "Selected %s\n", hosts_path);
|
||||
|
||||
/* Load the hosts file. */
|
||||
{
|
||||
char *hosts_file_data = nullptr;
|
||||
ON_SCOPE_EXIT { if (hosts_file_data != nullptr) { ams::Free(hosts_file_data); } };
|
||||
{
|
||||
::FsFile hosts_file;
|
||||
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(hosts_file), hosts_path, ams::fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { ::fsFileClose(std::addressof(hosts_file)); };
|
||||
|
||||
/* Get the hosts file size. */
|
||||
s64 hosts_size;
|
||||
R_ABORT_UNLESS(::fsFileGetSize(std::addressof(hosts_file), std::addressof(hosts_size)));
|
||||
|
||||
/* Validate we can read the file. */
|
||||
AMS_ABORT_UNLESS(0 <= hosts_size && hosts_size < 0x8000);
|
||||
|
||||
/* Read the data. */
|
||||
hosts_file_data = static_cast<char *>(ams::Malloc(0x8000));
|
||||
AMS_ABORT_UNLESS(hosts_file_data != nullptr);
|
||||
|
||||
u64 br;
|
||||
R_ABORT_UNLESS(::fsFileRead(std::addressof(hosts_file), 0, hosts_file_data, hosts_size, ::FsReadOption_None, std::addressof(br)));
|
||||
AMS_ABORT_UNLESS(br == static_cast<u64>(hosts_size));
|
||||
|
||||
/* Null-terminate. */
|
||||
hosts_file_data[hosts_size] = '\x00';
|
||||
}
|
||||
|
||||
/* Parse the hosts file. */
|
||||
ParseHostsFile(hosts_file_data);
|
||||
}
|
||||
|
||||
/* Print the redirections. */
|
||||
Log(log_file, "Redirections:\n");
|
||||
for (const auto &[host, address] : g_redirection_list) {
|
||||
Log(log_file, " `%s` -> %u.%u.%u.%u\n", host.c_str(), (address >> 0) & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF, (address >> 24) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
bool GetRedirectedHostByName(ams::socket::InAddrT *out, const char *hostname) {
|
||||
std::scoped_lock lk(g_redirection_lock);
|
||||
|
||||
for (const auto &[host, address] : g_redirection_list) {
|
||||
if (wildcardcmp(host.c_str(), hostname)) {
|
||||
*out = address;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::socket::resolver {
|
||||
|
||||
void InitializeResolverRedirections();
|
||||
|
||||
bool GetRedirectedHostByName(ams::socket::InAddrT *out, const char *hostname);
|
||||
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "dnsmitm_module.hpp"
|
||||
#include "dnsmitm_debug.hpp"
|
||||
#include "dnsmitm_resolver_impl.hpp"
|
||||
#include "dnsmitm_host_redirection.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver {
|
||||
|
||||
namespace {
|
||||
|
||||
enum PortIndex {
|
||||
PortIndex_Mitm,
|
||||
PortIndex_Count,
|
||||
};
|
||||
|
||||
constexpr sm::ServiceName DnsMitmServiceName = sm::ServiceName::Encode("sfdnsres");
|
||||
|
||||
constexpr size_t MaxSessions = 30;
|
||||
|
||||
struct ServerOptions {
|
||||
static constexpr size_t PointerBufferSize = sf::hipc::DefaultServerManagerOptions::PointerBufferSize;
|
||||
static constexpr size_t MaxDomains = sf::hipc::DefaultServerManagerOptions::MaxDomains;
|
||||
static constexpr size_t MaxDomainObjects = sf::hipc::DefaultServerManagerOptions::MaxDomainObjects;
|
||||
static constexpr bool CanDeferInvokeRequest = sf::hipc::DefaultServerManagerOptions::CanDeferInvokeRequest;
|
||||
static constexpr bool CanManageMitmServers = true;
|
||||
};
|
||||
|
||||
class ServerManager final : public sf::hipc::ServerManager<PortIndex_Count, ServerOptions, MaxSessions> {
|
||||
private:
|
||||
virtual Result OnNeedsToAccept(int port_index, Server *server) override;
|
||||
};
|
||||
|
||||
alignas(os::MemoryPageSize) constinit u8 g_resolver_allocator_buffer[16_KB];
|
||||
|
||||
ServerManager g_server_manager;
|
||||
|
||||
Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
|
||||
/* Acknowledge the mitm session. */
|
||||
std::shared_ptr<::Service> fsrv;
|
||||
sm::MitmProcessInfo client_info;
|
||||
server->AcknowledgeMitmSession(std::addressof(fsrv), std::addressof(client_info));
|
||||
|
||||
switch (port_index) {
|
||||
case PortIndex_Mitm:
|
||||
R_RETURN(this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<IResolver, ResolverImpl>(decltype(fsrv)(fsrv), client_info), fsrv));
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr size_t TotalThreads = 8;
|
||||
static_assert(TotalThreads >= 1, "TotalThreads");
|
||||
constexpr size_t NumExtraThreads = TotalThreads - 1;
|
||||
constexpr size_t ThreadStackSize = mitm::ModuleTraits<socket::resolver::MitmModule>::StackSize;
|
||||
alignas(os::MemoryPageSize) u8 g_extra_thread_stacks[NumExtraThreads][ThreadStackSize];
|
||||
|
||||
os::ThreadType g_extra_threads[NumExtraThreads];
|
||||
|
||||
void LoopServerThread(void *) {
|
||||
/* Loop forever, servicing our services. */
|
||||
g_server_manager.LoopProcess();
|
||||
}
|
||||
|
||||
void ProcessForServerOnAllThreads() {
|
||||
/* Initialize threads. */
|
||||
if constexpr (NumExtraThreads > 0) {
|
||||
const s32 priority = os::GetThreadCurrentPriority(os::GetCurrentThread());
|
||||
for (size_t i = 0; i < NumExtraThreads; i++) {
|
||||
R_ABORT_UNLESS(os::CreateThread(g_extra_threads + i, LoopServerThread, nullptr, g_extra_thread_stacks[i], ThreadStackSize, priority));
|
||||
}
|
||||
}
|
||||
|
||||
/* Start extra threads. */
|
||||
if constexpr (NumExtraThreads > 0) {
|
||||
for (size_t i = 0; i < NumExtraThreads; i++) {
|
||||
os::StartThread(g_extra_threads + i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop this thread. */
|
||||
LoopServerThread(nullptr);
|
||||
|
||||
/* Wait for extra threads to finish. */
|
||||
if constexpr (NumExtraThreads > 0) {
|
||||
for (size_t i = 0; i < NumExtraThreads; i++) {
|
||||
os::WaitThread(g_extra_threads + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ShouldMitmDns() {
|
||||
u8 en = 0;
|
||||
if (settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "enable_dns_mitm") == sizeof(en)) {
|
||||
return (en != 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShouldEnableDebugLog() {
|
||||
u8 en = 0;
|
||||
if (settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "enable_dns_mitm_debug_log") == sizeof(en)) {
|
||||
return (en != 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MitmModule::ThreadFunction(void *) {
|
||||
/* Wait until initialization is complete. */
|
||||
mitm::WaitInitialized();
|
||||
|
||||
/* If we shouldn't mitm dns, don't do anything at all. */
|
||||
if (!ShouldMitmDns()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize the socket allocator. */
|
||||
ams::socket::InitializeAllocatorForInternal(g_resolver_allocator_buffer, sizeof(g_resolver_allocator_buffer));
|
||||
|
||||
/* Initialize debug. */
|
||||
resolver::InitializeDebug(ShouldEnableDebugLog());
|
||||
|
||||
/* Initialize redirection map. */
|
||||
resolver::InitializeResolverRedirections();
|
||||
|
||||
/* Create mitm servers. */
|
||||
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<ResolverImpl>(PortIndex_Mitm, DnsMitmServiceName)));
|
||||
|
||||
/* Loop forever, servicing our services. */
|
||||
ProcessForServerOnAllThreads();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_module.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver {
|
||||
|
||||
DEFINE_MITM_MODULE_CLASS(0x2000, AMS_GET_SYSTEM_THREAD_PRIORITY(socket, ResolverIpcServer) - 1);
|
||||
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "dnsmitm_resolver_impl.hpp"
|
||||
#include "dnsmitm_debug.hpp"
|
||||
#include "dnsmitm_host_redirection.hpp"
|
||||
#include "serializer/serializer.hpp"
|
||||
#include "sfdnsres_shim.h"
|
||||
|
||||
namespace ams::mitm::socket::resolver {
|
||||
|
||||
ssize_t SerializeRedirectedHostEnt(u8 * const dst, size_t dst_size, const char *hostname, ams::socket::InAddrT redirect_addr) {
|
||||
struct in_addr addr = { .s_addr = redirect_addr };
|
||||
struct in_addr *addr_list[2] = { std::addressof(addr), nullptr };
|
||||
|
||||
struct hostent ent = {
|
||||
.h_name = const_cast<char *>(hostname),
|
||||
.h_aliases = nullptr,
|
||||
.h_addrtype = AF_INET,
|
||||
.h_length = sizeof(u32),
|
||||
.h_addr_list = (char **)addr_list,
|
||||
};
|
||||
|
||||
const auto result = serializer::DNSSerializer::ToBuffer(dst, dst_size, ent);
|
||||
AMS_ABORT_UNLESS(result >= 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t SerializeRedirectedAddrInfo(u8 * const dst, size_t dst_size, const char *hostname, ams::socket::InAddrT redirect_addr, u16 redirect_port, const struct addrinfo *hint) {
|
||||
AMS_UNUSED(hostname);
|
||||
|
||||
struct addrinfo ai = {
|
||||
.ai_flags = 0,
|
||||
.ai_family = AF_UNSPEC,
|
||||
.ai_socktype = 0,
|
||||
.ai_protocol = 0,
|
||||
.ai_addrlen = 0,
|
||||
.ai_canonname = nullptr,
|
||||
.ai_next = nullptr,
|
||||
};
|
||||
|
||||
if (hint != nullptr) {
|
||||
ai = *hint;
|
||||
}
|
||||
|
||||
switch (ai.ai_family) {
|
||||
case AF_UNSPEC: ai.ai_family = AF_INET; break;
|
||||
case AF_INET: ai.ai_family = AF_INET; break;
|
||||
case AF_INET6: AMS_ABORT("Redirected INET6 not supported"); break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
if (ai.ai_socktype == 0) {
|
||||
ai.ai_socktype = SOCK_STREAM;
|
||||
}
|
||||
|
||||
if (ai.ai_protocol == 0) {
|
||||
ai.ai_protocol = IPPROTO_TCP;
|
||||
}
|
||||
|
||||
const struct sockaddr_in sin = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_port = ams::socket::InetHtons(redirect_port),
|
||||
.sin_addr = { .s_addr = redirect_addr },
|
||||
.sin_zero = {},
|
||||
};
|
||||
|
||||
ai.ai_addrlen = sizeof(sin);
|
||||
ai.ai_addr = (struct sockaddr *)(std::addressof(sin));
|
||||
|
||||
const auto result = serializer::DNSSerializer::ToBuffer(dst, dst_size, ai);
|
||||
AMS_ABORT_UNLESS(result >= 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
Result ResolverImpl::GetHostByNameRequest(u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &name, sf::Out<u32> out_host_error, sf::Out<u32> out_errno, const sf::OutBuffer &out_hostent, sf::Out<u32> out_size) {
|
||||
AMS_UNUSED(cancel_handle, client_pid, use_nsd_resolve);
|
||||
|
||||
const char *hostname = reinterpret_cast<const char *>(name.GetPointer());
|
||||
|
||||
LogDebug("[%016lx]: GetHostByNameRequest(%s)\n", m_client_info.program_id.value, hostname);
|
||||
|
||||
R_UNLESS(hostname != nullptr, sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
ams::socket::InAddrT redirect_addr = {};
|
||||
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
LogDebug("[%016lx]: Redirecting %s to %u.%u.%u.%u\n", m_client_info.program_id.value, hostname, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF);
|
||||
const auto size = SerializeRedirectedHostEnt(out_hostent.GetPointer(), out_hostent.GetSize(), hostname, redirect_addr);
|
||||
|
||||
*out_host_error = 0;
|
||||
*out_errno = 0;
|
||||
*out_size = size;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ResolverImpl::GetAddrInfoRequest(u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutBuffer &out_addrinfo, sf::Out<u32> out_errno, sf::Out<s32> out_retval, sf::Out<u32> out_size) {
|
||||
AMS_UNUSED(cancel_handle, client_pid, use_nsd_resolve);
|
||||
|
||||
const char *hostname = reinterpret_cast<const char *>(node.GetPointer());
|
||||
|
||||
LogDebug("[%016lx]: GetAddrInfoRequest(%s, %s)\n", m_client_info.program_id.value, reinterpret_cast<const char *>(node.GetPointer()), reinterpret_cast<const char *>(srv.GetPointer()));
|
||||
|
||||
R_UNLESS(hostname != nullptr, sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
ams::socket::InAddrT redirect_addr = {};
|
||||
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
u16 port = 0;
|
||||
if (srv.GetPointer() != nullptr) {
|
||||
for (const char *cur = reinterpret_cast<const char *>(srv.GetPointer()); *cur != 0; ++cur) {
|
||||
AMS_ABORT_UNLESS(std::isdigit(static_cast<unsigned char>(*cur)));
|
||||
port *= 10;
|
||||
port += *cur - '0';
|
||||
}
|
||||
}
|
||||
|
||||
LogDebug("[%016lx]: Redirecting %s:%u to %u.%u.%u.%u\n", m_client_info.program_id.value, hostname, port, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF);
|
||||
|
||||
const bool use_hint = serialized_hint.GetPointer() != nullptr;
|
||||
struct addrinfo hint = {};
|
||||
if (use_hint) {
|
||||
AMS_ABORT_UNLESS(serializer::DNSSerializer::FromBuffer(hint, serialized_hint.GetPointer(), serialized_hint.GetSize()) >= 0);
|
||||
}
|
||||
ON_SCOPE_EXIT { if (use_hint) { serializer::FreeAddrInfo(hint); } };
|
||||
|
||||
const auto size = SerializeRedirectedAddrInfo(out_addrinfo.GetPointer(), out_addrinfo.GetSize(), hostname, redirect_addr, port, use_hint ? std::addressof(hint) : nullptr);
|
||||
|
||||
*out_retval = 0;
|
||||
*out_errno = 0;
|
||||
*out_size = size;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ResolverImpl::GetHostByNameRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InAutoSelectBuffer &name, const sf::OutAutoSelectBuffer &out_hostent, sf::Out<u32> out_size, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno) {
|
||||
AMS_UNUSED(client_pid, options_version, options, num_options);
|
||||
|
||||
const char *hostname = reinterpret_cast<const char *>(name.GetPointer());
|
||||
|
||||
LogDebug("[%016lx]: GetHostByNameRequestWithOptions(%s)\n", m_client_info.program_id.value, hostname);
|
||||
|
||||
R_UNLESS(hostname != nullptr, sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
ams::socket::InAddrT redirect_addr = {};
|
||||
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
LogDebug("[%016lx]: Redirecting %s to %u.%u.%u.%u\n", m_client_info.program_id.value, hostname, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF);
|
||||
const auto size = SerializeRedirectedHostEnt(out_hostent.GetPointer(), out_hostent.GetSize(), hostname, redirect_addr);
|
||||
|
||||
*out_host_error = 0;
|
||||
*out_errno = 0;
|
||||
*out_size = size;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ResolverImpl::GetAddrInfoRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutAutoSelectBuffer &out_addrinfo, sf::Out<u32> out_size, sf::Out<s32> out_retval, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno) {
|
||||
AMS_UNUSED(client_pid, options_version, options, num_options);
|
||||
|
||||
const char *hostname = reinterpret_cast<const char *>(node.GetPointer());
|
||||
|
||||
LogDebug("[%016lx]: GetAddrInfoRequestWithOptions(%s, %s)\n", m_client_info.program_id.value, hostname, reinterpret_cast<const char *>(srv.GetPointer()));
|
||||
|
||||
R_UNLESS(hostname != nullptr, sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
ams::socket::InAddrT redirect_addr = {};
|
||||
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
u16 port = 0;
|
||||
if (srv.GetPointer() != nullptr) {
|
||||
for (const char *cur = reinterpret_cast<const char *>(srv.GetPointer()); *cur != 0; ++cur) {
|
||||
AMS_ABORT_UNLESS(std::isdigit(static_cast<unsigned char>(*cur)));
|
||||
port *= 10;
|
||||
port += *cur - '0';
|
||||
}
|
||||
}
|
||||
|
||||
LogDebug("[%016lx]: Redirecting %s:%u to %u.%u.%u.%u\n", m_client_info.program_id.value, hostname, port, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF);
|
||||
|
||||
const bool use_hint = serialized_hint.GetPointer() != nullptr;
|
||||
struct addrinfo hint = {};
|
||||
if (use_hint) {
|
||||
AMS_ABORT_UNLESS(serializer::DNSSerializer::FromBuffer(hint, serialized_hint.GetPointer(), serialized_hint.GetSize()) >= 0);
|
||||
}
|
||||
ON_SCOPE_EXIT { if (use_hint) { serializer::FreeAddrInfo(hint); } };
|
||||
|
||||
const auto size = SerializeRedirectedAddrInfo(out_addrinfo.GetPointer(), out_addrinfo.GetSize(), hostname, redirect_addr, port, use_hint ? std::addressof(hint) : nullptr);
|
||||
|
||||
*out_retval = 0;
|
||||
*out_host_error = 0;
|
||||
*out_errno = 0;
|
||||
*out_size = size;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ResolverImpl::AtmosphereReloadHostsFile() {
|
||||
/* Perform a hosts file reload. */
|
||||
InitializeResolverRedirections();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#define AMS_DNS_MITM_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, GetHostByNameRequest, (u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &name, sf::Out<u32> out_host_error, sf::Out<u32> out_errno, const sf::OutBuffer &out_hostent, sf::Out<u32> out_size), (cancel_handle, client_pid, use_nsd_resolve, name, out_host_error, out_errno, out_hostent, out_size)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 6, Result, GetAddrInfoRequest, (u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutBuffer &out_addrinfo, sf::Out<u32> out_errno, sf::Out<s32> out_retval, sf::Out<u32> out_size), (cancel_handle, client_pid, use_nsd_resolve, node, srv, serialized_hint, out_addrinfo, out_errno, out_retval, out_size)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 10, Result, GetHostByNameRequestWithOptions, (const sf::ClientProcessId &client_pid, const sf::InAutoSelectBuffer &name, const sf::OutAutoSelectBuffer &out_hostent, sf::Out<u32> out_size, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno), (client_pid, name, out_hostent, out_size, options_version, options, num_options, out_host_error, out_errno), hos::Version_5_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 12, Result, GetAddrInfoRequestWithOptions, (const sf::ClientProcessId &client_pid, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutAutoSelectBuffer &out_addrinfo, sf::Out<u32> out_size, sf::Out<s32> out_retval, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno), (client_pid, node, srv, serialized_hint, out_addrinfo, out_size, out_retval, options_version, options, num_options, out_host_error, out_errno), hos::Version_5_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 65000, Result, AtmosphereReloadHostsFile, (), ())
|
||||
|
||||
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::socket::resolver, IResolver, AMS_DNS_MITM_INTERFACE_INFO, 0x5935F91A)
|
||||
|
||||
namespace ams::mitm::socket::resolver {
|
||||
|
||||
class ResolverImpl : public sf::MitmServiceImplBase {
|
||||
public:
|
||||
using MitmServiceImplBase::MitmServiceImplBase;
|
||||
public:
|
||||
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
|
||||
/* We will mitm:
|
||||
* - everything.
|
||||
*/
|
||||
AMS_UNUSED(client_info);
|
||||
return true;
|
||||
}
|
||||
public:
|
||||
/* Overridden commands. */
|
||||
Result GetHostByNameRequest(u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &name, sf::Out<u32> out_host_error, sf::Out<u32> out_errno, const sf::OutBuffer &out_hostent, sf::Out<u32> out_size);
|
||||
Result GetAddrInfoRequest(u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutBuffer &out_addrinfo, sf::Out<u32> out_errno, sf::Out<s32> out_retval, sf::Out<u32> out_size);
|
||||
Result GetHostByNameRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InAutoSelectBuffer &name, const sf::OutAutoSelectBuffer &out_hostent, sf::Out<u32> out_size, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno);
|
||||
Result GetAddrInfoRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutAutoSelectBuffer &out_addrinfo, sf::Out<u32> out_size, sf::Out<s32> out_retval, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno);
|
||||
|
||||
/* Extension commands. */
|
||||
Result AtmosphereReloadHostsFile();
|
||||
};
|
||||
static_assert(IsIResolver<ResolverImpl>);
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../dnsmitm_debug.hpp"
|
||||
#include "serializer.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver::serializer {
|
||||
|
||||
ssize_t DNSSerializer::CheckToBufferArguments(const u8 *dst, size_t dst_size, size_t required, int error_id) {
|
||||
/* TODO: Logging, using error_id */
|
||||
AMS_UNUSED(error_id);
|
||||
|
||||
if (dst == nullptr) {
|
||||
return -1;
|
||||
} else if (dst_size < required) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 DNSSerializer::InternalHton(const u32 &v) {
|
||||
return ams::socket::InetHtonl(v);
|
||||
}
|
||||
|
||||
u16 DNSSerializer::InternalHton(const u16 &v) {
|
||||
return ams::socket::InetHtons(v);
|
||||
}
|
||||
|
||||
u32 DNSSerializer::InternalNtoh(const u32 &v) {
|
||||
return ams::socket::InetNtohl(v);
|
||||
}
|
||||
|
||||
u16 DNSSerializer::InternalNtoh(const u16 &v) {
|
||||
return ams::socket::InetNtohs(v);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::socket::resolver::serializer {
|
||||
|
||||
class DNSSerializer {
|
||||
public:
|
||||
static ssize_t CheckToBufferArguments(const u8 *dst, size_t dst_size, size_t required, int error_id);
|
||||
|
||||
static u32 InternalHton(const u32 &v);
|
||||
static u16 InternalHton(const u16 &v);
|
||||
static u32 InternalNtoh(const u32 &v);
|
||||
static u16 InternalNtoh(const u16 &v);
|
||||
public:
|
||||
template<typename T>
|
||||
static size_t SizeOf(const T &in);
|
||||
|
||||
template<typename T>
|
||||
static size_t SizeOf(const T *in);
|
||||
|
||||
template<typename T>
|
||||
static size_t SizeOf(const T *in, size_t count);
|
||||
|
||||
template<typename T>
|
||||
static size_t SizeOf(const T **arr, u32 &out_count);
|
||||
|
||||
template<typename T>
|
||||
static ssize_t ToBuffer(u8 * const dst, size_t dst_size, const T &in);
|
||||
|
||||
template<typename T>
|
||||
static ssize_t FromBuffer(T &out, const u8 *src, size_t src_size);
|
||||
|
||||
template<typename T>
|
||||
static ssize_t ToBuffer(u8 * const dst, size_t dst_Size, T *in);
|
||||
|
||||
template<typename T>
|
||||
static ssize_t ToBuffer(u8 * const dst, size_t dst_size, T **arr);
|
||||
|
||||
template<typename T>
|
||||
static ssize_t FromBuffer(T *&out, const u8 *src, size_t src_size);
|
||||
|
||||
template<typename T>
|
||||
static ssize_t FromBuffer(T **&out_arr, const u8 *src, size_t src_size);
|
||||
|
||||
template<typename T>
|
||||
static ssize_t ToBuffer(u8 * const dst, size_t dst_size, const T * const arr, size_t count);
|
||||
|
||||
template<typename T>
|
||||
static ssize_t FromBuffer(T * const arr, size_t arr_size, const u8 *src, size_t src_size, size_t count);
|
||||
};
|
||||
|
||||
void FreeHostent(ams::socket::HostEnt &ent);
|
||||
void FreeHostent(struct hostent &ent);
|
||||
|
||||
void FreeAddrInfo(ams::socket::AddrInfo &addr_info);
|
||||
void FreeAddrInfo(struct addrinfo &addr_info);
|
||||
|
||||
}
|
||||
@@ -1,444 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../dnsmitm_debug.hpp"
|
||||
#include "../socket_allocator.hpp"
|
||||
#include "serializer.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver::serializer {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline u32 AddrInfoMagic = 0xBEEFCAFE;
|
||||
|
||||
template<typename T>
|
||||
concept IsAddrInfo = std::same_as<T, ams::socket::AddrInfo> || std::same_as<T, struct addrinfo>;
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
using SockAddrType = typename std::conditional<std::same_as<T, ams::socket::AddrInfo>, ams::socket::SockAddr, struct sockaddr>::type;
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
using SockAddrInType = typename std::conditional<std::same_as<T, ams::socket::AddrInfo>, ams::socket::SockAddrIn, struct sockaddr_in>::type;
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
using SockAddrIn6Type = typename std::conditional<std::same_as<T, ams::socket::AddrInfo>, struct sockaddr_in6, struct sockaddr_in6>::type;
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
constexpr bool IsAfInet(const auto ai_family) {
|
||||
if constexpr (std::same_as<T, ams::socket::AddrInfo>) {
|
||||
return ai_family == ams::socket::Family::Af_Inet;
|
||||
} else {
|
||||
return ai_family == AF_INET;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
constexpr bool IsAfInet6(const auto ai_family) {
|
||||
if constexpr (std::same_as<T, ams::socket::AddrInfo>) {
|
||||
return ai_family == ams::socket::Family::Af_Inet6;
|
||||
} else {
|
||||
return ai_family == AF_INET;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
void FreeAddrInfoImpl(T &addr_info) {
|
||||
T *next = nullptr;
|
||||
for (T *cur = std::addressof(addr_info); cur != nullptr; cur = next) {
|
||||
next = cur->ai_next;
|
||||
|
||||
if (cur->ai_addr != nullptr) {
|
||||
if (IsAfInet<T>(cur->ai_family)) {
|
||||
ams::socket::impl::Free(reinterpret_cast<SockAddrInType<T> *>(cur->ai_addr));
|
||||
} else if (IsAfInet6<T>(cur->ai_family)) {
|
||||
ams::socket::impl::Free(reinterpret_cast<SockAddrIn6Type<T> *>(cur->ai_addr));
|
||||
} else {
|
||||
ams::socket::impl::Free(cur->ai_addr);
|
||||
}
|
||||
cur->ai_addr = nullptr;
|
||||
}
|
||||
|
||||
if (cur->ai_canonname != nullptr) {
|
||||
ams::socket::impl::Free(cur->ai_canonname);
|
||||
cur->ai_canonname = nullptr;
|
||||
}
|
||||
|
||||
if (cur != std::addressof(addr_info)) {
|
||||
ams::socket::impl::Free(cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
size_t AddrInfoSingleSizeOf(const T *addr_info) {
|
||||
size_t rc = 6 * sizeof(u32);
|
||||
|
||||
if (addr_info->ai_addr == nullptr) {
|
||||
rc += sizeof(u32);
|
||||
} else if (IsAfInet<T>(addr_info->ai_family)) {
|
||||
rc += DNSSerializer::SizeOf(*reinterpret_cast<SockAddrInType<T> *>(addr_info->ai_addr));
|
||||
} else if (IsAfInet6<T>(addr_info->ai_family)) {
|
||||
rc += DNSSerializer::SizeOf(*reinterpret_cast<SockAddrIn6Type<T> *>(addr_info->ai_addr));
|
||||
} else if (addr_info->ai_addrlen == 0) {
|
||||
rc += sizeof(u32);
|
||||
} else {
|
||||
rc += addr_info->ai_addrlen;
|
||||
}
|
||||
|
||||
if (addr_info->ai_canonname != nullptr) {
|
||||
rc += DNSSerializer::SizeOf(static_cast<const char *>(addr_info->ai_canonname));
|
||||
} else {
|
||||
rc += sizeof(u8);
|
||||
}
|
||||
|
||||
if (addr_info->ai_next == nullptr) {
|
||||
rc += sizeof(u32);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
size_t SizeOfImpl(const T &in) {
|
||||
size_t rc = 0;
|
||||
|
||||
for (const T *addr_info = std::addressof(in); addr_info != nullptr; addr_info = addr_info->ai_next) {
|
||||
rc += AddrInfoSingleSizeOf(addr_info);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
ssize_t ToBufferInternalImpl(u8 * const dst, size_t dst_size, const T &addr_info) {
|
||||
ssize_t rc = -1;
|
||||
u8 *cur = dst;
|
||||
|
||||
{
|
||||
const u32 value = AddrInfoMagic;
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
const u32 value = static_cast<u32>(addr_info.ai_flags);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
const u32 value = static_cast<u32>(addr_info.ai_family);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
const u32 value = static_cast<u32>(addr_info.ai_socktype);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
const u32 value = static_cast<u32>(addr_info.ai_protocol);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
const u32 value = static_cast<u32>(addr_info.ai_addrlen);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
if (addr_info.ai_addr == nullptr || addr_info.ai_addrlen == 0) {
|
||||
const u32 value = 0;
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
} else if (IsAfInet<T>(addr_info.ai_family)) {
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), *reinterpret_cast<SockAddrInType<T> *>(addr_info.ai_addr))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
} else if (IsAfInet6<T>(addr_info.ai_family)) {
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), *reinterpret_cast<SockAddrIn6Type<T> *>(addr_info.ai_addr))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
} else {
|
||||
if (dst_size - (cur - dst) < addr_info.ai_addrlen) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* NOTE: This is clearly a nintendo bug, see the accompanying note in FromBufferInternalImpl */
|
||||
std::memmove(cur, std::addressof(addr_info.ai_addr), addr_info.ai_addrlen);
|
||||
rc = addr_info.ai_addrlen;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), addr_info.ai_canonname)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
if (addr_info.ai_next == nullptr) {
|
||||
const u32 value = 0;
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
rc = cur - dst;
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) {
|
||||
ssize_t rc = -1;
|
||||
u8 *cur = dst;
|
||||
std::memset(dst, 0, dst_size);
|
||||
|
||||
const size_t required = DNSSerializer::SizeOf(in);
|
||||
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, required, __LINE__)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
for (const T *addr_info = std::addressof(in); addr_info != nullptr; addr_info = addr_info->ai_next) {
|
||||
if ((rc = ToBufferInternalImpl(cur, dst_size, *addr_info)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
rc = cur - dst;
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
ssize_t FromBufferInternalImpl(T &out, const u8 *src, size_t src_size) {
|
||||
ssize_t rc = -1;
|
||||
const u8 *cur = src;
|
||||
|
||||
std::memset(std::addressof(out), 0, sizeof(out));
|
||||
|
||||
ON_SCOPE_EXIT { if (rc < 0) { FreeAddrInfo(out); } };
|
||||
|
||||
u32 tmp_value;
|
||||
|
||||
{
|
||||
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
} else if (tmp_value != AddrInfoMagic) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.ai_flags = static_cast<decltype(out.ai_flags)>(tmp_value);
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.ai_family = static_cast<decltype(out.ai_family)>(tmp_value);
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.ai_socktype = static_cast<decltype(out.ai_socktype)>(tmp_value);
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.ai_protocol = static_cast<decltype(out.ai_protocol)>(tmp_value);
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.ai_addrlen = static_cast<decltype(out.ai_addrlen)>(tmp_value);
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
if (out.ai_addrlen == 0) {
|
||||
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (tmp_value != 0) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
out.ai_addr = nullptr;
|
||||
} else if (IsAfInet<T>(out.ai_family)) {
|
||||
out.ai_addr = static_cast<SockAddrType<T> *>(ams::socket::impl::Alloc(sizeof(SockAddrInType<T>)));
|
||||
if (out.ai_addr == nullptr) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
std::memset(out.ai_addr, 0, sizeof(SockAddrInType<T>));
|
||||
|
||||
if ((rc = DNSSerializer::FromBuffer(*reinterpret_cast<SockAddrInType<T> *>(out.ai_addr), cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
} else if (IsAfInet6<T>(out.ai_family)) {
|
||||
out.ai_addr = static_cast<SockAddrType<T> *>(ams::socket::impl::Alloc(sizeof(SockAddrIn6Type<T>)));
|
||||
if (out.ai_addr == nullptr) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
std::memset(out.ai_addr, 0, sizeof(SockAddrIn6Type<T>));
|
||||
|
||||
if ((rc = DNSSerializer::FromBuffer(*reinterpret_cast<SockAddrIn6Type<T> *>(out.ai_addr), cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
} else {
|
||||
out.ai_addr = static_cast<decltype(out.ai_addr)>(ams::socket::impl::Alloc(out.ai_addrlen));
|
||||
if (out.ai_addr == nullptr) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* NOTE: This is *clearly* a nintendo bug. */
|
||||
/* They obviously intend to copy to the buffer they just allocated, but instead they copy to the addrinfo structure itself. */
|
||||
/* Probably &out.ai_addr instead of &out.ai_addr[0]? Either way, we'll implement what they do, but... */
|
||||
std::memcpy(std::addressof(out.ai_addr), cur, out.ai_addrlen);
|
||||
rc = out.ai_addrlen;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
if ((rc = DNSSerializer::FromBuffer(out.ai_canonname, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
} else if (tmp_value == 0) {
|
||||
out.ai_next = nullptr;
|
||||
cur += rc;
|
||||
} else if (tmp_value == AddrInfoMagic) {
|
||||
out.ai_next = static_cast<T *>(ams::socket::impl::Alloc(sizeof(T)));
|
||||
if (out.ai_next == nullptr) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::memset(out.ai_next, 0, sizeof(T));
|
||||
} else {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
rc = cur - src;
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) {
|
||||
ssize_t rc = 0;
|
||||
const u8 *cur = src;
|
||||
|
||||
const size_t required = DNSSerializer::SizeOf(out);
|
||||
if (src_size < required) {
|
||||
ams::socket::SetLastError(ams::socket::Errno::ENoSpc);
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
for (T *addr_info = std::addressof(out); addr_info != nullptr; addr_info = addr_info->ai_next) {
|
||||
if ((rc = FromBufferInternalImpl(*addr_info, cur, src_size - (cur - src))) == -1) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
rc = cur - src;
|
||||
return rc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const struct addrinfo &in) {
|
||||
return SizeOfImpl(in);
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const ams::socket::AddrInfo &in) {
|
||||
return SizeOfImpl(in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const ams::socket::AddrInfo &in) {
|
||||
return ToBufferImpl(dst, dst_size, in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct addrinfo &in) {
|
||||
return ToBufferImpl(dst, dst_size, in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(ams::socket::AddrInfo &out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(struct addrinfo &out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
void FreeAddrInfo(ams::socket::AddrInfo &addr_info) {
|
||||
return FreeAddrInfoImpl(addr_info);
|
||||
}
|
||||
|
||||
void FreeAddrInfo(struct addrinfo &addr_info) {
|
||||
return FreeAddrInfoImpl(addr_info);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,234 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../dnsmitm_debug.hpp"
|
||||
#include "../socket_allocator.hpp"
|
||||
#include "serializer.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver::serializer {
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename T>
|
||||
concept IsHostEnt = std::same_as<T, ams::socket::HostEnt> || std::same_as<T, struct hostent>;
|
||||
|
||||
template<typename T> requires IsHostEnt<T>
|
||||
using InAddrType = typename std::conditional<std::same_as<T, ams::socket::HostEnt>, ams::socket::InAddr, struct in_addr>::type;
|
||||
|
||||
template<typename T> requires IsHostEnt<T>
|
||||
constexpr bool IsAfInet(const auto h_addrtype) {
|
||||
if constexpr (std::same_as<T, ams::socket::HostEnt>) {
|
||||
return h_addrtype == ams::socket::Family::Af_Inet;
|
||||
} else {
|
||||
return h_addrtype == AF_INET;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> requires IsHostEnt<T>
|
||||
constexpr bool IsAfInet6(const auto h_addrtype) {
|
||||
if constexpr (std::same_as<T, ams::socket::HostEnt>) {
|
||||
return h_addrtype == ams::socket::Family::Af_Inet6;
|
||||
} else {
|
||||
return h_addrtype == AF_INET;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> requires IsHostEnt<T>
|
||||
size_t SizeOfImpl(const T &in) {
|
||||
size_t rc = 0;
|
||||
u32 dummy = 0;
|
||||
|
||||
rc += DNSSerializer::SizeOf((const char *)(in.h_name));
|
||||
rc += DNSSerializer::SizeOf((const char **)(in.h_aliases), dummy);
|
||||
rc += sizeof(u32);
|
||||
rc += sizeof(u32);
|
||||
rc += DNSSerializer::SizeOf((const InAddrType<T> **)(in.h_addr_list), dummy);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsHostEnt<T>
|
||||
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) {
|
||||
ssize_t rc = -1;
|
||||
u8 *cur = dst;
|
||||
|
||||
const size_t required = DNSSerializer::SizeOf(in);
|
||||
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, required, __LINE__)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), in.h_name)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), in.h_aliases)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
const u16 h_addrtype = static_cast<u16>(in.h_addrtype);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), h_addrtype)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
const u16 h_length = in.h_length;
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), h_length)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
if (IsAfInet<T>(in.h_addrtype)) {
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), (InAddrType<T> **)(in.h_addr_list))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
} else if (IsAfInet6<T>(in.h_addrtype)) {
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), (InAddrType<T> **)(in.h_addr_list))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
} else {
|
||||
const u32 null = 0;
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), null)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
rc = cur - dst;
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsHostEnt<T>
|
||||
ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) {
|
||||
ssize_t rc = -1;
|
||||
const u8 *cur = src;
|
||||
|
||||
std::memset(std::addressof(out), 0, sizeof(out));
|
||||
|
||||
ON_SCOPE_EXIT {
|
||||
if (rc < 0) {
|
||||
FreeHostent(out);
|
||||
}
|
||||
};
|
||||
|
||||
if ((rc = DNSSerializer::FromBuffer(out.h_name, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
if ((rc = DNSSerializer::FromBuffer(out.h_aliases, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
u16 h_addrtype = 0;
|
||||
if ((rc = DNSSerializer::FromBuffer(h_addrtype, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.h_addrtype = static_cast<decltype(out.h_addrtype)>(h_addrtype);
|
||||
cur += rc;
|
||||
|
||||
u16 h_length = 0;
|
||||
if ((rc = DNSSerializer::FromBuffer(h_length, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.h_length = h_length;
|
||||
cur += rc;
|
||||
|
||||
InAddrType<T> **addrs = nullptr;
|
||||
if ((rc = DNSSerializer::FromBuffer(addrs, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.h_addr_list = (char **)addrs;
|
||||
cur += rc;
|
||||
|
||||
rc = cur - src;
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsHostEnt<T>
|
||||
void FreeHostentImpl(T &ent) {
|
||||
if (ent.h_name != nullptr) {
|
||||
ams::socket::impl::Free(ent.h_name);
|
||||
ent.h_name = nullptr;
|
||||
}
|
||||
|
||||
if (ent.h_aliases != nullptr) {
|
||||
u32 i = 0;
|
||||
for (char *str = ent.h_aliases[i]; str != nullptr; str = ent.h_aliases[++i]) {
|
||||
ams::socket::impl::Free(str);
|
||||
ent.h_aliases[i] = nullptr;
|
||||
}
|
||||
|
||||
ams::socket::impl::Free(ent.h_aliases);
|
||||
ent.h_aliases = nullptr;
|
||||
}
|
||||
|
||||
if (ent.h_addr_list != nullptr) {
|
||||
AMS_ASSERT(ent.h_length == sizeof(u32));
|
||||
if (ent.h_length == sizeof(u32)) {
|
||||
auto **addr_list = reinterpret_cast<InAddrType<T> **>(ent.h_addr_list);
|
||||
|
||||
u32 i = 0;
|
||||
for (auto *addr = addr_list[i]; addr != nullptr; addr = addr_list[++i]) {
|
||||
ams::socket::impl::Free(addr);
|
||||
addr_list[i] = nullptr;
|
||||
}
|
||||
|
||||
ams::socket::impl::Free(ent.h_addr_list);
|
||||
ent.h_addr_list = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::memset(std::addressof(ent), 0, sizeof(ent));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const struct hostent &in) {
|
||||
return SizeOfImpl(in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct hostent &in) {
|
||||
return ToBufferImpl(dst, dst_size, in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(struct hostent &out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const ams::socket::HostEnt &in) {
|
||||
return SizeOfImpl(in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const ams::socket::HostEnt &in) {
|
||||
return ToBufferImpl(dst, dst_size, in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(ams::socket::HostEnt &out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
void FreeHostent(ams::socket::HostEnt &ent) {
|
||||
return FreeHostentImpl(ent);
|
||||
}
|
||||
|
||||
void FreeHostent(struct hostent &ent) {
|
||||
return FreeHostentImpl(ent);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../dnsmitm_debug.hpp"
|
||||
#include "../socket_allocator.hpp"
|
||||
#include "serializer.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver::serializer {
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename T>
|
||||
concept IsInAddr = std::same_as<T, ams::socket::InAddr> || std::same_as<T, struct in_addr>;
|
||||
|
||||
template<typename T> requires IsInAddr<T>
|
||||
size_t SizeOfImpl(const T &) {
|
||||
return sizeof(u32);
|
||||
}
|
||||
|
||||
template<typename T> requires IsInAddr<T>
|
||||
size_t SizeOfImpl(const T **in, u32 &out_count) {
|
||||
size_t rc = sizeof(u32);
|
||||
out_count = 0;
|
||||
|
||||
if (in != nullptr) {
|
||||
for (const T ** cur_addr = in; *cur_addr != nullptr; ++cur_addr) {
|
||||
++out_count;
|
||||
rc += sizeof(u32);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsInAddr<T>
|
||||
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) {
|
||||
ssize_t rc = -1;
|
||||
u8 *cur = dst;
|
||||
|
||||
const u32 val = DNSSerializer::InternalHton(in.s_addr);
|
||||
|
||||
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, sizeof(in), __LINE__)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::memcpy(cur, std::addressof(val), sizeof(val));
|
||||
rc += sizeof(val);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsInAddr<T>
|
||||
ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) {
|
||||
ssize_t rc = -1;
|
||||
if (src_size < sizeof(out)) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::memset(std::addressof(out), 0, sizeof(out));
|
||||
out.s_addr = DNSSerializer::InternalNtoh(*reinterpret_cast<const u32 *>(src));
|
||||
|
||||
rc = sizeof(u32);
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsInAddr<T>
|
||||
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, T **arr) {
|
||||
ssize_t rc = -1;
|
||||
u8 *cur = dst;
|
||||
|
||||
if (arr == nullptr && dst_size < sizeof(u32)) {
|
||||
return rc;
|
||||
} else if (arr == nullptr) {
|
||||
const u32 null = 0;
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), null)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
} else {
|
||||
u32 count = 0;
|
||||
for (auto *tmp = arr; *tmp != nullptr; ++tmp) {
|
||||
++count;
|
||||
}
|
||||
|
||||
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, (count + 1) * sizeof(**arr), __LINE__)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), count)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
rc = 0;
|
||||
for (auto i = 0; arr[i] != nullptr; ++i) {
|
||||
const T addr = *arr[i];
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), addr)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
rc = cur - dst;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsInAddr<T>
|
||||
ssize_t FromBufferImpl(T **&out, const u8 *src, size_t src_size) {
|
||||
ssize_t rc = -1;
|
||||
const u8 *cur = src;
|
||||
|
||||
out = nullptr;
|
||||
|
||||
ON_SCOPE_EXIT {
|
||||
if (rc == -1 && out != nullptr) {
|
||||
for (auto i = 0; out[i] != nullptr; ++i) {
|
||||
ams::socket::impl::Free(out[i]);
|
||||
out[i] = nullptr;
|
||||
}
|
||||
|
||||
ams::socket::impl::Free(out);
|
||||
out = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
if (src == nullptr) {
|
||||
rc = 0;
|
||||
return rc;
|
||||
} else if (src_size == 0) {
|
||||
rc = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
u32 count = 0;
|
||||
if ((rc = DNSSerializer::FromBuffer(count, cur, src_size)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
if (count == 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
out = static_cast<T **>(ams::socket::impl::Alloc((count + 1) * sizeof(T *)));
|
||||
if (out == nullptr) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
std::memset(out, 0, (count + 1) * sizeof(T *));
|
||||
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
out[i] = static_cast<T *>(ams::socket::impl::Alloc(sizeof(T)));
|
||||
if (out[i] == nullptr) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
u32 s_addr = 0;
|
||||
if ((rc = DNSSerializer::FromBuffer(s_addr, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out[i]->s_addr = s_addr;
|
||||
cur += rc;
|
||||
}
|
||||
out[count] = nullptr;
|
||||
|
||||
rc = cur - src;
|
||||
return rc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const struct in_addr &in) {
|
||||
return SizeOfImpl(in);
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const ams::socket::InAddr &in) {
|
||||
return SizeOfImpl(in);
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const struct in_addr **in, u32 &out_count) {
|
||||
return SizeOfImpl(in, out_count);
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const ams::socket::InAddr **in, u32 &out_count) {
|
||||
return SizeOfImpl(in, out_count);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct in_addr &in) {
|
||||
return ToBufferImpl(dst, dst_size, in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const ams::socket::InAddr &in) {
|
||||
return ToBufferImpl(dst, dst_size, in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(struct in_addr &out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(struct ams::socket::InAddr &out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, struct in_addr **arr) {
|
||||
return ToBufferImpl(dst, dst_size, arr);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, ams::socket::InAddr **arr) {
|
||||
return ToBufferImpl(dst, dst_size, arr);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(struct in_addr **&out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(struct ams::socket::InAddr **&out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../dnsmitm_debug.hpp"
|
||||
#include "serializer.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver::serializer {
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const u16 &in) {
|
||||
/* Convert the value. */
|
||||
u8 *cur = dst;
|
||||
const u16 val = InternalHton(in);
|
||||
|
||||
/* Check arguments. */
|
||||
ssize_t rc = -1;
|
||||
if ((rc = CheckToBufferArguments(cur, dst_size, sizeof(u16), __LINE__)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::memcpy(cur, std::addressof(val), sizeof(u16));
|
||||
rc += sizeof(u16);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(u16 &out, const u8 *src, size_t src_size) {
|
||||
if (src_size < sizeof(u16)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
out = InternalNtoh(*reinterpret_cast<const u16 *>(src));
|
||||
return sizeof(u16);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const u32 &in) {
|
||||
/* Convert the value. */
|
||||
u8 *cur = dst;
|
||||
const u32 val = InternalHton(in);
|
||||
|
||||
/* Check arguments. */
|
||||
ssize_t rc = -1;
|
||||
if ((rc = CheckToBufferArguments(cur, dst_size, sizeof(u32), __LINE__)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::memcpy(cur, std::addressof(val), sizeof(u32));
|
||||
rc += sizeof(u32);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(u32 &out, const u8 *src, size_t src_size) {
|
||||
if (src_size < sizeof(u32)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
out = InternalNtoh(*reinterpret_cast<const u32 *>(src));
|
||||
return sizeof(u32);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../dnsmitm_debug.hpp"
|
||||
#include "../socket_allocator.hpp"
|
||||
#include "serializer.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver::serializer {
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename T>
|
||||
concept IsSockAddrIn = std::same_as<T, ams::socket::SockAddrIn> || std::same_as<T, struct sockaddr_in>;
|
||||
|
||||
template<typename T> requires IsSockAddrIn<T>
|
||||
size_t SizeOfImpl(const T &in) {
|
||||
size_t rc = 0;
|
||||
rc += sizeof(u16);
|
||||
rc += sizeof(u16);
|
||||
rc += DNSSerializer::SizeOf(in.sin_addr);
|
||||
rc += sizeof(in.sin_zero);
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsSockAddrIn<T>
|
||||
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) {
|
||||
ssize_t rc = -1;
|
||||
u8 *cur = dst;
|
||||
|
||||
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, sizeof(in), __LINE__)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
const u16 sin_family = static_cast<u16>(in.sin_family);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin_family)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
const u16 sin_port = static_cast<u16>(in.sin_port);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin_port)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
const u32 s_addr = static_cast<u32>(in.sin_addr.s_addr);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), s_addr)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
if (dst_size - (cur - dst) < sizeof(in.sin_zero)) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::memcpy(cur, in.sin_zero, sizeof(in.sin_zero));
|
||||
cur += sizeof(in.sin_zero);
|
||||
|
||||
rc = cur - dst;
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsSockAddrIn<T>
|
||||
ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) {
|
||||
ssize_t rc = -1;
|
||||
const u8 *cur = src;
|
||||
|
||||
u16 sin_family;
|
||||
if ((rc = DNSSerializer::FromBuffer(sin_family, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.sin_family = static_cast<decltype(out.sin_family)>(sin_family);
|
||||
cur += rc;
|
||||
|
||||
u16 sin_port;
|
||||
if ((rc = DNSSerializer::FromBuffer(sin_port, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.sin_port = static_cast<decltype(out.sin_port)>(sin_port);
|
||||
cur += rc;
|
||||
|
||||
u32 s_addr;
|
||||
if ((rc = DNSSerializer::FromBuffer(s_addr, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.sin_addr.s_addr = static_cast<decltype(out.sin_addr.s_addr)>(s_addr);
|
||||
cur += rc;
|
||||
|
||||
if (src_size - (cur - src) < sizeof(out.sin_zero)) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::memcpy(out.sin_zero, cur, sizeof(out.sin_zero));
|
||||
cur += sizeof(out.sin_zero);
|
||||
|
||||
rc = cur - src;
|
||||
return rc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const struct sockaddr_in &in) {
|
||||
return SizeOfImpl(in);
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const ams::socket::SockAddrIn &in) {
|
||||
return SizeOfImpl(in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct sockaddr_in &in) {
|
||||
return ToBufferImpl(dst, dst_size, in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const ams::socket::SockAddrIn &in) {
|
||||
return ToBufferImpl(dst, dst_size, in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(struct sockaddr_in &out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(struct ams::socket::SockAddrIn &out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../dnsmitm_debug.hpp"
|
||||
#include "../socket_allocator.hpp"
|
||||
#include "serializer.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver::serializer {
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename T>
|
||||
concept IsSockAddrIn6 = std::same_as<T, struct sockaddr_in6>;
|
||||
|
||||
template<typename T> requires IsSockAddrIn6<T>
|
||||
size_t SizeOfImpl(const T &in) {
|
||||
size_t rc = 0;
|
||||
rc += sizeof(u16);
|
||||
rc += sizeof(u16);
|
||||
rc += sizeof(u32);
|
||||
rc += DNSSerializer::SizeOf(in.sin6_addr);
|
||||
rc += sizeof(u32);
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsSockAddrIn6<T>
|
||||
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) {
|
||||
ssize_t rc = -1;
|
||||
u8 *cur = dst;
|
||||
|
||||
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, sizeof(in), __LINE__)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
const u16 sin6_family = static_cast<u16>(in.sin6_family);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin6_family)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
const u16 sin6_port = static_cast<u16>(in.sin6_port);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin6_port)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
const u32 sin6_flowinfo = static_cast<u32>(in.sin6_flowinfo);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin6_flowinfo)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
std::memcpy(cur, std::addressof(in.sin6_addr), sizeof(in.sin6_addr));
|
||||
cur += sizeof(in.sin6_addr);
|
||||
|
||||
const u32 sin6_scope_id = static_cast<u32>(in.sin6_scope_id);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin6_scope_id)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
rc = cur - dst;
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsSockAddrIn6<T>
|
||||
ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) {
|
||||
ssize_t rc = -1;
|
||||
const u8 *cur = src;
|
||||
|
||||
u16 sin6_family;
|
||||
if ((rc = DNSSerializer::FromBuffer(sin6_family, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.sin6_family = static_cast<decltype(out.sin6_family)>(sin6_family);
|
||||
cur += rc;
|
||||
|
||||
u16 sin6_port;
|
||||
if ((rc = DNSSerializer::FromBuffer(sin6_port, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.sin6_port = static_cast<decltype(out.sin6_port)>(sin6_port);
|
||||
cur += rc;
|
||||
|
||||
u32 sin6_flowinfo;
|
||||
if ((rc = DNSSerializer::FromBuffer(sin6_flowinfo, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.sin6_flowinfo = static_cast<decltype(out.sin6_flowinfo)>(sin6_flowinfo);
|
||||
cur += rc;
|
||||
|
||||
std::memcpy(std::addressof(out.sin6_addr), cur, sizeof(out.sin6_addr));
|
||||
cur += sizeof(out.sin6_addr);
|
||||
|
||||
u32 sin6_scope_id;
|
||||
if ((rc = DNSSerializer::FromBuffer(sin6_scope_id, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.sin6_scope_id = static_cast<decltype(out.sin6_scope_id)>(sin6_scope_id);
|
||||
cur += rc;
|
||||
|
||||
rc = cur - src;
|
||||
return rc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const struct sockaddr_in6 &in) {
|
||||
return SizeOfImpl(in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct sockaddr_in6 &in) {
|
||||
return ToBufferImpl(dst, dst_size, in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(struct sockaddr_in6 &out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../dnsmitm_debug.hpp"
|
||||
#include "../socket_allocator.hpp"
|
||||
#include "serializer.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver::serializer {
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const char *str) {
|
||||
if (str == nullptr) {
|
||||
return sizeof(char);
|
||||
}
|
||||
return std::strlen(str) + 1;
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const char **str, u32 &out_count) {
|
||||
size_t rc = sizeof(u32);
|
||||
out_count = 0;
|
||||
|
||||
if (str != nullptr) {
|
||||
for (const char **cur = str; *cur != nullptr; ++cur) {
|
||||
++out_count;
|
||||
rc += SizeOf(*cur);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, char *str) {
|
||||
ssize_t rc = -1;
|
||||
u8 *cur = dst;
|
||||
|
||||
if (str == nullptr && dst_size == 0) {
|
||||
return -1;
|
||||
} else if (str == nullptr) {
|
||||
*cur = '\x00';
|
||||
return 1;
|
||||
} else if ((rc = SizeOf(static_cast<const char *>(str))) == 0) {
|
||||
*cur = '\x00';
|
||||
return 1;
|
||||
} else if (CheckToBufferArguments(cur, dst_size, rc + 1, __LINE__) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::memmove(cur, str, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(char *&out, const u8 *src, size_t src_size) {
|
||||
size_t len = 0;
|
||||
|
||||
if (src == nullptr) {
|
||||
return 0;
|
||||
} else if (src_size == 0) {
|
||||
return 0;
|
||||
} else if (src_size < (len = SizeOf(reinterpret_cast<const char *>(src)))) {
|
||||
return 1;
|
||||
} else if (src[0] == '\x00') {
|
||||
return 1;
|
||||
}
|
||||
|
||||
out = static_cast<char *>(ams::socket::impl::Alloc(len));
|
||||
if (out == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::memmove(out, src, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, char **str) {
|
||||
ssize_t rc = -1;
|
||||
u8 *cur = dst;
|
||||
u32 count = 0;
|
||||
|
||||
if (dst_size == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const size_t total_size = SizeOf(const_cast<const char **>(str), count);
|
||||
AMS_UNUSED(total_size);
|
||||
|
||||
if ((rc = CheckToBufferArguments(cur, dst_size, sizeof(u32), __LINE__)) == -1) {
|
||||
return rc;
|
||||
} else if ((rc = ToBuffer(cur, dst_size, count)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
dst_size -= rc;
|
||||
|
||||
if (str != nullptr) {
|
||||
for (char **cur_str = str; *cur_str != nullptr; ++cur_str) {
|
||||
const auto tmp = ToBuffer(cur, dst_size, *cur_str);
|
||||
if (tmp == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
cur += tmp;
|
||||
dst_size -= tmp;
|
||||
rc += tmp;
|
||||
}
|
||||
}
|
||||
|
||||
rc = cur - dst;
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(char **&out, const u8 *src, size_t src_size) {
|
||||
ssize_t rc = -1;
|
||||
const u8 *cur = src;
|
||||
u32 count = 0;
|
||||
|
||||
out = nullptr;
|
||||
|
||||
ON_SCOPE_EXIT {
|
||||
if (rc < 0 && out != nullptr) {
|
||||
u32 i = 0;
|
||||
for (char *str = *out; str != nullptr; str = out[++i]) {
|
||||
ams::socket::impl::Free(str);
|
||||
}
|
||||
ams::socket::impl::Free(out);
|
||||
out = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
if (src == nullptr) {
|
||||
rc = 0;
|
||||
return rc;
|
||||
} else if (src_size == 0) {
|
||||
rc = 0;
|
||||
return rc;
|
||||
} else if ((rc = FromBuffer(count, cur, src_size)) == -1) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
out = static_cast<char **>(ams::socket::impl::Alloc((count + 1) * sizeof(char *)));
|
||||
if (out == nullptr) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
std::memset(out, 0, (count + 1) * sizeof(char *));
|
||||
|
||||
u32 i;
|
||||
for (i = 0; i < count; ++i) {
|
||||
const size_t len = std::strlen(reinterpret_cast<const char *>(cur));
|
||||
out[i] = static_cast<char *>(ams::socket::impl::Alloc(len + 1));
|
||||
if (out[i] == nullptr) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::memmove(out[i], cur, len + 1);
|
||||
cur += len + 1;
|
||||
}
|
||||
|
||||
out[i] = nullptr;
|
||||
rc = cur - src;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "sfdnsres_shim.h"
|
||||
#include <stratosphere/sf/sf_mitm_dispatch.h>
|
||||
|
||||
/* Command forwarders. */
|
||||
Result sfdnsresGetHostByNameRequestWithOptionsFwd(Service *s, u64 process_id, const void *name, size_t name_size, void *out_hostent, size_t out_hostent_size, u32 *out_size, u32 options_version, const void *option, size_t option_size, u32 num_options, s32 *out_host_error, s32 *out_errno) {
|
||||
const struct {
|
||||
u32 options_version;
|
||||
u32 num_options;
|
||||
u64 process_id;
|
||||
} in = { options_version, num_options, process_id };
|
||||
struct {
|
||||
u32 size;
|
||||
s32 host_error;
|
||||
s32 errno;
|
||||
} out;
|
||||
|
||||
Result rc = serviceMitmDispatchInOut(s, 10, in, out,
|
||||
.buffer_attrs = {
|
||||
SfBufferAttr_HipcAutoSelect | SfBufferAttr_In,
|
||||
SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out,
|
||||
SfBufferAttr_HipcAutoSelect | SfBufferAttr_In
|
||||
},
|
||||
.buffers = {
|
||||
{ name, name_size },
|
||||
{ out_hostent, out_hostent_size },
|
||||
{ option, option_size }
|
||||
},
|
||||
.in_send_pid = true,
|
||||
.override_pid = process_id,
|
||||
);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (out_size) *out_size = out.size;
|
||||
if (out_host_error) *out_host_error = out.host_error;
|
||||
if (out_errno) *out_errno = out.errno;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result sfdnsresGetAddrInfoRequestWithOptionsFwd(Service *s, u64 process_id, const void *node, size_t node_size, const void *srv, size_t srv_size, const void *hint, size_t hint_size, void *out_ai, size_t out_ai_size, u32 *out_size, s32 *out_rv, u32 options_version, const void *option, size_t option_size, u32 num_options, s32 *out_host_error, s32 *out_errno) {
|
||||
const struct {
|
||||
u32 options_version;
|
||||
u32 num_options;
|
||||
u64 process_id;
|
||||
} in = { options_version, num_options, process_id };
|
||||
struct {
|
||||
u32 size;
|
||||
s32 rv;
|
||||
s32 host_error;
|
||||
s32 errno;
|
||||
} out;
|
||||
|
||||
Result rc = serviceMitmDispatchInOut(s, 12, in, out,
|
||||
.buffer_attrs = {
|
||||
SfBufferAttr_HipcMapAlias | SfBufferAttr_In,
|
||||
SfBufferAttr_HipcMapAlias | SfBufferAttr_In,
|
||||
SfBufferAttr_HipcMapAlias | SfBufferAttr_In,
|
||||
SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out,
|
||||
SfBufferAttr_HipcAutoSelect | SfBufferAttr_In
|
||||
},
|
||||
.buffers = {
|
||||
{ node, node_size },
|
||||
{ srv, srv_size },
|
||||
{ hint, hint_size },
|
||||
{ out_ai, out_ai_size },
|
||||
{ option, option_size }
|
||||
},
|
||||
.in_send_pid = true,
|
||||
.override_pid = process_id,
|
||||
);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (out_size) *out_size = out.size;
|
||||
if (out_rv) *out_rv = out.rv;
|
||||
if (out_host_error) *out_host_error = out.host_error;
|
||||
if (out_errno) *out_errno = out.errno;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* @file sfdnsres_shim.h
|
||||
* @brief IPC wrapper for dns.mitm.
|
||||
* @author SciresM
|
||||
* @copyright libnx Authors
|
||||
*/
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Command forwarders. */
|
||||
Result sfdnsresGetHostByNameRequestWithOptionsFwd(Service *s, u64 process_id, const void *name, size_t name_size, void *out_hostent, size_t out_hostent_size, u32 *out_size, u32 options_version, const void *option, size_t option_size, u32 num_options, s32 *out_host_error, s32 *out_errno);
|
||||
|
||||
Result sfdnsresGetAddrInfoRequestWithOptionsFwd(Service *s, u64 process_id, const void *node, size_t node_size, const void *srv, size_t srv_size, const void *hint, size_t hint_size, void *out_ai, size_t out_ai_size, u32 *out_size, s32 *out_rv, u32 options_version, const void *option, size_t option_size, u32 num_options, s32 *out_host_error, s32 *out_errno);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::socket::impl {
|
||||
|
||||
void *Alloc(size_t size);
|
||||
void *Calloc(size_t num, size_t size);
|
||||
void Free(void *ptr);
|
||||
|
||||
}
|
||||
@@ -1,383 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_fs_utils.hpp"
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "fs_shim.h"
|
||||
#include "fs_mitm_service.hpp"
|
||||
#include "fsmitm_boot0storage.hpp"
|
||||
#include "fsmitm_calibration_binary_storage.hpp"
|
||||
#include "fsmitm_layered_romfs_storage.hpp"
|
||||
#include "fsmitm_save_utils.hpp"
|
||||
#include "fsmitm_readonly_layered_filesystem.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
using namespace ams::fs;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const ams::fs::Path AtmosphereHblWebContentDirPath = fs::MakeConstantPath("/atmosphere/hbl_html/");
|
||||
constexpr const char ProgramWebContentDir[] = "/manual_html/";
|
||||
|
||||
constinit os::SdkMutex g_boot0_detect_lock;
|
||||
constinit bool g_detected_boot0_kind = false;
|
||||
constinit bool g_is_boot0_custom_public_key = false;
|
||||
|
||||
constinit fssrv::impl::ProgramIndexMapInfoManager g_program_index_map_info_manager;
|
||||
|
||||
bool IsBoot0CustomPublicKey(::FsStorage &storage) {
|
||||
if (AMS_UNLIKELY(!g_detected_boot0_kind)) {
|
||||
std::scoped_lock lk(g_boot0_detect_lock);
|
||||
|
||||
if (AMS_LIKELY(!g_detected_boot0_kind)) {
|
||||
g_is_boot0_custom_public_key = DetectBoot0CustomPublicKey(storage);
|
||||
g_detected_boot0_kind = true;
|
||||
}
|
||||
}
|
||||
|
||||
return g_is_boot0_custom_public_key;
|
||||
}
|
||||
|
||||
bool GetSettingsItemBooleanValue(const char *name, const char *key) {
|
||||
u8 tmp = 0;
|
||||
AMS_ABORT_UNLESS(settings::fwdbg::GetSettingsItemValue(std::addressof(tmp), sizeof(tmp), name, key) == sizeof(tmp));
|
||||
return (tmp != 0);
|
||||
}
|
||||
|
||||
template<typename... Arguments>
|
||||
constexpr ALWAYS_INLINE auto MakeSharedFileSystem(Arguments &&... args) {
|
||||
return sf::CreateSharedObjectEmplaced<ams::fssrv::sf::IFileSystem, ams::fssrv::impl::FileSystemInterfaceAdapter>(std::forward<Arguments>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Arguments>
|
||||
constexpr ALWAYS_INLINE auto MakeSharedStorage(Arguments &&... args) {
|
||||
return sf::CreateSharedObjectEmplaced<ams::fssrv::sf::IStorage, ams::fssrv::impl::StorageInterfaceAdapter>(std::forward<Arguments>(args)...);
|
||||
}
|
||||
|
||||
Result OpenHblWebContentFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> &out, ncm::ProgramId program_id) {
|
||||
/* Verify eligibility. */
|
||||
bool is_hbl;
|
||||
R_UNLESS(R_SUCCEEDED(ams::pm::info::IsHblProgramId(std::addressof(is_hbl), program_id)), sm::mitm::ResultShouldForwardToSession());
|
||||
R_UNLESS(is_hbl, sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Hbl html directory must exist. */
|
||||
{
|
||||
FsDir d;
|
||||
R_UNLESS(R_SUCCEEDED(mitm::fs::OpenSdDirectory(std::addressof(d), AtmosphereHblWebContentDirPath.GetString(), fs::OpenDirectoryMode_Directory)), sm::mitm::ResultShouldForwardToSession());
|
||||
fsDirClose(std::addressof(d));
|
||||
}
|
||||
|
||||
/* Open the SD card using fs.mitm's session. */
|
||||
FsFileSystem sd_fs;
|
||||
R_TRY(fsOpenSdCardFileSystem(std::addressof(sd_fs)));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(sd_fs.s))};
|
||||
std::unique_ptr<fs::fsa::IFileSystem> sd_ifs = std::make_unique<fs::RemoteFileSystem>(sd_fs);
|
||||
|
||||
auto subdir_fs = std::make_unique<fssystem::SubDirectoryFileSystem>(std::move(sd_ifs));
|
||||
R_TRY(subdir_fs->Initialize(AtmosphereHblWebContentDirPath));
|
||||
|
||||
out.SetValue(MakeSharedFileSystem(std::make_shared<fs::ReadOnlyFileSystem>(std::move(subdir_fs)), false), target_object_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result OpenProgramSpecificWebContentFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> &out, ncm::ProgramId program_id, FsFileSystemType filesystem_type, Service *fwd, const fssrv::sf::Path *path, bool with_id) {
|
||||
/* Directory must exist. */
|
||||
R_UNLESS(HasSdManualHtmlContent(program_id), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Open the SD card using fs.mitm's session. */
|
||||
FsFileSystem sd_fs;
|
||||
R_TRY(fsOpenSdCardFileSystem(std::addressof(sd_fs)));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(sd_fs.s))};
|
||||
std::unique_ptr<fs::fsa::IFileSystem> sd_ifs = std::make_unique<fs::RemoteFileSystem>(sd_fs);
|
||||
|
||||
/* Format the subdirectory path. */
|
||||
char program_web_content_raw_path[0x100];
|
||||
FormatAtmosphereSdPath(program_web_content_raw_path, sizeof(program_web_content_raw_path), program_id, ProgramWebContentDir);
|
||||
|
||||
ams::fs::Path program_web_content_path;
|
||||
R_TRY(program_web_content_path.SetShallowBuffer(program_web_content_raw_path));
|
||||
|
||||
/* Make a new filesystem. */
|
||||
{
|
||||
auto subdir_fs = std::make_unique<fssystem::SubDirectoryFileSystem>(std::move(sd_ifs));
|
||||
R_TRY(subdir_fs->Initialize(program_web_content_path));
|
||||
|
||||
std::shared_ptr<fs::fsa::IFileSystem> new_fs = nullptr;
|
||||
|
||||
/* Try to open the existing fs. */
|
||||
FsFileSystem base_fs;
|
||||
bool opened_base_fs = false;
|
||||
if (with_id) {
|
||||
opened_base_fs = R_SUCCEEDED(fsOpenFileSystemWithIdFwd(fwd, std::addressof(base_fs), static_cast<u64>(program_id), filesystem_type, path->str));
|
||||
} else {
|
||||
opened_base_fs = R_SUCCEEDED(fsOpenFileSystemWithPatchFwd(fwd, std::addressof(base_fs), static_cast<u64>(program_id), filesystem_type));
|
||||
}
|
||||
|
||||
if (opened_base_fs) {
|
||||
/* Create a layered adapter. */
|
||||
new_fs = std::make_shared<ReadOnlyLayeredFileSystem>(std::move(subdir_fs), std::make_unique<fs::RemoteFileSystem>(base_fs));
|
||||
} else {
|
||||
/* Without an existing FS, just make a read only adapter to the subdirectory. */
|
||||
new_fs = std::make_shared<fs::ReadOnlyFileSystem>(std::move(subdir_fs));
|
||||
}
|
||||
|
||||
out.SetValue(MakeSharedFileSystem(std::move(new_fs), false), target_object_id);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result OpenWebContentFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> &out, ncm::ProgramId client_program_id, ncm::ProgramId program_id, FsFileSystemType filesystem_type, Service *fwd, const fssrv::sf::Path *path, bool with_id, bool try_program_specific) {
|
||||
/* Check first that we're a web applet opening web content. */
|
||||
R_UNLESS(ncm::IsWebAppletId(client_program_id), sm::mitm::ResultShouldForwardToSession());
|
||||
R_UNLESS(filesystem_type == FsFileSystemType_ContentManual, sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Try to mount the HBL web filesystem. If this succeeds then we're done. */
|
||||
R_SUCCEED_IF(R_SUCCEEDED(OpenHblWebContentFileSystem(out, program_id)));
|
||||
|
||||
/* If program specific override shouldn't be attempted, fall back. */
|
||||
R_UNLESS(try_program_specific, sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* If we're not opening a HBL filesystem, just try to open a generic one. */
|
||||
R_RETURN(OpenProgramSpecificWebContentFileSystem(out, program_id, filesystem_type, fwd, path, with_id));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool HasSdManualHtmlContent(ncm::ProgramId program_id) {
|
||||
/* Directory must exist. */
|
||||
FsDir d;
|
||||
if (R_SUCCEEDED(OpenAtmosphereSdDirectory(std::addressof(d), program_id, ProgramWebContentDir, fs::OpenDirectoryMode_Directory))) {
|
||||
::fsDirClose(std::addressof(d));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Result FsMitmService::OpenFileSystemWithPatch(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, ncm::ProgramId program_id, u32 _filesystem_type) {
|
||||
R_RETURN(OpenWebContentFileSystem(out, m_client_info.program_id, program_id, static_cast<FsFileSystemType>(_filesystem_type), m_forward_service.get(), nullptr, false, m_client_info.override_status.IsProgramSpecific()));
|
||||
}
|
||||
|
||||
Result FsMitmService::OpenFileSystemWithId(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, const fssrv::sf::Path &path, ncm::ProgramId program_id, u32 _filesystem_type) {
|
||||
R_RETURN(OpenWebContentFileSystem(out, m_client_info.program_id, program_id, static_cast<FsFileSystemType>(_filesystem_type), m_forward_service.get(), std::addressof(path), true, m_client_info.override_status.IsProgramSpecific()));
|
||||
}
|
||||
|
||||
Result FsMitmService::OpenSdCardFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out) {
|
||||
/* We only care about redirecting this for NS/emummc. */
|
||||
R_UNLESS(m_client_info.program_id == ncm::SystemProgramId::Ns, sm::mitm::ResultShouldForwardToSession());
|
||||
R_UNLESS(emummc::IsActive(), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Create a new SD card filesystem. */
|
||||
FsFileSystem sd_fs;
|
||||
R_TRY(fsOpenSdCardFileSystemFwd(m_forward_service.get(), std::addressof(sd_fs)));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(sd_fs.s))};
|
||||
|
||||
/* Return output filesystem. */
|
||||
auto redir_fs = std::make_shared<fssystem::DirectoryRedirectionFileSystem>(std::make_unique<RemoteFileSystem>(sd_fs));
|
||||
R_TRY(redir_fs->InitializeWithFixedPath("/Nintendo", emummc::GetNintendoDirPath()));
|
||||
|
||||
out.SetValue(MakeSharedFileSystem(std::move(redir_fs), false), target_object_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FsMitmService::OpenSaveDataFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, u8 _space_id, const fs::SaveDataAttribute &attribute) {
|
||||
/* We only want to intercept saves for games, right now. */
|
||||
const bool is_game_or_hbl = m_client_info.override_status.IsHbl() || ncm::IsApplicationId(m_client_info.program_id);
|
||||
R_UNLESS(is_game_or_hbl, sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Only redirect if the appropriate system setting is set. */
|
||||
R_UNLESS(GetSettingsItemBooleanValue("atmosphere", "fsmitm_redirect_saves_to_sd"), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Only redirect if the specific title being accessed has a redirect save flag. */
|
||||
R_UNLESS(cfg::HasContentSpecificFlag(m_client_info.program_id, "redirect_save"), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Only redirect account savedata. */
|
||||
R_UNLESS(attribute.type == fs::SaveDataType::Account, sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Get enum type for space id. */
|
||||
auto space_id = static_cast<FsSaveDataSpaceId>(_space_id);
|
||||
|
||||
/* Verify we can open the save. */
|
||||
static_assert(sizeof(fs::SaveDataAttribute) == sizeof(::FsSaveDataAttribute));
|
||||
FsFileSystem save_fs;
|
||||
R_UNLESS(R_SUCCEEDED(fsOpenSaveDataFileSystemFwd(m_forward_service.get(), std::addressof(save_fs), space_id, reinterpret_cast<const FsSaveDataAttribute *>(std::addressof(attribute)))), sm::mitm::ResultShouldForwardToSession());
|
||||
std::unique_ptr<fs::fsa::IFileSystem> save_ifs = std::make_unique<fs::RemoteFileSystem>(save_fs);
|
||||
|
||||
/* Mount the SD card using fs.mitm's session. */
|
||||
FsFileSystem sd_fs;
|
||||
R_TRY(fsOpenSdCardFileSystem(std::addressof(sd_fs)));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(sd_fs.s))};
|
||||
std::shared_ptr<fs::fsa::IFileSystem> sd_ifs = std::make_shared<fs::RemoteFileSystem>(sd_fs);
|
||||
|
||||
/* Verify that we can open the save directory, and that it exists. */
|
||||
const ncm::ProgramId application_id = attribute.program_id == ncm::InvalidProgramId ? m_client_info.program_id : attribute.program_id;
|
||||
|
||||
char save_dir_raw_path[0x100];
|
||||
R_TRY(mitm::fs::SaveUtil::GetDirectorySaveDataPath(save_dir_raw_path, sizeof(save_dir_raw_path), application_id, space_id, attribute));
|
||||
|
||||
ams::fs::Path save_dir_path;
|
||||
R_TRY(save_dir_path.SetShallowBuffer(save_dir_raw_path));
|
||||
|
||||
/* Check if this is the first time we're making the save. */
|
||||
bool is_new_save = false;
|
||||
{
|
||||
fs::DirectoryEntryType ent;
|
||||
R_TRY_CATCH(sd_ifs->GetEntryType(std::addressof(ent), save_dir_path)) {
|
||||
R_CATCH(fs::ResultPathNotFound) { is_new_save = true; }
|
||||
R_CATCH_ALL() { /* ... */ }
|
||||
} R_END_TRY_CATCH;
|
||||
}
|
||||
|
||||
/* Ensure the directory exists. */
|
||||
R_TRY(fssystem::EnsureDirectory(sd_ifs.get(), save_dir_path));
|
||||
|
||||
/* Create directory savedata filesystem. */
|
||||
auto subdir_fs = std::make_unique<fssystem::SubDirectoryFileSystem>(sd_ifs);
|
||||
R_TRY(subdir_fs->Initialize(save_dir_path));
|
||||
|
||||
std::shared_ptr<fssystem::DirectorySaveDataFileSystem> dirsave_ifs = std::make_shared<fssystem::DirectorySaveDataFileSystem>(std::move(subdir_fs));
|
||||
|
||||
/* Ensure correct directory savedata filesystem state. */
|
||||
R_TRY(dirsave_ifs->Initialize(true, true, true));
|
||||
|
||||
/* If it's the first time we're making the save, copy existing savedata over. */
|
||||
if (is_new_save) {
|
||||
/* TODO: Check error? */
|
||||
fs::DirectoryEntry work_entry;
|
||||
constexpr const fs::Path root_path = fs::MakeConstantPath("/");
|
||||
|
||||
u8 savedata_copy_buffer[2_KB];
|
||||
fssystem::CopyDirectoryRecursively(dirsave_ifs.get(), save_ifs.get(), root_path, root_path, std::addressof(work_entry), savedata_copy_buffer, sizeof(savedata_copy_buffer));
|
||||
}
|
||||
|
||||
/* Set output. */
|
||||
out.SetValue(MakeSharedFileSystem(std::move(dirsave_ifs), false), target_object_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FsMitmService::OpenBisStorage(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, u32 _bis_partition_id) {
|
||||
const ::FsBisPartitionId bis_partition_id = static_cast<::FsBisPartitionId>(_bis_partition_id);
|
||||
|
||||
/* Try to open a storage for the partition. */
|
||||
FsStorage bis_storage;
|
||||
R_TRY(fsOpenBisStorageFwd(m_forward_service.get(), std::addressof(bis_storage), bis_partition_id));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(bis_storage.s))};
|
||||
|
||||
const bool is_sysmodule = ncm::IsSystemProgramId(m_client_info.program_id);
|
||||
const bool is_hbl = m_client_info.override_status.IsHbl();
|
||||
const bool can_write_bis = is_sysmodule || (is_hbl && GetSettingsItemBooleanValue("atmosphere", "enable_hbl_bis_write"));
|
||||
|
||||
/* Allow HBL to write to boot1 (safe firm) + package2. */
|
||||
/* This is needed to not break compatibility with ChoiDujourNX, which does not check for write access before beginning an update. */
|
||||
/* TODO: get fixed so that this can be turned off without causing bricks :/ */
|
||||
const bool is_package2 = (FsBisPartitionId_BootConfigAndPackage2Part1 <= bis_partition_id && bis_partition_id <= FsBisPartitionId_BootConfigAndPackage2Part6);
|
||||
const bool is_boot1 = bis_partition_id == FsBisPartitionId_BootPartition2Root;
|
||||
const bool can_write_bis_for_choi_support = is_hbl && (is_package2 || is_boot1);
|
||||
|
||||
/* Set output storage. */
|
||||
if (bis_partition_id == FsBisPartitionId_BootPartition1Root) {
|
||||
if (IsBoot0CustomPublicKey(bis_storage)) {
|
||||
out.SetValue(MakeSharedStorage(std::make_shared<CustomPublicKeyBoot0Storage>(bis_storage, m_client_info, spl::GetSocType())), target_object_id);
|
||||
} else {
|
||||
out.SetValue(MakeSharedStorage(std::make_shared<Boot0Storage>(bis_storage, m_client_info)), target_object_id);
|
||||
}
|
||||
} else if (bis_partition_id == FsBisPartitionId_CalibrationBinary) {
|
||||
out.SetValue(MakeSharedStorage(std::make_shared<CalibrationBinaryStorage>(bis_storage, m_client_info)), target_object_id);
|
||||
} else {
|
||||
if (can_write_bis || can_write_bis_for_choi_support) {
|
||||
/* We can write, so create a writable storage. */
|
||||
out.SetValue(MakeSharedStorage(std::make_shared<RemoteStorage>(bis_storage)), target_object_id);
|
||||
} else {
|
||||
/* We can only read, so create a readable storage. */
|
||||
std::unique_ptr<ams::fs::IStorage> unique_bis = std::make_unique<RemoteStorage>(bis_storage);
|
||||
out.SetValue(MakeSharedStorage(std::make_shared<ReadOnlyStorageAdapter>(std::move(unique_bis))), target_object_id);
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FsMitmService::OpenDataStorageByCurrentProcess(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out) {
|
||||
/* Only mitm if we should override contents for the current process. */
|
||||
R_UNLESS(m_client_info.override_status.IsProgramSpecific(), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Only mitm if there is actually an override romfs. */
|
||||
R_UNLESS(mitm::fs::HasSdRomfsContent(m_client_info.program_id), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Try to open the process romfs. */
|
||||
FsStorage data_storage;
|
||||
R_TRY(fsOpenDataStorageByCurrentProcessFwd(m_forward_service.get(), std::addressof(data_storage)));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
|
||||
|
||||
/* Get a layered storage for the process romfs. */
|
||||
out.SetValue(MakeSharedStorage(GetLayeredRomfsStorage(m_client_info.program_id, data_storage, true)), target_object_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FsMitmService::OpenDataStorageByDataId(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, ncm::DataId _data_id, u8 storage_id) {
|
||||
/* Only mitm if we should override contents for the current process. */
|
||||
R_UNLESS(m_client_info.override_status.IsProgramSpecific(), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* TODO: Decide how to handle DataId vs ProgramId for this API. */
|
||||
const ncm::ProgramId data_id = {_data_id.value};
|
||||
|
||||
/* Only mitm if there is actually an override romfs. */
|
||||
R_UNLESS(mitm::fs::HasSdRomfsContent(data_id), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Try to open the data id. */
|
||||
FsStorage data_storage;
|
||||
R_TRY(fsOpenDataStorageByDataIdFwd(m_forward_service.get(), std::addressof(data_storage), static_cast<u64>(data_id), static_cast<NcmStorageId>(storage_id)));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
|
||||
|
||||
/* Get a layered storage for the data id. */
|
||||
out.SetValue(MakeSharedStorage(GetLayeredRomfsStorage(data_id, data_storage, false)), target_object_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FsMitmService::OpenDataStorageWithProgramIndex(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, u8 program_index) {
|
||||
/* Only mitm if we should override contents for the current process. */
|
||||
R_UNLESS(m_client_info.override_status.IsProgramSpecific(), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Get the relevant program id. */
|
||||
const ncm::ProgramId program_id = g_program_index_map_info_manager.GetProgramId(m_client_info.program_id, program_index);
|
||||
|
||||
/* If we don't know about the program or don't have content, forward. */
|
||||
R_UNLESS(program_id != ncm::InvalidProgramId, sm::mitm::ResultShouldForwardToSession());
|
||||
R_UNLESS(mitm::fs::HasSdRomfsContent(program_id), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Try to open the process romfs. */
|
||||
FsStorage data_storage;
|
||||
R_TRY(fsOpenDataStorageWithProgramIndexFwd(m_forward_service.get(), std::addressof(data_storage), program_index));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
|
||||
|
||||
/* Get a layered storage for the process romfs. */
|
||||
out.SetValue(MakeSharedStorage(GetLayeredRomfsStorage(program_id, data_storage, true)), target_object_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FsMitmService::RegisterProgramIndexMapInfo(const sf::InBuffer &info_buffer, s32 info_count) {
|
||||
/* Try to register with FS. */
|
||||
R_TRY(fsRegisterProgramIndexMapInfoFwd(m_forward_service.get(), info_buffer.GetPointer(), info_buffer.GetSize(), info_count));
|
||||
|
||||
/* Register with ourselves. */
|
||||
R_ABORT_UNLESS(g_program_index_map_info_manager.Reset(reinterpret_cast<const fs::ProgramIndexMapInfo *>(info_buffer.GetPointer()), info_count));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/fssrv/fssrv_interface_adapters.hpp>
|
||||
|
||||
#define AMS_FS_MITM_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 7, Result, OpenFileSystemWithPatch, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, ncm::ProgramId program_id, u32 _filesystem_type), (out, program_id, _filesystem_type), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 8, Result, OpenFileSystemWithId, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, const fssrv::sf::Path &path, ncm::ProgramId program_id, u32 _filesystem_type), (out, path, program_id, _filesystem_type), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 18, Result, OpenSdCardFileSystem, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 51, Result, OpenSaveDataFileSystem, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, u8 space_id, const ams::fs::SaveDataAttribute &attribute), (out, space_id, attribute)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 12, Result, OpenBisStorage, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, u32 bis_partition_id), (out, bis_partition_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 200, Result, OpenDataStorageByCurrentProcess, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 202, Result, OpenDataStorageByDataId, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, ncm::DataId data_id, u8 storage_id), (out, data_id, storage_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 205, Result, OpenDataStorageWithProgramIndex, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, u8 program_index), (out, program_index), hos::Version_7_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 810, Result, RegisterProgramIndexMapInfo, (const sf::InBuffer &info_buffer, s32 info_count), (info_buffer, info_count), hos::Version_7_0_0)
|
||||
|
||||
|
||||
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::fs, IFsMitmInterface, AMS_FS_MITM_INTERFACE_INFO, 0x7DF34ED2)
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
class FsMitmService : public sf::MitmServiceImplBase {
|
||||
public:
|
||||
using MitmServiceImplBase::MitmServiceImplBase;
|
||||
public:
|
||||
static constexpr ALWAYS_INLINE bool ShouldMitmProgramId(const ncm::ProgramId program_id) {
|
||||
/* We want to mitm everything that isn't a system-module. */
|
||||
if (!ncm::IsSystemProgramId(program_id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* We want to mitm ns, to intercept SD card requests and program index map info registration. */
|
||||
if (program_id == ncm::SystemProgramId::Ns) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* We want to mitm settings, to intercept CAL0. */
|
||||
if (program_id == ncm::SystemProgramId::Settings) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* We want to mitm sdb, to support sd-romfs redirection of common system archives (like system font, etc). */
|
||||
/* NOTE: In 16.0.0+, this was moved to glue. */
|
||||
if (program_id == ncm::SystemProgramId::Sdb || program_id == ncm::SystemProgramId::Glue) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
|
||||
static std::atomic_bool has_launched_qlaunch = false;
|
||||
|
||||
/* TODO: intercepting everything seems to cause issues with sleep mode, for some reason. */
|
||||
/* Figure out why, and address it. */
|
||||
/* TODO: This may be because pre-rewrite code really mismanaged domain objects in a way that would cause bad things. */
|
||||
/* Need to verify if this is fixed now. */
|
||||
if (client_info.program_id == ncm::SystemAppletId::Qlaunch || client_info.program_id == ncm::SystemAppletId::MaintenanceMenu) {
|
||||
has_launched_qlaunch = true;
|
||||
}
|
||||
|
||||
return has_launched_qlaunch || ShouldMitmProgramId(client_info.program_id);
|
||||
}
|
||||
public:
|
||||
/* Overridden commands. */
|
||||
Result OpenFileSystemWithPatch(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, ncm::ProgramId program_id, u32 _filesystem_type);
|
||||
Result OpenFileSystemWithId(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, const fssrv::sf::Path &path, ncm::ProgramId program_id, u32 _filesystem_type);
|
||||
Result OpenSdCardFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out);
|
||||
Result OpenSaveDataFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, u8 space_id, const ams::fs::SaveDataAttribute &attribute);
|
||||
Result OpenBisStorage(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, u32 bis_partition_id);
|
||||
Result OpenDataStorageByCurrentProcess(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out);
|
||||
Result OpenDataStorageByDataId(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, ncm::DataId data_id, u8 storage_id);
|
||||
Result OpenDataStorageWithProgramIndex(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, u8 program_index);
|
||||
Result RegisterProgramIndexMapInfo(const sf::InBuffer &info_buffer, s32 info_count);
|
||||
};
|
||||
static_assert(IsIFsMitmInterface<FsMitmService>);
|
||||
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "fs_shim.h"
|
||||
|
||||
/* Missing fsp-srv commands. */
|
||||
static Result _fsOpenSession(Service *s, Service* out, u32 cmd_id) {
|
||||
return serviceDispatch(s, cmd_id,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = out,
|
||||
);
|
||||
}
|
||||
|
||||
Result fsOpenSdCardFileSystemFwd(Service* s, FsFileSystem* out) {
|
||||
return _fsOpenSession(s, &out->s, 18);
|
||||
}
|
||||
|
||||
Result fsOpenBisStorageFwd(Service* s, FsStorage* out, FsBisPartitionId partition_id) {
|
||||
const u32 tmp = partition_id;
|
||||
return serviceDispatchIn(s, 12, tmp,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = &out->s,
|
||||
);
|
||||
}
|
||||
|
||||
Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out) {
|
||||
return _fsOpenSession(s, &out->s, 200);
|
||||
}
|
||||
|
||||
Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorage* out, u64 data_id, NcmStorageId storage_id) {
|
||||
const struct {
|
||||
u8 storage_id;
|
||||
u64 data_id;
|
||||
} in = { storage_id, data_id };
|
||||
|
||||
return serviceDispatchIn(s, 202, in,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = &out->s,
|
||||
);
|
||||
}
|
||||
|
||||
Result fsOpenDataStorageWithProgramIndexFwd(Service* s, FsStorage* out, u8 program_index) {
|
||||
return serviceDispatchIn(s, 205, program_index,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = &out->s,
|
||||
);
|
||||
}
|
||||
|
||||
Result fsRegisterProgramIndexMapInfoFwd(Service* s, const void *buf, size_t buf_size, s32 count) {
|
||||
return serviceDispatchIn(s, 810, count,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
|
||||
.buffers = { { buf, buf_size } },
|
||||
);
|
||||
}
|
||||
|
||||
Result fsOpenSaveDataFileSystemFwd(Service* s, FsFileSystem* out, FsSaveDataSpaceId save_data_space_id, const FsSaveDataAttribute *attr) {
|
||||
const struct {
|
||||
u8 save_data_space_id;
|
||||
u8 pad[7];
|
||||
FsSaveDataAttribute attr;
|
||||
} in = { (u8)save_data_space_id, {0}, *attr };
|
||||
|
||||
return serviceDispatchIn(s, 51, in,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = &out->s,
|
||||
);
|
||||
}
|
||||
|
||||
Result fsOpenFileSystemWithPatchFwd(Service* s, FsFileSystem* out, u64 id, FsFileSystemType fsType) {
|
||||
const struct {
|
||||
u32 fsType;
|
||||
u64 id;
|
||||
} in = { fsType, id };
|
||||
|
||||
return serviceDispatchIn(s, 7, in,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = &out->s
|
||||
);
|
||||
}
|
||||
|
||||
Result fsOpenFileSystemWithIdFwd(Service* s, FsFileSystem* out, u64 id, FsFileSystemType fsType, const char* contentPath) {
|
||||
const struct {
|
||||
u32 fsType;
|
||||
u64 id;
|
||||
} in = { fsType, id };
|
||||
|
||||
return serviceDispatchIn(s, 8, in,
|
||||
.buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In },
|
||||
.buffers = { { contentPath, FS_MAX_PATH } },
|
||||
.out_num_objects = 1,
|
||||
.out_objects = &out->s
|
||||
);
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/**
|
||||
* @file fs_shim.h
|
||||
* @brief Filesystem Services (fs) IPC wrapper for fs.mitm.
|
||||
* @author SciresM
|
||||
* @copyright libnx Authors
|
||||
*/
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Missing fsp-srv commands. */
|
||||
Result fsOpenSdCardFileSystemFwd(Service* s, FsFileSystem* out);
|
||||
Result fsOpenBisStorageFwd(Service* s, FsStorage* out, FsBisPartitionId partition_id);
|
||||
Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out);
|
||||
Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorage* out, u64 data_id, NcmStorageId storage_id);
|
||||
Result fsOpenDataStorageWithProgramIndexFwd(Service* s, FsStorage* out, u8 program_index);
|
||||
|
||||
Result fsRegisterProgramIndexMapInfoFwd(Service* s, const void *buf, size_t buf_size, s32 count);
|
||||
|
||||
Result fsOpenSaveDataFileSystemFwd(Service* s, FsFileSystem* out, FsSaveDataSpaceId save_data_space_id, const FsSaveDataAttribute *attr);
|
||||
|
||||
Result fsOpenFileSystemWithPatchFwd(Service* s, FsFileSystem* out, u64 id, FsFileSystemType fsType);
|
||||
Result fsOpenFileSystemWithIdFwd(Service* s, FsFileSystem* out, u64 id, FsFileSystemType fsType, const char* contentPath);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,267 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsmitm_boot0storage.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
using namespace ams::fs;
|
||||
|
||||
namespace {
|
||||
|
||||
constinit os::SdkMutex g_boot0_access_mutex;
|
||||
constinit bool g_custom_public_key = false;
|
||||
constinit u8 g_boot0_bct_buffer[Boot0Storage::BctEndOffset];
|
||||
|
||||
/* Recognize special public key (https://gist.github.com/SciresM/16b63ac1d80494522bdba2c57995257c). */
|
||||
/* P = 19 */
|
||||
/* Q = 1696986749729493925354392349339746171297507422986462747526968361144447230710192316397327889522451749459854070558277878297255552508603806832852079596337539247651161831569525505882103311631577368514276343192042634740927726070847704397913856975832811679847928433261678072951551065705680482548543833651752439700272736498378724153330763357721354498194000536297732323628263256733931353143625854828275237159155585342783077681713929284136658773985266864804093157854331138230313706015557050002740810464618031715670281442110238274404626065924786185264268216336867948322976979393032640085259926883014490947373494538254895109731 */
|
||||
/* N = 0xFF696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696959 */
|
||||
/* E = 0x10001 */
|
||||
/* D = 6512128715229088976470211610075969347035078304643231077895577077900787352712063823560162578441773733649014439616165727455431015055675770987914713980812453585413988983206576233689754710500864883529402371292948326392791238474661859182717295176679567362482790015587820446999760239570255254879359445627372805817473978644067558931078225451477635089763009580492462097185005355990612929951162366081631888011031830742459571000203341001926135389508196521518687349554188686396554248868403128728646457247197407637887195043486221295751496667162366700477934591694110831002874992896076061627516220934290742867397720103040314639313 */
|
||||
|
||||
constexpr const u8 CustomPublicKey[0x100] = {
|
||||
0x59, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0xFF,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
bool Boot0Storage::CanModifyBctPublicKey() {
|
||||
if (exosphere::IsRcmBugPatched()) {
|
||||
/* RCM bug patched. */
|
||||
/* Only allow NS to update the BCT pubks. */
|
||||
/* AutoRCM on a patched unit will cause a brick, so homebrew should NOT be allowed to write. */
|
||||
return m_client_info.program_id == ncm::SystemProgramId::Ns;
|
||||
} else {
|
||||
/* RCM bug unpatched. */
|
||||
/* Allow homebrew but not NS to update the BCT pubks. */
|
||||
return m_client_info.override_status.IsHbl();
|
||||
}
|
||||
}
|
||||
|
||||
Result Boot0Storage::Read(s64 offset, void *_buffer, size_t size) {
|
||||
std::scoped_lock lk{g_boot0_access_mutex};
|
||||
AMS_ABORT_UNLESS(!g_custom_public_key);
|
||||
|
||||
/* Check if we have nothing to do. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
R_RETURN(Base::Read(offset, _buffer, size));
|
||||
}
|
||||
|
||||
Result Boot0Storage::Write(s64 offset, const void *_buffer, size_t size) {
|
||||
std::scoped_lock lk{g_boot0_access_mutex};
|
||||
AMS_ABORT_UNLESS(!g_custom_public_key);
|
||||
|
||||
const u8 *buffer = static_cast<const u8 *>(_buffer);
|
||||
|
||||
/* Check if we have nothing to do. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Protect the EKS region from writes. */
|
||||
if (offset <= EksStart) {
|
||||
if (offset + size < EksStart) {
|
||||
/* Fall through, no need to do anything here. */
|
||||
} else {
|
||||
if (offset + size > EksEnd) {
|
||||
/* Perform portion of write falling past end of keyblobs. */
|
||||
const s64 diff = EksEnd - offset;
|
||||
R_TRY(Base::Write(EksEnd, buffer + diff, size - diff));
|
||||
}
|
||||
/* Adjust size to avoid writing end of data. */
|
||||
size = EksStart - offset;
|
||||
}
|
||||
} else if (offset < EksEnd) {
|
||||
/* Ignore writes falling strictly within the region. */
|
||||
R_SUCCEED_IF(offset + size <= EksEnd);
|
||||
|
||||
/* Only write past the end of the region. */
|
||||
const s64 diff = EksEnd - offset;
|
||||
buffer += diff;
|
||||
size -= diff;
|
||||
offset = EksEnd;
|
||||
}
|
||||
|
||||
/* If we have nothing to write, succeed immediately. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* We want to protect AutoRCM from NS on ipatched units. If we can modify bct pubks or we're not touching any of them, proceed. */
|
||||
if (this->CanModifyBctPublicKey() || offset >= BctEndOffset || (util::AlignUp(offset, BctSize) >= BctEndOffset && (offset % BctSize) >= BctPubkEnd)) {
|
||||
R_RETURN(Base::Write(offset, buffer, size));
|
||||
}
|
||||
|
||||
/* Handle any data written past the end of the pubk region. */
|
||||
if (offset + size > BctEndOffset) {
|
||||
const u64 diff = BctEndOffset - offset;
|
||||
R_TRY(Base::Write(BctEndOffset, buffer + diff, size - diff));
|
||||
size = diff;
|
||||
}
|
||||
|
||||
/* Read in the current BCT region. */
|
||||
R_TRY(Base::Read(0, g_boot0_bct_buffer, BctEndOffset));
|
||||
|
||||
/* Update the bct buffer. */
|
||||
for (u64 cur_offset = offset; cur_offset < BctEndOffset && cur_offset < offset + size; cur_offset++) {
|
||||
const u64 cur_bct_relative_ofs = cur_offset % BctSize;
|
||||
if (cur_bct_relative_ofs < BctPubkStart || BctPubkEnd <= cur_bct_relative_ofs) {
|
||||
g_boot0_bct_buffer[cur_offset] = buffer[cur_offset - offset];
|
||||
}
|
||||
}
|
||||
|
||||
R_RETURN(Base::Write(0, g_boot0_bct_buffer, BctEndOffset));
|
||||
}
|
||||
|
||||
CustomPublicKeyBoot0Storage::CustomPublicKeyBoot0Storage(FsStorage &s, const sm::MitmProcessInfo &c, spl::SocType soc) : Base(s), m_client_info(c), m_soc_type(soc) {
|
||||
std::scoped_lock lk{g_boot0_access_mutex};
|
||||
|
||||
/* We're custom public key. */
|
||||
g_custom_public_key = true;
|
||||
}
|
||||
|
||||
Result CustomPublicKeyBoot0Storage::Read(s64 offset, void *_buffer, size_t size) {
|
||||
std::scoped_lock lk{g_boot0_access_mutex};
|
||||
AMS_ABORT_UNLESS(g_custom_public_key);
|
||||
|
||||
/* Check if we have nothing to do. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
u8 *buffer = static_cast<u8 *>(_buffer);
|
||||
|
||||
/* Check if we're reading the first BCTs for NS. */
|
||||
/* If we are, we want to lie about the contents of BCT0/1 so that they validate. */
|
||||
if (offset < 0x8000 && m_client_info.program_id == ncm::SystemProgramId::Ns) {
|
||||
R_TRY(Base::Read(0, g_boot0_bct_buffer, Boot0Storage::BctEndOffset));
|
||||
|
||||
/* Determine the readable size. */
|
||||
const size_t readable_bct01_size = std::min<size_t>(0x8000, offset + size) - offset;
|
||||
std::memcpy(buffer, g_boot0_bct_buffer + 0x8000 + offset, readable_bct01_size);
|
||||
|
||||
/* Advance. */
|
||||
buffer += readable_bct01_size;
|
||||
offset += readable_bct01_size;
|
||||
size -= readable_bct01_size;
|
||||
}
|
||||
|
||||
/* Check if we have nothing to do. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Perform whatever remains of the read. */
|
||||
R_RETURN(Base::Read(offset, buffer, size));
|
||||
}
|
||||
|
||||
Result CustomPublicKeyBoot0Storage::Write(s64 offset, const void *_buffer, size_t size) {
|
||||
std::scoped_lock lk{g_boot0_access_mutex};
|
||||
AMS_ABORT_UNLESS(g_custom_public_key);
|
||||
|
||||
const u8 *buffer = static_cast<const u8 *>(_buffer);
|
||||
|
||||
/* Check if we have nothing to do. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Drop writes to the first BCTs. */
|
||||
if (offset < 0x8000) {
|
||||
/* Determine the writable size. */
|
||||
const size_t writable_bct01_size = std::min<size_t>(0x8000, offset + size) - offset;
|
||||
|
||||
/* Advance. */
|
||||
buffer += writable_bct01_size;
|
||||
offset += writable_bct01_size;
|
||||
size -= writable_bct01_size;
|
||||
}
|
||||
|
||||
/* Check if we have nothing to do. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Similarly, we want to drop writes to the end of boot0, where custom bootloader lives. */
|
||||
R_SUCCEED_IF(offset >= 0x380000);
|
||||
if (offset + size >= 0x380000) {
|
||||
size = 0x380000 - offset;
|
||||
}
|
||||
|
||||
/* On erista, we want to protect the EKS region. */
|
||||
if (m_soc_type == spl::SocType_Erista) {
|
||||
if (offset <= Boot0Storage::EksStart) {
|
||||
if (offset + size < Boot0Storage::EksStart) {
|
||||
/* Fall through, no need to do anything here. */
|
||||
} else {
|
||||
if (offset + size > Boot0Storage::EksEnd) {
|
||||
/* Perform portion of write falling past end of keyblobs. */
|
||||
const s64 diff = Boot0Storage::EksEnd - offset;
|
||||
R_TRY(Base::Write(Boot0Storage::EksEnd, buffer + diff, size - diff));
|
||||
}
|
||||
/* Adjust size to avoid writing end of data. */
|
||||
size = Boot0Storage::EksStart - offset;
|
||||
}
|
||||
} else if (offset < Boot0Storage::EksEnd) {
|
||||
/* Ignore writes falling strictly within the region. */
|
||||
R_SUCCEED_IF(offset + size <= Boot0Storage::EksEnd);
|
||||
|
||||
/* Only write past the end of the region. */
|
||||
const s64 diff = Boot0Storage::EksEnd - offset;
|
||||
buffer += diff;
|
||||
size -= diff;
|
||||
offset = Boot0Storage::EksEnd;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we have nothing to write, succeed immediately. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Perform whatever remains of the write. */
|
||||
R_RETURN(Base::Write(offset, buffer, size));
|
||||
}
|
||||
|
||||
bool DetectBoot0CustomPublicKey(::FsStorage &storage) {
|
||||
/* Determine public key offset. */
|
||||
const size_t bct_pubk_offset = spl::GetSocType() == spl::SocType_Mariko ? 0x10 : 0x210;
|
||||
|
||||
u8 work_buffer[0x400];
|
||||
|
||||
/* Read BCT-Normal-Main. */
|
||||
R_ABORT_UNLESS(::fsStorageRead(std::addressof(storage), 0, work_buffer, sizeof(work_buffer)));
|
||||
|
||||
/* Check for custom public key. */
|
||||
if (std::memcmp(work_buffer + bct_pubk_offset, CustomPublicKey, sizeof(CustomPublicKey)) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Read BCT-Safe-Main. */
|
||||
R_ABORT_UNLESS(::fsStorageRead(std::addressof(storage), 0x4000, work_buffer, sizeof(work_buffer)));
|
||||
|
||||
/* Check for custom public key. */
|
||||
if (std::memcmp(work_buffer + bct_pubk_offset, CustomPublicKey, sizeof(CustomPublicKey)) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
template<class Base, size_t SectorSize>
|
||||
class SectoredStorageAdapter : public Base {
|
||||
static_assert(std::is_base_of<ams::fs::IStorage, Base>::value);
|
||||
private:
|
||||
u8 m_sector_buf[SectorSize];
|
||||
public:
|
||||
/* Inherit constructors. */
|
||||
using Base::Base;
|
||||
public:
|
||||
virtual Result Read(s64 offset, void *_buffer, size_t size) override {
|
||||
u8 *buffer = static_cast<u8 *>(_buffer);
|
||||
|
||||
const s64 seek = util::AlignDown(offset, SectorSize);
|
||||
const s64 sector_ofs = offset - seek;
|
||||
|
||||
/* Check if we have nothing to do. */
|
||||
if (size == 0) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Fast case. */
|
||||
if (sector_ofs == 0 && util::IsAligned(size, SectorSize)) {
|
||||
R_RETURN(Base::Read(offset, buffer, size));
|
||||
}
|
||||
|
||||
R_TRY(Base::Read(seek, m_sector_buf, SectorSize));
|
||||
|
||||
if (size + sector_ofs <= SectorSize) {
|
||||
/* Staying within the sector. */
|
||||
std::memcpy(buffer, m_sector_buf + sector_ofs, size);
|
||||
} else {
|
||||
/* Leaving the sector. */
|
||||
const size_t size_in_sector = SectorSize - sector_ofs;
|
||||
std::memcpy(buffer, m_sector_buf + sector_ofs, size_in_sector);
|
||||
size -= size_in_sector;
|
||||
|
||||
/* Read as many guaranteed aligned sectors as we can. */
|
||||
const size_t aligned_remaining_size = util::AlignDown(size, SectorSize);
|
||||
if (aligned_remaining_size) {
|
||||
R_TRY(Base::Read(offset + size_in_sector, buffer + size_in_sector, aligned_remaining_size));
|
||||
size -= aligned_remaining_size;
|
||||
}
|
||||
|
||||
/* Read any leftover data. */
|
||||
if (size) {
|
||||
R_TRY(Base::Read(offset + size_in_sector + aligned_remaining_size, m_sector_buf, SectorSize));
|
||||
std::memcpy(buffer + size_in_sector + aligned_remaining_size, m_sector_buf, size);
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *_buffer, size_t size) override {
|
||||
const u8 *buffer = static_cast<const u8 *>(_buffer);
|
||||
|
||||
const s64 seek = util::AlignDown(offset, SectorSize);
|
||||
const s64 sector_ofs = offset - seek;
|
||||
|
||||
/* Check if we have nothing to do. */
|
||||
if (size == 0) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Fast case. */
|
||||
if (sector_ofs == 0 && util::IsAligned(size, SectorSize)) {
|
||||
R_RETURN(Base::Write(offset, buffer, size));
|
||||
}
|
||||
|
||||
/* Load existing sector data. */
|
||||
R_TRY(Base::Read(seek, m_sector_buf, SectorSize));
|
||||
|
||||
if (size + sector_ofs <= SectorSize) {
|
||||
/* Staying within the sector. */
|
||||
std::memcpy(m_sector_buf + sector_ofs, buffer, size);
|
||||
R_TRY(Base::Write(seek, m_sector_buf, SectorSize));
|
||||
} else {
|
||||
/* Leaving the sector. */
|
||||
const size_t size_in_sector = SectorSize - sector_ofs;
|
||||
std::memcpy(m_sector_buf + sector_ofs, buffer, size_in_sector);
|
||||
R_TRY(Base::Write(seek, m_sector_buf, SectorSize));
|
||||
size -= size_in_sector;
|
||||
|
||||
/* Write as many guaranteed aligned sectors as we can. */
|
||||
const size_t aligned_remaining_size = util::AlignDown(size, SectorSize);
|
||||
if (aligned_remaining_size) {
|
||||
R_TRY(Base::Write(offset + size_in_sector, buffer + size_in_sector, aligned_remaining_size));
|
||||
size -= aligned_remaining_size;
|
||||
}
|
||||
|
||||
/* Write any leftover data. */
|
||||
if (size) {
|
||||
R_TRY(Base::Read(offset + size_in_sector + aligned_remaining_size, m_sector_buf, SectorSize));
|
||||
std::memcpy(m_sector_buf, buffer + size_in_sector + aligned_remaining_size, size);
|
||||
R_TRY(Base::Write(offset + size_in_sector + aligned_remaining_size, m_sector_buf, SectorSize));
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
/* Represents an RCM-preserving BOOT0 partition. */
|
||||
class Boot0Storage : public SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200> {
|
||||
public:
|
||||
using Base = SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200>;
|
||||
|
||||
static constexpr s64 BctEndOffset = 0xFC000;
|
||||
static constexpr s64 BctSize = static_cast<s64>(ams::updater::BctSize);
|
||||
static constexpr s64 BctPubkStart = 0x210;
|
||||
static constexpr s64 BctPubkSize = 0x100;
|
||||
static constexpr s64 BctPubkEnd = BctPubkStart + BctPubkSize;
|
||||
|
||||
static constexpr s64 EksStart = 0x180000;
|
||||
static constexpr s64 EksSize = static_cast<s64>(ams::updater::EksSize);
|
||||
static constexpr s64 EksEnd = EksStart + EksSize;
|
||||
private:
|
||||
sm::MitmProcessInfo m_client_info;
|
||||
private:
|
||||
bool CanModifyBctPublicKey();
|
||||
public:
|
||||
Boot0Storage(FsStorage &s, const sm::MitmProcessInfo &c) : Base(s), m_client_info(c) { /* ... */ }
|
||||
public:
|
||||
virtual Result Read(s64 offset, void *_buffer, size_t size) override;
|
||||
virtual Result Write(s64 offset, const void *_buffer, size_t size) override;
|
||||
};
|
||||
|
||||
class CustomPublicKeyBoot0Storage : public SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200> {
|
||||
public:
|
||||
using Base = SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200>;
|
||||
private:
|
||||
sm::MitmProcessInfo m_client_info;
|
||||
spl::SocType m_soc_type;
|
||||
public:
|
||||
CustomPublicKeyBoot0Storage(FsStorage &s, const sm::MitmProcessInfo &c, spl::SocType soc);
|
||||
public:
|
||||
virtual Result Read(s64 offset, void *_buffer, size_t size) override;
|
||||
virtual Result Write(s64 offset, const void *_buffer, size_t size) override;
|
||||
};
|
||||
|
||||
bool DetectBoot0CustomPublicKey(::FsStorage &storage);
|
||||
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsmitm_calibration_binary_storage.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
using namespace ams::fs;
|
||||
|
||||
namespace {
|
||||
|
||||
constinit os::SdkMutex g_cal0_access_mutex;
|
||||
|
||||
}
|
||||
|
||||
Result CalibrationBinaryStorage::Read(s64 offset, void *_buffer, size_t size) {
|
||||
/* Acquire exclusive calibration binary access. */
|
||||
std::scoped_lock lk(g_cal0_access_mutex);
|
||||
|
||||
/* Get u8 buffer. */
|
||||
u8 *buffer = static_cast<u8 *>(_buffer);
|
||||
|
||||
/* Succeed on zero-size. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Handle the blank region. */
|
||||
if (m_read_blank) {
|
||||
if (BlankStartOffset <= offset && offset < BlankEndOffset) {
|
||||
const size_t blank_size = std::min(size, static_cast<size_t>(BlankEndOffset - offset));
|
||||
mitm::ReadFromBlankCalibrationBinary(offset, buffer, blank_size);
|
||||
size -= blank_size;
|
||||
buffer += blank_size;
|
||||
offset += blank_size;
|
||||
}
|
||||
}
|
||||
|
||||
/* Succeed if we're done. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Handle any in-between data. */
|
||||
if (BlankEndOffset <= offset && offset < FakeSecureStartOffset) {
|
||||
const size_t mid_size = std::min(size, static_cast<size_t>(FakeSecureStartOffset - offset));
|
||||
R_TRY(Base::Read(offset, buffer, mid_size));
|
||||
size -= mid_size;
|
||||
buffer += mid_size;
|
||||
offset += mid_size;
|
||||
}
|
||||
|
||||
/* Succeed if we're done. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Handle the secure region. */
|
||||
if (FakeSecureStartOffset <= offset && offset < FakeSecureEndOffset) {
|
||||
const size_t fake_size = std::min(size, static_cast<size_t>(FakeSecureEndOffset - offset));
|
||||
mitm::ReadFromFakeSecureBackupStorage(offset, buffer, fake_size);
|
||||
size -= fake_size;
|
||||
buffer += fake_size;
|
||||
offset += fake_size;
|
||||
}
|
||||
|
||||
/* Succeed if we're done. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Handle any remaining data. */
|
||||
R_RETURN(Base::Read(offset, buffer, size));
|
||||
}
|
||||
|
||||
Result CalibrationBinaryStorage::Write(s64 offset, const void *_buffer, size_t size) {
|
||||
/* Acquire exclusive calibration binary access. */
|
||||
std::scoped_lock lk(g_cal0_access_mutex);
|
||||
|
||||
/* Get const u8 buffer. */
|
||||
const u8 *buffer = static_cast<const u8 *>(_buffer);
|
||||
|
||||
/* Succeed on zero-size. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Only allow writes if we should. */
|
||||
R_UNLESS(m_allow_writes, fs::ResultUnsupportedOperation());
|
||||
|
||||
/* Handle the blank region. */
|
||||
if (m_read_blank) {
|
||||
if (BlankStartOffset <= offset && offset < BlankEndOffset) {
|
||||
const size_t blank_size = std::min(size, static_cast<size_t>(BlankEndOffset - offset));
|
||||
mitm::WriteToBlankCalibrationBinary(offset, buffer, blank_size);
|
||||
size -= blank_size;
|
||||
buffer += blank_size;
|
||||
offset += blank_size;
|
||||
}
|
||||
}
|
||||
|
||||
/* Succeed if we're done. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Handle any in-between data. */
|
||||
if (BlankEndOffset <= offset && offset < FakeSecureStartOffset) {
|
||||
const size_t mid_size = std::min(size, static_cast<size_t>(FakeSecureStartOffset - offset));
|
||||
R_TRY(Base::Write(offset, buffer, mid_size));
|
||||
size -= mid_size;
|
||||
buffer += mid_size;
|
||||
offset += mid_size;
|
||||
}
|
||||
|
||||
/* Succeed if we're done. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Handle the secure region. */
|
||||
if (FakeSecureStartOffset <= offset && offset < FakeSecureEndOffset) {
|
||||
const size_t fake_size = std::min(size, static_cast<size_t>(FakeSecureEndOffset - offset));
|
||||
mitm::WriteToFakeSecureBackupStorage(offset, buffer, fake_size);
|
||||
size -= fake_size;
|
||||
buffer += fake_size;
|
||||
offset += fake_size;
|
||||
}
|
||||
|
||||
/* Succeed if we're done. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Handle any remaining data. */
|
||||
R_RETURN(Base::Write(offset, buffer, size));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsmitm_boot0storage.hpp"
|
||||
#include "../amsmitm_prodinfo_utils.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
/* Represents a protected calibration binary partition. */
|
||||
class CalibrationBinaryStorage : public SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200> {
|
||||
public:
|
||||
using Base = SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200>;
|
||||
|
||||
static constexpr s64 BlankStartOffset = 0x0;
|
||||
static constexpr s64 BlankSize = static_cast<s64>(CalibrationBinarySize);
|
||||
static constexpr s64 BlankEndOffset = BlankStartOffset + BlankSize;
|
||||
|
||||
static constexpr s64 FakeSecureStartOffset = SecureCalibrationInfoBackupOffset;
|
||||
static constexpr s64 FakeSecureSize = static_cast<s64>(SecureCalibrationBinaryBackupSize);
|
||||
static constexpr s64 FakeSecureEndOffset = FakeSecureStartOffset + FakeSecureSize;
|
||||
private:
|
||||
sm::MitmProcessInfo m_client_info;
|
||||
bool m_read_blank;
|
||||
bool m_allow_writes;
|
||||
public:
|
||||
CalibrationBinaryStorage(FsStorage &s, const sm::MitmProcessInfo &c)
|
||||
: Base(s), m_client_info(c),
|
||||
m_read_blank(mitm::ShouldReadBlankCalibrationBinary()),
|
||||
m_allow_writes(mitm::IsWriteToCalibrationBinaryAllowed())
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
public:
|
||||
virtual Result Read(s64 offset, void *_buffer, size_t size) override;
|
||||
virtual Result Write(s64 offset, const void *_buffer, size_t size) override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,432 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "../amsmitm_fs_utils.hpp"
|
||||
#include "fsmitm_layered_romfs_storage.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
namespace {
|
||||
|
||||
constinit os::SdkMutex g_mq_lock;
|
||||
constinit bool g_started_req_thread;
|
||||
constinit uintptr_t g_mq_storage[2];
|
||||
os::MessageQueue g_req_mq(g_mq_storage + 0, 1);
|
||||
os::MessageQueue g_ack_mq(g_mq_storage + 1, 1);
|
||||
|
||||
class LayeredRomfsStorageHolder : public util::IntrusiveRedBlackTreeBaseNode<LayeredRomfsStorageHolder> {
|
||||
public:
|
||||
using RedBlackKeyType = u64;
|
||||
private:
|
||||
LayeredRomfsStorageImpl *m_impl;
|
||||
u32 m_reference_count;
|
||||
bool m_second_chance;
|
||||
bool m_process_romfs;
|
||||
public:
|
||||
LayeredRomfsStorageHolder(LayeredRomfsStorageImpl *impl, bool process_rom) : m_impl(impl), m_reference_count(1), m_second_chance(true), m_process_romfs(process_rom) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
~LayeredRomfsStorageHolder() {
|
||||
delete m_impl;
|
||||
}
|
||||
|
||||
constexpr LayeredRomfsStorageImpl *GetImpl() const { return m_impl; }
|
||||
constexpr ncm::ProgramId GetProgramId() const { return m_impl->GetProgramId(); }
|
||||
constexpr u32 GetReferenceCount() const { return m_reference_count; }
|
||||
|
||||
void OpenReferenceImpl() { ++m_reference_count; }
|
||||
void CloseReferenceImpl() { --m_reference_count; }
|
||||
|
||||
bool GetSecondChanceImpl() const { return m_second_chance; }
|
||||
void SetSecondChanceImpl(bool sc) { m_second_chance = sc; }
|
||||
|
||||
bool IsProcessRomfs() const { return m_process_romfs; }
|
||||
|
||||
static constexpr ALWAYS_INLINE int Compare(const RedBlackKeyType &lval, const LayeredRomfsStorageHolder &rhs) {
|
||||
const auto rval = rhs.GetProgramId().value;
|
||||
|
||||
if (lval < rval) {
|
||||
return -1;
|
||||
} else if (lval == rval) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE int Compare(const LayeredRomfsStorageHolder &lhs, const LayeredRomfsStorageHolder &rhs) {
|
||||
return Compare(lhs.GetProgramId().value, rhs);
|
||||
}
|
||||
};
|
||||
|
||||
using LayeredRomfsStorageSet = typename util::IntrusiveRedBlackTreeBaseTraits<LayeredRomfsStorageHolder>::TreeType<LayeredRomfsStorageHolder>;
|
||||
|
||||
constinit os::SdkRecursiveMutex g_storage_set_mutex;
|
||||
constinit LayeredRomfsStorageSet g_storage_set;
|
||||
constinit os::SdkMutex g_initialization_mutex;
|
||||
|
||||
void OpenReference(LayeredRomfsStorageImpl *impl) {
|
||||
std::scoped_lock lk(g_storage_set_mutex);
|
||||
|
||||
auto it = g_storage_set.find_key(impl->GetProgramId().value);
|
||||
AMS_ABORT_UNLESS(it != g_storage_set.end());
|
||||
|
||||
it->OpenReferenceImpl();
|
||||
}
|
||||
|
||||
void CloseReference(LayeredRomfsStorageImpl *impl) {
|
||||
std::scoped_lock lk(g_storage_set_mutex);
|
||||
|
||||
auto it = g_storage_set.find_key(impl->GetProgramId().value);
|
||||
AMS_ABORT_UNLESS(it != g_storage_set.end());
|
||||
|
||||
AMS_ABORT_UNLESS(it->GetReferenceCount() > 0);
|
||||
it->CloseReferenceImpl();
|
||||
}
|
||||
|
||||
void RomfsInitializerThreadFunction(void *) {
|
||||
while (true) {
|
||||
uintptr_t storage_uptr = 0;
|
||||
|
||||
g_req_mq.Receive(std::addressof(storage_uptr));
|
||||
auto *impl = reinterpret_cast<LayeredRomfsStorageImpl *>(storage_uptr);
|
||||
g_ack_mq.Send(storage_uptr);
|
||||
|
||||
std::scoped_lock lk(g_initialization_mutex);
|
||||
|
||||
impl->InitializeImpl();
|
||||
|
||||
/* Close the initial reference. */
|
||||
CloseReference(impl);
|
||||
}
|
||||
}
|
||||
|
||||
void RomfsFinalizerThreadFunction(void *) {
|
||||
while (true) {
|
||||
{
|
||||
std::scoped_lock lk(g_storage_set_mutex);
|
||||
|
||||
auto it = g_storage_set.begin();
|
||||
while (it != g_storage_set.end()) {
|
||||
if (it->GetReferenceCount() > 0) {
|
||||
it->SetSecondChanceImpl(true);
|
||||
++it;
|
||||
} else if (it->GetSecondChanceImpl()) {
|
||||
it->SetSecondChanceImpl(false);
|
||||
++it;
|
||||
} else {
|
||||
auto *holder = std::addressof(*it);
|
||||
it = g_storage_set.erase(it);
|
||||
delete holder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
os::SleepThread(TimeSpan::FromMilliSeconds(500));
|
||||
}
|
||||
}
|
||||
|
||||
constexpr size_t RomfsInitializerThreadStackSize = 0x8000;
|
||||
os::ThreadType g_romfs_initializer_thread;
|
||||
os::ThreadType g_romfs_finalizer_thread;
|
||||
alignas(os::ThreadStackAlignment) u8 g_romfs_initializer_thread_stack[RomfsInitializerThreadStackSize];
|
||||
alignas(os::ThreadStackAlignment) u8 g_romfs_finalizer_thread_stack[os::MemoryPageSize];
|
||||
|
||||
void RequestInitializeStorage(uintptr_t storage_uptr) {
|
||||
std::scoped_lock lk(g_mq_lock);
|
||||
|
||||
if (AMS_UNLIKELY(!g_started_req_thread)) {
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_romfs_initializer_thread), RomfsInitializerThreadFunction, nullptr, g_romfs_initializer_thread_stack, sizeof(g_romfs_initializer_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(mitm_fs, RomFileSystemInitializeThread)));
|
||||
os::SetThreadNamePointer(std::addressof(g_romfs_initializer_thread), AMS_GET_SYSTEM_THREAD_NAME(mitm_fs, RomFileSystemInitializeThread));
|
||||
os::StartThread(std::addressof(g_romfs_initializer_thread));
|
||||
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_romfs_finalizer_thread), RomfsFinalizerThreadFunction, nullptr, g_romfs_finalizer_thread_stack, sizeof(g_romfs_finalizer_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(mitm_fs, RomFileSystemInitializeThread)));
|
||||
os::SetThreadNamePointer(std::addressof(g_romfs_finalizer_thread), AMS_GET_SYSTEM_THREAD_NAME(mitm_fs, RomFileSystemFinalizeThread));
|
||||
os::StartThread(std::addressof(g_romfs_finalizer_thread));
|
||||
|
||||
g_started_req_thread = true;
|
||||
}
|
||||
|
||||
g_req_mq.Send(storage_uptr);
|
||||
uintptr_t ack = 0;
|
||||
g_ack_mq.Receive(std::addressof(ack));
|
||||
AMS_ABORT_UNLESS(ack == storage_uptr);
|
||||
}
|
||||
|
||||
class LayeredRomfsStorage : public ams::fs::IStorage {
|
||||
private:
|
||||
LayeredRomfsStorageImpl *m_impl;
|
||||
public:
|
||||
LayeredRomfsStorage(LayeredRomfsStorageImpl *impl) : m_impl(impl) {
|
||||
OpenReference(m_impl);
|
||||
}
|
||||
|
||||
virtual ~LayeredRomfsStorage() {
|
||||
CloseReference(m_impl);
|
||||
}
|
||||
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
R_RETURN(m_impl->Read(offset, buffer, size));
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out_size) override {
|
||||
R_RETURN(m_impl->GetSize(out_size));
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
R_RETURN(m_impl->Flush());
|
||||
}
|
||||
|
||||
virtual Result OperateRange(void *dst, size_t dst_size, ams::fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
|
||||
R_RETURN(m_impl->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
/* TODO: Better result code? */
|
||||
AMS_UNUSED(offset, buffer, size);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
/* TODO: Better result code? */
|
||||
AMS_UNUSED(size);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using namespace ams::fs;
|
||||
|
||||
std::shared_ptr<ams::fs::IStorage> GetLayeredRomfsStorage(ncm::ProgramId program_id, ::FsStorage &data_storage, bool is_process_romfs) {
|
||||
/*Prepare to find or create a new storage. */
|
||||
LayeredRomfsStorageImpl *impl = nullptr;
|
||||
{
|
||||
std::scoped_lock lk(g_storage_set_mutex);
|
||||
|
||||
/* Find an existing storage. */
|
||||
if (auto it = g_storage_set.find_key(program_id.value); it != g_storage_set.end()) {
|
||||
return std::make_shared<LayeredRomfsStorage>(it->GetImpl());
|
||||
}
|
||||
|
||||
/* We don't have an existing storage. If we're creating process romfs, free any unreferenced process romfs. */
|
||||
/* This should help prevent too much memory in use at any time. */
|
||||
if (is_process_romfs) {
|
||||
auto it = g_storage_set.begin();
|
||||
while (it != g_storage_set.end()) {
|
||||
if (it->GetReferenceCount() > 0 || !it->IsProcessRomfs()) {
|
||||
++it;
|
||||
} else {
|
||||
auto *holder = std::addressof(*it);
|
||||
it = g_storage_set.erase(it);
|
||||
delete holder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a new storage. */
|
||||
{
|
||||
::FsFile data_file;
|
||||
if (R_SUCCEEDED(OpenAtmosphereSdFile(std::addressof(data_file), program_id, "romfs.bin", OpenMode_Read))) {
|
||||
impl = new LayeredRomfsStorageImpl(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), std::make_unique<ReadOnlyStorageAdapter>(new FileStorage(new RemoteFile(data_file))), program_id);
|
||||
} else {
|
||||
impl = new LayeredRomfsStorageImpl(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), nullptr, program_id);
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert holder. Reference count will now be one. */
|
||||
g_storage_set.insert(*(new LayeredRomfsStorageHolder(impl, is_process_romfs)));
|
||||
}
|
||||
|
||||
/* Begin initialization. When this finishes, a decref will occur. */
|
||||
AMS_ABORT_UNLESS(impl != nullptr);
|
||||
impl->BeginInitialize();
|
||||
|
||||
/* Return a new shared storage for the impl. */
|
||||
return std::make_shared<LayeredRomfsStorage>(impl);
|
||||
}
|
||||
|
||||
void FinalizeLayeredRomfsStorage(ncm::ProgramId program_id) {
|
||||
std::scoped_lock lk(g_initialization_mutex);
|
||||
std::scoped_lock lk2(g_storage_set_mutex);
|
||||
|
||||
/* Find an existing storage. */
|
||||
if (auto it = g_storage_set.find_key(program_id.value); it != g_storage_set.end()) {
|
||||
/* We need to delete the process romfs. Require invariant that it is unreferenced, by this point. */
|
||||
AMS_ABORT_UNLESS(it->GetReferenceCount() == 0);
|
||||
|
||||
auto *holder = std::addressof(*it);
|
||||
it = g_storage_set.erase(it);
|
||||
delete holder;
|
||||
}
|
||||
}
|
||||
|
||||
LayeredRomfsStorageImpl::LayeredRomfsStorageImpl(std::unique_ptr<IStorage> s_r, std::unique_ptr<IStorage> f_r, ncm::ProgramId pr_id) : m_storage_romfs(std::move(s_r)), m_file_romfs(std::move(f_r)), m_initialize_event(os::EventClearMode_ManualClear), m_program_id(std::move(pr_id)), m_is_initialized(false), m_started_initialize(false) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
LayeredRomfsStorageImpl::~LayeredRomfsStorageImpl() {
|
||||
for (size_t i = 0; i < m_source_infos.size(); i++) {
|
||||
m_source_infos[i].Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
void LayeredRomfsStorageImpl::BeginInitialize() {
|
||||
AMS_ABORT_UNLESS(!m_started_initialize);
|
||||
RequestInitializeStorage(reinterpret_cast<uintptr_t>(this));
|
||||
m_started_initialize = true;
|
||||
}
|
||||
|
||||
void LayeredRomfsStorageImpl::InitializeImpl() {
|
||||
/* Build new virtual romfs. */
|
||||
romfs::Builder builder(m_program_id);
|
||||
|
||||
if (mitm::IsInitialized()) {
|
||||
builder.AddSdFiles();
|
||||
}
|
||||
if (m_file_romfs) {
|
||||
builder.AddStorageFiles(m_file_romfs.get(), romfs::DataSourceType::File);
|
||||
}
|
||||
if (m_storage_romfs) {
|
||||
builder.AddStorageFiles(m_storage_romfs.get(), romfs::DataSourceType::Storage);
|
||||
}
|
||||
|
||||
builder.Build(std::addressof(m_source_infos));
|
||||
|
||||
m_is_initialized = true;
|
||||
m_initialize_event.Signal();
|
||||
}
|
||||
|
||||
Result LayeredRomfsStorageImpl::Read(s64 offset, void *buffer, size_t size) {
|
||||
/* Check if we can succeed immediately. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Ensure we're initialized. */
|
||||
if (!m_is_initialized) {
|
||||
m_initialize_event.Wait();
|
||||
}
|
||||
|
||||
/* Validate offset/size. */
|
||||
const s64 virt_size = this->GetSize();
|
||||
R_UNLESS(offset >= 0, fs::ResultInvalidOffset());
|
||||
R_UNLESS(offset < virt_size, fs::ResultInvalidOffset());
|
||||
if (static_cast<size_t>(virt_size - offset) < size) {
|
||||
size = static_cast<size_t>(virt_size - offset);
|
||||
}
|
||||
|
||||
/* Find first source info via binary search. */
|
||||
auto it = std::lower_bound(m_source_infos.begin(), m_source_infos.end(), offset);
|
||||
u8 *cur_dst = static_cast<u8 *>(buffer);
|
||||
|
||||
/* Our operator < compares against start of info instead of end, so we need to subtract one from lower bound. */
|
||||
it--;
|
||||
|
||||
size_t read_so_far = 0;
|
||||
while (read_so_far < size) {
|
||||
const auto &cur_source = *it;
|
||||
AMS_ABORT_UNLESS(offset >= cur_source.virtual_offset);
|
||||
|
||||
if (offset < cur_source.virtual_offset + cur_source.size) {
|
||||
const s64 offset_within_source = offset - cur_source.virtual_offset;
|
||||
const size_t cur_read_size = std::min(size - read_so_far, static_cast<size_t>(cur_source.size - offset_within_source));
|
||||
switch (cur_source.source_type) {
|
||||
case romfs::DataSourceType::Storage:
|
||||
R_ABORT_UNLESS(m_storage_romfs->Read(cur_source.storage_source_info.offset + offset_within_source, cur_dst, cur_read_size));
|
||||
break;
|
||||
case romfs::DataSourceType::File:
|
||||
R_ABORT_UNLESS(m_file_romfs->Read(cur_source.file_source_info.offset + offset_within_source, cur_dst, cur_read_size));
|
||||
break;
|
||||
case romfs::DataSourceType::LooseSdFile:
|
||||
{
|
||||
FsFile file;
|
||||
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdRomfsFile(std::addressof(file), m_program_id, cur_source.loose_source_info.path, OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fsFileClose(std::addressof(file)); };
|
||||
|
||||
u64 out_read = 0;
|
||||
R_ABORT_UNLESS(fsFileRead(std::addressof(file), offset_within_source, cur_dst, cur_read_size, FsReadOption_None, std::addressof(out_read)));
|
||||
AMS_ABORT_UNLESS(out_read == cur_read_size);
|
||||
}
|
||||
break;
|
||||
case romfs::DataSourceType::Memory:
|
||||
std::memcpy(cur_dst, cur_source.memory_source_info.data + offset_within_source, cur_read_size);
|
||||
break;
|
||||
case romfs::DataSourceType::Metadata:
|
||||
{
|
||||
size_t out_read = 0;
|
||||
R_ABORT_UNLESS(cur_source.metadata_source_info.file->Read(std::addressof(out_read), offset_within_source, cur_dst, cur_read_size));
|
||||
AMS_ABORT_UNLESS(out_read == cur_read_size);
|
||||
}
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
read_so_far += cur_read_size;
|
||||
cur_dst += cur_read_size;
|
||||
offset += cur_read_size;
|
||||
} else {
|
||||
/* Explicitly handle padding. */
|
||||
const auto &next_source = *(++it);
|
||||
const size_t padding_size = static_cast<size_t>(next_source.virtual_offset - offset);
|
||||
|
||||
std::memset(cur_dst, 0, padding_size);
|
||||
read_so_far += padding_size;
|
||||
cur_dst += padding_size;
|
||||
offset += padding_size;
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LayeredRomfsStorageImpl::GetSize(s64 *out_size) {
|
||||
/* Ensure we're initialized. */
|
||||
if (!m_is_initialized) {
|
||||
m_initialize_event.Wait();
|
||||
}
|
||||
|
||||
*out_size = this->GetSize();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LayeredRomfsStorageImpl::Flush() {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LayeredRomfsStorageImpl::OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
|
||||
AMS_UNUSED(offset, src, src_size);
|
||||
|
||||
switch (op_id) {
|
||||
case OperationId::Invalidate:
|
||||
case OperationId::QueryRange:
|
||||
if (size == 0) {
|
||||
if (op_id == OperationId::QueryRange) {
|
||||
R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
|
||||
R_UNLESS(dst_size == sizeof(QueryRangeInfo), fs::ResultInvalidSize());
|
||||
reinterpret_cast<QueryRangeInfo *>(dst)->Clear();
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
/* TODO: How to deal with this? */
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
default:
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsmitm_romfs.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
class LayeredRomfsStorageImpl {
|
||||
private:
|
||||
romfs::Builder::SourceInfoVector m_source_infos;
|
||||
std::unique_ptr<ams::fs::IStorage> m_storage_romfs;
|
||||
std::unique_ptr<ams::fs::IStorage> m_file_romfs;
|
||||
os::Event m_initialize_event;
|
||||
ncm::ProgramId m_program_id;
|
||||
bool m_is_initialized;
|
||||
bool m_started_initialize;
|
||||
protected:
|
||||
inline s64 GetSize() const {
|
||||
const auto &back = m_source_infos.back();
|
||||
return back.virtual_offset + back.size;
|
||||
}
|
||||
public:
|
||||
LayeredRomfsStorageImpl(std::unique_ptr<ams::fs::IStorage> s_r, std::unique_ptr<ams::fs::IStorage> f_r, ncm::ProgramId pr_id);
|
||||
~LayeredRomfsStorageImpl();
|
||||
|
||||
void BeginInitialize();
|
||||
void InitializeImpl();
|
||||
|
||||
constexpr ncm::ProgramId GetProgramId() const { return m_program_id; }
|
||||
|
||||
Result Read(s64 offset, void *buffer, size_t size);
|
||||
Result GetSize(s64 *out_size);
|
||||
Result Flush();
|
||||
Result OperateRange(void *dst, size_t dst_size, ams::fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size);
|
||||
};
|
||||
|
||||
std::shared_ptr<ams::fs::IStorage> GetLayeredRomfsStorage(ncm::ProgramId program_id, ::FsStorage &data_storage, bool is_process_romfs);
|
||||
|
||||
void FinalizeLayeredRomfsStorage(ncm::ProgramId program_id);
|
||||
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsmitm_module.hpp"
|
||||
#include "fs_mitm_service.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
namespace {
|
||||
|
||||
enum PortIndex {
|
||||
PortIndex_Mitm,
|
||||
PortIndex_Count,
|
||||
};
|
||||
|
||||
constexpr sm::ServiceName MitmServiceName = sm::ServiceName::Encode("fsp-srv");
|
||||
|
||||
struct ServerOptions {
|
||||
static constexpr size_t PointerBufferSize = 0x800;
|
||||
static constexpr size_t MaxDomains = 0x40;
|
||||
static constexpr size_t MaxDomainObjects = 0x4000;
|
||||
static constexpr bool CanDeferInvokeRequest = false;
|
||||
static constexpr bool CanManageMitmServers = true;
|
||||
};
|
||||
|
||||
constexpr size_t MaxSessions = 61;
|
||||
|
||||
class ServerManager final : public sf::hipc::ServerManager<PortIndex_Count, ServerOptions, MaxSessions> {
|
||||
private:
|
||||
virtual Result OnNeedsToAccept(int port_index, Server *server) override;
|
||||
};
|
||||
|
||||
ServerManager g_server_manager;
|
||||
|
||||
Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
|
||||
/* Acknowledge the mitm session. */
|
||||
std::shared_ptr<::Service> fsrv;
|
||||
sm::MitmProcessInfo client_info;
|
||||
server->AcknowledgeMitmSession(std::addressof(fsrv), std::addressof(client_info));
|
||||
|
||||
switch (port_index) {
|
||||
case PortIndex_Mitm:
|
||||
R_RETURN(this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<IFsMitmInterface, FsMitmService>(decltype(fsrv)(fsrv), client_info), fsrv));
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr size_t TotalThreads = 5;
|
||||
static_assert(TotalThreads >= 1, "TotalThreads");
|
||||
constexpr size_t NumExtraThreads = TotalThreads - 1;
|
||||
constexpr size_t ThreadStackSize = mitm::ModuleTraits<fs::MitmModule>::StackSize;
|
||||
alignas(os::MemoryPageSize) u8 g_extra_thread_stacks[NumExtraThreads][ThreadStackSize];
|
||||
|
||||
os::ThreadType g_extra_threads[NumExtraThreads];
|
||||
|
||||
void LoopServerThread(void *) {
|
||||
/* Loop forever, servicing our services. */
|
||||
g_server_manager.LoopProcess();
|
||||
}
|
||||
|
||||
void ProcessForServerOnAllThreads() {
|
||||
/* Initialize threads. */
|
||||
if constexpr (NumExtraThreads > 0) {
|
||||
const s32 priority = os::GetThreadCurrentPriority(os::GetCurrentThread());
|
||||
for (size_t i = 0; i < NumExtraThreads; i++) {
|
||||
R_ABORT_UNLESS(os::CreateThread(g_extra_threads + i, LoopServerThread, nullptr, g_extra_thread_stacks[i], ThreadStackSize, priority));
|
||||
}
|
||||
}
|
||||
|
||||
/* Start extra threads. */
|
||||
if constexpr (NumExtraThreads > 0) {
|
||||
for (size_t i = 0; i < NumExtraThreads; i++) {
|
||||
os::StartThread(g_extra_threads + i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop this thread. */
|
||||
LoopServerThread(nullptr);
|
||||
|
||||
/* Wait for extra threads to finish. */
|
||||
if constexpr (NumExtraThreads > 0) {
|
||||
for (size_t i = 0; i < NumExtraThreads; i++) {
|
||||
os::WaitThread(g_extra_threads + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MitmModule::ThreadFunction(void *) {
|
||||
/* Create fs mitm. */
|
||||
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<FsMitmService>(PortIndex_Mitm, MitmServiceName)));
|
||||
|
||||
/* Process for the server. */
|
||||
ProcessForServerOnAllThreads();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_module.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
DEFINE_MITM_MODULE_CLASS(0x8000, AMS_GET_SYSTEM_THREAD_PRIORITY(fs, WorkerThreadPool) - 1);
|
||||
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
class ReadOnlyLayeredFileSystem : public ams::fs::fsa::IFileSystem {
|
||||
private:
|
||||
ams::fs::ReadOnlyFileSystem m_fs_1;
|
||||
ams::fs::ReadOnlyFileSystem m_fs_2;
|
||||
public:
|
||||
explicit ReadOnlyLayeredFileSystem(std::unique_ptr<ams::fs::fsa::IFileSystem> a, std::unique_ptr<ams::fs::fsa::IFileSystem> b) : m_fs_1(std::move(a)), m_fs_2(std::move(b)) { /* ... */ }
|
||||
|
||||
virtual ~ReadOnlyLayeredFileSystem() { /* ... */ }
|
||||
private:
|
||||
virtual Result DoCreateFile(const ams::fs::Path &path, s64 size, int flags) override final {
|
||||
AMS_UNUSED(path, size, flags);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result DoDeleteFile(const ams::fs::Path &path) override final {
|
||||
AMS_UNUSED(path);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result DoCreateDirectory(const ams::fs::Path &path) override final {
|
||||
AMS_UNUSED(path);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result DoDeleteDirectory(const ams::fs::Path &path) override final {
|
||||
AMS_UNUSED(path);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result DoDeleteDirectoryRecursively(const ams::fs::Path &path) override final {
|
||||
AMS_UNUSED(path);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result DoRenameFile(const ams::fs::Path &old_path, const ams::fs::Path &new_path) override final {
|
||||
AMS_UNUSED(old_path, new_path);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result DoRenameDirectory(const ams::fs::Path &old_path, const ams::fs::Path &new_path) override final {
|
||||
AMS_UNUSED(old_path, new_path);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result DoGetEntryType(ams::fs::DirectoryEntryType *out, const ams::fs::Path &path) override final {
|
||||
R_SUCCEED_IF(R_SUCCEEDED(m_fs_1.GetEntryType(out, path)));
|
||||
R_RETURN(m_fs_2.GetEntryType(out, path));
|
||||
}
|
||||
|
||||
virtual Result DoOpenFile(std::unique_ptr<ams::fs::fsa::IFile> *out_file, const ams::fs::Path &path, ams::fs::OpenMode mode) override final {
|
||||
R_SUCCEED_IF(R_SUCCEEDED(m_fs_1.OpenFile(out_file, path, mode)));
|
||||
R_RETURN(m_fs_2.OpenFile(out_file, path, mode));
|
||||
}
|
||||
|
||||
virtual Result DoOpenDirectory(std::unique_ptr<ams::fs::fsa::IDirectory> *out_dir, const ams::fs::Path &path, ams::fs::OpenDirectoryMode mode) override final {
|
||||
R_SUCCEED_IF(R_SUCCEEDED(m_fs_1.OpenDirectory(out_dir, path, mode)));
|
||||
R_RETURN(m_fs_2.OpenDirectory(out_dir, path, mode));
|
||||
}
|
||||
|
||||
virtual Result DoCommit() override final {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result DoGetFreeSpaceSize(s64 *out, const ams::fs::Path &path) {
|
||||
AMS_UNUSED(out, path);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result DoGetTotalSpaceSize(s64 *out, const ams::fs::Path &path) {
|
||||
AMS_UNUSED(out, path);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result DoCleanDirectoryRecursively(const ams::fs::Path &path) {
|
||||
AMS_UNUSED(path);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result DoGetFileTimeStampRaw(ams::fs::FileTimeStampRaw *out, const ams::fs::Path &path) {
|
||||
R_SUCCEED_IF(R_SUCCEEDED(m_fs_1.GetFileTimeStampRaw(out, path)));
|
||||
R_RETURN(m_fs_2.GetFileTimeStampRaw(out, path));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,379 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::fs::romfs {
|
||||
|
||||
enum class DataSourceType : u8 {
|
||||
Storage,
|
||||
File,
|
||||
LooseSdFile,
|
||||
Metadata,
|
||||
Memory,
|
||||
};
|
||||
|
||||
enum AllocationType {
|
||||
AllocationType_FileName,
|
||||
AllocationType_DirName,
|
||||
AllocationType_FullPath,
|
||||
AllocationType_SourceInfo,
|
||||
AllocationType_BuildFileContext,
|
||||
AllocationType_BuildDirContext,
|
||||
AllocationType_TableCache,
|
||||
AllocationType_DirPointerArray,
|
||||
AllocationType_DirContextSet,
|
||||
AllocationType_FileContextSet,
|
||||
AllocationType_Memory,
|
||||
|
||||
AllocationType_Count,
|
||||
};
|
||||
|
||||
void *AllocateTracked(AllocationType type, size_t size);
|
||||
void FreeTracked(AllocationType type, void *p, size_t size);
|
||||
|
||||
template<typename T, typename... Args>
|
||||
T *AllocateTyped(AllocationType type, Args &&... args) {
|
||||
void *mem = AllocateTracked(type, sizeof(T));
|
||||
return std::construct_at(static_cast<T *>(mem), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<AllocationType AllocType, typename T>
|
||||
class TrackedAllocator {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
template<typename U>
|
||||
struct rebind {
|
||||
using other = TrackedAllocator<AllocType, U>;
|
||||
};
|
||||
public:
|
||||
TrackedAllocator() = default;
|
||||
|
||||
T *allocate(size_t n) {
|
||||
return static_cast<T *>(AllocateTracked(AllocType, sizeof(T) * n));
|
||||
}
|
||||
|
||||
void deallocate(T *p, size_t n) {
|
||||
FreeTracked(AllocType, p, sizeof(T) * n);
|
||||
}
|
||||
};
|
||||
|
||||
struct SourceInfo {
|
||||
s64 virtual_offset;
|
||||
s64 size;
|
||||
union {
|
||||
struct {
|
||||
s64 offset;
|
||||
} storage_source_info;
|
||||
struct {
|
||||
s64 offset;
|
||||
} file_source_info;
|
||||
struct {
|
||||
char *path;
|
||||
} loose_source_info;
|
||||
struct {
|
||||
ams::fs::fsa::IFile *file;
|
||||
} metadata_source_info;
|
||||
struct {
|
||||
u8 *data;
|
||||
} memory_source_info;
|
||||
};
|
||||
DataSourceType source_type;
|
||||
bool cleaned_up;
|
||||
|
||||
SourceInfo(s64 v_o, s64 sz, DataSourceType type, s64 p_o) : virtual_offset(v_o), size(sz), source_type(type), cleaned_up(false) {
|
||||
switch (this->source_type) {
|
||||
case DataSourceType::Storage:
|
||||
this->storage_source_info.offset = p_o;
|
||||
break;
|
||||
case DataSourceType::File:
|
||||
this->file_source_info.offset = p_o;
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
SourceInfo(s64 v_o, s64 sz, DataSourceType type, void *arg) : virtual_offset(v_o), size(sz), source_type(type), cleaned_up(false) {
|
||||
switch (this->source_type) {
|
||||
case DataSourceType::LooseSdFile:
|
||||
this->loose_source_info.path = static_cast<char *>(arg);
|
||||
break;
|
||||
case DataSourceType::Metadata:
|
||||
this->metadata_source_info.file = static_cast<ams::fs::fsa::IFile *>(arg);
|
||||
break;
|
||||
case DataSourceType::Memory:
|
||||
this->memory_source_info.data = static_cast<u8 *>(arg);
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
void Cleanup() {
|
||||
AMS_ABORT_UNLESS(!this->cleaned_up);
|
||||
this->cleaned_up = true;
|
||||
|
||||
switch (this->source_type) {
|
||||
case DataSourceType::Storage:
|
||||
case DataSourceType::File:
|
||||
break;
|
||||
case DataSourceType::Metadata:
|
||||
delete this->metadata_source_info.file;
|
||||
break;
|
||||
case DataSourceType::LooseSdFile:
|
||||
FreeTracked(AllocationType_FullPath, this->loose_source_info.path, std::strlen(this->loose_source_info.path) + 1);
|
||||
break;
|
||||
case DataSourceType::Memory:
|
||||
FreeTracked(AllocationType_Memory, this->memory_source_info.data, this->size);
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
constexpr inline bool operator<(const SourceInfo &lhs, const SourceInfo &rhs) {
|
||||
return lhs.virtual_offset < rhs.virtual_offset;
|
||||
}
|
||||
|
||||
constexpr inline bool operator<(const SourceInfo &lhs, const s64 rhs) {
|
||||
return lhs.virtual_offset <= rhs;
|
||||
}
|
||||
|
||||
struct BuildFileContext;
|
||||
|
||||
struct BuildDirectoryContext {
|
||||
NON_COPYABLE(BuildDirectoryContext);
|
||||
NON_MOVEABLE(BuildDirectoryContext);
|
||||
|
||||
char *path;
|
||||
union {
|
||||
BuildDirectoryContext *parent;
|
||||
};
|
||||
union {
|
||||
BuildDirectoryContext *child;
|
||||
struct {
|
||||
u32 parent_offset;
|
||||
u32 child_offset;
|
||||
};
|
||||
};
|
||||
union {
|
||||
BuildDirectoryContext *sibling;
|
||||
u32 sibling_offset;
|
||||
};
|
||||
union {
|
||||
BuildFileContext *file;
|
||||
u32 file_offset;
|
||||
};
|
||||
u32 path_len;
|
||||
u32 entry_offset;
|
||||
u32 hash_value;
|
||||
|
||||
struct RootTag{};
|
||||
|
||||
BuildDirectoryContext(RootTag) : parent(nullptr), child(nullptr), sibling(nullptr), file(nullptr), path_len(0), entry_offset(0), hash_value(0xFFFFFFFF) {
|
||||
this->path = static_cast<char *>(AllocateTracked(AllocationType_DirName, 1));
|
||||
this->path[0] = '\x00';
|
||||
}
|
||||
|
||||
BuildDirectoryContext(const char *entry_name, size_t entry_name_len) : parent(nullptr), child(nullptr), sibling(nullptr), file(nullptr), entry_offset(0) {
|
||||
this->path_len = entry_name_len;
|
||||
this->path = static_cast<char *>(AllocateTracked(AllocationType_DirName, this->path_len + 1));
|
||||
std::memcpy(this->path, entry_name, entry_name_len);
|
||||
this->path[this->path_len] = '\x00';
|
||||
}
|
||||
|
||||
~BuildDirectoryContext() {
|
||||
if (this->path != nullptr) {
|
||||
FreeTracked(AllocationType_DirName, this->path, this->path_len + 1);
|
||||
this->path = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void operator delete(void *p) {
|
||||
FreeTracked(AllocationType_BuildDirContext, p, sizeof(BuildDirectoryContext));
|
||||
}
|
||||
|
||||
size_t GetPathLength() const {
|
||||
if (this->parent == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this->parent->GetPathLength() + 1 + this->path_len;
|
||||
}
|
||||
|
||||
size_t GetPath(char *dst) const {
|
||||
if (this->parent == nullptr) {
|
||||
dst[0] = '\x00';
|
||||
return 0;
|
||||
}
|
||||
|
||||
const size_t parent_len = this->parent->GetPath(dst);
|
||||
dst[parent_len] = '/';
|
||||
std::memcpy(dst + parent_len + 1, this->path, this->path_len);
|
||||
dst[parent_len + 1 + this->path_len] = '\x00';
|
||||
return parent_len + 1 + this->path_len;
|
||||
}
|
||||
|
||||
bool HasHashMark() const {
|
||||
return reinterpret_cast<uintptr_t>(this->sibling) & UINT64_C(0x8000000000000000);
|
||||
}
|
||||
|
||||
void SetHashMark() {
|
||||
this->sibling = reinterpret_cast<BuildDirectoryContext *>(reinterpret_cast<uintptr_t>(this->sibling) | UINT64_C(0x8000000000000000));
|
||||
}
|
||||
|
||||
void ClearHashMark() {
|
||||
this->sibling = reinterpret_cast<BuildDirectoryContext *>(reinterpret_cast<uintptr_t>(this->sibling) & ~UINT64_C(0x8000000000000000));
|
||||
}
|
||||
};
|
||||
|
||||
struct BuildFileContext {
|
||||
NON_COPYABLE(BuildFileContext);
|
||||
NON_MOVEABLE(BuildFileContext);
|
||||
|
||||
char *path;
|
||||
BuildDirectoryContext *parent;
|
||||
union {
|
||||
BuildFileContext *sibling;
|
||||
u32 sibling_offset;
|
||||
};
|
||||
s64 offset;
|
||||
s64 size;
|
||||
s64 orig_offset;
|
||||
u32 path_len;
|
||||
u32 entry_offset;
|
||||
u32 hash_value;
|
||||
DataSourceType source_type;
|
||||
|
||||
BuildFileContext(const char *entry_name, size_t entry_name_len, s64 sz, s64 o_o, DataSourceType type) : parent(nullptr), sibling(nullptr), offset(0), size(sz), orig_offset(o_o), entry_offset(0), hash_value(0xFFFFFFFF), source_type(type) {
|
||||
this->path_len = entry_name_len;
|
||||
this->path = static_cast<char *>(AllocateTracked(AllocationType_FileName, this->path_len + 1));
|
||||
std::memcpy(this->path, entry_name, entry_name_len);
|
||||
this->path[this->path_len] = 0;
|
||||
}
|
||||
|
||||
~BuildFileContext() {
|
||||
if (this->path != nullptr) {
|
||||
FreeTracked(AllocationType_FileName, this->path, this->path_len + 1);
|
||||
this->path = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void operator delete(void *p) {
|
||||
FreeTracked(AllocationType_BuildFileContext, p, sizeof(BuildFileContext));
|
||||
}
|
||||
|
||||
size_t GetPathLength() const {
|
||||
if (this->parent == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this->parent->GetPathLength() + 1 + this->path_len;
|
||||
}
|
||||
|
||||
size_t GetPath(char *dst) const {
|
||||
if (this->parent == nullptr) {
|
||||
dst[0] = '\x00';
|
||||
return 0;
|
||||
}
|
||||
|
||||
const size_t parent_len = this->parent->GetPath(dst);
|
||||
dst[parent_len] = '/';
|
||||
std::memcpy(dst + parent_len + 1, this->path, this->path_len);
|
||||
dst[parent_len + 1 + this->path_len] = '\x00';
|
||||
return parent_len + 1 + this->path_len;
|
||||
}
|
||||
|
||||
bool HasHashMark() const {
|
||||
return reinterpret_cast<uintptr_t>(this->sibling) & UINT64_C(0x8000000000000000);
|
||||
}
|
||||
|
||||
void SetHashMark() {
|
||||
this->sibling = reinterpret_cast<BuildFileContext *>(reinterpret_cast<uintptr_t>(this->sibling) | UINT64_C(0x8000000000000000));
|
||||
}
|
||||
|
||||
void ClearHashMark() {
|
||||
this->sibling = reinterpret_cast<BuildFileContext *>(reinterpret_cast<uintptr_t>(this->sibling) & ~UINT64_C(0x8000000000000000));
|
||||
}
|
||||
};
|
||||
|
||||
class DirectoryTableReader;
|
||||
class FileTableReader;
|
||||
|
||||
class Builder {
|
||||
NON_COPYABLE(Builder);
|
||||
NON_MOVEABLE(Builder);
|
||||
public:
|
||||
using SourceInfoVector = std::vector<SourceInfo, TrackedAllocator<AllocationType_SourceInfo, SourceInfo>>;
|
||||
private:
|
||||
template<typename T>
|
||||
struct Comparator {
|
||||
static constexpr inline int Compare(const char *a, const char *b) {
|
||||
unsigned char c1{}, c2{};
|
||||
while ((c1 = *a++) == (c2 = *b++)) {
|
||||
if (c1 == '\x00') {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return (c1 - c2);
|
||||
}
|
||||
|
||||
constexpr bool operator()(const std::unique_ptr<T> &lhs, const std::unique_ptr<T> &rhs) const {
|
||||
char lhs_path[ams::fs::EntryNameLengthMax + 1];
|
||||
char rhs_path[ams::fs::EntryNameLengthMax + 1];
|
||||
lhs->GetPath(lhs_path);
|
||||
rhs->GetPath(rhs_path);
|
||||
return Comparator::Compare(lhs_path, rhs_path) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
template<AllocationType AllocType, typename T>
|
||||
using ContextSet = std::set<std::unique_ptr<T>, Comparator<T>, TrackedAllocator<AllocType, std::unique_ptr<T>>>;
|
||||
private:
|
||||
ncm::ProgramId m_program_id;
|
||||
BuildDirectoryContext *m_root;
|
||||
ContextSet<AllocationType_DirContextSet, BuildDirectoryContext> m_directories;
|
||||
ContextSet<AllocationType_FileContextSet, BuildFileContext> m_files;
|
||||
size_t m_num_dirs;
|
||||
size_t m_num_files;
|
||||
size_t m_dir_table_size;
|
||||
size_t m_file_table_size;
|
||||
size_t m_dir_hash_table_size;
|
||||
size_t m_file_hash_table_size;
|
||||
size_t m_file_partition_size;
|
||||
|
||||
::FsDirectoryEntry m_dir_entry;
|
||||
DataSourceType m_cur_source_type;
|
||||
private:
|
||||
void VisitDirectory(FsFileSystem *fs, BuildDirectoryContext *parent);
|
||||
void VisitDirectory(BuildDirectoryContext *parent, u32 parent_offset, DirectoryTableReader &dir_table, FileTableReader &file_table);
|
||||
|
||||
void AddDirectory(BuildDirectoryContext **out, BuildDirectoryContext *parent_ctx, std::unique_ptr<BuildDirectoryContext> file_ctx);
|
||||
void AddFile(BuildDirectoryContext *parent_ctx, std::unique_ptr<BuildFileContext> file_ctx);
|
||||
public:
|
||||
Builder(ncm::ProgramId pr_id);
|
||||
~Builder();
|
||||
|
||||
void AddSdFiles();
|
||||
void AddStorageFiles(ams::fs::IStorage *storage, DataSourceType source_type);
|
||||
|
||||
void Build(SourceInfoVector *out_infos);
|
||||
};
|
||||
|
||||
Result ConfigureDynamicHeap(u64 *out_size, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application);
|
||||
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsmitm_save_utils.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
using namespace ams::fs;
|
||||
|
||||
namespace {
|
||||
|
||||
Result GetSaveDataSpaceIdString(const char **out_str, u8 space_id) {
|
||||
switch (static_cast<SaveDataSpaceId>(space_id)) {
|
||||
case SaveDataSpaceId::System:
|
||||
case SaveDataSpaceId::ProperSystem:
|
||||
*out_str = "sys";
|
||||
break;
|
||||
case SaveDataSpaceId::User:
|
||||
*out_str = "user";
|
||||
break;
|
||||
case SaveDataSpaceId::SdSystem:
|
||||
*out_str = "sd_sys";
|
||||
break;
|
||||
case SaveDataSpaceId::Temporary:
|
||||
*out_str = "temp";
|
||||
break;
|
||||
case SaveDataSpaceId::SdUser:
|
||||
*out_str = "sd_user";
|
||||
break;
|
||||
case SaveDataSpaceId::SafeMode:
|
||||
*out_str = "safe";
|
||||
break;
|
||||
default:
|
||||
R_THROW(fs::ResultInvalidSaveDataSpaceId());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetSaveDataTypeString(const char **out_str, SaveDataType save_data_type) {
|
||||
switch (save_data_type) {
|
||||
case SaveDataType::System:
|
||||
*out_str = "system";
|
||||
break;
|
||||
case SaveDataType::Account:
|
||||
*out_str = "account";
|
||||
break;
|
||||
case SaveDataType::Bcat:
|
||||
*out_str = "bcat";
|
||||
break;
|
||||
case SaveDataType::Device:
|
||||
*out_str = "device";
|
||||
break;
|
||||
case SaveDataType::Temporary:
|
||||
*out_str = "temp";
|
||||
break;
|
||||
case SaveDataType::Cache:
|
||||
*out_str = "cache";
|
||||
break;
|
||||
case SaveDataType::SystemBcat:
|
||||
*out_str = "system_bcat";
|
||||
break;
|
||||
default:
|
||||
/* TODO: Better result? */
|
||||
R_THROW(fs::ResultInvalidArgument());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
constexpr inline bool IsEmptyAccountId(const UserId &uid) {
|
||||
return uid == InvalidUserId;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Result SaveUtil::GetDirectorySaveDataPath(char *dst, size_t dst_size, ncm::ProgramId program_id, u8 space_id, const fs::SaveDataAttribute &attribute) {
|
||||
/* Saves should be separate for emunand vs sysnand. */
|
||||
const char *emummc_str = emummc::IsActive() ? "emummc" : "sysmmc";
|
||||
|
||||
/* Get space_id, save_data_type strings. */
|
||||
const char *space_id_str, *save_type_str;
|
||||
R_TRY(GetSaveDataSpaceIdString(&space_id_str, space_id));
|
||||
R_TRY(GetSaveDataTypeString(&save_type_str, attribute.type));
|
||||
|
||||
/* Initialize the path. */
|
||||
const bool is_system = attribute.system_save_data_id != InvalidSystemSaveDataId && IsEmptyAccountId(attribute.user_id);
|
||||
size_t out_path_len;
|
||||
if (is_system) {
|
||||
out_path_len = static_cast<size_t>(util::SNPrintf(dst, dst_size, "/atmosphere/saves/%s/%s/%s/%016lx", emummc_str, space_id_str, save_type_str, attribute.system_save_data_id));
|
||||
} else {
|
||||
out_path_len = static_cast<size_t>(util::SNPrintf(dst, dst_size, "/atmosphere/saves/%s/%s/%s/%016lx/%016lx%016lx", emummc_str, space_id_str, save_type_str, static_cast<u64>(program_id), attribute.user_id.data[1], attribute.user_id.data[0]));
|
||||
}
|
||||
|
||||
R_UNLESS(out_path_len < dst_size, fs::ResultTooLongPath());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
class SaveUtil {
|
||||
public:
|
||||
static Result GetDirectorySaveDataPath(char *dst, size_t dst_size, ncm::ProgramId program_id, u8 space_id, const ams::fs::SaveDataAttribute &attribute);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#define NX_SERVICE_ASSUME_NON_DOMAIN
|
||||
#include "service_guard.h"
|
||||
#include "memlet.h"
|
||||
|
||||
static Service g_memletSrv;
|
||||
|
||||
NX_GENERATE_SERVICE_GUARD(memlet);
|
||||
|
||||
Result _memletInitialize(void) {
|
||||
return smGetService(&g_memletSrv, "memlet");
|
||||
}
|
||||
|
||||
void _memletCleanup(void) {
|
||||
serviceClose(&g_memletSrv);
|
||||
}
|
||||
|
||||
Service* memletGetServiceSession(void) {
|
||||
return &g_memletSrv;
|
||||
}
|
||||
|
||||
Result memletCreateAppletSharedMemory(Handle *out_shmem_h, u64 *out_size, u64 desired_size) {
|
||||
return serviceDispatchInOut(&g_memletSrv, 65000, desired_size, *out_size,
|
||||
.out_handle_attrs = { SfOutHandleAttr_HipcMove },
|
||||
.out_handles = out_shmem_h,
|
||||
);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
Result memletInitialize(void);
|
||||
void memletExit(void);
|
||||
Service* memletGetServiceSession(void);
|
||||
|
||||
Result memletCreateAppletSharedMemory(Handle *out_shmem_h, u64 *out_size, u64 desired_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,65 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <switch/types.h>
|
||||
#include <switch/result.h>
|
||||
#include <switch/kernel/mutex.h>
|
||||
#include <switch/sf/service.h>
|
||||
#include <switch/services/sm.h>
|
||||
|
||||
typedef struct ServiceGuard {
|
||||
Mutex mutex;
|
||||
u32 refCount;
|
||||
} ServiceGuard;
|
||||
|
||||
NX_INLINE bool serviceGuardBeginInit(ServiceGuard* g)
|
||||
{
|
||||
mutexLock(&g->mutex);
|
||||
return (g->refCount++) == 0;
|
||||
}
|
||||
|
||||
NX_INLINE Result serviceGuardEndInit(ServiceGuard* g, Result rc, void (*cleanupFunc)(void))
|
||||
{
|
||||
if (R_FAILED(rc)) {
|
||||
cleanupFunc();
|
||||
--g->refCount;
|
||||
}
|
||||
mutexUnlock(&g->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
NX_INLINE void serviceGuardExit(ServiceGuard* g, void (*cleanupFunc)(void))
|
||||
{
|
||||
mutexLock(&g->mutex);
|
||||
if (g->refCount && (--g->refCount) == 0)
|
||||
cleanupFunc();
|
||||
mutexUnlock(&g->mutex);
|
||||
}
|
||||
|
||||
#define NX_GENERATE_SERVICE_GUARD_PARAMS(name, _paramdecl, _parampass) \
|
||||
\
|
||||
static ServiceGuard g_##name##Guard; \
|
||||
NX_INLINE Result _##name##Initialize _paramdecl; \
|
||||
static void _##name##Cleanup(void); \
|
||||
\
|
||||
Result name##Initialize _paramdecl \
|
||||
{ \
|
||||
Result rc = 0; \
|
||||
if (serviceGuardBeginInit(&g_##name##Guard)) \
|
||||
rc = _##name##Initialize _parampass; \
|
||||
return serviceGuardEndInit(&g_##name##Guard, rc, _##name##Cleanup); \
|
||||
} \
|
||||
\
|
||||
void name##Exit(void) \
|
||||
{ \
|
||||
serviceGuardExit(&g_##name##Guard, _##name##Cleanup); \
|
||||
}
|
||||
|
||||
#define NX_GENERATE_SERVICE_GUARD(name) NX_GENERATE_SERVICE_GUARD_PARAMS(name, (void), ())
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "mitm_pm_module.hpp"
|
||||
#include "mitm_pm_service.hpp"
|
||||
|
||||
namespace ams::mitm::pm {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr sm::ServiceName PmServiceName = sm::ServiceName::Encode("mitm:pm");
|
||||
constexpr size_t PmMaxSessions = 1;
|
||||
|
||||
constexpr size_t MaxServers = 1;
|
||||
constexpr size_t MaxSessions = PmMaxSessions;
|
||||
using ServerOptions = sf::hipc::DefaultServerManagerOptions;
|
||||
sf::hipc::ServerManager<MaxServers, ServerOptions, MaxSessions> g_server_manager;
|
||||
|
||||
constinit sf::UnmanagedServiceObject<mitm::pm::impl::IPmInterface, mitm::pm::PmService> g_pm_service_object;
|
||||
|
||||
}
|
||||
|
||||
void MitmModule::ThreadFunction(void *) {
|
||||
/* Create bpc:ams. */
|
||||
R_ABORT_UNLESS(g_server_manager.RegisterObjectForServer(g_pm_service_object.GetShared(), PmServiceName, PmMaxSessions));
|
||||
|
||||
/* Loop forever, servicing our services. */
|
||||
g_server_manager.LoopProcess();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_module.hpp"
|
||||
|
||||
namespace ams::mitm::pm {
|
||||
|
||||
DEFINE_MITM_MODULE_CLASS(0x1000, AMS_GET_SYSTEM_THREAD_PRIORITY(fs, WorkerThreadPool) - 2);
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "mitm_pm_service.hpp"
|
||||
#include "mitm_pm_service.hpp"
|
||||
#include "../fs_mitm/fsmitm_romfs.hpp"
|
||||
|
||||
namespace ams::mitm::pm {
|
||||
|
||||
Result PmService::PrepareLaunchProgram(sf::Out<u64> out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application) {
|
||||
/* Default to zero heap. */
|
||||
*out = 0;
|
||||
|
||||
/* Actually configure the required boost size for romfs. */
|
||||
R_TRY(mitm::fs::romfs::ConfigureDynamicHeap(out.GetPointer(), program_id, status, is_application));
|
||||
|
||||
/* TODO: Is there anything else we should do, while we have the opportunity? */
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::pm {
|
||||
|
||||
class PmService {
|
||||
public:
|
||||
Result PrepareLaunchProgram(sf::Out<u64> out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application);
|
||||
};
|
||||
static_assert(impl::IsIPmInterface<PmService>);
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_fs_utils.hpp"
|
||||
#include "ns_am_mitm_service.hpp"
|
||||
#include "ns_shim.h"
|
||||
|
||||
namespace ams::mitm::ns {
|
||||
|
||||
Result NsAmMitmService::GetApplicationContentPath(const sf::OutBuffer &out_path, ncm::ProgramId application_id, u8 content_type) {
|
||||
R_RETURN(nsamGetApplicationContentPathFwd(m_forward_service.get(), out_path.GetPointer(), out_path.GetSize(), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type)));
|
||||
}
|
||||
|
||||
Result NsAmMitmService::ResolveApplicationContentPath(ncm::ProgramId application_id, u8 content_type) {
|
||||
/* Always succeed for web applets asking about HBL to enable hbl_html, and applications with manual_html to allow custom manual data. */
|
||||
bool is_hbl = false;
|
||||
if ((R_SUCCEEDED(ams::pm::info::IsHblProgramId(std::addressof(is_hbl), application_id)) && is_hbl) || (static_cast<ncm::ContentType>(content_type) == ncm::ContentType::HtmlDocument && mitm::fs::HasSdManualHtmlContent(application_id))) {
|
||||
nsamResolveApplicationContentPathFwd(m_forward_service.get(), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type));
|
||||
R_SUCCEED();
|
||||
}
|
||||
R_RETURN(nsamResolveApplicationContentPathFwd(m_forward_service.get(), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type)));
|
||||
}
|
||||
|
||||
Result NsAmMitmService::GetRunningApplicationProgramId(sf::Out<ncm::ProgramId> out, ncm::ProgramId application_id) {
|
||||
R_RETURN(nsamGetRunningApplicationProgramIdFwd(m_forward_service.get(), reinterpret_cast<u64 *>(out.GetPointer()), static_cast<u64>(application_id)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#define AMS_NS_AM_MITM_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 21, Result, GetApplicationContentPath, (const sf::OutBuffer &out_path, ncm::ProgramId application_id, u8 content_type), (out_path, application_id, content_type)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 23, Result, ResolveApplicationContentPath, (ncm::ProgramId application_id, u8 content_type), (application_id, content_type)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 92, Result, GetRunningApplicationProgramId, (sf::Out<ncm::ProgramId> out, ncm::ProgramId application_id), (out, application_id), hos::Version_6_0_0)
|
||||
|
||||
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::ns::impl, IAmMitmInterface, AMS_NS_AM_MITM_INTERFACE_INFO, 0x059D2C39)
|
||||
|
||||
namespace ams::mitm::ns {
|
||||
|
||||
class NsAmMitmService : public sf::MitmServiceImplBase {
|
||||
public:
|
||||
using MitmServiceImplBase::MitmServiceImplBase;
|
||||
public:
|
||||
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
|
||||
/* We will mitm:
|
||||
* - web applets, to facilitate hbl web browser launching.
|
||||
*/
|
||||
return ncm::IsWebAppletId(client_info.program_id);
|
||||
}
|
||||
public:
|
||||
/* Actual command API. */
|
||||
Result GetApplicationContentPath(const sf::OutBuffer &out_path, ncm::ProgramId application_id, u8 content_type);
|
||||
Result ResolveApplicationContentPath(ncm::ProgramId application_id, u8 content_type);
|
||||
Result GetRunningApplicationProgramId(sf::Out<ncm::ProgramId> out, ncm::ProgramId application_id);
|
||||
};
|
||||
static_assert(impl::IsIAmMitmInterface<NsAmMitmService>);
|
||||
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <switch.h>
|
||||
#include "ns_shim.h"
|
||||
|
||||
/* Command forwarders. */
|
||||
Result nsGetDocumentInterfaceFwd(Service* s, NsDocumentInterface* out) {
|
||||
return serviceDispatch(s, 7999,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = &out->s,
|
||||
);
|
||||
}
|
||||
|
||||
static Result _nsGetApplicationContentPathOld(Service *s, void* out, size_t out_size, u64 app_id, NcmContentType content_type) {
|
||||
const struct {
|
||||
u8 content_type;
|
||||
u64 app_id;
|
||||
} in = { content_type, app_id };
|
||||
return serviceDispatchIn(s, 21, in,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
||||
.buffers = { { out, out_size } },
|
||||
);
|
||||
}
|
||||
|
||||
static Result _nsGetApplicationContentPath(Service *s, void* out, size_t out_size, u8 *out_attr, u64 app_id, NcmContentType content_type) {
|
||||
const struct {
|
||||
u8 content_type;
|
||||
u64 app_id;
|
||||
} in = { content_type, app_id };
|
||||
return serviceDispatchInOut(s, 21, in, *out_attr,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
||||
.buffers = { { out, out_size } },
|
||||
);
|
||||
}
|
||||
|
||||
static Result _nsGetApplicationContentPath2(Service *s, void* out_path, size_t out_size, u64* out_program_id, u8 *out_attr, u64 app_id, NcmContentType content_type) {
|
||||
const struct {
|
||||
u8 content_type;
|
||||
u64 app_id;
|
||||
} in = { content_type, app_id };
|
||||
|
||||
struct {
|
||||
u8 attr;
|
||||
u64 program_id;
|
||||
} out;
|
||||
|
||||
Result rc = serviceDispatchInOut(s, 2524, in, out,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
||||
.buffers = { { out_path, out_size } },
|
||||
);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
*out_program_id = out.program_id;
|
||||
*out_attr = out.attr;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static Result _nsResolveApplicationContentPath(Service* s, u64 app_id, NcmContentType content_type) {
|
||||
const struct {
|
||||
u8 content_type;
|
||||
u64 app_id;
|
||||
} in = { content_type, app_id };
|
||||
return serviceDispatchIn(s, 23, in);
|
||||
}
|
||||
|
||||
static Result _nsGetRunningApplicationProgramId(Service* s, u64* out_program_id, u64 app_id) {
|
||||
if (hosversionBefore(6, 0, 0)) {
|
||||
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
|
||||
}
|
||||
return serviceDispatchInOut(s, 92, app_id, *out_program_id);
|
||||
}
|
||||
|
||||
/* Application Manager forwarders. */
|
||||
Result nsamGetApplicationContentPathFwd(Service* s, void* out, size_t out_size, u64 app_id, NcmContentType content_type) {
|
||||
return _nsGetApplicationContentPathOld(s, out, out_size, app_id, content_type);
|
||||
}
|
||||
|
||||
Result nsamResolveApplicationContentPathFwd(Service* s, u64 app_id, NcmContentType content_type) {
|
||||
return _nsResolveApplicationContentPath(s, app_id, content_type);
|
||||
}
|
||||
|
||||
Result nsamGetRunningApplicationProgramIdFwd(Service* s, u64* out_program_id, u64 app_id) {
|
||||
return _nsGetRunningApplicationProgramId(s, out_program_id, app_id);
|
||||
}
|
||||
|
||||
/* Web forwarders */
|
||||
Result nswebGetApplicationContentPath(NsDocumentInterface* doc, void* out, size_t out_size, u8 *out_attr, u64 app_id, NcmContentType content_type) {
|
||||
if (hosversionAtLeast(16,0,0)) {
|
||||
return _nsGetApplicationContentPath(&doc->s, out, out_size, out_attr, app_id, content_type);
|
||||
} else {
|
||||
return _nsGetApplicationContentPathOld(&doc->s, out, out_size, app_id, content_type);
|
||||
}
|
||||
}
|
||||
|
||||
Result nswebResolveApplicationContentPath(NsDocumentInterface* doc, u64 app_id, NcmContentType content_type) {
|
||||
return _nsResolveApplicationContentPath(&doc->s, app_id, content_type);
|
||||
}
|
||||
|
||||
Result nswebGetRunningApplicationProgramId(NsDocumentInterface* doc, u64* out_program_id, u64 app_id) {
|
||||
return _nsGetRunningApplicationProgramId(&doc->s, out_program_id, app_id);
|
||||
}
|
||||
|
||||
Result nswebGetApplicationContentPath2(NsDocumentInterface* doc, void* out, size_t out_size, u64* out_program_id, u8 *out_attr, u64 app_id, NcmContentType content_type) {
|
||||
return _nsGetApplicationContentPath2(&doc->s, out, out_size, out_program_id, out_attr, app_id, content_type);
|
||||
}
|
||||
|
||||
void nsDocumentInterfaceClose(NsDocumentInterface* doc) {
|
||||
serviceClose(&doc->s);
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/**
|
||||
* @file ns_shim.h
|
||||
* @brief Nintendo Shell Services (ns) IPC wrapper.
|
||||
* @author SciresM
|
||||
* @copyright libnx Authors
|
||||
*/
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
Service s;
|
||||
} NsDocumentInterface;
|
||||
|
||||
/* Command forwarders. */
|
||||
Result nsGetDocumentInterfaceFwd(Service* s, NsDocumentInterface* out);
|
||||
|
||||
Result nsamGetApplicationContentPathFwd(Service* s, void* out, size_t out_size, u64 app_id, NcmContentType content_type);
|
||||
Result nsamResolveApplicationContentPathFwd(Service* s, u64 app_id, NcmContentType content_type);
|
||||
Result nsamGetRunningApplicationProgramIdFwd(Service* s, u64* out_program_id, u64 app_id);
|
||||
|
||||
Result nswebGetApplicationContentPath(NsDocumentInterface* doc, void* out, size_t out_size, u8 *out_attr, u64 app_id, NcmContentType content_type);
|
||||
Result nswebResolveApplicationContentPath(NsDocumentInterface* doc, u64 app_id, NcmContentType content_type);
|
||||
Result nswebGetRunningApplicationProgramId(NsDocumentInterface* doc, u64* out_program_id, u64 app_id);
|
||||
Result nswebGetApplicationContentPath2(NsDocumentInterface* doc, void* out, size_t out_size, u64* out_program_id, u8 *out_attr, u64 app_id, NcmContentType content_type);
|
||||
|
||||
void nsDocumentInterfaceClose(NsDocumentInterface* doc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_fs_utils.hpp"
|
||||
#include "ns_web_mitm_service.hpp"
|
||||
|
||||
namespace ams::mitm::ns {
|
||||
|
||||
Result NsDocumentService::GetApplicationContentPath(const sf::OutBuffer &out_path, sf::Out<ams::fs::ContentAttributes> out_attr, ncm::ProgramId application_id, u8 content_type) {
|
||||
static_assert(sizeof(*out_attr.GetPointer()) == sizeof(u8));
|
||||
R_RETURN(nswebGetApplicationContentPath(m_srv.get(), out_path.GetPointer(), out_path.GetSize(), reinterpret_cast<u8 *>(out_attr.GetPointer()), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type)));
|
||||
}
|
||||
|
||||
Result NsDocumentService::ResolveApplicationContentPath(ncm::ProgramId application_id, u8 content_type) {
|
||||
/* Always succeed for web applets asking about HBL to enable hbl_html, and applications with manual_html to allow custom manual data. */
|
||||
bool is_hbl = false;
|
||||
if ((R_SUCCEEDED(ams::pm::info::IsHblProgramId(std::addressof(is_hbl), application_id)) && is_hbl) || (static_cast<ncm::ContentType>(content_type) == ncm::ContentType::HtmlDocument && mitm::fs::HasSdManualHtmlContent(application_id))) {
|
||||
nswebResolveApplicationContentPath(m_srv.get(), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type));
|
||||
R_SUCCEED();
|
||||
}
|
||||
R_RETURN(nswebResolveApplicationContentPath(m_srv.get(), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type)));
|
||||
}
|
||||
|
||||
Result NsDocumentService::GetRunningApplicationProgramId(sf::Out<ncm::ProgramId> out, ncm::ProgramId application_id) {
|
||||
R_RETURN(nswebGetRunningApplicationProgramId(m_srv.get(), reinterpret_cast<u64 *>(out.GetPointer()), static_cast<u64>(application_id)));
|
||||
}
|
||||
|
||||
Result NsDocumentService::GetApplicationContentPath2(const sf::OutBuffer &out_path, sf::Out<ncm::ProgramId> out_program_id, sf::Out<ams::fs::ContentAttributes> out_attr, ncm::ProgramId application_id, u8 content_type) {
|
||||
static_assert(sizeof(*out_attr.GetPointer()) == sizeof(u8));
|
||||
R_RETURN(nswebGetApplicationContentPath2(m_srv.get(), out_path.GetPointer(), out_path.GetSize(), reinterpret_cast<u64 *>(out_program_id.GetPointer()), reinterpret_cast<u8 *>(out_attr.GetPointer()), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type)));
|
||||
}
|
||||
|
||||
Result NsWebMitmService::GetDocumentInterface(sf::Out<sf::SharedPointer<impl::IDocumentInterface>> out) {
|
||||
/* Open a document interface. */
|
||||
NsDocumentInterface doc;
|
||||
R_TRY(nsGetDocumentInterfaceFwd(m_forward_service.get(), std::addressof(doc)));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(doc.s))};
|
||||
|
||||
out.SetValue(sf::CreateSharedObjectEmplaced<impl::IDocumentInterface, NsDocumentService>(m_client_info, std::make_unique<NsDocumentInterface>(doc)), target_object_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "ns_shim.h"
|
||||
|
||||
#define AMS_NS_DOCUMENT_MITM_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 21, Result, GetApplicationContentPath, (const sf::OutBuffer &out_path, sf::Out<ams::fs::ContentAttributes> out_attr, ncm::ProgramId application_id, u8 content_type), (out_path, out_attr, application_id, content_type)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 23, Result, ResolveApplicationContentPath, (ncm::ProgramId application_id, u8 content_type), (application_id, content_type)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 92, Result, GetRunningApplicationProgramId, (sf::Out<ncm::ProgramId> out, ncm::ProgramId application_id), (out, application_id), hos::Version_6_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2524, Result, GetApplicationContentPath2, (const sf::OutBuffer &out_path, sf::Out<ncm::ProgramId> out_program_id, sf::Out<ams::fs::ContentAttributes> out_attr, ncm::ProgramId application_id, u8 content_type), (out_path, out_program_id, out_attr, application_id, content_type), hos::Version_19_0_0)
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::mitm::ns::impl, IDocumentInterface, AMS_NS_DOCUMENT_MITM_INTERFACE_INFO, 0x0F9B1C00)
|
||||
|
||||
#define AMS_NS_WEB_MITM_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 7999, Result, GetDocumentInterface, (sf::Out<sf::SharedPointer<mitm::ns::impl::IDocumentInterface>> out), (out))
|
||||
|
||||
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::ns::impl, IWebMitmInterface, AMS_NS_WEB_MITM_INTERFACE_INFO, 0xF4EC2D1A)
|
||||
|
||||
namespace ams::mitm::ns {
|
||||
|
||||
class NsDocumentService {
|
||||
private:
|
||||
sm::MitmProcessInfo m_client_info;
|
||||
std::unique_ptr<::NsDocumentInterface> m_srv;
|
||||
public:
|
||||
NsDocumentService(const sm::MitmProcessInfo &cl, std::unique_ptr<::NsDocumentInterface> s) : m_client_info(cl), m_srv(std::move(s)) { /* .. */ }
|
||||
|
||||
virtual ~NsDocumentService() {
|
||||
nsDocumentInterfaceClose(m_srv.get());
|
||||
}
|
||||
public:
|
||||
/* Actual command API. */
|
||||
Result GetApplicationContentPath(const sf::OutBuffer &out_path, sf::Out<ams::fs::ContentAttributes> out_attr, ncm::ProgramId application_id, u8 content_type);
|
||||
Result ResolveApplicationContentPath(ncm::ProgramId application_id, u8 content_type);
|
||||
Result GetRunningApplicationProgramId(sf::Out<ncm::ProgramId> out, ncm::ProgramId application_id);
|
||||
Result GetApplicationContentPath2(const sf::OutBuffer &out_path, sf::Out<ncm::ProgramId> out_program_id, sf::Out<ams::fs::ContentAttributes> out_attr, ncm::ProgramId application_id, u8 content_type);
|
||||
};
|
||||
static_assert(impl::IsIDocumentInterface<NsDocumentService>);
|
||||
|
||||
class NsWebMitmService : public sf::MitmServiceImplBase {
|
||||
public:
|
||||
using MitmServiceImplBase::MitmServiceImplBase;
|
||||
public:
|
||||
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
|
||||
/* We will mitm:
|
||||
* - web applets, to facilitate hbl web browser launching.
|
||||
*/
|
||||
return ncm::IsWebAppletId(client_info.program_id);
|
||||
}
|
||||
public:
|
||||
Result GetDocumentInterface(sf::Out<sf::SharedPointer<impl::IDocumentInterface>> out);
|
||||
};
|
||||
static_assert(impl::IsIWebMitmInterface<NsWebMitmService>);
|
||||
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "nsmitm_module.hpp"
|
||||
#include "ns_am_mitm_service.hpp"
|
||||
#include "ns_web_mitm_service.hpp"
|
||||
|
||||
namespace ams::mitm::ns {
|
||||
|
||||
namespace {
|
||||
|
||||
enum PortIndex {
|
||||
PortIndex_Mitm,
|
||||
PortIndex_Count,
|
||||
};
|
||||
|
||||
constexpr sm::ServiceName NsAmMitmServiceName = sm::ServiceName::Encode("ns:am");
|
||||
constexpr sm::ServiceName NsWebMitmServiceName = sm::ServiceName::Encode("ns:web");
|
||||
|
||||
constexpr size_t MaxSessions = 5;
|
||||
|
||||
struct ServerOptions {
|
||||
static constexpr size_t PointerBufferSize = sf::hipc::DefaultServerManagerOptions::PointerBufferSize;
|
||||
static constexpr size_t MaxDomains = sf::hipc::DefaultServerManagerOptions::MaxDomains;
|
||||
static constexpr size_t MaxDomainObjects = sf::hipc::DefaultServerManagerOptions::MaxDomainObjects;
|
||||
static constexpr bool CanDeferInvokeRequest = sf::hipc::DefaultServerManagerOptions::CanDeferInvokeRequest;
|
||||
static constexpr bool CanManageMitmServers = true;
|
||||
};
|
||||
|
||||
class ServerManager final : public sf::hipc::ServerManager<PortIndex_Count, ServerOptions, MaxSessions> {
|
||||
private:
|
||||
virtual Result OnNeedsToAccept(int port_index, Server *server) override;
|
||||
};
|
||||
|
||||
ServerManager g_server_manager;
|
||||
|
||||
Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
|
||||
/* Acknowledge the mitm session. */
|
||||
std::shared_ptr<::Service> fsrv;
|
||||
sm::MitmProcessInfo client_info;
|
||||
server->AcknowledgeMitmSession(std::addressof(fsrv), std::addressof(client_info));
|
||||
|
||||
switch (port_index) {
|
||||
case PortIndex_Mitm:
|
||||
if (hos::GetVersion() < hos::Version_3_0_0) {
|
||||
R_RETURN(this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<impl::IAmMitmInterface, NsAmMitmService>(decltype(fsrv)(fsrv), client_info), fsrv));
|
||||
} else {
|
||||
R_RETURN(this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<impl::IWebMitmInterface, NsWebMitmService>(decltype(fsrv)(fsrv), client_info), fsrv));
|
||||
}
|
||||
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MitmModule::ThreadFunction(void *) {
|
||||
/* Wait until initialization is complete. */
|
||||
mitm::WaitInitialized();
|
||||
|
||||
/* Create mitm servers. */
|
||||
if (hos::GetVersion() < hos::Version_3_0_0) {
|
||||
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<NsAmMitmService>(PortIndex_Mitm, NsAmMitmServiceName)));
|
||||
} else {
|
||||
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<NsWebMitmService>(PortIndex_Mitm, NsWebMitmServiceName)));
|
||||
}
|
||||
|
||||
/* Loop forever, servicing our services. */
|
||||
g_server_manager.LoopProcess();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_module.hpp"
|
||||
|
||||
namespace ams::mitm::ns {
|
||||
|
||||
DEFINE_MITM_MODULE_CLASS(0x4000, AMS_GET_SYSTEM_THREAD_PRIORITY(ns, ApplicationManagerIpcSession) - 1);
|
||||
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "set_mitm_service.hpp"
|
||||
#include "set_shim.h"
|
||||
|
||||
namespace ams::mitm::settings {
|
||||
|
||||
using namespace ams::settings;
|
||||
|
||||
namespace {
|
||||
|
||||
constinit os::ProcessId g_application_process_id = os::InvalidProcessId;
|
||||
constinit cfg::OverrideLocale g_application_locale;
|
||||
constinit bool g_valid_language;
|
||||
constinit bool g_valid_region;
|
||||
}
|
||||
|
||||
SetMitmService::SetMitmService(std::shared_ptr<::Service> &&s, const sm::MitmProcessInfo &c) : sf::MitmServiceImplBase(std::forward<std::shared_ptr<::Service>>(s), c) {
|
||||
if (m_client_info.program_id == ncm::SystemProgramId::Ns) {
|
||||
os::ProcessId application_process_id;
|
||||
if (R_SUCCEEDED(ams::pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id))) && g_application_process_id == application_process_id) {
|
||||
m_locale = g_application_locale;
|
||||
m_is_valid_language = g_valid_language;
|
||||
m_is_valid_region = g_valid_region;
|
||||
m_got_locale = true;
|
||||
} else {
|
||||
this->InvalidateLocale();
|
||||
}
|
||||
} else {
|
||||
this->InvalidateLocale();
|
||||
}
|
||||
}
|
||||
|
||||
Result SetMitmService::EnsureLocale() {
|
||||
/* Optimization: if locale has already been gotten, we can stop. */
|
||||
if (AMS_LIKELY(m_got_locale)) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
std::scoped_lock lk(m_lock);
|
||||
|
||||
const bool is_ns = m_client_info.program_id == ncm::SystemProgramId::Ns;
|
||||
|
||||
if (!m_got_locale) {
|
||||
ncm::ProgramId program_id = m_client_info.program_id;
|
||||
os::ProcessId application_process_id = os::InvalidProcessId;
|
||||
|
||||
if (is_ns) {
|
||||
/* When NS asks for a locale, refresh to get the current application locale. */
|
||||
R_TRY(ams::pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id)));
|
||||
R_TRY(ams::pm::info::GetProgramId(std::addressof(program_id), application_process_id));
|
||||
}
|
||||
m_locale = cfg::GetOverrideLocale(program_id);
|
||||
m_is_valid_language = settings::IsValidLanguageCode(m_locale.language_code);
|
||||
m_is_valid_region = settings::IsValidRegionCode(m_locale.region_code);
|
||||
m_got_locale = true;
|
||||
|
||||
if (is_ns) {
|
||||
g_application_locale = m_locale;
|
||||
g_valid_language = m_is_valid_language;
|
||||
g_valid_region = m_is_valid_region;
|
||||
g_application_process_id = application_process_id;
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SetMitmService::InvalidateLocale() {
|
||||
std::scoped_lock lk(m_lock);
|
||||
|
||||
std::memset(std::addressof(m_locale), 0xCC, sizeof(m_locale));
|
||||
m_is_valid_language = false;
|
||||
m_is_valid_region = false;
|
||||
m_got_locale = false;
|
||||
}
|
||||
|
||||
Result SetMitmService::GetLanguageCode(sf::Out<settings::LanguageCode> out) {
|
||||
this->EnsureLocale();
|
||||
|
||||
/* If there's no override locale, just use the actual one. */
|
||||
if (AMS_UNLIKELY(!m_is_valid_language)) {
|
||||
static_assert(sizeof(u64) == sizeof(settings::LanguageCode));
|
||||
R_TRY(setGetLanguageCodeFwd(m_forward_service.get(), reinterpret_cast<u64 *>(std::addressof(m_locale.language_code))));
|
||||
|
||||
m_is_valid_language = true;
|
||||
if (m_client_info.program_id == ncm::SystemProgramId::Ns) {
|
||||
g_application_locale.language_code = m_locale.language_code;
|
||||
g_valid_language = true;
|
||||
}
|
||||
}
|
||||
|
||||
out.SetValue(m_locale.language_code);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetMitmService::GetRegionCode(sf::Out<settings::RegionCode> out) {
|
||||
this->EnsureLocale();
|
||||
|
||||
/* If there's no override locale, just use the actual one. */
|
||||
if (AMS_UNLIKELY(!m_is_valid_region)) {
|
||||
static_assert(sizeof(::SetRegion) == sizeof(settings::RegionCode));
|
||||
R_TRY(setGetRegionCodeFwd(m_forward_service.get(), reinterpret_cast<::SetRegion *>(std::addressof(m_locale.region_code))));
|
||||
|
||||
m_is_valid_region = true;
|
||||
if (m_client_info.program_id == ncm::SystemProgramId::Ns) {
|
||||
g_application_locale.region_code = m_locale.region_code;
|
||||
g_valid_region = true;
|
||||
}
|
||||
}
|
||||
|
||||
out.SetValue(m_locale.region_code);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#define AMS_SETTINGS_MITM_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, GetLanguageCode, (sf::Out<ams::settings::LanguageCode> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, GetRegionCode, (sf::Out<ams::settings::RegionCode> out), (out))
|
||||
|
||||
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::settings, ISetMitmInterface, AMS_SETTINGS_MITM_INTERFACE_INFO, 0x7F7BAF0A)
|
||||
|
||||
namespace ams::mitm::settings {
|
||||
|
||||
class SetMitmService : public sf::MitmServiceImplBase {
|
||||
private:
|
||||
os::SdkMutex m_lock{};
|
||||
cfg::OverrideLocale m_locale;
|
||||
bool m_got_locale = false;
|
||||
bool m_is_valid_language = false;
|
||||
bool m_is_valid_region = false;
|
||||
public:
|
||||
SetMitmService(std::shared_ptr<::Service> &&s, const sm::MitmProcessInfo &c);
|
||||
public:
|
||||
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
|
||||
/* We will mitm:
|
||||
* - ns and games, to allow for overriding game locales.
|
||||
*/
|
||||
const bool is_game = (ncm::IsApplicationId(client_info.program_id) && !client_info.override_status.IsHbl());
|
||||
return client_info.program_id == ncm::SystemProgramId::Ns || is_game;
|
||||
}
|
||||
private:
|
||||
void InvalidateLocale();
|
||||
Result EnsureLocale();
|
||||
public:
|
||||
Result GetLanguageCode(sf::Out<ams::settings::LanguageCode> out);
|
||||
Result GetRegionCode(sf::Out<ams::settings::RegionCode> out);
|
||||
};
|
||||
static_assert(IsISetMitmInterface<SetMitmService>);
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "set_shim.h"
|
||||
|
||||
static Result _setCmdNoInOut64(Service* srv, u64 *out, u32 cmd_id) {
|
||||
return serviceDispatchOut(srv, cmd_id, *out);
|
||||
}
|
||||
|
||||
static Result _setCmdNoInOutU32(Service* srv, u32 *out, u32 cmd_id) {
|
||||
return serviceDispatchOut(srv, cmd_id, *out);
|
||||
}
|
||||
|
||||
/* Forwarding shims. */
|
||||
Result setGetLanguageCodeFwd(Service *s, u64* out) {
|
||||
return _setCmdNoInOut64(s, out, 0);
|
||||
}
|
||||
|
||||
Result setGetRegionCodeFwd(Service *s, SetRegion *out) {
|
||||
s32 code=0;
|
||||
Result rc = _setCmdNoInOutU32(s, (u32*)&code, 4);
|
||||
if (R_SUCCEEDED(rc) && out) *out = code;
|
||||
return rc;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* @file set_shim.h
|
||||
* @brief Settings Services (fs) IPC wrapper for set.mitm.
|
||||
* @author SciresM
|
||||
* @copyright libnx Authors
|
||||
*/
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Forwarding shims. */
|
||||
Result setGetLanguageCodeFwd(Service *s, u64* out);
|
||||
Result setGetRegionCodeFwd(Service *s, SetRegion *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "setmitm_module.hpp"
|
||||
#include "set_mitm_service.hpp"
|
||||
#include "setsys_mitm_service.hpp"
|
||||
|
||||
namespace ams::mitm::settings {
|
||||
|
||||
namespace {
|
||||
|
||||
enum PortIndex {
|
||||
PortIndex_SetMitm,
|
||||
PortIndex_SetSysMitm,
|
||||
PortIndex_Count,
|
||||
};
|
||||
|
||||
constexpr sm::ServiceName SetMitmServiceName = sm::ServiceName::Encode("set");
|
||||
constexpr sm::ServiceName SetSysMitmServiceName = sm::ServiceName::Encode("set:sys");
|
||||
|
||||
struct ServerOptions {
|
||||
static constexpr size_t PointerBufferSize = 0x200;
|
||||
static constexpr size_t MaxDomains = 0;
|
||||
static constexpr size_t MaxDomainObjects = 0;
|
||||
static constexpr bool CanDeferInvokeRequest = false;
|
||||
static constexpr bool CanManageMitmServers = true;
|
||||
};
|
||||
|
||||
constexpr size_t MaxSessions = 60;
|
||||
|
||||
class ServerManager final : public sf::hipc::ServerManager<PortIndex_Count, ServerOptions, MaxSessions> {
|
||||
private:
|
||||
virtual Result OnNeedsToAccept(int port_index, Server *server) override;
|
||||
};
|
||||
|
||||
ServerManager g_server_manager;
|
||||
|
||||
Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
|
||||
/* Acknowledge the mitm session. */
|
||||
std::shared_ptr<::Service> fsrv;
|
||||
sm::MitmProcessInfo client_info;
|
||||
server->AcknowledgeMitmSession(std::addressof(fsrv), std::addressof(client_info));
|
||||
|
||||
switch (port_index) {
|
||||
case PortIndex_SetMitm:
|
||||
R_RETURN(this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<ISetMitmInterface, SetMitmService>(decltype(fsrv)(fsrv), client_info), fsrv));
|
||||
case PortIndex_SetSysMitm:
|
||||
R_RETURN(this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<ISetSysMitmInterface, SetSysMitmService>(decltype(fsrv)(fsrv), client_info), fsrv));
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MitmModule::ThreadFunction(void *) {
|
||||
/* Wait until initialization is complete. */
|
||||
mitm::WaitInitialized();
|
||||
|
||||
/* Create mitm servers. */
|
||||
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<SetMitmService>(PortIndex_SetMitm, SetMitmServiceName)));
|
||||
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<SetSysMitmService>(PortIndex_SetSysMitm, SetSysMitmServiceName)));
|
||||
|
||||
/* Loop forever, servicing our services. */
|
||||
g_server_manager.LoopProcess();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_module.hpp"
|
||||
|
||||
namespace ams::mitm::settings {
|
||||
|
||||
DEFINE_MITM_MODULE_CLASS(0x8000, AMS_GET_SYSTEM_THREAD_PRIORITY(settings, IpcServer) - 1);
|
||||
|
||||
}
|
||||
@@ -1,272 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "setsys_mitm_service.hpp"
|
||||
#include "settings_sd_kvs.hpp"
|
||||
#include "setsys_shim.h"
|
||||
|
||||
namespace ams::mitm::settings {
|
||||
|
||||
using namespace ams::settings;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const char ExternalBluetoothDatabasePath[] = "@Sdcard:/atmosphere/bluetooth_devices.db";
|
||||
|
||||
constinit os::SdkMutex g_firmware_version_lock;
|
||||
constinit bool g_cached_firmware_version;
|
||||
constinit settings::FirmwareVersion g_firmware_version;
|
||||
constinit settings::FirmwareVersion g_ams_firmware_version;
|
||||
|
||||
void CacheFirmwareVersion() {
|
||||
if (AMS_LIKELY(g_cached_firmware_version)) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::scoped_lock lk(g_firmware_version_lock);
|
||||
|
||||
if (AMS_UNLIKELY(g_cached_firmware_version)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Mount firmware version data archive. */
|
||||
{
|
||||
R_ABORT_UNLESS(ams::fs::MountSystemData("sysver", ncm::SystemDataId::SystemVersion));
|
||||
ON_SCOPE_EXIT { ams::fs::Unmount("sysver"); };
|
||||
|
||||
/* Firmware version file must exist. */
|
||||
ams::fs::FileHandle file;
|
||||
R_ABORT_UNLESS(ams::fs::OpenFile(std::addressof(file), "sysver:/file", fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { ams::fs::CloseFile(file); };
|
||||
|
||||
/* Must be possible to read firmware version from file. */
|
||||
R_ABORT_UNLESS(ams::fs::ReadFile(file, 0, std::addressof(g_firmware_version), sizeof(g_firmware_version)));
|
||||
|
||||
g_ams_firmware_version = g_firmware_version;
|
||||
}
|
||||
|
||||
/* Modify the atmosphere firmware version to display a custom version string. */
|
||||
{
|
||||
const auto api_info = exosphere::GetApiInfo();
|
||||
const char emummc_char = emummc::IsActive() ? 'E' : 'S';
|
||||
|
||||
/* NOTE: We have carefully accounted for the size of the string we print. */
|
||||
/* No truncation occurs assuming two-digits for all version number components. */
|
||||
char display_version[sizeof(g_ams_firmware_version.display_version)];
|
||||
|
||||
util::SNPrintf(display_version, sizeof(display_version), "%s|AMS %u.%u.%u|%c", g_ams_firmware_version.display_version, api_info.GetMajorVersion(), api_info.GetMinorVersion(), api_info.GetMicroVersion(), emummc_char);
|
||||
|
||||
std::memcpy(g_ams_firmware_version.display_version, display_version, sizeof(display_version));
|
||||
}
|
||||
|
||||
g_cached_firmware_version = true;
|
||||
}
|
||||
|
||||
Result GetFirmwareVersionImpl(settings::FirmwareVersion *out, const sm::MitmProcessInfo &client_info) {
|
||||
/* Ensure that we have the firmware version cached. */
|
||||
CacheFirmwareVersion();
|
||||
|
||||
/* We want to give a special firmware version to the home menu title, and nothing else. */
|
||||
/* This means qlaunch + maintenance menu, and nothing else. */
|
||||
if (client_info.program_id == ncm::SystemAppletId::Qlaunch || client_info.program_id == ncm::SystemAppletId::MaintenanceMenu) {
|
||||
*out = g_ams_firmware_version;
|
||||
} else {
|
||||
*out = g_firmware_version;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
bool IsExternalBluetoothDatabaseEnabled() {
|
||||
u8 en = 0;
|
||||
settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "enable_external_bluetooth_db");
|
||||
return en;
|
||||
}
|
||||
|
||||
bool HasExternalBluetoothDatabase() {
|
||||
bool file_exists;
|
||||
R_ABORT_UNLESS(fs::HasFile(std::addressof(file_exists), ExternalBluetoothDatabasePath));
|
||||
return file_exists;
|
||||
}
|
||||
|
||||
Result ReadExternalBluetoothDatabase(s32 *entries_read, settings::BluetoothDevicesSettings *db, size_t db_max_size) {
|
||||
/* Open the external database file. */
|
||||
fs::FileHandle file;
|
||||
R_TRY(fs::OpenFile(std::addressof(file), ExternalBluetoothDatabasePath, fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
/* Read the number of database entries stored in external database. */
|
||||
u64 total_entries;
|
||||
R_TRY(fs::ReadFile(file, 0, std::addressof(total_entries), sizeof(total_entries)));
|
||||
|
||||
u64 db_offset = sizeof(total_entries);
|
||||
if (total_entries > db_max_size) {
|
||||
/* Pairings are stored from least to most recent. Add offset to skip the older entries that won't fit. */
|
||||
db_offset += (total_entries - db_max_size) * sizeof(settings::BluetoothDevicesSettings);
|
||||
|
||||
/* Cap number of database entries read to size of database on this firmware. */
|
||||
total_entries = db_max_size;
|
||||
}
|
||||
|
||||
/* Read database entries. */
|
||||
R_TRY(fs::ReadFile(file, db_offset, db, total_entries * sizeof(settings::BluetoothDevicesSettings)));
|
||||
|
||||
/* Convert entries to the old format if running on a firmware below 13.0.0. */
|
||||
if (hos::GetVersion() < hos::Version_13_0_0) {
|
||||
for (size_t i = 0; i < total_entries; ++i) {
|
||||
/* Copy the newer name field to the older one. */
|
||||
util::TSNPrintf(db[i].name, sizeof(db[i].name), "%s", db[i].name2);
|
||||
|
||||
/* Clear the newer name field. */
|
||||
std::memset(db[i].name2, 0, sizeof(db[i].name2));
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the output. */
|
||||
*entries_read = static_cast<s32>(total_entries);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result StoreExternalBluetoothDatabase(const settings::BluetoothDevicesSettings *db, u64 total_entries) {
|
||||
/* Open the external database file. */
|
||||
fs::FileHandle file;
|
||||
R_TRY(fs::OpenFile(std::addressof(file), ExternalBluetoothDatabasePath, fs::OpenMode_Write));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
/* Ensure the file is the appropriate size for the number of entries. */
|
||||
R_TRY(fs::SetFileSize(file, sizeof(total_entries) + total_entries * sizeof(settings::BluetoothDevicesSettings)));
|
||||
|
||||
/* Write the number of database entries. */
|
||||
R_TRY(fs::WriteFile(file, 0, std::addressof(total_entries), sizeof(total_entries), fs::WriteOption::None));
|
||||
|
||||
/* Write the database entries. */
|
||||
u64 db_offset = sizeof(total_entries);
|
||||
if (hos::GetVersion() < hos::Version_13_0_0) {
|
||||
/* Convert entries to the new format if running on a firmware below 13.0.0. */
|
||||
for (size_t i = 0; i < total_entries; ++i) {
|
||||
/* Make a copy of the current database entry. */
|
||||
settings::BluetoothDevicesSettings tmp = db[i];
|
||||
|
||||
/* Copy the older name field to the newer one. */
|
||||
util::TSNPrintf(tmp.name2, sizeof(tmp.name2), "%s", tmp.name);
|
||||
|
||||
/* Clear the original name field. */
|
||||
std::memset(tmp.name, 0, sizeof(tmp.name));
|
||||
|
||||
/* Write the converted database entry. */
|
||||
R_TRY(fs::WriteFile(file, db_offset, std::addressof(tmp), sizeof(settings::BluetoothDevicesSettings), fs::WriteOption::None));
|
||||
|
||||
/* Advance to the next database entry. */
|
||||
db_offset += sizeof(settings::BluetoothDevicesSettings);
|
||||
}
|
||||
|
||||
/* Flush the data we've written. */
|
||||
R_TRY(fs::FlushFile(file));
|
||||
} else {
|
||||
/* We can just write the database to the file. */
|
||||
R_TRY(fs::WriteFile(file, db_offset, db, total_entries * sizeof(settings::BluetoothDevicesSettings), fs::WriteOption::Flush));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result SetSysMitmService::GetFirmwareVersion(sf::Out<settings::FirmwareVersion> out) {
|
||||
R_TRY(GetFirmwareVersionImpl(out.GetPointer(), m_client_info));
|
||||
|
||||
/* GetFirmwareVersion sanitizes the revision fields. */
|
||||
out.GetPointer()->revision_major = 0;
|
||||
out.GetPointer()->revision_minor = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetSysMitmService::GetFirmwareVersion2(sf::Out<settings::FirmwareVersion> out) {
|
||||
R_RETURN(GetFirmwareVersionImpl(out.GetPointer(), m_client_info));
|
||||
}
|
||||
|
||||
Result SetSysMitmService::SetBluetoothDevicesSettings(const sf::InMapAliasArray<settings::BluetoothDevicesSettings> &settings) {
|
||||
/* We only want to perform additional logic when the external database setting is enabled. */
|
||||
R_UNLESS(IsExternalBluetoothDatabaseEnabled(), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Create the external database if it doesn't exist. */
|
||||
if (!HasExternalBluetoothDatabase()) {
|
||||
R_TRY(fs::CreateFile(ExternalBluetoothDatabasePath, 0));
|
||||
}
|
||||
|
||||
/* Backup the local database to the sd card. */
|
||||
R_TRY(StoreExternalBluetoothDatabase(settings.GetPointer(), settings.GetSize()));
|
||||
|
||||
/* Ensure that the updated database is stored to the system save as usual. */
|
||||
static_assert(sizeof(settings::BluetoothDevicesSettings) == sizeof(::SetSysBluetoothDevicesSettings));
|
||||
R_TRY(setsysSetBluetoothDevicesSettingsFwd(m_forward_service.get(), reinterpret_cast<const ::SetSysBluetoothDevicesSettings *>(settings.GetPointer()), settings.GetSize()));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetSysMitmService::GetBluetoothDevicesSettings(sf::Out<s32> out_count, const sf::OutMapAliasArray<settings::BluetoothDevicesSettings> &out) {
|
||||
/* We only want to perform additional logic when the external database setting is enabled. */
|
||||
R_UNLESS(IsExternalBluetoothDatabaseEnabled(), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
if (!HasExternalBluetoothDatabase()) {
|
||||
/* Fetch the local database from the system save. */
|
||||
static_assert(sizeof(settings::BluetoothDevicesSettings) == sizeof(::SetSysBluetoothDevicesSettings));
|
||||
R_TRY(setsysGetBluetoothDevicesSettingsFwd(m_forward_service.get(), out_count.GetPointer(), reinterpret_cast<::SetSysBluetoothDevicesSettings *>(out.GetPointer()), out.GetSize()));
|
||||
|
||||
/* Create the external database file. */
|
||||
R_TRY(fs::CreateFile(ExternalBluetoothDatabasePath, 0));
|
||||
|
||||
/* Backup the local database to the sd card. */
|
||||
R_TRY(StoreExternalBluetoothDatabase(out.GetPointer(), out_count.GetValue()));
|
||||
} else {
|
||||
/* Read the external database from the sd card. */
|
||||
R_TRY(ReadExternalBluetoothDatabase(out_count.GetPointer(), out.GetPointer(), out.GetSize()));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetSysMitmService::GetSettingsItemValueSize(sf::Out<u64> out_size, const settings::SettingsName &name, const settings::SettingsItemKey &key) {
|
||||
R_TRY_CATCH(settings::fwdbg::GetSdCardKeyValueStoreSettingsItemValueSize(out_size.GetPointer(), name.value, key.value)) {
|
||||
R_CATCH_RETHROW(sf::impl::ResultRequestContextChanged)
|
||||
R_CONVERT_ALL(sm::mitm::ResultShouldForwardToSession());
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetSysMitmService::GetSettingsItemValue(sf::Out<u64> out_size, const sf::OutBuffer &out, const settings::SettingsName &name, const settings::SettingsItemKey &key) {
|
||||
R_TRY_CATCH(settings::fwdbg::GetSdCardKeyValueStoreSettingsItemValue(out_size.GetPointer(), out.GetPointer(), out.GetSize(), name.value, key.value)) {
|
||||
R_CATCH_RETHROW(sf::impl::ResultRequestContextChanged)
|
||||
R_CONVERT_ALL(sm::mitm::ResultShouldForwardToSession());
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetSysMitmService::GetDebugModeFlag(sf::Out<bool> out) {
|
||||
/* If we're not processing for am, just return the real flag value. */
|
||||
R_UNLESS(m_client_info.program_id == ncm::SystemProgramId::Am, sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Retrieve the user configuration. */
|
||||
u8 en = 0;
|
||||
settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "enable_am_debug_mode");
|
||||
|
||||
out.SetValue(en != 0);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#define AMS_SETTINGS_SYSTEM_MITM_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 3, Result, GetFirmwareVersion, (sf::Out<ams::settings::FirmwareVersion> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, GetFirmwareVersion2, (sf::Out<ams::settings::FirmwareVersion> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 11, Result, SetBluetoothDevicesSettings, (const sf::InMapAliasArray<ams::settings::BluetoothDevicesSettings> &settings), (settings)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 12, Result, GetBluetoothDevicesSettings, (sf::Out<s32> out_count, const sf::OutMapAliasArray<ams::settings::BluetoothDevicesSettings> &out), (out_count, out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 37, Result, GetSettingsItemValueSize, (sf::Out<u64> out_size, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key), (out_size, name, key)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 38, Result, GetSettingsItemValue, (sf::Out<u64> out_size, const sf::OutBuffer &out, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key), (out_size, out, name, key)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 62, Result, GetDebugModeFlag, (sf::Out<bool> out), (out))
|
||||
|
||||
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::settings, ISetSysMitmInterface, AMS_SETTINGS_SYSTEM_MITM_INTERFACE_INFO, 0x0E82ED13)
|
||||
|
||||
namespace ams::mitm::settings {
|
||||
|
||||
class SetSysMitmService : public sf::MitmServiceImplBase {
|
||||
public:
|
||||
using MitmServiceImplBase::MitmServiceImplBase;
|
||||
public:
|
||||
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
|
||||
/* We will mitm:
|
||||
* - everything, because we want to intercept all settings requests.
|
||||
*/
|
||||
AMS_UNUSED(client_info);
|
||||
return true;
|
||||
}
|
||||
public:
|
||||
Result GetFirmwareVersion(sf::Out<ams::settings::FirmwareVersion> out);
|
||||
Result GetFirmwareVersion2(sf::Out<ams::settings::FirmwareVersion> out);
|
||||
Result SetBluetoothDevicesSettings(const sf::InMapAliasArray<ams::settings::BluetoothDevicesSettings> &settings);
|
||||
Result GetBluetoothDevicesSettings(sf::Out<s32> out_count, const sf::OutMapAliasArray<ams::settings::BluetoothDevicesSettings> &out);
|
||||
Result GetSettingsItemValueSize(sf::Out<u64> out_size, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key);
|
||||
Result GetSettingsItemValue(sf::Out<u64> out_size, const sf::OutBuffer &out, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key);
|
||||
Result GetDebugModeFlag(sf::Out<bool> out);
|
||||
};
|
||||
static_assert(IsISetSysMitmInterface<SetSysMitmService>);
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user