diff --git a/libraries/libstratosphere/include/stratosphere/fs.hpp b/libraries/libstratosphere/include/stratosphere/fs.hpp index 7e97397f9..231180ac4 100644 --- a/libraries/libstratosphere/include/stratosphere/fs.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs.hpp @@ -62,4 +62,5 @@ #include #include #include +#include #include diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_substorage.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_substorage.hpp index 41383dc2d..5335bc5d0 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_substorage.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_substorage.hpp @@ -63,6 +63,9 @@ namespace ams::fs { AMS_ABORT_UNLESS(sub->m_size >= o + sz); } + ALWAYS_INLINE ::ams::fs::IStorage *operator->() { + return this; + } public: void SetResizable(bool rsz) { m_resizable = rsz; diff --git a/libraries/libstratosphere/include/stratosphere/fs/impl/fs_hash_generator_factory_selector.hpp b/libraries/libstratosphere/include/stratosphere/fs/impl/fs_hash_generator_factory_selector.hpp new file mode 100644 index 000000000..b223e71ae --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fs/impl/fs_hash_generator_factory_selector.hpp @@ -0,0 +1,24 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::fs::impl { + + fssystem::IHash256GeneratorFactorySelector *GetNcaHashGeneratorFactorySelector(); + fssystem::IHash256GeneratorFactorySelector *GetSaveDataHashGeneratorFactorySelector(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssrv/fscreator/fssrv_storage_on_nca_creator.hpp b/libraries/libstratosphere/include/stratosphere/fssrv/fscreator/fssrv_storage_on_nca_creator.hpp index 44a979112..221d8eee1 100644 --- a/libraries/libstratosphere/include/stratosphere/fssrv/fscreator/fssrv_storage_on_nca_creator.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssrv/fscreator/fssrv_storage_on_nca_creator.hpp @@ -17,10 +17,12 @@ #include #include #include +#include namespace ams::fssystem { struct NcaCryptoConfiguration; + struct NcaCompressionConfiguration; } @@ -31,22 +33,20 @@ namespace ams::fssrv::fscreator { NON_MOVEABLE(StorageOnNcaCreator); private: MemoryResource *m_allocator; - fssystem::IBufferManager * const m_buffer_manager; const fssystem::NcaCryptoConfiguration &m_nca_crypto_cfg; - bool m_is_prod; - bool m_is_enabled_program_verification; - private: - Result VerifyNcaHeaderSign2(fssystem::NcaReader *nca_reader, fs::IStorage *storage); + const fssystem::NcaCompressionConfiguration &m_nca_compression_cfg; + fssystem::IBufferManager * const m_buffer_manager; + fssystem::IHash256GeneratorFactorySelector * const m_hash_generator_factory_selector; public: - explicit StorageOnNcaCreator(MemoryResource *mr, const fssystem::NcaCryptoConfiguration &cfg, bool prod, fssystem::IBufferManager *bm) : m_allocator(mr), m_buffer_manager(bm), m_nca_crypto_cfg(cfg), m_is_prod(prod), m_is_enabled_program_verification(true) { + explicit StorageOnNcaCreator(MemoryResource *mr, const fssystem::NcaCryptoConfiguration &cfg, const fssystem::NcaCompressionConfiguration &c_cfg, fssystem::IBufferManager *bm, fssystem::IHash256GeneratorFactorySelector *hgfs) + : m_allocator(mr), m_nca_crypto_cfg(cfg), m_nca_compression_cfg(c_cfg), m_buffer_manager(bm), m_hash_generator_factory_selector(hgfs) + { /* ... */ } - virtual Result Create(std::shared_ptr *out, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr nca_reader, s32 index, bool verify_header_sign_2) override; - virtual Result CreateWithPatch(std::shared_ptr *out, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr original_nca_reader, std::shared_ptr current_nca_reader, s32 index, bool verify_header_sign_2) override; + virtual Result Create(std::shared_ptr *out, std::shared_ptr *out_splitter, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr nca_reader, s32 index) override; + virtual Result CreateWithPatch(std::shared_ptr *out, std::shared_ptr *out_splitter, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr original_nca_reader, std::shared_ptr current_nca_reader, s32 index) override; virtual Result CreateNcaReader(std::shared_ptr *out, std::shared_ptr storage) override; - virtual Result VerifyAcid(fs::fsa::IFileSystem *fs, fssystem::NcaReader *nca_reader) override; - virtual void SetEnabledProgramVerification(bool en) override; }; } diff --git a/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_i_file_system_creator.hpp b/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_i_file_system_creator.hpp index 3130b7177..88eb794fe 100644 --- a/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_i_file_system_creator.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssrv/fssrv_i_file_system_creator.hpp @@ -35,6 +35,8 @@ namespace ams::fssystem { class NcaReader; class NcaFsHeaderReader; + class IAsynchronousAccessSplitter; + namespace save { /* TODO */ @@ -60,11 +62,9 @@ namespace ams::fssrv::fscreator { class IStorageOnNcaCreator { public: virtual ~IStorageOnNcaCreator() { /* ... */ } - virtual Result Create(std::shared_ptr *out, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr nca_reader, s32 index, bool verify_header_sign_2) = 0; - virtual Result CreateWithPatch(std::shared_ptr *out, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr original_nca_reader, std::shared_ptr current_nca_reader, s32 index, bool verify_header_sign_2) = 0; + virtual Result Create(std::shared_ptr *out, std::shared_ptr *out_splitter, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr nca_reader, s32 index) = 0; + virtual Result CreateWithPatch(std::shared_ptr *out, std::shared_ptr *out_splitter, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr original_nca_reader, std::shared_ptr current_nca_reader, s32 index) = 0; virtual Result CreateNcaReader(std::shared_ptr *out, std::shared_ptr storage) = 0; - virtual Result VerifyAcid(fs::fsa::IFileSystem *fs, fssystem::NcaReader *nca_reader) = 0; - virtual void SetEnabledProgramVerification(bool en) = 0; }; struct FileSystemCreatorInterfaces { diff --git a/libraries/libstratosphere/include/stratosphere/fssystem.hpp b/libraries/libstratosphere/include/stratosphere/fssystem.hpp index cb98f8217..37d6a87a8 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem.hpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_aes_ctr_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_aes_ctr_storage.hpp index ab23078f5..0e9379eb2 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_aes_ctr_storage.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_aes_ctr_storage.hpp @@ -20,6 +20,7 @@ namespace ams::fssystem { + template class AesCtrStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable { NON_COPYABLE(AesCtrStorage); NON_MOVEABLE(AesCtrStorage); @@ -28,13 +29,13 @@ namespace ams::fssystem { static constexpr size_t KeySize = crypto::Aes128CtrEncryptor::KeySize; static constexpr size_t IvSize = crypto::Aes128CtrEncryptor::IvSize; private: - IStorage * const m_base_storage; + BasePointer m_base_storage; char m_key[KeySize]; char m_iv[IvSize]; public: static void MakeIv(void *dst, size_t dst_size, u64 upper, s64 offset); public: - AesCtrStorage(IStorage *base, const void *key, size_t key_size, const void *iv, size_t iv_size); + AesCtrStorage(BasePointer base, const void *key, size_t key_size, const void *iv, size_t iv_size); virtual Result Read(s64 offset, void *buffer, size_t size) override; virtual Result Write(s64 offset, const void *buffer, size_t size) override; @@ -47,4 +48,7 @@ namespace ams::fssystem { virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override; }; + using AesCtrStorageByPointer = AesCtrStorage; + using AesCtrStorageBySharedPointer = AesCtrStorage>; + } diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_aes_xts_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_aes_xts_storage.hpp index 2d5a23b6c..9566378d0 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_aes_xts_storage.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_aes_xts_storage.hpp @@ -21,6 +21,7 @@ namespace ams::fssystem { + template class AesXtsStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable { NON_COPYABLE(AesXtsStorage); NON_MOVEABLE(AesXtsStorage); @@ -29,13 +30,15 @@ namespace ams::fssystem { static constexpr size_t KeySize = crypto::Aes128XtsEncryptor::KeySize; static constexpr size_t IvSize = crypto::Aes128XtsEncryptor::IvSize; private: - IStorage * const m_base_storage; + BasePointer m_base_storage; char m_key[2][KeySize]; char m_iv[IvSize]; const size_t m_block_size; os::SdkMutex m_mutex; public: - AesXtsStorage(IStorage *base, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, size_t block_size); + static void MakeAesXtsIv(void *dst, size_t dst_size, s64 offset, size_t block_size); + public: + AesXtsStorage(BasePointer base, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, size_t block_size); virtual Result Read(s64 offset, void *buffer, size_t size) override; virtual Result Write(s64 offset, const void *buffer, size_t size) override; @@ -48,4 +51,7 @@ namespace ams::fssystem { virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override; }; + using AesXtsStorageByPointer = AesXtsStorage; + using AesXtsStorageBySharedPointer = AesXtsStorage>; + } diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_alignment_matching_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_alignment_matching_storage.hpp index b36985e67..20ecfe563 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_alignment_matching_storage.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_alignment_matching_storage.hpp @@ -44,7 +44,7 @@ namespace ams::fssystem { /* ... */ } - explicit AlignmentMatchingStorage(std::shared_ptr bs) : m_shared_base_storage(bs), m_base_storage(m_shared_base_storage.get()), m_is_base_storage_size_dirty(true) { + explicit AlignmentMatchingStorage(std::shared_ptr bs) : m_shared_base_storage(std::move(bs)), m_base_storage(m_shared_base_storage.get()), m_is_base_storage_size_dirty(true) { /* ... */ } @@ -109,25 +109,29 @@ namespace ams::fssystem { } virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { - /* Succeed if zero size. */ - R_SUCCEED_IF(size == 0); + if (op_id == fs::OperationId::Invalidate) { + return m_base_storage->OperateRange(fs::OperationId::Invalidate, offset, size); + } else { + /* Succeed if zero size. */ + R_SUCCEED_IF(size == 0); - /* Get the base storage size. */ - s64 bs_size = 0; - R_TRY(this->GetSize(std::addressof(bs_size))); - R_UNLESS(fs::IStorage::CheckOffsetAndSize(offset, size), fs::ResultOutOfRange()); + /* Get the base storage size. */ + s64 bs_size = 0; + R_TRY(this->GetSize(std::addressof(bs_size))); + R_UNLESS(fs::IStorage::CheckOffsetAndSize(offset, size), fs::ResultOutOfRange()); - /* Operate on the base storage. */ - const auto valid_size = std::min(size, bs_size - offset); - const auto aligned_offset = util::AlignDown(offset, DataAlign); - const auto aligned_offset_end = util::AlignUp(offset + valid_size, DataAlign); - const auto aligned_size = aligned_offset_end - aligned_offset; + /* Operate on the base storage. */ + const auto valid_size = std::min(size, bs_size - offset); + const auto aligned_offset = util::AlignDown(offset, DataAlign); + const auto aligned_offset_end = util::AlignUp(offset + valid_size, DataAlign); + const auto aligned_size = aligned_offset_end - aligned_offset; - return m_base_storage->OperateRange(dst, dst_size, op_id, aligned_offset, aligned_size, src, src_size); + return m_base_storage->OperateRange(dst, dst_size, op_id, aligned_offset, aligned_size, src, src_size); + } } }; - template + template class AlignmentMatchingStoragePooledBuffer : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable { NON_COPYABLE(AlignmentMatchingStoragePooledBuffer); NON_MOVEABLE(AlignmentMatchingStoragePooledBuffer); @@ -136,12 +140,12 @@ namespace ams::fssystem { static_assert(util::IsPowerOfTwo(BufferAlign)); private: - fs::IStorage * const m_base_storage; + BaseStorageType m_base_storage; s64 m_base_storage_size; size_t m_data_align; bool m_is_base_storage_size_dirty; public: - explicit AlignmentMatchingStoragePooledBuffer(fs::IStorage *bs, size_t da) : m_base_storage(bs), m_data_align(da), m_is_base_storage_size_dirty(true) { + explicit AlignmentMatchingStoragePooledBuffer(BaseStorageType bs, size_t da) : m_base_storage(std::move(bs)), m_data_align(da), m_is_base_storage_size_dirty(true) { AMS_ASSERT(util::IsPowerOfTwo(da)); } @@ -206,21 +210,25 @@ namespace ams::fssystem { } virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { - /* Succeed if zero size. */ - R_SUCCEED_IF(size == 0); + if (op_id == fs::OperationId::Invalidate) { + return m_base_storage->OperateRange(fs::OperationId::Invalidate, offset, size); + } else { + /* Succeed if zero size. */ + R_SUCCEED_IF(size == 0); - /* Get the base storage size. */ - s64 bs_size = 0; - R_TRY(this->GetSize(std::addressof(bs_size))); - R_UNLESS(fs::IStorage::CheckOffsetAndSize(offset, size), fs::ResultOutOfRange()); + /* Get the base storage size. */ + s64 bs_size = 0; + R_TRY(this->GetSize(std::addressof(bs_size))); + R_UNLESS(fs::IStorage::CheckOffsetAndSize(offset, size), fs::ResultOutOfRange()); - /* Operate on the base storage. */ - const auto valid_size = std::min(size, bs_size - offset); - const auto aligned_offset = util::AlignDown(offset, m_data_align); - const auto aligned_offset_end = util::AlignUp(offset + valid_size, m_data_align); - const auto aligned_size = aligned_offset_end - aligned_offset; + /* Operate on the base storage. */ + const auto valid_size = std::min(size, bs_size - offset); + const auto aligned_offset = util::AlignDown(offset, m_data_align); + const auto aligned_offset_end = util::AlignUp(offset + valid_size, m_data_align); + const auto aligned_size = aligned_offset_end - aligned_offset; - return m_base_storage->OperateRange(dst, dst_size, op_id, aligned_offset, aligned_size, src, src_size); + return m_base_storage->OperateRange(dst, dst_size, op_id, aligned_offset, aligned_size, src, src_size); + } } }; @@ -288,21 +296,25 @@ namespace ams::fssystem { } virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { - /* Succeed if zero size. */ - R_SUCCEED_IF(size == 0); + if (op_id == fs::OperationId::Invalidate) { + return m_base_storage->OperateRange(fs::OperationId::Invalidate, offset, size); + } else { + /* Succeed if zero size. */ + R_SUCCEED_IF(size == 0); - /* Get the base storage size. */ - s64 bs_size = 0; - R_TRY(this->GetSize(std::addressof(bs_size))); - R_UNLESS(fs::IStorage::CheckOffsetAndSize(offset, size), fs::ResultOutOfRange()); + /* Get the base storage size. */ + s64 bs_size = 0; + R_TRY(this->GetSize(std::addressof(bs_size))); + R_UNLESS(fs::IStorage::CheckOffsetAndSize(offset, size), fs::ResultOutOfRange()); - /* Operate on the base storage. */ - const auto valid_size = std::min(size, bs_size - offset); - const auto aligned_offset = util::AlignDown(offset, m_data_align); - const auto aligned_offset_end = util::AlignUp(offset + valid_size, m_data_align); - const auto aligned_size = aligned_offset_end - aligned_offset; + /* Operate on the base storage. */ + const auto valid_size = std::min(size, bs_size - offset); + const auto aligned_offset = util::AlignDown(offset, m_data_align); + const auto aligned_offset_end = util::AlignUp(offset + valid_size, m_data_align); + const auto aligned_size = aligned_offset_end - aligned_offset; - return m_base_storage->OperateRange(dst, dst_size, op_id, aligned_offset, aligned_size, src, src_size); + return m_base_storage->OperateRange(dst, dst_size, op_id, aligned_offset, aligned_size, src, src_size); + } } }; diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_alignment_matching_storage_impl.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_alignment_matching_storage_impl.hpp index c2f2be31a..23db1e997 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_alignment_matching_storage_impl.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_alignment_matching_storage_impl.hpp @@ -23,6 +23,14 @@ namespace ams::fssystem { public: static Result Read(fs::IStorage *base_storage, char *work_buf, size_t work_buf_size, size_t data_alignment, size_t buffer_alignment, s64 offset, char *buffer, size_t size); static Result Write(fs::IStorage *base_storage, char *work_buf, size_t work_buf_size, size_t data_alignment, size_t buffer_alignment, s64 offset, const char *buffer, size_t size); + + static Result Read(std::shared_ptr &base_storage, char *work_buf, size_t work_buf_size, size_t data_alignment, size_t buffer_alignment, s64 offset, char *buffer, size_t size) { + return Read(base_storage.get(), work_buf, work_buf_size, data_alignment, buffer_alignment, offset, buffer, size); + } + + static Result Write(std::shared_ptr &base_storage, char *work_buf, size_t work_buf_size, size_t data_alignment, size_t buffer_alignment, s64 offset, const char *buffer, size_t size) { + return Write(base_storage.get(), work_buf, work_buf_size, data_alignment, buffer_alignment, offset, buffer, size); + } }; } diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_asynchronous_access.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_asynchronous_access.hpp new file mode 100644 index 000000000..7eba3db59 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_asynchronous_access.hpp @@ -0,0 +1,56 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::fssystem { + + class IAsynchronousAccessSplitter { + public: + static IAsynchronousAccessSplitter *GetDefaultAsynchronousAccessSplitter(); + public: + constexpr IAsynchronousAccessSplitter() = default; + constexpr virtual ~IAsynchronousAccessSplitter() { /* ... */ } + public: + Result QueryNextOffset(s64 *out, s64 start_offset, s64 end_offset, s64 access_size, s64 alignment_size); + public: + virtual Result QueryAppropriateOffset(s64 *out, s64 offset, s64 access_size, s64 alignment_size) = 0; + virtual Result QueryInvocationCount(s64 *out, s64 start_offset, s64 end_offset, s64 access_size, s64 alignment_size); + }; + + class DefaultAsynchronousAccessSplitter final : public IAsynchronousAccessSplitter { + public: + constexpr DefaultAsynchronousAccessSplitter() = default; + public: + virtual Result QueryAppropriateOffset(s64 *out, s64 offset, s64 access_size, s64 alignment_size) override { + /* Align the access. */ + *out = util::AlignDown(offset + access_size, alignment_size); + return ResultSuccess(); + } + + virtual Result QueryInvocationCount(s64 *out, s64 start_offset, s64 end_offset, s64 access_size, s64 alignment_size) override { + /* Determine aligned access count. */ + *out = util::DivideUp(end_offset - util::AlignDown(start_offset, alignment_size), access_size); + return ResultSuccess(); + } + }; + + inline IAsynchronousAccessSplitter *IAsynchronousAccessSplitter::GetDefaultAsynchronousAccessSplitter() { + static constinit DefaultAsynchronousAccessSplitter s_default_access_splitter; + return std::addressof(s_default_access_splitter); + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_compressed_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_compressed_storage.hpp new file mode 100644 index 000000000..76010163a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_compressed_storage.hpp @@ -0,0 +1,96 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::fssystem { + + class CompressedStorage : public ::ams::fs::IStorage, public ::ams::fssystem::IAsynchronousAccessSplitter, public ::ams::fs::impl::Newable { + NON_COPYABLE(CompressedStorage); + NON_MOVEABLE(CompressedStorage); + public: + static constexpr size_t NodeSize = 16_KB; + + using IAllocator = BucketTree::IAllocator; + + struct Entry { + s64 virt_offset; + s64 phys_offset; + CompressionType compression_type; + s32 phys_size; + + s64 GetPhysicalSize() const { + return this->phys_size; + } + }; + static_assert(util::is_pod::value); + static_assert(sizeof(Entry) == 0x18); + public: + static constexpr s64 QueryNodeStorageSize(s32 entry_count) { + return BucketTree::QueryNodeStorageSize(NodeSize, sizeof(Entry), entry_count); + } + + static constexpr s64 QueryEntryStorageSize(s32 entry_count) { + return BucketTree::QueryEntryStorageSize(NodeSize, sizeof(Entry), entry_count); + } + private: + /* TODO: CompressedStorageCore m_core; */ + /* TODO: CacheManager m_cache_manager; */ + public: + CompressedStorage() { /* ... */ } + virtual ~CompressedStorage() { this->Finalize(); } + + Result Initialize(MemoryResource *bktr_allocator, IBufferManager *cache_allocator, fs::SubStorage data_storage, fs::SubStorage node_storage, fs::SubStorage entry_storage, s32 bktr_entry_count, size_t block_size_max, size_t continuous_reading_size_max, GetDecompressorFunction get_decompressor, size_t cache_size_0, size_t cache_size_1, s32 max_cache_entries); + + void Finalize() { + AMS_ABORT("TODO"); + /* m_cache_manager.Finalize(); */ + /* m_core.Finalize(); */ + } + public: + virtual Result QueryAppropriateOffset(s64 *out, s64 offset, s64 access_size, s64 alignment_size) override { + AMS_ABORT("TODO"); + /* return m_core.QueryAppropriateOffsetForAsynchronousAccess(out, offset, access_size, alignment_size); */ + } + public: + virtual Result Read(s64 offset, void *buffer, size_t size) override; + virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override; + + virtual Result GetSize(s64 *out) override { + AMS_ABORT("TODO"); + /* return m_core.GetSize(out); */ + } + + virtual Result Flush() override { + return ResultSuccess(); + } + + virtual Result Write(s64 offset, const void *buffer, size_t size) override { + AMS_UNUSED(offset, buffer, size); + return fs::ResultUnsupportedOperationInCompressedStorageA(); + } + + virtual Result SetSize(s64 size) override { + AMS_UNUSED(size); + /* NOTE: Is Nintendo returning the wrong result here? */ + return fs::ResultUnsupportedOperationInIndirectStorageB(); + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_compression_common.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_compression_common.hpp new file mode 100644 index 000000000..97d5fca89 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_compression_common.hpp @@ -0,0 +1,52 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::fssystem { + + enum CompressionType { + CompressionType_None = 0, + CompressionType_1 = 1, + CompressionType_2 = 2, + CompressionType_Lz4 = 3, + CompressionType_Unknown = 4, + }; + + using DecompressorFunction = Result (*)(void *, size_t, const void *, size_t); + using GetDecompressorFunction = DecompressorFunction (*)(CompressionType); + + namespace CompressionTypeUtility { + + constexpr bool IsBlockAlignmentRequired(CompressionType type) { + return type != CompressionType_None && type != CompressionType_1; + } + + constexpr bool IsDataStorageAccessRequired(CompressionType type) { + return type != CompressionType_1; + } + + constexpr bool IsRandomAccessible(CompressionType type) { + return CompressionType_None; + } + + constexpr bool IsUnknownType(CompressionType type) { + return type >= CompressionType_Unknown; + } + + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_compression_configuration.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_compression_configuration.hpp new file mode 100644 index 000000000..ff90c47d1 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_compression_configuration.hpp @@ -0,0 +1,24 @@ +/* + * 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 . + */ +#pragma once +#include +#include + +namespace ams::fssystem { + + const ::ams::fssystem::NcaCompressionConfiguration *GetNcaCompressionConfiguration(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_i_hash_256_generator.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_i_hash_256_generator.hpp new file mode 100644 index 000000000..5518c3689 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_i_hash_256_generator.hpp @@ -0,0 +1,83 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::fssystem { + + class IHash256Generator { + public: + static constexpr size_t HashSize = 256 / BITSIZEOF(u8); + public: + constexpr IHash256Generator() = default; + virtual constexpr ~IHash256Generator() { /* ... */ } + public: + void Initialize() { + return this->DoInitialize(); + } + + void Update(const void *data, size_t size) { + /* Check pre-conditions. */ + AMS_ASSERT(data != nullptr); + + return this->DoUpdate(data, size); + } + + void GetHash(void *dst, size_t dst_size) { + /* Check pre-conditions. */ + AMS_ASSERT(dst_size == HashSize); + + return this->GetHash(dst, dst_size); + } + protected: + virtual void DoInitialize() = 0; + virtual void DoUpdate(const void *data, size_t size) = 0; + virtual void DoGetHash(void *dst, size_t dst_size) = 0; + }; + + class IHash256GeneratorFactory { + public: + constexpr IHash256GeneratorFactory() = default; + virtual constexpr ~IHash256GeneratorFactory() { /* ... */ } + + std::unique_ptr Create() { + return this->DoCreate(); + } + + void GenerateHash(void *dst, size_t dst_size, const void *src, size_t src_size) { + /* Check pre-conditions. */ + AMS_ASSERT(dst != nullptr); + AMS_ASSERT(src != nullptr); + AMS_ASSERT(dst_size == IHash256Generator::HashSize); + + return this->DoGenerateHash(dst, dst_size, src, src_size); + } + protected: + virtual std::unique_ptr DoCreate() = 0; + virtual void DoGenerateHash(void *dst, size_t dst_size, const void *src, size_t src_size) = 0; + }; + + class IHash256GeneratorFactorySelector { + public: + constexpr IHash256GeneratorFactorySelector() = default; + virtual constexpr ~IHash256GeneratorFactorySelector() { /* ... */ } + + IHash256GeneratorFactory *GetFactory() { return this->DoGetFactory(); } + protected: + virtual IHash256GeneratorFactory *DoGetFactory() = 0; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_integrity_romfs_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_integrity_romfs_storage.hpp index 4e585476a..e863776cb 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_integrity_romfs_storage.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_integrity_romfs_storage.hpp @@ -37,7 +37,7 @@ namespace ams::fssystem { IntegrityRomFsStorage() : m_mutex() { /* ... */ } virtual ~IntegrityRomFsStorage() override { this->Finalize(); } - Result Initialize(save::HierarchicalIntegrityVerificationInformation level_hash_info, Hash master_hash, save::HierarchicalIntegrityVerificationStorage::HierarchicalStorageInformation storage_info, IBufferManager *bm); + Result Initialize(save::HierarchicalIntegrityVerificationInformation level_hash_info, Hash master_hash, save::HierarchicalIntegrityVerificationStorage::HierarchicalStorageInformation storage_info, IBufferManager *bm, IHash256GeneratorFactory *hgf); void Finalize(); virtual Result Read(s64 offset, void *buffer, size_t size) override { diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_file_system_driver.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_file_system_driver.hpp index bc8d3b73d..99a308d96 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_file_system_driver.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_file_system_driver.hpp @@ -17,11 +17,15 @@ #include #include #include +#include +#include +#include #include #include namespace ams::fssystem { + class CompressedStorage; class AesCtrCounterExtendedStorage; class IndirectStorage; class SparseStorage; @@ -57,6 +61,11 @@ namespace ams::fssystem { }; static_assert(util::is_pod::value); + struct NcaCompressionConfiguration { + GetDecompressorFunction get_decompressor; + }; + static_assert(util::is_pod::value); + constexpr inline bool IsInvalidKeyTypeValue(s32 key_type) { return key_type < 0; } @@ -87,22 +96,22 @@ namespace ams::fssystem { private: NcaHeader m_header; u8 m_decryption_keys[NcaHeader::DecryptionKey_Count][NcaCryptoConfiguration::Aes128KeySize]; - std::shared_ptr m_shared_base_storage; + std::shared_ptr m_body_storage; std::unique_ptr m_header_storage; - fs::IStorage *m_body_storage; u8 m_external_decryption_key[NcaCryptoConfiguration::Aes128KeySize]; DecryptAesCtrFunction m_decrypt_aes_ctr; DecryptAesCtrFunction m_decrypt_aes_ctr_external; bool m_is_software_aes_prioritized; NcaHeader::EncryptionType m_header_encryption_type; + GetDecompressorFunction m_get_decompressor; + IHash256GeneratorFactory *m_hash_generator_factory; public: NcaReader(); ~NcaReader(); - Result Initialize(fs::IStorage *base_storage, const NcaCryptoConfiguration &crypto_cfg); - Result Initialize(std::shared_ptr base_storage, const NcaCryptoConfiguration &crypto_cfg); + Result Initialize(std::shared_ptr base_storage, const NcaCryptoConfiguration &crypto_cfg, const NcaCompressionConfiguration &compression_cfg, IHash256GeneratorFactorySelector *hgf_selector); - fs::IStorage *GetBodyStorage(); + std::shared_ptr GetSharedBodyStorage(); u32 GetMagic() const; NcaHeader::DistributionType GetDistributionType() const; NcaHeader::ContentType GetContentType() const; @@ -124,7 +133,7 @@ namespace ams::fssystem { void GetEncryptedKey(void *dst, size_t size) const; const void *GetDecryptionKey(s32 index) const; bool HasValidInternalKey() const; - bool HasInternalDecryptionKeyForAesHardwareSpeedEmulation() const; + bool HasInternalDecryptionKeyForAesHw() const; bool IsSoftwareAesPrioritized() const; void PrioritizeSoftwareAes(); bool HasExternalDecryptionKey() const; @@ -136,7 +145,11 @@ namespace ams::fssystem { NcaHeader::EncryptionType GetEncryptionType() const; Result ReadHeader(NcaFsHeader *dst, s32 index) const; - Result VerifyHeaderSign2(const void *key, size_t key_size); + GetDecompressorFunction GetDecompressor() const; + IHash256GeneratorFactory *GetHashGeneratorFactory() const; + + void GetHeaderSign2(void *dst, size_t size); + void GetHeaderSign2TargetHash(void *dst, size_t size); }; class NcaFsHeaderReader : public ::ams::fs::impl::Newable { @@ -153,8 +166,9 @@ namespace ams::fssystem { Result Initialize(const NcaReader &reader, s32 index); bool IsInitialized() const { return m_fs_index >= 0; } - NcaFsHeader &GetData() { return m_data; } - const NcaFsHeader &GetData() const { return m_data; } + // NcaFsHeader &GetData() { return m_data; } + // const NcaFsHeader &GetData() const { return m_data; } + void GetRawData(void *dst, size_t dst_size) const; NcaFsHeader::HashData &GetHashData(); @@ -170,57 +184,85 @@ namespace ams::fssystem { bool ExistsSparseLayer() const; NcaSparseInfo &GetSparseInfo(); const NcaSparseInfo &GetSparseInfo() const; + bool ExistsCompressionLayer() const; + NcaCompressionInfo &GetCompressionInfo(); + const NcaCompressionInfo &GetCompressionInfo() const; }; class NcaFileSystemDriver : public ::ams::fs::impl::Newable { NON_COPYABLE(NcaFileSystemDriver); NON_MOVEABLE(NcaFileSystemDriver); - public: - class StorageOption; - class StorageOptionWithHeaderReader; + private: + struct StorageContext { + bool open_raw_storage; + std::shared_ptr body_substorage; + std::shared_ptr current_sparse_storage; + std::shared_ptr sparse_storage_meta_storage; + std::shared_ptr original_sparse_storage; + void *external_current_sparse_storage; /* TODO: Add real type? */ + void *external_original_sparse_storage; /* TODO: Add real type? */ + std::shared_ptr aes_ctr_ex_storage_meta_storage; + std::shared_ptr aes_ctr_ex_storage_data_storage; + std::shared_ptr aes_ctr_ex_storage; + std::shared_ptr indirect_storage_meta_storage; + std::shared_ptr indirect_storage; + std::shared_ptr fs_data_storage; + std::shared_ptr compressed_storage_meta_storage; + std::shared_ptr compressed_storage; + }; + + enum AlignmentStorageRequirement { + /* TODO */ + AlignmentStorageRequirement_CacheBlockSize = 0, + AlignmentStorageRequirement_None = 1, + }; private: std::shared_ptr m_original_reader; std::shared_ptr m_reader; MemoryResource * const m_allocator; fssystem::IBufferManager * const m_buffer_manager; + fssystem::IHash256GeneratorFactorySelector * const m_hash_generator_factory_selector; public: static Result SetupFsHeaderReader(NcaFsHeaderReader *out, const NcaReader &reader, s32 fs_index); public: - NcaFileSystemDriver(std::shared_ptr reader, MemoryResource *allocator, IBufferManager *buffer_manager) : m_original_reader(), m_reader(reader), m_allocator(allocator), m_buffer_manager(buffer_manager) { + NcaFileSystemDriver(std::shared_ptr reader, MemoryResource *allocator, IBufferManager *buffer_manager, IHash256GeneratorFactorySelector *hgf_selector) : m_original_reader(), m_reader(reader), m_allocator(allocator), m_buffer_manager(buffer_manager), m_hash_generator_factory_selector(hgf_selector) { AMS_ASSERT(m_reader != nullptr); + AMS_ASSERT(m_hash_generator_factory_selector != nullptr); } - NcaFileSystemDriver(std::shared_ptr original_reader, std::shared_ptr reader, MemoryResource *allocator, IBufferManager *buffer_manager) : m_original_reader(original_reader), m_reader(reader), m_allocator(allocator), m_buffer_manager(buffer_manager) { + NcaFileSystemDriver(std::shared_ptr original_reader, std::shared_ptr reader, MemoryResource *allocator, IBufferManager *buffer_manager, IHash256GeneratorFactorySelector *hgf_selector) : m_original_reader(original_reader), m_reader(reader), m_allocator(allocator), m_buffer_manager(buffer_manager), m_hash_generator_factory_selector(hgf_selector) { AMS_ASSERT(m_reader != nullptr); + AMS_ASSERT(m_hash_generator_factory_selector != nullptr); } - Result OpenRawStorage(std::shared_ptr *out, s32 fs_index); - - Result OpenStorage(std::shared_ptr *out, NcaFsHeaderReader *out_header_reader, s32 fs_index); - Result OpenStorage(std::shared_ptr *out, StorageOption *option); - - Result OpenStorage(std::shared_ptr *out, s32 fs_index) { - NcaFsHeaderReader dummy; - return this->OpenStorage(out, std::addressof(dummy), fs_index); - } - - Result OpenDecryptableStorage(std::shared_ptr *out, StorageOption *option, bool indirect_needed); - + Result OpenStorage(std::shared_ptr *out, std::shared_ptr *out_splitter, NcaFsHeaderReader *out_header_reader, s32 fs_index); private: - class BaseStorage; + Result OpenStorageImpl(std::shared_ptr *out, NcaFsHeaderReader *out_header_reader, s32 fs_index, StorageContext *ctx); - Result CreateBaseStorage(BaseStorage *out, StorageOption *option); + Result OpenIndirectableStorageAsOriginal(std::shared_ptr *out, const NcaFsHeaderReader *header_reader, StorageContext *ctx); - Result CreateDecryptableStorage(std::unique_ptr *out, StorageOption *option, BaseStorage *base_storage); - Result CreateAesXtsStorage(std::unique_ptr *out, BaseStorage *base_storage); - Result CreateAesCtrStorage(std::unique_ptr *out, BaseStorage *base_storage); - Result CreateAesCtrExStorage(std::unique_ptr *out, StorageOption *option, BaseStorage *base_storage); + Result CreateBodySubStorage(std::shared_ptr *out, s64 offset, s64 size); - Result CreateIndirectStorage(std::unique_ptr *out, StorageOption *option, std::unique_ptr base_storage); + Result CreateAesCtrStorage(std::shared_ptr *out, std::shared_ptr base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, AlignmentStorageRequirement alignment_storage_requirement); + Result CreateAesXtsStorage(std::shared_ptr *out, std::shared_ptr base_storage, s64 offset); - Result CreateVerificationStorage(std::unique_ptr *out, std::unique_ptr base_storage, NcaFsHeaderReader *header_reader); - Result CreateSha256Storage(std::unique_ptr *out, std::unique_ptr base_storage, NcaFsHeaderReader *header_reader); - Result CreateIntegrityVerificationStorage(std::unique_ptr *out, std::unique_ptr base_storage, NcaFsHeaderReader *header_reader); + Result CreateSparseStorageMetaStorage(std::shared_ptr *out, std::shared_ptr base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, const NcaSparseInfo &sparse_info); + Result CreateSparseStorageCore(std::shared_ptr *out, std::shared_ptr base_storage, s64 base_size, std::shared_ptr meta_storage, const NcaSparseInfo &sparse_info, bool external_info); + Result CreateSparseStorage(std::shared_ptr *out, s64 *out_fs_data_offset, std::shared_ptr *out_sparse_storage, std::shared_ptr *out_meta_storage, s32 index, const NcaAesCtrUpperIv &upper_iv, const NcaSparseInfo &sparse_info); + + Result CreateAesCtrExMetaStorage(std::shared_ptr *out, std::shared_ptr base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, const NcaPatchInfo &patch_info); + Result CreateAesCtrExStorage(std::shared_ptr *out, std::shared_ptr *out_ext, std::shared_ptr base_storage, std::shared_ptr meta_storage, s64 counter_offset, const NcaAesCtrUpperIv &upper_iv, const NcaPatchInfo &patch_info); + + Result CreateIndirectStorageMetaStorage(std::shared_ptr *out, std::shared_ptr base_storage, const NcaPatchInfo &patch_info); + Result CreateIndirectStorage(std::shared_ptr *out, std::shared_ptr *out_ind, std::shared_ptr base_storage, std::shared_ptr original_data_storage, std::shared_ptr meta_storage, const NcaPatchInfo &patch_info); + + Result CreateSha256Storage(std::shared_ptr *out, std::shared_ptr base_storage, const NcaFsHeader::HashData::HierarchicalSha256Data &sha256_data); + + Result CreateIntegrityVerificationStorage(std::shared_ptr *out, std::shared_ptr base_storage, const NcaFsHeader::HashData::IntegrityMetaInfo &meta_info); + + Result CreateCompressedStorage(std::shared_ptr *out, std::shared_ptr *out_cmp, std::shared_ptr *out_meta, std::shared_ptr base_storage, const NcaCompressionInfo &compression_info); + public: + Result CreateCompressedStorage(std::shared_ptr *out, std::shared_ptr *out_cmp, std::shared_ptr *out_meta, std::shared_ptr base_storage, const NcaCompressionInfo &compression_info, GetDecompressorFunction get_decompressor, MemoryResource *allocator, IBufferManager *buffer_manager); }; } diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_file_system_driver_impl.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_file_system_driver_impl.hpp index ab1b6dfbb..b3f9186bc 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_file_system_driver_impl.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_file_system_driver_impl.hpp @@ -20,131 +20,131 @@ namespace ams::fssystem { - class NcaFileSystemDriver::StorageOption { - private: - friend class NcaFileSystemDriver; - private: - const s32 m_fs_index; - NcaFsHeaderReader * const m_header_reader; - fs::IStorage *m_data_storage; - s64 m_data_storage_size; - fs::IStorage *m_aes_ctr_ex_table_storage; - AesCtrCounterExtendedStorage *m_aes_ctr_ex_storage_raw; - fs::IStorage *m_aes_ctr_ex_storage; - IndirectStorage *m_indirect_storage; - SparseStorage *m_sparse_storage; - public: - explicit StorageOption(NcaFsHeaderReader *reader) : m_fs_index(reader->GetFsIndex()), m_header_reader(reader), m_data_storage(), m_data_storage_size(), m_aes_ctr_ex_table_storage(), m_aes_ctr_ex_storage_raw(), m_aes_ctr_ex_storage(), m_indirect_storage(), m_sparse_storage() { - AMS_ASSERT(m_header_reader != nullptr); - } - - StorageOption(NcaFsHeaderReader *reader, s32 index) : m_fs_index(index), m_header_reader(reader), m_data_storage(), m_data_storage_size(), m_aes_ctr_ex_table_storage(), m_aes_ctr_ex_storage_raw(), m_aes_ctr_ex_storage(), m_indirect_storage(), m_sparse_storage() { - AMS_ASSERT(m_header_reader != nullptr); - AMS_ASSERT(0 <= index && index < NcaHeader::FsCountMax); - } - - s32 GetFsIndex() const { return m_fs_index; } - NcaFsHeaderReader &GetHeaderReader() { return *m_header_reader; } - const NcaFsHeaderReader &GetHeaderReader() const { return *m_header_reader; } - fs::SubStorage GetDataStorage() const { return fs::SubStorage(m_data_storage, 0, m_data_storage_size); } - fs::IStorage *GetAesCtrExTableStorage() const { return m_aes_ctr_ex_table_storage; } - fs::IStorage *GetAesCtrExStorage() const { return m_aes_ctr_ex_storage; } - AesCtrCounterExtendedStorage *GetAesCtrExStorageRaw() const { return m_aes_ctr_ex_storage_raw; } - IndirectStorage *GetIndirectStorage() const { return m_indirect_storage; } - SparseStorage *GetSparseStorage() const { return m_sparse_storage; } - private: - void SetDataStorage(fs::IStorage *storage, s64 size) { - AMS_ASSERT(storage != nullptr); - AMS_ASSERT(size >= 0); - m_data_storage = storage; - m_data_storage_size = size; - } - - void SetAesCtrExTableStorage(fs::IStorage *storage) { AMS_ASSERT(storage != nullptr); m_aes_ctr_ex_table_storage = storage; } - void SetAesCtrExStorage(fs::IStorage *storage) { AMS_ASSERT(storage != nullptr); m_aes_ctr_ex_storage = storage; } - void SetAesCtrExStorageRaw(AesCtrCounterExtendedStorage *storage) { AMS_ASSERT(storage != nullptr); m_aes_ctr_ex_storage_raw = storage; } - void SetIndirectStorage(IndirectStorage *storage) { AMS_ASSERT(storage != nullptr); m_indirect_storage = storage; } - void SetSparseStorage(SparseStorage *storage) { AMS_ASSERT(storage != nullptr); m_sparse_storage = storage; } - }; - - class NcaFileSystemDriver::StorageOptionWithHeaderReader : public NcaFileSystemDriver::StorageOption { - private: - NcaFsHeaderReader m_header_reader_data; - public: - explicit StorageOptionWithHeaderReader(s32 index) : StorageOption(std::addressof(m_header_reader_data), index) { /* ... */ } - }; - - class NcaFileSystemDriver::BaseStorage { - private: - std::unique_ptr m_storage; - fs::SubStorage m_sub_storage; - s64 m_storage_offset; - NcaAesCtrUpperIv m_aes_ctr_upper_iv; - public: - BaseStorage() : m_storage(), m_sub_storage(), m_storage_offset(0) { - m_aes_ctr_upper_iv.value = 0; - } - - explicit BaseStorage(const fs::SubStorage &ss) : m_storage(), m_sub_storage(ss), m_storage_offset(0) { - m_aes_ctr_upper_iv.value = 0; - } - - template - BaseStorage(T s, s64 offset, s64 size) : m_storage(), m_sub_storage(s, offset, size), m_storage_offset(0) { - m_aes_ctr_upper_iv.value = 0; - } - - void SetStorage(std::unique_ptr &&storage) { - m_storage = std::move(storage); - } - - template - void SetStorage(T storage, s64 offset, s64 size) { - m_sub_storage = fs::SubStorage(storage, offset, size); - } - - std::unique_ptr MakeStorage() { - if (m_storage != nullptr) { - return std::move(m_storage); - } - return std::make_unique(m_sub_storage); - } - - std::unique_ptr GetStorage() { - return std::move(m_storage); - } - - Result GetSubStorage(fs::SubStorage *out, s64 offset, s64 size) { - s64 storage_size = 0; - - if (m_storage != nullptr) { - R_TRY(m_storage->GetSize(std::addressof(storage_size))); - R_UNLESS(offset + size <= storage_size, fs::ResultNcaBaseStorageOutOfRangeA()); - *out = fs::SubStorage(m_storage.get(), offset, size); - } else { - R_TRY(m_sub_storage.GetSize(std::addressof(storage_size))); - R_UNLESS(offset + size <= storage_size, fs::ResultNcaBaseStorageOutOfRangeA()); - *out = fs::SubStorage(std::addressof(m_sub_storage), offset, size); - } - - return ResultSuccess(); - } - - void SetStorageOffset(s64 offset) { - m_storage_offset = offset; - } - - s64 GetStorageOffset() const { - return m_storage_offset; - } - - void SetAesCtrUpperIv(NcaAesCtrUpperIv v) { - m_aes_ctr_upper_iv = v; - } - - const NcaAesCtrUpperIv GetAesCtrUpperIv() const { - return m_aes_ctr_upper_iv; - } - }; + // class NcaFileSystemDriver::StorageOption { + // private: + // friend class NcaFileSystemDriver; + // private: + // const s32 m_fs_index; + // NcaFsHeaderReader * const m_header_reader; + // fs::IStorage *m_data_storage; + // s64 m_data_storage_size; + // fs::IStorage *m_aes_ctr_ex_table_storage; + // AesCtrCounterExtendedStorage *m_aes_ctr_ex_storage_raw; + // fs::IStorage *m_aes_ctr_ex_storage; + // IndirectStorage *m_indirect_storage; + // SparseStorage *m_sparse_storage; + // public: + // explicit StorageOption(NcaFsHeaderReader *reader) : m_fs_index(reader->GetFsIndex()), m_header_reader(reader), m_data_storage(), m_data_storage_size(), m_aes_ctr_ex_table_storage(), m_aes_ctr_ex_storage_raw(), m_aes_ctr_ex_storage(), m_indirect_storage(), m_sparse_storage() { + // AMS_ASSERT(m_header_reader != nullptr); + // } + // + // StorageOption(NcaFsHeaderReader *reader, s32 index) : m_fs_index(index), m_header_reader(reader), m_data_storage(), m_data_storage_size(), m_aes_ctr_ex_table_storage(), m_aes_ctr_ex_storage_raw(), m_aes_ctr_ex_storage(), m_indirect_storage(), m_sparse_storage() { + // AMS_ASSERT(m_header_reader != nullptr); + // AMS_ASSERT(0 <= index && index < NcaHeader::FsCountMax); + // } + // + // s32 GetFsIndex() const { return m_fs_index; } + // NcaFsHeaderReader &GetHeaderReader() { return *m_header_reader; } + // const NcaFsHeaderReader &GetHeaderReader() const { return *m_header_reader; } + // fs::SubStorage GetDataStorage() const { return fs::SubStorage(m_data_storage, 0, m_data_storage_size); } + // fs::IStorage *GetAesCtrExTableStorage() const { return m_aes_ctr_ex_table_storage; } + // fs::IStorage *GetAesCtrExStorage() const { return m_aes_ctr_ex_storage; } + // AesCtrCounterExtendedStorage *GetAesCtrExStorageRaw() const { return m_aes_ctr_ex_storage_raw; } + // IndirectStorage *GetIndirectStorage() const { return m_indirect_storage; } + // SparseStorage *GetSparseStorage() const { return m_sparse_storage; } + // private: + // void SetDataStorage(fs::IStorage *storage, s64 size) { + // AMS_ASSERT(storage != nullptr); + // AMS_ASSERT(size >= 0); + // m_data_storage = storage; + // m_data_storage_size = size; + // } + // + // void SetAesCtrExTableStorage(fs::IStorage *storage) { AMS_ASSERT(storage != nullptr); m_aes_ctr_ex_table_storage = storage; } + // void SetAesCtrExStorage(fs::IStorage *storage) { AMS_ASSERT(storage != nullptr); m_aes_ctr_ex_storage = storage; } + // void SetAesCtrExStorageRaw(AesCtrCounterExtendedStorage *storage) { AMS_ASSERT(storage != nullptr); m_aes_ctr_ex_storage_raw = storage; } + // void SetIndirectStorage(IndirectStorage *storage) { AMS_ASSERT(storage != nullptr); m_indirect_storage = storage; } + // void SetSparseStorage(SparseStorage *storage) { AMS_ASSERT(storage != nullptr); m_sparse_storage = storage; } + // }; + // + // class NcaFileSystemDriver::StorageOptionWithHeaderReader : public NcaFileSystemDriver::StorageOption { + // private: + // NcaFsHeaderReader m_header_reader_data; + // public: + // explicit StorageOptionWithHeaderReader(s32 index) : StorageOption(std::addressof(m_header_reader_data), index) { /* ... */ } + // }; + // + // class NcaFileSystemDriver::BaseStorage { + // private: + // std::unique_ptr m_storage; + // fs::SubStorage m_sub_storage; + // s64 m_storage_offset; + // NcaAesCtrUpperIv m_aes_ctr_upper_iv; + // public: + // BaseStorage() : m_storage(), m_sub_storage(), m_storage_offset(0) { + // m_aes_ctr_upper_iv.value = 0; + // } + // + // explicit BaseStorage(const fs::SubStorage &ss) : m_storage(), m_sub_storage(ss), m_storage_offset(0) { + // m_aes_ctr_upper_iv.value = 0; + // } + // + // template + // BaseStorage(T s, s64 offset, s64 size) : m_storage(), m_sub_storage(s, offset, size), m_storage_offset(0) { + // m_aes_ctr_upper_iv.value = 0; + // } + // + // void SetStorage(std::unique_ptr &&storage) { + // m_storage = std::move(storage); + // } + // + // template + // void SetStorage(T storage, s64 offset, s64 size) { + // m_sub_storage = fs::SubStorage(storage, offset, size); + // } + // + // std::unique_ptr MakeStorage() { + // if (m_storage != nullptr) { + // return std::move(m_storage); + // } + // return std::make_unique(m_sub_storage); + // } + // + // std::unique_ptr GetStorage() { + // return std::move(m_storage); + // } + // + // Result GetSubStorage(fs::SubStorage *out, s64 offset, s64 size) { + // s64 storage_size = 0; + // + // if (m_storage != nullptr) { + // R_TRY(m_storage->GetSize(std::addressof(storage_size))); + // R_UNLESS(offset + size <= storage_size, fs::ResultNcaBaseStorageOutOfRangeA()); + // *out = fs::SubStorage(m_storage.get(), offset, size); + // } else { + // R_TRY(m_sub_storage.GetSize(std::addressof(storage_size))); + // R_UNLESS(offset + size <= storage_size, fs::ResultNcaBaseStorageOutOfRangeA()); + // *out = fs::SubStorage(std::addressof(m_sub_storage), offset, size); + // } + // + // return ResultSuccess(); + // } + // + // void SetStorageOffset(s64 offset) { + // m_storage_offset = offset; + // } + // + // s64 GetStorageOffset() const { + // return m_storage_offset; + // } + // + // void SetAesCtrUpperIv(NcaAesCtrUpperIv v) { + // m_aes_ctr_upper_iv = v; + // } + // + // const NcaAesCtrUpperIv GetAesCtrUpperIv() const { + // return m_aes_ctr_upper_iv; + // } + // }; } diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_header.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_header.hpp index cdecb7851..85fd2ed3a 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_header.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_nca_header.hpp @@ -181,6 +181,11 @@ namespace ams::fssystem { }; static_assert(util::is_pod::value); + struct NcaCompressionInfo { + NcaBucketInfo bucket; + }; + static_assert(util::is_pod::value); + struct NcaFsHeader { static constexpr size_t Size = 0x200; static constexpr size_t HashDataOffset = 0x8; @@ -263,7 +268,8 @@ namespace ams::fssystem { NcaPatchInfo patch_info; NcaAesCtrUpperIv aes_ctr_upper_iv; NcaSparseInfo sparse_info; - u8 pad[0x88]; + NcaCompressionInfo compression_info; + u8 pad[0x68]; }; static_assert(sizeof(NcaFsHeader) == NcaFsHeader::Size); static_assert(util::is_pod::value); diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_hierarchical_integrity_verification_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_hierarchical_integrity_verification_storage.hpp index 76d4d51f2..29d9ffd06 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_hierarchical_integrity_verification_storage.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_hierarchical_integrity_verification_storage.hpp @@ -157,7 +157,7 @@ namespace ams::fssystem::save { HierarchicalIntegrityVerificationStorage() : m_buffers(nullptr), m_mutex(nullptr), m_data_size(-1), m_is_written_for_rollback(false) { /* ... */ } virtual ~HierarchicalIntegrityVerificationStorage() override { this->Finalize(); } - Result Initialize(const HierarchicalIntegrityVerificationInformation &info, HierarchicalStorageInformation storage, FileSystemBufferManagerSet *bufs, os::SdkRecursiveMutex *mtx, fs::StorageType storage_type); + Result Initialize(const HierarchicalIntegrityVerificationInformation &info, HierarchicalStorageInformation storage, FileSystemBufferManagerSet *bufs, IHash256GeneratorFactory *hgf, os::SdkRecursiveMutex *mtx, fs::StorageType storage_type); void Finalize(); virtual Result Read(s64 offset, void *buffer, size_t size) override; diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_integrity_verification_storage.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_integrity_verification_storage.hpp index 0905cee51..758e6ef8d 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_integrity_verification_storage.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem/save/fssystem_integrity_verification_storage.hpp @@ -47,11 +47,12 @@ namespace ams::fssystem::save { fs::HashSalt m_salt; bool m_is_real_data; fs::StorageType m_storage_type; + fssystem::IHash256GeneratorFactory *m_hash_generator_factory; public: IntegrityVerificationStorage() : m_verification_block_size(0), m_verification_block_order(0), m_upper_layer_verification_block_size(0), m_upper_layer_verification_block_order(0), m_buffer_manager(nullptr) { /* ... */ } virtual ~IntegrityVerificationStorage() override { this->Finalize(); } - Result Initialize(fs::SubStorage hs, fs::SubStorage ds, s64 verif_block_size, s64 upper_layer_verif_block_size, IBufferManager *bm, const fs::HashSalt &salt, bool is_real_data, fs::StorageType storage_type); + Result Initialize(fs::SubStorage hs, fs::SubStorage ds, s64 verif_block_size, s64 upper_layer_verif_block_size, IBufferManager *bm, fssystem::IHash256GeneratorFactory *hgf, const fs::HashSalt &salt, bool is_real_data, fs::StorageType storage_type); void Finalize(); virtual Result Read(s64 offset, void *buffer, size_t size) override; @@ -65,7 +66,10 @@ namespace ams::fssystem::save { virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override; using IStorage::OperateRange; - void CalcBlockHash(BlockHash *out, const void *buffer, size_t block_size) const; + void CalcBlockHash(BlockHash *out, const void *buffer, size_t block_size) const { + auto generator = m_hash_generator_factory->Create(); + return this->CalcBlockHash(out, buffer, block_size, generator); + } s64 GetBlockSize() const { return m_verification_block_size; @@ -73,9 +77,11 @@ namespace ams::fssystem::save { private: Result ReadBlockSignature(void *dst, size_t dst_size, s64 offset, size_t size); Result WriteBlockSignature(const void *src, size_t src_size, s64 offset, size_t size); - Result VerifyHash(const void *buf, BlockHash *hash); + Result VerifyHash(const void *buf, BlockHash *hash, std::unique_ptr &generator); - void CalcBlockHash(BlockHash *out, const void *buffer) const { + void CalcBlockHash(BlockHash *out, const void *buffer, size_t block_size, std::unique_ptr &generator) const; + + void CalcBlockHash(BlockHash *out, const void *buffer, std::unique_ptr &generator) const { return this->CalcBlockHash(out, buffer, static_cast(m_verification_block_size)); } diff --git a/libraries/libstratosphere/source/fssrv/fscreator/fssrv_storage_on_nca_creator.cpp b/libraries/libstratosphere/source/fssrv/fscreator/fssrv_storage_on_nca_creator.cpp index c50551681..dd421975a 100644 --- a/libraries/libstratosphere/source/fssrv/fscreator/fssrv_storage_on_nca_creator.cpp +++ b/libraries/libstratosphere/source/fssrv/fscreator/fssrv_storage_on_nca_creator.cpp @@ -17,124 +17,33 @@ namespace ams::fssrv::fscreator { - Result StorageOnNcaCreator::VerifyAcid(fs::fsa::IFileSystem *fs, fssystem::NcaReader *nca_reader) { - /* Open the npdm. */ - constexpr const char MetaFilePath[] = "/main.npdm"; - std::unique_ptr file; - R_TRY(fs->OpenFile(std::addressof(file), MetaFilePath, fs::OpenMode_Read)); - - size_t size; - - /* Read the Acid signature key generation. */ - constexpr s64 AcidSignatureKeyGenerationOffset = AMS_OFFSETOF(ldr::Npdm, signature_key_generation); - u32 acid_signature_key_generation; - R_TRY(file->Read(std::addressof(size), AcidSignatureKeyGenerationOffset, std::addressof(acid_signature_key_generation), sizeof(acid_signature_key_generation), fs::ReadOption())); - R_UNLESS(size == sizeof(acid_signature_key_generation), fs::ResultInvalidAcidFileSize()); - - /* Read the Acid offset. */ - constexpr s64 AcidOffsetOffset = AMS_OFFSETOF(ldr::Npdm, acid_offset); - s32 acid_offset; - R_TRY(file->Read(std::addressof(size), AcidOffsetOffset, std::addressof(acid_offset), sizeof(acid_offset), fs::ReadOption())); - R_UNLESS(size == sizeof(acid_offset), fs::ResultInvalidAcidFileSize()); - - /* Read the Acid size. */ - constexpr s64 AcidSizeOffset = AMS_OFFSETOF(ldr::Npdm, acid_size); - s32 acid_size; - R_TRY(file->Read(std::addressof(size), AcidSizeOffset, std::addressof(acid_size), sizeof(acid_size), fs::ReadOption())); - R_UNLESS(size == sizeof(acid_size), fs::ResultInvalidAcidFileSize()); - - /* Allocate memory for the acid. */ - u8 *acid = static_cast(m_allocator->Allocate(acid_size)); - R_UNLESS(acid != nullptr, fs::ResultAllocationFailureInStorageOnNcaCreatorA()); - ON_SCOPE_EXIT { m_allocator->Deallocate(acid, acid_size); }; - - /* Read the acid. */ - R_TRY(file->Read(std::addressof(size), acid_offset, acid, acid_size, fs::ReadOption())); - R_UNLESS(size == static_cast(acid_size), fs::ResultInvalidAcidSize()); - - /* Define interesting extents. */ - constexpr s32 AcidSignOffset = 0x000; - constexpr s32 AcidSignSize = 0x100; - constexpr s32 HeaderSign2KeyOffset = 0x100; - constexpr s32 HeaderSign2KeySize = 0x100; - constexpr s32 AcidSignTargetOffset = 0x100; - constexpr s32 AcidSignTargetSizeOffset = 0x204; - - /* Read the sign target size. */ - R_UNLESS(acid_size >= static_cast(AcidSignTargetSizeOffset + sizeof(s32)), fs::ResultInvalidAcidSize()); - const s32 acid_sign_target_size = *reinterpret_cast(acid + AcidSignTargetSizeOffset); - - /* Validate the sign target size. */ - R_UNLESS(acid_size >= static_cast(acid_sign_target_size + sizeof(s32)), fs::ResultInvalidAcidSize()); - R_UNLESS(acid_size >= AcidSignTargetOffset + acid_sign_target_size, fs::ResultInvalidAcidSize()); - - /* Verify the signature. */ - { - const u8 *sig = acid + AcidSignOffset; - const size_t sig_size = static_cast(AcidSignSize); - const u8 *mod = fssystem::GetAcidSignatureKeyModulus(m_is_prod, acid_signature_key_generation); - const size_t mod_size = fssystem::AcidSignatureKeyModulusSize; - const u8 *exp = fssystem::GetAcidSignatureKeyPublicExponent(); - const size_t exp_size = fssystem::AcidSignatureKeyPublicExponentSize; - const u8 *msg = acid + AcidSignTargetOffset; - const size_t msg_size = acid_sign_target_size; - const bool is_signature_valid = crypto::VerifyRsa2048PssSha256(sig, sig_size, mod, mod_size, exp, exp_size, msg, msg_size); - if (!is_signature_valid) { - /* If the signature is invalid, then unless program verification is disabled error out. */ - R_UNLESS(!m_is_enabled_program_verification, fs::ResultAcidVerificationFailed()); - - /* If program verification is disabled, then we're fine. */ - return ResultSuccess(); - } - } - - /* If we have an nca reader, verify the header signature using the key from the acid. */ - if (nca_reader) { - /* Verify that the acid contains a key to validate the second signature with. */ - R_UNLESS(acid_size >= HeaderSign2KeyOffset + HeaderSign2KeySize, fs::ResultInvalidAcidSize()); - - /* Validate that this key has its top byte set (and is thus approximately 2048 bits). */ - R_UNLESS(*(acid + HeaderSign2KeyOffset + HeaderSign2KeySize - 1) != 0x00, fs::ResultInvalidAcid()); - - R_TRY(nca_reader->VerifyHeaderSign2(reinterpret_cast(acid) + HeaderSign2KeyOffset, HeaderSign2KeySize)); - } - - return ResultSuccess(); - } - - Result StorageOnNcaCreator::Create(std::shared_ptr *out, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr nca_reader, s32 index, bool verify_header_sign_2) { + Result StorageOnNcaCreator::Create(std::shared_ptr *out, std::shared_ptr *out_splitter, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr nca_reader, s32 index) { /* Create a fs driver. */ - fssystem::NcaFileSystemDriver nca_fs_driver(nca_reader, m_allocator, m_buffer_manager); + fssystem::NcaFileSystemDriver nca_fs_driver(nca_reader, m_allocator, m_buffer_manager, m_hash_generator_factory_selector); /* Open the storage. */ std::shared_ptr storage; - R_TRY(nca_fs_driver.OpenStorage(std::addressof(storage), out_header_reader, index)); - - /* If we should, verify the header signature. */ - if (verify_header_sign_2) { - R_TRY(this->VerifyNcaHeaderSign2(nca_reader.get(), storage.get())); - } + std::shared_ptr splitter; + R_TRY(nca_fs_driver.OpenStorage(std::addressof(storage), std::addressof(splitter), out_header_reader, index)); /* Set the out storage. */ *out = std::move(storage); + *out_splitter = std::move(splitter); return ResultSuccess(); } - Result StorageOnNcaCreator::CreateWithPatch(std::shared_ptr *out, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr original_nca_reader, std::shared_ptr current_nca_reader, s32 index, bool verify_header_sign_2) { + Result StorageOnNcaCreator::CreateWithPatch(std::shared_ptr *out, std::shared_ptr *out_splitter, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr original_nca_reader, std::shared_ptr current_nca_reader, s32 index) { /* Create a fs driver. */ - fssystem::NcaFileSystemDriver nca_fs_driver(original_nca_reader, current_nca_reader, m_allocator, m_buffer_manager); + fssystem::NcaFileSystemDriver nca_fs_driver(original_nca_reader, current_nca_reader, m_allocator, m_buffer_manager, m_hash_generator_factory_selector); /* Open the storage. */ std::shared_ptr storage; - R_TRY(nca_fs_driver.OpenStorage(std::addressof(storage), out_header_reader, index)); - - /* If we should, verify the header signature. */ - if (verify_header_sign_2) { - R_TRY(this->VerifyNcaHeaderSign2(current_nca_reader.get(), storage.get())); - } + std::shared_ptr splitter; + R_TRY(nca_fs_driver.OpenStorage(std::addressof(storage), std::addressof(splitter), out_header_reader, index)); /* Set the out storage. */ *out = std::move(storage); + *out_splitter = std::move(splitter); return ResultSuccess(); } @@ -144,23 +53,11 @@ namespace ams::fssrv::fscreator { R_UNLESS(reader != nullptr, fs::ResultAllocationFailureInStorageOnNcaCreatorB()); /* Initialize the reader. */ - R_TRY(reader->Initialize(std::move(storage), m_nca_crypto_cfg)); + R_TRY(reader->Initialize(std::move(storage), m_nca_crypto_cfg, m_nca_compression_cfg, m_hash_generator_factory_selector)); /* Set the output. */ *out = std::move(reader); return ResultSuccess(); } - void StorageOnNcaCreator::SetEnabledProgramVerification(bool en) { - if (!m_is_prod) { - m_is_enabled_program_verification = en; - } - } - - Result StorageOnNcaCreator::VerifyNcaHeaderSign2(fssystem::NcaReader *nca_reader, fs::IStorage *storage) { - fssystem::PartitionFileSystem part_fs; - R_TRY(part_fs.Initialize(storage)); - return this->VerifyAcid(std::addressof(part_fs), nca_reader); - } - } diff --git a/libraries/libstratosphere/source/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp b/libraries/libstratosphere/source/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp index 880d7af55..b595a4e66 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp @@ -174,7 +174,7 @@ namespace ams::fssystem { NcaAesCtrUpperIv upper_iv = { .part = { .generation = static_cast(cur_entry.generation), .secure_value = m_secure_value } }; u8 iv[IvSize]; - AesCtrStorage::MakeIv(iv, IvSize, upper_iv.value, counter_offset); + AesCtrStorageByPointer::MakeIv(iv, IvSize, upper_iv.value, counter_offset); /* Decrypt. */ m_decryptor->Decrypt(cur_data, cur_size, m_key, KeySize, iv, IvSize); diff --git a/libraries/libstratosphere/source/fssystem/fssystem_aes_ctr_storage.cpp b/libraries/libstratosphere/source/fssystem/fssystem_aes_ctr_storage.cpp index 3f554969b..a2fdddbca 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_aes_ctr_storage.cpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_aes_ctr_storage.cpp @@ -17,8 +17,8 @@ namespace ams::fssystem { - void AesCtrStorage::MakeIv(void *dst, size_t dst_size, u64 upper, s64 offset) { - /* TODO: util::BytePtr? */ + template + void AesCtrStorage::MakeIv(void *dst, size_t dst_size, u64 upper, s64 offset) { AMS_ASSERT(dst != nullptr); AMS_ASSERT(dst_size == IvSize); AMS_ASSERT(offset >= 0); @@ -30,7 +30,8 @@ namespace ams::fssystem { util::StoreBigEndian(reinterpret_cast(out_addr + sizeof(u64)), static_cast(offset / BlockSize)); } - AesCtrStorage::AesCtrStorage(IStorage *base, const void *key, size_t key_size, const void *iv, size_t iv_size) : m_base_storage(base) { + template + AesCtrStorage::AesCtrStorage(BasePointer base, const void *key, size_t key_size, const void *iv, size_t iv_size) : m_base_storage(std::move(base)) { AMS_ASSERT(base != nullptr); AMS_ASSERT(key != nullptr); AMS_ASSERT(iv != nullptr); @@ -42,7 +43,8 @@ namespace ams::fssystem { std::memcpy(m_iv, iv, IvSize); } - Result AesCtrStorage::Read(s64 offset, void *buffer, size_t size) { + template + Result AesCtrStorage::Read(s64 offset, void *buffer, size_t size) { /* Allow zero-size reads. */ R_SUCCEED_IF(size == 0); @@ -71,7 +73,8 @@ namespace ams::fssystem { return ResultSuccess(); } - Result AesCtrStorage::Write(s64 offset, const void *buffer, size_t size) { + template + Result AesCtrStorage::Write(s64 offset, const void *buffer, size_t size) { /* Allow zero-size writes. */ R_SUCCEED_IF(size == 0); @@ -124,20 +127,24 @@ namespace ams::fssystem { return ResultSuccess(); } - Result AesCtrStorage::Flush() { + template + Result AesCtrStorage::Flush() { return m_base_storage->Flush(); } - Result AesCtrStorage::SetSize(s64 size) { + template + Result AesCtrStorage::SetSize(s64 size) { AMS_UNUSED(size); return fs::ResultUnsupportedOperationInAesCtrStorageA(); } - Result AesCtrStorage::GetSize(s64 *out) { + template + Result AesCtrStorage::GetSize(s64 *out) { return m_base_storage->GetSize(out); } - Result AesCtrStorage::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) { + template + Result AesCtrStorage::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) { /* Handle the zero size case. */ if (size == 0) { if (op_id == fs::OperationId::QueryRange) { @@ -179,4 +186,7 @@ namespace ams::fssystem { return ResultSuccess(); } + template class AesCtrStorage; + template class AesCtrStorage>; + } diff --git a/libraries/libstratosphere/source/fssystem/fssystem_aes_xts_storage.cpp b/libraries/libstratosphere/source/fssystem/fssystem_aes_xts_storage.cpp index 2b82edb85..962d31385 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_aes_xts_storage.cpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_aes_xts_storage.cpp @@ -17,7 +17,20 @@ namespace ams::fssystem { - AesXtsStorage::AesXtsStorage(IStorage *base, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, size_t block_size) : m_base_storage(base), m_block_size(block_size), m_mutex() { + template + void AesXtsStorage::MakeAesXtsIv(void *dst, size_t dst_size, s64 offset, size_t block_size) { + AMS_ASSERT(dst != nullptr); + AMS_ASSERT(dst_size == IvSize); + AMS_ASSERT(offset >= 0); + AMS_UNUSED(dst_size); + + const uintptr_t out_addr = reinterpret_cast(dst); + + util::StoreBigEndian(reinterpret_cast(out_addr + sizeof(s64)), offset / block_size); + } + + template + AesXtsStorage::AesXtsStorage(BasePointer base, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, size_t block_size) : m_base_storage(std::move(base)), m_block_size(block_size), m_mutex() { AMS_ASSERT(base != nullptr); AMS_ASSERT(key1 != nullptr); AMS_ASSERT(key2 != nullptr); @@ -32,7 +45,8 @@ namespace ams::fssystem { std::memcpy(m_iv, iv, IvSize); } - Result AesXtsStorage::Read(s64 offset, void *buffer, size_t size) { + template + Result AesXtsStorage::Read(s64 offset, void *buffer, size_t size) { /* Allow zero-size reads. */ R_SUCCEED_IF(size == 0); @@ -97,7 +111,8 @@ namespace ams::fssystem { return ResultSuccess(); } - Result AesXtsStorage::Write(s64 offset, const void *buffer, size_t size) { + template + Result AesXtsStorage::Write(s64 offset, const void *buffer, size_t size) { /* Allow zero-size writes. */ R_SUCCEED_IF(size == 0); @@ -194,21 +209,25 @@ namespace ams::fssystem { return ResultSuccess(); } - Result AesXtsStorage::Flush() { + template + Result AesXtsStorage::Flush() { return m_base_storage->Flush(); } - Result AesXtsStorage::SetSize(s64 size) { + template + Result AesXtsStorage::SetSize(s64 size) { R_UNLESS(util::IsAligned(size, AesBlockSize), fs::ResultUnexpectedInAesXtsStorageA()); return m_base_storage->SetSize(size); } - Result AesXtsStorage::GetSize(s64 *out) { + template + Result AesXtsStorage::GetSize(s64 *out) { return m_base_storage->GetSize(out); } - Result AesXtsStorage::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) { + template + Result AesXtsStorage::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) { /* Handle the zero size case. */ R_SUCCEED_IF(size == 0); @@ -219,4 +238,7 @@ namespace ams::fssystem { return m_base_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size); } + template class AesXtsStorage; + template class AesXtsStorage>; + } diff --git a/libraries/libstratosphere/source/fssystem/fssystem_file_system_proxy_api.cpp b/libraries/libstratosphere/source/fssystem/fssystem_file_system_proxy_api.cpp index 92517982a..cca546939 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_file_system_proxy_api.cpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_file_system_proxy_api.cpp @@ -143,7 +143,7 @@ namespace ams::fssystem { /* TODO FS-REIMPL: Revise for accuracy. */ util::ConstructAt(g_rom_fs_creator, GetPointer(g_allocator)); util::ConstructAt(g_partition_fs_creator); - util::ConstructAt(g_storage_on_nca_creator, GetPointer(g_allocator), *GetNcaCryptoConfiguration(is_prod), is_prod, GetPointer(g_buffer_manager)); + util::ConstructAt(g_storage_on_nca_creator, GetPointer(g_allocator), *GetNcaCryptoConfiguration(is_prod), *GetNcaCompressionConfiguration(), GetPointer(g_buffer_manager), fs::impl::GetNcaHashGeneratorFactorySelector()); /* TODO FS-REIMPL: Initialize other creators. */ diff --git a/libraries/libstratosphere/source/fssystem/fssystem_hierarchical_sha256_storage.cpp b/libraries/libstratosphere/source/fssystem/fssystem_hierarchical_sha256_storage.cpp index 625372c6a..d74fd031a 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_hierarchical_sha256_storage.cpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_hierarchical_sha256_storage.cpp @@ -33,16 +33,19 @@ namespace ams::fssystem { } - Result HierarchicalSha256Storage::Initialize(IStorage **base_storages, s32 layer_count, size_t htbs, void *hash_buf, size_t hash_buf_size) { + template + Result HierarchicalSha256Storage::Initialize(BaseStorageType *base_storages, s32 layer_count, size_t htbs, void *hash_buf, size_t hash_buf_size, fssystem::IHash256GeneratorFactory *hgf) { /* Validate preconditions. */ AMS_ASSERT(layer_count == LayerCount); AMS_ASSERT(util::IsPowerOfTwo(htbs)); AMS_ASSERT(hash_buf != nullptr); + AMS_ASSERT(hgf != nullptr); AMS_UNUSED(layer_count); /* Set size tracking members. */ m_hash_target_block_size = htbs; m_log_size_ratio = Log2(m_hash_target_block_size / HashSize); + m_hash_generator_factory = hgf; /* Get the base storage size. */ R_TRY(base_storages[2]->GetSize(std::addressof(m_base_storage_size))); @@ -72,13 +75,14 @@ namespace ams::fssystem { /* Calculate and verify the master hash. */ u8 calc_hash[HashSize]; - crypto::GenerateSha256Hash(calc_hash, sizeof(calc_hash), m_hash_buffer, static_cast(hash_storage_size)); + m_hash_generator_factory->GenerateHash(calc_hash, sizeof(calc_hash), m_hash_buffer, static_cast(hash_storage_size)); R_UNLESS(crypto::IsSameBytes(master_hash, calc_hash, HashSize), fs::ResultHierarchicalSha256HashVerificationFailed()); return ResultSuccess(); } - Result HierarchicalSha256Storage::Read(s64 offset, void *buffer, size_t size) { + template + Result HierarchicalSha256Storage::Read(s64 offset, void *buffer, size_t size) { /* Succeed if zero-size. */ R_SUCCEED_IF(size == 0); @@ -103,7 +107,7 @@ namespace ams::fssystem { /* Generate the hash of the region we're validating. */ u8 hash[HashSize]; const auto cur_size = static_cast(std::min(m_hash_target_block_size, remaining_size)); - crypto::GenerateSha256Hash(hash, sizeof(hash), static_cast(buffer) + (cur_offset - offset), cur_size); + m_hash_generator_factory->GenerateHash(hash, sizeof(hash), static_cast(buffer) + (cur_offset - offset), cur_size); AMS_ASSERT(static_cast(cur_offset >> m_log_size_ratio) < m_hash_buffer_size); @@ -125,7 +129,8 @@ namespace ams::fssystem { return ResultSuccess(); } - Result HierarchicalSha256Storage::Write(s64 offset, const void *buffer, size_t size) { + template + Result HierarchicalSha256Storage::Write(s64 offset, const void *buffer, size_t size) { /* Succeed if zero-size. */ R_SUCCEED_IF(size == 0); @@ -147,7 +152,7 @@ namespace ams::fssystem { { /* Temporarily increase our thread priority. */ ScopedThreadPriorityChanger cp(+1, ScopedThreadPriorityChanger::Mode::Relative); - crypto::GenerateSha256Hash(hash, sizeof(hash), static_cast(buffer) + (cur_offset - offset), cur_size); + m_hash_generator_factory->GenerateHash(hash, sizeof(hash), static_cast(buffer) + (cur_offset - offset), cur_size); } /* Write the data. */ @@ -167,19 +172,26 @@ namespace ams::fssystem { return ResultSuccess(); } - Result HierarchicalSha256Storage::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) { - /* Succeed if zero-size. */ - R_SUCCEED_IF(size == 0); + template + Result HierarchicalSha256Storage::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) { + if (op_id == fs::OperationId::Invalidate) { + return m_base_storage->OperateRange(fs::OperationId::Invalidate, offset, size); + } else { + /* Succeed if zero-size. */ + R_SUCCEED_IF(size == 0); - /* Validate preconditions. */ - R_UNLESS(util::IsAligned(offset, m_hash_target_block_size), fs::ResultInvalidArgument()); - R_UNLESS(util::IsAligned(size, m_hash_target_block_size), fs::ResultInvalidArgument()); + /* Validate preconditions. */ + R_UNLESS(util::IsAligned(offset, m_hash_target_block_size), fs::ResultInvalidArgument()); + R_UNLESS(util::IsAligned(size, m_hash_target_block_size), fs::ResultInvalidArgument()); - /* Determine size to use. */ - const auto reduced_size = std::min(m_base_storage_size, util::AlignUp(offset + size, m_hash_target_block_size)) - offset; + /* Determine size to use. */ + const auto reduced_size = std::min(m_base_storage_size, util::AlignUp(offset + size, m_hash_target_block_size)) - offset; - /* Operate on the base storage. */ - return m_base_storage->OperateRange(dst, dst_size, op_id, offset, reduced_size, src, src_size); + /* Operate on the base storage. */ + return m_base_storage->OperateRange(dst, dst_size, op_id, offset, reduced_size, src, src_size); + } } + template class HierarchicalSha256Storage; + } diff --git a/libraries/libstratosphere/source/fssystem/fssystem_hierarchical_sha256_storage.hpp b/libraries/libstratosphere/source/fssystem/fssystem_hierarchical_sha256_storage.hpp index 43ab21de3..a392e0746 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_hierarchical_sha256_storage.hpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_hierarchical_sha256_storage.hpp @@ -18,6 +18,7 @@ namespace ams::fssystem { + template class HierarchicalSha256Storage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable { NON_COPYABLE(HierarchicalSha256Storage); NON_MOVEABLE(HierarchicalSha256Storage); @@ -25,17 +26,18 @@ namespace ams::fssystem { static constexpr s32 LayerCount = 3; static constexpr size_t HashSize = crypto::Sha256Generator::HashSize; private: - os::SdkMutex m_mutex; - IStorage *m_base_storage; + BaseStorageType m_base_storage; s64 m_base_storage_size; char *m_hash_buffer; size_t m_hash_buffer_size; s32 m_hash_target_block_size; s32 m_log_size_ratio; + fssystem::IHash256GeneratorFactory *m_hash_generator_factory; + os::SdkMutex m_mutex; public: HierarchicalSha256Storage() : m_mutex() { /* ... */ } - Result Initialize(IStorage **base_storages, s32 layer_count, size_t htbs, void *hash_buf, size_t hash_buf_size); + Result Initialize(BaseStorageType *base_storages, s32 layer_count, size_t htbs, void *hash_buf, size_t hash_buf_size, fssystem::IHash256GeneratorFactory *hgf); virtual Result Read(s64 offset, void *buffer, size_t size) override; virtual Result Write(s64 offset, const void *buffer, size_t size) override; @@ -46,7 +48,7 @@ namespace ams::fssystem { } virtual Result Flush() override { - return ResultSuccess(); + return m_base_storage->Flush(); } virtual Result SetSize(s64 size) override { diff --git a/libraries/libstratosphere/source/fssystem/fssystem_integrity_romfs_storage.cpp b/libraries/libstratosphere/source/fssystem/fssystem_integrity_romfs_storage.cpp index 64789a0c0..edc5e3ac9 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_integrity_romfs_storage.cpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_integrity_romfs_storage.cpp @@ -17,7 +17,7 @@ namespace ams::fssystem { - Result IntegrityRomFsStorage::Initialize(save::HierarchicalIntegrityVerificationInformation level_hash_info, Hash master_hash, save::HierarchicalIntegrityVerificationStorage::HierarchicalStorageInformation storage_info, IBufferManager *bm) { + Result IntegrityRomFsStorage::Initialize(save::HierarchicalIntegrityVerificationInformation level_hash_info, Hash master_hash, save::HierarchicalIntegrityVerificationStorage::HierarchicalStorageInformation storage_info, IBufferManager *bm, IHash256GeneratorFactory *hgf) { /* Validate preconditions. */ AMS_ASSERT(bm != nullptr); @@ -35,7 +35,7 @@ namespace ams::fssystem { } /* Initialize our integrity storage. */ - return m_integrity_storage.Initialize(level_hash_info, storage_info, std::addressof(m_buffers), std::addressof(m_mutex), fs::StorageType_RomFs); + return m_integrity_storage.Initialize(level_hash_info, storage_info, std::addressof(m_buffers), hgf, std::addressof(m_mutex), fs::StorageType_RomFs); } void IntegrityRomFsStorage::Finalize() { diff --git a/libraries/libstratosphere/source/fssystem/fssystem_memory_resource_buffer_hold_storage.hpp b/libraries/libstratosphere/source/fssystem/fssystem_memory_resource_buffer_hold_storage.hpp new file mode 100644 index 000000000..e90e41c79 --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/fssystem_memory_resource_buffer_hold_storage.hpp @@ -0,0 +1,87 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::fssystem { + + class MemoryResourceBufferHoldStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable { + NON_COPYABLE(MemoryResourceBufferHoldStorage); + NON_MOVEABLE(MemoryResourceBufferHoldStorage); + private: + std::shared_ptr m_storage; + MemoryResource *m_memory_resource; + void *m_buffer; + size_t m_buffer_size; + public: + MemoryResourceBufferHoldStorage(std::shared_ptr storage, MemoryResource *mr, size_t buffer_size) : m_storage(std::move(storage)), m_memory_resource(mr), m_buffer(m_memory_resource->Allocate(buffer_size)), m_buffer_size(buffer_size) { + /* ... */ + } + + virtual ~MemoryResourceBufferHoldStorage() { + /* If we have a buffer, deallocate it. */ + if (m_buffer != nullptr) { + m_memory_resource->Deallocate(m_buffer, m_buffer_size); + } + } + + ALWAYS_INLINE bool IsValid() const { return m_buffer != nullptr; } + ALWAYS_INLINE void *GetBuffer() const { return m_buffer; } + public: + virtual Result Read(s64 offset, void *buffer, size_t size) override { + /* Check pre-conditions. */ + AMS_ASSERT(m_storage != nullptr); + + return m_storage->Read(offset, buffer, size); + } + + virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { + /* Check pre-conditions. */ + AMS_ASSERT(m_storage != nullptr); + + return m_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size); + } + + virtual Result GetSize(s64 *out) override { + /* Check pre-conditions. */ + AMS_ASSERT(m_storage != nullptr); + + return m_storage->GetSize(out); + } + + virtual Result Flush() override { + /* Check pre-conditions. */ + AMS_ASSERT(m_storage != nullptr); + + return m_storage->Flush(); + } + + virtual Result Write(s64 offset, const void *buffer, size_t size) override { + /* Check pre-conditions. */ + AMS_ASSERT(m_storage != nullptr); + + return m_storage->Write(offset, buffer, size); + } + + virtual Result SetSize(s64 size) override { + /* Check pre-conditions. */ + AMS_ASSERT(m_storage != nullptr); + + return m_storage->SetSize(size); + } + }; + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_nca_file_system_driver.cpp b/libraries/libstratosphere/source/fssystem/fssystem_nca_file_system_driver.cpp index d4581f3dd..14d975243 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_nca_file_system_driver.cpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_nca_file_system_driver.cpp @@ -16,6 +16,7 @@ #include #include "fssystem_read_only_block_cache_storage.hpp" #include "fssystem_hierarchical_sha256_storage.hpp" +#include "fssystem_memory_resource_buffer_hold_storage.hpp" namespace ams::fssystem { @@ -30,103 +31,68 @@ namespace ams::fssystem { constexpr inline s32 SparseTableCacheBlockSize = SparseStorage::NodeSize; constexpr inline s32 SparseTableCacheCount = 4; - class BufferHolder { - NON_COPYABLE(BufferHolder); + //TODO: Better names for these? + //constexpr inline s32 CompressedDataBlockSize = 64_KB; + //constexpr inline s32 CompressedContinuousReadingSizeMax = 640_KB; + //constexpr inline s32 CompressedCacheBlockSize = 16_KB; + //constexpr inline s32 CompressedCacheCount = 32; + + constexpr inline s32 AesCtrStorageCacheBlockSize = 0x200; + constexpr inline s32 AesCtrStorageCacheCount = 9; + + class SharedNcaBodyStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable { + NON_COPYABLE(SharedNcaBodyStorage); + NON_MOVEABLE(SharedNcaBodyStorage); private: - MemoryResource *m_allocator; - char *m_buffer; - size_t m_buffer_size; + std::shared_ptr m_storage; + std::shared_ptr m_nca_reader; public: - BufferHolder() : m_allocator(), m_buffer(), m_buffer_size() { /* ... */ } - BufferHolder(MemoryResource *a, size_t sz) : m_allocator(a), m_buffer(static_cast(a->Allocate(sz))), m_buffer_size(sz) { /* ... */ } - ~BufferHolder() { - if (m_buffer != nullptr) { - m_allocator->Deallocate(m_buffer, m_buffer_size); - m_buffer = nullptr; - } + SharedNcaBodyStorage(std::shared_ptr s, std::shared_ptr r) : m_storage(std::move(s)), m_nca_reader(std::move(r)) { + /* ... */ } - BufferHolder(BufferHolder &&rhs) : m_allocator(rhs.m_allocator), m_buffer(rhs.m_buffer), m_buffer_size(rhs.m_buffer_size) { - rhs.m_buffer = nullptr; + virtual Result Read(s64 offset, void *buffer, size_t size) override { + /* Validate pre-conditions. */ + AMS_ASSERT(m_storage != nullptr); + + /* Read from the base storage. */ + return m_storage->Read(offset, buffer, size); } - BufferHolder &operator=(BufferHolder &&rhs) { - if (this != std::addressof(rhs)) { - AMS_ASSERT(m_buffer == nullptr); - m_allocator = rhs.m_allocator; - m_buffer = rhs.m_buffer; - m_buffer_size = rhs.m_buffer_size; + virtual Result GetSize(s64 *out) override { + /* Validate pre-conditions. */ + AMS_ASSERT(m_storage != nullptr); - rhs.m_buffer = nullptr; - } - return *this; + return m_storage->GetSize(out); } - bool IsValid() const { return m_buffer != nullptr; } - char *Get() const { return m_buffer; } - size_t GetSize() const { return m_buffer_size; } - }; + virtual Result Flush() override { + /* Validate pre-conditions. */ + AMS_ASSERT(m_storage != nullptr); - template - class DerivedStorageHolderImpl; - - template - class DerivedStorageHolderImpl> : public Base { - NON_COPYABLE(DerivedStorageHolderImpl); - public: - using StoragePointer = std::unique_ptr; - - template - using IndexedStoragePointer = StoragePointer; - private: - std::shared_ptr m_nca_reader; - std::array m_storages; - private: - - template - void SetImpl(IndexedStoragePointer &&ptr) { - static_assert(N < sizeof...(Is)); - m_storages[N] = std::move(ptr); + return m_storage->Flush(); } - public: - DerivedStorageHolderImpl() : Base(), m_nca_reader(), m_storages() { /* ... */ } - explicit DerivedStorageHolderImpl(std::shared_ptr nr) : Base(), m_nca_reader(nr), m_storages() { /* ... */ } - #define DEFINE_CONSTRUCTORS(n) \ - template \ - explicit DerivedStorageHolderImpl(AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS##n (T, t)) : Base(AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS##n (T, t)), m_nca_reader(), m_storages() { /* ... */ } \ - template \ - explicit DerivedStorageHolderImpl(AMS_UTIL_VARIADIC_TEMPLATE_ARGUMENTS##n (T, t), std::shared_ptr nr) : Base(AMS_UTIL_VARIADIC_TEMPLATE_FORWARDS##n (T, t)), m_nca_reader(nr), m_storages() { /* ... */ } + virtual Result Write(s64 offset, const void *buffer, size_t size) override { + /* Validate pre-conditions. */ + AMS_ASSERT(m_storage != nullptr); - AMS_UTIL_VARIADIC_INVOKE_MACRO(DEFINE_CONSTRUCTORS) - - #undef DEFINE_CONSTRUCTORS - - void Set(IndexedStoragePointer &&... ptrs) { - (this->SetImpl(std::forward>(ptrs)), ...); + /* Read from the base storage. */ + return m_storage->Write(offset, buffer, size); } - }; - template - using DerivedStorageHolder = DerivedStorageHolderImpl>; + virtual Result SetSize(s64 size) override { + /* Validate pre-conditions. */ + AMS_ASSERT(m_storage != nullptr); - template - class DerivedStorageHolderWithBuffer : public DerivedStorageHolder { - NON_COPYABLE(DerivedStorageHolderWithBuffer); - private: - using BaseHolder = DerivedStorageHolder; - private: - BufferHolder m_buffer; - public: - DerivedStorageHolderWithBuffer() : BaseHolder(), m_buffer() { /* ... */ } + return m_storage->SetSize(size); + } - template - DerivedStorageHolderWithBuffer(Args &&... args) : BaseHolder(std::forward(args)...), m_buffer() { /* ... */ } + virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { + /* Validate pre-conditions. */ + AMS_ASSERT(m_storage != nullptr); - using BaseHolder::Set; - - void Set(BufferHolder &&buf) { - m_buffer = std::move(buf); + return m_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size); } }; @@ -138,13 +104,13 @@ namespace ams::fssystem { static constexpr size_t KeySize = crypto::Aes128CtrEncryptor::KeySize; static constexpr size_t IvSize = crypto::Aes128CtrEncryptor::IvSize; private: - IStorage * const m_base_storage; + std::shared_ptr m_base_storage; u8 m_iv[IvSize]; DecryptAesCtrFunction m_decrypt_function; s32 m_key_index; u8 m_encrypted_key[KeySize]; public: - AesCtrStorageExternal(fs::IStorage *bs, const void *enc_key, size_t enc_key_size, const void *iv, size_t iv_size, DecryptAesCtrFunction df, s32 kidx) : m_base_storage(bs), m_decrypt_function(df), m_key_index(kidx) { + AesCtrStorageExternal(std::shared_ptr bs, const void *enc_key, size_t enc_key_size, const void *iv, size_t iv_size, DecryptAesCtrFunction df, s32 kidx) : m_base_storage(std::move(bs)), m_decrypt_function(df), m_key_index(kidx) { AMS_ASSERT(bs != nullptr); AMS_ASSERT(enc_key_size == KeySize); AMS_ASSERT(iv != nullptr); @@ -260,15 +226,15 @@ namespace ams::fssystem { NON_COPYABLE(SwitchStorage); NON_MOVEABLE(SwitchStorage); private: - std::unique_ptr m_true_storage; - std::unique_ptr m_false_storage; + std::shared_ptr m_true_storage; + std::shared_ptr m_false_storage; F m_truth_function; private: - ALWAYS_INLINE std::unique_ptr &SelectStorage() { + ALWAYS_INLINE std::shared_ptr &SelectStorage() { return m_truth_function() ? m_true_storage : m_false_storage; } public: - SwitchStorage(std::unique_ptr &&t, std::unique_ptr &&f, F func) : m_true_storage(std::move(t)), m_false_storage(std::move(f)), m_truth_function(func) { /* ... */ } + SwitchStorage(std::shared_ptr t, std::shared_ptr f, F func) : m_true_storage(std::move(t)), m_false_storage(std::move(f)), m_truth_function(func) { /* ... */ } virtual Result Read(s64 offset, void *buffer, size_t size) override { return this->SelectStorage()->Read(offset, buffer, size); @@ -317,11 +283,7 @@ namespace ams::fssystem { return static_cast(reader.GetFsEndOffset(fs_index)); } - inline void MakeAesXtsIv(void *ctr, s64 base_offset) { - util::StoreBigEndian(static_cast(ctr) + 1, base_offset / NcaHeader::XtsBlockSize); - } - - inline bool IsUsingHardwareAesCtrForSpeedEmulation() { + inline bool IsUsingHwAesCtrForSpeedEmulation() { auto mode = fssystem::SpeedEmulationConfiguration::GetSpeedEmulationMode(); return mode == fs::SpeedEmulationMode::None || mode == fs::SpeedEmulationMode::Slower; } @@ -340,389 +302,521 @@ namespace ams::fssystem { } - Result NcaFileSystemDriver::OpenRawStorage(std::shared_ptr *out, s32 fs_index) { - /* Validate preconditions. */ - AMS_ASSERT(out != nullptr); - AMS_ASSERT(0 <= fs_index && fs_index < NcaHeader::FsCountMax); - AMS_ASSERT(m_reader != nullptr); + Result NcaFileSystemDriver::OpenStorage(std::shared_ptr *out, std::shared_ptr *out_splitter, NcaFsHeaderReader *out_header_reader, s32 fs_index) { + /* Create a storage context. */ + StorageContext ctx{}; - /* Get storage extents. */ - const auto storage_offset = GetFsOffset(*m_reader, fs_index); - const auto storage_size = GetFsEndOffset(*m_reader, fs_index) - storage_offset; - R_UNLESS(storage_size > 0, fs::ResultInvalidNcaHeader()); + /* Open storage. */ + R_TRY(this->OpenStorageImpl(out, out_header_reader, fs_index, std::addressof(ctx))); - /* Allocate a substorage. */ - *out = fssystem::AllocateShared>(m_reader->GetBodyStorage(), storage_offset, storage_size, m_reader); - R_UNLESS(*out != nullptr, fs::ResultAllocationFailureInAllocateShared()); + /* If we have a compressed storage, use it as splitter. */ + if (ctx.compressed_storage != nullptr) { + *out_splitter = std::move(ctx.compressed_storage); + } else { + /* Otherwise, allocate a default splitter. */ + *out_splitter = fssystem::AllocateShared(); + R_UNLESS(*out_splitter != nullptr, fs::ResultAllocationFailureInAllocateShared()); + } return ResultSuccess(); } - Result NcaFileSystemDriver::OpenStorage(std::shared_ptr *out, NcaFsHeaderReader *out_header_reader, s32 fs_index) { + Result NcaFileSystemDriver::OpenStorageImpl(std::shared_ptr *out, NcaFsHeaderReader *out_header_reader, s32 fs_index, StorageContext *ctx) { /* Validate preconditions. */ AMS_ASSERT(out != nullptr); AMS_ASSERT(out_header_reader != nullptr); AMS_ASSERT(0 <= fs_index && fs_index < NcaHeader::FsCountMax); - /* Open a reader with the appropriate option. */ - StorageOption option(out_header_reader, fs_index); - R_TRY(this->OpenStorage(out, std::addressof(option))); - - return ResultSuccess(); - } - - Result NcaFileSystemDriver::OpenStorage(std::shared_ptr *out, StorageOption *option) { - /* Validate preconditions. */ - AMS_ASSERT(out != nullptr); - AMS_ASSERT(option != nullptr); - AMS_ASSERT(m_reader != nullptr); - - /* Get and validate fs index. */ - const auto fs_index = option->GetFsIndex(); + /* Validate the fs index. */ R_UNLESS(m_reader->HasFsInfo(fs_index), fs::ResultPartitionNotFound()); - /* Initialize a reader for the fs header. */ - auto &header_reader = option->GetHeaderReader(); - R_TRY(header_reader.Initialize(*m_reader, fs_index)); + /* Initialize our header reader for the fs index. */ + R_TRY(out_header_reader->Initialize(*m_reader, fs_index)); - /* Create the storage. */ - std::unique_ptr storage; - { - BaseStorage base_storage; - R_TRY(this->CreateBaseStorage(std::addressof(base_storage), option)); - R_TRY(this->CreateDecryptableStorage(std::addressof(storage), option, std::addressof(base_storage))); - } - R_TRY(this->CreateIndirectStorage(std::addressof(storage), option, std::move(storage))); - R_TRY(this->CreateVerificationStorage(std::addressof(storage), std::move(storage), std::addressof(header_reader))); + /* Declare the storage we're opening. */ + std::shared_ptr storage; - /* Set the output. */ - *out = std::move(storage); - return ResultSuccess(); - } - - Result NcaFileSystemDriver::OpenDecryptableStorage(std::shared_ptr *out, StorageOption *option, bool indirect_needed) { - /* Validate preconditions. */ - AMS_ASSERT(out != nullptr); - AMS_ASSERT(option != nullptr); - AMS_ASSERT(m_reader != nullptr); - - /* Get and validate fs index. */ - const auto fs_index = option->GetFsIndex(); - R_UNLESS(m_reader->HasFsInfo(fs_index), fs::ResultPartitionNotFound()); - - /* Initialize a reader for the fs header. */ - auto &header_reader = option->GetHeaderReader(); - if (!header_reader.IsInitialized()) { - R_TRY(header_reader.Initialize(*m_reader, fs_index)); - } - - /* Create the storage. */ - std::unique_ptr storage; - { - BaseStorage base_storage; - R_TRY(this->CreateBaseStorage(std::addressof(base_storage), option)); - R_TRY(this->CreateDecryptableStorage(std::addressof(storage), option, std::addressof(base_storage))); - } - - /* Set the data storage. */ - { - const auto &patch_info = header_reader.GetPatchInfo(); - s64 data_storage_size = 0; - - if (header_reader.GetEncryptionType() == NcaFsHeader::EncryptionType::AesCtrEx) { - data_storage_size = patch_info.aes_ctr_ex_offset; - } else { - switch (header_reader.GetHashType()) { - case NcaFsHeader::HashType::HierarchicalSha256Hash: - { - const auto ®ion = GetSha256DataRegion(header_reader.GetHashData()); - data_storage_size = region.offset + region.size; - } - break; - case NcaFsHeader::HashType::HierarchicalIntegrityHash: - { - const auto &info = GetIntegrityDataInfo(header_reader.GetHashData()); - data_storage_size = info.offset + info.size; - } - break; - default: - return fs::ResultInvalidNcaFsHeaderHashType(); - } - - data_storage_size = util::AlignUp(data_storage_size, NcaHeader::XtsBlockSize); - } - - /* Set the data storage in option. */ - option->SetDataStorage(storage.get(), data_storage_size); - } - - /* Create the indirect storage if needed. */ - if (indirect_needed) { - R_TRY(this->CreateIndirectStorage(std::addressof(storage), option, std::move(storage))); - } - - /* Set the output. */ - *out = std::move(storage); - return ResultSuccess(); - } - - Result NcaFileSystemDriver::CreateBaseStorage(BaseStorage *out, StorageOption *option) { - /* Validate preconditions. */ - AMS_ASSERT(out != nullptr); - AMS_ASSERT(option != nullptr); - - /* Get the header reader. */ - const auto fs_index = option->GetFsIndex(); - const auto &header_reader = option->GetHeaderReader(); - - /* Get storage extents. */ - const auto storage_offset = GetFsOffset(*m_reader, fs_index); - const auto storage_size = GetFsEndOffset(*m_reader, fs_index) - storage_offset; - R_UNLESS(storage_size > 0, fs::ResultInvalidNcaHeader()); - - /* Set up the sparse storage if we need to, otherwise use body storage directly. */ - if (header_reader.ExistsSparseLayer()) { - const auto &sparse_info = header_reader.GetSparseInfo(); - - /* Read and verify the bucket tree header. */ - BucketTree::Header header; - std::memcpy(std::addressof(header), sparse_info.bucket.header, sizeof(header)); - R_TRY(header.Verify()); - - /* Create a new holder for the storages. */ - std::unique_ptr storage = std::make_unique>(m_reader); - R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInNew()); - - /* If there are no entries, there's nothing to actually do. */ - if (header.entry_count == 0) { - storage->Initialize(storage_size); - } else { - /* Prepare to create the decryptable storage. */ - const auto raw_storage = m_reader->GetBodyStorage(); - const auto raw_storage_offset = sparse_info.physical_offset; - const auto raw_storage_size = sparse_info.GetPhysicalSize(); - - /* Validate that we're within range. */ - s64 body_storage_size = 0; - R_TRY(raw_storage->GetSize(std::addressof(body_storage_size))); - R_UNLESS(raw_storage_offset + raw_storage_size <= body_storage_size, fs::ResultNcaBaseStorageOutOfRangeB()); - - /* Create the decryptable storage. */ - std::unique_ptr decryptable_storage; - { - BaseStorage base_storage(raw_storage, raw_storage_offset, raw_storage_size); - base_storage.SetStorageOffset(raw_storage_offset); - base_storage.SetAesCtrUpperIv(sparse_info.MakeAesCtrUpperIv(header_reader.GetAesCtrUpperIv())); - R_TRY(this->CreateAesCtrStorage(std::addressof(decryptable_storage), std::addressof(base_storage))); - } - - /* Create the table storage. */ - std::unique_ptr table_storage = std::make_unique(); - R_UNLESS(table_storage != nullptr, fs::ResultAllocationFailureInNew()); - - /* Initialize the table storage. */ - R_TRY(table_storage->Initialize(fs::SubStorage(decryptable_storage.get(), 0, raw_storage_size), m_buffer_manager, SparseTableCacheBlockSize, SparseTableCacheCount)); - - /* Determine storage extents. */ - const auto node_offset = sparse_info.bucket.offset; - const auto node_size = SparseStorage::QueryNodeStorageSize(header.entry_count); - const auto entry_offset = node_offset + node_size; - const auto entry_size = SparseStorage::QueryEntryStorageSize(header.entry_count); - - /* Initialize the storage. */ - R_TRY(storage->Initialize(m_allocator, fs::SubStorage(table_storage.get(), node_offset, node_size), fs::SubStorage(table_storage.get(), entry_offset, entry_size), header.entry_count)); - - /* Set the data/decryptable storage. */ - storage->SetDataStorage(raw_storage, raw_storage_offset, node_offset); - storage->Set(std::move(decryptable_storage), std::move(table_storage)); - } - - /* Set the sparse storage. */ - option->SetSparseStorage(storage.get()); - - /* Set the out storage. */ - out->SetStorage(std::move(storage)); + /* Process sparse layer. */ + s64 fs_data_offset = 0; + if (out_header_reader->ExistsSparseLayer()) { + /* Create the sparse storage. */ + R_TRY(this->CreateSparseStorage(std::addressof(storage), std::addressof(fs_data_offset), ctx != nullptr ? std::addressof(ctx->current_sparse_storage) : nullptr, ctx != nullptr ? std::addressof(ctx->sparse_storage_meta_storage) : nullptr, fs_index, out_header_reader->GetAesCtrUpperIv(), out_header_reader->GetSparseInfo())); } else { - /* Validate that we're within range. */ - s64 body_storage_size; - R_TRY(m_reader->GetBodyStorage()->GetSize(std::addressof(body_storage_size))); - R_UNLESS(storage_offset + storage_size <= body_storage_size, fs::ResultNcaBaseStorageOutOfRangeB()); + /* Get the data offsets. */ + fs_data_offset = GetFsOffset(*m_reader, fs_index); + const auto fs_end_offset = GetFsEndOffset(*m_reader, fs_index); - /* Set the out storage. */ - out->SetStorage(m_reader->GetBodyStorage(), storage_offset, storage_size); + /* Validate that we're within range. */ + const auto data_size = fs_end_offset - fs_data_offset; + R_UNLESS(data_size > 0, fs::ResultInvalidNcaHeader()); + + /* Create the body substorage. */ + R_TRY(this->CreateBodySubStorage(std::addressof(storage), fs_data_offset, data_size)); + + /* Potentially save the body substorage to our context. */ + if (ctx != nullptr) { + ctx->body_substorage = storage; + } } - /* Set the crypto variables. */ - out->SetStorageOffset(storage_offset); - out->SetAesCtrUpperIv(header_reader.GetAesCtrUpperIv()); + /* Process patch layer. */ + const auto &patch_info = out_header_reader->GetPatchInfo(); + if (patch_info.HasAesCtrExTable()) { + /* Check the encryption type. */ + AMS_ASSERT(out_header_reader->GetEncryptionType() == NcaFsHeader::EncryptionType::AesCtrEx); + /* Create the ex meta storage. */ + std::shared_ptr aes_ctr_ex_storage_meta_storage; + R_TRY(this->CreateAesCtrExMetaStorage(std::addressof(aes_ctr_ex_storage_meta_storage), storage, fs_data_offset, out_header_reader->GetAesCtrUpperIv(), patch_info)); + + /* Create the ex storage. */ + std::shared_ptr aes_ctr_ex_storage; + R_TRY(this->CreateAesCtrExStorage(std::addressof(aes_ctr_ex_storage), ctx != nullptr ? std::addressof(ctx->aes_ctr_ex_storage) : nullptr, std::move(storage), aes_ctr_ex_storage_meta_storage, fs_data_offset, out_header_reader->GetAesCtrUpperIv(), patch_info)); + + /* Set the base storage as the ex storage. */ + storage = std::move(aes_ctr_ex_storage); + + /* Potentially save storages to our context. */ + if (ctx != nullptr) { + ctx->aes_ctr_ex_storage_meta_storage = aes_ctr_ex_storage_meta_storage; + ctx->aes_ctr_ex_storage_data_storage = storage; + ctx->fs_data_storage = storage; + } + } else { + /* Create the appropriate storage for the encryption type. */ + switch (out_header_reader->GetEncryptionType()) { + case NcaFsHeader::EncryptionType::None: + /* If there's no encryption, use the base storage we made previously. */ + break; + case NcaFsHeader::EncryptionType::AesXts: + R_TRY(this->CreateAesXtsStorage(std::addressof(storage), std::move(storage), fs_data_offset)); + break; + case NcaFsHeader::EncryptionType::AesCtr: + R_TRY(this->CreateAesCtrStorage(std::addressof(storage), std::move(storage), fs_data_offset, out_header_reader->GetAesCtrUpperIv(), AlignmentStorageRequirement_None)); + break; + default: + return fs::ResultInvalidNcaFsHeaderEncryptionType(); + } + + /* Potentially save storages to our context. */ + if (ctx != nullptr) { + ctx->fs_data_storage = storage; + } + } + + /* Process indirect layer. */ + if (patch_info.HasIndirectTable()) { + /* Create the indirect meta storage */ + std::shared_ptr indirect_storage_meta_storage; + R_TRY(this->CreateIndirectStorageMetaStorage(std::addressof(indirect_storage_meta_storage), storage, patch_info)); + + /* Potentially save the indirect meta storage to our context. */ + if (ctx != nullptr) { + ctx->indirect_storage_meta_storage = indirect_storage_meta_storage; + } + + /* Get the original indirectable storage. */ + std::shared_ptr original_indirectable_storage; + if (m_original_reader != nullptr && m_original_reader->HasFsInfo(fs_index)) { + /* Create a driver for the original. */ + NcaFileSystemDriver original_driver(m_original_reader, m_allocator, m_buffer_manager, m_hash_generator_factory_selector); + + /* Create a header reader for the original. */ + NcaFsHeaderReader original_header_reader; + R_TRY(original_header_reader.Initialize(*m_original_reader, fs_index)); + + /* Open original indirectable storage. */ + R_TRY(original_driver.OpenIndirectableStorageAsOriginal(std::addressof(original_indirectable_storage), std::addressof(original_header_reader), ctx)); + } else { + /* Allocate a dummy memory storage as original storage. */ + original_indirectable_storage = fssystem::AllocateShared(nullptr, 0); + R_UNLESS(original_indirectable_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); + } + + /* Create the indirect storage. */ + std::shared_ptr indirect_storage; + R_TRY(this->CreateIndirectStorage(std::addressof(indirect_storage), ctx != nullptr ? std::addressof(ctx->indirect_storage) : nullptr, std::move(storage), std::move(original_indirectable_storage), std::move(indirect_storage_meta_storage), patch_info)); + + /* Set storage as the indirect storage. */ + storage = std::move(indirect_storage); + } + + /* Check if we're sparse or requested to skip the integrity layer. */ + if (out_header_reader->ExistsSparseLayer() || (ctx != nullptr && ctx->open_raw_storage)) { + *out = std::move(storage); + return ResultSuccess(); + } + + /* Process hash/integrity layer. */ + switch (out_header_reader->GetHashType()) { + case NcaFsHeader::HashType::HierarchicalSha256Hash: + R_TRY(this->CreateSha256Storage(std::addressof(storage), std::move(storage), out_header_reader->GetHashData().hierarchical_sha256_data)); + break; + case NcaFsHeader::HashType::HierarchicalIntegrityHash: + R_TRY(this->CreateIntegrityVerificationStorage(std::addressof(storage), std::move(storage), out_header_reader->GetHashData().integrity_meta_info)); + break; + default: + return fs::ResultInvalidNcaFsHeaderHashType(); + } + + /* Process compression layer. */ + if (out_header_reader->ExistsCompressionLayer()) { + R_TRY(this->CreateCompressedStorage(std::addressof(storage), ctx != nullptr ? std::addressof(ctx->compressed_storage) : nullptr, ctx != nullptr ? std::addressof(ctx->compressed_storage_meta_storage) : nullptr, std::move(storage), out_header_reader->GetCompressionInfo())); + } + + /* Set output storage. */ + *out = std::move(storage); return ResultSuccess(); } - Result NcaFileSystemDriver::CreateDecryptableStorage(std::unique_ptr *out, StorageOption *option, BaseStorage *base_storage) { - /* Validate preconditions. */ - AMS_ASSERT(out != nullptr); - AMS_ASSERT(option != nullptr); - AMS_ASSERT(base_storage != nullptr); + Result NcaFileSystemDriver::OpenIndirectableStorageAsOriginal(std::shared_ptr *out, const NcaFsHeaderReader *header_reader, StorageContext *ctx) { + /* Get the fs index. */ + const auto fs_index = header_reader->GetFsIndex(); - /* Get the header reader. */ - const auto &header_reader = option->GetHeaderReader(); + /* Declare the storage we're opening. */ + std::shared_ptr storage; + + /* Process sparse layer. */ + s64 fs_data_offset = 0; + if (header_reader->ExistsSparseLayer()) { + /* Create the sparse storage. */ + R_TRY(this->CreateSparseStorage(std::addressof(storage), std::addressof(fs_data_offset), ctx != nullptr ? std::addressof(ctx->original_sparse_storage) : nullptr, ctx != nullptr ? std::addressof(ctx->sparse_storage_meta_storage) : nullptr, fs_index, header_reader->GetAesCtrUpperIv(), header_reader->GetSparseInfo())); + } else { + /* Get the data offsets. */ + fs_data_offset = GetFsOffset(*m_reader, fs_index); + const auto fs_end_offset = GetFsEndOffset(*m_reader, fs_index); + + /* Validate that we're within range. */ + const auto data_size = fs_end_offset - fs_data_offset; + R_UNLESS(data_size > 0, fs::ResultInvalidNcaHeader()); + + /* Create the body substorage. */ + R_TRY(this->CreateBodySubStorage(std::addressof(storage), fs_data_offset, data_size)); + } /* Create the appropriate storage for the encryption type. */ - switch (header_reader.GetEncryptionType()) { + switch (header_reader->GetEncryptionType()) { case NcaFsHeader::EncryptionType::None: - *out = base_storage->MakeStorage(); - R_UNLESS(*out != nullptr, fs::ResultAllocationFailureInNew()); + /* If there's no encryption, use the base storage we made previously. */ break; case NcaFsHeader::EncryptionType::AesXts: - R_TRY(this->CreateAesXtsStorage(out, base_storage)); + R_TRY(this->CreateAesXtsStorage(std::addressof(storage), std::move(storage), fs_data_offset)); break; case NcaFsHeader::EncryptionType::AesCtr: - R_TRY(this->CreateAesCtrStorage(out, base_storage)); - break; - case NcaFsHeader::EncryptionType::AesCtrEx: - R_TRY(this->CreateAesCtrExStorage(out, option, base_storage)); + R_TRY(this->CreateAesCtrStorage(std::addressof(storage), std::move(storage), fs_data_offset, header_reader->GetAesCtrUpperIv(), AlignmentStorageRequirement_CacheBlockSize)); break; default: return fs::ResultInvalidNcaFsHeaderEncryptionType(); } - return ResultSuccess(); - } - - Result NcaFileSystemDriver::CreateAesXtsStorage(std::unique_ptr *out, BaseStorage *base_storage) { - /* Validate preconditions. */ - AMS_ASSERT(out != nullptr); - AMS_ASSERT(base_storage != nullptr); - - /* Create the iv. */ - u8 iv[AesXtsStorage::IvSize] = {}; - MakeAesXtsIv(iv, base_storage->GetStorageOffset()); - - /* Allocate a new raw storage. */ - std::unique_ptr raw_storage = base_storage->MakeStorage(); - R_UNLESS(raw_storage != nullptr, fs::ResultAllocationFailureInNew()); - - /* Make the aes xts storage. */ - const auto *key1 = m_reader->GetDecryptionKey(NcaHeader::DecryptionKey_AesXts1); - const auto *key2 = m_reader->GetDecryptionKey(NcaHeader::DecryptionKey_AesXts2); - std::unique_ptr xts_storage = std::make_unique(raw_storage.get(), key1, key2, AesXtsStorage::KeySize, iv, AesXtsStorage::IvSize, NcaHeader::XtsBlockSize); - R_UNLESS(xts_storage != nullptr, fs::ResultAllocationFailureInNew()); - - /* Make the out storage. */ - std::unique_ptr storage = std::make_unique, 2>>(xts_storage.get(), m_reader); - R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInNew()); - - /* Set the substorages. */ - storage->Set(std::move(raw_storage), std::move(xts_storage)); - - /* Set the output. */ + /* Set output storage. */ *out = std::move(storage); return ResultSuccess(); } - Result NcaFileSystemDriver::CreateAesCtrStorage(std::unique_ptr *out, BaseStorage *base_storage) { - /* Validate preconditions. */ + Result NcaFileSystemDriver::CreateBodySubStorage(std::shared_ptr *out, s64 offset, s64 size) { + /* Create the body storage. */ + auto body_storage = fssystem::AllocateShared(m_reader->GetSharedBodyStorage(), m_reader); + R_UNLESS(body_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); + + /* Get the body storage size. */ + s64 body_size = 0; + R_TRY(body_storage->GetSize(std::addressof(body_size))); + + /* Check that we're within range. */ + R_UNLESS(offset + size <= body_size, fs::ResultNcaBaseStorageOutOfRangeB()); + + /* Create substorage. */ + auto body_substorage = fssystem::AllocateShared(std::move(body_storage), offset, size); + R_UNLESS(body_substorage != nullptr, fs::ResultAllocationFailureInAllocateShared()); + + /* Set the output storage. */ + *out = std::move(body_substorage); + return ResultSuccess(); + } + + Result NcaFileSystemDriver::CreateAesCtrStorage(std::shared_ptr *out, std::shared_ptr base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, AlignmentStorageRequirement alignment_storage_requirement) { + /* Check pre-conditions. */ AMS_ASSERT(out != nullptr); AMS_ASSERT(base_storage != nullptr); + /* Enforce alignment of accesses to base storage. */ + switch (alignment_storage_requirement) { + case AlignmentStorageRequirement_CacheBlockSize: + { + /* Get the base storage's size. */ + s64 base_size; + R_TRY(base_storage->GetSize(std::addressof(base_size))); + + /* Create buffered storage. */ + auto buffered_storage = fssystem::AllocateShared(); + R_UNLESS(buffered_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); + + /* Initialize the buffered storage. */ + R_TRY(buffered_storage->Initialize(fs::SubStorage(std::move(base_storage), 0, base_size), m_buffer_manager, AesCtrStorageCacheBlockSize, AesCtrStorageCacheCount)); + + /* Enable bulk read in the buffered storage. */ + buffered_storage->EnableBulkRead(); + + /* Use the buffered storage in place of our base storage. */ + base_storage = std::move(buffered_storage); + } + break; + case AlignmentStorageRequirement_None: + default: + /* No alignment enforcing is required. */ + break; + } + /* Create the iv. */ - u8 iv[AesCtrStorage::IvSize] = {}; - AesCtrStorage::MakeIv(iv, sizeof(iv), base_storage->GetAesCtrUpperIv().value, base_storage->GetStorageOffset()); + u8 iv[AesCtrStorageBySharedPointer::IvSize] = {}; + AesCtrStorageBySharedPointer::MakeIv(iv, sizeof(iv), upper_iv.value, offset); - /* Create the raw storage. */ - std::unique_ptr raw_storage = base_storage->MakeStorage(); - - /* Create the decrypt storage. */ - const bool has_external_key = m_reader->HasExternalDecryptionKey(); - std::unique_ptr decrypt_storage; - if (has_external_key) { - decrypt_storage = std::make_unique(raw_storage.get(), m_reader->GetExternalDecryptionKey(), AesCtrStorageExternal::KeySize, iv, AesCtrStorageExternal::IvSize, m_reader->GetExternalDecryptAesCtrFunctionForExternalKey(), -1); - R_UNLESS(decrypt_storage != nullptr, fs::ResultAllocationFailureInNew()); + /* Create the ctr storage. */ + std::shared_ptr aes_ctr_storage; + if (m_reader->HasExternalDecryptionKey()) { + aes_ctr_storage = fssystem::AllocateShared(std::move(base_storage), m_reader->GetExternalDecryptionKey(), AesCtrStorageExternal::KeySize, iv, AesCtrStorageExternal::IvSize, m_reader->GetExternalDecryptAesCtrFunctionForExternalKey(), -1); + R_UNLESS(aes_ctr_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); } else { - /* Check if we have a hardware key. */ - const bool has_hardware_key = m_reader->HasInternalDecryptionKeyForAesHardwareSpeedEmulation(); - - /* Create the software decryption storage. */ - std::unique_ptr aes_ctr_sw_storage = std::make_unique(raw_storage.get(), m_reader->GetDecryptionKey(NcaHeader::DecryptionKey_AesCtr), AesCtrStorage::KeySize, iv, AesCtrStorage::IvSize); - R_UNLESS(aes_ctr_sw_storage != nullptr, fs::ResultAllocationFailureInNew()); + /* Create software decryption storage. */ + auto sw_storage = fssystem::AllocateShared(base_storage, m_reader->GetDecryptionKey(NcaHeader::DecryptionKey_AesCtr), AesCtrStorageBySharedPointer::KeySize, iv, AesCtrStorageBySharedPointer::IvSize); + R_UNLESS(sw_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); /* If we have a hardware key and should use it, make the hardware decryption storage. */ - if (has_hardware_key && !m_reader->IsSoftwareAesPrioritized()) { - std::unique_ptr aes_ctr_hw_storage = std::make_unique(raw_storage.get(), m_reader->GetDecryptionKey(NcaHeader::DecryptionKey_AesCtrHw), AesCtrStorageExternal::KeySize, iv, AesCtrStorageExternal::IvSize, m_reader->GetExternalDecryptAesCtrFunction(), GetKeyTypeValue(m_reader->GetKeyIndex(), m_reader->GetKeyGeneration())); - R_UNLESS(aes_ctr_hw_storage != nullptr, fs::ResultAllocationFailureInNew()); + if (m_reader->HasInternalDecryptionKeyForAesHw() && !m_reader->IsSoftwareAesPrioritized()) { + auto hw_storage = fssystem::AllocateShared(base_storage, m_reader->GetDecryptionKey(NcaHeader::DecryptionKey_AesCtrHw), AesCtrStorageExternal::KeySize, iv, AesCtrStorageExternal::IvSize, m_reader->GetExternalDecryptAesCtrFunction(), GetKeyTypeValue(m_reader->GetKeyIndex(), m_reader->GetKeyGeneration())); + R_UNLESS(hw_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); /* Create the selection storage. */ - decrypt_storage = std::make_unique>(std::move(aes_ctr_hw_storage), std::move(aes_ctr_sw_storage), IsUsingHardwareAesCtrForSpeedEmulation); - R_UNLESS(decrypt_storage != nullptr, fs::ResultAllocationFailureInNew()); + auto switch_storage = fssystem::AllocateShared>(std::move(hw_storage), std::move(sw_storage), IsUsingHwAesCtrForSpeedEmulation); + R_UNLESS(switch_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); + + /* Use the selection storage. */ + aes_ctr_storage = std::move(switch_storage); } else { /* Otherwise, just use the software decryption storage. */ - decrypt_storage = std::move(aes_ctr_sw_storage); + aes_ctr_storage = std::move(sw_storage); } } - /* Create the storage holder. */ - std::unique_ptr storage = std::make_unique, 2>>(decrypt_storage.get(), m_reader); - R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInNew()); - - /* Set the storage holder's storages. */ - storage->Set(std::move(raw_storage), std::move(decrypt_storage)); + /* Create alignment matching storage. */ + auto aligned_storage = fssystem::AllocateShared>(std::move(aes_ctr_storage)); + R_UNLESS(aligned_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); /* Set the out storage. */ - *out = std::move(storage); + *out = std::move(aligned_storage); return ResultSuccess(); } - Result NcaFileSystemDriver::CreateAesCtrExStorage(std::unique_ptr *out, StorageOption *option, BaseStorage *base_storage) { - /* Validate preconditions. */ + Result NcaFileSystemDriver::CreateAesXtsStorage(std::shared_ptr *out, std::shared_ptr base_storage, s64 offset) { + /* Check pre-conditions. */ AMS_ASSERT(out != nullptr); - AMS_ASSERT(option != nullptr); AMS_ASSERT(base_storage != nullptr); - /* Check if indirection is needed. */ - const auto &header_reader = option->GetHeaderReader(); - const auto &patch_info = header_reader.GetPatchInfo(); + /* Create the iv. */ + u8 iv[AesXtsStorageBySharedPointer::IvSize] = {}; + AesXtsStorageBySharedPointer::MakeAesXtsIv(iv, sizeof(iv), offset, NcaHeader::XtsBlockSize); + + /* Make the aes xts storage. */ + const auto * const key1 = m_reader->GetDecryptionKey(NcaHeader::DecryptionKey_AesXts1); + const auto * const key2 = m_reader->GetDecryptionKey(NcaHeader::DecryptionKey_AesXts2); + auto xts_storage = fssystem::AllocateShared(std::move(base_storage), key1, key2, AesXtsStorageBySharedPointer::KeySize, iv, AesXtsStorageBySharedPointer::IvSize, NcaHeader::XtsBlockSize); + R_UNLESS(xts_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); + + /* Create alignment matching storage. */ + auto aligned_storage = fssystem::AllocateShared>(std::move(xts_storage)); + R_UNLESS(aligned_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); + + /* Set the out storage. */ + *out = std::move(aligned_storage); + return ResultSuccess(); + } + + Result NcaFileSystemDriver::CreateSparseStorageMetaStorage(std::shared_ptr *out, std::shared_ptr base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, const NcaSparseInfo &sparse_info) { + /* Validate preconditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(base_storage != nullptr); + + /* Get the base storage size. */ + s64 base_size = 0; + R_TRY(base_storage->GetSize(std::addressof(base_size))); + + /* Get the meta extents. */ + const auto meta_offset = sparse_info.bucket.offset; + const auto meta_size = sparse_info.bucket.size; + R_UNLESS(meta_offset + meta_size - offset <= base_size, fs::ResultNcaBaseStorageOutOfRangeB()); + + /* Create the encrypted storage. */ + auto enc_storage = fssystem::AllocateShared(std::move(base_storage), meta_offset, meta_size); + R_UNLESS(enc_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); + + /* Create the decrypted storage. */ + std::shared_ptr decrypted_storage; + R_TRY(this->CreateAesCtrStorage(std::addressof(decrypted_storage), std::move(enc_storage), offset + meta_offset, sparse_info.MakeAesCtrUpperIv(upper_iv), AlignmentStorageRequirement_None)); + + /* Create meta storage. */ + auto meta_storage = fssystem::AllocateShared(); + R_UNLESS(meta_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); + + /* Initialize the meta storage. */ + R_TRY(meta_storage->Initialize(fs::SubStorage(std::move(decrypted_storage), 0, meta_size), m_buffer_manager, SparseTableCacheBlockSize, SparseTableCacheCount)); + + /* Set the output. */ + *out = std::move(meta_storage); + return ResultSuccess(); + } + + Result NcaFileSystemDriver::CreateSparseStorageCore(std::shared_ptr *out, std::shared_ptr base_storage, s64 base_size, std::shared_ptr meta_storage, const NcaSparseInfo &sparse_info, bool external_info) { + /* Validate preconditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(base_storage != nullptr); + AMS_ASSERT(meta_storage != nullptr); + + /* Read and verify the bucket tree header. */ + BucketTree::Header header; + std::memcpy(std::addressof(header), sparse_info.bucket.header, sizeof(header)); + R_TRY(header.Verify()); + + /* Determine storage extents. */ + const auto node_offset = 0; + const auto node_size = SparseStorage::QueryNodeStorageSize(header.entry_count); + const auto entry_offset = node_offset + node_size; + const auto entry_size = SparseStorage::QueryEntryStorageSize(header.entry_count); + + /* Create the sparse storage. */ + auto sparse_storage = fssystem::AllocateShared(); + R_UNLESS(sparse_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); + + /* Sanity check that we can be doing this. */ + AMS_ASSERT(header.entry_count != 0); + + /* Initialize the sparse storage. */ + R_TRY(sparse_storage->Initialize(m_allocator, fs::SubStorage(meta_storage, node_offset, node_size), fs::SubStorage(meta_storage, entry_offset, entry_size), header.entry_count)); + + /* If not external, set the data storage. */ + if (!external_info) { + sparse_storage->SetDataStorage(fs::SubStorage(std::move(base_storage), 0, base_size)); + } + + /* Set the output. */ + *out = std::move(sparse_storage); + return ResultSuccess(); + } + + Result NcaFileSystemDriver::CreateSparseStorage(std::shared_ptr *out, s64 *out_fs_data_offset, std::shared_ptr *out_sparse_storage, std::shared_ptr *out_meta_storage, s32 index, const NcaAesCtrUpperIv &upper_iv, const NcaSparseInfo &sparse_info) { + /* Validate preconditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(base_storage != nullptr); + + /* Check the sparse info generation. */ + R_UNLESS(sparse_info.generation != 0, fs::ResultInvalidNcaHeader()); + + /* Read and verify the bucket tree header. */ + BucketTree::Header header; + std::memcpy(std::addressof(header), sparse_info.bucket.header, sizeof(header)); + R_TRY(header.Verify()); + + /* Determine the storage extents. */ + const auto fs_offset = GetFsOffset(*m_reader, index); + const auto fs_end_offset = GetFsEndOffset(*m_reader, index); + const auto fs_size = fs_end_offset - fs_offset; + + /* Create the sparse storage. */ + std::shared_ptr sparse_storage; + if (header.entry_count != 0) { + /* Create the body substorage. */ + std::shared_ptr body_substorage; + R_TRY(this->CreateBodySubStorage(std::addressof(body_substorage), sparse_info.physical_offset, sparse_info.GetPhysicalSize())); + + /* Create the meta storage. */ + std::shared_ptr meta_storage; + R_TRY(this->CreateSparseStorageMetaStorage(std::addressof(meta_storage), body_substorage, sparse_info.physical_offset, upper_iv, sparse_info)); + + /* Potentially set the output meta storage. */ + if (out_meta_storage != nullptr) { + *out_meta_storage = meta_storage; + } + + /* Create the sparse storage. */ + R_TRY(this->CreateSparseStorageCore(std::addressof(sparse_storage), body_substorage, sparse_info.GetPhysicalSize(), std::move(meta_storage), sparse_info, false)); + } else { + /* If there are no entries, there's nothing to actually do. */ + sparse_storage = fssystem::AllocateShared(); + R_UNLESS(sparse_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); + + sparse_storage->Initialize(fs_size); + } + + /* Potentially set the output sparse storage. */ + if (out_sparse_storage != nullptr) { + *out_sparse_storage = sparse_storage; + } + + /* Set the output fs data offset. */ + *out_fs_data_offset = fs_offset; + + /* Set the output storage. */ + *out = std::move(sparse_storage); + return ResultSuccess(); + } + + Result NcaFileSystemDriver::CreateAesCtrExMetaStorage(std::shared_ptr *out, std::shared_ptr base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, const NcaPatchInfo &patch_info) { + /* Validate preconditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(base_storage != nullptr); + AMS_ASSERT(patch_info.HasAesCtrExTable()); + + /* Validate patch info extents. */ + R_UNLESS(patch_info.indirect_size > 0, fs::ResultInvalidNcaPatchInfoIndirectSize()); + R_UNLESS(patch_info.aes_ctr_ex_size > 0, fs::ResultInvalidNcaPatchInfoAesCtrExSize()); + R_UNLESS(patch_info.indirect_size + patch_info.indirect_offset <= patch_info.aes_ctr_ex_offset, fs::ResultInvalidNcaPatchInfoAesCtrExOffset()); + + /* Get the base storage size. */ + s64 base_size; + R_TRY(base_storage->GetSize(std::addressof(base_size))); + + /* Get and validate the meta extents. */ + const s64 meta_offset = patch_info.aes_ctr_ex_offset; + const s64 meta_size = util::AlignUp(patch_info.aes_ctr_ex_size, NcaHeader::XtsBlockSize); + R_UNLESS(meta_offset + meta_size <= base_size, fs::ResultNcaBaseStorageOutOfRangeB()); + + /* Create the encrypted storage. */ + auto enc_storage = fssystem::AllocateShared(std::move(base_storage), meta_offset, meta_size); + R_UNLESS(enc_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); + + /* Create the decrypted storage. */ + std::shared_ptr decrypted_storage; + R_TRY(this->CreateAesCtrStorage(std::addressof(decrypted_storage), std::move(enc_storage), offset + meta_offset, upper_iv, AlignmentStorageRequirement_None)); + + /* Create meta storage. */ + auto meta_storage = fssystem::AllocateShared(); + R_UNLESS(meta_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); + + /* Initialize the meta storage. */ + R_TRY(meta_storage->Initialize(fs::SubStorage(std::move(decrypted_storage), 0, meta_size), m_buffer_manager, AesCtrExTableCacheBlockSize, AesCtrExTableCacheCount)); + + /* Create an alignment-matching storage. */ + using AlignedStorage = AlignmentMatchingStorage; + auto aligned_storage = fssystem::AllocateShared(std::move(meta_storage)); + R_UNLESS(aligned_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); + + /* Set the output. */ + *out = std::move(aligned_storage); + return ResultSuccess(); + } + + Result NcaFileSystemDriver::CreateAesCtrExStorage(std::shared_ptr *out, std::shared_ptr *out_ext, std::shared_ptr base_storage, std::shared_ptr meta_storage, s64 counter_offset, const NcaAesCtrUpperIv &upper_iv, const NcaPatchInfo &patch_info) { + /* Validate pre-conditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(base_storage != nullptr); + AMS_ASSERT(meta_storage != nullptr); + AMS_ASSERT(patch_info.HasAesCtrExTable()); /* Read the bucket tree header. */ BucketTree::Header header; std::memcpy(std::addressof(header), patch_info.aes_ctr_ex_header, sizeof(header)); R_TRY(header.Verify()); - /* Validate patch info extents. */ - R_UNLESS(patch_info.indirect_size > 0, fs::ResultInvalidNcaPatchInfoIndirectSize()); - R_UNLESS(patch_info.aes_ctr_ex_size > 0, fs::ResultInvalidNcaPatchInfoAesCtrExSize()); - - /* Make new base storage. */ - const auto base_storage_offset = base_storage->GetStorageOffset(); - const auto base_storage_size = util::AlignUp(patch_info.aes_ctr_ex_offset + patch_info.aes_ctr_ex_size, NcaHeader::XtsBlockSize); - fs::SubStorage new_base_storage; - R_TRY(base_storage->GetSubStorage(std::addressof(new_base_storage), 0, base_storage_size)); - - /* Create the table storage. */ - std::unique_ptr table_storage; - { - BaseStorage aes_ctr_base_storage(std::addressof(new_base_storage), patch_info.aes_ctr_ex_offset, patch_info.aes_ctr_ex_size); - aes_ctr_base_storage.SetStorageOffset(base_storage_offset + patch_info.aes_ctr_ex_offset); - aes_ctr_base_storage.SetAesCtrUpperIv(header_reader.GetAesCtrUpperIv()); - R_TRY(this->CreateAesCtrStorage(std::addressof(table_storage), std::addressof(aes_ctr_base_storage))); - } - - /* Get the table size. */ - s64 table_size = 0; - R_TRY(table_storage->GetSize(std::addressof(table_size))); - - /* Create the buffered storage. */ - std::unique_ptr buffered_storage = std::make_unique(); - R_UNLESS(buffered_storage != nullptr, fs::ResultAllocationFailureInNew()); - - /* Initialize the buffered storage. */ - R_TRY(buffered_storage->Initialize(fs::SubStorage(table_storage.get(), 0, table_size), m_buffer_manager, AesCtrExTableCacheBlockSize, AesCtrExTableCacheCount)); - - /* Create an aligned storage for the buffered storage. */ - using AlignedStorage = AlignmentMatchingStorage; - std::unique_ptr aligned_storage = std::make_unique(buffered_storage.get()); - R_UNLESS(aligned_storage != nullptr, fs::ResultAllocationFailureInNew()); - /* Determine the bucket extents. */ const auto entry_count = header.entry_count; const s64 data_offset = 0; @@ -733,105 +827,117 @@ namespace ams::fssystem { const s64 entry_size = AesCtrCounterExtendedStorage::QueryEntryStorageSize(entry_count); /* Create bucket storages. */ - fs::SubStorage data_storage(std::addressof(new_base_storage), data_offset, data_size); - fs::SubStorage node_storage(aligned_storage.get(), node_offset, node_size); - fs::SubStorage entry_storage(aligned_storage.get(), entry_offset, entry_size); + fs::SubStorage data_storage(std::move(base_storage), data_offset, data_size); + fs::SubStorage node_storage(meta_storage, node_offset, node_size); + fs::SubStorage entry_storage(meta_storage, entry_offset, entry_size); /* Get the secure value. */ - const auto secure_value = header_reader.GetAesCtrUpperIv().part.secure_value; + const auto secure_value = upper_iv.part.secure_value; /* Create the aes ctr ex storage. */ - std::unique_ptr aes_ctr_ex_storage; - const bool has_external_key = m_reader->HasExternalDecryptionKey(); - if (has_external_key) { + std::shared_ptr aes_ctr_ex_storage; + if (m_reader->HasExternalDecryptionKey()) { /* Create the decryptor. */ std::unique_ptr decryptor; R_TRY(AesCtrCounterExtendedStorage::CreateExternalDecryptor(std::addressof(decryptor), m_reader->GetExternalDecryptAesCtrFunctionForExternalKey(), -1)); /* Create the aes ctr ex storage. */ - std::unique_ptr impl_storage = std::make_unique(); - R_UNLESS(impl_storage != nullptr, fs::ResultAllocationFailureInNew()); + auto impl_storage = fssystem::AllocateShared(); + R_UNLESS(impl_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); /* Initialize the aes ctr ex storage. */ - R_TRY(impl_storage->Initialize(m_allocator, m_reader->GetExternalDecryptionKey(), AesCtrStorage::KeySize, secure_value, base_storage_offset, data_storage, node_storage, entry_storage, entry_count, std::move(decryptor))); + R_TRY(impl_storage->Initialize(m_allocator, m_reader->GetExternalDecryptionKey(), AesCtrStorageBySharedPointer::KeySize, secure_value, counter_offset, data_storage, node_storage, entry_storage, entry_count, std::move(decryptor))); - /* Set the option's aes ctr ex storage. */ - option->SetAesCtrExStorageRaw(impl_storage.get()); + /* Potentially set the output implementation storage. */ + if (out_ext != nullptr) { + *out_ext = impl_storage; + } + /* Set the implementation storage. */ aes_ctr_ex_storage = std::move(impl_storage); } else { - /* Check if we have a hardware key. */ - const bool has_hardware_key = m_reader->HasInternalDecryptionKeyForAesHardwareSpeedEmulation(); - /* Create the software decryptor. */ std::unique_ptr sw_decryptor; R_TRY(AesCtrCounterExtendedStorage::CreateSoftwareDecryptor(std::addressof(sw_decryptor))); /* Make the software storage. */ - std::unique_ptr sw_storage = std::make_unique(); - R_UNLESS(sw_storage != nullptr, fs::ResultAllocationFailureInNew()); + auto sw_storage = fssystem::AllocateShared(); + R_UNLESS(sw_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); /* Initialize the software storage. */ - R_TRY(sw_storage->Initialize(m_allocator, m_reader->GetDecryptionKey(NcaHeader::DecryptionKey_AesCtr), AesCtrStorage::KeySize, secure_value, base_storage_offset, data_storage, node_storage, entry_storage, entry_count, std::move(sw_decryptor))); + R_TRY(sw_storage->Initialize(m_allocator, m_reader->GetDecryptionKey(NcaHeader::DecryptionKey_AesCtr), AesCtrStorageBySharedPointer::KeySize, secure_value, counter_offset, data_storage, node_storage, entry_storage, entry_count, std::move(sw_decryptor))); - /* Set the option's aes ctr ex storage. */ - option->SetAesCtrExStorageRaw(sw_storage.get()); + /* Potentially set the output implementation storage. */ + if (out_ext != nullptr) { + *out_ext = sw_storage; + } /* If we have a hardware key and should use it, make the hardware decryption storage. */ - if (has_hardware_key && !m_reader->IsSoftwareAesPrioritized()) { + if (m_reader->HasInternalDecryptionKeyForAesHw() && !m_reader->IsSoftwareAesPrioritized()) { /* Create the hardware decryptor. */ std::unique_ptr hw_decryptor; R_TRY(AesCtrCounterExtendedStorage::CreateExternalDecryptor(std::addressof(hw_decryptor), m_reader->GetExternalDecryptAesCtrFunction(), GetKeyTypeValue(m_reader->GetKeyIndex(), m_reader->GetKeyGeneration()))); /* Create the hardware storage. */ - std::unique_ptr hw_storage = std::make_unique(); - R_UNLESS(hw_storage != nullptr, fs::ResultAllocationFailureInNew()); + auto hw_storage = fssystem::AllocateShared(); + R_UNLESS(hw_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); /* Initialize the hardware storage. */ - R_TRY(hw_storage->Initialize(m_allocator, m_reader->GetDecryptionKey(NcaHeader::DecryptionKey_AesCtrHw), AesCtrStorage::KeySize, secure_value, base_storage_offset, data_storage, node_storage, entry_storage, entry_count, std::move(hw_decryptor))); + R_TRY(hw_storage->Initialize(m_allocator, m_reader->GetDecryptionKey(NcaHeader::DecryptionKey_AesCtrHw), AesCtrStorageBySharedPointer::KeySize, secure_value, counter_offset, data_storage, node_storage, entry_storage, entry_count, std::move(hw_decryptor))); /* Create the selection storage. */ - std::unique_ptr switch_storage = std::make_unique>(std::move(hw_storage), std::move(sw_storage), IsUsingHardwareAesCtrForSpeedEmulation); - R_UNLESS(switch_storage != nullptr, fs::ResultAllocationFailureInNew()); + auto switch_storage = fssystem::AllocateShared>(std::move(hw_storage), std::move(sw_storage), IsUsingHwAesCtrForSpeedEmulation); + R_UNLESS(switch_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); - /* Set the aes ctr ex storage. */ + /* Set the implementation storage. */ aes_ctr_ex_storage = std::move(switch_storage); } else { - /* Set the aes ctr ex storage. */ + /* Set the implementation storage. */ aes_ctr_ex_storage = std::move(sw_storage); } } - /* Create the storage holder. */ - std::unique_ptr storage = std::make_unique>(aes_ctr_ex_storage.get(), m_reader); - R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInNew()); + /* Create an alignment-matching storage. */ + using AlignedStorage = AlignmentMatchingStorage; + auto aligned_storage = fssystem::AllocateShared(std::move(aes_ctr_ex_storage)); + R_UNLESS(aligned_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); - /* Set the aes ctr ex storages in the option. */ - option->SetAesCtrExTableStorage(table_storage.get()); - option->SetAesCtrExStorage(storage.get()); - - /* Set the storage holder's storages. */ - storage->Set(std::move(base_storage->GetStorage()), std::move(table_storage), std::move(buffered_storage), std::move(aligned_storage), std::move(aes_ctr_ex_storage)); - - /* Set the out storage. */ - *out = std::move(storage); + /* Set the output. */ + *out = std::move(aligned_storage); return ResultSuccess(); } - Result NcaFileSystemDriver::CreateIndirectStorage(std::unique_ptr *out, StorageOption *option, std::unique_ptr base_storage) { + Result NcaFileSystemDriver::CreateIndirectStorageMetaStorage(std::shared_ptr *out, std::shared_ptr base_storage, const NcaPatchInfo &patch_info) { /* Validate preconditions. */ AMS_ASSERT(out != nullptr); - AMS_ASSERT(option != nullptr); AMS_ASSERT(base_storage != nullptr); + AMS_ASSERT(patch_info.HasIndirectTable()); - /* Check if indirection is needed. */ - const auto &header_reader = option->GetHeaderReader(); - const auto &patch_info = header_reader.GetPatchInfo(); + /* Get the base storage size. */ + s64 base_size = 0; + R_TRY(base_storage->GetSize(std::addressof(base_size))); - if (!patch_info.HasIndirectTable()) { - *out = std::move(base_storage); - return ResultSuccess(); - } + /* Check that we're within range. */ + R_UNLESS(patch_info.indirect_offset + patch_info.indirect_size <= base_size, fs::ResultNcaBaseStorageOutOfRangeE()); + + /* Allocate the meta storage. */ + auto meta_storage = fssystem::AllocateShared(); + R_UNLESS(meta_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); + + /* Initialize the meta storage. */ + R_TRY(meta_storage->Initialize(fs::SubStorage(base_storage, patch_info.indirect_offset, patch_info.indirect_size), m_buffer_manager, IndirectTableCacheBlockSize, IndirectTableCacheCount)); + + /* Set the output. */ + *out = std::move(meta_storage); + return ResultSuccess(); + } + + Result NcaFileSystemDriver::CreateIndirectStorage(std::shared_ptr *out, std::shared_ptr *out_ind, std::shared_ptr base_storage, std::shared_ptr original_data_storage, std::shared_ptr meta_storage, const NcaPatchInfo &patch_info) { + /* Validate preconditions. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(base_storage != nullptr); + AMS_ASSERT(meta_storage != nullptr); + AMS_ASSERT(patch_info.HasIndirectTable()); /* Read the bucket tree header. */ BucketTree::Header header; @@ -843,106 +949,58 @@ namespace ams::fssystem { const auto entry_size = IndirectStorage::QueryEntryStorageSize(header.entry_count); R_UNLESS(node_size + entry_size <= patch_info.indirect_size, fs::ResultInvalidIndirectStorageSize()); - /* Open the original storage. */ - std::unique_ptr original_storage; - { - const s32 fs_index = header_reader.GetFsIndex(); - - if (m_original_reader != nullptr && m_original_reader->HasFsInfo(fs_index)) { - NcaFsHeaderReader original_header_reader; - R_TRY(original_header_reader.Initialize(*m_original_reader, fs_index)); - - NcaFileSystemDriver original_driver(m_original_reader, m_allocator, m_buffer_manager); - StorageOption original_option(std::addressof(original_header_reader), fs_index); - - BaseStorage original_base_storage; - R_TRY(original_driver.CreateBaseStorage(std::addressof(original_base_storage), std::addressof(original_option))); - R_TRY(original_driver.CreateDecryptableStorage(std::addressof(original_storage), std::addressof(original_option), std::addressof(original_base_storage))); - } else { - original_storage = std::make_unique(nullptr, 0); - R_UNLESS(original_storage != nullptr, fs::ResultAllocationFailureInNew()); - } - } - - /* Get the original data size. */ - s64 original_data_size = 0; - R_TRY(original_storage->GetSize(std::addressof(original_data_size))); - /* Get the indirect data size. */ - s64 indirect_data_size = patch_info.indirect_offset; + const s64 indirect_data_size = patch_info.indirect_offset; AMS_ASSERT(util::IsAligned(indirect_data_size, NcaHeader::XtsBlockSize)); - /* Create the indirect table storage. */ - std::unique_ptr indirect_table_storage = std::make_unique(); - R_UNLESS(indirect_table_storage != nullptr, fs::ResultAllocationFailureInNew()); - - /* Initialize the indirect table storage. */ - R_TRY(indirect_table_storage->Initialize(fs::SubStorage(base_storage.get(), indirect_data_size, node_size + entry_size), m_buffer_manager, IndirectTableCacheBlockSize, IndirectTableCacheCount)); - /* Create the indirect data storage. */ - std::unique_ptr indirect_data_storage = std::make_unique(); - R_UNLESS(indirect_data_storage != nullptr, fs::ResultAllocationFailureInNew()); + auto indirect_data_storage = fssystem::AllocateShared(); + R_UNLESS(indirect_data_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); /* Initialize the indirect data storage. */ - R_TRY(indirect_data_storage->Initialize(fs::SubStorage(base_storage.get(), 0, indirect_data_size), m_buffer_manager, IndirectDataCacheBlockSize, IndirectDataCacheCount)); + R_TRY(indirect_data_storage->Initialize(fs::SubStorage(base_storage, 0, indirect_data_size), m_buffer_manager, IndirectDataCacheBlockSize, IndirectDataCacheCount)); - /* Create the storage holder. */ - std::unique_ptr storage = std::make_unique>(m_reader); - R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInNew()); + /* Enable bulk read on the data storage. */ + indirect_data_storage->EnableBulkRead(); - /* Initialize the storage holder. */ - R_TRY(storage->Initialize(m_allocator, fs::SubStorage(indirect_table_storage.get(), 0, node_size), fs::SubStorage(indirect_table_storage.get(), node_size, entry_size), header.entry_count)); + /* Create the indirect storage. */ + auto indirect_storage = fssystem::AllocateShared(); + R_UNLESS(indirect_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); - /* Set the storage holder's storages. */ - storage->SetStorage(0, original_storage.get(), 0, original_data_size); - storage->SetStorage(1, indirect_data_storage.get(), 0, indirect_data_size); - storage->Set(std::move(base_storage), std::move(original_storage), std::move(indirect_table_storage), std::move(indirect_data_storage)); + /* Initialize the indirect storage. */ + R_TRY(indirect_storage->Initialize(m_allocator, fs::SubStorage(meta_storage, 0, node_size), fs::SubStorage(meta_storage, node_size, entry_size), header.entry_count)); - /* Set the indirect storage. */ - option->SetIndirectStorage(storage.get()); + /* Get the original data size. */ + s64 original_data_size; + R_TRY(original_data_storage->GetSize(std::addressof(original_data_size))); - /* Set the out storage. */ - *out = std::move(storage); - return ResultSuccess(); - } + /* Set the indirect storages. */ + indirect_storage->SetStorage(0, fs::SubStorage(original_data_storage, 0, original_data_size)); + indirect_storage->SetStorage(1, fs::SubStorage(indirect_data_storage, 0, indirect_data_size)); - Result NcaFileSystemDriver::CreateVerificationStorage(std::unique_ptr *out, std::unique_ptr base_storage, NcaFsHeaderReader *header_reader) { - /* Validate preconditions. */ - AMS_ASSERT(out != nullptr); - AMS_ASSERT(base_storage != nullptr); - AMS_ASSERT(header_reader != nullptr); - - /* Create the appropriate storage for the encryption type. */ - switch (header_reader->GetHashType()) { - case NcaFsHeader::HashType::HierarchicalSha256Hash: - R_TRY(this->CreateSha256Storage(out, std::move(base_storage), header_reader)); - break; - case NcaFsHeader::HashType::HierarchicalIntegrityHash: - R_TRY(this->CreateIntegrityVerificationStorage(out, std::move(base_storage), header_reader)); - break; - default: - return fs::ResultInvalidNcaFsHeaderHashType(); + /* If necessary, set the output indirect storage. */ + if (out_ind != nullptr) { + *out_ind = indirect_storage; } + /* Set the output. */ + *out = std::move(indirect_storage); return ResultSuccess(); } - Result NcaFileSystemDriver::CreateSha256Storage(std::unique_ptr *out, std::unique_ptr base_storage, NcaFsHeaderReader *header_reader) { + Result NcaFileSystemDriver::CreateSha256Storage(std::shared_ptr *out, std::shared_ptr base_storage, const NcaFsHeader::HashData::HierarchicalSha256Data &hash_data) { /* Validate preconditions. */ AMS_ASSERT(out != nullptr); AMS_ASSERT(base_storage != nullptr); - AMS_ASSERT(header_reader != nullptr); /* Define storage types. */ - using VerificationStorage = HierarchicalSha256Storage; + using VerificationStorage = HierarchicalSha256Storage; using CacheStorage = ReadOnlyBlockCacheStorage; - using AlignedStorage = AlignmentMatchingStoragePooledBuffer<1>; - using StorageHolder = DerivedStorageHolderWithBuffer; + using AlignedStorage = AlignmentMatchingStoragePooledBuffer, 1>; - /* Get and validate the hash data. */ - auto &hash_data = header_reader->GetHashData().hierarchical_sha256_data; - R_UNLESS(util::IsPowerOfTwo(hash_data.hash_block_size), fs::ResultInvalidHierarchicalSha256BlockSize()); - R_UNLESS(hash_data.hash_layer_count == HierarchicalSha256Storage::LayerCount - 1, fs::ResultInvalidHierarchicalSha256LayerCount()); + /* Validate the hash data. */ + R_UNLESS(util::IsPowerOfTwo(hash_data.hash_block_size), fs::ResultInvalidHierarchicalSha256BlockSize()); + R_UNLESS(hash_data.hash_layer_count == VerificationStorage::LayerCount - 1, fs::ResultInvalidHierarchicalSha256LayerCount()); /* Get the regions. */ const auto &hash_region = hash_data.hash_layer_region[0]; @@ -954,106 +1012,139 @@ namespace ams::fssystem { const auto cache_buffer_size = CacheBlockCount * hash_data.hash_block_size; const auto total_buffer_size = hash_buffer_size + cache_buffer_size; - /* Make a buffer holder. */ - BufferHolder buffer_holder(m_allocator, total_buffer_size); - R_UNLESS(buffer_holder.IsValid(), fs::ResultAllocationFailureInNcaFileSystemDriverI()); + /* Make a buffer holder storage. */ + auto buffer_hold_storage = fssystem::AllocateShared(std::move(base_storage), m_allocator, total_buffer_size); + R_UNLESS(buffer_hold_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); + R_UNLESS(buffer_hold_storage->IsValid(), fs::ResultAllocationFailureInNcaFileSystemDriverI()); - /* Make the data storage. */ - std::unique_ptr data_storage = std::make_unique(base_storage.get(), data_region.offset, data_region.size); - R_UNLESS(data_storage != nullptr, fs::ResultAllocationFailureInNew()); + /* Get storage size. */ + s64 base_size; + R_TRY(buffer_hold_storage->GetSize(std::addressof(base_size))); + + /* Check that we're within range. */ + R_UNLESS(data_region.offset + data_region.size <= base_size, fs::ResultNcaBaseStorageOutOfRangeC()); + R_UNLESS(data_region.offset + data_region.size <= base_size, fs::ResultNcaBaseStorageOutOfRangeC()); + + /* Create the master hash storage. */ + fs::MemoryStorage master_hash_storage(const_cast(std::addressof(hash_data.fs_data_master_hash)), sizeof(Hash)); /* Make the verification storage. */ - std::unique_ptr verification_storage = std::make_unique(); - R_UNLESS(verification_storage != nullptr, fs::ResultAllocationFailureInNew()); + auto verification_storage = fssystem::AllocateShared(); + R_UNLESS(verification_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); /* Make layer storages. */ - fs::MemoryStorage master_hash_storage(std::addressof(hash_data.fs_data_master_hash), sizeof(Hash)); - fs::SubStorage layer_hash_storage(base_storage.get(), hash_region.offset, hash_region.size); - fs::IStorage *storages[VerificationStorage::LayerCount] = { - std::addressof(master_hash_storage), - std::addressof(layer_hash_storage), - data_storage.get() + fs::SubStorage layer_storages[VerificationStorage::LayerCount] = { + fs::SubStorage(std::addressof(master_hash_storage), 0, sizeof(Hash)), + fs::SubStorage(buffer_hold_storage.get(), hash_region.offset, hash_region.size), + fs::SubStorage(buffer_hold_storage, data_region.offset, data_region.size) }; /* Initialize the verification storage. */ - R_TRY(verification_storage->Initialize(storages, VerificationStorage::LayerCount, hash_data.hash_block_size, buffer_holder.Get(), hash_buffer_size)); + R_TRY(verification_storage->Initialize(layer_storages, util::size(layer_storages), hash_data.hash_block_size, buffer_hold_storage->GetBuffer(), hash_buffer_size, m_hash_generator_factory_selector->GetFactory())); /* Make the cache storage. */ - std::unique_ptr cache_storage = std::make_unique(verification_storage.get(), hash_data.hash_block_size, buffer_holder.Get() + hash_buffer_size, cache_buffer_size, CacheBlockCount); - R_UNLESS(cache_storage != nullptr, fs::ResultAllocationFailureInNew()); + auto cache_storage = fssystem::AllocateShared(std::move(verification_storage), hash_data.hash_block_size, static_cast(buffer_hold_storage->GetBuffer()) + hash_buffer_size, cache_buffer_size, CacheBlockCount); + R_UNLESS(cache_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); - /* Make the storage holder. */ - std::unique_ptr storage = std::make_unique(cache_storage.get(), hash_data.hash_block_size, m_reader); - R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInNew()); - - /* Set the storage holder's data. */ - storage->Set(std::move(base_storage), std::move(data_storage), std::move(verification_storage), std::move(cache_storage)); - storage->Set(std::move(buffer_holder)); + /* Make the aligned storage. */ + auto aligned_storage = fssystem::AllocateShared(std::move(cache_storage), hash_data.hash_block_size); + R_UNLESS(aligned_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); /* Set the output. */ - *out = std::move(storage); + *out = std::move(aligned_storage); return ResultSuccess(); } - Result NcaFileSystemDriver::CreateIntegrityVerificationStorage(std::unique_ptr *out, std::unique_ptr base_storage, NcaFsHeaderReader *header_reader) { + Result NcaFileSystemDriver::CreateIntegrityVerificationStorage(std::shared_ptr *out, std::shared_ptr base_storage, const NcaFsHeader::HashData::IntegrityMetaInfo &meta_info) { /* Validate preconditions. */ AMS_ASSERT(out != nullptr); AMS_ASSERT(base_storage != nullptr); - AMS_ASSERT(header_reader != nullptr); /* Define storage types. */ using VerificationStorage = save::HierarchicalIntegrityVerificationStorage; using StorageInfo = VerificationStorage::HierarchicalStorageInformation; - using StorageHolder = DerivedStorageHolder; - /* Get and validate the hash data. */ - auto &hash_data = header_reader->GetHashData().integrity_meta_info; + /* Validate the meta info. */ save::HierarchicalIntegrityVerificationInformation level_hash_info; - std::memcpy(std::addressof(level_hash_info), std::addressof(hash_data.level_hash_info), sizeof(level_hash_info)); + std::memcpy(std::addressof(level_hash_info), std::addressof(meta_info.level_hash_info), sizeof(level_hash_info)); R_UNLESS(save::IntegrityMinLayerCount <= level_hash_info.max_layers, fs::ResultInvalidHierarchicalIntegrityVerificationLayerCount()); R_UNLESS(level_hash_info.max_layers <= save::IntegrityMaxLayerCount, fs::ResultInvalidHierarchicalIntegrityVerificationLayerCount()); + /* Get the base storage size. */ + s64 base_storage_size; + R_TRY(base_storage->GetSize(std::addressof(base_storage_size))); + /* Create storage info. */ StorageInfo storage_info; for (s32 i = 0; i < static_cast(level_hash_info.max_layers - 2); ++i) { const auto &layer_info = level_hash_info.info[i]; - storage_info[i + 1] = fs::SubStorage(base_storage.get(), layer_info.offset, layer_info.size); + R_UNLESS(layer_info.offset + layer_info.size <= base_storage_size, fs::ResultNcaBaseStorageOutOfRangeD()); + + storage_info[i + 1] = fs::SubStorage(base_storage, layer_info.offset, layer_info.size); } /* Set the last layer info. */ const auto &layer_info = level_hash_info.info[level_hash_info.max_layers - 2]; - storage_info.SetDataStorage(fs::SubStorage(base_storage.get(), layer_info.offset, layer_info.size)); + R_UNLESS(layer_info.offset + layer_info.size <= base_storage_size, fs::ResultNcaBaseStorageOutOfRangeD()); + storage_info.SetDataStorage(fs::SubStorage(std::move(base_storage), layer_info.offset, layer_info.size)); - /* Make the storage holder. */ - std::unique_ptr storage = std::make_unique(m_reader); - R_UNLESS(storage != nullptr, fs::ResultAllocationFailureInNew()); + /* Make the integrity romfs storage. */ + auto integrity_storage = fssystem::AllocateShared(); + R_UNLESS(integrity_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); /* Initialize the integrity storage. */ - R_TRY(storage->Initialize(level_hash_info, hash_data.master_hash, storage_info, m_buffer_manager)); - - /* Set the storage holder's data. */ - storage->Set(std::move(base_storage)); + R_TRY(integrity_storage->Initialize(level_hash_info, meta_info.master_hash, storage_info, m_buffer_manager, m_hash_generator_factory_selector->GetFactory())); /* Set the output. */ - *out = std::move(storage); + *out = std::move(integrity_storage); return ResultSuccess(); } - Result NcaFileSystemDriver::SetupFsHeaderReader(NcaFsHeaderReader *out, const NcaReader &reader, s32 fs_index) { - /* Validate preconditions. */ + Result NcaFileSystemDriver::CreateCompressedStorage(std::shared_ptr *out, std::shared_ptr *out_cmp, std::shared_ptr *out_meta, std::shared_ptr base_storage, const NcaCompressionInfo &compression_info) { + return this->CreateCompressedStorage(out, out_cmp, out_meta, std::move(base_storage), compression_info, m_reader->GetDecompressor(), m_allocator, m_buffer_manager); + } + + Result NcaFileSystemDriver::CreateCompressedStorage(std::shared_ptr *out, std::shared_ptr *out_cmp, std::shared_ptr *out_meta, std::shared_ptr base_storage, const NcaCompressionInfo &compression_info, GetDecompressorFunction get_decompressor, MemoryResource *allocator, IBufferManager *buffer_manager) { + /* Check pre-conditions. */ AMS_ASSERT(out != nullptr); - AMS_ASSERT(0 <= fs_index && fs_index < NcaHeader::FsCountMax); + AMS_ASSERT(base_storage != nullptr); + AMS_ASSERT(get_decompressor != nullptr); - /* Validate magic. */ - R_UNLESS(reader.GetMagic() == NcaHeader::Magic, fs::ResultUnsupportedVersion()); + /* Read and verify the bucket tree header. */ + BucketTree::Header header; + std::memcpy(std::addressof(header), compression_info.bucket.header, sizeof(header)); + R_TRY(header.Verify()); - /* Check that the fs header exists. */ - R_UNLESS(reader.HasFsInfo(fs_index), fs::ResultPartitionNotFound()); + /* Determine the storage extents. */ + const auto table_offset = compression_info.bucket.offset; + const auto table_size = compression_info.bucket.size; + const auto node_size = CompressedStorage::QueryNodeStorageSize(header.entry_count); + const auto entry_size = CompressedStorage::QueryEntryStorageSize(header.entry_count); + R_UNLESS(node_size + entry_size <= table_size, fs::ResultInvalidCompressedStorageSize()); - /* Initialize the reader. */ - R_TRY(out->Initialize(reader, fs_index)); + /* If we should, set the output meta storage. */ + if (out_meta != nullptr) { + auto meta_storage = fssystem::AllocateShared(base_storage, table_offset, table_size); + R_UNLESS(meta_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); + *out_meta = std::move(meta_storage); + } + + /* Allocate the compressed storage. */ + auto compressed_storage = fssystem::AllocateShared(); + R_UNLESS(compressed_storage != nullptr, fs::ResultAllocationFailureInAllocateShared()); + + /* Initialize the compressed storage. */ + R_TRY(compressed_storage->Initialize(allocator, buffer_manager, fs::SubStorage(base_storage, 0, table_offset), fs::SubStorage(base_storage, table_offset, node_size), fs::SubStorage(base_storage, table_offset + node_size, entry_size), header.entry_count, 64_KB, 640_KB, get_decompressor, 16_KB, 16_KB, 32)); + + /* Potentially set the output compressed storage. */ + if (out_cmp) { + *out_cmp = compressed_storage; + } + + /* Set the output. */ + *out = std::move(compressed_storage); return ResultSuccess(); } diff --git a/libraries/libstratosphere/source/fssystem/fssystem_nca_header.cpp b/libraries/libstratosphere/source/fssystem/fssystem_nca_header.cpp index f82012ec6..3437fda39 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_nca_header.cpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_nca_header.cpp @@ -20,6 +20,7 @@ namespace ams::fssystem { u8 NcaHeader::GetProperKeyGeneration() const { return std::max(this->key_generation, this->key_generation_2); } + bool NcaPatchInfo::HasIndirectTable() const { return this->indirect_size != 0; } diff --git a/libraries/libstratosphere/source/fssystem/fssystem_nca_reader.cpp b/libraries/libstratosphere/source/fssystem/fssystem_nca_reader.cpp index ea1aab1d0..d1d1e440c 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_nca_reader.cpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_nca_reader.cpp @@ -35,7 +35,7 @@ namespace ams::fssystem { } - NcaReader::NcaReader() : m_shared_base_storage(), m_header_storage(), m_body_storage(), m_decrypt_aes_ctr(), m_decrypt_aes_ctr_external(), m_is_software_aes_prioritized(false), m_header_encryption_type(NcaHeader::EncryptionType::Auto) { + NcaReader::NcaReader() : m_body_storage(), m_header_storage(), m_decrypt_aes_ctr(), m_decrypt_aes_ctr_external(), m_is_software_aes_prioritized(false), m_header_encryption_type(NcaHeader::EncryptionType::Auto), m_get_decompressor(), m_hash_generator_factory() { std::memset(std::addressof(m_header), 0, sizeof(m_header)); std::memset(std::addressof(m_decryption_keys), 0, sizeof(m_decryption_keys)); std::memset(std::addressof(m_external_decryption_key), 0, sizeof(m_external_decryption_key)); @@ -45,33 +45,33 @@ namespace ams::fssystem { /* ... */ } - Result NcaReader::Initialize(std::shared_ptr base_storage, const NcaCryptoConfiguration &crypto_cfg) { - m_shared_base_storage = base_storage; - return this->Initialize(m_shared_base_storage.get(), crypto_cfg); - } - - Result NcaReader::Initialize(fs::IStorage *base_storage, const NcaCryptoConfiguration &crypto_cfg) { + Result NcaReader::Initialize(std::shared_ptr base_storage, const NcaCryptoConfiguration &crypto_cfg, const NcaCompressionConfiguration &compression_cfg, IHash256GeneratorFactorySelector *hgf_selector) { /* Validate preconditions. */ AMS_ASSERT(base_storage != nullptr); + AMS_ASSERT(hgf_selector != nullptr); AMS_ASSERT(m_body_storage == nullptr); + + /* Check that the crypto config is valid. */ R_UNLESS(crypto_cfg.generate_key != nullptr, fs::ResultInvalidArgument()); /* Generate keys for header. */ + using AesXtsStorageForNcaHeader = AesXtsStorageBySharedPointer; + u8 header_decryption_keys[NcaCryptoConfiguration::HeaderEncryptionKeyCount][NcaCryptoConfiguration::Aes128KeySize]; for (size_t i = 0; i < NcaCryptoConfiguration::HeaderEncryptionKeyCount; i++) { - crypto_cfg.generate_key(header_decryption_keys[i], AesXtsStorage::KeySize, crypto_cfg.header_encrypted_encryption_keys[i], AesXtsStorage::KeySize, static_cast(KeyType::NcaHeaderKey), crypto_cfg); + crypto_cfg.generate_key(header_decryption_keys[i], AesXtsStorageForNcaHeader::KeySize, crypto_cfg.header_encrypted_encryption_keys[i], AesXtsStorageForNcaHeader::KeySize, static_cast(KeyType::NcaHeaderKey), crypto_cfg); } /* Create the header storage. */ - const u8 header_iv[AesXtsStorage::IvSize] = {}; - std::unique_ptr work_header_storage = std::make_unique(base_storage, header_decryption_keys[0], header_decryption_keys[1], AesXtsStorage::KeySize, header_iv, AesXtsStorage::IvSize, NcaHeader::XtsBlockSize); + const u8 header_iv[AesXtsStorageForNcaHeader::IvSize] = {}; + std::unique_ptr work_header_storage = std::make_unique(base_storage, header_decryption_keys[0], header_decryption_keys[1], AesXtsStorageForNcaHeader::KeySize, header_iv, AesXtsStorageForNcaHeader::IvSize, NcaHeader::XtsBlockSize); R_UNLESS(work_header_storage != nullptr, fs::ResultAllocationFailureInNcaReaderA()); /* Read the header. */ R_TRY(work_header_storage->Read(0, std::addressof(m_header), sizeof(m_header))); /* Validate the magic. */ - if (Result magic_result = CheckNcaMagic(m_header.magic); R_FAILED(magic_result)) { + if (const Result magic_result = CheckNcaMagic(m_header.magic); R_FAILED(magic_result)) { /* If we're not allowed to use plaintext headers, stop here. */ R_UNLESS(crypto_cfg.is_plaintext_header_available, magic_result); @@ -85,6 +85,7 @@ namespace ams::fssystem { work_header_storage.reset(new fs::SubStorage(base_storage, 0, base_storage_size)); R_UNLESS(work_header_storage != nullptr, fs::ResultAllocationFailureInNcaReaderA()); + /* Set encryption type as plaintext. */ m_header_encryption_type = NcaHeader::EncryptionType::None; } @@ -101,6 +102,7 @@ namespace ams::fssystem { const size_t exp_size = NcaCryptoConfiguration::Rsa2048KeyPublicExponentSize; const u8 *msg = static_cast(static_cast(std::addressof(m_header.magic))); const size_t msg_size = NcaHeader::Size - NcaHeader::HeaderSignSize * NcaHeader::HeaderSignCount; + const bool is_signature_valid = crypto::VerifyRsa2048PssSha256(sig, sig_size, mod, mod_size, exp, exp_size, msg, msg_size); R_UNLESS(is_signature_valid, fs::ResultNcaHeaderSignature1VerificationFailed()); } @@ -128,14 +130,22 @@ namespace ams::fssystem { m_decrypt_aes_ctr = crypto_cfg.decrypt_aes_ctr; m_decrypt_aes_ctr_external = crypto_cfg.decrypt_aes_ctr_external; + /* Set our decompressor function getter. */ + m_get_decompressor = compression_cfg.get_decompressor; + + /* Set our hash generator factory. */ + m_hash_generator_factory = hgf_selector->GetFactory(); + AMS_ASSERT(m_hash_generator_factory != nullptr); + /* Set our storages. */ m_header_storage = std::move(work_header_storage); - m_body_storage = base_storage; + m_body_storage = std::move(base_storage); return ResultSuccess(); } - fs::IStorage *NcaReader::GetBodyStorage() { + std::shared_ptr NcaReader::GetSharedBodyStorage() { + AMS_ASSERT(m_body_storage != nullptr); return m_body_storage; } @@ -270,7 +280,7 @@ namespace ams::fssystem { return false; } - bool NcaReader::HasInternalDecryptionKeyForAesHardwareSpeedEmulation() const { + bool NcaReader::HasInternalDecryptionKeyForAesHw() const { constexpr const u8 ZeroKey[crypto::AesDecryptor128::KeySize] = {}; return !crypto::IsSameBytes(ZeroKey, this->GetDecryptionKey(NcaHeader::DecryptionKey_AesCtrHw), crypto::AesDecryptor128::KeySize); } @@ -319,6 +329,16 @@ namespace ams::fssystem { return m_decrypt_aes_ctr_external; } + GetDecompressorFunction NcaReader::GetDecompressor() const { + AMS_ASSERT(m_get_decompressor != nullptr); + return m_get_decompressor; + } + + IHash256GeneratorFactory *NcaReader::GetHashGeneratorFactory() const { + AMS_ASSERT(m_hash_generator_factory != nullptr); + return m_hash_generator_factory; + } + NcaHeader::EncryptionType NcaReader::GetEncryptionType() const { return m_header_encryption_type; } @@ -331,20 +351,19 @@ namespace ams::fssystem { return m_header_storage->Read(offset, dst, sizeof(NcaFsHeader)); } - Result NcaReader::VerifyHeaderSign2(const void *mod, size_t mod_size) { - AMS_ASSERT(m_body_storage != nullptr); - constexpr const u8 HeaderSign2KeyPublicExponent[] = { 0x01, 0x00, 0x01 }; + void NcaReader::GetHeaderSign2(void *dst, size_t size) { + AMS_ASSERT(dst != nullptr); + AMS_ASSERT(size == NcaHeader::HeaderSignSize); - const u8 *sig = m_header.header_sign_2; - const size_t sig_size = NcaHeader::HeaderSignSize; - const u8 *exp = HeaderSign2KeyPublicExponent; - const size_t exp_size = sizeof(HeaderSign2KeyPublicExponent); - const u8 *msg = static_cast(static_cast(std::addressof(m_header.magic))); - const size_t msg_size = NcaHeader::Size - NcaHeader::HeaderSignSize * NcaHeader::HeaderSignCount; - const bool is_signature_valid = crypto::VerifyRsa2048PssSha256(sig, sig_size, mod, mod_size, exp, exp_size, msg, msg_size); - R_UNLESS(is_signature_valid, fs::ResultNcaHeaderSignature2VerificationFailed()); + std::memcpy(dst, m_header.header_sign_2, size); + } - return ResultSuccess(); + void NcaReader::GetHeaderSign2TargetHash(void *dst, size_t size) { + AMS_ASSERT(m_hash_generator_factory != nullptr); + AMS_ASSERT(dst != nullptr); + AMS_ASSERT(size == IHash256Generator::HashSize); + + return m_hash_generator_factory->GenerateHash(dst, size, static_cast(std::addressof(m_header.magic)), NcaHeader::Size - NcaHeader::HeaderSignSize * NcaHeader::HeaderSignCount); } Result NcaFsHeaderReader::Initialize(const NcaReader &reader, s32 index) { @@ -440,4 +459,19 @@ namespace ams::fssystem { return m_data.sparse_info; } + bool NcaFsHeaderReader::ExistsCompressionLayer() const { + AMS_ASSERT(this->IsInitialized()); + return m_data.compression_info.bucket.offset != 0 && m_data.compression_info.bucket.size != 0; + } + + NcaCompressionInfo &NcaFsHeaderReader::GetCompressionInfo() { + AMS_ASSERT(this->IsInitialized()); + return m_data.compression_info; + } + + const NcaCompressionInfo &NcaFsHeaderReader::GetCompressionInfo() const { + AMS_ASSERT(this->IsInitialized()); + return m_data.compression_info; + } + } diff --git a/libraries/libstratosphere/source/fssystem/fssystem_read_only_block_cache_storage.hpp b/libraries/libstratosphere/source/fssystem/fssystem_read_only_block_cache_storage.hpp index 5f5f12e27..08d796b23 100644 --- a/libraries/libstratosphere/source/fssystem/fssystem_read_only_block_cache_storage.hpp +++ b/libraries/libstratosphere/source/fssystem/fssystem_read_only_block_cache_storage.hpp @@ -27,10 +27,10 @@ namespace ams::fssystem { private: os::SdkMutex m_mutex; BlockCache m_block_cache; - fs::IStorage * const m_base_storage; + std::shared_ptr m_base_storage; s32 m_block_size; public: - ReadOnlyBlockCacheStorage(IStorage *bs, s32 bsz, char *buf, size_t buf_size, s32 cache_block_count) : m_mutex(), m_block_cache(), m_base_storage(bs), m_block_size(bsz) { + ReadOnlyBlockCacheStorage(std::shared_ptr bs, s32 bsz, char *buf, size_t buf_size, s32 cache_block_count) : m_mutex(), m_block_cache(), m_base_storage(std::move(bs)), m_block_size(bsz) { /* Validate preconditions. */ AMS_ASSERT(buf_size >= static_cast(m_block_size)); AMS_ASSERT(util::IsPowerOfTwo(m_block_size)); @@ -88,32 +88,21 @@ namespace ams::fssystem { } } virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override { - /* Validate preconditions. */ - AMS_ASSERT(util::IsAligned(offset, m_block_size)); - AMS_ASSERT(util::IsAligned(size, m_block_size)); - /* If invalidating cache, invalidate our blocks. */ if (op_id == fs::OperationId::Invalidate) { - R_UNLESS(offset >= 0, fs::ResultInvalidOffset()); - std::scoped_lock lk(m_mutex); const size_t cache_block_count = m_block_cache.GetSize(); - BlockCache valid_cache; - - for (size_t count = 0; count < cache_block_count; ++count) { + for (size_t i = 0; i < cache_block_count; ++i) { auto lru = m_block_cache.PopLruNode(); - if (offset <= lru->m_key && lru->m_key < offset + size) { - m_block_cache.PushMruNode(std::move(lru), -1); - } else { - valid_cache.PushMruNode(std::move(lru), lru->m_key); - } + m_block_cache.PushMruNode(std::move(lru), -1); } - while (!valid_cache.IsEmpty()) { - auto lru = valid_cache.PopLruNode(); - m_block_cache.PushMruNode(std::move(lru), lru->m_key); - } + return ResultSuccess(); + } else { + /* Validate preconditions. */ + AMS_ASSERT(util::IsAligned(offset, m_block_size)); + AMS_ASSERT(util::IsAligned(size, m_block_size)); } /* Operate on the base storage. */ diff --git a/libraries/libstratosphere/source/fssystem/save/fssystem_hierarchical_integrity_verification_storage.cpp b/libraries/libstratosphere/source/fssystem/save/fssystem_hierarchical_integrity_verification_storage.cpp index a16a99a54..60c4c80fb 100644 --- a/libraries/libstratosphere/source/fssystem/save/fssystem_hierarchical_integrity_verification_storage.cpp +++ b/libraries/libstratosphere/source/fssystem/save/fssystem_hierarchical_integrity_verification_storage.cpp @@ -150,7 +150,7 @@ namespace ams::fssystem::save { m_storage = fs::SubStorage(); } - Result HierarchicalIntegrityVerificationStorage::Initialize(const HierarchicalIntegrityVerificationInformation &info, HierarchicalStorageInformation storage, FileSystemBufferManagerSet *bufs, os::SdkRecursiveMutex *mtx, fs::StorageType storage_type) { + Result HierarchicalIntegrityVerificationStorage::Initialize(const HierarchicalIntegrityVerificationInformation &info, HierarchicalStorageInformation storage, FileSystemBufferManagerSet *bufs, IHash256GeneratorFactory *hgf, os::SdkRecursiveMutex *mtx, fs::StorageType storage_type) { /* Validate preconditions. */ AMS_ASSERT(bufs != nullptr); AMS_ASSERT(IntegrityMinLayerCount <= info.max_layers && info.max_layers <= IntegrityMaxLayerCount); @@ -168,7 +168,7 @@ namespace ams::fssystem::save { { fs::HashSalt mac; crypto::GenerateHmacSha256Mac(mac.value, sizeof(mac), info.seed.value, sizeof(info.seed), KeyArray[0].key, KeyArray[0].size); - m_verify_storages[0].Initialize(storage[HierarchicalStorageInformation::MasterStorage], storage[HierarchicalStorageInformation::Layer1Storage], static_cast(1) << info.info[0].block_order, HashSize, m_buffers->buffers[m_max_layers - 2], mac, false, storage_type); + m_verify_storages[0].Initialize(storage[HierarchicalStorageInformation::MasterStorage], storage[HierarchicalStorageInformation::Layer1Storage], static_cast(1) << info.info[0].block_order, HashSize, m_buffers->buffers[m_max_layers - 2], hgf, mac, false, storage_type); } /* Ensure we don't leak state if further initialization goes wrong. */ @@ -203,7 +203,7 @@ namespace ams::fssystem::save { fs::SubStorage buffer_storage(std::addressof(m_buffer_storages[level]), 0, info.info[level].size); fs::HashSalt mac; crypto::GenerateHmacSha256Mac(mac.value, sizeof(mac), info.seed.value, sizeof(info.seed), KeyArray[level + 1].key, KeyArray[level + 1].size); - m_verify_storages[level + 1].Initialize(buffer_storage, storage[level + 2], static_cast(1) << info.info[level + 1].block_order, static_cast(1) << info.info[level].block_order, m_buffers->buffers[m_max_layers - 2], mac, false, storage_type); + m_verify_storages[level + 1].Initialize(buffer_storage, storage[level + 2], static_cast(1) << info.info[level + 1].block_order, static_cast(1) << info.info[level].block_order, m_buffers->buffers[m_max_layers - 2], hgf, mac, false, storage_type); } /* Initialize the buffer storage. */ @@ -217,7 +217,7 @@ namespace ams::fssystem::save { fs::SubStorage buffer_storage(std::addressof(m_buffer_storages[level]), 0, info.info[level].size); fs::HashSalt mac; crypto::GenerateHmacSha256Mac(mac.value, sizeof(mac), info.seed.value, sizeof(info.seed), KeyArray[level + 1].key, KeyArray[level + 1].size); - m_verify_storages[level + 1].Initialize(buffer_storage, storage[level + 2], static_cast(1) << info.info[level + 1].block_order, static_cast(1) << info.info[level].block_order, m_buffers->buffers[m_max_layers - 2], mac, true, storage_type); + m_verify_storages[level + 1].Initialize(buffer_storage, storage[level + 2], static_cast(1) << info.info[level + 1].block_order, static_cast(1) << info.info[level].block_order, m_buffers->buffers[m_max_layers - 2], hgf, mac, true, storage_type); } /* Initialize the buffer storage. */ diff --git a/libraries/libstratosphere/source/fssystem/save/fssystem_integrity_verification_storage.cpp b/libraries/libstratosphere/source/fssystem/save/fssystem_integrity_verification_storage.cpp index a261369d5..47d29448a 100644 --- a/libraries/libstratosphere/source/fssystem/save/fssystem_integrity_verification_storage.cpp +++ b/libraries/libstratosphere/source/fssystem/save/fssystem_integrity_verification_storage.cpp @@ -17,15 +17,19 @@ namespace ams::fssystem::save { - Result IntegrityVerificationStorage::Initialize(fs::SubStorage hs, fs::SubStorage ds, s64 verif_block_size, s64 upper_layer_verif_block_size, IBufferManager *bm, const fs::HashSalt &salt, bool is_real_data, fs::StorageType storage_type) { + Result IntegrityVerificationStorage::Initialize(fs::SubStorage hs, fs::SubStorage ds, s64 verif_block_size, s64 upper_layer_verif_block_size, IBufferManager *bm, fssystem::IHash256GeneratorFactory *hgf, const fs::HashSalt &salt, bool is_real_data, fs::StorageType storage_type) { /* Validate preconditions. */ AMS_ASSERT(verif_block_size >= HashSize); AMS_ASSERT(bm != nullptr); + AMS_ASSERT(hgf != nullptr); /* Set storages. */ m_hash_storage = hs; m_data_storage = ds; + /* Set hash generator factory. */ + m_hash_generator_factory = hgf; + /* Set verification block sizes. */ m_verification_block_size = verif_block_size; m_verification_block_order = ILog2(static_cast(verif_block_size)); @@ -111,14 +115,17 @@ namespace ams::fssystem::save { clear_guard.Cancel(); } + /* Verify the signatures. */ + Result verify_hash_result = ResultSuccess(); + + /* Create hash generator. */ + auto generator = m_hash_generator_factory->Create(); + /* Prepare to validate the signatures. */ const auto signature_count = size >> m_verification_block_order; PooledBuffer signature_buffer(signature_count * sizeof(BlockHash), sizeof(BlockHash)); const auto buffer_count = std::min(signature_count, signature_buffer.GetSize() / sizeof(BlockHash)); - /* Verify the signatures. */ - Result verify_hash_result = ResultSuccess(); - size_t verified_count = 0; while (verified_count < signature_count) { /* Read the current signatures. */ @@ -132,7 +139,7 @@ namespace ams::fssystem::save { for (size_t i = 0; i < cur_count && R_SUCCEEDED(cur_result); ++i) { const auto verified_size = (verified_count + i) << m_verification_block_order; u8 *cur_buf = static_cast(buffer) + verified_size; - cur_result = this->VerifyHash(cur_buf, reinterpret_cast(signature_buffer.GetBuffer()) + i); + cur_result = this->VerifyHash(cur_buf, reinterpret_cast(signature_buffer.GetBuffer()) + i, generator); /* If the data is corrupted, clear the corrupted parts. */ if (fs::ResultIntegrityVerificationStorageCorrupted::Includes(cur_result)) { @@ -211,6 +218,8 @@ namespace ams::fssystem::save { PooledBuffer signature_buffer(signature_count * sizeof(BlockHash), sizeof(BlockHash)); const auto buffer_count = std::min(signature_count, signature_buffer.GetSize() / sizeof(BlockHash)); + auto generator = m_hash_generator_factory->Create(); + while (updated_count < signature_count) { const auto cur_count = std::min(buffer_count, signature_count - updated_count); @@ -220,7 +229,7 @@ namespace ams::fssystem::save { for (size_t i = 0; i < cur_count; ++i) { const auto updated_size = (updated_count + i) << m_verification_block_order; - this->CalcBlockHash(reinterpret_cast(signature_buffer.GetBuffer()) + i, reinterpret_cast(buffer) + updated_size); + this->CalcBlockHash(reinterpret_cast(signature_buffer.GetBuffer()) + i, reinterpret_cast(buffer) + updated_size, generator); } } @@ -360,19 +369,18 @@ namespace ams::fssystem::save { } } - void IntegrityVerificationStorage::CalcBlockHash(BlockHash *out, const void *buffer, size_t block_size) const { - /* Create a sha256 generator. */ - crypto::Sha256Generator sha; - sha.Initialize(); + void IntegrityVerificationStorage::CalcBlockHash(BlockHash *out, const void *buffer, size_t block_size, std::unique_ptr &generator) const { + /* Initialize the generator. */ + generator->Initialize(); /* If calculating for save data, hash the salt. */ if (m_storage_type == fs::StorageType_SaveData) { - sha.Update(m_salt.value, sizeof(m_salt)); + generator->Update(m_salt.value, sizeof(m_salt)); } /* Update with the buffer and get the hash. */ - sha.Update(buffer, block_size); - sha.GetHash(out, sizeof(*out)); + generator->Update(buffer, block_size); + generator->GetHash(out, sizeof(*out)); /* Set the validation bit, if the hash is for save data. */ if (m_storage_type == fs::StorageType_SaveData) { @@ -428,7 +436,7 @@ namespace ams::fssystem::save { return ResultSuccess(); } - Result IntegrityVerificationStorage::VerifyHash(const void *buf, BlockHash *hash) { + Result IntegrityVerificationStorage::VerifyHash(const void *buf, BlockHash *hash, std::unique_ptr &generator) { /* Validate preconditions. */ AMS_ASSERT(buf != nullptr); AMS_ASSERT(hash != nullptr); @@ -445,7 +453,7 @@ namespace ams::fssystem::save { /* Get the calculated hash. */ BlockHash calc_hash; - this->CalcBlockHash(std::addressof(calc_hash), buf); + this->CalcBlockHash(std::addressof(calc_hash), buf, generator); /* Check that the signatures are equal. */ if (!crypto::IsSameBytes(std::addressof(cmp_hash), std::addressof(calc_hash), sizeof(BlockHash))) { diff --git a/libraries/libvapours/include/vapours/results/fs_results.hpp b/libraries/libvapours/include/vapours/results/fs_results.hpp index d7b602689..613c96327 100644 --- a/libraries/libvapours/include/vapours/results/fs_results.hpp +++ b/libraries/libvapours/include/vapours/results/fs_results.hpp @@ -206,8 +206,10 @@ namespace ams::fs { R_DEFINE_ERROR_RANGE(NcaCorrupted, 4501, 4599); R_DEFINE_ERROR_RESULT(NcaBaseStorageOutOfRangeA, 4508); R_DEFINE_ERROR_RESULT(NcaBaseStorageOutOfRangeB, 4509); + R_DEFINE_ERROR_RESULT(NcaBaseStorageOutOfRangeC, 4510); + R_DEFINE_ERROR_RESULT(NcaBaseStorageOutOfRangeD, 4511); - R_DEFINE_ERROR_RANGE(NcaFileSystemCorrupted, 4511, 4529); + R_DEFINE_ERROR_RANGE(NcaFileSystemCorrupted, 4512, 4529); R_DEFINE_ERROR_RESULT(InvalidNcaFileSystemType, 4512); R_DEFINE_ERROR_RESULT(InvalidAcidFileSize, 4513); R_DEFINE_ERROR_RESULT(InvalidAcidSize, 4514); @@ -227,6 +229,8 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(InvalidNcaHeader, 4528); R_DEFINE_ERROR_RESULT(InvalidNcaFsHeader, 4529); + R_DEFINE_ERROR_RESULT(NcaBaseStorageOutOfRangeE, 4530); + R_DEFINE_ERROR_RANGE(NcaHierarchicalSha256StorageCorrupted, 4531, 4539); R_DEFINE_ERROR_RESULT(InvalidHierarchicalSha256BlockSize, 4532); R_DEFINE_ERROR_RESULT(InvalidHierarchicalSha256LayerCount, 4533); @@ -236,6 +240,9 @@ namespace ams::fs { /* TODO: Range? */ R_DEFINE_ERROR_RESULT(InvalidNcaHeader1SignatureKeyGeneration, 4543); + /* TODO: Range? */ + R_DEFINE_ERROR_RESULT(InvalidCompressedStorageSize, 4547); + R_DEFINE_ERROR_RANGE(IntegrityVerificationStorageCorrupted, 4601, 4639); R_DEFINE_ERROR_RESULT(IncorrectIntegrityVerificationMagic, 4602); R_DEFINE_ERROR_RESULT(InvalidZeroHash, 4603); @@ -362,6 +369,7 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileSystemB, 6375); R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileA, 6376); R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileB, 6377); + R_DEFINE_ERROR_RESULT(UnsupportedOperationInCompressedStorageA, 6387); R_DEFINE_ERROR_RANGE(PermissionDenied, 6400, 6449);