Delete pre-rewrite ams_mitm code

This commit is contained in:
Michael Scire 2019-12-07 01:36:36 -08:00 committed by SciresM
parent bbdc643b6d
commit 2bae1ad116
77 changed files with 0 additions and 8873 deletions

View file

@ -1,170 +0,0 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/libnx/switch_rules
AMSBRANCH := $(shell git symbolic-ref --short HEAD)
AMSREV := $(AMSBRANCH)-$(shell git rev-parse --short HEAD)
ifneq (, $(strip $(shell git status --porcelain 2>/dev/null)))
AMSREV := $(AMSREV)-dirty
endif
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
# EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm".
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source source/fs_mitm source/set_mitm source/bpc_mitm source/ns_mitm source/hid_mitm
DATA := data
INCLUDES := include ../../common/include
EXEFS_SRC := exefs_src
DEFINES := -DATMOSPHERE_GIT_BRANCH=\"$(AMSBRANCH)\" -DATMOSPHERE_GIT_REV=\"$(AMSREV)\"
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17
CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \
-Wl,--wrap,__cxa_throw \
-Wl,--wrap,__cxa_rethrow \
-Wl,--wrap,__cxa_allocate_exception \
-Wl,--wrap,__cxa_begin_catch \
-Wl,--wrap,__cxa_end_catch \
-Wl,--wrap,__cxa_call_unexpected \
-Wl,--wrap,__cxa_call_terminate \
-Wl,--wrap,__gxx_personality_v0
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) $(CXXWRAPS) -Wl,-Map,$(notdir $*.map)
LIBS := -lstratosphere -lnx
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(LIBNX) $(CURDIR)/../libstratosphere
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.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) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
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: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).kip $(TARGET).elf
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(OUTPUT).kip
$(OUTPUT).kip : $(OUTPUT).elf
$(OUTPUT).elf : $(OFILES)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View file

@ -1,79 +0,0 @@
{
"name": "ams.mitm",
"title_id": "0x010041544D530000",
"main_thread_stack_size": "0x20000",
"main_thread_priority": 43,
"default_cpu_id": 3,
"process_category": 1,
"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",
"svcCreateSession": "0x40",
"svcAcceptSession": "0x41",
"svcReplyAndReceiveLight": "0x42",
"svcReplyAndReceive": "0x43",
"svcReplyAndReceiveWithUserBuffer": "0x44",
"svcCreateEvent": "0x45",
"svcCreateInterruptEvent": "0x53",
"svcReadWriteRegister": "0x4E",
"svcQueryIoMapping": "0x55",
"svcCreateDeviceAddressSpace": "0x56",
"svcAttachDeviceAddressSpace": "0x57",
"svcDetachDeviceAddressSpace": "0x58",
"svcMapDeviceAddressSpaceAligned": "0x5a",
"svcUnmapDeviceAddressSpace": "0x5c",
"svcGetSystemInfo": "0x6f",
"svcManageNamedPort": "0x71",
"svcCallSecureMonitor": "0x7F"
}
}
]
}

View file

@ -1,108 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdlib>
#include <cstdint>
#include <cstring>
#include <malloc.h>
#include <switch.h>
#include <atmosphere.h>
#include <stratosphere.hpp>
#include "amsmitm_modules.hpp"
#include "utils.hpp"
extern "C" {
extern u32 __start__;
u32 __nx_applet_type = AppletType_None;
#define INNER_HEAP_SIZE 0x1000000
size_t nx_inner_heap_size = INNER_HEAP_SIZE;
char nx_inner_heap[INNER_HEAP_SIZE];
void __libnx_initheap(void);
void __appInit(void);
void __appExit(void);
/* Exception handling. */
alignas(16) u8 __nx_exception_stack[0x1000];
u64 __nx_exception_stack_size = sizeof(__nx_exception_stack);
void __libnx_exception_handler(ThreadExceptionDump *ctx);
void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx);
}
ams::ncm::ProgramId __stratosphere_program_id = ams::ncm::ProgramId::AtmosphereMitm;
void __libnx_exception_handler(ThreadExceptionDump *ctx) {
StratosphereCrashHandler(ctx);
}
void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx) {
/* We're bpc-mitm (or ams_mitm, anyway), so manually reboot to fatal error. */
Utils::RebootToFatalError(ctx);
}
void __libnx_initheap(void) {
void* addr = nx_inner_heap;
size_t size = nx_inner_heap_size;
/* Newlib */
extern char* fake_heap_start;
extern char* fake_heap_end;
fake_heap_start = (char*)addr;
fake_heap_end = (char*)addr + size;
}
void __appInit(void) {
SetFirmwareVersionForLibnx();
DoWithSmSession([&]() {
R_ASSERT(fsInitialize());
R_ASSERT(pmdmntInitialize());
R_ASSERT(pminfoInitialize());
R_ASSERT(splFsInitialize());
});
CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION);
}
void __appExit(void) {
/* Cleanup services. */
splFsExit();
pminfoExit();
pmdmntExit();
fsExit();
}
int main(int argc, char **argv)
{
consoleDebugInit(debugDevice_SVC);
ams::os::Thread initializer_thread;
LaunchAllMitmModules();
R_ASSERT(initializer_thread.Initialize(&Utils::InitializeThreadFunc, NULL, 0x4000, 0x15));
R_ASSERT(initializer_thread.Start());
/* Wait for all mitm modules to end. */
WaitAllMitmModules();
return 0;
}

View file

@ -1,62 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include <stratosphere.hpp>
#include <cstring>
#include "debug.hpp"
#include "amsmitm_modules.hpp"
#include "fs_mitm/fsmitm_main.hpp"
#include "set_mitm/setmitm_main.hpp"
#include "bpc_mitm/bpcmitm_main.hpp"
#include "ns_mitm/nsmitm_main.hpp"
#include "hid_mitm/hidmitm_main.hpp"
static ams::os::Thread g_module_threads[MitmModuleId_Count];
static const struct {
ThreadFunc main;
u32 priority;
u32 stack_size;
} g_module_definitions[MitmModuleId_Count] = {
{ &FsMitmMain, FsMitmPriority, FsMitmStackSize }, /* FsMitm */
{ &SetMitmMain, SetMitmPriority, SetMitmStackSize }, /* SetMitm */
{ &BpcMitmMain, BpcMitmPriority, BpcMitmStackSize }, /* BpcMitm */
{ &NsMitmMain, NsMitmPriority, NsMitmStackSize }, /* NsMitm */
{ &HidMitmMain, HidMitmPriority, HidMitmStackSize }, /* HidMitm */
};
void LaunchAllMitmModules() {
/* Create thread for each module. */
for (u32 i = 0; i < static_cast<u32>(MitmModuleId_Count); i++) {
const auto cur_module = &g_module_definitions[i];
R_ASSERT(g_module_threads[i].Initialize(cur_module->main, nullptr, cur_module->stack_size, cur_module->priority));
}
/* Start thread for each module. */
for (u32 i = 0; i < static_cast<u32>(MitmModuleId_Count); i++) {
R_ASSERT(g_module_threads[i].Start());
}
}
void WaitAllMitmModules() {
/* Wait on thread for each module. */
for (u32 i = 0; i < static_cast<u32>(MitmModuleId_Count); i++) {
g_module_threads[i].Join();
}
}

View file

@ -1,32 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
enum MitmModuleId : u32 {
MitmModuleId_FsMitm = 0,
MitmModuleId_SetMitm = 1,
MitmModuleId_BpcMitm = 2,
MitmModuleId_NsMitm = 3,
MitmModuleId_HidMitm = 4,
/* Always keep this at the end. */
MitmModuleId_Count,
};
void LaunchAllMitmModules();
void WaitAllMitmModules();

View file

@ -1,32 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mutex>
#include <switch.h>
#include <stratosphere.hpp>
#include "bpc_ams_service.hpp"
#include "bpcmitm_reboot_manager.hpp"
Result BpcAtmosphereService::RebootToFatalError(InBuffer<AtmosphereFatalErrorContext> ctx) {
if (ctx.buffer == nullptr || ctx.num_elements != 1) {
return ResultKernelConnectionClosed;
}
/* Reboot to fusee with the input context. */
BpcRebootManager::RebootForFatalError(ctx.buffer);
return ResultSuccess();
}

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "../utils.hpp"
class BpcAtmosphereService : public IServiceObject {
enum class CommandId {
RebootToFatalError = 65000,
};
private:
Result RebootToFatalError(InBuffer<AtmosphereFatalErrorContext> ctx);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(BpcAtmosphereService, RebootToFatalError),
};
};

View file

@ -1,35 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mutex>
#include <switch.h>
#include <stratosphere.hpp>
#include "bpc_mitm_service.hpp"
#include "bpcmitm_reboot_manager.hpp"
void BpcMitmService::PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx) {
/* Nothing to do here */
}
Result BpcMitmService::ShutdownSystem() {
/* Use exosphere + reboot to perform real shutdown, instead of fake shutdown. */
PerformShutdownSmc();
return ResultSuccess();
}
Result BpcMitmService::RebootSystem() {
return BpcRebootManager::PerformReboot();
}

View file

@ -1,54 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "../utils.hpp"
class BpcMitmService : public IMitmServiceObject {
private:
enum class CommandId {
ShutdownSystem = 0,
RebootSystem = 1,
};
public:
BpcMitmService(std::shared_ptr<Service> s, u64 pid, ams::ncm::ProgramId tid) : IMitmServiceObject(s, pid, tid) {
/* ... */
}
static bool ShouldMitm(u64 pid, ams::ncm::ProgramId tid) {
/* We will mitm:
* - am, to intercept the Reboot/Power buttons in the overlay menu.
* - fatal, to simplify payload reboot logic significantly
* - applications, to allow homebrew to take advantage of the feature.
*/
return tid == ams::ncm::ProgramId::Am || tid == ams::ncm::ProgramId::Fatal || ams::ncm::IsApplicationProgramId(tid) || Utils::IsHblTid(static_cast<u64>(tid));
}
static void PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx);
protected:
/* Overridden commands. */
Result ShutdownSystem();
Result RebootSystem();
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(BpcMitmService, ShutdownSystem),
MAKE_SERVICE_COMMAND_META(BpcMitmService, RebootSystem),
};
};

View file

@ -1,57 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdlib>
#include <cstdint>
#include <cstring>
#include <malloc.h>
#include <switch.h>
#include <atmosphere.h>
#include <stratosphere.hpp>
#include "bpcmitm_main.hpp"
#include "bpc_mitm_service.hpp"
#include "bpc_ams_service.hpp"
#include "bpcmitm_reboot_manager.hpp"
#include "../utils.hpp"
void BpcMitmMain(void *arg) {
/* Wait for initialization to occur */
Utils::WaitSdInitialized();
/* Load a payload off of the SD */
BpcRebootManager::Initialize();
/* Create server manager */
static auto s_server_manager = WaitableManager(2);
/* Create bpc mitm. */
const char *service_name = "bpc";
if (GetRuntimeFirmwareVersion() < FirmwareVersion_200) {
service_name = "bpc:c";
}
AddMitmServerToManager<BpcMitmService>(&s_server_manager, service_name, 13);
/* Extension: Allow for reboot-to-error. */
/* Must be managed port in order for sm to be able to access. */
s_server_manager.AddWaitable(new ManagedPortServer<BpcAtmosphereService>("bpc:ams", 1));
/* Loop forever, servicing our services. */
s_server_manager.Process();
}

View file

@ -1,24 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
constexpr u32 BpcMitmPriority = 32;
constexpr u32 BpcMitmStackSize = 0x8000;
void BpcMitmMain(void *arg);

View file

@ -1,122 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include <stratosphere.hpp>
#include <strings.h>
#include "bpcmitm_reboot_manager.hpp"
#include "../utils.hpp"
/* TODO: Find a way to pre-populate this with the contents of fusee-primary. */
static u8 g_reboot_payload[IRAM_PAYLOAD_MAX_SIZE] __attribute__ ((aligned (0x1000)));
static u8 g_work_page[0x1000] __attribute__ ((aligned (0x1000)));
static bool g_payload_loaded = false;
static BpcRebootType g_reboot_type = BpcRebootType::ToPayload;
void BpcRebootManager::Initialize() {
/* Open payload file. */
FsFile payload_file;
if (R_FAILED(Utils::OpenSdFile("/atmosphere/reboot_payload.bin", FS_OPEN_READ, &payload_file))) {
return;
}
ON_SCOPE_EXIT { fsFileClose(&payload_file); };
/* Clear payload buffer */
std::memset(g_reboot_payload, 0xFF, sizeof(g_reboot_payload));
/* Read payload file. */
size_t actual_size;
fsFileRead(&payload_file, 0, g_reboot_payload, IRAM_PAYLOAD_MAX_SIZE, FS_READOPTION_NONE, &actual_size);
g_payload_loaded = true;
/* Figure out what kind of reboot we're gonna be doing. */
{
char reboot_type[0x40] = {0};
u64 reboot_type_size = 0;
if (R_SUCCEEDED(Utils::GetSettingsItemValue("atmosphere", "power_menu_reboot_function", reboot_type, sizeof(reboot_type)-1, &reboot_type_size))) {
if (strcasecmp(reboot_type, "stock") == 0 || strcasecmp(reboot_type, "normal") == 0 || strcasecmp(reboot_type, "standard") == 0) {
g_reboot_type = BpcRebootType::Standard;
} else if (strcasecmp(reboot_type, "rcm") == 0) {
g_reboot_type = BpcRebootType::ToRcm;
} else if (strcasecmp(reboot_type, "payload") == 0) {
g_reboot_type = BpcRebootType::ToPayload;
}
}
}
}
static void ClearIram() {
/* Make page FFs. */
memset(g_work_page, 0xFF, sizeof(g_work_page));
/* Overwrite all of IRAM with FFs. */
for (size_t ofs = 0; ofs < IRAM_SIZE; ofs += sizeof(g_work_page)) {
CopyToIram(IRAM_BASE + ofs, g_work_page, sizeof(g_work_page));
}
}
static void DoRebootToPayload() {
/* If we don't actually have a payload loaded, just go to RCM. */
if (!g_payload_loaded) {
RebootToRcm();
}
/* Ensure clean IRAM state. */
ClearIram();
/* Copy in payload. */
for (size_t ofs = 0; ofs < sizeof(g_reboot_payload); ofs += 0x1000) {
CopyToIram(IRAM_PAYLOAD_BASE + ofs, &g_reboot_payload[ofs], 0x1000);
}
RebootToIramPayload();
}
Result BpcRebootManager::PerformReboot() {
switch (g_reboot_type) {
case BpcRebootType::Standard:
return ResultAtmosphereMitmShouldForwardToSession;
case BpcRebootType::ToRcm:
RebootToRcm();
return ResultSuccess();
case BpcRebootType::ToPayload:
default:
DoRebootToPayload();
return ResultSuccess();
}
}
void BpcRebootManager::RebootForFatalError(AtmosphereFatalErrorContext *ctx) {
/* Ensure clean IRAM state. */
ClearIram();
/* Copy in payload. */
for (size_t ofs = 0; ofs < sizeof(g_reboot_payload); ofs += 0x1000) {
CopyToIram(IRAM_PAYLOAD_BASE + ofs, &g_reboot_payload[ofs], 0x1000);
}
memcpy(g_work_page, ctx, sizeof(*ctx));
CopyToIram(IRAM_PAYLOAD_BASE + IRAM_PAYLOAD_MAX_SIZE, g_work_page, sizeof(g_work_page));
/* If we don't actually have a payload loaded, just go to RCM. */
if (!g_payload_loaded) {
RebootToRcm();
}
RebootToIramPayload();
}

View file

@ -1,37 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#define IRAM_BASE 0x40000000ull
#define IRAM_SIZE 0x40000
#define IRAM_PAYLOAD_MAX_SIZE 0x2E000
#define IRAM_PAYLOAD_BASE 0x40010000ull
enum class BpcRebootType : u32 {
Standard,
ToRcm,
ToPayload,
};
class BpcRebootManager {
public:
static void Initialize();
static Result PerformReboot();
static void RebootForFatalError(AtmosphereFatalErrorContext *ctx);
};

View file

@ -1,28 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include <stratosphere.hpp>
#include <cstring>
#include "debug.hpp"
void Reboot() {
/* ... */
}
void Log(const void *data, int size) {
/* ... */
}

View file

@ -1,20 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
void Reboot();
void Log(const void *data, int size);

View file

@ -1,117 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstring>
#include <cstdlib>
#include <switch.h>
#include "fs_dir_utils.hpp"
#include "fs_ifile.hpp"
Result FsDirUtils::CopyFile(IFileSystem *dst_fs, IFileSystem *src_fs, const FsPath &dst_parent_path, const FsPath &src_path, const FsDirectoryEntry *dir_ent, void *work_buf, size_t work_buf_size) {
std::unique_ptr<IFile> src_file;
std::unique_ptr<IFile> dst_file;
const u64 file_size = dir_ent->fileSize;
/* Open source file for reading. */
R_TRY(src_fs->OpenFile(src_file, src_path, OpenMode_Read));
/* Create and open destination file. */
{
FsPath dst_path;
/* TODO: Error code? N aborts here. */
AMS_ASSERT(static_cast<size_t>(snprintf(dst_path.str, sizeof(dst_path.str), "%s%s", dst_parent_path.str, dir_ent->name)) < sizeof(dst_path));
R_TRY(dst_fs->CreateFile(dst_path, file_size));
R_TRY(dst_fs->OpenFile(dst_file, dst_path, OpenMode_Write));
}
/* Read/Write work_buf_size chunks. */
u64 offset = 0;
while (offset < file_size) {
u64 read_size;
R_TRY(src_file->Read(&read_size, offset, work_buf, work_buf_size));
R_TRY(dst_file->Write(offset, work_buf, read_size));
offset += read_size;
}
return ResultSuccess();
}
Result FsDirUtils::CopyDirectoryRecursively(IFileSystem *dst_fs, IFileSystem *src_fs, const FsPath &dst_path, const FsPath &src_path, void *work_buf, size_t work_buf_size) {
FsPath work_path = dst_path;
return IterateDirectoryRecursively(src_fs, src_path,
[&](const FsPath &path, const FsDirectoryEntry *dir_ent) -> Result { /* On Enter Directory */
/* Update path, create new dir. */
strncat(work_path.str, dir_ent->name, sizeof(work_path) - strnlen(work_path.str, sizeof(work_path) - 1) - 1);
strncat(work_path.str, "/", sizeof(work_path) - strnlen(work_path.str, sizeof(work_path) - 1) - 1);
return dst_fs->CreateDirectory(work_path);
},
[&](const FsPath &path, const FsDirectoryEntry *dir_ent) -> Result { /* On Exit Directory */
/* Check we have a parent directory. */
const size_t work_path_len = strnlen(work_path.str, sizeof(work_path));
if (work_path_len < 2) {
return ResultFsInvalidPathFormat;
}
/* Find previous separator, add NULL terminator */
char *p = &work_path.str[work_path_len - 2];
while (*p != '/' && p > work_path.str) {
p--;
}
p[1] = 0;
return ResultSuccess();
},
[&](const FsPath &path, const FsDirectoryEntry *dir_ent) -> Result { /* On File */
/* Just copy the file to the new fs. */
return CopyFile(dst_fs, src_fs, work_path, path, dir_ent, work_buf, work_buf_size);
});
}
Result FsDirUtils::EnsureDirectoryExists(IFileSystem *fs, const FsPath &path) {
FsPath normal_path;
size_t normal_path_len;
/* Normalize the path. */
R_TRY(FsPathUtils::Normalize(normal_path.str, sizeof(normal_path.str) - 1, path.str, &normal_path_len));
/* Repeatedly call CreateDirectory on each directory leading to the target. */
for (size_t i = 1; i < normal_path_len; i++) {
/* If we detect a separator, we're done. */
if (normal_path.str[i] == '/') {
normal_path.str[i] = 0;
{
R_TRY_CATCH(fs->CreateDirectory(normal_path)) {
R_CATCH(ResultFsPathAlreadyExists) {
/* If path already exists, there's no problem. */
}
} R_END_TRY_CATCH;
}
normal_path.str[i] = '/';
}
}
/* Call CreateDirectory on the final path. */
R_TRY_CATCH(fs->CreateDirectory(normal_path)) {
R_CATCH(ResultFsPathAlreadyExists) {
/* If path already exists, there's no problem. */
}
} R_END_TRY_CATCH;
return ResultSuccess();
}

View file

@ -1,142 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include "fs_path_utils.hpp"
#include "fs_ifilesystem.hpp"
class FsDirUtils {
private:
template<typename OnEnterDir, typename OnExitDir, typename OnFile>
static Result IterateDirectoryRecursivelyInternal(IFileSystem *fs, FsPath &work_path, FsDirectoryEntry *ent_buf, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
std::unique_ptr<IDirectory> dir;
/* Open the directory. */
R_TRY(fs->OpenDirectory(dir, work_path, DirectoryOpenMode_All));
const size_t parent_len = strnlen(work_path.str, sizeof(work_path.str) - 1);
/* Read and handle entries. */
while (true) {
/* Read a single entry. */
u64 read_count;
R_TRY(dir->Read(&read_count, ent_buf, 1));
/* If we're out of entries, we're done. */
if (read_count == 0) {
break;
}
const size_t child_name_len = strnlen(ent_buf->name, sizeof(ent_buf->name) - 1);
const bool is_dir = ent_buf->type == ENTRYTYPE_DIR;
const size_t child_path_len = parent_len + child_name_len + (is_dir ? 1 : 0);
/* Validate child path size. */
if (child_path_len >= sizeof(work_path.str)) {
return ResultFsTooLongPath;
}
strncat(work_path.str, ent_buf->name, sizeof(work_path.str) - 1 - parent_len);
if (is_dir) {
/* Enter directory. */
R_TRY(on_enter_dir(work_path, ent_buf));
/* Append separator, recurse. */
strncat(work_path.str, "/", sizeof(work_path.str) - 1 - parent_len - child_name_len);
R_TRY(IterateDirectoryRecursivelyInternal(fs, work_path, ent_buf, on_enter_dir, on_exit_dir, on_file));
/* Exit directory. */
R_TRY(on_exit_dir(work_path, ent_buf));
} else {
/* Call file handler. */
R_TRY(on_file(work_path, ent_buf));
}
/* Restore parent path. */
work_path.str[parent_len] = 0;
}
return ResultSuccess();
}
public:
template<typename OnEnterDir, typename OnExitDir, typename OnFile>
static Result IterateDirectoryRecursively(IFileSystem *fs, const FsPath &root_path, FsPath &work_path, FsDirectoryEntry *ent_buf, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
/* Ensure valid root path. */
size_t root_path_len = strnlen(root_path.str, sizeof(root_path.str));
if (root_path_len > FS_MAX_PATH - 1 || ((root_path_len == FS_MAX_PATH - 1) && root_path.str[root_path_len-1] != '/')) {
return ResultFsTooLongPath;
}
/* Copy path, ensure terminating separator. */
memcpy(work_path.str, root_path.str, root_path_len);
if (work_path.str[root_path_len-1] != '/') {
root_path_len++;
work_path.str[root_path_len-1] = '/';
}
work_path.str[root_path_len] = 0;
/* Actually iterate. */
return IterateDirectoryRecursivelyInternal(fs, work_path, ent_buf, on_enter_dir, on_exit_dir, on_file);
}
/* Helper for not specifying work path/entry buffer. */
template<typename OnEnterDir, typename OnExitDir, typename OnFile>
static Result IterateDirectoryRecursively(IFileSystem *fs, const FsPath &root_path, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
FsDirectoryEntry dir_ent = {0};
FsPath work_path = {0};
return IterateDirectoryRecursively(fs, root_path, work_path, &dir_ent, on_enter_dir, on_exit_dir, on_file);
}
/* Helper for iterating over the filesystem root. */
template<typename OnEnterDir, typename OnExitDir, typename OnFile>
static Result IterateDirectoryRecursively(IFileSystem *fs, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
return IterateDirectoryRecursively(fs, FsPathUtils::RootPath, on_enter_dir, on_exit_dir, on_file);
}
/* Copy API. */
static Result CopyFile(IFileSystem *dst_fs, IFileSystem *src_fs, const FsPath &dst_parent_path, const FsPath &src_path, const FsDirectoryEntry *dir_ent, void *work_buf, size_t work_buf_size);
static Result CopyFile(IFileSystem *fs, const FsPath &dst_parent_path, const FsPath &src_path, const FsDirectoryEntry *dir_ent, void *work_buf, size_t work_buf_size) {
return CopyFile(fs, fs, dst_parent_path, src_path, dir_ent, work_buf, work_buf_size);
}
static Result CopyDirectoryRecursively(IFileSystem *dst_fs, IFileSystem *src_fs, const FsPath &dst_path, const FsPath &src_path, void *work_buf, size_t work_buf_size);
static Result CopyDirectoryRecursively(IFileSystem *fs, const FsPath &dst_path, const FsPath &src_path, void *work_buf, size_t work_buf_size) {
return CopyDirectoryRecursively(fs, fs, dst_path, src_path, work_buf, work_buf_size);
}
/* Ensure directory existence. */
static Result EnsureDirectoryExists(IFileSystem *fs, const FsPath &path);
/* Other Utility. */
template<typename F>
static Result RetryUntilTargetNotLocked(F f) {
const size_t MaxRetries = 10;
for (size_t i = 0; i < MaxRetries; i++) {
R_TRY_CATCH(f()) {
R_CATCH(ResultFsTargetLocked) {
/* If target is locked, wait 100ms and try again. */
svcSleepThread(100'000'000ul);
}
} R_END_TRY_CATCH;
}
return ResultSuccess();
}
};

View file

@ -1,182 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstring>
#include <switch.h>
#include <stratosphere.hpp>
#include "../utils.hpp"
#include "fs_directory_redirection_filesystem.hpp"
#include "fs_path_utils.hpp"
static char *GetNormalizedDirectory(const char *dir_prefix) {
/* Normalize the path. */
char normal_path[FS_MAX_PATH + 1];
size_t normal_path_len;
R_ASSERT(FsPathUtils::Normalize(normal_path, sizeof(normal_path), dir_prefix, &normal_path_len));
/* Ensure terminating '/' */
if (normal_path[normal_path_len-1] != '/') {
AMS_ASSERT(normal_path_len + 2 <= sizeof(normal_path));
strncat(normal_path, "/", 2);
normal_path[sizeof(normal_path)-1] = 0;
normal_path_len++;
}
char *output = static_cast<char *>(std::malloc(normal_path_len + 1));
AMS_ASSERT(output != nullptr);
std::strncpy(output, normal_path, normal_path_len + 1);
output[normal_path_len] = 0;
return output;
}
Result DirectoryRedirectionFileSystem::Initialize(const char *before, const char *after) {
if (strnlen(before, FS_MAX_PATH) >= FS_MAX_PATH || strnlen(after, FS_MAX_PATH) >= FS_MAX_PATH) {
return ResultFsTooLongPath;
}
this->before_dir = GetNormalizedDirectory(before);
this->after_dir = GetNormalizedDirectory(after);
this->before_dir_len = strlen(this->before_dir);
this->after_dir_len = strlen(this->after_dir);
return ResultSuccess();
}
Result DirectoryRedirectionFileSystem::GetFullPath(char *out, size_t out_size, const char *relative_path) {
FsPath tmp_rel_path;
R_TRY(FsPathUtils::Normalize(tmp_rel_path.str, sizeof(tmp_rel_path), relative_path, nullptr));
if (std::strncmp(tmp_rel_path.str, this->before_dir, this->before_dir_len) == 0) {
if (this->after_dir_len + strnlen(tmp_rel_path.str, FS_MAX_PATH) - this->before_dir_len > out_size) {
return ResultFsTooLongPath;
}
/* Copy after path. */
std::strncpy(out, this->after_dir, out_size);
out[out_size - 1] = 0;
/* Normalize it. */
return FsPathUtils::Normalize(out + this->after_dir_len - 1, out_size - (this->after_dir_len - 1), relative_path + this->before_dir_len - 1, nullptr);
} else if (std::memcmp(tmp_rel_path.str, this->before_dir, this->before_dir_len - 1) == 0) {
/* Handling raw directory. */
if (this->after_dir_len + 1 > out_size) {
return ResultFsTooLongPath;
}
std::strncpy(out, this->after_dir, out_size);
out[out_size - 1] = 0;
return ResultSuccess();
} else {
return FsPathUtils::Normalize(out, out_size, relative_path, nullptr);
}
}
Result DirectoryRedirectionFileSystem::CreateFileImpl(const FsPath &path, uint64_t size, int flags) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->CreateFile(full_path, size, flags);
}
Result DirectoryRedirectionFileSystem::DeleteFileImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->DeleteFile(full_path);
}
Result DirectoryRedirectionFileSystem::CreateDirectoryImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->CreateDirectory(full_path);
}
Result DirectoryRedirectionFileSystem::DeleteDirectoryImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->DeleteDirectory(full_path);
}
Result DirectoryRedirectionFileSystem::DeleteDirectoryRecursivelyImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->DeleteDirectoryRecursively(full_path);
}
Result DirectoryRedirectionFileSystem::RenameFileImpl(const FsPath &old_path, const FsPath &new_path) {
FsPath full_old_path, full_new_path;
R_TRY(GetFullPath(full_old_path, old_path));
R_TRY(GetFullPath(full_new_path, new_path));
return this->base_fs->RenameFile(full_old_path, full_new_path);
}
Result DirectoryRedirectionFileSystem::RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) {
FsPath full_old_path, full_new_path;
R_TRY(GetFullPath(full_old_path, old_path));
R_TRY(GetFullPath(full_new_path, new_path));
return this->base_fs->RenameDirectory(full_old_path, full_new_path);
}
Result DirectoryRedirectionFileSystem::GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->GetEntryType(out, full_path);
}
Result DirectoryRedirectionFileSystem::OpenFileImpl(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->OpenFile(out_file, full_path, mode);
}
Result DirectoryRedirectionFileSystem::OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->OpenDirectory(out_dir, full_path, mode);
}
Result DirectoryRedirectionFileSystem::CommitImpl() {
return this->base_fs->Commit();
}
Result DirectoryRedirectionFileSystem::GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->GetFreeSpaceSize(out, full_path);
}
Result DirectoryRedirectionFileSystem::GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->GetTotalSpaceSize(out, full_path);
}
Result DirectoryRedirectionFileSystem::CleanDirectoryRecursivelyImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->CleanDirectoryRecursively(full_path);
}
Result DirectoryRedirectionFileSystem::GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->GetFileTimeStampRaw(out, full_path);
}
Result DirectoryRedirectionFileSystem::QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->QueryEntry(out, out_size, in, in_size, query, full_path);
}

