Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"

This reverts commit 15b7df8ef1.
This commit is contained in:
souldbminersmwc
2025-11-09 16:14:52 -05:00
parent 22ec140738
commit 21a3f953d7
3804 changed files with 435 additions and 570162 deletions

View File

@@ -1,131 +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>
#include <stratosphere/os.hpp>
#include <stratosphere/fs/fs_i_buffer_manager.hpp>
namespace ams::fssystem::buffers {
namespace impl {
constexpr inline auto RetryWait = TimeSpan::FromMilliSeconds(10);
}
/* ACCURATE_TO_VERSION: Unknown */
template<typename F, typename OnFailure>
Result DoContinuouslyUntilBufferIsAllocated(F f, OnFailure on_failure, const char *function_name) {
constexpr auto BufferAllocationRetryLogCountMax = 10;
constexpr auto BufferAllocationRetryLogInterval = 100;
for (auto count = 1; true; count++) {
R_TRY_CATCH(f()) {
R_CATCH(fs::ResultBufferAllocationFailed) {
if ((1 <= count && count <= BufferAllocationRetryLogCountMax) || ((count % BufferAllocationRetryLogInterval) == 0)) {
/* TODO: Log */
AMS_UNUSED(function_name);
}
R_TRY(on_failure());
os::SleepThread(impl::RetryWait);
continue;
}
} R_END_TRY_CATCH;
R_SUCCEED();
}
}
template<typename F>
Result DoContinuouslyUntilBufferIsAllocated(F f, const char *function_name) {
R_TRY(DoContinuouslyUntilBufferIsAllocated(f, []() ALWAYS_INLINE_LAMBDA { R_SUCCEED(); }, function_name));
R_SUCCEED();
}
/* ACCURATE_TO_VERSION: Unknown */
class BufferManagerContext {
private:
bool m_needs_blocking;
public:
constexpr BufferManagerContext() : m_needs_blocking(false) { /* ... */ }
public:
bool IsNeedBlocking() const { return m_needs_blocking; }
void SetNeedBlocking(bool need) { m_needs_blocking = need; }
};
void RegisterBufferManagerContext(const BufferManagerContext *context);
BufferManagerContext *GetBufferManagerContext();
void EnableBlockingBufferManagerAllocation();
/* ACCURATE_TO_VERSION: Unknown */
class ScopedBufferManagerContextRegistration {
private:
BufferManagerContext m_cur_context;
const BufferManagerContext *m_old_context;
public:
ALWAYS_INLINE explicit ScopedBufferManagerContextRegistration() {
m_old_context = GetBufferManagerContext();
if (m_old_context != nullptr) {
m_cur_context = *m_old_context;
}
RegisterBufferManagerContext(std::addressof(m_cur_context));
}
ALWAYS_INLINE ~ScopedBufferManagerContextRegistration() {
RegisterBufferManagerContext(m_old_context);
}
};
/* ACCURATE_TO_VERSION: Unknown */
template<typename IsValidBufferFunction>
Result AllocateBufferUsingBufferManagerContext(fs::IBufferManager::MemoryRange *out, fs::IBufferManager *buffer_manager, size_t size, const fs::IBufferManager::BufferAttribute attribute, IsValidBufferFunction is_valid_buffer, const char *func_name) {
AMS_ASSERT(out != nullptr);
AMS_ASSERT(buffer_manager != nullptr);
AMS_ASSERT(func_name != nullptr);
/* Clear the output. */
*out = fs::IBufferManager::MakeMemoryRange(0, 0);
/* Get the context. */
auto context = GetBufferManagerContext();
auto AllocateBufferImpl = [=]() -> Result {
auto buffer = buffer_manager->AllocateBuffer(size, attribute);
if (!is_valid_buffer(buffer)) {
if (buffer.first != 0) {
buffer_manager->DeallocateBuffer(buffer.first, buffer.second);
}
R_THROW(fs::ResultBufferAllocationFailed());
}
*out = buffer;
R_SUCCEED();
};
if (context == nullptr || !context->IsNeedBlocking()) {
/* If there's no context (or we don't need to block), just allocate the buffer. */
R_TRY(AllocateBufferImpl());
} else {
/* Otherwise, try to allocate repeatedly. */
R_TRY(DoContinuouslyUntilBufferIsAllocated(AllocateBufferImpl, func_name));
}
AMS_ASSERT(out->first != 0);
R_SUCCEED();
}
}

View File

@@ -1,193 +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>
#include <stratosphere/fs/impl/fs_newable.hpp>
#include <stratosphere/fs/fs_i_buffer_manager.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: Unknown */
class FileSystemBuddyHeap {
NON_COPYABLE(FileSystemBuddyHeap);
NON_MOVEABLE(FileSystemBuddyHeap);
public:
static constexpr size_t BufferAlignment = sizeof(void *);
static constexpr size_t BlockSizeMin = 2 * sizeof(void *);
static constexpr s32 OrderUpperLimit = BITSIZEOF(s32) - 1;
private:
class PageList;
struct PageEntry { PageEntry *next; };
static_assert(util::is_pod<PageEntry>::value);
static_assert(sizeof(PageEntry) <= BlockSizeMin);
class PageList : public ::ams::fs::impl::Newable {
NON_COPYABLE(PageList);
NON_MOVEABLE(PageList);
private:
PageEntry *m_first_page_entry;
PageEntry *m_last_page_entry;
s32 m_entry_count;
public:
constexpr PageList() : m_first_page_entry(), m_last_page_entry(), m_entry_count() { /* ... */ }
constexpr bool IsEmpty() const { return m_entry_count == 0; }
constexpr s32 GetSize() const { return m_entry_count; }
constexpr const PageEntry *GetFront() const { return m_first_page_entry; }
public:
PageEntry *PopFront();
void PushBack(PageEntry *page_entry);
bool Remove(PageEntry *page_entry);
};
private:
size_t m_block_size;
s32 m_order_max;
uintptr_t m_heap_start;
size_t m_heap_size;
PageList *m_free_lists;
size_t m_total_free_size;
PageList *m_external_free_lists;
std::unique_ptr<PageList[]> m_internal_free_lists;
public:
static constexpr s32 GetBlockCountFromOrder(s32 order) {
AMS_ASSERT(0 <= order);
AMS_ASSERT(order < OrderUpperLimit);
return (1 << order);
}
static constexpr size_t QueryWorkBufferSize(s32 order_max) {
AMS_ASSERT(0 < order_max && order_max < OrderUpperLimit);
return util::AlignUp(sizeof(PageList) * (order_max + 1) + alignof(PageList), alignof(u64));
}
static constexpr s32 QueryOrderMax(size_t size, size_t block_size) {
AMS_ASSERT(size >= block_size);
AMS_ASSERT(block_size >= BlockSizeMin);
AMS_ASSERT(util::IsPowerOfTwo(block_size));
const auto block_count = static_cast<s32>(util::AlignUp(size, block_size) / block_size);
for (auto order = 1; true; order++) {
if (block_count <= GetBlockCountFromOrder(order)) {
return order;
}
}
}
public:
constexpr FileSystemBuddyHeap() : m_block_size(), m_order_max(), m_heap_start(), m_heap_size(), m_free_lists(), m_total_free_size(), m_external_free_lists(), m_internal_free_lists() { /* ... */ }
Result Initialize(uintptr_t address, size_t size, size_t block_size, s32 order_max);
Result Initialize(uintptr_t address, size_t size, size_t block_size) {
R_RETURN(this->Initialize(address, size, block_size, QueryOrderMax(size, block_size)));
}
Result Initialize(uintptr_t address, size_t size, size_t block_size, s32 order_max, void *work, size_t work_size) {
AMS_ASSERT(work_size >= QueryWorkBufferSize(order_max));
AMS_UNUSED(work_size);
const auto aligned_work = util::AlignUp(reinterpret_cast<uintptr_t>(work), alignof(PageList));
m_external_free_lists = reinterpret_cast<PageList *>(aligned_work);
R_RETURN(this->Initialize(address, size, block_size, order_max));
}
Result Initialize(uintptr_t address, size_t size, size_t block_size, void *work, size_t work_size) {
R_RETURN(this->Initialize(address, size, block_size, QueryOrderMax(size, block_size), work, work_size));
}
void Finalize();
void *AllocateByOrder(s32 order);
void Free(void *ptr, s32 order);
size_t GetTotalFreeSize() const;
size_t GetAllocatableSizeMax() const;
void Dump() const;
s32 GetOrderFromBytes(size_t size) const {
AMS_ASSERT(m_free_lists != nullptr);
return this->GetOrderFromBlockCount(this->GetBlockCountFromSize(size));
}
size_t GetBytesFromOrder(s32 order) const {
AMS_ASSERT(m_free_lists != nullptr);
AMS_ASSERT(0 <= order);
AMS_ASSERT(order <= this->GetOrderMax());
return (this->GetBlockSize() << order);
}
s32 GetOrderMax() const {
AMS_ASSERT(m_free_lists != nullptr);
return m_order_max;
}
size_t GetBlockSize() const {
AMS_ASSERT(m_free_lists != nullptr);
return m_block_size;
}
s32 GetPageBlockCountMax() const {
AMS_ASSERT(m_free_lists != nullptr);
return 1 << this->GetOrderMax();
}
size_t GetPageSizeMax() const {
AMS_ASSERT(m_free_lists != nullptr);
return this->GetPageBlockCountMax() * this->GetBlockSize();
}
private:
void DivideBuddies(PageEntry *page_entry, s32 required_order, s32 chosen_order);
void JoinBuddies(PageEntry *page_entry, s32 order);
PageEntry *GetBuddy(PageEntry *page_entry, s32 order);
PageEntry *GetFreePageEntry(s32 order);
s32 GetOrderFromBlockCount(s32 block_count) const;
s32 GetBlockCountFromSize(size_t size) const {
const size_t bsize = this->GetBlockSize();
return static_cast<s32>(util::AlignUp(size, bsize) / bsize);
}
uintptr_t GetAddressFromPageEntry(const PageEntry &page_entry) const {
const uintptr_t address = reinterpret_cast<uintptr_t>(std::addressof(page_entry));
AMS_ASSERT(m_heap_start <= address);
AMS_ASSERT(address < m_heap_start + m_heap_size);
AMS_ASSERT(util::IsAligned(address - m_heap_start, this->GetBlockSize()));
return address;
}
PageEntry *GetPageEntryFromAddress(uintptr_t address) const {
AMS_ASSERT(m_heap_start <= address);
AMS_ASSERT(address < m_heap_start + m_heap_size);
return reinterpret_cast<PageEntry *>(m_heap_start + util::AlignDown(address - m_heap_start, this->GetBlockSize()));
}
s32 GetIndexFromPageEntry(const PageEntry &page_entry) const {
const uintptr_t address = reinterpret_cast<uintptr_t>(std::addressof(page_entry));
AMS_ASSERT(m_heap_start <= address);
AMS_ASSERT(address < m_heap_start + m_heap_size);
AMS_ASSERT(util::IsAligned(address - m_heap_start, this->GetBlockSize()));
return static_cast<s32>((address - m_heap_start) / this->GetBlockSize());
}
bool IsAlignedToOrder(const PageEntry *page_entry, s32 order) const {
return util::IsAligned(GetIndexFromPageEntry(*page_entry), GetBlockCountFromOrder(order));
}
};
}

View File

@@ -1,307 +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>
#include <stratosphere/lmem.hpp>
#include <stratosphere/fs/fs_memory_management.hpp>
#include <stratosphere/fs/fs_i_buffer_manager.hpp>
#include <stratosphere/fssystem/buffers/fssystem_file_system_buddy_heap.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: Unknown */
class FileSystemBufferManager : public fs::IBufferManager {
NON_COPYABLE(FileSystemBufferManager);
NON_MOVEABLE(FileSystemBufferManager);
public:
using BuddyHeap = FileSystemBuddyHeap;
private:
class CacheHandleTable {
NON_COPYABLE(CacheHandleTable);
NON_MOVEABLE(CacheHandleTable);
private:
class Entry {
private:
CacheHandle m_handle;
uintptr_t m_address;
size_t m_size;
BufferAttribute m_attr;
public:
constexpr void Initialize(CacheHandle h, uintptr_t a, size_t sz, BufferAttribute t) {
m_handle = h;
m_address = a;
m_size = sz;
m_attr = t;
}
constexpr CacheHandle GetHandle() const {
return m_handle;
}
constexpr uintptr_t GetAddress() const {
return m_address;
}
constexpr size_t GetSize() const {
return m_size;
}
constexpr BufferAttribute GetBufferAttribute() const {
return m_attr;
}
};
class AttrInfo : public util::IntrusiveListBaseNode<AttrInfo>, public ::ams::fs::impl::Newable {
NON_COPYABLE(AttrInfo);
NON_MOVEABLE(AttrInfo);
private:
s32 m_level;
s32 m_cache_count;
size_t m_cache_size;
public:
constexpr AttrInfo(s32 l, s32 cc, size_t cs) : m_level(l), m_cache_count(cc), m_cache_size(cs) {
/* ... */
}
constexpr s32 GetLevel() const {
return m_level;
}
constexpr s32 GetCacheCount() const {
return m_cache_count;
}
constexpr void IncrementCacheCount() {
++m_cache_count;
}
constexpr void DecrementCacheCount() {
--m_cache_count;
}
constexpr size_t GetCacheSize() const {
return m_cache_size;
}
constexpr void AddCacheSize(size_t diff) {
m_cache_size += diff;
}
constexpr void SubtractCacheSize(size_t diff) {
AMS_ASSERT(m_cache_size >= diff);
m_cache_size -= diff;
}
using Newable::operator new;
using Newable::operator delete;
static ALWAYS_INLINE void *operator new(size_t, void *p) noexcept { return p; }
static ALWAYS_INLINE void operator delete(void *, size_t, void*) noexcept { /* ... */ }
};
using AttrListTraits = util::IntrusiveListBaseTraits<AttrInfo>;
using AttrList = typename AttrListTraits::ListType;
private:
std::unique_ptr<char[], ::ams::fs::impl::Deleter> m_internal_entry_buffer;
char *m_external_entry_buffer;
size_t m_entry_buffer_size;
Entry *m_entries;
s32 m_entry_count;
s32 m_entry_count_max;
AttrList m_attr_list;
char *m_external_attr_info_buffer;
s32 m_external_attr_info_count;
s32 m_cache_count_min;
size_t m_cache_size_min;
size_t m_total_cache_size;
CacheHandle m_current_handle;
public:
static constexpr size_t QueryWorkBufferSize(s32 max_cache_count) {
AMS_ASSERT(max_cache_count > 0);
const auto entry_size = sizeof(Entry) * max_cache_count;
const auto attr_list_size = sizeof(AttrInfo) * 0x100;
return util::AlignUp(entry_size + attr_list_size + alignof(Entry) + alignof(AttrInfo), 8);
}
public:
CacheHandleTable() : m_internal_entry_buffer(), m_external_entry_buffer(), m_entry_buffer_size(), m_entries(), m_entry_count(), m_entry_count_max(), m_attr_list(), m_external_attr_info_buffer(), m_external_attr_info_count(), m_cache_count_min(), m_cache_size_min(), m_total_cache_size(), m_current_handle() {
/* ... */
}
~CacheHandleTable() {
this->Finalize();
}
Result Initialize(s32 max_cache_count);
Result Initialize(s32 max_cache_count, void *work, size_t work_size) {
const auto aligned_entry_buf = util::AlignUp(reinterpret_cast<uintptr_t>(work), alignof(Entry));
m_external_entry_buffer = reinterpret_cast<char *>(aligned_entry_buf);
m_entry_buffer_size = sizeof(Entry) * max_cache_count;
const auto aligned_attr_info_buf = util::AlignUp(reinterpret_cast<uintptr_t>(m_external_entry_buffer + m_entry_buffer_size), alignof(AttrInfo));
const auto work_end = reinterpret_cast<uintptr_t>(work) + work_size;
m_external_attr_info_buffer = reinterpret_cast<char *>(aligned_attr_info_buf);
m_external_attr_info_count = static_cast<s32>((work_end - aligned_attr_info_buf) / sizeof(AttrInfo));
R_SUCCEED();
}
void Finalize();
bool Register(CacheHandle *out, uintptr_t address, size_t size, const BufferAttribute &attr);
bool Unregister(uintptr_t *out_address, size_t *out_size, CacheHandle handle);
bool UnregisterOldest(uintptr_t *out_address, size_t *out_size, const BufferAttribute &attr, size_t required_size = 0);
CacheHandle PublishCacheHandle();
size_t GetTotalCacheSize() const;
private:
void UnregisterCore(uintptr_t *out_address, size_t *out_size, Entry *entry);
Entry *AcquireEntry(uintptr_t address, size_t size, const BufferAttribute &attr);
void ReleaseEntry(Entry *entry);
AttrInfo *FindAttrInfo(const BufferAttribute &attr);
s32 GetCacheCountMin(const BufferAttribute &attr) {
AMS_UNUSED(attr);
return m_cache_count_min;
}
size_t GetCacheSizeMin(const BufferAttribute &attr) {
AMS_UNUSED(attr);
return m_cache_size_min;
}
};
private:
BuddyHeap m_buddy_heap;
CacheHandleTable m_cache_handle_table;
size_t m_total_size;
size_t m_peak_free_size;
size_t m_peak_total_allocatable_size;
size_t m_retried_count;
mutable os::SdkMutex m_mutex;
public:
static constexpr size_t QueryWorkBufferSize(s32 max_cache_count, s32 max_order) {
const auto buddy_size = FileSystemBuddyHeap::QueryWorkBufferSize(max_order);
const auto table_size = CacheHandleTable::QueryWorkBufferSize(max_cache_count);
return buddy_size + table_size;
}
public:
FileSystemBufferManager() : m_total_size(), m_peak_free_size(), m_peak_total_allocatable_size(), m_retried_count(), m_mutex() { /* ... */ }
virtual ~FileSystemBufferManager() { /* ... */ }
Result Initialize(s32 max_cache_count, uintptr_t address, size_t buffer_size, size_t block_size) {
AMS_ASSERT(buffer_size > 0);
R_TRY(m_cache_handle_table.Initialize(max_cache_count));
R_TRY(m_buddy_heap.Initialize(address, buffer_size, block_size));
m_total_size = m_buddy_heap.GetTotalFreeSize();
m_peak_free_size = m_total_size;
m_peak_total_allocatable_size = m_total_size;
R_SUCCEED();
}
Result Initialize(s32 max_cache_count, uintptr_t address, size_t buffer_size, size_t block_size, s32 max_order) {
AMS_ASSERT(buffer_size > 0);
R_TRY(m_cache_handle_table.Initialize(max_cache_count));
R_TRY(m_buddy_heap.Initialize(address, buffer_size, block_size, max_order));
m_total_size = m_buddy_heap.GetTotalFreeSize();
m_peak_free_size = m_total_size;
m_peak_total_allocatable_size = m_total_size;
R_SUCCEED();
}
Result Initialize(s32 max_cache_count, uintptr_t address, size_t buffer_size, size_t block_size, void *work, size_t work_size) {
const auto table_size = CacheHandleTable::QueryWorkBufferSize(max_cache_count);
const auto buddy_size = work_size - table_size;
AMS_ASSERT(work_size > table_size);
const auto table_buffer = static_cast<char *>(work);
const auto buddy_buffer = table_buffer + table_size;
R_TRY(m_cache_handle_table.Initialize(max_cache_count, table_buffer, table_size));
R_TRY(m_buddy_heap.Initialize(address, buffer_size, block_size, buddy_buffer, buddy_size));
m_total_size = m_buddy_heap.GetTotalFreeSize();
m_peak_free_size = m_total_size;
m_peak_total_allocatable_size = m_total_size;
R_SUCCEED();
}
Result Initialize(s32 max_cache_count, uintptr_t address, size_t buffer_size, size_t block_size, s32 max_order, void *work, size_t work_size) {
const auto table_size = CacheHandleTable::QueryWorkBufferSize(max_cache_count);
const auto buddy_size = work_size - table_size;
AMS_ASSERT(work_size > table_size);
const auto table_buffer = static_cast<char *>(work);
const auto buddy_buffer = table_buffer + table_size;
R_TRY(m_cache_handle_table.Initialize(max_cache_count, table_buffer, table_size));
R_TRY(m_buddy_heap.Initialize(address, buffer_size, block_size, max_order, buddy_buffer, buddy_size));
m_total_size = m_buddy_heap.GetTotalFreeSize();
m_peak_free_size = m_total_size;
m_peak_total_allocatable_size = m_total_size;
R_SUCCEED();
}
void Finalize() {
m_buddy_heap.Finalize();
m_cache_handle_table.Finalize();
}
private:
virtual const fs::IBufferManager::MemoryRange DoAllocateBuffer(size_t size, const BufferAttribute &attr) override;
virtual void DoDeallocateBuffer(uintptr_t address, size_t size) override;
virtual CacheHandle DoRegisterCache(uintptr_t address, size_t size, const BufferAttribute &attr) override;
virtual const fs::IBufferManager::MemoryRange DoAcquireCache(CacheHandle handle) override;
virtual size_t DoGetTotalSize() const override;
virtual size_t DoGetFreeSize() const override;
virtual size_t DoGetTotalAllocatableSize() const override;
virtual size_t DoGetFreeSizePeak() const override;
virtual size_t DoGetTotalAllocatableSizePeak() const override;
virtual size_t DoGetRetriedCount() const override;
virtual void DoClearPeak() override;
private:
const fs::IBufferManager::MemoryRange AllocateBufferImpl(size_t size, const BufferAttribute &attr);
void DeallocateBufferImpl(uintptr_t address, size_t size);
CacheHandle RegisterCacheImpl(uintptr_t address, size_t size, const BufferAttribute &attr);
const fs::IBufferManager::MemoryRange AcquireCacheImpl(CacheHandle handle);
size_t GetFreeSizeImpl() const;
size_t GetTotalAllocatableSizeImpl() const;
size_t GetFreeSizePeakImpl() const;
size_t GetTotalAllocatableSizePeakImpl() const;
size_t GetRetriedCountImpl() const;
void ClearPeakImpl();
};
}

View File

@@ -1,132 +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>
#include <stratosphere/fssystem/fssystem_aes_ctr_storage.hpp>
#include <stratosphere/fssystem/fssystem_bucket_tree.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: Unknown */
class AesCtrCounterExtendedStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
NON_COPYABLE(AesCtrCounterExtendedStorage);
NON_MOVEABLE(AesCtrCounterExtendedStorage);
public:
static constexpr size_t BlockSize = crypto::Aes128CtrEncryptor::BlockSize;
static constexpr size_t KeySize = crypto::Aes128CtrEncryptor::KeySize;
static constexpr size_t IvSize = crypto::Aes128CtrEncryptor::IvSize;
static constexpr size_t NodeSize = 16_KB;
using IAllocator = BucketTree::IAllocator;
using DecryptFunction = void(*)(void *dst, size_t dst_size, u8 index, u8 gen, const void *enc_key, size_t enc_key_size, const void *iv, size_t iv_size, const void *src, size_t src_size);
class IDecryptor {
public:
virtual ~IDecryptor() { /* ... */ }
virtual void Decrypt(void *buf, size_t buf_size, const void *enc_key, size_t enc_key_size, void *iv, size_t iv_size) = 0;
virtual bool HasExternalDecryptionKey() const = 0;
};
struct Entry {
enum class Encryption : u8 {
Encrypted = 0,
NotEncrypted = 1,
};
u8 offset[sizeof(s64)];
Encryption encryption_value;
u8 reserved[3];
s32 generation;
void SetOffset(s64 value) {
std::memcpy(this->offset, std::addressof(value), sizeof(s64));
}
s64 GetOffset() const {
s64 value;
std::memcpy(std::addressof(value), this->offset, sizeof(s64));
return value;
}
};
static_assert(sizeof(Entry) == 0x10);
static_assert(alignof(Entry) == 4);
static_assert(util::is_pod<Entry>::value);
public:
static constexpr s64 QueryHeaderStorageSize() {
return BucketTree::QueryHeaderStorageSize();
}
static constexpr s64 QueryNodeStorageSize(s32 entry_count) {
return BucketTree::QueryNodeStorageSize(NodeSize, sizeof(Entry), entry_count);
}
static constexpr s64 QueryEntryStorageSize(s32 entry_count) {
return BucketTree::QueryEntryStorageSize(NodeSize, sizeof(Entry), entry_count);
}
static Result CreateExternalDecryptor(std::unique_ptr<IDecryptor> *out, DecryptFunction func, s32 key_index, s32 key_generation);
static Result CreateSoftwareDecryptor(std::unique_ptr<IDecryptor> *out);
private:
BucketTree m_table;
fs::SubStorage m_data_storage;
u8 m_key[KeySize];
u32 m_secure_value;
s64 m_counter_offset;
std::unique_ptr<IDecryptor> m_decryptor;
public:
AesCtrCounterExtendedStorage() : m_table(), m_data_storage(), m_secure_value(), m_counter_offset(), m_decryptor() { /* ... */ }
virtual ~AesCtrCounterExtendedStorage() { this->Finalize(); }
Result Initialize(IAllocator *allocator, const void *key, size_t key_size, u32 secure_value, s64 counter_offset, fs::SubStorage data_storage, fs::SubStorage node_storage, fs::SubStorage entry_storage, s32 entry_count, std::unique_ptr<IDecryptor> &&decryptor);
void Finalize();
bool IsInitialized() const { return m_table.IsInitialized(); }
virtual Result Read(s64 offset, void *buffer, size_t size) override;
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override;
virtual Result GetSize(s64 *out) override {
AMS_ASSERT(out != nullptr);
BucketTree::Offsets offsets;
R_TRY(m_table.GetOffsets(std::addressof(offsets)));
*out = offsets.end_offset;
R_SUCCEED();
}
virtual Result Flush() override {
R_SUCCEED();
}
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
AMS_UNUSED(offset, buffer, size);
R_THROW(fs::ResultUnsupportedWriteForAesCtrCounterExtendedStorage());
}
virtual Result SetSize(s64 size) override {
AMS_UNUSED(size);
R_THROW(fs::ResultUnsupportedSetSizeForAesCtrCounterExtendedStorage());
}
Result GetEntryList(Entry *out_entries, s32 *out_entry_count, s32 entry_count, s64 offset, s64 size);
private:
Result Initialize(IAllocator *allocator, const void *key, size_t key_size, u32 secure_value, fs::SubStorage data_storage, fs::SubStorage table_storage);
};
}

