mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 12:21:18 +00:00
fs.mitm: Implement SubDirectoryFileSystem
This commit is contained in:
parent
afcaf20020
commit
b62014554c
6 changed files with 438 additions and 7 deletions
|
@ -71,3 +71,98 @@ Result FsPathUtils::ConvertPathForServiceObject(FsPath *out, const char *path) {
|
|||
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 0;
|
||||
} else if (c == '.') {
|
||||
state = PathState::CurrentDir;
|
||||
} else {
|
||||
state = PathState::Normal;
|
||||
}
|
||||
break;
|
||||
case PathState::CurrentDir:
|
||||
if (c == '/') {
|
||||
*out = false;
|
||||
return 0;
|
||||
} else if (c == '.') {
|
||||
state = PathState::ParentDir;
|
||||
} else {
|
||||
state = PathState::Normal;
|
||||
}
|
||||
break;
|
||||
case PathState::ParentDir:
|
||||
if (c == '/') {
|
||||
*out = false;
|
||||
return 0;
|
||||
} else {
|
||||
state = PathState::Normal;
|
||||
}
|
||||
break;
|
||||
case PathState::WindowsDriveLetter:
|
||||
if (c == ':') {
|
||||
*out = true;
|
||||
return 0;
|
||||
} else {
|
||||
return ResultFsInvalidPathFormat;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case PathState::Start:
|
||||
case PathState::WindowsDriveLetter:
|
||||
return ResultFsInvalidPathFormat;
|
||||
case PathState::FirstSeparator:
|
||||
case PathState::Separator:
|
||||
*out = false;
|
||||
break;
|
||||
case PathState::Normal:
|
||||
case PathState::CurrentDir:
|
||||
case PathState::ParentDir:
|
||||
*out = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result FsPathUtils::Normalize(char *out, size_t max_out_size, const char *src, size_t *out_size) {
|
||||
/* TODO */
|
||||
return ResultFsNotImplemented;
|
||||
}
|
||||
|
|
|
@ -25,10 +25,16 @@ class FsPathUtils {
|
|||
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 IsWindowsAbsolutePath(const char *path) {
|
||||
/* Nintendo uses this in path comparisons... */
|
||||
return (('a' <= path[0] && path[0] <= 'z') || (('A' <= path[0] && path[0] <= 'Z'))) &&
|
||||
path[0] != 0 && path[1] == ':';
|
||||
return IsWindowsDriveLetter(path[0]) && path[1] == ':';
|
||||
}
|
||||
};
|
||||
|
|
|
@ -22,10 +22,15 @@ static constexpr u32 Module_Fs = 2;
|
|||
static constexpr Result ResultFsNotImplemented = MAKERESULT(Module_Fs, 3001);
|
||||
static constexpr Result ResultFsOutOfRange = MAKERESULT(Module_Fs, 3005);
|
||||
|
||||
static constexpr Result ResultFsInvalidArgument = MAKERESULT(Module_Fs, 6001);
|
||||
static constexpr Result ResultFsInvalidPath = MAKERESULT(Module_Fs, 6002);
|
||||
static constexpr Result ResultFsTooLongPath = MAKERESULT(Module_Fs, 6003);
|
||||
static constexpr Result ResultFsInvalidCharacter = MAKERESULT(Module_Fs, 6004);
|
||||
static constexpr Result ResultFsAllocationFailureInSubDirectoryFileSystem = MAKERESULT(Module_Fs, 3355);
|
||||
|
||||
static constexpr Result ResultFsInvalidArgument = MAKERESULT(Module_Fs, 6001);
|
||||
static constexpr Result ResultFsInvalidPath = MAKERESULT(Module_Fs, 6002);
|
||||
static constexpr Result ResultFsTooLongPath = MAKERESULT(Module_Fs, 6003);
|
||||
static constexpr Result ResultFsInvalidCharacter = MAKERESULT(Module_Fs, 6004);
|
||||
static constexpr Result ResultFsInvalidPathFormat = MAKERESULT(Module_Fs, 6005);
|
||||
static constexpr Result ResultFsDirectoryUnobtainable = MAKERESULT(Module_Fs, 6006);
|
||||
static constexpr Result ResultFsNotNormalized = MAKERESULT(Module_Fs, 6007);
|
||||
|
||||
static constexpr Result ResultFsInvalidOffset = MAKERESULT(Module_Fs, 6061);
|
||||
static constexpr Result ResultFsInvalidSize = MAKERESULT(Module_Fs, 6062);
|
||||
|
|
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "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;
|
||||
Result rc = FsPathUtils::Normalize(normal_path, sizeof(normal_path), bp, &normal_path_len);
|
||||
if (R_FAILED(rc)) {
|
||||
/* N calls svcBreak here. */
|
||||
std::abort();
|
||||
}
|
||||
|
||||
/* Ensure terminating '/' */
|
||||
if (normal_path[normal_path_len-1] != '/') {
|
||||
if (normal_path_len + 2 > sizeof(normal_path)) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
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 0;
|
||||
}
|
||||
|
||||
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(FsPath &path, uint64_t size, int flags) {
|
||||
Result rc;
|
||||
FsPath full_path;
|
||||
|
||||
if (R_FAILED((rc = GetFullPath(full_path, path)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return this->base_fs->CreateFile(full_path, size, flags);
|
||||
}
|
||||
|
||||
Result SubDirectoryFileSystem::DeleteFileImpl(FsPath &path) {
|
||||
Result rc;
|
||||
FsPath full_path;
|
||||
|
||||
if (R_FAILED((rc = GetFullPath(full_path, path)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return this->base_fs->DeleteFile(full_path);
|
||||
}
|
||||
|
||||
Result SubDirectoryFileSystem::CreateDirectoryImpl(FsPath &path) {
|
||||
Result rc;
|
||||
FsPath full_path;
|
||||
|
||||
if (R_FAILED((rc = GetFullPath(full_path, path)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return this->base_fs->CreateDirectory(full_path);
|
||||
}
|
||||
|
||||
Result SubDirectoryFileSystem::DeleteDirectoryImpl(FsPath &path) {
|
||||
Result rc;
|
||||
FsPath full_path;
|
||||
|
||||
if (R_FAILED((rc = GetFullPath(full_path, path)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return this->base_fs->DeleteDirectory(full_path);
|
||||
}
|
||||
|
||||
Result SubDirectoryFileSystem::DeleteDirectoryRecursivelyImpl(FsPath &path) {
|
||||
Result rc;
|
||||
FsPath full_path;
|
||||
|
||||
if (R_FAILED((rc = GetFullPath(full_path, path)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return this->base_fs->DeleteDirectoryRecursively(full_path);
|
||||
}
|
||||
|
||||
Result SubDirectoryFileSystem::RenameFileImpl(FsPath &old_path, FsPath &new_path) {
|
||||
Result rc;
|
||||
FsPath full_old_path, full_new_path;
|
||||
|
||||
if (R_FAILED((rc = GetFullPath(full_old_path, old_path)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (R_FAILED((rc = GetFullPath(full_new_path, new_path)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return this->base_fs->RenameFile(full_old_path, full_new_path);
|
||||
}
|
||||
|
||||
Result SubDirectoryFileSystem::RenameDirectoryImpl(FsPath &old_path, FsPath &new_path) {
|
||||
Result rc;
|
||||
FsPath full_old_path, full_new_path;
|
||||
|
||||
if (R_FAILED((rc = GetFullPath(full_old_path, old_path)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (R_FAILED((rc = GetFullPath(full_new_path, new_path)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return this->base_fs->RenameDirectory(full_old_path, full_new_path);
|
||||
}
|
||||
|
||||
Result SubDirectoryFileSystem::GetEntryTypeImpl(DirectoryEntryType *out, FsPath &path) {
|
||||
Result rc;
|
||||
FsPath full_path;
|
||||
|
||||
if (R_FAILED((rc = GetFullPath(full_path, path)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return this->base_fs->GetEntryType(out, full_path);
|
||||
}
|
||||
|
||||
Result SubDirectoryFileSystem::OpenFileImpl(std::unique_ptr<IFile> &out_file, FsPath &path, OpenMode mode) {
|
||||
Result rc;
|
||||
FsPath full_path;
|
||||
|
||||
if (R_FAILED((rc = GetFullPath(full_path, path)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return this->base_fs->OpenFile(out_file, full_path, mode);
|
||||
}
|
||||
|
||||
Result SubDirectoryFileSystem::OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, FsPath &path, DirectoryOpenMode mode) {
|
||||
Result rc;
|
||||
FsPath full_path;
|
||||
|
||||
if (R_FAILED((rc = GetFullPath(full_path, path)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return this->base_fs->OpenDirectory(out_dir, full_path, mode);
|
||||
}
|
||||
|
||||
Result SubDirectoryFileSystem::CommitImpl() {
|
||||
return this->base_fs->Commit();
|
||||
}
|
||||
|
||||
Result SubDirectoryFileSystem::GetFreeSpaceSizeImpl(uint64_t *out, FsPath &path) {
|
||||
Result rc;
|
||||
FsPath full_path;
|
||||
|
||||
if (R_FAILED((rc = GetFullPath(full_path, path)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return this->base_fs->GetFreeSpaceSize(out, full_path);
|
||||
}
|
||||
|
||||
Result SubDirectoryFileSystem::GetTotalSpaceSizeImpl(uint64_t *out, FsPath &path) {
|
||||
Result rc;
|
||||
FsPath full_path;
|
||||
|
||||
if (R_FAILED((rc = GetFullPath(full_path, path)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return this->base_fs->GetTotalSpaceSize(out, full_path);
|
||||
}
|
||||
|
||||
Result SubDirectoryFileSystem::CleanDirectoryRecursivelyImpl(FsPath &path) {
|
||||
Result rc;
|
||||
FsPath full_path;
|
||||
|
||||
if (R_FAILED((rc = GetFullPath(full_path, path)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return this->base_fs->CleanDirectoryRecursively(full_path);
|
||||
}
|
||||
|
||||
Result SubDirectoryFileSystem::GetFileTimeStampRawImpl(FsTimeStampRaw *out, FsPath &path) {
|
||||
Result rc;
|
||||
FsPath full_path;
|
||||
|
||||
if (R_FAILED((rc = GetFullPath(full_path, path)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return this->base_fs->GetFileTimeStampRaw(out, full_path);
|
||||
}
|
||||
|
||||
Result SubDirectoryFileSystem::QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, FsPath &path) {
|
||||
Result rc;
|
||||
FsPath full_path;
|
||||
|
||||
if (R_FAILED((rc = GetFullPath(full_path, path)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return this->base_fs->QueryEntry(out, out_size, in, in_size, query, full_path);
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "fs_ifilesystem.hpp"
|
||||
#include "fs_path_utils.hpp"
|
||||
|
||||
class 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) {
|
||||
Result rc = this->Initialize(bp);
|
||||
if (R_FAILED(rc)) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
}
|
||||
|
||||
SubDirectoryFileSystem(std::shared_ptr<IFileSystem> fs, const char *bp) : base_fs(fs) {
|
||||
Result rc = this->Initialize(bp);
|
||||
if (R_FAILED(rc)) {
|
||||
fatalSimple(rc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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, FsPath &relative_path) {
|
||||
return GetFullPath(full_path.str, sizeof(full_path.str), relative_path.str);
|
||||
}
|
||||
|
||||
public:
|
||||
virtual Result CreateFileImpl(FsPath &path, uint64_t size, int flags) override;
|
||||
virtual Result DeleteFileImpl(FsPath &path) override;
|
||||
virtual Result CreateDirectoryImpl(FsPath &path) override;
|
||||
virtual Result DeleteDirectoryImpl(FsPath &path) override;
|
||||
virtual Result DeleteDirectoryRecursivelyImpl(FsPath &path) override;
|
||||
virtual Result RenameFileImpl(FsPath &old_path, FsPath &new_path) override;
|
||||
virtual Result RenameDirectoryImpl(FsPath &old_path, FsPath &new_path) override;
|
||||
virtual Result GetEntryTypeImpl(DirectoryEntryType *out, FsPath &path) override;
|
||||
virtual Result OpenFileImpl(std::unique_ptr<IFile> &out_file, FsPath &path, OpenMode mode) override;
|
||||
virtual Result OpenDirectoryImpl(std::unique_ptr<IDirectory> &out_dir, FsPath &path, DirectoryOpenMode mode) override;
|
||||
virtual Result CommitImpl() override;
|
||||
virtual Result GetFreeSpaceSizeImpl(uint64_t *out, FsPath &path) override;
|
||||
virtual Result GetTotalSpaceSizeImpl(uint64_t *out, FsPath &path) override;
|
||||
virtual Result CleanDirectoryRecursivelyImpl(FsPath &path) override;
|
||||
virtual Result GetFileTimeStampRawImpl(FsTimeStampRaw *out, FsPath &path) override;
|
||||
virtual Result QueryEntryImpl(char *out, uint64_t out_size, const char *in, uint64_t in_size, int query, FsPath &path) override;
|
||||
};
|
|
@ -28,7 +28,7 @@
|
|||
#include "fsmitm_romstorage.hpp"
|
||||
#include "fsmitm_layeredrom.hpp"
|
||||
|
||||
#include "fs_ifilesystem.hpp"
|
||||
#include "fs_subdirectory_filesystem.hpp"
|
||||
|
||||
#include "../debug.hpp"
|
||||
|
||||
|
|
Loading…
Reference in a new issue