kernel+init: Add a VFS mount system and auto-populate the devfs

This commit is contained in:
apio 2023-05-17 19:40:37 +02:00
parent 4d106b6b8c
commit e7d482e78a
Signed by: apio
GPG Key ID: B8A7D06E42258954
16 changed files with 314 additions and 30 deletions

View File

@ -18,21 +18,6 @@
FILE* g_init_log;
#define xmknod(path, mode, maj, min) \
if (mknod(path, mode, makedev(maj, min)) < 0) exit(255);
// Too early for console logs (/dev/console is created here!), so we have to resort to exiting with a weird exit code in
// case of failure.
static void populate_devfs()
{
if (mkdir("/dev", 0755) < 0 && errno != EEXIST) exit(255);
xmknod("/dev/console", 0666, 1, 0);
xmknod("/dev/null", 0666, 2, 0);
xmknod("/dev/zero", 0666, 2, 1);
xmknod("/dev/fb0", 0222, 3, 0);
}
struct Service
{
String name;
@ -281,8 +266,6 @@ int main()
return 1;
}
populate_devfs();
// Before this point, we don't even have an stdin, stdout and stderr. Set it up now so that child processes (and us)
// can print stuff.
stdin = fopen("/dev/console", "r");

View File

@ -40,6 +40,7 @@ set(SOURCES
src/sys/uname.cpp
src/fs/VFS.cpp
src/fs/Pipe.cpp
src/fs/Mount.cpp
src/fs/tmpfs/FileSystem.cpp
src/fs/devices/DeviceRegistry.cpp
src/fs/devices/NullDevice.cpp

19
kernel/src/fs/Mount.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "fs/Mount.h"
LinkedList<MountInode> g_mounts;
Result<SharedPtr<VFS::Inode>> MountInode::create(SharedPtr<VFS::Inode> source, SharedPtr<VFS::FileSystem> fs)
{
auto inode = TRY(adopt_shared_if_nonnull(new (std::nothrow) MountInode()));
inode->m_source = source;
inode->m_mountee = fs;
inode->m_mount_root_inode = fs->root_inode();
auto parent = TRY(source->find(".."));
TRY(fs->set_mount_dir(parent));
g_mounts.append(inode.ptr());
return (SharedPtr<VFS::Inode>)inode;
}

150
kernel/src/fs/Mount.h Normal file
View File

@ -0,0 +1,150 @@
#pragma once
#include "fs/VFS.h"
#include <luna/LinkedList.h>
class MountInode : public VFS::Inode, public LinkedListNode<MountInode>
{
public:
static Result<SharedPtr<VFS::Inode>> create(SharedPtr<VFS::Inode> source, SharedPtr<VFS::FileSystem> fs);
void set_fs(SharedPtr<VFS::FileSystem> fs)
{
m_mountee = fs;
}
Result<SharedPtr<VFS::Inode>> find(const char* name) const override
{
return m_mount_root_inode->find(name);
}
Option<VFS::DirectoryEntry> get(usize index) const override
{
return m_mount_root_inode->get(index);
}
Result<usize> read(u8*, usize, usize) const override
{
return err(EISDIR);
}
Result<usize> write(const u8*, usize, usize) override
{
return err(EISDIR);
}
Result<void> truncate(usize) override
{
return err(EISDIR);
}
bool blocking() const override
{
return false;
}
usize size() const override
{
return 0;
}
mode_t mode() const override
{
return m_mount_root_inode->mode();
}
u32 uid() const override
{
return m_mount_root_inode->uid();
}
u32 gid() const override
{
return m_mount_root_inode->gid();
}
Result<void> chmod(mode_t mode) override
{
return m_mount_root_inode->chmod(mode);
}
Result<void> chown(u32 uid, u32 gid) override
{
return m_mount_root_inode->chown(uid, gid);
}
VFS::FileSystem* fs() const override
{
return m_mountee.ptr();
}
usize inode_number() const override
{
return m_mount_root_inode->inode_number();
}
VFS::InodeType type() const override
{
return VFS::InodeType::Directory;
}
void did_link() override
{
m_mount_root_inode->did_link();
}
void did_unlink() override
{
m_mount_root_inode->did_unlink();
}
usize entries() const override
{
return m_mount_root_inode->entries();
}
bool is_mountpoint() const override
{
return true;
}
SharedPtr<VFS::Inode> source() const
{
return m_source;
}
Result<void> remove_entry(const char* name) override
{
return m_mount_root_inode->remove_entry(name);
}
Result<SharedPtr<VFS::Inode>> create_file(const char* name) override
{
return m_mount_root_inode->create_file(name);
}
Result<SharedPtr<VFS::Inode>> create_subdirectory(const char* name) override
{
return m_mount_root_inode->create_subdirectory(name);
}
Result<void> add_entry(SharedPtr<VFS::Inode> inode, const char* name) override
{
return m_mount_root_inode->add_entry(inode, name);
}
Result<void> replace_entry(SharedPtr<VFS::Inode> inode, const char* name) override
{
return m_mount_root_inode->replace_entry(inode, name);
}
virtual ~MountInode() = default;
private:
SharedPtr<VFS::Inode> m_source;
SharedPtr<VFS::FileSystem> m_mountee;
SharedPtr<VFS::Inode> m_mount_root_inode;
MountInode() = default;
};
extern LinkedList<MountInode> g_mounts;

View File

@ -1,5 +1,6 @@
#include "fs/VFS.h"
#include "Log.h"
#include "fs/Mount.h"
#include "thread/Thread.h"
#include <bits/modes.h>
#include <luna/PathParser.h>
@ -97,6 +98,8 @@ namespace VFS
bool can_execute(SharedPtr<Inode> inode, Credentials auth)
{
if (auth.euid == 0) return true;
if (inode->uid() == auth.euid) { return inode->mode() & S_IXUSR; }
if (inode->gid() == auth.egid) { return inode->mode() & S_IXGRP; }
@ -105,6 +108,8 @@ namespace VFS
bool can_write(SharedPtr<Inode> inode, Credentials auth)
{
if (auth.euid == 0) return true;
if (inode->uid() == auth.euid) { return inode->mode() & S_IWUSR; }
if (inode->gid() == auth.egid) { return inode->mode() & S_IWGRP; }
@ -113,6 +118,8 @@ namespace VFS
bool can_read(SharedPtr<Inode> inode, Credentials auth)
{
if (auth.euid == 0) return true;
if (inode->uid() == auth.euid) { return inode->mode() & S_IRUSR; }
if (inode->gid() == auth.egid) { return inode->mode() & S_IRGRP; }
@ -128,4 +135,33 @@ namespace VFS
{
return inode->mode() & S_ISGID;
}
Result<void> mount_root(SharedPtr<VFS::FileSystem> fs)
{
root_fs = fs;
return {};
}
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Credentials auth,
SharedPtr<VFS::Inode> working_directory)
{
auto parser = TRY(PathParser::create(path));
auto parent_path = TRY(parser.dirname());
auto child = TRY(parser.basename());
kdbgln("vfs: mounting filesystem on target %s, parent dir %s", child.chars(), parent_path.chars());
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, working_directory));
auto inode = TRY(parent_inode->find(child.chars()));
if (inode->type() != VFS::InodeType::Directory) return err(ENOTDIR);
if (inode->is_mountpoint()) return err(EBUSY);
auto mount = TRY(MountInode::create(inode, fs));
TRY(parent_inode->replace_entry(mount, child.chars()));
return {};
}
}

