diff options
| author | Amlal El Mahrouss <amlal@nekernel.org> | 2025-09-28 14:58:47 +0200 |
|---|---|---|
| committer | Amlal El Mahrouss <amlal@nekernel.org> | 2025-09-28 14:58:47 +0200 |
| commit | 84a7325b22f1f90c0c719a2ec8ba131263e1208c (patch) | |
| tree | a7b706349a1fcefc2490acb624a023d010c03f7d /dev | |
| parent | 49047f8f1d799966d9204118351c25a6379b2e5b (diff) | |
feat: final changes before nekernel v0.0.6
Signed-off-by: Amlal El Mahrouss <amlal@nekernel.org>
Diffstat (limited to 'dev')
| -rw-r--r-- | dev/kernel/FSKit/Ext2+IFS.h | 155 | ||||
| -rw-r--r-- | dev/kernel/FSKit/Ext2.h | 17 | ||||
| -rw-r--r-- | dev/kernel/HALKit/AMD64/HalDebugOutput.cc | 2 | ||||
| -rw-r--r-- | dev/kernel/KernelKit/FileMgr.h | 48 | ||||
| -rw-r--r-- | dev/kernel/NeKit/ErrorOr.h | 4 | ||||
| -rw-r--r-- | dev/kernel/NeKit/Ref.h | 2 | ||||
| -rw-r--r-- | dev/kernel/amd64-desktop.make | 2 | ||||
| -rw-r--r-- | dev/kernel/src/FS/Ext2+FileMgr.cc | 2599 |
8 files changed, 1513 insertions, 1316 deletions
diff --git a/dev/kernel/FSKit/Ext2+IFS.h b/dev/kernel/FSKit/Ext2+IFS.h index d73ae43c..2a28fe17 100644 --- a/dev/kernel/FSKit/Ext2+IFS.h +++ b/dev/kernel/FSKit/Ext2+IFS.h @@ -8,11 +8,12 @@ #include <NeKit/KernelPanic.h> #include <NeKit/Utils.h> -namespace Kernel::Ext2 { +namespace Kernel { /// @brief Context for an EXT2 filesystem on a given drive -struct Ext2Context { - Kernel::DriveTrait* drive{nullptr}; - EXT2_SUPER_BLOCK* superblock{nullptr}; +class Ext2Context final { + public: + DriveTrait* drive{nullptr}; + EXT2_SUPER_BLOCK* superblock{nullptr}; /// @brief context with a drive Ext2Context(Kernel::DriveTrait* drv) : drive(drv) {} @@ -46,19 +47,19 @@ struct Ext2Context { return *this; } - Kernel::SizeT LeakBlockSize() const { + SizeT BlockSize() const { if (!superblock) return kExt2FSBlockSizeBase; return kExt2FSBlockSizeBase << superblock->fLogBlockSize; } - BOOL operator bool() { return superblock != nullptr; } + operator BOOL() { return superblock != nullptr; } }; /// ======================================================================= /// /// IFS FUNCTIONS /// ======================================================================= /// -inline bool ext2_read_block(Kernel::DriveTrait* drv, Kernel::UInt32 lba, void* buffer, +inline BOOL ext2_read_block(Kernel::DriveTrait* drv, Kernel::UInt32 lba, VoidPtr buffer, Kernel::UInt32 size) { if (!drv || !buffer) return false; @@ -71,12 +72,12 @@ inline bool ext2_read_block(Kernel::DriveTrait* drv, Kernel::UInt32 lba, void* b return pkt.fPacketGood; } -inline bool ext2_write_block(Kernel::DriveTrait* drv, Kernel::UInt32 lba, const void* buffer, +inline BOOL ext2_write_block(Kernel::DriveTrait* drv, Kernel::UInt32 lba, const VoidPtr buffer, Kernel::UInt32 size) { if (!drv || !buffer) return false; Kernel::DriveTrait::DrivePacket pkt{}; - pkt.fPacketContent = const_cast<void*>(buffer); + pkt.fPacketContent = const_cast<VoidPtr>(buffer); pkt.fPacketSize = size; pkt.fPacketLba = lba; drv->fOutput(pkt); @@ -119,7 +120,6 @@ inline Kernel::ErrorOr<Ext2Node*> ext2_load_inode(Ext2Context* ctx, Kernel::UInt // Compute block group and index within group Kernel::UInt32 inodesPerGroup = ctx->superblock->fInodesPerGroup; Kernel::UInt32 group = (inodeNumber - 1) / inodesPerGroup; - Kernel::UInt32 index = (inodeNumber - 1) % inodesPerGroup; // dummy: just offset first inode Kernel::UInt32 inodeTableBlock = ctx->superblock->fFirstInode + group; @@ -133,8 +133,133 @@ inline Kernel::ErrorOr<Ext2Node*> ext2_load_inode(Ext2Context* ctx, Kernel::UInt return Kernel::ErrorOr<Ext2Node*>(ext2Node); } -inline Kernel::UInt32 inode_offset(const Ext2Context* ctx, Kernel::UInt32 inodeNumber) { - if (!ctx || !ctx->superblock) return 0; - return ((inodeNumber - 1) % ctx->superblock->fInodesPerGroup) * ctx->superblock->fInodeSize; -} -} // namespace Kernel::Ext2 +/* + * Ext2FileSystemParser Class + * + * Provides high-level interface for EXT2 filesystem operations + */ +class Ext2FileSystemParser { + private: + Ext2Context ctx; // Internal EXT2 context + + public: + /* + * Constructor + * Initializes the parser with a drive interface + * + * @param drive: Pointer to drive trait for disk I/O operations + */ + explicit Ext2FileSystemParser(DriveTrait* drive); + + /* + * Open a file or directory by path + * + * @param path: Full path to the file/directory (e.g., "/home/user/file.txt") + * @param restrict_type: Access mode restriction (e.g., "r", "w", "rw") + * @return: VoidPtr handle to the opened file/directory, or nullptr on failure + */ + VoidPtr Open(const char* path, const char* restrict_type); + + /* + * Read data from an open file node + * + * @param node: File node handle returned by Open() + * @param flags: Read operation flags + * @param size: Number of bytes to read + * @return: Pointer to allocated buffer containing read data, or nullptr on failure + * Caller is responsible for freeing the returned buffer + */ + VoidPtr Read(VoidPtr node, Int32 flags, SizeT size); + + /* + * Write data to an open file node + * + * @param node: File node handle returned by Open() + * @param data: Buffer containing data to write + * @param flags: Write operation flags + * @param size: Number of bytes to write + */ + Void Write(VoidPtr node, VoidPtr data, Int32 flags, SizeT size); + + /* + * Seek to a specific position in the file + * + * @param node: File node handle + * @param offset: Byte offset from beginning of file + * @return: true on success, false on failure + */ + BOOL Seek(VoidPtr node, SizeT offset); + + /* + * Get current position in the file + * + * @param node: File node handle + * @return: Current byte offset from beginning of file + */ + SizeT Tell(VoidPtr node); + + /* + * Reset file position to beginning + * + * @param node: File node handle + * @return: true on success, false on failure + */ + BOOL Rewind(VoidPtr node); + + /* + * Read data from a named file within a directory node + * + * @param name: Name of file within the directory + * @param node: Directory node handle + * @param flags: Read operation flags + * @param size: Number of bytes to read + * @return: Pointer to allocated buffer containing read data, or nullptr on failure + */ + VoidPtr Read(const char* name, VoidPtr node, Int32 flags, SizeT size); + + /* + * Write data to a named file within a directory node + * + * @param name: Name of file within the directory + * @param node: Directory node handle + * @param data: Buffer containing data to write + * @param flags: Write operation flags + * @param size: Number of bytes to write + */ + Void Write(const char* name, VoidPtr node, VoidPtr data, Int32 flags, SizeT size); + + /* + * Create a new regular file + * + * @param path: Full path for the new file + * @return: VoidPtr handle to the created file, or nullptr on failure + */ + VoidPtr Create(const char* path); + + /* + * Create a new directory + * + * @param path: Full path for the new directory + * @return: VoidPtr handle to the created directory, or nullptr on failure + */ + VoidPtr CreateDirectory(const char* path); + + /* + * Close and free a file/directory node + * Note: This method is not shown in the implementation but would typically be needed + * + * @param node: Node handle to close and free + */ + Void Close(VoidPtr node); + + /* + * Get file/directory information + * Note: This method is not shown in the implementation but would typically be needed + * + * @param node: Node handle + * @param info: Structure to fill with file information (size, type, permissions, etc.) + * @return: true on success, false on failure + */ + BOOL GetInfo(VoidPtr node, VoidPtr info); +}; +} // namespace Kernel diff --git a/dev/kernel/FSKit/Ext2.h b/dev/kernel/FSKit/Ext2.h index 06370a8c..be2e34a6 100644 --- a/dev/kernel/FSKit/Ext2.h +++ b/dev/kernel/FSKit/Ext2.h @@ -15,8 +15,6 @@ /// @file Ext2.h /// @brief EXT2 filesystem structures, constants, and base wrappers. -namespace Ext2 { - /// EXT2 Constants #define kExt2FSMagic (0xEF53) #define kExt2FSMaxFileNameLen (255U) @@ -40,6 +38,17 @@ enum { kExt2FileTypeSymbolicLink = 7 }; +typedef struct EXT2_GROUP_DESCRIPTOR final { + UInt32 fBlockBitmap; + UInt32 fInodeBitmap; + UInt32 fInodeTable; + UInt16 fFreeBlocksCount; + UInt16 fFreeInodesCount; + UInt16 fBgUsedDirsCount; + UInt16 fBgPad; + UInt32 fBgReserved[3]; +} EXT2_GROUP_DESCRIPTOR; + struct PACKED EXT2_SUPER_BLOCK final { Kernel::UInt32 fInodeCount; Kernel::UInt32 fBlockCount; @@ -137,7 +146,3 @@ struct Ext2Node { EXT2_INODE inode; Kernel::UInt32 cursor{0}; }; - -class Ext2FileSystemMgr; - -} // namespace Ext2 diff --git a/dev/kernel/HALKit/AMD64/HalDebugOutput.cc b/dev/kernel/HALKit/AMD64/HalDebugOutput.cc index 1e9fbab3..789c1067 100644 --- a/dev/kernel/HALKit/AMD64/HalDebugOutput.cc +++ b/dev/kernel/HALKit/AMD64/HalDebugOutput.cc @@ -57,7 +57,9 @@ namespace Detail { TerminalDevice::~TerminalDevice() = default; +#ifdef __DEBUG__ STATIC SizeT kX = kFontSizeX, kY = kFontSizeY; +#endif // __DEBUG__ EXTERN_C void ke_utf_io_write(DeviceInterface<const Utf8Char*>* obj, const Utf8Char* bytes) { NE_UNUSED(bytes); diff --git a/dev/kernel/KernelKit/FileMgr.h b/dev/kernel/KernelKit/FileMgr.h index f925a96c..bad6cf85 100644 --- a/dev/kernel/KernelKit/FileMgr.h +++ b/dev/kernel/KernelKit/FileMgr.h @@ -27,7 +27,7 @@ /// @author Amlal El Mahrouss (amlal@nekernel.org) //! Include filesystems that NeKernel supports. -#include <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/ErrorOr.h b/dev/kernel/NeKit/ErrorOr.h index 52f0d6a9..e8b3b6fc 100644 --- a/dev/kernel/NeKit/ErrorOr.h +++ b/dev/kernel/NeKit/ErrorOr.h @@ -38,8 +38,8 @@ class ErrorOr final { return *this; } - T Value() const { return *mRef; } - + const T& Value() { return mRef.TryLeak(); } + Ref<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/FS/Ext2+FileMgr.cc b/dev/kernel/src/FS/Ext2+FileMgr.cc index d5d23c3f..c205fb10 100644 --- a/dev/kernel/src/FS/Ext2+FileMgr.cc +++ b/dev/kernel/src/FS/Ext2+FileMgr.cc @@ -1,1540 +1,1559 @@ #ifndef __NE_MINIMAL_OS__ #ifdef __FSKIT_INCLUDES_EXT2__ -#include <FSKit/Ext2.h> -#include <FSKit/Ext2IFS.h> -#include <KernelKit/FileMgr.h> +#include <FSKit/Ext2+IFS.h> +#include <FSKit/Ext2.h> #include <KernelKit/DebugOutput.h> +#include <KernelKit/FileMgr.h> #include <KernelKit/HeapMgr.h> -#include <NeKit/Utils.h> -#include <NeKit/KString.h> #include <NeKit/ErrorOr.h> +#include <NeKit/KString.h> #include <NeKit/KernelPanic.h> +#include <NeKit/Utils.h> -namespace Ext2 { - -constexpr UInt32 EXT2_DIRECT_BLOCKS = 12; -constexpr UInt32 EXT2_SINGLE_INDIRECT_INDEX = 12; -constexpr UInt32 EXT2_DOUBLE_INDIRECT_INDEX = 13; -constexpr UInt32 EXT2_TRIPLE_INDIRECT_INDEX = 14; -constexpr UInt32 EXT2_ROOT_INODE = 2; -constexpr UInt32 EXT2_SUPERBLOCK_BLOCK = 1; +constexpr UInt32 EXT2_DIRECT_BLOCKS = 12; +constexpr UInt32 EXT2_SINGLE_INDIRECT_INDEX = 12; +constexpr UInt32 EXT2_DOUBLE_INDIRECT_INDEX = 13; +constexpr UInt32 EXT2_TRIPLE_INDIRECT_INDEX = 14; +constexpr UInt32 EXT2_ROOT_INODE = 2; +constexpr UInt32 EXT2_SUPERBLOCK_BLOCK = 1; constexpr UInt32 EXT2_GROUP_DESC_BLOCK_SMALL = 2; constexpr UInt32 EXT2_GROUP_DESC_BLOCK_LARGE = 1; -static inline SizeT ext2_min(SizeT a, SizeT b) { return a < b ? a : b; } +static inline SizeT ext2_min(SizeT a, SizeT b) { + return a < b ? a : b; +} struct Ext2GroupInfo { - EXT2_GROUP_DESCRIPTOR* groupDesc; - UInt32 groupDescriptorBlock; - UInt32 offsetInGroupDescBlock; - UInt8* blockBuffer; + EXT2_GROUP_DESCRIPTOR* groupDesc; + UInt32 groupDescriptorBlock; + UInt32 offsetInGroupDescBlock; + UInt8* blockBuffer; }; // Convert EXT2 block number -> LBA (sector index) for Drive I/O. -static inline Kernel::UInt32 ext2_block_to_lba(Context* ctx, Kernel::UInt32 blockNumber) { - if (!ctx || !ctx->drive) return 0; - Kernel::UInt32 blockSize = ctx->BlockSize(); - Kernel::UInt32 sectorSize = ctx->drive->fSectorSz; - Kernel::UInt32 sectorsPerBlock = blockSize / sectorSize; - return blockNumber * sectorsPerBlock; +static inline Kernel::UInt32 ext2_block_to_lba(Kernel::Ext2Context* ctx, + Kernel::UInt32 blockNumber) { + if (!ctx || !ctx->drive) return 0; + Kernel::UInt32 blockSize = ctx->BlockSize(); + Kernel::UInt32 sectorSize = ctx->drive->fSectorSz; + Kernel::UInt32 sectorsPerBlock = blockSize / sectorSize; + return blockNumber * sectorsPerBlock; } // Read a block and return a pointer to its content -static ErrorOr<Kernel::UInt32*> ext2_read_block_ptr(Context* ctx, Kernel::UInt32 blockNumber) { - if (!ctx || !ctx->drive || !ctx->superblock) - return ErrorOr<Kernel::UInt32*>(Kernel::kErrorInvalidData); - - Kernel::UInt32 blockSize = ctx->BlockSize(); - auto buf = (Kernel::UInt32*)mm_alloc_ptr(blockSize, true, false); - if (!buf) return ErrorOr<Kernel::UInt32*>(Kernel::kErrorHeapOutOfMemory); - - Kernel::UInt32 lba = ext2_block_to_lba(ctx, blockNumber); - if (!ext2_read_block(ctx->drive, lba, buf, blockSize)) { - mm_free_ptr(buf); - return ErrorOr<Kernel::UInt32*>(Kernel::kErrorDisk); - } - return ErrorOr<Kernel::UInt32*>(buf); +static ErrorOr<Kernel::UInt32*> ext2_read_block_ptr(Kernel::Ext2Context* ctx, + Kernel::UInt32 blockNumber) { + if (!ctx || !ctx->drive || !ctx->superblock) + return ErrorOr<Kernel::UInt32*>(Kernel::kErrorInvalidData); + + Kernel::UInt32 blockSize = ctx->BlockSize(); + auto buf = (Kernel::UInt32*) mm_alloc_ptr(blockSize, true, false); + if (!buf) return ErrorOr<Kernel::UInt32*>(Kernel::kErrorHeapOutOfMemory); + + Kernel::UInt32 lba = ext2_block_to_lba(ctx, blockNumber); + if (!ext2_read_block(ctx->drive, lba, buf, blockSize)) { + mm_free_ptr(buf); + return ErrorOr<Kernel::UInt32*>(Kernel::kErrorDisk); + } + return ErrorOr<Kernel::UInt32*>(buf); } // Get the block address for a given logical block index -static ErrorOr<Kernel::UInt32> ext2_get_block_address(Context* ctx, Ext2Node* node, Kernel::UInt32 logicalIndex) { - if (!ctx || !node || !ctx->drive) - return ErrorOr<Kernel::UInt32>(Kernel::kErrorInvalidData); - - Kernel::UInt32 blockSize = ctx->BlockSize(); - Kernel::UInt32 pointersPerBlock = blockSize / sizeof(Kernel::UInt32); - - // Direct blocks - if (logicalIndex < EXT2_DIRECT_BLOCKS) { - Kernel::UInt32 bn = node->inode.fBlock[logicalIndex]; - if (bn == 0) return ErrorOr<Kernel::UInt32>(Kernel::kErrorInvalidData); - return ErrorOr<Kernel::UInt32>(bn); - } +static ErrorOr<Kernel::UInt32> ext2_get_block_address(Kernel::Ext2Context* ctx, Ext2Node* node, + Kernel::UInt32 logicalIndex) { + if (!ctx || !node || !ctx->drive) return ErrorOr<Kernel::UInt32>(Kernel::kErrorInvalidData); - // Single indirect blocks - if (logicalIndex < (EXT2_DIRECT_BLOCKS + pointersPerBlock)) { - Kernel::UInt32 iblock = node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX]; - if (iblock == 0) return ErrorOr<Kernel::UInt32>(Kernel::kErrorInvalidData); - - auto res = ext2_read_block_ptr(ctx, iblock); - if (!res) return ErrorOr<Kernel::UInt32>(res.Error()); - - // Using dereference operator - Kernel::UInt32* ptr = *res.Leak(); // operator* returns T (UInt32*) - - Kernel::UInt32 val = ptr[logicalIndex - EXT2_DIRECT_BLOCKS]; - mm_free_ptr(ptr); - - if (val == 0) return ErrorOr<Kernel::UInt32>(Kernel::kErrorInvalidData); - return ErrorOr<Kernel::UInt32>(val); - } + Kernel::UInt32 blockSize = ctx->BlockSize(); + Kernel::UInt32 pointersPerBlock = blockSize / sizeof(Kernel::UInt32); - // Double indirect blocks - Kernel::UInt32 doubleStart = EXT2_DIRECT_BLOCKS + pointersPerBlock; - Kernel::UInt32 doubleSpan = pointersPerBlock * pointersPerBlock; - if (logicalIndex < (doubleStart + doubleSpan)) { - Kernel::UInt32 db = node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX]; - if (db == 0) return ErrorOr<Kernel::UInt32>(Kernel::kErrorInvalidData); - - auto dblRes = ext2_read_block_ptr(ctx, db); - if (!dblRes) return ErrorOr<Kernel::UInt32>(dblRes.Error()); - - Kernel::UInt32* dblPtr = *dblRes.Leak(); - - Kernel::UInt32 idxWithin = logicalIndex - doubleStart; - Kernel::UInt32 firstIdx = idxWithin / pointersPerBlock; - Kernel::UInt32 secondIdx = idxWithin % pointersPerBlock; - Kernel::UInt32 singleBlockNum = dblPtr[firstIdx]; - - mm_free_ptr(dblPtr); - if (singleBlockNum == 0) return ErrorOr<Kernel::UInt32>(Kernel::kErrorInvalidData); - - auto singleRes = ext2_read_block_ptr(ctx, singleBlockNum); - if (!singleRes) return ErrorOr<Kernel::UInt32>(singleRes.Error()); - - Kernel::UInt32* singlePtr = *singleRes.Leak(); - Kernel::UInt32 val = singlePtr[secondIdx]; - mm_free_ptr(singlePtr); - - if (val == 0) return ErrorOr<Kernel::UInt32>(Kernel::kErrorInvalidData); - return ErrorOr<Kernel::UInt32>(val); - } + // Direct blocks + if (logicalIndex < EXT2_DIRECT_BLOCKS) { + Kernel::UInt32 bn = node->inode.fBlock[logicalIndex]; + if (bn == 0) return ErrorOr<Kernel::UInt32>(Kernel::kErrorInvalidData); + return ErrorOr<Kernel::UInt32>(bn); + } - return ErrorOr<Kernel::UInt32>(Kernel::kErrorUnimplemented); -} + // Single indirect blocks + if (logicalIndex < (EXT2_DIRECT_BLOCKS + pointersPerBlock)) { + Kernel::UInt32 iblock = node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX]; + if (iblock == 0) return ErrorOr<Kernel::UInt32>(Kernel::kErrorInvalidData); -static Kernel::ErrorOr<voidPtr> ext2_read_inode_data(Context* ctx, Ext2Node* node, SizeT size) { - using Kernel::ErrorOr; - using Kernel::UInt8; - using Kernel::UInt32; + auto res = ext2_read_block_ptr(ctx, iblock); + if (!res) return ErrorOr<Kernel::UInt32>(res.Error()); - if (!ctx || !ctx->drive || !node || size == 0) - return ErrorOr<voidPtr>(1); + // Using dereference operator + Kernel::UInt32* ptr = *res.Leak(); // operator* returns T (UInt32*) - auto blockSize = ctx->BlockSize(); - SizeT available = (node->inode.fSize > node->cursor) ? (node->inode.fSize - node->cursor) : 0; - SizeT bytesToRead = (size < available) ? size : available; - if (bytesToRead == 0) - return ErrorOr<voidPtr>(2); // nothing to read + Kernel::UInt32 val = ptr[logicalIndex - EXT2_DIRECT_BLOCKS]; + mm_free_ptr(ptr); - auto buffer = mm_alloc_ptr(bytesToRead, true, false); - if (!buffer) - return ErrorOr<voidPtr>(3); // allocation failed + if (val == 0) return ErrorOr<Kernel::UInt32>(Kernel::kErrorInvalidData); + return ErrorOr<Kernel::UInt32>(val); + } - UInt32 currentOffset = node->cursor; - SizeT remaining = bytesToRead; - UInt8* dest = reinterpret_cast<UInt8*>(buffer); + // Double indirect blocks + Kernel::UInt32 doubleStart = EXT2_DIRECT_BLOCKS + pointersPerBlock; + Kernel::UInt32 doubleSpan = pointersPerBlock * pointersPerBlock; + if (logicalIndex < (doubleStart + doubleSpan)) { + Kernel::UInt32 db = node->inode.fBlock[EXT2_DOUBLE_INDIRECT_INDEX]; + if (db == 0) return ErrorOr<Kernel::UInt32>(Kernel::kErrorInvalidData); - while (remaining > 0) { - UInt32 logicalIndex = currentOffset / blockSize; - UInt32 offsetInBlock = currentOffset % blockSize; + auto dblRes = ext2_read_block_ptr(ctx, db); + if (!dblRes) return ErrorOr<Kernel::UInt32>(dblRes.Error()); - auto phys = ext2_get_block_address(ctx, node, logicalIndex); - if (phys.HasError()) { - mm_free_ptr(buffer); - return ErrorOr<voidPtr>(phys.Error()); - } + Kernel::UInt32* dblPtr = *dblRes.Leak(); - UInt32 blockNumber = phys.Value(); - UInt32 lba = ext2_block_to_lba(ctx, blockNumber); - - auto blockBuf = mm_alloc_ptr(blockSize, true, false); - if (!blockBuf) { - mm_free_ptr(buffer); - return ErrorOr<voidPtr>(4); // block buffer allocation failed - } + Kernel::UInt32 idxWithin = logicalIndex - doubleStart; + Kernel::UInt32 firstIdx = idxWithin / pointersPerBlock; + Kernel::UInt32 secondIdx = idxWithin % pointersPerBlock; + Kernel::UInt32 singleBlockNum = dblPtr[firstIdx]; - if (!ext2_read_block(ctx->drive, lba, blockBuf, blockSize)) { - mm_free_ptr(blockBuf); - mm_free_ptr(buffer); - return ErrorOr<voidPtr>(5); // block read failed - } + mm_free_ptr(dblPtr); + if (singleBlockNum == 0) return ErrorOr<Kernel::UInt32>(Kernel::kErrorInvalidData); - 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); + auto singleRes = ext2_read_block_ptr(ctx, singleBlockNum); + if (!singleRes) return ErrorOr<Kernel::UInt32>(singleRes.Error()); - mm_free_ptr(blockBuf); + Kernel::UInt32* singlePtr = *singleRes.Leak(); + Kernel::UInt32 val = singlePtr[secondIdx]; + mm_free_ptr(singlePtr); - currentOffset += static_cast<UInt32>(chunk); - dest += chunk; - remaining -= chunk; - } + if (val == 0) return ErrorOr<Kernel::UInt32>(Kernel::kErrorInvalidData); + return ErrorOr<Kernel::UInt32>(val); + } - node->cursor += static_cast<UInt32>(bytesToRead); - return ErrorOr<voidPtr>(buffer); + return ErrorOr<Kernel::UInt32>(Kernel::kErrorUnimplemented); } +static Kernel::ErrorOr<voidPtr> ext2_read_inode_data(Kernel::Ext2Context* ctx, Ext2Node* node, + SizeT size) { + using Kernel::ErrorOr; + using Kernel::UInt32; + using Kernel::UInt8; -// Get group descriptor information for a given block/inode number -static ErrorOr<Ext2GroupInfo*> ext2_get_group_descriptor_info(Context* 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; - } - } + if (!ctx || !ctx->drive || !node || size == 0) return ErrorOr<voidPtr>(1); - // Calculate number of block groups - UInt32 groupsCount = static_cast<UInt32>((totalBlocks + blocksPerGroup - 1) / blocksPerGroup); - if (groupIndex >= groupsCount) - return ErrorOr<Ext2GroupInfo*>(kErrorInvalidData); + auto blockSize = ctx->BlockSize(); + SizeT available = (node->inode.fSize > node->cursor) ? (node->inode.fSize - node->cursor) : 0; + SizeT bytesToRead = (size < available) ? size : available; + if (bytesToRead == 0) return ErrorOr<voidPtr>(2); // nothing to read - // Determine GDT start block - UInt32 gdtStartBlock = (blockSize == 1024) ? EXT2_GROUP_DESC_BLOCK_SMALL : EXT2_GROUP_DESC_BLOCK_LARGE; + auto buffer = mm_alloc_ptr(bytesToRead, true, false); + if (!buffer) return ErrorOr<voidPtr>(3); // allocation failed - // Compute byte offset of descriptor within the GDT - const UInt32 descSize = sizeof(EXT2_GROUP_DESCRIPTOR); - UInt64 descByteOffset = static_cast<UInt64>(groupIndex) * descSize; + UInt32 currentOffset = node->cursor; + SizeT remaining = bytesToRead; + UInt8* dest = reinterpret_cast<UInt8*>(buffer); - // Which EXT2 block contains that descriptor? - UInt32 blockOffsetWithinGdt = static_cast<UInt32>(descByteOffset / blockSize); - UInt32 offsetInGroupDescBlock = static_cast<UInt32>(descByteOffset % blockSize); - UInt32 groupDescriptorBlock = gdtStartBlock + blockOffsetWithinGdt; + while (remaining > 0) { + UInt32 logicalIndex = currentOffset / blockSize; + UInt32 offsetInBlock = currentOffset % blockSize; + + auto phys = ext2_get_block_address(ctx, node, logicalIndex); + if (phys.HasError()) { + mm_free_ptr(buffer); + return ErrorOr<voidPtr>(phys.Error()); + } - // Allocate buffer and read the block containing the descriptor - auto blockBuffer = mm_alloc_ptr(blockSize, true, false); - if (!blockBuffer) return ErrorOr<Ext2GroupInfo*>(kErrorHeapOutOfMemory); + auto blockNumber = phys.Value(); + UInt32 lba = ext2_block_to_lba(ctx, blockNumber); - UInt32 groupDescriptorLba = ext2_block_to_lba(ctx, groupDescriptorBlock); - if (!ext2_read_block(ctx->drive, groupDescriptorLba, blockBuffer, blockSize)) { - mm_free_ptr(blockBuffer); - return ErrorOr<Ext2GroupInfo*>(kErrorDisk); + auto blockBuf = mm_alloc_ptr(blockSize, true, false); + if (!blockBuf) { + mm_free_ptr(buffer); + return ErrorOr<voidPtr>(4); // block buffer allocation failed } - auto groupInfo = (Ext2GroupInfo*)mm_alloc_ptr(sizeof(Ext2GroupInfo), true, false); - if (!groupInfo) { - mm_free_ptr(blockBuffer); - return ErrorOr<Ext2GroupInfo*>(kErrorHeapOutOfMemory); + if (!ext2_read_block(ctx->drive, lba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + mm_free_ptr(buffer); + return ErrorOr<voidPtr>(5); // block read failed } - groupInfo->groupDesc = reinterpret_cast<EXT2_GROUP_DESCRIPTOR*>( - reinterpret_cast<UInt8*>(blockBuffer) + offsetInGroupDescBlock); - groupInfo->groupDescriptorBlock = groupDescriptorBlock; - groupInfo->offsetInGroupDescBlock = offsetInGroupDescBlock; - groupInfo->blockBuffer = reinterpret_cast<UInt8*>(blockBuffer); + 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; + } - return ErrorOr<Ext2GroupInfo*>(groupInfo); + node->cursor += static_cast<UInt32>(bytesToRead); + return ErrorOr<voidPtr>(buffer); } -// Allocate a new block -inline ErrorOr<UInt32> ext2_alloc_block(Context* ctx, EXT2_GROUP_DESCRIPTOR* groupDesc) { - if (!ctx || !ctx->superblock || !groupDesc) - return ErrorOr<UInt32>(kErrorInvalidData); +// Get group descriptor information for a given block/inode number +static ErrorOr<Ext2GroupInfo*> ext2_get_group_descriptor_info(Kernel::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); +} - UInt32 blockSize = ctx->BlockSize(); +// Allocate a new block +inline ErrorOr<UInt32> ext2_alloc_block(Kernel::Ext2Context* ctx, + EXT2_GROUP_DESCRIPTOR* groupDesc) { + if (!ctx || !ctx->superblock || !groupDesc) return ErrorOr<UInt32>(kErrorInvalidData); - // for the bitmap - auto bitmap = mm_alloc_ptr(blockSize, true, false); - if (!bitmap) return ErrorOr<UInt32>(kErrorHeapOutOfMemory); + UInt32 blockSize = ctx->BlockSize(); - // Read block bitmap - if (!ext2_read_block(ctx->drive, groupDesc->fBlockBitmap, bitmap, blockSize)) { - mm_free_ptr(bitmap); - return ErrorOr<UInt32>(kErrorDisk); - } + // for the bitmap + auto bitmap = mm_alloc_ptr(blockSize, true, false); + if (!bitmap) return ErrorOr<UInt32>(kErrorHeapOutOfMemory); - // 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); - } - } + // 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); + mm_free_ptr(bitmap); + return ErrorOr<UInt32>(kErrorDiskIsFull); } // Indirect blocks -static Kernel::ErrorOr<Kernel::Void> ext2_set_block_address(Context* ctx, Ext2Node* node, - Kernel::UInt32 logicalBlockIndex, - Kernel::UInt32 physicalBlockNumber) { - using namespace Kernel; +static Kernel::ErrorOr<Kernel::Void*> ext2_set_block_address(Kernel::Ext2Context* ctx, + Ext2Node* node, + Kernel::UInt32 logicalBlockIndex, + Kernel::UInt32 physicalBlockNumber) { + using namespace Kernel; + + if (!ctx || !ctx->drive || !node) return ErrorOr<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()); + } - if (!ctx || !ctx->drive || !node) - return ErrorOr<Void>(kErrorInvalidData); + node->inode.fBlock[EXT2_SINGLE_INDIRECT_INDEX] = newBlockRes.Leak(); - auto blockSize = ctx->BlockSize(); - UInt32 blocksPerPointerBlock = blockSize / sizeof(UInt32); + UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr<Void*>(kErrorDisk); + } + + mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); - // Direct blocks - if (logicalBlockIndex < EXT2_DIRECT_BLOCKS) { - node->inode.fBlock[logicalBlockIndex] = physicalBlockNumber; - return ErrorOr<Void>(nullptr); + // 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); } - // 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()); - // 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* 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); + } - 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()); - mm_free_ptr(indirectPtr); - return ErrorOr<Void>(nullptr); + 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); } - // 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; - // 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); - } - } + 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()); + } - // Write to single-indirect block - auto singleRes = ext2_read_block_ptr(ctx, singleIndirectBlock); - if (singleRes.HasError()) return ErrorOr<Void>(singleRes.Error()); + auto groupInfo = groupInfoRes.Leak().Leak(); + auto newBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); + if (newBlockRes.HasError()) { + mm_free_ptr(doublePtr); + mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr<Void*>(newBlockRes.Error()); + } - UInt32* singlePtr = singleRes.Leak().Leak(); - singlePtr[secondIdx] = physicalBlockNumber; + singleIndirectBlock = newBlockRes.Leak(); + doublePtr[firstIdx] = singleIndirectBlock; - UInt32 singleLba = ext2_block_to_lba(ctx, singleIndirectBlock); - if (!ext2_write_block(ctx->drive, singleLba, singlePtr, blockSize)) { - mm_free_ptr(singlePtr); - return ErrorOr<Void>(kErrorDisk); - } + // Write back GDT + UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + mm_free_ptr(doublePtr); + mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return ErrorOr<Void*>(kErrorDisk); + } - mm_free_ptr(singlePtr); - return ErrorOr<Void>(nullptr); - } + mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); - // Triple indirect blocks not implemented - return ErrorOr<Void>(kErrorUnimplemented); -} + // Zero single-indirect block + auto zeroBuf = mm_alloc_ptr(blockSize, true, false); + if (!zeroBuf) { + mm_free_ptr(doublePtr); + return ErrorOr<Void*>(kErrorHeapOutOfMemory); + } -// Find a directory entry by name within a directory inode -static ErrorOr<EXT2_DIR_ENTRY*> ext2_find_dir_entry(Context* ctx, Ext2Node* dirNode, const char* name) { - if (!ctx || !ctx->drive || !dirNode || !name) - return ErrorOr<EXT2_DIR_ENTRY*>(Kernel::kErrorInvalidData); - - // Check directory type - auto type = (dirNode->inode.fMode >> 12) & 0xF; - if (type != kExt2FileTypeDirectory) - return ErrorOr<EXT2_DIR_ENTRY*>(Kernel::kErrorInvalidData); - - Kernel::UInt32 blockSize = ctx->BlockSize(); - auto blockBuf = mm_alloc_ptr(blockSize, true, false); - if (!blockBuf) return ErrorOr<EXT2_DIR_ENTRY*>(Kernel::kErrorHeapOutOfMemory); - - SizeT nameLen = rt_string_len(name); - for (int i = 0; i < EXT2_DIRECT_BLOCKS; ++i) { - Kernel::UInt32 blockNum = dirNode->inode.fBlock[i]; - if (blockNum == 0) continue; - - Kernel::UInt32 lba = ext2_block_to_lba(ctx, blockNum); - if (!ext2_read_block(ctx->drive, lba, blockBuf, blockSize)) { - mm_free_ptr(blockBuf); - return ErrorOr<EXT2_DIR_ENTRY*>(Kernel::kErrorDisk); - } + rt_zero_memory(zeroBuf, blockSize); + UInt32 singleLba = ext2_block_to_lba(ctx, singleIndirectBlock); + if (!ext2_write_block(ctx->drive, singleLba, zeroBuf, blockSize)) { + mm_free_ptr(zeroBuf); + mm_free_ptr(doublePtr); + return ErrorOr<Void*>(kErrorDisk); + } - Kernel::UInt32 offset = 0; - while (offset + sizeof(Kernel::UInt32) + sizeof(Kernel::UInt16) <= blockSize) { - auto onDiskEntry = reinterpret_cast<EXT2_DIR_ENTRY*>((Kernel::UInt8*)blockBuf + offset); - if (onDiskEntry->fRecordLength == 0) break; // corrupted - - if (onDiskEntry->fInode != 0 && onDiskEntry->fNameLength == nameLen) { - // Compare names - if (rt_string_cmp(name, onDiskEntry->fName, nameLen) == 0) { - // Allocate a result sized to hold the name + metadata - SizeT recSize = sizeof(EXT2_DIR_ENTRY); - auto found = (EXT2_DIR_ENTRY*)mm_alloc_ptr(recSize, true, false); - if (!found) { - mm_free_ptr(blockBuf); - return ErrorOr<EXT2_DIR_ENTRY*>(Kernel::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(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(blockBuf); - return ErrorOr<EXT2_DIR_ENTRY*>(Kernel::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 -} + mm_free_ptr(doublePtr); -static ErrorOr<Kernel::Void> ext2_add_dir_entry(Context* ctx, Ext2Node* parentDirNode, - const char* name, Kernel::UInt32 inodeNumber, Kernel::UInt8 fileType) { - using namespace Kernel; + // Write to single-indirect block + auto singleRes = ext2_read_block_ptr(ctx, singleIndirectBlock); + if (singleRes.HasError()) return ErrorOr<Void*>(singleRes.Error()); - if (!ctx || !ctx->drive || !parentDirNode || !name) - return ErrorOr<Void>(kErrorInvalidData); + UInt32* singlePtr = singleRes.Leak().Leak(); + singlePtr[secondIdx] = physicalBlockNumber; - UInt32 blockSize = ctx->BlockSize(); - SizeT nameLen = rt_string_len(name); - if (nameLen == 0 || nameLen > 255) - return ErrorOr<Void>(kErrorInvalidData); + 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); + } - UInt16 newRecIdeal = ext2_dir_entry_ideal_len(static_cast<UInt8>(nameLen)); + mm_free_ptr(singlePtr); + return ErrorOr<Void*>(nullptr); + } - auto blockBuf = mm_alloc_ptr(blockSize, true, false); - if (!blockBuf) return ErrorOr<Void>(kErrorHeapOutOfMemory); - - for (int bi = 0; bi < EXT2_DIRECT_BLOCKS; ++bi) { - UInt32 blockNum = parentDirNode->inode.fBlock[bi]; - - if (blockNum == 0) { - // Allocate new block - auto groupInfoRes = ext2_get_group_descriptor_info(ctx, parentDirNode->inodeNumber); - if (!groupInfoRes) { - mm_free_ptr(blockBuf); - return ErrorOr<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()); - } + // 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(Kernel::Ext2Context* ctx, Ext2Node* dirNode, + const char* name) { + if (!ctx || !ctx->drive || !dirNode || !name) + return ErrorOr<EXT2_DIR_ENTRY*>(Kernel::kErrorInvalidData); + + // Check directory type + auto type = (dirNode->inode.fMode >> 12) & 0xF; + if (type != kExt2FileTypeDirectory) return ErrorOr<EXT2_DIR_ENTRY*>(Kernel::kErrorInvalidData); + + Kernel::UInt32 blockSize = ctx->BlockSize(); + auto blockBuf = mm_alloc_ptr(blockSize, true, false); + if (!blockBuf) return ErrorOr<EXT2_DIR_ENTRY*>(Kernel::kErrorHeapOutOfMemory); + + SizeT nameLen = rt_string_len(name); + for (Kernel::UInt32 i = 0; i < EXT2_DIRECT_BLOCKS; ++i) { + Kernel::UInt32 blockNum = dirNode->inode.fBlock[i]; + if (blockNum == 0) continue; + + Kernel::UInt32 lba = ext2_block_to_lba(ctx, blockNum); + if (!ext2_read_block(ctx->drive, lba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return ErrorOr<EXT2_DIR_ENTRY*>(Kernel::kErrorDisk); + } + + Kernel::UInt32 offset = 0; + while (offset + sizeof(Kernel::UInt32) + sizeof(Kernel::UInt16) <= blockSize) { + auto onDiskEntry = reinterpret_cast<EXT2_DIR_ENTRY*>((Kernel::UInt8*) blockBuf + offset); + if (onDiskEntry->fRecordLength == 0) break; // corrupted + + if (onDiskEntry->fInode != 0 && onDiskEntry->fNameLength == nameLen) { + // Compare names + if (rt_string_cmp(name, onDiskEntry->fName, nameLen) == 0) { + // Allocate a result sized to hold the name + metadata + SizeT recSize = sizeof(EXT2_DIR_ENTRY); + auto found = (EXT2_DIR_ENTRY*) mm_alloc_ptr(recSize, true, false); + if (!found) { mm_free_ptr(blockBuf); - return ErrorOr<Void>(nullptr); - } + return ErrorOr<EXT2_DIR_ENTRY*>(Kernel::kErrorHeapOutOfMemory); + } - // read it - UInt32 blockLba = ext2_block_to_lba(ctx, blockNum); - if (!ext2_read_block(ctx->drive, blockLba, blockBuf, blockSize)) { - mm_free_ptr(blockBuf); - return ErrorOr<Void>(kErrorDisk); + // 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; + } + } - 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; - } + mm_free_ptr(blockBuf); + return ErrorOr<EXT2_DIR_ENTRY*>(Kernel::kErrorFileNotFound); +} - if (!lastEntry) continue; +// Compute ideal record length for a directory name +static inline UInt16 ext2_dir_entry_ideal_len(UInt8 nameLen) { + UInt16 raw = + static_cast<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 +} - UInt16 lastIdeal = ext2_dir_entry_ideal_len(lastEntry->fNameLength); +static ErrorOr<Kernel::Void*> ext2_add_dir_entry(Kernel::Ext2Context* ctx, Ext2Node* parentDirNode, + const char* name, Kernel::UInt32 inodeNumber, + Kernel::UInt8 fileType) { + using namespace Kernel; - if (lastEntry->fRecordLength >= (UInt16)(lastIdeal + newRecIdeal)) { - UInt16 origRec = lastEntry->fRecordLength; - lastEntry->fRecordLength = lastIdeal; + if (!ctx || !ctx->drive || !parentDirNode || !name) return ErrorOr<Void*>(kErrorInvalidData); - 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); + UInt32 blockSize = ctx->BlockSize(); + SizeT nameLen = rt_string_len(name); + if (nameLen == 0 || nameLen > 255) return ErrorOr<Void*>(kErrorInvalidData); - if (!ext2_write_block(ctx->drive, blockLba, blockBuf, blockSize)) { - mm_free_ptr(blockBuf); - return ErrorOr<Void>(kErrorDisk); - } + UInt16 newRecIdeal = ext2_dir_entry_ideal_len(static_cast<UInt8>(nameLen)); - mm_free_ptr(blockBuf); - return ErrorOr<Void>(nullptr); - } - } + auto blockBuf = mm_alloc_ptr(blockSize, true, false); + if (!blockBuf) return ErrorOr<Void*>(kErrorHeapOutOfMemory); - // No space in direct blocks -> allocate new block - int targetIndex = -1; - for (int i = 0; i < EXT2_DIRECT_BLOCKS; ++i) { - if (parentDirNode->inode.fBlock[i] == 0) { targetIndex = i; break; } - } - if (targetIndex == -1) { - mm_free_ptr(blockBuf); - return ErrorOr<Void>(kErrorUnimplemented); - } + for (int bi = 0; bi < EXT2_DIRECT_BLOCKS; ++bi) { + UInt32 blockNum = parentDirNode->inode.fBlock[bi]; - auto groupInfoResult = ext2_get_group_descriptor_info(ctx, parentDirNode->inodeNumber); - if (!groupInfoResult) { - mm_free_ptr(blockBuf); - return ErrorOr<Void>(groupInfoResult.Error()); - } + if (blockNum == 0) { + // Allocate new block + auto groupInfoRes = ext2_get_group_descriptor_info(ctx, parentDirNode->inodeNumber); + if (!groupInfoRes) { + mm_free_ptr(blockBuf); + return ErrorOr<Void*>(groupInfoRes.Error()); + } - auto groupInfo = *groupInfoResult.Leak(); // Dereference to get Ext2GroupInfo* - auto newBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); - if (!newBlockRes) { + auto groupInfo = *groupInfoRes.Leak(); // Dereference to get Ext2GroupInfo* + auto allocBlockRes = ext2_alloc_block(ctx, groupInfo->groupDesc); + if (!allocBlockRes) { mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); mm_free_ptr(groupInfo); mm_free_ptr(blockBuf); - return ErrorOr<Void>(newBlockRes.Error()); - } + return ErrorOr<Void*>(allocBlockRes.Error()); + } + + UInt32 newBlock = *allocBlockRes.Leak(); // Dereference to get UInt32 + UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); - UInt32 newBlockNum = *newBlockRes.Leak(); // Dereference to get UInt32 - UInt32 gdtLba = ext2_block_to_lba(ctx, groupInfo->groupDescriptorBlock); - if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + if (!ext2_write_block(ctx->drive, gdtLba, groupInfo->blockBuffer, blockSize)) { mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); mm_free_ptr(groupInfo); mm_free_ptr(blockBuf); - return ErrorOr<Void>(kErrorDisk); + 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); } - mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); + // read it + UInt32 blockLba = ext2_block_to_lba(ctx, blockNum); + if (!ext2_read_block(ctx->drive, blockLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return ErrorOr<Void*>(kErrorDisk); + } + + UInt32 offset = 0; + EXT2_DIR_ENTRY* lastEntry = nullptr; + UInt32 lastOffset = 0; - 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); + 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); - UInt32 newBlockLba = ext2_block_to_lba(ctx, newBlockNum); - if (!ext2_write_block(ctx->drive, newBlockLba, blockBuf, blockSize)) { + if (!ext2_write_block(ctx->drive, blockLba, blockBuf, blockSize)) { mm_free_ptr(blockBuf); - return ErrorOr<Void>(kErrorDisk); + return ErrorOr<Void*>(kErrorDisk); + } + + mm_free_ptr(blockBuf); + return ErrorOr<Void*>(nullptr); } + } - auto setRes = ext2_set_block_address(ctx, parentDirNode, targetIndex, newBlockNum); - if (!setRes) { - mm_free_ptr(blockBuf); - return ErrorOr<Void>(setRes.Error()); + // No space in direct blocks -> allocate new block + int targetIndex = -1; + for (Kernel::UInt32 i = 0; i < EXT2_DIRECT_BLOCKS; ++i) { + if (parentDirNode->inode.fBlock[i] == 0) { + targetIndex = i; + break; } + } + if (targetIndex == -1) { + mm_free_ptr(blockBuf); + return ErrorOr<Void*>(kErrorUnimplemented); + } + auto groupInfoResult = ext2_get_group_descriptor_info(ctx, parentDirNode->inodeNumber); + if (!groupInfoResult) { mm_free_ptr(blockBuf); - return ErrorOr<Void>(nullptr); + 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(Context* ctx, EXT2_GROUP_DESCRIPTOR* groupDesc) { - if (!ctx || !ctx->superblock || !groupDesc) - return ErrorOr<UInt32>(kErrorInvalidData); - - UInt32 blockSize = ctx->BlockSize(); +static ErrorOr<UInt32> ext2_alloc_inode(Kernel::Ext2Context* ctx, + EXT2_GROUP_DESCRIPTOR* groupDesc) { + if (!ctx || !ctx->superblock || !groupDesc) return ErrorOr<UInt32>(kErrorInvalidData); - // buffer for the inode bitmap - auto bitmap = mm_alloc_ptr(blockSize, true, false); - if (!bitmap) return ErrorOr<UInt32>(kErrorHeapOutOfMemory); + UInt32 blockSize = ctx->BlockSize(); - // Read inode bitmap - if (!ext2_read_block(ctx->drive, groupDesc->fInodeBitmap, bitmap, blockSize)) { - mm_free_ptr(bitmap); - return ErrorOr<UInt32>(kErrorDisk); - } + // buffer for the inode bitmap + auto bitmap = mm_alloc_ptr(blockSize, true, false); + if (!bitmap) return ErrorOr<UInt32>(kErrorHeapOutOfMemory); - // 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); - } - } + // 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); + mm_free_ptr(bitmap); + return ErrorOr<UInt32>(kErrorDiskIsFull); } // to write an inode to its correct location on disk -static ErrorOr<Void> ext2_write_inode(Context* 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; - 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); - } +static ErrorOr<Void*> ext2_write_inode(Kernel::Ext2Context* ctx, Ext2Node* node) { + using namespace Kernel; - if (!ext2_read_block(ctx->drive, inodeLba, blockBuf, blockSize)) { - mm_free_ptr(blockBuf); - mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - return ErrorOr<Void>(kErrorDisk); - } + if (!ctx || !ctx->superblock || !ctx->drive || !node) return ErrorOr<Void*>(kErrorInvalidData); - // 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); + auto blockSize = ctx->BlockSize(); + UInt32 inodesPerGroup = ctx->superblock->fInodesPerGroup; - // Write the block back - if (!ext2_write_block(ctx->drive, inodeLba, blockBuf, blockSize)) { - mm_free_ptr(blockBuf); - mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - return ErrorOr<Void>(kErrorDisk); - } + 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>(nullptr); + 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; - } +// new +struct PathComponents { + const char** components; + int count; + Char* buffer; - int compCount = 0; - Char* p = buffer; - - while (*p != '\0') { - // skip slashes - while (*p == '/') p++; - if (*p == '\0') break; - - Char* start = p; - while (*p != '/' && *p != '\0') p++; - Char saved = *p; - *p = '\0'; - - // handle ".", "..", or normal - if (rt_string_cmp(start, ".", 1) == 0) { - // ignore - } else if (rt_string_cmp(start, "..", 2) == 0) { - if (compCount > 0) compCount--; // go up one level - } else { - temp[compCount++] = start; - } - - *p = saved; - } + PathComponents(const char* path) : components(nullptr), count(0), buffer(nullptr) { + if (!path || *path == '\0') return; - if (compCount == 0) { - mm_free_ptr(temp); - return; - } + SizeT pathLen = rt_string_len(path); + buffer = (Char*) mm_alloc_ptr(pathLen + 1, true, false); + if (!buffer) return; - components = (const char**)mm_alloc_ptr(sizeof(char*) * compCount, true, false); - if (!components) { - mm_free_ptr(temp); - return; - } + rt_copy_memory_safe((void*) path, buffer, pathLen, pathLen + 1); + buffer[pathLen] = '\0'; - for (int i = 0; i < compCount; i++) components[i] = temp[i]; - count = compCount; - - mm_free_ptr(temp); + // temp array + const char** temp = (const char**) mm_alloc_ptr(sizeof(char*) * (pathLen + 1), true, false); + if (!temp) { + mm_free_ptr(buffer); + buffer = nullptr; + return; } - ~PathComponents() { - if (components) mm_free_ptr(components); - if (buffer) mm_free_ptr(buffer); - } -}; -} // anonymous namespace + int compCount = 0; + Char* p = buffer; -// The Ext2FileSystemMgr -Ext2FileSystemMgr::Ext2FileSystemMgr(DriveTrait* drive) : ctx(drive) {} -NodePtr Ext2FileSystemMgr::Open(const char* path, const char* restrict_type) { - if (!path || *path == '\0' || !this->ctx.drive) { - return nullptr; - } + while (*p != '\0') { + // skip slashes + while (*p == '/') p++; + if (*p == '\0') break; - // Root ("/") - if (rt_string_len(path) == 1 && rt_string_cmp(path, "/", 1) == 0) { - auto inodeResult = ext2_load_inode(&this->ctx, EXT2_ROOT_INODE); - if (!inodeResult) { - return nullptr; - } + Char* start = p; + while (*p != '/' && *p != '\0') p++; + Char saved = *p; + *p = '\0'; - auto heapNode = (Ext2Node*)mm_alloc_ptr(sizeof(Ext2Node), true, false); - if (!heapNode) return nullptr; + // handle ".", "..", or normal + if (rt_string_cmp(start, ".", 1) == 0) { + // ignore + } else if (rt_string_cmp(start, "..", 2) == 0) { + if (compCount > 0) compCount--; // go up one level + } else { + temp[compCount++] = start; + } - *heapNode = *inodeResult.Leak(); - heapNode->cursor = 0; - return reinterpret_cast<NodePtr>(heapNode); + *p = saved; } - PathComponents pathComponents(path); - if (pathComponents.count == 0) { - return nullptr; + if (compCount == 0) { + mm_free_ptr(temp); + return; } - UInt32 currentInodeNumber = EXT2_ROOT_INODE; - Ext2Node* currentDirNode = nullptr; + components = (const char**) mm_alloc_ptr(sizeof(char*) * compCount, true, false); + if (!components) { + mm_free_ptr(temp); + return; + } - for (int i = 0; i < pathComponents.count; ++i) { - auto inodeResult = ext2_load_inode(&this->ctx, currentInodeNumber); - if (!inodeResult) { - if (currentDirNode) mm_free_ptr(currentDirNode); - return nullptr; - } + for (Kernel::UInt32 i = 0; i < compCount; i++) components[i] = temp[i]; + count = compCount; - if (currentDirNode) { - mm_free_ptr(currentDirNode); - currentDirNode = nullptr; - } + mm_free_ptr(temp); + } - currentDirNode = (Ext2Node*)mm_alloc_ptr(sizeof(Ext2Node), true, false); - if (!currentDirNode) { - return nullptr; - } + ~PathComponents() { + if (components) mm_free_ptr(components); + if (buffer) mm_free_ptr(buffer); + } +}; +} // anonymous namespace - *currentDirNode = *inodeResult.Leak(); - currentDirNode->cursor = 0; +// The Ext2FileSystemParser (not manager!) +Ext2FileSystemParser::Ext2FileSystemParser(DriveTrait* drive) : ctx(drive) {} +NodePtr Ext2FileSystemParser::Open(const char* path, const char* restrict_type) { + NE_UNUSED(restrict_type); + if (!path || *path == '\0' || !this->ctx.drive) { + return nullptr; + } - if (i < pathComponents.count - 1) { - UInt32 type = (currentDirNode->inode.fMode >> 12) & 0xF; - if (type != kExt2FileTypeDirectory) { - mm_free_ptr(currentDirNode); - return nullptr; - } - } + // Root ("/") + if (rt_string_len(path) == 1 && rt_string_cmp(path, "/", 1) == 0) { + auto inodeResult = ext2_load_inode(&this->ctx, EXT2_ROOT_INODE); + if (!inodeResult) { + return nullptr; + } - auto dirEntryResult = ext2_find_dir_entry(&this->ctx, currentDirNode, pathComponents.components[i]); - if (!dirEntryResult) { - mm_free_ptr(currentDirNode); - return nullptr; - } + auto heapNode = (Ext2Node*) mm_alloc_ptr(sizeof(Ext2Node), true, false); + if (!heapNode) return nullptr; - EXT2_DIR_ENTRY* entryPtr = *dirEntryResult.Leak(); - currentInodeNumber = entryPtr->fInode; - mm_free_ptr(entryPtr); - } + *heapNode = *inodeResult.Leak().Leak(); + heapNode->cursor = 0; + return reinterpret_cast<NodePtr>(heapNode); + } - auto finalInodeResult = ext2_load_inode(&this->ctx, currentInodeNumber); - if (!finalInodeResult) { - if (currentDirNode) mm_free_ptr(currentDirNode); - return nullptr; + PathComponents pathComponents(path); + if (pathComponents.count == 0) { + return nullptr; + } + + UInt32 currentInodeNumber = EXT2_ROOT_INODE; + Ext2Node* currentDirNode = nullptr; + + for (Kernel::UInt32 i = 0; i < pathComponents.count; ++i) { + auto inodeResult = ext2_load_inode(&this->ctx, currentInodeNumber); + if (!inodeResult) { + if (currentDirNode) mm_free_ptr(currentDirNode); + return nullptr; } if (currentDirNode) { - mm_free_ptr(currentDirNode); + mm_free_ptr(currentDirNode); + currentDirNode = nullptr; } - auto resultNode = (Ext2Node*)mm_alloc_ptr(sizeof(Ext2Node), true, false); - if (!resultNode) { + currentDirNode = (Ext2Node*) mm_alloc_ptr(sizeof(Ext2Node), true, false); + if (!currentDirNode) { + return nullptr; + } + + *currentDirNode = *inodeResult.Leak().Leak(); + currentDirNode->cursor = 0; + + if (i < pathComponents.count - 1) { + UInt32 type = (currentDirNode->inode.fMode >> 12) & 0xF; + if (type != kExt2FileTypeDirectory) { + mm_free_ptr(currentDirNode); return nullptr; + } + } + + auto dirEntryResult = + ext2_find_dir_entry(&this->ctx, currentDirNode, pathComponents.components[i]); + if (!dirEntryResult) { + mm_free_ptr(currentDirNode); + return nullptr; } - *resultNode = *finalInodeResult.Leak(); - resultNode->cursor = 0; - return reinterpret_cast<NodePtr>(resultNode); + EXT2_DIR_ENTRY* entryPtr = *dirEntryResult.Leak(); + currentInodeNumber = entryPtr->fInode; + mm_free_ptr(entryPtr); + } + + auto finalInodeResult = ext2_load_inode(&this->ctx, currentInodeNumber); + if (!finalInodeResult) { + if (currentDirNode) mm_free_ptr(currentDirNode); + return nullptr; + } + + if (currentDirNode) { + mm_free_ptr(currentDirNode); + } + + auto resultNode = (Ext2Node*) mm_alloc_ptr(sizeof(Ext2Node), true, false); + if (!resultNode) { + return nullptr; + } + + *resultNode = *finalInodeResult.Leak().Leak(); + resultNode->cursor = 0; + return reinterpret_cast<NodePtr>(resultNode); } +void* Ext2FileSystemParser::Read(NodePtr node, Int32 flags, SizeT size) { + if (!node) return nullptr; -void* Ext2FileSystemMgr::Read(NodePtr node, Int32 flags, SizeT size) { - if (!node) return nullptr; + NE_UNUSED(flags); - auto extNode = reinterpret_cast<Ext2Node*>(node); - auto dataResult = ext2_read_inode_data(&this->ctx, extNode, size); + auto extNode = reinterpret_cast<Ext2Node*>(node); + auto dataResult = ext2_read_inode_data(&this->ctx, extNode, size); - if (!dataResult) { - return nullptr; // error, nothing to return - } + if (!dataResult) { + return nullptr; // error, nothing to return + } - void* data = *dataResult.Leak(); - if (data) { - extNode->cursor += static_cast<UInt32>(size); - } + void* data = *dataResult.Leak(); + if (data) { + extNode->cursor += static_cast<UInt32>(size); + } - return data; + return data; } +void Ext2FileSystemParser::Write(NodePtr node, void* data, Int32 flags, SizeT size) { + if (!node || !data || size == 0) return; -void Ext2FileSystemMgr::Write(NodePtr node, void* data, Int32 flags, SizeT size) { - if (!node || !data || size == 0) return; - - auto extNode = reinterpret_cast<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(); - } + 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); - UInt32 physicalLba = ext2_block_to_lba(&this->ctx, physicalBlock); + while (bytesWritten < size) { + UInt32 logicalBlockIndex = currentOffset / blockSize; + UInt32 offsetInBlock = currentOffset % blockSize; - auto blockBuf = mm_alloc_ptr(blockSize, true, false); - if (!blockBuf) return; + auto physBlockResult = ext2_get_block_address(&this->ctx, extNode, logicalBlockIndex); + UInt32 physicalBlock = 0; - if (offsetInBlock > 0 || (size - bytesWritten) < blockSize) { - if (!ext2_read_block(this->ctx.drive, physicalLba, blockBuf, blockSize)) { - mm_free_ptr(blockBuf); - return; - } - } else { - rt_zero_memory(blockBuf, blockSize); + if (!physBlockResult) { + auto err = physBlockResult.Error(); + if (err == kErrorInvalidData || err == kErrorUnimplemented) { + auto groupInfoResult = ext2_get_group_descriptor_info(&this->ctx, extNode->inodeNumber); + if (!groupInfoResult) { + return; } - UInt32 bytesInCurrentBlock = static_cast<UInt32>(ext2_min(size - bytesWritten, blockSize - offsetInBlock)); - rt_copy_memory_safe(src, static_cast<void*>((UInt8*)blockBuf + offsetInBlock), - bytesInCurrentBlock, blockSize - offsetInBlock); + auto groupInfo = *groupInfoResult.Leak(); + auto allocResult = ext2_alloc_block(&this->ctx, groupInfo->groupDesc); + if (!allocResult) { + mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return; + } - if (!ext2_write_block(this->ctx.drive, physicalLba, blockBuf, blockSize)) { - mm_free_ptr(blockBuf); - return; + physicalBlock = *allocResult.Leak(); + + auto setRes = ext2_set_block_address(&this->ctx, extNode, logicalBlockIndex, physicalBlock); + if (!setRes) { + mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return; } - mm_free_ptr(blockBuf); + UInt32 gdtLba = ext2_block_to_lba(&this->ctx, groupInfo->groupDescriptorBlock); + if (!ext2_write_block(this->ctx.drive, gdtLba, groupInfo->blockBuffer, blockSize)) { + mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + return; + } - currentOffset += bytesInCurrentBlock; - src += bytesInCurrentBlock; - bytesWritten += bytesInCurrentBlock; + mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); + } else { + return; + } + } else { + physicalBlock = physBlockResult.Value(); } - if (currentOffset > extNode->inode.fSize) { - extNode->inode.fSize = currentOffset; + UInt32 physicalLba = ext2_block_to_lba(&this->ctx, physicalBlock); + + auto blockBuf = mm_alloc_ptr(blockSize, true, false); + if (!blockBuf) return; + + if (offsetInBlock > 0 || (size - bytesWritten) < blockSize) { + if (!ext2_read_block(this->ctx.drive, physicalLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return; + } + } else { + rt_zero_memory(blockBuf, blockSize); } - extNode->inode.fBlocks = (extNode->inode.fSize + blockSize - 1) / blockSize; - extNode->inode.fModifyTime = 0; + UInt32 bytesInCurrentBlock = + static_cast<UInt32>(ext2_min(size - bytesWritten, blockSize - offsetInBlock)); + rt_copy_memory_safe(src, static_cast<void*>((UInt8*) blockBuf + offsetInBlock), + bytesInCurrentBlock, blockSize - offsetInBlock); - auto writeInodeRes = ext2_write_inode(&this->ctx, extNode); - if (!writeInodeRes) { - // Failed to persist inode + if (!ext2_write_block(this->ctx.drive, physicalLba, blockBuf, blockSize)) { + mm_free_ptr(blockBuf); + return; } - extNode->cursor = currentOffset; -} + mm_free_ptr(blockBuf); + + currentOffset += bytesInCurrentBlock; + src += bytesInCurrentBlock; + bytesWritten += bytesInCurrentBlock; + } -bool Ext2FileSystemMgr::Seek(NodePtr node, SizeT offset) { - if (!node) return false; - auto extNode = reinterpret_cast<Ext2Node*>(node); - extNode->cursor = static_cast<UInt32>(offset); - return true; + if (currentOffset > extNode->inode.fSize) { + extNode->inode.fSize = currentOffset; + } + + extNode->inode.fBlocks = (extNode->inode.fSize + blockSize - 1) / blockSize; + extNode->inode.fModifyTime = 0; + + auto writeInodeRes = ext2_write_inode(&this->ctx, extNode); + if (!writeInodeRes) { + // Failed to persist inode + } + + extNode->cursor = currentOffset; } -SizeT Ext2FileSystemMgr::Tell(NodePtr node) { - if (!node) return 0; - auto extNode = reinterpret_cast<Ext2Node*>(node); - return extNode->cursor; +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; } -bool Ext2FileSystemMgr::Rewind(NodePtr node) { - if (!node) return false; - auto extNode = reinterpret_cast<Ext2Node*>(node); - extNode->cursor = 0; - return true; +SizeT Ext2FileSystemParser::Tell(NodePtr node) { + if (!node) return 0; + auto extNode = reinterpret_cast<Ext2Node*>(node); + return extNode->cursor; } -void* Ext2FileSystemMgr::Read(const char* name, NodePtr node, Int32 flags, SizeT size) { - return Read(node, flags, size); +bool Ext2FileSystemParser::Rewind(NodePtr node) { + if (!node) return false; + auto extNode = reinterpret_cast<Ext2Node*>(node); + extNode->cursor = 0; + return true; } -void Ext2FileSystemMgr::Write(const char* name, NodePtr node, void* data, Int32 flags, SizeT size) { - Write(node, data, flags, size); +void* Ext2FileSystemParser::Read(const char* name, NodePtr node, Int32 flags, SizeT size) { + NE_UNUSED(name); + return Read(node, flags, size); } -NodePtr Ext2FileSystemMgr::Create(const char* path) { - if (!path || *path == '\0') return nullptr; - - PathComponents pathComponents(path); - if (pathComponents.count == 0) return nullptr; - - const char* filename = pathComponents.components[pathComponents.count - 1]; - if (rt_string_len(filename) > kExt2FSMaxFileNameLen) return nullptr; - - // Build parent path - Char parentPathBuf[256] = {0}; - SizeT currentPathLen = 0; - for (int i = 0; i < pathComponents.count - 1; ++i) { - SizeT componentLen = rt_string_len(pathComponents.components[i]); - if (currentPathLen + componentLen + 1 >= sizeof(parentPathBuf)) return nullptr; - if (i > 0) parentPathBuf[currentPathLen++] = '/'; - rt_copy_memory_safe(const_cast<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(); - reinterpret_cast<Ext2Node*>(parentDirNodePtr)->cursor = 0; - } else { - parentDirNodePtr = Open(parentPathBuf, "r"); - } +void Ext2FileSystemParser::Write(const char* name, NodePtr node, void* data, Int32 flags, + SizeT size) { + NE_UNUSED(name); + Write(node, data, flags, size); +} +NodePtr Ext2FileSystemParser::Create(const char* path) { + if (!path || *path == '\0') return nullptr; + + PathComponents pathComponents(path); + if (pathComponents.count == 0) return nullptr; + + const char* filename = pathComponents.components[pathComponents.count - 1]; + if (rt_string_len(filename) > kExt2FSMaxFileNameLen) return nullptr; + + // Build parent path + Char parentPathBuf[256] = {0}; + SizeT currentPathLen = 0; + for (Kernel::UInt32 i = 0; (i < pathComponents.count - 1); ++i) { + SizeT componentLen = rt_string_len(pathComponents.components[i]); + if (currentPathLen + componentLen + 1 >= sizeof(parentPathBuf)) return nullptr; + if (i > 0) parentPathBuf[currentPathLen++] = '/'; + rt_copy_memory_safe(const_cast<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"); + } - auto parentDirNode = reinterpret_cast<Ext2Node*>(parentDirNodePtr); + if (!parentDirNodePtr) return nullptr; - // Ensure parent is a directory - UInt32 type = (parentDirNode->inode.fMode >> 12) & 0xF; - if (type != kExt2FileTypeDirectory) { - mm_free_ptr(parentDirNode); - return nullptr; - } + auto parentDirNode = reinterpret_cast<Ext2Node*>(parentDirNodePtr); - // Get group info for allocation - auto groupInfoResult = ext2_get_group_descriptor_info(&this->ctx, parentDirNode->inodeNumber); - if (!groupInfoResult) { - mm_free_ptr(parentDirNode); - return nullptr; - } - auto groupInfo = *groupInfoResult.Leak(); + // Ensure parent is a directory + UInt32 type = (parentDirNode->inode.fMode >> 12) & 0xF; + if (type != kExt2FileTypeDirectory) { + mm_free_ptr(parentDirNode); + return nullptr; + } - // Allocate new inode - auto newInodeRes = ext2_alloc_inode(&this->ctx, groupInfo->groupDesc); - if (!newInodeRes) { - mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); // so this works - mm_free_ptr(parentDirNode); - return nullptr; - } - UInt32 newInodeNumber = newInodeRes.Value(); + // Get group info for allocation + auto groupInfoResult = ext2_get_group_descriptor_info(&this->ctx, parentDirNode->inodeNumber); + if (!groupInfoResult) { + mm_free_ptr(parentDirNode); + return nullptr; + } + auto groupInfo = *groupInfoResult.Leak(); - UInt32 gdtLba = ext2_block_to_lba(&this->ctx, groupInfo->groupDescriptorBlock); - if (!ext2_write_block(this->ctx.drive, gdtLba, groupInfo->blockBuffer, this->ctx.BlockSize())) { - mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - mm_free_ptr(parentDirNode); - return nullptr; - } + // Allocate new inode + auto newInodeRes = ext2_alloc_inode(&this->ctx, groupInfo->groupDesc); + if (!newInodeRes) { + mm_free_ptr(reinterpret_cast<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(groupInfo); + mm_free_ptr(parentDirNode); + return nullptr; + } - // Create new Ext2Node - Ext2Node* newFileNode = reinterpret_cast<Ext2Node*>(mm_alloc_ptr(sizeof(Ext2Node), true, false)); - if (!newFileNode) { - mm_free_ptr(parentDirNode); - return nullptr; - } + mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); + mm_free_ptr(groupInfo); - newFileNode->inodeNumber = newInodeNumber; - rt_zero_memory(&newFileNode->inode, sizeof(EXT2_INODE)); - - newFileNode->inode.fMode = (kExt2FileTypeRegular << 12); - newFileNode->inode.fUID = 0; - newFileNode->inode.fGID = 0; - newFileNode->inode.fLinksCount = 1; - newFileNode->inode.fSize = 0; - newFileNode->inode.fBlocks = 0; - newFileNode->inode.fCreateTime = 0; - newFileNode->inode.fModifyTime = 0; - - // Persist new inode - auto writeInodeRes = ext2_write_inode(&this->ctx, newFileNode); - if (!writeInodeRes) { - mm_free_ptr(parentDirNode); - mm_free_ptr(newFileNode); - return nullptr; - } + // Create new Ext2Node + Ext2Node* newFileNode = reinterpret_cast<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; + } - // Add directory entry - auto addRes = ext2_add_dir_entry(&this->ctx, parentDirNode, filename, newInodeNumber, kExt2FileTypeRegular); - if (!addRes) { - mm_free_ptr(parentDirNode); - mm_free_ptr(newFileNode); - return nullptr; - } + // Update parent inode + auto parentWriteRes = ext2_write_inode(&this->ctx, parentDirNode); + // ignore failure - // Update parent inode - auto parentWriteRes = ext2_write_inode(&this->ctx, parentDirNode); - // ignore failure + NE_UNUSED(parentWriteRes); - mm_free_ptr(parentDirNode); - return reinterpret_cast<NodePtr>(newFileNode); + mm_free_ptr(parentDirNode); + return reinterpret_cast<NodePtr>(newFileNode); } -NodePtr Ext2FileSystemMgr::CreateDirectory(const char* path) { - if (!path || *path == '\0') return nullptr; +NodePtr Ext2FileSystemParser::CreateDirectory(const char* path) { + if (!path || *path == '\0') return nullptr; - PathComponents pathComponents(path); - if (pathComponents.count == 0) { - kout << "EXT2: Failed to parse path for CreateDirectory.\n"; - return nullptr; - } + PathComponents pathComponents(path); + if (pathComponents.count == 0) { + kout << "EXT2: Failed to parse path for CreateDirectory.\n"; + return nullptr; + } - const char* dirname = pathComponents.components[pathComponents.count - 1]; - if (rt_string_len(dirname) > kExt2FSMaxFileNameLen) { - kout << "EXT2: Directory name too long: " << dirname << ".\n"; - return nullptr; - } + const char* dirname = pathComponents.components[pathComponents.count - 1]; + if (rt_string_len(dirname) > kExt2FSMaxFileNameLen) { + kout << "EXT2: Directory name too long: " << dirname << ".\n"; + return nullptr; + } - // Build parent path - Char parentPathBuf[256]; - SizeT currentPathLen = 0; - for (int i = 0; i < pathComponents.count - 1; ++i) { - SizeT componentLen = rt_string_len(pathComponents.components[i]); - if (currentPathLen + componentLen + 1 >= sizeof(parentPathBuf)) { - kout << "EXT2: Parent path too long for CreateDirectory.\n"; - return nullptr; - } - - if (i > 0) parentPathBuf[currentPathLen++] = '/'; - - rt_copy_memory_safe(static_cast<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(); - 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; + // Build parent path + Char parentPathBuf[256]; + SizeT currentPathLen = 0; + for (Kernel::UInt32 i = 0; (i < pathComponents.count - 1); ++i) { + SizeT componentLen = rt_string_len(pathComponents.components[i]); + if (currentPathLen + componentLen + 1 >= sizeof(parentPathBuf)) { + kout << "EXT2: Parent path too long for CreateDirectory.\n"; + return nullptr; } - // Allocate inode - auto groupInfoResult = ext2_get_group_descriptor_info(&this->ctx, parentDirNode->inodeNumber); - if (!groupInfoResult) { - kout << "EXT2: Failed to get group descriptor info for new dir inode.\n"; - mm_free_ptr(parentDirNode); - return nullptr; - } - - auto groupInfo = *groupInfoResult.Leak(); - auto newInodeRes = ext2_alloc_inode(&this->ctx, groupInfo->groupDesc); - if (!newInodeRes) { - kout << "EXT2: Failed to allocate inode for new directory.\n"; - mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - mm_free_ptr(parentDirNode); - return nullptr; - } - - UInt32 newInodeNumber = *newInodeRes.Leak(); + if (i > 0) parentPathBuf[currentPathLen++] = '/'; - // Write back group descriptor block - UInt32 gdtLba = ext2_block_to_lba(&this->ctx, groupInfo->groupDescriptorBlock); - if (!ext2_write_block(this->ctx.drive, gdtLba, groupInfo->blockBuffer, this->ctx.BlockSize())) { - kout << "EXT2: Failed to write group descriptor after inode allocation.\n"; - mm_free_ptr(reinterpret_cast<void*>(groupInfo->blockBuffer)); - mm_free_ptr(groupInfo); - mm_free_ptr(parentDirNode); - return nullptr; + 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; + } - // 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; - } - + 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; + } - // Set the block in newDirNode - auto setBlkRes = ext2_set_block_address(&this->ctx, newDirNode, 0, newDirBlockNum); - if (!setBlkRes) { - kout << "EXT2: Failed to set data block for new directory.\n"; - mm_free_ptr(parentDirNode); - mm_free_ptr(newDirNode); - return nullptr; - } + UInt32 newDirBlockNum = *newBlockRes.Leak(); - // Prepare block with '.' and '..' - auto dirBlockBuf = mm_alloc_ptr(this->ctx.BlockSize(), true, false); - if (!dirBlockBuf) { - kout << "EXT2: Out of memory preparing directory block.\n"; - mm_free_ptr(parentDirNode); - mm_free_ptr(newDirNode); - return nullptr; - } - - rt_zero_memory(dirBlockBuf, this->ctx.BlockSize()); - - // '.' entry - auto dot = reinterpret_cast<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); + // 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; + } - // Persist new directory inode - auto writeInodeRes = ext2_write_inode(&this->ctx, newDirNode); - if (!writeInodeRes) { - kout << "EXT2: Failed to write new directory inode to disk.\n"; - mm_free_ptr(parentDirNode); - mm_free_ptr(newDirNode); - return nullptr; - } + mm_free_ptr(reinterpret_cast<void*>(groupForBlock->blockBuffer)); + mm_free_ptr(groupForBlock); - // Add directory entry into parent - auto addRes = ext2_add_dir_entry(&this->ctx, parentDirNode, dirname, newInodeNumber, kExt2FileTypeDirectory); - if (!addRes) { - kout << "EXT2: Failed to add directory entry for '" << dirname << "' to parent.\n"; - mm_free_ptr(parentDirNode); - mm_free_ptr(newDirNode); - return nullptr; - } + // Set the block in newDirNode + auto setBlkRes = ext2_set_block_address(&this->ctx, newDirNode, 0, newDirBlockNum); + if (!setBlkRes) { + kout << "EXT2: Failed to set data block for new directory.\n"; + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + // Prepare block with '.' and '..' + auto dirBlockBuf = mm_alloc_ptr(this->ctx.BlockSize(), true, false); + if (!dirBlockBuf) { + kout << "EXT2: Out of memory preparing directory block.\n"; + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + rt_zero_memory(dirBlockBuf, this->ctx.BlockSize()); + + // '.' entry + auto dot = reinterpret_cast<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; + } - // Increment parent link count and persist parent inode - parentDirNode->inode.fLinksCount += 1; - auto parentWriteRes = ext2_write_inode(&this->ctx, parentDirNode); - if (!parentWriteRes) { - kout << "EXT2: Warning: failed to update parent inode after directory creation.\n"; - } + mm_free_ptr(dirBlockBuf); + // Persist new directory inode + auto writeInodeRes = ext2_write_inode(&this->ctx, newDirNode); + if (!writeInodeRes) { + kout << "EXT2: Failed to write new directory inode to disk.\n"; + mm_free_ptr(parentDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + // Add directory entry into parent + auto addRes = ext2_add_dir_entry(&this->ctx, parentDirNode, dirname, newInodeNumber, + kExt2FileTypeDirectory); + if (!addRes) { + kout << "EXT2: Failed to add directory entry for '" << dirname << "' to parent.\n"; mm_free_ptr(parentDirNode); - return reinterpret_cast<NodePtr>(newDirNode); + mm_free_ptr(newDirNode); + return nullptr; + } + + // Increment parent link count and persist parent inode + parentDirNode->inode.fLinksCount += 1; + auto parentWriteRes = ext2_write_inode(&this->ctx, parentDirNode); + if (!parentWriteRes) { + kout << "EXT2: Warning: failed to update parent inode after directory creation.\n"; + } + + mm_free_ptr(parentDirNode); + return reinterpret_cast<NodePtr>(newDirNode); } -} // namespace Ext2 - #endif -#endif +#endif |
