Compare commits

..

14 Commits

Author SHA1 Message Date
21d093b1fa
initrd: Rename test-ext2fs to mount-ext2fs
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-06-25 20:54:37 +02:00
e9e7b22323
kernel: Separate a thread's page directory into two
All checks were successful
continuous-integration/drone/pr Build is passing
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.
2023-06-25 20:35:40 +02:00
5f4103251a
kernel: Preserve the new page directory while exec() is running 2023-06-25 20:35:40 +02:00
491416ddaf
tools: Bump up the ext2 filesystem size 2023-06-25 20:35:39 +02:00
da689dd1a7
kernel/ext2: Allow reading up to 4 MB of data from files
This is done by scanning the singly indirect pointer of the inode.
2023-06-25 20:35:39 +02:00
41f578aa18
kernel/ext2: Add support for symbolic links 2023-06-25 20:35:38 +02:00
34e1ef36b1
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.
2023-06-25 20:35:38 +02:00
a62265b504
kernel/ext2: Implement directory traversal 2023-06-25 20:35:37 +02:00
d7486326bf
tools: Generate the Ext2 filesystem using genext2fs instead
mkbootimg's filenames have some kind of bug...
2023-06-25 20:35:37 +02:00
4fe6c506ec
kernel/ext2: Implement Inode::read() 2023-06-25 20:35:37 +02:00
77686b26f8
kernel/Ext2: Read the root inode metadata from the disk 2023-06-25 20:35:36 +02:00
2aa7056e11
mount: Put the source argument first 2023-06-25 20:35:36 +02:00
a9460469d9
kernel+libc+apps: Add a source parameter to the mount() system call 2023-06-25 20:35:35 +02:00
707f64acb5
kernel: Add an Ext2 filesystem skeleton 2023-06-25 20:35:35 +02:00
30 changed files with 781 additions and 23 deletions

View File

@ -11,7 +11,7 @@ steps:
image: ubuntu image: ubuntu
commands: commands:
- apt update - 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 - wget https://pub.cloudapio.eu/luna/toolchains/ci-toolchain-arm64.tar.gz --quiet
- tar xf ci-toolchain-arm64.tar.gz - tar xf ci-toolchain-arm64.tar.gz
- rm ci-toolchain-arm64.tar.gz - rm ci-toolchain-arm64.tar.gz

View File