View file

@ -1,76 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "fs_ifilesystem.hpp"
#include "fs_path_utils.hpp"
class DirectoryRedirectionFileSystem : public IFileSystem {
private:
std::shared_ptr<IFileSystem> base_fs;
char *before_dir = nullptr;
size_t before_dir_len = 0;
char *after_dir = nullptr;
size_t after_dir_len = 0;
public:
DirectoryRedirectionFileSystem(IFileSystem *fs, const char *before, const char *after) : base_fs(fs) {
R_ASSERT(this->Initialize(before, after));
}
DirectoryRedirectionFileSystem(std::shared_ptr<IFileSystem> fs, const char *before, const char *after) : base_fs(fs) {
R_ASSERT(this->Initialize(before, after));
}
virtual ~DirectoryRedirectionFileSystem() {
if (this->before_dir != nullptr) {
free(this->before_dir);
}
if (this->after_dir != nullptr) {
free(this->after_dir);
}
}
private:
Result Initialize(const char *before, const char *after);
protected:
Result GetFullPath(char *out, size_t out_size, const char *relative_path);
Result GetFullPath(FsPath &full_path, const FsPath &relative_path) {
return GetFullPath(full_path.str, sizeof(full_path.str), relative_path.str);
}
public:
virtual Result CreateFileImpl(const FsPath &path, uint64_t size, int flags) override;
virtual Result DeleteFileImpl(const FsPath &path) override;
virtual Result CreateDirectoryImpl(const FsPath &path) override;
virtual Result DeleteDirectoryImpl(const FsPath &path) override;
virtual Result DeleteDirectoryRecursivelyImpl(const FsPath &path) override;
virtual Result RenameFileImpl(const FsPath &old_path, const FsPath &new_path) override;
virtual Result RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) override;
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) override;
virtual Result OpenFileImpl(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) override;
virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) override;
virtual Result CommitImpl() override;
virtual Result GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) override;
virtual Result GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) override;
virtual Result CleanDirectoryRecursivelyImpl(const FsPath &path) override;
virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) override;
virtual Result QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) override;
};

View file

@ -1,335 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstring>
#include <switch.h>
#include <stratosphere.hpp>
#include "../utils.hpp"
#include "fs_directory_savedata_filesystem.hpp"
#include "fs_dir_utils.hpp"
class DirectorySaveDataFile : public IFile {
private:
std::unique_ptr<IFile> base_file;
DirectorySaveDataFileSystem *parent_fs; /* TODO: shared_ptr + enabled_shared_from_this? */
int open_mode;
public:
DirectorySaveDataFile(std::unique_ptr<IFile> f, DirectorySaveDataFileSystem *p, int m) : base_file(std::move(f)), parent_fs(p), open_mode(m) {
/* ... */
}
virtual ~DirectorySaveDataFile() {
/* Observe closing of writable file. */
if (this->open_mode & OpenMode_Write) {
this->parent_fs->OnWritableFileClose();
}
}
public:
virtual Result ReadImpl(u64 *out, u64 offset, void *buffer, u64 size) override {
return this->base_file->Read(out, offset, buffer, size);
}
virtual Result GetSizeImpl(u64 *out) override {
return this->base_file->GetSize(out);
}
virtual Result FlushImpl() override {
return this->base_file->Flush();
}
virtual Result WriteImpl(u64 offset, void *buffer, u64 size, u32 option) override {
return this->base_file->Write(offset, buffer, size, option);
}
virtual Result SetSizeImpl(u64 size) override {
return this->base_file->SetSize(size);
}
virtual Result OperateRangeImpl(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
return this->base_file->OperateRange(operation_type, offset, size, out_range_info);
}
};
/* ================================================================================================ */
Result DirectorySaveDataFileSystem::Initialize() {
DirectoryEntryType ent_type;
/* Check that the working directory exists. */
R_TRY_CATCH(this->fs->GetEntryType(&ent_type, WorkingDirectoryPath)) {
/* If path isn't found, create working directory and committed directory. */
R_CATCH(ResultFsPathNotFound) {
R_TRY(this->fs->CreateDirectory(WorkingDirectoryPath));
R_TRY(this->fs->CreateDirectory(CommittedDirectoryPath));
}
} R_END_TRY_CATCH;
/* Now check for the committed directory. */
R_TRY_CATCH(this->fs->GetEntryType(&ent_type, CommittedDirectoryPath)) {
/* Committed doesn't exist, so synchronize and rename. */
R_CATCH(ResultFsPathNotFound) {
R_TRY(this->SynchronizeDirectory(SynchronizingDirectoryPath, WorkingDirectoryPath));
return this->fs->RenameDirectory(SynchronizingDirectoryPath, CommittedDirectoryPath);
}
} R_END_TRY_CATCH;
/* If committed exists, synchronize it to the working directory. */
return this->SynchronizeDirectory(WorkingDirectoryPath, CommittedDirectoryPath);
}
Result DirectorySaveDataFileSystem::SynchronizeDirectory(const FsPath &dst_dir, const FsPath &src_dir) {
/* Delete destination dir and recreate it. */
R_TRY_CATCH(this->fs->DeleteDirectoryRecursively(dst_dir)) {
R_CATCH(ResultFsPathNotFound) {
/* Nintendo returns error unconditionally, but I think that's a bug in their code. */
}
} R_END_TRY_CATCH;
R_TRY(this->fs->CreateDirectory(dst_dir));
/* Get a buffer to work with. */
void *work_buf = nullptr;
size_t work_buf_size = 0;
R_TRY(this->AllocateWorkBuffer(&work_buf, &work_buf_size, IdealWorkBuffersize));
ON_SCOPE_EXIT { free(work_buf); };
return FsDirUtils::CopyDirectoryRecursively(this->fs, dst_dir, src_dir, work_buf, work_buf_size);
}
Result DirectorySaveDataFileSystem::AllocateWorkBuffer(void **out_buf, size_t *out_size, const size_t ideal_size) {
size_t try_size = ideal_size;
/* Repeatedly try to allocate until success. */
while (true) {
void *buf = malloc(try_size);
if (buf != nullptr) {
*out_buf = buf;
*out_size = try_size;
return ResultSuccess();
}
/* Divide size by two. */
try_size >>= 1;
AMS_ASSERT(try_size > 0x200);
}
/* TODO: Return a result here? Nintendo does not, but they have other allocation failed results. */
/* Consider returning ResultFsAllocationFailureInDirectorySaveDataFileSystem? */
}
Result DirectorySaveDataFileSystem::GetFullPath(char *out, size_t out_size, const char *relative_path) {
/* Validate path. */
if (1 + strnlen(relative_path, FS_MAX_PATH) >= out_size) {
return ResultFsTooLongPath;
}
if (relative_path[0] != '/') {
return ResultFsInvalidPath;
}
/* Copy working directory path. */
std::strncpy(out, WorkingDirectoryPath.str, out_size);
out[out_size-1] = 0;
/* Normalize it. */
constexpr size_t working_len = WorkingDirectoryPathLen - 1;
return FsPathUtils::Normalize(out + working_len, out_size - working_len, relative_path, nullptr);
}
void DirectorySaveDataFileSystem::OnWritableFileClose() {
std::scoped_lock lk(this->lock);
this->open_writable_files--;
/* TODO: Abort if < 0? N does not. */
}
Result DirectorySaveDataFileSystem::CopySaveFromProxy() {
if (this->proxy_save_fs != nullptr) {
/* Get a buffer to work with. */
void *work_buf = nullptr;
size_t work_buf_size = 0;
R_TRY(this->AllocateWorkBuffer(&work_buf, &work_buf_size, IdealWorkBuffersize));
ON_SCOPE_EXIT { free(work_buf); };
R_TRY(FsDirUtils::CopyDirectoryRecursively(this, this->proxy_save_fs.get(), FsPathUtils::RootPath, FsPathUtils::RootPath, work_buf, work_buf_size));
return this->Commit();
}
return ResultSuccess();
}
/* ================================================================================================ */
Result DirectorySaveDataFileSystem::CreateFileImpl(const FsPath &path, uint64_t size, int flags) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
std::scoped_lock lk(this->lock);
return this->fs->CreateFile(full_path, size, flags);
}
Result DirectorySaveDataFileSystem::DeleteFileImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
std::scoped_lock lk(this->lock);
return this->fs->DeleteFile(full_path);
}
Result DirectorySaveDataFileSystem::CreateDirectoryImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
std::scoped_lock lk(this->lock);
return this->fs->CreateDirectory(full_path);
}
Result DirectorySaveDataFileSystem::DeleteDirectoryImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
std::scoped_lock lk(this->lock);
return this->fs->DeleteDirectory(full_path);
}
Result DirectorySaveDataFileSystem::DeleteDirectoryRecursivelyImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
std::scoped_lock lk(this->lock);
return this->fs->DeleteDirectoryRecursively(full_path);
}
Result DirectorySaveDataFileSystem::RenameFileImpl(const FsPath &old_path, const FsPath &new_path) {
FsPath full_old_path, full_new_path;
R_TRY(GetFullPath(full_old_path, old_path));
R_TRY(GetFullPath(full_new_path, new_path));
std::scoped_lock lk(this->lock);
return this->fs->RenameFile(full_old_path, full_new_path);
}
Result DirectorySaveDataFileSystem::RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) {
FsPath full_old_path, full_new_path;
R_TRY(GetFullPath(full_old_path, old_path));
R_TRY(GetFullPath(full_new_path, new_path));
std::scoped_lock lk(this->lock);
return this->fs->RenameDirectory(full_old_path, full_new_path);
}
Result DirectorySaveDataFileSystem::GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
std::scoped_lock lk(this->lock);
return this->fs->GetEntryType(out, full_path);
}
Result DirectorySaveDataFileSystem::OpenFileImpl(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
std::scoped_lock lk(this->lock);
{
/* Open the raw file. */
std::unique_ptr<IFile> file;
R_TRY(this->fs->OpenFile(file, full_path, mode));
/* Create DirectorySaveDataFile wrapper. */
out_file = std::make_unique<DirectorySaveDataFile>(std::move(file), this, mode);
}
/* Check for allocation failure. */
if (out_file == nullptr) {
return ResultFsAllocationFailureInDirectorySaveDataFileSystem;
}
/* Increment open writable files, if needed. */
if (mode & OpenMode_Write) {
this->open_writable_files++;
}
return ResultSuccess();
}
Result DirectorySaveDataFileSystem::OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
std::scoped_lock lk(this->lock);
return this->fs->OpenDirectory(out_dir, full_path, mode);
}
Result DirectorySaveDataFileSystem::CommitImpl() {
/* Here, Nintendo does the following (with retries): */
/* - Rename Committed -> Synchronizing. */
/* - Synchronize Working -> Synchronizing (deleting Synchronizing). */
/* - Rename Synchronizing -> Committed. */
/* I think this is not the optimal order to do things, as the previous committed directory */
/* will be deleted if there is an error during synchronization. */
/* Instead, we will synchronize first, then delete committed, then rename. */
std::scoped_lock lk(this->lock);
/* Ensure we don't have any open writable files. */
if (this->open_writable_files != 0) {
return ResultFsPreconditionViolation;
}
const auto SynchronizeWorkingDir = [&]() { return this->SynchronizeDirectory(SynchronizingDirectoryPath, WorkingDirectoryPath); };
const auto DeleteCommittedDir = [&]() { return this->fs->DeleteDirectoryRecursively(CommittedDirectoryPath); };
const auto RenameSynchDir = [&]() { return this->fs->RenameDirectory(SynchronizingDirectoryPath, CommittedDirectoryPath); };
/* Synchronize working directory. */
R_TRY(FsDirUtils::RetryUntilTargetNotLocked(std::move(SynchronizeWorkingDir)));
/* Delete committed directory. */
R_TRY_CATCH(FsDirUtils::RetryUntilTargetNotLocked(std::move(DeleteCommittedDir))) {
R_CATCH(ResultFsPathNotFound) {
/* It is okay for us to not have a committed directory here. */
}
} R_END_TRY_CATCH;
/* Rename synchronizing directory to committed directory. */
R_TRY(FsDirUtils::RetryUntilTargetNotLocked(std::move(RenameSynchDir)));
/* TODO: Should I call this->fs->Commit()? Nintendo does not. */
return ResultSuccess();
}
Result DirectorySaveDataFileSystem::GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) {
/* TODO: How should this work? N returns ResultFsNotImplemented. */
return ResultFsNotImplemented;
}
Result DirectorySaveDataFileSystem::GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) {
/* TODO: How should this work? N returns ResultFsNotImplemented. */
return ResultFsNotImplemented;
}
Result DirectorySaveDataFileSystem::CleanDirectoryRecursivelyImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
std::scoped_lock lk(this->lock);
return this->fs->CleanDirectoryRecursively(full_path);
}
Result DirectorySaveDataFileSystem::GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) {
/* TODO: How should this work? N returns ResultFsNotImplemented. */
return ResultFsNotImplemented;
}
Result DirectorySaveDataFileSystem::QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) {
/* TODO: How should this work? N returns ResultFsNotImplemented. */
return ResultFsNotImplemented;
}

View file

@ -1,91 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "fs_ifilesystem.hpp"
#include "fs_path_utils.hpp"
class DirectorySaveDataFileSystem : public IFileSystem {
private:
static constexpr FsPath CommittedDirectoryPath = EncodeConstantFsPath("/0/");
static constexpr FsPath WorkingDirectoryPath = EncodeConstantFsPath("/1/");
static constexpr FsPath SynchronizingDirectoryPath = EncodeConstantFsPath("/_/");
static constexpr size_t CommittedDirectoryPathLen = GetConstantFsPathLen(CommittedDirectoryPath);
static constexpr size_t WorkingDirectoryPathLen = GetConstantFsPathLen(WorkingDirectoryPath);
static constexpr size_t SynchronizingDirectoryPathLen = GetConstantFsPathLen(SynchronizingDirectoryPath);
static constexpr size_t IdealWorkBuffersize = 0x100000; /* 1 MB */
private:
std::shared_ptr<IFileSystem> shared_fs;
std::unique_ptr<IFileSystem> unique_fs;
std::unique_ptr<IFileSystem> proxy_save_fs;
IFileSystem *fs;
ams::os::Mutex lock;
size_t open_writable_files = 0;
public:
DirectorySaveDataFileSystem(IFileSystem *fs, std::unique_ptr<IFileSystem> pfs) : unique_fs(fs), proxy_save_fs(std::move(pfs)) {
this->fs = this->unique_fs.get();
R_ASSERT(this->Initialize());
}
DirectorySaveDataFileSystem(std::unique_ptr<IFileSystem> fs, std::unique_ptr<IFileSystem> pfs) : unique_fs(std::move(fs)), proxy_save_fs(std::move(pfs)) {
this->fs = this->unique_fs.get();
R_ASSERT(this->Initialize());
}
DirectorySaveDataFileSystem(std::shared_ptr<IFileSystem> fs, std::unique_ptr<IFileSystem> pfs) : shared_fs(fs), proxy_save_fs(std::move(pfs)) {
this->fs = this->shared_fs.get();
R_ASSERT(this->Initialize());
}
virtual ~DirectorySaveDataFileSystem() { }
private:
Result Initialize();
protected:
Result SynchronizeDirectory(const FsPath &dst_dir, const FsPath &src_dir);
Result AllocateWorkBuffer(void **out_buf, size_t *out_size, const size_t ideal_size);
Result GetFullPath(char *out, size_t out_size, const char *relative_path);
Result GetFullPath(FsPath &full_path, const FsPath &relative_path) {
return GetFullPath(full_path.str, sizeof(full_path.str), relative_path.str);
}
public:
void OnWritableFileClose();
Result CopySaveFromProxy();
public:
virtual Result CreateFileImpl(const FsPath &path, uint64_t size, int flags) override;
virtual Result DeleteFileImpl(const FsPath &path) override;
virtual Result CreateDirectoryImpl(const FsPath &path) override;
virtual Result DeleteDirectoryImpl(const FsPath &path) override;
virtual Result DeleteDirectoryRecursivelyImpl(const FsPath &path) override;
virtual Result RenameFileImpl(const FsPath &old_path, const FsPath &new_path) override;
virtual Result RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) override;
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) override;
virtual Result OpenFileImpl(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) override;
virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) override;
virtual Result CommitImpl() override;
virtual Result GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) override;
virtual Result GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) override;
virtual Result CleanDirectoryRecursivelyImpl(const FsPath &path) override;
virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) override;
virtual Result QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) override;
};

View file

@ -1,103 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstring>
#include <switch.h>
#include <stratosphere.hpp>
#include "../utils.hpp"
#include "fs_file_storage.hpp"
Result FileStorage::UpdateSize() {
if (this->size == InvalidSize) {
return this->file->GetSize(&this->size);
}
return ResultSuccess();
}
Result FileStorage::Read(void *buffer, size_t size, u64 offset) {
u64 read_size;
if (size == 0) {
return ResultSuccess();
}
if (buffer == nullptr) {
return ResultFsNullptrArgument;
}
R_TRY(this->UpdateSize());
if (!IStorage::IsRangeValid(offset, size, this->size)) {
return ResultFsOutOfRange;
}
/* Nintendo doesn't do check output read size, but we will for safety. */
R_TRY(this->file->Read(&read_size, offset, buffer, size));
if (read_size != size && read_size) {
return this->Read(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(buffer) + read_size), size - read_size, offset + read_size);
}
return ResultSuccess();
}
Result FileStorage::Write(void *buffer, size_t size, u64 offset) {
if (size == 0) {
return ResultSuccess();
}
if (buffer == nullptr) {
return ResultFsNullptrArgument;
}
R_TRY(this->UpdateSize());
if (!IStorage::IsRangeValid(offset, size, this->size)) {
return ResultFsOutOfRange;
}
return this->file->Write(offset, buffer, size);
}
Result FileStorage::Flush() {
return this->file->Flush();
}
Result FileStorage::GetSize(u64 *out_size) {
R_TRY(this->UpdateSize());
*out_size = this->size;
return ResultSuccess();
}
Result FileStorage::SetSize(u64 size) {
this->size = InvalidSize;
return this->file->SetSize(size);
}
Result FileStorage::OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) {
switch (operation_type) {
case FsOperationId_InvalidateCache:
case FsOperationId_QueryRange:
if (size == 0) {
if (operation_type == FsOperationId_QueryRange) {
if (out_range_info == nullptr) {
return ResultFsNullptrArgument;
}
/* N checks for size == sizeof(*out_range_info) here, but that's because their wrapper api is bad. */
std::memset(out_range_info, 0, sizeof(*out_range_info));
}
return ResultSuccess();
}
R_TRY(this->UpdateSize());
/* N checks for positivity + signed overflow on offset/size here, but we're using unsigned types... */
return this->file->OperateRange(operation_type, offset, size, out_range_info);
default:
return ResultFsUnsupportedOperation;
}
}

View file

@ -1,54 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "fs_shim.h"
#include "../debug.hpp"
#include "fs_istorage.hpp"
#include "fs_ifile.hpp"
class FileStorage : public IStorage {
public:
static constexpr u64 InvalidSize = UINT64_MAX;
private:
std::shared_ptr<IFile> base_file;
IFile *file;
u64 size;
public:
FileStorage(IFile *f) : base_file(f) {
this->file = this->base_file.get();
this->size = InvalidSize;
};
FileStorage(std::shared_ptr<IFile> f) : base_file(f) {
this->file = this->base_file.get();
this->size = InvalidSize;
};
virtual ~FileStorage() {
/* ... */
};
protected:
Result UpdateSize();
public:
virtual Result Read(void *buffer, size_t size, u64 offset) override;
virtual Result Write(void *buffer, size_t size, u64 offset) override;
virtual Result Flush() override;
virtual Result GetSize(u64 *out_size) override;
virtual Result SetSize(u64 size) override;
virtual Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override;
};

View file

@ -1,38 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
enum OpenMode {
OpenMode_Read = (1 << 0),
OpenMode_Write = (1 << 1),
OpenMode_Append = (1 << 2),
OpenMode_ReadWrite = OpenMode_Read | OpenMode_Write,
OpenMode_All = OpenMode_ReadWrite | OpenMode_Append,
};
enum DirectoryOpenMode {
DirectoryOpenMode_Directories = (1 << 0),
DirectoryOpenMode_Files = (1 << 1),
DirectoryOpenMode_All = (DirectoryOpenMode_Directories | DirectoryOpenMode_Files),
};
enum DirectoryEntryType {
DirectoryEntryType_Directory,
DirectoryEntryType_File,
};

View file

@ -1,115 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
class IDirectory {
public:
virtual ~IDirectory() {}
Result Read(uint64_t *out_count, FsDirectoryEntry *out_entries, uint64_t max_entries) {
if (out_count == nullptr) {
return ResultFsNullptrArgument;
}
if (max_entries == 0) {
*out_count = 0;
return ResultSuccess();
}
if (out_entries == nullptr) {
return ResultFsNullptrArgument;
}
return ReadImpl(out_count, out_entries, max_entries);
}
Result GetEntryCount(uint64_t *count) {
if (count == nullptr) {
return ResultFsNullptrArgument;
}
return GetEntryCountImpl(count);
}
protected:
/* ...? */
private:
virtual Result ReadImpl(uint64_t *out_count, FsDirectoryEntry *out_entries, uint64_t max_entries) = 0;
virtual Result GetEntryCountImpl(uint64_t *count) = 0;
};
class IDirectoryInterface : public IServiceObject {
private:
enum class CommandId {
Read = 0,
GetEntryCount = 1,
};
private:
std::unique_ptr<IDirectory> base_dir;
public:
IDirectoryInterface(IDirectory *d) : base_dir(d) {
/* ... */
};
IDirectoryInterface(std::unique_ptr<IDirectory> d) : base_dir(std::move(d)) {
/* ... */
};
private:
/* Actual command API. */
virtual Result Read(OutBuffer<FsDirectoryEntry> buffer, Out<u64> out_count) final {
return this->base_dir->Read(out_count.GetPointer(), buffer.buffer, buffer.num_elements);
};
virtual Result GetEntryCount(Out<u64> out_count) final {
return this->base_dir->GetEntryCount(out_count.GetPointer());
};
public:
DEFINE_SERVICE_DISPATCH_TABLE {
/* 1.0.0- */
MAKE_SERVICE_COMMAND_META(IDirectoryInterface, Read),
MAKE_SERVICE_COMMAND_META(IDirectoryInterface, GetEntryCount),
};
};
class ProxyDirectory : public IDirectory {
private:
std::unique_ptr<FsDir> base_dir;
public:
ProxyDirectory(FsDir *d) : base_dir(d) {
/* ... */
}
ProxyDirectory(std::unique_ptr<FsDir> d) : base_dir(std::move(d)) {
/* ... */
}
ProxyDirectory(FsDir d) {
this->base_dir = std::make_unique<FsDir>(d);
}
virtual ~ProxyDirectory() {
fsDirClose(this->base_dir.get());
}
public:
virtual Result ReadImpl(uint64_t *out_count, FsDirectoryEntry *out_entries, uint64_t max_entries) {
size_t count;
R_TRY(fsDirRead(this->base_dir.get(), 0, &count, max_entries, out_entries));
*out_count = count;
return ResultSuccess();
}
virtual Result GetEntryCountImpl(uint64_t *count) {
return fsDirGetEntryCount(this->base_dir.get(), count);
}
};

View file

@ -1,195 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "fs_shim.h"
class IFile {
public:
virtual ~IFile() {}
Result Read(uint64_t *out, uint64_t offset, void *buffer, uint64_t size, uint32_t flags) {
(void)(flags);
if (out == nullptr) {
return ResultFsNullptrArgument;
}
if (size == 0) {
*out = 0;
return ResultSuccess();
}
if (buffer == nullptr) {
return ResultFsNullptrArgument;
}
return ReadImpl(out, offset, buffer, size);
}
Result Read(uint64_t *out, uint64_t offset, void *buffer, uint64_t size) {
return Read(out, offset, buffer, size, 0);
}
Result GetSize(uint64_t *out) {
if (out == nullptr) {
return ResultFsNullptrArgument;
}
return GetSizeImpl(out);
}
Result Flush() {
return FlushImpl();
}
Result Write(uint64_t offset, void *buffer, uint64_t size, uint32_t flags) {
if (size == 0) {
return ResultSuccess();
}
if (buffer == nullptr) {
return ResultFsNullptrArgument;
}
return WriteImpl(offset, buffer, size, flags);
}
Result Write(uint64_t offset, void *buffer, uint64_t size, bool flush = false) {
if (size == 0) {
return ResultSuccess();
}
if (buffer == nullptr) {
return ResultFsNullptrArgument;
}
return WriteImpl(offset, buffer, size, flush);
}
Result SetSize(uint64_t size) {
return SetSizeImpl(size);
}
Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) {
if (operation_type == FsOperationId_QueryRange) {
return OperateRangeImpl(operation_type, offset, size, out_range_info);
}
return ResultFsUnsupportedOperation;
}
protected:
/* ...? */
private:
virtual Result ReadImpl(u64 *out, u64 offset, void *buffer, u64 size) = 0;
virtual Result GetSizeImpl(u64 *out) = 0;
virtual Result FlushImpl() = 0;
virtual Result WriteImpl(u64 offset, void *buffer, u64 size, u32 option) = 0;
virtual Result SetSizeImpl(u64 size) = 0;
virtual Result OperateRangeImpl(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0;
};
class IFileInterface : public IServiceObject {
private:
enum class CommandId {
Read = 0,
Write = 1,
Flush = 2,
SetSize = 3,
GetSize = 4,
OperateRange = 5,
};
private:
std::unique_ptr<IFile> base_file;
public:
IFileInterface(IFile *f) : base_file(f) {
/* ... */
};
IFileInterface(std::unique_ptr<IFile> f) : base_file(std::move(f)) {
/* ... */
};
private:
/* Actual command API. */
virtual Result Read(OutBuffer<u8, BufferType_Type1> buffer, Out<u64> out_read, u64 offset, u64 size, u32 read_flags) final {
return this->base_file->Read(out_read.GetPointer(), offset, buffer.buffer, std::min(buffer.num_elements, size), read_flags);
};
virtual Result Write(InBuffer<u8, BufferType_Type1> buffer, u64 offset, u64 size, u32 write_flags) final {
return this->base_file->Write(offset, buffer.buffer, std::min(buffer.num_elements, size), write_flags);
};
virtual Result Flush() final {
return this->base_file->Flush();
};
virtual Result SetSize(u64 size) final {
return this->base_file->SetSize(size);
};
virtual Result GetSize(Out<u64> size) final {
return this->base_file->GetSize(size.GetPointer());
};
virtual Result OperateRange(Out<FsRangeInfo> range_info, u32 operation_type, u64 offset, u64 size) final {
return this->base_file->OperateRange(static_cast<FsOperationId>(operation_type), offset, size, range_info.GetPointer());
};
public:
DEFINE_SERVICE_DISPATCH_TABLE {
/* 1.0.0- */
MAKE_SERVICE_COMMAND_META(IFileInterface, Read),
MAKE_SERVICE_COMMAND_META(IFileInterface, Write),
MAKE_SERVICE_COMMAND_META(IFileInterface, Flush),
MAKE_SERVICE_COMMAND_META(IFileInterface, SetSize),
MAKE_SERVICE_COMMAND_META(IFileInterface, GetSize),
/* 4.0.0- */
MAKE_SERVICE_COMMAND_META(IFileInterface, OperateRange, FirmwareVersion_400),
};
};
class ProxyFile : public IFile {
private:
std::unique_ptr<FsFile> base_file;
public:
ProxyFile(FsFile *f) : base_file(f) {
/* ... */
}
ProxyFile(std::unique_ptr<FsFile> f) : base_file(std::move(f)) {
/* ... */
}
ProxyFile(FsFile f) {
this->base_file = std::make_unique<FsFile>(f);
}
virtual ~ProxyFile() {
fsFileClose(this->base_file.get());
}
public:
virtual Result ReadImpl(u64 *out, u64 offset, void *buffer, u64 size) override {
size_t out_sz;
R_TRY(fsFileRead(this->base_file.get(), offset, buffer, size, FS_READOPTION_NONE, &out_sz));
*out = out_sz;
return ResultSuccess();
}
virtual Result GetSizeImpl(u64 *out) override {
return fsFileGetSize(this->base_file.get(), out);
}
virtual Result FlushImpl() override {
return fsFileFlush(this->base_file.get());
}
virtual Result WriteImpl(u64 offset, void *buffer, u64 size, u32 option) override {
return fsFileWrite(this->base_file.get(), offset, buffer, size, option);
}
virtual Result SetSizeImpl(u64 size) override {
return fsFileSetSize(this->base_file.get(), size);
}
virtual Result OperateRangeImpl(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
return fsFileOperateRange(this->base_file.get(), operation_type, offset, size, out_range_info);
}
};

View file

