From 5658e274451652cd396263e5d5754f3ee5aba986 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Fri, 6 Mar 2026 20:17:23 +0000 Subject: [PATCH] lib: fs: add library for implementing a filesystem service --- lib/libfs/CMakeLists.txt | 29 ++ lib/libfs/allocator.c | 35 ++ lib/libfs/btree.c | 691 ++++++++++++++++++++++++++++++ lib/libfs/btree.h | 475 ++++++++++++++++++++ lib/libfs/context.c | 86 ++++ lib/libfs/file.h | 17 + lib/libfs/include/fs/allocator.h | 19 + lib/libfs/include/fs/context.h | 31 ++ lib/libfs/include/fs/dentry.h | 18 + lib/libfs/include/fs/file.h | 15 + lib/libfs/include/fs/inode.h | 17 + lib/libfs/include/fs/superblock.h | 20 + lib/libfs/interface.h | 21 + lib/libfs/interface/open.c | 21 + lib/libfs/superblock.c | 11 + 15 files changed, 1506 insertions(+) create mode 100644 lib/libfs/CMakeLists.txt create mode 100644 lib/libfs/allocator.c create mode 100644 lib/libfs/btree.c create mode 100644 lib/libfs/btree.h create mode 100644 lib/libfs/context.c create mode 100644 lib/libfs/file.h create mode 100644 lib/libfs/include/fs/allocator.h create mode 100644 lib/libfs/include/fs/context.h create mode 100644 lib/libfs/include/fs/dentry.h create mode 100644 lib/libfs/include/fs/file.h create mode 100644 lib/libfs/include/fs/inode.h create mode 100644 lib/libfs/include/fs/superblock.h create mode 100644 lib/libfs/interface.h create mode 100644 lib/libfs/interface/open.c create mode 100644 lib/libfs/superblock.c diff --git a/lib/libfs/CMakeLists.txt b/lib/libfs/CMakeLists.txt new file mode 100644 index 0000000..8753962 --- /dev/null +++ b/lib/libfs/CMakeLists.txt @@ -0,0 +1,29 @@ +file(GLOB sources + ${CMAKE_CURRENT_SOURCE_DIR}/*.c + ${CMAKE_CURRENT_SOURCE_DIR}/interface/*.c) +file(GLOB headers + ${CMAKE_CURRENT_SOURCE_DIR}/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/fs/*.h) + +set(public_include_dirs + ${CMAKE_CURRENT_SOURCE_DIR}/include) + +rosetta_add_library( + NAME libfs SHARED STATIC + PUBLIC_INCLUDE_DIRS ${public_include_dirs} + SOURCES ${sources} + HEADERS ${headers}) + +sysroot_add_library( + NAME libfs + HEADER_DIR /usr/include + LIB_DIR /usr/lib) +sysroot_add_library( + NAME libfs-static + HEADER_DIR /usr/include + LIB_DIR /usr/lib) + +target_link_libraries(libfs libmango interface::fs libc) +target_link_libraries(libfs-static libmango interface::fs libc-core) + +set_target_properties(libfs-static PROPERTIES POSITION_INDEPENDENT_CODE FALSE) diff --git a/lib/libfs/allocator.c b/lib/libfs/allocator.c new file mode 100644 index 0000000..dceae7c --- /dev/null +++ b/lib/libfs/allocator.c @@ -0,0 +1,35 @@ +#include + +void *fs_alloc(struct fs_allocator *alloc, size_t count) +{ + if (alloc->fs_alloc) { + return alloc->fs_alloc(alloc, count); + } + + return NULL; +} + +void *fs_calloc(struct fs_allocator *alloc, size_t count, size_t sz) +{ + if (alloc->fs_calloc) { + return alloc->fs_calloc(alloc, count, sz); + } + + return NULL; +} + +void *fs_realloc(struct fs_allocator *alloc, void *p, size_t count) +{ + if (alloc->fs_realloc) { + return alloc->fs_realloc(alloc, p, count); + } + + return NULL; +} + +void fs_free(struct fs_allocator *alloc, void *p) +{ + if (alloc->fs_free) { + alloc->fs_free(alloc, p); + } +} diff --git a/lib/libfs/btree.c b/lib/libfs/btree.c new file mode 100644 index 0000000..d999e96 --- /dev/null +++ b/lib/libfs/btree.c @@ -0,0 +1,691 @@ +/* + The Clear BSD License + + Copyright (c) 2023 Max Wash + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted (subject to the limitations in the disclaimer + below) provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + */ + +/* templated AVL binary tree implementation + + this file implements an extensible AVL binary tree data structure. + + the primary rule of an AVL binary tree is that for a given node N, + the heights of N's left and right subtrees can differ by at most 1. + + the height of a subtree is the length of the longest path between + the root of the subtree and a leaf node, including the root node itself. + + the height of a leaf node is 1. + + when a node is inserted into or deleted from the tree, this rule may + be broken, in which the tree must be rotated to restore the balance. + + no more than one rotation is required for any insert operations, + while multiple rotations may be required for a delete operation. + + there are four types of rotations that can be applied to a tree: + - left rotation + - right rotation + - double left rotations + - double right rotations + + by enforcing the balance rule, for a tree with n nodes, the worst-case + performance for insert, delete, and search operations is guaranteed + to be O(log n). + + this file intentionally excludes any kind of search function implementation. + it is up to the programmer to implement their own tree node type + using struct btree_node, and their own search function using struct btree. + this allows the programmer to define their own node types with complex + non-integer key types. btree.h contains a number of macros to help + define these functions. the macros do all the work, you just have to + provide a comparator function. +*/ + +#include "btree.h" + +#include + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +#define IS_LEFT_CHILD(p, c) ((p) && (c) && ((p)->b_left == (c))) +#define IS_RIGHT_CHILD(p, c) ((p) && (c) && ((p)->b_right == (c))) + +#define HAS_LEFT_CHILD(x) ((x) && ((x)->b_left)) +#define HAS_RIGHT_CHILD(x) ((x) && ((x)->b_right)) + +#define HAS_NO_CHILDREN(x) ((x) && (!(x)->b_left) && (!(x)->b_right)) +#define HAS_ONE_CHILD(x) \ + ((HAS_LEFT_CHILD(x) && !HAS_RIGHT_CHILD(x)) \ + || (!HAS_LEFT_CHILD(x) && HAS_RIGHT_CHILD(x))) +#define HAS_TWO_CHILDREN(x) (HAS_LEFT_CHILD(x) && HAS_RIGHT_CHILD(x)) + +#define HEIGHT(x) ((x) ? (x)->b_height : 0) + +static inline void update_height(struct btree_node *x) +{ + x->b_height = MAX(HEIGHT(x->b_left), HEIGHT((x->b_right))) + 1; +} + +static inline int bf(struct btree_node *x) +{ + int bf = 0; + + if (!x) { + return bf; + } + + if (x->b_right) { + bf += x->b_right->b_height; + } + + if (x->b_left) { + bf -= x->b_left->b_height; + } + + return bf; +} + +/* perform a left rotation on a subtree + + if you have a tree like this: + + Z + / \ + X . + / \ + . Y + / \ + . . + + and you perform a left rotation on node X, + you will get the following tree: + + Z + / \ + Y . + / \ + X . + / \ + . . + + note that this function does NOT update b_height for the rotated + nodes. it is up to you to call update_height_to_root(). +*/ +static void rotate_left(struct btree *tree, struct btree_node *x) +{ + struct btree_node *y = x->b_right; + + struct btree_node *p = x->b_parent; + + if (y->b_left) { + y->b_left->b_parent = x; + } + + x->b_right = y->b_left; + + if (!p) { + tree->b_root = y; + } else if (x == p->b_left) { + p->b_left = y; + } else { + p->b_right = y; + } + + x->b_parent = y; + y->b_left = x; + y->b_parent = p; +} + +static void update_height_to_root(struct btree_node *x) +{ + while (x) { + update_height(x); + x = x->b_parent; + } +} + +/* perform a right rotation on a subtree + + if you have a tree like this: + + Z + / \ + . X + / \ + Y . + / \ + . . + + and you perform a right rotation on node X, + you will get the following tree: + + Z + / \ + . Y + / \ + . X + / \ + . . + + note that this function does NOT update b_height for the rotated + nodes. it is up to you to call update_height_to_root(). +*/ +static void rotate_right(struct btree *tree, struct btree_node *y) +{ + struct btree_node *x = y->b_left; + + struct btree_node *p = y->b_parent; + + if (x->b_right) { + x->b_right->b_parent = y; + } + + y->b_left = x->b_right; + + if (!p) { + tree->b_root = x; + } else if (y == p->b_left) { + p->b_left = x; + } else { + p->b_right = x; + } + + y->b_parent = x; + x->b_right = y; + x->b_parent = p; +} + +/* for a given node Z, perform a right rotation on Z's right child, + followed by a left rotation on Z itself. + + if you have a tree like this: + + Z + / \ + . X + / \ + Y . + / \ + . . + + and you perform a double-left rotation on node Z, + you will get the following tree: + + Y + / \ + / \ + Z X + / \ / \ + . . . . + + note that, unlike rotate_left and rotate_right, this function + DOES update b_height for the rotated nodes (since it needs to be + done in a certain order). +*/ +static void rotate_double_left(struct btree *tree, struct btree_node *z) +{ + struct btree_node *x = z->b_right; + struct btree_node *y = x->b_left; + + rotate_right(tree, x); + rotate_left(tree, z); + + update_height(z); + update_height(x); + + while (y) { + update_height(y); + y = y->b_parent; + } +} + +/* for a given node Z, perform a left rotation on Z's left child, + followed by a right rotation on Z itself. + + if you have a tree like this: + + Z + / \ + X . + / \ + . Y + / \ + . . + + and you perform a double-right rotation on node Z, + you will get the following tree: + + Y + / \ + / \ + X Z + / \ / \ + . . . . + + note that, unlike rotate_left and rotate_right, this function + DOES update b_height for the rotated nodes (since it needs to be + done in a certain order). +*/ +static void rotate_double_right(struct btree *tree, struct btree_node *z) +{ + struct btree_node *x = z->b_left; + struct btree_node *y = x->b_right; + + rotate_left(tree, x); + rotate_right(tree, z); + + update_height(z); + update_height(x); + + while (y) { + update_height(y); + y = y->b_parent; + } +} + +/* run after an insert operation. checks that the balance factor + of the local subtree is within the range -1 <= BF <= 1. if it + is not, rotate the subtree to restore balance. + + note that at most one rotation should be required after a node + is inserted into the tree. + + this function depends on all nodes in the tree having + correct b_height values. + + @param w the node that was just inserted into the tree +*/ +static void insert_fixup(struct btree *tree, struct btree_node *w) +{ + struct btree_node *z = NULL, *y = NULL, *x = NULL; + + z = w; + while (z) { + if (bf(z) >= -1 && bf(z) <= 1) { + goto next_ancestor; + } + + if (IS_LEFT_CHILD(z, y)) { + if (IS_LEFT_CHILD(y, x)) { + rotate_right(tree, z); + update_height_to_root(z); + } else { + rotate_double_right(tree, z); + } + } else { + if (IS_LEFT_CHILD(y, x)) { + rotate_double_left(tree, z); + } else { + rotate_left(tree, z); + update_height_to_root(z); + } + } + + next_ancestor: + x = y; + y = z; + z = z->b_parent; + } +} + +/* run after a delete operation. checks that the balance factor + of the local subtree is within the range -1 <= BF <= 1. if it + is not, rotate the subtree to restore balance. + + note that, unlike insert_fixup, multiple rotations may be required + to restore balance after a node is deleted. + + this function depends on all nodes in the tree having + correct b_height values. + + @param w one of the following: + - the parent of the node that was deleted if the node + had no children. + - the parent of the node that replaced the deleted node + if the deleted node had two children. + - the node that replaced the node that was deleted, if + the node that was deleted had one child. +*/ +static void delete_fixup(struct btree *tree, struct btree_node *w) +{ + struct btree_node *z = w; + + while (z) { + if (bf(z) > 1) { + if (bf(z->b_right) >= 0) { + rotate_left(tree, z); + update_height_to_root(z); + } else { + rotate_double_left(tree, z); + } + } else if (bf(z) < -1) { + if (bf(z->b_left) <= 0) { + rotate_right(tree, z); + update_height_to_root(z); + } else { + rotate_double_right(tree, z); + } + } + + z = z->b_parent; + } +} + +/* updates b_height for all nodes between the inserted node and the root + of the tree, and calls insert_fixup. + + @param node the node that was just inserted into the tree. +*/ +void btree_insert_fixup(struct btree *tree, struct btree_node *node) +{ + node->b_height = 0; + + struct btree_node *cur = node; + while (cur) { + update_height(cur); + cur = cur->b_parent; + } + + insert_fixup(tree, node); +} + +/* remove a node from a tree. + + this function assumes that `node` has no children, and therefore + doesn't need to be replaced. + + updates b_height for all nodes between `node` and the tree root. + + @param node the node to delete. +*/ +static struct btree_node *remove_node_with_no_children( + struct btree *tree, + struct btree_node *node) +{ + struct btree_node *w = node->b_parent; + struct btree_node *p = node->b_parent; + node->b_parent = NULL; + + if (!p) { + tree->b_root = NULL; + } else if (IS_LEFT_CHILD(p, node)) { + p->b_left = NULL; + } else { + p->b_right = NULL; + } + + while (p) { + update_height(p); + p = p->b_parent; + } + + return w; +} + +/* remove a node from a tree. + + this function assumes that `node` has one child. + the child of `node` is inherited by `node`'s parent, and `node` is removed. + + updates b_height for all nodes between the node that replaced + `node` and the tree root. + + @param node the node to delete. +*/ +static struct btree_node *replace_node_with_one_subtree( + struct btree *tree, + struct btree_node *node) +{ + struct btree_node *p = node->b_parent; + struct btree_node *z = NULL; + + if (HAS_LEFT_CHILD(node)) { + z = node->b_left; + } else { + z = node->b_right; + } + + struct btree_node *w = z; + if (!p) { + tree->b_root = z; + } else if (IS_LEFT_CHILD(p, node)) { + p->b_left = z; + } else if (IS_RIGHT_CHILD(p, node)) { + p->b_right = z; + } + + z->b_parent = p; + + node->b_parent = NULL; + node->b_left = node->b_right = NULL; + + while (z) { + update_height(z); + z = z->b_parent; + } + + return w; +} + +/* remove a node from a tree. + + this function assumes that `node` has two children. + find the in-order successor Y of `node` (the largest node in `node`'s left + sub-tree), removes `node` from the tree and moves Y to where `node` used to + be. + + if Y has a child (it will never have more than one), have Y's parent inherit + Y's child. + + updates b_height for all nodes between the deepest node that was modified + and the tree root. + + @param z the node to delete. +*/ +static struct btree_node *replace_node_with_two_subtrees( + struct btree *tree, + struct btree_node *z) +{ + /* x will replace z */ + struct btree_node *x = z->b_left; + + while (x->b_right) { + x = x->b_right; + } + + /* y is the node that will replace x (if x has a left child) */ + struct btree_node *y = x->b_left; + + /* w is the starting point for the height update and fixup */ + struct btree_node *w = x; + if (w->b_parent != z) { + w = w->b_parent; + } + + if (y) { + w = y; + } + + if (IS_LEFT_CHILD(x->b_parent, x)) { + x->b_parent->b_left = y; + } else if (IS_RIGHT_CHILD(x->b_parent, x)) { + x->b_parent->b_right = y; + } + + if (y) { + y->b_parent = x->b_parent; + } + + if (IS_LEFT_CHILD(z->b_parent, z)) { + z->b_parent->b_left = x; + } else if (IS_RIGHT_CHILD(z->b_parent, z)) { + z->b_parent->b_right = x; + } + + x->b_parent = z->b_parent; + x->b_left = z->b_left; + x->b_right = z->b_right; + + if (x->b_left) { + x->b_left->b_parent = x; + } + + if (x->b_right) { + x->b_right->b_parent = x; + } + + if (!x->b_parent) { + tree->b_root = x; + } + + struct btree_node *cur = w; + while (cur) { + update_height(cur); + cur = cur->b_parent; + } + + return w; +} + +/* delete a node from the tree and re-balance it afterwards */ +void btree_delete(struct btree *tree, struct btree_node *node) +{ + struct btree_node *w = NULL; + + if (HAS_NO_CHILDREN(node)) { + w = remove_node_with_no_children(tree, node); + } else if (HAS_ONE_CHILD(node)) { + w = replace_node_with_one_subtree(tree, node); + } else if (HAS_TWO_CHILDREN(node)) { + w = replace_node_with_two_subtrees(tree, node); + } + + if (w) { + delete_fixup(tree, w); + } + + node->b_left = node->b_right = node->b_parent = NULL; +} + +struct btree_node *btree_first(struct btree *tree) +{ + /* the first node in the tree is the node with the smallest key. + we keep moving left until we can't go any further */ + struct btree_node *cur = tree->b_root; + if (!cur) { + return NULL; + } + + while (cur->b_left) { + cur = cur->b_left; + } + + return cur; +} + +struct btree_node *btree_last(struct btree *tree) +{ + /* the first node in the tree is the node with the largest key. + we keep moving right until we can't go any further */ + struct btree_node *cur = tree->b_root; + if (!cur) { + return NULL; + } + + while (cur->b_right) { + cur = cur->b_right; + } + + return cur; +} + +struct btree_node *btree_next(struct btree_node *node) +{ + if (!node) { + return NULL; + } + + /* there are two possibilities for the next node: + + 1. if `node` has a right sub-tree, every node in this sub-tree is + bigger than node. the in-order successor of `node` is the smallest + node in this subtree. + 2. if `node` has no right sub-tree, we've reached the largest node in + the sub-tree rooted at `node`. we need to go back to our parent + and continue the search elsewhere. + */ + if (node->b_right) { + /* case 1: step into `node`'s right sub-tree and keep going + left to find the smallest node */ + struct btree_node *cur = node->b_right; + while (cur->b_left) { + cur = cur->b_left; + } + + return cur; + } + + /* case 2: keep stepping back up towards the root of the tree. + if we encounter a step where we are our parent's left child, + we've found a parent with a value larger than us. this parent + is the in-order successor of `node` */ + while (node->b_parent && node->b_parent->b_left != node) { + node = node->b_parent; + } + + return node->b_parent; +} + +struct btree_node *btree_prev(struct btree_node *node) +{ + if (!node) { + return NULL; + } + + /* there are two possibilities for the previous node: + + 1. if `node` has a left sub-tree, every node in this sub-tree is + smaller than `node`. the in-order predecessor of `node` is the + largest node in this subtree. + 2. if `node` has no left sub-tree, we've reached the smallest node in + the sub-tree rooted at `node`. we need to go back to our parent + and continue the search elsewhere. + */ + if (node->b_left) { + /* case 1: step into `node`'s left sub-tree and keep going + right to find the largest node */ + struct btree_node *cur = node->b_left; + while (cur->b_right) { + cur = cur->b_right; + } + + return cur; + } + + /* case 2: keep stepping back up towards the root of the tree. + if we encounter a step where we are our parent's right child, + we've found a parent with a value smaller than us. this parent + is the in-order predecessor of `node`. */ + while (node->b_parent && node->b_parent->b_right != node) { + node = node->b_parent; + } + + return node->b_parent; +} diff --git a/lib/libfs/btree.h b/lib/libfs/btree.h new file mode 100644 index 0000000..53dc291 --- /dev/null +++ b/lib/libfs/btree.h @@ -0,0 +1,475 @@ +/* + The Clear BSD License + + Copyright (c) 2023 Max Wash + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted (subject to the limitations in the disclaimer + below) provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + */ + +#ifndef _FS_BTREE_H_ +#define _FS_BTREE_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* if your custom structure contains a struct btree_node (i.e. it can be part of + a btree), you can use this macro to convert a struct btree_node* to a + your_type* + + @param t the name of your custom type (something that can be passed to + offsetof) + @param m the name of the struct btree_node member variable within your custom + type. + @param v the struct btree_node pointer that you wish to convert. if this is + NULL, NULL will be returned. +*/ +#define BTREE_CONTAINER(t, m, v) \ + ((void *)((v) ? (uintptr_t)(v) - (offsetof(t, m)) : 0)) + +/* defines a simple node insertion function. + this function assumes that your nodes have simple integer keys that can be + compared with the usual operators. + + EXAMPLE: + if you have a tree node type like this: + + struct my_tree_node { + int key; + struct btree_node base; + } + + You would use the following call to generate an insert function for a tree + with this node type: + + BTREE_DEFINE_SIMPLE_INSERT(struct my_tree_node, base, key, + my_tree_node_insert); + + Which would emit a function defined like: + + static void my_tree_node_insert(struct btree *tree, struct my_tree_node + *node); + + @param node_type your custom tree node type. usually a structure that + contains a struct btree_node member. + @param container_node_member the name of the struct btree_node member + variable within your custom type. + @param container_key_member the name of the key member variable within your + custom type. + @param function_name the name of the function to generate. +*/ +#define BTREE_DEFINE_SIMPLE_INSERT( \ + node_type, \ + container_node_member, \ + container_key_member, \ + function_name) \ + void function_name(struct btree *tree, node_type *node) \ + { \ + if (!tree->b_root) { \ + tree->b_root = &node->container_node_member; \ + btree_insert_fixup( \ + tree, \ + &node->container_node_member); \ + return; \ + } \ + \ + struct btree_node *cur = tree->b_root; \ + while (1) { \ + node_type *cur_node = BTREE_CONTAINER( \ + node_type, \ + container_node_member, \ + cur); \ + struct btree_node *next = NULL; \ + \ + if (node->container_key_member \ + > cur_node->container_key_member) { \ + next = btree_right(cur); \ + \ + if (!next) { \ + btree_put_right( \ + cur, \ + &node->container_node_member); \ + break; \ + } \ + } else if ( \ + node->container_key_member \ + < cur_node->container_key_member) { \ + next = btree_left(cur); \ + \ + if (!next) { \ + btree_put_left( \ + cur, \ + &node->container_node_member); \ + break; \ + } \ + } else { \ + return; \ + } \ + \ + cur = next; \ + } \ + \ + btree_insert_fixup(tree, &node->container_node_member); \ + } + +/* defines a node insertion function. + this function should be used for trees with complex node keys that cannot be + directly compared. a comparator for your keys must be supplied. + + EXAMPLE: + if you have a tree node type like this: + + struct my_tree_node { + complex_key_t key; + struct btree_node base; + } + + You would need to define a comparator function or macro with the following + signature: + + int my_comparator(struct my_tree_node *a, struct my_tree_node *b); + + Which implements the following: + + return -1 if a < b + return 0 if a == b + return 1 if a > b + + You would use the following call to generate an insert function for a tree + with this node type: + + BTREE_DEFINE_INSERT(struct my_tree_node, base, key, my_tree_node_insert, + my_comparator); + + Which would emit a function defined like: + + static void my_tree_node_insert(struct btree *tree, struct my_tree_node + *node); + + @param node_type your custom tree node type. usually a structure that + contains a struct btree_node member. + @param container_node_member the name of the struct btree_node member + variable within your custom type. + @param container_key_member the name of the key member variable within your + custom type. + @param function_name the name of the function to generate. + @param comparator the name of a comparator function or functional-macro that + conforms to the requirements listed above. +*/ +#define BTREE_DEFINE_INSERT( \ + node_type, \ + container_node_member, \ + container_key_member, \ + function_name, \ + comparator) \ + void function_name(struct btree *tree, node_type *node) \ + { \ + if (!tree->b_root) { \ + tree->b_root = &node->container_node_member; \ + btree_insert_fixup( \ + tree, \ + &node->container_node_member); \ + return; \ + } \ + \ + struct btree_node *cur = tree->b_root; \ + while (1) { \ + node_type *cur_node = BTREE_CONTAINER( \ + node_type, \ + container_node_member, \ + cur); \ + struct btree_node *next = NULL; \ + int cmp = comparator(node, cur_node); \ + \ + if (cmp == 1) { \ + next = btree_right(cur); \ + \ + if (!next) { \ + btree_put_right( \ + cur, \ + &node->container_node_member); \ + break; \ + } \ + } else if (cmp == -1) { \ + next = btree_left(cur); \ + \ + if (!next) { \ + btree_put_left( \ + cur, \ + &node->container_node_member); \ + break; \ + } \ + } else { \ + return; \ + } \ + \ + cur = next; \ + } \ + \ + btree_insert_fixup(tree, &node->container_node_member); \ + } + +/* defines a simple tree search function. + this function assumes that your nodes have simple integer keys that can be + compared with the usual operators. + + EXAMPLE: + if you have a tree node type like this: + + struct my_tree_node { + int key; + struct btree_node base; + } + + You would use the following call to generate a search function for a tree + with this node type: + + BTREE_DEFINE_SIMPLE_GET(struct my_tree_node, int, base, key, + my_tree_node_get); + + Which would emit a function defined like: + + static struct my_tree_node *my_tree_node_get(struct btree *tree, int key); + + @param node_type your custom tree node type. usually a structure that + contains a struct btree_node member. + @param key_type the type name of the key embedded in your custom tree node + type. this type must be compatible with the builtin comparison operators. + @param container_node_member the name of the struct btree_node member + variable within your custom type. + @param container_key_member the name of the key member variable within your + custom type. + @param function_name the name of the function to generate. +*/ +#define BTREE_DEFINE_SIMPLE_GET( \ + node_type, \ + key_type, \ + container_node_member, \ + container_key_member, \ + function_name) \ + node_type *function_name(struct btree *tree, key_type key) \ + { \ + struct btree_node *cur = tree->b_root; \ + while (cur) { \ + node_type *cur_node = BTREE_CONTAINER( \ + node_type, \ + container_node_member, \ + cur); \ + if (key > cur_node->container_key_member) { \ + cur = btree_right(cur); \ + } else if (key < cur_node->container_key_member) { \ + cur = btree_left(cur); \ + } else { \ + return cur_node; \ + } \ + } \ + \ + return NULL; \ + } + +/* perform an in-order traversal of a binary tree + + If you have a tree defined like: + + struct btree my_tree; + + with nodes defined like: + + struct my_tree_node { + int key; + struct btree_node base; + } + + and you want to do something like: + + foreach (struct my_tree_node *node : my_tree) { ... } + + you should use this: + + btree_foreach (struct my_tree_node, node, &my_tree, base) { ... } + + @param iter_type the type name of the iterator variable. this should be the + tree's node type, and shouldn't be a pointer. + @param iter_name the name of the iterator variable. + @param tree_name a pointer to the tree to traverse. + @param node_member the name of the struct btree_node member variable within + the tree node type. +*/ +#define btree_foreach(iter_type, iter_name, tree_name, node_member) \ + for (iter_type *iter_name = BTREE_CONTAINER( \ + iter_type, \ + node_member, \ + btree_first(tree_name)); \ + iter_name; \ + iter_name = BTREE_CONTAINER( \ + iter_type, \ + node_member, \ + btree_next(&((iter_name)->node_member)))) + +/* perform an reverse in-order traversal of a binary tree + + If you have a tree defined like: + + struct btree my_tree; + + with nodes defined like: + + struct my_tree_node { + int key; + struct btree_node base; + } + + and you want to do something like: + + foreach (struct my_tree_node *node : reverse(my_tree)) { ... } + + you should use this: + + btree_foreach_r (struct my_tree_node, node, &my_tree, base) { ... } + + @param iter_type the type name of the iterator variable. this should be the + tree's node type, and shouldn't be a pointer. + @param iter_name the name of the iterator variable. + @param tree_name a pointer to the tree to traverse. + @param node_member the name of the struct btree_node member variable within + the tree node type. +*/ +#define btree_foreach_r(iter_type, iter_name, tree_name, node_member) \ + for (iter_type *iter_name \ + = BTREE_CONTAINER(iter_type, node_member, btree_last(tree_name)); \ + iter_name; \ + iter_name = BTREE_CONTAINER( \ + iter_type, \ + node_member, \ + btree_prev(&((iter_name)->node_member)))) + +/* binary tree nodes. this *cannot* be used directly. you need to define a + custom node type that contains a member variable of type struct btree_node. + + you would then use the supplied macros to define functions to manipulate your + custom binary tree. +*/ +struct btree_node { + struct btree_node *b_parent, *b_left, *b_right; + unsigned short b_height; +}; + +/* binary tree. unlike struct btree_node, you can define variables of type + * struct btree. */ +struct btree { + struct btree_node *b_root; +}; + +/* re-balance a binary tree after an insertion operation. + + NOTE that, if you define an insertion function using BTREE_DEFINE_INSERT or + similar, this function will automatically called for you. + + @param tree the tree to re-balance. + @param node the node that was just inserted into the tree. +*/ +extern void btree_insert_fixup(struct btree *tree, struct btree_node *node); + +/* delete a node from a binary tree and re-balance the tree afterwards. + + @param tree the tree to delete from + @param node the node to delete. +*/ +extern void btree_delete(struct btree *tree, struct btree_node *node); + +/* get the first node in a binary tree. + + this will be the node with the smallest key (i.e. the node that is + furthest-left from the root) +*/ +extern struct btree_node *btree_first(struct btree *tree); + +/* get the last node in a binary tree. + + this will be the node with the largest key (i.e. the node that is + furthest-right from the root) +*/ +extern struct btree_node *btree_last(struct btree *tree); +/* for any binary tree node, this function returns the node with the + * next-largest key value */ +extern struct btree_node *btree_next(struct btree_node *node); +/* for any binary tree node, this function returns the node with the + * next-smallest key value */ +extern struct btree_node *btree_prev(struct btree_node *node); + +static inline bool btree_empty(const struct btree *tree) +{ + return tree->b_root == NULL; +} + +/* sets `child` as the immediate left-child of `parent` */ +static inline void btree_put_left( + struct btree_node *parent, + struct btree_node *child) +{ + parent->b_left = child; + child->b_parent = parent; +} + +/* sets `child` as the immediate right-child of `parent` */ +static inline void btree_put_right( + struct btree_node *parent, + struct btree_node *child) +{ + parent->b_right = child; + child->b_parent = parent; +} + +/* get the immediate left-child of `node` */ +static inline struct btree_node *btree_left(struct btree_node *node) +{ + return node->b_left; +} + +/* get the immediate right-child of `node` */ +static inline struct btree_node *btree_right(struct btree_node *node) +{ + return node->b_right; +} + +/* get the immediate parent of `node` */ +static inline struct btree_node *btree_parent(struct btree_node *node) +{ + return node->b_parent; +} + +/* get the height of `node`. + + the height of a node is defined as the length of the longest path + between the node and a leaf node. + + this count includes the node itself, so the height of a leaf node will be 1. +*/ +static inline unsigned short btree_height(struct btree_node *node) +{ + return node->b_height; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/libfs/context.c b/lib/libfs/context.c new file mode 100644 index 0000000..fc96338 --- /dev/null +++ b/lib/libfs/context.c @@ -0,0 +1,86 @@ +#include "btree.h" +#include "file.h" +#include "interface.h" + +#include +#include + +BTREE_DEFINE_SIMPLE_GET(struct fs_file, unsigned long, f_node, f_id, get_file); +BTREE_DEFINE_SIMPLE_INSERT(struct fs_file, f_node, f_id, put_file); + +struct fs_context { + struct fs_superblock *ctx_sb; + struct fs_allocator *ctx_alloc; + struct btree ctx_filelist; + + struct fs_vtable ctx_vtable; +}; + +struct fs_context *fs_context_create( + struct fs_allocator *alloc, + struct fs_superblock *sb) +{ + struct fs_context *ctx = fs_alloc(alloc, sizeof *ctx); + if (!ctx) { + return NULL; + } + + memset(ctx, 0x0, sizeof *ctx); + + ctx->ctx_sb = sb; + ctx->ctx_alloc = alloc; + + ctx->ctx_vtable.open = fs_msg_open; + + return ctx; +} + +void fs_context_destroy(struct fs_context *ctx) +{ + fs_free(ctx->ctx_alloc, ctx); +} + +struct fs_file *fs_context_open_file(struct fs_context *ctx, unsigned long id) +{ + struct fs_file *f = get_file(&ctx->ctx_filelist, id); + if (!f) { + f = fs_alloc(ctx->ctx_alloc, sizeof *f); + if (!f) { + return NULL; + } + + memset(f, 0x0, sizeof *f); + + f->f_id = id; + put_file(&ctx->ctx_filelist, f); + } + + return f; +} + +void fs_context_close_file(struct fs_context *ctx, struct fs_file *f) +{ +} + +struct fs_dentry *fs_context_resolve_path( + struct fs_context *ctx, + const char *path) +{ + return NULL; +} + +kern_status_t fs_context_dispatch_msg( + struct fs_context *ctx, + kern_handle_t channel, + struct msg_endpoint *sender, + struct msg_header *hdr) +{ + return fs_dispatch( + channel, + &ctx->ctx_vtable, + sender, + hdr, + NULL, + 0, + ctx); +} diff --git a/lib/libfs/file.h b/lib/libfs/file.h new file mode 100644 index 0000000..99611bb --- /dev/null +++ b/lib/libfs/file.h @@ -0,0 +1,17 @@ +#ifndef _FS_FILE_H_ +#define _FS_FILE_H_ + +#include "btree.h" + +#include + +struct fs_file { + /* id of the open file, equal to the koid of the port being used to + * access the file */ + unsigned long f_id; + struct btree_node f_node; + + const struct fs_file_ops *f_ops; +}; + +#endif diff --git a/lib/libfs/include/fs/allocator.h b/lib/libfs/include/fs/allocator.h new file mode 100644 index 0000000..cb15ea1 --- /dev/null +++ b/lib/libfs/include/fs/allocator.h @@ -0,0 +1,19 @@ +#ifndef FS_ALLOCATOR_H_ +#define FS_ALLOCATOR_H_ + +#include + +struct fs_allocator { + void *fs_arg; + void *(*fs_alloc)(struct fs_allocator *, size_t); + void *(*fs_calloc)(struct fs_allocator *, size_t, size_t); + void *(*fs_realloc)(struct fs_allocator *, void *, size_t); + void (*fs_free)(struct fs_allocator *, void *); +}; + +extern void *fs_alloc(struct fs_allocator *alloc, size_t count); +extern void *fs_calloc(struct fs_allocator *alloc, size_t count, size_t sz); +extern void *fs_realloc(struct fs_allocator *alloc, void *p, size_t count); +extern void fs_free(struct fs_allocator *alloc, void *p); + +#endif diff --git a/lib/libfs/include/fs/context.h b/lib/libfs/include/fs/context.h new file mode 100644 index 0000000..fc40f19 --- /dev/null +++ b/lib/libfs/include/fs/context.h @@ -0,0 +1,31 @@ +#ifndef FS_CONTEXT_H_ +#define FS_CONTEXT_H_ + +#include + +struct fs_file; +struct fs_context; +struct fs_allocator; +struct fs_superblock; + +extern struct fs_context *fs_context_create( + struct fs_allocator *alloc, + struct fs_superblock *sb); +extern void fs_context_destroy(struct fs_context *ctx); + +extern struct fs_file *fs_context_open_file( + struct fs_context *ctx, + unsigned long id); +extern void fs_context_close_file(struct fs_context *ctx, struct fs_file *f); + +extern struct fs_dentry *fs_context_resolve_path( + struct fs_context *ctx, + const char *path); + +extern kern_status_t fs_context_dispatch_msg( + struct fs_context *ctx, + kern_handle_t channel, + struct msg_endpoint *sender, + struct msg_header *hdr); + +#endif diff --git a/lib/libfs/include/fs/dentry.h b/lib/libfs/include/fs/dentry.h new file mode 100644 index 0000000..f751f85 --- /dev/null +++ b/lib/libfs/include/fs/dentry.h @@ -0,0 +1,18 @@ +#ifndef FS_DENTRY_H_ +#define FS_DENTRY_H_ + +struct fs_inode; +struct fs_superblock; + +struct fs_dentry_ops { +}; + +struct fs_dentry { + struct fs_inode *d_inode; + struct fs_dentry *d_parent; + struct fs_superblock *d_sb; + const struct fs_dentry_ops *d_ops; + void *d_fsdata; +}; + +#endif diff --git a/lib/libfs/include/fs/file.h b/lib/libfs/include/fs/file.h new file mode 100644 index 0000000..129da9f --- /dev/null +++ b/lib/libfs/include/fs/file.h @@ -0,0 +1,15 @@ +#ifndef FS_FILE_H_ +#define FS_FILE_H_ + +#include +#include + +struct fs_file; + +struct fs_file_ops { + ssize_t (*f_read)(struct fs_file *, void *, size_t); + ssize_t (*f_write)(struct fs_file *, const void *, size_t); + off_t (*f_seek)(struct fs_file *, off_t, int); +}; + +#endif diff --git a/lib/libfs/include/fs/inode.h b/lib/libfs/include/fs/inode.h new file mode 100644 index 0000000..8a853db --- /dev/null +++ b/lib/libfs/include/fs/inode.h @@ -0,0 +1,17 @@ +#ifndef FS_INODE_H_ +#define FS_INODE_H_ + +struct fs_inode; +struct fs_dentry; +struct fs_superblock; + +struct fs_inode_ops { + int (*i_lookup)(struct fs_inode *, struct fs_dentry *); +}; + +struct fs_inode { + struct fs_superblock *i_sb; + const struct fs_inode_ops *i_ops; +}; + +#endif diff --git a/lib/libfs/include/fs/superblock.h b/lib/libfs/include/fs/superblock.h new file mode 100644 index 0000000..e354f2e --- /dev/null +++ b/lib/libfs/include/fs/superblock.h @@ -0,0 +1,20 @@ +#ifndef FS_SUPERBLOCK_H_ +#define FS_SUPERBLOCK_H_ + +struct fs_inode; +struct fs_dentry; +struct fs_superblock; + +struct fs_superblock_ops { + struct fs_inode *(*s_alloc_inode)(struct fs_superblock *); +}; + +struct fs_superblock { + const struct fs_superblock_ops *s_ops; + + struct fs_dentry *s_root; +}; + +extern struct fs_inode *fs_superblock_alloc_inode(struct fs_superblock *sb); + +#endif diff --git a/lib/libfs/interface.h b/lib/libfs/interface.h new file mode 100644 index 0000000..d6d4b0c --- /dev/null +++ b/lib/libfs/interface.h @@ -0,0 +1,21 @@ +#ifndef _FS_INTERFACE_H_ +#define _FS_INTERFACE_H_ + +#include + +struct msg_endpoint; + +extern kern_status_t fs_msg_open( + const struct msg_endpoint *sender, + const char *path, + int flags, + int *out_err, + void *arg); +extern kern_status_t fs_msg_close( + const struct msg_endpoint *sender, + const char *path, + int flags, + int *out_err, + void *arg); + +#endif diff --git a/lib/libfs/interface/open.c b/lib/libfs/interface/open.c new file mode 100644 index 0000000..ee3eeff --- /dev/null +++ b/lib/libfs/interface/open.c @@ -0,0 +1,21 @@ +#include +#include + +extern kern_status_t fs_msg_open( + const struct msg_endpoint *sender, + const char *path, + int flags, + int *out_err, + void *arg) +{ + struct fs_context *ctx = arg; + + struct fs_dentry *dent = fs_context_resolve_path(ctx, path); + if (!dent) { + *out_err = ENOENT; + return KERN_OK; + } + + *out_err = SUCCESS; + return KERN_OK; +} diff --git a/lib/libfs/superblock.c b/lib/libfs/superblock.c new file mode 100644 index 0000000..ac0ba54 --- /dev/null +++ b/lib/libfs/superblock.c @@ -0,0 +1,11 @@ +#include +#include + +struct fs_inode *fs_superblock_alloc_inode(struct fs_superblock *sb) +{ + if (!sb->s_ops->s_alloc_inode) { + return NULL; + } + + return sb->s_ops->s_alloc_inode(sb); +}