View File

@@ -1,55 +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>
#include <stratosphere/fs/fs_istorage.hpp>
#include <stratosphere/fs/impl/fs_newable.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: Unknown */
template<fs::PointerToStorage BasePointer>
class AesCtrStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
NON_COPYABLE(AesCtrStorage);
NON_MOVEABLE(AesCtrStorage);
public:
static constexpr size_t BlockSize = crypto::Aes128CtrEncryptor::BlockSize;
static constexpr size_t KeySize = crypto::Aes128CtrEncryptor::KeySize;
static constexpr size_t IvSize = crypto::Aes128CtrEncryptor::IvSize;
private:
BasePointer m_base_storage;
char m_key[KeySize];
char m_iv[IvSize];
public:
static void MakeIv(void *dst, size_t dst_size, u64 upper, s64 offset);
public:
AesCtrStorage(BasePointer base, const void *key, size_t key_size, const void *iv, size_t iv_size);
virtual Result Read(s64 offset, void *buffer, size_t size) override;
virtual Result Write(s64 offset, const void *buffer, size_t size) override;
virtual Result Flush() override;
virtual Result SetSize(s64 size) override;
virtual Result GetSize(s64 *out) override;
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override;
};
using AesCtrStorageByPointer = AesCtrStorage<fs::IStorage *>;
using AesCtrStorageBySharedPointer = AesCtrStorage<std::shared_ptr<fs::IStorage>>;
}

View File

@@ -1,50 +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>
#include <stratosphere/fs/fs_istorage.hpp>
#include <stratosphere/fs/impl/fs_newable.hpp>
#include <stratosphere/fssystem/fssystem_nca_file_system_driver.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: 14.3.0.0 */
class AesCtrStorageExternal : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
NON_COPYABLE(AesCtrStorageExternal);
NON_MOVEABLE(AesCtrStorageExternal);
public:
static constexpr size_t BlockSize = crypto::Aes128CtrEncryptor::BlockSize;
static constexpr size_t KeySize = crypto::Aes128CtrEncryptor::KeySize;
static constexpr size_t IvSize = crypto::Aes128CtrEncryptor::IvSize;
private:
std::shared_ptr<fs::IStorage> m_base_storage;
u8 m_iv[IvSize];
DecryptAesCtrFunction m_decrypt_function;
s32 m_key_index;
s32 m_key_generation;
u8 m_encrypted_key[KeySize];
public:
AesCtrStorageExternal(std::shared_ptr<fs::IStorage> bs, const void *enc_key, size_t enc_key_size, const void *iv, size_t iv_size, DecryptAesCtrFunction df, s32 kidx, s32 kgen);
virtual Result Read(s64 offset, void *buffer, size_t size) override;
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override;
virtual Result GetSize(s64 *out) override;
virtual Result Flush() override;
virtual Result Write(s64 offset, const void *buffer, size_t size) override;
virtual Result SetSize(s64 size) override;
};
}

View File

@@ -1,58 +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>
#include <stratosphere/fs/fs_istorage.hpp>
#include <stratosphere/fs/impl/fs_newable.hpp>
#include <stratosphere/os.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: Unknown */
template<fs::PointerToStorage BasePointer>
class AesXtsStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
NON_COPYABLE(AesXtsStorage);
NON_MOVEABLE(AesXtsStorage);
public:
static constexpr size_t AesBlockSize = crypto::Aes128XtsEncryptor::BlockSize;
static constexpr size_t KeySize = crypto::Aes128XtsEncryptor::KeySize;
static constexpr size_t IvSize = crypto::Aes128XtsEncryptor::IvSize;
private:
BasePointer m_base_storage;
char m_key[2][KeySize];
char m_iv[IvSize];
const size_t m_block_size;
os::SdkMutex m_mutex;
public:
static void MakeAesXtsIv(void *dst, size_t dst_size, s64 offset, size_t block_size);
public:
AesXtsStorage(BasePointer base, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, size_t block_size);
virtual Result Read(s64 offset, void *buffer, size_t size) override;
virtual Result Write(s64 offset, const void *buffer, size_t size) override;
virtual Result Flush() override;
virtual Result SetSize(s64 size) override;
virtual Result GetSize(s64 *out) override;
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override;
};
using AesXtsStorageByPointer = AesXtsStorage<fs::IStorage *>;
using AesXtsStorageBySharedPointer = AesXtsStorage<std::shared_ptr<fs::IStorage>>;
}

View File

@@ -1,54 +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>
#include <stratosphere/fs/fs_istorage.hpp>
#include <stratosphere/fs/impl/fs_newable.hpp>
#include <stratosphere/fssystem/fssystem_nca_file_system_driver.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: 14.3.0.0 */
template<fs::PointerToStorage BasePointer>
class AesXtsStorageExternal : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
NON_COPYABLE(AesXtsStorageExternal);
NON_MOVEABLE(AesXtsStorageExternal);
public:
static constexpr size_t AesBlockSize = crypto::Aes128XtsEncryptor::BlockSize;
static constexpr size_t KeySize = crypto::Aes128XtsEncryptor::KeySize;
static constexpr size_t IvSize = crypto::Aes128XtsEncryptor::IvSize;
private:
BasePointer m_base_storage;
char m_key[2][KeySize];
char m_iv[IvSize];
const size_t m_block_size;
CryptAesXtsFunction m_encrypt_function;
CryptAesXtsFunction m_decrypt_function;
public:
AesXtsStorageExternal(BasePointer bs, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, size_t block_size, CryptAesXtsFunction ef, CryptAesXtsFunction df);
virtual Result Read(s64 offset, void *buffer, size_t size) override;
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override;
virtual Result GetSize(s64 *out) override;
virtual Result Flush() override;
virtual Result Write(s64 offset, const void *buffer, size_t size) override;
virtual Result SetSize(s64 size) override;
};
using AesXtsStorageExternalByPointer = AesXtsStorageExternal<fs::IStorage *>;
using AesXtsStorageExternalBySharedPointer = AesXtsStorageExternal<std::shared_ptr<fs::IStorage>>;
}

View File

@@ -1,324 +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>
#include <stratosphere/fs/fs_istorage.hpp>
#include <stratosphere/fs/impl/fs_newable.hpp>
#include <stratosphere/fssystem/fssystem_alignment_matching_storage_impl.hpp>
#include <stratosphere/fssystem/fssystem_pooled_buffer.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: Unknown */
template<size_t _DataAlign, size_t _BufferAlign>
class AlignmentMatchingStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
NON_COPYABLE(AlignmentMatchingStorage);
NON_MOVEABLE(AlignmentMatchingStorage);
public:
static constexpr size_t DataAlign = _DataAlign;
static constexpr size_t BufferAlign = _BufferAlign;
static constexpr size_t DataAlignMax = 0x200;
static_assert(DataAlign <= DataAlignMax);
static_assert(util::IsPowerOfTwo(DataAlign));
static_assert(util::IsPowerOfTwo(BufferAlign));
private:
std::shared_ptr<fs::IStorage> m_shared_base_storage;
fs::IStorage * const m_base_storage;
s64 m_base_storage_size;
bool m_is_base_storage_size_dirty;
public:
explicit AlignmentMatchingStorage(fs::IStorage *bs) : m_base_storage(bs), m_is_base_storage_size_dirty(true) {
/* ... */
}
explicit AlignmentMatchingStorage(std::shared_ptr<fs::IStorage> bs) : m_shared_base_storage(std::move(bs)), m_base_storage(m_shared_base_storage.get()), m_is_base_storage_size_dirty(true) {
/* ... */
}
virtual Result Read(s64 offset, void *buffer, size_t size) override {
/* Allocate a work buffer on stack. */
__attribute__((aligned(DataAlignMax))) char work_buf[DataAlign];
static_assert(util::IsAligned(alignof(work_buf), BufferAlign));
/* Succeed if zero size. */
R_SUCCEED_IF(size == 0);
/* Validate arguments. */
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
s64 bs_size = 0;
R_TRY(this->GetSize(std::addressof(bs_size)));
R_TRY(fs::IStorage::CheckAccessRange(offset, size, bs_size));
R_RETURN(AlignmentMatchingStorageImpl::Read(m_base_storage, work_buf, sizeof(work_buf), DataAlign, BufferAlign, offset, static_cast<char *>(buffer), size));
}
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
/* Allocate a work buffer on stack. */
__attribute__((aligned(DataAlignMax))) char work_buf[DataAlign];
static_assert(util::IsAligned(alignof(work_buf), BufferAlign));
/* Succeed if zero size. */
R_SUCCEED_IF(size == 0);
/* Validate arguments. */
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
s64 bs_size = 0;
R_TRY(this->GetSize(std::addressof(bs_size)));
R_TRY(fs::IStorage::CheckAccessRange(offset, size, bs_size));
R_RETURN(AlignmentMatchingStorageImpl::Write(m_base_storage, work_buf, sizeof(work_buf), DataAlign, BufferAlign, offset, static_cast<const char *>(buffer), size));
}
virtual Result Flush() override {
R_RETURN(m_base_storage->Flush());
}
virtual Result SetSize(s64 size) override {
ON_SCOPE_EXIT { m_is_base_storage_size_dirty = true; };
R_RETURN(m_base_storage->SetSize(util::AlignUp(size, DataAlign)));
}
virtual Result GetSize(s64 *out) override {
AMS_ASSERT(out != nullptr);
if (m_is_base_storage_size_dirty) {
s64 size;
R_TRY(m_base_storage->GetSize(std::addressof(size)));
m_base_storage_size = size;
m_is_base_storage_size_dirty = false;
}
*out = m_base_storage_size;
R_SUCCEED();
}
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
if (op_id == fs::OperationId::Invalidate) {
R_RETURN(m_base_storage->OperateRange(fs::OperationId::Invalidate, offset, size));
} else {
/* Succeed if zero size. */
R_SUCCEED_IF(size == 0);
/* Get the base storage size. */
s64 bs_size = 0;
R_TRY(this->GetSize(std::addressof(bs_size)));
R_TRY(fs::IStorage::CheckOffsetAndSize(offset, size));
/* Operate on the base storage. */
const auto valid_size = std::min(size, bs_size - offset);
const auto aligned_offset = util::AlignDown(offset, DataAlign);
const auto aligned_offset_end = util::AlignUp(offset + valid_size, DataAlign);
const auto aligned_size = aligned_offset_end - aligned_offset;
R_RETURN(m_base_storage->OperateRange(dst, dst_size, op_id, aligned_offset, aligned_size, src, src_size));
}
}
};
/* ACCURATE_TO_VERSION: Unknown */
template<fs::PointerToStorage BasePointer, size_t _BufferAlign>
class AlignmentMatchingStoragePooledBuffer : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
NON_COPYABLE(AlignmentMatchingStoragePooledBuffer);
NON_MOVEABLE(AlignmentMatchingStoragePooledBuffer);
public:
static constexpr size_t BufferAlign = _BufferAlign;
static_assert(util::IsPowerOfTwo(BufferAlign));
private:
BasePointer m_base_storage;
s64 m_base_storage_size;
size_t m_data_align;
bool m_is_base_storage_size_dirty;
public:
explicit AlignmentMatchingStoragePooledBuffer(BasePointer bs, size_t da) : m_base_storage(std::move(bs)), m_data_align(da), m_is_base_storage_size_dirty(true) {
AMS_ASSERT(util::IsPowerOfTwo(da));
}
virtual Result Read(s64 offset, void *buffer, size_t size) override {
/* Succeed if zero size. */
R_SUCCEED_IF(size == 0);
/* Validate arguments. */
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
s64 bs_size = 0;
R_TRY(this->GetSize(std::addressof(bs_size)));
R_TRY(fs::IStorage::CheckAccessRange(offset, size, bs_size));
/* Allocate a pooled buffer. */
PooledBuffer pooled_buffer;
pooled_buffer.AllocateParticularlyLarge(m_data_align, m_data_align);
R_RETURN(AlignmentMatchingStorageImpl::Read(m_base_storage, pooled_buffer.GetBuffer(), pooled_buffer.GetSize(), m_data_align, BufferAlign, offset, static_cast<char *>(buffer), size));
}
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
/* Succeed if zero size. */
R_SUCCEED_IF(size == 0);
/* Validate arguments. */
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
s64 bs_size = 0;
R_TRY(this->GetSize(std::addressof(bs_size)));
R_TRY(fs::IStorage::CheckAccessRange(offset, size, bs_size));
/* Allocate a pooled buffer. */
PooledBuffer pooled_buffer;
pooled_buffer.AllocateParticularlyLarge(m_data_align, m_data_align);
R_RETURN(AlignmentMatchingStorageImpl::Write(m_base_storage, pooled_buffer.GetBuffer(), pooled_buffer.GetSize(), m_data_align, BufferAlign, offset, static_cast<const char *>(buffer), size));
}
virtual Result Flush() override {
R_RETURN(m_base_storage->Flush());
}
virtual Result SetSize(s64 size) override {
ON_SCOPE_EXIT { m_is_base_storage_size_dirty = true; };
R_RETURN(m_base_storage->SetSize(util::AlignUp(size, m_data_align)));
}
virtual Result GetSize(s64 *out) override {
AMS_ASSERT(out != nullptr);
if (m_is_base_storage_size_dirty) {
s64 size;
R_TRY(m_base_storage->GetSize(std::addressof(size)));
m_base_storage_size = size;
m_is_base_storage_size_dirty = false;
}
*out = m_base_storage_size;
R_SUCCEED();
}
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
if (op_id == fs::OperationId::Invalidate) {
R_RETURN(m_base_storage->OperateRange(fs::OperationId::Invalidate, offset, size));
} else {
/* Succeed if zero size. */
R_SUCCEED_IF(size == 0);
/* Get the base storage size. */
s64 bs_size = 0;
R_TRY(this->GetSize(std::addressof(bs_size)));
R_TRY(fs::IStorage::CheckOffsetAndSize(offset, size));
/* Operate on the base storage. */
const auto valid_size = std::min(size, bs_size - offset);
const auto aligned_offset = util::AlignDown(offset, m_data_align);
const auto aligned_offset_end = util::AlignUp(offset + valid_size, m_data_align);
const auto aligned_size = aligned_offset_end - aligned_offset;
R_RETURN(m_base_storage->OperateRange(dst, dst_size, op_id, aligned_offset, aligned_size, src, src_size));
}
}
};
/* ACCURATE_TO_VERSION: Unknown */
template<size_t _BufferAlign>
class AlignmentMatchingStorageInBulkRead : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
NON_COPYABLE(AlignmentMatchingStorageInBulkRead);
NON_MOVEABLE(AlignmentMatchingStorageInBulkRead);
public:
static constexpr size_t BufferAlign = _BufferAlign;
static_assert(util::IsPowerOfTwo(BufferAlign));
private:
std::shared_ptr<fs::IStorage> m_shared_base_storage;
fs::IStorage * const m_base_storage;
s64 m_base_storage_size;
size_t m_data_align;
public:
explicit AlignmentMatchingStorageInBulkRead(fs::IStorage *bs, size_t da) : m_shared_base_storage(), m_base_storage(bs), m_base_storage_size(-1), m_data_align(da) {
AMS_ASSERT(util::IsPowerOfTwo(m_data_align));
}
explicit AlignmentMatchingStorageInBulkRead(std::shared_ptr<fs::IStorage> bs, size_t da) : m_shared_base_storage(bs), m_base_storage(m_shared_base_storage.get()), m_base_storage_size(-1), m_data_align(da) {
AMS_ASSERT(util::IsPowerOfTwo(da));
}
virtual Result Read(s64 offset, void *buffer, size_t size) override;
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
/* Succeed if zero size. */
R_SUCCEED_IF(size == 0);
/* Validate arguments. */
R_UNLESS(buffer != nullptr, fs::ResultNullptrArgument());
s64 bs_size = 0;
R_TRY(this->GetSize(std::addressof(bs_size)));
R_TRY(fs::IStorage::CheckAccessRange(offset, size, bs_size));
/* Allocate a pooled buffer. */
PooledBuffer pooled_buffer(m_data_align, m_data_align);
R_RETURN(AlignmentMatchingStorageImpl::Write(m_base_storage, pooled_buffer.GetBuffer(), pooled_buffer.GetSize(), m_data_align, BufferAlign, offset, static_cast<const char *>(buffer), size));
}
virtual Result Flush() override {
R_RETURN(m_base_storage->Flush());
}
virtual Result SetSize(s64 size) override {
ON_SCOPE_EXIT { m_base_storage_size = -1; };
R_RETURN(m_base_storage->SetSize(util::AlignUp(size, m_data_align)));
}
virtual Result GetSize(s64 *out) override {
AMS_ASSERT(out != nullptr);
if (m_base_storage_size < 0) {
s64 size;
R_TRY(m_base_storage->GetSize(std::addressof(size)));
m_base_storage_size = size;
}
*out = m_base_storage_size;
R_SUCCEED();
}
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
if (op_id == fs::OperationId::Invalidate) {
R_RETURN(m_base_storage->OperateRange(fs::OperationId::Invalidate, offset, size));
} else {
/* Succeed if zero size. */
R_SUCCEED_IF(size == 0);
/* Get the base storage size. */
s64 bs_size = 0;
R_TRY(this->GetSize(std::addressof(bs_size)));
R_TRY(fs::IStorage::CheckOffsetAndSize(offset, size));
/* Operate on the base storage. */
const auto valid_size = std::min(size, bs_size - offset);
const auto aligned_offset = util::AlignDown(offset, m_data_align);
const auto aligned_offset_end = util::AlignUp(offset + valid_size, m_data_align);
const auto aligned_size = aligned_offset_end - aligned_offset;
R_RETURN(m_base_storage->OperateRange(dst, dst_size, op_id, aligned_offset, aligned_size, src, src_size));
}
}
};
}

View File

@@ -1,37 +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>
#include <stratosphere/fs/fs_istorage.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: Unknown */
class AlignmentMatchingStorageImpl {
public:
static Result Read(fs::IStorage *base_storage, char *work_buf, size_t work_buf_size, size_t data_alignment, size_t buffer_alignment, s64 offset, char *buffer, size_t size);
static Result Write(fs::IStorage *base_storage, char *work_buf, size_t work_buf_size, size_t data_alignment, size_t buffer_alignment, s64 offset, const char *buffer, size_t size);
static Result Read(std::shared_ptr<fs::IStorage> &base_storage, char *work_buf, size_t work_buf_size, size_t data_alignment, size_t buffer_alignment, s64 offset, char *buffer, size_t size) {
R_RETURN(Read(base_storage.get(), work_buf, work_buf_size, data_alignment, buffer_alignment, offset, buffer, size));
}
static Result Write(std::shared_ptr<fs::IStorage> &base_storage, char *work_buf, size_t work_buf_size, size_t data_alignment, size_t buffer_alignment, s64 offset, const char *buffer, size_t size) {
R_RETURN(Write(base_storage.get(), work_buf, work_buf_size, data_alignment, buffer_alignment, offset, buffer, size));
}
};
}

View File

@@ -1,116 +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>
/* Forward declare ams::fs allocate shared. */
namespace ams::fs::impl {
template<typename T, template<typename, typename> class AllocatorTemplateT, typename Impl, typename... Args>
std::shared_ptr<T> AllocateSharedImpl(Args &&... args);
}
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: Unknown */
using AllocateFunction = void *(*)(size_t size);
using DeallocateFunction = void (*)(void *ptr, size_t size);
void InitializeAllocator(AllocateFunction allocate_func, DeallocateFunction deallocate_func);
void InitializeAllocatorForSystem(AllocateFunction allocate_func, DeallocateFunction deallocate_func);
void *Allocate(size_t size);
void Deallocate(void *ptr, size_t size);
namespace impl {
template<bool ForSystem>
class AllocatorFunctionSet {
public:
static void *Allocate(size_t size);
static void *AllocateUnsafe(size_t size);
static void Deallocate(void *ptr, size_t size);
static void DeallocateUnsafe(void *ptr, size_t size);
static void LockAllocatorMutex();
static void UnlockAllocatorMutex();
};
using AllocatorFunctionSetForNormal = AllocatorFunctionSet<false>;
using AllocatorFunctionSetForSystem = AllocatorFunctionSet<true>;
template<typename T, typename Impl, bool RequireNonNull, bool AllocateWhileLocked>
class AllocatorTemplate : public std::allocator<T> {
public:
template<typename U>
struct rebind {
using other = AllocatorTemplate<U, Impl, RequireNonNull, AllocateWhileLocked>;
};
private:
static ALWAYS_INLINE T *AllocateImpl(::std::size_t n) {
if constexpr (AllocateWhileLocked) {
auto * const p = Impl::AllocateUnsafe(sizeof(T) * n);
Impl::UnlockAllocatorMutex();
return static_cast<T *>(p);
} else {
return static_cast<T *>(Impl::Allocate(sizeof(T) * n));
}
}
public:
AllocatorTemplate() { /* ... */ }
template<typename U>
AllocatorTemplate(const AllocatorTemplate<U, Impl, RequireNonNull, AllocateWhileLocked> &) { /* ... */ }
[[nodiscard]] T *allocate(::std::size_t n) {
auto * const p = AllocateImpl(n);
if constexpr (RequireNonNull) {
AMS_ABORT_UNLESS(p != nullptr);
}
return p;
}
void deallocate(T *p, ::std::size_t n) {
Impl::Deallocate(p, sizeof(T) * n);
}
};
template<typename T, typename Impl>
using AllocatorTemplateForAllocateShared = AllocatorTemplate<T, Impl, true, true>;
}
template<typename T, typename... Args>
std::shared_ptr<T> AllocateShared(Args &&... args) {
return ::ams::fs::impl::AllocateSharedImpl<T, impl::AllocatorTemplateForAllocateShared, impl::AllocatorFunctionSetForNormal>(std::forward<Args>(args)...);
}
template<typename TImpl, typename ErrorResult, typename TIntf, typename... Args>
Result AllocateSharedForSystem(std::shared_ptr<TIntf> *out, Args &&... args) {
/* Allocate the object. */
auto p = ::ams::fs::impl::AllocateSharedImpl<TImpl, impl::AllocatorTemplateForAllocateShared, impl::AllocatorFunctionSetForSystem>(std::forward<Args>(args)...);
/* Check that allocation succeeded. */
R_UNLESS(p != nullptr, ErrorResult());
/* Return the allocated object. */
*out = std::move(p);
R_SUCCEED();
}
}

View File

