214 lines
5.3 KiB
C++
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 = {};
|
|
};
|
|
|
|
}
|