#include #include #include #include #include #include #include #include #include #include #include #include #include namespace { constinit auto static active_vfs = std::optional{}; } // namespace namespace kernel::filesystem { 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 { // Mount rootfs at / as the temporary base auto root_fs = kstd::make_shared(); root_fs->mount(nullptr); auto root_fs_root_dentry = kstd::make_shared(nullptr, root_fs->root_inode(), "/"); m_mount_table.add_mount(kstd::make_shared(nullptr, root_fs_root_dentry, root_fs, "/", nullptr)); // Mount devfs at /dev in rootfs (temporary, will be shadowed) auto device_fs = kstd::make_shared(); device_fs->mount(nullptr); auto dev_mount_point_dentry = resolve_path("/dev"); if (!dev_mount_point_dentry) { kapi::system::panic("[FILESYSTEM] failed to resolve /dev for initial devfs mount."); } do_mount_internal("/dev", dev_mount_point_dentry, device_fs); // Mount boot filesystem at / (will shadow rootfs) if (auto boot_device_dentry = resolve_path("/dev/ram0")) { if (auto boot_root_fs = kernel::filesystem::filesystem::probe_and_mount(boot_device_dentry->get_inode())) { do_mount_internal("/", root_fs_root_dentry, boot_root_fs); // Resolve / to get the boot root dentry if (auto boot_root_dentry = resolve_path("/")) { auto dev_dentry = kstd::make_shared(boot_root_dentry, device_fs->root_inode(), "dev"); boot_root_dentry->add_child(dev_dentry); do_mount_internal("/dev", dev_dentry, device_fs); } } } } 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 { return resolve_path(path); } auto vfs::do_mount(std::string_view source, std::string_view target) -> operation_result { // TODO BA-FS26 better path validation if (target.empty() || target.front() != '/' || (target.size() > 1 && target.back() == '/')) { return operation_result::invalid_path; } // TODO BA-FS26 check if target is directory? if (auto mount_point_dentry = resolve_path(target)) { if (auto source_dentry = resolve_path(source)) { if (auto fs = kernel::filesystem::filesystem::probe_and_mount(source_dentry->get_inode())) { do_mount_internal(target, mount_point_dentry, fs); return operation_result::success; } return operation_result::invalid_filesystem; } return operation_result::non_existent_path; } return operation_result::mount_point_not_found; } auto vfs::unmount(std::string_view path) -> operation_result { // TODO BA-FS26 better path validation if (path.empty() || path.front() != '/' || (path.size() > 1 && path.back() == '/')) { return operation_result::invalid_path; } auto remove_result = m_mount_table.remove_mount(path); if (remove_result == mount_table::operation_result::removed) { return operation_result::success; } if (remove_result == mount_table::operation_result::has_child_mounts) { return operation_result::unmount_failed; } return operation_result::mount_point_not_found; } auto vfs::do_mount_internal(std::string_view path, kstd::shared_ptr const & mount_point_dentry, kstd::shared_ptr const & fs) -> void { auto parent_mount = m_mount_table.find_longest_prefix_mount(path); auto new_fs_root = kstd::make_shared(mount_point_dentry->get_parent(), fs->root_inode(), mount_point_dentry->get_name()); auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, path, parent_mount); m_mount_table.add_mount(new_mount); } auto vfs::resolve_path(std::string_view path) -> kstd::shared_ptr { // TODO BA-FS26 better path validation // TODO BA-FS26 implement a path parser (maybe in libs?) and use it here and in do_mount if (path.empty() || path.front() != '/') return nullptr; auto current_mount = m_mount_table.find_longest_prefix_mount("/"); if (!current_mount) { kapi::system::panic("[FILESYSTEM] no root mount found."); } auto current_dentry = current_mount->root_dentry(); std::string_view remaining = path.substr(current_mount->get_mount_path().size()); auto path_parts = std::views::split(remaining, '/') | std::views::filter([](auto const & part) { return !part.empty(); }); for (auto it = path_parts.begin(); it != path_parts.end(); ++it) { std::string_view part_view{*it}; if (part_view == ".") { continue; } if (part_view == "..") { auto parent_dentry = current_dentry->get_parent(); if (current_dentry == current_mount->root_dentry()) { // change the mount point if (auto parent_mount = current_mount->get_parent_mount()) { current_mount = parent_mount; current_dentry = current_dentry->get_parent(); if (!parent_dentry) { parent_dentry = current_mount->root_dentry(); } } } if (!parent_dentry) { return nullptr; } current_dentry = parent_dentry; continue; } auto next_dentry = current_dentry->find_child(part_view); if (!next_dentry) { auto current_fs = current_mount->get_filesystem(); auto found_inode = current_fs->lookup(current_dentry->get_inode(), part_view); if (!found_inode) { return nullptr; } next_dentry = kstd::make_shared(current_dentry, found_inode, part_view); current_dentry->add_child(next_dentry); } else if (next_dentry->has_flag(dentry::dentry_flags::mounted)) { // TODO BA-FS26 really do it like this? or just call "get" without longes_prefix stuff current_mount = m_mount_table.find_longest_prefix_mount(next_dentry->get_full_path().view()); if (!current_mount) { kapi::system::panic("[FILESYSTEM] mount for dentry with mounted flag not found."); } next_dentry = current_mount->root_dentry(); } if (next_dentry->get_inode()->is_symbolic_link()) { // TODO BA-FS26 this for loop is ugly --> fix kstd::string symlink_path = "halo"; if (symlink_path.size() > 0 && symlink_path.front() == '/') { for (auto it2 = std::next(it); it2 != path_parts.end(); ++it2) { symlink_path += "/"; symlink_path.append(std::string_view{*it2}); } return resolve_path(symlink_path.view()); } else { kstd::string combined; for (auto it3 = path_parts.begin(); it3 != it; ++it3) { combined += "/"; combined.append(std::string_view{*it3}); } combined += "/"; combined += symlink_path; for (auto it4 = std::next(it); it4 != path_parts.end(); ++it4) { combined += "/"; combined.append(std::string_view{*it4}); } return resolve_path(combined.view()); } } current_dentry = next_dentry; } return current_dentry; } } // namespace kernel::filesystem namespace kernel::tests::filesystem::vfs { auto deinit() -> void { active_vfs.reset(); } } // namespace kernel::tests::filesystem::vfs