@ -1,463 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "../utils.hpp"
#include "fs_filesystem_types.hpp"
#include "fs_path_utils.hpp"
#include "fs_ifile.hpp"
#include "fs_idirectory.hpp"
class IFile;
class IDirectory;
class IFileSystem {
public:
virtual ~IFileSystem() {}
Result CreateFile(const FsPath &path, uint64_t size, int flags) {
return CreateFileImpl(path, size, flags);
}
Result CreateFile(const FsPath &path, uint64_t size) {
return CreateFileImpl(path, size, 0);
}
Result DeleteFile(const FsPath &path) {
return DeleteFileImpl(path);
}
Result CreateDirectory(const FsPath &path) {
return CreateDirectoryImpl(path);
}
Result DeleteDirectory(const FsPath &path) {
return DeleteDirectoryImpl(path);
}
Result DeleteDirectoryRecursively(const FsPath &path) {
return DeleteDirectoryRecursivelyImpl(path);
}
Result RenameFile(const FsPath &old_path, const FsPath &new_path) {
return RenameFileImpl(old_path, new_path);
}
Result RenameDirectory(const FsPath &old_path, const FsPath &new_path) {
return RenameDirectoryImpl(old_path, new_path);
}
Result GetEntryType(DirectoryEntryType *out, const FsPath &path) {
if (out == nullptr) {
return ResultFsNullptrArgument;
}
return GetEntryTypeImpl(out, path);
}
Result OpenFile(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) {
if (!(mode & OpenMode_ReadWrite)) {
return ResultFsInvalidArgument;
}
if (mode & ~OpenMode_All) {
return ResultFsInvalidArgument;
}
return OpenFileImpl(out_file, path, mode);
}
Result OpenDirectory(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) {
if (!(mode & DirectoryOpenMode_All)) {
return ResultFsInvalidArgument;
}
if (mode & ~DirectoryOpenMode_All) {
return ResultFsInvalidArgument;
}
return OpenDirectoryImpl(out_dir, path, mode);
}
Result Commit() {
return CommitImpl();
}
Result GetFreeSpaceSize(uint64_t *out, const FsPath &path) {
if (out == nullptr) {
return ResultFsNullptrArgument;
}
return GetFreeSpaceSizeImpl(out, path);
}
Result GetTotalSpaceSize(uint64_t *out, const FsPath &path) {
if (out == nullptr) {
return ResultFsNullptrArgument;
}
return GetTotalSpaceSizeImpl(out, path);
}
Result CleanDirectoryRecursively(const FsPath &path) {
return CleanDirectoryRecursivelyImpl(path);
}
Result GetFileTimeStampRaw(FsTimeStampRaw *out, const FsPath &path) {
if (out == nullptr) {
return ResultFsNullptrArgument;
}
return GetFileTimeStampRawImpl(out, path);
}
Result QueryEntry(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) {
return QueryEntryImpl(out, out_size, in, in_size, query, path);
}
protected:
/* ...? */
private:
virtual Result CreateFileImpl(const FsPath &path, uint64_t size, int flags) = 0;
virtual Result DeleteFileImpl(const FsPath &path) = 0;
virtual Result CreateDirectoryImpl(const FsPath &path) = 0;
virtual Result DeleteDirectoryImpl(const FsPath &path) = 0;
virtual Result DeleteDirectoryRecursivelyImpl(const FsPath &path) = 0;
virtual Result RenameFileImpl(const FsPath &old_path, const FsPath &new_path) = 0;
virtual Result RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) = 0;
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) = 0;
virtual Result OpenFileImpl(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) = 0;
virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) = 0;
virtual Result CommitImpl() = 0;
virtual Result GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) {
(void)(out);
(void)(path);
return ResultFsNotImplemented;
}
virtual Result GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) {
(void)(out);
(void)(path);
return ResultFsNotImplemented;
}
virtual Result CleanDirectoryRecursivelyImpl(const FsPath &path) = 0;
virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) {
(void)(out);
(void)(path);
return ResultFsNotImplemented;
}
virtual Result QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) {
(void)(out);
(void)(out_size);
(void)(in);
(void)(in_size);
(void)(query);
(void)(path);
return ResultFsNotImplemented;
}
};
class IFileSystemInterface : public IServiceObject {
private:
enum class CommandId {
/* 1.0.0+ */
CreateFile = 0,
DeleteFile = 1,
CreateDirectory = 2,
DeleteDirectory = 3,
DeleteDirectoryRecursively = 4,
RenameFile = 5,
RenameDirectory = 6,
GetEntryType = 7,
OpenFile = 8,
OpenDirectory = 9,
Commit = 10,
GetFreeSpaceSize = 11,
GetTotalSpaceSize = 12,
/* 3.0.0+ */
CleanDirectoryRecursively = 13,
GetFileTimeStampRaw = 14,
/* 4.0.0+ */
QueryEntry = 15,
};
private:
std::unique_ptr<IFileSystem> unique_fs;
std::shared_ptr<IFileSystem> shared_fs;
IFileSystem *base_fs;
public:
IFileSystemInterface(IFileSystem *fs) : unique_fs(fs) {
this->base_fs = this->unique_fs.get();
};
IFileSystemInterface(std::unique_ptr<IFileSystem> fs) : unique_fs(std::move(fs)) {
this->base_fs = this->unique_fs.get();
};
IFileSystemInterface(std::shared_ptr<IFileSystem> fs) : shared_fs(fs) {
this->base_fs = this->shared_fs.get();
};
private:
/* Actual command API. */
virtual Result CreateFile(InPointer<char> in_path, uint64_t size, int flags) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
return this->base_fs->CreateFile(path, size, flags);
}
virtual Result DeleteFile(InPointer<char> in_path) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
return this->base_fs->DeleteFile(path);
}
virtual Result CreateDirectory(InPointer<char> in_path) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
return this->base_fs->CreateDirectory(path);
}
virtual Result DeleteDirectory(InPointer<char> in_path) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
return this->base_fs->DeleteDirectory(path);
}
virtual Result DeleteDirectoryRecursively(InPointer<char> in_path) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
return this->base_fs->DeleteDirectoryRecursively(path);
}
virtual Result RenameFile(InPointer<char> in_old_path, InPointer<char> in_new_path) final {
FsPath old_path;
FsPath new_path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&old_path, in_old_path.pointer));
R_TRY(FsPathUtils::ConvertPathForServiceObject(&new_path, in_new_path.pointer));
return this->base_fs->RenameFile(old_path, new_path);
}
virtual Result RenameDirectory(InPointer<char> in_old_path, InPointer<char> in_new_path) final {
FsPath old_path;
FsPath new_path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&old_path, in_old_path.pointer));
R_TRY(FsPathUtils::ConvertPathForServiceObject(&new_path, in_new_path.pointer));
return this->base_fs->RenameDirectory(old_path, new_path);
}
virtual Result GetEntryType(Out<u32> out_type, InPointer<char> in_path) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
DirectoryEntryType type;
R_TRY(this->base_fs->GetEntryType(&type, path));
out_type.SetValue(type);
return ResultSuccess();
}
virtual Result OpenFile(Out<std::shared_ptr<IFileInterface>> out_intf, InPointer<char> in_path, uint32_t mode) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
std::unique_ptr<IFile> out_file;
R_TRY(this->base_fs->OpenFile(out_file, path, static_cast<OpenMode>(mode)));
out_intf.SetValue(std::make_shared<IFileInterface>(std::move(out_file)));
return ResultSuccess();
}
virtual Result OpenDirectory(Out<std::shared_ptr<IDirectoryInterface>> out_intf, InPointer<char> in_path, uint32_t mode) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
std::unique_ptr<IDirectory> out_dir;
R_TRY(this->base_fs->OpenDirectory(out_dir, path, static_cast<DirectoryOpenMode>(mode)));
out_intf.SetValue(std::make_shared<IDirectoryInterface>(std::move(out_dir)));
return ResultSuccess();
}
virtual Result Commit() final {
return this->base_fs->Commit();
}
virtual Result GetFreeSpaceSize(Out<uint64_t> out_size, InPointer<char> in_path) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
return this->base_fs->GetFreeSpaceSize(out_size.GetPointer(), path);
}
virtual Result GetTotalSpaceSize(Out<uint64_t> out_size, InPointer<char> in_path) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
return this->base_fs->GetTotalSpaceSize(out_size.GetPointer(), path);
}
virtual Result CleanDirectoryRecursively(InPointer<char> in_path) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
return this->base_fs->CleanDirectoryRecursively(path);
}
virtual Result GetFileTimeStampRaw(Out<FsTimeStampRaw> out_timestamp, InPointer<char> in_path) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
return this->base_fs->GetFileTimeStampRaw(out_timestamp.GetPointer(), path);
}
virtual Result QueryEntry(OutBuffer<char, BufferType_Type1> out_buffer, InBuffer<char, BufferType_Type1> in_buffer, int query, InPointer<char> in_path) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
return this->base_fs->QueryEntry(out_buffer.buffer, out_buffer.num_elements, in_buffer.buffer, in_buffer.num_elements, query, path);
}
public:
DEFINE_SERVICE_DISPATCH_TABLE {
/* 1.0.0- */
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, CreateFile),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, DeleteFile),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, CreateDirectory),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, DeleteDirectory),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, DeleteDirectoryRecursively),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, RenameFile),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, RenameDirectory),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, GetEntryType),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, OpenFile),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, OpenDirectory),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, Commit),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, GetFreeSpaceSize),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, GetTotalSpaceSize),
/* 3.0.0- */
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, CleanDirectoryRecursively, FirmwareVersion_300),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, GetFileTimeStampRaw, FirmwareVersion_300),
/* 4.0.0- */
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, QueryEntry, FirmwareVersion_400),
};
};
class ProxyFileSystem : public IFileSystem {
private:
std::unique_ptr<FsFileSystem> base_fs;
public:
ProxyFileSystem(FsFileSystem *fs) : base_fs(fs) {
/* ... */
}
ProxyFileSystem(std::unique_ptr<FsFileSystem> fs) : base_fs(std::move(fs)) {
/* ... */
}
ProxyFileSystem(FsFileSystem fs) {
this->base_fs = std::make_unique<FsFileSystem>(fs);
}
virtual ~ProxyFileSystem() {
fsFsClose(this->base_fs.get());
}
public:
virtual Result CreateFileImpl(const FsPath &path, uint64_t size, int flags) {
return fsFsCreateFile(this->base_fs.get(), path.str, size, flags);
}
virtual Result DeleteFileImpl(const FsPath &path) {
return fsFsDeleteFile(this->base_fs.get(), path.str);
}
virtual Result CreateDirectoryImpl(const FsPath &path) {
return fsFsCreateDirectory(this->base_fs.get(), path.str);
}
virtual Result DeleteDirectoryImpl(const FsPath &path) {
return fsFsDeleteDirectory(this->base_fs.get(), path.str);
}
virtual Result DeleteDirectoryRecursivelyImpl(const FsPath &path) {
return fsFsDeleteDirectoryRecursively(this->base_fs.get(), path.str);
}
virtual Result RenameFileImpl(const FsPath &old_path, const FsPath &new_path) {
return fsFsRenameFile(this->base_fs.get(), old_path.str, new_path.str);
}
virtual Result RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) {
return fsFsRenameDirectory(this->base_fs.get(), old_path.str, new_path.str);
}
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) {
FsEntryType type;
R_TRY(fsFsGetEntryType(this->base_fs.get(), path.str, &type));
*out = static_cast<DirectoryEntryType>(static_cast<u32>(type));
return ResultSuccess();
}
virtual Result OpenFileImpl(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) {
FsFile f;
R_TRY(fsFsOpenFile(this->base_fs.get(), path.str, static_cast<int>(mode), &f));
out_file = std::make_unique<ProxyFile>(f);
return ResultSuccess();
}
virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) {
FsDir d;
R_TRY(fsFsOpenDirectory(this->base_fs.get(), path.str, static_cast<int>(mode), &d));
out_dir = std::make_unique<ProxyDirectory>(d);
return ResultSuccess();
}
virtual Result CommitImpl() {
return fsFsCommit(this->base_fs.get());
}
virtual Result GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) {
return fsFsGetFreeSpace(this->base_fs.get(), path.str, out);
}
virtual Result GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) {
return fsFsGetTotalSpace(this->base_fs.get(), path.str, out);
}
virtual Result CleanDirectoryRecursivelyImpl(const FsPath &path) {
return fsFsCleanDirectoryRecursively(this->base_fs.get(), path.str);
}
virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) {
return fsFsGetFileTimeStampRaw(this->base_fs.get(), path.str, out);
}
virtual Result QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) {
return fsFsQueryEntry(this->base_fs.get(), out, out_size, in, in_size, path.str,static_cast<FsFileSystemQueryType>(query));
}
};

View file

@ -1,175 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "fs_shim.h"
#include "../debug.hpp"
class IStorage {
public:
virtual ~IStorage();
virtual Result Read(void *buffer, size_t size, u64 offset) = 0;
virtual Result Write(void *buffer, size_t size, u64 offset) = 0;
virtual Result Flush() = 0;
virtual Result SetSize(u64 size) = 0;
virtual Result GetSize(u64 *out_size) = 0;
virtual Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0;
static inline bool IsRangeValid(uint64_t offset, uint64_t size, uint64_t total_size) {
return size <= total_size && offset <= total_size - size;
}
};
class IStorageInterface : public IServiceObject {
private:
enum class CommandId {
Read = 0,
Write = 1,
Flush = 2,
SetSize = 3,
GetSize = 4,
OperateRange = 5,
};
private:
IStorage *base_storage;
public:
IStorageInterface(IStorage *s) : base_storage(s) {
/* ... */
};
~IStorageInterface() {
delete base_storage;
};
private:
/* Actual command API. */
virtual Result Read(OutBuffer<u8, BufferType_Type1> buffer, u64 offset, u64 size) final {
return this->base_storage->Read(buffer.buffer, std::min(buffer.num_elements, size), offset);
};
virtual Result Write(InBuffer<u8, BufferType_Type1> buffer, u64 offset, u64 size) final {
return this->base_storage->Write(buffer.buffer, std::min(buffer.num_elements, size), offset);
};
virtual Result Flush() final {
return this->base_storage->Flush();
};
virtual Result SetSize(u64 size) final {
return this->base_storage->SetSize(size);
};
virtual Result GetSize(Out<u64> size) final {
return this->base_storage->GetSize(size.GetPointer());
};
virtual Result OperateRange(Out<FsRangeInfo> range_info, u32 operation_type, u64 offset, u64 size) final {
return this->base_storage->OperateRange(static_cast<FsOperationId>(operation_type), offset, size, range_info.GetPointer());
};
public:
DEFINE_SERVICE_DISPATCH_TABLE {
/* 1.0.0- */
MAKE_SERVICE_COMMAND_META(IStorageInterface, Read),
MAKE_SERVICE_COMMAND_META(IStorageInterface, Write),
MAKE_SERVICE_COMMAND_META(IStorageInterface, Flush),
MAKE_SERVICE_COMMAND_META(IStorageInterface, SetSize),
MAKE_SERVICE_COMMAND_META(IStorageInterface, GetSize),
/* 4.0.0- */
MAKE_SERVICE_COMMAND_META(IStorageInterface, OperateRange, FirmwareVersion_400),
};
};
class IROStorage : public IStorage {
public:
virtual Result Read(void *buffer, size_t size, u64 offset) = 0;
virtual Result Write(void *buffer, size_t size, u64 offset) final {
(void)(buffer);
(void)(offset);
(void)(size);
return ResultFsUnsupportedOperation;
};
virtual Result Flush() final {
return ResultSuccess();
};
virtual Result SetSize(u64 size) final {
(void)(size);
return ResultFsUnsupportedOperation;
};
virtual Result GetSize(u64 *out_size) = 0;
virtual Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0;
};
class ProxyStorage : public IStorage {
private:
FsStorage *base_storage;
public:
ProxyStorage(FsStorage *s) : base_storage(s) {
/* ... */
};
ProxyStorage(FsStorage s) {
this->base_storage = new FsStorage(s);
};
virtual ~ProxyStorage() {
fsStorageClose(base_storage);
delete base_storage;
};
public:
virtual Result Read(void *buffer, size_t size, u64 offset) override {
return fsStorageRead(this->base_storage, offset, buffer, size);
};
virtual Result Write(void *buffer, size_t size, u64 offset) override {
return fsStorageWrite(this->base_storage, offset, buffer, size);
};
virtual Result Flush() override {
return fsStorageFlush(this->base_storage);
};
virtual Result GetSize(u64 *out_size) override {
return fsStorageGetSize(this->base_storage, out_size);
};
virtual Result SetSize(u64 size) override {
return fsStorageSetSize(this->base_storage, size);
};
virtual Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
return fsStorageOperateRange(this->base_storage, operation_type, offset, size, out_range_info);
};
};
class ReadOnlyStorageAdapter : public IROStorage {
private:
std::shared_ptr<IStorage> base_storage;
IStorage *storage;
public:
ReadOnlyStorageAdapter(IStorage *s) : base_storage(s) {
this->storage = this->base_storage.get();
}
ReadOnlyStorageAdapter(std::shared_ptr<IStorage> s) : base_storage(s) {
this->storage = this->base_storage.get();
}
virtual ~ReadOnlyStorageAdapter() {
/* ... */
}
public:
virtual Result Read(void *buffer, size_t size, u64 offset) override {
return this->base_storage->Read(buffer, size, offset);
};
virtual Result GetSize(u64 *out_size) override {
return this->base_storage->GetSize(out_size);
};
virtual Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
return this->base_storage->OperateRange(operation_type, offset, size, out_range_info);
};
};

View file

@ -1,267 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstring>
#include <cstdlib>
#include <switch.h>
#include <stratosphere.hpp>
#include "fs_path_utils.hpp"
Result FsPathUtils::VerifyPath(const char *path, size_t max_path_len, size_t max_name_len) {
const char *cur = path;
size_t name_len = 0;
for (size_t path_len = 0; path_len <= max_path_len && name_len <= max_name_len; path_len++) {
const char c = *(cur++);
/* If terminated, we're done. */
if (c == 0) {
return ResultSuccess();
}
/* TODO: Nintendo converts the path from utf-8 to utf-32, one character at a time. */
/* We should do this. */
/* Banned characters: :*?<>| */
if (c == ':' || c == '*' || c == '?' || c == '<' || c == '>' || c == '|') {
return ResultFsInvalidCharacter;
}
name_len++;
/* Check for separator. */
if (c == '/' || c == '\\') {
name_len = 0;
}
}
return ResultFsTooLongPath;
}
Result FsPathUtils::ConvertPathForServiceObject(FsPath *out, const char *path) {
/* Check for nullptr. */
if (out == nullptr || path == nullptr) {
return ResultFsInvalidPath;
}
/* Copy string, NULL terminate. */
/* NOTE: Nintendo adds an extra char at 0x301 for NULL terminator */
/* But then forces 0x300 to NULL anyway... */
std::strncpy(out->str, path, sizeof(out->str) - 1);
out->str[sizeof(out->str)-1] = 0;
/* Replace any instances of \ with / */
for (size_t i = 0; i < sizeof(out->str); i++) {
if (out->str[i] == '\\') {
out->str[i] = '/';
}
}
/* Nintendo allows some liberties if the path is a windows path. Who knows why... */
const auto prefix_len = FsPathUtils::IsWindowsAbsolutePath(path) ? 2 : 0;
const size_t max_len = (FS_MAX_PATH-1) - prefix_len;
return FsPathUtils::VerifyPath(out->str + prefix_len, max_len, max_len);
}
Result FsPathUtils::IsNormalized(bool *out, const char *path) {
/* Nintendo uses a state machine here. */
enum class PathState {
Start,
Normal,
FirstSeparator,
Separator,
CurrentDir,
ParentDir,
WindowsDriveLetter,
};
PathState state = PathState::Start;
for (const char *cur = path; *cur != 0; cur++) {
const char c = *cur;
switch (state) {
case PathState::Start:
if (IsWindowsDriveLetter(c)) {
state = PathState::WindowsDriveLetter;
} else if (c == '/') {
state = PathState::FirstSeparator;
} else {
return ResultFsInvalidPathFormat;
}
break;
case PathState::Normal:
if (c == '/') {
state = PathState::Separator;
}
break;
case PathState::FirstSeparator:
case PathState::Separator:
/* It is unclear why first separator and separator are separate states... */
if (c == '/') {
*out = false;
return ResultSuccess();
} else if (c == '.') {
state = PathState::CurrentDir;
} else {
state = PathState::Normal;
}
break;
case PathState::CurrentDir:
if (c == '/') {
*out = false;
return ResultSuccess();
} else if (c == '.') {
state = PathState::ParentDir;
} else {
state = PathState::Normal;
}
break;
case PathState::ParentDir:
if (c == '/') {
*out = false;
return ResultSuccess();
} else {
state = PathState::Normal;
}
break;
case PathState::WindowsDriveLetter:
if (c == ':') {
*out = true;
return ResultSuccess();
} else {
return ResultFsInvalidPathFormat;
}
break;
}
}
switch (state) {
case PathState::Start:
case PathState::WindowsDriveLetter:
return ResultFsInvalidPathFormat;
case PathState::FirstSeparator:
case PathState::Normal:
*out = true;
break;
case PathState::CurrentDir:
case PathState::ParentDir:
case PathState::Separator:
*out = false;
break;
}
return ResultSuccess();
}
Result FsPathUtils::Normalize(char *out, size_t max_out_size, const char *src, size_t *out_len) {
/* Paths must start with / */
if (src[0] != '/') {
return ResultFsInvalidPathFormat;
}
bool skip_next_sep = false;
size_t i = 0;
size_t len = 0;
while (src[i] != 0) {
if (src[i] == '/') {
/* Swallow separators. */
while (src[++i] == '/') { }
if (src[i] == 0) {
break;
}
/* Handle skip if needed */
if (!skip_next_sep) {
if (len + 1 == max_out_size) {
out[len] = 0;
if (out_len != nullptr) {
*out_len = len;
}
return ResultFsTooLongPath;
}
out[len++] = '/';
/* TODO: N has some weird windows support stuff here under a bool. */
/* Boolean is normally false though? */
}
skip_next_sep = false;
}
/* See length of current dir. */
size_t dir_len = 0;
while (src[i+dir_len] != '/' && src[i+dir_len] != 0) {
dir_len++;
}
if (FsPathUtils::IsCurrentDirectory(&src[i])) {
skip_next_sep = true;
} else if (FsPathUtils::IsParentDirectory(&src[i])) {
if (len == 1) {
return ResultFsDirectoryUnobtainable;
}
/* Walk up a directory. */
len -= 2;
while (out[len] != '/') {
len--;
}
} else {
/* Copy, possibly truncating. */
if (len + dir_len + 1 <= max_out_size) {
for (size_t j = 0; j < dir_len; j++) {
out[len++] = src[i+j];
}
} else {
const size_t copy_len = max_out_size - 1 - len;
for (size_t j = 0; j < copy_len; j++) {
out[len++] = src[i+j];
}
out[len] = 0;
if (out_len != nullptr) {
*out_len = len;
}
return ResultFsTooLongPath;
}
}
i += dir_len;
}
if (skip_next_sep) {
len--;
}
if (len == 0 && max_out_size) {
out[len++] = '/';
}
if (max_out_size < len - 1) {
return ResultFsTooLongPath;
}
/* NULL terminate. */
out[len] = 0;
if (out_len != nullptr) {
*out_len = len;
}
/* Assert normalized. */
bool normalized = false;
R_ASSERT(FsPathUtils::IsNormalized(&normalized, out));
AMS_ASSERT(normalized);
return ResultSuccess();
}

View file

@ -1,72 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
struct FsPath {
char str[FS_MAX_PATH];
};
constexpr FsPath EncodeConstantFsPath(const char *p) {
FsPath path = {0};
for (size_t i = 0; i < sizeof(path) - 1; i++) {
path.str[i] = p[i];
if (p[i] == 0) {
break;
}
}
return path;
}
constexpr size_t GetConstantFsPathLen(FsPath path) {
size_t len = 0;
for (size_t i = 0; i < sizeof(path) - 1; i++) {
if (path.str[i] == 0) {
break;
}
len++;
}
return len;
}
class FsPathUtils {
public:
static constexpr FsPath RootPath = EncodeConstantFsPath("/");
public:
static Result VerifyPath(const char *path, size_t max_path_len, size_t max_name_len);
static Result ConvertPathForServiceObject(FsPath *out, const char *path);
static Result IsNormalized(bool *out, const char *path);
static Result Normalize(char *out, size_t max_out_size, const char *src, size_t *out_size);
static bool IsWindowsDriveLetter(const char c) {
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
}
static bool IsCurrentDirectory(const char *path) {
return path[0] == '.' && (path[1] == 0 || path[1] == '/');
}
static bool IsParentDirectory(const char *path) {
return path[0] == '.' && path[1] == '.' && (path[2] == 0 || path[2] == '/');
}
static bool IsWindowsAbsolutePath(const char *path) {
/* Nintendo uses this in path comparisons... */
return IsWindowsDriveLetter(path[0]) && path[1] == ':';
}
};

View file

@ -1,125 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstring>
#include <cstdlib>
#include <switch.h>
#include "fs_save_utils.hpp"
Result FsSaveUtils::GetSaveDataSpaceIdString(const char **out_str, u8 space_id) {
const char *str = nullptr;
switch (space_id) {
case FsSaveDataSpaceId_NandSystem:
case 100: /* ProperSystem */
case 101: /* SafeMode */
str = "sys";
break;
case FsSaveDataSpaceId_NandUser:
str = "user";
break;
case FsSaveDataSpaceId_SdCard:
str = "sd_sys";
break;
case FsSaveDataSpaceId_TemporaryStorage:
str = "temp";
break;
case 4: /* SdUser */
str = "sd_user";
break;
default: /* Unexpected */
str = nullptr;
break;
}
if (str == nullptr) {
return ResultFsInvalidSaveDataSpaceId;
}
if (out_str) {
*out_str = str;
}
return ResultSuccess();
}
Result FsSaveUtils::GetSaveDataTypeString(const char **out_str, u8 save_data_type) {
const char *str = nullptr;
switch (save_data_type) {
case FsSaveDataType_SystemSaveData:
str = "system";
break;
case FsSaveDataType_SaveData:
str = "account";
break;
case FsSaveDataType_BcatDeliveryCacheStorage:
str = "bcat";
break;
case FsSaveDataType_DeviceSaveData:
str = "device";
break;
case FsSaveDataType_TemporaryStorage:
str = "temp";
break;
case FsSaveDataType_CacheStorage:
str = "cache";
break;
case 6: /* System Bcat Save */
str = "bcat_sys";
break;
default: /* Unexpected */
str = nullptr;
break;
}
if (str == nullptr) {
/* TODO: Better result? */
return ResultFsInvalidArgument;
}
if (out_str) {
*out_str = str;
}
return ResultSuccess();
}
Result FsSaveUtils::GetSaveDataDirectoryPath(FsPath &out_path, u8 space_id, u8 save_data_type, u64 program_id, u128 user_id, u64 save_id) {
const char *space_id_str, *save_type_str;
/* Get space_id, save_data_type strings. */
R_TRY(GetSaveDataSpaceIdString(&space_id_str, space_id));
R_TRY(GetSaveDataTypeString(&save_type_str, save_data_type));
/* Clear and initialize the path. */
std::memset(&out_path, 0, sizeof(out_path));
const bool is_system = (save_id != 0 && user_id == 0);
size_t out_path_len;
if (is_system) {
out_path_len = static_cast<size_t>(snprintf(out_path.str, sizeof(out_path.str), "/atmosphere/saves/sysnand/%s/%s/%016lx",
space_id_str, save_type_str, save_id));
} else {
out_path_len = static_cast<size_t>(snprintf(out_path.str, sizeof(out_path.str), "/atmosphere/saves/sysnand/%s/%s/%016lx/%016lx%016lx",
space_id_str, save_type_str, program_id, static_cast<u64>(user_id >> 64ul), static_cast<u64>(user_id)));
}
if (out_path_len >= sizeof(out_path)) {
/* TODO: Should we abort here? */
return ResultFsTooLongPath;
}
return ResultSuccess();
}

View file

@ -1,29 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include "fs_path_utils.hpp"
#include "fs_ifilesystem.hpp"
class FsSaveUtils {
private:
static Result GetSaveDataSpaceIdString(const char **out_str, u8 space_id);
static Result GetSaveDataTypeString(const char **out_str, u8 save_data_type);
public:
static Result GetSaveDataDirectoryPath(FsPath &out_path, u8 space_id, u8 save_data_type, u64 program_id, u128 user_id, u64 save_id);
};

View file

@ -1,266 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include <string.h>
#include "fs_shim.h"
/* Missing fsp-srv commands. */
Result fsOpenBisStorageFwd(Service* s, FsStorage* out, FsBisStorageId PartitionId) {
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
u32 PartitionId;
} *raw;
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 12;
raw->PartitionId = PartitionId;
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
serviceCreateSubservice(&out->s, s, &r, 0);
}
}
return rc;
}
Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out) {
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
} *raw;
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 200;
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
serviceCreateSubservice(&out->s, s, &r, 0);
}
}
return rc;
}
Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorageId storage_id, u64 data_id, FsStorage* out) {
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
FsStorageId storage_id;
u64 data_id;
} *raw;
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 202;
raw->storage_id = storage_id;
raw->data_id = data_id;
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
serviceCreateSubservice(&out->s, s, &r, 0);
}
}
return rc;
}
Result fsOpenFileSystemWithPatchFwd(Service* s, FsFileSystem* out, u64 titleId, FsFileSystemType fsType) {
if (hosversionBefore(2, 0, 0)) {
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
}
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
u32 fsType;
u64 titleId;
} *raw;
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 7;
raw->fsType = fsType;
raw->titleId = titleId;
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
serviceCreateSubservice(&out->s, s, &r, 0);
}
}
return rc;
}
Result fsOpenFileSystemWithIdFwd(Service* s, FsFileSystem* out, u64 titleId, FsFileSystemType fsType, const char* contentPath) {
if (hosversionBefore(2, 0, 0)) {
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
}
char sendStr[FS_MAX_PATH] = {0};
strncpy(sendStr, contentPath, sizeof(sendStr)-1);
IpcCommand c;
ipcInitialize(&c);
ipcAddSendStatic(&c, sendStr, sizeof(sendStr), 0);
struct {
u64 magic;
u64 cmd_id;
u32 fsType;
u64 titleId;
} *raw;
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 8;
raw->fsType = fsType;
raw->titleId = titleId;
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
serviceCreateSubservice(&out->s, s, &r, 0);
}
}
return rc;
}
Result fsOpenSaveDataFileSystemFwd(Service *s, FsFileSystem* out, u8 inval, FsSave *save) {
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
u64 inval;//Actually u8.
FsSave save;
} PACKED *raw;
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 51;
raw->inval = (u64)inval;
memcpy(&raw->save, save, sizeof(FsSave));
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
serviceCreateSubservice(&out->s, s, &r, 0);
}
}
return rc;
}

View file

