mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-09 22:56:35 +00:00
fs.mitm: Implement DirectorySaveDataFileSystem.
This commit is contained in:
parent
994d7d5429
commit
d00b183f92
11 changed files with 877 additions and 85 deletions
97
stratosphere/ams_mitm/source/fs_mitm/fs_dir_utils.cpp
Normal file
97
stratosphere/ams_mitm/source/fs_mitm/fs_dir_utils.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <switch.h>
|
||||
#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<IFile> src_file;
|
||||
std::unique_ptr<IFile> 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<size_t>(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);
|
||||
});
|
||||
}
|
155
stratosphere/ams_mitm/source/fs_mitm/fs_dir_utils.hpp
Normal file
155
stratosphere/ams_mitm/source/fs_mitm/fs_dir_utils.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#include "fs_path_utils.hpp"
|
||||
#include "fs_ifilesystem.hpp"
|
||||
|
||||
class FsDirUtils {
|
||||
private:
|
||||
template<typename OnEnterDir, typename OnExitDir, typename OnFile>
|
||||
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<IDirectory> 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<typename OnEnterDir, typename OnExitDir, typename OnFile>
|
||||
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<typename OnEnterDir, typename OnExitDir, typename OnFile>
|
||||
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<typename OnEnterDir, typename OnExitDir, typename OnFile>
|
||||
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<typename F>
|
||||
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;
|
||||
}
|
||||
};
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "../utils.hpp"
|
||||
#include "fs_directory_savedata_filesystem.hpp"
|
||||
#include "fs_dir_utils.hpp"
|
||||
|
||||
class DirectorySaveDataFile : public IFile {
|
||||
private:
|
||||
std::unique_ptr<IFile> base_file;
|
||||
DirectorySaveDataFileSystem *parent_fs; /* TODO: shared_ptr + enabled_shared_from_this? */
|
||||
int open_mode;
|
||||
public:
|
||||
DirectorySaveDataFile(std::unique_ptr<IFile> 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<HosMutex> 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<HosMutex> 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<HosMutex> 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<HosMutex> 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<HosMutex> 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<HosMutex> 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<HosMutex> 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<HosMutex> 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<HosMutex> lk(this->lock);
|
||||
return this->fs->GetEntryType(out, full_path);
|
||||
}
|
||||
|
||||
Result DirectorySaveDataFileSystem::OpenFileImpl(std::unique_ptr<IFile> &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<HosMutex> lk(this->lock);
|
||||
|
||||
{
|
||||
/* Open the raw file. */
|
||||
std::unique_ptr<IFile> file;
|
||||
if (R_FAILED((rc = this->fs->OpenFile(file, full_path, mode)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Create DirectorySaveDataFile wrapper. */
|
||||
out_file = std::make_unique<DirectorySaveDataFile>(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<IDirectory> &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<HosMutex> 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<HosMutex> 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<HosMutex> 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;
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#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<IFileSystem> shared_fs;
|
||||
std::unique_ptr<IFileSystem> 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<IFileSystem> 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<IFileSystem> 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<IFile> &out_file, const FsPath &path, OpenMode mode) override;
|
||||
virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> &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;
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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<IFile> &out_file, FsPath &path, OpenMode mode) {
|
||||
Result OpenFile(std::unique_ptr<IFile> &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<IDirectory> &out_dir, FsPath &path, DirectoryOpenMode mode) {
|
||||
Result OpenDirectory(std::unique_ptr<IDirectory> &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<IFile> &out_file, FsPath &path, OpenMode mode) = 0;
|
||||
virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> &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<IFile> &out_file, const FsPath &path, OpenMode mode) = 0;
|
||||
virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> &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<IFile> &out_file, FsPath &path, OpenMode mode) {
|
||||
virtual Result OpenFileImpl(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) {
|
||||
FsFile f;
|
||||
|
||||
Result rc = fsFsOpenFile(this->base_fs.get(), path.str, static_cast<int>(mode), &f);
|
||||
|
@ -498,7 +502,7 @@ class ProxyFileSystem : public IFileSystem {
|
|||
return rc;
|
||||
}
|
||||
|
||||
virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, FsPath &path, DirectoryOpenMode mode) {
|
||||
virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) {
|
||||
FsDir d;
|
||||
|
||||
Result rc = fsFsOpenDirectory(this->base_fs.get(), path.str, static_cast<int>(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<FsFileSystemQueryType>(query));
|
||||
}
|
||||
};
|
|
@ -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);
|
||||
|
|
|
@ -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 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);
|
||||
|
|
|
@ -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<IFile> &out_file, FsPath &path, OpenMode mode) {
|
||||
Result SubDirectoryFileSystem::OpenFileImpl(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) {
|
||||
Result rc;
|
||||
FsPath full_path;
|
||||
|
||||
|
@ -178,7 +178,7 @@ Result SubDirectoryFileSystem::OpenFileImpl(std::unique_ptr<IFile> &out_file, Fs
|
|||
return this->base_fs->OpenFile(out_file, full_path, mode);
|
||||
}
|
||||
|
||||
Result SubDirectoryFileSystem::OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, FsPath &path, DirectoryOpenMode mode) {
|
||||
Result SubDirectoryFileSystem::OpenDirectoryImpl(std::unique_ptr<IDirectory> &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;
|
||||
|
||||
|
|
|
@ -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<IFile> &out_file, FsPath &path, OpenMode mode) override;
|
||||
virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> &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<IFile> &out_file, const FsPath &path, OpenMode mode) override;
|
||||
virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> &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;
|
||||
};
|
|
@ -29,6 +29,7 @@
|
|||
#include "fsmitm_layeredrom.hpp"
|
||||
|
||||
#include "fs_subdirectory_filesystem.hpp"
|
||||
#include "fs_directory_savedata_filesystem.hpp"
|
||||
|
||||
#include "../debug.hpp"
|
||||
|
||||
|
|
Loading…
Reference in a new issue