summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Maiolino <[email protected]>2026-03-08 13:37:37 +0100
committerCarlos Maiolino <[email protected]>2026-03-08 13:47:28 +0100
commit84b464a185d20d8e329b7aad0b482687a9705b58 (patch)
tree273954db9a38ca577a1bb8171f2f31a9ddbce54b
parent5ba10e3ee774602e20553091568551e210731fbb (diff)
Add a path parsing mechanism
Now the system can parse paths to files/directories. It follows the same Unix standard namespace. For now, only absolute paths are allowed. The parse mechanism relies on the struct path_part. Which represents each component on a single path. All the components are chain-linked through path_part->next, where the last path component will have ->next == NULL. This also introduces path_root struct, which represents the root of the path. The first component, linked to path_root->head, is always the "/" component. The disk_id represents the disk the root directory is found into. This should actually (and likely will) be a mountpoint not a disk id. Signed-off-by: Carlos Maiolino <[email protected]>
-rw-r--r--Makefile7
-rw-r--r--src/fs/path.c194
-rw-r--r--src/include/fs/path.h25
-rw-r--r--src/include/toxic/config.h4
4 files changed, 229 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 2414134..f9c7c06 100644
--- a/Makefile
+++ b/Makefile
@@ -20,7 +20,8 @@ KOBJ_FILES = $(KERNEL_ASM_OBJ) \
./build/mm/paging.asm.o \
./build/mm/paging.o \
./build/drivers/ata.o \
- ./build/block/block.o
+ ./build/block/block.o \
+ ./build/fs/path.o
BOOT_TGT =./bin/boot.bin
@@ -88,6 +89,9 @@ $(BUILD_DIR)/drivers/ata.o: ./src/drivers/ata.c
$(BUILD_DIR)/block/block.o: ./src/block/block.c
i686-elf-gcc $(INCLUDES) $(FLAGS) -std=gnu99 -c ./src/block/block.c -o $(BUILD_DIR)/block/block.o
+$(BUILD_DIR)/fs/path.o: ./src/fs/path.c
+ i686-elf-gcc $(INCLUDES) $(FLAGS) -std=gnu99 -c ./src/fs/path.c -o $(BUILD_DIR)/fs/path.o
+
clean:
rm -f $(BOOT_TGT)
rm -f bin/kernel.bin
@@ -102,4 +106,5 @@ clean:
rm -f build/mm/*.o
rm -f build/drivers/*.o
rm -f build/block/*.o
+ rm -f build/fs/*.o
rm -f bin/os.bin
diff --git a/src/fs/path.c b/src/fs/path.c
new file mode 100644
index 0000000..d0e31af
--- /dev/null
+++ b/src/fs/path.c
@@ -0,0 +1,194 @@
+#include <fs/path.h>
+#include <toxic/string.h>
+#include <toxic/config.h>
+#include <mm/kernel_heap.h>
+#include <toxic/errno.h>
+
+static int
+is_valid(const char *path)
+{
+ int len = strnlen(path, PATH_MAX);
+
+ /* Only allow for absolute paths for now */
+ if (len < PATH_MAX && path[len] == '\0' &&
+ strncmp(path, "/", 1) == 0)
+ return true;
+
+ return false;
+}
+
+/*
+ * Name must be dynamically allocated and not NULL
+ */
+static struct path_part *
+path_alloc(const char *name)
+{
+ struct path_part *p = kzalloc(sizeof(struct path_part));
+ p->name = name;
+
+ return p;
+}
+
+/*
+ * This is the main parser routine.
+ * It is designed to be called in a loop until it returns NULL,
+ * indicating the end of the path.
+ *
+ * It will return a copy of the current part of the path.
+ *
+ * So for a path like: /home/foo/bar, each call to this routine
+ * will return: "home", "foo", "bar" and NULL (signaling the end
+ * of the path). It uses the double pointer argument as its own
+ * cursor.
+ *
+ * - This makes no distinction if what it receives is an absolute
+ * or relative path. It just extracts the components.
+ * - The caller is responsible for checking if the path is
+ * absolute or relative.
+ *
+ * - It makes no distinction between files or directories, this only
+ * parses a path.
+ *
+ * - Double slashes like /home//foo are ignored and the entries returned
+ * will be "home" and "foo".
+ */
+static char *
+get_path_part(char **path)
+{
+ char *name;
+ char *cur = *path;
+ int i = 0;
+ size_t pathlen = strlen(cur);
+
+ /* We reached the end of the full path */
+ if (!pathlen)
+ return NULL;
+
+ /* Ignore any leading slashes */
+ while (cur[i] == '/' && i < pathlen)
+ i++;
+
+ /* There are no more components, just leading slash(es) */
+ if (i >= pathlen)
+ return NULL;
+
+
+ /* Advance cursor to the first non-slash character */
+ *path = cur += i;
+
+ /*
+ * Reset the index and advance it accordingly to the
+ * new cursor position.
+ */
+ for (i = 0; cur[i] != '/' && cur[i] != '\0'; i++);
+
+ name = kzalloc(i + 1); /* Size of the current component + null */
+ strncpy(name, cur, i);
+
+ /* kzalloc'ed, null terminator already there, but just in case...*/
+ name[i] = '\0';
+
+ /*
+ * Advance path to the next component
+ *
+ * The last for loop incremented 'i' to either the next '/' in the path
+ * or to the null terminator of the full path.
+ *
+ * Just point the caller's cursor to that location. Next call here will
+ * either return NULL or begin with a '/'
+ */
+ *path += (i + 1); /* Move the path cursor to the next component */
+
+ return name;
+}
+
+/*
+ * If last is not NULL, we are parsing a relative path
+ * so we should just extend the current path.
+ *
+ * Relative paths not implemented yet... last should ALWAYS
+ * be NULL
+ */
+static struct path_part *
+construct_path(
+ struct path_part *last,
+ char *p)
+{
+ char *cur = p; /* Used to walk through the path */
+ char *name = kzalloc(2); /* Alloc space for '/' */
+
+ name[0] = '/';
+ name[1] = '\0';
+
+ /* Root path component has NULL as its name */
+ struct path_part *root = path_alloc(name);
+ struct path_part *ptr_next = root;
+
+
+ /* Reset the name pointer before starting to parse the path */
+ name = NULL;
+
+ /* check (last == NULL); */
+
+ while ((name = get_path_part(&cur))) {
+ ptr_next->next = path_alloc(name);
+
+ /* Gotta check if path_aloc() failed*/
+
+ ptr_next = ptr_next->next;
+ }
+
+ /* Ensure we null terminate the component's linked list */
+ ptr_next->next = NULL;
+ return root;
+
+err_out:
+ return NULL;
+}
+
+static struct path_root *
+root_init(void)
+{
+ struct path_root *r = kzalloc(sizeof(struct path_root));
+
+ /*
+ * We just have support for one disk by now, and the root
+ * shouldn't be tied to a specific disk. I want a Unix-like
+ * namespace
+ */
+ r->disk_id = 0;
+ r->head = NULL;
+ return r;
+}
+
+void
+free_root(struct path_root *r)
+{
+ if (r)
+ kfree(r);
+}
+
+/*
+ * This should account for a relative path.
+ * By now it only deals with absolute paths (see is_valid())
+ */
+
+struct path_root *
+parse_path(char *path)
+{
+ struct path_root *root;
+
+ if (!is_valid(path))
+ return NULL;
+
+ root = root_init();
+ root->head = construct_path(NULL, path);
+
+ if (!root->head) {
+ free_root(root);
+ return NULL;
+ }
+
+ return root;
+}
+
diff --git a/src/include/fs/path.h b/src/include/fs/path.h
new file mode 100644
index 0000000..369e8b0
--- /dev/null
+++ b/src/include/fs/path.h
@@ -0,0 +1,25 @@
+#ifndef PATH_H
+#define PATH_H
+
+/*
+ * Path structure follows unix format:
+ *
+ * /foo/bar/file
+ *
+ */
+
+struct path_part {
+ const char *name;
+ struct path_part *next;
+};
+
+/* head is a linked list of all path components.
+ * the '/' is a component itself with name == NULL
+ */
+struct path_root {
+ int disk_id;
+ struct path_part *head;
+};
+
+struct path_root * parse_path(char *path);
+#endif /* PATH_H */
diff --git a/src/include/toxic/config.h b/src/include/toxic/config.h
index c1c1e7f..593eb2a 100644
--- a/src/include/toxic/config.h
+++ b/src/include/toxic/config.h
@@ -9,4 +9,8 @@
#define PAGE_SIZE (4096)
#define SECTOR_SIZE (512)
+
+/* Path parsing */
+#define PATH_MAX (128)
+
#endif /* CONFIG_H */