@ -1,24 +0,0 @@
/**
* @file fs_shim.h
* @brief Filesystem Services (fs) IPC wrapper. To be merged into libnx, eventually.
* @author SciresM
* @copyright libnx Authors
*/
#pragma once
#include <switch.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Missing fsp-srv commands. */
Result fsOpenBisStorageFwd(Service* s, FsStorage* out, FsBisStorageId PartitionId);
Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out);
Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorageId storage_id, u64 data_id, FsStorage* out);
Result fsOpenFileSystemWithPatchFwd(Service* s, FsFileSystem* out, u64 titleId, FsFileSystemType fsType);
Result fsOpenFileSystemWithIdFwd(Service* s, FsFileSystem* out, u64 titleId, FsFileSystemType fsType, const char* contentPath);
Result fsOpenSaveDataFileSystemFwd(Service* s, FsFileSystem* out, u8 inval, FsSave *save);
#ifdef __cplusplus
}
#endif

View file

@ -1,177 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstring>
#include <switch.h>
#include <stratosphere.hpp>
#include "../utils.hpp"
#include "fs_subdirectory_filesystem.hpp"
#include "fs_path_utils.hpp"
Result SubDirectoryFileSystem::Initialize(const char *bp) {
if (strnlen(bp, FS_MAX_PATH) >= FS_MAX_PATH) {
return ResultFsTooLongPath;
}
/* Normalize the path. */
char normal_path[FS_MAX_PATH + 1];
size_t normal_path_len;
R_ASSERT(FsPathUtils::Normalize(normal_path, sizeof(normal_path), bp, &normal_path_len));
/* Ensure terminating '/' */
if (normal_path[normal_path_len-1] != '/') {
AMS_ASSERT(normal_path_len + 2 <= sizeof(normal_path));
strncat(normal_path, "/", 2);
normal_path[sizeof(normal_path)-1] = 0;
normal_path_len++;
}
this->base_path_len = normal_path_len + 1;
this->base_path = reinterpret_cast<char *>(malloc(this->base_path_len));
if (this->base_path == nullptr) {
return ResultFsAllocationFailureInSubDirectoryFileSystem;
}
std::strncpy(this->base_path, normal_path, this->base_path_len);
this->base_path[this->base_path_len-1] = 0;
return ResultSuccess();
}
Result SubDirectoryFileSystem::GetFullPath(char *out, size_t out_size, const char *relative_path) {
if (this->base_path_len + strnlen(relative_path, FS_MAX_PATH) > out_size) {
return ResultFsTooLongPath;
}
/* Copy base path. */
std::strncpy(out, this->base_path, out_size);
out[out_size-1] = 0;
/* Normalize it. */
return FsPathUtils::Normalize(out + this->base_path_len - 2, out_size - (this->base_path_len - 2), relative_path, nullptr);
}
Result SubDirectoryFileSystem::CreateFileImpl(const FsPath &path, uint64_t size, int flags) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->CreateFile(full_path, size, flags);
}
Result SubDirectoryFileSystem::DeleteFileImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->DeleteFile(full_path);
}
Result SubDirectoryFileSystem::CreateDirectoryImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->CreateDirectory(full_path);
}
Result SubDirectoryFileSystem::DeleteDirectoryImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->DeleteDirectory(full_path);
}
Result SubDirectoryFileSystem::DeleteDirectoryRecursivelyImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->DeleteDirectoryRecursively(full_path);
}
Result SubDirectoryFileSystem::RenameFileImpl(const FsPath &old_path, const FsPath &new_path) {
FsPath full_old_path, full_new_path;
R_TRY(GetFullPath(full_old_path, old_path));
R_TRY(GetFullPath(full_new_path, new_path));
return this->base_fs->RenameFile(full_old_path, full_new_path);
}
Result SubDirectoryFileSystem::RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) {
FsPath full_old_path, full_new_path;
R_TRY(GetFullPath(full_old_path, old_path));
R_TRY(GetFullPath(full_new_path, new_path));
return this->base_fs->RenameDirectory(full_old_path, full_new_path);
}
Result SubDirectoryFileSystem::GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->GetEntryType(out, full_path);
}
Result SubDirectoryFileSystem::OpenFileImpl(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->OpenFile(out_file, full_path, mode);
}
Result SubDirectoryFileSystem::OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->OpenDirectory(out_dir, full_path, mode);
}
Result SubDirectoryFileSystem::CommitImpl() {
return this->base_fs->Commit();
}
Result SubDirectoryFileSystem::GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->GetFreeSpaceSize(out, full_path);
}
Result SubDirectoryFileSystem::GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->GetTotalSpaceSize(out, full_path);
}
Result SubDirectoryFileSystem::CleanDirectoryRecursivelyImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->CleanDirectoryRecursively(full_path);
}
Result SubDirectoryFileSystem::GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->GetFileTimeStampRaw(out, full_path);
}
Result SubDirectoryFileSystem::QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->QueryEntry(out, out_size, in, in_size, query, full_path);
}

View file

@ -1,71 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "fs_ifilesystem.hpp"
#include "fs_path_utils.hpp"
class SubDirectoryFileSystem : public IFileSystem {
private:
std::shared_ptr<IFileSystem> base_fs;
char *base_path = nullptr;
size_t base_path_len = 0;
public:
SubDirectoryFileSystem(IFileSystem *fs, const char *bp) : base_fs(fs) {
R_ASSERT(this->Initialize(bp));
}
SubDirectoryFileSystem(std::shared_ptr<IFileSystem> fs, const char *bp) : base_fs(fs) {
R_ASSERT(this->Initialize(bp));
}
virtual ~SubDirectoryFileSystem() {
if (this->base_path != nullptr) {
free(this->base_path);
}
}
private:
Result Initialize(const char *bp);
protected:
Result GetFullPath(char *out, size_t out_size, const char *relative_path);
Result GetFullPath(FsPath &full_path, const FsPath &relative_path) {
return GetFullPath(full_path.str, sizeof(full_path.str), relative_path.str);
}
public:
virtual Result CreateFileImpl(const FsPath &path, uint64_t size, int flags) override;
virtual Result DeleteFileImpl(const FsPath &path) override;
virtual Result CreateDirectoryImpl(const FsPath &path) override;
virtual Result DeleteDirectoryImpl(const FsPath &path) override;
virtual Result DeleteDirectoryRecursivelyImpl(const FsPath &path) override;
virtual Result RenameFileImpl(const FsPath &old_path, const FsPath &new_path) override;
virtual Result RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) override;
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) override;
virtual Result OpenFileImpl(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) override;
virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) override;
virtual Result CommitImpl() override;
virtual Result GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) override;
virtual Result GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) override;
virtual Result CleanDirectoryRecursivelyImpl(const FsPath &path) override;
virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) override;
virtual Result QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) override;
};

View file

@ -1,110 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include <cstring>
#include <stratosphere.hpp>
#include "fsmitm_boot0storage.hpp"
static ams::os::Mutex g_boot0_mutex;
static u8 g_boot0_bct_buffer[Boot0Storage::BctEndOffset];
bool Boot0Storage::CanModifyBctPubks() {
if (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 this->program_id == ams::ncm::ProgramId::Ns;
} else {
/* RCM bug unpatched. */
/* Allow homebrew but not NS to update the BCT pubks. */
return this->program_id != ams::ncm::ProgramId::Ns;
}
}
Result Boot0Storage::Read(void *_buffer, size_t size, u64 offset) {
std::scoped_lock lk{g_boot0_mutex};
return Base::Read(_buffer, size, offset);
}
Result Boot0Storage::Write(void *_buffer, size_t size, u64 offset) {
std::scoped_lock lk{g_boot0_mutex};
u8 *buffer = static_cast<u8 *>(_buffer);
/* Protect the keyblob region from writes. */
if (offset <= EksStart) {
if (offset + size < EksStart) {
/* Fall through, no need to do anything here. */
} else {
if (offset + size < EksEnd) {
/* Adjust size to avoid writing end of data. */
size = EksStart - offset;
} else {
/* Perform portion of write falling past end of keyblobs. */
const u64 diff = EksEnd - offset;
R_TRY(Base::Write(buffer + diff, size - diff, EksEnd));
/* Adjust size to avoid writing end of data. */
size = EksStart - offset;
}
}
} else {
if (offset < EksEnd) {
if (offset + size < EksEnd) {
/* Ignore writes falling strictly within the region. */
return ResultSuccess();
} else {
/* Only write past the end of the keyblob region. */
buffer = buffer + (EksEnd - offset);
size -= (EksEnd - offset);
offset = EksEnd;
}
} else {
/* Fall through, no need to do anything here. */
}
}
if (size == 0) {
return ResultSuccess();
}
/* We care about protecting autorcm from NS. */
if (CanModifyBctPubks() || offset >= BctEndOffset || (offset + BctSize >= BctEndOffset && offset % BctSize >= BctPubkEnd)) {
return Base::Write(buffer, size, offset);
}
/* First, let's deal with the data past the end. */
if (offset + size >= BctEndOffset) {
const u64 diff = BctEndOffset - offset;
R_TRY(ProxyStorage::Write(buffer + diff, size - diff, BctEndOffset));
size = diff;
}
/* Read in the current BCT region. */
R_TRY(ProxyStorage::Read(g_boot0_bct_buffer, BctEndOffset, 0));
/* Update the bct buffer. */
for (u64 cur_ofs = offset; cur_ofs < BctEndOffset && cur_ofs < offset + size; cur_ofs++) {
const u64 cur_bct_rel_ofs = cur_ofs % BctSize;
if (cur_bct_rel_ofs < BctPubkStart || BctPubkEnd <= cur_bct_rel_ofs) {
g_boot0_bct_buffer[cur_ofs] = buffer[cur_ofs - offset];
}
}
return ProxyStorage::Write(g_boot0_bct_buffer, BctEndOffset, 0);
}

View file

@ -1,143 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <cstring>
#include <stratosphere.hpp>
#include "fs_istorage.hpp"
/* Represents a sectored storage. */
template<u64 SectorSize>
class SectoredProxyStorage : public ProxyStorage {
private:
u64 cur_seek = 0;
u64 cur_sector = 0;
u64 cur_sector_ofs = 0;
u8 sector_buf[SectorSize];
private:
void Seek(u64 offset) {
this->cur_sector_ofs = offset % SectorSize;
this->cur_seek = offset - this->cur_sector_ofs;
}
public:
SectoredProxyStorage(FsStorage *s) : ProxyStorage(s) { }
SectoredProxyStorage(FsStorage s) : ProxyStorage(s) { }
public:
virtual Result Read(void *_buffer, size_t size, u64 offset) override {
u8 *buffer = static_cast<u8 *>(_buffer);
this->Seek(offset);
if (this->cur_sector_ofs == 0 && size % SectorSize == 0) {
/* Fast case. */
return ProxyStorage::Read(buffer, size, offset);
}
R_TRY(ProxyStorage::Read(this->sector_buf, SectorSize, this->cur_seek));
if (size + this->cur_sector_ofs <= SectorSize) {
memcpy(buffer, sector_buf + this->cur_sector_ofs, size);
} else {
/* Leaving the sector... */
size_t ofs = SectorSize - this->cur_sector_ofs;
memcpy(buffer, sector_buf + this->cur_sector_ofs, ofs);
size -= ofs;
/* We're guaranteed alignment, here. */
const size_t aligned_remaining_size = size - (size % SectorSize);
if (aligned_remaining_size) {
R_TRY(ProxyStorage::Read(buffer + ofs, aligned_remaining_size, offset + ofs));
ofs += aligned_remaining_size;
size -= aligned_remaining_size;
}
/* Read any leftover data. */
if (size) {
R_TRY(ProxyStorage::Read(this->sector_buf, SectorSize, offset + ofs));
memcpy(buffer + ofs, sector_buf, size);
}
}
return ResultSuccess();
}
virtual Result Write(void *_buffer, size_t size, u64 offset) override {
u8 *buffer = static_cast<u8 *>(_buffer);
this->Seek(offset);
if (this->cur_sector_ofs == 0 && size % SectorSize == 0) {
/* Fast case. */
return ProxyStorage::Write(buffer, size, offset);
}
R_TRY(ProxyStorage::Read(this->sector_buf, SectorSize, this->cur_seek));
if (size + this->cur_sector_ofs <= SectorSize) {
memcpy(this->sector_buf + this->cur_sector_ofs, buffer, size);
R_TRY(ProxyStorage::Write(this->sector_buf, SectorSize, this->cur_seek));
} else {
/* Leaving the sector... */
size_t ofs = SectorSize - this->cur_sector_ofs;
memcpy(this->sector_buf + this->cur_sector_ofs, buffer, ofs);
R_TRY(ProxyStorage::Write(this->sector_buf, ofs, this->cur_seek));
size -= ofs;
/* We're guaranteed alignment, here. */
const size_t aligned_remaining_size = size - (size % SectorSize);
if (aligned_remaining_size) {
R_TRY(ProxyStorage::Write(buffer + ofs, aligned_remaining_size, offset + ofs));
ofs += aligned_remaining_size;
size -= aligned_remaining_size;
}
/* Write any leftover data. */
if (size) {
R_TRY(ProxyStorage::Read(this->sector_buf, SectorSize, offset + ofs));
memcpy(this->sector_buf, buffer + ofs, size);
R_TRY(ProxyStorage::Write(this->sector_buf, SectorSize, this->cur_seek));
}
}
return ResultSuccess();
}
};
/* Represents an RCM-preserving BOOT0 partition. */
class Boot0Storage : public SectoredProxyStorage<0x200> {
using Base = SectoredProxyStorage<0x200>;
public:
static constexpr u64 BctEndOffset = 0xFC000;
static constexpr u64 BctSize = 0x4000;
static constexpr u64 BctPubkStart = 0x210;
static constexpr u64 BctPubkSize = 0x100;
static constexpr u64 BctPubkEnd = BctPubkStart + BctPubkSize;
static constexpr u64 EksStart = 0x180000;
static constexpr u64 EksSize = 0x4000;
static constexpr u64 EksEnd = EksStart + EksSize;
private:
ams::ncm::ProgramId program_id;
private:
bool CanModifyBctPubks();
public:
Boot0Storage(FsStorage *s, ams::ncm::ProgramId t) : Base(s), program_id(t) { }
Boot0Storage(FsStorage s, ams::ncm::ProgramId t) : Base(s), program_id(t) { }
public:
virtual Result Read(void *_buffer, size_t size, u64 offset) override;
virtual Result Write(void *_buffer, size_t size, u64 offset) override;
};

View file

@ -1,151 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include <stratosphere.hpp>
#include "fsmitm_layeredrom.hpp"
#include "../utils.hpp"
#include "../debug.hpp"
IStorage::~IStorage() = default;
LayeredRomFS::LayeredRomFS(std::shared_ptr<IROStorage> s_r, std::shared_ptr<IROStorage> f_r, u64 tid) : storage_romfs(s_r), file_romfs(f_r), program_id(tid) {
/* Start building the new virtual romfs. */
RomFSBuildContext build_ctx(this->program_id);
this->p_source_infos = std::shared_ptr<std::vector<RomFSSourceInfo>>(new std::vector<RomFSSourceInfo>(), [](std::vector<RomFSSourceInfo> *to_delete) {
for (unsigned int i = 0; i < to_delete->size(); i++) {
(*to_delete)[i].Cleanup();
}
delete to_delete;
});
if (Utils::IsSdInitialized()) {
build_ctx.MergeSdFiles();
}
if (this->file_romfs) {
build_ctx.MergeRomStorage(this->file_romfs.get(), RomFSDataSource::FileRomFS);
}
if (this->storage_romfs) {
build_ctx.MergeRomStorage(this->storage_romfs.get(), RomFSDataSource::BaseRomFS);
}
build_ctx.Build(this->p_source_infos.get());
}
Result LayeredRomFS::Read(void *buffer, size_t size, u64 offset) {
/* Size zero reads should always succeed. */
if (size == 0) {
return ResultSuccess();
}
/* Validate size. */
u64 virt_size = (*this->p_source_infos)[this->p_source_infos->size() - 1].virtual_offset + (*this->p_source_infos)[this->p_source_infos->size() - 1].size;
if (offset >= virt_size) {
return ResultFsInvalidOffset;
}
if (virt_size - offset < size) {
size = virt_size - offset;
}
/* Find first source info via binary search. */
u32 cur_source_ind = 0;
u32 low = 0, high = this->p_source_infos->size() - 1;
while (low <= high) {
u32 mid = (low + high) / 2;
if ((*this->p_source_infos)[mid].virtual_offset > offset) {
/* Too high. */
high = mid - 1;
} else {
/* sources[mid].virtual_offset <= offset, invariant */
if (mid == this->p_source_infos->size() - 1 || (*this->p_source_infos)[mid + 1].virtual_offset > offset) {
/* Success */
cur_source_ind = mid;
break;
}
low = mid + 1;
}
}
size_t read_so_far = 0;
while (read_so_far < size) {
RomFSSourceInfo *cur_source = &((*this->p_source_infos)[cur_source_ind]);
if (cur_source->virtual_offset + cur_source->size > offset) {
u64 cur_read_size = size - read_so_far;
if (cur_read_size > cur_source->size - (offset - cur_source->virtual_offset)) {
cur_read_size = cur_source->size - (offset - cur_source->virtual_offset);
}
switch (cur_source->type) {
case RomFSDataSource::MetaData:
{
FsFile file;
R_ASSERT(Utils::OpenSdFileForAtmosphere(this->program_id, ROMFS_METADATA_FILE_PATH, FS_OPEN_READ, &file));
size_t out_read;
R_ASSERT(fsFileRead(&file, (offset - cur_source->virtual_offset), (void *)((uintptr_t)buffer + read_so_far), cur_read_size, FS_READOPTION_NONE, &out_read));
AMS_ASSERT(out_read == cur_read_size);
fsFileClose(&file);
}
break;
case RomFSDataSource::LooseFile:
{
FsFile file;
R_ASSERT(Utils::OpenRomFSSdFile(this->program_id, cur_source->loose_source_info.path, FS_OPEN_READ, &file));
size_t out_read;
R_ASSERT(fsFileRead(&file, (offset - cur_source->virtual_offset), (void *)((uintptr_t)buffer + read_so_far), cur_read_size, FS_READOPTION_NONE, &out_read));
AMS_ASSERT(out_read == cur_read_size);
fsFileClose(&file);
}
break;
case RomFSDataSource::Memory:
{
memcpy((void *)((uintptr_t)buffer + read_so_far), cur_source->memory_source_info.data + (offset - cur_source->virtual_offset), cur_read_size);
}
break;
case RomFSDataSource::BaseRomFS:
{
R_ASSERT(this->storage_romfs->Read((void *)((uintptr_t)buffer + read_so_far), cur_read_size, cur_source->base_source_info.offset + (offset - cur_source->virtual_offset)));
}
break;
case RomFSDataSource::FileRomFS:
{
R_ASSERT(this->file_romfs->Read((void *)((uintptr_t)buffer + read_so_far), cur_read_size, cur_source->base_source_info.offset + (offset - cur_source->virtual_offset)));
}
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
read_so_far += cur_read_size;
offset += cur_read_size;
} else {
/* Handle padding explicitly. */
cur_source_ind++;
/* Zero out the padding we skip, here. */
memset((void *)((uintptr_t)buffer + read_so_far), 0, ((*this->p_source_infos)[cur_source_ind]).virtual_offset - offset);
read_so_far += ((*this->p_source_infos)[cur_source_ind]).virtual_offset - offset;
offset = ((*this->p_source_infos)[cur_source_ind]).virtual_offset;
}
}
return ResultSuccess();
}
Result LayeredRomFS::GetSize(u64 *out_size) {
*out_size = (*this->p_source_infos)[this->p_source_infos->size() - 1].virtual_offset + (*this->p_source_infos)[this->p_source_infos->size() - 1].size;
return ResultSuccess();
}
Result LayeredRomFS::OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) {
/* TODO: How should I implement this for a virtual romfs? */
if (operation_type == FsOperationId_QueryRange) {
*out_range_info = {0};
}
return ResultSuccess();
}

View file

@ -1,43 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "fs_istorage.hpp"
#include "fsmitm_romfsbuild.hpp"
#include "../utils.hpp"
/* Represents a merged RomFS. */
class LayeredRomFS : public IROStorage {
private:
/* Data Sources. */
std::shared_ptr<IROStorage> storage_romfs;
std::shared_ptr<IROStorage> file_romfs;
/* Information about the merged RomFS. */
u64 program_id;
std::shared_ptr<std::vector<RomFSSourceInfo>> p_source_infos;
public:
LayeredRomFS(std::shared_ptr<IROStorage> s_r, std::shared_ptr<IROStorage> f_r, u64 tid);
virtual ~LayeredRomFS() = default;
virtual Result Read(void *buffer, size_t size, u64 offset) override;
virtual Result GetSize(u64 *out_size) override;
virtual Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override;
};

View file

@ -1,48 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdlib>
#include <cstdint>
#include <cstring>
#include <malloc.h>
#include <switch.h>
#include <atmosphere.h>
#include <stratosphere.hpp>
#include "fsmitm_main.hpp"
#include "fsmitm_service.hpp"
#include "../utils.hpp"
struct FsMitmManagerOptions {
static const size_t PointerBufferSize = 0x800;
static const size_t MaxDomains = 0x40;
static const size_t MaxDomainObjects = 0x4000;
};
using FsMitmManager = WaitableManager<FsMitmManagerOptions>;
void FsMitmMain(void *arg) {
/* Create server manager. */
static auto s_server_manager = FsMitmManager(5);
/* Create fsp-srv mitm. */
AddMitmServerToManager<FsMitmService>(&s_server_manager, "fsp-srv", 61);
/* Loop forever, servicing our services. */
s_server_manager.Process();
}

View file

@ -1,29 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdlib>
#include <cstdint>
#include <cstring>
#include <malloc.h>
#include <switch.h>
#include <atmosphere.h>
#include <stratosphere.hpp>
constexpr u32 FsMitmPriority = 43;
constexpr u32 FsMitmStackSize = 0x8000;
void FsMitmMain(void *arg);

View file

@ -1,408 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include <string.h>
#include <stratosphere.hpp>
#include "../utils.hpp"
#include "fsmitm_romfsbuild.hpp"
#include "../debug.hpp"
void RomFSBuildContext::VisitDirectory(FsFileSystem *filesys, RomFSBuildDirectoryContext *parent) {
FsDir dir;
std::vector<RomFSBuildDirectoryContext *> child_dirs;
/* Open the current parent directory. */
R_ASSERT(Utils::OpenRomFSDir(filesys, this->program_id, parent->path, &dir));
{
ON_SCOPE_EXIT { fsDirClose(&dir); };
u64 read_entries;
while (true) {
R_ASSERT(fsDirRead(&dir, 0, &read_entries, 1, &this->dir_entry));
if (read_entries != 1) {
break;
}
AMS_ASSERT(this->dir_entry.type == ENTRYTYPE_DIR || this->dir_entry.type == ENTRYTYPE_FILE);
if (this->dir_entry.type == ENTRYTYPE_DIR) {
RomFSBuildDirectoryContext *child = new RomFSBuildDirectoryContext({0});
/* Set child's path. */
child->cur_path_ofs = parent->path_len + 1;
child->path_len = child->cur_path_ofs + strlen(this->dir_entry.name);
child->path = new char[child->path_len + 1];
strcpy(child->path, parent->path);
if (child->path_len > FS_MAX_PATH - 1) {
fatalSimple(ResultFsTooLongPath);
}
strcat(child->path + parent->path_len, "/");
strcat(child->path + parent->path_len, this->dir_entry.name);
if (!this->AddDirectory(parent, child, NULL)) {
delete[] child->path;
delete child;
} else {
child_dirs.push_back(child);
}
} else /* if (this->dir_entry.type == ENTRYTYPE_FILE) */ {
RomFSBuildFileContext *child = new RomFSBuildFileContext({0});
/* Set child's path. */
child->cur_path_ofs = parent->path_len + 1;
child->path_len = child->cur_path_ofs + strlen(this->dir_entry.name);
child->path = new char[child->path_len + 1];
strcpy(child->path, parent->path);
if (child->path_len > FS_MAX_PATH - 1) {
fatalSimple(ResultFsTooLongPath);
}
strcat(child->path + parent->path_len, "/");
strcat(child->path + parent->path_len, this->dir_entry.name);
child->source = this->cur_source_type;
child->size = this->dir_entry.fileSize;
if (!this->AddFile(parent, child)) {
delete[] child->path;
delete child;
}
}
}
}
for (auto &child : child_dirs) {
this->VisitDirectory(filesys, child);
}
}
void RomFSBuildContext::MergeSdFiles() {
FsFileSystem sd_filesystem;
FsDir dir;
if (!Utils::IsSdInitialized()) {
return;
}
if (R_FAILED((Utils::OpenSdDirForAtmosphere(this->program_id, "/romfs", &dir)))) {
return;
}
fsDirClose(&dir);
if (R_FAILED(fsMountSdcard(&sd_filesystem))) {
return;
}
this->cur_source_type = RomFSDataSource::LooseFile;
this->VisitDirectory(&sd_filesystem, this->root);
fsFsClose(&sd_filesystem);
}
void RomFSBuildContext::VisitDirectory(RomFSBuildDirectoryContext *parent, u32 parent_offset, void *dir_table, size_t dir_table_size, void *file_table, size_t file_table_size) {
RomFSDirectoryEntry *parent_entry = romfs_get_direntry(dir_table, parent_offset);
if (parent_entry->file != ROMFS_ENTRY_EMPTY) {
RomFSFileEntry *cur_file = romfs_get_fentry(file_table, parent_entry->file);
while (cur_file != NULL) {
RomFSBuildFileContext *child = new RomFSBuildFileContext({0});
/* Set child's path. */
child->cur_path_ofs = parent->path_len + 1;
child->path_len = child->cur_path_ofs + cur_file->name_size;
child->path = new char[child->path_len + 1];
strcpy(child->path, parent->path);
if (child->path_len > FS_MAX_PATH - 1) {
fatalSimple(ResultFsTooLongPath);
}
strcat(child->path + parent->path_len, "/");
strncat(child->path + parent->path_len, cur_file->name, cur_file->name_size);
child->size = cur_file->size;
child->source = this->cur_source_type;
child->orig_offset = cur_file->offset;
if (!this->AddFile(parent, child)) {
delete[] child->path;
delete child;
}
if (cur_file->sibling == ROMFS_ENTRY_EMPTY) {
cur_file = NULL;
} else {
cur_file = romfs_get_fentry(file_table, cur_file->sibling);
}
}
}
if (parent_entry->child != ROMFS_ENTRY_EMPTY) {
RomFSDirectoryEntry *cur_child = romfs_get_direntry(dir_table, parent_entry->child);
u32 cur_child_offset = parent_entry->child;
while (cur_child != NULL) {
RomFSBuildDirectoryContext *child = new RomFSBuildDirectoryContext({0});
/* Set child's path. */
child->cur_path_ofs = parent->path_len + 1;
child->path_len = child->cur_path_ofs + cur_child->name_size;
child->path = new char[child->path_len + 1];
strcpy(child->path, parent->path);
if (child->path_len > FS_MAX_PATH - 1) {
fatalSimple(ResultFsTooLongPath);
}
strcat(child->path + parent->path_len, "/");
strncat(child->path + parent->path_len, cur_child->name, cur_child->name_size);
RomFSBuildDirectoryContext *real = NULL;
if (!this->AddDirectory(parent, child, &real)) {
delete[] child->path;
delete child;
}
if (real == NULL) {
/* TODO: Better error. */
fatalSimple(ResultKernelConnectionClosed);
}
this->VisitDirectory(real, cur_child_offset, dir_table, dir_table_size, file_table, file_table_size);
if (cur_child->sibling == ROMFS_ENTRY_EMPTY) {
cur_child = NULL;
} else {
cur_child_offset = cur_child->sibling;
cur_child = romfs_get_direntry(dir_table, cur_child->sibling);
}
}
}
}
void RomFSBuildContext::MergeRomStorage(IROStorage *storage, RomFSDataSource source) {
RomFSHeader header;
R_ASSERT(storage->Read(&header, sizeof(header), 0));
AMS_ASSERT(header.header_size == sizeof(header));
/* Read tables. */
auto dir_table = std::make_unique<u8[]>(header.dir_table_size);
auto file_table = std::make_unique<u8[]>(header.file_table_size);
R_ASSERT(storage->Read(dir_table.get(), header.dir_table_size, header.dir_table_ofs));
R_ASSERT(storage->Read(file_table.get(), header.file_table_size, header.file_table_ofs));
this->cur_source_type = source;
this->VisitDirectory(this->root, 0x0, dir_table.get(), (size_t)header.dir_table_size, file_table.get(), (size_t)header.file_table_size);
}
bool RomFSBuildContext::AddDirectory(RomFSBuildDirectoryContext *parent_dir_ctx, RomFSBuildDirectoryContext *dir_ctx, RomFSBuildDirectoryContext **out_dir_ctx) {
/* Check whether it's already in the known directories. */
auto existing = this->directories.find(dir_ctx->path);
if (existing != this->directories.end()) {
if (out_dir_ctx) {
*out_dir_ctx = existing->second;
}
return false;
}
/* Add a new directory. */
this->num_dirs++;
this->dir_table_size += sizeof(RomFSDirectoryEntry) + ((dir_ctx->path_len - dir_ctx->cur_path_ofs + 3) & ~3);
dir_ctx->parent = parent_dir_ctx;
this->directories.insert({dir_ctx->path, dir_ctx});
if (out_dir_ctx) {
*out_dir_ctx = dir_ctx;
}
return true;
}
bool RomFSBuildContext::AddFile(RomFSBuildDirectoryContext *parent_dir_ctx, RomFSBuildFileContext *file_ctx) {
/* Check whether it's already in the known files. */
auto existing = this->files.find(file_ctx->path);
if (existing != this->files.end()) {
return false;
}
/* Add a new file. */
this->num_files++;
this->file_table_size += sizeof(RomFSFileEntry) + ((file_ctx->path_len - file_ctx->cur_path_ofs + 3) & ~3);
file_ctx->parent = parent_dir_ctx;
this->files.insert({file_ctx->path, file_ctx});
return true;
}
void RomFSBuildContext::Build(std::vector<RomFSSourceInfo> *out_infos) {
RomFSBuildFileContext *cur_file;
RomFSBuildDirectoryContext *cur_dir;
u32 entry_offset;
u32 dir_hash_table_entry_count = romfs_get_hash_table_count(this->num_dirs);
u32 file_hash_table_entry_count = romfs_get_hash_table_count(this->num_files);
this->dir_hash_table_size = 4 * dir_hash_table_entry_count;
this->file_hash_table_size = 4 * file_hash_table_entry_count;
/* Assign metadata pointers */
auto *header = reinterpret_cast<RomFSHeader*>(std::malloc(sizeof(RomFSHeader)));
*header = {};
const size_t metadata_size = this->dir_hash_table_size + this->dir_table_size + this->file_hash_table_size + this->file_table_size;
u8 *metadata = reinterpret_cast<u8*>(std::malloc(metadata_size));
u32 *dir_hash_table = (u32 *)((uintptr_t)metadata);
RomFSDirectoryEntry *dir_table = (RomFSDirectoryEntry *)((uintptr_t)dir_hash_table + this->dir_hash_table_size);
u32 *file_hash_table = (u32 *)((uintptr_t)dir_table + this->dir_table_size);
RomFSFileEntry *file_table = (RomFSFileEntry *)((uintptr_t)file_hash_table + this->file_hash_table_size);
/* Clear out hash tables. */
for (u32 i = 0; i < dir_hash_table_entry_count; i++) {
dir_hash_table[i] = ROMFS_ENTRY_EMPTY;
}
for (u32 i = 0; i < file_hash_table_entry_count; i++) {
file_hash_table[i] = ROMFS_ENTRY_EMPTY;
}
out_infos->clear();
out_infos->emplace_back(0, sizeof(*header), header, RomFSDataSource::Memory);
/* Determine file offsets. */
entry_offset = 0;
RomFSBuildFileContext *prev_file = NULL;
for (const auto &it : this->files) {
cur_file = it.second;
this->file_partition_size = (this->file_partition_size + 0xFULL) & ~0xFULL;
/* Check for extra padding in the original romfs source and preserve it, to help ourselves later. */
if (prev_file && prev_file->source == cur_file->source && (prev_file->source == RomFSDataSource::BaseRomFS || prev_file->source == RomFSDataSource::FileRomFS)) {
u64 expected = (this->file_partition_size - prev_file->offset + prev_file->orig_offset);
if (expected != cur_file->orig_offset) {
if (expected > cur_file->orig_offset) {
/* This case should NEVER happen. */
/* TODO: Better error. */
fatalSimple(ResultKernelConnectionClosed);
}
this->file_partition_size += cur_file->orig_offset - expected;
}
}
cur_file->offset = this->file_partition_size;
this->file_partition_size += cur_file->size;
cur_file->entry_offset = entry_offset;
entry_offset += sizeof(RomFSFileEntry) + ((cur_file->path_len - cur_file->cur_path_ofs + 3) & ~3);
prev_file = cur_file;
}
/* Assign deferred parent/sibling ownership. */
for (auto it = this->files.rbegin(); it != this->files.rend(); it++) {
cur_file = it->second;
cur_file->sibling = cur_file->parent->file;
cur_file->parent->file = cur_file;
}
/* Determine directory offsets. */
entry_offset = 0;
for (const auto &it : this->directories) {
cur_dir = it.second;
cur_dir->entry_offset = entry_offset;
entry_offset += sizeof(RomFSDirectoryEntry) + ((cur_dir->path_len - cur_dir->cur_path_ofs + 3) & ~3);
}
/* Assign deferred parent/sibling ownership. */
for (auto it = this->directories.rbegin(); it->second != this->root; it++) {
cur_dir = it->second;
cur_dir->sibling = cur_dir->parent->child;
cur_dir->parent->child = cur_dir;
}
/* Populate file tables. */
for (const auto &it : this->files) {
cur_file = it.second;
RomFSFileEntry *cur_entry = romfs_get_fentry(file_table, cur_file->entry_offset);
cur_entry->parent = cur_file->parent->entry_offset;
cur_entry->sibling = (cur_file->sibling == NULL) ? ROMFS_ENTRY_EMPTY : cur_file->sibling->entry_offset;
cur_entry->offset = cur_file->offset;
cur_entry->size = cur_file->size;
u32 name_size = cur_file->path_len - cur_file->cur_path_ofs;
u32 hash = romfs_calc_path_hash(cur_file->parent->entry_offset, (unsigned char *)cur_file->path + cur_file->cur_path_ofs, 0, name_size);
cur_entry->hash = file_hash_table[hash % file_hash_table_entry_count];
file_hash_table[hash % file_hash_table_entry_count] = cur_file->entry_offset;
cur_entry->name_size = name_size;
memset(cur_entry->name, 0, (cur_entry->name_size + 3) & ~3);
memcpy(cur_entry->name, cur_file->path + cur_file->cur_path_ofs, name_size);
switch (cur_file->source) {
case RomFSDataSource::BaseRomFS:
case RomFSDataSource::FileRomFS:
/* Try to compact, if possible. */
if (out_infos->back().type == cur_file->source) {
out_infos->back().size = cur_file->offset + ROMFS_FILEPARTITION_OFS + cur_file->size - out_infos->back().virtual_offset;
} else {
out_infos->emplace_back(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->size, cur_file->orig_offset + ROMFS_FILEPARTITION_OFS, cur_file->source);
}
break;
case RomFSDataSource::LooseFile:
{
char *path = new char[cur_file->path_len + 1];
strcpy(path, cur_file->path);
out_infos->emplace_back(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->size, path, cur_file->source);
}
break;
default:
/* TODO: Better error. */
fatalSimple(ResultKernelConnectionClosed);
break;
}
}
/* Populate dir tables. */
for (const auto &it : this->directories) {
cur_dir = it.second;
RomFSDirectoryEntry *cur_entry = romfs_get_direntry(dir_table, cur_dir->entry_offset);
cur_entry->parent = cur_dir == this->root ? 0 : cur_dir->parent->entry_offset;
cur_entry->sibling = (cur_dir->sibling == NULL) ? ROMFS_ENTRY_EMPTY : cur_dir->sibling->entry_offset;
cur_entry->child = (cur_dir->child == NULL) ? ROMFS_ENTRY_EMPTY : cur_dir->child->entry_offset;
cur_entry->file = (cur_dir->file == NULL) ? ROMFS_ENTRY_EMPTY : cur_dir->file->entry_offset;
u32 name_size = cur_dir->path_len - cur_dir->cur_path_ofs;
u32 hash = romfs_calc_path_hash(cur_dir == this->root ? 0 : cur_dir->parent->entry_offset, (unsigned char *)cur_dir->path + cur_dir->cur_path_ofs, 0, name_size);
cur_entry->hash = dir_hash_table[hash % dir_hash_table_entry_count];
dir_hash_table[hash % dir_hash_table_entry_count] = cur_dir->entry_offset;
cur_entry->name_size = name_size;
memset(cur_entry->name, 0, (cur_entry->name_size + 3) & ~3);
memcpy(cur_entry->name, cur_dir->path + cur_dir->cur_path_ofs, name_size);
}
/* Delete directories. */
for (const auto &it : this->directories) {
cur_dir = it.second;
delete[] cur_dir->path;
delete cur_dir;
}
this->root = NULL;
this->directories.clear();
/* Delete files. */
for (const auto &it : this->files) {
cur_file = it.second;
delete[] cur_file->path;
delete cur_file;
}
this->files.clear();
/* Set header fields. */
header->header_size = sizeof(*header);
header->file_hash_table_size = this->file_hash_table_size;
header->file_table_size = this->file_table_size;
header->dir_hash_table_size = this->dir_hash_table_size;
header->dir_table_size = this->dir_table_size;
header->file_partition_ofs = ROMFS_FILEPARTITION_OFS;
header->dir_hash_table_ofs = (header->file_partition_ofs + this->file_partition_size + 3ULL) & ~3ULL;
header->dir_table_ofs = header->dir_hash_table_ofs + header->dir_hash_table_size;
header->file_hash_table_ofs = header->dir_table_ofs + header->dir_table_size;
header->file_table_ofs = header->file_hash_table_ofs + header->file_hash_table_size;
/* Try to save metadata to the SD card, to save on memory space. */
if (R_SUCCEEDED(Utils::SaveSdFileForAtmosphere(this->program_id, ROMFS_METADATA_FILE_PATH, metadata, metadata_size))) {
out_infos->emplace_back(header->dir_hash_table_ofs, metadata_size, RomFSDataSource::MetaData);
std::free(metadata);
} else {
out_infos->emplace_back(header->dir_hash_table_ofs, metadata_size, metadata, RomFSDataSource::Memory);
}
}

