kernel/Ext2: Read the root inode metadata from the disk
All checks were successful
continuous-integration/drone/pr Build is passing

This commit is contained in:
apio 2023-06-21 21:32:28 +02:00
parent 1461fffba8
commit 62ac42ffc3
Signed by: apio
GPG Key ID: B8A7D06E42258954
10 changed files with 262 additions and 5 deletions

View File

@ -0,0 +1,3 @@
Name=ext2fs
Script=/sbin/test-ext2fs
Wait=true

4
initrd/sbin/test-ext2fs Normal file
View File

@ -0,0 +1,4 @@
# (for testing) Remove this automatic mount once ext2 is working.
mkdir /mnt
mount -t ext2 /dev/cd0p2 /mnt

View File

@ -48,6 +48,7 @@ set(SOURCES
src/fs/tmpfs/FileSystem.cpp src/fs/tmpfs/FileSystem.cpp
src/fs/tmpfs/Inode.cpp src/fs/tmpfs/Inode.cpp
src/fs/ext2/FileSystem.cpp src/fs/ext2/FileSystem.cpp
src/fs/ext2/Inode.cpp
src/fs/devices/DeviceRegistry.cpp src/fs/devices/DeviceRegistry.cpp
src/fs/devices/NullDevice.cpp src/fs/devices/NullDevice.cpp
src/fs/devices/ZeroDevice.cpp src/fs/devices/ZeroDevice.cpp

View File

@ -36,6 +36,11 @@ namespace VFS
virtual Result<void> set_mount_dir(SharedPtr<Inode> parent) = 0; virtual Result<void> set_mount_dir(SharedPtr<Inode> parent) = 0;
virtual bool is_readonly() const
{
return false;
}
virtual u64 handles() const virtual u64 handles() const
{ {
return m_handles; return m_handles;

View File

@ -1,4 +1,23 @@
#include "fs/ext2/FileSystem.h" #include "fs/ext2/FileSystem.h"
#include "fs/ext2/Inode.h"
#include <luna/Alignment.h>
static VFS::InodeType vfs_type_from_ext2_type(mode_t mode)
{
auto type = mode & 0xf000;
switch (type)
{
case EXT2_FIFO: return VFS::InodeType::FIFO;
case EXT2_CHR: return VFS::InodeType::CharacterDevice;
case EXT2_DIR: return VFS::InodeType::Directory;
case EXT2_BLK: return VFS::InodeType::BlockDevice;
case EXT2_REG: return VFS::InodeType::RegularFile;
case EXT2_LNK: return VFS::InodeType::Symlink;
case EXT2_SOCK: [[fallthrough]]; // TODO: Sockets not supported on Luna at the moment.
default: fail("ext2: Unknown or unsupported inode type");
}
}
namespace Ext2 namespace Ext2
{ {
@ -6,15 +25,54 @@ namespace Ext2
{ {
} }
Result<SharedPtr<VFS::Inode>> FileSystem::find_inode_by_number(ino_t inode) Result<SharedPtr<VFS::Inode>> FileSystem::find_inode_by_number(ino_t inum)
{ {
auto maybe_inode = m_inode_cache.try_get(inode); check(inum < m_superblock.nr_inodes);
auto maybe_inode = m_inode_cache.try_get(inum);
if (maybe_inode.has_value()) return maybe_inode.value(); if (maybe_inode.has_value()) return maybe_inode.value();
const u32 block_group = (u32)((inum - 1) / m_superblock.inodes_per_block_group);
const auto* block_group_descriptor = TRY(find_block_group_descriptor(block_group));
check(block_group_descriptor);
// FIXME: This is determined by a field in the Superblock if the Ext2 revision >= 1.0.
static constexpr usize INODE_SIZE = 128;
const u64 index = (inum - 1) % m_superblock.inodes_per_block_group;
const u64 inode_address = (block_group_descriptor->inode_table_start * m_block_size) + (index * INODE_SIZE);
auto inode = TRY(adopt_shared_if_nonnull(new (std::nothrow) Ext2::Inode({}, this)));
TRY(m_host_device->read((u8*)&inode->m_raw_inode, inode_address, INODE_SIZE));
inode->m_type = vfs_type_from_ext2_type(inode->m_raw_inode.mode);
inode->m_inum = inum;
kdbgln("ext2: Read inode %lu with mode %#x (%#x + %#o), size %lu", inum, inode->m_raw_inode.mode,
inode->m_raw_inode.mode & 0xf000, inode->mode(), inode->size());
// TODO: Locate the inode's block group descriptor and find it in the block group's inode table. // TODO: Locate the inode's block group descriptor and find it in the block group's inode table.
return err(ENOENT); return err(ENOENT);
} }
Result<const BlockGroupDescriptor*> FileSystem::find_block_group_descriptor(u32 index)
{
check(index < m_block_groups);
auto maybe_desc = m_block_group_descriptor_cache.try_get_ref(index);
if (maybe_desc) return maybe_desc;
const u64 address = (m_superblock.first_data_block + 1) * m_block_size + (index * sizeof(BlockGroupDescriptor));
BlockGroupDescriptor descriptor;
TRY(m_host_device->read((u8*)&descriptor, address, sizeof(descriptor)));
check(TRY(m_block_group_descriptor_cache.try_set(index, descriptor)));
return m_block_group_descriptor_cache.try_get_ref(index);
}
Result<SharedPtr<VFS::FileSystem>> FileSystem::create(SharedPtr<Device> host_device) Result<SharedPtr<VFS::FileSystem>> FileSystem::create(SharedPtr<Device> host_device)
{ {
SharedPtr<FileSystem> fs = TRY(adopt_shared_if_nonnull(new (std::nothrow) FileSystem())); SharedPtr<FileSystem> fs = TRY(adopt_shared_if_nonnull(new (std::nothrow) FileSystem()));
@ -22,6 +80,19 @@ namespace Ext2
if (nread != 1024) return err(EINVAL); // Source had an invalid superblock. if (nread != 1024) return err(EINVAL); // Source had an invalid superblock.
if (fs->m_superblock.signature != EXT2_MAGIC) return err(EINVAL); // Source had an invalid superblock. if (fs->m_superblock.signature != EXT2_MAGIC) return err(EINVAL); // Source had an invalid superblock.
fs->m_host_device = host_device;
fs->m_block_size = 1024 << fs->m_superblock.log_block_size;
fs->m_block_groups = get_blocks_from_size(fs->m_superblock.nr_blocks, fs->m_superblock.blocks_per_block_group);
kdbgln("ext2: Mounting new Ext2 file system, block size=%lu, blocks=%u, inodes=%u, block group=(%u blocks, %u "
"inodes), %lu block groups",
fs->m_block_size, fs->m_superblock.nr_blocks, fs->m_superblock.nr_inodes,
fs->m_superblock.blocks_per_block_group, fs->m_superblock.inodes_per_block_group, fs->m_block_groups);
// Lookup the root inode.
fs->find_inode_by_number(2);
// TODO: Implement basic Ext2 reading, enough to be able to mount a volume. // TODO: Implement basic Ext2 reading, enough to be able to mount a volume.
return err(ENOTSUP); return err(ENOTSUP);
} }

View File

@ -11,10 +11,10 @@ namespace Ext2
{ {
u32 nr_inodes; u32 nr_inodes;
u32 nr_blocks; u32 nr_blocks;
u32 nr_reserved; u32 nr_reserved_blocks;
u32 nr_free_inodes;
u32 nr_free_blocks; u32 nr_free_blocks;
u32 superblock_block; u32 nr_free_inodes;
u32 first_data_block;
u32 log_block_size; u32 log_block_size;
u32 log_fragment_size; u32 log_fragment_size;
u32 blocks_per_block_group; u32 blocks_per_block_group;
@ -22,23 +22,64 @@ namespace Ext2
u32 inodes_per_block_group; u32 inodes_per_block_group;
u32 last_mount_time; u32 last_mount_time;
u32 last_write_time; u32 last_write_time;
u16 mounts_since_last_fsck; u16 mounts_since_last_fsck;
u16 mounts_allowed_before_fsck; u16 mounts_allowed_before_fsck;
u16 signature; u16 signature;
u16 fs_state; u16 fs_state;
u16 error_action; u16 error_action;
u16 minor_version; u16 minor_version;
u32 last_fsck_time; u32 last_fsck_time;
u32 fsck_time_interval; u32 fsck_time_interval;
u32 os_id; u32 os_id;
u32 major_version; u32 major_version;
u16 reserved_block_uid; u16 reserved_block_uid;
u16 reserved_block_gid; u16 reserved_block_gid;
// TODO: Add extended superblock fields. // TODO: Add extended superblock fields.
u8 padding[1024 - 84]; u8 padding[1024 - 84];
}; };
struct [[gnu::packed]] BlockGroupDescriptor
{
u32 block_usage_addr;
u32 inode_usage_addr;
u32 inode_table_start;
u16 nr_free_blocks;
u16 nr_free_inodes;
u16 nr_directories;
u8 padding[32 - 18];
};
struct [[gnu::packed]] RawInode
{
u16 mode;
u16 uid;
u32 size_low;
u32 atime;
u32 create_time;
u32 mtime;
u32 delete_time;
u16 gid;
u16 nlinks;
u32 sectors_used;
u32 flags;
u32 os_specific_1;
u32 direct_pointers[12];
u32 singly_indirect_ptr;
u32 doubly_indirect_ptr;
u32 triply_indirect_ptr;
u32 gen_number;
u32 extended_attrs;
u32 size_high;
u32 frag_addr;
u32 os_specific_2[3];
};
static_assert(sizeof(Superblock) == 1024); static_assert(sizeof(Superblock) == 1024);
static_assert(sizeof(BlockGroupDescriptor) == 32);
static_assert(sizeof(RawInode) == 128);
class FileSystem : public VFS::FileSystem class FileSystem : public VFS::FileSystem
{ {
@ -73,6 +114,11 @@ namespace Ext2
return {}; return {};
} }
bool is_readonly() const override
{
return true;
}
static Result<SharedPtr<VFS::FileSystem>> create(SharedPtr<Device> host_device); static Result<SharedPtr<VFS::FileSystem>> create(SharedPtr<Device> host_device);
dev_t host_device_id() const override dev_t host_device_id() const override
@ -81,6 +127,7 @@ namespace Ext2
} }
Result<SharedPtr<VFS::Inode>> find_inode_by_number(ino_t inode); Result<SharedPtr<VFS::Inode>> find_inode_by_number(ino_t inode);
Result<const BlockGroupDescriptor*> find_block_group_descriptor(u32 index);
virtual ~FileSystem() = default; virtual ~FileSystem() = default;
@ -94,8 +141,13 @@ namespace Ext2
Superblock m_superblock; Superblock m_superblock;
u64 m_block_size;
u64 m_block_groups;
// FIXME: This inode cache will keep all inodes in it alive despite having no other references to it, but we're // FIXME: This inode cache will keep all inodes in it alive despite having no other references to it, but we're
// not worrying about that as for now the filesystem implementation is read-only. // not worrying about that as for now the filesystem implementation is read-only.
HashMap<ino_t, SharedPtr<VFS::Inode>> m_inode_cache; HashMap<ino_t, SharedPtr<VFS::Inode>> m_inode_cache;
HashMap<u32, BlockGroupDescriptor> m_block_group_descriptor_cache;
}; };
} }

