mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-09 22:56:35 +00:00
fs: first pass at compressed storage (works on iridium with wip hac2l code)
This commit is contained in:
parent
df631d74f0
commit
d638bbbb62
34 changed files with 2375 additions and 722 deletions
|
@ -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
|
#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)
|
||||||
{
|
{
|
||||||
/* ... */
|
/* ... */
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,9 +64,9 @@ namespace ams::fssystem {
|
||||||
s64 phys_offset = entry.GetPhysicalOffset();
|
s64 phys_offset = entry.GetPhysicalOffset();
|
||||||
|
|
||||||
/* Start merge tracking. */
|
/* Start merge tracking. */
|
||||||
s64 merge_size = 0;
|
s64 merge_size = 0;
|
||||||
s64 readable_size = 0;
|
s64 readable_size = 0;
|
||||||
bool merged = false;
|
bool merged = false;
|
||||||
|
|
||||||
/* Iterate. */
|
/* Iterate. */
|
||||||
auto entry_index = param.entry_index;
|
auto entry_index = param.entry_index;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -103,7 +108,7 @@ namespace ams::fssystem {
|
||||||
|
|
||||||
/* Determine how much data we should read. */
|
/* Determine how much data we should read. */
|
||||||
const auto remaining_size = end_offset - cur_offset;
|
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);
|
AMS_ASSERT(read_size <= param.size);
|
||||||
|
|
||||||
/* Update our merge tracking. */
|
/* Update our merge tracking. */
|
||||||
|
@ -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. */
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
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. */
|
/* 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) {
|
||||||
/* 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();
|
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));
|
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() { /* ... */ }
|
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 {
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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/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();
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
@ -130,8 +134,8 @@ namespace ams::fssystem {
|
||||||
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>()->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));
|
||||||
|
|
|
@ -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_size = static_cast<size_t>(offset_end - core_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);
|
||||||
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();
|
||||||
|
|
|
@ -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,69 +218,77 @@ 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)));
|
|
||||||
R_TRY(m_node_storage.OperateRange(fs::OperationId::Invalidate, 0, storage_size));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Refresh start/end offsets. */
|
|
||||||
{
|
|
||||||
/* Read node. */
|
|
||||||
R_TRY(m_node_storage.Read(0, m_node_l1.Get(), m_node_size));
|
|
||||||
|
|
||||||
/* Verify node. */
|
|
||||||
R_TRY(m_node_l1->Verify(0, m_node_size, sizeof(s64)));
|
|
||||||
|
|
||||||
/* Validate offsets. */
|
|
||||||
const auto * const node = m_node_l1.Get<Node>();
|
|
||||||
|
|
||||||
s64 start_offset;
|
|
||||||
if (m_offset_count < m_entry_set_count && node->GetCount() < m_offset_count) {
|
|
||||||
start_offset = *node->GetEnd();
|
|
||||||
} else {
|
|
||||||
start_offset = *node->GetBegin();
|
|
||||||
}
|
|
||||||
const auto end_offset = node->GetEndOffset();
|
|
||||||
|
|
||||||
R_UNLESS(0 <= start_offset && start_offset <= node->GetBeginOffset(), fs::ResultInvalidBucketTreeEntryOffset());
|
|
||||||
R_UNLESS(start_offset < end_offset, fs::ResultInvalidBucketTreeEntryOffset());
|
|
||||||
|
|
||||||
/* Set refreshed offsets. */
|
|
||||||
m_start_offset = start_offset;
|
|
||||||
m_end_offset = end_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Invalidate the entry storage cache. */
|
/* Invalidate the entry storage cache. */
|
||||||
{
|
R_TRY(m_entry_storage.OperateRange(fs::OperationId::Invalidate, 0, std::numeric_limits<s64>::max()));
|
||||||
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();
|
/* Reset our offsets. */
|
||||||
|
m_offset_cache.is_initialized = false;
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result BucketTree::Visitor::Initialize(const BucketTree *tree) {
|
Result BucketTree::EnsureOffsetCache() {
|
||||||
|
/* If we already have an offset cache, we're good. */
|
||||||
|
R_SUCCEED_IF(m_offset_cache.is_initialized);
|
||||||
|
|
||||||
|
/* 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)));
|
||||||
|
|
||||||
|
/* Get the node. */
|
||||||
|
auto * const node = m_node_l1.Get<Node>();
|
||||||
|
|
||||||
|
s64 start_offset;
|
||||||
|
if (m_offset_count < m_entry_set_count && node->GetCount() < m_offset_count) {
|
||||||
|
start_offset = *node->GetEnd();
|
||||||
|
} else {
|
||||||
|
start_offset = *node->GetBegin();
|
||||||
|
}
|
||||||
|
const auto end_offset = node->GetEndOffset();
|
||||||
|
|
||||||
|
R_UNLESS(0 <= start_offset && start_offset <= node->GetBeginOffset(), fs::ResultInvalidBucketTreeEntryOffset());
|
||||||
|
R_UNLESS(start_offset < end_offset, fs::ResultInvalidBucketTreeEntryOffset());
|
||||||
|
|
||||||
|
m_offset_cache.offsets.start_offset = start_offset;
|
||||||
|
m_offset_cache.offsets.end_offset = end_offset;
|
||||||
|
m_offset_cache.is_initialized = true;
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result BucketTree::Visitor::Initialize(const BucketTree *tree, const BucketTree::Offsets &offsets) {
|
||||||
AMS_ASSERT(tree != nullptr);
|
AMS_ASSERT(tree != nullptr);
|
||||||
AMS_ASSERT(m_tree == nullptr || m_tree == tree);
|
AMS_ASSERT(m_tree == nullptr || m_tree == tree);
|
||||||
|
|
||||||
|
@ -284,7 +296,8 @@ namespace ams::fssystem {
|
||||||
m_entry = tree->GetAllocator()->Allocate(tree->m_entry_size);
|
m_entry = tree->GetAllocator()->Allocate(tree->m_entry_size);
|
||||||
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;
|
||||||
|
|
|
@ -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) {
|
if (!m_table.IsEmpty()) {
|
||||||
/* Validate arguments. */
|
/* Invalidate our table's cache. */
|
||||||
R_UNLESS(m_table.Includes(offset, size), fs::ResultOutOfRange());
|
R_TRY(m_table.InvalidateCache());
|
||||||
if (!m_table.IsEmpty()) {
|
|
||||||
/* Invalidate our table's cache. */
|
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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));
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
110
libraries/libvapours/include/vapours/util/util_i_function.hpp
Normal file
110
libraries/libvapours/include/vapours/util/util_i_function.hpp
Normal 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue