fs: first pass at compressed storage (works on iridium with wip hac2l code)
This commit is contained in:
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
#pragma once
|
||||
#include <vapours.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>
|
||||
|
||||
namespace ams::fssystem {
|
||||
@@ -35,10 +35,10 @@ namespace ams::fssrv::fscreator {
|
||||
MemoryResource *m_allocator;
|
||||
const fssystem::NcaCryptoConfiguration &m_nca_crypto_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;
|
||||
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)
|
||||
{
|
||||
/* ... */
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/os.hpp>
|
||||
#include <stratosphere/fs/fs_i_buffer_manager.hpp>
|
||||
|
||||
namespace ams::fssystem::buffers {
|
||||
|
||||
@@ -88,13 +89,13 @@ namespace ams::fssystem::buffers {
|
||||
};
|
||||
|
||||
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(buffer_manager != nullptr);
|
||||
AMS_ASSERT(func_name != nullptr);
|
||||
|
||||
/* Clear the output. */
|
||||
*out = std::pair<uintptr_t, size_t>(0, 0);
|
||||
*out = fs::IBufferManager::MakeMemoryRange(0, 0);
|
||||
|
||||
/* Get the context. */
|
||||
auto context = GetBufferManagerContext();
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/fs/impl/fs_newable.hpp>
|
||||
#include <stratosphere/fs/fs_i_buffer_manager.hpp>
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/lmem.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>
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
class FileSystemBufferManager : public IBufferManager {
|
||||
class FileSystemBufferManager : public fs::IBufferManager {
|
||||
NON_COPYABLE(FileSystemBufferManager);
|
||||
NON_MOVEABLE(FileSystemBufferManager);
|
||||
public:
|
||||
@@ -194,7 +194,7 @@ namespace ams::fssystem {
|
||||
size_t m_peak_free_size;
|
||||
size_t m_peak_total_allocatable_size;
|
||||
size_t m_retried_count;
|
||||
mutable os::SdkRecursiveMutex m_mutex;
|
||||
mutable os::SdkMutex m_mutex;
|
||||
public:
|
||||
static constexpr size_t QueryWorkBufferSize(s32 max_cache_count, s32 max_order) {
|
||||
const auto buddy_size = FileSystemBuddyHeap::QueryWorkBufferSize(max_order);
|
||||
@@ -269,27 +269,27 @@ namespace ams::fssystem {
|
||||
m_cache_handle_table.Finalize();
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -94,22 +94,27 @@ namespace ams::fssystem {
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
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 {
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
AMS_UNUSED(offset, buffer, size);
|
||||
return fs::ResultUnsupportedOperationInAesCtrCounterExtendedStorageA();
|
||||
R_THROW(fs::ResultUnsupportedOperationInAesCtrCounterExtendedStorageA());
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
AMS_UNUSED(size);
|
||||
return fs::ResultUnsupportedOperationInAesCtrCounterExtendedStorageB();
|
||||
R_THROW(fs::ResultUnsupportedOperationInAesCtrCounterExtendedStorageB());
|
||||
}
|
||||
private:
|
||||
Result Initialize(IAllocator *allocator, const void *key, size_t key_size, u32 secure_value, fs::SubStorage data_storage, fs::SubStorage table_storage);
|
||||
|
||||
@@ -53,6 +53,29 @@ namespace ams::fssystem {
|
||||
static_assert(util::is_pod<NodeHeader>::value);
|
||||
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 {
|
||||
private:
|
||||
size_t m_read_size;
|
||||
@@ -213,10 +236,9 @@ namespace ams::fssystem {
|
||||
s32 m_entry_count;
|
||||
s32 m_offset_count;
|
||||
s32 m_entry_set_count;
|
||||
s64 m_start_offset;
|
||||
s64 m_end_offset;
|
||||
OffsetCache m_offset_cache;
|
||||
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(); }
|
||||
|
||||
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 IsEmpty() const { return m_entry_size == 0; }
|
||||
|
||||
Result Find(Visitor *visitor, s64 virtual_address) const;
|
||||
Result Find(Visitor *visitor, s64 virtual_address);
|
||||
Result InvalidateCache();
|
||||
|
||||
s32 GetEntryCount() const { return m_entry_count; }
|
||||
IAllocator *GetAllocator() const { return m_node_l1.GetAllocator(); }
|
||||
|
||||
s64 GetStart() const { return m_start_offset; }
|
||||
s64 GetEnd() const { return m_end_offset; }
|
||||
s64 GetSize() const { return m_end_offset - m_start_offset; }
|
||||
Result GetOffsets(Offsets *out) {
|
||||
/* Ensure we have an offset cache. */
|
||||
R_TRY(this->EnsureOffsetCache());
|
||||
|
||||
bool Includes(s64 offset) const {
|
||||
return m_start_offset <= offset && offset < m_end_offset;
|
||||
}
|
||||
|
||||
bool Includes(s64 offset, s64 size) const {
|
||||
return size > 0 && m_start_offset <= offset && size <= m_end_offset - offset;
|
||||
/* Set the output. */
|
||||
*out = m_offset_cache.offsets;
|
||||
R_SUCCEED();
|
||||
}
|
||||
private:
|
||||
template<typename EntryType>
|
||||
@@ -250,6 +269,7 @@ namespace ams::fssystem {
|
||||
size_t size;
|
||||
NodeHeader entry_set;
|
||||
s32 entry_index;
|
||||
Offsets offsets;
|
||||
EntryType entry;
|
||||
};
|
||||
private:
|
||||
@@ -262,6 +282,8 @@ namespace ams::fssystem {
|
||||
s64 GetEntrySetIndex(s32 node_index, s32 offset_index) const {
|
||||
return (m_offset_count - m_node_l1->count) + (m_offset_count * node_index) + offset_index;
|
||||
}
|
||||
|
||||
Result EnsureOffsetCache();
|
||||
};
|
||||
|
||||
class BucketTree::Visitor {
|
||||
@@ -283,6 +305,7 @@ namespace ams::fssystem {
|
||||
static_assert(util::is_pod<EntrySetHeader>::value);
|
||||
private:
|
||||
const BucketTree *m_tree;
|
||||
BucketTree::Offsets m_offsets;
|
||||
void *m_entry;
|
||||
s32 m_entry_index;
|
||||
s32 m_entry_set_count;
|
||||
@@ -314,7 +337,7 @@ namespace ams::fssystem {
|
||||
|
||||
const BucketTree *GetTree() const { return m_tree; }
|
||||
private:
|
||||
Result Initialize(const BucketTree *tree);
|
||||
Result Initialize(const BucketTree *tree, const BucketTree::Offsets &offsets);
|
||||
|
||||
Result Find(s64 virtual_address);
|
||||
|
||||
|
||||
@@ -47,10 +47,15 @@ namespace ams::fssystem {
|
||||
PooledBuffer pool(m_node_size, 1);
|
||||
char *buffer = nullptr;
|
||||
|
||||
s64 entry_storage_size;
|
||||
R_TRY(m_entry_storage.GetSize(std::addressof(entry_storage_size)));
|
||||
|
||||
/* Read the node. */
|
||||
if (m_node_size <= pool.GetSize()) {
|
||||
buffer = pool.GetBuffer();
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -59,9 +64,9 @@ namespace ams::fssystem {
|
||||
s64 phys_offset = entry.GetPhysicalOffset();
|
||||
|
||||
/* Start merge tracking. */
|
||||
s64 merge_size = 0;
|
||||
s64 merge_size = 0;
|
||||
s64 readable_size = 0;
|
||||
bool merged = false;
|
||||
bool merged = false;
|
||||
|
||||
/* Iterate. */
|
||||
auto entry_index = param.entry_index;
|
||||
@@ -89,7 +94,7 @@ namespace ams::fssystem {
|
||||
}
|
||||
|
||||
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 {
|
||||
next_entry_offset = param.entry_set.offset;
|
||||
}
|
||||
@@ -103,7 +108,7 @@ namespace ams::fssystem {
|
||||
|
||||
/* Determine how much data we should read. */
|
||||
const auto remaining_size = end_offset - cur_offset;
|
||||
const size_t read_size = static_cast<size_t>(std::min(data_size, remaining_size));
|
||||
const size_t read_size = static_cast<size_t>(std::min(data_size, remaining_size));
|
||||
AMS_ASSERT(read_size <= param.size);
|
||||
|
||||
/* Update our merge tracking. */
|
||||
@@ -156,6 +161,7 @@ namespace ams::fssystem {
|
||||
ContinuousReadingParam<EntryType> param = {
|
||||
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));
|
||||
|
||||
/* Scan. */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,9 +18,9 @@
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
enum CompressionType {
|
||||
enum CompressionType : u8 {
|
||||
CompressionType_None = 0,
|
||||
CompressionType_1 = 1,
|
||||
CompressionType_Zeros = 1,
|
||||
CompressionType_2 = 2,
|
||||
CompressionType_Lz4 = 3,
|
||||
CompressionType_Unknown = 4,
|
||||
@@ -29,14 +29,16 @@ namespace ams::fssystem {
|
||||
using DecompressorFunction = Result (*)(void *, size_t, const void *, size_t);
|
||||
using GetDecompressorFunction = DecompressorFunction (*)(CompressionType);
|
||||
|
||||
constexpr s64 CompressionBlockAlignment = 0x10;
|
||||
|
||||
namespace CompressionTypeUtility {
|
||||
|
||||
constexpr bool IsBlockAlignmentRequired(CompressionType type) {
|
||||
return type != CompressionType_None && type != CompressionType_1;
|
||||
return type != CompressionType_None && type != CompressionType_Zeros;
|
||||
}
|
||||
|
||||
constexpr bool IsDataStorageAccessRequired(CompressionType type) {
|
||||
return type != CompressionType_1;
|
||||
return type != CompressionType_Zeros;
|
||||
}
|
||||
|
||||
constexpr bool IsRandomAccessible(CompressionType type) {
|
||||
|
||||
@@ -133,22 +133,26 @@ namespace ams::fssystem {
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
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 {
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
AMS_UNUSED(offset, buffer, size);
|
||||
return fs::ResultUnsupportedOperationInIndirectStorageA();
|
||||
R_THROW(fs::ResultUnsupportedOperationInIndirectStorageA());
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
AMS_UNUSED(size);
|
||||
return fs::ResultUnsupportedOperationInIndirectStorageB();
|
||||
R_THROW(fs::ResultUnsupportedOperationInIndirectStorageB());
|
||||
}
|
||||
protected:
|
||||
BucketTree &GetEntryTable() { return m_table; }
|
||||
@@ -158,7 +162,7 @@ namespace ams::fssystem {
|
||||
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);
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
template<bool ContinuousCheck, typename F>
|
||||
template<bool ContinuousCheck, bool RangeCheck, typename F>
|
||||
Result IndirectStorage::OperatePerEntry(s64 offset, s64 size, F func) {
|
||||
/* Validate preconditions. */
|
||||
AMS_ASSERT(offset >= 0);
|
||||
@@ -28,15 +28,19 @@ namespace ams::fssystem {
|
||||
/* Succeed if there's nothing to operate on. */
|
||||
R_SUCCEED_IF(size == 0);
|
||||
|
||||
/* Get the table offsets. */
|
||||
BucketTree::Offsets table_offsets;
|
||||
R_TRY(m_table.GetOffsets(std::addressof(table_offsets)));
|
||||
|
||||
/* 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. */
|
||||
BucketTree::Visitor visitor;
|
||||
R_TRY(m_table.Find(std::addressof(visitor), offset));
|
||||
{
|
||||
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. */
|
||||
@@ -67,16 +71,21 @@ namespace ams::fssystem {
|
||||
/* Ensure that we can process. */
|
||||
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. */
|
||||
const auto data_offset = cur_offset - cur_entry_offset;
|
||||
const auto cur_entry_phys_offset = cur_entry.GetPhysicalOffset();
|
||||
const auto cur_size = static_cast<s64>(cr_info.GetReadSize());
|
||||
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());
|
||||
|
||||
/* 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(cur_entry_phys_offset + data_offset + cur_size <= cur_data_storage_size, fs::ResultInvalidIndirectStorageSize());
|
||||
}
|
||||
|
||||
/* Operate. */
|
||||
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()) {
|
||||
R_TRY(visitor.MoveNext());
|
||||
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 {
|
||||
next_entry_offset = m_table.GetEnd();
|
||||
next_entry_offset = table_offsets.end_offset;
|
||||
}
|
||||
R_UNLESS(cur_offset < next_entry_offset, fs::ResultInvalidIndirectEntryOffset());
|
||||
|
||||
/* Get the offset of the entry in the data we read. */
|
||||
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);
|
||||
|
||||
/* Determine how much is left. */
|
||||
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);
|
||||
|
||||
/* Operate, if we need to. */
|
||||
@@ -116,14 +125,17 @@ namespace ams::fssystem {
|
||||
}
|
||||
|
||||
if (needs_operate) {
|
||||
/* Get the current data storage's size. */
|
||||
s64 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. */
|
||||
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(cur_entry_phys_offset + data_offset + cur_size <= cur_data_storage_size, fs::ResultIndirectStorageCorrupted());
|
||||
|
||||
if constexpr (RangeCheck) {
|
||||
/* Get the current data storage's size. */
|
||||
s64 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. */
|
||||
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_TRY(func(std::addressof(m_data_storage[cur_entry.storage_index]), cur_entry_phys_offset + data_offset, cur_offset, cur_size));
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace ams::fssystem {
|
||||
IntegrityRomFsStorage() : m_mutex() { /* ... */ }
|
||||
virtual ~IntegrityRomFsStorage() override { this->Finalize(); }
|
||||
|
||||
Result Initialize(save::HierarchicalIntegrityVerificationInformation level_hash_info, Hash master_hash, save::HierarchicalIntegrityVerificationStorage::HierarchicalStorageInformation storage_info, IBufferManager *bm, IHash256GeneratorFactory *hgf);
|
||||
Result Initialize(save::HierarchicalIntegrityVerificationInformation level_hash_info, Hash master_hash, save::HierarchicalIntegrityVerificationStorage::HierarchicalStorageInformation storage_info, fs::IBufferManager *bm, IHash256GeneratorFactory *hgf);
|
||||
void Finalize();
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include <stratosphere/fssystem/fssystem_i_hash_256_generator.hpp>
|
||||
#include <stratosphere/fssystem/fssystem_asynchronous_access.hpp>
|
||||
#include <stratosphere/fssystem/fssystem_nca_header.hpp>
|
||||
#include <stratosphere/fssystem/buffers/fssystem_i_buffer_manager.hpp>
|
||||
#include <stratosphere/fs/fs_i_buffer_manager.hpp>
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
@@ -228,17 +228,17 @@ namespace ams::fssystem {
|
||||
std::shared_ptr<NcaReader> m_original_reader;
|
||||
std::shared_ptr<NcaReader> m_reader;
|
||||
MemoryResource * const m_allocator;
|
||||
fssystem::IBufferManager * const m_buffer_manager;
|
||||
fs::IBufferManager * const m_buffer_manager;
|
||||
fssystem::IHash256GeneratorFactorySelector * const m_hash_generator_factory_selector;
|
||||
public:
|
||||
static Result SetupFsHeaderReader(NcaFsHeaderReader *out, const NcaReader &reader, s32 fs_index);
|
||||
public:
|
||||
NcaFileSystemDriver(std::shared_ptr<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_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_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);
|
||||
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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -36,32 +36,32 @@ namespace ams::fssystem {
|
||||
if (size > 0) {
|
||||
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 {
|
||||
AMS_UNUSED(dst, dst_size, op_id, offset, size, src, src_size);
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result GetSize(s64 *out) override {
|
||||
AMS_ASSERT(out != nullptr);
|
||||
*out = std::numeric_limits<s64>::max();
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result Flush() override {
|
||||
return ResultSuccess();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
|
||||
AMS_UNUSED(offset, buffer, size);
|
||||
return fs::ResultUnsupportedOperationInZeroStorageA();
|
||||
R_THROW(fs::ResultUnsupportedOperationInZeroStorageA());
|
||||
}
|
||||
|
||||
virtual Result SetSize(s64 size) override {
|
||||
AMS_UNUSED(size);
|
||||
return fs::ResultUnsupportedOperationInZeroStorageB();
|
||||
R_THROW(fs::ResultUnsupportedOperationInZeroStorageB());
|
||||
}
|
||||
};
|
||||
private:
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <stratosphere/fs/fs_memory_management.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/impl/fssystem_block_cache_manager.hpp>
|
||||
|
||||
namespace ams::fssystem::save {
|
||||
|
||||
@@ -30,7 +31,7 @@ namespace ams::fssystem::save {
|
||||
constexpr inline size_t IntegrityLayerCountSaveDataMeta = 4;
|
||||
|
||||
struct FileSystemBufferManagerSet {
|
||||
IBufferManager *buffers[IntegrityMaxLayerCount];
|
||||
fs::IBufferManager *buffers[IntegrityMaxLayerCount];
|
||||
};
|
||||
static_assert(util::is_pod<FileSystemBufferManagerSet>::value);
|
||||
|
||||
@@ -40,51 +41,77 @@ namespace ams::fssystem::save {
|
||||
public:
|
||||
static constexpr size_t DefaultMaxCacheEntryCount = 24;
|
||||
private:
|
||||
using MemoryRange = std::pair<uintptr_t, size_t>;
|
||||
using CacheIndex = s32;
|
||||
using MemoryRange = fs::IBufferManager::MemoryRange;
|
||||
|
||||
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 {
|
||||
size_t size;
|
||||
AccessRange range;
|
||||
bool is_valid;
|
||||
bool is_write_back;
|
||||
bool is_cached;
|
||||
bool is_flushing;
|
||||
s64 offset;
|
||||
IBufferManager::CacheHandle handle;
|
||||
u16 lru_counter;
|
||||
fs::IBufferManager::CacheHandle handle;
|
||||
uintptr_t memory_address;
|
||||
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);
|
||||
|
||||
using BlockCacheManager = ::ams::fssystem::impl::BlockCacheManager<CacheEntry, fs::IBufferManager>;
|
||||
using CacheIndex = BlockCacheManager::CacheIndex;
|
||||
|
||||
enum Flag : s32 {
|
||||
Flag_KeepBurstMode = (1 << 8),
|
||||
Flag_RealData = (1 << 10),
|
||||
};
|
||||
private:
|
||||
IBufferManager *m_buffer_manager;
|
||||
os::SdkRecursiveMutex *m_mutex;
|
||||
std::unique_ptr<CacheEntry[], ::ams::fs::impl::Deleter> m_entries;
|
||||
IStorage *m_data_storage;
|
||||
Result m_last_result;
|
||||
s64 m_data_size;
|
||||
size_t m_verification_block_size;
|
||||
size_t m_verification_block_shift;
|
||||
CacheIndex m_invalidate_index;
|
||||
s32 m_max_cache_entry_count;
|
||||
s32 m_flags;
|
||||
s32 m_buffer_level;
|
||||
fs::StorageType m_storage_type;
|
||||
BlockCacheManager m_block_cache_manager;
|
||||
public:
|
||||
BlockCacheBufferedStorage();
|
||||
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();
|
||||
|
||||
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::ResultUnsupportedOperationInBlockCacheBufferedStorageA(); }
|
||||
virtual Result SetSize(s64) override { R_THROW(fs::ResultUnsupportedOperationInBlockCacheBufferedStorageA()); }
|
||||
virtual Result GetSize(s64 *out) override;
|
||||
|
||||
virtual Result Flush() override;
|
||||
@@ -119,40 +146,24 @@ namespace ams::fssystem::save {
|
||||
}
|
||||
}
|
||||
private:
|
||||
s32 GetMaxCacheEntryCount() const {
|
||||
return m_max_cache_entry_count;
|
||||
}
|
||||
|
||||
Result ClearImpl(s64 offset, s64 size);
|
||||
Result ClearSignatureImpl(s64 offset, s64 size);
|
||||
Result InvalidateCacheImpl(s64 offset, s64 size);
|
||||
Result FillZeroImpl(s64 offset, s64 size);
|
||||
Result DestroySignatureImpl(s64 offset, s64 size);
|
||||
Result InvalidateImpl();
|
||||
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);
|
||||
|
||||
void DestroyBuffer(CacheEntry *entry, const MemoryRange &range);
|
||||
|
||||
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(CacheIndex *out, const MemoryRange &range, CacheEntry *entry);
|
||||
|
||||
Result StoreOrDestroyBuffer(const MemoryRange &range, CacheEntry *entry) {
|
||||
AMS_ASSERT(entry != nullptr);
|
||||
|
||||
ON_RESULT_FAILURE { this->DestroyBuffer(entry, range); };
|
||||
|
||||
R_TRY(this->StoreAssociateBuffer(range, *entry));
|
||||
|
||||
R_SUCCEED();
|
||||
CacheIndex dummy;
|
||||
R_RETURN(this->StoreOrDestroyBuffer(std::addressof(dummy), range, entry));
|
||||
}
|
||||
|
||||
Result FlushCacheEntry(CacheIndex index, bool invalidate);
|
||||
Result FlushRangeCacheEntries(s64 offset, s64 size, bool invalidate);
|
||||
void InvalidateRangeCacheEntries(s64 offset, s64 size);
|
||||
|
||||
Result FlushAllCacheEntries();
|
||||
Result InvalidateAllCacheEntries();
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include <stratosphere/os.hpp>
|
||||
#include <stratosphere/fs/fs_istorage.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 {
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace ams::fssystem::save {
|
||||
class SharedCache;
|
||||
private:
|
||||
fs::SubStorage m_base_storage;
|
||||
IBufferManager *m_buffer_manager;
|
||||
fs::IBufferManager *m_buffer_manager;
|
||||
size_t m_block_size;
|
||||
s64 m_base_storage_size;
|
||||
std::unique_ptr<Cache[]> m_caches;
|
||||
@@ -44,7 +44,7 @@ namespace ams::fssystem::save {
|
||||
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();
|
||||
|
||||
bool IsInitialized() const { return m_caches != nullptr; }
|
||||
@@ -61,7 +61,7 @@ namespace ams::fssystem::save {
|
||||
|
||||
void InvalidateCaches();
|
||||
|
||||
IBufferManager *GetBufferManager() const { return m_buffer_manager; }
|
||||
fs::IBufferManager *GetBufferManager() const { return m_buffer_manager; }
|
||||
|
||||
void EnableBulkRead() { m_bulk_read_enabled = true; }
|
||||
private:
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace ams::fssystem::save {
|
||||
s64 m_verification_block_order;
|
||||
s64 m_upper_layer_verification_block_size;
|
||||
s64 m_upper_layer_verification_block_order;
|
||||
IBufferManager *m_buffer_manager;
|
||||
fs::IBufferManager *m_buffer_manager;
|
||||
fs::HashSalt m_salt;
|
||||
bool m_is_real_data;
|
||||
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) { /* ... */ }
|
||||
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();
|
||||
|
||||
virtual Result Read(s64 offset, void *buffer, size_t size) override;
|
||||
|
||||
Reference in New Issue
Block a user