ams_mitm: begin skeleton refactor

This commit is contained in:
Michael Scire
2019-11-20 23:58:18 -08:00
committed by SciresM
parent 02d4c97c6d
commit 393596ef9a
105 changed files with 1541 additions and 352 deletions

View File

@@ -1,117 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstring>
#include <cstdlib>
#include <switch.h>
#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) {
std::unique_ptr<IFile> src_file;
std::unique_ptr<IFile> dst_file;
const u64 file_size = dir_ent->fileSize;
/* Open source file for reading. */
R_TRY(src_fs->OpenFile(src_file, src_path, OpenMode_Read));
/* Create and open destination file. */
{
FsPath dst_path;
/* TODO: Error code? N aborts here. */
AMS_ASSERT(static_cast<size_t>(snprintf(dst_path.str, sizeof(dst_path.str), "%s%s", dst_parent_path.str, dir_ent->name)) < sizeof(dst_path));
R_TRY(dst_fs->CreateFile(dst_path, file_size));
R_TRY(dst_fs->OpenFile(dst_file, dst_path, OpenMode_Write));
}
/* Read/Write work_buf_size chunks. */
u64 offset = 0;
while (offset < file_size) {
u64 read_size;
R_TRY(src_file->Read(&read_size, offset, work_buf, work_buf_size));
R_TRY(dst_file->Write(offset, work_buf, read_size));
offset += read_size;
}
return ResultSuccess();
}
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 ResultSuccess();
},
[&](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);
});
}
Result FsDirUtils::EnsureDirectoryExists(IFileSystem *fs, const FsPath &path) {
FsPath normal_path;
size_t normal_path_len;
/* Normalize the path. */
R_TRY(FsPathUtils::Normalize(normal_path.str, sizeof(normal_path.str) - 1, path.str, &normal_path_len));
/* Repeatedly call CreateDirectory on each directory leading to the target. */
for (size_t i = 1; i < normal_path_len; i++) {
/* If we detect a separator, we're done. */
if (normal_path.str[i] == '/') {
normal_path.str[i] = 0;
{
R_TRY_CATCH(fs->CreateDirectory(normal_path)) {
R_CATCH(ResultFsPathAlreadyExists) {
/* If path already exists, there's no problem. */
}
} R_END_TRY_CATCH;
}
normal_path.str[i] = '/';
}
}
/* Call CreateDirectory on the final path. */
R_TRY_CATCH(fs->CreateDirectory(normal_path)) {
R_CATCH(ResultFsPathAlreadyExists) {
/* If path already exists, there's no problem. */
}
} R_END_TRY_CATCH;
return ResultSuccess();
}

View File

@@ -1,142 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <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) {
std::unique_ptr<IDirectory> dir;
/* Open the directory. */
R_TRY(fs->OpenDirectory(dir, work_path, DirectoryOpenMode_All));
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;
R_TRY(dir->Read(&read_count, ent_buf, 1));
/* 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. */
R_TRY(on_enter_dir(work_path, ent_buf));
/* Append separator, recurse. */
strncat(work_path.str, "/", sizeof(work_path.str) - 1 - parent_len - child_name_len);
R_TRY(IterateDirectoryRecursivelyInternal(fs, work_path, ent_buf, on_enter_dir, on_exit_dir, on_file));
/* Exit directory. */
R_TRY(on_exit_dir(work_path, ent_buf));
} else {
/* Call file handler. */
R_TRY(on_file(work_path, ent_buf));
}
/* Restore parent path. */
work_path.str[parent_len] = 0;
}
return ResultSuccess();
}
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);
}
/* Ensure directory existence. */
static Result EnsureDirectoryExists(IFileSystem *fs, const FsPath &path);
/* Other Utility. */
template<typename F>
static Result RetryUntilTargetNotLocked(F f) {
const size_t MaxRetries = 10;
for (size_t i = 0; i < MaxRetries; i++) {
R_TRY_CATCH(f()) {
R_CATCH(ResultFsTargetLocked) {
/* If target is locked, wait 100ms and try again. */
svcSleepThread(100'000'000ul);
}
} R_END_TRY_CATCH;
}
return ResultSuccess();
}
};

View File

@@ -1,182 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstring>
#include <switch.h>
#include <stratosphere.hpp>
#include "../utils.hpp"
#include "fs_directory_redirection_filesystem.hpp"
#include "fs_path_utils.hpp"
static char *GetNormalizedDirectory(const char *dir_prefix) {
/* Normalize the path. */
char normal_path[FS_MAX_PATH + 1];
size_t normal_path_len;
R_ASSERT(FsPathUtils::Normalize(normal_path, sizeof(normal_path), dir_prefix, &normal_path_len));
/* Ensure terminating '/' */
if (normal_path[normal_path_len-1] != '/') {
AMS_ASSERT(normal_path_len + 2 <= sizeof(normal_path));
strncat(normal_path, "/", 2);
normal_path[sizeof(normal_path)-1] = 0;
normal_path_len++;
}
char *output = static_cast<char *>(std::malloc(normal_path_len + 1));
AMS_ASSERT(output != nullptr);
std::strncpy(output, normal_path, normal_path_len + 1);
output[normal_path_len] = 0;
return output;
}
Result DirectoryRedirectionFileSystem::Initialize(const char *before, const char *after) {
if (strnlen(before, FS_MAX_PATH) >= FS_MAX_PATH || strnlen(after, FS_MAX_PATH) >= FS_MAX_PATH) {
return ResultFsTooLongPath;
}
this->before_dir = GetNormalizedDirectory(before);
this->after_dir = GetNormalizedDirectory(after);
this->before_dir_len = strlen(this->before_dir);
this->after_dir_len = strlen(this->after_dir);
return ResultSuccess();
}
Result DirectoryRedirectionFileSystem::GetFullPath(char *out, size_t out_size, const char *relative_path) {
FsPath tmp_rel_path;
R_TRY(FsPathUtils::Normalize(tmp_rel_path.str, sizeof(tmp_rel_path), relative_path, nullptr));
if (std::strncmp(tmp_rel_path.str, this->before_dir, this->before_dir_len) == 0) {
if (this->after_dir_len + strnlen(tmp_rel_path.str, FS_MAX_PATH) - this->before_dir_len > out_size) {
return ResultFsTooLongPath;
}
/* Copy after path. */
std::strncpy(out, this->after_dir, out_size);
out[out_size - 1] = 0;
/* Normalize it. */
return FsPathUtils::Normalize(out + this->after_dir_len - 1, out_size - (this->after_dir_len - 1), relative_path + this->before_dir_len - 1, nullptr);
} else if (std::memcmp(tmp_rel_path.str, this->before_dir, this->before_dir_len - 1) == 0) {
/* Handling raw directory. */
if (this->after_dir_len + 1 > out_size) {
return ResultFsTooLongPath;
}
std::strncpy(out, this->after_dir, out_size);
out[out_size - 1] = 0;
return ResultSuccess();
} else {
return FsPathUtils::Normalize(out, out_size, relative_path, nullptr);
}
}
Result DirectoryRedirectionFileSystem::CreateFileImpl(const FsPath &path, uint64_t size, int flags) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->CreateFile(full_path, size, flags);
}
Result DirectoryRedirectionFileSystem::DeleteFileImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->DeleteFile(full_path);
}
Result DirectoryRedirectionFileSystem::CreateDirectoryImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->CreateDirectory(full_path);
}
Result DirectoryRedirectionFileSystem::DeleteDirectoryImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->DeleteDirectory(full_path);
}
Result DirectoryRedirectionFileSystem::DeleteDirectoryRecursivelyImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->DeleteDirectoryRecursively(full_path);
}
Result DirectoryRedirectionFileSystem::RenameFileImpl(const FsPath &old_path, const FsPath &new_path) {
FsPath full_old_path, full_new_path;
R_TRY(GetFullPath(full_old_path, old_path));
R_TRY(GetFullPath(full_new_path, new_path));
return this->base_fs->RenameFile(full_old_path, full_new_path);
}
Result DirectoryRedirectionFileSystem::RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) {
FsPath full_old_path, full_new_path;
R_TRY(GetFullPath(full_old_path, old_path));
R_TRY(GetFullPath(full_new_path, new_path));
return this->base_fs->RenameDirectory(full_old_path, full_new_path);
}
Result DirectoryRedirectionFileSystem::GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->GetEntryType(out, full_path);
}
Result DirectoryRedirectionFileSystem::OpenFileImpl(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->OpenFile(out_file, full_path, mode);
}
Result DirectoryRedirectionFileSystem::OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->OpenDirectory(out_dir, full_path, mode);
}
Result DirectoryRedirectionFileSystem::CommitImpl() {
return this->base_fs->Commit();
}
Result DirectoryRedirectionFileSystem::GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->GetFreeSpaceSize(out, full_path);
}
Result DirectoryRedirectionFileSystem::GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->GetTotalSpaceSize(out, full_path);
}
Result DirectoryRedirectionFileSystem::CleanDirectoryRecursivelyImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->CleanDirectoryRecursively(full_path);
}
Result DirectoryRedirectionFileSystem::GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->GetFileTimeStampRaw(out, full_path);
}
Result DirectoryRedirectionFileSystem::QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->QueryEntry(out, out_size, in, in_size, query, full_path);
}

View File

@@ -1,76 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "fs_ifilesystem.hpp"
#include "fs_path_utils.hpp"
class DirectoryRedirectionFileSystem : public IFileSystem {
private:
std::shared_ptr<IFileSystem> base_fs;
char *before_dir = nullptr;
size_t before_dir_len = 0;
char *after_dir = nullptr;
size_t after_dir_len = 0;
public:
DirectoryRedirectionFileSystem(IFileSystem *fs, const char *before, const char *after) : base_fs(fs) {
R_ASSERT(this->Initialize(before, after));
}
DirectoryRedirectionFileSystem(std::shared_ptr<IFileSystem> fs, const char *before, const char *after) : base_fs(fs) {
R_ASSERT(this->Initialize(before, after));
}
virtual ~DirectoryRedirectionFileSystem() {
if (this->before_dir != nullptr) {
free(this->before_dir);
}
if (this->after_dir != nullptr) {
free(this->after_dir);
}
}
private:
Result Initialize(const char *before, const char *after);
protected:
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:
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

@@ -1,335 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <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, u32 option) override {
return this->base_file->Write(offset, buffer, size, option);
}
virtual Result SetSizeImpl(u64 size) override {
return this->base_file->SetSize(size);
}
virtual Result OperateRangeImpl(FsOperationId 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() {
DirectoryEntryType ent_type;
/* Check that the working directory exists. */
R_TRY_CATCH(this->fs->GetEntryType(&ent_type, WorkingDirectoryPath)) {
/* If path isn't found, create working directory and committed directory. */
R_CATCH(ResultFsPathNotFound) {
R_TRY(this->fs->CreateDirectory(WorkingDirectoryPath));
R_TRY(this->fs->CreateDirectory(CommittedDirectoryPath));
}
} R_END_TRY_CATCH;
/* Now check for the committed directory. */
R_TRY_CATCH(this->fs->GetEntryType(&ent_type, CommittedDirectoryPath)) {
/* Committed doesn't exist, so synchronize and rename. */
R_CATCH(ResultFsPathNotFound) {
R_TRY(this->SynchronizeDirectory(SynchronizingDirectoryPath, WorkingDirectoryPath));
return this->fs->RenameDirectory(SynchronizingDirectoryPath, CommittedDirectoryPath);
}
} R_END_TRY_CATCH;
/* If committed exists, synchronize it to the working directory. */
return this->SynchronizeDirectory(WorkingDirectoryPath, CommittedDirectoryPath);
}
Result DirectorySaveDataFileSystem::SynchronizeDirectory(const FsPath &dst_dir, const FsPath &src_dir) {
/* Delete destination dir and recreate it. */
R_TRY_CATCH(this->fs->DeleteDirectoryRecursively(dst_dir)) {
R_CATCH(ResultFsPathNotFound) {
/* Nintendo returns error unconditionally, but I think that's a bug in their code. */
}
} R_END_TRY_CATCH;
R_TRY(this->fs->CreateDirectory(dst_dir));
/* Get a buffer to work with. */
void *work_buf = nullptr;
size_t work_buf_size = 0;
R_TRY(this->AllocateWorkBuffer(&work_buf, &work_buf_size, IdealWorkBuffersize));
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 (true) {
void *buf = malloc(try_size);
if (buf != nullptr) {
*out_buf = buf;
*out_size = try_size;
return ResultSuccess();
}
/* Divide size by two. */
try_size >>= 1;
AMS_ASSERT(try_size > 0x200);
}
/* TODO: Return a result here? Nintendo does not, but they have other allocation failed results. */
/* Consider returning ResultFsAllocationFailureInDirectorySaveDataFileSystem? */
}
Result DirectorySaveDataFileSystem::GetFullPath(char *out, size_t out_size, const char *relative_path) {
/* Validate path. */
if (1 + strnlen(relative_path, FS_MAX_PATH) >= out_size) {
return ResultFsTooLongPath;
}
if (relative_path[0] != '/') {
return ResultFsInvalidPath;
}
/* Copy working directory path. */
std::strncpy(out, WorkingDirectoryPath.str, out_size);
out[out_size-1] = 0;
/* Normalize it. */
constexpr size_t working_len = WorkingDirectoryPathLen - 1;
return FsPathUtils::Normalize(out + working_len, out_size - working_len, relative_path, nullptr);
}
void DirectorySaveDataFileSystem::OnWritableFileClose() {
std::scoped_lock lk(this->lock);
this->open_writable_files--;
/* TODO: Abort if < 0? N does not. */
}
Result DirectorySaveDataFileSystem::CopySaveFromProxy() {
if (this->proxy_save_fs != nullptr) {
/* Get a buffer to work with. */
void *work_buf = nullptr;
size_t work_buf_size = 0;
R_TRY(this->AllocateWorkBuffer(&work_buf, &work_buf_size, IdealWorkBuffersize));
ON_SCOPE_EXIT { free(work_buf); };
R_TRY(FsDirUtils::CopyDirectoryRecursively(this, this->proxy_save_fs.get(), FsPathUtils::RootPath, FsPathUtils::RootPath, work_buf, work_buf_size));
return this->Commit();
}
return ResultSuccess();
}
/* ================================================================================================ */
Result DirectorySaveDataFileSystem::CreateFileImpl(const FsPath &path, uint64_t size, int flags) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
std::scoped_lock lk(this->lock);
return this->fs->CreateFile(full_path, size, flags);
}
Result DirectorySaveDataFileSystem::DeleteFileImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
std::scoped_lock lk(this->lock);
return this->fs->DeleteFile(full_path);
}
Result DirectorySaveDataFileSystem::CreateDirectoryImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
std::scoped_lock lk(this->lock);
return this->fs->CreateDirectory(full_path);
}
Result DirectorySaveDataFileSystem::DeleteDirectoryImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
std::scoped_lock lk(this->lock);
return this->fs->DeleteDirectory(full_path);
}
Result DirectorySaveDataFileSystem::DeleteDirectoryRecursivelyImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
std::scoped_lock lk(this->lock);
return this->fs->DeleteDirectoryRecursively(full_path);
}
Result DirectorySaveDataFileSystem::RenameFileImpl(const FsPath &old_path, const FsPath &new_path) {
FsPath full_old_path, full_new_path;
R_TRY(GetFullPath(full_old_path, old_path));
R_TRY(GetFullPath(full_new_path, new_path));
std::scoped_lock lk(this->lock);
return this->fs->RenameFile(full_old_path, full_new_path);
}
Result DirectorySaveDataFileSystem::RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) {
FsPath full_old_path, full_new_path;
R_TRY(GetFullPath(full_old_path, old_path));
R_TRY(GetFullPath(full_new_path, new_path));
std::scoped_lock lk(this->lock);
return this->fs->RenameDirectory(full_old_path, full_new_path);
}
Result DirectorySaveDataFileSystem::GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
std::scoped_lock lk(this->lock);
return this->fs->GetEntryType(out, full_path);
}
Result DirectorySaveDataFileSystem::OpenFileImpl(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
std::scoped_lock lk(this->lock);
{
/* Open the raw file. */
std::unique_ptr<IFile> file;
R_TRY(this->fs->OpenFile(file, full_path, mode));
/* 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 ResultSuccess();
}
Result DirectorySaveDataFileSystem::OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
std::scoped_lock lk(this->lock);
return this->fs->OpenDirectory(out_dir, full_path, mode);
}
Result DirectorySaveDataFileSystem::CommitImpl() {
/* Here, Nintendo does the following (with retries): */
/* - Rename Committed -> Synchronizing. */
/* - Synchronize Working -> Synchronizing (deleting Synchronizing). */
/* - Rename Synchronizing -> Committed. */
/* I think this is not the optimal order to do things, as the previous committed directory */
/* will be deleted if there is an error during synchronization. */
/* Instead, we will synchronize first, then delete committed, then rename. */
std::scoped_lock lk(this->lock);
/* 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. */
R_TRY(FsDirUtils::RetryUntilTargetNotLocked(std::move(SynchronizeWorkingDir)));
/* Delete committed directory. */
R_TRY_CATCH(FsDirUtils::RetryUntilTargetNotLocked(std::move(DeleteCommittedDir))) {
R_CATCH(ResultFsPathNotFound) {
/* It is okay for us to not have a committed directory here. */
}
} R_END_TRY_CATCH;
/* Rename synchronizing directory to committed directory. */
R_TRY(FsDirUtils::RetryUntilTargetNotLocked(std::move(RenameSynchDir)));
/* TODO: Should I call this->fs->Commit()? Nintendo does not. */
return ResultSuccess();
}
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) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
std::scoped_lock lk(this->lock);
return this->fs->CleanDirectoryRecursively(full_path);
}
Result DirectorySaveDataFileSystem::GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) {
/* TODO: How should this work? N returns ResultFsNotImplemented. */
return ResultFsNotImplemented;
}
Result DirectorySaveDataFileSystem::QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, const FsPath &path) {
/* TODO: How should this work? N returns ResultFsNotImplemented. */
return ResultFsNotImplemented;
}