View file

@ -1,288 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdlib>
#include <switch.h>
#include <map>
#include "../debug.hpp"
#include "fs_istorage.hpp"
#define ROMFS_ENTRY_EMPTY 0xFFFFFFFF
#define ROMFS_FILEPARTITION_OFS 0x200
#define ROMFS_METADATA_FILE_PATH "romfs_metadata.bin"
/* Types for RomFS Meta construction. */
enum class RomFSDataSource {
BaseRomFS,
FileRomFS,
LooseFile,
MetaData,
Memory,
};
struct RomFSBaseSourceInfo {
u64 offset;
};
struct RomFSFileSourceInfo {
u64 offset;
};
struct RomFSLooseSourceInfo {
const char *path;
};
struct RomFSMemorySourceInfo {
const u8 *data;
};
struct RomFSMetaDataSourceInfo {
};
struct RomFSSourceInfo {
u64 virtual_offset;
u64 size;
union {
RomFSBaseSourceInfo base_source_info;
RomFSFileSourceInfo file_source_info;
RomFSLooseSourceInfo loose_source_info;
RomFSMemorySourceInfo memory_source_info;
RomFSMetaDataSourceInfo metadata_source_info;
};
RomFSDataSource type;
RomFSSourceInfo(u64 v_o, u64 s, u64 offset, RomFSDataSource t) : virtual_offset(v_o), size(s), type(t) {
switch (this->type) {
case RomFSDataSource::BaseRomFS:
this->base_source_info.offset = offset;
break;
case RomFSDataSource::FileRomFS:
this->file_source_info.offset = offset;
break;
case RomFSDataSource::LooseFile:
case RomFSDataSource::MetaData:
case RomFSDataSource::Memory:
default:
/* TODO: Better error. */
fatalSimple(ResultKernelConnectionClosed);
break;
}
}
RomFSSourceInfo(u64 v_o, u64 s, const void *arg, RomFSDataSource t) : virtual_offset(v_o), size(s), type(t) {
switch (this->type) {
case RomFSDataSource::LooseFile:
this->loose_source_info.path = (decltype(this->loose_source_info.path))arg;
break;
case RomFSDataSource::Memory:
this->memory_source_info.data = (decltype(this->memory_source_info.data))arg;
break;
case RomFSDataSource::MetaData:
case RomFSDataSource::BaseRomFS:
case RomFSDataSource::FileRomFS:
default:
/* TODO: Better error. */
fatalSimple(ResultKernelConnectionClosed);
break;
}
}
RomFSSourceInfo(u64 v_o, u64 s, RomFSDataSource t) : virtual_offset(v_o), size(s), type(t) {
switch (this->type) {
case RomFSDataSource::MetaData:
break;
case RomFSDataSource::LooseFile:
case RomFSDataSource::Memory:
case RomFSDataSource::BaseRomFS:
case RomFSDataSource::FileRomFS:
default:
/* TODO: Better error. */
fatalSimple(ResultKernelConnectionClosed);
break;
}
}
void Cleanup() {
switch (this->type) {
case RomFSDataSource::BaseRomFS:
case RomFSDataSource::FileRomFS:
case RomFSDataSource::MetaData:
break;
case RomFSDataSource::LooseFile:
delete[] this->loose_source_info.path;
break;
case RomFSDataSource::Memory:
std::free((void*)this->memory_source_info.data);
break;
default:
/* TODO: Better error. */
fatalSimple(ResultKernelConnectionClosed);
break;
}
}
static bool Compare(RomFSSourceInfo *a, RomFSSourceInfo *b) {
return (a->virtual_offset < b->virtual_offset);
}
};
/* Types for building a RomFS. */
struct RomFSHeader {
u64 header_size;
u64 dir_hash_table_ofs;
u64 dir_hash_table_size;
u64 dir_table_ofs;
u64 dir_table_size;
u64 file_hash_table_ofs;
u64 file_hash_table_size;
u64 file_table_ofs;
u64 file_table_size;
u64 file_partition_ofs;
};
static_assert(sizeof(RomFSHeader) == 0x50, "Incorrect RomFS Header definition!");
struct RomFSDirectoryEntry {
u32 parent;
u32 sibling;
u32 child;
u32 file;
u32 hash;
u32 name_size;
char name[];
};
static_assert(sizeof(RomFSDirectoryEntry) == 0x18, "Incorrect RomFSDirectoryEntry definition!");
struct RomFSFileEntry {
u32 parent;
u32 sibling;
u64 offset;
u64 size;
u32 hash;
u32 name_size;
char name[];
};
static_assert(sizeof(RomFSFileEntry) == 0x20, "Incorrect RomFSFileEntry definition!");
struct RomFSBuildFileContext;
/* Used as comparator for std::map<char *, RomFSBuild*Context> */
struct build_ctx_cmp {
bool operator()(const char *a, const char *b) const {
return strcmp(a, b) < 0;
}
};
struct RomFSBuildDirectoryContext {
char *path;
u32 cur_path_ofs;
u32 path_len;
u32 entry_offset = 0;
RomFSBuildDirectoryContext *parent = NULL;
RomFSBuildDirectoryContext *child = NULL;
RomFSBuildDirectoryContext *sibling = NULL;
RomFSBuildFileContext *file = NULL;
};
struct RomFSBuildFileContext {
char *path;
u32 cur_path_ofs;
u32 path_len;
u32 entry_offset = 0;
u64 offset = 0;
u64 size = 0;
RomFSBuildDirectoryContext *parent = NULL;
RomFSBuildFileContext *sibling = NULL;
RomFSDataSource source{0};
u64 orig_offset = 0;
};
class RomFSBuildContext {
private:
u64 program_id;
RomFSBuildDirectoryContext *root;
std::map<char *, RomFSBuildDirectoryContext *, build_ctx_cmp> directories;
std::map<char *, RomFSBuildFileContext *, build_ctx_cmp> files;
u64 num_dirs = 0;
u64 num_files = 0;
u64 dir_table_size = 0;
u64 file_table_size = 0;
u64 dir_hash_table_size = 0;
u64 file_hash_table_size = 0;
u64 file_partition_size = 0;
FsDirectoryEntry dir_entry;
RomFSDataSource cur_source_type;
void VisitDirectory(FsFileSystem *filesys, RomFSBuildDirectoryContext *parent);
void VisitDirectory(RomFSBuildDirectoryContext *parent, u32 parent_offset, void *dir_table, size_t dir_table_size, void *file_table, size_t file_table_size);
bool AddDirectory(RomFSBuildDirectoryContext *parent_dir_ctx, RomFSBuildDirectoryContext *dir_ctx, RomFSBuildDirectoryContext **out_dir_ctx);
bool AddFile(RomFSBuildDirectoryContext *parent_dir_ctx, RomFSBuildFileContext *file_ctx);
public:
RomFSBuildContext(u64 tid) : program_id(tid) {
this->root = new RomFSBuildDirectoryContext({0});
this->root->path = new char[1];
this->root->path[0] = '\x00';
this->directories.insert({this->root->path, this->root});
this->num_dirs = 1;
this->dir_table_size = 0x18;
}
void MergeSdFiles();
void MergeRomStorage(IROStorage *storage, RomFSDataSource source);
/* This finalizes the context. */
void Build(std::vector<RomFSSourceInfo> *out_infos);
};
static inline RomFSDirectoryEntry *romfs_get_direntry(void *directories, uint32_t offset) {
return (RomFSDirectoryEntry *)((uintptr_t)directories + offset);
}
static inline RomFSFileEntry *romfs_get_fentry(void *files, uint32_t offset) {
return (RomFSFileEntry *)((uintptr_t)files + offset);
}
static inline uint32_t romfs_calc_path_hash(uint32_t parent, const unsigned char *path, uint32_t start, size_t path_len) {
uint32_t hash = parent ^ 123456789;
for (uint32_t i = 0; i < path_len; i++) {
hash = (hash >> 5) | (hash << 27);
hash ^= path[start + i];
}
return hash;
}
static inline uint32_t romfs_get_hash_table_count(uint32_t num_entries) {
if (num_entries < 3) {
return 3;
} else if (num_entries < 19) {
return num_entries | 1;
}
uint32_t count = num_entries;
while (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 || count % 11 == 0 || count % 13 == 0 || count % 17 == 0) {
count++;
}
return count;
}

View file

@ -1,57 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "fs_istorage.hpp"
/* Represents a RomFS stored in some file. */
class RomFileStorage : public IROStorage {
private:
FsFile *base_file;
public:
RomFileStorage(FsFile *f) : base_file(f) {
/* ... */
};
RomFileStorage(FsFile f) {
this->base_file = new FsFile(f);
};
virtual ~RomFileStorage() {
fsFileClose(base_file);
delete base_file;
};
public:
Result Read(void *buffer, size_t size, u64 offset) override {
size_t out_sz = 0;
R_TRY(fsFileRead(this->base_file, offset, buffer, size, FS_READOPTION_NONE, &out_sz));
if (out_sz != size && out_sz) {
R_TRY(this->Read((void *)((uintptr_t)buffer + out_sz), size - out_sz, offset + out_sz));
}
return ResultSuccess();
};
Result GetSize(u64 *out_size) override {
return fsFileGetSize(this->base_file, out_size);
};
Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
/* TODO: Merge into libnx? */
return fsFileOperateRange(this->base_file, operation_type, offset, size, out_range_info);
};
};
/* Represents a RomFS accessed via some IStorage. */
using RomInterfaceStorage = ROProxyStorage;

View file

@ -1,386 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <map>
#include <memory>
#include <mutex>
#include <switch.h>
#include <stratosphere.hpp>
#include "fsmitm_service.hpp"
#include "fs_shim.h"
#include "../utils.hpp"
#include "fsmitm_boot0storage.hpp"
#include "fsmitm_layeredrom.hpp"
#include "fs_dir_utils.hpp"
#include "fs_save_utils.hpp"
#include "fs_file_storage.hpp"
#include "fs_subdirectory_filesystem.hpp"
#include "fs_directory_redirection_filesystem.hpp"
#include "fs_directory_savedata_filesystem.hpp"
#include "../debug.hpp"
static ams::os::Mutex g_StorageCacheLock;
static std::unordered_map<u64, std::weak_ptr<IStorageInterface>> g_StorageCache;
static bool StorageCacheGetEntry(ams::ncm::ProgramId program_id, std::shared_ptr<IStorageInterface> *out) {
std::scoped_lock lock(g_StorageCacheLock);
if (g_StorageCache.find(static_cast<u64>(program_id)) == g_StorageCache.end()) {
return false;
}
auto intf = g_StorageCache[static_cast<u64>(program_id)].lock();
if (intf != nullptr) {
*out = intf;
return true;
}
return false;
}
static void StorageCacheSetEntry(ams::ncm::ProgramId program_id, std::shared_ptr<IStorageInterface> *ptr) {
std::scoped_lock lock(g_StorageCacheLock);
/* Ensure we always use the cached copy if present. */
if (g_StorageCache.find(static_cast<u64>(program_id)) != g_StorageCache.end()) {
auto intf = g_StorageCache[static_cast<u64>(program_id)].lock();
if (intf != nullptr) {
*ptr = intf;
}
}
g_StorageCache[static_cast<u64>(program_id)] = *ptr;
}
void FsMitmService::PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx) {
auto this_ptr = static_cast<FsMitmService *>(obj);
switch (static_cast<CommandId>(ctx->cmd_id)) {
case CommandId::SetCurrentProcess:
if (R_SUCCEEDED(ctx->rc)) {
this_ptr->has_initialized = true;
}
break;
default:
break;
}
}
Result FsMitmService::OpenHblWebContentFileSystem(Out<std::shared_ptr<IFileSystemInterface>> &out_fs) {
/* Mount the SD card using fs.mitm's session. */
FsFileSystem sd_fs;
R_TRY(fsMountSdcard(&sd_fs));
/* Set output filesystem. */
std::unique_ptr<IFileSystem> web_ifs = std::make_unique<SubDirectoryFileSystem>(std::make_shared<ProxyFileSystem>(sd_fs), AtmosphereHblWebContentDir);
out_fs.SetValue(std::make_shared<IFileSystemInterface>(std::move(web_ifs)));
if (out_fs.IsDomain()) {
out_fs.ChangeObjectId(sd_fs.s.object_id);
}
return ResultSuccess();
}
Result FsMitmService::OpenFileSystemWithPatch(Out<std::shared_ptr<IFileSystemInterface>> out_fs, u64 program_id, u32 filesystem_type) {
/* Check for eligibility. */
{
FsDir d;
if (!Utils::IsWebAppletTid(static_cast<u64>(this->program_id)) || filesystem_type != FsFileSystemType_ContentManual || !Utils::IsHblTid(program_id) ||
R_FAILED(Utils::OpenSdDir(AtmosphereHblWebContentDir, &d))) {
return ResultAtmosphereMitmShouldForwardToSession;
}
fsDirClose(&d);
}
/* If there's an existing filesystem, don't override. */
/* TODO: Multiplex, overriding existing content with HBL content. */
{
FsFileSystem fs;
if (R_SUCCEEDED(fsOpenFileSystemWithPatchFwd(this->forward_service.get(), &fs, program_id, static_cast<FsFileSystemType>(filesystem_type)))) {
fsFsClose(&fs);
return ResultAtmosphereMitmShouldForwardToSession;
}
}
return this->OpenHblWebContentFileSystem(out_fs);
}
Result FsMitmService::OpenFileSystemWithId(Out<std::shared_ptr<IFileSystemInterface>> out_fs, InPointer<char> path, u64 program_id, u32 filesystem_type) {
/* Check for eligibility. */
{
FsDir d;
if (!Utils::IsWebAppletTid(static_cast<u64>(this->program_id)) || filesystem_type != FsFileSystemType_ContentManual || !Utils::IsHblTid(program_id) ||
R_FAILED(Utils::OpenSdDir(AtmosphereHblWebContentDir, &d))) {
return ResultAtmosphereMitmShouldForwardToSession;
}
fsDirClose(&d);
}
/* If there's an existing filesystem, don't override. */
/* TODO: Multiplex, overriding existing content with HBL content. */
{
FsFileSystem fs;
if (R_SUCCEEDED(fsOpenFileSystemWithIdFwd(this->forward_service.get(), &fs, program_id, static_cast<FsFileSystemType>(filesystem_type), path.pointer))) {
fsFsClose(&fs);
return ResultAtmosphereMitmShouldForwardToSession;
}
}
return this->OpenHblWebContentFileSystem(out_fs);
}
Result FsMitmService::OpenSdCardFileSystem(Out<std::shared_ptr<IFileSystemInterface>> out_fs) {
/* We only care about redirecting this for NS/Emummc. */
if (this->program_id != ams::ncm::ProgramId::Ns) {
return ResultAtmosphereMitmShouldForwardToSession;
}
if (!IsEmummc()) {
return ResultAtmosphereMitmShouldForwardToSession;
}
/* Mount the SD card. */
FsFileSystem sd_fs;
R_TRY(fsMountSdcard(&sd_fs));
/* Set output filesystem. */
std::unique_ptr<IFileSystem> redir_fs = std::make_unique<DirectoryRedirectionFileSystem>(new ProxyFileSystem(sd_fs), "/Nintendo", GetEmummcNintendoDirPath());
out_fs.SetValue(std::make_shared<IFileSystemInterface>(std::move(redir_fs)));
if (out_fs.IsDomain()) {
out_fs.ChangeObjectId(sd_fs.s.object_id);
}
return ResultSuccess();
}
Result FsMitmService::OpenSaveDataFileSystem(Out<std::shared_ptr<IFileSystemInterface>> out_fs, u8 space_id, FsSave save_struct) {
bool should_redirect_saves = false;
const bool has_redirect_save_flags = Utils::HasFlag(static_cast<u64>(this->program_id), "redirect_save");
if (R_FAILED(Utils::GetSettingsItemBooleanValue("atmosphere", "fsmitm_redirect_saves_to_sd", &should_redirect_saves))) {
return ResultAtmosphereMitmShouldForwardToSession;
}
/* For now, until we're sure this is robust, only intercept normal savedata , check if flag exist*/
if (!has_redirect_save_flags || !should_redirect_saves || save_struct.saveDataType != FsSaveDataType_SaveData) {
return ResultAtmosphereMitmShouldForwardToSession;
}
/* Verify we can open the save. */
FsFileSystem save_fs;
if (R_FAILED(fsOpenSaveDataFileSystemFwd(this->forward_service.get(), &save_fs, space_id, &save_struct))) {
return ResultAtmosphereMitmShouldForwardToSession;
}
std::unique_ptr<IFileSystem> save_ifs = std::make_unique<ProxyFileSystem>(save_fs);
{
/* Mount the SD card using fs.mitm's session. */
FsFileSystem sd_fs;
R_TRY(fsMountSdcard(&sd_fs));
std::shared_ptr<IFileSystem> sd_ifs = std::make_shared<ProxyFileSystem>(sd_fs);
/* Verify that we can open the save directory, and that it exists. */
const u64 target_tid = save_struct.titleID == 0 ? static_cast<u64>(this->program_id) : save_struct.titleID;
FsPath save_dir_path;
R_TRY(FsSaveUtils::GetSaveDataDirectoryPath(save_dir_path, space_id, save_struct.saveDataType, target_tid, save_struct.userID, save_struct.saveID));
/* Check if this is the first time we're making the save. */
bool is_new_save = false;
{
DirectoryEntryType ent;
if (sd_ifs->GetEntryType(&ent, save_dir_path) == ResultFsPathNotFound) {
is_new_save = true;
}
}
/* Ensure the directory exists. */
R_TRY(FsDirUtils::EnsureDirectoryExists(sd_ifs.get(), save_dir_path));
std::shared_ptr<DirectorySaveDataFileSystem> dirsave_ifs = std::make_shared<DirectorySaveDataFileSystem>(new SubDirectoryFileSystem(sd_ifs, save_dir_path.str), std::move(save_ifs));
/* If it's the first time we're making the save, copy existing savedata over. */
if (is_new_save) {
/* TODO: Check error? */
dirsave_ifs->CopySaveFromProxy();
}
out_fs.SetValue(std::make_shared<IFileSystemInterface>(static_cast<std::shared_ptr<IFileSystem>>(dirsave_ifs)));
if (out_fs.IsDomain()) {
out_fs.ChangeObjectId(sd_fs.s.object_id);
}
return ResultSuccess();
}
}
/* Gate access to the BIS partitions. */
Result FsMitmService::OpenBisStorage(Out<std::shared_ptr<IStorageInterface>> out_storage, u32 _bis_partition_id) {
const FsBisStorageId bis_partition_id = static_cast<FsBisStorageId>(_bis_partition_id);
/* Try to open a storage for the partition. */
FsStorage bis_storage;
R_TRY(fsOpenBisStorageFwd(this->forward_service.get(), &bis_storage, bis_partition_id));
const bool is_sysmodule = ams::ncm::IsSystemProgramId(this->program_id);
const bool has_bis_write_flag = Utils::HasFlag(static_cast<u64>(this->program_id), "bis_write");
const bool has_cal0_read_flag = Utils::HasFlag(static_cast<u64>(this->program_id), "cal_read");
/* Set output storage. */
if (bis_partition_id == FsBisStorageId_Boot0) {
out_storage.SetValue(std::make_shared<IStorageInterface>(new Boot0Storage(bis_storage, this->program_id)));
} else if (bis_partition_id == FsBisStorageId_CalibrationBinary) {
/* PRODINFO should *never* be writable. */
if (is_sysmodule || has_cal0_read_flag) {
out_storage.SetValue(std::make_shared<IStorageInterface>(new ReadOnlyStorageAdapter(new ProxyStorage(bis_storage))));
} else {
/* Do not allow non-sysmodules to read *or* write CAL0. */
fsStorageClose(&bis_storage);
return ResultFsPermissionDenied;
}
} else {
if (is_sysmodule || has_bis_write_flag) {
/* Sysmodules should still be allowed to read and write. */
out_storage.SetValue(std::make_shared<IStorageInterface>(new ProxyStorage(bis_storage)));
} else if (Utils::IsHblTid(static_cast<u64>(this->program_id)) &&
((FsBisStorageId_BootConfigAndPackage2NormalMain <= bis_partition_id && bis_partition_id <= FsBisStorageId_BootConfigAndPackage2RepairSub) ||
bis_partition_id == FsBisStorageId_Boot1)) {
/* 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 :/ */
out_storage.SetValue(std::make_shared<IStorageInterface>(new ProxyStorage(bis_storage)));
} else {
/* Non-sysmodules should be allowed to read. */
out_storage.SetValue(std::make_shared<IStorageInterface>(new ReadOnlyStorageAdapter(new ProxyStorage(bis_storage))));
}
}
/* Copy domain id. */
if (out_storage.IsDomain()) {
out_storage.ChangeObjectId(bis_storage.s.object_id);
}
return ResultSuccess();
}
/* Add redirection for RomFS to the SD card. */
Result FsMitmService::OpenDataStorageByCurrentProcess(Out<std::shared_ptr<IStorageInterface>> out_storage) {
if (!this->should_override_contents) {
return ResultAtmosphereMitmShouldForwardToSession;
}
/* If we don't have anything to modify, there's no sense in maintaining a copy of the metadata tables. */
if (!Utils::HasSdRomfsContent(static_cast<u64>(this->program_id))) {
return ResultAtmosphereMitmShouldForwardToSession;
}
/* Try to get from the cache. */
{
std::shared_ptr<IStorageInterface> cached_storage = nullptr;
bool has_cache = StorageCacheGetEntry(this->program_id, &cached_storage);
if (has_cache) {
if (out_storage.IsDomain()) {
/* TODO: Don't leak object id? */
FsStorage s = {0};
R_TRY(fsOpenDataStorageByCurrentProcessFwd(this->forward_service.get(), &s));
out_storage.ChangeObjectId(s.s.object_id);
}
out_storage.SetValue(std::move(cached_storage));
return ResultSuccess();
}
}
/* Try to open process romfs. */
FsStorage data_storage;
R_TRY(fsOpenDataStorageByCurrentProcessFwd(this->forward_service.get(), &data_storage));
/* Make new layered romfs, cacheing to storage. */
{
std::shared_ptr<IStorageInterface> storage_to_cache = nullptr;
/* TODO: Is there a sensible path that ends in ".romfs" we can use?" */
FsFile data_file;
if (R_SUCCEEDED(Utils::OpenSdFileForAtmosphere(static_cast<u64>(this->program_id), "romfs.bin", FS_OPEN_READ, &data_file))) {
storage_to_cache = std::make_shared<IStorageInterface>(new LayeredRomFS(std::make_shared<ReadOnlyStorageAdapter>(new ProxyStorage(data_storage)), std::make_shared<ReadOnlyStorageAdapter>(new FileStorage(new ProxyFile(data_file))), static_cast<u64>(this->program_id)));
} else {
storage_to_cache = std::make_shared<IStorageInterface>(new LayeredRomFS(std::make_shared<ReadOnlyStorageAdapter>(new ProxyStorage(data_storage)), nullptr, static_cast<u64>(this->program_id)));
}
StorageCacheSetEntry(this->program_id, &storage_to_cache);
out_storage.SetValue(std::move(storage_to_cache));
if (out_storage.IsDomain()) {
out_storage.ChangeObjectId(data_storage.s.object_id);
}
}
return ResultSuccess();
}
/* Add redirection for System Data Archives to the SD card. */
Result FsMitmService::OpenDataStorageByDataId(Out<std::shared_ptr<IStorageInterface>> out_storage, u64 data_id, u8 sid) {
if (!this->should_override_contents) {
return ResultAtmosphereMitmShouldForwardToSession;
}
/* If we don't have anything to modify, there's no sense in maintaining a copy of the metadata tables. */
if (!Utils::HasSdRomfsContent(data_id)) {
return ResultAtmosphereMitmShouldForwardToSession;
}
FsStorageId storage_id = static_cast<FsStorageId>(sid);
/* Try to get from the cache. */
{
std::shared_ptr<IStorageInterface> cached_storage = nullptr;
bool has_cache = StorageCacheGetEntry(ams::ncm::ProgramId{data_id}, &cached_storage);
if (has_cache) {
if (out_storage.IsDomain()) {
/* TODO: Don't leak object id? */
FsStorage s = {0};
R_TRY(fsOpenDataStorageByDataIdFwd(this->forward_service.get(), storage_id, data_id, &s));
out_storage.ChangeObjectId(s.s.object_id);
}
out_storage.SetValue(std::move(cached_storage));
return ResultSuccess();
}
}
/* Try to open data storage. */
FsStorage data_storage;
R_TRY(fsOpenDataStorageByDataIdFwd(this->forward_service.get(), storage_id, data_id, &data_storage));
/* Make new layered romfs, cacheing to storage. */
{
std::shared_ptr<IStorageInterface> storage_to_cache = nullptr;
/* TODO: Is there a sensible path that ends in ".romfs" we can use?" */
FsFile data_file;
if (R_SUCCEEDED(Utils::OpenSdFileForAtmosphere(data_id, "romfs.bin", FS_OPEN_READ, &data_file))) {
storage_to_cache = std::make_shared<IStorageInterface>(new LayeredRomFS(std::make_shared<ReadOnlyStorageAdapter>(new ProxyStorage(data_storage)), std::make_shared<ReadOnlyStorageAdapter>(new FileStorage(new ProxyFile(data_file))), data_id));
} else {
storage_to_cache = std::make_shared<IStorageInterface>(new LayeredRomFS(std::make_shared<ReadOnlyStorageAdapter>(new ProxyStorage(data_storage)), nullptr, data_id));
}
StorageCacheSetEntry(ams::ncm::ProgramId{data_id}, &storage_to_cache);
out_storage.SetValue(std::move(storage_to_cache));
if (out_storage.IsDomain()) {
out_storage.ChangeObjectId(data_storage.s.object_id);
}
}
return ResultSuccess();
}