@@ -1,59 +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 {
/* ACCURATE_TO_VERSION: 13.4.0.0 */
class IAsynchronousAccessSplitter {
public:
static IAsynchronousAccessSplitter *GetDefaultAsynchronousAccessSplitter();
public:
constexpr IAsynchronousAccessSplitter() = default;
constexpr virtual ~IAsynchronousAccessSplitter() { /* ... */ }
public:
Result QueryNextOffset(s64 *out, s64 start_offset, s64 end_offset, s64 access_size, s64 alignment_size);
public:
virtual Result QueryAppropriateOffset(s64 *out, s64 offset, s64 access_size, s64 alignment_size) = 0;
virtual Result QueryInvocationCount(s64 *out, s64 start_offset, s64 end_offset, s64 access_size, s64 alignment_size) { AMS_UNUSED(out, start_offset, end_offset, access_size, alignment_size); AMS_ABORT("TODO"); }
};
/* ACCURATE_TO_VERSION: 13.4.0.0 */
class DefaultAsynchronousAccessSplitter final : public IAsynchronousAccessSplitter {
public:
constexpr DefaultAsynchronousAccessSplitter() = default;
public:
virtual Result QueryAppropriateOffset(s64 *out, s64 offset, s64 access_size, s64 alignment_size) override {
/* Align the access. */
*out = util::AlignDown(offset + access_size, alignment_size);
R_SUCCEED();
}
virtual Result QueryInvocationCount(s64 *out, s64 start_offset, s64 end_offset, s64 access_size, s64 alignment_size) override {
/* Determine aligned access count. */
*out = util::DivideUp(end_offset - util::AlignDown(start_offset, alignment_size), access_size);
R_SUCCEED();
}
};
/* ACCURATE_TO_VERSION: 13.4.0.0 */
inline IAsynchronousAccessSplitter *IAsynchronousAccessSplitter::GetDefaultAsynchronousAccessSplitter() {
static constinit DefaultAsynchronousAccessSplitter s_default_access_splitter;
return std::addressof(s_default_access_splitter);
}
}

View File

@@ -1,58 +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 {
/* ACCURATE_TO_VERSION: 13.4.0.0 */
constexpr inline s32 CountLeadingZeros(u32 val) {
return util::CountLeadingZeros(val);
}
constexpr inline s32 CountLeadingOnes(u32 val) {
return CountLeadingZeros(~val);
}
inline u32 ReadU32(const u8 *buf, size_t index) {
u32 val;
std::memcpy(std::addressof(val), buf + index, sizeof(u32));
return val;
}
inline void WriteU32(u8 *buf, size_t index, u32 val) {
std::memcpy(buf + index, std::addressof(val), sizeof(u32));
}
constexpr inline bool IsPowerOfTwo(s32 val) {
return util::IsPowerOfTwo(val);
}
constexpr inline u32 ILog2(u32 val) {
AMS_ASSERT(val > 0);
return (BITSIZEOF(u32) - 1 - util::CountLeadingZeros<u32>(val));
}
constexpr inline u32 CeilingPowerOfTwo(u32 val) {
if (val == 0) {
return 1;
}
return util::CeilingPowerOfTwo<u32>(val);
}
}

View File

@@ -1,180 +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>
#include <stratosphere/os.hpp>
#include <stratosphere/fs/fs_storage_type.hpp>
#include <stratosphere/fs/fs_istorage.hpp>
#include <stratosphere/fs/fs_memory_management.hpp>
#include <stratosphere/fssystem/buffers/fssystem_file_system_buffer_manager.hpp>
#include <stratosphere/fssystem/impl/fssystem_block_cache_manager.hpp>
namespace ams::fssystem {
constexpr inline size_t IntegrityMinLayerCount = 2;
constexpr inline size_t IntegrityMaxLayerCount = 7;
constexpr inline size_t IntegrityLayerCountSave = 5;
constexpr inline size_t IntegrityLayerCountSaveDataMeta = 4;
struct FileSystemBufferManagerSet {
fs::IBufferManager *buffers[IntegrityMaxLayerCount];
};
static_assert(util::is_pod<FileSystemBufferManagerSet>::value);
/* ACCURATE_TO_VERSION: 13.4.0.0 */
class BlockCacheBufferedStorage : public ::ams::fs::IStorage {
NON_COPYABLE(BlockCacheBufferedStorage);
NON_MOVEABLE(BlockCacheBufferedStorage);
public:
static constexpr size_t DefaultMaxCacheEntryCount = 24;
private:
using MemoryRange = fs::IBufferManager::MemoryRange;
struct AccessRange {
s64 offset;
size_t size;
s64 GetEndOffset() const {
return this->offset + this->size;
}
bool IsIncluded(s64 ofs) const {
return this->offset <= ofs && ofs < this->GetEndOffset();
}
};
static_assert(util::is_pod<AccessRange>::value);
struct CacheEntry {
AccessRange range;
bool is_valid;
bool is_write_back;
bool is_cached;
bool is_flushing;
u16 lru_counter;
fs::IBufferManager::CacheHandle handle;
uintptr_t memory_address;
size_t memory_size;
void Invalidate() {
this->is_write_back = false;
this->is_flushing = false;
}
bool IsAllocated() const {
return this->is_valid && (this->is_write_back ? this->memory_address != 0 : this->handle != 0);
}
bool IsWriteBack() const {
return this->is_write_back;
}
};
static_assert(util::is_pod<CacheEntry>::value);
using BlockCacheManager = ::ams::fssystem::impl::BlockCacheManager<CacheEntry, fs::IBufferManager>;
using CacheIndex = BlockCacheManager::CacheIndex;
enum Flag : s32 {
Flag_KeepBurstMode = (1 << 8),
Flag_RealData = (1 << 10),
};
private:
os::SdkRecursiveMutex *m_mutex;
IStorage *m_data_storage;
Result m_last_result;
s64 m_data_size;
size_t m_verification_block_size;
size_t m_verification_block_shift;
s32 m_flags;
s32 m_buffer_level;
BlockCacheManager m_block_cache_manager;
bool m_is_writable;
public:
BlockCacheBufferedStorage();
virtual ~BlockCacheBufferedStorage() override;
Result Initialize(fs::IBufferManager *bm, os::SdkRecursiveMutex *mtx, IStorage *data, s64 data_size, size_t verif_block_size, s32 max_cache_entries, bool is_real_data, s8 buffer_level, bool is_keep_burst_mode, bool is_writable);
void Finalize();
virtual Result Read(s64 offset, void *buffer, size_t size) override;
virtual Result Write(s64 offset, const void *buffer, size_t size) override;
virtual Result SetSize(s64) override { R_THROW(fs::ResultUnsupportedSetSizeForBlockCacheBufferedStorage()); }
virtual Result GetSize(s64 *out) override;
virtual Result Flush() override;
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override;
using IStorage::OperateRange;
Result Commit();
Result OnRollback();
bool IsEnabledKeepBurstMode() const {
return (m_flags & Flag_KeepBurstMode) != 0;
}
bool IsRealDataCache() const {
return (m_flags & Flag_RealData) != 0;
}
void SetKeepBurstMode(bool en) {
if (en) {
m_flags |= Flag_KeepBurstMode;
} else {
m_flags &= ~Flag_KeepBurstMode;
}
}
void SetRealDataCache(bool en) {
if (en) {
m_flags |= Flag_RealData;
} else {
m_flags &= ~Flag_RealData;
}
}
private:
Result FillZeroImpl(s64 offset, s64 size);
Result DestroySignatureImpl(s64 offset, s64 size);
Result InvalidateImpl();
Result QueryRangeImpl(void *dst, size_t dst_size, s64 offset, s64 size);
Result GetAssociateBuffer(MemoryRange *out_range, CacheEntry *out_entry, s64 offset, size_t ideal_size, bool is_allocate_for_write);
Result StoreOrDestroyBuffer(CacheIndex *out, const MemoryRange &range, CacheEntry *entry);
Result StoreOrDestroyBuffer(const MemoryRange &range, CacheEntry *entry) {
AMS_ASSERT(entry != nullptr);
CacheIndex dummy;
R_RETURN(this->StoreOrDestroyBuffer(std::addressof(dummy), range, entry));
}
Result FlushCacheEntry(CacheIndex index, bool invalidate);
Result FlushRangeCacheEntries(s64 offset, s64 size, bool invalidate);
Result FlushAllCacheEntries();
Result InvalidateAllCacheEntries();
Result ControlDirtiness();
Result UpdateLastResult(Result result);
Result ReadHeadCache(MemoryRange *out_range, CacheEntry *out_entry, bool *out_cache_needed, s64 *offset, s64 *aligned_offset, s64 aligned_offset_end, char **buffer, size_t *size);
Result ReadTailCache(MemoryRange *out_range, CacheEntry *out_entry, bool *out_cache_needed, s64 offset, s64 aligned_offset, s64 *aligned_offset_end, char *buffer, size_t *size);
Result BulkRead(s64 offset, void *buffer, size_t size, MemoryRange *range_head, MemoryRange *range_tail, CacheEntry *entry_head, CacheEntry *entry_tail, bool head_cache_needed, bool tail_cache_needed);
};
}

View File

@@ -1,355 +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>
#include <stratosphere/fs/fs_substorage.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: Unknown */
class BucketTree {
NON_COPYABLE(BucketTree);
NON_MOVEABLE(BucketTree);
public:
static constexpr u32 Magic = util::FourCC<'B','K','T','R'>::Code;
static constexpr u32 Version = 1;
static constexpr size_t NodeSizeMin = 1_KB;
static constexpr size_t NodeSizeMax = 512_KB;
public:
class Visitor;
struct Header {
u32 magic;
u32 version;
s32 entry_count;
s32 reserved;
void Format(s32 entry_count);
Result Verify() const;
};
static_assert(util::is_pod<Header>::value);
static_assert(sizeof(Header) == 0x10);
struct NodeHeader {
s32 index;
s32 count;
s64 offset;
Result Verify(s32 node_index, size_t node_size, size_t entry_size) const;
};
static_assert(util::is_pod<NodeHeader>::value);
static_assert(sizeof(NodeHeader) == 0x10);
struct Offsets {
s64 start_offset;
s64 end_offset;
constexpr bool IsInclude(s64 offset) const {
return this->start_offset <= offset && offset < this->end_offset;
}
constexpr bool IsInclude(s64 offset, s64 size) const {
return size > 0 && this->start_offset <= offset && size <= (this->end_offset - offset);
}
};
static_assert(util::is_pod<Offsets>::value);
static_assert(sizeof(Offsets) == 0x10);
struct OffsetCache {
Offsets offsets;
os::SdkMutex mutex;
bool is_initialized;
constexpr OffsetCache() : offsets{ -1, -1 }, mutex(), is_initialized(false) { /* ... */ }
};
class ContinuousReadingInfo {
private:
size_t m_read_size;
s32 m_skip_count;
bool m_done;
public:
constexpr ContinuousReadingInfo() : m_read_size(), m_skip_count(), m_done() { /* ... */ }
constexpr void Reset() { m_read_size = 0; m_skip_count = 0; m_done = false; }
constexpr void SetSkipCount(s32 count) { AMS_ASSERT(count >= 0); m_skip_count = count; }
constexpr s32 GetSkipCount() const { return m_skip_count; }
constexpr bool CheckNeedScan() { return (--m_skip_count) <= 0; }
constexpr void Done() { m_read_size = 0; m_done = true; }
constexpr bool IsDone() const { return m_done; }
constexpr void SetReadSize(size_t size) { m_read_size = size; }
constexpr size_t GetReadSize() const { return m_read_size; }
constexpr bool CanDo() const { return m_read_size > 0; }
};
using IAllocator = MemoryResource;
private:
class NodeBuffer {
NON_COPYABLE(NodeBuffer);
private:
IAllocator *m_allocator;
void *m_header;
public:
NodeBuffer() : m_allocator(), m_header() { /* ... */ }
~NodeBuffer() {
AMS_ASSERT(m_header == nullptr);
}
NodeBuffer(NodeBuffer &&rhs) : m_allocator(rhs.m_allocator), m_header(rhs.m_header) {
rhs.m_allocator = nullptr;
rhs.m_header = nullptr;
}
NodeBuffer &operator=(NodeBuffer &&rhs) {
if (this != std::addressof(rhs)) {
AMS_ASSERT(m_header == nullptr);
m_allocator = rhs.m_allocator;
m_header = rhs.m_header;
rhs.m_allocator = nullptr;
rhs.m_header = nullptr;
}
return *this;
}
bool Allocate(IAllocator *allocator, size_t node_size) {
AMS_ASSERT(m_header == nullptr);
m_allocator = allocator;
m_header = allocator->Allocate(node_size, sizeof(s64));
AMS_ASSERT(util::IsAligned(m_header, sizeof(s64)));
return m_header != nullptr;
}
void Free(size_t node_size) {
if (m_header) {
m_allocator->Deallocate(m_header, node_size);
m_header = nullptr;
}
m_allocator = nullptr;
}
void FillZero(size_t node_size) const {
if (m_header) {
std::memset(m_header, 0, node_size);
}
}
NodeHeader *Get() const {
return reinterpret_cast<NodeHeader *>(m_header);
}
NodeHeader *operator->() const { return this->Get(); }
template<typename T>
T *Get() const {
static_assert(util::is_pod<T>::value);
static_assert(sizeof(T) == sizeof(NodeHeader));
return reinterpret_cast<T *>(m_header);
}
IAllocator *GetAllocator() const {
return m_allocator;
}
};
private:
static constexpr s32 GetEntryCount(size_t node_size, size_t entry_size) {
return static_cast<s32>((node_size - sizeof(NodeHeader)) / entry_size);
}
static constexpr s32 GetOffsetCount(size_t node_size) {
return static_cast<s32>((node_size - sizeof(NodeHeader)) / sizeof(s64));
}
static constexpr s32 GetEntrySetCount(size_t node_size, size_t entry_size, s32 entry_count) {
const s32 entry_count_per_node = GetEntryCount(node_size, entry_size);
return util::DivideUp(entry_count, entry_count_per_node);
}
static constexpr s32 GetNodeL2Count(size_t node_size, size_t entry_size, s32 entry_count) {
const s32 offset_count_per_node = GetOffsetCount(node_size);
const s32 entry_set_count = GetEntrySetCount(node_size, entry_size, entry_count);
if (entry_set_count <= offset_count_per_node) {
return 0;
}
const s32 node_l2_count = util::DivideUp(entry_set_count, offset_count_per_node);
AMS_ABORT_UNLESS(node_l2_count <= offset_count_per_node);
return util::DivideUp(entry_set_count - (offset_count_per_node - (node_l2_count - 1)), offset_count_per_node);
}
public:
static constexpr s64 QueryHeaderStorageSize() { return sizeof(Header); }
static constexpr s64 QueryNodeStorageSize(size_t node_size, size_t entry_size, s32 entry_count) {
AMS_ASSERT(entry_size >= sizeof(s64));
AMS_ASSERT(node_size >= entry_size + sizeof(NodeHeader));
AMS_ASSERT(NodeSizeMin <= node_size && node_size <= NodeSizeMax);
AMS_ASSERT(util::IsPowerOfTwo(node_size));
AMS_ASSERT(entry_count >= 0);
if (entry_count <= 0) {
return 0;
}
return (1 + GetNodeL2Count(node_size, entry_size, entry_count)) * static_cast<s64>(node_size);
}
static constexpr s64 QueryEntryStorageSize(size_t node_size, size_t entry_size, s32 entry_count) {
AMS_ASSERT(entry_size >= sizeof(s64));
AMS_ASSERT(node_size >= entry_size + sizeof(NodeHeader));
AMS_ASSERT(NodeSizeMin <= node_size && node_size <= NodeSizeMax);
AMS_ASSERT(util::IsPowerOfTwo(node_size));
AMS_ASSERT(entry_count >= 0);
if (entry_count <= 0) {
return 0;
}
return GetEntrySetCount(node_size, entry_size, entry_count) * static_cast<s64>(node_size);
}
private:
mutable fs::SubStorage m_node_storage;
mutable fs::SubStorage m_entry_storage;
NodeBuffer m_node_l1;
size_t m_node_size;
size_t m_entry_size;
s32 m_entry_count;
s32 m_offset_count;
s32 m_entry_set_count;
OffsetCache m_offset_cache;
public:
BucketTree() : m_node_storage(), m_entry_storage(), m_node_l1(), m_node_size(), m_entry_size(), m_entry_count(), m_offset_count(), m_entry_set_count(), m_offset_cache() { /* ... */ }
~BucketTree() { this->Finalize(); }
Result Initialize(IAllocator *allocator, fs::SubStorage node_storage, fs::SubStorage entry_storage, size_t node_size, size_t entry_size, s32 entry_count);
void Initialize(size_t node_size, s64 end_offset);
void Finalize();
bool IsInitialized() const { return m_node_size > 0; }
bool IsEmpty() const { return m_entry_size == 0; }
Result Find(Visitor *visitor, s64 virtual_address);
Result InvalidateCache();
s32 GetEntryCount() const { return m_entry_count; }
IAllocator *GetAllocator() const { return m_node_l1.GetAllocator(); }
Result GetOffsets(Offsets *out) {
/* Ensure we have an offset cache. */
R_TRY(this->EnsureOffsetCache());
/* Set the output. */
*out = m_offset_cache.offsets;
R_SUCCEED();
}
private:
template<typename EntryType>
struct ContinuousReadingParam {
s64 offset;
size_t size;
NodeHeader entry_set;
s32 entry_index;
Offsets offsets;
EntryType entry;
};
private:
template<typename EntryType>
Result ScanContinuousReading(ContinuousReadingInfo *out_info, const ContinuousReadingParam<EntryType> &param) const;
bool IsExistL2() const { return m_offset_count < m_entry_set_count; }
bool IsExistOffsetL2OnL1() const { return this->IsExistL2() && m_node_l1->count < m_offset_count; }
s64 GetEntrySetIndex(s32 node_index, s32 offset_index) const {
return (m_offset_count - m_node_l1->count) + (m_offset_count * node_index) + offset_index;
}
Result EnsureOffsetCache();
};
/* ACCURATE_TO_VERSION: Unknown */
class BucketTree::Visitor {
NON_COPYABLE(Visitor);
NON_MOVEABLE(Visitor);
private:
friend class BucketTree;
union EntrySetHeader {
NodeHeader header;
struct Info {
s32 index;
s32 count;
s64 end;
s64 start;
} info;
static_assert(util::is_pod<Info>::value);
};
static_assert(util::is_pod<EntrySetHeader>::value);
private:
const BucketTree *m_tree;
BucketTree::Offsets m_offsets;
void *m_entry;
s32 m_entry_index;
s32 m_entry_set_count;
EntrySetHeader m_entry_set;
public:
constexpr Visitor() : m_tree(), m_entry(), m_entry_index(-1), m_entry_set_count(), m_entry_set{} { /* ... */ }
~Visitor() {
if (m_entry != nullptr) {
m_tree->GetAllocator()->Deallocate(m_entry, m_tree->m_entry_size);
m_tree = nullptr;
m_entry = nullptr;
}
}
bool IsValid() const { return m_entry_index >= 0; }
bool CanMoveNext() const { return this->IsValid() && (m_entry_index + 1 < m_entry_set.info.count || m_entry_set.info.index + 1 < m_entry_set_count); }
bool CanMovePrevious() const { return this->IsValid() && (m_entry_index > 0 || m_entry_set.info.index > 0); }
Result MoveNext();
Result MovePrevious();
template<typename EntryType>
Result ScanContinuousReading(ContinuousReadingInfo *out_info, s64 offset, size_t size) const;
const void *Get() const { AMS_ASSERT(this->IsValid()); return m_entry; }
template<typename T>
const T *Get() const { AMS_ASSERT(this->IsValid()); return reinterpret_cast<const T *>(m_entry); }
const BucketTree *GetTree() const { return m_tree; }
private:
Result Initialize(const BucketTree *tree, const BucketTree::Offsets &offsets);
Result Find(s64 virtual_address);
Result FindEntrySet(s32 *out_index, s64 virtual_address, s32 node_index);
Result FindEntrySetWithBuffer(s32 *out_index, s64 virtual_address, s32 node_index, char *buffer);
Result FindEntrySetWithoutBuffer(s32 *out_index, s64 virtual_address, s32 node_index);
Result FindEntry(s64 virtual_address, s32 entry_set_index);
Result FindEntryWithBuffer(s64 virtual_address, s32 entry_set_index, char *buffer);
Result FindEntryWithoutBuffer(s64 virtual_address, s32 entry_set_index);
};
}

View File

@@ -1,172 +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 <stratosphere/fssystem/fssystem_pooled_buffer.hpp>
#include <stratosphere/fssystem/fssystem_bucket_tree.hpp>
#include <stratosphere/fssystem/fssystem_bucket_tree_utils.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: 13.4.0.0 */
template<typename EntryType>
Result BucketTree::ScanContinuousReading(ContinuousReadingInfo *out_info, const ContinuousReadingParam<EntryType> &param) const {
static_assert(util::is_pod<ContinuousReadingParam<EntryType>>::value);
/* Validate our preconditions. */
AMS_ASSERT(this->IsInitialized());
AMS_ASSERT(out_info != nullptr);
AMS_ASSERT(m_entry_size == sizeof(EntryType));
/* Reset the output. */
out_info->Reset();
/* If there's nothing to read, we're done. */
R_SUCCEED_IF(param.size == 0);
/* If we're reading a fragment, we're done. */
R_SUCCEED_IF(param.entry.IsFragment());
/* Validate the first entry. */
auto entry = param.entry;
auto cur_offset = param.offset;
R_UNLESS(entry.GetVirtualOffset() <= cur_offset, fs::ResultOutOfRange());
/* Create a pooled buffer for our scan. */
PooledBuffer pool(m_node_size, 1);
char *buffer = nullptr;
s64 entry_storage_size;
R_TRY(m_entry_storage.GetSize(std::addressof(entry_storage_size)));
/* Read the node. */
if (m_node_size <= pool.GetSize()) {
buffer = pool.GetBuffer();
const auto ofs = param.entry_set.index * static_cast<s64>(m_node_size);
R_UNLESS(m_node_size + ofs <= static_cast<size_t>(entry_storage_size), fs::ResultInvalidBucketTreeNodeEntryCount());
R_TRY(m_entry_storage.Read(ofs, buffer, m_node_size));
}
/* Calculate extents. */
const auto end_offset = cur_offset + static_cast<s64>(param.size);
s64 phys_offset = entry.GetPhysicalOffset();
/* Start merge tracking. */
s64 merge_size = 0;
s64 readable_size = 0;
bool merged = false;
/* Iterate. */
auto entry_index = param.entry_index;
for (const auto entry_count = param.entry_set.count; entry_index < entry_count; ++entry_index) {
/* If we're past the end, we're done. */
if (end_offset <= cur_offset) {
break;
}
/* Validate the entry offset. */
const auto entry_offset = entry.GetVirtualOffset();
R_UNLESS(entry_offset <= cur_offset, fs::ResultInvalidIndirectEntryOffset());
/* Get the next entry. */
EntryType next_entry = {};
s64 next_entry_offset;
if (entry_index + 1 < entry_count) {
if (buffer != nullptr) {
const auto ofs = impl::GetBucketTreeEntryOffset(0, m_entry_size, entry_index + 1);
std::memcpy(std::addressof(next_entry), buffer + ofs, m_entry_size);
} else {
const auto ofs = impl::GetBucketTreeEntryOffset(param.entry_set.index, m_node_size, m_entry_size, entry_index + 1);
R_TRY(m_entry_storage.Read(ofs, std::addressof(next_entry), m_entry_size));
}
next_entry_offset = next_entry.GetVirtualOffset();
R_UNLESS(param.offsets.IsInclude(next_entry_offset), fs::ResultInvalidIndirectEntryOffset());
} else {
next_entry_offset = param.entry_set.offset;
}
/* Validate the next entry offset. */
R_UNLESS(cur_offset < next_entry_offset, fs::ResultInvalidIndirectEntryOffset());
/* Determine the much data there is. */
const auto data_size = next_entry_offset - cur_offset;
AMS_ASSERT(data_size > 0);
/* Determine how much data we should read. */
const auto remaining_size = end_offset - cur_offset;
const size_t read_size = static_cast<size_t>(std::min(data_size, remaining_size));
AMS_ASSERT(read_size <= param.size);
/* Update our merge tracking. */
if (entry.IsFragment()) {
/* If we can't merge, stop looping. */
if (EntryType::FragmentSizeMax <= read_size || remaining_size <= data_size) {
break;
}
/* Otherwise, add the current size to the merge size. */
merge_size += read_size;
} else {
/* If we can't merge, stop looping. */
if (phys_offset != entry.GetPhysicalOffset()) {
break;
}
/* Add the size to the readable amount. */
readable_size += merge_size + read_size;
AMS_ASSERT(readable_size <= static_cast<s64>(param.size));
/* Update whether we've merged. */
merged |= merge_size > 0;
merge_size = 0;
}
/* Advance. */
cur_offset += read_size;
AMS_ASSERT(cur_offset <= end_offset);
phys_offset += next_entry_offset - entry_offset;
entry = next_entry;
}
/* If we merged, set our readable size. */
if (merged) {
out_info->SetReadSize(static_cast<size_t>(readable_size));
}
out_info->SetSkipCount(entry_index - param.entry_index);
R_SUCCEED();
}
template<typename EntryType>
Result BucketTree::Visitor::ScanContinuousReading(ContinuousReadingInfo *out_info, s64 offset, size_t size) const {
static_assert(util::is_pod<EntryType>::value);
AMS_ASSERT(this->IsValid());
/* Create our parameters. */
ContinuousReadingParam<EntryType> param = {
offset, size, m_entry_set.header, m_entry_index
};
std::memcpy(std::addressof(param.offsets), std::addressof(m_offsets), sizeof(BucketTree::Offsets));
std::memcpy(std::addressof(param.entry), m_entry, sizeof(EntryType));
/* Scan. */
R_RETURN(m_tree->ScanContinuousReading<EntryType>(out_info, param));
}
}

