aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml12
-rw-r--r--kernel/CMakeLists.txt12
-rw-r--r--kernel/include/kernel/devices/block_device.hpp5
-rw-r--r--kernel/include/kernel/devices/device.hpp9
-rw-r--r--kernel/include/kernel/filesystem/custody.hpp23
-rw-r--r--kernel/include/kernel/filesystem/dentry.hpp43
-rw-r--r--kernel/include/kernel/filesystem/devfs/devfs_filesystem.hpp25
-rw-r--r--kernel/include/kernel/filesystem/devfs/devfs_root_inode.hpp19
-rw-r--r--kernel/include/kernel/filesystem/device_inode.hpp (renamed from kernel/include/kernel/filesystem/device_file.hpp)16
-rw-r--r--kernel/include/kernel/filesystem/ext2/ext2_file.hpp15
-rw-r--r--kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp3
-rw-r--r--kernel/include/kernel/filesystem/ext2/ext2_inode.hpp25
-rw-r--r--kernel/include/kernel/filesystem/file.hpp19
-rw-r--r--kernel/include/kernel/filesystem/file_descriptor_table.hpp10
-rw-r--r--kernel/include/kernel/filesystem/filesystem.hpp11
-rw-r--r--kernel/include/kernel/filesystem/inode.hpp27
-rw-r--r--kernel/include/kernel/filesystem/inode_file.hpp27
-rw-r--r--kernel/include/kernel/filesystem/inode_metadata.hpp24
-rw-r--r--kernel/include/kernel/filesystem/mount.hpp14
-rw-r--r--kernel/include/kernel/filesystem/mount_table.hpp25
-rw-r--r--kernel/include/kernel/filesystem/open_file_description.hpp6
-rw-r--r--kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp23
-rw-r--r--kernel/include/kernel/filesystem/rootfs/rootfs_inode.hpp31
-rw-r--r--kernel/include/kernel/filesystem/vfs.hpp31
-rw-r--r--kernel/src/devices/block_device.cpp5
-rw-r--r--kernel/src/devices/device.cpp7
-rw-r--r--kernel/src/devices/storage/ram_disk/ram_disk_device.cpp15
-rw-r--r--kernel/src/filesystem/custody.cpp30
-rw-r--r--kernel/src/filesystem/dentry.cpp61
-rw-r--r--kernel/src/filesystem/devfs/devfs_filesystem.cpp58
-rw-r--r--kernel/src/filesystem/devfs/devfs_root_inode.cpp22
-rw-r--r--kernel/src/filesystem/device_inode.cpp (renamed from kernel/src/filesystem/device_file.cpp)32
-rw-r--r--kernel/src/filesystem/ext2/ext2_file.cpp20
-rw-r--r--kernel/src/filesystem/ext2/ext2_filesystem.cpp24
-rw-r--r--kernel/src/filesystem/ext2/ext2_inode.cpp24
-rw-r--r--kernel/src/filesystem/file_descriptor_table.cpp24
-rw-r--r--kernel/src/filesystem/filesystem.cpp15
-rw-r--r--kernel/src/filesystem/inode.cpp85
-rw-r--r--kernel/src/filesystem/inode_file.cpp35
-rw-r--r--kernel/src/filesystem/mount.cpp23
-rw-r--r--kernel/src/filesystem/mount_table.cpp38
-rw-r--r--kernel/src/filesystem/open_file_description.cpp14
-rw-r--r--kernel/src/filesystem/rootfs/rootfs_filesystem.cpp29
-rw-r--r--kernel/src/filesystem/rootfs/rootfs_inode.cpp39
-rw-r--r--kernel/src/filesystem/vfs.cpp146
-rw-r--r--kernel/src/main.cpp128
-rw-r--r--libs/kstd/CMakeLists.txt1
-rw-r--r--libs/kstd/include/kstd/bits/shared_ptr.hpp345
-rw-r--r--libs/kstd/include/kstd/string348
-rw-r--r--libs/kstd/tests/src/string.cpp445
50 files changed, 1977 insertions, 491 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 05bba19..2231956 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -27,18 +27,28 @@ bht:
image: registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:15.2.0-4
before_script:
- apt update
- - apt install -y build-essential cmake ninja-build lcov libcatch2-dev
+ - apt install -y build-essential cmake ninja-build lcov libcatch2-dev gcovr
script:
- cmake --preset bht
- cmake --build --preset bht-dbg
- ctest --preset bht-dbg
- lcov --config-file .lcovrc --capture --directory $(pwd) --output-file coverage.info
- lcov --config-file .lcovrc --list coverage.info
+ - genhtml --prefix $(pwd) --output-directory coverage coverage.info
+ - gcovr --root . --cobertura-pretty --output coverage/cobertura-coverage.xml
+ after_script:
+ - echo "CoverageReport public URL - https://teachos.pages.ost.ch/-/kernel/-/jobs/$CI_JOB_ID/artifacts/coverage/index.html"
coverage: '/Total:\|\s*(\d+(\.\d+)?)\%/'
artifacts:
paths:
- coverage.info
+ - coverage/
expire_in: 24 hours
+ when: always
+ reports:
+ coverage_report:
+ coverage_format: cobertura
+ path: coverage/cobertura-coverage.xml
license_check:
stage: .pre
diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt
index 398022c..eb762ac 100644
--- a/kernel/CMakeLists.txt
+++ b/kernel/CMakeLists.txt
@@ -21,15 +21,21 @@ add_executable("kernel"
"src/devices/storage/storage_management.cpp"
"src/devices/storage/ram_disk/ram_disk_controller.cpp"
"src/devices/storage/ram_disk/ram_disk_device.cpp"
+ "src/filesystem/devfs/devfs_filesystem.cpp"
+ "src/filesystem/devfs/devfs_root_inode.cpp"
+ "src/filesystem/ext2/ext2_file.cpp"
"src/filesystem/ext2/ext2_filesystem.cpp"
- "src/filesystem/custody.cpp"
- "src/filesystem/device_file.cpp"
+ "src/filesystem/ext2/ext2_inode.cpp"
+ "src/filesystem/dentry.cpp"
+ "src/filesystem/device_inode.cpp"
"src/filesystem/file_descriptor_table.cpp"
"src/filesystem/filesystem.cpp"
- "src/filesystem/inode_file.cpp"
"src/filesystem/inode.cpp"
+ "src/filesystem/mount_table.cpp"
"src/filesystem/mount.cpp"
"src/filesystem/open_file_description.cpp"
+ "src/filesystem/rootfs/rootfs_filesystem.cpp"
+ "src/filesystem/rootfs/rootfs_inode.cpp"
"src/filesystem/vfs.cpp"
)
diff --git a/kernel/include/kernel/devices/block_device.hpp b/kernel/include/kernel/devices/block_device.hpp
index fb7d104..e2026dd 100644
--- a/kernel/include/kernel/devices/block_device.hpp
+++ b/kernel/include/kernel/devices/block_device.hpp
@@ -3,8 +3,9 @@
#include "kernel/devices/device.hpp"
+#include <kstd/string>
+
#include <cstddef>
-#include <string_view>
namespace devices
{
@@ -20,7 +21,7 @@ namespace devices
* @param name Device name.
* @param block_size Size of one logical block in bytes.
*/
- block_device(size_t major, size_t minor, std::string_view name, size_t block_size);
+ block_device(size_t major, size_t minor, kstd::string const & name, size_t block_size);
/**
* @brief Read data from the block at @p block_index into @p buffer.
diff --git a/kernel/include/kernel/devices/device.hpp b/kernel/include/kernel/devices/device.hpp
index d6f520f..66cb7f8 100644
--- a/kernel/include/kernel/devices/device.hpp
+++ b/kernel/include/kernel/devices/device.hpp
@@ -1,8 +1,9 @@
#ifndef TEACH_OS_KERNEL_DEVICES_DEVICE_HPP
#define TEACH_OS_KERNEL_DEVICES_DEVICE_HPP
+#include <kstd/string>
+
#include <cstddef>
-#include <string_view>
namespace devices
{
@@ -17,7 +18,7 @@ namespace devices
* @param minor Device minor number.
* @param name Device name.
*/
- device(size_t major, size_t minor, std::string_view name);
+ device(size_t major, size_t minor, kstd::string const & name);
/**
* @brief Virtual destructor for device.
@@ -40,7 +41,7 @@ namespace devices
* @brief Returns the name of the device.
* @return Device name.
*/
- [[nodiscard]] auto name() const -> std::string_view;
+ [[nodiscard]] auto name() const -> kstd::string const &;
/**
* @brief Check if the device is a block device.
@@ -54,7 +55,7 @@ namespace devices
private:
size_t m_major;
size_t m_minor;
- std::string_view m_name;
+ kstd::string m_name;
};
} // namespace devices
diff --git a/kernel/include/kernel/filesystem/custody.hpp b/kernel/include/kernel/filesystem/custody.hpp
deleted file mode 100644
index 8a0e09a..0000000
--- a/kernel/include/kernel/filesystem/custody.hpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef TEACH_OS_KERNEL_CUSTODY_HPP
-#define TEACH_OS_KERNEL_CUSTODY_HPP
-
-#include "kernel/filesystem/inode.hpp"
-
-#include <kstd/memory>
-
-namespace filesystem
-{
- struct custody
- {
- custody(kstd::shared_ptr<custody> const & parent, kstd::shared_ptr<inode> const & node);
-
- [[nodiscard]] auto get_inode() const -> kstd::shared_ptr<inode> const &;
- [[nodiscard]] auto get_parent() const -> kstd::shared_ptr<custody> const &;
-
- private:
- kstd::shared_ptr<custody> m_parent;
- kstd::shared_ptr<inode> m_inode;
- };
-} // namespace filesystem
-
-#endif \ No newline at end of file
diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp
new file mode 100644
index 0000000..c28246f
--- /dev/null
+++ b/kernel/include/kernel/filesystem/dentry.hpp
@@ -0,0 +1,43 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_DENTRY_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_DENTRY_HPP
+
+#include "kernel/filesystem/inode.hpp"
+
+#include <kstd/memory>
+#include <kstd/string>
+#include <kstd/vector>
+
+#include <cstdint>
+#include <string_view>
+
+namespace filesystem
+{
+ struct dentry
+ {
+ enum class dentry_flags : uint32_t
+ {
+ dcache_mounted = 1 << 15
+ };
+
+ dentry(kstd::shared_ptr<dentry> const & parent, kstd::shared_ptr<inode> const & node, std::string_view name = {});
+
+ [[nodiscard]] auto get_inode() const -> kstd::shared_ptr<inode> const &;
+ [[nodiscard]] auto get_parent() const -> kstd::shared_ptr<dentry> const &;
+
+ auto add_child(kstd::shared_ptr<dentry> const & child) -> void;
+ [[nodiscard]] auto find_child(std::string_view name) const -> kstd::shared_ptr<dentry>;
+
+ auto set_flag(dentry_flags flag) -> void;
+ auto unset_flag(dentry_flags flag) -> void;
+ [[nodiscard]] auto has_flag(dentry_flags flag) const -> bool;
+
+ private:
+ kstd::string m_name;
+ kstd::shared_ptr<dentry> m_parent;
+ kstd::vector<kstd::shared_ptr<dentry>> m_children;
+ kstd::shared_ptr<inode> m_inode;
+ uint32_t m_flags{0};
+ };
+} // namespace filesystem
+
+#endif \ No newline at end of file
diff --git a/kernel/include/kernel/filesystem/devfs/devfs_filesystem.hpp b/kernel/include/kernel/filesystem/devfs/devfs_filesystem.hpp
new file mode 100644
index 0000000..5559c2a
--- /dev/null
+++ b/kernel/include/kernel/filesystem/devfs/devfs_filesystem.hpp
@@ -0,0 +1,25 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVFS_DEVFS_FILESYSTEM_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_DEVFS_DEVFS_FILESYSTEM_HPP
+
+#include "kernel/devices/device.hpp"
+#include "kernel/filesystem/filesystem.hpp"
+#include "kernel/filesystem/inode.hpp"
+
+#include <kstd/memory>
+#include <kstd/vector>
+
+#include <string_view>
+
+namespace filesystem::devfs
+{
+ struct devfs_filesystem : filesystem
+ {
+ auto mount(kstd::shared_ptr<devices::device> const & device) -> int override;
+ auto lookup(kstd::shared_ptr<inode> const & parent, std::string_view name) -> kstd::shared_ptr<inode> override;
+
+ private:
+ auto build_device_inode_table() -> void;
+ };
+} // namespace filesystem::devfs
+
+#endif \ No newline at end of file
diff --git a/kernel/include/kernel/filesystem/devfs/devfs_root_inode.hpp b/kernel/include/kernel/filesystem/devfs/devfs_root_inode.hpp
new file mode 100644
index 0000000..b1d37ab
--- /dev/null
+++ b/kernel/include/kernel/filesystem/devfs/devfs_root_inode.hpp
@@ -0,0 +1,19 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVFS_DEVFS_ROOT_INODE_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_DEVFS_DEVFS_ROOT_INODE_HPP
+
+#include "kernel/filesystem/inode.hpp"
+
+#include <cstddef>
+
+namespace filesystem::devfs
+{
+ struct devfs_root_inode : inode
+ {
+ devfs_root_inode();
+
+ auto read(void * buffer, size_t offset, size_t size) const -> size_t override;
+ auto write(void const * buffer, size_t offset, size_t size) -> size_t override;
+ };
+} // namespace filesystem::devfs
+
+#endif \ No newline at end of file
diff --git a/kernel/include/kernel/filesystem/device_file.hpp b/kernel/include/kernel/filesystem/device_inode.hpp
index 06aa9b2..1cf08d4 100644
--- a/kernel/include/kernel/filesystem/device_file.hpp
+++ b/kernel/include/kernel/filesystem/device_inode.hpp
@@ -1,9 +1,9 @@
-#ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVICE_FILE_HPP
-#define TEACH_OS_KERNEL_FILESYSTEM_DEVICE_FILE_HPP
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP
#include "kernel/devices/block_device.hpp"
#include "kernel/devices/device.hpp"
-#include "kernel/filesystem/file.hpp"
+#include "kernel/filesystem/inode.hpp"
#include <kstd/memory>
@@ -11,15 +11,15 @@
namespace filesystem
{
- struct device_file : file
+ struct device_inode : inode
{
- explicit device_file(kstd::shared_ptr<devices::device> const & device);
-
- auto open() -> void override;
+ explicit device_inode(kstd::shared_ptr<devices::device> const & device);
auto read(void * buffer, size_t offset, size_t size) const -> size_t override;
auto write(void const * buffer, size_t offset, size_t size) -> size_t override;
+ [[nodiscard]] auto device() const -> kstd::shared_ptr<devices::device> const &;
+
private:
using block_op = void (*)(size_t idx, size_t off, size_t len, size_t done, devices::block_device * device,
std::byte * scratch, void * buffer);
@@ -29,4 +29,4 @@ namespace filesystem
};
} // namespace filesystem
-#endif
+#endif \ No newline at end of file
diff --git a/kernel/include/kernel/filesystem/ext2/ext2_file.hpp b/kernel/include/kernel/filesystem/ext2/ext2_file.hpp
new file mode 100644
index 0000000..e5357e3
--- /dev/null
+++ b/kernel/include/kernel/filesystem/ext2/ext2_file.hpp
@@ -0,0 +1,15 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILE_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILE_HPP
+
+#include <cstddef>
+
+namespace filesystem::ext2
+{
+ struct ext2_file
+ {
+ auto read(void * buffer, size_t offset, size_t size) const -> size_t;
+ auto write(void const * buffer, size_t offset, size_t size) -> size_t;
+ };
+} // namespace filesystem::ext2
+
+#endif \ No newline at end of file
diff --git a/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp b/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp
index d6f69c8..1445e5a 100644
--- a/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp
+++ b/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp
@@ -14,10 +14,9 @@ namespace filesystem::ext2
struct ext2_filesystem : filesystem
{
auto mount(kstd::shared_ptr<devices::device> const & device) -> int override;
- auto lookup(inode const & parent, std::string_view name) -> inode * override;
+ auto lookup(kstd::shared_ptr<inode> const & parent, std::string_view name) -> kstd::shared_ptr<inode> override;
private:
- kstd::shared_ptr<devices::device> m_device{};
};
} // namespace filesystem::ext2
diff --git a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp
new file mode 100644
index 0000000..5f4d16a
--- /dev/null
+++ b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp
@@ -0,0 +1,25 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_INODE_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_EXT2_INODE_HPP
+
+#include "kernel/filesystem/ext2/ext2_file.hpp"
+#include "kernel/filesystem/inode.hpp"
+
+#include <kstd/memory>
+
+#include <cstddef>
+
+namespace filesystem::ext2
+{
+ struct ext2_inode : inode
+ {
+ explicit ext2_inode();
+
+ auto read(void * buffer, size_t offset, size_t size) const -> size_t override;
+ auto write(void const * buffer, size_t offset, size_t size) -> size_t override;
+
+ private:
+ kstd::shared_ptr<ext2_file> m_file;
+ };
+} // namespace filesystem::ext2
+
+#endif \ No newline at end of file
diff --git a/kernel/include/kernel/filesystem/file.hpp b/kernel/include/kernel/filesystem/file.hpp
deleted file mode 100644
index e7e1b12..0000000
--- a/kernel/include/kernel/filesystem/file.hpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef TEACH_OS_KERNEL_FILESYSTEM_FILE_HPP
-#define TEACH_OS_KERNEL_FILESYSTEM_FILE_HPP
-
-#include <cstddef>
-
-namespace filesystem
-{
- struct file
- {
- virtual ~file() = default;
-
- virtual auto open() -> void = 0;
-
- virtual auto read(void * buffer, size_t offset, size_t size) const -> size_t = 0;
- virtual auto write(void const * buffer, size_t offset, size_t size) -> size_t = 0;
- };
-} // namespace filesystem
-
-#endif \ No newline at end of file
diff --git a/kernel/include/kernel/filesystem/file_descriptor_table.hpp b/kernel/include/kernel/filesystem/file_descriptor_table.hpp
index 6d78532..bc6fb24 100644
--- a/kernel/include/kernel/filesystem/file_descriptor_table.hpp
+++ b/kernel/include/kernel/filesystem/file_descriptor_table.hpp
@@ -3,10 +3,9 @@
#include "open_file_description.hpp"
+#include <kstd/memory>
#include <kstd/vector>
-#include <optional>
-
namespace filesystem
{
struct file_descriptor_table
@@ -16,15 +15,14 @@ namespace filesystem
~file_descriptor_table() = default;
- auto add_file(open_file_description & f) -> int;
- [[nodiscard]] auto get_file(int fd) const -> std::optional<open_file_description>;
+ auto add_file(kstd::shared_ptr<open_file_description> const & f) -> int;
+ [[nodiscard]] auto get_file(int fd) const -> kstd::shared_ptr<open_file_description>;
auto remove_file(int fd) -> void;
private:
file_descriptor_table() = default;
- // TODO BA-FS26 use kstd::shared_ptr when available
- kstd::vector<std::optional<open_file_description>> m_open_files{};
+ kstd::vector<kstd::shared_ptr<open_file_description>> m_open_files{};
};
} // namespace filesystem
diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp
index 23924c4..e069ced 100644
--- a/kernel/include/kernel/filesystem/filesystem.hpp
+++ b/kernel/include/kernel/filesystem/filesystem.hpp
@@ -5,6 +5,7 @@
#include "kernel/filesystem/inode.hpp"
#include <kstd/memory>
+#include <kstd/vector>
#include <string_view>
@@ -14,13 +15,15 @@ namespace filesystem
{
virtual ~filesystem() = default;
- virtual auto mount(kstd::shared_ptr<devices::device> const & device) -> int = 0;
- virtual auto lookup(inode const & parent, std::string_view name) -> inode * = 0;
+ virtual auto mount(kstd::shared_ptr<devices::device> const & device) -> int;
+ virtual auto lookup(kstd::shared_ptr<inode> const & parent, std::string_view name) -> kstd::shared_ptr<inode> = 0;
- [[nodiscard]] auto root_inode() const -> inode const &;
+ [[nodiscard]] auto root_inode() const -> kstd::shared_ptr<inode> const &;
protected:
- inode m_root_inode{}; // TODO BA-FS26 set during mount?
+ kstd::shared_ptr<inode> m_root_inode{};
+ kstd::shared_ptr<devices::device> m_device{};
+ kstd::vector<kstd::shared_ptr<inode>> m_inodes{};
};
} // namespace filesystem
diff --git a/kernel/include/kernel/filesystem/inode.hpp b/kernel/include/kernel/filesystem/inode.hpp
index a2955f9..6d8f0d4 100644
--- a/kernel/include/kernel/filesystem/inode.hpp
+++ b/kernel/include/kernel/filesystem/inode.hpp
@@ -1,37 +1,32 @@
#ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP
#define TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP
-#include "kernel/devices/device.hpp"
-#include "kernel/filesystem/inode_metadata.hpp"
-
-#include <kstd/memory>
-
#include <cstddef>
namespace filesystem
{
struct inode
{
- inode() = default;
+ enum class inode_kind
+ {
+ regular,
+ directory,
+ device
+ };
+
explicit inode(inode_kind kind);
- explicit inode(kstd::shared_ptr<devices::device> const & device);
- [[nodiscard]] auto metadata() const -> inode_metadata;
+ virtual ~inode() = default;
+
+ virtual auto read(void * buffer, size_t offset, size_t size) const -> size_t = 0;
+ virtual auto write(void const * buffer, size_t offset, size_t size) -> size_t = 0;
[[nodiscard]] auto is_directory() const -> bool;
[[nodiscard]] auto is_regular() const -> bool;
[[nodiscard]] auto is_device() const -> bool;
- [[nodiscard]] auto is_block_device() const -> bool;
- [[nodiscard]] auto major_device() const -> size_t;
- [[nodiscard]] auto minor_device() const -> size_t;
- [[nodiscard]] auto backing_device() const -> kstd::shared_ptr<devices::device> const &;
-
- auto read(void * buffer, size_t offset, size_t size) const -> size_t;
- auto write(void const * buffer, size_t offset, size_t size) -> size_t;
private:
inode_kind m_kind{inode_kind::regular};
- kstd::shared_ptr<devices::device> m_device{};
};
} // namespace filesystem
diff --git a/kernel/include/kernel/filesystem/inode_file.hpp b/kernel/include/kernel/filesystem/inode_file.hpp
deleted file mode 100644
index ee84eee..0000000
--- a/kernel/include/kernel/filesystem/inode_file.hpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_FILE_HPP
-#define TEACH_OS_KERNEL_FILESYSTEM_INODE_FILE_HPP
-
-#include "kernel/filesystem/file.hpp"
-#include "kernel/filesystem/inode.hpp"
-
-#include <kstd/memory>
-
-#include <cstddef>
-
-namespace filesystem
-{
- struct inode_file : file
- {
- explicit inode_file(kstd::shared_ptr<inode> const & inode);
-
- auto open() -> void override;
-
- auto read(void * buffer, size_t offset, size_t size) const -> size_t override;
- auto write(void const * buffer, size_t offset, size_t size) -> size_t override;
-
- private:
- kstd::shared_ptr<inode> m_inode;
- };
-} // namespace filesystem
-
-#endif \ No newline at end of file
diff --git a/kernel/include/kernel/filesystem/inode_metadata.hpp b/kernel/include/kernel/filesystem/inode_metadata.hpp
deleted file mode 100644
index ed5a09d..0000000
--- a/kernel/include/kernel/filesystem/inode_metadata.hpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_METADATA_HPP
-#define TEACH_OS_KERNEL_FILESYSTEM_INODE_METADATA_HPP
-
-#include <cstddef>
-#include <optional>
-
-namespace filesystem
-{
- enum class inode_kind
- {
- regular,
- directory,
- device
- };
-
- struct inode_metadata
- {
- inode_kind kind{inode_kind::regular};
- std::optional<size_t> major{};
- std::optional<size_t> minor{};
- };
-} // namespace filesystem
-
-#endif // TEACH_OS_KERNEL_FILESYSTEM_INODE_METADATA_HPP \ No newline at end of file
diff --git a/kernel/include/kernel/filesystem/mount.hpp b/kernel/include/kernel/filesystem/mount.hpp
index 232a9be..0f37687 100644
--- a/kernel/include/kernel/filesystem/mount.hpp
+++ b/kernel/include/kernel/filesystem/mount.hpp
@@ -1,9 +1,11 @@
#ifndef TEACH_OS_KERNEL_FILESYSTEM_MOUNT_HPP
#define TEACH_OS_KERNEL_FILESYSTEM_MOUNT_HPP
+#include "kernel/filesystem/dentry.hpp"
#include "kernel/filesystem/filesystem.hpp"
#include <kstd/memory>
+#include <kstd/string>
#include <string_view>
@@ -11,13 +13,19 @@ namespace filesystem
{
struct mount
{
- mount(std::string_view const & path, kstd::shared_ptr<filesystem> const & fs);
+ mount(kstd::shared_ptr<dentry> const & mount_dentry, kstd::shared_ptr<dentry> const & root_dentry,
+ kstd::shared_ptr<filesystem> const & fs, std::string_view mount_path);
+
+ [[nodiscard]] auto get_mount_dentry() const -> kstd::shared_ptr<dentry>;
+ [[nodiscard]] auto root_dentry() const -> kstd::shared_ptr<dentry> const &;
- [[nodiscard]] auto path() const -> std::string_view;
[[nodiscard]] auto get_filesystem() const -> kstd::shared_ptr<filesystem> const &;
+ [[nodiscard]] auto get_mount_path() const -> std::string_view;
private:
- std::string_view m_path;
+ kstd::string m_mount_path;
+ kstd::shared_ptr<dentry> m_mount_dentry;
+ kstd::shared_ptr<dentry> m_root_dentry;
kstd::shared_ptr<filesystem> m_filesystem{};
};
} // namespace filesystem
diff --git a/kernel/include/kernel/filesystem/mount_table.hpp b/kernel/include/kernel/filesystem/mount_table.hpp
new file mode 100644
index 0000000..2cd66ea
--- /dev/null
+++ b/kernel/include/kernel/filesystem/mount_table.hpp
@@ -0,0 +1,25 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_MOUNT_TABLE_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_MOUNT_TABLE_HPP
+
+#include "kernel/filesystem/mount.hpp"
+
+#include <kstd/memory>
+#include <kstd/vector>
+
+#include <string_view>
+
+namespace filesystem
+{
+ struct mount_table
+ {
+ public:
+ void add_mount(kstd::shared_ptr<mount>);
+
+ [[nodiscard]] auto find_longest_prefix_mount(std::string_view path) const -> kstd::shared_ptr<mount>;
+
+ private:
+ kstd::vector<kstd::shared_ptr<mount>> m_mounts;
+ };
+} // namespace filesystem
+
+#endif
diff --git a/kernel/include/kernel/filesystem/open_file_description.hpp b/kernel/include/kernel/filesystem/open_file_description.hpp
index 5ff094d..e17f9fe 100644
--- a/kernel/include/kernel/filesystem/open_file_description.hpp
+++ b/kernel/include/kernel/filesystem/open_file_description.hpp
@@ -1,7 +1,7 @@
#ifndef TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTION_HPP
#define TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTION_HPP
-#include "file.hpp"
+#include "kernel/filesystem/inode.hpp"
#include <kstd/memory>
@@ -11,7 +11,7 @@ namespace filesystem
{
struct open_file_description
{
- open_file_description(kstd::shared_ptr<file> const & file);
+ explicit open_file_description(kstd::shared_ptr<inode> const & inode);
~open_file_description() = default;
@@ -19,7 +19,7 @@ namespace filesystem
auto write(void const * buffer, size_t size) -> size_t;
private:
- kstd::shared_ptr<file> m_file;
+ kstd::shared_ptr<inode> m_inode;
size_t m_offset;
};
diff --git a/kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp b/kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp
new file mode 100644
index 0000000..b3e03a9
--- /dev/null
+++ b/kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp
@@ -0,0 +1,23 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_ROOTFS_FILESYSTEM_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_ROOTFS_FILESYSTEM_HPP
+
+#include "kernel/devices/device.hpp"
+#include "kernel/filesystem/filesystem.hpp"
+#include "kernel/filesystem/inode.hpp"
+
+#include <kstd/memory>
+#include <kstd/string>
+#include <kstd/vector>
+
+#include <string_view>
+
+namespace filesystem::rootfs
+{
+ struct rootfs_filesystem : filesystem
+ {
+ auto mount(kstd::shared_ptr<devices::device> const & device) -> int override;
+ auto lookup(kstd::shared_ptr<inode> const & parent, std::string_view name) -> kstd::shared_ptr<inode> override;
+ };
+} // namespace filesystem::rootfs
+
+#endif
diff --git a/kernel/include/kernel/filesystem/rootfs/rootfs_inode.hpp b/kernel/include/kernel/filesystem/rootfs/rootfs_inode.hpp
new file mode 100644
index 0000000..de4fb7c
--- /dev/null
+++ b/kernel/include/kernel/filesystem/rootfs/rootfs_inode.hpp
@@ -0,0 +1,31 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_ROOTFS_INODE_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_ROOTFS_INODE_HPP
+
+#include "kernel/filesystem/inode.hpp"
+
+#include <kstd/memory>
+#include <kstd/string>
+#include <kstd/vector>
+
+#include <cstddef>
+#include <string_view>
+#include <utility>
+
+namespace filesystem::rootfs
+{
+ struct rootfs_inode : inode
+ {
+ rootfs_inode();
+
+ auto read(void * buffer, size_t offset, size_t size) const -> size_t override;
+ auto write(void const * buffer, size_t offset, size_t size) -> size_t override;
+
+ auto add_child(std::string_view name) -> void;
+ auto lookup_child(std::string_view name) -> kstd::shared_ptr<inode>;
+
+ private:
+ kstd::vector<std::pair<kstd::string, kstd::shared_ptr<rootfs_inode>>> m_children;
+ };
+} // namespace filesystem::rootfs
+
+#endif
diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp
index a2894a6..9bee104 100644
--- a/kernel/include/kernel/filesystem/vfs.hpp
+++ b/kernel/include/kernel/filesystem/vfs.hpp
@@ -1,17 +1,13 @@
#ifndef TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP
#define TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP
-#include "kernel/devices/device.hpp"
-#include "kernel/filesystem/custody.hpp"
-#include "kernel/filesystem/ext2/ext2_filesystem.hpp"
-#include "kernel/filesystem/inode.hpp"
-#include "kernel/filesystem/mount.hpp"
+#include "kernel/filesystem/dentry.hpp"
+#include "kernel/filesystem/filesystem.hpp"
+#include "kernel/filesystem/mount_table.hpp"
#include "kernel/filesystem/open_file_description.hpp"
#include <kstd/memory>
-#include <kstd/vector>
-#include <optional>
#include <string_view>
namespace filesystem
@@ -23,23 +19,18 @@ namespace filesystem
~vfs() = default;
- auto open(std::string_view path) -> std::optional<open_file_description>;
+ auto open(std::string_view path) -> kstd::shared_ptr<open_file_description>;
+ auto do_mount(std::string_view path, kstd::shared_ptr<filesystem> const & filesystem) -> int;
private:
- struct device_node_entry
- {
- std::string_view name;
- kstd::shared_ptr<inode> node;
- };
-
vfs() = default;
- auto make_device_node(kstd::shared_ptr<devices::device> const & device) -> void;
- [[nodiscard]] auto resolve_path(std::string_view path) -> std::optional<custody>;
+ auto init_internal() -> void;
+
+ [[nodiscard]] auto resolve_path(std::string_view path) -> kstd::shared_ptr<dentry>;
+ auto do_mount_internal(std::string_view path, kstd::shared_ptr<dentry> const & mount_point_dentry,
+ kstd::shared_ptr<filesystem> const & fs) -> void;
- kstd::shared_ptr<ext2::ext2_filesystem> m_root_fs;
- std::optional<mount> m_root_mount;
- // kstd::vector<mount> m_mounts; // TODO BA-FS26 really needed?
- kstd::vector<std::optional<device_node_entry>> m_device_nodes; // TODO BA-FS26 remove again, use devtempfs
+ mount_table m_mount_table;
};
} // namespace filesystem
diff --git a/kernel/src/devices/block_device.cpp b/kernel/src/devices/block_device.cpp
index d12251b..3402814 100644
--- a/kernel/src/devices/block_device.cpp
+++ b/kernel/src/devices/block_device.cpp
@@ -4,12 +4,13 @@
#include "kernel/devices/device.hpp"
+#include <kstd/string>
+
#include <cstddef>
-#include <string_view>
namespace devices
{
- block_device::block_device(size_t major, size_t minor, std::string_view name, size_t block_size)
+ block_device::block_device(size_t major, size_t minor, kstd::string const & name, size_t block_size)
: device(major, minor, name)
, m_block_size(block_size)
{
diff --git a/kernel/src/devices/device.cpp b/kernel/src/devices/device.cpp
index 29498fa..287f14b 100644
--- a/kernel/src/devices/device.cpp
+++ b/kernel/src/devices/device.cpp
@@ -1,11 +1,12 @@
#include "kernel/devices/device.hpp"
+#include <kstd/string>
+
#include <cstddef>
-#include <string_view>
namespace devices
{
- device::device(size_t major, size_t minor, std::string_view name)
+ device::device(size_t major, size_t minor, kstd::string const & name)
: m_major(major)
, m_minor(minor)
, m_name(name)
@@ -21,7 +22,7 @@ namespace devices
return m_minor;
}
- auto device::name() const -> std::string_view
+ auto device::name() const -> kstd::string const &
{
return m_name;
}
diff --git a/kernel/src/devices/storage/ram_disk/ram_disk_device.cpp b/kernel/src/devices/storage/ram_disk/ram_disk_device.cpp
index 650a151..bf329cb 100644
--- a/kernel/src/devices/storage/ram_disk/ram_disk_device.cpp
+++ b/kernel/src/devices/storage/ram_disk/ram_disk_device.cpp
@@ -6,30 +6,19 @@
#include "kernel/devices/block_device.hpp"
#include <kstd/cstring>
+#include <kstd/string>
-#include <array>
#include <cstddef>
-#include <string_view>
namespace devices::storage::ram_disk
{
namespace
{
constexpr size_t RAM_DISK_BLOCK_SIZE = 512uz;
-
- // TODO BA-FS26 @Felix
- // TODO BA-FS26 currently only names for 9 minor devices
- constinit std::array<char, 5> name = {'r', 'a', 'm', '0', '\0'};
-
- auto determine_device_name(size_t minor) -> std::string_view
- {
- name[3] = '0' + minor;
- return std::string_view{name};
- }
} // namespace
ram_disk_device::ram_disk_device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor)
- : block_device(major, minor, determine_device_name(minor), RAM_DISK_BLOCK_SIZE)
+ : block_device(major, minor, "ram" + kstd::to_string(minor), RAM_DISK_BLOCK_SIZE)
, m_boot_module(module)
{}
diff --git a/kernel/src/filesystem/custody.cpp b/kernel/src/filesystem/custody.cpp
deleted file mode 100644
index a4dd12c..0000000
--- a/kernel/src/filesystem/custody.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-#include "kernel/filesystem/custody.hpp"
-
-#include "kapi/system.hpp"
-
-#include "kernel/filesystem/inode.hpp"
-
-#include <kstd/memory>
-
-namespace filesystem
-{
- custody::custody(kstd::shared_ptr<custody> const & parent, kstd::shared_ptr<inode> const & node)
- : m_parent(parent)
- , m_inode(node)
- {
- if (!m_inode)
- {
- kapi::system::panic("[FILESYSTEM] custody constructed with null inode.");
- }
- }
-
- auto custody::get_inode() const -> kstd::shared_ptr<inode> const &
- {
- return m_inode;
- }
-
- auto custody::get_parent() const -> kstd::shared_ptr<custody> const &
- {
- return m_parent;
- }
-} // namespace filesystem \ No newline at end of file
diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp
new file mode 100644
index 0000000..76949f2
--- /dev/null
+++ b/kernel/src/filesystem/dentry.cpp
@@ -0,0 +1,61 @@
+#include "kernel/filesystem/dentry.hpp"
+
+#include "kapi/system.hpp"
+
+#include "kernel/filesystem/inode.hpp"
+
+#include <kstd/memory>
+
+#include <algorithm>
+#include <cstdint>
+#include <string_view>
+
+namespace filesystem
+{
+ dentry::dentry(kstd::shared_ptr<dentry> const & parent, kstd::shared_ptr<inode> const & node, std::string_view name)
+ : m_name(name)
+ , m_parent(parent)
+ , m_inode(node)
+ {
+ if (!m_inode)
+ {
+ kapi::system::panic("[FILESYSTEM] dentry constructed with null inode.");
+ }
+ }
+
+ auto dentry::get_inode() const -> kstd::shared_ptr<inode> const &
+ {
+ return m_inode;
+ }
+
+ auto dentry::get_parent() const -> kstd::shared_ptr<dentry> const &
+ {
+ return m_parent;
+ }
+
+ auto dentry::add_child(kstd::shared_ptr<dentry> const & child) -> void
+ {
+ m_children.push_back(child);
+ }
+
+ auto dentry::find_child(std::string_view name) const -> kstd::shared_ptr<dentry>
+ {
+ auto it = std::ranges::find_if(m_children, [&](auto const & child) { return child->m_name == name; });
+ return (it != m_children.end()) ? *it : nullptr;
+ }
+
+ auto dentry::set_flag(dentry_flags flag) -> void
+ {
+ m_flags |= static_cast<uint32_t>(flag);
+ }
+
+ auto dentry::unset_flag(dentry_flags flag) -> void
+ {
+ m_flags &= ~static_cast<uint32_t>(flag);
+ }
+
+ auto dentry::has_flag(dentry_flags flag) const -> bool
+ {
+ return (m_flags & static_cast<uint32_t>(flag)) != 0;
+ }
+} // namespace filesystem \ No newline at end of file
diff --git a/kernel/src/filesystem/devfs/devfs_filesystem.cpp b/kernel/src/filesystem/devfs/devfs_filesystem.cpp
new file mode 100644
index 0000000..c4cd81a
--- /dev/null
+++ b/kernel/src/filesystem/devfs/devfs_filesystem.cpp
@@ -0,0 +1,58 @@
+#include "kernel/filesystem/devfs/devfs_filesystem.hpp"
+
+#include "kernel/devices/device.hpp"
+#include "kernel/devices/storage/storage_management.hpp"
+#include "kernel/filesystem/devfs/devfs_root_inode.hpp"
+#include "kernel/filesystem/device_inode.hpp"
+#include "kernel/filesystem/inode.hpp"
+
+#include <kstd/memory>
+
+#include <algorithm>
+#include <string_view>
+
+namespace filesystem::devfs
+{
+ auto devfs_filesystem::mount(kstd::shared_ptr<devices::device> const &) -> int
+ {
+ m_root_inode = kstd::make_shared<devfs_root_inode>();
+ build_device_inode_table();
+
+ return 0;
+ }
+
+ auto devfs_filesystem::lookup(kstd::shared_ptr<inode> const & parent, std::string_view name)
+ -> kstd::shared_ptr<inode>
+ {
+ if (!parent || !parent->is_directory())
+ {
+ return nullptr;
+ }
+
+ if (parent.get() != m_root_inode.get())
+ {
+ return nullptr;
+ }
+
+ auto it = std::ranges::find_if(m_inodes, [&](auto const & dev_node) {
+ auto device_inode_ptr = static_cast<device_inode *>(dev_node.get());
+ if (!device_inode_ptr)
+ {
+ return false;
+ }
+ return device_inode_ptr->device()->name() == name;
+ });
+ return (it != m_inodes.end()) ? *it : nullptr;
+ }
+
+ auto devfs_filesystem::build_device_inode_table() -> void
+ {
+ m_inodes.clear();
+
+ auto storage_mgmt = devices::storage::storage_management::get();
+ std::ranges::for_each(storage_mgmt.all_controllers(), [&](auto const & controller) {
+ std::ranges::for_each(controller->all_devices(),
+ [&](auto const & device) { m_inodes.push_back(kstd::make_shared<device_inode>(device)); });
+ });
+ }
+} // namespace filesystem::devfs \ No newline at end of file
diff --git a/kernel/src/filesystem/devfs/devfs_root_inode.cpp b/kernel/src/filesystem/devfs/devfs_root_inode.cpp
new file mode 100644
index 0000000..53441b0
--- /dev/null
+++ b/kernel/src/filesystem/devfs/devfs_root_inode.cpp
@@ -0,0 +1,22 @@
+#include "kernel/filesystem/devfs/devfs_root_inode.hpp"
+
+#include "kernel/filesystem/inode.hpp"
+
+#include <cstddef>
+
+namespace filesystem::devfs
+{
+ devfs_root_inode::devfs_root_inode()
+ : inode(inode_kind::directory)
+ {}
+
+ auto devfs_root_inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t
+ {
+ return 0;
+ }
+
+ auto devfs_root_inode::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t
+ {
+ return 0;
+ }
+} // namespace filesystem::devfs \ No newline at end of file
diff --git a/kernel/src/filesystem/device_file.cpp b/kernel/src/filesystem/device_inode.cpp
index c6db5af..64cd6e9 100644
--- a/kernel/src/filesystem/device_file.cpp
+++ b/kernel/src/filesystem/device_inode.cpp
@@ -1,9 +1,10 @@
-#include "kernel/filesystem/device_file.hpp"
+#include "kernel/filesystem/device_inode.hpp"
#include "kapi/system.hpp"
#include "kernel/devices/block_device.hpp"
#include "kernel/devices/device.hpp"
+#include "kernel/filesystem/inode.hpp"
#include <kstd/cstring>
#include <kstd/memory>
@@ -14,21 +15,17 @@
namespace filesystem
{
- device_file::device_file(kstd::shared_ptr<devices::device> const & device)
- : m_device(device)
+ device_inode::device_inode(kstd::shared_ptr<devices::device> const & device)
+ : inode(inode_kind::device)
+ , m_device(device)
{
- if (!m_device)
+ if (!device)
{
- kapi::system::panic("[FILESYSTEM] device_file constructed with null device.");
+ kapi::system::panic("[FILESYSTEM] device_inode constructed with null device.");
}
}
- auto device_file::open() -> void
- {
- // Hook point for permission checks or lazy metadata loading.
- }
-
- auto device_file::read(void * buffer, size_t offset, size_t size) const -> size_t
+ auto device_inode::read(void * buffer, size_t offset, size_t size) const -> size_t
{
if (m_device->is_block_device())
{
@@ -53,7 +50,7 @@ namespace filesystem
}
}
- auto device_file::write(void const * buffer, size_t offset, size_t size) -> size_t
+ auto device_inode::write(void const * buffer, size_t offset, size_t size) -> size_t
{
if (m_device->is_block_device())
{
@@ -79,11 +76,16 @@ namespace filesystem
}
}
- auto device_file::process_blocks(size_t offset, size_t size, void * buffer, block_op op) const -> size_t
+ auto device_inode::device() const -> kstd::shared_ptr<devices::device> const &
+ {
+ return m_device;
+ }
+
+ auto device_inode::process_blocks(size_t offset, size_t size, void * buffer, block_op op) const -> size_t
{
if (buffer == nullptr)
{
- kapi::system::panic("[FILESYSTEM] device_file::write called with null buffer.");
+ kapi::system::panic("[FILESYSTEM] device_file::process_blocks called with null buffer.");
}
if (size == 0)
@@ -121,4 +123,4 @@ namespace filesystem
return processed;
}
-} // namespace filesystem
+} // namespace filesystem \ No newline at end of file
diff --git a/kernel/src/filesystem/ext2/ext2_file.cpp b/kernel/src/filesystem/ext2/ext2_file.cpp
new file mode 100644
index 0000000..7217c77
--- /dev/null
+++ b/kernel/src/filesystem/ext2/ext2_file.cpp
@@ -0,0 +1,20 @@
+#include "kernel/filesystem/ext2/ext2_file.hpp"
+
+#include "kapi/system.hpp"
+
+#include <cstddef>
+
+namespace filesystem::ext2
+{
+ auto ext2_file::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t
+ {
+ kapi::system::panic("[FILESYSTEM] ext2_file::read is not implemented yet.");
+ return 0;
+ }
+
+ auto ext2_file::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t
+ {
+ kapi::system::panic("[FILESYSTEM] ext2_file::write is not implemented yet.");
+ return 0;
+ }
+} // namespace filesystem::ext2
diff --git a/kernel/src/filesystem/ext2/ext2_filesystem.cpp b/kernel/src/filesystem/ext2/ext2_filesystem.cpp
index 408b292..373c6a2 100644
--- a/kernel/src/filesystem/ext2/ext2_filesystem.cpp
+++ b/kernel/src/filesystem/ext2/ext2_filesystem.cpp
@@ -1,8 +1,9 @@
#include "kernel/filesystem/ext2/ext2_filesystem.hpp"
#include "kernel/devices/device.hpp"
+#include "kernel/filesystem/ext2/ext2_inode.hpp"
+#include "kernel/filesystem/filesystem.hpp"
#include "kernel/filesystem/inode.hpp"
-#include "kernel/filesystem/inode_metadata.hpp"
#include <kstd/memory>
@@ -12,22 +13,25 @@ namespace filesystem::ext2
{
auto ext2_filesystem::mount(kstd::shared_ptr<devices::device> const & device) -> int
{
- if (!device)
- {
- return -1; // TODO BA-FS26 panic or errorcode?
- }
-
- m_device = device;
+ filesystem::mount(device); // TODO BA-FS26 error handling?
// TODO BA-FS26 load proper root inode from ext2 metadata
- m_root_inode = inode{inode_kind::directory};
+ // m_root_inode = inode{inode_kind::directory};
// TODO BA-FS26 implement
+ m_root_inode = kstd::make_shared<ext2_inode>();
return 0;
}
- auto ext2_filesystem::lookup(inode const & /*parent*/, std::string_view /*name*/) -> inode *
+ auto ext2_filesystem::lookup(kstd::shared_ptr<inode> const & /*parent*/, std::string_view name)
+ -> kstd::shared_ptr<inode>
{
// TODO BA-FS26 implement ext2 directory traversal and inode loading
- return nullptr;
+ if (name == "dev")
+ {
+ // TODO BA-FS26 just for testing
+ return nullptr;
+ }
+
+ return kstd::make_shared<ext2_inode>();
}
} // namespace filesystem::ext2
diff --git a/kernel/src/filesystem/ext2/ext2_inode.cpp b/kernel/src/filesystem/ext2/ext2_inode.cpp
new file mode 100644
index 0000000..3cc0fb2
--- /dev/null
+++ b/kernel/src/filesystem/ext2/ext2_inode.cpp
@@ -0,0 +1,24 @@
+#include "kernel/filesystem/ext2/ext2_inode.hpp"
+
+#include "kernel/filesystem/inode.hpp"
+
+#include <cstddef>
+
+namespace filesystem::ext2
+{
+ ext2_inode::ext2_inode()
+ : inode(inode_kind::regular)
+ {}
+
+ auto ext2_inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t
+ {
+ // TODO BA-FS26 implement
+ return 0;
+ }
+
+ auto ext2_inode::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t
+ {
+ // TODO BA-FS26 implement
+ return 0;
+ }
+} // namespace filesystem::ext2 \ No newline at end of file
diff --git a/kernel/src/filesystem/file_descriptor_table.cpp b/kernel/src/filesystem/file_descriptor_table.cpp
index 814322e..6eb3845 100644
--- a/kernel/src/filesystem/file_descriptor_table.cpp
+++ b/kernel/src/filesystem/file_descriptor_table.cpp
@@ -4,6 +4,8 @@
#include "kernel/filesystem/open_file_description.hpp"
+#include <kstd/memory>
+
#include <algorithm>
#include <cstddef>
#include <optional>
@@ -35,9 +37,15 @@ namespace filesystem
return *global_file_descriptor_table;
}
- auto file_descriptor_table::add_file(open_file_description & file_description) -> int
+ auto file_descriptor_table::add_file(kstd::shared_ptr<open_file_description> const & file_description) -> int
{
- auto it = std::ranges::find_if(m_open_files, [](auto & open_file) { return !open_file.has_value(); });
+ if (!file_description)
+ {
+ // TODO BA-FS26 panic or errorcode?
+ return -1;
+ }
+
+ auto it = std::ranges::find_if(m_open_files, [](auto const & open_file) { return open_file == nullptr; });
if (it != m_open_files.end())
{
*it = file_description;
@@ -48,20 +56,20 @@ namespace filesystem
return static_cast<int>(m_open_files.size() - 1);
}
- auto file_descriptor_table::get_file(int fd) const -> std::optional<open_file_description>
+ auto file_descriptor_table::get_file(int fd) const -> kstd::shared_ptr<open_file_description>
{
if (fd < 0)
{
- return std::nullopt;
+ return nullptr;
}
auto const index = static_cast<size_t>(fd);
- if (index >= m_open_files.size() || !m_open_files.at(fd).has_value())
+ if (index >= m_open_files.size())
{
- return std::nullopt;
+ return nullptr;
}
- return m_open_files.at(fd);
+ return m_open_files.at(index);
}
auto file_descriptor_table::remove_file(int fd) -> void
@@ -77,6 +85,6 @@ namespace filesystem
return;
}
- m_open_files.at(fd).reset();
+ m_open_files.at(index) = nullptr;
}
} // namespace filesystem \ No newline at end of file
diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp
index 86b7940..0e33d95 100644
--- a/kernel/src/filesystem/filesystem.cpp
+++ b/kernel/src/filesystem/filesystem.cpp
@@ -1,10 +1,23 @@
#include "kernel/filesystem/filesystem.hpp"
+#include "kernel/devices/device.hpp"
#include "kernel/filesystem/inode.hpp"
+#include <kstd/memory>
+
namespace filesystem
{
- auto filesystem::root_inode() const -> inode const &
+ auto filesystem::mount(kstd::shared_ptr<devices::device> const & device) -> int
+ {
+ if (!device)
+ {
+ return -1; // TODO BA-FS26 panic or errorcode?
+ }
+ m_device = device;
+ return 0;
+ }
+
+ auto filesystem::root_inode() const -> kstd::shared_ptr<inode> const &
{
return m_root_inode;
}
diff --git a/kernel/src/filesystem/inode.cpp b/kernel/src/filesystem/inode.cpp
index af73662..de3282f 100644
--- a/kernel/src/filesystem/inode.cpp
+++ b/kernel/src/filesystem/inode.cpp
@@ -1,44 +1,11 @@
#include "kernel/filesystem/inode.hpp"
-#include "kapi/system.hpp"
-
-#include "kernel/devices/device.hpp"
-#include "kernel/filesystem/inode_metadata.hpp"
-
-#include <kstd/memory>
-
-#include <cstddef>
-
namespace filesystem
{
inode::inode(inode_kind kind)
: m_kind(kind)
{}
- inode::inode(kstd::shared_ptr<devices::device> const & device)
- : m_kind(inode_kind::device)
- , m_device(device)
- {
- if (!m_device)
- {
- kapi::system::panic("[FILESYSTEM] inode constructed with null device.");
- }
- }
-
- auto inode::metadata() const -> inode_metadata
- {
- auto meta = inode_metadata{};
- meta.kind = m_kind;
-
- if (is_device())
- {
- meta.major = m_device->major();
- meta.minor = m_device->minor();
- }
-
- return meta;
- }
-
auto inode::is_directory() const -> bool
{
return m_kind == inode_kind::directory;
@@ -53,56 +20,4 @@ namespace filesystem
{
return m_kind == inode_kind::device;
}
-
- auto inode::is_block_device() const -> bool
- {
- return is_device() && m_device->is_block_device();
- }
-
- auto inode::major_device() const -> size_t
- {
- if (!is_device())
- {
- kapi::system::panic("[FILESYSTEM] inode::major_device called on non-device inode.");
- }
-
- return m_device->major();
- }
-
- auto inode::minor_device() const -> size_t
- {
- if (!is_device())
- {
- kapi::system::panic("[FILESYSTEM] inode::minor_device called on non-device inode.");
- }
-
- return m_device->minor();
- }
-
- auto inode::backing_device() const -> kstd::shared_ptr<devices::device> const &
- {
- return m_device;
- }
-
- auto inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t
- {
- if (is_device())
- {
- kapi::system::panic("[FILESYSTEM] inode::read called on device inode. Open it as a device file first.");
- }
-
- // TODO BA-FS26
- return 0;
- }
-
- auto inode::write(void const *, size_t, size_t) -> size_t
- {
- if (is_device())
- {
- kapi::system::panic("[FILESYSTEM] inode::write called on device inode. Open it as a device file first.");
- }
-
- kapi::system::panic("[FILESYSTEM] inode::write is not implemented yet");
- return 0;
- }
} // namespace filesystem \ No newline at end of file
diff --git a/kernel/src/filesystem/inode_file.cpp b/kernel/src/filesystem/inode_file.cpp
deleted file mode 100644
index 7abac7b..0000000
--- a/kernel/src/filesystem/inode_file.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#include "kernel/filesystem/inode_file.hpp"
-
-#include "kapi/system.hpp"
-
-#include "kernel/filesystem/inode.hpp"
-
-#include <kstd/memory>
-
-#include <cstddef>
-
-namespace filesystem
-{
- inode_file::inode_file(kstd::shared_ptr<inode> const & inode)
- : m_inode(inode)
- {
- if (!m_inode)
- {
- kapi::system::panic("[FILESYSTEM] inode_file constructed with null inode");
- }
- }
-
- auto inode_file::open() -> void
- {
- // Hook point for permission checks or lazy metadata loading.
- }
-
- auto inode_file::read(void * buffer, size_t offset, size_t size) const -> size_t
- {
- return m_inode->read(buffer, offset, size);
- }
- auto inode_file::write(void const * buffer, size_t offset, size_t size) -> size_t
- {
- return m_inode->write(buffer, offset, size);
- }
-} // namespace filesystem \ No newline at end of file
diff --git a/kernel/src/filesystem/mount.cpp b/kernel/src/filesystem/mount.cpp
index a2c501f..afc07fa 100644
--- a/kernel/src/filesystem/mount.cpp
+++ b/kernel/src/filesystem/mount.cpp
@@ -2,16 +2,21 @@
#include "kapi/system.hpp"
+#include "kernel/filesystem/dentry.hpp"
#include "kernel/filesystem/filesystem.hpp"
#include <kstd/memory>
+#include <kstd/string>
#include <string_view>
namespace filesystem
{
- mount::mount(std::string_view const & path, kstd::shared_ptr<filesystem> const & fs)
- : m_path(path)
+ mount::mount(kstd::shared_ptr<dentry> const & mount_dentry, kstd::shared_ptr<dentry> const & root_dentry,
+ kstd::shared_ptr<filesystem> const & fs, std::string_view mount_path)
+ : m_mount_path(mount_path)
+ , m_mount_dentry(mount_dentry)
+ , m_root_dentry(root_dentry)
, m_filesystem(fs)
{
if (!m_filesystem)
@@ -20,13 +25,23 @@ namespace filesystem
}
}
- auto mount::path() const -> std::string_view
+ auto mount::get_mount_dentry() const -> kstd::shared_ptr<dentry>
{
- return m_path;
+ return m_mount_dentry;
}
auto mount::get_filesystem() const -> kstd::shared_ptr<filesystem> const &
{
return m_filesystem;
}
+
+ auto mount::root_dentry() const -> kstd::shared_ptr<dentry> const &
+ {
+ return m_root_dentry;
+ }
+
+ auto mount::get_mount_path() const -> std::string_view
+ {
+ return m_mount_path.view();
+ }
} // namespace filesystem \ No newline at end of file
diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp
new file mode 100644
index 0000000..b9e57d4
--- /dev/null
+++ b/kernel/src/filesystem/mount_table.cpp
@@ -0,0 +1,38 @@
+#include "kernel/filesystem/mount_table.hpp"
+
+#include "kernel/filesystem/mount.hpp"
+
+#include <kstd/memory>
+
+#include <cstddef>
+#include <string_view>
+
+namespace filesystem
+{
+ void mount_table::add_mount(kstd::shared_ptr<mount> mount)
+ {
+ m_mounts.push_back(mount);
+ }
+
+ auto mount_table::find_longest_prefix_mount(std::string_view path) const -> kstd::shared_ptr<mount>
+ {
+ kstd::shared_ptr<mount> mount_with_longest_prefix = nullptr;
+ std::size_t best_len = 0;
+
+ for (auto const & mount : m_mounts)
+ {
+ auto mp = mount->get_mount_path();
+
+ // /a/b/c should match /a/b but not /a/bb or /a/b/c/d, / should match everything
+ bool is_prefix = path.starts_with(mp) && (mp == "/" || path.size() == mp.size() || path[mp.size()] == '/');
+
+ if (is_prefix && mp.size() >= best_len)
+ {
+ mount_with_longest_prefix = mount;
+ best_len = mp.size();
+ }
+ }
+
+ return mount_with_longest_prefix;
+ }
+} // namespace filesystem \ No newline at end of file
diff --git a/kernel/src/filesystem/open_file_description.cpp b/kernel/src/filesystem/open_file_description.cpp
index ff4d678..93c38ac 100644
--- a/kernel/src/filesystem/open_file_description.cpp
+++ b/kernel/src/filesystem/open_file_description.cpp
@@ -1,6 +1,6 @@
#include "kernel/filesystem/open_file_description.hpp"
-#include "kernel/filesystem/file.hpp"
+#include "kernel/filesystem/inode.hpp"
#include <kstd/memory>
#include <kstd/os/error.hpp>
@@ -9,26 +9,26 @@
namespace filesystem
{
- open_file_description::open_file_description(kstd::shared_ptr<file> const & file)
- : m_file(file)
+ open_file_description::open_file_description(kstd::shared_ptr<inode> const & inode)
+ : m_inode(inode)
, m_offset(0)
{
- if (!file)
+ if (!inode)
{
- kstd::os::panic("[FILESYSTEM] open_file_description constructed with null file.");
+ kstd::os::panic("[FILESYSTEM] open_file_description constructed with null inode.");
}
}
auto open_file_description::read(void * buffer, size_t size) -> size_t
{
- auto read_bytes = m_file->read(buffer, m_offset, size);
+ auto read_bytes = m_inode->read(buffer, m_offset, size);
m_offset += read_bytes;
return read_bytes;
}
auto open_file_description::write(void const * buffer, size_t size) -> size_t
{
- auto written_bytes = m_file->write(buffer, m_offset, size);
+ auto written_bytes = m_inode->write(buffer, m_offset, size);
m_offset += written_bytes;
return written_bytes;
}
diff --git a/kernel/src/filesystem/rootfs/rootfs_filesystem.cpp b/kernel/src/filesystem/rootfs/rootfs_filesystem.cpp
new file mode 100644
index 0000000..22502aa
--- /dev/null
+++ b/kernel/src/filesystem/rootfs/rootfs_filesystem.cpp
@@ -0,0 +1,29 @@
+#include "kernel/filesystem/rootfs/rootfs_filesystem.hpp"
+
+#include "kernel/devices/device.hpp"
+#include "kernel/filesystem/inode.hpp"
+#include "kernel/filesystem/rootfs/rootfs_inode.hpp"
+
+#include <kstd/memory>
+
+#include <string_view>
+
+namespace filesystem::rootfs
+{
+ auto rootfs_filesystem::mount(kstd::shared_ptr<devices::device> const &) -> int
+ {
+ auto rfs_inode = kstd::make_shared<rootfs_inode>();
+ rfs_inode->add_child("dev");
+ m_root_inode = rfs_inode;
+
+ return 0;
+ }
+
+ auto rootfs_filesystem::lookup(kstd::shared_ptr<inode> const & parent, std::string_view name)
+ -> kstd::shared_ptr<inode>
+ {
+ if (auto * rfs_inode = static_cast<rootfs_inode *>(parent.get()))
+ return rfs_inode->lookup_child(name);
+ return nullptr;
+ }
+} // namespace filesystem::rootfs
diff --git a/kernel/src/filesystem/rootfs/rootfs_inode.cpp b/kernel/src/filesystem/rootfs/rootfs_inode.cpp
new file mode 100644
index 0000000..9bbfbce
--- /dev/null
+++ b/kernel/src/filesystem/rootfs/rootfs_inode.cpp
@@ -0,0 +1,39 @@
+#include "kernel/filesystem/rootfs/rootfs_inode.hpp"
+
+#include "kernel/filesystem/inode.hpp"
+
+#include <kstd/memory>
+#include <kstd/string>
+
+#include <algorithm>
+#include <cstddef>
+#include <string_view>
+#include <utility>
+
+namespace filesystem::rootfs
+{
+ rootfs_inode::rootfs_inode()
+ : inode(inode_kind::directory)
+ {}
+
+ auto rootfs_inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t
+ {
+ return 0;
+ }
+
+ auto rootfs_inode::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t
+ {
+ return 0;
+ }
+
+ auto rootfs_inode::add_child(std::string_view name) -> void
+ {
+ m_children.push_back(std::make_pair(kstd::string{name}, kstd::make_shared<rootfs_inode>()));
+ }
+
+ auto rootfs_inode::lookup_child(std::string_view name) -> kstd::shared_ptr<inode>
+ {
+ auto it = std::ranges::find_if(m_children, [&](auto const & pair) { return pair.first == name; });
+ return (it != m_children.end()) ? it->second : nullptr;
+ }
+} // namespace filesystem::rootfs
diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp
index 4e0b6bf..7a90531 100644
--- a/kernel/src/filesystem/vfs.cpp
+++ b/kernel/src/filesystem/vfs.cpp
@@ -2,20 +2,19 @@
#include "kapi/system.hpp"
-#include "kernel/devices/device.hpp"
#include "kernel/devices/storage/storage_management.hpp"
-#include "kernel/filesystem/custody.hpp"
-#include "kernel/filesystem/device_file.hpp"
+#include "kernel/filesystem/dentry.hpp"
+#include "kernel/filesystem/devfs/devfs_filesystem.hpp"
#include "kernel/filesystem/ext2/ext2_filesystem.hpp"
-#include "kernel/filesystem/inode.hpp"
-#include "kernel/filesystem/inode_file.hpp"
+#include "kernel/filesystem/filesystem.hpp"
#include "kernel/filesystem/mount.hpp"
#include "kernel/filesystem/open_file_description.hpp"
+#include "kernel/filesystem/rootfs/rootfs_filesystem.hpp"
#include <kstd/memory>
-#include <algorithm>
#include <optional>
+#include <ranges>
#include <string_view>
namespace filesystem
@@ -33,26 +32,29 @@ namespace filesystem
}
active_vfs.emplace(vfs{});
+ active_vfs->init_internal();
+ }
- auto storage_mgmt = devices::storage::storage_management::get();
- if (auto boot_device = storage_mgmt.determine_boot_device())
- {
- active_vfs->m_root_fs = kstd::make_shared<ext2::ext2_filesystem>();
- if (active_vfs->m_root_fs->mount(boot_device) != 0)
- {
- kapi::system::panic("[FILESYSTEM] Failed to mount root filesystem.");
- }
+ auto vfs::init_internal() -> void
+ {
+ auto root_fs = kstd::make_shared<rootfs::rootfs_filesystem>();
+ root_fs->mount(nullptr);
- active_vfs->m_root_mount = mount{"/", active_vfs->m_root_fs};
+ auto root_fs_root_dentry = kstd::make_shared<dentry>(nullptr, root_fs->root_inode());
+ m_mount_table.add_mount(kstd::make_shared<mount>(nullptr, root_fs_root_dentry, root_fs, ""));
- std::ranges::for_each(storage_mgmt.all_controllers(), [&](auto controller) {
- std::ranges::for_each(controller->all_devices(), [&](auto device) { active_vfs->make_device_node(device); });
- });
- }
- else
+ auto storage_mgmt = devices::storage::storage_management::get();
+ if (auto boot_device = storage_mgmt.determine_boot_device())
{
- // TODO BA-FS26 ?? what when no boot_device == no modules loaded??
+ // TODO BA-FS26 detect fs type from boot device and load corresponding fs, for now just assume ext2
+ auto boot_root_fs = kstd::make_shared<ext2::ext2_filesystem>();
+ boot_root_fs->mount(boot_device);
+ do_mount_internal("/", root_fs_root_dentry, boot_root_fs);
}
+
+ auto device_fs = kstd::make_shared<devfs::devfs_filesystem>();
+ device_fs->mount(nullptr);
+ do_mount_internal("/dev", root_fs_root_dentry, device_fs);
}
auto vfs::get() -> vfs &
@@ -65,58 +67,98 @@ namespace filesystem
return *active_vfs;
}
- auto vfs::open(std::string_view path) -> std::optional<open_file_description>
+ auto vfs::open(std::string_view path) -> kstd::shared_ptr<open_file_description>
{
- if (auto custody = resolve_path(path))
+ if (auto dentry = resolve_path(path))
{
- auto node = custody->get_inode();
- if (node->is_device())
- {
- auto current_device_file = kstd::make_shared<device_file>(node->backing_device());
- current_device_file->open();
- return open_file_description{current_device_file};
- }
-
- auto current_inode_file = kstd::make_shared<inode_file>(node);
- current_inode_file->open();
- return open_file_description{current_inode_file};
+ return kstd::make_shared<open_file_description>(dentry->get_inode());
}
- return std::nullopt;
+ return nullptr;
}
- auto vfs::make_device_node(kstd::shared_ptr<devices::device> const & device) -> void
+ auto vfs::do_mount(std::string_view path, kstd::shared_ptr<filesystem> const & filesystem) -> int
{
- if (!device)
+ if (!filesystem)
{
- kapi::system::panic("[FILESYSTEM] make_device_node called with null device.");
+ return -1; // TODO BA-FS26 panic or errorcode?
}
- m_device_nodes.push_back(device_node_entry{device->name(), kstd::make_shared<inode>(device)});
+ 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?
+ }
+
+ if (auto mount_point_dentry = resolve_path(path))
+ {
+ do_mount_internal(path, mount_point_dentry, filesystem);
+ return 0;
+ }
+
+ return -1;
}
- auto vfs::resolve_path(std::string_view path) -> std::optional<custody>
+ auto vfs::do_mount_internal(std::string_view path, kstd::shared_ptr<dentry> const & mount_point_dentry,
+ kstd::shared_ptr<filesystem> const & fs) -> void
{
- // TODO BA-FS26 implement real path resolution with mounts and directories etc.
- // For now, just support device nodes at /dev/<device_name>.
+ // TODO BA-FS26 check if mount point is already mounted and handle it (unmount old fs, fail, etc.)
+ auto new_fs_root = kstd::make_shared<dentry>(mount_point_dentry, fs->root_inode());
+ auto new_mount = kstd::make_shared<mount>(mount_point_dentry, new_fs_root, fs, path);
+ m_mount_table.add_mount(new_mount);
+ mount_point_dentry->set_flag(dentry::dentry_flags::dcache_mounted);
+ }
+
+ auto vfs::resolve_path(std::string_view path) -> kstd::shared_ptr<dentry>
+ {
+ // TODO BA-FS26 implement full path resolution semantics.
+ // 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;
- constexpr auto device_prefix = std::string_view{"/dev/"};
- if (path.starts_with(device_prefix))
+ // TODO BA-FS26 longest match in mount_table -> jump into final fs directly
+ // TODO BA-FS26 performance optimization first check mounted_flag O(1) then check mount_table O(n)
+
+ auto best_mount = m_mount_table.find_longest_prefix_mount(path);
+ if (!best_mount)
+ {
+ kapi::system::panic("[FILESYSTEM] no root mount found.");
+ }
+
+ auto current_dentry = best_mount->root_dentry();
+ auto current_fs = best_mount->get_filesystem();
+
+ std::string_view remaining = path.substr(best_mount->get_mount_path().size());
+
+ auto path_parts =
+ std::views::split(remaining, '/') | std::views::filter([](auto const & part) { return !part.empty(); });
+
+ for (auto const & part : path_parts)
{
- 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;
- });
+ std::string_view part_view{part};
- if (entry != m_device_nodes.end())
+ auto next_dentry = current_dentry->find_child(part_view);
+ if (!next_dentry)
{
- return custody{nullptr, entry->value().node};
+ auto found_inode = current_fs->lookup(current_dentry->get_inode(), part_view);
+ if (!found_inode)
+ return nullptr;
+
+ next_dentry = kstd::make_shared<dentry>(current_dentry, found_inode, part_view);
+ current_dentry->add_child(next_dentry);
}
- return std::nullopt;
+ current_dentry = next_dentry;
}
- return std::nullopt;
+ return current_dentry;
}
} // namespace filesystem \ No newline at end of file
diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp
index eb59402..98c88f2 100644
--- a/kernel/src/main.cpp
+++ b/kernel/src/main.cpp
@@ -4,29 +4,41 @@
#include "kapi/system.hpp"
#include "kernel/devices/storage/storage_management.hpp"
-#include "kernel/filesystem/device_file.hpp"
+#include "kernel/filesystem/device_inode.hpp"
+#include "kernel/filesystem/ext2/ext2_filesystem.hpp"
#include "kernel/filesystem/file_descriptor_table.hpp"
#include "kernel/filesystem/open_file_description.hpp"
#include "kernel/filesystem/vfs.hpp"
#include "kernel/memory.hpp"
+#include <kstd/format>
#include <kstd/memory>
#include <kstd/os/error.hpp>
#include <kstd/print>
#include <kstd/vector>
+#include <algorithm>
#include <cstddef>
-#include <cstdint>
-auto run_test_code() -> void
+auto test_device_names() -> void
+{
+ auto storage_mgmt = devices::storage::storage_management::get();
+ std::ranges::for_each(storage_mgmt.all_controllers(), [](auto const & controller) {
+ std::ranges::for_each(controller->all_devices(),
+ [](auto const & device) { kstd::println("{}", device->name().view()); });
+ });
+}
+
+auto test_file_description_manually() -> void
{
// setup
auto fd_table = filesystem::file_descriptor_table::get();
auto storage_mgmt = devices::storage::storage_management::get();
auto device = storage_mgmt.device_by_major_minor(1, 0);
- auto dev_file = kstd::make_shared<filesystem::device_file>(device);
- filesystem::open_file_description ofd(dev_file);
+ auto dev_node = kstd::make_shared<filesystem::device_inode>(device);
+
+ auto ofd = kstd::make_shared<filesystem::open_file_description>(dev_node);
auto fd_index = fd_table.add_file(ofd);
// use: read two bytes and write two again
@@ -38,17 +50,8 @@ auto run_test_code() -> void
kstd::vector<std::byte> buffer{2};
auto number_of_read_bytes = fd->read(buffer.data(), buffer.size());
-
- for (size_t i = 0; i < number_of_read_bytes; ++i)
- {
- kstd::print("{:02x} ", static_cast<uint8_t>(buffer[i]));
-
- if ((i + 1) % 16 == 0)
- {
- kstd::println("");
- }
- }
- kstd::println("---");
+ kstd::println("read bytes: {}", number_of_read_bytes);
+ kstd::println("buffer: {::#04x}", buffer);
// write half of the file new
auto const value1 = std::byte{0xAA};
@@ -61,7 +64,7 @@ auto run_test_code() -> void
fd_table.remove_file(fd_index);
// use: read four bytes again -> two old bytes two new bytes
- filesystem::open_file_description ofd1(dev_file);
+ auto ofd1 = kstd::make_shared<filesystem::open_file_description>(dev_node);
fd_index = fd_table.add_file(ofd1);
auto fd1 = fd_table.get_file(fd_index);
@@ -72,17 +75,93 @@ auto run_test_code() -> void
kstd::vector<std::byte> buffer1{4};
number_of_read_bytes = fd1->read(buffer1.data(), buffer1.size());
+ kstd::println("read bytes: {}", number_of_read_bytes);
+ kstd::println("buffer: {::#04x}", buffer1);
+}
+
+auto test_device_with_vfs() -> void
+{
+ // TODO BA-FS26
+
+ auto vfs = filesystem::vfs::get();
+ auto ofd = vfs.open("/dev/ram0");
+ if (!ofd)
+ {
+ kstd::os::panic("test code failed");
+ }
+
+ auto fd_table = filesystem::file_descriptor_table::get();
+ auto fd = fd_table.add_file(ofd);
+ kstd::vector<std::byte> buffer{2};
+ auto file = fd_table.get_file(fd);
+ if (!file)
+ {
+ kstd::os::panic("test code failed");
+ }
+
+ auto number_of_read_bytes = file->read(buffer.data(), buffer.size());
+ kstd::println("read bytes: {}", number_of_read_bytes);
+ kstd::println("buffer: {::#04x}", buffer);
+}
+
+auto test_file_lookup() -> void
+{
+ // TODO BA-FS26 implement a more complete test with multiple files and directories and mounts etc.
+
+ auto vfs = filesystem::vfs::get();
+ auto storage_mgmt = devices::storage::storage_management::get();
+
+ auto ofd1 = vfs.open("/a/b/c");
+ auto ofd2 = vfs.open("/dev/ram0");
+ auto ofd3 = vfs.open("/a/d/e");
+ if (!ofd1 || !ofd2 || !ofd3)
+ {
+ kstd::os::panic("test code failed");
+ }
- for (size_t i = 0; i < number_of_read_bytes; ++i)
+ if (auto ofd4 = vfs.open("/dev/xxx"))
+ {
+ kstd::os::panic("test code failed");
+ }
+
+ auto new_filesystem = kstd::make_shared<filesystem::ext2::ext2_filesystem>();
+ auto device = storage_mgmt.device_by_major_minor(1, 16);
+ new_filesystem->mount(device);
+ if (vfs.do_mount("/a/b", new_filesystem) != 0)
+ {
+ kstd::os::panic("test code failed");
+ }
+ auto ofd5 = vfs.open("/a/b/c");
+ if (!ofd5)
{
- kstd::print("{:02x} ", static_cast<uint8_t>(buffer1[i]));
+ kstd::os::panic("test code failed");
+ }
- if ((i + 1) % 16 == 0)
- {
- kstd::println("");
- }
+ if (auto ofd6 = vfs.open("x/y/z"))
+ {
+ kstd::os::panic("test code failed");
}
- kstd::println("---");
+}
+
+auto run_test_code() -> void
+{
+ kstd::println("[TEST] Running test code...");
+
+ kstd::println("[TEST] device names");
+ test_device_names();
+ kstd::println("---------------------------------");
+
+ kstd::println("[TEST] file description manually");
+ test_file_description_manually();
+ kstd::println("---------------------------------");
+
+ kstd::println("[TEST] device with VFS");
+ test_device_with_vfs();
+ kstd::println("---------------------------------");
+
+ kstd::println("[TEST] file lookup");
+ test_file_lookup();
+ kstd::println("---------------------------------");
}
auto main() -> int
@@ -108,6 +187,7 @@ auto main() -> int
kstd::println("[OS] Virtual filesystem initialized.");
run_test_code();
+ kstd::println("[TEST] All tests completed.");
kapi::system::panic("Returning from kernel main!");
}
diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt
index 06543ab..d4f415f 100644
--- a/libs/kstd/CMakeLists.txt
+++ b/libs/kstd/CMakeLists.txt
@@ -46,6 +46,7 @@ if(NOT CMAKE_CROSSCOMPILING)
add_executable("kstd_tests"
"tests/src/vector.cpp"
"tests/src/os_panic.cpp"
+ "tests/src/string.cpp"
)
target_include_directories("kstd_tests" PRIVATE
diff --git a/libs/kstd/include/kstd/bits/shared_ptr.hpp b/libs/kstd/include/kstd/bits/shared_ptr.hpp
index 6bce83f..8930095 100644
--- a/libs/kstd/include/kstd/bits/shared_ptr.hpp
+++ b/libs/kstd/include/kstd/bits/shared_ptr.hpp
@@ -10,10 +10,200 @@
namespace kstd
{
+ /**
+ * @brief Control block for shared_ptr and weak_ptr. This control block contains the reference counts for shared
+ * ownership and weak ownership. The shared_count tracks the number of shared_ptr instances that own the managed
+ * object, while the weak_count tracks the number of weak_ptr instances that reference the managed object.
+ * The weak_count is needed to determine when it is safe to delete the shared_control_block itself
+ */
+ struct shared_control_block
+ {
+ std::atomic<std::size_t> shared_count;
+ std::atomic<std::size_t> weak_count;
+
+ explicit shared_control_block(std::size_t shared = 1, std::size_t weak = 0)
+ : shared_count(shared)
+ , weak_count(weak)
+ {}
+ };
+
template<typename T>
struct shared_ptr;
/**
+ * @brief weak_ptr is a smart pointer that holds a non-owning weak reference to an object that is managed by
+ * shared_ptr. It must be converted to shared_ptr in to be able to access the referenced object. A weak_ptr is created
+ * as a copy of a shared_ptr, or as a copy of another weak_ptr. A weak_ptr is typically used to break circular
+ * references between shared_ptr to avoid memory leaks. A weak_ptr does not contribute to the reference count of the
+ * object, and it does not manage the lifetime of the object. If the object managed by shared_ptr is destroyed, the
+ * weak_ptr becomes expired and cannot be used to access the object anymore.
+ */
+ template<typename T>
+ struct weak_ptr
+ {
+ template<typename U>
+ friend struct shared_ptr;
+
+ /**
+ * @brief Constructs a null weak_ptr.
+ */
+ weak_ptr() noexcept
+ : pointer(nullptr)
+ , control(nullptr)
+ {}
+
+ template<typename U>
+ requires(std::is_convertible_v<U *, T *>)
+ weak_ptr(shared_ptr<U> const & other)
+ : pointer(other.pointer)
+ , control(other.control)
+ {
+ if (control != nullptr)
+ {
+ ++(control->weak_count);
+ }
+ }
+
+ /**
+ * @brief Copy constructor. Constructs a weak_ptr which shares ownership of the object managed by other.
+ */
+ weak_ptr(weak_ptr const & other)
+ : pointer(other.pointer)
+ , control(other.control)
+ {
+ if (control != nullptr)
+ {
+ ++(control->weak_count);
+ }
+ }
+
+ /**
+ * @brief Move constructor. Constructs a weak_ptr which takes ownership of the object managed by other.
+ */
+ weak_ptr(weak_ptr && other) noexcept
+ : pointer(other.pointer)
+ , control(other.control)
+ {
+ other.pointer = nullptr;
+ other.control = nullptr;
+ }
+
+ /**
+ * @brief Assignment operator. Assigns the weak_ptr to another weak_ptr.
+ */
+ auto operator=(weak_ptr const & other) -> weak_ptr &
+ {
+ if (this != &other)
+ {
+ cleanup();
+ pointer = other.pointer;
+ control = other.control;
+ if (control != nullptr)
+ {
+ ++(control->weak_count);
+ }
+ }
+
+ return *this;
+ }
+
+ /**
+ * @brief Move assignment operator. Move-assigns a weak_ptr from other.
+ */
+ auto operator=(weak_ptr && other) noexcept -> weak_ptr &
+ {
+ if (this != &other)
+ {
+ cleanup();
+ pointer = other.pointer;
+ control = other.control;
+ other.pointer = nullptr;
+ other.control = nullptr;
+ }
+
+ return *this;
+ }
+
+ /**
+ * @brief Destructor. Cleans up resources if necessary.
+ */
+ ~weak_ptr()
+ {
+ cleanup();
+ }
+
+ /**
+ * @brief Returns a shared_ptr that shares ownership of the managed object if the managed object still exists, or an
+ * empty shared_ptr otherwise.
+ */
+ [[nodiscard]] auto lock() const -> shared_ptr<T>
+ {
+ return shared_ptr<T>(*this);
+ }
+
+ private:
+ auto cleanup() -> void
+ {
+ if (control != nullptr)
+ {
+ if (--(control->weak_count) == 0 && control->shared_count == 0)
+ {
+ delete control;
+ }
+ }
+ }
+
+ T * pointer;
+ shared_control_block * control;
+ };
+
+ /**
+ * @brief enable_shared_from_this is a base class that allows an object that is currently managed by a shared_ptr to
+ * create additional shared_ptr instances that share ownership of the same object. This is usefl when you want to
+ * create shared_ptr instances in a member function of the object.
+ *
+ * @tparam T The type of the managed object.
+ */
+ template<typename T>
+ struct enable_shared_from_this
+ {
+ template<typename U>
+ friend struct shared_ptr;
+
+ friend T;
+
+ public:
+ /**
+ * @brief Returns a shared_ptr that shares ownership of *this.
+ */
+ auto shared_from_this() -> shared_ptr<T>
+ {
+ return shared_ptr<T>(weak_this);
+ }
+
+ /**
+ * @brief Returns a shared_ptr that shares ownership of *this.
+ */
+ auto shared_from_this() const -> shared_ptr<T const>
+ {
+ return shared_ptr<T const>(weak_this);
+ }
+
+ private:
+ enable_shared_from_this() = default;
+ enable_shared_from_this(enable_shared_from_this const &) = default;
+ auto operator=(enable_shared_from_this const &) -> enable_shared_from_this & = default;
+ ~enable_shared_from_this() = default;
+
+ void internal_assign_ptr(shared_ptr<T> const & ptr) const
+ {
+ weak_this = ptr;
+ }
+
+ mutable weak_ptr<T> weak_this{}; ///< Weak pointer to the object, used for shared_from_this functionality.
+ };
+
+ /**
* @brief Shared_pointer is a smart pointer that retains shared ownership of an object through a pointer. Several
* shared_ptr objects may own the same object. The object is destroyed and its memory deallocated when either of
* the following happens: the last remaining shared_ptr owning the object is destroyed; the last remaining
@@ -31,12 +221,23 @@ namespace kstd
template<typename U>
friend struct shared_ptr;
+ template<typename U>
+ friend struct weak_ptr;
+
+ /**
+ * @brief Construct an empty shared_ptr.
+ */
+ shared_ptr() noexcept
+ : pointer(nullptr)
+ , control(nullptr)
+ {}
+
/**
* @brief Construct an empty shared_ptr from nullptr.
*/
shared_ptr(std::nullptr_t) noexcept
: pointer(nullptr)
- , ref_count(nullptr)
+ , control(nullptr)
{}
/**
@@ -44,11 +245,33 @@ namespace kstd
*
* @param pointer A pointer to an object to manage (default is nullptr).
*/
- explicit shared_ptr(T * pointer = nullptr)
+ template<typename U>
+ requires(std::is_convertible_v<U *, T *>)
+ explicit shared_ptr(U * pointer = nullptr)
: pointer(pointer)
- , ref_count(pointer != nullptr ? new std::atomic<std::size_t>(1) : nullptr)
+ , control(pointer != nullptr ? new shared_control_block() : nullptr)
+ {
+ assign_enable_shared_from_this(pointer);
+ }
+
+ /**
+ * @brief Constructor a shared_ptr from a weak_ptr. If other is not expired, constructs a shared_ptr which shares
+ * ownership of the object managed by other. Otherwise, constructs an empty shared_ptr.
+ *
+ * @param other The weak_ptr to construct from.
+ */
+ template<typename U>
+ requires(std::is_convertible_v<U *, T *>)
+ explicit shared_ptr(weak_ptr<U> const & other)
+ : pointer(nullptr)
+ , control(nullptr)
{
- // Nothing to do.
+ if (other.control != nullptr && other.control->shared_count != 0)
+ {
+ pointer = other.pointer;
+ control = other.control;
+ ++(control->shared_count);
+ }
}
/**
@@ -58,11 +281,11 @@ namespace kstd
*/
shared_ptr(shared_ptr const & other)
: pointer(other.pointer)
- , ref_count(other.ref_count)
+ , control(other.control)
{
- if (ref_count != nullptr)
+ if (control != nullptr)
{
- ++(*ref_count);
+ ++(control->shared_count);
}
}
@@ -76,11 +299,11 @@ namespace kstd
requires(std::is_convertible_v<U *, T *>)
shared_ptr(shared_ptr<U> const & other)
: pointer(other.pointer)
- , ref_count(other.ref_count)
+ , control(other.control)
{
- if (ref_count != nullptr)
+ if (control != nullptr)
{
- ++(*ref_count);
+ ++(control->shared_count);
}
}
@@ -91,10 +314,10 @@ namespace kstd
*/
shared_ptr(shared_ptr && other) noexcept
: pointer(other.pointer)
- , ref_count(other.ref_count)
+ , control(other.control)
{
other.pointer = nullptr;
- other.ref_count = nullptr;
+ other.control = nullptr;
}
/**
@@ -107,10 +330,10 @@ namespace kstd
requires(std::is_convertible_v<U *, T *>)
shared_ptr(shared_ptr<U> && other) noexcept
: pointer(other.pointer)
- , ref_count(other.ref_count)
+ , control(other.control)
{
other.pointer = nullptr;
- other.ref_count = nullptr;
+ other.control = nullptr;
}
/**
@@ -127,11 +350,11 @@ namespace kstd
{
cleanup();
pointer = other.pointer;
- ref_count = other.ref_count;
+ control = other.control;
- if (ref_count != nullptr)
+ if (control != nullptr)
{
- ++(*ref_count);
+ ++(control->shared_count);
}
}
@@ -151,11 +374,11 @@ namespace kstd
{
cleanup();
pointer = other.pointer;
- ref_count = other.ref_count;
+ control = other.control;
- if (ref_count != nullptr)
+ if (control != nullptr)
{
- ++(*ref_count);
+ ++(control->shared_count);
}
return *this;
@@ -174,9 +397,9 @@ namespace kstd
{
cleanup();
pointer = other.pointer;
- ref_count = other.ref_count;
+ control = other.control;
other.pointer = nullptr;
- other.ref_count = nullptr;
+ other.control = nullptr;
}
return *this;
@@ -195,9 +418,9 @@ namespace kstd
{
cleanup();
pointer = other.pointer;
- ref_count = other.ref_count;
+ control = other.control;
other.pointer = nullptr;
- other.ref_count = nullptr;
+ other.control = nullptr;
return *this;
}
@@ -209,7 +432,7 @@ namespace kstd
{
cleanup();
pointer = nullptr;
- ref_count = nullptr;
+ control = nullptr;
return *this;
}
@@ -230,7 +453,8 @@ namespace kstd
{
cleanup();
pointer = ptr;
- ref_count = ptr != nullptr ? new std::atomic<std::size_t>(1) : nullptr;
+ control = ptr != nullptr ? new shared_control_block() : nullptr;
+ assign_enable_shared_from_this(ptr);
}
/**
@@ -242,7 +466,7 @@ namespace kstd
void swap(shared_ptr & other)
{
std::swap(pointer, other.pointer);
- std::swap(ref_count, other.ref_count);
+ std::swap(control, other.control);
}
/**
@@ -288,9 +512,9 @@ namespace kstd
*/
[[nodiscard]] auto use_count() const -> std::size_t
{
- if (ref_count != nullptr)
+ if (control != nullptr)
{
- return *ref_count;
+ return control->shared_count;
}
return 0;
@@ -322,26 +546,49 @@ namespace kstd
return ptr.pointer == nullptr;
}
+ private:
/**
- * @brief Defaulted three-way comparator operator.
+ * @brief If the candidate type inherits from enable_shared_from_this, assigns the internal weak pointer to this
+ * shared_ptr. This weak_ptr is used to implement shared_from_this functionality for the candidate type. If the
+ * candidate type does not inherit from enable_shared_from_this, this function does nothing.
+ *
+ * @tparam U The candidate type to check for enable_shared_from_this inheritance.
+ * @param candidate The candidate object to assign the internal weak pointer for.
*/
- [[nodiscard]] auto operator<=>(shared_ptr const & other) const = default;
+ template<typename U>
+ auto assign_enable_shared_from_this(U * candidate) -> void
+ {
+ if constexpr (requires(U * p, shared_ptr<T> const & sp) { p->internal_assign_ptr(sp); })
+ {
+ if (candidate != nullptr)
+ {
+ candidate->internal_assign_ptr(*this);
+ }
+ }
+ }
- private:
/**
* @brief Releases ownership and deletes the object if this was the last reference to the owned managed object.
*/
auto cleanup() -> void
{
- if (ref_count != nullptr && --(*ref_count) == 0)
+ if (control != nullptr)
{
- delete pointer;
- delete ref_count;
+ if (--(control->shared_count) == 0)
+ {
+ delete pointer;
+ pointer = nullptr;
+
+ if (control->weak_count == 0)
+ {
+ delete control;
+ }
+ }
}
}
- T * pointer; ///< The managed object.
- std::atomic<std::size_t> * ref_count; ///< Reference count.
+ T * pointer; ///< The managed object.
+ shared_control_block * control; ///< Shared control block.
};
/**
@@ -372,6 +619,30 @@ namespace kstd
{
return shared_ptr<T>(new T(std::forward<Args>(args)...));
}
+
+ /**
+ * @brief Equality operator for shared_ptr. Two shared_ptr instances are equal if they point to the same object
+ * @tparam T, U Types of the managed objects of the shared_ptr instances being compared.
+ * @param lhs, rhs The shared_ptr instances to compare.
+ * @return true if lhs and rhs point to the same object, false otherwise.
+ */
+ template<typename T, typename U>
+ [[nodiscard]] auto inline operator==(shared_ptr<T> const & lhs, shared_ptr<U> const & rhs) -> bool
+ {
+ return lhs.get() == rhs.get();
+ }
+
+ /**
+ * @brief Three-way comparison operator for shared_ptr. Compares the stored pointers of lhs and rhs using operator<=>.
+ * @tparam T, U Types of the managed objects of the shared_ptr instances being compared.
+ * @param lhs, rhs The shared_ptr instances to compare.
+ * @return The result of comparing the stored pointers of lhs and rhs using operator<=>
+ */
+ template<typename T, typename U>
+ [[nodiscard]] auto inline operator<=>(shared_ptr<T> const & lhs, shared_ptr<U> const & rhs)
+ {
+ return lhs.get() <=> rhs.get();
+ }
} // namespace kstd
#endif \ No newline at end of file
diff --git a/libs/kstd/include/kstd/string b/libs/kstd/include/kstd/string
new file mode 100644
index 0000000..075422e
--- /dev/null
+++ b/libs/kstd/include/kstd/string
@@ -0,0 +1,348 @@
+#ifndef KSTD_STRING_HPP
+#define KSTD_STRING_HPP
+
+#include <kstd/cstring>
+#include <kstd/os/error.hpp>
+#include <kstd/vector>
+
+#include <algorithm>
+#include <concepts>
+#include <cstddef>
+#include <string_view>
+
+namespace kstd
+{
+ /**
+ * @brief A simple string implementation that owns its data and provides basic operations.
+ */
+ struct string
+ {
+ //! The type of the characters contained in this string.
+ using value_type = char;
+ //! The type of the underlying storage used by this string.
+ using storage_type = kstd::vector<value_type>;
+ //! The type of all sizes used in and with this string.
+ using size_type = std::size_t;
+ //! The type of the difference between two iterators.
+ using difference_type = std::ptrdiff_t;
+ //! The type of references to single values in this string.
+ using reference = value_type &;
+ //! The type of references to constant single values in this string.
+ using const_reference = value_type const &;
+ //! The type of pointers to single values in this string.
+ using pointer = value_type *;
+ //! The type of pointers to constant single values in this string.
+ using const_pointer = value_type const *;
+ //! The type of iterators into this string.
+ using iterator = pointer;
+ //! The type of constant iterators into this string.
+ using const_iterator = const_pointer;
+
+ /**
+ * @brief Constructs an empty null-terminated string.
+ */
+ string()
+ : m_storage{value_type{'\0'}}
+ {}
+
+ /**
+ * @brief Constructs a string from a string view by copying the characters into owned storage.
+ * @param view The string view to copy the characters from.
+ */
+ string(std::string_view view)
+ : string()
+ {
+ append(view);
+ }
+
+ /**
+ * @brief Constructs a string from a null-terminated C-style string by copying the characters into owned storage.
+ * @param c_str The null-terminated C-style string to copy.
+ */
+ string(char const * c_str)
+ : string()
+ {
+ if (c_str != nullptr)
+ {
+ append(std::string_view{c_str});
+ }
+ }
+
+ /**
+ * @brief Constructs a string containing a single character.
+ * @param c The character to copy.
+ */
+ string(value_type c)
+ : string()
+ {
+ push_back(c);
+ }
+
+ /**
+ * @brief Constructs a string by copying another string.
+ * @param other The string to copy.
+ */
+ constexpr string(string const & other)
+ : m_storage{other.m_storage}
+ {}
+
+ /**
+ * @brief Destructs the string.
+ */
+ constexpr ~string() = default;
+
+ /**
+ * @brief Assigns the value of another string to this string.
+ * @param other The string to assign from.
+ * @return A reference to this string.
+ */
+ constexpr auto operator=(string const & other) -> string & = default;
+
+ /**
+ * @brief Returns the number of characters in this string, not including the null terminator.
+ */
+ [[nodiscard]] constexpr auto size() const noexcept -> size_type
+ {
+ return m_storage.empty() ? 0 : m_storage.size() - 1;
+ }
+
+ /**
+ * @brief Checks if this string is empty, not including the null terminator.
+ */
+ [[nodiscard]] constexpr auto empty() const noexcept -> bool
+ {
+ return size() == 0;
+ }
+
+ /**
+ * @brief Clears the content of the string, resulting in an empty string.
+ * The string remains null-terminated after this operation.
+ */
+ constexpr auto clear() -> void
+ {
+ m_storage.clear();
+ m_storage.push_back(value_type{'\0'});
+ }
+
+ //! Get a pointer to the underlying storage of the string
+ [[nodiscard]] constexpr auto data() noexcept -> pointer
+ {
+ return m_storage.data();
+ }
+
+ //! Get a const pointer to the underlying storage of the string
+ [[nodiscard]] constexpr auto data() const noexcept -> const_pointer
+ {
+ return m_storage.data();
+ }
+
+ //! Get a const pointer to the underlying storage of the string
+ [[nodiscard]] constexpr auto c_str() const noexcept -> const_pointer
+ {
+ return data();
+ }
+
+ //! Get an iterator to the beginning of the string
+ [[nodiscard]] constexpr auto begin() noexcept -> iterator
+ {
+ return data();
+ }
+
+ //! Get an const iterator to the beginning of the string
+ [[nodiscard]] constexpr auto begin() const noexcept -> const_iterator
+ {
+ return data();
+ }
+
+ //! Get an const iterator to the beginning of the string
+ [[nodiscard]] constexpr auto cbegin() const noexcept -> const_iterator
+ {
+ return begin();
+ }
+
+ //! Get an iterator to the end of the string
+ [[nodiscard]] constexpr auto end() noexcept -> iterator
+ {
+ return data() + size();
+ }
+
+ //! Get an const iterator to the end of the string
+ [[nodiscard]] constexpr auto end() const noexcept -> const_iterator
+ {
+ return data() + size();
+ }
+
+ //! Get an const iterator to the end of the string
+ [[nodiscard]] constexpr auto cend() const noexcept -> const_iterator
+ {
+ return end();
+ }
+
+ //! Get a reference to the first character of the string
+ [[nodiscard]] constexpr auto front() -> reference
+ {
+ return m_storage.front();
+ }
+
+ //! Get a const reference to the first character of the string
+ [[nodiscard]] constexpr auto front() const -> const_reference
+ {
+ return m_storage.front();
+ }
+
+ //! Get a reference to the last character of the string
+ [[nodiscard]] constexpr auto back() -> reference
+ {
+ return m_storage[size() - 1];
+ }
+
+ //! Get a const reference to the last character of the string
+ [[nodiscard]] constexpr auto back() const -> const_reference
+ {
+ return m_storage[size() - 1];
+ }
+
+ /**
+ * @brief Appends a character to the end of the string.
+ * @param ch The character to append.
+ */
+ constexpr auto push_back(value_type ch) -> void
+ {
+ m_storage.back() = ch;
+ m_storage.push_back(value_type{'\0'});
+ }
+
+ /**
+ * @brief Appends a string view to the end of the string by copying the characters into owned storage.
+ * @param view The string view to append.
+ * @return A reference to this string.
+ */
+ constexpr auto append(std::string_view view) -> string &
+ {
+ if (!view.empty())
+ {
+ std::ranges::for_each(view, [this](auto const ch) { push_back(ch); });
+ }
+
+ return *this;
+ }
+
+ /**
+ * @brief Appends another string to the end of this string by copying the characters into owned storage.
+ * @param other The string to append.
+ * @return A reference to this string.
+ */
+ constexpr auto append(string const & other) -> string &
+ {
+ return append(other.view());
+ }
+
+ /**
+ * @brief Appends another string to the end of this string by copying the characters into owned storage.
+ * @param other The string to append.
+ * @return A reference to this string.
+ */
+ constexpr auto operator+=(string const & other) -> string &
+ {
+ return append(other);
+ }
+
+ /**
+ * @brief Appends a character to the end of the string.
+ * @param ch The character to append.
+ * @return A reference to this string.
+ */
+ constexpr auto operator+=(value_type ch) -> string &
+ {
+ push_back(ch);
+ return *this;
+ }
+
+ /**
+ * @brief Returns a string view of this string, which is a non-owning view into the characters of this string.
+ */
+ [[nodiscard]] constexpr auto view() const noexcept -> std::string_view
+ {
+ return std::string_view{data(), size()};
+ }
+
+ private:
+ //! The underlying storage of the string, which owns the characters and ensures null-termination.
+ storage_type m_storage{};
+ };
+
+ /**
+ * @brief Concatenates a strings and a character and returns the result as a new string.
+ * @param lhs The string to concatenate.
+ * @param rhs The string to concatenate.
+ * @return A new string that is the result of concatenating @p lhs and @p rhs.
+ */
+ [[nodiscard]] constexpr auto inline operator+(string const & lhs, string const & rhs) -> string
+ {
+ string result{lhs};
+ result += rhs;
+ return result;
+ }
+
+ /**
+ * @brief Converts an unsigned integer to a string by converting each digit to the corresponding character and
+ * concatenating them.
+ * @tparam N The type of the unsigned integer to convert.
+ * @param value The unsigned integer to convert.
+ * @return A string representation of the given unsigned integer.
+ */
+ template<typename N>
+ requires std::unsigned_integral<N>
+ [[nodiscard]] constexpr auto inline to_string(N value) -> string
+ {
+ if (value == 0)
+ {
+ return "0";
+ }
+
+ string result;
+
+ while (value > 0)
+ {
+ char const digit = '0' + (value % 10);
+ result.push_back(digit);
+ value /= 10;
+ }
+
+ std::reverse(result.begin(), result.end());
+ return result;
+ }
+
+ [[nodiscard]] constexpr auto inline operator==(string const & lhs, string const & rhs) -> bool
+ {
+ return lhs.view() == rhs.view();
+ }
+
+ [[nodiscard]] constexpr auto inline operator!=(string const & lhs, string const & rhs) -> bool
+ {
+ return !(lhs == rhs);
+ }
+
+ [[nodiscard]] constexpr auto inline operator==(string const & lhs, std::string_view rhs) -> bool
+ {
+ return lhs.view() == rhs;
+ }
+
+ [[nodiscard]] constexpr auto inline operator!=(string const & lhs, std::string_view rhs) -> bool
+ {
+ return !(lhs == rhs);
+ }
+
+ [[nodiscard]] constexpr auto inline operator==(std::string_view lhs, string const & rhs) -> bool
+ {
+ return lhs == rhs.view();
+ }
+
+ [[nodiscard]] constexpr auto inline operator!=(std::string_view lhs, string const & rhs) -> bool
+ {
+ return !(lhs == rhs);
+ }
+
+} // namespace kstd
+
+#endif \ No newline at end of file
diff --git a/libs/kstd/tests/src/string.cpp b/libs/kstd/tests/src/string.cpp
new file mode 100644
index 0000000..43e9a6b
--- /dev/null
+++ b/libs/kstd/tests/src/string.cpp
@@ -0,0 +1,445 @@
+#include <kstd/string>
+
+#include <catch2/catch_test_macros.hpp>
+
+#include <algorithm>
+#include <ranges>
+#include <string_view>
+
+SCENARIO("String initialization and construction", "[string]")
+{
+ GIVEN("Nothing")
+ {
+ WHEN("constructing an empty string")
+ {
+ auto str = kstd::string{};
+
+ THEN("the size is zero and therefore the string is empty")
+ {
+ REQUIRE(str.empty());
+ REQUIRE(str.size() == 0);
+ }
+
+ THEN("the string is empty")
+ {
+ REQUIRE(str.view() == std::string_view{});
+ }
+ }
+ }
+
+ GIVEN("A string view")
+ {
+ auto view = std::string_view{"Blub Blub"};
+
+ WHEN("constructing a string from string_view")
+ {
+ auto str = kstd::string{view};
+
+ THEN("the string is not empty and has the same size as the view")
+ {
+ REQUIRE(!str.empty());
+ REQUIRE(str.size() == view.size());
+ }
+
+ THEN("the string contains the same characters as the view")
+ {
+ REQUIRE(str.view() == view);
+ }
+ }
+ }
+
+ GIVEN("A C-style string")
+ {
+ auto c_str = "Blub Blub";
+
+ WHEN("constructing a string from the C-style string")
+ {
+ auto str = kstd::string{c_str};
+
+ THEN("the string is not empty and has the same size as the C-style string")
+ {
+ REQUIRE(!str.empty());
+ REQUIRE(str.size() == strlen(c_str));
+ }
+
+ THEN("the string contains the same characters as the C-style string")
+ {
+ REQUIRE(str.view() == c_str);
+ }
+ }
+ }
+
+ GIVEN("A character")
+ {
+ auto ch = 'x';
+
+ WHEN("constructing a string from the character")
+ {
+ auto str = kstd::string{ch};
+
+ THEN("the string is not empty and has size 1")
+ {
+ REQUIRE(!str.empty());
+ REQUIRE(str.size() == 1);
+ }
+
+ THEN("the string contains the same character as the given character")
+ {
+ REQUIRE(str.view() == std::string_view{&ch, 1});
+ }
+ }
+ }
+
+ GIVEN("Another string")
+ {
+ auto other = kstd::string{"Blub Blub"};
+
+ WHEN("copy constructing a new string")
+ {
+ auto str = kstd::string{other};
+
+ THEN("the new string contains the same characters as the original")
+ {
+ REQUIRE(str.view() == other.view());
+ }
+ }
+
+ auto str = kstd::string{"Blub"};
+
+ WHEN("copy assigning another string")
+ {
+ auto other = kstd::string{"Blub Blub"};
+ str = other;
+
+ THEN("the string contains the same characters as the assigned string")
+ {
+ REQUIRE(str.view() == other.view());
+ }
+ }
+ }
+
+ GIVEN("A string")
+ {
+ auto str = kstd::string{"Hello"};
+
+ WHEN("copy assigning a string view")
+ {
+ auto view = std::string_view{"Hello, world!"};
+ str = view;
+
+ THEN("the string contains the same characters as the assigned view")
+ {
+ REQUIRE(str.view() == view);
+ }
+ }
+ }
+
+ GIVEN("A string")
+ {
+ auto str = kstd::string{"Hello"};
+
+ WHEN("copy assigning a C-style string")
+ {
+ auto c_str = "Hello, world!";
+ str = c_str;
+
+ THEN("the string contains the same characters as the assigned C-style string")
+ {
+ REQUIRE(str.view() == c_str);
+ }
+ }
+ }
+}
+
+SCENARIO("String concatenation", "[string]")
+{
+ GIVEN("Two strings")
+ {
+ auto str1 = kstd::string{"Blub"};
+ auto str2 = kstd::string{" Blub"};
+
+ WHEN("appending the second string to the first string")
+ {
+ str1.append(str2);
+
+ THEN("the first string contains the characters of both strings concatenated")
+ {
+ REQUIRE(str1.view() == "Blub Blub");
+ }
+
+ THEN("the size of the first string is the sum of the sizes of both strings")
+ {
+ REQUIRE(str1.size() == str2.size() + 4);
+ }
+ }
+
+ WHEN("using operator+= to append the second string to the first string")
+ {
+ str1 += str2;
+
+ THEN("the first string contains the characters of both strings concatenated")
+ {
+ REQUIRE(str1.view() == "Blub Blub");
+ }
+
+ THEN("the size of the first string is the sum of the sizes of both strings")
+ {
+ REQUIRE(str1.size() == str2.size() + 4);
+ }
+ }
+
+ WHEN("using operator+ to concatenate the two strings into a new string")
+ {
+ auto str3 = str1 + str2;
+
+ THEN("the new string contains the characters of both strings concatenated")
+ {
+ REQUIRE(str3.view() == "Blub Blub");
+ }
+
+ THEN("the size of the new string is the sum of the sizes of both strings")
+ {
+ REQUIRE(str3.size() == str1.size() + str2.size());
+ }
+ }
+ }
+
+ GIVEN("A string and a string view")
+ {
+ auto str = kstd::string{"Blub"};
+ auto view = std::string_view{" Blub"};
+
+ WHEN("appending the string view to the string")
+ {
+ str.append(view);
+
+ THEN("the string contains the characters of both the original string and the appended view concatenated")
+ {
+ REQUIRE(str.view() == "Blub Blub");
+ }
+
+ THEN("the size of the string is the sum of the sizes of the original string and the appended view")
+ {
+ REQUIRE(str.size() == view.size() + 4);
+ }
+ }
+ }
+
+ GIVEN("A string and a character")
+ {
+ auto str = kstd::string{"Blub"};
+ auto ch = '!';
+
+ WHEN("appending the character to the string")
+ {
+ str.push_back(ch);
+
+ THEN("the string contains the original characters followed by the appended character")
+ {
+ REQUIRE(str.view() == "Blub!");
+ }
+
+ THEN("the size of the string is one more than the original size")
+ {
+ REQUIRE(str.size() == 5);
+ }
+ }
+
+ WHEN("using operator+= to append the character to the string")
+ {
+ str += ch;
+
+ THEN("the string contains the original characters followed by the appended character")
+ {
+ REQUIRE(str.view() == "Blub!");
+ }
+
+ THEN("the size of the string is one more than the original size")
+ {
+ REQUIRE(str.size() == 5);
+ }
+ }
+ }
+}
+
+SCENARIO("String conversion and comparison", "[string]")
+{
+ GIVEN("An unsigned integer")
+ {
+ auto value1 = 12345u;
+ auto value2 = 0u;
+
+ WHEN("converting the unsigned integer to a string")
+ {
+ auto str1 = kstd::to_string(value1);
+ auto str2 = kstd::to_string(value2);
+
+ THEN("the string contains the decimal representation of the unsigned integer")
+ {
+ REQUIRE(str1.view() == "12345");
+ REQUIRE(str2.view() == "0");
+ }
+ }
+ }
+
+ GIVEN("Two strings with the same characters")
+ {
+ auto str1 = kstd::string{"Blub Blub"};
+ auto str2 = kstd::string{"Blub Blub"};
+
+ THEN("the strings are equal")
+ {
+ REQUIRE(str1 == str2);
+ }
+
+ THEN("the strings are not unequal")
+ {
+ REQUIRE(!(str1 != str2));
+ }
+ }
+
+ GIVEN("A string and a string view with the same characters")
+ {
+ auto str = kstd::string{"Blub Blub"};
+ auto view = std::string_view{"Blub Blub"};
+
+ THEN("the string and the string view are equal")
+ {
+ REQUIRE(str == view);
+ REQUIRE(view == str);
+ }
+
+ THEN("the string and the string view are not unequal")
+ {
+ REQUIRE(!(str != view));
+ REQUIRE(!(view != str));
+ }
+ }
+}
+
+SCENARIO("String clearing", "[string]")
+{
+ GIVEN("A non-empty string")
+ {
+ auto str = kstd::string{"Blub Blub"};
+
+ WHEN("clearing the string")
+ {
+ str.clear();
+
+ THEN("the string is empty and has size zero")
+ {
+ REQUIRE(str.empty());
+ REQUIRE(str.size() == 0);
+ }
+
+ THEN("the string contains no characters")
+ {
+ REQUIRE(str.view() == std::string_view{});
+ }
+ }
+ }
+}
+
+SCENARIO("String iteration", "[string]")
+{
+ GIVEN("A string")
+ {
+ auto str = kstd::string{"Blub"};
+
+ WHEN("iterating over the characters of the string as string_view using a range-based for loop")
+ {
+ kstd::string result;
+
+ for (auto ch : str.view())
+ {
+ result.push_back(ch);
+ }
+
+ THEN("the iterated characters are the same as the characters in the string")
+ {
+ REQUIRE(result == str);
+ }
+ }
+
+ WHEN("using std::ranges::for_each to iterate over the characters of the string")
+ {
+ kstd::string result;
+
+ std::ranges::for_each(str, [&result](auto ch) { result.push_back(ch); });
+
+ THEN("the iterated characters are the same as the characters in the string")
+ {
+ REQUIRE(result == str);
+ }
+ }
+
+ WHEN("using front and back to access the first and last characters of the string")
+ {
+ THEN("front returns the first character of the string")
+ {
+ REQUIRE(str.front() == 'B');
+ }
+
+ THEN("back returns the last character of the string")
+ {
+ REQUIRE(str.back() == 'b');
+ }
+ }
+ }
+
+ GIVEN("A const string")
+ {
+ auto const str = kstd::string{"Blub"};
+
+ WHEN("iterating over the characters of the string as string_view using a range-based for loop")
+ {
+ kstd::string result;
+
+ for (auto ch : str.view())
+ {
+ result.push_back(ch);
+ }
+
+ THEN("the iterated characters are the same as the characters in the string")
+ {
+ REQUIRE(result == str.view());
+ }
+ }
+
+ WHEN("using front and back to access the first and last characters of the string")
+ {
+ THEN("front returns the first character of the string")
+ {
+ REQUIRE(str.front() == 'B');
+ }
+
+ THEN("back returns the last character of the string")
+ {
+ REQUIRE(str.back() == 'b');
+ }
+ }
+ }
+
+ GIVEN("An empty string")
+ {
+ auto str = kstd::string{};
+
+ WHEN("iterating over the characters of an empty string")
+ {
+ kstd::string result;
+
+ for (auto ch : str.view())
+ {
+ result.push_back(ch);
+ }
+
+ THEN("no characters are iterated and the result is an empty string")
+ {
+ REQUIRE(result.empty());
+ REQUIRE(result.size() == 0);
+ REQUIRE(result.view() == std::string_view{});
+ }
+ }
+ }
+}