fs: first pass at compressed storage (works on iridium with wip hac2l code)

This commit is contained in:
Michael Scire 2022-03-12 13:03:17 -08:00
parent 502a89e1e3
commit 1d0c9ae71a
34 changed files with 2375 additions and 722 deletions

View file

@ -0,0 +1,122 @@
/*
* 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::fs {
class IBufferManager {
public:
class BufferAttribute {
private:
s32 m_level;
public:
constexpr BufferAttribute() : m_level(0) { /* ... */ }
constexpr explicit BufferAttribute(s32 l) : m_level(l) { /* ... */ }
constexpr s32 GetLevel() const { return m_level; }
};
using CacheHandle = u64;
static constexpr s32 BufferLevelMin = 0;
using MemoryRange = std::pair<uintptr_t, size_t>;
static constexpr ALWAYS_INLINE MemoryRange MakeMemoryRange(uintptr_t address, size_t size) { return MemoryRange(address, size); }
public:
virtual ~IBufferManager() { /* ... */ }
ALWAYS_INLINE const MemoryRange AllocateBuffer(size_t size, const BufferAttribute &attr) {
return this->DoAllocateBuffer(size, attr);
}
ALWAYS_INLINE const MemoryRange AllocateBuffer(size_t size) {
return this->DoAllocateBuffer(size, BufferAttribute());
}
ALWAYS_INLINE void DeallocateBuffer(uintptr_t address, size_t size) {
return this->DoDeallocateBuffer(address, size);
}
ALWAYS_INLINE void DeallocateBuffer(const MemoryRange &memory_range) {
return this->DoDeallocateBuffer(memory_range.first, memory_range.second);
}
ALWAYS_INLINE CacheHandle RegisterCache(uintptr_t address, size_t size, const BufferAttribute &attr) {
return this->DoRegisterCache(address, size, attr);
}
ALWAYS_INLINE CacheHandle RegisterCache(const MemoryRange &memory_range, const BufferAttribute &attr) {
return this->DoRegisterCache(memory_range.first, memory_range.second, attr);
}
ALWAYS_INLINE const std::pair<uintptr_t, size_t> AcquireCache(CacheHandle handle) {
return this->DoAcquireCache(handle);
}
ALWAYS_INLINE size_t GetTotalSize() const {
return this->DoGetTotalSize();
}
ALWAYS_INLINE size_t GetFreeSize() const {
return this->DoGetFreeSize();
}
ALWAYS_INLINE size_t GetTotalAllocatableSize() const {
return this->DoGetTotalAllocatableSize();
}
ALWAYS_INLINE size_t GetFreeSizePeak() const {
return this->DoGetFreeSizePeak();
}
ALWAYS_INLINE size_t GetTotalAllocatableSizePeak() const {
return this->DoGetTotalAllocatableSizePeak();
}
ALWAYS_INLINE size_t GetRetriedCount() const {
return this->DoGetRetriedCount();
}
ALWAYS_INLINE void ClearPeak() {
return this->DoClearPeak();
}
protected:
virtual const MemoryRange DoAllocateBuffer(size_t size, const BufferAttribute &attr) = 0;
virtual void DoDeallocateBuffer(uintptr_t address, size_t size) = 0;
virtual CacheHandle DoRegisterCache(uintptr_t address, size_t size, const BufferAttribute &attr) = 0;
virtual const MemoryRange DoAcquireCache(CacheHandle handle) = 0;
virtual size_t DoGetTotalSize() const = 0;
virtual size_t DoGetFreeSize() const = 0;
virtual size_t DoGetTotalAllocatableSize() const = 0;
virtual size_t DoGetFreeSizePeak() const = 0;
virtual size_t DoGetTotalAllocatableSizePeak() const = 0;
virtual size_t DoGetRetriedCount() const = 0;
virtual void DoClearPeak() = 0;
};
}

View file

