Kernel: Start work on proper TmpFS in Heap instead of kmalloc memory

This commit is contained in:
2023-11-04 18:13:52 +02:00
parent 8b4e129fc1
commit 99d7b0917d
6 changed files with 848 additions and 0 deletions

View File

@@ -0,0 +1,268 @@
#include <kernel/FS/TmpFS/FileSystem.h>
#include <kernel/Memory/Heap.h>
#include <kernel/Memory/PageTable.h>
namespace Kernel
{
BAN::ErrorOr<TmpFileSystem*> TmpFileSystem::create(size_t max_pages, mode_t mode, uid_t uid, gid_t gid)
{
auto* result = new TmpFileSystem(max_pages);
if (result == nullptr)
return BAN::Error::from_errno(ENOMEM);
TRY(result->initialize(mode, uid, gid));
return result;
}
TmpFileSystem::TmpFileSystem(size_t max_pages)
: m_max_pages(max_pages)
{ }
BAN::ErrorOr<void> TmpFileSystem::initialize(mode_t mode, uid_t uid, gid_t gid)
{
paddr_t data_paddr = Heap::get().take_free_page();
if (data_paddr == 0)
return BAN::Error::from_errno(ENOMEM);
m_data_pages.set_paddr(data_paddr);
m_data_pages.set_flags(PageInfo::Flags::Present);
PageTable::with_fast_page(data_paddr, [&] {
memset(PageTable::fast_page_as_ptr(), 0x00, PAGE_SIZE);
});
paddr_t inodes_paddr = Heap::get().take_free_page();
if (inodes_paddr == 0)
return BAN::Error::from_errno(ENOMEM);
m_inode_pages.set_paddr(inodes_paddr);
m_inode_pages.set_flags(PageInfo::Flags::Present);
PageTable::with_fast_page(inodes_paddr, [&] {
memset(PageTable::fast_page_as_ptr(), 0x00, PAGE_SIZE);
});
m_root_inode = TRY(TmpDirectoryInode::create_root(*this, mode, uid, gid));
TRY(m_inode_cache.insert(m_root_inode->ino(), m_root_inode));
return {};
}
TmpFileSystem::~TmpFileSystem()
{
ASSERT_NOT_REACHED();
}
BAN::ErrorOr<BAN::RefPtr<TmpInode>> TmpFileSystem::open_inode(ino_t ino)
{
if (m_inode_cache.contains(ino))
return m_inode_cache[ino];
TmpInodeInfo inode_info;
auto inode_location = find_inode(ino);
PageTable::with_fast_page(inode_location.paddr, [&] {
inode_info = PageTable::fast_page_as_sized<TmpInodeInfo>(inode_location.index);
});
auto inode = TRY(TmpInode::create_from_existing(*this, ino, inode_info));
TRY(m_inode_cache.insert(ino, inode));
return inode;
}
void TmpFileSystem::read_inode(ino_t ino, TmpInodeInfo& out)
{
auto inode_location = find_inode(ino);
PageTable::with_fast_page(inode_location.paddr, [&] {
out = PageTable::fast_page_as_sized<TmpInodeInfo>(inode_location.index);
});
}
void TmpFileSystem::write_inode(ino_t ino, const TmpInodeInfo& info)
{
auto inode_location = find_inode(ino);
PageTable::with_fast_page(inode_location.paddr, [&] {
auto& inode_info = PageTable::fast_page_as_sized<TmpInodeInfo>(inode_location.index);
inode_info = info;
});
}
void TmpFileSystem::delete_inode(ino_t ino)
{
auto inode_location = find_inode(ino);
PageTable::with_fast_page(inode_location.paddr, [&] {
auto& inode_info = PageTable::fast_page_as_sized<TmpInodeInfo>(inode_location.index);
ASSERT_EQ(inode_info.nlink, 0);
for (auto paddr : inode_info.block)
ASSERT_EQ(paddr, 0);
inode_info = {};
});
}
BAN::ErrorOr<ino_t> TmpFileSystem::allocate_inode(const TmpInodeInfo& info)
{
constexpr size_t inodes_per_page = PAGE_SIZE / sizeof(TmpInodeInfo);
ino_t ino = first_inode;
TRY(for_each_indirect_paddr_allocating(m_inode_pages, [&](paddr_t paddr, bool) {
BAN::Iteration result = BAN::Iteration::Continue;
PageTable::with_fast_page(paddr, [&] {
for (size_t i = 0; i < inodes_per_page; i++, ino++)
{
auto& inode_info = PageTable::fast_page_as_sized<TmpInodeInfo>(i);
if (inode_info.mode != 0)
continue;
inode_info = info;
result = BAN::Iteration::Break;
return;
}
});
return result;
}, 2));
return ino;
}
TmpFileSystem::InodeLocation TmpFileSystem::find_inode(ino_t ino)
{
ASSERT_GTE(ino, first_inode);
ASSERT_LT(ino, max_inodes);
constexpr size_t inodes_per_page = PAGE_SIZE / sizeof(TmpInodeInfo);
size_t index_of_page = (ino - first_inode) / inodes_per_page;
size_t index_in_page = (ino - first_inode) % inodes_per_page;
return {
.paddr = find_indirect(m_inode_pages, index_of_page, 2),
.index = index_in_page
};
}
void TmpFileSystem::read_block(size_t index, BAN::ByteSpan buffer)
{
ASSERT(buffer.size() >= PAGE_SIZE);
paddr_t block_paddr = find_block(index);
PageTable::with_fast_page(block_paddr, [&] {
memcpy(buffer.data(), PageTable::fast_page_as_ptr(), PAGE_SIZE);
});
}
void TmpFileSystem::write_block(size_t index, BAN::ConstByteSpan buffer)
{
ASSERT(buffer.size() >= PAGE_SIZE);
paddr_t block_paddr = find_block(index);
PageTable::with_fast_page(block_paddr, [&] {
memcpy(PageTable::fast_page_as_ptr(), buffer.data(), PAGE_SIZE);
});
}
void TmpFileSystem::free_block(size_t index)
{
ASSERT_NOT_REACHED();
}
BAN::ErrorOr<size_t> TmpFileSystem::allocate_block()
{
size_t result = first_data_page;
TRY(for_each_indirect_paddr_allocating(m_data_pages, [&] (paddr_t paddr, bool allocated) {
if (allocated)
return BAN::Iteration::Break;
result++;
return BAN::Iteration::Continue;
}, 3));
return result;
}
paddr_t TmpFileSystem::find_block(size_t index)
{
ASSERT_GT(index, 0);
return find_indirect(m_data_pages, index - first_data_page, 3);
}
paddr_t TmpFileSystem::find_indirect(PageInfo root, size_t index, size_t depth)
{
ASSERT(root.flags() & PageInfo::Flags::Present);
if (depth == 0)
return root.paddr();
constexpr size_t addresses_per_page = PAGE_SIZE / sizeof(PageInfo);
size_t divisor = 1;
for (size_t i = 0; i < depth; i++)
divisor *= addresses_per_page;
size_t index_of_page = index / divisor;
size_t index_in_page = index % divisor;
ASSERT(index_of_page < addresses_per_page);
PageInfo next;
PageTable::with_fast_page(root.paddr(), [&] {
next = PageTable::fast_page_as_sized<PageInfo>(index_of_page);
});
return find_indirect(next, index_in_page, depth - 1);
}
template<for_each_indirect_paddr_allocating_callback F>
BAN::ErrorOr<BAN::Iteration> TmpFileSystem::for_each_indirect_paddr_allocating_internal(PageInfo page_info, F callback, size_t depth)
{
ASSERT_GT(depth, 0);
ASSERT(page_info.flags() & PageInfo::Flags::Present);
for (size_t i = 0; i < PAGE_SIZE / sizeof(PageInfo); i++)
{
PageInfo next_info;
PageTable::with_fast_page(page_info.paddr(), [&] {
next_info = PageTable::fast_page_as_sized<PageInfo>(i);
});
bool allocated = false;
if (!(next_info.flags() & PageInfo::Flags::Present))
{
paddr_t new_paddr = Heap::get().take_free_page();
if (new_paddr == 0)
return BAN::Error::from_errno(ENOMEM);
PageTable::with_fast_page(new_paddr, [&] {
memset(PageTable::fast_page_as_ptr(), 0x00, PAGE_SIZE);
});
next_info.set_paddr(new_paddr);
next_info.set_flags(PageInfo::Flags::Present);
PageTable::with_fast_page(page_info.paddr(), [&] {
auto& to_update_info = PageTable::fast_page_as_sized<PageInfo>(i);
to_update_info = next_info;
});
allocated = true;
}
BAN::Iteration result;
if (depth == 1)
result = callback(next_info.paddr(), allocated);
else
result = TRY(for_each_indirect_paddr_allocating_internal(next_info, callback, depth - 1));
switch (result)
{
case BAN::Iteration::Continue:
break;
case BAN::Iteration::Break:
return BAN::Iteration::Break;
default:
ASSERT_NOT_REACHED();
}
}
return BAN::Iteration::Continue;
}
template<for_each_indirect_paddr_allocating_callback F>
BAN::ErrorOr<void> TmpFileSystem::for_each_indirect_paddr_allocating(PageInfo page_info, F callback, size_t depth)
{
BAN::Iteration result = TRY(for_each_indirect_paddr_allocating_internal(page_info, callback, depth));
ASSERT(result == BAN::Iteration::Break);
return {};
}
}