@ -279,7 +279,7 @@ static void mount_devfs()
{ {
if (mkdir("/dev", 0755) < 0 && errno != EEXIST) exit(255); 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() int main()

View File

@ -6,15 +6,17 @@ Result<int> luna_main(int argc, char** argv)
{ {
StringView target; StringView target;
StringView fstype { "auto" }; StringView fstype { "auto" };
StringView source;
os::ArgumentParser parser; os::ArgumentParser parser;
parser.add_description("Mount a file system."); parser.add_description("Mount a file system.");
parser.add_system_program_info("mount"_sv); 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_positional_argument(target, "mountpoint"_sv, true);
parser.add_value_argument(fstype, 't', "type"_sv, "the file system type to use"); parser.add_value_argument(fstype, 't', "type"_sv, "the file system type to use");
parser.parse(argc, argv); parser.parse(argc, argv);
if (mount(target.chars(), fstype.chars()) < 0) if (mount(target.chars(), fstype.chars(), source.chars()) < 0)
{ {
perror("mount"); perror("mount");
return 1; return 1;

View File

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

2
initrd/sbin/mount-ext2fs Normal file
View File

@ -0,0 +1,2 @@
mkdir /mnt
mount -t ext2 /dev/cd0p2 /mnt

View File

@ -1,5 +1,5 @@
#!/bin/sh #!/bin/sh
mkdir -p /tmp mkdir -p /tmp
mount -t tmpfs /tmp mount -t tmpfs tmpfs /tmp
chmod 1777 /tmp chmod 1777 /tmp

View File

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

View File

@ -7,6 +7,7 @@ target_compile_definitions(moon PRIVATE EXEC_DEBUG)
target_compile_definitions(moon PRIVATE OPEN_DEBUG) target_compile_definitions(moon PRIVATE OPEN_DEBUG)
target_compile_definitions(moon PRIVATE REAP_DEBUG) target_compile_definitions(moon PRIVATE REAP_DEBUG)
target_compile_definitions(moon PRIVATE PCI_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 DEVICE_REGISTRY_DEBUG)
target_compile_definitions(moon PRIVATE FORK_DEBUG) target_compile_definitions(moon PRIVATE FORK_DEBUG)
target_compile_options(moon PRIVATE -fsanitize=undefined) target_compile_options(moon PRIVATE -fsanitize=undefined)

View File

@ -146,7 +146,7 @@ namespace ATA
{ {
if (drive == m_current_drive) return; if (drive == m_current_drive) return;
u8 value = (drive << 4) | 0xa0; u8 value = (u8)(drive << 4) | 0xa0;
write_register(Register::DriveSelect, value); write_register(Register::DriveSelect, value);
delay_400ns(); delay_400ns();

View File

@ -222,6 +222,7 @@ namespace VFS
g_root_inode = new_root_inode; g_root_inode = new_root_inode;
TRY(new_root_parent_inode->replace_entry(((MountInode*)g_root_inode.ptr())->source(), new_root_path.chars())); TRY(new_root_parent_inode->replace_entry(((MountInode*)g_root_inode.ptr())->source(), new_root_path.chars()));
((MountInode*)g_root_inode.ptr())->set_source({}); ((MountInode*)g_root_inode.ptr())->set_source({});
g_root_inode->fs()->reset_mount_dir();
return {}; return {};
} }

View File

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

View File

@ -0,0 +1,133 @@
#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
{
FileSystem::FileSystem()
{
}
Result<SharedPtr<VFS::Inode>> 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<VFS::Inode>)inode;
}
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()));
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<VFS::FileSystem>)fs;
}
Result<void> FileSystem::set_mount_dir(SharedPtr<VFS::Inode> inode)
{
return m_root_inode->replace_entry(inode, "..");
}
Result<void> FileSystem::reset_mount_dir()
{
return m_root_inode->replace_entry(m_root_inode, "..");
}
}

View File

@ -0,0 +1,197 @@
#pragma once
#include "fs/VFS.h"
#include "fs/devices/DeviceRegistry.h"
#include <luna/HashMap.h>
#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<VFS::Inode> root_inode() const override
{
return m_root_inode;
}
Result<SharedPtr<VFS::Inode>> create_file_inode() override
{
return err(EROFS);
}
Result<SharedPtr<VFS::Inode>> create_dir_inode(SharedPtr<VFS::Inode>) override
{
return err(EROFS);
}
Result<SharedPtr<VFS::Inode>> create_device_inode(u32, u32) override
{
return err(EROFS);
}
Result<SharedPtr<VFS::Inode>> create_symlink_inode(StringView) override
{
return err(EROFS);
}
Result<void> set_mount_dir(SharedPtr<VFS::Inode>) override;
Result<void> reset_mount_dir() override;
bool is_readonly() const override
{
return true;
}
static Result<SharedPtr<VFS::FileSystem>> create(SharedPtr<Device> host_device);
dev_t host_device_id() const override
{
return m_host_device_id;
}
Result<SharedPtr<VFS::Inode>> find_inode_by_number(ino_t inode, bool initialize_dir_now = false);
Result<const BlockGroupDescriptor*> find_block_group_descriptor(u32 index);
virtual ~FileSystem() = default;
private:
FileSystem();
SharedPtr<VFS::Inode> m_root_inode;
SharedPtr<Device> 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<ino_t, SharedPtr<VFS::Inode>> m_inode_cache;
HashMap<u32, BlockGroupDescriptor> m_block_group_descriptor_cache;
friend class Inode;
};
}

View File

