diff --git a/libraries/libstratosphere/include/stratosphere/fssystem.hpp b/libraries/libstratosphere/include/stratosphere/fssystem.hpp index de899e024..1a7c20e6d 100644 --- a/libraries/libstratosphere/include/stratosphere/fssystem.hpp +++ b/libraries/libstratosphere/include/stratosphere/fssystem.hpp @@ -17,6 +17,7 @@ #pragma once #include "fssystem/fssystem_utility.hpp" #include "fssystem/fssystem_external_code.hpp" +#include "fssystem/fssystem_partition_file_system_meta.hpp" #include "fssystem/fssystem_path_tool.hpp" #include "fssystem/fssystem_subdirectory_filesystem.hpp" #include "fssystem/fssystem_directory_redirection_filesystem.hpp" diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_partition_file_system_meta.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_partition_file_system_meta.hpp new file mode 100644 index 000000000..ee8e89472 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_partition_file_system_meta.hpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#pragma once +#include +#include + +namespace ams::fssystem { + + namespace impl { + + struct PartitionFileSystemFormat { + #pragma pack(push, 1) + struct PartitionEntry { + u64 offset; + u64 size; + u32 name_offset; + u32 reserved; + }; + static_assert(std::is_pod::value); + #pragma pack(pop) + + static constexpr char VersionSignature[] = { 'P', 'F', 'S', '0' }; + + static constexpr size_t EntryNameLengthMax = ::ams::fs::EntryNameLengthMax; + static constexpr size_t FileDataAlignmentSize = 0x20; + }; + + } + + template + 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; + using ResultSignatureVerificationFailed = fs::ResultSha256PartitionSignatureVerificationFailed; + protected: + bool initialized; + PartitionFileSystemHeader *header; + PartitionEntry *entries; + char *name_table; + size_t meta_data_size; + MemoryResource *allocator; + char *buffer; + public: + PartitionFileSystemMetaCore() { /* ... */ } + ~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; + Result QueryMetaDataSize(size_t *out_size, fs::IStorage *storage) const; + protected: + void DeallocateBuffer(); + }; + + using PartitionFileSystemMeta = PartitionFileSystemMetaCore; + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_partition_file_system_meta.cpp b/libraries/libstratosphere/source/fssystem/fssystem_partition_file_system_meta.cpp new file mode 100644 index 000000000..4e7aeb87a --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/fssystem_partition_file_system_meta.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#include + +namespace ams::fssystem { + + template + struct PartitionFileSystemMetaCore::PartitionFileSystemHeader { + u32 signature; + s32 entry_count; + u32 name_table_size; + u32 reserved; + }; + + template + PartitionFileSystemMetaCore::~PartitionFileSystemMetaCore() { + this->DeallocateBuffer(); + } + + template + Result PartitionFileSystemMetaCore::Initialize(fs::IStorage *storage, MemoryResource *allocator) { + /* Determine the meta data size. */ + R_TRY(this->QueryMetaDataSize(std::addressof(this->meta_data_size), storage)); + + /* Deallocate any old meta buffer and allocate a new one. */ + this->DeallocateBuffer(); + this->allocator = allocator; + this->buffer = reinterpret_cast(this->allocator->Allocate(this->meta_data_size)); + R_UNLESS(this->buffer != nullptr, fs::ResultAllocationFailureInPartitionFileSystemMetaCore()); + + /* Perform regular initialization. */ + return this->Initialize(storage, this->buffer, this->meta_data_size); + } + + template + Result PartitionFileSystemMetaCore::Initialize(fs::IStorage *storage, void *meta, size_t meta_size) { + /* Validate size for header. */ + R_UNLESS(meta_size >= sizeof(PartitionFileSystemHeader), fs::ResultInvalidSize()); + + /* Read the header. */ + R_TRY(storage->Read(0, meta, sizeof(PartitionFileSystemHeader))); + + /* Set and validate the header. */ + this->header = reinterpret_cast(meta); + R_UNLESS(crypto::IsSameBytes(this->header, Format::VersionSignature, sizeof(Format::VersionSignature)), ResultSignatureVerificationFailed()); + + /* Setup entries and name table. */ + const size_t entries_size = this->header->entry_count * sizeof(typename Format::PartitionEntry); + this->entries = reinterpret_cast(reinterpret_cast(meta) + sizeof(PartitionFileSystemHeader)); + this->name_table = reinterpret_cast(meta) + sizeof(PartitionFileSystemHeader) + entries_size; + + /* Validate size for header + entries + name table. */ + R_UNLESS(meta_size >= sizeof(PartitionFileSystemHeader) + entries_size + this->header->name_table_size, fs::ResultInvalidSize()); + + /* Read entries and name table. */ + R_TRY(storage->Read(sizeof(PartitionFileSystemHeader), this->entries, entries_size + this->header->name_table_size)); + + /* Mark as initialized. */ + this->initialized = true; + return ResultSuccess(); + } + + template + void PartitionFileSystemMetaCore::DeallocateBuffer() { + if (this->buffer != nullptr) { + AMS_ABORT_UNLESS(this->allocator != nullptr); + this->allocator->Deallocate(this->buffer, this->meta_data_size); + this->buffer = nullptr; + } + } + + template + const typename Format::PartitionEntry *PartitionFileSystemMetaCore::GetEntry(s32 index) const { + if (this->initialized && index >= 0 && index < this->header->entry_count) { + return std::addressof(this->entries[index]); + } + return nullptr; + } + + template + s32 PartitionFileSystemMetaCore::GetEntryCount() const { + if (this->initialized) { + return this->header->entry_count; + } + return 0; + } + + template + s32 PartitionFileSystemMetaCore::GetEntryIndex(const char *name) const { + if (this->initialized) { + for (s32 i = 0; i < this->header->entry_count; i++) { + const auto &entry = this->entries[i]; + + /* Name offset is invalid. */ + if (entry.name_offset >= this->header->name_table_size) { + return 0; + } + + /* Compare to input name. */ + const s32 max_count = this->header->name_table_size - entry.name_offset; + if (std::strncmp(std::addressof(this->name_table[entry.name_offset]), name, max_count) == 0) { + return i; + } + } + + /* Not found. */ + return -1; + } + + return 0; + } + + template + const char *PartitionFileSystemMetaCore::GetEntryName(s32 index) const { + if (this->initialized && index < this->header->entry_count) { + return std::addressof(this->name_table[this->GetEntry(index)->name_offset]); + } + return nullptr; + } + + template + size_t PartitionFileSystemMetaCore::GetHeaderSize() const { + return sizeof(PartitionFileSystemHeader); + } + + template + size_t PartitionFileSystemMetaCore::GetMetaDataSize() const { + return this->meta_data_size; + } + + template + Result PartitionFileSystemMetaCore::QueryMetaDataSize(size_t *out_size, fs::IStorage *storage) const { + AMS_ABORT_UNLESS(allocator != nullptr); + + /* Read and validate the header. */ + PartitionFileSystemHeader header; + R_TRY(storage->Read(0, std::addressof(header), sizeof(PartitionFileSystemHeader))); + R_UNLESS(crypto::IsSameBytes(std::addressof(header), Format::VersionSignature, sizeof(Format::VersionSignature)), ResultSignatureVerificationFailed()); + + /* Output size. */ + *out_size = sizeof(PartitionFileSystemHeader) + header.entry_count * sizeof(typename Format::PartitionEntry) + header.name_table_size; + return ResultSuccess(); + } + + template class PartitionFileSystemMetaCore; + +} diff --git a/libraries/libvapours/include/vapours.hpp b/libraries/libvapours/include/vapours.hpp index d51797e3a..9da48965b 100644 --- a/libraries/libvapours/include/vapours.hpp +++ b/libraries/libvapours/include/vapours.hpp @@ -19,6 +19,7 @@ #include #include +#include #include #include diff --git a/libraries/libvapours/include/vapours/allocator.hpp b/libraries/libvapours/include/vapours/allocator.hpp new file mode 100644 index 000000000..446706b56 --- /dev/null +++ b/libraries/libvapours/include/vapours/allocator.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#pragma once +#include +#include + +namespace ams { + + constexpr inline size_t DefaultAlignment = alignof(max_align_t); + + using AllocateFunction = void *(*)(size_t); + using AllocateFunctionWithUserData = void *(*)(size_t, void *); + using AlignedAllocateFunction = void *(*)(size_t, size_t); + using AlignedAllocateFunctionWithUserData = void *(*)(size_t, size_t, void *); + using DeallocateFunction = void (*)(void *, size_t); + using FreeFunction = void (*)(void *); + using FreeFunctionWithUserData = void (*)(void *, void *); + + class MemoryResource { + public: + ALWAYS_INLINE void *allocate(size_t size, size_t alignment = DefaultAlignment) { + return this->AllocateImpl(size, alignment); + } + ALWAYS_INLINE void deallocate(void *buffer, size_t size, size_t alignment = DefaultAlignment) { + this->DeallocateImpl(buffer, size, alignment); + } + ALWAYS_INLINE bool is_equal(const MemoryResource &resource) const { + return this->IsEqualImpl(resource); + } + ALWAYS_INLINE void *Allocate(size_t size, size_t alignment = DefaultAlignment) { + return this->AllocateImpl(size, alignment); + } + ALWAYS_INLINE void Deallocate(void *buffer, size_t size, size_t alignment = DefaultAlignment) { + this->DeallocateImpl(buffer, size, alignment); + } + ALWAYS_INLINE bool IsEqual(const MemoryResource &resource) const { + return this->IsEqualImpl(resource); + } + protected: + virtual void *AllocateImpl(size_t size, size_t alignment) = 0; + virtual void DeallocateImpl(void *buffer, size_t size, size_t alignment) = 0; + virtual bool IsEqualImpl(const MemoryResource &resource) const = 0; + }; + +} diff --git a/libraries/libvapours/include/vapours/results/fs_results.hpp b/libraries/libvapours/include/vapours/results/fs_results.hpp index 2f5d74fd6..76f77445d 100644 --- a/libraries/libvapours/include/vapours/results/fs_results.hpp +++ b/libraries/libvapours/include/vapours/results/fs_results.hpp @@ -75,6 +75,7 @@ namespace ams::fs { R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemB, 3248); R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemC, 3249); R_DEFINE_ERROR_RESULT(AllocationFailureInDirectorySaveDataFileSystem, 3321); + R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemMetaCore, 3350); R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemD, 3352); R_DEFINE_ERROR_RESULT(AllocationFailureInSubDirectoryFileSystem, 3355); R_DEFINE_ERROR_RESULT(AllocationFailureInRegisterA, 3365);