View File

@@ -1,91 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <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;
std::unique_ptr<IFileSystem> proxy_save_fs;
IFileSystem *fs;
ams::os::Mutex lock;
size_t open_writable_files = 0;
public:
DirectorySaveDataFileSystem(IFileSystem *fs, std::unique_ptr<IFileSystem> pfs) : unique_fs(fs), proxy_save_fs(std::move(pfs)) {
this->fs = this->unique_fs.get();
R_ASSERT(this->Initialize());
}
DirectorySaveDataFileSystem(std::unique_ptr<IFileSystem> fs, std::unique_ptr<IFileSystem> pfs) : unique_fs(std::move(fs)), proxy_save_fs(std::move(pfs)) {
this->fs = this->unique_fs.get();
R_ASSERT(this->Initialize());
}
DirectorySaveDataFileSystem(std::shared_ptr<IFileSystem> fs, std::unique_ptr<IFileSystem> pfs) : shared_fs(fs), proxy_save_fs(std::move(pfs)) {
this->fs = this->shared_fs.get();
R_ASSERT(this->Initialize());
}
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();
Result CopySaveFromProxy();
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

@@ -1,103 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstring>
#include <switch.h>
#include <stratosphere.hpp>
#include "../utils.hpp"
#include "fs_file_storage.hpp"
Result FileStorage::UpdateSize() {
if (this->size == InvalidSize) {
return this->file->GetSize(&this->size);
}
return ResultSuccess();
}
Result FileStorage::Read(void *buffer, size_t size, u64 offset) {
u64 read_size;
if (size == 0) {
return ResultSuccess();
}
if (buffer == nullptr) {
return ResultFsNullptrArgument;
}
R_TRY(this->UpdateSize());
if (!IStorage::IsRangeValid(offset, size, this->size)) {
return ResultFsOutOfRange;
}
/* Nintendo doesn't do check output read size, but we will for safety. */
R_TRY(this->file->Read(&read_size, offset, buffer, size));
if (read_size != size && read_size) {
return this->Read(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(buffer) + read_size), size - read_size, offset + read_size);
}
return ResultSuccess();
}
Result FileStorage::Write(void *buffer, size_t size, u64 offset) {
if (size == 0) {
return ResultSuccess();
}
if (buffer == nullptr) {
return ResultFsNullptrArgument;
}
R_TRY(this->UpdateSize());
if (!IStorage::IsRangeValid(offset, size, this->size)) {
return ResultFsOutOfRange;
}
return this->file->Write(offset, buffer, size);
}
Result FileStorage::Flush() {
return this->file->Flush();
}
Result FileStorage::GetSize(u64 *out_size) {
R_TRY(this->UpdateSize());
*out_size = this->size;
return ResultSuccess();
}
Result FileStorage::SetSize(u64 size) {
this->size = InvalidSize;
return this->file->SetSize(size);
}
Result FileStorage::OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) {
switch (operation_type) {
case FsOperationId_InvalidateCache:
case FsOperationId_QueryRange:
if (size == 0) {
if (operation_type == FsOperationId_QueryRange) {
if (out_range_info == nullptr) {
return ResultFsNullptrArgument;
}
/* N checks for size == sizeof(*out_range_info) here, but that's because their wrapper api is bad. */
std::memset(out_range_info, 0, sizeof(*out_range_info));
}
return ResultSuccess();
}
R_TRY(this->UpdateSize());
/* N checks for positivity + signed overflow on offset/size here, but we're using unsigned types... */
return this->file->OperateRange(operation_type, offset, size, out_range_info);
default:
return ResultFsUnsupportedOperation;
}
}

View File

@@ -1,54 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "fs_shim.h"
#include "../debug.hpp"
#include "fs_istorage.hpp"
#include "fs_ifile.hpp"
class FileStorage : public IStorage {
public:
static constexpr u64 InvalidSize = UINT64_MAX;
private:
std::shared_ptr<IFile> base_file;
IFile *file;
u64 size;
public:
FileStorage(IFile *f) : base_file(f) {
this->file = this->base_file.get();
this->size = InvalidSize;
};
FileStorage(std::shared_ptr<IFile> f) : base_file(f) {
this->file = this->base_file.get();
this->size = InvalidSize;
};
virtual ~FileStorage() {
/* ... */
};
protected:
Result UpdateSize();
public:
virtual Result Read(void *buffer, size_t size, u64 offset) override;
virtual Result Write(void *buffer, size_t size, u64 offset) override;
virtual Result Flush() override;
virtual Result GetSize(u64 *out_size) override;
virtual Result SetSize(u64 size) override;
virtual Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override;
};

View File

@@ -1,115 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
class IDirectory {
public:
virtual ~IDirectory() {}
Result Read(uint64_t *out_count, FsDirectoryEntry *out_entries, uint64_t max_entries) {
if (out_count == nullptr) {
return ResultFsNullptrArgument;
}
if (max_entries == 0) {
*out_count = 0;
return ResultSuccess();
}
if (out_entries == nullptr) {
return ResultFsNullptrArgument;
}
return ReadImpl(out_count, out_entries, max_entries);
}
Result GetEntryCount(uint64_t *count) {
if (count == nullptr) {
return ResultFsNullptrArgument;
}
return GetEntryCountImpl(count);
}
protected:
/* ...? */
private:
virtual Result ReadImpl(uint64_t *out_count, FsDirectoryEntry *out_entries, uint64_t max_entries) = 0;
virtual Result GetEntryCountImpl(uint64_t *count) = 0;
};
class IDirectoryInterface : public IServiceObject {
private:
enum class CommandId {
Read = 0,
GetEntryCount = 1,
};
private:
std::unique_ptr<IDirectory> base_dir;
public:
IDirectoryInterface(IDirectory *d) : base_dir(d) {
/* ... */
};
IDirectoryInterface(std::unique_ptr<IDirectory> d) : base_dir(std::move(d)) {
/* ... */
};
private:
/* Actual command API. */
virtual Result Read(OutBuffer<FsDirectoryEntry> buffer, Out<u64> out_count) final {
return this->base_dir->Read(out_count.GetPointer(), buffer.buffer, buffer.num_elements);
};
virtual Result GetEntryCount(Out<u64> out_count) final {
return this->base_dir->GetEntryCount(out_count.GetPointer());
};
public:
DEFINE_SERVICE_DISPATCH_TABLE {
/* 1.0.0- */
MAKE_SERVICE_COMMAND_META(IDirectoryInterface, Read),
MAKE_SERVICE_COMMAND_META(IDirectoryInterface, GetEntryCount),
};
};
class ProxyDirectory : public IDirectory {
private:
std::unique_ptr<FsDir> base_dir;
public:
ProxyDirectory(FsDir *d) : base_dir(d) {
/* ... */
}
ProxyDirectory(std::unique_ptr<FsDir> d) : base_dir(std::move(d)) {
/* ... */
}
ProxyDirectory(FsDir d) {
this->base_dir = std::make_unique<FsDir>(d);
}
virtual ~ProxyDirectory() {
fsDirClose(this->base_dir.get());
}
public:
virtual Result ReadImpl(uint64_t *out_count, FsDirectoryEntry *out_entries, uint64_t max_entries) {
size_t count;
R_TRY(fsDirRead(this->base_dir.get(), 0, &count, max_entries, out_entries));
*out_count = count;
return ResultSuccess();
}
virtual Result GetEntryCountImpl(uint64_t *count) {
return fsDirGetEntryCount(this->base_dir.get(), count);
}
};

View File

@@ -1,195 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "fs_shim.h"
class IFile {
public:
virtual ~IFile() {}
Result Read(uint64_t *out, uint64_t offset, void *buffer, uint64_t size, uint32_t flags) {
(void)(flags);
if (out == nullptr) {
return ResultFsNullptrArgument;
}
if (size == 0) {
*out = 0;
return ResultSuccess();
}
if (buffer == nullptr) {
return ResultFsNullptrArgument;
}
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;
}
return GetSizeImpl(out);
}
Result Flush() {
return FlushImpl();
}
Result Write(uint64_t offset, void *buffer, uint64_t size, uint32_t flags) {
if (size == 0) {
return ResultSuccess();
}
if (buffer == nullptr) {
return ResultFsNullptrArgument;
}
return WriteImpl(offset, buffer, size, flags);
}
Result Write(uint64_t offset, void *buffer, uint64_t size, bool flush = false) {
if (size == 0) {
return ResultSuccess();
}
if (buffer == nullptr) {
return ResultFsNullptrArgument;
}
return WriteImpl(offset, buffer, size, flush);
}
Result SetSize(uint64_t size) {
return SetSizeImpl(size);
}
Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) {
if (operation_type == FsOperationId_QueryRange) {
return OperateRangeImpl(operation_type, offset, size, out_range_info);
}
return ResultFsUnsupportedOperation;
}
protected:
/* ...? */
private:
virtual Result ReadImpl(u64 *out, u64 offset, void *buffer, u64 size) = 0;
virtual Result GetSizeImpl(u64 *out) = 0;
virtual Result FlushImpl() = 0;
virtual Result WriteImpl(u64 offset, void *buffer, u64 size, u32 option) = 0;
virtual Result SetSizeImpl(u64 size) = 0;
virtual Result OperateRangeImpl(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0;
};
class IFileInterface : public IServiceObject {
private:
enum class CommandId {
Read = 0,
Write = 1,
Flush = 2,
SetSize = 3,
GetSize = 4,
OperateRange = 5,
};
private:
std::unique_ptr<IFile> base_file;
public:
IFileInterface(IFile *f) : base_file(f) {
/* ... */
};
IFileInterface(std::unique_ptr<IFile> f) : base_file(std::move(f)) {
/* ... */
};
private:
/* Actual command API. */
virtual Result Read(OutBuffer<u8, BufferType_Type1> buffer, Out<u64> out_read, u64 offset, u64 size, u32 read_flags) final {
return this->base_file->Read(out_read.GetPointer(), offset, buffer.buffer, std::min(buffer.num_elements, size), read_flags);
};
virtual Result Write(InBuffer<u8, BufferType_Type1> buffer, u64 offset, u64 size, u32 write_flags) final {
return this->base_file->Write(offset, buffer.buffer, std::min(buffer.num_elements, size), write_flags);
};
virtual Result Flush() final {
return this->base_file->Flush();
};
virtual Result SetSize(u64 size) final {
return this->base_file->SetSize(size);
};
virtual Result GetSize(Out<u64> size) final {
return this->base_file->GetSize(size.GetPointer());
};
virtual Result OperateRange(Out<FsRangeInfo> range_info, u32 operation_type, u64 offset, u64 size) final {
return this->base_file->OperateRange(static_cast<FsOperationId>(operation_type), offset, size, range_info.GetPointer());
};
public:
DEFINE_SERVICE_DISPATCH_TABLE {
/* 1.0.0- */
MAKE_SERVICE_COMMAND_META(IFileInterface, Read),
MAKE_SERVICE_COMMAND_META(IFileInterface, Write),
MAKE_SERVICE_COMMAND_META(IFileInterface, Flush),
MAKE_SERVICE_COMMAND_META(IFileInterface, SetSize),
MAKE_SERVICE_COMMAND_META(IFileInterface, GetSize),
/* 4.0.0- */
MAKE_SERVICE_COMMAND_META(IFileInterface, OperateRange, FirmwareVersion_400),
};
};
class ProxyFile : public IFile {
private:
std::unique_ptr<FsFile> base_file;
public:
ProxyFile(FsFile *f) : base_file(f) {
/* ... */
}
ProxyFile(std::unique_ptr<FsFile> f) : base_file(std::move(f)) {
/* ... */
}
ProxyFile(FsFile f) {
this->base_file = std::make_unique<FsFile>(f);
}
virtual ~ProxyFile() {
fsFileClose(this->base_file.get());
}
public:
virtual Result ReadImpl(u64 *out, u64 offset, void *buffer, u64 size) override {
size_t out_sz;
R_TRY(fsFileRead(this->base_file.get(), offset, buffer, size, FS_READOPTION_NONE, &out_sz));
*out = out_sz;
return ResultSuccess();
}
virtual Result GetSizeImpl(u64 *out) override {
return fsFileGetSize(this->base_file.get(), out);
}
virtual Result FlushImpl() override {
return fsFileFlush(this->base_file.get());
}
virtual Result WriteImpl(u64 offset, void *buffer, u64 size, u32 option) override {
return fsFileWrite(this->base_file.get(), offset, buffer, size, option);
}
virtual Result SetSizeImpl(u64 size) override {
return fsFileSetSize(this->base_file.get(), size);
}
virtual Result OperateRangeImpl(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
return fsFileOperateRange(this->base_file.get(), operation_type, offset, size, out_range_info);
}
};

View File

