diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_dir_utils.cpp b/stratosphere/ams_mitm/source/fs_mitm/fs_dir_utils.cpp
new file mode 100644
index 000000000..5401329d9
--- /dev/null
+++ b/stratosphere/ams_mitm/source/fs_mitm/fs_dir_utils.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2018 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
+#include
+#include
+#include "fs_results.hpp"
+#include "fs_dir_utils.hpp"
+#include "fs_ifile.hpp"
+
+Result FsDirUtils::CopyFile(IFileSystem *dst_fs, IFileSystem *src_fs, const FsPath &dst_parent_path, const FsPath &src_path, const FsDirectoryEntry *dir_ent, void *work_buf, size_t work_buf_size) {
+ Result rc;
+ std::unique_ptr src_file;
+ std::unique_ptr dst_file;
+ const u64 file_size = dir_ent->fileSize;
+
+ /* Open source file for reading. */
+ if (R_FAILED((rc = src_fs->OpenFile(src_file, src_path, OpenMode_Read)))) {
+ return rc;
+ }
+
+ /* Create and open destination file. */
+ {
+ FsPath dst_path;
+ if (static_cast(snprintf(dst_path.str, sizeof(dst_path.str), "%s%s", dst_parent_path.str, src_path.str)) >= sizeof(dst_path)) {
+ /* TODO: Error code? N aborts here. */
+ std::abort();
+ }
+
+ if (R_FAILED((rc = dst_fs->CreateFile(dst_path, file_size)))) {
+ return rc;
+ }
+ if (R_FAILED((rc = dst_fs->OpenFile(dst_file, dst_path, OpenMode_Write)))) {
+ return rc;
+ }
+ }
+
+ /* Read/Write work_buf_size chunks. */
+ u64 offset = 0;
+ while (offset < file_size) {
+ u64 read_size;
+ if (R_FAILED((rc = src_file->Read(&read_size, offset, work_buf, work_buf_size)))) {
+ return rc;
+ }
+ if (R_FAILED((rc = dst_file->Write(offset, work_buf, read_size)))) {
+ return rc;
+ }
+
+ offset += read_size;
+ }
+
+ return 0;
+}
+
+Result FsDirUtils::CopyDirectoryRecursively(IFileSystem *dst_fs, IFileSystem *src_fs, const FsPath &dst_path, const FsPath &src_path, void *work_buf, size_t work_buf_size) {
+ FsPath work_path = dst_path;
+
+ return IterateDirectoryRecursively(src_fs, src_path,
+ [&](const FsPath &path, const FsDirectoryEntry *dir_ent) -> Result { /* On Enter Directory */
+ /* Update path, create new dir. */
+ strncat(work_path.str, dir_ent->name, sizeof(work_path) - strnlen(work_path.str, sizeof(work_path) - 1) - 1);
+ strncat(work_path.str, "/", sizeof(work_path) - strnlen(work_path.str, sizeof(work_path) - 1) - 1);
+ return dst_fs->CreateDirectory(work_path);
+ },
+ [&](const FsPath &path, const FsDirectoryEntry *dir_ent) -> Result { /* On Exit Directory */
+ /* Check we have a parent directory. */
+ const size_t work_path_len = strnlen(work_path.str, sizeof(work_path));
+ if (work_path_len < 2) {
+ return ResultFsInvalidPathFormat;
+ }
+
+ /* Find previous separator, add NULL terminator */
+ char *p = &work_path.str[work_path_len - 2];
+ while (*p != '/' && p > work_path.str) {
+ p--;
+ }
+ p[1] = 0;
+
+ return 0;
+ },
+ [&](const FsPath &path, const FsDirectoryEntry *dir_ent) -> Result { /* On File */
+ /* Just copy the file to the new fs. */
+ return CopyFile(dst_fs, src_fs, work_path, path, dir_ent, work_buf, work_buf_size);
+ });
+}
\ No newline at end of file
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_dir_utils.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_dir_utils.hpp
new file mode 100644
index 000000000..76ce21be7
--- /dev/null
+++ b/stratosphere/ams_mitm/source/fs_mitm/fs_dir_utils.hpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2018 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 "fs_path_utils.hpp"
+#include "fs_ifilesystem.hpp"
+
+class FsDirUtils {
+ private:
+ template
+ static Result IterateDirectoryRecursivelyInternal(IFileSystem *fs, FsPath &work_path, FsDirectoryEntry *ent_buf, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
+ Result rc;
+ std::unique_ptr dir;
+
+ /* Open the directory. */
+ if (R_FAILED((rc = fs->OpenDirectory(dir, work_path, DirectoryOpenMode_All)))) {
+ return rc;
+ }
+
+ const size_t parent_len = strnlen(work_path.str, sizeof(work_path.str - 1));
+
+ /* Read and handle entries. */
+ while (true) {
+ /* Read a single entry. */
+ u64 read_count;
+ if (R_FAILED((rc = dir->Read(&read_count, ent_buf, 1)))) {
+ return rc;
+ }
+
+ /* If we're out of entries, we're done. */
+ if (read_count == 0) {
+ break;
+ }
+
+ const size_t child_name_len = strnlen(ent_buf->name, sizeof(ent_buf->name) - 1);
+ const bool is_dir = ent_buf->type == ENTRYTYPE_DIR;
+ const size_t child_path_len = parent_len + child_name_len + (is_dir ? 1 : 0);
+
+ /* Validate child path size. */
+ if (child_path_len >= sizeof(work_path.str)) {
+ return ResultFsTooLongPath;
+ }
+
+ strncat(work_path.str, ent_buf->name, sizeof(work_path.str) - 1 - parent_len);
+ if (is_dir) {
+ /* Enter directory. */
+ if (R_FAILED((rc = on_enter_dir(work_path, ent_buf)))) {
+ return rc;
+ }
+
+ /* Append separator, recurse. */
+ strncat(work_path.str, "/", sizeof(work_path.str) - 1 - parent_len - child_name_len);
+ if (R_FAILED((rc = IterateDirectoryRecursivelyInternal(fs, work_path, ent_buf, on_enter_dir, on_exit_dir, on_file)))) {
+ return rc;
+ }
+
+ /* Exit directory. */
+ if (R_FAILED((rc = on_exit_dir(work_path, ent_buf)))) {
+ return rc;
+ }
+ } else {
+ /* Call file handler. */
+ if (R_FAILED((rc = on_file(work_path, ent_buf)))) {
+ return rc;
+ }
+ }
+
+ /* Restore parent path. */
+ work_path.str[parent_len] = 0;
+ }
+
+ return 0;
+ }
+
+ public:
+ template
+ static Result IterateDirectoryRecursively(IFileSystem *fs, const FsPath &root_path, FsPath &work_path, FsDirectoryEntry *ent_buf, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
+ /* Ensure valid root path. */
+ size_t root_path_len = strnlen(root_path.str, sizeof(root_path.str));
+ if (root_path_len > FS_MAX_PATH - 1 || ((root_path_len == FS_MAX_PATH - 1) && root_path.str[root_path_len-1] != '/')) {
+ return ResultFsTooLongPath;
+ }
+
+ /* Copy path, ensure terminating separator. */
+ memcpy(work_path.str, root_path.str, root_path_len);
+ if (work_path.str[root_path_len-1] != '/') {
+ root_path_len++;
+ work_path.str[root_path_len-1] = '/';
+ }
+ work_path.str[root_path_len] = 0;
+
+ /* Actually iterate. */
+ return IterateDirectoryRecursivelyInternal(fs, work_path, ent_buf, on_enter_dir, on_exit_dir, on_file);
+ }
+
+ /* Helper for not specifying work path/entry buffer. */
+ template
+ static Result IterateDirectoryRecursively(IFileSystem *fs, const FsPath &root_path, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
+ FsDirectoryEntry dir_ent = {0};
+ FsPath work_path = {0};
+ return IterateDirectoryRecursively(fs, root_path, work_path, &dir_ent, on_enter_dir, on_exit_dir, on_file);
+ }
+
+ /* Helper for iterating over the filesystem root. */
+ template
+ static Result IterateDirectoryRecursively(IFileSystem *fs, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
+ return IterateDirectoryRecursively(fs, FsPathUtils::RootPath, on_enter_dir, on_exit_dir, on_file);
+ }
+
+ /* Copy API. */
+ static Result CopyFile(IFileSystem *dst_fs, IFileSystem *src_fs, const FsPath &dst_parent_path, const FsPath &src_path, const FsDirectoryEntry *dir_ent, void *work_buf, size_t work_buf_size);
+ static Result CopyFile(IFileSystem *fs, const FsPath &dst_parent_path, const FsPath &src_path, const FsDirectoryEntry *dir_ent, void *work_buf, size_t work_buf_size) {
+ return CopyFile(fs, fs, dst_parent_path, src_path, dir_ent, work_buf, work_buf_size);
+ }
+
+ static Result CopyDirectoryRecursively(IFileSystem *dst_fs, IFileSystem *src_fs, const FsPath &dst_path, const FsPath &src_path, void *work_buf, size_t work_buf_size);
+ static Result CopyDirectoryRecursively(IFileSystem *fs, const FsPath &dst_path, const FsPath &src_path, void *work_buf, size_t work_buf_size) {
+ return CopyDirectoryRecursively(fs, fs, dst_path, src_path, work_buf, work_buf_size);
+ }
+
+ /* Other Utility. */
+ template
+ static Result RetryUntilTargetNotLocked(F f) {
+ const size_t MaxRetries = 10;
+ Result rc = 0;
+
+ for (size_t i = 0; i < MaxRetries; i++) {
+ rc = f();
+
+ if (rc != ResultFsTargetLocked) {
+ break;
+ }
+
+ /* If target is locked, wait 100ms and try again. */
+ svcSleepThread(100'000'000ul);
+ }
+
+ return rc;
+ }
+};
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_directory_savedata_filesystem.cpp b/stratosphere/ams_mitm/source/fs_mitm/fs_directory_savedata_filesystem.cpp
new file mode 100644
index 000000000..62e46f700
--- /dev/null
+++ b/stratosphere/ams_mitm/source/fs_mitm/fs_directory_savedata_filesystem.cpp
@@ -0,0 +1,394 @@
+/*
+ * Copyright (c) 2018 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
+#include
+#include
+
+#include "../utils.hpp"
+#include "fs_directory_savedata_filesystem.hpp"
+#include "fs_dir_utils.hpp"
+
+class DirectorySaveDataFile : public IFile {
+ private:
+ std::unique_ptr base_file;
+ DirectorySaveDataFileSystem *parent_fs; /* TODO: shared_ptr + enabled_shared_from_this? */
+ int open_mode;
+ public:
+ DirectorySaveDataFile(std::unique_ptr f, DirectorySaveDataFileSystem *p, int m) : base_file(std::move(f)), parent_fs(p), open_mode(m) {
+ /* ... */
+ }
+
+ virtual ~DirectorySaveDataFile() {
+ /* Observe closing of writable file. */
+ if (this->open_mode & OpenMode_Write) {
+ this->parent_fs->OnWritableFileClose();
+ }
+ }
+ public:
+ virtual Result ReadImpl(u64 *out, u64 offset, void *buffer, u64 size) override {
+ return this->base_file->Read(out, offset, buffer, size);
+ }
+ virtual Result GetSizeImpl(u64 *out) override {
+ return this->base_file->GetSize(out);
+ }
+ virtual Result FlushImpl() override {
+ return this->base_file->Flush();
+ }
+ virtual Result WriteImpl(u64 offset, void *buffer, u64 size, bool flush) override {
+ return this->base_file->Write(offset, buffer, size, flush);
+ }
+ virtual Result SetSizeImpl(u64 size) override {
+ return this->base_file->SetSize(size);
+ }
+ virtual Result OperateRangeImpl(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
+ return this->base_file->OperateRange(operation_type, offset, size, out_range_info);
+ }
+};
+
+/* ================================================================================================ */
+
+Result DirectorySaveDataFileSystem::Initialize() {
+ Result rc;
+ DirectoryEntryType ent_type;
+
+ /* Check that the working directory exists. */
+ if (R_FAILED((rc = this->fs->GetEntryType(&ent_type, WorkingDirectoryPath)))) {
+ /* If path isn't found, create working directory and committed directory. */
+ if (rc == ResultFsPathNotFound) {
+ if (R_FAILED((rc = this->fs->CreateDirectory(WorkingDirectoryPath)))) {
+ return rc;
+ }
+ if (R_FAILED((rc = this->fs->CreateDirectory(CommittedDirectoryPath)))) {
+ return rc;
+ }
+ } else {
+ return rc;
+ }
+ }
+
+ /* Now check for the committed directory. */
+ rc = this->fs->GetEntryType(&ent_type, CommittedDirectoryPath);
+ if (R_SUCCEEDED(rc)) {
+ /* If committed exists, synchronize it to the working directory. */
+ return this->SynchronizeDirectory(WorkingDirectoryPath, CommittedDirectoryPath);
+ } else if (rc == ResultFsPathNotFound) {
+ if (R_FAILED((rc = this->SynchronizeDirectory(SynchronizingDirectoryPath, WorkingDirectoryPath)))) {
+ return rc;
+ }
+ return this->fs->RenameDirectory(SynchronizingDirectoryPath, CommittedDirectoryPath);
+ } else {
+ return rc;
+ }
+}
+
+Result DirectorySaveDataFileSystem::SynchronizeDirectory(const FsPath &dst_dir, const FsPath &src_dir) {
+ Result rc;
+
+ /* Delete destination dir and recreate it. */
+ if (R_FAILED((rc = this->fs->DeleteDirectoryRecursively(dst_dir)))) {
+ /* Nintendo returns error unconditionally, but I think that's a bug in their code. */
+ if (rc != ResultFsPathNotFound) {
+ return rc;
+ }
+ }
+ if (R_FAILED((rc = this->fs->CreateDirectory(dst_dir)))) {
+ return rc;
+ }
+
+ /* Get a buffer to work with. */
+ void *work_buf = nullptr;
+ size_t work_buf_size = 0;
+ if (R_FAILED((rc = this->AllocateWorkBuffer(&work_buf, &work_buf_size, IdealWorkBuffersize)))) {
+ return rc;
+ }
+ ON_SCOPE_EXIT { free(work_buf); };
+
+ return FsDirUtils::CopyDirectoryRecursively(this->fs, dst_dir, src_dir, work_buf, work_buf_size);
+}
+
+Result DirectorySaveDataFileSystem::AllocateWorkBuffer(void **out_buf, size_t *out_size, const size_t ideal_size) {
+ size_t try_size = ideal_size;
+
+ /* Repeatedly try to allocate until success. */
+ while (try_size > 0x200) {
+ void *buf = malloc(try_size);
+ if (buf != nullptr) {
+ *out_buf = buf;
+ *out_size = try_size;
+ return 0;
+ }
+
+ /* Divide size by two. */
+ try_size >>= 1;
+ }
+
+ /* TODO: Return a result here? Nintendo does not, but they have other allocation failed results. */
+ /* Consider returning ResultFsAllocationFailureInDirectorySaveDataFileSystem? */
+ std::abort();
+}
+
+Result DirectorySaveDataFileSystem::GetFullPath(char *out, size_t out_size, const char *relative_path) {
+ /* Validate path. */
+ if (1 + strnlen(relative_path, FS_MAX_PATH) >= out_size) {
+ return ResultFsTooLongPath;
+ }
+ if (relative_path[0] != '/') {
+ return ResultFsInvalidPath;
+ }
+
+ /* Copy working directory path. */
+ std::strncpy(out, WorkingDirectoryPath.str, out_size);
+ out[out_size-1] = 0;
+
+ /* Normalize it. */
+ constexpr size_t working_len = WorkingDirectoryPathLen - 1;
+ return FsPathUtils::Normalize(out + working_len, out_size - working_len, relative_path, nullptr);
+}
+
+void DirectorySaveDataFileSystem::OnWritableFileClose() {
+ std::scoped_lock lk(this->lock);
+ this->open_writable_files--;
+
+ /* TODO: Abort if < 0? N does not. */
+}
+
+/* ================================================================================================ */
+
+Result DirectorySaveDataFileSystem::CreateFileImpl(const FsPath &path, uint64_t size, int flags) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ std::scoped_lock lk(this->lock);
+ return this->fs->CreateFile(full_path, size, flags);
+}
+
+Result DirectorySaveDataFileSystem::DeleteFileImpl(const FsPath &path) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ std::scoped_lock lk(this->lock);
+ return this->fs->DeleteFile(full_path);
+}
+
+Result DirectorySaveDataFileSystem::CreateDirectoryImpl(const FsPath &path) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ std::scoped_lock lk(this->lock);
+ return this->fs->CreateDirectory(full_path);
+}
+
+Result DirectorySaveDataFileSystem::DeleteDirectoryImpl(const FsPath &path) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ std::scoped_lock lk(this->lock);
+ return this->fs->DeleteDirectory(full_path);
+}
+
+Result DirectorySaveDataFileSystem::DeleteDirectoryRecursivelyImpl(const FsPath &path) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ std::scoped_lock lk(this->lock);
+ return this->fs->DeleteDirectoryRecursively(full_path);
+}
+
+Result DirectorySaveDataFileSystem::RenameFileImpl(const FsPath &old_path, const FsPath &new_path) {
+ Result rc;
+ FsPath full_old_path, full_new_path;
+
+ if (R_FAILED((rc = GetFullPath(full_old_path, old_path)))) {
+ return rc;
+ }
+
+ if (R_FAILED((rc = GetFullPath(full_new_path, new_path)))) {
+ return rc;
+ }
+
+ std::scoped_lock lk(this->lock);
+ return this->fs->RenameFile(full_old_path, full_new_path);
+}
+
+Result DirectorySaveDataFileSystem::RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) {
+ Result rc;
+ FsPath full_old_path, full_new_path;
+
+ if (R_FAILED((rc = GetFullPath(full_old_path, old_path)))) {
+ return rc;
+ }
+
+ if (R_FAILED((rc = GetFullPath(full_new_path, new_path)))) {
+ return rc;
+ }
+
+ std::scoped_lock lk(this->lock);
+ return this->fs->RenameDirectory(full_old_path, full_new_path);
+}
+
+Result DirectorySaveDataFileSystem::GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ std::scoped_lock lk(this->lock);
+ return this->fs->GetEntryType(out, full_path);
+}
+
+Result DirectorySaveDataFileSystem::OpenFileImpl(std::unique_ptr &out_file, const FsPath &path, OpenMode mode) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ std::scoped_lock lk(this->lock);
+
+ {
+ /* Open the raw file. */
+ std::unique_ptr file;
+ if (R_FAILED((rc = this->fs->OpenFile(file, full_path, mode)))) {
+ return rc;
+ }
+
+ /* Create DirectorySaveDataFile wrapper. */
+ out_file = std::make_unique(std::move(file), this, mode);
+ }
+
+ /* Check for allocation failure. */
+ if (out_file == nullptr) {
+ return ResultFsAllocationFailureInDirectorySaveDataFileSystem;
+ }
+
+ /* Increment open writable files, if needed. */
+ if (mode & OpenMode_Write) {
+ this->open_writable_files++;
+ }
+
+ return 0;
+}
+
+Result DirectorySaveDataFileSystem::OpenDirectoryImpl(std::unique_ptr &out_dir, const FsPath &path, DirectoryOpenMode mode) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ std::scoped_lock lk(this->lock);
+ return this->fs->OpenDirectory(out_dir, full_path, mode);
+}
+
+Result DirectorySaveDataFileSystem::CommitImpl() {
+ /* Here, Nintendo does the following (with retries): */
+ /* - Rename Committed -> Synchronizing. */
+ /* - Synchronize Working -> Synchronizing (deleting Synchronizing). */
+ /* - Rename Synchronizing -> Committed. */
+ /* I think this is not the optimal order to do things, as the previous committed directory */
+ /* will be deleted if there is an error during synchronization. */
+ /* Instead, we will synchronize first, then delete committed, then rename. */
+
+ std::scoped_lock lk(this->lock);
+ Result rc;
+
+ /* Ensure we don't have any open writable files. */
+ if (this->open_writable_files != 0) {
+ return ResultFsPreconditionViolation;
+ }
+
+ const auto SynchronizeWorkingDir = [&]() { return this->SynchronizeDirectory(SynchronizingDirectoryPath, WorkingDirectoryPath); };
+ const auto DeleteCommittedDir = [&]() { return this->fs->DeleteDirectoryRecursively(CommittedDirectoryPath); };
+ const auto RenameSynchDir = [&]() { return this->fs->RenameDirectory(SynchronizingDirectoryPath, CommittedDirectoryPath); };
+
+ /* Synchronize working directory. */
+ if (R_FAILED((rc = FsDirUtils::RetryUntilTargetNotLocked(std::move(SynchronizeWorkingDir))))) {
+ return rc;
+ }
+
+ /* Delete committed directory. */
+ if (R_FAILED((rc = FsDirUtils::RetryUntilTargetNotLocked(std::move(DeleteCommittedDir))))) {
+ /* It is okay for us to not have a committed directory here. */
+ if (rc != ResultFsPathNotFound) {
+ return rc;
+ }
+ }
+
+ /* Rename synchronizing directory to committed directory. */
+ if (R_FAILED((rc = FsDirUtils::RetryUntilTargetNotLocked(std::move(RenameSynchDir))))) {
+ return rc;
+ }
+
+ /* TODO: Should I call this->fs->Commit()? Nintendo does not. */
+ return rc;
+}
+
+Result DirectorySaveDataFileSystem::GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) {
+ /* TODO: How should this work? N returns ResultFsNotImplemented. */
+ return ResultFsNotImplemented;
+}
+
+Result DirectorySaveDataFileSystem::GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) {
+ /* TODO: How should this work? N returns ResultFsNotImplemented. */
+ return ResultFsNotImplemented;
+}
+
+Result DirectorySaveDataFileSystem::CleanDirectoryRecursivelyImpl(const FsPath &path) {
+ Result rc;
+ FsPath full_path;
+
+ if (R_FAILED((rc = GetFullPath(full_path, path)))) {
+ return rc;
+ }
+
+ std::scoped_lock lk(this->lock);
+ return this->fs->CleanDirectoryRecursively(full_path);
+}
+
+Result DirectorySaveDataFileSystem::GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) {
+ /* TODO: How should this work? N returns ResultFsNotImplemented. */
+ return ResultFsNotImplemented;
+}
+
+Result DirectorySaveDataFileSystem::QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) {
+ /* TODO: How should this work? N returns ResultFsNotImplemented. */
+ return ResultFsNotImplemented;
+}
\ No newline at end of file
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_directory_savedata_filesystem.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_directory_savedata_filesystem.hpp
new file mode 100644
index 000000000..e40521faa
--- /dev/null
+++ b/stratosphere/ams_mitm/source/fs_mitm/fs_directory_savedata_filesystem.hpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2018 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
+
+#include "fs_ifilesystem.hpp"
+#include "fs_path_utils.hpp"
+
+class DirectorySaveDataFileSystem : public IFileSystem {
+ private:
+ static constexpr FsPath CommittedDirectoryPath = EncodeConstantFsPath("/0/");
+ static constexpr FsPath WorkingDirectoryPath = EncodeConstantFsPath("/1/");
+ static constexpr FsPath SynchronizingDirectoryPath = EncodeConstantFsPath("/_/");
+
+ static constexpr size_t CommittedDirectoryPathLen = GetConstantFsPathLen(CommittedDirectoryPath);
+ static constexpr size_t WorkingDirectoryPathLen = GetConstantFsPathLen(WorkingDirectoryPath);
+ static constexpr size_t SynchronizingDirectoryPathLen = GetConstantFsPathLen(SynchronizingDirectoryPath);
+
+ static constexpr size_t IdealWorkBuffersize = 0x100000; /* 1 MB */
+ private:
+ std::shared_ptr shared_fs;
+ std::unique_ptr unique_fs;
+ IFileSystem *fs;
+ HosMutex lock;
+ size_t open_writable_files = 0;
+
+ public:
+ DirectorySaveDataFileSystem(IFileSystem *fs) : unique_fs(fs) {
+ this->fs = this->unique_fs.get();
+ Result rc = this->Initialize();
+ if (R_FAILED(rc)) {
+ fatalSimple(rc);
+ }
+ }
+
+ DirectorySaveDataFileSystem(std::unique_ptr fs) : unique_fs(std::move(fs)) {
+ this->fs = this->unique_fs.get();
+ Result rc = this->Initialize();
+ if (R_FAILED(rc)) {
+ fatalSimple(rc);
+ }
+ }
+
+ DirectorySaveDataFileSystem(std::shared_ptr fs) : shared_fs(fs) {
+ this->fs = this->shared_fs.get();
+ Result rc = this->Initialize();
+ if (R_FAILED(rc)) {
+ fatalSimple(rc);
+ }
+ }
+
+
+ virtual ~DirectorySaveDataFileSystem() { }
+
+ private:
+ Result Initialize();
+ protected:
+ Result SynchronizeDirectory(const FsPath &dst_dir, const FsPath &src_dir);
+ Result AllocateWorkBuffer(void **out_buf, size_t *out_size, const size_t ideal_size);
+
+ Result GetFullPath(char *out, size_t out_size, const char *relative_path);
+ Result GetFullPath(FsPath &full_path, const FsPath &relative_path) {
+ return GetFullPath(full_path.str, sizeof(full_path.str), relative_path.str);
+ }
+ public:
+ void OnWritableFileClose();
+ public:
+ virtual Result CreateFileImpl(const FsPath &path, uint64_t size, int flags) override;
+ virtual Result DeleteFileImpl(const FsPath &path) override;
+ virtual Result CreateDirectoryImpl(const FsPath &path) override;
+ virtual Result DeleteDirectoryImpl(const FsPath &path) override;
+ virtual Result DeleteDirectoryRecursivelyImpl(const FsPath &path) override;
+ virtual Result RenameFileImpl(const FsPath &old_path, const FsPath &new_path) override;
+ virtual Result RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) override;
+ virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) override;
+ virtual Result OpenFileImpl(std::unique_ptr &out_file, const FsPath &path, OpenMode mode) override;
+ virtual Result OpenDirectoryImpl(std::unique_ptr &out_dir, const FsPath &path, DirectoryOpenMode mode) override;
+ virtual Result CommitImpl() override;
+ virtual Result GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) override;
+ virtual Result GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) override;
+ virtual Result CleanDirectoryRecursivelyImpl(const FsPath &path) override;
+ virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) override;
+ virtual Result QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) override;
+};
\ No newline at end of file
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_ifile.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_ifile.hpp
index 8da0e5838..9f7cb17d2 100644
--- a/stratosphere/ams_mitm/source/fs_mitm/fs_ifile.hpp
+++ b/stratosphere/ams_mitm/source/fs_mitm/fs_ifile.hpp
@@ -49,6 +49,10 @@ class IFile {
return ReadImpl(out, offset, buffer, size);
}
+ Result Read(uint64_t *out, uint64_t offset, void *buffer, uint64_t size) {
+ return Read(out, offset, buffer, size, 0);
+ }
+
Result GetSize(uint64_t *out) {
if (out == nullptr) {
return ResultFsNullptrArgument;
@@ -71,8 +75,14 @@ class IFile {
return WriteImpl(offset, buffer, size, flush);
}
- Result Write(uint64_t offset, void *buffer, uint64_t size) {
- return WriteImpl(offset, buffer, size, false);
+ Result Write(uint64_t offset, void *buffer, uint64_t size, bool flush = false) {
+ if (size == 0) {
+ return 0;
+ }
+ if (buffer == nullptr) {
+ return ResultFsNullptrArgument;
+ }
+ return WriteImpl(offset, buffer, size, flush);
}
Result SetSize(uint64_t size) {
@@ -162,7 +172,7 @@ class ProxyFile : public IFile {
fsFileClose(this->base_file.get());
}
public:
- virtual Result ReadImpl(u64 *out, u64 offset, void *buffer, u64 size) {
+ virtual Result ReadImpl(u64 *out, u64 offset, void *buffer, u64 size) override {
size_t out_sz;
Result rc = fsFileRead(this->base_file.get(), offset, buffer, size, &out_sz);
@@ -172,13 +182,13 @@ class ProxyFile : public IFile {
return rc;
}
- virtual Result GetSizeImpl(u64 *out) {
+ virtual Result GetSizeImpl(u64 *out) override {
return fsFileGetSize(this->base_file.get(), out);
}
- virtual Result FlushImpl() {
+ virtual Result FlushImpl() override {
return fsFileFlush(this->base_file.get());
}
- virtual Result WriteImpl(u64 offset, void *buffer, u64 size, bool flush) {
+ virtual Result WriteImpl(u64 offset, void *buffer, u64 size, bool flush) override {
Result rc = fsFileWrite(this->base_file.get(), offset, buffer, size);
if (R_SUCCEEDED(rc)) {
/* libnx doesn't allow passing the flush flag. */
@@ -186,10 +196,10 @@ class ProxyFile : public IFile {
}
return rc;
}
- virtual Result SetSizeImpl(u64 size) {
+ virtual Result SetSizeImpl(u64 size) override {
return fsFileSetSize(this->base_file.get(), size);
}
- virtual Result OperateRangeImpl(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) {
+ virtual Result OperateRangeImpl(u32 operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
return fsFileOperateRange(this->base_file.get(), operation_type, offset, size, out_range_info);
}
};
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_ifilesystem.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_ifilesystem.hpp
index 2b3fda4c0..4e176302a 100644
--- a/stratosphere/ams_mitm/source/fs_mitm/fs_ifilesystem.hpp
+++ b/stratosphere/ams_mitm/source/fs_mitm/fs_ifilesystem.hpp
@@ -58,42 +58,46 @@ class IFileSystem {
public:
virtual ~IFileSystem() {}
- Result CreateFile(FsPath &path, uint64_t size, int flags) {
+ Result CreateFile(const FsPath &path, uint64_t size, int flags) {
return CreateFileImpl(path, size, flags);
}
- Result DeleteFile(FsPath &path) {
+ Result CreateFile(const FsPath &path, uint64_t size) {
+ return CreateFileImpl(path, size, 0);
+ }
+
+ Result DeleteFile(const FsPath &path) {
return DeleteFileImpl(path);
}
- Result CreateDirectory(FsPath &path) {
+ Result CreateDirectory(const FsPath &path) {
return CreateDirectoryImpl(path);
}
- Result DeleteDirectory(FsPath &path) {
+ Result DeleteDirectory(const FsPath &path) {
return DeleteDirectoryImpl(path);
}
- Result DeleteDirectoryRecursively(FsPath &path) {
+ Result DeleteDirectoryRecursively(const FsPath &path) {
return DeleteDirectoryRecursivelyImpl(path);
}
- Result RenameFile(FsPath &old_path, FsPath &new_path) {
+ Result RenameFile(const FsPath &old_path, const FsPath &new_path) {
return RenameFileImpl(old_path, new_path);
}
- Result RenameDirectory(FsPath &old_path, FsPath &new_path) {
+ Result RenameDirectory(const FsPath &old_path, const FsPath &new_path) {
return RenameDirectoryImpl(old_path, new_path);
}
- Result GetEntryType(DirectoryEntryType *out, FsPath &path) {
+ Result GetEntryType(DirectoryEntryType *out, const FsPath &path) {
if (out == nullptr) {
return ResultFsNullptrArgument;
}
return GetEntryTypeImpl(out, path);
}
- Result OpenFile(std::unique_ptr &out_file, FsPath &path, OpenMode mode) {
+ Result OpenFile(std::unique_ptr &out_file, const FsPath &path, OpenMode mode) {
if (!(mode & OpenMode_ReadWrite)) {
return ResultFsInvalidArgument;
}
@@ -103,7 +107,7 @@ class IFileSystem {
return OpenFileImpl(out_file, path, mode);
}
- Result OpenDirectory(std::unique_ptr &out_dir, FsPath &path, DirectoryOpenMode mode) {
+ Result OpenDirectory(std::unique_ptr &out_dir, const FsPath &path, DirectoryOpenMode mode) {
if (!(mode & DirectoryOpenMode_All)) {
return ResultFsInvalidArgument;
}
@@ -117,32 +121,32 @@ class IFileSystem {
return CommitImpl();
}
- Result GetFreeSpaceSize(uint64_t *out, FsPath &path) {
+ Result GetFreeSpaceSize(uint64_t *out, const FsPath &path) {
if (out == nullptr) {
return ResultFsNullptrArgument;
}
return GetFreeSpaceSizeImpl(out, path);
}
- Result GetTotalSpaceSize(uint64_t *out, FsPath &path) {
+ Result GetTotalSpaceSize(uint64_t *out, const FsPath &path) {
if (out == nullptr) {
return ResultFsNullptrArgument;
}
return GetTotalSpaceSizeImpl(out, path);
}
- Result CleanDirectoryRecursively(FsPath &path) {
+ Result CleanDirectoryRecursively(const FsPath &path) {
return CleanDirectoryRecursivelyImpl(path);
}
- Result GetFileTimeStampRaw(FsTimeStampRaw *out, FsPath &path) {
+ Result GetFileTimeStampRaw(FsTimeStampRaw *out, const FsPath &path) {
if (out == nullptr) {
return ResultFsNullptrArgument;
}
return GetFileTimeStampRawImpl(out, path);
}
- Result QueryEntry(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, FsPath &path) {
+ Result QueryEntry(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) {
return QueryEntryImpl(out, out_size, in, in_size, query, path);
}
@@ -150,39 +154,39 @@ class IFileSystem {
protected:
/* ...? */
private:
- virtual Result CreateFileImpl(FsPath &path, uint64_t size, int flags) = 0;
- virtual Result DeleteFileImpl(FsPath &path) = 0;
- virtual Result CreateDirectoryImpl(FsPath &path) = 0;
- virtual Result DeleteDirectoryImpl(FsPath &path) = 0;
- virtual Result DeleteDirectoryRecursivelyImpl(FsPath &path) = 0;
- virtual Result RenameFileImpl(FsPath &old_path, FsPath &new_path) = 0;
- virtual Result RenameDirectoryImpl(FsPath &old_path, FsPath &new_path) = 0;
- virtual Result GetEntryTypeImpl(DirectoryEntryType *out, FsPath &path) = 0;
- virtual Result OpenFileImpl(std::unique_ptr &out_file, FsPath &path, OpenMode mode) = 0;
- virtual Result OpenDirectoryImpl(std::unique_ptr &out_dir, FsPath &path, DirectoryOpenMode mode) = 0;
+ virtual Result CreateFileImpl(const FsPath &path, uint64_t size, int flags) = 0;
+ virtual Result DeleteFileImpl(const FsPath &path) = 0;
+ virtual Result CreateDirectoryImpl(const FsPath &path) = 0;
+ virtual Result DeleteDirectoryImpl(const FsPath &path) = 0;
+ virtual Result DeleteDirectoryRecursivelyImpl(const FsPath &path) = 0;
+ virtual Result RenameFileImpl(const FsPath &old_path, const FsPath &new_path) = 0;
+ virtual Result RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) = 0;
+ virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) = 0;
+ virtual Result OpenFileImpl(std::unique_ptr &out_file, const FsPath &path, OpenMode mode) = 0;
+ virtual Result OpenDirectoryImpl(std::unique_ptr &out_dir, const FsPath &path, DirectoryOpenMode mode) = 0;
virtual Result CommitImpl() = 0;
- virtual Result GetFreeSpaceSizeImpl(uint64_t *out, FsPath &path) {
+ virtual Result GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) {
(void)(out);
(void)(path);
return ResultFsNotImplemented;
}
- virtual Result GetTotalSpaceSizeImpl(uint64_t *out, FsPath &path) {
+ virtual Result GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) {
(void)(out);
(void)(path);
return ResultFsNotImplemented;
}
- virtual Result CleanDirectoryRecursivelyImpl(FsPath &path) = 0;
+ virtual Result CleanDirectoryRecursivelyImpl(const FsPath &path) = 0;
- virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, FsPath &path) {
+ virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) {
(void)(out);
(void)(path);
return ResultFsNotImplemented;
}
- virtual Result QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, FsPath &path) {
+ virtual Result QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) {
(void)(out);
(void)(out_size);
(void)(in);
@@ -449,35 +453,35 @@ class ProxyFileSystem : public IFileSystem {
}
public:
- virtual Result CreateFileImpl(FsPath &path, uint64_t size, int flags) {
+ virtual Result CreateFileImpl(const FsPath &path, uint64_t size, int flags) {
return fsFsCreateFile(this->base_fs.get(), path.str, size, flags);
}
- virtual Result DeleteFileImpl(FsPath &path) {
+ virtual Result DeleteFileImpl(const FsPath &path) {
return fsFsDeleteFile(this->base_fs.get(), path.str);
}
- virtual Result CreateDirectoryImpl(FsPath &path) {
+ virtual Result CreateDirectoryImpl(const FsPath &path) {
return fsFsCreateDirectory(this->base_fs.get(), path.str);
}
- virtual Result DeleteDirectoryImpl(FsPath &path) {
+ virtual Result DeleteDirectoryImpl(const FsPath &path) {
return fsFsDeleteDirectory(this->base_fs.get(), path.str);
}
- virtual Result DeleteDirectoryRecursivelyImpl(FsPath &path) {
+ virtual Result DeleteDirectoryRecursivelyImpl(const FsPath &path) {
return fsFsDeleteDirectoryRecursively(this->base_fs.get(), path.str);
}
- virtual Result RenameFileImpl(FsPath &old_path, FsPath &new_path) {
+ virtual Result RenameFileImpl(const FsPath &old_path, const FsPath &new_path) {
return fsFsRenameFile(this->base_fs.get(), old_path.str, new_path.str);
}
- virtual Result RenameDirectoryImpl(FsPath &old_path, FsPath &new_path) {
+ virtual Result RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) {
return fsFsRenameDirectory(this->base_fs.get(), old_path.str, new_path.str);
}
- virtual Result GetEntryTypeImpl(DirectoryEntryType *out, FsPath &path) {
+ virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) {
FsEntryType type;
Result rc = fsFsGetEntryType(this->base_fs.get(), path.str, &type);
@@ -487,7 +491,7 @@ class ProxyFileSystem : public IFileSystem {
return rc;
}
- virtual Result OpenFileImpl(std::unique_ptr &out_file, FsPath &path, OpenMode mode) {
+ virtual Result OpenFileImpl(std::unique_ptr &out_file, const FsPath &path, OpenMode mode) {
FsFile f;
Result rc = fsFsOpenFile(this->base_fs.get(), path.str, static_cast(mode), &f);
@@ -498,7 +502,7 @@ class ProxyFileSystem : public IFileSystem {
return rc;
}
- virtual Result OpenDirectoryImpl(std::unique_ptr &out_dir, FsPath &path, DirectoryOpenMode mode) {
+ virtual Result OpenDirectoryImpl(std::unique_ptr &out_dir, const FsPath &path, DirectoryOpenMode mode) {
FsDir d;
Result rc = fsFsOpenDirectory(this->base_fs.get(), path.str, static_cast(mode), &d);
@@ -513,23 +517,23 @@ class ProxyFileSystem : public IFileSystem {
return fsFsCommit(this->base_fs.get());
}
- virtual Result GetFreeSpaceSizeImpl(uint64_t *out, FsPath &path) {
+ virtual Result GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) {
return fsFsGetFreeSpace(this->base_fs.get(), path.str, out);
}
- virtual Result GetTotalSpaceSizeImpl(uint64_t *out, FsPath &path) {
+ virtual Result GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) {
return fsFsGetTotalSpace(this->base_fs.get(), path.str, out);
}
- virtual Result CleanDirectoryRecursivelyImpl(FsPath &path) {
+ virtual Result CleanDirectoryRecursivelyImpl(const FsPath &path) {
return fsFsCleanDirectoryRecursively(this->base_fs.get(), path.str);
}
- virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, FsPath &path) {
+ virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) {
return fsFsGetFileTimeStampRaw(this->base_fs.get(), path.str, out);
}
- virtual Result QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, FsPath &path) {
+ virtual Result QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) {
return fsFsQueryEntry(this->base_fs.get(), out, out_size, in, in_size, path.str,static_cast(query));
}
};
\ No newline at end of file
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_path_utils.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_path_utils.hpp
index 142fd25b7..b4ad49863 100644
--- a/stratosphere/ams_mitm/source/fs_mitm/fs_path_utils.hpp
+++ b/stratosphere/ams_mitm/source/fs_mitm/fs_path_utils.hpp
@@ -21,7 +21,31 @@ struct FsPath {
char str[FS_MAX_PATH];
};
+constexpr FsPath EncodeConstantFsPath(const char *p) {
+ FsPath path = {0};
+ for (size_t i = 0; i < sizeof(path) - 1; i++) {
+ path.str[i] = p[i];
+ if (p[i] == 0) {
+ break;
+ }
+ }
+ return path;
+}
+
+constexpr size_t GetConstantFsPathLen(FsPath path) {
+ size_t len = 0;
+ for (size_t i = 0; i < sizeof(path) - 1; i++) {
+ if (path.str[i] == 0) {
+ break;
+ }
+ len++;
+ }
+ return len;
+}
+
class FsPathUtils {
+ public:
+ static constexpr FsPath RootPath = EncodeConstantFsPath("/");
public:
static Result VerifyPath(const char *path, size_t max_path_len, size_t max_name_len);
static Result ConvertPathForServiceObject(FsPath *out, const char *path);
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_results.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_results.hpp
index 9dd12dd60..3bcf5d50a 100644
--- a/stratosphere/ams_mitm/source/fs_mitm/fs_results.hpp
+++ b/stratosphere/ams_mitm/source/fs_mitm/fs_results.hpp
@@ -19,11 +19,19 @@
static constexpr u32 Module_Fs = 2;
+static constexpr Result ResultFsPathNotFound = MAKERESULT(Module_Fs, 1);
+static constexpr Result ResultFsPathAlreadyExists = MAKERESULT(Module_Fs, 2);
+
+static constexpr Result ResultFsTargetLocked = MAKERESULT(Module_Fs, 7);
+static constexpr Result ResultFsDirectoryNotEmpty = MAKERESULT(Module_Fs, 8);
+
static constexpr Result ResultFsNotImplemented = MAKERESULT(Module_Fs, 3001);
static constexpr Result ResultFsOutOfRange = MAKERESULT(Module_Fs, 3005);
-static constexpr Result ResultFsAllocationFailureInSubDirectoryFileSystem = MAKERESULT(Module_Fs, 3355);
+static constexpr Result ResultFsAllocationFailureInDirectorySaveDataFileSystem = MAKERESULT(Module_Fs, 3321);
+static constexpr Result ResultFsAllocationFailureInSubDirectoryFileSystem = MAKERESULT(Module_Fs, 3355);
+static constexpr Result ResultFsPreconditionViolation = MAKERESULT(Module_Fs, 6000);
static constexpr Result ResultFsInvalidArgument = MAKERESULT(Module_Fs, 6001);
static constexpr Result ResultFsInvalidPath = MAKERESULT(Module_Fs, 6002);
static constexpr Result ResultFsTooLongPath = MAKERESULT(Module_Fs, 6003);
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_subdirectory_filesystem.cpp b/stratosphere/ams_mitm/source/fs_mitm/fs_subdirectory_filesystem.cpp
index 6d14c418a..88622e814 100644
--- a/stratosphere/ams_mitm/source/fs_mitm/fs_subdirectory_filesystem.cpp
+++ b/stratosphere/ams_mitm/source/fs_mitm/fs_subdirectory_filesystem.cpp
@@ -71,7 +71,7 @@ Result SubDirectoryFileSystem::GetFullPath(char *out, size_t out_size, const cha
return FsPathUtils::Normalize(out + this->base_path_len - 2, out_size - (this->base_path_len - 2), relative_path, nullptr);
}
-Result SubDirectoryFileSystem::CreateFileImpl(FsPath &path, uint64_t size, int flags) {
+Result SubDirectoryFileSystem::CreateFileImpl(const FsPath &path, uint64_t size, int flags) {
Result rc;
FsPath full_path;
@@ -82,7 +82,7 @@ Result SubDirectoryFileSystem::CreateFileImpl(FsPath &path, uint64_t size, int f
return this->base_fs->CreateFile(full_path, size, flags);
}
-Result SubDirectoryFileSystem::DeleteFileImpl(FsPath &path) {
+Result SubDirectoryFileSystem::DeleteFileImpl(const FsPath &path) {
Result rc;
FsPath full_path;
@@ -93,7 +93,7 @@ Result SubDirectoryFileSystem::DeleteFileImpl(FsPath &path) {
return this->base_fs->DeleteFile(full_path);
}
-Result SubDirectoryFileSystem::CreateDirectoryImpl(FsPath &path) {
+Result SubDirectoryFileSystem::CreateDirectoryImpl(const FsPath &path) {
Result rc;
FsPath full_path;
@@ -104,7 +104,7 @@ Result SubDirectoryFileSystem::CreateDirectoryImpl(FsPath &path) {
return this->base_fs->CreateDirectory(full_path);
}
-Result SubDirectoryFileSystem::DeleteDirectoryImpl(FsPath &path) {
+Result SubDirectoryFileSystem::DeleteDirectoryImpl(const FsPath &path) {
Result rc;
FsPath full_path;
@@ -115,7 +115,7 @@ Result SubDirectoryFileSystem::DeleteDirectoryImpl(FsPath &path) {
return this->base_fs->DeleteDirectory(full_path);
}
-Result SubDirectoryFileSystem::DeleteDirectoryRecursivelyImpl(FsPath &path) {
+Result SubDirectoryFileSystem::DeleteDirectoryRecursivelyImpl(const FsPath &path) {
Result rc;
FsPath full_path;
@@ -126,7 +126,7 @@ Result SubDirectoryFileSystem::DeleteDirectoryRecursivelyImpl(FsPath &path) {
return this->base_fs->DeleteDirectoryRecursively(full_path);
}
-Result SubDirectoryFileSystem::RenameFileImpl(FsPath &old_path, FsPath &new_path) {
+Result SubDirectoryFileSystem::RenameFileImpl(const FsPath &old_path, const FsPath &new_path) {
Result rc;
FsPath full_old_path, full_new_path;
@@ -141,7 +141,7 @@ Result SubDirectoryFileSystem::RenameFileImpl(FsPath &old_path, FsPath &new_path
return this->base_fs->RenameFile(full_old_path, full_new_path);
}
-Result SubDirectoryFileSystem::RenameDirectoryImpl(FsPath &old_path, FsPath &new_path) {
+Result SubDirectoryFileSystem::RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) {
Result rc;
FsPath full_old_path, full_new_path;
@@ -156,7 +156,7 @@ Result SubDirectoryFileSystem::RenameDirectoryImpl(FsPath &old_path, FsPath &new
return this->base_fs->RenameDirectory(full_old_path, full_new_path);
}
-Result SubDirectoryFileSystem::GetEntryTypeImpl(DirectoryEntryType *out, FsPath &path) {
+Result SubDirectoryFileSystem::GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) {
Result rc;
FsPath full_path;
@@ -167,7 +167,7 @@ Result SubDirectoryFileSystem::GetEntryTypeImpl(DirectoryEntryType *out, FsPath
return this->base_fs->GetEntryType(out, full_path);
}
-Result SubDirectoryFileSystem::OpenFileImpl(std::unique_ptr &out_file, FsPath &path, OpenMode mode) {
+Result SubDirectoryFileSystem::OpenFileImpl(std::unique_ptr &out_file, const FsPath &path, OpenMode mode) {
Result rc;
FsPath full_path;
@@ -178,7 +178,7 @@ Result SubDirectoryFileSystem::OpenFileImpl(std::unique_ptr &out_file, Fs
return this->base_fs->OpenFile(out_file, full_path, mode);
}
-Result SubDirectoryFileSystem::OpenDirectoryImpl(std::unique_ptr &out_dir, FsPath &path, DirectoryOpenMode mode) {
+Result SubDirectoryFileSystem::OpenDirectoryImpl(std::unique_ptr &out_dir, const FsPath &path, DirectoryOpenMode mode) {
Result rc;
FsPath full_path;
@@ -193,7 +193,7 @@ Result SubDirectoryFileSystem::CommitImpl() {
return this->base_fs->Commit();
}
-Result SubDirectoryFileSystem::GetFreeSpaceSizeImpl(uint64_t *out, FsPath &path) {
+Result SubDirectoryFileSystem::GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) {
Result rc;
FsPath full_path;
@@ -204,7 +204,7 @@ Result SubDirectoryFileSystem::GetFreeSpaceSizeImpl(uint64_t *out, FsPath &path)
return this->base_fs->GetFreeSpaceSize(out, full_path);
}
-Result SubDirectoryFileSystem::GetTotalSpaceSizeImpl(uint64_t *out, FsPath &path) {
+Result SubDirectoryFileSystem::GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) {
Result rc;
FsPath full_path;
@@ -215,7 +215,7 @@ Result SubDirectoryFileSystem::GetTotalSpaceSizeImpl(uint64_t *out, FsPath &path
return this->base_fs->GetTotalSpaceSize(out, full_path);
}
-Result SubDirectoryFileSystem::CleanDirectoryRecursivelyImpl(FsPath &path) {
+Result SubDirectoryFileSystem::CleanDirectoryRecursivelyImpl(const FsPath &path) {
Result rc;
FsPath full_path;
@@ -226,7 +226,7 @@ Result SubDirectoryFileSystem::CleanDirectoryRecursivelyImpl(FsPath &path) {
return this->base_fs->CleanDirectoryRecursively(full_path);
}
-Result SubDirectoryFileSystem::GetFileTimeStampRawImpl(FsTimeStampRaw *out, FsPath &path) {
+Result SubDirectoryFileSystem::GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) {
Result rc;
FsPath full_path;
@@ -237,7 +237,7 @@ Result SubDirectoryFileSystem::GetFileTimeStampRawImpl(FsTimeStampRaw *out, FsPa
return this->base_fs->GetFileTimeStampRaw(out, full_path);
}
-Result SubDirectoryFileSystem::QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, FsPath &path) {
+Result SubDirectoryFileSystem::QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) {
Result rc;
FsPath full_path;
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_subdirectory_filesystem.hpp b/stratosphere/ams_mitm/source/fs_mitm/fs_subdirectory_filesystem.hpp
index e6c406fa1..d1a28cef3 100644
--- a/stratosphere/ams_mitm/source/fs_mitm/fs_subdirectory_filesystem.hpp
+++ b/stratosphere/ams_mitm/source/fs_mitm/fs_subdirectory_filesystem.hpp
@@ -53,25 +53,25 @@ class SubDirectoryFileSystem : public IFileSystem {
Result Initialize(const char *bp);
protected:
Result GetFullPath(char *out, size_t out_size, const char *relative_path);
- Result GetFullPath(FsPath &full_path, FsPath &relative_path) {
+ Result GetFullPath(FsPath &full_path, const FsPath &relative_path) {
return GetFullPath(full_path.str, sizeof(full_path.str), relative_path.str);
}
public:
- virtual Result CreateFileImpl(FsPath &path, uint64_t size, int flags) override;
- virtual Result DeleteFileImpl(FsPath &path) override;
- virtual Result CreateDirectoryImpl(FsPath &path) override;
- virtual Result DeleteDirectoryImpl(FsPath &path) override;
- virtual Result DeleteDirectoryRecursivelyImpl(FsPath &path) override;
- virtual Result RenameFileImpl(FsPath &old_path, FsPath &new_path) override;
- virtual Result RenameDirectoryImpl(FsPath &old_path, FsPath &new_path) override;
- virtual Result GetEntryTypeImpl(DirectoryEntryType *out, FsPath &path) override;
- virtual Result OpenFileImpl(std::unique_ptr &out_file, FsPath &path, OpenMode mode) override;
- virtual Result OpenDirectoryImpl(std::unique_ptr &out_dir, FsPath &path, DirectoryOpenMode mode) override;
+ virtual Result CreateFileImpl(const FsPath &path, uint64_t size, int flags) override;
+ virtual Result DeleteFileImpl(const FsPath &path) override;
+ virtual Result CreateDirectoryImpl(const FsPath &path) override;
+ virtual Result DeleteDirectoryImpl(const FsPath &path) override;
+ virtual Result DeleteDirectoryRecursivelyImpl(const FsPath &path) override;
+ virtual Result RenameFileImpl(const FsPath &old_path, const FsPath &new_path) override;
+ virtual Result RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) override;
+ virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) override;
+ virtual Result OpenFileImpl(std::unique_ptr &out_file, const FsPath &path, OpenMode mode) override;
+ virtual Result OpenDirectoryImpl(std::unique_ptr &out_dir, const FsPath &path, DirectoryOpenMode mode) override;
virtual Result CommitImpl() override;
- virtual Result GetFreeSpaceSizeImpl(uint64_t *out, FsPath &path) override;
- virtual Result GetTotalSpaceSizeImpl(uint64_t *out, FsPath &path) override;
- virtual Result CleanDirectoryRecursivelyImpl(FsPath &path) override;
- virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, FsPath &path) override;
- virtual Result QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, FsPath &path) override;
+ virtual Result GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) override;
+ virtual Result GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) override;
+ virtual Result CleanDirectoryRecursivelyImpl(const FsPath &path) override;
+ virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) override;
+ virtual Result QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) override;
};
\ No newline at end of file
diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp
index 33f6eda10..9541f2d4e 100644
--- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp
+++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_service.cpp
@@ -29,6 +29,7 @@
#include "fsmitm_layeredrom.hpp"
#include "fs_subdirectory_filesystem.hpp"
+#include "fs_directory_savedata_filesystem.hpp"
#include "../debug.hpp"