#include "fs/tmpfs/FileSystem.h" #include "fs/Mount.h" #include "fs/devices/DeviceRegistry.h" #include #include #include namespace TmpFS { Result> FileSystem::create() { SharedPtr fs = TRY(adopt_shared_if_nonnull(new (std::nothrow) FileSystem())); SharedPtr root = TRY(fs->create_dir_inode({})); root->chmod(0755); fs->set_root(root); return (SharedPtr)fs; } Result> FileSystem::create_file_inode() { SharedPtr inode = TRY(make_shared()); inode->set_fs(*this, {}); inode->set_inode_number(m_next_inode_number++, {}); return (SharedPtr)inode; } Result> FileSystem::create_dir_inode(SharedPtr parent) { SharedPtr inode = TRY(make_shared()); TRY(inode->add_entry(inode, ".")); TRY(inode->add_entry(parent ? parent : (SharedPtr)inode, "..")); inode->set_self(inode, {}); inode->set_fs(*this, {}); inode->set_inode_number(m_next_inode_number++, {}); return (SharedPtr)inode; } Result> FileSystem::create_device_inode(u32 major, u32 minor) { SharedPtr device = TRY(DeviceRegistry::fetch_special_device(major, minor)); SharedPtr inode = TRY(make_shared()); inode->set_fs(*this, {}); inode->set_inode_number(m_next_inode_number++, {}); inode->set_device(device, {}); return (SharedPtr)inode; } Result FileSystem::set_mount_dir(SharedPtr parent) { return m_root_inode->replace_entry(parent, ".."); } void FileSystem::set_root(SharedPtr root) { m_root_inode = root; } Result> 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 DirInode::replace_entry(SharedPtr inode, const char* name) { for (auto& entry : m_entries) { if (!strcmp(name, entry.name.chars())) { entry.inode = inode; return {}; } } return err(ENOENT); } Option DirInode::get(usize index) const { if (index >= m_entries.size()) return {}; return m_entries[index]; } Result DirInode::add_entry(SharedPtr 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 DirInode::remove_entry(const char* name) { SharedPtr 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> DirInode::create_file(const char* name) { auto inode = TRY(m_fs->create_file_inode()); TRY(add_entry(inode, name)); return inode; } Result> DirInode::create_subdirectory(const char* name) { auto inode = TRY(m_fs->create_dir_inode(m_self)); TRY(add_entry(inode, name)); return inode; } Result 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 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); return length; } Result 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); return {}; } usize FileInode::size() const { return m_data_buffer.size(); } }