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);