fs.mitm: Implement DirectorySaveDataFileSystem.

This commit is contained in:
Michael Scire 2019-03-28 10:43:33 -07:00
parent 994d7d5429
commit d00b183f92
11 changed files with 877 additions and 85 deletions

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

View 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;
}
};

View file

@ -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;
}

View file

@ -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;
};

View file

@ -49,6 +49,10 @@ class IFile {
return ReadImpl(out, offset, buffer, size); 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) { Result GetSize(uint64_t *out) {
if (out == nullptr) { if (out == nullptr) {
return ResultFsNullptrArgument; return ResultFsNullptrArgument;
@ -71,8 +75,14 @@ class IFile {
return WriteImpl(offset, buffer, size, flush); return WriteImpl(offset, buffer, size, flush);
} }
Result Write(uint64_t offset, void *buffer, uint64_t size) { Result Write(uint64_t offset, void *buffer, uint64_t size, bool flush = false) {
return WriteImpl(offset, buffer, size, false); if (size == 0) {
return 0;
}
if (buffer == nullptr) {
return ResultFsNullptrArgument;
}
return WriteImpl(offset, buffer, size, flush);
} }
Result SetSize(uint64_t size) { Result SetSize(uint64_t size) {
@ -162,7 +172,7 @@ class ProxyFile : public IFile {
fsFileClose(this->base_file.get()); fsFileClose(this->base_file.get());
} }
public: 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; size_t out_sz;
Result rc = fsFileRead(this->base_file.get(), offset, buffer, size, &out_sz); Result rc = fsFileRead(this->base_file.get(), offset, buffer, size, &out_sz);
@ -172,13 +182,13 @@ class ProxyFile : public IFile {
return rc; return rc;
} }
virtual Result GetSizeImpl(u64 *out) { virtual Result GetSizeImpl(u64 *out) override {
return fsFileGetSize(this->base_file.get(), out); return fsFileGetSize(this->base_file.get(), out);
} }
virtual Result FlushImpl() { virtual Result FlushImpl() override {
return fsFileFlush(this->base_file.get()); 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); Result rc = fsFileWrite(this->base_file.get(), offset, buffer, size);
if (R_SUCCEEDED(rc)) { if (R_SUCCEEDED(rc)) {
/* libnx doesn't allow passing the flush flag. */ /* libnx doesn't allow passing the flush flag. */
@ -186,10 +196,10 @@ class ProxyFile : public IFile {
} }
return rc; return rc;
} }
virtual Result SetSizeImpl(u64 size) { virtual Result SetSizeImpl(u64 size) override {
return fsFileSetSize(this->base_file.get(), size); 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); return fsFileOperateRange(this->base_file.get(), operation_type, offset, size, out_range_info);
} }
}; };

View file