View File

@@ -1,87 +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 <stratosphere/fssystem/fssystem_bucket_tree.hpp>
namespace ams::fssystem::impl {
/* ACCURATE_TO_VERSION: Unknown */
class SafeValue {
public:
static ALWAYS_INLINE s64 GetInt64(const void *ptr) {
s64 value;
std::memcpy(std::addressof(value), ptr, sizeof(s64));
return value;
}
static ALWAYS_INLINE s64 GetInt64(const s64 *ptr) {
return GetInt64(static_cast<const void *>(ptr));
}
static ALWAYS_INLINE s64 GetInt64(const s64 &v) {
return GetInt64(std::addressof(v));
}
static ALWAYS_INLINE void SetInt64(void *dst, const void *src) {
std::memcpy(dst, src, sizeof(s64));
}
static ALWAYS_INLINE void SetInt64(void *dst, const s64 *src) {
return SetInt64(dst, static_cast<const void *>(src));
}
static ALWAYS_INLINE void SetInt64(void *dst, const s64 &v) {
return SetInt64(dst, std::addressof(v));
}
};
/* ACCURATE_TO_VERSION: Unknown */
template<typename IteratorType>
struct BucketTreeNode {
using Header = BucketTree::NodeHeader;
Header header;
s32 GetCount() const { return this->header.count; }
void *GetArray() { return std::addressof(this->header) + 1; }
template<typename T> T *GetArray() { return reinterpret_cast<T *>(this->GetArray()); }
const void *GetArray() const { return std::addressof(this->header) + 1; }
template<typename T> const T *GetArray() const { return reinterpret_cast<const T *>(this->GetArray()); }
s64 GetBeginOffset() const { return *this->GetArray<s64>(); }
s64 GetEndOffset() const { return this->header.offset; }
IteratorType GetBegin() { return IteratorType(this->GetArray<s64>()); }
IteratorType GetEnd() { return IteratorType(this->GetArray<s64>()) + this->header.count; }
IteratorType GetBegin() const { return IteratorType(this->GetArray<s64>()); }
IteratorType GetEnd() const { return IteratorType(this->GetArray<s64>()) + this->header.count; }
IteratorType GetBegin(size_t entry_size) { return IteratorType(this->GetArray(), entry_size); }
IteratorType GetEnd(size_t entry_size) { return IteratorType(this->GetArray(), entry_size) + this->header.count; }
IteratorType GetBegin(size_t entry_size) const { return IteratorType(this->GetArray(), entry_size); }
IteratorType GetEnd(size_t entry_size) const { return IteratorType(this->GetArray(), entry_size) + this->header.count; }
};
constexpr inline s64 GetBucketTreeEntryOffset(s64 entry_set_offset, size_t entry_size, s32 entry_index) {
return entry_set_offset + sizeof(BucketTree::NodeHeader) + entry_index * static_cast<s64>(entry_size);
}
constexpr inline s64 GetBucketTreeEntryOffset(s32 entry_set_index, size_t node_size, size_t entry_size, s32 entry_index) {
return GetBucketTreeEntryOffset(entry_set_index * static_cast<s64>(node_size), entry_size, entry_index);
}
}

View File

@@ -1,81 +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>
#include <stratosphere/os.hpp>
#include <stratosphere/fs/fs_istorage.hpp>
#include <stratosphere/fs/fs_substorage.hpp>
#include <stratosphere/fs/fs_i_buffer_manager.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: Unknown */
class BufferedStorage : public ::ams::fs::IStorage {
NON_COPYABLE(BufferedStorage);
NON_MOVEABLE(BufferedStorage);
private:
class Cache;
class UniqueCache;
class SharedCache;
private:
fs::SubStorage m_base_storage;
fs::IBufferManager *m_buffer_manager;
size_t m_block_size;
s64 m_base_storage_size;
std::unique_ptr<Cache[]> m_caches;
s32 m_cache_count;
Cache *m_next_acquire_cache;
Cache *m_next_fetch_cache;
os::SdkMutex m_mutex;
bool m_bulk_read_enabled;
public:
BufferedStorage();
virtual ~BufferedStorage();
Result Initialize(fs::SubStorage base_storage, fs::IBufferManager *buffer_manager, size_t block_size, s32 buffer_count);
void Finalize();
bool IsInitialized() const { return m_caches != nullptr; }
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 GetSize(s64 *out) override;
virtual Result SetSize(s64 size) override;
virtual Result Flush() override;
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override;
using IStorage::OperateRange;
void InvalidateCaches();
fs::IBufferManager *GetBufferManager() const { return m_buffer_manager; }
void EnableBulkRead() { m_bulk_read_enabled = true; }
private:
Result PrepareAllocation();
Result ControlDirtiness();
Result ReadCore(s64 offset, void *buffer, size_t size);
bool ReadHeadCache(s64 *offset, void *buffer, size_t *size, s64 *buffer_offset);
bool ReadTailCache(s64 offset, void *buffer, size_t *size, s64 buffer_offset);
Result BulkRead(s64 offset, void *buffer, size_t size, bool head_cache_needed, bool tail_cache_needed);
Result WriteCore(s64 offset, const void *buffer, size_t size);
};
}

View File

@@ -1,55 +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 {
/* ACCURATE_TO_VERSION: 13.4.0.0 */
enum CompressionType : u8 {
CompressionType_None = 0,
CompressionType_Zeros = 1,
CompressionType_2 = 2,
CompressionType_Lz4 = 3,
CompressionType_Unknown = 4,
};
using DecompressorFunction = Result (*)(void *, size_t, const void *, size_t);
using GetDecompressorFunction = DecompressorFunction (*)(CompressionType);
constexpr s64 CompressionBlockAlignment = 0x10;
namespace CompressionTypeUtility {
constexpr bool IsBlockAlignmentRequired(CompressionType type) {
return type != CompressionType_None && type != CompressionType_Zeros;
}
constexpr bool IsDataStorageAccessRequired(CompressionType type) {
return type != CompressionType_Zeros;
}
constexpr bool IsRandomAccessible(CompressionType type) {
return type == CompressionType_None;
}
constexpr bool IsUnknownType(CompressionType type) {
return type >= CompressionType_Unknown;
}
}
}

View File

@@ -1,24 +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>
#include <stratosphere/fssystem/fssystem_nca_file_system_driver.hpp>
namespace ams::fssystem {
const ::ams::fssystem::NcaCompressionConfiguration *GetNcaCompressionConfiguration();
}

View File

@@ -1,38 +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>
#include <stratosphere/fssystem/fssystem_nca_file_system_driver.hpp>
#include <stratosphere/ncm/ncm_content_meta_platform.hpp>
namespace ams::fssystem {
const ::ams::fssystem::NcaCryptoConfiguration *GetNcaCryptoConfiguration(bool prod);
void SetUpKekAccessKeys(bool prod);
void InvalidateHardwareAesKey();
bool IsValidSignatureKeyGeneration(ncm::ContentMetaPlatform platform, size_t key_generation);
const u8 *GetAcidSignatureKeyModulus(ncm::ContentMetaPlatform platform, bool prod, size_t key_generation, bool unk_unused);
size_t GetAcidSignatureKeyModulusSize(ncm::ContentMetaPlatform platform, bool unk_unused);
const u8 *GetAcidSignatureKeyPublicExponent();
constexpr inline size_t AcidSignatureKeyPublicExponentSize = NcaCryptoConfiguration::Rsa2048KeyPublicExponentSize;
}

View File

@@ -1,179 +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 <stratosphere/fs/fs_common.hpp>
#include <stratosphere/fs/fsa/fs_ifile.hpp>
#include <stratosphere/fs/fsa/fs_idirectory.hpp>
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
#include <stratosphere/fs/impl/fs_newable.hpp>
namespace ams::fssystem {
class DirectoryRedirectionFileSystem : public fs::fsa::IFileSystem, public fs::impl::Newable {
NON_COPYABLE(DirectoryRedirectionFileSystem);
private:
std::unique_ptr<fs::fsa::IFileSystem> m_base_fs;
fs::Path m_before_dir;
fs::Path m_after_dir;
public:
DirectoryRedirectionFileSystem(std::unique_ptr<fs::fsa::IFileSystem> fs) : m_base_fs(std::move(fs)), m_before_dir(), m_after_dir() {
/* ... */
}
Result InitializeWithFixedPath(const char *before, const char *after) {
R_TRY(fs::SetUpFixedPath(std::addressof(m_before_dir), before));
R_TRY(fs::SetUpFixedPath(std::addressof(m_after_dir), after));
R_SUCCEED();
}
private:
Result ResolveFullPath(fs::Path *out, const fs::Path &path) {
if (path.IsMatchHead(m_before_dir.GetString(), m_before_dir.GetLength())) {
R_TRY(out->Initialize(m_after_dir));
R_TRY(out->AppendChild(path.GetString() + m_before_dir.GetLength()));
} else {
R_TRY(out->Initialize(path));
}
R_SUCCEED();
}
public:
virtual Result DoCreateFile(const fs::Path &path, s64 size, int option) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->CreateFile(full_path, size, option));
}
virtual Result DoDeleteFile(const fs::Path &path) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->DeleteFile(full_path));
}
virtual Result DoCreateDirectory(const fs::Path &path) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->CreateDirectory(full_path));
}
virtual Result DoDeleteDirectory(const fs::Path &path) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->DeleteDirectory(full_path));
}
virtual Result DoDeleteDirectoryRecursively(const fs::Path &path) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->DeleteDirectoryRecursively(full_path));
}
virtual Result DoRenameFile(const fs::Path &old_path, const fs::Path &new_path) override {
fs::Path old_full_path;
fs::Path new_full_path;
R_TRY(this->ResolveFullPath(std::addressof(old_full_path), old_path));
R_TRY(this->ResolveFullPath(std::addressof(new_full_path), new_path));
R_RETURN(m_base_fs->RenameFile(old_full_path, new_full_path));
}
virtual Result DoRenameDirectory(const fs::Path &old_path, const fs::Path &new_path) override {
fs::Path old_full_path;
fs::Path new_full_path;
R_TRY(this->ResolveFullPath(std::addressof(old_full_path), old_path));
R_TRY(this->ResolveFullPath(std::addressof(new_full_path), new_path));
R_RETURN(m_base_fs->RenameDirectory(old_full_path, new_full_path));
}
virtual Result DoGetEntryType(fs::DirectoryEntryType *out, const fs::Path &path) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->GetEntryType(out, full_path));
}
virtual Result DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const fs::Path &path, fs::OpenMode mode) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->OpenFile(out_file, full_path, mode));
}
virtual Result DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const fs::Path &path, fs::OpenDirectoryMode mode) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->OpenDirectory(out_dir, full_path, mode));
}
virtual Result DoCommit() override {
R_RETURN(m_base_fs->Commit());
}
virtual Result DoGetFreeSpaceSize(s64 *out, const fs::Path &path) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->GetFreeSpaceSize(out, full_path));
}
virtual Result DoGetTotalSpaceSize(s64 *out, const fs::Path &path) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->GetTotalSpaceSize(out, full_path));
}
virtual Result DoCleanDirectoryRecursively(const fs::Path &path) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->CleanDirectoryRecursively(full_path));
}
virtual Result DoGetFileTimeStampRaw(fs::FileTimeStampRaw *out, const fs::Path &path) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->GetFileTimeStampRaw(out, full_path));
}
virtual Result DoQueryEntry(char *dst, size_t dst_size, const char *src, size_t src_size, fs::fsa::QueryId query, const fs::Path &path) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->QueryEntry(dst, dst_size, src, src_size, query, full_path));
}
/* These aren't accessible as commands. */
virtual Result DoCommitProvisionally(s64 counter) override {
R_RETURN(m_base_fs->CommitProvisionally(counter));
}
virtual Result DoRollback() override {
R_RETURN(m_base_fs->Rollback());
}
virtual Result DoFlush() override {
R_RETURN(m_base_fs->Flush());
}
};
}

View File

@@ -1,70 +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 <stratosphere/fs/fs_common.hpp>
#include <stratosphere/fs/fsa/fs_ifile.hpp>
#include <stratosphere/fs/fsa/fs_idirectory.hpp>
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
#include <stratosphere/fs/impl/fs_newable.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: 13.4.0.0 */
class DirectorySaveDataFileSystem : public fs::fsa::IFileSystem, public fs::impl::Newable {
NON_COPYABLE(DirectorySaveDataFileSystem);
private:
std::unique_ptr<fs::fsa::IFileSystem> m_unique_fs;
fs::fsa::IFileSystem * const m_base_fs;
os::SdkMutex m_accessor_mutex = {};
s32 m_open_writable_files = 0;
bool m_is_journaling_supported = false;
bool m_is_multi_commit_supported = false;
bool m_is_journaling_enabled = false;
/* Extension member to ensure proper savedata locking. */
std::unique_ptr<fs::fsa::IFile> m_lock_file = nullptr;
public:
DirectorySaveDataFileSystem(std::unique_ptr<fs::fsa::IFileSystem> fs) : m_unique_fs(std::move(fs)), m_base_fs(m_unique_fs.get()) { /* ... */ }
DirectorySaveDataFileSystem(fs::fsa::IFileSystem *fs) : m_unique_fs(), m_base_fs(fs) { /* ... */ }
Result Initialize(bool journaling_supported, bool multi_commit_enabled, bool journaling_enabled);
private:
Result SynchronizeDirectory(const fs::Path &dst, const fs::Path &src);
Result ResolvePath(fs::Path *out, const fs::Path &path);
Result AcquireLockFile();
public:
void DecrementWriteOpenFileCount();
public:
virtual Result DoCreateFile(const fs::Path &path, s64 size, int option) override;
virtual Result DoDeleteFile(const fs::Path &path) override;
virtual Result DoCreateDirectory(const fs::Path &path) override;
virtual Result DoDeleteDirectory(const fs::Path &path) override;
virtual Result DoDeleteDirectoryRecursively(const fs::Path &path) override;
virtual Result DoRenameFile(const fs::Path &old_path, const fs::Path &new_path) override;
virtual Result DoRenameDirectory(const fs::Path &old_path, const fs::Path &new_path) override;
virtual Result DoGetEntryType(fs::DirectoryEntryType *out, const fs::Path &path) override;
virtual Result DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const fs::Path &path, fs::OpenMode mode) override;
virtual Result DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const fs::Path &path, fs::OpenDirectoryMode mode) override;
virtual Result DoCommit() override;
virtual Result DoGetFreeSpaceSize(s64 *out, const fs::Path &path) override;
virtual Result DoGetTotalSpaceSize(s64 *out, const fs::Path &path) override;
virtual Result DoCleanDirectoryRecursively(const fs::Path &path) override;
/* These aren't accessible as commands. */
virtual Result DoCommitProvisionally(s64 counter) override;
virtual Result DoRollback() override;
};
}

View File

@@ -1,28 +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>
#include <stratosphere/ncm/ncm_ids.hpp>
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
namespace ams::fssystem {
fs::fsa::IFileSystem *GetExternalCodeFileSystem(ncm::ProgramId program_id);
Result CreateExternalCode(os::NativeHandle *out, ncm::ProgramId program_id);
void DestroyExternalCode(ncm::ProgramId program_id);
}

View File

@@ -1,35 +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::fssrv::fscreator {
struct FileSystemCreatorInterfaces;
}
namespace ams::fssystem {
/* TODO: This is kind of really a fs process function/tied into fs main. */
/* This should be re-examined when FS is reimplemented. */
void InitializeForFileSystemProxy();
void InitializeForAtmosphereMitm();
const ::ams::fssrv::fscreator::FileSystemCreatorInterfaces *GetFileSystemCreatorInterfaces();
}

View File

@@ -1,178 +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 <stratosphere/fs/fs_common.hpp>
#include <stratosphere/fs/impl/fs_newable.hpp>
#include <stratosphere/fs/fsa/fs_ifile.hpp>
#include <stratosphere/fs/fsa/fs_idirectory.hpp>
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
#include <stratosphere/fs/fs_query_range.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: 13.4.0.0 */
class ForwardingFile final : public ::ams::fs::fsa::IFile, public ::ams::fs::impl::Newable {
NON_COPYABLE(ForwardingFile);
NON_MOVEABLE(ForwardingFile);
private:
std::unique_ptr<fs::fsa::IFile> m_base_file;
public:
ForwardingFile(std::unique_ptr<fs::fsa::IFile> f) : m_base_file(std::move(f)) { /* ... */ }
virtual ~ForwardingFile() { /* ... */ }
public:
virtual Result DoRead(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final {
R_RETURN(m_base_file->Read(out, offset, buffer, size, option));
}
virtual Result DoGetSize(s64 *out) override final {
R_RETURN(m_base_file->GetSize(out));
}
virtual Result DoFlush() override final {
R_RETURN(m_base_file->Flush());
}
virtual Result DoWrite(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final {
R_RETURN(m_base_file->Write(offset, buffer, size, option));
}
virtual Result DoSetSize(s64 size) override final {
R_RETURN(m_base_file->SetSize(size));
}
virtual Result DoOperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final {
R_RETURN(m_base_file->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
}
public:
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override final {
return m_base_file->GetDomainObjectId();
}
};
/* ACCURATE_TO_VERSION: 13.4.0.0 */
class ForwardingDirectory final : public ::ams::fs::fsa::IDirectory, public ::ams::fs::impl::Newable {
NON_COPYABLE(ForwardingDirectory);
NON_MOVEABLE(ForwardingDirectory);
private:
std::unique_ptr<fs::fsa::IDirectory> m_base_dir;
public:
ForwardingDirectory(std::unique_ptr<fs::fsa::IDirectory> d) : m_base_dir(std::move(d)) { /* ... */ }
virtual ~ForwardingDirectory() { /* ... */ }
public:
virtual Result DoRead(s64 *out_count, fs::DirectoryEntry *out_entries, s64 max_entries) override final {
R_RETURN(m_base_dir->Read(out_count, out_entries, max_entries));
}
virtual Result DoGetEntryCount(s64 *out) override final {
R_RETURN(m_base_dir->GetEntryCount(out));
}
public:
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override final {
return m_base_dir->GetDomainObjectId();
}
};
/* ACCURATE_TO_VERSION: 13.4.0.0 */
class ForwardingFileSystem final : public ::ams::fs::fsa::IFileSystem, public ::ams::fs::impl::Newable {
NON_COPYABLE(ForwardingFileSystem);
NON_MOVEABLE(ForwardingFileSystem);
private:
std::shared_ptr<fs::fsa::IFileSystem> m_base_fs;
public:
ForwardingFileSystem(std::shared_ptr<fs::fsa::IFileSystem> fs) : m_base_fs(std::move(fs)) { /* ... */ }
virtual ~ForwardingFileSystem() { /* ... */ }
public:
virtual Result DoCreateFile(const fs::Path &path, s64 size, int flags) override final {
R_RETURN(m_base_fs->CreateFile(path, size, flags));
}
virtual Result DoDeleteFile(const fs::Path &path) override final {
R_RETURN(m_base_fs->DeleteFile(path));
}
virtual Result DoCreateDirectory(const fs::Path &path) override final {
R_RETURN(m_base_fs->CreateDirectory(path));
}
virtual Result DoDeleteDirectory(const fs::Path &path) override final {
R_RETURN(m_base_fs->DeleteDirectory(path));
}
virtual Result DoDeleteDirectoryRecursively(const fs::Path &path) override final {
R_RETURN(m_base_fs->DeleteDirectoryRecursively(path));
}
virtual Result DoRenameFile(const fs::Path &old_path, const fs::Path &new_path) override final {
R_RETURN(m_base_fs->RenameFile(old_path, new_path));
}
virtual Result DoRenameDirectory(const fs::Path &old_path, const fs::Path &new_path) override final {
R_RETURN(m_base_fs->RenameDirectory(old_path, new_path));
}
virtual Result DoGetEntryType(fs::DirectoryEntryType *out, const fs::Path &path) override final {
R_RETURN(m_base_fs->GetEntryType(out, path));
}
virtual Result DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const fs::Path &path, fs::OpenMode mode) override final {
R_RETURN(m_base_fs->OpenFile(out_file, path, mode));
}
virtual Result DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const fs::Path &path, fs::OpenDirectoryMode mode) override final {
R_RETURN(m_base_fs->OpenDirectory(out_dir, path, mode));
}
virtual Result DoCommit() override final {
R_RETURN(m_base_fs->Commit());
}
virtual Result DoGetFreeSpaceSize(s64 *out, const fs::Path &path) override final {
R_RETURN(m_base_fs->GetFreeSpaceSize(out, path));
}
virtual Result DoGetTotalSpaceSize(s64 *out, const fs::Path &path) override final {
R_RETURN(m_base_fs->GetTotalSpaceSize(out, path));
}
virtual Result DoCleanDirectoryRecursively(const fs::Path &path) override final {
R_RETURN(m_base_fs->CleanDirectoryRecursively(path));
}
virtual Result DoGetFileTimeStampRaw(fs::FileTimeStampRaw *out, const fs::Path &path) override final {
R_RETURN(m_base_fs->GetFileTimeStampRaw(out, path));
}
virtual Result DoQueryEntry(char *dst, size_t dst_size, const char *src, size_t src_size, fs::fsa::QueryId query, const fs::Path &path) override final {
R_RETURN(m_base_fs->QueryEntry(dst, dst_size, src, src_size, query, path));
}
virtual Result DoCommitProvisionally(s64 counter) override final {
R_RETURN(m_base_fs->CommitProvisionally(counter));
}
virtual Result DoRollback() override final {
R_RETURN(m_base_fs->Rollback());
}
virtual Result DoFlush() override final {
R_RETURN(m_base_fs->Flush());
}
};
}

View File

