Skip to content

Commit

Permalink
Use the ext2 filesystem for our root partition
Browse files Browse the repository at this point in the history
  • Loading branch information
malwarepad committed Jul 24, 2024
1 parent 3afd390 commit a378040
Show file tree
Hide file tree
Showing 13 changed files with 879 additions and 11 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ Important to mention these goals may never be satisfied, take a very long time t
- [x] Interrupts: ISR, IRQ, GDT
- [x] Ring3: Scheduling, ELF64, SSE, paging, `systemcall`
- [x] Generic: RTC
- Filesystems
- [x] VFS: Mountpoints, special files
- [x] FAT32: Read
- [x] Ext2: Read
- [ ] FAT32: Write
- [ ] Ext2: Write
- Device drivers
- [x] Storage: AHCI
- [x] Networking: RTL8139, RTL8169
Expand Down
289 changes: 289 additions & 0 deletions src/kernel/filesystems/ext2/ext2_controller.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
#include <ext2.h>
#include <malloc.h>
#include <string.h>
#include <system.h>
#include <task.h>
#include <timer.h>
#include <util.h>

bool ext2Mount(MountPoint *mount) {
// assign fsInfo
mount->fsInfo = malloc(sizeof(Ext2));
memset(mount->fsInfo, 0, sizeof(Ext2));
Ext2 *ext2 = EXT2_PTR(mount->fsInfo);

// base offset
ext2->offsetBase = mount->mbr.lba_first_sector;
ext2->offsetSuperblock = mount->mbr.lba_first_sector + 2;

// get superblock
uint8_t tmp[sizeof(Ext2Superblock)] = {0};
getDiskBytes(tmp, ext2->offsetSuperblock, 2);

// store it
memcpy(&ext2->superblock, tmp, sizeof(Ext2Superblock));

// checks
if (ext2->superblock.ext2_magic != 0xEF53) {
debugf("[ext2] Invalid magic number!\n");
goto error;
}

if (ext2->superblock.major < 1) {
debugf(
"[ext2] FATAL! Ancient, pre-historic ext2 partition discovered! Please "
"contact your local museum for further info...\n");
goto error;
}

if (ext2->superblock.extended.required_feature != EXT2_R_F_TYPE_FIELD) {
debugf("[ext2] FATAL! Unsupported flags detected: compression{%d} type{%d} "
"replay{%d} device{%d}\n",
ext2->superblock.extended.required_feature & EXT2_R_F_COMPRESSION,
ext2->superblock.extended.required_feature & EXT2_R_F_TYPE_FIELD,
ext2->superblock.extended.required_feature & EXT2_R_F_JOURNAL_REPLAY,
ext2->superblock.extended.required_feature &
EXT2_R_F_JOURNAL_DEVICE);
goto error;
}

if (ext2->superblock.fs_state != EXT2_FS_S_CLEAN) {
if (ext2->superblock.err == EXT2_FS_E_REMOUNT_RO) {
debugf("[ext2] FATAL! Read-only partition!\n");
goto error;
} else if (ext2->superblock.err == EXT2_FS_E_KPANIC) {
debugf("[ext2] FATAL! Superblock error caused panic!\n");
panic();
}
}

// log2.. why???!
ext2->blockSize = 1024 << ext2->superblock.log2block_size;

if ((ext2->blockSize % SECTOR_SIZE) != 0) {
debugf("[ext2] FATAL! Block size is not sector-aligned! blockSize{%d}\n",
ext2->blockSize);
goto error;
}

// calculate block groups
uint64_t blockGroups1 = DivRoundUp(ext2->superblock.total_blocks,
ext2->superblock.blocks_per_group);
uint64_t blockGroups2 = DivRoundUp(ext2->superblock.total_inodes,
ext2->superblock.inodes_per_group);
if (blockGroups1 != blockGroups2) {
debugf("[ext2] Total block group calculation doesn't match up! 1{%ld} "
"2{%ld}\n",
blockGroups1, blockGroups2);
goto error;
}
ext2->blockGroups = blockGroups1;

// find the Block Group Descriptor Table
// remember, very max is block size
ext2->offsetBGDT = BLOCK_TO_LBA(ext2, 0, ext2->superblock.superblock_idx + 1);
ext2->bgdts = (Ext2BlockGroup *)malloc(ext2->blockSize);
getDiskBytes((void *)ext2->bgdts, ext2->offsetBGDT,
DivRoundUp(ext2->blockSize, SECTOR_SIZE));

ext2->inodeSize = ext2->superblock.extended.inode_size;
ext2->inodeSizeRounded =
DivRoundUp(ext2->inodeSize, SECTOR_SIZE) * SECTOR_SIZE;

// done :")
return true;

error:
free(ext2);
return false;
}