@ -58,42 +58,46 @@ class IFileSystem {
public: public:
virtual ~IFileSystem() {} 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); 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); return DeleteFileImpl(path);
} }
Result CreateDirectory(FsPath &path) { Result CreateDirectory(const FsPath &path) {
return CreateDirectoryImpl(path); return CreateDirectoryImpl(path);
} }
Result DeleteDirectory(FsPath &path) { Result DeleteDirectory(const FsPath &path) {
return DeleteDirectoryImpl(path); return DeleteDirectoryImpl(path);
} }
Result DeleteDirectoryRecursively(FsPath &path) { Result DeleteDirectoryRecursively(const FsPath &path) {
return DeleteDirectoryRecursivelyImpl(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); 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); return RenameDirectoryImpl(old_path, new_path);
} }
Result GetEntryType(DirectoryEntryType *out, FsPath &path) { Result GetEntryType(DirectoryEntryType *out, const FsPath &path) {
if (out == nullptr) { if (out == nullptr) {
return ResultFsNullptrArgument; return ResultFsNullptrArgument;
} }
return GetEntryTypeImpl(out, path); 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)) { if (!(mode & OpenMode_ReadWrite)) {
return ResultFsInvalidArgument; return ResultFsInvalidArgument;
} }
@ -103,7 +107,7 @@ class IFileSystem {
return OpenFileImpl(out_file, path, mode); 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)) { if (!(mode & DirectoryOpenMode_All)) {
return ResultFsInvalidArgument; return ResultFsInvalidArgument;
} }
@ -117,32 +121,32 @@ class IFileSystem {
return CommitImpl(); return CommitImpl();
} }
Result GetFreeSpaceSize(uint64_t *out, FsPath &path) { Result GetFreeSpaceSize(uint64_t *out, const FsPath &path) {
if (out == nullptr) { if (out == nullptr) {
return ResultFsNullptrArgument; return ResultFsNullptrArgument;
} }
return GetFreeSpaceSizeImpl(out, path); return GetFreeSpaceSizeImpl(out, path);
} }
Result GetTotalSpaceSize(uint64_t *out, FsPath &path) { Result GetTotalSpaceSize(uint64_t *out, const FsPath &path) {
if (out == nullptr) { if (out == nullptr) {
return ResultFsNullptrArgument; return ResultFsNullptrArgument;
} }
return GetTotalSpaceSizeImpl(out, path); return GetTotalSpaceSizeImpl(out, path);
} }
Result CleanDirectoryRecursively(FsPath &path) { Result CleanDirectoryRecursively(const FsPath &path) {
return CleanDirectoryRecursivelyImpl(path); return CleanDirectoryRecursivelyImpl(path);
} }
Result GetFileTimeStampRaw(FsTimeStampRaw *out, FsPath &path) { Result GetFileTimeStampRaw(FsTimeStampRaw *out, const FsPath &path) {
if (out == nullptr) { if (out == nullptr) {
return ResultFsNullptrArgument; return ResultFsNullptrArgument;
} }
return GetFileTimeStampRawImpl(out, path); 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); return QueryEntryImpl(out, out_size, in, in_size, query, path);
} }
@ -150,39 +154,39 @@ class IFileSystem {
protected: protected:
/* ...? */ /* ...? */
private: private:
virtual Result CreateFileImpl(FsPath &path, uint64_t size, int flags) = 0; virtual Result CreateFileImpl(const FsPath &path, uint64_t size, int flags) = 0;
virtual Result DeleteFileImpl(FsPath &path) = 0; virtual Result DeleteFileImpl(const FsPath &path) = 0;
virtual Result CreateDirectoryImpl(FsPath &path) = 0; virtual Result CreateDirectoryImpl(const FsPath &path) = 0;
virtual Result DeleteDirectoryImpl(FsPath &path) = 0; virtual Result DeleteDirectoryImpl(const FsPath &path) = 0;
virtual Result DeleteDirectoryRecursivelyImpl(FsPath &path) = 0; virtual Result DeleteDirectoryRecursivelyImpl(const FsPath &path) = 0;
virtual Result RenameFileImpl(FsPath &old_path, FsPath &new_path) = 0; virtual Result RenameFileImpl(const FsPath &old_path, const FsPath &new_path) = 0;
virtual Result RenameDirectoryImpl(FsPath &old_path, FsPath &new_path) = 0; virtual Result RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) = 0;
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, FsPath &path) = 0; virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) = 0;
virtual Result OpenFileImpl(std::unique_ptr<IFile> &out_file, FsPath &path, OpenMode mode) = 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, FsPath &path, DirectoryOpenMode mode) = 0; virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) = 0;
virtual Result CommitImpl() = 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)(out);
(void)(path); (void)(path);
return ResultFsNotImplemented; return ResultFsNotImplemented;
} }
virtual Result GetTotalSpaceSizeImpl(uint64_t *out, FsPath &path) { virtual Result GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) {
(void)(out); (void)(out);
(void)(path); (void)(path);
return ResultFsNotImplemented; 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)(out);
(void)(path); (void)(path);
return ResultFsNotImplemented; 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);
(void)(out_size); (void)(out_size);
(void)(in); (void)(in);
@ -449,35 +453,35 @@ class ProxyFileSystem : public IFileSystem {
} }
public: 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); 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); 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); 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); 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); 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); 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); 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; FsEntryType type;
Result rc = fsFsGetEntryType(this->base_fs.get(), path.str, &type); Result rc = fsFsGetEntryType(this->base_fs.get(), path.str, &type);
@ -487,7 +491,7 @@ class ProxyFileSystem : public IFileSystem {
return rc; 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; FsFile f;
Result rc = fsFsOpenFile(this->base_fs.get(), path.str, static_cast<int>(mode), &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; 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; FsDir d;
Result rc = fsFsOpenDirectory(this->base_fs.get(), path.str, static_cast<int>(mode), &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()); 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); 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); 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); 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); 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)); return fsFsQueryEntry(this->base_fs.get(), out, out_size, in, in_size, path.str,static_cast<FsFileSystemQueryType>(query));
} }
}; };

View file