@@ -1,211 +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>
#include <stratosphere/os.hpp>
#include <stratosphere/fs/fs_istorage.hpp>
#include <stratosphere/fs/fs_substorage.hpp>
#include <stratosphere/fs/fs_storage_type.hpp>
#include <stratosphere/fssystem/fssystem_integrity_verification_storage.hpp>
#include <stratosphere/fssystem/fssystem_block_cache_buffered_storage.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: 14.3.0.0 */
struct HierarchicalIntegrityVerificationLevelInformation {
fs::Int64 offset;
fs::Int64 size;
s32 block_order;
u8 reserved[4];
};
static_assert(util::is_pod<HierarchicalIntegrityVerificationLevelInformation>::value);
static_assert(sizeof(HierarchicalIntegrityVerificationLevelInformation) == 0x18);
static_assert(alignof(HierarchicalIntegrityVerificationLevelInformation) == 0x4);
struct HierarchicalIntegrityVerificationInformation {
u32 max_layers;
HierarchicalIntegrityVerificationLevelInformation info[IntegrityMaxLayerCount - 1];
fs::HashSalt seed;
s64 GetLayeredHashSize() const {
return this->info[this->max_layers - 2].offset;
}
s64 GetDataOffset() const {
return this->info[this->max_layers - 2].offset;
}
s64 GetDataSize() const {
return this->info[this->max_layers - 2].size;
}
};
static_assert(util::is_pod<HierarchicalIntegrityVerificationInformation>::value);
struct HierarchicalIntegrityVerificationMetaInformation {
u32 magic;
u32 version;
u32 master_hash_size;
HierarchicalIntegrityVerificationInformation level_hash_info;
/* TODO: Format */
};
static_assert(util::is_pod<HierarchicalIntegrityVerificationMetaInformation>::value);
struct HierarchicalIntegrityVerificationSizeSet {
s64 control_size;
s64 master_hash_size;
s64 layered_hash_sizes[IntegrityMaxLayerCount - 2];
};
static_assert(util::is_pod<HierarchicalIntegrityVerificationSizeSet>::value);
class HierarchicalIntegrityVerificationStorageControlArea {
NON_COPYABLE(HierarchicalIntegrityVerificationStorageControlArea);
NON_MOVEABLE(HierarchicalIntegrityVerificationStorageControlArea);
public:
static constexpr size_t HashSize = crypto::Sha256Generator::HashSize;
struct InputParam {
size_t level_block_size[IntegrityMaxLayerCount - 1];
};
static_assert(util::is_pod<InputParam>::value);
private:
fs::SubStorage m_storage;
HierarchicalIntegrityVerificationMetaInformation m_meta;
public:
static Result QuerySize(HierarchicalIntegrityVerificationSizeSet *out, const InputParam &input_param, s32 layer_count, s64 data_size);
/* TODO Format */
static Result Expand(fs::SubStorage meta_storage, const HierarchicalIntegrityVerificationMetaInformation &meta);
public:
HierarchicalIntegrityVerificationStorageControlArea() { /* ... */ }
Result Initialize(fs::SubStorage meta_storage);
void Finalize();
u32 GetMasterHashSize() const { return m_meta.master_hash_size; }
void GetLevelHashInfo(HierarchicalIntegrityVerificationInformation *out) {
AMS_ASSERT(out != nullptr);
*out = m_meta.level_hash_info;
}
};
class HierarchicalIntegrityVerificationStorage : public ::ams::fs::IStorage {
NON_COPYABLE(HierarchicalIntegrityVerificationStorage);
NON_MOVEABLE(HierarchicalIntegrityVerificationStorage);
private:
friend struct HierarchicalIntegrityVerificationMetaInformation;
protected:
static constexpr s64 HashSize = crypto::Sha256Generator::HashSize;
static constexpr size_t MaxLayers = IntegrityMaxLayerCount;
public:
using GenerateRandomFunction = void (*)(void *dst, size_t size);
class HierarchicalStorageInformation {
public:
enum {
MasterStorage = 0,
Layer1Storage = 1,
Layer2Storage = 2,
Layer3Storage = 3,
Layer4Storage = 4,
Layer5Storage = 5,
DataStorage = 6,
};
private:
fs::SubStorage m_storages[DataStorage + 1];
public:
void SetMasterHashStorage(fs::SubStorage s) { m_storages[MasterStorage] = s; }
void SetLayer1HashStorage(fs::SubStorage s) { m_storages[Layer1Storage] = s; }
void SetLayer2HashStorage(fs::SubStorage s) { m_storages[Layer2Storage] = s; }
void SetLayer3HashStorage(fs::SubStorage s) { m_storages[Layer3Storage] = s; }
void SetLayer4HashStorage(fs::SubStorage s) { m_storages[Layer4Storage] = s; }
void SetLayer5HashStorage(fs::SubStorage s) { m_storages[Layer5Storage] = s; }
void SetDataStorage(fs::SubStorage s) { m_storages[DataStorage] = s; }
fs::SubStorage &operator[](s32 index) {
AMS_ASSERT(MasterStorage <= index && index <= DataStorage);
return m_storages[index];
}
};
private:
static GenerateRandomFunction s_generate_random;
static void SetGenerateRandomFunction(GenerateRandomFunction func) {
s_generate_random = func;
}
private:
FileSystemBufferManagerSet *m_buffers;
os::SdkRecursiveMutex *m_mutex;
IntegrityVerificationStorage m_verify_storages[MaxLayers - 1];
BlockCacheBufferedStorage m_buffer_storages[MaxLayers - 1];
os::Semaphore *m_read_semaphore;
os::Semaphore *m_write_semaphore;
s64 m_data_size;
s32 m_max_layers;
public:
HierarchicalIntegrityVerificationStorage() : m_buffers(nullptr), m_mutex(nullptr), m_data_size(-1) { /* ... */ }
virtual ~HierarchicalIntegrityVerificationStorage() override { this->Finalize(); }
Result Initialize(const HierarchicalIntegrityVerificationInformation &info, HierarchicalStorageInformation storage, FileSystemBufferManagerSet *bufs, IHash256GeneratorFactory *hgf, bool hash_salt_enabled, os::SdkRecursiveMutex *mtx, os::Semaphore *read_sema, os::Semaphore *write_sema, int max_data_cache_entries, int max_hash_cache_entries, s8 buffer_level, bool is_writable, bool allow_cleared_blocks);
Result Initialize(const HierarchicalIntegrityVerificationInformation &info, HierarchicalStorageInformation storage, FileSystemBufferManagerSet *bufs, IHash256GeneratorFactory *hgf, bool hash_salt_enabled, os::SdkRecursiveMutex *mtx, int max_data_cache_entries, int max_hash_cache_entries, s8 buffer_level, bool is_writable, bool allow_cleared_blocks) {
R_RETURN(this->Initialize(info, storage, bufs, hgf, hash_salt_enabled, mtx, nullptr, nullptr, max_data_cache_entries, max_hash_cache_entries, buffer_level, is_writable, allow_cleared_blocks));
}
void Finalize();
virtual Result Read(s64 offset, void *buffer, size_t size) override;
virtual Result Write(s64 offset, const void *buffer, size_t size) override;
virtual Result SetSize(s64 size) override { AMS_UNUSED(size); R_THROW(fs::ResultUnsupportedSetSizeForHierarchicalIntegrityVerificationStorage()); }
virtual Result GetSize(s64 *out) override;
virtual Result Flush() override;
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override;
using IStorage::OperateRange;
Result Commit();
Result OnRollback();
bool IsInitialized() const {
return m_data_size >= 0;
}
FileSystemBufferManagerSet *GetBuffers() {
return m_buffers;
}
void GetParameters(HierarchicalIntegrityVerificationStorageControlArea::InputParam *out) const {
AMS_ASSERT(out != nullptr);
for (auto level = 0; level <= m_max_layers - 2; ++level) {
out->level_block_size[level] = static_cast<size_t>(m_verify_storages[level].GetBlockSize());
}
}
s64 GetL1HashVerificationBlockSize() const {
return m_verify_storages[m_max_layers - 2].GetBlockSize();
}
fs::SubStorage GetL1HashStorage() {
return fs::SubStorage(std::addressof(m_buffer_storages[m_max_layers - 3]), 0, util::DivideUp(m_data_size, this->GetL1HashVerificationBlockSize()));
}
public:
static constexpr s8 GetDefaultDataCacheBufferLevel(u32 max_layers) {
return 16 + max_layers - 2;
}
};
}

View File

@@ -1,91 +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 {
enum HashAlgorithmType : u8 {
HashAlgorithmType_Sha2 = 0,
HashAlgorithmType_Sha3 = 1,
};
/* ACCURATE_TO_VERSION: 14.3.0.0 */
class IHash256Generator {
public:
static constexpr size_t HashSize = 256 / BITSIZEOF(u8);
public:
constexpr IHash256Generator() = default;
virtual constexpr ~IHash256Generator() { /* ... */ }
public:
void Initialize() {
return this->DoInitialize();
}
void Update(const void *data, size_t size) {
/* Check pre-conditions. */
AMS_ASSERT(data != nullptr);
return this->DoUpdate(data, size);
}
void GetHash(void *dst, size_t dst_size) {
/* Check pre-conditions. */
AMS_ASSERT(dst_size == HashSize);
return this->DoGetHash(dst, dst_size);
}
protected:
virtual void DoInitialize() = 0;
virtual void DoUpdate(const void *data, size_t size) = 0;
virtual void DoGetHash(void *dst, size_t dst_size) = 0;
};
/* ACCURATE_TO_VERSION: 14.3.0.0 */
class IHash256GeneratorFactory {
public:
constexpr IHash256GeneratorFactory() = default;
virtual constexpr ~IHash256GeneratorFactory() { /* ... */ }
Result Create(std::unique_ptr<IHash256Generator> *out) {
return this->DoCreate(out);
}
void GenerateHash(void *dst, size_t dst_size, const void *src, size_t src_size) {
/* Check pre-conditions. */
AMS_ASSERT(dst != nullptr);
AMS_ASSERT(src != nullptr);
AMS_ASSERT(dst_size == IHash256Generator::HashSize);
return this->DoGenerateHash(dst, dst_size, src, src_size);
}
protected:
virtual Result DoCreate(std::unique_ptr<IHash256Generator> *out) = 0;
virtual void DoGenerateHash(void *dst, size_t dst_size, const void *src, size_t src_size) = 0;
};
/* ACCURATE_TO_VERSION: 14.3.0.0 */
class IHash256GeneratorFactorySelector {
public:
constexpr IHash256GeneratorFactorySelector() = default;
virtual constexpr ~IHash256GeneratorFactorySelector() { /* ... */ }
IHash256GeneratorFactory *GetFactory(HashAlgorithmType alg) { return this->DoGetFactory(alg); }
protected:
virtual IHash256GeneratorFactory *DoGetFactory(HashAlgorithmType alg) = 0;
};
}

View File

@@ -1,170 +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>
#include <stratosphere/fssystem/fssystem_bucket_tree.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: Unknown */
class IndirectStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
NON_COPYABLE(IndirectStorage);
NON_MOVEABLE(IndirectStorage);
public:
static constexpr s32 StorageCount = 2;
static constexpr size_t NodeSize = 16_KB;
using IAllocator = MemoryResource;
struct Entry {
u8 virt_offset[sizeof(s64)];
u8 phys_offset[sizeof(s64)];
s32 storage_index;
void SetVirtualOffset(const s64 &ofs) {
std::memcpy(this->virt_offset, std::addressof(ofs), sizeof(s64));
}
s64 GetVirtualOffset() const {
s64 offset;
std::memcpy(std::addressof(offset), this->virt_offset, sizeof(s64));
return offset;
}
void SetPhysicalOffset(const s64 &ofs) {
std::memcpy(this->phys_offset, std::addressof(ofs), sizeof(s64));
}
s64 GetPhysicalOffset() const {
s64 offset;
std::memcpy(std::addressof(offset), this->phys_offset, sizeof(s64));
return offset;
}
};
static_assert(util::is_pod<Entry>::value);
static_assert(sizeof(Entry) == 0x14);
struct EntryData {
s64 virt_offset;
s64 phys_offset;
s32 storage_index;
void Set(const Entry &entry) {
this->virt_offset = entry.GetVirtualOffset();
this->phys_offset = entry.GetPhysicalOffset();
this->storage_index = entry.storage_index;
}
};
static_assert(util::is_pod<EntryData>::value);
private:
struct ContinuousReadingEntry {
static constexpr size_t FragmentSizeMax = 4_KB;
IndirectStorage::Entry entry;
s64 GetVirtualOffset() const {
return this->entry.GetVirtualOffset();
}
s64 GetPhysicalOffset() const {
return this->entry.GetPhysicalOffset();
}
bool IsFragment() const {
return this->entry.storage_index != 0;
}
};
static_assert(util::is_pod<ContinuousReadingEntry>::value);
public:
static constexpr s64 QueryHeaderStorageSize() {
return BucketTree::QueryHeaderStorageSize();
}
static constexpr s64 QueryNodeStorageSize(s32 entry_count) {
return BucketTree::QueryNodeStorageSize(NodeSize, sizeof(Entry), entry_count);
}
static constexpr s64 QueryEntryStorageSize(s32 entry_count) {
return BucketTree::QueryEntryStorageSize(NodeSize, sizeof(Entry), entry_count);
}
private:
BucketTree m_table;
fs::SubStorage m_data_storage[StorageCount];
public:
IndirectStorage() : m_table(), m_data_storage() { /* ... */ }
virtual ~IndirectStorage() { this->Finalize(); }
Result Initialize(IAllocator *allocator, fs::SubStorage table_storage);
void Finalize();
bool IsInitialized() const { return m_table.IsInitialized(); }
Result Initialize(IAllocator *allocator, fs::SubStorage node_storage, fs::SubStorage entry_storage, s32 entry_count) {
R_RETURN(m_table.Initialize(allocator, node_storage, entry_storage, NodeSize, sizeof(Entry), entry_count));
}
void SetStorage(s32 idx, fs::SubStorage storage) {
AMS_ASSERT(0 <= idx && idx < StorageCount);
m_data_storage[idx] = storage;
}
template<typename T>
void SetStorage(s32 idx, T storage, s64 offset, s64 size) {
AMS_ASSERT(0 <= idx && idx < StorageCount);
m_data_storage[idx] = fs::SubStorage(storage, offset, size);
}
Result GetEntryList(Entry *out_entries, s32 *out_entry_count, s32 entry_count, s64 offset, s64 size);
virtual Result Read(s64 offset, void *buffer, size_t size) override;
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override;
virtual Result GetSize(s64 *out) override {
AMS_ASSERT(out != nullptr);
BucketTree::Offsets offsets;
R_TRY(m_table.GetOffsets(std::addressof(offsets)));
*out = offsets.end_offset;
R_SUCCEED();
}
virtual Result Flush() override {
R_SUCCEED();
}
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
AMS_UNUSED(offset, buffer, size);
R_THROW(fs::ResultUnsupportedWriteForIndirectStorage());
}
virtual Result SetSize(s64 size) override {
AMS_UNUSED(size);
R_THROW(fs::ResultUnsupportedSetSizeForIndirectStorage());
}
protected:
BucketTree &GetEntryTable() { return m_table; }
fs::SubStorage &GetDataStorage(s32 index) {
AMS_ASSERT(0 <= index && index < StorageCount);
return m_data_storage[index];
}
template<bool ContinuousCheck, bool RangeCheck, typename F>
Result OperatePerEntry(s64 offset, s64 size, F func);
};
}

View File

@@ -1,150 +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 <stratosphere/fssystem/fssystem_indirect_storage.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: 13.4.0.0 */
template<bool ContinuousCheck, bool RangeCheck, typename F>
Result IndirectStorage::OperatePerEntry(s64 offset, s64 size, F func) {
/* Validate preconditions. */
AMS_ASSERT(offset >= 0);
AMS_ASSERT(size >= 0);
AMS_ASSERT(this->IsInitialized());
/* Succeed if there's nothing to operate on. */
R_SUCCEED_IF(size == 0);
/* Get the table offsets. */
BucketTree::Offsets table_offsets;
R_TRY(m_table.GetOffsets(std::addressof(table_offsets)));
/* Validate arguments. */
R_UNLESS(table_offsets.IsInclude(offset, size), fs::ResultOutOfRange());
/* Find the offset in our tree. */
BucketTree::Visitor visitor;
R_TRY(m_table.Find(std::addressof(visitor), offset));
{
const auto entry_offset = visitor.Get<Entry>()->GetVirtualOffset();
R_UNLESS(0 <= entry_offset && table_offsets.IsInclude(entry_offset), fs::ResultInvalidIndirectEntryOffset());
}
/* Prepare to operate in chunks. */
auto cur_offset = offset;
const auto end_offset = offset + static_cast<s64>(size);
BucketTree::ContinuousReadingInfo cr_info;
while (cur_offset < end_offset) {
/* Get the current entry. */
const auto cur_entry = *visitor.Get<Entry>();
/* Get and validate the entry's offset. */
const auto cur_entry_offset = cur_entry.GetVirtualOffset();
R_UNLESS(cur_entry_offset <= cur_offset, fs::ResultInvalidIndirectEntryOffset());
/* Validate the storage index. */
R_UNLESS(0 <= cur_entry.storage_index && cur_entry.storage_index < StorageCount, fs::ResultInvalidIndirectEntryStorageIndex());
/* If we need to check the continuous info, do so. */
if constexpr (ContinuousCheck) {
/* Scan, if we need to. */
if (cr_info.CheckNeedScan()) {
R_TRY(visitor.ScanContinuousReading<ContinuousReadingEntry>(std::addressof(cr_info), cur_offset, static_cast<size_t>(end_offset - cur_offset)));
}
/* Process a base storage entry. */
if (cr_info.CanDo()) {
/* Ensure that we can process. */
R_UNLESS(cur_entry.storage_index == 0, fs::ResultInvalidIndirectEntryStorageIndex());
/* Ensure that we remain within range. */
const auto data_offset = cur_offset - cur_entry_offset;
const auto cur_entry_phys_offset = cur_entry.GetPhysicalOffset();
const auto cur_size = static_cast<s64>(cr_info.GetReadSize());
/* If we should, verify the range. */
if constexpr (RangeCheck) {
/* Get the current data storage's size. */
s64 cur_data_storage_size;
R_TRY(m_data_storage[0].GetSize(std::addressof(cur_data_storage_size)));
R_UNLESS(0 <= cur_entry_phys_offset && cur_entry_phys_offset <= cur_data_storage_size, fs::ResultInvalidIndirectEntryOffset());
R_UNLESS(cur_entry_phys_offset + data_offset + cur_size <= cur_data_storage_size, fs::ResultInvalidIndirectStorageSize());
}
/* Operate. */
R_TRY(func(std::addressof(m_data_storage[0]), cur_entry_phys_offset + data_offset, cur_offset, cur_size));
/* Mark as done. */
cr_info.Done();
}
}
/* Get and validate the next entry offset. */
s64 next_entry_offset;
if (visitor.CanMoveNext()) {
R_TRY(visitor.MoveNext());
next_entry_offset = visitor.Get<Entry>()->GetVirtualOffset();
R_UNLESS(table_offsets.IsInclude(next_entry_offset), fs::ResultInvalidIndirectEntryOffset());
} else {
next_entry_offset = table_offsets.end_offset;
}
R_UNLESS(cur_offset < next_entry_offset, fs::ResultInvalidIndirectEntryOffset());
/* Get the offset of the entry in the data we read. */
const auto data_offset = cur_offset - cur_entry_offset;
const auto data_size = (next_entry_offset - cur_entry_offset);
AMS_ASSERT(data_size > 0);
/* Determine how much is left. */
const auto remaining_size = end_offset - cur_offset;
const auto cur_size = std::min<s64>(remaining_size, data_size - data_offset);
AMS_ASSERT(cur_size <= size);
/* Operate, if we need to. */
bool needs_operate;
if constexpr (!ContinuousCheck) {
needs_operate = true;
} else {
needs_operate = !cr_info.IsDone() || cur_entry.storage_index != 0;
}
if (needs_operate) {
const auto cur_entry_phys_offset = cur_entry.GetPhysicalOffset();
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));
}
cur_offset += cur_size;
}
R_SUCCEED();
}
}

View File

@@ -1,76 +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>
#include <stratosphere/fs/fs_istorage.hpp>
#include <stratosphere/fs/fs_memory_storage.hpp>
#include <stratosphere/fs/impl/fs_newable.hpp>
#include <stratosphere/fssystem/fssystem_nca_header.hpp>
#include <stratosphere/fssystem/fssystem_hierarchical_integrity_verification_storage.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: Unknown */
constexpr inline size_t IntegrityLayerCountRomFs = 7;
constexpr inline size_t IntegrityHashLayerBlockSize = 16_KB;
class IntegrityRomFsStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
private:
HierarchicalIntegrityVerificationStorage m_integrity_storage;
FileSystemBufferManagerSet m_buffers;
os::SdkRecursiveMutex m_mutex;
Hash m_master_hash;
std::unique_ptr<fs::MemoryStorage> m_master_hash_storage;
public:
IntegrityRomFsStorage() : m_mutex() { /* ... */ }
virtual ~IntegrityRomFsStorage() override { this->Finalize(); }
Result Initialize(HierarchicalIntegrityVerificationInformation level_hash_info, Hash master_hash, HierarchicalIntegrityVerificationStorage::HierarchicalStorageInformation storage_info, fs::IBufferManager *bm, int max_data_cache_entries, int max_hash_cache_entries, s8 buffer_level, IHash256GeneratorFactory *hgf);
void Finalize();
virtual Result Read(s64 offset, void *buffer, size_t size) override {
R_RETURN(m_integrity_storage.Read(offset, buffer, size));
}
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
R_RETURN(m_integrity_storage.Write(offset, buffer, size));
}
virtual Result SetSize(s64 size) override { AMS_UNUSED(size); R_THROW(fs::ResultUnsupportedSetSizeForIntegrityRomFsStorage()); }
virtual Result GetSize(s64 *out) override {
R_RETURN(m_integrity_storage.GetSize(out));
}
virtual Result Flush() override {
R_RETURN(m_integrity_storage.Flush());
}
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
R_RETURN(m_integrity_storage.OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
}
Result Commit() {
R_RETURN(m_integrity_storage.Commit());
}
FileSystemBufferManagerSet *GetBuffers() {
return m_integrity_storage.GetBuffers();
}
};
}

View File

@@ -1,96 +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>
#include <stratosphere/os.hpp>
#include <stratosphere/fs/fs_istorage.hpp>
#include <stratosphere/fs/fs_substorage.hpp>
#include <stratosphere/fs/fs_storage_type.hpp>
#include <stratosphere/fssystem/fssystem_block_cache_buffered_storage.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: 14.3.0.0 */
class IntegrityVerificationStorage : public ::ams::fs::IStorage {
NON_COPYABLE(IntegrityVerificationStorage);
NON_MOVEABLE(IntegrityVerificationStorage);
public:
static constexpr s64 HashSize = crypto::Sha256Generator::HashSize;
struct BlockHash {
u8 hash[HashSize];
};
static_assert(util::is_pod<BlockHash>::value);
private:
fs::SubStorage m_hash_storage;
fs::SubStorage m_data_storage;
s64 m_verification_block_size;
s64 m_verification_block_order;
s64 m_upper_layer_verification_block_size;
s64 m_upper_layer_verification_block_order;
fs::IBufferManager *m_buffer_manager;
util::optional<fs::HashSalt> m_salt;
bool m_is_real_data;
fssystem::IHash256GeneratorFactory *m_hash_generator_factory;
bool m_is_writable;
bool m_allow_cleared_blocks;
public:
IntegrityVerificationStorage() : m_verification_block_size(0), m_verification_block_order(0), m_upper_layer_verification_block_size(0), m_upper_layer_verification_block_order(0), m_buffer_manager(nullptr), m_salt(util::nullopt) { /* ... */ }
virtual ~IntegrityVerificationStorage() override { this->Finalize(); }
void Initialize(fs::SubStorage hs, fs::SubStorage ds, s64 verif_block_size, s64 upper_layer_verif_block_size, fs::IBufferManager *bm, fssystem::IHash256GeneratorFactory *hgf, const util::optional<fs::HashSalt> &salt, bool is_real_data, bool is_writable, bool allow_cleared_blocks);
void Finalize();
virtual Result Read(s64 offset, void *buffer, size_t size) override;
virtual Result Write(s64 offset, const void *buffer, size_t size) override;
virtual Result SetSize(s64 size) override { AMS_UNUSED(size); R_THROW(fs::ResultUnsupportedSetSizeForIntegrityVerificationStorage()); }
virtual Result GetSize(s64 *out) override;
virtual Result Flush() override;
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override;
using IStorage::OperateRange;
void CalcBlockHash(BlockHash *out, const void *buffer, std::unique_ptr<fssystem::IHash256Generator> &generator) const {
return this->CalcBlockHash(out, buffer, static_cast<size_t>(m_verification_block_size), generator);
}
s64 GetBlockSize() const {
return m_verification_block_size;
}
private:
Result ReadBlockSignature(void *dst, size_t dst_size, s64 offset, size_t size);
Result WriteBlockSignature(const void *src, size_t src_size, s64 offset, size_t size);
Result VerifyHash(const void *buf, BlockHash *hash, std::unique_ptr<fssystem::IHash256Generator> &generator);
void CalcBlockHash(BlockHash *out, const void *buffer, size_t block_size, std::unique_ptr<fssystem::IHash256Generator> &generator) const;
Result IsCleared(bool *is_cleared, const BlockHash &hash);
private:
static void SetValidationBit(BlockHash *hash) {
AMS_ASSERT(hash != nullptr);
hash->hash[HashSize - 1] |= 0x80;
}
static bool IsValidationBit(const BlockHash *hash) {
AMS_ASSERT(hash != nullptr);
return (hash->hash[HashSize - 1] & 0x80) != 0;
}
};
}

View File