@ -0,0 +1,205 @@
#include "fs/ext2/Inode.h"
#include <luna/Buffer.h>
namespace Ext2
{
Inode::Inode(Badge<FileSystem>, 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<usize> 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<usize> 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<u32*>(&m_singly_indirect_block.data()[block_index]);
}
Result<void> 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<u8>(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<void> Inode::replace_entry(SharedPtr<VFS::Inode> 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<SharedPtr<VFS::Inode>> 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<VFS::DirectoryEntry> 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<StringView> 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();
}
}

156
kernel/src/fs/ext2/Inode.h Normal file
View File

@ -0,0 +1,156 @@
#pragma once
#include "fs/ext2/FileSystem.h"
#include <luna/Buffer.h>
#include <luna/String.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::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<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);
}
Result<SharedPtr<VFS::Inode>> find(const char*) const override;
Option<VFS::DirectoryEntry> get(usize) const override;
Result<SharedPtr<VFS::Inode>> create_file(const char*) override
{
if (m_type != VFS::InodeType::Directory) return err(ENOTDIR);
return err(EROFS);
}
Result<SharedPtr<VFS::Inode>> create_subdirectory(const char*) override
{
if (m_type != VFS::InodeType::Directory) return err(ENOTDIR);
return err(EROFS);
}
Result<void> add_entry(SharedPtr<VFS::Inode>, const char*) override
{
if (m_type != VFS::InodeType::Directory) return err(ENOTDIR);
return err(EROFS);
}
Result<void> replace_entry(SharedPtr<VFS::Inode>, const char*) override;
Result<void> 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<StringView> readlink() override;
Result<void> lazy_initialize_dir() const;
// FIXME: Implement readlink() and device numbers.
Inode(Badge<FileSystem>, 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<VFS::DirectoryEntry> m_entries;
mutable bool m_dir_already_lazily_initialized { false };
Result<usize> find_block(usize index) const;
friend class FileSystem;
};
}

View File

