#include "fs/ext2/Inode.h" #include namespace Ext2 { Inode::Inode(Badge, FileSystem* fs) : m_fs(fs) { } usize Inode::size() const { return (m_fs->m_uses_extended_size && (m_type == VFS::InodeType::RegularFile)) ? ((u64)m_raw_inode.size_high << 32) | (u64)m_raw_inode.size_low : m_raw_inode.size_low; } Result Inode::read(u8* buf, usize offset, usize length) const { if (length == 0) return 0; if (offset > size()) return 0; if (offset + length > size()) length = size() - offset; const usize block_size = m_fs->m_block_size; usize to_read = length; if (offset % block_size) { usize block_offset = (offset % block_size); usize block = TRY(find_block(offset / block_size)); usize size_to_read = block_size - block_offset; if (size_to_read > to_read) size_to_read = to_read; usize host_offset = (block * block_size) + block_offset; // FIXME: Cache this data. TRY(m_fs->m_host_device->read(buf, host_offset, size_to_read)); to_read -= size_to_read; buf += size_to_read; offset += size_to_read; } while (to_read >= block_size) { usize block = TRY(find_block(offset / block_size)); usize host_offset = block * block_size; // FIXME: Cache this data. TRY(m_fs->m_host_device->read(buf, host_offset, block_size)); to_read -= block_size; buf += block_size; offset += block_size; } if (to_read > 0) { usize block = TRY(find_block(offset / block_size)); usize host_offset = block * block_size; // FIXME: Cache this data. TRY(m_fs->m_host_device->read(buf, host_offset, to_read)); } return length; } Result Inode::find_block(usize index) const { if (index < 12) return m_raw_inode.direct_pointers[index]; usize block_index = (index - 12) * sizeof(u32); if (block_index >= m_fs->m_block_size) { fail("ext2: Finding blocks beyond the singly indirect pointer block is not yet supported"); } usize block_size = m_fs->m_block_size; if (m_singly_indirect_block.is_empty()) { TRY(m_singly_indirect_block.try_resize(block_size)); TRY(m_fs->m_host_device->read(m_singly_indirect_block.data(), m_raw_inode.singly_indirect_ptr * block_size, block_size)); } return *reinterpret_cast(&m_singly_indirect_block.data()[block_index]); } Result Inode::lazy_initialize_dir() const { check(m_type == VFS::InodeType::Directory); const usize inode_size = size(); const usize block_size = m_fs->m_block_size; u8* const buf = TRY(make_array(block_size)); auto guard = make_scope_guard([buf] { delete[] buf; }); m_entries.clear(); for (usize offset = 0; offset < inode_size; offset += block_size) { TRY(read(buf, offset, block_size)); usize dir_offset = 0; while (dir_offset < block_size) { auto& entry = *(Ext2::RawDirectoryEntry*)&buf[dir_offset]; if (entry.inum != 0) { auto inode = TRY(m_fs->find_inode_by_number(entry.inum)); VFS::DirectoryEntry vfs_entry { inode, "" }; vfs_entry.name.adopt(entry.name, m_fs->m_dirs_have_type_field ? entry.name_length_low : entry.name_length); #ifdef EXT2_DEBUG kdbgln("ext2: Read new directory entry: inum=%u, name=%s, namelen=%lu", entry.inum, vfs_entry.name.chars(), vfs_entry.name.length()); #endif TRY(m_entries.try_append(move(vfs_entry))); } dir_offset += entry.size; } } m_dir_already_lazily_initialized = true; return {}; } Result Inode::replace_entry(SharedPtr inode, const char* name) { if (m_type != VFS::InodeType::Directory) return err(ENOTDIR); if (!m_dir_already_lazily_initialized) TRY(lazy_initialize_dir()); for (auto& entry : m_entries) { if (!strcmp(name, entry.name.chars())) { entry.inode = inode; return {}; } } return err(ENOENT); } Result> Inode::find(const char* name) const { if (m_type != VFS::InodeType::Directory) return err(ENOTDIR); if (!m_dir_already_lazily_initialized) TRY(lazy_initialize_dir()); for (const auto& entry : m_entries) { if (!strcmp(name, entry.name.chars())) return entry.inode; } return err(ENOENT); } Option Inode::get(usize index) const { if (m_type != VFS::InodeType::Directory) return {}; if (!m_dir_already_lazily_initialized) if (lazy_initialize_dir().has_error()) return {}; if (index >= m_entries.size()) return {}; return m_entries[index]; } Result Inode::readlink() { check(m_type == VFS::InodeType::Symlink); if (!m_link.is_empty()) return m_link.view(); const usize length = size(); if (length < 60) { // The symlink location is stored inline within the inode's data blocks. m_link = TRY(String::from_string_view( StringView::from_fixed_size_cstring((char*)&m_raw_inode.direct_pointers[0], length))); return m_link.view(); } Buffer buf = TRY(Buffer::create_sized(length)); TRY(read(buf.data(), 0, length)); m_link = TRY(String::from_string_view(StringView::from_fixed_size_cstring((char*)buf.data(), length))); return m_link.view(); } }