diff --git a/include/stratosphere/cfg/cfg_api.hpp b/include/stratosphere/cfg/cfg_api.hpp
index 747f7e35..e111164a 100644
--- a/include/stratosphere/cfg/cfg_api.hpp
+++ b/include/stratosphere/cfg/cfg_api.hpp
@@ -16,6 +16,7 @@
#pragma once
#include "cfg_types.hpp"
#include "cfg_locale_types.hpp"
+#include "../sm/sm_types.hpp"
namespace ams::cfg {
@@ -36,7 +37,7 @@ namespace ams::cfg {
OverrideLocale GetOverrideLocale(ncm::ProgramId program_id);
/* Flag utilities. */
- bool HasFlag(ncm::ProgramId program_id, const char *flag);
+ bool HasFlag(const sm::MitmProcessInfo &process_info, const char *flag);
bool HasContentSpecificFlag(ncm::ProgramId program_id, const char *flag);
bool HasGlobalFlag(const char *flag);
diff --git a/include/stratosphere/fs/fs_path_tool.hpp b/include/stratosphere/fs/fs_path_tool.hpp
index c74c6831..3143ca65 100644
--- a/include/stratosphere/fs/fs_path_tool.hpp
+++ b/include/stratosphere/fs/fs_path_tool.hpp
@@ -30,7 +30,7 @@ namespace ams::fs {
class PathTool {
public:
- static constexpr fssrv::sf::Path RootPath = fssrv::sf::FspPath::Encode("/");
+ static constexpr const char RootPath[] = "/";
public:
static constexpr inline bool IsSeparator(char c) {
return c == StringTraits::DirectorySeparator;
diff --git a/include/stratosphere/fs/fs_remote_filesystem.hpp b/include/stratosphere/fs/fs_remote_filesystem.hpp
index 8c39b461..c5b2942f 100644
--- a/include/stratosphere/fs/fs_remote_filesystem.hpp
+++ b/include/stratosphere/fs/fs_remote_filesystem.hpp
@@ -33,7 +33,7 @@ namespace ams::fs {
virtual ~RemoteFile() { fsFileClose(this->base_file.get()); }
public:
- virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const ReadOption &option) override final {
+ virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override final {
return fsFileRead(this->base_file.get(), offset, buffer, size, option.value, out);
}
@@ -45,7 +45,7 @@ namespace ams::fs {
return fsFileFlush(this->base_file.get());
}
- virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const WriteOption &option) override final {
+ virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override final {
return fsFileWrite(this->base_file.get(), offset, buffer, size, option.value);
}
@@ -53,7 +53,7 @@ namespace ams::fs {
return fsFileSetSize(this->base_file.get(), size);
}
- virtual Result OperateRangeImpl(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) override final {
+ 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 {
/* TODO: How should this be handled? */
return fs::ResultNotImplemented();
}
diff --git a/include/stratosphere/fs/fsa/fs_ifile.hpp b/include/stratosphere/fs/fsa/fs_ifile.hpp
index 9ecc920a..3cafb276 100644
--- a/include/stratosphere/fs/fsa/fs_ifile.hpp
+++ b/include/stratosphere/fs/fsa/fs_ifile.hpp
@@ -24,7 +24,7 @@ namespace ams::fs::fsa {
public:
virtual ~IFile() { /* ... */ }
- Result Read(size_t *out, s64 offset, void *buffer, size_t size, const ReadOption &option) {
+ Result Read(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) {
R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
if (size == 0) {
*out = 0;
@@ -51,7 +51,7 @@ namespace ams::fs::fsa {
return this->FlushImpl();
}
- Result Write(s64 offset, const void *buffer, size_t size, const WriteOption &option) {
+ Result Write(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) {
if (size == 0) {
if (option.HasFlushFlag()) {
R_TRY(this->Flush());
@@ -71,11 +71,11 @@ namespace ams::fs::fsa {
return this->SetSizeImpl(size);
}
- Result OperateRange(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
+ Result OperateRange(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) {
return this->OperateRangeImpl(dst, dst_size, op_id, offset, size, src, src_size);
}
- Result OperateRange(OperationId op_id, s64 offset, s64 size) {
+ Result OperateRange(fs::OperationId op_id, s64 offset, s64 size) {
return this->OperateRangeImpl(nullptr, 0, op_id, offset, size, nullptr, 0);
}
public:
@@ -84,12 +84,12 @@ namespace ams::fs::fsa {
protected:
/* ...? */
private:
- virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const ReadOption &option) = 0;
+ virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) = 0;
virtual Result GetSizeImpl(s64 *out) = 0;
virtual Result FlushImpl() = 0;
- virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const WriteOption &option) = 0;
+ virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) = 0;
virtual Result SetSizeImpl(s64 size) = 0;
- virtual Result OperateRangeImpl(void *dst, size_t dst_size, OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) = 0;
+ virtual Result OperateRangeImpl(void *dst, size_t dst_size, fs::OperationId op_id, s64 offset, s64 size, const void *src, size_t src_size) = 0;
};
}
diff --git a/include/stratosphere/fssystem.hpp b/include/stratosphere/fssystem.hpp
index 90d84444..d786b2e9 100644
--- a/include/stratosphere/fssystem.hpp
+++ b/include/stratosphere/fssystem.hpp
@@ -19,3 +19,4 @@
#include "fssystem/fssystem_path_tool.hpp"
#include "fssystem/fssystem_subdirectory_filesystem.hpp"
#include "fssystem/fssystem_directory_redirection_filesystem.hpp"
+#include "fssystem/fssystem_directory_savedata_filesystem.hpp"
diff --git a/include/stratosphere/fssystem/fssystem_directory_redirection_filesystem.hpp b/include/stratosphere/fssystem/fssystem_directory_redirection_filesystem.hpp
index 3d56f3e6..daa7babb 100644
--- a/include/stratosphere/fssystem/fssystem_directory_redirection_filesystem.hpp
+++ b/include/stratosphere/fssystem/fssystem_directory_redirection_filesystem.hpp
@@ -14,29 +14,33 @@
* along with this program. If not, see .
*/
#pragma once
-#include "fssystem_path_resolution_filesystem.hpp"
+#include "impl/fssystem_path_resolution_filesystem.hpp"
namespace ams::fssystem {
- class DirectoryRedirectionFileSystem : public IPathResolutionFileSystem {
+ class DirectoryRedirectionFileSystem : public impl::IPathResolutionFileSystem {
NON_COPYABLE(DirectoryRedirectionFileSystem);
private:
- using PathResolutionFileSystem = IPathResolutionFileSystem;
+ using PathResolutionFileSystem = impl::IPathResolutionFileSystem;
+ friend class impl::IPathResolutionFileSystem;
private:
char *before_dir;
size_t before_dir_len;
char *after_dir;
size_t after_dir_len;
- bool unc_preserved;
public:
- DirectoryRedirectionFileSystem(std::shared_ptr fs, const char *before, const char *after);
- DirectoryRedirectionFileSystem(std::shared_ptr fs, const char *before, const char *after, bool unc);
+ DirectoryRedirectionFileSystem(std::shared_ptr fs, const char *before, const char *after, bool unc = false);
+ DirectoryRedirectionFileSystem(std::unique_ptr &&fs, const char *before, const char *after, bool unc = false);
virtual ~DirectoryRedirectionFileSystem();
+ protected:
+ inline std::optional> GetAccessorLock() const {
+ /* No accessor lock is needed. */
+ return std::nullopt;
+ }
private:
Result GetNormalizedDirectoryPath(char **out, size_t *out_size, const char *dir);
Result Initialize(const char *before, const char *after);
- public:
Result ResolveFullPath(char *out, size_t out_size, const char *relative_path);
};
diff --git a/include/stratosphere/fssystem/fssystem_directory_savedata_filesystem.hpp b/include/stratosphere/fssystem/fssystem_directory_savedata_filesystem.hpp
new file mode 100644
index 00000000..2014ea43
--- /dev/null
+++ b/include/stratosphere/fssystem/fssystem_directory_savedata_filesystem.hpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2018-2019 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 "impl/fssystem_path_resolution_filesystem.hpp"
+
+namespace ams::fssystem {
+
+ class DirectorySaveDataFileSystem : public impl::IPathResolutionFileSystem {
+ NON_COPYABLE(DirectorySaveDataFileSystem);
+ private:
+ using PathResolutionFileSystem = impl::IPathResolutionFileSystem;
+ friend class impl::IPathResolutionFileSystem;
+ private:
+ os::Mutex accessor_mutex;
+ s32 open_writable_files;
+ public:
+ DirectorySaveDataFileSystem(std::shared_ptr fs);
+ DirectorySaveDataFileSystem(std::unique_ptr fs);
+ Result Initialize();
+
+ virtual ~DirectorySaveDataFileSystem();
+ protected:
+ inline std::optional> GetAccessorLock() {
+ /* We have a real accessor lock that we want to use. */
+ return std::make_optional>(this->accessor_mutex);
+ }
+ private:
+ Result AllocateWorkBuffer(std::unique_ptr *out, size_t *out_size, size_t ideal_size);
+ Result SynchronizeDirectory(const char *dst, const char *src);
+ Result ResolveFullPath(char *out, size_t out_size, const char *relative_path);
+ public:
+ void OnWritableFileClose();
+ Result CopySaveFromFileSystem(fs::fsa::IFileSystem *save_fs);
+ public:
+ /* Overridden from IPathResolutionFileSystem */
+ virtual Result OpenFileImpl(std::unique_ptr *out_file, const char *path, fs::OpenMode mode) override;
+ virtual Result CommitImpl() override;
+
+ /* Overridden from IPathResolutionFileSystem but not commands. */
+ virtual Result CommitProvisionallyImpl(s64 counter) override;
+ virtual Result RollbackImpl() override;
+
+ /* Explicitly overridden to be not implemented. */
+ virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) override;
+ virtual Result GetTotalSpaceSizeImpl(s64 *out, const char *path) override;
+ virtual Result GetFileTimeStampRawImpl(fs::FileTimeStampRaw *out, const char *path) override;
+ virtual Result QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, fs::fsa::QueryId query, const char *path) override;
+ virtual Result FlushImpl() override;
+ };
+
+}
diff --git a/include/stratosphere/fssystem/fssystem_subdirectory_filesystem.hpp b/include/stratosphere/fssystem/fssystem_subdirectory_filesystem.hpp
index 0f827e06..99a8213c 100644
--- a/include/stratosphere/fssystem/fssystem_subdirectory_filesystem.hpp
+++ b/include/stratosphere/fssystem/fssystem_subdirectory_filesystem.hpp
@@ -14,26 +14,30 @@
* along with this program. If not, see .
*/
#pragma once
-#include "fssystem_path_resolution_filesystem.hpp"
+#include "impl/fssystem_path_resolution_filesystem.hpp"
namespace ams::fssystem {
- class SubDirectoryFileSystem : public IPathResolutionFileSystem {
+ class SubDirectoryFileSystem : public impl::IPathResolutionFileSystem {
NON_COPYABLE(SubDirectoryFileSystem);
private:
- using PathResolutionFileSystem = IPathResolutionFileSystem;
+ using PathResolutionFileSystem = impl::IPathResolutionFileSystem;
+ friend class impl::IPathResolutionFileSystem;
private:
char *base_path;
size_t base_path_len;
- bool unc_preserved;
public:
- SubDirectoryFileSystem(std::shared_ptr fs, const char *bp);
- SubDirectoryFileSystem(std::shared_ptr fs, const char *bp, bool unc);
+ SubDirectoryFileSystem(std::shared_ptr fs, const char *bp, bool unc = false);
+ SubDirectoryFileSystem(std::unique_ptr &&fs, const char *bp, bool unc = false);
virtual ~SubDirectoryFileSystem();
+ protected:
+ inline std::optional> GetAccessorLock() const {
+ /* No accessor lock is needed. */
+ return std::nullopt;
+ }
private:
Result Initialize(const char *bp);
- public:
Result ResolveFullPath(char *out, size_t out_size, const char *relative_path);
};
diff --git a/include/stratosphere/fssystem/fssystem_utility.hpp b/include/stratosphere/fssystem/fssystem_utility.hpp
index 4376c601..62aa3bf5 100644
--- a/include/stratosphere/fssystem/fssystem_utility.hpp
+++ b/include/stratosphere/fssystem/fssystem_utility.hpp
@@ -15,9 +15,119 @@
*/
#pragma once
#include "../fs/fs_common.hpp"
+#include "../fs/fs_file.hpp"
+#include "../fs/fs_directory.hpp"
+#include "../fs/fs_filesystem.hpp"
+#include "fssystem_path_tool.hpp"
namespace ams::fssystem {
+ namespace impl {
+
+ /* Iteration. */
+ template
+ Result IterateDirectoryRecursivelyImpl(fs::fsa::IFileSystem *fs, char *work_path, size_t work_path_size, fs::DirectoryEntry *dir_ent, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
+ /* Open the directory. */
+ std::unique_ptr dir;
+ R_TRY(fs->OpenDirectory(&dir, work_path, fs::OpenDirectoryMode_All));
+
+ const size_t parent_len = strnlen(work_path, work_path_size - 1);
+
+ /* Read and handle entries. */
+ while (true) {
+ /* Read a single entry. */
+ s64 read_count = 0;
+ R_TRY(dir->Read(&read_count, dir_ent, 1));
+
+ /* If we're out of entries, we're done. */
+ if (read_count == 0) {
+ break;
+ }
+
+ /* Validate child path size. */
+ const size_t child_name_len = strnlen(dir_ent->name, sizeof(dir_ent->name) - 1);
+ const bool is_dir = dir_ent->type == fs::DirectoryEntryType_Directory;
+ const size_t separator_size = is_dir ? 1 : 0;
+ R_UNLESS(parent_len + child_name_len + separator_size < work_path_size, fs::ResultTooLongPath());
+
+ /* Set child path. */
+ std::strncat(work_path, dir_ent->name, work_path_size - parent_len - 1);
+ {
+ if (is_dir) {
+ /* Enter directory. */
+ R_TRY(on_enter_dir(work_path, *dir_ent));
+
+ /* Append separator, recurse. */
+ std::strncat(work_path, "/", work_path_size - (parent_len + child_name_len) - 1);
+ R_TRY(IterateDirectoryRecursivelyImpl(fs, work_path, work_path_size, dir_ent, on_enter_dir, on_exit_dir, on_file));
+
+ /* Exit directory. */
+ R_TRY(on_exit_dir(work_path, *dir_ent));
+ } else {
+ /* Call file handler. */
+ R_TRY(on_file(work_path, *dir_ent));
+ }
+ }
+
+ /* Restore parent path. */
+ work_path[parent_len] = StringTraits::NullTerminator;
+ }
+
+ return ResultSuccess();
+ }
+
+ /* TODO: Cleanup. */
+
+ }
+
+ /* Iteration API */
+ template
+ Result IterateDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *root_path, char *work_path, size_t work_path_size, fs::DirectoryEntry *dir_ent_buf, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
+ AMS_ASSERT(work_path_size >= fs::EntryNameLengthMax + 1);
+
+ /* Get size of the root path. */
+ size_t root_path_len = strnlen(root_path, fs::EntryNameLengthMax + 1);
+ R_UNLESS(root_path_len <= fs::EntryNameLengthMax, fs::ResultTooLongPath());
+
+ /* Copy root path in, add a / if necessary. */
+ std::memcpy(work_path, root_path, root_path_len);
+ if (!PathTool::IsSeparator(work_path[root_path_len - 1])) {
+ work_path[root_path_len++] = StringTraits::DirectorySeparator;
+ }
+
+ /* Make sure the result path is still valid. */
+ R_UNLESS(root_path_len <= fs::EntryNameLengthMax, fs::ResultTooLongPath());
+ work_path[root_path_len] = StringTraits::NullTerminator;
+
+ return impl::IterateDirectoryRecursivelyImpl(fs, work_path, work_path_size, dir_ent_buf, on_enter_dir, on_exit_dir, on_file);
+ }
+
+ template
+ Result IterateDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *root_path, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
+ fs::DirectoryEntry dir_entry = {};
+ char work_path[fs::EntryNameLengthMax + 1] = {};
+ return IterateDirectoryRecursively(fs, root_path, work_path, sizeof(work_path), &dir_entry, on_enter_dir, on_exit_dir, on_file);
+ }
+
+ template
+ Result IterateDirectoryRecursively(fs::fsa::IFileSystem *fs, OnEnterDir on_enter_dir, OnExitDir on_exit_dir, OnFile on_file) {
+ return IterateDirectoryRecursively(fs, PathTool::RootPath, on_enter_dir, on_exit_dir, on_file);
+ }
+
+ /* TODO: Cleanup API */
+
+ /* Copy API. */
+ Result CopyFile(fs::fsa::IFileSystem *dst_fs, fs::fsa::IFileSystem *src_fs, const char *dst_parent_path, const char *src_path, const fs::DirectoryEntry *dir_ent, void *work_buf, size_t work_buf_size);
+ NX_INLINE Result CopyFile(fs::fsa::IFileSystem *fs, const char *dst_parent_path, const char *src_path, const fs::DirectoryEntry *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);
+ }
+
+ Result CopyDirectoryRecursively(fs::fsa::IFileSystem *dst_fs, fs::fsa::IFileSystem *src_fs, const char *dst_path, const char *src_path, void *work_buf, size_t work_buf_size);
+ NX_INLINE Result CopyDirectoryRecursively(fs::fsa::IFileSystem *fs, const char *dst_path, const char *src_path, void *work_buf, size_t work_buf_size) {
+ return CopyDirectoryRecursively(fs, fs, dst_path, src_path, work_buf, work_buf_size);
+ }
+
+ /* Semaphore adapter class. */
class SemaphoreAdapter : public os::Semaphore {
public:
SemaphoreAdapter(int c, int mc) : os::Semaphore(c, mc) { /* ... */ }
@@ -31,4 +141,29 @@ namespace ams::fssystem {
}
};
+ /* Other utility. */
+ Result EnsureDirectoryExistsRecursively(fs::fsa::IFileSystem *fs, const char *path);
+
+ template
+ NX_INLINE Result RetryFinitelyForTargetLocked(F f) {
+ /* Retry up to 10 times, 100ms between retries. */
+ constexpr s32 MaxRetryCount = 10;
+ constexpr u64 RetryWaitTime = 100'000'000ul;
+
+ s32 remaining_retries = MaxRetryCount;
+ while (true) {
+ R_TRY_CATCH(f()) {
+ R_CATCH(fs::ResultTargetLocked) {
+ R_UNLESS(remaining_retries > 0, fs::ResultTargetLocked());
+
+ remaining_retries--;
+ svcSleepThread(RetryWaitTime);
+ continue;
+ }
+ } R_END_TRY_CATCH;
+
+ return ResultSuccess();
+ }
+ }
+
}
diff --git a/include/stratosphere/fssystem/fssystem_path_resolution_filesystem.hpp b/include/stratosphere/fssystem/impl/fssystem_path_resolution_filesystem.hpp
similarity index 75%
rename from include/stratosphere/fssystem/fssystem_path_resolution_filesystem.hpp
rename to include/stratosphere/fssystem/impl/fssystem_path_resolution_filesystem.hpp
index d0d99e2b..93908597 100644
--- a/include/stratosphere/fssystem/fssystem_path_resolution_filesystem.hpp
+++ b/include/stratosphere/fssystem/impl/fssystem_path_resolution_filesystem.hpp
@@ -14,27 +14,29 @@
* along with this program. If not, see .
*/
#pragma once
-#include "../fs/fs_common.hpp"
-#include "../fs/fsa/fs_ifile.hpp"
-#include "../fs/fsa/fs_idirectory.hpp"
-#include "../fs/fsa/fs_ifilesystem.hpp"
+#include "../../fs/fs_common.hpp"
+#include "../../fs/fsa/fs_ifile.hpp"
+#include "../../fs/fsa/fs_idirectory.hpp"
+#include "../../fs/fsa/fs_ifilesystem.hpp"
-namespace ams::fssystem {
+namespace ams::fssystem::impl {
template
class IPathResolutionFileSystem : public fs::fsa::IFileSystem {
NON_COPYABLE(IPathResolutionFileSystem);
private:
std::shared_ptr shared_fs;
- fs::fsa::IFileSystem *base_fs;
+ std::unique_ptr unique_fs;
bool unc_preserved;
+ protected:
+ fs::fsa::IFileSystem * const base_fs;
public:
- IPathResolutionFileSystem(std::shared_ptr fs) : shared_fs(std::move(fs)), unc_preserved(false) {
- this->base_fs = this->shared_fs.get();
+ IPathResolutionFileSystem(std::shared_ptr fs, bool unc = false) : shared_fs(std::move(fs)), unc_preserved(unc), base_fs(shared_fs.get()) {
+ /* ... */
}
- IPathResolutionFileSystem(std::shared_ptr fs, bool unc) : shared_fs(std::move(fs)), unc_preserved(unc) {
- this->base_fs = this->shared_fs.get();
+ IPathResolutionFileSystem(std::unique_ptr &&fs, bool unc = false) : unique_fs(std::move(fs)), unc_preserved(unc), base_fs(unique_fs.get()) {
+ /* ... */
}
virtual ~IPathResolutionFileSystem() { /* ... */ }
@@ -47,6 +49,7 @@ namespace ams::fssystem {
char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path));
+ std::optional optional_lock = static_cast(this)->GetAccessorLock();
return this->base_fs->CreateFile(full_path, size, option);
}
@@ -54,6 +57,7 @@ namespace ams::fssystem {
char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path));
+ std::optional optional_lock = static_cast(this)->GetAccessorLock();
return this->base_fs->DeleteFile(full_path);
}
@@ -61,6 +65,7 @@ namespace ams::fssystem {
char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path));
+ std::optional optional_lock = static_cast(this)->GetAccessorLock();
return this->base_fs->CreateDirectory(full_path);
}
@@ -68,6 +73,7 @@ namespace ams::fssystem {
char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path));
+ std::optional optional_lock = static_cast(this)->GetAccessorLock();
return this->base_fs->DeleteDirectory(full_path);
}
@@ -75,6 +81,7 @@ namespace ams::fssystem {
char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path));
+ std::optional optional_lock = static_cast(this)->GetAccessorLock();
return this->base_fs->DeleteDirectoryRecursively(full_path);
}
@@ -84,6 +91,7 @@ namespace ams::fssystem {
R_TRY(static_cast(this)->ResolveFullPath(old_full_path, sizeof(old_full_path), old_path));
R_TRY(static_cast(this)->ResolveFullPath(new_full_path, sizeof(new_full_path), new_path));
+ std::optional optional_lock = static_cast(this)->GetAccessorLock();
return this->base_fs->RenameFile(old_path, new_path);
}
@@ -93,6 +101,7 @@ namespace ams::fssystem {
R_TRY(static_cast(this)->ResolveFullPath(old_full_path, sizeof(old_full_path), old_path));
R_TRY(static_cast(this)->ResolveFullPath(new_full_path, sizeof(new_full_path), new_path));
+ std::optional optional_lock = static_cast(this)->GetAccessorLock();
return this->base_fs->RenameDirectory(old_path, new_path);
}
@@ -100,6 +109,7 @@ namespace ams::fssystem {
char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path));
+ std::optional optional_lock = static_cast(this)->GetAccessorLock();
return this->base_fs->GetEntryType(out, full_path);
}
@@ -107,6 +117,7 @@ namespace ams::fssystem {
char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path));
+ std::optional optional_lock = static_cast(this)->GetAccessorLock();
return this->base_fs->OpenFile(out_file, full_path, mode);
}
@@ -114,17 +125,20 @@ namespace ams::fssystem {
char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path));
+ std::optional optional_lock = static_cast(this)->GetAccessorLock();
return this->base_fs->OpenDirectory(out_dir, full_path, mode);
}
virtual Result CommitImpl() override {
- return this->base_fs->Rollback();
+ std::optional optional_lock = static_cast(this)->GetAccessorLock();
+ return this->base_fs->Commit();
}
virtual Result GetFreeSpaceSizeImpl(s64 *out, const char *path) override {
char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path));
+ std::optional optional_lock = static_cast(this)->GetAccessorLock();
return this->base_fs->GetFreeSpaceSize(out, full_path);
}
@@ -132,6 +146,7 @@ namespace ams::fssystem {
char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path));
+ std::optional optional_lock = static_cast(this)->GetAccessorLock();
return this->base_fs->GetTotalSpaceSize(out, full_path);
}
@@ -139,6 +154,7 @@ namespace ams::fssystem {
char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path));
+ std::optional optional_lock = static_cast(this)->GetAccessorLock();
return this->base_fs->CleanDirectoryRecursively(full_path);
}
@@ -146,6 +162,7 @@ namespace ams::fssystem {
char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path));
+ std::optional optional_lock = static_cast(this)->GetAccessorLock();
return this->base_fs->GetFileTimeStampRaw(out, full_path);
}
@@ -153,19 +170,23 @@ namespace ams::fssystem {
char full_path[fs::EntryNameLengthMax + 1];
R_TRY(static_cast(this)->ResolveFullPath(full_path, sizeof(full_path), path));
+ std::optional optional_lock = static_cast(this)->GetAccessorLock();
return this->base_fs->QueryEntry(dst, dst_size, src, src_size, query, full_path);
}
/* These aren't accessible as commands. */
virtual Result CommitProvisionallyImpl(s64 counter) override {
+ std::optional optional_lock = static_cast(this)->GetAccessorLock();
return this->base_fs->CommitProvisionally(counter);
}
virtual Result RollbackImpl() override {
+ std::optional optional_lock = static_cast(this)->GetAccessorLock();
return this->base_fs->Rollback();
}
virtual Result FlushImpl() override {
+ std::optional optional_lock = static_cast(this)->GetAccessorLock();
return this->base_fs->Flush();
}
};
diff --git a/include/stratosphere/sf/cmif/sf_cmif_domain_service_object.hpp b/include/stratosphere/sf/cmif/sf_cmif_domain_service_object.hpp
index 77758190..d5f86dd2 100644
--- a/include/stratosphere/sf/cmif/sf_cmif_domain_service_object.hpp
+++ b/include/stratosphere/sf/cmif/sf_cmif_domain_service_object.hpp
@@ -56,6 +56,10 @@ namespace ams::sf::cmif {
return this->impl_metadata.GetOutObjectCount();
}
+ constexpr size_t GetImplOutHeadersSize() const {
+ return this->impl_metadata.GetOutHeadersSize();
+ }
+
constexpr size_t GetImplOutDataTotalSize() const {
return this->impl_metadata.GetOutDataSize() + this->impl_metadata.GetOutHeadersSize();
}
diff --git a/source/cfg/cfg_flags.cpp b/source/cfg/cfg_flags.cpp
index 2f29a865..8b5e0436 100644
--- a/source/cfg/cfg_flags.cpp
+++ b/source/cfg/cfg_flags.cpp
@@ -47,8 +47,8 @@ namespace ams::cfg {
}
/* Flag utilities. */
- bool HasFlag(ncm::ProgramId program_id, const char *flag) {
- return HasContentSpecificFlag(program_id, flag) || (IsHblProgramId(program_id) && HasHblFlag(flag));
+ bool HasFlag(const sm::MitmProcessInfo &process_info, const char *flag) {
+ return HasContentSpecificFlag(process_info.program_id, flag) || (process_info.override_status.IsHbl() && HasHblFlag(flag));
}
bool HasContentSpecificFlag(ncm::ProgramId program_id, const char *flag) {
diff --git a/source/fssystem/fssystem_directory_redirection_filesystem.cpp b/source/fssystem/fssystem_directory_redirection_filesystem.cpp
index 24d17928..0da78f51 100644
--- a/source/fssystem/fssystem_directory_redirection_filesystem.cpp
+++ b/source/fssystem/fssystem_directory_redirection_filesystem.cpp
@@ -17,13 +17,17 @@
namespace ams::fssystem {
- DirectoryRedirectionFileSystem::DirectoryRedirectionFileSystem(std::shared_ptr fs, const char *before, const char *after) : PathResolutionFileSystem(fs) {
+ DirectoryRedirectionFileSystem::DirectoryRedirectionFileSystem(std::shared_ptr fs, const char *before, const char *after, bool unc)
+ : PathResolutionFileSystem(fs, unc)
+ {
this->before_dir = nullptr;
this->after_dir = nullptr;
R_ASSERT(this->Initialize(before, after));
}
- DirectoryRedirectionFileSystem::DirectoryRedirectionFileSystem(std::shared_ptr fs, const char *before, const char *after, bool unc) : PathResolutionFileSystem(fs, unc) {
+ DirectoryRedirectionFileSystem::DirectoryRedirectionFileSystem(std::unique_ptr &&fs, const char *before, const char *after, bool unc)
+ : PathResolutionFileSystem(std::forward>(fs), unc)
+ {
this->before_dir = nullptr;
this->after_dir = nullptr;
R_ASSERT(this->Initialize(before, after));
diff --git a/source/fssystem/fssystem_directory_savedata_filesystem.cpp b/source/fssystem/fssystem_directory_savedata_filesystem.cpp
new file mode 100644
index 00000000..c7d7530b
--- /dev/null
+++ b/source/fssystem/fssystem_directory_savedata_filesystem.cpp
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2018-2019 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 {
+
+ constexpr size_t IdealWorkBufferSize = 0x100000; /* 1 MiB */
+
+ constexpr const char CommittedDirectoryPath[] = "/0/";
+ constexpr const char WorkingDirectoryPath[] = "/1/";
+ constexpr const char SynchronizingDirectoryPath[] = "/_/";
+
+ class DirectorySaveDataFile : public fs::fsa::IFile {
+ private:
+ std::unique_ptr base_file;
+ DirectorySaveDataFileSystem *parent_fs;
+ fs::OpenMode open_mode;
+ public:
+ DirectorySaveDataFile(std::unique_ptr f, DirectorySaveDataFileSystem *p, fs::OpenMode m) : base_file(std::move(f)), parent_fs(p), open_mode(m) {
+ /* ... */
+ }
+
+ virtual ~DirectorySaveDataFile() {
+ /* Observe closing of writable file. */
+ if (this->open_mode & fs::OpenMode_Write) {
+ this->parent_fs->OnWritableFileClose();
+ }
+ }
+ public:
+ virtual Result ReadImpl(size_t *out, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) override {
+ return this->base_file->Read(out, offset, buffer, size, option);
+ }
+
+ virtual Result GetSizeImpl(s64 *out) override {
+ return this->base_file->GetSize(out);
+ }
+
+ virtual Result FlushImpl() override {
+ return this->base_file->Flush();
+ }
+
+ virtual Result WriteImpl(s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) override {
+ return this->base_file->Write(offset, buffer, size, option);
+ }
+
+ virtual Result SetSizeImpl(s64 size) override {
+ return this->base_file->SetSize(size);
+ }
+
+ 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 {
+ return this->base_file->OperateRange(dst, dst_size, op_id, offset, size, src, src_size);
+ }
+ public:
+ virtual sf::cmif::DomainObjectId GetDomainObjectId() const override {
+ return this->base_file->GetDomainObjectId();
+ }
+ };
+
+ }
+
+ DirectorySaveDataFileSystem::DirectorySaveDataFileSystem(std::shared_ptr fs)
+ : PathResolutionFileSystem(fs)
+ {
+ /* ... */
+ }
+
+ DirectorySaveDataFileSystem::DirectorySaveDataFileSystem(std::unique_ptr fs)
+ : PathResolutionFileSystem(std::forward>(fs))
+ {
+ /* ... */
+ }
+
+ DirectorySaveDataFileSystem::~DirectorySaveDataFileSystem() {
+ /* ... */
+ }
+
+ Result DirectorySaveDataFileSystem::Initialize() {
+ /* Nintendo does not acquire the lock here, but I think we probably should. */
+ std::scoped_lock lk(this->accessor_mutex);
+
+ fs::DirectoryEntryType type;
+
+ /* Check that the working directory exists. */
+ R_TRY_CATCH(this->base_fs->GetEntryType(&type, WorkingDirectoryPath)) {
+ /* If path isn't found, create working directory and committed directory. */
+ R_CATCH(fs::ResultPathNotFound) {
+ R_TRY(this->base_fs->CreateDirectory(WorkingDirectoryPath));
+ R_TRY(this->base_fs->CreateDirectory(CommittedDirectoryPath));
+ }
+ } R_END_TRY_CATCH;
+
+ /* Now check for the committed directory. */
+ R_TRY_CATCH(this->base_fs->GetEntryType(&type, CommittedDirectoryPath)) {
+ /* Committed doesn't exist, so synchronize and rename. */
+ R_CATCH(fs::ResultPathNotFound) {
+ R_TRY(this->SynchronizeDirectory(SynchronizingDirectoryPath, WorkingDirectoryPath));
+ R_TRY(this->base_fs->RenameDirectory(SynchronizingDirectoryPath, CommittedDirectoryPath));
+ return ResultSuccess();
+ }
+ } R_END_TRY_CATCH;
+
+ /* The committed directory exists, so synchronize it to the working directory. */
+ return this->SynchronizeDirectory(WorkingDirectoryPath, CommittedDirectoryPath);
+ }
+
+ Result DirectorySaveDataFileSystem::AllocateWorkBuffer(std::unique_ptr *out, size_t *out_size, size_t size) {
+ /* Repeatedly try to allocate until success. */
+ while (size > 0x200) {
+ /* Allocate the buffer. */
+ if (auto mem = new (std::nothrow) u8[size]; mem != nullptr) {
+ out->reset(mem);
+ *out_size = size;
+ return ResultSuccess();
+ } else {
+ /* Divide size by two. */
+ size >>= 1;
+ }
+ }
+
+ /* TODO: Return a result here? Nintendo does not, but they have other allocation failed results. */
+ /* Consider returning ResultFsAllocationFailureInDirectorySaveDataFileSystem? */
+ AMS_ASSERT(false);
+ }
+
+ Result DirectorySaveDataFileSystem::SynchronizeDirectory(const char *dst, const char *src) {
+ /* Delete destination dir and recreate it. */
+ R_TRY_CATCH(this->base_fs->DeleteDirectoryRecursively(dst)) {
+ R_CATCH(fs::ResultPathNotFound) { /* Nintendo returns error unconditionally, but I think that's a bug in their code. */}
+ } R_END_TRY_CATCH;
+
+ R_TRY(this->base_fs->CreateDirectory(dst));
+
+ /* Get a work buffer to work with. */
+ std::unique_ptr work_buf;
+ size_t work_buf_size;
+ R_TRY(this->AllocateWorkBuffer(&work_buf, &work_buf_size, IdealWorkBufferSize));
+
+ /* Copy the directory recursively. */
+ return fssystem::CopyDirectoryRecursively(this->base_fs, dst, src, work_buf.get(), work_buf_size);
+ }
+
+ Result DirectorySaveDataFileSystem::ResolveFullPath(char *out, size_t out_size, const char *relative_path) {
+ R_UNLESS(strnlen(relative_path, fs::EntryNameLengthMax + 1) < fs::EntryNameLengthMax + 1, fs::ResultTooLongPath());
+ R_UNLESS(PathTool::IsSeparator(relative_path[0]), fs::ResultInvalidPath());
+
+ /* Copy working directory path. */
+ std::strncpy(out, WorkingDirectoryPath, out_size);
+ out[out_size - 1] = StringTraits::NullTerminator;
+
+ /* Normalize it. */
+ constexpr size_t WorkingDirectoryPathLength = sizeof(WorkingDirectoryPath) - 1;
+ size_t normalized_length;
+ return PathTool::Normalize(out + WorkingDirectoryPathLength - 1, &normalized_length, relative_path, out_size - (WorkingDirectoryPathLength - 1));
+ }
+
+ void DirectorySaveDataFileSystem::OnWritableFileClose() {
+ std::scoped_lock lk(this->accessor_mutex);
+ this->open_writable_files--;
+
+ /* Nintendo does not check this, but I think it's sensible to do so. */
+ AMS_ASSERT(this->open_writable_files >= 0);
+ }
+
+ Result DirectorySaveDataFileSystem::CopySaveFromFileSystem(fs::fsa::IFileSystem *save_fs) {
+ /* If the input save is null, there's nothing to copy. */
+ R_UNLESS(save_fs != nullptr, ResultSuccess());
+
+ /* Get a work buffer to work with. */
+ std::unique_ptr work_buf;
+ size_t work_buf_size;
+ R_TRY(this->AllocateWorkBuffer(&work_buf, &work_buf_size, IdealWorkBufferSize));
+
+ /* Copy the directory recursively. */
+ R_TRY(fssystem::CopyDirectoryRecursively(this->base_fs, save_fs, PathTool::RootPath, PathTool::RootPath, work_buf.get(), work_buf_size));
+
+ return this->Commit();
+ }
+
+ /* Overridden from IPathResolutionFileSystem */
+ Result DirectorySaveDataFileSystem::OpenFileImpl(std::unique_ptr *out_file, const char *path, fs::OpenMode mode) {
+ char full_path[fs::EntryNameLengthMax + 1];
+ R_TRY(this->ResolveFullPath(full_path, sizeof(full_path), path));
+
+ std::scoped_lock lk(this->accessor_mutex);
+ std::unique_ptr base_file;
+ R_TRY(this->base_fs->OpenFile(&base_file, full_path, mode));
+
+ std::unique_ptr file(new (std::nothrow) DirectorySaveDataFile(std::move(base_file), this, mode));
+ R_UNLESS(file != nullptr, fs::ResultAllocationFailureInDirectorySaveDataFileSystem());
+
+ if (mode & fs::OpenMode_Write) {
+ this->open_writable_files++;
+ }
+
+ *out_file = std::move(file);
+ return ResultSuccess();
+ }
+
+ Result DirectorySaveDataFileSystem::CommitImpl() {
+ /* Here, Nintendo does the following (with retries): */
+ /* - Rename Committed -> Synchronizing. */
+ /* - Synchronize Working -> Synchronizing (deleting Synchronizing). */
+ /* - Rename Synchronizing -> Committed. */
+ std::scoped_lock lk(this->accessor_mutex);
+
+ R_UNLESS(this->open_writable_files == 0, fs::ResultPreconditionViolation());
+
+ const auto RenameCommitedDir = [&]() { return this->base_fs->RenameDirectory(CommittedDirectoryPath, SynchronizingDirectoryPath); };
+ const auto SynchronizeWorkingDir = [&]() { return this->SynchronizeDirectory(SynchronizingDirectoryPath, WorkingDirectoryPath); };
+ const auto RenameSynchronizingDir = [&]() { return this->base_fs->RenameDirectory(SynchronizingDirectoryPath, CommittedDirectoryPath); };
+
+ /* Rename Committed -> Synchronizing. */
+ R_TRY(fssystem::RetryFinitelyForTargetLocked(std::move(RenameCommitedDir)));
+
+ /* - Synchronize Working -> Synchronizing (deleting Synchronizing). */
+ R_TRY(fssystem::RetryFinitelyForTargetLocked(std::move(SynchronizeWorkingDir)));
+
+ /* - Rename Synchronizing -> Committed. */
+ R_TRY(fssystem::RetryFinitelyForTargetLocked(std::move(RenameSynchronizingDir)));
+
+ /* TODO: Should I call this->base_fs->Commit()? Nintendo does not. */
+ return ResultSuccess();
+ }
+
+ /* Overridden from IPathResolutionFileSystem but not commands. */
+ Result DirectorySaveDataFileSystem::CommitProvisionallyImpl(s64 counter) {
+ /* Nintendo does nothing here. */
+ return ResultSuccess();
+ }
+
+ Result DirectorySaveDataFileSystem::RollbackImpl() {
+ /* Initialize overwrites the working directory with the committed directory. */
+ return this->Initialize();
+ }
+
+ /* Explicitly overridden to be not implemented. */
+ Result DirectorySaveDataFileSystem::GetFreeSpaceSizeImpl(s64 *out, const char *path) {
+ return fs::ResultNotImplemented();
+ }
+
+ Result DirectorySaveDataFileSystem::GetTotalSpaceSizeImpl(s64 *out, const char *path) {
+ return fs::ResultNotImplemented();
+ }
+
+ Result DirectorySaveDataFileSystem::GetFileTimeStampRawImpl(fs::FileTimeStampRaw *out, const char *path) {
+ return fs::ResultNotImplemented();
+ }
+
+ Result DirectorySaveDataFileSystem::QueryEntryImpl(char *dst, size_t dst_size, const char *src, size_t src_size, fs::fsa::QueryId query, const char *path) {
+ return fs::ResultNotImplemented();
+ }
+
+ Result DirectorySaveDataFileSystem::FlushImpl() {
+ return fs::ResultNotImplemented();
+ }
+
+}
diff --git a/source/fssystem/fssystem_subdirectory_filesystem.cpp b/source/fssystem/fssystem_subdirectory_filesystem.cpp
index bcfafcac..f359b10f 100644
--- a/source/fssystem/fssystem_subdirectory_filesystem.cpp
+++ b/source/fssystem/fssystem_subdirectory_filesystem.cpp
@@ -17,12 +17,16 @@
namespace ams::fssystem {
- SubDirectoryFileSystem::SubDirectoryFileSystem(std::shared_ptr fs, const char *bp) : PathResolutionFileSystem(fs) {
+ SubDirectoryFileSystem::SubDirectoryFileSystem(std::shared_ptr fs, const char *bp, bool unc)
+ : PathResolutionFileSystem(fs, unc)
+ {
this->base_path = nullptr;
R_ASSERT(this->Initialize(bp));
}
- SubDirectoryFileSystem::SubDirectoryFileSystem(std::shared_ptr fs, const char *bp, bool unc) : PathResolutionFileSystem(fs, unc) {
+ SubDirectoryFileSystem::SubDirectoryFileSystem(std::unique_ptr &&fs, const char *bp, bool unc)
+ : PathResolutionFileSystem(std::forward>(fs), unc)
+ {
this->base_path = nullptr;
R_ASSERT(this->Initialize(bp));
}
diff --git a/source/fssystem/fssystem_utility.cpp b/source/fssystem/fssystem_utility.cpp
new file mode 100644
index 00000000..6b8254dd
--- /dev/null
+++ b/source/fssystem/fssystem_utility.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2018-2019 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 {
+
+ inline Result EnsureDirectoryExists(fs::fsa::IFileSystem *fs, const char *path) {
+ R_TRY_CATCH(fs->CreateDirectory(path)) {
+ R_CATCH(fs::ResultPathAlreadyExists) { /* If path already exists, there's no problem. */ }
+ } R_END_TRY_CATCH;
+
+ return ResultSuccess();
+ }
+
+ }
+
+ Result CopyFile(fs::fsa::IFileSystem *dst_fs, fs::fsa::IFileSystem *src_fs, const char *dst_parent_path, const char *src_path, const fs::DirectoryEntry *entry, void *work_buf, size_t work_buf_size) {
+ /* Open source file. */
+ std::unique_ptr src_file;
+ R_TRY(src_fs->OpenFile(&src_file, src_path, fs::OpenMode_Read));
+
+ /* Open dst file. */
+ std::unique_ptr dst_file;
+ {
+ char dst_path[fs::EntryNameLengthMax + 1];
+ const size_t original_size = static_cast(std::snprintf(dst_path, sizeof(dst_path), "%s%s", dst_parent_path, entry->name));
+ /* TODO: Error code? N aborts here. */
+ AMS_ASSERT(original_size < sizeof(dst_path));
+
+ R_TRY(dst_fs->CreateFile(dst_path, entry->file_size));
+ R_TRY(dst_fs->OpenFile(&dst_file, dst_path, fs::OpenMode_Write));
+ }
+
+ /* Read/Write file in work buffer sized chunks. */
+ s64 remaining = entry->file_size;
+ s64 offset = 0;
+ while (remaining > 0) {
+ size_t read_size;
+ R_TRY(src_file->Read(&read_size, offset, work_buf, work_buf_size, fs::ReadOption()));
+ R_TRY(dst_file->Write(offset, work_buf, read_size, fs::WriteOption()));
+
+ remaining -= read_size;
+ offset += read_size;
+ }
+
+ return ResultSuccess();
+ }
+
+ Result CopyDirectoryRecursively(fs::fsa::IFileSystem *dst_fs, fs::fsa::IFileSystem *src_fs, const char *dst_path, const char *src_path, void *work_buf, size_t work_buf_size) {
+ char dst_path_buf[fs::EntryNameLengthMax + 1];
+ const size_t original_size = static_cast(std::snprintf(dst_path_buf, sizeof(dst_path_buf), "%s", dst_path));
+ AMS_ASSERT(original_size < sizeof(dst_path_buf));
+
+ return IterateDirectoryRecursively(src_fs, src_path,
+ [&](const char *path, const fs::DirectoryEntry &entry) -> Result { /* On Enter Directory */
+ /* Update path, create new dir. */
+ std::strncat(dst_path_buf, entry.name, sizeof(dst_path_buf) - strnlen(dst_path_buf, sizeof(dst_path_buf) - 1) - 1);
+ std::strncat(dst_path_buf, "/", sizeof(dst_path_buf) - strnlen(dst_path_buf, sizeof(dst_path_buf) - 1) - 1);
+ return dst_fs->CreateDirectory(dst_path_buf);
+ },
+ [&](const char *path, const fs::DirectoryEntry &entry) -> Result { /* On Exit Directory */
+ /* Check we have a parent directory. */
+ const size_t len = strnlen(dst_path_buf, sizeof(dst_path_buf));
+ R_UNLESS(len >= 2, fs::ResultInvalidPathFormat());
+
+ /* Find previous separator, add null terminator */
+ char *cur = &dst_path_buf[len - 2];
+ while (!PathTool::IsSeparator(*cur) && cur > dst_path_buf) {
+ cur--;
+ }
+ cur[1] = StringTraits::NullTerminator;
+
+ return ResultSuccess();
+ },
+ [&](const char *path, const fs::DirectoryEntry &entry) -> Result { /* On File */
+ return CopyFile(dst_fs, src_fs, dst_path_buf, path, &entry, work_buf, work_buf_size);
+ }
+ );
+ }
+
+ Result EnsureDirectoryExistsRecursively(fs::fsa::IFileSystem *fs, const char *path) {
+ /* Normalize the path. */
+ char normalized_path[fs::EntryNameLengthMax + 1];
+ size_t normalized_path_len;
+ R_TRY(PathTool::Normalize(normalized_path, &normalized_path_len, path, sizeof(normalized_path)));
+
+ /* Repeatedly call CreateDirectory on each directory leading to the target. */
+ for (size_t i = 1; i < normalized_path_len; i++) {
+ /* If we detect a separator, create the directory. */
+ if (PathTool::IsSeparator(normalized_path[i])) {
+ normalized_path[i] = StringTraits::NullTerminator;
+ R_TRY(EnsureDirectoryExists(fs, normalized_path));
+ normalized_path[i] = StringTraits::DirectorySeparator;
+ }
+ }
+
+ /* Call CreateDirectory on the final path. */
+ R_TRY(EnsureDirectoryExists(fs, normalized_path));
+
+ return ResultSuccess();
+ }
+
+}
diff --git a/source/sf/cmif/sf_cmif_domain_service_object.cpp b/source/sf/cmif/sf_cmif_domain_service_object.cpp
index 05705538..15325a3a 100644
--- a/source/sf/cmif/sf_cmif_domain_service_object.cpp
+++ b/source/sf/cmif/sf_cmif_domain_service_object.cpp
@@ -149,8 +149,8 @@ namespace ams::sf::cmif {
/* Write out header. */
constexpr size_t out_header_size = sizeof(CmifDomainOutHeader);
- const size_t impl_out_data_total_size = this->GetImplOutDataTotalSize();
- AMS_ASSERT(out_header_size + impl_out_data_total_size <= raw_data.GetSize());
+ const size_t impl_out_headers_size = this->GetImplOutHeadersSize();
+ AMS_ASSERT(out_header_size + impl_out_headers_size <= raw_data.GetSize());
*reinterpret_cast(raw_data.GetPointer()) = CmifDomainOutHeader{ .num_out_objects = 0, };
/* Set output raw data. */