From 36689a09a8b91c471f79ec465fc659cbf0425a61 Mon Sep 17 00:00:00 2001 From: Leonmmcoset Date: Fri, 10 Apr 2026 17:05:46 +0800 Subject: [PATCH] Stage 6 --- Makefile | 5 +- clks/include/clks/boot.h | 4 +- clks/include/clks/fs.h | 24 ++ clks/include/clks/limine.h | 22 +- clks/include/clks/ramdisk.h | 24 ++ clks/kernel/fs.c | 572 ++++++++++++++++++++++++++++++++++ clks/kernel/kmain.c | 23 +- clks/kernel/limine_requests.c | 33 +- clks/kernel/ramdisk.c | 221 +++++++++++++ docs/stage6.md | 35 +++ 10 files changed, 957 insertions(+), 6 deletions(-) create mode 100644 clks/include/clks/fs.h create mode 100644 clks/include/clks/ramdisk.h create mode 100644 clks/kernel/fs.c create mode 100644 clks/kernel/ramdisk.c create mode 100644 docs/stage6.md diff --git a/Makefile b/Makefile index fe24ddf..e0447cf 100644 --- a/Makefile +++ b/Makefile @@ -75,6 +75,8 @@ C_SOURCES := \ clks/kernel/elf64.c \ clks/kernel/elfrunner.c \ clks/kernel/syscall.c \ + clks/kernel/ramdisk.c \ + clks/kernel/fs.c \ clks/lib/string.c \ clks/drivers/serial/serial.c \ clks/drivers/video/framebuffer.c \ @@ -242,4 +244,5 @@ help: > @echo " make iso" > @echo " make run" > @echo " make debug" -> @echo " make NO_COLOR=1 " \ No newline at end of file +> @echo " make NO_COLOR=1 " + diff --git a/clks/include/clks/boot.h b/clks/include/clks/boot.h index 9dd0c67..108dd93 100644 --- a/clks/include/clks/boot.h +++ b/clks/include/clks/boot.h @@ -8,5 +8,7 @@ clks_bool clks_boot_base_revision_supported(void); const struct limine_framebuffer *clks_boot_get_framebuffer(void); const struct limine_memmap_response *clks_boot_get_memmap(void); const struct limine_file *clks_boot_get_executable_file(void); +u64 clks_boot_get_module_count(void); +const struct limine_file *clks_boot_get_module(u64 index); -#endif \ No newline at end of file +#endif diff --git a/clks/include/clks/fs.h b/clks/include/clks/fs.h new file mode 100644 index 0000000..6ef902e --- /dev/null +++ b/clks/include/clks/fs.h @@ -0,0 +1,24 @@ +#ifndef CLKS_FS_H +#define CLKS_FS_H + +#include + +enum clks_fs_node_type { + CLKS_FS_NODE_FILE = 1, + CLKS_FS_NODE_DIR = 2, +}; + +struct clks_fs_node_info { + enum clks_fs_node_type type; + u64 size; +}; + +void clks_fs_init(void); +clks_bool clks_fs_is_ready(void); +clks_bool clks_fs_stat(const char *path, struct clks_fs_node_info *out_info); +const void *clks_fs_read_all(const char *path, u64 *out_size); +u64 clks_fs_count_children(const char *dir_path); +clks_bool clks_fs_get_child_name(const char *dir_path, u64 index, char *out_name, usize out_name_size); +u64 clks_fs_node_count(void); + +#endif diff --git a/clks/include/clks/limine.h b/clks/include/clks/limine.h index e8cddec..fc8fa6b 100644 --- a/clks/include/clks/limine.h +++ b/clks/include/clks/limine.h @@ -39,6 +39,14 @@ 0x31eb5d1c5ff23b69ULL \ } +#define LIMINE_MODULE_REQUEST \ + { \ + LIMINE_COMMON_MAGIC, \ + LIMINE_REQUEST_MAGIC, \ + 0x3e7e279702be32afULL, \ + 0xca1c4f3bd1280ceeULL \ + } + #define LIMINE_MEMMAP_USABLE 0ULL #define LIMINE_MEMMAP_RESERVED 1ULL #define LIMINE_MEMMAP_ACPI_RECLAIMABLE 2ULL @@ -132,4 +140,16 @@ struct limine_executable_file_request { struct limine_executable_file_response *response; }; -#endif \ No newline at end of file +struct limine_module_response { + u64 revision; + u64 module_count; + struct limine_file **modules; +}; + +struct limine_module_request { + u64 id[4]; + u64 revision; + struct limine_module_response *response; +}; + +#endif diff --git a/clks/include/clks/ramdisk.h b/clks/include/clks/ramdisk.h new file mode 100644 index 0000000..f86b799 --- /dev/null +++ b/clks/include/clks/ramdisk.h @@ -0,0 +1,24 @@ +#ifndef CLKS_RAMDISK_H +#define CLKS_RAMDISK_H + +#include + +#define CLKS_RAMDISK_PATH_MAX 192U + +enum clks_ramdisk_entry_type { + CLKS_RAMDISK_ENTRY_FILE = 1, + CLKS_RAMDISK_ENTRY_DIR = 2, +}; + +struct clks_ramdisk_entry { + enum clks_ramdisk_entry_type type; + char path[CLKS_RAMDISK_PATH_MAX]; + const void *data; + u64 size; +}; + +typedef clks_bool (*clks_ramdisk_iter_fn)(const struct clks_ramdisk_entry *entry, void *ctx); + +clks_bool clks_ramdisk_iterate(const void *image, u64 image_size, clks_ramdisk_iter_fn iter_fn, void *ctx); + +#endif diff --git a/clks/kernel/fs.c b/clks/kernel/fs.c new file mode 100644 index 0000000..ef554cf --- /dev/null +++ b/clks/kernel/fs.c @@ -0,0 +1,572 @@ +#include +#include +#include +#include +#include +#include + +#define CLKS_FS_MAX_NODES 512U +#define CLKS_FS_PATH_MAX CLKS_RAMDISK_PATH_MAX + +struct clks_fs_node { + clks_bool used; + enum clks_fs_node_type type; + u16 parent; + u16 reserved; + const void *data; + u64 size; + char path[CLKS_FS_PATH_MAX]; +}; + +struct clks_fs_build_stats { + u64 file_count; + u64 dir_count; +}; + +static struct clks_fs_node clks_fs_nodes[CLKS_FS_MAX_NODES]; +static u16 clks_fs_nodes_used = 0U; +static clks_bool clks_fs_ready = CLKS_FALSE; + +static clks_bool clks_fs_normalize_external_path(const char *path, char *out_internal, usize out_size) { + usize in_pos = 0; + usize out_pos = 0; + + if (path == CLKS_NULL || out_internal == CLKS_NULL || out_size == 0U) { + return CLKS_FALSE; + } + + if (path[0] != '/') { + return CLKS_FALSE; + } + + while (path[in_pos] == '/') { + in_pos++; + } + + while (path[in_pos] != '\0') { + usize comp_start = in_pos; + usize comp_len; + + while (path[in_pos] != '\0' && path[in_pos] != '/') { + in_pos++; + } + + comp_len = in_pos - comp_start; + + if (comp_len == 0U) { + while (path[in_pos] == '/') { + in_pos++; + } + continue; + } + + if (comp_len == 1U && path[comp_start] == '.') { + while (path[in_pos] == '/') { + in_pos++; + } + continue; + } + + if (comp_len == 2U && path[comp_start] == '.' && path[comp_start + 1U] == '.') { + return CLKS_FALSE; + } + + if (out_pos != 0U) { + if (out_pos + 1U >= out_size) { + return CLKS_FALSE; + } + out_internal[out_pos++] = '/'; + } + + if (out_pos + comp_len >= out_size) { + return CLKS_FALSE; + } + + clks_memcpy(out_internal + out_pos, path + comp_start, comp_len); + out_pos += comp_len; + + while (path[in_pos] == '/') { + in_pos++; + } + } + + out_internal[out_pos] = '\0'; + return CLKS_TRUE; +} + +static i32 clks_fs_find_node_by_internal(const char *internal_path) { + u16 i; + + for (i = 0U; i < clks_fs_nodes_used; i++) { + if (clks_fs_nodes[i].used == CLKS_FALSE) { + continue; + } + + if (clks_strcmp(clks_fs_nodes[i].path, internal_path) == 0) { + return (i32)i; + } + } + + return -1; +} + +static i32 clks_fs_find_node_by_external(const char *external_path) { + char internal[CLKS_FS_PATH_MAX]; + + if (clks_fs_normalize_external_path(external_path, internal, sizeof(internal)) == CLKS_FALSE) { + return -1; + } + + return clks_fs_find_node_by_internal(internal); +} + +static const char *clks_fs_basename(const char *internal_path) { + usize len; + usize i; + + if (internal_path == CLKS_NULL) { + return ""; + } + + len = clks_strlen(internal_path); + + if (len == 0U) { + return ""; + } + + for (i = len; i != 0U; i--) { + if (internal_path[i - 1U] == '/') { + return &internal_path[i]; + } + } + + return internal_path; +} + +static clks_bool clks_fs_split_parent(const char *internal_path, char *parent_out, usize parent_out_size) { + usize len; + usize i; + + if (internal_path == CLKS_NULL || parent_out == CLKS_NULL || parent_out_size == 0U) { + return CLKS_FALSE; + } + + len = clks_strlen(internal_path); + + if (len == 0U) { + parent_out[0] = '\0'; + return CLKS_TRUE; + } + + for (i = len; i != 0U; i--) { + if (internal_path[i - 1U] == '/') { + usize parent_len = i - 1U; + + if (parent_len >= parent_out_size) { + return CLKS_FALSE; + } + + clks_memcpy(parent_out, internal_path, parent_len); + parent_out[parent_len] = '\0'; + return CLKS_TRUE; + } + } + + parent_out[0] = '\0'; + return CLKS_TRUE; +} + +static i32 clks_fs_create_or_update_node(const char *internal_path, + enum clks_fs_node_type type, + u16 parent, + const void *data, + u64 size) { + i32 existing; + usize path_len; + + if (internal_path == CLKS_NULL) { + return -1; + } + + path_len = clks_strlen(internal_path); + + if (path_len >= CLKS_FS_PATH_MAX) { + return -1; + } + + existing = clks_fs_find_node_by_internal(internal_path); + + if (existing >= 0) { + struct clks_fs_node *node = &clks_fs_nodes[(u16)existing]; + + if (node->type != type) { + return -1; + } + + node->parent = parent; + + if (type == CLKS_FS_NODE_FILE) { + node->data = data; + node->size = size; + } + + return existing; + } + + if (clks_fs_nodes_used >= CLKS_FS_MAX_NODES) { + return -1; + } + + clks_fs_nodes[clks_fs_nodes_used].used = CLKS_TRUE; + clks_fs_nodes[clks_fs_nodes_used].type = type; + clks_fs_nodes[clks_fs_nodes_used].parent = parent; + clks_fs_nodes[clks_fs_nodes_used].reserved = 0U; + clks_fs_nodes[clks_fs_nodes_used].data = (type == CLKS_FS_NODE_FILE) ? data : CLKS_NULL; + clks_fs_nodes[clks_fs_nodes_used].size = (type == CLKS_FS_NODE_FILE) ? size : 0ULL; + clks_memcpy(clks_fs_nodes[clks_fs_nodes_used].path, internal_path, path_len + 1U); + + clks_fs_nodes_used++; + return (i32)(clks_fs_nodes_used - 1U); +} + +static clks_bool clks_fs_ensure_root(void) { + if (clks_fs_create_or_update_node("", CLKS_FS_NODE_DIR, 0U, CLKS_NULL, 0ULL) != 0) { + return CLKS_FALSE; + } + + return CLKS_TRUE; +} + +static clks_bool clks_fs_ensure_dir_hierarchy(const char *internal_dir_path) { + char prefix[CLKS_FS_PATH_MAX]; + usize cursor = 0; + usize i = 0; + u16 current_parent = 0U; + + prefix[0] = '\0'; + + if (internal_dir_path == CLKS_NULL) { + return CLKS_FALSE; + } + + if (internal_dir_path[0] == '\0') { + return CLKS_TRUE; + } + + while (internal_dir_path[i] != '\0') { + usize comp_start = i; + usize comp_len; + i32 node_index; + + while (internal_dir_path[i] != '\0' && internal_dir_path[i] != '/') { + i++; + } + + comp_len = i - comp_start; + + if (comp_len == 0U) { + return CLKS_FALSE; + } + + if (cursor != 0U) { + if (cursor + 1U >= sizeof(prefix)) { + return CLKS_FALSE; + } + prefix[cursor++] = '/'; + } + + if (cursor + comp_len >= sizeof(prefix)) { + return CLKS_FALSE; + } + + clks_memcpy(prefix + cursor, internal_dir_path + comp_start, comp_len); + cursor += comp_len; + prefix[cursor] = '\0'; + + node_index = clks_fs_find_node_by_internal(prefix); + + if (node_index < 0) { + node_index = clks_fs_create_or_update_node(prefix, CLKS_FS_NODE_DIR, current_parent, CLKS_NULL, 0ULL); + + if (node_index < 0) { + return CLKS_FALSE; + } + } else if (clks_fs_nodes[(u16)node_index].type != CLKS_FS_NODE_DIR) { + return CLKS_FALSE; + } + + current_parent = (u16)node_index; + + if (internal_dir_path[i] == '/') { + i++; + } + } + + return CLKS_TRUE; +} + +static clks_bool clks_fs_require_directory(const char *external_path) { + i32 node_index = clks_fs_find_node_by_external(external_path); + + if (node_index < 0) { + clks_log(CLKS_LOG_ERROR, "FS", "MISSING REQUIRED DIRECTORY"); + clks_log(CLKS_LOG_ERROR, "FS", external_path); + return CLKS_FALSE; + } + + if (clks_fs_nodes[(u16)node_index].type != CLKS_FS_NODE_DIR) { + clks_log(CLKS_LOG_ERROR, "FS", "REQUIRED PATH IS NOT DIRECTORY"); + clks_log(CLKS_LOG_ERROR, "FS", external_path); + return CLKS_FALSE; + } + + return CLKS_TRUE; +} + +static clks_bool clks_fs_ramdisk_visit(const struct clks_ramdisk_entry *entry, void *ctx) { + struct clks_fs_build_stats *stats = (struct clks_fs_build_stats *)ctx; + + if (entry == CLKS_NULL || stats == CLKS_NULL) { + return CLKS_FALSE; + } + + if (entry->type == CLKS_RAMDISK_ENTRY_DIR) { + if (clks_fs_ensure_dir_hierarchy(entry->path) == CLKS_FALSE) { + return CLKS_FALSE; + } + + stats->dir_count++; + return CLKS_TRUE; + } + + if (entry->type == CLKS_RAMDISK_ENTRY_FILE) { + char parent[CLKS_FS_PATH_MAX]; + i32 parent_index; + + if (clks_fs_split_parent(entry->path, parent, sizeof(parent)) == CLKS_FALSE) { + return CLKS_FALSE; + } + + if (clks_fs_ensure_dir_hierarchy(parent) == CLKS_FALSE) { + return CLKS_FALSE; + } + + parent_index = clks_fs_find_node_by_internal(parent); + + if (parent_index < 0) { + return CLKS_FALSE; + } + + if (clks_fs_create_or_update_node(entry->path, + CLKS_FS_NODE_FILE, + (u16)parent_index, + entry->data, + entry->size) < 0) { + return CLKS_FALSE; + } + + stats->file_count++; + return CLKS_TRUE; + } + + return CLKS_TRUE; +} + +void clks_fs_init(void) { + const struct limine_file *module; + struct clks_fs_build_stats stats; + u64 module_count; + + clks_fs_ready = CLKS_FALSE; + clks_fs_nodes_used = 0U; + clks_memset(clks_fs_nodes, 0, sizeof(clks_fs_nodes)); + clks_memset(&stats, 0, sizeof(stats)); + + if (clks_fs_ensure_root() == CLKS_FALSE) { + clks_log(CLKS_LOG_ERROR, "FS", "FAILED TO CREATE ROOT NODE"); + return; + } + + module_count = clks_boot_get_module_count(); + + if (module_count == 0ULL) { + clks_log(CLKS_LOG_ERROR, "FS", "NO RAMDISK MODULE FROM LIMINE"); + return; + } + + module = clks_boot_get_module(0ULL); + + if (module == CLKS_NULL || module->address == CLKS_NULL || module->size == 0ULL) { + clks_log(CLKS_LOG_ERROR, "FS", "INVALID RAMDISK MODULE"); + return; + } + + if (clks_ramdisk_iterate(module->address, module->size, clks_fs_ramdisk_visit, &stats) == CLKS_FALSE) { + clks_log(CLKS_LOG_ERROR, "FS", "RAMDISK TAR PARSE FAILED"); + return; + } + + clks_log(CLKS_LOG_INFO, "FS", "RAMDISK VFS ONLINE"); + clks_log_hex(CLKS_LOG_INFO, "FS", "MODULE_SIZE", module->size); + clks_log_hex(CLKS_LOG_INFO, "FS", "NODE_COUNT", (u64)clks_fs_nodes_used); + clks_log_hex(CLKS_LOG_INFO, "FS", "FILE_COUNT", stats.file_count); + + if (clks_fs_require_directory("/system") == CLKS_FALSE) { + return; + } + + if (clks_fs_require_directory("/shell") == CLKS_FALSE) { + return; + } + + if (clks_fs_require_directory("/temp") == CLKS_FALSE) { + return; + } + + if (clks_fs_require_directory("/driver") == CLKS_FALSE) { + return; + } + + clks_fs_ready = CLKS_TRUE; + clks_log(CLKS_LOG_INFO, "FS", "LAYOUT /SYSTEM /SHELL /TEMP /DRIVER OK"); +} + +clks_bool clks_fs_is_ready(void) { + return clks_fs_ready; +} + +clks_bool clks_fs_stat(const char *path, struct clks_fs_node_info *out_info) { + i32 node_index; + + if (clks_fs_ready == CLKS_FALSE || out_info == CLKS_NULL) { + return CLKS_FALSE; + } + + node_index = clks_fs_find_node_by_external(path); + + if (node_index < 0) { + return CLKS_FALSE; + } + + out_info->type = clks_fs_nodes[(u16)node_index].type; + out_info->size = clks_fs_nodes[(u16)node_index].size; + return CLKS_TRUE; +} + +const void *clks_fs_read_all(const char *path, u64 *out_size) { + i32 node_index; + + if (clks_fs_ready == CLKS_FALSE) { + return CLKS_NULL; + } + + node_index = clks_fs_find_node_by_external(path); + + if (node_index < 0) { + return CLKS_NULL; + } + + if (clks_fs_nodes[(u16)node_index].type != CLKS_FS_NODE_FILE) { + return CLKS_NULL; + } + + if (out_size != CLKS_NULL) { + *out_size = clks_fs_nodes[(u16)node_index].size; + } + + return clks_fs_nodes[(u16)node_index].data; +} + +u64 clks_fs_count_children(const char *dir_path) { + i32 dir_index; + u64 count = 0ULL; + u16 i; + + if (clks_fs_ready == CLKS_FALSE) { + return 0ULL; + } + + dir_index = clks_fs_find_node_by_external(dir_path); + + if (dir_index < 0) { + return 0ULL; + } + + if (clks_fs_nodes[(u16)dir_index].type != CLKS_FS_NODE_DIR) { + return 0ULL; + } + + for (i = 0U; i < clks_fs_nodes_used; i++) { + if (clks_fs_nodes[i].used == CLKS_FALSE) { + continue; + } + + if ((u16)dir_index == i) { + continue; + } + + if (clks_fs_nodes[i].parent == (u16)dir_index) { + count++; + } + } + + return count; +} + +clks_bool clks_fs_get_child_name(const char *dir_path, u64 index, char *out_name, usize out_name_size) { + i32 dir_index; + u64 current = 0ULL; + u16 i; + + if (clks_fs_ready == CLKS_FALSE || out_name == CLKS_NULL || out_name_size == 0U) { + return CLKS_FALSE; + } + + dir_index = clks_fs_find_node_by_external(dir_path); + + if (dir_index < 0 || clks_fs_nodes[(u16)dir_index].type != CLKS_FS_NODE_DIR) { + return CLKS_FALSE; + } + + for (i = 0U; i < clks_fs_nodes_used; i++) { + const char *base; + usize base_len; + + if (clks_fs_nodes[i].used == CLKS_FALSE) { + continue; + } + + if ((u16)dir_index == i) { + continue; + } + + if (clks_fs_nodes[i].parent != (u16)dir_index) { + continue; + } + + if (current != index) { + current++; + continue; + } + + base = clks_fs_basename(clks_fs_nodes[i].path); + base_len = clks_strlen(base); + + if (base_len + 1U > out_name_size) { + return CLKS_FALSE; + } + + clks_memcpy(out_name, base, base_len + 1U); + return CLKS_TRUE; + } + + return CLKS_FALSE; +} + +u64 clks_fs_node_count(void) { + if (clks_fs_ready == CLKS_FALSE) { + return 0ULL; + } + + return (u64)clks_fs_nodes_used; +} diff --git a/clks/kernel/kmain.c b/clks/kernel/kmain.c index 6dd85d1..6eedfd3 100644 --- a/clks/kernel/kmain.c +++ b/clks/kernel/kmain.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -19,8 +20,10 @@ void clks_kernel_main(void) { struct clks_pmm_stats pmm_stats; struct clks_heap_stats heap_stats; struct clks_scheduler_stats sched_stats; + struct clks_fs_node_info fs_system_dir = {0}; void *heap_probe = CLKS_NULL; u64 syscall_ticks; + u64 fs_root_children; clks_serial_init(); @@ -36,7 +39,7 @@ void clks_kernel_main(void) { clks_tty_init(); } - clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS STAGE5 START"); + clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS STAGE6 START"); if (boot_fb == CLKS_NULL) { clks_log(CLKS_LOG_WARN, "VIDEO", "NO FRAMEBUFFER FROM LIMINE"); @@ -83,6 +86,21 @@ void clks_kernel_main(void) { clks_kfree(heap_probe); } + clks_fs_init(); + + if (clks_fs_is_ready() == CLKS_FALSE) { + clks_log(CLKS_LOG_ERROR, "FS", "RAMDISK FS INIT FAILED"); + clks_cpu_halt_forever(); + } + + fs_root_children = clks_fs_count_children("/"); + clks_log_hex(CLKS_LOG_INFO, "FS", "ROOT_CHILDREN", fs_root_children); + + if (clks_fs_stat("/system", &fs_system_dir) == CLKS_FALSE || fs_system_dir.type != CLKS_FS_NODE_DIR) { + clks_log(CLKS_LOG_ERROR, "FS", "/SYSTEM DIRECTORY CHECK FAILED"); + clks_cpu_halt_forever(); + } + clks_scheduler_init(); if (clks_scheduler_add_kernel_task("klogd", 4U) == CLKS_FALSE) { @@ -114,4 +132,5 @@ void clks_kernel_main(void) { clks_log(CLKS_LOG_DEBUG, "KERNEL", "IDLE LOOP ENTER"); clks_cpu_halt_forever(); -} \ No newline at end of file +} + diff --git a/clks/kernel/limine_requests.c b/clks/kernel/limine_requests.c index 361b471..92a5d7f 100644 --- a/clks/kernel/limine_requests.c +++ b/clks/kernel/limine_requests.c @@ -28,6 +28,13 @@ CLKS_USED static volatile struct limine_executable_file_request limine_executabl .response = CLKS_NULL, }; +CLKS_USED static volatile struct limine_module_request limine_module_request + __attribute__((section(".limine_requests"))) = { + .id = LIMINE_MODULE_REQUEST, + .revision = 0, + .response = CLKS_NULL, + }; + CLKS_USED static volatile u64 limine_requests_end[] __attribute__((section(".limine_requests_end"))) = LIMINE_REQUESTS_END_MARKER; @@ -67,4 +74,28 @@ const struct limine_file *clks_boot_get_executable_file(void) { } return request->response->executable_file; -} \ No newline at end of file +} + +u64 clks_boot_get_module_count(void) { + volatile struct limine_module_request *request = &limine_module_request; + + if (request->response == CLKS_NULL) { + return 0ULL; + } + + return request->response->module_count; +} + +const struct limine_file *clks_boot_get_module(u64 index) { + volatile struct limine_module_request *request = &limine_module_request; + + if (request->response == CLKS_NULL) { + return CLKS_NULL; + } + + if (index >= request->response->module_count) { + return CLKS_NULL; + } + + return request->response->modules[index]; +} diff --git a/clks/kernel/ramdisk.c b/clks/kernel/ramdisk.c new file mode 100644 index 0000000..64562f8 --- /dev/null +++ b/clks/kernel/ramdisk.c @@ -0,0 +1,221 @@ +#include +#include +#include +#include + +#define CLKS_TAR_BLOCK_SIZE 512ULL + +struct clks_tar_header { + char name[100]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char chksum[8]; + char typeflag; + char linkname[100]; + char magic[6]; + char version[2]; + char uname[32]; + char gname[32]; + char devmajor[8]; + char devminor[8]; + char prefix[155]; + char pad[12]; +} CLKS_PACKED; + +static clks_bool clks_ramdisk_is_zero_block(const u8 *block) { + u64 i; + + for (i = 0; i < CLKS_TAR_BLOCK_SIZE; i++) { + if (block[i] != 0U) { + return CLKS_FALSE; + } + } + + return CLKS_TRUE; +} + +static usize clks_ramdisk_field_len(const char *field, usize max_len) { + usize i = 0; + + while (i < max_len && field[i] != '\0') { + i++; + } + + return i; +} + +static clks_bool clks_ramdisk_parse_octal_u64(const char *field, usize len, u64 *out_value) { + usize i = 0; + u64 value = 0ULL; + + if (out_value == CLKS_NULL) { + return CLKS_FALSE; + } + + while (i < len && (field[i] == ' ' || field[i] == '\0')) { + i++; + } + + for (; i < len; i++) { + char ch = field[i]; + + if (ch == ' ' || ch == '\0') { + break; + } + + if (ch < '0' || ch > '7') { + return CLKS_FALSE; + } + + if (value > (0xffffffffffffffffULL >> 3)) { + return CLKS_FALSE; + } + + value = (value << 3) + (u64)(ch - '0'); + } + + *out_value = value; + return CLKS_TRUE; +} + +static clks_bool clks_ramdisk_build_path(const struct clks_tar_header *hdr, char *out_path, usize out_path_size) { + char raw[CLKS_RAMDISK_PATH_MAX]; + usize prefix_len; + usize name_len; + usize cursor = 0; + usize read_pos = 0; + usize out_cursor = 0; + + if (hdr == CLKS_NULL || out_path == CLKS_NULL || out_path_size == 0U) { + return CLKS_FALSE; + } + + raw[0] = '\0'; + out_path[0] = '\0'; + + prefix_len = clks_ramdisk_field_len(hdr->prefix, sizeof(hdr->prefix)); + name_len = clks_ramdisk_field_len(hdr->name, sizeof(hdr->name)); + + if (name_len == 0U) { + return CLKS_FALSE; + } + + if (prefix_len != 0U) { + if (prefix_len + 1U >= sizeof(raw)) { + return CLKS_FALSE; + } + + clks_memcpy(raw, hdr->prefix, prefix_len); + cursor = prefix_len; + raw[cursor++] = '/'; + } + + if (cursor + name_len >= sizeof(raw)) { + return CLKS_FALSE; + } + + clks_memcpy(raw + cursor, hdr->name, name_len); + cursor += name_len; + raw[cursor] = '\0'; + + while ((raw[read_pos] == '.' && raw[read_pos + 1U] == '/') || raw[read_pos] == '/') { + if (raw[read_pos] == '.') { + read_pos += 2U; + } else { + read_pos++; + } + } + + while (raw[read_pos] != '\0' && out_cursor + 1U < out_path_size) { + out_path[out_cursor++] = raw[read_pos++]; + } + + if (raw[read_pos] != '\0') { + return CLKS_FALSE; + } + + while (out_cursor > 0U && out_path[out_cursor - 1U] == '/') { + out_cursor--; + } + + out_path[out_cursor] = '\0'; + + if (out_cursor == 0U) { + return CLKS_FALSE; + } + + return CLKS_TRUE; +} + +clks_bool clks_ramdisk_iterate(const void *image, u64 image_size, clks_ramdisk_iter_fn iter_fn, void *ctx) { + const u8 *bytes = (const u8 *)image; + u64 offset = 0ULL; + + if (image == CLKS_NULL || iter_fn == CLKS_NULL) { + return CLKS_FALSE; + } + + while (offset + CLKS_TAR_BLOCK_SIZE <= image_size) { + const struct clks_tar_header *hdr; + u64 payload_offset; + u64 file_size; + u64 aligned_size; + struct clks_ramdisk_entry entry; + clks_bool emit = CLKS_FALSE; + + hdr = (const struct clks_tar_header *)(bytes + offset); + + if (clks_ramdisk_is_zero_block((const u8 *)hdr) == CLKS_TRUE) { + break; + } + + if (clks_ramdisk_parse_octal_u64(hdr->size, sizeof(hdr->size), &file_size) == CLKS_FALSE) { + return CLKS_FALSE; + } + + payload_offset = offset + CLKS_TAR_BLOCK_SIZE; + + if (payload_offset > image_size) { + return CLKS_FALSE; + } + + if (file_size > (image_size - payload_offset)) { + return CLKS_FALSE; + } + + clks_memset(&entry, 0, sizeof(entry)); + + if (clks_ramdisk_build_path(hdr, entry.path, sizeof(entry.path)) == CLKS_TRUE) { + if (hdr->typeflag == '5') { + entry.type = CLKS_RAMDISK_ENTRY_DIR; + entry.data = CLKS_NULL; + entry.size = 0ULL; + emit = CLKS_TRUE; + } else if (hdr->typeflag == '\0' || hdr->typeflag == '0') { + entry.type = CLKS_RAMDISK_ENTRY_FILE; + entry.data = (const void *)(bytes + payload_offset); + entry.size = file_size; + emit = CLKS_TRUE; + } + } + + if (emit == CLKS_TRUE) { + if (iter_fn(&entry, ctx) == CLKS_FALSE) { + return CLKS_FALSE; + } + } + + aligned_size = (file_size + (CLKS_TAR_BLOCK_SIZE - 1ULL)) & ~(CLKS_TAR_BLOCK_SIZE - 1ULL); + + if (payload_offset + aligned_size < payload_offset) { + return CLKS_FALSE; + } + + offset = payload_offset + aligned_size; + } + + return CLKS_TRUE; +} diff --git a/docs/stage6.md b/docs/stage6.md new file mode 100644 index 0000000..4bbad8e --- /dev/null +++ b/docs/stage6.md @@ -0,0 +1,35 @@ +# CLeonOS Stage6 + +## Stage Goal +- Add ramdisk filesystem foundation in CLKS based on the Limine module. +- Parse tar-format ramdisk and build hierarchical directory/file nodes. +- Provide VFS-style path interfaces for stat/read/list operations. +- Enforce required root layout: `/system`, `/shell`, `/temp`, `/driver`. + +## Acceptance Criteria +- Kernel boots and prints `CLEONOS STAGE6 START`. +- Filesystem logs `RAMDISK VFS ONLINE` and node/file statistics. +- Root layout validation reports `/SYSTEM /SHELL /TEMP /DRIVER OK`. +- `clks_fs_count_children("/")` returns non-zero and is logged. +- Kernel continues to scheduler/ELF/syscall/interrupt init without panic. + +## Build Targets +- `make setup` +- `make iso` +- `make run` +- `make debug` + +## QEMU Command +- `qemu-system-x86_64 -M q35 -m 1024M -cdrom build/CLeonOS-x86_64.iso -serial stdio` + +## Common Bugs and Debugging +- `NO RAMDISK MODULE FROM LIMINE`: + - Verify `module_path: boot():/boot/cleonos_ramdisk.tar` exists in `configs/limine.conf`. +- `RAMDISK TAR PARSE FAILED`: + - Ensure ramdisk is packed as tar (`make ramdisk`) and module size is not zero. +- `MISSING REQUIRED DIRECTORY`: + - Confirm ramdisk root contains `/system`, `/shell`, `/temp`, `/driver`. +- Filesystem APIs always fail: + - Check `clks_fs_init()` is called and `clks_fs_is_ready()` is true. +- Build failure on new symbols: + - Confirm `ramdisk.c` and `fs.c` are present in `C_SOURCES`.