@@ -1,85 +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>
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
#include <stratosphere/fs/fs_memory_management.hpp>
#include <stratosphere/fs/fs_path.hpp>
namespace ams::fssystem {
/* TODO: Put this in its own header? */
enum PathCaseSensitiveMode {
PathCaseSensitiveMode_CaseInsensitive = 0,
PathCaseSensitiveMode_CaseSensitive = 1,
};
/* ACCURATE_TO_VERSION: 13.4.0.0 */
class LocalFileSystem : public fs::fsa::IFileSystem, public fs::impl::Newable {
NON_COPYABLE(LocalFileSystem);
NON_MOVEABLE(LocalFileSystem);
private:
#if defined(ATMOSPHERE_OS_WINDOWS)
using NativeCharacterType = wchar_t;
#else
using NativeCharacterType = char;
#endif
using NativePathBuffer = std::unique_ptr<NativeCharacterType[], ::ams::fs::impl::Deleter>;
private:
fs::Path m_root_path;
fssystem::PathCaseSensitiveMode m_case_sensitive_mode;
NativePathBuffer m_native_path_buffer;
int m_native_path_length;
bool m_use_posix_time;
public:
LocalFileSystem(bool posix_time = true) : m_root_path(), m_native_path_buffer(), m_native_path_length(0), m_use_posix_time(posix_time) {
/* ... */
}
Result Initialize(const fs::Path &root_path, fssystem::PathCaseSensitiveMode case_sensitive_mode);
Result GetCaseSensitivePath(int *out_size, char *dst, size_t dst_size, const char *path, const char *work_path);
private:
Result CheckPathCaseSensitively(const NativeCharacterType *path, const NativeCharacterType *root_path, NativeCharacterType *cs_buf, size_t cs_size, bool check_case_sensitivity);
Result ResolveFullPath(NativePathBuffer *out, const fs::Path &path, int max_len, int min_len, bool check_case_sensitivity);
public:
virtual Result DoCreateFile(const fs::Path &path, s64 size, int flags) override;
virtual Result DoDeleteFile(const fs::Path &path) override;
virtual Result DoCreateDirectory(const fs::Path &path) override;
virtual Result DoDeleteDirectory(const fs::Path &path) override;
virtual Result DoDeleteDirectoryRecursively(const fs::Path &path) override;
virtual Result DoRenameFile(const fs::Path &old_path, const fs::Path &new_path) override;
virtual Result DoRenameDirectory(const fs::Path &old_path, const fs::Path &new_path) override;
virtual Result DoGetEntryType(fs::DirectoryEntryType *out, const fs::Path &path) override;
virtual Result DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const fs::Path &path, fs::OpenMode mode) override;
virtual Result DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const fs::Path &path, fs::OpenDirectoryMode mode) override;
virtual Result DoCommit() override;
virtual Result DoGetFreeSpaceSize(s64 *out, const fs::Path &path) override;
virtual Result DoGetTotalSpaceSize(s64 *out, const fs::Path &path) override;
virtual Result DoCleanDirectoryRecursively(const fs::Path &path) override;
virtual Result DoGetFileTimeStampRaw(fs::FileTimeStampRaw *out, const fs::Path &path) override;
virtual Result DoQueryEntry(char *dst, size_t dst_size, const char *src, size_t src_size, fs::fsa::QueryId query, const fs::Path &path) override;
/* These aren't accessible as commands. */
virtual Result DoCommitProvisionally(s64 counter) override;
virtual Result DoRollback() override;
public:
Result DoGetDiskFreeSpace(s64 *out_free, s64 *out_total, s64 *out_total_free, const fs::Path &path);
Result DeleteDirectoryRecursivelyInternal(const NativeCharacterType *path, bool delete_top);
};
}

View File

@@ -1,343 +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>
#include <stratosphere/fs/impl/fs_newable.hpp>
#include <stratosphere/fs/fs_istorage.hpp>
#include <stratosphere/fssystem/fssystem_compression_common.hpp>
#include <stratosphere/fssystem/fssystem_i_hash_256_generator.hpp>
#include <stratosphere/fssystem/fssystem_asynchronous_access.hpp>
#include <stratosphere/fssystem/fssystem_nca_header.hpp>
#include <stratosphere/fs/fs_i_buffer_manager.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: 14.3.0.0 */
class CompressedStorage;
class AesCtrCounterExtendedStorage;
class IndirectStorage;
class SparseStorage;
struct NcaCryptoConfiguration;
using KeyGenerationFunction = void (*)(void *dst_key, size_t dst_key_size, const void *src_key, size_t src_key_size, s32 key_type);
using DecryptAesCtrFunction = void (*)(void *dst, size_t dst_size, u8 key_index, u8 key_generation, const void *src_key, size_t src_key_size, const void *iv, size_t iv_size, const void *src, size_t src_size);
using CryptAesXtsFunction = Result (*)(void *dst, size_t dst_size, const void *key1, const void *key2, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size);
using VerifySign1Function = bool (*)(const void *sig, size_t sig_size, const void *data, size_t data_size, u8 generation);
struct NcaCryptoConfiguration {
static constexpr size_t Rsa2048KeyModulusSize = crypto::Rsa2048PssSha256Verifier::ModulusSize;
static constexpr size_t Rsa2048KeyPublicExponentSize = crypto::Rsa2048PssSha256Verifier::MaximumExponentSize;
static constexpr size_t Rsa2048KeyPrivateExponentSize = Rsa2048KeyModulusSize;
static constexpr size_t Aes128KeySize = crypto::AesEncryptor128::KeySize;
static constexpr size_t Header1SignatureKeyGenerationMax = 1;
static constexpr s32 KeyAreaEncryptionKeyIndexCount = 3;
static constexpr s32 HeaderEncryptionKeyCount = 2;
static constexpr u8 KeyAreaEncryptionKeyIndexZeroKey = 0xFF;
static constexpr size_t KeyGenerationMax = 32;
const u8 *header_1_sign_key_moduli[Header1SignatureKeyGenerationMax + 1];
u8 header_1_sign_key_public_exponent[Rsa2048KeyPublicExponentSize];
u8 key_area_encryption_key_source[KeyAreaEncryptionKeyIndexCount][Aes128KeySize];
u8 header_encryption_key_source[Aes128KeySize];
u8 header_encrypted_encryption_keys[HeaderEncryptionKeyCount][Aes128KeySize];
KeyGenerationFunction generate_key;
CryptAesXtsFunction decrypt_aes_xts_external;
CryptAesXtsFunction encrypt_aes_xts_external;
DecryptAesCtrFunction decrypt_aes_ctr;
DecryptAesCtrFunction decrypt_aes_ctr_external;
VerifySign1Function verify_sign1;
bool is_plaintext_header_available;
bool is_available_sw_key;
#if !defined(ATMOSPHERE_BOARD_NINTENDO_NX)
bool is_unsigned_header_available_for_host_tool;
#endif
};
static_assert(util::is_pod<NcaCryptoConfiguration>::value);
struct NcaCompressionConfiguration {
GetDecompressorFunction get_decompressor;
};
static_assert(util::is_pod<NcaCompressionConfiguration>::value);
constexpr inline s32 KeyAreaEncryptionKeyCount = NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexCount * NcaCryptoConfiguration::KeyGenerationMax;
enum class KeyType : s32 {
ZeroKey = -2,
InvalidKey = -1,
NcaHeaderKey1 = KeyAreaEncryptionKeyCount + 0,
NcaHeaderKey2 = KeyAreaEncryptionKeyCount + 1,
NcaExternalKey = KeyAreaEncryptionKeyCount + 2,
SaveDataDeviceUniqueMac = KeyAreaEncryptionKeyCount + 3,
SaveDataSeedUniqueMac = KeyAreaEncryptionKeyCount + 4,
SaveDataTransferMac = KeyAreaEncryptionKeyCount + 5,
};
constexpr inline bool IsInvalidKeyTypeValue(s32 key_type) {
return key_type < 0;
}
constexpr inline s32 GetKeyTypeValue(u8 key_index, u8 key_generation) {
if (key_index == NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexZeroKey) {
return util::ToUnderlying(KeyType::ZeroKey);
}
if (key_index >= NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexCount) {
return util::ToUnderlying(KeyType::InvalidKey);
}
return NcaCryptoConfiguration::KeyAreaEncryptionKeyIndexCount * key_generation + key_index;
}
class NcaReader : public ::ams::fs::impl::Newable {
NON_COPYABLE(NcaReader);
NON_MOVEABLE(NcaReader);
private:
NcaHeader m_header;
u8 m_decryption_keys[NcaHeader::DecryptionKey_Count][NcaCryptoConfiguration::Aes128KeySize];
std::shared_ptr<fs::IStorage> m_body_storage;
std::unique_ptr<fs::IStorage> m_header_storage;
u8 m_external_decryption_key[NcaCryptoConfiguration::Aes128KeySize];
DecryptAesCtrFunction m_decrypt_aes_ctr;
DecryptAesCtrFunction m_decrypt_aes_ctr_external;
bool m_is_software_aes_prioritized;
bool m_is_available_sw_key;
NcaHeader::EncryptionType m_header_encryption_type;
bool m_is_header_sign1_signature_valid;
GetDecompressorFunction m_get_decompressor;
IHash256GeneratorFactorySelector *m_hash_generator_factory_selector;
public:
NcaReader();
~NcaReader();
Result Initialize(std::shared_ptr<fs::IStorage> base_storage, const NcaCryptoConfiguration &crypto_cfg, const NcaCompressionConfiguration &compression_cfg, IHash256GeneratorFactorySelector *hgf_selector);
std::shared_ptr<fs::IStorage> GetSharedBodyStorage();
u32 GetMagic() const;
NcaHeader::DistributionType GetDistributionType() const;
NcaHeader::ContentType GetContentType() const;
u8 GetHeaderSign1KeyGeneration() const;
u8 GetKeyGeneration() const;
u8 GetKeyIndex() const;
u64 GetContentSize() const;
u64 GetProgramId() const;
u32 GetContentIndex() const;
u32 GetSdkAddonVersion() const;
void GetRightsId(u8 *dst, size_t dst_size) const;
bool HasFsInfo(s32 index) const;
s32 GetFsCount() const;
const Hash &GetFsHeaderHash(s32 index) const;
void GetFsHeaderHash(Hash *dst, s32 index) const;
void GetFsInfo(NcaHeader::FsInfo *dst, s32 index) const;
u64 GetFsOffset(s32 index) const;
u64 GetFsEndOffset(s32 index) const;
u64 GetFsSize(s32 index) const;
void GetEncryptedKey(void *dst, size_t size) const;
const void *GetDecryptionKey(s32 index) const;
bool HasValidInternalKey() const;
bool HasInternalDecryptionKeyForAesHw() const;
bool IsSoftwareAesPrioritized() const;
void PrioritizeSoftwareAes();
bool IsAvailableSwKey() const;
bool HasExternalDecryptionKey() const;
const void *GetExternalDecryptionKey() const;
void SetExternalDecryptionKey(const void *src, size_t size);
void GetRawData(void *dst, size_t dst_size) const;
DecryptAesCtrFunction GetExternalDecryptAesCtrFunction() const;
DecryptAesCtrFunction GetExternalDecryptAesCtrFunctionForExternalKey() const;
NcaHeader::EncryptionType GetEncryptionType() const;
Result ReadHeader(NcaFsHeader *dst, s32 index) const;
GetDecompressorFunction GetDecompressor() const;
IHash256GeneratorFactorySelector *GetHashGeneratorFactorySelector() const;
bool GetHeaderSign1Valid() const;
void GetHeaderSign2(void *dst, size_t size) const;
void GetHeaderSign2TargetHash(void *dst, size_t size) const;
};
class NcaFsHeaderReader : public ::ams::fs::impl::Newable {
NON_COPYABLE(NcaFsHeaderReader);
NON_MOVEABLE(NcaFsHeaderReader);
private:
NcaFsHeader m_data;
s32 m_fs_index;
public:
NcaFsHeaderReader() : m_fs_index(-1) {
std::memset(std::addressof(m_data), 0, sizeof(m_data));
}
Result Initialize(const NcaReader &reader, s32 index);
bool IsInitialized() const { return m_fs_index >= 0; }
// NcaFsHeader &GetData() { return m_data; }
// const NcaFsHeader &GetData() const { return m_data; }
void GetRawData(void *dst, size_t dst_size) const;
NcaFsHeader::HashData &GetHashData();
const NcaFsHeader::HashData &GetHashData() const;
u16 GetVersion() const;
s32 GetFsIndex() const;
NcaFsHeader::FsType GetFsType() const;
NcaFsHeader::HashType GetHashType() const;
NcaFsHeader::EncryptionType GetEncryptionType() const;
NcaPatchInfo &GetPatchInfo();
const NcaPatchInfo &GetPatchInfo() const;
const NcaAesCtrUpperIv GetAesCtrUpperIv() const;
bool IsSkipLayerHashEncryption() const;
Result GetHashTargetOffset(s64 *out) const;
bool ExistsSparseLayer() const;
NcaSparseInfo &GetSparseInfo();
const NcaSparseInfo &GetSparseInfo() const;
bool ExistsCompressionLayer() const;
NcaCompressionInfo &GetCompressionInfo();
const NcaCompressionInfo &GetCompressionInfo() const;
bool ExistsPatchMetaHashLayer() const;
NcaMetaDataHashDataInfo &GetPatchMetaDataHashDataInfo();
const NcaMetaDataHashDataInfo &GetPatchMetaDataHashDataInfo() const;
NcaFsHeader::MetaDataHashType GetPatchMetaHashType() const;
bool ExistsSparseMetaHashLayer() const;
NcaMetaDataHashDataInfo &GetSparseMetaDataHashDataInfo();
const NcaMetaDataHashDataInfo &GetSparseMetaDataHashDataInfo() const;
NcaFsHeader::MetaDataHashType GetSparseMetaHashType() const;
};
class NcaFileSystemDriver : public ::ams::fs::impl::Newable {
NON_COPYABLE(NcaFileSystemDriver);
NON_MOVEABLE(NcaFileSystemDriver);
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
private:
#else
public:
#endif
struct StorageContext {
bool open_raw_storage;
std::shared_ptr<fs::IStorage> body_substorage;
std::shared_ptr<fssystem::SparseStorage> current_sparse_storage;
std::shared_ptr<fs::IStorage> sparse_storage_meta_storage;
std::shared_ptr<fssystem::SparseStorage> original_sparse_storage;
void *external_current_sparse_storage; /* TODO: Add real type? */
void *external_original_sparse_storage; /* TODO: Add real type? */
std::shared_ptr<fs::IStorage> aes_ctr_ex_storage_meta_storage;
std::shared_ptr<fs::IStorage> aes_ctr_ex_storage_data_storage;
std::shared_ptr<fssystem::AesCtrCounterExtendedStorage> aes_ctr_ex_storage;
std::shared_ptr<fs::IStorage> indirect_storage_meta_storage;
std::shared_ptr<fssystem::IndirectStorage> indirect_storage;
std::shared_ptr<fs::IStorage> fs_data_storage;
std::shared_ptr<fs::IStorage> compressed_storage_meta_storage;
std::shared_ptr<fssystem::CompressedStorage> compressed_storage;
std::shared_ptr<fs::IStorage> patch_layer_info_storage;
std::shared_ptr<fs::IStorage> sparse_layer_info_storage;
/* For tools. */
std::shared_ptr<fs::IStorage> external_original_storage;
};
private:
enum AlignmentStorageRequirement {
/* TODO */
AlignmentStorageRequirement_CacheBlockSize = 0,
AlignmentStorageRequirement_None = 1,
};
private:
std::shared_ptr<NcaReader> m_original_reader;
std::shared_ptr<NcaReader> m_reader;
MemoryResource * const m_allocator;
fs::IBufferManager * const m_buffer_manager;
fssystem::IHash256GeneratorFactorySelector * const m_hash_generator_factory_selector;
public:
static Result SetupFsHeaderReader(NcaFsHeaderReader *out, const NcaReader &reader, s32 fs_index);
public:
NcaFileSystemDriver(std::shared_ptr<NcaReader> reader, MemoryResource *allocator, fs::IBufferManager *buffer_manager, IHash256GeneratorFactorySelector *hgf_selector) : m_original_reader(), m_reader(reader), m_allocator(allocator), m_buffer_manager(buffer_manager), m_hash_generator_factory_selector(hgf_selector) {
AMS_ASSERT(m_reader != nullptr);
AMS_ASSERT(m_hash_generator_factory_selector != nullptr);
}
NcaFileSystemDriver(std::shared_ptr<NcaReader> original_reader, std::shared_ptr<NcaReader> reader, MemoryResource *allocator, fs::IBufferManager *buffer_manager, IHash256GeneratorFactorySelector *hgf_selector) : m_original_reader(original_reader), m_reader(reader), m_allocator(allocator), m_buffer_manager(buffer_manager), m_hash_generator_factory_selector(hgf_selector) {
AMS_ASSERT(m_reader != nullptr);
AMS_ASSERT(m_hash_generator_factory_selector != nullptr);
}
Result OpenStorageWithContext(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<IAsynchronousAccessSplitter> *out_splitter, NcaFsHeaderReader *out_header_reader, s32 fs_index, StorageContext *ctx);
Result OpenStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<IAsynchronousAccessSplitter> *out_splitter, NcaFsHeaderReader *out_header_reader, s32 fs_index) {
/* Create a storage context. */
StorageContext ctx{};
/* Open the storage. */
R_RETURN(OpenStorageWithContext(out, out_splitter, out_header_reader, fs_index, std::addressof(ctx)));
}
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
private:
#else
public:
#endif
Result CreateStorageByRawStorage(std::shared_ptr<fs::IStorage> *out, const NcaFsHeaderReader *header_reader, std::shared_ptr<fs::IStorage> raw_storage, StorageContext *ctx);
private:
Result OpenStorageImpl(std::shared_ptr<fs::IStorage> *out, NcaFsHeaderReader *out_header_reader, s32 fs_index, StorageContext *ctx);
Result OpenIndirectableStorageAsOriginal(std::shared_ptr<fs::IStorage> *out, const NcaFsHeaderReader *header_reader, StorageContext *ctx);
Result CreateBodySubStorage(std::shared_ptr<fs::IStorage> *out, s64 offset, s64 size);
Result CreateAesCtrStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, AlignmentStorageRequirement alignment_storage_requirement);
Result CreateAesXtsStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, s64 offset);
Result CreateSparseStorageMetaStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, const NcaSparseInfo &sparse_info);
Result CreateSparseStorageCore(std::shared_ptr<fssystem::SparseStorage> *out, std::shared_ptr<fs::IStorage> base_storage, s64 base_size, std::shared_ptr<fs::IStorage> meta_storage, const NcaSparseInfo &sparse_info, bool external_info);
Result CreateSparseStorage(std::shared_ptr<fs::IStorage> *out, s64 *out_fs_data_offset, std::shared_ptr<fssystem::SparseStorage> *out_sparse_storage, std::shared_ptr<fs::IStorage> *out_meta_storage, s32 index, const NcaAesCtrUpperIv &upper_iv, const NcaSparseInfo &sparse_info);
Result CreateSparseStorageMetaStorageWithVerification(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> *out_verification, std::shared_ptr<fs::IStorage> base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, const NcaSparseInfo &sparse_info, const NcaMetaDataHashDataInfo &meta_data_hash_data_info, IHash256GeneratorFactory *hgf);
Result CreateSparseStorageWithVerification(std::shared_ptr<fs::IStorage> *out, s64 *out_fs_data_offset, std::shared_ptr<fssystem::SparseStorage> *out_sparse_storage, std::shared_ptr<fs::IStorage> *out_meta_storage, std::shared_ptr<fs::IStorage> *out_verification, s32 index, const NcaAesCtrUpperIv &upper_iv, const NcaSparseInfo &sparse_info, const NcaMetaDataHashDataInfo &meta_data_hash_data_info, NcaFsHeader::MetaDataHashType meta_data_hash_type);
Result CreateAesCtrExStorageMetaStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, s64 offset, NcaFsHeader::EncryptionType encryption_type, const NcaAesCtrUpperIv &upper_iv, const NcaPatchInfo &patch_info);
Result CreateAesCtrExStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fssystem::AesCtrCounterExtendedStorage> *out_ext, std::shared_ptr<fs::IStorage> base_storage, std::shared_ptr<fs::IStorage> meta_storage, s64 counter_offset, const NcaAesCtrUpperIv &upper_iv, const NcaPatchInfo &patch_info);
Result CreateIndirectStorageMetaStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, const NcaPatchInfo &patch_info);
Result CreateIndirectStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fssystem::IndirectStorage> *out_ind, std::shared_ptr<fs::IStorage> base_storage, std::shared_ptr<fs::IStorage> original_data_storage, std::shared_ptr<fs::IStorage> meta_storage, const NcaPatchInfo &patch_info);
Result CreatePatchMetaStorage(std::shared_ptr<fs::IStorage> *out_aes_ctr_ex_meta, std::shared_ptr<fs::IStorage> *out_indirect_meta, std::shared_ptr<fs::IStorage> *out_verification, std::shared_ptr<fs::IStorage> base_storage, s64 offset, const NcaAesCtrUpperIv &upper_iv, const NcaPatchInfo &patch_info, const NcaMetaDataHashDataInfo &meta_data_hash_data_info, IHash256GeneratorFactory *hgf);
Result CreateSha256Storage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, const NcaFsHeader::HashData::HierarchicalSha256Data &sha256_data, IHash256GeneratorFactory *hgf);
Result CreateIntegrityVerificationStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, const NcaFsHeader::HashData::IntegrityMetaInfo &meta_info, IHash256GeneratorFactory *hgf);
Result CreateIntegrityVerificationStorageForMeta(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> *out_verification, std::shared_ptr<fs::IStorage> base_storage, s64 offset, const NcaMetaDataHashDataInfo &meta_data_hash_data_info, IHash256GeneratorFactory *hgf);
Result CreateIntegrityVerificationStorageImpl(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fs::IStorage> base_storage, const NcaFsHeader::HashData::IntegrityMetaInfo &meta_info, s64 layer_info_offset, int max_data_cache_entries, int max_hash_cache_entries, s8 buffer_level, IHash256GeneratorFactory *hgf);
Result CreateRegionSwitchStorage(std::shared_ptr<fs::IStorage> *out, const NcaFsHeaderReader *header_reader, std::shared_ptr<fs::IStorage> inside_storage, std::shared_ptr<fs::IStorage> outside_storage);
Result CreateCompressedStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fssystem::CompressedStorage> *out_cmp, std::shared_ptr<fs::IStorage> *out_meta, std::shared_ptr<fs::IStorage> base_storage, const NcaCompressionInfo &compression_info);
public:
Result CreateCompressedStorage(std::shared_ptr<fs::IStorage> *out, std::shared_ptr<fssystem::CompressedStorage> *out_cmp, std::shared_ptr<fs::IStorage> *out_meta, std::shared_ptr<fs::IStorage> base_storage, const NcaCompressionInfo &compression_info, GetDecompressorFunction get_decompressor, MemoryResource *allocator, fs::IBufferManager *buffer_manager);
};
}

View File

