Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"
This reverts commit 15b7df8ef1.
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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>>;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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>>;
|
||||
|
||||
}
|
||||
@@ -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>>;
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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> ¶m) 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);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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> ¶m) 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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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>;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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)); \
|
||||
} \
|
||||
}; \
|
||||
\
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
/* ... */
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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 */
|
||||
|
||||
}
|
||||
@@ -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 */
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user