#include "kernel/filesystem/vfs.hpp" #include "kapi/system.hpp" #include "kernel/devices/device.hpp" #include "kernel/devices/storage/storage_management.hpp" #include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/device_inode.hpp" #include "kernel/filesystem/ext2/ext2_filesystem.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/mount.hpp" #include "kernel/filesystem/open_file_description.hpp" #include #include #include #include #include namespace filesystem { namespace { constinit auto static active_vfs = std::optional{}; } // namespace auto vfs::init() -> void { if (active_vfs) { kapi::system::panic("[FILESYSTEM] vfs has already been initialized."); } active_vfs.emplace(vfs{}); active_vfs->init_internal(); } auto vfs::init_internal() -> void { auto storage_mgmt = devices::storage::storage_management::get(); // TODO BA-FS26 fix mounting boot_device if (auto boot_device = storage_mgmt.determine_boot_device()) { m_root_fs = kstd::make_shared(); m_root_fs->mount(boot_device); m_root_dentry = kstd::make_shared(nullptr, m_root_fs->root_inode()); // if (do_mount("/", m_root_fs) != 0) // { // kapi::system::panic("[FILESYSTEM] Failed to mount root filesystem."); // } // TODO BA-FS26 use do_mount when tempdevfs is implemented -> just call /dev/ with all devices in devtempfs std::ranges::for_each(storage_mgmt.all_controllers(), [&](auto controller) { std::ranges::for_each(controller->all_devices(), [&](auto device) { make_device_node(device); }); }); } else { // TODO BA-FS26 ?? what when no boot_device == no modules loaded?? } } auto vfs::get() -> vfs & { if (!active_vfs) { kapi::system::panic("[FILESYSTEM] vfs has not been initialized."); } return *active_vfs; } auto vfs::open(std::string_view path) -> kstd::shared_ptr { if (auto dentry = resolve_path(path)) { return kstd::make_shared(dentry->get_inode()); } return nullptr; } auto vfs::do_mount(std::string_view path, kstd::shared_ptr const & new_filesystem) -> int { if (!new_filesystem) { return -1; // TODO BA-FS26 panic or errorcode? } if (path.empty() || path.front() != '/') { return -1; // TODO BA-FS26 panic or errorcode? } // TODO BA-FS26 better path validation if ((path.size() > 1 && path.back() == '/')) { return -1; // TODO BA-FS26 panic or errorcode? } auto mount_dentry = resolve_path(path); if (!mount_dentry) { return -1; // mount point path doesn't exist } // TODO BA-FS26 check if mount point is already mounted and handle it (unmount old fs, fail, etc.) mount_dentry->set_flag(dentry::dentry_flags::dcache_mounted); m_mount_table.add_mount(kstd::make_shared(mount_dentry, new_filesystem)); new_filesystem->set_root_dentry( kstd::make_shared(mount_dentry->get_parent(), new_filesystem->root_inode())); return 0; } auto vfs::make_device_node(kstd::shared_ptr const & device) -> void { if (!device) { kapi::system::panic("[FILESYSTEM] make_device_node called with null device."); } m_device_nodes.push_back(device_node_entry{device->name().view(), kstd::make_shared(device)}); } auto vfs::resolve_path(std::string_view path) -> kstd::shared_ptr { // TODO BA-FS26 implement real path resolution with mounts and directories etc. // For now, just support device nodes at /dev/. // TODO BA-FS26 better path validation // TODO BA-FS26 implement a path parser (maybe in libs?) and use it here and in do_mount constexpr auto device_prefix = std::string_view{"/dev/"}; if (path.starts_with(device_prefix)) { auto const device_name = path.substr(device_prefix.size()); auto entry = std::ranges::find_if(m_device_nodes, [&](auto const & device_entry) { return device_entry.has_value() && device_entry->name == device_name; }); if (entry != m_device_nodes.end()) { return kstd::make_shared(nullptr, entry->value().node); } return nullptr; } else if (!path.empty() && path.front() == '/') { auto path_parts = std::views::split(path, '/') | std::views::filter([](auto const & part) { return !part.empty(); }); auto current_parent = m_root_dentry; auto current_fs = m_root_fs; for (auto const & part : path_parts) { if (auto child = current_parent->find_child(std::string_view{part})) { if (child->has_flag(dentry::dentry_flags::dcache_mounted)) // found dentry is a mounted fs { auto found_mount = m_mount_table.find_mount_by_dentry(child); current_fs = found_mount->get_filesystem(); current_parent = current_fs->root_dentry(); } else { // found dentry is NOT a mounted fs -> continue path resolution in current fs current_parent = child; } } else if (auto found_inode = m_root_fs->lookup(current_parent->get_inode(), std::string_view{part})) { auto next_dentry = kstd::make_shared(current_parent, found_inode, std::string_view{part}); current_parent->add_child(next_dentry); current_parent = next_dentry; } else { return nullptr; } } // | std::views::transform([this](auto const & part) { // // TODO BA-FS26 implement real path resolution with mounts and directories etc. // // For now, just return null for any non-device-node path. // return std::optional>{}; // }) // | std::views::filter([](auto const & opt) { return opt.has_value(); }) // | std::views::transform([](auto const & opt) { return opt.value(); }) // | std::ranges::find_if(m_mount_table, [&](auto const & mount) { // // TODO BA-FS26 implement real path resolution with mounts and directories etc. // // For now, just check if the first path component matches a mount point. // return part == mount.path(); // }); } return nullptr; } } // namespace filesystem