From 5f942e2dff9dd71d45935978e1d580abbb49fe8c Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Sat, 6 Feb 2021 14:23:42 +0000 Subject: [PATCH] Split VFS implementations from API This allows better validation and simplified default argument handling. Could also be useful in the future when we switch to proper VFS error reporting. --- app/src/main/cpp/skyline/vfs/backing.h | 47 +++++++++++++++---- .../cpp/skyline/vfs/ctr_encrypted_backing.cpp | 7 ++- .../cpp/skyline/vfs/ctr_encrypted_backing.h | 5 +- app/src/main/cpp/skyline/vfs/filesystem.h | 43 +++++++++++++---- app/src/main/cpp/skyline/vfs/os_backing.cpp | 12 ++--- app/src/main/cpp/skyline/vfs/os_backing.h | 13 ++--- .../main/cpp/skyline/vfs/os_filesystem.cpp | 13 ++--- app/src/main/cpp/skyline/vfs/os_filesystem.h | 21 +++++---- .../cpp/skyline/vfs/partition_filesystem.cpp | 8 ++-- .../cpp/skyline/vfs/partition_filesystem.h | 13 ++--- app/src/main/cpp/skyline/vfs/region_backing.h | 18 +++---- .../main/cpp/skyline/vfs/rom_filesystem.cpp | 6 +-- app/src/main/cpp/skyline/vfs/rom_filesystem.h | 13 ++--- 13 files changed, 137 insertions(+), 82 deletions(-) diff --git a/app/src/main/cpp/skyline/vfs/backing.h b/app/src/main/cpp/skyline/vfs/backing.h index 8eca7d8b..2d9f0e09 100644 --- a/app/src/main/cpp/skyline/vfs/backing.h +++ b/app/src/main/cpp/skyline/vfs/backing.h @@ -10,6 +10,17 @@ namespace skyline::vfs { * @brief The Backing class provides abstract access to a storage device, all access can be done without using a specific backing */ class Backing { + protected: + virtual size_t ReadImpl(span output, size_t offset) = 0; + + virtual size_t WriteImpl(span input, size_t offset) { + throw exception("This backing does not support being written to"); + } + + virtual void ResizeImpl(size_t pSize) { + throw exception("This backing does not support being resized"); + } + public: union Mode { struct { @@ -42,7 +53,15 @@ namespace skyline::vfs { * @param offset The offset to start reading from * @return The amount of bytes read */ - virtual size_t Read(span output, size_t offset = 0) = 0; + size_t Read(span output, size_t offset = 0) { + if (!mode.read) + throw exception("Attempting to read a backing that is not readable"); + + if ((static_cast(size) - offset) < output.size()) + throw exception("Trying to read past the end of a backing: 0x{:X}/0x{:X} (Offset: 0x{:X})", output.size(), size, offset); + + return ReadImpl(output, offset); + }; /** * @brief Implicit casting for reading into spans of different types @@ -70,8 +89,18 @@ namespace skyline::vfs { * @param offset The offset where the input buffer should be written * @return The amount of bytes written */ - virtual size_t Write(span input, size_t offset = 0) { - throw exception("This backing does not support being written to"); + size_t Write(span input, size_t offset = 0) { + if (!mode.write) + throw exception("Attempting to write to a backing that is not writable"); + + if (input.size() > (static_cast(size) - offset)) { + if (mode.append) + Resize(offset + input.size()); + else + throw exception("Trying to write past the end of a non-appendable backing: 0x{:X}/0x{:X} (Offset: 0x{:X})", input.size(), size, offset); + } + + return WriteImpl(input, offset); } /** @@ -81,17 +110,17 @@ namespace skyline::vfs { */ template void WriteObject(const T &object, size_t offset = 0) { - size_t size; - if ((size = Write(span(reinterpret_cast(&object), sizeof(T)), offset)) != sizeof(T)) - throw exception("Object wasn't written fully into output backing: {}/{}", size, sizeof(T)); + size_t lSize; + if ((lSize = Write(span(reinterpret_cast(&object), sizeof(T)), offset)) != sizeof(T)) + throw exception("Object wasn't written fully into output backing: {}/{}", lSize, sizeof(T)); } /** * @brief Resizes a backing to the given size - * @param size The new size for the backing + * @param pSize The new size for the backing */ - virtual void Resize(size_t size) { - throw exception("This backing does not support being resized"); + void Resize(size_t pSize) { + ResizeImpl(pSize); } }; } diff --git a/app/src/main/cpp/skyline/vfs/ctr_encrypted_backing.cpp b/app/src/main/cpp/skyline/vfs/ctr_encrypted_backing.cpp index d9e15d08..53f504f7 100644 --- a/app/src/main/cpp/skyline/vfs/ctr_encrypted_backing.cpp +++ b/app/src/main/cpp/skyline/vfs/ctr_encrypted_backing.cpp @@ -6,7 +6,10 @@ namespace skyline::vfs { constexpr size_t SectorSize{0x10}; - CtrEncryptedBacking::CtrEncryptedBacking(crypto::KeyStore::Key128 ctr, crypto::KeyStore::Key128 key, std::shared_ptr backing, size_t baseOffset) : Backing({true, false, false}), ctr(ctr), cipher(key, MBEDTLS_CIPHER_AES_128_CTR), backing(std::move(backing)), baseOffset(baseOffset) {} + CtrEncryptedBacking::CtrEncryptedBacking(crypto::KeyStore::Key128 ctr, crypto::KeyStore::Key128 key, std::shared_ptr backing, size_t baseOffset) : Backing({true, false, false}), ctr(ctr), cipher(key, MBEDTLS_CIPHER_AES_128_CTR), backing(std::move(backing)), baseOffset(baseOffset) { + if (mode.write || mode.append) + throw exception("Cannot open a CtrEncryptedBacking as writable"); + } void CtrEncryptedBacking::UpdateCtr(u64 offset) { offset >>= 4; @@ -15,7 +18,7 @@ namespace skyline::vfs { cipher.SetIV(ctr); } - size_t CtrEncryptedBacking::Read(span output, size_t offset) { + size_t CtrEncryptedBacking::ReadImpl(span output, size_t offset) { size_t size{output.size()}; if (size == 0) return 0; diff --git a/app/src/main/cpp/skyline/vfs/ctr_encrypted_backing.h b/app/src/main/cpp/skyline/vfs/ctr_encrypted_backing.h index 4809d8dc..d22b277f 100644 --- a/app/src/main/cpp/skyline/vfs/ctr_encrypted_backing.h +++ b/app/src/main/cpp/skyline/vfs/ctr_encrypted_backing.h @@ -24,9 +24,10 @@ namespace skyline::vfs { */ void UpdateCtr(u64 offset); + protected: + size_t ReadImpl(span output, size_t offset) override; + public: CtrEncryptedBacking(crypto::KeyStore::Key128 ctr, crypto::KeyStore::Key128 key, std::shared_ptr backing, size_t baseOffset); - - size_t Read(span output, size_t offset = 0) override; }; } diff --git a/app/src/main/cpp/skyline/vfs/filesystem.h b/app/src/main/cpp/skyline/vfs/filesystem.h index c292625f..3d6d66e7 100644 --- a/app/src/main/cpp/skyline/vfs/filesystem.h +++ b/app/src/main/cpp/skyline/vfs/filesystem.h @@ -11,6 +11,23 @@ namespace skyline::vfs { * @brief The FileSystem class represents an abstract filesystem with child files and folders */ class FileSystem { + protected: + virtual bool CreateFileImpl(const std::string &path, size_t size) { + throw exception("This filesystem does not support creating files"); + }; + + virtual bool CreateDirectoryImpl(const std::string &path, bool parents) { + throw exception("This filesystem does not support creating directories"); + }; + + virtual std::shared_ptr OpenFileImpl(const std::string &path, Backing::Mode mode) = 0; + + virtual std::optional GetEntryTypeImpl(const std::string &path) = 0; + + virtual std::shared_ptr OpenDirectoryImpl(const std::string &path, Directory::ListMode listMode) { + throw exception("This filesystem does not support opening directories"); + }; + public: FileSystem() = default; @@ -27,8 +44,8 @@ namespace skyline::vfs { * @param size The size of the file to create * @return Whether creating the file succeeded */ - virtual bool CreateFile(const std::string &path, size_t size) { - throw exception("This filesystem does not support creating files"); + bool CreateFile(const std::string &path, size_t size) { + return CreateFileImpl(path, size); }; /** @@ -37,8 +54,8 @@ namespace skyline::vfs { * @param parents Whether all parent directories in the given path should be created * @return Whether creating the directory succeeded */ - virtual bool CreateDirectory(const std::string &path, bool parents) { - throw exception("This filesystem does not support creating directories"); + bool CreateDirectory(const std::string &path, bool parents) { + return CreateDirectoryImpl(path, parents); }; /** @@ -47,14 +64,21 @@ namespace skyline::vfs { * @param mode The mode to open the file with * @return A shared pointer to a Backing object of the file */ - virtual std::shared_ptr OpenFile(const std::string &path, Backing::Mode mode = {true, false, false}) = 0; + std::shared_ptr OpenFile(const std::string &path, Backing::Mode mode = {true, false, false}) { + if (!mode.write && !mode.read) + throw exception("Cannot open a file with a mode that is neither readable nor writable"); + + return OpenFileImpl(path, mode); + } /** * @brief Queries the type of the entry given by path * @param path The path to the entry * @return The type of the entry, if present */ - virtual std::optional GetEntryType(const std::string &path) = 0; + std::optional GetEntryType(const std::string &path) { + return GetEntryTypeImpl(path); + } /** * @brief Checks if a given file exists in a filesystem @@ -82,8 +106,11 @@ namespace skyline::vfs { * @param listMode The list mode for the directory * @return A shared pointer to a Directory object of the directory */ - virtual std::shared_ptr OpenDirectory(const std::string &path, Directory::ListMode listMode) { - throw exception("This filesystem does not support opening directories"); + std::shared_ptr OpenDirectory(const std::string &path, Directory::ListMode listMode = {true, true}) { + if (!listMode.raw) + throw exception("Cannot open a directory with an empty listMode"); + + return OpenDirectoryImpl(path, listMode); }; }; } diff --git a/app/src/main/cpp/skyline/vfs/os_backing.cpp b/app/src/main/cpp/skyline/vfs/os_backing.cpp index a0e8f41f..bf0457f5 100644 --- a/app/src/main/cpp/skyline/vfs/os_backing.cpp +++ b/app/src/main/cpp/skyline/vfs/os_backing.cpp @@ -20,10 +20,7 @@ namespace skyline::vfs { close(fd); } - size_t OsBacking::Read(span output, size_t offset) { - if (!mode.read) - throw exception("Attempting to read a backing that is not readable"); - + size_t OsBacking::ReadImpl(span output, size_t offset) { auto ret{pread64(fd, output.data(), output.size(), offset)}; if (ret < 0) throw exception("Failed to read from fd: {}", strerror(errno)); @@ -31,10 +28,7 @@ namespace skyline::vfs { return static_cast(ret); } - size_t OsBacking::Write(span input, size_t offset) { - if (!mode.write) - throw exception("Attempting to write to a backing that is not writable"); - + size_t OsBacking::WriteImpl(span input, size_t offset) { auto ret{pwrite64(fd, input.data(), input.size(), offset)}; if (ret < 0) throw exception("Failed to write to fd: {}", strerror(errno)); @@ -42,7 +36,7 @@ namespace skyline::vfs { return static_cast(ret); } - void OsBacking::Resize(size_t pSize) { + void OsBacking::ResizeImpl(size_t pSize) { int ret{ftruncate(fd, pSize)}; if (ret < 0) throw exception("Failed to resize file: {}", strerror(errno)); diff --git a/app/src/main/cpp/skyline/vfs/os_backing.h b/app/src/main/cpp/skyline/vfs/os_backing.h index 6cb3a4f9..70382ed6 100644 --- a/app/src/main/cpp/skyline/vfs/os_backing.h +++ b/app/src/main/cpp/skyline/vfs/os_backing.h @@ -14,6 +14,13 @@ namespace skyline::vfs { int fd; //!< An FD to the backing bool closable; //!< Whether the FD can be closed when the backing is destroyed + protected: + size_t ReadImpl(span output, size_t offset) override; + + size_t WriteImpl(span input, size_t offset) override; + + void ResizeImpl(size_t size) override; + public: /** * @param fd The file descriptor of the backing @@ -21,11 +28,5 @@ namespace skyline::vfs { OsBacking(int fd, bool closable = false, Mode = {true, false, false}); ~OsBacking(); - - size_t Read(span output, size_t offset = 0); - - size_t Write(span input, size_t offset = 0); - - void Resize(size_t size); }; } diff --git a/app/src/main/cpp/skyline/vfs/os_filesystem.cpp b/app/src/main/cpp/skyline/vfs/os_filesystem.cpp index f93f575a..bba4eec7 100644 --- a/app/src/main/cpp/skyline/vfs/os_filesystem.cpp +++ b/app/src/main/cpp/skyline/vfs/os_filesystem.cpp @@ -15,7 +15,7 @@ namespace skyline::vfs { throw exception("Error creating the OS filesystem backing directory"); } - bool OsFileSystem::CreateFile(const std::string &path, size_t size) { + bool OsFileSystem::CreateFileImpl(const std::string &path, size_t size) { auto fullPath{basePath + path}; // Create a directory that will hold the file @@ -38,7 +38,7 @@ namespace skyline::vfs { return true; } - bool OsFileSystem::CreateDirectory(const std::string &path, bool parents) { + bool OsFileSystem::CreateDirectoryImpl(const std::string &path, bool parents) { if (!parents) { int ret{mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)}; return ret == 0 || errno == EEXIST; @@ -58,10 +58,7 @@ namespace skyline::vfs { return true; } - std::shared_ptr OsFileSystem::OpenFile(const std::string &path, Backing::Mode mode) { - if (!(mode.read || mode.write)) - throw exception("Cannot open a file that is neither readable or writable"); - + std::shared_ptr OsFileSystem::OpenFileImpl(const std::string &path, Backing::Mode mode) { int fd{open((basePath + path).c_str(), (mode.read && mode.write) ? O_RDWR : (mode.write ? O_WRONLY : O_RDONLY))}; if (fd < 0) throw exception("Failed to open file at '{}': {}", path, strerror(errno)); @@ -69,7 +66,7 @@ namespace skyline::vfs { return std::make_shared(fd, true, mode); } - std::optional OsFileSystem::GetEntryType(const std::string &path) { + std::optional OsFileSystem::GetEntryTypeImpl(const std::string &path) { auto fullPath{basePath + path}; auto directory{opendir(fullPath.c_str())}; @@ -84,7 +81,7 @@ namespace skyline::vfs { return std::nullopt; } - std::shared_ptr OsFileSystem::OpenDirectory(const std::string &path, Directory::ListMode listMode) { + std::shared_ptr OsFileSystem::OpenDirectoryImpl(const std::string &path, Directory::ListMode listMode) { return std::make_shared(basePath + path, listMode); } diff --git a/app/src/main/cpp/skyline/vfs/os_filesystem.h b/app/src/main/cpp/skyline/vfs/os_filesystem.h index 5404daeb..3a9bd0d3 100644 --- a/app/src/main/cpp/skyline/vfs/os_filesystem.h +++ b/app/src/main/cpp/skyline/vfs/os_filesystem.h @@ -13,18 +13,19 @@ namespace skyline::vfs { private: std::string basePath; //!< The base path for filesystem operations + protected: + bool CreateFileImpl(const std::string &path, size_t size) override; + + bool CreateDirectoryImpl(const std::string &path, bool parents) override; + + std::shared_ptr OpenFileImpl(const std::string &path, Backing::Mode mode) override; + + std::optional GetEntryTypeImpl(const std::string &path) override; + + std::shared_ptr OpenDirectoryImpl(const std::string &path, Directory::ListMode listMode) override; + public: OsFileSystem(const std::string &basePath); - - bool CreateFile(const std::string &path, size_t size); - - bool CreateDirectory(const std::string &path, bool parents); - - std::shared_ptr OpenFile(const std::string &path, Backing::Mode mode = {true, false, false}); - - std::optional GetEntryType(const std::string &path); - - std::shared_ptr OpenDirectory(const std::string &path, Directory::ListMode listMode); }; /** diff --git a/app/src/main/cpp/skyline/vfs/partition_filesystem.cpp b/app/src/main/cpp/skyline/vfs/partition_filesystem.cpp index c92bbcd5..9ecd7451 100644 --- a/app/src/main/cpp/skyline/vfs/partition_filesystem.cpp +++ b/app/src/main/cpp/skyline/vfs/partition_filesystem.cpp @@ -31,7 +31,7 @@ namespace skyline::vfs { } } - std::shared_ptr PartitionFileSystem::OpenFile(const std::string &path, Backing::Mode mode) { + std::shared_ptr PartitionFileSystem::OpenFileImpl(const std::string &path, Backing::Mode mode) { try { auto &entry{fileMap.at(path)}; return std::make_shared(backing, fileDataOffset + entry.offset, entry.size, mode); @@ -40,16 +40,16 @@ namespace skyline::vfs { } } - std::optional PartitionFileSystem::GetEntryType(const std::string &path) { + std::optional PartitionFileSystem::GetEntryTypeImpl(const std::string &path) { if (fileMap.count(path)) return Directory::EntryType::File; return std::nullopt; } - std::shared_ptr PartitionFileSystem::OpenDirectory(const std::string &path, Directory::ListMode listMode) { + std::shared_ptr PartitionFileSystem::OpenDirectoryImpl(const std::string &path, Directory::ListMode listMode) { // PFS doesn't have directories - if (path != "") + if (!path.empty()) return nullptr; std::vector fileList; diff --git a/app/src/main/cpp/skyline/vfs/partition_filesystem.h b/app/src/main/cpp/skyline/vfs/partition_filesystem.h index 5be41be0..31bb75ea 100644 --- a/app/src/main/cpp/skyline/vfs/partition_filesystem.h +++ b/app/src/main/cpp/skyline/vfs/partition_filesystem.h @@ -39,14 +39,15 @@ namespace skyline::vfs { std::shared_ptr backing; //!< The backing file of the filesystem std::unordered_map fileMap; //!< A map that maps file names to their corresponding entry + protected: + std::shared_ptr OpenFileImpl(const std::string &path, Backing::Mode mode) override; + + std::optional GetEntryTypeImpl(const std::string &path) override; + + std::shared_ptr OpenDirectoryImpl(const std::string &path, Directory::ListMode listMode) override; + public: PartitionFileSystem(const std::shared_ptr &backing); - - std::shared_ptr OpenFile(const std::string &path, Backing::Mode mode = {true, false, false}); - - std::optional GetEntryType(const std::string &path); - - std::shared_ptr OpenDirectory(const std::string &path, Directory::ListMode listMode); }; /** diff --git a/app/src/main/cpp/skyline/vfs/region_backing.h b/app/src/main/cpp/skyline/vfs/region_backing.h index daf8aa0b..0765f060 100644 --- a/app/src/main/cpp/skyline/vfs/region_backing.h +++ b/app/src/main/cpp/skyline/vfs/region_backing.h @@ -14,20 +14,20 @@ namespace skyline::vfs { std::shared_ptr backing; //!< The parent backing size_t baseOffset; //!< The offset of the region in the parent backing + protected: + size_t ReadImpl(span output, size_t offset) override { + return backing->Read(output, baseOffset + offset); + } + public: /** * @param file The backing to create the RegionBacking from * @param offset The offset of the region start within the parent backing * @param size The size of the region in the parent backing */ - RegionBacking(const std::shared_ptr &backing, size_t offset, size_t size, Mode mode = {true, false, false}) : Backing(mode, size), backing(backing), baseOffset(offset) {}; - - size_t Read(span output, size_t offset = 0) { - if (!mode.read) - throw exception("Attempting to read a backing that is not readable"); - if (size - offset < output.size()) - throw exception("Trying to read past the end of a region backing: 0x{:X}/0x{:X} (Offset: 0x{:X})", output.size(), size, offset); - return backing->Read(output, baseOffset + offset); - } + RegionBacking(const std::shared_ptr &backing, size_t offset, size_t size, Mode mode = {true, false, false}) : Backing(mode, size), backing(backing), baseOffset(offset) { + if (mode.write || mode.append) + throw exception("Cannot open a RegionBacking as writable"); + }; }; } diff --git a/app/src/main/cpp/skyline/vfs/rom_filesystem.cpp b/app/src/main/cpp/skyline/vfs/rom_filesystem.cpp index 39dcd7a6..8bf927e1 100644 --- a/app/src/main/cpp/skyline/vfs/rom_filesystem.cpp +++ b/app/src/main/cpp/skyline/vfs/rom_filesystem.cpp @@ -49,7 +49,7 @@ namespace skyline::vfs { TraverseDirectory(entry.siblingOffset, path); } - std::shared_ptr RomFileSystem::OpenFile(const std::string &path, Backing::Mode mode) { + std::shared_ptr RomFileSystem::OpenFileImpl(const std::string &path, Backing::Mode mode) { try { const auto &entry{fileMap.at(path)}; return std::make_shared(backing, header.dataOffset + entry.offset, entry.size, mode); @@ -58,7 +58,7 @@ namespace skyline::vfs { } } - std::optional RomFileSystem::GetEntryType(const std::string &path) { + std::optional RomFileSystem::GetEntryTypeImpl(const std::string &path) { if (fileMap.count(path)) return Directory::EntryType::File; else if (directoryMap.count(path)) @@ -67,7 +67,7 @@ namespace skyline::vfs { return std::nullopt; } - std::shared_ptr RomFileSystem::OpenDirectory(const std::string &path, Directory::ListMode listMode) { + std::shared_ptr RomFileSystem::OpenDirectoryImpl(const std::string &path, Directory::ListMode listMode) { try { auto &entry{directoryMap.at(path)}; return std::make_shared(backing, header, entry, listMode); diff --git a/app/src/main/cpp/skyline/vfs/rom_filesystem.h b/app/src/main/cpp/skyline/vfs/rom_filesystem.h index 2883e5cd..4f9bc27d 100644 --- a/app/src/main/cpp/skyline/vfs/rom_filesystem.h +++ b/app/src/main/cpp/skyline/vfs/rom_filesystem.h @@ -32,6 +32,13 @@ namespace skyline { */ void TraverseDirectory(u32 offset, const std::string &path); + protected: + std::shared_ptr OpenFileImpl(const std::string &path, Backing::Mode mode) override; + + std::optional GetEntryTypeImpl(const std::string &path) override; + + std::shared_ptr OpenDirectoryImpl(const std::string &path, Directory::ListMode listMode) override; + public: struct RomFsHeader { u64 headerSize; //!< The size of the header @@ -69,12 +76,6 @@ namespace skyline { std::unordered_map directoryMap; //!< A map that maps directory names to their corresponding entry RomFileSystem(std::shared_ptr backing); - - std::shared_ptr OpenFile(const std::string &path, Backing::Mode mode = {true, false, false}); - - std::optional GetEntryType(const std::string &path); - - std::shared_ptr OpenDirectory(const std::string &path, Directory::ListMode listMode); }; /**