fs: implement user fs wrappers

This commit is contained in:
Michael Scire 2020-03-04 00:02:40 -08:00
parent 32f6660b7a
commit 6514fb75a8
14 changed files with 594 additions and 1 deletions

View file

@ -25,6 +25,7 @@
#include "fs/fs_remote_storage.hpp"
#include "fs/fs_file_storage.hpp"
#include "fs/fs_query_range.hpp"
#include "fs/impl/fs_common_mount_name.hpp"
#include "fs/fs_mount.hpp"
#include "fs/fs_path_tool.hpp"
#include "fs/fs_path_utils.hpp"

View file

@ -22,4 +22,12 @@ namespace ams::fs {
using DirectoryEntry = ::FsDirectoryEntry;
struct DirectoryHandle {
void *handle;
};
Result ReadDirectory(s64 *out_count, DirectoryEntry *out_entries, DirectoryHandle handle, s64 max_entries);
Result GetDirectoryEntryCount(s64 *out, DirectoryHandle handle);
void CloseDirectory(DirectoryHandle handle);
}

View file

@ -60,4 +60,19 @@ namespace ams::fs {
static_assert(std::is_pod<WriteOption>::value && sizeof(WriteOption) == sizeof(u32));
struct FileHandle {
void *handle;
};
Result ReadFile(FileHandle handle, s64 offset, void *buffer, size_t size, const fs::ReadOption &option);
Result ReadFile(FileHandle handle, s64 offset, void *buffer, size_t size);
Result ReadFile(size_t *out, FileHandle handle, s64 offset, void *buffer, size_t size, const fs::ReadOption &option);
Result ReadFile(size_t *out, FileHandle handle, s64 offset, void *buffer, size_t size);
Result GetFileSize(s64 *out, FileHandle handle);
Result FlushFile(FileHandle handle);
Result WriteFile(FileHandle handle, s64 offset, const void *buffer, size_t size, const fs::WriteOption &option);
Result SetFileSize(FileHandle handle, s64 size);
int GetFileOpenMode(FileHandle handle);
void CloseFile(FileHandle handle);
}

View file