@ -21,7 +21,31 @@ struct FsPath {
char str[FS_MAX_PATH]; 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 { class FsPathUtils {
public:
static constexpr FsPath RootPath = EncodeConstantFsPath("/");
public: public:
static Result VerifyPath(const char *path, size_t max_path_len, size_t max_name_len); static Result VerifyPath(const char *path, size_t max_path_len, size_t max_name_len);
static Result ConvertPathForServiceObject(FsPath *out, const char *path); static Result ConvertPathForServiceObject(FsPath *out, const char *path);

View file

@ -19,11 +19,19 @@
static constexpr u32 Module_Fs = 2; 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 ResultFsNotImplemented = MAKERESULT(Module_Fs, 3001);
static constexpr Result ResultFsOutOfRange = MAKERESULT(Module_Fs, 3005); static constexpr Result ResultFsOutOfRange = MAKERESULT(Module_Fs, 3005);
static constexpr Result ResultFsAllocationFailureInSubDirectoryFileSystem = MAKERESULT(Module_Fs, 3355); static constexpr Result ResultFsAllocationFailureInDirectorySaveDataFileSystem = MAKERESULT(Module_Fs, 3321);
static constexpr Result ResultFsAllocationFailureInSubDirectoryFileSystem = MAKERESULT(Module_Fs, 3355);
static constexpr Result ResultFsPreconditionViolation = MAKERESULT(Module_Fs, 6000);
static constexpr Result ResultFsInvalidArgument = MAKERESULT(Module_Fs, 6001); static constexpr Result ResultFsInvalidArgument = MAKERESULT(Module_Fs, 6001);
static constexpr Result ResultFsInvalidPath = MAKERESULT(Module_Fs, 6002); static constexpr Result ResultFsInvalidPath = MAKERESULT(Module_Fs, 6002);
static constexpr Result ResultFsTooLongPath = MAKERESULT(Module_Fs, 6003); static constexpr Result ResultFsTooLongPath = MAKERESULT(Module_Fs, 6003);

View file

@ -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); 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; Result rc;
FsPath full_path; 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); return this->base_fs->CreateFile(full_path, size, flags);
} }
Result SubDirectoryFileSystem::DeleteFileImpl(FsPath &path) { Result SubDirectoryFileSystem::DeleteFileImpl(const FsPath &path) {
Result rc; Result rc;
FsPath full_path; FsPath full_path;
@ -93,7 +93,7 @@ Result SubDirectoryFileSystem::DeleteFileImpl(FsPath &path) {
return this->base_fs->DeleteFile(full_path); return this->base_fs->DeleteFile(full_path);
} }
Result SubDirectoryFileSystem::CreateDirectoryImpl(FsPath &path) { Result SubDirectoryFileSystem::CreateDirectoryImpl(const FsPath &path) {
Result rc; Result rc;
FsPath full_path; FsPath full_path;
@ -104,7 +104,7 @@ Result SubDirectoryFileSystem::CreateDirectoryImpl(FsPath &path) {
return this->base_fs->CreateDirectory(full_path); return this->base_fs->CreateDirectory(full_path);
} }
Result SubDirectoryFileSystem::DeleteDirectoryImpl(FsPath &path) { Result SubDirectoryFileSystem::DeleteDirectoryImpl(const FsPath &path) {
Result rc; Result rc;
FsPath full_path; FsPath full_path;
@ -115,7 +115,7 @@ Result SubDirectoryFileSystem::DeleteDirectoryImpl(FsPath &path) {
return this->base_fs->DeleteDirectory(full_path); return this->base_fs->DeleteDirectory(full_path);
} }
Result SubDirectoryFileSystem::DeleteDirectoryRecursivelyImpl(FsPath &path) { Result SubDirectoryFileSystem::DeleteDirectoryRecursivelyImpl(const FsPath &path) {
Result rc; Result rc;
FsPath full_path; FsPath full_path;
@ -126,7 +126,7 @@ Result SubDirectoryFileSystem::DeleteDirectoryRecursivelyImpl(FsPath &path) {
return this->base_fs->DeleteDirectoryRecursively(full_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; Result rc;
FsPath full_old_path, full_new_path; 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); 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; Result rc;
FsPath full_old_path, full_new_path; 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); 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; Result rc;
FsPath full_path; FsPath full_path;
@ -167,7 +167,7 @@ Result SubDirectoryFileSystem::GetEntryTypeImpl(DirectoryEntryType *out, FsPath
return this->base_fs->GetEntryType(out, full_path); 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; Result rc;
FsPath full_path; 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); 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; Result rc;
FsPath full_path; FsPath full_path;
@ -193,7 +193,7 @@ Result SubDirectoryFileSystem::CommitImpl() {
return this->base_fs->Commit(); return this->base_fs->Commit();
} }
Result SubDirectoryFileSystem::GetFreeSpaceSizeImpl(uint64_t *out, FsPath &path) { Result SubDirectoryFileSystem::GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) {
Result rc; Result rc;
FsPath full_path; FsPath full_path;
@ -204,7 +204,7 @@ Result SubDirectoryFileSystem::GetFreeSpaceSizeImpl(uint64_t *out, FsPath &path)
return this->base_fs->GetFreeSpaceSize(out, full_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; Result rc;
FsPath full_path; FsPath full_path;
@ -215,7 +215,7 @@ Result SubDirectoryFileSystem::GetTotalSpaceSizeImpl(uint64_t *out, FsPath &path
return this->base_fs->GetTotalSpaceSize(out, full_path); return this->base_fs->GetTotalSpaceSize(out, full_path);
} }
Result SubDirectoryFileSystem::CleanDirectoryRecursivelyImpl(FsPath &path) { Result SubDirectoryFileSystem::CleanDirectoryRecursivelyImpl(const FsPath &path) {
Result rc; Result rc;
FsPath full_path; FsPath full_path;
@ -226,7 +226,7 @@ Result SubDirectoryFileSystem::CleanDirectoryRecursivelyImpl(FsPath &path) {
return this->base_fs->CleanDirectoryRecursively(full_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; Result rc;
FsPath full_path; FsPath full_path;
@ -237,7 +237,7 @@ Result SubDirectoryFileSystem::GetFileTimeStampRawImpl(FsTimeStampRaw *out, FsPa
return this->base_fs->GetFileTimeStampRaw(out, full_path); 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; Result rc;
FsPath full_path; FsPath full_path;

View file

@ -53,25 +53,25 @@ class SubDirectoryFileSystem : public IFileSystem {
Result Initialize(const char *bp); Result Initialize(const char *bp);
protected: protected:
Result GetFullPath(char *out, size_t out_size, const char *relative_path); 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); return GetFullPath(full_path.str, sizeof(full_path.str), relative_path.str);
} }
public: public:
virtual Result CreateFileImpl(FsPath &path, uint64_t size, int flags) override; virtual Result CreateFileImpl(const FsPath &path, uint64_t size, int flags) override;
virtual Result DeleteFileImpl(FsPath &path) override; virtual Result DeleteFileImpl(const FsPath &path) override;
virtual Result CreateDirectoryImpl(FsPath &path) override; virtual Result CreateDirectoryImpl(const FsPath &path) override;
virtual Result DeleteDirectoryImpl(FsPath &path) override; virtual Result DeleteDirectoryImpl(const FsPath &path) override;
virtual Result DeleteDirectoryRecursivelyImpl(FsPath &path) override; virtual Result DeleteDirectoryRecursivelyImpl(const FsPath &path) override;
virtual Result RenameFileImpl(FsPath &old_path, FsPath &new_path) override; virtual Result RenameFileImpl(const FsPath &old_path, const FsPath &new_path) override;
virtual Result RenameDirectoryImpl(FsPath &old_path, FsPath &new_path) override; virtual Result RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) override;
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, FsPath &path) override; virtual Result GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) override;
virtual Result OpenFileImpl(std::unique_ptr<IFile> &out_file, FsPath &path, OpenMode mode) 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, FsPath &path, DirectoryOpenMode mode) override; virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) override;
virtual Result CommitImpl() override; virtual Result CommitImpl() override;
virtual Result GetFreeSpaceSizeImpl(uint64_t *out, FsPath &path) override; virtual Result GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) override;
virtual Result GetTotalSpaceSizeImpl(uint64_t *out, FsPath &path) override; virtual Result GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) override;
virtual Result CleanDirectoryRecursivelyImpl(FsPath &path) override; virtual Result CleanDirectoryRecursivelyImpl(const FsPath &path) override;
virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, 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, 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;
}; };

View file

@ -29,6 +29,7 @@
#include "fsmitm_layeredrom.hpp" #include "fsmitm_layeredrom.hpp"
#include "fs_subdirectory_filesystem.hpp" #include "fs_subdirectory_filesystem.hpp"
#include "fs_directory_savedata_filesystem.hpp"
#include "../debug.hpp" #include "../debug.hpp"