From 3bf488ce520a81811bf6e949e2153aabf4b713ea Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 3 Aug 2018 11:46:30 -0400 Subject: vfs: Add VfsFilesystem interface and default implementation --- src/core/file_sys/vfs.cpp | 148 ++++++++++++++++++++++++++++++++++++++++++++++ src/core/file_sys/vfs.h | 66 ++++++++++++++++++++- 2 files changed, 211 insertions(+), 3 deletions(-) (limited to 'src/core') diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index dae1c16ef..24e158962 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp @@ -4,12 +4,160 @@ #include #include +#include +#include "common/common_paths.h" #include "common/file_util.h" #include "common/logging/backend.h" #include "core/file_sys/vfs.h" namespace FileSys { +VfsFilesystem::VfsFilesystem(VirtualDir root_) : root(std::move(root_)) {} + +VfsFilesystem::~VfsFilesystem() = default; + +std::string VfsFilesystem::GetName() const { + return root->GetName(); +} + +bool VfsFilesystem::IsReadable() const { + return root->IsReadable(); +} + +bool VfsFilesystem::IsWritable() const { + return root->IsWritable(); +} + +VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const { + const auto path = FileUtil::SanitizePath(path_); + if (root->GetFileRelative(path) != nullptr) + return VfsEntryType::File; + if (root->GetDirectoryRelative(path) != nullptr) + return VfsEntryType::Directory; + + return VfsEntryType::None; +} + +VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) { + const auto path = FileUtil::SanitizePath(path_); + return root->GetFileRelative(path); +} + +VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) { + const auto path = FileUtil::SanitizePath(path_); + return root->CreateFileRelative(path); +} + +VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { + const auto old_path = FileUtil::SanitizePath(old_path_); + const auto new_path = FileUtil::SanitizePath(new_path_); + + // VfsDirectory impls are only required to implement copy across the current directory. + if (FileUtil::GetParentPath(old_path) == FileUtil::GetParentPath(new_path)) { + if (!root->Copy(FileUtil::GetFilename(old_path), FileUtil::GetFilename(new_path))) + return nullptr; + return OpenFile(new_path, Mode::ReadWrite); + } + + // Do it using RawCopy. Non-default impls are encouraged to optimize this. + const auto old_file = OpenFile(old_path, Mode::Read); + if (old_file == nullptr) + return nullptr; + auto new_file = OpenFile(new_path, Mode::Read); + if (new_file != nullptr) + return nullptr; + new_file = CreateFile(new_path, Mode::Write); + if (new_file == nullptr) + return nullptr; + if (!VfsRawCopy(old_file, new_file)) + return nullptr; + return new_file; +} + +VirtualFile VfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { + const auto old_path = FileUtil::SanitizePath(old_path_); + const auto new_path = FileUtil::SanitizePath(new_path_); + + // Again, non-default impls are highly encouraged to provide a more optimized version of this. + auto out = CopyFile(old_path_, new_path_); + if (out == nullptr) + return nullptr; + if (DeleteFile(old_path)) + return out; + return nullptr; +} + +bool VfsFilesystem::DeleteFile(std::string_view path_) { + const auto path = FileUtil::SanitizePath(path_); + auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write); + if (parent == nullptr) + return false; + return parent->DeleteFile(FileUtil::GetFilename(path)); +} + +VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { + const auto path = FileUtil::SanitizePath(path_); + return root->GetDirectoryRelative(path); +} + +VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { + const auto path = FileUtil::SanitizePath(path_); + return root->CreateDirectoryRelative(path); +} + +VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) { + const auto old_path = FileUtil::SanitizePath(old_path_); + const auto new_path = FileUtil::SanitizePath(new_path_); + + // Non-default impls are highly encouraged to provide a more optimized version of this. + auto old_dir = OpenDirectory(old_path, Mode::Read); + if (old_dir == nullptr) + return nullptr; + auto new_dir = OpenDirectory(new_path, Mode::Read); + if (new_dir != nullptr) + return nullptr; + new_dir = CreateDirectory(new_path, Mode::Write); + if (new_dir == nullptr) + return nullptr; + + for (const auto& file : old_dir->GetFiles()) { + const auto x = + CopyFile(old_path + DIR_SEP + file->GetName(), new_path + DIR_SEP + file->GetName()); + if (x == nullptr) + return nullptr; + } + + for (const auto& dir : old_dir->GetSubdirectories()) { + const auto x = + CopyDirectory(old_path + DIR_SEP + dir->GetName(), new_path + DIR_SEP + dir->GetName()); + if (x == nullptr) + return nullptr; + } + + return new_dir; +} + +VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path_, std::string_view new_path_) { + const auto old_path = FileUtil::SanitizePath(old_path_); + const auto new_path = FileUtil::SanitizePath(new_path_); + + // Non-default impls are highly encouraged to provide a more optimized version of this. + auto out = CopyDirectory(old_path_, new_path_); + if (out == nullptr) + return nullptr; + if (DeleteDirectory(old_path)) + return out; + return nullptr; +} + +bool VfsFilesystem::DeleteDirectory(std::string_view path_) { + const auto path = FileUtil::SanitizePath(path_); + auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write); + if (parent == nullptr) + return false; + return parent->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path)); +} + VfsFile::~VfsFile() = default; std::string VfsFile::GetExtension() const { diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h index fab9e2b45..9c7ef93b8 100644 --- a/src/core/file_sys/vfs.h +++ b/src/core/file_sys/vfs.h @@ -11,14 +11,74 @@ #include #include "boost/optional.hpp" #include "common/common_types.h" +#include "core/file_sys/mode.h" namespace FileSys { + +struct VfsFilesystem; struct VfsFile; struct VfsDirectory; -// Convenience typedefs to use VfsDirectory and VfsFile -using VirtualDir = std::shared_ptr; -using VirtualFile = std::shared_ptr; +// Convenience typedefs to use Vfs* interfaces +using VirtualFilesystem = std::shared_ptr; +using VirtualDir = std::shared_ptr; +using VirtualFile = std::shared_ptr; + +// An enumeration representing what can be at the end of a path in a VfsFilesystem +enum class VfsEntryType { + None, + File, + Directory, +}; + +// A class represnting an abstract filesystem. A default implementation given the root VirtualDir is +// provided for convenience, but if the Vfs implementation has any additional state or +// functionality, they will need to override. +struct VfsFilesystem : NonCopyable { + VfsFilesystem(VirtualDir root); + virtual ~VfsFilesystem(); + + // Gets the friendly name for the filesystem. + virtual std::string GetName() const; + + // Return whether or not the user has read permissions on this filesystem. + virtual bool IsReadable() const; + // Return whether or not the user has write permission on this filesystem. + virtual bool IsWritable() const; + + // Determine if the entry at path is non-existant, a file, or a directory. + virtual VfsEntryType GetEntryType(std::string_view path) const; + + // Opens the file with path relative to root. If it doesn't exist, returns nullptr. + virtual VirtualFile OpenFile(std::string_view path, Mode perms); + // Creates a new, empty file at path + virtual VirtualFile CreateFile(std::string_view path, Mode perms); + // Copies the file from old_path to new_path, returning the new file on success and nullptr on + // failure. + virtual VirtualFile CopyFile(std::string_view old_path, std::string_view new_path); + // Moves the file from old_path to new_path, returning the moved file on success and nullptr on + // failure. + virtual VirtualFile MoveFile(std::string_view old_path, std::string_view new_path); + // Deletes the file with path relative to root, returing true on success. + virtual bool DeleteFile(std::string_view path); + + // Opens the directory with path relative to root. If it doesn't exist, returns nullptr. + virtual VirtualDir OpenDirectory(std::string_view path, Mode perms); + // Creates a new, empty directory at path + virtual VirtualDir CreateDirectory(std::string_view path, Mode perms); + // Copies the directory from old_path to new_path, returning the new directory on success and + // nullptr on failure. + virtual VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path); + // Moves the directory from old_path to new_path, returning the moved directory on success and + // nullptr on failure. + virtual VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path); + // Deletes the directory with path relative to root, returing true on success. + virtual bool DeleteDirectory(std::string_view path); + +protected: + // Root directory in default implementation. + VirtualDir root; +}; // A class representing a file in an abstract filesystem. struct VfsFile : NonCopyable { -- cgit v1.2.3