@@ -1,463 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "../utils.hpp"
#include "fs_filesystem_types.hpp"
#include "fs_path_utils.hpp"
#include "fs_ifile.hpp"
#include "fs_idirectory.hpp"
class IFile;
class IDirectory;
class IFileSystem {
public:
virtual ~IFileSystem() {}
Result CreateFile(const FsPath &path, uint64_t size, int flags) {
return CreateFileImpl(path, size, flags);
}
Result CreateFile(const FsPath &path, uint64_t size) {
return CreateFileImpl(path, size, 0);
}
Result DeleteFile(const FsPath &path) {
return DeleteFileImpl(path);
}
Result CreateDirectory(const FsPath &path) {
return CreateDirectoryImpl(path);
}
Result DeleteDirectory(const FsPath &path) {
return DeleteDirectoryImpl(path);
}
Result DeleteDirectoryRecursively(const FsPath &path) {
return DeleteDirectoryRecursivelyImpl(path);
}
Result RenameFile(const FsPath &old_path, const FsPath &new_path) {
return RenameFileImpl(old_path, new_path);
}
Result RenameDirectory(const FsPath &old_path, const FsPath &new_path) {
return RenameDirectoryImpl(old_path, new_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, const FsPath &path, OpenMode mode) {
if (!(mode & OpenMode_ReadWrite)) {
return ResultFsInvalidArgument;
}
if (mode & ~OpenMode_All) {
return ResultFsInvalidArgument;
}
return OpenFileImpl(out_file, path, mode);
}
Result OpenDirectory(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) {
if (!(mode & DirectoryOpenMode_All)) {
return ResultFsInvalidArgument;
}
if (mode & ~DirectoryOpenMode_All) {
return ResultFsInvalidArgument;
}
return OpenDirectoryImpl(out_dir, path, mode);
}
Result Commit() {
return CommitImpl();
}
Result GetFreeSpaceSize(uint64_t *out, const FsPath &path) {
if (out == nullptr) {
return ResultFsNullptrArgument;
}
return GetFreeSpaceSizeImpl(out, path);
}
Result GetTotalSpaceSize(uint64_t *out, const FsPath &path) {
if (out == nullptr) {
return ResultFsNullptrArgument;
}
return GetTotalSpaceSizeImpl(out, path);
}
Result CleanDirectoryRecursively(const FsPath &path) {
return CleanDirectoryRecursivelyImpl(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, const FsPath &path) {
return QueryEntryImpl(out, out_size, in, in_size, query, path);
}
protected:
/* ...? */
private:
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, const FsPath &path) {
(void)(out);
(void)(path);
return ResultFsNotImplemented;
}
virtual Result GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) {
(void)(out);
(void)(path);
return ResultFsNotImplemented;
}
virtual Result CleanDirectoryRecursivelyImpl(const FsPath &path) = 0;
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, const FsPath &path) {
(void)(out);
(void)(out_size);
(void)(in);
(void)(in_size);
(void)(query);
(void)(path);
return ResultFsNotImplemented;
}
};
class IFileSystemInterface : public IServiceObject {
private:
enum class CommandId {
/* 1.0.0+ */
CreateFile = 0,
DeleteFile = 1,
CreateDirectory = 2,
DeleteDirectory = 3,
DeleteDirectoryRecursively = 4,
RenameFile = 5,
RenameDirectory = 6,
GetEntryType = 7,
OpenFile = 8,
OpenDirectory = 9,
Commit = 10,
GetFreeSpaceSize = 11,
GetTotalSpaceSize = 12,
/* 3.0.0+ */
CleanDirectoryRecursively = 13,
GetFileTimeStampRaw = 14,
/* 4.0.0+ */
QueryEntry = 15,
};
private:
std::unique_ptr<IFileSystem> unique_fs;
std::shared_ptr<IFileSystem> shared_fs;
IFileSystem *base_fs;
public:
IFileSystemInterface(IFileSystem *fs) : unique_fs(fs) {
this->base_fs = this->unique_fs.get();
};
IFileSystemInterface(std::unique_ptr<IFileSystem> fs) : unique_fs(std::move(fs)) {
this->base_fs = this->unique_fs.get();
};
IFileSystemInterface(std::shared_ptr<IFileSystem> fs) : shared_fs(fs) {
this->base_fs = this->shared_fs.get();
};
private:
/* Actual command API. */
virtual Result CreateFile(InPointer<char> in_path, uint64_t size, int flags) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
return this->base_fs->CreateFile(path, size, flags);
}
virtual Result DeleteFile(InPointer<char> in_path) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
return this->base_fs->DeleteFile(path);
}
virtual Result CreateDirectory(InPointer<char> in_path) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
return this->base_fs->CreateDirectory(path);
}
virtual Result DeleteDirectory(InPointer<char> in_path) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
return this->base_fs->DeleteDirectory(path);
}
virtual Result DeleteDirectoryRecursively(InPointer<char> in_path) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
return this->base_fs->DeleteDirectoryRecursively(path);
}
virtual Result RenameFile(InPointer<char> in_old_path, InPointer<char> in_new_path) final {
FsPath old_path;
FsPath new_path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&old_path, in_old_path.pointer));
R_TRY(FsPathUtils::ConvertPathForServiceObject(&new_path, in_new_path.pointer));
return this->base_fs->RenameFile(old_path, new_path);
}
virtual Result RenameDirectory(InPointer<char> in_old_path, InPointer<char> in_new_path) final {
FsPath old_path;
FsPath new_path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&old_path, in_old_path.pointer));
R_TRY(FsPathUtils::ConvertPathForServiceObject(&new_path, in_new_path.pointer));
return this->base_fs->RenameDirectory(old_path, new_path);
}
virtual Result GetEntryType(Out<u32> out_type, InPointer<char> in_path) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
DirectoryEntryType type;
R_TRY(this->base_fs->GetEntryType(&type, path));
out_type.SetValue(type);
return ResultSuccess();
}
virtual Result OpenFile(Out<std::shared_ptr<IFileInterface>> out_intf, InPointer<char> in_path, uint32_t mode) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
std::unique_ptr<IFile> out_file;
R_TRY(this->base_fs->OpenFile(out_file, path, static_cast<OpenMode>(mode)));
out_intf.SetValue(std::make_shared<IFileInterface>(std::move(out_file)));
return ResultSuccess();
}
virtual Result OpenDirectory(Out<std::shared_ptr<IDirectoryInterface>> out_intf, InPointer<char> in_path, uint32_t mode) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
std::unique_ptr<IDirectory> out_dir;
R_TRY(this->base_fs->OpenDirectory(out_dir, path, static_cast<DirectoryOpenMode>(mode)));
out_intf.SetValue(std::make_shared<IDirectoryInterface>(std::move(out_dir)));
return ResultSuccess();
}
virtual Result Commit() final {
return this->base_fs->Commit();
}
virtual Result GetFreeSpaceSize(Out<uint64_t> out_size, InPointer<char> in_path) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
return this->base_fs->GetFreeSpaceSize(out_size.GetPointer(), path);
}
virtual Result GetTotalSpaceSize(Out<uint64_t> out_size, InPointer<char> in_path) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
return this->base_fs->GetTotalSpaceSize(out_size.GetPointer(), path);
}
virtual Result CleanDirectoryRecursively(InPointer<char> in_path) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
return this->base_fs->CleanDirectoryRecursively(path);
}
virtual Result GetFileTimeStampRaw(Out<FsTimeStampRaw> out_timestamp, InPointer<char> in_path) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
return this->base_fs->GetFileTimeStampRaw(out_timestamp.GetPointer(), path);
}
virtual Result QueryEntry(OutBuffer<char, BufferType_Type1> out_buffer, InBuffer<char, BufferType_Type1> in_buffer, int query, InPointer<char> in_path) final {
FsPath path;
R_TRY(FsPathUtils::ConvertPathForServiceObject(&path, in_path.pointer));
return this->base_fs->QueryEntry(out_buffer.buffer, out_buffer.num_elements, in_buffer.buffer, in_buffer.num_elements, query, path);
}
public:
DEFINE_SERVICE_DISPATCH_TABLE {
/* 1.0.0- */
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, CreateFile),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, DeleteFile),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, CreateDirectory),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, DeleteDirectory),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, DeleteDirectoryRecursively),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, RenameFile),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, RenameDirectory),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, GetEntryType),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, OpenFile),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, OpenDirectory),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, Commit),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, GetFreeSpaceSize),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, GetTotalSpaceSize),
/* 3.0.0- */
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, CleanDirectoryRecursively, FirmwareVersion_300),
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, GetFileTimeStampRaw, FirmwareVersion_300),
/* 4.0.0- */
MAKE_SERVICE_COMMAND_META(IFileSystemInterface, QueryEntry, FirmwareVersion_400),
};
};
class ProxyFileSystem : public IFileSystem {
private:
std::unique_ptr<FsFileSystem> base_fs;
public:
ProxyFileSystem(FsFileSystem *fs) : base_fs(fs) {
/* ... */
}
ProxyFileSystem(std::unique_ptr<FsFileSystem> fs) : base_fs(std::move(fs)) {
/* ... */
}
ProxyFileSystem(FsFileSystem fs) {
this->base_fs = std::make_unique<FsFileSystem>(fs);
}
virtual ~ProxyFileSystem() {
fsFsClose(this->base_fs.get());
}
public:
virtual Result CreateFileImpl(const FsPath &path, uint64_t size, int flags) {
return fsFsCreateFile(this->base_fs.get(), path.str, size, flags);
}
virtual Result DeleteFileImpl(const FsPath &path) {
return fsFsDeleteFile(this->base_fs.get(), path.str);
}
virtual Result CreateDirectoryImpl(const FsPath &path) {
return fsFsCreateDirectory(this->base_fs.get(), path.str);
}
virtual Result DeleteDirectoryImpl(const FsPath &path) {
return fsFsDeleteDirectory(this->base_fs.get(), path.str);
}
virtual Result DeleteDirectoryRecursivelyImpl(const FsPath &path) {
return fsFsDeleteDirectoryRecursively(this->base_fs.get(), path.str);
}
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(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, const FsPath &path) {
FsEntryType type;
R_TRY(fsFsGetEntryType(this->base_fs.get(), path.str, &type));
*out = static_cast<DirectoryEntryType>(static_cast<u32>(type));
return ResultSuccess();
}
virtual Result OpenFileImpl(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) {
FsFile f;
R_TRY(fsFsOpenFile(this->base_fs.get(), path.str, static_cast<int>(mode), &f));
out_file = std::make_unique<ProxyFile>(f);
return ResultSuccess();
}
virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) {
FsDir d;
R_TRY(fsFsOpenDirectory(this->base_fs.get(), path.str, static_cast<int>(mode), &d));
out_dir = std::make_unique<ProxyDirectory>(d);
return ResultSuccess();
}
virtual Result CommitImpl() {
return fsFsCommit(this->base_fs.get());
}
virtual Result GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) {
return fsFsGetFreeSpace(this->base_fs.get(), path.str, out);
}
virtual Result GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) {
return fsFsGetTotalSpace(this->base_fs.get(), path.str, out);
}
virtual Result CleanDirectoryRecursivelyImpl(const FsPath &path) {
return fsFsCleanDirectoryRecursively(this->base_fs.get(), path.str);
}
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, const FsPath &path) {
return fsFsQueryEntry(this->base_fs.get(), out, out_size, in, in_size, path.str,static_cast<FsFileSystemQueryType>(query));
}
};

View File

@@ -1,175 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "fs_shim.h"
#include "../debug.hpp"
class IStorage {
public:
virtual ~IStorage();
virtual Result Read(void *buffer, size_t size, u64 offset) = 0;
virtual Result Write(void *buffer, size_t size, u64 offset) = 0;
virtual Result Flush() = 0;
virtual Result SetSize(u64 size) = 0;
virtual Result GetSize(u64 *out_size) = 0;
virtual Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0;
static inline bool IsRangeValid(uint64_t offset, uint64_t size, uint64_t total_size) {
return size <= total_size && offset <= total_size - size;
}
};
class IStorageInterface : public IServiceObject {
private:
enum class CommandId {
Read = 0,
Write = 1,
Flush = 2,
SetSize = 3,
GetSize = 4,
OperateRange = 5,
};
private:
IStorage *base_storage;
public:
IStorageInterface(IStorage *s) : base_storage(s) {
/* ... */
};
~IStorageInterface() {
delete base_storage;
};
private:
/* Actual command API. */
virtual Result Read(OutBuffer<u8, BufferType_Type1> buffer, u64 offset, u64 size) final {
return this->base_storage->Read(buffer.buffer, std::min(buffer.num_elements, size), offset);
};
virtual Result Write(InBuffer<u8, BufferType_Type1> buffer, u64 offset, u64 size) final {
return this->base_storage->Write(buffer.buffer, std::min(buffer.num_elements, size), offset);
};
virtual Result Flush() final {
return this->base_storage->Flush();
};
virtual Result SetSize(u64 size) final {
return this->base_storage->SetSize(size);
};
virtual Result GetSize(Out<u64> size) final {
return this->base_storage->GetSize(size.GetPointer());
};
virtual Result OperateRange(Out<FsRangeInfo> range_info, u32 operation_type, u64 offset, u64 size) final {
return this->base_storage->OperateRange(static_cast<FsOperationId>(operation_type), offset, size, range_info.GetPointer());
};
public:
DEFINE_SERVICE_DISPATCH_TABLE {
/* 1.0.0- */
MAKE_SERVICE_COMMAND_META(IStorageInterface, Read),
MAKE_SERVICE_COMMAND_META(IStorageInterface, Write),
MAKE_SERVICE_COMMAND_META(IStorageInterface, Flush),
MAKE_SERVICE_COMMAND_META(IStorageInterface, SetSize),
MAKE_SERVICE_COMMAND_META(IStorageInterface, GetSize),
/* 4.0.0- */
MAKE_SERVICE_COMMAND_META(IStorageInterface, OperateRange, FirmwareVersion_400),
};
};
class IROStorage : public IStorage {
public:
virtual Result Read(void *buffer, size_t size, u64 offset) = 0;
virtual Result Write(void *buffer, size_t size, u64 offset) final {
(void)(buffer);
(void)(offset);
(void)(size);
return ResultFsUnsupportedOperation;
};
virtual Result Flush() final {
return ResultSuccess();
};
virtual Result SetSize(u64 size) final {
(void)(size);
return ResultFsUnsupportedOperation;
};
virtual Result GetSize(u64 *out_size) = 0;
virtual Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) = 0;
};
class ProxyStorage : public IStorage {
private:
FsStorage *base_storage;
public:
ProxyStorage(FsStorage *s) : base_storage(s) {
/* ... */
};
ProxyStorage(FsStorage s) {
this->base_storage = new FsStorage(s);
};
virtual ~ProxyStorage() {
fsStorageClose(base_storage);
delete base_storage;
};
public:
virtual Result Read(void *buffer, size_t size, u64 offset) override {
return fsStorageRead(this->base_storage, offset, buffer, size);
};
virtual Result Write(void *buffer, size_t size, u64 offset) override {
return fsStorageWrite(this->base_storage, offset, buffer, size);
};
virtual Result Flush() override {
return fsStorageFlush(this->base_storage);
};
virtual Result GetSize(u64 *out_size) override {
return fsStorageGetSize(this->base_storage, out_size);
};
virtual Result SetSize(u64 size) override {
return fsStorageSetSize(this->base_storage, size);
};
virtual Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
return fsStorageOperateRange(this->base_storage, operation_type, offset, size, out_range_info);
};
};
class ReadOnlyStorageAdapter : public IROStorage {
private:
std::shared_ptr<IStorage> base_storage;
IStorage *storage;
public:
ReadOnlyStorageAdapter(IStorage *s) : base_storage(s) {
this->storage = this->base_storage.get();
}
ReadOnlyStorageAdapter(std::shared_ptr<IStorage> s) : base_storage(s) {
this->storage = this->base_storage.get();
}
virtual ~ReadOnlyStorageAdapter() {
/* ... */
}
public:
virtual Result Read(void *buffer, size_t size, u64 offset) override {
return this->base_storage->Read(buffer, size, offset);
};
virtual Result GetSize(u64 *out_size) override {
return this->base_storage->GetSize(out_size);
};
virtual Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
return this->base_storage->OperateRange(operation_type, offset, size, out_range_info);
};
};

View File

@@ -15,24 +15,28 @@
*/
#pragma once
#include <stratosphere.hpp>
enum OpenMode {
OpenMode_Read = (1 << 0),
OpenMode_Write = (1 << 1),
OpenMode_Append = (1 << 2),
namespace ams::mitm::fs {
OpenMode_ReadWrite = OpenMode_Read | OpenMode_Write,
OpenMode_All = OpenMode_ReadWrite | OpenMode_Append,
};
class FsMitmService : public sf::IMitmServiceObject {
private:
enum class CommandId {
/* TODO */
};
public:
static bool ShouldMitm(os::ProcessId process_id, ncm::ProgramId program_id) {
/* TODO */
return false;
}
public:
SF_MITM_SERVICE_OBJECT_CTOR(FsMitmService) { /* ... */ }
protected:
/* TODO */
public:
DEFINE_SERVICE_DISPATCH_TABLE {
/* TODO */
};
};
enum DirectoryOpenMode {
DirectoryOpenMode_Directories = (1 << 0),
DirectoryOpenMode_Files = (1 << 1),
DirectoryOpenMode_All = (DirectoryOpenMode_Directories | DirectoryOpenMode_Files),
};
enum DirectoryEntryType {
DirectoryEntryType_Directory,
DirectoryEntryType_File,
};
}

View File

