/* * Copyright 2015 - 2024 gary@drinkingtea.net * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #pragma once #include #include #include #include #include #include "directory.hpp" namespace ox { namespace detail { static inline void fsBuffFree(char *buff) noexcept { safeDelete(buff); } } class FileSystem { public: virtual ~FileSystem() noexcept = default; virtual Error mkdir(CRStringView path, bool recursive) noexcept = 0; /** * Moves an entry from one directory to another. * @param src the path to the file * @param dest the path of the destination directory */ virtual Error move(CRStringView src, CRStringView dest) noexcept = 0; Error read(const FileAddress &addr, void *buffer, std::size_t size) noexcept; Result read(const FileAddress &addr) noexcept; Result read(CRStringView path) noexcept; inline Error read(CRStringView path, void *buffer, std::size_t buffSize) noexcept { return readFilePath(path, buffer, buffSize); } inline Error read(uint64_t inode, void *buffer, std::size_t buffSize) noexcept { return readFileInode(inode, buffer, buffSize); } Error read(const FileAddress &addr, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept; virtual Result> ls(CRStringView dir) const noexcept = 0; virtual Error remove(CRStringView path, bool recursive) noexcept = 0; Error remove(const FileAddress &addr, bool recursive = false) noexcept; virtual Error resize(uint64_t size, void *buffer) noexcept = 0; Error write(CRStringView path, const void *buffer, uint64_t size) noexcept { return writeFilePath(path, buffer, size, FileType::NormalFile); } Error write(CRStringView path, ox::Span const&buff) noexcept { return write(path, buff.data(), buff.size(), FileType::NormalFile); } Error write(uint64_t inode, const void *buffer, uint64_t size) noexcept { return write(inode, buffer, size, FileType::NormalFile); } Error write(uint64_t inode, ox::Span const&buff) noexcept { return write(inode, buff.data(), buff.size(), FileType::NormalFile); } Error write(const FileAddress &addr, const void *buffer, uint64_t size, FileType fileType = FileType::NormalFile) noexcept; inline Error write(CRStringView path, const void *buffer, uint64_t size, FileType fileType) noexcept { return writeFilePath(path, buffer, size, fileType); } inline Error write(uint64_t inode, const void *buffer, uint64_t size, FileType fileType) noexcept { return writeFileInode(inode, buffer, size, fileType); } inline Result stat(uint64_t inode) const noexcept { return statInode(inode); } inline Result stat(CRStringView path) const noexcept { return statPath(path); } Result stat(const FileAddress &addr) const noexcept; [[nodiscard]] virtual uint64_t spaceNeeded(uint64_t size) const noexcept = 0; [[nodiscard]] virtual Result available() const noexcept = 0; [[nodiscard]] virtual Result size() const noexcept = 0; [[nodiscard]] virtual char *buff() noexcept = 0; virtual Error walk(Error(*cb)(uint8_t, uint64_t, uint64_t)) noexcept = 0; [[nodiscard]] virtual bool valid() const noexcept = 0; protected: virtual Result statInode(uint64_t inode) const noexcept = 0; virtual Result statPath(CRStringView path) const noexcept = 0; virtual Error readFilePath(CRStringView path, void *buffer, std::size_t buffSize) noexcept = 0; virtual Error readFileInode(uint64_t inode, void *buffer, std::size_t size) noexcept = 0; virtual Error readFileInodeRange(uint64_t inode, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept = 0; virtual Error writeFilePath(CRStringView path, const void *buffer, uint64_t size, FileType fileType) noexcept = 0; virtual Error writeFileInode(uint64_t inode, const void *buffer, uint64_t size, FileType fileType) noexcept = 0; }; class MemFS: public FileSystem { public: Result directAccess(const FileAddress &addr) const noexcept; inline Result directAccess(CRStringView path) const noexcept { return directAccessPath(path); } inline Result directAccess(uint64_t inode) const noexcept { return directAccessInode(inode); } protected: virtual Result directAccessPath(CRStringView path) const noexcept = 0; virtual Result directAccessInode(uint64_t inode) const noexcept = 0; }; /** * FileSystemTemplate used to create file system that wraps around a FileStore, * taking an inode size and a directory type as parameters. * * Note: Directory parameter must have a default constructor. */ template class FileSystemTemplate: public MemFS { private: static constexpr auto InodeFsData = 2; struct OX_PACKED FileSystemData { LittleEndian rootDirInode; }; FileStore m_fs; void(*m_freeBuffer)(char*) = nullptr; public: FileSystemTemplate() noexcept = default; FileSystemTemplate(void *buffer, uint64_t bufferSize, void(*freeBuffer)(char*) = detail::fsBuffFree) noexcept; explicit FileSystemTemplate(ox::Buffer &buffer) noexcept; explicit FileSystemTemplate(FileStore fs) noexcept; ~FileSystemTemplate() noexcept override; static Error format(void *buff, uint64_t buffSize) noexcept; Error mkdir(CRStringView path, bool recursive) noexcept override; Error move(CRStringView src, CRStringView dest) noexcept override; Error readFilePath(CRStringView path, void *buffer, std::size_t buffSize) noexcept override; Result directAccessPath(CRStringView) const noexcept override; Error readFileInode(uint64_t inode, void *buffer, std::size_t size) noexcept override; Error readFileInodeRange(uint64_t inode, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept override; Result directAccessInode(uint64_t) const noexcept override; Result> ls(CRStringView dir) const noexcept override; template Error ls(CRStringView path, F cb) const; Error remove(CRStringView path, bool recursive) noexcept override; /** * Resizes FileSystem to minimum possible size. */ Error resize() noexcept; Error resize(uint64_t size, void *buffer) noexcept override; Error writeFilePath(CRStringView path, const void *buffer, uint64_t size, FileType fileType) noexcept override; Error writeFileInode(uint64_t inode, const void *buffer, uint64_t size, FileType fileType) noexcept override; Result statInode(uint64_t inode) const noexcept override; Result statPath(CRStringView path) const noexcept override; [[nodiscard]] uint64_t spaceNeeded(uint64_t size) const noexcept override; Result available() const noexcept override; Result size() const noexcept override; char *buff() noexcept override; Error walk(Error(*cb)(uint8_t, uint64_t, uint64_t)) noexcept override; [[nodiscard]] bool valid() const noexcept override; private: Result fileSystemData() const noexcept; /** * Finds the inode ID at the given path. */ Result find(CRStringView path) const noexcept; Result rootDir() const noexcept; }; template FileSystemTemplate::FileSystemTemplate(FileStore fs) noexcept { m_fs = fs; } template FileSystemTemplate::FileSystemTemplate(ox::Buffer &buffer) noexcept: m_fs(buffer.data(), static_cast(buffer.size())) { } template FileSystemTemplate::FileSystemTemplate(void *buffer, uint64_t bufferSize, void(*freeBuffer)(char*)) noexcept: m_fs(buffer, static_cast(bufferSize)), m_freeBuffer(freeBuffer) { } template FileSystemTemplate::~FileSystemTemplate() noexcept { if (m_freeBuffer && m_fs.buff()) { m_freeBuffer(m_fs.buff()); } } template Error FileSystemTemplate::format(void *buff, uint64_t buffSize) noexcept { oxReturnError(FileStore::format(buff, static_cast(buffSize))); FileStore fs(buff, static_cast(buffSize)); constexpr auto rootDirInode = MaxValue / 2; Directory rootDir(fs, rootDirInode); oxReturnError(rootDir.init()); FileSystemData fd; fd.rootDirInode = rootDirInode; oxTracef("ox.fs.FileSystemTemplate.format", "rootDirInode: {}", fd.rootDirInode.get()); oxReturnError(fs.write(InodeFsData, &fd, sizeof(fd))); if (!fs.read(fd.rootDirInode).valid()) { oxTrace("ox.fs.FileSystemTemplate.format.error", "FileSystemTemplate::format did not correctly create root directory"); return OxError(1); } return OxError(0); } template Error FileSystemTemplate::mkdir(CRStringView path, bool recursive) noexcept { oxTracef("ox.fs.FileSystemTemplate.mkdir", "path: {}, recursive: {}", path, recursive); oxRequireM(rootDir, this->rootDir()); return rootDir.mkdir(path, recursive); } template Error FileSystemTemplate::move(CRStringView src, CRStringView dest) noexcept { oxRequire(fd, fileSystemData()); Directory rootDir(m_fs, fd.rootDirInode); oxRequireM(inode, rootDir.find(src)); oxReturnError(rootDir.write(dest, inode)); oxReturnError(rootDir.remove(src)); return OxError(0); } template Error FileSystemTemplate::readFilePath(CRStringView path, void *buffer, std::size_t buffSize) noexcept { oxRequire(fd, fileSystemData()); Directory rootDir(m_fs, fd.rootDirInode); oxRequire(s, stat(path)); if (s.size > buffSize) { return OxError(1, "Buffer to small to load file"); } return readFileInodeRange(s.inode, 0, static_cast(s.size), buffer, &buffSize); } template Result FileSystemTemplate::directAccessPath(CRStringView path) const noexcept { oxRequire(fd, fileSystemData()); Directory rootDir(m_fs, fd.rootDirInode); oxRequire(inode, rootDir.find(path)); return directAccessInode(inode); } template Error FileSystemTemplate::readFileInode(uint64_t inode, void *buffer, std::size_t buffSize) noexcept { oxRequire(s, stat(inode)); if (s.size > buffSize) { return OxError(1, "Buffer to small to load file"); } return readFileInodeRange(inode, 0, static_cast(s.size), buffer, &buffSize); } template Error FileSystemTemplate::readFileInodeRange(uint64_t inode, std::size_t readStart, std::size_t readSize, void *buffer, std::size_t *size) noexcept { return m_fs.read(inode, readStart, readSize, reinterpret_cast(buffer), size); } template Result FileSystemTemplate::directAccessInode(uint64_t inode) const noexcept { auto data = m_fs.read(inode); if (!data.valid()) { return OxError(1, "Data not valid"); } return reinterpret_cast(data.get()); } template Result> FileSystemTemplate::ls(CRStringView path) const noexcept { Vector out; oxReturnError(ls(path, [&out](CRStringView name, typename FileStore::InodeId_t) { out.emplace_back(name); return OxError(0); })); return out; } template template Error FileSystemTemplate::ls(CRStringView path, F cb) const { oxTracef("ox.fs.FileSystemTemplate.ls", "path: {}", path); oxRequire(s, stat(path)); Directory dir(m_fs, s.inode); return dir.ls(cb); } template Error FileSystemTemplate::remove(CRStringView path, bool recursive) noexcept { oxRequire(fd, fileSystemData()); Directory rootDir(m_fs, fd.rootDirInode); oxRequire(inode, rootDir.find(path)); oxRequire(st, statInode(inode)); if (st.fileType == FileType::NormalFile || recursive) { if (auto err = rootDir.remove(path)) { // removal failed, try putting the index back oxLogError(rootDir.write(path, inode)); return err; } } else { oxTrace("FileSystemTemplate.remove.fail", "Tried to remove directory without recursive setting."); return OxError(1); } return OxError(0); } template Error FileSystemTemplate::resize() noexcept { return m_fs.resize(); } template Error FileSystemTemplate::resize(uint64_t size, void *buffer) noexcept { oxReturnError(m_fs.resize(static_cast(size), buffer)); return OxError(0); } template Error FileSystemTemplate::writeFilePath(CRStringView path, const void *buffer, uint64_t size, FileType fileType) noexcept { auto [inode, err] = find(path); if (err) { oxRequire(generatedId, m_fs.generateInodeId()); inode = generatedId; oxRequireM(rootDir, this->rootDir()); oxReturnError(rootDir.write(path, inode)); } oxReturnError(writeFileInode(inode, buffer, size, fileType)); return OxError(0); } template Error FileSystemTemplate::writeFileInode(uint64_t inode, const void *buffer, uint64_t size, FileType fileType) noexcept { return m_fs.write(inode, buffer, static_cast(size), static_cast(fileType)); } template Result FileSystemTemplate::statInode(uint64_t inode) const noexcept { oxRequire(s, m_fs.stat(inode)); FileStat out; out.inode = s.inode; out.links = s.links; out.size = s.size; out.fileType = static_cast(s.fileType); return out; } template Result FileSystemTemplate::statPath(CRStringView path) const noexcept { oxRequire(inode, find(path)); return stat(inode); } template uint64_t FileSystemTemplate::spaceNeeded(uint64_t size) const noexcept { return m_fs.spaceNeeded(static_cast(size)); } template Result FileSystemTemplate::available() const noexcept { return m_fs.available(); } template Result FileSystemTemplate::size() const noexcept { return m_fs.size(); } template char *FileSystemTemplate::buff() noexcept { return m_fs.buff(); } template Error FileSystemTemplate::walk(Error(*cb)(uint8_t, uint64_t, uint64_t)) noexcept { return m_fs.walk(cb); } template bool FileSystemTemplate::valid() const noexcept { return m_fs.valid(); } template Result::FileSystemData> FileSystemTemplate::fileSystemData() const noexcept { FileSystemData fd; oxReturnError(m_fs.read(InodeFsData, &fd, sizeof(fd))); return fd; } template Result FileSystemTemplate::find(CRStringView path) const noexcept { oxRequire(fd, fileSystemData()); // return root as a special case if (path == "/") { return static_cast(fd.rootDirInode); } Directory rootDir(m_fs, fd.rootDirInode); oxRequire(out, rootDir.find(path)); return static_cast(out); } template Result FileSystemTemplate::rootDir() const noexcept { oxRequire(fd, fileSystemData()); return Directory(m_fs, fd.rootDirInode); } extern template class FileSystemTemplate; extern template class FileSystemTemplate; using FileSystem16 = FileSystemTemplate; using FileSystem32 = FileSystemTemplate; }