@ -18,6 +18,12 @@
namespace ams::fs {
namespace fsa {
class IFile;
}
enum OpenMode {
OpenMode_Read = ::FsOpenMode_Read,
OpenMode_Write = ::FsOpenMode_Write,
@ -49,4 +55,27 @@ namespace ams::fs {
using FileTimeStampRaw = ::FsTimeStampRaw;
struct FileHandle;
struct DirectoryHandle;
Result CreateFile(const char *path, s64 size);
Result CreateFile(const char* path, s64 size, int option);
Result DeleteFile(const char *path);
Result CreateDirectory(const char *path);
Result DeleteDirectory(const char *path);
Result DeleteDirectoryRecursively(const char *path);
Result RenameFile(const char *old_path, const char *new_path);
Result RenameDirectory(const char *old_path, const char *new_path);
Result GetEntryType(DirectoryEntryType *out, const char *path);
Result OpenFile(FileHandle *out_file, const char *path, int mode);
Result OpenDirectory(DirectoryHandle *out_dir, const char *path, int mode);
Result CleanDirectoryRecursively(const char *path);
Result GetFreeSpaceSize(s64 *out, const char *path);
Result GetTotalSpaceSize(s64 *out, const char *path);
Result SetConcatenationFileAttribute(const char *path);
Result GetFileTimeStampRaw(FileTimeStampRaw *out, const char *path);
Result OpenFile(FileHandle *out, std::unique_ptr<fsa::IFile> &&file, int mode);
}

View file

@ -26,12 +26,17 @@ namespace ams::fs {
constexpr inline char Dot = '.';
constexpr inline char NullTerminator = '\x00';
constexpr inline char UnsupportedDirectorySeparator = '/';
}
class PathTool {
public:
static constexpr const char RootPath[] = "/";
public:
static constexpr inline bool IsUnsupportedSeparator(char c) {
return c == StringTraits::UnsupportedDirectorySeparator;
}
static constexpr inline bool IsSeparator(char c) {
return c == StringTraits::DirectorySeparator;
}

View file

@ -15,6 +15,7 @@
*/
#pragma once
#include "fs_common.hpp"
#include "fs_file.hpp"
namespace ams::fs {
@ -42,4 +43,6 @@ namespace ams::fs {
using FileQueryRangeInfo = QueryRangeInfo;
using StorageQueryRangeInfo = QueryRangeInfo;
Result QueryRange(QueryRangeInfo *out, FileHandle handle, s64 offset, s64 size);
}

View file

@ -87,7 +87,7 @@ namespace ams::fs {
}
};
class RemoteFileSystem : public fsa::IFileSystem {
class RemoteFileSystem : public fsa::IFileSystem, public impl::Newable {
private:
std::unique_ptr<::FsFileSystem, impl::Deleter> base_fs;
public:

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2018-2020 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
namespace ams::fs::impl {
/* Delimiting of mount names. */
constexpr inline const char ReservedMountNamePrefixCharacter = '@';
constexpr inline const char *MountNameDelimiter = ":/";
/* Filesystem names. */
constexpr inline const char *HostRootFileSystemMountName = "@Host";
constexpr inline const char *SdCardFileSystemMountName = "@Sdcard";
constexpr inline const char *GameCardFileSystemMountName = "@Gc";
constexpr inline size_t GameCardFileSystemMountNameSuffixLength = 1;
constexpr inline const char *GameCardFileSystemMountNameUpdateSuffix = "U";
constexpr inline const char *GameCardFileSystemMountNameNormalSuffix = "N";
constexpr inline const char *GameCardFileSystemMountNameSecureSuffix = "S";
/* Built-in storage names. */
constexpr inline const char *BisCalibrationFilePartitionMountName = "@CalibFile";
constexpr inline const char *BisSafeModePartitionMountName = "@Safe";
constexpr inline const char *BisUserPartitionMountName = "@User";
constexpr inline const char *BisSystemPartitionMountName = "@System";
/* Registered update partition. */
constexpr inline const char *RegisteredUpdatePartitionMountName = "@RegUpdate";
}

View file

@ -0,0 +1,168 @@
/*
* Copyright (c) 2018-2020 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 <stratosphere.hpp>
#include "fs_filesystem_accessor.hpp"
#include "fs_mount_utils.hpp"
#include "fs_user_mount_table.hpp"
namespace ams::fs::impl {
namespace {
const char *FindMountNameDriveSeparator(const char *path) {
for (const char *cur = path; cur < path + MountNameLengthMax + 1; cur++) {
if (PathTool::IsDriveSeparator(*cur)) {
return cur;
} else if (PathTool::IsNullTerminator(*cur)) {
break;
}
}
return nullptr;
}
Result GetMountNameImpl(MountName &out, const char *path) {
const char *drive_separator = FindMountNameDriveSeparator(path);
R_UNLESS(drive_separator != nullptr, fs::ResultInvalidMountName());
const size_t len = drive_separator - path;
R_UNLESS(len + 1 <= sizeof(MountName), fs::ResultInvalidMountName());
std::memcpy(out.str, path, len);
out.str[len] = StringTraits::NullTerminator;
return ResultSuccess();
}
}
MountName GetMountName(const char *path) {
MountName mount_name;
if (IsWindowsDrive(path)) {
std::strncpy(mount_name.str, HostRootFileSystemMountName, MountNameLengthMax);
mount_name.str[MountNameLengthMax] = StringTraits::NullTerminator;
} else {
R_ABORT_UNLESS(GetMountNameImpl(mount_name, path));
}
return mount_name;
}
const char *GetSubPath(const char *path) {
if (IsWindowsDrive(path)) {
return path;
}
const char *sub_path = path;
while (!PathTool::IsDriveSeparator(*sub_path)) {
sub_path++;
}
AMS_ABORT_UNLESS(PathTool::IsSeparator(sub_path[1]) || PathTool::IsUnsupportedSeparator(sub_path[1]));
return sub_path + 1;
}
bool IsValidMountName(const char *name) {
if (PathTool::IsNullTerminator(*name)) {
return false;
}
if (PathTool::IsWindowsDriveCharacter(name[0]) && PathTool::IsNullTerminator(name[1])) {
return false;
}
size_t len = 0;
for (const char *cur = name; !PathTool::IsNullTerminator(*cur); cur++) {
if (PathTool::IsDriveSeparator(*cur) || PathTool::IsSeparator(*cur)) {
return false;
}
if ((++len) > MountNameLengthMax) {
return false;
}
}
/* TODO: N validates that the mount name decodes via utf-8 here. */
return true;
}
bool IsWindowsDrive(const char *name) {
return PathTool::IsWindowsAbsolutePath(name);
}
bool IsReservedMountName(const char *name) {
return name[0] != ReservedMountNamePrefixCharacter;
}
Result CheckMountName(const char *name) {
R_TRY(CheckMountNameAllowingReserved(name));
R_UNLESS(!impl::IsReservedMountName(name), fs::ResultInvalidMountName());
return ResultSuccess();
}
Result CheckMountNameAllowingReserved(const char *name) {
R_UNLESS(name != nullptr, fs::ResultInvalidMountName());
R_UNLESS(impl::IsValidMountName(name), fs::ResultInvalidMountName());
return ResultSuccess();
}
Result FindFileSystem(FileSystemAccessor **out, const char *path) {
R_UNLESS(path != nullptr, fs::ResultInvalidPath());
R_UNLESS(strncmp(path, HostRootFileSystemMountName, strnlen(HostRootFileSystemMountName, sizeof(MountName))) != 0, fs::ResultInvalidPathFormat());
return impl::Find(out, GetMountName(path).str);
}
}
namespace ams::fs {
namespace {
Result UnmountImpl(const char *name) {
impl::FileSystemAccessor *accessor;
R_TRY(impl::Find(std::addressof(accessor), name));
if (accessor->IsFileDataCacheAttachable()) {
/* TODO: Data cache purge */
}
impl::Unregister(name);
return ResultSuccess();
}
}
Result ConvertToFsCommonPath(char *dst, size_t dst_size, const char *src) {
/* Get the mount name for the path. */
MountName mount_name = impl::GetMountName(src);
impl::FileSystemAccessor *accessor;
R_TRY(impl::Find(std::addressof(accessor), mount_name.str));
R_TRY(accessor->GetCommonMountName(dst, dst_size));
const auto mount_name_len = strnlen(dst, dst_size);
const auto common_path_len = std::snprintf(dst + mount_name_len, dst_size - mount_name_len, "%s", impl::GetSubPath(src));
R_UNLESS(static_cast<size_t>(common_path_len) < dst_size - mount_name_len, fs::ResultTooLongPath());
return ResultSuccess();
}
void Unmount(const char *mount_name) {
R_ABORT_UNLESS(UnmountImpl(mount_name));
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2018-2020 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 <stratosphere.hpp>
namespace ams::fs::impl {
class FileSystemAccessor;
Result FindFileSystem(FileSystemAccessor **out, const char *path);
const char *GetSubPath(const char *path);
bool IsWindowsDrive(const char *name);
bool IsReservedMountName(const char *name);
Result CheckMountName(const char *name);
Result CheckMountNameAllowingReserved(const char *name);
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2018-2020 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 <stratosphere.hpp>
#include "fs_directory_accessor.hpp"
#include "fs_filesystem_accessor.hpp"
namespace ams::fs {
namespace {
ALWAYS_INLINE impl::DirectoryAccessor *Get(DirectoryHandle handle) {
return reinterpret_cast<impl::DirectoryAccessor *>(handle.handle);
}
}
Result ReadDirectory(s64 *out_count, DirectoryEntry *out_entries, DirectoryHandle handle, s64 max_entries) {
return Get(handle)->Read(out_count, out_entries, max_entries);
}
Result GetDirectoryEntryCount(s64 *out, DirectoryHandle handle) {
return Get(handle)->GetEntryCount(out);
}
void CloseDirectory(DirectoryHandle handle) {
delete Get(handle);
}
}

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2018-2020 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 <stratosphere.hpp>
#include "fs_file_accessor.hpp"
#include "fs_filesystem_accessor.hpp"
namespace ams::fs {
namespace {
ALWAYS_INLINE impl::FileAccessor *Get(FileHandle handle) {
return reinterpret_cast<impl::FileAccessor *>(handle.handle);
}
}
Result ReadFile(FileHandle handle, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) {
size_t read_size;
R_TRY(ReadFile(std::addressof(size), handle, offset, buffer, size, option));
R_UNLESS(read_size == size, fs::ResultOutOfRange());
return ResultSuccess();
}
Result ReadFile(FileHandle handle, s64 offset, void *buffer, size_t size) {
return ReadFile(handle, offset, buffer, size, ReadOption());
}
Result ReadFile(size_t *out, FileHandle handle, s64 offset, void *buffer, size_t size, const fs::ReadOption &option) {
return Get(handle)->Read(out, offset, buffer, size, option);
}
Result ReadFile(size_t *out, FileHandle handle, s64 offset, void *buffer, size_t size) {
return ReadFile(out, handle, offset, buffer, size, ReadOption());
}
Result GetFileSize(s64 *out, FileHandle handle) {
return Get(handle)->GetSize(out);
}
Result FlushFile(FileHandle handle) {
return Get(handle)->Flush();
}
Result WriteFile(FileHandle handle, s64 offset, const void *buffer, size_t size, const fs::WriteOption &option) {
return Get(handle)->Write(offset, buffer, size, option);
}
Result SetFileSize(FileHandle handle, s64 size) {
return Get(handle)->SetSize(size);
}
int GetFileOpenMode(FileHandle handle) {
return Get(handle)->GetOpenMode();
}
void CloseFile(FileHandle handle) {
delete Get(handle);
}
Result QueryRange(QueryRangeInfo *out, FileHandle handle, s64 offset, s64 size) {
return Get(handle)->OperateRange(out, sizeof(*out), OperationId::QueryRange, offset, size, nullptr, 0);
}
}

View file

@ -0,0 +1,170 @@
/*
* Copyright (c) 2018-2020 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 <stratosphere.hpp>
#include "fs_filesystem_accessor.hpp"
#include "fs_file_accessor.hpp"
#include "fs_directory_accessor.hpp"
#include "fs_mount_utils.hpp"
#include "fs_user_mount_table.hpp"
namespace ams::fs {
Result CreateFile(const char *path, s64 size) {
return CreateFile(path, size, 0);
}
Result CreateFile(const char* path, s64 size, int option) {
impl::FileSystemAccessor *accessor;
R_TRY(impl::FindFileSystem(std::addressof(accessor), path));
return accessor->CreateFile(impl::GetSubPath(path), size, option);
}
Result DeleteFile(const char *path) {
impl::FileSystemAccessor *accessor;
R_TRY(impl::FindFileSystem(std::addressof(accessor), path));
return accessor->DeleteFile(impl::GetSubPath(path));
}
Result CreateDirectory(const char *path) {
impl::FileSystemAccessor *accessor;
R_TRY(impl::FindFileSystem(std::addressof(accessor), path));
return accessor->CreateDirectory(impl::GetSubPath(path));
}
Result DeleteDirectory(const char *path) {
impl::FileSystemAccessor *accessor;
R_TRY(impl::FindFileSystem(std::addressof(accessor), path));
return accessor->DeleteDirectory(impl::GetSubPath(path));
}
Result DeleteDirectoryRecursively(const char *path) {
impl::FileSystemAccessor *accessor;
R_TRY(impl::FindFileSystem(std::addressof(accessor), path));
return accessor->DeleteDirectoryRecursively(impl::GetSubPath(path));
}
Result RenameFile(const char *old_path, const char *new_path) {
impl::FileSystemAccessor *old_accessor;
impl::FileSystemAccessor *new_accessor;
R_TRY(impl::FindFileSystem(std::addressof(old_accessor), old_path));
R_TRY(impl::FindFileSystem(std::addressof(new_accessor), new_path));
R_UNLESS(old_accessor == new_accessor, fs::ResultRenameToOtherFileSystem());
return old_accessor->RenameFile(impl::GetSubPath(old_path), impl::GetSubPath(new_path));
}
Result RenameDirectory(const char *old_path, const char *new_path) {
impl::FileSystemAccessor *old_accessor;
impl::FileSystemAccessor *new_accessor;
R_TRY(impl::FindFileSystem(std::addressof(old_accessor), old_path));
R_TRY(impl::FindFileSystem(std::addressof(new_accessor), new_path));
R_UNLESS(old_accessor == new_accessor, fs::ResultRenameToOtherFileSystem());
return old_accessor->RenameDirectory(impl::GetSubPath(old_path), impl::GetSubPath(new_path));
}
Result GetEntryType(DirectoryEntryType *out, const char *path) {
impl::FileSystemAccessor *accessor;
R_TRY(impl::FindFileSystem(std::addressof(accessor), path));
return accessor->GetEntryType(out, impl::GetSubPath(path));
}
Result OpenFile(FileHandle *out_file, const char *path, int mode) {
impl::FileSystemAccessor *accessor;
R_TRY(impl::FindFileSystem(std::addressof(accessor), path));
R_UNLESS(out_file != nullptr, fs::ResultNullptrArgument());
std::unique_ptr<impl::FileAccessor> file_accessor;
R_TRY(accessor->OpenFile(std::addressof(file_accessor), impl::GetSubPath(path), static_cast<OpenMode>(mode)));
out_file->handle = file_accessor.release();
return ResultSuccess();
}
Result OpenDirectory(DirectoryHandle *out_dir, const char *path, int mode) {
impl::FileSystemAccessor *accessor;
R_TRY(impl::FindFileSystem(std::addressof(accessor), path));
R_UNLESS(out_dir != nullptr, fs::ResultNullptrArgument());
std::unique_ptr<impl::DirectoryAccessor> dir_accessor;
R_TRY(accessor->OpenDirectory(std::addressof(dir_accessor), impl::GetSubPath(path), static_cast<OpenDirectoryMode>(mode)));
out_dir->handle = dir_accessor.release();
return ResultSuccess();
}
Result CleanDirectoryRecursively(const char *path) {
impl::FileSystemAccessor *accessor;
R_TRY(impl::FindFileSystem(std::addressof(accessor), path));
return accessor->CleanDirectoryRecursively(impl::GetSubPath(path));
}
Result GetFreeSpaceSize(s64 *out, const char *path) {
impl::FileSystemAccessor *accessor;
R_TRY(impl::FindFileSystem(std::addressof(accessor), path));
return accessor->GetFreeSpaceSize(out, impl::GetSubPath(path));
}
Result GetTotalSpaceSize(s64 *out, const char *path) {
impl::FileSystemAccessor *accessor;
R_TRY(impl::FindFileSystem(std::addressof(accessor), path));
return accessor->GetTotalSpaceSize(out, impl::GetSubPath(path));
}
Result SetConcatenationFileAttribute(const char *path) {
impl::FileSystemAccessor *accessor;
R_TRY(impl::FindFileSystem(std::addressof(accessor), path));
return accessor->QueryEntry(nullptr, 0, nullptr, 0, fsa::QueryId::SetConcatenationFileAttribute, impl::GetSubPath(path));
}
Result GetFileTimeStampRaw(FileTimeStampRaw *out, const char *path) {
impl::FileSystemAccessor *accessor;
R_TRY(impl::FindFileSystem(std::addressof(accessor), path));
return accessor->GetFileTimeStampRaw(out, impl::GetSubPath(path));
}
Result OpenFile(FileHandle *out, std::unique_ptr<fsa::IFile> &&file, int mode) {
R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
std::unique_ptr<impl::FileAccessor> file_accessor(new impl::FileAccessor(std::move(file), nullptr, static_cast<OpenMode>(mode)));
R_UNLESS(file_accessor != nullptr, fs::ResultAllocationFailureInUserFileSystem());
out->handle = file_accessor.release();
return ResultSuccess();
}
}

View file

@ -56,6 +56,7 @@ namespace ams::fs {
R_DEFINE_ERROR_RESULT(AllocationFailureInRegisterB, 3366);
R_DEFINE_ERROR_RESULT(AllocationFailureInPathNormalizer, 3367);
R_DEFINE_ERROR_RESULT(AllocationFailureInFileSystemInterfaceAdapter, 3407);
R_DEFINE_ERROR_RESULT(AllocationFailureInUserFileSystem, 3420);
R_DEFINE_ERROR_RANGE(MmcAccessFailed, 3500, 3999);