bool ext2Open(MountPoint *mount, OpenFile *fd, char *filename) {
Ext2 *ext2 = EXT2_PTR(mount->fsInfo);

uint32_t inode = ext2TraversePath(ext2, filename, EXT2_ROOT_INODE);
if (!inode)
return false;

Ext2OpenFd *dir = (Ext2OpenFd *)malloc(sizeof(Ext2OpenFd));
memset(dir, 0, sizeof(Ext2OpenFd));

Ext2Inode *inodeFetched = ext2InodeFetch(ext2, inode);
fd->dir = dir;

dir->inodeNum = inode;
memcpy(&dir->inode, inodeFetched, sizeof(Ext2Inode));

if ((dir->inode.permission & 0xF000) == EXT2_S_IFDIR) {
size_t len = strlength(filename) + 1;
fd->dirname = malloc(len);
memcpy(fd->dirname, filename, len);
}

ext2BlockFetchInit(ext2, &dir->lookup);

// pointers & stuff
dir->ptr = 0;

free(inodeFetched);
return true;
}

int ext2Read(MountPoint *mount, OpenFile *fd, uint8_t *buff, int limit) {
Ext2 *ext2 = EXT2_PTR(mount->fsInfo);
Ext2OpenFd *dir = EXT2_DIR_PTR(fd->dir);

size_t filesize = ext2GetFilesize(fd);
if (dir->ptr >= filesize)
return 0;

size_t blocksRequired = DivRoundUp(limit, ext2->blockSize);
uint32_t *blocks =
ext2BlockChain(ext2, dir, dir->ptr / ext2->blockSize, blocksRequired);
uint8_t *bytes = (uint8_t *)malloc(ext2->blockSize);

int curr = 0; // will be used to return

// optimization: we can use consecutive sectors to make our life easier
int consecStart = -1;
int consecEnd = 0;

// +1 for starting
for (int i = 0; i < (blocksRequired + 1); i++) {
if (!blocks[i])
break;
bool last = i == (blocksRequired - 1);
if (consecStart < 0) {
// nothing consecutive yet
if (!last && blocks[i + 1] == (blocks[i] + 1)) {
// consec starts here
consecStart = i;
continue;
}
} else {
// we are in a consecutive that started since consecStart
if (last || blocks[i + 1] != (blocks[i] + 1))
consecEnd = i; // either last or the end
else // otherwise, we good
continue;
}

uint32_t offsetStarting = dir->ptr % ext2->blockSize; // remainder
if (consecEnd) {
// optimized consecutive cluster reading
int needed = consecEnd - consecStart + 1;
uint8_t *optimizedBytes = malloc(needed * ext2->blockSize);
getDiskBytes(optimizedBytes, BLOCK_TO_LBA(ext2, 0, blocks[consecStart]),
(needed * ext2->blockSize) / SECTOR_SIZE);

for (uint32_t i = offsetStarting; i < (needed * ext2->blockSize); i++) {
if (curr >= limit || dir->ptr >= filesize) {
free(optimizedBytes);
goto cleanup;
}

if (buff)
buff[curr] = optimizedBytes[i];

dir->ptr++;
curr++;
}

free(optimizedBytes);
} else {
getDiskBytes(bytes, BLOCK_TO_LBA(ext2, 0, blocks[i]),
ext2->blockSize / SECTOR_SIZE);

for (uint32_t i = offsetStarting; i < ext2->blockSize; i++) {
if (curr >= limit || dir->ptr >= filesize)
goto cleanup;

if (buff)
buff[curr] = bytes[i];

dir->ptr++;
curr++;
}
}

// traverse
consecStart = -1;
consecEnd = 0;
}

cleanup:
free(bytes);
free(blocks);
// debugf("[fd:%d id:%d] read %d bytes\n", fd->id, currentTask->id, curr);
// debugf("%d / %d\n", dir->ptr, dir->inode.size);
return curr;
}

bool ext2Seek(MountPoint *mount, OpenFile *fd, uint32_t target) {
Ext2OpenFd *dir = EXT2_DIR_PTR(fd->dir);

if (target > ext2GetFilesize(fd))
return false;
dir->ptr = target;
return true;
}