@ -16,7 +16,7 @@
#pragma once #pragma once
#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/fs/fs_i_buffer_manager.hpp>
#include <stratosphere/fssystem/fssystem_i_hash_256_generator.hpp> #include <stratosphere/fssystem/fssystem_i_hash_256_generator.hpp>
namespace ams::fssystem { namespace ams::fssystem {
@ -35,10 +35,10 @@ namespace ams::fssrv::fscreator {
MemoryResource *m_allocator; MemoryResource *m_allocator;
const fssystem::NcaCryptoConfiguration &m_nca_crypto_cfg; const fssystem::NcaCryptoConfiguration &m_nca_crypto_cfg;
const fssystem::NcaCompressionConfiguration &m_nca_compression_cfg; const fssystem::NcaCompressionConfiguration &m_nca_compression_cfg;
fssystem::IBufferManager * const m_buffer_manager; fs::IBufferManager * const m_buffer_manager;
fssystem::IHash256GeneratorFactorySelector * const m_hash_generator_factory_selector; fssystem::IHash256GeneratorFactorySelector * const m_hash_generator_factory_selector;
public: public:
explicit StorageOnNcaCreator(MemoryResource *mr, const fssystem::NcaCryptoConfiguration &cfg, const fssystem::NcaCompressionConfiguration &c_cfg, fssystem::IBufferManager *bm, fssystem::IHash256GeneratorFactorySelector *hgfs) explicit StorageOnNcaCreator(MemoryResource *mr, const fssystem::NcaCryptoConfiguration &cfg, const fssystem::NcaCompressionConfiguration &c_cfg, fs::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) : m_allocator(mr), m_nca_crypto_cfg(cfg), m_nca_compression_cfg(c_cfg), m_buffer_manager(bm), m_hash_generator_factory_selector(hgfs)
{ {
/* ... */ /* ... */

View file

@ -16,6 +16,7 @@
#pragma once #pragma once
#include <vapours.hpp> #include <vapours.hpp>
#include <stratosphere/os.hpp> #include <stratosphere/os.hpp>
#include <stratosphere/fs/fs_i_buffer_manager.hpp>
namespace ams::fssystem::buffers { namespace ams::fssystem::buffers {
@ -88,13 +89,13 @@ namespace ams::fssystem::buffers {
}; };
template<typename IsValidBufferFunction> template<typename IsValidBufferFunction>
Result AllocateBufferUsingBufferManagerContext(std::pair<uintptr_t, size_t> *out, fssystem::IBufferManager *buffer_manager, size_t size, const IBufferManager::BufferAttribute attribute, IsValidBufferFunction is_valid_buffer, const char *func_name) { Result AllocateBufferUsingBufferManagerContext(fs::IBufferManager::MemoryRange *out, fs::IBufferManager *buffer_manager, size_t size, const fs::IBufferManager::BufferAttribute attribute, IsValidBufferFunction is_valid_buffer, const char *func_name) {
AMS_ASSERT(out != nullptr); AMS_ASSERT(out != nullptr);
AMS_ASSERT(buffer_manager != nullptr); AMS_ASSERT(buffer_manager != nullptr);
AMS_ASSERT(func_name != nullptr); AMS_ASSERT(func_name != nullptr);
/* Clear the output. */ /* Clear the output. */
*out = std::pair<uintptr_t, size_t>(0, 0); *out = fs::IBufferManager::MakeMemoryRange(0, 0);
/* Get the context. */ /* Get the context. */
auto context = GetBufferManagerContext(); auto context = GetBufferManagerContext();

View file

@ -16,6 +16,7 @@
#pragma once #pragma once
#include <vapours.hpp> #include <vapours.hpp>
#include <stratosphere/fs/impl/fs_newable.hpp> #include <stratosphere/fs/impl/fs_newable.hpp>
#include <stratosphere/fs/fs_i_buffer_manager.hpp>
namespace ams::fssystem { namespace ams::fssystem {

View file

@ -17,12 +17,12 @@
#include <vapours.hpp> #include <vapours.hpp>
#include <stratosphere/lmem.hpp> #include <stratosphere/lmem.hpp>
#include <stratosphere/fs/fs_memory_management.hpp> #include <stratosphere/fs/fs_memory_management.hpp>
#include <stratosphere/fssystem/buffers/fssystem_i_buffer_manager.hpp> #include <stratosphere/fs/fs_i_buffer_manager.hpp>
#include <stratosphere/fssystem/buffers/fssystem_file_system_buddy_heap.hpp> #include <stratosphere/fssystem/buffers/fssystem_file_system_buddy_heap.hpp>
namespace ams::fssystem { namespace ams::fssystem {
class FileSystemBufferManager : public IBufferManager { class FileSystemBufferManager : public fs::IBufferManager {
NON_COPYABLE(FileSystemBufferManager); NON_COPYABLE(FileSystemBufferManager);
NON_MOVEABLE(FileSystemBufferManager); NON_MOVEABLE(FileSystemBufferManager);
public: public:
@ -194,7 +194,7 @@ namespace ams::fssystem {
size_t m_peak_free_size; size_t m_peak_free_size;
size_t m_peak_total_allocatable_size; size_t m_peak_total_allocatable_size;
size_t m_retried_count; size_t m_retried_count;
mutable os::SdkRecursiveMutex m_mutex; mutable os::SdkMutex m_mutex;
public: public:
static constexpr size_t QueryWorkBufferSize(s32 max_cache_count, s32 max_order) { static constexpr size_t QueryWorkBufferSize(s32 max_cache_count, s32 max_order) {
const auto buddy_size = FileSystemBuddyHeap::QueryWorkBufferSize(max_order); const auto buddy_size = FileSystemBuddyHeap::QueryWorkBufferSize(max_order);
@ -269,27 +269,27 @@ namespace ams::fssystem {
m_cache_handle_table.Finalize(); m_cache_handle_table.Finalize();
} }
private: private:
virtual const std::pair<uintptr_t, size_t> AllocateBufferImpl(size_t size, const BufferAttribute &attr) override; virtual const std::pair<uintptr_t, size_t> DoAllocateBuffer(size_t size, const BufferAttribute &attr) override;
virtual void DeallocateBufferImpl(uintptr_t address, size_t size) override; virtual void DoDeallocateBuffer(uintptr_t address, size_t size) override;
virtual CacheHandle RegisterCacheImpl(uintptr_t address, size_t size, const BufferAttribute &attr) override; virtual CacheHandle DoRegisterCache(uintptr_t address, size_t size, const BufferAttribute &attr) override;
virtual const std::pair<uintptr_t, size_t> AcquireCacheImpl(CacheHandle handle) override; virtual const std::pair<uintptr_t, size_t> DoAcquireCache(CacheHandle handle) override;
virtual size_t GetTotalSizeImpl() const override; virtual size_t DoGetTotalSize() const override;
virtual size_t GetFreeSizeImpl() const override; virtual size_t DoGetFreeSize() const override;
virtual size_t GetTotalAllocatableSizeImpl() const override; virtual size_t DoGetTotalAllocatableSize() const override;
virtual size_t GetPeakFreeSizeImpl() const override; virtual size_t DoGetFreeSizePeak() const override;
virtual size_t GetPeakTotalAllocatableSizeImpl() const override; virtual size_t DoGetTotalAllocatableSizePeak() const override;
virtual size_t GetRetriedCountImpl() const override; virtual size_t DoGetRetriedCount() const override;
virtual void ClearPeakImpl() override; virtual void DoClearPeak() override;
}; };
} }

View file

@ -1,110 +0,0 @@
/*
* 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 IBufferManager {
public:
class BufferAttribute {
private:
s32 m_level;
public:
constexpr BufferAttribute() : m_level(0) { /* ... */ }
constexpr explicit BufferAttribute(s32 l) : m_level(l) { /* ... */ }
constexpr s32 GetLevel() const { return m_level; }
};
using CacheHandle = s64;
static constexpr s32 BufferLevelMin = 0;
public:
virtual ~IBufferManager() { /* ... */ }
const std::pair<uintptr_t, size_t> AllocateBuffer(size_t size, const BufferAttribute &attr) {
return this->AllocateBufferImpl(size, attr);
}
const std::pair<uintptr_t, size_t> AllocateBuffer(size_t size) {
return this->AllocateBufferImpl(size, BufferAttribute());
}
void DeallocateBuffer(uintptr_t address, size_t size) {
return this->DeallocateBufferImpl(address, size);
}
CacheHandle RegisterCache(uintptr_t address, size_t size, const BufferAttribute &attr) {
return this->RegisterCacheImpl(address, size, attr);
}
const std::pair<uintptr_t, size_t> AcquireCache(CacheHandle handle) {
return this->AcquireCacheImpl(handle);
}
size_t GetTotalSize() const {
return this->GetTotalSizeImpl();
}
size_t GetFreeSize() const {
return this->GetFreeSizeImpl();
}
size_t GetTotalAllocatableSize() const {
return this->GetTotalAllocatableSizeImpl();
}
size_t GetPeakFreeSize() const {
return this->GetPeakFreeSizeImpl();
}
size_t GetPeakTotalAllocatableSize() const {
return this->GetPeakTotalAllocatableSizeImpl();
}
size_t GetRetriedCount() const {
return this->GetRetriedCountImpl();
}
void ClearPeak() {
return this->ClearPeakImpl();
}
protected:
virtual const std::pair<uintptr_t, size_t> AllocateBufferImpl(size_t size, const BufferAttribute &attr) = 0;
virtual void DeallocateBufferImpl(uintptr_t address, size_t size) = 0;
virtual CacheHandle RegisterCacheImpl(uintptr_t address, size_t size, const BufferAttribute &attr) = 0;
virtual const std::pair<uintptr_t, size_t> AcquireCacheImpl(CacheHandle handle) = 0;
virtual size_t GetTotalSizeImpl() const = 0;
virtual size_t GetFreeSizeImpl() const = 0;
virtual size_t GetTotalAllocatableSizeImpl() const = 0;
virtual size_t GetPeakFreeSizeImpl() const = 0;
virtual size_t GetPeakTotalAllocatableSizeImpl() const = 0;
virtual size_t GetRetriedCountImpl() const = 0;
virtual void ClearPeakImpl() = 0;
};
}

View file

@ -94,22 +94,27 @@ namespace ams::fssystem {
virtual Result GetSize(s64 *out) override { virtual Result GetSize(s64 *out) override {
AMS_ASSERT(out != nullptr); AMS_ASSERT(out != nullptr);
*out = m_table.GetSize();
return ResultSuccess(); BucketTree::Offsets offsets;
R_TRY(m_table.GetOffsets(std::addressof(offsets)));
*out = offsets.end_offset;
R_SUCCEED();
} }
virtual Result Flush() override { virtual Result Flush() override {
return ResultSuccess(); R_SUCCEED();
} }
virtual Result Write(s64 offset, const void *buffer, size_t size) override { virtual Result Write(s64 offset, const void *buffer, size_t size) override {
AMS_UNUSED(offset, buffer, size); AMS_UNUSED(offset, buffer, size);
return fs::ResultUnsupportedOperationInAesCtrCounterExtendedStorageA(); R_THROW(fs::ResultUnsupportedOperationInAesCtrCounterExtendedStorageA());
} }
virtual Result SetSize(s64 size) override { virtual Result SetSize(s64 size) override {
AMS_UNUSED(size); AMS_UNUSED(size);
return fs::ResultUnsupportedOperationInAesCtrCounterExtendedStorageB(); R_THROW(fs::ResultUnsupportedOperationInAesCtrCounterExtendedStorageB());
} }
private: private:
Result Initialize(IAllocator *allocator, const void *key, size_t key_size, u32 secure_value, fs::SubStorage data_storage, fs::SubStorage table_storage); Result Initialize(IAllocator *allocator, const void *key, size_t key_size, u32 secure_value, fs::SubStorage data_storage, fs::SubStorage table_storage);

View file

@ -53,6 +53,29 @@ namespace ams::fssystem {
static_assert(util::is_pod<NodeHeader>::value); static_assert(util::is_pod<NodeHeader>::value);
static_assert(sizeof(NodeHeader) == 0x10); static_assert(sizeof(NodeHeader) == 0x10);
struct Offsets {
s64 start_offset;
s64 end_offset;
constexpr bool IsInclude(s64 offset) const {
return this->start_offset <= offset & offset < this->end_offset;
}
constexpr bool IsInclude(s64 offset, s64 size) const {
return size > 0 && this->start_offset <= offset && size <= (this->end_offset - offset);
}
};
static_assert(util::is_pod<Offsets>::value);
static_assert(sizeof(Offsets) == 0x10);
struct OffsetCache {
Offsets offsets;
os::SdkMutex mutex;
bool is_initialized;
constexpr OffsetCache() : offsets{ -1, -1 }, mutex(), is_initialized(false) { /* ... */ }
};
class ContinuousReadingInfo { class ContinuousReadingInfo {
private: private:
size_t m_read_size; size_t m_read_size;
@ -213,10 +236,9 @@ namespace ams::fssystem {
s32 m_entry_count; s32 m_entry_count;
s32 m_offset_count; s32 m_offset_count;
s32 m_entry_set_count; s32 m_entry_set_count;
s64 m_start_offset; OffsetCache m_offset_cache;
s64 m_end_offset;
public: public:
BucketTree() : m_node_storage(), m_entry_storage(), m_node_l1(), m_node_size(), m_entry_size(), m_entry_count(), m_offset_count(), m_entry_set_count(), m_start_offset(), m_end_offset() { /* ... */ } BucketTree() : m_node_storage(), m_entry_storage(), m_node_l1(), m_node_size(), m_entry_size(), m_entry_count(), m_offset_count(), m_entry_set_count(), m_offset_cache() { /* ... */ }
~BucketTree() { this->Finalize(); } ~BucketTree() { this->Finalize(); }
Result Initialize(IAllocator *allocator, fs::SubStorage node_storage, fs::SubStorage entry_storage, size_t node_size, size_t entry_size, s32 entry_count); Result Initialize(IAllocator *allocator, fs::SubStorage node_storage, fs::SubStorage entry_storage, size_t node_size, size_t entry_size, s32 entry_count);
@ -226,22 +248,19 @@ namespace ams::fssystem {
bool IsInitialized() const { return m_node_size > 0; } bool IsInitialized() const { return m_node_size > 0; }
bool IsEmpty() const { return m_entry_size == 0; } bool IsEmpty() const { return m_entry_size == 0; }
Result Find(Visitor *visitor, s64 virtual_address) const; Result Find(Visitor *visitor, s64 virtual_address);
Result InvalidateCache(); Result InvalidateCache();
s32 GetEntryCount() const { return m_entry_count; } s32 GetEntryCount() const { return m_entry_count; }
IAllocator *GetAllocator() const { return m_node_l1.GetAllocator(); } IAllocator *GetAllocator() const { return m_node_l1.GetAllocator(); }
s64 GetStart() const { return m_start_offset; } Result GetOffsets(Offsets *out) {
s64 GetEnd() const { return m_end_offset; } /* Ensure we have an offset cache. */
s64 GetSize() const { return m_end_offset - m_start_offset; } R_TRY(this->EnsureOffsetCache());
bool Includes(s64 offset) const { /* Set the output. */
return m_start_offset <= offset && offset < m_end_offset; *out = m_offset_cache.offsets;
} R_SUCCEED();
bool Includes(s64 offset, s64 size) const {
return size > 0 && m_start_offset <= offset && size <= m_end_offset - offset;
} }
private: private:
template<typename EntryType> template<typename EntryType>
@ -250,6 +269,7 @@ namespace ams::fssystem {
size_t size; size_t size;
NodeHeader entry_set; NodeHeader entry_set;
s32 entry_index; s32 entry_index;
Offsets offsets;
EntryType entry; EntryType entry;
}; };
private: private:
@ -262,6 +282,8 @@ namespace ams::fssystem {
s64 GetEntrySetIndex(s32 node_index, s32 offset_index) const { s64 GetEntrySetIndex(s32 node_index, s32 offset_index) const {
return (m_offset_count - m_node_l1->count) + (m_offset_count * node_index) + offset_index; return (m_offset_count - m_node_l1->count) + (m_offset_count * node_index) + offset_index;
} }
Result EnsureOffsetCache();
}; };
class BucketTree::Visitor { class BucketTree::Visitor {
@ -283,6 +305,7 @@ namespace ams::fssystem {
static_assert(util::is_pod<EntrySetHeader>::value); static_assert(util::is_pod<EntrySetHeader>::value);
private: private:
const BucketTree *m_tree; const BucketTree *m_tree;
BucketTree::Offsets m_offsets;
void *m_entry; void *m_entry;
s32 m_entry_index; s32 m_entry_index;
s32 m_entry_set_count; s32 m_entry_set_count;
@ -314,7 +337,7 @@ namespace ams::fssystem {
const BucketTree *GetTree() const { return m_tree; } const BucketTree *GetTree() const { return m_tree; }
private: private:
Result Initialize(const BucketTree *tree); Result Initialize(const BucketTree *tree, const BucketTree::Offsets &offsets);
Result Find(s64 virtual_address); Result Find(s64 virtual_address);

View file

@ -47,10 +47,15 @@ namespace ams::fssystem {
PooledBuffer pool(m_node_size, 1); PooledBuffer pool(m_node_size, 1);
char *buffer = nullptr; char *buffer = nullptr;
s64 entry_storage_size;
R_TRY(m_entry_storage.GetSize(std::addressof(entry_storage_size)));
/* Read the node. */ /* Read the node. */
if (m_node_size <= pool.GetSize()) { if (m_node_size <= pool.GetSize()) {
buffer = pool.GetBuffer(); buffer = pool.GetBuffer();
const auto ofs = param.entry_set.index * static_cast<s64>(m_node_size); const auto ofs = param.entry_set.index * static_cast<s64>(m_node_size);
R_UNLESS(m_node_size + ofs <= static_cast<size_t>(entry_storage_size), fs::ResultInvalidBucketTreeNodeEntryCount());
R_TRY(m_entry_storage.Read(ofs, buffer, m_node_size)); R_TRY(m_entry_storage.Read(ofs, buffer, m_node_size));
} }
@ -89,7 +94,7 @@ namespace ams::fssystem {
} }
next_entry_offset = next_entry.GetVirtualOffset(); next_entry_offset = next_entry.GetVirtualOffset();
R_UNLESS(this->Includes(next_entry_offset), fs::ResultInvalidIndirectEntryOffset()); R_UNLESS(param.offsets.IsInclude(next_entry_offset), fs::ResultInvalidIndirectEntryOffset());
} else { } else {
next_entry_offset = param.entry_set.offset; next_entry_offset = param.entry_set.offset;
} }
@ -156,6 +161,7 @@ namespace ams::fssystem {
ContinuousReadingParam<EntryType> param = { ContinuousReadingParam<EntryType> param = {
offset, size, m_entry_set.header, m_entry_index offset, size, m_entry_set.header, m_entry_index
}; };
std::memcpy(std::addressof(param.offsets), std::addressof(m_offsets), sizeof(BucketTree::Offsets));
std::memcpy(std::addressof(param.entry), m_entry, sizeof(EntryType)); std::memcpy(std::addressof(param.entry), m_entry, sizeof(EntryType));
/* Scan. */ /* Scan. */

View file

@ -18,9 +18,9 @@
namespace ams::fssystem { namespace ams::fssystem {
enum CompressionType { enum CompressionType : u8 {
CompressionType_None = 0, CompressionType_None = 0,
CompressionType_1 = 1, CompressionType_Zeros = 1,
CompressionType_2 = 2, CompressionType_2 = 2,
CompressionType_Lz4 = 3, CompressionType_Lz4 = 3,
CompressionType_Unknown = 4, CompressionType_Unknown = 4,
@ -29,14 +29,16 @@ namespace ams::fssystem {
using DecompressorFunction = Result (*)(void *, size_t, const void *, size_t); using DecompressorFunction = Result (*)(void *, size_t, const void *, size_t);
using GetDecompressorFunction = DecompressorFunction (*)(CompressionType); using GetDecompressorFunction = DecompressorFunction (*)(CompressionType);
constexpr s64 CompressionBlockAlignment = 0x10;
namespace CompressionTypeUtility { namespace CompressionTypeUtility {
constexpr bool IsBlockAlignmentRequired(CompressionType type) { constexpr bool IsBlockAlignmentRequired(CompressionType type) {
return type != CompressionType_None && type != CompressionType_1; return type != CompressionType_None && type != CompressionType_Zeros;
} }
constexpr bool IsDataStorageAccessRequired(CompressionType type) { constexpr bool IsDataStorageAccessRequired(CompressionType type) {
return type != CompressionType_1; return type != CompressionType_Zeros;
} }
constexpr bool IsRandomAccessible(CompressionType type) { constexpr bool IsRandomAccessible(CompressionType type) {

View file

@ -133,22 +133,26 @@ namespace ams::fssystem {
virtual Result GetSize(s64 *out) override { virtual Result GetSize(s64 *out) override {
AMS_ASSERT(out != nullptr); AMS_ASSERT(out != nullptr);
*out = m_table.GetEnd();
return ResultSuccess(); BucketTree::Offsets offsets;
R_TRY(m_table.GetOffsets(std::addressof(offsets)));
*out = offsets.end_offset;
R_SUCCEED();
} }
virtual Result Flush() override { virtual Result Flush() override {
return ResultSuccess(); R_SUCCEED();
} }
virtual Result Write(s64 offset, const void *buffer, size_t size) override { virtual Result Write(s64 offset, const void *buffer, size_t size) override {
AMS_UNUSED(offset, buffer, size); AMS_UNUSED(offset, buffer, size);
return fs::ResultUnsupportedOperationInIndirectStorageA(); R_THROW(fs::ResultUnsupportedOperationInIndirectStorageA());
} }
virtual Result SetSize(s64 size) override { virtual Result SetSize(s64 size) override {
AMS_UNUSED(size); AMS_UNUSED(size);
return fs::ResultUnsupportedOperationInIndirectStorageB(); R_THROW(fs::ResultUnsupportedOperationInIndirectStorageB());
} }
protected: protected:
BucketTree &GetEntryTable() { return m_table; } BucketTree &GetEntryTable() { return m_table; }
@ -158,7 +162,7 @@ namespace ams::fssystem {
return m_data_storage[index]; return m_data_storage[index];
} }
template<bool ContinuousCheck, typename F> template<bool ContinuousCheck, bool RangeCheck, typename F>
Result OperatePerEntry(s64 offset, s64 size, F func); Result OperatePerEntry(s64 offset, s64 size, F func);
}; };

View file

@ -18,7 +18,7 @@
namespace ams::fssystem { namespace ams::fssystem {
template<bool ContinuousCheck, typename F> template<bool ContinuousCheck, bool RangeCheck, typename F>
Result IndirectStorage::OperatePerEntry(s64 offset, s64 size, F func) { Result IndirectStorage::OperatePerEntry(s64 offset, s64 size, F func) {
/* Validate preconditions. */ /* Validate preconditions. */
AMS_ASSERT(offset >= 0); AMS_ASSERT(offset >= 0);
@ -28,15 +28,19 @@ namespace ams::fssystem {
/* Succeed if there's nothing to operate on. */ /* Succeed if there's nothing to operate on. */
R_SUCCEED_IF(size == 0); R_SUCCEED_IF(size == 0);
/* Get the table offsets. */
BucketTree::Offsets table_offsets;
R_TRY(m_table.GetOffsets(std::addressof(table_offsets)));
/* Validate arguments. */ /* Validate arguments. */
R_UNLESS(m_table.Includes(offset, size), fs::ResultOutOfRange()); R_UNLESS(table_offsets.IsInclude(offset, size), fs::ResultOutOfRange());
/* Find the offset in our tree. */ /* Find the offset in our tree. */
BucketTree::Visitor visitor; BucketTree::Visitor visitor;
R_TRY(m_table.Find(std::addressof(visitor), offset)); R_TRY(m_table.Find(std::addressof(visitor), offset));
{ {
const auto entry_offset = visitor.Get<Entry>()->GetVirtualOffset(); const auto entry_offset = visitor.Get<Entry>()->GetVirtualOffset();
R_UNLESS(0 <= entry_offset && m_table.Includes(entry_offset), fs::ResultInvalidIndirectEntryOffset()); R_UNLESS(0 <= entry_offset && table_offsets.IsInclude(entry_offset), fs::ResultInvalidIndirectEntryOffset());
} }
/* Prepare to operate in chunks. */ /* Prepare to operate in chunks. */
@ -67,16 +71,21 @@ namespace ams::fssystem {
/* Ensure that we can process. */ /* Ensure that we can process. */
R_UNLESS(cur_entry.storage_index == 0, fs::ResultInvalidIndirectEntryStorageIndex()); R_UNLESS(cur_entry.storage_index == 0, fs::ResultInvalidIndirectEntryStorageIndex());
/* Get the current data storage's size. */
s64 cur_data_storage_size;
R_TRY(m_data_storage[0].GetSize(std::addressof(cur_data_storage_size)));
/* Ensure that we remain within range. */ /* Ensure that we remain within range. */
const auto data_offset = cur_offset - cur_entry_offset; const auto data_offset = cur_offset - cur_entry_offset;
const auto cur_entry_phys_offset = cur_entry.GetPhysicalOffset(); const auto cur_entry_phys_offset = cur_entry.GetPhysicalOffset();
const auto cur_size = static_cast<s64>(cr_info.GetReadSize()); const auto cur_size = static_cast<s64>(cr_info.GetReadSize());
/* If we should, verify the range. */
if constexpr (RangeCheck) {
/* Get the current data storage's size. */
s64 cur_data_storage_size;
R_TRY(m_data_storage[0].GetSize(std::addressof(cur_data_storage_size)));
R_UNLESS(0 <= cur_entry_phys_offset && cur_entry_phys_offset <= cur_data_storage_size, fs::ResultInvalidIndirectEntryOffset()); R_UNLESS(0 <= cur_entry_phys_offset && cur_entry_phys_offset <= cur_data_storage_size, fs::ResultInvalidIndirectEntryOffset());
R_UNLESS(cur_entry_phys_offset + data_offset + cur_size <= cur_data_storage_size, fs::ResultInvalidIndirectStorageSize()); R_UNLESS(cur_entry_phys_offset + data_offset + cur_size <= cur_data_storage_size, fs::ResultInvalidIndirectStorageSize());
}
/* Operate. */ /* Operate. */
R_TRY(func(std::addressof(m_data_storage[0]), cur_entry_phys_offset + data_offset, cur_offset, cur_size)); R_TRY(func(std::addressof(m_data_storage[0]), cur_entry_phys_offset + data_offset, cur_offset, cur_size));
@ -91,20 +100,20 @@ namespace ams::fssystem {
if (visitor.CanMoveNext()) { if (visitor.CanMoveNext()) {
R_TRY(visitor.MoveNext()); R_TRY(visitor.MoveNext());
next_entry_offset = visitor.Get<Entry>()->GetVirtualOffset(); next_entry_offset = visitor.Get<Entry>()->GetVirtualOffset();
R_UNLESS(m_table.Includes(next_entry_offset), fs::ResultInvalidIndirectEntryOffset()); R_UNLESS(table_offsets.IsInclude(next_entry_offset), fs::ResultInvalidIndirectEntryOffset());
} else { } else {
next_entry_offset = m_table.GetEnd(); next_entry_offset = table_offsets.end_offset;
} }
R_UNLESS(cur_offset < next_entry_offset, fs::ResultInvalidIndirectEntryOffset()); R_UNLESS(cur_offset < next_entry_offset, fs::ResultInvalidIndirectEntryOffset());
/* Get the offset of the entry in the data we read. */ /* Get the offset of the entry in the data we read. */
const auto data_offset = cur_offset - cur_entry_offset; const auto data_offset = cur_offset - cur_entry_offset;
const auto data_size = (next_entry_offset - cur_entry_offset) - data_offset; const auto data_size = (next_entry_offset - cur_entry_offset);
AMS_ASSERT(data_size > 0); AMS_ASSERT(data_size > 0);
/* Determine how much is left. */ /* Determine how much is left. */
const auto remaining_size = end_offset - cur_offset; const auto remaining_size = end_offset - cur_offset;
const auto cur_size = std::min(remaining_size, data_size); const auto cur_size = std::min<s64>(remaining_size, data_size - data_offset);
AMS_ASSERT(cur_size <= size); AMS_ASSERT(cur_size <= size);
/* Operate, if we need to. */ /* Operate, if we need to. */
@ -116,14 +125,17 @@ namespace ams::fssystem {
} }
if (needs_operate) { if (needs_operate) {
const auto cur_entry_phys_offset = cur_entry.GetPhysicalOffset();
if constexpr (RangeCheck) {
/* Get the current data storage's size. */ /* Get the current data storage's size. */
s64 cur_data_storage_size; s64 cur_data_storage_size;
R_TRY(m_data_storage[cur_entry.storage_index].GetSize(std::addressof(cur_data_storage_size))); R_TRY(m_data_storage[cur_entry.storage_index].GetSize(std::addressof(cur_data_storage_size)));
/* Ensure that we remain within range. */ /* Ensure that we remain within range. */
const auto cur_entry_phys_offset = cur_entry.GetPhysicalOffset();
R_UNLESS(0 <= cur_entry_phys_offset && cur_entry_phys_offset <= cur_data_storage_size, fs::ResultIndirectStorageCorrupted()); R_UNLESS(0 <= cur_entry_phys_offset && cur_entry_phys_offset <= cur_data_storage_size, fs::ResultIndirectStorageCorrupted());
R_UNLESS(cur_entry_phys_offset + data_offset + cur_size <= cur_data_storage_size, fs::ResultIndirectStorageCorrupted()); R_UNLESS(cur_entry_phys_offset + data_offset + cur_size <= cur_data_storage_size, fs::ResultIndirectStorageCorrupted());
}
R_TRY(func(std::addressof(m_data_storage[cur_entry.storage_index]), cur_entry_phys_offset + data_offset, cur_offset, cur_size)); R_TRY(func(std::addressof(m_data_storage[cur_entry.storage_index]), cur_entry_phys_offset + data_offset, cur_offset, cur_size));
} }

View file

@ -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, IHash256GeneratorFactory *hgf); Result Initialize(save::HierarchicalIntegrityVerificationInformation level_hash_info, Hash master_hash, save::HierarchicalIntegrityVerificationStorage::HierarchicalStorageInformation storage_info, fs::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 {

View file

@ -21,7 +21,7 @@
#include <stratosphere/fssystem/fssystem_i_hash_256_generator.hpp> #include <stratosphere/fssystem/fssystem_i_hash_256_generator.hpp>
#include <stratosphere/fssystem/fssystem_asynchronous_access.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/fs/fs_i_buffer_manager.hpp>
namespace ams::fssystem { namespace ams::fssystem {
@ -228,17 +228,17 @@ namespace ams::fssystem {
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; fs::IBufferManager * const m_buffer_manager;
fssystem::IHash256GeneratorFactorySelector * const m_hash_generator_factory_selector; 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, IHash256GeneratorFactorySelector *hgf_selector) : m_original_reader(), m_reader(reader), m_allocator(allocator), m_buffer_manager(buffer_manager), m_hash_generator_factory_selector(hgf_selector) { NcaFileSystemDriver(std::shared_ptr<NcaReader> reader, MemoryResource *allocator, fs::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); 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, 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) { NcaFileSystemDriver(std::shared_ptr<NcaReader> original_reader, std::shared_ptr<NcaReader> reader, MemoryResource *allocator, fs::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); AMS_ASSERT(m_hash_generator_factory_selector != nullptr);
} }
@ -278,7 +278,7 @@ namespace ams::fssystem {
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); 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: 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); 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, fs::IBufferManager *buffer_manager);
}; };
} }

View file

@ -36,32 +36,32 @@ namespace ams::fssystem {
if (size > 0) { if (size > 0) {
std::memset(buffer, 0, size); std::memset(buffer, 0, size);
} }
return ResultSuccess(); 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 { 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 {
AMS_UNUSED(dst, dst_size, op_id, offset, size, src, src_size); AMS_UNUSED(dst, dst_size, op_id, offset, size, src, src_size);
return ResultSuccess(); R_SUCCEED();
} }
virtual Result GetSize(s64 *out) override { virtual Result GetSize(s64 *out) override {
AMS_ASSERT(out != nullptr); AMS_ASSERT(out != nullptr);
*out = std::numeric_limits<s64>::max(); *out = std::numeric_limits<s64>::max();
return ResultSuccess(); R_SUCCEED();
} }
virtual Result Flush() override { virtual Result Flush() override {
return ResultSuccess(); R_SUCCEED();
} }
virtual Result Write(s64 offset, const void *buffer, size_t size) override { virtual Result Write(s64 offset, const void *buffer, size_t size) override {
AMS_UNUSED(offset, buffer, size); AMS_UNUSED(offset, buffer, size);
return fs::ResultUnsupportedOperationInZeroStorageA(); R_THROW(fs::ResultUnsupportedOperationInZeroStorageA());
} }
virtual Result SetSize(s64 size) override { virtual Result SetSize(s64 size) override {
AMS_UNUSED(size); AMS_UNUSED(size);
return fs::ResultUnsupportedOperationInZeroStorageB(); R_THROW(fs::ResultUnsupportedOperationInZeroStorageB());
} }
}; };
private: private:

View file

@ -0,0 +1,278 @@
/*
* 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/impl/fs_newable.hpp>
namespace ams::fssystem::impl {
template<typename CacheEntryType, typename AllocatorType>
class BlockCacheManager {
NON_COPYABLE(BlockCacheManager);
NON_MOVEABLE(BlockCacheManager);
public:
using MemoryRange = AllocatorType::MemoryRange;
using CacheIndex = s32;
using BufferAttribute = AllocatorType::BufferAttribute;
static constexpr CacheIndex InvalidCacheIndex = -1;
using CacheEntry = CacheEntryType;
static_assert(util::is_pod<CacheEntry>::value);
private:
AllocatorType *m_allocator = nullptr;
std::unique_ptr<CacheEntry[], ::ams::fs::impl::Deleter> m_entries{};
s32 m_max_cache_entry_count = 0;
public:
constexpr BlockCacheManager() = default;
public:
Result Initialize(AllocatorType *allocator, s32 max_entries) {
/* Check pre-conditions. */
AMS_ASSERT(m_allocator == nullptr);
AMS_ASSERT(m_entries == nullptr);
AMS_ASSERT(allocator != nullptr);
/* Setup our entries buffer, if necessary. */
if (max_entries > 0) {
/* Create the entries. */
m_entries = fs::impl::MakeUnique<CacheEntry[]>(static_cast<size_t>(max_entries));
R_UNLESS(m_entries != nullptr, fs::ResultAllocationFailureInMakeUnique());
/* Clear the entries. */
std::memset(m_entries.get(), 0, sizeof(CacheEntry) * max_entries);
}
/* Set fields. */
m_allocator = allocator;
m_max_cache_entry_count = max_entries;
R_SUCCEED();
}
void Finalize() {
/* Reset all fields. */
m_entries.reset(nullptr);
m_allocator = nullptr;
m_max_cache_entry_count = 0;
}
bool IsInitialized() const {
return m_allocator != nullptr;
}
AllocatorType *GetAllocator() { return m_allocator; }
s32 GetCount() const { return m_max_cache_entry_count; }
void AcquireCacheEntry(CacheEntry *out_entry, MemoryRange *out_range, CacheIndex index) {
/* Check pre-conditions. */
AMS_ASSERT(this->IsInitialized());
AMS_ASSERT(index < this->GetCount());
/* Get the entry. */
auto &entry = m_entries[index];
/* Set the out range. */
if (entry.IsWriteBack()) {
*out_range = AllocatorType::MakeMemoryRange(entry.memory_address, entry.memory_size);
} else {
*out_range = m_allocator->AcquireCache(entry.handle);
}
/* Set the out entry. */
*out_entry = entry;
/* Sanity check. */
AMS_ASSERT(out_entry->is_valid);
AMS_ASSERT(out_entry->is_cached);
/* Clear our local entry. */
entry.is_valid = false;
entry.handle = 0;
entry.memory_address = 0;
entry.memory_size = 0;
entry.lru_counter = 0;
/* Update the out entry. */
out_entry->is_valid = true;
out_entry->handle = 0;
out_entry->memory_address = 0;
out_entry->memory_size = 0;
out_entry->lru_counter = 0;
}
bool ExistsRedundantCacheEntry(const CacheEntry &entry) const {
/* Check pre-conditions. */
AMS_ASSERT(this->IsInitialized());
/* Iterate over all entries, checking if any contain our extents. */
for (auto i = 0; i < this->GetCount(); ++i) {
if (const auto &cur_entry = m_entries[i]; cur_entry.IsAllocated()) {
if (cur_entry.range.offset < entry.range.GetEndOffset() && entry.range.offset < cur_entry.range.GetEndOffset()) {
return true;
}
}
}
return false;
}
void GetEmptyCacheEntryIndex(CacheIndex *out_empty, CacheIndex *out_lru) {
/* Find empty and lru indices. */
CacheIndex empty = InvalidCacheIndex, lru = InvalidCacheIndex;
for (auto i = 0; i < this->GetCount(); ++i) {
if (auto &entry = m_entries[i]; entry.is_valid) {
/* Get/Update the lru counter. */
if (entry.lru_counter != std::numeric_limits<decltype(entry.lru_counter)>::max()) {
++entry.lru_counter;
}
/* Update the lru index. */
if (lru == InvalidCacheIndex || m_entries[lru].lru_counter < entry.lru_counter) {
lru = i;
}
} else {
/* The entry is invalid, so we can update the empty index. */
if (empty == InvalidCacheIndex) {
empty = i;
}
}
}
/* Set the output. */
*out_empty = empty;
*out_lru = lru;
}
void Invalidate() {
/* Check pre-conditions. */
AMS_ASSERT(this->IsInitialized());
/* Invalidate all entries. */
for (auto i = 0; i < this->GetCount(); ++i) {
if (m_entries[i].is_valid) {
this->InvalidateCacheEntry(i);
}
}
}
void InvalidateCacheEntry(CacheIndex index) {
/* Check pre-conditions. */
AMS_ASSERT(this->IsInitialized());
AMS_ASSERT(index < this->GetCount());
/* Get the entry. */
auto &entry = m_entries[index];
AMS_ASSERT(entry.is_valid);
/* If necessary, perform write-back. */
if (entry.IsWriteBack()) {
AMS_ASSERT(entry.memory_address != 0 && entry.handle == 0);
m_allocator->DeallocateBuffer(AllocatorType::MakeMemoryRange(entry.memory_address, entry.memory_size));
} else {
AMS_ASSERT(entry.memory_address == 0 && entry.handle != 0);
if (const auto memory_range = m_allocator->AcquireCache(entry.handle); memory_range.first) {
m_allocator->DeallocateBuffer(memory_range);
}
}
/* Set entry as invalid. */
entry.is_valid = false;
entry.Invalidate();
}
void RegisterCacheEntry(CacheIndex index, const MemoryRange &memory_range, const BufferAttribute &attribute) {
/* Check pre-conditions. */
AMS_ASSERT(this->IsInitialized());
/* Register the entry. */
if (auto &entry = m_entries[index]; entry.IsWriteBack()) {
entry.handle = 0;
entry.memory_address = memory_range.first;
entry.memory_size = memory_range.second;
} else {
entry.handle = m_allocator->RegisterCache(memory_range, attribute);
entry.memory_address = 0;
entry.memory_size = 0;
}
}
void ReleaseCacheEntry(CacheEntry *entry, const MemoryRange &memory_range) {
/* Check pre-conditions. */
AMS_ASSERT(this->IsInitialized());
/* Release the entry. */
m_allocator->DeallocateBuffer(memory_range);
entry->is_valid = false;
entry->is_cached = false;
}
void ReleaseCacheEntry(CacheIndex index, const MemoryRange &memory_range) {
return this->ReleaseCacheEntry(std::addressof(m_entries[index]), memory_range);
}
bool SetCacheEntry(CacheIndex index, const CacheEntry &entry, const MemoryRange &memory_range, const BufferAttribute &attr) {
/* Check pre-conditions. */
AMS_ASSERT(this->IsInitialized());
AMS_ASSERT(0 <= index && index < this->GetCount());
/* Write the entry. */
m_entries[index] = entry;
/* Sanity check. */
AMS_ASSERT(entry.is_valid);
AMS_ASSERT(entry.is_cached);
AMS_ASSERT(entry.handle == 0);
AMS_ASSERT(entry.memory_address == 0);
/* Register or release. */
if (this->ExistsRedundantCacheEntry(entry)) {
this->ReleaseCacheEntry(index, memory_range);
return false;
} else {
this->RegisterCacheEntry(index, memory_range, attr);
return true;
}
}
bool SetCacheEntry(CacheIndex index, const CacheEntry &entry, const MemoryRange &memory_range) {
const BufferAttribute attr{};
return this->SetCacheEntry(index, entry, memory_range, attr);
}
void SetFlushing(CacheIndex index, bool en) {
if constexpr (requires { m_entries[index].is_flushing; }) {
m_entries[index].is_flushing = en;
}
}
void SetWriteBack(CacheIndex index, bool en) {
if constexpr (requires { m_entries[index].is_write_back; }) {
m_entries[index].is_write_back = en;
}
}
const CacheEntry &operator[](CacheIndex index) const {
/* Check pre-conditions. */
AMS_ASSERT(this->IsInitialized());
AMS_ASSERT(0 <= index && index < this->GetCount());
return m_entries[index];
}
};
}

View file

@ -21,6 +21,7 @@
#include <stratosphere/fs/fs_memory_management.hpp> #include <stratosphere/fs/fs_memory_management.hpp>
#include <stratosphere/fssystem/save/fssystem_i_save_file_system_driver.hpp> #include <stratosphere/fssystem/save/fssystem_i_save_file_system_driver.hpp>
#include <stratosphere/fssystem/buffers/fssystem_file_system_buffer_manager.hpp> #include <stratosphere/fssystem/buffers/fssystem_file_system_buffer_manager.hpp>
#include <stratosphere/fssystem/impl/fssystem_block_cache_manager.hpp>
namespace ams::fssystem::save { namespace ams::fssystem::save {
@ -30,7 +31,7 @@ namespace ams::fssystem::save {
constexpr inline size_t IntegrityLayerCountSaveDataMeta = 4; constexpr inline size_t IntegrityLayerCountSaveDataMeta = 4;
struct FileSystemBufferManagerSet { struct FileSystemBufferManagerSet {
IBufferManager *buffers[IntegrityMaxLayerCount]; fs::IBufferManager *buffers[IntegrityMaxLayerCount];
}; };
static_assert(util::is_pod<FileSystemBufferManagerSet>::value); static_assert(util::is_pod<FileSystemBufferManagerSet>::value);
@ -40,51 +41,77 @@ namespace ams::fssystem::save {
public: public:
static constexpr size_t DefaultMaxCacheEntryCount = 24; static constexpr size_t DefaultMaxCacheEntryCount = 24;
private: private:
using MemoryRange = std::pair<uintptr_t, size_t>; using MemoryRange = fs::IBufferManager::MemoryRange;
using CacheIndex = s32;
struct AccessRange {
s64 offset;
size_t size;
s64 GetEndOffset() const {
return this->offset + this->size;
}
bool IsIncluded(s64 ofs) const {
return this->offset <= ofs && ofs < this->GetEndOffset();
}
};
static_assert(util::is_pod<AccessRange>::value);
struct CacheEntry { struct CacheEntry {
size_t size; AccessRange range;
bool is_valid; bool is_valid;
bool is_write_back; bool is_write_back;
bool is_cached; bool is_cached;
bool is_flushing; bool is_flushing;
s64 offset; u16 lru_counter;
IBufferManager::CacheHandle handle; fs::IBufferManager::CacheHandle handle;
uintptr_t memory_address; uintptr_t memory_address;
size_t memory_size; size_t memory_size;
void Invalidate() {
this->is_write_back = false;
this->is_flushing = false;
}
bool IsAllocated() const {
return this->is_valid && (this->is_write_back ? this->memory_address != 0 : this->handle != 0);
}
bool IsWriteBack() const {
return this->is_write_back;
}
}; };
static_assert(util::is_pod<CacheEntry>::value); static_assert(util::is_pod<CacheEntry>::value);
using BlockCacheManager = ::ams::fssystem::impl::BlockCacheManager<CacheEntry, fs::IBufferManager>;
using CacheIndex = BlockCacheManager::CacheIndex;
enum Flag : s32 { enum Flag : s32 {
Flag_KeepBurstMode = (1 << 8), Flag_KeepBurstMode = (1 << 8),
Flag_RealData = (1 << 10), Flag_RealData = (1 << 10),
}; };
private: private:
IBufferManager *m_buffer_manager;
os::SdkRecursiveMutex *m_mutex; os::SdkRecursiveMutex *m_mutex;
std::unique_ptr<CacheEntry[], ::ams::fs::impl::Deleter> m_entries;
IStorage *m_data_storage; IStorage *m_data_storage;
Result m_last_result; Result m_last_result;
s64 m_data_size; s64 m_data_size;
size_t m_verification_block_size; size_t m_verification_block_size;
size_t m_verification_block_shift; size_t m_verification_block_shift;
CacheIndex m_invalidate_index;
s32 m_max_cache_entry_count;
s32 m_flags; s32 m_flags;
s32 m_buffer_level; s32 m_buffer_level;
fs::StorageType m_storage_type; fs::StorageType m_storage_type;
BlockCacheManager m_block_cache_manager;
public: public:
BlockCacheBufferedStorage(); BlockCacheBufferedStorage();
virtual ~BlockCacheBufferedStorage() override; virtual ~BlockCacheBufferedStorage() override;
Result Initialize(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, 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;
virtual Result Write(s64 offset, const 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::ResultUnsupportedOperationInBlockCacheBufferedStorageA(); } virtual Result SetSize(s64) override { R_THROW(fs::ResultUnsupportedOperationInBlockCacheBufferedStorageA()); }
virtual Result GetSize(s64 *out) override; virtual Result GetSize(s64 *out) override;
virtual Result Flush() override; virtual Result Flush() override;
@ -119,40 +146,24 @@ namespace ams::fssystem::save {
} }
} }
private: private:
s32 GetMaxCacheEntryCount() const { Result FillZeroImpl(s64 offset, s64 size);
return m_max_cache_entry_count; Result DestroySignatureImpl(s64 offset, s64 size);
} Result InvalidateImpl();
Result ClearImpl(s64 offset, s64 size);
Result ClearSignatureImpl(s64 offset, s64 size);
Result InvalidateCacheImpl(s64 offset, s64 size);
Result QueryRangeImpl(void *dst, size_t dst_size, s64 offset, s64 size); Result QueryRangeImpl(void *dst, size_t dst_size, s64 offset, s64 size);
bool ExistsRedundantCacheEntry(const CacheEntry &entry) const;
Result GetAssociateBuffer(MemoryRange *out_range, CacheEntry *out_entry, s64 offset, size_t ideal_size, bool is_allocate_for_write); Result GetAssociateBuffer(MemoryRange *out_range, CacheEntry *out_entry, s64 offset, size_t ideal_size, bool is_allocate_for_write);
void DestroyBuffer(CacheEntry *entry, const MemoryRange &range); Result StoreOrDestroyBuffer(CacheIndex *out, const MemoryRange &range, CacheEntry *entry);
Result StoreAssociateBuffer(CacheIndex *out, const MemoryRange &range, const CacheEntry &entry);
Result StoreAssociateBuffer(const MemoryRange &range, const CacheEntry &entry) {
CacheIndex dummy;
return this->StoreAssociateBuffer(std::addressof(dummy), range, entry);
}
Result StoreOrDestroyBuffer(const MemoryRange &range, CacheEntry *entry) { Result StoreOrDestroyBuffer(const MemoryRange &range, CacheEntry *entry) {
AMS_ASSERT(entry != nullptr); AMS_ASSERT(entry != nullptr);
ON_RESULT_FAILURE { this->DestroyBuffer(entry, range); }; CacheIndex dummy;
R_RETURN(this->StoreOrDestroyBuffer(std::addressof(dummy), range, entry));
R_TRY(this->StoreAssociateBuffer(range, *entry));
R_SUCCEED();
} }
Result FlushCacheEntry(CacheIndex index, bool invalidate); Result FlushCacheEntry(CacheIndex index, bool invalidate);
Result FlushRangeCacheEntries(s64 offset, s64 size, bool invalidate); Result FlushRangeCacheEntries(s64 offset, s64 size, bool invalidate);
void InvalidateRangeCacheEntries(s64 offset, s64 size);
Result FlushAllCacheEntries(); Result FlushAllCacheEntries();
Result InvalidateAllCacheEntries(); Result InvalidateAllCacheEntries();

View file

@ -18,7 +18,7 @@
#include <stratosphere/os.hpp> #include <stratosphere/os.hpp>
#include <stratosphere/fs/fs_istorage.hpp> #include <stratosphere/fs/fs_istorage.hpp>
#include <stratosphere/fs/fs_substorage.hpp> #include <stratosphere/fs/fs_substorage.hpp>
#include <stratosphere/fssystem/buffers/fssystem_i_buffer_manager.hpp> #include <stratosphere/fs/fs_i_buffer_manager.hpp>
namespace ams::fssystem::save { namespace ams::fssystem::save {
@ -31,7 +31,7 @@ namespace ams::fssystem::save {
class SharedCache; class SharedCache;
private: private:
fs::SubStorage m_base_storage; fs::SubStorage m_base_storage;
IBufferManager *m_buffer_manager; fs::IBufferManager *m_buffer_manager;
size_t m_block_size; size_t m_block_size;
s64 m_base_storage_size; s64 m_base_storage_size;
std::unique_ptr<Cache[]> m_caches; std::unique_ptr<Cache[]> m_caches;
@ -44,7 +44,7 @@ namespace ams::fssystem::save {
BufferedStorage(); BufferedStorage();
virtual ~BufferedStorage(); virtual ~BufferedStorage();
Result Initialize(fs::SubStorage base_storage, IBufferManager *buffer_manager, size_t block_size, s32 buffer_count); Result Initialize(fs::SubStorage base_storage, fs::IBufferManager *buffer_manager, size_t block_size, s32 buffer_count);
void Finalize(); void Finalize();
bool IsInitialized() const { return m_caches != nullptr; } bool IsInitialized() const { return m_caches != nullptr; }
@ -61,7 +61,7 @@ namespace ams::fssystem::save {
void InvalidateCaches(); void InvalidateCaches();
IBufferManager *GetBufferManager() const { return m_buffer_manager; } fs::IBufferManager *GetBufferManager() const { return m_buffer_manager; }
void EnableBulkRead() { m_bulk_read_enabled = true; } void EnableBulkRead() { m_bulk_read_enabled = true; }
private: private:

View file

@ -43,7 +43,7 @@ namespace ams::fssystem::save {
s64 m_verification_block_order; s64 m_verification_block_order;
s64 m_upper_layer_verification_block_size; s64 m_upper_layer_verification_block_size;
s64 m_upper_layer_verification_block_order; s64 m_upper_layer_verification_block_order;
IBufferManager *m_buffer_manager; fs::IBufferManager *m_buffer_manager;
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;
@ -52,7 +52,7 @@ namespace ams::fssystem::save {
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, 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 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;

View file

@ -239,10 +239,10 @@ namespace ams::fssystem {
return it != m_attr_list.end() ? std::addressof(*it) : nullptr; return it != m_attr_list.end() ? std::addressof(*it) : nullptr;
} }
const std::pair<uintptr_t, size_t> FileSystemBufferManager::AllocateBufferImpl(size_t size, const BufferAttribute &attr) { const fs::IBufferManager::MemoryRange FileSystemBufferManager::DoAllocateBuffer(size_t size, const BufferAttribute &attr) {
std::scoped_lock lk(m_mutex); std::scoped_lock lk(m_mutex);
std::pair<uintptr_t, size_t> range = {}; fs::IBufferManager::MemoryRange range = {};
const auto order = m_buddy_heap.GetOrderFromBytes(size); const auto order = m_buddy_heap.GetOrderFromBytes(size);
AMS_ASSERT(order >= 0); AMS_ASSERT(order >= 0);
@ -277,7 +277,7 @@ namespace ams::fssystem {
return range; return range;
} }
void FileSystemBufferManager::DeallocateBufferImpl(uintptr_t address, size_t size) { void FileSystemBufferManager::DoDeallocateBuffer(uintptr_t address, size_t size) {
AMS_ASSERT(util::IsPowerOfTwo(size)); AMS_ASSERT(util::IsPowerOfTwo(size));
std::scoped_lock lk(m_mutex); std::scoped_lock lk(m_mutex);
@ -285,7 +285,7 @@ namespace ams::fssystem {
m_buddy_heap.Free(reinterpret_cast<void *>(address), m_buddy_heap.GetOrderFromBytes(size)); m_buddy_heap.Free(reinterpret_cast<void *>(address), m_buddy_heap.GetOrderFromBytes(size));
} }
FileSystemBufferManager::CacheHandle FileSystemBufferManager::RegisterCacheImpl(uintptr_t address, size_t size, const BufferAttribute &attr) { FileSystemBufferManager::CacheHandle FileSystemBufferManager::DoRegisterCache(uintptr_t address, size_t size, const BufferAttribute &attr) {
std::scoped_lock lk(m_mutex); std::scoped_lock lk(m_mutex);
CacheHandle handle = 0; CacheHandle handle = 0;
@ -312,10 +312,10 @@ namespace ams::fssystem {
return handle; return handle;
} }
const std::pair<uintptr_t, size_t> FileSystemBufferManager::AcquireCacheImpl(CacheHandle handle) { const fs::IBufferManager::MemoryRange FileSystemBufferManager::DoAcquireCache(CacheHandle handle) {
std::scoped_lock lk(m_mutex); std::scoped_lock lk(m_mutex);
std::pair<uintptr_t, size_t> range = {}; fs::IBufferManager::MemoryRange range = {};
if (m_cache_handle_table.Unregister(std::addressof(range.first), std::addressof(range.second), handle)) { if (m_cache_handle_table.Unregister(std::addressof(range.first), std::addressof(range.second), handle)) {
const size_t total_allocatable_size = m_buddy_heap.GetTotalFreeSize() + m_cache_handle_table.GetTotalCacheSize(); const size_t total_allocatable_size = m_buddy_heap.GetTotalFreeSize() + m_cache_handle_table.GetTotalCacheSize();
m_peak_total_allocatable_size = std::min(m_peak_total_allocatable_size, total_allocatable_size); m_peak_total_allocatable_size = std::min(m_peak_total_allocatable_size, total_allocatable_size);
@ -327,33 +327,33 @@ namespace ams::fssystem {
return range; return range;
} }
size_t FileSystemBufferManager::GetTotalSizeImpl() const { size_t FileSystemBufferManager::DoGetTotalSize() const {
return m_total_size; return m_total_size;
} }
size_t FileSystemBufferManager::GetFreeSizeImpl() const { size_t FileSystemBufferManager::DoGetFreeSize() const {
std::scoped_lock lk(m_mutex); std::scoped_lock lk(m_mutex);
return m_buddy_heap.GetTotalFreeSize(); return m_buddy_heap.GetTotalFreeSize();
} }
size_t FileSystemBufferManager::GetTotalAllocatableSizeImpl() const { size_t FileSystemBufferManager::DoGetTotalAllocatableSize() const {
return this->GetFreeSize() + m_cache_handle_table.GetTotalCacheSize(); return this->GetFreeSize() + m_cache_handle_table.GetTotalCacheSize();
} }
size_t FileSystemBufferManager::GetPeakFreeSizeImpl() const { size_t FileSystemBufferManager::DoGetFreeSizePeak() const {
return m_peak_free_size; return m_peak_free_size;
} }
size_t FileSystemBufferManager::GetPeakTotalAllocatableSizeImpl() const { size_t FileSystemBufferManager::DoGetTotalAllocatableSizePeak() const {
return m_peak_total_allocatable_size; return m_peak_total_allocatable_size;
} }
size_t FileSystemBufferManager::GetRetriedCountImpl() const { size_t FileSystemBufferManager::DoGetRetriedCount() const {
return m_retried_count; return m_retried_count;
} }
void FileSystemBufferManager::ClearPeakImpl() { void FileSystemBufferManager::DoClearPeak() {
m_peak_free_size = this->GetFreeSize(); m_peak_free_size = this->GetFreeSize();
m_retried_count = 0; m_retried_count = 0;
} }

View file

@ -117,7 +117,11 @@ namespace ams::fssystem {
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
R_UNLESS(util::IsAligned(offset, BlockSize), fs::ResultInvalidOffset()); R_UNLESS(util::IsAligned(offset, BlockSize), fs::ResultInvalidOffset());
R_UNLESS(util::IsAligned(size, BlockSize), fs::ResultInvalidSize()); R_UNLESS(util::IsAligned(size, BlockSize), fs::ResultInvalidSize());
R_UNLESS(m_table.Includes(offset, size), fs::ResultOutOfRange());
BucketTree::Offsets table_offsets;
R_TRY(m_table.GetOffsets(std::addressof(table_offsets)));
R_UNLESS(table_offsets.IsInclude(offset, size), fs::ResultOutOfRange());
/* Read the data. */ /* Read the data. */
R_TRY(m_data_storage.Read(offset, buffer, size)); R_TRY(m_data_storage.Read(offset, buffer, size));
@ -131,7 +135,7 @@ namespace ams::fssystem {
{ {
const auto entry_offset = visitor.Get<Entry>()->GetOffset(); const auto entry_offset = visitor.Get<Entry>()->GetOffset();
R_UNLESS(util::IsAligned(entry_offset, BlockSize), fs::ResultInvalidAesCtrCounterExtendedEntryOffset()); R_UNLESS(util::IsAligned(entry_offset, BlockSize), fs::ResultInvalidAesCtrCounterExtendedEntryOffset());
R_UNLESS(0 <= entry_offset && m_table.Includes(entry_offset), fs::ResultInvalidAesCtrCounterExtendedEntryOffset()); R_UNLESS(0 <= entry_offset && table_offsets.IsInclude(entry_offset), fs::ResultInvalidAesCtrCounterExtendedEntryOffset());
} }
/* Prepare to read in chunks. */ /* Prepare to read in chunks. */
@ -152,9 +156,9 @@ namespace ams::fssystem {
if (visitor.CanMoveNext()) { if (visitor.CanMoveNext()) {
R_TRY(visitor.MoveNext()); R_TRY(visitor.MoveNext());
next_entry_offset = visitor.Get<Entry>()->GetOffset(); next_entry_offset = visitor.Get<Entry>()->GetOffset();
R_UNLESS(m_table.Includes(next_entry_offset), fs::ResultInvalidAesCtrCounterExtendedEntryOffset()); R_UNLESS(table_offsets.IsInclude(next_entry_offset), fs::ResultInvalidAesCtrCounterExtendedEntryOffset());
} else { } else {
next_entry_offset = m_table.GetEnd(); next_entry_offset = table_offsets.end_offset;
} }
R_UNLESS(util::IsAligned(next_entry_offset, BlockSize), fs::ResultInvalidAesCtrCounterExtendedEntryOffset()); R_UNLESS(util::IsAligned(next_entry_offset, BlockSize), fs::ResultInvalidAesCtrCounterExtendedEntryOffset());
R_UNLESS(cur_offset < next_entry_offset, fs::ResultInvalidAesCtrCounterExtendedEntryOffset()); R_UNLESS(cur_offset < next_entry_offset, fs::ResultInvalidAesCtrCounterExtendedEntryOffset());
@ -192,22 +196,13 @@ namespace ams::fssystem {
case fs::OperationId::Invalidate: case fs::OperationId::Invalidate:
{ {
/* Validate preconditions. */ /* Validate preconditions. */
AMS_ASSERT(offset >= 0);
AMS_ASSERT(this->IsInitialized()); AMS_ASSERT(this->IsInitialized());
/* Succeed if there's nothing to operate on. */
R_SUCCEED_IF(size == 0);
/* Validate arguments. */
R_UNLESS(util::IsAligned(offset, BlockSize), fs::ResultInvalidOffset());
R_UNLESS(util::IsAligned(size, BlockSize), fs::ResultInvalidSize());
R_UNLESS(m_table.Includes(offset, size), fs::ResultOutOfRange());
/* Invalidate our table's cache. */ /* Invalidate our table's cache. */
R_TRY(m_table.InvalidateCache()); R_TRY(m_table.InvalidateCache());
/* Operate on our data storage. */ /* Operate on our data storage. */
R_TRY(m_data_storage.OperateRange(dst, dst_size, op_id, offset, size, src, src_size)); R_TRY(m_data_storage.OperateRange(fs::OperationId::Invalidate, 0, std::numeric_limits<s64>::max()));
return ResultSuccess(); return ResultSuccess();
} }
@ -230,7 +225,11 @@ namespace ams::fssystem {
/* Validate arguments. */ /* Validate arguments. */
R_UNLESS(util::IsAligned(offset, BlockSize), fs::ResultInvalidOffset()); R_UNLESS(util::IsAligned(offset, BlockSize), fs::ResultInvalidOffset());
R_UNLESS(util::IsAligned(size, BlockSize), fs::ResultInvalidSize()); R_UNLESS(util::IsAligned(size, BlockSize), fs::ResultInvalidSize());
R_UNLESS(m_table.Includes(offset, size), fs::ResultOutOfRange());
BucketTree::Offsets table_offsets;
R_TRY(m_table.GetOffsets(std::addressof(table_offsets)));
R_UNLESS(table_offsets.IsInclude(offset, size), fs::ResultOutOfRange());
/* Operate on our data storage. */ /* Operate on our data storage. */
R_TRY(m_data_storage.OperateRange(dst, dst_size, op_id, offset, size, src, src_size)); R_TRY(m_data_storage.OperateRange(dst, dst_size, op_id, offset, size, src, src_size));

View file

@ -249,9 +249,10 @@ namespace ams::fssystem {
/* Handle any data after the aligned portion. */ /* Handle any data after the aligned portion. */
if (core_offset_end < offset_end) { if (core_offset_end < offset_end) {
const auto tail_buffer = static_cast<char *>(buffer) + (core_offset_end - offset);
const auto tail_size = static_cast<size_t>(offset_end - core_offset_end); const auto tail_size = static_cast<size_t>(offset_end - core_offset_end);
R_TRY(m_base_storage->Read(core_offset_end, pooled_buffer.GetBuffer(), m_data_align)); R_TRY(m_base_storage->Read(core_offset_end, pooled_buffer.GetBuffer(), m_data_align));
std::memcpy(buffer, pooled_buffer.GetBuffer(), tail_size); std::memcpy(tail_buffer, pooled_buffer.GetBuffer(), tail_size);
} }
return ResultSuccess(); return ResultSuccess();

View file

@ -154,7 +154,7 @@ namespace ams::fssystem {
/* Allocate node. */ /* Allocate node. */
R_UNLESS(m_node_l1.Allocate(allocator, node_size), fs::ResultBufferAllocationFailed()); R_UNLESS(m_node_l1.Allocate(allocator, node_size), fs::ResultBufferAllocationFailed());
auto node_guard = SCOPE_GUARD { m_node_l1.Free(node_size); }; ON_RESULT_FAILURE { m_node_l1.Free(node_size); };
/* Read node. */ /* Read node. */
R_TRY(node_storage.Read(0, m_node_l1.Get(), node_size)); R_TRY(node_storage.Read(0, m_node_l1.Get(), node_size));
@ -186,12 +186,13 @@ namespace ams::fssystem {
m_entry_count = entry_count; m_entry_count = entry_count;
m_offset_count = offset_count; m_offset_count = offset_count;
m_entry_set_count = entry_set_count; m_entry_set_count = entry_set_count;
m_start_offset = start_offset;
m_end_offset = end_offset; m_offset_cache.offsets.start_offset = start_offset;
m_offset_cache.offsets.end_offset = end_offset;
m_offset_cache.is_initialized = true;
/* Cancel guard. */ /* Cancel guard. */
node_guard.Cancel(); R_SUCCEED();
return ResultSuccess();
} }
void BucketTree::Initialize(size_t node_size, s64 end_offset) { void BucketTree::Initialize(size_t node_size, s64 end_offset) {
@ -201,7 +202,10 @@ namespace ams::fssystem {
AMS_ASSERT(!this->IsInitialized()); AMS_ASSERT(!this->IsInitialized());
m_node_size = node_size; m_node_size = node_size;
m_end_offset = end_offset;
m_offset_cache.offsets.start_offset = 0;
m_offset_cache.offsets.end_offset = end_offset;
m_offset_cache.is_initialized = true;
} }
void BucketTree::Finalize() { void BucketTree::Finalize() {
@ -214,41 +218,57 @@ namespace ams::fssystem {
m_entry_count = 0; m_entry_count = 0;
m_offset_count = 0; m_offset_count = 0;
m_entry_set_count = 0; m_entry_set_count = 0;
m_start_offset = 0;
m_end_offset = 0; m_offset_cache.offsets.start_offset = 0;
m_offset_cache.offsets.end_offset = 0;
m_offset_cache.is_initialized = false;
} }
} }
Result BucketTree::Find(Visitor *visitor, s64 virtual_address) const { Result BucketTree::Find(Visitor *visitor, s64 virtual_address) {
AMS_ASSERT(visitor != nullptr); AMS_ASSERT(visitor != nullptr);
AMS_ASSERT(this->IsInitialized()); AMS_ASSERT(this->IsInitialized());
R_UNLESS(virtual_address >= 0, fs::ResultInvalidOffset()); R_UNLESS(virtual_address >= 0, fs::ResultInvalidOffset());
R_UNLESS(!this->IsEmpty(), fs::ResultOutOfRange()); R_UNLESS(!this->IsEmpty(), fs::ResultOutOfRange());
R_TRY(visitor->Initialize(this)); BucketTree::Offsets offsets;
R_TRY(this->GetOffsets(std::addressof(offsets)));
R_TRY(visitor->Initialize(this, offsets));
return visitor->Find(virtual_address); return visitor->Find(virtual_address);
} }
Result BucketTree::InvalidateCache() { Result BucketTree::InvalidateCache() {
/* Invalidate the node storage cache. */ /* Invalidate the node storage cache. */
{ R_TRY(m_node_storage.OperateRange(fs::OperationId::Invalidate, 0, std::numeric_limits<s64>::max()));
s64 storage_size;
R_TRY(m_node_storage.GetSize(std::addressof(storage_size))); /* Invalidate the entry storage cache. */
R_TRY(m_node_storage.OperateRange(fs::OperationId::Invalidate, 0, storage_size)); R_TRY(m_entry_storage.OperateRange(fs::OperationId::Invalidate, 0, std::numeric_limits<s64>::max()));
/* Reset our offsets. */
m_offset_cache.is_initialized = false;
R_SUCCEED();
} }
/* Refresh start/end offsets. */ Result BucketTree::EnsureOffsetCache() {
{ /* If we already have an offset cache, we're good. */
/* Read node. */ R_SUCCEED_IF(m_offset_cache.is_initialized);
R_TRY(m_node_storage.Read(0, m_node_l1.Get(), m_node_size));
/* Verify node. */ /* Acquire exclusive right to edit the offset cache. */
std::scoped_lock lk(m_offset_cache.mutex);
/* Check again, to be sure. */
R_SUCCEED_IF(m_offset_cache.is_initialized);
/* Read/verify L1. */
R_TRY(m_node_storage.Read(0, m_node_l1.Get(), m_node_size));
R_TRY(m_node_l1->Verify(0, m_node_size, sizeof(s64))); R_TRY(m_node_l1->Verify(0, m_node_size, sizeof(s64)));
/* Validate offsets. */ /* Get the node. */
const auto * const node = m_node_l1.Get<Node>(); auto * const node = m_node_l1.Get<Node>();
s64 start_offset; s64 start_offset;
if (m_offset_count < m_entry_set_count && node->GetCount() < m_offset_count) { if (m_offset_count < m_entry_set_count && node->GetCount() < m_offset_count) {
@ -261,22 +281,14 @@ namespace ams::fssystem {
R_UNLESS(0 <= start_offset && start_offset <= node->GetBeginOffset(), fs::ResultInvalidBucketTreeEntryOffset()); R_UNLESS(0 <= start_offset && start_offset <= node->GetBeginOffset(), fs::ResultInvalidBucketTreeEntryOffset());
R_UNLESS(start_offset < end_offset, fs::ResultInvalidBucketTreeEntryOffset()); R_UNLESS(start_offset < end_offset, fs::ResultInvalidBucketTreeEntryOffset());
/* Set refreshed offsets. */ m_offset_cache.offsets.start_offset = start_offset;
m_start_offset = start_offset; m_offset_cache.offsets.end_offset = end_offset;
m_end_offset = end_offset; m_offset_cache.is_initialized = true;
R_SUCCEED();
} }
/* Invalidate the entry storage cache. */ Result BucketTree::Visitor::Initialize(const BucketTree *tree, const BucketTree::Offsets &offsets) {
{
s64 storage_size;
R_TRY(m_entry_storage.GetSize(std::addressof(storage_size)));
R_TRY(m_entry_storage.OperateRange(fs::OperationId::Invalidate, 0, storage_size));
}
return ResultSuccess();
}
Result BucketTree::Visitor::Initialize(const BucketTree *tree) {
AMS_ASSERT(tree != nullptr); AMS_ASSERT(tree != nullptr);
AMS_ASSERT(m_tree == nullptr || m_tree == tree); AMS_ASSERT(m_tree == nullptr || m_tree == tree);
@ -285,6 +297,7 @@ namespace ams::fssystem {
R_UNLESS(m_entry != nullptr, fs::ResultBufferAllocationFailed()); R_UNLESS(m_entry != nullptr, fs::ResultBufferAllocationFailed());
m_tree = tree; m_tree = tree;
m_offsets = offsets;
} }
return ResultSuccess(); return ResultSuccess();
@ -319,7 +332,7 @@ namespace ams::fssystem {
/* Read the new entry. */ /* Read the new entry. */
const auto entry_size = m_tree->m_entry_size; const auto entry_size = m_tree->m_entry_size;
const auto entry_offset = impl::GetBucketTreeEntryOffset(m_entry_set.info.index, m_tree->m_node_size, entry_size, entry_index); const auto entry_offset = impl::GetBucketTreeEntryOffset(m_entry_set.info.index, m_tree->m_node_size, entry_size, entry_index);
R_TRY(m_tree->m_entry_storage.Read(entry_offset, std::addressof(m_entry), entry_size)); R_TRY(m_tree->m_entry_storage.Read(entry_offset, m_entry, entry_size));
/* Note that we changed index. */ /* Note that we changed index. */
m_entry_index = entry_index; m_entry_index = entry_index;
@ -357,7 +370,7 @@ namespace ams::fssystem {
/* Read the new entry. */ /* Read the new entry. */
const auto entry_size = m_tree->m_entry_size; const auto entry_size = m_tree->m_entry_size;
const auto entry_offset = impl::GetBucketTreeEntryOffset(m_entry_set.info.index, m_tree->m_node_size, entry_size, entry_index); const auto entry_offset = impl::GetBucketTreeEntryOffset(m_entry_set.info.index, m_tree->m_node_size, entry_size, entry_index);
R_TRY(m_tree->m_entry_storage.Read(entry_offset, std::addressof(m_entry), entry_size)); R_TRY(m_tree->m_entry_storage.Read(entry_offset, m_entry, entry_size));
/* Note that we changed index. */ /* Note that we changed index. */
m_entry_index = entry_index; m_entry_index = entry_index;

View file

@ -59,14 +59,17 @@ namespace ams::fssystem {
R_UNLESS(out_entries != nullptr || entry_count == 0, fs::ResultNullptrArgument()); R_UNLESS(out_entries != nullptr || entry_count == 0, fs::ResultNullptrArgument());
/* Check that our range is valid. */ /* Check that our range is valid. */
R_UNLESS(m_table.Includes(offset, size), fs::ResultOutOfRange()); BucketTree::Offsets table_offsets;
R_TRY(m_table.GetOffsets(std::addressof(table_offsets)));
R_UNLESS(table_offsets.IsInclude(offset, size), fs::ResultOutOfRange());
/* Find the offset in our tree. */ /* Find the offset in our tree. */
BucketTree::Visitor visitor; BucketTree::Visitor visitor;
R_TRY(m_table.Find(std::addressof(visitor), offset)); R_TRY(m_table.Find(std::addressof(visitor), offset));
{ {
const auto entry_offset = visitor.Get<Entry>()->GetVirtualOffset(); const auto entry_offset = visitor.Get<Entry>()->GetVirtualOffset();
R_UNLESS(0 <= entry_offset && m_table.Includes(entry_offset), fs::ResultInvalidIndirectEntryOffset()); R_UNLESS(0 <= entry_offset && table_offsets.IsInclude(entry_offset), fs::ResultInvalidIndirectEntryOffset());
} }
/* Prepare to loop over entries. */ /* Prepare to loop over entries. */
@ -96,7 +99,7 @@ namespace ams::fssystem {
/* Write the output count. */ /* Write the output count. */
*out_entry_count = count; *out_entry_count = count;
return ResultSuccess(); R_SUCCEED();
} }
Result IndirectStorage::Read(s64 offset, void *buffer, size_t size) { Result IndirectStorage::Read(s64 offset, void *buffer, size_t size) {
@ -110,35 +113,28 @@ namespace ams::fssystem {
/* Ensure that we have a buffer to read to. */ /* Ensure that we have a buffer to read to. */
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
R_TRY(this->OperatePerEntry<true>(offset, size, [=](fs::IStorage *storage, s64 data_offset, s64 cur_offset, s64 cur_size) -> Result { R_TRY((this->OperatePerEntry<true, true>(offset, size, [=](fs::IStorage *storage, s64 data_offset, s64 cur_offset, s64 cur_size) -> Result {
R_TRY(storage->Read(data_offset, reinterpret_cast<u8 *>(buffer) + (cur_offset - offset), static_cast<size_t>(cur_size))); R_TRY(storage->Read(data_offset, reinterpret_cast<u8 *>(buffer) + (cur_offset - offset), static_cast<size_t>(cur_size)));
return ResultSuccess(); R_SUCCEED();
})); })));
return ResultSuccess(); R_SUCCEED();
} }
Result IndirectStorage::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) { Result IndirectStorage::OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
switch (op_id) { switch (op_id) {
case fs::OperationId::Invalidate: case fs::OperationId::Invalidate:
{ {
if (size > 0) {
/* Validate arguments. */
R_UNLESS(m_table.Includes(offset, size), fs::ResultOutOfRange());
if (!m_table.IsEmpty()) { if (!m_table.IsEmpty()) {
/* Invalidate our table's cache. */ /* Invalidate our table's cache. */
R_TRY(m_table.InvalidateCache()); R_TRY(m_table.InvalidateCache());
/* Operate on our entries. */ /* Invalidate our storages. */
R_TRY(this->OperatePerEntry<false>(offset, size, [=](fs::IStorage *storage, s64 data_offset, s64 cur_offset, s64 cur_size) -> Result { for (auto &storage : m_data_storage) {
AMS_UNUSED(cur_offset); R_TRY(storage.OperateRange(fs::OperationId::Invalidate, 0, std::numeric_limits<s64>::max()));
R_TRY(storage->OperateRange(dst, dst_size, op_id, data_offset, cur_size, src, src_size));
return ResultSuccess();
}));
} }
return ResultSuccess();
} }
return ResultSuccess(); R_SUCCEED();
} }
case fs::OperationId::QueryRange: case fs::OperationId::QueryRange:
{ {
@ -148,33 +144,37 @@ namespace ams::fssystem {
if (size > 0) { if (size > 0) {
/* Validate arguments. */ /* Validate arguments. */
R_UNLESS(m_table.Includes(offset, size), fs::ResultOutOfRange()); BucketTree::Offsets table_offsets;
R_TRY(m_table.GetOffsets(std::addressof(table_offsets)));
R_UNLESS(table_offsets.IsInclude(offset, size), fs::ResultOutOfRange());
if (!m_table.IsEmpty()) { if (!m_table.IsEmpty()) {
/* Create a new info. */ /* Create a new info. */
fs::QueryRangeInfo merged_info; fs::QueryRangeInfo merged_info;
merged_info.Clear(); merged_info.Clear();
/* Operate on our entries. */ /* Operate on our entries. */
R_TRY(this->OperatePerEntry<false>(offset, size, [=, &merged_info](fs::IStorage *storage, s64 data_offset, s64 cur_offset, s64 cur_size) -> Result { R_TRY((this->OperatePerEntry<false, true>(offset, size, [=, &merged_info](fs::IStorage *storage, s64 data_offset, s64 cur_offset, s64 cur_size) -> Result {
AMS_UNUSED(cur_offset); AMS_UNUSED(cur_offset);
fs::QueryRangeInfo cur_info; fs::QueryRangeInfo cur_info;
R_TRY(storage->OperateRange(std::addressof(cur_info), sizeof(cur_info), op_id, data_offset, cur_size, src, src_size)); R_TRY(storage->OperateRange(std::addressof(cur_info), sizeof(cur_info), op_id, data_offset, cur_size, src, src_size));
merged_info.Merge(cur_info); merged_info.Merge(cur_info);
return ResultSuccess(); R_SUCCEED();
})); })));
/* Write the merged info. */ /* Write the merged info. */
*reinterpret_cast<fs::QueryRangeInfo *>(dst) = merged_info; *reinterpret_cast<fs::QueryRangeInfo *>(dst) = merged_info;
} }
} }
return ResultSuccess(); R_SUCCEED();
} }
default: default:
return fs::ResultUnsupportedOperationInIndirectStorageC(); return fs::ResultUnsupportedOperationInIndirectStorageC();
} }
return ResultSuccess(); R_SUCCEED();
} }
} }

View file

@ -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, IHash256GeneratorFactory *hgf) { Result IntegrityRomFsStorage::Initialize(save::HierarchicalIntegrityVerificationInformation level_hash_info, Hash master_hash, save::HierarchicalIntegrityVerificationStorage::HierarchicalStorageInformation storage_info, fs::IBufferManager *bm, IHash256GeneratorFactory *hgf) {
/* Validate preconditions. */ /* Validate preconditions. */
AMS_ASSERT(bm != nullptr); AMS_ASSERT(bm != nullptr);

View file

@ -1105,7 +1105,7 @@ namespace ams::fssystem {
return this->CreateCompressedStorage(out, out_cmp, out_meta, std::move(base_storage), compression_info, m_reader->GetDecompressor(), m_allocator, m_buffer_manager); return this->CreateCompressedStorage(out, out_cmp, out_meta, std::move(base_storage), compression_info, m_reader->GetDecompressor(), m_allocator, m_buffer_manager);
} }
Result NcaFileSystemDriver::CreateCompressedStorage(std::shared_ptr<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) { Result NcaFileSystemDriver::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, fs::IBufferManager *buffer_manager) {
/* Check pre-conditions. */ /* Check pre-conditions. */
AMS_ASSERT(out != nullptr); AMS_ASSERT(out != nullptr);
AMS_ASSERT(base_storage != nullptr); AMS_ASSERT(base_storage != nullptr);

View file

@ -29,13 +29,17 @@ namespace ams::fssystem {
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument()); R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
if (this->GetEntryTable().IsEmpty()) { if (this->GetEntryTable().IsEmpty()) {
R_UNLESS(this->GetEntryTable().Includes(offset, size), fs::ResultOutOfRange()); BucketTree::Offsets table_offsets;
R_TRY(this->GetEntryTable().GetOffsets(std::addressof(table_offsets)));
R_UNLESS(table_offsets.IsInclude(offset, size), fs::ResultOutOfRange());
std::memset(buffer, 0, size); std::memset(buffer, 0, size);
} else { } else {
R_TRY(this->OperatePerEntry<false>(offset, size, [=](fs::IStorage *storage, s64 data_offset, s64 cur_offset, s64 cur_size) -> Result { R_TRY((this->OperatePerEntry<false, true>(offset, size, [=](fs::IStorage *storage, s64 data_offset, s64 cur_offset, s64 cur_size) -> Result {
R_TRY(storage->Read(data_offset, reinterpret_cast<u8 *>(buffer) + (cur_offset - offset), static_cast<size_t>(cur_size))); R_TRY(storage->Read(data_offset, reinterpret_cast<u8 *>(buffer) + (cur_offset - offset), static_cast<size_t>(cur_size)));
return ResultSuccess(); return ResultSuccess();
})); })));
} }
return ResultSuccess(); return ResultSuccess();

View file

@ -35,7 +35,7 @@ namespace ams::fssystem::save {
private: private:
BufferedStorage *m_buffered_storage; BufferedStorage *m_buffered_storage;
std::pair<uintptr_t, size_t> m_memory_range; std::pair<uintptr_t, size_t> m_memory_range;
IBufferManager::CacheHandle m_cache_handle; fs::IBufferManager::CacheHandle m_cache_handle;
s64 m_offset; s64 m_offset;
std::atomic<bool> m_is_valid; std::atomic<bool> m_is_valid;
std::atomic<bool> m_is_dirty; std::atomic<bool> m_is_dirty;
@ -139,7 +139,7 @@ namespace ams::fssystem::save {
/* Ensure our buffer state is coherent. */ /* Ensure our buffer state is coherent. */
if (m_memory_range.first != InvalidAddress && !m_is_dirty) { if (m_memory_range.first != InvalidAddress && !m_is_dirty) {
if (this->IsValid()) { if (this->IsValid()) {
m_cache_handle = m_buffered_storage->m_buffer_manager->RegisterCache(m_memory_range.first, m_memory_range.second, IBufferManager::BufferAttribute()); m_cache_handle = m_buffered_storage->m_buffer_manager->RegisterCache(m_memory_range.first, m_memory_range.second, fs::IBufferManager::BufferAttribute());
} else { } else {
m_buffered_storage->m_buffer_manager->DeallocateBuffer(m_memory_range.first, m_memory_range.second); m_buffered_storage->m_buffer_manager->DeallocateBuffer(m_memory_range.first, m_memory_range.second);
} }
@ -360,11 +360,11 @@ namespace ams::fssystem::save {
} }
private: private:
Result AllocateFetchBuffer() { Result AllocateFetchBuffer() {
IBufferManager *buffer_manager = m_buffered_storage->m_buffer_manager; fs::IBufferManager *buffer_manager = m_buffered_storage->m_buffer_manager;
AMS_ASSERT(buffer_manager->AcquireCache(m_cache_handle).first == InvalidAddress); AMS_ASSERT(buffer_manager->AcquireCache(m_cache_handle).first == InvalidAddress);
auto range_guard = SCOPE_GUARD { m_memory_range.first = InvalidAddress; }; auto range_guard = SCOPE_GUARD { m_memory_range.first = InvalidAddress; };
R_TRY(buffers::AllocateBufferUsingBufferManagerContext(std::addressof(m_memory_range), buffer_manager, m_buffered_storage->m_block_size, IBufferManager::BufferAttribute(), [](const std::pair<uintptr_t, size_t> &buffer) { R_TRY(buffers::AllocateBufferUsingBufferManagerContext(std::addressof(m_memory_range), buffer_manager, m_buffered_storage->m_block_size, fs::IBufferManager::BufferAttribute(), [](const std::pair<uintptr_t, size_t> &buffer) {
return buffer.first != 0; return buffer.first != 0;
}, AMS_CURRENT_FUNCTION_NAME)); }, AMS_CURRENT_FUNCTION_NAME));
@ -591,7 +591,7 @@ namespace ams::fssystem::save {
this->Finalize(); this->Finalize();
} }
Result BufferedStorage::Initialize(fs::SubStorage base_storage, IBufferManager *buffer_manager, size_t block_size, s32 buffer_count) { Result BufferedStorage::Initialize(fs::SubStorage base_storage, fs::IBufferManager *buffer_manager, size_t block_size, s32 buffer_count) {
AMS_ASSERT(buffer_manager != nullptr); AMS_ASSERT(buffer_manager != nullptr);
AMS_ASSERT(block_size > 0); AMS_ASSERT(block_size > 0);
AMS_ASSERT(util::IsPowerOfTwo(block_size)); AMS_ASSERT(util::IsPowerOfTwo(block_size));

View file

@ -17,7 +17,7 @@
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, fssystem::IHash256GeneratorFactory *hgf, 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, fs::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);

View file

@ -307,6 +307,7 @@ namespace ams::fs {
R_DEFINE_ERROR_RESULT(UnexpectedInCompressedStorageA, 5324); R_DEFINE_ERROR_RESULT(UnexpectedInCompressedStorageA, 5324);
R_DEFINE_ERROR_RESULT(UnexpectedInCompressedStorageB, 5325); R_DEFINE_ERROR_RESULT(UnexpectedInCompressedStorageB, 5325);
R_DEFINE_ERROR_RESULT(UnexpectedInCompressedStorageC, 5326); R_DEFINE_ERROR_RESULT(UnexpectedInCompressedStorageC, 5326);
R_DEFINE_ERROR_RESULT(UnexpectedInCompressedStorageD, 5327);
R_DEFINE_ERROR_RESULT(UnexpectedInPathA, 5328); R_DEFINE_ERROR_RESULT(UnexpectedInPathA, 5328);
R_DEFINE_ERROR_RANGE(PreconditionViolation, 6000, 6499); R_DEFINE_ERROR_RANGE(PreconditionViolation, 6000, 6499);
@ -394,6 +395,7 @@ namespace ams::fs {
R_DEFINE_ERROR_RESULT(UnsupportedOperationInLocalFileA, 6378); R_DEFINE_ERROR_RESULT(UnsupportedOperationInLocalFileA, 6378);
R_DEFINE_ERROR_RESULT(UnsupportedOperationInDirectorySaveDataFileSystemA, 6384); R_DEFINE_ERROR_RESULT(UnsupportedOperationInDirectorySaveDataFileSystemA, 6384);
R_DEFINE_ERROR_RESULT(UnsupportedOperationInCompressedStorageA, 6387); R_DEFINE_ERROR_RESULT(UnsupportedOperationInCompressedStorageA, 6387);
R_DEFINE_ERROR_RESULT(UnsupportedOperationInCompressedStorageB, 6388);
R_DEFINE_ERROR_RANGE(PermissionDenied, 6400, 6449); R_DEFINE_ERROR_RANGE(PermissionDenied, 6400, 6449);
R_DEFINE_ERROR_RESULT(PermissionDeniedForCreateHostFileSystem, 6403); R_DEFINE_ERROR_RESULT(PermissionDeniedForCreateHostFileSystem, 6403);

View file

@ -57,6 +57,8 @@
#include <vapours/util/util_function_local_static.hpp> #include <vapours/util/util_function_local_static.hpp>
#include <vapours/util/util_i_function.hpp>
#ifdef ATMOSPHERE_IS_STRATOSPHERE #ifdef ATMOSPHERE_IS_STRATOSPHERE
#include <vapours/util/util_mutex_utils.hpp> #include <vapours/util/util_mutex_utils.hpp>
#endif #endif

View file

@ -0,0 +1,110 @@
/*
* 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/common.hpp>
#include <vapours/assert.hpp>
namespace ams::util {
template<typename T>
class IFunction;
namespace impl {
template<typename>
struct GetIFunctionTypeForObject;
template<typename F, typename R, typename... Args>
struct GetIFunctionTypeForObject<R (F::*)(Args...)> { using Type = R(Args...); };
template<typename F, typename R, typename... Args>
struct GetIFunctionTypeForObject<R (F::*)(Args...) const> { using Type = R(Args...); };
template<typename>
struct GetIFunctionType;
template<typename R, typename... Args>
struct GetIFunctionType<R(Args...)> { using Type = R(Args...); };
template<typename R, typename... Args>
struct GetIFunctionType<R(*)(Args...)> : public GetIFunctionType<R(Args...)>{};
template<typename F>
struct GetIFunctionType<std::reference_wrapper<F>> : public GetIFunctionType<F>{};
template<typename F>
struct GetIFunctionType : public GetIFunctionTypeForObject<decltype(&F::operator())>{};
template<typename T, typename F, typename Enabled = void>
class Function;
template<typename R, typename... Args, typename F>
class Function<R(Args...), F, typename std::enable_if<!(std::is_class<F>::value && !std::is_final<F>::value)>::type> final : public IFunction<R(Args...)> {
private:
F m_f;
public:
constexpr explicit Function(F f) : m_f(std::move(f)) { /* ... */}
constexpr virtual R operator()(Args... args) const override final {
return m_f(std::forward<Args>(args)...);
}
};
template<typename R, typename... Args, typename F>
class Function<R(Args...), F, typename std::enable_if<std::is_class<F>::value && !std::is_final<F>::value>::type> final : public IFunction<R(Args...)>, private F {
public:
constexpr explicit Function(F f) : F(std::move(f)) { /* ... */}
constexpr virtual R operator()(Args... args) const override final {
return static_cast<const F &>(*this).operator()(std::forward<Args>(args)...);
}
};
template<typename I, typename F>
constexpr ALWAYS_INLINE auto MakeIFunctionExplicitly(F f) {
using FunctionType = ::ams::util::impl::Function<I, typename std::decay<F>::type>;
return FunctionType{ std::move(f) };
}
template<typename I, typename T, typename R>
constexpr ALWAYS_INLINE auto MakeIFunctionExplicitly(R T::*f) {
return MakeIFunctionExplicitly<I>(std::mem_fn(f));
}
}
template<typename R, typename... Args>
class IFunction<R(Args...)> {
protected:
constexpr virtual ~IFunction() = default;
public:
constexpr virtual R operator()(Args... args) const = 0;
template<typename F>
static constexpr ALWAYS_INLINE auto Make(F f) {
return ::ams::util::impl::MakeIFunctionExplicitly<R(Args...)>(std::move(f));
}
};
template<typename F, typename = std::enable_if<!std::is_member_pointer<F>::value>::type>
constexpr ALWAYS_INLINE auto MakeIFunction(F f) {
static_assert(!std::is_member_pointer<F>::value);
return IFunction<typename ::ams::util::impl::GetIFunctionType<typename std::decay<F>::type>::Type>::Make(std::move(f));
}
}