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
*/
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:
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 <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
@ -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 <u8> input, size_t offset = 0) {
throw exception("This backing does not support being written to");
size_t Write(span <u8> 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<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>
void WriteObject(const T &object, size_t offset = 0) {
size_t size;
if ((size = Write(span(reinterpret_cast<u8 *>(&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<u8 *>(&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);
}
};
}

View File

@ -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> 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) {
offset >>= 4;
@ -15,7 +18,7 @@ namespace skyline::vfs {
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()};
if (size == 0)
return 0;

View File

@ -24,9 +24,10 @@ namespace skyline::vfs {
*/
void UpdateCtr(u64 offset);
protected:
size_t ReadImpl(span<u8> output, size_t offset) override;
public:
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
*/
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:
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<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
* @param path The path to the entry
* @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
@ -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<Directory> OpenDirectory(const std::string &path, Directory::ListMode listMode) {
throw exception("This filesystem does not support opening directories");
std::shared_ptr<Directory> 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);
};
};
}

View File

@ -20,10 +20,7 @@ namespace skyline::vfs {
close(fd);
}
size_t OsBacking::Read(span<u8> output, size_t offset) {
if (!mode.read)
throw exception("Attempting to read a backing that is not readable");
size_t OsBacking::ReadImpl(span<u8> 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<size_t>(ret);
}
size_t OsBacking::Write(span<u8> input, size_t offset) {
if (!mode.write)
throw exception("Attempting to write to a backing that is not writable");
size_t OsBacking::WriteImpl(span<u8> 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<size_t>(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));

View File

@ -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<u8> output, size_t offset) override;
size_t WriteImpl(span<u8> 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<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");
}
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<Backing> 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<Backing> 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<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 directory{opendir(fullPath.c_str())};
@ -84,7 +81,7 @@ namespace skyline::vfs {
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);
}

View File

@ -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<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:
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 {
auto &entry{fileMap.at(path)};
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))
return Directory::EntryType::File;
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
if (path != "")
if (!path.empty())
return nullptr;
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::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:
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
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:
/**
* @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<vfs::Backing> &backing, size_t offset, size_t size, Mode mode = {true, false, false}) : Backing(mode, size), backing(backing), baseOffset(offset) {};
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 (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<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)
throw exception("Cannot open a RegionBacking as writable");
};
};
}

View File

@ -49,7 +49,7 @@ namespace skyline::vfs {
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 {
const auto &entry{fileMap.at(path)};
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))
return Directory::EntryType::File;
else if (directoryMap.count(path))
@ -67,7 +67,7 @@ namespace skyline::vfs {
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 {
auto &entry{directoryMap.at(path)};
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);
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:
struct RomFsHeader {
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
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);
};
/**