diff --git a/apps/init.cpp b/apps/init.cpp index 2b23681b..baf907da 100644 --- a/apps/init.cpp +++ b/apps/init.cpp @@ -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"); diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 622aa392..0106b734 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -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 diff --git a/kernel/src/fs/Mount.cpp b/kernel/src/fs/Mount.cpp new file mode 100644 index 00000000..950d492a --- /dev/null +++ b/kernel/src/fs/Mount.cpp @@ -0,0 +1,19 @@ +#include "fs/Mount.h" + +LinkedList g_mounts; + +Result> MountInode::create(SharedPtr source, SharedPtr 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)inode; +} diff --git a/kernel/src/fs/Mount.h b/kernel/src/fs/Mount.h new file mode 100644 index 00000000..9cefc6f9 --- /dev/null +++ b/kernel/src/fs/Mount.h @@ -0,0 +1,150 @@ +#pragma once +#include "fs/VFS.h" +#include + +class MountInode : public VFS::Inode, public LinkedListNode +{ + public: + static Result> create(SharedPtr source, SharedPtr fs); + + void set_fs(SharedPtr fs) + { + m_mountee = fs; + } + + Result> find(const char* name) const override + { + return m_mount_root_inode->find(name); + } + + Option get(usize index) const override + { + return m_mount_root_inode->get(index); + } + + Result read(u8*, usize, usize) const override + { + return err(EISDIR); + } + + Result write(const u8*, usize, usize) override + { + return err(EISDIR); + } + + Result 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 chmod(mode_t mode) override + { + return m_mount_root_inode->chmod(mode); + } + + Result 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 source() const + { + return m_source; + } + + Result remove_entry(const char* name) override + { + return m_mount_root_inode->remove_entry(name); + } + + Result> create_file(const char* name) override + { + return m_mount_root_inode->create_file(name); + } + + Result> create_subdirectory(const char* name) override + { + return m_mount_root_inode->create_subdirectory(name); + } + + Result add_entry(SharedPtr inode, const char* name) override + { + return m_mount_root_inode->add_entry(inode, name); + } + + Result replace_entry(SharedPtr inode, const char* name) override + { + return m_mount_root_inode->replace_entry(inode, name); + } + + virtual ~MountInode() = default; + + private: + SharedPtr m_source; + SharedPtr m_mountee; + SharedPtr m_mount_root_inode; + + MountInode() = default; +}; + +extern LinkedList g_mounts; diff --git a/kernel/src/fs/VFS.cpp b/kernel/src/fs/VFS.cpp index 3d55a1eb..599eb681 100644 --- a/kernel/src/fs/VFS.cpp +++ b/kernel/src/fs/VFS.cpp @@ -1,5 +1,6 @@ #include "fs/VFS.h" #include "Log.h" +#include "fs/Mount.h" #include "thread/Thread.h" #include #include @@ -97,6 +98,8 @@ namespace VFS bool can_execute(SharedPtr 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, 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, 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 mount_root(SharedPtr fs) + { + root_fs = fs; + + return {}; + } + + Result mount(const char* path, SharedPtr fs, Credentials auth, + SharedPtr 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 {}; + } } diff --git a/kernel/src/fs/VFS.h b/kernel/src/fs/VFS.h index 2e881d99..a44d3096 100644 --- a/kernel/src/fs/VFS.h +++ b/kernel/src/fs/VFS.h @@ -46,6 +46,8 @@ namespace VFS virtual Result remove_entry(const char* name) = 0; + virtual Result replace_entry(SharedPtr 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 replace_entry(SharedPtr, const char*) override + { + return err(ENOTDIR); + } + Result remove_entry(const char*) override { return err(ENOTDIR); @@ -169,6 +181,11 @@ namespace VFS return err(ENOTDIR); } + Result replace_entry(SharedPtr, const char*) override + { + return err(ENOTDIR); + } + Result remove_entry(const char*) override { return err(ENOTDIR); @@ -198,10 +215,28 @@ namespace VFS virtual Result> create_device_inode(u32 major, u32 minor) = 0; - virtual ~FileSystem() = default; - }; + virtual Result set_mount_dir(SharedPtr parent) = 0; - extern SharedPtr 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> resolve_path(const char* path, Credentials auth, SharedPtr working_directory = {}); @@ -221,4 +256,8 @@ namespace VFS bool is_setgid(SharedPtr inode); Inode& root_inode(); + + Result mount_root(SharedPtr fs); + Result mount(const char* path, SharedPtr fs, Credentials auth, + SharedPtr working_directory = {}); } diff --git a/kernel/src/fs/devices/ConsoleDevice.cpp b/kernel/src/fs/devices/ConsoleDevice.cpp index 594261c2..11677ede 100644 --- a/kernel/src/fs/devices/ConsoleDevice.cpp +++ b/kernel/src/fs/devices/ConsoleDevice.cpp @@ -18,7 +18,7 @@ static bool g_echo { true }; Result ConsoleDevice::create() { auto device = (SharedPtr)TRY(make_shared()); - return DeviceRegistry::register_special_device(DeviceRegistry::Console, 0, device); + return DeviceRegistry::register_special_device(DeviceRegistry::Console, 0, device, "console"); } Result ConsoleDevice::read(u8* buf, usize, usize length) const diff --git a/kernel/src/fs/devices/DeviceRegistry.cpp b/kernel/src/fs/devices/DeviceRegistry.cpp index b9d56179..9f8689be 100644 --- a/kernel/src/fs/devices/DeviceRegistry.cpp +++ b/kernel/src/fs/devices/DeviceRegistry.cpp @@ -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 struct DeviceDescriptor @@ -11,10 +14,14 @@ struct DeviceDescriptor SharedPtr device; u32 major; u32 minor; + const char* name; + mode_t mode; }; Vector g_available_devices = {}; +SharedPtr g_device_fs; + namespace DeviceRegistry { Result> fetch_special_device(u32 major, u32 minor) @@ -27,20 +34,39 @@ namespace DeviceRegistry return err(ENODEV); } - Result register_special_device(u32 major, u32 minor, SharedPtr device) + Result register_special_device(u32 major, u32 minor, SharedPtr 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 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 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(); diff --git a/kernel/src/fs/devices/DeviceRegistry.h b/kernel/src/fs/devices/DeviceRegistry.h index 330c22ae..a68c4039 100644 --- a/kernel/src/fs/devices/DeviceRegistry.h +++ b/kernel/src/fs/devices/DeviceRegistry.h @@ -2,6 +2,7 @@ #include "fs/devices/Device.h" #include +#include namespace DeviceRegistry { @@ -15,7 +16,8 @@ namespace DeviceRegistry Result> fetch_special_device(u32 major, u32 minor); - Result register_special_device(u32 major, u32 minor, SharedPtr device); + Result register_special_device(u32 major, u32 minor, SharedPtr device, const char* name, + mode_t mode = 0666); Result init(); } diff --git a/kernel/src/fs/devices/FramebufferDevice.cpp b/kernel/src/fs/devices/FramebufferDevice.cpp index 5a060cbe..9711c175 100644 --- a/kernel/src/fs/devices/FramebufferDevice.cpp +++ b/kernel/src/fs/devices/FramebufferDevice.cpp @@ -6,7 +6,7 @@ Result FramebufferDevice::create() { auto device = (SharedPtr)TRY(make_shared()); - return DeviceRegistry::register_special_device(DeviceRegistry::Framebuffer, 0, device); + return DeviceRegistry::register_special_device(DeviceRegistry::Framebuffer, 0, device, "fb0", 0600); } Result FramebufferDevice::read(u8*, usize, usize) const diff --git a/kernel/src/fs/devices/NullDevice.cpp b/kernel/src/fs/devices/NullDevice.cpp index 0fd8efee..ef8ef167 100644 --- a/kernel/src/fs/devices/NullDevice.cpp +++ b/kernel/src/fs/devices/NullDevice.cpp @@ -3,5 +3,5 @@ Result NullDevice::create() { auto device = (SharedPtr)TRY(make_shared()); - return DeviceRegistry::register_special_device(DeviceRegistry::Memory, 0, device); + return DeviceRegistry::register_special_device(DeviceRegistry::Memory, 0, device, "null"); } diff --git a/kernel/src/fs/devices/ZeroDevice.cpp b/kernel/src/fs/devices/ZeroDevice.cpp index 57403234..ac75b252 100644 --- a/kernel/src/fs/devices/ZeroDevice.cpp +++ b/kernel/src/fs/devices/ZeroDevice.cpp @@ -3,5 +3,5 @@ Result ZeroDevice::create() { auto device = (SharedPtr)TRY(make_shared()); - return DeviceRegistry::register_special_device(DeviceRegistry::Memory, 1, device); + return DeviceRegistry::register_special_device(DeviceRegistry::Memory, 1, device, "zero"); } diff --git a/kernel/src/fs/tmpfs/FileSystem.cpp b/kernel/src/fs/tmpfs/FileSystem.cpp index a73077e8..1024876d 100644 --- a/kernel/src/fs/tmpfs/FileSystem.cpp +++ b/kernel/src/fs/tmpfs/FileSystem.cpp @@ -1,4 +1,5 @@ #include "fs/tmpfs/FileSystem.h" +#include "fs/Mount.h" #include "fs/devices/DeviceRegistry.h" #include #include @@ -49,6 +50,11 @@ namespace TmpFS return (SharedPtr)inode; } + Result FileSystem::set_mount_dir(SharedPtr parent) + { + return m_root_inode->replace_entry(parent, ".."); + } + void FileSystem::set_root(SharedPtr root) { m_root_inode = root; @@ -64,6 +70,20 @@ namespace TmpFS return err(ENOENT); } + Result DirInode::replace_entry(SharedPtr inode, const char* name) + { + for (auto& entry : m_entries) + { + if (!strcmp(name, entry.name.chars())) + { + entry.inode = inode; + return {}; + } + } + + return err(ENOENT); + } + Option 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); }); diff --git a/kernel/src/fs/tmpfs/FileSystem.h b/kernel/src/fs/tmpfs/FileSystem.h index 0cf0808b..8691552c 100644 --- a/kernel/src/fs/tmpfs/FileSystem.h +++ b/kernel/src/fs/tmpfs/FileSystem.h @@ -21,6 +21,8 @@ namespace TmpFS Result> create_dir_inode(SharedPtr parent) override; Result> create_device_inode(u32 major, u32 minor) override; + Result set_mount_dir(SharedPtr parent) override; + static Result> create(); virtual ~FileSystem() = default; @@ -341,6 +343,7 @@ namespace TmpFS Result> create_subdirectory(const char* name) override; Result add_entry(SharedPtr inode, const char* name); + Result replace_entry(SharedPtr inode, const char* name); virtual ~DirInode() = default; diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index b58bac14..31dc8137 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -42,9 +42,10 @@ Result 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")); diff --git a/tools/install.sh b/tools/install.sh index c7d9cc34..ae081bb5 100755 --- a/tools/install.sh +++ b/tools/install.sh @@ -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