@ -72,6 +72,11 @@ namespace TmpFS
return m_root_inode->replace_entry(parent, ".."); return m_root_inode->replace_entry(parent, "..");
} }
Result<void> FileSystem::reset_mount_dir()
{
return m_root_inode->replace_entry(m_root_inode, "..");
}
void FileSystem::set_root(SharedPtr<VFS::Inode> root) void FileSystem::set_root(SharedPtr<VFS::Inode> root)
{ {
m_root_inode = root; m_root_inode = root;

View File

@ -20,6 +20,8 @@ namespace TmpFS
Result<void> set_mount_dir(SharedPtr<VFS::Inode> parent) override; Result<void> set_mount_dir(SharedPtr<VFS::Inode> parent) override;
Result<void> reset_mount_dir() override;
static Result<SharedPtr<VFS::FileSystem>> create(); static Result<SharedPtr<VFS::FileSystem>> create();
dev_t host_device_id() const override dev_t host_device_id() const override

View File

@ -70,7 +70,7 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
kdbgln("exec: attempting to replace current image with %s", path.chars()); kdbgln("exec: attempting to replace current image with %s", path.chars());
#endif #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)); auto image = TRY(ThreadImage::try_load_from_elf(inode));
@ -99,7 +99,7 @@ Result<u64> 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_setuid(inode)) current->auth.euid = current->auth.suid = inode->uid();
if (VFS::is_setgid(inode)) current->auth.egid = current->auth.sgid = inode->gid(); if (VFS::is_setgid(inode)) current->auth.egid = current->auth.sgid = inode->gid();
@ -108,7 +108,7 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
image->apply(current); 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); current->set_arguments(user_argc, user_argv, user_envc, user_envp);
@ -123,7 +123,7 @@ Result<u64> sys_fork(Registers* regs, SyscallArgs)
{ {
auto current = Scheduler::current(); 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(&current->regs, regs, sizeof(*regs)); memcpy(&current->regs, regs, sizeof(*regs));

View File

@ -1,4 +1,5 @@
#include "fs/VFS.h" #include "fs/VFS.h"
#include "fs/ext2/FileSystem.h"
#include "fs/tmpfs/FileSystem.h" #include "fs/tmpfs/FileSystem.h"
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "sys/Syscall.h" #include "sys/Syscall.h"
@ -8,15 +9,28 @@ Result<u64> sys_mount(Registers*, SyscallArgs args)
{ {
auto target = TRY(MemoryManager::strdup_from_user(args[0])); auto target = TRY(MemoryManager::strdup_from_user(args[0]));
auto fstype = TRY(MemoryManager::strdup_from_user(args[1])); auto fstype = TRY(MemoryManager::strdup_from_user(args[1]));
auto source = TRY(MemoryManager::strdup_from_user(args[2]));
auto* current = Scheduler::current(); auto* current = Scheduler::current();
if (current->auth.euid != 0) return err(EPERM); if (current->auth.euid != 0) return err(EPERM);
auto get_source = [current, &source]() -> Result<SharedPtr<Device>> {
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<VFS::FileSystem> fs; SharedPtr<VFS::FileSystem> fs;
if (fstype.view() == "tmpfs") fs = TRY(TmpFS::FileSystem::create()); if (fstype.view() == "tmpfs") fs = TRY(TmpFS::FileSystem::create());
else if (fstype.view() == "devfs") else if (fstype.view() == "devfs")
fs = TRY(DeviceRegistry::create_devfs_instance()); 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 else
return err(ENODEV); return err(ENODEV);

View File

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

View File

@ -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; delete thread;
@ -235,9 +235,9 @@ namespace Scheduler
{ {
switch_context(old_thread, new_thread, regs); switch_context(old_thread, new_thread, regs);
if (!old_thread->is_kernel) old_thread->fp_data.save(); 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(); old_thread->active_directory = MMU::get_page_directory();
if (new_thread->directory) MMU::switch_page_directory(new_thread->directory); if (new_thread->active_directory) MMU::switch_page_directory(new_thread->active_directory);
if (!new_thread->is_kernel) if (!new_thread->is_kernel)
{ {
CPU::switch_kernel_stack(new_thread->kernel_stack.top()); CPU::switch_kernel_stack(new_thread->kernel_stack.top());

View File

@ -97,7 +97,8 @@ struct Thread : public LinkedListNode<Thread>
Thread* parent { nullptr }; Thread* parent { nullptr };
Option<pid_t> child_being_waited_for = {}; Option<pid_t> child_being_waited_for = {};
PageDirectory* directory; PageDirectory* self_directory;
PageDirectory* active_directory { nullptr };
[[noreturn]] void exit_and_signal_parent(u8 status); [[noreturn]] void exit_and_signal_parent(u8 status);

View File

@ -69,7 +69,7 @@ Result<OwnedPtr<ThreadImage>> ThreadImage::clone_from_thread(Thread* parent)
auto vm_allocator = TRY(parent->vm_allocator->clone()); 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() }; const ELFData data = { .entry = parent->ip() };
@ -124,7 +124,8 @@ void ThreadImage::apply(Thread* thread)
thread->stack = m_user_stack; thread->stack = m_user_stack;
thread->set_sp(align_down<16>(m_sp)); 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); thread->vm_allocator = move(m_vm_allocator);
} }

View File

@ -9,7 +9,7 @@ extern "C"
#endif #endif
/* Mount a file system on target. */ /* 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. */ /* Unmount the file system mounted on target. */
int umount(const char* target); int umount(const char* target);

View File

@ -5,9 +5,9 @@
extern "C" 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); __errno_return(rc, int);
} }

View File

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

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <luna/Alloc.h> #include <luna/Alloc.h>
#include <luna/Atomic.h> #include <luna/Atomic.h>
#include <luna/Hash.h>
#include <luna/OwnedPtr.h> #include <luna/OwnedPtr.h>
#include <luna/Result.h> #include <luna/Result.h>
#include <luna/ScopeGuard.h> #include <luna/ScopeGuard.h>
@ -84,6 +85,11 @@ template <typename T> class SharedPtr
return *this; return *this;
} }
bool operator==(const SharedPtr<T>& other)
{
return m_ptr == other.m_ptr && m_ref_count == other.m_ref_count;
}
T* ptr() const T* ptr() const
{ {
return m_ptr; return m_ptr;

View File

@ -25,6 +25,16 @@ template <usize Size> class StaticString
m_length = length; 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) void adopt(StringView string)
{ {
usize length = strlcpy(m_buffer, string.chars(), usize length = strlcpy(m_buffer, string.chars(),

View File

@ -16,9 +16,8 @@
}, },
{ {
"type": "ext2", "type": "ext2",
"size": 2, "file": "build/ext2fs.bin",
"directory": "base", "name": "luna-rootfs"
"name": "sysroot"
} }
] ]
} }

View File

@ -7,4 +7,6 @@ cd $LUNA_ROOT
fakeroot -u -s $LUNA_ROOT/.fakeroot -- tools/install.sh 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