Luna/kernel/src/fs/tmpfs/Inode.cpp
2023-08-02 22:20:05 +02:00

157 lines
4.1 KiB
C++

#include "fs/tmpfs/Inode.h"
#include "arch/MMU.h"
#include "memory/SharedMemory.h"
namespace TmpFS
{
Result<SharedPtr<VFS::Inode>> DirInode::find(const char* name) const
{
for (const auto& entry : m_entries)
{
if (!strcmp(name, entry.name.chars())) return entry.inode;
}
return err(ENOENT);
}
Result<void> DirInode::replace_entry(SharedPtr<VFS::Inode> inode, const char* name)
{
for (auto& entry : m_entries)
{
if (!strcmp(name, entry.name.chars()))
{
entry.inode = inode;
return {};
}
}
return err(ENOENT);
}
Option<VFS::DirectoryEntry> DirInode::get(usize index) const
{
if (index >= m_entries.size()) return {};
return m_entries[index];
}
Result<void> DirInode::add_entry(SharedPtr<VFS::Inode> inode, const char* name)
{
if (find(name).has_value()) return err(EEXIST);
VFS::DirectoryEntry entry { inode, name };
TRY(m_entries.try_append(move(entry)));
inode->did_link();
return {};
}
Result<void> DirInode::remove_entry(const char* name)
{
SharedPtr<VFS::Inode> inode = TRY(find(name));
if (inode->type() == VFS::InodeType::Directory && inode->entries() != 2) return err(ENOTEMPTY);
if (inode->is_mountpoint()) return err(EBUSY);
m_entries.remove_first_matching(
[&](const VFS::DirectoryEntry& entry) { return !strcmp(entry.name.chars(), name); });
inode->did_unlink();
return {};
}
Result<SharedPtr<VFS::Inode>> DirInode::create_file(const char* name, mode_t mode)
{
auto inode = TRY(m_fs->create_file_inode(mode));
TRY(add_entry(inode, name));
return inode;
}
Result<SharedPtr<VFS::Inode>> DirInode::create_subdirectory(const char* name, mode_t mode)
{
auto inode = TRY(m_fs->create_dir_inode(m_self, mode));
TRY(add_entry(inode, name));
return inode;
}
Result<usize> FileInode::read(u8* buf, usize offset, usize length) const
{
if (length == 0) return 0;
if (offset > m_data_buffer.size()) return 0;
if (offset + length > m_data_buffer.size()) length = m_data_buffer.size() - offset;
memcpy(buf, m_data_buffer.data() + offset, length);
return length;
}
Result<usize> FileInode::write(const u8* buf, usize offset, usize length)
{
if (length == 0) return 0;
if (offset > m_data_buffer.size())
{
// Fill the in-between space with zeroes.
usize old_size = m_data_buffer.size();
usize zeroes = offset - old_size;
TRY(m_data_buffer.try_resize(offset));
memset(m_data_buffer.data() + old_size, 0, zeroes);
}
u8* slice = TRY(m_data_buffer.slice(offset, length));
memcpy(slice, buf, length);
m_metadata.size = m_data_buffer.size();
return length;
}
Result<void> FileInode::truncate(usize size)
{
usize old_size = m_data_buffer.size();
TRY(m_data_buffer.try_resize(size));
if (size > old_size) memset(m_data_buffer.data() + old_size, 0, size - old_size);
m_metadata.size = m_data_buffer.size();
return {};
}
Result<u64> FileInode::query_shared_memory(off_t offset, usize count)
{
if (offset + (count * ARCH_PAGE_SIZE) > m_data_buffer.size()) return err(EINVAL);
if (!m_shmid.has_value())
{
u64 shmid = TRY(SharedMemory::create(m_data_buffer.data() + offset, offset, count));
m_shmid = shmid;
return shmid;
}
auto* shm = g_shared_memory_map.try_get_ref(*m_shmid);
if (shm->offset > offset)
{
TRY(shm->grow_backward(m_data_buffer.data() + offset, (shm->offset - offset) / ARCH_PAGE_SIZE));
}
if (shm->frames.size() < count)
{
TRY(shm->grow_forward(m_data_buffer.data() + offset + (shm->frames.size() * ARCH_PAGE_SIZE),
count - shm->frames.size()));
}
return *m_shmid;
}
}