size_t ext2GetFilesize(OpenFile *fd) {
Ext2OpenFd *dir = EXT2_DIR_PTR(fd->dir);
return COMBINE_64(dir->inode.size_high, dir->inode.size);
}

void ext2StatInternal(Ext2Inode *inode, uint32_t inodeNum,
struct stat *target) {
target->st_dev = 69; // todo
target->st_ino = inodeNum;
target->st_mode = S_IFREG | S_IRUSR | S_IWUSR | S_IXUSR;
target->st_nlink = inode->hard_links;
target->st_uid = 0;
target->st_gid = 0;
target->st_rdev = 0;
target->st_blksize = 0x1000; // todo: honesty :")

if ((inode->permission & 0xF000) == 0x4000) {
target->st_size = 0x1000;

target->st_mode &= ~S_IFREG; // mark as dir
target->st_mode |= S_IFDIR;
} else
target->st_size = COMBINE_64(inode->size_high, inode->size);

target->st_blocks =
(DivRoundUp(target->st_size, target->st_blksize) * target->st_blksize) /
512;

target->st_atime = inode->atime;
target->st_mtime = inode->mtime;
target->st_ctime = inode->ctime;
}

bool ext2Stat(Ext2 *ext2, char *filename, struct stat *target) {
uint32_t inodeNum = ext2TraversePath(ext2, filename, EXT2_ROOT_INODE);
if (!inodeNum)
return false;
Ext2Inode *inode = ext2InodeFetch(ext2, inodeNum);

ext2StatInternal(inode, inodeNum, target);

free(inode);
return true;
}

bool ext2StatFd(Ext2 *ext2, OpenFile *fd, struct stat *target) {
Ext2OpenFd *dir = EXT2_DIR_PTR(fd->dir);
ext2StatInternal(&dir->inode, dir->inodeNum, target);
return true;
}

bool ext2Close(MountPoint *mount, OpenFile *fd) {
Ext2OpenFd *dir = EXT2_DIR_PTR(fd->dir);

ext2BlockFetchCleanup(&dir->lookup);

free(fd->dir);
return true;
}
64 changes: 64 additions & 0 deletions src/kernel/filesystems/ext2/ext2_dirs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#include <ext2.h>
#include <malloc.h>
#include <system.h>
#include <timer.h>
#include <util.h>

int ext2Getdents64(OpenFile *file, void *start, unsigned int hardlimit) {
Ext2 *ext2 = EXT2_PTR(file->mountPoint->fsInfo);
Ext2OpenFd *edir = EXT2_DIR_PTR(file->dir);

if ((edir->inode.permission & 0xF000) != EXT2_S_IFDIR)
return -ENOTDIR;

int64_t allocatedlimit = 0;
Ext2Inode *ino = &edir->inode;
uint8_t *names = (uint8_t *)malloc(ext2->blockSize);

struct linux_dirent64 *dirp = (struct linux_dirent64 *)start;

int dirsAvailable = 0;
while (true) {
size_t block =
ext2BlockFetch(ext2, ino, &edir->lookup, edir->ptr / ext2->blockSize);
if (!block)
break;
Ext2Directory *dir =
(Ext2Directory *)((size_t)names + (edir->ptr % ext2->blockSize));

getDiskBytes(names, BLOCK_TO_LBA(ext2, 0, block),
ext2->blockSize / SECTOR_SIZE);

while (((size_t)dir - (size_t)names) < ext2->blockSize) {
if (++dirsAvailable >= COMBINE_64(ino->size_high, ino->size))
break;
size_t reclen = 23 + dir->filenameLength + 1;
if ((allocatedlimit + reclen + 2) > hardlimit)
goto cleanup; // todo: error code special
dirp->d_ino = dir->inode;
dirp->d_off = rand(); // xd
if (dir->type == 2)
dirp->d_type = CDT_DIR;
else if (dir->type == 7)
dirp->d_type = CDT_LNK;
else
dirp->d_type = CDT_REG;
memcpy(dirp->d_name, dir->filename, dir->filenameLength);
dirp->d_name[dir->filenameLength] = '\0';
dirp->d_reclen = reclen;

allocatedlimit += reclen;
dirp = (struct linux_dirent64 *)((size_t)dirp + dirp->d_reclen);
edir->ptr += dir->size;
dir = (void *)((size_t)dir + dir->size);
}

int rem = edir->ptr % ext2->blockSize;
if (rem)
edir->ptr += ext2->blockSize - rem;
}

cleanup:
free(names);
return allocatedlimit;
}
Loading

0 comments on commit a378040

Please sign in to comment.