@@ -1,267 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstring>
#include <cstdlib>
#include <switch.h>
#include <stratosphere.hpp>
#include "fs_path_utils.hpp"
Result FsPathUtils::VerifyPath(const char *path, size_t max_path_len, size_t max_name_len) {
const char *cur = path;
size_t name_len = 0;
for (size_t path_len = 0; path_len <= max_path_len && name_len <= max_name_len; path_len++) {
const char c = *(cur++);
/* If terminated, we're done. */
if (c == 0) {
return ResultSuccess();
}
/* TODO: Nintendo converts the path from utf-8 to utf-32, one character at a time. */
/* We should do this. */
/* Banned characters: :*?<>| */
if (c == ':' || c == '*' || c == '?' || c == '<' || c == '>' || c == '|') {
return ResultFsInvalidCharacter;
}
name_len++;
/* Check for separator. */
if (c == '/' || c == '\\') {
name_len = 0;
}
}
return ResultFsTooLongPath;
}
Result FsPathUtils::ConvertPathForServiceObject(FsPath *out, const char *path) {
/* Check for nullptr. */
if (out == nullptr || path == nullptr) {
return ResultFsInvalidPath;
}
/* Copy string, NULL terminate. */
/* NOTE: Nintendo adds an extra char at 0x301 for NULL terminator */
/* But then forces 0x300 to NULL anyway... */
std::strncpy(out->str, path, sizeof(out->str) - 1);
out->str[sizeof(out->str)-1] = 0;
/* Replace any instances of \ with / */
for (size_t i = 0; i < sizeof(out->str); i++) {
if (out->str[i] == '\\') {
out->str[i] = '/';
}
}
/* Nintendo allows some liberties if the path is a windows path. Who knows why... */
const auto prefix_len = FsPathUtils::IsWindowsAbsolutePath(path) ? 2 : 0;
const size_t max_len = (FS_MAX_PATH-1) - prefix_len;
return FsPathUtils::VerifyPath(out->str + prefix_len, max_len, max_len);
}
Result FsPathUtils::IsNormalized(bool *out, const char *path) {
/* Nintendo uses a state machine here. */
enum class PathState {
Start,
Normal,
FirstSeparator,
Separator,
CurrentDir,
ParentDir,
WindowsDriveLetter,
};
PathState state = PathState::Start;
for (const char *cur = path; *cur != 0; cur++) {
const char c = *cur;
switch (state) {
case PathState::Start:
if (IsWindowsDriveLetter(c)) {
state = PathState::WindowsDriveLetter;
} else if (c == '/') {
state = PathState::FirstSeparator;
} else {
return ResultFsInvalidPathFormat;
}
break;
case PathState::Normal:
if (c == '/') {
state = PathState::Separator;
}
break;
case PathState::FirstSeparator:
case PathState::Separator:
/* It is unclear why first separator and separator are separate states... */
if (c == '/') {
*out = false;
return ResultSuccess();
} else if (c == '.') {
state = PathState::CurrentDir;
} else {
state = PathState::Normal;
}
break;
case PathState::CurrentDir:
if (c == '/') {
*out = false;
return ResultSuccess();
} else if (c == '.') {
state = PathState::ParentDir;
} else {
state = PathState::Normal;
}
break;
case PathState::ParentDir:
if (c == '/') {
*out = false;
return ResultSuccess();
} else {
state = PathState::Normal;
}
break;
case PathState::WindowsDriveLetter:
if (c == ':') {
*out = true;
return ResultSuccess();
} else {
return ResultFsInvalidPathFormat;
}
break;
}
}
switch (state) {
case PathState::Start:
case PathState::WindowsDriveLetter:
return ResultFsInvalidPathFormat;
case PathState::FirstSeparator:
case PathState::Normal:
*out = true;
break;
case PathState::CurrentDir:
case PathState::ParentDir:
case PathState::Separator:
*out = false;
break;
}
return ResultSuccess();
}
Result FsPathUtils::Normalize(char *out, size_t max_out_size, const char *src, size_t *out_len) {
/* Paths must start with / */
if (src[0] != '/') {
return ResultFsInvalidPathFormat;
}
bool skip_next_sep = false;
size_t i = 0;
size_t len = 0;
while (src[i] != 0) {
if (src[i] == '/') {
/* Swallow separators. */
while (src[++i] == '/') { }
if (src[i] == 0) {
break;
}
/* Handle skip if needed */
if (!skip_next_sep) {
if (len + 1 == max_out_size) {
out[len] = 0;
if (out_len != nullptr) {
*out_len = len;
}
return ResultFsTooLongPath;
}
out[len++] = '/';
/* TODO: N has some weird windows support stuff here under a bool. */
/* Boolean is normally false though? */
}
skip_next_sep = false;
}
/* See length of current dir. */
size_t dir_len = 0;
while (src[i+dir_len] != '/' && src[i+dir_len] != 0) {
dir_len++;
}
if (FsPathUtils::IsCurrentDirectory(&src[i])) {
skip_next_sep = true;
} else if (FsPathUtils::IsParentDirectory(&src[i])) {
if (len == 1) {
return ResultFsDirectoryUnobtainable;
}
/* Walk up a directory. */
len -= 2;
while (out[len] != '/') {
len--;
}
} else {
/* Copy, possibly truncating. */
if (len + dir_len + 1 <= max_out_size) {
for (size_t j = 0; j < dir_len; j++) {
out[len++] = src[i+j];
}
} else {
const size_t copy_len = max_out_size - 1 - len;
for (size_t j = 0; j < copy_len; j++) {
out[len++] = src[i+j];
}
out[len] = 0;
if (out_len != nullptr) {
*out_len = len;
}
return ResultFsTooLongPath;
}
}
i += dir_len;
}
if (skip_next_sep) {
len--;
}
if (len == 0 && max_out_size) {
out[len++] = '/';
}
if (max_out_size < len - 1) {
return ResultFsTooLongPath;
}
/* NULL terminate. */
out[len] = 0;
if (out_len != nullptr) {
*out_len = len;
}
/* Assert normalized. */
bool normalized = false;
R_ASSERT(FsPathUtils::IsNormalized(&normalized, out));
AMS_ASSERT(normalized);
return ResultSuccess();
}

View File

@@ -1,72 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
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);
static Result IsNormalized(bool *out, const char *path);
static Result Normalize(char *out, size_t max_out_size, const char *src, size_t *out_size);
static bool IsWindowsDriveLetter(const char c) {
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
}
static bool IsCurrentDirectory(const char *path) {
return path[0] == '.' && (path[1] == 0 || path[1] == '/');
}
static bool IsParentDirectory(const char *path) {
return path[0] == '.' && path[1] == '.' && (path[2] == 0 || path[2] == '/');
}
static bool IsWindowsAbsolutePath(const char *path) {
/* Nintendo uses this in path comparisons... */
return IsWindowsDriveLetter(path[0]) && path[1] == ':';
}
};

View File

@@ -1,125 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstring>
#include <cstdlib>
#include <switch.h>
#include "fs_save_utils.hpp"
Result FsSaveUtils::GetSaveDataSpaceIdString(const char **out_str, u8 space_id) {
const char *str = nullptr;
switch (space_id) {
case FsSaveDataSpaceId_NandSystem:
case 100: /* ProperSystem */
case 101: /* SafeMode */
str = "sys";
break;
case FsSaveDataSpaceId_NandUser:
str = "user";
break;
case FsSaveDataSpaceId_SdCard:
str = "sd_sys";
break;
case FsSaveDataSpaceId_TemporaryStorage:
str = "temp";
break;
case 4: /* SdUser */
str = "sd_user";
break;
default: /* Unexpected */
str = nullptr;
break;
}
if (str == nullptr) {
return ResultFsInvalidSaveDataSpaceId;
}
if (out_str) {
*out_str = str;
}
return ResultSuccess();
}
Result FsSaveUtils::GetSaveDataTypeString(const char **out_str, u8 save_data_type) {
const char *str = nullptr;
switch (save_data_type) {
case FsSaveDataType_SystemSaveData:
str = "system";
break;
case FsSaveDataType_SaveData:
str = "account";
break;
case FsSaveDataType_BcatDeliveryCacheStorage:
str = "bcat";
break;
case FsSaveDataType_DeviceSaveData:
str = "device";
break;
case FsSaveDataType_TemporaryStorage:
str = "temp";
break;
case FsSaveDataType_CacheStorage:
str = "cache";
break;
case 6: /* System Bcat Save */
str = "bcat_sys";
break;
default: /* Unexpected */
str = nullptr;
break;
}
if (str == nullptr) {
/* TODO: Better result? */
return ResultFsInvalidArgument;
}
if (out_str) {
*out_str = str;
}
return ResultSuccess();
}
Result FsSaveUtils::GetSaveDataDirectoryPath(FsPath &out_path, u8 space_id, u8 save_data_type, u64 title_id, u128 user_id, u64 save_id) {
const char *space_id_str, *save_type_str;
/* Get space_id, save_data_type strings. */
R_TRY(GetSaveDataSpaceIdString(&space_id_str, space_id));
R_TRY(GetSaveDataTypeString(&save_type_str, save_data_type));
/* Clear and initialize the path. */
std::memset(&out_path, 0, sizeof(out_path));
const bool is_system = (save_id != 0 && user_id == 0);
size_t out_path_len;
if (is_system) {
out_path_len = static_cast<size_t>(snprintf(out_path.str, sizeof(out_path.str), "/atmosphere/saves/sysnand/%s/%s/%016lx",
space_id_str, save_type_str, save_id));
} else {
out_path_len = static_cast<size_t>(snprintf(out_path.str, sizeof(out_path.str), "/atmosphere/saves/sysnand/%s/%s/%016lx/%016lx%016lx",
space_id_str, save_type_str, title_id, static_cast<u64>(user_id >> 64ul), static_cast<u64>(user_id)));
}
if (out_path_len >= sizeof(out_path)) {
/* TODO: Should we abort here? */
return ResultFsTooLongPath;
}
return ResultSuccess();
}

View File

@@ -1,29 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include "fs_path_utils.hpp"
#include "fs_ifilesystem.hpp"
class FsSaveUtils {
private:
static Result GetSaveDataSpaceIdString(const char **out_str, u8 space_id);
static Result GetSaveDataTypeString(const char **out_str, u8 save_data_type);
public:
static Result GetSaveDataDirectoryPath(FsPath &out_path, u8 space_id, u8 save_data_type, u64 title_id, u128 user_id, u64 save_id);
};

View File

@@ -1,266 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include <string.h>
#include "fs_shim.h"
/* Missing fsp-srv commands. */
Result fsOpenBisStorageFwd(Service* s, FsStorage* out, FsBisStorageId PartitionId) {
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
u32 PartitionId;
} *raw;
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 12;
raw->PartitionId = PartitionId;
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
serviceCreateSubservice(&out->s, s, &r, 0);
}
}
return rc;
}
Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out) {
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
} *raw;
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 200;
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
serviceCreateSubservice(&out->s, s, &r, 0);
}
}
return rc;
}
Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorageId storage_id, u64 data_id, FsStorage* out) {
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
FsStorageId storage_id;
u64 data_id;
} *raw;
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 202;
raw->storage_id = storage_id;
raw->data_id = data_id;
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
serviceCreateSubservice(&out->s, s, &r, 0);
}
}
return rc;
}
Result fsOpenFileSystemWithPatchFwd(Service* s, FsFileSystem* out, u64 titleId, FsFileSystemType fsType) {
if (hosversionBefore(2, 0, 0)) {
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
}
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
u32 fsType;
u64 titleId;
} *raw;
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 7;
raw->fsType = fsType;
raw->titleId = titleId;
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
serviceCreateSubservice(&out->s, s, &r, 0);
}
}
return rc;
}
Result fsOpenFileSystemWithIdFwd(Service* s, FsFileSystem* out, u64 titleId, FsFileSystemType fsType, const char* contentPath) {
if (hosversionBefore(2, 0, 0)) {
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
}
char sendStr[FS_MAX_PATH] = {0};
strncpy(sendStr, contentPath, sizeof(sendStr)-1);
IpcCommand c;
ipcInitialize(&c);
ipcAddSendStatic(&c, sendStr, sizeof(sendStr), 0);
struct {
u64 magic;
u64 cmd_id;
u32 fsType;
u64 titleId;
} *raw;
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 8;
raw->fsType = fsType;
raw->titleId = titleId;
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
serviceCreateSubservice(&out->s, s, &r, 0);
}
}
return rc;
}
Result fsOpenSaveDataFileSystemFwd(Service *s, FsFileSystem* out, u8 inval, FsSave *save) {
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
u64 inval;//Actually u8.
FsSave save;
} PACKED *raw;
raw = serviceIpcPrepareHeader(s, &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 51;
raw->inval = (u64)inval;
memcpy(&raw->save, save, sizeof(FsSave));
Result rc = serviceIpcDispatch(s);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(s, &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
serviceCreateSubservice(&out->s, s, &r, 0);
}
}
return rc;
}

View File

@@ -1,24 +0,0 @@
/**
* @file fs_shim.h
* @brief Filesystem Services (fs) IPC wrapper. To be merged into libnx, eventually.
* @author SciresM
* @copyright libnx Authors
*/
#pragma once
#include <switch.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Missing fsp-srv commands. */
Result fsOpenBisStorageFwd(Service* s, FsStorage* out, FsBisStorageId PartitionId);
Result fsOpenDataStorageByCurrentProcessFwd(Service* s, FsStorage* out);
Result fsOpenDataStorageByDataIdFwd(Service* s, FsStorageId storage_id, u64 data_id, FsStorage* out);
Result fsOpenFileSystemWithPatchFwd(Service* s, FsFileSystem* out, u64 titleId, FsFileSystemType fsType);
Result fsOpenFileSystemWithIdFwd(Service* s, FsFileSystem* out, u64 titleId, FsFileSystemType fsType, const char* contentPath);
Result fsOpenSaveDataFileSystemFwd(Service* s, FsFileSystem* out, u8 inval, FsSave *save);
#ifdef __cplusplus
}
#endif

View File

@@ -1,177 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstring>
#include <switch.h>
#include <stratosphere.hpp>
#include "../utils.hpp"
#include "fs_subdirectory_filesystem.hpp"
#include "fs_path_utils.hpp"
Result SubDirectoryFileSystem::Initialize(const char *bp) {
if (strnlen(bp, FS_MAX_PATH) >= FS_MAX_PATH) {
return ResultFsTooLongPath;
}
/* Normalize the path. */
char normal_path[FS_MAX_PATH + 1];
size_t normal_path_len;
R_ASSERT(FsPathUtils::Normalize(normal_path, sizeof(normal_path), bp, &normal_path_len));
/* Ensure terminating '/' */
if (normal_path[normal_path_len-1] != '/') {
AMS_ASSERT(normal_path_len + 2 <= sizeof(normal_path));
strncat(normal_path, "/", 2);
normal_path[sizeof(normal_path)-1] = 0;
normal_path_len++;
}
this->base_path_len = normal_path_len + 1;
this->base_path = reinterpret_cast<char *>(malloc(this->base_path_len));
if (this->base_path == nullptr) {
return ResultFsAllocationFailureInSubDirectoryFileSystem;
}
std::strncpy(this->base_path, normal_path, this->base_path_len);
this->base_path[this->base_path_len-1] = 0;
return ResultSuccess();
}
Result SubDirectoryFileSystem::GetFullPath(char *out, size_t out_size, const char *relative_path) {
if (this->base_path_len + strnlen(relative_path, FS_MAX_PATH) > out_size) {
return ResultFsTooLongPath;
}
/* Copy base path. */
std::strncpy(out, this->base_path, out_size);
out[out_size-1] = 0;
/* Normalize it. */
return FsPathUtils::Normalize(out + this->base_path_len - 2, out_size - (this->base_path_len - 2), relative_path, nullptr);
}
Result SubDirectoryFileSystem::CreateFileImpl(const FsPath &path, uint64_t size, int flags) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->CreateFile(full_path, size, flags);
}
Result SubDirectoryFileSystem::DeleteFileImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->DeleteFile(full_path);
}
Result SubDirectoryFileSystem::CreateDirectoryImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->CreateDirectory(full_path);
}
Result SubDirectoryFileSystem::DeleteDirectoryImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->DeleteDirectory(full_path);
}
Result SubDirectoryFileSystem::DeleteDirectoryRecursivelyImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->DeleteDirectoryRecursively(full_path);
}
Result SubDirectoryFileSystem::RenameFileImpl(const FsPath &old_path, const FsPath &new_path) {
FsPath full_old_path, full_new_path;
R_TRY(GetFullPath(full_old_path, old_path));
R_TRY(GetFullPath(full_new_path, new_path));
return this->base_fs->RenameFile(full_old_path, full_new_path);
}
Result SubDirectoryFileSystem::RenameDirectoryImpl(const FsPath &old_path, const FsPath &new_path) {
FsPath full_old_path, full_new_path;
R_TRY(GetFullPath(full_old_path, old_path));
R_TRY(GetFullPath(full_new_path, new_path));
return this->base_fs->RenameDirectory(full_old_path, full_new_path);
}
Result SubDirectoryFileSystem::GetEntryTypeImpl(DirectoryEntryType *out, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->GetEntryType(out, full_path);
}
Result SubDirectoryFileSystem::OpenFileImpl(std::unique_ptr<IFile> &out_file, const FsPath &path, OpenMode mode) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->OpenFile(out_file, full_path, mode);
}
Result SubDirectoryFileSystem::OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, const FsPath &path, DirectoryOpenMode mode) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->OpenDirectory(out_dir, full_path, mode);
}
Result SubDirectoryFileSystem::CommitImpl() {
return this->base_fs->Commit();
}
Result SubDirectoryFileSystem::GetFreeSpaceSizeImpl(uint64_t *out, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->GetFreeSpaceSize(out, full_path);
}
Result SubDirectoryFileSystem::GetTotalSpaceSizeImpl(uint64_t *out, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->GetTotalSpaceSize(out, full_path);
}
Result SubDirectoryFileSystem::CleanDirectoryRecursivelyImpl(const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->CleanDirectoryRecursively(full_path);
}
Result SubDirectoryFileSystem::GetFileTimeStampRawImpl(FsTimeStampRaw *out, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, 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, const FsPath &path) {
FsPath full_path;
R_TRY(GetFullPath(full_path, path));
return this->base_fs->QueryEntry(out, out_size, in, in_size, query, full_path);
}