View File

@ -0,0 +1,13 @@
#include "fs/ext2/Inode.h"
namespace Ext2
{
Inode::Inode(Badge<FileSystem>, FileSystem* fs) : m_fs(fs)
{
}
Result<usize> Inode::read(u8* /*buf*/, usize /*offset*/, usize /*length*/) const
{
fail("FIXME: Ext2::Inode::read()");
}
}

View File

@ -0,0 +1,99 @@
#pragma once
#include "fs/ext2/FileSystem.h"
#define EXT2_FIFO 0x1000
#define EXT2_CHR 0x2000
#define EXT2_DIR 0x4000
#define EXT2_BLK 0x6000
#define EXT2_REG 0x8000
#define EXT2_LNK 0xA000
#define EXT2_SOCK 0xC000
namespace Ext2
{
class Inode : public VFS::FileInode
{
public:
VFS::InodeType type() const override
{
return m_type;
}
usize size() const override
{
// FIXME: If EXT2_REVISION >= 1.0, use size_high as well.
return m_raw_inode.size_low;
}
mode_t mode() const override
{
return m_raw_inode.mode & 07777;
}
nlink_t nlinks() const override
{
return m_raw_inode.nlinks;
}
u32 uid() const override
{
return m_raw_inode.uid;
}
u32 gid() const override
{
return m_raw_inode.gid;
}
usize inode_number() const override
{
return m_inum;
}
VFS::FileSystem* fs() const override
{
return m_fs;
}
void did_link() override
{
}
void did_unlink() override
{
}
Result<void> chmod(mode_t) override
{
return err(EROFS);
}
Result<void> chown(u32, u32) override
{
return err(EROFS);
}
Result<usize> read(u8* buf, usize offset, usize length) const override;
Result<usize> write(const u8*, usize, usize) override
{
return err(EROFS);
}
Result<void> truncate(usize) override
{
return err(EROFS);
}
Inode(Badge<FileSystem>, FileSystem* fs);
virtual ~Inode() = default;
private:
VFS::InodeType m_type;
RawInode m_raw_inode;
FileSystem* m_fs;
ino_t m_inum;
friend class FileSystem;
};
}

View File

@ -63,6 +63,8 @@ Result<u64> sys_openat(Registers*, SyscallArgs args)
inode->chown(current->auth.euid, current->auth.egid); inode->chown(current->auth.euid, current->auth.egid);
} }
if ((flags & O_WRONLY) && inode->fs() && inode->fs()->is_readonly()) return err(EROFS);
if (inode->type() != VFS::InodeType::Directory && (flags & O_DIRECTORY)) return err(ENOTDIR); if (inode->type() != VFS::InodeType::Directory && (flags & O_DIRECTORY)) return err(ENOTDIR);
if (inode->type() == VFS::InodeType::Directory) if (inode->type() == VFS::InodeType::Directory)

View File

@ -32,6 +32,13 @@ template <typename K, typename V> struct HashMap
return p->value; return p->value;
} }
V* try_get_ref(const K& key)
{
auto* p = m_table.try_find(HashPair<K, V> { key, {} });
if (!p) return nullptr;
return p->value.value_ptr();
}
bool try_remove(const K& key) bool try_remove(const K& key)
{ {
return m_table.try_remove(HashPair<K, V> { key, {} }); return m_table.try_remove(HashPair<K, V> { key, {} });