diff options
| author | Amlal El Mahrouss <amlal@nekernel.org> | 2025-09-28 17:01:48 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-09-28 17:01:48 +0200 |
| commit | 2117a9b0f4b84f5bd6c99566bcf5849a64104467 (patch) | |
| tree | 590b20056c62d04a496aa22f6bee0034a1430e4e /dev | |
| parent | 0f88e96c0cf7ffaccecae94794024164c510f735 (diff) | |
| parent | b1302067703566624390d780dffd621bb99bb439 (diff) | |
Merge pull request #64 from nekernel-org/dev
ver: `kernel` v0.0.6
Diffstat (limited to 'dev')
39 files changed, 2153 insertions, 138 deletions
diff --git a/dev/boot/amd64-desktop.make b/dev/boot/amd64-desktop.make index 443e5677..d8c3fd86 100644 --- a/dev/boot/amd64-desktop.make +++ b/dev/boot/amd64-desktop.make @@ -117,15 +117,15 @@ compile-amd64: .PHONY: run-efi-amd64-ahci run-efi-amd64-ahci: - $(EMU) $(EMU_FLAGS) -monitor stdio -hda $(IMG) -s -S -boot menu=on + $(EMU) $(EMU_FLAGS) -monitor stdio -hda $(IMG) -s -boot menu=on .PHONY: run-efi-amd64-ata-pio run-efi-amd64-ata-pio: - $(EMU) $(EMU_FLAGS) -device piix3-ide,id=ide -drive id=disk,file=$(IMG),format=raw,if=none -device ide-hd,drive=disk,bus=ide.0 -s -S -d int -boot menu=on + $(EMU) $(EMU_FLAGS) -device piix3-ide,id=ide -drive id=disk,file=$(IMG),format=raw,if=none -device ide-hd,drive=disk,bus=ide.0 -s -d int -boot menu=on .PHONY: run-efi-amd64-ata-dma run-efi-amd64-ata-dma: - $(EMU) $(EMU_FLAGS) -device piix4-ide,id=ide -drive id=disk,file=$(IMG),format=raw,if=none -device ide-hd,drive=disk,bus=ide.0 -s -S -boot menu=on + $(EMU) $(EMU_FLAGS) -device piix4-ide,id=ide -drive id=disk,file=$(IMG),format=raw,if=none -device ide-hd,drive=disk,bus=ide.0 -s -boot menu=on .PHONY: run-efi-amd64-ata run-efi-amd64-ata: run-efi-amd64-ata-pio diff --git a/dev/kernel/FSKit/Ext2+IFS.h b/dev/kernel/FSKit/Ext2+IFS.h new file mode 100644 index 00000000..661c8d5f --- /dev/null +++ b/dev/kernel/FSKit/Ext2+IFS.h @@ -0,0 +1,271 @@ +/* ------------------------------------------- + + Copyright (C) 2024-2025, Amlal El Mahrouss, all rights reserved. + +------------------------------------------- */ + +#pragma once + +#include <FSKit/Ext2.h> +#include <KernelKit/DriveMgr.h> +#include <KernelKit/HeapMgr.h> +#include <KernelKit/KPC.h> +#include <NeKit/ErrorOr.h> +#include <NeKit/KernelPanic.h> +#include <NeKit/Utils.h> + +namespace Kernel { +/// @brief Context for an EXT2 filesystem on a given drive +class Ext2Context final { + public: + 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; + } + + SizeT BlockSize() const { + if (!superblock) return kExt2FSBlockSizeBase; + return kExt2FSBlockSizeBase << superblock->fLogBlockSize; + } + + operator BOOL() { return superblock != nullptr; } +}; + +/// ======================================================================= /// +/// IFS FUNCTIONS +/// ======================================================================= /// + +inline BOOL ext2_read_block(Kernel::DriveTrait* drv, Kernel::UInt32 lba, VoidPtr 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 VoidPtr buffer, + Kernel::UInt32 size) { + if (!drv || !buffer) return false; + + Kernel::DriveTrait::DrivePacket pkt{}; + pkt.fPacketContent = const_cast<VoidPtr>(buffer); + pkt.fPacketSize = size; + pkt.fPacketLba = lba; + drv->fOutput(pkt); + return pkt.fPacketGood; +} + +inline Kernel::ErrorOr<EXT2_SUPER_BLOCK*> ext2_load_superblock(Ext2Context* ctx) { + if (!ctx || !ctx->drive) return Kernel::ErrorOr<EXT2_SUPER_BLOCK*>(Kernel::kErrorInvalidData); + + auto buf = Kernel::mm_alloc_ptr(sizeof(EXT2_SUPER_BLOCK), true, false); + if (!buf) return Kernel::ErrorOr<EXT2_SUPER_BLOCK*>(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<EXT2_SUPER_BLOCK*>(Kernel::kErrorDisk); + } + + auto sb = reinterpret_cast<EXT2_SUPER_BLOCK*>(buf); + if (sb->fMagic != kExt2FSMagic) { + Kernel::mm_free_ptr(buf); + return Kernel::ErrorOr<EXT2_SUPER_BLOCK*>(Kernel::kErrorInvalidData); + } + + ctx->superblock = sb; + return Kernel::ErrorOr<EXT2_SUPER_BLOCK*>(sb); +} + +// Load inode +inline Kernel::ErrorOr<Ext2Node*> ext2_load_inode(Ext2Context* ctx, Kernel::UInt32 inodeNumber) { + if (!ctx || !ctx->superblock) return Kernel::ErrorOr<Ext2Node*>(Kernel::kErrorInvalidData); + + auto nodePtr = Kernel::mm_alloc_ptr(sizeof(Ext2Node), true, false); + if (!nodePtr) return Kernel::ErrorOr<Ext2Node*>(Kernel::kErrorHeapOutOfMemory); + + auto ext2Node = reinterpret_cast<Ext2Node*>(nodePtr); + ext2Node->inodeNumber = inodeNumber; + + // Compute block group and index within group + Kernel::UInt32 inodesPerGroup = ctx->superblock->fInodesPerGroup; + Kernel::UInt32 group = (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<Ext2Node*>(Kernel::kErrorDisk); + } + + ext2Node->cursor = 0; + return Kernel::ErrorOr<Ext2Node*>(ext2Node); +} + +/* + * 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 35129dcd..be2e34a6 100644 --- a/dev/kernel/FSKit/Ext2.h +++ b/dev/kernel/FSKit/Ext2.h @@ -13,20 +13,20 @@ #include <hint/CompilerHint.h> /// @file Ext2.h -/// @brief EXT2 filesystem structures and constants. +/// @brief EXT2 filesystem structures, constants, and base wrappers. +/// EXT2 Constants #define kExt2FSMagic (0xEF53) #define kExt2FSMaxFileNameLen (255U) #define kExt2FSSuperblockOffset (1024) #define kExt2FSRootInodeNumber (2) - #define kExt2FSInodeSize (128U) #define kExt2FSBlockSizeBase (1024U) #define kExt2FSRev0 (0) #define kExt2FSRev1 (1) -/// @brief EXT2's file types. +/// EXT2 file types enum { kExt2FileTypeUnknown = 0, kExt2FileTypeRegular = 1, @@ -38,7 +38,17 @@ enum { kExt2FileTypeSymbolicLink = 7 }; -/// @brief The super block structure, located at LBA 1024. +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; @@ -55,7 +65,7 @@ struct PACKED EXT2_SUPER_BLOCK final { Kernel::UInt32 fWriteTime; Kernel::UInt16 fMountCount; Kernel::UInt16 fMaxMountCount; - Kernel::UInt16 fMagic; // should be 0xEF53 + Kernel::UInt16 fMagic; Kernel::UInt16 fState; Kernel::UInt16 fErrors; Kernel::UInt16 fMinorRevision; @@ -78,7 +88,6 @@ struct PACKED EXT2_SUPER_BLOCK final { Kernel::Char fLastMounted[64]; Kernel::UInt32 fAlgoBitmap; - // Optional journal fields and padding Kernel::UInt8 fPreallocBlocks; Kernel::UInt8 fPreallocDirBlocks; Kernel::UInt16 fReservedGDTBlocks; @@ -112,22 +121,28 @@ struct PACKED EXT2_INODE final { Kernel::UInt32 fFlags; Kernel::UInt32 fOSD1; - Kernel::UInt32 - fBlock[15]; // 0-11: direct, 12: indirect, 13: double indirect, 14: triple indirect + Kernel::UInt32 fBlock[15]; // direct 0-11, indirect 12, double 13, triple 14 Kernel::UInt32 fGeneration; Kernel::UInt32 fFileACL; - Kernel::UInt32 fDirACL; // Only for revision 1+ + Kernel::UInt32 fDirACL; Kernel::UInt32 fFragmentAddr; Kernel::UInt8 fOSD2[12]; }; +/// Directory entry struct PACKED EXT2_DIR_ENTRY final { Kernel::UInt32 fInode; Kernel::UInt16 fRecordLength; Kernel::UInt8 fNameLength; Kernel::UInt8 fFileType; - Kernel::Char - fName[kExt2FSMaxFileNameLen]; // null-terminated, not fixed-length in actual on-disk layout -};
\ No newline at end of file + Kernel::Char fName[kExt2FSMaxFileNameLen]; +}; + +/// VFS usage +struct Ext2Node { + Kernel::UInt32 inodeNumber; + EXT2_INODE inode; + Kernel::UInt32 cursor{0}; +}; diff --git a/dev/kernel/HALKit/AMD64/CxxAbi.cc b/dev/kernel/HALKit/AMD64/CxxAbi.cc index cd135abc..84a00449 100644 --- a/dev/kernel/HALKit/AMD64/CxxAbi.cc +++ b/dev/kernel/HALKit/AMD64/CxxAbi.cc @@ -62,17 +62,18 @@ EXTERN_C void __cxa_finalize(void* f) { } namespace cxxabiv1 { -EXTERN_C int __cxa_guard_acquire(__guard* g) { - (void) g; +EXTERN_C int __cxa_guard_acquire(__guard g) { + if ((*g & 1) || (*g & 2)) return 1; + *g |= 2; return 0; } -EXTERN_C int __cxa_guard_release(__guard* g) { - *(char*) g = 1; - return 0; +EXTERN_C void __cxa_guard_release(__guard g) { + *g |= 1; + *g &= 2; } -EXTERN_C void __cxa_guard_abort(__guard* g) { - (void) g; +EXTERN_C void __cxa_guard_abort(__guard g) { + *g &= ~2; } } // namespace cxxabiv1 diff --git a/dev/kernel/HALKit/AMD64/HalApplicationProcessor.cc b/dev/kernel/HALKit/AMD64/HalApplicationProcessor.cc index df5386e4..e3e08830 100644 --- a/dev/kernel/HALKit/AMD64/HalApplicationProcessor.cc +++ b/dev/kernel/HALKit/AMD64/HalApplicationProcessor.cc @@ -88,7 +88,7 @@ struct LAPIC final { /// @param target /// @return /***********************************************************************************/ -Void hal_send_ipi_msg(UInt32 target, UInt32 apic_id, UInt8 vector) { +EXTERN_C Void hal_send_ipi_msg(UInt32 target, UInt32 apic_id, UInt8 vector) { Kernel::ke_dma_write<UInt32>(target, APIC_ICR_HIGH, apic_id << 24); Kernel::ke_dma_write<UInt32>(target, APIC_ICR_LOW, 0x00000600 | 0x00004000 | 0x00000000 | vector); @@ -200,10 +200,7 @@ Void mp_init_cores(VoidPtr vendor_ptr) noexcept { UInt8 type = *entry_ptr; UInt8 length = *(entry_ptr + 1); - // Avoid infinite loop on bad APIC tables. - if (length < 2) break; - - if (type == 0) { + if (type == 0 && length == sizeof(struct LAPIC)) { volatile LAPIC* entry_struct = (volatile LAPIC*) entry_ptr; if (entry_struct->Flags & 0x1) { @@ -212,15 +209,15 @@ Void mp_init_cores(VoidPtr vendor_ptr) noexcept { ++kSMPCount; - kout << "Kind: LAPIC: ON\r"; + kout << "AP: kind: LAPIC: ON.\r"; // 0x7c00, as recommended by the Intel SDM. hal_send_ipi_msg(kApicBaseAddress, entry_struct->ProcessorID, 0x7c); } else { - kout << "Kind: LAPIC: OFF\r"; + kout << "AP: kind: LAPIC: OFF.\r"; } } else { - kout << "Kind: UNKNOWN: OFF\r"; + kout << "AP: kind: UNKNOWN: OFF.\r"; } entry_ptr += length; 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<const Utf8Char*>* obj, const Utf8Char* bytes) { NE_UNUSED(bytes); diff --git a/dev/kernel/HALKit/AMD64/HalKernelMain.cc b/dev/kernel/HALKit/AMD64/HalKernelMain.cc index f121fbb4..6f4d7e0a 100644 --- a/dev/kernel/HALKit/AMD64/HalKernelMain.cc +++ b/dev/kernel/HALKit/AMD64/HalKernelMain.cc @@ -32,17 +32,14 @@ EXTERN_C Int32 hal_init_platform(Kernel::HEL::BootInfoHeader* handover_hdr) { HAL::rt_sti(); - kHandoverHeader = handover_hdr; - - FB::fb_clear_video(); - fw_init_efi((EfiSystemTable*) handover_hdr->f_FirmwareCustomTables[1]); Boot::ExitBootServices(handover_hdr->f_HardwareTables.f_ImageKey, handover_hdr->f_HardwareTables.f_ImageHandle); - kBitMapCursor = 0UL; - kKernelVM = kHandoverHeader->f_PageStart; + kHandoverHeader = handover_hdr; + + kKernelVM = kHandoverHeader->f_PageStart; if (!kKernelVM) { MUST_PASS(kKernelVM); @@ -55,6 +52,7 @@ EXTERN_C Int32 hal_init_platform(Kernel::HEL::BootInfoHeader* handover_hdr) { /* INITIALIZE BIT MAP. */ /************************************** */ + kBitMapCursor = 0UL; kKernelBitMpSize = kHandoverHeader->f_BitMapSize; kKernelBitMpStart = reinterpret_cast<VoidPtr>(reinterpret_cast<UIntPtr>(kHandoverHeader->f_BitMapStart)); @@ -120,6 +118,8 @@ EXTERN_C Int32 hal_init_platform(Kernel::HEL::BootInfoHeader* handover_hdr) { kGDTArray[4].fFlags = 0; kGDTArray[4].fBaseHigh = 0; + FB::fb_clear_video(); + // Load memory descriptors. HAL::Register64 gdt_reg; @@ -133,24 +133,14 @@ EXTERN_C Int32 hal_init_platform(Kernel::HEL::BootInfoHeader* handover_hdr) { return kEfiFail; } -EXTERN_C Kernel::Void hal_real_init(Kernel::Void) noexcept { +EXTERN_C Kernel::Void hal_real_init(Kernel::Void) { using namespace Kernel; - for (SizeT index = 0UL; index < HardwareThreadScheduler::The().Capacity(); ++index) { - HardwareThreadScheduler::The()[index].Leak()->Kind() = ThreadKind::kAPStandard; - HardwareThreadScheduler::The()[index].Leak()->ID() = index; - HardwareThreadScheduler::The()[index].Leak()->Busy(NO); - } - - for (SizeT index = 0UL; index < UserProcessScheduler::The().TheCurrentTeam().AsArray().Count(); - ++index) { - UserProcessScheduler::The().TheCurrentTeam().AsArray()[index].Status = - ProcessStatusKind::kInvalid; - } + HAL::Register64 idt_reg; + idt_reg.Base = reinterpret_cast<UIntPtr>(kInterruptVectorTable); - rtl_create_user_process(sched_idle_task, "MgmtSrv"); //! Mgmt command server. - rtl_create_user_process(sched_idle_task, "LaunchSrv"); //! launchd - rtl_create_user_process(sched_idle_task, "SecSrv"); //! Login Server + HAL::IDTLoader idt_loader; + idt_loader.Load(idt_reg); HAL::mp_init_cores(kHandoverHeader->f_HardwareTables.f_VendorPtr); @@ -162,13 +152,6 @@ EXTERN_C Kernel::Void hal_real_init(Kernel::Void) noexcept { NeFS::fs_init_nefs(); #endif - HAL::Register64 idt_reg; - idt_reg.Base = reinterpret_cast<UIntPtr>(kInterruptVectorTable); - - HAL::IDTLoader idt_loader; - - idt_loader.Load(idt_reg); - while (YES) ; } diff --git a/dev/kernel/HALKit/ARM64/HalHandoverStub.s b/dev/kernel/HALKit/ARM64/HalHandoverStub.s index 7c70ebfd..c0e7cb2b 100644 --- a/dev/kernel/HALKit/ARM64/HalHandoverStub.s +++ b/dev/kernel/HALKit/ARM64/HalHandoverStub.s @@ -9,11 +9,11 @@ .section .ldr - ;; MAGIC + ;; // MAGIC .quad 0xDAB4 - ;; VERSION (1.0.0) + ;; // VERSION (1.0.0) .word 100 - ;; CPU (ARM64) + ;; // CPU (ARM64) .word 0 - ;; TYPE (KERNEL) + ;; // TYPE (KERNEL) .word 122 diff --git a/dev/kernel/HALKit/ARM64/HalKernelMain.cc b/dev/kernel/HALKit/ARM64/HalKernelMain.cc index d7663f4e..b711b833 100644 --- a/dev/kernel/HALKit/ARM64/HalKernelMain.cc +++ b/dev/kernel/HALKit/ARM64/HalKernelMain.cc @@ -35,10 +35,6 @@ EXTERN_C void hal_init_platform(Kernel::HEL::BootInfoHeader* handover_hdr) { return; } - FB::fb_clear_video(); - - kBitMapCursor = 0UL; - #ifdef __NE_ARM64_EFI__ fw_init_efi((EfiSystemTable*) handover_hdr->f_FirmwareCustomTables[1]); @@ -46,31 +42,19 @@ EXTERN_C void hal_init_platform(Kernel::HEL::BootInfoHeader* handover_hdr) { handover_hdr->f_HardwareTables.f_ImageHandle); #endif + FB::fb_clear_video(); + /************************************** */ /* INITIALIZE BIT MAP. */ /************************************** */ + kBitMapCursor = 0UL; kKernelBitMpSize = kHandoverHeader->f_BitMapSize; kKernelBitMpStart = reinterpret_cast<Kernel::VoidPtr>( reinterpret_cast<Kernel::UIntPtr>(kHandoverHeader->f_BitMapStart)); /// @note do initialize the interrupts after it. - for (SizeT index = 0UL; index < HardwareThreadScheduler::The().Capacity(); ++index) { - HardwareThreadScheduler::The()[index].Leak()->Kind() = ThreadKind::kAPStandard; - HardwareThreadScheduler::The()[index].Leak()->Busy(NO); - } - - for (SizeT index = 0UL; index < UserProcessScheduler::The().TheCurrentTeam().AsArray().Count(); - ++index) { - UserProcessScheduler::The().TheCurrentTeam().AsArray()[index].Status = - ProcessStatusKind::kInvalid; - } - - rtl_create_user_process(sched_idle_task, "MgmtSrv"); //! Mgmt command server. - rtl_create_user_process(sched_idle_task, "LaunchSrv"); //! launchd - rtl_create_user_process(sched_idle_task, "SecSrv"); //! Login Server - Kernel::mp_init_cores(); while (YES) 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 <FSKit/Ext2.h> +#include <FSKit/Ext2+IFS.h> #include <FSKit/HeFS.h> #include <FSKit/NeFS.h> @@ -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/CxxAbi.h b/dev/kernel/NeKit/CxxAbi.h index 164a257d..4b7bf002 100644 --- a/dev/kernel/NeKit/CxxAbi.h +++ b/dev/kernel/NeKit/CxxAbi.h @@ -7,7 +7,7 @@ #include <NeKit/Defines.h> -#ifndef __TOOLCHAINKIT__ +#ifndef __NECTI__ #define kAtExitMacDestructors (128) @@ -17,10 +17,10 @@ struct atexit_func_entry_t { void* dso_handle; }; -typedef unsigned uarch_t; +typedef Kernel::UInt32 uarch_t; namespace cxxabiv1 { -typedef void* __guard; +typedef Kernel::SizeT* __guard; } -#endif // __GNUC__ +#endif // !__NECTI__ diff --git a/dev/kernel/NeKit/ErrorOr.h b/dev/kernel/NeKit/ErrorOr.h index b653e0ee..e8b3b6fc 100644 --- a/dev/kernel/NeKit/ErrorOr.h +++ b/dev/kernel/NeKit/ErrorOr.h @@ -38,6 +38,8 @@ class ErrorOr final { return *this; } + const T& Value() { return mRef.TryLeak(); } + Ref<T>& 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/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<voidPtr> 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; diff --git a/dev/kernel/src/AsciiUtils.cc b/dev/kernel/src/AsciiUtils.cc index 24e4e220..cca3a368 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) { @@ -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<const UInt8*>(src); auto d = reinterpret_cast<UInt8*>(dst); @@ -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 true; + 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; } @@ -166,7 +171,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/FS/Ext2+FileMgr.cc b/dev/kernel/src/FS/Ext2+FileMgr.cc index 810e7e44..7c28c0c9 100644 --- a/dev/kernel/src/FS/Ext2+FileMgr.cc +++ b/dev/kernel/src/FS/Ext2+FileMgr.cc @@ -7,8 +7,1555 @@ #ifndef __NE_MINIMAL_OS__ #ifdef __FSKIT_INCLUDES_EXT2__ +#include <FSKit/Ext2+IFS.h> +#include <FSKit/Ext2.h> +#include <KernelKit/DebugOutput.h> #include <KernelKit/FileMgr.h> #include <KernelKit/HeapMgr.h> +#include <NeKit/ErrorOr.h> +#include <NeKit/KString.h> +#include <NeKit/KernelPanic.h> +#include <NeKit/Utils.h> -#endif // ifdef __FSKIT_INCLUDES_EXT2__ -#endif // ifndef __NE_MINIMAL_OS__ +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 UInt32 ext2_block_to_lba(Ext2Context* ctx, + UInt32 blockNumber) { + if (!ctx || !ctx->drive) return 0; + 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<UInt32*> ext2_read_block_ptr(Ext2Context* ctx, + UInt32 blockNumber) { + if (!ctx || !ctx->drive || !ctx->superblock) + return ErrorOr<UInt32*>(kErrorInvalidData); + + UInt32 blockSize = ctx->BlockSize(); + auto buf = (UInt32*) mm_alloc_ptr(blockSize, true, false); + if (!buf) return ErrorOr<UInt32*>(kErrorHeapOutOfMemory); + + UInt32 lba = ext2_block_to_lba(ctx, blockNumber); + if (!ext2_read_block(ctx->drive, lba, buf, blockSize)) { + mm_free_ptr(buf); + return ErrorOr<UInt32*>(kErrorDisk); + } + return ErrorOr<UInt32*>(buf); +} + +// Get the block address for a given logical block index +static ErrorOr<UInt32> ext2_get_block_address(Ext2Context* ctx, Ext2Node* node, + UInt32 logicalIndex) { + if (!ctx || !node || !ctx->drive) return ErrorOr<UInt32>(kErrorInvalidData); + + UInt32 blockSize = ctx->BlockSize(); + UInt32 pointersPerBlock = blockSize / sizeof(UInt32); + + // Direct blocks + if (logicalIndex < EXT2_DIRECT_BLOCKS) { + UInt32 bn = node->inode.fBlock[logicalIndex]; + if (bn == 0) return ErrorOr<UInt32>(kErrorInvalidData); + return ErrorOr<UInt32>(bn); + } + + // Single indirect blocks + if (logicalIndex < (EXT2_DIRECT_BLOCKS + pointersPerBlock)) { + UInt32 iblock = node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX]; + if (iblock == 0) return ErrorOr<UInt32>(kErrorInvalidData); + + auto res = ext2_read_block_ptr(ctx, iblock); + if (!res) return ErrorOr<UInt32>(res.Error()); + + // Using dereference operator + UInt32* ptr = *res.Leak(); // operator* returns T (UInt32*) + + UInt32 val = ptr[logicalIndex - EXT2_DIRECT_BLOCKS]; + mm_free_ptr(ptr); + + if (val == 0) return ErrorOr<UInt32>(kErrorInvalidData); + return ErrorOr<UInt32>(val); + } + + // Double indirect blocks + UInt32 doubleStart = EXT2_DIRECT_BLOCKS + pointersPerBlock; + UInt32 doubleSpan = pointersPerBlock * pointersPerBlock; + if (logicalIndex < (doubleStart + doubleSpan)) { + UInt32 db = node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX]; + if (db == 0) return ErrorOr<UInt32>(kErrorInvalidData); + + auto dblRes = ext2_read_block_ptr(ctx, db); + if (!dblRes) return ErrorOr<UInt32>(dblRes.Error()); + + UInt32* dblPtr = *dblRes.Leak(); + + 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<UInt32>(kErrorInvalidData); + + auto singleRes = ext2_read_block_ptr(ctx, singleBlockNum); + if (!singleRes) return ErrorOr<UInt32>(singleRes.Error()); + + UInt32* singlePtr = *singleRes.Leak(); + UInt32 val = singlePtr[secondIdx]; + mm_free_ptr(singlePtr); + + if (val == 0) return ErrorOr<UInt32>(kErrorInvalidData); + return ErrorOr<UInt32>(val); + } + + return ErrorOr<UInt32>(kErrorUnimplemented); +} + +static ErrorOr<voidPtr> ext2_read_inode_data(Ext2Context* ctx, Ext2Node* node, + SizeT size) { + if (!ctx || !ctx->drive || !node || size == 0) return ErrorOr<voidPtr>(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<voidPtr>(2); // nothing to read + + auto buffer = mm_alloc_ptr(bytesToRead, true, false); + if (!buffer) return ErrorOr<voidPtr>(3); // allocation failed + + UInt32 currentOffset = node->cursor; + SizeT remaining = bytesToRead; + UInt8* dest = reinterpret_cast<UInt8*>(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<voidPtr>(phys.Error()); + } + + auto 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<voidPtr>(4); // block buffer allocation failed + } + + if (!ext2_read_block(ctx->drive, lba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + mm_free_ptr(buffer); + return ErrorOr<voidPtr>(5); // block read failed + } + + SizeT chunk = ext2_min(remaining, blockSize - offsetInBlock); + rt_copy_memory_safe(static_cast<void*>(static_cast<UInt8*>(blockBuf) + offsetInBlock), + static_cast<void*>(dest), chunk, chunk); + + mm_free_ptr(blockBuf); + + currentOffset += static_cast<UInt32>(chunk); + dest += chunk; + remaining -= chunk; + } + + node->cursor += static_cast<UInt32>(bytesToRead); + return ErrorOr<voidPtr>(buffer); +} + +// Get group descriptor information for a given block/inode number +static ErrorOr<Ext2GroupInfo*> ext2_get_group_descriptor_info(Ext2Context* ctx, + UInt32 targetBlockOrInode) { + if (!ctx || !ctx->superblock || !ctx->drive) return ErrorOr<Ext2GroupInfo*>(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<Ext2GroupInfo*>(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<UInt32>((totalBlocks + blocksPerGroup - 1) / blocksPerGroup); + if (groupIndex >= groupsCount) return ErrorOr<Ext2GroupInfo*>(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<UInt64>(groupIndex) * descSize; + + // Which EXT2 block contains that descriptor? + UInt32 blockOffsetWithinGdt = static_cast<UInt32>(descByteOffset / blockSize); + UInt32 offsetInGroupDescBlock = static_cast<UInt32>(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<Ext2GroupInfo*>(kErrorHeapOutOfMemory); + + UInt32 groupDescriptorLba = ext2_block_to_lba(ctx, groupDescriptorBlock); + if (!ext2_read_block(ctx->drive, groupDescriptorLba, blockBuffer, blockSize)) { + mm_free_ptr(blockBuffer); + return ErrorOr<Ext2GroupInfo*>(kErrorDisk); + } + + auto groupInfo = (Ext2GroupInfo*) mm_alloc_ptr(sizeof(Ext2GroupInfo), true, false); + if (!groupInfo) { + mm_free_ptr(blockBuffer); + return ErrorOr<Ext2GroupInfo*>(kErrorHeapOutOfMemory); + } + + groupInfo->groupDesc = reinterpret_cast<EXT2_GROUP_DESCRIPTOR*>( + reinterpret_cast<UInt8*>(blockBuffer) + offsetInGroupDescBlock); + groupInfo->groupDescriptorBlock = groupDescriptorBlock; + groupInfo->offsetInGroupDescBlock = offsetInGroupDescBlock; + groupInfo->blockBuffer = reinterpret_cast<UInt8*>(blockBuffer); + + return ErrorOr<Ext2GroupInfo*>(groupInfo); +} + +// Allocate a new block +inline ErrorOr<UInt32> ext2_alloc_block(Ext2Context* ctx, + EXT2_GROUP_DESCRIPTOR* groupDesc) { + if (!ctx || !ctx->superblock || !groupDesc) return ErrorOr<UInt32>(kErrorInvalidData); + + UInt32 blockSize = ctx->BlockSize(); + + // for the bitmap + auto bitmap = mm_alloc_ptr(blockSize, true, false); + if (!bitmap) return ErrorOr<UInt32>(kErrorHeapOutOfMemory); + + // Read block bitmap + if (!ext2_read_block(ctx->drive, groupDesc->fBlockBitmap, bitmap, blockSize)) { + mm_free_ptr(bitmap); + return ErrorOr<UInt32>(kErrorDisk); + } + + // bit = 0 + for (UInt32 byteIdx = 0; byteIdx < blockSize; ++byteIdx) { + auto byte = reinterpret_cast<unsigned char*>(bitmap)[byteIdx]; + if (byte != 0xFF) { + for (int bit = 0; bit < 8; ++bit) { + if (!(byte & (1 << bit))) { + // Mark bit as used + reinterpret_cast<unsigned char*>(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<UInt32>(kErrorDisk); + } + + // Update group descriptor free count + groupDesc->fFreeBlocksCount--; + mm_free_ptr(bitmap); + return ErrorOr<UInt32>(blockNumber); + } + } + } + } + + mm_free_ptr(bitmap); + return ErrorOr<UInt32>(kErrorDiskIsFull); +} + +// Indirect blocks +static ErrorOr<Void*> ext2_set_block_address(Ext2Context* ctx, + Ext2Node* node, + UInt32 logicalBlockIndex, + UInt32 physicalBlockNumber) { + using namespace Kernel; + + if (!ctx || !ctx->drive || !node) return ErrorOr<Void*>(kErrorInvalidData); + + auto blockSize = ctx->BlockSize(); + UInt32 blocksPerPointerBlock = blockSize / sizeof(UInt32); + + // Direct blocks + if (logicalBlockIndex < EXT2_DIRECT_BLOCKS) { + node->inode.fBlock[logicalBlockIndex] = physicalBlockNumber; + return ErrorOr<Void*>(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<Void*>(groupInfoRes.Error()); + + auto groupInfo = groupInfoRes.Leak().Leak(); // Ref<Ext2GroupInfo*> + auto newBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); + if (newBlockRes.HasError()) { + mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr<Void*>(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<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr<Void*>(kErrorDisk); + } + + mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + // Zero out new indirect block + auto zeroBuf = mm_alloc_ptr(blockSize, true, false); + if (!zeroBuf) return ErrorOr<Void*>(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<Void*>(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<Void*>(indirectRes.Error()); + + UInt32* indirectPtr = indirectRes.Leak().Leak(); // Ref<UInt32*> + 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<Void*>(kErrorDisk); + } + + mm_free_ptr(indirectPtr); + return ErrorOr<Void*>(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<Void*>(groupInfoRes.Error()); + + auto groupInfo = groupInfoRes.Leak().Leak(); + auto newBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); + if (newBlockRes.HasError()) { + mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr<Void*>(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<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr<Void*>(kErrorDisk); + } + + mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + // Zero new double-indirect block + auto zeroBuf = mm_alloc_ptr(blockSize, true, false); + if (!zeroBuf) return ErrorOr<Void*>(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<Void*>(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<Void*>(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<Void*>(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<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr<Void*>(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<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr<Void*>(kErrorDisk); + } + + mm_free_ptr(reinterpret_cast<void*>(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<Void*>(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<Void*>(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<Void*>(kErrorDisk); + } + } + + mm_free_ptr(doublePtr); + + // Write to single-indirect block + auto singleRes = ext2_read_block_ptr(ctx, singleIndirectBlock); + if (singleRes.HasError()) return ErrorOr<Void*>(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<Void*>(kErrorDisk); + } + + mm_free_ptr(singlePtr); + return ErrorOr<Void*>(nullptr); + } + + // Triple indirect blocks not implemented + return ErrorOr<Void*>(kErrorUnimplemented); +} + +// Find a directory entry by name within a directory inode +static ErrorOr<EXT2_DIR_ENTRY*> ext2_find_dir_entry(Ext2Context* ctx, Ext2Node* dirNode, + const char* name) { + if (!ctx || !ctx->drive || !dirNode || !name) + return ErrorOr<EXT2_DIR_ENTRY*>(kErrorInvalidData); + + // Check directory type + auto type = (dirNode->inode.fMode >> 12) & 0xF; + if (type != kExt2FileTypeDirectory) return ErrorOr<EXT2_DIR_ENTRY*>(kErrorInvalidData); + + UInt32 blockSize = ctx->BlockSize(); + auto blockBuf = mm_alloc_ptr(blockSize, true, false); + if (!blockBuf) return ErrorOr<EXT2_DIR_ENTRY*>(kErrorHeapOutOfMemory); + + SizeT nameLen = rt_string_len(name); + for (UInt32 i = 0; i < EXT2_DIRECT_BLOCKS; ++i) { + UInt32 blockNum = dirNode->inode.fBlock[i]; + if (blockNum == 0) continue; + + UInt32 lba = ext2_block_to_lba(ctx, blockNum); + if (!ext2_read_block(ctx->drive, lba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return ErrorOr<EXT2_DIR_ENTRY*>(kErrorDisk); + } + + UInt32 offset = 0; + while (offset + sizeof(UInt32) + sizeof(UInt16) <= blockSize) { + auto onDiskEntry = reinterpret_cast<EXT2_DIR_ENTRY*>((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<EXT2_DIR_ENTRY*>(kErrorHeapOutOfMemory); + } + + // Copy only record-length bytes + rt_copy_memory_safe(onDiskEntry, found, onDiskEntry->fRecordLength, recSize); + mm_free_ptr(blockBuf); + return ErrorOr<EXT2_DIR_ENTRY*>(found); + } + } + offset += onDiskEntry->fRecordLength; + } + } + + mm_free_ptr(blockBuf); + return ErrorOr<EXT2_DIR_ENTRY*>(kErrorFileNotFound); +} + +// Compute ideal record length for a directory name +static inline UInt16 ext2_dir_entry_ideal_len(UInt8 nameLen) { + UInt16 raw = + static_cast<UInt16>(8 + nameLen); // 8 = inode(4)+rec_len(2)+name_len(1)+file_type(1) + return static_cast<UInt16>((raw + 3) & ~3u); // align up to 4 +} + +static ErrorOr<Void*> 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<Void*>(kErrorInvalidData); + + UInt32 blockSize = ctx->BlockSize(); + SizeT nameLen = rt_string_len(name); + if (nameLen == 0 || nameLen > 255) return ErrorOr<Void*>(kErrorInvalidData); + + UInt16 newRecIdeal = ext2_dir_entry_ideal_len(static_cast<UInt8>(nameLen)); + + auto blockBuf = mm_alloc_ptr(blockSize, true, false); + if (!blockBuf) return ErrorOr<Void*>(kErrorHeapOutOfMemory); + + for (UInt32 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<Void*>(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<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(blockBuf); + return ErrorOr<Void*>(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<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(blockBuf); + return ErrorOr<Void*>(kErrorDisk); + } + + mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + // Zero block & insert entry + rt_zero_memory(blockBuf, blockSize); + auto entry = reinterpret_cast<EXT2_DIR_ENTRY*>(blockBuf); + entry->fInode = inodeNumber; + entry->fNameLength = static_cast<UInt8>(nameLen); + entry->fFileType = fileType; + entry->fRecordLength = static_cast<UInt16>(blockSize); + rt_copy_memory_safe(const_cast<char*>(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<Void*>(kErrorDisk); + } + + auto setRes = ext2_set_block_address(ctx, parentDirNode, bi, newBlock); + if (!setRes) { + mm_free_ptr(blockBuf); + return ErrorOr<Void*>(setRes.Error()); + } + + mm_free_ptr(blockBuf); + return ErrorOr<Void*>(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<Void*>(kErrorDisk); + } + + UInt32 offset = 0; + EXT2_DIR_ENTRY* lastEntry = nullptr; + UInt32 lastOffset = 0; + + while (offset < blockSize) { + if (offset + 8 > blockSize) break; + auto e = reinterpret_cast<EXT2_DIR_ENTRY*>((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<EXT2_DIR_ENTRY*>((UInt8*) blockBuf + newOffset); + newEntry->fInode = inodeNumber; + newEntry->fNameLength = static_cast<UInt8>(nameLen); + newEntry->fFileType = fileType; + newEntry->fRecordLength = static_cast<UInt16>(origRec - lastIdeal); + rt_copy_memory_safe(const_cast<char*>(name), newEntry->fName, nameLen, + newEntry->fRecordLength); + + if (!ext2_write_block(ctx->drive, blockLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return ErrorOr<Void*>(kErrorDisk); + } + + mm_free_ptr(blockBuf); + return ErrorOr<Void*>(nullptr); + } + } + + // No space in direct blocks -> allocate new block + int targetIndex = -1; + for (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<Void*>(kErrorUnimplemented); + } + + auto groupInfoResult = ext2_get_group_descriptor_info(ctx, parentDirNode->inodeNumber); + if (!groupInfoResult) { + mm_free_ptr(blockBuf); + return ErrorOr<Void*>(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<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(blockBuf); + return ErrorOr<Void*>(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<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(blockBuf); + return ErrorOr<Void*>(kErrorDisk); + } + + mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + rt_zero_memory(blockBuf, blockSize); + auto entry = reinterpret_cast<EXT2_DIR_ENTRY*>(blockBuf); + entry->fInode = inodeNumber; + entry->fNameLength = static_cast<UInt8>(nameLen); + entry->fFileType = fileType; + entry->fRecordLength = static_cast<UInt16>(blockSize); + rt_copy_memory_safe(const_cast<char*>(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<Void*>(kErrorDisk); + } + + auto setRes = ext2_set_block_address(ctx, parentDirNode, targetIndex, newBlockNum); + if (!setRes) { + mm_free_ptr(blockBuf); + return ErrorOr<Void*>(setRes.Error()); + } + + mm_free_ptr(blockBuf); + return ErrorOr<Void*>(nullptr); +} + +// Soon +static ErrorOr<UInt32> ext2_alloc_inode(Ext2Context* ctx, + EXT2_GROUP_DESCRIPTOR* groupDesc) { + if (!ctx || !ctx->superblock || !groupDesc) return ErrorOr<UInt32>(kErrorInvalidData); + + UInt32 blockSize = ctx->BlockSize(); + + // buffer for the inode bitmap + auto bitmap = mm_alloc_ptr(blockSize, true, false); + if (!bitmap) return ErrorOr<UInt32>(kErrorHeapOutOfMemory); + + // Read inode bitmap + if (!ext2_read_block(ctx->drive, groupDesc->fInodeBitmap, bitmap, blockSize)) { + mm_free_ptr(bitmap); + return ErrorOr<UInt32>(kErrorDisk); + } + + // Find first free inode (bit = 0) + for (UInt32 byteIdx = 0; byteIdx < blockSize; ++byteIdx) { + auto byte = reinterpret_cast<unsigned char*>(bitmap)[byteIdx]; + if (byte != 0xFF) { + for (int bit = 0; bit < 8; ++bit) { + if (!(byte & (1 << bit))) { + // Mark bit as used + reinterpret_cast<unsigned char*>(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<UInt32>(kErrorDisk); + } + + // Update group descriptor free count + groupDesc->fFreeInodesCount--; + mm_free_ptr(bitmap); + return ErrorOr<UInt32>(inodeNumber); + } + } + } + } + + mm_free_ptr(bitmap); + return ErrorOr<UInt32>(kErrorDiskIsFull); +} + +// to write an inode to its correct location on disk +static ErrorOr<Void*> ext2_write_inode(Ext2Context* ctx, Ext2Node* node) { + using namespace Kernel; + + if (!ctx || !ctx->superblock || !ctx->drive || !node) return ErrorOr<Void*>(kErrorInvalidData); + + auto blockSize = ctx->BlockSize(); + UInt32 inodesPerGroup = ctx->superblock->fInodesPerGroup; + + if (inodesPerGroup == 0) return ErrorOr<Void*>(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<Void*>(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<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr<Void*>(kErrorHeapOutOfMemory); + } + + if (!ext2_read_block(ctx->drive, inodeLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr<Void*>(kErrorDisk); + } + + // Copy the updated inode into the block buffer + rt_copy_memory_safe(&node->inode, static_cast<void*>((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<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr<Void*>(kErrorDisk); + } + + mm_free_ptr(blockBuf); + mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + return ErrorOr<Void*>(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; + } + + UInt32 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 (UInt32 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 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; + } + + // 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().Leak(); + heapNode->cursor = 0; + return reinterpret_cast<NodePtr>(heapNode); + } + + PathComponents pathComponents(path); + if (pathComponents.count == 0) { + return nullptr; + } + + UInt32 currentInodeNumber = EXT2_ROOT_INODE; + Ext2Node* currentDirNode = nullptr; + + 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); + 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().Leak(); + currentDirNode->cursor = 0; + + if (i < pathComponents.count - 1U) { + 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().Leak(); + resultNode->cursor = 0; + return reinterpret_cast<NodePtr>(resultNode); +} + +void* Ext2FileSystemParser::Read(NodePtr node, Int32 flags, SizeT size) { + if (!node) return nullptr; + + NE_UNUSED(flags); + + auto extNode = reinterpret_cast<Ext2Node*>(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<UInt32>(size); + } + + return data; +} + +void Ext2FileSystemParser::Write(NodePtr node, void* data, Int32 flags, SizeT size) { + if (!node || !data || size == 0) return; + + NE_UNUSED(flags); + + auto extNode = reinterpret_cast<Ext2Node*>(node); + auto blockSize = this->ctx.BlockSize(); + SizeT bytesWritten = 0; + + UInt32 currentOffset = extNode->cursor; + UInt8* src = reinterpret_cast<UInt8*>(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<void*>(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<void*>(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<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return; + } + + mm_free_ptr(reinterpret_cast<void*>(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<UInt32>(ext2_min(size - bytesWritten, blockSize - offsetInBlock)); + rt_copy_memory_safe(src, static_cast<void*>((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 Ext2FileSystemParser::Seek(NodePtr node, SizeT offset) { + if (!node) return false; + auto extNode = reinterpret_cast<Ext2Node*>(node); + extNode->cursor = static_cast<UInt32>(offset); + return true; +} + +SizeT Ext2FileSystemParser::Tell(NodePtr node) { + if (!node) return 0; + auto extNode = reinterpret_cast<Ext2Node*>(node); + return extNode->cursor; +} + +bool Ext2FileSystemParser::Rewind(NodePtr node) { + if (!node) return false; + auto extNode = reinterpret_cast<Ext2Node*>(node); + extNode->cursor = 0; + return true; +} + +void* Ext2FileSystemParser::Read(const char* name, NodePtr node, Int32 flags, SizeT size) { + NE_UNUSED(name); + return Read(node, flags, size); +} + +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 (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++] = '/'; + rt_copy_memory_safe(const_cast<char*>(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<Ext2Node*>(parentDirNodePtr) = *inodeRes.Leak().Leak(); + reinterpret_cast<Ext2Node*>(parentDirNodePtr)->cursor = 0; + } else { + parentDirNodePtr = Open(parentPathBuf, "r"); + } + + if (!parentDirNodePtr) return nullptr; + + auto parentDirNode = reinterpret_cast<Ext2Node*>(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<void*>(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<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(parentDirNode); + return nullptr; + } + + mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + // Create new Ext2Node + Ext2Node* newFileNode = reinterpret_cast<Ext2Node*>(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 + + NE_UNUSED(parentWriteRes); + + mm_free_ptr(parentDirNode); + return reinterpret_cast<NodePtr>(newFileNode); +} + +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; + } + + 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 (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"; + return nullptr; + } + + if (i > 0) parentPathBuf[currentPathLen++] = '/'; + + rt_copy_memory_safe(static_cast<void*>(const_cast<char*>(pathComponents.components[i])), + static_cast<void*>(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<NodePtr>(mm_alloc_ptr(sizeof(Ext2Node), true, false)); + if (!parentDirNodePtr) return nullptr; + + *reinterpret_cast<Ext2Node*>(parentDirNodePtr) = *inodeRes.Leak().Leak(); + reinterpret_cast<Ext2Node*>(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<Ext2Node*>(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<void*>(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<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + mm_free_ptr(parentDirNode); + return nullptr; + } + + mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + + // Create new Ext2Node and initialize inode fields + Ext2Node* newDirNode = reinterpret_cast<Ext2Node*>(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<void*>(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<void*>(groupForBlock->blockBuffer)); + mm_free_ptr(groupForBlock); + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + mm_free_ptr(reinterpret_cast<void*>(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<EXT2_DIR_ENTRY*>(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<EXT2_DIR_ENTRY*>((UInt8*) dirBlockBuf + dot->fRecordLength); + dotdot->fInode = parentDirNode->inodeNumber; + dotdot->fNameLength = 2; + dotdot->fFileType = kExt2FileTypeDirectory; + dotdot->fRecordLength = static_cast<UInt16>(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<NodePtr>(newDirNode); +} + +#endif +#endif 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 <FSKit/Ext2.h> +#include <FSKit/Ext2+IFS.h> #include <FirmwareKit/EPM.h> #include <KernelKit/KPC.h> #include <KernelKit/ProcessScheduler.h> 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; } 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<Utf8Char*>(src); Utf8Char* dstChar = reinterpret_cast<Utf8Char*>(dst); diff --git a/dev/launch/.keep b/dev/launch/.keep new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/dev/launch/.keep diff --git a/dev/launch/LaunchKit/.keep b/dev/launch/LaunchKit/.keep new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/dev/launch/LaunchKit/.keep diff --git a/dev/launch/LaunchKit/LaunchKit.h b/dev/launch/LaunchKit/LaunchKit.h new file mode 100644 index 00000000..2fa9607b --- /dev/null +++ b/dev/launch/LaunchKit/LaunchKit.h @@ -0,0 +1,20 @@ +/* ------------------------------------------- + + Copyright (C) 2025, Amlal El Mahrouss, all rights reserved. + + ------------------------------------------- */ + +#pragma once + +#include <libSystem/SystemKit/System.h> + +/// @author Amlal El Mahrouss +/// @brief NeKernel Launch Kit. + +#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 new file mode 100644 index 00000000..e3d26a46 --- /dev/null +++ b/dev/launch/ne_launch.json @@ -0,0 +1,20 @@ +{ + "compiler_path": "x86_64-w64-mingw32-g++", + "compiler_std": "c++20", + "headers_path": ["../", "./"], + "sources_path": ["src/*.cc", "src/*.S"], + "output_name": "ne_launch", + "compiler_flags": [ + "-ffreestanding", + "-shared", + "-fPIC", + "-fno-rtti", + "-fno-exceptions", + "-Wl,--subsystem=17" + ], + "cpp_macros": [ + "kNeLaunchVersion=0x0100", + "kNeLaunchVersionHighest=0x0100", + "kNeLaunchVersionLowest=0x0100" + ] +} diff --git a/dev/launch/obj/.keep b/dev/launch/obj/.keep new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/dev/launch/obj/.keep diff --git a/dev/launch/src/.keep b/dev/launch/src/.keep new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/dev/launch/src/.keep diff --git a/dev/launch/src/CRT0.S b/dev/launch/src/CRT0.S new file mode 100644 index 00000000..84a293e6 --- /dev/null +++ b/dev/launch/src/CRT0.S @@ -0,0 +1,18 @@ +/* ------------------------------------------- + + Copyright (C) 2025, Amlal El Mahrouss, all rights reserved. + + ------------------------------------------- */ + + .text + +.extern nelaunch_startup_fn +.globl _NeMain + +_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 new file mode 100644 index 00000000..f5c9ee3c --- /dev/null +++ b/dev/launch/src/LaunchSrv.cc @@ -0,0 +1,17 @@ +/* ------------------------------------------- + + Copyright (C) 2025, Amlal El Mahrouss, all rights reserved. + + ------------------------------------------- */ + +#include <LaunchKit/LaunchKit.h> +#include <libSystem/SystemKit/Err.h> + +/// @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 NeKernel services..."); + + return kErrorSuccess; +} diff --git a/dev/libDDK/DriverKit/ddk.h b/dev/libDDK/DriverKit/ddk.h index 254137f9..2e2dddf0 100644 --- a/dev/libDDK/DriverKit/ddk.h +++ b/dev/libDDK/DriverKit/ddk.h @@ -1,6 +1,6 @@ /* ------------------------------------------- - Copyright Amlal El Mahrouss. + Copyright Amlal El Mahrouss 2025, all rights reserved. FILE: ddk.h PURPOSE: DDK Driver model base header. @@ -29,6 +29,8 @@ struct DDK_STATUS_STRUCT DDK_FINAL { struct DDK_OBJECT_MANIFEST* s_object; }; +typedef void* ptr_t; + /// @brief Call Kernel procedure. /// @param name the procedure name. /// @param cnt number of elements in **dat** 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 <CompilerKit/CompilerKit.h> +#include <DriverKit/macros.h> + +#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/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 <DriverKit/io.h> 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_kernel_call.c b/dev/libDDK/src/ddk_kernel_call.c index 1ac0a0aa..5976665b 100644 --- a/dev/libDDK/src/ddk_kernel_call.c +++ b/dev/libDDK/src/ddk_kernel_call.c @@ -12,8 +12,8 @@ #include <stdarg.h> /// @brief this is an internal call, do not use it. -DDK_EXTERN ATTRIBUTE(naked) void* __ke_call_dispatch(const int32_t name, int32_t cnt, void* data, - size_t sz); +DDK_EXTERN ATTRIBUTE(naked) ptr_t + __ke_call_dispatch(const int32_t name, int32_t cnt, void* data, size_t sz); /// @brief This function hashes the path into a FNV symbol. /// @param path the path to hash. /// @retval 0 symbol wasn't hashed. diff --git a/dev/libDDK/src/ddk_kernel_call_dispatch.S b/dev/libDDK/src/ddk_kernel_call_dispatch.S index 7b7a26cb..a607fe40 100644 --- a/dev/libDDK/src/ddk_kernel_call_dispatch.S +++ b/dev/libDDK/src/ddk_kernel_call_dispatch.S @@ -21,10 +21,11 @@ __ke_call_dispatch: push rbp mov rbp, rsp + /* registers have already been pushed. */ + int kKernelCallTrapId pop rbp - ret #elif defined(__DDK_POWER64__) 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) { diff --git a/dev/libSystem/SystemKit/Syscall.h b/dev/libSystem/SystemKit/Syscall.h index a1505b46..35bfc6f8 100644 --- a/dev/libSystem/SystemKit/Syscall.h +++ b/dev/libSystem/SystemKit/Syscall.h @@ -14,22 +14,8 @@ IMPORT_C VoidPtr libsys_syscall_arg_2(SizeT id, VoidPtr arg1); IMPORT_C VoidPtr libsys_syscall_arg_3(SizeT id, VoidPtr arg1, VoidPtr arg3); IMPORT_C VoidPtr libsys_syscall_arg_4(SizeT id, VoidPtr arg1, VoidPtr arg3, VoidPtr arg4); -inline UInt64 libsys_hash_64(const Char* path) { - if (!path || *path == 0) return 0; - - const UInt64 FNV_OFFSET_BASIS = 0xcbf29ce484222325ULL; - const UInt64 FNV_PRIME = 0x100000001b3ULL; - - UInt64 hash = FNV_OFFSET_BASIS; - - while (*path) { - hash ^= (Char) (*path++); - hash *= FNV_PRIME; - } - - return hash; -} +IMPORT_C UInt64 libsys_hash_64(const Char* path); #ifndef SYSCALL_HASH #define SYSCALL_HASH(str) libsys_hash_64(str) -#endif // !SYSCALL_HASH
\ No newline at end of file +#endif // !SYSCALL_HASH diff --git a/dev/libSystem/SystemKit/Verify.h b/dev/libSystem/SystemKit/Verify.h new file mode 100644 index 00000000..cbf85830 --- /dev/null +++ b/dev/libSystem/SystemKit/Verify.h @@ -0,0 +1,40 @@ +/* ------------------------------------------- + + Copyright (C) 2025, Amlal El Mahrouss, all rights reserved. + + File: Verify.h + Purpose: System Call Interface Verification Layer. + + ------------------------------------------- */ + +#pragma once + +#include <libSystem/SystemKit/System.h> + +namespace LibSystem::Detail { +/// @author 0xf00sec, and Amlal El Mahrouss +/// @brief safe cast operator. +template <typename T, typename R = VoidPtr> +inline R sys_safe_cast(const T* ptr) { + _rtl_assert(ptr, "safe cast failed!"); + return static_cast<R>(const_cast<T*>(ptr)); +} + +template <typename T, typename U> +struct must_cast_traits { + constexpr static BOOL value = false; +}; + +template <typename T> +struct must_cast_traits<T, T> { + constexpr static BOOL value = true; +}; + +/// @author Amlal El Mahrouss +/// @brief Safe constexpr cast. +template <typename T, typename R> +inline constexpr R* sys_constexpr_cast(T* ptr) { + static_assert(must_cast_traits<T, R>::value, "constexpr cast failed! types are a mismatch!"); + return static_cast<R*>(ptr); +} +} // namespace LibSystem::Detail diff --git a/dev/libSystem/src/System.cc b/dev/libSystem/src/System.cc index 3870ff18..da9931fe 100644 --- a/dev/libSystem/src/System.cc +++ b/dev/libSystem/src/System.cc @@ -7,14 +7,9 @@ #include <libSystem/SystemKit/Err.h>
#include <libSystem/SystemKit/Syscall.h>
#include <libSystem/SystemKit/System.h>
+#include <libSystem/SystemKit/Verify.h>
-namespace Detail {
-template <typename T>
-static VoidPtr safe_void_cast(const T* ptr) {
- _rtl_assert(ptr, "safe void cast failed!");
- return static_cast<VoidPtr>(const_cast<T*>(ptr));
-}
-} // namespace Detail
+using namespace LibSystem;
IMPORT_C Void _rtl_assert(Bool expr, const Char* origin) {
if (!expr) {
@@ -23,6 +18,29 @@ IMPORT_C Void _rtl_assert(Bool expr, const Char* origin) { }
}
+/// @note this uses the FNV 64-bit variant.
+IMPORT_C UInt64 libsys_hash_64(const Char* path) {
+ if (!path || *path == 0) return 0;
+
+ const UInt64 kFNVSeed = 0xcbf29ce484222325ULL;
+ const UInt64 kFNVPrime = 0x100000001b3ULL;
+
+ UInt64 hash = kFNVSeed;
+
+ while (*path) {
+ hash ^= (Char) (*path++);
+ hash *= kFNVPrime;
+ }
+
+ return hash;
+}
+
+IMPORT_C Char* StrFmt(const Char* fmt, ...) {
+ if (!fmt || *fmt == 0) return const_cast<Char*>("(null)");
+
+ return const_cast<Char*>("");
+}
+
// 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
@@ -41,8 +59,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<UIntPtr>(rs) % sizeof(UInt64) == 0) &&
(reinterpret_cast<UIntPtr>(rd) % sizeof(UInt64) == 0)) {
- auto rsw = reinterpret_cast<const UInt64*>(rs);
- auto rdw = reinterpret_cast<UInt64*>(rd);
+ auto rsw = reinterpret_cast<const UInt64*>(rs);
+ auto rdw = reinterpret_cast<UInt64*>(rd);
+
SizeT words = len / sizeof(UInt64);
for (SizeT i = 0; i < words; ++i) {
@@ -88,9 +107,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<SInt64>(p - in);
}
@@ -125,9 +146,8 @@ IMPORT_C VoidPtr MmFillMemory(_Input VoidPtr dest, _Input SizeT len, _Input UInt }
IMPORT_C Ref IoOpenFile(_Input const Char* path, _Input const Char* drv_letter) {
- return static_cast<Ref>(libsys_syscall_arg_3(SYSCALL_HASH("IoOpenFile"),
- Detail::safe_void_cast(path),
- Detail::safe_void_cast(drv_letter)));
+ return static_cast<Ref>(libsys_syscall_arg_3(
+ SYSCALL_HASH("IoOpenFile"), Detail::sys_safe_cast(path), Detail::sys_safe_cast(drv_letter)));
}
IMPORT_C Void IoCloseFile(_Input Ref desc) {
@@ -163,7 +183,7 @@ IMPORT_C SInt32 PrintOut(_Input IORef desc, const Char* fmt, ...) { // if truncated, `needed` >= kBufferSz; we still send truncated buffer
auto ret_ptr = libsys_syscall_arg_3(SYSCALL_HASH("PrintOut"), static_cast<VoidPtr>(desc),
- Detail::safe_void_cast(buf));
+ Detail::sys_safe_cast(buf));
if (!ret_ptr) return -kErrorInvalidData;
diff --git a/dev/misc/BenchKit/HWChronometer.h b/dev/misc/BenchKit/HWChronometer.h index e232db7e..56e4f2af 100644 --- a/dev/misc/BenchKit/HWChronometer.h +++ b/dev/misc/BenchKit/HWChronometer.h @@ -14,7 +14,7 @@ struct HWChronoTraits; template <typename ChronoTraits = HWChronoTraits> class HWChrono; -/// @brief BenchKit chrono logic for x64. +/// @brief BenchKit chrono logic for x64/ARM64. struct HWChronoTraits final { private: STATIC UInt64 TickImpl_(void) { |
