summaryrefslogtreecommitdiffhomepage
path: root/dev/kernel
diff options
context:
space:
mode:
authorAmlal El Mahrouss <amlal@nekernel.org>2025-09-28 17:01:48 +0200
committerGitHub <noreply@github.com>2025-09-28 17:01:48 +0200
commit2117a9b0f4b84f5bd6c99566bcf5849a64104467 (patch)
tree590b20056c62d04a496aa22f6bee0034a1430e4e /dev/kernel
parent0f88e96c0cf7ffaccecae94794024164c510f735 (diff)
parentb1302067703566624390d780dffd621bb99bb439 (diff)
Merge pull request #64 from nekernel-org/dev
ver: `kernel` v0.0.6
Diffstat (limited to 'dev/kernel')
-rw-r--r--dev/kernel/FSKit/Ext2+IFS.h271
-rw-r--r--dev/kernel/FSKit/Ext2.h39
-rw-r--r--dev/kernel/HALKit/AMD64/CxxAbi.cc15
-rw-r--r--dev/kernel/HALKit/AMD64/HalApplicationProcessor.cc13
-rw-r--r--dev/kernel/HALKit/AMD64/HalDebugOutput.cc2
-rw-r--r--dev/kernel/HALKit/AMD64/HalKernelMain.cc39
-rw-r--r--dev/kernel/HALKit/ARM64/HalHandoverStub.s8
-rw-r--r--dev/kernel/HALKit/ARM64/HalKernelMain.cc22
-rw-r--r--dev/kernel/KernelKit/FileMgr.h48
-rw-r--r--dev/kernel/NeKit/CxxAbi.h8
-rw-r--r--dev/kernel/NeKit/ErrorOr.h2
-rw-r--r--dev/kernel/NeKit/Ref.h2
-rw-r--r--dev/kernel/amd64-desktop.make2
-rw-r--r--dev/kernel/src/ACPIFactoryInterface.cc2
-rw-r--r--dev/kernel/src/AsciiUtils.cc15
-rw-r--r--dev/kernel/src/FS/Ext2+FileMgr.cc1551
-rw-r--r--dev/kernel/src/FS/Ext2+FileSystemParser.cc2
-rw-r--r--dev/kernel/src/UserProcessScheduler.cc2
-rw-r--r--dev/kernel/src/UtfUtils.cc2
19 files changed, 1949 insertions, 96 deletions
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);