View file

@ -1,95 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "fs_istorage.hpp"
#include "fs_ifilesystem.hpp"
#include "../utils.hpp"
class FsMitmService : public IMitmServiceObject {
private:
enum class CommandId {
OpenFileSystemDeprecated = 0,
SetCurrentProcess = 1,
OpenFileSystemWithPatch = 7,
OpenFileSystemWithId = 8,
OpenSdCardFileSystem = 18,
OpenSaveDataFileSystem = 51,
OpenBisStorage = 12,
OpenDataStorageByCurrentProcess = 200,
OpenDataStorageByDataId = 202,
};
private:
static constexpr const char *AtmosphereHblWebContentDir = "/atmosphere/hbl_html";
private:
bool has_initialized = false;
bool should_override_contents;
public:
FsMitmService(std::shared_ptr<Service> s, u64 pid, ams::ncm::ProgramId tid) : IMitmServiceObject(s, pid, tid) {
if (Utils::HasSdDisableMitMFlag(static_cast<u64>(this->program_id))) {
this->should_override_contents = false;
} else {
this->should_override_contents = (this->program_id >= ams::ncm::ProgramId::ApplicationStart || Utils::HasSdMitMFlag(static_cast<u64>(this->program_id))) && Utils::HasOverrideButton(static_cast<u64>(this->program_id));
}
}
static bool ShouldMitm(u64 pid, ams::ncm::ProgramId tid) {
/* Don't Mitm KIPs */
if (pid < 0x50) {
return false;
}
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. */
if (tid == ams::ncm::ProgramId::AppletQlaunch || tid == ams::ncm::ProgramId::AppletMaintenanceMenu) {
has_launched_qlaunch = true;
}
return has_launched_qlaunch || tid == ams::ncm::ProgramId::Ns || tid >= ams::ncm::ProgramId::ApplicationStart || Utils::HasSdMitMFlag(static_cast<u64>(tid));
}
static void PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx);
private:
Result OpenHblWebContentFileSystem(Out<std::shared_ptr<IFileSystemInterface>> &out);
protected:
/* Overridden commands. */
Result OpenFileSystemWithPatch(Out<std::shared_ptr<IFileSystemInterface>> out, u64 program_id, u32 filesystem_type);
Result OpenFileSystemWithId(Out<std::shared_ptr<IFileSystemInterface>> out, InPointer<char> path, u64 program_id, u32 filesystem_type);
Result OpenSdCardFileSystem(Out<std::shared_ptr<IFileSystemInterface>> out);
Result OpenSaveDataFileSystem(Out<std::shared_ptr<IFileSystemInterface>> out, u8 space_id, FsSave save_struct);
Result OpenBisStorage(Out<std::shared_ptr<IStorageInterface>> out, u32 bis_partition_id);
Result OpenDataStorageByCurrentProcess(Out<std::shared_ptr<IStorageInterface>> out);
Result OpenDataStorageByDataId(Out<std::shared_ptr<IStorageInterface>> out, u64 data_id, u8 storage_id);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
/* TODO MAKE_SERVICE_COMMAND_META(FsMitmService, OpenFileSystemDeprecated), */
MAKE_SERVICE_COMMAND_META(FsMitmService, OpenFileSystemWithPatch, FirmwareVersion_200),
MAKE_SERVICE_COMMAND_META(FsMitmService, OpenFileSystemWithId, FirmwareVersion_200),
MAKE_SERVICE_COMMAND_META(FsMitmService, OpenSdCardFileSystem),
MAKE_SERVICE_COMMAND_META(FsMitmService, OpenSaveDataFileSystem),
MAKE_SERVICE_COMMAND_META(FsMitmService, OpenBisStorage),
MAKE_SERVICE_COMMAND_META(FsMitmService, OpenDataStorageByCurrentProcess),
MAKE_SERVICE_COMMAND_META(FsMitmService, OpenDataStorageByDataId),
};
};

View file

@ -1,69 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <unordered_map>
#include <switch.h>
#include <stratosphere.hpp>
#include "hid_shim.h"
#include "hid_mitm_service.hpp"
namespace {
std::unordered_map<u64, u64> g_hbl_map;
bool ShouldSetSystemExt(u64 process_id) {
return g_hbl_map.find(process_id) != g_hbl_map.end();
}
void AddSession(u64 process_id) {
if (g_hbl_map.find(process_id) != g_hbl_map.end()) {
g_hbl_map[process_id]++;
} else {
g_hbl_map[process_id] = 0;
}
}
void RemoveSession(u64 process_id) {
if ((--g_hbl_map[process_id]) == 0) {
g_hbl_map.erase(process_id);
}
}
}
HidMitmService::~HidMitmService() {
if (this->should_set_system_ext) {
RemoveSession(this->process_id);
}
}
void HidMitmService::PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx) {
/* Nothing to do here */
}
Result HidMitmService::SetSupportedNpadStyleSet(u64 applet_resource_user_id, u32 style_set, PidDescriptor pid_desc) {
if (!this->should_set_system_ext && (style_set & (TYPE_SYSTEM | TYPE_SYSTEM_EXT))) {
/* Guaranteed: hbmenu 3.1.1 will cause this to be true. */
/* This prevents setting this for non-HBL. */
this->should_set_system_ext = true;
AddSession(this->process_id);
}
if (ShouldSetSystemExt(this->process_id)) {
style_set |= TYPE_SYSTEM | TYPE_SYSTEM_EXT;
}
return hidSetSupportedNpadStyleSetFwd(this->forward_service.get(), this->process_id, applet_resource_user_id, static_cast<HidControllerType>(style_set));
}

View file

@ -1,54 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "../utils.hpp"
class HidMitmService : public IMitmServiceObject {
private:
enum class CommandId {
SetSupportedNpadStyleSet = 100,
};
private:
bool should_set_system_ext = false;
public:
HidMitmService(std::shared_ptr<Service> s, u64 pid, ams::ncm::ProgramId tid) : IMitmServiceObject(s, pid, tid) {
/* ... */
}
~HidMitmService();
static bool ShouldMitm(u64 pid, ams::ncm::ProgramId tid) {
/* TODO: Consider removing in Atmosphere 0.10.0/1.0.0. */
/* We will mitm:
* - hbl, to help homebrew not need to be recompiled.
*/
return Utils::IsHblTid(static_cast<u64>(tid));
}
static void PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx);
protected:
/* Overridden commands. */
Result SetSupportedNpadStyleSet(u64 applet_resource_user_id, u32 style_set, PidDescriptor pid_desc);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(HidMitmService, SetSupportedNpadStyleSet),
};
};

View file

@ -1,64 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <switch.h>
#include "hid_shim.h"
/* Command forwarders. */
Result hidSetSupportedNpadStyleSetFwd(Service* s, u64 process_id, u64 aruid, HidControllerType type) {
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
u32 type;
u64 AppletResourceUserId;
} *raw;
ipcSendPid(&c);
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 100;
raw->type = type;
raw->AppletResourceUserId = aruid;
/* Override Process ID. */
{
u32 *cmd_buf = (u32 *)armGetTls();
cmd_buf[3] = (u32)(process_id >> 0);
cmd_buf[4] = (u32)((process_id >> 32) & 0xFFFF) | 0xFFFE0000ul;
}
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
ipcParse(&r);
struct {
u64 magic;
u64 result;
} *resp = r.Raw;
rc = resp->result;
}
return rc;
}

View file

@ -1,19 +0,0 @@
/**
* @file hid_shim.h
* @brief Human Interface Devices Services (hid) IPC wrapper.
* @author SciresM
* @copyright libnx Authors
*/
#pragma once
#include <switch.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Command forwarders. */
Result hidSetSupportedNpadStyleSetFwd(Service* s, u64 process_id, u64 aruid, HidControllerType type);
#ifdef __cplusplus
}
#endif

View file

@ -1,56 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdlib>
#include <cstdint>
#include <cstring>
#include <malloc.h>
#include <switch.h>
#include <atmosphere.h>
#include <stratosphere.hpp>
#include "hidmitm_main.hpp"
#include "hid_mitm_service.hpp"
#include "../utils.hpp"
struct HidMitmManagerOptions {
static const size_t PointerBufferSize = 0x200;
static const size_t MaxDomains = 0x0;
static const size_t MaxDomainObjects = 0x0;
};
using HidMitmManager = WaitableManager<HidMitmManagerOptions>;
void HidMitmMain(void *arg) {
/* This is only necessary on 9.x+ */
if (GetRuntimeFirmwareVersion() < FirmwareVersion_900) {
return;
}
/* Ensure we can talk to HID. */
Utils::WaitHidAvailable();
/* Create server manager */
static auto s_server_manager = HidMitmManager(1);
/* Create hid mitm. */
/* Note: official HID passes 100 as sessions, despite this being > 0x40. */
AddMitmServerToManager<HidMitmService>(&s_server_manager, "hid", 100);
/* Loop forever, servicing our services. */
s_server_manager.Process();
}

View file

@ -1,24 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
constexpr u32 HidMitmPriority = 47;
constexpr u32 HidMitmStackSize = 0x8000;
void HidMitmMain(void *arg);

View file

@ -1,269 +0,0 @@
/* inih -- simple .INI file parser
inih is released under the New BSD license (see LICENSE.txt). Go to the project
home page for more info:
https://github.com/benhoyt/inih
*/
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "ini.h"
#if !INI_USE_STACK
#include <stdlib.h>
#endif
#define MAX_SECTION 72
#define MAX_NAME 72
/* Used by ini_parse_string() to keep track of string parsing state. */
typedef struct {
const char* ptr;
size_t num_left;
} ini_parse_string_ctx;
/* Strip whitespace chars off end of given string, in place. Return s. */
static char* rstrip(char* s)
{
char* p = s + strlen(s);
while (p > s && isspace((unsigned char)(*--p)))
*p = '\0';
return s;
}
/* Return pointer to first non-whitespace char in given string. */
static char* lskip(const char* s)
{
while (*s && isspace((unsigned char)(*s)))
s++;
return (char*)s;
}
/* Return pointer to first char (of chars) or inline comment in given string,
or pointer to null at end of string if neither found. Inline comment must
be prefixed by a whitespace character to register as a comment. */
static char* find_chars_or_comment(const char* s, const char* chars)
{
#if INI_ALLOW_INLINE_COMMENTS
int was_space = 0;
while (*s && (!chars || !strchr(chars, *s)) &&
!(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
was_space = isspace((unsigned char)(*s));
s++;
}
#else
while (*s && (!chars || !strchr(chars, *s))) {
s++;
}
#endif
return (char*)s;
}
/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
static char* strncpy0(char* dest, const char* src, size_t size)
{
strncpy(dest, src, size - 1);
dest[size - 1] = '\0';
return dest;
}
/* See documentation in header file. */
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
void* user)
{
/* Uses a fair bit of stack (use heap instead if you need to) */
#if INI_USE_STACK
char line[INI_MAX_LINE];
int max_line = INI_MAX_LINE;
#else
char* line;
int max_line = INI_INITIAL_ALLOC;
#endif
#if INI_ALLOW_REALLOC
char* new_line;
int offset;
#endif
char section[MAX_SECTION] = "";
char prev_name[MAX_NAME] = "";
char* start;
char* end;
char* name;
char* value;
int lineno = 0;
int error = 0;
#if !INI_USE_STACK
line = (char*)malloc(INI_INITIAL_ALLOC);
if (!line) {
return -2;
}
#endif
#if INI_HANDLER_LINENO
#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
#else
#define HANDLER(u, s, n, v) handler(u, s, n, v)
#endif
/* Scan through stream line by line */
while (reader(line, max_line, stream) != NULL) {
#if INI_ALLOW_REALLOC
offset = strlen(line);
while (offset == max_line - 1 && line[offset - 1] != '\n') {
max_line *= 2;
if (max_line > INI_MAX_LINE)
max_line = INI_MAX_LINE;
new_line = realloc(line, max_line);
if (!new_line) {
free(line);
return -2;
}
line = new_line;
if (reader(line + offset, max_line - offset, stream) == NULL)
break;
if (max_line >= INI_MAX_LINE)
break;
offset += strlen(line + offset);
}
#endif
lineno++;
start = line;
#if INI_ALLOW_BOM
if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
(unsigned char)start[1] == 0xBB &&
(unsigned char)start[2] == 0xBF) {
start += 3;
}
#endif
start = lskip(rstrip(start));
if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
/* Start-of-line comment */
}
#if INI_ALLOW_MULTILINE
else if (*prev_name && *start && start > line) {
/* Non-blank line with leading whitespace, treat as continuation
of previous name's value (as per Python configparser). */
if (!HANDLER(user, section, prev_name, start) && !error)
error = lineno;
}
#endif
else if (*start == '[') {
/* A "[section]" line */
end = find_chars_or_comment(start + 1, "]");
if (*end == ']') {
*end = '\0';
strncpy0(section, start + 1, sizeof(section));
*prev_name = '\0';
}
else if (!error) {
/* No ']' found on section line */
error = lineno;
}
}
else if (*start) {
/* Not a comment, must be a name[=:]value pair */
end = find_chars_or_comment(start, "=:");
if (*end == '=' || *end == ':') {
*end = '\0';
name = rstrip(start);
value = end + 1;
#if INI_ALLOW_INLINE_COMMENTS
end = find_chars_or_comment(value, NULL);
if (*end)
*end = '\0';
#endif
value = lskip(value);
rstrip(value);
/* Valid name[=:]value pair found, call handler */
strncpy0(prev_name, name, sizeof(prev_name));
if (!HANDLER(user, section, name, value) && !error)
error = lineno;
}
else if (!error) {
/* No '=' or ':' found on name[=:]value line */
error = lineno;
}
}
#if INI_STOP_ON_FIRST_ERROR
if (error)
break;
#endif
}
#if !INI_USE_STACK
free(line);
#endif
return error;
}
/* See documentation in header file. */
int ini_parse_file(FILE* file, ini_handler handler, void* user)
{
return ini_parse_stream((ini_reader)fgets, file, handler, user);
}
/* See documentation in header file. */
int ini_parse(const char* filename, ini_handler handler, void* user)
{
FILE* file;
int error;
file = fopen(filename, "r");
if (!file)
return -1;
error = ini_parse_file(file, handler, user);
fclose(file);
return error;
}
/* An ini_reader function to read the next line from a string buffer. This
is the fgets() equivalent used by ini_parse_string(). */
static char* ini_reader_string(char* str, int num, void* stream) {
ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream;
const char* ctx_ptr = ctx->ptr;
size_t ctx_num_left = ctx->num_left;
char* strp = str;
char c;
if (ctx_num_left == 0 || num < 2)
return NULL;
while (num > 1 && ctx_num_left != 0) {
c = *ctx_ptr++;
ctx_num_left--;
*strp++ = c;
if (c == '\n')
break;
num--;
}
*strp = '\0';
ctx->ptr = ctx_ptr;
ctx->num_left = ctx_num_left;
return str;
}
/* See documentation in header file. */
int ini_parse_string(const char* string, ini_handler handler, void* user) {
ini_parse_string_ctx ctx;
ctx.ptr = string;
ctx.num_left = strlen(string);
return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
user);
}

View file

@ -1,130 +0,0 @@
/* inih -- simple .INI file parser
inih is released under the New BSD license (see LICENSE.txt). Go to the project
home page for more info:
https://github.com/benhoyt/inih
*/
#ifndef __INI_H__
#define __INI_H__
/* Make this header file easier to include in C++ code */
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
/* Nonzero if ini_handler callback should accept lineno parameter. */
#ifndef INI_HANDLER_LINENO
#define INI_HANDLER_LINENO 0
#endif
/* Typedef for prototype of handler function. */
#if INI_HANDLER_LINENO
typedef int (*ini_handler)(void* user, const char* section,
const char* name, const char* value,
int lineno);
#else
typedef int (*ini_handler)(void* user, const char* section,
const char* name, const char* value);
#endif
/* Typedef for prototype of fgets-style reader function. */
typedef char* (*ini_reader)(char* str, int num, void* stream);
/* Parse given INI-style file. May have [section]s, name=value pairs
(whitespace stripped), and comments starting with ';' (semicolon). Section
is "" if name=value pair parsed before any section heading. name:value
pairs are also supported as a concession to Python's configparser.
For each name=value pair parsed, call handler function with given user
pointer as well as section, name, and value (data only valid for duration
of handler call). Handler should return nonzero on success, zero on error.
Returns 0 on success, line number of first error on parse error (doesn't
stop on first error), -1 on file open error, or -2 on memory allocation
error (only when INI_USE_STACK is zero).
*/
int ini_parse(const char* filename, ini_handler handler, void* user);
/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
close the file when it's finished -- the caller must do that. */
int ini_parse_file(FILE* file, ini_handler handler, void* user);
/* Same as ini_parse(), but takes an ini_reader function pointer instead of
filename. Used for implementing custom or string-based I/O (see also
ini_parse_string). */
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
void* user);
/* Same as ini_parse(), but takes a zero-terminated string with the INI data
instead of a file. Useful for parsing INI data from a network socket or
already in memory. */
int ini_parse_string(const char* string, ini_handler handler, void* user);
/* Nonzero to allow multi-line value parsing, in the style of Python's
configparser. If allowed, ini_parse() will call the handler with the same
name for each subsequent line parsed. */
#ifndef INI_ALLOW_MULTILINE
#define INI_ALLOW_MULTILINE 1
#endif
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
the file. See http://code.google.com/p/inih/issues/detail?id=21 */
#ifndef INI_ALLOW_BOM
#define INI_ALLOW_BOM 1
#endif
/* Chars that begin a start-of-line comment. Per Python configparser, allow
both ; and # comments at the start of a line by default. */
#ifndef INI_START_COMMENT_PREFIXES
#define INI_START_COMMENT_PREFIXES ";#"
#endif
/* Nonzero to allow inline comments (with valid inline comment characters
specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
Python 3.2+ configparser behaviour. */
#ifndef INI_ALLOW_INLINE_COMMENTS
#define INI_ALLOW_INLINE_COMMENTS 1
#endif
#ifndef INI_INLINE_COMMENT_PREFIXES
#define INI_INLINE_COMMENT_PREFIXES ";"
#endif
/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */
#ifndef INI_USE_STACK
#define INI_USE_STACK 1
#endif
/* Maximum line length for any line in INI file (stack or heap). Note that
this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */
#ifndef INI_MAX_LINE
#define INI_MAX_LINE 200
#endif
/* Nonzero to allow heap line buffer to grow via realloc(), zero for a
fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is
zero. */
#ifndef INI_ALLOW_REALLOC
#define INI_ALLOW_REALLOC 0
#endif
/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK
is zero. */
#ifndef INI_INITIAL_ALLOC
#define INI_INITIAL_ALLOC 200
#endif
/* Stop parsing on first error (default is to keep parsing). */
#ifndef INI_STOP_ON_FIRST_ERROR
#define INI_STOP_ON_FIRST_ERROR 0
#endif
#ifdef __cplusplus
}
#endif
#endif /* __INI_H__ */

View file

@ -1,189 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <switch.h>
#include "ns_shim.h"
/* Command forwarders. */
Result nsGetDocumentInterfaceFwd(Service* s, NsDocumentInterface* out) {
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
} *raw;
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 7999;
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
serviceCreateSubservice(&out->s, s, &r, 0);
}
}
return rc;
}
Result nsamGetApplicationContentPathFwd(Service* s, void* out, size_t out_size, u64 app_id, FsStorageId storage_id) {
IpcCommand c;
ipcInitialize(&c);
ipcAddRecvBuffer(&c, out, out_size, 0);
struct {
u64 magic;
u64 cmd_id;
u8 storage_id;
u64 app_id;
} *raw;
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 21;
raw->storage_id = storage_id;
raw->app_id = app_id;
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
}
return rc;
}
Result nsamResolveApplicationContentPathFwd(Service* s, u64 title_id, FsStorageId storage_id) {
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
u8 storage_id;
u64 title_id;
} *raw;
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 23;
raw->storage_id = storage_id;
raw->title_id = title_id;
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
}
return rc;
}
Result nsamGetRunningApplicationProgramIdFwd(Service* s, u64* out_tid, u64 app_id) {
if (hosversionBefore(6, 0, 0)) {
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
}
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
u64 app_id;
} *raw;
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 92;
raw->app_id = app_id;
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
u64 title_id;
} *resp;
serviceIpcParse(s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
if (out_tid) {
*out_tid = resp->title_id;
}
}
}
return rc;
}
/* Web forwarders */
Result nswebGetApplicationContentPath(NsDocumentInterface* doc, void* out, size_t out_size, u64 app_id, FsStorageId storage_id) {
return nsamGetApplicationContentPathFwd(&doc->s, out, out_size, app_id, storage_id);
}
Result nswebResolveApplicationContentPath(NsDocumentInterface* doc, u64 title_id, FsStorageId storage_id) {
return nsamResolveApplicationContentPathFwd(&doc->s, title_id, storage_id);
}
Result nswebGetRunningApplicationProgramId(NsDocumentInterface* doc, u64* out_tid, u64 app_id) {
return nsamGetRunningApplicationProgramIdFwd(&doc->s, out_tid, app_id);
}

View file

@ -1,31 +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, FsStorageId storage_id);
Result nsamResolveApplicationContentPathFwd(Service* s, u64 title_id, FsStorageId storage_id);
Result nsamGetRunningApplicationProgramIdFwd(Service* s, u64* out_tid, u64 app_id);
Result nswebGetApplicationContentPath(NsDocumentInterface* doc, void* out, size_t out_size, u64 app_id, FsStorageId storage_id);
Result nswebResolveApplicationContentPath(NsDocumentInterface* doc, u64 title_id, FsStorageId storage_id);
Result nswebGetRunningApplicationProgramId(NsDocumentInterface* doc, u64* out_tid, u64 app_id);
#ifdef __cplusplus
}
#endif

View file

@ -1,43 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mutex>
#include <switch.h>
#include <stratosphere.hpp>
#include "nsmitm_am_service.hpp"
#include "ns_shim.h"
void NsAmMitmService::PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx) {
/* Nothing to do here */
}
Result NsAmMitmService::GetApplicationContentPath(OutBuffer<u8> out_path, u64 app_id, u8 storage_type) {
return nsamGetApplicationContentPathFwd(this->forward_service.get(), out_path.buffer, out_path.num_elements, app_id, static_cast<FsStorageId>(storage_type));
}
Result NsAmMitmService::ResolveApplicationContentPath(u64 program_id, u8 storage_type) {
/* Always succeed for web applet asking about HBL. */
if (Utils::IsWebAppletTid(static_cast<u64>(this->program_id)) && Utils::IsHblTid(program_id)) {
nsamResolveApplicationContentPathFwd(this->forward_service.get(), program_id, static_cast<FsStorageId>(storage_type));
return ResultSuccess();
}
return nsamResolveApplicationContentPathFwd(this->forward_service.get(), program_id, static_cast<FsStorageId>(storage_type));
}
Result NsAmMitmService::GetRunningApplicationProgramId(Out<u64> out_tid, u64 app_id) {
return nsamGetRunningApplicationProgramIdFwd(this->forward_service.get(), out_tid.GetPointer(), app_id);
}

View file

@ -1,55 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "../utils.hpp"
class NsAmMitmService : public IMitmServiceObject {
private:
enum class CommandId {
GetApplicationContentPath = 21,
ResolveApplicationContentPath = 23,
GetRunningApplicationProgramId = 92,
};
public:
NsAmMitmService(std::shared_ptr<Service> s, u64 pid, ams::ncm::ProgramId tid) : IMitmServiceObject(s, pid, tid) {
/* ... */
}
static bool ShouldMitm(u64 pid, ams::ncm::ProgramId tid) {
/* We will mitm:
* - web applets, to facilitate hbl web browser launching.
*/
return Utils::IsWebAppletTid(static_cast<u64>(tid));
}
static void PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx);
protected:
/* Overridden commands. */
Result GetApplicationContentPath(OutBuffer<u8> out_path, u64 app_id, u8 storage_type);
Result ResolveApplicationContentPath(u64 program_id, u8 storage_type);
Result GetRunningApplicationProgramId(Out<u64> out_tid, u64 app_id);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(NsAmMitmService, GetApplicationContentPath),
MAKE_SERVICE_COMMAND_META(NsAmMitmService, ResolveApplicationContentPath),
MAKE_SERVICE_COMMAND_META(NsAmMitmService, GetRunningApplicationProgramId, FirmwareVersion_600),
};
};

View file

@ -1,56 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdlib>
#include <cstdint>
#include <cstring>
#include <malloc.h>
#include <switch.h>
#include <atmosphere.h>
#include <stratosphere.hpp>
#include "../utils.hpp"
#include "nsmitm_main.hpp"
#include "nsmitm_am_service.hpp"
#include "nsmitm_web_service.hpp"
void NsMitmMain(void *arg) {
/* Wait for initialization to occur */
Utils::WaitSdInitialized();
/* Ensure we can talk to NS. */
DoWithSmSession([&]() {
R_ASSERT(nsInitialize());
});
nsExit();
/* Create server manager */
static auto s_server_manager = WaitableManager(1);
/* Create ns mitm. */
if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) {
AddMitmServerToManager<NsAmMitmService>(&s_server_manager, "ns:am", 5);
} else {
AddMitmServerToManager<NsWebMitmService>(&s_server_manager, "ns:web", 5);
}
/* Loop forever, servicing our services. */
s_server_manager.Process();
}

View file

@ -1,29 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdlib>
#include <cstdint>
#include <cstring>
#include <malloc.h>
#include <switch.h>
#include <atmosphere.h>
#include <stratosphere.hpp>
constexpr u32 NsMitmPriority = 48;
constexpr u32 NsMitmStackSize = 0x4000;
void NsMitmMain(void *arg);

View file

