rewrite fs
This commit is contained in:
parent
a0df6785ef
commit
d42b6e00d1
479
src/fs.cpp
479
src/fs.cpp
@ -1,10 +1,12 @@
|
|||||||
#include "fs.hpp"
|
#include "fs.hpp"
|
||||||
|
#include "block_device.hpp"
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstdint>
|
#include <concepts>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace vc5::filesys {
|
namespace vc5::filesys {
|
||||||
|
|
||||||
@ -13,16 +15,10 @@ namespace {
|
|||||||
auto inode_mode(INodeType type) -> u16
|
auto inode_mode(INodeType type) -> u16
|
||||||
{
|
{
|
||||||
u16 mode = 0;
|
u16 mode = 0;
|
||||||
mode |= std::to_underlying(type) << inode_type_bitoffset;
|
mode |= std::to_underlying(type) << INode::type_bitoffset;
|
||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto inode_mode_type(u16 mode) -> INodeType
|
|
||||||
{
|
|
||||||
return static_cast<INodeType>(
|
|
||||||
mode >> inode_type_bitoffset & inode_type_bitmask);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto unwrap_inode_id(u16 inode_id) -> std::tuple<u16, u16>
|
auto unwrap_inode_id(u16 inode_id) -> std::tuple<u16, u16>
|
||||||
{
|
{
|
||||||
u16 inode_block_idx = inode_id / inodes_per_block;
|
u16 inode_block_idx = inode_id / inodes_per_block;
|
||||||
@ -37,234 +33,244 @@ namespace {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Fs::init_new(u16 volume_id, u16 block_count) -> Result<void>
|
auto Fs::init(u16 volume_id, u16 block_count) -> void
|
||||||
{
|
{
|
||||||
m_superblock = Superblock {
|
m_superblock = Superblock {
|
||||||
.volume_id = volume_id,
|
.volume_id = volume_id,
|
||||||
.fs_version = 1,
|
.fs_version = 1,
|
||||||
.blocks_count = block_count,
|
.blocks_count = 0,
|
||||||
.blocks_free = block_count,
|
.blocks_free = block_count,
|
||||||
.root_inode = 0,
|
.root_inode = 0,
|
||||||
.inode_blocks_count = 0,
|
.inode_blocks_count = 0,
|
||||||
.iblocks = {},
|
.inode_blocks = {},
|
||||||
.inode_bitmap = {},
|
.inode_bitmap = {},
|
||||||
};
|
};
|
||||||
|
|
||||||
auto root_inode_id = make_directory_inode();
|
m_blocks_bitmap = {};
|
||||||
if (not root_inode_id)
|
m_blocks_bitmap.bitmap[0] = true;
|
||||||
return std::unexpected(root_inode_id.error());
|
m_blocks_bitmap.bitmap[1] = true;
|
||||||
|
m_blocks_bitmap.bitmap[sb_loc] = true;
|
||||||
|
m_blocks_bitmap.bitmap[blocks_bitmap_loc] = true;
|
||||||
|
|
||||||
m_superblock.root_inode = *root_inode_id;
|
auto root_inode_id = allocate_inode();
|
||||||
|
auto root_inode = make_dir_with_inode_ids(root_inode_id, root_inode_id);
|
||||||
|
(void)root_inode;
|
||||||
|
|
||||||
if (auto result = sync_to_disk(); not result)
|
m_superblock.root_inode = root_inode_id;
|
||||||
return result;
|
|
||||||
|
|
||||||
return {};
|
m_out_of_sync = true;
|
||||||
|
sync_to_disk();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Fs::init_from_existing() -> Result<void>
|
auto Fs::load() -> void
|
||||||
{
|
{
|
||||||
return sync_from_disk();
|
m_superblock = read_block<Superblock>(sb_loc);
|
||||||
|
m_blocks_bitmap = read_block<BlocksBitmap>(blocks_bitmap_loc);
|
||||||
|
m_out_of_sync = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Fs::make_file(u16 parent_inode_id, std::string_view name) -> Result<u16>
|
auto Fs::root_inode() -> INode
|
||||||
{
|
{
|
||||||
return make_inode(parent_inode_id, name, INodeType::Dir);
|
return read_inode(m_superblock.root_inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Fs::make_directory(u16 parent_inode_id, std::string_view name)
|
auto Fs::make_dir(INode& parent, std::string_view name) -> INode
|
||||||
-> Result<u16>
|
{
|
||||||
|
auto inode_id = allocate_inode();
|
||||||
|
auto inode = make_dir_with_inode_ids(inode_id, parent.id);
|
||||||
|
write_inode(inode);
|
||||||
|
add_inode_to_dir(parent, inode, name);
|
||||||
|
sync_to_disk();
|
||||||
|
return inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Fs::make_file(INode& parent, std::string_view name) -> INode
|
||||||
{
|
{
|
||||||
return make_inode(parent_inode_id, name, INodeType::Dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
// auto Fs::write(u16 inode_id, const void* data, u16 size) -> Result<u16>
|
|
||||||
// {
|
|
||||||
// auto inode_result = read_inode(inode_id);
|
|
||||||
// if (not inode_result)
|
|
||||||
// return std::unexpected(inode_result.error());
|
|
||||||
// auto& inode = *inode_result;
|
|
||||||
//
|
|
||||||
// if (auto result = inode_for_each_block_id(inode,
|
|
||||||
// [&](u16& block_id) {
|
|
||||||
// deallocate_block(block_id);
|
|
||||||
// block_id = 0;
|
|
||||||
// });
|
|
||||||
// not result)
|
|
||||||
// return std::unexpected(result.error());
|
|
||||||
// }
|
|
||||||
|
|
||||||
auto Fs::make_inode(u16 parent_inode_id, std::string_view name, INodeType type)
|
|
||||||
-> Result<u16>
|
|
||||||
{
|
|
||||||
if (name.size() > name_max_len)
|
|
||||||
return std::unexpected("invalid filename");
|
|
||||||
|
|
||||||
auto inode_id = allocate_inode();
|
auto inode_id = allocate_inode();
|
||||||
if (not inode_id)
|
|
||||||
return inode_id;
|
|
||||||
|
|
||||||
auto inode = INode {
|
auto inode = INode {
|
||||||
.mode = inode_mode(type),
|
.id = inode_id,
|
||||||
|
.mode = inode_mode(INodeType::File),
|
||||||
.blocks_count = 0,
|
.blocks_count = 0,
|
||||||
.links_count = 0,
|
.links_count = 0,
|
||||||
.last_block_size = 0,
|
|
||||||
.size = 0,
|
.size = 0,
|
||||||
|
.last_block_size = 0,
|
||||||
.blocks = {},
|
.blocks = {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
write_inode(inode);
|
||||||
|
add_inode_to_dir(parent, inode, name);
|
||||||
|
sync_to_disk();
|
||||||
|
return inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Func>
|
||||||
|
requires ReturnsControlFlow<Func, u16>
|
||||||
|
auto Fs::inode_foreach_block(INode& inode, Func func) -> void
|
||||||
|
{
|
||||||
|
using ControlFlow::Break;
|
||||||
|
|
||||||
|
for (u16 i = 0; i < inode.blocks_count && i < INode::ndir_blocks; ++i) {
|
||||||
|
if (func(inode.blocks[i]) == Break) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto block_id = inode.blocks[INode::ind_block];
|
||||||
|
assert(block_id != 0);
|
||||||
|
|
||||||
|
auto ind_block = read_block<IndirectionBlock>(block_id);
|
||||||
|
|
||||||
|
for (u16 i = 0; i < inode.blocks_count - INode::ndir_blocks; ++i) {
|
||||||
|
if (func(ind_block.values[i]) == Break) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Fs::add_inode_to_dir(INode& dir, const INode& inode, std::string_view name)
|
||||||
|
{
|
||||||
|
if (not dir.is_dir()) {
|
||||||
|
throw FsError("incorrect inode type");
|
||||||
|
}
|
||||||
|
if (name.size() > 256) {
|
||||||
|
throw FsError("invalid argument");
|
||||||
|
}
|
||||||
|
|
||||||
auto entry = DirEntry {
|
auto entry = DirEntry {
|
||||||
.inode = *inode_id,
|
.inode = inode.id,
|
||||||
.next = 0,
|
.next = 0,
|
||||||
.name_len = static_cast<u16>(name.size()),
|
.name_len = static_cast<u16>(name.size()),
|
||||||
.name = {},
|
.name = {},
|
||||||
};
|
};
|
||||||
|
|
||||||
std::memcpy(entry.name.data(), name.data(), name.size());
|
std::memcpy(entry.name.data(), name.data(), name.size());
|
||||||
|
write_dir_entry(dir, entry);
|
||||||
if (auto result = append_to_directory(parent_inode_id, entry); not result)
|
write_inode(dir);
|
||||||
return std::unexpected(result.error());
|
|
||||||
|
|
||||||
if (auto result = write_inode(*inode_id, inode); not result)
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
|
|
||||||
return inode_id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Fs::append_to_directory(u16 dir_inode_id, const DirEntry& entry)
|
auto Fs::make_dir_with_inode_ids(u16 inode_id, u16 parent_inode_id) -> INode
|
||||||
-> Result<void>
|
|
||||||
{
|
{
|
||||||
auto dir_inode_result = read_inode(dir_inode_id);
|
|
||||||
if (not dir_inode_result)
|
|
||||||
return std::unexpected(dir_inode_result.error());
|
|
||||||
auto& dir_inode = *dir_inode_result;
|
|
||||||
|
|
||||||
if (inode_mode_type(dir_inode.mode) != INodeType::Dir)
|
|
||||||
return std::unexpected("incorrect inode type");
|
|
||||||
|
|
||||||
if (dir_inode.blocks_count == inode_last_block
|
|
||||||
&& entry.size() > block_size - dir_inode.last_block_size) {
|
|
||||||
return std::unexpected("inode at capacity");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto last_block_id = inode_last_block_id(dir_inode);
|
|
||||||
if (not last_block_id)
|
|
||||||
return std::unexpected(last_block_id.error());
|
|
||||||
|
|
||||||
if (auto result = write_block_part(
|
|
||||||
*last_block_id, entry, dir_inode.last_block_size, entry.size());
|
|
||||||
not result)
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
|
|
||||||
dir_inode.last_block_size += entry.size();
|
|
||||||
|
|
||||||
if (auto result = write_inode(dir_inode_id, dir_inode); not result)
|
|
||||||
return std::unexpected(result.error());
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Fs::make_directory_inode() -> Result<u16>
|
|
||||||
{
|
|
||||||
auto inode_id = allocate_inode();
|
|
||||||
if (not inode_id)
|
|
||||||
return inode_id;
|
|
||||||
|
|
||||||
auto inode = INode {
|
auto inode = INode {
|
||||||
|
.id = inode_id,
|
||||||
.mode = inode_mode(INodeType::Dir),
|
.mode = inode_mode(INodeType::Dir),
|
||||||
.blocks_count = 0,
|
.blocks_count = 0,
|
||||||
.links_count = 0,
|
.links_count = 0,
|
||||||
.last_block_size = 0,
|
|
||||||
.size = 0,
|
.size = 0,
|
||||||
|
.last_block_size = 0,
|
||||||
.blocks = {},
|
.blocks = {},
|
||||||
};
|
};
|
||||||
|
write_inode(inode);
|
||||||
|
|
||||||
if (auto result = write_inode(*inode_id, inode); not result)
|
auto parent_name = std::string_view("..");
|
||||||
return std::unexpected(result.error());
|
auto parent_entry = DirEntry {
|
||||||
|
.inode = parent_inode_id,
|
||||||
return inode_id;
|
.next = 0,
|
||||||
}
|
.name_len = static_cast<u16>(parent_name.size()),
|
||||||
|
.name = {},
|
||||||
auto Fs::inode_last_block_id(const INode& inode) -> Result<u16>
|
|
||||||
{
|
|
||||||
if (inode.blocks_count < inode_ndir_blocks) {
|
|
||||||
return inode.blocks_count - 1;
|
|
||||||
|
|
||||||
} else if (inode.blocks_count >= inode_ind_block
|
|
||||||
&& inode.blocks_count <= inode_ind_block + block_size) {
|
|
||||||
|
|
||||||
auto block_id = inode.blocks[inode_ind_block];
|
|
||||||
assert(block_id != 0);
|
|
||||||
|
|
||||||
auto ind_block = read_block<IntBlock<u16>>(block_id);
|
|
||||||
if (not ind_block)
|
|
||||||
return std::unexpected(ind_block.error());
|
|
||||||
|
|
||||||
return ind_block->data[inode.blocks_count - inode_ind_block];
|
|
||||||
|
|
||||||
} else {
|
|
||||||
return std::unexpected("invalid inode");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Func>
|
|
||||||
auto Fs::inode_for_each_block_id(const INode& inode, Func func) -> Result<void>
|
|
||||||
{
|
|
||||||
for (u16 i = 0; i < inode.blocks_count && i < inode_ndir_blocks; ++i) {
|
|
||||||
func(inode.blocks[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto block_id = inode.blocks[inode_ind_block];
|
|
||||||
assert(block_id != 0);
|
|
||||||
|
|
||||||
auto ind_block = read_block<IntBlock<u16>>(block_id);
|
|
||||||
if (not ind_block)
|
|
||||||
return std::unexpected(ind_block.error());
|
|
||||||
|
|
||||||
for (u16 i = 0; i < inode.blocks_count - inode_ndir_blocks; ++i) {
|
|
||||||
func(inode.blocks[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Fs::read_inode(u16 inode_id) -> Result<INode>
|
|
||||||
{
|
|
||||||
auto [inode_block_idx, block_offset] = unwrap_inode_id(inode_id);
|
|
||||||
|
|
||||||
if (inode_block_idx >= m_superblock.inode_blocks_count)
|
|
||||||
return std::unexpected("inode lookup failed");
|
|
||||||
|
|
||||||
auto block_id = m_superblock.iblocks[inode_block_idx];
|
|
||||||
|
|
||||||
struct INodeData {
|
|
||||||
u16 mode;
|
|
||||||
u16 blocks_count;
|
|
||||||
u16 links_count;
|
|
||||||
u16 last_block_size;
|
|
||||||
u32 size;
|
|
||||||
std::array<u16, inode_blocks_count> blocks;
|
|
||||||
};
|
};
|
||||||
static_assert(sizeof(INodeData) == sizeof(INode));
|
std::memcpy(
|
||||||
|
parent_entry.name.data(), parent_name.data(), parent_name.size());
|
||||||
|
write_dir_entry(inode, parent_entry);
|
||||||
|
|
||||||
return read_block_part<INode>(block_id, block_offset);
|
return inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Fs::write_inode(u16 inode_id, const INode& inode) -> Result<void>
|
auto Fs::write_dir_entry(INode& dir_inode, const DirEntry& entry) -> void
|
||||||
|
{
|
||||||
|
u16 entry_block_id;
|
||||||
|
bool new_block = false;
|
||||||
|
if (dir_inode.last_block_size + entry.size() > block_size) {
|
||||||
|
entry_block_id = inode_allocate_block(dir_inode);
|
||||||
|
new_block = true;
|
||||||
|
} else {
|
||||||
|
entry_block_id = inode_last_block(dir_inode);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto entry_block = read_block<DirEntryBlock>(entry_block_id);
|
||||||
|
|
||||||
|
if (not new_block) {
|
||||||
|
u16 offset = 0;
|
||||||
|
DirEntry entry;
|
||||||
|
|
||||||
|
do {
|
||||||
|
entry = entry_block.read_entry(offset);
|
||||||
|
|
||||||
|
if (entry.next != 0) {
|
||||||
|
offset = entry.next;
|
||||||
|
}
|
||||||
|
} while (entry.next != 0);
|
||||||
|
|
||||||
|
u16 new_offset = offset + static_cast<u16>(entry.size());
|
||||||
|
entry.next = new_offset;
|
||||||
|
|
||||||
|
entry_block.write_entry(offset, entry);
|
||||||
|
entry_block.write_entry(new_offset, entry);
|
||||||
|
} else {
|
||||||
|
entry_block.write_entry(0, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_block(entry_block_id, entry_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Fs::inode_allocate_block(INode& inode) -> u16
|
||||||
|
{
|
||||||
|
auto block_id = allocate_block();
|
||||||
|
inode.blocks[inode.blocks_count] = block_id;
|
||||||
|
inode.blocks_count++;
|
||||||
|
return block_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Fs::inode_last_block(INode& inode) -> u16
|
||||||
|
{
|
||||||
|
if (inode.blocks_count == 0) {
|
||||||
|
auto block_id = allocate_block();
|
||||||
|
inode.blocks[0] = block_id;
|
||||||
|
inode.blocks_count += 1;
|
||||||
|
return block_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inode.blocks_count < INode::ndir_blocks) {
|
||||||
|
return inode.blocks[inode.blocks_count - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inode.blocks_count >= INode::ind_block
|
||||||
|
&& inode.blocks_count - INode::ind_block < block_size) {
|
||||||
|
|
||||||
|
auto ind_block_id = inode.blocks[INode::ind_block];
|
||||||
|
assert(ind_block_id != 0);
|
||||||
|
|
||||||
|
auto ind_block = read_block<IndirectionBlock>(ind_block_id);
|
||||||
|
return ind_block.values[inode.blocks_count - INode::ind_block];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw FsError("invalid inode");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Fs::read_inode(u16 inode_id) -> INode
|
||||||
{
|
{
|
||||||
auto [inode_block_idx, block_offset] = unwrap_inode_id(inode_id);
|
auto [inode_block_idx, block_offset] = unwrap_inode_id(inode_id);
|
||||||
|
|
||||||
if (inode_block_idx >= m_superblock.inode_blocks_count)
|
if (inode_block_idx >= m_superblock.inode_blocks_count)
|
||||||
return std::unexpected("inode lookup failed");
|
throw FsError("inode lookup failed");
|
||||||
|
|
||||||
auto block_id = m_superblock.iblocks[inode_block_idx];
|
auto block_id = m_superblock.inode_blocks[inode_block_idx];
|
||||||
|
|
||||||
if (auto result = write_block_part(block_id, inode, block_offset);
|
return read_block<INode>(block_id, block_offset);
|
||||||
not result)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Fs::allocate_inode() -> Result<u16>
|
auto Fs::write_inode(const INode& inode) -> void
|
||||||
|
{
|
||||||
|
auto [inode_block_idx, block_offset] = unwrap_inode_id(inode.id);
|
||||||
|
|
||||||
|
if (inode_block_idx >= m_superblock.inode_blocks_count)
|
||||||
|
throw FsError("inode lookup failed");
|
||||||
|
|
||||||
|
auto block_id = m_superblock.inode_blocks[inode_block_idx];
|
||||||
|
|
||||||
|
write_block(block_id, inode, block_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Fs::allocate_inode() -> u16
|
||||||
{
|
{
|
||||||
auto inode_id = std::optional<u16>();
|
auto inode_id = std::optional<u16>();
|
||||||
auto& sb = m_superblock;
|
auto& sb = m_superblock;
|
||||||
@ -277,22 +283,22 @@ auto Fs::allocate_inode() -> Result<u16>
|
|||||||
|
|
||||||
if (not inode_id) {
|
if (not inode_id) {
|
||||||
if (sb.inode_blocks_count >= iblocks_max_count) {
|
if (sb.inode_blocks_count >= iblocks_max_count) {
|
||||||
return std::unexpected("inode allocate failed");
|
throw FsError("inode allocate failed");
|
||||||
}
|
}
|
||||||
auto new_block_id = allocate_block();
|
auto new_block_id = allocate_block();
|
||||||
if (not new_block_id)
|
|
||||||
return new_block_id;
|
|
||||||
|
|
||||||
sb.iblocks[sb.inode_blocks_count] = *new_block_id;
|
sb.inode_blocks[sb.inode_blocks_count] = new_block_id;
|
||||||
inode_id = sb.inode_blocks_count * 16;
|
inode_id = sb.inode_blocks_count * 16;
|
||||||
sb.inode_blocks_count += 1;
|
sb.inode_blocks_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.inode_bitmap[*inode_id] = true;
|
sb.inode_bitmap[*inode_id] = true;
|
||||||
|
m_out_of_sync = true;
|
||||||
|
|
||||||
return *inode_id;
|
return *inode_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Fs::allocate_block() -> Result<u16>
|
auto Fs::allocate_block() -> u16
|
||||||
{
|
{
|
||||||
u16 block_id = 0;
|
u16 block_id = 0;
|
||||||
|
|
||||||
@ -304,43 +310,27 @@ auto Fs::allocate_block() -> Result<u16>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (block_id == 0) {
|
if (block_id == 0) {
|
||||||
return std::unexpected("allocate block failed");
|
throw FsError("allocate block failed");
|
||||||
}
|
}
|
||||||
m_blocks_bitmap.bitmap[block_id] = true;
|
m_blocks_bitmap.bitmap[block_id] = true;
|
||||||
|
m_out_of_sync = true;
|
||||||
|
|
||||||
return block_id;
|
return block_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fs::deallocate_block(u16 block_id)
|
void Fs::deallocate_block(u16 block_id)
|
||||||
{
|
{
|
||||||
m_blocks_bitmap.bitmap[block_id] = false;
|
m_blocks_bitmap.bitmap[block_id] = false;
|
||||||
|
m_out_of_sync = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Fs::sync_from_disk() -> Result<void>
|
auto Fs::sync_to_disk() -> void
|
||||||
{
|
{
|
||||||
auto superblock = read_block<Superblock>(sb_loc);
|
if (m_out_of_sync) {
|
||||||
if (not superblock)
|
write_block(sb_loc, m_superblock);
|
||||||
return std::unexpected(superblock.error());
|
write_block(blocks_bitmap_loc, m_blocks_bitmap);
|
||||||
|
m_out_of_sync = false;
|
||||||
auto blocks_bitmap = read_block<BlocksBitmap>(blocks_bitmap_loc);
|
|
||||||
if (not blocks_bitmap)
|
|
||||||
return std::unexpected(superblock.error());
|
|
||||||
|
|
||||||
m_superblock = *superblock;
|
|
||||||
m_blocks_bitmap = *blocks_bitmap;
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Fs::sync_to_disk() -> Result<void>
|
|
||||||
{
|
|
||||||
if (auto result = write_block(m_superblock, sb_loc); not result)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
if (auto result = write_block(m_blocks_bitmap, blocks_bitmap_loc);
|
|
||||||
not result)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class BeWriter {
|
class BeWriter {
|
||||||
@ -430,21 +420,31 @@ private:
|
|||||||
const u8* m_data;
|
const u8* m_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
using BlockData = std::array<u8, block_size>;
|
using RawBlock = std::array<uint8_t, block_size>;
|
||||||
|
|
||||||
template <typename Data>
|
template <typename Data>
|
||||||
requires(sizeof(Data) <= block_size)
|
requires(sizeof(Data) <= block_size)
|
||||||
auto Fs::read_block_part(u16 block_id, u16 offset, u16 size) -> Result<Data>
|
auto Fs::write_block(u16 block_id, const Data& data, size_t offset) -> void
|
||||||
{
|
{
|
||||||
assert(size + offset <= block_size);
|
assert(sizeof(Data) + offset <= block_size);
|
||||||
|
|
||||||
auto block = BlockData {};
|
auto block = RawBlock {};
|
||||||
m_disk->read(block.data(), block_id);
|
m_disk->read(block.data(), block_id);
|
||||||
|
|
||||||
auto raw_data = BlockData {};
|
auto writer = BeWriter(block.data() + offset);
|
||||||
std::memcpy(raw_data.data(), block.data() + offset, size);
|
data.write(writer);
|
||||||
|
|
||||||
auto reader = BeReader(raw_data.data());
|
m_disk->write(block.data(), block_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Data>
|
||||||
|
requires(sizeof(Data) <= block_size)
|
||||||
|
auto Fs::read_block(u16 block_id, size_t offset) -> Data
|
||||||
|
{
|
||||||
|
auto block = RawBlock {};
|
||||||
|
m_disk->read(block.data(), block_id);
|
||||||
|
|
||||||
|
auto reader = BeReader(block.data() + offset);
|
||||||
|
|
||||||
Data data;
|
Data data;
|
||||||
data.read(reader);
|
data.read(reader);
|
||||||
@ -452,54 +452,35 @@ auto Fs::read_block_part(u16 block_id, u16 offset, u16 size) -> Result<Data>
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Data>
|
void Fs::write_block_raw(
|
||||||
requires(sizeof(Data) <= block_size)
|
u16 block_id, const void* data, size_t size, size_t offset)
|
||||||
auto Fs::write_block_part(u16 block_id, const Data& data, u16 offset, u16 size)
|
|
||||||
-> Result<void>
|
|
||||||
{
|
{
|
||||||
assert(size + offset <= block_size);
|
assert(size + offset <= BlockDevice::block_size);
|
||||||
|
|
||||||
auto raw_data = BlockData {};
|
if (size == BlockDevice::block_size && offset == 0) {
|
||||||
|
return m_disk->write(static_cast<const u8*>(data), block_id);
|
||||||
|
}
|
||||||
|
|
||||||
auto writer = BeWriter(raw_data.data());
|
auto block = RawBlock {};
|
||||||
data.write(writer);
|
|
||||||
|
|
||||||
auto block = BlockData {};
|
|
||||||
m_disk->read(block.data(), block_id);
|
m_disk->read(block.data(), block_id);
|
||||||
|
|
||||||
std::memcpy(block.data() + offset, raw_data.data(), size);
|
std::memcpy(block.data() + offset, data, size);
|
||||||
|
|
||||||
m_disk->write(block.data(), block_id);
|
m_disk->write(block.data(), block_id);
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Data>
|
void Fs::read_block_raw(void* data, size_t size, u16 block_id, size_t offset)
|
||||||
requires(sizeof(Data) <= block_size)
|
|
||||||
auto Fs::read_block(u16 block_id) -> Result<Data>
|
|
||||||
{
|
{
|
||||||
auto block = BlockData {};
|
assert(size + offset <= BlockDevice::block_size);
|
||||||
|
|
||||||
|
if (size == BlockDevice::block_size && offset == 0) {
|
||||||
|
return m_disk->read(static_cast<u8*>(data), block_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto block = RawBlock {};
|
||||||
m_disk->read(block.data(), block_id);
|
m_disk->read(block.data(), block_id);
|
||||||
|
|
||||||
auto reader = BeReader(block.data());
|
std::memcpy(data, block.data() + offset, size);
|
||||||
|
|
||||||
Data data;
|
|
||||||
data.read(reader);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Data>
|
|
||||||
requires(sizeof(Data) <= block_size)
|
|
||||||
auto Fs::write_block(const Data& data, u16 block_id) -> Result<void>
|
|
||||||
{
|
|
||||||
auto block = BlockData {};
|
|
||||||
|
|
||||||
auto writer = BeWriter(block.data());
|
|
||||||
data.write(writer);
|
|
||||||
|
|
||||||
m_disk->write(block.data(), block_id);
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
255
src/fs.hpp
255
src/fs.hpp
@ -3,42 +3,18 @@
|
|||||||
#include "block_device.hpp"
|
#include "block_device.hpp"
|
||||||
#include "types.hpp"
|
#include "types.hpp"
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
|
#include <cassert>
|
||||||
#include <concepts>
|
#include <concepts>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <expected>
|
#include <cstring>
|
||||||
|
#include <stdexcept>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace vc5::filesys {
|
namespace vc5::filesys {
|
||||||
|
|
||||||
constexpr u16 block_size = 0x200;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto size() const -> u16
|
|
||||||
{
|
|
||||||
return sizeof(inode) + sizeof(next) + sizeof(name_len)
|
|
||||||
+ sizeof(char) * name_len;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr u16 inode_size = 32;
|
constexpr u16 inode_size = 32;
|
||||||
constexpr u16 inodes_per_block = block_size / inode_size;
|
constexpr u16 inodes_per_block = block_size / inode_size;
|
||||||
|
|
||||||
@ -48,22 +24,23 @@ enum class INodeType : u16 {
|
|||||||
Link = 2,
|
Link = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr u16 inode_type_bitoffset = 0;
|
|
||||||
constexpr u16 inode_type_bitmask = 0b11;
|
|
||||||
|
|
||||||
constexpr u16 inode_ndir_blocks = 9;
|
|
||||||
constexpr u16 inode_ind_block = inode_ndir_blocks;
|
|
||||||
constexpr u16 inode_blocks_count = inode_ind_block + 1;
|
|
||||||
|
|
||||||
constexpr u16 inode_last_block = inode_ndir_blocks + block_size;
|
|
||||||
|
|
||||||
struct INode {
|
struct INode {
|
||||||
|
static constexpr u16 type_bitoffset = 0;
|
||||||
|
static constexpr u16 type_bitmask = 0b11;
|
||||||
|
|
||||||
|
static constexpr u16 ndir_blocks = 8;
|
||||||
|
static constexpr u16 ind_block = ndir_blocks;
|
||||||
|
static constexpr u16 max_blocks_count = ind_block + 1;
|
||||||
|
|
||||||
|
static constexpr u16 inode_last_block = ndir_blocks + block_size;
|
||||||
|
|
||||||
|
u16 id;
|
||||||
u16 mode;
|
u16 mode;
|
||||||
u16 blocks_count;
|
u16 blocks_count;
|
||||||
u16 links_count;
|
u16 links_count;
|
||||||
u16 last_block_size;
|
|
||||||
u32 size;
|
u32 size;
|
||||||
std::array<u16, inode_blocks_count> blocks;
|
u16 last_block_size;
|
||||||
|
std::array<u16, max_blocks_count> blocks;
|
||||||
|
|
||||||
template <typename Writer> void write(Writer& w) const
|
template <typename Writer> void write(Writer& w) const
|
||||||
{
|
{
|
||||||
@ -71,11 +48,23 @@ struct INode {
|
|||||||
<< blocks;
|
<< blocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Reader> auto read(Reader& r)
|
template <typename Reader> void read(Reader& r)
|
||||||
{
|
{
|
||||||
r >> mode >> blocks_count >> links_count >> last_block_size >> size
|
r >> mode >> blocks_count >> links_count >> last_block_size >> size
|
||||||
>> blocks;
|
>> blocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto is_file() const -> bool
|
||||||
|
{
|
||||||
|
return static_cast<INodeType>(mode >> type_bitoffset & type_bitmask)
|
||||||
|
== INodeType::File;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto is_dir() const -> bool
|
||||||
|
{
|
||||||
|
return static_cast<INodeType>(mode >> type_bitoffset & type_bitmask)
|
||||||
|
== INodeType::Dir;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(INode) <= inode_size);
|
static_assert(sizeof(INode) <= inode_size);
|
||||||
@ -93,19 +82,19 @@ struct Superblock {
|
|||||||
u16 blocks_free;
|
u16 blocks_free;
|
||||||
u16 root_inode;
|
u16 root_inode;
|
||||||
u16 inode_blocks_count;
|
u16 inode_blocks_count;
|
||||||
std::array<u16, iblocks_max_count> iblocks;
|
std::array<u16, iblocks_max_count> inode_blocks;
|
||||||
std::bitset<inodes_max_count> inode_bitmap;
|
std::bitset<inodes_max_count> inode_bitmap;
|
||||||
|
|
||||||
template <typename Writer> void write(Writer& w) const
|
template <typename Writer> void write(Writer& w) const
|
||||||
{
|
{
|
||||||
w << volume_id << fs_version << blocks_count << blocks_free
|
w << volume_id << fs_version << blocks_count << blocks_free
|
||||||
<< root_inode << inode_blocks_count << iblocks << inode_bitmap;
|
<< root_inode << inode_blocks_count << inode_blocks << inode_bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Reader> auto read(Reader& r)
|
template <typename Reader> void read(Reader& r)
|
||||||
{
|
{
|
||||||
r >> volume_id >> fs_version >> blocks_count >> blocks_free
|
r >> volume_id >> fs_version >> blocks_count >> blocks_free
|
||||||
>> root_inode >> inode_blocks_count >> iblocks >> inode_bitmap;
|
>> root_inode >> inode_blocks_count >> inode_blocks >> inode_bitmap;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -119,29 +108,107 @@ struct BlocksBitmap {
|
|||||||
w << bitmap;
|
w << bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Reader> auto read(Reader& r)
|
template <typename Reader> void read(Reader& r)
|
||||||
{
|
{
|
||||||
r >> bitmap;
|
r >> bitmap;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
static_assert(sizeof(BlocksBitmap) <= block_size);
|
||||||
|
|
||||||
template <typename Int>
|
struct DirEntry {
|
||||||
requires std::is_integral_v<Int>
|
static constexpr u16 name_max_len = 256;
|
||||||
struct IntBlock {
|
|
||||||
std::array<u16, block_size / sizeof(Int)> data;
|
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;
|
||||||
|
|
||||||
|
for (int i = 0; i < name_len; ++i) {
|
||||||
|
w << name[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Reader> void read(Reader& r)
|
||||||
|
{
|
||||||
|
r >> inode >> next >> name_len;
|
||||||
|
|
||||||
|
for (int i = 0; i < name_len; ++i) {
|
||||||
|
r << name[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto size() const -> u16
|
||||||
|
{
|
||||||
|
return sizeof(inode) + sizeof(next) + sizeof(name_len)
|
||||||
|
+ sizeof(char) * name_len;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IndirectionBlock {
|
||||||
|
std::array<u16, block_size / sizeof(u16)> values;
|
||||||
|
|
||||||
|
template <typename Reader> void read(Reader& r)
|
||||||
|
{
|
||||||
|
r >> values;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Writer> void write(Writer& w) const
|
||||||
|
{
|
||||||
|
w << values;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DirEntryBlock {
|
||||||
|
std::array<uint8_t, block_size> data;
|
||||||
|
|
||||||
|
template <typename Reader> void read(Reader& r)
|
||||||
|
{
|
||||||
|
r >> data;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Writer> void write(Writer& w) const
|
template <typename Writer> void write(Writer& w) const
|
||||||
{
|
{
|
||||||
w << data;
|
w << data;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Reader> auto read(Reader& r)
|
inline DirEntry read_entry(u16 offset)
|
||||||
{
|
{
|
||||||
r >> data;
|
// it's ok to read into the entire entry
|
||||||
|
DirEntry entry;
|
||||||
|
std::memcpy(&entry,
|
||||||
|
data.data() + offset,
|
||||||
|
std::min<size_t>(sizeof(DirEntry), block_size - offset));
|
||||||
|
|
||||||
|
assert(entry.size() + offset <= block_size);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void write_entry(u16 offset, const DirEntry& entry)
|
||||||
|
{
|
||||||
|
assert(entry.size() + offset <= block_size);
|
||||||
|
|
||||||
|
std::memcpy(data.data() + offset, &entry, entry.size());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(BlocksBitmap) <= block_size);
|
enum class ControlFlow {
|
||||||
|
Continue,
|
||||||
|
Break,
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Func, typename... Args>
|
||||||
|
concept ReturnsControlFlow
|
||||||
|
= std::invocable<Func, Args...> && requires(Func func, Args... args) {
|
||||||
|
{ func(args...) } -> std::convertible_to<ControlFlow>;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FsError : public std::runtime_error {
|
||||||
|
using std::runtime_error::runtime_error;
|
||||||
|
};
|
||||||
|
|
||||||
class Fs {
|
class Fs {
|
||||||
public:
|
public:
|
||||||
@ -149,85 +216,55 @@ public:
|
|||||||
: m_disk(&disk)
|
: m_disk(&disk)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
auto init(u16 volume_id, u16 block_count) -> void;
|
||||||
|
auto load() -> void;
|
||||||
|
|
||||||
template <typename T> using Result = std::expected<T, std::string_view>;
|
auto root_inode() -> INode;
|
||||||
|
auto make_dir(INode& parent, std::string_view name) -> INode;
|
||||||
|
auto make_file(INode& parent, std::string_view name) -> INode;
|
||||||
|
|
||||||
static auto init_new(BlockDevice& disk, u16 volume_id, u16 block_count)
|
auto write(u16 inode_id, const void* data, u16 size) -> u16;
|
||||||
-> 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 parent_inode_id, std::string_view name) -> Result<u16>;
|
|
||||||
auto make_directory(u16 parent_inode_id, std::string_view name)
|
|
||||||
-> Result<u16>;
|
|
||||||
|
|
||||||
auto write(u16 inode_id, const void* data, u16 size) -> Result<u16>;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
auto init_new(u16 volume_id, u16 block_count) -> Result<void>;
|
|
||||||
auto init_from_existing() -> Result<void>;
|
|
||||||
|
|
||||||
auto make_inode(u16 parent_inode_id, std::string_view name, INodeType type)
|
|
||||||
-> Result<u16>;
|
|
||||||
|
|
||||||
auto append_to_directory(u16 dir_inode_id, const DirEntry& entry)
|
|
||||||
-> Result<void>;
|
|
||||||
auto make_directory_inode() -> Result<u16>;
|
|
||||||
|
|
||||||
auto inode_last_block_id(const INode& inode) -> Result<u16>;
|
|
||||||
|
|
||||||
template <typename Func>
|
template <typename Func>
|
||||||
auto inode_for_each_block_id(const INode& inode, Func func) -> Result<void>;
|
requires ReturnsControlFlow<Func, u16>
|
||||||
|
auto inode_foreach_block(INode& inode, Func func) -> void;
|
||||||
|
|
||||||
auto write_inode(u16 inode_id, const INode& inode) -> Result<void>;
|
void add_inode_to_dir(
|
||||||
auto read_inode(u16 inode_id) -> Result<INode>;
|
INode& dir, const INode& inode, std::string_view name);
|
||||||
auto allocate_inode() -> Result<u16>;
|
auto make_dir_with_inode_ids(u16 inode_id, u16 parent_inode_id) -> INode;
|
||||||
|
auto write_dir_entry(INode& dir_inode, const DirEntry& entry) -> void;
|
||||||
|
auto inode_allocate_block(INode& inode) -> u16;
|
||||||
|
auto inode_last_block(INode& inode) -> u16;
|
||||||
|
|
||||||
auto allocate_block() -> Result<u16>;
|
auto write_inode(const INode& inode) -> void;
|
||||||
|
auto read_inode(u16 inode_id) -> INode;
|
||||||
|
|
||||||
|
auto allocate_inode() -> u16;
|
||||||
|
auto allocate_block() -> u16;
|
||||||
void deallocate_block(u16 block_id);
|
void deallocate_block(u16 block_id);
|
||||||
|
|
||||||
auto sync_from_disk() -> Result<void>;
|
auto sync_to_disk() -> void;
|
||||||
auto sync_to_disk() -> Result<void>;
|
|
||||||
|
|
||||||
// templates declared here to get private access
|
// templates declared here to get private access
|
||||||
|
|
||||||
template <typename Data>
|
template <typename Data>
|
||||||
requires(sizeof(Data) <= block_size)
|
requires(sizeof(Data) <= block_size)
|
||||||
auto read_block_part(u16 block_id, u16 offset, u16 size = sizeof(Data))
|
auto write_block(u16 block_id, const Data& data, size_t offset = 0) -> void;
|
||||||
-> Result<Data>;
|
|
||||||
template <typename Data>
|
template <typename Data>
|
||||||
requires(sizeof(Data) <= block_size)
|
requires(sizeof(Data) <= block_size)
|
||||||
auto write_block_part(
|
auto read_block(u16 block_id, size_t offset = 0) -> Data;
|
||||||
u16 block_id, const Data& data, u16 offset, u16 size = sizeof(Data))
|
|
||||||
-> Result<void>;
|
|
||||||
|
|
||||||
template <typename Data>
|
void write_block_raw(
|
||||||
requires(sizeof(Data) <= block_size)
|
u16 block_id, const void* data, size_t size, size_t offset = 0);
|
||||||
auto read_block(u16 block_id) -> Result<Data>;
|
void read_block_raw(
|
||||||
template <typename Data>
|
void* data, size_t size, u16 block_id, size_t offset = 0);
|
||||||
requires(sizeof(Data) <= block_size)
|
|
||||||
auto write_block(const Data& data, u16 block_id) -> Result<void>;
|
|
||||||
|
|
||||||
BlockDevice* m_disk;
|
BlockDevice* m_disk;
|
||||||
Superblock m_superblock = {};
|
Superblock m_superblock = {};
|
||||||
BlocksBitmap m_blocks_bitmap = {};
|
BlocksBitmap m_blocks_bitmap = {};
|
||||||
|
|
||||||
|
bool m_out_of_sync = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,8 +18,8 @@ using namespace vc5;
|
|||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
auto device = IoDevice::create().value();
|
// auto device = IoDevice::create().value();
|
||||||
device->set_title("vc5");
|
// device->set_title("vc5");
|
||||||
|
|
||||||
if (argc > 2) {
|
if (argc > 2) {
|
||||||
std::println("error: no boot disk (filename) specified");
|
std::println("error: no boot disk (filename) specified");
|
||||||
@ -37,19 +37,22 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto fs = filesys::Fs::init_new(disk, 0, 128).value();
|
auto fs = filesys::Fs(disk);
|
||||||
|
fs.init(0, 128);
|
||||||
|
|
||||||
auto root = fs.root_inode_id().value();
|
auto root = fs.root_inode();
|
||||||
|
|
||||||
auto file = fs.make_file(root, "test");
|
auto file = fs.make_file(root, "test");
|
||||||
|
|
||||||
} catch (std::bad_expected_access<std::string_view>& ex) {
|
} catch (filesys::FsError& ex) {
|
||||||
std::println(stderr, "error: {}", ex.error());
|
std::println(stderr, "error: {}", ex.what());
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto vm = VM(*device, disk);
|
// auto vm = VM(*device, disk);
|
||||||
vm.run();
|
// vm.run();
|
||||||
|
|
||||||
|
std::println("OK!");
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" const char* __asan_default_options(void)
|
extern "C" const char* __asan_default_options(void)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user