From d3f87b1b84d355ad72366ced5d7e5a43207226c0 Mon Sep 17 00:00:00 2001 From: Amlal El Mahrouss Date: Sun, 7 Sep 2025 23:29:48 +0200 Subject: feat: kernel: startup sequence fix, and new mgmt.hefs manual. wip: LaunchKit for the `ne_launch` program. Signed-off-by: Amlal El Mahrouss --- dev/kernel/src/UserProcessScheduler.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'dev/kernel/src') diff --git a/dev/kernel/src/UserProcessScheduler.cc b/dev/kernel/src/UserProcessScheduler.cc index 174862a4..07c4a572 100644 --- a/dev/kernel/src/UserProcessScheduler.cc +++ b/dev/kernel/src/UserProcessScheduler.cc @@ -438,7 +438,7 @@ ProcessID UserProcessScheduler::Spawn(const Char* name, VoidPtr code, VoidPtr im } (Void)(kout << "ProcessID: " << number(process.ProcessId) << kendl); - (Void)(kout << "Name: " << process.Name << kendl); + (Void)(kout << "ProcesName: " << process.Name << kendl); return pid; } -- cgit v1.2.3 From b1e3dac861c0d6c28bdb7768d7f96f19456e9ad0 Mon Sep 17 00:00:00 2001 From: Amlal El Mahrouss Date: Tue, 9 Sep 2025 21:28:04 +0200 Subject: feat:! ddk/dki, ne_launch, ifs: Big architectural changes and improvements. Signed-off-by: Amlal El Mahrouss --- dev/kernel/FSKit/Ext2+IFS.h | 140 +++++++++++++++++++++++++++++ dev/kernel/FSKit/Ext2IFS.h | 135 ---------------------------- dev/kernel/HALKit/AMD64/HalKernelMain.cc | 2 +- dev/kernel/HALKit/ARM64/HalKernelMain.cc | 2 +- dev/kernel/src/FS/Ext2+FileSystemParser.cc | 2 +- dev/launch/LaunchKit/LaunchKit.h | 2 + dev/launch/ne_launch.json | 2 +- dev/launch/src/CRT0.S | 17 ++++ dev/launch/src/LaunchSrv.cc | 5 +- dev/libDDK/DriverKit/cpp/object.hpp | 16 ---- dev/libDDK/DriverKit/dki/contract.h | 32 +++++++ dev/libSystem/src/System.cc | 15 +++- 12 files changed, 210 insertions(+), 160 deletions(-) create mode 100644 dev/kernel/FSKit/Ext2+IFS.h delete mode 100644 dev/kernel/FSKit/Ext2IFS.h create mode 100644 dev/launch/src/CRT0.S delete mode 100644 dev/libDDK/DriverKit/cpp/object.hpp create mode 100644 dev/libDDK/DriverKit/dki/contract.h (limited to 'dev/kernel/src') diff --git a/dev/kernel/FSKit/Ext2+IFS.h b/dev/kernel/FSKit/Ext2+IFS.h new file mode 100644 index 00000000..d73ae43c --- /dev/null +++ b/dev/kernel/FSKit/Ext2+IFS.h @@ -0,0 +1,140 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel::Ext2 { +/// @brief Context for an EXT2 filesystem on a given drive +struct Ext2Context { + Kernel::DriveTrait* drive{nullptr}; + EXT2_SUPER_BLOCK* superblock{nullptr}; + + /// @brief context with a drive + Ext2Context(Kernel::DriveTrait* drv) : drive(drv) {} + + /// @brief Clean up + ~Ext2Context() { + if (superblock) { + Kernel::mm_free_ptr(superblock); + superblock = nullptr; + } + } + + Ext2Context(const Ext2Context&) = delete; + Ext2Context& operator=(const Ext2Context&) = delete; + + Ext2Context(Ext2Context&& other) noexcept : drive(other.drive), superblock(other.superblock) { + other.drive = nullptr; + other.superblock = nullptr; + } + + Ext2Context& operator=(Ext2Context&& other) noexcept { + if (this != &other) { + if (superblock) { + Kernel::mm_free_ptr(superblock); + } + drive = other.drive; + superblock = other.superblock; + other.drive = nullptr; + other.superblock = nullptr; + } + return *this; + } + + Kernel::SizeT LeakBlockSize() const { + if (!superblock) return kExt2FSBlockSizeBase; + return kExt2FSBlockSizeBase << superblock->fLogBlockSize; + } + + BOOL operator bool() { return superblock != nullptr; } +}; + +/// ======================================================================= /// +/// IFS FUNCTIONS +/// ======================================================================= /// + +inline bool ext2_read_block(Kernel::DriveTrait* drv, Kernel::UInt32 lba, void* buffer, + Kernel::UInt32 size) { + if (!drv || !buffer) return false; + + Kernel::DriveTrait::DrivePacket pkt{}; + pkt.fPacketContent = buffer; + pkt.fPacketSize = size; + pkt.fPacketLba = lba; + drv->fInput(pkt); + + return pkt.fPacketGood; +} + +inline bool ext2_write_block(Kernel::DriveTrait* drv, Kernel::UInt32 lba, const void* buffer, + Kernel::UInt32 size) { + if (!drv || !buffer) return false; + + Kernel::DriveTrait::DrivePacket pkt{}; + pkt.fPacketContent = const_cast(buffer); + pkt.fPacketSize = size; + pkt.fPacketLba = lba; + drv->fOutput(pkt); + return pkt.fPacketGood; +} + +inline Kernel::ErrorOr ext2_load_superblock(Ext2Context* ctx) { + if (!ctx || !ctx->drive) return Kernel::ErrorOr(Kernel::kErrorInvalidData); + + auto buf = Kernel::mm_alloc_ptr(sizeof(EXT2_SUPER_BLOCK), true, false); + if (!buf) return Kernel::ErrorOr(Kernel::kErrorHeapOutOfMemory); + + Kernel::UInt32 blockLba = kExt2FSSuperblockOffset / ctx->drive->fSectorSz; + + if (!ext2_read_block(ctx->drive, blockLba, buf, sizeof(EXT2_SUPER_BLOCK))) { + Kernel::mm_free_ptr(buf); + return Kernel::ErrorOr(Kernel::kErrorDisk); + } + + auto sb = reinterpret_cast(buf); + if (sb->fMagic != kExt2FSMagic) { + Kernel::mm_free_ptr(buf); + return Kernel::ErrorOr(Kernel::kErrorInvalidData); + } + + ctx->superblock = sb; + return Kernel::ErrorOr(sb); +} + +// Load inode +inline Kernel::ErrorOr ext2_load_inode(Ext2Context* ctx, Kernel::UInt32 inodeNumber) { + if (!ctx || !ctx->superblock) return Kernel::ErrorOr(Kernel::kErrorInvalidData); + + auto nodePtr = Kernel::mm_alloc_ptr(sizeof(Ext2Node), true, false); + if (!nodePtr) return Kernel::ErrorOr(Kernel::kErrorHeapOutOfMemory); + + auto ext2Node = reinterpret_cast(nodePtr); + ext2Node->inodeNumber = inodeNumber; + + // Compute block group and index within group + Kernel::UInt32 inodesPerGroup = ctx->superblock->fInodesPerGroup; + Kernel::UInt32 group = (inodeNumber - 1) / inodesPerGroup; + Kernel::UInt32 index = (inodeNumber - 1) % inodesPerGroup; + + // dummy: just offset first inode + Kernel::UInt32 inodeTableBlock = ctx->superblock->fFirstInode + group; + + if (!ext2_read_block(ctx->drive, inodeTableBlock, &ext2Node->inode, sizeof(EXT2_INODE))) { + Kernel::mm_free_ptr(nodePtr); + return Kernel::ErrorOr(Kernel::kErrorDisk); + } + + ext2Node->cursor = 0; + return Kernel::ErrorOr(ext2Node); +} + +inline Kernel::UInt32 inode_offset(const Ext2Context* ctx, Kernel::UInt32 inodeNumber) { + if (!ctx || !ctx->superblock) return 0; + return ((inodeNumber - 1) % ctx->superblock->fInodesPerGroup) * ctx->superblock->fInodeSize; +} +} // namespace Kernel::Ext2 diff --git a/dev/kernel/FSKit/Ext2IFS.h b/dev/kernel/FSKit/Ext2IFS.h deleted file mode 100644 index 2550cfea..00000000 --- a/dev/kernel/FSKit/Ext2IFS.h +++ /dev/null @@ -1,135 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Ext2 { - -/// @brief Context for an EXT2 filesystem on a given drive -struct Context { - Kernel::DriveTrait* drive{nullptr}; - EXT2_SUPER_BLOCK* superblock{nullptr}; - - /// @brief context with a drive - explicit Context(Kernel::DriveTrait* drv) : drive(drv) {} - - /// @brief Clean up - ~Context() { - if (superblock) { - Kernel::mm_free_ptr(superblock); - superblock = nullptr; - } - } - - Context(const Context&) = delete; - Context& operator=(const Context&) = delete; - - Context(Context&& other) noexcept : drive(other.drive), superblock(other.superblock) { - other.drive = nullptr; - other.superblock = nullptr; - } - - Context& operator=(Context&& other) noexcept { - if (this != &other) { - if (superblock) { - Kernel::mm_free_ptr(superblock); - } - drive = other.drive; - superblock = other.superblock; - other.drive = nullptr; - other.superblock = nullptr; - } - return *this; - } - - inline Kernel::UInt32 BlockSize() const { - if (!superblock) return kExt2FSBlockSizeBase; - return kExt2FSBlockSizeBase << superblock->fLogBlockSize; - } -}; - -inline bool ext2_read_block(Kernel::DriveTrait* drv, Kernel::UInt32 lba, void* buffer, - Kernel::UInt32 size) { - if (!drv || !buffer) return false; - - Kernel::DriveTrait::DrivePacket pkt{}; - pkt.fPacketContent = buffer; - pkt.fPacketSize = size; - pkt.fPacketLba = lba; - drv->fInput(pkt); - return pkt.fPacketGood; -} - -inline bool ext2_write_block(Kernel::DriveTrait* drv, Kernel::UInt32 lba, const void* buffer, - Kernel::UInt32 size) { - if (!drv || !buffer) return false; - - Kernel::DriveTrait::DrivePacket pkt{}; - pkt.fPacketContent = const_cast(buffer); - pkt.fPacketSize = size; - pkt.fPacketLba = lba; - drv->fOutput(pkt); - return pkt.fPacketGood; -} - -// Load superblock -inline Kernel::ErrorOr ext2_load_superblock(Context* ctx) { - if (!ctx || !ctx->drive) return Kernel::ErrorOr(Kernel::kErrorInvalidData); - - auto buf = Kernel::mm_alloc_ptr(sizeof(EXT2_SUPER_BLOCK), true, false); - if (!buf) return Kernel::ErrorOr(Kernel::kErrorHeapOutOfMemory); - - Kernel::UInt32 blockLba = kExt2FSSuperblockOffset / ctx->drive->fSectorSz; - if (!ext2_read_block(ctx->drive, blockLba, buf, sizeof(EXT2_SUPER_BLOCK))) { - Kernel::mm_free_ptr(buf); - return Kernel::ErrorOr(Kernel::kErrorDisk); - } - - auto sb = reinterpret_cast(buf); - if (sb->fMagic != kExt2FSMagic) { - Kernel::mm_free_ptr(buf); - return Kernel::ErrorOr(Kernel::kErrorInvalidData); - } - - ctx->superblock = sb; - return Kernel::ErrorOr(sb); -} - -// Load inode -inline Kernel::ErrorOr ext2_load_inode(Context* ctx, Kernel::UInt32 inodeNumber) { - if (!ctx || !ctx->superblock) return Kernel::ErrorOr(Kernel::kErrorInvalidData); - - auto nodePtr = Kernel::mm_alloc_ptr(sizeof(Ext2Node), true, false); - if (!nodePtr) return Kernel::ErrorOr(Kernel::kErrorHeapOutOfMemory); - - auto ext2Node = reinterpret_cast(nodePtr); - ext2Node->inodeNumber = inodeNumber; - - // Compute block group and index within group - Kernel::UInt32 inodesPerGroup = ctx->superblock->fInodesPerGroup; - Kernel::UInt32 group = (inodeNumber - 1) / inodesPerGroup; - Kernel::UInt32 index = (inodeNumber - 1) % inodesPerGroup; - - // dummy: just offset first inode - Kernel::UInt32 inodeTableBlock = ctx->superblock->fFirstInode + group; - - if (!ext2_read_block(ctx->drive, inodeTableBlock, &ext2Node->inode, sizeof(EXT2_INODE))) { - Kernel::mm_free_ptr(nodePtr); - return Kernel::ErrorOr(Kernel::kErrorDisk); - } - - ext2Node->cursor = 0; - return Kernel::ErrorOr(ext2Node); -} - -inline Kernel::UInt32 inode_offset(const Context* ctx, Kernel::UInt32 inodeNumber) { - if (!ctx || !ctx->superblock) return 0; - return ((inodeNumber - 1) % ctx->superblock->fInodesPerGroup) * ctx->superblock->fInodeSize; -} - -} // namespace Ext2 diff --git a/dev/kernel/HALKit/AMD64/HalKernelMain.cc b/dev/kernel/HALKit/AMD64/HalKernelMain.cc index 16670f5c..6f4d7e0a 100644 --- a/dev/kernel/HALKit/AMD64/HalKernelMain.cc +++ b/dev/kernel/HALKit/AMD64/HalKernelMain.cc @@ -119,7 +119,7 @@ EXTERN_C Int32 hal_init_platform(Kernel::HEL::BootInfoHeader* handover_hdr) { kGDTArray[4].fBaseHigh = 0; FB::fb_clear_video(); - + // Load memory descriptors. HAL::Register64 gdt_reg; diff --git a/dev/kernel/HALKit/ARM64/HalKernelMain.cc b/dev/kernel/HALKit/ARM64/HalKernelMain.cc index 240b3ac8..b711b833 100644 --- a/dev/kernel/HALKit/ARM64/HalKernelMain.cc +++ b/dev/kernel/HALKit/ARM64/HalKernelMain.cc @@ -48,7 +48,7 @@ EXTERN_C void hal_init_platform(Kernel::HEL::BootInfoHeader* handover_hdr) { /* INITIALIZE BIT MAP. */ /************************************** */ - kBitMapCursor = 0UL; + kBitMapCursor = 0UL; kKernelBitMpSize = kHandoverHeader->f_BitMapSize; kKernelBitMpStart = reinterpret_cast( reinterpret_cast(kHandoverHeader->f_BitMapStart)); diff --git a/dev/kernel/src/FS/Ext2+FileSystemParser.cc b/dev/kernel/src/FS/Ext2+FileSystemParser.cc index 80449ed9..318f83d6 100644 --- a/dev/kernel/src/FS/Ext2+FileSystemParser.cc +++ b/dev/kernel/src/FS/Ext2+FileSystemParser.cc @@ -6,7 +6,7 @@ #ifdef __FSKIT_INCLUDES_EXT2__ -#include +#include #include #include #include diff --git a/dev/launch/LaunchKit/LaunchKit.h b/dev/launch/LaunchKit/LaunchKit.h index 1a134e8f..2fa9607b 100644 --- a/dev/launch/LaunchKit/LaunchKit.h +++ b/dev/launch/LaunchKit/LaunchKit.h @@ -14,5 +14,7 @@ #define NELAUNCH_INFO(MSG) PrintOut(nullptr, "INFO: [LAUNCH] %s\n", MSG) #define NELAUNCH_WARN(MSG) PrintOut(nullptr, "WARN: [LAUNCH] %s\n", MSG) +namespace Launch { using LaunchHandle = VoidPtr; using KernelStatus = SInt32; +} // namespace Launch diff --git a/dev/launch/ne_launch.json b/dev/launch/ne_launch.json index 38379de9..e3d26a46 100644 --- a/dev/launch/ne_launch.json +++ b/dev/launch/ne_launch.json @@ -2,7 +2,7 @@ "compiler_path": "x86_64-w64-mingw32-g++", "compiler_std": "c++20", "headers_path": ["../", "./"], - "sources_path": ["src/*.cc"], + "sources_path": ["src/*.cc", "src/*.S"], "output_name": "ne_launch", "compiler_flags": [ "-ffreestanding", diff --git a/dev/launch/src/CRT0.S b/dev/launch/src/CRT0.S new file mode 100644 index 00000000..ffc7398a --- /dev/null +++ b/dev/launch/src/CRT0.S @@ -0,0 +1,17 @@ +/* ------------------------------------------- + + Copyright (C) 2025, Amlal El Mahrouss, all rights reserved. + + ------------------------------------------- */ + + .text + +.extern nelaunch_startup_fn + +_NeMain: + push %rbp + movq %rsp, %rbp + callq nelaunch_startup_fn + popq %rbp + + retq diff --git a/dev/launch/src/LaunchSrv.cc b/dev/launch/src/LaunchSrv.cc index 40027f65..f5c9ee3c 100644 --- a/dev/launch/src/LaunchSrv.cc +++ b/dev/launch/src/LaunchSrv.cc @@ -7,10 +7,11 @@ #include #include -SInt32 _NeMain(Void) { +/// @note This called by _NeMain from its own runtime. +extern "C" SInt32 nelaunch_startup_fn(Void) { /// @todo Start LaunchServices.fwrk services, make the launcher manageable too (via mgmt.launch) - NELAUNCH_INFO("Starting services..."); + NELAUNCH_INFO("Starting NeKernel services..."); return kErrorSuccess; } diff --git a/dev/libDDK/DriverKit/cpp/object.hpp b/dev/libDDK/DriverKit/cpp/object.hpp deleted file mode 100644 index 3bf52657..00000000 --- a/dev/libDDK/DriverKit/cpp/object.hpp +++ /dev/null @@ -1,16 +0,0 @@ -/* ------------------------------------------- - - Copyright Amlal El Mahrouss 2025, all rights reserved. - - FILE: ddk.h - PURPOSE: DDK Driver model base header. - - ------------------------------------------- */ - -#pragma once - -#include - -#define DK_INTERFACE_IMPL : public ::DKInterface - -class DKInterface; diff --git a/dev/libDDK/DriverKit/dki/contract.h b/dev/libDDK/DriverKit/dki/contract.h new file mode 100644 index 00000000..23884e02 --- /dev/null +++ b/dev/libDDK/DriverKit/dki/contract.h @@ -0,0 +1,32 @@ +/* ------------------------------------------- + + Copyright Amlal El Mahrouss 2025, all rights reserved. + + FILE: ddk.h + PURPOSE: Driver Kernel Interface Model base header. + + ------------------------------------------- */ + +#pragma once + +#include +#include + +#define DKI_CONTRACT_IMPL : public ::Kernel::DKIContract + +/// @author Amlal El Mahrouss + +namespace Kernel::DKI { +class DKIContract { + public: + explicit DKIContract() = default; + virtual ~DKIContract() = default; + + NE_COPY_DEFAULT(DKIContract); + + virtual BOOL IsCastable() { return false; } + virtual BOOL IsActive() { return false; } + virtual VoidPtr Leak() { return nullptr; } + virtual Int32 Type() { return 0; } +}; +} // namespace Kernel::DKI diff --git a/dev/libSystem/src/System.cc b/dev/libSystem/src/System.cc index ba848a18..2215c4d8 100644 --- a/dev/libSystem/src/System.cc +++ b/dev/libSystem/src/System.cc @@ -40,6 +40,12 @@ IMPORT_C UInt64 libsys_hash_64(const Char* path) { return hash; } +IMPORT_C Char* StrFmt(const Char* fmt, ...) { + if (!fmt || *fmt == 0) return const_cast("(null)"); + + return const_cast(""); +} + // memmove-style copy IMPORT_C VoidPtr MmCopyMemory(_Input VoidPtr dest, _Input VoidPtr src, _Input SizeT len) { // handles overlap, prefers 64-bit word copies when aligned @@ -58,8 +64,9 @@ IMPORT_C VoidPtr MmCopyMemory(_Input VoidPtr dest, _Input VoidPtr src, _Input Si // try 64-bit aligned backward copy if (len >= sizeof(UInt64) && (reinterpret_cast(rs) % sizeof(UInt64) == 0) && (reinterpret_cast(rd) % sizeof(UInt64) == 0)) { - auto rsw = reinterpret_cast(rs); - auto rdw = reinterpret_cast(rd); + auto rsw = reinterpret_cast(rs); + auto rdw = reinterpret_cast(rd); + SizeT words = len / sizeof(UInt64); for (SizeT i = 0; i < words; ++i) { @@ -105,9 +112,11 @@ IMPORT_C VoidPtr MmCopyMemory(_Input VoidPtr dest, _Input VoidPtr src, _Input Si IMPORT_C SInt64 MmStrLen(const Char* in) { // strlen via pointer walk - if (!in) return 0; + if (!in) return -kErrorInvalidData; + const Char* p = in; while (*p) ++p; + return static_cast(p - in); } -- cgit v1.2.3 From d7ede77b3e13bc985e1b320249948434040c73a8 Mon Sep 17 00:00:00 2001 From: Amlal El Mahrouss Date: Mon, 15 Sep 2025 16:37:13 +0200 Subject: feat: kernel: type fixes. Signed-off-by: Amlal El Mahrouss --- dev/kernel/src/AsciiUtils.cc | 8 ++++---- dev/kernel/src/UtfUtils.cc | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'dev/kernel/src') diff --git a/dev/kernel/src/AsciiUtils.cc b/dev/kernel/src/AsciiUtils.cc index 24e4e220..30773d91 100644 --- a/dev/kernel/src/AsciiUtils.cc +++ b/dev/kernel/src/AsciiUtils.cc @@ -45,7 +45,7 @@ const Char* rt_alloc_string(const Char* src) { return buffer; } -Int rt_copy_memory_safe(const voidPtr src, voidPtr dst, Size len, Size dst_size) { +Int32 rt_copy_memory_safe(const voidPtr src, voidPtr dst, Size len, Size dst_size) { if (!src || !dst || len > dst_size) { if (dst && dst_size) { rt_set_memory_safe(dst, 0, dst_size, dst_size); @@ -85,7 +85,7 @@ rt_set_memory(voidPtr src, UInt32 value, Size len) { #ifdef __NE_ENFORCE_DEPRECATED_WARNINGS [[deprecated("Use rt_copy_memory_safe instead")]] #endif -Int rt_copy_memory(const voidPtr src, voidPtr dst, Size len) { +Int32 rt_copy_memory(const voidPtr src, voidPtr dst, Size len) { if (!src || !dst) return -1; auto s = reinterpret_cast(src); auto d = reinterpret_cast(dst); @@ -135,7 +135,7 @@ Bool rt_to_string(Char* str, UInt64 value, Int32 base) { str[i - j - 1] = tmp; } #endif - return true; + return false; } VoidPtr rt_string_in_string(const Char* haystack, const Char* needle) { @@ -166,7 +166,7 @@ EXTERN_C void* memcpy(void* dst, const void* src, long long unsigned int len) { return dst; } -EXTERN_C Kernel::Int32 strcmp(const char* a, const char* b) { +EXTERN_C Int32 strcmp(const char* a, const char* b) { return Kernel::rt_string_cmp(a, b, rt_string_len(a)); } diff --git a/dev/kernel/src/UtfUtils.cc b/dev/kernel/src/UtfUtils.cc index 907632ad..e98b8306 100644 --- a/dev/kernel/src/UtfUtils.cc +++ b/dev/kernel/src/UtfUtils.cc @@ -37,7 +37,7 @@ Int32 urt_string_cmp(const Utf8Char* src, const Utf8Char* cmp, Size size) { return counter; } -Int urt_copy_memory(const VoidPtr src, VoidPtr dst, Size len) { +Int32 urt_copy_memory(const VoidPtr src, VoidPtr dst, Size len) { Utf8Char* srcChr = reinterpret_cast(src); Utf8Char* dstChar = reinterpret_cast(dst); -- cgit v1.2.3 From dfd6aca50dd2349e828afadc0183419e8e4d0acd Mon Sep 17 00:00:00 2001 From: Amlal El Mahrouss Date: Tue, 16 Sep 2025 10:07:23 +0200 Subject: feat: kernel/AsciiUtils: use correct type `Int32` instead of `Int` Signed-off-by: Amlal El Mahrouss --- dev/kernel/src/AsciiUtils.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'dev/kernel/src') diff --git a/dev/kernel/src/AsciiUtils.cc b/dev/kernel/src/AsciiUtils.cc index 30773d91..920a5b75 100644 --- a/dev/kernel/src/AsciiUtils.cc +++ b/dev/kernel/src/AsciiUtils.cc @@ -8,7 +8,7 @@ namespace Kernel { -Int rt_copy_memory_safe(const voidPtr src, voidPtr dst, Size len, Size dst_size); +Int32 rt_copy_memory_safe(const voidPtr src, voidPtr dst, Size len, Size dst_size); voidPtr rt_set_memory_safe(voidPtr dst, UInt32 value, Size len, Size dst_size); Int32 rt_string_cmp(const Char* src, const Char* cmp, Size size) { -- cgit v1.2.3 From a461a1b5a568087ea4991cb3af2300dd6f8ef90e Mon Sep 17 00:00:00 2001 From: Amlal El Mahrouss Date: Fri, 19 Sep 2025 10:22:54 +0200 Subject: feat: kernel && libDDK: `DDK` and `kernel` improvements. what: - Hardened `AsciiUtils` and `libDDK` Signed-off-by: Amlal El Mahrouss --- dev/kernel/src/AsciiUtils.cc | 9 +++++++-- dev/libDDK/src/ddk_dev.c | 4 ++-- dev/libDDK/src/ddk_io.c | 8 ++++++-- dev/libDDK/src/ddk_str.c | 2 ++ 4 files changed, 17 insertions(+), 6 deletions(-) (limited to 'dev/kernel/src') diff --git a/dev/kernel/src/AsciiUtils.cc b/dev/kernel/src/AsciiUtils.cc index 920a5b75..cca3a368 100644 --- a/dev/kernel/src/AsciiUtils.cc +++ b/dev/kernel/src/AsciiUtils.cc @@ -8,7 +8,7 @@ namespace Kernel { -Int32 rt_copy_memory_safe(const voidPtr src, voidPtr dst, Size len, Size dst_size); +Int32 rt_copy_memory_safe(const voidPtr src, voidPtr dst, Size len, Size dst_size); voidPtr rt_set_memory_safe(voidPtr dst, UInt32 value, Size len, Size dst_size); Int32 rt_string_cmp(const Char* src, const Char* cmp, Size size) { @@ -134,11 +134,15 @@ Bool rt_to_string(Char* str, UInt64 value, Int32 base) { str[j] = str[i - j - 1]; str[i - j - 1] = tmp; } + + return YES; #endif - return false; + return NO; } VoidPtr rt_string_in_string(const Char* haystack, const Char* needle) { + if (!haystack || !needle) return nullptr; + SizeT needle_len = rt_string_len(needle); SizeT hay_len = rt_string_len(haystack); @@ -152,6 +156,7 @@ VoidPtr rt_string_in_string(const Char* haystack, const Char* needle) { } Char* rt_string_has_char(Char* str, Char ch) { + if (!str) return nullptr; while (*str && *str != ch) ++str; return (*str == ch) ? str : nullptr; } diff --git a/dev/libDDK/src/ddk_dev.c b/dev/libDDK/src/ddk_dev.c index 32ec2442..d20684aa 100644 --- a/dev/libDDK/src/ddk_dev.c +++ b/dev/libDDK/src/ddk_dev.c @@ -11,7 +11,7 @@ /// @brief Open a new binary device from path. DDK_EXTERN DDK_DEVICE_PTR open(const char* devicePath) { - if (!devicePath) return nil; + if (nil == devicePath) return nil; return ke_call_dispatch("dk_open_dev", 1, (void*) devicePath, kstrlen(devicePath)); } @@ -19,7 +19,7 @@ DDK_EXTERN DDK_DEVICE_PTR open(const char* devicePath) { /// @brief Close any device. /// @param device valid device. DDK_EXTERN BOOL close(DDK_DEVICE_PTR device) { - if (!device) return NO; + if (nil == device) return NO; ke_call_dispatch("dk_close_dev", 1, device, sizeof(DDK_DEVICE)); return YES; diff --git a/dev/libDDK/src/ddk_io.c b/dev/libDDK/src/ddk_io.c index c6cdd457..825e82a7 100644 --- a/dev/libDDK/src/ddk_io.c +++ b/dev/libDDK/src/ddk_io.c @@ -1,7 +1,9 @@ /* ------------------------------------------- - Copyright Amlal El Mahrouss. + libDDK. + Copyright 2025 - Amlal El Mahrouss and NeKernel contributors. + File: ddk_io.c Purpose: DDK Text I/O. ------------------------------------------- */ @@ -9,6 +11,8 @@ #include DDK_EXTERN void kputc(const char ch) { + if (!ch) return; + char assembled[2] = {0}; assembled[0] = ch; assembled[1] = 0; @@ -19,7 +23,7 @@ DDK_EXTERN void kputc(const char ch) { /// @brief print string to UART. /// @param message UART to transmit. DDK_EXTERN void kprint(const char* message) { - if (!message) return; + if (nil == message) return; if (*message == 0) return; size_t index = 0; diff --git a/dev/libDDK/src/ddk_str.c b/dev/libDDK/src/ddk_str.c index 514cddc7..1e2fde19 100644 --- a/dev/libDDK/src/ddk_str.c +++ b/dev/libDDK/src/ddk_str.c @@ -23,6 +23,8 @@ DDK_EXTERN size_t kstrlen(const char* in) { } DDK_EXTERN int kstrncpy(char* dst, const char* src, size_t len) { + if (nil == dst || nil == src) return 0; + size_t index = 0; while (index != len) { -- cgit v1.2.3 From 78bd706f8703d0c5cce7c8a66e4668ed28532e07 Mon Sep 17 00:00:00 2001 From: Amlal El Mahrouss Date: Fri, 19 Sep 2025 10:24:21 +0200 Subject: fix: acpi: return error code as negative value instead of positive one. Signed-off-by: Amlal El Mahrouss --- dev/kernel/src/ACPIFactoryInterface.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'dev/kernel/src') diff --git a/dev/kernel/src/ACPIFactoryInterface.cc b/dev/kernel/src/ACPIFactoryInterface.cc index 711ea588..6cebf26c 100644 --- a/dev/kernel/src/ACPIFactoryInterface.cc +++ b/dev/kernel/src/ACPIFactoryInterface.cc @@ -70,7 +70,7 @@ ErrorOr ACPIFactoryInterface::Find(const Char* signature) { @param len the length of it. */ bool ACPIFactoryInterface::Checksum(const Char* checksum, SSizeT len) { - if (len == 0 || !checksum) return false; + if (len == 0 || !checksum) return NO; Char chr = 0; -- cgit v1.2.3 From 71fab5288e19e0b6a8de7614ac56d6c73c1e5ef4 Mon Sep 17 00:00:00 2001 From: 0xf00sec <159052166+0xf00sec@users.noreply.github.com> Date: Sun, 28 Sep 2025 15:08:57 +0300 Subject: Ext2FileMgr Ext2 Manager implementation with full read/write capabilities. The implementation supports file and directory creation, path resolution. Signed-off-by: 0xf00sec <159052166+0xf00sec@users.noreply.github.com> --- dev/kernel/src/FS/Ext2+FileMgr.cc | 1542 ++++++++++++++++++++++++++++++++++++- 1 file changed, 1534 insertions(+), 8 deletions(-) (limited to 'dev/kernel/src') diff --git a/dev/kernel/src/FS/Ext2+FileMgr.cc b/dev/kernel/src/FS/Ext2+FileMgr.cc index 810e7e44..d5d23c3f 100644 --- a/dev/kernel/src/FS/Ext2+FileMgr.cc +++ b/dev/kernel/src/FS/Ext2+FileMgr.cc @@ -1,14 +1,1540 @@ -/* ------------------------------------------- - - Copyright (C) 2024-2025, Amlal El Mahrouss, all rights reserved. - -------------------------------------------- */ - #ifndef __NE_MINIMAL_OS__ #ifdef __FSKIT_INCLUDES_EXT2__ +#include +#include #include +#include #include +#include +#include +#include +#include + +namespace Ext2 { + +constexpr UInt32 EXT2_DIRECT_BLOCKS = 12; +constexpr UInt32 EXT2_SINGLE_INDIRECT_INDEX = 12; +constexpr UInt32 EXT2_DOUBLE_INDIRECT_INDEX = 13; +constexpr UInt32 EXT2_TRIPLE_INDIRECT_INDEX = 14; +constexpr UInt32 EXT2_ROOT_INODE = 2; +constexpr UInt32 EXT2_SUPERBLOCK_BLOCK = 1; +constexpr UInt32 EXT2_GROUP_DESC_BLOCK_SMALL = 2; +constexpr UInt32 EXT2_GROUP_DESC_BLOCK_LARGE = 1; + +static inline SizeT ext2_min(SizeT a, SizeT b) { return a < b ? a : b; } + +struct Ext2GroupInfo { + EXT2_GROUP_DESCRIPTOR* groupDesc; + UInt32 groupDescriptorBlock; + UInt32 offsetInGroupDescBlock; + UInt8* blockBuffer; +}; + +// Convert EXT2 block number -> LBA (sector index) for Drive I/O. +static inline Kernel::UInt32 ext2_block_to_lba(Context* ctx, Kernel::UInt32 blockNumber) { + if (!ctx || !ctx->drive) return 0; + Kernel::UInt32 blockSize = ctx->BlockSize(); + Kernel::UInt32 sectorSize = ctx->drive->fSectorSz; + Kernel::UInt32 sectorsPerBlock = blockSize / sectorSize; + return blockNumber * sectorsPerBlock; +} + +// Read a block and return a pointer to its content +static ErrorOr ext2_read_block_ptr(Context* ctx, Kernel::UInt32 blockNumber) { + if (!ctx || !ctx->drive || !ctx->superblock) + return ErrorOr(Kernel::kErrorInvalidData); + + Kernel::UInt32 blockSize = ctx->BlockSize(); + auto buf = (Kernel::UInt32*)mm_alloc_ptr(blockSize, true, false); + if (!buf) return ErrorOr(Kernel::kErrorHeapOutOfMemory); + + Kernel::UInt32 lba = ext2_block_to_lba(ctx, blockNumber); + if (!ext2_read_block(ctx->drive, lba, buf, blockSize)) { + mm_free_ptr(buf); + return ErrorOr(Kernel::kErrorDisk); + } + return ErrorOr(buf); +} + +// Get the block address for a given logical block index +static ErrorOr ext2_get_block_address(Context* ctx, Ext2Node* node, Kernel::UInt32 logicalIndex) { + if (!ctx || !node || !ctx->drive) + return ErrorOr(Kernel::kErrorInvalidData); + + Kernel::UInt32 blockSize = ctx->BlockSize(); + Kernel::UInt32 pointersPerBlock = blockSize / sizeof(Kernel::UInt32); + + // Direct blocks + if (logicalIndex < EXT2_DIRECT_BLOCKS) { + Kernel::UInt32 bn = node->inode.fBlock[logicalIndex]; + if (bn == 0) return ErrorOr(Kernel::kErrorInvalidData); + return ErrorOr(bn); + } + + // Single indirect blocks + if (logicalIndex < (EXT2_DIRECT_BLOCKS + pointersPerBlock)) { + Kernel::UInt32 iblock = node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX]; + if (iblock == 0) return ErrorOr(Kernel::kErrorInvalidData); + + auto res = ext2_read_block_ptr(ctx, iblock); + if (!res) return ErrorOr(res.Error()); + + // Using dereference operator + Kernel::UInt32* ptr = *res.Leak(); // operator* returns T (UInt32*) + + Kernel::UInt32 val = ptr[logicalIndex - EXT2_DIRECT_BLOCKS]; + mm_free_ptr(ptr); + + if (val == 0) return ErrorOr(Kernel::kErrorInvalidData); + return ErrorOr(val); + } + + // Double indirect blocks + Kernel::UInt32 doubleStart = EXT2_DIRECT_BLOCKS + pointersPerBlock; + Kernel::UInt32 doubleSpan = pointersPerBlock * pointersPerBlock; + if (logicalIndex < (doubleStart + doubleSpan)) { + Kernel::UInt32 db = node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX]; + if (db == 0) return ErrorOr(Kernel::kErrorInvalidData); + + auto dblRes = ext2_read_block_ptr(ctx, db); + if (!dblRes) return ErrorOr(dblRes.Error()); + + Kernel::UInt32* dblPtr = *dblRes.Leak(); + + Kernel::UInt32 idxWithin = logicalIndex - doubleStart; + Kernel::UInt32 firstIdx = idxWithin / pointersPerBlock; + Kernel::UInt32 secondIdx = idxWithin % pointersPerBlock; + Kernel::UInt32 singleBlockNum = dblPtr[firstIdx]; + + mm_free_ptr(dblPtr); + if (singleBlockNum == 0) return ErrorOr(Kernel::kErrorInvalidData); + + auto singleRes = ext2_read_block_ptr(ctx, singleBlockNum); + if (!singleRes) return ErrorOr(singleRes.Error()); + + Kernel::UInt32* singlePtr = *singleRes.Leak(); + Kernel::UInt32 val = singlePtr[secondIdx]; + mm_free_ptr(singlePtr); + + if (val == 0) return ErrorOr(Kernel::kErrorInvalidData); + return ErrorOr(val); + } + + return ErrorOr(Kernel::kErrorUnimplemented); +} + +static Kernel::ErrorOr ext2_read_inode_data(Context* ctx, Ext2Node* node, SizeT size) { + using Kernel::ErrorOr; + using Kernel::UInt8; + using Kernel::UInt32; + + if (!ctx || !ctx->drive || !node || size == 0) + return ErrorOr(1); + + auto blockSize = ctx->BlockSize(); + SizeT available = (node->inode.fSize > node->cursor) ? (node->inode.fSize - node->cursor) : 0; + SizeT bytesToRead = (size < available) ? size : available; + if (bytesToRead == 0) + return ErrorOr(2); // nothing to read + + auto buffer = mm_alloc_ptr(bytesToRead, true, false); + if (!buffer) + return ErrorOr(3); // allocation failed + + UInt32 currentOffset = node->cursor; + SizeT remaining = bytesToRead; + UInt8* dest = reinterpret_cast(buffer); + + while (remaining > 0) { + UInt32 logicalIndex = currentOffset / blockSize; + UInt32 offsetInBlock = currentOffset % blockSize; + + auto phys = ext2_get_block_address(ctx, node, logicalIndex); + if (phys.HasError()) { + mm_free_ptr(buffer); + return ErrorOr(phys.Error()); + } + + UInt32 blockNumber = phys.Value(); + UInt32 lba = ext2_block_to_lba(ctx, blockNumber); + + auto blockBuf = mm_alloc_ptr(blockSize, true, false); + if (!blockBuf) { + mm_free_ptr(buffer); + return ErrorOr(4); // block buffer allocation failed + } + + if (!ext2_read_block(ctx->drive, lba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + mm_free_ptr(buffer); + return ErrorOr(5); // block read failed + } + + SizeT chunk = ext2_min(remaining, blockSize - offsetInBlock); + rt_copy_memory_safe(static_cast(static_cast(blockBuf) + offsetInBlock), + static_cast(dest), chunk, chunk); + + mm_free_ptr(blockBuf); + + currentOffset += static_cast(chunk); + dest += chunk; + remaining -= chunk; + } + + node->cursor += static_cast(bytesToRead); + return ErrorOr(buffer); +} + + +// Get group descriptor information for a given block/inode number +static ErrorOr ext2_get_group_descriptor_info(Context* ctx, UInt32 targetBlockOrInode) { + if (!ctx || !ctx->superblock || !ctx->drive) + return ErrorOr(kErrorInvalidData); + + UInt32 blockSize = ctx->BlockSize(); + UInt32 blocksPerGroup = ctx->superblock->fBlocksPerGroup; + UInt32 inodesPerGroup = ctx->superblock->fInodesPerGroup; + UInt32 totalBlocks = ctx->superblock->fBlockCount; + UInt32 totalInodes = ctx->superblock->fInodeCount; + + if (blocksPerGroup == 0 || inodesPerGroup == 0) + return ErrorOr(kErrorInvalidData); + + // block group index + UInt32 groupIndex = 0; + if (targetBlockOrInode == 0) { + groupIndex = 0; + } else if (targetBlockOrInode <= totalInodes) { + // 1-based + groupIndex = (targetBlockOrInode - 1) / inodesPerGroup; + } else { + // EXT2 block number + if (targetBlockOrInode < ctx->superblock->fFirstDataBlock) { + groupIndex = 0; + } else { + groupIndex = (targetBlockOrInode - ctx->superblock->fFirstDataBlock) / blocksPerGroup; + } + } + + // Calculate number of block groups + UInt32 groupsCount = static_cast((totalBlocks + blocksPerGroup - 1) / blocksPerGroup); + if (groupIndex >= groupsCount) + return ErrorOr(kErrorInvalidData); + + // Determine GDT start block + UInt32 gdtStartBlock = (blockSize == 1024) ? EXT2_GROUP_DESC_BLOCK_SMALL : EXT2_GROUP_DESC_BLOCK_LARGE; + + // Compute byte offset of descriptor within the GDT + const UInt32 descSize = sizeof(EXT2_GROUP_DESCRIPTOR); + UInt64 descByteOffset = static_cast(groupIndex) * descSize; + + // Which EXT2 block contains that descriptor? + UInt32 blockOffsetWithinGdt = static_cast(descByteOffset / blockSize); + UInt32 offsetInGroupDescBlock = static_cast(descByteOffset % blockSize); + UInt32 groupDescriptorBlock = gdtStartBlock + blockOffsetWithinGdt; + + // Allocate buffer and read the block containing the descriptor + auto blockBuffer = mm_alloc_ptr(blockSize, true, false); + if (!blockBuffer) return ErrorOr(kErrorHeapOutOfMemory); + + UInt32 groupDescriptorLba = ext2_block_to_lba(ctx, groupDescriptorBlock); + if (!ext2_read_block(ctx->drive, groupDescriptorLba, blockBuffer, blockSize)) { + mm_free_ptr(blockBuffer); + return ErrorOr(kErrorDisk); + } + + auto groupInfo = (Ext2GroupInfo*)mm_alloc_ptr(sizeof(Ext2GroupInfo), true, false); + if (!groupInfo) { + mm_free_ptr(blockBuffer); + return ErrorOr(kErrorHeapOutOfMemory); + } + + groupInfo->groupDesc = reinterpret_cast( + reinterpret_cast(blockBuffer) + offsetInGroupDescBlock); + groupInfo->groupDescriptorBlock = groupDescriptorBlock; + groupInfo->offsetInGroupDescBlock = offsetInGroupDescBlock; + groupInfo->blockBuffer = reinterpret_cast(blockBuffer); + + return ErrorOr(groupInfo); +} + +// Allocate a new block +inline ErrorOr ext2_alloc_block(Context* ctx, EXT2_GROUP_DESCRIPTOR* groupDesc) { + if (!ctx || !ctx->superblock || !groupDesc) + return ErrorOr(kErrorInvalidData); + + UInt32 blockSize = ctx->BlockSize(); + + // for the bitmap + auto bitmap = mm_alloc_ptr(blockSize, true, false); + if (!bitmap) return ErrorOr(kErrorHeapOutOfMemory); + + // Read block bitmap + if (!ext2_read_block(ctx->drive, groupDesc->fBlockBitmap, bitmap, blockSize)) { + mm_free_ptr(bitmap); + return ErrorOr(kErrorDisk); + } + + // bit = 0 + for (UInt32 byteIdx = 0; byteIdx < blockSize; ++byteIdx) { + auto byte = reinterpret_cast(bitmap)[byteIdx]; + if (byte != 0xFF) { + for (int bit = 0; bit < 8; ++bit) { + if (!(byte & (1 << bit))) { + // Mark bit as used + reinterpret_cast(bitmap)[byteIdx] |= (1 << bit); + + // Compute block number + UInt32 blockNumber = byteIdx * 8 + bit; + + // Write bitmap back + if (!ext2_write_block(ctx->drive, groupDesc->fBlockBitmap, bitmap, blockSize)) { + mm_free_ptr(bitmap); + return ErrorOr(kErrorDisk); + } + + // Update group descriptor free count + groupDesc->fFreeBlocksCount--; + mm_free_ptr(bitmap); + return ErrorOr(blockNumber); + } + } + } + } + + mm_free_ptr(bitmap); + return ErrorOr(kErrorDiskIsFull); +} + +// Indirect blocks +static Kernel::ErrorOr ext2_set_block_address(Context* ctx, Ext2Node* node, + Kernel::UInt32 logicalBlockIndex, + Kernel::UInt32 physicalBlockNumber) { + using namespace Kernel; + + if (!ctx || !ctx->drive || !node) + return ErrorOr(kErrorInvalidData); + + auto blockSize = ctx->BlockSize(); + UInt32 blocksPerPointerBlock = blockSize / sizeof(UInt32); + + // Direct blocks + if (logicalBlockIndex < EXT2_DIRECT_BLOCKS) { + node->inode.fBlock[logicalBlockIndex] = physicalBlockNumber; + return ErrorOr(nullptr); + } + + // Single indirect blocks + if (logicalBlockIndex < EXT2_DIRECT_BLOCKS + blocksPerPointerBlock) { + if (node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX] == 0) { + auto groupInfoRes = ext2_get_group_descriptor_info(ctx, node->inodeNumber); + if (groupInfoRes.HasError()) return ErrorOr(groupInfoRes.Error()); + + auto groupInfo = groupInfoRes.Leak().Leak(); // Ref + auto newBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); + if (newBlockRes.HasError()) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(newBlockRes.Error()); + } + + node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX] = newBlockRes.Leak(); + + UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + // Zero out new indirect block + auto zeroBuf = mm_alloc_ptr(blockSize, true, false); + if (!zeroBuf) return ErrorOr(kErrorHeapOutOfMemory); + + rt_zero_memory(zeroBuf, blockSize); + UInt32 indirectLba = ext2_block_to_lba(ctx, node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX]); + if (!ext2_write_block(ctx->drive, indirectLba, zeroBuf, blockSize)) { + mm_free_ptr(zeroBuf); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(zeroBuf); + } + + // Read, modify, and write single indirect block + auto indirectRes = ext2_read_block_ptr(ctx, node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX]); + if (indirectRes.HasError()) return ErrorOr(indirectRes.Error()); + + UInt32* indirectPtr = indirectRes.Leak().Leak(); // Ref + indirectPtr[logicalBlockIndex - EXT2_DIRECT_BLOCKS] = physicalBlockNumber; + + UInt32 indirectLba = ext2_block_to_lba(ctx, node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX]); + if (!ext2_write_block(ctx->drive, indirectLba, indirectPtr, blockSize)) { + mm_free_ptr(indirectPtr); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(indirectPtr); + return ErrorOr(nullptr); + } + + // Double + UInt32 doubleStart = EXT2_DIRECT_BLOCKS + blocksPerPointerBlock; + UInt32 doubleSpan = blocksPerPointerBlock * blocksPerPointerBlock; + if (logicalBlockIndex < doubleStart + doubleSpan) { + if (node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX] == 0) { + auto groupInfoRes = ext2_get_group_descriptor_info(ctx, node->inodeNumber); + if (groupInfoRes.HasError()) return ErrorOr(groupInfoRes.Error()); + + auto groupInfo = groupInfoRes.Leak().Leak(); + auto newBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); + if (newBlockRes.HasError()) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(newBlockRes.Error()); + } + + node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX] = newBlockRes.Leak(); + + UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + // Zero new double-indirect block + auto zeroBuf = mm_alloc_ptr(blockSize, true, false); + if (!zeroBuf) return ErrorOr(kErrorHeapOutOfMemory); + + rt_zero_memory(zeroBuf, blockSize); + UInt32 dblLba = ext2_block_to_lba(ctx, node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX]); + if (!ext2_write_block(ctx->drive, dblLba, zeroBuf, blockSize)) { + mm_free_ptr(zeroBuf); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(zeroBuf); + } + + // Compute indices + UInt32 idxWithin = logicalBlockIndex - doubleStart; + UInt32 firstIdx = idxWithin / blocksPerPointerBlock; + UInt32 secondIdx = idxWithin % blocksPerPointerBlock; + + auto doubleRes = ext2_read_block_ptr(ctx, node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX]); + if (doubleRes.HasError()) return ErrorOr(doubleRes.Error()); + + UInt32* doublePtr = doubleRes.Leak().Leak(); + UInt32 singleIndirectBlock = doublePtr[firstIdx]; + + // Allocate single-indirect if missing + if (singleIndirectBlock == 0) { + auto groupInfoRes = ext2_get_group_descriptor_info(ctx, node->inodeNumber); + if (groupInfoRes.HasError()) { mm_free_ptr(doublePtr); return ErrorOr(groupInfoRes.Error()); } + + auto groupInfo = groupInfoRes.Leak().Leak(); + auto newBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); + if (newBlockRes.HasError()) { + mm_free_ptr(doublePtr); + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(newBlockRes.Error()); + } + + singleIndirectBlock = newBlockRes.Leak(); + doublePtr[firstIdx] = singleIndirectBlock; + + // Write back GDT + UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + mm_free_ptr(doublePtr); + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + // Zero single-indirect block + auto zeroBuf = mm_alloc_ptr(blockSize, true, false); + if (!zeroBuf) { mm_free_ptr(doublePtr); return ErrorOr(kErrorHeapOutOfMemory); } + + rt_zero_memory(zeroBuf, blockSize); + UInt32 singleLba = ext2_block_to_lba(ctx, singleIndirectBlock); + if (!ext2_write_block(ctx->drive, singleLba, zeroBuf, blockSize)) { + mm_free_ptr(zeroBuf); + mm_free_ptr(doublePtr); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(zeroBuf); + + // Write double-indirect back to disk + UInt32 dblLba = ext2_block_to_lba(ctx, node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX]); + if (!ext2_write_block(ctx->drive, dblLba, doublePtr, blockSize)) { + mm_free_ptr(doublePtr); + return ErrorOr(kErrorDisk); + } + } + + mm_free_ptr(doublePtr); + + // Write to single-indirect block + auto singleRes = ext2_read_block_ptr(ctx, singleIndirectBlock); + if (singleRes.HasError()) return ErrorOr(singleRes.Error()); + + UInt32* singlePtr = singleRes.Leak().Leak(); + singlePtr[secondIdx] = physicalBlockNumber; + + UInt32 singleLba = ext2_block_to_lba(ctx, singleIndirectBlock); + if (!ext2_write_block(ctx->drive, singleLba, singlePtr, blockSize)) { + mm_free_ptr(singlePtr); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(singlePtr); + return ErrorOr(nullptr); + } + + // Triple indirect blocks not implemented + return ErrorOr(kErrorUnimplemented); +} + +// Find a directory entry by name within a directory inode +static ErrorOr ext2_find_dir_entry(Context* ctx, Ext2Node* dirNode, const char* name) { + if (!ctx || !ctx->drive || !dirNode || !name) + return ErrorOr(Kernel::kErrorInvalidData); + + // Check directory type + auto type = (dirNode->inode.fMode >> 12) & 0xF; + if (type != kExt2FileTypeDirectory) + return ErrorOr(Kernel::kErrorInvalidData); + + Kernel::UInt32 blockSize = ctx->BlockSize(); + auto blockBuf = mm_alloc_ptr(blockSize, true, false); + if (!blockBuf) return ErrorOr(Kernel::kErrorHeapOutOfMemory); + + SizeT nameLen = rt_string_len(name); + for (int i = 0; i < EXT2_DIRECT_BLOCKS; ++i) { + Kernel::UInt32 blockNum = dirNode->inode.fBlock[i]; + if (blockNum == 0) continue; + + Kernel::UInt32 lba = ext2_block_to_lba(ctx, blockNum); + if (!ext2_read_block(ctx->drive, lba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return ErrorOr(Kernel::kErrorDisk); + } + + Kernel::UInt32 offset = 0; + while (offset + sizeof(Kernel::UInt32) + sizeof(Kernel::UInt16) <= blockSize) { + auto onDiskEntry = reinterpret_cast((Kernel::UInt8*)blockBuf + offset); + if (onDiskEntry->fRecordLength == 0) break; // corrupted + + if (onDiskEntry->fInode != 0 && onDiskEntry->fNameLength == nameLen) { + // Compare names + if (rt_string_cmp(name, onDiskEntry->fName, nameLen) == 0) { + // Allocate a result sized to hold the name + metadata + SizeT recSize = sizeof(EXT2_DIR_ENTRY); + auto found = (EXT2_DIR_ENTRY*)mm_alloc_ptr(recSize, true, false); + if (!found) { + mm_free_ptr(blockBuf); + return ErrorOr(Kernel::kErrorHeapOutOfMemory); + } + + // Copy only record-length bytes + rt_copy_memory_safe(onDiskEntry, found, onDiskEntry->fRecordLength, recSize); + mm_free_ptr(blockBuf); + return ErrorOr(found); + } + } + offset += onDiskEntry->fRecordLength; + } + } + + mm_free_ptr(blockBuf); + return ErrorOr(Kernel::kErrorFileNotFound); +} + +// Compute ideal record length for a directory name +static inline UInt16 ext2_dir_entry_ideal_len(UInt8 nameLen) { + UInt16 raw = static_cast(8 + nameLen); // 8 = inode(4)+rec_len(2)+name_len(1)+file_type(1) + return static_cast((raw + 3) & ~3u); // align up to 4 +} + +static ErrorOr ext2_add_dir_entry(Context* ctx, Ext2Node* parentDirNode, + const char* name, Kernel::UInt32 inodeNumber, Kernel::UInt8 fileType) { + using namespace Kernel; + + if (!ctx || !ctx->drive || !parentDirNode || !name) + return ErrorOr(kErrorInvalidData); + + UInt32 blockSize = ctx->BlockSize(); + SizeT nameLen = rt_string_len(name); + if (nameLen == 0 || nameLen > 255) + return ErrorOr(kErrorInvalidData); + + UInt16 newRecIdeal = ext2_dir_entry_ideal_len(static_cast(nameLen)); + + auto blockBuf = mm_alloc_ptr(blockSize, true, false); + if (!blockBuf) return ErrorOr(kErrorHeapOutOfMemory); + + for (int bi = 0; bi < EXT2_DIRECT_BLOCKS; ++bi) { + UInt32 blockNum = parentDirNode->inode.fBlock[bi]; + + if (blockNum == 0) { + // Allocate new block + auto groupInfoRes = ext2_get_group_descriptor_info(ctx, parentDirNode->inodeNumber); + if (!groupInfoRes) { + mm_free_ptr(blockBuf); + return ErrorOr(groupInfoRes.Error()); + } + + auto groupInfo = *groupInfoRes.Leak(); // Dereference to get Ext2GroupInfo* + auto allocBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); + if (!allocBlockRes) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(blockBuf); + return ErrorOr(allocBlockRes.Error()); + } + + UInt32 newBlock = *allocBlockRes.Leak(); // Dereference to get UInt32 + UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); + + if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(blockBuf); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + // Zero block & insert entry + rt_zero_memory(blockBuf, blockSize); + auto entry = reinterpret_cast(blockBuf); + entry->fInode = inodeNumber; + entry->fNameLength = static_cast(nameLen); + entry->fFileType = fileType; + entry->fRecordLength = static_cast(blockSize); + rt_copy_memory_safe(const_cast(name), entry->fName, nameLen, blockSize); + + UInt32 blockLba = ext2_block_to_lba(ctx, newBlock); + if (!ext2_write_block(ctx->drive, blockLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return ErrorOr(kErrorDisk); + } + + auto setRes = ext2_set_block_address(ctx, parentDirNode, bi, newBlock); + if (!setRes) { + mm_free_ptr(blockBuf); + return ErrorOr(setRes.Error()); + } + + mm_free_ptr(blockBuf); + return ErrorOr(nullptr); + } + + // read it + UInt32 blockLba = ext2_block_to_lba(ctx, blockNum); + if (!ext2_read_block(ctx->drive, blockLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return ErrorOr(kErrorDisk); + } + + UInt32 offset = 0; + EXT2_DIR_ENTRY* lastEntry = nullptr; + UInt32 lastOffset = 0; + + while (offset < blockSize) { + if (offset + 8 > blockSize) break; + auto e = reinterpret_cast((UInt8*)blockBuf + offset); + if (e->fRecordLength == 0) break; + lastEntry = e; + lastOffset = offset; + offset += e->fRecordLength; + } + + if (!lastEntry) continue; + + UInt16 lastIdeal = ext2_dir_entry_ideal_len(lastEntry->fNameLength); + + if (lastEntry->fRecordLength >= (UInt16)(lastIdeal + newRecIdeal)) { + UInt16 origRec = lastEntry->fRecordLength; + lastEntry->fRecordLength = lastIdeal; + + UInt32 newOffset = lastOffset + lastIdeal; + auto newEntry = reinterpret_cast((UInt8*)blockBuf + newOffset); + newEntry->fInode = inodeNumber; + newEntry->fNameLength = static_cast(nameLen); + newEntry->fFileType = fileType; + newEntry->fRecordLength = static_cast(origRec - lastIdeal); + rt_copy_memory_safe(const_cast(name), newEntry->fName, nameLen, newEntry->fRecordLength); + + if (!ext2_write_block(ctx->drive, blockLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(blockBuf); + return ErrorOr(nullptr); + } + } + + // No space in direct blocks -> allocate new block + int targetIndex = -1; + for (int i = 0; i < EXT2_DIRECT_BLOCKS; ++i) { + if (parentDirNode->inode.fBlock[i] == 0) { targetIndex = i; break; } + } + if (targetIndex == -1) { + mm_free_ptr(blockBuf); + return ErrorOr(kErrorUnimplemented); + } + + auto groupInfoResult = ext2_get_group_descriptor_info(ctx, parentDirNode->inodeNumber); + if (!groupInfoResult) { + mm_free_ptr(blockBuf); + return ErrorOr(groupInfoResult.Error()); + } + + auto groupInfo = *groupInfoResult.Leak(); // Dereference to get Ext2GroupInfo* + auto newBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); + if (!newBlockRes) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(blockBuf); + return ErrorOr(newBlockRes.Error()); + } + + UInt32 newBlockNum = *newBlockRes.Leak(); // Dereference to get UInt32 + UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(blockBuf); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + rt_zero_memory(blockBuf, blockSize); + auto entry = reinterpret_cast(blockBuf); + entry->fInode = inodeNumber; + entry->fNameLength = static_cast(nameLen); + entry->fFileType = fileType; + entry->fRecordLength = static_cast(blockSize); + rt_copy_memory_safe(const_cast(name), entry->fName, nameLen, blockSize); + + UInt32 newBlockLba = ext2_block_to_lba(ctx, newBlockNum); + if (!ext2_write_block(ctx->drive, newBlockLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return ErrorOr(kErrorDisk); + } + + auto setRes = ext2_set_block_address(ctx, parentDirNode, targetIndex, newBlockNum); + if (!setRes) { + mm_free_ptr(blockBuf); + return ErrorOr(setRes.Error()); + } + + mm_free_ptr(blockBuf); + return ErrorOr(nullptr); +} + +// Soon +static ErrorOr ext2_alloc_inode(Context* ctx, EXT2_GROUP_DESCRIPTOR* groupDesc) { + if (!ctx || !ctx->superblock || !groupDesc) + return ErrorOr(kErrorInvalidData); + + UInt32 blockSize = ctx->BlockSize(); + + // buffer for the inode bitmap + auto bitmap = mm_alloc_ptr(blockSize, true, false); + if (!bitmap) return ErrorOr(kErrorHeapOutOfMemory); + + // Read inode bitmap + if (!ext2_read_block(ctx->drive, groupDesc->fInodeBitmap, bitmap, blockSize)) { + mm_free_ptr(bitmap); + return ErrorOr(kErrorDisk); + } + + // Find first free inode (bit = 0) + for (UInt32 byteIdx = 0; byteIdx < blockSize; ++byteIdx) { + auto byte = reinterpret_cast(bitmap)[byteIdx]; + if (byte != 0xFF) { + for (int bit = 0; bit < 8; ++bit) { + if (!(byte & (1 << bit))) { + // Mark bit as used + reinterpret_cast(bitmap)[byteIdx] |= (1 << bit); + + // Compute inode number + UInt32 inodeNumber = byteIdx * 8 + bit + 1; // Inodes are 1-based + + // Write bitmap back + if (!ext2_write_block(ctx->drive, groupDesc->fInodeBitmap, bitmap, blockSize)) { + mm_free_ptr(bitmap); + return ErrorOr(kErrorDisk); + } + + // Update group descriptor free count + groupDesc->fFreeInodesCount--; + mm_free_ptr(bitmap); + return ErrorOr(inodeNumber); + } + } + } + } + + mm_free_ptr(bitmap); + return ErrorOr(kErrorDiskIsFull); +} + +// to write an inode to its correct location on disk +static ErrorOr ext2_write_inode(Context* ctx, Ext2Node* node) { + using namespace Kernel; + + if (!ctx || !ctx->superblock || !ctx->drive || !node) + return ErrorOr(kErrorInvalidData); + + auto blockSize = ctx->BlockSize(); + UInt32 inodesPerGroup = ctx->superblock->fInodesPerGroup; + + if (inodesPerGroup == 0) return ErrorOr(kErrorInvalidData); + + // Calculate which group this inode belongs to + UInt32 groupIndex = (node->inodeNumber - 1) / inodesPerGroup; + UInt32 inodeIndexInGroup = (node->inodeNumber - 1) % inodesPerGroup; + + // Get group descriptor + auto groupInfoResult = ext2_get_group_descriptor_info(ctx, node->inodeNumber); + if (!groupInfoResult) return ErrorOr(groupInfoResult.Error()); + + auto groupInfo = *groupInfoResult.Leak(); // Dereference to get Ext2GroupInfo* + + // Calculate inode table position + UInt32 inodeTableBlock = groupInfo->groupDesc->fInodeTable; + UInt32 inodeSize = ctx->superblock->fInodeSize; + UInt32 inodesPerBlock = blockSize / inodeSize; + + UInt32 blockOffset = inodeIndexInGroup / inodesPerBlock; + UInt32 offsetInBlock = (inodeIndexInGroup % inodesPerBlock) * inodeSize; + + UInt32 inodeBlock = inodeTableBlock + blockOffset; + UInt32 inodeLba = ext2_block_to_lba(ctx, inodeBlock); + + // Read the block containing the inode + auto blockBuf = mm_alloc_ptr(blockSize, true, false); + if (!blockBuf) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(kErrorHeapOutOfMemory); + } + + if (!ext2_read_block(ctx->drive, inodeLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(kErrorDisk); + } + + // Copy the updated inode into the block buffer + rt_copy_memory_safe(&node->inode, static_cast((UInt8*)blockBuf + offsetInBlock), + sizeof(EXT2_INODE), blockSize - offsetInBlock); + + // Write the block back + if (!ext2_write_block(ctx->drive, inodeLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(blockBuf); + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + return ErrorOr(nullptr); +} + +namespace { + // new + struct PathComponents { + const char** components; + int count; + Char* buffer; + + PathComponents(const char* path) : components(nullptr), count(0), buffer(nullptr) { + if (!path || *path == '\0') return; + + SizeT pathLen = rt_string_len(path); + buffer = (Char*)mm_alloc_ptr(pathLen + 1, true, false); + if (!buffer) return; + + rt_copy_memory_safe((void*)path, buffer, pathLen, pathLen + 1); + buffer[pathLen] = '\0'; + + // temp array + const char** temp = (const char**)mm_alloc_ptr(sizeof(char*) * (pathLen + 1), true, false); + if (!temp) { + mm_free_ptr(buffer); + buffer = nullptr; + return; + } + + int compCount = 0; + Char* p = buffer; + + while (*p != '\0') { + // skip slashes + while (*p == '/') p++; + if (*p == '\0') break; + + Char* start = p; + while (*p != '/' && *p != '\0') p++; + Char saved = *p; + *p = '\0'; + + // handle ".", "..", or normal + if (rt_string_cmp(start, ".", 1) == 0) { + // ignore + } else if (rt_string_cmp(start, "..", 2) == 0) { + if (compCount > 0) compCount--; // go up one level + } else { + temp[compCount++] = start; + } + + *p = saved; + } + + if (compCount == 0) { + mm_free_ptr(temp); + return; + } + + components = (const char**)mm_alloc_ptr(sizeof(char*) * compCount, true, false); + if (!components) { + mm_free_ptr(temp); + return; + } + + for (int i = 0; i < compCount; i++) components[i] = temp[i]; + count = compCount; + + mm_free_ptr(temp); + } + + ~PathComponents() { + if (components) mm_free_ptr(components); + if (buffer) mm_free_ptr(buffer); + } +}; +} // anonymous namespace + +// The Ext2FileSystemMgr +Ext2FileSystemMgr::Ext2FileSystemMgr(DriveTrait* drive) : ctx(drive) {} +NodePtr Ext2FileSystemMgr::Open(const char* path, const char* restrict_type) { + if (!path || *path == '\0' || !this->ctx.drive) { + return nullptr; + } + + // Root ("/") + if (rt_string_len(path) == 1 && rt_string_cmp(path, "/", 1) == 0) { + auto inodeResult = ext2_load_inode(&this->ctx, EXT2_ROOT_INODE); + if (!inodeResult) { + return nullptr; + } + + auto heapNode = (Ext2Node*)mm_alloc_ptr(sizeof(Ext2Node), true, false); + if (!heapNode) return nullptr; + + *heapNode = *inodeResult.Leak(); + heapNode->cursor = 0; + return reinterpret_cast(heapNode); + } + + PathComponents pathComponents(path); + if (pathComponents.count == 0) { + return nullptr; + } + + UInt32 currentInodeNumber = EXT2_ROOT_INODE; + Ext2Node* currentDirNode = nullptr; + + for (int i = 0; i < pathComponents.count; ++i) { + auto inodeResult = ext2_load_inode(&this->ctx, currentInodeNumber); + if (!inodeResult) { + if (currentDirNode) mm_free_ptr(currentDirNode); + return nullptr; + } + + if (currentDirNode) { + mm_free_ptr(currentDirNode); + currentDirNode = nullptr; + } + + currentDirNode = (Ext2Node*)mm_alloc_ptr(sizeof(Ext2Node), true, false); + if (!currentDirNode) { + return nullptr; + } + + *currentDirNode = *inodeResult.Leak(); + currentDirNode->cursor = 0; + + if (i < pathComponents.count - 1) { + UInt32 type = (currentDirNode->inode.fMode >> 12) & 0xF; + if (type != kExt2FileTypeDirectory) { + mm_free_ptr(currentDirNode); + return nullptr; + } + } + + auto dirEntryResult = ext2_find_dir_entry(&this->ctx, currentDirNode, pathComponents.components[i]); + if (!dirEntryResult) { + mm_free_ptr(currentDirNode); + return nullptr; + } + + EXT2_DIR_ENTRY* entryPtr = *dirEntryResult.Leak(); + currentInodeNumber = entryPtr->fInode; + mm_free_ptr(entryPtr); + } + + auto finalInodeResult = ext2_load_inode(&this->ctx, currentInodeNumber); + if (!finalInodeResult) { + if (currentDirNode) mm_free_ptr(currentDirNode); + return nullptr; + } + + if (currentDirNode) { + mm_free_ptr(currentDirNode); + } + + auto resultNode = (Ext2Node*)mm_alloc_ptr(sizeof(Ext2Node), true, false); + if (!resultNode) { + return nullptr; + } + + *resultNode = *finalInodeResult.Leak(); + resultNode->cursor = 0; + return reinterpret_cast(resultNode); +} + + +void* Ext2FileSystemMgr::Read(NodePtr node, Int32 flags, SizeT size) { + if (!node) return nullptr; + + auto extNode = reinterpret_cast(node); + auto dataResult = ext2_read_inode_data(&this->ctx, extNode, size); + + if (!dataResult) { + return nullptr; // error, nothing to return + } + + void* data = *dataResult.Leak(); + if (data) { + extNode->cursor += static_cast(size); + } + + return data; +} + + +void Ext2FileSystemMgr::Write(NodePtr node, void* data, Int32 flags, SizeT size) { + if (!node || !data || size == 0) return; + + auto extNode = reinterpret_cast(node); + auto blockSize = this->ctx.BlockSize(); + SizeT bytesWritten = 0; + + UInt32 currentOffset = extNode->cursor; + UInt8* src = reinterpret_cast(data); + + while (bytesWritten < size) { + UInt32 logicalBlockIndex = currentOffset / blockSize; + UInt32 offsetInBlock = currentOffset % blockSize; + + auto physBlockResult = ext2_get_block_address(&this->ctx, extNode, logicalBlockIndex); + UInt32 physicalBlock = 0; + + if (!physBlockResult) { + auto err = physBlockResult.Error(); + if (err == kErrorInvalidData || err == kErrorUnimplemented) { + auto groupInfoResult = ext2_get_group_descriptor_info(&this->ctx, extNode->inodeNumber); + if (!groupInfoResult) { + return; + } + + auto groupInfo = *groupInfoResult.Leak(); + auto allocResult = ext2_alloc_block(&this->ctx, groupInfo->groupDesc); + if (!allocResult) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return; + } + + physicalBlock = *allocResult.Leak(); + + auto setRes = ext2_set_block_address(&this->ctx, extNode, logicalBlockIndex, physicalBlock); + if (!setRes) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return; + } + + UInt32 gdtLba = ext2_block_to_lba(&this->ctx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(this->ctx.drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return; + } + + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + } else { + return; + } + } else { + physicalBlock = physBlockResult.Value(); + } + + UInt32 physicalLba = ext2_block_to_lba(&this->ctx, physicalBlock); + + auto blockBuf = mm_alloc_ptr(blockSize, true, false); + if (!blockBuf) return; + + if (offsetInBlock > 0 || (size - bytesWritten) < blockSize) { + if (!ext2_read_block(this->ctx.drive, physicalLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return; + } + } else { + rt_zero_memory(blockBuf, blockSize); + } + + UInt32 bytesInCurrentBlock = static_cast(ext2_min(size - bytesWritten, blockSize - offsetInBlock)); + rt_copy_memory_safe(src, static_cast((UInt8*)blockBuf + offsetInBlock), + bytesInCurrentBlock, blockSize - offsetInBlock); + + if (!ext2_write_block(this->ctx.drive, physicalLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return; + } + + mm_free_ptr(blockBuf); + + currentOffset += bytesInCurrentBlock; + src += bytesInCurrentBlock; + bytesWritten += bytesInCurrentBlock; + } + + if (currentOffset > extNode->inode.fSize) { + extNode->inode.fSize = currentOffset; + } + + extNode->inode.fBlocks = (extNode->inode.fSize + blockSize - 1) / blockSize; + extNode->inode.fModifyTime = 0; + + auto writeInodeRes = ext2_write_inode(&this->ctx, extNode); + if (!writeInodeRes) { + // Failed to persist inode + } + + extNode->cursor = currentOffset; +} + +bool Ext2FileSystemMgr::Seek(NodePtr node, SizeT offset) { + if (!node) return false; + auto extNode = reinterpret_cast(node); + extNode->cursor = static_cast(offset); + return true; +} + +SizeT Ext2FileSystemMgr::Tell(NodePtr node) { + if (!node) return 0; + auto extNode = reinterpret_cast(node); + return extNode->cursor; +} + +bool Ext2FileSystemMgr::Rewind(NodePtr node) { + if (!node) return false; + auto extNode = reinterpret_cast(node); + extNode->cursor = 0; + return true; +} + +void* Ext2FileSystemMgr::Read(const char* name, NodePtr node, Int32 flags, SizeT size) { + return Read(node, flags, size); +} + +void Ext2FileSystemMgr::Write(const char* name, NodePtr node, void* data, Int32 flags, SizeT size) { + Write(node, data, flags, size); +} + +NodePtr Ext2FileSystemMgr::Create(const char* path) { + if (!path || *path == '\0') return nullptr; + + PathComponents pathComponents(path); + if (pathComponents.count == 0) return nullptr; + + const char* filename = pathComponents.components[pathComponents.count - 1]; + if (rt_string_len(filename) > kExt2FSMaxFileNameLen) return nullptr; + + // Build parent path + Char parentPathBuf[256] = {0}; + SizeT currentPathLen = 0; + for (int i = 0; i < pathComponents.count - 1; ++i) { + SizeT componentLen = rt_string_len(pathComponents.components[i]); + if (currentPathLen + componentLen + 1 >= sizeof(parentPathBuf)) return nullptr; + if (i > 0) parentPathBuf[currentPathLen++] = '/'; + rt_copy_memory_safe(const_cast(pathComponents.components[i]), + parentPathBuf + currentPathLen, + componentLen, sizeof(parentPathBuf) - currentPathLen); + currentPathLen += componentLen; + } + parentPathBuf[currentPathLen] = '\0'; + + // Open parent directory + NodePtr parentDirNodePtr = nullptr; + if (currentPathLen == 0) { + // root + auto inodeRes = ext2_load_inode(&this->ctx, EXT2_ROOT_INODE); + if (!inodeRes) return nullptr; + parentDirNodePtr = mm_alloc_ptr(sizeof(Ext2Node), true, false); + if (!parentDirNodePtr) return nullptr; + *reinterpret_cast(parentDirNodePtr) = *inodeRes.Leak(); + reinterpret_cast(parentDirNodePtr)->cursor = 0; + } else { + parentDirNodePtr = Open(parentPathBuf, "r"); + } + + if (!parentDirNodePtr) return nullptr; + + auto parentDirNode = reinterpret_cast(parentDirNodePtr); + + // Ensure parent is a directory + UInt32 type = (parentDirNode->inode.fMode >> 12) & 0xF; + if (type != kExt2FileTypeDirectory) { + mm_free_ptr(parentDirNode); + return nullptr; + } + + // Get group info for allocation + auto groupInfoResult = ext2_get_group_descriptor_info(&this->ctx, parentDirNode->inodeNumber); + if (!groupInfoResult) { + mm_free_ptr(parentDirNode); + return nullptr; + } + auto groupInfo = *groupInfoResult.Leak(); + + // Allocate new inode + auto newInodeRes = ext2_alloc_inode(&this->ctx, groupInfo->groupDesc); + if (!newInodeRes) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); // so this works + mm_free_ptr(parentDirNode); + return nullptr; + } + UInt32 newInodeNumber = newInodeRes.Value(); + + UInt32 gdtLba = ext2_block_to_lba(&this->ctx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(this->ctx.drive, gdtLba, groupInfo->blockBuffer, this->ctx.BlockSize())) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(parentDirNode); + return nullptr; + } + + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + // Create new Ext2Node + Ext2Node* newFileNode = reinterpret_cast(mm_alloc_ptr(sizeof(Ext2Node), true, false)); + if (!newFileNode) { + mm_free_ptr(parentDirNode); + return nullptr; + } + + newFileNode->inodeNumber = newInodeNumber; + rt_zero_memory(&newFileNode->inode, sizeof(EXT2_INODE)); + + newFileNode->inode.fMode = (kExt2FileTypeRegular << 12); + newFileNode->inode.fUID = 0; + newFileNode->inode.fGID = 0; + newFileNode->inode.fLinksCount = 1; + newFileNode->inode.fSize = 0; + newFileNode->inode.fBlocks = 0; + newFileNode->inode.fCreateTime = 0; + newFileNode->inode.fModifyTime = 0; + + // Persist new inode + auto writeInodeRes = ext2_write_inode(&this->ctx, newFileNode); + if (!writeInodeRes) { + mm_free_ptr(parentDirNode); + mm_free_ptr(newFileNode); + return nullptr; + } + + // Add directory entry + auto addRes = ext2_add_dir_entry(&this->ctx, parentDirNode, filename, newInodeNumber, kExt2FileTypeRegular); + if (!addRes) { + mm_free_ptr(parentDirNode); + mm_free_ptr(newFileNode); + return nullptr; + } + + // Update parent inode + auto parentWriteRes = ext2_write_inode(&this->ctx, parentDirNode); + // ignore failure + + mm_free_ptr(parentDirNode); + return reinterpret_cast(newFileNode); +} + +NodePtr Ext2FileSystemMgr::CreateDirectory(const char* path) { + if (!path || *path == '\0') return nullptr; + + PathComponents pathComponents(path); + if (pathComponents.count == 0) { + kout << "EXT2: Failed to parse path for CreateDirectory.\n"; + return nullptr; + } + + const char* dirname = pathComponents.components[pathComponents.count - 1]; + if (rt_string_len(dirname) > kExt2FSMaxFileNameLen) { + kout << "EXT2: Directory name too long: " << dirname << ".\n"; + return nullptr; + } + + // Build parent path + Char parentPathBuf[256]; + SizeT currentPathLen = 0; + for (int i = 0; i < pathComponents.count - 1; ++i) { + SizeT componentLen = rt_string_len(pathComponents.components[i]); + if (currentPathLen + componentLen + 1 >= sizeof(parentPathBuf)) { + kout << "EXT2: Parent path too long for CreateDirectory.\n"; + return nullptr; + } + + if (i > 0) parentPathBuf[currentPathLen++] = '/'; + + rt_copy_memory_safe(static_cast(const_cast(pathComponents.components[i])), + static_cast(parentPathBuf + currentPathLen), + componentLen, sizeof(parentPathBuf) - currentPathLen); + currentPathLen += componentLen; + } + + parentPathBuf[currentPathLen] = '\0'; + + // Open parent directory node + NodePtr parentDirNodePtr = nullptr; + if (currentPathLen == 0) { + auto inodeRes = ext2_load_inode(&this->ctx, EXT2_ROOT_INODE); + if (!inodeRes) { + return nullptr; + } + + parentDirNodePtr = reinterpret_cast(mm_alloc_ptr(sizeof(Ext2Node), true, false)); + if (!parentDirNodePtr) return nullptr; + + *reinterpret_cast(parentDirNodePtr) = *inodeRes.Leak(); + reinterpret_cast(parentDirNodePtr)->cursor = 0; + } else { + parentDirNodePtr = Open(parentPathBuf, "r"); + } + + if (!parentDirNodePtr) { + kout << "EXT2: Failed to open parent directory for CreateDirectory: " << parentPathBuf << ".\n"; + return nullptr; + } + + auto parentDirNode = reinterpret_cast(parentDirNodePtr); + + // Check parent is a directory + UInt32 parentType = (parentDirNode->inode.fMode >> 12) & 0xF; + if (parentType != kExt2FileTypeDirectory) { + kout << "EXT2: Parent is not a directory: " << parentPathBuf << ".\n"; + mm_free_ptr(parentDirNode); + return nullptr; + } + + // Allocate inode + auto groupInfoResult = ext2_get_group_descriptor_info(&this->ctx, parentDirNode->inodeNumber); + if (!groupInfoResult) { + kout << "EXT2: Failed to get group descriptor info for new dir inode.\n"; + mm_free_ptr(parentDirNode); + return nullptr; + } + + auto groupInfo = *groupInfoResult.Leak(); + auto newInodeRes = ext2_alloc_inode(&this->ctx, groupInfo->groupDesc); + if (!newInodeRes) { + kout << "EXT2: Failed to allocate inode for new directory.\n"; + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(parentDirNode); + return nullptr; + } + + UInt32 newInodeNumber = *newInodeRes.Leak(); + + // Write back group descriptor block + UInt32 gdtLba = ext2_block_to_lba(&this->ctx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(this->ctx.drive, gdtLba, groupInfo->blockBuffer, this->ctx.BlockSize())) { + kout << "EXT2: Failed to write group descriptor after inode allocation.\n"; + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(parentDirNode); + return nullptr; + } + + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + // Create new Ext2Node and initialize inode fields + Ext2Node* newDirNode = reinterpret_cast(mm_alloc_ptr(sizeof(Ext2Node), true, false)); + if (!newDirNode) { + kout << "EXT2: Out of memory for new directory node.\n"; + mm_free_ptr(parentDirNode); + return nullptr; + } + + newDirNode->inodeNumber = newInodeNumber; + rt_zero_memory(&newDirNode->inode, sizeof(EXT2_INODE)); + newDirNode->inode.fMode = (kExt2FileTypeDirectory << 12); + newDirNode->inode.fUID = 0; + newDirNode->inode.fGID = 0; + newDirNode->inode.fLinksCount = 2; // . and .. + newDirNode->inode.fSize = this->ctx.BlockSize(); + newDirNode->inode.fBlocks = 1; + newDirNode->inode.fCreateTime = 0; + newDirNode->inode.fModifyTime = 0; + + // Allocate a data block for the new directory + auto groupForBlockRes = ext2_get_group_descriptor_info(&this->ctx, newDirNode->inodeNumber); + if (!groupForBlockRes) { + kout << "EXT2: Failed to get group info for directory block allocation.\n"; + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + auto groupForBlock = *groupForBlockRes.Leak(); + auto newBlockRes = ext2_alloc_block(&this->ctx, groupForBlock->groupDesc); + if (!newBlockRes) { + kout << "EXT2: Failed to allocate block for new directory contents.\n"; + mm_free_ptr(reinterpret_cast(groupForBlock->blockBuffer)); + mm_free_ptr(groupForBlock); + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + UInt32 newDirBlockNum = *newBlockRes.Leak(); + + // Write back GDT + UInt32 gdtLba2 = ext2_block_to_lba(&this->ctx, groupForBlock->groupDescriptorBlock); + if (!ext2_write_block(this->ctx.drive, gdtLba2, groupForBlock->blockBuffer, this->ctx.BlockSize())) { + kout << "EXT2: Failed to write GDT after directory block allocation.\n"; + mm_free_ptr(reinterpret_cast(groupForBlock->blockBuffer)); + mm_free_ptr(groupForBlock); + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + mm_free_ptr(reinterpret_cast(groupForBlock->blockBuffer)); + mm_free_ptr(groupForBlock); + + // Set the block in newDirNode + auto setBlkRes = ext2_set_block_address(&this->ctx, newDirNode, 0, newDirBlockNum); + if (!setBlkRes) { + kout << "EXT2: Failed to set data block for new directory.\n"; + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + // Prepare block with '.' and '..' + auto dirBlockBuf = mm_alloc_ptr(this->ctx.BlockSize(), true, false); + if (!dirBlockBuf) { + kout << "EXT2: Out of memory preparing directory block.\n"; + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + rt_zero_memory(dirBlockBuf, this->ctx.BlockSize()); + + // '.' entry + auto dot = reinterpret_cast(dirBlockBuf); + dot->fInode = newInodeNumber; + dot->fNameLength = 1; + dot->fFileType = kExt2FileTypeDirectory; + dot->fRecordLength = ext2_dir_entry_ideal_len(dot->fNameLength); + dot->fName[0] = '.'; + + // '..' entry occupies rest of block + auto dotdot = reinterpret_cast((UInt8*)dirBlockBuf + dot->fRecordLength); + dotdot->fInode = parentDirNode->inodeNumber; + dotdot->fNameLength = 2; + dotdot->fFileType = kExt2FileTypeDirectory; + dotdot->fRecordLength = static_cast(this->ctx.BlockSize() - dot->fRecordLength); + dotdot->fName[0] = '.'; + dotdot->fName[1] = '.'; + + // Write dir block to disk + UInt32 newDirBlockLba = ext2_block_to_lba(&this->ctx, newDirBlockNum); + if (!ext2_write_block(this->ctx.drive, newDirBlockLba, dirBlockBuf, this->ctx.BlockSize())) { + kout << "EXT2: Failed to write directory block to disk.\n"; + mm_free_ptr(dirBlockBuf); + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + mm_free_ptr(dirBlockBuf); + + // Persist new directory inode + auto writeInodeRes = ext2_write_inode(&this->ctx, newDirNode); + if (!writeInodeRes) { + kout << "EXT2: Failed to write new directory inode to disk.\n"; + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + // Add directory entry into parent + auto addRes = ext2_add_dir_entry(&this->ctx, parentDirNode, dirname, newInodeNumber, kExt2FileTypeDirectory); + if (!addRes) { + kout << "EXT2: Failed to add directory entry for '" << dirname << "' to parent.\n"; + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + // Increment parent link count and persist parent inode + parentDirNode->inode.fLinksCount += 1; + auto parentWriteRes = ext2_write_inode(&this->ctx, parentDirNode); + if (!parentWriteRes) { + kout << "EXT2: Warning: failed to update parent inode after directory creation.\n"; + } + + mm_free_ptr(parentDirNode); + return reinterpret_cast(newDirNode); +} + +} // namespace Ext2 -#endif // ifdef __FSKIT_INCLUDES_EXT2__ -#endif // ifndef __NE_MINIMAL_OS__ +#endif +#endif -- cgit v1.2.3 From 84a7325b22f1f90c0c719a2ec8ba131263e1208c Mon Sep 17 00:00:00 2001 From: Amlal El Mahrouss Date: Sun, 28 Sep 2025 14:58:47 +0200 Subject: feat: final changes before nekernel v0.0.6 Signed-off-by: Amlal El Mahrouss --- dev/kernel/FSKit/Ext2+IFS.h | 155 +- dev/kernel/FSKit/Ext2.h | 17 +- dev/kernel/HALKit/AMD64/HalDebugOutput.cc | 2 + dev/kernel/KernelKit/FileMgr.h | 48 +- dev/kernel/NeKit/ErrorOr.h | 4 +- dev/kernel/NeKit/Ref.h | 2 +- dev/kernel/amd64-desktop.make | 2 +- dev/kernel/src/FS/Ext2+FileMgr.cc | 2599 +++++++++++++++-------------- 8 files changed, 1513 insertions(+), 1316 deletions(-) (limited to 'dev/kernel/src') diff --git a/dev/kernel/FSKit/Ext2+IFS.h b/dev/kernel/FSKit/Ext2+IFS.h index d73ae43c..2a28fe17 100644 --- a/dev/kernel/FSKit/Ext2+IFS.h +++ b/dev/kernel/FSKit/Ext2+IFS.h @@ -8,11 +8,12 @@ #include #include -namespace Kernel::Ext2 { +namespace Kernel { /// @brief Context for an EXT2 filesystem on a given drive -struct Ext2Context { - Kernel::DriveTrait* drive{nullptr}; - EXT2_SUPER_BLOCK* superblock{nullptr}; +class Ext2Context final { + public: + DriveTrait* drive{nullptr}; + EXT2_SUPER_BLOCK* superblock{nullptr}; /// @brief context with a drive Ext2Context(Kernel::DriveTrait* drv) : drive(drv) {} @@ -46,19 +47,19 @@ struct Ext2Context { return *this; } - Kernel::SizeT LeakBlockSize() const { + SizeT BlockSize() const { if (!superblock) return kExt2FSBlockSizeBase; return kExt2FSBlockSizeBase << superblock->fLogBlockSize; } - BOOL operator bool() { return superblock != nullptr; } + operator BOOL() { return superblock != nullptr; } }; /// ======================================================================= /// /// IFS FUNCTIONS /// ======================================================================= /// -inline bool ext2_read_block(Kernel::DriveTrait* drv, Kernel::UInt32 lba, void* buffer, +inline BOOL ext2_read_block(Kernel::DriveTrait* drv, Kernel::UInt32 lba, VoidPtr buffer, Kernel::UInt32 size) { if (!drv || !buffer) return false; @@ -71,12 +72,12 @@ inline bool ext2_read_block(Kernel::DriveTrait* drv, Kernel::UInt32 lba, void* b return pkt.fPacketGood; } -inline bool ext2_write_block(Kernel::DriveTrait* drv, Kernel::UInt32 lba, const void* buffer, +inline BOOL ext2_write_block(Kernel::DriveTrait* drv, Kernel::UInt32 lba, const VoidPtr buffer, Kernel::UInt32 size) { if (!drv || !buffer) return false; Kernel::DriveTrait::DrivePacket pkt{}; - pkt.fPacketContent = const_cast(buffer); + pkt.fPacketContent = const_cast(buffer); pkt.fPacketSize = size; pkt.fPacketLba = lba; drv->fOutput(pkt); @@ -119,7 +120,6 @@ inline Kernel::ErrorOr ext2_load_inode(Ext2Context* ctx, Kernel::UInt // Compute block group and index within group Kernel::UInt32 inodesPerGroup = ctx->superblock->fInodesPerGroup; Kernel::UInt32 group = (inodeNumber - 1) / inodesPerGroup; - Kernel::UInt32 index = (inodeNumber - 1) % inodesPerGroup; // dummy: just offset first inode Kernel::UInt32 inodeTableBlock = ctx->superblock->fFirstInode + group; @@ -133,8 +133,133 @@ inline Kernel::ErrorOr ext2_load_inode(Ext2Context* ctx, Kernel::UInt return Kernel::ErrorOr(ext2Node); } -inline Kernel::UInt32 inode_offset(const Ext2Context* ctx, Kernel::UInt32 inodeNumber) { - if (!ctx || !ctx->superblock) return 0; - return ((inodeNumber - 1) % ctx->superblock->fInodesPerGroup) * ctx->superblock->fInodeSize; -} -} // namespace Kernel::Ext2 +/* + * Ext2FileSystemParser Class + * + * Provides high-level interface for EXT2 filesystem operations + */ +class Ext2FileSystemParser { + private: + Ext2Context ctx; // Internal EXT2 context + + public: + /* + * Constructor + * Initializes the parser with a drive interface + * + * @param drive: Pointer to drive trait for disk I/O operations + */ + explicit Ext2FileSystemParser(DriveTrait* drive); + + /* + * Open a file or directory by path + * + * @param path: Full path to the file/directory (e.g., "/home/user/file.txt") + * @param restrict_type: Access mode restriction (e.g., "r", "w", "rw") + * @return: VoidPtr handle to the opened file/directory, or nullptr on failure + */ + VoidPtr Open(const char* path, const char* restrict_type); + + /* + * Read data from an open file node + * + * @param node: File node handle returned by Open() + * @param flags: Read operation flags + * @param size: Number of bytes to read + * @return: Pointer to allocated buffer containing read data, or nullptr on failure + * Caller is responsible for freeing the returned buffer + */ + VoidPtr Read(VoidPtr node, Int32 flags, SizeT size); + + /* + * Write data to an open file node + * + * @param node: File node handle returned by Open() + * @param data: Buffer containing data to write + * @param flags: Write operation flags + * @param size: Number of bytes to write + */ + Void Write(VoidPtr node, VoidPtr data, Int32 flags, SizeT size); + + /* + * Seek to a specific position in the file + * + * @param node: File node handle + * @param offset: Byte offset from beginning of file + * @return: true on success, false on failure + */ + BOOL Seek(VoidPtr node, SizeT offset); + + /* + * Get current position in the file + * + * @param node: File node handle + * @return: Current byte offset from beginning of file + */ + SizeT Tell(VoidPtr node); + + /* + * Reset file position to beginning + * + * @param node: File node handle + * @return: true on success, false on failure + */ + BOOL Rewind(VoidPtr node); + + /* + * Read data from a named file within a directory node + * + * @param name: Name of file within the directory + * @param node: Directory node handle + * @param flags: Read operation flags + * @param size: Number of bytes to read + * @return: Pointer to allocated buffer containing read data, or nullptr on failure + */ + VoidPtr Read(const char* name, VoidPtr node, Int32 flags, SizeT size); + + /* + * Write data to a named file within a directory node + * + * @param name: Name of file within the directory + * @param node: Directory node handle + * @param data: Buffer containing data to write + * @param flags: Write operation flags + * @param size: Number of bytes to write + */ + Void Write(const char* name, VoidPtr node, VoidPtr data, Int32 flags, SizeT size); + + /* + * Create a new regular file + * + * @param path: Full path for the new file + * @return: VoidPtr handle to the created file, or nullptr on failure + */ + VoidPtr Create(const char* path); + + /* + * Create a new directory + * + * @param path: Full path for the new directory + * @return: VoidPtr handle to the created directory, or nullptr on failure + */ + VoidPtr CreateDirectory(const char* path); + + /* + * Close and free a file/directory node + * Note: This method is not shown in the implementation but would typically be needed + * + * @param node: Node handle to close and free + */ + Void Close(VoidPtr node); + + /* + * Get file/directory information + * Note: This method is not shown in the implementation but would typically be needed + * + * @param node: Node handle + * @param info: Structure to fill with file information (size, type, permissions, etc.) + * @return: true on success, false on failure + */ + BOOL GetInfo(VoidPtr node, VoidPtr info); +}; +} // namespace Kernel diff --git a/dev/kernel/FSKit/Ext2.h b/dev/kernel/FSKit/Ext2.h index 06370a8c..be2e34a6 100644 --- a/dev/kernel/FSKit/Ext2.h +++ b/dev/kernel/FSKit/Ext2.h @@ -15,8 +15,6 @@ /// @file Ext2.h /// @brief EXT2 filesystem structures, constants, and base wrappers. -namespace Ext2 { - /// EXT2 Constants #define kExt2FSMagic (0xEF53) #define kExt2FSMaxFileNameLen (255U) @@ -40,6 +38,17 @@ enum { kExt2FileTypeSymbolicLink = 7 }; +typedef struct EXT2_GROUP_DESCRIPTOR final { + UInt32 fBlockBitmap; + UInt32 fInodeBitmap; + UInt32 fInodeTable; + UInt16 fFreeBlocksCount; + UInt16 fFreeInodesCount; + UInt16 fBgUsedDirsCount; + UInt16 fBgPad; + UInt32 fBgReserved[3]; +} EXT2_GROUP_DESCRIPTOR; + struct PACKED EXT2_SUPER_BLOCK final { Kernel::UInt32 fInodeCount; Kernel::UInt32 fBlockCount; @@ -137,7 +146,3 @@ struct Ext2Node { EXT2_INODE inode; Kernel::UInt32 cursor{0}; }; - -class Ext2FileSystemMgr; - -} // namespace Ext2 diff --git a/dev/kernel/HALKit/AMD64/HalDebugOutput.cc b/dev/kernel/HALKit/AMD64/HalDebugOutput.cc index 1e9fbab3..789c1067 100644 --- a/dev/kernel/HALKit/AMD64/HalDebugOutput.cc +++ b/dev/kernel/HALKit/AMD64/HalDebugOutput.cc @@ -57,7 +57,9 @@ namespace Detail { TerminalDevice::~TerminalDevice() = default; +#ifdef __DEBUG__ STATIC SizeT kX = kFontSizeX, kY = kFontSizeY; +#endif // __DEBUG__ EXTERN_C void ke_utf_io_write(DeviceInterface* obj, const Utf8Char* bytes) { NE_UNUSED(bytes); diff --git a/dev/kernel/KernelKit/FileMgr.h b/dev/kernel/KernelKit/FileMgr.h index f925a96c..bad6cf85 100644 --- a/dev/kernel/KernelKit/FileMgr.h +++ b/dev/kernel/KernelKit/FileMgr.h @@ -27,7 +27,7 @@ /// @author Amlal El Mahrouss (amlal@nekernel.org) //! Include filesystems that NeKernel supports. -#include +#include #include #include @@ -177,6 +177,52 @@ class NeFileSystemMgr final : public IFilesystemMgr { #endif // ifdef __FSKIT_INCLUDES_NEFS__ +#ifdef __FSKIT_INCLUDES_EXT2__ +/** + * @brief Based of IFilesystemMgr, takes care of managing NeFS + * disks. + */ +class Ext2FileSystemMgr final : public IFilesystemMgr { + public: + explicit Ext2FileSystemMgr(); + ~Ext2FileSystemMgr() override; + + public: + NE_COPY_DEFAULT(Ext2FileSystemMgr) + + public: + NodePtr Create(const Char* path) override; + NodePtr CreateAlias(const Char* path) override; + NodePtr CreateDirectory(const Char* path) override; + NodePtr CreateSwapFile(const Char* path) override; + + public: + bool Remove(_Input const Char* path) override; + NodePtr Open(_Input const Char* path, _Input const Char* r) override; + Void Write(_Input NodePtr node, _Input VoidPtr data, _Input Int32 flags, + _Input SizeT sz) override; + VoidPtr Read(_Input NodePtr node, _Input Int32 flags, _Input SizeT sz) override; + bool Seek(_Input NodePtr node, _Input SizeT off) override; + SizeT Tell(_Input NodePtr node) override; + bool Rewind(_Input NodePtr node) override; + + Void Write(_Input const Char* name, _Input NodePtr node, _Input VoidPtr data, _Input Int32 flags, + _Input SizeT size) override; + + _Output VoidPtr Read(_Input const Char* name, _Input NodePtr node, _Input Int32 flags, + _Input SizeT sz) override; + + public: + /// @brief Get NeFS parser class. + /// @return The filesystem parser class. + Ext2FileSystemParser* GetParser() noexcept; + + private: + Ext2FileSystemParser* mParser{nullptr}; +}; + +#endif // ifdef __FSKIT_INCLUDES_EXT2__ + #ifdef __FSKIT_INCLUDES_HEFS__ /** * @brief Based of IFilesystemMgr, takes care of managing NeFS diff --git a/dev/kernel/NeKit/ErrorOr.h b/dev/kernel/NeKit/ErrorOr.h index 52f0d6a9..e8b3b6fc 100644 --- a/dev/kernel/NeKit/ErrorOr.h +++ b/dev/kernel/NeKit/ErrorOr.h @@ -38,8 +38,8 @@ class ErrorOr final { return *this; } - T Value() const { return *mRef; } - + const T& Value() { return mRef.TryLeak(); } + Ref& Leak() { return mRef; } Int32 Error() { return mId; } diff --git a/dev/kernel/NeKit/Ref.h b/dev/kernel/NeKit/Ref.h index 46e94f88..a791ee1a 100644 --- a/dev/kernel/NeKit/Ref.h +++ b/dev/kernel/NeKit/Ref.h @@ -36,7 +36,7 @@ class Ref final { T& Leak() noexcept { return fClass; } - T& TryLeak() const noexcept { return fClass; } + T& TryLeak() noexcept { return fClass; } T operator*() { return fClass; } diff --git a/dev/kernel/amd64-desktop.make b/dev/kernel/amd64-desktop.make index 36b0f18e..bfcca16b 100644 --- a/dev/kernel/amd64-desktop.make +++ b/dev/kernel/amd64-desktop.make @@ -5,7 +5,7 @@ CXX = x86_64-w64-mingw32-g++ LD = x86_64-w64-mingw32-ld -CCFLAGS = -fshort-wchar -c -D__NE_AMD64__ -D__NE_VEPM__ -Wall -Wpedantic -Wextra -mno-red-zone -fno-rtti -fno-exceptions -std=c++20 -D__FSKIT_INCLUDES_HEFS__ -D__NE_SUPPORT_NX__ -O0 -I../vendor -D__NEOSKRNL__ -D__HAVE_NE_APIS__ -D__FREESTANDING__ -D__NE_VIRTUAL_MEMORY_SUPPORT__ -D__NE_AUTO_FORMAT__ -D__NE__ -I./ -I../ -I../boot +CCFLAGS = -fshort-wchar -c -D__NE_AMD64__ -D__NE_VEPM__ -Wall -Wpedantic -Wextra -mno-red-zone -fno-rtti -fno-exceptions -std=c++20 -D__FSKIT_INCLUDES_HEFS__ -D__FSKIT_INCLUDES_EXT2__ -D__NE_SUPPORT_NX__ -O0 -I../vendor -D__NEOSKRNL__ -D__HAVE_NE_APIS__ -D__FREESTANDING__ -D__NE_VIRTUAL_MEMORY_SUPPORT__ -D__NE_AUTO_FORMAT__ -D__NE__ -I./ -I../ -I../boot ASM = nasm diff --git a/dev/kernel/src/FS/Ext2+FileMgr.cc b/dev/kernel/src/FS/Ext2+FileMgr.cc index d5d23c3f..c205fb10 100644 --- a/dev/kernel/src/FS/Ext2+FileMgr.cc +++ b/dev/kernel/src/FS/Ext2+FileMgr.cc @@ -1,1540 +1,1559 @@ #ifndef __NE_MINIMAL_OS__ #ifdef __FSKIT_INCLUDES_EXT2__ -#include -#include -#include +#include +#include #include +#include #include -#include -#include #include +#include #include +#include -namespace Ext2 { - -constexpr UInt32 EXT2_DIRECT_BLOCKS = 12; -constexpr UInt32 EXT2_SINGLE_INDIRECT_INDEX = 12; -constexpr UInt32 EXT2_DOUBLE_INDIRECT_INDEX = 13; -constexpr UInt32 EXT2_TRIPLE_INDIRECT_INDEX = 14; -constexpr UInt32 EXT2_ROOT_INODE = 2; -constexpr UInt32 EXT2_SUPERBLOCK_BLOCK = 1; +constexpr UInt32 EXT2_DIRECT_BLOCKS = 12; +constexpr UInt32 EXT2_SINGLE_INDIRECT_INDEX = 12; +constexpr UInt32 EXT2_DOUBLE_INDIRECT_INDEX = 13; +constexpr UInt32 EXT2_TRIPLE_INDIRECT_INDEX = 14; +constexpr UInt32 EXT2_ROOT_INODE = 2; +constexpr UInt32 EXT2_SUPERBLOCK_BLOCK = 1; constexpr UInt32 EXT2_GROUP_DESC_BLOCK_SMALL = 2; constexpr UInt32 EXT2_GROUP_DESC_BLOCK_LARGE = 1; -static inline SizeT ext2_min(SizeT a, SizeT b) { return a < b ? a : b; } +static inline SizeT ext2_min(SizeT a, SizeT b) { + return a < b ? a : b; +} struct Ext2GroupInfo { - EXT2_GROUP_DESCRIPTOR* groupDesc; - UInt32 groupDescriptorBlock; - UInt32 offsetInGroupDescBlock; - UInt8* blockBuffer; + EXT2_GROUP_DESCRIPTOR* groupDesc; + UInt32 groupDescriptorBlock; + UInt32 offsetInGroupDescBlock; + UInt8* blockBuffer; }; // Convert EXT2 block number -> LBA (sector index) for Drive I/O. -static inline Kernel::UInt32 ext2_block_to_lba(Context* ctx, Kernel::UInt32 blockNumber) { - if (!ctx || !ctx->drive) return 0; - Kernel::UInt32 blockSize = ctx->BlockSize(); - Kernel::UInt32 sectorSize = ctx->drive->fSectorSz; - Kernel::UInt32 sectorsPerBlock = blockSize / sectorSize; - return blockNumber * sectorsPerBlock; +static inline Kernel::UInt32 ext2_block_to_lba(Kernel::Ext2Context* ctx, + Kernel::UInt32 blockNumber) { + if (!ctx || !ctx->drive) return 0; + Kernel::UInt32 blockSize = ctx->BlockSize(); + Kernel::UInt32 sectorSize = ctx->drive->fSectorSz; + Kernel::UInt32 sectorsPerBlock = blockSize / sectorSize; + return blockNumber * sectorsPerBlock; } // Read a block and return a pointer to its content -static ErrorOr ext2_read_block_ptr(Context* ctx, Kernel::UInt32 blockNumber) { - if (!ctx || !ctx->drive || !ctx->superblock) - return ErrorOr(Kernel::kErrorInvalidData); - - Kernel::UInt32 blockSize = ctx->BlockSize(); - auto buf = (Kernel::UInt32*)mm_alloc_ptr(blockSize, true, false); - if (!buf) return ErrorOr(Kernel::kErrorHeapOutOfMemory); - - Kernel::UInt32 lba = ext2_block_to_lba(ctx, blockNumber); - if (!ext2_read_block(ctx->drive, lba, buf, blockSize)) { - mm_free_ptr(buf); - return ErrorOr(Kernel::kErrorDisk); - } - return ErrorOr(buf); +static ErrorOr ext2_read_block_ptr(Kernel::Ext2Context* ctx, + Kernel::UInt32 blockNumber) { + if (!ctx || !ctx->drive || !ctx->superblock) + return ErrorOr(Kernel::kErrorInvalidData); + + Kernel::UInt32 blockSize = ctx->BlockSize(); + auto buf = (Kernel::UInt32*) mm_alloc_ptr(blockSize, true, false); + if (!buf) return ErrorOr(Kernel::kErrorHeapOutOfMemory); + + Kernel::UInt32 lba = ext2_block_to_lba(ctx, blockNumber); + if (!ext2_read_block(ctx->drive, lba, buf, blockSize)) { + mm_free_ptr(buf); + return ErrorOr(Kernel::kErrorDisk); + } + return ErrorOr(buf); } // Get the block address for a given logical block index -static ErrorOr ext2_get_block_address(Context* ctx, Ext2Node* node, Kernel::UInt32 logicalIndex) { - if (!ctx || !node || !ctx->drive) - return ErrorOr(Kernel::kErrorInvalidData); - - Kernel::UInt32 blockSize = ctx->BlockSize(); - Kernel::UInt32 pointersPerBlock = blockSize / sizeof(Kernel::UInt32); - - // Direct blocks - if (logicalIndex < EXT2_DIRECT_BLOCKS) { - Kernel::UInt32 bn = node->inode.fBlock[logicalIndex]; - if (bn == 0) return ErrorOr(Kernel::kErrorInvalidData); - return ErrorOr(bn); - } +static ErrorOr ext2_get_block_address(Kernel::Ext2Context* ctx, Ext2Node* node, + Kernel::UInt32 logicalIndex) { + if (!ctx || !node || !ctx->drive) return ErrorOr(Kernel::kErrorInvalidData); - // Single indirect blocks - if (logicalIndex < (EXT2_DIRECT_BLOCKS + pointersPerBlock)) { - Kernel::UInt32 iblock = node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX]; - if (iblock == 0) return ErrorOr(Kernel::kErrorInvalidData); - - auto res = ext2_read_block_ptr(ctx, iblock); - if (!res) return ErrorOr(res.Error()); - - // Using dereference operator - Kernel::UInt32* ptr = *res.Leak(); // operator* returns T (UInt32*) - - Kernel::UInt32 val = ptr[logicalIndex - EXT2_DIRECT_BLOCKS]; - mm_free_ptr(ptr); - - if (val == 0) return ErrorOr(Kernel::kErrorInvalidData); - return ErrorOr(val); - } + Kernel::UInt32 blockSize = ctx->BlockSize(); + Kernel::UInt32 pointersPerBlock = blockSize / sizeof(Kernel::UInt32); - // Double indirect blocks - Kernel::UInt32 doubleStart = EXT2_DIRECT_BLOCKS + pointersPerBlock; - Kernel::UInt32 doubleSpan = pointersPerBlock * pointersPerBlock; - if (logicalIndex < (doubleStart + doubleSpan)) { - Kernel::UInt32 db = node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX]; - if (db == 0) return ErrorOr(Kernel::kErrorInvalidData); - - auto dblRes = ext2_read_block_ptr(ctx, db); - if (!dblRes) return ErrorOr(dblRes.Error()); - - Kernel::UInt32* dblPtr = *dblRes.Leak(); - - Kernel::UInt32 idxWithin = logicalIndex - doubleStart; - Kernel::UInt32 firstIdx = idxWithin / pointersPerBlock; - Kernel::UInt32 secondIdx = idxWithin % pointersPerBlock; - Kernel::UInt32 singleBlockNum = dblPtr[firstIdx]; - - mm_free_ptr(dblPtr); - if (singleBlockNum == 0) return ErrorOr(Kernel::kErrorInvalidData); - - auto singleRes = ext2_read_block_ptr(ctx, singleBlockNum); - if (!singleRes) return ErrorOr(singleRes.Error()); - - Kernel::UInt32* singlePtr = *singleRes.Leak(); - Kernel::UInt32 val = singlePtr[secondIdx]; - mm_free_ptr(singlePtr); - - if (val == 0) return ErrorOr(Kernel::kErrorInvalidData); - return ErrorOr(val); - } + // Direct blocks + if (logicalIndex < EXT2_DIRECT_BLOCKS) { + Kernel::UInt32 bn = node->inode.fBlock[logicalIndex]; + if (bn == 0) return ErrorOr(Kernel::kErrorInvalidData); + return ErrorOr(bn); + } - return ErrorOr(Kernel::kErrorUnimplemented); -} + // Single indirect blocks + if (logicalIndex < (EXT2_DIRECT_BLOCKS + pointersPerBlock)) { + Kernel::UInt32 iblock = node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX]; + if (iblock == 0) return ErrorOr(Kernel::kErrorInvalidData); -static Kernel::ErrorOr ext2_read_inode_data(Context* ctx, Ext2Node* node, SizeT size) { - using Kernel::ErrorOr; - using Kernel::UInt8; - using Kernel::UInt32; + auto res = ext2_read_block_ptr(ctx, iblock); + if (!res) return ErrorOr(res.Error()); - if (!ctx || !ctx->drive || !node || size == 0) - return ErrorOr(1); + // Using dereference operator + Kernel::UInt32* ptr = *res.Leak(); // operator* returns T (UInt32*) - auto blockSize = ctx->BlockSize(); - SizeT available = (node->inode.fSize > node->cursor) ? (node->inode.fSize - node->cursor) : 0; - SizeT bytesToRead = (size < available) ? size : available; - if (bytesToRead == 0) - return ErrorOr(2); // nothing to read + Kernel::UInt32 val = ptr[logicalIndex - EXT2_DIRECT_BLOCKS]; + mm_free_ptr(ptr); - auto buffer = mm_alloc_ptr(bytesToRead, true, false); - if (!buffer) - return ErrorOr(3); // allocation failed + if (val == 0) return ErrorOr(Kernel::kErrorInvalidData); + return ErrorOr(val); + } - UInt32 currentOffset = node->cursor; - SizeT remaining = bytesToRead; - UInt8* dest = reinterpret_cast(buffer); + // Double indirect blocks + Kernel::UInt32 doubleStart = EXT2_DIRECT_BLOCKS + pointersPerBlock; + Kernel::UInt32 doubleSpan = pointersPerBlock * pointersPerBlock; + if (logicalIndex < (doubleStart + doubleSpan)) { + Kernel::UInt32 db = node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX]; + if (db == 0) return ErrorOr(Kernel::kErrorInvalidData); - while (remaining > 0) { - UInt32 logicalIndex = currentOffset / blockSize; - UInt32 offsetInBlock = currentOffset % blockSize; + auto dblRes = ext2_read_block_ptr(ctx, db); + if (!dblRes) return ErrorOr(dblRes.Error()); - auto phys = ext2_get_block_address(ctx, node, logicalIndex); - if (phys.HasError()) { - mm_free_ptr(buffer); - return ErrorOr(phys.Error()); - } + Kernel::UInt32* dblPtr = *dblRes.Leak(); - UInt32 blockNumber = phys.Value(); - UInt32 lba = ext2_block_to_lba(ctx, blockNumber); - - auto blockBuf = mm_alloc_ptr(blockSize, true, false); - if (!blockBuf) { - mm_free_ptr(buffer); - return ErrorOr(4); // block buffer allocation failed - } + Kernel::UInt32 idxWithin = logicalIndex - doubleStart; + Kernel::UInt32 firstIdx = idxWithin / pointersPerBlock; + Kernel::UInt32 secondIdx = idxWithin % pointersPerBlock; + Kernel::UInt32 singleBlockNum = dblPtr[firstIdx]; - if (!ext2_read_block(ctx->drive, lba, blockBuf, blockSize)) { - mm_free_ptr(blockBuf); - mm_free_ptr(buffer); - return ErrorOr(5); // block read failed - } + mm_free_ptr(dblPtr); + if (singleBlockNum == 0) return ErrorOr(Kernel::kErrorInvalidData); - SizeT chunk = ext2_min(remaining, blockSize - offsetInBlock); - rt_copy_memory_safe(static_cast(static_cast(blockBuf) + offsetInBlock), - static_cast(dest), chunk, chunk); + auto singleRes = ext2_read_block_ptr(ctx, singleBlockNum); + if (!singleRes) return ErrorOr(singleRes.Error()); - mm_free_ptr(blockBuf); + Kernel::UInt32* singlePtr = *singleRes.Leak(); + Kernel::UInt32 val = singlePtr[secondIdx]; + mm_free_ptr(singlePtr); - currentOffset += static_cast(chunk); - dest += chunk; - remaining -= chunk; - } + if (val == 0) return ErrorOr(Kernel::kErrorInvalidData); + return ErrorOr(val); + } - node->cursor += static_cast(bytesToRead); - return ErrorOr(buffer); + return ErrorOr(Kernel::kErrorUnimplemented); } +static Kernel::ErrorOr ext2_read_inode_data(Kernel::Ext2Context* ctx, Ext2Node* node, + SizeT size) { + using Kernel::ErrorOr; + using Kernel::UInt32; + using Kernel::UInt8; -// Get group descriptor information for a given block/inode number -static ErrorOr ext2_get_group_descriptor_info(Context* ctx, UInt32 targetBlockOrInode) { - if (!ctx || !ctx->superblock || !ctx->drive) - return ErrorOr(kErrorInvalidData); - - UInt32 blockSize = ctx->BlockSize(); - UInt32 blocksPerGroup = ctx->superblock->fBlocksPerGroup; - UInt32 inodesPerGroup = ctx->superblock->fInodesPerGroup; - UInt32 totalBlocks = ctx->superblock->fBlockCount; - UInt32 totalInodes = ctx->superblock->fInodeCount; - - if (blocksPerGroup == 0 || inodesPerGroup == 0) - return ErrorOr(kErrorInvalidData); - - // block group index - UInt32 groupIndex = 0; - if (targetBlockOrInode == 0) { - groupIndex = 0; - } else if (targetBlockOrInode <= totalInodes) { - // 1-based - groupIndex = (targetBlockOrInode - 1) / inodesPerGroup; - } else { - // EXT2 block number - if (targetBlockOrInode < ctx->superblock->fFirstDataBlock) { - groupIndex = 0; - } else { - groupIndex = (targetBlockOrInode - ctx->superblock->fFirstDataBlock) / blocksPerGroup; - } - } + if (!ctx || !ctx->drive || !node || size == 0) return ErrorOr(1); - // Calculate number of block groups - UInt32 groupsCount = static_cast((totalBlocks + blocksPerGroup - 1) / blocksPerGroup); - if (groupIndex >= groupsCount) - return ErrorOr(kErrorInvalidData); + auto blockSize = ctx->BlockSize(); + SizeT available = (node->inode.fSize > node->cursor) ? (node->inode.fSize - node->cursor) : 0; + SizeT bytesToRead = (size < available) ? size : available; + if (bytesToRead == 0) return ErrorOr(2); // nothing to read - // Determine GDT start block - UInt32 gdtStartBlock = (blockSize == 1024) ? EXT2_GROUP_DESC_BLOCK_SMALL : EXT2_GROUP_DESC_BLOCK_LARGE; + auto buffer = mm_alloc_ptr(bytesToRead, true, false); + if (!buffer) return ErrorOr(3); // allocation failed - // Compute byte offset of descriptor within the GDT - const UInt32 descSize = sizeof(EXT2_GROUP_DESCRIPTOR); - UInt64 descByteOffset = static_cast(groupIndex) * descSize; + UInt32 currentOffset = node->cursor; + SizeT remaining = bytesToRead; + UInt8* dest = reinterpret_cast(buffer); - // Which EXT2 block contains that descriptor? - UInt32 blockOffsetWithinGdt = static_cast(descByteOffset / blockSize); - UInt32 offsetInGroupDescBlock = static_cast(descByteOffset % blockSize); - UInt32 groupDescriptorBlock = gdtStartBlock + blockOffsetWithinGdt; + while (remaining > 0) { + UInt32 logicalIndex = currentOffset / blockSize; + UInt32 offsetInBlock = currentOffset % blockSize; + + auto phys = ext2_get_block_address(ctx, node, logicalIndex); + if (phys.HasError()) { + mm_free_ptr(buffer); + return ErrorOr(phys.Error()); + } - // Allocate buffer and read the block containing the descriptor - auto blockBuffer = mm_alloc_ptr(blockSize, true, false); - if (!blockBuffer) return ErrorOr(kErrorHeapOutOfMemory); + auto blockNumber = phys.Value(); + UInt32 lba = ext2_block_to_lba(ctx, blockNumber); - UInt32 groupDescriptorLba = ext2_block_to_lba(ctx, groupDescriptorBlock); - if (!ext2_read_block(ctx->drive, groupDescriptorLba, blockBuffer, blockSize)) { - mm_free_ptr(blockBuffer); - return ErrorOr(kErrorDisk); + auto blockBuf = mm_alloc_ptr(blockSize, true, false); + if (!blockBuf) { + mm_free_ptr(buffer); + return ErrorOr(4); // block buffer allocation failed } - auto groupInfo = (Ext2GroupInfo*)mm_alloc_ptr(sizeof(Ext2GroupInfo), true, false); - if (!groupInfo) { - mm_free_ptr(blockBuffer); - return ErrorOr(kErrorHeapOutOfMemory); + if (!ext2_read_block(ctx->drive, lba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + mm_free_ptr(buffer); + return ErrorOr(5); // block read failed } - groupInfo->groupDesc = reinterpret_cast( - reinterpret_cast(blockBuffer) + offsetInGroupDescBlock); - groupInfo->groupDescriptorBlock = groupDescriptorBlock; - groupInfo->offsetInGroupDescBlock = offsetInGroupDescBlock; - groupInfo->blockBuffer = reinterpret_cast(blockBuffer); + SizeT chunk = ext2_min(remaining, blockSize - offsetInBlock); + rt_copy_memory_safe(static_cast(static_cast(blockBuf) + offsetInBlock), + static_cast(dest), chunk, chunk); + + mm_free_ptr(blockBuf); + + currentOffset += static_cast(chunk); + dest += chunk; + remaining -= chunk; + } - return ErrorOr(groupInfo); + node->cursor += static_cast(bytesToRead); + return ErrorOr(buffer); } -// Allocate a new block -inline ErrorOr ext2_alloc_block(Context* ctx, EXT2_GROUP_DESCRIPTOR* groupDesc) { - if (!ctx || !ctx->superblock || !groupDesc) - return ErrorOr(kErrorInvalidData); +// Get group descriptor information for a given block/inode number +static ErrorOr ext2_get_group_descriptor_info(Kernel::Ext2Context* ctx, + UInt32 targetBlockOrInode) { + if (!ctx || !ctx->superblock || !ctx->drive) return ErrorOr(kErrorInvalidData); + + UInt32 blockSize = ctx->BlockSize(); + UInt32 blocksPerGroup = ctx->superblock->fBlocksPerGroup; + UInt32 inodesPerGroup = ctx->superblock->fInodesPerGroup; + UInt32 totalBlocks = ctx->superblock->fBlockCount; + UInt32 totalInodes = ctx->superblock->fInodeCount; + + if (blocksPerGroup == 0 || inodesPerGroup == 0) return ErrorOr(kErrorInvalidData); + + // block group index + UInt32 groupIndex = 0; + if (targetBlockOrInode == 0) { + groupIndex = 0; + } else if (targetBlockOrInode <= totalInodes) { + // 1-based + groupIndex = (targetBlockOrInode - 1) / inodesPerGroup; + } else { + // EXT2 block number + if (targetBlockOrInode < ctx->superblock->fFirstDataBlock) { + groupIndex = 0; + } else { + groupIndex = (targetBlockOrInode - ctx->superblock->fFirstDataBlock) / blocksPerGroup; + } + } + + // Calculate number of block groups + UInt32 groupsCount = static_cast((totalBlocks + blocksPerGroup - 1) / blocksPerGroup); + if (groupIndex >= groupsCount) return ErrorOr(kErrorInvalidData); + + // Determine GDT start block + UInt32 gdtStartBlock = + (blockSize == 1024) ? EXT2_GROUP_DESC_BLOCK_SMALL : EXT2_GROUP_DESC_BLOCK_LARGE; + + // Compute byte offset of descriptor within the GDT + const UInt32 descSize = sizeof(EXT2_GROUP_DESCRIPTOR); + UInt64 descByteOffset = static_cast(groupIndex) * descSize; + + // Which EXT2 block contains that descriptor? + UInt32 blockOffsetWithinGdt = static_cast(descByteOffset / blockSize); + UInt32 offsetInGroupDescBlock = static_cast(descByteOffset % blockSize); + UInt32 groupDescriptorBlock = gdtStartBlock + blockOffsetWithinGdt; + + // Allocate buffer and read the block containing the descriptor + auto blockBuffer = mm_alloc_ptr(blockSize, true, false); + if (!blockBuffer) return ErrorOr(kErrorHeapOutOfMemory); + + UInt32 groupDescriptorLba = ext2_block_to_lba(ctx, groupDescriptorBlock); + if (!ext2_read_block(ctx->drive, groupDescriptorLba, blockBuffer, blockSize)) { + mm_free_ptr(blockBuffer); + return ErrorOr(kErrorDisk); + } + + auto groupInfo = (Ext2GroupInfo*) mm_alloc_ptr(sizeof(Ext2GroupInfo), true, false); + if (!groupInfo) { + mm_free_ptr(blockBuffer); + return ErrorOr(kErrorHeapOutOfMemory); + } + + groupInfo->groupDesc = reinterpret_cast( + reinterpret_cast(blockBuffer) + offsetInGroupDescBlock); + groupInfo->groupDescriptorBlock = groupDescriptorBlock; + groupInfo->offsetInGroupDescBlock = offsetInGroupDescBlock; + groupInfo->blockBuffer = reinterpret_cast(blockBuffer); + + return ErrorOr(groupInfo); +} - UInt32 blockSize = ctx->BlockSize(); +// Allocate a new block +inline ErrorOr ext2_alloc_block(Kernel::Ext2Context* ctx, + EXT2_GROUP_DESCRIPTOR* groupDesc) { + if (!ctx || !ctx->superblock || !groupDesc) return ErrorOr(kErrorInvalidData); - // for the bitmap - auto bitmap = mm_alloc_ptr(blockSize, true, false); - if (!bitmap) return ErrorOr(kErrorHeapOutOfMemory); + UInt32 blockSize = ctx->BlockSize(); - // Read block bitmap - if (!ext2_read_block(ctx->drive, groupDesc->fBlockBitmap, bitmap, blockSize)) { - mm_free_ptr(bitmap); - return ErrorOr(kErrorDisk); - } + // for the bitmap + auto bitmap = mm_alloc_ptr(blockSize, true, false); + if (!bitmap) return ErrorOr(kErrorHeapOutOfMemory); - // bit = 0 - for (UInt32 byteIdx = 0; byteIdx < blockSize; ++byteIdx) { - auto byte = reinterpret_cast(bitmap)[byteIdx]; - if (byte != 0xFF) { - for (int bit = 0; bit < 8; ++bit) { - if (!(byte & (1 << bit))) { - // Mark bit as used - reinterpret_cast(bitmap)[byteIdx] |= (1 << bit); - - // Compute block number - UInt32 blockNumber = byteIdx * 8 + bit; - - // Write bitmap back - if (!ext2_write_block(ctx->drive, groupDesc->fBlockBitmap, bitmap, blockSize)) { - mm_free_ptr(bitmap); - return ErrorOr(kErrorDisk); - } - - // Update group descriptor free count - groupDesc->fFreeBlocksCount--; - mm_free_ptr(bitmap); - return ErrorOr(blockNumber); - } - } + // Read block bitmap + if (!ext2_read_block(ctx->drive, groupDesc->fBlockBitmap, bitmap, blockSize)) { + mm_free_ptr(bitmap); + return ErrorOr(kErrorDisk); + } + + // bit = 0 + for (UInt32 byteIdx = 0; byteIdx < blockSize; ++byteIdx) { + auto byte = reinterpret_cast(bitmap)[byteIdx]; + if (byte != 0xFF) { + for (int bit = 0; bit < 8; ++bit) { + if (!(byte & (1 << bit))) { + // Mark bit as used + reinterpret_cast(bitmap)[byteIdx] |= (1 << bit); + + // Compute block number + UInt32 blockNumber = byteIdx * 8 + bit; + + // Write bitmap back + if (!ext2_write_block(ctx->drive, groupDesc->fBlockBitmap, bitmap, blockSize)) { + mm_free_ptr(bitmap); + return ErrorOr(kErrorDisk); + } + + // Update group descriptor free count + groupDesc->fFreeBlocksCount--; + mm_free_ptr(bitmap); + return ErrorOr(blockNumber); } + } } + } - mm_free_ptr(bitmap); - return ErrorOr(kErrorDiskIsFull); + mm_free_ptr(bitmap); + return ErrorOr(kErrorDiskIsFull); } // Indirect blocks -static Kernel::ErrorOr ext2_set_block_address(Context* ctx, Ext2Node* node, - Kernel::UInt32 logicalBlockIndex, - Kernel::UInt32 physicalBlockNumber) { - using namespace Kernel; +static Kernel::ErrorOr ext2_set_block_address(Kernel::Ext2Context* ctx, + Ext2Node* node, + Kernel::UInt32 logicalBlockIndex, + Kernel::UInt32 physicalBlockNumber) { + using namespace Kernel; + + if (!ctx || !ctx->drive || !node) return ErrorOr(kErrorInvalidData); + + auto blockSize = ctx->BlockSize(); + UInt32 blocksPerPointerBlock = blockSize / sizeof(UInt32); + + // Direct blocks + if (logicalBlockIndex < EXT2_DIRECT_BLOCKS) { + node->inode.fBlock[logicalBlockIndex] = physicalBlockNumber; + return ErrorOr(nullptr); + } + + // Single indirect blocks + if (logicalBlockIndex < EXT2_DIRECT_BLOCKS + blocksPerPointerBlock) { + if (node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX] == 0) { + auto groupInfoRes = ext2_get_group_descriptor_info(ctx, node->inodeNumber); + if (groupInfoRes.HasError()) return ErrorOr(groupInfoRes.Error()); + + auto groupInfo = groupInfoRes.Leak().Leak(); // Ref + auto newBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); + if (newBlockRes.HasError()) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(newBlockRes.Error()); + } - if (!ctx || !ctx->drive || !node) - return ErrorOr(kErrorInvalidData); + node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX] = newBlockRes.Leak(); - auto blockSize = ctx->BlockSize(); - UInt32 blocksPerPointerBlock = blockSize / sizeof(UInt32); + UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); - // Direct blocks - if (logicalBlockIndex < EXT2_DIRECT_BLOCKS) { - node->inode.fBlock[logicalBlockIndex] = physicalBlockNumber; - return ErrorOr(nullptr); + // Zero out new indirect block + auto zeroBuf = mm_alloc_ptr(blockSize, true, false); + if (!zeroBuf) return ErrorOr(kErrorHeapOutOfMemory); + + rt_zero_memory(zeroBuf, blockSize); + UInt32 indirectLba = ext2_block_to_lba(ctx, node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX]); + if (!ext2_write_block(ctx->drive, indirectLba, zeroBuf, blockSize)) { + mm_free_ptr(zeroBuf); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(zeroBuf); } - // Single indirect blocks - if (logicalBlockIndex < EXT2_DIRECT_BLOCKS + blocksPerPointerBlock) { - if (node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX] == 0) { - auto groupInfoRes = ext2_get_group_descriptor_info(ctx, node->inodeNumber); - if (groupInfoRes.HasError()) return ErrorOr(groupInfoRes.Error()); - - auto groupInfo = groupInfoRes.Leak().Leak(); // Ref - auto newBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); - if (newBlockRes.HasError()) { - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - return ErrorOr(newBlockRes.Error()); - } - - node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX] = newBlockRes.Leak(); - - UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); - if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - return ErrorOr(kErrorDisk); - } - - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - - // Zero out new indirect block - auto zeroBuf = mm_alloc_ptr(blockSize, true, false); - if (!zeroBuf) return ErrorOr(kErrorHeapOutOfMemory); - - rt_zero_memory(zeroBuf, blockSize); - UInt32 indirectLba = ext2_block_to_lba(ctx, node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX]); - if (!ext2_write_block(ctx->drive, indirectLba, zeroBuf, blockSize)) { - mm_free_ptr(zeroBuf); - return ErrorOr(kErrorDisk); - } - - mm_free_ptr(zeroBuf); - } + // Read, modify, and write single indirect block + auto indirectRes = ext2_read_block_ptr(ctx, node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX]); + if (indirectRes.HasError()) return ErrorOr(indirectRes.Error()); - // Read, modify, and write single indirect block - auto indirectRes = ext2_read_block_ptr(ctx, node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX]); - if (indirectRes.HasError()) return ErrorOr(indirectRes.Error()); + UInt32* indirectPtr = indirectRes.Leak().Leak(); // Ref + indirectPtr[logicalBlockIndex - EXT2_DIRECT_BLOCKS] = physicalBlockNumber; - UInt32* indirectPtr = indirectRes.Leak().Leak(); // Ref - indirectPtr[logicalBlockIndex - EXT2_DIRECT_BLOCKS] = physicalBlockNumber; + UInt32 indirectLba = ext2_block_to_lba(ctx, node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX]); + if (!ext2_write_block(ctx->drive, indirectLba, indirectPtr, blockSize)) { + mm_free_ptr(indirectPtr); + return ErrorOr(kErrorDisk); + } - UInt32 indirectLba = ext2_block_to_lba(ctx, node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX]); - if (!ext2_write_block(ctx->drive, indirectLba, indirectPtr, blockSize)) { - mm_free_ptr(indirectPtr); - return ErrorOr(kErrorDisk); - } + mm_free_ptr(indirectPtr); + return ErrorOr(nullptr); + } + + // Double + UInt32 doubleStart = EXT2_DIRECT_BLOCKS + blocksPerPointerBlock; + UInt32 doubleSpan = blocksPerPointerBlock * blocksPerPointerBlock; + if (logicalBlockIndex < doubleStart + doubleSpan) { + if (node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX] == 0) { + auto groupInfoRes = ext2_get_group_descriptor_info(ctx, node->inodeNumber); + if (groupInfoRes.HasError()) return ErrorOr(groupInfoRes.Error()); - mm_free_ptr(indirectPtr); - return ErrorOr(nullptr); + auto groupInfo = groupInfoRes.Leak().Leak(); + auto newBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); + if (newBlockRes.HasError()) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(newBlockRes.Error()); + } + + node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX] = newBlockRes.Leak(); + + UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + // Zero new double-indirect block + auto zeroBuf = mm_alloc_ptr(blockSize, true, false); + if (!zeroBuf) return ErrorOr(kErrorHeapOutOfMemory); + + rt_zero_memory(zeroBuf, blockSize); + UInt32 dblLba = ext2_block_to_lba(ctx, node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX]); + if (!ext2_write_block(ctx->drive, dblLba, zeroBuf, blockSize)) { + mm_free_ptr(zeroBuf); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(zeroBuf); } - // Double - UInt32 doubleStart = EXT2_DIRECT_BLOCKS + blocksPerPointerBlock; - UInt32 doubleSpan = blocksPerPointerBlock * blocksPerPointerBlock; - if (logicalBlockIndex < doubleStart + doubleSpan) { - if (node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX] == 0) { - auto groupInfoRes = ext2_get_group_descriptor_info(ctx, node->inodeNumber); - if (groupInfoRes.HasError()) return ErrorOr(groupInfoRes.Error()); - - auto groupInfo = groupInfoRes.Leak().Leak(); - auto newBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); - if (newBlockRes.HasError()) { - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - return ErrorOr(newBlockRes.Error()); - } - - node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX] = newBlockRes.Leak(); - - UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); - if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - return ErrorOr(kErrorDisk); - } - - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - - // Zero new double-indirect block - auto zeroBuf = mm_alloc_ptr(blockSize, true, false); - if (!zeroBuf) return ErrorOr(kErrorHeapOutOfMemory); - - rt_zero_memory(zeroBuf, blockSize); - UInt32 dblLba = ext2_block_to_lba(ctx, node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX]); - if (!ext2_write_block(ctx->drive, dblLba, zeroBuf, blockSize)) { - mm_free_ptr(zeroBuf); - return ErrorOr(kErrorDisk); - } - - mm_free_ptr(zeroBuf); - } + // Compute indices + UInt32 idxWithin = logicalBlockIndex - doubleStart; + UInt32 firstIdx = idxWithin / blocksPerPointerBlock; + UInt32 secondIdx = idxWithin % blocksPerPointerBlock; - // Compute indices - UInt32 idxWithin = logicalBlockIndex - doubleStart; - UInt32 firstIdx = idxWithin / blocksPerPointerBlock; - UInt32 secondIdx = idxWithin % blocksPerPointerBlock; - - auto doubleRes = ext2_read_block_ptr(ctx, node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX]); - if (doubleRes.HasError()) return ErrorOr(doubleRes.Error()); - - UInt32* doublePtr = doubleRes.Leak().Leak(); - UInt32 singleIndirectBlock = doublePtr[firstIdx]; - - // Allocate single-indirect if missing - if (singleIndirectBlock == 0) { - auto groupInfoRes = ext2_get_group_descriptor_info(ctx, node->inodeNumber); - if (groupInfoRes.HasError()) { mm_free_ptr(doublePtr); return ErrorOr(groupInfoRes.Error()); } - - auto groupInfo = groupInfoRes.Leak().Leak(); - auto newBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); - if (newBlockRes.HasError()) { - mm_free_ptr(doublePtr); - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - return ErrorOr(newBlockRes.Error()); - } - - singleIndirectBlock = newBlockRes.Leak(); - doublePtr[firstIdx] = singleIndirectBlock; - - // Write back GDT - UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); - if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { - mm_free_ptr(doublePtr); - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - return ErrorOr(kErrorDisk); - } - - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - - // Zero single-indirect block - auto zeroBuf = mm_alloc_ptr(blockSize, true, false); - if (!zeroBuf) { mm_free_ptr(doublePtr); return ErrorOr(kErrorHeapOutOfMemory); } - - rt_zero_memory(zeroBuf, blockSize); - UInt32 singleLba = ext2_block_to_lba(ctx, singleIndirectBlock); - if (!ext2_write_block(ctx->drive, singleLba, zeroBuf, blockSize)) { - mm_free_ptr(zeroBuf); - mm_free_ptr(doublePtr); - return ErrorOr(kErrorDisk); - } - - mm_free_ptr(zeroBuf); - - // Write double-indirect back to disk - UInt32 dblLba = ext2_block_to_lba(ctx, node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX]); - if (!ext2_write_block(ctx->drive, dblLba, doublePtr, blockSize)) { - mm_free_ptr(doublePtr); - return ErrorOr(kErrorDisk); - } - } + auto doubleRes = ext2_read_block_ptr(ctx, node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX]); + if (doubleRes.HasError()) return ErrorOr(doubleRes.Error()); + UInt32* doublePtr = doubleRes.Leak().Leak(); + UInt32 singleIndirectBlock = doublePtr[firstIdx]; + + // Allocate single-indirect if missing + if (singleIndirectBlock == 0) { + auto groupInfoRes = ext2_get_group_descriptor_info(ctx, node->inodeNumber); + if (groupInfoRes.HasError()) { mm_free_ptr(doublePtr); + return ErrorOr(groupInfoRes.Error()); + } - // Write to single-indirect block - auto singleRes = ext2_read_block_ptr(ctx, singleIndirectBlock); - if (singleRes.HasError()) return ErrorOr(singleRes.Error()); + auto groupInfo = groupInfoRes.Leak().Leak(); + auto newBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); + if (newBlockRes.HasError()) { + mm_free_ptr(doublePtr); + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(newBlockRes.Error()); + } - UInt32* singlePtr = singleRes.Leak().Leak(); - singlePtr[secondIdx] = physicalBlockNumber; + singleIndirectBlock = newBlockRes.Leak(); + doublePtr[firstIdx] = singleIndirectBlock; - UInt32 singleLba = ext2_block_to_lba(ctx, singleIndirectBlock); - if (!ext2_write_block(ctx->drive, singleLba, singlePtr, blockSize)) { - mm_free_ptr(singlePtr); - return ErrorOr(kErrorDisk); - } + // Write back GDT + UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + mm_free_ptr(doublePtr); + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(kErrorDisk); + } - mm_free_ptr(singlePtr); - return ErrorOr(nullptr); - } + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); - // Triple indirect blocks not implemented - return ErrorOr(kErrorUnimplemented); -} + // Zero single-indirect block + auto zeroBuf = mm_alloc_ptr(blockSize, true, false); + if (!zeroBuf) { + mm_free_ptr(doublePtr); + return ErrorOr(kErrorHeapOutOfMemory); + } -// Find a directory entry by name within a directory inode -static ErrorOr ext2_find_dir_entry(Context* ctx, Ext2Node* dirNode, const char* name) { - if (!ctx || !ctx->drive || !dirNode || !name) - return ErrorOr(Kernel::kErrorInvalidData); - - // Check directory type - auto type = (dirNode->inode.fMode >> 12) & 0xF; - if (type != kExt2FileTypeDirectory) - return ErrorOr(Kernel::kErrorInvalidData); - - Kernel::UInt32 blockSize = ctx->BlockSize(); - auto blockBuf = mm_alloc_ptr(blockSize, true, false); - if (!blockBuf) return ErrorOr(Kernel::kErrorHeapOutOfMemory); - - SizeT nameLen = rt_string_len(name); - for (int i = 0; i < EXT2_DIRECT_BLOCKS; ++i) { - Kernel::UInt32 blockNum = dirNode->inode.fBlock[i]; - if (blockNum == 0) continue; - - Kernel::UInt32 lba = ext2_block_to_lba(ctx, blockNum); - if (!ext2_read_block(ctx->drive, lba, blockBuf, blockSize)) { - mm_free_ptr(blockBuf); - return ErrorOr(Kernel::kErrorDisk); - } + rt_zero_memory(zeroBuf, blockSize); + UInt32 singleLba = ext2_block_to_lba(ctx, singleIndirectBlock); + if (!ext2_write_block(ctx->drive, singleLba, zeroBuf, blockSize)) { + mm_free_ptr(zeroBuf); + mm_free_ptr(doublePtr); + return ErrorOr(kErrorDisk); + } - Kernel::UInt32 offset = 0; - while (offset + sizeof(Kernel::UInt32) + sizeof(Kernel::UInt16) <= blockSize) { - auto onDiskEntry = reinterpret_cast((Kernel::UInt8*)blockBuf + offset); - if (onDiskEntry->fRecordLength == 0) break; // corrupted - - if (onDiskEntry->fInode != 0 && onDiskEntry->fNameLength == nameLen) { - // Compare names - if (rt_string_cmp(name, onDiskEntry->fName, nameLen) == 0) { - // Allocate a result sized to hold the name + metadata - SizeT recSize = sizeof(EXT2_DIR_ENTRY); - auto found = (EXT2_DIR_ENTRY*)mm_alloc_ptr(recSize, true, false); - if (!found) { - mm_free_ptr(blockBuf); - return ErrorOr(Kernel::kErrorHeapOutOfMemory); - } - - // Copy only record-length bytes - rt_copy_memory_safe(onDiskEntry, found, onDiskEntry->fRecordLength, recSize); - mm_free_ptr(blockBuf); - return ErrorOr(found); - } - } - offset += onDiskEntry->fRecordLength; - } + mm_free_ptr(zeroBuf); + + // Write double-indirect back to disk + UInt32 dblLba = ext2_block_to_lba(ctx, node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX]); + if (!ext2_write_block(ctx->drive, dblLba, doublePtr, blockSize)) { + mm_free_ptr(doublePtr); + return ErrorOr(kErrorDisk); + } } - - mm_free_ptr(blockBuf); - return ErrorOr(Kernel::kErrorFileNotFound); -} -// Compute ideal record length for a directory name -static inline UInt16 ext2_dir_entry_ideal_len(UInt8 nameLen) { - UInt16 raw = static_cast(8 + nameLen); // 8 = inode(4)+rec_len(2)+name_len(1)+file_type(1) - return static_cast((raw + 3) & ~3u); // align up to 4 -} + mm_free_ptr(doublePtr); -static ErrorOr ext2_add_dir_entry(Context* ctx, Ext2Node* parentDirNode, - const char* name, Kernel::UInt32 inodeNumber, Kernel::UInt8 fileType) { - using namespace Kernel; + // Write to single-indirect block + auto singleRes = ext2_read_block_ptr(ctx, singleIndirectBlock); + if (singleRes.HasError()) return ErrorOr(singleRes.Error()); - if (!ctx || !ctx->drive || !parentDirNode || !name) - return ErrorOr(kErrorInvalidData); + UInt32* singlePtr = singleRes.Leak().Leak(); + singlePtr[secondIdx] = physicalBlockNumber; - UInt32 blockSize = ctx->BlockSize(); - SizeT nameLen = rt_string_len(name); - if (nameLen == 0 || nameLen > 255) - return ErrorOr(kErrorInvalidData); + UInt32 singleLba = ext2_block_to_lba(ctx, singleIndirectBlock); + if (!ext2_write_block(ctx->drive, singleLba, singlePtr, blockSize)) { + mm_free_ptr(singlePtr); + return ErrorOr(kErrorDisk); + } - UInt16 newRecIdeal = ext2_dir_entry_ideal_len(static_cast(nameLen)); + mm_free_ptr(singlePtr); + return ErrorOr(nullptr); + } - auto blockBuf = mm_alloc_ptr(blockSize, true, false); - if (!blockBuf) return ErrorOr(kErrorHeapOutOfMemory); - - for (int bi = 0; bi < EXT2_DIRECT_BLOCKS; ++bi) { - UInt32 blockNum = parentDirNode->inode.fBlock[bi]; - - if (blockNum == 0) { - // Allocate new block - auto groupInfoRes = ext2_get_group_descriptor_info(ctx, parentDirNode->inodeNumber); - if (!groupInfoRes) { - mm_free_ptr(blockBuf); - return ErrorOr(groupInfoRes.Error()); - } - - auto groupInfo = *groupInfoRes.Leak(); // Dereference to get Ext2GroupInfo* - auto allocBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); - if (!allocBlockRes) { - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - mm_free_ptr(blockBuf); - return ErrorOr(allocBlockRes.Error()); - } - - UInt32 newBlock = *allocBlockRes.Leak(); // Dereference to get UInt32 - UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); - - if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - mm_free_ptr(blockBuf); - return ErrorOr(kErrorDisk); - } - - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - - // Zero block & insert entry - rt_zero_memory(blockBuf, blockSize); - auto entry = reinterpret_cast(blockBuf); - entry->fInode = inodeNumber; - entry->fNameLength = static_cast(nameLen); - entry->fFileType = fileType; - entry->fRecordLength = static_cast(blockSize); - rt_copy_memory_safe(const_cast(name), entry->fName, nameLen, blockSize); - - UInt32 blockLba = ext2_block_to_lba(ctx, newBlock); - if (!ext2_write_block(ctx->drive, blockLba, blockBuf, blockSize)) { - mm_free_ptr(blockBuf); - return ErrorOr(kErrorDisk); - } - - auto setRes = ext2_set_block_address(ctx, parentDirNode, bi, newBlock); - if (!setRes) { - mm_free_ptr(blockBuf); - return ErrorOr(setRes.Error()); - } + // Triple indirect blocks not implemented + return ErrorOr(kErrorUnimplemented); +} +// Find a directory entry by name within a directory inode +static ErrorOr ext2_find_dir_entry(Kernel::Ext2Context* ctx, Ext2Node* dirNode, + const char* name) { + if (!ctx || !ctx->drive || !dirNode || !name) + return ErrorOr(Kernel::kErrorInvalidData); + + // Check directory type + auto type = (dirNode->inode.fMode >> 12) & 0xF; + if (type != kExt2FileTypeDirectory) return ErrorOr(Kernel::kErrorInvalidData); + + Kernel::UInt32 blockSize = ctx->BlockSize(); + auto blockBuf = mm_alloc_ptr(blockSize, true, false); + if (!blockBuf) return ErrorOr(Kernel::kErrorHeapOutOfMemory); + + SizeT nameLen = rt_string_len(name); + for (Kernel::UInt32 i = 0; i < EXT2_DIRECT_BLOCKS; ++i) { + Kernel::UInt32 blockNum = dirNode->inode.fBlock[i]; + if (blockNum == 0) continue; + + Kernel::UInt32 lba = ext2_block_to_lba(ctx, blockNum); + if (!ext2_read_block(ctx->drive, lba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return ErrorOr(Kernel::kErrorDisk); + } + + Kernel::UInt32 offset = 0; + while (offset + sizeof(Kernel::UInt32) + sizeof(Kernel::UInt16) <= blockSize) { + auto onDiskEntry = reinterpret_cast((Kernel::UInt8*) blockBuf + offset); + if (onDiskEntry->fRecordLength == 0) break; // corrupted + + if (onDiskEntry->fInode != 0 && onDiskEntry->fNameLength == nameLen) { + // Compare names + if (rt_string_cmp(name, onDiskEntry->fName, nameLen) == 0) { + // Allocate a result sized to hold the name + metadata + SizeT recSize = sizeof(EXT2_DIR_ENTRY); + auto found = (EXT2_DIR_ENTRY*) mm_alloc_ptr(recSize, true, false); + if (!found) { mm_free_ptr(blockBuf); - return ErrorOr(nullptr); - } + return ErrorOr(Kernel::kErrorHeapOutOfMemory); + } - // read it - UInt32 blockLba = ext2_block_to_lba(ctx, blockNum); - if (!ext2_read_block(ctx->drive, blockLba, blockBuf, blockSize)) { - mm_free_ptr(blockBuf); - return ErrorOr(kErrorDisk); + // Copy only record-length bytes + rt_copy_memory_safe(onDiskEntry, found, onDiskEntry->fRecordLength, recSize); + mm_free_ptr(blockBuf); + return ErrorOr(found); } + } + offset += onDiskEntry->fRecordLength; + } + } - UInt32 offset = 0; - EXT2_DIR_ENTRY* lastEntry = nullptr; - UInt32 lastOffset = 0; - - while (offset < blockSize) { - if (offset + 8 > blockSize) break; - auto e = reinterpret_cast((UInt8*)blockBuf + offset); - if (e->fRecordLength == 0) break; - lastEntry = e; - lastOffset = offset; - offset += e->fRecordLength; - } + mm_free_ptr(blockBuf); + return ErrorOr(Kernel::kErrorFileNotFound); +} - if (!lastEntry) continue; +// Compute ideal record length for a directory name +static inline UInt16 ext2_dir_entry_ideal_len(UInt8 nameLen) { + UInt16 raw = + static_cast(8 + nameLen); // 8 = inode(4)+rec_len(2)+name_len(1)+file_type(1) + return static_cast((raw + 3) & ~3u); // align up to 4 +} - UInt16 lastIdeal = ext2_dir_entry_ideal_len(lastEntry->fNameLength); +static ErrorOr ext2_add_dir_entry(Kernel::Ext2Context* ctx, Ext2Node* parentDirNode, + const char* name, Kernel::UInt32 inodeNumber, + Kernel::UInt8 fileType) { + using namespace Kernel; - if (lastEntry->fRecordLength >= (UInt16)(lastIdeal + newRecIdeal)) { - UInt16 origRec = lastEntry->fRecordLength; - lastEntry->fRecordLength = lastIdeal; + if (!ctx || !ctx->drive || !parentDirNode || !name) return ErrorOr(kErrorInvalidData); - UInt32 newOffset = lastOffset + lastIdeal; - auto newEntry = reinterpret_cast((UInt8*)blockBuf + newOffset); - newEntry->fInode = inodeNumber; - newEntry->fNameLength = static_cast(nameLen); - newEntry->fFileType = fileType; - newEntry->fRecordLength = static_cast(origRec - lastIdeal); - rt_copy_memory_safe(const_cast(name), newEntry->fName, nameLen, newEntry->fRecordLength); + UInt32 blockSize = ctx->BlockSize(); + SizeT nameLen = rt_string_len(name); + if (nameLen == 0 || nameLen > 255) return ErrorOr(kErrorInvalidData); - if (!ext2_write_block(ctx->drive, blockLba, blockBuf, blockSize)) { - mm_free_ptr(blockBuf); - return ErrorOr(kErrorDisk); - } + UInt16 newRecIdeal = ext2_dir_entry_ideal_len(static_cast(nameLen)); - mm_free_ptr(blockBuf); - return ErrorOr(nullptr); - } - } + auto blockBuf = mm_alloc_ptr(blockSize, true, false); + if (!blockBuf) return ErrorOr(kErrorHeapOutOfMemory); - // No space in direct blocks -> allocate new block - int targetIndex = -1; - for (int i = 0; i < EXT2_DIRECT_BLOCKS; ++i) { - if (parentDirNode->inode.fBlock[i] == 0) { targetIndex = i; break; } - } - if (targetIndex == -1) { - mm_free_ptr(blockBuf); - return ErrorOr(kErrorUnimplemented); - } + for (int bi = 0; bi < EXT2_DIRECT_BLOCKS; ++bi) { + UInt32 blockNum = parentDirNode->inode.fBlock[bi]; - auto groupInfoResult = ext2_get_group_descriptor_info(ctx, parentDirNode->inodeNumber); - if (!groupInfoResult) { - mm_free_ptr(blockBuf); - return ErrorOr(groupInfoResult.Error()); - } + if (blockNum == 0) { + // Allocate new block + auto groupInfoRes = ext2_get_group_descriptor_info(ctx, parentDirNode->inodeNumber); + if (!groupInfoRes) { + mm_free_ptr(blockBuf); + return ErrorOr(groupInfoRes.Error()); + } - auto groupInfo = *groupInfoResult.Leak(); // Dereference to get Ext2GroupInfo* - auto newBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); - if (!newBlockRes) { + auto groupInfo = *groupInfoRes.Leak(); // Dereference to get Ext2GroupInfo* + auto allocBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); + if (!allocBlockRes) { mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); mm_free_ptr(groupInfo); mm_free_ptr(blockBuf); - return ErrorOr(newBlockRes.Error()); - } + return ErrorOr(allocBlockRes.Error()); + } + + UInt32 newBlock = *allocBlockRes.Leak(); // Dereference to get UInt32 + UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); - UInt32 newBlockNum = *newBlockRes.Leak(); // Dereference to get UInt32 - UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); - if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); mm_free_ptr(groupInfo); mm_free_ptr(blockBuf); - return ErrorOr(kErrorDisk); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + // Zero block & insert entry + rt_zero_memory(blockBuf, blockSize); + auto entry = reinterpret_cast(blockBuf); + entry->fInode = inodeNumber; + entry->fNameLength = static_cast(nameLen); + entry->fFileType = fileType; + entry->fRecordLength = static_cast(blockSize); + rt_copy_memory_safe(const_cast(name), entry->fName, nameLen, blockSize); + + UInt32 blockLba = ext2_block_to_lba(ctx, newBlock); + if (!ext2_write_block(ctx->drive, blockLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return ErrorOr(kErrorDisk); + } + + auto setRes = ext2_set_block_address(ctx, parentDirNode, bi, newBlock); + if (!setRes) { + mm_free_ptr(blockBuf); + return ErrorOr(setRes.Error()); + } + + mm_free_ptr(blockBuf); + return ErrorOr(nullptr); } - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); + // read it + UInt32 blockLba = ext2_block_to_lba(ctx, blockNum); + if (!ext2_read_block(ctx->drive, blockLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return ErrorOr(kErrorDisk); + } + + UInt32 offset = 0; + EXT2_DIR_ENTRY* lastEntry = nullptr; + UInt32 lastOffset = 0; - rt_zero_memory(blockBuf, blockSize); - auto entry = reinterpret_cast(blockBuf); - entry->fInode = inodeNumber; - entry->fNameLength = static_cast(nameLen); - entry->fFileType = fileType; - entry->fRecordLength = static_cast(blockSize); - rt_copy_memory_safe(const_cast(name), entry->fName, nameLen, blockSize); + while (offset < blockSize) { + if (offset + 8 > blockSize) break; + auto e = reinterpret_cast((UInt8*) blockBuf + offset); + if (e->fRecordLength == 0) break; + lastEntry = e; + lastOffset = offset; + offset += e->fRecordLength; + } + + if (!lastEntry) continue; + + UInt16 lastIdeal = ext2_dir_entry_ideal_len(lastEntry->fNameLength); + + if (lastEntry->fRecordLength >= (UInt16) (lastIdeal + newRecIdeal)) { + UInt16 origRec = lastEntry->fRecordLength; + lastEntry->fRecordLength = lastIdeal; + + UInt32 newOffset = lastOffset + lastIdeal; + auto newEntry = reinterpret_cast((UInt8*) blockBuf + newOffset); + newEntry->fInode = inodeNumber; + newEntry->fNameLength = static_cast(nameLen); + newEntry->fFileType = fileType; + newEntry->fRecordLength = static_cast(origRec - lastIdeal); + rt_copy_memory_safe(const_cast(name), newEntry->fName, nameLen, + newEntry->fRecordLength); - UInt32 newBlockLba = ext2_block_to_lba(ctx, newBlockNum); - if (!ext2_write_block(ctx->drive, newBlockLba, blockBuf, blockSize)) { + if (!ext2_write_block(ctx->drive, blockLba, blockBuf, blockSize)) { mm_free_ptr(blockBuf); - return ErrorOr(kErrorDisk); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(blockBuf); + return ErrorOr(nullptr); } + } - auto setRes = ext2_set_block_address(ctx, parentDirNode, targetIndex, newBlockNum); - if (!setRes) { - mm_free_ptr(blockBuf); - return ErrorOr(setRes.Error()); + // No space in direct blocks -> allocate new block + int targetIndex = -1; + for (Kernel::UInt32 i = 0; i < EXT2_DIRECT_BLOCKS; ++i) { + if (parentDirNode->inode.fBlock[i] == 0) { + targetIndex = i; + break; } + } + if (targetIndex == -1) { + mm_free_ptr(blockBuf); + return ErrorOr(kErrorUnimplemented); + } + auto groupInfoResult = ext2_get_group_descriptor_info(ctx, parentDirNode->inodeNumber); + if (!groupInfoResult) { mm_free_ptr(blockBuf); - return ErrorOr(nullptr); + return ErrorOr(groupInfoResult.Error()); + } + + auto groupInfo = *groupInfoResult.Leak(); // Dereference to get Ext2GroupInfo* + auto newBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); + if (!newBlockRes) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(blockBuf); + return ErrorOr(newBlockRes.Error()); + } + + UInt32 newBlockNum = *newBlockRes.Leak(); // Dereference to get UInt32 + UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(blockBuf); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + rt_zero_memory(blockBuf, blockSize); + auto entry = reinterpret_cast(blockBuf); + entry->fInode = inodeNumber; + entry->fNameLength = static_cast(nameLen); + entry->fFileType = fileType; + entry->fRecordLength = static_cast(blockSize); + rt_copy_memory_safe(const_cast(name), entry->fName, nameLen, blockSize); + + UInt32 newBlockLba = ext2_block_to_lba(ctx, newBlockNum); + if (!ext2_write_block(ctx->drive, newBlockLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return ErrorOr(kErrorDisk); + } + + auto setRes = ext2_set_block_address(ctx, parentDirNode, targetIndex, newBlockNum); + if (!setRes) { + mm_free_ptr(blockBuf); + return ErrorOr(setRes.Error()); + } + + mm_free_ptr(blockBuf); + return ErrorOr(nullptr); } // Soon -static ErrorOr ext2_alloc_inode(Context* ctx, EXT2_GROUP_DESCRIPTOR* groupDesc) { - if (!ctx || !ctx->superblock || !groupDesc) - return ErrorOr(kErrorInvalidData); - - UInt32 blockSize = ctx->BlockSize(); +static ErrorOr ext2_alloc_inode(Kernel::Ext2Context* ctx, + EXT2_GROUP_DESCRIPTOR* groupDesc) { + if (!ctx || !ctx->superblock || !groupDesc) return ErrorOr(kErrorInvalidData); - // buffer for the inode bitmap - auto bitmap = mm_alloc_ptr(blockSize, true, false); - if (!bitmap) return ErrorOr(kErrorHeapOutOfMemory); + UInt32 blockSize = ctx->BlockSize(); - // Read inode bitmap - if (!ext2_read_block(ctx->drive, groupDesc->fInodeBitmap, bitmap, blockSize)) { - mm_free_ptr(bitmap); - return ErrorOr(kErrorDisk); - } + // buffer for the inode bitmap + auto bitmap = mm_alloc_ptr(blockSize, true, false); + if (!bitmap) return ErrorOr(kErrorHeapOutOfMemory); - // Find first free inode (bit = 0) - for (UInt32 byteIdx = 0; byteIdx < blockSize; ++byteIdx) { - auto byte = reinterpret_cast(bitmap)[byteIdx]; - if (byte != 0xFF) { - for (int bit = 0; bit < 8; ++bit) { - if (!(byte & (1 << bit))) { - // Mark bit as used - reinterpret_cast(bitmap)[byteIdx] |= (1 << bit); - - // Compute inode number - UInt32 inodeNumber = byteIdx * 8 + bit + 1; // Inodes are 1-based - - // Write bitmap back - if (!ext2_write_block(ctx->drive, groupDesc->fInodeBitmap, bitmap, blockSize)) { - mm_free_ptr(bitmap); - return ErrorOr(kErrorDisk); - } - - // Update group descriptor free count - groupDesc->fFreeInodesCount--; - mm_free_ptr(bitmap); - return ErrorOr(inodeNumber); - } - } + // Read inode bitmap + if (!ext2_read_block(ctx->drive, groupDesc->fInodeBitmap, bitmap, blockSize)) { + mm_free_ptr(bitmap); + return ErrorOr(kErrorDisk); + } + + // Find first free inode (bit = 0) + for (UInt32 byteIdx = 0; byteIdx < blockSize; ++byteIdx) { + auto byte = reinterpret_cast(bitmap)[byteIdx]; + if (byte != 0xFF) { + for (int bit = 0; bit < 8; ++bit) { + if (!(byte & (1 << bit))) { + // Mark bit as used + reinterpret_cast(bitmap)[byteIdx] |= (1 << bit); + + // Compute inode number + UInt32 inodeNumber = byteIdx * 8 + bit + 1; // Inodes are 1-based + + // Write bitmap back + if (!ext2_write_block(ctx->drive, groupDesc->fInodeBitmap, bitmap, blockSize)) { + mm_free_ptr(bitmap); + return ErrorOr(kErrorDisk); + } + + // Update group descriptor free count + groupDesc->fFreeInodesCount--; + mm_free_ptr(bitmap); + return ErrorOr(inodeNumber); } + } } + } - mm_free_ptr(bitmap); - return ErrorOr(kErrorDiskIsFull); + mm_free_ptr(bitmap); + return ErrorOr(kErrorDiskIsFull); } // to write an inode to its correct location on disk -static ErrorOr ext2_write_inode(Context* ctx, Ext2Node* node) { - using namespace Kernel; - - if (!ctx || !ctx->superblock || !ctx->drive || !node) - return ErrorOr(kErrorInvalidData); - - auto blockSize = ctx->BlockSize(); - UInt32 inodesPerGroup = ctx->superblock->fInodesPerGroup; - - if (inodesPerGroup == 0) return ErrorOr(kErrorInvalidData); - - // Calculate which group this inode belongs to - UInt32 groupIndex = (node->inodeNumber - 1) / inodesPerGroup; - UInt32 inodeIndexInGroup = (node->inodeNumber - 1) % inodesPerGroup; - - // Get group descriptor - auto groupInfoResult = ext2_get_group_descriptor_info(ctx, node->inodeNumber); - if (!groupInfoResult) return ErrorOr(groupInfoResult.Error()); - - auto groupInfo = *groupInfoResult.Leak(); // Dereference to get Ext2GroupInfo* - - // Calculate inode table position - UInt32 inodeTableBlock = groupInfo->groupDesc->fInodeTable; - UInt32 inodeSize = ctx->superblock->fInodeSize; - UInt32 inodesPerBlock = blockSize / inodeSize; - - UInt32 blockOffset = inodeIndexInGroup / inodesPerBlock; - UInt32 offsetInBlock = (inodeIndexInGroup % inodesPerBlock) * inodeSize; - - UInt32 inodeBlock = inodeTableBlock + blockOffset; - UInt32 inodeLba = ext2_block_to_lba(ctx, inodeBlock); - - // Read the block containing the inode - auto blockBuf = mm_alloc_ptr(blockSize, true, false); - if (!blockBuf) { - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - return ErrorOr(kErrorHeapOutOfMemory); - } +static ErrorOr ext2_write_inode(Kernel::Ext2Context* ctx, Ext2Node* node) { + using namespace Kernel; - if (!ext2_read_block(ctx->drive, inodeLba, blockBuf, blockSize)) { - mm_free_ptr(blockBuf); - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - return ErrorOr(kErrorDisk); - } + if (!ctx || !ctx->superblock || !ctx->drive || !node) return ErrorOr(kErrorInvalidData); - // Copy the updated inode into the block buffer - rt_copy_memory_safe(&node->inode, static_cast((UInt8*)blockBuf + offsetInBlock), - sizeof(EXT2_INODE), blockSize - offsetInBlock); + auto blockSize = ctx->BlockSize(); + UInt32 inodesPerGroup = ctx->superblock->fInodesPerGroup; - // Write the block back - if (!ext2_write_block(ctx->drive, inodeLba, blockBuf, blockSize)) { - mm_free_ptr(blockBuf); - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - return ErrorOr(kErrorDisk); - } + if (inodesPerGroup == 0) return ErrorOr(kErrorInvalidData); + + // Calculate which group this inode belongs to + UInt32 groupIndex = (node->inodeNumber - 1) / inodesPerGroup; + NE_UNUSED(groupIndex); + UInt32 inodeIndexInGroup = (node->inodeNumber - 1) % inodesPerGroup; + + // Get group descriptor + auto groupInfoResult = ext2_get_group_descriptor_info(ctx, node->inodeNumber); + if (!groupInfoResult) return ErrorOr(groupInfoResult.Error()); + + auto groupInfo = *groupInfoResult.Leak(); // Dereference to get Ext2GroupInfo* + + // Calculate inode table position + UInt32 inodeTableBlock = groupInfo->groupDesc->fInodeTable; + UInt32 inodeSize = ctx->superblock->fInodeSize; + UInt32 inodesPerBlock = blockSize / inodeSize; + + UInt32 blockOffset = inodeIndexInGroup / inodesPerBlock; + UInt32 offsetInBlock = (inodeIndexInGroup % inodesPerBlock) * inodeSize; + + UInt32 inodeBlock = inodeTableBlock + blockOffset; + UInt32 inodeLba = ext2_block_to_lba(ctx, inodeBlock); + + // Read the block containing the inode + auto blockBuf = mm_alloc_ptr(blockSize, true, false); + if (!blockBuf) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(kErrorHeapOutOfMemory); + } + + if (!ext2_read_block(ctx->drive, inodeLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr(kErrorDisk); + } + + // Copy the updated inode into the block buffer + rt_copy_memory_safe(&node->inode, static_cast((UInt8*) blockBuf + offsetInBlock), + sizeof(EXT2_INODE), blockSize - offsetInBlock); + // Write the block back + if (!ext2_write_block(ctx->drive, inodeLba, blockBuf, blockSize)) { mm_free_ptr(blockBuf); mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); mm_free_ptr(groupInfo); - - return ErrorOr(nullptr); + return ErrorOr(kErrorDisk); + } + + mm_free_ptr(blockBuf); + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + return ErrorOr(nullptr); } namespace { - // new - struct PathComponents { - const char** components; - int count; - Char* buffer; - - PathComponents(const char* path) : components(nullptr), count(0), buffer(nullptr) { - if (!path || *path == '\0') return; - - SizeT pathLen = rt_string_len(path); - buffer = (Char*)mm_alloc_ptr(pathLen + 1, true, false); - if (!buffer) return; - - rt_copy_memory_safe((void*)path, buffer, pathLen, pathLen + 1); - buffer[pathLen] = '\0'; - - // temp array - const char** temp = (const char**)mm_alloc_ptr(sizeof(char*) * (pathLen + 1), true, false); - if (!temp) { - mm_free_ptr(buffer); - buffer = nullptr; - return; - } +// new +struct PathComponents { + const char** components; + int count; + Char* buffer; - int compCount = 0; - Char* p = buffer; - - while (*p != '\0') { - // skip slashes - while (*p == '/') p++; - if (*p == '\0') break; - - Char* start = p; - while (*p != '/' && *p != '\0') p++; - Char saved = *p; - *p = '\0'; - - // handle ".", "..", or normal - if (rt_string_cmp(start, ".", 1) == 0) { - // ignore - } else if (rt_string_cmp(start, "..", 2) == 0) { - if (compCount > 0) compCount--; // go up one level - } else { - temp[compCount++] = start; - } - - *p = saved; - } + PathComponents(const char* path) : components(nullptr), count(0), buffer(nullptr) { + if (!path || *path == '\0') return; - if (compCount == 0) { - mm_free_ptr(temp); - return; - } + SizeT pathLen = rt_string_len(path); + buffer = (Char*) mm_alloc_ptr(pathLen + 1, true, false); + if (!buffer) return; - components = (const char**)mm_alloc_ptr(sizeof(char*) * compCount, true, false); - if (!components) { - mm_free_ptr(temp); - return; - } + rt_copy_memory_safe((void*) path, buffer, pathLen, pathLen + 1); + buffer[pathLen] = '\0'; - for (int i = 0; i < compCount; i++) components[i] = temp[i]; - count = compCount; - - mm_free_ptr(temp); + // temp array + const char** temp = (const char**) mm_alloc_ptr(sizeof(char*) * (pathLen + 1), true, false); + if (!temp) { + mm_free_ptr(buffer); + buffer = nullptr; + return; } - ~PathComponents() { - if (components) mm_free_ptr(components); - if (buffer) mm_free_ptr(buffer); - } -}; -} // anonymous namespace + int compCount = 0; + Char* p = buffer; -// The Ext2FileSystemMgr -Ext2FileSystemMgr::Ext2FileSystemMgr(DriveTrait* drive) : ctx(drive) {} -NodePtr Ext2FileSystemMgr::Open(const char* path, const char* restrict_type) { - if (!path || *path == '\0' || !this->ctx.drive) { - return nullptr; - } + while (*p != '\0') { + // skip slashes + while (*p == '/') p++; + if (*p == '\0') break; - // Root ("/") - if (rt_string_len(path) == 1 && rt_string_cmp(path, "/", 1) == 0) { - auto inodeResult = ext2_load_inode(&this->ctx, EXT2_ROOT_INODE); - if (!inodeResult) { - return nullptr; - } + Char* start = p; + while (*p != '/' && *p != '\0') p++; + Char saved = *p; + *p = '\0'; - auto heapNode = (Ext2Node*)mm_alloc_ptr(sizeof(Ext2Node), true, false); - if (!heapNode) return nullptr; + // handle ".", "..", or normal + if (rt_string_cmp(start, ".", 1) == 0) { + // ignore + } else if (rt_string_cmp(start, "..", 2) == 0) { + if (compCount > 0) compCount--; // go up one level + } else { + temp[compCount++] = start; + } - *heapNode = *inodeResult.Leak(); - heapNode->cursor = 0; - return reinterpret_cast(heapNode); + *p = saved; } - PathComponents pathComponents(path); - if (pathComponents.count == 0) { - return nullptr; + if (compCount == 0) { + mm_free_ptr(temp); + return; } - UInt32 currentInodeNumber = EXT2_ROOT_INODE; - Ext2Node* currentDirNode = nullptr; + components = (const char**) mm_alloc_ptr(sizeof(char*) * compCount, true, false); + if (!components) { + mm_free_ptr(temp); + return; + } - for (int i = 0; i < pathComponents.count; ++i) { - auto inodeResult = ext2_load_inode(&this->ctx, currentInodeNumber); - if (!inodeResult) { - if (currentDirNode) mm_free_ptr(currentDirNode); - return nullptr; - } + for (Kernel::UInt32 i = 0; i < compCount; i++) components[i] = temp[i]; + count = compCount; - if (currentDirNode) { - mm_free_ptr(currentDirNode); - currentDirNode = nullptr; - } + mm_free_ptr(temp); + } - currentDirNode = (Ext2Node*)mm_alloc_ptr(sizeof(Ext2Node), true, false); - if (!currentDirNode) { - return nullptr; - } + ~PathComponents() { + if (components) mm_free_ptr(components); + if (buffer) mm_free_ptr(buffer); + } +}; +} // anonymous namespace - *currentDirNode = *inodeResult.Leak(); - currentDirNode->cursor = 0; +// The Ext2FileSystemParser (not manager!) +Ext2FileSystemParser::Ext2FileSystemParser(DriveTrait* drive) : ctx(drive) {} +NodePtr Ext2FileSystemParser::Open(const char* path, const char* restrict_type) { + NE_UNUSED(restrict_type); + if (!path || *path == '\0' || !this->ctx.drive) { + return nullptr; + } - if (i < pathComponents.count - 1) { - UInt32 type = (currentDirNode->inode.fMode >> 12) & 0xF; - if (type != kExt2FileTypeDirectory) { - mm_free_ptr(currentDirNode); - return nullptr; - } - } + // Root ("/") + if (rt_string_len(path) == 1 && rt_string_cmp(path, "/", 1) == 0) { + auto inodeResult = ext2_load_inode(&this->ctx, EXT2_ROOT_INODE); + if (!inodeResult) { + return nullptr; + } - auto dirEntryResult = ext2_find_dir_entry(&this->ctx, currentDirNode, pathComponents.components[i]); - if (!dirEntryResult) { - mm_free_ptr(currentDirNode); - return nullptr; - } + auto heapNode = (Ext2Node*) mm_alloc_ptr(sizeof(Ext2Node), true, false); + if (!heapNode) return nullptr; - EXT2_DIR_ENTRY* entryPtr = *dirEntryResult.Leak(); - currentInodeNumber = entryPtr->fInode; - mm_free_ptr(entryPtr); - } + *heapNode = *inodeResult.Leak().Leak(); + heapNode->cursor = 0; + return reinterpret_cast(heapNode); + } - auto finalInodeResult = ext2_load_inode(&this->ctx, currentInodeNumber); - if (!finalInodeResult) { - if (currentDirNode) mm_free_ptr(currentDirNode); - return nullptr; + PathComponents pathComponents(path); + if (pathComponents.count == 0) { + return nullptr; + } + + UInt32 currentInodeNumber = EXT2_ROOT_INODE; + Ext2Node* currentDirNode = nullptr; + + for (Kernel::UInt32 i = 0; i < pathComponents.count; ++i) { + auto inodeResult = ext2_load_inode(&this->ctx, currentInodeNumber); + if (!inodeResult) { + if (currentDirNode) mm_free_ptr(currentDirNode); + return nullptr; } if (currentDirNode) { - mm_free_ptr(currentDirNode); + mm_free_ptr(currentDirNode); + currentDirNode = nullptr; } - auto resultNode = (Ext2Node*)mm_alloc_ptr(sizeof(Ext2Node), true, false); - if (!resultNode) { + currentDirNode = (Ext2Node*) mm_alloc_ptr(sizeof(Ext2Node), true, false); + if (!currentDirNode) { + return nullptr; + } + + *currentDirNode = *inodeResult.Leak().Leak(); + currentDirNode->cursor = 0; + + if (i < pathComponents.count - 1) { + UInt32 type = (currentDirNode->inode.fMode >> 12) & 0xF; + if (type != kExt2FileTypeDirectory) { + mm_free_ptr(currentDirNode); return nullptr; + } + } + + auto dirEntryResult = + ext2_find_dir_entry(&this->ctx, currentDirNode, pathComponents.components[i]); + if (!dirEntryResult) { + mm_free_ptr(currentDirNode); + return nullptr; } - *resultNode = *finalInodeResult.Leak(); - resultNode->cursor = 0; - return reinterpret_cast(resultNode); + EXT2_DIR_ENTRY* entryPtr = *dirEntryResult.Leak(); + currentInodeNumber = entryPtr->fInode; + mm_free_ptr(entryPtr); + } + + auto finalInodeResult = ext2_load_inode(&this->ctx, currentInodeNumber); + if (!finalInodeResult) { + if (currentDirNode) mm_free_ptr(currentDirNode); + return nullptr; + } + + if (currentDirNode) { + mm_free_ptr(currentDirNode); + } + + auto resultNode = (Ext2Node*) mm_alloc_ptr(sizeof(Ext2Node), true, false); + if (!resultNode) { + return nullptr; + } + + *resultNode = *finalInodeResult.Leak().Leak(); + resultNode->cursor = 0; + return reinterpret_cast(resultNode); } +void* Ext2FileSystemParser::Read(NodePtr node, Int32 flags, SizeT size) { + if (!node) return nullptr; -void* Ext2FileSystemMgr::Read(NodePtr node, Int32 flags, SizeT size) { - if (!node) return nullptr; + NE_UNUSED(flags); - auto extNode = reinterpret_cast(node); - auto dataResult = ext2_read_inode_data(&this->ctx, extNode, size); + auto extNode = reinterpret_cast(node); + auto dataResult = ext2_read_inode_data(&this->ctx, extNode, size); - if (!dataResult) { - return nullptr; // error, nothing to return - } + if (!dataResult) { + return nullptr; // error, nothing to return + } - void* data = *dataResult.Leak(); - if (data) { - extNode->cursor += static_cast(size); - } + void* data = *dataResult.Leak(); + if (data) { + extNode->cursor += static_cast(size); + } - return data; + return data; } +void Ext2FileSystemParser::Write(NodePtr node, void* data, Int32 flags, SizeT size) { + if (!node || !data || size == 0) return; -void Ext2FileSystemMgr::Write(NodePtr node, void* data, Int32 flags, SizeT size) { - if (!node || !data || size == 0) return; - - auto extNode = reinterpret_cast(node); - auto blockSize = this->ctx.BlockSize(); - SizeT bytesWritten = 0; - - UInt32 currentOffset = extNode->cursor; - UInt8* src = reinterpret_cast(data); - - while (bytesWritten < size) { - UInt32 logicalBlockIndex = currentOffset / blockSize; - UInt32 offsetInBlock = currentOffset % blockSize; - - auto physBlockResult = ext2_get_block_address(&this->ctx, extNode, logicalBlockIndex); - UInt32 physicalBlock = 0; - - if (!physBlockResult) { - auto err = physBlockResult.Error(); - if (err == kErrorInvalidData || err == kErrorUnimplemented) { - auto groupInfoResult = ext2_get_group_descriptor_info(&this->ctx, extNode->inodeNumber); - if (!groupInfoResult) { - return; - } - - auto groupInfo = *groupInfoResult.Leak(); - auto allocResult = ext2_alloc_block(&this->ctx, groupInfo->groupDesc); - if (!allocResult) { - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - return; - } - - physicalBlock = *allocResult.Leak(); - - auto setRes = ext2_set_block_address(&this->ctx, extNode, logicalBlockIndex, physicalBlock); - if (!setRes) { - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - return; - } - - UInt32 gdtLba = ext2_block_to_lba(&this->ctx, groupInfo->groupDescriptorBlock); - if (!ext2_write_block(this->ctx.drive, gdtLba, groupInfo->blockBuffer, blockSize)) { - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - return; - } - - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - } else { - return; - } - } else { - physicalBlock = physBlockResult.Value(); - } + NE_UNUSED(flags); + + auto extNode = reinterpret_cast(node); + auto blockSize = this->ctx.BlockSize(); + SizeT bytesWritten = 0; + + UInt32 currentOffset = extNode->cursor; + UInt8* src = reinterpret_cast(data); - UInt32 physicalLba = ext2_block_to_lba(&this->ctx, physicalBlock); + while (bytesWritten < size) { + UInt32 logicalBlockIndex = currentOffset / blockSize; + UInt32 offsetInBlock = currentOffset % blockSize; - auto blockBuf = mm_alloc_ptr(blockSize, true, false); - if (!blockBuf) return; + auto physBlockResult = ext2_get_block_address(&this->ctx, extNode, logicalBlockIndex); + UInt32 physicalBlock = 0; - if (offsetInBlock > 0 || (size - bytesWritten) < blockSize) { - if (!ext2_read_block(this->ctx.drive, physicalLba, blockBuf, blockSize)) { - mm_free_ptr(blockBuf); - return; - } - } else { - rt_zero_memory(blockBuf, blockSize); + if (!physBlockResult) { + auto err = physBlockResult.Error(); + if (err == kErrorInvalidData || err == kErrorUnimplemented) { + auto groupInfoResult = ext2_get_group_descriptor_info(&this->ctx, extNode->inodeNumber); + if (!groupInfoResult) { + return; } - UInt32 bytesInCurrentBlock = static_cast(ext2_min(size - bytesWritten, blockSize - offsetInBlock)); - rt_copy_memory_safe(src, static_cast((UInt8*)blockBuf + offsetInBlock), - bytesInCurrentBlock, blockSize - offsetInBlock); + auto groupInfo = *groupInfoResult.Leak(); + auto allocResult = ext2_alloc_block(&this->ctx, groupInfo->groupDesc); + if (!allocResult) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return; + } - if (!ext2_write_block(this->ctx.drive, physicalLba, blockBuf, blockSize)) { - mm_free_ptr(blockBuf); - return; + physicalBlock = *allocResult.Leak(); + + auto setRes = ext2_set_block_address(&this->ctx, extNode, logicalBlockIndex, physicalBlock); + if (!setRes) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return; } - mm_free_ptr(blockBuf); + UInt32 gdtLba = ext2_block_to_lba(&this->ctx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(this->ctx.drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return; + } - currentOffset += bytesInCurrentBlock; - src += bytesInCurrentBlock; - bytesWritten += bytesInCurrentBlock; + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + } else { + return; + } + } else { + physicalBlock = physBlockResult.Value(); } - if (currentOffset > extNode->inode.fSize) { - extNode->inode.fSize = currentOffset; + UInt32 physicalLba = ext2_block_to_lba(&this->ctx, physicalBlock); + + auto blockBuf = mm_alloc_ptr(blockSize, true, false); + if (!blockBuf) return; + + if (offsetInBlock > 0 || (size - bytesWritten) < blockSize) { + if (!ext2_read_block(this->ctx.drive, physicalLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return; + } + } else { + rt_zero_memory(blockBuf, blockSize); } - extNode->inode.fBlocks = (extNode->inode.fSize + blockSize - 1) / blockSize; - extNode->inode.fModifyTime = 0; + UInt32 bytesInCurrentBlock = + static_cast(ext2_min(size - bytesWritten, blockSize - offsetInBlock)); + rt_copy_memory_safe(src, static_cast((UInt8*) blockBuf + offsetInBlock), + bytesInCurrentBlock, blockSize - offsetInBlock); - auto writeInodeRes = ext2_write_inode(&this->ctx, extNode); - if (!writeInodeRes) { - // Failed to persist inode + if (!ext2_write_block(this->ctx.drive, physicalLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return; } - extNode->cursor = currentOffset; -} + mm_free_ptr(blockBuf); + + currentOffset += bytesInCurrentBlock; + src += bytesInCurrentBlock; + bytesWritten += bytesInCurrentBlock; + } -bool Ext2FileSystemMgr::Seek(NodePtr node, SizeT offset) { - if (!node) return false; - auto extNode = reinterpret_cast(node); - extNode->cursor = static_cast(offset); - return true; + if (currentOffset > extNode->inode.fSize) { + extNode->inode.fSize = currentOffset; + } + + extNode->inode.fBlocks = (extNode->inode.fSize + blockSize - 1) / blockSize; + extNode->inode.fModifyTime = 0; + + auto writeInodeRes = ext2_write_inode(&this->ctx, extNode); + if (!writeInodeRes) { + // Failed to persist inode + } + + extNode->cursor = currentOffset; } -SizeT Ext2FileSystemMgr::Tell(NodePtr node) { - if (!node) return 0; - auto extNode = reinterpret_cast(node); - return extNode->cursor; +bool Ext2FileSystemParser::Seek(NodePtr node, SizeT offset) { + if (!node) return false; + auto extNode = reinterpret_cast(node); + extNode->cursor = static_cast(offset); + return true; } -bool Ext2FileSystemMgr::Rewind(NodePtr node) { - if (!node) return false; - auto extNode = reinterpret_cast(node); - extNode->cursor = 0; - return true; +SizeT Ext2FileSystemParser::Tell(NodePtr node) { + if (!node) return 0; + auto extNode = reinterpret_cast(node); + return extNode->cursor; } -void* Ext2FileSystemMgr::Read(const char* name, NodePtr node, Int32 flags, SizeT size) { - return Read(node, flags, size); +bool Ext2FileSystemParser::Rewind(NodePtr node) { + if (!node) return false; + auto extNode = reinterpret_cast(node); + extNode->cursor = 0; + return true; } -void Ext2FileSystemMgr::Write(const char* name, NodePtr node, void* data, Int32 flags, SizeT size) { - Write(node, data, flags, size); +void* Ext2FileSystemParser::Read(const char* name, NodePtr node, Int32 flags, SizeT size) { + NE_UNUSED(name); + return Read(node, flags, size); } -NodePtr Ext2FileSystemMgr::Create(const char* path) { - if (!path || *path == '\0') return nullptr; - - PathComponents pathComponents(path); - if (pathComponents.count == 0) return nullptr; - - const char* filename = pathComponents.components[pathComponents.count - 1]; - if (rt_string_len(filename) > kExt2FSMaxFileNameLen) return nullptr; - - // Build parent path - Char parentPathBuf[256] = {0}; - SizeT currentPathLen = 0; - for (int i = 0; i < pathComponents.count - 1; ++i) { - SizeT componentLen = rt_string_len(pathComponents.components[i]); - if (currentPathLen + componentLen + 1 >= sizeof(parentPathBuf)) return nullptr; - if (i > 0) parentPathBuf[currentPathLen++] = '/'; - rt_copy_memory_safe(const_cast(pathComponents.components[i]), - parentPathBuf + currentPathLen, - componentLen, sizeof(parentPathBuf) - currentPathLen); - currentPathLen += componentLen; - } - parentPathBuf[currentPathLen] = '\0'; - - // Open parent directory - NodePtr parentDirNodePtr = nullptr; - if (currentPathLen == 0) { - // root - auto inodeRes = ext2_load_inode(&this->ctx, EXT2_ROOT_INODE); - if (!inodeRes) return nullptr; - parentDirNodePtr = mm_alloc_ptr(sizeof(Ext2Node), true, false); - if (!parentDirNodePtr) return nullptr; - *reinterpret_cast(parentDirNodePtr) = *inodeRes.Leak(); - reinterpret_cast(parentDirNodePtr)->cursor = 0; - } else { - parentDirNodePtr = Open(parentPathBuf, "r"); - } +void Ext2FileSystemParser::Write(const char* name, NodePtr node, void* data, Int32 flags, + SizeT size) { + NE_UNUSED(name); + Write(node, data, flags, size); +} +NodePtr Ext2FileSystemParser::Create(const char* path) { + if (!path || *path == '\0') return nullptr; + + PathComponents pathComponents(path); + if (pathComponents.count == 0) return nullptr; + + const char* filename = pathComponents.components[pathComponents.count - 1]; + if (rt_string_len(filename) > kExt2FSMaxFileNameLen) return nullptr; + + // Build parent path + Char parentPathBuf[256] = {0}; + SizeT currentPathLen = 0; + for (Kernel::UInt32 i = 0; (i < pathComponents.count - 1); ++i) { + SizeT componentLen = rt_string_len(pathComponents.components[i]); + if (currentPathLen + componentLen + 1 >= sizeof(parentPathBuf)) return nullptr; + if (i > 0) parentPathBuf[currentPathLen++] = '/'; + rt_copy_memory_safe(const_cast(pathComponents.components[i]), + parentPathBuf + currentPathLen, componentLen, + sizeof(parentPathBuf) - currentPathLen); + currentPathLen += componentLen; + } + parentPathBuf[currentPathLen] = '\0'; + + // Open parent directory + NodePtr parentDirNodePtr = nullptr; + if (currentPathLen == 0) { + // root + auto inodeRes = ext2_load_inode(&this->ctx, EXT2_ROOT_INODE); + if (!inodeRes) return nullptr; + parentDirNodePtr = mm_alloc_ptr(sizeof(Ext2Node), true, false); if (!parentDirNodePtr) return nullptr; + *reinterpret_cast(parentDirNodePtr) = *inodeRes.Leak().Leak(); + reinterpret_cast(parentDirNodePtr)->cursor = 0; + } else { + parentDirNodePtr = Open(parentPathBuf, "r"); + } - auto parentDirNode = reinterpret_cast(parentDirNodePtr); + if (!parentDirNodePtr) return nullptr; - // Ensure parent is a directory - UInt32 type = (parentDirNode->inode.fMode >> 12) & 0xF; - if (type != kExt2FileTypeDirectory) { - mm_free_ptr(parentDirNode); - return nullptr; - } + auto parentDirNode = reinterpret_cast(parentDirNodePtr); - // Get group info for allocation - auto groupInfoResult = ext2_get_group_descriptor_info(&this->ctx, parentDirNode->inodeNumber); - if (!groupInfoResult) { - mm_free_ptr(parentDirNode); - return nullptr; - } - auto groupInfo = *groupInfoResult.Leak(); + // Ensure parent is a directory + UInt32 type = (parentDirNode->inode.fMode >> 12) & 0xF; + if (type != kExt2FileTypeDirectory) { + mm_free_ptr(parentDirNode); + return nullptr; + } - // Allocate new inode - auto newInodeRes = ext2_alloc_inode(&this->ctx, groupInfo->groupDesc); - if (!newInodeRes) { - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); // so this works - mm_free_ptr(parentDirNode); - return nullptr; - } - UInt32 newInodeNumber = newInodeRes.Value(); + // Get group info for allocation + auto groupInfoResult = ext2_get_group_descriptor_info(&this->ctx, parentDirNode->inodeNumber); + if (!groupInfoResult) { + mm_free_ptr(parentDirNode); + return nullptr; + } + auto groupInfo = *groupInfoResult.Leak(); - UInt32 gdtLba = ext2_block_to_lba(&this->ctx, groupInfo->groupDescriptorBlock); - if (!ext2_write_block(this->ctx.drive, gdtLba, groupInfo->blockBuffer, this->ctx.BlockSize())) { - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - mm_free_ptr(parentDirNode); - return nullptr; - } + // Allocate new inode + auto newInodeRes = ext2_alloc_inode(&this->ctx, groupInfo->groupDesc); + if (!newInodeRes) { + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); // so this works + mm_free_ptr(parentDirNode); + return nullptr; + } + UInt32 newInodeNumber = newInodeRes.Value(); + UInt32 gdtLba = ext2_block_to_lba(&this->ctx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(this->ctx.drive, gdtLba, groupInfo->blockBuffer, this->ctx.BlockSize())) { mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); + mm_free_ptr(groupInfo); + mm_free_ptr(parentDirNode); + return nullptr; + } - // Create new Ext2Node - Ext2Node* newFileNode = reinterpret_cast(mm_alloc_ptr(sizeof(Ext2Node), true, false)); - if (!newFileNode) { - mm_free_ptr(parentDirNode); - return nullptr; - } + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); - newFileNode->inodeNumber = newInodeNumber; - rt_zero_memory(&newFileNode->inode, sizeof(EXT2_INODE)); - - newFileNode->inode.fMode = (kExt2FileTypeRegular << 12); - newFileNode->inode.fUID = 0; - newFileNode->inode.fGID = 0; - newFileNode->inode.fLinksCount = 1; - newFileNode->inode.fSize = 0; - newFileNode->inode.fBlocks = 0; - newFileNode->inode.fCreateTime = 0; - newFileNode->inode.fModifyTime = 0; - - // Persist new inode - auto writeInodeRes = ext2_write_inode(&this->ctx, newFileNode); - if (!writeInodeRes) { - mm_free_ptr(parentDirNode); - mm_free_ptr(newFileNode); - return nullptr; - } + // Create new Ext2Node + Ext2Node* newFileNode = reinterpret_cast(mm_alloc_ptr(sizeof(Ext2Node), true, false)); + if (!newFileNode) { + mm_free_ptr(parentDirNode); + return nullptr; + } + + newFileNode->inodeNumber = newInodeNumber; + rt_zero_memory(&newFileNode->inode, sizeof(EXT2_INODE)); + + newFileNode->inode.fMode = (kExt2FileTypeRegular << 12); + newFileNode->inode.fUID = 0; + newFileNode->inode.fGID = 0; + newFileNode->inode.fLinksCount = 1; + newFileNode->inode.fSize = 0; + newFileNode->inode.fBlocks = 0; + newFileNode->inode.fCreateTime = 0; + newFileNode->inode.fModifyTime = 0; + + // Persist new inode + auto writeInodeRes = ext2_write_inode(&this->ctx, newFileNode); + if (!writeInodeRes) { + mm_free_ptr(parentDirNode); + mm_free_ptr(newFileNode); + return nullptr; + } + + // Add directory entry + auto addRes = + ext2_add_dir_entry(&this->ctx, parentDirNode, filename, newInodeNumber, kExt2FileTypeRegular); + if (!addRes) { + mm_free_ptr(parentDirNode); + mm_free_ptr(newFileNode); + return nullptr; + } - // Add directory entry - auto addRes = ext2_add_dir_entry(&this->ctx, parentDirNode, filename, newInodeNumber, kExt2FileTypeRegular); - if (!addRes) { - mm_free_ptr(parentDirNode); - mm_free_ptr(newFileNode); - return nullptr; - } + // Update parent inode + auto parentWriteRes = ext2_write_inode(&this->ctx, parentDirNode); + // ignore failure - // Update parent inode - auto parentWriteRes = ext2_write_inode(&this->ctx, parentDirNode); - // ignore failure + NE_UNUSED(parentWriteRes); - mm_free_ptr(parentDirNode); - return reinterpret_cast(newFileNode); + mm_free_ptr(parentDirNode); + return reinterpret_cast(newFileNode); } -NodePtr Ext2FileSystemMgr::CreateDirectory(const char* path) { - if (!path || *path == '\0') return nullptr; +NodePtr Ext2FileSystemParser::CreateDirectory(const char* path) { + if (!path || *path == '\0') return nullptr; - PathComponents pathComponents(path); - if (pathComponents.count == 0) { - kout << "EXT2: Failed to parse path for CreateDirectory.\n"; - return nullptr; - } + PathComponents pathComponents(path); + if (pathComponents.count == 0) { + kout << "EXT2: Failed to parse path for CreateDirectory.\n"; + return nullptr; + } - const char* dirname = pathComponents.components[pathComponents.count - 1]; - if (rt_string_len(dirname) > kExt2FSMaxFileNameLen) { - kout << "EXT2: Directory name too long: " << dirname << ".\n"; - return nullptr; - } + const char* dirname = pathComponents.components[pathComponents.count - 1]; + if (rt_string_len(dirname) > kExt2FSMaxFileNameLen) { + kout << "EXT2: Directory name too long: " << dirname << ".\n"; + return nullptr; + } - // Build parent path - Char parentPathBuf[256]; - SizeT currentPathLen = 0; - for (int i = 0; i < pathComponents.count - 1; ++i) { - SizeT componentLen = rt_string_len(pathComponents.components[i]); - if (currentPathLen + componentLen + 1 >= sizeof(parentPathBuf)) { - kout << "EXT2: Parent path too long for CreateDirectory.\n"; - return nullptr; - } - - if (i > 0) parentPathBuf[currentPathLen++] = '/'; - - rt_copy_memory_safe(static_cast(const_cast(pathComponents.components[i])), - static_cast(parentPathBuf + currentPathLen), - componentLen, sizeof(parentPathBuf) - currentPathLen); - currentPathLen += componentLen; - } - - parentPathBuf[currentPathLen] = '\0'; - - // Open parent directory node - NodePtr parentDirNodePtr = nullptr; - if (currentPathLen == 0) { - auto inodeRes = ext2_load_inode(&this->ctx, EXT2_ROOT_INODE); - if (!inodeRes) { - return nullptr; - } - - parentDirNodePtr = reinterpret_cast(mm_alloc_ptr(sizeof(Ext2Node), true, false)); - if (!parentDirNodePtr) return nullptr; - - *reinterpret_cast(parentDirNodePtr) = *inodeRes.Leak(); - reinterpret_cast(parentDirNodePtr)->cursor = 0; - } else { - parentDirNodePtr = Open(parentPathBuf, "r"); - } - - if (!parentDirNodePtr) { - kout << "EXT2: Failed to open parent directory for CreateDirectory: " << parentPathBuf << ".\n"; - return nullptr; - } - - auto parentDirNode = reinterpret_cast(parentDirNodePtr); - - // Check parent is a directory - UInt32 parentType = (parentDirNode->inode.fMode >> 12) & 0xF; - if (parentType != kExt2FileTypeDirectory) { - kout << "EXT2: Parent is not a directory: " << parentPathBuf << ".\n"; - mm_free_ptr(parentDirNode); - return nullptr; + // Build parent path + Char parentPathBuf[256]; + SizeT currentPathLen = 0; + for (Kernel::UInt32 i = 0; (i < pathComponents.count - 1); ++i) { + SizeT componentLen = rt_string_len(pathComponents.components[i]); + if (currentPathLen + componentLen + 1 >= sizeof(parentPathBuf)) { + kout << "EXT2: Parent path too long for CreateDirectory.\n"; + return nullptr; } - // Allocate inode - auto groupInfoResult = ext2_get_group_descriptor_info(&this->ctx, parentDirNode->inodeNumber); - if (!groupInfoResult) { - kout << "EXT2: Failed to get group descriptor info for new dir inode.\n"; - mm_free_ptr(parentDirNode); - return nullptr; - } - - auto groupInfo = *groupInfoResult.Leak(); - auto newInodeRes = ext2_alloc_inode(&this->ctx, groupInfo->groupDesc); - if (!newInodeRes) { - kout << "EXT2: Failed to allocate inode for new directory.\n"; - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - mm_free_ptr(parentDirNode); - return nullptr; - } - - UInt32 newInodeNumber = *newInodeRes.Leak(); + if (i > 0) parentPathBuf[currentPathLen++] = '/'; - // Write back group descriptor block - UInt32 gdtLba = ext2_block_to_lba(&this->ctx, groupInfo->groupDescriptorBlock); - if (!ext2_write_block(this->ctx.drive, gdtLba, groupInfo->blockBuffer, this->ctx.BlockSize())) { - kout << "EXT2: Failed to write group descriptor after inode allocation.\n"; - mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - mm_free_ptr(parentDirNode); - return nullptr; + rt_copy_memory_safe(static_cast(const_cast(pathComponents.components[i])), + static_cast(parentPathBuf + currentPathLen), componentLen, + sizeof(parentPathBuf) - currentPathLen); + currentPathLen += componentLen; + } + + parentPathBuf[currentPathLen] = '\0'; + + // Open parent directory node + NodePtr parentDirNodePtr = nullptr; + if (currentPathLen == 0) { + auto inodeRes = ext2_load_inode(&this->ctx, EXT2_ROOT_INODE); + if (!inodeRes) { + return nullptr; } - + + parentDirNodePtr = reinterpret_cast(mm_alloc_ptr(sizeof(Ext2Node), true, false)); + if (!parentDirNodePtr) return nullptr; + + *reinterpret_cast(parentDirNodePtr) = *inodeRes.Leak().Leak(); + reinterpret_cast(parentDirNodePtr)->cursor = 0; + } else { + parentDirNodePtr = Open(parentPathBuf, "r"); + } + + if (!parentDirNodePtr) { + kout << "EXT2: Failed to open parent directory for CreateDirectory: " << parentPathBuf << ".\n"; + return nullptr; + } + + auto parentDirNode = reinterpret_cast(parentDirNodePtr); + + // Check parent is a directory + UInt32 parentType = (parentDirNode->inode.fMode >> 12) & 0xF; + if (parentType != kExt2FileTypeDirectory) { + kout << "EXT2: Parent is not a directory: " << parentPathBuf << ".\n"; + mm_free_ptr(parentDirNode); + return nullptr; + } + + // Allocate inode + auto groupInfoResult = ext2_get_group_descriptor_info(&this->ctx, parentDirNode->inodeNumber); + if (!groupInfoResult) { + kout << "EXT2: Failed to get group descriptor info for new dir inode.\n"; + mm_free_ptr(parentDirNode); + return nullptr; + } + + auto groupInfo = *groupInfoResult.Leak(); + auto newInodeRes = ext2_alloc_inode(&this->ctx, groupInfo->groupDesc); + if (!newInodeRes) { + kout << "EXT2: Failed to allocate inode for new directory.\n"; mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); mm_free_ptr(groupInfo); + mm_free_ptr(parentDirNode); + return nullptr; + } - // Create new Ext2Node and initialize inode fields - Ext2Node* newDirNode = reinterpret_cast(mm_alloc_ptr(sizeof(Ext2Node), true, false)); - if (!newDirNode) { - kout << "EXT2: Out of memory for new directory node.\n"; - mm_free_ptr(parentDirNode); - return nullptr; - } - - newDirNode->inodeNumber = newInodeNumber; - rt_zero_memory(&newDirNode->inode, sizeof(EXT2_INODE)); - newDirNode->inode.fMode = (kExt2FileTypeDirectory << 12); - newDirNode->inode.fUID = 0; - newDirNode->inode.fGID = 0; - newDirNode->inode.fLinksCount = 2; // . and .. - newDirNode->inode.fSize = this->ctx.BlockSize(); - newDirNode->inode.fBlocks = 1; - newDirNode->inode.fCreateTime = 0; - newDirNode->inode.fModifyTime = 0; - - // Allocate a data block for the new directory - auto groupForBlockRes = ext2_get_group_descriptor_info(&this->ctx, newDirNode->inodeNumber); - if (!groupForBlockRes) { - kout << "EXT2: Failed to get group info for directory block allocation.\n"; - mm_free_ptr(parentDirNode); - mm_free_ptr(newDirNode); - return nullptr; - } - - auto groupForBlock = *groupForBlockRes.Leak(); - auto newBlockRes = ext2_alloc_block(&this->ctx, groupForBlock->groupDesc); - if (!newBlockRes) { - kout << "EXT2: Failed to allocate block for new directory contents.\n"; - mm_free_ptr(reinterpret_cast(groupForBlock->blockBuffer)); - mm_free_ptr(groupForBlock); - mm_free_ptr(parentDirNode); - mm_free_ptr(newDirNode); - return nullptr; - } - - UInt32 newDirBlockNum = *newBlockRes.Leak(); - - // Write back GDT - UInt32 gdtLba2 = ext2_block_to_lba(&this->ctx, groupForBlock->groupDescriptorBlock); - if (!ext2_write_block(this->ctx.drive, gdtLba2, groupForBlock->blockBuffer, this->ctx.BlockSize())) { - kout << "EXT2: Failed to write GDT after directory block allocation.\n"; - mm_free_ptr(reinterpret_cast(groupForBlock->blockBuffer)); - mm_free_ptr(groupForBlock); - mm_free_ptr(parentDirNode); - mm_free_ptr(newDirNode); - return nullptr; - } - + UInt32 newInodeNumber = *newInodeRes.Leak(); + + // Write back group descriptor block + UInt32 gdtLba = ext2_block_to_lba(&this->ctx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(this->ctx.drive, gdtLba, groupInfo->blockBuffer, this->ctx.BlockSize())) { + kout << "EXT2: Failed to write group descriptor after inode allocation.\n"; + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(parentDirNode); + return nullptr; + } + + mm_free_ptr(reinterpret_cast(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + // Create new Ext2Node and initialize inode fields + Ext2Node* newDirNode = reinterpret_cast(mm_alloc_ptr(sizeof(Ext2Node), true, false)); + if (!newDirNode) { + kout << "EXT2: Out of memory for new directory node.\n"; + mm_free_ptr(parentDirNode); + return nullptr; + } + + newDirNode->inodeNumber = newInodeNumber; + rt_zero_memory(&newDirNode->inode, sizeof(EXT2_INODE)); + newDirNode->inode.fMode = (kExt2FileTypeDirectory << 12); + newDirNode->inode.fUID = 0; + newDirNode->inode.fGID = 0; + newDirNode->inode.fLinksCount = 2; // . and .. + newDirNode->inode.fSize = this->ctx.BlockSize(); + newDirNode->inode.fBlocks = 1; + newDirNode->inode.fCreateTime = 0; + newDirNode->inode.fModifyTime = 0; + + // Allocate a data block for the new directory + auto groupForBlockRes = ext2_get_group_descriptor_info(&this->ctx, newDirNode->inodeNumber); + if (!groupForBlockRes) { + kout << "EXT2: Failed to get group info for directory block allocation.\n"; + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + auto groupForBlock = *groupForBlockRes.Leak(); + auto newBlockRes = ext2_alloc_block(&this->ctx, groupForBlock->groupDesc); + if (!newBlockRes) { + kout << "EXT2: Failed to allocate block for new directory contents.\n"; mm_free_ptr(reinterpret_cast(groupForBlock->blockBuffer)); mm_free_ptr(groupForBlock); + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } - // Set the block in newDirNode - auto setBlkRes = ext2_set_block_address(&this->ctx, newDirNode, 0, newDirBlockNum); - if (!setBlkRes) { - kout << "EXT2: Failed to set data block for new directory.\n"; - mm_free_ptr(parentDirNode); - mm_free_ptr(newDirNode); - return nullptr; - } + UInt32 newDirBlockNum = *newBlockRes.Leak(); - // Prepare block with '.' and '..' - auto dirBlockBuf = mm_alloc_ptr(this->ctx.BlockSize(), true, false); - if (!dirBlockBuf) { - kout << "EXT2: Out of memory preparing directory block.\n"; - mm_free_ptr(parentDirNode); - mm_free_ptr(newDirNode); - return nullptr; - } - - rt_zero_memory(dirBlockBuf, this->ctx.BlockSize()); - - // '.' entry - auto dot = reinterpret_cast(dirBlockBuf); - dot->fInode = newInodeNumber; - dot->fNameLength = 1; - dot->fFileType = kExt2FileTypeDirectory; - dot->fRecordLength = ext2_dir_entry_ideal_len(dot->fNameLength); - dot->fName[0] = '.'; - - // '..' entry occupies rest of block - auto dotdot = reinterpret_cast((UInt8*)dirBlockBuf + dot->fRecordLength); - dotdot->fInode = parentDirNode->inodeNumber; - dotdot->fNameLength = 2; - dotdot->fFileType = kExt2FileTypeDirectory; - dotdot->fRecordLength = static_cast(this->ctx.BlockSize() - dot->fRecordLength); - dotdot->fName[0] = '.'; - dotdot->fName[1] = '.'; - - // Write dir block to disk - UInt32 newDirBlockLba = ext2_block_to_lba(&this->ctx, newDirBlockNum); - if (!ext2_write_block(this->ctx.drive, newDirBlockLba, dirBlockBuf, this->ctx.BlockSize())) { - kout << "EXT2: Failed to write directory block to disk.\n"; - mm_free_ptr(dirBlockBuf); - mm_free_ptr(parentDirNode); - mm_free_ptr(newDirNode); - return nullptr; - } - - mm_free_ptr(dirBlockBuf); + // Write back GDT + UInt32 gdtLba2 = ext2_block_to_lba(&this->ctx, groupForBlock->groupDescriptorBlock); + if (!ext2_write_block(this->ctx.drive, gdtLba2, groupForBlock->blockBuffer, + this->ctx.BlockSize())) { + kout << "EXT2: Failed to write GDT after directory block allocation.\n"; + mm_free_ptr(reinterpret_cast(groupForBlock->blockBuffer)); + mm_free_ptr(groupForBlock); + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } - // Persist new directory inode - auto writeInodeRes = ext2_write_inode(&this->ctx, newDirNode); - if (!writeInodeRes) { - kout << "EXT2: Failed to write new directory inode to disk.\n"; - mm_free_ptr(parentDirNode); - mm_free_ptr(newDirNode); - return nullptr; - } + mm_free_ptr(reinterpret_cast(groupForBlock->blockBuffer)); + mm_free_ptr(groupForBlock); - // Add directory entry into parent - auto addRes = ext2_add_dir_entry(&this->ctx, parentDirNode, dirname, newInodeNumber, kExt2FileTypeDirectory); - if (!addRes) { - kout << "EXT2: Failed to add directory entry for '" << dirname << "' to parent.\n"; - mm_free_ptr(parentDirNode); - mm_free_ptr(newDirNode); - return nullptr; - } + // Set the block in newDirNode + auto setBlkRes = ext2_set_block_address(&this->ctx, newDirNode, 0, newDirBlockNum); + if (!setBlkRes) { + kout << "EXT2: Failed to set data block for new directory.\n"; + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + // Prepare block with '.' and '..' + auto dirBlockBuf = mm_alloc_ptr(this->ctx.BlockSize(), true, false); + if (!dirBlockBuf) { + kout << "EXT2: Out of memory preparing directory block.\n"; + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + rt_zero_memory(dirBlockBuf, this->ctx.BlockSize()); + + // '.' entry + auto dot = reinterpret_cast(dirBlockBuf); + dot->fInode = newInodeNumber; + dot->fNameLength = 1; + dot->fFileType = kExt2FileTypeDirectory; + dot->fRecordLength = ext2_dir_entry_ideal_len(dot->fNameLength); + dot->fName[0] = '.'; + + // '..' entry occupies rest of block + auto dotdot = reinterpret_cast((UInt8*) dirBlockBuf + dot->fRecordLength); + dotdot->fInode = parentDirNode->inodeNumber; + dotdot->fNameLength = 2; + dotdot->fFileType = kExt2FileTypeDirectory; + dotdot->fRecordLength = static_cast(this->ctx.BlockSize() - dot->fRecordLength); + dotdot->fName[0] = '.'; + dotdot->fName[1] = '.'; + + // Write dir block to disk + UInt32 newDirBlockLba = ext2_block_to_lba(&this->ctx, newDirBlockNum); + if (!ext2_write_block(this->ctx.drive, newDirBlockLba, dirBlockBuf, this->ctx.BlockSize())) { + kout << "EXT2: Failed to write directory block to disk.\n"; + mm_free_ptr(dirBlockBuf); + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } - // Increment parent link count and persist parent inode - parentDirNode->inode.fLinksCount += 1; - auto parentWriteRes = ext2_write_inode(&this->ctx, parentDirNode); - if (!parentWriteRes) { - kout << "EXT2: Warning: failed to update parent inode after directory creation.\n"; - } + mm_free_ptr(dirBlockBuf); + // Persist new directory inode + auto writeInodeRes = ext2_write_inode(&this->ctx, newDirNode); + if (!writeInodeRes) { + kout << "EXT2: Failed to write new directory inode to disk.\n"; + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + // Add directory entry into parent + auto addRes = ext2_add_dir_entry(&this->ctx, parentDirNode, dirname, newInodeNumber, + kExt2FileTypeDirectory); + if (!addRes) { + kout << "EXT2: Failed to add directory entry for '" << dirname << "' to parent.\n"; mm_free_ptr(parentDirNode); - return reinterpret_cast(newDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + // Increment parent link count and persist parent inode + parentDirNode->inode.fLinksCount += 1; + auto parentWriteRes = ext2_write_inode(&this->ctx, parentDirNode); + if (!parentWriteRes) { + kout << "EXT2: Warning: failed to update parent inode after directory creation.\n"; + } + + mm_free_ptr(parentDirNode); + return reinterpret_cast(newDirNode); } -} // namespace Ext2 - #endif -#endif +#endif -- cgit v1.2.3 From 0ab69c370059d2334506776e933070dc33f27500 Mon Sep 17 00:00:00 2001 From: Amlal El Mahrouss Date: Sun, 28 Sep 2025 15:05:44 +0200 Subject: feat: Ext2+FileMgr.cc: important fixes and patches regarding code quality. Signed-off-by: Amlal El Mahrouss --- dev/kernel/src/FS/Ext2+FileMgr.cc | 162 +++++++++++++++++++------------------- 1 file changed, 79 insertions(+), 83 deletions(-) (limited to 'dev/kernel/src') diff --git a/dev/kernel/src/FS/Ext2+FileMgr.cc b/dev/kernel/src/FS/Ext2+FileMgr.cc index c205fb10..41c0d452 100644 --- a/dev/kernel/src/FS/Ext2+FileMgr.cc +++ b/dev/kernel/src/FS/Ext2+FileMgr.cc @@ -32,106 +32,102 @@ struct Ext2GroupInfo { }; // Convert EXT2 block number -> LBA (sector index) for Drive I/O. -static inline Kernel::UInt32 ext2_block_to_lba(Kernel::Ext2Context* ctx, - Kernel::UInt32 blockNumber) { +static inline UInt32 ext2_block_to_lba(Ext2Context* ctx, + UInt32 blockNumber) { if (!ctx || !ctx->drive) return 0; - Kernel::UInt32 blockSize = ctx->BlockSize(); - Kernel::UInt32 sectorSize = ctx->drive->fSectorSz; - Kernel::UInt32 sectorsPerBlock = blockSize / sectorSize; + UInt32 blockSize = ctx->BlockSize(); + UInt32 sectorSize = ctx->drive->fSectorSz; + UInt32 sectorsPerBlock = blockSize / sectorSize; return blockNumber * sectorsPerBlock; } // Read a block and return a pointer to its content -static ErrorOr ext2_read_block_ptr(Kernel::Ext2Context* ctx, - Kernel::UInt32 blockNumber) { +static ErrorOr ext2_read_block_ptr(Ext2Context* ctx, + UInt32 blockNumber) { if (!ctx || !ctx->drive || !ctx->superblock) - return ErrorOr(Kernel::kErrorInvalidData); + return ErrorOr(kErrorInvalidData); - Kernel::UInt32 blockSize = ctx->BlockSize(); - auto buf = (Kernel::UInt32*) mm_alloc_ptr(blockSize, true, false); - if (!buf) return ErrorOr(Kernel::kErrorHeapOutOfMemory); + UInt32 blockSize = ctx->BlockSize(); + auto buf = (UInt32*) mm_alloc_ptr(blockSize, true, false); + if (!buf) return ErrorOr(kErrorHeapOutOfMemory); - Kernel::UInt32 lba = ext2_block_to_lba(ctx, blockNumber); + UInt32 lba = ext2_block_to_lba(ctx, blockNumber); if (!ext2_read_block(ctx->drive, lba, buf, blockSize)) { mm_free_ptr(buf); - return ErrorOr(Kernel::kErrorDisk); + return ErrorOr(kErrorDisk); } - return ErrorOr(buf); + return ErrorOr(buf); } // Get the block address for a given logical block index -static ErrorOr ext2_get_block_address(Kernel::Ext2Context* ctx, Ext2Node* node, - Kernel::UInt32 logicalIndex) { - if (!ctx || !node || !ctx->drive) return ErrorOr(Kernel::kErrorInvalidData); +static ErrorOr ext2_get_block_address(Ext2Context* ctx, Ext2Node* node, + UInt32 logicalIndex) { + if (!ctx || !node || !ctx->drive) return ErrorOr(kErrorInvalidData); - Kernel::UInt32 blockSize = ctx->BlockSize(); - Kernel::UInt32 pointersPerBlock = blockSize / sizeof(Kernel::UInt32); + UInt32 blockSize = ctx->BlockSize(); + UInt32 pointersPerBlock = blockSize / sizeof(UInt32); // Direct blocks if (logicalIndex < EXT2_DIRECT_BLOCKS) { - Kernel::UInt32 bn = node->inode.fBlock[logicalIndex]; - if (bn == 0) return ErrorOr(Kernel::kErrorInvalidData); - return ErrorOr(bn); + UInt32 bn = node->inode.fBlock[logicalIndex]; + if (bn == 0) return ErrorOr(kErrorInvalidData); + return ErrorOr(bn); } // Single indirect blocks if (logicalIndex < (EXT2_DIRECT_BLOCKS + pointersPerBlock)) { - Kernel::UInt32 iblock = node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX]; - if (iblock == 0) return ErrorOr(Kernel::kErrorInvalidData); + UInt32 iblock = node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX]; + if (iblock == 0) return ErrorOr(kErrorInvalidData); auto res = ext2_read_block_ptr(ctx, iblock); - if (!res) return ErrorOr(res.Error()); + if (!res) return ErrorOr(res.Error()); // Using dereference operator - Kernel::UInt32* ptr = *res.Leak(); // operator* returns T (UInt32*) + UInt32* ptr = *res.Leak(); // operator* returns T (UInt32*) - Kernel::UInt32 val = ptr[logicalIndex - EXT2_DIRECT_BLOCKS]; + UInt32 val = ptr[logicalIndex - EXT2_DIRECT_BLOCKS]; mm_free_ptr(ptr); - if (val == 0) return ErrorOr(Kernel::kErrorInvalidData); - return ErrorOr(val); + if (val == 0) return ErrorOr(kErrorInvalidData); + return ErrorOr(val); } // Double indirect blocks - Kernel::UInt32 doubleStart = EXT2_DIRECT_BLOCKS + pointersPerBlock; - Kernel::UInt32 doubleSpan = pointersPerBlock * pointersPerBlock; + UInt32 doubleStart = EXT2_DIRECT_BLOCKS + pointersPerBlock; + UInt32 doubleSpan = pointersPerBlock * pointersPerBlock; if (logicalIndex < (doubleStart + doubleSpan)) { - Kernel::UInt32 db = node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX]; - if (db == 0) return ErrorOr(Kernel::kErrorInvalidData); + UInt32 db = node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX]; + if (db == 0) return ErrorOr(kErrorInvalidData); auto dblRes = ext2_read_block_ptr(ctx, db); - if (!dblRes) return ErrorOr(dblRes.Error()); + if (!dblRes) return ErrorOr(dblRes.Error()); - Kernel::UInt32* dblPtr = *dblRes.Leak(); + UInt32* dblPtr = *dblRes.Leak(); - Kernel::UInt32 idxWithin = logicalIndex - doubleStart; - Kernel::UInt32 firstIdx = idxWithin / pointersPerBlock; - Kernel::UInt32 secondIdx = idxWithin % pointersPerBlock; - Kernel::UInt32 singleBlockNum = dblPtr[firstIdx]; + UInt32 idxWithin = logicalIndex - doubleStart; + UInt32 firstIdx = idxWithin / pointersPerBlock; + UInt32 secondIdx = idxWithin % pointersPerBlock; + UInt32 singleBlockNum = dblPtr[firstIdx]; mm_free_ptr(dblPtr); - if (singleBlockNum == 0) return ErrorOr(Kernel::kErrorInvalidData); + if (singleBlockNum == 0) return ErrorOr(kErrorInvalidData); auto singleRes = ext2_read_block_ptr(ctx, singleBlockNum); - if (!singleRes) return ErrorOr(singleRes.Error()); + if (!singleRes) return ErrorOr(singleRes.Error()); - Kernel::UInt32* singlePtr = *singleRes.Leak(); - Kernel::UInt32 val = singlePtr[secondIdx]; + UInt32* singlePtr = *singleRes.Leak(); + UInt32 val = singlePtr[secondIdx]; mm_free_ptr(singlePtr); - if (val == 0) return ErrorOr(Kernel::kErrorInvalidData); - return ErrorOr(val); + if (val == 0) return ErrorOr(kErrorInvalidData); + return ErrorOr(val); } - return ErrorOr(Kernel::kErrorUnimplemented); + return ErrorOr(kErrorUnimplemented); } -static Kernel::ErrorOr ext2_read_inode_data(Kernel::Ext2Context* ctx, Ext2Node* node, +static ErrorOr ext2_read_inode_data(Ext2Context* ctx, Ext2Node* node, SizeT size) { - using Kernel::ErrorOr; - using Kernel::UInt32; - using Kernel::UInt8; - if (!ctx || !ctx->drive || !node || size == 0) return ErrorOr(1); auto blockSize = ctx->BlockSize(); @@ -187,7 +183,7 @@ static Kernel::ErrorOr ext2_read_inode_data(Kernel::Ext2Context* ctx, E } // Get group descriptor information for a given block/inode number -static ErrorOr ext2_get_group_descriptor_info(Kernel::Ext2Context* ctx, +static ErrorOr ext2_get_group_descriptor_info(Ext2Context* ctx, UInt32 targetBlockOrInode) { if (!ctx || !ctx->superblock || !ctx->drive) return ErrorOr(kErrorInvalidData); @@ -258,7 +254,7 @@ static ErrorOr ext2_get_group_descriptor_info(Kernel::Ext2Contex } // Allocate a new block -inline ErrorOr ext2_alloc_block(Kernel::Ext2Context* ctx, +inline ErrorOr ext2_alloc_block(Ext2Context* ctx, EXT2_GROUP_DESCRIPTOR* groupDesc) { if (!ctx || !ctx->superblock || !groupDesc) return ErrorOr(kErrorInvalidData); @@ -306,10 +302,10 @@ inline ErrorOr ext2_alloc_block(Kernel::Ext2Context* ctx, } // Indirect blocks -static Kernel::ErrorOr ext2_set_block_address(Kernel::Ext2Context* ctx, +static ErrorOr ext2_set_block_address(Ext2Context* ctx, Ext2Node* node, - Kernel::UInt32 logicalBlockIndex, - Kernel::UInt32 physicalBlockNumber) { + UInt32 logicalBlockIndex, + UInt32 physicalBlockNumber) { using namespace Kernel; if (!ctx || !ctx->drive || !node) return ErrorOr(kErrorInvalidData); @@ -514,33 +510,33 @@ static Kernel::ErrorOr ext2_set_block_address(Kernel::Ext2Context } // Find a directory entry by name within a directory inode -static ErrorOr ext2_find_dir_entry(Kernel::Ext2Context* ctx, Ext2Node* dirNode, +static ErrorOr ext2_find_dir_entry(Ext2Context* ctx, Ext2Node* dirNode, const char* name) { if (!ctx || !ctx->drive || !dirNode || !name) - return ErrorOr(Kernel::kErrorInvalidData); + return ErrorOr(kErrorInvalidData); // Check directory type auto type = (dirNode->inode.fMode >> 12) & 0xF; - if (type != kExt2FileTypeDirectory) return ErrorOr(Kernel::kErrorInvalidData); + if (type != kExt2FileTypeDirectory) return ErrorOr(kErrorInvalidData); - Kernel::UInt32 blockSize = ctx->BlockSize(); + UInt32 blockSize = ctx->BlockSize(); auto blockBuf = mm_alloc_ptr(blockSize, true, false); - if (!blockBuf) return ErrorOr(Kernel::kErrorHeapOutOfMemory); + if (!blockBuf) return ErrorOr(kErrorHeapOutOfMemory); SizeT nameLen = rt_string_len(name); - for (Kernel::UInt32 i = 0; i < EXT2_DIRECT_BLOCKS; ++i) { - Kernel::UInt32 blockNum = dirNode->inode.fBlock[i]; + for (UInt32 i = 0; i < EXT2_DIRECT_BLOCKS; ++i) { + UInt32 blockNum = dirNode->inode.fBlock[i]; if (blockNum == 0) continue; - Kernel::UInt32 lba = ext2_block_to_lba(ctx, blockNum); + UInt32 lba = ext2_block_to_lba(ctx, blockNum); if (!ext2_read_block(ctx->drive, lba, blockBuf, blockSize)) { mm_free_ptr(blockBuf); - return ErrorOr(Kernel::kErrorDisk); + return ErrorOr(kErrorDisk); } - Kernel::UInt32 offset = 0; - while (offset + sizeof(Kernel::UInt32) + sizeof(Kernel::UInt16) <= blockSize) { - auto onDiskEntry = reinterpret_cast((Kernel::UInt8*) blockBuf + offset); + UInt32 offset = 0; + while (offset + sizeof(UInt32) + sizeof(UInt16) <= blockSize) { + auto onDiskEntry = reinterpret_cast((UInt8*) blockBuf + offset); if (onDiskEntry->fRecordLength == 0) break; // corrupted if (onDiskEntry->fInode != 0 && onDiskEntry->fNameLength == nameLen) { @@ -551,7 +547,7 @@ static ErrorOr ext2_find_dir_entry(Kernel::Ext2Context* ctx, Ex auto found = (EXT2_DIR_ENTRY*) mm_alloc_ptr(recSize, true, false); if (!found) { mm_free_ptr(blockBuf); - return ErrorOr(Kernel::kErrorHeapOutOfMemory); + return ErrorOr(kErrorHeapOutOfMemory); } // Copy only record-length bytes @@ -565,7 +561,7 @@ static ErrorOr ext2_find_dir_entry(Kernel::Ext2Context* ctx, Ex } mm_free_ptr(blockBuf); - return ErrorOr(Kernel::kErrorFileNotFound); + return ErrorOr(kErrorFileNotFound); } // Compute ideal record length for a directory name @@ -575,9 +571,9 @@ static inline UInt16 ext2_dir_entry_ideal_len(UInt8 nameLen) { return static_cast((raw + 3) & ~3u); // align up to 4 } -static ErrorOr ext2_add_dir_entry(Kernel::Ext2Context* ctx, Ext2Node* parentDirNode, - const char* name, Kernel::UInt32 inodeNumber, - Kernel::UInt8 fileType) { +static ErrorOr ext2_add_dir_entry(Ext2Context* ctx, Ext2Node* parentDirNode, + const char* name, UInt32 inodeNumber, + UInt8 fileType) { using namespace Kernel; if (!ctx || !ctx->drive || !parentDirNode || !name) return ErrorOr(kErrorInvalidData); @@ -591,7 +587,7 @@ static ErrorOr ext2_add_dir_entry(Kernel::Ext2Context* ctx, Ext2N auto blockBuf = mm_alloc_ptr(blockSize, true, false); if (!blockBuf) return ErrorOr(kErrorHeapOutOfMemory); - for (int bi = 0; bi < EXT2_DIRECT_BLOCKS; ++bi) { + for (UInt32 bi = 0; bi < EXT2_DIRECT_BLOCKS; ++bi) { UInt32 blockNum = parentDirNode->inode.fBlock[bi]; if (blockNum == 0) { @@ -698,7 +694,7 @@ static ErrorOr ext2_add_dir_entry(Kernel::Ext2Context* ctx, Ext2N // No space in direct blocks -> allocate new block int targetIndex = -1; - for (Kernel::UInt32 i = 0; i < EXT2_DIRECT_BLOCKS; ++i) { + for (UInt32 i = 0; i < EXT2_DIRECT_BLOCKS; ++i) { if (parentDirNode->inode.fBlock[i] == 0) { targetIndex = i; break; @@ -761,7 +757,7 @@ static ErrorOr ext2_add_dir_entry(Kernel::Ext2Context* ctx, Ext2N } // Soon -static ErrorOr ext2_alloc_inode(Kernel::Ext2Context* ctx, +static ErrorOr ext2_alloc_inode(Ext2Context* ctx, EXT2_GROUP_DESCRIPTOR* groupDesc) { if (!ctx || !ctx->superblock || !groupDesc) return ErrorOr(kErrorInvalidData); @@ -809,7 +805,7 @@ static ErrorOr ext2_alloc_inode(Kernel::Ext2Context* ctx, } // to write an inode to its correct location on disk -static ErrorOr ext2_write_inode(Kernel::Ext2Context* ctx, Ext2Node* node) { +static ErrorOr ext2_write_inode(Ext2Context* ctx, Ext2Node* node) { using namespace Kernel; if (!ctx || !ctx->superblock || !ctx->drive || !node) return ErrorOr(kErrorInvalidData); @@ -900,7 +896,7 @@ struct PathComponents { return; } - int compCount = 0; + UInt32 compCount = 0; Char* p = buffer; while (*p != '\0') { @@ -936,7 +932,7 @@ struct PathComponents { return; } - for (Kernel::UInt32 i = 0; i < compCount; i++) components[i] = temp[i]; + for (UInt32 i = 0; i < compCount; i++) components[i] = temp[i]; count = compCount; mm_free_ptr(temp); @@ -980,7 +976,7 @@ NodePtr Ext2FileSystemParser::Open(const char* path, const char* restrict_type) UInt32 currentInodeNumber = EXT2_ROOT_INODE; Ext2Node* currentDirNode = nullptr; - for (Kernel::UInt32 i = 0; i < pathComponents.count; ++i) { + for (UInt32 i = 0; i < (UInt32)pathComponents.count; ++i) { auto inodeResult = ext2_load_inode(&this->ctx, currentInodeNumber); if (!inodeResult) { if (currentDirNode) mm_free_ptr(currentDirNode); @@ -1000,7 +996,7 @@ NodePtr Ext2FileSystemParser::Open(const char* path, const char* restrict_type) *currentDirNode = *inodeResult.Leak().Leak(); currentDirNode->cursor = 0; - if (i < pathComponents.count - 1) { + if (i < pathComponents.count - 1U) { UInt32 type = (currentDirNode->inode.fMode >> 12) & 0xF; if (type != kExt2FileTypeDirectory) { mm_free_ptr(currentDirNode); @@ -1209,7 +1205,7 @@ NodePtr Ext2FileSystemParser::Create(const char* path) { // Build parent path Char parentPathBuf[256] = {0}; SizeT currentPathLen = 0; - for (Kernel::UInt32 i = 0; (i < pathComponents.count - 1); ++i) { + for (UInt32 i = 0; (i < pathComponents.count - 1U); ++i) { SizeT componentLen = rt_string_len(pathComponents.components[i]); if (currentPathLen + componentLen + 1 >= sizeof(parentPathBuf)) return nullptr; if (i > 0) parentPathBuf[currentPathLen++] = '/'; @@ -1338,7 +1334,7 @@ NodePtr Ext2FileSystemParser::CreateDirectory(const char* path) { // Build parent path Char parentPathBuf[256]; SizeT currentPathLen = 0; - for (Kernel::UInt32 i = 0; (i < pathComponents.count - 1); ++i) { + for (UInt32 i = 0; (i < pathComponents.count - 1U); ++i) { SizeT componentLen = rt_string_len(pathComponents.components[i]); if (currentPathLen + componentLen + 1 >= sizeof(parentPathBuf)) { kout << "EXT2: Parent path too long for CreateDirectory.\n"; -- cgit v1.2.3 From b1302067703566624390d780dffd621bb99bb439 Mon Sep 17 00:00:00 2001 From: Amlal El Mahrouss Date: Sun, 28 Sep 2025 16:25:26 +0200 Subject: feat: copyright was removed. Signed-off-by: Amlal El Mahrouss --- dev/kernel/src/FS/Ext2+FileMgr.cc | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'dev/kernel/src') diff --git a/dev/kernel/src/FS/Ext2+FileMgr.cc b/dev/kernel/src/FS/Ext2+FileMgr.cc index 41c0d452..7c28c0c9 100644 --- a/dev/kernel/src/FS/Ext2+FileMgr.cc +++ b/dev/kernel/src/FS/Ext2+FileMgr.cc @@ -1,3 +1,9 @@ +/* ------------------------------------------- + + Copyright (C) 2024-2025, Amlal El Mahrouss, all rights reserved. + +------------------------------------------- */ + #ifndef __NE_MINIMAL_OS__ #ifdef __FSKIT_INCLUDES_EXT2__ -- cgit v1.2.3