@ -1,56 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mutex>
#include <switch.h>
#include <stratosphere.hpp>
#include "nsmitm_web_service.hpp"
void NsWebMitmService::PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx) {
/* Nothing to do here */
}
Result NsWebMitmService::GetDocumentInterface(Out<std::shared_ptr<NsDocumentService>> out_intf) {
/* Open a document interface. */
NsDocumentInterface doc;
R_TRY(nsGetDocumentInterfaceFwd(this->forward_service.get(), &doc));
/* Set output interface. */
out_intf.SetValue(std::move(std::make_shared<NsDocumentService>(static_cast<u64>(this->program_id), doc)));
if (out_intf.IsDomain()) {
out_intf.ChangeObjectId(doc.s.object_id);
}
return ResultSuccess();
}
Result NsDocumentService::GetApplicationContentPath(OutBuffer<u8> out_path, u64 app_id, u8 storage_type) {
return nswebGetApplicationContentPath(this->srv.get(), out_path.buffer, out_path.num_elements, app_id, static_cast<FsStorageId>(storage_type));
}
Result NsDocumentService::ResolveApplicationContentPath(u64 program_id, u8 storage_type) {
/* Always succeed for web applet asking about HBL. */
if (Utils::IsWebAppletTid(static_cast<u64>(this->program_id)) && Utils::IsHblTid(program_id)) {
nswebResolveApplicationContentPath(this->srv.get(), program_id, static_cast<FsStorageId>(storage_type));
return ResultSuccess();
}
return nswebResolveApplicationContentPath(this->srv.get(), program_id, static_cast<FsStorageId>(storage_type));
}
Result NsDocumentService::GetRunningApplicationProgramId(Out<u64> out_tid, u64 app_id) {
return nswebGetRunningApplicationProgramId(this->srv.get(), out_tid.GetPointer(), app_id);
}

View file

@ -1,90 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "../utils.hpp"
#include "ns_shim.h"
class NsDocumentService : public IServiceObject {
private:
enum class CommandId {
GetApplicationContentPath = 21,
ResolveApplicationContentPath = 23,
GetRunningApplicationProgramId = 92,
};
private:
u64 program_id;
std::unique_ptr<NsDocumentInterface> srv;
public:
NsDocumentService(u64 t, NsDocumentInterface *s) : program_id(t), srv(s) {
/* ... */
}
NsDocumentService(u64 t, std::unique_ptr<NsDocumentInterface> s) : program_id(t), srv(std::move(s)) {
/* ... */
}
NsDocumentService(u64 t, NsDocumentInterface s) : program_id(t) {
srv = std::make_unique<NsDocumentInterface>(s);
}
virtual ~NsDocumentService() {
serviceClose(&srv->s);
}
private:
/* Actual command API. */
Result GetApplicationContentPath(OutBuffer<u8> out_path, u64 app_id, u8 storage_type);
Result ResolveApplicationContentPath(u64 program_id, u8 storage_type);
Result GetRunningApplicationProgramId(Out<u64> out_tid, u64 app_id);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(NsDocumentService, GetApplicationContentPath),
MAKE_SERVICE_COMMAND_META(NsDocumentService, ResolveApplicationContentPath),
MAKE_SERVICE_COMMAND_META(NsDocumentService, GetRunningApplicationProgramId, FirmwareVersion_600),
};
};
class NsWebMitmService : public IMitmServiceObject {
private:
enum class CommandId {
GetDocumentInterface = 7999,
};
public:
NsWebMitmService(std::shared_ptr<Service> s, u64 pid, ams::ncm::ProgramId tid) : IMitmServiceObject(s, pid, tid) {
/* ... */
}
static bool ShouldMitm(u64 pid, ams::ncm::ProgramId tid) {
/* We will mitm:
* - web applets, to facilitate hbl web browser launching.
*/
return Utils::IsWebAppletTid(static_cast<u64>(tid));
}
static void PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx);
protected:
/* Overridden commands. */
Result GetDocumentInterface(Out<std::shared_ptr<NsDocumentService>> out_intf);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(NsWebMitmService, GetDocumentInterface, FirmwareVersion_300),
};
};

View file

@ -1,105 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mutex>
#include <algorithm>
#include <switch.h>
#include "set_mitm_service.hpp"
void SetMitmService::PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx) {
/* No commands need postprocessing. */
}
bool SetMitmService::IsValidLanguageCode(u64 lang_code) {
static constexpr u64 s_valid_language_codes[] = {
LanguageCode_Japanese,
LanguageCode_AmericanEnglish,
LanguageCode_French,
LanguageCode_German,
LanguageCode_Italian,
LanguageCode_Spanish,
LanguageCode_Chinese,
LanguageCode_Korean,
LanguageCode_Dutch,
LanguageCode_Portuguese,
LanguageCode_Russian,
LanguageCode_Taiwanese,
LanguageCode_BritishEnglish,
LanguageCode_CanadianFrench,
LanguageCode_LatinAmericanSpanish,
LanguageCode_SimplifiedChinese,
LanguageCode_TraditionalChinese,
};
size_t num_language_codes = ams::util::size(s_valid_language_codes);
if (GetRuntimeFirmwareVersion() < FirmwareVersion_400) {
/* 4.0.0 added simplified and traditional chinese. */
num_language_codes -= 2;
}
for (size_t i = 0; i < num_language_codes; i++) {
if (lang_code == s_valid_language_codes[i]) {
return true;
}
}
return false;
}
bool SetMitmService::IsValidRegionCode(u32 region_code) {
return region_code < RegionCode_Max;
}
Result SetMitmService::EnsureLocale() {
std::scoped_lock lk(this->lock);
if (!this->got_locale) {
std::memset(&this->locale, 0xCC, sizeof(this->locale));
if (this->program_id == ams::ncm::ProgramId::Ns) {
u64 app_pid = 0;
u64 app_tid = 0;
R_TRY(pmdmntGetApplicationPid(&app_pid));
R_TRY(pminfoGetProgramId(&app_tid, app_pid));
this->locale = Utils::GetTitleOverrideLocale(app_tid);
} else {
this->locale = Utils::GetTitleOverrideLocale(static_cast<u64>(this->program_id));
this->got_locale = true;
}
}
return ResultSuccess();
}
Result SetMitmService::GetLanguageCode(Out<u64> out_lang_code) {
this->EnsureLocale();
if (!IsValidLanguageCode(this->locale.language_code)) {
return ResultAtmosphereMitmShouldForwardToSession;
}
out_lang_code.SetValue(this->locale.language_code);
return ResultSuccess();
}
Result SetMitmService::GetRegionCode(Out<u32> out_region_code) {
this->EnsureLocale();
if (!IsValidRegionCode(this->locale.region_code)) {
return ResultAtmosphereMitmShouldForwardToSession;
}
out_region_code.SetValue(this->locale.region_code);
return ResultSuccess();
}

View file

@ -1,59 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "../utils.hpp"
class SetMitmService : public IMitmServiceObject {
private:
enum class CommandId {
GetLanguageCode = 0,
GetRegionCode = 4,
};
private:
ams::os::Mutex lock;
OverrideLocale locale;
bool got_locale;
public:
SetMitmService(std::shared_ptr<Service> s, u64 pid, ams::ncm::ProgramId tid) : IMitmServiceObject(s, pid, tid) {
this->got_locale = false;
}
static bool ShouldMitm(u64 pid, ams::ncm::ProgramId tid) {
/* Mitm all applications. */
return tid == ams::ncm::ProgramId::Ns || ams::ncm::IsApplicationProgramId(tid);
}
static void PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx);
protected:
static bool IsValidLanguageCode(u64 lang_code);
static bool IsValidRegionCode(u32 region_code);
Result EnsureLocale();
protected:
/* Overridden commands. */
Result GetLanguageCode(Out<u64> out_lang_code);
Result GetRegionCode(Out<u32> out_region_code);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(SetMitmService, GetLanguageCode),
MAKE_SERVICE_COMMAND_META(SetMitmService, GetRegionCode),
};
};

View file

@ -1,62 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdlib>
#include <cstdint>
#include <cstring>
#include <malloc.h>
#include <switch.h>
#include <atmosphere.h>
#include <stratosphere.hpp>
#include "setmitm_main.hpp"
#include "setsys_mitm_service.hpp"
#include "setsys_settings_items.hpp"
#include "setsys_firmware_version.hpp"
#include "set_mitm_service.hpp"
#include "../utils.hpp"
struct SetSysManagerOptions {
static const size_t PointerBufferSize = 0x100;
static const size_t MaxDomains = 4;
static const size_t MaxDomainObjects = 0x100;
};
using SetMitmManager = WaitableManager<SetSysManagerOptions>;
void SetMitmMain(void *arg) {
/* Wait for SD to initialize. */
Utils::WaitSdInitialized();
/* Initialize version manager. */
VersionManager::Initialize();
/* Create server manager */
static auto s_server_manager = SetMitmManager(4);
/* Create set:sys mitm. */
AddMitmServerToManager<SetSysMitmService>(&s_server_manager, "set:sys", 60);
/* Create set mitm. */
AddMitmServerToManager<SetMitmService>(&s_server_manager, "set", 60);
/* Loop forever, servicing our services. */
s_server_manager.Process();
}

View file

@ -1,29 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdlib>
#include <cstdint>
#include <cstring>
#include <malloc.h>
#include <switch.h>
#include <atmosphere.h>
#include <stratosphere.hpp>
constexpr u32 SetMitmPriority = 43;
constexpr u32 SetMitmStackSize = 0x8000;
void SetMitmMain(void *arg);

View file

@ -1,79 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mutex>
#include <switch.h>
#include "setsys_firmware_version.hpp"
static ams::os::Mutex g_version_mutex;
static bool g_got_version = false;
static SetSysFirmwareVersion g_ams_fw_version = {0};
static SetSysFirmwareVersion g_fw_version = {0};
void VersionManager::Initialize() {
std::scoped_lock lock(g_version_mutex);
if (g_got_version) {
return;
}
/* Mount firmware version data archive. */
R_ASSERT(romfsMountFromDataArchive(static_cast<u64>(ams::ncm::ProgramId::ArchiveSystemVersion), FsStorageId_NandSystem, "sysver"));
{
ON_SCOPE_EXIT { romfsUnmount("sysver"); };
SetSysFirmwareVersion fw_ver;
/* Firmware version file must exist. */
FILE *f = fopen("sysver:/file", "rb");
AMS_ASSERT(f != NULL);
ON_SCOPE_EXIT { fclose(f); };
/* Must be possible to read firmware version from file. */
AMS_ASSERT(fread(&fw_ver, sizeof(fw_ver), 1, f) == 1);
g_fw_version = fw_ver;
g_ams_fw_version = fw_ver;
}
/* Modify the output firmware version. */
{
u32 major, minor, micro;
GetAtmosphereApiVersion(&major, &minor, &micro, nullptr, nullptr);
const char emummc_char = IsEmummc() ? 'E' : 'S';
{
char display_version[sizeof(g_ams_fw_version.display_version)] = {0};
std::snprintf(display_version, sizeof(display_version), "%s|AMS %u.%u.%u|%c", g_ams_fw_version.display_version, major, minor, micro, emummc_char);
std::memcpy(g_ams_fw_version.display_version, display_version, sizeof(g_ams_fw_version.display_version));
}
}
g_got_version = true;
}
Result VersionManager::GetFirmwareVersion(ams::ncm::ProgramId program_id, SetSysFirmwareVersion *out) {
VersionManager::Initialize();
/* Report atmosphere string to qlaunch, maintenance and nothing else. */
if (program_id == ams::ncm::ProgramId::AppletQlaunch || program_id == ams::ncm::ProgramId::AppletMaintenanceMenu) {
*out = g_ams_fw_version;
} else {
*out = g_fw_version;
}
return ResultSuccess();
}

View file

@ -1,25 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
class VersionManager {
public:
static void Initialize();
static Result GetFirmwareVersion(ams::ncm::ProgramId program_id, SetSysFirmwareVersion *out);
};

View file

@ -1,100 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mutex>
#include <algorithm>
#include <switch.h>
#include "setsys_mitm_service.hpp"
#include "setsys_firmware_version.hpp"
#include "setsys_settings_items.hpp"
void SetSysMitmService::PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx) {
/* No commands need postprocessing. */
}
Result SetSysMitmService::GetFirmwareVersion(OutPointerWithServerSize<SetSysFirmwareVersion, 0x1> out) {
/* Get firmware version from manager. */
R_TRY(VersionManager::GetFirmwareVersion(this->program_id, out.pointer));
/* GetFirmwareVersion sanitizes these fields. */
out.pointer->revision_major = 0;
out.pointer->revision_minor = 0;
return ResultSuccess();
}
Result SetSysMitmService::GetFirmwareVersion2(OutPointerWithServerSize<SetSysFirmwareVersion, 0x1> out) {
return VersionManager::GetFirmwareVersion(this->program_id, out.pointer);
}
Result SetSysMitmService::GetSettingsItemValueSize(Out<u64> out_size, InPointer<char> in_name, InPointer<char> in_key) {
char name[SET_MAX_NAME_SIZE] = {0};
char key[SET_MAX_NAME_SIZE] = {0};
/* Validate name and key. */
R_TRY(SettingsItemManager::ValidateName(in_name.pointer));
R_TRY(SettingsItemManager::ValidateKey(in_key.pointer));
if (in_name.num_elements < SET_MAX_NAME_SIZE) {
strncpy(name, in_name.pointer, in_name.num_elements);
} else {
strncpy(name, in_name.pointer, SET_MAX_NAME_SIZE-1);
}
if (in_key.num_elements < SET_MAX_NAME_SIZE) {
strncpy(key, in_key.pointer, in_key.num_elements);
} else {
strncpy(key, in_key.pointer, SET_MAX_NAME_SIZE-1);
}
/* Try to get override setting, fall back to real setting. */
if (R_FAILED(SettingsItemManager::GetValueSize(name, key, out_size.GetPointer()))) {
R_TRY(setsysGetSettingsItemValueSize(name, key, out_size.GetPointer()));
}
return ResultSuccess();
}
Result SetSysMitmService::GetSettingsItemValue(Out<u64> out_size, OutBuffer<u8> out_value, InPointer<char> in_name, InPointer<char> in_key) {
char name[SET_MAX_NAME_SIZE] = {0};
char key[SET_MAX_NAME_SIZE] = {0};
/* Validate name and key. */
R_TRY(SettingsItemManager::ValidateName(in_name.pointer));
R_TRY(SettingsItemManager::ValidateKey(in_key.pointer));
if (out_value.buffer == nullptr) {
return ResultSettingsItemValueBufferNull;
}
if (in_name.num_elements < SET_MAX_NAME_SIZE) {
strncpy(name, in_name.pointer, in_name.num_elements);
} else {
strncpy(name, in_name.pointer, SET_MAX_NAME_SIZE-1);
}
if (in_key.num_elements < SET_MAX_NAME_SIZE) {
strncpy(key, in_key.pointer, in_key.num_elements);
} else {
strncpy(key, in_key.pointer, SET_MAX_NAME_SIZE-1);
}
/* Try to get override setting, fall back to real setting. */
if (R_FAILED(SettingsItemManager::GetValue(name, key, out_value.buffer, out_value.num_elements, out_size.GetPointer()))) {
R_TRY(setsysGetSettingsItemValueFwd(this->forward_service.get(), name, key, out_value.buffer, out_value.num_elements, out_size.GetPointer()));
}
return ResultSuccess();
}

View file

@ -1,56 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "setsys_shim.h"
class SetSysMitmService : public IMitmServiceObject {
private:
enum class CommandId {
GetFirmwareVersion = 3,
GetFirmwareVersion2 = 4,
GetSettingsItemValueSize = 37,
GetSettingsItemValue = 38,
};
public:
SetSysMitmService(std::shared_ptr<Service> s, u64 pid, ams::ncm::ProgramId tid) : IMitmServiceObject(s, pid, tid) {
/* ... */
}
static bool ShouldMitm(u64 pid, ams::ncm::ProgramId tid) {
/* Mitm everything. */
return true;
}
static void PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx);
protected:
/* Overridden commands. */
Result GetFirmwareVersion(OutPointerWithServerSize<SetSysFirmwareVersion, 0x1> out);
Result GetFirmwareVersion2(OutPointerWithServerSize<SetSysFirmwareVersion, 0x1> out);
Result GetSettingsItemValueSize(Out<u64> out_size, InPointer<char> name, InPointer<char> key);
Result GetSettingsItemValue(Out<u64> out_size, OutBuffer<u8> out_value, InPointer<char> name, InPointer<char> key);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(SetSysMitmService, GetFirmwareVersion),
MAKE_SERVICE_COMMAND_META(SetSysMitmService, GetFirmwareVersion2),
MAKE_SERVICE_COMMAND_META(SetSysMitmService, GetSettingsItemValueSize),
MAKE_SERVICE_COMMAND_META(SetSysMitmService, GetSettingsItemValue),
};
};

View file

@ -1,304 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mutex>
#include <algorithm>
#include <map>
#include <string>
#include <utility>
#include <switch.h>
#include <strings.h>
#include <ctype.h>
#include "setsys_settings_items.hpp"
#include "../utils.hpp"
#include "../ini.h"
struct SettingsItemValue {
size_t size;
u8 *data;
};
std::map<std::string, SettingsItemValue> g_settings_items;
static bool g_threw_fatal = false;
static ams::os::Thread g_fatal_thread;
static void FatalThreadFunc(void *arg) {
svcSleepThread(5000000000ULL);
fatalSimple(static_cast<Result>(reinterpret_cast<uintptr_t>(arg)));
}
static bool IsCorrectFormat(const char *str, size_t len) {
if (len > 0 && str[len - 1] == '.') {
return false;
}
for (size_t i = 0; i < len; i++) {
const char c = *(str++);
if ('a' <= c && c <= 'z') {
continue;
}
if ('0' <= c && c <= '9') {
continue;
}
if (c == '-' || c == '.' || c == '_') {
continue;
}
return false;
}
return true;
}
Result SettingsItemManager::ValidateName(const char *name, size_t max_size) {
if (name == nullptr) {
return ResultSettingsItemNameNull;
}
const size_t name_len = strnlen(name, std::min(max_size, MaxNameLength + 1));
if (name_len == 0) {
return ResultSettingsItemNameEmpty;
} else if (name_len > MaxNameLength) {
return ResultSettingsItemNameTooLong;
}
if (!IsCorrectFormat(name, name_len)) {
return ResultSettingsItemNameInvalidFormat;
}
return ResultSuccess();
}
Result SettingsItemManager::ValidateName(const char *name) {
return ValidateName(name, MaxNameLength + 1);
}
Result SettingsItemManager::ValidateKey(const char *key, size_t max_size) {
if (key == nullptr) {
return ResultSettingsItemKeyNull;
}
const size_t key_len = strnlen(key, std::min(max_size, MaxKeyLength + 1));
if (key_len == 0) {
return ResultSettingsItemKeyEmpty;
} else if (key_len > MaxKeyLength) {
return ResultSettingsItemKeyTooLong;
}
if (!IsCorrectFormat(key, key_len)) {
return ResultSettingsItemKeyInvalidFormat;
}
return ResultSuccess();
}
Result SettingsItemManager::ValidateKey(const char *key) {
return ValidateKey(key, MaxKeyLength + 1);
}
static bool IsHexadecimal(const char *str) {
while (*str) {
if (isxdigit((unsigned char)*str)) {
str++;
} else {
return false;
}
}
return true;
}
static char hextoi(char c) {
if ('a' <= c && c <= 'f') return c - 'a' + 0xA;
if ('A' <= c && c <= 'F') return c - 'A' + 0xA;
if ('0' <= c && c <= '9') return c - '0';
return 0;
}
static Result ParseValue(const char *name, const char *key, const char *val_tup) {
const char *delimiter = strchr(val_tup, '!');
const char *value_str = delimiter + 1;
const char *type = val_tup;
if (delimiter == NULL) {
return ResultSettingsItemValueInvalidFormat;
}
while (isspace((unsigned char)*type) && type != delimiter) {
type++;
}
size_t type_len = delimiter - type;
size_t value_len = strlen(value_str);
if (delimiter == NULL || value_len == 0 || type_len == 0) {
return ResultSettingsItemValueInvalidFormat;
}
std::string kv = std::string(name).append("!").append(key);
SettingsItemValue value;
if (strncasecmp(type, "str", type_len) == 0 || strncasecmp(type, "string", type_len) == 0) {
/* String */
value.size = value_len + 1;
value.data = reinterpret_cast<u8 *>(strdup(value_str));
if (value.data == nullptr) {
return ResultSettingsItemValueAllocationFailed;
}
} else if (strncasecmp(type, "hex", type_len) == 0 || strncasecmp(type, "bytes", type_len) == 0) {
/* hex */
if (value_len % 2 || !IsHexadecimal(value_str)) {
return ResultSettingsItemValueInvalidFormat;
}
value.size = value_len / 2;
u8 *data = reinterpret_cast<u8 *>(malloc(value.size));
if (data == nullptr) {
return ResultSettingsItemValueAllocationFailed;
}
memset(data, 0, value.size);
for (size_t i = 0; i < value_len; i++) {
data[i >> 1] |= hextoi(value_str[i]) << (4 * (i & 1));
}
value.data = data;
} else if (strncasecmp(type, "u8", type_len) == 0) {
/* u8 */
value.size = sizeof(u8);
u8 *data = reinterpret_cast<u8 *>(malloc(value.size));
if (data == nullptr) {
return ResultSettingsItemValueAllocationFailed;
}
*data = (u8)(strtoul(value_str, nullptr, 0));
value.data = reinterpret_cast<u8 *>(data);
} else if (strncasecmp(type, "u16", type_len) == 0) {
/* u16 */
value.size = sizeof(u16);
u16 *data = reinterpret_cast<u16 *>(malloc(value.size));
if (data == nullptr) {
return ResultSettingsItemValueAllocationFailed;
}
*data = (u16)(strtoul(value_str, nullptr, 0));
value.data = reinterpret_cast<u8 *>(data);
} else if (strncasecmp(type, "u32", type_len) == 0) {
/* u32 */
value.size = sizeof(u32);
u32 *data = reinterpret_cast<u32 *>(malloc(value.size));
if (data == nullptr) {
return ResultSettingsItemValueAllocationFailed;
}
*data = (u32)(strtoul(value_str, nullptr, 0));
value.data = reinterpret_cast<u8 *>(data);
} else if (strncasecmp(type, "u64", type_len) == 0) {
/* u64 */
value.size = sizeof(u64);
u64 *data = reinterpret_cast<u64 *>(malloc(value.size));
if (data == nullptr) {
return ResultSettingsItemValueAllocationFailed;
}
*data = (u64)(strtoul(value_str, nullptr, 0));
value.data = reinterpret_cast<u8 *>(data);
} else {
return ResultSettingsItemValueInvalidFormat;
}
g_settings_items[kv] = value;
return ResultSuccess();
}
static Result ParseSettingsItemValue(const char *name, const char *key, const char *value) {
/* Validate name and key, then parse value. */
R_TRY(SettingsItemManager::ValidateName(name));
R_TRY(SettingsItemManager::ValidateKey(name));
R_TRY(ParseValue(name, key, value));
return ResultSuccess();
}
static int SettingsItemIniHandler(void *user, const char *name, const char *key, const char *value) {
Result *user_res = reinterpret_cast<Result *>(user);
/* Stop parsing after we fail to parse a value. */
if (R_FAILED(*user_res)) {
return 0;
}
*user_res = ParseSettingsItemValue(name, key, value);
return R_SUCCEEDED(*user_res) ? 1 : 0;
}
static Result LoadConfigurationImpl() {
/* Open file. */
FsFile config_file;
R_TRY(Utils::OpenSdFile("/atmosphere/system_settings.ini", FS_OPEN_READ, &config_file));
ON_SCOPE_EXIT {
fsFileClose(&config_file);
};
/* Allocate buffer. */
std::string config_buf(0xFFFF, '\0');
/* Read from file. */
size_t actual_size;
R_TRY(fsFileRead(&config_file, 0, config_buf.data(), config_buf.size(), FS_READOPTION_NONE, &actual_size));
/* Parse. */
Result parse_res = ResultSuccess;
ini_parse_string(config_buf.c_str(), SettingsItemIniHandler, &parse_res);
return parse_res;
}
void SettingsItemManager::LoadConfiguration() {
const Result load_res = LoadConfigurationImpl();
if (R_FAILED(load_res) && !g_threw_fatal) {
/* Report error if we encountered one. */
g_threw_fatal = true;
g_fatal_thread.Initialize(&FatalThreadFunc, reinterpret_cast<void *>(load_res), 0x1000, 49);
g_fatal_thread.Start();
}
}
Result SettingsItemManager::GetValueSize(const char *name, const char *key, u64 *out_size) {
const std::string kv = std::string(name).append("!").append(key);
auto it = g_settings_items.find(kv);
if (it == g_settings_items.end()) {
return ResultSettingsItemNotFound;
}
*out_size = it->second.size;
return ResultSuccess();
}
Result SettingsItemManager::GetValue(const char *name, const char *key, void *out, size_t max_size, u64 *out_size) {
const std::string kv = std::string(name).append("!").append(key);
auto it = g_settings_items.find(kv);
if (it == g_settings_items.end()) {
return ResultSettingsItemNotFound;
}
size_t copy_size = it->second.size;
if (max_size < copy_size) {
copy_size = max_size;
}
*out_size = copy_size;
memcpy(out, it->second.data, copy_size);
return ResultSuccess();
}

View file

@ -1,36 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
class SettingsItemManager {
public:
static constexpr size_t MaxNameLength = 64;
static constexpr size_t MaxKeyLength = 64;
public:
static Result ValidateName(const char *name, size_t max_size);
static Result ValidateName(const char *name);
static Result ValidateKey(const char *key, size_t max_size);
static Result ValidateKey(const char *key);
static void LoadConfiguration();
static Result GetValueSize(const char *name, const char *key, u64 *out_size);
static Result GetValue(const char *name, const char *key, void *out, size_t max_size, u64 *out_size);
};

View file

@ -1,67 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <switch.h>
#include "setsys_shim.h"
/* Command forwarders. */
Result setsysGetSettingsItemValueFwd(Service *s, const char *name, const char *item_key, void *value_out, size_t value_out_size, u64 *size_out) {
char send_name[SET_MAX_NAME_SIZE];
char send_item_key[SET_MAX_NAME_SIZE];
memset(send_name, 0, SET_MAX_NAME_SIZE);
memset(send_item_key, 0, SET_MAX_NAME_SIZE);
strncpy(send_name, name, SET_MAX_NAME_SIZE-1);
strncpy(send_item_key, item_key, SET_MAX_NAME_SIZE-1);
IpcCommand c;
ipcInitialize(&c);
ipcAddSendStatic(&c, send_name, SET_MAX_NAME_SIZE, 0);
ipcAddSendStatic(&c, send_item_key, SET_MAX_NAME_SIZE, 0);
ipcAddRecvBuffer(&c, value_out, value_out_size, 0);
struct {
u64 magic;
u64 cmd_id;
} *raw;
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 38;
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
u64 size_out;
} *resp;
serviceIpcParse(s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
*size_out = resp->size_out;
}
}
return rc;
}

View file

@ -1,19 +0,0 @@
/**
* @file setsys_shim.h
* @brief System Settings Services (set:sys) IPC wrapper. To be merged into libnx, eventually.
* @author SciresM
* @copyright libnx Authors
*/
#pragma once
#include <switch.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Command forwarders. */
Result setsysGetSettingsItemValueFwd(Service* s, const char *name, const char *item_key, void *value_out, size_t value_out_size, u64 *size_out);
#ifdef __cplusplus
}
#endif

View file

