Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,36 @@ jobs:
xorriso \
grub-pc-bin \
grub-common \
mtools \
qemu-system-x86 \
llvm \

- name: Compile & Link
run: |
meson setup build
ninja -C build

- name: Test
- name: Artifact Analysis
run: |
timeout 30s ninja -C build run || true
timeout 10s ninja -C build runmin || true

grep -q "1KCSWF23Z456789" build/bootstrap.log
! grep -i "panic\|fault\|crash\|oops\|error" build/serial.log

ls -la build/voidframe.krnl
test -s build/voidframe.krnl

# Check if ISO was created
ls -la build/VoidFrame.iso
test -s build/VoidFrame.iso

file build/voidframe.krnl | grep -q "ELF.*executable"

# Verify kernel size is reasonable (not empty, not huge)
KERNEL_SIZE=$(stat -c%s build/voidframe.krnl)
test $KERNEL_SIZE -gt 100000 # > 100KB
test $KERNEL_SIZE -lt 10000000 # < 10MB

# Check ISO bootability
file build/VoidFrame.iso | grep -q "ISO.*bootable"

- name: Upload kernel artifacts
uses: actions/upload-artifact@v4
Expand Down
347 changes: 347 additions & 0 deletions fs/EXT/Ext2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,347 @@
#include "Ext2.h"
#include "../VFS.h"
#include "../../kernel/etc/Console.h"
#include "../../drivers/Ide.h"
#include "../../mm/KernelHeap.h"
#include "../../mm/MemOps.h"
#include "../../kernel/etc/StringOps.h"

#define EXT2_SUPERBLOCK_OFFSET 1024
#define EXT2_MAGIC 0xEF53

typedef struct {
uint8_t drive;
uint32_t block_size;
uint32_t inode_size;
uint32_t blocks_per_group;
uint32_t inodes_per_group;
uint32_t num_groups;
Ext2Superblock superblock;
Ext2GroupDesc* group_descs;
} Ext2Volume;

static Ext2Volume volume;
int ext2_initialized = 0;

// Helper to read a block from the disk
int Ext2ReadBlock(uint32_t block, void* buffer) {
if (block >= volume.superblock.s_blocks_count) {
PrintKernelF("[EXT2] Block %u out of bounds (max: %u)\n",
block, volume.superblock.s_blocks_count - 1);
return -1;
}
uint32_t sector_start = block * (volume.block_size / 512);
uint32_t num_sectors = volume.block_size / 512;
for (uint32_t i = 0; i < num_sectors; i++) {
if (IdeReadSector(volume.drive,
sector_start + i,
(uint8_t*)buffer + (i * 512)) != IDE_OK) {
return -1;
}
}
return 0;
}

int Ext2Init(uint8_t drive) {
if (ext2_initialized) {
return 0;
}
volume.drive = drive;

// The superblock is 1024 bytes long and located at offset 1024.
// We assume 512-byte sectors, so we need to read 2 sectors starting from sector 2.
uint8_t sb_buffer[1024];
if (IdeReadSector(drive, 2, sb_buffer) != IDE_OK || IdeReadSector(drive, 3, sb_buffer + 512) != IDE_OK) {
PrintKernelF("[EXT2] Failed to read superblock.\n");
return -1;
}

FastMemcpy(&volume.superblock, sb_buffer, sizeof(Ext2Superblock));

// Check for EXT2 magic number
if (volume.superblock.s_magic != EXT2_MAGIC) {
PrintKernelF("[EXT2] Invalid magic number. Not an EXT2 filesystem.\n");
return -1;
}

// Calculate important values
if (volume.superblock.s_log_block_size > 10) { // Max 1MB blocks
PrintKernelF("[EXT2] Invalid block size shift: %u\n",
volume.superblock.s_log_block_size);
return -1;
}
volume.block_size = 1024 << volume.superblock.s_log_block_size;
volume.inode_size = volume.superblock.s_inode_size;
volume.blocks_per_group = volume.superblock.s_blocks_per_group;
volume.inodes_per_group = volume.superblock.s_inodes_per_group;
if (volume.blocks_per_group == 0) {
PrintKernelF("[EXT2] Invalid blocks_per_group: 0\n");
return -1;
}
volume.num_groups = (volume.superblock.s_blocks_count + volume.blocks_per_group - 1) / volume.blocks_per_group;

PrintKernelF("[EXT2] Block size: %d bytes\n", volume.block_size);
PrintKernelF("[EXT2] Inode size: %d bytes\n", volume.inode_size);
PrintKernelF("[EXT2] Block groups: %d\n", volume.num_groups);

// Read Block Group Descriptor Table
uint32_t bgdt_size = volume.num_groups * sizeof(Ext2GroupDesc);
volume.group_descs = KernelMemoryAlloc(bgdt_size);
if (!volume.group_descs) {
PrintKernelF("[EXT2] Failed to allocate memory for BGD table.\n");
return -1;
}

uint32_t bgdt_block = (volume.block_size == 1024) ? 2 : 1;
if (Ext2ReadBlock(bgdt_block, volume.group_descs) != 0) {
PrintKernelF("[EXT2] Failed to read BGD table.\n");
KernelFree(volume.group_descs);
return -1;
}

PrintKernelSuccess("[EXT2] Filesystem initialized successfully.\n");
ext2_initialized = 1;
return 0;
}

