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/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..12841fae 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(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); - if (mount(target.chars(), fstype.chars()) < 0) + if (mount(target.chars(), fstype.chars(), source.chars()) < 0) { perror("mount"); return 1; diff --git a/initrd/etc/init/02-ext2fs b/initrd/etc/init/02-ext2fs new file mode 100644 index 00000000..a03c3bce --- /dev/null +++ b/initrd/etc/init/02-ext2fs @@ -0,0 +1,3 @@ +Name=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/mount-tmpfs b/initrd/sbin/mount-tmpfs index 7625ebd7..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 +mount -t tmpfs tmpfs /tmp chmod 1777 /tmp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index eae441b3..de8c9847 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -47,6 +47,8 @@ set(SOURCES src/fs/GPT.cpp 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/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/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/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 4533033c..8abf7602 100644 --- a/kernel/src/fs/VFS.h +++ b/kernel/src/fs/VFS.h @@ -36,6 +36,13 @@ namespace VFS virtual Result set_mount_dir(SharedPtr parent) = 0; + virtual Result reset_mount_dir() = 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 new file mode 100644 index 00000000..31a80ec6 --- /dev/null +++ b/kernel/src/fs/ext2/FileSystem.cpp @@ -0,0 +1,133 @@ +#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 +{ + FileSystem::FileSystem() + { + } + + Result> FileSystem::find_inode_by_number(ino_t inum, bool initialize_dir_now) + { + 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: 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 * 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 + + 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) + { + 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())); + 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. + + 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->m_root_inode = TRY(fs->find_inode_by_number(2, true)); + + return (SharedPtr)fs; + } + + Result FileSystem::set_mount_dir(SharedPtr inode) + { + 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 new file mode 100644 index 00000000..cd2945fd --- /dev/null +++ b/kernel/src/fs/ext2/FileSystem.h @@ -0,0 +1,197 @@ +#pragma once +#include "fs/VFS.h" +#include "fs/devices/DeviceRegistry.h" +#include + +#define EXT2_MAGIC 0xef53 + +#define EXT2_REQUIRED_COMPAT_D_TYPE 0x0002 + +namespace Ext2 +{ + struct [[gnu::packed]] Superblock + { + u32 nr_inodes; + u32 nr_blocks; + u32 nr_reserved_blocks; + u32 nr_free_blocks; + u32 nr_free_inodes; + u32 first_data_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; + 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 + { + 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]; + }; + + 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); + + class Inode; + + 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; + + Result reset_mount_dir() override; + + bool is_readonly() const override + { + return true; + } + + 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, bool initialize_dir_now = false); + Result find_block_group_descriptor(u32 index); + + virtual ~FileSystem() = default; + + private: + FileSystem(); + + SharedPtr m_root_inode; + + SharedPtr m_host_device; + dev_t m_host_device_id; + + Superblock m_superblock; + + 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; + + 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 new file mode 100644 index 00000000..309a4278 --- /dev/null +++ b/kernel/src/fs/ext2/Inode.cpp @@ -0,0 +1,205 @@ +#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(); + } +} diff --git a/kernel/src/fs/ext2/Inode.h b/kernel/src/fs/ext2/Inode.h new file mode 100644 index 00000000..5c2973d0 --- /dev/null +++ b/kernel/src/fs/ext2/Inode.h @@ -0,0 +1,156 @@ +#pragma once +#include "fs/ext2/FileSystem.h" +#include +#include + +#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::Inode + { + public: + VFS::InodeType type() const override + { + return m_type; + } + + usize size() const override; + + 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); + } + + 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 readlink() override; + + Result lazy_initialize_dir() const; + + // FIXME: Implement readlink() and device numbers. + + Inode(Badge, FileSystem* fs); + virtual ~Inode() = default; + + private: + VFS::InodeType m_type; + RawInode m_raw_inode; + 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 }; + + Result find_block(usize index) const; + + friend class FileSystem; + }; +} 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 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/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/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/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index 090e6ca9..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; @@ -235,9 +235,9 @@ 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()) - old_thread->directory = MMU::get_page_directory(); - if (new_thread->directory) MMU::switch_page_directory(new_thread->directory); + if (MMU::get_page_directory() != MMU::kernel_page_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); } 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/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, {} }); 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; 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(), 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..fd2865a5 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 -fakeroot -u -i $LUNA_ROOT/.fakeroot -- mkbootimg luna.json Luna.iso +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