fs: update nca drivers (and dependents/callees) for 14.0.0 changes
This commit is contained in:
@@ -39,6 +39,9 @@
|
||||
#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_storage_external.hpp>
|
||||
#include <stratosphere/fssystem/fssystem_aes_xts_storage_external.hpp>
|
||||
#include <stratosphere/fssystem/fssystem_switch_storage.hpp>
|
||||
#include <stratosphere/fssystem/buffers/fssystem_buffer_manager_utils.hpp>
|
||||
#include <stratosphere/fssystem/buffers/fssystem_file_system_buffer_manager.hpp>
|
||||
#include <stratosphere/fssystem/fssystem_pooled_buffer.hpp>
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace ams::fssystem {
|
||||
|
||||
using IAllocator = BucketTree::IAllocator;
|
||||
|
||||
using DecryptFunction = void(*)(void *dst, size_t dst_size, s32 index, const void *enc_key, size_t enc_key_size, const void *iv, size_t iv_size, const void *src, size_t src_size);
|
||||
using DecryptFunction = void(*)(void *dst, size_t dst_size, u8 index, u8 gen, const void *enc_key, size_t enc_key_size, const void *iv, size_t iv_size, const void *src, size_t src_size);
|
||||
|
||||
class IDecryptor {
|
||||
public:
|
||||
@@ -72,7 +72,7 @@ namespace ams::fssystem {
|
||||
return BucketTree::QueryEntryStorageSize(NodeSize, sizeof(Entry), entry_count);
|
||||
}
|
||||
|
||||
static Result CreateExternalDecryptor(std::unique_ptr<IDecryptor> *out, DecryptFunction func, s32 key_index);
|
||||
static Result CreateExternalDecryptor(std::unique_ptr<IDecryptor> *out, DecryptFunction func, s32 key_index, s32 key_generation);
|
||||
static Result CreateSoftwareDecryptor(std::unique_ptr<IDecryptor> *out);
|
||||
private:
|
||||
BucketTree m_table;
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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/fs/fs_istorage.hpp>
|
||||
#include <stratosphere/fs/impl/fs_newable.hpp>
|
||||
#include <stratosphere/fssystem/fssystem_nca_file_system_driver.hpp>
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
/* ACCURATE_TO_VERSION: 14.3.0.0 */
|
||||
class AesCtrStorageExternal : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
|
||||
NON_COPYABLE(AesCtrStorageExternal);
|
||||
NON_MOVEABLE(AesCtrStorageExternal);
|
||||
public:
|
||||
static constexpr size_t BlockSize = crypto::Aes128CtrEncryptor::BlockSize;
|
||||
static constexpr size_t KeySize = crypto::Aes128CtrEncryptor::KeySize;
|
||||
static constexpr size_t IvSize = crypto::Aes128CtrEncryptor::IvSize;
|
||||
private:
|
||||
std::shared_ptr<fs::IStorage> m_base_storage;
|
||||
u8 m_iv[IvSize];
|
||||
DecryptAesCtrFunction m_decrypt_function;
|
||||
s32 m_key_index;
|
||||
s32 m_key_generation;
|
||||
u8 m_encrypted_key[KeySize];
|
||||
public:
|
||||
AesCtrStorageExternal(std::shared_ptr<fs::IStorage> bs, const void *enc_key, size_t enc_key_size, const void *iv, size_t iv_size, DecryptAesCtrFunction df, s32 kidx, s32 kgen);
|
||||
|
||||
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;
|
||||
virtual Result Flush() override;
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override;
|
||||
virtual Result SetSize(s64 size) override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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/fs/fs_istorage.hpp>
|
||||
#include <stratosphere/fs/impl/fs_newable.hpp>
|
||||
#include <stratosphere/fssystem/fssystem_nca_file_system_driver.hpp>
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
/* ACCURATE_TO_VERSION: 14.3.0.0 */
|
||||
template<fs::PointerToStorage BasePointer>
|
||||
class AesXtsStorageExternal : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
|
||||
NON_COPYABLE(AesXtsStorageExternal);
|
||||
NON_MOVEABLE(AesXtsStorageExternal);
|
||||
public:
|
||||
static constexpr size_t AesBlockSize = crypto::Aes128XtsEncryptor::BlockSize;
|
||||
static constexpr size_t KeySize = crypto::Aes128XtsEncryptor::KeySize;
|
||||
static constexpr size_t IvSize = crypto::Aes128XtsEncryptor::IvSize;
|
||||
private:
|
||||
BasePointer m_base_storage;
|
||||
char m_key[2][KeySize];
|
||||
char m_iv[IvSize];
|
||||
const size_t m_block_size;
|
||||
CryptAesXtsFunction m_encrypt_function;
|
||||
CryptAesXtsFunction m_decrypt_function;
|
||||
public:
|
||||
AesXtsStorageExternal(BasePointer bs, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, size_t block_size, CryptAesXtsFunction ef, CryptAesXtsFunction df);
|
||||
|
||||
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;
|
||||
virtual Result Flush() override;
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override;
|
||||
virtual Result SetSize(s64 size) override;
|
||||
};
|
||||
|
||||
using AesXtsStorageExternalByPointer = AesXtsStorageExternal<fs::IStorage *>;
|
||||
using AesXtsStorageExternalBySharedPointer = AesXtsStorageExternal<std::shared_ptr<fs::IStorage>>;
|
||||
|
||||
}
|
||||
@@ -99,13 +99,13 @@ namespace ams::fssystem {
|
||||
size_t m_verification_block_shift;
|
||||
s32 m_flags;
|
||||
s32 m_buffer_level;
|
||||
fs::StorageType m_storage_type;
|
||||
BlockCacheManager m_block_cache_manager;
|
||||
bool m_is_writable;
|
||||
public:
|
||||
BlockCacheBufferedStorage();
|
||||
virtual ~BlockCacheBufferedStorage() override;
|
||||
|
||||
Result Initialize(fs::IBufferManager *bm, os::SdkRecursiveMutex *mtx, IStorage *data, s64 data_size, size_t verif_block_size, s32 max_cache_entries, bool is_real_data, s8 buffer_level, bool is_keep_burst_mode, fs::StorageType storage_type);
|
||||
Result Initialize(fs::IBufferManager *bm, os::SdkRecursiveMutex *mtx, IStorage *data, s64 data_size, size_t verif_block_size, s32 max_cache_entries, bool is_real_data, s8 buffer_level, bool is_keep_burst_mode, bool is_writable);
|
||||
void Finalize();
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override;
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
/* ACCURATE_TO_VERSION: Unknown */
|
||||
/* ACCURATE_TO_VERSION: 14.3.0.0 */
|
||||
|
||||
struct HierarchicalIntegrityVerificationLevelInformation {
|
||||
fs::Int64 offset;
|
||||
@@ -151,20 +151,25 @@ namespace ams::fssystem {
|
||||
os::SdkRecursiveMutex *m_mutex;
|
||||
IntegrityVerificationStorage m_verify_storages[MaxLayers - 1];
|
||||
BlockCacheBufferedStorage m_buffer_storages[MaxLayers - 1];
|
||||
os::Semaphore *m_read_semaphore;
|
||||
os::Semaphore *m_write_semaphore;
|
||||
s64 m_data_size;
|
||||
s32 m_max_layers;
|
||||
bool m_is_written_for_rollback;
|
||||
public:
|
||||
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) { /* ... */ }
|
||||
virtual ~HierarchicalIntegrityVerificationStorage() override { this->Finalize(); }
|
||||
|
||||
Result Initialize(const HierarchicalIntegrityVerificationInformation &info, HierarchicalStorageInformation storage, FileSystemBufferManagerSet *bufs, IHash256GeneratorFactory *hgf, os::SdkRecursiveMutex *mtx, fs::StorageType storage_type);
|
||||
Result Initialize(const HierarchicalIntegrityVerificationInformation &info, HierarchicalStorageInformation storage, FileSystemBufferManagerSet *bufs, IHash256GeneratorFactory *hgf, bool hash_salt_enabled, os::SdkRecursiveMutex *mtx, os::Semaphore *read_sema, os::Semaphore *write_sema, int max_data_cache_entries, int max_hash_cache_entries, s8 buffer_level, bool is_writable, bool allow_cleared_blocks);
|
||||
Result Initialize(const HierarchicalIntegrityVerificationInformation &info, HierarchicalStorageInformation storage, FileSystemBufferManagerSet *bufs, IHash256GeneratorFactory *hgf, bool hash_salt_enabled, os::SdkRecursiveMutex *mtx, int max_data_cache_entries, int max_hash_cache_entries, s8 buffer_level, bool is_writable, bool allow_cleared_blocks) {
|
||||
R_RETURN(this->Initialize(info, storage, bufs, hgf, hash_salt_enabled, mtx, nullptr, nullptr, max_data_cache_entries, max_hash_cache_entries, buffer_level, is_writable, allow_cleared_blocks));
|
||||
}
|
||||
|
||||
void Finalize();
|
||||
|
||||
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 SetSize(s64 size) override { AMS_UNUSED(size); return fs::ResultUnsupportedSetSizeForHierarchicalIntegrityVerificationStorage(); }
|
||||
virtual Result SetSize(s64 size) override { AMS_UNUSED(size); R_THROW(fs::ResultUnsupportedSetSizeForHierarchicalIntegrityVerificationStorage()); }
|
||||
virtual Result GetSize(s64 *out) override;
|
||||
|
||||
virtual Result Flush() override;
|
||||
@@ -179,10 +184,6 @@ namespace ams::fssystem {
|
||||
return m_data_size >= 0;
|
||||
}
|
||||
|
||||
bool IsWrittenForRollback() const {
|
||||
return m_is_written_for_rollback;
|
||||
}
|
||||
|
||||
FileSystemBufferManagerSet *GetBuffers() {
|
||||
return m_buffers;
|
||||
}
|
||||
@@ -201,6 +202,10 @@ namespace ams::fssystem {
|
||||
fs::SubStorage GetL1HashStorage() {
|
||||
return fs::SubStorage(std::addressof(m_buffer_storages[m_max_layers - 3]), 0, util::DivideUp(m_data_size, this->GetL1HashVerificationBlockSize()));
|
||||
}
|
||||
public:
|
||||
static constexpr s8 GetDefaultDataCacheBufferLevel(u32 max_layers) {
|
||||
return 16 + max_layers - 2;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -39,33 +39,33 @@ namespace ams::fssystem {
|
||||
IntegrityRomFsStorage() : m_mutex() { /* ... */ }
|
||||
virtual ~IntegrityRomFsStorage() override { this->Finalize(); }
|
||||
|
||||
Result Initialize(HierarchicalIntegrityVerificationInformation level_hash_info, Hash master_hash, HierarchicalIntegrityVerificationStorage::HierarchicalStorageInformation storage_info, fs::IBufferManager *bm, IHash256GeneratorFactory *hgf);
|
||||
Result Initialize(HierarchicalIntegrityVerificationInformation level_hash_info, Hash master_hash, HierarchicalIntegrityVerificationStorage::HierarchicalStorageInformation storage_info, fs::IBufferManager *bm, int max_data_cache_entries, int max_hash_cache_entries, s8 buffer_level, IHash256GeneratorFactory *hgf);
|
||||
void Finalize();
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
return m_integrity_storage.Read(offset, buffer, size);
|
||||
R_RETURN(m_integrity_storage.Read(offset, buffer, size));
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
return m_integrity_storage.Write(offset, buffer, size);
|
||||
R_RETURN(m_integrity_storage.Write(offset, buffer, size));
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override { AMS_UNUSED(size); return fs::ResultUnsupportedSetSizeForIntegrityRomFsStorage(); }
|
||||
virtual Result SetSize(s64 size) override { AMS_UNUSED(size); R_THROW(fs::ResultUnsupportedSetSizeForIntegrityRomFsStorage()); }
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
return m_integrity_storage.GetSize(out);
|
||||
R_RETURN(m_integrity_storage.GetSize(out));
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
return m_integrity_storage.Flush();
|
||||
R_RETURN(m_integrity_storage.Flush());
|
||||
}
|
||||
|
||||
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 {
|
||||
return m_integrity_storage.OperateRange(dst, dst_size, op_id, offset, size, src, src_size);
|
||||
R_RETURN(m_integrity_storage.OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
}
|
||||
|
||||
Result Commit() {
|
||||
return m_integrity_storage.Commit();
|
||||
R_RETURN(m_integrity_storage.Commit());
|
||||
}
|
||||
|
||||
FileSystemBufferManagerSet *GetBuffers() {
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
/* ACCURATE_TO_VERSION: Unknown */
|
||||
/* ACCURATE_TO_VERSION: 14.3.0.0 */
|
||||
|
||||
class IntegrityVerificationStorage : public ::ams::fs::IStorage {
|
||||
NON_COPYABLE(IntegrityVerificationStorage);
|
||||
@@ -43,15 +43,16 @@ namespace ams::fssystem {
|
||||
s64 m_upper_layer_verification_block_size;
|
||||
s64 m_upper_layer_verification_block_order;
|
||||
fs::IBufferManager *m_buffer_manager;
|
||||
fs::HashSalt m_salt;
|
||||
util::optional<fs::HashSalt> m_salt;
|
||||
bool m_is_real_data;
|
||||
fs::StorageType m_storage_type;
|
||||
fssystem::IHash256GeneratorFactory *m_hash_generator_factory;
|
||||
bool m_is_writable;
|
||||
bool m_allow_cleared_blocks;
|
||||
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), m_salt(util::nullopt) { /* ... */ }
|
||||
virtual ~IntegrityVerificationStorage() override { this->Finalize(); }
|
||||
|
||||
Result Initialize(fs::SubStorage hs, fs::SubStorage ds, s64 verif_block_size, s64 upper_layer_verif_block_size, fs::IBufferManager *bm, fssystem::IHash256GeneratorFactory *hgf, 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, fs::IBufferManager *bm, fssystem::IHash256GeneratorFactory *hgf, const util::optional<fs::HashSalt> &salt, bool is_real_data, bool is_writable, bool allow_cleared_blocks);
|
||||
void Finalize();
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override;
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
/* ACCURATE_TO_VERSION: 13.4.0.0 */
|
||||
/* ACCURATE_TO_VERSION: 14.3.0.0 */
|
||||
|
||||
class CompressedStorage;
|
||||
class AesCtrCounterExtendedStorage;
|
||||
@@ -34,8 +34,11 @@ namespace ams::fssystem {
|
||||
|
||||
struct NcaCryptoConfiguration;
|
||||
|
||||
using KeyGenerationFunction = void (*)(void *dst_key, size_t dst_key_size, const void *src_key, size_t src_key_size, s32 key_type, const NcaCryptoConfiguration &cfg);
|
||||
using DecryptAesCtrFunction = void (*)(void *dst, size_t dst_size, s32 key_type, const void *src_key, size_t src_key_size, const void *iv, size_t iv_size, const void *src, size_t src_size);
|
||||
using KeyGenerationFunction = void (*)(void *dst_key, size_t dst_key_size, const void *src_key, size_t src_key_size, s32 key_type);
|
||||
using DecryptAesCtrFunction = void (*)(void *dst, size_t dst_size, u8 key_index, u8 key_generation, const void *src_key, size_t src_key_size, const void *iv, size_t iv_size, const void *src, size_t src_size);
|
||||
|
||||
using CryptAesXtsFunction = Result (*)(void *dst, size_t dst_size, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size);
|
||||
using VerifySign1Function = bool (*)(const void *sig, size_t sig_size, const void *data, size_t data_size, u8 generation, const NcaCryptoConfiguration &cfg);
|
||||
|
||||
struct NcaCryptoConfiguration {
|
||||
static constexpr size_t Rsa2048KeyModulusSize = crypto::Rsa2048PssSha256Verifier::ModulusSize;
|
||||
@@ -49,6 +52,8 @@ namespace ams::fssystem {
|
||||
static constexpr s32 KeyAreaEncryptionKeyIndexCount = 3;
|
||||
static constexpr s32 HeaderEncryptionKeyCount = 2;
|
||||
|
||||
static constexpr u8 KeyAreaEncryptionKeyIndexZeroKey = 0xFF;
|
||||
|
||||
static constexpr size_t KeyGenerationMax = 32;
|
||||
|
||||
const u8 *header_1_sign_key_moduli[Header1SignatureKeyGenerationMax + 1];
|
||||
@@ -57,9 +62,13 @@ namespace ams::fssystem {
|
||||
u8 header_encryption_key_source[Aes128KeySize];
|
||||
u8 header_encrypted_encryption_keys[HeaderEncryptionKeyCount][Aes128KeySize];
|
||||
KeyGenerationFunction generate_key;
|
||||
CryptAesXtsFunction decrypt_aes_xts_external;
|
||||
CryptAesXtsFunction encrypt_aes_xts_external;
|
||||
DecryptAesCtrFunction decrypt_aes_ctr;
|
||||
DecryptAesCtrFunction decrypt_aes_ctr_external;
|
||||
VerifySign1Function verify_sign1;
|
||||
bool is_plaintext_header_available;
|
||||
bool is_available_sw_key;
|
||||
|
||||
#if !defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||
bool is_unsigned_header_available_for_host_tool;
|
||||
@@ -72,30 +81,35 @@ namespace ams::fssystem {
|
||||
};
|
||||
static_assert(util::is_pod<NcaCompressionConfiguration>::value);
|
||||
|
||||
constexpr inline s32 KeyAreaEncryptionKeyCount = NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexCount * NcaCryptoConfiguration::KeyGenerationMax;
|
||||
|
||||
enum class KeyType : s32 {
|
||||
ZeroKey = -2,
|
||||
InvalidKey = -1,
|
||||
NcaHeaderKey1 = KeyAreaEncryptionKeyCount + 0,
|
||||
NcaHeaderKey2 = KeyAreaEncryptionKeyCount + 1,
|
||||
NcaExternalKey = KeyAreaEncryptionKeyCount + 2,
|
||||
SaveDataDeviceUniqueMac = KeyAreaEncryptionKeyCount + 3,
|
||||
SaveDataSeedUniqueMac = KeyAreaEncryptionKeyCount + 4,
|
||||
SaveDataTransferMac = KeyAreaEncryptionKeyCount + 5,
|
||||
};
|
||||
|
||||
constexpr inline bool IsInvalidKeyTypeValue(s32 key_type) {
|
||||
return key_type < 0;
|
||||
}
|
||||
|
||||
constexpr inline s32 GetKeyTypeValue(u8 key_index, u8 key_generation) {
|
||||
constexpr s32 InvalidKeyTypeValue = -1;
|
||||
static_assert(IsInvalidKeyTypeValue(InvalidKeyTypeValue));
|
||||
if (key_index == NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexZeroKey) {
|
||||
return util::ToUnderlying(KeyType::ZeroKey);
|
||||
}
|
||||
|
||||
if (key_index >= NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexCount) {
|
||||
return InvalidKeyTypeValue;
|
||||
return util::ToUnderlying(KeyType::InvalidKey);
|
||||
}
|
||||
|
||||
return NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexCount * key_generation + key_index;
|
||||
}
|
||||
|
||||
constexpr inline s32 KeyAreaEncryptionKeyCount = NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexCount * NcaCryptoConfiguration::KeyGenerationMax;
|
||||
|
||||
enum class KeyType : s32 {
|
||||
NcaHeaderKey = KeyAreaEncryptionKeyCount + 0,
|
||||
NcaExternalKey = KeyAreaEncryptionKeyCount + 1,
|
||||
SaveDataDeviceUniqueMac = KeyAreaEncryptionKeyCount + 2,
|
||||
SaveDataSeedUniqueMac = KeyAreaEncryptionKeyCount + 3,
|
||||
};
|
||||
|
||||
class NcaReader : public ::ams::fs::impl::Newable {
|
||||
NON_COPYABLE(NcaReader);
|
||||
NON_MOVEABLE(NcaReader);
|
||||
@@ -108,6 +122,7 @@ namespace ams::fssystem {
|
||||
DecryptAesCtrFunction m_decrypt_aes_ctr;
|
||||
DecryptAesCtrFunction m_decrypt_aes_ctr_external;
|
||||
bool m_is_software_aes_prioritized;
|
||||
bool m_is_available_sw_key;
|
||||
NcaHeader::EncryptionType m_header_encryption_type;
|
||||
bool m_is_header_sign1_signature_valid;
|
||||
GetDecompressorFunction m_get_decompressor;
|
||||
@@ -144,6 +159,7 @@ namespace ams::fssystem {
|
||||
bool HasInternalDecryptionKeyForAesHw() const;
|
||||
bool IsSoftwareAesPrioritized() const;
|
||||
void PrioritizeSoftwareAes();
|
||||
bool IsAvailableSwKey() const;
|
||||
bool HasExternalDecryptionKey() const;
|
||||
const void *GetExternalDecryptionKey() const;
|
||||
void SetExternalDecryptionKey(const void *src, size_t size);
|
||||
@@ -191,12 +207,27 @@ namespace ams::fssystem {
|
||||
NcaPatchInfo &GetPatchInfo();
|
||||
const NcaPatchInfo &GetPatchInfo() const;
|
||||
const NcaAesCtrUpperIv GetAesCtrUpperIv() const;
|
||||
|
||||
bool IsSkipLayerHashEncryption() const;
|
||||
Result GetHashTargetOffset(s64 *out) const;
|
||||
|
||||
bool ExistsSparseLayer() const;
|
||||
NcaSparseInfo &GetSparseInfo();
|
||||
const NcaSparseInfo &GetSparseInfo() const;
|
||||
|
||||
bool ExistsCompressionLayer() const;
|
||||
NcaCompressionInfo &GetCompressionInfo();
|
||||
const NcaCompressionInfo &GetCompressionInfo() const;
|
||||
|
||||
bool ExistsPatchMetaHashLayer() const;
|
||||
NcaMetaDataHashDataInfo &GetPatchMetaDataHashDataInfo();
|
||||
const NcaMetaDataHashDataInfo &GetPatchMetaDataHashDataInfo() const;
|
||||
NcaFsHeader::MetaDataHashType GetPatchMetaHashType() const;
|
||||
|
||||
bool ExistsSparseMetaHashLayer() const;
|
||||
NcaMetaDataHashDataInfo &GetSparseMetaDataHashDataInfo();
|
||||
const NcaMetaDataHashDataInfo &GetSparseMetaDataHashDataInfo() const;
|
||||
NcaFsHeader::MetaDataHashType GetSparseMetaHashType() const;
|
||||
};
|
||||
|
||||
class NcaFileSystemDriver : public ::ams::fs::impl::Newable {
|
||||
@@ -224,6 +255,9 @@ namespace ams::fssystem {
|
||||
std::shared_ptr<fs::IStorage> compressed_storage_meta_storage;
|
||||
std::shared_ptr<fssystem::CompressedStorage> compressed_storage;
|
||||
|
||||
std::shared_ptr<fs::IStorage> patch_layer_info_storage;
|
||||
std::shared_ptr<fs::IStorage> sparse_layer_info_storage;
|
||||
|
||||
/* For tools. */
|
||||
std::shared_ptr<fs::IStorage> external_original_storage;
|
||||
};
|
||||
@@ -282,15 +316,24 @@ namespace ams::fssystem {
|
||||
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 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 CreateSparseStorageMetaStorageWithVerification(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> *out_verification, std::shared_ptr<fs::IStorage> base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, const NcaSparseInfo &sparse_info, const NcaMetaDataHashDataInfo &meta_data_hash_data_info, IHash256GeneratorFactory *hgf);
|
||||
Result CreateSparseStorageWithVerification(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, std::shared_ptr<fs::IStorage> *out_verification, s32 index, const NcaAesCtrUpperIv &upper_iv, const NcaSparseInfo &sparse_info, const NcaMetaDataHashDataInfo &meta_data_hash_data_info, NcaFsHeader::MetaDataHashType meta_data_hash_type);
|
||||
|
||||
Result CreateAesCtrExStorageMetaStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, s64 offset, NcaFsHeader::EncryptionType encryption_type, 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 CreatePatchMetaStorage(std::shared_ptr<fs::IStorage> *out_aes_ctr_ex_meta, std::shared_ptr<fs::IStorage> *out_indirect_meta, std::shared_ptr<fs::IStorage> *out_verification, std::shared_ptr<fs::IStorage> base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, const NcaPatchInfo &patch_info, const NcaMetaDataHashDataInfo &meta_data_hash_data_info, IHash256GeneratorFactory *hgf);
|
||||
|
||||
Result CreateSha256Storage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, const NcaFsHeader::HashData::HierarchicalSha256Data &sha256_data, IHash256GeneratorFactory *hgf);
|
||||
|
||||
Result CreateIntegrityVerificationStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, const NcaFsHeader::HashData::IntegrityMetaInfo &meta_info, IHash256GeneratorFactory *hgf);
|
||||
Result CreateIntegrityVerificationStorageForMeta(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> *out_verification, std::shared_ptr<fs::IStorage> base_storage, s64 offset, const NcaMetaDataHashDataInfo &meta_data_hash_data_info, IHash256GeneratorFactory *hgf);
|
||||
Result CreateIntegrityVerificationStorageImpl(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, const NcaFsHeader::HashData::IntegrityMetaInfo &meta_info, s64 layer_info_offset, int max_data_cache_entries, int max_hash_cache_entries, s8 buffer_level, IHash256GeneratorFactory *hgf);
|
||||
|
||||
Result CreateRegionSwitchStorage(std::shared_ptr<fs::IStorage> *out, const NcaFsHeaderReader *header_reader, std::shared_ptr<fs::IStorage> inside_storage, std::shared_ptr<fs::IStorage> outside_storage);
|
||||
|
||||
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:
|
||||
|
||||
@@ -15,13 +15,14 @@
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/fssystem/fssystem_i_hash_256_generator.hpp>
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
/* ACCURATE_TO_VERSION: 13.4.0.0 */
|
||||
/* ACCURATE_TO_VERSION: 14.3.0.0 */
|
||||
|
||||
struct Hash {
|
||||
static constexpr size_t Size = crypto::Sha256Generator::HashSize;
|
||||
static constexpr size_t Size = IHash256Generator::HashSize;
|
||||
u8 value[Size];
|
||||
};
|
||||
static_assert(sizeof(Hash) == Hash::Size);
|
||||
@@ -185,9 +186,17 @@ namespace ams::fssystem {
|
||||
|
||||
struct NcaCompressionInfo {
|
||||
NcaBucketInfo bucket;
|
||||
u8 reserved[8];
|
||||
};
|
||||
static_assert(util::is_pod<NcaCompressionInfo>::value);
|
||||
|
||||
struct NcaMetaDataHashDataInfo {
|
||||
fs::Int64 offset;
|
||||
fs::Int64 size;
|
||||
Hash hash;
|
||||
};
|
||||
static_assert(util::is_pod<NcaMetaDataHashDataInfo>::value);
|
||||
|
||||
struct NcaFsHeader {
|
||||
static constexpr size_t Size = 0x200;
|
||||
static constexpr size_t HashDataOffset = 0x8;
|
||||
@@ -204,18 +213,28 @@ namespace ams::fssystem {
|
||||
};
|
||||
|
||||
enum class EncryptionType : u8 {
|
||||
Auto = 0,
|
||||
None = 1,
|
||||
AesXts = 2,
|
||||
AesCtr = 3,
|
||||
AesCtrEx = 4,
|
||||
Auto = 0,
|
||||
None = 1,
|
||||
AesXts = 2,
|
||||
AesCtr = 3,
|
||||
AesCtrEx = 4,
|
||||
AesCtrSkipLayerHash = 5,
|
||||
AesCtrExSkipLayerHash = 6,
|
||||
};
|
||||
|
||||
enum class HashType : u8 {
|
||||
Auto = 0,
|
||||
None = 1,
|
||||
HierarchicalSha256Hash = 2,
|
||||
HierarchicalIntegrityHash = 3,
|
||||
Auto = 0,
|
||||
None = 1,
|
||||
HierarchicalSha256Hash = 2,
|
||||
HierarchicalIntegrityHash = 3,
|
||||
AutoSha3 = 4,
|
||||
HierarchicalSha3256Hash = 5,
|
||||
HierarchicalIntegritySha3Hash = 6,
|
||||
};
|
||||
|
||||
enum class MetaDataHashType : u8 {
|
||||
None = 0,
|
||||
HierarchicalIntegrity = 1,
|
||||
};
|
||||
|
||||
union HashData {
|
||||
@@ -265,13 +284,34 @@ namespace ams::fssystem {
|
||||
FsType fs_type;
|
||||
HashType hash_type;
|
||||
EncryptionType encryption_type;
|
||||
u8 reserved[3];
|
||||
MetaDataHashType meta_data_hash_type;
|
||||
u8 reserved[2];
|
||||
HashData hash_data;
|
||||
NcaPatchInfo patch_info;
|
||||
NcaAesCtrUpperIv aes_ctr_upper_iv;
|
||||
NcaSparseInfo sparse_info;
|
||||
NcaCompressionInfo compression_info;
|
||||
u8 pad[0x68];
|
||||
NcaMetaDataHashDataInfo meta_data_hash_data_info;
|
||||
u8 pad[0x30];
|
||||
|
||||
bool IsSkipLayerHashEncryption() const {
|
||||
return this->encryption_type == EncryptionType::AesCtrSkipLayerHash || this->encryption_type == EncryptionType::AesCtrExSkipLayerHash;
|
||||
}
|
||||
|
||||
Result GetHashTargetOffset(s64 *out) const {
|
||||
switch (this->hash_type) {
|
||||
case HashType::HierarchicalIntegrityHash:
|
||||
case HashType::HierarchicalIntegritySha3Hash:
|
||||
*out = this->hash_data.integrity_meta_info.level_hash_info.info[this->hash_data.integrity_meta_info.level_hash_info.max_layers - 2].offset;
|
||||
R_SUCCEED();
|
||||
case HashType::HierarchicalSha256Hash:
|
||||
case HashType::HierarchicalSha3256Hash:
|
||||
*out = this->hash_data.hierarchical_sha256_data.hash_layer_region[this->hash_data.hierarchical_sha256_data.hash_layer_count - 1].offset;
|
||||
R_SUCCEED();
|
||||
default:
|
||||
R_THROW(fs::ResultInvalidNcaFsHeader());
|
||||
}
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(NcaFsHeader) == NcaFsHeader::Size);
|
||||
static_assert(util::is_pod<NcaFsHeader>::value);
|
||||
@@ -280,4 +320,11 @@ namespace ams::fssystem {
|
||||
inline constexpr const size_t NcaFsHeader::HashData::HierarchicalSha256Data::MasterHashOffset = AMS_OFFSETOF(NcaFsHeader, hash_data.hierarchical_sha256_data.fs_data_master_hash);
|
||||
inline constexpr const size_t NcaFsHeader::HashData::IntegrityMetaInfo::MasterHashOffset = AMS_OFFSETOF(NcaFsHeader, hash_data.integrity_meta_info.master_hash);
|
||||
|
||||
struct NcaMetaDataHashData {
|
||||
s64 layer_info_offset;
|
||||
NcaFsHeader::HashData::IntegrityMetaInfo integrity_meta_info;
|
||||
};
|
||||
static_assert(sizeof(NcaMetaDataHashData) == sizeof(NcaFsHeader::HashData::IntegrityMetaInfo) + sizeof(s64));
|
||||
static_assert(util::is_pod<NcaMetaDataHashData>::value);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* 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/fs/fs_istorage.hpp>
|
||||
#include <stratosphere/fs/impl/fs_newable.hpp>
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
/* ACCURATE_TO_VERSION: 14.3.0.0 */
|
||||
template<typename F>
|
||||
class SwitchStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
|
||||
NON_COPYABLE(SwitchStorage);
|
||||
NON_MOVEABLE(SwitchStorage);
|
||||
private:
|
||||
std::shared_ptr<fs::IStorage> m_true_storage;
|
||||
std::shared_ptr<fs::IStorage> m_false_storage;
|
||||
F m_truth_function;
|
||||
private:
|
||||
ALWAYS_INLINE std::shared_ptr<fs::IStorage> &SelectStorage() {
|
||||
return m_truth_function() ? m_true_storage : m_false_storage;
|
||||
}
|
||||
public:
|
||||
SwitchStorage(std::shared_ptr<fs::IStorage> t, std::shared_ptr<fs::IStorage> f, F func) : m_true_storage(std::move(t)), m_false_storage(std::move(f)), m_truth_function(func) { /* ... */ }
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
R_RETURN(this->SelectStorage()->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 {
|
||||
switch (op_id) {
|
||||
case fs::OperationId::Invalidate:
|
||||
{
|
||||
R_TRY(m_true_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
R_TRY(m_false_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
R_SUCCEED();
|
||||
}
|
||||
case fs::OperationId::QueryRange:
|
||||
{
|
||||
R_TRY(this->SelectStorage()->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
R_SUCCEED();
|
||||
}
|
||||
default:
|
||||
R_THROW(fs::ResultUnsupportedOperateRangeForSwitchStorage());
|
||||
}
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
R_RETURN(this->SelectStorage()->GetSize(out));
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
R_RETURN(this->SelectStorage()->Flush());
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
R_RETURN(this->SelectStorage()->Write(offset, buffer, size));
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
R_RETURN(this->SelectStorage()->SetSize(size));
|
||||
}
|
||||
};
|
||||
|
||||
class RegionSwitchStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
|
||||
NON_COPYABLE(RegionSwitchStorage);
|
||||
NON_MOVEABLE(RegionSwitchStorage);
|
||||
public:
|
||||
struct Region {
|
||||
s64 offset;
|
||||
s64 size;
|
||||
};
|
||||
private:
|
||||
std::shared_ptr<fs::IStorage> m_inside_region_storage;
|
||||
std::shared_ptr<fs::IStorage> m_outside_region_storage;
|
||||
Region m_region;
|
||||
public:
|
||||
RegionSwitchStorage(std::shared_ptr<fs::IStorage> &&i, std::shared_ptr<fs::IStorage> &&o, Region r) : m_inside_region_storage(std::move(i)), m_outside_region_storage(std::move(o)), m_region(r) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
/* Process until we're done. */
|
||||
size_t processed = 0;
|
||||
while (processed < size) {
|
||||
/* Process on the appropriate storage. */
|
||||
s64 cur_size = 0;
|
||||
if (this->CheckRegions(std::addressof(cur_size), offset + processed, size - processed)) {
|
||||
R_TRY(m_inside_region_storage->Read(offset + processed, static_cast<u8 *>(buffer) + processed, cur_size));
|
||||
} else {
|
||||
R_TRY(m_outside_region_storage->Read(offset + processed, static_cast<u8 *>(buffer) + processed, cur_size));
|
||||
}
|
||||
|
||||
/* Advance. */
|
||||
processed += cur_size;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
/* Process until we're done. */
|
||||
size_t processed = 0;
|
||||
while (processed < size) {
|
||||
/* Process on the appropriate storage. */
|
||||
s64 cur_size = 0;
|
||||
if (this->CheckRegions(std::addressof(cur_size), offset + processed, size - processed)) {
|
||||
R_TRY(m_inside_region_storage->Write(offset + processed, static_cast<const u8 *>(buffer) + processed, cur_size));
|
||||
} else {
|
||||
R_TRY(m_outside_region_storage->Write(offset + processed, static_cast<const u8 *>(buffer) + processed, cur_size));
|
||||
}
|
||||
|
||||
/* Advance. */
|
||||
processed += cur_size;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
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 {
|
||||
switch (op_id) {
|
||||
case fs::OperationId::Invalidate:
|
||||
{
|
||||
R_TRY(m_inside_region_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
R_TRY(m_outside_region_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
|
||||
R_SUCCEED();
|
||||
}
|
||||
case fs::OperationId::QueryRange:
|
||||
{
|
||||
/* Create a new info. */
|
||||
fs::QueryRangeInfo merged_info;
|
||||
merged_info.Clear();
|
||||
|
||||
/* Process until we're done. */
|
||||
s64 processed = 0;
|
||||
while (processed < size) {
|
||||
/* Process on the appropriate storage. */
|
||||
s64 cur_size = 0;
|
||||
fs::QueryRangeInfo cur_info;
|
||||
if (this->CheckRegions(std::addressof(cur_size), offset + processed, size - processed)) {
|
||||
R_TRY(m_inside_region_storage->OperateRange(std::addressof(cur_info), sizeof(cur_info), op_id, offset + processed, cur_size, src, src_size));
|
||||
} else {
|
||||
R_TRY(m_outside_region_storage->OperateRange(std::addressof(cur_info), sizeof(cur_info), op_id, offset + processed, cur_size, src, src_size));
|
||||
}
|
||||
|
||||
/* Merge the current info. */
|
||||
merged_info.Merge(cur_info);
|
||||
|
||||
/* Advance. */
|
||||
processed += cur_size;
|
||||
}
|
||||
|
||||
/* Write the merged info. */
|
||||
*reinterpret_cast<fs::QueryRangeInfo *>(dst) = merged_info;
|
||||
R_SUCCEED();
|
||||
}
|
||||
default:
|
||||
R_THROW(fs::ResultUnsupportedOperateRangeForRegionSwitchStorage());
|
||||
}
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
R_RETURN(m_inside_region_storage->GetSize(out));
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
/* Flush both storages. */
|
||||
R_TRY(m_inside_region_storage->Flush());
|
||||
R_TRY(m_outside_region_storage->Flush());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
/* Set size for both storages. */
|
||||
R_TRY(m_inside_region_storage->SetSize(size));
|
||||
R_TRY(m_outside_region_storage->SetSize(size));
|
||||
R_SUCCEED();
|
||||
}
|
||||
private:
|
||||
bool CheckRegions(s64 *out_current_size, s64 offset, s64 size) const {
|
||||
/* Check if our region contains the access. */
|
||||
if (m_region.offset <= offset) {
|
||||
if (offset < m_region.offset + m_region.size) {
|
||||
if (m_region.offset + m_region.size <= offset + size) {
|
||||
*out_current_size = m_region.offset + m_region.size - offset;
|
||||
} else {
|
||||
*out_current_size = size;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
*out_current_size = size;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (m_region.offset <= offset + size) {
|
||||
*out_current_size = m_region.offset - offset;
|
||||
} else {
|
||||
*out_current_size = size;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user