View File

@ -46,6 +46,8 @@ namespace VFS
virtual Result<void> remove_entry(const char* name) = 0;
virtual Result<void> replace_entry(SharedPtr<Inode> inode, const char* name) = 0;
virtual usize entries() const = 0;
// File-specific methods
@ -72,6 +74,11 @@ namespace VFS
return 0;
}
virtual bool is_mountpoint() const
{
return false;
}
virtual void did_link() = 0;
virtual void did_unlink() = 0;
@ -118,6 +125,11 @@ namespace VFS
return err(ENOTDIR);
}
Result<void> replace_entry(SharedPtr<Inode>, const char*) override
{
return err(ENOTDIR);
}
Result<void> remove_entry(const char*) override
{
return err(ENOTDIR);
@ -169,6 +181,11 @@ namespace VFS
return err(ENOTDIR);
}
Result<void> replace_entry(SharedPtr<Inode>, const char*) override
{
return err(ENOTDIR);
}
Result<void> remove_entry(const char*) override
{
return err(ENOTDIR);
@ -198,10 +215,28 @@ namespace VFS
virtual Result<SharedPtr<Inode>> create_device_inode(u32 major, u32 minor) = 0;
virtual ~FileSystem() = default;
};
virtual Result<void> set_mount_dir(SharedPtr<Inode> parent) = 0;
extern SharedPtr<FileSystem> root_fs;
virtual u64 handles() const
{
return m_handles;
}
virtual void add_handle()
{
m_handles++;
}
virtual void remove_handle()
{
m_handles--;
}
virtual ~FileSystem() = default;
protected:
u64 m_handles { 0 };
};
Result<SharedPtr<Inode>> resolve_path(const char* path, Credentials auth,
SharedPtr<VFS::Inode> working_directory = {});
@ -221,4 +256,8 @@ namespace VFS
bool is_setgid(SharedPtr<Inode> inode);
Inode& root_inode();
Result<void> mount_root(SharedPtr<VFS::FileSystem> fs);
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Credentials auth,
SharedPtr<Inode> working_directory = {});
}

