From 707f64acb50b1cf3fe2dee5b0d78f03d71f0c548 Mon Sep 17 00:00:00 2001 From: apio Date: Tue, 20 Jun 2023 21:39:22 +0200 Subject: [PATCH 01/14] kernel: Add an Ext2 filesystem skeleton --- kernel/src/fs/ext2/FileSystem.cpp | 27 ++++++++ kernel/src/fs/ext2/FileSystem.h | 100 ++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 kernel/src/fs/ext2/FileSystem.cpp create mode 100644 kernel/src/fs/ext2/FileSystem.h diff --git a/kernel/src/fs/ext2/FileSystem.cpp b/kernel/src/fs/ext2/FileSystem.cpp new file mode 100644 index 00000000..d77af270 --- /dev/null +++ b/kernel/src/fs/ext2/FileSystem.cpp @@ -0,0 +1,27 @@ +#include "fs/ext2/FileSystem.h" + +namespace Ext2 +{ + FileSystem::FileSystem() + { + } + + Result> FileSystem::find_inode_by_number(ino_t inode) + { + auto maybe_inode = m_inode_cache.try_get(inode); + if (maybe_inode.has_value()) return maybe_inode.value(); + + return err(ENOENT); + } + + Result> FileSystem::create(SharedPtr host_device) + { + SharedPtr fs = TRY(adopt_shared_if_nonnull(new (std::nothrow) FileSystem())); + const usize nread = TRY(host_device->read((u8*)&fs->m_superblock, 1024, 1024)); + 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. + + // TODO: Implement basic Ext2 reading, enough to be able to mount a volume. + return err(ENOTSUP); + } +} diff --git a/kernel/src/fs/ext2/FileSystem.h b/kernel/src/fs/ext2/FileSystem.h new file mode 100644 index 00000000..157edf2e --- /dev/null +++ b/kernel/src/fs/ext2/FileSystem.h @@ -0,0 +1,100 @@ +#pragma once +#include "fs/VFS.h" +#include "fs/devices/DeviceRegistry.h" +#include + +#define EXT2_MAGIC 0xef53 + +namespace Ext2 +{ + struct [[gnu::packed]] Superblock + { + u32 nr_inodes; + u32 nr_blocks; + u32 nr_reserved; + u32 nr_free_inodes; + u32 nr_free_blocks; + u32 superblock_block; + u32 log_block_size; + u32 log_fragment_size; + u32 blocks_per_block_group; + u32 fragments_per_block_group; + 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; + u8 padding[1024 - 84]; + }; + + static_assert(sizeof(Superblock) == 1024); + + class FileSystem : public VFS::FileSystem + { + public: + SharedPtr root_inode() const override + { + return m_root_inode; + } + + Result> create_file_inode() override + { + return err(EROFS); + } + + Result> create_dir_inode(SharedPtr) override + { + return err(EROFS); + } + + Result> create_device_inode(u32, u32) override + { + return err(EROFS); + } + + Result> create_symlink_inode(StringView) override + { + return err(EROFS); + } + + Result set_mount_dir(SharedPtr) override + { + return {}; + } + + static Result> create(SharedPtr host_device); + + dev_t host_device_id() const override + { + return m_host_device_id; + } + + Result> find_inode_by_number(ino_t inode); + + virtual ~FileSystem() = default; + + private: + FileSystem(); + + SharedPtr m_root_inode; + + SharedPtr m_host_device; + dev_t m_host_device_id; + + Superblock m_superblock; + + // 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> m_inode_cache; + }; +} -- 2.34.1 From a9460469d9145f937a262964dca6e1dad13e5773 Mon Sep 17 00:00:00 2001 From: apio Date: Tue, 20 Jun 2023 21:39:41 +0200 Subject: [PATCH 02/14] kernel+libc+apps: Add a source parameter to the mount() system call --- apps/init.cpp | 2 +- apps/mount.cpp | 4 +++- initrd/sbin/mount-tmpfs | 2 +- kernel/CMakeLists.txt | 1 + kernel/src/fs/ext2/FileSystem.cpp | 1 + kernel/src/fs/ext2/FileSystem.h | 1 + kernel/src/sys/mount.cpp | 14 ++++++++++++++ libc/include/sys/mount.h | 2 +- libc/src/sys/mount.cpp | 4 ++-- libluna/include/luna/SharedPtr.h | 6 ++++++ 10 files changed, 31 insertions(+), 6 deletions(-) diff --git a/apps/init.cpp b/apps/init.cpp index afc47547..2bec17ac 100644 --- a/apps/init.cpp +++ b/apps/init.cpp @@ -279,7 +279,7 @@ static void mount_devfs() { if (mkdir("/dev", 0755) < 0 && errno != EEXIST) exit(255); - if (mount("/dev", "devfs") < 0) exit(255); + if (mount("/dev", "devfs", "devfs") < 0) exit(255); } int main() diff --git a/apps/mount.cpp b/apps/mount.cpp index b5762219..1f9b48c0 100644 --- a/apps/mount.cpp +++ b/apps/mount.cpp @@ -6,15 +6,17 @@ Result luna_main(int argc, char** argv) { StringView target; StringView fstype { "auto" }; + StringView source; os::ArgumentParser parser; parser.add_description("Mount a file system."); parser.add_system_program_info("mount"_sv); parser.add_positional_argument(target, "mountpoint"_sv, true); + parser.add_positional_argument(source, "source"_sv, true); parser.add_value_argument(fstype, 't', "type"_sv, "the file system type to use"); parser.parse(argc, argv); - if (mount(target.chars(), fstype.chars()) < 0) + if (mount(target.chars(), fstype.chars(), source.chars()) < 0) { perror("mount"); return 1; diff --git a/initrd/sbin/mount-tmpfs b/initrd/sbin/mount-tmpfs index 7625ebd7..a86562e7 100644 --- a/initrd/sbin/mount-tmpfs +++ b/initrd/sbin/mount-tmpfs @@ -1,5 +1,5 @@ #!/bin/sh mkdir -p /tmp -mount -t tmpfs /tmp +mount -t tmpfs /tmp tmpfs chmod 1777 /tmp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index eae441b3..31b8b940 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -47,6 +47,7 @@ set(SOURCES src/fs/GPT.cpp src/fs/tmpfs/FileSystem.cpp src/fs/tmpfs/Inode.cpp + src/fs/ext2/FileSystem.cpp src/fs/devices/DeviceRegistry.cpp src/fs/devices/NullDevice.cpp src/fs/devices/ZeroDevice.cpp diff --git a/kernel/src/fs/ext2/FileSystem.cpp b/kernel/src/fs/ext2/FileSystem.cpp index d77af270..b4cf7862 100644 --- a/kernel/src/fs/ext2/FileSystem.cpp +++ b/kernel/src/fs/ext2/FileSystem.cpp @@ -11,6 +11,7 @@ namespace Ext2 auto maybe_inode = m_inode_cache.try_get(inode); if (maybe_inode.has_value()) return maybe_inode.value(); + // TODO: Locate the inode's block group descriptor and find it in the block group's inode table. return err(ENOENT); } diff --git a/kernel/src/fs/ext2/FileSystem.h b/kernel/src/fs/ext2/FileSystem.h index 157edf2e..c6801f61 100644 --- a/kernel/src/fs/ext2/FileSystem.h +++ b/kernel/src/fs/ext2/FileSystem.h @@ -34,6 +34,7 @@ namespace Ext2 u32 major_version; u16 reserved_block_uid; u16 reserved_block_gid; + // TODO: Add extended superblock fields. u8 padding[1024 - 84]; }; diff --git a/kernel/src/sys/mount.cpp b/kernel/src/sys/mount.cpp index e9e6a6c5..b8e210a3 100644 --- a/kernel/src/sys/mount.cpp +++ b/kernel/src/sys/mount.cpp @@ -1,4 +1,5 @@ #include "fs/VFS.h" +#include "fs/ext2/FileSystem.h" #include "fs/tmpfs/FileSystem.h" #include "memory/MemoryManager.h" #include "sys/Syscall.h" @@ -8,15 +9,28 @@ Result sys_mount(Registers*, SyscallArgs args) { auto target = TRY(MemoryManager::strdup_from_user(args[0])); auto fstype = TRY(MemoryManager::strdup_from_user(args[1])); + auto source = TRY(MemoryManager::strdup_from_user(args[2])); auto* current = Scheduler::current(); if (current->auth.euid != 0) return err(EPERM); + auto get_source = [current, &source]() -> Result> { + auto inode = TRY(VFS::resolve_path(source.chars(), current->auth, current->current_directory)); + if (inode->type() != VFS::InodeType::BlockDevice) return err(ENOTBLK); + dev_t device_id = inode->device_id(); + return TRY(DeviceRegistry::fetch_special_device(luna_dev_major(device_id), luna_dev_minor(device_id))); + }; + SharedPtr fs; if (fstype.view() == "tmpfs") fs = TRY(TmpFS::FileSystem::create()); else if (fstype.view() == "devfs") fs = TRY(DeviceRegistry::create_devfs_instance()); + else if (fstype.view() == "ext2") + { + auto source_device = TRY(get_source()); + fs = TRY(Ext2::FileSystem::create(source_device)); + } else return err(ENODEV); diff --git a/libc/include/sys/mount.h b/libc/include/sys/mount.h index 2711e287..618e6cc9 100644 --- a/libc/include/sys/mount.h +++ b/libc/include/sys/mount.h @@ -9,7 +9,7 @@ extern "C" #endif /* Mount a file system on target. */ - int mount(const char* target, const char* fstype); + int mount(const char* target, const char* fstype, const char* source); /* Unmount the file system mounted on target. */ int umount(const char* target); diff --git a/libc/src/sys/mount.cpp b/libc/src/sys/mount.cpp index cf8384ee..99e2deb6 100644 --- a/libc/src/sys/mount.cpp +++ b/libc/src/sys/mount.cpp @@ -5,9 +5,9 @@ extern "C" { - int mount(const char* target, const char* fstype) + int mount(const char* target, const char* fstype, const char* source) { - long rc = syscall(SYS_mount, target, fstype); + long rc = syscall(SYS_mount, target, fstype, source); __errno_return(rc, int); } diff --git a/libluna/include/luna/SharedPtr.h b/libluna/include/luna/SharedPtr.h index 020c37a5..a71f372c 100644 --- a/libluna/include/luna/SharedPtr.h +++ b/libluna/include/luna/SharedPtr.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include #include #include @@ -84,6 +85,11 @@ template class SharedPtr return *this; } + bool operator==(const SharedPtr& other) + { + return m_ptr == other.m_ptr && m_ref_count == other.m_ref_count; + } + T* ptr() const { return m_ptr; -- 2.34.1 From 2aa7056e11c2e5a31e5f3f19d938a71558b8bed7 Mon Sep 17 00:00:00 2001 From: apio Date: Tue, 20 Jun 2023 21:44:09 +0200 Subject: [PATCH 03/14] mount: Put the source argument first --- apps/mount.cpp | 2 +- initrd/sbin/mount-tmpfs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/mount.cpp b/apps/mount.cpp index 1f9b48c0..12841fae 100644 --- a/apps/mount.cpp +++ b/apps/mount.cpp @@ -11,8 +11,8 @@ Result luna_main(int argc, char** argv) os::ArgumentParser parser; parser.add_description("Mount a file system."); parser.add_system_program_info("mount"_sv); - parser.add_positional_argument(target, "mountpoint"_sv, true); parser.add_positional_argument(source, "source"_sv, true); + parser.add_positional_argument(target, "mountpoint"_sv, true); parser.add_value_argument(fstype, 't', "type"_sv, "the file system type to use"); parser.parse(argc, argv); diff --git a/initrd/sbin/mount-tmpfs b/initrd/sbin/mount-tmpfs index a86562e7..ece039a3 100644 --- a/initrd/sbin/mount-tmpfs +++ b/initrd/sbin/mount-tmpfs @@ -1,5 +1,5 @@ #!/bin/sh mkdir -p /tmp -mount -t tmpfs /tmp tmpfs +mount -t tmpfs tmpfs /tmp chmod 1777 /tmp -- 2.34.1 From 77686b26f8c6fee9e630c70d5b9c1cffa9193e2f Mon Sep 17 00:00:00 2001 From: apio Date: Wed, 21 Jun 2023 21:32:28 +0200 Subject: [PATCH 04/14] kernel/Ext2: Read the root inode metadata from the disk --- initrd/etc/init/02-ext2fs | 3 + initrd/sbin/test-ext2fs | 4 ++ kernel/CMakeLists.txt | 1 + kernel/src/fs/VFS.h | 5 ++ kernel/src/fs/ext2/FileSystem.cpp | 75 ++++++++++++++++++++++- kernel/src/fs/ext2/FileSystem.h | 58 +++++++++++++++++- kernel/src/fs/ext2/Inode.cpp | 13 ++++ kernel/src/fs/ext2/Inode.h | 99 +++++++++++++++++++++++++++++++ kernel/src/sys/open.cpp | 2 + libluna/include/luna/HashMap.h | 7 +++ 10 files changed, 262 insertions(+), 5 deletions(-) create mode 100644 initrd/etc/init/02-ext2fs create mode 100644 initrd/sbin/test-ext2fs create mode 100644 kernel/src/fs/ext2/Inode.cpp create mode 100644 kernel/src/fs/ext2/Inode.h diff --git a/initrd/etc/init/02-ext2fs b/initrd/etc/init/02-ext2fs new file mode 100644 index 00000000..eeaf3c3e --- /dev/null +++ b/initrd/etc/init/02-ext2fs @@ -0,0 +1,3 @@ +Name=ext2fs +Script=/sbin/test-ext2fs +Wait=true diff --git a/initrd/sbin/test-ext2fs b/initrd/sbin/test-ext2fs new file mode 100644 index 00000000..e172354c --- /dev/null +++ b/initrd/sbin/test-ext2fs @@ -0,0 +1,4 @@ +# (for testing) Remove this automatic mount once ext2 is working. + +mkdir /mnt +mount -t ext2 /dev/cd0p2 /mnt diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 31b8b940..de8c9847 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -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 diff --git a/kernel/src/fs/VFS.h b/kernel/src/fs/VFS.h index 4533033c..cdb5b6ff 100644 --- a/kernel/src/fs/VFS.h +++ b/kernel/src/fs/VFS.h @@ -36,6 +36,11 @@ namespace VFS virtual Result set_mount_dir(SharedPtr parent) = 0; + virtual bool is_readonly() const + { + return false; + } + virtual u64 handles() const { return m_handles; diff --git a/kernel/src/fs/ext2/FileSystem.cpp b/kernel/src/fs/ext2/FileSystem.cpp index b4cf7862..36695b00 100644 --- a/kernel/src/fs/ext2/FileSystem.cpp +++ b/kernel/src/fs/ext2/FileSystem.cpp @@ -1,4 +1,23 @@ #include "fs/ext2/FileSystem.h" +#include "fs/ext2/Inode.h" +#include + +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> FileSystem::find_inode_by_number(ino_t inode) + Result> 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 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> FileSystem::create(SharedPtr host_device) { SharedPtr 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); } diff --git a/kernel/src/fs/ext2/FileSystem.h b/kernel/src/fs/ext2/FileSystem.h index c6801f61..f7f424a0 100644 --- a/kernel/src/fs/ext2/FileSystem.h +++ b/kernel/src/fs/ext2/FileSystem.h @@ -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> create(SharedPtr host_device); dev_t host_device_id() const override @@ -81,6 +127,7 @@ namespace Ext2 } Result> find_inode_by_number(ino_t inode); + Result 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> m_inode_cache; + + HashMap m_block_group_descriptor_cache; }; } diff --git a/kernel/src/fs/ext2/Inode.cpp b/kernel/src/fs/ext2/Inode.cpp new file mode 100644 index 00000000..e1af5fd1 --- /dev/null +++ b/kernel/src/fs/ext2/Inode.cpp @@ -0,0 +1,13 @@ +#include "fs/ext2/Inode.h" + +namespace Ext2 +{ + Inode::Inode(Badge, FileSystem* fs) : m_fs(fs) + { + } + + Result Inode::read(u8* /*buf*/, usize /*offset*/, usize /*length*/) const + { + fail("FIXME: Ext2::Inode::read()"); + } +} diff --git a/kernel/src/fs/ext2/Inode.h b/kernel/src/fs/ext2/Inode.h new file mode 100644 index 00000000..0e69bd33 --- /dev/null +++ b/kernel/src/fs/ext2/Inode.h @@ -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 chmod(mode_t) override + { + return err(EROFS); + } + + Result chown(u32, u32) override + { + return err(EROFS); + } + + Result read(u8* buf, usize offset, usize length) const override; + + Result write(const u8*, usize, usize) override + { + return err(EROFS); + } + + Result truncate(usize) override + { + return err(EROFS); + } + + Inode(Badge, FileSystem* fs); + virtual ~Inode() = default; + + private: + VFS::InodeType m_type; + RawInode m_raw_inode; + FileSystem* m_fs; + ino_t m_inum; + + friend class FileSystem; + }; +} diff --git a/kernel/src/sys/open.cpp b/kernel/src/sys/open.cpp index 4bb2ffc6..ce160a19 100644 --- a/kernel/src/sys/open.cpp +++ b/kernel/src/sys/open.cpp @@ -63,6 +63,8 @@ Result 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) diff --git a/libluna/include/luna/HashMap.h b/libluna/include/luna/HashMap.h index ba149304..639151d5 100644 --- a/libluna/include/luna/HashMap.h +++ b/libluna/include/luna/HashMap.h @@ -32,6 +32,13 @@ template struct HashMap return p->value; } + V* try_get_ref(const K& key) + { + auto* p = m_table.try_find(HashPair { key, {} }); + if (!p) return nullptr; + return p->value.value_ptr(); + } + bool try_remove(const K& key) { return m_table.try_remove(HashPair { key, {} }); -- 2.34.1 From 4fe6c506eca3c4b60a7376c188a58dd514d2f4b1 Mon Sep 17 00:00:00 2001 From: apio Date: Thu, 22 Jun 2023 16:34:22 +0200 Subject: [PATCH 05/14] kernel/ext2: Implement Inode::read() --- kernel/src/fs/ext2/FileSystem.h | 4 +++ kernel/src/fs/ext2/Inode.cpp | 60 +++++++++++++++++++++++++++++++-- kernel/src/fs/ext2/Inode.h | 2 ++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/kernel/src/fs/ext2/FileSystem.h b/kernel/src/fs/ext2/FileSystem.h index f7f424a0..9bc06762 100644 --- a/kernel/src/fs/ext2/FileSystem.h +++ b/kernel/src/fs/ext2/FileSystem.h @@ -81,6 +81,8 @@ namespace Ext2 static_assert(sizeof(BlockGroupDescriptor) == 32); static_assert(sizeof(RawInode) == 128); + class Inode; + class FileSystem : public VFS::FileSystem { public: @@ -149,5 +151,7 @@ namespace Ext2 HashMap> m_inode_cache; HashMap m_block_group_descriptor_cache; + + friend class Inode; }; } diff --git a/kernel/src/fs/ext2/Inode.cpp b/kernel/src/fs/ext2/Inode.cpp index e1af5fd1..96a9d29d 100644 --- a/kernel/src/fs/ext2/Inode.cpp +++ b/kernel/src/fs/ext2/Inode.cpp @@ -6,8 +6,64 @@ namespace Ext2 { } - Result Inode::read(u8* /*buf*/, usize /*offset*/, usize /*length*/) const + Result Inode::read(u8* buf, usize offset, usize length) const { - fail("FIXME: Ext2::Inode::read()"); + 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 = find_block(offset / block_size); + usize size_to_read = block_size - block_offset; + + 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 = 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 = 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; + } + + usize Inode::find_block(usize index) const + { + expect(index < 12, "ext2: Finding blocks in the indirect pointers is not yet supported"); + + return m_raw_inode.direct_pointers[index]; } } diff --git a/kernel/src/fs/ext2/Inode.h b/kernel/src/fs/ext2/Inode.h index 0e69bd33..53cfd2b8 100644 --- a/kernel/src/fs/ext2/Inode.h +++ b/kernel/src/fs/ext2/Inode.h @@ -94,6 +94,8 @@ namespace Ext2 FileSystem* m_fs; ino_t m_inum; + usize find_block(usize index) const; + friend class FileSystem; }; } -- 2.34.1 From d7486326bf5a272fef29999b7ce0b98dd74e3292 Mon Sep 17 00:00:00 2001 From: apio Date: Thu, 22 Jun 2023 19:39:43 +0200 Subject: [PATCH 06/14] tools: Generate the Ext2 filesystem using genext2fs instead mkbootimg's filenames have some kind of bug... --- .drone.yml | 2 +- luna.json | 5 ++--- tools/make-iso.sh | 2 ++ 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.drone.yml b/.drone.yml index f6fdfbb6..e42bf022 100644 --- a/.drone.yml +++ b/.drone.yml @@ -11,7 +11,7 @@ steps: image: ubuntu commands: - apt update - - apt install build-essential cmake ninja-build wget nasm -y + - apt install build-essential cmake ninja-build wget nasm genext2fs -y - wget https://pub.cloudapio.eu/luna/toolchains/ci-toolchain-arm64.tar.gz --quiet - tar xf ci-toolchain-arm64.tar.gz - rm ci-toolchain-arm64.tar.gz diff --git a/luna.json b/luna.json index 978f701d..dd2d4731 100644 --- a/luna.json +++ b/luna.json @@ -16,9 +16,8 @@ }, { "type": "ext2", - "size": 2, - "directory": "base", - "name": "sysroot" + "file": "build/ext2fs.bin", + "name": "luna-rootfs" } ] } diff --git a/tools/make-iso.sh b/tools/make-iso.sh index 3e92e6b7..71c5dfe8 100755 --- a/tools/make-iso.sh +++ b/tools/make-iso.sh @@ -7,4 +7,6 @@ cd $LUNA_ROOT fakeroot -u -s $LUNA_ROOT/.fakeroot -- tools/install.sh +genext2fs -d base -B 4096 -b 512 -L luna-rootfs -U -N 512 build/ext2fs.bin + fakeroot -u -i $LUNA_ROOT/.fakeroot -- mkbootimg luna.json Luna.iso -- 2.34.1 From a62265b504c5fee9b433389d988acf2417346344 Mon Sep 17 00:00:00 2001 From: apio Date: Thu, 22 Jun 2023 19:41:35 +0200 Subject: [PATCH 07/14] kernel/ext2: Implement directory traversal --- kernel/debug.cmake | 1 + kernel/src/fs/ext2/FileSystem.cpp | 45 +++++++++++--- kernel/src/fs/ext2/FileSystem.h | 52 +++++++++++++--- kernel/src/fs/ext2/Inode.cpp | 96 +++++++++++++++++++++++++++++ kernel/src/fs/ext2/Inode.h | 59 ++++++++++++++++-- libluna/include/luna/StaticString.h | 10 +++ 6 files changed, 242 insertions(+), 21 deletions(-) diff --git a/kernel/debug.cmake b/kernel/debug.cmake index 0dfb5512..0cd0778f 100644 --- a/kernel/debug.cmake +++ b/kernel/debug.cmake @@ -7,6 +7,7 @@ target_compile_definitions(moon PRIVATE EXEC_DEBUG) target_compile_definitions(moon PRIVATE OPEN_DEBUG) target_compile_definitions(moon PRIVATE REAP_DEBUG) target_compile_definitions(moon PRIVATE PCI_DEBUG) +target_compile_definitions(moon PRIVATE EXT2_DEBUG) target_compile_definitions(moon PRIVATE DEVICE_REGISTRY_DEBUG) target_compile_definitions(moon PRIVATE FORK_DEBUG) target_compile_options(moon PRIVATE -fsanitize=undefined) diff --git a/kernel/src/fs/ext2/FileSystem.cpp b/kernel/src/fs/ext2/FileSystem.cpp index 36695b00..1be95315 100644 --- a/kernel/src/fs/ext2/FileSystem.cpp +++ b/kernel/src/fs/ext2/FileSystem.cpp @@ -25,7 +25,7 @@ namespace Ext2 { } - Result> FileSystem::find_inode_by_number(ino_t inum) + Result> FileSystem::find_inode_by_number(ino_t inum, bool initialize_dir_now) { check(inum < m_superblock.nr_inodes); @@ -37,23 +37,29 @@ namespace Ext2 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. + // FIXME: Even if the inode size is bigger (Ext2::FileSystem::m_inode_size), we only read this amount. Enlarge + // the Inode structure to fit this case. 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); + const u64 inode_address = (block_group_descriptor->inode_table_start * m_block_size) + (index * m_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; +#ifdef EXT2_DEBUG 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()); +#endif - // TODO: Locate the inode's block group descriptor and find it in the block group's inode table. - return err(ENOENT); + m_inode_cache.try_set(inum, inode); + + if (initialize_dir_now && inode->m_type == VFS::InodeType::Directory) TRY(inode->lazy_initialize_dir()); + + return (SharedPtr)inode; } Result FileSystem::find_block_group_descriptor(u32 index) @@ -80,20 +86,43 @@ 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. + if (fs->m_superblock.major_version >= 1) + { + auto required = fs->m_superblock.ext_superblock.required_features; + if (required & EXT2_REQUIRED_COMPAT_D_TYPE) fs->m_dirs_have_type_field = true; + + required &= ~EXT2_REQUIRED_COMPAT_D_TYPE; + + if (required > 0) + { + kwarnln("ext2: File system has required features not supported by the implementation, cannot mount"); + return err(EINVAL); + } + + fs->m_uses_extended_size = true; + fs->m_inode_size = fs->m_superblock.ext_superblock.inode_size; + } + 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); +#ifdef EXT2_DEBUG 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); +#endif // Lookup the root inode. - fs->find_inode_by_number(2); + fs->m_root_inode = TRY(fs->find_inode_by_number(2, true)); - // TODO: Implement basic Ext2 reading, enough to be able to mount a volume. - return err(ENOTSUP); + return (SharedPtr)fs; + } + + Result FileSystem::set_mount_dir(SharedPtr inode) + { + return m_root_inode->replace_entry(inode, ".."); } } diff --git a/kernel/src/fs/ext2/FileSystem.h b/kernel/src/fs/ext2/FileSystem.h index 9bc06762..812dd079 100644 --- a/kernel/src/fs/ext2/FileSystem.h +++ b/kernel/src/fs/ext2/FileSystem.h @@ -5,6 +5,8 @@ #define EXT2_MAGIC 0xef53 +#define EXT2_REQUIRED_COMPAT_D_TYPE 0x0002 + namespace Ext2 { struct [[gnu::packed]] Superblock @@ -37,8 +39,27 @@ namespace Ext2 u16 reserved_block_uid; u16 reserved_block_gid; - // TODO: Add extended superblock fields. - u8 padding[1024 - 84]; + struct + { + u32 first_non_reserved_inode; + u16 inode_size; + u16 this_block_group; + u32 optional_features; + u32 required_features; + u32 ro_features; + u8 fsid[16]; + u8 name[16]; + u8 last_mountpoint[64]; + u32 compression_algs; + u8 nr_preallocated_blocks_for_files; + u8 nr_preallocated_blocks_for_dirs; + u16 unused; + u8 journal_id[16]; + u32 journal_inode; + u32 journal_device; + u32 orphan_inode_head; + } ext_superblock; + u8 padding[1024 - 236]; }; struct [[gnu::packed]] BlockGroupDescriptor @@ -77,6 +98,21 @@ namespace Ext2 u32 os_specific_2[3]; }; + struct [[gnu::packed]] RawDirectoryEntry + { + u32 inum; + u16 size; + union { + u16 name_length; + struct + { + u8 name_length_low; + u8 type; + }; + }; + char name[4]; // Names should be padded to a multiple of 4 bytes. + }; + static_assert(sizeof(Superblock) == 1024); static_assert(sizeof(BlockGroupDescriptor) == 32); static_assert(sizeof(RawInode) == 128); @@ -111,10 +147,7 @@ namespace Ext2 return err(EROFS); } - Result set_mount_dir(SharedPtr) override - { - return {}; - } + Result set_mount_dir(SharedPtr) override; bool is_readonly() const override { @@ -128,7 +161,7 @@ namespace Ext2 return m_host_device_id; } - Result> find_inode_by_number(ino_t inode); + Result> find_inode_by_number(ino_t inode, bool initialize_dir_now = false); Result find_block_group_descriptor(u32 index); virtual ~FileSystem() = default; @@ -146,6 +179,11 @@ namespace Ext2 u64 m_block_size; u64 m_block_groups; + u32 m_inode_size { 128 }; + + bool m_dirs_have_type_field { false }; + bool m_uses_extended_size { false }; + // 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> m_inode_cache; diff --git a/kernel/src/fs/ext2/Inode.cpp b/kernel/src/fs/ext2/Inode.cpp index 96a9d29d..da6bd70b 100644 --- a/kernel/src/fs/ext2/Inode.cpp +++ b/kernel/src/fs/ext2/Inode.cpp @@ -1,4 +1,5 @@ #include "fs/ext2/Inode.h" +#include namespace Ext2 { @@ -6,6 +7,13 @@ namespace Ext2 { } + 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; @@ -22,6 +30,7 @@ namespace Ext2 usize block_offset = (offset % block_size); usize block = 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; @@ -66,4 +75,91 @@ namespace Ext2 return m_raw_inode.direct_pointers[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]; + } } diff --git a/kernel/src/fs/ext2/Inode.h b/kernel/src/fs/ext2/Inode.h index 53cfd2b8..2084cebd 100644 --- a/kernel/src/fs/ext2/Inode.h +++ b/kernel/src/fs/ext2/Inode.h @@ -11,7 +11,7 @@ namespace Ext2 { - class Inode : public VFS::FileInode + class Inode : public VFS::Inode { public: VFS::InodeType type() const override @@ -19,11 +19,7 @@ namespace Ext2 return m_type; } - usize size() const override - { - // FIXME: If EXT2_REVISION >= 1.0, use size_high as well. - return m_raw_inode.size_low; - } + usize size() const override; mode_t mode() const override { @@ -85,6 +81,54 @@ namespace Ext2 return err(EROFS); } + Result> find(const char*) const override; + + Option get(usize) const override; + + Result> create_file(const char*) override + { + if (m_type != VFS::InodeType::Directory) return err(ENOTDIR); + + return err(EROFS); + } + + Result> create_subdirectory(const char*) override + { + if (m_type != VFS::InodeType::Directory) return err(ENOTDIR); + + return err(EROFS); + } + + Result add_entry(SharedPtr, const char*) override + { + if (m_type != VFS::InodeType::Directory) return err(ENOTDIR); + + return err(EROFS); + } + + Result replace_entry(SharedPtr, const char*) override; + + Result remove_entry(const char*) override + { + if (m_type != VFS::InodeType::Directory) return err(ENOTDIR); + + return err(EROFS); + } + + usize entries() const override + { + return m_entries.size(); + } + + bool blocking() const override + { + return false; + } + + Result lazy_initialize_dir() const; + + // FIXME: Implement readlink() and device numbers. + Inode(Badge, FileSystem* fs); virtual ~Inode() = default; @@ -94,6 +138,9 @@ namespace Ext2 FileSystem* m_fs; ino_t m_inum; + mutable Vector m_entries; + mutable bool m_dir_already_lazily_initialized { false }; + usize find_block(usize index) const; friend class FileSystem; diff --git a/libluna/include/luna/StaticString.h b/libluna/include/luna/StaticString.h index 33e79b11..19be34a6 100644 --- a/libluna/include/luna/StaticString.h +++ b/libluna/include/luna/StaticString.h @@ -25,6 +25,16 @@ template class StaticString m_length = length; } + void adopt(const char* string, usize length) + { + if (length > Size) length = Size; + + memcpy(m_buffer, string, length); + + m_buffer[length] = 0; + m_length = length; + } + void adopt(StringView string) { usize length = strlcpy(m_buffer, string.chars(), -- 2.34.1 From 34e1ef36b12a13f3eccead171f1f3f817990daea Mon Sep 17 00:00:00 2001 From: apio Date: Thu, 22 Jun 2023 19:57:12 +0200 Subject: [PATCH 08/14] kernel: Make pivot_root() reset the parent entry of the new root directory Otherwise it would just be pointing to the old parent fs, and we don't want that. --- kernel/src/fs/VFS.cpp | 1 + kernel/src/fs/VFS.h | 2 ++ kernel/src/fs/ext2/FileSystem.cpp | 5 +++++ kernel/src/fs/ext2/FileSystem.h | 2 ++ kernel/src/fs/tmpfs/FileSystem.cpp | 5 +++++ kernel/src/fs/tmpfs/FileSystem.h | 2 ++ 6 files changed, 17 insertions(+) diff --git a/kernel/src/fs/VFS.cpp b/kernel/src/fs/VFS.cpp index c57225e0..d7b8a9fa 100644 --- a/kernel/src/fs/VFS.cpp +++ b/kernel/src/fs/VFS.cpp @@ -222,6 +222,7 @@ namespace VFS g_root_inode = new_root_inode; TRY(new_root_parent_inode->replace_entry(((MountInode*)g_root_inode.ptr())->source(), new_root_path.chars())); ((MountInode*)g_root_inode.ptr())->set_source({}); + g_root_inode->fs()->reset_mount_dir(); return {}; } diff --git a/kernel/src/fs/VFS.h b/kernel/src/fs/VFS.h index cdb5b6ff..8abf7602 100644 --- a/kernel/src/fs/VFS.h +++ b/kernel/src/fs/VFS.h @@ -36,6 +36,8 @@ namespace VFS virtual Result set_mount_dir(SharedPtr parent) = 0; + virtual Result reset_mount_dir() = 0; + virtual bool is_readonly() const { return false; diff --git a/kernel/src/fs/ext2/FileSystem.cpp b/kernel/src/fs/ext2/FileSystem.cpp index 1be95315..31a80ec6 100644 --- a/kernel/src/fs/ext2/FileSystem.cpp +++ b/kernel/src/fs/ext2/FileSystem.cpp @@ -125,4 +125,9 @@ namespace Ext2 { return m_root_inode->replace_entry(inode, ".."); } + + Result FileSystem::reset_mount_dir() + { + return m_root_inode->replace_entry(m_root_inode, ".."); + } } diff --git a/kernel/src/fs/ext2/FileSystem.h b/kernel/src/fs/ext2/FileSystem.h index 812dd079..cd2945fd 100644 --- a/kernel/src/fs/ext2/FileSystem.h +++ b/kernel/src/fs/ext2/FileSystem.h @@ -149,6 +149,8 @@ namespace Ext2 Result set_mount_dir(SharedPtr) override; + Result reset_mount_dir() override; + bool is_readonly() const override { return true; diff --git a/kernel/src/fs/tmpfs/FileSystem.cpp b/kernel/src/fs/tmpfs/FileSystem.cpp index 465fb5f5..e871e2b5 100644 --- a/kernel/src/fs/tmpfs/FileSystem.cpp +++ b/kernel/src/fs/tmpfs/FileSystem.cpp @@ -72,6 +72,11 @@ namespace TmpFS return m_root_inode->replace_entry(parent, ".."); } + Result FileSystem::reset_mount_dir() + { + return m_root_inode->replace_entry(m_root_inode, ".."); + } + void FileSystem::set_root(SharedPtr root) { m_root_inode = root; diff --git a/kernel/src/fs/tmpfs/FileSystem.h b/kernel/src/fs/tmpfs/FileSystem.h index 374b6f44..2542c740 100644 --- a/kernel/src/fs/tmpfs/FileSystem.h +++ b/kernel/src/fs/tmpfs/FileSystem.h @@ -20,6 +20,8 @@ namespace TmpFS Result set_mount_dir(SharedPtr parent) override; + Result reset_mount_dir() override; + static Result> create(); dev_t host_device_id() const override -- 2.34.1 From 41f578aa1874ad83fc27181185ec7aed00b99351 Mon Sep 17 00:00:00 2001 From: apio Date: Thu, 22 Jun 2023 20:10:09 +0200 Subject: [PATCH 09/14] kernel/ext2: Add support for symbolic links --- kernel/src/fs/ext2/Inode.cpp | 27 ++++++++++++++++++++++++++- kernel/src/fs/ext2/Inode.h | 5 +++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/kernel/src/fs/ext2/Inode.cpp b/kernel/src/fs/ext2/Inode.cpp index da6bd70b..53c063f9 100644 --- a/kernel/src/fs/ext2/Inode.cpp +++ b/kernel/src/fs/ext2/Inode.cpp @@ -1,5 +1,5 @@ #include "fs/ext2/Inode.h" -#include +#include namespace Ext2 { @@ -162,4 +162,29 @@ namespace Ext2 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(); + } } diff --git a/kernel/src/fs/ext2/Inode.h b/kernel/src/fs/ext2/Inode.h index 2084cebd..d1359795 100644 --- a/kernel/src/fs/ext2/Inode.h +++ b/kernel/src/fs/ext2/Inode.h @@ -1,5 +1,6 @@ #pragma once #include "fs/ext2/FileSystem.h" +#include #define EXT2_FIFO 0x1000 #define EXT2_CHR 0x2000 @@ -125,6 +126,8 @@ namespace Ext2 return false; } + Result readlink() override; + Result lazy_initialize_dir() const; // FIXME: Implement readlink() and device numbers. @@ -138,6 +141,8 @@ namespace Ext2 FileSystem* m_fs; ino_t m_inum; + String m_link; + mutable Vector m_entries; mutable bool m_dir_already_lazily_initialized { false }; -- 2.34.1 From da689dd1a74774551319da01c9d131228fa78595 Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 24 Jun 2023 22:21:22 +0200 Subject: [PATCH 10/14] kernel/ext2: Allow reading up to 4 MB of data from files This is done by scanning the singly indirect pointer of the inode. --- kernel/src/fs/ext2/Inode.cpp | 27 +++++++++++++++++++++------ kernel/src/fs/ext2/Inode.h | 5 ++++- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/kernel/src/fs/ext2/Inode.cpp b/kernel/src/fs/ext2/Inode.cpp index 53c063f9..309a4278 100644 --- a/kernel/src/fs/ext2/Inode.cpp +++ b/kernel/src/fs/ext2/Inode.cpp @@ -28,7 +28,7 @@ namespace Ext2 if (offset % block_size) { usize block_offset = (offset % block_size); - usize block = find_block(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; @@ -44,7 +44,7 @@ namespace Ext2 while (to_read >= block_size) { - usize block = find_block(offset / block_size); + usize block = TRY(find_block(offset / block_size)); usize host_offset = block * block_size; @@ -58,7 +58,7 @@ namespace Ext2 if (to_read > 0) { - usize block = find_block(offset / block_size); + usize block = TRY(find_block(offset / block_size)); usize host_offset = block * block_size; @@ -69,11 +69,26 @@ namespace Ext2 return length; } - usize Inode::find_block(usize index) const + Result Inode::find_block(usize index) const { - expect(index < 12, "ext2: Finding blocks in the indirect pointers is not yet supported"); + if (index < 12) return m_raw_inode.direct_pointers[index]; - 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 diff --git a/kernel/src/fs/ext2/Inode.h b/kernel/src/fs/ext2/Inode.h index d1359795..5c2973d0 100644 --- a/kernel/src/fs/ext2/Inode.h +++ b/kernel/src/fs/ext2/Inode.h @@ -1,5 +1,6 @@ #pragma once #include "fs/ext2/FileSystem.h" +#include #include #define EXT2_FIFO 0x1000 @@ -141,12 +142,14 @@ namespace Ext2 FileSystem* m_fs; ino_t m_inum; + mutable Buffer m_singly_indirect_block; + String m_link; mutable Vector m_entries; mutable bool m_dir_already_lazily_initialized { false }; - usize find_block(usize index) const; + Result find_block(usize index) const; friend class FileSystem; }; -- 2.34.1 From 491416ddaf6f517fe3d155926af514b5b01fa970 Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 24 Jun 2023 22:22:20 +0200 Subject: [PATCH 11/14] tools: Bump up the ext2 filesystem size --- tools/make-iso.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/make-iso.sh b/tools/make-iso.sh index 71c5dfe8..fd2865a5 100755 --- a/tools/make-iso.sh +++ b/tools/make-iso.sh @@ -7,6 +7,6 @@ cd $LUNA_ROOT fakeroot -u -s $LUNA_ROOT/.fakeroot -- tools/install.sh -genext2fs -d base -B 4096 -b 512 -L luna-rootfs -U -N 512 build/ext2fs.bin +genext2fs -d base -B 4096 -b 1024 -L luna-rootfs -U -N 1024 build/ext2fs.bin -fakeroot -u -i $LUNA_ROOT/.fakeroot -- mkbootimg luna.json Luna.iso +fakeroot -u -i $LUNA_ROOT/.fakeroot mkbootimg luna.json Luna.iso -- 2.34.1 From 5f4103251a0b9f0b6477a58c450dfe00ec7bdba9 Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 24 Jun 2023 22:25:18 +0200 Subject: [PATCH 12/14] kernel: Preserve the new page directory while exec() is running --- kernel/src/thread/Scheduler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index 090e6ca9..e2ac9197 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -235,7 +235,7 @@ namespace Scheduler { switch_context(old_thread, new_thread, regs); if (!old_thread->is_kernel) old_thread->fp_data.save(); - if (old_thread->is_kernel && MMU::get_page_directory() != MMU::kernel_page_directory()) + if (MMU::get_page_directory() != MMU::kernel_page_directory()) old_thread->directory = MMU::get_page_directory(); if (new_thread->directory) MMU::switch_page_directory(new_thread->directory); if (!new_thread->is_kernel) -- 2.34.1 From e9e7b223232daf198204add4b90dd02dd5f3cfd9 Mon Sep 17 00:00:00 2001 From: apio Date: Sun, 25 Jun 2023 20:09:40 +0200 Subject: [PATCH 13/14] kernel: Separate a thread's page directory into two The self directory, and the active directory. The active directory is the one the thread is currently using, and the self directory is the one the thread owns. This lets us keep track of both, which fixes ext2 executables crashing the system. --- kernel/src/arch/x86_64/disk/ATA.cpp | 2 +- kernel/src/sys/exec.cpp | 8 ++++---- kernel/src/thread/Scheduler.cpp | 6 +++--- kernel/src/thread/Thread.h | 3 ++- kernel/src/thread/ThreadImage.cpp | 5 +++-- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/kernel/src/arch/x86_64/disk/ATA.cpp b/kernel/src/arch/x86_64/disk/ATA.cpp index 7786b6a8..ecb69ce9 100644 --- a/kernel/src/arch/x86_64/disk/ATA.cpp +++ b/kernel/src/arch/x86_64/disk/ATA.cpp @@ -146,7 +146,7 @@ namespace ATA { if (drive == m_current_drive) return; - u8 value = (drive << 4) | 0xa0; + u8 value = (u8)(drive << 4) | 0xa0; write_register(Register::DriveSelect, value); delay_400ns(); diff --git a/kernel/src/sys/exec.cpp b/kernel/src/sys/exec.cpp index 1fdc13f5..9283a463 100644 --- a/kernel/src/sys/exec.cpp +++ b/kernel/src/sys/exec.cpp @@ -70,7 +70,7 @@ Result sys_execve(Registers* regs, SyscallArgs args) kdbgln("exec: attempting to replace current image with %s", path.chars()); #endif - auto guard = make_scope_guard([current] { MMU::switch_page_directory(current->directory); }); + auto guard = make_scope_guard([current] { MMU::switch_page_directory(current->self_directory); }); auto image = TRY(ThreadImage::try_load_from_elf(inode)); @@ -99,7 +99,7 @@ Result sys_execve(Registers* regs, SyscallArgs args) } } - MMU::delete_userspace_page_directory(current->directory); + MMU::delete_userspace_page_directory(current->self_directory); if (VFS::is_setuid(inode)) current->auth.euid = current->auth.suid = inode->uid(); if (VFS::is_setgid(inode)) current->auth.egid = current->auth.sgid = inode->gid(); @@ -108,7 +108,7 @@ Result sys_execve(Registers* regs, SyscallArgs args) image->apply(current); - MMU::switch_page_directory(current->directory); + MMU::switch_page_directory(current->self_directory); current->set_arguments(user_argc, user_argv, user_envc, user_envp); @@ -123,7 +123,7 @@ Result sys_fork(Registers* regs, SyscallArgs) { auto current = Scheduler::current(); - auto guard = make_scope_guard([current] { MMU::switch_page_directory(current->directory); }); + auto guard = make_scope_guard([current] { MMU::switch_page_directory(current->self_directory); }); memcpy(¤t->regs, regs, sizeof(*regs)); diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index e2ac9197..876069d0 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -191,7 +191,7 @@ namespace Scheduler } } - if (!thread->is_kernel) MMU::delete_userspace_page_directory(thread->directory); + if (!thread->is_kernel) MMU::delete_userspace_page_directory(thread->self_directory); delete thread; @@ -236,8 +236,8 @@ namespace Scheduler switch_context(old_thread, new_thread, regs); if (!old_thread->is_kernel) old_thread->fp_data.save(); if (MMU::get_page_directory() != MMU::kernel_page_directory()) - old_thread->directory = MMU::get_page_directory(); - if (new_thread->directory) MMU::switch_page_directory(new_thread->directory); + old_thread->active_directory = MMU::get_page_directory(); + if (new_thread->active_directory) MMU::switch_page_directory(new_thread->active_directory); if (!new_thread->is_kernel) { CPU::switch_kernel_stack(new_thread->kernel_stack.top()); diff --git a/kernel/src/thread/Thread.h b/kernel/src/thread/Thread.h index ad6903a6..7ac4f3b0 100644 --- a/kernel/src/thread/Thread.h +++ b/kernel/src/thread/Thread.h @@ -97,7 +97,8 @@ struct Thread : public LinkedListNode Thread* parent { nullptr }; Option child_being_waited_for = {}; - PageDirectory* directory; + PageDirectory* self_directory; + PageDirectory* active_directory { nullptr }; [[noreturn]] void exit_and_signal_parent(u8 status); diff --git a/kernel/src/thread/ThreadImage.cpp b/kernel/src/thread/ThreadImage.cpp index 63ed43a6..0c999bd8 100644 --- a/kernel/src/thread/ThreadImage.cpp +++ b/kernel/src/thread/ThreadImage.cpp @@ -69,7 +69,7 @@ Result> ThreadImage::clone_from_thread(Thread* parent) auto vm_allocator = TRY(parent->vm_allocator->clone()); - auto new_directory = TRY(MMU::clone_userspace_page_directory(parent->directory)); + auto new_directory = TRY(MMU::clone_userspace_page_directory(parent->self_directory)); const ELFData data = { .entry = parent->ip() }; @@ -124,7 +124,8 @@ void ThreadImage::apply(Thread* thread) thread->stack = m_user_stack; thread->set_sp(align_down<16>(m_sp)); - thread->directory = m_directory; + thread->self_directory = m_directory; + thread->active_directory = m_directory; thread->vm_allocator = move(m_vm_allocator); } -- 2.34.1 From 21d093b1fa4dbe5bcc020180eb4671a4140e4433 Mon Sep 17 00:00:00 2001 From: apio Date: Sun, 25 Jun 2023 20:54:37 +0200 Subject: [PATCH 14/14] initrd: Rename test-ext2fs to mount-ext2fs --- initrd/etc/init/02-ext2fs | 2 +- initrd/sbin/mount-ext2fs | 2 ++ initrd/sbin/test-ext2fs | 4 ---- 3 files changed, 3 insertions(+), 5 deletions(-) create mode 100644 initrd/sbin/mount-ext2fs delete mode 100644 initrd/sbin/test-ext2fs diff --git a/initrd/etc/init/02-ext2fs b/initrd/etc/init/02-ext2fs index eeaf3c3e..a03c3bce 100644 --- a/initrd/etc/init/02-ext2fs +++ b/initrd/etc/init/02-ext2fs @@ -1,3 +1,3 @@ Name=ext2fs -Script=/sbin/test-ext2fs +Script=/sbin/mount-ext2fs Wait=true diff --git a/initrd/sbin/mount-ext2fs b/initrd/sbin/mount-ext2fs new file mode 100644 index 00000000..94bdf961 --- /dev/null +++ b/initrd/sbin/mount-ext2fs @@ -0,0 +1,2 @@ +mkdir /mnt +mount -t ext2 /dev/cd0p2 /mnt diff --git a/initrd/sbin/test-ext2fs b/initrd/sbin/test-ext2fs deleted file mode 100644 index e172354c..00000000 --- a/initrd/sbin/test-ext2fs +++ /dev/null @@ -1,4 +0,0 @@ -# (for testing) Remove this automatic mount once ext2 is working. - -mkdir /mnt -mount -t ext2 /dev/cd0p2 /mnt -- 2.34.1