diff --git a/stratosphere/fs_mitm/source/fs_istorage.hpp b/stratosphere/fs_mitm/source/fs_istorage.hpp index 23233e27f..8bd44cdd2 100644 --- a/stratosphere/fs_mitm/source/fs_istorage.hpp +++ b/stratosphere/fs_mitm/source/fs_istorage.hpp @@ -120,7 +120,7 @@ class ProxyStorage : public IStorage { ProxyStorage(FsStorage s) { this->base_storage = new FsStorage(s); }; - ~ProxyStorage() { + virtual ~ProxyStorage() { fsStorageClose(base_storage); delete base_storage; }; @@ -155,7 +155,7 @@ class ROProxyStorage : public IROStorage { ROProxyStorage(FsStorage s) { this->base_storage = new FsStorage(s); }; - ~ROProxyStorage() { + virtual ~ROProxyStorage() { fsStorageClose(base_storage); delete base_storage; }; diff --git a/stratosphere/fs_mitm/source/fsmitm_boot0storage.hpp b/stratosphere/fs_mitm/source/fsmitm_boot0storage.hpp new file mode 100644 index 000000000..d06351175 --- /dev/null +++ b/stratosphere/fs_mitm/source/fsmitm_boot0storage.hpp @@ -0,0 +1,156 @@ +/* + * 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 . + */ + +#pragma once +#include +#include + +#include "fs_istorage.hpp" + +/* Represents a sectored storage. */ +template +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(_buffer); + this->Seek(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(_buffer); + this->Seek(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>; + private: + bool allow_writes; + private: + HosMutex *GetMutex() { + static HosMutex s_boot0_mutex; + return &s_boot0_mutex; + } + public: + Boot0Storage(FsStorage *s, bool w) : Base(s), allow_writes(w) { } + Boot0Storage(FsStorage s, bool w) : Base(s), allow_writes(w) { } + + public: + virtual Result Read(void *_buffer, size_t size, u64 offset) override { + GetMutex()->Lock(); + ON_SCOPE_EXIT { GetMutex()->Unlock(); }; + + return Base::Read(_buffer, size, offset); + } + + virtual Result Write(void *_buffer, size_t size, u64 offset) override { + GetMutex()->Lock(); + ON_SCOPE_EXIT { GetMutex()->Unlock(); }; + + if (!this->allow_writes) { + return 0x313802; + } + + return Base::Write(_buffer, size, offset); + } +}; \ No newline at end of file diff --git a/stratosphere/fs_mitm/source/fsmitm_romstorage.hpp b/stratosphere/fs_mitm/source/fsmitm_romstorage.hpp index 04d0cf8d6..1a186ec4c 100644 --- a/stratosphere/fs_mitm/source/fsmitm_romstorage.hpp +++ b/stratosphere/fs_mitm/source/fsmitm_romstorage.hpp @@ -31,7 +31,7 @@ class RomFileStorage : public IROStorage { RomFileStorage(FsFile f) { this->base_file = new FsFile(f); }; - ~RomFileStorage() { + virtual ~RomFileStorage() { fsFileClose(base_file); delete base_file; }; diff --git a/stratosphere/fs_mitm/source/fsmitm_service.cpp b/stratosphere/fs_mitm/source/fsmitm_service.cpp index 505af638b..ccf29db94 100644 --- a/stratosphere/fs_mitm/source/fsmitm_service.cpp +++ b/stratosphere/fs_mitm/source/fsmitm_service.cpp @@ -24,6 +24,7 @@ #include "fs_shim.h" #include "fsmitm_utils.hpp" +#include "fsmitm_boot0storage.hpp" #include "fsmitm_romstorage.hpp" #include "fsmitm_layeredrom.hpp" @@ -98,11 +99,16 @@ Result FsMitmService::OpenBisStorage(Out> out FsStorage bis_storage; rc = fsOpenBisStorageFwd(this->forward_service.get(), &bis_storage, bis_partition_id); if (R_SUCCEEDED(rc)) { - if (this->title_id >= 0x0100000000001000) { - storage = std::make_shared(new ROProxyStorage(bis_storage)); + const bool allow_writes = this->title_id < 0x0100000000001000; + if (bis_partition_id == BisStorageId_Boot0) { + storage = std::make_shared(new Boot0Storage(bis_storage, allow_writes)); } else { - /* Sysmodules should still be allowed to read and write. */ - storage = std::make_shared(new ProxyStorage(bis_storage)); + if (allow_writes) { + storage = std::make_shared(new ROProxyStorage(bis_storage)); + } else { + /* Sysmodules should still be allowed to read and write. */ + storage = std::make_shared(new ProxyStorage(bis_storage)); + } } if (out_storage.IsDomain()) { out_domain_id = bis_storage.s.object_id;