View File

@ -18,7 +18,7 @@ static bool g_echo { true };
Result<void> ConsoleDevice::create()
{
auto device = (SharedPtr<Device>)TRY(make_shared<ConsoleDevice>());
return DeviceRegistry::register_special_device(DeviceRegistry::Console, 0, device);
return DeviceRegistry::register_special_device(DeviceRegistry::Console, 0, device, "console");
}
Result<usize> ConsoleDevice::read(u8* buf, usize, usize length) const

View File

@ -1,9 +1,12 @@
#include "fs/devices/DeviceRegistry.h"
#include "Log.h"
#include "fs/VFS.h"
#include "fs/devices/ConsoleDevice.h"
#include "fs/devices/FramebufferDevice.h"
#include "fs/devices/NullDevice.h"
#include "fs/devices/ZeroDevice.h"
#include "fs/tmpfs/FileSystem.h"
#include "thread/Thread.h"
#include <luna/Vector.h>
struct DeviceDescriptor
@ -11,10 +14,14 @@ struct DeviceDescriptor
SharedPtr<Device> device;
u32 major;
u32 minor;
const char* name;
mode_t mode;
};
Vector<DeviceDescriptor> g_available_devices = {};
SharedPtr<VFS::FileSystem> g_device_fs;
namespace DeviceRegistry
{
Result<SharedPtr<Device>> fetch_special_device(u32 major, u32 minor)
@ -27,20 +34,39 @@ namespace DeviceRegistry
return err(ENODEV);
}
Result<void> register_special_device(u32 major, u32 minor, SharedPtr<Device> device)
Result<void> register_special_device(u32 major, u32 minor, SharedPtr<Device> device, const char* name, mode_t mode)
{
for (const auto& descriptor : g_available_devices)
{
if (descriptor.major == major && descriptor.minor == minor) return err(EEXIST);
}
kdbgln("DeviceRegistry: registered new device type %u:%u", major, minor);
kdbgln("DeviceRegistry: registered new device type %u:%u at path /dev/%s", major, minor, name);
return g_available_devices.try_append(DeviceDescriptor { .device = device, .major = major, .minor = minor });
auto desc = DeviceDescriptor { .device = device, .major = major, .minor = minor, .name = name, .mode = mode };
TRY(g_available_devices.try_append(desc));
TRY(create_special_device_inode(desc));
return {};
}
Result<void> create_special_device_inode(DeviceDescriptor& descriptor)
{
auto inode = TRY(g_device_fs->create_device_inode(descriptor.major, descriptor.minor));
inode->chmod(descriptor.mode);
TRY(g_device_fs->root_inode()->add_entry(inode, descriptor.name));
return {};
}
Result<void> init()
{
auto device_fs = TRY(TmpFS::FileSystem::create());
TRY(VFS::mount("/dev", device_fs, Credentials {}));
g_device_fs = device_fs;
NullDevice::create();
ZeroDevice::create();
ConsoleDevice::create();

View File

@ -2,6 +2,7 @@
#include "fs/devices/Device.h"
#include <luna/SharedPtr.h>
#include <sys/types.h>
namespace DeviceRegistry
{
@ -15,7 +16,8 @@ namespace DeviceRegistry
Result<SharedPtr<Device>> fetch_special_device(u32 major, u32 minor);
Result<void> register_special_device(u32 major, u32 minor, SharedPtr<Device> device);
Result<void> register_special_device(u32 major, u32 minor, SharedPtr<Device> device, const char* name,
mode_t mode = 0666);
Result<void> init();
}

View File

@ -6,7 +6,7 @@
Result<void> FramebufferDevice::create()
{
auto device = (SharedPtr<Device>)TRY(make_shared<FramebufferDevice>());
return DeviceRegistry::register_special_device(DeviceRegistry::Framebuffer, 0, device);
return DeviceRegistry::register_special_device(DeviceRegistry::Framebuffer, 0, device, "fb0", 0600);
}
Result<usize> FramebufferDevice::read(u8*, usize, usize) const

View File

@ -3,5 +3,5 @@
Result<void> NullDevice::create()
{
auto device = (SharedPtr<Device>)TRY(make_shared<NullDevice>());
return DeviceRegistry::register_special_device(DeviceRegistry::Memory, 0, device);
return DeviceRegistry::register_special_device(DeviceRegistry::Memory, 0, device, "null");
}

View File

@ -3,5 +3,5 @@
Result<void> ZeroDevice::create()
{
auto device = (SharedPtr<Device>)TRY(make_shared<ZeroDevice>());
return DeviceRegistry::register_special_device(DeviceRegistry::Memory, 1, device);
return DeviceRegistry::register_special_device(DeviceRegistry::Memory, 1, device, "zero");
}

View File

@ -1,4 +1,5 @@
#include "fs/tmpfs/FileSystem.h"
#include "fs/Mount.h"
#include "fs/devices/DeviceRegistry.h"
#include <luna/Alloc.h>
#include <luna/CString.h>
@ -49,6 +50,11 @@ namespace TmpFS
return (SharedPtr<VFS::Inode>)inode;
}
Result<void> FileSystem::set_mount_dir(SharedPtr<VFS::Inode> parent)
{
return m_root_inode->replace_entry(parent, "..");
}
void FileSystem::set_root(SharedPtr<VFS::Inode> root)
{
m_root_inode = root;
@ -64,6 +70,20 @@ namespace TmpFS
return err(ENOENT);
}
Result<void> DirInode::replace_entry(SharedPtr<VFS::Inode> inode, const char* name)
{
for (auto& entry : m_entries)
{
if (!strcmp(name, entry.name.chars()))
{
entry.inode = inode;
return {};
}
}
return err(ENOENT);
}
Option<VFS::DirectoryEntry> DirInode::get(usize index) const
{
if (index >= m_entries.size()) return {};
@ -90,6 +110,8 @@ namespace TmpFS
if (inode->type() == VFS::InodeType::Directory && inode->entries() != 2) return err(ENOTEMPTY);
if (inode->is_mountpoint()) return err(EBUSY);
m_entries.remove_first_matching(
[&](const VFS::DirectoryEntry& entry) { return !strcmp(entry.name.chars(), name); });

View File

@ -21,6 +21,8 @@ namespace TmpFS
Result<SharedPtr<VFS::Inode>> create_dir_inode(SharedPtr<VFS::Inode> parent) override;
Result<SharedPtr<VFS::Inode>> create_device_inode(u32 major, u32 minor) override;
Result<void> set_mount_dir(SharedPtr<VFS::Inode> parent) override;
static Result<SharedPtr<VFS::FileSystem>> create();
virtual ~FileSystem() = default;
@ -341,6 +343,7 @@ namespace TmpFS
Result<SharedPtr<VFS::Inode>> create_subdirectory(const char* name) override;
Result<void> add_entry(SharedPtr<VFS::Inode> inode, const char* name);
Result<void> replace_entry(SharedPtr<VFS::Inode> inode, const char* name);
virtual ~DirInode() = default;

View File

@ -42,9 +42,10 @@ Result<void> init()
kinfoln("Used memory: %s", to_dynamic_unit(MemoryManager::used()).release_value().chars());
kinfoln("Reserved memory: %s", to_dynamic_unit(MemoryManager::reserved()).release_value().chars());
VFS::root_fs = TRY(TmpFS::FileSystem::create());
auto root = TRY(TmpFS::FileSystem::create());
TRY(VFS::mount_root(root));
TRY(InitRD::populate_vfs());
TRY(DeviceRegistry::init());
InitRD::populate_vfs();
auto init = TRY(VFS::resolve_path("/bin/init", Credentials {}));
auto init_thread = TRY(Scheduler::new_userspace_thread(init, "/bin/init"));

View File

@ -26,3 +26,5 @@ chmod a+s initrd/bin/su
mkdir -p initrd/home/selene
chown 1000:1000 initrd/home/selene
mkdir -p initrd/dev