From 98087ef3467f3feea667173d9f57b4d29ddc05c9 Mon Sep 17 00:00:00 2001 From: Adubbz Date: Fri, 27 Mar 2020 16:07:52 +1100 Subject: [PATCH] fssystem: implement PartitionFileSystemCore --- .../include/stratosphere/fssystem.hpp | 1 + .../fssystem_partition_file_system.hpp | 70 ++++ .../fssystem_partition_file_system.cpp | 356 ++++++++++++++++++ .../include/vapours/results/fs_results.hpp | 9 + 4 files changed, 436 insertions(+) create mode 100644 libraries/libstratosphere/include/stratosphere/fssystem/fssystem_partition_file_system.hpp create mode 100644 libraries/libstratosphere/source/fssystem/fssystem_partition_file_system.cpp diff --git a/libraries/libstratosphere/include/stratosphere/fssystem.hpp b/libraries/libstratosphere/include/stratosphere/fssystem.hpp index 1a7c20e6d..3eabf62c8 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.hpp" #include "fssystem/fssystem_partition_file_system_meta.hpp" #include "fssystem/fssystem_path_tool.hpp" #include "fssystem/fssystem_subdirectory_filesystem.hpp" diff --git a/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_partition_file_system.hpp b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_partition_file_system.hpp new file mode 100644 index 000000000..2a5e1ff29 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/fssystem/fssystem_partition_file_system.hpp @@ -0,0 +1,70 @@ +/* + * 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 . + */ +#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 + class PartitionFileSystemCore : public fs::impl::Newable, public fs::fsa::IFileSystem { + NON_COPYABLE(PartitionFileSystemCore); + private: + class PartitionFile; + class PartitionDirectory; + private: + fs::IStorage *base_storage; + MetaType *meta_data; + bool initialized; + size_t meta_data_size; + std::unique_ptr unique_meta_data; + std::shared_ptr shared_storage; + private: + Result Initialize(fs::IStorage *base_storage, MemoryResource *allocator); + public: + PartitionFileSystemCore(); + virtual ~PartitionFileSystemCore() override; + + Result Initialize(std::unique_ptr &meta_data, std::shared_ptr base_storage); + Result Initialize(MetaType *meta_data, std::shared_ptr base_storage); + Result Initialize(fs::IStorage *base_storage); + Result Initialize(std::shared_ptr base_storage); + Result Initialize(std::shared_ptr 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 *out_file, const char *path, fs::OpenMode mode) override; + virtual Result OpenDirectoryImpl(std::unique_ptr *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; + +} diff --git a/libraries/libstratosphere/source/fssystem/fssystem_partition_file_system.cpp b/libraries/libstratosphere/source/fssystem/fssystem_partition_file_system.cpp new file mode 100644 index 000000000..d977cb014 --- /dev/null +++ b/libraries/libstratosphere/source/fssystem/fssystem_partition_file_system.cpp @@ -0,0 +1,356 @@ +/* + * 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 . + */ +#include + +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 + class PartitionFileSystemCore::PartitionFile : public fs::fsa::IFile, public fs::impl::Newable { + private: + const typename MetaType::PartitionEntry *partition_entry; + PartitionFileSystemCore *parent; + fs::OpenMode mode; + public: + PartitionFile(PartitionFileSystemCore *parent, const typename MetaType::PartitionEntry *partition_entry, fs::OpenMode mode) : partition_entry(partition_entry), parent(parent), mode(mode) { /* ... */ } + + virtual ~PartitionFile() { /* ... */ } + 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)); + + /* 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(this->partition_entry->size), fs::ResultOutOfRange()); + R_UNLESS(static_cast(offset + size) <= static_cast(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(this->partition_entry->size), fs::ResultOutOfRange()); + R_UNLESS(static_cast(offset + size) <= static_cast(this->partition_entry->size), fs::ResultInvalidSize()); + R_UNLESS(static_cast(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::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 + class PartitionFileSystemCore::PartitionDirectory : public fs::fsa::IDirectory, public fs::impl::Newable { + private: + u32 cur_index; + PartitionFileSystemCore *parent; + fs::OpenDirectoryMode mode; + public: + PartitionDirectory(PartitionFileSystemCore *parent, fs::OpenDirectoryMode mode) : cur_index(0), parent(parent), mode(mode) { /* ... */ } + + virtual ~PartitionDirectory() { /* ... */ } + 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)) { + *out_count = 0; + return ResultSuccess(); + } + + /* Calculate number of entries. */ + const s32 entry_count = std::min(max_entries, static_cast(this->parent->meta_data->GetEntryCount() - this->cur_index)); + + /* Populate output directory entries. */ + for (s32 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), fs::EntryNameLengthMax); + dir_entry.name[fs::EntryNameLengthMax] = 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 + Result PartitionFileSystemCore::CleanDirectoryRecursivelyImpl(const char *path) { + return fs::ResultUnsupportedOperationInPartitionFileSystemA(); + } + + template + Result PartitionFileSystemCore::CommitImpl() { + return ResultSuccess(); + } + + template + Result PartitionFileSystemCore::CommitProvisionallyImpl(s64 counter) { + return fs::ResultUnsupportedOperationInPartitionFileSystemB(); + } + + template + Result PartitionFileSystemCore::CreateDirectoryImpl(const char *path) { + return fs::ResultUnsupportedOperationInPartitionFileSystemA(); + } + + template + Result PartitionFileSystemCore::CreateFileImpl(const char *path, s64 size, int option) { + return fs::ResultUnsupportedOperationInPartitionFileSystemA(); + } + + template + Result PartitionFileSystemCore::DeleteDirectoryImpl(const char *path) { + return fs::ResultUnsupportedOperationInPartitionFileSystemA(); + } + + template + Result PartitionFileSystemCore::DeleteDirectoryRecursivelyImpl(const char *path) { + return fs::ResultUnsupportedOperationInPartitionFileSystemA(); + } + + template + Result PartitionFileSystemCore::DeleteFileImpl(const char *path) { + return fs::ResultUnsupportedOperationInPartitionFileSystemA(); + } + + template + Result PartitionFileSystemCore::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 + Result PartitionFileSystemCore::OpenDirectoryImpl(std::unique_ptr *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(this, mode); + R_UNLESS(directory != nullptr, fs::ResultAllocationFailureInPartitionFileSystemA()); + *out_dir = std::move(directory); + return ResultSuccess(); + } + + template + Result PartitionFileSystemCore::OpenFileImpl(std::unique_ptr *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(this, this->meta_data->GetEntry(entry_index), mode); + R_UNLESS(file != nullptr, fs::ResultAllocationFailureInPartitionFileSystemB()); + *out_file = std::move(file); + return ResultSuccess(); + } + + template + Result PartitionFileSystemCore::RenameDirectoryImpl(const char *old_path, const char *new_path) { + return fs::ResultUnsupportedOperationInPartitionFileSystemA(); + } + + template + Result PartitionFileSystemCore::RenameFileImpl(const char *old_path, const char *new_path) { + return fs::ResultUnsupportedOperationInPartitionFileSystemA(); + } + + template + Result PartitionFileSystemCore::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 + Result PartitionFileSystemCore::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(); + 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 + Result PartitionFileSystemCore::Initialize(std::unique_ptr &meta_data, std::shared_ptr base_storage) { + this->unique_meta_data = std::move(meta_data); + return this->Initialize(this->unique_meta_data.get(), base_storage); + } + + template + Result PartitionFileSystemCore::Initialize(MetaType *meta_data, std::shared_ptr base_storage) { + /* Validate preconditions. */ + R_UNLESS(!this->initialized, fs::ResultPreconditionViolation()); + + /* Initialize members. */ + this->shared_storage = 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 + Result PartitionFileSystemCore::Initialize(fs::IStorage *base_storage) { + return this->Initialize(base_storage, std::addressof(g_partition_filesystem_default_allocator)); + } + + template + Result PartitionFileSystemCore::Initialize(std::shared_ptr base_storage) { + this->shared_storage = base_storage; + return this->Initialize(this->shared_storage.get()); + } + + template + Result PartitionFileSystemCore::Initialize(std::shared_ptr base_storage, MemoryResource *allocator) { + this->shared_storage = base_storage; + return this->Initialize(this->shared_storage.get(), allocator); + } + + template class PartitionFileSystemCore; + +} diff --git a/libraries/libvapours/include/vapours/results/fs_results.hpp b/libraries/libvapours/include/vapours/results/fs_results.hpp index c2985f2e1..acc581d62 100644 --- a/libraries/libvapours/include/vapours/results/fs_results.hpp +++ b/libraries/libvapours/include/vapours/results/fs_results.hpp @@ -74,7 +74,12 @@ 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); @@ -260,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);