View File

@@ -1,71 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "fs_ifilesystem.hpp"
#include "fs_path_utils.hpp"
class SubDirectoryFileSystem : public IFileSystem {
private:
std::shared_ptr<IFileSystem> base_fs;
char *base_path = nullptr;
size_t base_path_len = 0;
public:
SubDirectoryFileSystem(IFileSystem *fs, const char *bp) : base_fs(fs) {
R_ASSERT(this->Initialize(bp));
}
SubDirectoryFileSystem(std::shared_ptr<IFileSystem> fs, const char *bp) : base_fs(fs) {
R_ASSERT(this->Initialize(bp));
}
virtual ~SubDirectoryFileSystem() {
if (this->base_path != nullptr) {
free(this->base_path);
}
}
private:
Result Initialize(const char *bp);
protected:
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:
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

@@ -1,110 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include <cstring>
#include <stratosphere.hpp>
#include "fsmitm_boot0storage.hpp"
static ams::os::Mutex g_boot0_mutex;
static u8 g_boot0_bct_buffer[Boot0Storage::BctEndOffset];
bool Boot0Storage::CanModifyBctPubks() {
if (IsRcmBugPatched()) {
/* RCM bug patched. */
/* Only allow NS to update the BCT pubks. */
/* AutoRCM on a patched unit will cause a brick, so homebrew should NOT be allowed to write. */
return this->title_id == ams::ncm::TitleId::Ns;
} else {
/* RCM bug unpatched. */
/* Allow homebrew but not NS to update the BCT pubks. */
return this->title_id != ams::ncm::TitleId::Ns;
}
}
Result Boot0Storage::Read(void *_buffer, size_t size, u64 offset) {
std::scoped_lock lk{g_boot0_mutex};
return Base::Read(_buffer, size, offset);
}
Result Boot0Storage::Write(void *_buffer, size_t size, u64 offset) {
std::scoped_lock lk{g_boot0_mutex};
u8 *buffer = static_cast<u8 *>(_buffer);
/* Protect the keyblob region from writes. */
if (offset <= EksStart) {
if (offset + size < EksStart) {
/* Fall through, no need to do anything here. */
} else {
if (offset + size < EksEnd) {
/* Adjust size to avoid writing end of data. */
size = EksStart - offset;
} else {
/* Perform portion of write falling past end of keyblobs. */
const u64 diff = EksEnd - offset;
R_TRY(Base::Write(buffer + diff, size - diff, EksEnd));
/* Adjust size to avoid writing end of data. */
size = EksStart - offset;
}
}
} else {
if (offset < EksEnd) {
if (offset + size < EksEnd) {
/* Ignore writes falling strictly within the region. */
return ResultSuccess();
} else {
/* Only write past the end of the keyblob region. */
buffer = buffer + (EksEnd - offset);
size -= (EksEnd - offset);
offset = EksEnd;
}
} else {
/* Fall through, no need to do anything here. */
}
}
if (size == 0) {
return ResultSuccess();
}
/* We care about protecting autorcm from NS. */
if (CanModifyBctPubks() || offset >= BctEndOffset || (offset + BctSize >= BctEndOffset && offset % BctSize >= BctPubkEnd)) {
return Base::Write(buffer, size, offset);
}
/* First, let's deal with the data past the end. */
if (offset + size >= BctEndOffset) {
const u64 diff = BctEndOffset - offset;
R_TRY(ProxyStorage::Write(buffer + diff, size - diff, BctEndOffset));
size = diff;
}
/* Read in the current BCT region. */
R_TRY(ProxyStorage::Read(g_boot0_bct_buffer, BctEndOffset, 0));
/* Update the bct buffer. */
for (u64 cur_ofs = offset; cur_ofs < BctEndOffset && cur_ofs < offset + size; cur_ofs++) {
const u64 cur_bct_rel_ofs = cur_ofs % BctSize;
if (cur_bct_rel_ofs < BctPubkStart || BctPubkEnd <= cur_bct_rel_ofs) {
g_boot0_bct_buffer[cur_ofs] = buffer[cur_ofs - offset];
}
}
return ProxyStorage::Write(g_boot0_bct_buffer, BctEndOffset, 0);
}

View File

@@ -1,143 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <cstring>
#include <stratosphere.hpp>
#include "fs_istorage.hpp"
/* Represents a sectored storage. */
template<u64 SectorSize>
class SectoredProxyStorage : public ProxyStorage {
private:
u64 cur_seek = 0;
u64 cur_sector = 0;
u64 cur_sector_ofs = 0;
u8 sector_buf[SectorSize];
private:
void Seek(u64 offset) {
this->cur_sector_ofs = offset % SectorSize;
this->cur_seek = offset - this->cur_sector_ofs;
}
public:
SectoredProxyStorage(FsStorage *s) : ProxyStorage(s) { }
SectoredProxyStorage(FsStorage s) : ProxyStorage(s) { }
public:
virtual Result Read(void *_buffer, size_t size, u64 offset) override {
u8 *buffer = static_cast<u8 *>(_buffer);
this->Seek(offset);
if (this->cur_sector_ofs == 0 && size % SectorSize == 0) {
/* Fast case. */
return ProxyStorage::Read(buffer, size, offset);
}
R_TRY(ProxyStorage::Read(this->sector_buf, SectorSize, this->cur_seek));
if (size + this->cur_sector_ofs <= SectorSize) {
memcpy(buffer, sector_buf + this->cur_sector_ofs, size);
} else {
/* Leaving the sector... */
size_t ofs = SectorSize - this->cur_sector_ofs;
memcpy(buffer, sector_buf + this->cur_sector_ofs, ofs);
size -= ofs;
/* We're guaranteed alignment, here. */
const size_t aligned_remaining_size = size - (size % SectorSize);
if (aligned_remaining_size) {
R_TRY(ProxyStorage::Read(buffer + ofs, aligned_remaining_size, offset + ofs));
ofs += aligned_remaining_size;
size -= aligned_remaining_size;
}
/* Read any leftover data. */
if (size) {
R_TRY(ProxyStorage::Read(this->sector_buf, SectorSize, offset + ofs));
memcpy(buffer + ofs, sector_buf, size);
}
}
return ResultSuccess();
}
virtual Result Write(void *_buffer, size_t size, u64 offset) override {
u8 *buffer = static_cast<u8 *>(_buffer);
this->Seek(offset);
if (this->cur_sector_ofs == 0 && size % SectorSize == 0) {
/* Fast case. */
return ProxyStorage::Write(buffer, size, offset);
}
R_TRY(ProxyStorage::Read(this->sector_buf, SectorSize, this->cur_seek));
if (size + this->cur_sector_ofs <= SectorSize) {
memcpy(this->sector_buf + this->cur_sector_ofs, buffer, size);
R_TRY(ProxyStorage::Write(this->sector_buf, SectorSize, this->cur_seek));
} else {
/* Leaving the sector... */
size_t ofs = SectorSize - this->cur_sector_ofs;
memcpy(this->sector_buf + this->cur_sector_ofs, buffer, ofs);
R_TRY(ProxyStorage::Write(this->sector_buf, ofs, this->cur_seek));
size -= ofs;
/* We're guaranteed alignment, here. */
const size_t aligned_remaining_size = size - (size % SectorSize);
if (aligned_remaining_size) {
R_TRY(ProxyStorage::Write(buffer + ofs, aligned_remaining_size, offset + ofs));
ofs += aligned_remaining_size;
size -= aligned_remaining_size;
}
/* Write any leftover data. */
if (size) {
R_TRY(ProxyStorage::Read(this->sector_buf, SectorSize, offset + ofs));
memcpy(this->sector_buf, buffer + ofs, size);
R_TRY(ProxyStorage::Write(this->sector_buf, SectorSize, this->cur_seek));
}
}
return ResultSuccess();
}
};
/* Represents an RCM-preserving BOOT0 partition. */
class Boot0Storage : public SectoredProxyStorage<0x200> {
using Base = SectoredProxyStorage<0x200>;
public:
static constexpr u64 BctEndOffset = 0xFC000;
static constexpr u64 BctSize = 0x4000;
static constexpr u64 BctPubkStart = 0x210;
static constexpr u64 BctPubkSize = 0x100;
static constexpr u64 BctPubkEnd = BctPubkStart + BctPubkSize;
static constexpr u64 EksStart = 0x180000;
static constexpr u64 EksSize = 0x4000;
static constexpr u64 EksEnd = EksStart + EksSize;
private:
ams::ncm::TitleId title_id;
private:
bool CanModifyBctPubks();
public:
Boot0Storage(FsStorage *s, ams::ncm::TitleId t) : Base(s), title_id(t) { }
Boot0Storage(FsStorage s, ams::ncm::TitleId t) : Base(s), title_id(t) { }
public:
virtual Result Read(void *_buffer, size_t size, u64 offset) override;
virtual Result Write(void *_buffer, size_t size, u64 offset) override;
};

View File

@@ -1,151 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include <stratosphere.hpp>
#include "fsmitm_layeredrom.hpp"
#include "../utils.hpp"
#include "../debug.hpp"
IStorage::~IStorage() = default;
LayeredRomFS::LayeredRomFS(std::shared_ptr<IROStorage> s_r, std::shared_ptr<IROStorage> f_r, u64 tid) : storage_romfs(s_r), file_romfs(f_r), title_id(tid) {
/* Start building the new virtual romfs. */
RomFSBuildContext build_ctx(this->title_id);
this->p_source_infos = std::shared_ptr<std::vector<RomFSSourceInfo>>(new std::vector<RomFSSourceInfo>(), [](std::vector<RomFSSourceInfo> *to_delete) {
for (unsigned int i = 0; i < to_delete->size(); i++) {
(*to_delete)[i].Cleanup();
}
delete to_delete;
});
if (Utils::IsSdInitialized()) {
build_ctx.MergeSdFiles();
}
if (this->file_romfs) {
build_ctx.MergeRomStorage(this->file_romfs.get(), RomFSDataSource::FileRomFS);
}
if (this->storage_romfs) {
build_ctx.MergeRomStorage(this->storage_romfs.get(), RomFSDataSource::BaseRomFS);
}
build_ctx.Build(this->p_source_infos.get());
}
Result LayeredRomFS::Read(void *buffer, size_t size, u64 offset) {
/* Size zero reads should always succeed. */
if (size == 0) {
return ResultSuccess();
}
/* Validate size. */
u64 virt_size = (*this->p_source_infos)[this->p_source_infos->size() - 1].virtual_offset + (*this->p_source_infos)[this->p_source_infos->size() - 1].size;
if (offset >= virt_size) {
return ResultFsInvalidOffset;
}
if (virt_size - offset < size) {
size = virt_size - offset;
}
/* Find first source info via binary search. */
u32 cur_source_ind = 0;
u32 low = 0, high = this->p_source_infos->size() - 1;
while (low <= high) {
u32 mid = (low + high) / 2;
if ((*this->p_source_infos)[mid].virtual_offset > offset) {
/* Too high. */
high = mid - 1;
} else {
/* sources[mid].virtual_offset <= offset, invariant */
if (mid == this->p_source_infos->size() - 1 || (*this->p_source_infos)[mid + 1].virtual_offset > offset) {
/* Success */
cur_source_ind = mid;
break;
}
low = mid + 1;
}
}
size_t read_so_far = 0;
while (read_so_far < size) {
RomFSSourceInfo *cur_source = &((*this->p_source_infos)[cur_source_ind]);
if (cur_source->virtual_offset + cur_source->size > offset) {
u64 cur_read_size = size - read_so_far;
if (cur_read_size > cur_source->size - (offset - cur_source->virtual_offset)) {
cur_read_size = cur_source->size - (offset - cur_source->virtual_offset);
}
switch (cur_source->type) {
case RomFSDataSource::MetaData:
{
FsFile file;
R_ASSERT(Utils::OpenSdFileForAtmosphere(this->title_id, ROMFS_METADATA_FILE_PATH, FS_OPEN_READ, &file));
size_t out_read;
R_ASSERT(fsFileRead(&file, (offset - cur_source->virtual_offset), (void *)((uintptr_t)buffer + read_so_far), cur_read_size, FS_READOPTION_NONE, &out_read));
AMS_ASSERT(out_read == cur_read_size);
fsFileClose(&file);
}
break;
case RomFSDataSource::LooseFile:
{
FsFile file;
R_ASSERT(Utils::OpenRomFSSdFile(this->title_id, cur_source->loose_source_info.path, FS_OPEN_READ, &file));
size_t out_read;
R_ASSERT(fsFileRead(&file, (offset - cur_source->virtual_offset), (void *)((uintptr_t)buffer + read_so_far), cur_read_size, FS_READOPTION_NONE, &out_read));
AMS_ASSERT(out_read == cur_read_size);
fsFileClose(&file);
}
break;
case RomFSDataSource::Memory:
{
memcpy((void *)((uintptr_t)buffer + read_so_far), cur_source->memory_source_info.data + (offset - cur_source->virtual_offset), cur_read_size);
}
break;
case RomFSDataSource::BaseRomFS:
{
R_ASSERT(this->storage_romfs->Read((void *)((uintptr_t)buffer + read_so_far), cur_read_size, cur_source->base_source_info.offset + (offset - cur_source->virtual_offset)));
}
break;
case RomFSDataSource::FileRomFS:
{
R_ASSERT(this->file_romfs->Read((void *)((uintptr_t)buffer + read_so_far), cur_read_size, cur_source->base_source_info.offset + (offset - cur_source->virtual_offset)));
}
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
read_so_far += cur_read_size;
offset += cur_read_size;
} else {
/* Handle padding explicitly. */
cur_source_ind++;
/* Zero out the padding we skip, here. */
memset((void *)((uintptr_t)buffer + read_so_far), 0, ((*this->p_source_infos)[cur_source_ind]).virtual_offset - offset);
read_so_far += ((*this->p_source_infos)[cur_source_ind]).virtual_offset - offset;
offset = ((*this->p_source_infos)[cur_source_ind]).virtual_offset;
}
}
return ResultSuccess();
}
Result LayeredRomFS::GetSize(u64 *out_size) {
*out_size = (*this->p_source_infos)[this->p_source_infos->size() - 1].virtual_offset + (*this->p_source_infos)[this->p_source_infos->size() - 1].size;
return ResultSuccess();
}
Result LayeredRomFS::OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) {
/* TODO: How should I implement this for a virtual romfs? */
if (operation_type == FsOperationId_QueryRange) {
*out_range_info = {0};
}
return ResultSuccess();
}

View File

@@ -1,43 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "fs_istorage.hpp"
#include "fsmitm_romfsbuild.hpp"
#include "../utils.hpp"
/* Represents a merged RomFS. */
class LayeredRomFS : public IROStorage {
private:
/* Data Sources. */
std::shared_ptr<IROStorage> storage_romfs;
std::shared_ptr<IROStorage> file_romfs;
/* Information about the merged RomFS. */
u64 title_id;
std::shared_ptr<std::vector<RomFSSourceInfo>> p_source_infos;
public:
LayeredRomFS(std::shared_ptr<IROStorage> s_r, std::shared_ptr<IROStorage> f_r, u64 tid);
virtual ~LayeredRomFS() = default;
virtual Result Read(void *buffer, size_t size, u64 offset) override;
virtual Result GetSize(u64 *out_size) override;
virtual Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override;
};

View File

@@ -1,48 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdlib>
#include <cstdint>
#include <cstring>
#include <malloc.h>
#include <switch.h>
#include <atmosphere.h>
#include <stratosphere.hpp>
#include "fsmitm_main.hpp"
#include "fsmitm_service.hpp"
#include "../utils.hpp"
struct FsMitmManagerOptions {
static const size_t PointerBufferSize = 0x800;
static const size_t MaxDomains = 0x40;
static const size_t MaxDomainObjects = 0x4000;
};
using FsMitmManager = WaitableManager<FsMitmManagerOptions>;
void FsMitmMain(void *arg) {
/* Create server manager. */
static auto s_server_manager = FsMitmManager(5);
/* Create fsp-srv mitm. */
AddMitmServerToManager<FsMitmService>(&s_server_manager, "fsp-srv", 61);
/* Loop forever, servicing our services. */
s_server_manager.Process();
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "fsmitm_module.hpp"
#include "fs_mitm_service.hpp"
namespace ams::mitm::fs {
namespace {
constexpr sm::ServiceName MitmServiceName = sm::ServiceName::Encode("fsp-srv");
struct ServerOptions {
static constexpr size_t PointerBufferSize = 0x800;
static constexpr size_t MaxDomains = 0x40;
static constexpr size_t MaxDomainObjects = 0x4000;
};
constexpr size_t MaxServers = 1;
constexpr size_t MaxSessions = 61;
sf::hipc::ServerManager<MaxServers, ServerOptions, MaxSessions> g_server_manager;
}
void MitmModule::ThreadFunction(void *arg) {
/* Create fs mitm. */
R_ASSERT(g_server_manager.RegisterMitmServer<FsMitmService>(MitmServiceName));
/* Loop forever, servicing our services. */
g_server_manager.LoopProcess();
}
}

View File

@@ -13,17 +13,12 @@
* 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 <cstdlib>
#include <cstdint>
#include <cstring>
#include <malloc.h>
#include <switch.h>
#include <atmosphere.h>
#pragma once
#include <stratosphere.hpp>
#include "../amsmitm_module.hpp"
constexpr u32 FsMitmPriority = 43;
constexpr u32 FsMitmStackSize = 0x8000;
namespace ams::mitm::fs {
void FsMitmMain(void *arg);
DEFINE_MITM_MODULE_CLASS(0x8000, 43);
}

View File

@@ -1,408 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include <string.h>
#include <stratosphere.hpp>
#include "../utils.hpp"
#include "fsmitm_romfsbuild.hpp"
#include "../debug.hpp"
void RomFSBuildContext::VisitDirectory(FsFileSystem *filesys, RomFSBuildDirectoryContext *parent) {
FsDir dir;
std::vector<RomFSBuildDirectoryContext *> child_dirs;
/* Open the current parent directory. */
R_ASSERT(Utils::OpenRomFSDir(filesys, this->title_id, parent->path, &dir));
{
ON_SCOPE_EXIT { fsDirClose(&dir); };
u64 read_entries;
while (true) {
R_ASSERT(fsDirRead(&dir, 0, &read_entries, 1, &this->dir_entry));
if (read_entries != 1) {
break;
}
AMS_ASSERT(this->dir_entry.type == ENTRYTYPE_DIR || this->dir_entry.type == ENTRYTYPE_FILE);
if (this->dir_entry.type == ENTRYTYPE_DIR) {
RomFSBuildDirectoryContext *child = new RomFSBuildDirectoryContext({0});
/* Set child's path. */
child->cur_path_ofs = parent->path_len + 1;
child->path_len = child->cur_path_ofs + strlen(this->dir_entry.name);
child->path = new char[child->path_len + 1];
strcpy(child->path, parent->path);
if (child->path_len > FS_MAX_PATH - 1) {
fatalSimple(ResultFsTooLongPath);
}
strcat(child->path + parent->path_len, "/");
strcat(child->path + parent->path_len, this->dir_entry.name);
if (!this->AddDirectory(parent, child, NULL)) {
delete[] child->path;
delete child;
} else {
child_dirs.push_back(child);
}
} else /* if (this->dir_entry.type == ENTRYTYPE_FILE) */ {
RomFSBuildFileContext *child = new RomFSBuildFileContext({0});
/* Set child's path. */
child->cur_path_ofs = parent->path_len + 1;
child->path_len = child->cur_path_ofs + strlen(this->dir_entry.name);
child->path = new char[child->path_len + 1];
strcpy(child->path, parent->path);
if (child->path_len > FS_MAX_PATH - 1) {
fatalSimple(ResultFsTooLongPath);
}
strcat(child->path + parent->path_len, "/");
strcat(child->path + parent->path_len, this->dir_entry.name);
child->source = this->cur_source_type;
child->size = this->dir_entry.fileSize;
if (!this->AddFile(parent, child)) {
delete[] child->path;
delete child;
}
}
}
}
for (auto &child : child_dirs) {
this->VisitDirectory(filesys, child);
}
}
void RomFSBuildContext::MergeSdFiles() {
FsFileSystem sd_filesystem;
FsDir dir;
if (!Utils::IsSdInitialized()) {
return;
}
if (R_FAILED((Utils::OpenSdDirForAtmosphere(this->title_id, "/romfs", &dir)))) {
return;
}
fsDirClose(&dir);
if (R_FAILED(fsMountSdcard(&sd_filesystem))) {
return;
}
this->cur_source_type = RomFSDataSource::LooseFile;
this->VisitDirectory(&sd_filesystem, this->root);
fsFsClose(&sd_filesystem);
}
void RomFSBuildContext::VisitDirectory(RomFSBuildDirectoryContext *parent, u32 parent_offset, void *dir_table, size_t dir_table_size, void *file_table, size_t file_table_size) {
RomFSDirectoryEntry *parent_entry = romfs_get_direntry(dir_table, parent_offset);
if (parent_entry->file != ROMFS_ENTRY_EMPTY) {
RomFSFileEntry *cur_file = romfs_get_fentry(file_table, parent_entry->file);
while (cur_file != NULL) {
RomFSBuildFileContext *child = new RomFSBuildFileContext({0});
/* Set child's path. */
child->cur_path_ofs = parent->path_len + 1;
child->path_len = child->cur_path_ofs + cur_file->name_size;
child->path = new char[child->path_len + 1];
strcpy(child->path, parent->path);
if (child->path_len > FS_MAX_PATH - 1) {
fatalSimple(ResultFsTooLongPath);
}
strcat(child->path + parent->path_len, "/");
strncat(child->path + parent->path_len, cur_file->name, cur_file->name_size);
child->size = cur_file->size;
child->source = this->cur_source_type;
child->orig_offset = cur_file->offset;
if (!this->AddFile(parent, child)) {
delete[] child->path;
delete child;
}
if (cur_file->sibling == ROMFS_ENTRY_EMPTY) {
cur_file = NULL;
} else {
cur_file = romfs_get_fentry(file_table, cur_file->sibling);
}
}
}
if (parent_entry->child != ROMFS_ENTRY_EMPTY) {
RomFSDirectoryEntry *cur_child = romfs_get_direntry(dir_table, parent_entry->child);
u32 cur_child_offset = parent_entry->child;
while (cur_child != NULL) {
RomFSBuildDirectoryContext *child = new RomFSBuildDirectoryContext({0});
/* Set child's path. */
child->cur_path_ofs = parent->path_len + 1;
child->path_len = child->cur_path_ofs + cur_child->name_size;
child->path = new char[child->path_len + 1];
strcpy(child->path, parent->path);
if (child->path_len > FS_MAX_PATH - 1) {
fatalSimple(ResultFsTooLongPath);
}
strcat(child->path + parent->path_len, "/");
strncat(child->path + parent->path_len, cur_child->name, cur_child->name_size);
RomFSBuildDirectoryContext *real = NULL;
if (!this->AddDirectory(parent, child, &real)) {
delete[] child->path;
delete child;
}
if (real == NULL) {
/* TODO: Better error. */
fatalSimple(ResultKernelConnectionClosed);
}
this->VisitDirectory(real, cur_child_offset, dir_table, dir_table_size, file_table, file_table_size);
if (cur_child->sibling == ROMFS_ENTRY_EMPTY) {
cur_child = NULL;
} else {
cur_child_offset = cur_child->sibling;
cur_child = romfs_get_direntry(dir_table, cur_child->sibling);
}
}
}
}
void RomFSBuildContext::MergeRomStorage(IROStorage *storage, RomFSDataSource source) {
RomFSHeader header;
R_ASSERT(storage->Read(&header, sizeof(header), 0));
AMS_ASSERT(header.header_size == sizeof(header));
/* Read tables. */
auto dir_table = std::make_unique<u8[]>(header.dir_table_size);
auto file_table = std::make_unique<u8[]>(header.file_table_size);
R_ASSERT(storage->Read(dir_table.get(), header.dir_table_size, header.dir_table_ofs));
R_ASSERT(storage->Read(file_table.get(), header.file_table_size, header.file_table_ofs));
this->cur_source_type = source;
this->VisitDirectory(this->root, 0x0, dir_table.get(), (size_t)header.dir_table_size, file_table.get(), (size_t)header.file_table_size);
}
bool RomFSBuildContext::AddDirectory(RomFSBuildDirectoryContext *parent_dir_ctx, RomFSBuildDirectoryContext *dir_ctx, RomFSBuildDirectoryContext **out_dir_ctx) {
/* Check whether it's already in the known directories. */
auto existing = this->directories.find(dir_ctx->path);
if (existing != this->directories.end()) {
if (out_dir_ctx) {
*out_dir_ctx = existing->second;
}
return false;
}
/* Add a new directory. */
this->num_dirs++;
this->dir_table_size += sizeof(RomFSDirectoryEntry) + ((dir_ctx->path_len - dir_ctx->cur_path_ofs + 3) & ~3);
dir_ctx->parent = parent_dir_ctx;
this->directories.insert({dir_ctx->path, dir_ctx});
if (out_dir_ctx) {
*out_dir_ctx = dir_ctx;
}
return true;
}
bool RomFSBuildContext::AddFile(RomFSBuildDirectoryContext *parent_dir_ctx, RomFSBuildFileContext *file_ctx) {
/* Check whether it's already in the known files. */
auto existing = this->files.find(file_ctx->path);
if (existing != this->files.end()) {
return false;
}
/* Add a new file. */
this->num_files++;
this->file_table_size += sizeof(RomFSFileEntry) + ((file_ctx->path_len - file_ctx->cur_path_ofs + 3) & ~3);
file_ctx->parent = parent_dir_ctx;
this->files.insert({file_ctx->path, file_ctx});
return true;
}
void RomFSBuildContext::Build(std::vector<RomFSSourceInfo> *out_infos) {
RomFSBuildFileContext *cur_file;
RomFSBuildDirectoryContext *cur_dir;
u32 entry_offset;
u32 dir_hash_table_entry_count = romfs_get_hash_table_count(this->num_dirs);
u32 file_hash_table_entry_count = romfs_get_hash_table_count(this->num_files);
this->dir_hash_table_size = 4 * dir_hash_table_entry_count;
this->file_hash_table_size = 4 * file_hash_table_entry_count;
/* Assign metadata pointers */
auto *header = reinterpret_cast<RomFSHeader*>(std::malloc(sizeof(RomFSHeader)));
*header = {};
const size_t metadata_size = this->dir_hash_table_size + this->dir_table_size + this->file_hash_table_size + this->file_table_size;
u8 *metadata = reinterpret_cast<u8*>(std::malloc(metadata_size));
u32 *dir_hash_table = (u32 *)((uintptr_t)metadata);
RomFSDirectoryEntry *dir_table = (RomFSDirectoryEntry *)((uintptr_t)dir_hash_table + this->dir_hash_table_size);
u32 *file_hash_table = (u32 *)((uintptr_t)dir_table + this->dir_table_size);
RomFSFileEntry *file_table = (RomFSFileEntry *)((uintptr_t)file_hash_table + this->file_hash_table_size);
/* Clear out hash tables. */
for (u32 i = 0; i < dir_hash_table_entry_count; i++) {
dir_hash_table[i] = ROMFS_ENTRY_EMPTY;
}
for (u32 i = 0; i < file_hash_table_entry_count; i++) {
file_hash_table[i] = ROMFS_ENTRY_EMPTY;
}
out_infos->clear();
out_infos->emplace_back(0, sizeof(*header), header, RomFSDataSource::Memory);
/* Determine file offsets. */
entry_offset = 0;
RomFSBuildFileContext *prev_file = NULL;
for (const auto &it : this->files) {
cur_file = it.second;
this->file_partition_size = (this->file_partition_size + 0xFULL) & ~0xFULL;
/* Check for extra padding in the original romfs source and preserve it, to help ourselves later. */
if (prev_file && prev_file->source == cur_file->source && (prev_file->source == RomFSDataSource::BaseRomFS || prev_file->source == RomFSDataSource::FileRomFS)) {
u64 expected = (this->file_partition_size - prev_file->offset + prev_file->orig_offset);
if (expected != cur_file->orig_offset) {
if (expected > cur_file->orig_offset) {
/* This case should NEVER happen. */
/* TODO: Better error. */
fatalSimple(ResultKernelConnectionClosed);
}
this->file_partition_size += cur_file->orig_offset - expected;
}
}
cur_file->offset = this->file_partition_size;
this->file_partition_size += cur_file->size;
cur_file->entry_offset = entry_offset;
entry_offset += sizeof(RomFSFileEntry) + ((cur_file->path_len - cur_file->cur_path_ofs + 3) & ~3);
prev_file = cur_file;
}
/* Assign deferred parent/sibling ownership. */
for (auto it = this->files.rbegin(); it != this->files.rend(); it++) {
cur_file = it->second;
cur_file->sibling = cur_file->parent->file;
cur_file->parent->file = cur_file;
}
/* Determine directory offsets. */
entry_offset = 0;
for (const auto &it : this->directories) {
cur_dir = it.second;
cur_dir->entry_offset = entry_offset;
entry_offset += sizeof(RomFSDirectoryEntry) + ((cur_dir->path_len - cur_dir->cur_path_ofs + 3) & ~3);
}
/* Assign deferred parent/sibling ownership. */
for (auto it = this->directories.rbegin(); it->second != this->root; it++) {
cur_dir = it->second;
cur_dir->sibling = cur_dir->parent->child;
cur_dir->parent->child = cur_dir;
}
/* Populate file tables. */
for (const auto &it : this->files) {
cur_file = it.second;
RomFSFileEntry *cur_entry = romfs_get_fentry(file_table, cur_file->entry_offset);
cur_entry->parent = cur_file->parent->entry_offset;
cur_entry->sibling = (cur_file->sibling == NULL) ? ROMFS_ENTRY_EMPTY : cur_file->sibling->entry_offset;
cur_entry->offset = cur_file->offset;
cur_entry->size = cur_file->size;
u32 name_size = cur_file->path_len - cur_file->cur_path_ofs;
u32 hash = romfs_calc_path_hash(cur_file->parent->entry_offset, (unsigned char *)cur_file->path + cur_file->cur_path_ofs, 0, name_size);
cur_entry->hash = file_hash_table[hash % file_hash_table_entry_count];
file_hash_table[hash % file_hash_table_entry_count] = cur_file->entry_offset;
cur_entry->name_size = name_size;
memset(cur_entry->name, 0, (cur_entry->name_size + 3) & ~3);
memcpy(cur_entry->name, cur_file->path + cur_file->cur_path_ofs, name_size);
switch (cur_file->source) {
case RomFSDataSource::BaseRomFS:
case RomFSDataSource::FileRomFS:
/* Try to compact, if possible. */
if (out_infos->back().type == cur_file->source) {
out_infos->back().size = cur_file->offset + ROMFS_FILEPARTITION_OFS + cur_file->size - out_infos->back().virtual_offset;
} else {
out_infos->emplace_back(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->size, cur_file->orig_offset + ROMFS_FILEPARTITION_OFS, cur_file->source);
}
break;
case RomFSDataSource::LooseFile:
{
char *path = new char[cur_file->path_len + 1];
strcpy(path, cur_file->path);
out_infos->emplace_back(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->size, path, cur_file->source);
}
break;
default:
/* TODO: Better error. */
fatalSimple(ResultKernelConnectionClosed);
break;
}
}
/* Populate dir tables. */
for (const auto &it : this->directories) {
cur_dir = it.second;
RomFSDirectoryEntry *cur_entry = romfs_get_direntry(dir_table, cur_dir->entry_offset);
cur_entry->parent = cur_dir == this->root ? 0 : cur_dir->parent->entry_offset;
cur_entry->sibling = (cur_dir->sibling == NULL) ? ROMFS_ENTRY_EMPTY : cur_dir->sibling->entry_offset;
cur_entry->child = (cur_dir->child == NULL) ? ROMFS_ENTRY_EMPTY : cur_dir->child->entry_offset;
cur_entry->file = (cur_dir->file == NULL) ? ROMFS_ENTRY_EMPTY : cur_dir->file->entry_offset;
u32 name_size = cur_dir->path_len - cur_dir->cur_path_ofs;
u32 hash = romfs_calc_path_hash(cur_dir == this->root ? 0 : cur_dir->parent->entry_offset, (unsigned char *)cur_dir->path + cur_dir->cur_path_ofs, 0, name_size);
cur_entry->hash = dir_hash_table[hash % dir_hash_table_entry_count];
dir_hash_table[hash % dir_hash_table_entry_count] = cur_dir->entry_offset;
cur_entry->name_size = name_size;
memset(cur_entry->name, 0, (cur_entry->name_size + 3) & ~3);
memcpy(cur_entry->name, cur_dir->path + cur_dir->cur_path_ofs, name_size);
}
/* Delete directories. */
for (const auto &it : this->directories) {
cur_dir = it.second;
delete[] cur_dir->path;
delete cur_dir;
}
this->root = NULL;
this->directories.clear();
/* Delete files. */
for (const auto &it : this->files) {
cur_file = it.second;
delete[] cur_file->path;
delete cur_file;
}
this->files.clear();
/* Set header fields. */
header->header_size = sizeof(*header);
header->file_hash_table_size = this->file_hash_table_size;
header->file_table_size = this->file_table_size;
header->dir_hash_table_size = this->dir_hash_table_size;
header->dir_table_size = this->dir_table_size;
header->file_partition_ofs = ROMFS_FILEPARTITION_OFS;
header->dir_hash_table_ofs = (header->file_partition_ofs + this->file_partition_size + 3ULL) & ~3ULL;
header->dir_table_ofs = header->dir_hash_table_ofs + header->dir_hash_table_size;
header->file_hash_table_ofs = header->dir_table_ofs + header->dir_table_size;
header->file_table_ofs = header->file_hash_table_ofs + header->file_hash_table_size;
/* Try to save metadata to the SD card, to save on memory space. */
if (R_SUCCEEDED(Utils::SaveSdFileForAtmosphere(this->title_id, ROMFS_METADATA_FILE_PATH, metadata, metadata_size))) {
out_infos->emplace_back(header->dir_hash_table_ofs, metadata_size, RomFSDataSource::MetaData);
std::free(metadata);
} else {
out_infos->emplace_back(header->dir_hash_table_ofs, metadata_size, metadata, RomFSDataSource::Memory);
}
}

View File

@@ -1,288 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdlib>
#include <switch.h>
#include <map>
#include "../debug.hpp"
#include "fs_istorage.hpp"
#define ROMFS_ENTRY_EMPTY 0xFFFFFFFF
#define ROMFS_FILEPARTITION_OFS 0x200
#define ROMFS_METADATA_FILE_PATH "romfs_metadata.bin"
/* Types for RomFS Meta construction. */
enum class RomFSDataSource {
BaseRomFS,
FileRomFS,
LooseFile,
MetaData,
Memory,
};
struct RomFSBaseSourceInfo {
u64 offset;
};
struct RomFSFileSourceInfo {
u64 offset;
};
struct RomFSLooseSourceInfo {
const char *path;
};
struct RomFSMemorySourceInfo {
const u8 *data;
};
struct RomFSMetaDataSourceInfo {
};
struct RomFSSourceInfo {
u64 virtual_offset;
u64 size;
union {
RomFSBaseSourceInfo base_source_info;
RomFSFileSourceInfo file_source_info;
RomFSLooseSourceInfo loose_source_info;
RomFSMemorySourceInfo memory_source_info;
RomFSMetaDataSourceInfo metadata_source_info;
};
RomFSDataSource type;
RomFSSourceInfo(u64 v_o, u64 s, u64 offset, RomFSDataSource t) : virtual_offset(v_o), size(s), type(t) {
switch (this->type) {
case RomFSDataSource::BaseRomFS:
this->base_source_info.offset = offset;
break;
case RomFSDataSource::FileRomFS:
this->file_source_info.offset = offset;
break;
case RomFSDataSource::LooseFile:
case RomFSDataSource::MetaData:
case RomFSDataSource::Memory:
default:
/* TODO: Better error. */
fatalSimple(ResultKernelConnectionClosed);
break;
}
}
RomFSSourceInfo(u64 v_o, u64 s, const void *arg, RomFSDataSource t) : virtual_offset(v_o), size(s), type(t) {
switch (this->type) {
case RomFSDataSource::LooseFile:
this->loose_source_info.path = (decltype(this->loose_source_info.path))arg;
break;
case RomFSDataSource::Memory:
this->memory_source_info.data = (decltype(this->memory_source_info.data))arg;
break;
case RomFSDataSource::MetaData:
case RomFSDataSource::BaseRomFS:
case RomFSDataSource::FileRomFS:
default:
/* TODO: Better error. */
fatalSimple(ResultKernelConnectionClosed);
break;
}
}
RomFSSourceInfo(u64 v_o, u64 s, RomFSDataSource t) : virtual_offset(v_o), size(s), type(t) {
switch (this->type) {
case RomFSDataSource::MetaData:
break;
case RomFSDataSource::LooseFile:
case RomFSDataSource::Memory:
case RomFSDataSource::BaseRomFS:
case RomFSDataSource::FileRomFS:
default:
/* TODO: Better error. */
fatalSimple(ResultKernelConnectionClosed);
break;
}
}
void Cleanup() {
switch (this->type) {
case RomFSDataSource::BaseRomFS:
case RomFSDataSource::FileRomFS:
case RomFSDataSource::MetaData:
break;
case RomFSDataSource::LooseFile:
delete[] this->loose_source_info.path;
break;
case RomFSDataSource::Memory:
std::free((void*)this->memory_source_info.data);
break;
default:
/* TODO: Better error. */
fatalSimple(ResultKernelConnectionClosed);
break;
}
}
static bool Compare(RomFSSourceInfo *a, RomFSSourceInfo *b) {
return (a->virtual_offset < b->virtual_offset);
}
};
/* Types for building a RomFS. */
struct RomFSHeader {
u64 header_size;
u64 dir_hash_table_ofs;
u64 dir_hash_table_size;
u64 dir_table_ofs;
u64 dir_table_size;
u64 file_hash_table_ofs;
u64 file_hash_table_size;
u64 file_table_ofs;
u64 file_table_size;
u64 file_partition_ofs;
};
static_assert(sizeof(RomFSHeader) == 0x50, "Incorrect RomFS Header definition!");
struct RomFSDirectoryEntry {
u32 parent;
u32 sibling;
u32 child;
u32 file;
u32 hash;
u32 name_size;
char name[];
};
static_assert(sizeof(RomFSDirectoryEntry) == 0x18, "Incorrect RomFSDirectoryEntry definition!");
struct RomFSFileEntry {
u32 parent;
u32 sibling;
u64 offset;
u64 size;
u32 hash;
u32 name_size;
char name[];
};
static_assert(sizeof(RomFSFileEntry) == 0x20, "Incorrect RomFSFileEntry definition!");
struct RomFSBuildFileContext;
/* Used as comparator for std::map<char *, RomFSBuild*Context> */
struct build_ctx_cmp {
bool operator()(const char *a, const char *b) const {
return strcmp(a, b) < 0;
}
};
struct RomFSBuildDirectoryContext {
char *path;
u32 cur_path_ofs;
u32 path_len;
u32 entry_offset = 0;
RomFSBuildDirectoryContext *parent = NULL;
RomFSBuildDirectoryContext *child = NULL;
RomFSBuildDirectoryContext *sibling = NULL;
RomFSBuildFileContext *file = NULL;
};
struct RomFSBuildFileContext {
char *path;
u32 cur_path_ofs;
u32 path_len;
u32 entry_offset = 0;
u64 offset = 0;
u64 size = 0;
RomFSBuildDirectoryContext *parent = NULL;
RomFSBuildFileContext *sibling = NULL;
RomFSDataSource source{0};
u64 orig_offset = 0;
};
class RomFSBuildContext {
private:
u64 title_id;
RomFSBuildDirectoryContext *root;
std::map<char *, RomFSBuildDirectoryContext *, build_ctx_cmp> directories;
std::map<char *, RomFSBuildFileContext *, build_ctx_cmp> files;
u64 num_dirs = 0;
u64 num_files = 0;
u64 dir_table_size = 0;
u64 file_table_size = 0;
u64 dir_hash_table_size = 0;
u64 file_hash_table_size = 0;
u64 file_partition_size = 0;
FsDirectoryEntry dir_entry;
RomFSDataSource cur_source_type;
void VisitDirectory(FsFileSystem *filesys, RomFSBuildDirectoryContext *parent);
void VisitDirectory(RomFSBuildDirectoryContext *parent, u32 parent_offset, void *dir_table, size_t dir_table_size, void *file_table, size_t file_table_size);
bool AddDirectory(RomFSBuildDirectoryContext *parent_dir_ctx, RomFSBuildDirectoryContext *dir_ctx, RomFSBuildDirectoryContext **out_dir_ctx);
bool AddFile(RomFSBuildDirectoryContext *parent_dir_ctx, RomFSBuildFileContext *file_ctx);
public:
RomFSBuildContext(u64 tid) : title_id(tid) {
this->root = new RomFSBuildDirectoryContext({0});
this->root->path = new char[1];
this->root->path[0] = '\x00';
this->directories.insert({this->root->path, this->root});
this->num_dirs = 1;
this->dir_table_size = 0x18;
}
void MergeSdFiles();
void MergeRomStorage(IROStorage *storage, RomFSDataSource source);
/* This finalizes the context. */
void Build(std::vector<RomFSSourceInfo> *out_infos);
};
static inline RomFSDirectoryEntry *romfs_get_direntry(void *directories, uint32_t offset) {
return (RomFSDirectoryEntry *)((uintptr_t)directories + offset);
}
static inline RomFSFileEntry *romfs_get_fentry(void *files, uint32_t offset) {
return (RomFSFileEntry *)((uintptr_t)files + offset);
}
static inline uint32_t romfs_calc_path_hash(uint32_t parent, const unsigned char *path, uint32_t start, size_t path_len) {
uint32_t hash = parent ^ 123456789;
for (uint32_t i = 0; i < path_len; i++) {
hash = (hash >> 5) | (hash << 27);
hash ^= path[start + i];
}
return hash;
}
static inline uint32_t romfs_get_hash_table_count(uint32_t num_entries) {
if (num_entries < 3) {
return 3;
} else if (num_entries < 19) {
return num_entries | 1;
}
uint32_t count = num_entries;
while (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 || count % 11 == 0 || count % 13 == 0 || count % 17 == 0) {
count++;
}
return count;
}

View File

@@ -1,57 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "fs_istorage.hpp"
/* Represents a RomFS stored in some file. */
class RomFileStorage : public IROStorage {
private:
FsFile *base_file;
public:
RomFileStorage(FsFile *f) : base_file(f) {
/* ... */
};
RomFileStorage(FsFile f) {
this->base_file = new FsFile(f);
};
virtual ~RomFileStorage() {
fsFileClose(base_file);
delete base_file;
};
public:
Result Read(void *buffer, size_t size, u64 offset) override {
size_t out_sz = 0;
R_TRY(fsFileRead(this->base_file, offset, buffer, size, FS_READOPTION_NONE, &out_sz));
if (out_sz != size && out_sz) {
R_TRY(this->Read((void *)((uintptr_t)buffer + out_sz), size - out_sz, offset + out_sz));
}
return ResultSuccess();
};
Result GetSize(u64 *out_size) override {
return fsFileGetSize(this->base_file, out_size);
};
Result OperateRange(FsOperationId operation_type, u64 offset, u64 size, FsRangeInfo *out_range_info) override {
/* TODO: Merge into libnx? */
return fsFileOperateRange(this->base_file, operation_type, offset, size, out_range_info);
};
};
/* Represents a RomFS accessed via some IStorage. */
using RomInterfaceStorage = ROProxyStorage;

View File

@@ -1,386 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <map>
#include <memory>
#include <mutex>
#include <switch.h>
#include <stratosphere.hpp>
#include "fsmitm_service.hpp"
#include "fs_shim.h"
#include "../utils.hpp"
#include "fsmitm_boot0storage.hpp"
#include "fsmitm_layeredrom.hpp"
#include "fs_dir_utils.hpp"
#include "fs_save_utils.hpp"
#include "fs_file_storage.hpp"
#include "fs_subdirectory_filesystem.hpp"
#include "fs_directory_redirection_filesystem.hpp"
#include "fs_directory_savedata_filesystem.hpp"
#include "../debug.hpp"
static ams::os::Mutex g_StorageCacheLock;
static std::unordered_map<u64, std::weak_ptr<IStorageInterface>> g_StorageCache;
static bool StorageCacheGetEntry(ams::ncm::TitleId title_id, std::shared_ptr<IStorageInterface> *out) {
std::scoped_lock lock(g_StorageCacheLock);
if (g_StorageCache.find(static_cast<u64>(title_id)) == g_StorageCache.end()) {
return false;
}
auto intf = g_StorageCache[static_cast<u64>(title_id)].lock();
if (intf != nullptr) {
*out = intf;
return true;
}
return false;
}
static void StorageCacheSetEntry(ams::ncm::TitleId title_id, std::shared_ptr<IStorageInterface> *ptr) {
std::scoped_lock lock(g_StorageCacheLock);
/* Ensure we always use the cached copy if present. */
if (g_StorageCache.find(static_cast<u64>(title_id)) != g_StorageCache.end()) {
auto intf = g_StorageCache[static_cast<u64>(title_id)].lock();
if (intf != nullptr) {
*ptr = intf;
}
}
g_StorageCache[static_cast<u64>(title_id)] = *ptr;
}
void FsMitmService::PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx) {
auto this_ptr = static_cast<FsMitmService *>(obj);
switch (static_cast<CommandId>(ctx->cmd_id)) {
case CommandId::SetCurrentProcess:
if (R_SUCCEEDED(ctx->rc)) {
this_ptr->has_initialized = true;
}
break;
default:
break;
}
}
Result FsMitmService::OpenHblWebContentFileSystem(Out<std::shared_ptr<IFileSystemInterface>> &out_fs) {
/* Mount the SD card using fs.mitm's session. */
FsFileSystem sd_fs;
R_TRY(fsMountSdcard(&sd_fs));
/* Set output filesystem. */
std::unique_ptr<IFileSystem> web_ifs = std::make_unique<SubDirectoryFileSystem>(std::make_shared<ProxyFileSystem>(sd_fs), AtmosphereHblWebContentDir);
out_fs.SetValue(std::make_shared<IFileSystemInterface>(std::move(web_ifs)));
if (out_fs.IsDomain()) {
out_fs.ChangeObjectId(sd_fs.s.object_id);
}
return ResultSuccess();
}
Result FsMitmService::OpenFileSystemWithPatch(Out<std::shared_ptr<IFileSystemInterface>> out_fs, u64 title_id, u32 filesystem_type) {
/* Check for eligibility. */
{
FsDir d;
if (!Utils::IsWebAppletTid(static_cast<u64>(this->title_id)) || filesystem_type != FsFileSystemType_ContentManual || !Utils::IsHblTid(title_id) ||
R_FAILED(Utils::OpenSdDir(AtmosphereHblWebContentDir, &d))) {
return ResultAtmosphereMitmShouldForwardToSession;
}
fsDirClose(&d);
}
/* If there's an existing filesystem, don't override. */
/* TODO: Multiplex, overriding existing content with HBL content. */
{
FsFileSystem fs;
if (R_SUCCEEDED(fsOpenFileSystemWithPatchFwd(this->forward_service.get(), &fs, title_id, static_cast<FsFileSystemType>(filesystem_type)))) {
fsFsClose(&fs);
return ResultAtmosphereMitmShouldForwardToSession;
}
}
return this->OpenHblWebContentFileSystem(out_fs);
}
Result FsMitmService::OpenFileSystemWithId(Out<std::shared_ptr<IFileSystemInterface>> out_fs, InPointer<char> path, u64 title_id, u32 filesystem_type) {
/* Check for eligibility. */
{
FsDir d;
if (!Utils::IsWebAppletTid(static_cast<u64>(this->title_id)) || filesystem_type != FsFileSystemType_ContentManual || !Utils::IsHblTid(title_id) ||
R_FAILED(Utils::OpenSdDir(AtmosphereHblWebContentDir, &d))) {
return ResultAtmosphereMitmShouldForwardToSession;
}
fsDirClose(&d);
}
/* If there's an existing filesystem, don't override. */
/* TODO: Multiplex, overriding existing content with HBL content. */
{
FsFileSystem fs;
if (R_SUCCEEDED(fsOpenFileSystemWithIdFwd(this->forward_service.get(), &fs, title_id, static_cast<FsFileSystemType>(filesystem_type), path.pointer))) {
fsFsClose(&fs);
return ResultAtmosphereMitmShouldForwardToSession;
}
}
return this->OpenHblWebContentFileSystem(out_fs);
}
Result FsMitmService::OpenSdCardFileSystem(Out<std::shared_ptr<IFileSystemInterface>> out_fs) {
/* We only care about redirecting this for NS/Emummc. */
if (this->title_id != ams::ncm::TitleId::Ns) {
return ResultAtmosphereMitmShouldForwardToSession;
}
if (!IsEmummc()) {
return ResultAtmosphereMitmShouldForwardToSession;
}
/* Mount the SD card. */
FsFileSystem sd_fs;
R_TRY(fsMountSdcard(&sd_fs));
/* Set output filesystem. */
std::unique_ptr<IFileSystem> redir_fs = std::make_unique<DirectoryRedirectionFileSystem>(new ProxyFileSystem(sd_fs), "/Nintendo", GetEmummcNintendoDirPath());
out_fs.SetValue(std::make_shared<IFileSystemInterface>(std::move(redir_fs)));
if (out_fs.IsDomain()) {
out_fs.ChangeObjectId(sd_fs.s.object_id);
}
return ResultSuccess();
}
Result FsMitmService::OpenSaveDataFileSystem(Out<std::shared_ptr<IFileSystemInterface>> out_fs, u8 space_id, FsSave save_struct) {
bool should_redirect_saves = false;
const bool has_redirect_save_flags = Utils::HasFlag(static_cast<u64>(this->title_id), "redirect_save");
if (R_FAILED(Utils::GetSettingsItemBooleanValue("atmosphere", "fsmitm_redirect_saves_to_sd", &should_redirect_saves))) {
return ResultAtmosphereMitmShouldForwardToSession;
}
/* For now, until we're sure this is robust, only intercept normal savedata , check if flag exist*/
if (!has_redirect_save_flags || !should_redirect_saves || save_struct.saveDataType != FsSaveDataType_SaveData) {
return ResultAtmosphereMitmShouldForwardToSession;
}
/* Verify we can open the save. */
FsFileSystem save_fs;
if (R_FAILED(fsOpenSaveDataFileSystemFwd(this->forward_service.get(), &save_fs, space_id, &save_struct))) {
return ResultAtmosphereMitmShouldForwardToSession;
}
std::unique_ptr<IFileSystem> save_ifs = std::make_unique<ProxyFileSystem>(save_fs);
{
/* Mount the SD card using fs.mitm's session. */
FsFileSystem sd_fs;
R_TRY(fsMountSdcard(&sd_fs));
std::shared_ptr<IFileSystem> sd_ifs = std::make_shared<ProxyFileSystem>(sd_fs);
/* Verify that we can open the save directory, and that it exists. */
const u64 target_tid = save_struct.titleID == 0 ? static_cast<u64>(this->title_id) : save_struct.titleID;
FsPath save_dir_path;
R_TRY(FsSaveUtils::GetSaveDataDirectoryPath(save_dir_path, space_id, save_struct.saveDataType, target_tid, save_struct.userID, save_struct.saveID));
/* Check if this is the first time we're making the save. */
bool is_new_save = false;
{
DirectoryEntryType ent;
if (sd_ifs->GetEntryType(&ent, save_dir_path) == ResultFsPathNotFound) {
is_new_save = true;
}
}
/* Ensure the directory exists. */
R_TRY(FsDirUtils::EnsureDirectoryExists(sd_ifs.get(), save_dir_path));
std::shared_ptr<DirectorySaveDataFileSystem> dirsave_ifs = std::make_shared<DirectorySaveDataFileSystem>(new SubDirectoryFileSystem(sd_ifs, save_dir_path.str), std::move(save_ifs));
/* If it's the first time we're making the save, copy existing savedata over. */
if (is_new_save) {
/* TODO: Check error? */
dirsave_ifs->CopySaveFromProxy();
}
out_fs.SetValue(std::make_shared<IFileSystemInterface>(static_cast<std::shared_ptr<IFileSystem>>(dirsave_ifs)));
if (out_fs.IsDomain()) {
out_fs.ChangeObjectId(sd_fs.s.object_id);
}
return ResultSuccess();
}
}
/* Gate access to the BIS partitions. */
Result FsMitmService::OpenBisStorage(Out<std::shared_ptr<IStorageInterface>> out_storage, u32 _bis_partition_id) {
const FsBisStorageId bis_partition_id = static_cast<FsBisStorageId>(_bis_partition_id);
/* Try to open a storage for the partition. */
FsStorage bis_storage;
R_TRY(fsOpenBisStorageFwd(this->forward_service.get(), &bis_storage, bis_partition_id));
const bool is_sysmodule = ams::ncm::IsSystemTitleId(this->title_id);
const bool has_bis_write_flag = Utils::HasFlag(static_cast<u64>(this->title_id), "bis_write");
const bool has_cal0_read_flag = Utils::HasFlag(static_cast<u64>(this->title_id), "cal_read");
/* Set output storage. */
if (bis_partition_id == FsBisStorageId_Boot0) {
out_storage.SetValue(std::make_shared<IStorageInterface>(new Boot0Storage(bis_storage, this->title_id)));
} else if (bis_partition_id == FsBisStorageId_CalibrationBinary) {
/* PRODINFO should *never* be writable. */
if (is_sysmodule || has_cal0_read_flag) {
out_storage.SetValue(std::make_shared<IStorageInterface>(new ReadOnlyStorageAdapter(new ProxyStorage(bis_storage))));
} else {
/* Do not allow non-sysmodules to read *or* write CAL0. */
fsStorageClose(&bis_storage);
return ResultFsPermissionDenied;
}
} else {
if (is_sysmodule || has_bis_write_flag) {
/* Sysmodules should still be allowed to read and write. */
out_storage.SetValue(std::make_shared<IStorageInterface>(new ProxyStorage(bis_storage)));
} else if (Utils::IsHblTid(static_cast<u64>(this->title_id)) &&
((FsBisStorageId_BootConfigAndPackage2NormalMain <= bis_partition_id && bis_partition_id <= FsBisStorageId_BootConfigAndPackage2RepairSub) ||
bis_partition_id == FsBisStorageId_Boot1)) {
/* Allow HBL to write to boot1 (safe firm) + package2. */
/* This is needed to not break compatibility with ChoiDujourNX, which does not check for write access before beginning an update. */
/* TODO: get fixed so that this can be turned off without causing bricks :/ */
out_storage.SetValue(std::make_shared<IStorageInterface>(new ProxyStorage(bis_storage)));
} else {
/* Non-sysmodules should be allowed to read. */
out_storage.SetValue(std::make_shared<IStorageInterface>(new ReadOnlyStorageAdapter(new ProxyStorage(bis_storage))));
}
}
/* Copy domain id. */
if (out_storage.IsDomain()) {
out_storage.ChangeObjectId(bis_storage.s.object_id);
}
return ResultSuccess();
}
/* Add redirection for RomFS to the SD card. */
Result FsMitmService::OpenDataStorageByCurrentProcess(Out<std::shared_ptr<IStorageInterface>> out_storage) {
if (!this->should_override_contents) {
return ResultAtmosphereMitmShouldForwardToSession;
}
/* If we don't have anything to modify, there's no sense in maintaining a copy of the metadata tables. */
if (!Utils::HasSdRomfsContent(static_cast<u64>(this->title_id))) {
return ResultAtmosphereMitmShouldForwardToSession;
}
/* Try to get from the cache. */
{
std::shared_ptr<IStorageInterface> cached_storage = nullptr;
bool has_cache = StorageCacheGetEntry(this->title_id, &cached_storage);
if (has_cache) {
if (out_storage.IsDomain()) {
/* TODO: Don't leak object id? */
FsStorage s = {0};
R_TRY(fsOpenDataStorageByCurrentProcessFwd(this->forward_service.get(), &s));
out_storage.ChangeObjectId(s.s.object_id);
}
out_storage.SetValue(std::move(cached_storage));
return ResultSuccess();
}
}
/* Try to open process romfs. */
FsStorage data_storage;
R_TRY(fsOpenDataStorageByCurrentProcessFwd(this->forward_service.get(), &data_storage));
/* Make new layered romfs, cacheing to storage. */
{
std::shared_ptr<IStorageInterface> storage_to_cache = nullptr;
/* TODO: Is there a sensible path that ends in ".romfs" we can use?" */
FsFile data_file;
if (R_SUCCEEDED(Utils::OpenSdFileForAtmosphere(static_cast<u64>(this->title_id), "romfs.bin", FS_OPEN_READ, &data_file))) {
storage_to_cache = std::make_shared<IStorageInterface>(new LayeredRomFS(std::make_shared<ReadOnlyStorageAdapter>(new ProxyStorage(data_storage)), std::make_shared<ReadOnlyStorageAdapter>(new FileStorage(new ProxyFile(data_file))), static_cast<u64>(this->title_id)));
} else {
storage_to_cache = std::make_shared<IStorageInterface>(new LayeredRomFS(std::make_shared<ReadOnlyStorageAdapter>(new ProxyStorage(data_storage)), nullptr, static_cast<u64>(this->title_id)));
}
StorageCacheSetEntry(this->title_id, &storage_to_cache);
out_storage.SetValue(std::move(storage_to_cache));
if (out_storage.IsDomain()) {
out_storage.ChangeObjectId(data_storage.s.object_id);
}
}
return ResultSuccess();
}
/* Add redirection for System Data Archives to the SD card. */
Result FsMitmService::OpenDataStorageByDataId(Out<std::shared_ptr<IStorageInterface>> out_storage, u64 data_id, u8 sid) {
if (!this->should_override_contents) {
return ResultAtmosphereMitmShouldForwardToSession;
}
/* If we don't have anything to modify, there's no sense in maintaining a copy of the metadata tables. */
if (!Utils::HasSdRomfsContent(data_id)) {
return ResultAtmosphereMitmShouldForwardToSession;
}
FsStorageId storage_id = static_cast<FsStorageId>(sid);
/* Try to get from the cache. */
{
std::shared_ptr<IStorageInterface> cached_storage = nullptr;
bool has_cache = StorageCacheGetEntry(ams::ncm::TitleId{data_id}, &cached_storage);
if (has_cache) {
if (out_storage.IsDomain()) {
/* TODO: Don't leak object id? */
FsStorage s = {0};
R_TRY(fsOpenDataStorageByDataIdFwd(this->forward_service.get(), storage_id, data_id, &s));
out_storage.ChangeObjectId(s.s.object_id);
}
out_storage.SetValue(std::move(cached_storage));
return ResultSuccess();
}
}
/* Try to open data storage. */
FsStorage data_storage;
R_TRY(fsOpenDataStorageByDataIdFwd(this->forward_service.get(), storage_id, data_id, &data_storage));
/* Make new layered romfs, cacheing to storage. */
{
std::shared_ptr<IStorageInterface> storage_to_cache = nullptr;
/* TODO: Is there a sensible path that ends in ".romfs" we can use?" */
FsFile data_file;
if (R_SUCCEEDED(Utils::OpenSdFileForAtmosphere(data_id, "romfs.bin", FS_OPEN_READ, &data_file))) {
storage_to_cache = std::make_shared<IStorageInterface>(new LayeredRomFS(std::make_shared<ReadOnlyStorageAdapter>(new ProxyStorage(data_storage)), std::make_shared<ReadOnlyStorageAdapter>(new FileStorage(new ProxyFile(data_file))), data_id));
} else {
storage_to_cache = std::make_shared<IStorageInterface>(new LayeredRomFS(std::make_shared<ReadOnlyStorageAdapter>(new ProxyStorage(data_storage)), nullptr, data_id));
}
StorageCacheSetEntry(ams::ncm::TitleId{data_id}, &storage_to_cache);
out_storage.SetValue(std::move(storage_to_cache));
if (out_storage.IsDomain()) {
out_storage.ChangeObjectId(data_storage.s.object_id);
}
}
return ResultSuccess();
}

View File

@@ -1,95 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "fs_istorage.hpp"
#include "fs_ifilesystem.hpp"
#include "../utils.hpp"
class FsMitmService : public IMitmServiceObject {
private:
enum class CommandId {
OpenFileSystemDeprecated = 0,
SetCurrentProcess = 1,
OpenFileSystemWithPatch = 7,
OpenFileSystemWithId = 8,
OpenSdCardFileSystem = 18,
OpenSaveDataFileSystem = 51,
OpenBisStorage = 12,
OpenDataStorageByCurrentProcess = 200,
OpenDataStorageByDataId = 202,
};
private:
static constexpr const char *AtmosphereHblWebContentDir = "/atmosphere/hbl_html";
private:
bool has_initialized = false;
bool should_override_contents;
public:
FsMitmService(std::shared_ptr<Service> s, u64 pid, ams::ncm::TitleId tid) : IMitmServiceObject(s, pid, tid) {
if (Utils::HasSdDisableMitMFlag(static_cast<u64>(this->title_id))) {
this->should_override_contents = false;
} else {
this->should_override_contents = (this->title_id >= ams::ncm::TitleId::ApplicationStart || Utils::HasSdMitMFlag(static_cast<u64>(this->title_id))) && Utils::HasOverrideButton(static_cast<u64>(this->title_id));
}
}
static bool ShouldMitm(u64 pid, ams::ncm::TitleId tid) {
/* Don't Mitm KIPs */
if (pid < 0x50) {
return false;
}
static std::atomic_bool has_launched_qlaunch = false;
/* TODO: intercepting everything seems to cause issues with sleep mode, for some reason. */
/* Figure out why, and address it. */
if (tid == ams::ncm::TitleId::AppletQlaunch || tid == ams::ncm::TitleId::AppletMaintenanceMenu) {
has_launched_qlaunch = true;
}
return has_launched_qlaunch || tid == ams::ncm::TitleId::Ns || tid >= ams::ncm::TitleId::ApplicationStart || Utils::HasSdMitMFlag(static_cast<u64>(tid));
}
static void PostProcess(IMitmServiceObject *obj, IpcResponseContext *ctx);
private:
Result OpenHblWebContentFileSystem(Out<std::shared_ptr<IFileSystemInterface>> &out);
protected:
/* Overridden commands. */
Result OpenFileSystemWithPatch(Out<std::shared_ptr<IFileSystemInterface>> out, u64 title_id, u32 filesystem_type);
Result OpenFileSystemWithId(Out<std::shared_ptr<IFileSystemInterface>> out, InPointer<char> path, u64 title_id, u32 filesystem_type);
Result OpenSdCardFileSystem(Out<std::shared_ptr<IFileSystemInterface>> out);
Result OpenSaveDataFileSystem(Out<std::shared_ptr<IFileSystemInterface>> out, u8 space_id, FsSave save_struct);
Result OpenBisStorage(Out<std::shared_ptr<IStorageInterface>> out, u32 bis_partition_id);
Result OpenDataStorageByCurrentProcess(Out<std::shared_ptr<IStorageInterface>> out);
Result OpenDataStorageByDataId(Out<std::shared_ptr<IStorageInterface>> out, u64 data_id, u8 storage_id);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
/* TODO MAKE_SERVICE_COMMAND_META(FsMitmService, OpenFileSystemDeprecated), */
MAKE_SERVICE_COMMAND_META(FsMitmService, OpenFileSystemWithPatch, FirmwareVersion_200),
MAKE_SERVICE_COMMAND_META(FsMitmService, OpenFileSystemWithId, FirmwareVersion_200),
MAKE_SERVICE_COMMAND_META(FsMitmService, OpenSdCardFileSystem),
MAKE_SERVICE_COMMAND_META(FsMitmService, OpenSaveDataFileSystem),
MAKE_SERVICE_COMMAND_META(FsMitmService, OpenBisStorage),
MAKE_SERVICE_COMMAND_META(FsMitmService, OpenDataStorageByCurrentProcess),
MAKE_SERVICE_COMMAND_META(FsMitmService, OpenDataStorageByDataId),
};
};