View File

@@ -0,0 +1,303 @@
#include <kernel/FS/TmpFS/FileSystem.h>
#include <kernel/FS/TmpFS/Inode.h>
#include <kernel/Timer/Timer.h>
namespace Kernel
{
static TmpInodeInfo create_inode_info(mode_t mode, uid_t uid, gid_t gid)
{
auto current_time = SystemTimer::get().real_time();
TmpInodeInfo info;
info.uid = uid;
info.gid = gid;
info.mode = mode;
info.atime = current_time;
info.mtime = current_time;
info.ctime = current_time;
return info;
}
static uint8_t inode_mode_to_dt_type(Inode::Mode mode)
{
if (mode.ifreg())
return DT_REG;
if (mode.ifdir())
return DT_DIR;
if (mode.ifchr())
return DT_CHR;
if (mode.ifblk())
return DT_BLK;
if (mode.ififo())
return DT_FIFO;
if (mode.ifsock())
return DT_SOCK;
if (mode.iflnk())
return DT_LNK;
ASSERT_NOT_REACHED();
}
/* GENERAL INODE */
BAN::ErrorOr<BAN::RefPtr<TmpInode>> TmpInode::create_from_existing(TmpFileSystem& fs, ino_t ino, const TmpInodeInfo& info)
{
TmpInode* inode_ptr = nullptr;
switch (info.mode & Mode::TYPE_MASK)
{
case Mode::IFDIR:
inode_ptr = new TmpDirectoryInode(fs, ino, info);
break;
case Mode::IFREG:
inode_ptr = new TmpFileInode(fs, ino, info);
break;
default:
ASSERT_NOT_REACHED();
}
if (inode_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
return BAN::RefPtr<TmpInode>::adopt(inode_ptr);
}
TmpInode::TmpInode(TmpFileSystem& fs, ino_t ino, const TmpInodeInfo& info)
: m_fs(fs)
, m_inode_info(info)
, m_ino(ino)
{}
void TmpInode::sync()
{
m_fs.write_inode(m_ino, m_inode_info);
}
void TmpInode::free_all_blocks()
{
for (auto block : m_inode_info.block)
ASSERT(block == 0);
}
size_t TmpInode::block_index(size_t data_block_index)
{
ASSERT(data_block_index < TmpInodeInfo::direct_block_count);
ASSERT(m_inode_info.block[data_block_index]);
return m_inode_info.block[data_block_index];
}
BAN::ErrorOr<size_t> TmpInode::block_index_with_allocation(size_t data_block_index)
{
if (data_block_index >= TmpInodeInfo::direct_block_count)
{
dprintln("only {} blocks supported :D", TmpInodeInfo::direct_block_count);
return BAN::Error::from_errno(ENOSPC);
}
if (m_inode_info.block[data_block_index] == 0)
{
m_inode_info.block[data_block_index] = TRY(m_fs.allocate_block());
m_inode_info.blocks++;
}
return m_inode_info.block[data_block_index];
}
/* FILE INODE */
BAN::ErrorOr<BAN::RefPtr<TmpFileInode>> TmpFileInode::create(TmpFileSystem& fs, mode_t mode, uid_t uid, gid_t gid)
{
auto info = create_inode_info(Mode::IFREG | mode, uid, gid);
ino_t ino = TRY(fs.allocate_inode(info));
auto* inode_ptr = new TmpFileInode(fs, ino, info);
if (inode_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
return BAN::RefPtr<TmpFileInode>::adopt(inode_ptr);
}
TmpFileInode::TmpFileInode(TmpFileSystem& fs, ino_t ino, const TmpInodeInfo& info)
: TmpInode(fs, ino, info)
{
ASSERT(mode().ifreg());
}
TmpFileInode::~TmpFileInode()
{
if (nlink() > 0)
{
sync();
return;
}
free_all_blocks();
m_fs.delete_inode(ino());
}
/* DIRECTORY INODE */
BAN::ErrorOr<BAN::RefPtr<TmpDirectoryInode>> TmpDirectoryInode::create_root(TmpFileSystem& fs, mode_t mode, uid_t uid, gid_t gid)
{
auto info = create_inode_info(Mode::IFDIR | mode, uid, gid);
ino_t ino = TRY(fs.allocate_inode(info));
auto* inode_ptr = new TmpDirectoryInode(fs, ino, info);
if (inode_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto inode = BAN::RefPtr<TmpDirectoryInode>::adopt(inode_ptr);
TRY(inode->link_inode(*inode, "."sv));
TRY(inode->link_inode(*inode, ".."sv));
return inode;
}
BAN::ErrorOr<BAN::RefPtr<TmpDirectoryInode>> TmpDirectoryInode::create_new(TmpFileSystem& fs, mode_t mode, uid_t uid, gid_t gid, TmpInode& parent)
{
auto info = create_inode_info(Mode::IFDIR | mode, uid, gid);
ino_t ino = TRY(fs.allocate_inode(info));
auto* inode_ptr = new TmpDirectoryInode(fs, ino, info);
if (inode_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto inode = BAN::RefPtr<TmpDirectoryInode>::adopt(inode_ptr);
TRY(inode->link_inode(*inode, "."sv));
TRY(inode->link_inode(parent, "."sv));
return inode;
}
TmpDirectoryInode::TmpDirectoryInode(TmpFileSystem& fs, ino_t ino, const TmpInodeInfo& info)
: TmpInode(fs, ino, info)
{
ASSERT(mode().ifdir());
}
TmpDirectoryInode::~TmpDirectoryInode()
{
if (nlink() >= 2)
{
sync();
return;
}
free_all_blocks();
m_fs.delete_inode(ino());
}
BAN::ErrorOr<BAN::RefPtr<Inode>> TmpDirectoryInode::find_inode_impl(BAN::StringView name)
{
ino_t result = 0;
for_each_entry([&](const TmpDirectoryEntry& entry) {
if (entry.type == DT_UNKNOWN)
return BAN::Iteration::Continue;
if (entry.name_sv() != name)
return BAN::Iteration::Continue;
result = entry.ino;
return BAN::Iteration::Break;
});
if (result == 0)
return BAN::Error::from_errno(ENOENT);
auto inode = TRY(m_fs.open_inode(result));
return BAN::RefPtr<Inode>(inode);
}
BAN::ErrorOr<void> TmpDirectoryInode::list_next_inodes_impl(off_t, DirectoryEntryList*, size_t)
{
return BAN::Error::from_errno(ENOTSUP);
}
BAN::ErrorOr<void> TmpDirectoryInode::create_file_impl(BAN::StringView name, mode_t mode, uid_t uid, gid_t gid)
{
auto new_inode = TRY(TmpFileInode::create(m_fs, mode, uid, gid));
TRY(link_inode(*new_inode, name));
return {};
}
BAN::ErrorOr<void> TmpDirectoryInode::create_directory_impl(BAN::StringView name, mode_t mode, uid_t uid, gid_t gid)
{
auto new_inode = TRY(TmpDirectoryInode::create_new(m_fs, mode, uid, gid, *this));
TRY(link_inode(*new_inode, name));
return {};
}
BAN::ErrorOr<void> TmpDirectoryInode::unlink_impl(BAN::StringView)
{
return BAN::Error::from_errno(ENOTSUP);
}
BAN::ErrorOr<void> TmpDirectoryInode::link_inode(TmpInode& inode, BAN::StringView name)
{
static constexpr size_t directory_entry_alignment = 16;
size_t current_size = size();
size_t new_entry_size = sizeof(TmpDirectoryEntry) + name.size();
if (auto rem = new_entry_size % directory_entry_alignment)
new_entry_size += directory_entry_alignment - rem;
ASSERT(new_entry_size < (size_t)blksize());
size_t new_entry_offset = current_size % blksize();
// Target is the last block, or if it doesn't fit the new entry, the next one.
size_t target_data_block = current_size / blksize();
if (blksize() - new_entry_offset < new_entry_size)
target_data_block++;
size_t block_index = TRY(block_index_with_allocation(target_data_block));
BAN::Vector<uint8_t> buffer;
TRY(buffer.resize(blksize()));
BAN::ByteSpan bytespan = buffer.span();
m_fs.read_block(block_index, bytespan);
auto& new_entry = bytespan.slice(new_entry_offset).as<TmpDirectoryEntry>();
new_entry.type = inode_mode_to_dt_type(inode.mode());
new_entry.ino = inode.ino();
new_entry.name_len = name.size();
new_entry.rec_len = new_entry_size;
memcpy(new_entry.name, name.data(), name.size());
m_fs.write_block(block_index, bytespan);
// increase current size
m_inode_info.size += new_entry_size;
// add link to linked inode
inode.m_inode_info.nlink++;
return {};
}
template<for_each_entry_callback F>
void TmpDirectoryInode::for_each_entry(F callback)
{
size_t full_offset = 0;
while (full_offset < (size_t)size())
{
size_t data_block_index = full_offset / blksize();
size_t block_index = this->block_index(data_block_index);
// FIXME: implement fast heap pages?
BAN::Vector<uint8_t> buffer;
MUST(buffer.resize(blksize()));
BAN::ByteSpan bytespan = buffer.span();
m_fs.read_block(block_index, bytespan);
size_t byte_count = BAN::Math::min<size_t>(blksize(), size() - full_offset);
bytespan = bytespan.slice(0, byte_count);
while (bytespan.size() > 0)
{
auto& entry = bytespan.as<TmpDirectoryEntry>();
callback(entry);
bytespan = bytespan.slice(entry.rec_len);
}
full_offset += blksize();
}
}
}