mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
fssystem: Implement PartitionFileSystemCore (#856)
* fssystem: implement PartitionFileSystemMetaCore * fssystem: PartitionFileSystemMetaCore cleanup * fs: add IFile::DryWrite, update results * fssystem: implement PartitionFileSystemCore * fssystem: cleanup PartitionFileSystemCore * fssystem: implement Sha256PartitionFileSystem Co-authored-by: Michael Scire <SciresM@gmail.com>
This commit is contained in:
parent
0af2758fde
commit
3d518759da
9 changed files with 949 additions and 2 deletions
|
@ -85,7 +85,7 @@ namespace ams::fs::fsa {
|
|||
protected:
|
||||
Result DryRead(size_t *out, s64 offset, size_t size, const fs::ReadOption &option, OpenMode open_mode) {
|
||||
/* Check that we can read. */
|
||||
R_UNLESS((open_mode & OpenMode_Read) != 0, fs::ResultInvalidOperationForOpenMode());
|
||||
R_UNLESS((open_mode & OpenMode_Read) != 0, fs::ResultReadNotPermitted());
|
||||
|
||||
/* Get the file size, and validate our offset. */
|
||||
s64 file_size = 0;
|
||||
|
@ -98,12 +98,31 @@ namespace ams::fs::fsa {
|
|||
|
||||
Result DrySetSize(s64 size, fs::OpenMode open_mode) {
|
||||
/* Check that we can write. */
|
||||
R_UNLESS((open_mode & OpenMode_Write) != 0, fs::ResultInvalidOperationForOpenMode());
|
||||
R_UNLESS((open_mode & OpenMode_Write) != 0, fs::ResultWriteNotPermitted());
|
||||
|
||||
AMS_ASSERT(size >= 0);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result DryWrite(bool *out_append, s64 offset, size_t size, const fs::WriteOption &option, fs::OpenMode open_mode) {
|
||||
/* Check that we can write. */
|
||||
R_UNLESS((open_mode & OpenMode_Write) != 0, fs::ResultWriteNotPermitted());
|
||||
|
||||
/* Get the file size. */
|
||||
s64 file_size = 0;
|
||||
R_TRY(this->GetSize(&file_size));
|
||||
|
||||
/* Determine if we need to append. */
|
||||
if (file_size < offset + static_cast<s64>(size)) {
|
||||
R_UNLESS((open_mode & OpenMode_AllowAppend) != 0, fs::ResultFileExtensionWithoutOpenModeAllowAppend());
|
||||
*out_append = true;
|
||||
} else {
|
||||
*out_append = false;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
private:
|
||||
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) = 0;
|
||||
virtual Result GetSizeImpl(s64 *out) = 0;
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#pragma once
|
||||
#include "fssystem/fssystem_utility.hpp"
|
||||
#include "fssystem/fssystem_external_code.hpp"
|
||||
#include "fssystem/fssystem_partition_file_system.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"
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Adubbz, 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 "fssystem_partition_file_system_meta.hpp"
|
||||
#include "../fs/fsa/fs_ifile.hpp"
|
||||
#include "../fs/fsa/fs_idirectory.hpp"
|
||||
#include "../fs/fsa/fs_ifilesystem.hpp"
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
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 *base_storage;
|
||||
MetaType *meta_data;
|
||||
bool initialized;
|
||||
size_t meta_data_size;
|
||||
std::unique_ptr<MetaType> unique_meta_data;
|
||||
std::shared_ptr<fs::IStorage> 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 CreateFileImpl(const char *path, s64 size, int option) override;
|
||||
virtual Result DeleteFileImpl(const char *path) override;
|
||||
virtual Result CreateDirectoryImpl(const char *path) override;
|
||||
virtual Result DeleteDirectoryImpl(const char *path) override;
|
||||
virtual Result DeleteDirectoryRecursivelyImpl(const char *path) override;
|
||||
virtual Result RenameFileImpl(const char *old_path, const char *new_path) override;
|
||||
virtual Result RenameDirectoryImpl(const char *old_path, const char *new_path) override;
|
||||
virtual Result GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) override;
|
||||
virtual Result OpenFileImpl(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) override;
|
||||
virtual Result OpenDirectoryImpl(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) override;
|
||||
virtual Result CommitImpl() override;
|
||||
virtual Result CleanDirectoryRecursivelyImpl(const char *path) override;
|
||||
|
||||
/* These aren't accessible as commands. */
|
||||
virtual Result CommitProvisionallyImpl(s64 counter) override;
|
||||
};
|
||||
|
||||
using PartitionFileSystem = PartitionFileSystemCore<PartitionFileSystemMeta>;
|
||||
using Sha256PartitionFileSystem = PartitionFileSystemCore<Sha256PartitionFileSystemMeta>;
|
||||
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Adubbz, 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 {
|
||||
|
||||
namespace impl {
|
||||
|
||||
struct PartitionFileSystemFormat {
|
||||
#pragma pack(push, 1)
|
||||
struct PartitionEntry {
|
||||
u64 offset;
|
||||
u64 size;
|
||||
u32 name_offset;
|
||||
u32 reserved;
|
||||
};
|
||||
static_assert(std::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(std::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 initialized;
|
||||
PartitionFileSystemHeader *header;
|
||||
PartitionEntry *entries;
|
||||
char *name_table;
|
||||
size_t meta_data_size;
|
||||
MemoryResource *allocator;
|
||||
char *buffer;
|
||||
public:
|
||||
PartitionFileSystemMetaCore() : initialized(false), allocator(nullptr), 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, std::optional<u8> suffix = std::nullopt);
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,450 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Adubbz, 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/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
namespace {
|
||||
|
||||
class PartitionFileSystemDefaultAllocator : public MemoryResource {
|
||||
private:
|
||||
virtual void *AllocateImpl(size_t size, size_t alignment) override {
|
||||
return ::ams::fs::impl::Allocate(size);
|
||||
}
|
||||
|
||||
virtual void DeallocateImpl(void *buffer, size_t size, size_t alignment) override {
|
||||
::ams::fs::impl::Deallocate(buffer, size);
|
||||
}
|
||||
|
||||
virtual bool IsEqualImpl(const MemoryResource &rhs) const override {
|
||||
return this == std::addressof(rhs);
|
||||
}
|
||||
};
|
||||
|
||||
PartitionFileSystemDefaultAllocator g_partition_filesystem_default_allocator;
|
||||
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
class PartitionFileSystemCore<MetaType>::PartitionFile : public fs::fsa::IFile, public fs::impl::Newable {
|
||||
private:
|
||||
const typename MetaType::PartitionEntry *partition_entry;
|
||||
const PartitionFileSystemCore<MetaType> *parent;
|
||||
const fs::OpenMode mode;
|
||||
public:
|
||||
PartitionFile(PartitionFileSystemCore<MetaType> *parent, const typename MetaType::PartitionEntry *partition_entry, fs::OpenMode mode) : partition_entry(partition_entry), parent(parent), mode(mode) { /* ... */ }
|
||||
private:
|
||||
virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final;
|
||||
|
||||
virtual Result GetSizeImpl(s64 *out) override final {
|
||||
*out = this->partition_entry->size;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result FlushImpl() override final {
|
||||
/* Nothing to do if writing disallowed. */
|
||||
R_SUCCEED_IF((this->mode & fs::OpenMode_Write) == 0);
|
||||
|
||||
/* Flush base storage. */
|
||||
return this->parent->base_storage->Flush();
|
||||
}
|
||||
|
||||
virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final {
|
||||
/* Ensure appending is not required. */
|
||||
bool needs_append;
|
||||
R_TRY(this->DryWrite(std::addressof(needs_append), offset, size, option, this->mode));
|
||||
R_UNLESS(!needs_append, fs::ResultUnsupportedOperationInPartitionFileA());
|
||||
|
||||
/* Appending is prohibited. */
|
||||
AMS_ASSERT((this->mode & fs::OpenMode_AllowAppend) == 0);
|
||||
|
||||
/* Validate offset and size. */
|
||||
R_UNLESS(offset <= static_cast<s64>(this->partition_entry->size), fs::ResultOutOfRange());
|
||||
R_UNLESS(static_cast<s64>(offset + size) <= static_cast<s64>(this->partition_entry->size), fs::ResultInvalidSize());
|
||||
|
||||
/* Write to the base storage. */
|
||||
return this->parent->base_storage->Write(this->parent->meta_data_size + this->partition_entry->offset + offset, buffer, size);
|
||||
}
|
||||
|
||||
virtual Result SetSizeImpl(s64 size) override final {
|
||||
R_TRY(this->DrySetSize(size, this->mode));
|
||||
return fs::ResultUnsupportedOperationInPartitionFileA();
|
||||
}
|
||||
|
||||
virtual Result OperateRangeImpl(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final {
|
||||
/* Validate preconditions for operation. */
|
||||
switch (op_id) {
|
||||
case fs::OperationId::InvalidateCache:
|
||||
R_UNLESS((this->mode & fs::OpenMode_Read) != 0, fs::ResultReadNotPermitted());
|
||||
R_UNLESS((this->mode & fs::OpenMode_Write) == 0, fs::ResultUnsupportedOperationInPartitionFileB());
|
||||
break;
|
||||
case fs::OperationId::QueryRange:
|
||||
break;
|
||||
default:
|
||||
return fs::ResultUnsupportedOperationInPartitionFileB();
|
||||
}
|
||||
|
||||
/* Validate offset and size. */
|
||||
R_UNLESS(offset >= 0, fs::ResultOutOfRange());
|
||||
R_UNLESS(offset <= static_cast<s64>(this->partition_entry->size), fs::ResultOutOfRange());
|
||||
R_UNLESS(static_cast<s64>(offset + size) <= static_cast<s64>(this->partition_entry->size), fs::ResultInvalidSize());
|
||||
R_UNLESS(static_cast<s64>(offset + size) >= offset, fs::ResultInvalidSize());
|
||||
|
||||
return this->parent->base_storage->OperateRange(dst, dst_size, op_id, this->parent->meta_data_size + this->partition_entry->offset + offset, size, src, src_size);
|
||||
}
|
||||
public:
|
||||
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
|
||||
/* TODO: How should this be handled? */
|
||||
return sf::cmif::InvalidDomainObjectId;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
Result PartitionFileSystemCore<PartitionFileSystemMeta>::PartitionFile::ReadImpl(size_t *out, s64 offset, void *dst, size_t dst_size, const fs::ReadOption &option) {
|
||||
/* Perform a dry read. */
|
||||
size_t read_size = 0;
|
||||
R_TRY(this->DryRead(std::addressof(read_size), offset, dst_size, option, this->mode));
|
||||
|
||||
/* Read from the base storage. */
|
||||
R_TRY(this->parent->base_storage->Read(this->parent->meta_data_size + this->partition_entry->offset + offset, dst, read_size));
|
||||
|
||||
/* Set output size. */
|
||||
*out = read_size;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template<>
|
||||
Result PartitionFileSystemCore<Sha256PartitionFileSystemMeta>::PartitionFile::ReadImpl(size_t *out, s64 offset, void *dst, size_t dst_size, const fs::ReadOption &option) {
|
||||
/* Perform a dry read. */
|
||||
size_t read_size = 0;
|
||||
R_TRY(this->DryRead(std::addressof(read_size), offset, dst_size, option, this->mode));
|
||||
|
||||
const s64 entry_start = this->parent->meta_data_size + this->partition_entry->offset;
|
||||
const s64 read_end = static_cast<s64>(offset + read_size);
|
||||
const s64 hash_start = static_cast<s64>(this->partition_entry->hash_target_offset);
|
||||
const s64 hash_end = hash_start + this->partition_entry->hash_target_size;
|
||||
|
||||
if (read_end <= hash_start || hash_end <= offset) {
|
||||
/* We aren't reading hashed data, so we can just read from the base storage. */
|
||||
R_TRY(this->parent->base_storage->Read(entry_start + offset, dst, read_size));
|
||||
} else {
|
||||
/* Only hash target offset == 0 is supported. */
|
||||
R_UNLESS(hash_start == 0, fs::ResultInvalidSha256PartitionHashTarget());
|
||||
|
||||
/* Ensure that the hash region is valid. */
|
||||
R_UNLESS(this->partition_entry->hash_target_offset + this->partition_entry->hash_target_size <= this->partition_entry->size, fs::ResultInvalidSha256PartitionHashTarget());
|
||||
|
||||
/* Validate our read offset. */
|
||||
const s64 read_offset = entry_start + offset;
|
||||
R_UNLESS(read_offset >= offset, fs::ResultOutOfRange());
|
||||
|
||||
/* Prepare a buffer for our calculated hash. */
|
||||
char hash[crypto::Sha256Generator::HashSize];
|
||||
crypto::Sha256Generator generator;
|
||||
|
||||
/* Ensure we can perform our read. */
|
||||
const bool hash_in_read = offset <= hash_start && hash_end <= read_end;
|
||||
const bool read_in_hash = hash_start <= offset && read_end <= hash_end;
|
||||
R_UNLESS(hash_in_read || read_in_hash, fs::ResultInvalidSha256PartitionHashTarget());
|
||||
|
||||
/* Initialize the generator. */
|
||||
generator.Initialize();
|
||||
|
||||
if (hash_in_read) {
|
||||
/* Easy case: hash region is contained within the bounds. */
|
||||
R_TRY(this->parent->base_storage->Read(entry_start + offset, dst, read_size));
|
||||
generator.Update(static_cast<u8 *>(dst) + hash_start - offset, this->partition_entry->hash_target_size);
|
||||
} else /* if (read_in_hash) */ {
|
||||
/* We're reading a portion of what's hashed. */
|
||||
s64 remaining_hash_size = this->partition_entry->hash_target_size;
|
||||
s64 hash_offset = entry_start + hash_start;
|
||||
s64 remaining_size = read_size;
|
||||
s64 copy_offset = 0;
|
||||
while (remaining_hash_size > 0) {
|
||||
/* Read some portion of data into the buffer. */
|
||||
constexpr size_t HashBufferSize = 0x200;
|
||||
char hash_buffer[HashBufferSize];
|
||||
size_t cur_size = static_cast<size_t>(std::min(static_cast<s64>(HashBufferSize), remaining_hash_size));
|
||||
R_TRY(this->parent->base_storage->Read(hash_offset, hash_buffer, cur_size));
|
||||
|
||||
/* Update the hash. */
|
||||
generator.Update(hash_buffer, cur_size);
|
||||
|
||||
/* If we need to copy, do so. */
|
||||
if (read_offset <= (hash_offset + static_cast<s64>(cur_size)) && remaining_size > 0) {
|
||||
const s64 hash_buffer_offset = std::max<s64>(read_offset - hash_offset, 0);
|
||||
const size_t copy_size = static_cast<size_t>(std::min<s64>(cur_size - hash_buffer_offset, remaining_size));
|
||||
std::memcpy(static_cast<u8 *>(dst) + copy_offset, hash_buffer + hash_buffer_offset, copy_size);
|
||||
remaining_size -= copy_size;
|
||||
copy_offset += copy_size;
|
||||
}
|
||||
|
||||
/* Update offsets. */
|
||||
remaining_hash_size -= cur_size;
|
||||
hash_offset += cur_size;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the hash. */
|
||||
generator.GetHash(hash, sizeof(hash));
|
||||
|
||||
/* Validate the hash. */
|
||||
auto hash_guard = SCOPE_GUARD { std::memset(dst, 0, read_size); };
|
||||
R_UNLESS(crypto::IsSameBytes(this->partition_entry->hash, hash, sizeof(hash)), fs::ResultSha256PartitionHashVerificationFailed());
|
||||
|
||||
/* We successfully completed our read. */
|
||||
hash_guard.Cancel();
|
||||
}
|
||||
|
||||
/* Set output size. */
|
||||
*out = read_size;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
class PartitionFileSystemCore<MetaType>::PartitionDirectory : public fs::fsa::IDirectory, public fs::impl::Newable {
|
||||
private:
|
||||
u32 cur_index;
|
||||
const PartitionFileSystemCore<MetaType> *parent;
|
||||
const fs::OpenDirectoryMode mode;
|
||||
public:
|
||||
PartitionDirectory(PartitionFileSystemCore<MetaType> *parent, fs::OpenDirectoryMode mode) : cur_index(0), parent(parent), mode(mode) { /* ... */ }
|
||||
public:
|
||||
virtual Result ReadImpl(s64 *out_count, fs::DirectoryEntry *out_entries, s64 max_entries) override final {
|
||||
/* There are no subdirectories. */
|
||||
if ((this->mode & fs::OpenDirectoryMode_File) == 0) {
|
||||
*out_count = 0;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
/* Calculate number of entries. */
|
||||
const s64 entry_count = std::min(max_entries, static_cast<s64>(this->parent->meta_data->GetEntryCount() - this->cur_index));
|
||||
|
||||
/* Populate output directory entries. */
|
||||
for (auto i = 0; i < entry_count; i++, this->cur_index++) {
|
||||
fs::DirectoryEntry &dir_entry = out_entries[i];
|
||||
|
||||
/* Setup the output directory entry. */
|
||||
dir_entry.type = fs::DirectoryEntryType_File;
|
||||
dir_entry.file_size = this->parent->meta_data->GetEntry(this->cur_index)->size;
|
||||
std::strncpy(dir_entry.name, this->parent->meta_data->GetEntryName(this->cur_index), sizeof(dir_entry.name) - 1);
|
||||
dir_entry.name[sizeof(dir_entry.name) - 1] = StringTraits::NullTerminator;
|
||||
}
|
||||
|
||||
*out_count = entry_count;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual Result GetEntryCountImpl(s64 *out) override final {
|
||||
/* Output the parent meta data entry count for files, otherwise 0. */
|
||||
if (this->mode & fs::OpenDirectoryMode_File) {
|
||||
*out = this->parent->meta_data->GetEntryCount();
|
||||
} else {
|
||||
*out = 0;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
|
||||
/* TODO: How should this be handled? */
|
||||
return sf::cmif::InvalidDomainObjectId;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename MetaType>
|
||||
PartitionFileSystemCore<MetaType>::PartitionFileSystemCore() : initialized(false) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
PartitionFileSystemCore<MetaType>::~PartitionFileSystemCore() {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::Initialize(fs::IStorage *base_storage, MemoryResource *allocator) {
|
||||
/* Validate preconditions. */
|
||||
R_UNLESS(!this->initialized, fs::ResultPreconditionViolation());
|
||||
|
||||
/* Allocate meta data. */
|
||||
this->unique_meta_data = std::make_unique<MetaType>();
|
||||
R_UNLESS(this->unique_meta_data != nullptr, fs::ResultAllocationFailureInPartitionFileSystemA());
|
||||
|
||||
/* Initialize meta data. */
|
||||
R_TRY(this->unique_meta_data->Initialize(base_storage, allocator));
|
||||
|
||||
/* Initialize members. */
|
||||
this->meta_data = this->unique_meta_data.get();
|
||||
this->base_storage = base_storage;
|
||||
this->meta_data_size = this->meta_data->GetMetaDataSize();
|
||||
this->initialized = true;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::Initialize(std::unique_ptr<MetaType> &&meta_data, std::shared_ptr<fs::IStorage> base_storage) {
|
||||
this->unique_meta_data = std::move(meta_data);
|
||||
return this->Initialize(this->unique_meta_data.get(), base_storage);
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::Initialize(MetaType *meta_data, std::shared_ptr<fs::IStorage> base_storage) {
|
||||
/* Validate preconditions. */
|
||||
R_UNLESS(!this->initialized, fs::ResultPreconditionViolation());
|
||||
|
||||
/* Initialize members. */
|
||||
this->shared_storage = std::move(base_storage);
|
||||
this->base_storage = this->shared_storage.get();
|
||||
this->meta_data = meta_data;
|
||||
this->meta_data_size = this->meta_data->GetMetaDataSize();
|
||||
this->initialized = true;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::Initialize(fs::IStorage *base_storage) {
|
||||
return this->Initialize(base_storage, std::addressof(g_partition_filesystem_default_allocator));
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::Initialize(std::shared_ptr<fs::IStorage> base_storage) {
|
||||
this->shared_storage = std::move(base_storage);
|
||||
return this->Initialize(this->shared_storage.get());
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::Initialize(std::shared_ptr<fs::IStorage> base_storage, MemoryResource *allocator) {
|
||||
this->shared_storage = std::move(base_storage);
|
||||
return this->Initialize(this->shared_storage.get(), allocator);
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::GetFileBaseOffset(s64 *out_offset, const char *path) {
|
||||
/* Validate preconditions. */
|
||||
R_UNLESS(this->initialized, fs::ResultPreconditionViolation());
|
||||
|
||||
/* Obtain and validate the entry index. */
|
||||
const s32 entry_index = this->meta_data->GetEntryIndex(path + 1);
|
||||
R_UNLESS(entry_index >= 0, fs::ResultPathNotFound());
|
||||
|
||||
/* Output offset. */
|
||||
*out_offset = this->meta_data_size + this->meta_data->GetEntry(entry_index)->offset;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::GetEntryTypeImpl(fs::DirectoryEntryType *out, const char *path) {
|
||||
/* Validate preconditions. */
|
||||
R_UNLESS(this->initialized, fs::ResultPreconditionViolation());
|
||||
R_UNLESS(PathTool::IsSeparator(path[0]), fs::ResultInvalidPathFormat());
|
||||
|
||||
/* Check if the path is for a directory. */
|
||||
if (std::strncmp(path, PathTool::RootPath, sizeof(PathTool::RootPath)) == 0) {
|
||||
*out = fs::DirectoryEntryType_Directory;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
/* Ensure that path is for a file. */
|
||||
R_UNLESS(this->meta_data->GetEntryIndex(path + 1) >= 0, fs::ResultPathNotFound());
|
||||
|
||||
*out = fs::DirectoryEntryType_File;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::OpenFileImpl(std::unique_ptr<fs::fsa::IFile> *out_file, const char *path, fs::OpenMode mode) {
|
||||
/* Validate preconditions. */
|
||||
R_UNLESS(this->initialized, fs::ResultPreconditionViolation());
|
||||
|
||||
/* Obtain and validate the entry index. */
|
||||
const s32 entry_index = this->meta_data->GetEntryIndex(path + 1);
|
||||
R_UNLESS(entry_index >= 0, fs::ResultPathNotFound());
|
||||
|
||||
/* Create and output the file directory. */
|
||||
std::unique_ptr file = std::make_unique<PartitionFile>(this, this->meta_data->GetEntry(entry_index), mode);
|
||||
R_UNLESS(file != nullptr, fs::ResultAllocationFailureInPartitionFileSystemB());
|
||||
*out_file = std::move(file);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::OpenDirectoryImpl(std::unique_ptr<fs::fsa::IDirectory> *out_dir, const char *path, fs::OpenDirectoryMode mode) {
|
||||
/* Validate preconditions. */
|
||||
R_UNLESS(this->initialized, fs::ResultPreconditionViolation());
|
||||
R_UNLESS(std::strncmp(path, PathTool::RootPath, sizeof(PathTool::RootPath)) == 0, fs::ResultPathNotFound());
|
||||
|
||||
/* Create and output the partition directory. */
|
||||
std::unique_ptr directory = std::make_unique<PartitionDirectory>(this, mode);
|
||||
R_UNLESS(directory != nullptr, fs::ResultAllocationFailureInPartitionFileSystemC());
|
||||
*out_dir = std::move(directory);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::CommitImpl() {
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::CleanDirectoryRecursivelyImpl(const char *path) {
|
||||
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::CreateDirectoryImpl(const char *path) {
|
||||
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::CreateFileImpl(const char *path, s64 size, int option) {
|
||||
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::DeleteDirectoryImpl(const char *path) {
|
||||
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::DeleteDirectoryRecursivelyImpl(const char *path) {
|
||||
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::DeleteFileImpl(const char *path) {
|
||||
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::RenameDirectoryImpl(const char *old_path, const char *new_path) {
|
||||
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::RenameFileImpl(const char *old_path, const char *new_path) {
|
||||
return fs::ResultUnsupportedOperationInPartitionFileSystemA();
|
||||
}
|
||||
|
||||
template <typename MetaType>
|
||||
Result PartitionFileSystemCore<MetaType>::CommitProvisionallyImpl(s64 counter) {
|
||||
return fs::ResultUnsupportedOperationInPartitionFileSystemB();
|
||||
}
|
||||
|
||||
template class PartitionFileSystemCore<PartitionFileSystemMeta>;
|
||||
template class PartitionFileSystemCore<Sha256PartitionFileSystemMeta>;
|
||||
|
||||
}
|
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Adubbz, 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/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
template <typename Format>
|
||||
struct PartitionFileSystemMetaCore<Format>::PartitionFileSystemHeader {
|
||||
char signature[sizeof(Format::VersionSignature)];
|
||||
s32 entry_count;
|
||||
u32 name_table_size;
|
||||
u32 reserved;
|
||||
};
|
||||
static_assert(std::is_pod<PartitionFileSystemMeta::PartitionFileSystemHeader>::value);
|
||||
static_assert(sizeof(PartitionFileSystemMeta::PartitionFileSystemHeader) == 0x10);
|
||||
|
||||
template <typename Format>
|
||||
PartitionFileSystemMetaCore<Format>::~PartitionFileSystemMetaCore() {
|
||||
this->DeallocateBuffer();
|
||||
}
|
||||
|
||||
template <typename Format>
|
||||
Result PartitionFileSystemMetaCore<Format>::Initialize(fs::IStorage *storage, MemoryResource *allocator) {
|
||||
/* Validate preconditions. */
|
||||
AMS_ASSERT(allocator != nullptr);
|
||||
|
||||
/* 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 = static_cast<char *>(this->allocator->Allocate(this->meta_data_size));
|
||||
R_UNLESS(this->buffer != nullptr, fs::ResultAllocationFailureInPartitionFileSystemMetaA());
|
||||
|
||||
/* Perform regular initialization. */
|
||||
return this->Initialize(storage, this->buffer, this->meta_data_size);
|
||||
}
|
||||
|
||||
template <typename Format>
|
||||
Result PartitionFileSystemMetaCore<Format>::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<PartitionFileSystemHeader *>(meta);
|
||||
R_UNLESS(crypto::IsSameBytes(this->header->signature, Format::VersionSignature, sizeof(Format::VersionSignature)), typename Format::ResultSignatureVerificationFailed());
|
||||
|
||||
/* Setup entries and name table. */
|
||||
const size_t entries_size = this->header->entry_count * sizeof(typename Format::PartitionEntry);
|
||||
this->entries = reinterpret_cast<PartitionEntry *>(static_cast<u8 *>(meta) + sizeof(PartitionFileSystemHeader));
|
||||
this->name_table = static_cast<char *>(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 <typename Format>
|
||||
void PartitionFileSystemMetaCore<Format>::DeallocateBuffer() {
|
||||
if (this->buffer != nullptr) {
|
||||
AMS_ABORT_UNLESS(this->allocator != nullptr);
|
||||
this->allocator->Deallocate(this->buffer, this->meta_data_size);
|
||||
this->buffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Format>
|
||||
const typename Format::PartitionEntry *PartitionFileSystemMetaCore<Format>::GetEntry(s32 index) const {
|
||||
if (this->initialized && 0 <= index && index < static_cast<s32>(this->header->entry_count)) {
|
||||
return std::addressof(this->entries[index]);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Format>
|
||||
s32 PartitionFileSystemMetaCore<Format>::GetEntryCount() const {
|
||||
if (this->initialized) {
|
||||
return this->header->entry_count;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename Format>
|
||||
s32 PartitionFileSystemMetaCore<Format>::GetEntryIndex(const char *name) const {
|
||||
/* Fail if not initialized. */
|
||||
if (!this->initialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (s32 i = 0; i < static_cast<s32>(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_name_len = this->header->name_table_size - entry.name_offset;
|
||||
if (std::strncmp(std::addressof(this->name_table[entry.name_offset]), name, max_name_len) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not found. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
template <typename Format>
|
||||
const char *PartitionFileSystemMetaCore<Format>::GetEntryName(s32 index) const {
|
||||
if (this->initialized && index < static_cast<s32>(this->header->entry_count)) {
|
||||
return std::addressof(this->name_table[this->GetEntry(index)->name_offset]);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename Format>
|
||||
size_t PartitionFileSystemMetaCore<Format>::GetHeaderSize() const {
|
||||
return sizeof(PartitionFileSystemHeader);
|
||||
}
|
||||
|
||||
template <typename Format>
|
||||
size_t PartitionFileSystemMetaCore<Format>::GetMetaDataSize() const {
|
||||
return this->meta_data_size;
|
||||
}
|
||||
|
||||
template <typename Format>
|
||||
Result PartitionFileSystemMetaCore<Format>::QueryMetaDataSize(size_t *out_size, fs::IStorage *storage) {
|
||||
/* 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)), typename Format::ResultSignatureVerificationFailed());
|
||||
|
||||
/* Output size. */
|
||||
*out_size = sizeof(PartitionFileSystemHeader) + header.entry_count * sizeof(typename Format::PartitionEntry) + header.name_table_size;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
template class PartitionFileSystemMetaCore<impl::PartitionFileSystemFormat>;
|
||||
template class PartitionFileSystemMetaCore<impl::Sha256PartitionFileSystemFormat>;
|
||||
|
||||
Result Sha256PartitionFileSystemMeta::Initialize(fs::IStorage *base_storage, MemoryResource *allocator, const void *hash, size_t hash_size, std::optional<u8> suffix) {
|
||||
/* Ensure preconditions. */
|
||||
R_UNLESS(hash_size == crypto::Sha256Generator::HashSize, fs::ResultPreconditionViolation());
|
||||
|
||||
/* Get metadata size. */
|
||||
R_TRY(QueryMetaDataSize(std::addressof(this->meta_data_size), base_storage));
|
||||
|
||||
/* Ensure we have no buffer. */
|
||||
this->DeallocateBuffer();
|
||||
|
||||
/* Set allocator and allocate buffer. */
|
||||
this->allocator = allocator;
|
||||
this->buffer = static_cast<char *>(this->allocator->Allocate(this->meta_data_size));
|
||||
R_UNLESS(this->buffer != nullptr, fs::ResultAllocationFailureInPartitionFileSystemMetaB());
|
||||
|
||||
/* Read metadata. */
|
||||
R_TRY(base_storage->Read(0, this->buffer, this->meta_data_size));
|
||||
|
||||
/* Calculate hash. */
|
||||
char calc_hash[crypto::Sha256Generator::HashSize];
|
||||
{
|
||||
crypto::Sha256Generator generator;
|
||||
generator.Initialize();
|
||||
generator.Update(this->buffer, this->meta_data_size);
|
||||
if (suffix) {
|
||||
u8 suffix_val = *suffix;
|
||||
generator.Update(std::addressof(suffix_val), 1);
|
||||
}
|
||||
generator.GetHash(calc_hash, sizeof(calc_hash));
|
||||
}
|
||||
|
||||
/* Ensure hash is valid. */
|
||||
R_UNLESS(crypto::IsSameBytes(hash, calc_hash, sizeof(calc_hash)), fs::ResultSha256PartitionHashVerificationFailed());
|
||||
|
||||
/* Give access to Format */
|
||||
using Format = impl::Sha256PartitionFileSystemFormat;
|
||||
|
||||
/* Set header. */
|
||||
this->header = reinterpret_cast<PartitionFileSystemHeader *>(this->buffer);
|
||||
R_UNLESS(crypto::IsSameBytes(this->header->signature, Format::VersionSignature, sizeof(Format::VersionSignature)), typename Format::ResultSignatureVerificationFailed());
|
||||
|
||||
/* Validate size for entries and name table. */
|
||||
const size_t entries_size = this->header->entry_count * sizeof(typename Format::PartitionEntry);
|
||||
R_UNLESS(this->meta_data_size >= sizeof(PartitionFileSystemHeader) + entries_size + this->header->name_table_size, fs::ResultInvalidSha256PartitionMetaDataSize());
|
||||
|
||||
/* Set entries and name table. */
|
||||
this->entries = reinterpret_cast<PartitionEntry *>(this->buffer + sizeof(PartitionFileSystemHeader));
|
||||
this->name_table = this->buffer + sizeof(PartitionFileSystemHeader) + entries_size;
|
||||
|
||||
/* We initialized. */
|
||||
this->initialized = true;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
|
@ -19,6 +19,7 @@
|
|||
#include <vapours/assert.hpp>
|
||||
#include <vapours/literals.hpp>
|
||||
|
||||
#include <vapours/allocator.hpp>
|
||||
#include <vapours/timespan.hpp>
|
||||
#include <vapours/span.hpp>
|
||||
|
||||
|
|
58
libraries/libvapours/include/vapours/allocator.hpp
Normal file
58
libraries/libvapours/include/vapours/allocator.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
|
@ -74,7 +74,14 @@ namespace ams::fs {
|
|||
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemA, 3247);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemB, 3248);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemC, 3249);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemCreatorA, 3280);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInDirectorySaveDataFileSystem, 3321);
|
||||
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemA, 3347);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemB, 3348);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemC, 3349);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemMetaA, 3350);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInPartitionFileSystemMetaB, 3351);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInRomFsFileSystemD, 3352);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInSubDirectoryFileSystem, 3355);
|
||||
R_DEFINE_ERROR_RESULT(AllocationFailureInRegisterA, 3365);
|
||||
|
@ -238,6 +245,8 @@ namespace ams::fs {
|
|||
|
||||
R_DEFINE_ERROR_RANGE(InvalidOperationForOpenMode, 6200, 6299);
|
||||
R_DEFINE_ERROR_RESULT(FileExtensionWithoutOpenModeAllowAppend, 6201);
|
||||
R_DEFINE_ERROR_RESULT(ReadNotPermitted, 6202);
|
||||
R_DEFINE_ERROR_RESULT(WriteNotPermitted, 6203);
|
||||
|
||||
R_DEFINE_ERROR_RANGE(UnsupportedOperation, 6300, 6399);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInSubStorageA, 6302);
|
||||
|
@ -256,6 +265,10 @@ namespace ams::fs {
|
|||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileSystemTemplateC, 6371);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileA, 6372);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInReadOnlyFileB, 6373);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileSystemA, 6374);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileSystemB, 6375);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileA, 6376);
|
||||
R_DEFINE_ERROR_RESULT(UnsupportedOperationInPartitionFileB, 6377);
|
||||
|
||||
R_DEFINE_ERROR_RANGE(PermissionDenied, 6400, 6449);
|
||||
|
||||
|
|
Loading…
Reference in a new issue