vc5/src/fs.hpp
2026-02-02 19:32:17 +01:00

214 lines
5.3 KiB
C++

#pragma once
#include "block_device.hpp"
#include "types.hpp"
#include <bitset>
#include <concepts>
#include <cstddef>
#include <expected>
#include <string_view>
#include <type_traits>
#include <utility>
namespace vc5::filesys {
constexpr u16 block_size = 0x200;
constexpr u16 name_max_len = 256;
struct DirEntry {
u16 inode;
u16 next;
u16 name_len;
std::array<char, name_max_len> name;
template <typename Writer> void write(Writer& w) const
{
w << inode << next << name_len << name;
}
template <typename Reader> auto read(Reader& r)
{
r >> inode >> next >> name_len >> name;
}
};
constexpr u16 inode_size = 32;
constexpr u16 inodes_per_block = block_size / inode_size;
enum class INodeType : u16 {
File = 0,
Dir = 1,
Link = 2,
};
constexpr u16 inode_type_bitoffset = 0;
constexpr u16 inode_type_bitmask = 0b11;
constexpr u16 inode_ndir_blocks = 8;
constexpr u16 inode_ind_block = inode_ndir_blocks;
constexpr u16 inode_dind_block = inode_ind_block + 1;
constexpr u16 inode_blocks_count = inode_dind_block + 1;
struct INode {
u16 mode;
u16 blocks_count;
u16 links_count;
u16 last_block_size;
u32 size;
std::array<u16, inode_blocks_count> blocks;
template <typename Writer> void write(Writer& w) const
{
w << mode << blocks_count << links_count << last_block_size << size
<< blocks;
}
template <typename Reader> auto read(Reader& r)
{
r >> mode >> blocks_count >> links_count >> last_block_size >> size
>> blocks;
}
};
static_assert(sizeof(INode) <= inode_size);
constexpr u16 sb_loc = 3;
constexpr u16 blocks_bitmap_loc = 4;
constexpr u16 blocks_max_count = block_size * 8;
constexpr u16 iblocks_max_count = 32;
constexpr u16 inodes_max_count = block_size / inode_size * iblocks_max_count;
struct Superblock {
u16 volume_id;
u16 fs_version;
u16 blocks_count;
u16 blocks_free;
u16 root_inode;
u16 inode_blocks_count;
std::array<u16, iblocks_max_count> iblocks;
std::bitset<inodes_max_count> inode_bitmap;
template <typename Writer> void write(Writer& w) const
{
w << volume_id << fs_version << blocks_count << blocks_free
<< root_inode << inode_blocks_count << iblocks << inode_bitmap;
}
template <typename Reader> auto read(Reader& r)
{
r >> volume_id >> fs_version >> blocks_count >> blocks_free
>> root_inode >> inode_blocks_count >> iblocks >> inode_bitmap;
}
};
static_assert(sizeof(Superblock) <= block_size);
struct BlocksBitmap {
std::bitset<blocks_max_count> bitmap;
template <typename Writer> void write(Writer& w) const
{
w << bitmap;
}
template <typename Reader> auto read(Reader& r)
{
r >> bitmap;
}
};
template <typename Int>
requires std::is_integral_v<Int>
struct IntBlock {
std::array<u16, block_size / sizeof(Int)> data;
template <typename Writer> void write(Writer& w) const
{
w << data;
}
template <typename Reader> auto read(Reader& r)
{
r >> data;
}
};
static_assert(sizeof(BlocksBitmap) <= block_size);
class Fs {
public:
explicit Fs(BlockDevice& disk)
: m_disk(&disk)
{
}
template <typename T> using Result = std::expected<T, std::string_view>;
static auto init_new(BlockDevice& disk, u16 volume_id, u16 block_count)
-> Result<Fs>
{
auto fs = Fs(disk);
if (auto result = fs.init_new(volume_id, block_count); not result)
return std::unexpected(result.error());
return fs;
}
static auto load(BlockDevice& disk) -> Result<Fs>
{
auto fs = Fs(disk);
if (auto result = fs.init_from_existing(); not result)
return std::unexpected(result.error());
return fs;
}
auto root_inode_id() const -> Result<u16>
{
return m_superblock.root_inode;
}
auto make_file(u16 dir_inode_id, std::string_view name) -> Result<u16>;
private:
auto init_new(u16 volume_id, u16 block_count) -> Result<void>;
auto init_from_existing() -> Result<void>;
auto append_to_directory(INode& dir_inode, const DirEntry& entry)
-> Result<void>;
auto make_directory_inode() -> Result<u16>;
auto inode_last_block_id(const INode& inode) -> Result<u16>;
auto write_inode(u16 inode_id, const INode& inode) -> Result<void>;
auto read_inode(u16 inode_id) -> Result<INode>;
auto allocate_inode() -> Result<u16>;
auto allocate_block() -> Result<u16>;
auto sync_from_disk() -> Result<void>;
auto sync_to_disk() -> Result<void>;
// templates declared here to get private access
template <typename Data>
requires(sizeof(Data) <= block_size)
auto read_block_part(u16 block_id, u16 offset) -> Result<Data>;
template <typename Data>
requires(sizeof(Data) <= block_size)
auto write_block_part(u16 block_id, const Data& data, u16 offset)
-> Result<void>;
template <typename Data>
requires(sizeof(Data) <= block_size)
auto read_block(u16 block_id) -> Result<Data>;
template <typename Data>
requires(sizeof(Data) <= block_size)
auto write_block(const Data& data, u16 block_id) -> Result<void>;
BlockDevice* m_disk;
Superblock m_superblock = {};
BlocksBitmap m_blocks_bitmap = {};
};
}