mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
fs: revise NcaFileSystemDriver for latest semantics
This commit is contained in:
parent
ccf29a1302
commit
52296fc2dd
37 changed files with 1612 additions and 1026 deletions
|
@ -62,4 +62,5 @@
|
||||||
#include <stratosphere/fs/fs_system_data.hpp>
|
#include <stratosphere/fs/fs_system_data.hpp>
|
||||||
#include <stratosphere/fs/fs_program_index_map_info.hpp>
|
#include <stratosphere/fs/fs_program_index_map_info.hpp>
|
||||||
#include <stratosphere/fs/impl/fs_access_log_impl.hpp>
|
#include <stratosphere/fs/impl/fs_access_log_impl.hpp>
|
||||||
|
#include <stratosphere/fs/impl/fs_hash_generator_factory_selector.hpp>
|
||||||
#include <stratosphere/fs/fs_api.hpp>
|
#include <stratosphere/fs/fs_api.hpp>
|
||||||
|
|
|
@ -63,6 +63,9 @@ namespace ams::fs {
|
||||||
AMS_ABORT_UNLESS(sub->m_size >= o + sz);
|
AMS_ABORT_UNLESS(sub->m_size >= o + sz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE ::ams::fs::IStorage *operator->() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
public:
|
public:
|
||||||
void SetResizable(bool rsz) {
|
void SetResizable(bool rsz) {
|
||||||
m_resizable = rsz;
|
m_resizable = rsz;
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere/fssystem/fssystem_i_hash_256_generator.hpp>
|
||||||
|
|
||||||
|
namespace ams::fs::impl {
|
||||||
|
|
||||||
|
fssystem::IHash256GeneratorFactorySelector *GetNcaHashGeneratorFactorySelector();
|
||||||
|
fssystem::IHash256GeneratorFactorySelector *GetSaveDataHashGeneratorFactorySelector();
|
||||||
|
|
||||||
|
}
|
|
@ -17,10 +17,12 @@
|
||||||
#include <vapours.hpp>
|
#include <vapours.hpp>
|
||||||
#include <stratosphere/fssrv/fssrv_i_file_system_creator.hpp>
|
#include <stratosphere/fssrv/fssrv_i_file_system_creator.hpp>
|
||||||
#include <stratosphere/fssystem/buffers/fssystem_i_buffer_manager.hpp>
|
#include <stratosphere/fssystem/buffers/fssystem_i_buffer_manager.hpp>
|
||||||
|
#include <stratosphere/fssystem/fssystem_i_hash_256_generator.hpp>
|
||||||
|
|
||||||
namespace ams::fssystem {
|
namespace ams::fssystem {
|
||||||
|
|
||||||
struct NcaCryptoConfiguration;
|
struct NcaCryptoConfiguration;
|
||||||
|
struct NcaCompressionConfiguration;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,22 +33,20 @@ namespace ams::fssrv::fscreator {
|
||||||
NON_MOVEABLE(StorageOnNcaCreator);
|
NON_MOVEABLE(StorageOnNcaCreator);
|
||||||
private:
|
private:
|
||||||
MemoryResource *m_allocator;
|
MemoryResource *m_allocator;
|
||||||
fssystem::IBufferManager * const m_buffer_manager;
|
|
||||||
const fssystem::NcaCryptoConfiguration &m_nca_crypto_cfg;
|
const fssystem::NcaCryptoConfiguration &m_nca_crypto_cfg;
|
||||||
bool m_is_prod;
|
const fssystem::NcaCompressionConfiguration &m_nca_compression_cfg;
|
||||||
bool m_is_enabled_program_verification;
|
fssystem::IBufferManager * const m_buffer_manager;
|
||||||
private:
|
fssystem::IHash256GeneratorFactorySelector * const m_hash_generator_factory_selector;
|
||||||
Result VerifyNcaHeaderSign2(fssystem::NcaReader *nca_reader, fs::IStorage *storage);
|
|
||||||
public:
|
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<fs::IStorage> *out, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr<fssystem::NcaReader> nca_reader, s32 index, bool verify_header_sign_2) override;
|
virtual Result Create(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fssystem::IAsynchronousAccessSplitter> *out_splitter, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr<fssystem::NcaReader> nca_reader, s32 index) override;
|
||||||
virtual Result CreateWithPatch(std::shared_ptr<fs::IStorage> *out, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr<fssystem::NcaReader> original_nca_reader, std::shared_ptr<fssystem::NcaReader> current_nca_reader, s32 index, bool verify_header_sign_2) override;
|
virtual Result CreateWithPatch(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fssystem::IAsynchronousAccessSplitter> *out_splitter, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr<fssystem::NcaReader> original_nca_reader, std::shared_ptr<fssystem::NcaReader> current_nca_reader, s32 index) override;
|
||||||
virtual Result CreateNcaReader(std::shared_ptr<fssystem::NcaReader> *out, std::shared_ptr<fs::IStorage> storage) override;
|
virtual Result CreateNcaReader(std::shared_ptr<fssystem::NcaReader> *out, std::shared_ptr<fs::IStorage> storage) override;
|
||||||
virtual Result VerifyAcid(fs::fsa::IFileSystem *fs, fssystem::NcaReader *nca_reader) override;
|
|
||||||
virtual void SetEnabledProgramVerification(bool en) override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,8 @@ namespace ams::fssystem {
|
||||||
class NcaReader;
|
class NcaReader;
|
||||||
class NcaFsHeaderReader;
|
class NcaFsHeaderReader;
|
||||||
|
|
||||||
|
class IAsynchronousAccessSplitter;
|
||||||
|
|
||||||
namespace save {
|
namespace save {
|
||||||
|
|
||||||
/* TODO */
|
/* TODO */
|
||||||
|
@ -60,11 +62,9 @@ namespace ams::fssrv::fscreator {
|
||||||
class IStorageOnNcaCreator {
|
class IStorageOnNcaCreator {
|
||||||
public:
|
public:
|
||||||
virtual ~IStorageOnNcaCreator() { /* ... */ }
|
virtual ~IStorageOnNcaCreator() { /* ... */ }
|
||||||
virtual Result Create(std::shared_ptr<fs::IStorage> *out, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr<fssystem::NcaReader> nca_reader, s32 index, bool verify_header_sign_2) = 0;
|
virtual Result Create(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fssystem::IAsynchronousAccessSplitter> *out_splitter, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr<fssystem::NcaReader> nca_reader, s32 index) = 0;
|
||||||
virtual Result CreateWithPatch(std::shared_ptr<fs::IStorage> *out, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr<fssystem::NcaReader> original_nca_reader, std::shared_ptr<fssystem::NcaReader> current_nca_reader, s32 index, bool verify_header_sign_2) = 0;
|
virtual Result CreateWithPatch(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fssystem::IAsynchronousAccessSplitter> *out_splitter, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr<fssystem::NcaReader> original_nca_reader, std::shared_ptr<fssystem::NcaReader> current_nca_reader, s32 index) = 0;
|
||||||
virtual Result CreateNcaReader(std::shared_ptr<fssystem::NcaReader> *out, std::shared_ptr<fs::IStorage> storage) = 0;
|
virtual Result CreateNcaReader(std::shared_ptr<fssystem::NcaReader> *out, std::shared_ptr<fs::IStorage> storage) = 0;
|
||||||
virtual Result VerifyAcid(fs::fsa::IFileSystem *fs, fssystem::NcaReader *nca_reader) = 0;
|
|
||||||
virtual void SetEnabledProgramVerification(bool en) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FileSystemCreatorInterfaces {
|
struct FileSystemCreatorInterfaces {
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include <stratosphere/fssystem/fssystem_nca_file_system_driver.hpp>
|
#include <stratosphere/fssystem/fssystem_nca_file_system_driver.hpp>
|
||||||
#include <stratosphere/fssystem/fssystem_nca_file_system_driver_impl.hpp>
|
#include <stratosphere/fssystem/fssystem_nca_file_system_driver_impl.hpp>
|
||||||
#include <stratosphere/fssystem/fssystem_crypto_configuration.hpp>
|
#include <stratosphere/fssystem/fssystem_crypto_configuration.hpp>
|
||||||
|
#include <stratosphere/fssystem/fssystem_compression_configuration.hpp>
|
||||||
#include <stratosphere/fssystem/fssystem_aes_ctr_counter_extended_storage.hpp>
|
#include <stratosphere/fssystem/fssystem_aes_ctr_counter_extended_storage.hpp>
|
||||||
#include <stratosphere/fssystem/buffers/fssystem_buffer_manager_utils.hpp>
|
#include <stratosphere/fssystem/buffers/fssystem_buffer_manager_utils.hpp>
|
||||||
#include <stratosphere/fssystem/buffers/fssystem_file_system_buffer_manager.hpp>
|
#include <stratosphere/fssystem/buffers/fssystem_file_system_buffer_manager.hpp>
|
||||||
|
@ -44,6 +45,7 @@
|
||||||
#include <stratosphere/fssystem/fssystem_service_context.hpp>
|
#include <stratosphere/fssystem/fssystem_service_context.hpp>
|
||||||
#include <stratosphere/fssystem/fssystem_alignment_matching_storage_impl.hpp>
|
#include <stratosphere/fssystem/fssystem_alignment_matching_storage_impl.hpp>
|
||||||
#include <stratosphere/fssystem/fssystem_alignment_matching_storage.hpp>
|
#include <stratosphere/fssystem/fssystem_alignment_matching_storage.hpp>
|
||||||
|
#include <stratosphere/fssystem/fssystem_compressed_storage.hpp>
|
||||||
#include <stratosphere/fssystem/save/fssystem_buffered_storage.hpp>
|
#include <stratosphere/fssystem/save/fssystem_buffered_storage.hpp>
|
||||||
#include <stratosphere/fssystem/save/fssystem_hierarchical_integrity_verification_storage.hpp>
|
#include <stratosphere/fssystem/save/fssystem_hierarchical_integrity_verification_storage.hpp>
|
||||||
#include <stratosphere/fssystem/fssystem_integrity_romfs_storage.hpp>
|
#include <stratosphere/fssystem/fssystem_integrity_romfs_storage.hpp>
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
namespace ams::fssystem {
|
namespace ams::fssystem {
|
||||||
|
|
||||||
|
template<typename BasePointer>
|
||||||
class AesCtrStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
|
class AesCtrStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
|
||||||
NON_COPYABLE(AesCtrStorage);
|
NON_COPYABLE(AesCtrStorage);
|
||||||
NON_MOVEABLE(AesCtrStorage);
|
NON_MOVEABLE(AesCtrStorage);
|
||||||
|
@ -28,13 +29,13 @@ namespace ams::fssystem {
|
||||||
static constexpr size_t KeySize = crypto::Aes128CtrEncryptor::KeySize;
|
static constexpr size_t KeySize = crypto::Aes128CtrEncryptor::KeySize;
|
||||||
static constexpr size_t IvSize = crypto::Aes128CtrEncryptor::IvSize;
|
static constexpr size_t IvSize = crypto::Aes128CtrEncryptor::IvSize;
|
||||||
private:
|
private:
|
||||||
IStorage * const m_base_storage;
|
BasePointer m_base_storage;
|
||||||
char m_key[KeySize];
|
char m_key[KeySize];
|
||||||
char m_iv[IvSize];
|
char m_iv[IvSize];
|
||||||
public:
|
public:
|
||||||
static void MakeIv(void *dst, size_t dst_size, u64 upper, s64 offset);
|
static void MakeIv(void *dst, size_t dst_size, u64 upper, s64 offset);
|
||||||
public:
|
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 Read(s64 offset, void *buffer, size_t size) override;
|
||||||
virtual Result Write(s64 offset, const 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;
|
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<fs::IStorage *>;
|
||||||
|
using AesCtrStorageBySharedPointer = AesCtrStorage<std::shared_ptr<fs::IStorage>>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
namespace ams::fssystem {
|
namespace ams::fssystem {
|
||||||
|
|
||||||
|
template<typename BasePointer>
|
||||||
class AesXtsStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
|
class AesXtsStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
|
||||||
NON_COPYABLE(AesXtsStorage);
|
NON_COPYABLE(AesXtsStorage);
|
||||||
NON_MOVEABLE(AesXtsStorage);
|
NON_MOVEABLE(AesXtsStorage);
|
||||||
|
@ -29,13 +30,15 @@ namespace ams::fssystem {
|
||||||
static constexpr size_t KeySize = crypto::Aes128XtsEncryptor::KeySize;
|
static constexpr size_t KeySize = crypto::Aes128XtsEncryptor::KeySize;
|
||||||
static constexpr size_t IvSize = crypto::Aes128XtsEncryptor::IvSize;
|
static constexpr size_t IvSize = crypto::Aes128XtsEncryptor::IvSize;
|
||||||
private:
|
private:
|
||||||
IStorage * const m_base_storage;
|
BasePointer m_base_storage;
|
||||||
char m_key[2][KeySize];
|
char m_key[2][KeySize];
|
||||||
char m_iv[IvSize];
|
char m_iv[IvSize];
|
||||||
const size_t m_block_size;
|
const size_t m_block_size;
|
||||||
os::SdkMutex m_mutex;
|
os::SdkMutex m_mutex;
|
||||||
public:
|
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 Read(s64 offset, void *buffer, size_t size) override;
|
||||||
virtual Result Write(s64 offset, const 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;
|
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<fs::IStorage *>;
|
||||||
|
using AesXtsStorageBySharedPointer = AesXtsStorage<std::shared_ptr<fs::IStorage>>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ namespace ams::fssystem {
|
||||||
/* ... */
|
/* ... */
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit AlignmentMatchingStorage(std::shared_ptr<fs::IStorage> 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<fs::IStorage> bs) : m_shared_base_storage(std::move(bs)), m_base_storage(m_shared_base_storage.get()), m_is_base_storage_size_dirty(true) {
|
||||||
/* ... */
|
/* ... */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,6 +109,9 @@ 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 {
|
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 {
|
||||||
|
if (op_id == fs::OperationId::Invalidate) {
|
||||||
|
return m_base_storage->OperateRange(fs::OperationId::Invalidate, offset, size);
|
||||||
|
} else {
|
||||||
/* Succeed if zero size. */
|
/* Succeed if zero size. */
|
||||||
R_SUCCEED_IF(size == 0);
|
R_SUCCEED_IF(size == 0);
|
||||||
|
|
||||||
|
@ -125,9 +128,10 @@ namespace ams::fssystem {
|
||||||
|
|
||||||
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<size_t _BufferAlign>
|
template<typename BaseStorageType, size_t _BufferAlign>
|
||||||
class AlignmentMatchingStoragePooledBuffer : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
|
class AlignmentMatchingStoragePooledBuffer : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
|
||||||
NON_COPYABLE(AlignmentMatchingStoragePooledBuffer);
|
NON_COPYABLE(AlignmentMatchingStoragePooledBuffer);
|
||||||
NON_MOVEABLE(AlignmentMatchingStoragePooledBuffer);
|
NON_MOVEABLE(AlignmentMatchingStoragePooledBuffer);
|
||||||
|
@ -136,12 +140,12 @@ namespace ams::fssystem {
|
||||||
|
|
||||||
static_assert(util::IsPowerOfTwo(BufferAlign));
|
static_assert(util::IsPowerOfTwo(BufferAlign));
|
||||||
private:
|
private:
|
||||||
fs::IStorage * const m_base_storage;
|
BaseStorageType m_base_storage;
|
||||||
s64 m_base_storage_size;
|
s64 m_base_storage_size;
|
||||||
size_t m_data_align;
|
size_t m_data_align;
|
||||||
bool m_is_base_storage_size_dirty;
|
bool m_is_base_storage_size_dirty;
|
||||||
public:
|
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));
|
AMS_ASSERT(util::IsPowerOfTwo(da));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,6 +210,9 @@ 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 {
|
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 {
|
||||||
|
if (op_id == fs::OperationId::Invalidate) {
|
||||||
|
return m_base_storage->OperateRange(fs::OperationId::Invalidate, offset, size);
|
||||||
|
} else {
|
||||||
/* Succeed if zero size. */
|
/* Succeed if zero size. */
|
||||||
R_SUCCEED_IF(size == 0);
|
R_SUCCEED_IF(size == 0);
|
||||||
|
|
||||||
|
@ -222,6 +229,7 @@ namespace ams::fssystem {
|
||||||
|
|
||||||
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<size_t _BufferAlign>
|
template<size_t _BufferAlign>
|
||||||
|
@ -288,6 +296,9 @@ 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 {
|
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 {
|
||||||
|
if (op_id == fs::OperationId::Invalidate) {
|
||||||
|
return m_base_storage->OperateRange(fs::OperationId::Invalidate, offset, size);
|
||||||
|
} else {
|
||||||
/* Succeed if zero size. */
|
/* Succeed if zero size. */
|
||||||
R_SUCCEED_IF(size == 0);
|
R_SUCCEED_IF(size == 0);
|
||||||
|
|
||||||
|
@ -304,6 +315,7 @@ namespace ams::fssystem {
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,14 @@ namespace ams::fssystem {
|
||||||
public:
|
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 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 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<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) {
|
||||||
|
return Read(base_storage.get(), work_buf, work_buf_size, data_alignment, buffer_alignment, offset, buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Result Write(std::shared_ptr<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) {
|
||||||
|
return Write(base_storage.get(), work_buf, work_buf_size, data_alignment, buffer_alignment, offset, buffer, size);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/fssystem/fssystem_asynchronous_access.hpp>
|
||||||
|
#include <stratosphere/fssystem/fssystem_bucket_tree.hpp>
|
||||||
|
#include <stratosphere/fssystem/fssystem_compression_common.hpp>
|
||||||
|
|
||||||
|
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<Entry>::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();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/fssystem/fssystem_nca_file_system_driver.hpp>
|
||||||
|
|
||||||
|
namespace ams::fssystem {
|
||||||
|
|
||||||
|
const ::ams::fssystem::NcaCompressionConfiguration *GetNcaCompressionConfiguration();
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
|
||||||
|
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<IHash256Generator> 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<IHash256Generator> 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -37,7 +37,7 @@ namespace ams::fssystem {
|
||||||
IntegrityRomFsStorage() : m_mutex() { /* ... */ }
|
IntegrityRomFsStorage() : m_mutex() { /* ... */ }
|
||||||
virtual ~IntegrityRomFsStorage() override { this->Finalize(); }
|
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();
|
void Finalize();
|
||||||
|
|
||||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||||
|
|
|
@ -17,11 +17,15 @@
|
||||||
#include <vapours.hpp>
|
#include <vapours.hpp>
|
||||||
#include <stratosphere/fs/impl/fs_newable.hpp>
|
#include <stratosphere/fs/impl/fs_newable.hpp>
|
||||||
#include <stratosphere/fs/fs_istorage.hpp>
|
#include <stratosphere/fs/fs_istorage.hpp>
|
||||||
|
#include <stratosphere/fssystem/fssystem_compression_common.hpp>
|
||||||
|
#include <stratosphere/fssystem/fssystem_i_hash_256_generator.hpp>
|
||||||
|
#include <stratosphere/fssystem/fssystem_asynchronous_access.hpp>
|
||||||
#include <stratosphere/fssystem/fssystem_nca_header.hpp>
|
#include <stratosphere/fssystem/fssystem_nca_header.hpp>
|
||||||
#include <stratosphere/fssystem/buffers/fssystem_i_buffer_manager.hpp>
|
#include <stratosphere/fssystem/buffers/fssystem_i_buffer_manager.hpp>
|
||||||
|
|
||||||
namespace ams::fssystem {
|
namespace ams::fssystem {
|
||||||
|
|
||||||
|
class CompressedStorage;
|
||||||
class AesCtrCounterExtendedStorage;
|
class AesCtrCounterExtendedStorage;
|
||||||
class IndirectStorage;
|
class IndirectStorage;
|
||||||
class SparseStorage;
|
class SparseStorage;
|
||||||
|
@ -57,6 +61,11 @@ namespace ams::fssystem {
|
||||||
};
|
};
|
||||||
static_assert(util::is_pod<NcaCryptoConfiguration>::value);
|
static_assert(util::is_pod<NcaCryptoConfiguration>::value);
|
||||||
|
|
||||||
|
struct NcaCompressionConfiguration {
|
||||||
|
GetDecompressorFunction get_decompressor;
|
||||||
|
};
|
||||||
|
static_assert(util::is_pod<NcaCompressionConfiguration>::value);
|
||||||
|
|
||||||
constexpr inline bool IsInvalidKeyTypeValue(s32 key_type) {
|
constexpr inline bool IsInvalidKeyTypeValue(s32 key_type) {
|
||||||
return key_type < 0;
|
return key_type < 0;
|
||||||
}
|
}
|
||||||
|
@ -87,22 +96,22 @@ namespace ams::fssystem {
|
||||||
private:
|
private:
|
||||||
NcaHeader m_header;
|
NcaHeader m_header;
|
||||||
u8 m_decryption_keys[NcaHeader::DecryptionKey_Count][NcaCryptoConfiguration::Aes128KeySize];
|
u8 m_decryption_keys[NcaHeader::DecryptionKey_Count][NcaCryptoConfiguration::Aes128KeySize];
|
||||||
std::shared_ptr<fs::IStorage> m_shared_base_storage;
|
std::shared_ptr<fs::IStorage> m_body_storage;
|
||||||
std::unique_ptr<fs::IStorage> m_header_storage;
|
std::unique_ptr<fs::IStorage> m_header_storage;
|
||||||
fs::IStorage *m_body_storage;
|
|
||||||
u8 m_external_decryption_key[NcaCryptoConfiguration::Aes128KeySize];
|
u8 m_external_decryption_key[NcaCryptoConfiguration::Aes128KeySize];
|
||||||
DecryptAesCtrFunction m_decrypt_aes_ctr;
|
DecryptAesCtrFunction m_decrypt_aes_ctr;
|
||||||
DecryptAesCtrFunction m_decrypt_aes_ctr_external;
|
DecryptAesCtrFunction m_decrypt_aes_ctr_external;
|
||||||
bool m_is_software_aes_prioritized;
|
bool m_is_software_aes_prioritized;
|
||||||
NcaHeader::EncryptionType m_header_encryption_type;
|
NcaHeader::EncryptionType m_header_encryption_type;
|
||||||
|
GetDecompressorFunction m_get_decompressor;
|
||||||
|
IHash256GeneratorFactory *m_hash_generator_factory;
|
||||||
public:
|
public:
|
||||||
NcaReader();
|
NcaReader();
|
||||||
~NcaReader();
|
~NcaReader();
|
||||||
|
|
||||||
Result Initialize(fs::IStorage *base_storage, const NcaCryptoConfiguration &crypto_cfg);
|
Result Initialize(std::shared_ptr<fs::IStorage> base_storage, const NcaCryptoConfiguration &crypto_cfg, const NcaCompressionConfiguration &compression_cfg, IHash256GeneratorFactorySelector *hgf_selector);
|
||||||
Result Initialize(std::shared_ptr<fs::IStorage> base_storage, const NcaCryptoConfiguration &crypto_cfg);
|
|
||||||
|
|
||||||
fs::IStorage *GetBodyStorage();
|
std::shared_ptr<fs::IStorage> GetSharedBodyStorage();
|
||||||
u32 GetMagic() const;
|
u32 GetMagic() const;
|
||||||
NcaHeader::DistributionType GetDistributionType() const;
|
NcaHeader::DistributionType GetDistributionType() const;
|
||||||
NcaHeader::ContentType GetContentType() const;
|
NcaHeader::ContentType GetContentType() const;
|
||||||
|
@ -124,7 +133,7 @@ namespace ams::fssystem {
|
||||||
void GetEncryptedKey(void *dst, size_t size) const;
|
void GetEncryptedKey(void *dst, size_t size) const;
|
||||||
const void *GetDecryptionKey(s32 index) const;
|
const void *GetDecryptionKey(s32 index) const;
|
||||||
bool HasValidInternalKey() const;
|
bool HasValidInternalKey() const;
|
||||||
bool HasInternalDecryptionKeyForAesHardwareSpeedEmulation() const;
|
bool HasInternalDecryptionKeyForAesHw() const;
|
||||||
bool IsSoftwareAesPrioritized() const;
|
bool IsSoftwareAesPrioritized() const;
|
||||||
void PrioritizeSoftwareAes();
|
void PrioritizeSoftwareAes();
|
||||||
bool HasExternalDecryptionKey() const;
|
bool HasExternalDecryptionKey() const;
|
||||||
|
@ -136,7 +145,11 @@ namespace ams::fssystem {
|
||||||
NcaHeader::EncryptionType GetEncryptionType() const;
|
NcaHeader::EncryptionType GetEncryptionType() const;
|
||||||
Result ReadHeader(NcaFsHeader *dst, s32 index) 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 {
|
class NcaFsHeaderReader : public ::ams::fs::impl::Newable {
|
||||||
|
@ -153,8 +166,9 @@ namespace ams::fssystem {
|
||||||
Result Initialize(const NcaReader &reader, s32 index);
|
Result Initialize(const NcaReader &reader, s32 index);
|
||||||
bool IsInitialized() const { return m_fs_index >= 0; }
|
bool IsInitialized() const { return m_fs_index >= 0; }
|
||||||
|
|
||||||
NcaFsHeader &GetData() { return m_data; }
|
// NcaFsHeader &GetData() { return m_data; }
|
||||||
const NcaFsHeader &GetData() const { return m_data; }
|
// const NcaFsHeader &GetData() const { return m_data; }
|
||||||
|
|
||||||
void GetRawData(void *dst, size_t dst_size) const;
|
void GetRawData(void *dst, size_t dst_size) const;
|
||||||
|
|
||||||
NcaFsHeader::HashData &GetHashData();
|
NcaFsHeader::HashData &GetHashData();
|
||||||
|
@ -170,57 +184,85 @@ namespace ams::fssystem {
|
||||||
bool ExistsSparseLayer() const;
|
bool ExistsSparseLayer() const;
|
||||||
NcaSparseInfo &GetSparseInfo();
|
NcaSparseInfo &GetSparseInfo();
|
||||||
const NcaSparseInfo &GetSparseInfo() const;
|
const NcaSparseInfo &GetSparseInfo() const;
|
||||||
|
bool ExistsCompressionLayer() const;
|
||||||
|
NcaCompressionInfo &GetCompressionInfo();
|
||||||
|
const NcaCompressionInfo &GetCompressionInfo() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NcaFileSystemDriver : public ::ams::fs::impl::Newable {
|
class NcaFileSystemDriver : public ::ams::fs::impl::Newable {
|
||||||
NON_COPYABLE(NcaFileSystemDriver);
|
NON_COPYABLE(NcaFileSystemDriver);
|
||||||
NON_MOVEABLE(NcaFileSystemDriver);
|
NON_MOVEABLE(NcaFileSystemDriver);
|
||||||
public:
|
private:
|
||||||
class StorageOption;
|
struct StorageContext {
|
||||||
class StorageOptionWithHeaderReader;
|
bool open_raw_storage;
|
||||||
|
std::shared_ptr<fs::IStorage> body_substorage;
|
||||||
|
std::shared_ptr<fssystem::SparseStorage> current_sparse_storage;
|
||||||
|
std::shared_ptr<fs::IStorage> sparse_storage_meta_storage;
|
||||||
|
std::shared_ptr<fssystem::SparseStorage> original_sparse_storage;
|
||||||
|
void *external_current_sparse_storage; /* TODO: Add real type? */
|
||||||
|
void *external_original_sparse_storage; /* TODO: Add real type? */
|
||||||
|
std::shared_ptr<fs::IStorage> aes_ctr_ex_storage_meta_storage;
|
||||||
|
std::shared_ptr<fs::IStorage> aes_ctr_ex_storage_data_storage;
|
||||||
|
std::shared_ptr<fssystem::AesCtrCounterExtendedStorage> aes_ctr_ex_storage;
|
||||||
|
std::shared_ptr<fs::IStorage> indirect_storage_meta_storage;
|
||||||
|
std::shared_ptr<fssystem::IndirectStorage> indirect_storage;
|
||||||
|
std::shared_ptr<fs::IStorage> fs_data_storage;
|
||||||
|
std::shared_ptr<fs::IStorage> compressed_storage_meta_storage;
|
||||||
|
std::shared_ptr<fssystem::CompressedStorage> compressed_storage;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum AlignmentStorageRequirement {
|
||||||
|
/* TODO */
|
||||||
|
AlignmentStorageRequirement_CacheBlockSize = 0,
|
||||||
|
AlignmentStorageRequirement_None = 1,
|
||||||
|
};
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<NcaReader> m_original_reader;
|
std::shared_ptr<NcaReader> m_original_reader;
|
||||||
std::shared_ptr<NcaReader> m_reader;
|
std::shared_ptr<NcaReader> m_reader;
|
||||||
MemoryResource * const m_allocator;
|
MemoryResource * const m_allocator;
|
||||||
fssystem::IBufferManager * const m_buffer_manager;
|
fssystem::IBufferManager * const m_buffer_manager;
|
||||||
|
fssystem::IHash256GeneratorFactorySelector * const m_hash_generator_factory_selector;
|
||||||
public:
|
public:
|
||||||
static Result SetupFsHeaderReader(NcaFsHeaderReader *out, const NcaReader &reader, s32 fs_index);
|
static Result SetupFsHeaderReader(NcaFsHeaderReader *out, const NcaReader &reader, s32 fs_index);
|
||||||
public:
|
public:
|
||||||
NcaFileSystemDriver(std::shared_ptr<NcaReader> reader, MemoryResource *allocator, IBufferManager *buffer_manager) : m_original_reader(), m_reader(reader), m_allocator(allocator), m_buffer_manager(buffer_manager) {
|
NcaFileSystemDriver(std::shared_ptr<NcaReader> 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_reader != nullptr);
|
||||||
|
AMS_ASSERT(m_hash_generator_factory_selector != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
NcaFileSystemDriver(std::shared_ptr<NcaReader> original_reader, std::shared_ptr<NcaReader> 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<NcaReader> original_reader, std::shared_ptr<NcaReader> 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_reader != nullptr);
|
||||||
|
AMS_ASSERT(m_hash_generator_factory_selector != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result OpenRawStorage(std::shared_ptr<fs::IStorage> *out, s32 fs_index);
|
Result OpenStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<IAsynchronousAccessSplitter> *out_splitter, NcaFsHeaderReader *out_header_reader, s32 fs_index);
|
||||||
|
|
||||||
Result OpenStorage(std::shared_ptr<fs::IStorage> *out, NcaFsHeaderReader *out_header_reader, s32 fs_index);
|
|
||||||
Result OpenStorage(std::shared_ptr<fs::IStorage> *out, StorageOption *option);
|
|
||||||
|
|
||||||
Result OpenStorage(std::shared_ptr<fs::IStorage> *out, s32 fs_index) {
|
|
||||||
NcaFsHeaderReader dummy;
|
|
||||||
return this->OpenStorage(out, std::addressof(dummy), fs_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
Result OpenDecryptableStorage(std::shared_ptr<fs::IStorage> *out, StorageOption *option, bool indirect_needed);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class BaseStorage;
|
Result OpenStorageImpl(std::shared_ptr<fs::IStorage> *out, NcaFsHeaderReader *out_header_reader, s32 fs_index, StorageContext *ctx);
|
||||||
|
|
||||||
Result CreateBaseStorage(BaseStorage *out, StorageOption *option);
|
Result OpenIndirectableStorageAsOriginal(std::shared_ptr<fs::IStorage> *out, const NcaFsHeaderReader *header_reader, StorageContext *ctx);
|
||||||
|
|
||||||
Result CreateDecryptableStorage(std::unique_ptr<fs::IStorage> *out, StorageOption *option, BaseStorage *base_storage);
|
Result CreateBodySubStorage(std::shared_ptr<fs::IStorage> *out, s64 offset, s64 size);
|
||||||
Result CreateAesXtsStorage(std::unique_ptr<fs::IStorage> *out, BaseStorage *base_storage);
|
|
||||||
Result CreateAesCtrStorage(std::unique_ptr<fs::IStorage> *out, BaseStorage *base_storage);
|
|
||||||
Result CreateAesCtrExStorage(std::unique_ptr<fs::IStorage> *out, StorageOption *option, BaseStorage *base_storage);
|
|
||||||
|
|
||||||
Result CreateIndirectStorage(std::unique_ptr<fs::IStorage> *out, StorageOption *option, std::unique_ptr<fs::IStorage> base_storage);
|
Result CreateAesCtrStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, AlignmentStorageRequirement alignment_storage_requirement);
|
||||||
|
Result CreateAesXtsStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, s64 offset);
|
||||||
|
|
||||||
Result CreateVerificationStorage(std::unique_ptr<fs::IStorage> *out, std::unique_ptr<fs::IStorage> base_storage, NcaFsHeaderReader *header_reader);
|
Result CreateSparseStorageMetaStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, const NcaSparseInfo &sparse_info);
|
||||||
Result CreateSha256Storage(std::unique_ptr<fs::IStorage> *out, std::unique_ptr<fs::IStorage> base_storage, NcaFsHeaderReader *header_reader);
|
Result CreateSparseStorageCore(std::shared_ptr<fssystem::SparseStorage> *out, std::shared_ptr<fs::IStorage> base_storage, s64 base_size, std::shared_ptr<fs::IStorage> meta_storage, const NcaSparseInfo &sparse_info, bool external_info);
|
||||||
Result CreateIntegrityVerificationStorage(std::unique_ptr<fs::IStorage> *out, std::unique_ptr<fs::IStorage> base_storage, NcaFsHeaderReader *header_reader);
|
Result CreateSparseStorage(std::shared_ptr<fs::IStorage> *out, s64 *out_fs_data_offset, std::shared_ptr<fssystem::SparseStorage> *out_sparse_storage, std::shared_ptr<fs::IStorage> *out_meta_storage, s32 index, const NcaAesCtrUpperIv &upper_iv, const NcaSparseInfo &sparse_info);
|
||||||
|
|
||||||
|
Result CreateAesCtrExMetaStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, const NcaPatchInfo &patch_info);
|
||||||
|
Result CreateAesCtrExStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fssystem::AesCtrCounterExtendedStorage> *out_ext, std::shared_ptr<fs::IStorage> base_storage, std::shared_ptr<fs::IStorage> meta_storage, s64 counter_offset, const NcaAesCtrUpperIv &upper_iv, const NcaPatchInfo &patch_info);
|
||||||
|
|
||||||
|
Result CreateIndirectStorageMetaStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, const NcaPatchInfo &patch_info);
|
||||||
|
Result CreateIndirectStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fssystem::IndirectStorage> *out_ind, std::shared_ptr<fs::IStorage> base_storage, std::shared_ptr<fs::IStorage> original_data_storage, std::shared_ptr<fs::IStorage> meta_storage, const NcaPatchInfo &patch_info);
|
||||||
|
|
||||||
|
Result CreateSha256Storage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, const NcaFsHeader::HashData::HierarchicalSha256Data &sha256_data);
|
||||||
|
|
||||||
|
Result CreateIntegrityVerificationStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, const NcaFsHeader::HashData::IntegrityMetaInfo &meta_info);
|
||||||
|
|
||||||
|
Result CreateCompressedStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fssystem::CompressedStorage> *out_cmp, std::shared_ptr<fs::IStorage> *out_meta, std::shared_ptr<fs::IStorage> base_storage, const NcaCompressionInfo &compression_info);
|
||||||
|
public:
|
||||||
|
Result CreateCompressedStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fssystem::CompressedStorage> *out_cmp, std::shared_ptr<fs::IStorage> *out_meta, std::shared_ptr<fs::IStorage> base_storage, const NcaCompressionInfo &compression_info, GetDecompressorFunction get_decompressor, MemoryResource *allocator, IBufferManager *buffer_manager);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,131 +20,131 @@
|
||||||
|
|
||||||
namespace ams::fssystem {
|
namespace ams::fssystem {
|
||||||
|
|
||||||
class NcaFileSystemDriver::StorageOption {
|
// class NcaFileSystemDriver::StorageOption {
|
||||||
private:
|
// private:
|
||||||
friend class NcaFileSystemDriver;
|
// friend class NcaFileSystemDriver;
|
||||||
private:
|
// private:
|
||||||
const s32 m_fs_index;
|
// const s32 m_fs_index;
|
||||||
NcaFsHeaderReader * const m_header_reader;
|
// NcaFsHeaderReader * const m_header_reader;
|
||||||
fs::IStorage *m_data_storage;
|
// fs::IStorage *m_data_storage;
|
||||||
s64 m_data_storage_size;
|
// s64 m_data_storage_size;
|
||||||
fs::IStorage *m_aes_ctr_ex_table_storage;
|
// fs::IStorage *m_aes_ctr_ex_table_storage;
|
||||||
AesCtrCounterExtendedStorage *m_aes_ctr_ex_storage_raw;
|
// AesCtrCounterExtendedStorage *m_aes_ctr_ex_storage_raw;
|
||||||
fs::IStorage *m_aes_ctr_ex_storage;
|
// fs::IStorage *m_aes_ctr_ex_storage;
|
||||||
IndirectStorage *m_indirect_storage;
|
// IndirectStorage *m_indirect_storage;
|
||||||
SparseStorage *m_sparse_storage;
|
// SparseStorage *m_sparse_storage;
|
||||||
public:
|
// 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() {
|
// 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);
|
// 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() {
|
// 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(m_header_reader != nullptr);
|
||||||
AMS_ASSERT(0 <= index && index < NcaHeader::FsCountMax);
|
// AMS_ASSERT(0 <= index && index < NcaHeader::FsCountMax);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
s32 GetFsIndex() const { return m_fs_index; }
|
// s32 GetFsIndex() const { return m_fs_index; }
|
||||||
NcaFsHeaderReader &GetHeaderReader() { return *m_header_reader; }
|
// NcaFsHeaderReader &GetHeaderReader() { return *m_header_reader; }
|
||||||
const NcaFsHeaderReader &GetHeaderReader() const { 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::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 *GetAesCtrExTableStorage() const { return m_aes_ctr_ex_table_storage; }
|
||||||
fs::IStorage *GetAesCtrExStorage() const { return m_aes_ctr_ex_storage; }
|
// fs::IStorage *GetAesCtrExStorage() const { return m_aes_ctr_ex_storage; }
|
||||||
AesCtrCounterExtendedStorage *GetAesCtrExStorageRaw() const { return m_aes_ctr_ex_storage_raw; }
|
// AesCtrCounterExtendedStorage *GetAesCtrExStorageRaw() const { return m_aes_ctr_ex_storage_raw; }
|
||||||
IndirectStorage *GetIndirectStorage() const { return m_indirect_storage; }
|
// IndirectStorage *GetIndirectStorage() const { return m_indirect_storage; }
|
||||||
SparseStorage *GetSparseStorage() const { return m_sparse_storage; }
|
// SparseStorage *GetSparseStorage() const { return m_sparse_storage; }
|
||||||
private:
|
// private:
|
||||||
void SetDataStorage(fs::IStorage *storage, s64 size) {
|
// void SetDataStorage(fs::IStorage *storage, s64 size) {
|
||||||
AMS_ASSERT(storage != nullptr);
|
// AMS_ASSERT(storage != nullptr);
|
||||||
AMS_ASSERT(size >= 0);
|
// AMS_ASSERT(size >= 0);
|
||||||
m_data_storage = storage;
|
// m_data_storage = storage;
|
||||||
m_data_storage_size = size;
|
// m_data_storage_size = size;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
void SetAesCtrExTableStorage(fs::IStorage *storage) { AMS_ASSERT(storage != nullptr); m_aes_ctr_ex_table_storage = storage; }
|
// 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 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 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 SetIndirectStorage(IndirectStorage *storage) { AMS_ASSERT(storage != nullptr); m_indirect_storage = storage; }
|
||||||
void SetSparseStorage(SparseStorage *storage) { AMS_ASSERT(storage != nullptr); m_sparse_storage = storage; }
|
// void SetSparseStorage(SparseStorage *storage) { AMS_ASSERT(storage != nullptr); m_sparse_storage = storage; }
|
||||||
};
|
// };
|
||||||
|
//
|
||||||
class NcaFileSystemDriver::StorageOptionWithHeaderReader : public NcaFileSystemDriver::StorageOption {
|
// class NcaFileSystemDriver::StorageOptionWithHeaderReader : public NcaFileSystemDriver::StorageOption {
|
||||||
private:
|
// private:
|
||||||
NcaFsHeaderReader m_header_reader_data;
|
// NcaFsHeaderReader m_header_reader_data;
|
||||||
public:
|
// public:
|
||||||
explicit StorageOptionWithHeaderReader(s32 index) : StorageOption(std::addressof(m_header_reader_data), index) { /* ... */ }
|
// explicit StorageOptionWithHeaderReader(s32 index) : StorageOption(std::addressof(m_header_reader_data), index) { /* ... */ }
|
||||||
};
|
// };
|
||||||
|
//
|
||||||
class NcaFileSystemDriver::BaseStorage {
|
// class NcaFileSystemDriver::BaseStorage {
|
||||||
private:
|
// private:
|
||||||
std::unique_ptr<fs::IStorage> m_storage;
|
// std::unique_ptr<fs::IStorage> m_storage;
|
||||||
fs::SubStorage m_sub_storage;
|
// fs::SubStorage m_sub_storage;
|
||||||
s64 m_storage_offset;
|
// s64 m_storage_offset;
|
||||||
NcaAesCtrUpperIv m_aes_ctr_upper_iv;
|
// NcaAesCtrUpperIv m_aes_ctr_upper_iv;
|
||||||
public:
|
// public:
|
||||||
BaseStorage() : m_storage(), m_sub_storage(), m_storage_offset(0) {
|
// BaseStorage() : m_storage(), m_sub_storage(), m_storage_offset(0) {
|
||||||
m_aes_ctr_upper_iv.value = 0;
|
// m_aes_ctr_upper_iv.value = 0;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
explicit BaseStorage(const fs::SubStorage &ss) : m_storage(), m_sub_storage(ss), m_storage_offset(0) {
|
// explicit BaseStorage(const fs::SubStorage &ss) : m_storage(), m_sub_storage(ss), m_storage_offset(0) {
|
||||||
m_aes_ctr_upper_iv.value = 0;
|
// m_aes_ctr_upper_iv.value = 0;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
template<typename T>
|
// template<typename T>
|
||||||
BaseStorage(T s, s64 offset, s64 size) : m_storage(), m_sub_storage(s, offset, size), m_storage_offset(0) {
|
// 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;
|
// m_aes_ctr_upper_iv.value = 0;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
void SetStorage(std::unique_ptr<fs::IStorage> &&storage) {
|
// void SetStorage(std::unique_ptr<fs::IStorage> &&storage) {
|
||||||
m_storage = std::move(storage);
|
// m_storage = std::move(storage);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
template<typename T>
|
// template<typename T>
|
||||||
void SetStorage(T storage, s64 offset, s64 size) {
|
// void SetStorage(T storage, s64 offset, s64 size) {
|
||||||
m_sub_storage = fs::SubStorage(storage, offset, size);
|
// m_sub_storage = fs::SubStorage(storage, offset, size);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
std::unique_ptr<fs::IStorage> MakeStorage() {
|
// std::unique_ptr<fs::IStorage> MakeStorage() {
|
||||||
if (m_storage != nullptr) {
|
// if (m_storage != nullptr) {
|
||||||
return std::move(m_storage);
|
// return std::move(m_storage);
|
||||||
}
|
// }
|
||||||
return std::make_unique<fs::SubStorage>(m_sub_storage);
|
// return std::make_unique<fs::SubStorage>(m_sub_storage);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
std::unique_ptr<fs::IStorage> GetStorage() {
|
// std::unique_ptr<fs::IStorage> GetStorage() {
|
||||||
return std::move(m_storage);
|
// return std::move(m_storage);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Result GetSubStorage(fs::SubStorage *out, s64 offset, s64 size) {
|
// Result GetSubStorage(fs::SubStorage *out, s64 offset, s64 size) {
|
||||||
s64 storage_size = 0;
|
// s64 storage_size = 0;
|
||||||
|
//
|
||||||
if (m_storage != nullptr) {
|
// if (m_storage != nullptr) {
|
||||||
R_TRY(m_storage->GetSize(std::addressof(storage_size)));
|
// R_TRY(m_storage->GetSize(std::addressof(storage_size)));
|
||||||
R_UNLESS(offset + size <= storage_size, fs::ResultNcaBaseStorageOutOfRangeA());
|
// R_UNLESS(offset + size <= storage_size, fs::ResultNcaBaseStorageOutOfRangeA());
|
||||||
*out = fs::SubStorage(m_storage.get(), offset, size);
|
// *out = fs::SubStorage(m_storage.get(), offset, size);
|
||||||
} else {
|
// } else {
|
||||||
R_TRY(m_sub_storage.GetSize(std::addressof(storage_size)));
|
// R_TRY(m_sub_storage.GetSize(std::addressof(storage_size)));
|
||||||
R_UNLESS(offset + size <= storage_size, fs::ResultNcaBaseStorageOutOfRangeA());
|
// R_UNLESS(offset + size <= storage_size, fs::ResultNcaBaseStorageOutOfRangeA());
|
||||||
*out = fs::SubStorage(std::addressof(m_sub_storage), offset, size);
|
// *out = fs::SubStorage(std::addressof(m_sub_storage), offset, size);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
return ResultSuccess();
|
// return ResultSuccess();
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
void SetStorageOffset(s64 offset) {
|
// void SetStorageOffset(s64 offset) {
|
||||||
m_storage_offset = offset;
|
// m_storage_offset = offset;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
s64 GetStorageOffset() const {
|
// s64 GetStorageOffset() const {
|
||||||
return m_storage_offset;
|
// return m_storage_offset;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
void SetAesCtrUpperIv(NcaAesCtrUpperIv v) {
|
// void SetAesCtrUpperIv(NcaAesCtrUpperIv v) {
|
||||||
m_aes_ctr_upper_iv = v;
|
// m_aes_ctr_upper_iv = v;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
const NcaAesCtrUpperIv GetAesCtrUpperIv() const {
|
// const NcaAesCtrUpperIv GetAesCtrUpperIv() const {
|
||||||
return m_aes_ctr_upper_iv;
|
// return m_aes_ctr_upper_iv;
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,6 +181,11 @@ namespace ams::fssystem {
|
||||||
};
|
};
|
||||||
static_assert(util::is_pod<NcaSparseInfo>::value);
|
static_assert(util::is_pod<NcaSparseInfo>::value);
|
||||||
|
|
||||||
|
struct NcaCompressionInfo {
|
||||||
|
NcaBucketInfo bucket;
|
||||||
|
};
|
||||||
|
static_assert(util::is_pod<NcaCompressionInfo>::value);
|
||||||
|
|
||||||
struct NcaFsHeader {
|
struct NcaFsHeader {
|
||||||
static constexpr size_t Size = 0x200;
|
static constexpr size_t Size = 0x200;
|
||||||
static constexpr size_t HashDataOffset = 0x8;
|
static constexpr size_t HashDataOffset = 0x8;
|
||||||
|
@ -263,7 +268,8 @@ namespace ams::fssystem {
|
||||||
NcaPatchInfo patch_info;
|
NcaPatchInfo patch_info;
|
||||||
NcaAesCtrUpperIv aes_ctr_upper_iv;
|
NcaAesCtrUpperIv aes_ctr_upper_iv;
|
||||||
NcaSparseInfo sparse_info;
|
NcaSparseInfo sparse_info;
|
||||||
u8 pad[0x88];
|
NcaCompressionInfo compression_info;
|
||||||
|
u8 pad[0x68];
|
||||||
};
|
};
|
||||||
static_assert(sizeof(NcaFsHeader) == NcaFsHeader::Size);
|
static_assert(sizeof(NcaFsHeader) == NcaFsHeader::Size);
|
||||||
static_assert(util::is_pod<NcaFsHeader>::value);
|
static_assert(util::is_pod<NcaFsHeader>::value);
|
||||||
|
|
|
@ -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) { /* ... */ }
|
HierarchicalIntegrityVerificationStorage() : m_buffers(nullptr), m_mutex(nullptr), m_data_size(-1), m_is_written_for_rollback(false) { /* ... */ }
|
||||||
virtual ~HierarchicalIntegrityVerificationStorage() override { this->Finalize(); }
|
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();
|
void Finalize();
|
||||||
|
|
||||||
virtual Result Read(s64 offset, void *buffer, size_t size) override;
|
virtual Result Read(s64 offset, void *buffer, size_t size) override;
|
||||||
|
|
|
@ -47,11 +47,12 @@ namespace ams::fssystem::save {
|
||||||
fs::HashSalt m_salt;
|
fs::HashSalt m_salt;
|
||||||
bool m_is_real_data;
|
bool m_is_real_data;
|
||||||
fs::StorageType m_storage_type;
|
fs::StorageType m_storage_type;
|
||||||
|
fssystem::IHash256GeneratorFactory *m_hash_generator_factory;
|
||||||
public:
|
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) { /* ... */ }
|
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(); }
|
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();
|
void Finalize();
|
||||||
|
|
||||||
virtual Result Read(s64 offset, void *buffer, size_t size) override;
|
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;
|
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;
|
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 {
|
s64 GetBlockSize() const {
|
||||||
return m_verification_block_size;
|
return m_verification_block_size;
|
||||||
|
@ -73,9 +77,11 @@ namespace ams::fssystem::save {
|
||||||
private:
|
private:
|
||||||
Result ReadBlockSignature(void *dst, size_t dst_size, s64 offset, size_t size);
|
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 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<fssystem::IHash256Generator> &generator);
|
||||||
|
|
||||||
void CalcBlockHash(BlockHash *out, const void *buffer) const {
|
void CalcBlockHash(BlockHash *out, const void *buffer, size_t block_size, std::unique_ptr<fssystem::IHash256Generator> &generator) const;
|
||||||
|
|
||||||
|
void CalcBlockHash(BlockHash *out, const void *buffer, std::unique_ptr<fssystem::IHash256Generator> &generator) const {
|
||||||
return this->CalcBlockHash(out, buffer, static_cast<size_t>(m_verification_block_size));
|
return this->CalcBlockHash(out, buffer, static_cast<size_t>(m_verification_block_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,124 +17,33 @@
|
||||||
|
|
||||||
namespace ams::fssrv::fscreator {
|
namespace ams::fssrv::fscreator {
|
||||||
|
|
||||||
Result StorageOnNcaCreator::VerifyAcid(fs::fsa::IFileSystem *fs, fssystem::NcaReader *nca_reader) {
|
Result StorageOnNcaCreator::Create(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fssystem::IAsynchronousAccessSplitter> *out_splitter, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr<fssystem::NcaReader> nca_reader, s32 index) {
|
||||||
/* Open the npdm. */
|
|
||||||
constexpr const char MetaFilePath[] = "/main.npdm";
|
|
||||||
std::unique_ptr<fs::fsa::IFile> 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<u8 *>(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<size_t>(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<s32>(AcidSignTargetSizeOffset + sizeof(s32)), fs::ResultInvalidAcidSize());
|
|
||||||
const s32 acid_sign_target_size = *reinterpret_cast<const s32 *>(acid + AcidSignTargetSizeOffset);
|
|
||||||
|
|
||||||
/* Validate the sign target size. */
|
|
||||||
R_UNLESS(acid_size >= static_cast<s32>(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<size_t>(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<char *>(acid) + HeaderSign2KeyOffset, HeaderSign2KeySize));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result StorageOnNcaCreator::Create(std::shared_ptr<fs::IStorage> *out, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr<fssystem::NcaReader> nca_reader, s32 index, bool verify_header_sign_2) {
|
|
||||||
/* Create a fs driver. */
|
/* 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. */
|
/* Open the storage. */
|
||||||
std::shared_ptr<fs::IStorage> storage;
|
std::shared_ptr<fs::IStorage> storage;
|
||||||
R_TRY(nca_fs_driver.OpenStorage(std::addressof(storage), out_header_reader, index));
|
std::shared_ptr<fssystem::IAsynchronousAccessSplitter> splitter;
|
||||||
|
R_TRY(nca_fs_driver.OpenStorage(std::addressof(storage), std::addressof(splitter), 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()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the out storage. */
|
/* Set the out storage. */
|
||||||
*out = std::move(storage);
|
*out = std::move(storage);
|
||||||
|
*out_splitter = std::move(splitter);
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result StorageOnNcaCreator::CreateWithPatch(std::shared_ptr<fs::IStorage> *out, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr<fssystem::NcaReader> original_nca_reader, std::shared_ptr<fssystem::NcaReader> current_nca_reader, s32 index, bool verify_header_sign_2) {
|
Result StorageOnNcaCreator::CreateWithPatch(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fssystem::IAsynchronousAccessSplitter> *out_splitter, fssystem::NcaFsHeaderReader *out_header_reader, std::shared_ptr<fssystem::NcaReader> original_nca_reader, std::shared_ptr<fssystem::NcaReader> current_nca_reader, s32 index) {
|
||||||
/* Create a fs driver. */
|
/* 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. */
|
/* Open the storage. */
|
||||||
std::shared_ptr<fs::IStorage> storage;
|
std::shared_ptr<fs::IStorage> storage;
|
||||||
R_TRY(nca_fs_driver.OpenStorage(std::addressof(storage), out_header_reader, index));
|
std::shared_ptr<fssystem::IAsynchronousAccessSplitter> splitter;
|
||||||
|
R_TRY(nca_fs_driver.OpenStorage(std::addressof(storage), std::addressof(splitter), 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()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the out storage. */
|
/* Set the out storage. */
|
||||||
*out = std::move(storage);
|
*out = std::move(storage);
|
||||||
|
*out_splitter = std::move(splitter);
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,23 +53,11 @@ namespace ams::fssrv::fscreator {
|
||||||
R_UNLESS(reader != nullptr, fs::ResultAllocationFailureInStorageOnNcaCreatorB());
|
R_UNLESS(reader != nullptr, fs::ResultAllocationFailureInStorageOnNcaCreatorB());
|
||||||
|
|
||||||
/* Initialize the reader. */
|
/* 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. */
|
/* Set the output. */
|
||||||
*out = std::move(reader);
|
*out = std::move(reader);
|
||||||
return ResultSuccess();
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,7 +174,7 @@ namespace ams::fssystem {
|
||||||
NcaAesCtrUpperIv upper_iv = { .part = { .generation = static_cast<u32>(cur_entry.generation), .secure_value = m_secure_value } };
|
NcaAesCtrUpperIv upper_iv = { .part = { .generation = static_cast<u32>(cur_entry.generation), .secure_value = m_secure_value } };
|
||||||
|
|
||||||
u8 iv[IvSize];
|
u8 iv[IvSize];
|
||||||
AesCtrStorage::MakeIv(iv, IvSize, upper_iv.value, counter_offset);
|
AesCtrStorageByPointer::MakeIv(iv, IvSize, upper_iv.value, counter_offset);
|
||||||
|
|
||||||
/* Decrypt. */
|
/* Decrypt. */
|
||||||
m_decryptor->Decrypt(cur_data, cur_size, m_key, KeySize, iv, IvSize);
|
m_decryptor->Decrypt(cur_data, cur_size, m_key, KeySize, iv, IvSize);
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
|
|
||||||
namespace ams::fssystem {
|
namespace ams::fssystem {
|
||||||
|
|
||||||
void AesCtrStorage::MakeIv(void *dst, size_t dst_size, u64 upper, s64 offset) {
|
template<typename BasePointer>
|
||||||
/* TODO: util::BytePtr? */
|
void AesCtrStorage<BasePointer>::MakeIv(void *dst, size_t dst_size, u64 upper, s64 offset) {
|
||||||
AMS_ASSERT(dst != nullptr);
|
AMS_ASSERT(dst != nullptr);
|
||||||
AMS_ASSERT(dst_size == IvSize);
|
AMS_ASSERT(dst_size == IvSize);
|
||||||
AMS_ASSERT(offset >= 0);
|
AMS_ASSERT(offset >= 0);
|
||||||
|
@ -30,7 +30,8 @@ namespace ams::fssystem {
|
||||||
util::StoreBigEndian(reinterpret_cast<s64 *>(out_addr + sizeof(u64)), static_cast<s64>(offset / BlockSize));
|
util::StoreBigEndian(reinterpret_cast<s64 *>(out_addr + sizeof(u64)), static_cast<s64>(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<typename BasePointer>
|
||||||
|
AesCtrStorage<BasePointer>::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(base != nullptr);
|
||||||
AMS_ASSERT(key != nullptr);
|
AMS_ASSERT(key != nullptr);
|
||||||
AMS_ASSERT(iv != nullptr);
|
AMS_ASSERT(iv != nullptr);
|
||||||
|
@ -42,7 +43,8 @@ namespace ams::fssystem {
|
||||||
std::memcpy(m_iv, iv, IvSize);
|
std::memcpy(m_iv, iv, IvSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result AesCtrStorage::Read(s64 offset, void *buffer, size_t size) {
|
template<typename BasePointer>
|
||||||
|
Result AesCtrStorage<BasePointer>::Read(s64 offset, void *buffer, size_t size) {
|
||||||
/* Allow zero-size reads. */
|
/* Allow zero-size reads. */
|
||||||
R_SUCCEED_IF(size == 0);
|
R_SUCCEED_IF(size == 0);
|
||||||
|
|
||||||
|
@ -71,7 +73,8 @@ namespace ams::fssystem {
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result AesCtrStorage::Write(s64 offset, const void *buffer, size_t size) {
|
template<typename BasePointer>
|
||||||
|
Result AesCtrStorage<BasePointer>::Write(s64 offset, const void *buffer, size_t size) {
|
||||||
/* Allow zero-size writes. */
|
/* Allow zero-size writes. */
|
||||||
R_SUCCEED_IF(size == 0);
|
R_SUCCEED_IF(size == 0);
|
||||||
|
|
||||||
|
@ -124,20 +127,24 @@ namespace ams::fssystem {
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result AesCtrStorage::Flush() {
|
template<typename BasePointer>
|
||||||
|
Result AesCtrStorage<BasePointer>::Flush() {
|
||||||
return m_base_storage->Flush();
|
return m_base_storage->Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result AesCtrStorage::SetSize(s64 size) {
|
template<typename BasePointer>
|
||||||
|
Result AesCtrStorage<BasePointer>::SetSize(s64 size) {
|
||||||
AMS_UNUSED(size);
|
AMS_UNUSED(size);
|
||||||
return fs::ResultUnsupportedOperationInAesCtrStorageA();
|
return fs::ResultUnsupportedOperationInAesCtrStorageA();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result AesCtrStorage::GetSize(s64 *out) {
|
template<typename BasePointer>
|
||||||
|
Result AesCtrStorage<BasePointer>::GetSize(s64 *out) {
|
||||||
return m_base_storage->GetSize(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<typename BasePointer>
|
||||||
|
Result AesCtrStorage<BasePointer>::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. */
|
/* Handle the zero size case. */
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
if (op_id == fs::OperationId::QueryRange) {
|
if (op_id == fs::OperationId::QueryRange) {
|
||||||
|
@ -179,4 +186,7 @@ namespace ams::fssystem {
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template class AesCtrStorage<fs::IStorage *>;
|
||||||
|
template class AesCtrStorage<std::shared_ptr<fs::IStorage>>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,20 @@
|
||||||
|
|
||||||
namespace ams::fssystem {
|
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<typename BasePointer>
|
||||||
|
void AesXtsStorage<BasePointer>::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<uintptr_t>(dst);
|
||||||
|
|
||||||
|
util::StoreBigEndian<s64>(reinterpret_cast<s64 *>(out_addr + sizeof(s64)), offset / block_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename BasePointer>
|
||||||
|
AesXtsStorage<BasePointer>::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(base != nullptr);
|
||||||
AMS_ASSERT(key1 != nullptr);
|
AMS_ASSERT(key1 != nullptr);
|
||||||
AMS_ASSERT(key2 != nullptr);
|
AMS_ASSERT(key2 != nullptr);
|
||||||
|
@ -32,7 +45,8 @@ namespace ams::fssystem {
|
||||||
std::memcpy(m_iv, iv, IvSize);
|
std::memcpy(m_iv, iv, IvSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result AesXtsStorage::Read(s64 offset, void *buffer, size_t size) {
|
template<typename BasePointer>
|
||||||
|
Result AesXtsStorage<BasePointer>::Read(s64 offset, void *buffer, size_t size) {
|
||||||
/* Allow zero-size reads. */
|
/* Allow zero-size reads. */
|
||||||
R_SUCCEED_IF(size == 0);
|
R_SUCCEED_IF(size == 0);
|
||||||
|
|
||||||
|
@ -97,7 +111,8 @@ namespace ams::fssystem {
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result AesXtsStorage::Write(s64 offset, const void *buffer, size_t size) {
|
template<typename BasePointer>
|
||||||
|
Result AesXtsStorage<BasePointer>::Write(s64 offset, const void *buffer, size_t size) {
|
||||||
/* Allow zero-size writes. */
|
/* Allow zero-size writes. */
|
||||||
R_SUCCEED_IF(size == 0);
|
R_SUCCEED_IF(size == 0);
|
||||||
|
|
||||||
|
@ -194,21 +209,25 @@ namespace ams::fssystem {
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result AesXtsStorage::Flush() {
|
template<typename BasePointer>
|
||||||
|
Result AesXtsStorage<BasePointer>::Flush() {
|
||||||
return m_base_storage->Flush();
|
return m_base_storage->Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result AesXtsStorage::SetSize(s64 size) {
|
template<typename BasePointer>
|
||||||
|
Result AesXtsStorage<BasePointer>::SetSize(s64 size) {
|
||||||
R_UNLESS(util::IsAligned(size, AesBlockSize), fs::ResultUnexpectedInAesXtsStorageA());
|
R_UNLESS(util::IsAligned(size, AesBlockSize), fs::ResultUnexpectedInAesXtsStorageA());
|
||||||
|
|
||||||
return m_base_storage->SetSize(size);
|
return m_base_storage->SetSize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result AesXtsStorage::GetSize(s64 *out) {
|
template<typename BasePointer>
|
||||||
|
Result AesXtsStorage<BasePointer>::GetSize(s64 *out) {
|
||||||
return m_base_storage->GetSize(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<typename BasePointer>
|
||||||
|
Result AesXtsStorage<BasePointer>::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. */
|
/* Handle the zero size case. */
|
||||||
R_SUCCEED_IF(size == 0);
|
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);
|
return m_base_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template class AesXtsStorage<fs::IStorage *>;
|
||||||
|
template class AesXtsStorage<std::shared_ptr<fs::IStorage>>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,7 +143,7 @@ namespace ams::fssystem {
|
||||||
/* TODO FS-REIMPL: Revise for accuracy. */
|
/* TODO FS-REIMPL: Revise for accuracy. */
|
||||||
util::ConstructAt(g_rom_fs_creator, GetPointer(g_allocator));
|
util::ConstructAt(g_rom_fs_creator, GetPointer(g_allocator));
|
||||||
util::ConstructAt(g_partition_fs_creator);
|
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. */
|
/* TODO FS-REIMPL: Initialize other creators. */
|
||||||
|
|
||||||
|
|
|
@ -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<typename BaseStorageType>
|
||||||
|
Result HierarchicalSha256Storage<BaseStorageType>::Initialize(BaseStorageType *base_storages, s32 layer_count, size_t htbs, void *hash_buf, size_t hash_buf_size, fssystem::IHash256GeneratorFactory *hgf) {
|
||||||
/* Validate preconditions. */
|
/* Validate preconditions. */
|
||||||
AMS_ASSERT(layer_count == LayerCount);
|
AMS_ASSERT(layer_count == LayerCount);
|
||||||
AMS_ASSERT(util::IsPowerOfTwo(htbs));
|
AMS_ASSERT(util::IsPowerOfTwo(htbs));
|
||||||
AMS_ASSERT(hash_buf != nullptr);
|
AMS_ASSERT(hash_buf != nullptr);
|
||||||
|
AMS_ASSERT(hgf != nullptr);
|
||||||
AMS_UNUSED(layer_count);
|
AMS_UNUSED(layer_count);
|
||||||
|
|
||||||
/* Set size tracking members. */
|
/* Set size tracking members. */
|
||||||
m_hash_target_block_size = htbs;
|
m_hash_target_block_size = htbs;
|
||||||
m_log_size_ratio = Log2(m_hash_target_block_size / HashSize);
|
m_log_size_ratio = Log2(m_hash_target_block_size / HashSize);
|
||||||
|
m_hash_generator_factory = hgf;
|
||||||
|
|
||||||
/* Get the base storage size. */
|
/* Get the base storage size. */
|
||||||
R_TRY(base_storages[2]->GetSize(std::addressof(m_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. */
|
/* Calculate and verify the master hash. */
|
||||||
u8 calc_hash[HashSize];
|
u8 calc_hash[HashSize];
|
||||||
crypto::GenerateSha256Hash(calc_hash, sizeof(calc_hash), m_hash_buffer, static_cast<size_t>(hash_storage_size));
|
m_hash_generator_factory->GenerateHash(calc_hash, sizeof(calc_hash), m_hash_buffer, static_cast<size_t>(hash_storage_size));
|
||||||
R_UNLESS(crypto::IsSameBytes(master_hash, calc_hash, HashSize), fs::ResultHierarchicalSha256HashVerificationFailed());
|
R_UNLESS(crypto::IsSameBytes(master_hash, calc_hash, HashSize), fs::ResultHierarchicalSha256HashVerificationFailed());
|
||||||
|
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result HierarchicalSha256Storage::Read(s64 offset, void *buffer, size_t size) {
|
template<typename BaseStorageType>
|
||||||
|
Result HierarchicalSha256Storage<BaseStorageType>::Read(s64 offset, void *buffer, size_t size) {
|
||||||
/* Succeed if zero-size. */
|
/* Succeed if zero-size. */
|
||||||
R_SUCCEED_IF(size == 0);
|
R_SUCCEED_IF(size == 0);
|
||||||
|
|
||||||
|
@ -103,7 +107,7 @@ namespace ams::fssystem {
|
||||||
/* Generate the hash of the region we're validating. */
|
/* Generate the hash of the region we're validating. */
|
||||||
u8 hash[HashSize];
|
u8 hash[HashSize];
|
||||||
const auto cur_size = static_cast<size_t>(std::min<s64>(m_hash_target_block_size, remaining_size));
|
const auto cur_size = static_cast<size_t>(std::min<s64>(m_hash_target_block_size, remaining_size));
|
||||||
crypto::GenerateSha256Hash(hash, sizeof(hash), static_cast<u8 *>(buffer) + (cur_offset - offset), cur_size);
|
m_hash_generator_factory->GenerateHash(hash, sizeof(hash), static_cast<u8 *>(buffer) + (cur_offset - offset), cur_size);
|
||||||
|
|
||||||
AMS_ASSERT(static_cast<size_t>(cur_offset >> m_log_size_ratio) < m_hash_buffer_size);
|
AMS_ASSERT(static_cast<size_t>(cur_offset >> m_log_size_ratio) < m_hash_buffer_size);
|
||||||
|
|
||||||
|
@ -125,7 +129,8 @@ namespace ams::fssystem {
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result HierarchicalSha256Storage::Write(s64 offset, const void *buffer, size_t size) {
|
template<typename BaseStorageType>
|
||||||
|
Result HierarchicalSha256Storage<BaseStorageType>::Write(s64 offset, const void *buffer, size_t size) {
|
||||||
/* Succeed if zero-size. */
|
/* Succeed if zero-size. */
|
||||||
R_SUCCEED_IF(size == 0);
|
R_SUCCEED_IF(size == 0);
|
||||||
|
|
||||||
|
@ -147,7 +152,7 @@ namespace ams::fssystem {
|
||||||
{
|
{
|
||||||
/* Temporarily increase our thread priority. */
|
/* Temporarily increase our thread priority. */
|
||||||
ScopedThreadPriorityChanger cp(+1, ScopedThreadPriorityChanger::Mode::Relative);
|
ScopedThreadPriorityChanger cp(+1, ScopedThreadPriorityChanger::Mode::Relative);
|
||||||
crypto::GenerateSha256Hash(hash, sizeof(hash), static_cast<const u8 *>(buffer) + (cur_offset - offset), cur_size);
|
m_hash_generator_factory->GenerateHash(hash, sizeof(hash), static_cast<const u8 *>(buffer) + (cur_offset - offset), cur_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write the data. */
|
/* Write the data. */
|
||||||
|
@ -167,7 +172,11 @@ namespace ams::fssystem {
|
||||||
return ResultSuccess();
|
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) {
|
template<typename BaseStorageType>
|
||||||
|
Result HierarchicalSha256Storage<BaseStorageType>::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. */
|
/* Succeed if zero-size. */
|
||||||
R_SUCCEED_IF(size == 0);
|
R_SUCCEED_IF(size == 0);
|
||||||
|
|
||||||
|
@ -181,5 +190,8 @@ namespace ams::fssystem {
|
||||||
/* Operate on the base storage. */
|
/* Operate on the base storage. */
|
||||||
return m_base_storage->OperateRange(dst, dst_size, op_id, offset, reduced_size, src, src_size);
|
return m_base_storage->OperateRange(dst, dst_size, op_id, offset, reduced_size, src, src_size);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template class HierarchicalSha256Storage<fs::SubStorage>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
namespace ams::fssystem {
|
namespace ams::fssystem {
|
||||||
|
|
||||||
|
template<typename BaseStorageType>
|
||||||
class HierarchicalSha256Storage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
|
class HierarchicalSha256Storage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
|
||||||
NON_COPYABLE(HierarchicalSha256Storage);
|
NON_COPYABLE(HierarchicalSha256Storage);
|
||||||
NON_MOVEABLE(HierarchicalSha256Storage);
|
NON_MOVEABLE(HierarchicalSha256Storage);
|
||||||
|
@ -25,17 +26,18 @@ namespace ams::fssystem {
|
||||||
static constexpr s32 LayerCount = 3;
|
static constexpr s32 LayerCount = 3;
|
||||||
static constexpr size_t HashSize = crypto::Sha256Generator::HashSize;
|
static constexpr size_t HashSize = crypto::Sha256Generator::HashSize;
|
||||||
private:
|
private:
|
||||||
os::SdkMutex m_mutex;
|
BaseStorageType m_base_storage;
|
||||||
IStorage *m_base_storage;
|
|
||||||
s64 m_base_storage_size;
|
s64 m_base_storage_size;
|
||||||
char *m_hash_buffer;
|
char *m_hash_buffer;
|
||||||
size_t m_hash_buffer_size;
|
size_t m_hash_buffer_size;
|
||||||
s32 m_hash_target_block_size;
|
s32 m_hash_target_block_size;
|
||||||
s32 m_log_size_ratio;
|
s32 m_log_size_ratio;
|
||||||
|
fssystem::IHash256GeneratorFactory *m_hash_generator_factory;
|
||||||
|
os::SdkMutex m_mutex;
|
||||||
public:
|
public:
|
||||||
HierarchicalSha256Storage() : m_mutex() { /* ... */ }
|
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 Read(s64 offset, void *buffer, size_t size) override;
|
||||||
virtual Result Write(s64 offset, const 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 {
|
virtual Result Flush() override {
|
||||||
return ResultSuccess();
|
return m_base_storage->Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Result SetSize(s64 size) override {
|
virtual Result SetSize(s64 size) override {
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
namespace ams::fssystem {
|
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. */
|
/* Validate preconditions. */
|
||||||
AMS_ASSERT(bm != nullptr);
|
AMS_ASSERT(bm != nullptr);
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ namespace ams::fssystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize our integrity storage. */
|
/* 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() {
|
void IntegrityRomFsStorage::Finalize() {
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::fssystem {
|
||||||
|
|
||||||
|
class MemoryResourceBufferHoldStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
|
||||||
|
NON_COPYABLE(MemoryResourceBufferHoldStorage);
|
||||||
|
NON_MOVEABLE(MemoryResourceBufferHoldStorage);
|
||||||
|
private:
|
||||||
|
std::shared_ptr<fs::IStorage> m_storage;
|
||||||
|
MemoryResource *m_memory_resource;
|
||||||
|
void *m_buffer;
|
||||||
|
size_t m_buffer_size;
|
||||||
|
public:
|
||||||
|
MemoryResourceBufferHoldStorage(std::shared_ptr<fs::IStorage> 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -20,6 +20,7 @@ namespace ams::fssystem {
|
||||||
u8 NcaHeader::GetProperKeyGeneration() const {
|
u8 NcaHeader::GetProperKeyGeneration() const {
|
||||||
return std::max(this->key_generation, this->key_generation_2);
|
return std::max(this->key_generation, this->key_generation_2);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NcaPatchInfo::HasIndirectTable() const {
|
bool NcaPatchInfo::HasIndirectTable() const {
|
||||||
return this->indirect_size != 0;
|
return this->indirect_size != 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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_header), 0, sizeof(m_header));
|
||||||
std::memset(std::addressof(m_decryption_keys), 0, sizeof(m_decryption_keys));
|
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));
|
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<fs::IStorage> base_storage, const NcaCryptoConfiguration &crypto_cfg) {
|
Result NcaReader::Initialize(std::shared_ptr<fs::IStorage> base_storage, const NcaCryptoConfiguration &crypto_cfg, const NcaCompressionConfiguration &compression_cfg, IHash256GeneratorFactorySelector *hgf_selector) {
|
||||||
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) {
|
|
||||||
/* Validate preconditions. */
|
/* Validate preconditions. */
|
||||||
AMS_ASSERT(base_storage != nullptr);
|
AMS_ASSERT(base_storage != nullptr);
|
||||||
|
AMS_ASSERT(hgf_selector != nullptr);
|
||||||
AMS_ASSERT(m_body_storage == nullptr);
|
AMS_ASSERT(m_body_storage == nullptr);
|
||||||
|
|
||||||
|
/* Check that the crypto config is valid. */
|
||||||
R_UNLESS(crypto_cfg.generate_key != nullptr, fs::ResultInvalidArgument());
|
R_UNLESS(crypto_cfg.generate_key != nullptr, fs::ResultInvalidArgument());
|
||||||
|
|
||||||
/* Generate keys for header. */
|
/* Generate keys for header. */
|
||||||
|
using AesXtsStorageForNcaHeader = AesXtsStorageBySharedPointer;
|
||||||
|
|
||||||
u8 header_decryption_keys[NcaCryptoConfiguration::HeaderEncryptionKeyCount][NcaCryptoConfiguration::Aes128KeySize];
|
u8 header_decryption_keys[NcaCryptoConfiguration::HeaderEncryptionKeyCount][NcaCryptoConfiguration::Aes128KeySize];
|
||||||
for (size_t i = 0; i < NcaCryptoConfiguration::HeaderEncryptionKeyCount; i++) {
|
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<s32>(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<s32>(KeyType::NcaHeaderKey), crypto_cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create the header storage. */
|
/* Create the header storage. */
|
||||||
const u8 header_iv[AesXtsStorage::IvSize] = {};
|
const u8 header_iv[AesXtsStorageForNcaHeader::IvSize] = {};
|
||||||
std::unique_ptr<fs::IStorage> work_header_storage = std::make_unique<AesXtsStorage>(base_storage, header_decryption_keys[0], header_decryption_keys[1], AesXtsStorage::KeySize, header_iv, AesXtsStorage::IvSize, NcaHeader::XtsBlockSize);
|
std::unique_ptr<fs::IStorage> work_header_storage = std::make_unique<AesXtsStorageForNcaHeader>(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());
|
R_UNLESS(work_header_storage != nullptr, fs::ResultAllocationFailureInNcaReaderA());
|
||||||
|
|
||||||
/* Read the header. */
|
/* Read the header. */
|
||||||
R_TRY(work_header_storage->Read(0, std::addressof(m_header), sizeof(m_header)));
|
R_TRY(work_header_storage->Read(0, std::addressof(m_header), sizeof(m_header)));
|
||||||
|
|
||||||
/* Validate the magic. */
|
/* 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. */
|
/* If we're not allowed to use plaintext headers, stop here. */
|
||||||
R_UNLESS(crypto_cfg.is_plaintext_header_available, magic_result);
|
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));
|
work_header_storage.reset(new fs::SubStorage(base_storage, 0, base_storage_size));
|
||||||
R_UNLESS(work_header_storage != nullptr, fs::ResultAllocationFailureInNcaReaderA());
|
R_UNLESS(work_header_storage != nullptr, fs::ResultAllocationFailureInNcaReaderA());
|
||||||
|
|
||||||
|
/* Set encryption type as plaintext. */
|
||||||
m_header_encryption_type = NcaHeader::EncryptionType::None;
|
m_header_encryption_type = NcaHeader::EncryptionType::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,6 +102,7 @@ namespace ams::fssystem {
|
||||||
const size_t exp_size = NcaCryptoConfiguration::Rsa2048KeyPublicExponentSize;
|
const size_t exp_size = NcaCryptoConfiguration::Rsa2048KeyPublicExponentSize;
|
||||||
const u8 *msg = static_cast<const u8 *>(static_cast<const void *>(std::addressof(m_header.magic)));
|
const u8 *msg = static_cast<const u8 *>(static_cast<const void *>(std::addressof(m_header.magic)));
|
||||||
const size_t msg_size = NcaHeader::Size - NcaHeader::HeaderSignSize * NcaHeader::HeaderSignCount;
|
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);
|
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());
|
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 = crypto_cfg.decrypt_aes_ctr;
|
||||||
m_decrypt_aes_ctr_external = crypto_cfg.decrypt_aes_ctr_external;
|
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. */
|
/* Set our storages. */
|
||||||
m_header_storage = std::move(work_header_storage);
|
m_header_storage = std::move(work_header_storage);
|
||||||
m_body_storage = base_storage;
|
m_body_storage = std::move(base_storage);
|
||||||
|
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::IStorage *NcaReader::GetBodyStorage() {
|
std::shared_ptr<fs::IStorage> NcaReader::GetSharedBodyStorage() {
|
||||||
|
AMS_ASSERT(m_body_storage != nullptr);
|
||||||
return m_body_storage;
|
return m_body_storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,7 +280,7 @@ namespace ams::fssystem {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NcaReader::HasInternalDecryptionKeyForAesHardwareSpeedEmulation() const {
|
bool NcaReader::HasInternalDecryptionKeyForAesHw() const {
|
||||||
constexpr const u8 ZeroKey[crypto::AesDecryptor128::KeySize] = {};
|
constexpr const u8 ZeroKey[crypto::AesDecryptor128::KeySize] = {};
|
||||||
return !crypto::IsSameBytes(ZeroKey, this->GetDecryptionKey(NcaHeader::DecryptionKey_AesCtrHw), 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;
|
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 {
|
NcaHeader::EncryptionType NcaReader::GetEncryptionType() const {
|
||||||
return m_header_encryption_type;
|
return m_header_encryption_type;
|
||||||
}
|
}
|
||||||
|
@ -331,20 +351,19 @@ namespace ams::fssystem {
|
||||||
return m_header_storage->Read(offset, dst, sizeof(NcaFsHeader));
|
return m_header_storage->Read(offset, dst, sizeof(NcaFsHeader));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result NcaReader::VerifyHeaderSign2(const void *mod, size_t mod_size) {
|
void NcaReader::GetHeaderSign2(void *dst, size_t size) {
|
||||||
AMS_ASSERT(m_body_storage != nullptr);
|
AMS_ASSERT(dst != nullptr);
|
||||||
constexpr const u8 HeaderSign2KeyPublicExponent[] = { 0x01, 0x00, 0x01 };
|
AMS_ASSERT(size == NcaHeader::HeaderSignSize);
|
||||||
|
|
||||||
const u8 *sig = m_header.header_sign_2;
|
std::memcpy(dst, m_header.header_sign_2, size);
|
||||||
const size_t sig_size = NcaHeader::HeaderSignSize;
|
}
|
||||||
const u8 *exp = HeaderSign2KeyPublicExponent;
|
|
||||||
const size_t exp_size = sizeof(HeaderSign2KeyPublicExponent);
|
|
||||||
const u8 *msg = static_cast<const u8 *>(static_cast<const void *>(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());
|
|
||||||
|
|
||||||
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<const void *>(std::addressof(m_header.magic)), NcaHeader::Size - NcaHeader::HeaderSignSize * NcaHeader::HeaderSignCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result NcaFsHeaderReader::Initialize(const NcaReader &reader, s32 index) {
|
Result NcaFsHeaderReader::Initialize(const NcaReader &reader, s32 index) {
|
||||||
|
@ -440,4 +459,19 @@ namespace ams::fssystem {
|
||||||
return m_data.sparse_info;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,10 +27,10 @@ namespace ams::fssystem {
|
||||||
private:
|
private:
|
||||||
os::SdkMutex m_mutex;
|
os::SdkMutex m_mutex;
|
||||||
BlockCache m_block_cache;
|
BlockCache m_block_cache;
|
||||||
fs::IStorage * const m_base_storage;
|
std::shared_ptr<fs::IStorage> m_base_storage;
|
||||||
s32 m_block_size;
|
s32 m_block_size;
|
||||||
public:
|
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<fs::IStorage> 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. */
|
/* Validate preconditions. */
|
||||||
AMS_ASSERT(buf_size >= static_cast<size_t>(m_block_size));
|
AMS_ASSERT(buf_size >= static_cast<size_t>(m_block_size));
|
||||||
AMS_ASSERT(util::IsPowerOfTwo(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 {
|
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 invalidating cache, invalidate our blocks. */
|
||||||
if (op_id == fs::OperationId::Invalidate) {
|
if (op_id == fs::OperationId::Invalidate) {
|
||||||
R_UNLESS(offset >= 0, fs::ResultInvalidOffset());
|
|
||||||
|
|
||||||
std::scoped_lock lk(m_mutex);
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
const size_t cache_block_count = m_block_cache.GetSize();
|
const size_t cache_block_count = m_block_cache.GetSize();
|
||||||
BlockCache valid_cache;
|
for (size_t i = 0; i < cache_block_count; ++i) {
|
||||||
|
|
||||||
for (size_t count = 0; count < cache_block_count; ++count) {
|
|
||||||
auto lru = m_block_cache.PopLruNode();
|
auto lru = m_block_cache.PopLruNode();
|
||||||
if (offset <= lru->m_key && lru->m_key < offset + size) {
|
|
||||||
m_block_cache.PushMruNode(std::move(lru), -1);
|
m_block_cache.PushMruNode(std::move(lru), -1);
|
||||||
} else {
|
|
||||||
valid_cache.PushMruNode(std::move(lru), lru->m_key);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!valid_cache.IsEmpty()) {
|
return ResultSuccess();
|
||||||
auto lru = valid_cache.PopLruNode();
|
} else {
|
||||||
m_block_cache.PushMruNode(std::move(lru), lru->m_key);
|
/* Validate preconditions. */
|
||||||
}
|
AMS_ASSERT(util::IsAligned(offset, m_block_size));
|
||||||
|
AMS_ASSERT(util::IsAligned(size, m_block_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Operate on the base storage. */
|
/* Operate on the base storage. */
|
||||||
|
|
|
@ -150,7 +150,7 @@ namespace ams::fssystem::save {
|
||||||
m_storage = fs::SubStorage();
|
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. */
|
/* Validate preconditions. */
|
||||||
AMS_ASSERT(bufs != nullptr);
|
AMS_ASSERT(bufs != nullptr);
|
||||||
AMS_ASSERT(IntegrityMinLayerCount <= info.max_layers && info.max_layers <= IntegrityMaxLayerCount);
|
AMS_ASSERT(IntegrityMinLayerCount <= info.max_layers && info.max_layers <= IntegrityMaxLayerCount);
|
||||||
|
@ -168,7 +168,7 @@ namespace ams::fssystem::save {
|
||||||
{
|
{
|
||||||
fs::HashSalt mac;
|
fs::HashSalt mac;
|
||||||
crypto::GenerateHmacSha256Mac(mac.value, sizeof(mac), info.seed.value, sizeof(info.seed), KeyArray[0].key, KeyArray[0].size);
|
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<s64>(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<s64>(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. */
|
/* 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::SubStorage buffer_storage(std::addressof(m_buffer_storages[level]), 0, info.info[level].size);
|
||||||
fs::HashSalt mac;
|
fs::HashSalt mac;
|
||||||
crypto::GenerateHmacSha256Mac(mac.value, sizeof(mac), info.seed.value, sizeof(info.seed), KeyArray[level + 1].key, KeyArray[level + 1].size);
|
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<s64>(1) << info.info[level + 1].block_order, static_cast<s64>(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<s64>(1) << info.info[level + 1].block_order, static_cast<s64>(1) << info.info[level].block_order, m_buffers->buffers[m_max_layers - 2], hgf, mac, false, storage_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize the buffer storage. */
|
/* 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::SubStorage buffer_storage(std::addressof(m_buffer_storages[level]), 0, info.info[level].size);
|
||||||
fs::HashSalt mac;
|
fs::HashSalt mac;
|
||||||
crypto::GenerateHmacSha256Mac(mac.value, sizeof(mac), info.seed.value, sizeof(info.seed), KeyArray[level + 1].key, KeyArray[level + 1].size);
|
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<s64>(1) << info.info[level + 1].block_order, static_cast<s64>(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<s64>(1) << info.info[level + 1].block_order, static_cast<s64>(1) << info.info[level].block_order, m_buffers->buffers[m_max_layers - 2], hgf, mac, true, storage_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize the buffer storage. */
|
/* Initialize the buffer storage. */
|
||||||
|
|
|
@ -17,15 +17,19 @@
|
||||||
|
|
||||||
namespace ams::fssystem::save {
|
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. */
|
/* Validate preconditions. */
|
||||||
AMS_ASSERT(verif_block_size >= HashSize);
|
AMS_ASSERT(verif_block_size >= HashSize);
|
||||||
AMS_ASSERT(bm != nullptr);
|
AMS_ASSERT(bm != nullptr);
|
||||||
|
AMS_ASSERT(hgf != nullptr);
|
||||||
|
|
||||||
/* Set storages. */
|
/* Set storages. */
|
||||||
m_hash_storage = hs;
|
m_hash_storage = hs;
|
||||||
m_data_storage = ds;
|
m_data_storage = ds;
|
||||||
|
|
||||||
|
/* Set hash generator factory. */
|
||||||
|
m_hash_generator_factory = hgf;
|
||||||
|
|
||||||
/* Set verification block sizes. */
|
/* Set verification block sizes. */
|
||||||
m_verification_block_size = verif_block_size;
|
m_verification_block_size = verif_block_size;
|
||||||
m_verification_block_order = ILog2(static_cast<u32>(verif_block_size));
|
m_verification_block_order = ILog2(static_cast<u32>(verif_block_size));
|
||||||
|
@ -111,14 +115,17 @@ namespace ams::fssystem::save {
|
||||||
clear_guard.Cancel();
|
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. */
|
/* Prepare to validate the signatures. */
|
||||||
const auto signature_count = size >> m_verification_block_order;
|
const auto signature_count = size >> m_verification_block_order;
|
||||||
PooledBuffer signature_buffer(signature_count * sizeof(BlockHash), sizeof(BlockHash));
|
PooledBuffer signature_buffer(signature_count * sizeof(BlockHash), sizeof(BlockHash));
|
||||||
const auto buffer_count = std::min(signature_count, signature_buffer.GetSize() / 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;
|
size_t verified_count = 0;
|
||||||
while (verified_count < signature_count) {
|
while (verified_count < signature_count) {
|
||||||
/* Read the current signatures. */
|
/* 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) {
|
for (size_t i = 0; i < cur_count && R_SUCCEEDED(cur_result); ++i) {
|
||||||
const auto verified_size = (verified_count + i) << m_verification_block_order;
|
const auto verified_size = (verified_count + i) << m_verification_block_order;
|
||||||
u8 *cur_buf = static_cast<u8 *>(buffer) + verified_size;
|
u8 *cur_buf = static_cast<u8 *>(buffer) + verified_size;
|
||||||
cur_result = this->VerifyHash(cur_buf, reinterpret_cast<BlockHash *>(signature_buffer.GetBuffer()) + i);
|
cur_result = this->VerifyHash(cur_buf, reinterpret_cast<BlockHash *>(signature_buffer.GetBuffer()) + i, generator);
|
||||||
|
|
||||||
/* If the data is corrupted, clear the corrupted parts. */
|
/* If the data is corrupted, clear the corrupted parts. */
|
||||||
if (fs::ResultIntegrityVerificationStorageCorrupted::Includes(cur_result)) {
|
if (fs::ResultIntegrityVerificationStorageCorrupted::Includes(cur_result)) {
|
||||||
|
@ -211,6 +218,8 @@ namespace ams::fssystem::save {
|
||||||
PooledBuffer signature_buffer(signature_count * sizeof(BlockHash), sizeof(BlockHash));
|
PooledBuffer signature_buffer(signature_count * sizeof(BlockHash), sizeof(BlockHash));
|
||||||
const auto buffer_count = std::min(signature_count, signature_buffer.GetSize() / 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) {
|
while (updated_count < signature_count) {
|
||||||
const auto cur_count = std::min(buffer_count, signature_count - updated_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) {
|
for (size_t i = 0; i < cur_count; ++i) {
|
||||||
const auto updated_size = (updated_count + i) << m_verification_block_order;
|
const auto updated_size = (updated_count + i) << m_verification_block_order;
|
||||||
this->CalcBlockHash(reinterpret_cast<BlockHash *>(signature_buffer.GetBuffer()) + i, reinterpret_cast<const u8 *>(buffer) + updated_size);
|
this->CalcBlockHash(reinterpret_cast<BlockHash *>(signature_buffer.GetBuffer()) + i, reinterpret_cast<const u8 *>(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 {
|
void IntegrityVerificationStorage::CalcBlockHash(BlockHash *out, const void *buffer, size_t block_size, std::unique_ptr<fssystem::IHash256Generator> &generator) const {
|
||||||
/* Create a sha256 generator. */
|
/* Initialize the generator. */
|
||||||
crypto::Sha256Generator sha;
|
generator->Initialize();
|
||||||
sha.Initialize();
|
|
||||||
|
|
||||||
/* If calculating for save data, hash the salt. */
|
/* If calculating for save data, hash the salt. */
|
||||||
if (m_storage_type == fs::StorageType_SaveData) {
|
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. */
|
/* Update with the buffer and get the hash. */
|
||||||
sha.Update(buffer, block_size);
|
generator->Update(buffer, block_size);
|
||||||
sha.GetHash(out, sizeof(*out));
|
generator->GetHash(out, sizeof(*out));
|
||||||
|
|
||||||
/* Set the validation bit, if the hash is for save data. */
|
/* Set the validation bit, if the hash is for save data. */
|
||||||
if (m_storage_type == fs::StorageType_SaveData) {
|
if (m_storage_type == fs::StorageType_SaveData) {
|
||||||
|
@ -428,7 +436,7 @@ namespace ams::fssystem::save {
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result IntegrityVerificationStorage::VerifyHash(const void *buf, BlockHash *hash) {
|
Result IntegrityVerificationStorage::VerifyHash(const void *buf, BlockHash *hash, std::unique_ptr<fssystem::IHash256Generator> &generator) {
|
||||||
/* Validate preconditions. */
|
/* Validate preconditions. */
|
||||||
AMS_ASSERT(buf != nullptr);
|
AMS_ASSERT(buf != nullptr);
|
||||||
AMS_ASSERT(hash != nullptr);
|
AMS_ASSERT(hash != nullptr);
|
||||||
|
@ -445,7 +453,7 @@ namespace ams::fssystem::save {
|
||||||
|
|
||||||
/* Get the calculated hash. */
|
/* Get the calculated hash. */
|
||||||
BlockHash calc_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. */
|
/* Check that the signatures are equal. */
|
||||||
if (!crypto::IsSameBytes(std::addressof(cmp_hash), std::addressof(calc_hash), sizeof(BlockHash))) {
|
if (!crypto::IsSameBytes(std::addressof(cmp_hash), std::addressof(calc_hash), sizeof(BlockHash))) {
|
||||||
|
|
|
@ -206,8 +206,10 @@ namespace ams::fs {
|
||||||
R_DEFINE_ERROR_RANGE(NcaCorrupted, 4501, 4599);
|
R_DEFINE_ERROR_RANGE(NcaCorrupted, 4501, 4599);
|
||||||
R_DEFINE_ERROR_RESULT(NcaBaseStorageOutOfRangeA, 4508);
|
R_DEFINE_ERROR_RESULT(NcaBaseStorageOutOfRangeA, 4508);
|
||||||
R_DEFINE_ERROR_RESULT(NcaBaseStorageOutOfRangeB, 4509);
|
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(InvalidNcaFileSystemType, 4512);
|
||||||
R_DEFINE_ERROR_RESULT(InvalidAcidFileSize, 4513);
|
R_DEFINE_ERROR_RESULT(InvalidAcidFileSize, 4513);
|
||||||
R_DEFINE_ERROR_RESULT(InvalidAcidSize, 4514);
|
R_DEFINE_ERROR_RESULT(InvalidAcidSize, 4514);
|
||||||
|
@ -227,6 +229,8 @@ namespace ams::fs {
|
||||||
R_DEFINE_ERROR_RESULT(InvalidNcaHeader, 4528);
|
R_DEFINE_ERROR_RESULT(InvalidNcaHeader, 4528);
|
||||||
R_DEFINE_ERROR_RESULT(InvalidNcaFsHeader, 4529);
|
R_DEFINE_ERROR_RESULT(InvalidNcaFsHeader, 4529);
|
||||||
|
|
||||||
|
R_DEFINE_ERROR_RESULT(NcaBaseStorageOutOfRangeE, 4530);
|
||||||
|
|
||||||
R_DEFINE_ERROR_RANGE(NcaHierarchicalSha256StorageCorrupted, 4531, 4539);
|
R_DEFINE_ERROR_RANGE(NcaHierarchicalSha256StorageCorrupted, 4531, 4539);
|
||||||
R_DEFINE_ERROR_RESULT(InvalidHierarchicalSha256BlockSize, 4532);
|
R_DEFINE_ERROR_RESULT(InvalidHierarchicalSha256BlockSize, 4532);
|
||||||
R_DEFINE_ERROR_RESULT(InvalidHierarchicalSha256LayerCount, 4533);
|
R_DEFINE_ERROR_RESULT(InvalidHierarchicalSha256LayerCount, 4533);
|
||||||
|
@ -236,6 +240,9 @@ namespace ams::fs {
|
||||||
/* TODO: Range? */
|
/* TODO: Range? */
|
||||||
R_DEFINE_ERROR_RESULT(InvalidNcaHeader1SignatureKeyGeneration, 4543);
|
R_DEFINE_ERROR_RESULT(InvalidNcaHeader1SignatureKeyGeneration, 4543);
|
||||||
|
|
||||||
|
/* TODO: Range? */
|
||||||
|
R_DEFINE_ERROR_RESULT(InvalidCompressedStorageSize, 4547);
|
||||||
|
|
||||||
R_DEFINE_ERROR_RANGE(IntegrityVerificationStorageCorrupted, 4601, 4639);
|
R_DEFINE_ERROR_RANGE(IntegrityVerificationStorageCorrupted, 4601, 4639);
|
||||||
R_DEFINE_ERROR_RESULT(IncorrectIntegrityVerificationMagic, 4602);
|
R_DEFINE_ERROR_RESULT(IncorrectIntegrityVerificationMagic, 4602);
|
||||||
R_DEFINE_ERROR_RESULT(InvalidZeroHash, 4603);
|
R_DEFINE_ERROR_RESULT(InvalidZeroHash, 4603);
|
||||||
|
@ -362,6 +369,7 @@ namespace ams::fs {
|
||||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileSystemB, 6375);
|
R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileSystemB, 6375);
|
||||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileA, 6376);
|
R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileA, 6376);
|
||||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileB, 6377);
|
R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileB, 6377);
|
||||||
|
R_DEFINE_ERROR_RESULT(UnsupportedOperationInCompressedStorageA, 6387);
|
||||||
|
|
||||||
R_DEFINE_ERROR_RANGE(PermissionDenied, 6400, 6449);
|
R_DEFINE_ERROR_RANGE(PermissionDenied, 6400, 6449);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue