Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"
This reverts commit 15b7df8ef1.
This commit is contained in:
@@ -1,261 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "amsmitm_initialization.hpp"
|
||||
#include "amsmitm_fs_utils.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
using namespace ams::fs;
|
||||
|
||||
namespace {
|
||||
|
||||
/* Globals. */
|
||||
FsFileSystem g_sd_filesystem;
|
||||
|
||||
/* Helpers. */
|
||||
Result EnsureSdInitialized() {
|
||||
R_UNLESS(serviceIsActive(std::addressof(g_sd_filesystem.s)), ams::fs::ResultSdCardNotPresent());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void FormatAtmosphereRomfsPath(char *dst_path, size_t dst_path_size, ncm::ProgramId program_id, const char *src_path) {
|
||||
return FormatAtmosphereSdPath(dst_path, dst_path_size, program_id, "romfs", src_path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void OpenGlobalSdCardFileSystem() {
|
||||
R_ABORT_UNLESS(fsOpenSdCardFileSystem(std::addressof(g_sd_filesystem)));
|
||||
}
|
||||
|
||||
Result CreateSdFile(const char *path, s64 size, s32 option) {
|
||||
R_TRY(EnsureSdInitialized());
|
||||
R_RETURN(fsFsCreateFile(std::addressof(g_sd_filesystem), path, size, option));
|
||||
}
|
||||
|
||||
Result DeleteSdFile(const char *path) {
|
||||
R_TRY(EnsureSdInitialized());
|
||||
R_RETURN(fsFsDeleteFile(std::addressof(g_sd_filesystem), path));
|
||||
}
|
||||
|
||||
bool HasSdFile(const char *path) {
|
||||
if (R_FAILED(EnsureSdInitialized())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FsDirEntryType type;
|
||||
if (R_FAILED(fsFsGetEntryType(std::addressof(g_sd_filesystem), path, std::addressof(type)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return type == FsDirEntryType_File;
|
||||
}
|
||||
|
||||
bool HasAtmosphereSdFile(const char *path) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
|
||||
return HasSdFile(fixed_path);
|
||||
}
|
||||
|
||||
Result DeleteAtmosphereSdFile(const char *path) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
|
||||
R_RETURN(DeleteSdFile(fixed_path));
|
||||
}
|
||||
|
||||
Result CreateAtmosphereSdFile(const char *path, s64 size, s32 option) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
|
||||
R_RETURN(CreateSdFile(fixed_path, size, option));
|
||||
}
|
||||
|
||||
Result OpenSdFile(FsFile *out, const char *path, u32 mode) {
|
||||
R_TRY(EnsureSdInitialized());
|
||||
R_RETURN(fsFsOpenFile(std::addressof(g_sd_filesystem), path, mode, out));
|
||||
}
|
||||
|
||||
Result OpenAtmosphereSdFile(FsFile *out, const char *path, u32 mode) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
|
||||
R_RETURN(OpenSdFile(out, fixed_path, mode));
|
||||
}
|
||||
|
||||
Result OpenAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||
R_RETURN(OpenSdFile(out, fixed_path, mode));
|
||||
}
|
||||
|
||||
Result OpenAtmosphereSdRomfsFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereRomfsPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||
R_RETURN(OpenSdFile(out, fixed_path, mode));
|
||||
}
|
||||
|
||||
Result OpenAtmosphereRomfsFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode, FsFileSystem *fs) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereRomfsPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||
R_RETURN(fsFsOpenFile(fs, fixed_path, mode, out));
|
||||
}
|
||||
|
||||
Result CreateSdDirectory(const char *path) {
|
||||
R_TRY(EnsureSdInitialized());
|
||||
R_RETURN(fsFsCreateDirectory(std::addressof(g_sd_filesystem), path));
|
||||
}
|
||||
|
||||
Result CreateAtmosphereSdDirectory(const char *path) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
|
||||
R_RETURN(CreateSdDirectory(fixed_path));
|
||||
}
|
||||
|
||||
Result OpenSdDirectory(FsDir *out, const char *path, u32 mode) {
|
||||
R_TRY(EnsureSdInitialized());
|
||||
R_RETURN(fsFsOpenDirectory(std::addressof(g_sd_filesystem), path, mode, out));
|
||||
}
|
||||
|
||||
Result OpenAtmosphereSdDirectory(FsDir *out, const char *path, u32 mode) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), path);
|
||||
R_RETURN(OpenSdDirectory(out, fixed_path, mode));
|
||||
}
|
||||
|
||||
Result OpenAtmosphereSdDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||
R_RETURN(OpenSdDirectory(out, fixed_path, mode));
|
||||
}
|
||||
|
||||
Result OpenAtmosphereSdRomfsDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereRomfsPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||
R_RETURN(OpenSdDirectory(out, fixed_path, mode));
|
||||
}
|
||||
|
||||
Result OpenAtmosphereRomfsDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode, FsFileSystem *fs) {
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereRomfsPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||
R_RETURN(fsFsOpenDirectory(fs, fixed_path, mode, out));
|
||||
}
|
||||
|
||||
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, const char *src_path) {
|
||||
if (src_path[0] == '/') {
|
||||
util::SNPrintf(dst_path, dst_path_size, "/atmosphere%s", src_path);
|
||||
} else {
|
||||
util::SNPrintf(dst_path, dst_path_size, "/atmosphere/%s", src_path);
|
||||
}
|
||||
}
|
||||
|
||||
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, const char *subdir, const char *src_path) {
|
||||
if (src_path[0] == '/') {
|
||||
util::SNPrintf(dst_path, dst_path_size, "/atmosphere/%s%s", subdir, src_path);
|
||||
} else {
|
||||
util::SNPrintf(dst_path, dst_path_size, "/atmosphere/%s/%s", subdir, src_path);
|
||||
}
|
||||
}
|
||||
|
||||
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, ncm::ProgramId program_id, const char *src_path) {
|
||||
if (src_path[0] == '/') {
|
||||
util::SNPrintf(dst_path, dst_path_size, "/atmosphere/contents/%016lx%s", static_cast<u64>(program_id), src_path);
|
||||
} else {
|
||||
util::SNPrintf(dst_path, dst_path_size, "/atmosphere/contents/%016lx/%s", static_cast<u64>(program_id), src_path);
|
||||
}
|
||||
}
|
||||
|
||||
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, ncm::ProgramId program_id, const char *subdir, const char *src_path) {
|
||||
if (src_path[0] == '/') {
|
||||
util::SNPrintf(dst_path, dst_path_size, "/atmosphere/contents/%016lx/%s%s", static_cast<u64>(program_id), subdir, src_path);
|
||||
} else {
|
||||
util::SNPrintf(dst_path, dst_path_size, "/atmosphere/contents/%016lx/%s/%s", static_cast<u64>(program_id), subdir, src_path);
|
||||
}
|
||||
}
|
||||
|
||||
bool HasSdRomfsContent(ncm::ProgramId program_id) {
|
||||
/* Check if romfs.bin is present. */
|
||||
{
|
||||
FsFile romfs_file;
|
||||
if (R_SUCCEEDED(OpenAtmosphereSdFile(std::addressof(romfs_file), program_id, "romfs.bin", OpenMode_Read))) {
|
||||
fsFileClose(std::addressof(romfs_file));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for romfs folder with content. */
|
||||
FsDir romfs_dir;
|
||||
if (R_FAILED(OpenAtmosphereSdRomfsDirectory(std::addressof(romfs_dir), program_id, "", fs::OpenDirectoryMode_All))) {
|
||||
return false;
|
||||
}
|
||||
ON_SCOPE_EXIT { fsDirClose(std::addressof(romfs_dir)); };
|
||||
|
||||
/* Verify the folder has at least one entry. */
|
||||
s64 num_entries = 0;
|
||||
return R_SUCCEEDED(fsDirGetEntryCount(std::addressof(romfs_dir), std::addressof(num_entries))) && num_entries > 0;
|
||||
}
|
||||
|
||||
Result SaveAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, void *data, size_t size) {
|
||||
R_TRY(EnsureSdInitialized());
|
||||
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||
|
||||
/* Unconditionally create. */
|
||||
/* Don't check error, as a failure here should be okay. */
|
||||
FsFile f;
|
||||
fsFsCreateFile(std::addressof(g_sd_filesystem), fixed_path, size, 0);
|
||||
|
||||
/* Try to open. */
|
||||
R_TRY(fsFsOpenFile(std::addressof(g_sd_filesystem), fixed_path, OpenMode_ReadWrite, std::addressof(f)));
|
||||
auto file_guard = SCOPE_GUARD { fsFileClose(std::addressof(f)); };
|
||||
|
||||
/* Try to set the size. */
|
||||
R_TRY(fsFileSetSize(std::addressof(f), static_cast<s64>(size)));
|
||||
|
||||
/* Try to write data. */
|
||||
R_TRY(fsFileWrite(std::addressof(f), 0, data, size, FsWriteOption_Flush));
|
||||
|
||||
/* Set output. */
|
||||
file_guard.Cancel();
|
||||
*out = f;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CreateAndOpenAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, size_t size) {
|
||||
R_TRY(EnsureSdInitialized());
|
||||
|
||||
char fixed_path[ams::fs::EntryNameLengthMax + 1];
|
||||
FormatAtmosphereSdPath(fixed_path, sizeof(fixed_path), program_id, path);
|
||||
|
||||
/* Unconditionally create. */
|
||||
/* Don't check error, as a failure here should be okay. */
|
||||
FsFile f;
|
||||
fsFsCreateFile(std::addressof(g_sd_filesystem), fixed_path, size, 0);
|
||||
|
||||
/* Try to open. */
|
||||
R_TRY(fsFsOpenFile(std::addressof(g_sd_filesystem), fixed_path, OpenMode_ReadWrite, std::addressof(f)));
|
||||
auto file_guard = SCOPE_GUARD { fsFileClose(std::addressof(f)); };
|
||||
|
||||
/* Try to set the size. */
|
||||
R_TRY(fsFileSetSize(std::addressof(f), static_cast<s64>(size)));
|
||||
|
||||
/* Set output. */
|
||||
file_guard.Cancel();
|
||||
*out = f;
|
||||
R_SUCCEED();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
/* Initialization. */
|
||||
void OpenGlobalSdCardFileSystem();
|
||||
|
||||
/* Utilities. */
|
||||
Result DeleteAtmosphereSdFile(const char *path);
|
||||
Result CreateSdFile(const char *path, s64 size, s32 option);
|
||||
Result CreateAtmosphereSdFile(const char *path, s64 size, s32 option);
|
||||
Result OpenSdFile(FsFile *out, const char *path, u32 mode);
|
||||
Result OpenAtmosphereSdFile(FsFile *out, const char *path, u32 mode);
|
||||
Result OpenAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode);
|
||||
Result OpenAtmosphereSdRomfsFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode);
|
||||
Result OpenAtmosphereRomfsFile(FsFile *out, ncm::ProgramId program_id, const char *path, u32 mode, FsFileSystem *fs);
|
||||
|
||||
bool HasSdFile(const char *path);
|
||||
bool HasAtmosphereSdFile(const char *path);
|
||||
|
||||
Result CreateSdDirectory(const char *path);
|
||||
Result CreateAtmosphereSdDirectory(const char *path);
|
||||
Result OpenSdDirectory(FsDir *out, const char *path, u32 mode);
|
||||
Result OpenAtmosphereSdDirectory(FsDir *out, const char *path, u32 mode);
|
||||
Result OpenAtmosphereSdDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode);
|
||||
Result OpenAtmosphereSdRomfsDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode);
|
||||
Result OpenAtmosphereRomfsDirectory(FsDir *out, ncm::ProgramId program_id, const char *path, u32 mode, FsFileSystem *fs);
|
||||
|
||||
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, const char *src_path);
|
||||
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, const char *subdir, const char *src_path);
|
||||
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, ncm::ProgramId program_id, const char *src_path);
|
||||
void FormatAtmosphereSdPath(char *dst_path, size_t dst_path_size, ncm::ProgramId program_id, const char *subdir, const char *src_path);
|
||||
|
||||
bool HasSdRomfsContent(ncm::ProgramId program_id);
|
||||
|
||||
Result SaveAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, void *data, size_t size);
|
||||
Result CreateAndOpenAtmosphereSdFile(FsFile *out, ncm::ProgramId program_id, const char *path, size_t size);
|
||||
|
||||
/* NOTE: Implemented in fs.mitm logic. */
|
||||
bool HasSdManualHtmlContent(ncm::ProgramId program_id);
|
||||
|
||||
}
|
||||
@@ -1,245 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "amsmitm_initialization.hpp"
|
||||
#include "amsmitm_fs_utils.hpp"
|
||||
#include "amsmitm_prodinfo_utils.hpp"
|
||||
#include "bpc_mitm/bpc_ams_power_utils.hpp"
|
||||
#include "set_mitm/settings_sd_kvs.hpp"
|
||||
|
||||
namespace ams::mitm {
|
||||
|
||||
namespace {
|
||||
|
||||
/* BIS key sources. */
|
||||
constexpr u8 BisKeySources[4][2][0x10] = {
|
||||
{
|
||||
{0xF8, 0x3F, 0x38, 0x6E, 0x2C, 0xD2, 0xCA, 0x32, 0xA8, 0x9A, 0xB9, 0xAA, 0x29, 0xBF, 0xC7, 0x48},
|
||||
{0x7D, 0x92, 0xB0, 0x3A, 0xA8, 0xBF, 0xDE, 0xE1, 0xA7, 0x4C, 0x3B, 0x6E, 0x35, 0xCB, 0x71, 0x06}
|
||||
},
|
||||
{
|
||||
{0x41, 0x00, 0x30, 0x49, 0xDD, 0xCC, 0xC0, 0x65, 0x64, 0x7A, 0x7E, 0xB4, 0x1E, 0xED, 0x9C, 0x5F},
|
||||
{0x44, 0x42, 0x4E, 0xDA, 0xB4, 0x9D, 0xFC, 0xD9, 0x87, 0x77, 0x24, 0x9A, 0xDC, 0x9F, 0x7C, 0xA4}
|
||||
},
|
||||
{
|
||||
{0x52, 0xC2, 0xE9, 0xEB, 0x09, 0xE3, 0xEE, 0x29, 0x32, 0xA1, 0x0C, 0x1F, 0xB6, 0xA0, 0x92, 0x6C},
|
||||
{0x4D, 0x12, 0xE1, 0x4B, 0x2A, 0x47, 0x4C, 0x1C, 0x09, 0xCB, 0x03, 0x59, 0xF0, 0x15, 0xF4, 0xE4}
|
||||
},
|
||||
{
|
||||
{0x52, 0xC2, 0xE9, 0xEB, 0x09, 0xE3, 0xEE, 0x29, 0x32, 0xA1, 0x0C, 0x1F, 0xB6, 0xA0, 0x92, 0x6C},
|
||||
{0x4D, 0x12, 0xE1, 0x4B, 0x2A, 0x47, 0x4C, 0x1C, 0x09, 0xCB, 0x03, 0x59, 0xF0, 0x15, 0xF4, 0xE4}
|
||||
}
|
||||
};
|
||||
|
||||
constexpr u8 BisKekSource[0x10] = {
|
||||
0x34, 0xC1, 0xA0, 0xC4, 0x82, 0x58, 0xF8, 0xB4, 0xFA, 0x9E, 0x5E, 0x6A, 0xDA, 0xFC, 0x7E, 0x4F,
|
||||
};
|
||||
|
||||
void InitializeThreadFunc(void *arg);
|
||||
|
||||
constexpr size_t InitializeThreadStackSize = 0x4000;
|
||||
|
||||
/* Globals. */
|
||||
os::Event g_init_event(os::EventClearMode_ManualClear);
|
||||
|
||||
os::ThreadType g_initialize_thread;
|
||||
alignas(os::ThreadStackAlignment) u8 g_initialize_thread_stack[InitializeThreadStackSize];
|
||||
|
||||
/* Console-unique data backup and protection. */
|
||||
FsFile g_bis_key_file;
|
||||
|
||||
/* Emummc file protection. */
|
||||
FsFile g_emummc_file;
|
||||
|
||||
/* Maintain exclusive access to the fusee external package. */
|
||||
FsFile g_stratosphere_file;
|
||||
FsFile g_package3_file;
|
||||
|
||||
constexpr inline bool IsHexadecimal(const char *str) {
|
||||
while (*str) {
|
||||
if (std::isxdigit(static_cast<unsigned char>(*str))) {
|
||||
str++;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GetBackupFileName(char *dst, size_t dst_size, const char *serial_number, const char *fn) {
|
||||
if (strlen(serial_number) > 0) {
|
||||
util::SNPrintf(dst, dst_size, "automatic_backups/%s_%s", serial_number, fn);
|
||||
} else {
|
||||
util::SNPrintf(dst, dst_size, "automatic_backups/%s", fn);
|
||||
}
|
||||
}
|
||||
|
||||
void CreateAutomaticBackups() {
|
||||
/* Create a backup directory, if one doesn't exist. */
|
||||
mitm::fs::CreateAtmosphereSdDirectory("/automatic_backups");
|
||||
|
||||
/* Initialize PRODINFO and get a reference for the device. */
|
||||
char device_reference[0x40] = {};
|
||||
ON_SCOPE_EXIT { std::memset(device_reference, 0, sizeof(device_reference)); };
|
||||
mitm::SaveProdInfoBackupsAndWipeMemory(device_reference, sizeof(device_reference));
|
||||
|
||||
/* Backup BIS keys. */
|
||||
{
|
||||
u64 key_generation = 0;
|
||||
if (hos::GetVersion() >= hos::Version_5_0_0) {
|
||||
R_ABORT_UNLESS(spl::GetConfig(std::addressof(key_generation), spl::ConfigItem::DeviceUniqueKeyGeneration));
|
||||
}
|
||||
|
||||
u8 bis_keys[4][2][0x10];
|
||||
std::memset(bis_keys, 0xCC, sizeof(bis_keys));
|
||||
ON_SCOPE_EXIT { std::memset(bis_keys, 0xCC, sizeof(bis_keys)); };
|
||||
|
||||
/* TODO: Clean this up. */
|
||||
for (size_t partition = 0; partition < 4; partition++) {
|
||||
if (partition == 0) {
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
R_ABORT_UNLESS(spl::GenerateSpecificAesKey(bis_keys[partition][i], 0x10, BisKeySources[partition][i], 0x10, key_generation, i));
|
||||
}
|
||||
} else {
|
||||
const u32 option = (partition == 3 && spl::IsRecoveryBoot()) ? 0x4 : 0x1;
|
||||
|
||||
spl::AccessKey access_key;
|
||||
R_ABORT_UNLESS(spl::GenerateAesKek(std::addressof(access_key), BisKekSource, 0x10, key_generation, option));
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
R_ABORT_UNLESS(spl::GenerateAesKey(bis_keys[partition][i], 0x10, access_key, BisKeySources[partition][i], 0x10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char bis_keys_backup_name[ams::fs::EntryNameLengthMax + 1];
|
||||
GetBackupFileName(bis_keys_backup_name, sizeof(bis_keys_backup_name), device_reference, "BISKEYS.bin");
|
||||
|
||||
mitm::fs::CreateAtmosphereSdFile(bis_keys_backup_name, sizeof(bis_keys), ams::fs::CreateOption_None);
|
||||
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(g_bis_key_file), bis_keys_backup_name, ams::fs::OpenMode_ReadWrite));
|
||||
R_ABORT_UNLESS(fsFileSetSize(std::addressof(g_bis_key_file), sizeof(bis_keys)));
|
||||
R_ABORT_UNLESS(fsFileWrite(std::addressof(g_bis_key_file), 0, bis_keys, sizeof(bis_keys), FsWriteOption_Flush));
|
||||
/* NOTE: g_bis_key_file is intentionally not closed here. This prevents any other process from opening it. */
|
||||
}
|
||||
|
||||
/* Open a reference to the fusee external package. */
|
||||
/* As upcoming/current atmosphere releases may contain more than one zip which users much choose between, */
|
||||
/* maintaining an open reference prevents cleanly the issue of "automatic" updaters selecting the incorrect */
|
||||
/* zip, and encourages good updating hygiene -- atmosphere should not be updated on SD while HOS is alive. */
|
||||
{
|
||||
R_ABORT_UNLESS(mitm::fs::OpenSdFile(std::addressof(g_package3_file), "/atmosphere/package3", ams::fs::OpenMode_Read));
|
||||
R_ABORT_UNLESS(mitm::fs::OpenSdFile(std::addressof(g_stratosphere_file), "/atmosphere/stratosphere.romfs", ams::fs::OpenMode_Read));
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialization implementation */
|
||||
void InitializeThreadFunc(void *) {
|
||||
/* Wait for the SD card to be ready. */
|
||||
cfg::WaitSdCardInitialized();
|
||||
|
||||
/* Open global SD card file system, so that other threads can begin using the SD. */
|
||||
mitm::fs::OpenGlobalSdCardFileSystem();
|
||||
|
||||
/* Mount the sd card at a convenient mountpoint. */
|
||||
ams::fs::MountSdCard(ams::fs::impl::SdCardFileSystemMountName);
|
||||
|
||||
/* Initialize the reboot manager (load a payload off the SD). */
|
||||
/* Discard result, since it doesn't need to succeed. */
|
||||
mitm::bpc::LoadRebootPayload();
|
||||
|
||||
/* Backup Calibration Binary and BIS keys. */
|
||||
CreateAutomaticBackups();
|
||||
|
||||
/* If we're emummc, persist a write-handle to prevent other processes from touching the image. */
|
||||
if (emummc::IsActive()) {
|
||||
if (const char *emummc_file_path = emummc::GetFilePath(); emummc_file_path != nullptr) {
|
||||
char emummc_path[ams::fs::EntryNameLengthMax + 1];
|
||||
util::SNPrintf(emummc_path, sizeof(emummc_path), "%s/eMMC", emummc_file_path);
|
||||
mitm::fs::OpenSdFile(std::addressof(g_emummc_file), emummc_path, ams::fs::OpenMode_Read);
|
||||
}
|
||||
|
||||
/* NOTE: due to an Atmosphere bug, NS accesses to the Nintendo dir accessed /Nintendo/Nintendo */
|
||||
/* instead of /Nintendo. This logic is potentially temporary, and fixes the case where this would have happened. */
|
||||
{
|
||||
auto HasDir = [](const char *p) -> bool { bool res{}; R_ABORT_UNLESS(ams::fs::HasDirectory(std::addressof(res), p)); return res; };
|
||||
auto HasFile = [](const char *p) -> bool { bool res{}; R_ABORT_UNLESS(ams::fs::HasFile(std::addressof(res), p)); return res; };
|
||||
|
||||
char emummc_path[ams::fs::EntryNameLengthMax + 1];
|
||||
char emummc_bug_path[ams::fs::EntryNameLengthMax + 1];
|
||||
util::SNPrintf(emummc_path, sizeof(emummc_path), "%s:/%s", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
|
||||
util::SNPrintf(emummc_bug_path, sizeof(emummc_bug_path), "%s:/%s/Nintendo", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
|
||||
|
||||
if (HasDir(emummc_bug_path)) {
|
||||
/* Ensure Contents directory exists for normal emummc. */
|
||||
/* NOTE: Allowed to fail on already-exists. */
|
||||
util::SNPrintf(emummc_path, sizeof(emummc_path), "%s:/%s/Contents", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
|
||||
ams::fs::CreateDirectory(emummc_path);
|
||||
|
||||
/* Fix Contents/private */
|
||||
util::SNPrintf(emummc_path, sizeof(emummc_path), "%s:/%s/Contents/private", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
|
||||
util::SNPrintf(emummc_bug_path, sizeof(emummc_bug_path), "%s:/%s/Nintendo/Contents/private", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
|
||||
if (HasFile(emummc_bug_path) && !HasFile(emummc_path)) {
|
||||
R_ABORT_UNLESS(ams::fs::RenameFile(emummc_bug_path, emummc_path));
|
||||
}
|
||||
|
||||
/* Fix Contents/private1 */
|
||||
util::SNPrintf(emummc_path, sizeof(emummc_path), "%s:/%s/Contents/private1", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
|
||||
util::SNPrintf(emummc_bug_path, sizeof(emummc_bug_path), "%s:/%s/Nintendo/Contents/private1", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
|
||||
if (HasFile(emummc_bug_path) && !HasFile(emummc_path)) {
|
||||
R_ABORT_UNLESS(ams::fs::RenameFile(emummc_bug_path, emummc_path));
|
||||
}
|
||||
|
||||
/* Delete bug directory. */
|
||||
util::SNPrintf(emummc_bug_path, sizeof(emummc_bug_path), "%s:/%s/Nintendo", ams::fs::impl::SdCardFileSystemMountName, emummc::GetNintendoDirPath());
|
||||
R_ABORT_UNLESS(ams::fs::DeleteDirectoryRecursively(emummc_bug_path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Connect to set:sys. */
|
||||
R_ABORT_UNLESS(setInitialize());
|
||||
R_ABORT_UNLESS(setsysInitialize());
|
||||
|
||||
/* Load settings off the SD card. */
|
||||
settings::fwdbg::InitializeSdCardKeyValueStore();
|
||||
|
||||
/* Ensure that we reboot using the user's preferred method. */
|
||||
R_ABORT_UNLESS(mitm::bpc::DetectPreferredRebootFunctionality());
|
||||
|
||||
/* Signal to waiters that we are ready. */
|
||||
g_init_event.Signal();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void StartInitialize() {
|
||||
/* Initialize prodinfo. */
|
||||
mitm::InitializeProdInfoManagement();
|
||||
|
||||
/* Launch initialize thread. */
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_initialize_thread), InitializeThreadFunc, nullptr, g_initialize_thread_stack, sizeof(g_initialize_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(mitm, InitializeThread)));
|
||||
os::SetThreadNamePointer(std::addressof(g_initialize_thread), AMS_GET_SYSTEM_THREAD_NAME(mitm, InitializeThread));
|
||||
os::StartThread(std::addressof(g_initialize_thread));
|
||||
}
|
||||
|
||||
bool IsInitialized() {
|
||||
return g_init_event.TryWait();
|
||||
}
|
||||
|
||||
void WaitInitialized() {
|
||||
g_init_event.Wait();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm {
|
||||
|
||||
void StartInitialize();
|
||||
|
||||
bool IsInitialized();
|
||||
void WaitInitialized();
|
||||
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "amsmitm_initialization.hpp"
|
||||
#include "amsmitm_module_management.hpp"
|
||||
#include "bpc_mitm/bpc_ams_power_utils.hpp"
|
||||
#include "sysupdater/sysupdater_fs_utils.hpp"
|
||||
|
||||
namespace ams {
|
||||
|
||||
namespace {
|
||||
|
||||
/* TODO: we really shouldn't be using malloc just to avoid dealing with real allocator separation. */
|
||||
constexpr size_t MallocBufferSize = 12_MB;
|
||||
alignas(os::MemoryPageSize) constinit u8 g_malloc_buffer[MallocBufferSize];
|
||||
|
||||
}
|
||||
|
||||
namespace init {
|
||||
|
||||
void InitializeSystemModule() {
|
||||
/* Initialize our connection to sm. */
|
||||
R_ABORT_UNLESS(sm::Initialize());
|
||||
|
||||
/* Initialize fs. */
|
||||
fs::InitializeForSystem();
|
||||
fs::SetEnabledAutoAbort(false);
|
||||
|
||||
/* Initialize other services. */
|
||||
R_ABORT_UNLESS(pmdmntInitialize());
|
||||
R_ABORT_UNLESS(pminfoInitialize());
|
||||
ncm::Initialize();
|
||||
|
||||
/* Verify that we can sanely execute. */
|
||||
ams::CheckApiVersion();
|
||||
}
|
||||
|
||||
void FinalizeSystemModule() { /* ... */ }
|
||||
|
||||
void Startup() {
|
||||
/* Initialize the global malloc allocator. */
|
||||
init::InitializeAllocator(g_malloc_buffer, sizeof(g_malloc_buffer));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ExceptionHandler(FatalErrorContext *ctx) {
|
||||
/* We're bpc-mitm (or ams_mitm, anyway), so manually reboot to fatal error. */
|
||||
mitm::bpc::RebootForFatalError(ctx);
|
||||
}
|
||||
|
||||
void NORETURN Exit(int rc) {
|
||||
AMS_UNUSED(rc);
|
||||
AMS_ABORT("Exit called by immortal process");
|
||||
}
|
||||
|
||||
void Main() {
|
||||
/* Register "ams" port, use up its session. */
|
||||
{
|
||||
svc::Handle ams_port;
|
||||
R_ABORT_UNLESS(svc::ManageNamedPort(std::addressof(ams_port), "ams", 1));
|
||||
|
||||
svc::Handle ams_session;
|
||||
R_ABORT_UNLESS(svc::ConnectToNamedPort(std::addressof(ams_session), "ams"));
|
||||
}
|
||||
|
||||
/* Initialize fssystem library. */
|
||||
fssystem::InitializeForAtmosphereMitm();
|
||||
|
||||
/* Configure ncm to use fssystem library to mount content from the sd card. */
|
||||
ncm::SetMountContentMetaFunction(mitm::sysupdater::MountSdCardContentMeta);
|
||||
|
||||
/* Launch all mitm modules in sequence. */
|
||||
mitm::LaunchAllModules();
|
||||
|
||||
/* Wait for all mitm modules to end. */
|
||||
mitm::WaitAllModules();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm {
|
||||
|
||||
template<typename T>
|
||||
concept IsModule = requires(T, void *arg) {
|
||||
{ T::ThreadPriority } -> std::convertible_to<s32>;
|
||||
{ T::StackSize } -> std::convertible_to<size_t>;
|
||||
{ T::Stack } -> std::convertible_to<void *>;
|
||||
{ T::ThreadFunction(arg) } -> std::same_as<void>;
|
||||
};
|
||||
|
||||
#define DEFINE_MITM_MODULE_CLASS(ss, prio) class MitmModule { \
|
||||
public: \
|
||||
static constexpr s32 ThreadPriority = prio; \
|
||||
static constexpr size_t StackSize = ss; \
|
||||
alignas(os::ThreadStackAlignment) static inline u8 Stack[StackSize]; \
|
||||
public: \
|
||||
static void ThreadFunction(void *); \
|
||||
}
|
||||
|
||||
template<class M> requires IsModule<M>
|
||||
struct ModuleTraits {
|
||||
static constexpr void *Stack = &M::Stack[0];
|
||||
static constexpr size_t StackSize = M::StackSize;
|
||||
|
||||
static constexpr s32 ThreadPriority = M::ThreadPriority;
|
||||
|
||||
static constexpr ::ThreadFunc ThreadFunction = &M::ThreadFunction;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "amsmitm_module_management.hpp"
|
||||
#include "amsmitm_module.hpp"
|
||||
|
||||
#include "fs_mitm/fsmitm_module.hpp"
|
||||
#include "set_mitm/setmitm_module.hpp"
|
||||
#include "bpc_mitm/bpcmitm_module.hpp"
|
||||
#include "bpc_mitm/bpc_ams_module.hpp"
|
||||
#include "ns_mitm/nsmitm_module.hpp"
|
||||
#include "dns_mitm/dnsmitm_module.hpp"
|
||||
#include "sysupdater/sysupdater_module.hpp"
|
||||
#include "mitm_pm/mitm_pm_module.hpp"
|
||||
|
||||
namespace ams::mitm {
|
||||
|
||||
namespace {
|
||||
|
||||
enum ModuleId : u32 {
|
||||
ModuleId_FsMitm,
|
||||
ModuleId_SetMitm,
|
||||
ModuleId_BpcMitm,
|
||||
ModuleId_BpcAms,
|
||||
ModuleId_NsMitm,
|
||||
ModuleId_DnsMitm,
|
||||
ModuleId_Sysupdater,
|
||||
ModuleId_PmService,
|
||||
|
||||
ModuleId_Count,
|
||||
};
|
||||
|
||||
struct ModuleDefinition {
|
||||
ThreadFunc main;
|
||||
void *stack_mem;
|
||||
s32 priority;
|
||||
u32 stack_size;
|
||||
};
|
||||
|
||||
template<class M>
|
||||
constexpr ModuleDefinition GetModuleDefinition() {
|
||||
using Traits = ModuleTraits<M>;
|
||||
|
||||
return ModuleDefinition {
|
||||
.main = Traits::ThreadFunction,
|
||||
.stack_mem = Traits::Stack,
|
||||
.priority = Traits::ThreadPriority,
|
||||
.stack_size = static_cast<u32>(Traits::StackSize),
|
||||
};
|
||||
}
|
||||
|
||||
ams::os::ThreadType g_module_threads[ModuleId_Count];
|
||||
|
||||
constexpr ModuleDefinition g_module_definitions[ModuleId_Count] = {
|
||||
GetModuleDefinition<fs::MitmModule>(),
|
||||
GetModuleDefinition<settings::MitmModule>(),
|
||||
GetModuleDefinition<bpc::MitmModule>(),
|
||||
GetModuleDefinition<bpc_ams::MitmModule>(),
|
||||
GetModuleDefinition<ns::MitmModule>(),
|
||||
GetModuleDefinition<socket::resolver::MitmModule>(),
|
||||
GetModuleDefinition<sysupdater::MitmModule>(),
|
||||
GetModuleDefinition<pm::MitmModule>(),
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void LaunchAllModules() {
|
||||
/* Create thread for each module. */
|
||||
for (u32 i = 0; i < static_cast<u32>(ModuleId_Count); i++) {
|
||||
const ModuleDefinition &cur_module = g_module_definitions[i];
|
||||
R_ABORT_UNLESS(os::CreateThread(g_module_threads + i, cur_module.main, nullptr, cur_module.stack_mem, cur_module.stack_size, cur_module.priority));
|
||||
}
|
||||
|
||||
/* Start thread for each module. */
|
||||
for (u32 i = 0; i < static_cast<u32>(ModuleId_Count); i++) {
|
||||
os::StartThread(g_module_threads + i);
|
||||
}
|
||||
}
|
||||
|
||||
void WaitAllModules() {
|
||||
/* Wait on thread for each module. */
|
||||
for (u32 i = 0; i < static_cast<u32>(ModuleId_Count); i++) {
|
||||
os::WaitThread(g_module_threads + i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm {
|
||||
|
||||
void LaunchAllModules();
|
||||
void WaitAllModules();
|
||||
|
||||
}
|
||||
@@ -1,637 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "amsmitm_fs_utils.hpp"
|
||||
#include "amsmitm_prodinfo_utils.hpp"
|
||||
|
||||
namespace ams::mitm {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline u16 Crc16InitialValue = 0x55AA;
|
||||
|
||||
constexpr inline u16 Crc16Table[] = {
|
||||
0x0000, 0xCC01, 0xD801, 0x1400,
|
||||
0xF001, 0x3C00, 0x2800, 0xE401,
|
||||
0xA001, 0x6C00, 0x7800, 0xB401,
|
||||
0x5000, 0x9C01, 0x8801, 0x4400,
|
||||
};
|
||||
|
||||
u16 GetCrc16(const void *data, size_t size) {
|
||||
AMS_ASSERT(data != nullptr);
|
||||
AMS_ASSERT(size > 0);
|
||||
|
||||
const u8 *src = static_cast<const u8 *>(data);
|
||||
|
||||
u16 crc = Crc16InitialValue;
|
||||
|
||||
u16 tmp = 0;
|
||||
while ((size--) > 0) {
|
||||
tmp = Crc16Table[crc & 0xF];
|
||||
crc = ((crc >> 4) & 0x0FFF) ^ tmp ^ Crc16Table[*src & 0xF];
|
||||
tmp = Crc16Table[crc & 0xF];
|
||||
crc = ((crc >> 4) & 0x0FFF) ^ tmp ^ Crc16Table[(*(src++) >> 4) & 0xF];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
bool IsBlank(const void *data, size_t size) {
|
||||
AMS_ASSERT(data != nullptr);
|
||||
AMS_ASSERT(size > 0);
|
||||
|
||||
const u8 *src = static_cast<const u8 *>(data);
|
||||
while ((size--) > 0) {
|
||||
if (*(src++) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr inline u32 CalibrationMagic = util::FourCC<'C','A','L','0'>::Code;
|
||||
|
||||
struct Sha256Hash {
|
||||
u8 data[crypto::Sha256Generator::HashSize];
|
||||
};
|
||||
|
||||
struct CalibrationInfoHeader {
|
||||
u32 magic;
|
||||
u32 version;
|
||||
u32 body_size;
|
||||
u16 model;
|
||||
u16 update_count;
|
||||
u8 pad[0xE];
|
||||
u16 crc;
|
||||
Sha256Hash body_hash;
|
||||
};
|
||||
static_assert(sizeof(CalibrationInfoHeader) == 0x40);
|
||||
|
||||
constexpr inline size_t CalibrationInfoBodySizeMax = CalibrationBinarySize - sizeof(CalibrationInfoHeader);
|
||||
|
||||
struct CalibrationInfo {
|
||||
CalibrationInfoHeader header;
|
||||
u8 body[CalibrationInfoBodySizeMax]; /* TODO: CalibrationInfoBody body; */
|
||||
|
||||
template<typename Block>
|
||||
Block &GetBlock() {
|
||||
static_assert(Block::Offset >= sizeof(CalibrationInfoHeader));
|
||||
static_assert(Block::Offset < sizeof(CalibrationInfo));
|
||||
static_assert(Block::Offset + Block::Size <= sizeof(CalibrationInfo));
|
||||
return *static_cast<Block *>(static_cast<void *>(std::addressof(this->body[Block::Offset - sizeof(this->header)])));
|
||||
}
|
||||
|
||||
template<typename Block>
|
||||
const Block &GetBlock() const {
|
||||
static_assert(Block::Offset >= sizeof(CalibrationInfoHeader));
|
||||
static_assert(Block::Offset < sizeof(CalibrationInfo));
|
||||
static_assert(Block::Offset + Block::Size <= sizeof(CalibrationInfo));
|
||||
return *static_cast<const Block *>(static_cast<const void *>(std::addressof(this->body[Block::Offset - sizeof(this->header)])));
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(CalibrationInfo) == CalibrationBinarySize);
|
||||
|
||||
struct SecureCalibrationInfoBackup {
|
||||
CalibrationInfo info;
|
||||
Sha256Hash hash;
|
||||
u8 pad[SecureCalibrationBinaryBackupSize - sizeof(info) - sizeof(hash)];
|
||||
};
|
||||
static_assert(sizeof(SecureCalibrationInfoBackup) == SecureCalibrationBinaryBackupSize);
|
||||
|
||||
bool IsValidSha256Hash(const Sha256Hash &hash, const void *data, size_t data_size) {
|
||||
Sha256Hash calc_hash;
|
||||
ON_SCOPE_EXIT { ::ams::crypto::ClearMemory(std::addressof(calc_hash), sizeof(calc_hash)); };
|
||||
|
||||
::ams::crypto::GenerateSha256(std::addressof(calc_hash), sizeof(calc_hash), data, data_size);
|
||||
return ::ams::crypto::IsSameBytes(std::addressof(calc_hash), std::addressof(hash), sizeof(Sha256Hash));
|
||||
}
|
||||
|
||||
bool IsValid(const CalibrationInfoHeader &header) {
|
||||
return header.magic == CalibrationMagic && GetCrc16(std::addressof(header), AMS_OFFSETOF(CalibrationInfoHeader, crc)) == header.crc;
|
||||
}
|
||||
|
||||
bool IsValid(const CalibrationInfoHeader &header, const void *body) {
|
||||
return IsValid(header) && IsValidSha256Hash(header.body_hash, body, header.body_size);
|
||||
}
|
||||
|
||||
#define DEFINE_CALIBRATION_CRC_BLOCK(_TypeName, _Offset, _Size, _Decl, _MemberName) \
|
||||
struct _TypeName { \
|
||||
static constexpr size_t Offset = _Offset; \
|
||||
static constexpr size_t Size = _Size; \
|
||||
static constexpr bool IsCrcBlock = true; \
|
||||
static constexpr bool IsShaBlock = false; \
|
||||
_Decl; \
|
||||
static_assert(Size >= sizeof(_MemberName) + sizeof(u16)); \
|
||||
u8 pad[Size - sizeof(_MemberName) - sizeof(u16)]; \
|
||||
u16 crc; \
|
||||
}; \
|
||||
static_assert(sizeof(_TypeName) == _TypeName::Size)
|
||||
|
||||
#define DEFINE_CALIBRATION_SHA_BLOCK(_TypeName, _Offset, _Size, _Decl, _MemberName) \
|
||||
struct _TypeName { \
|
||||
static constexpr size_t Offset = _Offset; \
|
||||
static constexpr size_t Size = _Size; \
|
||||
static constexpr bool IsCrcBlock = false; \
|
||||
static constexpr bool IsShaBlock = true; \
|
||||
_Decl; \
|
||||
static_assert(Size == sizeof(_MemberName) + sizeof(Sha256Hash)); \
|
||||
Sha256Hash sha256_hash; \
|
||||
}; \
|
||||
static_assert(sizeof(_TypeName) == _TypeName::Size)
|
||||
|
||||
DEFINE_CALIBRATION_CRC_BLOCK(SerialNumberBlock, 0x0250, 0x020, ::ams::settings::factory::SerialNumber serial_number, serial_number);
|
||||
DEFINE_CALIBRATION_CRC_BLOCK(EccB233DeviceCertificateBlock, 0x0480, 0x190, ::ams::settings::factory::EccB233DeviceCertificate device_certificate, device_certificate);
|
||||
DEFINE_CALIBRATION_CRC_BLOCK(SslKeyBlock, 0x09B0, 0x120, u8 ssl_key[0x110], ssl_key);
|
||||
DEFINE_CALIBRATION_CRC_BLOCK(SslCertificateSizeBlock, 0x0AD0, 0x010, u64 ssl_certificate_size, ssl_certificate_size);
|
||||
DEFINE_CALIBRATION_SHA_BLOCK(SslCertificateBlock, 0x0AE0, 0x820, u8 ssl_certificate[0x800], ssl_certificate);
|
||||
DEFINE_CALIBRATION_CRC_BLOCK(EcqvEcdsaAmiiboRootCertificateBlock, 0x35A0, 0x080, u8 data[0x70], data);
|
||||
DEFINE_CALIBRATION_CRC_BLOCK(EcqvBlsAmiiboRootCertificateBlock, 0x36A0, 0x0A0, u8 data[0x90], data);
|
||||
DEFINE_CALIBRATION_CRC_BLOCK(ExtendedSslKeyBlock, 0x3AE0, 0x140, u8 ssl_key[0x134], ssl_key);
|
||||
DEFINE_CALIBRATION_CRC_BLOCK(Rsa2048DeviceKeyBlock, 0x3D70, 0x250, u8 device_key[0x240], device_key);
|
||||
DEFINE_CALIBRATION_CRC_BLOCK(Rsa2048DeviceCertificateBlock, 0x3FC0, 0x250, ::ams::settings::factory::Rsa2048DeviceCertificate device_certificate, device_certificate);
|
||||
|
||||
#undef DEFINE_CALIBRATION_CRC_BLOCK
|
||||
#undef DEFINE_CALIBRATION_SHA_BLOCK
|
||||
|
||||
constexpr inline const char BlankSerialNumberString[] = "XAW00000000000";
|
||||
|
||||
template<typename Block>
|
||||
void Blank(Block &block) {
|
||||
if constexpr (std::is_same<Block, SerialNumberBlock>::value) {
|
||||
static_assert(sizeof(BlankSerialNumberString) <= sizeof(SerialNumberBlock::serial_number));
|
||||
std::memset(std::addressof(block), 0, Block::Size - sizeof(block.crc));
|
||||
std::memcpy(block.serial_number.str, BlankSerialNumberString, sizeof(BlankSerialNumberString));
|
||||
block.crc = GetCrc16(std::addressof(block), Block::Size - sizeof(block.crc));
|
||||
} else if constexpr (std::is_same<Block, SslCertificateBlock>::value) {
|
||||
std::memset(std::addressof(block), 0, sizeof(block.ssl_certificate));
|
||||
} else if constexpr (Block::IsCrcBlock) {
|
||||
std::memset(std::addressof(block), 0, Block::Size - sizeof(block.crc));
|
||||
block.crc = GetCrc16(std::addressof(block), Block::Size - sizeof(block.crc));
|
||||
} else {
|
||||
static_assert(Block::IsShaBlock);
|
||||
std::memset(std::addressof(block), 0, Block::Size);
|
||||
::ams::crypto::GenerateSha256(std::addressof(block.sha256_hash), sizeof(block.sha256_hash), std::addressof(block), Block::Size - sizeof(block.sha256_hash));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Block>
|
||||
bool IsBlank(const Block &block) {
|
||||
if constexpr (std::is_same<Block, SerialNumberBlock>::value) {
|
||||
static_assert(sizeof(BlankSerialNumberString) <= sizeof(SerialNumberBlock::serial_number));
|
||||
return std::memcmp(block.serial_number.str, BlankSerialNumberString, sizeof(BlankSerialNumberString) - 1) == 0 || IsBlank(std::addressof(block), Block::Size - sizeof(block.crc));
|
||||
} else if constexpr (Block::IsCrcBlock) {
|
||||
return IsBlank(std::addressof(block), Block::Size - sizeof(block.crc));
|
||||
} else {
|
||||
return IsBlank(std::addressof(block), Block::Size - sizeof(block.sha256_hash));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Block>
|
||||
bool IsValid(const Block &block, size_t size = 0) {
|
||||
if constexpr (Block::IsCrcBlock) {
|
||||
return GetCrc16(std::addressof(block), Block::Size - sizeof(block.crc)) == block.crc;
|
||||
} else {
|
||||
static_assert(Block::IsShaBlock);
|
||||
return IsValidSha256Hash(block.sha256_hash, std::addressof(block), size != 0 ? size : Block::Size - sizeof(block.sha256_hash));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Blank(CalibrationInfo &info) {
|
||||
/* Set header. */
|
||||
info.header.magic = CalibrationMagic;
|
||||
info.header.body_size = sizeof(info.body);
|
||||
info.header.crc = GetCrc16(std::addressof(info.header), AMS_OFFSETOF(CalibrationInfoHeader, crc));
|
||||
|
||||
/* Set blocks. */
|
||||
Blank(info.GetBlock<SerialNumberBlock>());
|
||||
Blank(info.GetBlock<SslCertificateSizeBlock>());
|
||||
Blank(info.GetBlock<SslCertificateBlock>());
|
||||
Blank(info.GetBlock<EcqvEcdsaAmiiboRootCertificateBlock>());
|
||||
Blank(info.GetBlock<EcqvBlsAmiiboRootCertificateBlock>());
|
||||
Blank(info.GetBlock<ExtendedSslKeyBlock>());
|
||||
|
||||
/* Set header hash. */
|
||||
crypto::GenerateSha256(std::addressof(info.header.body_hash), sizeof(info.header.body_hash), std::addressof(info.body), sizeof(info.body));
|
||||
}
|
||||
|
||||
bool IsValidHeader(const CalibrationInfo &cal) {
|
||||
return IsValid(cal.header) && cal.header.body_size <= CalibrationInfoBodySizeMax && IsValid(cal.header, cal.body);
|
||||
}
|
||||
|
||||
bool IsValidSerialNumber(const char *sn) {
|
||||
for (size_t i = 0; i < std::strlen(sn); i++) {
|
||||
if (!std::isalnum(static_cast<unsigned char>(sn[i]))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GetSerialNumber(char *dst, const CalibrationInfo &info) {
|
||||
std::memcpy(dst, std::addressof(info.GetBlock<SerialNumberBlock>()), sizeof(info.GetBlock<SerialNumberBlock>().serial_number));
|
||||
dst[sizeof(info.GetBlock<SerialNumberBlock>().serial_number) + 1] = '\x00';
|
||||
}
|
||||
|
||||
bool IsValidSerialNumber(const CalibrationInfo &cal) {
|
||||
char sn[0x20] = {};
|
||||
ON_SCOPE_EXIT { std::memset(sn, 0, sizeof(sn)); };
|
||||
|
||||
GetSerialNumber(sn, cal);
|
||||
return IsValidSerialNumber(sn);
|
||||
}
|
||||
|
||||
bool IsValid(const CalibrationInfo &cal) {
|
||||
return IsValidHeader(cal) &&
|
||||
IsValid(cal.GetBlock<SerialNumberBlock>()) &&
|
||||
IsValid(cal.GetBlock<EccB233DeviceCertificateBlock>()) &&
|
||||
IsValid(cal.GetBlock<SslKeyBlock>()) &&
|
||||
IsValid(cal.GetBlock<SslCertificateSizeBlock>()) &&
|
||||
cal.GetBlock<SslCertificateSizeBlock>().ssl_certificate_size <= sizeof(cal.GetBlock<SslCertificateBlock>().ssl_certificate) &&
|
||||
IsValid(cal.GetBlock<SslCertificateBlock>(), cal.GetBlock<SslCertificateSizeBlock>().ssl_certificate_size) &&
|
||||
IsValid(cal.GetBlock<EcqvEcdsaAmiiboRootCertificateBlock>()) &&
|
||||
IsValid(cal.GetBlock<EcqvBlsAmiiboRootCertificateBlock>()) &&
|
||||
IsValid(cal.GetBlock<ExtendedSslKeyBlock>()) &&
|
||||
IsValidSerialNumber(cal);
|
||||
}
|
||||
|
||||
bool ContainsCorrectDeviceId(const EccB233DeviceCertificateBlock &block, u64 device_id) {
|
||||
static constexpr size_t DeviceIdOffset = 0xC6;
|
||||
char found_device_id_str[sizeof("0011223344556677")] = {};
|
||||
ON_SCOPE_EXIT { std::memset(found_device_id_str, 0, sizeof(found_device_id_str)); };
|
||||
std::memcpy(found_device_id_str, std::addressof(block.device_certificate.data[DeviceIdOffset]), sizeof(found_device_id_str) - 1);
|
||||
|
||||
static constexpr u64 DeviceIdLowMask = 0x00FFFFFFFFFFFFFFul;
|
||||
|
||||
return (std::strtoul(found_device_id_str, nullptr, 16) & DeviceIdLowMask) == (device_id & DeviceIdLowMask);
|
||||
}
|
||||
|
||||
bool ContainsCorrectDeviceId(const CalibrationInfo &cal) {
|
||||
return ContainsCorrectDeviceId(cal.GetBlock<EccB233DeviceCertificateBlock>(), exosphere::GetDeviceId());
|
||||
}
|
||||
|
||||
bool IsValidForSecureBackup(const CalibrationInfo &cal) {
|
||||
return IsValid(cal) && ContainsCorrectDeviceId(cal);
|
||||
}
|
||||
|
||||
bool IsBlank(const CalibrationInfo &cal) {
|
||||
return IsBlank(cal.GetBlock<SerialNumberBlock>()) ||
|
||||
IsBlank(cal.GetBlock<SslCertificateSizeBlock>()) ||
|
||||
IsBlank(cal.GetBlock<SslCertificateBlock>()) ||
|
||||
IsBlank(cal.GetBlock<EcqvEcdsaAmiiboRootCertificateBlock>()) ||
|
||||
IsBlank(cal.GetBlock<EcqvBlsAmiiboRootCertificateBlock>()) ||
|
||||
IsBlank(cal.GetBlock<ExtendedSslKeyBlock>());
|
||||
}
|
||||
|
||||
void ReadStorageCalibrationBinary(CalibrationInfo *out) {
|
||||
FsStorage calibration_binary_storage;
|
||||
R_ABORT_UNLESS(fsOpenBisStorage(std::addressof(calibration_binary_storage), FsBisPartitionId_CalibrationBinary));
|
||||
ON_SCOPE_EXIT { fsStorageClose(std::addressof(calibration_binary_storage)); };
|
||||
|
||||
R_ABORT_UNLESS(fsStorageRead(std::addressof(calibration_binary_storage), 0, out, sizeof(*out)));
|
||||
}
|
||||
|
||||
constexpr inline const u8 SecureCalibrationBinaryBackupIv[crypto::Aes128CtrDecryptor::IvSize] = {};
|
||||
|
||||
void ReadStorageEncryptedSecureCalibrationBinaryBackupUnsafe(SecureCalibrationInfoBackup *out) {
|
||||
FsStorage calibration_binary_storage;
|
||||
R_ABORT_UNLESS(fsOpenBisStorage(std::addressof(calibration_binary_storage), FsBisPartitionId_CalibrationBinary));
|
||||
ON_SCOPE_EXIT { fsStorageClose(std::addressof(calibration_binary_storage)); };
|
||||
|
||||
R_ABORT_UNLESS(fsStorageRead(std::addressof(calibration_binary_storage), SecureCalibrationInfoBackupOffset, out, sizeof(*out)));
|
||||
}
|
||||
|
||||
void WriteStorageEncryptedSecureCalibrationBinaryBackupUnsafe(const SecureCalibrationInfoBackup *src) {
|
||||
FsStorage calibration_binary_storage;
|
||||
R_ABORT_UNLESS(fsOpenBisStorage(std::addressof(calibration_binary_storage), FsBisPartitionId_CalibrationBinary));
|
||||
ON_SCOPE_EXIT { fsStorageClose(std::addressof(calibration_binary_storage)); };
|
||||
|
||||
R_ABORT_UNLESS(fsStorageWrite(std::addressof(calibration_binary_storage), SecureCalibrationInfoBackupOffset, src, sizeof(*src)));
|
||||
}
|
||||
|
||||
void GenerateSecureCalibrationBinaryBackupKey(void *dst, size_t dst_size) {
|
||||
static constexpr const u8 SecureCalibrationBinaryBackupKeySource[crypto::Aes128CtrDecryptor::KeySize] = { '|', '-', 'A', 'M', 'S', '-', 'C', 'A', 'L', '0', '-', 'K', 'E', 'Y', '-', '|' };
|
||||
spl::AccessKey access_key;
|
||||
ON_SCOPE_EXIT { crypto::ClearMemory(std::addressof(access_key), sizeof(access_key)); };
|
||||
|
||||
/* Generate a personalized kek. */
|
||||
R_ABORT_UNLESS(spl::GenerateAesKek(std::addressof(access_key), SecureCalibrationBinaryBackupKeySource, sizeof(SecureCalibrationBinaryBackupKeySource), 0, 1));
|
||||
|
||||
/* Generate a personalized key. */
|
||||
R_ABORT_UNLESS(spl::GenerateAesKey(dst, dst_size, access_key, SecureCalibrationBinaryBackupKeySource, sizeof(SecureCalibrationBinaryBackupKeySource)));
|
||||
}
|
||||
|
||||
bool ReadStorageSecureCalibrationBinaryBackup(SecureCalibrationInfoBackup *out) {
|
||||
/* Read the data. */
|
||||
ReadStorageEncryptedSecureCalibrationBinaryBackupUnsafe(out);
|
||||
|
||||
/* Don't leak any data unless we validate. */
|
||||
auto clear_guard = SCOPE_GUARD { std::memset(out, 0, sizeof(*out)); };
|
||||
|
||||
{
|
||||
/* Create a buffer to hold our key. */
|
||||
u8 key[crypto::Aes128CtrDecryptor::KeySize];
|
||||
ON_SCOPE_EXIT { crypto::ClearMemory(key, sizeof(key)); };
|
||||
|
||||
/* Generate the key. */
|
||||
GenerateSecureCalibrationBinaryBackupKey(key, sizeof(key));
|
||||
|
||||
/* Decrypt the data in place. */
|
||||
crypto::DecryptAes128Ctr(out, sizeof(*out), key, sizeof(key), SecureCalibrationBinaryBackupIv, sizeof(SecureCalibrationBinaryBackupIv), out, sizeof(*out));
|
||||
}
|
||||
|
||||
/* Generate a hash for the data. */
|
||||
if (!IsValidSha256Hash(out->hash, std::addressof(out->info), sizeof(out->info))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Validate the backup. */
|
||||
if (!IsValidForSecureBackup(out->info)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Our backup is valid. */
|
||||
clear_guard.Cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
void WriteStorageSecureCalibrationBinaryBackup(SecureCalibrationInfoBackup *src) {
|
||||
/* Clear the input once we've written it. */
|
||||
ON_SCOPE_EXIT { std::memset(src, 0, sizeof(*src)); };
|
||||
|
||||
/* Ensure that the input is valid. */
|
||||
AMS_ABORT_UNLESS(IsValidForSecureBackup(src->info));
|
||||
|
||||
/* Set the Sha256 hash. */
|
||||
crypto::GenerateSha256(std::addressof(src->hash), sizeof(src->hash), std::addressof(src->info), sizeof(src->info));
|
||||
|
||||
/* Validate the hash. */
|
||||
AMS_ABORT_UNLESS(IsValidSha256Hash(src->hash, std::addressof(src->info), sizeof(src->info)));
|
||||
|
||||
/* Encrypt the data. */
|
||||
{
|
||||
/* Create a buffer to hold our key. */
|
||||
u8 key[crypto::Aes128CtrDecryptor::KeySize];
|
||||
ON_SCOPE_EXIT { crypto::ClearMemory(key, sizeof(key)); };
|
||||
|
||||
/* Generate the key. */
|
||||
GenerateSecureCalibrationBinaryBackupKey(key, sizeof(key));
|
||||
|
||||
/* Encrypt the data in place. */
|
||||
crypto::EncryptAes128Ctr(src, sizeof(*src), key, sizeof(key), SecureCalibrationBinaryBackupIv, sizeof(SecureCalibrationBinaryBackupIv), src, sizeof(*src));
|
||||
}
|
||||
|
||||
/* Write the encrypted data. */
|
||||
WriteStorageEncryptedSecureCalibrationBinaryBackupUnsafe(src);
|
||||
}
|
||||
|
||||
void GetBackupFileName(char *dst, size_t dst_size, const CalibrationInfo &info) {
|
||||
char sn[0x20] = {};
|
||||
ON_SCOPE_EXIT { std::memset(sn, 0, sizeof(sn)); };
|
||||
|
||||
|
||||
if (IsValidForSecureBackup(info)) {
|
||||
GetSerialNumber(sn, info);
|
||||
util::SNPrintf(dst, dst_size, "automatic_backups/%s_PRODINFO.bin", sn);
|
||||
} else {
|
||||
Sha256Hash hash;
|
||||
crypto::GenerateSha256(std::addressof(hash), sizeof(hash), std::addressof(info), sizeof(info));
|
||||
ON_SCOPE_EXIT { crypto::ClearMemory(std::addressof(hash), sizeof(hash)); };
|
||||
|
||||
if (IsValid(info)) {
|
||||
if (IsBlank(info)) {
|
||||
util::SNPrintf(dst, dst_size, "automatic_backups/BLANK_PRODINFO_%02X%02X%02X%02X.bin", hash.data[0], hash.data[1], hash.data[2], hash.data[3]);
|
||||
} else {
|
||||
GetSerialNumber(sn, info);
|
||||
util::SNPrintf(dst, dst_size, "automatic_backups/%s_PRODINFO_%02X%02X%02X%02X.bin", sn, hash.data[0], hash.data[1], hash.data[2], hash.data[3]);
|
||||
}
|
||||
} else {
|
||||
util::SNPrintf(dst, dst_size, "automatic_backups/INVALID_PRODINFO_%02X%02X%02X%02X.bin", hash.data[0], hash.data[1], hash.data[2], hash.data[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SafeRead(ams::fs::fsa::IFile *file, s64 offset, void *dst, size_t size) {
|
||||
size_t read_size = 0;
|
||||
R_ABORT_UNLESS(file->Read(std::addressof(read_size), offset, dst, size));
|
||||
AMS_ABORT_UNLESS(read_size == size);
|
||||
}
|
||||
|
||||
alignas(os::MemoryPageSize) CalibrationInfo g_temp_calibration_info = {};
|
||||
|
||||
void SaveProdInfoBackup(util::optional<ams::fs::FileStorage> *dst, const CalibrationInfo &info) {
|
||||
char backup_fn[0x100];
|
||||
GetBackupFileName(backup_fn, sizeof(backup_fn), info);
|
||||
|
||||
/* Create the file, in case it does not exist. */
|
||||
mitm::fs::CreateAtmosphereSdFile(backup_fn, sizeof(CalibrationInfo), ams::fs::CreateOption_None);
|
||||
|
||||
/* Open the file. */
|
||||
FsFile libnx_file;
|
||||
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(libnx_file), backup_fn, ams::fs::OpenMode_ReadWrite));
|
||||
|
||||
/* Create our accessor. */
|
||||
std::unique_ptr<ams::fs::fsa::IFile> file = std::make_unique<ams::fs::RemoteFile>(libnx_file);
|
||||
AMS_ABORT_UNLESS(file != nullptr);
|
||||
|
||||
/* Check if we're valid already. */
|
||||
bool valid = false;
|
||||
s64 size;
|
||||
R_ABORT_UNLESS(file->GetSize(std::addressof(size)));
|
||||
if (size == sizeof(CalibrationInfo)) {
|
||||
SafeRead(file.get(), 0, std::addressof(g_temp_calibration_info), sizeof(g_temp_calibration_info));
|
||||
ON_SCOPE_EXIT { std::memset(std::addressof(g_temp_calibration_info), 0, sizeof(g_temp_calibration_info)); };
|
||||
|
||||
if (std::memcmp(std::addressof(info), std::addressof(g_temp_calibration_info), sizeof(CalibrationInfo)) == 0) {
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we're not valid, we need to save. */
|
||||
if (!valid) {
|
||||
R_ABORT_UNLESS(file->Write(0, std::addressof(info), sizeof(info), ams::fs::WriteOption::Flush));
|
||||
}
|
||||
|
||||
/* Save our storage to output. */
|
||||
if (dst != nullptr) {
|
||||
dst->emplace(std::move(file));
|
||||
}
|
||||
}
|
||||
|
||||
void GetRandomEntropy(Sha256Hash *dst) {
|
||||
AMS_ASSERT(dst != nullptr);
|
||||
|
||||
u64 data_buffer[3] = {};
|
||||
ON_SCOPE_EXIT { crypto::ClearMemory(data_buffer, sizeof(data_buffer)); };
|
||||
|
||||
data_buffer[0] = os::GetSystemTick().GetInt64Value();
|
||||
R_ABORT_UNLESS(svc::GetInfo(data_buffer + 1, svc::InfoType_AliasRegionAddress, svc::PseudoHandle::CurrentProcess, 0));
|
||||
if (hos::GetVersion() >= hos::Version_2_0_0) {
|
||||
R_ABORT_UNLESS(svc::GetInfo(data_buffer + 2, svc::InfoType_RandomEntropy, svc::InvalidHandle, (data_buffer[0] ^ (data_buffer[1] >> 24)) & 3));
|
||||
} else {
|
||||
data_buffer[2] = os::GetSystemTick().GetInt64Value();
|
||||
}
|
||||
|
||||
return crypto::GenerateSha256(dst, sizeof(*dst), data_buffer, sizeof(data_buffer));
|
||||
}
|
||||
|
||||
void FillWithGarbage(void *dst, size_t dst_size) {
|
||||
/* Get random entropy. */
|
||||
Sha256Hash entropy;
|
||||
ON_SCOPE_EXIT { crypto::ClearMemory(std::addressof(entropy), sizeof(entropy)); };
|
||||
GetRandomEntropy(std::addressof(entropy));
|
||||
|
||||
/* Clear dst. */
|
||||
std::memset(dst, 0xCC, dst_size);
|
||||
|
||||
/* Encrypt dst. */
|
||||
static_assert(sizeof(entropy) == crypto::Aes128CtrEncryptor::KeySize + crypto::Aes128CtrEncryptor::IvSize);
|
||||
crypto::EncryptAes128Ctr(dst, dst_size, entropy.data, crypto::Aes128CtrEncryptor::KeySize, entropy.data + crypto::Aes128CtrEncryptor::KeySize, crypto::Aes128CtrEncryptor::IvSize, dst, dst_size);
|
||||
}
|
||||
|
||||
alignas(os::MemoryPageSize) constinit CalibrationInfo g_calibration_info = {};
|
||||
alignas(os::MemoryPageSize) constinit CalibrationInfo g_blank_calibration_info = {};
|
||||
alignas(os::MemoryPageSize) constinit SecureCalibrationInfoBackup g_secure_calibration_info_backup = {};
|
||||
|
||||
constinit util::optional<ams::fs::FileStorage> g_prodinfo_backup_file;
|
||||
constinit util::optional<ams::fs::MemoryStorage> g_blank_prodinfo_storage;
|
||||
constinit util::optional<ams::fs::MemoryStorage> g_fake_secure_backup_storage;
|
||||
|
||||
constinit bool g_allow_writes = false;
|
||||
constinit bool g_has_secure_backup = false;
|
||||
|
||||
constinit os::SdkMutex g_prodinfo_management_lock;
|
||||
|
||||
}
|
||||
|
||||
void InitializeProdInfoManagement() {
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
|
||||
/* First, get our options. */
|
||||
const bool should_blank = exosphere::ShouldBlankProdInfo();
|
||||
bool allow_writes = exosphere::ShouldAllowWritesToProdInfo();
|
||||
|
||||
/* Next, read our prodinfo. */
|
||||
ReadStorageCalibrationBinary(std::addressof(g_calibration_info));
|
||||
|
||||
/* Next, check if we have a secure backup. */
|
||||
bool has_secure_backup = ReadStorageSecureCalibrationBinaryBackup(std::addressof(g_secure_calibration_info_backup));
|
||||
|
||||
/* Only allow writes if we have a secure backup. */
|
||||
if (allow_writes && !has_secure_backup) {
|
||||
/* If we can make a secure backup, great. */
|
||||
if (IsValidForSecureBackup(g_calibration_info)) {
|
||||
g_secure_calibration_info_backup.info = g_calibration_info;
|
||||
WriteStorageSecureCalibrationBinaryBackup(std::addressof(g_secure_calibration_info_backup));
|
||||
g_secure_calibration_info_backup.info = g_calibration_info;
|
||||
has_secure_backup = true;
|
||||
} else {
|
||||
/* Don't allow writes if we can't make a secure backup. */
|
||||
allow_writes = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure our preconditions are met. */
|
||||
AMS_ABORT_UNLESS(!allow_writes || has_secure_backup);
|
||||
|
||||
/* Set globals. */
|
||||
g_allow_writes = allow_writes;
|
||||
g_has_secure_backup = has_secure_backup;
|
||||
|
||||
/* If we should blank, do so. */
|
||||
if (should_blank) {
|
||||
g_blank_calibration_info = g_calibration_info;
|
||||
Blank(g_blank_calibration_info);
|
||||
g_blank_prodinfo_storage.emplace(std::addressof(g_blank_calibration_info), sizeof(g_blank_calibration_info));
|
||||
}
|
||||
|
||||
/* Ensure that we have a blank file only if we need one. */
|
||||
AMS_ABORT_UNLESS(should_blank == static_cast<bool>(g_blank_prodinfo_storage));
|
||||
}
|
||||
|
||||
void SaveProdInfoBackupsAndWipeMemory(char *out_name, size_t out_name_size) {
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
|
||||
ON_SCOPE_EXIT {
|
||||
FillWithGarbage(std::addressof(g_calibration_info), sizeof(g_calibration_info));
|
||||
FillWithGarbage(std::addressof(g_secure_calibration_info_backup), sizeof(g_secure_calibration_info_backup));
|
||||
};
|
||||
|
||||
/* Save our backup. We always prefer to save a secure copy of data over a non-secure one. */
|
||||
if (g_has_secure_backup) {
|
||||
GetSerialNumber(out_name, g_secure_calibration_info_backup.info);
|
||||
SaveProdInfoBackup(std::addressof(g_prodinfo_backup_file), g_secure_calibration_info_backup.info);
|
||||
} else {
|
||||
if (IsValid(g_calibration_info) && !IsBlank(g_calibration_info)) {
|
||||
GetSerialNumber(out_name, g_calibration_info);
|
||||
} else {
|
||||
Sha256Hash hash;
|
||||
ON_SCOPE_EXIT { crypto::ClearMemory(std::addressof(hash), sizeof(hash)); };
|
||||
crypto::GenerateSha256(std::addressof(hash), sizeof(hash), std::addressof(g_calibration_info), sizeof(g_calibration_info));
|
||||
|
||||
util::SNPrintf(out_name, out_name_size, "%02X%02X%02X%02X", hash.data[0], hash.data[1], hash.data[2], hash.data[3]);
|
||||
}
|
||||
SaveProdInfoBackup(std::addressof(g_prodinfo_backup_file), g_calibration_info);
|
||||
}
|
||||
|
||||
/* Ensure we made our backup. */
|
||||
AMS_ABORT_UNLESS(g_prodinfo_backup_file);
|
||||
|
||||
/* Setup our memory storage. */
|
||||
g_fake_secure_backup_storage.emplace(std::addressof(g_secure_calibration_info_backup), sizeof(g_secure_calibration_info_backup));
|
||||
|
||||
/* Ensure that we have a fake storage. */
|
||||
AMS_ABORT_UNLESS(static_cast<bool>(g_fake_secure_backup_storage));
|
||||
}
|
||||
|
||||
bool ShouldReadBlankCalibrationBinary() {
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
return static_cast<bool>(g_blank_prodinfo_storage);
|
||||
}
|
||||
|
||||
bool IsWriteToCalibrationBinaryAllowed() {
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
return g_allow_writes;
|
||||
}
|
||||
|
||||
void ReadFromBlankCalibrationBinary(s64 offset, void *dst, size_t size) {
|
||||
AMS_ABORT_UNLESS(ShouldReadBlankCalibrationBinary());
|
||||
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
R_ABORT_UNLESS(g_blank_prodinfo_storage->Read(offset, dst, size));
|
||||
}
|
||||
|
||||
void WriteToBlankCalibrationBinary(s64 offset, const void *src, size_t size) {
|
||||
AMS_ABORT_UNLESS(ShouldReadBlankCalibrationBinary());
|
||||
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
R_ABORT_UNLESS(g_blank_prodinfo_storage->Write(offset, src, size));
|
||||
}
|
||||
|
||||
void ReadFromFakeSecureBackupStorage(s64 offset, void *dst, size_t size) {
|
||||
AMS_ABORT_UNLESS(IsWriteToCalibrationBinaryAllowed());
|
||||
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
R_ABORT_UNLESS(g_fake_secure_backup_storage->Read(offset, dst, size));
|
||||
}
|
||||
|
||||
void WriteToFakeSecureBackupStorage(s64 offset, const void *src, size_t size) {
|
||||
AMS_ABORT_UNLESS(IsWriteToCalibrationBinaryAllowed());
|
||||
|
||||
std::scoped_lock lk(g_prodinfo_management_lock);
|
||||
R_ABORT_UNLESS(g_fake_secure_backup_storage->Write(offset, src, size));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm {
|
||||
|
||||
constexpr inline size_t CalibrationBinarySize = 0x8000;
|
||||
|
||||
constexpr inline s64 SecureCalibrationInfoBackupOffset = 3_MB;
|
||||
constexpr inline size_t SecureCalibrationBinaryBackupSize = 0xC000;
|
||||
|
||||
void InitializeProdInfoManagement();
|
||||
|
||||
void SaveProdInfoBackupsAndWipeMemory(char *out_name, size_t out_name_size);
|
||||
|
||||
bool ShouldReadBlankCalibrationBinary();
|
||||
bool IsWriteToCalibrationBinaryAllowed();
|
||||
|
||||
void ReadFromBlankCalibrationBinary(s64 offset, void *dst, size_t size);
|
||||
void WriteToBlankCalibrationBinary(s64 offset, const void *src, size_t size);
|
||||
|
||||
void ReadFromFakeSecureBackupStorage(s64 offset, void *dst, size_t size);
|
||||
void WriteToFakeSecureBackupStorage(s64 offset, const void *src, size_t size);
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "bpc_ams_module.hpp"
|
||||
#include "bpc_ams_service.hpp"
|
||||
|
||||
namespace ams::mitm::bpc_ams {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr sm::ServiceName AtmosphereServiceName = sm::ServiceName::Encode("bpc:ams");
|
||||
constexpr size_t AtmosphereMaxSessions = 4;
|
||||
|
||||
constexpr size_t MaxServers = 1;
|
||||
constexpr size_t MaxSessions = AtmosphereMaxSessions;
|
||||
using ServerOptions = sf::hipc::DefaultServerManagerOptions;
|
||||
sf::hipc::ServerManager<MaxServers, ServerOptions, MaxSessions> g_server_manager;
|
||||
|
||||
constinit sf::UnmanagedServiceObject<bpc::impl::IAtmosphereInterface, bpc::AtmosphereService> g_ams_service_object;
|
||||
|
||||
}
|
||||
|
||||
void MitmModule::ThreadFunction(void *) {
|
||||
/* Create bpc:ams. */
|
||||
{
|
||||
os::NativeHandle bpcams_h;
|
||||
R_ABORT_UNLESS(svc::ManageNamedPort(&bpcams_h, AtmosphereServiceName.name, AtmosphereMaxSessions));
|
||||
g_server_manager.RegisterObjectForServer(g_ams_service_object.GetShared(), bpcams_h);
|
||||
}
|
||||
|
||||
/* Loop forever, servicing our services. */
|
||||
g_server_manager.LoopProcess();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_module.hpp"
|
||||
|
||||
namespace ams::mitm::bpc_ams {
|
||||
|
||||
DEFINE_MITM_MODULE_CLASS(0x8000, AMS_GET_SYSTEM_THREAD_PRIORITY(bpc, IpcServer));
|
||||
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "bpc_ams_power_utils.hpp"
|
||||
#include "../amsmitm_fs_utils.hpp"
|
||||
|
||||
namespace ams::mitm::bpc {
|
||||
|
||||
namespace {
|
||||
|
||||
/* Convenience definitions. */
|
||||
constexpr uintptr_t IramBase = 0x40000000ull;
|
||||
constexpr uintptr_t IramPayloadBase = 0x40010000ull;
|
||||
constexpr size_t IramSize = 0x40000;
|
||||
constexpr size_t IramPayloadMaxSize = 0x24000;
|
||||
constexpr size_t IramFatalErrorContextOffset = 0x2E000;
|
||||
|
||||
/* Helper enum. */
|
||||
enum class RebootType : u32 {
|
||||
Standard,
|
||||
ToRcm,
|
||||
ToPayload,
|
||||
ByPmic,
|
||||
};
|
||||
|
||||
/* Globals. */
|
||||
alignas(os::MemoryPageSize) u8 g_work_page[os::MemoryPageSize];
|
||||
alignas(os::MemoryPageSize) u8 g_reboot_payload[IramPayloadMaxSize];
|
||||
RebootType g_reboot_type = RebootType::ToRcm;
|
||||
|
||||
/* Helpers. */
|
||||
void ClearIram() {
|
||||
/* Make page CCs. */
|
||||
std::memset(g_work_page, 0xCC, sizeof(g_work_page));
|
||||
|
||||
/* Overwrite all of IRAM with CCs. */
|
||||
for (size_t ofs = 0; ofs < IramSize; ofs += sizeof(g_work_page)) {
|
||||
exosphere::CopyToIram(IramBase + ofs, g_work_page, sizeof(g_work_page));
|
||||
}
|
||||
}
|
||||
|
||||
void DoRebootToPayload() {
|
||||
/* Ensure clean IRAM state. */
|
||||
ClearIram();
|
||||
|
||||
/* Copy in payload. */
|
||||
for (size_t ofs = 0; ofs < sizeof(g_reboot_payload); ofs += sizeof(g_work_page)) {
|
||||
std::memcpy(g_work_page, g_reboot_payload + ofs, std::min(sizeof(g_reboot_payload) - ofs, sizeof(g_work_page)));
|
||||
exosphere::CopyToIram(IramPayloadBase + ofs, g_work_page, sizeof(g_work_page));
|
||||
}
|
||||
|
||||
exosphere::ForceRebootToIramPayload();
|
||||
}
|
||||
|
||||
void DoRebootToFatalError(const ams::FatalErrorContext *ctx) {
|
||||
/* Ensure clean IRAM state. */
|
||||
ClearIram();
|
||||
|
||||
/* Copy in payload. */
|
||||
for (size_t ofs = 0; ofs < sizeof(g_reboot_payload); ofs += sizeof(g_work_page)) {
|
||||
std::memcpy(g_work_page, g_reboot_payload + ofs, std::min(sizeof(g_reboot_payload) - ofs, sizeof(g_work_page)));
|
||||
exosphere::CopyToIram(IramPayloadBase + ofs, g_work_page, sizeof(g_work_page));
|
||||
}
|
||||
|
||||
/* Copy in fatal error context, if relevant. */
|
||||
if (ctx != nullptr) {
|
||||
std::memset(g_work_page, 0xCC, sizeof(g_work_page));
|
||||
std::memcpy(g_work_page, ctx, sizeof(*ctx));
|
||||
exosphere::CopyToIram(IramPayloadBase + IramFatalErrorContextOffset, g_work_page, sizeof(g_work_page));
|
||||
}
|
||||
|
||||
exosphere::ForceRebootToFatalError();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Power utilities. */
|
||||
bool IsRebootManaged() {
|
||||
return g_reboot_type != RebootType::Standard;
|
||||
}
|
||||
|
||||
void RebootSystem() {
|
||||
switch (g_reboot_type) {
|
||||
case RebootType::ByPmic:
|
||||
exosphere::ForceRebootByPmic();
|
||||
break;
|
||||
case RebootType::ToRcm:
|
||||
exosphere::ForceRebootToRcm();
|
||||
break;
|
||||
case RebootType::ToPayload:
|
||||
default: /* This should never be called with ::Standard */
|
||||
DoRebootToPayload();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ShutdownSystem() {
|
||||
exosphere::ForceShutdown();
|
||||
}
|
||||
|
||||
/* Atmosphere power utilities. */
|
||||
void RebootForFatalError(const ams::FatalErrorContext *ctx) {
|
||||
DoRebootToFatalError(ctx);
|
||||
}
|
||||
|
||||
void SetRebootPayload(const void *payload, size_t payload_size) {
|
||||
/* Mariko does not support reboot-to-payload. */
|
||||
if (spl::GetSocType() == spl::SocType_Mariko) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Clear payload buffer */
|
||||
std::memset(g_reboot_payload, 0xCC, sizeof(g_reboot_payload));
|
||||
|
||||
/* Ensure valid. */
|
||||
AMS_ABORT_UNLESS(payload != nullptr && payload_size <= sizeof(g_reboot_payload));
|
||||
|
||||
/* Copy in payload. */
|
||||
std::memcpy(g_reboot_payload, payload, payload_size);
|
||||
|
||||
/* Note to the secure monitor that we have a payload. */
|
||||
spl::smc::AsyncOperationKey dummy;
|
||||
spl::smc::SetConfig(std::addressof(dummy), spl::ConfigItem::ExospherePayloadAddress, nullptr, 0, g_reboot_payload);
|
||||
|
||||
/* NOTE: Preferred reboot type may be overrwritten when parsed from settings during boot. */
|
||||
g_reboot_type = RebootType::ToPayload;
|
||||
}
|
||||
|
||||
Result LoadRebootPayload() {
|
||||
/* Mariko does not support reboot-to-payload. */
|
||||
R_SUCCEED_IF(spl::GetSocType() == spl::SocType_Mariko)
|
||||
|
||||
/* Clear payload buffer */
|
||||
std::memset(g_reboot_payload, 0xCC, sizeof(g_reboot_payload));
|
||||
|
||||
/* Open payload file. */
|
||||
FsFile payload_file;
|
||||
R_TRY(fs::OpenAtmosphereSdFile(std::addressof(payload_file), "/reboot_payload.bin", ams::fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fsFileClose(std::addressof(payload_file)); };
|
||||
|
||||
/* Read payload file. Discard result. */
|
||||
{
|
||||
size_t actual_size;
|
||||
fsFileRead(std::addressof(payload_file), 0, g_reboot_payload, sizeof(g_reboot_payload), FsReadOption_None, std::addressof(actual_size));
|
||||
}
|
||||
|
||||
/* NOTE: Preferred reboot type will be parsed from settings later on. */
|
||||
g_reboot_type = RebootType::ToPayload;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result DetectPreferredRebootFunctionality() {
|
||||
char reboot_type[0x40] = {};
|
||||
settings::fwdbg::GetSettingsItemValue(reboot_type, sizeof(reboot_type) - 1, "atmosphere", "power_menu_reboot_function");
|
||||
|
||||
if (strcasecmp(reboot_type, "stock") == 0 || strcasecmp(reboot_type, "normal") == 0 || strcasecmp(reboot_type, "standard") == 0) {
|
||||
g_reboot_type = RebootType::Standard;
|
||||
} else if (strcasecmp(reboot_type, "rcm") == 0) {
|
||||
g_reboot_type = RebootType::ToRcm;
|
||||
} else if (strcasecmp(reboot_type, "payload") == 0) {
|
||||
g_reboot_type = RebootType::ToPayload;
|
||||
}
|
||||
|
||||
/* TODO: Should we actually allow control over this on mariko? */
|
||||
if (spl::GetSocType() == spl::SocType_Mariko) {
|
||||
g_reboot_type = RebootType::ByPmic;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::bpc {
|
||||
|
||||
/* Power utilities. */
|
||||
bool IsRebootManaged();
|
||||
void RebootSystem();
|
||||
void ShutdownSystem();
|
||||
|
||||
/* Atmosphere power utilities. */
|
||||
void SetRebootPayload(const void *payload, size_t payload_size);
|
||||
Result LoadRebootPayload();
|
||||
Result DetectPreferredRebootFunctionality();
|
||||
void RebootForFatalError(const ams::FatalErrorContext *ctx);
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "bpc_ams_service.hpp"
|
||||
#include "bpc_ams_power_utils.hpp"
|
||||
|
||||
namespace ams::mitm::bpc {
|
||||
|
||||
namespace {
|
||||
|
||||
bool g_set_initial_payload = false;
|
||||
|
||||
}
|
||||
|
||||
void AtmosphereService::RebootToFatalError(const ams::FatalErrorContext &ctx) {
|
||||
bpc::RebootForFatalError(std::addressof(ctx));
|
||||
}
|
||||
|
||||
void AtmosphereService::SetRebootPayload(const ams::sf::InBuffer &payload) {
|
||||
/* Set the reboot payload. */
|
||||
bpc::SetRebootPayload(payload.GetPointer(), payload.GetSize());
|
||||
|
||||
/* If this is being called for the first time (by boot sysmodule), */
|
||||
/* Then we should kick off the rest of init. */
|
||||
if (!g_set_initial_payload) {
|
||||
g_set_initial_payload = true;
|
||||
|
||||
/* Start the initialization process. */
|
||||
::ams::mitm::StartInitialize();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#define AMS_BPC_MITM_ATMOSPHERE_INTERFACE_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 65000, void, RebootToFatalError, (const ams::FatalErrorContext &ctx), (ctx)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 65001, void, SetRebootPayload, (const ams::sf::InBuffer &payload), (payload))
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::mitm::bpc::impl, IAtmosphereInterface, AMS_BPC_MITM_ATMOSPHERE_INTERFACE_INTERFACE_INFO, 0x00000000)
|
||||
|
||||
namespace ams::mitm::bpc {
|
||||
|
||||
class AtmosphereService {
|
||||
public:
|
||||
void RebootToFatalError(const ams::FatalErrorContext &ctx);
|
||||
void SetRebootPayload(const ams::sf::InBuffer &payload);
|
||||
};
|
||||
static_assert(impl::IsIAtmosphereInterface<AtmosphereService>);
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "bpc_mitm_service.hpp"
|
||||
#include "bpc_ams_power_utils.hpp"
|
||||
|
||||
namespace ams::mitm::bpc {
|
||||
|
||||
Result BpcMitmService::RebootSystem() {
|
||||
R_UNLESS(bpc::IsRebootManaged(), sm::mitm::ResultShouldForwardToSession());
|
||||
bpc::RebootSystem();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result BpcMitmService::ShutdownSystem() {
|
||||
bpc::ShutdownSystem();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#define AMS_BPC_MITM_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, ShutdownSystem, (), ()) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, RebootSystem, (), ())
|
||||
|
||||
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::bpc::impl, IBpcMitmInterface, AMS_BPC_MITM_INTERFACE_INFO, 0xF6C277FD)
|
||||
|
||||
namespace ams::mitm::bpc {
|
||||
|
||||
class BpcMitmService : public sf::MitmServiceImplBase {
|
||||
public:
|
||||
using MitmServiceImplBase::MitmServiceImplBase;
|
||||
public:
|
||||
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
|
||||
/* We will mitm:
|
||||
* - am (omm on 14.0.0+), to intercept the Reboot/Power buttons in the overlay menu.
|
||||
* - fatal, to simplify payload reboot logic significantly
|
||||
* - hbl, to allow homebrew to take advantage of the feature.
|
||||
*/
|
||||
return client_info.program_id == ncm::SystemProgramId::Am ||
|
||||
client_info.program_id == ncm::SystemProgramId::Omm ||
|
||||
client_info.program_id == ncm::SystemProgramId::Fatal ||
|
||||
client_info.override_status.IsHbl();
|
||||
}
|
||||
public:
|
||||
/* Overridden commands. */
|
||||
Result ShutdownSystem();
|
||||
Result RebootSystem();
|
||||
};
|
||||
static_assert(impl::IsIBpcMitmInterface<BpcMitmService>);
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "bpcmitm_module.hpp"
|
||||
#include "bpc_mitm_service.hpp"
|
||||
|
||||
namespace ams::mitm::bpc {
|
||||
|
||||
namespace {
|
||||
|
||||
enum PortIndex {
|
||||
PortIndex_Mitm,
|
||||
PortIndex_Count,
|
||||
};
|
||||
|
||||
constexpr sm::ServiceName MitmServiceName = sm::ServiceName::Encode("bpc");
|
||||
constexpr sm::ServiceName DeprecatedMitmServiceName = sm::ServiceName::Encode("bpc:c");
|
||||
constexpr size_t MitmServiceMaxSessions = 13;
|
||||
|
||||
constexpr size_t MaxSessions = MitmServiceMaxSessions;
|
||||
|
||||
struct ServerOptions {
|
||||
static constexpr size_t PointerBufferSize = sf::hipc::DefaultServerManagerOptions::PointerBufferSize;
|
||||
static constexpr size_t MaxDomains = sf::hipc::DefaultServerManagerOptions::MaxDomains;
|
||||
static constexpr size_t MaxDomainObjects = sf::hipc::DefaultServerManagerOptions::MaxDomainObjects;
|
||||
static constexpr bool CanDeferInvokeRequest = sf::hipc::DefaultServerManagerOptions::CanDeferInvokeRequest;
|
||||
static constexpr bool CanManageMitmServers = true;
|
||||
};
|
||||
|
||||
class ServerManager final : public sf::hipc::ServerManager<PortIndex_Count, ServerOptions, MaxSessions> {
|
||||
private:
|
||||
virtual Result OnNeedsToAccept(int port_index, Server *server) override;
|
||||
};
|
||||
|
||||
ServerManager g_server_manager;
|
||||
|
||||
Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
|
||||
/* Acknowledge the mitm session. */
|
||||
std::shared_ptr<::Service> fsrv;
|
||||
sm::MitmProcessInfo client_info;
|
||||
server->AcknowledgeMitmSession(std::addressof(fsrv), std::addressof(client_info));
|
||||
|
||||
switch (port_index) {
|
||||
case PortIndex_Mitm:
|
||||
R_RETURN(this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<impl::IBpcMitmInterface, BpcMitmService>(decltype(fsrv)(fsrv), client_info), fsrv));
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MitmModule::ThreadFunction(void *) {
|
||||
/* Wait until initialization is complete. */
|
||||
mitm::WaitInitialized();
|
||||
|
||||
/* Create bpc mitm. */
|
||||
const sm::ServiceName service_name = (hos::GetVersion() >= hos::Version_2_0_0) ? MitmServiceName : DeprecatedMitmServiceName;
|
||||
|
||||
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<BpcMitmService>(PortIndex_Mitm, service_name)));
|
||||
|
||||
/* Loop forever, servicing our services. */
|
||||
g_server_manager.LoopProcess();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_module.hpp"
|
||||
|
||||
namespace ams::mitm::bpc {
|
||||
|
||||
DEFINE_MITM_MODULE_CLASS(0x8000, AMS_GET_SYSTEM_THREAD_PRIORITY(bpc, IpcServer));
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_fs_utils.hpp"
|
||||
#include "dnsmitm_debug.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver {
|
||||
|
||||
namespace {
|
||||
|
||||
constinit os::SdkMutex g_log_mutex;
|
||||
constinit bool g_log_enabled;
|
||||
|
||||
constinit ::FsFile g_log_file;
|
||||
constinit s64 g_log_ofs;
|
||||
|
||||
|
||||
constinit char g_log_buf[0x400];
|
||||
|
||||
}
|
||||
|
||||
void InitializeDebug(bool enable_log) {
|
||||
{
|
||||
std::scoped_lock lk(g_log_mutex);
|
||||
|
||||
g_log_enabled = enable_log;
|
||||
|
||||
if (g_log_enabled) {
|
||||
/* Create the logs directory. */
|
||||
mitm::fs::CreateAtmosphereSdDirectory("/logs");
|
||||
|
||||
/* Create the log file. */
|
||||
mitm::fs::CreateAtmosphereSdFile("/logs/dns_mitm_debug.log", 0, ams::fs::CreateOption_None);
|
||||
|
||||
/* Open the log file. */
|
||||
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(g_log_file), "/logs/dns_mitm_debug.log", ams::fs::OpenMode_ReadWrite | ams::fs::OpenMode_AllowAppend));
|
||||
|
||||
/* Get the current log offset. */
|
||||
R_ABORT_UNLESS(::fsFileGetSize(std::addressof(g_log_file), std::addressof(g_log_ofs)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Start a new log. */
|
||||
LogDebug("\n---\n");
|
||||
}
|
||||
|
||||
void LogDebug(const char *fmt, ...) {
|
||||
std::scoped_lock lk(g_log_mutex);
|
||||
|
||||
if (g_log_enabled) {
|
||||
int len = 0;
|
||||
{
|
||||
std::va_list vl;
|
||||
va_start(vl, fmt);
|
||||
len = util::VSNPrintf(g_log_buf, sizeof(g_log_buf), fmt, vl);
|
||||
va_end(vl);
|
||||
}
|
||||
|
||||
R_ABORT_UNLESS(::fsFileWrite(std::addressof(g_log_file), g_log_ofs, g_log_buf, len, FsWriteOption_Flush));
|
||||
g_log_ofs += len;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::socket::resolver {
|
||||
|
||||
void InitializeDebug(bool enable_log);
|
||||
|
||||
void LogDebug(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||
|
||||
}
|
||||
@@ -1,399 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_fs_utils.hpp"
|
||||
#include "dnsmitm_debug.hpp"
|
||||
#include "dnsmitm_host_redirection.hpp"
|
||||
#include "socket_allocator.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver {
|
||||
|
||||
namespace {
|
||||
|
||||
/* https://github.com/clibs/wildcardcmp */
|
||||
constexpr int wildcardcmp(const char *pattern, const char *string) {
|
||||
const char *w = nullptr; /* last `*` */
|
||||
const char *s = nullptr; /* last checked char */
|
||||
|
||||
/* malformed */
|
||||
if (!pattern || !string) return 0;
|
||||
|
||||
/* loop 1 char at a time */
|
||||
while (1) {
|
||||
if (!*string) {
|
||||
if (!*pattern) return 1;
|
||||
if ('*' == *pattern) return 1;
|
||||
if (!s || !*s) return 0;
|
||||
string = s++;
|
||||
pattern = w;
|
||||
continue;
|
||||
} else {
|
||||
if (*pattern != *string) {
|
||||
if ('*' == *pattern) {
|
||||
w = ++pattern;
|
||||
s = string;
|
||||
/* "*" -> "foobar" */
|
||||
if (*pattern) continue;
|
||||
return 1;
|
||||
} else if (w) {
|
||||
string++;
|
||||
/* "*ooba*" -> "foobar" */
|
||||
continue;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
string++;
|
||||
pattern++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
constexpr const char DefaultHostsFile[] =
|
||||
"# Nintendo telemetry servers\n"
|
||||
"127.0.0.1 receive-%.dg.srv.nintendo.net receive-%.er.srv.nintendo.net\n";
|
||||
|
||||
constinit os::SdkMutex g_redirection_lock;
|
||||
std::vector<std::pair<std::string, ams::socket::InAddrT>> g_redirection_list;
|
||||
|
||||
void RemoveRedirection(const char *hostname) {
|
||||
for (auto it = g_redirection_list.begin(); it != g_redirection_list.end(); ++it) {
|
||||
if (std::strcmp(it->first.c_str(), hostname) == 0) {
|
||||
g_redirection_list.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddRedirection(const char *hostname, ams::socket::InAddrT addr) {
|
||||
RemoveRedirection(hostname);
|
||||
g_redirection_list.emplace(g_redirection_list.begin(), std::string(hostname), addr);
|
||||
}
|
||||
|
||||
constinit char g_specific_emummc_hosts_path[0x40] = {};
|
||||
|
||||
void ParseHostsFile(const char *file_data) {
|
||||
/* Get the environment identifier from settings. */
|
||||
const auto env = ams::nsd::impl::device::GetEnvironmentIdentifierFromSettings();
|
||||
const auto env_len = std::strlen(env.value);
|
||||
|
||||
/* Parse the file. */
|
||||
enum class State {
|
||||
IgnoredLine,
|
||||
BeginLine,
|
||||
Ip1,
|
||||
IpDot1,
|
||||
Ip2,
|
||||
IpDot2,
|
||||
Ip3,
|
||||
IpDot3,
|
||||
Ip4,
|
||||
WhiteSpace,
|
||||
HostName,
|
||||
};
|
||||
|
||||
ams::socket::InAddrT current_address{};
|
||||
char current_hostname[0x200];
|
||||
u32 work{};
|
||||
|
||||
State state = State::BeginLine;
|
||||
for (const char *cur = file_data; *cur != '\x00'; ++cur) {
|
||||
const char c = *cur;
|
||||
switch (state) {
|
||||
case State::IgnoredLine:
|
||||
if (c == '\n') {
|
||||
state = State::BeginLine;
|
||||
}
|
||||
break;
|
||||
case State::BeginLine:
|
||||
if (std::isdigit(static_cast<unsigned char>(c))) {
|
||||
current_address = 0;
|
||||
work = static_cast<u32>(c - '0');
|
||||
state = State::Ip1;
|
||||
} else if (c == '\n') {
|
||||
state = State::BeginLine;
|
||||
} else {
|
||||
state = State::IgnoredLine;
|
||||
}
|
||||
break;
|
||||
case State::Ip1:
|
||||
if (std::isdigit(static_cast<unsigned char>(c))) {
|
||||
work *= 10;
|
||||
work += static_cast<u32>(c - '0');
|
||||
} else if (c == '.') {
|
||||
current_address |= (work & 0xFF) << 0;
|
||||
work = 0;
|
||||
state = State::IpDot1;
|
||||
} else {
|
||||
state = State::IgnoredLine;
|
||||
}
|
||||
break;
|
||||
case State::IpDot1:
|
||||
if (std::isdigit(static_cast<unsigned char>(c))) {
|
||||
work = static_cast<u32>(c - '0');
|
||||
state = State::Ip2;
|
||||
} else {
|
||||
state = State::IgnoredLine;
|
||||
}
|
||||
break;
|
||||
case State::Ip2:
|
||||
if (std::isdigit(static_cast<unsigned char>(c))) {
|
||||
work *= 10;
|
||||
work += static_cast<u32>(c - '0');
|
||||
} else if (c == '.') {
|
||||
current_address |= (work & 0xFF) << 8;
|
||||
work = 0;
|
||||
state = State::IpDot2;
|
||||
} else {
|
||||
state = State::IgnoredLine;
|
||||
}
|
||||
break;
|
||||
case State::IpDot2:
|
||||
if (std::isdigit(static_cast<unsigned char>(c))) {
|
||||
work = static_cast<u32>(c - '0');
|
||||
state = State::Ip3;
|
||||
} else {
|
||||
state = State::IgnoredLine;
|
||||
}
|
||||
break;
|
||||
case State::Ip3:
|
||||
if (std::isdigit(static_cast<unsigned char>(c))) {
|
||||
work *= 10;
|
||||
work += static_cast<u32>(c - '0');
|
||||
} else if (c == '.') {
|
||||
current_address |= (work & 0xFF) << 16;
|
||||
work = 0;
|
||||
state = State::IpDot3;
|
||||
} else {
|
||||
state = State::IgnoredLine;
|
||||
}
|
||||
break;
|
||||
case State::IpDot3:
|
||||
if (std::isdigit(static_cast<unsigned char>(c))) {
|
||||
work = static_cast<u32>(c - '0');
|
||||
state = State::Ip4;
|
||||
} else {
|
||||
state = State::IgnoredLine;
|
||||
}
|
||||
break;
|
||||
case State::Ip4:
|
||||
if (std::isdigit(static_cast<unsigned char>(c))) {
|
||||
work *= 10;
|
||||
work += static_cast<u32>(c - '0');
|
||||
} else if (c == ' ' || c == '\t') {
|
||||
current_address |= (work & 0xFF) << 24;
|
||||
work = 0;
|
||||
state = State::WhiteSpace;
|
||||
} else {
|
||||
state = State::IgnoredLine;
|
||||
}
|
||||
break;
|
||||
case State::WhiteSpace:
|
||||
if (c == '\n') {
|
||||
state = State::BeginLine;
|
||||
} else if (c != ' ' && c != '\r' && c != '\t') {
|
||||
if (c == '%') {
|
||||
std::memcpy(current_hostname, env.value, env_len);
|
||||
work = env_len;
|
||||
} else {
|
||||
current_hostname[0] = c;
|
||||
work = 1;
|
||||
}
|
||||
state = State::HostName;
|
||||
}
|
||||
break;
|
||||
case State::HostName:
|
||||
if (c == ' ' || c == '\r' || c == '\n' || c == '\t') {
|
||||
AMS_ABORT_UNLESS(work < sizeof(current_hostname));
|
||||
current_hostname[work] = '\x00';
|
||||
|
||||
AddRedirection(current_hostname, current_address);
|
||||
work = 0;
|
||||
|
||||
if (c == '\n') {
|
||||
state = State::BeginLine;
|
||||
} else {
|
||||
state = State::WhiteSpace;
|
||||
}
|
||||
} else if (c == '%') {
|
||||
AMS_ABORT_UNLESS(work < sizeof(current_hostname) - env_len);
|
||||
std::memcpy(current_hostname + work, env.value, env_len);
|
||||
work += env_len;
|
||||
} else {
|
||||
AMS_ABORT_UNLESS(work < sizeof(current_hostname) - 1);
|
||||
current_hostname[work++] = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state == State::HostName) {
|
||||
AMS_ABORT_UNLESS(work < sizeof(current_hostname));
|
||||
current_hostname[work] = '\x00';
|
||||
|
||||
AddRedirection(current_hostname, current_address);
|
||||
}
|
||||
}
|
||||
|
||||
void Log(::FsFile &f, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
|
||||
void Log(::FsFile &f, const char *fmt, ...) {
|
||||
char log_buf[0x100];
|
||||
int len = 0;
|
||||
{
|
||||
std::va_list vl;
|
||||
va_start(vl, fmt);
|
||||
len = util::VSNPrintf(log_buf, sizeof(log_buf), fmt, vl);
|
||||
va_end(vl);
|
||||
}
|
||||
|
||||
s64 ofs;
|
||||
R_ABORT_UNLESS(::fsFileGetSize(std::addressof(f), std::addressof(ofs)));
|
||||
R_ABORT_UNLESS(::fsFileWrite(std::addressof(f), ofs, log_buf, len, FsWriteOption_Flush));
|
||||
}
|
||||
|
||||
const char *SelectHostsFile(::FsFile &log_file) {
|
||||
Log(log_file, "Selecting hosts file...\n");
|
||||
const bool is_emummc = emummc::IsActive();
|
||||
const u32 emummc_id = emummc::GetActiveId();
|
||||
util::SNPrintf(g_specific_emummc_hosts_path, sizeof(g_specific_emummc_hosts_path), "/hosts/emummc_%04x.txt", emummc_id);
|
||||
|
||||
if (is_emummc) {
|
||||
if (mitm::fs::HasAtmosphereSdFile(g_specific_emummc_hosts_path)) {
|
||||
return g_specific_emummc_hosts_path;
|
||||
}
|
||||
Log(log_file, "Skipping %s because it does not exist...\n", g_specific_emummc_hosts_path);
|
||||
|
||||
if (mitm::fs::HasAtmosphereSdFile("/hosts/emummc.txt")) {
|
||||
return "/hosts/emummc.txt";
|
||||
}
|
||||
Log(log_file, "Skipping %s because it does not exist...\n", "/hosts/emummc.txt");
|
||||
} else {
|
||||
if (mitm::fs::HasAtmosphereSdFile("/hosts/sysmmc.txt")) {
|
||||
return "/hosts/sysmmc.txt";
|
||||
}
|
||||
Log(log_file, "Skipping %s because it does not exist...\n", "/hosts/sysmmc.txt");
|
||||
}
|
||||
|
||||
return "/hosts/default.txt";
|
||||
}
|
||||
|
||||
bool ShouldAddDefaultResolverRedirections() {
|
||||
u8 en = 0;
|
||||
if (settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "add_defaults_to_dns_hosts") == sizeof(en)) {
|
||||
return (en != 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InitializeResolverRedirections() {
|
||||
/* Get whether we should add defaults. */
|
||||
const bool add_defaults = ShouldAddDefaultResolverRedirections();
|
||||
|
||||
/* Acquire exclusive access to the map. */
|
||||
std::scoped_lock lk(g_redirection_lock);
|
||||
|
||||
/* Clear the redirections map. */
|
||||
g_redirection_list.clear();
|
||||
|
||||
/* Open log file. */
|
||||
::FsFile log_file;
|
||||
mitm::fs::DeleteAtmosphereSdFile("/logs/dns_mitm_startup.log");
|
||||
mitm::fs::CreateAtmosphereSdDirectory("/logs");
|
||||
R_ABORT_UNLESS(mitm::fs::CreateAtmosphereSdFile("/logs/dns_mitm_startup.log", 0, ams::fs::CreateOption_None));
|
||||
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(log_file), "/logs/dns_mitm_startup.log", ams::fs::OpenMode_ReadWrite | ams::fs::OpenMode_AllowAppend));
|
||||
ON_SCOPE_EXIT { ::fsFileClose(std::addressof(log_file)); };
|
||||
|
||||
Log(log_file, "DNS Mitm:\n");
|
||||
|
||||
/* If a default hosts file doesn't exist on the sd card, create one. */
|
||||
if (!mitm::fs::HasAtmosphereSdFile("/hosts/default.txt")) {
|
||||
Log(log_file, "Creating /hosts/default.txt because it does not exist.\n");
|
||||
|
||||
mitm::fs::CreateAtmosphereSdDirectory("/hosts");
|
||||
R_ABORT_UNLESS(mitm::fs::CreateAtmosphereSdFile("/hosts/default.txt", sizeof(DefaultHostsFile) - 1, ams::fs::CreateOption_None));
|
||||
|
||||
::FsFile default_file;
|
||||
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(default_file), "/hosts/default.txt", ams::fs::OpenMode_ReadWrite));
|
||||
R_ABORT_UNLESS(::fsFileWrite(std::addressof(default_file), 0, DefaultHostsFile, sizeof(DefaultHostsFile) - 1, ::FsWriteOption_Flush));
|
||||
::fsFileClose(std::addressof(default_file));
|
||||
}
|
||||
|
||||
/* If we should, add the defaults. */
|
||||
if (add_defaults) {
|
||||
Log(log_file, "Adding defaults to redirection list.\n");
|
||||
ParseHostsFile(DefaultHostsFile);
|
||||
}
|
||||
|
||||
/* Select the hosts file. */
|
||||
const char *hosts_path = SelectHostsFile(log_file);
|
||||
Log(log_file, "Selected %s\n", hosts_path);
|
||||
|
||||
/* Load the hosts file. */
|
||||
{
|
||||
char *hosts_file_data = nullptr;
|
||||
ON_SCOPE_EXIT { if (hosts_file_data != nullptr) { ams::Free(hosts_file_data); } };
|
||||
{
|
||||
::FsFile hosts_file;
|
||||
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdFile(std::addressof(hosts_file), hosts_path, ams::fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { ::fsFileClose(std::addressof(hosts_file)); };
|
||||
|
||||
/* Get the hosts file size. */
|
||||
s64 hosts_size;
|
||||
R_ABORT_UNLESS(::fsFileGetSize(std::addressof(hosts_file), std::addressof(hosts_size)));
|
||||
|
||||
/* Validate we can read the file. */
|
||||
AMS_ABORT_UNLESS(0 <= hosts_size && hosts_size < 0x8000);
|
||||
|
||||
/* Read the data. */
|
||||
hosts_file_data = static_cast<char *>(ams::Malloc(0x8000));
|
||||
AMS_ABORT_UNLESS(hosts_file_data != nullptr);
|
||||
|
||||
u64 br;
|
||||
R_ABORT_UNLESS(::fsFileRead(std::addressof(hosts_file), 0, hosts_file_data, hosts_size, ::FsReadOption_None, std::addressof(br)));
|
||||
AMS_ABORT_UNLESS(br == static_cast<u64>(hosts_size));
|
||||
|
||||
/* Null-terminate. */
|
||||
hosts_file_data[hosts_size] = '\x00';
|
||||
}
|
||||
|
||||
/* Parse the hosts file. */
|
||||
ParseHostsFile(hosts_file_data);
|
||||
}
|
||||
|
||||
/* Print the redirections. */
|
||||
Log(log_file, "Redirections:\n");
|
||||
for (const auto &[host, address] : g_redirection_list) {
|
||||
Log(log_file, " `%s` -> %u.%u.%u.%u\n", host.c_str(), (address >> 0) & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF, (address >> 24) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
bool GetRedirectedHostByName(ams::socket::InAddrT *out, const char *hostname) {
|
||||
std::scoped_lock lk(g_redirection_lock);
|
||||
|
||||
for (const auto &[host, address] : g_redirection_list) {
|
||||
if (wildcardcmp(host.c_str(), hostname)) {
|
||||
*out = address;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::socket::resolver {
|
||||
|
||||
void InitializeResolverRedirections();
|
||||
|
||||
bool GetRedirectedHostByName(ams::socket::InAddrT *out, const char *hostname);
|
||||
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "dnsmitm_module.hpp"
|
||||
#include "dnsmitm_debug.hpp"
|
||||
#include "dnsmitm_resolver_impl.hpp"
|
||||
#include "dnsmitm_host_redirection.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver {
|
||||
|
||||
namespace {
|
||||
|
||||
enum PortIndex {
|
||||
PortIndex_Mitm,
|
||||
PortIndex_Count,
|
||||
};
|
||||
|
||||
constexpr sm::ServiceName DnsMitmServiceName = sm::ServiceName::Encode("sfdnsres");
|
||||
|
||||
constexpr size_t MaxSessions = 30;
|
||||
|
||||
struct ServerOptions {
|
||||
static constexpr size_t PointerBufferSize = sf::hipc::DefaultServerManagerOptions::PointerBufferSize;
|
||||
static constexpr size_t MaxDomains = sf::hipc::DefaultServerManagerOptions::MaxDomains;
|
||||
static constexpr size_t MaxDomainObjects = sf::hipc::DefaultServerManagerOptions::MaxDomainObjects;
|
||||
static constexpr bool CanDeferInvokeRequest = sf::hipc::DefaultServerManagerOptions::CanDeferInvokeRequest;
|
||||
static constexpr bool CanManageMitmServers = true;
|
||||
};
|
||||
|
||||
class ServerManager final : public sf::hipc::ServerManager<PortIndex_Count, ServerOptions, MaxSessions> {
|
||||
private:
|
||||
virtual Result OnNeedsToAccept(int port_index, Server *server) override;
|
||||
};
|
||||
|
||||
alignas(os::MemoryPageSize) constinit u8 g_resolver_allocator_buffer[16_KB];
|
||||
|
||||
ServerManager g_server_manager;
|
||||
|
||||
Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
|
||||
/* Acknowledge the mitm session. */
|
||||
std::shared_ptr<::Service> fsrv;
|
||||
sm::MitmProcessInfo client_info;
|
||||
server->AcknowledgeMitmSession(std::addressof(fsrv), std::addressof(client_info));
|
||||
|
||||
switch (port_index) {
|
||||
case PortIndex_Mitm:
|
||||
R_RETURN(this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<IResolver, ResolverImpl>(decltype(fsrv)(fsrv), client_info), fsrv));
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr size_t TotalThreads = 8;
|
||||
static_assert(TotalThreads >= 1, "TotalThreads");
|
||||
constexpr size_t NumExtraThreads = TotalThreads - 1;
|
||||
constexpr size_t ThreadStackSize = mitm::ModuleTraits<socket::resolver::MitmModule>::StackSize;
|
||||
alignas(os::MemoryPageSize) u8 g_extra_thread_stacks[NumExtraThreads][ThreadStackSize];
|
||||
|
||||
os::ThreadType g_extra_threads[NumExtraThreads];
|
||||
|
||||
void LoopServerThread(void *) {
|
||||
/* Loop forever, servicing our services. */
|
||||
g_server_manager.LoopProcess();
|
||||
}
|
||||
|
||||
void ProcessForServerOnAllThreads() {
|
||||
/* Initialize threads. */
|
||||
if constexpr (NumExtraThreads > 0) {
|
||||
const s32 priority = os::GetThreadCurrentPriority(os::GetCurrentThread());
|
||||
for (size_t i = 0; i < NumExtraThreads; i++) {
|
||||
R_ABORT_UNLESS(os::CreateThread(g_extra_threads + i, LoopServerThread, nullptr, g_extra_thread_stacks[i], ThreadStackSize, priority));
|
||||
}
|
||||
}
|
||||
|
||||
/* Start extra threads. */
|
||||
if constexpr (NumExtraThreads > 0) {
|
||||
for (size_t i = 0; i < NumExtraThreads; i++) {
|
||||
os::StartThread(g_extra_threads + i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop this thread. */
|
||||
LoopServerThread(nullptr);
|
||||
|
||||
/* Wait for extra threads to finish. */
|
||||
if constexpr (NumExtraThreads > 0) {
|
||||
for (size_t i = 0; i < NumExtraThreads; i++) {
|
||||
os::WaitThread(g_extra_threads + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ShouldMitmDns() {
|
||||
u8 en = 0;
|
||||
if (settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "enable_dns_mitm") == sizeof(en)) {
|
||||
return (en != 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShouldEnableDebugLog() {
|
||||
u8 en = 0;
|
||||
if (settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "enable_dns_mitm_debug_log") == sizeof(en)) {
|
||||
return (en != 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MitmModule::ThreadFunction(void *) {
|
||||
/* Wait until initialization is complete. */
|
||||
mitm::WaitInitialized();
|
||||
|
||||
/* If we shouldn't mitm dns, don't do anything at all. */
|
||||
if (!ShouldMitmDns()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize the socket allocator. */
|
||||
ams::socket::InitializeAllocatorForInternal(g_resolver_allocator_buffer, sizeof(g_resolver_allocator_buffer));
|
||||
|
||||
/* Initialize debug. */
|
||||
resolver::InitializeDebug(ShouldEnableDebugLog());
|
||||
|
||||
/* Initialize redirection map. */
|
||||
resolver::InitializeResolverRedirections();
|
||||
|
||||
/* Create mitm servers. */
|
||||
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<ResolverImpl>(PortIndex_Mitm, DnsMitmServiceName)));
|
||||
|
||||
/* Loop forever, servicing our services. */
|
||||
ProcessForServerOnAllThreads();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_module.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver {
|
||||
|
||||
DEFINE_MITM_MODULE_CLASS(0x2000, AMS_GET_SYSTEM_THREAD_PRIORITY(socket, ResolverIpcServer) - 1);
|
||||
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "dnsmitm_resolver_impl.hpp"
|
||||
#include "dnsmitm_debug.hpp"
|
||||
#include "dnsmitm_host_redirection.hpp"
|
||||
#include "serializer/serializer.hpp"
|
||||
#include "sfdnsres_shim.h"
|
||||
|
||||
namespace ams::mitm::socket::resolver {
|
||||
|
||||
ssize_t SerializeRedirectedHostEnt(u8 * const dst, size_t dst_size, const char *hostname, ams::socket::InAddrT redirect_addr) {
|
||||
struct in_addr addr = { .s_addr = redirect_addr };
|
||||
struct in_addr *addr_list[2] = { std::addressof(addr), nullptr };
|
||||
|
||||
struct hostent ent = {
|
||||
.h_name = const_cast<char *>(hostname),
|
||||
.h_aliases = nullptr,
|
||||
.h_addrtype = AF_INET,
|
||||
.h_length = sizeof(u32),
|
||||
.h_addr_list = (char **)addr_list,
|
||||
};
|
||||
|
||||
const auto result = serializer::DNSSerializer::ToBuffer(dst, dst_size, ent);
|
||||
AMS_ABORT_UNLESS(result >= 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
ssize_t SerializeRedirectedAddrInfo(u8 * const dst, size_t dst_size, const char *hostname, ams::socket::InAddrT redirect_addr, u16 redirect_port, const struct addrinfo *hint) {
|
||||
AMS_UNUSED(hostname);
|
||||
|
||||
struct addrinfo ai = {
|
||||
.ai_flags = 0,
|
||||
.ai_family = AF_UNSPEC,
|
||||
.ai_socktype = 0,
|
||||
.ai_protocol = 0,
|
||||
.ai_addrlen = 0,
|
||||
.ai_canonname = nullptr,
|
||||
.ai_next = nullptr,
|
||||
};
|
||||
|
||||
if (hint != nullptr) {
|
||||
ai = *hint;
|
||||
}
|
||||
|
||||
switch (ai.ai_family) {
|
||||
case AF_UNSPEC: ai.ai_family = AF_INET; break;
|
||||
case AF_INET: ai.ai_family = AF_INET; break;
|
||||
case AF_INET6: AMS_ABORT("Redirected INET6 not supported"); break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
if (ai.ai_socktype == 0) {
|
||||
ai.ai_socktype = SOCK_STREAM;
|
||||
}
|
||||
|
||||
if (ai.ai_protocol == 0) {
|
||||
ai.ai_protocol = IPPROTO_TCP;
|
||||
}
|
||||
|
||||
const struct sockaddr_in sin = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_port = ams::socket::InetHtons(redirect_port),
|
||||
.sin_addr = { .s_addr = redirect_addr },
|
||||
.sin_zero = {},
|
||||
};
|
||||
|
||||
ai.ai_addrlen = sizeof(sin);
|
||||
ai.ai_addr = (struct sockaddr *)(std::addressof(sin));
|
||||
|
||||
const auto result = serializer::DNSSerializer::ToBuffer(dst, dst_size, ai);
|
||||
AMS_ABORT_UNLESS(result >= 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
Result ResolverImpl::GetHostByNameRequest(u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &name, sf::Out<u32> out_host_error, sf::Out<u32> out_errno, const sf::OutBuffer &out_hostent, sf::Out<u32> out_size) {
|
||||
AMS_UNUSED(cancel_handle, client_pid, use_nsd_resolve);
|
||||
|
||||
const char *hostname = reinterpret_cast<const char *>(name.GetPointer());
|
||||
|
||||
LogDebug("[%016lx]: GetHostByNameRequest(%s)\n", m_client_info.program_id.value, hostname);
|
||||
|
||||
R_UNLESS(hostname != nullptr, sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
ams::socket::InAddrT redirect_addr = {};
|
||||
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
LogDebug("[%016lx]: Redirecting %s to %u.%u.%u.%u\n", m_client_info.program_id.value, hostname, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF);
|
||||
const auto size = SerializeRedirectedHostEnt(out_hostent.GetPointer(), out_hostent.GetSize(), hostname, redirect_addr);
|
||||
|
||||
*out_host_error = 0;
|
||||
*out_errno = 0;
|
||||
*out_size = size;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ResolverImpl::GetAddrInfoRequest(u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutBuffer &out_addrinfo, sf::Out<u32> out_errno, sf::Out<s32> out_retval, sf::Out<u32> out_size) {
|
||||
AMS_UNUSED(cancel_handle, client_pid, use_nsd_resolve);
|
||||
|
||||
const char *hostname = reinterpret_cast<const char *>(node.GetPointer());
|
||||
|
||||
LogDebug("[%016lx]: GetAddrInfoRequest(%s, %s)\n", m_client_info.program_id.value, reinterpret_cast<const char *>(node.GetPointer()), reinterpret_cast<const char *>(srv.GetPointer()));
|
||||
|
||||
R_UNLESS(hostname != nullptr, sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
ams::socket::InAddrT redirect_addr = {};
|
||||
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
u16 port = 0;
|
||||
if (srv.GetPointer() != nullptr) {
|
||||
for (const char *cur = reinterpret_cast<const char *>(srv.GetPointer()); *cur != 0; ++cur) {
|
||||
AMS_ABORT_UNLESS(std::isdigit(static_cast<unsigned char>(*cur)));
|
||||
port *= 10;
|
||||
port += *cur - '0';
|
||||
}
|
||||
}
|
||||
|
||||
LogDebug("[%016lx]: Redirecting %s:%u to %u.%u.%u.%u\n", m_client_info.program_id.value, hostname, port, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF);
|
||||
|
||||
const bool use_hint = serialized_hint.GetPointer() != nullptr;
|
||||
struct addrinfo hint = {};
|
||||
if (use_hint) {
|
||||
AMS_ABORT_UNLESS(serializer::DNSSerializer::FromBuffer(hint, serialized_hint.GetPointer(), serialized_hint.GetSize()) >= 0);
|
||||
}
|
||||
ON_SCOPE_EXIT { if (use_hint) { serializer::FreeAddrInfo(hint); } };
|
||||
|
||||
const auto size = SerializeRedirectedAddrInfo(out_addrinfo.GetPointer(), out_addrinfo.GetSize(), hostname, redirect_addr, port, use_hint ? std::addressof(hint) : nullptr);
|
||||
|
||||
*out_retval = 0;
|
||||
*out_errno = 0;
|
||||
*out_size = size;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ResolverImpl::GetHostByNameRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InAutoSelectBuffer &name, const sf::OutAutoSelectBuffer &out_hostent, sf::Out<u32> out_size, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno) {
|
||||
AMS_UNUSED(client_pid, options_version, options, num_options);
|
||||
|
||||
const char *hostname = reinterpret_cast<const char *>(name.GetPointer());
|
||||
|
||||
LogDebug("[%016lx]: GetHostByNameRequestWithOptions(%s)\n", m_client_info.program_id.value, hostname);
|
||||
|
||||
R_UNLESS(hostname != nullptr, sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
ams::socket::InAddrT redirect_addr = {};
|
||||
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
LogDebug("[%016lx]: Redirecting %s to %u.%u.%u.%u\n", m_client_info.program_id.value, hostname, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF);
|
||||
const auto size = SerializeRedirectedHostEnt(out_hostent.GetPointer(), out_hostent.GetSize(), hostname, redirect_addr);
|
||||
|
||||
*out_host_error = 0;
|
||||
*out_errno = 0;
|
||||
*out_size = size;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ResolverImpl::GetAddrInfoRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutAutoSelectBuffer &out_addrinfo, sf::Out<u32> out_size, sf::Out<s32> out_retval, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno) {
|
||||
AMS_UNUSED(client_pid, options_version, options, num_options);
|
||||
|
||||
const char *hostname = reinterpret_cast<const char *>(node.GetPointer());
|
||||
|
||||
LogDebug("[%016lx]: GetAddrInfoRequestWithOptions(%s, %s)\n", m_client_info.program_id.value, hostname, reinterpret_cast<const char *>(srv.GetPointer()));
|
||||
|
||||
R_UNLESS(hostname != nullptr, sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
ams::socket::InAddrT redirect_addr = {};
|
||||
R_UNLESS(GetRedirectedHostByName(std::addressof(redirect_addr), hostname), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
u16 port = 0;
|
||||
if (srv.GetPointer() != nullptr) {
|
||||
for (const char *cur = reinterpret_cast<const char *>(srv.GetPointer()); *cur != 0; ++cur) {
|
||||
AMS_ABORT_UNLESS(std::isdigit(static_cast<unsigned char>(*cur)));
|
||||
port *= 10;
|
||||
port += *cur - '0';
|
||||
}
|
||||
}
|
||||
|
||||
LogDebug("[%016lx]: Redirecting %s:%u to %u.%u.%u.%u\n", m_client_info.program_id.value, hostname, port, (redirect_addr >> 0) & 0xFF, (redirect_addr >> 8) & 0xFF, (redirect_addr >> 16) & 0xFF, (redirect_addr >> 24) & 0xFF);
|
||||
|
||||
const bool use_hint = serialized_hint.GetPointer() != nullptr;
|
||||
struct addrinfo hint = {};
|
||||
if (use_hint) {
|
||||
AMS_ABORT_UNLESS(serializer::DNSSerializer::FromBuffer(hint, serialized_hint.GetPointer(), serialized_hint.GetSize()) >= 0);
|
||||
}
|
||||
ON_SCOPE_EXIT { if (use_hint) { serializer::FreeAddrInfo(hint); } };
|
||||
|
||||
const auto size = SerializeRedirectedAddrInfo(out_addrinfo.GetPointer(), out_addrinfo.GetSize(), hostname, redirect_addr, port, use_hint ? std::addressof(hint) : nullptr);
|
||||
|
||||
*out_retval = 0;
|
||||
*out_host_error = 0;
|
||||
*out_errno = 0;
|
||||
*out_size = size;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ResolverImpl::AtmosphereReloadHostsFile() {
|
||||
/* Perform a hosts file reload. */
|
||||
InitializeResolverRedirections();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#define AMS_DNS_MITM_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, GetHostByNameRequest, (u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &name, sf::Out<u32> out_host_error, sf::Out<u32> out_errno, const sf::OutBuffer &out_hostent, sf::Out<u32> out_size), (cancel_handle, client_pid, use_nsd_resolve, name, out_host_error, out_errno, out_hostent, out_size)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 6, Result, GetAddrInfoRequest, (u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutBuffer &out_addrinfo, sf::Out<u32> out_errno, sf::Out<s32> out_retval, sf::Out<u32> out_size), (cancel_handle, client_pid, use_nsd_resolve, node, srv, serialized_hint, out_addrinfo, out_errno, out_retval, out_size)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 10, Result, GetHostByNameRequestWithOptions, (const sf::ClientProcessId &client_pid, const sf::InAutoSelectBuffer &name, const sf::OutAutoSelectBuffer &out_hostent, sf::Out<u32> out_size, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno), (client_pid, name, out_hostent, out_size, options_version, options, num_options, out_host_error, out_errno), hos::Version_5_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 12, Result, GetAddrInfoRequestWithOptions, (const sf::ClientProcessId &client_pid, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutAutoSelectBuffer &out_addrinfo, sf::Out<u32> out_size, sf::Out<s32> out_retval, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno), (client_pid, node, srv, serialized_hint, out_addrinfo, out_size, out_retval, options_version, options, num_options, out_host_error, out_errno), hos::Version_5_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 65000, Result, AtmosphereReloadHostsFile, (), ())
|
||||
|
||||
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::socket::resolver, IResolver, AMS_DNS_MITM_INTERFACE_INFO, 0x5935F91A)
|
||||
|
||||
namespace ams::mitm::socket::resolver {
|
||||
|
||||
class ResolverImpl : public sf::MitmServiceImplBase {
|
||||
public:
|
||||
using MitmServiceImplBase::MitmServiceImplBase;
|
||||
public:
|
||||
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
|
||||
/* We will mitm:
|
||||
* - everything.
|
||||
*/
|
||||
AMS_UNUSED(client_info);
|
||||
return true;
|
||||
}
|
||||
public:
|
||||
/* Overridden commands. */
|
||||
Result GetHostByNameRequest(u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &name, sf::Out<u32> out_host_error, sf::Out<u32> out_errno, const sf::OutBuffer &out_hostent, sf::Out<u32> out_size);
|
||||
Result GetAddrInfoRequest(u32 cancel_handle, const sf::ClientProcessId &client_pid, bool use_nsd_resolve, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutBuffer &out_addrinfo, sf::Out<u32> out_errno, sf::Out<s32> out_retval, sf::Out<u32> out_size);
|
||||
Result GetHostByNameRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InAutoSelectBuffer &name, const sf::OutAutoSelectBuffer &out_hostent, sf::Out<u32> out_size, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno);
|
||||
Result GetAddrInfoRequestWithOptions(const sf::ClientProcessId &client_pid, const sf::InBuffer &node, const sf::InBuffer &srv, const sf::InBuffer &serialized_hint, const sf::OutAutoSelectBuffer &out_addrinfo, sf::Out<u32> out_size, sf::Out<s32> out_retval, u32 options_version, const sf::InAutoSelectBuffer &options, u32 num_options, sf::Out<s32> out_host_error, sf::Out<s32> out_errno);
|
||||
|
||||
/* Extension commands. */
|
||||
Result AtmosphereReloadHostsFile();
|
||||
};
|
||||
static_assert(IsIResolver<ResolverImpl>);
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../dnsmitm_debug.hpp"
|
||||
#include "serializer.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver::serializer {
|
||||
|
||||
ssize_t DNSSerializer::CheckToBufferArguments(const u8 *dst, size_t dst_size, size_t required, int error_id) {
|
||||
/* TODO: Logging, using error_id */
|
||||
AMS_UNUSED(error_id);
|
||||
|
||||
if (dst == nullptr) {
|
||||
return -1;
|
||||
} else if (dst_size < required) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 DNSSerializer::InternalHton(const u32 &v) {
|
||||
return ams::socket::InetHtonl(v);
|
||||
}
|
||||
|
||||
u16 DNSSerializer::InternalHton(const u16 &v) {
|
||||
return ams::socket::InetHtons(v);
|
||||
}
|
||||
|
||||
u32 DNSSerializer::InternalNtoh(const u32 &v) {
|
||||
return ams::socket::InetNtohl(v);
|
||||
}
|
||||
|
||||
u16 DNSSerializer::InternalNtoh(const u16 &v) {
|
||||
return ams::socket::InetNtohs(v);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::socket::resolver::serializer {
|
||||
|
||||
class DNSSerializer {
|
||||
public:
|
||||
static ssize_t CheckToBufferArguments(const u8 *dst, size_t dst_size, size_t required, int error_id);
|
||||
|
||||
static u32 InternalHton(const u32 &v);
|
||||
static u16 InternalHton(const u16 &v);
|
||||
static u32 InternalNtoh(const u32 &v);
|
||||
static u16 InternalNtoh(const u16 &v);
|
||||
public:
|
||||
template<typename T>
|
||||
static size_t SizeOf(const T &in);
|
||||
|
||||
template<typename T>
|
||||
static size_t SizeOf(const T *in);
|
||||
|
||||
template<typename T>
|
||||
static size_t SizeOf(const T *in, size_t count);
|
||||
|
||||
template<typename T>
|
||||
static size_t SizeOf(const T **arr, u32 &out_count);
|
||||
|
||||
template<typename T>
|
||||
static ssize_t ToBuffer(u8 * const dst, size_t dst_size, const T &in);
|
||||
|
||||
template<typename T>
|
||||
static ssize_t FromBuffer(T &out, const u8 *src, size_t src_size);
|
||||
|
||||
template<typename T>
|
||||
static ssize_t ToBuffer(u8 * const dst, size_t dst_Size, T *in);
|
||||
|
||||
template<typename T>
|
||||
static ssize_t ToBuffer(u8 * const dst, size_t dst_size, T **arr);
|
||||
|
||||
template<typename T>
|
||||
static ssize_t FromBuffer(T *&out, const u8 *src, size_t src_size);
|
||||
|
||||
template<typename T>
|
||||
static ssize_t FromBuffer(T **&out_arr, const u8 *src, size_t src_size);
|
||||
|
||||
template<typename T>
|
||||
static ssize_t ToBuffer(u8 * const dst, size_t dst_size, const T * const arr, size_t count);
|
||||
|
||||
template<typename T>
|
||||
static ssize_t FromBuffer(T * const arr, size_t arr_size, const u8 *src, size_t src_size, size_t count);
|
||||
};
|
||||
|
||||
void FreeHostent(ams::socket::HostEnt &ent);
|
||||
void FreeHostent(struct hostent &ent);
|
||||
|
||||
void FreeAddrInfo(ams::socket::AddrInfo &addr_info);
|
||||
void FreeAddrInfo(struct addrinfo &addr_info);
|
||||
|
||||
}
|
||||
@@ -1,444 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../dnsmitm_debug.hpp"
|
||||
#include "../socket_allocator.hpp"
|
||||
#include "serializer.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver::serializer {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline u32 AddrInfoMagic = 0xBEEFCAFE;
|
||||
|
||||
template<typename T>
|
||||
concept IsAddrInfo = std::same_as<T, ams::socket::AddrInfo> || std::same_as<T, struct addrinfo>;
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
using SockAddrType = typename std::conditional<std::same_as<T, ams::socket::AddrInfo>, ams::socket::SockAddr, struct sockaddr>::type;
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
using SockAddrInType = typename std::conditional<std::same_as<T, ams::socket::AddrInfo>, ams::socket::SockAddrIn, struct sockaddr_in>::type;
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
using SockAddrIn6Type = typename std::conditional<std::same_as<T, ams::socket::AddrInfo>, struct sockaddr_in6, struct sockaddr_in6>::type;
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
constexpr bool IsAfInet(const auto ai_family) {
|
||||
if constexpr (std::same_as<T, ams::socket::AddrInfo>) {
|
||||
return ai_family == ams::socket::Family::Af_Inet;
|
||||
} else {
|
||||
return ai_family == AF_INET;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
constexpr bool IsAfInet6(const auto ai_family) {
|
||||
if constexpr (std::same_as<T, ams::socket::AddrInfo>) {
|
||||
return ai_family == ams::socket::Family::Af_Inet6;
|
||||
} else {
|
||||
return ai_family == AF_INET;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
void FreeAddrInfoImpl(T &addr_info) {
|
||||
T *next = nullptr;
|
||||
for (T *cur = std::addressof(addr_info); cur != nullptr; cur = next) {
|
||||
next = cur->ai_next;
|
||||
|
||||
if (cur->ai_addr != nullptr) {
|
||||
if (IsAfInet<T>(cur->ai_family)) {
|
||||
ams::socket::impl::Free(reinterpret_cast<SockAddrInType<T> *>(cur->ai_addr));
|
||||
} else if (IsAfInet6<T>(cur->ai_family)) {
|
||||
ams::socket::impl::Free(reinterpret_cast<SockAddrIn6Type<T> *>(cur->ai_addr));
|
||||
} else {
|
||||
ams::socket::impl::Free(cur->ai_addr);
|
||||
}
|
||||
cur->ai_addr = nullptr;
|
||||
}
|
||||
|
||||
if (cur->ai_canonname != nullptr) {
|
||||
ams::socket::impl::Free(cur->ai_canonname);
|
||||
cur->ai_canonname = nullptr;
|
||||
}
|
||||
|
||||
if (cur != std::addressof(addr_info)) {
|
||||
ams::socket::impl::Free(cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
size_t AddrInfoSingleSizeOf(const T *addr_info) {
|
||||
size_t rc = 6 * sizeof(u32);
|
||||
|
||||
if (addr_info->ai_addr == nullptr) {
|
||||
rc += sizeof(u32);
|
||||
} else if (IsAfInet<T>(addr_info->ai_family)) {
|
||||
rc += DNSSerializer::SizeOf(*reinterpret_cast<SockAddrInType<T> *>(addr_info->ai_addr));
|
||||
} else if (IsAfInet6<T>(addr_info->ai_family)) {
|
||||
rc += DNSSerializer::SizeOf(*reinterpret_cast<SockAddrIn6Type<T> *>(addr_info->ai_addr));
|
||||
} else if (addr_info->ai_addrlen == 0) {
|
||||
rc += sizeof(u32);
|
||||
} else {
|
||||
rc += addr_info->ai_addrlen;
|
||||
}
|
||||
|
||||
if (addr_info->ai_canonname != nullptr) {
|
||||
rc += DNSSerializer::SizeOf(static_cast<const char *>(addr_info->ai_canonname));
|
||||
} else {
|
||||
rc += sizeof(u8);
|
||||
}
|
||||
|
||||
if (addr_info->ai_next == nullptr) {
|
||||
rc += sizeof(u32);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
size_t SizeOfImpl(const T &in) {
|
||||
size_t rc = 0;
|
||||
|
||||
for (const T *addr_info = std::addressof(in); addr_info != nullptr; addr_info = addr_info->ai_next) {
|
||||
rc += AddrInfoSingleSizeOf(addr_info);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
ssize_t ToBufferInternalImpl(u8 * const dst, size_t dst_size, const T &addr_info) {
|
||||
ssize_t rc = -1;
|
||||
u8 *cur = dst;
|
||||
|
||||
{
|
||||
const u32 value = AddrInfoMagic;
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
const u32 value = static_cast<u32>(addr_info.ai_flags);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
const u32 value = static_cast<u32>(addr_info.ai_family);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
const u32 value = static_cast<u32>(addr_info.ai_socktype);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
const u32 value = static_cast<u32>(addr_info.ai_protocol);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
const u32 value = static_cast<u32>(addr_info.ai_addrlen);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
if (addr_info.ai_addr == nullptr || addr_info.ai_addrlen == 0) {
|
||||
const u32 value = 0;
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
} else if (IsAfInet<T>(addr_info.ai_family)) {
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), *reinterpret_cast<SockAddrInType<T> *>(addr_info.ai_addr))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
} else if (IsAfInet6<T>(addr_info.ai_family)) {
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), *reinterpret_cast<SockAddrIn6Type<T> *>(addr_info.ai_addr))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
} else {
|
||||
if (dst_size - (cur - dst) < addr_info.ai_addrlen) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* NOTE: This is clearly a nintendo bug, see the accompanying note in FromBufferInternalImpl */
|
||||
std::memmove(cur, std::addressof(addr_info.ai_addr), addr_info.ai_addrlen);
|
||||
rc = addr_info.ai_addrlen;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), addr_info.ai_canonname)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
if (addr_info.ai_next == nullptr) {
|
||||
const u32 value = 0;
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), value)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
rc = cur - dst;
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) {
|
||||
ssize_t rc = -1;
|
||||
u8 *cur = dst;
|
||||
std::memset(dst, 0, dst_size);
|
||||
|
||||
const size_t required = DNSSerializer::SizeOf(in);
|
||||
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, required, __LINE__)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
for (const T *addr_info = std::addressof(in); addr_info != nullptr; addr_info = addr_info->ai_next) {
|
||||
if ((rc = ToBufferInternalImpl(cur, dst_size, *addr_info)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
rc = cur - dst;
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
ssize_t FromBufferInternalImpl(T &out, const u8 *src, size_t src_size) {
|
||||
ssize_t rc = -1;
|
||||
const u8 *cur = src;
|
||||
|
||||
std::memset(std::addressof(out), 0, sizeof(out));
|
||||
|
||||
ON_SCOPE_EXIT { if (rc < 0) { FreeAddrInfo(out); } };
|
||||
|
||||
u32 tmp_value;
|
||||
|
||||
{
|
||||
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
} else if (tmp_value != AddrInfoMagic) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.ai_flags = static_cast<decltype(out.ai_flags)>(tmp_value);
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.ai_family = static_cast<decltype(out.ai_family)>(tmp_value);
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.ai_socktype = static_cast<decltype(out.ai_socktype)>(tmp_value);
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.ai_protocol = static_cast<decltype(out.ai_protocol)>(tmp_value);
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.ai_addrlen = static_cast<decltype(out.ai_addrlen)>(tmp_value);
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
if (out.ai_addrlen == 0) {
|
||||
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (tmp_value != 0) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
out.ai_addr = nullptr;
|
||||
} else if (IsAfInet<T>(out.ai_family)) {
|
||||
out.ai_addr = static_cast<SockAddrType<T> *>(ams::socket::impl::Alloc(sizeof(SockAddrInType<T>)));
|
||||
if (out.ai_addr == nullptr) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
std::memset(out.ai_addr, 0, sizeof(SockAddrInType<T>));
|
||||
|
||||
if ((rc = DNSSerializer::FromBuffer(*reinterpret_cast<SockAddrInType<T> *>(out.ai_addr), cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
} else if (IsAfInet6<T>(out.ai_family)) {
|
||||
out.ai_addr = static_cast<SockAddrType<T> *>(ams::socket::impl::Alloc(sizeof(SockAddrIn6Type<T>)));
|
||||
if (out.ai_addr == nullptr) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
std::memset(out.ai_addr, 0, sizeof(SockAddrIn6Type<T>));
|
||||
|
||||
if ((rc = DNSSerializer::FromBuffer(*reinterpret_cast<SockAddrIn6Type<T> *>(out.ai_addr), cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
} else {
|
||||
out.ai_addr = static_cast<decltype(out.ai_addr)>(ams::socket::impl::Alloc(out.ai_addrlen));
|
||||
if (out.ai_addr == nullptr) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* NOTE: This is *clearly* a nintendo bug. */
|
||||
/* They obviously intend to copy to the buffer they just allocated, but instead they copy to the addrinfo structure itself. */
|
||||
/* Probably &out.ai_addr instead of &out.ai_addr[0]? Either way, we'll implement what they do, but... */
|
||||
std::memcpy(std::addressof(out.ai_addr), cur, out.ai_addrlen);
|
||||
rc = out.ai_addrlen;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
if ((rc = DNSSerializer::FromBuffer(out.ai_canonname, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
{
|
||||
if ((rc = DNSSerializer::FromBuffer(tmp_value, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
} else if (tmp_value == 0) {
|
||||
out.ai_next = nullptr;
|
||||
cur += rc;
|
||||
} else if (tmp_value == AddrInfoMagic) {
|
||||
out.ai_next = static_cast<T *>(ams::socket::impl::Alloc(sizeof(T)));
|
||||
if (out.ai_next == nullptr) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::memset(out.ai_next, 0, sizeof(T));
|
||||
} else {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
rc = cur - src;
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsAddrInfo<T>
|
||||
ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) {
|
||||
ssize_t rc = 0;
|
||||
const u8 *cur = src;
|
||||
|
||||
const size_t required = DNSSerializer::SizeOf(out);
|
||||
if (src_size < required) {
|
||||
ams::socket::SetLastError(ams::socket::Errno::ENoSpc);
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
for (T *addr_info = std::addressof(out); addr_info != nullptr; addr_info = addr_info->ai_next) {
|
||||
if ((rc = FromBufferInternalImpl(*addr_info, cur, src_size - (cur - src))) == -1) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
rc = cur - src;
|
||||
return rc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const struct addrinfo &in) {
|
||||
return SizeOfImpl(in);
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const ams::socket::AddrInfo &in) {
|
||||
return SizeOfImpl(in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const ams::socket::AddrInfo &in) {
|
||||
return ToBufferImpl(dst, dst_size, in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct addrinfo &in) {
|
||||
return ToBufferImpl(dst, dst_size, in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(ams::socket::AddrInfo &out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(struct addrinfo &out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
void FreeAddrInfo(ams::socket::AddrInfo &addr_info) {
|
||||
return FreeAddrInfoImpl(addr_info);
|
||||
}
|
||||
|
||||
void FreeAddrInfo(struct addrinfo &addr_info) {
|
||||
return FreeAddrInfoImpl(addr_info);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,234 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../dnsmitm_debug.hpp"
|
||||
#include "../socket_allocator.hpp"
|
||||
#include "serializer.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver::serializer {
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename T>
|
||||
concept IsHostEnt = std::same_as<T, ams::socket::HostEnt> || std::same_as<T, struct hostent>;
|
||||
|
||||
template<typename T> requires IsHostEnt<T>
|
||||
using InAddrType = typename std::conditional<std::same_as<T, ams::socket::HostEnt>, ams::socket::InAddr, struct in_addr>::type;
|
||||
|
||||
template<typename T> requires IsHostEnt<T>
|
||||
constexpr bool IsAfInet(const auto h_addrtype) {
|
||||
if constexpr (std::same_as<T, ams::socket::HostEnt>) {
|
||||
return h_addrtype == ams::socket::Family::Af_Inet;
|
||||
} else {
|
||||
return h_addrtype == AF_INET;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> requires IsHostEnt<T>
|
||||
constexpr bool IsAfInet6(const auto h_addrtype) {
|
||||
if constexpr (std::same_as<T, ams::socket::HostEnt>) {
|
||||
return h_addrtype == ams::socket::Family::Af_Inet6;
|
||||
} else {
|
||||
return h_addrtype == AF_INET;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> requires IsHostEnt<T>
|
||||
size_t SizeOfImpl(const T &in) {
|
||||
size_t rc = 0;
|
||||
u32 dummy = 0;
|
||||
|
||||
rc += DNSSerializer::SizeOf((const char *)(in.h_name));
|
||||
rc += DNSSerializer::SizeOf((const char **)(in.h_aliases), dummy);
|
||||
rc += sizeof(u32);
|
||||
rc += sizeof(u32);
|
||||
rc += DNSSerializer::SizeOf((const InAddrType<T> **)(in.h_addr_list), dummy);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsHostEnt<T>
|
||||
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) {
|
||||
ssize_t rc = -1;
|
||||
u8 *cur = dst;
|
||||
|
||||
const size_t required = DNSSerializer::SizeOf(in);
|
||||
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, required, __LINE__)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), in.h_name)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), in.h_aliases)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
const u16 h_addrtype = static_cast<u16>(in.h_addrtype);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), h_addrtype)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
const u16 h_length = in.h_length;
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), h_length)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
if (IsAfInet<T>(in.h_addrtype)) {
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), (InAddrType<T> **)(in.h_addr_list))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
} else if (IsAfInet6<T>(in.h_addrtype)) {
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), (InAddrType<T> **)(in.h_addr_list))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
} else {
|
||||
const u32 null = 0;
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), null)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
rc = cur - dst;
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsHostEnt<T>
|
||||
ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) {
|
||||
ssize_t rc = -1;
|
||||
const u8 *cur = src;
|
||||
|
||||
std::memset(std::addressof(out), 0, sizeof(out));
|
||||
|
||||
ON_SCOPE_EXIT {
|
||||
if (rc < 0) {
|
||||
FreeHostent(out);
|
||||
}
|
||||
};
|
||||
|
||||
if ((rc = DNSSerializer::FromBuffer(out.h_name, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
if ((rc = DNSSerializer::FromBuffer(out.h_aliases, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
u16 h_addrtype = 0;
|
||||
if ((rc = DNSSerializer::FromBuffer(h_addrtype, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.h_addrtype = static_cast<decltype(out.h_addrtype)>(h_addrtype);
|
||||
cur += rc;
|
||||
|
||||
u16 h_length = 0;
|
||||
if ((rc = DNSSerializer::FromBuffer(h_length, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.h_length = h_length;
|
||||
cur += rc;
|
||||
|
||||
InAddrType<T> **addrs = nullptr;
|
||||
if ((rc = DNSSerializer::FromBuffer(addrs, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.h_addr_list = (char **)addrs;
|
||||
cur += rc;
|
||||
|
||||
rc = cur - src;
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsHostEnt<T>
|
||||
void FreeHostentImpl(T &ent) {
|
||||
if (ent.h_name != nullptr) {
|
||||
ams::socket::impl::Free(ent.h_name);
|
||||
ent.h_name = nullptr;
|
||||
}
|
||||
|
||||
if (ent.h_aliases != nullptr) {
|
||||
u32 i = 0;
|
||||
for (char *str = ent.h_aliases[i]; str != nullptr; str = ent.h_aliases[++i]) {
|
||||
ams::socket::impl::Free(str);
|
||||
ent.h_aliases[i] = nullptr;
|
||||
}
|
||||
|
||||
ams::socket::impl::Free(ent.h_aliases);
|
||||
ent.h_aliases = nullptr;
|
||||
}
|
||||
|
||||
if (ent.h_addr_list != nullptr) {
|
||||
AMS_ASSERT(ent.h_length == sizeof(u32));
|
||||
if (ent.h_length == sizeof(u32)) {
|
||||
auto **addr_list = reinterpret_cast<InAddrType<T> **>(ent.h_addr_list);
|
||||
|
||||
u32 i = 0;
|
||||
for (auto *addr = addr_list[i]; addr != nullptr; addr = addr_list[++i]) {
|
||||
ams::socket::impl::Free(addr);
|
||||
addr_list[i] = nullptr;
|
||||
}
|
||||
|
||||
ams::socket::impl::Free(ent.h_addr_list);
|
||||
ent.h_addr_list = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::memset(std::addressof(ent), 0, sizeof(ent));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const struct hostent &in) {
|
||||
return SizeOfImpl(in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct hostent &in) {
|
||||
return ToBufferImpl(dst, dst_size, in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(struct hostent &out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const ams::socket::HostEnt &in) {
|
||||
return SizeOfImpl(in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const ams::socket::HostEnt &in) {
|
||||
return ToBufferImpl(dst, dst_size, in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(ams::socket::HostEnt &out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
void FreeHostent(ams::socket::HostEnt &ent) {
|
||||
return FreeHostentImpl(ent);
|
||||
}
|
||||
|
||||
void FreeHostent(struct hostent &ent) {
|
||||
return FreeHostentImpl(ent);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../dnsmitm_debug.hpp"
|
||||
#include "../socket_allocator.hpp"
|
||||
#include "serializer.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver::serializer {
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename T>
|
||||
concept IsInAddr = std::same_as<T, ams::socket::InAddr> || std::same_as<T, struct in_addr>;
|
||||
|
||||
template<typename T> requires IsInAddr<T>
|
||||
size_t SizeOfImpl(const T &) {
|
||||
return sizeof(u32);
|
||||
}
|
||||
|
||||
template<typename T> requires IsInAddr<T>
|
||||
size_t SizeOfImpl(const T **in, u32 &out_count) {
|
||||
size_t rc = sizeof(u32);
|
||||
out_count = 0;
|
||||
|
||||
if (in != nullptr) {
|
||||
for (const T ** cur_addr = in; *cur_addr != nullptr; ++cur_addr) {
|
||||
++out_count;
|
||||
rc += sizeof(u32);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsInAddr<T>
|
||||
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) {
|
||||
ssize_t rc = -1;
|
||||
u8 *cur = dst;
|
||||
|
||||
const u32 val = DNSSerializer::InternalHton(in.s_addr);
|
||||
|
||||
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, sizeof(in), __LINE__)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::memcpy(cur, std::addressof(val), sizeof(val));
|
||||
rc += sizeof(val);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsInAddr<T>
|
||||
ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) {
|
||||
ssize_t rc = -1;
|
||||
if (src_size < sizeof(out)) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::memset(std::addressof(out), 0, sizeof(out));
|
||||
out.s_addr = DNSSerializer::InternalNtoh(*reinterpret_cast<const u32 *>(src));
|
||||
|
||||
rc = sizeof(u32);
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsInAddr<T>
|
||||
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, T **arr) {
|
||||
ssize_t rc = -1;
|
||||
u8 *cur = dst;
|
||||
|
||||
if (arr == nullptr && dst_size < sizeof(u32)) {
|
||||
return rc;
|
||||
} else if (arr == nullptr) {
|
||||
const u32 null = 0;
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), null)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
} else {
|
||||
u32 count = 0;
|
||||
for (auto *tmp = arr; *tmp != nullptr; ++tmp) {
|
||||
++count;
|
||||
}
|
||||
|
||||
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, (count + 1) * sizeof(**arr), __LINE__)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), count)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
rc = 0;
|
||||
for (auto i = 0; arr[i] != nullptr; ++i) {
|
||||
const T addr = *arr[i];
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), addr)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
cur += rc;
|
||||
}
|
||||
|
||||
rc = cur - dst;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsInAddr<T>
|
||||
ssize_t FromBufferImpl(T **&out, const u8 *src, size_t src_size) {
|
||||
ssize_t rc = -1;
|
||||
const u8 *cur = src;
|
||||
|
||||
out = nullptr;
|
||||
|
||||
ON_SCOPE_EXIT {
|
||||
if (rc == -1 && out != nullptr) {
|
||||
for (auto i = 0; out[i] != nullptr; ++i) {
|
||||
ams::socket::impl::Free(out[i]);
|
||||
out[i] = nullptr;
|
||||
}
|
||||
|
||||
ams::socket::impl::Free(out);
|
||||
out = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
if (src == nullptr) {
|
||||
rc = 0;
|
||||
return rc;
|
||||
} else if (src_size == 0) {
|
||||
rc = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
u32 count = 0;
|
||||
if ((rc = DNSSerializer::FromBuffer(count, cur, src_size)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
if (count == 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
out = static_cast<T **>(ams::socket::impl::Alloc((count + 1) * sizeof(T *)));
|
||||
if (out == nullptr) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
std::memset(out, 0, (count + 1) * sizeof(T *));
|
||||
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
out[i] = static_cast<T *>(ams::socket::impl::Alloc(sizeof(T)));
|
||||
if (out[i] == nullptr) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
u32 s_addr = 0;
|
||||
if ((rc = DNSSerializer::FromBuffer(s_addr, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out[i]->s_addr = s_addr;
|
||||
cur += rc;
|
||||
}
|
||||
out[count] = nullptr;
|
||||
|
||||
rc = cur - src;
|
||||
return rc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const struct in_addr &in) {
|
||||
return SizeOfImpl(in);
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const ams::socket::InAddr &in) {
|
||||
return SizeOfImpl(in);
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const struct in_addr **in, u32 &out_count) {
|
||||
return SizeOfImpl(in, out_count);
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const ams::socket::InAddr **in, u32 &out_count) {
|
||||
return SizeOfImpl(in, out_count);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct in_addr &in) {
|
||||
return ToBufferImpl(dst, dst_size, in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const ams::socket::InAddr &in) {
|
||||
return ToBufferImpl(dst, dst_size, in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(struct in_addr &out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(struct ams::socket::InAddr &out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, struct in_addr **arr) {
|
||||
return ToBufferImpl(dst, dst_size, arr);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, ams::socket::InAddr **arr) {
|
||||
return ToBufferImpl(dst, dst_size, arr);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(struct in_addr **&out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(struct ams::socket::InAddr **&out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../dnsmitm_debug.hpp"
|
||||
#include "serializer.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver::serializer {
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const u16 &in) {
|
||||
/* Convert the value. */
|
||||
u8 *cur = dst;
|
||||
const u16 val = InternalHton(in);
|
||||
|
||||
/* Check arguments. */
|
||||
ssize_t rc = -1;
|
||||
if ((rc = CheckToBufferArguments(cur, dst_size, sizeof(u16), __LINE__)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::memcpy(cur, std::addressof(val), sizeof(u16));
|
||||
rc += sizeof(u16);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(u16 &out, const u8 *src, size_t src_size) {
|
||||
if (src_size < sizeof(u16)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
out = InternalNtoh(*reinterpret_cast<const u16 *>(src));
|
||||
return sizeof(u16);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const u32 &in) {
|
||||
/* Convert the value. */
|
||||
u8 *cur = dst;
|
||||
const u32 val = InternalHton(in);
|
||||
|
||||
/* Check arguments. */
|
||||
ssize_t rc = -1;
|
||||
if ((rc = CheckToBufferArguments(cur, dst_size, sizeof(u32), __LINE__)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::memcpy(cur, std::addressof(val), sizeof(u32));
|
||||
rc += sizeof(u32);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(u32 &out, const u8 *src, size_t src_size) {
|
||||
if (src_size < sizeof(u32)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
out = InternalNtoh(*reinterpret_cast<const u32 *>(src));
|
||||
return sizeof(u32);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../dnsmitm_debug.hpp"
|
||||
#include "../socket_allocator.hpp"
|
||||
#include "serializer.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver::serializer {
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename T>
|
||||
concept IsSockAddrIn = std::same_as<T, ams::socket::SockAddrIn> || std::same_as<T, struct sockaddr_in>;
|
||||
|
||||
template<typename T> requires IsSockAddrIn<T>
|
||||
size_t SizeOfImpl(const T &in) {
|
||||
size_t rc = 0;
|
||||
rc += sizeof(u16);
|
||||
rc += sizeof(u16);
|
||||
rc += DNSSerializer::SizeOf(in.sin_addr);
|
||||
rc += sizeof(in.sin_zero);
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsSockAddrIn<T>
|
||||
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) {
|
||||
ssize_t rc = -1;
|
||||
u8 *cur = dst;
|
||||
|
||||
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, sizeof(in), __LINE__)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
const u16 sin_family = static_cast<u16>(in.sin_family);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin_family)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
const u16 sin_port = static_cast<u16>(in.sin_port);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin_port)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
const u32 s_addr = static_cast<u32>(in.sin_addr.s_addr);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), s_addr)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
if (dst_size - (cur - dst) < sizeof(in.sin_zero)) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::memcpy(cur, in.sin_zero, sizeof(in.sin_zero));
|
||||
cur += sizeof(in.sin_zero);
|
||||
|
||||
rc = cur - dst;
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsSockAddrIn<T>
|
||||
ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) {
|
||||
ssize_t rc = -1;
|
||||
const u8 *cur = src;
|
||||
|
||||
u16 sin_family;
|
||||
if ((rc = DNSSerializer::FromBuffer(sin_family, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.sin_family = static_cast<decltype(out.sin_family)>(sin_family);
|
||||
cur += rc;
|
||||
|
||||
u16 sin_port;
|
||||
if ((rc = DNSSerializer::FromBuffer(sin_port, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.sin_port = static_cast<decltype(out.sin_port)>(sin_port);
|
||||
cur += rc;
|
||||
|
||||
u32 s_addr;
|
||||
if ((rc = DNSSerializer::FromBuffer(s_addr, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.sin_addr.s_addr = static_cast<decltype(out.sin_addr.s_addr)>(s_addr);
|
||||
cur += rc;
|
||||
|
||||
if (src_size - (cur - src) < sizeof(out.sin_zero)) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::memcpy(out.sin_zero, cur, sizeof(out.sin_zero));
|
||||
cur += sizeof(out.sin_zero);
|
||||
|
||||
rc = cur - src;
|
||||
return rc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const struct sockaddr_in &in) {
|
||||
return SizeOfImpl(in);
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const ams::socket::SockAddrIn &in) {
|
||||
return SizeOfImpl(in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct sockaddr_in &in) {
|
||||
return ToBufferImpl(dst, dst_size, in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const ams::socket::SockAddrIn &in) {
|
||||
return ToBufferImpl(dst, dst_size, in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(struct sockaddr_in &out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(struct ams::socket::SockAddrIn &out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../dnsmitm_debug.hpp"
|
||||
#include "../socket_allocator.hpp"
|
||||
#include "serializer.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver::serializer {
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename T>
|
||||
concept IsSockAddrIn6 = std::same_as<T, struct sockaddr_in6>;
|
||||
|
||||
template<typename T> requires IsSockAddrIn6<T>
|
||||
size_t SizeOfImpl(const T &in) {
|
||||
size_t rc = 0;
|
||||
rc += sizeof(u16);
|
||||
rc += sizeof(u16);
|
||||
rc += sizeof(u32);
|
||||
rc += DNSSerializer::SizeOf(in.sin6_addr);
|
||||
rc += sizeof(u32);
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsSockAddrIn6<T>
|
||||
ssize_t ToBufferImpl(u8 * const dst, size_t dst_size, const T &in) {
|
||||
ssize_t rc = -1;
|
||||
u8 *cur = dst;
|
||||
|
||||
if ((rc = DNSSerializer::CheckToBufferArguments(cur, dst_size, sizeof(in), __LINE__)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
const u16 sin6_family = static_cast<u16>(in.sin6_family);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin6_family)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
const u16 sin6_port = static_cast<u16>(in.sin6_port);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin6_port)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
const u32 sin6_flowinfo = static_cast<u32>(in.sin6_flowinfo);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin6_flowinfo)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
std::memcpy(cur, std::addressof(in.sin6_addr), sizeof(in.sin6_addr));
|
||||
cur += sizeof(in.sin6_addr);
|
||||
|
||||
const u32 sin6_scope_id = static_cast<u32>(in.sin6_scope_id);
|
||||
if ((rc = DNSSerializer::ToBuffer(cur, dst_size - (cur - dst), sin6_scope_id)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
rc = cur - dst;
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<typename T> requires IsSockAddrIn6<T>
|
||||
ssize_t FromBufferImpl(T &out, const u8 *src, size_t src_size) {
|
||||
ssize_t rc = -1;
|
||||
const u8 *cur = src;
|
||||
|
||||
u16 sin6_family;
|
||||
if ((rc = DNSSerializer::FromBuffer(sin6_family, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.sin6_family = static_cast<decltype(out.sin6_family)>(sin6_family);
|
||||
cur += rc;
|
||||
|
||||
u16 sin6_port;
|
||||
if ((rc = DNSSerializer::FromBuffer(sin6_port, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.sin6_port = static_cast<decltype(out.sin6_port)>(sin6_port);
|
||||
cur += rc;
|
||||
|
||||
u32 sin6_flowinfo;
|
||||
if ((rc = DNSSerializer::FromBuffer(sin6_flowinfo, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.sin6_flowinfo = static_cast<decltype(out.sin6_flowinfo)>(sin6_flowinfo);
|
||||
cur += rc;
|
||||
|
||||
std::memcpy(std::addressof(out.sin6_addr), cur, sizeof(out.sin6_addr));
|
||||
cur += sizeof(out.sin6_addr);
|
||||
|
||||
u32 sin6_scope_id;
|
||||
if ((rc = DNSSerializer::FromBuffer(sin6_scope_id, cur, src_size - (cur - src))) == -1) {
|
||||
return rc;
|
||||
}
|
||||
out.sin6_scope_id = static_cast<decltype(out.sin6_scope_id)>(sin6_scope_id);
|
||||
cur += rc;
|
||||
|
||||
rc = cur - src;
|
||||
return rc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const struct sockaddr_in6 &in) {
|
||||
return SizeOfImpl(in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, const struct sockaddr_in6 &in) {
|
||||
return ToBufferImpl(dst, dst_size, in);
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(struct sockaddr_in6 &out, const u8 *src, size_t src_size) {
|
||||
return FromBufferImpl(out, src, src_size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../dnsmitm_debug.hpp"
|
||||
#include "../socket_allocator.hpp"
|
||||
#include "serializer.hpp"
|
||||
|
||||
namespace ams::mitm::socket::resolver::serializer {
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const char *str) {
|
||||
if (str == nullptr) {
|
||||
return sizeof(char);
|
||||
}
|
||||
return std::strlen(str) + 1;
|
||||
}
|
||||
|
||||
template<> size_t DNSSerializer::SizeOf(const char **str, u32 &out_count) {
|
||||
size_t rc = sizeof(u32);
|
||||
out_count = 0;
|
||||
|
||||
if (str != nullptr) {
|
||||
for (const char **cur = str; *cur != nullptr; ++cur) {
|
||||
++out_count;
|
||||
rc += SizeOf(*cur);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, char *str) {
|
||||
ssize_t rc = -1;
|
||||
u8 *cur = dst;
|
||||
|
||||
if (str == nullptr && dst_size == 0) {
|
||||
return -1;
|
||||
} else if (str == nullptr) {
|
||||
*cur = '\x00';
|
||||
return 1;
|
||||
} else if ((rc = SizeOf(static_cast<const char *>(str))) == 0) {
|
||||
*cur = '\x00';
|
||||
return 1;
|
||||
} else if (CheckToBufferArguments(cur, dst_size, rc + 1, __LINE__) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::memmove(cur, str, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(char *&out, const u8 *src, size_t src_size) {
|
||||
size_t len = 0;
|
||||
|
||||
if (src == nullptr) {
|
||||
return 0;
|
||||
} else if (src_size == 0) {
|
||||
return 0;
|
||||
} else if (src_size < (len = SizeOf(reinterpret_cast<const char *>(src)))) {
|
||||
return 1;
|
||||
} else if (src[0] == '\x00') {
|
||||
return 1;
|
||||
}
|
||||
|
||||
out = static_cast<char *>(ams::socket::impl::Alloc(len));
|
||||
if (out == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::memmove(out, src, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::ToBuffer(u8 * const dst, size_t dst_size, char **str) {
|
||||
ssize_t rc = -1;
|
||||
u8 *cur = dst;
|
||||
u32 count = 0;
|
||||
|
||||
if (dst_size == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const size_t total_size = SizeOf(const_cast<const char **>(str), count);
|
||||
AMS_UNUSED(total_size);
|
||||
|
||||
if ((rc = CheckToBufferArguments(cur, dst_size, sizeof(u32), __LINE__)) == -1) {
|
||||
return rc;
|
||||
} else if ((rc = ToBuffer(cur, dst_size, count)) == -1) {
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
dst_size -= rc;
|
||||
|
||||
if (str != nullptr) {
|
||||
for (char **cur_str = str; *cur_str != nullptr; ++cur_str) {
|
||||
const auto tmp = ToBuffer(cur, dst_size, *cur_str);
|
||||
if (tmp == -1) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
cur += tmp;
|
||||
dst_size -= tmp;
|
||||
rc += tmp;
|
||||
}
|
||||
}
|
||||
|
||||
rc = cur - dst;
|
||||
return rc;
|
||||
}
|
||||
|
||||
template<> ssize_t DNSSerializer::FromBuffer(char **&out, const u8 *src, size_t src_size) {
|
||||
ssize_t rc = -1;
|
||||
const u8 *cur = src;
|
||||
u32 count = 0;
|
||||
|
||||
out = nullptr;
|
||||
|
||||
ON_SCOPE_EXIT {
|
||||
if (rc < 0 && out != nullptr) {
|
||||
u32 i = 0;
|
||||
for (char *str = *out; str != nullptr; str = out[++i]) {
|
||||
ams::socket::impl::Free(str);
|
||||
}
|
||||
ams::socket::impl::Free(out);
|
||||
out = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
if (src == nullptr) {
|
||||
rc = 0;
|
||||
return rc;
|
||||
} else if (src_size == 0) {
|
||||
rc = 0;
|
||||
return rc;
|
||||
} else if ((rc = FromBuffer(count, cur, src_size)) == -1) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
cur += rc;
|
||||
|
||||
out = static_cast<char **>(ams::socket::impl::Alloc((count + 1) * sizeof(char *)));
|
||||
if (out == nullptr) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
std::memset(out, 0, (count + 1) * sizeof(char *));
|
||||
|
||||
u32 i;
|
||||
for (i = 0; i < count; ++i) {
|
||||
const size_t len = std::strlen(reinterpret_cast<const char *>(cur));
|
||||
out[i] = static_cast<char *>(ams::socket::impl::Alloc(len + 1));
|
||||
if (out[i] == nullptr) {
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::memmove(out[i], cur, len + 1);
|
||||
cur += len + 1;
|
||||
}
|
||||
|
||||
out[i] = nullptr;
|
||||
rc = cur - src;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "sfdnsres_shim.h"
|
||||
#include <stratosphere/sf/sf_mitm_dispatch.h>
|
||||
|
||||
/* Command forwarders. */
|
||||
Result sfdnsresGetHostByNameRequestWithOptionsFwd(Service *s, u64 process_id, const void *name, size_t name_size, void *out_hostent, size_t out_hostent_size, u32 *out_size, u32 options_version, const void *option, size_t option_size, u32 num_options, s32 *out_host_error, s32 *out_errno) {
|
||||
const struct {
|
||||
u32 options_version;
|
||||
u32 num_options;
|
||||
u64 process_id;
|
||||
} in = { options_version, num_options, process_id };
|
||||
struct {
|
||||
u32 size;
|
||||
s32 host_error;
|
||||
s32 errno;
|
||||
} out;
|
||||
|
||||
Result rc = serviceMitmDispatchInOut(s, 10, in, out,
|
||||
.buffer_attrs = {
|
||||
SfBufferAttr_HipcAutoSelect | SfBufferAttr_In,
|
||||
SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out,
|
||||
SfBufferAttr_HipcAutoSelect | SfBufferAttr_In
|
||||
},
|
||||
.buffers = {
|
||||
{ name, name_size },
|
||||
{ out_hostent, out_hostent_size },
|
||||
{ option, option_size }
|
||||
},
|
||||
.in_send_pid = true,
|
||||
.override_pid = process_id,
|
||||
);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (out_size) *out_size = out.size;
|
||||
if (out_host_error) *out_host_error = out.host_error;
|
||||
if (out_errno) *out_errno = out.errno;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result sfdnsresGetAddrInfoRequestWithOptionsFwd(Service *s, u64 process_id, const void *node, size_t node_size, const void *srv, size_t srv_size, const void *hint, size_t hint_size, void *out_ai, size_t out_ai_size, u32 *out_size, s32 *out_rv, u32 options_version, const void *option, size_t option_size, u32 num_options, s32 *out_host_error, s32 *out_errno) {
|
||||
const struct {
|
||||
u32 options_version;
|
||||
u32 num_options;
|
||||
u64 process_id;
|
||||
} in = { options_version, num_options, process_id };
|
||||
struct {
|
||||
u32 size;
|
||||
s32 rv;
|
||||
s32 host_error;
|
||||
s32 errno;
|
||||
} out;
|
||||
|
||||
Result rc = serviceMitmDispatchInOut(s, 12, in, out,
|
||||
.buffer_attrs = {
|
||||
SfBufferAttr_HipcMapAlias | SfBufferAttr_In,
|
||||
SfBufferAttr_HipcMapAlias | SfBufferAttr_In,
|
||||
SfBufferAttr_HipcMapAlias | SfBufferAttr_In,
|
||||
SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out,
|
||||
SfBufferAttr_HipcAutoSelect | SfBufferAttr_In
|
||||
},
|
||||
.buffers = {
|
||||
{ node, node_size },
|
||||
{ srv, srv_size },
|
||||
{ hint, hint_size },
|
||||
{ out_ai, out_ai_size },
|
||||
{ option, option_size }
|
||||
},
|
||||
.in_send_pid = true,
|
||||
.override_pid = process_id,
|
||||
);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (out_size) *out_size = out.size;
|
||||
if (out_rv) *out_rv = out.rv;
|
||||
if (out_host_error) *out_host_error = out.host_error;
|
||||
if (out_errno) *out_errno = out.errno;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* @file sfdnsres_shim.h
|
||||
* @brief IPC wrapper for dns.mitm.
|
||||
* @author SciresM
|
||||
* @copyright libnx Authors
|
||||
*/
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Command forwarders. */
|
||||
Result sfdnsresGetHostByNameRequestWithOptionsFwd(Service *s, u64 process_id, const void *name, size_t name_size, void *out_hostent, size_t out_hostent_size, u32 *out_size, u32 options_version, const void *option, size_t option_size, u32 num_options, s32 *out_host_error, s32 *out_errno);
|
||||
|
||||
Result sfdnsresGetAddrInfoRequestWithOptionsFwd(Service *s, u64 process_id, const void *node, size_t node_size, const void *srv, size_t srv_size, const void *hint, size_t hint_size, void *out_ai, size_t out_ai_size, u32 *out_size, s32 *out_rv, u32 options_version, const void *option, size_t option_size, u32 num_options, s32 *out_host_error, s32 *out_errno);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::socket::impl {
|
||||
|
||||
void *Alloc(size_t size);
|
||||
void *Calloc(size_t num, size_t size);
|
||||
void Free(void *ptr);
|
||||
|
||||
}
|
||||
@@ -1,383 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_fs_utils.hpp"
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "fs_shim.h"
|
||||
#include "fs_mitm_service.hpp"
|
||||
#include "fsmitm_boot0storage.hpp"
|
||||
#include "fsmitm_calibration_binary_storage.hpp"
|
||||
#include "fsmitm_layered_romfs_storage.hpp"
|
||||
#include "fsmitm_save_utils.hpp"
|
||||
#include "fsmitm_readonly_layered_filesystem.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
using namespace ams::fs;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const ams::fs::Path AtmosphereHblWebContentDirPath = fs::MakeConstantPath("/atmosphere/hbl_html/");
|
||||
constexpr const char ProgramWebContentDir[] = "/manual_html/";
|
||||
|
||||
constinit os::SdkMutex g_boot0_detect_lock;
|
||||
constinit bool g_detected_boot0_kind = false;
|
||||
constinit bool g_is_boot0_custom_public_key = false;
|
||||
|
||||
constinit fssrv::impl::ProgramIndexMapInfoManager g_program_index_map_info_manager;
|
||||
|
||||
bool IsBoot0CustomPublicKey(::FsStorage &storage) {
|
||||
if (AMS_UNLIKELY(!g_detected_boot0_kind)) {
|
||||
std::scoped_lock lk(g_boot0_detect_lock);
|
||||
|
||||
if (AMS_LIKELY(!g_detected_boot0_kind)) {
|
||||
g_is_boot0_custom_public_key = DetectBoot0CustomPublicKey(storage);
|
||||
g_detected_boot0_kind = true;
|
||||
}
|
||||
}
|
||||
|
||||
return g_is_boot0_custom_public_key;
|
||||
}
|
||||
|
||||
bool GetSettingsItemBooleanValue(const char *name, const char *key) {
|
||||
u8 tmp = 0;
|
||||
AMS_ABORT_UNLESS(settings::fwdbg::GetSettingsItemValue(std::addressof(tmp), sizeof(tmp), name, key) == sizeof(tmp));
|
||||
return (tmp != 0);
|
||||
}
|
||||
|
||||
template<typename... Arguments>
|
||||
constexpr ALWAYS_INLINE auto MakeSharedFileSystem(Arguments &&... args) {
|
||||
return sf::CreateSharedObjectEmplaced<ams::fssrv::sf::IFileSystem, ams::fssrv::impl::FileSystemInterfaceAdapter>(std::forward<Arguments>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Arguments>
|
||||
constexpr ALWAYS_INLINE auto MakeSharedStorage(Arguments &&... args) {
|
||||
return sf::CreateSharedObjectEmplaced<ams::fssrv::sf::IStorage, ams::fssrv::impl::StorageInterfaceAdapter>(std::forward<Arguments>(args)...);
|
||||
}
|
||||
|
||||
Result OpenHblWebContentFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> &out, ncm::ProgramId program_id) {
|
||||
/* Verify eligibility. */
|
||||
bool is_hbl;
|
||||
R_UNLESS(R_SUCCEEDED(ams::pm::info::IsHblProgramId(std::addressof(is_hbl), program_id)), sm::mitm::ResultShouldForwardToSession());
|
||||
R_UNLESS(is_hbl, sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Hbl html directory must exist. */
|
||||
{
|
||||
FsDir d;
|
||||
R_UNLESS(R_SUCCEEDED(mitm::fs::OpenSdDirectory(std::addressof(d), AtmosphereHblWebContentDirPath.GetString(), fs::OpenDirectoryMode_Directory)), sm::mitm::ResultShouldForwardToSession());
|
||||
fsDirClose(std::addressof(d));
|
||||
}
|
||||
|
||||
/* Open the SD card using fs.mitm's session. */
|
||||
FsFileSystem sd_fs;
|
||||
R_TRY(fsOpenSdCardFileSystem(std::addressof(sd_fs)));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(sd_fs.s))};
|
||||
std::unique_ptr<fs::fsa::IFileSystem> sd_ifs = std::make_unique<fs::RemoteFileSystem>(sd_fs);
|
||||
|
||||
auto subdir_fs = std::make_unique<fssystem::SubDirectoryFileSystem>(std::move(sd_ifs));
|
||||
R_TRY(subdir_fs->Initialize(AtmosphereHblWebContentDirPath));
|
||||
|
||||
out.SetValue(MakeSharedFileSystem(std::make_shared<fs::ReadOnlyFileSystem>(std::move(subdir_fs)), false), target_object_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result OpenProgramSpecificWebContentFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> &out, ncm::ProgramId program_id, FsFileSystemType filesystem_type, Service *fwd, const fssrv::sf::Path *path, bool with_id) {
|
||||
/* Directory must exist. */
|
||||
R_UNLESS(HasSdManualHtmlContent(program_id), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Open the SD card using fs.mitm's session. */
|
||||
FsFileSystem sd_fs;
|
||||
R_TRY(fsOpenSdCardFileSystem(std::addressof(sd_fs)));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(sd_fs.s))};
|
||||
std::unique_ptr<fs::fsa::IFileSystem> sd_ifs = std::make_unique<fs::RemoteFileSystem>(sd_fs);
|
||||
|
||||
/* Format the subdirectory path. */
|
||||
char program_web_content_raw_path[0x100];
|
||||
FormatAtmosphereSdPath(program_web_content_raw_path, sizeof(program_web_content_raw_path), program_id, ProgramWebContentDir);
|
||||
|
||||
ams::fs::Path program_web_content_path;
|
||||
R_TRY(program_web_content_path.SetShallowBuffer(program_web_content_raw_path));
|
||||
|
||||
/* Make a new filesystem. */
|
||||
{
|
||||
auto subdir_fs = std::make_unique<fssystem::SubDirectoryFileSystem>(std::move(sd_ifs));
|
||||
R_TRY(subdir_fs->Initialize(program_web_content_path));
|
||||
|
||||
std::shared_ptr<fs::fsa::IFileSystem> new_fs = nullptr;
|
||||
|
||||
/* Try to open the existing fs. */
|
||||
FsFileSystem base_fs;
|
||||
bool opened_base_fs = false;
|
||||
if (with_id) {
|
||||
opened_base_fs = R_SUCCEEDED(fsOpenFileSystemWithIdFwd(fwd, std::addressof(base_fs), static_cast<u64>(program_id), filesystem_type, path->str));
|
||||
} else {
|
||||
opened_base_fs = R_SUCCEEDED(fsOpenFileSystemWithPatchFwd(fwd, std::addressof(base_fs), static_cast<u64>(program_id), filesystem_type));
|
||||
}
|
||||
|
||||
if (opened_base_fs) {
|
||||
/* Create a layered adapter. */
|
||||
new_fs = std::make_shared<ReadOnlyLayeredFileSystem>(std::move(subdir_fs), std::make_unique<fs::RemoteFileSystem>(base_fs));
|
||||
} else {
|
||||
/* Without an existing FS, just make a read only adapter to the subdirectory. */
|
||||
new_fs = std::make_shared<fs::ReadOnlyFileSystem>(std::move(subdir_fs));
|
||||
}
|
||||
|
||||
out.SetValue(MakeSharedFileSystem(std::move(new_fs), false), target_object_id);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result OpenWebContentFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> &out, ncm::ProgramId client_program_id, ncm::ProgramId program_id, FsFileSystemType filesystem_type, Service *fwd, const fssrv::sf::Path *path, bool with_id, bool try_program_specific) {
|
||||
/* Check first that we're a web applet opening web content. */
|
||||
R_UNLESS(ncm::IsWebAppletId(client_program_id), sm::mitm::ResultShouldForwardToSession());
|
||||
R_UNLESS(filesystem_type == FsFileSystemType_ContentManual, sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Try to mount the HBL web filesystem. If this succeeds then we're done. */
|
||||
R_SUCCEED_IF(R_SUCCEEDED(OpenHblWebContentFileSystem(out, program_id)));
|
||||
|
||||
/* If program specific override shouldn't be attempted, fall back. */
|
||||
R_UNLESS(try_program_specific, sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* If we're not opening a HBL filesystem, just try to open a generic one. */
|
||||
R_RETURN(OpenProgramSpecificWebContentFileSystem(out, program_id, filesystem_type, fwd, path, with_id));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool HasSdManualHtmlContent(ncm::ProgramId program_id) {
|
||||
/* Directory must exist. */
|
||||
FsDir d;
|
||||
if (R_SUCCEEDED(OpenAtmosphereSdDirectory(std::addressof(d), program_id, ProgramWebContentDir, fs::OpenDirectoryMode_Directory))) {
|
||||
::fsDirClose(std::addressof(d));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Result FsMitmService::OpenFileSystemWithPatch(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, ncm::ProgramId program_id, u32 _filesystem_type) {
|
||||
R_RETURN(OpenWebContentFileSystem(out, m_client_info.program_id, program_id, static_cast<FsFileSystemType>(_filesystem_type), m_forward_service.get(), nullptr, false, m_client_info.override_status.IsProgramSpecific()));
|
||||
}
|
||||
|
||||
Result FsMitmService::OpenFileSystemWithId(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, const fssrv::sf::Path &path, ncm::ProgramId program_id, u32 _filesystem_type) {
|
||||
R_RETURN(OpenWebContentFileSystem(out, m_client_info.program_id, program_id, static_cast<FsFileSystemType>(_filesystem_type), m_forward_service.get(), std::addressof(path), true, m_client_info.override_status.IsProgramSpecific()));
|
||||
}
|
||||
|
||||
Result FsMitmService::OpenSdCardFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out) {
|
||||
/* We only care about redirecting this for NS/emummc. */
|
||||
R_UNLESS(m_client_info.program_id == ncm::SystemProgramId::Ns, sm::mitm::ResultShouldForwardToSession());
|
||||
R_UNLESS(emummc::IsActive(), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Create a new SD card filesystem. */
|
||||
FsFileSystem sd_fs;
|
||||
R_TRY(fsOpenSdCardFileSystemFwd(m_forward_service.get(), std::addressof(sd_fs)));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(sd_fs.s))};
|
||||
|
||||
/* Return output filesystem. */
|
||||
auto redir_fs = std::make_shared<fssystem::DirectoryRedirectionFileSystem>(std::make_unique<RemoteFileSystem>(sd_fs));
|
||||
R_TRY(redir_fs->InitializeWithFixedPath("/Nintendo", emummc::GetNintendoDirPath()));
|
||||
|
||||
out.SetValue(MakeSharedFileSystem(std::move(redir_fs), false), target_object_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FsMitmService::OpenSaveDataFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, u8 _space_id, const fs::SaveDataAttribute &attribute) {
|
||||
/* We only want to intercept saves for games, right now. */
|
||||
const bool is_game_or_hbl = m_client_info.override_status.IsHbl() || ncm::IsApplicationId(m_client_info.program_id);
|
||||
R_UNLESS(is_game_or_hbl, sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Only redirect if the appropriate system setting is set. */
|
||||
R_UNLESS(GetSettingsItemBooleanValue("atmosphere", "fsmitm_redirect_saves_to_sd"), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Only redirect if the specific title being accessed has a redirect save flag. */
|
||||
R_UNLESS(cfg::HasContentSpecificFlag(m_client_info.program_id, "redirect_save"), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Only redirect account savedata. */
|
||||
R_UNLESS(attribute.type == fs::SaveDataType::Account, sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Get enum type for space id. */
|
||||
auto space_id = static_cast<FsSaveDataSpaceId>(_space_id);
|
||||
|
||||
/* Verify we can open the save. */
|
||||
static_assert(sizeof(fs::SaveDataAttribute) == sizeof(::FsSaveDataAttribute));
|
||||
FsFileSystem save_fs;
|
||||
R_UNLESS(R_SUCCEEDED(fsOpenSaveDataFileSystemFwd(m_forward_service.get(), std::addressof(save_fs), space_id, reinterpret_cast<const FsSaveDataAttribute *>(std::addressof(attribute)))), sm::mitm::ResultShouldForwardToSession());
|
||||
std::unique_ptr<fs::fsa::IFileSystem> save_ifs = std::make_unique<fs::RemoteFileSystem>(save_fs);
|
||||
|
||||
/* Mount the SD card using fs.mitm's session. */
|
||||
FsFileSystem sd_fs;
|
||||
R_TRY(fsOpenSdCardFileSystem(std::addressof(sd_fs)));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(sd_fs.s))};
|
||||
std::shared_ptr<fs::fsa::IFileSystem> sd_ifs = std::make_shared<fs::RemoteFileSystem>(sd_fs);
|
||||
|
||||
/* Verify that we can open the save directory, and that it exists. */
|
||||
const ncm::ProgramId application_id = attribute.program_id == ncm::InvalidProgramId ? m_client_info.program_id : attribute.program_id;
|
||||
|
||||
char save_dir_raw_path[0x100];
|
||||
R_TRY(mitm::fs::SaveUtil::GetDirectorySaveDataPath(save_dir_raw_path, sizeof(save_dir_raw_path), application_id, space_id, attribute));
|
||||
|
||||
ams::fs::Path save_dir_path;
|
||||
R_TRY(save_dir_path.SetShallowBuffer(save_dir_raw_path));
|
||||
|
||||
/* Check if this is the first time we're making the save. */
|
||||
bool is_new_save = false;
|
||||
{
|
||||
fs::DirectoryEntryType ent;
|
||||
R_TRY_CATCH(sd_ifs->GetEntryType(std::addressof(ent), save_dir_path)) {
|
||||
R_CATCH(fs::ResultPathNotFound) { is_new_save = true; }
|
||||
R_CATCH_ALL() { /* ... */ }
|
||||
} R_END_TRY_CATCH;
|
||||
}
|
||||
|
||||
/* Ensure the directory exists. */
|
||||
R_TRY(fssystem::EnsureDirectory(sd_ifs.get(), save_dir_path));
|
||||
|
||||
/* Create directory savedata filesystem. */
|
||||
auto subdir_fs = std::make_unique<fssystem::SubDirectoryFileSystem>(sd_ifs);
|
||||
R_TRY(subdir_fs->Initialize(save_dir_path));
|
||||
|
||||
std::shared_ptr<fssystem::DirectorySaveDataFileSystem> dirsave_ifs = std::make_shared<fssystem::DirectorySaveDataFileSystem>(std::move(subdir_fs));
|
||||
|
||||
/* Ensure correct directory savedata filesystem state. */
|
||||
R_TRY(dirsave_ifs->Initialize(true, true, true));
|
||||
|
||||
/* If it's the first time we're making the save, copy existing savedata over. */
|
||||
if (is_new_save) {
|
||||
/* TODO: Check error? */
|
||||
fs::DirectoryEntry work_entry;
|
||||
constexpr const fs::Path root_path = fs::MakeConstantPath("/");
|
||||
|
||||
u8 savedata_copy_buffer[2_KB];
|
||||
fssystem::CopyDirectoryRecursively(dirsave_ifs.get(), save_ifs.get(), root_path, root_path, std::addressof(work_entry), savedata_copy_buffer, sizeof(savedata_copy_buffer));
|
||||
}
|
||||
|
||||
/* Set output. */
|
||||
out.SetValue(MakeSharedFileSystem(std::move(dirsave_ifs), false), target_object_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FsMitmService::OpenBisStorage(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, u32 _bis_partition_id) {
|
||||
const ::FsBisPartitionId bis_partition_id = static_cast<::FsBisPartitionId>(_bis_partition_id);
|
||||
|
||||
/* Try to open a storage for the partition. */
|
||||
FsStorage bis_storage;
|
||||
R_TRY(fsOpenBisStorageFwd(m_forward_service.get(), std::addressof(bis_storage), bis_partition_id));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(bis_storage.s))};
|
||||
|
||||
const bool is_sysmodule = ncm::IsSystemProgramId(m_client_info.program_id);
|
||||
const bool is_hbl = m_client_info.override_status.IsHbl();
|
||||
const bool can_write_bis = is_sysmodule || (is_hbl && GetSettingsItemBooleanValue("atmosphere", "enable_hbl_bis_write"));
|
||||
|
||||
/* Allow HBL to write to boot1 (safe firm) + package2. */
|
||||
/* This is needed to not break compatibility with ChoiDujourNX, which does not check for write access before beginning an update. */
|
||||
/* TODO: get fixed so that this can be turned off without causing bricks :/ */
|
||||
const bool is_package2 = (FsBisPartitionId_BootConfigAndPackage2Part1 <= bis_partition_id && bis_partition_id <= FsBisPartitionId_BootConfigAndPackage2Part6);
|
||||
const bool is_boot1 = bis_partition_id == FsBisPartitionId_BootPartition2Root;
|
||||
const bool can_write_bis_for_choi_support = is_hbl && (is_package2 || is_boot1);
|
||||
|
||||
/* Set output storage. */
|
||||
if (bis_partition_id == FsBisPartitionId_BootPartition1Root) {
|
||||
if (IsBoot0CustomPublicKey(bis_storage)) {
|
||||
out.SetValue(MakeSharedStorage(std::make_shared<CustomPublicKeyBoot0Storage>(bis_storage, m_client_info, spl::GetSocType())), target_object_id);
|
||||
} else {
|
||||
out.SetValue(MakeSharedStorage(std::make_shared<Boot0Storage>(bis_storage, m_client_info)), target_object_id);
|
||||
}
|
||||
} else if (bis_partition_id == FsBisPartitionId_CalibrationBinary) {
|
||||
out.SetValue(MakeSharedStorage(std::make_shared<CalibrationBinaryStorage>(bis_storage, m_client_info)), target_object_id);
|
||||
} else {
|
||||
if (can_write_bis || can_write_bis_for_choi_support) {
|
||||
/* We can write, so create a writable storage. */
|
||||
out.SetValue(MakeSharedStorage(std::make_shared<RemoteStorage>(bis_storage)), target_object_id);
|
||||
} else {
|
||||
/* We can only read, so create a readable storage. */
|
||||
std::unique_ptr<ams::fs::IStorage> unique_bis = std::make_unique<RemoteStorage>(bis_storage);
|
||||
out.SetValue(MakeSharedStorage(std::make_shared<ReadOnlyStorageAdapter>(std::move(unique_bis))), target_object_id);
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FsMitmService::OpenDataStorageByCurrentProcess(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out) {
|
||||
/* Only mitm if we should override contents for the current process. */
|
||||
R_UNLESS(m_client_info.override_status.IsProgramSpecific(), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Only mitm if there is actually an override romfs. */
|
||||
R_UNLESS(mitm::fs::HasSdRomfsContent(m_client_info.program_id), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Try to open the process romfs. */
|
||||
FsStorage data_storage;
|
||||
R_TRY(fsOpenDataStorageByCurrentProcessFwd(m_forward_service.get(), std::addressof(data_storage)));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
|
||||
|
||||
/* Get a layered storage for the process romfs. */
|
||||
out.SetValue(MakeSharedStorage(GetLayeredRomfsStorage(m_client_info.program_id, data_storage, true)), target_object_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FsMitmService::OpenDataStorageByDataId(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, ncm::DataId _data_id, u8 storage_id) {
|
||||
/* Only mitm if we should override contents for the current process. */
|
||||
R_UNLESS(m_client_info.override_status.IsProgramSpecific(), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* TODO: Decide how to handle DataId vs ProgramId for this API. */
|
||||
const ncm::ProgramId data_id = {_data_id.value};
|
||||
|
||||
/* Only mitm if there is actually an override romfs. */
|
||||
R_UNLESS(mitm::fs::HasSdRomfsContent(data_id), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Try to open the data id. */
|
||||
FsStorage data_storage;
|
||||
R_TRY(fsOpenDataStorageByDataIdFwd(m_forward_service.get(), std::addressof(data_storage), static_cast<u64>(data_id), static_cast<NcmStorageId>(storage_id)));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
|
||||
|
||||
/* Get a layered storage for the data id. */
|
||||
out.SetValue(MakeSharedStorage(GetLayeredRomfsStorage(data_id, data_storage, false)), target_object_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FsMitmService::OpenDataStorageWithProgramIndex(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, u8 program_index) {
|
||||
/* Only mitm if we should override contents for the current process. */
|
||||
R_UNLESS(m_client_info.override_status.IsProgramSpecific(), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Get the relevant program id. */
|
||||
const ncm::ProgramId program_id = g_program_index_map_info_manager.GetProgramId(m_client_info.program_id, program_index);
|
||||
|
||||
/* If we don't know about the program or don't have content, forward. */
|
||||
R_UNLESS(program_id != ncm::InvalidProgramId, sm::mitm::ResultShouldForwardToSession());
|
||||
R_UNLESS(mitm::fs::HasSdRomfsContent(program_id), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Try to open the process romfs. */
|
||||
FsStorage data_storage;
|
||||
R_TRY(fsOpenDataStorageWithProgramIndexFwd(m_forward_service.get(), std::addressof(data_storage), program_index));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(data_storage.s))};
|
||||
|
||||
/* Get a layered storage for the process romfs. */
|
||||
out.SetValue(MakeSharedStorage(GetLayeredRomfsStorage(program_id, data_storage, true)), target_object_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FsMitmService::RegisterProgramIndexMapInfo(const sf::InBuffer &info_buffer, s32 info_count) {
|
||||
/* Try to register with FS. */
|
||||
R_TRY(fsRegisterProgramIndexMapInfoFwd(m_forward_service.get(), info_buffer.GetPointer(), info_buffer.GetSize(), info_count));
|
||||
|
||||
/* Register with ourselves. */
|
||||
R_ABORT_UNLESS(g_program_index_map_info_manager.Reset(reinterpret_cast<const fs::ProgramIndexMapInfo *>(info_buffer.GetPointer()), info_count));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/fssrv/fssrv_interface_adapters.hpp>
|
||||
|
||||
#define AMS_FS_MITM_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 7, Result, OpenFileSystemWithPatch, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, ncm::ProgramId program_id, u32 _filesystem_type), (out, program_id, _filesystem_type), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 8, Result, OpenFileSystemWithId, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, const fssrv::sf::Path &path, ncm::ProgramId program_id, u32 _filesystem_type), (out, path, program_id, _filesystem_type), hos::Version_2_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 18, Result, OpenSdCardFileSystem, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 51, Result, OpenSaveDataFileSystem, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, u8 space_id, const ams::fs::SaveDataAttribute &attribute), (out, space_id, attribute)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 12, Result, OpenBisStorage, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, u32 bis_partition_id), (out, bis_partition_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 200, Result, OpenDataStorageByCurrentProcess, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 202, Result, OpenDataStorageByDataId, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, ncm::DataId data_id, u8 storage_id), (out, data_id, storage_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 205, Result, OpenDataStorageWithProgramIndex, (sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, u8 program_index), (out, program_index), hos::Version_7_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 810, Result, RegisterProgramIndexMapInfo, (const sf::InBuffer &info_buffer, s32 info_count), (info_buffer, info_count), hos::Version_7_0_0)
|
||||
|
||||
|
||||
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::fs, IFsMitmInterface, AMS_FS_MITM_INTERFACE_INFO, 0x7DF34ED2)
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
class FsMitmService : public sf::MitmServiceImplBase {
|
||||
public:
|
||||
using MitmServiceImplBase::MitmServiceImplBase;
|
||||
public:
|
||||
static constexpr ALWAYS_INLINE bool ShouldMitmProgramId(const ncm::ProgramId program_id) {
|
||||
/* We want to mitm everything that isn't a system-module. */
|
||||
if (!ncm::IsSystemProgramId(program_id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* We want to mitm ns, to intercept SD card requests and program index map info registration. */
|
||||
if (program_id == ncm::SystemProgramId::Ns) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* We want to mitm settings, to intercept CAL0. */
|
||||
if (program_id == ncm::SystemProgramId::Settings) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* We want to mitm sdb, to support sd-romfs redirection of common system archives (like system font, etc). */
|
||||
/* NOTE: In 16.0.0+, this was moved to glue. */
|
||||
if (program_id == ncm::SystemProgramId::Sdb || program_id == ncm::SystemProgramId::Glue) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
|
||||
static std::atomic_bool has_launched_qlaunch = false;
|
||||
|
||||
/* TODO: intercepting everything seems to cause issues with sleep mode, for some reason. */
|
||||
/* Figure out why, and address it. */
|
||||
/* TODO: This may be because pre-rewrite code really mismanaged domain objects in a way that would cause bad things. */
|
||||
/* Need to verify if this is fixed now. */
|
||||
if (client_info.program_id == ncm::SystemAppletId::Qlaunch || client_info.program_id == ncm::SystemAppletId::MaintenanceMenu) {
|
||||
has_launched_qlaunch = true;
|
||||
}
|
||||
|
||||
return has_launched_qlaunch || ShouldMitmProgramId(client_info.program_id);
|
||||
}
|
||||
public:
|
||||
/* Overridden commands. */
|
||||
Result OpenFileSystemWithPatch(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, ncm::ProgramId program_id, u32 _filesystem_type);
|
||||
Result OpenFileSystemWithId(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, const fssrv::sf::Path &path, ncm::ProgramId program_id, u32 _filesystem_type);
|
||||
Result OpenSdCardFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out);
|
||||
Result OpenSaveDataFileSystem(sf::Out<sf::SharedPointer<ams::fssrv::sf::IFileSystem>> out, u8 space_id, const ams::fs::SaveDataAttribute &attribute);
|
||||
Result OpenBisStorage(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, u32 bis_partition_id);
|
||||
Result OpenDataStorageByCurrentProcess(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out);
|
||||
Result OpenDataStorageByDataId(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, ncm::DataId data_id, u8 storage_id);
|
||||
Result OpenDataStorageWithProgramIndex(sf::Out<sf::SharedPointer<ams::fssrv::sf::IStorage>> out, u8 program_index);
|
||||
Result RegisterProgramIndexMapInfo(const sf::InBuffer &info_buffer, s32 info_count);
|
||||
};
|
||||
static_assert(IsIFsMitmInterface<FsMitmService>);
|
||||
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "fs_shim.h"
|
||||
|
||||
/* Missing fsp-srv commands. */
|
||||
static Result _fsOpenSession(Service *s, Service* out, u32 cmd_id) {
|
||||
return serviceDispatch(s, cmd_id,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = out,
|
||||
);
|
||||
}
|
||||
|
||||
Result fsOpenSdCardFileSystemFwd(Service* s, FsFileSystem* out) {
|
||||
return _fsOpenSession(s, &out->s, 18);
|
||||
}
|
||||
|
||||
Result fsOpenBisStorageFwd(Service* s, FsStorage* out, FsBisPartitionId partition_id) {
|
||||
const u32 tmp = partition_id;
|
||||
return serviceDispatchIn(s, 12, tmp,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = &out->s,
|
||||
);
|
||||
}
|
||||
|
||||
Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out) {
|
||||
return _fsOpenSession(s, &out->s, 200);
|
||||
}
|
||||
|
||||
Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorage* out, u64 data_id, NcmStorageId storage_id) {
|
||||
const struct {
|
||||
u8 storage_id;
|
||||
u64 data_id;
|
||||
} in = { storage_id, data_id };
|
||||
|
||||
return serviceDispatchIn(s, 202, in,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = &out->s,
|
||||
);
|
||||
}
|
||||
|
||||
Result fsOpenDataStorageWithProgramIndexFwd(Service* s, FsStorage* out, u8 program_index) {
|
||||
return serviceDispatchIn(s, 205, program_index,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = &out->s,
|
||||
);
|
||||
}
|
||||
|
||||
Result fsRegisterProgramIndexMapInfoFwd(Service* s, const void *buf, size_t buf_size, s32 count) {
|
||||
return serviceDispatchIn(s, 810, count,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
|
||||
.buffers = { { buf, buf_size } },
|
||||
);
|
||||
}
|
||||
|
||||
Result fsOpenSaveDataFileSystemFwd(Service* s, FsFileSystem* out, FsSaveDataSpaceId save_data_space_id, const FsSaveDataAttribute *attr) {
|
||||
const struct {
|
||||
u8 save_data_space_id;
|
||||
u8 pad[7];
|
||||
FsSaveDataAttribute attr;
|
||||
} in = { (u8)save_data_space_id, {0}, *attr };
|
||||
|
||||
return serviceDispatchIn(s, 51, in,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = &out->s,
|
||||
);
|
||||
}
|
||||
|
||||
Result fsOpenFileSystemWithPatchFwd(Service* s, FsFileSystem* out, u64 id, FsFileSystemType fsType) {
|
||||
const struct {
|
||||
u32 fsType;
|
||||
u64 id;
|
||||
} in = { fsType, id };
|
||||
|
||||
return serviceDispatchIn(s, 7, in,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = &out->s
|
||||
);
|
||||
}
|
||||
|
||||
Result fsOpenFileSystemWithIdFwd(Service* s, FsFileSystem* out, u64 id, FsFileSystemType fsType, const char* contentPath) {
|
||||
const struct {
|
||||
u32 fsType;
|
||||
u64 id;
|
||||
} in = { fsType, id };
|
||||
|
||||
return serviceDispatchIn(s, 8, in,
|
||||
.buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In },
|
||||
.buffers = { { contentPath, FS_MAX_PATH } },
|
||||
.out_num_objects = 1,
|
||||
.out_objects = &out->s
|
||||
);
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/**
|
||||
* @file fs_shim.h
|
||||
* @brief Filesystem Services (fs) IPC wrapper for fs.mitm.
|
||||
* @author SciresM
|
||||
* @copyright libnx Authors
|
||||
*/
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Missing fsp-srv commands. */
|
||||
Result fsOpenSdCardFileSystemFwd(Service* s, FsFileSystem* out);
|
||||
Result fsOpenBisStorageFwd(Service* s, FsStorage* out, FsBisPartitionId partition_id);
|
||||
Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out);
|
||||
Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorage* out, u64 data_id, NcmStorageId storage_id);
|
||||
Result fsOpenDataStorageWithProgramIndexFwd(Service* s, FsStorage* out, u8 program_index);
|
||||
|
||||
Result fsRegisterProgramIndexMapInfoFwd(Service* s, const void *buf, size_t buf_size, s32 count);
|
||||
|
||||
Result fsOpenSaveDataFileSystemFwd(Service* s, FsFileSystem* out, FsSaveDataSpaceId save_data_space_id, const FsSaveDataAttribute *attr);
|
||||
|
||||
Result fsOpenFileSystemWithPatchFwd(Service* s, FsFileSystem* out, u64 id, FsFileSystemType fsType);
|
||||
Result fsOpenFileSystemWithIdFwd(Service* s, FsFileSystem* out, u64 id, FsFileSystemType fsType, const char* contentPath);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,267 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsmitm_boot0storage.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
using namespace ams::fs;
|
||||
|
||||
namespace {
|
||||
|
||||
constinit os::SdkMutex g_boot0_access_mutex;
|
||||
constinit bool g_custom_public_key = false;
|
||||
constinit u8 g_boot0_bct_buffer[Boot0Storage::BctEndOffset];
|
||||
|
||||
/* Recognize special public key (https://gist.github.com/SciresM/16b63ac1d80494522bdba2c57995257c). */
|
||||
/* P = 19 */
|
||||
/* Q = 1696986749729493925354392349339746171297507422986462747526968361144447230710192316397327889522451749459854070558277878297255552508603806832852079596337539247651161831569525505882103311631577368514276343192042634740927726070847704397913856975832811679847928433261678072951551065705680482548543833651752439700272736498378724153330763357721354498194000536297732323628263256733931353143625854828275237159155585342783077681713929284136658773985266864804093157854331138230313706015557050002740810464618031715670281442110238274404626065924786185264268216336867948322976979393032640085259926883014490947373494538254895109731 */
|
||||
/* N = 0xFF696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696959 */
|
||||
/* E = 0x10001 */
|
||||
/* D = 6512128715229088976470211610075969347035078304643231077895577077900787352712063823560162578441773733649014439616165727455431015055675770987914713980812453585413988983206576233689754710500864883529402371292948326392791238474661859182717295176679567362482790015587820446999760239570255254879359445627372805817473978644067558931078225451477635089763009580492462097185005355990612929951162366081631888011031830742459571000203341001926135389508196521518687349554188686396554248868403128728646457247197407637887195043486221295751496667162366700477934591694110831002874992896076061627516220934290742867397720103040314639313 */
|
||||
|
||||
constexpr const u8 CustomPublicKey[0x100] = {
|
||||
0x59, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
|
||||
0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0xFF,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
bool Boot0Storage::CanModifyBctPublicKey() {
|
||||
if (exosphere::IsRcmBugPatched()) {
|
||||
/* RCM bug patched. */
|
||||
/* Only allow NS to update the BCT pubks. */
|
||||
/* AutoRCM on a patched unit will cause a brick, so homebrew should NOT be allowed to write. */
|
||||
return m_client_info.program_id == ncm::SystemProgramId::Ns;
|
||||
} else {
|
||||
/* RCM bug unpatched. */
|
||||
/* Allow homebrew but not NS to update the BCT pubks. */
|
||||
return m_client_info.override_status.IsHbl();
|
||||
}
|
||||
}
|
||||
|
||||
Result Boot0Storage::Read(s64 offset, void *_buffer, size_t size) {
|
||||
std::scoped_lock lk{g_boot0_access_mutex};
|
||||
AMS_ABORT_UNLESS(!g_custom_public_key);
|
||||
|
||||
/* Check if we have nothing to do. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
R_RETURN(Base::Read(offset, _buffer, size));
|
||||
}
|
||||
|
||||
Result Boot0Storage::Write(s64 offset, const void *_buffer, size_t size) {
|
||||
std::scoped_lock lk{g_boot0_access_mutex};
|
||||
AMS_ABORT_UNLESS(!g_custom_public_key);
|
||||
|
||||
const u8 *buffer = static_cast<const u8 *>(_buffer);
|
||||
|
||||
/* Check if we have nothing to do. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Protect the EKS region from writes. */
|
||||
if (offset <= EksStart) {
|
||||
if (offset + size < EksStart) {
|
||||
/* Fall through, no need to do anything here. */
|
||||
} else {
|
||||
if (offset + size > EksEnd) {
|
||||
/* Perform portion of write falling past end of keyblobs. */
|
||||
const s64 diff = EksEnd - offset;
|
||||
R_TRY(Base::Write(EksEnd, buffer + diff, size - diff));
|
||||
}
|
||||
/* Adjust size to avoid writing end of data. */
|
||||
size = EksStart - offset;
|
||||
}
|
||||
} else if (offset < EksEnd) {
|
||||
/* Ignore writes falling strictly within the region. */
|
||||
R_SUCCEED_IF(offset + size <= EksEnd);
|
||||
|
||||
/* Only write past the end of the region. */
|
||||
const s64 diff = EksEnd - offset;
|
||||
buffer += diff;
|
||||
size -= diff;
|
||||
offset = EksEnd;
|
||||
}
|
||||
|
||||
/* If we have nothing to write, succeed immediately. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* We want to protect AutoRCM from NS on ipatched units. If we can modify bct pubks or we're not touching any of them, proceed. */
|
||||
if (this->CanModifyBctPublicKey() || offset >= BctEndOffset || (util::AlignUp(offset, BctSize) >= BctEndOffset && (offset % BctSize) >= BctPubkEnd)) {
|
||||
R_RETURN(Base::Write(offset, buffer, size));
|
||||
}
|
||||
|
||||
/* Handle any data written past the end of the pubk region. */
|
||||
if (offset + size > BctEndOffset) {
|
||||
const u64 diff = BctEndOffset - offset;
|
||||
R_TRY(Base::Write(BctEndOffset, buffer + diff, size - diff));
|
||||
size = diff;
|
||||
}
|
||||
|
||||
/* Read in the current BCT region. */
|
||||
R_TRY(Base::Read(0, g_boot0_bct_buffer, BctEndOffset));
|
||||
|
||||
/* Update the bct buffer. */
|
||||
for (u64 cur_offset = offset; cur_offset < BctEndOffset && cur_offset < offset + size; cur_offset++) {
|
||||
const u64 cur_bct_relative_ofs = cur_offset % BctSize;
|
||||
if (cur_bct_relative_ofs < BctPubkStart || BctPubkEnd <= cur_bct_relative_ofs) {
|
||||
g_boot0_bct_buffer[cur_offset] = buffer[cur_offset - offset];
|
||||
}
|
||||
}
|
||||
|
||||
R_RETURN(Base::Write(0, g_boot0_bct_buffer, BctEndOffset));
|
||||
}
|
||||
|
||||
CustomPublicKeyBoot0Storage::CustomPublicKeyBoot0Storage(FsStorage &s, const sm::MitmProcessInfo &c, spl::SocType soc) : Base(s), m_client_info(c), m_soc_type(soc) {
|
||||
std::scoped_lock lk{g_boot0_access_mutex};
|
||||
|
||||
/* We're custom public key. */
|
||||
g_custom_public_key = true;
|
||||
}
|
||||
|
||||
Result CustomPublicKeyBoot0Storage::Read(s64 offset, void *_buffer, size_t size) {
|
||||
std::scoped_lock lk{g_boot0_access_mutex};
|
||||
AMS_ABORT_UNLESS(g_custom_public_key);
|
||||
|
||||
/* Check if we have nothing to do. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
u8 *buffer = static_cast<u8 *>(_buffer);
|
||||
|
||||
/* Check if we're reading the first BCTs for NS. */
|
||||
/* If we are, we want to lie about the contents of BCT0/1 so that they validate. */
|
||||
if (offset < 0x8000 && m_client_info.program_id == ncm::SystemProgramId::Ns) {
|
||||
R_TRY(Base::Read(0, g_boot0_bct_buffer, Boot0Storage::BctEndOffset));
|
||||
|
||||
/* Determine the readable size. */
|
||||
const size_t readable_bct01_size = std::min<size_t>(0x8000, offset + size) - offset;
|
||||
std::memcpy(buffer, g_boot0_bct_buffer + 0x8000 + offset, readable_bct01_size);
|
||||
|
||||
/* Advance. */
|
||||
buffer += readable_bct01_size;
|
||||
offset += readable_bct01_size;
|
||||
size -= readable_bct01_size;
|
||||
}
|
||||
|
||||
/* Check if we have nothing to do. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Perform whatever remains of the read. */
|
||||
R_RETURN(Base::Read(offset, buffer, size));
|
||||
}
|
||||
|
||||
Result CustomPublicKeyBoot0Storage::Write(s64 offset, const void *_buffer, size_t size) {
|
||||
std::scoped_lock lk{g_boot0_access_mutex};
|
||||
AMS_ABORT_UNLESS(g_custom_public_key);
|
||||
|
||||
const u8 *buffer = static_cast<const u8 *>(_buffer);
|
||||
|
||||
/* Check if we have nothing to do. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Drop writes to the first BCTs. */
|
||||
if (offset < 0x8000) {
|
||||
/* Determine the writable size. */
|
||||
const size_t writable_bct01_size = std::min<size_t>(0x8000, offset + size) - offset;
|
||||
|
||||
/* Advance. */
|
||||
buffer += writable_bct01_size;
|
||||
offset += writable_bct01_size;
|
||||
size -= writable_bct01_size;
|
||||
}
|
||||
|
||||
/* Check if we have nothing to do. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Similarly, we want to drop writes to the end of boot0, where custom bootloader lives. */
|
||||
R_SUCCEED_IF(offset >= 0x380000);
|
||||
if (offset + size >= 0x380000) {
|
||||
size = 0x380000 - offset;
|
||||
}
|
||||
|
||||
/* On erista, we want to protect the EKS region. */
|
||||
if (m_soc_type == spl::SocType_Erista) {
|
||||
if (offset <= Boot0Storage::EksStart) {
|
||||
if (offset + size < Boot0Storage::EksStart) {
|
||||
/* Fall through, no need to do anything here. */
|
||||
} else {
|
||||
if (offset + size > Boot0Storage::EksEnd) {
|
||||
/* Perform portion of write falling past end of keyblobs. */
|
||||
const s64 diff = Boot0Storage::EksEnd - offset;
|
||||
R_TRY(Base::Write(Boot0Storage::EksEnd, buffer + diff, size - diff));
|
||||
}
|
||||
/* Adjust size to avoid writing end of data. */
|
||||
size = Boot0Storage::EksStart - offset;
|
||||
}
|
||||
} else if (offset < Boot0Storage::EksEnd) {
|
||||
/* Ignore writes falling strictly within the region. */
|
||||
R_SUCCEED_IF(offset + size <= Boot0Storage::EksEnd);
|
||||
|
||||
/* Only write past the end of the region. */
|
||||
const s64 diff = Boot0Storage::EksEnd - offset;
|
||||
buffer += diff;
|
||||
size -= diff;
|
||||
offset = Boot0Storage::EksEnd;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we have nothing to write, succeed immediately. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Perform whatever remains of the write. */
|
||||
R_RETURN(Base::Write(offset, buffer, size));
|
||||
}
|
||||
|
||||
bool DetectBoot0CustomPublicKey(::FsStorage &storage) {
|
||||
/* Determine public key offset. */
|
||||
const size_t bct_pubk_offset = spl::GetSocType() == spl::SocType_Mariko ? 0x10 : 0x210;
|
||||
|
||||
u8 work_buffer[0x400];
|
||||
|
||||
/* Read BCT-Normal-Main. */
|
||||
R_ABORT_UNLESS(::fsStorageRead(std::addressof(storage), 0, work_buffer, sizeof(work_buffer)));
|
||||
|
||||
/* Check for custom public key. */
|
||||
if (std::memcmp(work_buffer + bct_pubk_offset, CustomPublicKey, sizeof(CustomPublicKey)) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Read BCT-Safe-Main. */
|
||||
R_ABORT_UNLESS(::fsStorageRead(std::addressof(storage), 0x4000, work_buffer, sizeof(work_buffer)));
|
||||
|
||||
/* Check for custom public key. */
|
||||
if (std::memcmp(work_buffer + bct_pubk_offset, CustomPublicKey, sizeof(CustomPublicKey)) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
template<class Base, size_t SectorSize>
|
||||
class SectoredStorageAdapter : public Base {
|
||||
static_assert(std::is_base_of<ams::fs::IStorage, Base>::value);
|
||||
private:
|
||||
u8 m_sector_buf[SectorSize];
|
||||
public:
|
||||
/* Inherit constructors. */
|
||||
using Base::Base;
|
||||
public:
|
||||
virtual Result Read(s64 offset, void *_buffer, size_t size) override {
|
||||
u8 *buffer = static_cast<u8 *>(_buffer);
|
||||
|
||||
const s64 seek = util::AlignDown(offset, SectorSize);
|
||||
const s64 sector_ofs = offset - seek;
|
||||
|
||||
/* Check if we have nothing to do. */
|
||||
if (size == 0) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Fast case. */
|
||||
if (sector_ofs == 0 && util::IsAligned(size, SectorSize)) {
|
||||
R_RETURN(Base::Read(offset, buffer, size));
|
||||
}
|
||||
|
||||
R_TRY(Base::Read(seek, m_sector_buf, SectorSize));
|
||||
|
||||
if (size + sector_ofs <= SectorSize) {
|
||||
/* Staying within the sector. */
|
||||
std::memcpy(buffer, m_sector_buf + sector_ofs, size);
|
||||
} else {
|
||||
/* Leaving the sector. */
|
||||
const size_t size_in_sector = SectorSize - sector_ofs;
|
||||
std::memcpy(buffer, m_sector_buf + sector_ofs, size_in_sector);
|
||||
size -= size_in_sector;
|
||||
|
||||
/* Read as many guaranteed aligned sectors as we can. */
|
||||
const size_t aligned_remaining_size = util::AlignDown(size, SectorSize);
|
||||
if (aligned_remaining_size) {
|
||||
R_TRY(Base::Read(offset + size_in_sector, buffer + size_in_sector, aligned_remaining_size));
|
||||
size -= aligned_remaining_size;
|
||||
}
|
||||
|
||||
/* Read any leftover data. */
|
||||
if (size) {
|
||||
R_TRY(Base::Read(offset + size_in_sector + aligned_remaining_size, m_sector_buf, SectorSize));
|
||||
std::memcpy(buffer + size_in_sector + aligned_remaining_size, m_sector_buf, size);
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *_buffer, size_t size) override {
|
||||
const u8 *buffer = static_cast<const u8 *>(_buffer);
|
||||
|
||||
const s64 seek = util::AlignDown(offset, SectorSize);
|
||||
const s64 sector_ofs = offset - seek;
|
||||
|
||||
/* Check if we have nothing to do. */
|
||||
if (size == 0) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Fast case. */
|
||||
if (sector_ofs == 0 && util::IsAligned(size, SectorSize)) {
|
||||
R_RETURN(Base::Write(offset, buffer, size));
|
||||
}
|
||||
|
||||
/* Load existing sector data. */
|
||||
R_TRY(Base::Read(seek, m_sector_buf, SectorSize));
|
||||
|
||||
if (size + sector_ofs <= SectorSize) {
|
||||
/* Staying within the sector. */
|
||||
std::memcpy(m_sector_buf + sector_ofs, buffer, size);
|
||||
R_TRY(Base::Write(seek, m_sector_buf, SectorSize));
|
||||
} else {
|
||||
/* Leaving the sector. */
|
||||
const size_t size_in_sector = SectorSize - sector_ofs;
|
||||
std::memcpy(m_sector_buf + sector_ofs, buffer, size_in_sector);
|
||||
R_TRY(Base::Write(seek, m_sector_buf, SectorSize));
|
||||
size -= size_in_sector;
|
||||
|
||||
/* Write as many guaranteed aligned sectors as we can. */
|
||||
const size_t aligned_remaining_size = util::AlignDown(size, SectorSize);
|
||||
if (aligned_remaining_size) {
|
||||
R_TRY(Base::Write(offset + size_in_sector, buffer + size_in_sector, aligned_remaining_size));
|
||||
size -= aligned_remaining_size;
|
||||
}
|
||||
|
||||
/* Write any leftover data. */
|
||||
if (size) {
|
||||
R_TRY(Base::Read(offset + size_in_sector + aligned_remaining_size, m_sector_buf, SectorSize));
|
||||
std::memcpy(m_sector_buf, buffer + size_in_sector + aligned_remaining_size, size);
|
||||
R_TRY(Base::Write(offset + size_in_sector + aligned_remaining_size, m_sector_buf, SectorSize));
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
/* Represents an RCM-preserving BOOT0 partition. */
|
||||
class Boot0Storage : public SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200> {
|
||||
public:
|
||||
using Base = SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200>;
|
||||
|
||||
static constexpr s64 BctEndOffset = 0xFC000;
|
||||
static constexpr s64 BctSize = static_cast<s64>(ams::updater::BctSize);
|
||||
static constexpr s64 BctPubkStart = 0x210;
|
||||
static constexpr s64 BctPubkSize = 0x100;
|
||||
static constexpr s64 BctPubkEnd = BctPubkStart + BctPubkSize;
|
||||
|
||||
static constexpr s64 EksStart = 0x180000;
|
||||
static constexpr s64 EksSize = static_cast<s64>(ams::updater::EksSize);
|
||||
static constexpr s64 EksEnd = EksStart + EksSize;
|
||||
private:
|
||||
sm::MitmProcessInfo m_client_info;
|
||||
private:
|
||||
bool CanModifyBctPublicKey();
|
||||
public:
|
||||
Boot0Storage(FsStorage &s, const sm::MitmProcessInfo &c) : Base(s), m_client_info(c) { /* ... */ }
|
||||
public:
|
||||
virtual Result Read(s64 offset, void *_buffer, size_t size) override;
|
||||
virtual Result Write(s64 offset, const void *_buffer, size_t size) override;
|
||||
};
|
||||
|
||||
class CustomPublicKeyBoot0Storage : public SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200> {
|
||||
public:
|
||||
using Base = SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200>;
|
||||
private:
|
||||
sm::MitmProcessInfo m_client_info;
|
||||
spl::SocType m_soc_type;
|
||||
public:
|
||||
CustomPublicKeyBoot0Storage(FsStorage &s, const sm::MitmProcessInfo &c, spl::SocType soc);
|
||||
public:
|
||||
virtual Result Read(s64 offset, void *_buffer, size_t size) override;
|
||||
virtual Result Write(s64 offset, const void *_buffer, size_t size) override;
|
||||
};
|
||||
|
||||
bool DetectBoot0CustomPublicKey(::FsStorage &storage);
|
||||
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsmitm_calibration_binary_storage.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
using namespace ams::fs;
|
||||
|
||||
namespace {
|
||||
|
||||
constinit os::SdkMutex g_cal0_access_mutex;
|
||||
|
||||
}
|
||||
|
||||
Result CalibrationBinaryStorage::Read(s64 offset, void *_buffer, size_t size) {
|
||||
/* Acquire exclusive calibration binary access. */
|
||||
std::scoped_lock lk(g_cal0_access_mutex);
|
||||
|
||||
/* Get u8 buffer. */
|
||||
u8 *buffer = static_cast<u8 *>(_buffer);
|
||||
|
||||
/* Succeed on zero-size. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Handle the blank region. */
|
||||
if (m_read_blank) {
|
||||
if (BlankStartOffset <= offset && offset < BlankEndOffset) {
|
||||
const size_t blank_size = std::min(size, static_cast<size_t>(BlankEndOffset - offset));
|
||||
mitm::ReadFromBlankCalibrationBinary(offset, buffer, blank_size);
|
||||
size -= blank_size;
|
||||
buffer += blank_size;
|
||||
offset += blank_size;
|
||||
}
|
||||
}
|
||||
|
||||
/* Succeed if we're done. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Handle any in-between data. */
|
||||
if (BlankEndOffset <= offset && offset < FakeSecureStartOffset) {
|
||||
const size_t mid_size = std::min(size, static_cast<size_t>(FakeSecureStartOffset - offset));
|
||||
R_TRY(Base::Read(offset, buffer, mid_size));
|
||||
size -= mid_size;
|
||||
buffer += mid_size;
|
||||
offset += mid_size;
|
||||
}
|
||||
|
||||
/* Succeed if we're done. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Handle the secure region. */
|
||||
if (FakeSecureStartOffset <= offset && offset < FakeSecureEndOffset) {
|
||||
const size_t fake_size = std::min(size, static_cast<size_t>(FakeSecureEndOffset - offset));
|
||||
mitm::ReadFromFakeSecureBackupStorage(offset, buffer, fake_size);
|
||||
size -= fake_size;
|
||||
buffer += fake_size;
|
||||
offset += fake_size;
|
||||
}
|
||||
|
||||
/* Succeed if we're done. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Handle any remaining data. */
|
||||
R_RETURN(Base::Read(offset, buffer, size));
|
||||
}
|
||||
|
||||
Result CalibrationBinaryStorage::Write(s64 offset, const void *_buffer, size_t size) {
|
||||
/* Acquire exclusive calibration binary access. */
|
||||
std::scoped_lock lk(g_cal0_access_mutex);
|
||||
|
||||
/* Get const u8 buffer. */
|
||||
const u8 *buffer = static_cast<const u8 *>(_buffer);
|
||||
|
||||
/* Succeed on zero-size. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Only allow writes if we should. */
|
||||
R_UNLESS(m_allow_writes, fs::ResultUnsupportedOperation());
|
||||
|
||||
/* Handle the blank region. */
|
||||
if (m_read_blank) {
|
||||
if (BlankStartOffset <= offset && offset < BlankEndOffset) {
|
||||
const size_t blank_size = std::min(size, static_cast<size_t>(BlankEndOffset - offset));
|
||||
mitm::WriteToBlankCalibrationBinary(offset, buffer, blank_size);
|
||||
size -= blank_size;
|
||||
buffer += blank_size;
|
||||
offset += blank_size;
|
||||
}
|
||||
}
|
||||
|
||||
/* Succeed if we're done. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Handle any in-between data. */
|
||||
if (BlankEndOffset <= offset && offset < FakeSecureStartOffset) {
|
||||
const size_t mid_size = std::min(size, static_cast<size_t>(FakeSecureStartOffset - offset));
|
||||
R_TRY(Base::Write(offset, buffer, mid_size));
|
||||
size -= mid_size;
|
||||
buffer += mid_size;
|
||||
offset += mid_size;
|
||||
}
|
||||
|
||||
/* Succeed if we're done. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Handle the secure region. */
|
||||
if (FakeSecureStartOffset <= offset && offset < FakeSecureEndOffset) {
|
||||
const size_t fake_size = std::min(size, static_cast<size_t>(FakeSecureEndOffset - offset));
|
||||
mitm::WriteToFakeSecureBackupStorage(offset, buffer, fake_size);
|
||||
size -= fake_size;
|
||||
buffer += fake_size;
|
||||
offset += fake_size;
|
||||
}
|
||||
|
||||
/* Succeed if we're done. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Handle any remaining data. */
|
||||
R_RETURN(Base::Write(offset, buffer, size));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsmitm_boot0storage.hpp"
|
||||
#include "../amsmitm_prodinfo_utils.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
/* Represents a protected calibration binary partition. */
|
||||
class CalibrationBinaryStorage : public SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200> {
|
||||
public:
|
||||
using Base = SectoredStorageAdapter<ams::fs::RemoteStorage, 0x200>;
|
||||
|
||||
static constexpr s64 BlankStartOffset = 0x0;
|
||||
static constexpr s64 BlankSize = static_cast<s64>(CalibrationBinarySize);
|
||||
static constexpr s64 BlankEndOffset = BlankStartOffset + BlankSize;
|
||||
|
||||
static constexpr s64 FakeSecureStartOffset = SecureCalibrationInfoBackupOffset;
|
||||
static constexpr s64 FakeSecureSize = static_cast<s64>(SecureCalibrationBinaryBackupSize);
|
||||
static constexpr s64 FakeSecureEndOffset = FakeSecureStartOffset + FakeSecureSize;
|
||||
private:
|
||||
sm::MitmProcessInfo m_client_info;
|
||||
bool m_read_blank;
|
||||
bool m_allow_writes;
|
||||
public:
|
||||
CalibrationBinaryStorage(FsStorage &s, const sm::MitmProcessInfo &c)
|
||||
: Base(s), m_client_info(c),
|
||||
m_read_blank(mitm::ShouldReadBlankCalibrationBinary()),
|
||||
m_allow_writes(mitm::IsWriteToCalibrationBinaryAllowed())
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
public:
|
||||
virtual Result Read(s64 offset, void *_buffer, size_t size) override;
|
||||
virtual Result Write(s64 offset, const void *_buffer, size_t size) override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,432 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "../amsmitm_fs_utils.hpp"
|
||||
#include "fsmitm_layered_romfs_storage.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
namespace {
|
||||
|
||||
constinit os::SdkMutex g_mq_lock;
|
||||
constinit bool g_started_req_thread;
|
||||
constinit uintptr_t g_mq_storage[2];
|
||||
os::MessageQueue g_req_mq(g_mq_storage + 0, 1);
|
||||
os::MessageQueue g_ack_mq(g_mq_storage + 1, 1);
|
||||
|
||||
class LayeredRomfsStorageHolder : public util::IntrusiveRedBlackTreeBaseNode<LayeredRomfsStorageHolder> {
|
||||
public:
|
||||
using RedBlackKeyType = u64;
|
||||
private:
|
||||
LayeredRomfsStorageImpl *m_impl;
|
||||
u32 m_reference_count;
|
||||
bool m_second_chance;
|
||||
bool m_process_romfs;
|
||||
public:
|
||||
LayeredRomfsStorageHolder(LayeredRomfsStorageImpl *impl, bool process_rom) : m_impl(impl), m_reference_count(1), m_second_chance(true), m_process_romfs(process_rom) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
~LayeredRomfsStorageHolder() {
|
||||
delete m_impl;
|
||||
}
|
||||
|
||||
constexpr LayeredRomfsStorageImpl *GetImpl() const { return m_impl; }
|
||||
constexpr ncm::ProgramId GetProgramId() const { return m_impl->GetProgramId(); }
|
||||
constexpr u32 GetReferenceCount() const { return m_reference_count; }
|
||||
|
||||
void OpenReferenceImpl() { ++m_reference_count; }
|
||||
void CloseReferenceImpl() { --m_reference_count; }
|
||||
|
||||
bool GetSecondChanceImpl() const { return m_second_chance; }
|
||||
void SetSecondChanceImpl(bool sc) { m_second_chance = sc; }
|
||||
|
||||
bool IsProcessRomfs() const { return m_process_romfs; }
|
||||
|
||||
static constexpr ALWAYS_INLINE int Compare(const RedBlackKeyType &lval, const LayeredRomfsStorageHolder &rhs) {
|
||||
const auto rval = rhs.GetProgramId().value;
|
||||
|
||||
if (lval < rval) {
|
||||
return -1;
|
||||
} else if (lval == rval) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr ALWAYS_INLINE int Compare(const LayeredRomfsStorageHolder &lhs, const LayeredRomfsStorageHolder &rhs) {
|
||||
return Compare(lhs.GetProgramId().value, rhs);
|
||||
}
|
||||
};
|
||||
|
||||
using LayeredRomfsStorageSet = typename util::IntrusiveRedBlackTreeBaseTraits<LayeredRomfsStorageHolder>::TreeType<LayeredRomfsStorageHolder>;
|
||||
|
||||
constinit os::SdkRecursiveMutex g_storage_set_mutex;
|
||||
constinit LayeredRomfsStorageSet g_storage_set;
|
||||
constinit os::SdkMutex g_initialization_mutex;
|
||||
|
||||
void OpenReference(LayeredRomfsStorageImpl *impl) {
|
||||
std::scoped_lock lk(g_storage_set_mutex);
|
||||
|
||||
auto it = g_storage_set.find_key(impl->GetProgramId().value);
|
||||
AMS_ABORT_UNLESS(it != g_storage_set.end());
|
||||
|
||||
it->OpenReferenceImpl();
|
||||
}
|
||||
|
||||
void CloseReference(LayeredRomfsStorageImpl *impl) {
|
||||
std::scoped_lock lk(g_storage_set_mutex);
|
||||
|
||||
auto it = g_storage_set.find_key(impl->GetProgramId().value);
|
||||
AMS_ABORT_UNLESS(it != g_storage_set.end());
|
||||
|
||||
AMS_ABORT_UNLESS(it->GetReferenceCount() > 0);
|
||||
it->CloseReferenceImpl();
|
||||
}
|
||||
|
||||
void RomfsInitializerThreadFunction(void *) {
|
||||
while (true) {
|
||||
uintptr_t storage_uptr = 0;
|
||||
|
||||
g_req_mq.Receive(std::addressof(storage_uptr));
|
||||
auto *impl = reinterpret_cast<LayeredRomfsStorageImpl *>(storage_uptr);
|
||||
g_ack_mq.Send(storage_uptr);
|
||||
|
||||
std::scoped_lock lk(g_initialization_mutex);
|
||||
|
||||
impl->InitializeImpl();
|
||||
|
||||
/* Close the initial reference. */
|
||||
CloseReference(impl);
|
||||
}
|
||||
}
|
||||
|
||||
void RomfsFinalizerThreadFunction(void *) {
|
||||
while (true) {
|
||||
{
|
||||
std::scoped_lock lk(g_storage_set_mutex);
|
||||
|
||||
auto it = g_storage_set.begin();
|
||||
while (it != g_storage_set.end()) {
|
||||
if (it->GetReferenceCount() > 0) {
|
||||
it->SetSecondChanceImpl(true);
|
||||
++it;
|
||||
} else if (it->GetSecondChanceImpl()) {
|
||||
it->SetSecondChanceImpl(false);
|
||||
++it;
|
||||
} else {
|
||||
auto *holder = std::addressof(*it);
|
||||
it = g_storage_set.erase(it);
|
||||
delete holder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
os::SleepThread(TimeSpan::FromMilliSeconds(500));
|
||||
}
|
||||
}
|
||||
|
||||
constexpr size_t RomfsInitializerThreadStackSize = 0x8000;
|
||||
os::ThreadType g_romfs_initializer_thread;
|
||||
os::ThreadType g_romfs_finalizer_thread;
|
||||
alignas(os::ThreadStackAlignment) u8 g_romfs_initializer_thread_stack[RomfsInitializerThreadStackSize];
|
||||
alignas(os::ThreadStackAlignment) u8 g_romfs_finalizer_thread_stack[os::MemoryPageSize];
|
||||
|
||||
void RequestInitializeStorage(uintptr_t storage_uptr) {
|
||||
std::scoped_lock lk(g_mq_lock);
|
||||
|
||||
if (AMS_UNLIKELY(!g_started_req_thread)) {
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_romfs_initializer_thread), RomfsInitializerThreadFunction, nullptr, g_romfs_initializer_thread_stack, sizeof(g_romfs_initializer_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(mitm_fs, RomFileSystemInitializeThread)));
|
||||
os::SetThreadNamePointer(std::addressof(g_romfs_initializer_thread), AMS_GET_SYSTEM_THREAD_NAME(mitm_fs, RomFileSystemInitializeThread));
|
||||
os::StartThread(std::addressof(g_romfs_initializer_thread));
|
||||
|
||||
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_romfs_finalizer_thread), RomfsFinalizerThreadFunction, nullptr, g_romfs_finalizer_thread_stack, sizeof(g_romfs_finalizer_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(mitm_fs, RomFileSystemInitializeThread)));
|
||||
os::SetThreadNamePointer(std::addressof(g_romfs_finalizer_thread), AMS_GET_SYSTEM_THREAD_NAME(mitm_fs, RomFileSystemFinalizeThread));
|
||||
os::StartThread(std::addressof(g_romfs_finalizer_thread));
|
||||
|
||||
g_started_req_thread = true;
|
||||
}
|
||||
|
||||
g_req_mq.Send(storage_uptr);
|
||||
uintptr_t ack = 0;
|
||||
g_ack_mq.Receive(std::addressof(ack));
|
||||
AMS_ABORT_UNLESS(ack == storage_uptr);
|
||||
}
|
||||
|
||||
class LayeredRomfsStorage : public ams::fs::IStorage {
|
||||
private:
|
||||
LayeredRomfsStorageImpl *m_impl;
|
||||
public:
|
||||
LayeredRomfsStorage(LayeredRomfsStorageImpl *impl) : m_impl(impl) {
|
||||
OpenReference(m_impl);
|
||||
}
|
||||
|
||||
virtual ~LayeredRomfsStorage() {
|
||||
CloseReference(m_impl);
|
||||
}
|
||||
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
R_RETURN(m_impl->Read(offset, buffer, size));
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out_size) override {
|
||||
R_RETURN(m_impl->GetSize(out_size));
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
R_RETURN(m_impl->Flush());
|
||||
}
|
||||
|
||||
virtual Result OperateRange(void *dst, size_t dst_size, ams::fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
|
||||
R_RETURN(m_impl->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
/* TODO: Better result code? */
|
||||
AMS_UNUSED(offset, buffer, size);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
/* TODO: Better result code? */
|
||||
AMS_UNUSED(size);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using namespace ams::fs;
|
||||
|
||||
std::shared_ptr<ams::fs::IStorage> GetLayeredRomfsStorage(ncm::ProgramId program_id, ::FsStorage &data_storage, bool is_process_romfs) {
|
||||
/*Prepare to find or create a new storage. */
|
||||
LayeredRomfsStorageImpl *impl = nullptr;
|
||||
{
|
||||
std::scoped_lock lk(g_storage_set_mutex);
|
||||
|
||||
/* Find an existing storage. */
|
||||
if (auto it = g_storage_set.find_key(program_id.value); it != g_storage_set.end()) {
|
||||
return std::make_shared<LayeredRomfsStorage>(it->GetImpl());
|
||||
}
|
||||
|
||||
/* We don't have an existing storage. If we're creating process romfs, free any unreferenced process romfs. */
|
||||
/* This should help prevent too much memory in use at any time. */
|
||||
if (is_process_romfs) {
|
||||
auto it = g_storage_set.begin();
|
||||
while (it != g_storage_set.end()) {
|
||||
if (it->GetReferenceCount() > 0 || !it->IsProcessRomfs()) {
|
||||
++it;
|
||||
} else {
|
||||
auto *holder = std::addressof(*it);
|
||||
it = g_storage_set.erase(it);
|
||||
delete holder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Create a new storage. */
|
||||
{
|
||||
::FsFile data_file;
|
||||
if (R_SUCCEEDED(OpenAtmosphereSdFile(std::addressof(data_file), program_id, "romfs.bin", OpenMode_Read))) {
|
||||
impl = new LayeredRomfsStorageImpl(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), std::make_unique<ReadOnlyStorageAdapter>(new FileStorage(new RemoteFile(data_file))), program_id);
|
||||
} else {
|
||||
impl = new LayeredRomfsStorageImpl(std::make_unique<ReadOnlyStorageAdapter>(new RemoteStorage(data_storage)), nullptr, program_id);
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert holder. Reference count will now be one. */
|
||||
g_storage_set.insert(*(new LayeredRomfsStorageHolder(impl, is_process_romfs)));
|
||||
}
|
||||
|
||||
/* Begin initialization. When this finishes, a decref will occur. */
|
||||
AMS_ABORT_UNLESS(impl != nullptr);
|
||||
impl->BeginInitialize();
|
||||
|
||||
/* Return a new shared storage for the impl. */
|
||||
return std::make_shared<LayeredRomfsStorage>(impl);
|
||||
}
|
||||
|
||||
void FinalizeLayeredRomfsStorage(ncm::ProgramId program_id) {
|
||||
std::scoped_lock lk(g_initialization_mutex);
|
||||
std::scoped_lock lk2(g_storage_set_mutex);
|
||||
|
||||
/* Find an existing storage. */
|
||||
if (auto it = g_storage_set.find_key(program_id.value); it != g_storage_set.end()) {
|
||||
/* We need to delete the process romfs. Require invariant that it is unreferenced, by this point. */
|
||||
AMS_ABORT_UNLESS(it->GetReferenceCount() == 0);
|
||||
|
||||
auto *holder = std::addressof(*it);
|
||||
it = g_storage_set.erase(it);
|
||||
delete holder;
|
||||
}
|
||||
}
|
||||
|
||||
LayeredRomfsStorageImpl::LayeredRomfsStorageImpl(std::unique_ptr<IStorage> s_r, std::unique_ptr<IStorage> f_r, ncm::ProgramId pr_id) : m_storage_romfs(std::move(s_r)), m_file_romfs(std::move(f_r)), m_initialize_event(os::EventClearMode_ManualClear), m_program_id(std::move(pr_id)), m_is_initialized(false), m_started_initialize(false) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
LayeredRomfsStorageImpl::~LayeredRomfsStorageImpl() {
|
||||
for (size_t i = 0; i < m_source_infos.size(); i++) {
|
||||
m_source_infos[i].Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
void LayeredRomfsStorageImpl::BeginInitialize() {
|
||||
AMS_ABORT_UNLESS(!m_started_initialize);
|
||||
RequestInitializeStorage(reinterpret_cast<uintptr_t>(this));
|
||||
m_started_initialize = true;
|
||||
}
|
||||
|
||||
void LayeredRomfsStorageImpl::InitializeImpl() {
|
||||
/* Build new virtual romfs. */
|
||||
romfs::Builder builder(m_program_id);
|
||||
|
||||
if (mitm::IsInitialized()) {
|
||||
builder.AddSdFiles();
|
||||
}
|
||||
if (m_file_romfs) {
|
||||
builder.AddStorageFiles(m_file_romfs.get(), romfs::DataSourceType::File);
|
||||
}
|
||||
if (m_storage_romfs) {
|
||||
builder.AddStorageFiles(m_storage_romfs.get(), romfs::DataSourceType::Storage);
|
||||
}
|
||||
|
||||
builder.Build(std::addressof(m_source_infos));
|
||||
|
||||
m_is_initialized = true;
|
||||
m_initialize_event.Signal();
|
||||
}
|
||||
|
||||
Result LayeredRomfsStorageImpl::Read(s64 offset, void *buffer, size_t size) {
|
||||
/* Check if we can succeed immediately. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Ensure we're initialized. */
|
||||
if (!m_is_initialized) {
|
||||
m_initialize_event.Wait();
|
||||
}
|
||||
|
||||
/* Validate offset/size. */
|
||||
const s64 virt_size = this->GetSize();
|
||||
R_UNLESS(offset >= 0, fs::ResultInvalidOffset());
|
||||
R_UNLESS(offset < virt_size, fs::ResultInvalidOffset());
|
||||
if (static_cast<size_t>(virt_size - offset) < size) {
|
||||
size = static_cast<size_t>(virt_size - offset);
|
||||
}
|
||||
|
||||
/* Find first source info via binary search. */
|
||||
auto it = std::lower_bound(m_source_infos.begin(), m_source_infos.end(), offset);
|
||||
u8 *cur_dst = static_cast<u8 *>(buffer);
|
||||
|
||||
/* Our operator < compares against start of info instead of end, so we need to subtract one from lower bound. */
|
||||
it--;
|
||||
|
||||
size_t read_so_far = 0;
|
||||
while (read_so_far < size) {
|
||||
const auto &cur_source = *it;
|
||||
AMS_ABORT_UNLESS(offset >= cur_source.virtual_offset);
|
||||
|
||||
if (offset < cur_source.virtual_offset + cur_source.size) {
|
||||
const s64 offset_within_source = offset - cur_source.virtual_offset;
|
||||
const size_t cur_read_size = std::min(size - read_so_far, static_cast<size_t>(cur_source.size - offset_within_source));
|
||||
switch (cur_source.source_type) {
|
||||
case romfs::DataSourceType::Storage:
|
||||
R_ABORT_UNLESS(m_storage_romfs->Read(cur_source.storage_source_info.offset + offset_within_source, cur_dst, cur_read_size));
|
||||
break;
|
||||
case romfs::DataSourceType::File:
|
||||
R_ABORT_UNLESS(m_file_romfs->Read(cur_source.file_source_info.offset + offset_within_source, cur_dst, cur_read_size));
|
||||
break;
|
||||
case romfs::DataSourceType::LooseSdFile:
|
||||
{
|
||||
FsFile file;
|
||||
R_ABORT_UNLESS(mitm::fs::OpenAtmosphereSdRomfsFile(std::addressof(file), m_program_id, cur_source.loose_source_info.path, OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fsFileClose(std::addressof(file)); };
|
||||
|
||||
u64 out_read = 0;
|
||||
R_ABORT_UNLESS(fsFileRead(std::addressof(file), offset_within_source, cur_dst, cur_read_size, FsReadOption_None, std::addressof(out_read)));
|
||||
AMS_ABORT_UNLESS(out_read == cur_read_size);
|
||||
}
|
||||
break;
|
||||
case romfs::DataSourceType::Memory:
|
||||
std::memcpy(cur_dst, cur_source.memory_source_info.data + offset_within_source, cur_read_size);
|
||||
break;
|
||||
case romfs::DataSourceType::Metadata:
|
||||
{
|
||||
size_t out_read = 0;
|
||||
R_ABORT_UNLESS(cur_source.metadata_source_info.file->Read(std::addressof(out_read), offset_within_source, cur_dst, cur_read_size));
|
||||
AMS_ABORT_UNLESS(out_read == cur_read_size);
|
||||
}
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
read_so_far += cur_read_size;
|
||||
cur_dst += cur_read_size;
|
||||
offset += cur_read_size;
|
||||
} else {
|
||||
/* Explicitly handle padding. */
|
||||
const auto &next_source = *(++it);
|
||||
const size_t padding_size = static_cast<size_t>(next_source.virtual_offset - offset);
|
||||
|
||||
std::memset(cur_dst, 0, padding_size);
|
||||
read_so_far += padding_size;
|
||||
cur_dst += padding_size;
|
||||
offset += padding_size;
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LayeredRomfsStorageImpl::GetSize(s64 *out_size) {
|
||||
/* Ensure we're initialized. */
|
||||
if (!m_is_initialized) {
|
||||
m_initialize_event.Wait();
|
||||
}
|
||||
|
||||
*out_size = this->GetSize();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LayeredRomfsStorageImpl::Flush() {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LayeredRomfsStorageImpl::OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
|
||||
AMS_UNUSED(offset, src, src_size);
|
||||
|
||||
switch (op_id) {
|
||||
case OperationId::Invalidate:
|
||||
case OperationId::QueryRange:
|
||||
if (size == 0) {
|
||||
if (op_id == OperationId::QueryRange) {
|
||||
R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
|
||||
R_UNLESS(dst_size == sizeof(QueryRangeInfo), fs::ResultInvalidSize());
|
||||
reinterpret_cast<QueryRangeInfo *>(dst)->Clear();
|
||||
}
|
||||
R_SUCCEED();
|
||||
}
|
||||
/* TODO: How to deal with this? */
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
default:
|
||||
R_THROW(fs::ResultUnsupportedOperation());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsmitm_romfs.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
class LayeredRomfsStorageImpl {
|
||||
private:
|
||||
romfs::Builder::SourceInfoVector m_source_infos;
|
||||
std::unique_ptr<ams::fs::IStorage> m_storage_romfs;
|
||||
std::unique_ptr<ams::fs::IStorage> m_file_romfs;
|
||||
os::Event m_initialize_event;
|
||||
ncm::ProgramId m_program_id;
|
||||
bool m_is_initialized;
|
||||
bool m_started_initialize;
|
||||
protected:
|
||||
inline s64 GetSize() const {
|
||||
const auto &back = m_source_infos.back();
|
||||
return back.virtual_offset + back.size;
|
||||
}
|
||||
public:
|
||||
LayeredRomfsStorageImpl(std::unique_ptr<ams::fs::IStorage> s_r, std::unique_ptr<ams::fs::IStorage> f_r, ncm::ProgramId pr_id);
|
||||
~LayeredRomfsStorageImpl();
|
||||
|
||||
void BeginInitialize();
|
||||
void InitializeImpl();
|
||||
|
||||
constexpr ncm::ProgramId GetProgramId() const { return m_program_id; }
|
||||
|
||||
Result Read(s64 offset, void *buffer, size_t size);
|
||||
Result GetSize(s64 *out_size);
|
||||
Result Flush();
|
||||
Result OperateRange(void *dst, size_t dst_size, ams::fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size);
|
||||
};
|
||||
|
||||
std::shared_ptr<ams::fs::IStorage> GetLayeredRomfsStorage(ncm::ProgramId program_id, ::FsStorage &data_storage, bool is_process_romfs);
|
||||
|
||||
void FinalizeLayeredRomfsStorage(ncm::ProgramId program_id);
|
||||
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsmitm_module.hpp"
|
||||
#include "fs_mitm_service.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
namespace {
|
||||
|
||||
enum PortIndex {
|
||||
PortIndex_Mitm,
|
||||
PortIndex_Count,
|
||||
};
|
||||
|
||||
constexpr sm::ServiceName MitmServiceName = sm::ServiceName::Encode("fsp-srv");
|
||||
|
||||
struct ServerOptions {
|
||||
static constexpr size_t PointerBufferSize = 0x800;
|
||||
static constexpr size_t MaxDomains = 0x40;
|
||||
static constexpr size_t MaxDomainObjects = 0x4000;
|
||||
static constexpr bool CanDeferInvokeRequest = false;
|
||||
static constexpr bool CanManageMitmServers = true;
|
||||
};
|
||||
|
||||
constexpr size_t MaxSessions = 61;
|
||||
|
||||
class ServerManager final : public sf::hipc::ServerManager<PortIndex_Count, ServerOptions, MaxSessions> {
|
||||
private:
|
||||
virtual Result OnNeedsToAccept(int port_index, Server *server) override;
|
||||
};
|
||||
|
||||
ServerManager g_server_manager;
|
||||
|
||||
Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
|
||||
/* Acknowledge the mitm session. */
|
||||
std::shared_ptr<::Service> fsrv;
|
||||
sm::MitmProcessInfo client_info;
|
||||
server->AcknowledgeMitmSession(std::addressof(fsrv), std::addressof(client_info));
|
||||
|
||||
switch (port_index) {
|
||||
case PortIndex_Mitm:
|
||||
R_RETURN(this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<IFsMitmInterface, FsMitmService>(decltype(fsrv)(fsrv), client_info), fsrv));
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr size_t TotalThreads = 5;
|
||||
static_assert(TotalThreads >= 1, "TotalThreads");
|
||||
constexpr size_t NumExtraThreads = TotalThreads - 1;
|
||||
constexpr size_t ThreadStackSize = mitm::ModuleTraits<fs::MitmModule>::StackSize;
|
||||
alignas(os::MemoryPageSize) u8 g_extra_thread_stacks[NumExtraThreads][ThreadStackSize];
|
||||
|
||||
os::ThreadType g_extra_threads[NumExtraThreads];
|
||||
|
||||
void LoopServerThread(void *) {
|
||||
/* Loop forever, servicing our services. */
|
||||
g_server_manager.LoopProcess();
|
||||
}
|
||||
|
||||
void ProcessForServerOnAllThreads() {
|
||||
/* Initialize threads. */
|
||||
if constexpr (NumExtraThreads > 0) {
|
||||
const s32 priority = os::GetThreadCurrentPriority(os::GetCurrentThread());
|
||||
for (size_t i = 0; i < NumExtraThreads; i++) {
|
||||
R_ABORT_UNLESS(os::CreateThread(g_extra_threads + i, LoopServerThread, nullptr, g_extra_thread_stacks[i], ThreadStackSize, priority));
|
||||
}
|
||||
}
|
||||
|
||||
/* Start extra threads. */
|
||||
if constexpr (NumExtraThreads > 0) {
|
||||
for (size_t i = 0; i < NumExtraThreads; i++) {
|
||||
os::StartThread(g_extra_threads + i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop this thread. */
|
||||
LoopServerThread(nullptr);
|
||||
|
||||
/* Wait for extra threads to finish. */
|
||||
if constexpr (NumExtraThreads > 0) {
|
||||
for (size_t i = 0; i < NumExtraThreads; i++) {
|
||||
os::WaitThread(g_extra_threads + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MitmModule::ThreadFunction(void *) {
|
||||
/* Create fs mitm. */
|
||||
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<FsMitmService>(PortIndex_Mitm, MitmServiceName)));
|
||||
|
||||
/* Process for the server. */
|
||||
ProcessForServerOnAllThreads();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_module.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
DEFINE_MITM_MODULE_CLASS(0x8000, AMS_GET_SYSTEM_THREAD_PRIORITY(fs, WorkerThreadPool) - 1);
|
||||
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
class ReadOnlyLayeredFileSystem : public ams::fs::fsa::IFileSystem {
|
||||
private:
|
||||
ams::fs::ReadOnlyFileSystem m_fs_1;
|
||||
ams::fs::ReadOnlyFileSystem m_fs_2;
|
||||
public:
|
||||
explicit ReadOnlyLayeredFileSystem(std::unique_ptr<ams::fs::fsa::IFileSystem> a, std::unique_ptr<ams::fs::fsa::IFileSystem> b) : m_fs_1(std::move(a)), m_fs_2(std::move(b)) { /* ... */ }
|
||||
|
||||
virtual ~ReadOnlyLayeredFileSystem() { /* ... */ }
|
||||
private:
|
||||
virtual Result DoCreateFile(const ams::fs::Path &path, s64 size, int flags) override final {
|
||||
AMS_UNUSED(path, size, flags);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result DoDeleteFile(const ams::fs::Path &path) override final {
|
||||
AMS_UNUSED(path);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result DoCreateDirectory(const ams::fs::Path &path) override final {
|
||||
AMS_UNUSED(path);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result DoDeleteDirectory(const ams::fs::Path &path) override final {
|
||||
AMS_UNUSED(path);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result DoDeleteDirectoryRecursively(const ams::fs::Path &path) override final {
|
||||
AMS_UNUSED(path);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result DoRenameFile(const ams::fs::Path &old_path, const ams::fs::Path &new_path) override final {
|
||||
AMS_UNUSED(old_path, new_path);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result DoRenameDirectory(const ams::fs::Path &old_path, const ams::fs::Path &new_path) override final {
|
||||
AMS_UNUSED(old_path, new_path);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result DoGetEntryType(ams::fs::DirectoryEntryType *out, const ams::fs::Path &path) override final {
|
||||
R_SUCCEED_IF(R_SUCCEEDED(m_fs_1.GetEntryType(out, path)));
|
||||
R_RETURN(m_fs_2.GetEntryType(out, path));
|
||||
}
|
||||
|
||||
virtual Result DoOpenFile(std::unique_ptr<ams::fs::fsa::IFile> *out_file, const ams::fs::Path &path, ams::fs::OpenMode mode) override final {
|
||||
R_SUCCEED_IF(R_SUCCEEDED(m_fs_1.OpenFile(out_file, path, mode)));
|
||||
R_RETURN(m_fs_2.OpenFile(out_file, path, mode));
|
||||
}
|
||||
|
||||
virtual Result DoOpenDirectory(std::unique_ptr<ams::fs::fsa::IDirectory> *out_dir, const ams::fs::Path &path, ams::fs::OpenDirectoryMode mode) override final {
|
||||
R_SUCCEED_IF(R_SUCCEEDED(m_fs_1.OpenDirectory(out_dir, path, mode)));
|
||||
R_RETURN(m_fs_2.OpenDirectory(out_dir, path, mode));
|
||||
}
|
||||
|
||||
virtual Result DoCommit() override final {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result DoGetFreeSpaceSize(s64 *out, const ams::fs::Path &path) {
|
||||
AMS_UNUSED(out, path);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result DoGetTotalSpaceSize(s64 *out, const ams::fs::Path &path) {
|
||||
AMS_UNUSED(out, path);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result DoCleanDirectoryRecursively(const ams::fs::Path &path) {
|
||||
AMS_UNUSED(path);
|
||||
R_THROW(ams::fs::ResultUnsupportedOperation())
|
||||
}
|
||||
|
||||
virtual Result DoGetFileTimeStampRaw(ams::fs::FileTimeStampRaw *out, const ams::fs::Path &path) {
|
||||
R_SUCCEED_IF(R_SUCCEEDED(m_fs_1.GetFileTimeStampRaw(out, path)));
|
||||
R_RETURN(m_fs_2.GetFileTimeStampRaw(out, path));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,379 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::fs::romfs {
|
||||
|
||||
enum class DataSourceType : u8 {
|
||||
Storage,
|
||||
File,
|
||||
LooseSdFile,
|
||||
Metadata,
|
||||
Memory,
|
||||
};
|
||||
|
||||
enum AllocationType {
|
||||
AllocationType_FileName,
|
||||
AllocationType_DirName,
|
||||
AllocationType_FullPath,
|
||||
AllocationType_SourceInfo,
|
||||
AllocationType_BuildFileContext,
|
||||
AllocationType_BuildDirContext,
|
||||
AllocationType_TableCache,
|
||||
AllocationType_DirPointerArray,
|
||||
AllocationType_DirContextSet,
|
||||
AllocationType_FileContextSet,
|
||||
AllocationType_Memory,
|
||||
|
||||
AllocationType_Count,
|
||||
};
|
||||
|
||||
void *AllocateTracked(AllocationType type, size_t size);
|
||||
void FreeTracked(AllocationType type, void *p, size_t size);
|
||||
|
||||
template<typename T, typename... Args>
|
||||
T *AllocateTyped(AllocationType type, Args &&... args) {
|
||||
void *mem = AllocateTracked(type, sizeof(T));
|
||||
return std::construct_at(static_cast<T *>(mem), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<AllocationType AllocType, typename T>
|
||||
class TrackedAllocator {
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
template<typename U>
|
||||
struct rebind {
|
||||
using other = TrackedAllocator<AllocType, U>;
|
||||
};
|
||||
public:
|
||||
TrackedAllocator() = default;
|
||||
|
||||
T *allocate(size_t n) {
|
||||
return static_cast<T *>(AllocateTracked(AllocType, sizeof(T) * n));
|
||||
}
|
||||
|
||||
void deallocate(T *p, size_t n) {
|
||||
FreeTracked(AllocType, p, sizeof(T) * n);
|
||||
}
|
||||
};
|
||||
|
||||
struct SourceInfo {
|
||||
s64 virtual_offset;
|
||||
s64 size;
|
||||
union {
|
||||
struct {
|
||||
s64 offset;
|
||||
} storage_source_info;
|
||||
struct {
|
||||
s64 offset;
|
||||
} file_source_info;
|
||||
struct {
|
||||
char *path;
|
||||
} loose_source_info;
|
||||
struct {
|
||||
ams::fs::fsa::IFile *file;
|
||||
} metadata_source_info;
|
||||
struct {
|
||||
u8 *data;
|
||||
} memory_source_info;
|
||||
};
|
||||
DataSourceType source_type;
|
||||
bool cleaned_up;
|
||||
|
||||
SourceInfo(s64 v_o, s64 sz, DataSourceType type, s64 p_o) : virtual_offset(v_o), size(sz), source_type(type), cleaned_up(false) {
|
||||
switch (this->source_type) {
|
||||
case DataSourceType::Storage:
|
||||
this->storage_source_info.offset = p_o;
|
||||
break;
|
||||
case DataSourceType::File:
|
||||
this->file_source_info.offset = p_o;
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
SourceInfo(s64 v_o, s64 sz, DataSourceType type, void *arg) : virtual_offset(v_o), size(sz), source_type(type), cleaned_up(false) {
|
||||
switch (this->source_type) {
|
||||
case DataSourceType::LooseSdFile:
|
||||
this->loose_source_info.path = static_cast<char *>(arg);
|
||||
break;
|
||||
case DataSourceType::Metadata:
|
||||
this->metadata_source_info.file = static_cast<ams::fs::fsa::IFile *>(arg);
|
||||
break;
|
||||
case DataSourceType::Memory:
|
||||
this->memory_source_info.data = static_cast<u8 *>(arg);
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
void Cleanup() {
|
||||
AMS_ABORT_UNLESS(!this->cleaned_up);
|
||||
this->cleaned_up = true;
|
||||
|
||||
switch (this->source_type) {
|
||||
case DataSourceType::Storage:
|
||||
case DataSourceType::File:
|
||||
break;
|
||||
case DataSourceType::Metadata:
|
||||
delete this->metadata_source_info.file;
|
||||
break;
|
||||
case DataSourceType::LooseSdFile:
|
||||
FreeTracked(AllocationType_FullPath, this->loose_source_info.path, std::strlen(this->loose_source_info.path) + 1);
|
||||
break;
|
||||
case DataSourceType::Memory:
|
||||
FreeTracked(AllocationType_Memory, this->memory_source_info.data, this->size);
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
constexpr inline bool operator<(const SourceInfo &lhs, const SourceInfo &rhs) {
|
||||
return lhs.virtual_offset < rhs.virtual_offset;
|
||||
}
|
||||
|
||||
constexpr inline bool operator<(const SourceInfo &lhs, const s64 rhs) {
|
||||
return lhs.virtual_offset <= rhs;
|
||||
}
|
||||
|
||||
struct BuildFileContext;
|
||||
|
||||
struct BuildDirectoryContext {
|
||||
NON_COPYABLE(BuildDirectoryContext);
|
||||
NON_MOVEABLE(BuildDirectoryContext);
|
||||
|
||||
char *path;
|
||||
union {
|
||||
BuildDirectoryContext *parent;
|
||||
};
|
||||
union {
|
||||
BuildDirectoryContext *child;
|
||||
struct {
|
||||
u32 parent_offset;
|
||||
u32 child_offset;
|
||||
};
|
||||
};
|
||||
union {
|
||||
BuildDirectoryContext *sibling;
|
||||
u32 sibling_offset;
|
||||
};
|
||||
union {
|
||||
BuildFileContext *file;
|
||||
u32 file_offset;
|
||||
};
|
||||
u32 path_len;
|
||||
u32 entry_offset;
|
||||
u32 hash_value;
|
||||
|
||||
struct RootTag{};
|
||||
|
||||
BuildDirectoryContext(RootTag) : parent(nullptr), child(nullptr), sibling(nullptr), file(nullptr), path_len(0), entry_offset(0), hash_value(0xFFFFFFFF) {
|
||||
this->path = static_cast<char *>(AllocateTracked(AllocationType_DirName, 1));
|
||||
this->path[0] = '\x00';
|
||||
}
|
||||
|
||||
BuildDirectoryContext(const char *entry_name, size_t entry_name_len) : parent(nullptr), child(nullptr), sibling(nullptr), file(nullptr), entry_offset(0) {
|
||||
this->path_len = entry_name_len;
|
||||
this->path = static_cast<char *>(AllocateTracked(AllocationType_DirName, this->path_len + 1));
|
||||
std::memcpy(this->path, entry_name, entry_name_len);
|
||||
this->path[this->path_len] = '\x00';
|
||||
}
|
||||
|
||||
~BuildDirectoryContext() {
|
||||
if (this->path != nullptr) {
|
||||
FreeTracked(AllocationType_DirName, this->path, this->path_len + 1);
|
||||
this->path = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void operator delete(void *p) {
|
||||
FreeTracked(AllocationType_BuildDirContext, p, sizeof(BuildDirectoryContext));
|
||||
}
|
||||
|
||||
size_t GetPathLength() const {
|
||||
if (this->parent == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this->parent->GetPathLength() + 1 + this->path_len;
|
||||
}
|
||||
|
||||
size_t GetPath(char *dst) const {
|
||||
if (this->parent == nullptr) {
|
||||
dst[0] = '\x00';
|
||||
return 0;
|
||||
}
|
||||
|
||||
const size_t parent_len = this->parent->GetPath(dst);
|
||||
dst[parent_len] = '/';
|
||||
std::memcpy(dst + parent_len + 1, this->path, this->path_len);
|
||||
dst[parent_len + 1 + this->path_len] = '\x00';
|
||||
return parent_len + 1 + this->path_len;
|
||||
}
|
||||
|
||||
bool HasHashMark() const {
|
||||
return reinterpret_cast<uintptr_t>(this->sibling) & UINT64_C(0x8000000000000000);
|
||||
}
|
||||
|
||||
void SetHashMark() {
|
||||
this->sibling = reinterpret_cast<BuildDirectoryContext *>(reinterpret_cast<uintptr_t>(this->sibling) | UINT64_C(0x8000000000000000));
|
||||
}
|
||||
|
||||
void ClearHashMark() {
|
||||
this->sibling = reinterpret_cast<BuildDirectoryContext *>(reinterpret_cast<uintptr_t>(this->sibling) & ~UINT64_C(0x8000000000000000));
|
||||
}
|
||||
};
|
||||
|
||||
struct BuildFileContext {
|
||||
NON_COPYABLE(BuildFileContext);
|
||||
NON_MOVEABLE(BuildFileContext);
|
||||
|
||||
char *path;
|
||||
BuildDirectoryContext *parent;
|
||||
union {
|
||||
BuildFileContext *sibling;
|
||||
u32 sibling_offset;
|
||||
};
|
||||
s64 offset;
|
||||
s64 size;
|
||||
s64 orig_offset;
|
||||
u32 path_len;
|
||||
u32 entry_offset;
|
||||
u32 hash_value;
|
||||
DataSourceType source_type;
|
||||
|
||||
BuildFileContext(const char *entry_name, size_t entry_name_len, s64 sz, s64 o_o, DataSourceType type) : parent(nullptr), sibling(nullptr), offset(0), size(sz), orig_offset(o_o), entry_offset(0), hash_value(0xFFFFFFFF), source_type(type) {
|
||||
this->path_len = entry_name_len;
|
||||
this->path = static_cast<char *>(AllocateTracked(AllocationType_FileName, this->path_len + 1));
|
||||
std::memcpy(this->path, entry_name, entry_name_len);
|
||||
this->path[this->path_len] = 0;
|
||||
}
|
||||
|
||||
~BuildFileContext() {
|
||||
if (this->path != nullptr) {
|
||||
FreeTracked(AllocationType_FileName, this->path, this->path_len + 1);
|
||||
this->path = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void operator delete(void *p) {
|
||||
FreeTracked(AllocationType_BuildFileContext, p, sizeof(BuildFileContext));
|
||||
}
|
||||
|
||||
size_t GetPathLength() const {
|
||||
if (this->parent == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this->parent->GetPathLength() + 1 + this->path_len;
|
||||
}
|
||||
|
||||
size_t GetPath(char *dst) const {
|
||||
if (this->parent == nullptr) {
|
||||
dst[0] = '\x00';
|
||||
return 0;
|
||||
}
|
||||
|
||||
const size_t parent_len = this->parent->GetPath(dst);
|
||||
dst[parent_len] = '/';
|
||||
std::memcpy(dst + parent_len + 1, this->path, this->path_len);
|
||||
dst[parent_len + 1 + this->path_len] = '\x00';
|
||||
return parent_len + 1 + this->path_len;
|
||||
}
|
||||
|
||||
bool HasHashMark() const {
|
||||
return reinterpret_cast<uintptr_t>(this->sibling) & UINT64_C(0x8000000000000000);
|
||||
}
|
||||
|
||||
void SetHashMark() {
|
||||
this->sibling = reinterpret_cast<BuildFileContext *>(reinterpret_cast<uintptr_t>(this->sibling) | UINT64_C(0x8000000000000000));
|
||||
}
|
||||
|
||||
void ClearHashMark() {
|
||||
this->sibling = reinterpret_cast<BuildFileContext *>(reinterpret_cast<uintptr_t>(this->sibling) & ~UINT64_C(0x8000000000000000));
|
||||
}
|
||||
};
|
||||
|
||||
class DirectoryTableReader;
|
||||
class FileTableReader;
|
||||
|
||||
class Builder {
|
||||
NON_COPYABLE(Builder);
|
||||
NON_MOVEABLE(Builder);
|
||||
public:
|
||||
using SourceInfoVector = std::vector<SourceInfo, TrackedAllocator<AllocationType_SourceInfo, SourceInfo>>;
|
||||
private:
|
||||
template<typename T>
|
||||
struct Comparator {
|
||||
static constexpr inline int Compare(const char *a, const char *b) {
|
||||
unsigned char c1{}, c2{};
|
||||
while ((c1 = *a++) == (c2 = *b++)) {
|
||||
if (c1 == '\x00') {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return (c1 - c2);
|
||||
}
|
||||
|
||||
constexpr bool operator()(const std::unique_ptr<T> &lhs, const std::unique_ptr<T> &rhs) const {
|
||||
char lhs_path[ams::fs::EntryNameLengthMax + 1];
|
||||
char rhs_path[ams::fs::EntryNameLengthMax + 1];
|
||||
lhs->GetPath(lhs_path);
|
||||
rhs->GetPath(rhs_path);
|
||||
return Comparator::Compare(lhs_path, rhs_path) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
template<AllocationType AllocType, typename T>
|
||||
using ContextSet = std::set<std::unique_ptr<T>, Comparator<T>, TrackedAllocator<AllocType, std::unique_ptr<T>>>;
|
||||
private:
|
||||
ncm::ProgramId m_program_id;
|
||||
BuildDirectoryContext *m_root;
|
||||
ContextSet<AllocationType_DirContextSet, BuildDirectoryContext> m_directories;
|
||||
ContextSet<AllocationType_FileContextSet, BuildFileContext> m_files;
|
||||
size_t m_num_dirs;
|
||||
size_t m_num_files;
|
||||
size_t m_dir_table_size;
|
||||
size_t m_file_table_size;
|
||||
size_t m_dir_hash_table_size;
|
||||
size_t m_file_hash_table_size;
|
||||
size_t m_file_partition_size;
|
||||
|
||||
::FsDirectoryEntry m_dir_entry;
|
||||
DataSourceType m_cur_source_type;
|
||||
private:
|
||||
void VisitDirectory(FsFileSystem *fs, BuildDirectoryContext *parent);
|
||||
void VisitDirectory(BuildDirectoryContext *parent, u32 parent_offset, DirectoryTableReader &dir_table, FileTableReader &file_table);
|
||||
|
||||
void AddDirectory(BuildDirectoryContext **out, BuildDirectoryContext *parent_ctx, std::unique_ptr<BuildDirectoryContext> file_ctx);
|
||||
void AddFile(BuildDirectoryContext *parent_ctx, std::unique_ptr<BuildFileContext> file_ctx);
|
||||
public:
|
||||
Builder(ncm::ProgramId pr_id);
|
||||
~Builder();
|
||||
|
||||
void AddSdFiles();
|
||||
void AddStorageFiles(ams::fs::IStorage *storage, DataSourceType source_type);
|
||||
|
||||
void Build(SourceInfoVector *out_infos);
|
||||
};
|
||||
|
||||
Result ConfigureDynamicHeap(u64 *out_size, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application);
|
||||
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsmitm_save_utils.hpp"
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
using namespace ams::fs;
|
||||
|
||||
namespace {
|
||||
|
||||
Result GetSaveDataSpaceIdString(const char **out_str, u8 space_id) {
|
||||
switch (static_cast<SaveDataSpaceId>(space_id)) {
|
||||
case SaveDataSpaceId::System:
|
||||
case SaveDataSpaceId::ProperSystem:
|
||||
*out_str = "sys";
|
||||
break;
|
||||
case SaveDataSpaceId::User:
|
||||
*out_str = "user";
|
||||
break;
|
||||
case SaveDataSpaceId::SdSystem:
|
||||
*out_str = "sd_sys";
|
||||
break;
|
||||
case SaveDataSpaceId::Temporary:
|
||||
*out_str = "temp";
|
||||
break;
|
||||
case SaveDataSpaceId::SdUser:
|
||||
*out_str = "sd_user";
|
||||
break;
|
||||
case SaveDataSpaceId::SafeMode:
|
||||
*out_str = "safe";
|
||||
break;
|
||||
default:
|
||||
R_THROW(fs::ResultInvalidSaveDataSpaceId());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetSaveDataTypeString(const char **out_str, SaveDataType save_data_type) {
|
||||
switch (save_data_type) {
|
||||
case SaveDataType::System:
|
||||
*out_str = "system";
|
||||
break;
|
||||
case SaveDataType::Account:
|
||||
*out_str = "account";
|
||||
break;
|
||||
case SaveDataType::Bcat:
|
||||
*out_str = "bcat";
|
||||
break;
|
||||
case SaveDataType::Device:
|
||||
*out_str = "device";
|
||||
break;
|
||||
case SaveDataType::Temporary:
|
||||
*out_str = "temp";
|
||||
break;
|
||||
case SaveDataType::Cache:
|
||||
*out_str = "cache";
|
||||
break;
|
||||
case SaveDataType::SystemBcat:
|
||||
*out_str = "system_bcat";
|
||||
break;
|
||||
default:
|
||||
/* TODO: Better result? */
|
||||
R_THROW(fs::ResultInvalidArgument());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
constexpr inline bool IsEmptyAccountId(const UserId &uid) {
|
||||
return uid == InvalidUserId;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Result SaveUtil::GetDirectorySaveDataPath(char *dst, size_t dst_size, ncm::ProgramId program_id, u8 space_id, const fs::SaveDataAttribute &attribute) {
|
||||
/* Saves should be separate for emunand vs sysnand. */
|
||||
const char *emummc_str = emummc::IsActive() ? "emummc" : "sysmmc";
|
||||
|
||||
/* Get space_id, save_data_type strings. */
|
||||
const char *space_id_str, *save_type_str;
|
||||
R_TRY(GetSaveDataSpaceIdString(&space_id_str, space_id));
|
||||
R_TRY(GetSaveDataTypeString(&save_type_str, attribute.type));
|
||||
|
||||
/* Initialize the path. */
|
||||
const bool is_system = attribute.system_save_data_id != InvalidSystemSaveDataId && IsEmptyAccountId(attribute.user_id);
|
||||
size_t out_path_len;
|
||||
if (is_system) {
|
||||
out_path_len = static_cast<size_t>(util::SNPrintf(dst, dst_size, "/atmosphere/saves/%s/%s/%s/%016lx", emummc_str, space_id_str, save_type_str, attribute.system_save_data_id));
|
||||
} else {
|
||||
out_path_len = static_cast<size_t>(util::SNPrintf(dst, dst_size, "/atmosphere/saves/%s/%s/%s/%016lx/%016lx%016lx", emummc_str, space_id_str, save_type_str, static_cast<u64>(program_id), attribute.user_id.data[1], attribute.user_id.data[0]));
|
||||
}
|
||||
|
||||
R_UNLESS(out_path_len < dst_size, fs::ResultTooLongPath());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::fs {
|
||||
|
||||
class SaveUtil {
|
||||
public:
|
||||
static Result GetDirectorySaveDataPath(char *dst, size_t dst_size, ncm::ProgramId program_id, u8 space_id, const ams::fs::SaveDataAttribute &attribute);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#define NX_SERVICE_ASSUME_NON_DOMAIN
|
||||
#include "service_guard.h"
|
||||
#include "memlet.h"
|
||||
|
||||
static Service g_memletSrv;
|
||||
|
||||
NX_GENERATE_SERVICE_GUARD(memlet);
|
||||
|
||||
Result _memletInitialize(void) {
|
||||
return smGetService(&g_memletSrv, "memlet");
|
||||
}
|
||||
|
||||
void _memletCleanup(void) {
|
||||
serviceClose(&g_memletSrv);
|
||||
}
|
||||
|
||||
Service* memletGetServiceSession(void) {
|
||||
return &g_memletSrv;
|
||||
}
|
||||
|
||||
Result memletCreateAppletSharedMemory(Handle *out_shmem_h, u64 *out_size, u64 desired_size) {
|
||||
return serviceDispatchInOut(&g_memletSrv, 65000, desired_size, *out_size,
|
||||
.out_handle_attrs = { SfOutHandleAttr_HipcMove },
|
||||
.out_handles = out_shmem_h,
|
||||
);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
Result memletInitialize(void);
|
||||
void memletExit(void);
|
||||
Service* memletGetServiceSession(void);
|
||||
|
||||
Result memletCreateAppletSharedMemory(Handle *out_shmem_h, u64 *out_size, u64 desired_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,65 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <switch/types.h>
|
||||
#include <switch/result.h>
|
||||
#include <switch/kernel/mutex.h>
|
||||
#include <switch/sf/service.h>
|
||||
#include <switch/services/sm.h>
|
||||
|
||||
typedef struct ServiceGuard {
|
||||
Mutex mutex;
|
||||
u32 refCount;
|
||||
} ServiceGuard;
|
||||
|
||||
NX_INLINE bool serviceGuardBeginInit(ServiceGuard* g)
|
||||
{
|
||||
mutexLock(&g->mutex);
|
||||
return (g->refCount++) == 0;
|
||||
}
|
||||
|
||||
NX_INLINE Result serviceGuardEndInit(ServiceGuard* g, Result rc, void (*cleanupFunc)(void))
|
||||
{
|
||||
if (R_FAILED(rc)) {
|
||||
cleanupFunc();
|
||||
--g->refCount;
|
||||
}
|
||||
mutexUnlock(&g->mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
NX_INLINE void serviceGuardExit(ServiceGuard* g, void (*cleanupFunc)(void))
|
||||
{
|
||||
mutexLock(&g->mutex);
|
||||
if (g->refCount && (--g->refCount) == 0)
|
||||
cleanupFunc();
|
||||
mutexUnlock(&g->mutex);
|
||||
}
|
||||
|
||||
#define NX_GENERATE_SERVICE_GUARD_PARAMS(name, _paramdecl, _parampass) \
|
||||
\
|
||||
static ServiceGuard g_##name##Guard; \
|
||||
NX_INLINE Result _##name##Initialize _paramdecl; \
|
||||
static void _##name##Cleanup(void); \
|
||||
\
|
||||
Result name##Initialize _paramdecl \
|
||||
{ \
|
||||
Result rc = 0; \
|
||||
if (serviceGuardBeginInit(&g_##name##Guard)) \
|
||||
rc = _##name##Initialize _parampass; \
|
||||
return serviceGuardEndInit(&g_##name##Guard, rc, _##name##Cleanup); \
|
||||
} \
|
||||
\
|
||||
void name##Exit(void) \
|
||||
{ \
|
||||
serviceGuardExit(&g_##name##Guard, _##name##Cleanup); \
|
||||
}
|
||||
|
||||
#define NX_GENERATE_SERVICE_GUARD(name) NX_GENERATE_SERVICE_GUARD_PARAMS(name, (void), ())
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "mitm_pm_module.hpp"
|
||||
#include "mitm_pm_service.hpp"
|
||||
|
||||
namespace ams::mitm::pm {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr sm::ServiceName PmServiceName = sm::ServiceName::Encode("mitm:pm");
|
||||
constexpr size_t PmMaxSessions = 1;
|
||||
|
||||
constexpr size_t MaxServers = 1;
|
||||
constexpr size_t MaxSessions = PmMaxSessions;
|
||||
using ServerOptions = sf::hipc::DefaultServerManagerOptions;
|
||||
sf::hipc::ServerManager<MaxServers, ServerOptions, MaxSessions> g_server_manager;
|
||||
|
||||
constinit sf::UnmanagedServiceObject<mitm::pm::impl::IPmInterface, mitm::pm::PmService> g_pm_service_object;
|
||||
|
||||
}
|
||||
|
||||
void MitmModule::ThreadFunction(void *) {
|
||||
/* Create bpc:ams. */
|
||||
R_ABORT_UNLESS(g_server_manager.RegisterObjectForServer(g_pm_service_object.GetShared(), PmServiceName, PmMaxSessions));
|
||||
|
||||
/* Loop forever, servicing our services. */
|
||||
g_server_manager.LoopProcess();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_module.hpp"
|
||||
|
||||
namespace ams::mitm::pm {
|
||||
|
||||
DEFINE_MITM_MODULE_CLASS(0x1000, AMS_GET_SYSTEM_THREAD_PRIORITY(fs, WorkerThreadPool) - 2);
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "mitm_pm_service.hpp"
|
||||
#include "mitm_pm_service.hpp"
|
||||
#include "../fs_mitm/fsmitm_romfs.hpp"
|
||||
|
||||
namespace ams::mitm::pm {
|
||||
|
||||
Result PmService::PrepareLaunchProgram(sf::Out<u64> out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application) {
|
||||
/* Default to zero heap. */
|
||||
*out = 0;
|
||||
|
||||
/* Actually configure the required boost size for romfs. */
|
||||
R_TRY(mitm::fs::romfs::ConfigureDynamicHeap(out.GetPointer(), program_id, status, is_application));
|
||||
|
||||
/* TODO: Is there anything else we should do, while we have the opportunity? */
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::pm {
|
||||
|
||||
class PmService {
|
||||
public:
|
||||
Result PrepareLaunchProgram(sf::Out<u64> out, ncm::ProgramId program_id, const cfg::OverrideStatus &status, bool is_application);
|
||||
};
|
||||
static_assert(impl::IsIPmInterface<PmService>);
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_fs_utils.hpp"
|
||||
#include "ns_am_mitm_service.hpp"
|
||||
#include "ns_shim.h"
|
||||
|
||||
namespace ams::mitm::ns {
|
||||
|
||||
Result NsAmMitmService::GetApplicationContentPath(const sf::OutBuffer &out_path, ncm::ProgramId application_id, u8 content_type) {
|
||||
R_RETURN(nsamGetApplicationContentPathFwd(m_forward_service.get(), out_path.GetPointer(), out_path.GetSize(), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type)));
|
||||
}
|
||||
|
||||
Result NsAmMitmService::ResolveApplicationContentPath(ncm::ProgramId application_id, u8 content_type) {
|
||||
/* Always succeed for web applets asking about HBL to enable hbl_html, and applications with manual_html to allow custom manual data. */
|
||||
bool is_hbl = false;
|
||||
if ((R_SUCCEEDED(ams::pm::info::IsHblProgramId(std::addressof(is_hbl), application_id)) && is_hbl) || (static_cast<ncm::ContentType>(content_type) == ncm::ContentType::HtmlDocument && mitm::fs::HasSdManualHtmlContent(application_id))) {
|
||||
nsamResolveApplicationContentPathFwd(m_forward_service.get(), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type));
|
||||
R_SUCCEED();
|
||||
}
|
||||
R_RETURN(nsamResolveApplicationContentPathFwd(m_forward_service.get(), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type)));
|
||||
}
|
||||
|
||||
Result NsAmMitmService::GetRunningApplicationProgramId(sf::Out<ncm::ProgramId> out, ncm::ProgramId application_id) {
|
||||
R_RETURN(nsamGetRunningApplicationProgramIdFwd(m_forward_service.get(), reinterpret_cast<u64 *>(out.GetPointer()), static_cast<u64>(application_id)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#define AMS_NS_AM_MITM_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 21, Result, GetApplicationContentPath, (const sf::OutBuffer &out_path, ncm::ProgramId application_id, u8 content_type), (out_path, application_id, content_type)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 23, Result, ResolveApplicationContentPath, (ncm::ProgramId application_id, u8 content_type), (application_id, content_type)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 92, Result, GetRunningApplicationProgramId, (sf::Out<ncm::ProgramId> out, ncm::ProgramId application_id), (out, application_id), hos::Version_6_0_0)
|
||||
|
||||
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::ns::impl, IAmMitmInterface, AMS_NS_AM_MITM_INTERFACE_INFO, 0x059D2C39)
|
||||
|
||||
namespace ams::mitm::ns {
|
||||
|
||||
class NsAmMitmService : public sf::MitmServiceImplBase {
|
||||
public:
|
||||
using MitmServiceImplBase::MitmServiceImplBase;
|
||||
public:
|
||||
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
|
||||
/* We will mitm:
|
||||
* - web applets, to facilitate hbl web browser launching.
|
||||
*/
|
||||
return ncm::IsWebAppletId(client_info.program_id);
|
||||
}
|
||||
public:
|
||||
/* Actual command API. */
|
||||
Result GetApplicationContentPath(const sf::OutBuffer &out_path, ncm::ProgramId application_id, u8 content_type);
|
||||
Result ResolveApplicationContentPath(ncm::ProgramId application_id, u8 content_type);
|
||||
Result GetRunningApplicationProgramId(sf::Out<ncm::ProgramId> out, ncm::ProgramId application_id);
|
||||
};
|
||||
static_assert(impl::IsIAmMitmInterface<NsAmMitmService>);
|
||||
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <switch.h>
|
||||
#include "ns_shim.h"
|
||||
|
||||
/* Command forwarders. */
|
||||
Result nsGetDocumentInterfaceFwd(Service* s, NsDocumentInterface* out) {
|
||||
return serviceDispatch(s, 7999,
|
||||
.out_num_objects = 1,
|
||||
.out_objects = &out->s,
|
||||
);
|
||||
}
|
||||
|
||||
static Result _nsGetApplicationContentPathOld(Service *s, void* out, size_t out_size, u64 app_id, NcmContentType content_type) {
|
||||
const struct {
|
||||
u8 content_type;
|
||||
u64 app_id;
|
||||
} in = { content_type, app_id };
|
||||
return serviceDispatchIn(s, 21, in,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
||||
.buffers = { { out, out_size } },
|
||||
);
|
||||
}
|
||||
|
||||
static Result _nsGetApplicationContentPath(Service *s, void* out, size_t out_size, u8 *out_attr, u64 app_id, NcmContentType content_type) {
|
||||
const struct {
|
||||
u8 content_type;
|
||||
u64 app_id;
|
||||
} in = { content_type, app_id };
|
||||
return serviceDispatchInOut(s, 21, in, *out_attr,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
||||
.buffers = { { out, out_size } },
|
||||
);
|
||||
}
|
||||
|
||||
static Result _nsGetApplicationContentPath2(Service *s, void* out_path, size_t out_size, u64* out_program_id, u8 *out_attr, u64 app_id, NcmContentType content_type) {
|
||||
const struct {
|
||||
u8 content_type;
|
||||
u64 app_id;
|
||||
} in = { content_type, app_id };
|
||||
|
||||
struct {
|
||||
u8 attr;
|
||||
u64 program_id;
|
||||
} out;
|
||||
|
||||
Result rc = serviceDispatchInOut(s, 2524, in, out,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
||||
.buffers = { { out_path, out_size } },
|
||||
);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
*out_program_id = out.program_id;
|
||||
*out_attr = out.attr;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static Result _nsResolveApplicationContentPath(Service* s, u64 app_id, NcmContentType content_type) {
|
||||
const struct {
|
||||
u8 content_type;
|
||||
u64 app_id;
|
||||
} in = { content_type, app_id };
|
||||
return serviceDispatchIn(s, 23, in);
|
||||
}
|
||||
|
||||
static Result _nsGetRunningApplicationProgramId(Service* s, u64* out_program_id, u64 app_id) {
|
||||
if (hosversionBefore(6, 0, 0)) {
|
||||
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
|
||||
}
|
||||
return serviceDispatchInOut(s, 92, app_id, *out_program_id);
|
||||
}
|
||||
|
||||
/* Application Manager forwarders. */
|
||||
Result nsamGetApplicationContentPathFwd(Service* s, void* out, size_t out_size, u64 app_id, NcmContentType content_type) {
|
||||
return _nsGetApplicationContentPathOld(s, out, out_size, app_id, content_type);
|
||||
}
|
||||
|
||||
Result nsamResolveApplicationContentPathFwd(Service* s, u64 app_id, NcmContentType content_type) {
|
||||
return _nsResolveApplicationContentPath(s, app_id, content_type);
|
||||
}
|
||||
|
||||
Result nsamGetRunningApplicationProgramIdFwd(Service* s, u64* out_program_id, u64 app_id) {
|
||||
return _nsGetRunningApplicationProgramId(s, out_program_id, app_id);
|
||||
}
|
||||
|
||||
/* Web forwarders */
|
||||
Result nswebGetApplicationContentPath(NsDocumentInterface* doc, void* out, size_t out_size, u8 *out_attr, u64 app_id, NcmContentType content_type) {
|
||||
if (hosversionAtLeast(16,0,0)) {
|
||||
return _nsGetApplicationContentPath(&doc->s, out, out_size, out_attr, app_id, content_type);
|
||||
} else {
|
||||
return _nsGetApplicationContentPathOld(&doc->s, out, out_size, app_id, content_type);
|
||||
}
|
||||
}
|
||||
|
||||
Result nswebResolveApplicationContentPath(NsDocumentInterface* doc, u64 app_id, NcmContentType content_type) {
|
||||
return _nsResolveApplicationContentPath(&doc->s, app_id, content_type);
|
||||
}
|
||||
|
||||
Result nswebGetRunningApplicationProgramId(NsDocumentInterface* doc, u64* out_program_id, u64 app_id) {
|
||||
return _nsGetRunningApplicationProgramId(&doc->s, out_program_id, app_id);
|
||||
}
|
||||
|
||||
Result nswebGetApplicationContentPath2(NsDocumentInterface* doc, void* out, size_t out_size, u64* out_program_id, u8 *out_attr, u64 app_id, NcmContentType content_type) {
|
||||
return _nsGetApplicationContentPath2(&doc->s, out, out_size, out_program_id, out_attr, app_id, content_type);
|
||||
}
|
||||
|
||||
void nsDocumentInterfaceClose(NsDocumentInterface* doc) {
|
||||
serviceClose(&doc->s);
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/**
|
||||
* @file ns_shim.h
|
||||
* @brief Nintendo Shell Services (ns) IPC wrapper.
|
||||
* @author SciresM
|
||||
* @copyright libnx Authors
|
||||
*/
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
Service s;
|
||||
} NsDocumentInterface;
|
||||
|
||||
/* Command forwarders. */
|
||||
Result nsGetDocumentInterfaceFwd(Service* s, NsDocumentInterface* out);
|
||||
|
||||
Result nsamGetApplicationContentPathFwd(Service* s, void* out, size_t out_size, u64 app_id, NcmContentType content_type);
|
||||
Result nsamResolveApplicationContentPathFwd(Service* s, u64 app_id, NcmContentType content_type);
|
||||
Result nsamGetRunningApplicationProgramIdFwd(Service* s, u64* out_program_id, u64 app_id);
|
||||
|
||||
Result nswebGetApplicationContentPath(NsDocumentInterface* doc, void* out, size_t out_size, u8 *out_attr, u64 app_id, NcmContentType content_type);
|
||||
Result nswebResolveApplicationContentPath(NsDocumentInterface* doc, u64 app_id, NcmContentType content_type);
|
||||
Result nswebGetRunningApplicationProgramId(NsDocumentInterface* doc, u64* out_program_id, u64 app_id);
|
||||
Result nswebGetApplicationContentPath2(NsDocumentInterface* doc, void* out, size_t out_size, u64* out_program_id, u8 *out_attr, u64 app_id, NcmContentType content_type);
|
||||
|
||||
void nsDocumentInterfaceClose(NsDocumentInterface* doc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_fs_utils.hpp"
|
||||
#include "ns_web_mitm_service.hpp"
|
||||
|
||||
namespace ams::mitm::ns {
|
||||
|
||||
Result NsDocumentService::GetApplicationContentPath(const sf::OutBuffer &out_path, sf::Out<ams::fs::ContentAttributes> out_attr, ncm::ProgramId application_id, u8 content_type) {
|
||||
static_assert(sizeof(*out_attr.GetPointer()) == sizeof(u8));
|
||||
R_RETURN(nswebGetApplicationContentPath(m_srv.get(), out_path.GetPointer(), out_path.GetSize(), reinterpret_cast<u8 *>(out_attr.GetPointer()), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type)));
|
||||
}
|
||||
|
||||
Result NsDocumentService::ResolveApplicationContentPath(ncm::ProgramId application_id, u8 content_type) {
|
||||
/* Always succeed for web applets asking about HBL to enable hbl_html, and applications with manual_html to allow custom manual data. */
|
||||
bool is_hbl = false;
|
||||
if ((R_SUCCEEDED(ams::pm::info::IsHblProgramId(std::addressof(is_hbl), application_id)) && is_hbl) || (static_cast<ncm::ContentType>(content_type) == ncm::ContentType::HtmlDocument && mitm::fs::HasSdManualHtmlContent(application_id))) {
|
||||
nswebResolveApplicationContentPath(m_srv.get(), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type));
|
||||
R_SUCCEED();
|
||||
}
|
||||
R_RETURN(nswebResolveApplicationContentPath(m_srv.get(), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type)));
|
||||
}
|
||||
|
||||
Result NsDocumentService::GetRunningApplicationProgramId(sf::Out<ncm::ProgramId> out, ncm::ProgramId application_id) {
|
||||
R_RETURN(nswebGetRunningApplicationProgramId(m_srv.get(), reinterpret_cast<u64 *>(out.GetPointer()), static_cast<u64>(application_id)));
|
||||
}
|
||||
|
||||
Result NsDocumentService::GetApplicationContentPath2(const sf::OutBuffer &out_path, sf::Out<ncm::ProgramId> out_program_id, sf::Out<ams::fs::ContentAttributes> out_attr, ncm::ProgramId application_id, u8 content_type) {
|
||||
static_assert(sizeof(*out_attr.GetPointer()) == sizeof(u8));
|
||||
R_RETURN(nswebGetApplicationContentPath2(m_srv.get(), out_path.GetPointer(), out_path.GetSize(), reinterpret_cast<u64 *>(out_program_id.GetPointer()), reinterpret_cast<u8 *>(out_attr.GetPointer()), static_cast<u64>(application_id), static_cast<NcmContentType>(content_type)));
|
||||
}
|
||||
|
||||
Result NsWebMitmService::GetDocumentInterface(sf::Out<sf::SharedPointer<impl::IDocumentInterface>> out) {
|
||||
/* Open a document interface. */
|
||||
NsDocumentInterface doc;
|
||||
R_TRY(nsGetDocumentInterfaceFwd(m_forward_service.get(), std::addressof(doc)));
|
||||
const sf::cmif::DomainObjectId target_object_id{serviceGetObjectId(std::addressof(doc.s))};
|
||||
|
||||
out.SetValue(sf::CreateSharedObjectEmplaced<impl::IDocumentInterface, NsDocumentService>(m_client_info, std::make_unique<NsDocumentInterface>(doc)), target_object_id);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "ns_shim.h"
|
||||
|
||||
#define AMS_NS_DOCUMENT_MITM_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 21, Result, GetApplicationContentPath, (const sf::OutBuffer &out_path, sf::Out<ams::fs::ContentAttributes> out_attr, ncm::ProgramId application_id, u8 content_type), (out_path, out_attr, application_id, content_type)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 23, Result, ResolveApplicationContentPath, (ncm::ProgramId application_id, u8 content_type), (application_id, content_type)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 92, Result, GetRunningApplicationProgramId, (sf::Out<ncm::ProgramId> out, ncm::ProgramId application_id), (out, application_id), hos::Version_6_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2524, Result, GetApplicationContentPath2, (const sf::OutBuffer &out_path, sf::Out<ncm::ProgramId> out_program_id, sf::Out<ams::fs::ContentAttributes> out_attr, ncm::ProgramId application_id, u8 content_type), (out_path, out_program_id, out_attr, application_id, content_type), hos::Version_19_0_0)
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::mitm::ns::impl, IDocumentInterface, AMS_NS_DOCUMENT_MITM_INTERFACE_INFO, 0x0F9B1C00)
|
||||
|
||||
#define AMS_NS_WEB_MITM_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 7999, Result, GetDocumentInterface, (sf::Out<sf::SharedPointer<mitm::ns::impl::IDocumentInterface>> out), (out))
|
||||
|
||||
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::ns::impl, IWebMitmInterface, AMS_NS_WEB_MITM_INTERFACE_INFO, 0xF4EC2D1A)
|
||||
|
||||
namespace ams::mitm::ns {
|
||||
|
||||
class NsDocumentService {
|
||||
private:
|
||||
sm::MitmProcessInfo m_client_info;
|
||||
std::unique_ptr<::NsDocumentInterface> m_srv;
|
||||
public:
|
||||
NsDocumentService(const sm::MitmProcessInfo &cl, std::unique_ptr<::NsDocumentInterface> s) : m_client_info(cl), m_srv(std::move(s)) { /* .. */ }
|
||||
|
||||
virtual ~NsDocumentService() {
|
||||
nsDocumentInterfaceClose(m_srv.get());
|
||||
}
|
||||
public:
|
||||
/* Actual command API. */
|
||||
Result GetApplicationContentPath(const sf::OutBuffer &out_path, sf::Out<ams::fs::ContentAttributes> out_attr, ncm::ProgramId application_id, u8 content_type);
|
||||
Result ResolveApplicationContentPath(ncm::ProgramId application_id, u8 content_type);
|
||||
Result GetRunningApplicationProgramId(sf::Out<ncm::ProgramId> out, ncm::ProgramId application_id);
|
||||
Result GetApplicationContentPath2(const sf::OutBuffer &out_path, sf::Out<ncm::ProgramId> out_program_id, sf::Out<ams::fs::ContentAttributes> out_attr, ncm::ProgramId application_id, u8 content_type);
|
||||
};
|
||||
static_assert(impl::IsIDocumentInterface<NsDocumentService>);
|
||||
|
||||
class NsWebMitmService : public sf::MitmServiceImplBase {
|
||||
public:
|
||||
using MitmServiceImplBase::MitmServiceImplBase;
|
||||
public:
|
||||
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
|
||||
/* We will mitm:
|
||||
* - web applets, to facilitate hbl web browser launching.
|
||||
*/
|
||||
return ncm::IsWebAppletId(client_info.program_id);
|
||||
}
|
||||
public:
|
||||
Result GetDocumentInterface(sf::Out<sf::SharedPointer<impl::IDocumentInterface>> out);
|
||||
};
|
||||
static_assert(impl::IsIWebMitmInterface<NsWebMitmService>);
|
||||
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "nsmitm_module.hpp"
|
||||
#include "ns_am_mitm_service.hpp"
|
||||
#include "ns_web_mitm_service.hpp"
|
||||
|
||||
namespace ams::mitm::ns {
|
||||
|
||||
namespace {
|
||||
|
||||
enum PortIndex {
|
||||
PortIndex_Mitm,
|
||||
PortIndex_Count,
|
||||
};
|
||||
|
||||
constexpr sm::ServiceName NsAmMitmServiceName = sm::ServiceName::Encode("ns:am");
|
||||
constexpr sm::ServiceName NsWebMitmServiceName = sm::ServiceName::Encode("ns:web");
|
||||
|
||||
constexpr size_t MaxSessions = 5;
|
||||
|
||||
struct ServerOptions {
|
||||
static constexpr size_t PointerBufferSize = sf::hipc::DefaultServerManagerOptions::PointerBufferSize;
|
||||
static constexpr size_t MaxDomains = sf::hipc::DefaultServerManagerOptions::MaxDomains;
|
||||
static constexpr size_t MaxDomainObjects = sf::hipc::DefaultServerManagerOptions::MaxDomainObjects;
|
||||
static constexpr bool CanDeferInvokeRequest = sf::hipc::DefaultServerManagerOptions::CanDeferInvokeRequest;
|
||||
static constexpr bool CanManageMitmServers = true;
|
||||
};
|
||||
|
||||
class ServerManager final : public sf::hipc::ServerManager<PortIndex_Count, ServerOptions, MaxSessions> {
|
||||
private:
|
||||
virtual Result OnNeedsToAccept(int port_index, Server *server) override;
|
||||
};
|
||||
|
||||
ServerManager g_server_manager;
|
||||
|
||||
Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
|
||||
/* Acknowledge the mitm session. */
|
||||
std::shared_ptr<::Service> fsrv;
|
||||
sm::MitmProcessInfo client_info;
|
||||
server->AcknowledgeMitmSession(std::addressof(fsrv), std::addressof(client_info));
|
||||
|
||||
switch (port_index) {
|
||||
case PortIndex_Mitm:
|
||||
if (hos::GetVersion() < hos::Version_3_0_0) {
|
||||
R_RETURN(this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<impl::IAmMitmInterface, NsAmMitmService>(decltype(fsrv)(fsrv), client_info), fsrv));
|
||||
} else {
|
||||
R_RETURN(this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<impl::IWebMitmInterface, NsWebMitmService>(decltype(fsrv)(fsrv), client_info), fsrv));
|
||||
}
|
||||
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MitmModule::ThreadFunction(void *) {
|
||||
/* Wait until initialization is complete. */
|
||||
mitm::WaitInitialized();
|
||||
|
||||
/* Create mitm servers. */
|
||||
if (hos::GetVersion() < hos::Version_3_0_0) {
|
||||
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<NsAmMitmService>(PortIndex_Mitm, NsAmMitmServiceName)));
|
||||
} else {
|
||||
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<NsWebMitmService>(PortIndex_Mitm, NsWebMitmServiceName)));
|
||||
}
|
||||
|
||||
/* Loop forever, servicing our services. */
|
||||
g_server_manager.LoopProcess();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_module.hpp"
|
||||
|
||||
namespace ams::mitm::ns {
|
||||
|
||||
DEFINE_MITM_MODULE_CLASS(0x4000, AMS_GET_SYSTEM_THREAD_PRIORITY(ns, ApplicationManagerIpcSession) - 1);
|
||||
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "set_mitm_service.hpp"
|
||||
#include "set_shim.h"
|
||||
|
||||
namespace ams::mitm::settings {
|
||||
|
||||
using namespace ams::settings;
|
||||
|
||||
namespace {
|
||||
|
||||
constinit os::ProcessId g_application_process_id = os::InvalidProcessId;
|
||||
constinit cfg::OverrideLocale g_application_locale;
|
||||
constinit bool g_valid_language;
|
||||
constinit bool g_valid_region;
|
||||
}
|
||||
|
||||
SetMitmService::SetMitmService(std::shared_ptr<::Service> &&s, const sm::MitmProcessInfo &c) : sf::MitmServiceImplBase(std::forward<std::shared_ptr<::Service>>(s), c) {
|
||||
if (m_client_info.program_id == ncm::SystemProgramId::Ns) {
|
||||
os::ProcessId application_process_id;
|
||||
if (R_SUCCEEDED(ams::pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id))) && g_application_process_id == application_process_id) {
|
||||
m_locale = g_application_locale;
|
||||
m_is_valid_language = g_valid_language;
|
||||
m_is_valid_region = g_valid_region;
|
||||
m_got_locale = true;
|
||||
} else {
|
||||
this->InvalidateLocale();
|
||||
}
|
||||
} else {
|
||||
this->InvalidateLocale();
|
||||
}
|
||||
}
|
||||
|
||||
Result SetMitmService::EnsureLocale() {
|
||||
/* Optimization: if locale has already been gotten, we can stop. */
|
||||
if (AMS_LIKELY(m_got_locale)) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
std::scoped_lock lk(m_lock);
|
||||
|
||||
const bool is_ns = m_client_info.program_id == ncm::SystemProgramId::Ns;
|
||||
|
||||
if (!m_got_locale) {
|
||||
ncm::ProgramId program_id = m_client_info.program_id;
|
||||
os::ProcessId application_process_id = os::InvalidProcessId;
|
||||
|
||||
if (is_ns) {
|
||||
/* When NS asks for a locale, refresh to get the current application locale. */
|
||||
R_TRY(ams::pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id)));
|
||||
R_TRY(ams::pm::info::GetProgramId(std::addressof(program_id), application_process_id));
|
||||
}
|
||||
m_locale = cfg::GetOverrideLocale(program_id);
|
||||
m_is_valid_language = settings::IsValidLanguageCode(m_locale.language_code);
|
||||
m_is_valid_region = settings::IsValidRegionCode(m_locale.region_code);
|
||||
m_got_locale = true;
|
||||
|
||||
if (is_ns) {
|
||||
g_application_locale = m_locale;
|
||||
g_valid_language = m_is_valid_language;
|
||||
g_valid_region = m_is_valid_region;
|
||||
g_application_process_id = application_process_id;
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SetMitmService::InvalidateLocale() {
|
||||
std::scoped_lock lk(m_lock);
|
||||
|
||||
std::memset(std::addressof(m_locale), 0xCC, sizeof(m_locale));
|
||||
m_is_valid_language = false;
|
||||
m_is_valid_region = false;
|
||||
m_got_locale = false;
|
||||
}
|
||||
|
||||
Result SetMitmService::GetLanguageCode(sf::Out<settings::LanguageCode> out) {
|
||||
this->EnsureLocale();
|
||||
|
||||
/* If there's no override locale, just use the actual one. */
|
||||
if (AMS_UNLIKELY(!m_is_valid_language)) {
|
||||
static_assert(sizeof(u64) == sizeof(settings::LanguageCode));
|
||||
R_TRY(setGetLanguageCodeFwd(m_forward_service.get(), reinterpret_cast<u64 *>(std::addressof(m_locale.language_code))));
|
||||
|
||||
m_is_valid_language = true;
|
||||
if (m_client_info.program_id == ncm::SystemProgramId::Ns) {
|
||||
g_application_locale.language_code = m_locale.language_code;
|
||||
g_valid_language = true;
|
||||
}
|
||||
}
|
||||
|
||||
out.SetValue(m_locale.language_code);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetMitmService::GetRegionCode(sf::Out<settings::RegionCode> out) {
|
||||
this->EnsureLocale();
|
||||
|
||||
/* If there's no override locale, just use the actual one. */
|
||||
if (AMS_UNLIKELY(!m_is_valid_region)) {
|
||||
static_assert(sizeof(::SetRegion) == sizeof(settings::RegionCode));
|
||||
R_TRY(setGetRegionCodeFwd(m_forward_service.get(), reinterpret_cast<::SetRegion *>(std::addressof(m_locale.region_code))));
|
||||
|
||||
m_is_valid_region = true;
|
||||
if (m_client_info.program_id == ncm::SystemProgramId::Ns) {
|
||||
g_application_locale.region_code = m_locale.region_code;
|
||||
g_valid_region = true;
|
||||
}
|
||||
}
|
||||
|
||||
out.SetValue(m_locale.region_code);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#define AMS_SETTINGS_MITM_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, GetLanguageCode, (sf::Out<ams::settings::LanguageCode> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, GetRegionCode, (sf::Out<ams::settings::RegionCode> out), (out))
|
||||
|
||||
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::settings, ISetMitmInterface, AMS_SETTINGS_MITM_INTERFACE_INFO, 0x7F7BAF0A)
|
||||
|
||||
namespace ams::mitm::settings {
|
||||
|
||||
class SetMitmService : public sf::MitmServiceImplBase {
|
||||
private:
|
||||
os::SdkMutex m_lock{};
|
||||
cfg::OverrideLocale m_locale;
|
||||
bool m_got_locale = false;
|
||||
bool m_is_valid_language = false;
|
||||
bool m_is_valid_region = false;
|
||||
public:
|
||||
SetMitmService(std::shared_ptr<::Service> &&s, const sm::MitmProcessInfo &c);
|
||||
public:
|
||||
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
|
||||
/* We will mitm:
|
||||
* - ns and games, to allow for overriding game locales.
|
||||
*/
|
||||
const bool is_game = (ncm::IsApplicationId(client_info.program_id) && !client_info.override_status.IsHbl());
|
||||
return client_info.program_id == ncm::SystemProgramId::Ns || is_game;
|
||||
}
|
||||
private:
|
||||
void InvalidateLocale();
|
||||
Result EnsureLocale();
|
||||
public:
|
||||
Result GetLanguageCode(sf::Out<ams::settings::LanguageCode> out);
|
||||
Result GetRegionCode(sf::Out<ams::settings::RegionCode> out);
|
||||
};
|
||||
static_assert(IsISetMitmInterface<SetMitmService>);
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "set_shim.h"
|
||||
|
||||
static Result _setCmdNoInOut64(Service* srv, u64 *out, u32 cmd_id) {
|
||||
return serviceDispatchOut(srv, cmd_id, *out);
|
||||
}
|
||||
|
||||
static Result _setCmdNoInOutU32(Service* srv, u32 *out, u32 cmd_id) {
|
||||
return serviceDispatchOut(srv, cmd_id, *out);
|
||||
}
|
||||
|
||||
/* Forwarding shims. */
|
||||
Result setGetLanguageCodeFwd(Service *s, u64* out) {
|
||||
return _setCmdNoInOut64(s, out, 0);
|
||||
}
|
||||
|
||||
Result setGetRegionCodeFwd(Service *s, SetRegion *out) {
|
||||
s32 code=0;
|
||||
Result rc = _setCmdNoInOutU32(s, (u32*)&code, 4);
|
||||
if (R_SUCCEEDED(rc) && out) *out = code;
|
||||
return rc;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* @file set_shim.h
|
||||
* @brief Settings Services (fs) IPC wrapper for set.mitm.
|
||||
* @author SciresM
|
||||
* @copyright libnx Authors
|
||||
*/
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Forwarding shims. */
|
||||
Result setGetLanguageCodeFwd(Service *s, u64* out);
|
||||
Result setGetRegionCodeFwd(Service *s, SetRegion *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "setmitm_module.hpp"
|
||||
#include "set_mitm_service.hpp"
|
||||
#include "setsys_mitm_service.hpp"
|
||||
|
||||
namespace ams::mitm::settings {
|
||||
|
||||
namespace {
|
||||
|
||||
enum PortIndex {
|
||||
PortIndex_SetMitm,
|
||||
PortIndex_SetSysMitm,
|
||||
PortIndex_Count,
|
||||
};
|
||||
|
||||
constexpr sm::ServiceName SetMitmServiceName = sm::ServiceName::Encode("set");
|
||||
constexpr sm::ServiceName SetSysMitmServiceName = sm::ServiceName::Encode("set:sys");
|
||||
|
||||
struct ServerOptions {
|
||||
static constexpr size_t PointerBufferSize = 0x200;
|
||||
static constexpr size_t MaxDomains = 0;
|
||||
static constexpr size_t MaxDomainObjects = 0;
|
||||
static constexpr bool CanDeferInvokeRequest = false;
|
||||
static constexpr bool CanManageMitmServers = true;
|
||||
};
|
||||
|
||||
constexpr size_t MaxSessions = 60;
|
||||
|
||||
class ServerManager final : public sf::hipc::ServerManager<PortIndex_Count, ServerOptions, MaxSessions> {
|
||||
private:
|
||||
virtual Result OnNeedsToAccept(int port_index, Server *server) override;
|
||||
};
|
||||
|
||||
ServerManager g_server_manager;
|
||||
|
||||
Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
|
||||
/* Acknowledge the mitm session. */
|
||||
std::shared_ptr<::Service> fsrv;
|
||||
sm::MitmProcessInfo client_info;
|
||||
server->AcknowledgeMitmSession(std::addressof(fsrv), std::addressof(client_info));
|
||||
|
||||
switch (port_index) {
|
||||
case PortIndex_SetMitm:
|
||||
R_RETURN(this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<ISetMitmInterface, SetMitmService>(decltype(fsrv)(fsrv), client_info), fsrv));
|
||||
case PortIndex_SetSysMitm:
|
||||
R_RETURN(this->AcceptMitmImpl(server, sf::CreateSharedObjectEmplaced<ISetSysMitmInterface, SetSysMitmService>(decltype(fsrv)(fsrv), client_info), fsrv));
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MitmModule::ThreadFunction(void *) {
|
||||
/* Wait until initialization is complete. */
|
||||
mitm::WaitInitialized();
|
||||
|
||||
/* Create mitm servers. */
|
||||
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<SetMitmService>(PortIndex_SetMitm, SetMitmServiceName)));
|
||||
R_ABORT_UNLESS((g_server_manager.RegisterMitmServer<SetSysMitmService>(PortIndex_SetSysMitm, SetSysMitmServiceName)));
|
||||
|
||||
/* Loop forever, servicing our services. */
|
||||
g_server_manager.LoopProcess();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_module.hpp"
|
||||
|
||||
namespace ams::mitm::settings {
|
||||
|
||||
DEFINE_MITM_MODULE_CLASS(0x8000, AMS_GET_SYSTEM_THREAD_PRIORITY(settings, IpcServer) - 1);
|
||||
|
||||
}
|
||||
@@ -1,272 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "setsys_mitm_service.hpp"
|
||||
#include "settings_sd_kvs.hpp"
|
||||
#include "setsys_shim.h"
|
||||
|
||||
namespace ams::mitm::settings {
|
||||
|
||||
using namespace ams::settings;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const char ExternalBluetoothDatabasePath[] = "@Sdcard:/atmosphere/bluetooth_devices.db";
|
||||
|
||||
constinit os::SdkMutex g_firmware_version_lock;
|
||||
constinit bool g_cached_firmware_version;
|
||||
constinit settings::FirmwareVersion g_firmware_version;
|
||||
constinit settings::FirmwareVersion g_ams_firmware_version;
|
||||
|
||||
void CacheFirmwareVersion() {
|
||||
if (AMS_LIKELY(g_cached_firmware_version)) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::scoped_lock lk(g_firmware_version_lock);
|
||||
|
||||
if (AMS_UNLIKELY(g_cached_firmware_version)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Mount firmware version data archive. */
|
||||
{
|
||||
R_ABORT_UNLESS(ams::fs::MountSystemData("sysver", ncm::SystemDataId::SystemVersion));
|
||||
ON_SCOPE_EXIT { ams::fs::Unmount("sysver"); };
|
||||
|
||||
/* Firmware version file must exist. */
|
||||
ams::fs::FileHandle file;
|
||||
R_ABORT_UNLESS(ams::fs::OpenFile(std::addressof(file), "sysver:/file", fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { ams::fs::CloseFile(file); };
|
||||
|
||||
/* Must be possible to read firmware version from file. */
|
||||
R_ABORT_UNLESS(ams::fs::ReadFile(file, 0, std::addressof(g_firmware_version), sizeof(g_firmware_version)));
|
||||
|
||||
g_ams_firmware_version = g_firmware_version;
|
||||
}
|
||||
|
||||
/* Modify the atmosphere firmware version to display a custom version string. */
|
||||
{
|
||||
const auto api_info = exosphere::GetApiInfo();
|
||||
const char emummc_char = emummc::IsActive() ? 'E' : 'S';
|
||||
|
||||
/* NOTE: We have carefully accounted for the size of the string we print. */
|
||||
/* No truncation occurs assuming two-digits for all version number components. */
|
||||
char display_version[sizeof(g_ams_firmware_version.display_version)];
|
||||
|
||||
util::SNPrintf(display_version, sizeof(display_version), "%s|AMS %u.%u.%u|%c", g_ams_firmware_version.display_version, api_info.GetMajorVersion(), api_info.GetMinorVersion(), api_info.GetMicroVersion(), emummc_char);
|
||||
|
||||
std::memcpy(g_ams_firmware_version.display_version, display_version, sizeof(display_version));
|
||||
}
|
||||
|
||||
g_cached_firmware_version = true;
|
||||
}
|
||||
|
||||
Result GetFirmwareVersionImpl(settings::FirmwareVersion *out, const sm::MitmProcessInfo &client_info) {
|
||||
/* Ensure that we have the firmware version cached. */
|
||||
CacheFirmwareVersion();
|
||||
|
||||
/* We want to give a special firmware version to the home menu title, and nothing else. */
|
||||
/* This means qlaunch + maintenance menu, and nothing else. */
|
||||
if (client_info.program_id == ncm::SystemAppletId::Qlaunch || client_info.program_id == ncm::SystemAppletId::MaintenanceMenu) {
|
||||
*out = g_ams_firmware_version;
|
||||
} else {
|
||||
*out = g_firmware_version;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
bool IsExternalBluetoothDatabaseEnabled() {
|
||||
u8 en = 0;
|
||||
settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "enable_external_bluetooth_db");
|
||||
return en;
|
||||
}
|
||||
|
||||
bool HasExternalBluetoothDatabase() {
|
||||
bool file_exists;
|
||||
R_ABORT_UNLESS(fs::HasFile(std::addressof(file_exists), ExternalBluetoothDatabasePath));
|
||||
return file_exists;
|
||||
}
|
||||
|
||||
Result ReadExternalBluetoothDatabase(s32 *entries_read, settings::BluetoothDevicesSettings *db, size_t db_max_size) {
|
||||
/* Open the external database file. */
|
||||
fs::FileHandle file;
|
||||
R_TRY(fs::OpenFile(std::addressof(file), ExternalBluetoothDatabasePath, fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
/* Read the number of database entries stored in external database. */
|
||||
u64 total_entries;
|
||||
R_TRY(fs::ReadFile(file, 0, std::addressof(total_entries), sizeof(total_entries)));
|
||||
|
||||
u64 db_offset = sizeof(total_entries);
|
||||
if (total_entries > db_max_size) {
|
||||
/* Pairings are stored from least to most recent. Add offset to skip the older entries that won't fit. */
|
||||
db_offset += (total_entries - db_max_size) * sizeof(settings::BluetoothDevicesSettings);
|
||||
|
||||
/* Cap number of database entries read to size of database on this firmware. */
|
||||
total_entries = db_max_size;
|
||||
}
|
||||
|
||||
/* Read database entries. */
|
||||
R_TRY(fs::ReadFile(file, db_offset, db, total_entries * sizeof(settings::BluetoothDevicesSettings)));
|
||||
|
||||
/* Convert entries to the old format if running on a firmware below 13.0.0. */
|
||||
if (hos::GetVersion() < hos::Version_13_0_0) {
|
||||
for (size_t i = 0; i < total_entries; ++i) {
|
||||
/* Copy the newer name field to the older one. */
|
||||
util::TSNPrintf(db[i].name, sizeof(db[i].name), "%s", db[i].name2);
|
||||
|
||||
/* Clear the newer name field. */
|
||||
std::memset(db[i].name2, 0, sizeof(db[i].name2));
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the output. */
|
||||
*entries_read = static_cast<s32>(total_entries);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result StoreExternalBluetoothDatabase(const settings::BluetoothDevicesSettings *db, u64 total_entries) {
|
||||
/* Open the external database file. */
|
||||
fs::FileHandle file;
|
||||
R_TRY(fs::OpenFile(std::addressof(file), ExternalBluetoothDatabasePath, fs::OpenMode_Write));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
/* Ensure the file is the appropriate size for the number of entries. */
|
||||
R_TRY(fs::SetFileSize(file, sizeof(total_entries) + total_entries * sizeof(settings::BluetoothDevicesSettings)));
|
||||
|
||||
/* Write the number of database entries. */
|
||||
R_TRY(fs::WriteFile(file, 0, std::addressof(total_entries), sizeof(total_entries), fs::WriteOption::None));
|
||||
|
||||
/* Write the database entries. */
|
||||
u64 db_offset = sizeof(total_entries);
|
||||
if (hos::GetVersion() < hos::Version_13_0_0) {
|
||||
/* Convert entries to the new format if running on a firmware below 13.0.0. */
|
||||
for (size_t i = 0; i < total_entries; ++i) {
|
||||
/* Make a copy of the current database entry. */
|
||||
settings::BluetoothDevicesSettings tmp = db[i];
|
||||
|
||||
/* Copy the older name field to the newer one. */
|
||||
util::TSNPrintf(tmp.name2, sizeof(tmp.name2), "%s", tmp.name);
|
||||
|
||||
/* Clear the original name field. */
|
||||
std::memset(tmp.name, 0, sizeof(tmp.name));
|
||||
|
||||
/* Write the converted database entry. */
|
||||
R_TRY(fs::WriteFile(file, db_offset, std::addressof(tmp), sizeof(settings::BluetoothDevicesSettings), fs::WriteOption::None));
|
||||
|
||||
/* Advance to the next database entry. */
|
||||
db_offset += sizeof(settings::BluetoothDevicesSettings);
|
||||
}
|
||||
|
||||
/* Flush the data we've written. */
|
||||
R_TRY(fs::FlushFile(file));
|
||||
} else {
|
||||
/* We can just write the database to the file. */
|
||||
R_TRY(fs::WriteFile(file, db_offset, db, total_entries * sizeof(settings::BluetoothDevicesSettings), fs::WriteOption::Flush));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result SetSysMitmService::GetFirmwareVersion(sf::Out<settings::FirmwareVersion> out) {
|
||||
R_TRY(GetFirmwareVersionImpl(out.GetPointer(), m_client_info));
|
||||
|
||||
/* GetFirmwareVersion sanitizes the revision fields. */
|
||||
out.GetPointer()->revision_major = 0;
|
||||
out.GetPointer()->revision_minor = 0;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetSysMitmService::GetFirmwareVersion2(sf::Out<settings::FirmwareVersion> out) {
|
||||
R_RETURN(GetFirmwareVersionImpl(out.GetPointer(), m_client_info));
|
||||
}
|
||||
|
||||
Result SetSysMitmService::SetBluetoothDevicesSettings(const sf::InMapAliasArray<settings::BluetoothDevicesSettings> &settings) {
|
||||
/* We only want to perform additional logic when the external database setting is enabled. */
|
||||
R_UNLESS(IsExternalBluetoothDatabaseEnabled(), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Create the external database if it doesn't exist. */
|
||||
if (!HasExternalBluetoothDatabase()) {
|
||||
R_TRY(fs::CreateFile(ExternalBluetoothDatabasePath, 0));
|
||||
}
|
||||
|
||||
/* Backup the local database to the sd card. */
|
||||
R_TRY(StoreExternalBluetoothDatabase(settings.GetPointer(), settings.GetSize()));
|
||||
|
||||
/* Ensure that the updated database is stored to the system save as usual. */
|
||||
static_assert(sizeof(settings::BluetoothDevicesSettings) == sizeof(::SetSysBluetoothDevicesSettings));
|
||||
R_TRY(setsysSetBluetoothDevicesSettingsFwd(m_forward_service.get(), reinterpret_cast<const ::SetSysBluetoothDevicesSettings *>(settings.GetPointer()), settings.GetSize()));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetSysMitmService::GetBluetoothDevicesSettings(sf::Out<s32> out_count, const sf::OutMapAliasArray<settings::BluetoothDevicesSettings> &out) {
|
||||
/* We only want to perform additional logic when the external database setting is enabled. */
|
||||
R_UNLESS(IsExternalBluetoothDatabaseEnabled(), sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
if (!HasExternalBluetoothDatabase()) {
|
||||
/* Fetch the local database from the system save. */
|
||||
static_assert(sizeof(settings::BluetoothDevicesSettings) == sizeof(::SetSysBluetoothDevicesSettings));
|
||||
R_TRY(setsysGetBluetoothDevicesSettingsFwd(m_forward_service.get(), out_count.GetPointer(), reinterpret_cast<::SetSysBluetoothDevicesSettings *>(out.GetPointer()), out.GetSize()));
|
||||
|
||||
/* Create the external database file. */
|
||||
R_TRY(fs::CreateFile(ExternalBluetoothDatabasePath, 0));
|
||||
|
||||
/* Backup the local database to the sd card. */
|
||||
R_TRY(StoreExternalBluetoothDatabase(out.GetPointer(), out_count.GetValue()));
|
||||
} else {
|
||||
/* Read the external database from the sd card. */
|
||||
R_TRY(ReadExternalBluetoothDatabase(out_count.GetPointer(), out.GetPointer(), out.GetSize()));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetSysMitmService::GetSettingsItemValueSize(sf::Out<u64> out_size, const settings::SettingsName &name, const settings::SettingsItemKey &key) {
|
||||
R_TRY_CATCH(settings::fwdbg::GetSdCardKeyValueStoreSettingsItemValueSize(out_size.GetPointer(), name.value, key.value)) {
|
||||
R_CATCH_RETHROW(sf::impl::ResultRequestContextChanged)
|
||||
R_CONVERT_ALL(sm::mitm::ResultShouldForwardToSession());
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetSysMitmService::GetSettingsItemValue(sf::Out<u64> out_size, const sf::OutBuffer &out, const settings::SettingsName &name, const settings::SettingsItemKey &key) {
|
||||
R_TRY_CATCH(settings::fwdbg::GetSdCardKeyValueStoreSettingsItemValue(out_size.GetPointer(), out.GetPointer(), out.GetSize(), name.value, key.value)) {
|
||||
R_CATCH_RETHROW(sf::impl::ResultRequestContextChanged)
|
||||
R_CONVERT_ALL(sm::mitm::ResultShouldForwardToSession());
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetSysMitmService::GetDebugModeFlag(sf::Out<bool> out) {
|
||||
/* If we're not processing for am, just return the real flag value. */
|
||||
R_UNLESS(m_client_info.program_id == ncm::SystemProgramId::Am, sm::mitm::ResultShouldForwardToSession());
|
||||
|
||||
/* Retrieve the user configuration. */
|
||||
u8 en = 0;
|
||||
settings::fwdbg::GetSettingsItemValue(std::addressof(en), sizeof(en), "atmosphere", "enable_am_debug_mode");
|
||||
|
||||
out.SetValue(en != 0);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#define AMS_SETTINGS_SYSTEM_MITM_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 3, Result, GetFirmwareVersion, (sf::Out<ams::settings::FirmwareVersion> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, GetFirmwareVersion2, (sf::Out<ams::settings::FirmwareVersion> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 11, Result, SetBluetoothDevicesSettings, (const sf::InMapAliasArray<ams::settings::BluetoothDevicesSettings> &settings), (settings)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 12, Result, GetBluetoothDevicesSettings, (sf::Out<s32> out_count, const sf::OutMapAliasArray<ams::settings::BluetoothDevicesSettings> &out), (out_count, out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 37, Result, GetSettingsItemValueSize, (sf::Out<u64> out_size, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key), (out_size, name, key)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 38, Result, GetSettingsItemValue, (sf::Out<u64> out_size, const sf::OutBuffer &out, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key), (out_size, out, name, key)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 62, Result, GetDebugModeFlag, (sf::Out<bool> out), (out))
|
||||
|
||||
AMS_SF_DEFINE_MITM_INTERFACE(ams::mitm::settings, ISetSysMitmInterface, AMS_SETTINGS_SYSTEM_MITM_INTERFACE_INFO, 0x0E82ED13)
|
||||
|
||||
namespace ams::mitm::settings {
|
||||
|
||||
class SetSysMitmService : public sf::MitmServiceImplBase {
|
||||
public:
|
||||
using MitmServiceImplBase::MitmServiceImplBase;
|
||||
public:
|
||||
static bool ShouldMitm(const sm::MitmProcessInfo &client_info) {
|
||||
/* We will mitm:
|
||||
* - everything, because we want to intercept all settings requests.
|
||||
*/
|
||||
AMS_UNUSED(client_info);
|
||||
return true;
|
||||
}
|
||||
public:
|
||||
Result GetFirmwareVersion(sf::Out<ams::settings::FirmwareVersion> out);
|
||||
Result GetFirmwareVersion2(sf::Out<ams::settings::FirmwareVersion> out);
|
||||
Result SetBluetoothDevicesSettings(const sf::InMapAliasArray<ams::settings::BluetoothDevicesSettings> &settings);
|
||||
Result GetBluetoothDevicesSettings(sf::Out<s32> out_count, const sf::OutMapAliasArray<ams::settings::BluetoothDevicesSettings> &out);
|
||||
Result GetSettingsItemValueSize(sf::Out<u64> out_size, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key);
|
||||
Result GetSettingsItemValue(sf::Out<u64> out_size, const sf::OutBuffer &out, const ams::settings::SettingsName &name, const ams::settings::SettingsItemKey &key);
|
||||
Result GetDebugModeFlag(sf::Out<bool> out);
|
||||
};
|
||||
static_assert(IsISetSysMitmInterface<SetSysMitmService>);
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "setsys_shim.h"
|
||||
|
||||
Result setsysSetBluetoothDevicesSettingsFwd(Service *s, const SetSysBluetoothDevicesSettings *settings, s32 count) {
|
||||
return serviceDispatch(s, 11,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
|
||||
.buffers = { { settings, count * sizeof(SetSysBluetoothDevicesSettings) } },
|
||||
);
|
||||
}
|
||||
|
||||
Result setsysGetBluetoothDevicesSettingsFwd(Service *s, s32 *total_out, SetSysBluetoothDevicesSettings *settings, s32 count) {
|
||||
return serviceDispatchOut(s, 12, *total_out,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
||||
.buffers = { { settings, count * sizeof(SetSysBluetoothDevicesSettings) } },
|
||||
);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* @file setsys_shim.h
|
||||
* @brief Settings Services (fs) IPC wrapper for setsys.mitm.
|
||||
* @author ndeadly
|
||||
* @copyright libnx Authors
|
||||
*/
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Forwarding shims. */
|
||||
Result setsysSetBluetoothDevicesSettingsFwd(Service *s, const SetSysBluetoothDevicesSettings *settings, s32 count);
|
||||
Result setsysGetBluetoothDevicesSettingsFwd(Service *s, s32 *total_out, SetSysBluetoothDevicesSettings *settings, s32 count);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "settings_sd_kvs.hpp"
|
||||
|
||||
namespace ams::settings::fwdbg {
|
||||
|
||||
size_t GetSettingsItemValueSize(const char *name, const char *key) {
|
||||
u64 size = 0;
|
||||
|
||||
if (R_SUCCEEDED(GetSdCardKeyValueStoreSettingsItemValueSize(&size, name, key))) {
|
||||
return size;
|
||||
}
|
||||
|
||||
R_ABORT_UNLESS(setsysGetSettingsItemValueSize(name, key, &size));
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t GetSettingsItemValue(void *dst, size_t dst_size, const char *name, const char *key) {
|
||||
u64 size = 0;
|
||||
|
||||
if (R_SUCCEEDED(GetSdCardKeyValueStoreSettingsItemValue(&size, dst, dst_size, name, key))) {
|
||||
return size;
|
||||
}
|
||||
|
||||
R_ABORT_UNLESS(setsysGetSettingsItemValue(name, key, dst, dst_size, &size));
|
||||
return size;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,460 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_fs_utils.hpp"
|
||||
#include "settings_sd_kvs.hpp"
|
||||
|
||||
namespace ams::settings::fwdbg {
|
||||
|
||||
namespace {
|
||||
|
||||
struct SdKeyValueStoreEntry {
|
||||
const char *name;
|
||||
const char *key;
|
||||
void *value;
|
||||
size_t value_size;
|
||||
|
||||
constexpr inline bool HasValue() const { return this->value != nullptr; }
|
||||
|
||||
constexpr inline void GetNameAndKey(char *dst) const {
|
||||
size_t offset = 0;
|
||||
for (size_t i = 0; i < std::strlen(this->name); i++) {
|
||||
dst[offset++] = this->name[i];
|
||||
}
|
||||
dst[offset++] = '!';
|
||||
for (size_t i = 0; i < std::strlen(this->key); i++) {
|
||||
dst[offset++] = this->key[i];
|
||||
}
|
||||
dst[offset] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(util::is_pod<SdKeyValueStoreEntry>::value);
|
||||
|
||||
constexpr inline bool operator==(const SdKeyValueStoreEntry &lhs, const SdKeyValueStoreEntry &rhs) {
|
||||
if (lhs.HasValue() != rhs.HasValue()) {
|
||||
return false;
|
||||
}
|
||||
return lhs.HasValue() && std::strcmp(lhs.name, rhs.name) == 0 && std::strcmp(lhs.key, rhs.key) == 0;
|
||||
}
|
||||
|
||||
inline bool operator<(const SdKeyValueStoreEntry &lhs, const SdKeyValueStoreEntry &rhs) {
|
||||
AMS_ABORT_UNLESS(lhs.HasValue());
|
||||
AMS_ABORT_UNLESS(rhs.HasValue());
|
||||
|
||||
char lhs_name_key[SettingsNameLengthMax + 1 + SettingsItemKeyLengthMax + 1];
|
||||
char rhs_name_key[SettingsNameLengthMax + 1 + SettingsItemKeyLengthMax + 1];
|
||||
|
||||
lhs.GetNameAndKey(lhs_name_key);
|
||||
rhs.GetNameAndKey(rhs_name_key);
|
||||
|
||||
return std::strcmp(lhs_name_key, rhs_name_key) < 0;
|
||||
}
|
||||
|
||||
constexpr size_t MaxEntries = 0x200;
|
||||
constexpr size_t SettingsItemValueStorageSize = 0x10000;
|
||||
|
||||
SettingsName g_names[MaxEntries];
|
||||
SettingsItemKey g_item_keys[MaxEntries];
|
||||
u8 g_value_storage[SettingsItemValueStorageSize];
|
||||
size_t g_allocated_value_storage_size;
|
||||
|
||||
SdKeyValueStoreEntry g_entries[MaxEntries];
|
||||
size_t g_num_entries;
|
||||
|
||||
constexpr bool IsValidSettingsFormat(const char *str, size_t len) {
|
||||
AMS_ABORT_UNLESS(str != nullptr);
|
||||
|
||||
if (len > 0 && str[len - 1] == '.') {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
const char c = str[i];
|
||||
|
||||
if ('a' <= c && c <= 'z') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ('0' <= c && c <= '9') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '-' || c == '.' || c == '_') {
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool IsHexadecimal(const char *str) {
|
||||
while (*str) {
|
||||
if (std::isxdigit(static_cast<unsigned char>(*str))) {
|
||||
str++;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr inline 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;
|
||||
}
|
||||
|
||||
Result ValidateSettingsName(const char *name) {
|
||||
R_UNLESS(name != nullptr, settings::ResultNullSettingsName());
|
||||
const size_t len = strnlen(name, SettingsNameLengthMax + 1);
|
||||
R_UNLESS(len > 0, settings::ResultEmptySettingsName());
|
||||
R_UNLESS(len <= SettingsNameLengthMax, settings::ResultTooLongSettingsName());
|
||||
R_UNLESS(IsValidSettingsFormat(name, len), settings::ResultInvalidFormatSettingsName());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ValidateSettingsItemKey(const char *key) {
|
||||
R_UNLESS(key != nullptr, settings::ResultNullSettingsName());
|
||||
const size_t len = strnlen(key, SettingsItemKeyLengthMax + 1);
|
||||
R_UNLESS(len > 0, settings::ResultEmptySettingsItemKey());
|
||||
R_UNLESS(len <= SettingsNameLengthMax, settings::ResultTooLongSettingsItemKey());
|
||||
R_UNLESS(IsValidSettingsFormat(key, len), settings::ResultInvalidFormatSettingsItemKey());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result AllocateValue(void **out, size_t size) {
|
||||
R_UNLESS(g_allocated_value_storage_size + size <= sizeof(g_value_storage), settings::ResultSettingsItemValueAllocationFailed());
|
||||
|
||||
*out = g_value_storage + g_allocated_value_storage_size;
|
||||
g_allocated_value_storage_size += size;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FindSettingsName(const char **out, const char *name) {
|
||||
for (auto &stored : g_names) {
|
||||
if (std::strcmp(stored.value, name) == 0) {
|
||||
*out = stored.value;
|
||||
R_SUCCEED();
|
||||
} else if (std::strcmp(stored.value, "") == 0) {
|
||||
*out = stored.value;
|
||||
std::strcpy(stored.value, name);
|
||||
R_SUCCEED();
|
||||
}
|
||||
}
|
||||
R_THROW(settings::ResultSettingsItemKeyAllocationFailed());
|
||||
}
|
||||
|
||||
Result FindSettingsItemKey(const char **out, const char *key) {
|
||||
for (auto &stored : g_item_keys) {
|
||||
if (std::strcmp(stored.value, key) == 0) {
|
||||
*out = stored.value;
|
||||
R_SUCCEED();
|
||||
} else if (std::strcmp(stored.value, "") == 0) {
|
||||
std::strcpy(stored.value, key);
|
||||
*out = stored.value;
|
||||
R_SUCCEED();
|
||||
}
|
||||
}
|
||||
R_THROW(settings::ResultSettingsItemKeyAllocationFailed());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Result ParseSettingsItemIntegralValue(SdKeyValueStoreEntry &out, const char *value_str) {
|
||||
R_TRY(AllocateValue(std::addressof(out.value), sizeof(T)));
|
||||
out.value_size = sizeof(T);
|
||||
|
||||
T value = static_cast<T>(strtoul(value_str, nullptr, 0));
|
||||
std::memcpy(out.value, std::addressof(value), sizeof(T));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetEntry(SdKeyValueStoreEntry **out, const char *name, const char *key) {
|
||||
/* Validate name/key. */
|
||||
R_TRY(ValidateSettingsName(name));
|
||||
R_TRY(ValidateSettingsItemKey(key));
|
||||
|
||||
u8 dummy_value = 0;
|
||||
SdKeyValueStoreEntry test_entry { .name = name, .key = key, .value = std::addressof(dummy_value), .value_size = sizeof(dummy_value) };
|
||||
|
||||
auto *begin = g_entries;
|
||||
auto *end = begin + g_num_entries;
|
||||
auto it = std::lower_bound(begin, end, test_entry);
|
||||
R_UNLESS(it != end, settings::ResultSettingsItemNotFound());
|
||||
R_UNLESS(*it == test_entry, settings::ResultSettingsItemNotFound());
|
||||
|
||||
*out = std::addressof(*it);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ParseSettingsItemValueImpl(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;
|
||||
|
||||
R_UNLESS(delimiter != nullptr, settings::ResultInvalidFormatSettingsItemValue());
|
||||
|
||||
while (std::isspace(static_cast<unsigned char>(*type)) && type != delimiter) {
|
||||
type++;
|
||||
}
|
||||
|
||||
const size_t type_len = delimiter - type;
|
||||
const size_t value_len = strlen(value_str);
|
||||
R_UNLESS(type_len > 0, settings::ResultInvalidFormatSettingsItemValue());
|
||||
R_UNLESS(value_len > 0, settings::ResultInvalidFormatSettingsItemValue());
|
||||
|
||||
/* Create new value. */
|
||||
SdKeyValueStoreEntry new_value = {};
|
||||
|
||||
/* Find name and key. */
|
||||
R_TRY(FindSettingsName(std::addressof(new_value.name), name));
|
||||
R_TRY(FindSettingsItemKey(std::addressof(new_value.key), key));
|
||||
|
||||
if (strncasecmp(type, "str", type_len) == 0 || strncasecmp(type, "string", type_len) == 0) {
|
||||
const size_t size = value_len + 1;
|
||||
R_TRY(AllocateValue(std::addressof(new_value.value), size));
|
||||
std::memcpy(new_value.value, value_str, size);
|
||||
new_value.value_size = size;
|
||||
} else if (strncasecmp(type, "hex", type_len) == 0 || strncasecmp(type, "bytes", type_len) == 0) {
|
||||
R_UNLESS(value_len > 0, settings::ResultInvalidFormatSettingsItemValue());
|
||||
R_UNLESS(value_len % 2 == 0, settings::ResultInvalidFormatSettingsItemValue());
|
||||
R_UNLESS(IsHexadecimal(value_str), settings::ResultInvalidFormatSettingsItemValue());
|
||||
|
||||
const size_t size = value_len / 2;
|
||||
R_TRY(AllocateValue(std::addressof(new_value.value), size));
|
||||
new_value.value_size = size;
|
||||
|
||||
u8 *data = reinterpret_cast<u8 *>(new_value.value);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
data[i >> 1] = hextoi(value_str[i]) << (4 * (i & 1));
|
||||
}
|
||||
} else if (strncasecmp(type, "u8", type_len) == 0) {
|
||||
R_TRY((ParseSettingsItemIntegralValue<u8>(new_value, value_str)));
|
||||
} else if (strncasecmp(type, "u16", type_len) == 0) {
|
||||
R_TRY((ParseSettingsItemIntegralValue<u16>(new_value, value_str)));
|
||||
} else if (strncasecmp(type, "u32", type_len) == 0) {
|
||||
R_TRY((ParseSettingsItemIntegralValue<u32>(new_value, value_str)));
|
||||
} else if (strncasecmp(type, "u64", type_len) == 0) {
|
||||
R_TRY((ParseSettingsItemIntegralValue<u64>(new_value, value_str)));
|
||||
} else {
|
||||
R_THROW(settings::ResultInvalidFormatSettingsItemValue());
|
||||
}
|
||||
|
||||
/* Insert the entry. */
|
||||
bool inserted = false;
|
||||
for (auto &entry : g_entries) {
|
||||
if (!entry.HasValue() || entry == new_value) {
|
||||
entry = new_value;
|
||||
inserted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
R_UNLESS(inserted, settings::ResultSettingsItemValueAllocationFailed());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ParseSettingsItemValue(const char *name, const char *key, const char *value) {
|
||||
R_TRY(ValidateSettingsName(name));
|
||||
R_TRY(ValidateSettingsItemKey(key));
|
||||
R_RETURN(ParseSettingsItemValueImpl(name, key, value));
|
||||
}
|
||||
|
||||
static int SystemSettingsIniHandler(void *user, const char *name, const char *key, const char *value) {
|
||||
Result *parse_res = reinterpret_cast<Result *>(user);
|
||||
|
||||
/* Once we fail to parse a value, don't parse further. */
|
||||
if (R_FAILED(*parse_res)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*parse_res = ParseSettingsItemValue(name, key, value);
|
||||
return R_SUCCEEDED(*parse_res) ? 1 : 0;
|
||||
}
|
||||
|
||||
Result LoadSdCardKeyValueStore() {
|
||||
/* Open file. */
|
||||
/* It's okay if the file isn't readable/present, because we already loaded defaults. */
|
||||
std::unique_ptr<ams::fs::fsa::IFile> file;
|
||||
{
|
||||
FsFile f;
|
||||
R_SUCCEED_IF(R_FAILED(ams::mitm::fs::OpenAtmosphereSdFile(std::addressof(f), "/config/system_settings.ini", fs::OpenMode_Read)));
|
||||
file = std::make_unique<ams::fs::RemoteFile>(f);
|
||||
}
|
||||
AMS_ABORT_UNLESS(file != nullptr);
|
||||
|
||||
Result parse_result = ResultSuccess();
|
||||
util::ini::ParseFile(file.get(), std::addressof(parse_result), SystemSettingsIniHandler);
|
||||
R_TRY(parse_result);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void LoadDefaultCustomSettings() {
|
||||
/* Disable uploading error reports to Nintendo. */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("eupld", "upload_enabled", "u8!0x0"));
|
||||
|
||||
/* Enable USB 3.0 superspeed for homebrew */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("usb", "usb30_force_enabled", spl::IsUsb30ForceEnabled() ? "u8!0x1" : "u8!0x0"));
|
||||
|
||||
/* Control whether RO should ease its validation of NROs. */
|
||||
/* (note: this is normally not necessary, and ips patches can be used.) */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("ro", "ease_nro_restriction", "u8!0x1"));
|
||||
|
||||
/* Control whether lm should log to the SD card. */
|
||||
/* Note that this setting does nothing when log manager is not enabled. */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("lm", "enable_sd_card_logging", "u8!0x1"));
|
||||
|
||||
/* Control the output directory for SD card logs. */
|
||||
/* Note that this setting does nothing when log manager is not enabled/sd card logging is not enabled. */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("lm", "sd_card_log_output_directory", "str!atmosphere/binlogs"));
|
||||
|
||||
/* Control whether erpt reports should always be preserved, instead of automatically cleaning periodically. */
|
||||
/* 0 = Disabled, 1 = Enabled */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("erpt", "disable_automatic_report_cleanup", "u8!0x0"));
|
||||
|
||||
/* Atmosphere custom settings. */
|
||||
|
||||
/* Reboot from fatal automatically after some number of milliseconds. */
|
||||
/* If field is not present or 0, fatal will wait indefinitely for user input. */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "fatal_auto_reboot_interval", "u64!0x0"));
|
||||
|
||||
/* Make the power menu's "reboot" button reboot to payload. */
|
||||
/* Set to "normal" for normal reboot, "rcm" for rcm reboot. */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "power_menu_reboot_function", "str!payload"));
|
||||
|
||||
/* Enable writing to BIS partitions for HBL. */
|
||||
/* This is probably undesirable for normal usage. */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_hbl_bis_write", "u8!0x0"));
|
||||
|
||||
/* Controls whether dmnt cheats should be toggled on or off by */
|
||||
/* default. 1 = toggled on by default, 0 = toggled off by default. */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "dmnt_cheats_enabled_by_default", "u8!0x1"));
|
||||
|
||||
/* Controls whether dmnt should always save cheat toggle state */
|
||||
/* for restoration on new game launch. 1 = always save toggles, */
|
||||
/* 0 = only save toggles if toggle file exists. */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "dmnt_always_save_cheat_toggles", "u8!0x0"));
|
||||
|
||||
/* Controls whether fs.mitm should redirect save files */
|
||||
/* to directories on the sd card. */
|
||||
/* 0 = Do not redirect, 1 = Redirect. */
|
||||
/* NOTE: EXPERIMENTAL */
|
||||
/* If you do not know what you are doing, do not touch this yet. */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "fsmitm_redirect_saves_to_sd", "u8!0x0"));
|
||||
|
||||
/* Controls whether am sees system settings "DebugModeFlag" as */
|
||||
/* enabled or disabled. */
|
||||
/* 0 = Disabled (not debug mode), 1 = Enabled (debug mode) */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_am_debug_mode", "u8!0x0"));
|
||||
|
||||
/* Controls whether dns.mitm is enabled. */
|
||||
/* 0 = Disabled, 1 = Enabled */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_dns_mitm", "u8!0x1"));
|
||||
|
||||
/* Controls whether dns.mitm uses the default redirections in addition to */
|
||||
/* whatever is specified in the user's hosts file. */
|
||||
/* 0 = Disabled (use hosts file contents), 1 = Enabled (use defaults and hosts file contents) */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "add_defaults_to_dns_hosts", "u8!0x1"));
|
||||
|
||||
/* Controls whether dns.mitm logs to the sd card for debugging. */
|
||||
/* 0 = Disabled, 1 = Enabled */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_dns_mitm_debug_log", "u8!0x0"));
|
||||
|
||||
/* Controls whether htc is enabled. */
|
||||
/* TODO: Change this to default 1 when tma2 is ready for inclusion in atmosphere releases. */
|
||||
/* 0 = Disabled, 1 = Enabled */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_htc", "u8!0x0"));
|
||||
|
||||
/* Controls whether atmosphere's dmnt.gen2 gdbstub should run as a standalone via sockets. */
|
||||
/* Note that this setting is ignored (and treated as 0) when htc is enabled. */
|
||||
/* Note that this setting may disappear in the future. */
|
||||
/* 0 = Disabled, 1 = Enabled */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_standalone_gdbstub", "u8!0x0"));
|
||||
|
||||
/* Controls whether atmosphere's log manager is enabled. */
|
||||
/* Note that this setting is ignored (and treated as 1) when htc is enabled. */
|
||||
/* 0 = Disabled, 1 = Enabled */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_log_manager", "u8!0x0"));
|
||||
|
||||
/* Controls whether the bluetooth pairing database is redirected to the SD card (shared across sysmmc/all emummcs) */
|
||||
/* NOTE: On <13.0.0, the database size was 10 instead of 20; booting pre-13.0.0 will truncate the database. */
|
||||
/* 0 = Disabled, 1 = Enabled */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("atmosphere", "enable_external_bluetooth_db", "u8!0x0"));
|
||||
|
||||
/* Hbloader custom settings. */
|
||||
|
||||
/* Controls the size of the homebrew heap when running as applet. */
|
||||
/* If set to zero, all available applet memory is used as heap. */
|
||||
/* The default is zero. */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("hbloader", "applet_heap_size", "u64!0x0"));
|
||||
|
||||
/* Controls the amount of memory to reserve when running as applet */
|
||||
/* for usage by other applets. This setting has no effect if */
|
||||
/* applet_heap_size is non-zero. The default is 0x8600000. */
|
||||
R_ABORT_UNLESS(ParseSettingsItemValue("hbloader", "applet_heap_reservation_size", "u64!0x8600000"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void InitializeSdCardKeyValueStore() {
|
||||
/* Load in hardcoded defaults. */
|
||||
/* These will be overwritten if present on the SD card. */
|
||||
LoadDefaultCustomSettings();
|
||||
|
||||
/* Parse custom settings off the SD card. */
|
||||
R_ABORT_UNLESS(LoadSdCardKeyValueStore());
|
||||
|
||||
/* Determine how many custom settings are present. */
|
||||
for (size_t i = 0; i < util::size(g_entries); i++) {
|
||||
if (!g_entries[i].HasValue()) {
|
||||
g_num_entries = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure that the custom settings entries are sorted. */
|
||||
if (g_num_entries) {
|
||||
std::sort(g_entries, g_entries + g_num_entries);
|
||||
}
|
||||
}
|
||||
|
||||
Result GetSdCardKeyValueStoreSettingsItemValueSize(size_t *out_size, const char *name, const char *key) {
|
||||
SdKeyValueStoreEntry *entry = nullptr;
|
||||
R_TRY(GetEntry(std::addressof(entry), name, key));
|
||||
|
||||
*out_size = entry->value_size;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetSdCardKeyValueStoreSettingsItemValue(size_t *out_size, void *dst, size_t dst_size, const char *name, const char *key) {
|
||||
R_UNLESS(dst != nullptr, settings::ResultNullSettingsItemValueBuffer());
|
||||
|
||||
SdKeyValueStoreEntry *entry = nullptr;
|
||||
R_TRY(GetEntry(std::addressof(entry), name, key));
|
||||
|
||||
const size_t size = std::min(entry->value_size, dst_size);
|
||||
if (size > 0) {
|
||||
std::memcpy(dst, entry->value, size);
|
||||
}
|
||||
*out_size = size;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::settings::fwdbg {
|
||||
|
||||
void InitializeSdCardKeyValueStore();
|
||||
|
||||
Result GetSdCardKeyValueStoreSettingsItemValueSize(size_t *out_size, const char *name, const char *key);
|
||||
Result GetSdCardKeyValueStoreSettingsItemValue(size_t *out_size, void *dst, size_t dst_size, const char *name, const char *key);
|
||||
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "sysupdater_apply_manager.hpp"
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
namespace {
|
||||
|
||||
alignas(os::MemoryPageSize) u8 g_boot_image_update_buffer[64_KB];
|
||||
|
||||
updater::BootImageUpdateType GetBootImageUpdateType() {
|
||||
/* NOTE: Here Nintendo uses the value of system setting systeminitializer!boot_image_update_type...but we prefer not to take the risk. */
|
||||
return updater::GetBootImageUpdateType(spl::GetHardwareType());
|
||||
}
|
||||
|
||||
Result MarkPreCommitForBootImages() {
|
||||
/* Set verification required for both normal and safe mode. */
|
||||
R_TRY(updater::MarkVerifyingRequired(updater::BootModeType::Normal, g_boot_image_update_buffer, sizeof(g_boot_image_update_buffer)));
|
||||
R_TRY(updater::MarkVerifyingRequired(updater::BootModeType::Safe, g_boot_image_update_buffer, sizeof(g_boot_image_update_buffer)));
|
||||
|
||||
/* Pre-commit is now marked. */
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result UpdateBootImages() {
|
||||
/* Define a helper to update the images. */
|
||||
auto UpdateBootImageImpl = [](updater::BootModeType boot_mode, updater::BootImageUpdateType boot_image_update_type) -> Result {
|
||||
/* Get the boot image package id. */
|
||||
ncm::SystemDataId boot_image_package_id = {};
|
||||
R_TRY_CATCH(updater::GetBootImagePackageId(std::addressof(boot_image_package_id), boot_mode, g_boot_image_update_buffer, sizeof(g_boot_image_update_buffer))) {
|
||||
R_CATCH(updater::ResultBootImagePackageNotFound) {
|
||||
/* Nintendo simply falls through when the package is not found. */
|
||||
}
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
|
||||
/* Update the boot images. */
|
||||
R_TRY_CATCH(updater::UpdateBootImagesFromPackage(boot_image_package_id, boot_mode, g_boot_image_update_buffer, sizeof(g_boot_image_update_buffer), boot_image_update_type)) {
|
||||
R_CATCH(updater::ResultBootImagePackageNotFound) {
|
||||
/* Nintendo simply falls through when the package is not found. */
|
||||
}
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
/* Mark the images verified. */
|
||||
R_TRY(updater::MarkVerified(boot_mode, g_boot_image_update_buffer, sizeof(g_boot_image_update_buffer)));
|
||||
|
||||
/* The boot images are updated. */
|
||||
R_SUCCEED();
|
||||
};
|
||||
|
||||
/* Get the boot image update type. */
|
||||
auto boot_image_update_type = GetBootImageUpdateType();
|
||||
|
||||
/* Update boot images for safe mode. */
|
||||
R_TRY(UpdateBootImageImpl(updater::BootModeType::Safe, boot_image_update_type));
|
||||
|
||||
/* Update boot images for normal mode. */
|
||||
R_TRY(UpdateBootImageImpl(updater::BootModeType::Normal, boot_image_update_type));
|
||||
|
||||
/* Both sets of images are updated. */
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result SystemUpdateApplyManager::ApplyPackageTask(ncm::PackageSystemDowngradeTask *task) {
|
||||
/* Lock the apply mutex. */
|
||||
std::scoped_lock lk(m_apply_mutex);
|
||||
|
||||
/* NOTE: Here, Nintendo creates a system report for the update. */
|
||||
|
||||
/* Mark boot images to note that we're updating. */
|
||||
R_TRY(MarkPreCommitForBootImages());
|
||||
|
||||
/* Commit the task. */
|
||||
R_TRY(task->Commit());
|
||||
|
||||
/* Update the boot images. */
|
||||
R_TRY(UpdateBootImages());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
class SystemUpdateApplyManager {
|
||||
private:
|
||||
os::SdkMutex m_apply_mutex;
|
||||
public:
|
||||
constexpr SystemUpdateApplyManager() : m_apply_mutex() { /* ... */ }
|
||||
|
||||
Result ApplyPackageTask(ncm::PackageSystemDowngradeTask *task);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "sysupdater_async_impl.hpp"
|
||||
#include "sysupdater_async_thread_allocator.hpp"
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
Result AsyncBase::ToAsyncResult(Result result) {
|
||||
R_TRY_CATCH(result) {
|
||||
R_CONVERT(nim::ResultHttpConnectionCanceled, ns::ResultCanceled());
|
||||
R_CONVERT(ncm::ResultInstallTaskCancelled, ns::ResultCanceled());
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
AsyncPrepareSdCardUpdateImpl::~AsyncPrepareSdCardUpdateImpl() {
|
||||
if (m_thread_info) {
|
||||
os::WaitThread(m_thread_info->thread);
|
||||
os::DestroyThread(m_thread_info->thread);
|
||||
GetAsyncThreadAllocator()->Free(*m_thread_info);
|
||||
}
|
||||
}
|
||||
|
||||
Result AsyncPrepareSdCardUpdateImpl::Run() {
|
||||
/* Get a thread info. */
|
||||
ThreadInfo info;
|
||||
R_TRY(GetAsyncThreadAllocator()->Allocate(std::addressof(info)));
|
||||
|
||||
/* Set the thread info's priority. */
|
||||
info.priority = AMS_GET_SYSTEM_THREAD_PRIORITY(mitm_sysupdater, AsyncPrepareSdCardUpdateTask);
|
||||
|
||||
/* Ensure that we clean up appropriately. */
|
||||
ON_SCOPE_EXIT {
|
||||
if (!m_thread_info) {
|
||||
GetAsyncThreadAllocator()->Free(info);
|
||||
}
|
||||
};
|
||||
|
||||
/* Create a thread for the task. */
|
||||
R_TRY(os::CreateThread(info.thread, [](void *arg) {
|
||||
auto *async = reinterpret_cast<AsyncPrepareSdCardUpdateImpl *>(arg);
|
||||
async->m_result = async->Execute();
|
||||
async->m_event.Signal();
|
||||
}, this, info.stack, info.stack_size, info.priority));
|
||||
|
||||
/* Set the thread name. */
|
||||
os::SetThreadNamePointer(info.thread, AMS_GET_SYSTEM_THREAD_NAME(mitm_sysupdater, AsyncPrepareSdCardUpdateTask));
|
||||
|
||||
/* Start the thread. */
|
||||
os::StartThread(info.thread);
|
||||
|
||||
/* Set our thread info. */
|
||||
m_thread_info = info;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result AsyncPrepareSdCardUpdateImpl::Execute() {
|
||||
R_RETURN(m_task->PrepareAndExecute());
|
||||
}
|
||||
|
||||
void AsyncPrepareSdCardUpdateImpl::CancelImpl() {
|
||||
m_task->Cancel();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "sysupdater_thread_allocator.hpp"
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
class ErrorContextHolder {
|
||||
private:
|
||||
err::ErrorContext m_error_context;
|
||||
public:
|
||||
constexpr ErrorContextHolder() : m_error_context{} { /* ... */ }
|
||||
|
||||
virtual ~ErrorContextHolder() { /* ... */ }
|
||||
|
||||
template<typename T>
|
||||
Result SaveErrorContextIfFailed(T &async, Result result) {
|
||||
ON_RESULT_FAILURE { async.GetErrorContext(std::addressof(m_error_context)); };
|
||||
|
||||
R_RETURN(result);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Result GetAndSaveErrorContext(T &async) {
|
||||
R_RETURN(this->SaveErrorContextIfFailed(async, async.Get()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Result SaveInternalTaskErrorContextIfFailed(T &async, Result result) {
|
||||
ON_RESULT_FAILURE { async.CreateErrorContext(std::addressof(m_error_context)); };
|
||||
|
||||
R_RETURN(result);
|
||||
}
|
||||
|
||||
const err::ErrorContext &GetErrorContextImpl() {
|
||||
return m_error_context;
|
||||
}
|
||||
};
|
||||
|
||||
class AsyncBase {
|
||||
public:
|
||||
virtual ~AsyncBase() { /* ... */ }
|
||||
|
||||
static Result ToAsyncResult(Result result);
|
||||
|
||||
Result Cancel() {
|
||||
this->CancelImpl();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result GetErrorContext(sf::Out<err::ErrorContext> out) {
|
||||
*out = {};
|
||||
R_SUCCEED();
|
||||
}
|
||||
private:
|
||||
virtual void CancelImpl() = 0;
|
||||
};
|
||||
|
||||
class AsyncResultBase : public AsyncBase {
|
||||
public:
|
||||
virtual ~AsyncResultBase() { /* ... */ }
|
||||
|
||||
Result Get() {
|
||||
R_RETURN(ToAsyncResult(this->GetImpl()));
|
||||
}
|
||||
private:
|
||||
virtual Result GetImpl() = 0;
|
||||
};
|
||||
static_assert(ns::impl::IsIAsyncResult<AsyncResultBase>);
|
||||
|
||||
/* NOTE: Based off of ns AsyncPrepareCardUpdateImpl. */
|
||||
/* We don't implement the RequestServer::ManagedStop details, as we don't implement stoppable request list. */
|
||||
class AsyncPrepareSdCardUpdateImpl : public AsyncResultBase, private ErrorContextHolder {
|
||||
private:
|
||||
Result m_result;
|
||||
os::SystemEvent m_event;
|
||||
util::optional<ThreadInfo> m_thread_info;
|
||||
ncm::InstallTaskBase *m_task;
|
||||
public:
|
||||
AsyncPrepareSdCardUpdateImpl(ncm::InstallTaskBase *task) : m_result(ResultSuccess()), m_event(os::EventClearMode_ManualClear, true), m_thread_info(), m_task(task) { /* ... */ }
|
||||
virtual ~AsyncPrepareSdCardUpdateImpl();
|
||||
|
||||
os::SystemEvent &GetEvent() { return m_event; }
|
||||
|
||||
virtual Result GetErrorContext(sf::Out<err::ErrorContext> out) override {
|
||||
*out = ErrorContextHolder::GetErrorContextImpl();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Run();
|
||||
private:
|
||||
Result Execute();
|
||||
|
||||
virtual void CancelImpl() override;
|
||||
virtual Result GetImpl() override { return m_result; }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "sysupdater_async_thread_allocator.hpp"
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline int AsyncThreadCount = 1;
|
||||
constexpr inline size_t AsyncThreadStackSize = 32_KB;
|
||||
|
||||
os::ThreadType g_async_threads[AsyncThreadCount];
|
||||
alignas(os::ThreadStackAlignment) u8 g_async_thread_stack_heap[AsyncThreadCount * AsyncThreadStackSize];
|
||||
|
||||
constinit ThreadAllocator g_async_thread_allocator(g_async_threads, AsyncThreadCount, os::InvalidThreadPriority, g_async_thread_stack_heap, sizeof(g_async_thread_stack_heap), AsyncThreadStackSize);
|
||||
|
||||
}
|
||||
|
||||
ThreadAllocator *GetAsyncThreadAllocator() {
|
||||
return std::addressof(g_async_thread_allocator);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "sysupdater_thread_allocator.hpp"
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
ThreadAllocator *GetAsyncThreadAllocator();
|
||||
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "sysupdater_fs_utils.hpp"
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline const char * const NcaExtension = ".nca";
|
||||
constexpr inline const char * const NspExtension = ".nsp";
|
||||
constexpr inline const size_t NcaExtensionSize = 4;
|
||||
constexpr inline const size_t NspExtensionSize = 4;
|
||||
|
||||
constexpr const ams::fs::PathFlags SdCardContentMetaPathNormalizePathFlags = [] {
|
||||
fs::PathFlags flags{};
|
||||
flags.AllowMountName();
|
||||
return flags;
|
||||
}();
|
||||
|
||||
static_assert(NcaExtensionSize == NspExtensionSize);
|
||||
constexpr inline const size_t NcaNspExtensionSize = NcaExtensionSize;
|
||||
|
||||
Result CheckNcaOrNsp(const char **path) {
|
||||
/* Ensure that the path is currently at the mount name delimeter. */
|
||||
R_UNLESS(util::Strncmp(*path, ams::fs::impl::MountNameDelimiter, util::Strnlen(ams::fs::impl::MountNameDelimiter, ams::fs::EntryNameLengthMax)) == 0, fs::ResultPathNotFound());
|
||||
|
||||
/* Advance past the :. */
|
||||
static_assert(ams::fs::impl::MountNameDelimiter[0] == ':');
|
||||
*path += 1;
|
||||
|
||||
/* Ensure path is long enough for the extension. */
|
||||
const size_t path_len = util::Strnlen(*path, ams::fs::EntryNameLengthMax);
|
||||
R_UNLESS(path_len > NcaNspExtensionSize, fs::ResultPathNotFound());
|
||||
|
||||
/* Get the extension. */
|
||||
const char * const extension = *path + path_len - NcaNspExtensionSize;
|
||||
|
||||
/* Ensure nca or nsp. */
|
||||
const bool is_nca = util::Strnicmp(extension, NcaExtension, NcaNspExtensionSize) == 0;
|
||||
const bool is_nsp = util::Strnicmp(extension, NspExtension, NcaNspExtensionSize) == 0;
|
||||
R_UNLESS(is_nca || is_nsp, fs::ResultPathNotFound());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ParseMountName(const char **path, std::shared_ptr<ams::fs::fsa::IFileSystem> *out) {
|
||||
/* The equivalent function here supports all the common mount names; we'll only support the SD card, system content storage. */
|
||||
if (const auto mount_len = util::Strnlen(ams::fs::impl::SdCardFileSystemMountName, ams::fs::MountNameLengthMax); util::Strncmp(*path, ams::fs::impl::SdCardFileSystemMountName, mount_len) == 0) {
|
||||
/* Advance the path. */
|
||||
*path += mount_len;
|
||||
|
||||
/* Open the SD card. This uses libnx bindings. */
|
||||
FsFileSystem fs;
|
||||
R_TRY(fsOpenSdCardFileSystem(std::addressof(fs)));
|
||||
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_shared<ams::fs::RemoteFileSystem>(fs);
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationMemoryFailedInSdCardA());
|
||||
|
||||
/* Set the output fs. */
|
||||
*out = std::move(fsa);
|
||||
} else if (const auto mount_len = util::Strnlen(ams::fs::impl::ContentStorageSystemMountName, ams::fs::MountNameLengthMax); util::Strncmp(*path, ams::fs::impl::ContentStorageSystemMountName, mount_len) == 0) {
|
||||
/* Advance the path. */
|
||||
*path += mount_len;
|
||||
|
||||
/* Open the system content storage. This uses libnx bindings. */
|
||||
FsFileSystem fs;
|
||||
R_TRY(fsOpenContentStorageFileSystem(std::addressof(fs), FsContentStorageId_System));
|
||||
|
||||
/* Allocate a new filesystem wrapper. */
|
||||
auto fsa = std::make_shared<ams::fs::RemoteFileSystem>(fs);
|
||||
R_UNLESS(fsa != nullptr, fs::ResultAllocationMemoryFailedInContentStorageA());
|
||||
|
||||
/* Set the output fs. */
|
||||
*out = std::move(fsa);
|
||||
} else {
|
||||
R_THROW(fs::ResultPathNotFound());
|
||||
}
|
||||
|
||||
/* Ensure that there's something that could be a mount name delimiter. */
|
||||
R_UNLESS(util::Strnlen(*path, fs::EntryNameLengthMax) != 0, fs::ResultPathNotFound());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ParseNsp(const char **path, std::shared_ptr<ams::fs::fsa::IFileSystem> *out, std::shared_ptr<ams::fs::fsa::IFileSystem> base_fs) {
|
||||
const char *work_path = *path;
|
||||
|
||||
/* Advance to the nsp extension. */
|
||||
while (true) {
|
||||
if (util::Strnicmp(work_path, NspExtension, NspExtensionSize) == 0) {
|
||||
if (work_path[NspExtensionSize] == '\x00' || work_path[NspExtensionSize] == '/') {
|
||||
break;
|
||||
}
|
||||
work_path += NspExtensionSize;
|
||||
} else {
|
||||
R_UNLESS(*work_path != '\x00', fs::ResultPathNotFound());
|
||||
|
||||
work_path += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Advance past the extension. */
|
||||
work_path += NspExtensionSize;
|
||||
|
||||
/* Get the nsp path. */
|
||||
ams::fs::Path nsp_path;
|
||||
R_TRY(nsp_path.InitializeWithNormalization(*path, work_path - *path));
|
||||
|
||||
/* Open the file storage. */
|
||||
std::shared_ptr<ams::fs::FileStorageBasedFileSystem> file_storage = fssystem::AllocateShared<ams::fs::FileStorageBasedFileSystem>();
|
||||
R_UNLESS(file_storage != nullptr, fs::ResultAllocationMemoryFailedInNcaFileSystemServiceImplA());
|
||||
R_TRY(file_storage->Initialize(std::move(base_fs), nsp_path, ams::fs::OpenMode_Read));
|
||||
|
||||
/* Create a partition fs. */
|
||||
R_TRY(fssystem::GetFileSystemCreatorInterfaces()->partition_fs_creator->Create(out, std::move(file_storage)));
|
||||
|
||||
/* Update the path. */
|
||||
*path = work_path;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ParseNca(const char **path, std::shared_ptr<fssystem::NcaReader> *out, std::shared_ptr<ams::fs::fsa::IFileSystem> base_fs) {
|
||||
/* Open the file storage. */
|
||||
std::shared_ptr<ams::fs::FileStorageBasedFileSystem> file_storage = fssystem::AllocateShared<ams::fs::FileStorageBasedFileSystem>();
|
||||
R_UNLESS(file_storage != nullptr, fs::ResultAllocationMemoryFailedInNcaFileSystemServiceImplB());
|
||||
|
||||
/* Get the nca path. */
|
||||
ams::fs::Path nca_path;
|
||||
R_TRY(nca_path.InitializeWithNormalization(*path));
|
||||
|
||||
R_TRY(file_storage->Initialize(std::move(base_fs), nca_path, ams::fs::OpenMode_Read));
|
||||
|
||||
/* Create the nca reader. */
|
||||
std::shared_ptr<fssystem::NcaReader> nca_reader;
|
||||
R_TRY(fssystem::GetFileSystemCreatorInterfaces()->storage_on_nca_creator->CreateNcaReader(std::addressof(nca_reader), file_storage));
|
||||
|
||||
/* NOTE: Here Nintendo validates program ID, but this does not need checking in the meta case. */
|
||||
|
||||
/* Set output reader. */
|
||||
*out = std::move(nca_reader);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result OpenMetaStorage(std::shared_ptr<ams::fs::IStorage> *out, std::shared_ptr<fssystem::IAsynchronousAccessSplitter> *out_splitter, std::shared_ptr<fssystem::NcaReader> nca_reader, fssystem::NcaFsHeader::FsType *out_fs_type) {
|
||||
/* Ensure the nca is a meta nca. */
|
||||
R_UNLESS(nca_reader->GetContentType() == fssystem::NcaHeader::ContentType::Meta, fs::ResultPreconditionViolation());
|
||||
|
||||
/* We only support SD card ncas, so ensure this isn't a gamecard nca. */
|
||||
R_UNLESS(nca_reader->GetDistributionType() != fssystem::NcaHeader::DistributionType::GameCard, fs::ResultPermissionDenied());
|
||||
|
||||
/* Here Nintendo would call GetPartitionIndex(), but we don't need to, because it's meta. */
|
||||
constexpr int MetaPartitionIndex = 0;
|
||||
|
||||
/* Open fs header reader. */
|
||||
fssystem::NcaFsHeaderReader fs_header_reader;
|
||||
R_TRY(fssystem::GetFileSystemCreatorInterfaces()->storage_on_nca_creator->Create(out, out_splitter, std::addressof(fs_header_reader), std::move(nca_reader), MetaPartitionIndex));
|
||||
|
||||
/* Set the output fs type. */
|
||||
*out_fs_type = fs_header_reader.GetFsType();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result OpenContentMetaFileSystem(std::shared_ptr<ams::fs::fsa::IFileSystem> *out, const char *path) {
|
||||
/* Parse the mount name to get a filesystem. */
|
||||
const char *cur_path = path;
|
||||
std::shared_ptr<ams::fs::fsa::IFileSystem> base_fs;
|
||||
R_TRY(ParseMountName(std::addressof(cur_path), std::addressof(base_fs)));
|
||||
|
||||
/* Ensure the path is an nca or nsp. */
|
||||
R_TRY(CheckNcaOrNsp(std::addressof(cur_path)));
|
||||
|
||||
/* Try to parse as nsp. */
|
||||
std::shared_ptr<ams::fs::fsa::IFileSystem> nsp_fs;
|
||||
if (R_SUCCEEDED(ParseNsp(std::addressof(cur_path), std::addressof(nsp_fs), base_fs))) {
|
||||
/* nsp target is only allowed for type package, and we're assuming type meta. */
|
||||
R_UNLESS(*path != '\x00', fs::ResultInvalidArgument());
|
||||
|
||||
/* Use the nsp fs as the base fs. */
|
||||
base_fs = std::move(nsp_fs);
|
||||
}
|
||||
|
||||
/* Parse as nca. */
|
||||
std::shared_ptr<fssystem::NcaReader> nca_reader;
|
||||
R_TRY(ParseNca(std::addressof(cur_path), std::addressof(nca_reader), std::move(base_fs)));
|
||||
|
||||
/* Open meta storage. */
|
||||
std::shared_ptr<ams::fs::IStorage> storage;
|
||||
std::shared_ptr<fssystem::IAsynchronousAccessSplitter> splitter;
|
||||
fssystem::NcaFsHeader::FsType fs_type = static_cast<fssystem::NcaFsHeader::FsType>(~0);
|
||||
R_TRY(OpenMetaStorage(std::addressof(storage), std::addressof(splitter), std::move(nca_reader), std::addressof(fs_type)));
|
||||
|
||||
/* Open the appropriate interface. */
|
||||
const auto * const creator_intfs = fssystem::GetFileSystemCreatorInterfaces();
|
||||
switch (fs_type) {
|
||||
case fssystem::NcaFsHeader::FsType::PartitionFs: R_RETURN(creator_intfs->partition_fs_creator->Create(out, std::move(storage)));
|
||||
case fssystem::NcaFsHeader::FsType::RomFs: R_RETURN(creator_intfs->rom_fs_creator->Create(out, std::move(storage)));
|
||||
default:
|
||||
R_THROW(fs::ResultInvalidNcaFileSystemType());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool PathView::HasPrefix(util::string_view prefix) const {
|
||||
return m_path.compare(0, prefix.length(), prefix) == 0;
|
||||
}
|
||||
|
||||
bool PathView::HasSuffix(util::string_view suffix) const {
|
||||
return m_path.compare(m_path.length() - suffix.length(), suffix.length(), suffix) == 0;
|
||||
}
|
||||
|
||||
util::string_view PathView::GetFileName() const {
|
||||
auto pos = m_path.find_last_of("/");
|
||||
return pos != util::string_view::npos ? m_path.substr(pos + 1) : m_path;
|
||||
}
|
||||
|
||||
Result MountSdCardContentMeta(const char *mount_name, const char *path, ams::fs::ContentAttributes attr) {
|
||||
/* TODO: What does attributes actually get used for? */
|
||||
AMS_UNUSED(attr);
|
||||
|
||||
/* Sanitize input. */
|
||||
/* NOTE: This is an internal API, so we won't bother with mount name sanitization. */
|
||||
R_UNLESS(path != nullptr, fs::ResultInvalidPath());
|
||||
|
||||
/* Normalize the path. */
|
||||
char normalized_path[fs::EntryNameLengthMax + 1];
|
||||
R_TRY(ams::fs::PathFormatter::Normalize(normalized_path, sizeof(normalized_path), path, std::strlen(path) + 1, SdCardContentMetaPathNormalizePathFlags));
|
||||
|
||||
/* Open the filesystem. */
|
||||
std::shared_ptr<ams::fs::fsa::IFileSystem> fs;
|
||||
R_TRY(OpenContentMetaFileSystem(std::addressof(fs), normalized_path));
|
||||
|
||||
/* Create a holder for the fs. */
|
||||
std::unique_ptr unique_fs = std::make_unique<fssystem::ForwardingFileSystem>(std::move(fs));
|
||||
R_UNLESS(unique_fs != nullptr, fs::ResultAllocationMemoryFailedNew());
|
||||
|
||||
/* Register the fs. */
|
||||
R_RETURN(ams::fs::fsa::Register(mount_name, std::move(unique_fs)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
class PathView {
|
||||
private:
|
||||
util::string_view m_path;
|
||||
public:
|
||||
PathView(util::string_view p) : m_path(p) { /* ...*/ }
|
||||
bool HasPrefix(util::string_view prefix) const;
|
||||
bool HasSuffix(util::string_view suffix) const;
|
||||
util::string_view GetFileName() const;
|
||||
};
|
||||
|
||||
Result MountSdCardContentMeta(const char *mount_name, const char *path, ams::fs::ContentAttributes attr);
|
||||
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_initialization.hpp"
|
||||
#include "sysupdater_module.hpp"
|
||||
#include "sysupdater_service.hpp"
|
||||
#include "sysupdater_async_impl.hpp"
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
namespace {
|
||||
|
||||
enum PortIndex {
|
||||
PortIndex_Sysupdater,
|
||||
PortIndex_Count,
|
||||
};
|
||||
|
||||
constexpr sm::ServiceName SystemUpdateServiceName = sm::ServiceName::Encode("ams:su");
|
||||
constexpr size_t SystemUpdateMaxSessions = 1;
|
||||
|
||||
constexpr size_t MaxSessions = SystemUpdateMaxSessions + 3;
|
||||
|
||||
struct ServerOptions {
|
||||
static constexpr size_t PointerBufferSize = 1_KB;
|
||||
static constexpr size_t MaxDomains = 0;
|
||||
static constexpr size_t MaxDomainObjects = 0;
|
||||
static constexpr bool CanDeferInvokeRequest = false;
|
||||
static constexpr bool CanManageMitmServers = false;
|
||||
};
|
||||
|
||||
sf::hipc::ServerManager<PortIndex_Count, ServerOptions, MaxSessions> g_server_manager;
|
||||
|
||||
constinit sf::UnmanagedServiceObject<sysupdater::impl::ISystemUpdateInterface, sysupdater::SystemUpdateService> g_system_update_service_object;
|
||||
|
||||
}
|
||||
|
||||
void MitmModule::ThreadFunction(void *) {
|
||||
/* Wait until initialization is complete. */
|
||||
mitm::WaitInitialized();
|
||||
|
||||
/* Connect to nim. */
|
||||
nim::InitializeForNetworkInstallManager();
|
||||
ON_SCOPE_EXIT { nim::FinalizeForNetworkInstallManager(); };
|
||||
|
||||
/* Register ams:su. */
|
||||
R_ABORT_UNLESS(g_server_manager.RegisterObjectForServer(g_system_update_service_object.GetShared(), SystemUpdateServiceName, SystemUpdateMaxSessions));
|
||||
|
||||
/* Loop forever, servicing our services. */
|
||||
g_server_manager.LoopProcess();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "../amsmitm_module.hpp"
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
DEFINE_MITM_MODULE_CLASS(0x8000, AMS_GET_SYSTEM_THREAD_PRIORITY(mitm_sysupdater, IpcServer));
|
||||
|
||||
}
|
||||
@@ -1,522 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "sysupdater_service.hpp"
|
||||
#include "sysupdater_async_impl.hpp"
|
||||
#include "sysupdater_fs_utils.hpp"
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
namespace {
|
||||
|
||||
/* ExFat NCAs prior to 2.0.0 do not actually include the exfat driver, and don't boot. */
|
||||
constexpr inline u32 MinimumVersionForExFatDriver = 65536;
|
||||
|
||||
bool IsExFatDriverSupported(const ncm::ContentMetaInfo &info) {
|
||||
return info.version >= MinimumVersionForExFatDriver && ((info.attributes & ncm::ContentMetaAttribute_IncludesExFatDriver) != 0);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
Result ForEachFileInDirectory(const char *root_path, F f) {
|
||||
/* Open the directory. */
|
||||
fs::DirectoryHandle dir;
|
||||
R_TRY(fs::OpenDirectory(std::addressof(dir), root_path, fs::OpenDirectoryMode_File));
|
||||
ON_SCOPE_EXIT { fs::CloseDirectory(dir); };
|
||||
|
||||
while (true) {
|
||||
/* Read the current entry. */
|
||||
s64 count;
|
||||
fs::DirectoryEntry entry;
|
||||
R_TRY(fs::ReadDirectory(std::addressof(count), std::addressof(entry), dir, 1));
|
||||
if (count == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Invoke our handler on the entry. */
|
||||
bool done;
|
||||
R_TRY(f(std::addressof(done), entry));
|
||||
R_SUCCEED_IF(done);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ConvertToFsCommonPath(char *dst, size_t dst_size, const char *package_root_path, const char *entry_path) {
|
||||
char package_path[ams::fs::EntryNameLengthMax];
|
||||
|
||||
const size_t path_len = util::SNPrintf(package_path, sizeof(package_path), "%s%s", package_root_path, entry_path);
|
||||
AMS_ABORT_UNLESS(path_len < ams::fs::EntryNameLengthMax);
|
||||
|
||||
R_RETURN(ams::fs::ConvertToFsCommonPath(dst, dst_size, package_path));
|
||||
}
|
||||
|
||||
Result LoadContentMeta(ncm::AutoBuffer *out, const char *package_root_path, const fs::DirectoryEntry &entry) {
|
||||
AMS_ABORT_UNLESS(PathView(entry.name).HasSuffix(".cnmt.nca"));
|
||||
|
||||
char path[ams::fs::EntryNameLengthMax];
|
||||
R_TRY(ConvertToFsCommonPath(path, sizeof(path), package_root_path, entry.name));
|
||||
|
||||
R_RETURN(ncm::TryReadContentMetaPath(out, path, ncm::ReadContentMetaPathAlongWithExtendedDataAndDigest));
|
||||
}
|
||||
|
||||
Result ReadContentMetaPath(ncm::AutoBuffer *out, const char *package_root, const ncm::ContentInfo &content_info) {
|
||||
/* Get the .cnmt.nca path for the info. */
|
||||
char cnmt_nca_name[ncm::ContentIdStringLength + 10];
|
||||
ncm::GetStringFromContentId(cnmt_nca_name, sizeof(cnmt_nca_name), content_info.GetId());
|
||||
std::memcpy(cnmt_nca_name + ncm::ContentIdStringLength, ".cnmt.nca", std::strlen(".cnmt.nca"));
|
||||
cnmt_nca_name[sizeof(cnmt_nca_name) - 1] = '\x00';
|
||||
|
||||
/* Create a new path. */
|
||||
ncm::Path content_path;
|
||||
R_TRY(ConvertToFsCommonPath(content_path.str, sizeof(content_path.str), package_root, cnmt_nca_name));
|
||||
|
||||
/* Read the content meta path. */
|
||||
R_RETURN(ncm::TryReadContentMetaPath(out, content_path.str, ncm::ReadContentMetaPathAlongWithExtendedDataAndDigest));
|
||||
}
|
||||
|
||||
Result GetSystemUpdateUpdateContentInfoFromPackage(ncm::ContentInfo *out, const char *package_root) {
|
||||
bool found_system_update = false;
|
||||
|
||||
/* Iterate over all files to find the system update meta. */
|
||||
R_TRY(ForEachFileInDirectory(package_root, [&](bool *done, const fs::DirectoryEntry &entry) -> Result {
|
||||
/* Don't early terminate by default. */
|
||||
*done = false;
|
||||
|
||||
/* We have nothing to list if we're not looking at a meta. */
|
||||
R_SUCCEED_IF(!PathView(entry.name).HasSuffix(".cnmt.nca"));
|
||||
|
||||
/* Read the content meta path, and build. */
|
||||
ncm::AutoBuffer package_meta;
|
||||
R_TRY(LoadContentMeta(std::addressof(package_meta), package_root, entry));
|
||||
|
||||
/* Create a reader. */
|
||||
const auto reader = ncm::PackagedContentMetaReader(package_meta.Get(), package_meta.GetSize());
|
||||
|
||||
/* If we find a system update, we're potentially done. */
|
||||
if (reader.GetHeader()->type == ncm::ContentMetaType::SystemUpdate) {
|
||||
/* Try to parse a content id from the name. */
|
||||
auto content_id = ncm::GetContentIdFromString(entry.name, sizeof(entry.name));
|
||||
R_UNLESS(content_id, ncm::ResultInvalidPackageFormat());
|
||||
|
||||
/* We're done. */
|
||||
*done = true;
|
||||
found_system_update = true;
|
||||
|
||||
*out = ncm::ContentInfo::Make(*content_id, entry.file_size, ncm::ContentInfo::DefaultContentAttributes, ncm::ContentType::Meta);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}));
|
||||
|
||||
/* If we didn't find anything, error. */
|
||||
R_UNLESS(found_system_update, ncm::ResultSystemUpdateNotFoundInPackage());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ValidateSystemUpdate(Result *out_result, Result *out_exfat_result, UpdateValidationInfo *out_info, const ncm::PackagedContentMetaReader &update_reader, const char *package_root) {
|
||||
/* Clear output. */
|
||||
*out_result = ResultSuccess();
|
||||
*out_exfat_result = ResultSuccess();
|
||||
|
||||
/* We want to track all content the update requires. */
|
||||
const size_t num_content_metas = update_reader.GetContentMetaCount();
|
||||
bool content_meta_valid[num_content_metas] = {};
|
||||
|
||||
/* Allocate a buffer to use for validation. */
|
||||
size_t data_buffer_size = 1_MB;
|
||||
void *data_buffer;
|
||||
do {
|
||||
data_buffer = std::malloc(data_buffer_size);
|
||||
if (data_buffer != nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
data_buffer_size /= 2;
|
||||
} while (data_buffer_size >= 16_KB);
|
||||
R_UNLESS(data_buffer != nullptr, fs::ResultAllocationMemoryFailedNew());
|
||||
|
||||
ON_SCOPE_EXIT { std::free(data_buffer); };
|
||||
|
||||
/* Declare helper for result validation. */
|
||||
auto ValidateResult = [&](Result result) ALWAYS_INLINE_LAMBDA -> Result {
|
||||
*out_result = result;
|
||||
R_RETURN(result);
|
||||
};
|
||||
|
||||
/* Iterate over all files to find all content metas. */
|
||||
R_TRY(ForEachFileInDirectory(package_root, [&](bool *done, const fs::DirectoryEntry &entry) -> Result {
|
||||
/* Clear output. */
|
||||
*out_info = {};
|
||||
|
||||
/* Don't early terminate by default. */
|
||||
*done = false;
|
||||
|
||||
/* We have nothing to list if we're not looking at a meta. */
|
||||
R_SUCCEED_IF(!PathView(entry.name).HasSuffix(".cnmt.nca"));
|
||||
|
||||
/* Read the content meta path, and build. */
|
||||
ncm::AutoBuffer package_meta;
|
||||
R_TRY(LoadContentMeta(std::addressof(package_meta), package_root, entry));
|
||||
|
||||
/* Create a reader. */
|
||||
const auto reader = ncm::PackagedContentMetaReader(package_meta.Get(), package_meta.GetSize());
|
||||
|
||||
/* Get the key for the reader. */
|
||||
const auto key = reader.GetKey();
|
||||
|
||||
/* Check if we need to validate this content. */
|
||||
bool need_validate = false;
|
||||
size_t validation_index = 0;
|
||||
for (size_t i = 0; i < num_content_metas; ++i) {
|
||||
if (update_reader.GetContentMetaInfo(i)->ToKey() == key) {
|
||||
need_validate = true;
|
||||
validation_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we don't need to validate, continue. */
|
||||
R_SUCCEED_IF(!need_validate);
|
||||
|
||||
/* We're validating. */
|
||||
out_info->invalid_key = key;
|
||||
|
||||
/* Validate all contents. */
|
||||
for (size_t i = 0; i < reader.GetContentCount(); ++i) {
|
||||
const auto *content_info = reader.GetContentInfo(i);
|
||||
const auto &content_id = content_info->GetId();
|
||||
const s64 content_size = content_info->info.GetSize();
|
||||
out_info->invalid_content_id = content_id;
|
||||
|
||||
/* Get the content id string. */
|
||||
auto content_id_str = ncm::GetContentIdString(content_id);
|
||||
|
||||
/* Open the file. */
|
||||
fs::FileHandle file;
|
||||
{
|
||||
char path[fs::EntryNameLengthMax];
|
||||
util::SNPrintf(path, sizeof(path), "%s%s%s", package_root, content_id_str.data, content_info->GetType() == ncm::ContentType::Meta ? ".cnmt.nca" : ".nca");
|
||||
if (R_FAILED(ValidateResult(fs::OpenFile(std::addressof(file), path, ams::fs::OpenMode_Read)))) {
|
||||
*done = true;
|
||||
R_SUCCEED();
|
||||
}
|
||||
}
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
/* Validate the file size is correct. */
|
||||
s64 file_size;
|
||||
if (R_FAILED(ValidateResult(fs::GetFileSize(std::addressof(file_size), file)))) {
|
||||
*done = true;
|
||||
R_SUCCEED();
|
||||
}
|
||||
if (file_size != content_size) {
|
||||
*out_result = ncm::ResultInvalidContentHash();
|
||||
*done = true;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Read and hash the file in chunks. */
|
||||
crypto::Sha256Generator sha;
|
||||
sha.Initialize();
|
||||
|
||||
s64 ofs = 0;
|
||||
while (ofs < content_size) {
|
||||
const size_t cur_size = std::min(static_cast<size_t>(content_size - ofs), data_buffer_size);
|
||||
if (R_FAILED(ValidateResult(fs::ReadFile(file, ofs, data_buffer, cur_size)))) {
|
||||
*done = true;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
sha.Update(data_buffer, cur_size);
|
||||
|
||||
ofs += cur_size;
|
||||
}
|
||||
|
||||
/* Get the hash. */
|
||||
ncm::Digest calc_digest;
|
||||
sha.GetHash(std::addressof(calc_digest), sizeof(calc_digest));
|
||||
|
||||
/* Validate the hash. */
|
||||
if (std::memcmp(std::addressof(calc_digest), std::addressof(content_info->digest), sizeof(ncm::Digest)) != 0) {
|
||||
*out_result = ncm::ResultInvalidContentHash();
|
||||
*done = true;
|
||||
R_SUCCEED();
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark the relevant content as validated. */
|
||||
content_meta_valid[validation_index] = true;
|
||||
*out_info = {};
|
||||
|
||||
R_SUCCEED();
|
||||
}));
|
||||
|
||||
/* If we're otherwise going to succeed, ensure that every content was found. */
|
||||
if (R_SUCCEEDED(*out_result)) {
|
||||
for (size_t i = 0; i < num_content_metas; ++i) {
|
||||
if (!content_meta_valid[i]) {
|
||||
const ncm::ContentMetaInfo *info = update_reader.GetContentMetaInfo(i);
|
||||
|
||||
*out_info = { .invalid_key = info->ToKey(), };
|
||||
|
||||
if (IsExFatDriverSupported(*info)) {
|
||||
*out_exfat_result = fs::ResultPathNotFound();
|
||||
/* Continue, in case there's a non-exFAT failure result. */
|
||||
} else {
|
||||
*out_result = fs::ResultPathNotFound();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FormatUserPackagePath(ncm::Path *out, const ncm::Path &user_path) {
|
||||
/* Ensure that the user path is valid. */
|
||||
R_UNLESS(user_path.str[0] == '/', fs::ResultInvalidPath());
|
||||
|
||||
/* Print as @Sdcard:<user_path>/ */
|
||||
util::SNPrintf(out->str, sizeof(out->str), "%s:%s/", ams::fs::impl::SdCardFileSystemMountName, user_path.str);
|
||||
|
||||
/* Normalize, if the user provided an ending / */
|
||||
const size_t len = std::strlen(out->str);
|
||||
if (out->str[len - 1] == '/' && out->str[len - 2] == '/') {
|
||||
out->str[len - 1] = '\x00';
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
const char *GetFirmwareVariationSettingName(settings::system::PlatformRegion region) {
|
||||
switch (region) {
|
||||
case settings::system::PlatformRegion_Global: return "firmware_variation";
|
||||
case settings::system::PlatformRegion_China: return "t_firmware_variation";
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
ncm::FirmwareVariationId GetFirmwareVariationId() {
|
||||
/* Get the firmware variation setting name. */
|
||||
const char * const setting_name = GetFirmwareVariationSettingName(settings::system::GetPlatformRegion());
|
||||
|
||||
/* Retrieve the firmware variation id. */
|
||||
ncm::FirmwareVariationId id = {};
|
||||
settings::fwdbg::GetSettingsItemValue(std::addressof(id.value), sizeof(u8), "ns.systemupdate", setting_name);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result SystemUpdateService::GetUpdateInformation(sf::Out<UpdateInformation> out, const ncm::Path &path) {
|
||||
/* Adjust the path. */
|
||||
ncm::Path package_root;
|
||||
R_TRY(FormatUserPackagePath(std::addressof(package_root), path));
|
||||
|
||||
/* Create a new update information. */
|
||||
UpdateInformation update_info = {};
|
||||
|
||||
/* Parse the update. */
|
||||
{
|
||||
/* Get the content info for the system update. */
|
||||
ncm::ContentInfo content_info;
|
||||
R_TRY(GetSystemUpdateUpdateContentInfoFromPackage(std::addressof(content_info), package_root.str));
|
||||
|
||||
/* Read the content meta. */
|
||||
ncm::AutoBuffer content_meta_buffer;
|
||||
R_TRY(ReadContentMetaPath(std::addressof(content_meta_buffer), package_root.str, content_info));
|
||||
|
||||
/* Create a reader. */
|
||||
const auto reader = ncm::PackagedContentMetaReader(content_meta_buffer.Get(), content_meta_buffer.GetSize());
|
||||
|
||||
/* Get the version from the header. */
|
||||
update_info.version = reader.GetHeader()->version;
|
||||
|
||||
/* Iterate over infos to find the system update info. */
|
||||
for (size_t i = 0; i < reader.GetContentMetaCount(); ++i) {
|
||||
const auto &meta_info = *reader.GetContentMetaInfo(i);
|
||||
|
||||
switch (meta_info.type) {
|
||||
case ncm::ContentMetaType::BootImagePackage:
|
||||
/* Detect exFAT support. */
|
||||
update_info.exfat_supported |= IsExFatDriverSupported(meta_info);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Default to no firmware variations. */
|
||||
update_info.firmware_variation_count = 0;
|
||||
|
||||
/* Parse firmware variations if relevant. */
|
||||
if (reader.GetExtendedDataSize() != 0) {
|
||||
/* Get the actual firmware variation count. */
|
||||
ncm::SystemUpdateMetaExtendedDataReader extended_data_reader(reader.GetExtendedData(), reader.GetExtendedDataSize());
|
||||
update_info.firmware_variation_count = extended_data_reader.GetFirmwareVariationCount();
|
||||
|
||||
/* NOTE: Update this if Nintendo ever actually releases an update with this many variations? */
|
||||
R_UNLESS(update_info.firmware_variation_count <= FirmwareVariationCountMax, ncm::ResultInvalidFirmwareVariation());
|
||||
|
||||
for (size_t i = 0; i < update_info.firmware_variation_count; ++i) {
|
||||
update_info.firmware_variation_ids[i] = *extended_data_reader.GetFirmwareVariationId(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the parsed update info. */
|
||||
out.SetValue(update_info);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SystemUpdateService::ValidateUpdate(sf::Out<Result> out_validate_result, sf::Out<Result> out_validate_exfat_result, sf::Out<UpdateValidationInfo> out_validate_info, const ncm::Path &path) {
|
||||
/* Adjust the path. */
|
||||
ncm::Path package_root;
|
||||
R_TRY(FormatUserPackagePath(std::addressof(package_root), path));
|
||||
|
||||
/* Parse the update. */
|
||||
{
|
||||
/* Get the content info for the system update. */
|
||||
ncm::ContentInfo content_info;
|
||||
R_TRY(GetSystemUpdateUpdateContentInfoFromPackage(std::addressof(content_info), package_root.str));
|
||||
|
||||
/* Read the content meta. */
|
||||
ncm::AutoBuffer content_meta_buffer;
|
||||
R_TRY(ReadContentMetaPath(std::addressof(content_meta_buffer), package_root.str, content_info));
|
||||
|
||||
/* Create a reader. */
|
||||
const auto reader = ncm::PackagedContentMetaReader(content_meta_buffer.Get(), content_meta_buffer.GetSize());
|
||||
|
||||
/* Validate the update. */
|
||||
R_TRY(ValidateSystemUpdate(out_validate_result.GetPointer(), out_validate_exfat_result.GetPointer(), out_validate_info.GetPointer(), reader, package_root.str));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
};
|
||||
|
||||
Result SystemUpdateService::SetupUpdate(sf::CopyHandle &&transfer_memory, u64 transfer_memory_size, const ncm::Path &path, bool exfat) {
|
||||
R_RETURN(this->SetupUpdateImpl(std::move(transfer_memory), transfer_memory_size, path, exfat, GetFirmwareVariationId()));
|
||||
}
|
||||
|
||||
Result SystemUpdateService::SetupUpdateWithVariation(sf::CopyHandle &&transfer_memory, u64 transfer_memory_size, const ncm::Path &path, bool exfat, ncm::FirmwareVariationId firmware_variation_id) {
|
||||
R_RETURN(this->SetupUpdateImpl(std::move(transfer_memory), transfer_memory_size, path, exfat, firmware_variation_id));
|
||||
}
|
||||
|
||||
Result SystemUpdateService::RequestPrepareUpdate(sf::OutCopyHandle out_event_handle, sf::Out<sf::SharedPointer<ns::impl::IAsyncResult>> out_async) {
|
||||
/* Ensure the update is setup but not prepared. */
|
||||
R_UNLESS(m_setup_update, ns::ResultCardUpdateNotSetup());
|
||||
R_UNLESS(!m_requested_update, ns::ResultPrepareCardUpdateAlreadyRequested());
|
||||
|
||||
/* Create the async result. */
|
||||
auto async_result = sf::CreateSharedObjectEmplaced<ns::impl::IAsyncResult, AsyncPrepareSdCardUpdateImpl>(std::addressof(*m_update_task));
|
||||
R_UNLESS(async_result != nullptr, ns::ResultOutOfMaxRunningTask());
|
||||
|
||||
/* Run the task. */
|
||||
R_TRY(async_result.GetImpl().Run());
|
||||
|
||||
/* We prepared the task! */
|
||||
m_requested_update = true;
|
||||
out_event_handle.SetValue(async_result.GetImpl().GetEvent().GetReadableHandle(), false);
|
||||
*out_async = std::move(async_result);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SystemUpdateService::GetPrepareUpdateProgress(sf::Out<SystemUpdateProgress> out) {
|
||||
/* Ensure the update is setup. */
|
||||
R_UNLESS(m_setup_update, ns::ResultCardUpdateNotSetup());
|
||||
|
||||
/* Get the progress. */
|
||||
auto install_progress = m_update_task->GetProgress();
|
||||
out.SetValue({ .current_size = install_progress.installed_size, .total_size = install_progress.total_size });
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SystemUpdateService::HasPreparedUpdate(sf::Out<bool> out) {
|
||||
/* Ensure the update is setup. */
|
||||
R_UNLESS(m_setup_update, ns::ResultCardUpdateNotSetup());
|
||||
|
||||
out.SetValue(m_update_task->GetProgress().state == ncm::InstallProgressState::Downloaded);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SystemUpdateService::ApplyPreparedUpdate() {
|
||||
/* Ensure the update is setup. */
|
||||
R_UNLESS(m_setup_update, ns::ResultCardUpdateNotSetup());
|
||||
|
||||
/* Ensure the update is prepared. */
|
||||
R_UNLESS(m_update_task->GetProgress().state == ncm::InstallProgressState::Downloaded, ns::ResultCardUpdateNotPrepared());
|
||||
|
||||
/* Apply the task. */
|
||||
R_TRY(m_apply_manager.ApplyPackageTask(std::addressof(*m_update_task)));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SystemUpdateService::SetupUpdateImpl(sf::NativeHandle &&transfer_memory, u64 transfer_memory_size, const ncm::Path &path, bool exfat, ncm::FirmwareVariationId firmware_variation_id) {
|
||||
/* Ensure we don't already have an update set up. */
|
||||
R_UNLESS(!m_setup_update, ns::ResultCardUpdateAlreadySetup());
|
||||
|
||||
/* Destroy any existing update tasks. */
|
||||
nim::SystemUpdateTaskId id;
|
||||
auto count = nim::ListSystemUpdateTask(std::addressof(id), 1);
|
||||
if (count > 0) {
|
||||
R_TRY(nim::DestroySystemUpdateTask(id));
|
||||
}
|
||||
|
||||
/* Initialize the update task. */
|
||||
R_TRY(InitializeUpdateTask(std::move(transfer_memory), transfer_memory_size, path, exfat, firmware_variation_id));
|
||||
|
||||
/* The update is now set up. */
|
||||
m_setup_update = true;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SystemUpdateService::InitializeUpdateTask(sf::NativeHandle &&transfer_memory, u64 transfer_memory_size, const ncm::Path &path, bool exfat, ncm::FirmwareVariationId firmware_variation_id) {
|
||||
/* Map the transfer memory. */
|
||||
const size_t tmem_buffer_size = static_cast<size_t>(transfer_memory_size);
|
||||
m_update_transfer_memory.emplace(tmem_buffer_size, transfer_memory.GetOsHandle(), transfer_memory.IsManaged());
|
||||
transfer_memory.Detach();
|
||||
|
||||
void *tmem_buffer;
|
||||
R_TRY(m_update_transfer_memory->Map(std::addressof(tmem_buffer), os::MemoryPermission_None));
|
||||
auto tmem_guard = SCOPE_GUARD {
|
||||
m_update_transfer_memory->Unmap();
|
||||
m_update_transfer_memory = util::nullopt;
|
||||
};
|
||||
|
||||
/* Adjust the package root. */
|
||||
ncm::Path package_root;
|
||||
R_TRY(FormatUserPackagePath(std::addressof(package_root), path));
|
||||
|
||||
/* Ensure that we can create an update context. */
|
||||
R_TRY(fs::EnsureDirectory("@Sdcard:/atmosphere/update/"));
|
||||
const char *context_path = "@Sdcard:/atmosphere/update/cup.ctx";
|
||||
|
||||
/* Create and initialize the update task. */
|
||||
m_update_task.emplace();
|
||||
R_TRY(m_update_task->Initialize(package_root.str, context_path, tmem_buffer, tmem_buffer_size, exfat, firmware_variation_id));
|
||||
|
||||
/* We successfully setup the update. */
|
||||
tmem_guard.Cancel();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "sysupdater_apply_manager.hpp"
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
constexpr inline size_t FirmwareVariationCountMax = 16;
|
||||
|
||||
struct UpdateInformation {
|
||||
u32 version;
|
||||
bool exfat_supported;
|
||||
u32 firmware_variation_count;
|
||||
ncm::FirmwareVariationId firmware_variation_ids[FirmwareVariationCountMax];
|
||||
};
|
||||
|
||||
struct UpdateValidationInfo {
|
||||
ncm::ContentMetaKey invalid_key;
|
||||
ncm::ContentId invalid_content_id;
|
||||
};
|
||||
|
||||
struct SystemUpdateProgress {
|
||||
s64 current_size;
|
||||
s64 total_size;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#define AMS_SYSUPDATER_SYSTEM_UPDATE_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, GetUpdateInformation, (sf::Out<mitm::sysupdater::UpdateInformation> out, const ncm::Path &path), (out, path)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, ValidateUpdate, (sf::Out<Result> out_validate_result, sf::Out<Result> out_validate_exfat_result, sf::Out<mitm::sysupdater::UpdateValidationInfo> out_validate_info, const ncm::Path &path), (out_validate_result, out_validate_exfat_result, out_validate_info, path)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, SetupUpdate, (sf::CopyHandle &&transfer_memory, u64 transfer_memory_size, const ncm::Path &path, bool exfat), (std::move(transfer_memory), transfer_memory_size, path, exfat)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 3, Result, SetupUpdateWithVariation, (sf::CopyHandle &&transfer_memory, u64 transfer_memory_size, const ncm::Path &path, bool exfat, ncm::FirmwareVariationId firmware_variation_id), (std::move(transfer_memory), transfer_memory_size, path, exfat, firmware_variation_id)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, RequestPrepareUpdate, (sf::OutCopyHandle out_event_handle, sf::Out<sf::SharedPointer<ns::impl::IAsyncResult>> out_async), (out_event_handle, out_async)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 5, Result, GetPrepareUpdateProgress, (sf::Out<mitm::sysupdater::SystemUpdateProgress> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 6, Result, HasPreparedUpdate, (sf::Out<bool> out), (out)) \
|
||||
AMS_SF_METHOD_INFO(C, H, 7, Result, ApplyPreparedUpdate, (), ())
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(ams::mitm::sysupdater::impl, ISystemUpdateInterface, AMS_SYSUPDATER_SYSTEM_UPDATE_INTERFACE_INFO, 0x00000000)
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
class SystemUpdateService {
|
||||
private:
|
||||
SystemUpdateApplyManager m_apply_manager;
|
||||
util::optional<ncm::PackageSystemDowngradeTask> m_update_task;
|
||||
util::optional<os::TransferMemory> m_update_transfer_memory;
|
||||
bool m_setup_update;
|
||||
bool m_requested_update;
|
||||
public:
|
||||
constexpr SystemUpdateService() : m_apply_manager(), m_update_task(), m_update_transfer_memory(), m_setup_update(false), m_requested_update(false) { /* ... */ }
|
||||
private:
|
||||
Result SetupUpdateImpl(sf::NativeHandle &&transfer_memory, u64 transfer_memory_size, const ncm::Path &path, bool exfat, ncm::FirmwareVariationId firmware_variation_id);
|
||||
Result InitializeUpdateTask(sf::NativeHandle &&transfer_memory, u64 transfer_memory_size, const ncm::Path &path, bool exfat, ncm::FirmwareVariationId firmware_variation_id);
|
||||
public:
|
||||
Result GetUpdateInformation(sf::Out<UpdateInformation> out, const ncm::Path &path);
|
||||
Result ValidateUpdate(sf::Out<Result> out_validate_result, sf::Out<Result> out_validate_exfat_result, sf::Out<UpdateValidationInfo> out_validate_info, const ncm::Path &path);
|
||||
Result SetupUpdate(sf::CopyHandle &&transfer_memory, u64 transfer_memory_size, const ncm::Path &path, bool exfat);
|
||||
Result SetupUpdateWithVariation(sf::CopyHandle &&transfer_memory, u64 transfer_memory_size, const ncm::Path &path, bool exfat, ncm::FirmwareVariationId firmware_variation_id);
|
||||
Result RequestPrepareUpdate(sf::OutCopyHandle out_event_handle, sf::Out<sf::SharedPointer<ns::impl::IAsyncResult>> out_async);
|
||||
Result GetPrepareUpdateProgress(sf::Out<SystemUpdateProgress> out);
|
||||
Result HasPreparedUpdate(sf::Out<bool> out);
|
||||
Result ApplyPreparedUpdate();
|
||||
};
|
||||
static_assert(impl::IsISystemUpdateInterface<SystemUpdateService>);
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "sysupdater_thread_allocator.hpp"
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
Result ThreadAllocator::Allocate(ThreadInfo *out) {
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
for (int i = 0; i < m_thread_count; ++i) {
|
||||
const u64 mask = (static_cast<u64>(1) << i);
|
||||
if ((m_bitmap & mask) == 0) {
|
||||
*out = {
|
||||
.thread = m_thread_list + i,
|
||||
.priority = m_thread_priority,
|
||||
.stack = m_stack_heap + (m_stack_size * i),
|
||||
.stack_size = m_stack_size,
|
||||
};
|
||||
m_bitmap |= mask;
|
||||
R_SUCCEED();
|
||||
}
|
||||
}
|
||||
|
||||
R_THROW(ns::ResultOutOfMaxRunningTask());
|
||||
}
|
||||
|
||||
void ThreadAllocator::Free(const ThreadInfo &info) {
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
for (int i = 0; i < m_thread_count; ++i) {
|
||||
if (info.thread == std::addressof(m_thread_list[i])) {
|
||||
const u64 mask = (static_cast<u64>(1) << i);
|
||||
m_bitmap &= ~mask;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AMS_ABORT("Invalid thread passed to ThreadAllocator::Free");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::mitm::sysupdater {
|
||||
|
||||
struct ThreadInfo {
|
||||
os::ThreadType *thread;
|
||||
int priority;
|
||||
void *stack;
|
||||
size_t stack_size;
|
||||
};
|
||||
|
||||
/* NOTE: Nintendo uses a util::BitArray, but this seems excessive. */
|
||||
class ThreadAllocator {
|
||||
private:
|
||||
os::ThreadType *m_thread_list;
|
||||
const int m_thread_priority;
|
||||
const int m_thread_count;
|
||||
u8 *m_stack_heap;
|
||||
const size_t m_stack_heap_size;
|
||||
const size_t m_stack_size;
|
||||
u64 m_bitmap;
|
||||
os::SdkMutex m_mutex;
|
||||
public:
|
||||
constexpr ThreadAllocator(os::ThreadType *thread_list, int count, int priority, u8 *stack_heap, size_t stack_heap_size, size_t stack_size)
|
||||
: m_thread_list(thread_list), m_thread_priority(priority), m_thread_count(count), m_stack_heap(stack_heap), m_stack_heap_size(stack_heap_size), m_stack_size(stack_size), m_bitmap()
|
||||
{
|
||||
AMS_ASSERT(count <= static_cast<int>(stack_heap_size / stack_size));
|
||||
AMS_ASSERT(count <= static_cast<int>(BITSIZEOF(m_bitmap)));
|
||||
}
|
||||
|
||||
Result Allocate(ThreadInfo *out);
|
||||
void Free(const ThreadInfo &info);
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user