int Ext2ReadInode(uint32_t inode_num, Ext2Inode* inode) {
if (inode_num == 0) return -1;

uint32_t group = (inode_num - 1) / volume.inodes_per_group;
if (group >= volume.num_groups) return -1;

uint32_t index = (inode_num - 1) % volume.inodes_per_group;
uint32_t inode_table_block = volume.group_descs[group].bg_inode_table;

uint32_t block_offset = (index * volume.inode_size) / volume.block_size;
uint32_t offset_in_block = (index * volume.inode_size) % volume.block_size;

uint8_t* block_buffer = KernelMemoryAlloc(volume.block_size);
if (!block_buffer) return -1;

if (Ext2ReadBlock(inode_table_block + block_offset, block_buffer) != 0) {
KernelFree(block_buffer);
return -1;
}

FastMemcpy(inode, block_buffer + offset_in_block, sizeof(Ext2Inode));

KernelFree(block_buffer);
return 0;
}

// Find a directory entry in a directory inode
uint32_t Ext2FindInDir(Ext2Inode* dir_inode, const char* name) {
if (!S_ISDIR(dir_inode->i_mode)) {
return 0; // Not a directory
}
uint8_t* block_buffer = KernelMemoryAlloc(volume.block_size);
if (!block_buffer) return 0;
// We only handle direct blocks for now
for (int i = 0; i < 12; i++) {
if (dir_inode->i_block[i] == 0) continue;
if (Ext2ReadBlock(dir_inode->i_block[i], block_buffer) != 0) {
continue;
}
Ext2DirEntry* entry = (Ext2DirEntry*)block_buffer;
uint32_t offset = 0;
while (offset < volume.block_size) {
if (entry->inode == 0) break;
char name_buf[256];
FastMemcpy(name_buf, entry->name, entry->name_len);
name_buf[entry->name_len] = '\0';
PrintKernelF(" %s\n", name_buf);
offset += entry->rec_len;
if (entry->rec_len == 0) {
PrintKernelF("[EXT2] Corrupted directory entry\n");
break;
}
entry = (Ext2DirEntry*)((uint8_t*)entry + entry->rec_len);
}
}
KernelFree(block_buffer);
return 0; // Not found
}

uint32_t Ext2PathToInode(const char* path) {
if (!ext2_initialized || !path) {
return 0;
}

if (path[0] == '/' && (path[1] == '\0' || path[1] == ' ')) {
return 2; // Root directory inode
}

// Start from root inode
uint32_t current_inode_num = 2;
Ext2Inode current_inode;
if (Ext2ReadInode(current_inode_num, &current_inode) != 0) {
return 0;
}

char component[256];
const char* p = path;
if (*p == '/') p++;

while (*p) {
// Extract next path component
int i = 0;
while (*p && *p != '/' && i < 255) {
component[i++] = *p++;
}
component[i] = '\0';

if (!S_ISDIR(current_inode.i_mode)) {
return 0; // Not a directory, but path continues
}

current_inode_num = Ext2FindInDir(&current_inode, component);
if (current_inode_num == 0) {
return 0; // Component not found
}

if (Ext2ReadInode(current_inode_num, &current_inode) != 0) {
return 0; // Failed to read next inode
}

if (*p == '/') p++;
}

return current_inode_num;
}