@ -1,801 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include <stratosphere.hpp>
#include <stratosphere/spl.hpp>
#include <atomic>
#include <algorithm>
#include <strings.h>
#include "debug.hpp"
#include "utils.hpp"
#include "ini.h"
#include "set_mitm/setsys_settings_items.hpp"
#include "bpc_mitm/bpcmitm_reboot_manager.hpp"
static FsFileSystem g_sd_filesystem = {0};
/* Non-autoclear events for SD/HID init. */
static ams::os::Event g_sd_event(false), g_hid_event(false);
static std::vector<u64> g_mitm_flagged_tids;
static std::vector<u64> g_disable_mitm_flagged_tids;
static std::atomic_bool g_has_initialized = false;
static std::atomic_bool g_has_hid_session = false;
/* Content override support variables/types */
static OverrideKey g_default_override_key = {
.key_combination = KEY_L,
.override_by_default = true
};
struct HblOverrideConfig {
OverrideKey override_key;
u64 program_id;
bool override_any_app;
};
static HblOverrideConfig g_hbl_override_config = {
.override_key = {
.key_combination = KEY_R,
.override_by_default = false
},
.program_id = static_cast<u64>(ams::ncm::ProgramId::AppletPhotoViewer),
.override_any_app = true
};
/* Static buffer for loader.ini contents at runtime. */
static char g_config_ini_data[0x800];
/* Backup file for CAL0 partition. */
static constexpr size_t ProdinfoSize = 0x8000;
static FsFile g_cal0_file = {};
static FsFile g_bis_key_file = {};
static u8 g_cal0_storage_backup[ProdinfoSize];
static u8 g_cal0_backup[ProdinfoSize];
/* Emummc-related file. */
static FsFile g_emummc_file = {0};
static bool IsHexadecimal(const char *str) {
while (*str) {
if (isxdigit((unsigned char)*str)) {
str++;
} else {
return false;
}
}
return true;
}
void Utils::InitializeThreadFunc(void *args) {
/* Get required services. */
DoWithSmSession([&]() {
Handle tmp_hnd = 0;
static const char * const required_active_services[] = {"pcv", "gpio", "pinmux", "psc:m"};
for (unsigned int i = 0; i < ams::util::size(required_active_services); i++) {
R_ASSERT(smGetServiceOriginal(&tmp_hnd, smEncodeName(required_active_services[i])));
svcCloseHandle(tmp_hnd);
}
});
/* Mount SD. */
while (R_FAILED(fsMountSdcard(&g_sd_filesystem))) {
svcSleepThread(1000000ULL);
}
/* Back up CAL0, if it's not backed up already. */
fsFsCreateDirectory(&g_sd_filesystem, "/atmosphere/automatic_backups");
{
FsStorage cal0_storage;
R_ASSERT(fsOpenBisStorage(&cal0_storage, FsBisStorageId_CalibrationBinary));
R_ASSERT(fsStorageRead(&cal0_storage, 0, g_cal0_storage_backup, ProdinfoSize));
fsStorageClose(&cal0_storage);
char serial_number[0x40] = {0};
memcpy(serial_number, g_cal0_storage_backup + 0x250, 0x18);
/* Automatically backup PRODINFO. */
{
char prodinfo_backup_path[FS_MAX_PATH] = {0};
if (strlen(serial_number) > 0) {
snprintf(prodinfo_backup_path, sizeof(prodinfo_backup_path) - 1, "/atmosphere/automatic_backups/%s_PRODINFO.bin", serial_number);
} else {
snprintf(prodinfo_backup_path, sizeof(prodinfo_backup_path) - 1, "/atmosphere/automatic_backups/PRODINFO.bin");
}
fsFsCreateFile(&g_sd_filesystem, prodinfo_backup_path, ProdinfoSize, 0);
if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, prodinfo_backup_path, FS_OPEN_READ | FS_OPEN_WRITE, &g_cal0_file))) {
bool has_auto_backup = false;
size_t read = 0;
if (R_SUCCEEDED(fsFileRead(&g_cal0_file, 0, g_cal0_backup, sizeof(g_cal0_backup), FS_READOPTION_NONE, &read)) && read == sizeof(g_cal0_backup)) {
bool is_cal0_valid = true;
is_cal0_valid &= memcmp(g_cal0_backup, "CAL0", 4) == 0;
is_cal0_valid &= memcmp(g_cal0_backup + 0x250, serial_number, 0x18) == 0;
u32 cal0_size = ((u32 *)g_cal0_backup)[2];
is_cal0_valid &= cal0_size + 0x40 <= ProdinfoSize;
if (is_cal0_valid) {
u8 calc_hash[0x20];
sha256CalculateHash(calc_hash, g_cal0_backup + 0x40, cal0_size);
is_cal0_valid &= memcmp(calc_hash, g_cal0_backup + 0x20, sizeof(calc_hash)) == 0;
}
has_auto_backup = is_cal0_valid;
}
if (!has_auto_backup) {
fsFileSetSize(&g_cal0_file, ProdinfoSize);
fsFileWrite(&g_cal0_file, 0, g_cal0_storage_backup, ProdinfoSize, FS_WRITEOPTION_FLUSH);
}
/* NOTE: g_cal0_file is intentionally not closed here. This prevents any other process from opening it. */
memset(g_cal0_storage_backup, 0, sizeof(g_cal0_storage_backup));
memset(g_cal0_backup, 0, sizeof(g_cal0_backup));
}
}
/* Automatically backup BIS keys. */
{
u64 key_generation = 0;
if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) {
R_ASSERT(splGetConfig(SplConfigItem_NewKeyGeneration, &key_generation));
}
static constexpr u8 const 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}
}
};
u8 bis_keys[4][2][0x10] = {};
/* TODO: Clean this up in ams_mitm refactor. */
for (size_t partition = 0; partition < 4; partition++) {
if (partition == 0) {
for (size_t i = 0; i < 2; i++) {
R_ASSERT(splFsGenerateSpecificAesKey(BisKeySources[partition][i], key_generation, i, bis_keys[partition][i]));
}
} else {
static constexpr u8 const BisKekSource[0x10] = {
0x34, 0xC1, 0xA0, 0xC4, 0x82, 0x58, 0xF8, 0xB4, 0xFA, 0x9E, 0x5E, 0x6A, 0xDA, 0xFC, 0x7E, 0x4F,
};
const u32 option = (partition == 3 && ams::spl::IsRecoveryBoot()) ? 0x4 : 0x1;
u8 access_key[0x10];
R_ASSERT(splCryptoGenerateAesKek(BisKekSource, key_generation, option, access_key));
for (size_t i = 0; i < 2; i++) {
R_ASSERT(splCryptoGenerateAesKey(access_key, BisKeySources[partition][i], bis_keys[partition][i]));
}
}
}
char bis_key_backup_path[FS_MAX_PATH] = {0};
if (strlen(serial_number) > 0) {
snprintf(bis_key_backup_path, sizeof(bis_key_backup_path) - 1, "/atmosphere/automatic_backups/%s_BISKEYS.bin", serial_number);
} else {
snprintf(bis_key_backup_path, sizeof(bis_key_backup_path) - 1, "/atmosphere/automatic_backups/BISKEYS.bin");
}
fsFsCreateFile(&g_sd_filesystem, bis_key_backup_path, sizeof(bis_keys), 0);
if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, bis_key_backup_path, FS_OPEN_READ | FS_OPEN_WRITE, &g_bis_key_file))) {
fsFileSetSize(&g_bis_key_file, sizeof(bis_keys));
fsFileWrite(&g_bis_key_file, 0, bis_keys, sizeof(bis_keys), FS_WRITEOPTION_FLUSH);
/* NOTE: g_bis_key_file is intentionally not closed here. This prevents any other process from opening it. */
}
}
}
/* Check for MitM flags. */
FsDir titles_dir;
if (R_SUCCEEDED(fsFsOpenDirectory(&g_sd_filesystem, "/atmosphere/titles", FS_DIROPEN_DIRECTORY, &titles_dir))) {
FsDirectoryEntry dir_entry;
FsFile f;
u64 read_entries;
while (R_SUCCEEDED((fsDirRead(&titles_dir, 0, &read_entries, 1, &dir_entry))) && read_entries == 1) {
if (strlen(dir_entry.name) == 0x10 && IsHexadecimal(dir_entry.name)) {
u64 program_id = strtoul(dir_entry.name, NULL, 16);
char title_path[FS_MAX_PATH] = {0};
strcpy(title_path, "/atmosphere/titles/");
strcat(title_path, dir_entry.name);
strcat(title_path, "/flags/fsmitm.flag");
if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, title_path, FS_OPEN_READ, &f))) {
g_mitm_flagged_tids.push_back(program_id);
fsFileClose(&f);
} else {
/* TODO: Deprecate. */
memset(title_path, 0, sizeof(title_path));
strcpy(title_path, "/atmosphere/titles/");
strcat(title_path, dir_entry.name);
strcat(title_path, "/fsmitm.flag");
if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, title_path, FS_OPEN_READ, &f))) {
g_mitm_flagged_tids.push_back(program_id);
fsFileClose(&f);
}
}
memset(title_path, 0, sizeof(title_path));
strcpy(title_path, "/atmosphere/titles/");
strcat(title_path, dir_entry.name);
strcat(title_path, "/flags/fsmitm_disable.flag");
if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, title_path, FS_OPEN_READ, &f))) {
g_disable_mitm_flagged_tids.push_back(program_id);
fsFileClose(&f);
} else {
/* TODO: Deprecate. */
memset(title_path, 0, sizeof(title_path));
strcpy(title_path, "/atmosphere/titles/");
strcat(title_path, dir_entry.name);
strcat(title_path, "/fsmitm_disable.flag");
if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, title_path, FS_OPEN_READ, &f))) {
g_disable_mitm_flagged_tids.push_back(program_id);
fsFileClose(&f);
}
}
}
}
fsDirClose(&titles_dir);
}
Utils::RefreshConfiguration();
/* If we're emummc, persist a write handle to prevent other processes from touching the image. */
if (IsEmummc()) {
const char *emummc_file_path = GetEmummcFilePath();
if (emummc_file_path != nullptr) {
char emummc_path[0x100] = {0};
std::strncpy(emummc_path, emummc_file_path, 0x80);
std::strcat(emummc_path, "/eMMC");
fsFsOpenFile(&g_sd_filesystem, emummc_file_path, FS_OPEN_READ | FS_OPEN_WRITE, &g_emummc_file);
}
}
/* Initialize set:sys. */
DoWithSmSession([&]() {
R_ASSERT(setsysInitialize());
});
/* Signal SD is initialized. */
g_has_initialized = true;
/* Load custom settings configuration. */
SettingsItemManager::LoadConfiguration();
/* Signal to waiters that we are ready. */
g_sd_event.Signal();
/* Initialize HID. */
while (!g_has_hid_session) {
DoWithSmSession([&]() {
if (R_SUCCEEDED(hidInitialize())) {
g_has_hid_session = true;
}
});
if (!g_has_hid_session) {
svcSleepThread(1000000ULL);
}
}
/* Signal to waiters that we are ready. */
g_hid_event.Signal();
}
bool Utils::IsSdInitialized() {
return g_has_initialized;
}
void Utils::WaitSdInitialized() {
g_sd_event.Wait();
}
bool Utils::IsHidAvailable() {
return g_has_hid_session;
}
void Utils::WaitHidAvailable() {
g_hid_event.Wait();
}
Result Utils::OpenSdFile(const char *fn, int flags, FsFile *out) {
if (!IsSdInitialized()) {
return ResultFsSdCardNotPresent;
}
return fsFsOpenFile(&g_sd_filesystem, fn, flags, out);
}
Result Utils::OpenSdFileForAtmosphere(u64 program_id, const char *fn, int flags, FsFile *out) {
if (!IsSdInitialized()) {
return ResultFsSdCardNotPresent;
}
char path[FS_MAX_PATH];
if (*fn == '/') {
snprintf(path, sizeof(path), "/atmosphere/titles/%016lx%s", program_id, fn);
} else {
snprintf(path, sizeof(path), "/atmosphere/titles/%016lx/%s", program_id, fn);
}
return fsFsOpenFile(&g_sd_filesystem, path, flags, out);
}
Result Utils::OpenRomFSSdFile(u64 program_id, const char *fn, int flags, FsFile *out) {
if (!IsSdInitialized()) {
return ResultFsSdCardNotPresent;
}
return OpenRomFSFile(&g_sd_filesystem, program_id, fn, flags, out);
}
Result Utils::OpenSdDir(const char *path, FsDir *out) {
if (!IsSdInitialized()) {
return ResultFsSdCardNotPresent;
}
return fsFsOpenDirectory(&g_sd_filesystem, path, FS_DIROPEN_DIRECTORY | FS_DIROPEN_FILE, out);
}
Result Utils::OpenSdDirForAtmosphere(u64 program_id, const char *path, FsDir *out) {
if (!IsSdInitialized()) {
return ResultFsSdCardNotPresent;
}
char safe_path[FS_MAX_PATH];
if (*path == '/') {
snprintf(safe_path, sizeof(safe_path), "/atmosphere/titles/%016lx%s", program_id, path);
} else {
snprintf(safe_path, sizeof(safe_path), "/atmosphere/titles/%016lx/%s", program_id, path);
}
return fsFsOpenDirectory(&g_sd_filesystem, safe_path, FS_DIROPEN_DIRECTORY | FS_DIROPEN_FILE, out);
}
Result Utils::OpenRomFSSdDir(u64 program_id, const char *path, FsDir *out) {
if (!IsSdInitialized()) {
return ResultFsSdCardNotPresent;
}
return OpenRomFSDir(&g_sd_filesystem, program_id, path, out);
}
Result Utils::OpenRomFSFile(FsFileSystem *fs, u64 program_id, const char *fn, int flags, FsFile *out) {
char path[FS_MAX_PATH];
if (*fn == '/') {
snprintf(path, sizeof(path), "/atmosphere/titles/%016lx/romfs%s", program_id, fn);
} else {
snprintf(path, sizeof(path), "/atmosphere/titles/%016lx/romfs/%s", program_id, fn);
}
return fsFsOpenFile(fs, path, flags, out);
}
Result Utils::OpenRomFSDir(FsFileSystem *fs, u64 program_id, const char *path, FsDir *out) {
char safe_path[FS_MAX_PATH];
if (*path == '/') {
snprintf(safe_path, sizeof(safe_path), "/atmosphere/titles/%016lx/romfs%s", program_id, path);
} else {
snprintf(safe_path, sizeof(safe_path), "/atmosphere/titles/%016lx/romfs/%s", program_id, path);
}
return fsFsOpenDirectory(fs, safe_path, FS_DIROPEN_DIRECTORY | FS_DIROPEN_FILE, out);
}
bool Utils::HasSdRomfsContent(u64 program_id) {
/* Check for romfs.bin. */
FsFile data_file;
if (R_SUCCEEDED(Utils::OpenSdFileForAtmosphere(program_id, "romfs.bin", FS_OPEN_READ, &data_file))) {
fsFileClose(&data_file);
return true;
}
/* Check for romfs folder with non-zero content. */
FsDir dir;
if (R_FAILED(Utils::OpenRomFSSdDir(program_id, "", &dir))) {
return false;
}
ON_SCOPE_EXIT {
fsDirClose(&dir);
};
FsDirectoryEntry dir_entry;
u64 read_entries;
return R_SUCCEEDED(fsDirRead(&dir, 0, &read_entries, 1, &dir_entry)) && read_entries == 1;
}
Result Utils::SaveSdFileForAtmosphere(u64 program_id, const char *fn, void *data, size_t size) {
if (!IsSdInitialized()) {
return ResultFsSdCardNotPresent;
}
char path[FS_MAX_PATH];
if (*fn == '/') {
snprintf(path, sizeof(path), "/atmosphere/titles/%016lx%s", program_id, fn);
} else {
snprintf(path, sizeof(path), "/atmosphere/titles/%016lx/%s", program_id, fn);
}
/* Unconditionally create. */
FsFile f;
fsFsCreateFile(&g_sd_filesystem, path, size, 0);
/* Try to open. */
R_TRY(fsFsOpenFile(&g_sd_filesystem, path, FS_OPEN_READ | FS_OPEN_WRITE, &f));
ON_SCOPE_EXIT { fsFileClose(&f); };
/* Try to make it big enough. */
R_TRY(fsFileSetSize(&f, size));
/* Try to write the data. */
R_TRY(fsFileWrite(&f, 0, data, size, FS_WRITEOPTION_FLUSH));
return ResultSuccess();
}
bool Utils::IsHblTid(u64 _tid) {
const ams::ncm::ProgramId tid{_tid};
return (g_hbl_override_config.override_any_app && ams::ncm::IsApplicationProgramId(tid)) || (_tid == g_hbl_override_config.program_id);
}
bool Utils::IsWebAppletTid(u64 _tid) {
const ams::ncm::ProgramId tid{_tid};
return tid == ams::ncm::ProgramId::AppletWeb || tid == ams::ncm::ProgramId::AppletOfflineWeb || tid == ams::ncm::ProgramId::AppletLoginShare || tid == ams::ncm::ProgramId::AppletWifiWebAuth;
}
bool Utils::HasTitleFlag(u64 tid, const char *flag) {
if (IsSdInitialized()) {
FsFile f;
char flag_path[FS_MAX_PATH];
memset(flag_path, 0, sizeof(flag_path));
snprintf(flag_path, sizeof(flag_path) - 1, "flags/%s.flag", flag);
if (R_SUCCEEDED(OpenSdFileForAtmosphere(tid, flag_path, FS_OPEN_READ, &f))) {
fsFileClose(&f);
return true;
}
/* TODO: Deprecate. */
snprintf(flag_path, sizeof(flag_path) - 1, "%s.flag", flag);
if (R_SUCCEEDED(OpenSdFileForAtmosphere(tid, flag_path, FS_OPEN_READ, &f))) {
fsFileClose(&f);
return true;
}
}
return false;
}
bool Utils::HasGlobalFlag(const char *flag) {
if (IsSdInitialized()) {
FsFile f;
char flag_path[FS_MAX_PATH] = {0};
snprintf(flag_path, sizeof(flag_path), "/atmosphere/flags/%s.flag", flag);
if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, flag_path, FS_OPEN_READ, &f))) {
fsFileClose(&f);
return true;
}
}
return false;
}
bool Utils::HasHblFlag(const char *flag) {
char hbl_flag[FS_MAX_PATH] = {0};
snprintf(hbl_flag, sizeof(hbl_flag), "hbl_%s", flag);
return HasGlobalFlag(hbl_flag);
}
bool Utils::HasFlag(u64 tid, const char *flag) {
return HasTitleFlag(tid, flag) || (IsHblTid(tid) && HasHblFlag(flag));
}
bool Utils::HasSdMitMFlag(u64 tid) {
if (IsHblTid(tid)) {
return true;
}
if (IsSdInitialized()) {
return std::find(g_mitm_flagged_tids.begin(), g_mitm_flagged_tids.end(), tid) != g_mitm_flagged_tids.end();
}
return false;
}
bool Utils::HasSdDisableMitMFlag(u64 tid) {
if (IsSdInitialized()) {
return std::find(g_disable_mitm_flagged_tids.begin(), g_disable_mitm_flagged_tids.end(), tid) != g_disable_mitm_flagged_tids.end();
}
return false;
}
Result Utils::GetKeysHeld(u64 *keys) {
if (!Utils::IsHidAvailable()) {
return MAKERESULT(Module_Libnx, LibnxError_InitFail_HID);
}
hidScanInput();
*keys = 0;
for (int controller = 0; controller < 10; controller++) {
*keys |= hidKeysHeld((HidControllerID) controller);
}
return ResultSuccess();
}
static bool HasOverrideKey(OverrideKey *cfg) {
u64 kDown = 0;
bool keys_triggered = (R_SUCCEEDED(Utils::GetKeysHeld(&kDown)) && ((kDown & cfg->key_combination) != 0));
return Utils::IsSdInitialized() && (cfg->override_by_default ^ keys_triggered);
}
bool Utils::HasOverrideButton(u64 tid) {
if ((!ams::ncm::IsApplicationProgramId(ams::ncm::ProgramId{tid})) || (!IsSdInitialized())) {
/* Disable button override disable for non-applications. */
return true;
}
/* Unconditionally refresh loader.ini contents. */
RefreshConfiguration();
if (IsHblTid(tid) && HasOverrideKey(&g_hbl_override_config.override_key)) {
return true;
}
OverrideKey title_cfg = GetTitleOverrideKey(tid);
return HasOverrideKey(&title_cfg);
}
static OverrideKey ParseOverrideKey(const char *value) {
OverrideKey cfg;
/* Parse on by default. */
if (value[0] == '!') {
cfg.override_by_default = true;
value++;
} else {
cfg.override_by_default = false;
}
/* Parse key combination. */
if (strcasecmp(value, "A") == 0) {
cfg.key_combination = KEY_A;
} else if (strcasecmp(value, "B") == 0) {
cfg.key_combination = KEY_B;
} else if (strcasecmp(value, "X") == 0) {
cfg.key_combination = KEY_X;
} else if (strcasecmp(value, "Y") == 0) {
cfg.key_combination = KEY_Y;
} else if (strcasecmp(value, "LS") == 0) {
cfg.key_combination = KEY_LSTICK;
} else if (strcasecmp(value, "RS") == 0) {
cfg.key_combination = KEY_RSTICK;
} else if (strcasecmp(value, "L") == 0) {
cfg.key_combination = KEY_L;
} else if (strcasecmp(value, "R") == 0) {
cfg.key_combination = KEY_R;
} else if (strcasecmp(value, "ZL") == 0) {
cfg.key_combination = KEY_ZL;
} else if (strcasecmp(value, "ZR") == 0) {
cfg.key_combination = KEY_ZR;
} else if (strcasecmp(value, "PLUS") == 0) {
cfg.key_combination = KEY_PLUS;
} else if (strcasecmp(value, "MINUS") == 0) {
cfg.key_combination = KEY_MINUS;
} else if (strcasecmp(value, "DLEFT") == 0) {
cfg.key_combination = KEY_DLEFT;
} else if (strcasecmp(value, "DUP") == 0) {
cfg.key_combination = KEY_DUP;
} else if (strcasecmp(value, "DRIGHT") == 0) {
cfg.key_combination = KEY_DRIGHT;
} else if (strcasecmp(value, "DDOWN") == 0) {
cfg.key_combination = KEY_DDOWN;
} else if (strcasecmp(value, "SL") == 0) {
cfg.key_combination = KEY_SL;
} else if (strcasecmp(value, "SR") == 0) {
cfg.key_combination = KEY_SR;
} else {
cfg.key_combination = 0;
}
return cfg;
}
static int FsMitmIniHandler(void *user, const char *section, const char *name, const char *value) {
/* Taken and modified, with love, from Rajkosto's implementation. */
if (strcasecmp(section, "hbl_config") == 0) {
if (strcasecmp(name, "program_id") == 0) {
if (strcasecmp(value, "app") == 0) {
/* DEPRECATED */
g_hbl_override_config.override_any_app = true;
g_hbl_override_config.program_id = 0;
} else {
u64 override_tid = strtoul(value, NULL, 16);
if (override_tid != 0) {
g_hbl_override_config.program_id = override_tid;
}
}
} else if (strcasecmp(name, "override_key") == 0) {
g_hbl_override_config.override_key = ParseOverrideKey(value);
} else if (strcasecmp(name, "override_any_app") == 0) {
if (strcasecmp(value, "true") == 0 || strcasecmp(value, "1") == 0) {
g_hbl_override_config.override_any_app = true;
} else if (strcasecmp(value, "false") == 0 || strcasecmp(value, "0") == 0) {
g_hbl_override_config.override_any_app = false;
} else {
/* I guess we default to not changing the value? */
}
}
} else if (strcasecmp(section, "default_config") == 0) {
if (strcasecmp(name, "override_key") == 0) {
g_default_override_key = ParseOverrideKey(value);
}
} else {
return 0;
}
return 1;
}
static int FsMitmContentSpecificIniHandler(void *user, const char *section, const char *name, const char *value) {
/* We'll output an override key when relevant. */
OverrideKey *user_cfg = reinterpret_cast<OverrideKey *>(user);
if (strcasecmp(section, "override_config") == 0) {
if (strcasecmp(name, "override_key") == 0) {
*user_cfg = ParseOverrideKey(value);
}
} else {
return 0;
}
return 1;
}
OverrideKey Utils::GetTitleOverrideKey(u64 tid) {
OverrideKey cfg = g_default_override_key;
char path[FS_MAX_PATH+1] = {0};
snprintf(path, FS_MAX_PATH, "/atmosphere/titles/%016lx/config.ini", tid);
FsFile cfg_file;
if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, path, FS_OPEN_READ, &cfg_file))) {
ON_SCOPE_EXIT { fsFileClose(&cfg_file); };
size_t config_file_size = 0x20000;
fsFileGetSize(&cfg_file, &config_file_size);
char *config_buf = reinterpret_cast<char *>(calloc(1, config_file_size + 1));
if (config_buf != NULL) {
ON_SCOPE_EXIT { free(config_buf); };
/* Read title ini contents. */
fsFileRead(&cfg_file, 0, config_buf, config_file_size, FS_READOPTION_NONE, &config_file_size);
/* Parse title ini. */
ini_parse_string(config_buf, FsMitmContentSpecificIniHandler, &cfg);
}
}
return cfg;
}
static int FsMitmContentSpecificLocaleIniHandler(void *user, const char *section, const char *name, const char *value) {
/* We'll output an override locale when relevant. */
OverrideLocale *user_locale = reinterpret_cast<OverrideLocale *>(user);
if (strcasecmp(section, "override_config") == 0) {
if (strcasecmp(name, "override_language") == 0) {
user_locale->language_code = EncodeLanguageCode(value);
} else if (strcasecmp(name, "override_region") == 0) {
if (strcasecmp(value, "jpn") == 0) {
user_locale->region_code = RegionCode_Japan;
} else if (strcasecmp(value, "usa") == 0) {
user_locale->region_code = RegionCode_America;
} else if (strcasecmp(value, "eur") == 0) {
user_locale->region_code = RegionCode_Europe;
} else if (strcasecmp(value, "aus") == 0) {
user_locale->region_code = RegionCode_Australia;
} else if (strcasecmp(value, "chn") == 0) {
user_locale->region_code = RegionCode_China;
} else if (strcasecmp(value, "kor") == 0) {
user_locale->region_code = RegionCode_Korea;
} else if (strcasecmp(value, "twn") == 0) {
user_locale->region_code = RegionCode_Taiwan;
}
}
} else {
return 0;
}
return 1;
}
OverrideLocale Utils::GetTitleOverrideLocale(u64 tid) {
OverrideLocale locale;
std::memset(&locale, 0xCC, sizeof(locale));
char path[FS_MAX_PATH+1] = {0};
snprintf(path, FS_MAX_PATH, "/atmosphere/titles/%016lx/config.ini", tid);
FsFile cfg_file;
if (R_SUCCEEDED(fsFsOpenFile(&g_sd_filesystem, path, FS_OPEN_READ, &cfg_file))) {
ON_SCOPE_EXIT { fsFileClose(&cfg_file); };
size_t config_file_size = 0x20000;
fsFileGetSize(&cfg_file, &config_file_size);
char *config_buf = reinterpret_cast<char *>(calloc(1, config_file_size + 1));
if (config_buf != NULL) {
ON_SCOPE_EXIT { free(config_buf); };
/* Read title ini contents. */
fsFileRead(&cfg_file, 0, config_buf, config_file_size, FS_READOPTION_NONE, &config_file_size);
/* Parse title ini. */
ini_parse_string(config_buf, FsMitmContentSpecificLocaleIniHandler, &locale);
}
}
return locale;
}
void Utils::RefreshConfiguration() {
FsFile config_file;
if (R_FAILED(fsFsOpenFile(&g_sd_filesystem, "/atmosphere/loader.ini", FS_OPEN_READ, &config_file))) {
return;
}
u64 size;
if (R_FAILED(fsFileGetSize(&config_file, &size))) {
return;
}
size = std::min(size, (decltype(size))0x7FF);
/* Read in string. */
std::fill(g_config_ini_data, g_config_ini_data + 0x800, 0);
size_t r_s;
fsFileRead(&config_file, 0, g_config_ini_data, size, FS_READOPTION_NONE, &r_s);
fsFileClose(&config_file);
ini_parse_string(g_config_ini_data, FsMitmIniHandler, NULL);
}
Result Utils::GetSettingsItemValueSize(const char *name, const char *key, u64 *out_size) {
return SettingsItemManager::GetValueSize(name, key, out_size);
}
Result Utils::GetSettingsItemValue(const char *name, const char *key, void *out, size_t max_size, u64 *out_size) {
return SettingsItemManager::GetValue(name, key, out, max_size, out_size);
}
Result Utils::GetSettingsItemBooleanValue(const char *name, const char *key, bool *out) {
u8 val = 0;
u64 out_size;
R_TRY(Utils::GetSettingsItemValue(name, key, &val, sizeof(val), &out_size));
if (out) {
*out = val != 0;
}
return ResultSuccess();
}
void Utils::RebootToFatalError(AtmosphereFatalErrorContext *ctx) {
BpcRebootManager::RebootForFatalError(ctx);
}

View file

@ -1,129 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
struct OverrideKey {
u64 key_combination;
bool override_by_default;
};
struct OverrideLocale {
u64 language_code;
u32 region_code;
};
enum RegionCode : u32 {
RegionCode_Japan = 0,
RegionCode_America = 1,
RegionCode_Europe = 2,
RegionCode_Australia = 3,
RegionCode_China = 4,
RegionCode_Korea = 5,
RegionCode_Taiwan = 6,
RegionCode_Max,
};
static constexpr inline u64 EncodeLanguageCode(const char *code) {
u64 lang_code = 0;
for (size_t i = 0; i < sizeof(lang_code); i++) {
if (code[i] == '\x00') {
break;
}
lang_code |= static_cast<u64>(code[i]) << (8ul * i);
}
return lang_code;
}
enum LanguageCode : u64 {
LanguageCode_Japanese = EncodeLanguageCode("ja"),
LanguageCode_AmericanEnglish = EncodeLanguageCode("en-US"),
LanguageCode_French = EncodeLanguageCode("fr"),
LanguageCode_German = EncodeLanguageCode("de"),
LanguageCode_Italian = EncodeLanguageCode("it"),
LanguageCode_Spanish = EncodeLanguageCode("es"),
LanguageCode_Chinese = EncodeLanguageCode("zh-CN"),
LanguageCode_Korean = EncodeLanguageCode("ko"),
LanguageCode_Dutch = EncodeLanguageCode("nl"),
LanguageCode_Portuguese = EncodeLanguageCode("pt"),
LanguageCode_Russian = EncodeLanguageCode("ru"),
LanguageCode_Taiwanese = EncodeLanguageCode("zh-TW"),
LanguageCode_BritishEnglish = EncodeLanguageCode("en-GB"),
LanguageCode_CanadianFrench = EncodeLanguageCode("fr-CA"),
LanguageCode_LatinAmericanSpanish = EncodeLanguageCode("es-419"),
/* 4.0.0+ */
LanguageCode_SimplifiedChinese = EncodeLanguageCode("zh-Hans"),
LanguageCode_TraditionalChinese = EncodeLanguageCode("zh-Hant"),
};
class Utils {
public:
static bool IsSdInitialized();
static void WaitSdInitialized();
static Result OpenSdFile(const char *fn, int flags, FsFile *out);
static Result OpenSdFileForAtmosphere(u64 program_id, const char *fn, int flags, FsFile *out);
static Result OpenRomFSSdFile(u64 program_id, const char *fn, int flags, FsFile *out);
static Result OpenSdDir(const char *path, FsDir *out);
static Result OpenSdDirForAtmosphere(u64 program_id, const char *path, FsDir *out);
static Result OpenRomFSSdDir(u64 program_id, const char *path, FsDir *out);
static Result OpenRomFSFile(FsFileSystem *fs, u64 program_id, const char *fn, int flags, FsFile *out);
static Result OpenRomFSDir(FsFileSystem *fs, u64 program_id, const char *path, FsDir *out);
static Result SaveSdFileForAtmosphere(u64 program_id, const char *fn, void *data, size_t size);
static bool HasSdRomfsContent(u64 program_id);
/* Delayed Initialization + MitM detection. */
static void InitializeThreadFunc(void *args);
static bool IsHblTid(u64 tid);
static bool IsWebAppletTid(u64 tid);
static bool HasTitleFlag(u64 tid, const char *flag);
static bool HasHblFlag(const char *flag);
static bool HasGlobalFlag(const char *flag);
static bool HasFlag(u64 tid, const char *flag);
static bool HasSdMitMFlag(u64 tid);
static bool HasSdDisableMitMFlag(u64 tid);
static bool IsHidAvailable();
static void WaitHidAvailable();
static Result GetKeysHeld(u64 *keys);
static OverrideKey GetTitleOverrideKey(u64 tid);
static bool HasOverrideButton(u64 tid);
static OverrideLocale GetTitleOverrideLocale(u64 tid);
/* Settings! */
static Result GetSettingsItemValueSize(const char *name, const char *key, u64 *out_size);
static Result GetSettingsItemValue(const char *name, const char *key, void *out, size_t max_size, u64 *out_size);
static Result GetSettingsItemBooleanValue(const char *name, const char *key, bool *out);
/* Error occurred. */
static void RebootToFatalError(AtmosphereFatalErrorContext *ctx);
private:
static void RefreshConfiguration();
};