From f1089e2b8a6b286732c577c347d20bd3694ec4ed Mon Sep 17 00:00:00 2001
From: Bananymous <bananymousosq@gmail.com>
Date: Mon, 10 Jul 2023 13:26:14 +0300
Subject: [PATCH] Kernel: start work on ram file system

---
 kernel/CMakeLists.txt                       |   2 +
 kernel/include/kernel/FS/RamFS/FileSystem.h |  40 +++++
 kernel/include/kernel/FS/RamFS/Inode.h      |  95 ++++++++++++
 kernel/kernel/FS/RamFS/FileSystem.cpp       |  45 ++++++
 kernel/kernel/FS/RamFS/Inode.cpp            | 161 ++++++++++++++++++++
 kernel/kernel/FS/VirtualFileSystem.cpp      |  13 +-
 6 files changed, 355 insertions(+), 1 deletion(-)
 create mode 100644 kernel/include/kernel/FS/RamFS/FileSystem.h
 create mode 100644 kernel/include/kernel/FS/RamFS/Inode.h
 create mode 100644 kernel/kernel/FS/RamFS/FileSystem.cpp
 create mode 100644 kernel/kernel/FS/RamFS/Inode.cpp

diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt
index 475d3f41..386fce49 100644
--- a/kernel/CMakeLists.txt
+++ b/kernel/CMakeLists.txt
@@ -21,6 +21,8 @@ set(KERNEL_SOURCES
 	kernel/FS/Ext2.cpp
 	kernel/FS/Inode.cpp
 	kernel/FS/Pipe.cpp
+	kernel/FS/RamFS/FileSystem.cpp
+	kernel/FS/RamFS/Inode.cpp
 	kernel/FS/VirtualFileSystem.cpp
 	kernel/Input/PS2Controller.cpp
 	kernel/Input/PS2Keyboard.cpp
diff --git a/kernel/include/kernel/FS/RamFS/FileSystem.h b/kernel/include/kernel/FS/RamFS/FileSystem.h
new file mode 100644
index 00000000..eb40c7cc
--- /dev/null
+++ b/kernel/include/kernel/FS/RamFS/FileSystem.h
@@ -0,0 +1,40 @@
+#pragma once
+
+#include <BAN/HashMap.h>
+#include <kernel/FS/FileSystem.h>
+#include <kernel/SpinLock.h>
+
+namespace Kernel
+{
+	
+	class RamInode;
+
+	class RamFileSystem final : public FileSystem
+	{
+	public:
+		static BAN::ErrorOr<RamFileSystem*> create(size_t size, mode_t, uid_t, gid_t);
+		~RamFileSystem() = default;
+
+		virtual BAN::RefPtr<Inode> root_inode() override { return m_inodes[m_root_inode]; }
+
+		BAN::ErrorOr<void> add_inode(BAN::RefPtr<RamInode>);
+		BAN::ErrorOr<BAN::RefPtr<RamInode>> get_inode(ino_t);
+
+		blksize_t blksize() const { return m_blksize; }
+		ino_t next_ino() { return m_next_ino++; }
+
+	private:
+		RamFileSystem() = default;
+
+	private:
+		SpinLock m_lock;
+		size_t m_size { 0 };
+
+		BAN::HashMap<ino_t, BAN::RefPtr<RamInode>> m_inodes;
+		ino_t m_root_inode;
+
+		const blksize_t m_blksize = PAGE_SIZE;
+		ino_t m_next_ino { 1 };
+	};
+
+}
\ No newline at end of file
diff --git a/kernel/include/kernel/FS/RamFS/Inode.h b/kernel/include/kernel/FS/RamFS/Inode.h
new file mode 100644
index 00000000..9ec183f7
--- /dev/null
+++ b/kernel/include/kernel/FS/RamFS/Inode.h
@@ -0,0 +1,95 @@
+#pragma once
+
+#include <kernel/FS/Inode.h>
+
+#include <limits.h>
+
+namespace Kernel
+{
+
+	class RamFileSystem;
+
+	class RamInode : public Inode
+	{
+	public:
+		virtual ~RamInode() = default;
+	
+		virtual ino_t		ino()		const override { return m_inode_info.ino; }
+		virtual Mode		mode()		const override { return { m_inode_info.mode }; }
+		virtual nlink_t		nlink()		const override { return m_inode_info.nlink; }
+		virtual uid_t		uid()		const override { return m_inode_info.uid; }
+		virtual gid_t		gid()		const override { return m_inode_info.gid; }
+		virtual off_t		size()		const override { return m_inode_info.size; }
+		virtual timespec	atime()		const override { return m_inode_info.atime; }
+		virtual timespec	mtime()		const override { return m_inode_info.mtime; }
+		virtual timespec	ctime()		const override { return m_inode_info.ctime; }
+		virtual blksize_t	blksize()	const override { return m_inode_info.blksize; }
+		virtual blkcnt_t	blocks()	const override { return m_inode_info.blocks; }
+		virtual dev_t		dev()		const override { return m_inode_info.dev; }
+		virtual dev_t		rdev()		const override { return m_inode_info.rdev; }
+
+		virtual BAN::StringView name() const override { ASSERT_NOT_REACHED(); }
+
+		void add_link() { m_inode_info.nlink++; }
+
+	protected:
+		RamInode(RamFileSystem& fs, mode_t, uid_t, gid_t);
+		static BAN::ErrorOr<BAN::RefPtr<RamInode>> create(RamFileSystem&, mode_t, uid_t, gid_t);
+
+	protected:
+		struct FullInodeInfo
+		{
+			ino_t		ino;
+			mode_t		mode;
+			nlink_t		nlink;
+			uid_t		uid;
+			gid_t		gid;
+			off_t		size;
+			timespec	atime;
+			timespec	mtime;
+			timespec	ctime;
+			blksize_t	blksize;
+			blkcnt_t	blocks;
+			dev_t		dev;
+			dev_t		rdev;
+		};
+
+	protected:
+		RamFileSystem& m_fs;
+		FullInodeInfo m_inode_info;
+
+		BAN::Vector<uint8_t> m_data;
+
+		friend class RamFileSystem;
+	};
+
+	class RamDirectoryInode final : public RamInode
+	{
+	public:
+		~RamDirectoryInode() = default;
+
+		virtual BAN::ErrorOr<BAN::RefPtr<Inode>> directory_find_inode(BAN::StringView) override;
+		virtual BAN::ErrorOr<void> directory_read_next_entries(off_t, DirectoryEntryList*, size_t) override;
+		virtual BAN::ErrorOr<void> create_file(BAN::StringView, mode_t)	override;
+
+	private:
+		RamDirectoryInode(RamFileSystem&, ino_t parent, mode_t, uid_t, gid_t);
+		static BAN::ErrorOr<BAN::RefPtr<RamDirectoryInode>> create(RamFileSystem&, ino_t parent, mode_t, uid_t, gid_t);
+	
+	private:
+		static constexpr size_t m_name_max = NAME_MAX;
+		struct Entry
+		{
+			char name[m_name_max + 1];
+			size_t name_len = 0;
+			ino_t ino;
+		};
+
+	private:
+		BAN::Vector<Entry> m_entries;
+		ino_t m_parent;
+
+		friend class RamFileSystem;
+	};
+
+}
\ No newline at end of file
diff --git a/kernel/kernel/FS/RamFS/FileSystem.cpp b/kernel/kernel/FS/RamFS/FileSystem.cpp
new file mode 100644
index 00000000..010ea255
--- /dev/null
+++ b/kernel/kernel/FS/RamFS/FileSystem.cpp
@@ -0,0 +1,45 @@
+#include <BAN/ScopeGuard.h>
+#include <kernel/FS/RamFS/FileSystem.h>
+#include <kernel/FS/RamFS/Inode.h>
+#include <kernel/LockGuard.h>
+#include <kernel/RTC.h>
+
+namespace Kernel
+{
+
+	BAN::ErrorOr<RamFileSystem*> RamFileSystem::create(size_t size, mode_t mode, uid_t uid, gid_t gid)
+	{
+		auto* ramfs = new RamFileSystem;
+		if (ramfs == nullptr)
+			return BAN::Error::from_errno(ENOMEM);
+		ramfs->m_size = size;
+
+		BAN::ScopeGuard deleter([ramfs] { delete ramfs; });
+
+		auto root_inode = TRY(RamDirectoryInode::create(*ramfs, 0, mode, uid, gid));
+		TRY(ramfs->add_inode(root_inode));
+		ramfs->m_root_inode = root_inode->ino();
+
+		deleter.disable();
+
+		return ramfs;
+	}
+
+	BAN::ErrorOr<void> RamFileSystem::add_inode(BAN::RefPtr<RamInode> inode)
+	{
+		LockGuard _(m_lock);
+		if (m_inodes.contains(inode->ino()))
+			return BAN::Error::from_errno(EEXIST);
+		TRY(m_inodes.insert(inode->ino(), inode));
+		return {};
+	}
+
+	BAN::ErrorOr<BAN::RefPtr<RamInode>> RamFileSystem::get_inode(ino_t ino)
+	{
+		LockGuard _(m_lock);
+		if (!m_inodes.contains(ino))
+			return BAN::Error::from_errno(ENOENT);
+		return m_inodes[ino];
+	}
+
+}
\ No newline at end of file
diff --git a/kernel/kernel/FS/RamFS/Inode.cpp b/kernel/kernel/FS/RamFS/Inode.cpp
new file mode 100644
index 00000000..843766f2
--- /dev/null
+++ b/kernel/kernel/FS/RamFS/Inode.cpp
@@ -0,0 +1,161 @@
+#include <kernel/FS/RamFS/FileSystem.h>
+#include <kernel/FS/RamFS/Inode.h>
+#include <kernel/RTC.h>
+
+namespace Kernel
+{
+
+	/*
+	
+		RAM INODE
+
+	*/
+
+	BAN::ErrorOr<BAN::RefPtr<RamInode>> RamInode::create(RamFileSystem& fs, mode_t mode, uid_t uid, gid_t gid)
+	{
+		ASSERT(Mode{ mode }.ifreg());
+		auto* ram_inode = new RamInode(fs, mode, uid, gid);
+		if (ram_inode == nullptr)
+			return BAN::Error::from_errno(ENOMEM);
+		return BAN::RefPtr<RamInode>::adopt(ram_inode);
+	}
+
+	RamInode::RamInode(RamFileSystem& fs, mode_t mode, uid_t uid, gid_t gid)
+			: m_fs(fs)
+	{
+		uint64_t current_unix_time = BAN::to_unix_time(RTC::get_current_time());
+		timespec current_timespec;
+		current_timespec.tv_sec = current_unix_time;
+		current_timespec.tv_nsec = 0;
+
+		m_inode_info.ino = fs.next_ino();
+		m_inode_info.mode = mode;
+		m_inode_info.nlink = 1;
+		m_inode_info.uid = uid;
+		m_inode_info.gid = gid;
+		m_inode_info.size = 0;
+		m_inode_info.atime = current_timespec;
+		m_inode_info.mtime = current_timespec;
+		m_inode_info.ctime = current_timespec;
+		m_inode_info.blksize = fs.blksize();
+		m_inode_info.blocks = 0;
+		m_inode_info.dev = 0;
+		m_inode_info.rdev = 0;
+	}
+
+	/*
+	
+		RAM DIRECTORY INODE
+
+	*/
+
+	BAN::ErrorOr<BAN::RefPtr<RamDirectoryInode>> RamDirectoryInode::create(RamFileSystem& fs, ino_t parent, mode_t mode, uid_t uid, gid_t gid)
+	{
+		ASSERT(Mode{ mode }.ifdir());
+		auto* ram_inode = new RamDirectoryInode(fs, parent, mode, uid, gid);
+		if (ram_inode == nullptr)
+			return BAN::Error::from_errno(ENOMEM);
+		return BAN::RefPtr<RamDirectoryInode>::adopt(ram_inode);
+	}
+
+	RamDirectoryInode::RamDirectoryInode(RamFileSystem& fs, ino_t parent, mode_t mode, uid_t uid, gid_t gid)
+		: RamInode(fs, mode, uid, gid)
+	{
+		// "." links to this
+		m_inode_info.nlink++;
+
+		// ".." links to this, if there is no parent
+		if (parent == 0)
+		{
+			m_inode_info.nlink++;
+			m_parent = ino();
+		}
+		else
+		{
+			MUST(fs.get_inode(parent))->add_link();
+			m_parent = parent;
+		}
+	}
+
+	BAN::ErrorOr<BAN::RefPtr<Inode>> RamDirectoryInode::directory_find_inode(BAN::StringView name)
+	{
+		if (name == "."sv)
+		{
+			BAN::RefPtr<Inode> inode = TRY(m_fs.get_inode(ino()));
+			return inode;
+		}
+
+		if (name == ".."sv)
+		{
+			BAN::RefPtr<Inode> inode = TRY(m_fs.get_inode(m_parent));
+			return inode;
+		}
+
+		for (const auto& entry : m_entries)
+		{
+			if (name == entry.name)
+			{
+				BAN::RefPtr<Inode> inode = TRY(m_fs.get_inode(entry.ino));
+				return inode;
+			}
+		}
+
+		sizeof(Entry);
+
+		return BAN::Error::from_errno(ENOENT);
+	}
+
+	BAN::ErrorOr<void> RamDirectoryInode::directory_read_next_entries(off_t offset, DirectoryEntryList* list, size_t list_size)
+	{
+		// TODO: don't require memory for all entries on single call
+		if (offset != 0)
+		{
+			list->entry_count = 0;
+			return {};
+		}
+
+		size_t needed_size = sizeof(DirectoryEntryList);
+		needed_size += sizeof(DirectoryEntry) + 2; // "."
+		needed_size += sizeof(DirectoryEntry) + 3; // ".."
+		for (auto& entry : m_entries)
+			needed_size += sizeof(DirectoryEntry) + entry.name_len + 1;
+		if (needed_size > list_size)
+			return BAN::Error::from_errno(EINVAL);
+
+		DirectoryEntry* ptr = list->array;
+
+		// "."
+		{
+			ptr->dirent.d_ino = ino();
+			ptr->rec_len = sizeof(DirectoryEntry) + 2;
+			strcpy(ptr->dirent.d_name, ".");
+			ptr = ptr->next();
+		}
+
+		// ".."
+		{
+			ptr->dirent.d_ino = m_parent;
+			ptr->rec_len = sizeof(DirectoryEntry) + 3;
+			strcpy(ptr->dirent.d_name, "..");
+			ptr = ptr->next();
+		}
+
+		for (auto& entry : m_entries)
+		{
+			ptr->dirent.d_ino = entry.ino;
+			ptr->rec_len = sizeof(DirectoryEntry) + entry.name_len + 1;
+			strcpy(ptr->dirent.d_name, entry.name);
+			ptr = ptr->next();
+		}
+
+		list->entry_count = m_entries.size() + 2;
+
+		return {};
+	}
+
+	BAN::ErrorOr<void> RamDirectoryInode::create_file(BAN::StringView, mode_t)
+	{
+		ASSERT_NOT_REACHED();
+	}
+
+}
\ No newline at end of file
diff --git a/kernel/kernel/FS/VirtualFileSystem.cpp b/kernel/kernel/FS/VirtualFileSystem.cpp
index 210a01c5..35e5a2f9 100644
--- a/kernel/kernel/FS/VirtualFileSystem.cpp
+++ b/kernel/kernel/FS/VirtualFileSystem.cpp
@@ -2,6 +2,8 @@
 #include <BAN/StringView.h>
 #include <kernel/DeviceManager.h>
 #include <kernel/FS/Ext2.h>
+#include <kernel/FS/RamFS/FileSystem.h>
+#include <kernel/FS/RamFS/Inode.h>
 #include <kernel/FS/VirtualFileSystem.h>
 #include <kernel/LockGuard.h>
 #include <fcntl.h>
@@ -23,8 +25,17 @@ namespace Kernel
 		auto partition_inode = MUST(DeviceManager::get().directory_find_inode(root));
 		s_instance->m_root_fs = MUST(Ext2FS::create(*(Partition*)partition_inode.ptr()));
 
+		Credentials root_creds { 0, 0, 0, 0 };
+
 		DeviceManager::get().set_blksize(s_instance->m_root_fs->root_inode()->blksize());
-		MUST(s_instance->mount({ 0, 0, 0, 0 }, &DeviceManager::get(), "/dev"));
+		MUST(s_instance->mount(root_creds, &DeviceManager::get(), "/dev"));
+
+		mode_t tmpfs_mode = Inode::Mode::IFDIR
+			 | Inode::Mode::IRUSR | Inode::Mode::IWUSR | Inode::Mode::IXUSR
+			 | Inode::Mode::IRGRP | Inode::Mode::IWGRP | Inode::Mode::IXGRP
+			 | Inode::Mode::IROTH | Inode::Mode::IWOTH | Inode::Mode::IXOTH;
+		auto* tmpfs = MUST(RamFileSystem::create(1024 * 1024, tmpfs_mode, 0, 0));
+		MUST(s_instance->mount(root_creds, tmpfs, "/tmp"));
 	}
 	
 	VirtualFileSystem& VirtualFileSystem::get()