int Ext2ReadFile(const char* path, void* buffer, uint32_t max_size) {
if (!ext2_initialized) return -1;

uint32_t inode_num = Ext2PathToInode(path);
if (inode_num == 0) return -1; // Not found

Ext2Inode inode;
if (Ext2ReadInode(inode_num, &inode) != 0) {
return -1; // Failed to read inode
}

if (!S_ISREG(inode.i_mode)) {
return -1; // Not a regular file
}

uint32_t file_size = inode.i_size;
uint32_t bytes_to_read = (file_size < max_size) ? file_size : max_size;
uint32_t bytes_read = 0;

uint8_t* block_buffer = KernelMemoryAlloc(volume.block_size);
if (!block_buffer) return -1;

// Only handle direct blocks for now
for (int i = 0; i < 12 && bytes_read < bytes_to_read; i++) {
if (inode.i_block[i] == 0) continue;

if (Ext2ReadBlock(inode.i_block[i], block_buffer) != 0) {
KernelFree(block_buffer);
return -1;
}

uint32_t remaining_in_block = volume.block_size;
uint32_t remaining_in_file = bytes_to_read - bytes_read;
uint32_t copy_size = (remaining_in_block < remaining_in_file) ? remaining_in_block : remaining_in_file;

FastMemcpy((uint8_t*)buffer + bytes_read, block_buffer, copy_size);
bytes_read += copy_size;
}

KernelFree(block_buffer);
return bytes_read;
}

int Ext2WriteFile(const char* path, const void* buffer, uint32_t size) {
PrintKernelF("[EXT2] WriteFile: %s (Not implemented)\n", path);
return -1;
}

int Ext2ListDir(const char* path) {
if (!ext2_initialized) return -1;

uint32_t inode_num = Ext2PathToInode(path);
if (inode_num == 0) return -1;

Ext2Inode inode;
if (Ext2ReadInode(inode_num, &inode) != 0) return -1;

if (!S_ISDIR(inode.i_mode)) return -1;

uint8_t* block_buffer = KernelMemoryAlloc(volume.block_size);
if (!block_buffer) return -1;

PrintKernelF("Listing directory: %s\n", path);

// Direct blocks only
for (int i = 0; i < 12; i++) {
if (inode.i_block[i] == 0) continue;
if (Ext2ReadBlock(inode.i_block[i], block_buffer) != 0) continue;

Ext2DirEntry* entry = (Ext2DirEntry*)block_buffer;
uint32_t offset = 0;
while (offset < volume.block_size) {
if (entry->inode == 0) break;

char name_buf[256];
FastMemcpy(name_buf, entry->name, entry->name_len);
name_buf[entry->name_len] = '\0';
PrintKernelF(" %s\n", name_buf);

offset += entry->rec_len;
entry = (Ext2DirEntry*)((uint8_t*)entry + entry->rec_len);
}
}

KernelFree(block_buffer);
return 0;
}

int Ext2CreateFile(const char* path) {
PrintKernelF("[EXT2] CreateFile: %s (Not implemented)\n", path);
return -1;
}

int Ext2CreateDir(const char* path) {
PrintKernelF("[EXT2] CreateDir: %s (Not implemented)\n", path);
return -1;
}

int Ext2Delete(const char* path) {
PrintKernelF("[EXT2] Delete: %s (Not implemented)\n", path);
return -1;
}

int Ext2IsFile(const char* path) {
if (!ext2_initialized) return 0;
uint32_t inode_num = Ext2PathToInode(path);
if (inode_num == 0) return 0;

Ext2Inode inode;
if (Ext2ReadInode(inode_num, &inode) != 0) return 0;

return S_ISREG(inode.i_mode);
}

int Ext2IsDir(const char* path) {
if (!ext2_initialized) return 0;
uint32_t inode_num = Ext2PathToInode(path);
if (inode_num == 0) return 0;

Ext2Inode inode;
if (Ext2ReadInode(inode_num, &inode) != 0) return 0;

return S_ISDIR(inode.i_mode);
}

uint64_t Ext2GetFileSize(const char* path) {
if (!ext2_initialized) return 0;
uint32_t inode_num = Ext2PathToInode(path);
if (inode_num == 0) return 0;

Ext2Inode inode;
if (Ext2ReadInode(inode_num, &inode) != 0) return 0;

return inode.i_size;
}
Loading
Loading