Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"
This reverts commit 15b7df8ef1.
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user