Merge fs.mitm and set.mitm.
This commit is contained in:
171
stratosphere/ams_mitm/source/fs_mitm/fs_istorage.hpp
Normal file
171
stratosphere/ams_mitm/source/fs_mitm/fs_istorage.hpp
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "fs_shim.h"
|
||||
|
||||
#include "../debug.hpp"
|
||||
|
||||
enum FsIStorageCmd : u32 {
|
||||
FsIStorageCmd_Read = 0,
|
||||
FsIStorageCmd_Write = 1,
|
||||
FsIStorageCmd_Flush = 2,
|
||||
FsIStorageCmd_SetSize = 3,
|
||||
FsIStorageCmd_GetSize = 4,
|
||||
FsIStorageCmd_OperateRange = 5,
|
||||
};
|
||||
|
||||
class IStorage {
|
||||
public:
|
||||
virtual ~IStorage();
|
||||
|
||||
virtual Result Read(void *buffer, size_t size, u64 offset) = 0;
|
||||
virtual Result Write(void *buffer, size_t size, u64 offset) = 0;
|
||||
virtual Result Flush() = 0;
|
||||
virtual Result SetSize(u64 size) = 0;
|
||||
virtual Result GetSize(u64 *out_size) = 0;
|
||||
virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0;
|
||||
};
|
||||
|
||||
class IStorageInterface : public IServiceObject {
|
||||
private:
|
||||
IStorage *base_storage;
|
||||
public:
|
||||
IStorageInterface(IStorage *s) : base_storage(s) {
|
||||
/* ... */
|
||||
};
|
||||
|
||||
~IStorageInterface() {
|
||||
delete base_storage;
|
||||
};
|
||||
|
||||
private:
|
||||
/* Actual command API. */
|
||||
virtual Result Read(OutBuffer<u8, BufferType_Type1> buffer, u64 offset, u64 size) final {
|
||||
return this->base_storage->Read(buffer.buffer, std::min(buffer.num_elements, size), offset);
|
||||
};
|
||||
virtual Result Write(InBuffer<u8, BufferType_Type1> buffer, u64 offset, u64 size) final {
|
||||
return this->base_storage->Write(buffer.buffer, std::min(buffer.num_elements, size), offset);
|
||||
};
|
||||
virtual Result Flush() final {
|
||||
return this->base_storage->Flush();
|
||||
};
|
||||
virtual Result SetSize(u64 size) final {
|
||||
return this->base_storage->SetSize(size);
|
||||
};
|
||||
virtual Result GetSize(Out<u64> size) final {
|
||||
return this->base_storage->GetSize(size.GetPointer());
|
||||
};
|
||||
virtual Result OperateRange(Out<FsRangeInfo> range_info, u32 operation_type, u64 offset, u64 size) final {
|
||||
return this->base_storage->OperateRange(operation_type, offset, size, range_info.GetPointer());
|
||||
};
|
||||
public:
|
||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||
/* 1.0.0- */
|
||||
MakeServiceCommandMeta<FsIStorageCmd_Read, &IStorageInterface::Read>(),
|
||||
MakeServiceCommandMeta<FsIStorageCmd_Write, &IStorageInterface::Write>(),
|
||||
MakeServiceCommandMeta<FsIStorageCmd_Flush, &IStorageInterface::Flush>(),
|
||||
MakeServiceCommandMeta<FsIStorageCmd_SetSize, &IStorageInterface::SetSize>(),
|
||||
MakeServiceCommandMeta<FsIStorageCmd_GetSize, &IStorageInterface::GetSize>(),
|
||||
|
||||
/* 4.0.0- */
|
||||
MakeServiceCommandMeta<FsIStorageCmd_OperateRange, &IStorageInterface::OperateRange, FirmwareVersion_400>(),
|
||||
};
|
||||
};
|
||||
|
||||
class IROStorage : public IStorage {
|
||||
public:
|
||||
virtual Result Read(void *buffer, size_t size, u64 offset) = 0;
|
||||
virtual Result Write(void *buffer, size_t size, u64 offset) final {
|
||||
(void)(buffer);
|
||||
(void)(offset);
|
||||
(void)(size);
|
||||
return 0x313802;
|
||||
};
|
||||
virtual Result Flush() final {
|
||||
return 0x0;
|
||||
};
|
||||
virtual Result SetSize(u64 size) final {
|
||||
(void)(size);
|
||||
return 0x313802;
|
||||
};
|
||||
virtual Result GetSize(u64 *out_size) = 0;
|
||||
virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0;
|
||||
};
|
||||
|
||||
|
||||
class ProxyStorage : public IStorage {
|
||||
private:
|
||||
FsStorage *base_storage;
|
||||
public:
|
||||
ProxyStorage(FsStorage *s) : base_storage(s) {
|
||||
/* ... */
|
||||
};
|
||||
ProxyStorage(FsStorage s) {
|
||||
this->base_storage = new FsStorage(s);
|
||||
};
|
||||
virtual ~ProxyStorage() {
|
||||
fsStorageClose(base_storage);
|
||||
delete base_storage;
|
||||
};
|
||||
public:
|
||||
virtual Result Read(void *buffer, size_t size, u64 offset) override {
|
||||
return fsStorageRead(this->base_storage, offset, buffer, size);
|
||||
};
|
||||
virtual Result Write(void *buffer, size_t size, u64 offset) override {
|
||||
return fsStorageWrite(this->base_storage, offset, buffer, size);
|
||||
};
|
||||
virtual Result Flush() override {
|
||||
return fsStorageFlush(this->base_storage);
|
||||
};
|
||||
virtual Result GetSize(u64 *out_size) override {
|
||||
return fsStorageGetSize(this->base_storage, out_size);
|
||||
};
|
||||
virtual Result SetSize(u64 size) override {
|
||||
return fsStorageSetSize(this->base_storage, size);
|
||||
};
|
||||
virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
|
||||
return fsStorageOperateRange(this->base_storage, operation_type, offset, size, out_range_info);
|
||||
};
|
||||
};
|
||||
|
||||
class ROProxyStorage : public IROStorage {
|
||||
private:
|
||||
FsStorage *base_storage;
|
||||
public:
|
||||
ROProxyStorage(FsStorage *s) : base_storage(s) {
|
||||
/* ... */
|
||||
};
|
||||
ROProxyStorage(FsStorage s) {
|
||||
this->base_storage = new FsStorage(s);
|
||||
};
|
||||
virtual ~ROProxyStorage() {
|
||||
fsStorageClose(base_storage);
|
||||
delete base_storage;
|
||||
};
|
||||
public:
|
||||
virtual Result Read(void *buffer, size_t size, u64 offset) override {
|
||||
return fsStorageRead(this->base_storage, offset, buffer, size);
|
||||
};
|
||||
virtual Result GetSize(u64 *out_size) override {
|
||||
return fsStorageGetSize(this->base_storage, out_size);
|
||||
};
|
||||
virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
|
||||
return fsStorageOperateRange(this->base_storage, operation_type, offset, size, out_range_info);
|
||||
};
|
||||
};
|
||||
215
stratosphere/ams_mitm/source/fs_mitm/fs_shim.c
Normal file
215
stratosphere/ams_mitm/source/fs_mitm/fs_shim.c
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include "fs_shim.h"
|
||||
|
||||
/* Missing fsp-srv commands. */
|
||||
Result fsOpenBisStorageFwd(Service* s, FsStorage* out, u32 PartitionId) {
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
u32 PartitionId;
|
||||
} *raw;
|
||||
|
||||
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 12;
|
||||
raw->PartitionId = PartitionId;
|
||||
|
||||
Result rc = serviceIpcDispatch(s);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
} *resp;
|
||||
|
||||
serviceIpcParse(s, &r, sizeof(*resp));
|
||||
resp = r.Raw;
|
||||
|
||||
rc = resp->result;
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
serviceCreateSubservice(&out->s, s, &r, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out) {
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
} *raw;
|
||||
|
||||
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 200;
|
||||
|
||||
Result rc = serviceIpcDispatch(s);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
} *resp;
|
||||
|
||||
serviceIpcParse(s, &r, sizeof(*resp));
|
||||
resp = r.Raw;
|
||||
|
||||
rc = resp->result;
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
serviceCreateSubservice(&out->s, s, &r, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorageId storage_id, u64 data_id, FsStorage* out) {
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
FsStorageId storage_id;
|
||||
u64 data_id;
|
||||
} *raw;
|
||||
|
||||
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 202;
|
||||
raw->storage_id = storage_id;
|
||||
raw->data_id = data_id;
|
||||
|
||||
Result rc = serviceIpcDispatch(s);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
} *resp;
|
||||
|
||||
serviceIpcParse(s, &r, sizeof(*resp));
|
||||
resp = r.Raw;
|
||||
|
||||
rc = resp->result;
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
serviceCreateSubservice(&out->s, s, &r, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Missing FS File commands. */
|
||||
Result fsFileOperateRange(FsFile* f, u32 op_id, u64 off, u64 len, FsRangeInfo *out) {
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
u32 op_id;
|
||||
u64 off;
|
||||
u64 len;
|
||||
} *raw;
|
||||
|
||||
raw = serviceIpcPrepareHeader(&f->s, &c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 5;
|
||||
raw->op_id = op_id;
|
||||
raw->off = off;
|
||||
raw->len = len;
|
||||
|
||||
Result rc = serviceIpcDispatch(&f->s);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
FsRangeInfo range_info;
|
||||
} *resp;
|
||||
|
||||
serviceIpcParse(&f->s, &r, sizeof(*resp));
|
||||
resp = r.Raw;
|
||||
|
||||
rc = resp->result;
|
||||
if (R_SUCCEEDED(rc) && out) *out = resp->range_info;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Missing FS Storage commands. */
|
||||
Result fsStorageOperateRange(FsStorage* s, u32 op_id, u64 off, u64 len, FsRangeInfo *out) {
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
u32 op_id;
|
||||
u64 off;
|
||||
u64 len;
|
||||
} *raw;
|
||||
|
||||
raw = serviceIpcPrepareHeader(&s->s, &c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 5;
|
||||
raw->op_id = op_id;
|
||||
raw->off = off;
|
||||
raw->len = len;
|
||||
|
||||
Result rc = serviceIpcDispatch(&s->s);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
struct {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
FsRangeInfo range_info;
|
||||
} *resp;
|
||||
|
||||
serviceIpcParse(&s->s, &r, sizeof(*resp));
|
||||
resp = r.Raw;
|
||||
|
||||
rc = resp->result;
|
||||
if (R_SUCCEEDED(rc) && out) *out = resp->range_info;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
32
stratosphere/ams_mitm/source/fs_mitm/fs_shim.h
Normal file
32
stratosphere/ams_mitm/source/fs_mitm/fs_shim.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* @file fs_shim.h
|
||||
* @brief Filesystem Services (fs) IPC wrapper. To be merged into libnx, eventually.
|
||||
* @author SciresM
|
||||
* @copyright libnx Authors
|
||||
*/
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* TODO: Reverse this more. */
|
||||
typedef struct {
|
||||
u32 flags[0x40/sizeof(u32)];
|
||||
} FsRangeInfo;
|
||||
|
||||
/* Missing fsp-srv commands. */
|
||||
Result fsOpenBisStorageFwd(Service* s, FsStorage* out, u32 PartitionId);
|
||||
Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out);
|
||||
Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorageId storage_id, u64 data_id, FsStorage* out);
|
||||
|
||||
/* Missing FS File commands. */
|
||||
Result fsFileOperateRange(FsFile* f, u32 op_id, u64 off, u64 len, FsRangeInfo *out);
|
||||
|
||||
/* Missing FS Storage commands. */
|
||||
Result fsStorageOperateRange(FsStorage* s, u32 op_id, u64 off, u64 len, FsRangeInfo *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
108
stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp
Normal file
108
stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <cstring>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "fsmitm_boot0storage.hpp"
|
||||
|
||||
static HosMutex g_boot0_mutex;
|
||||
static u8 g_boot0_bct_buffer[Boot0Storage::BctEndOffset];
|
||||
|
||||
bool Boot0Storage::CanModifyBctPubks() {
|
||||
return this->title_id != 0x010000000000001FULL;
|
||||
}
|
||||
|
||||
Result Boot0Storage::Read(void *_buffer, size_t size, u64 offset) {
|
||||
std::scoped_lock<HosMutex> lk{g_boot0_mutex};
|
||||
|
||||
return Base::Read(_buffer, size, offset);
|
||||
}
|
||||
|
||||
Result Boot0Storage::Write(void *_buffer, size_t size, u64 offset) {
|
||||
std::scoped_lock<HosMutex> lk{g_boot0_mutex};
|
||||
|
||||
Result rc = 0;
|
||||
u8 *buffer = static_cast<u8 *>(_buffer);
|
||||
|
||||
/* Protect the keyblob region from writes. */
|
||||
if (offset <= EksStart) {
|
||||
if (offset + size < EksStart) {
|
||||
/* Fall through, no need to do anything here. */
|
||||
} else {
|
||||
if (offset + size < EksEnd) {
|
||||
/* Adjust size to avoid writing end of data. */
|
||||
size = EksStart - offset;
|
||||
} else {
|
||||
/* Perform portion of write falling past end of keyblobs. */
|
||||
const u64 diff = EksEnd - offset;
|
||||
if (R_FAILED((rc = Base::Write(buffer + diff, size - diff, EksEnd)))) {
|
||||
return rc;
|
||||
}
|
||||
/* Adjust size to avoid writing end of data. */
|
||||
size = EksStart - offset;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (offset < EksEnd) {
|
||||
if (offset + size < EksEnd) {
|
||||
/* Ignore writes falling strictly within the region. */
|
||||
return 0;
|
||||
} else {
|
||||
/* Only write past the end of the keyblob region. */
|
||||
buffer = buffer + (EksEnd - offset);
|
||||
size -= (EksEnd - offset);
|
||||
offset = EksEnd;
|
||||
}
|
||||
} else {
|
||||
/* Fall through, no need to do anything here. */
|
||||
}
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We care about protecting autorcm from NS. */
|
||||
if (CanModifyBctPubks() || offset >= BctEndOffset || (offset + BctSize >= BctEndOffset && offset % BctSize >= BctPubkEnd)) {
|
||||
return Base::Write(buffer, size, offset);
|
||||
}
|
||||
|
||||
/* First, let's deal with the data past the end. */
|
||||
if (offset + size >= BctEndOffset) {
|
||||
const u64 diff = BctEndOffset - offset;
|
||||
if (R_FAILED((rc = ProxyStorage::Write(buffer + diff, size - diff, BctEndOffset)))) {
|
||||
return rc;
|
||||
}
|
||||
size = diff;
|
||||
}
|
||||
|
||||
/* Read in the current BCT region. */
|
||||
if (R_FAILED((rc = ProxyStorage::Read(g_boot0_bct_buffer, BctEndOffset, 0)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Update the bct buffer. */
|
||||
for (u64 cur_ofs = offset; cur_ofs < BctEndOffset && cur_ofs < offset + size; cur_ofs++) {
|
||||
const u64 cur_bct_rel_ofs = cur_ofs % BctSize;
|
||||
if (cur_bct_rel_ofs < BctPubkStart || BctPubkEnd <= cur_bct_rel_ofs) {
|
||||
g_boot0_bct_buffer[cur_ofs] = buffer[cur_ofs - offset];
|
||||
}
|
||||
}
|
||||
|
||||
return ProxyStorage::Write(g_boot0_bct_buffer, BctEndOffset, 0);
|
||||
}
|
||||
159
stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.hpp
Normal file
159
stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.hpp
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <cstring>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "fs_istorage.hpp"
|
||||
|
||||
/* Represents a sectored storage. */
|
||||
template<u64 SectorSize>
|
||||
class SectoredProxyStorage : public ProxyStorage {
|
||||
private:
|
||||
u64 cur_seek = 0;
|
||||
u64 cur_sector = 0;
|
||||
u64 cur_sector_ofs = 0;
|
||||
u8 sector_buf[SectorSize];
|
||||
private:
|
||||
void Seek(u64 offset) {
|
||||
this->cur_sector_ofs = offset % SectorSize;
|
||||
this->cur_seek = offset - this->cur_sector_ofs;
|
||||
}
|
||||
public:
|
||||
SectoredProxyStorage(FsStorage *s) : ProxyStorage(s) { }
|
||||
SectoredProxyStorage(FsStorage s) : ProxyStorage(s) { }
|
||||
public:
|
||||
virtual Result Read(void *_buffer, size_t size, u64 offset) override {
|
||||
Result rc = 0;
|
||||
u8 *buffer = static_cast<u8 *>(_buffer);
|
||||
this->Seek(offset);
|
||||
|
||||
if (this->cur_sector_ofs == 0 && size % SectorSize == 0) {
|
||||
/* Fast case. */
|
||||
return ProxyStorage::Read(buffer, size, offset);
|
||||
}
|
||||
|
||||
if (R_FAILED((rc = ProxyStorage::Read(this->sector_buf, SectorSize, this->cur_seek)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (size + this->cur_sector_ofs <= SectorSize) {
|
||||
memcpy(buffer, sector_buf + this->cur_sector_ofs, size);
|
||||
} else {
|
||||
/* Leaving the sector... */
|
||||
size_t ofs = SectorSize - this->cur_sector_ofs;
|
||||
memcpy(buffer, sector_buf + this->cur_sector_ofs, ofs);
|
||||
size -= ofs;
|
||||
|
||||
/* We're guaranteed alignment, here. */
|
||||
const size_t aligned_remaining_size = size - (size % SectorSize);
|
||||
if (aligned_remaining_size) {
|
||||
if (R_FAILED((rc = ProxyStorage::Read(buffer + ofs, aligned_remaining_size, offset + ofs)))) {
|
||||
return rc;
|
||||
}
|
||||
ofs += aligned_remaining_size;
|
||||
size -= aligned_remaining_size;
|
||||
}
|
||||
|
||||
/* Read any leftover data. */
|
||||
if (size) {
|
||||
if (R_FAILED((rc = ProxyStorage::Read(this->sector_buf, SectorSize, offset + ofs)))) {
|
||||
return rc;
|
||||
}
|
||||
memcpy(buffer + ofs, sector_buf, size);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
};
|
||||
virtual Result Write(void *_buffer, size_t size, u64 offset) override {
|
||||
Result rc = 0;
|
||||
u8 *buffer = static_cast<u8 *>(_buffer);
|
||||
this->Seek(offset);
|
||||
|
||||
if (this->cur_sector_ofs == 0 && size % SectorSize == 0) {
|
||||
/* Fast case. */
|
||||
return ProxyStorage::Write(buffer, size, offset);
|
||||
}
|
||||
|
||||
if (R_FAILED((rc = ProxyStorage::Read(this->sector_buf, SectorSize, this->cur_seek)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
if (size + this->cur_sector_ofs <= SectorSize) {
|
||||
memcpy(this->sector_buf + this->cur_sector_ofs, buffer, size);
|
||||
rc = ProxyStorage::Write(this->sector_buf, SectorSize, this->cur_seek);
|
||||
} else {
|
||||
/* Leaving the sector... */
|
||||
size_t ofs = SectorSize - this->cur_sector_ofs;
|
||||
memcpy(this->sector_buf + this->cur_sector_ofs, buffer, ofs);
|
||||
if (R_FAILED((rc = ProxyStorage::Write(this->sector_buf, ofs, this->cur_seek)))) {
|
||||
return rc;
|
||||
}
|
||||
size -= ofs;
|
||||
|
||||
/* We're guaranteed alignment, here. */
|
||||
const size_t aligned_remaining_size = size - (size % SectorSize);
|
||||
if (aligned_remaining_size) {
|
||||
if (R_FAILED((rc = ProxyStorage::Write(buffer + ofs, aligned_remaining_size, offset + ofs)))) {
|
||||
return rc;
|
||||
}
|
||||
ofs += aligned_remaining_size;
|
||||
size -= aligned_remaining_size;
|
||||
}
|
||||
|
||||
/* Write any leftover data. */
|
||||
if (size) {
|
||||
if (R_FAILED((rc = ProxyStorage::Read(this->sector_buf, SectorSize, offset + ofs)))) {
|
||||
return rc;
|
||||
}
|
||||
memcpy(this->sector_buf, buffer + ofs, size);
|
||||
rc = ProxyStorage::Write(this->sector_buf, SectorSize, this->cur_seek);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
};
|
||||
};
|
||||
|
||||
/* Represents an RCM-preserving BOOT0 partition. */
|
||||
class Boot0Storage : public SectoredProxyStorage<0x200> {
|
||||
using Base = SectoredProxyStorage<0x200>;
|
||||
|
||||
public:
|
||||
static constexpr u64 BctEndOffset = 0xFC000;
|
||||
static constexpr u64 BctSize = 0x4000;
|
||||
static constexpr u64 BctPubkStart = 0x210;
|
||||
static constexpr u64 BctPubkSize = 0x100;
|
||||
static constexpr u64 BctPubkEnd = BctPubkStart + BctPubkSize;
|
||||
|
||||
static constexpr u64 EksStart = 0x180000;
|
||||
static constexpr u64 EksSize = 0x4000;
|
||||
static constexpr u64 EksEnd = EksStart + EksSize;
|
||||
private:
|
||||
u64 title_id;
|
||||
private:
|
||||
bool CanModifyBctPubks();
|
||||
public:
|
||||
Boot0Storage(FsStorage *s, u64 t) : Base(s), title_id(t) { }
|
||||
Boot0Storage(FsStorage s, u64 t) : Base(s), title_id(t) { }
|
||||
public:
|
||||
virtual Result Read(void *_buffer, size_t size, u64 offset) override;
|
||||
virtual Result Write(void *_buffer, size_t size, u64 offset) override;
|
||||
};
|
||||
171
stratosphere/ams_mitm/source/fs_mitm/fsmitm_layeredrom.cpp
Normal file
171
stratosphere/ams_mitm/source/fs_mitm/fsmitm_layeredrom.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "fsmitm_layeredrom.hpp"
|
||||
#include "../utils.hpp"
|
||||
#include "../debug.hpp"
|
||||
|
||||
IStorage::~IStorage() = default;
|
||||
|
||||
LayeredRomFS::LayeredRomFS(std::shared_ptr<RomInterfaceStorage> s_r, std::shared_ptr<RomFileStorage> f_r, u64 tid) : storage_romfs(s_r), file_romfs(f_r), title_id(tid) {
|
||||
/* Start building the new virtual romfs. */
|
||||
RomFSBuildContext build_ctx(this->title_id);
|
||||
this->p_source_infos = std::shared_ptr<std::vector<RomFSSourceInfo>>(new std::vector<RomFSSourceInfo>(), [](std::vector<RomFSSourceInfo> *to_delete) {
|
||||
for (unsigned int i = 0; i < to_delete->size(); i++) {
|
||||
(*to_delete)[i].Cleanup();
|
||||
}
|
||||
delete to_delete;
|
||||
});
|
||||
if (Utils::IsSdInitialized()) {
|
||||
build_ctx.MergeSdFiles();
|
||||
}
|
||||
if (this->file_romfs) {
|
||||
build_ctx.MergeRomStorage(this->file_romfs.get(), RomFSDataSource::FileRomFS);
|
||||
}
|
||||
if (this->storage_romfs) {
|
||||
build_ctx.MergeRomStorage(this->storage_romfs.get(), RomFSDataSource::BaseRomFS);
|
||||
}
|
||||
build_ctx.Build(this->p_source_infos.get());
|
||||
}
|
||||
|
||||
|
||||
Result LayeredRomFS::Read(void *buffer, size_t size, u64 offset) {
|
||||
/* Size zero reads should always succeed. */
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Validate size. */
|
||||
u64 virt_size = (*this->p_source_infos)[this->p_source_infos->size() - 1].virtual_offset + (*this->p_source_infos)[this->p_source_infos->size() - 1].size;
|
||||
if (offset >= virt_size) {
|
||||
return 0x2F5A02;
|
||||
}
|
||||
if (virt_size - offset < size) {
|
||||
size = virt_size - offset;
|
||||
}
|
||||
/* Find first source info via binary search. */
|
||||
u32 cur_source_ind = 0;
|
||||
u32 low = 0, high = this->p_source_infos->size() - 1;
|
||||
while (low <= high) {
|
||||
u32 mid = (low + high) / 2;
|
||||
if ((*this->p_source_infos)[mid].virtual_offset > offset) {
|
||||
/* Too high. */
|
||||
high = mid - 1;
|
||||
} else {
|
||||
/* sources[mid].virtual_offset <= offset, invariant */
|
||||
if (mid == this->p_source_infos->size() - 1 || (*this->p_source_infos)[mid + 1].virtual_offset > offset) {
|
||||
/* Success */
|
||||
cur_source_ind = mid;
|
||||
break;
|
||||
}
|
||||
low = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
Result rc;
|
||||
size_t read_so_far = 0;
|
||||
while (read_so_far < size) {
|
||||
RomFSSourceInfo *cur_source = &((*this->p_source_infos)[cur_source_ind]);
|
||||
if (cur_source->virtual_offset + cur_source->size > offset) {
|
||||
u64 cur_read_size = size - read_so_far;
|
||||
if (cur_read_size > cur_source->size - (offset - cur_source->virtual_offset)) {
|
||||
cur_read_size = cur_source->size - (offset - cur_source->virtual_offset);
|
||||
}
|
||||
switch (cur_source->type) {
|
||||
case RomFSDataSource::MetaData:
|
||||
{
|
||||
FsFile file;
|
||||
if (R_FAILED((rc = Utils::OpenSdFileForAtmosphere(this->title_id, ROMFS_METADATA_FILE_PATH, FS_OPEN_READ, &file)))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
size_t out_read;
|
||||
if (R_FAILED((rc = fsFileRead(&file, (offset - cur_source->virtual_offset), (void *)((uintptr_t)buffer + read_so_far), cur_read_size, &out_read)))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
if (out_read != cur_read_size) {
|
||||
Reboot();
|
||||
}
|
||||
fsFileClose(&file);
|
||||
}
|
||||
break;
|
||||
case RomFSDataSource::LooseFile:
|
||||
{
|
||||
FsFile file;
|
||||
if (R_FAILED((rc = Utils::OpenRomFSSdFile(this->title_id, cur_source->loose_source_info.path, FS_OPEN_READ, &file)))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
size_t out_read;
|
||||
if (R_FAILED((rc = fsFileRead(&file, (offset - cur_source->virtual_offset), (void *)((uintptr_t)buffer + read_so_far), cur_read_size, &out_read)))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
if (out_read != cur_read_size) {
|
||||
Reboot();
|
||||
}
|
||||
fsFileClose(&file);
|
||||
}
|
||||
break;
|
||||
case RomFSDataSource::Memory:
|
||||
{
|
||||
memcpy((void *)((uintptr_t)buffer + read_so_far), cur_source->memory_source_info.data + (offset - cur_source->virtual_offset), cur_read_size);
|
||||
}
|
||||
break;
|
||||
case RomFSDataSource::BaseRomFS:
|
||||
{
|
||||
if (R_FAILED((rc = this->storage_romfs->Read((void *)((uintptr_t)buffer + read_so_far), cur_read_size, cur_source->base_source_info.offset + (offset - cur_source->virtual_offset))))) {
|
||||
/* TODO: Can this ever happen? */
|
||||
/* fatalSimple(rc); */
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RomFSDataSource::FileRomFS:
|
||||
{
|
||||
if (R_FAILED((rc = this->file_romfs->Read((void *)((uintptr_t)buffer + read_so_far), cur_read_size, cur_source->base_source_info.offset + (offset - cur_source->virtual_offset))))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fatalSimple(0xF601);
|
||||
}
|
||||
read_so_far += cur_read_size;
|
||||
offset += cur_read_size;
|
||||
} else {
|
||||
/* Handle padding explicitly. */
|
||||
cur_source_ind++;
|
||||
/* Zero out the padding we skip, here. */
|
||||
memset((void *)((uintptr_t)buffer + read_so_far), 0, ((*this->p_source_infos)[cur_source_ind]).virtual_offset - offset);
|
||||
read_so_far += ((*this->p_source_infos)[cur_source_ind]).virtual_offset - offset;
|
||||
offset = ((*this->p_source_infos)[cur_source_ind]).virtual_offset;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Result LayeredRomFS::GetSize(u64 *out_size) {
|
||||
*out_size = (*this->p_source_infos)[this->p_source_infos->size() - 1].virtual_offset + (*this->p_source_infos)[this->p_source_infos->size() - 1].size;
|
||||
return 0x0;
|
||||
}
|
||||
Result LayeredRomFS::OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) {
|
||||
/* TODO: How should I implement this for a virtual romfs? */
|
||||
if (operation_type == 3) {
|
||||
*out_range_info = {0};
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
43
stratosphere/ams_mitm/source/fs_mitm/fsmitm_layeredrom.hpp
Normal file
43
stratosphere/ams_mitm/source/fs_mitm/fsmitm_layeredrom.hpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "fsmitm_romstorage.hpp"
|
||||
#include "fsmitm_romfsbuild.hpp"
|
||||
#include "../utils.hpp"
|
||||
|
||||
|
||||
/* Represents a merged RomFS. */
|
||||
class LayeredRomFS : public IROStorage {
|
||||
private:
|
||||
/* Data Sources. */
|
||||
std::shared_ptr<RomInterfaceStorage> storage_romfs;
|
||||
std::shared_ptr<RomFileStorage> file_romfs;
|
||||
/* Information about the merged RomFS. */
|
||||
u64 title_id;
|
||||
std::shared_ptr<std::vector<RomFSSourceInfo>> p_source_infos;
|
||||
|
||||
public:
|
||||
LayeredRomFS(std::shared_ptr<RomInterfaceStorage> s_r, std::shared_ptr<RomFileStorage> f_r, u64 tid);
|
||||
virtual ~LayeredRomFS() = default;
|
||||
|
||||
virtual Result Read(void *buffer, size_t size, u64 offset) override;
|
||||
virtual Result GetSize(u64 *out_size) override;
|
||||
virtual Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override;
|
||||
};
|
||||
50
stratosphere/ams_mitm/source/fs_mitm/fsmitm_main.cpp
Normal file
50
stratosphere/ams_mitm/source/fs_mitm/fsmitm_main.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <switch.h>
|
||||
#include <atmosphere.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "fsmitm_main.hpp"
|
||||
#include "fsmitm_service.hpp"
|
||||
|
||||
#include "../utils.hpp"
|
||||
|
||||
struct FsMitmManagerOptions {
|
||||
static const size_t PointerBufferSize = 0x800;
|
||||
static const size_t MaxDomains = 0x40;
|
||||
static const size_t MaxDomainObjects = 0x4000;
|
||||
};
|
||||
using FsMitmManager = WaitableManager<FsMitmManagerOptions>;
|
||||
|
||||
void FsMitmMain(void *arg) {
|
||||
/* Create server manager. */
|
||||
auto server_manager = new FsMitmManager(5);
|
||||
|
||||
/* Create fsp-srv mitm. */
|
||||
AddMitmServerToManager<FsMitmService>(server_manager, "fsp-srv", 61);
|
||||
|
||||
/* Loop forever, servicing our services. */
|
||||
server_manager->Process();
|
||||
|
||||
delete server_manager;
|
||||
}
|
||||
|
||||
29
stratosphere/ams_mitm/source/fs_mitm/fsmitm_main.hpp
Normal file
29
stratosphere/ams_mitm/source/fs_mitm/fsmitm_main.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <malloc.h>
|
||||
|
||||
#include <switch.h>
|
||||
#include <atmosphere.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
constexpr u32 FsMitmPriority = 43;
|
||||
constexpr u32 FsMitmStackSize = 0x8000;
|
||||
|
||||
void FsMitmMain(void *arg);
|
||||
411
stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfsbuild.cpp
Normal file
411
stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfsbuild.cpp
Normal file
@@ -0,0 +1,411 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <string.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "../utils.hpp"
|
||||
#include "fsmitm_romfsbuild.hpp"
|
||||
|
||||
#include "../debug.hpp"
|
||||
|
||||
void RomFSBuildContext::VisitDirectory(FsFileSystem *filesys, RomFSBuildDirectoryContext *parent) {
|
||||
FsDir dir;
|
||||
Result rc;
|
||||
|
||||
std::vector<RomFSBuildDirectoryContext *> child_dirs;
|
||||
|
||||
/* Open the current parent directory. */
|
||||
if (R_FAILED((rc = Utils::OpenRomFSDir(filesys, this->title_id, parent->path, &dir)))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
|
||||
u64 read_entries;
|
||||
while (R_SUCCEEDED((rc = fsDirRead(&dir, 0, &read_entries, 1, &this->dir_entry))) && read_entries == 1) {
|
||||
if (this->dir_entry.type == ENTRYTYPE_DIR) {
|
||||
RomFSBuildDirectoryContext *child = new RomFSBuildDirectoryContext({0});
|
||||
/* Set child's path. */
|
||||
child->cur_path_ofs = parent->path_len + 1;
|
||||
child->path_len = child->cur_path_ofs + strlen(this->dir_entry.name);
|
||||
child->path = new char[child->path_len + 1];
|
||||
strcpy(child->path, parent->path);
|
||||
if (child->path_len > FS_MAX_PATH - 1) {
|
||||
fatalSimple(0xF601);
|
||||
}
|
||||
strcat(child->path + parent->path_len, "/");
|
||||
strcat(child->path + parent->path_len, this->dir_entry.name);
|
||||
|
||||
if (!this->AddDirectory(parent, child, NULL)) {
|
||||
delete child->path;
|
||||
delete child;
|
||||
} else {
|
||||
child_dirs.push_back(child);
|
||||
}
|
||||
} else if (this->dir_entry.type == ENTRYTYPE_FILE) {
|
||||
RomFSBuildFileContext *child = new RomFSBuildFileContext({0});
|
||||
/* Set child's path. */
|
||||
child->cur_path_ofs = parent->path_len + 1;
|
||||
child->path_len = child->cur_path_ofs + strlen(this->dir_entry.name);
|
||||
child->path = new char[child->path_len + 1];
|
||||
strcpy(child->path, parent->path);
|
||||
if (child->path_len > FS_MAX_PATH - 1) {
|
||||
fatalSimple(0xF601);
|
||||
}
|
||||
strcat(child->path + parent->path_len, "/");
|
||||
strcat(child->path + parent->path_len, this->dir_entry.name);
|
||||
|
||||
child->source = this->cur_source_type;
|
||||
|
||||
child->size = this->dir_entry.fileSize;
|
||||
|
||||
if (!this->AddFile(parent, child)) {
|
||||
delete child->path;
|
||||
delete child;
|
||||
}
|
||||
} else {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
}
|
||||
fsDirClose(&dir);
|
||||
|
||||
for (auto &child : child_dirs) {
|
||||
this->VisitDirectory(filesys, child);
|
||||
}
|
||||
}
|
||||
|
||||
void RomFSBuildContext::MergeSdFiles() {
|
||||
FsFileSystem sd_filesystem;
|
||||
FsDir dir;
|
||||
if (!Utils::IsSdInitialized()) {
|
||||
return;
|
||||
}
|
||||
if (R_FAILED((Utils::OpenSdDirForAtmosphere(this->title_id, "/romfs", &dir)))) {
|
||||
return;
|
||||
}
|
||||
fsDirClose(&dir);
|
||||
if (R_FAILED(fsMountSdcard(&sd_filesystem))) {
|
||||
return;
|
||||
}
|
||||
this->cur_source_type = RomFSDataSource::LooseFile;
|
||||
this->VisitDirectory(&sd_filesystem, this->root);
|
||||
fsFsClose(&sd_filesystem);
|
||||
}
|
||||
|
||||
void RomFSBuildContext::VisitDirectory(RomFSBuildDirectoryContext *parent, u32 parent_offset, void *dir_table, size_t dir_table_size, void *file_table, size_t file_table_size) {
|
||||
RomFSDirectoryEntry *parent_entry = romfs_get_direntry(dir_table, parent_offset);
|
||||
if (parent_entry->file != ROMFS_ENTRY_EMPTY) {
|
||||
RomFSFileEntry *cur_file = romfs_get_fentry(file_table, parent_entry->file);
|
||||
while (cur_file != NULL) {
|
||||
RomFSBuildFileContext *child = new RomFSBuildFileContext({0});
|
||||
/* Set child's path. */
|
||||
child->cur_path_ofs = parent->path_len + 1;
|
||||
child->path_len = child->cur_path_ofs + cur_file->name_size;
|
||||
child->path = new char[child->path_len + 1];
|
||||
strcpy(child->path, parent->path);
|
||||
if (child->path_len > FS_MAX_PATH - 1) {
|
||||
fatalSimple(0xF601);
|
||||
}
|
||||
strcat(child->path + parent->path_len, "/");
|
||||
strncat(child->path + parent->path_len, cur_file->name, cur_file->name_size);
|
||||
child->size = cur_file->size;
|
||||
|
||||
child->source = this->cur_source_type;
|
||||
child->orig_offset = cur_file->offset;
|
||||
if (!this->AddFile(parent, child)) {
|
||||
delete child->path;
|
||||
delete child;
|
||||
}
|
||||
if (cur_file->sibling == ROMFS_ENTRY_EMPTY) {
|
||||
cur_file = NULL;
|
||||
} else {
|
||||
cur_file = romfs_get_fentry(file_table, cur_file->sibling);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parent_entry->child != ROMFS_ENTRY_EMPTY) {
|
||||
RomFSDirectoryEntry *cur_child = romfs_get_direntry(dir_table, parent_entry->child);
|
||||
u32 cur_child_offset = parent_entry->child;
|
||||
while (cur_child != NULL) {
|
||||
RomFSBuildDirectoryContext *child = new RomFSBuildDirectoryContext({0});
|
||||
/* Set child's path. */
|
||||
child->cur_path_ofs = parent->path_len + 1;
|
||||
child->path_len = child->cur_path_ofs + cur_child->name_size;
|
||||
child->path = new char[child->path_len + 1];
|
||||
strcpy(child->path, parent->path);
|
||||
if (child->path_len > FS_MAX_PATH - 1) {
|
||||
fatalSimple(0xF601);
|
||||
}
|
||||
strcat(child->path + parent->path_len, "/");
|
||||
strncat(child->path + parent->path_len, cur_child->name, cur_child->name_size);
|
||||
|
||||
RomFSBuildDirectoryContext *real = NULL;
|
||||
if (!this->AddDirectory(parent, child, &real)) {
|
||||
delete child->path;
|
||||
delete child;
|
||||
}
|
||||
if (real == NULL) {
|
||||
fatalSimple(0xF601);
|
||||
}
|
||||
|
||||
this->VisitDirectory(real, cur_child_offset, dir_table, dir_table_size, file_table, file_table_size);
|
||||
|
||||
if (cur_child->sibling == ROMFS_ENTRY_EMPTY) {
|
||||
cur_child = NULL;
|
||||
} else {
|
||||
cur_child_offset = cur_child->sibling;
|
||||
cur_child = romfs_get_direntry(dir_table, cur_child->sibling);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RomFSBuildContext::MergeRomStorage(IROStorage *storage, RomFSDataSource source) {
|
||||
Result rc;
|
||||
RomFSHeader header;
|
||||
if (R_FAILED((rc = storage->Read(&header, sizeof(header), 0)))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
if (header.header_size != sizeof(header)) {
|
||||
/* what */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Read tables. */
|
||||
auto dir_table = std::make_unique<u8[]>(header.dir_table_size);
|
||||
auto file_table = std::make_unique<u8[]>(header.file_table_size);
|
||||
if (R_FAILED((rc = storage->Read(dir_table.get(), header.dir_table_size, header.dir_table_ofs)))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
if (R_FAILED((rc = storage->Read(file_table.get(), header.file_table_size, header.file_table_ofs)))) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
|
||||
this->cur_source_type = source;
|
||||
this->VisitDirectory(this->root, 0x0, dir_table.get(), (size_t)header.dir_table_size, file_table.get(), (size_t)header.file_table_size);
|
||||
}
|
||||
|
||||
bool RomFSBuildContext::AddDirectory(RomFSBuildDirectoryContext *parent_dir_ctx, RomFSBuildDirectoryContext *dir_ctx, RomFSBuildDirectoryContext **out_dir_ctx) {
|
||||
/* Check whether it's already in the known directories. */
|
||||
auto existing = this->directories.find(dir_ctx->path);
|
||||
if (existing != this->directories.end()) {
|
||||
if (out_dir_ctx) {
|
||||
*out_dir_ctx = existing->second;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Add a new directory. */
|
||||
this->num_dirs++;
|
||||
this->dir_table_size += sizeof(RomFSDirectoryEntry) + ((dir_ctx->path_len - dir_ctx->cur_path_ofs + 3) & ~3);
|
||||
dir_ctx->parent = parent_dir_ctx;
|
||||
this->directories.insert({dir_ctx->path, dir_ctx});
|
||||
|
||||
if (out_dir_ctx) {
|
||||
*out_dir_ctx = dir_ctx;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RomFSBuildContext::AddFile(RomFSBuildDirectoryContext *parent_dir_ctx, RomFSBuildFileContext *file_ctx) {
|
||||
/* Check whether it's already in the known files. */
|
||||
auto existing = this->files.find(file_ctx->path);
|
||||
if (existing != this->files.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Add a new file. */
|
||||
this->num_files++;
|
||||
this->file_table_size += sizeof(RomFSFileEntry) + ((file_ctx->path_len - file_ctx->cur_path_ofs + 3) & ~3);
|
||||
file_ctx->parent = parent_dir_ctx;
|
||||
this->files.insert({file_ctx->path, file_ctx});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RomFSBuildContext::Build(std::vector<RomFSSourceInfo> *out_infos) {
|
||||
RomFSBuildFileContext *cur_file;
|
||||
RomFSBuildDirectoryContext *cur_dir;
|
||||
u32 entry_offset;
|
||||
|
||||
u32 dir_hash_table_entry_count = romfs_get_hash_table_count(this->num_dirs);
|
||||
u32 file_hash_table_entry_count = romfs_get_hash_table_count(this->num_files);
|
||||
this->dir_hash_table_size = 4 * dir_hash_table_entry_count;
|
||||
this->file_hash_table_size = 4 * file_hash_table_entry_count;
|
||||
|
||||
/* Assign metadata pointers */
|
||||
RomFSHeader *header = new RomFSHeader({0});
|
||||
u8 *metadata = new u8[this->dir_hash_table_size + this->dir_table_size + this->file_hash_table_size + this->file_table_size];
|
||||
u32 *dir_hash_table = (u32 *)((uintptr_t)metadata);
|
||||
RomFSDirectoryEntry *dir_table = (RomFSDirectoryEntry *)((uintptr_t)dir_hash_table + this->dir_hash_table_size);
|
||||
u32 *file_hash_table = (u32 *)((uintptr_t)dir_table + this->dir_table_size);
|
||||
RomFSFileEntry *file_table = (RomFSFileEntry *)((uintptr_t)file_hash_table + this->file_hash_table_size);
|
||||
|
||||
/* Clear out hash tables. */
|
||||
for (u32 i = 0; i < dir_hash_table_entry_count; i++) {
|
||||
dir_hash_table[i] = ROMFS_ENTRY_EMPTY;
|
||||
}
|
||||
for (u32 i = 0; i < file_hash_table_entry_count; i++) {
|
||||
file_hash_table[i] = ROMFS_ENTRY_EMPTY;
|
||||
}
|
||||
|
||||
out_infos->clear();
|
||||
out_infos->emplace_back(0, sizeof(*header), header, RomFSDataSource::Memory);
|
||||
|
||||
/* Determine file offsets. */
|
||||
entry_offset = 0;
|
||||
RomFSBuildFileContext *prev_file = NULL;
|
||||
for (const auto &it : this->files) {
|
||||
cur_file = it.second;
|
||||
this->file_partition_size = (this->file_partition_size + 0xFULL) & ~0xFULL;
|
||||
/* Check for extra padding in the original romfs source and preserve it, to help ourselves later. */
|
||||
if (prev_file && prev_file->source == cur_file->source && (prev_file->source == RomFSDataSource::BaseRomFS || prev_file->source == RomFSDataSource::FileRomFS)) {
|
||||
u64 expected = (this->file_partition_size - prev_file->offset + prev_file->orig_offset);
|
||||
if (expected != cur_file->orig_offset) {
|
||||
if (expected > cur_file->orig_offset) {
|
||||
/* This case should NEVER happen. */
|
||||
fatalSimple(0xF601);
|
||||
}
|
||||
this->file_partition_size += cur_file->orig_offset - expected;
|
||||
}
|
||||
}
|
||||
cur_file->offset = this->file_partition_size;
|
||||
this->file_partition_size += cur_file->size;
|
||||
cur_file->entry_offset = entry_offset;
|
||||
entry_offset += sizeof(RomFSFileEntry) + ((cur_file->path_len - cur_file->cur_path_ofs + 3) & ~3);
|
||||
prev_file = cur_file;
|
||||
}
|
||||
/* Assign deferred parent/sibling ownership. */
|
||||
for (auto it = this->files.rbegin(); it != this->files.rend(); it++) {
|
||||
cur_file = it->second;
|
||||
cur_file->sibling = cur_file->parent->file;
|
||||
cur_file->parent->file = cur_file;
|
||||
}
|
||||
|
||||
/* Determine directory offsets. */
|
||||
entry_offset = 0;
|
||||
for (const auto &it : this->directories) {
|
||||
cur_dir = it.second;
|
||||
cur_dir->entry_offset = entry_offset;
|
||||
entry_offset += sizeof(RomFSDirectoryEntry) + ((cur_dir->path_len - cur_dir->cur_path_ofs + 3) & ~3);
|
||||
}
|
||||
/* Assign deferred parent/sibling ownership. */
|
||||
for (auto it = this->directories.rbegin(); it->second != this->root; it++) {
|
||||
cur_dir = it->second;
|
||||
cur_dir->sibling = cur_dir->parent->child;
|
||||
cur_dir->parent->child = cur_dir;
|
||||
}
|
||||
|
||||
|
||||
/* Populate file tables. */
|
||||
for (const auto &it : this->files) {
|
||||
cur_file = it.second;
|
||||
RomFSFileEntry *cur_entry = romfs_get_fentry(file_table, cur_file->entry_offset);
|
||||
|
||||
cur_entry->parent = cur_file->parent->entry_offset;
|
||||
cur_entry->sibling = (cur_file->sibling == NULL) ? ROMFS_ENTRY_EMPTY : cur_file->sibling->entry_offset;
|
||||
cur_entry->offset = cur_file->offset;
|
||||
cur_entry->size = cur_file->size;
|
||||
|
||||
u32 name_size = cur_file->path_len - cur_file->cur_path_ofs;
|
||||
u32 hash = romfs_calc_path_hash(cur_file->parent->entry_offset, (unsigned char *)cur_file->path + cur_file->cur_path_ofs, 0, name_size);
|
||||
cur_entry->hash = file_hash_table[hash % file_hash_table_entry_count];
|
||||
file_hash_table[hash % file_hash_table_entry_count] = cur_file->entry_offset;
|
||||
|
||||
cur_entry->name_size = name_size;
|
||||
memset(cur_entry->name, 0, (cur_entry->name_size + 3) & ~3);
|
||||
memcpy(cur_entry->name, cur_file->path + cur_file->cur_path_ofs, name_size);
|
||||
|
||||
switch (cur_file->source) {
|
||||
case RomFSDataSource::BaseRomFS:
|
||||
case RomFSDataSource::FileRomFS:
|
||||
/* Try to compact, if possible. */
|
||||
if (out_infos->back().type == cur_file->source) {
|
||||
out_infos->back().size = cur_file->offset + ROMFS_FILEPARTITION_OFS + cur_file->size - out_infos->back().virtual_offset;
|
||||
} else {
|
||||
out_infos->emplace_back(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->size, cur_file->orig_offset + ROMFS_FILEPARTITION_OFS, cur_file->source);
|
||||
}
|
||||
break;
|
||||
case RomFSDataSource::LooseFile:
|
||||
{
|
||||
char *path = new char[cur_file->path_len + 1];
|
||||
strcpy(path, cur_file->path);
|
||||
out_infos->emplace_back(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->size, path, cur_file->source);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fatalSimple(0xF601);
|
||||
}
|
||||
}
|
||||
|
||||
/* Populate dir tables. */
|
||||
for (const auto &it : this->directories) {
|
||||
cur_dir = it.second;
|
||||
RomFSDirectoryEntry *cur_entry = romfs_get_direntry(dir_table, cur_dir->entry_offset);
|
||||
cur_entry->parent = cur_dir == this->root ? 0 : cur_dir->parent->entry_offset;
|
||||
cur_entry->sibling = (cur_dir->sibling == NULL) ? ROMFS_ENTRY_EMPTY : cur_dir->sibling->entry_offset;
|
||||
cur_entry->child = (cur_dir->child == NULL) ? ROMFS_ENTRY_EMPTY : cur_dir->child->entry_offset;
|
||||
cur_entry->file = (cur_dir->file == NULL) ? ROMFS_ENTRY_EMPTY : cur_dir->file->entry_offset;
|
||||
|
||||
u32 name_size = cur_dir->path_len - cur_dir->cur_path_ofs;
|
||||
u32 hash = romfs_calc_path_hash(cur_dir == this->root ? 0 : cur_dir->parent->entry_offset, (unsigned char *)cur_dir->path + cur_dir->cur_path_ofs, 0, name_size);
|
||||
cur_entry->hash = dir_hash_table[hash % dir_hash_table_entry_count];
|
||||
dir_hash_table[hash % dir_hash_table_entry_count] = cur_dir->entry_offset;
|
||||
|
||||
cur_entry->name_size = name_size;
|
||||
memset(cur_entry->name, 0, (cur_entry->name_size + 3) & ~3);
|
||||
memcpy(cur_entry->name, cur_dir->path + cur_dir->cur_path_ofs, name_size);
|
||||
}
|
||||
|
||||
/* Delete directories. */
|
||||
for (const auto &it : this->directories) {
|
||||
cur_dir = it.second;
|
||||
delete cur_dir->path;
|
||||
delete cur_dir;
|
||||
}
|
||||
this->root = NULL;
|
||||
this->directories.clear();
|
||||
|
||||
/* Delete files. */
|
||||
for (const auto &it : this->files) {
|
||||
cur_file = it.second;
|
||||
delete cur_file->path;
|
||||
delete cur_file;
|
||||
}
|
||||
this->files.clear();
|
||||
|
||||
/* Set header fields. */
|
||||
header->header_size = sizeof(*header);
|
||||
header->file_hash_table_size = this->file_hash_table_size;
|
||||
header->file_table_size = this->file_table_size;
|
||||
header->dir_hash_table_size = this->dir_hash_table_size;
|
||||
header->dir_table_size = this->dir_table_size;
|
||||
header->file_partition_ofs = ROMFS_FILEPARTITION_OFS;
|
||||
header->dir_hash_table_ofs = (header->file_partition_ofs + this->file_partition_size + 3ULL) & ~3ULL;
|
||||
header->dir_table_ofs = header->dir_hash_table_ofs + header->dir_hash_table_size;
|
||||
header->file_hash_table_ofs = header->dir_table_ofs + header->dir_table_size;
|
||||
header->file_table_ofs = header->file_hash_table_ofs + header->file_hash_table_size;
|
||||
|
||||
const size_t metadata_size = this->dir_hash_table_size + this->dir_table_size + this->file_hash_table_size + this->file_table_size;
|
||||
|
||||
/* Try to save metadata to the SD card, to save on memory space. */
|
||||
if (R_SUCCEEDED(Utils::SaveSdFileForAtmosphere(this->title_id, ROMFS_METADATA_FILE_PATH, metadata, metadata_size))) {
|
||||
out_infos->emplace_back(header->dir_hash_table_ofs, metadata_size, RomFSDataSource::MetaData);
|
||||
delete metadata;
|
||||
} else {
|
||||
out_infos->emplace_back(header->dir_hash_table_ofs, metadata_size, metadata, RomFSDataSource::Memory);
|
||||
}
|
||||
|
||||
}
|
||||
280
stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfsbuild.hpp
Normal file
280
stratosphere/ams_mitm/source/fs_mitm/fsmitm_romfsbuild.hpp
Normal file
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <map>
|
||||
|
||||
#include "fsmitm_romstorage.hpp"
|
||||
|
||||
#include "../debug.hpp"
|
||||
|
||||
#define ROMFS_ENTRY_EMPTY 0xFFFFFFFF
|
||||
#define ROMFS_FILEPARTITION_OFS 0x200
|
||||
|
||||
#define ROMFS_METADATA_FILE_PATH "romfs_metadata.bin"
|
||||
|
||||
/* Types for RomFS Meta construction. */
|
||||
enum class RomFSDataSource {
|
||||
BaseRomFS,
|
||||
FileRomFS,
|
||||
LooseFile,
|
||||
MetaData,
|
||||
Memory,
|
||||
};
|
||||
|
||||
struct RomFSBaseSourceInfo {
|
||||
u64 offset;
|
||||
};
|
||||
|
||||
struct RomFSFileSourceInfo {
|
||||
u64 offset;
|
||||
};
|
||||
|
||||
struct RomFSLooseSourceInfo {
|
||||
const char *path;
|
||||
};
|
||||
|
||||
struct RomFSMemorySourceInfo {
|
||||
const u8 *data;
|
||||
};
|
||||
|
||||
struct RomFSMetaDataSourceInfo {
|
||||
|
||||
};
|
||||
|
||||
struct RomFSSourceInfo {
|
||||
u64 virtual_offset;
|
||||
u64 size;
|
||||
union {
|
||||
RomFSBaseSourceInfo base_source_info;
|
||||
RomFSFileSourceInfo file_source_info;
|
||||
RomFSLooseSourceInfo loose_source_info;
|
||||
RomFSMemorySourceInfo memory_source_info;
|
||||
RomFSMemorySourceInfo metadata_source_info;
|
||||
};
|
||||
RomFSDataSource type;
|
||||
|
||||
RomFSSourceInfo(u64 v_o, u64 s, u64 offset, RomFSDataSource t) : virtual_offset(v_o), size(s), type(t) {
|
||||
switch (this->type) {
|
||||
case RomFSDataSource::BaseRomFS:
|
||||
this->base_source_info.offset = offset;
|
||||
break;
|
||||
case RomFSDataSource::FileRomFS:
|
||||
this->file_source_info.offset = offset;
|
||||
break;
|
||||
case RomFSDataSource::LooseFile:
|
||||
case RomFSDataSource::MetaData:
|
||||
case RomFSDataSource::Memory:
|
||||
default:
|
||||
fatalSimple(0xF601);
|
||||
}
|
||||
}
|
||||
|
||||
RomFSSourceInfo(u64 v_o, u64 s, const void *arg, RomFSDataSource t) : virtual_offset(v_o), size(s), type(t) {
|
||||
switch (this->type) {
|
||||
case RomFSDataSource::LooseFile:
|
||||
this->loose_source_info.path = (decltype(this->loose_source_info.path))arg;
|
||||
break;
|
||||
case RomFSDataSource::Memory:
|
||||
this->memory_source_info.data = (decltype(this->memory_source_info.data))arg;
|
||||
break;
|
||||
case RomFSDataSource::MetaData:
|
||||
case RomFSDataSource::BaseRomFS:
|
||||
case RomFSDataSource::FileRomFS:
|
||||
default:
|
||||
fatalSimple(0xF601);
|
||||
}
|
||||
}
|
||||
|
||||
RomFSSourceInfo(u64 v_o, u64 s, RomFSDataSource t) : virtual_offset(v_o), size(s), type(t) {
|
||||
switch (this->type) {
|
||||
case RomFSDataSource::MetaData:
|
||||
break;
|
||||
case RomFSDataSource::LooseFile:
|
||||
case RomFSDataSource::Memory:
|
||||
case RomFSDataSource::BaseRomFS:
|
||||
case RomFSDataSource::FileRomFS:
|
||||
default:
|
||||
fatalSimple(0xF601);
|
||||
}
|
||||
}
|
||||
|
||||
void Cleanup() {
|
||||
switch (this->type) {
|
||||
case RomFSDataSource::BaseRomFS:
|
||||
case RomFSDataSource::FileRomFS:
|
||||
case RomFSDataSource::MetaData:
|
||||
break;
|
||||
case RomFSDataSource::LooseFile:
|
||||
delete this->loose_source_info.path;
|
||||
break;
|
||||
case RomFSDataSource::Memory:
|
||||
delete this->memory_source_info.data;
|
||||
break;
|
||||
default:
|
||||
fatalSimple(0xF601);
|
||||
}
|
||||
}
|
||||
|
||||
static bool Compare(RomFSSourceInfo *a, RomFSSourceInfo *b) {
|
||||
return (a->virtual_offset < b->virtual_offset);
|
||||
}
|
||||
};
|
||||
|
||||
/* Types for building a RomFS. */
|
||||
struct RomFSHeader {
|
||||
u64 header_size;
|
||||
u64 dir_hash_table_ofs;
|
||||
u64 dir_hash_table_size;
|
||||
u64 dir_table_ofs;
|
||||
u64 dir_table_size;
|
||||
u64 file_hash_table_ofs;
|
||||
u64 file_hash_table_size;
|
||||
u64 file_table_ofs;
|
||||
u64 file_table_size;
|
||||
u64 file_partition_ofs;
|
||||
};
|
||||
|
||||
static_assert(sizeof(RomFSHeader) == 0x50, "Incorrect RomFS Header definition!");
|
||||
|
||||
struct RomFSDirectoryEntry {
|
||||
u32 parent;
|
||||
u32 sibling;
|
||||
u32 child;
|
||||
u32 file;
|
||||
u32 hash;
|
||||
u32 name_size;
|
||||
char name[];
|
||||
};
|
||||
|
||||
static_assert(sizeof(RomFSDirectoryEntry) == 0x18, "Incorrect RomFSDirectoryEntry definition!");
|
||||
|
||||
struct RomFSFileEntry {
|
||||
u32 parent;
|
||||
u32 sibling;
|
||||
u64 offset;
|
||||
u64 size;
|
||||
u32 hash;
|
||||
u32 name_size;
|
||||
char name[];
|
||||
};
|
||||
|
||||
static_assert(sizeof(RomFSFileEntry) == 0x20, "Incorrect RomFSFileEntry definition!");
|
||||
|
||||
struct RomFSBuildFileContext;
|
||||
|
||||
/* Used as comparator for std::map<char *, RomFSBuild*Context> */
|
||||
struct build_ctx_cmp {
|
||||
bool operator()(const char *a, const char *b) const {
|
||||
return strcmp(a, b) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct RomFSBuildDirectoryContext {
|
||||
char *path;
|
||||
u32 cur_path_ofs;
|
||||
u32 path_len;
|
||||
u32 entry_offset = 0;
|
||||
RomFSBuildDirectoryContext *parent = NULL;
|
||||
RomFSBuildDirectoryContext *child = NULL;
|
||||
RomFSBuildDirectoryContext *sibling = NULL;
|
||||
RomFSBuildFileContext *file = NULL;
|
||||
};
|
||||
|
||||
struct RomFSBuildFileContext {
|
||||
char *path;
|
||||
u32 cur_path_ofs;
|
||||
u32 path_len;
|
||||
u32 entry_offset = 0;
|
||||
u64 offset = 0;
|
||||
u64 size = 0;
|
||||
RomFSBuildDirectoryContext *parent = NULL;
|
||||
RomFSBuildFileContext *sibling = NULL;
|
||||
RomFSDataSource source{0};
|
||||
u64 orig_offset = 0;
|
||||
};
|
||||
|
||||
class RomFSBuildContext {
|
||||
private:
|
||||
u64 title_id;
|
||||
RomFSBuildDirectoryContext *root;
|
||||
std::map<char *, RomFSBuildDirectoryContext *, build_ctx_cmp> directories;
|
||||
std::map<char *, RomFSBuildFileContext *, build_ctx_cmp> files;
|
||||
u64 num_dirs = 0;
|
||||
u64 num_files = 0;
|
||||
u64 dir_table_size = 0;
|
||||
u64 file_table_size = 0;
|
||||
u64 dir_hash_table_size = 0;
|
||||
u64 file_hash_table_size = 0;
|
||||
u64 file_partition_size = 0;
|
||||
|
||||
FsDirectoryEntry dir_entry;
|
||||
RomFSDataSource cur_source_type;
|
||||
|
||||
void VisitDirectory(FsFileSystem *filesys, RomFSBuildDirectoryContext *parent);
|
||||
void VisitDirectory(RomFSBuildDirectoryContext *parent, u32 parent_offset, void *dir_table, size_t dir_table_size, void *file_table, size_t file_table_size);
|
||||
|
||||
bool AddDirectory(RomFSBuildDirectoryContext *parent_dir_ctx, RomFSBuildDirectoryContext *dir_ctx, RomFSBuildDirectoryContext **out_dir_ctx);
|
||||
bool AddFile(RomFSBuildDirectoryContext *parent_dir_ctx, RomFSBuildFileContext *file_ctx);
|
||||
public:
|
||||
RomFSBuildContext(u64 tid) : title_id(tid) {
|
||||
this->root = new RomFSBuildDirectoryContext({0});
|
||||
this->root->path = new char[1];
|
||||
this->root->path[0] = '\x00';
|
||||
this->directories.insert({this->root->path, this->root});
|
||||
this->num_dirs = 1;
|
||||
this->dir_table_size = 0x18;
|
||||
}
|
||||
|
||||
void MergeSdFiles();
|
||||
void MergeRomStorage(IROStorage *storage, RomFSDataSource source);
|
||||
|
||||
/* This finalizes the context. */
|
||||
void Build(std::vector<RomFSSourceInfo> *out_infos);
|
||||
};
|
||||
|
||||
|
||||
static inline RomFSDirectoryEntry *romfs_get_direntry(void *directories, uint32_t offset) {
|
||||
return (RomFSDirectoryEntry *)((uintptr_t)directories + offset);
|
||||
}
|
||||
|
||||
static inline RomFSFileEntry *romfs_get_fentry(void *files, uint32_t offset) {
|
||||
return (RomFSFileEntry *)((uintptr_t)files + offset);
|
||||
}
|
||||
|
||||
static inline uint32_t romfs_calc_path_hash(uint32_t parent, const unsigned char *path, uint32_t start, size_t path_len) {
|
||||
uint32_t hash = parent ^ 123456789;
|
||||
for (uint32_t i = 0; i < path_len; i++) {
|
||||
hash = (hash >> 5) | (hash << 27);
|
||||
hash ^= path[start + i];
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static inline uint32_t romfs_get_hash_table_count(uint32_t num_entries) {
|
||||
if (num_entries < 3) {
|
||||
return 3;
|
||||
} else if (num_entries < 19) {
|
||||
return num_entries | 1;
|
||||
}
|
||||
uint32_t count = num_entries;
|
||||
while (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 || count % 11 == 0 || count % 13 == 0 || count % 17 == 0) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
57
stratosphere/ams_mitm/source/fs_mitm/fsmitm_romstorage.hpp
Normal file
57
stratosphere/ams_mitm/source/fs_mitm/fsmitm_romstorage.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "fs_istorage.hpp"
|
||||
|
||||
/* Represents a RomFS stored in some file. */
|
||||
class RomFileStorage : public IROStorage {
|
||||
private:
|
||||
FsFile *base_file;
|
||||
public:
|
||||
RomFileStorage(FsFile *f) : base_file(f) {
|
||||
/* ... */
|
||||
};
|
||||
RomFileStorage(FsFile f) {
|
||||
this->base_file = new FsFile(f);
|
||||
};
|
||||
virtual ~RomFileStorage() {
|
||||
fsFileClose(base_file);
|
||||
delete base_file;
|
||||
};
|
||||
public:
|
||||
Result Read(void *buffer, size_t size, u64 offset) override {
|
||||
size_t out_sz = 0;
|
||||
Result rc = fsFileRead(this->base_file, offset, buffer, size, &out_sz);
|
||||
if (R_SUCCEEDED(rc) && out_sz != size && out_sz) {
|
||||
return this->Read((void *)((uintptr_t)buffer + out_sz), size - out_sz, offset + out_sz);
|
||||
}
|
||||
return rc;
|
||||
};
|
||||
Result GetSize(u64 *out_size) override {
|
||||
return fsFileGetSize(this->base_file, out_size);
|
||||
};
|
||||
Result OperateRange(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
|
||||
/* TODO: Merge into libnx? */
|
||||
return fsFileOperateRange(this->base_file, operation_type, offset, size, out_range_info);
|
||||
};
|
||||
};
|
||||
|
||||
/* Represents a RomFS accessed via some IStorage. */
|
||||
using RomInterfaceStorage = ROProxyStorage;
|
||||
267
stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp
Normal file
267
stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp
Normal file
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "fsmitm_service.hpp"
|
||||
#include "fs_shim.h"
|
||||
|
||||
#include "../utils.hpp"
|
||||
#include "fsmitm_boot0storage.hpp"
|
||||
#include "fsmitm_romstorage.hpp"
|
||||
#include "fsmitm_layeredrom.hpp"
|
||||
|
||||
#include "../debug.hpp"
|
||||
|
||||
static HosMutex g_StorageCacheLock;
|
||||
static std::unordered_map<u64, std::weak_ptr<IStorageInterface>> g_StorageCache;
|
||||
|
||||
static bool StorageCacheGetEntry(u64 title_id, std::shared_ptr<IStorageInterface> *out) {
|
||||
std::scoped_lock<HosMutex> lock(g_StorageCacheLock);
|
||||
if (g_StorageCache.find(title_id) == g_StorageCache.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto intf = g_StorageCache[title_id].lock();
|
||||
if (intf != nullptr) {
|
||||
*out = intf;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void StorageCacheSetEntry(u64 title_id, std::shared_ptr<IStorageInterface> *ptr) {
|
||||
std::scoped_lock<HosMutex> lock(g_StorageCacheLock);
|
||||
|
||||
/* Ensure we always use the cached copy if present. */
|
||||
if (g_StorageCache.find(title_id) != g_StorageCache.end()) {
|
||||
auto intf = g_StorageCache[title_id].lock();
|
||||
if (intf != nullptr) {
|
||||
*ptr = intf;
|
||||
}
|
||||
}
|
||||
|
||||
g_StorageCache[title_id] = *ptr;
|
||||
}
|
||||
|
||||
void FsMitmService::PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx) {
|
||||
auto this_ptr = static_cast<FsMitmService *>(obj);
|
||||
switch ((FspSrvCmd)ctx->cmd_id) {
|
||||
case FspSrvCmd_SetCurrentProcess:
|
||||
if (R_SUCCEEDED(ctx->rc)) {
|
||||
this_ptr->has_initialized = true;
|
||||
this_ptr->process_id = ctx->request.Pid;
|
||||
this_ptr->title_id = this_ptr->process_id;
|
||||
if (R_FAILED(MitmQueryUtils::GetAssociatedTidForPid(this_ptr->process_id, &this_ptr->title_id))) {
|
||||
/* Log here, if desired. */
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Gate access to the BIS partitions. */
|
||||
Result FsMitmService::OpenBisStorage(Out<std::shared_ptr<IStorageInterface>> out_storage, u32 bis_partition_id) {
|
||||
std::shared_ptr<IStorageInterface> storage = nullptr;
|
||||
u32 out_domain_id = 0;
|
||||
Result rc = 0;
|
||||
|
||||
ON_SCOPE_EXIT {
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
out_storage.SetValue(std::move(storage));
|
||||
if (out_storage.IsDomain()) {
|
||||
out_storage.ChangeObjectId(out_domain_id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
FsStorage bis_storage;
|
||||
rc = fsOpenBisStorageFwd(this->forward_service.get(), &bis_storage, bis_partition_id);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
const bool is_sysmodule = this->title_id < 0x0100000000001000;
|
||||
const bool has_bis_write_flag = Utils::HasFlag(this->title_id, "bis_write");
|
||||
const bool has_cal0_read_flag = Utils::HasFlag(this->title_id, "cal_read");
|
||||
if (bis_partition_id == BisStorageId_Boot0) {
|
||||
storage = std::make_shared<IStorageInterface>(new Boot0Storage(bis_storage, this->title_id));
|
||||
} else if (bis_partition_id == BisStorageId_Prodinfo) {
|
||||
/* PRODINFO should *never* be writable. */
|
||||
if (is_sysmodule || has_cal0_read_flag) {
|
||||
storage = std::make_shared<IStorageInterface>(new ROProxyStorage(bis_storage));
|
||||
} else {
|
||||
/* Do not allow non-sysmodules to read *or* write CAL0. */
|
||||
fsStorageClose(&bis_storage);
|
||||
return 0x320002;
|
||||
}
|
||||
} else {
|
||||
if (is_sysmodule || has_bis_write_flag) {
|
||||
/* Sysmodules should still be allowed to read and write. */
|
||||
storage = std::make_shared<IStorageInterface>(new ProxyStorage(bis_storage));
|
||||
} else {
|
||||
/* Non-sysmodules should be allowed to read. */
|
||||
storage = std::make_shared<IStorageInterface>(new ROProxyStorage(bis_storage));
|
||||
}
|
||||
}
|
||||
if (out_storage.IsDomain()) {
|
||||
out_domain_id = bis_storage.s.object_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Add redirection for RomFS to the SD card. */
|
||||
Result FsMitmService::OpenDataStorageByCurrentProcess(Out<std::shared_ptr<IStorageInterface>> out_storage) {
|
||||
std::shared_ptr<IStorageInterface> storage = nullptr;
|
||||
u32 out_domain_id = 0;
|
||||
Result rc = 0;
|
||||
|
||||
if (!this->should_override_contents) {
|
||||
return RESULT_FORWARD_TO_SESSION;
|
||||
}
|
||||
|
||||
bool has_cache = StorageCacheGetEntry(this->title_id, &storage);
|
||||
|
||||
ON_SCOPE_EXIT {
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (!has_cache) {
|
||||
StorageCacheSetEntry(this->title_id, &storage);
|
||||
}
|
||||
|
||||
out_storage.SetValue(std::move(storage));
|
||||
if (out_storage.IsDomain()) {
|
||||
out_storage.ChangeObjectId(out_domain_id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if (has_cache) {
|
||||
if (out_storage.IsDomain()) {
|
||||
FsStorage s = {0};
|
||||
rc = fsOpenDataStorageByCurrentProcessFwd(this->forward_service.get(), &s);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
out_domain_id = s.s.object_id;
|
||||
}
|
||||
} else {
|
||||
rc = 0;
|
||||
}
|
||||
if (R_FAILED(rc)) {
|
||||
storage.reset();
|
||||
}
|
||||
} else {
|
||||
FsStorage data_storage;
|
||||
FsFile data_file;
|
||||
|
||||
rc = fsOpenDataStorageByCurrentProcessFwd(this->forward_service.get(), &data_storage);
|
||||
|
||||
Log(armGetTls(), 0x100);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (Utils::HasSdRomfsContent(this->title_id)) {
|
||||
/* TODO: Is there a sensible path that ends in ".romfs" we can use?" */
|
||||
if (R_SUCCEEDED(Utils::OpenSdFileForAtmosphere(this->title_id, "romfs.bin", FS_OPEN_READ, &data_file))) {
|
||||
storage = std::make_shared<IStorageInterface>(new LayeredRomFS(std::make_shared<RomInterfaceStorage>(data_storage), std::make_shared<RomFileStorage>(data_file), this->title_id));
|
||||
} else {
|
||||
storage = std::make_shared<IStorageInterface>(new LayeredRomFS(std::make_shared<RomInterfaceStorage>(data_storage), nullptr, this->title_id));
|
||||
}
|
||||
if (out_storage.IsDomain()) {
|
||||
out_domain_id = data_storage.s.object_id;
|
||||
}
|
||||
} else {
|
||||
/* If we don't have anything to modify, there's no sense in maintaining a copy of the metadata tables. */
|
||||
fsStorageClose(&data_storage);
|
||||
rc = RESULT_FORWARD_TO_SESSION;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Add redirection for System Data Archives to the SD card. */
|
||||
Result FsMitmService::OpenDataStorageByDataId(Out<std::shared_ptr<IStorageInterface>> out_storage, u64 data_id, u8 sid) {
|
||||
FsStorageId storage_id = (FsStorageId)sid;
|
||||
FsStorage data_storage;
|
||||
FsFile data_file;
|
||||
|
||||
if (!this->should_override_contents) {
|
||||
return RESULT_FORWARD_TO_SESSION;
|
||||
}
|
||||
|
||||
std::shared_ptr<IStorageInterface> storage = nullptr;
|
||||
u32 out_domain_id = 0;
|
||||
Result rc = 0;
|
||||
|
||||
bool has_cache = StorageCacheGetEntry(data_id, &storage);
|
||||
|
||||
ON_SCOPE_EXIT {
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (!has_cache) {
|
||||
StorageCacheSetEntry(data_id, &storage);
|
||||
}
|
||||
|
||||
out_storage.SetValue(std::move(storage));
|
||||
if (out_storage.IsDomain()) {
|
||||
out_storage.ChangeObjectId(out_domain_id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (has_cache) {
|
||||
if (out_storage.IsDomain()) {
|
||||
FsStorage s = {0};
|
||||
rc = fsOpenDataStorageByDataIdFwd(this->forward_service.get(), storage_id, data_id, &s);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
out_domain_id = s.s.object_id;
|
||||
}
|
||||
} else {
|
||||
rc = 0;
|
||||
}
|
||||
if (R_FAILED(rc)) {
|
||||
storage.reset();
|
||||
}
|
||||
} else {
|
||||
rc = fsOpenDataStorageByDataIdFwd(this->forward_service.get(), storage_id, data_id, &data_storage);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (Utils::HasSdRomfsContent(data_id)) {
|
||||
/* TODO: Is there a sensible path that ends in ".romfs" we can use?" */
|
||||
if (R_SUCCEEDED(Utils::OpenSdFileForAtmosphere(data_id, "romfs.bin", FS_OPEN_READ, &data_file))) {
|
||||
storage = std::make_shared<IStorageInterface>(new LayeredRomFS(std::make_shared<RomInterfaceStorage>(data_storage), std::make_shared<RomFileStorage>(data_file), data_id));
|
||||
} else {
|
||||
storage = std::make_shared<IStorageInterface>(new LayeredRomFS(std::make_shared<RomInterfaceStorage>(data_storage), nullptr, data_id));
|
||||
}
|
||||
if (out_storage.IsDomain()) {
|
||||
out_domain_id = data_storage.s.object_id;
|
||||
}
|
||||
} else {
|
||||
/* If we don't have anything to modify, there's no sense in maintaining a copy of the metadata tables. */
|
||||
fsStorageClose(&data_storage);
|
||||
rc = RESULT_FORWARD_TO_SESSION;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
73
stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.hpp
Normal file
73
stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.hpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "fs_istorage.hpp"
|
||||
#include "../utils.hpp"
|
||||
|
||||
enum FspSrvCmd : u32 {
|
||||
FspSrvCmd_SetCurrentProcess = 1,
|
||||
FspSrvCmd_OpenBisStorage = 12,
|
||||
FspSrvCmd_OpenDataStorageByCurrentProcess = 200,
|
||||
FspSrvCmd_OpenDataStorageByDataId = 202,
|
||||
};
|
||||
|
||||
class FsMitmService : public IMitmServiceObject {
|
||||
private:
|
||||
bool has_initialized = false;
|
||||
bool should_override_contents;
|
||||
public:
|
||||
FsMitmService(std::shared_ptr<Service> s, u64 pid) : IMitmServiceObject(s, pid) {
|
||||
if (Utils::HasSdDisableMitMFlag(this->title_id)) {
|
||||
this->should_override_contents = false;
|
||||
} else {
|
||||
this->should_override_contents = (this->title_id >= 0x0100000000010000ULL || Utils::HasSdMitMFlag(this->title_id)) && Utils::HasOverrideButton(this->title_id);
|
||||
}
|
||||
}
|
||||
|
||||
static bool ShouldMitm(u64 pid, u64 tid) {
|
||||
/* Don't Mitm KIPs */
|
||||
if (pid < 0x50) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::atomic_bool has_launched_qlaunch = false;
|
||||
|
||||
/* TODO: intercepting everything seems to cause issues with sleep mode, for some reason. */
|
||||
/* Figure out why, and address it. */
|
||||
if (tid == 0x0100000000001000ULL) {
|
||||
has_launched_qlaunch = true;
|
||||
}
|
||||
|
||||
return has_launched_qlaunch || tid == 0x010000000000001FULL || tid >= 0x0100000000010000ULL || Utils::HasSdMitMFlag(tid);
|
||||
}
|
||||
|
||||
static void PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx);
|
||||
|
||||
protected:
|
||||
/* Overridden commands. */
|
||||
Result OpenBisStorage(Out<std::shared_ptr<IStorageInterface>> out, u32 bis_partition_id);
|
||||
Result OpenDataStorageByCurrentProcess(Out<std::shared_ptr<IStorageInterface>> out);
|
||||
Result OpenDataStorageByDataId(Out<std::shared_ptr<IStorageInterface>> out, u64 data_id, u8 storage_id);
|
||||
public:
|
||||
DEFINE_SERVICE_DISPATCH_TABLE {
|
||||
MakeServiceCommandMeta<FspSrvCmd_OpenBisStorage, &FsMitmService::OpenBisStorage>(),
|
||||
MakeServiceCommandMeta<FspSrvCmd_OpenDataStorageByCurrentProcess, &FsMitmService::OpenDataStorageByCurrentProcess>(),
|
||||
MakeServiceCommandMeta<FspSrvCmd_OpenDataStorageByDataId, &FsMitmService::OpenDataStorageByDataId>(),
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user