kernel/Ext2: Read the root inode metadata from the disk
All checks were successful
continuous-integration/drone/pr Build is passing
All checks were successful
continuous-integration/drone/pr Build is passing
This commit is contained in:
parent
1461fffba8
commit
62ac42ffc3
3
initrd/etc/init/02-ext2fs
Normal file
3
initrd/etc/init/02-ext2fs
Normal file
@ -0,0 +1,3 @@
|
||||
Name=ext2fs
|
||||
Script=/sbin/test-ext2fs
|
||||
Wait=true
|
4
initrd/sbin/test-ext2fs
Normal file
4
initrd/sbin/test-ext2fs
Normal file
@ -0,0 +1,4 @@
|
||||
# (for testing) Remove this automatic mount once ext2 is working.
|
||||
|
||||
mkdir /mnt
|
||||
mount -t ext2 /dev/cd0p2 /mnt
|
@ -48,6 +48,7 @@ set(SOURCES
|
||||
src/fs/tmpfs/FileSystem.cpp
|
||||
src/fs/tmpfs/Inode.cpp
|
||||
src/fs/ext2/FileSystem.cpp
|
||||
src/fs/ext2/Inode.cpp
|
||||
src/fs/devices/DeviceRegistry.cpp
|
||||
src/fs/devices/NullDevice.cpp
|
||||
src/fs/devices/ZeroDevice.cpp
|
||||
|
@ -36,6 +36,11 @@ namespace VFS
|
||||
|
||||
virtual Result<void> set_mount_dir(SharedPtr<Inode> parent) = 0;
|
||||
|
||||
virtual bool is_readonly() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual u64 handles() const
|
||||
{
|
||||
return m_handles;
|
||||
|
@ -1,4 +1,23 @@
|
||||
#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
|
||||
{
|
||||
@ -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();
|
||||
|
||||
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.
|
||||
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)
|
||||
{
|
||||
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 (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.
|
||||
return err(ENOTSUP);
|
||||
}
|
||||
|
@ -11,10 +11,10 @@ namespace Ext2
|
||||
{
|
||||
u32 nr_inodes;
|
||||
u32 nr_blocks;
|
||||
u32 nr_reserved;
|
||||
u32 nr_free_inodes;
|
||||
u32 nr_reserved_blocks;
|
||||
u32 nr_free_blocks;
|
||||
u32 superblock_block;
|
||||
u32 nr_free_inodes;
|
||||
u32 first_data_block;
|
||||
u32 log_block_size;
|
||||
u32 log_fragment_size;
|
||||
u32 blocks_per_block_group;
|
||||
@ -22,23 +22,64 @@ namespace Ext2
|
||||
u32 inodes_per_block_group;
|
||||
u32 last_mount_time;
|
||||
u32 last_write_time;
|
||||
|
||||
u16 mounts_since_last_fsck;
|
||||
u16 mounts_allowed_before_fsck;
|
||||
u16 signature;
|
||||
u16 fs_state;
|
||||
u16 error_action;
|
||||
u16 minor_version;
|
||||
|
||||
u32 last_fsck_time;
|
||||
u32 fsck_time_interval;
|
||||
u32 os_id;
|
||||
u32 major_version;
|
||||
|
||||
u16 reserved_block_uid;
|
||||
u16 reserved_block_gid;
|
||||
// TODO: Add extended superblock fields.
|
||||
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(BlockGroupDescriptor) == 32);
|
||||
static_assert(sizeof(RawInode) == 128);
|
||||
|
||||
class FileSystem : public VFS::FileSystem
|
||||
{
|
||||
@ -73,6 +114,11 @@ namespace Ext2
|
||||
return {};
|
||||
}
|
||||
|
||||
bool is_readonly() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static Result<SharedPtr<VFS::FileSystem>> create(SharedPtr<Device> host_device);
|
||||
|
||||
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<const BlockGroupDescriptor*> find_block_group_descriptor(u32 index);
|
||||
|
||||
virtual ~FileSystem() = default;
|
||||
|
||||
@ -94,8 +141,13 @@ namespace Ext2
|
||||
|
||||
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
|
||||
// not worrying about that as for now the filesystem implementation is read-only.
|
||||
HashMap<ino_t, SharedPtr<VFS::Inode>> m_inode_cache;
|
||||
|
||||
HashMap<u32, BlockGroupDescriptor> m_block_group_descriptor_cache;
|
||||
};
|
||||
}
|
||||
|
13
kernel/src/fs/ext2/Inode.cpp
Normal file
13
kernel/src/fs/ext2/Inode.cpp
Normal 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()");
|
||||
}
|
||||
}
|
99
kernel/src/fs/ext2/Inode.h
Normal file
99
kernel/src/fs/ext2/Inode.h
Normal 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;
|
||||
};
|
||||
}
|
@ -63,6 +63,8 @@ Result<u64> sys_openat(Registers*, SyscallArgs args)
|
||||
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)
|
||||
|
@ -32,6 +32,13 @@ template <typename K, typename V> struct HashMap
|
||||
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)
|
||||
{
|
||||
return m_table.try_remove(HashPair<K, V> { key, {} });
|
||||
|
Loading…
Reference in New Issue
Block a user