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.
This commit is contained in:
Billy Laws 2021-02-06 14:23:42 +00:00 committed by ◱ Mark
parent 48acb6d369
commit 5f942e2dff
13 changed files with 137 additions and 82 deletions

View File

@ -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 * @brief The Backing class provides abstract access to a storage device, all access can be done without using a specific backing
*/ */
class Backing { class Backing {
protected:
virtual size_t ReadImpl(span <u8> output, size_t offset) = 0;
virtual size_t WriteImpl(span <u8> 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: public:
union Mode { union Mode {
struct { struct {
@ -42,7 +53,15 @@ namespace skyline::vfs {
* @param offset The offset to start reading from * @param offset The offset to start reading from
* @return The amount of bytes read * @return The amount of bytes read
*/ */
virtual size_t Read(span <u8> output, size_t offset = 0) = 0; size_t Read(span <u8> output, size_t offset = 0) {
if (!mode.read)
throw exception("Attempting to read a backing that is not readable");
if ((static_cast<ssize_t>(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 * @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 * @param offset The offset where the input buffer should be written
* @return The amount of bytes written * @return The amount of bytes written
*/ */
virtual size_t Write(span <u8> input, size_t offset = 0) { size_t Write(span <u8> input, size_t offset = 0) {
throw exception("This backing does not support being written to"); if (!mode.write)
throw exception("Attempting to write to a backing that is not writable");
if (input.size() > (static_cast<ssize_t>(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<typename T> template<typename T>
void WriteObject(const T &object, size_t offset = 0) { void WriteObject(const T &object, size_t offset = 0) {
size_t size; size_t lSize;
if ((size = Write(span(reinterpret_cast<u8 *>(&object), sizeof(T)), offset)) != sizeof(T)) if ((lSize = Write(span(reinterpret_cast<u8 *>(&object), sizeof(T)), offset)) != sizeof(T))
throw exception("Object wasn't written fully into output backing: {}/{}", size, sizeof(T)); throw exception("Object wasn't written fully into output backing: {}/{}", lSize, sizeof(T));
} }
/** /**
* @brief Resizes a backing to the given size * @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) { void Resize(size_t pSize) {
throw exception("This backing does not support being resized"); ResizeImpl(pSize);
} }
}; };
} }

View File

@ -6,7 +6,10 @@
namespace skyline::vfs { namespace skyline::vfs {
constexpr size_t SectorSize{0x10}; constexpr size_t SectorSize{0x10};
CtrEncryptedBacking::CtrEncryptedBacking(crypto::KeyStore::Key128 ctr, crypto::KeyStore::Key128 key, std::shared_ptr<Backing> 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> 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) { void CtrEncryptedBacking::UpdateCtr(u64 offset) {
offset >>= 4; offset >>= 4;
@ -15,7 +18,7 @@ namespace skyline::vfs {
cipher.SetIV(ctr); cipher.SetIV(ctr);
} }
size_t CtrEncryptedBacking::Read(span<u8> output, size_t offset) { size_t CtrEncryptedBacking::ReadImpl(span<u8> output, size_t offset) {
size_t size{output.size()}; size_t size{output.size()};
if (size == 0) if (size == 0)
return 0; return 0;

View File

@ -24,9 +24,10 @@ namespace skyline::vfs {
*/ */
void UpdateCtr(u64 offset); void UpdateCtr(u64 offset);
protected:
size_t ReadImpl(span<u8> output, size_t offset) override;
public: public:
CtrEncryptedBacking(crypto::KeyStore::Key128 ctr, crypto::KeyStore::Key128 key, std::shared_ptr<Backing> backing, size_t baseOffset); CtrEncryptedBacking(crypto::KeyStore::Key128 ctr, crypto::KeyStore::Key128 key, std::shared_ptr<Backing> backing, size_t baseOffset);
size_t Read(span<u8> output, size_t offset = 0) override;
}; };
} }

View File

@ -11,6 +11,23 @@ namespace skyline::vfs {
* @brief The FileSystem class represents an abstract filesystem with child files and folders * @brief The FileSystem class represents an abstract filesystem with child files and folders
*/ */
class FileSystem { 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<Backing> OpenFileImpl(const std::string &path, Backing::Mode mode) = 0;
virtual std::optional<Directory::EntryType> GetEntryTypeImpl(const std::string &path) = 0;
virtual std::shared_ptr<Directory> OpenDirectoryImpl(const std::string &path, Directory::ListMode listMode) {
throw exception("This filesystem does not support opening directories");
};
public: public:
FileSystem() = default; FileSystem() = default;
@ -27,8 +44,8 @@ namespace skyline::vfs {
* @param size The size of the file to create * @param size The size of the file to create
* @return Whether creating the file succeeded * @return Whether creating the file succeeded
*/ */
virtual bool CreateFile(const std::string &path, size_t size) { bool CreateFile(const std::string &path, size_t size) {
throw exception("This filesystem does not support creating files"); return CreateFileImpl(path, size);
}; };
/** /**
@ -37,8 +54,8 @@ namespace skyline::vfs {
* @param parents Whether all parent directories in the given path should be created * @param parents Whether all parent directories in the given path should be created
* @return Whether creating the directory succeeded * @return Whether creating the directory succeeded
*/ */
virtual bool CreateDirectory(const std::string &path, bool parents) { bool CreateDirectory(const std::string &path, bool parents) {
throw exception("This filesystem does not support creating directories"); return CreateDirectoryImpl(path, parents);
}; };
/** /**
@ -47,14 +64,21 @@ namespace skyline::vfs {
* @param mode The mode to open the file with * @param mode The mode to open the file with
* @return A shared pointer to a Backing object of the file * @return A shared pointer to a Backing object of the file
*/ */
virtual std::shared_ptr<Backing> OpenFile(const std::string &path, Backing::Mode mode = {true, false, false}) = 0; std::shared_ptr<Backing> 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 * @brief Queries the type of the entry given by path
* @param path The path to the entry * @param path The path to the entry
* @return The type of the entry, if present * @return The type of the entry, if present
*/ */
virtual std::optional<Directory::EntryType> GetEntryType(const std::string &path) = 0; std::optional<Directory::EntryType> GetEntryType(const std::string &path) {
return GetEntryTypeImpl(path);
}
/** /**
* @brief Checks if a given file exists in a filesystem * @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 * @param listMode The list mode for the directory
* @return A shared pointer to a Directory object of the directory * @return A shared pointer to a Directory object of the directory
*/ */
virtual std::shared_ptr<Directory> OpenDirectory(const std::string &path, Directory::ListMode listMode) { std::shared_ptr<Directory> OpenDirectory(const std::string &path, Directory::ListMode listMode = {true, true}) {
throw exception("This filesystem does not support opening directories"); if (!listMode.raw)
throw exception("Cannot open a directory with an empty listMode");
return OpenDirectoryImpl(path, listMode);
}; };
}; };
} }

View File

@ -20,10 +20,7 @@ namespace skyline::vfs {
close(fd); close(fd);
} }
size_t OsBacking::Read(span<u8> output, size_t offset) { size_t OsBacking::ReadImpl(span<u8> output, size_t offset) {
if (!mode.read)
throw exception("Attempting to read a backing that is not readable");
auto ret{pread64(fd, output.data(), output.size(), offset)}; auto ret{pread64(fd, output.data(), output.size(), offset)};
if (ret < 0) if (ret < 0)
throw exception("Failed to read from fd: {}", strerror(errno)); throw exception("Failed to read from fd: {}", strerror(errno));
@ -31,10 +28,7 @@ namespace skyline::vfs {
return static_cast<size_t>(ret); return static_cast<size_t>(ret);
} }
size_t OsBacking::Write(span<u8> input, size_t offset) { size_t OsBacking::WriteImpl(span<u8> input, size_t offset) {
if (!mode.write)
throw exception("Attempting to write to a backing that is not writable");
auto ret{pwrite64(fd, input.data(), input.size(), offset)}; auto ret{pwrite64(fd, input.data(), input.size(), offset)};
if (ret < 0) if (ret < 0)
throw exception("Failed to write to fd: {}", strerror(errno)); throw exception("Failed to write to fd: {}", strerror(errno));
@ -42,7 +36,7 @@ namespace skyline::vfs {
return static_cast<size_t>(ret); return static_cast<size_t>(ret);
} }
void OsBacking::Resize(size_t pSize) { void OsBacking::ResizeImpl(size_t pSize) {
int ret{ftruncate(fd, pSize)}; int ret{ftruncate(fd, pSize)};
if (ret < 0) if (ret < 0)
throw exception("Failed to resize file: {}", strerror(errno)); throw exception("Failed to resize file: {}", strerror(errno));

View File

@ -14,6 +14,13 @@ namespace skyline::vfs {
int fd; //!< An FD to the backing int fd; //!< An FD to the backing
bool closable; //!< Whether the FD can be closed when the backing is destroyed bool closable; //!< Whether the FD can be closed when the backing is destroyed
protected:
size_t ReadImpl(span<u8> output, size_t offset) override;
size_t WriteImpl(span<u8> input, size_t offset) override;
void ResizeImpl(size_t size) override;
public: public:
/** /**
* @param fd The file descriptor of the backing * @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(int fd, bool closable = false, Mode = {true, false, false});
~OsBacking(); ~OsBacking();
size_t Read(span<u8> output, size_t offset = 0);
size_t Write(span<u8> input, size_t offset = 0);
void Resize(size_t size);
}; };
} }

View File

@ -15,7 +15,7 @@ namespace skyline::vfs {
throw exception("Error creating the OS filesystem backing directory"); 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}; auto fullPath{basePath + path};
// Create a directory that will hold the file // Create a directory that will hold the file
@ -38,7 +38,7 @@ namespace skyline::vfs {
return true; return true;
} }
bool OsFileSystem::CreateDirectory(const std::string &path, bool parents) { bool OsFileSystem::CreateDirectoryImpl(const std::string &path, bool parents) {
if (!parents) { if (!parents) {
int ret{mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)}; int ret{mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)};
return ret == 0 || errno == EEXIST; return ret == 0 || errno == EEXIST;
@ -58,10 +58,7 @@ namespace skyline::vfs {
return true; return true;
} }
std::shared_ptr<Backing> OsFileSystem::OpenFile(const std::string &path, Backing::Mode mode) { std::shared_ptr<Backing> OsFileSystem::OpenFileImpl(const std::string &path, Backing::Mode mode) {
if (!(mode.read || mode.write))
throw exception("Cannot open a file that is neither readable or writable");
int fd{open((basePath + path).c_str(), (mode.read && mode.write) ? O_RDWR : (mode.write ? O_WRONLY : O_RDONLY))}; int fd{open((basePath + path).c_str(), (mode.read && mode.write) ? O_RDWR : (mode.write ? O_WRONLY : O_RDONLY))};
if (fd < 0) if (fd < 0)
throw exception("Failed to open file at '{}': {}", path, strerror(errno)); throw exception("Failed to open file at '{}': {}", path, strerror(errno));
@ -69,7 +66,7 @@ namespace skyline::vfs {
return std::make_shared<OsBacking>(fd, true, mode); return std::make_shared<OsBacking>(fd, true, mode);
} }
std::optional<Directory::EntryType> OsFileSystem::GetEntryType(const std::string &path) { std::optional<Directory::EntryType> OsFileSystem::GetEntryTypeImpl(const std::string &path) {
auto fullPath{basePath + path}; auto fullPath{basePath + path};
auto directory{opendir(fullPath.c_str())}; auto directory{opendir(fullPath.c_str())};
@ -84,7 +81,7 @@ namespace skyline::vfs {
return std::nullopt; return std::nullopt;
} }
std::shared_ptr<Directory> OsFileSystem::OpenDirectory(const std::string &path, Directory::ListMode listMode) { std::shared_ptr<Directory> OsFileSystem::OpenDirectoryImpl(const std::string &path, Directory::ListMode listMode) {
return std::make_shared<OsFileSystemDirectory>(basePath + path, listMode); return std::make_shared<OsFileSystemDirectory>(basePath + path, listMode);
} }

View File

@ -13,18 +13,19 @@ namespace skyline::vfs {
private: private:
std::string basePath; //!< The base path for filesystem operations 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<Backing> OpenFileImpl(const std::string &path, Backing::Mode mode) override;
std::optional<Directory::EntryType> GetEntryTypeImpl(const std::string &path) override;
std::shared_ptr<Directory> OpenDirectoryImpl(const std::string &path, Directory::ListMode listMode) override;
public: public:
OsFileSystem(const std::string &basePath); 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<Backing> OpenFile(const std::string &path, Backing::Mode mode = {true, false, false});
std::optional<Directory::EntryType> GetEntryType(const std::string &path);
std::shared_ptr<Directory> OpenDirectory(const std::string &path, Directory::ListMode listMode);
}; };
/** /**

View File

@ -31,7 +31,7 @@ namespace skyline::vfs {
} }
} }
std::shared_ptr<Backing> PartitionFileSystem::OpenFile(const std::string &path, Backing::Mode mode) { std::shared_ptr<Backing> PartitionFileSystem::OpenFileImpl(const std::string &path, Backing::Mode mode) {
try { try {
auto &entry{fileMap.at(path)}; auto &entry{fileMap.at(path)};
return std::make_shared<RegionBacking>(backing, fileDataOffset + entry.offset, entry.size, mode); return std::make_shared<RegionBacking>(backing, fileDataOffset + entry.offset, entry.size, mode);
@ -40,16 +40,16 @@ namespace skyline::vfs {
} }
} }
std::optional<Directory::EntryType> PartitionFileSystem::GetEntryType(const std::string &path) { std::optional<Directory::EntryType> PartitionFileSystem::GetEntryTypeImpl(const std::string &path) {
if (fileMap.count(path)) if (fileMap.count(path))
return Directory::EntryType::File; return Directory::EntryType::File;
return std::nullopt; return std::nullopt;
} }
std::shared_ptr<Directory> PartitionFileSystem::OpenDirectory(const std::string &path, Directory::ListMode listMode) { std::shared_ptr<Directory> PartitionFileSystem::OpenDirectoryImpl(const std::string &path, Directory::ListMode listMode) {
// PFS doesn't have directories // PFS doesn't have directories
if (path != "") if (!path.empty())
return nullptr; return nullptr;
std::vector<Directory::Entry> fileList; std::vector<Directory::Entry> fileList;

View File

@ -39,14 +39,15 @@ namespace skyline::vfs {
std::shared_ptr<Backing> backing; //!< The backing file of the filesystem std::shared_ptr<Backing> backing; //!< The backing file of the filesystem
std::unordered_map<std::string, PartitionFileEntry> fileMap; //!< A map that maps file names to their corresponding entry std::unordered_map<std::string, PartitionFileEntry> fileMap; //!< A map that maps file names to their corresponding entry
protected:
std::shared_ptr<Backing> OpenFileImpl(const std::string &path, Backing::Mode mode) override;
std::optional<Directory::EntryType> GetEntryTypeImpl(const std::string &path) override;
std::shared_ptr<Directory> OpenDirectoryImpl(const std::string &path, Directory::ListMode listMode) override;
public: public:
PartitionFileSystem(const std::shared_ptr<Backing> &backing); PartitionFileSystem(const std::shared_ptr<Backing> &backing);
std::shared_ptr<Backing> OpenFile(const std::string &path, Backing::Mode mode = {true, false, false});
std::optional<Directory::EntryType> GetEntryType(const std::string &path);
std::shared_ptr<Directory> OpenDirectory(const std::string &path, Directory::ListMode listMode);
}; };
/** /**

View File

@ -14,20 +14,20 @@ namespace skyline::vfs {
std::shared_ptr<vfs::Backing> backing; //!< The parent backing std::shared_ptr<vfs::Backing> backing; //!< The parent backing
size_t baseOffset; //!< The offset of the region in the parent backing size_t baseOffset; //!< The offset of the region in the parent backing
protected:
size_t ReadImpl(span <u8> output, size_t offset) override {
return backing->Read(output, baseOffset + offset);
}
public: public:
/** /**
* @param file The backing to create the RegionBacking from * @param file The backing to create the RegionBacking from
* @param offset The offset of the region start within the parent backing * @param offset The offset of the region start within the parent backing
* @param size The size of the region in the parent backing * @param size The size of the region in the parent backing
*/ */
RegionBacking(const std::shared_ptr<vfs::Backing> &backing, size_t offset, size_t size, Mode mode = {true, false, false}) : Backing(mode, size), backing(backing), baseOffset(offset) {}; RegionBacking(const std::shared_ptr<vfs::Backing> &backing, size_t offset, size_t size, Mode mode = {true, false, false}) : Backing(mode, size), backing(backing), baseOffset(offset) {
if (mode.write || mode.append)
size_t Read(span <u8> output, size_t offset = 0) { throw exception("Cannot open a RegionBacking as writable");
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);
}
}; };
} }

View File

@ -49,7 +49,7 @@ namespace skyline::vfs {
TraverseDirectory(entry.siblingOffset, path); TraverseDirectory(entry.siblingOffset, path);
} }
std::shared_ptr<Backing> RomFileSystem::OpenFile(const std::string &path, Backing::Mode mode) { std::shared_ptr<Backing> RomFileSystem::OpenFileImpl(const std::string &path, Backing::Mode mode) {
try { try {
const auto &entry{fileMap.at(path)}; const auto &entry{fileMap.at(path)};
return std::make_shared<RegionBacking>(backing, header.dataOffset + entry.offset, entry.size, mode); return std::make_shared<RegionBacking>(backing, header.dataOffset + entry.offset, entry.size, mode);
@ -58,7 +58,7 @@ namespace skyline::vfs {
} }
} }
std::optional<Directory::EntryType> RomFileSystem::GetEntryType(const std::string &path) { std::optional<Directory::EntryType> RomFileSystem::GetEntryTypeImpl(const std::string &path) {
if (fileMap.count(path)) if (fileMap.count(path))
return Directory::EntryType::File; return Directory::EntryType::File;
else if (directoryMap.count(path)) else if (directoryMap.count(path))
@ -67,7 +67,7 @@ namespace skyline::vfs {
return std::nullopt; return std::nullopt;
} }
std::shared_ptr<Directory> RomFileSystem::OpenDirectory(const std::string &path, Directory::ListMode listMode) { std::shared_ptr<Directory> RomFileSystem::OpenDirectoryImpl(const std::string &path, Directory::ListMode listMode) {
try { try {
auto &entry{directoryMap.at(path)}; auto &entry{directoryMap.at(path)};
return std::make_shared<RomFileSystemDirectory>(backing, header, entry, listMode); return std::make_shared<RomFileSystemDirectory>(backing, header, entry, listMode);

View File

@ -32,6 +32,13 @@ namespace skyline {
*/ */
void TraverseDirectory(u32 offset, const std::string &path); void TraverseDirectory(u32 offset, const std::string &path);
protected:
std::shared_ptr<Backing> OpenFileImpl(const std::string &path, Backing::Mode mode) override;
std::optional<Directory::EntryType> GetEntryTypeImpl(const std::string &path) override;
std::shared_ptr<Directory> OpenDirectoryImpl(const std::string &path, Directory::ListMode listMode) override;
public: public:
struct RomFsHeader { struct RomFsHeader {
u64 headerSize; //!< The size of the header u64 headerSize; //!< The size of the header
@ -69,12 +76,6 @@ namespace skyline {
std::unordered_map<std::string, RomFsDirectoryEntry> directoryMap; //!< A map that maps directory names to their corresponding entry std::unordered_map<std::string, RomFsDirectoryEntry> directoryMap; //!< A map that maps directory names to their corresponding entry
RomFileSystem(std::shared_ptr<Backing> backing); RomFileSystem(std::shared_ptr<Backing> backing);
std::shared_ptr<Backing> OpenFile(const std::string &path, Backing::Mode mode = {true, false, false});
std::optional<Directory::EntryType> GetEntryType(const std::string &path);
std::shared_ptr<Directory> OpenDirectory(const std::string &path, Directory::ListMode listMode);
}; };
/** /**