@@ -1,330 +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>
#include <stratosphere/fssystem/fssystem_i_hash_256_generator.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: 14.3.0.0 */
struct Hash {
static constexpr size_t Size = IHash256Generator::HashSize;
u8 value[Size];
};
static_assert(sizeof(Hash) == Hash::Size);
static_assert(util::is_pod<Hash>::value);
using NcaDigest = Hash;
struct NcaHeader {
enum class ContentType : u8 {
Program = 0,
Meta = 1,
Control = 2,
Manual = 3,
Data = 4,
PublicData = 5,
Start = Program,
End = PublicData,
};
enum class DistributionType : u8 {
Download = 0,
GameCard = 1,
Start = Download,
End = GameCard,
};
enum class EncryptionType : u8 {
Auto = 0,
None = 1,
};
enum DecryptionKey {
DecryptionKey_AesXts = 0,
DecryptionKey_AesXts1 = DecryptionKey_AesXts,
DecryptionKey_AesXts2 = 1,
DecryptionKey_AesCtr = 2,
DecryptionKey_AesCtrEx = 3,
DecryptionKey_AesCtrHw = 4,
DecryptionKey_Count,
};
struct FsInfo {
u32 start_sector;
u32 end_sector;
u32 hash_sectors;
u32 reserved;
};
static_assert(sizeof(FsInfo) == 0x10);
static_assert(util::is_pod<FsInfo>::value);
static constexpr u32 Magic0 = util::FourCC<'N','C','A','0'>::Code;
static constexpr u32 Magic1 = util::FourCC<'N','C','A','1'>::Code;
static constexpr u32 Magic2 = util::FourCC<'N','C','A','2'>::Code;
static constexpr u32 Magic3 = util::FourCC<'N','C','A','3'>::Code;
static constexpr u32 Magic = Magic3;
static constexpr size_t Size = 1_KB;
static constexpr s32 FsCountMax = 4;
static constexpr size_t HeaderSignCount = 2;
static constexpr size_t HeaderSignSize = 0x100;
static constexpr size_t EncryptedKeyAreaSize = 0x100;
static constexpr size_t SectorSize = 0x200;
static constexpr size_t SectorShift = 9;
static constexpr size_t RightsIdSize = 0x10;
static constexpr size_t XtsBlockSize = 0x200;
static constexpr size_t CtrBlockSize = 0x10;
static_assert(SectorSize == (1 << SectorShift));
/* Data members. */
u8 header_sign_1[HeaderSignSize];
u8 header_sign_2[HeaderSignSize];
u32 magic;
DistributionType distribution_type;
ContentType content_type;
u8 key_generation;
u8 key_index;
u64 content_size;
u64 program_id;
u32 content_index;
u32 sdk_addon_version;
u8 key_generation_2;
u8 header1_signature_key_generation;
u8 reserved_222[2];
u32 reserved_224[3];
u8 rights_id[RightsIdSize];
FsInfo fs_info[FsCountMax];
Hash fs_header_hash[FsCountMax];
u8 encrypted_key_area[EncryptedKeyAreaSize];
static constexpr u64 SectorToByte(u32 sector) {
return static_cast<u64>(sector) << SectorShift;
}
static constexpr u32 ByteToSector(u64 byte) {
return static_cast<u32>(byte >> SectorShift);
}
u8 GetProperKeyGeneration() const;
};
static_assert(sizeof(NcaHeader) == NcaHeader::Size);
static_assert(util::is_pod<NcaHeader>::value);
struct NcaBucketInfo {
static constexpr size_t HeaderSize = 0x10;
fs::Int64 offset;
fs::Int64 size;
u8 header[HeaderSize];
};
static_assert(util::is_pod<NcaBucketInfo>::value);
struct NcaPatchInfo {
static constexpr size_t Size = 0x40;
static constexpr size_t Offset = 0x100;
fs::Int64 indirect_offset;
fs::Int64 indirect_size;
u8 indirect_header[NcaBucketInfo::HeaderSize];
fs::Int64 aes_ctr_ex_offset;
fs::Int64 aes_ctr_ex_size;
u8 aes_ctr_ex_header[NcaBucketInfo::HeaderSize];
bool HasIndirectTable() const;
bool HasAesCtrExTable() const;
};
static_assert(util::is_pod<NcaPatchInfo>::value);
union NcaAesCtrUpperIv {
u64 value;
struct {
u32 generation;
u32 secure_value;
} part;
};
static_assert(util::is_pod<NcaAesCtrUpperIv>::value);
struct NcaSparseInfo {
NcaBucketInfo bucket;
fs::Int64 physical_offset;
u16 generation;
u8 reserved[6];
s64 GetPhysicalSize() const {
return this->bucket.offset + this->bucket.size;
}
u32 GetGeneration() const {
return static_cast<u32>(this->generation) << 16;
}
const NcaAesCtrUpperIv MakeAesCtrUpperIv(NcaAesCtrUpperIv upper_iv) const {
NcaAesCtrUpperIv sparse_upper_iv = upper_iv;
sparse_upper_iv.part.generation = this->GetGeneration();
return sparse_upper_iv;
}
};
static_assert(util::is_pod<NcaSparseInfo>::value);
struct NcaCompressionInfo {
NcaBucketInfo bucket;
u8 reserved[8];
};
static_assert(util::is_pod<NcaCompressionInfo>::value);
struct NcaMetaDataHashDataInfo {
fs::Int64 offset;
fs::Int64 size;
Hash hash;
};
static_assert(util::is_pod<NcaMetaDataHashDataInfo>::value);
struct NcaFsHeader {
static constexpr size_t Size = 0x200;
static constexpr size_t HashDataOffset = 0x8;
struct Region {
fs::Int64 offset;
fs::Int64 size;
};
static_assert(util::is_pod<Region>::value);
enum class FsType : u8 {
RomFs = 0,
PartitionFs = 1,
};
enum class EncryptionType : u8 {
Auto = 0,
None = 1,
AesXts = 2,
AesCtr = 3,
AesCtrEx = 4,
AesCtrSkipLayerHash = 5,
AesCtrExSkipLayerHash = 6,
};
enum class HashType : u8 {
Auto = 0,
None = 1,
HierarchicalSha256Hash = 2,
HierarchicalIntegrityHash = 3,
AutoSha3 = 4,
HierarchicalSha3256Hash = 5,
HierarchicalIntegritySha3Hash = 6,
};
enum class MetaDataHashType : u8 {
None = 0,
HierarchicalIntegrity = 1,
};
union HashData {
struct HierarchicalSha256Data {
static constexpr size_t HashLayerCountMax = 5;
static const size_t MasterHashOffset;
Hash fs_data_master_hash;
s32 hash_block_size;
s32 hash_layer_count;
Region hash_layer_region[HashLayerCountMax];
} hierarchical_sha256_data;
static_assert(util::is_pod<HierarchicalSha256Data>::value);
struct IntegrityMetaInfo {
static const size_t MasterHashOffset;
u32 magic;
u32 version;
u32 master_hash_size;
struct LevelHashInfo {
u32 max_layers;
struct HierarchicalIntegrityVerificationLevelInformation {
static constexpr size_t IntegrityMaxLayerCount = 7;
fs::Int64 offset;
fs::Int64 size;
s32 block_order;
u8 reserved[4];
} info[HierarchicalIntegrityVerificationLevelInformation::IntegrityMaxLayerCount - 1];
struct SignatureSalt {
static constexpr size_t Size = 0x20;
u8 value[Size];
} seed;
} level_hash_info;
Hash master_hash;
} integrity_meta_info;
static_assert(util::is_pod<IntegrityMetaInfo>::value);
u8 padding[NcaPatchInfo::Offset - HashDataOffset];
};
u16 version;
FsType fs_type;
HashType hash_type;
EncryptionType encryption_type;
MetaDataHashType meta_data_hash_type;
u8 reserved[2];
HashData hash_data;
NcaPatchInfo patch_info;
NcaAesCtrUpperIv aes_ctr_upper_iv;
NcaSparseInfo sparse_info;
NcaCompressionInfo compression_info;
NcaMetaDataHashDataInfo meta_data_hash_data_info;
u8 pad[0x30];
bool IsSkipLayerHashEncryption() const {
return this->encryption_type == EncryptionType::AesCtrSkipLayerHash || this->encryption_type == EncryptionType::AesCtrExSkipLayerHash;
}
Result GetHashTargetOffset(s64 *out) const {
switch (this->hash_type) {
case HashType::HierarchicalIntegrityHash:
case HashType::HierarchicalIntegritySha3Hash:
*out = this->hash_data.integrity_meta_info.level_hash_info.info[this->hash_data.integrity_meta_info.level_hash_info.max_layers - 2].offset;
R_SUCCEED();
case HashType::HierarchicalSha256Hash:
case HashType::HierarchicalSha3256Hash:
*out = this->hash_data.hierarchical_sha256_data.hash_layer_region[this->hash_data.hierarchical_sha256_data.hash_layer_count - 1].offset;
R_SUCCEED();
default:
R_THROW(fs::ResultInvalidNcaFsHeader());
}
}
};
static_assert(sizeof(NcaFsHeader) == NcaFsHeader::Size);
static_assert(util::is_pod<NcaFsHeader>::value);
static_assert(AMS_OFFSETOF(NcaFsHeader, patch_info) == NcaPatchInfo::Offset);
inline constexpr const size_t NcaFsHeader::HashData::HierarchicalSha256Data::MasterHashOffset = AMS_OFFSETOF(NcaFsHeader, hash_data.hierarchical_sha256_data.fs_data_master_hash);
inline constexpr const size_t NcaFsHeader::HashData::IntegrityMetaInfo::MasterHashOffset = AMS_OFFSETOF(NcaFsHeader, hash_data.integrity_meta_info.master_hash);
struct NcaMetaDataHashData {
s64 layer_info_offset;
NcaFsHeader::HashData::IntegrityMetaInfo integrity_meta_info;
};
static_assert(sizeof(NcaMetaDataHashData) == sizeof(NcaFsHeader::HashData::IntegrityMetaInfo) + sizeof(s64));
static_assert(util::is_pod<NcaMetaDataHashData>::value);
}

View File

@@ -1,74 +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 <stratosphere/fssystem/fssystem_partition_file_system_meta.hpp>
#include <stratosphere/fs/fsa/fs_ifile.hpp>
#include <stratosphere/fs/fsa/fs_idirectory.hpp>
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: Unknown */
template<typename MetaType>
class PartitionFileSystemCore : public fs::impl::Newable, public fs::fsa::IFileSystem {
NON_COPYABLE(PartitionFileSystemCore);
NON_MOVEABLE(PartitionFileSystemCore);
private:
class PartitionFile;
class PartitionDirectory;
private:
fs::IStorage *m_base_storage;
MetaType *m_meta_data;
bool m_initialized;
size_t m_meta_data_size;
std::unique_ptr<MetaType> m_unique_meta_data;
std::shared_ptr<fs::IStorage> m_shared_storage;
private:
Result Initialize(fs::IStorage *base_storage, MemoryResource *allocator);
public:
PartitionFileSystemCore();
virtual ~PartitionFileSystemCore() override;
Result Initialize(std::unique_ptr<MetaType> &&meta_data, std::shared_ptr<fs::IStorage> base_storage);
Result Initialize(MetaType *meta_data, std::shared_ptr<fs::IStorage> base_storage);
Result Initialize(fs::IStorage *base_storage);
Result Initialize(std::shared_ptr<fs::IStorage> base_storage);
Result Initialize(std::shared_ptr<fs::IStorage> base_storage, MemoryResource *allocator);
Result GetFileBaseOffset(s64 *out_offset, const char *path);
virtual Result DoCreateFile(const fs::Path &path, s64 size, int option) override;
virtual Result DoDeleteFile(const fs::Path &path) override;
virtual Result DoCreateDirectory(const fs::Path &path) override;
virtual Result DoDeleteDirectory(const fs::Path &path) override;
virtual Result DoDeleteDirectoryRecursively(const fs::Path &path) override;
virtual Result DoRenameFile(const fs::Path &old_path, const fs::Path &new_path) override;
virtual Result DoRenameDirectory(const fs::Path &old_path, const fs::Path &new_path) override;
virtual Result DoGetEntryType(fs::DirectoryEntryType *out, const fs::Path &path) override;
virtual Result DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const fs::Path &path, fs::OpenMode mode) override;
virtual Result DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const fs::Path &path, fs::OpenDirectoryMode mode) override;
virtual Result DoCommit() override;
virtual Result DoCleanDirectoryRecursively(const fs::Path &path) override;
/* These aren't accessible as commands. */
virtual Result DoCommitProvisionally(s64 counter) override;
};
using PartitionFileSystem = PartitionFileSystemCore<PartitionFileSystemMeta>;
using Sha256PartitionFileSystem = PartitionFileSystemCore<Sha256PartitionFileSystemMeta>;
}

View File

@@ -1,115 +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 <stratosphere/fs/fs_common.hpp>
#include <stratosphere/fs/impl/fs_newable.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: Unknown */
namespace impl {
struct PartitionFileSystemFormat {
#pragma pack(push, 1)
struct PartitionEntry {
u64 offset;
u64 size;
u32 name_offset;
u32 reserved;
};
static_assert(util::is_pod<PartitionEntry>::value);
#pragma pack(pop)
static constexpr const char VersionSignature[] = { 'P', 'F', 'S', '0' };
static constexpr size_t EntryNameLengthMax = ::ams::fs::EntryNameLengthMax;
static constexpr size_t FileDataAlignmentSize = 0x20;
using ResultSignatureVerificationFailed = fs::ResultPartitionSignatureVerificationFailed;
};
struct Sha256PartitionFileSystemFormat {
static constexpr size_t HashSize = ::ams::crypto::Sha256Generator::HashSize;
#pragma pack(push, 1)
struct PartitionEntry {
u64 offset;
u64 size;
u32 name_offset;
u32 hash_target_size;
u64 hash_target_offset;
char hash[HashSize];
};
static_assert(util::is_pod<PartitionEntry>::value);
#pragma pack(pop)
static constexpr const char VersionSignature[] = { 'H', 'F', 'S', '0' };
static constexpr size_t EntryNameLengthMax = ::ams::fs::EntryNameLengthMax;
static constexpr size_t FileDataAlignmentSize = 0x200;
using ResultSignatureVerificationFailed = fs::ResultSha256PartitionSignatureVerificationFailed;
};
}
template<typename Format>
class PartitionFileSystemMetaCore : public fs::impl::Newable {
public:
static constexpr size_t EntryNameLengthMax = Format::EntryNameLengthMax;
static constexpr size_t FileDataAlignmentSize = Format::FileDataAlignmentSize;
/* Forward declare header. */
struct PartitionFileSystemHeader;
using PartitionEntry = typename Format::PartitionEntry;
protected:
bool m_initialized;
PartitionFileSystemHeader *m_header;
PartitionEntry *m_entries;
char *m_name_table;
size_t m_meta_data_size;
MemoryResource *m_allocator;
char *m_buffer;
public:
PartitionFileSystemMetaCore() : m_initialized(false), m_allocator(nullptr), m_buffer(nullptr) { /* ... */ }
~PartitionFileSystemMetaCore();
Result Initialize(fs::IStorage *storage, MemoryResource *allocator);
Result Initialize(fs::IStorage *storage, void *header, size_t header_size);
const PartitionEntry *GetEntry(s32 index) const;
s32 GetEntryCount() const;
s32 GetEntryIndex(const char *name) const;
const char *GetEntryName(s32 index) const;
size_t GetHeaderSize() const;
size_t GetMetaDataSize() const;
public:
static Result QueryMetaDataSize(size_t *out_size, fs::IStorage *storage);
protected:
void DeallocateBuffer();
};
using PartitionFileSystemMeta = PartitionFileSystemMetaCore<impl::PartitionFileSystemFormat>;
class Sha256PartitionFileSystemMeta : public PartitionFileSystemMetaCore<impl::Sha256PartitionFileSystemFormat> {
public:
using PartitionFileSystemMetaCore<impl::Sha256PartitionFileSystemFormat>::Initialize;
Result Initialize(fs::IStorage *base_storage, MemoryResource *allocator, const void *hash, size_t hash_size, util::optional<u8> suffix = util::nullopt);
};
}

View File

@@ -1,72 +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 {
/* ACCURATE_TO_VERSION: 13.4.0.0 */
namespace impl {
template<typename T, size_t Size>
struct PimplHelper {
static void Construct(void *);
static void Destroy(void *);
};
}
template<typename T, size_t Size>
class Pimpl {
private:
#if defined(ATMOSPHERE_OS_HORIZON) || defined(ATMOSPHERE_OS_WINDOWS) || defined(ATMOSPHERE_OS_LINUX)
static constexpr size_t ExtraSizeToEnsureCompatibility = 0;
#elif defined(ATMOSPHERE_OS_MACOS)
static constexpr size_t ExtraSizeToEnsureCompatibility = 0x20;
#endif
static constexpr size_t StorageSize = Size + ExtraSizeToEnsureCompatibility;
private:
alignas(0x10) u8 m_storage[StorageSize];
public:
ALWAYS_INLINE Pimpl() { impl::PimplHelper<T, StorageSize>::Construct(m_storage); }
ALWAYS_INLINE ~Pimpl() { impl::PimplHelper<T, StorageSize>::Destroy(m_storage); }
ALWAYS_INLINE T *Get() { return reinterpret_cast<T *>(m_storage + 0); }
ALWAYS_INLINE T *operator->() { return reinterpret_cast<T *>(m_storage + 0); }
};
#define AMS_FSSYSTEM_ENABLE_PIMPL(_CLASSNAME_) \
namespace ams::fssystem::impl { \
\
template<size_t Size> \
struct PimplHelper<_CLASSNAME_, Size> { \
static ALWAYS_INLINE void Construct(void *p) { \
static_assert(sizeof(_CLASSNAME_) <= Size); \
static_assert(alignof(_CLASSNAME_) <= 0x10); \
\
std::construct_at(static_cast<_CLASSNAME_ *>(p)); \
} \
\
static ALWAYS_INLINE void Destroy(void *p) { \
std::destroy_at(static_cast<_CLASSNAME_ *>(p)); \
} \
}; \
\
}
}

View File

@@ -1,114 +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>
#include <stratosphere/fs/impl/fs_newable.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: Unknown */
constexpr inline size_t BufferPoolAlignment = 4_KB;
constexpr inline size_t BufferPoolWorkSize = 320;
class PooledBuffer {
NON_COPYABLE(PooledBuffer);
private:
char *m_buffer;
size_t m_size;
private:
static size_t GetAllocatableSizeMaxCore(bool large);
public:
static size_t GetAllocatableSizeMax() { return GetAllocatableSizeMaxCore(false); }
static size_t GetAllocatableParticularlyLargeSizeMax() { return GetAllocatableSizeMaxCore(true); }
private:
void Swap(PooledBuffer &rhs) {
std::swap(m_buffer, rhs.m_buffer);
std::swap(m_size, rhs.m_size);
}
public:
/* Constructor/Destructor. */
constexpr PooledBuffer() : m_buffer(), m_size() { /* ... */ }
PooledBuffer(size_t ideal_size, size_t required_size) : m_buffer(), m_size() {
this->Allocate(ideal_size, required_size);
}
~PooledBuffer() {
this->Deallocate();
}
/* Move and assignment. */
explicit PooledBuffer(PooledBuffer &&rhs) : m_buffer(rhs.m_buffer), m_size(rhs.m_size) {
rhs.m_buffer = nullptr;
rhs.m_size = 0;
}
PooledBuffer &operator=(PooledBuffer &&rhs) {
PooledBuffer(std::move(rhs)).Swap(*this);
return *this;
}
/* Allocation API. */
void Allocate(size_t ideal_size, size_t required_size) {
return this->AllocateCore(ideal_size, required_size, false);
}
void AllocateParticularlyLarge(size_t ideal_size, size_t required_size) {
return this->AllocateCore(ideal_size, required_size, true);
}
void Shrink(size_t ideal_size);
void Deallocate() {
/* Shrink the buffer to empty. */
this->Shrink(0);
AMS_ASSERT(m_buffer == nullptr);
}
char *GetBuffer() const {
AMS_ASSERT(m_buffer != nullptr);
return m_buffer;
}
size_t GetSize() const {
AMS_ASSERT(m_buffer != nullptr);
return m_size;
}
private:
void AllocateCore(size_t ideal_size, size_t required_size, bool large);
};
Result InitializeBufferPool(char *buffer, size_t size);
Result InitializeBufferPool(char *buffer, size_t size, char *work, size_t work_size);
bool IsPooledBuffer(const void *buffer);
size_t GetPooledBufferRetriedCount();
size_t GetPooledBufferReduceAllocationCount();
size_t GetPooledBufferFreeSizePeak();
void ClearPooledBufferPeak();
void RegisterAdditionalDeviceAddress(uintptr_t address, size_t size);
void UnregisterAdditionalDeviceAddress(uintptr_t address);
bool IsAdditionalDeviceAddress(const void *ptr);
inline bool IsDeviceAddress(const void *buffer) {
return IsPooledBuffer(buffer) || IsAdditionalDeviceAddress(buffer);
}
}

View File

@@ -1,80 +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>
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
#include <stratosphere/fs/impl/fs_newable.hpp>
#include <stratosphere/fs/common/fs_dbm_hierarchical_rom_file_table.hpp>
#include <stratosphere/fs/fs_istorage.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: Unknown */
class RomFsFileSystem : public fs::fsa::IFileSystem, public fs::impl::Newable {
NON_COPYABLE(RomFsFileSystem);
public:
using RomFileTable = fs::HierarchicalRomFileTable;
private:
RomFileTable m_rom_file_table;
fs::IStorage *m_base_storage;
std::shared_ptr<fs::IStorage> m_shared_storage;
std::unique_ptr<fs::IStorage> m_dir_bucket_storage;
std::unique_ptr<fs::IStorage> m_dir_entry_storage;
std::unique_ptr<fs::IStorage> m_file_bucket_storage;
std::unique_ptr<fs::IStorage> m_file_entry_storage;
s64 m_entry_size;
private:
Result GetFileInfo(RomFileTable::FileInfo *out, const char *path);
Result GetFileInfo(RomFileTable::FileInfo *out, const fs::Path &path) {
R_RETURN(this->GetFileInfo(out, path.GetString()));
}
Result CheckPathFormat(const fs::Path &path) const {
R_RETURN(fs::PathFormatter::CheckPathFormat(path.GetString(), fs::PathFlags{}));
}
public:
static Result GetRequiredWorkingMemorySize(size_t *out, fs::IStorage *storage);
public:
RomFsFileSystem();
virtual ~RomFsFileSystem() override;
Result Initialize(fs::IStorage *base, void *work, size_t work_size, bool use_cache);
Result Initialize(std::shared_ptr<fs::IStorage> base, void *work, size_t work_size, bool use_cache);
fs::IStorage *GetBaseStorage();
RomFileTable *GetRomFileTable();
Result GetFileBaseOffset(s64 *out, const fs::Path &path);
public:
virtual Result DoCreateFile(const fs::Path &path, s64 size, int flags) override;
virtual Result DoDeleteFile(const fs::Path &path) override;
virtual Result DoCreateDirectory(const fs::Path &path) override;
virtual Result DoDeleteDirectory(const fs::Path &path) override;
virtual Result DoDeleteDirectoryRecursively(const fs::Path &path) override;
virtual Result DoRenameFile(const fs::Path &old_path, const fs::Path &new_path) override;
virtual Result DoRenameDirectory(const fs::Path &old_path, const fs::Path &new_path) override;
virtual Result DoGetEntryType(fs::DirectoryEntryType *out, const fs::Path &path) override;
virtual Result DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const fs::Path &path, fs::OpenMode mode) override;
virtual Result DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const fs::Path &path, fs::OpenDirectoryMode mode) override;
virtual Result DoCommit() override;
virtual Result DoGetFreeSpaceSize(s64 *out, const fs::Path &path) override;
virtual Result DoCleanDirectoryRecursively(const fs::Path &path) override;
/* These aren't accessible as commands. */
virtual Result DoCommitProvisionally(s64 counter) override;
};
}

View File

@@ -1,55 +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>
#include <stratosphere/fs/fs_priority.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: 13.4.0.0 */
class ServiceContext {
private:
struct DeferredProcessContextForDeviceError {
u64 process_id;
bool is_process_deferred;
bool is_invoke_deferral_requested;
};
struct DeferredProcessContextForPriority {
int session_type;
bool is_process_deferred;
bool is_acquired;
};
private:
fs::PriorityRaw m_priority;
DeferredProcessContextForDeviceError m_deferred_process_context_for_device_error;
DeferredProcessContextForPriority m_deferred_process_context_for_priority;
int m_storage_flag;
void *m_request_hook_context;
bool m_enable_count_failed_ideal_pooled_buffer_allocations;
public:
ServiceContext() : m_priority(fs::PriorityRaw_Normal), m_storage_flag(0), m_request_hook_context(nullptr), m_enable_count_failed_ideal_pooled_buffer_allocations(false) {
/* ... */
}
};
void RegisterServiceContext(ServiceContext *context);
void UnregisterServiceContext();
ServiceContext *GetServiceContext();
}

View File

@@ -1,113 +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>
#include <stratosphere/fssystem/fssystem_i_hash_256_generator.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: 14.3.0.0 */
namespace impl {
template<typename Traits>
class ShaHashGenerator final : public ::ams::fssystem::IHash256Generator, public ::ams::fs::impl::Newable {
static_assert(Traits::Generator::HashSize == IHash256Generator::HashSize);
NON_COPYABLE(ShaHashGenerator);
NON_MOVEABLE(ShaHashGenerator);
private:
using Generator = typename Traits::Generator;
private:
Generator m_generator;
public:
ShaHashGenerator() = default;
protected:
virtual void DoInitialize() override {
m_generator.Initialize();
}
virtual void DoUpdate(const void *data, size_t size) override {
m_generator.Update(data, size);
}
virtual void DoGetHash(void *dst, size_t dst_size) override {
m_generator.GetHash(dst, dst_size);
}
};
template<typename Traits>
class ShaHashGeneratorFactory final : public IHash256GeneratorFactory, public ::ams::fs::impl::Newable {
static_assert(Traits::Generator::HashSize == IHash256Generator::HashSize);
NON_COPYABLE(ShaHashGeneratorFactory);
NON_MOVEABLE(ShaHashGeneratorFactory);
public:
constexpr ShaHashGeneratorFactory() = default;
protected:
virtual Result DoCreate(std::unique_ptr<IHash256Generator> *out) override {
auto generator = std::unique_ptr<IHash256Generator>(new ShaHashGenerator<Traits>());
R_UNLESS(generator != nullptr, fs::ResultAllocationMemoryFailedNew());
*out = std::move(generator);
R_SUCCEED();
}
virtual void DoGenerateHash(void *dst, size_t dst_size, const void *src, size_t src_size) override {
Traits::Generate(dst, dst_size, src, src_size);
}
};
struct Sha256Traits {
using Generator = crypto::Sha256Generator;
static ALWAYS_INLINE void Generate(void *dst, size_t dst_size, const void *src, size_t src_size) {
return crypto::GenerateSha256(dst, dst_size, src, src_size);
}
};
struct Sha3256Traits {
using Generator = crypto::Sha3256Generator;
static ALWAYS_INLINE void Generate(void *dst, size_t dst_size, const void *src, size_t src_size) {
return crypto::GenerateSha3256(dst, dst_size, src, src_size);
}
};
}
using Sha256HashGenerator = impl::ShaHashGenerator<impl::Sha256Traits>;
using Sha256HashGeneratorFactory = impl::ShaHashGeneratorFactory<impl::Sha256Traits>;
using Sha3256HashGenerator = impl::ShaHashGenerator<impl::Sha3256Traits>;
using Sha3256HashGeneratorFactory = impl::ShaHashGeneratorFactory<impl::Sha3256Traits>;
class ShaHashGeneratorFactorySelector final : public IHash256GeneratorFactorySelector, public ::ams::fs::impl::Newable {
NON_COPYABLE(ShaHashGeneratorFactorySelector);
NON_MOVEABLE(ShaHashGeneratorFactorySelector);
private:
Sha256HashGeneratorFactory m_sha256_factory;
Sha3256HashGeneratorFactory m_sha3_256_factory;
public:
constexpr ShaHashGeneratorFactorySelector() = default;
protected:
virtual IHash256GeneratorFactory *DoGetFactory(HashAlgorithmType alg) override {
switch (alg) {
case HashAlgorithmType_Sha2: return std::addressof(m_sha256_factory);
case HashAlgorithmType_Sha3: return std::addressof(m_sha3_256_factory);
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
};
}

View File

@@ -1,104 +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>
#include <stratosphere/fssystem/fssystem_indirect_storage.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: Unknown */
class SparseStorage : public IndirectStorage {
NON_COPYABLE(SparseStorage);
NON_MOVEABLE(SparseStorage);
private:
class ZeroStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
public:
ZeroStorage() { /* ... */ }
virtual ~ZeroStorage() { /* ... */ }
public:
virtual Result Read(s64 offset, void *buffer, size_t size) override {
AMS_ASSERT(offset >= 0);
AMS_ASSERT(buffer != nullptr || size == 0);
AMS_UNUSED(offset);
if (size > 0) {
std::memset(buffer, 0, size);
}
R_SUCCEED();
}
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
AMS_UNUSED(dst, dst_size, op_id, offset, size, src, src_size);
R_SUCCEED();
}
virtual Result GetSize(s64 *out) override {
AMS_ASSERT(out != nullptr);
*out = std::numeric_limits<s64>::max();
R_SUCCEED();
}
virtual Result Flush() override {
R_SUCCEED();
}
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
AMS_UNUSED(offset, buffer, size);
R_THROW(fs::ResultUnsupportedWriteForZeroStorage());
}
virtual Result SetSize(s64 size) override {
AMS_UNUSED(size);
R_THROW(fs::ResultUnsupportedSetSizeForZeroStorage());
}
};
private:
ZeroStorage m_zero_storage;
public:
SparseStorage() : IndirectStorage(), m_zero_storage() { /* ... */ }
virtual ~SparseStorage() { /* ... */ }
using IndirectStorage::Initialize;
void Initialize(s64 end_offset) {
this->GetEntryTable().Initialize(NodeSize, end_offset);
this->SetZeroStorage();
}
void SetDataStorage(fs::SubStorage storage) {
AMS_ASSERT(this->IsInitialized());
this->SetStorage(0, storage);
this->SetZeroStorage();
}
template<typename T>
void SetDataStorage(T storage, s64 offset, s64 size) {
AMS_ASSERT(this->IsInitialized());
this->SetStorage(0, storage, offset, size);
this->SetZeroStorage();
}
virtual Result Read(s64 offset, void *buffer, size_t size) override;
private:
void SetZeroStorage() {
return this->SetStorage(1, std::addressof(m_zero_storage), 0, std::numeric_limits<s64>::max());
}
};
}

View File

@@ -1,30 +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>
#include <stratosphere/fs/fs_speed_emulation.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: Unknown */
class SpeedEmulationConfiguration {
public:
static void SetSpeedEmulationMode(::ams::fs::SpeedEmulationMode mode);
static ::ams::fs::SpeedEmulationMode GetSpeedEmulationMode();
};
}

View File

@@ -1,177 +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 <stratosphere/fs/fs_common.hpp>
#include <stratosphere/fs/fsa/fs_ifile.hpp>
#include <stratosphere/fs/fsa/fs_idirectory.hpp>
#include <stratosphere/fs/fsa/fs_ifilesystem.hpp>
#include <stratosphere/fs/impl/fs_newable.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: 13.4.0.0 */
class SubDirectoryFileSystem : public fs::fsa::IFileSystem, public fs::impl::Newable {
NON_COPYABLE(SubDirectoryFileSystem);
private:
std::shared_ptr<fs::fsa::IFileSystem> m_shared_fs;
fs::fsa::IFileSystem * const m_base_fs;
fs::Path m_root_path;
public:
SubDirectoryFileSystem(std::shared_ptr<fs::fsa::IFileSystem> fs) : m_shared_fs(std::move(fs)), m_base_fs(m_shared_fs.get()), m_root_path() {
/* ... */
}
SubDirectoryFileSystem(fs::fsa::IFileSystem *fs) : m_shared_fs(), m_base_fs(fs), m_root_path() {
/* ... */
}
Result Initialize(const fs::Path &path) {
R_RETURN(m_root_path.Initialize(path));
}
private:
Result ResolveFullPath(fs::Path *out, const fs::Path &path) {
R_RETURN(out->Combine(m_root_path, path));
}
public:
virtual Result DoCreateFile(const fs::Path &path, s64 size, int option) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->CreateFile(full_path, size, option));
}
virtual Result DoDeleteFile(const fs::Path &path) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->DeleteFile(full_path));
}
virtual Result DoCreateDirectory(const fs::Path &path) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->CreateDirectory(full_path));
}
virtual Result DoDeleteDirectory(const fs::Path &path) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->DeleteDirectory(full_path));
}
virtual Result DoDeleteDirectoryRecursively(const fs::Path &path) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->DeleteDirectoryRecursively(full_path));
}
virtual Result DoRenameFile(const fs::Path &old_path, const fs::Path &new_path) override {
fs::Path old_full_path;
fs::Path new_full_path;
R_TRY(this->ResolveFullPath(std::addressof(old_full_path), old_path));
R_TRY(this->ResolveFullPath(std::addressof(new_full_path), new_path));
R_RETURN(m_base_fs->RenameFile(old_full_path, new_full_path));
}
virtual Result DoRenameDirectory(const fs::Path &old_path, const fs::Path &new_path) override {
fs::Path old_full_path;
fs::Path new_full_path;
R_TRY(this->ResolveFullPath(std::addressof(old_full_path), old_path));
R_TRY(this->ResolveFullPath(std::addressof(new_full_path), new_path));
R_RETURN(m_base_fs->RenameDirectory(old_full_path, new_full_path));
}
virtual Result DoGetEntryType(fs::DirectoryEntryType *out, const fs::Path &path) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->GetEntryType(out, full_path));
}
virtual Result DoOpenFile(std::unique_ptr<fs::fsa::IFile> *out_file, const fs::Path &path, fs::OpenMode mode) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->OpenFile(out_file, full_path, mode));
}
virtual Result DoOpenDirectory(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const fs::Path &path, fs::OpenDirectoryMode mode) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->OpenDirectory(out_dir, full_path, mode));
}
virtual Result DoCommit() override {
R_RETURN(m_base_fs->Commit());
}
virtual Result DoGetFreeSpaceSize(s64 *out, const fs::Path &path) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->GetFreeSpaceSize(out, full_path));
}
virtual Result DoGetTotalSpaceSize(s64 *out, const fs::Path &path) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->GetTotalSpaceSize(out, full_path));
}
virtual Result DoCleanDirectoryRecursively(const fs::Path &path) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->CleanDirectoryRecursively(full_path));
}
virtual Result DoGetFileTimeStampRaw(fs::FileTimeStampRaw *out, const fs::Path &path) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->GetFileTimeStampRaw(out, full_path));
}
virtual Result DoQueryEntry(char *dst, size_t dst_size, const char *src, size_t src_size, fs::fsa::QueryId query, const fs::Path &path) override {
fs::Path full_path;
R_TRY(this->ResolveFullPath(std::addressof(full_path), path));
R_RETURN(m_base_fs->QueryEntry(dst, dst_size, src, src_size, query, full_path));
}
/* These aren't accessible as commands. */
virtual Result DoCommitProvisionally(s64 counter) override {
R_RETURN(m_base_fs->CommitProvisionally(counter));
}
virtual Result DoRollback() override {
R_RETURN(m_base_fs->Rollback());
}
virtual Result DoFlush() override {
R_RETURN(m_base_fs->Flush());
}
};
}

View File

@@ -1,218 +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>
#include <stratosphere/fs/fs_istorage.hpp>
#include <stratosphere/fs/impl/fs_newable.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: 14.3.0.0 */
template<typename F>
class SwitchStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
NON_COPYABLE(SwitchStorage);
NON_MOVEABLE(SwitchStorage);
private:
std::shared_ptr<fs::IStorage> m_true_storage;
std::shared_ptr<fs::IStorage> m_false_storage;
F m_truth_function;
private:
ALWAYS_INLINE std::shared_ptr<fs::IStorage> &SelectStorage() {
return m_truth_function() ? m_true_storage : m_false_storage;
}
public:
SwitchStorage(std::shared_ptr<fs::IStorage> t, std::shared_ptr<fs::IStorage> f, F func) : m_true_storage(std::move(t)), m_false_storage(std::move(f)), m_truth_function(func) { /* ... */ }
virtual Result Read(s64 offset, void *buffer, size_t size) override {
R_RETURN(this->SelectStorage()->Read(offset, buffer, size));
}
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
switch (op_id) {
case fs::OperationId::Invalidate:
{
R_TRY(m_true_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
R_TRY(m_false_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
R_SUCCEED();
}
case fs::OperationId::QueryRange:
{
R_TRY(this->SelectStorage()->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
R_SUCCEED();
}
default:
R_THROW(fs::ResultUnsupportedOperateRangeForSwitchStorage());
}
}
virtual Result GetSize(s64 *out) override {
R_RETURN(this->SelectStorage()->GetSize(out));
}
virtual Result Flush() override {
R_RETURN(this->SelectStorage()->Flush());
}
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
R_RETURN(this->SelectStorage()->Write(offset, buffer, size));
}
virtual Result SetSize(s64 size) override {
R_RETURN(this->SelectStorage()->SetSize(size));
}
};
class RegionSwitchStorage : public ::ams::fs::IStorage, public ::ams::fs::impl::Newable {
NON_COPYABLE(RegionSwitchStorage);
NON_MOVEABLE(RegionSwitchStorage);
public:
struct Region {
s64 offset;
s64 size;
};
private:
std::shared_ptr<fs::IStorage> m_inside_region_storage;
std::shared_ptr<fs::IStorage> m_outside_region_storage;
Region m_region;
public:
RegionSwitchStorage(std::shared_ptr<fs::IStorage> &&i, std::shared_ptr<fs::IStorage> &&o, Region r) : m_inside_region_storage(std::move(i)), m_outside_region_storage(std::move(o)), m_region(r) {
/* ... */
}
virtual Result Read(s64 offset, void *buffer, size_t size) override {
/* Process until we're done. */
size_t processed = 0;
while (processed < size) {
/* Process on the appropriate storage. */
s64 cur_size = 0;
if (this->CheckRegions(std::addressof(cur_size), offset + processed, size - processed)) {
R_TRY(m_inside_region_storage->Read(offset + processed, static_cast<u8 *>(buffer) + processed, cur_size));
} else {
R_TRY(m_outside_region_storage->Read(offset + processed, static_cast<u8 *>(buffer) + processed, cur_size));
}
/* Advance. */
processed += cur_size;
}
R_SUCCEED();
}
virtual Result Write(s64 offset, const void *buffer, size_t size) override {
/* Process until we're done. */
size_t processed = 0;
while (processed < size) {
/* Process on the appropriate storage. */
s64 cur_size = 0;
if (this->CheckRegions(std::addressof(cur_size), offset + processed, size - processed)) {
R_TRY(m_inside_region_storage->Write(offset + processed, static_cast<const u8 *>(buffer) + processed, cur_size));
} else {
R_TRY(m_outside_region_storage->Write(offset + processed, static_cast<const u8 *>(buffer) + processed, cur_size));
}
/* Advance. */
processed += cur_size;
}
R_SUCCEED();
}
virtual Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override {
switch (op_id) {
case fs::OperationId::Invalidate:
{
R_TRY(m_inside_region_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
R_TRY(m_outside_region_storage->OperateRange(dst, dst_size, op_id, offset, size, src, src_size));
R_SUCCEED();
}
case fs::OperationId::QueryRange:
{
/* Create a new info. */
fs::QueryRangeInfo merged_info;
merged_info.Clear();
/* Process until we're done. */
s64 processed = 0;
while (processed < size) {
/* Process on the appropriate storage. */
s64 cur_size = 0;
fs::QueryRangeInfo cur_info;
if (this->CheckRegions(std::addressof(cur_size), offset + processed, size - processed)) {
R_TRY(m_inside_region_storage->OperateRange(std::addressof(cur_info), sizeof(cur_info), op_id, offset + processed, cur_size, src, src_size));
} else {
R_TRY(m_outside_region_storage->OperateRange(std::addressof(cur_info), sizeof(cur_info), op_id, offset + processed, cur_size, src, src_size));
}
/* Merge the current info. */
merged_info.Merge(cur_info);
/* Advance. */
processed += cur_size;
}
/* Write the merged info. */
*reinterpret_cast<fs::QueryRangeInfo *>(dst) = merged_info;
R_SUCCEED();
}
default:
R_THROW(fs::ResultUnsupportedOperateRangeForRegionSwitchStorage());
}
}
virtual Result GetSize(s64 *out) override {
R_RETURN(m_inside_region_storage->GetSize(out));
}
virtual Result Flush() override {
/* Flush both storages. */
R_TRY(m_inside_region_storage->Flush());
R_TRY(m_outside_region_storage->Flush());
R_SUCCEED();
}
virtual Result SetSize(s64 size) override {
/* Set size for both storages. */
R_TRY(m_inside_region_storage->SetSize(size));
R_TRY(m_outside_region_storage->SetSize(size));
R_SUCCEED();
}
private:
bool CheckRegions(s64 *out_current_size, s64 offset, s64 size) const {
/* Check if our region contains the access. */
if (m_region.offset <= offset) {
if (offset < m_region.offset + m_region.size) {
if (m_region.offset + m_region.size <= offset + size) {
*out_current_size = m_region.offset + m_region.size - offset;
} else {
*out_current_size = size;
}
return true;
} else {
*out_current_size = size;
return false;
}
} else {
if (m_region.offset <= offset + size) {
*out_current_size = m_region.offset - offset;
} else {
*out_current_size = size;
}
return false;
}
}
};
}

View File

@@ -1,60 +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>
#include <stratosphere/os.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: Unknown */
class ScopedThreadPriorityChanger {
public:
enum class Mode {
Absolute,
Relative,
};
private:
os::ThreadType *m_thread;
s32 m_priority;
public:
ALWAYS_INLINE explicit ScopedThreadPriorityChanger(s32 priority, Mode mode) : m_thread(os::GetCurrentThread()), m_priority(0) {
const auto result_priority = std::min((mode == Mode::Relative) ? os::GetThreadPriority(m_thread) + priority : priority, os::LowestSystemThreadPriority);
m_priority = os::ChangeThreadPriority(m_thread, result_priority);
}
ALWAYS_INLINE ~ScopedThreadPriorityChanger() {
os::ChangeThreadPriority(m_thread, m_priority);
}
};
class ScopedThreadPriorityChangerByAccessPriority {
public:
enum class AccessMode {
Read,
Write,
};
private:
static s32 GetThreadPriorityByAccessPriority(AccessMode mode);
private:
ScopedThreadPriorityChanger m_scoped_changer;
public:
ALWAYS_INLINE explicit ScopedThreadPriorityChangerByAccessPriority(AccessMode mode) : m_scoped_changer(GetThreadPriorityByAccessPriority(mode), ScopedThreadPriorityChanger::Mode::Absolute) {
/* ... */
}
};
}

View File

@@ -1,252 +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 <stratosphere/fs/fs_common.hpp>
#include <stratosphere/fs/fs_file.hpp>
#include <stratosphere/fs/fs_directory.hpp>
#include <stratosphere/fs/fs_filesystem.hpp>
#include <stratosphere/fs/fs_path.hpp>
namespace ams::fssystem {
/* ACCURATE_TO_VERSION: 13.4.0.0 */
namespace impl {
template<typename F>
concept IterateDirectoryHandler = requires (F f, const fs::Path &path, const fs::DirectoryEntry &entry) {
{ f(path, entry) } -> std::convertible_to<::ams::Result>;
};
/* Iteration. */
template<IterateDirectoryHandler OnEnterDir, IterateDirectoryHandler OnExitDir, IterateDirectoryHandler OnFile>
Result IterateDirectoryRecursivelyImpl(fs::fsa::IFileSystem *fs, fs::Path &work_path, fs::DirectoryEntry *dir_ent, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
/* Open the directory. */
std::unique_ptr<fs::fsa::IDirectory> dir;
R_TRY(fs->OpenDirectory(std::addressof(dir), work_path, fs::OpenDirectoryMode_All));
/* Read and handle entries. */
while (true) {
/* Read a single entry. */
s64 read_count = 0;
R_TRY(dir->Read(std::addressof(read_count), dir_ent, 1));
/* If we're out of entries, we're done. */
if (read_count == 0) {
break;
}
/* Append child path. */
R_TRY(work_path.AppendChild(dir_ent->name));
{
if (dir_ent->type == fs::DirectoryEntryType_Directory) {
/* Enter directory. */
R_TRY(on_enter_dir(work_path, *dir_ent));
/* Recurse. */
R_TRY(IterateDirectoryRecursivelyImpl(fs, work_path, dir_ent, on_enter_dir, on_exit_dir, on_file));
/* Exit directory. */
R_TRY(on_exit_dir(work_path, *dir_ent));
} else {
/* Call file handler. */
R_TRY(on_file(work_path, *dir_ent));
}
}
R_TRY(work_path.RemoveChild());
}
R_SUCCEED();
}
/* TODO: Cleanup. */
}
/* Iteration API */
template<impl::IterateDirectoryHandler OnEnterDir, impl::IterateDirectoryHandler OnExitDir, impl::IterateDirectoryHandler OnFile>
Result IterateDirectoryRecursively(fs::fsa::IFileSystem *fs, const fs::Path &root_path, fs::DirectoryEntry *dir_ent_buf, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
/* Create work path from the root path. */
fs::Path work_path;
R_TRY(work_path.Initialize(root_path));
R_RETURN(impl::IterateDirectoryRecursivelyImpl(fs, work_path, dir_ent_buf, on_enter_dir, on_exit_dir, on_file));
}
template<impl::IterateDirectoryHandler OnEnterDir, impl::IterateDirectoryHandler OnExitDir, impl::IterateDirectoryHandler OnFile>
Result IterateDirectoryRecursively(fs::fsa::IFileSystem *fs, const fs::Path &root_path, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
fs::DirectoryEntry dir_entry = {};
R_RETURN(IterateDirectoryRecursively(fs, root_path, std::addressof(dir_entry), on_enter_dir, on_exit_dir, on_file));
}
template<impl::IterateDirectoryHandler OnEnterDir, impl::IterateDirectoryHandler OnExitDir, impl::IterateDirectoryHandler OnFile>
Result IterateDirectoryRecursively(fs::fsa::IFileSystem *fs, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
R_RETURN(IterateDirectoryRecursively(fs, fs::MakeConstantPath("/"), on_enter_dir, on_exit_dir, on_file));
}
/* TODO: Cleanup API */
/* Copy API. */
Result CopyFile(fs::fsa::IFileSystem *dst_fs, fs::fsa::IFileSystem *src_fs, const fs::Path &dst_path, const fs::Path &src_path, void *work_buf, size_t work_buf_size);
ALWAYS_INLINE Result CopyFile(fs::fsa::IFileSystem *fs, const fs::Path &dst_path, const fs::Path &src_path, void *work_buf, size_t work_buf_size) {
R_RETURN(CopyFile(fs, fs, dst_path, src_path, work_buf, work_buf_size));
}
Result CopyDirectoryRecursively(fs::fsa::IFileSystem *dst_fs, fs::fsa::IFileSystem *src_fs, const fs::Path &dst_path, const fs::Path &src_path, fs::DirectoryEntry *entry, void *work_buf, size_t work_buf_size);
ALWAYS_INLINE Result CopyDirectoryRecursively(fs::fsa::IFileSystem *fs, const fs::Path &dst_path, const fs::Path &src_path, fs::DirectoryEntry *entry, void *work_buf, size_t work_buf_size) {
R_RETURN(CopyDirectoryRecursively(fs, fs, dst_path, src_path, entry, work_buf, work_buf_size));
}
/* Locking utilities. */
class SemaphoreAdaptor : public os::Semaphore {
public:
SemaphoreAdaptor(int c, int mc) : os::Semaphore(c, mc) { /* ... */ }
bool TryLock(int *out_acquired, int count) {
AMS_ASSERT(count > 0);
for (auto i = 0; i < count; ++i) {
if (!this->TryAcquire()) {
*out_acquired = i;
return false;
}
}
*out_acquired = count;
return true;
}
void Unlock(int count) {
if (count > 0) {
this->Release(count);
}
}
bool try_lock() {
return this->TryAcquire();
}
void unlock() {
this->Release();
}
};
Result TryAcquireCountSemaphore(util::unique_lock<SemaphoreAdaptor> *out, SemaphoreAdaptor *adaptor);
class IUniqueLock {
NON_COPYABLE(IUniqueLock);
NON_MOVEABLE(IUniqueLock);
public:
virtual ~IUniqueLock() { /* ... */ }
};
template<typename T>
class UniqueLockWithPin final : public IUniqueLock, public ::ams::fs::impl::Newable {
private:
util::unique_lock<SemaphoreAdaptor> m_lock;
T m_pinned_object;
public:
UniqueLockWithPin(util::unique_lock<SemaphoreAdaptor> lock, T obj) : m_lock(std::move(lock)), m_pinned_object(std::move(obj)) { /* ... */ }
virtual ~UniqueLockWithPin() override {
m_lock = {};
}
};
template<typename T>
class MultiLockWithPin final : public IUniqueLock, public ::ams::fs::impl::Newable {
private:
T m_pinned_object;
SemaphoreAdaptor *m_semaphore_adaptor;
int m_lock_count;
public:
MultiLockWithPin(T obj, SemaphoreAdaptor *adaptor) : m_pinned_object(std::move(obj)), m_semaphore_adaptor(adaptor), m_lock_count(0) {
/* ... */
}
virtual ~MultiLockWithPin() override {
if (m_lock_count > 0) {
m_semaphore_adaptor->Unlock(m_lock_count);
}
}
Result Lock(int count) {
AMS_ASSERT(m_lock_count == 0);
R_UNLESS(m_semaphore_adaptor->TryLock(std::addressof(m_lock_count), count), fs::ResultOpenCountLimit());
R_SUCCEED();
}
};
template<typename T>
Result MakeUniqueLockWithPin(std::unique_ptr<IUniqueLock> *out, SemaphoreAdaptor *adaptor, T obj) {
/* Create the semaphore unique lock. */
util::unique_lock<SemaphoreAdaptor> sema_lock;
R_TRY(TryAcquireCountSemaphore(std::addressof(sema_lock), adaptor));
/* Create the output unique lock. */
auto result_lock = std::unique_ptr<UniqueLockWithPin<T>>(new UniqueLockWithPin<T>(std::move(sema_lock), std::move(obj)));
R_UNLESS(result_lock != nullptr, fs::ResultAllocationMemoryFailedNew());
/* Set the output. */
*out = std::move(result_lock);
R_SUCCEED();
}
template<typename T>
Result MakeUniqueLockWithPin(std::unique_ptr<IUniqueLock> *out, SemaphoreAdaptor *adaptor, int count, T obj) {
/* Create the output unique lock. */
auto result_lock = std::unique_ptr<MultiLockWithPin<T>>(new MultiLockWithPin<T>(std::move(obj), adaptor));
R_UNLESS(result_lock != nullptr, fs::ResultAllocationMemoryFailedNew());
/* Acquire the output lock. */
R_TRY(result_lock->Lock(count));
/* Set the output. */
*out = std::move(result_lock);
R_SUCCEED();
}
/* Other utility. */
Result HasFile(bool *out, fs::fsa::IFileSystem *fs, const fs::Path &path);
Result HasDirectory(bool *out, fs::fsa::IFileSystem *fs, const fs::Path &path);
Result EnsureDirectory(fs::fsa::IFileSystem *fs, const fs::Path &path);
template<s64 RetryMilliSeconds = 100, s32 MaxTryCount = 10>
ALWAYS_INLINE Result RetryFinitelyForTargetLocked(auto f) {
/* Retry sleeping between retries. */
constexpr TimeSpan RetryWaitTime = TimeSpan::FromMilliSeconds(RetryMilliSeconds);
Result result = f();
for (int i = 0; i < MaxTryCount && fs::ResultTargetLocked::Includes(result); ++i) {
os::SleepThread(RetryWaitTime);
result = f();
}
R_RETURN(result);
}
ALWAYS_INLINE Result RetryToAvoidTargetLocked(auto f) {
R_RETURN((RetryFinitelyForTargetLocked<2, 25>(f)));
}
void AddCounter(void *counter, size_t counter_size, u64 value);
}

View File

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

View File

@@ -1,23 +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::save {
/* TODO */
}

View File

@@ -1,23 +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::save {
/* TODO */
}