diff --git a/include/config.h b/include/config.h index 3e985a8..d8e13a2 100644 --- a/include/config.h +++ b/include/config.h @@ -28,6 +28,7 @@ #define PROCESS_NAME_MAX 64 #define PROCESS_STACK_SIZE 0x10000 // 64kb #define PROCESS_STACK_TOP 0x80000000 +#define PROCESS_FD_MAX 32 /* sched */ // 1 tick = 1 ms => quantum = 10ms @@ -47,6 +48,7 @@ /* fs */ #define CALYXFS_FILES_MAX 10 +#define TAR_OPEN_FILES_MAX 64 /* paging */ #define PAGING_MAX_PHYS 0x200000000 diff --git a/include/fs/tar.h b/include/fs/tar.h index 0726de1..43ac6eb 100644 --- a/include/fs/tar.h +++ b/include/fs/tar.h @@ -9,6 +9,7 @@ #include #include +#include struct tar_header { @@ -33,4 +34,9 @@ int tar_init_fs(struct limine_file* file); struct tar_header* tar_file_lookup(const char* filename); void tar_file_read(struct tar_header* header, uint8_t* buf); +int tar_open(const char* path, int flags); +int tar_close(int fd); +int64_t tar_read(int fd, char* buf, size_t count); +int64_t tar_write(int fd, char* buf, size_t count); + #endif \ No newline at end of file diff --git a/include/fs/vfs.h b/include/fs/vfs.h new file mode 100644 index 0000000..cfc7ac0 --- /dev/null +++ b/include/fs/vfs.h @@ -0,0 +1,50 @@ +/* + * @author xamidev + * @brief Virtual filesystem layer + * @license GPL-3.0-only + */ + +/* + PepperOS will not work like the Unix-based mountpoint approach, + but rather like what Windows does. Filesystems will be separated + into "drives", and each drive will have a number assigned. + */ + +#ifndef VFS_H +#define VFS_H + +#include +#include +#include + +typedef enum +{ + DRIVE_TAR +} DriveType; + +struct drive +{ + unsigned int id; + DriveType type; + struct fs_operations* operations; + + struct drive* next; +}; + +struct fs_operations +{ + int (*open)(const char* path, int flags); + int (*close)(int fd); + int64_t (*read)(int fd, char* buf, size_t count); + int64_t (*write)(int fd, char* buf, size_t count); +}; + +int vfs_mount(struct limine_file* file, DriveType type); +int vfs_umount(unsigned int drive_id); + +int vfs_open(const char* path, int flags); +int vfs_close(int fd); +int64_t vfs_read(int fd, char* buf, size_t count); +int64_t vfs_write(int fd, const char* buf, size_t count); + +#endif \ No newline at end of file diff --git a/include/kernel.h b/include/kernel.h index 7a9712b..d9779d4 100644 --- a/include/kernel.h +++ b/include/kernel.h @@ -10,7 +10,13 @@ #include "limine.h" enum ErrorCodes { ENOMEM, - EIO + EIO, + EINVAL, + ENOENT, + EFAULT, + EBADF, + EBUSY, + ENOSYS }; #define CLEAR_INTERRUPTS __asm__ volatile("cli") diff --git a/include/sched/process.h b/include/sched/process.h index 13fe331..19a29bd 100644 --- a/include/sched/process.h +++ b/include/sched/process.h @@ -11,6 +11,7 @@ #include #include #include +#include typedef enum { READY, @@ -18,6 +19,12 @@ typedef enum { DEAD } status_t; +struct process_fd { + bool used; + unsigned int drive_id; + int fs_fd; +}; + struct process { size_t pid; char name[PROCESS_NAME_MAX]; @@ -26,6 +33,7 @@ struct process { struct cpu_status* context; void* root_page_table; // Process PML4 (should contain kernel PML4 in higher half [256-511] void* kernel_stack; // Used for interrupts (syscall: int 0x80), defines the TSS RSP0 + struct process_fd fds[PROCESS_FD_MAX]; struct process* next; }; @@ -40,4 +48,9 @@ void process_display_list(struct process* processes_list); void process_create_user(struct limine_file* file, char* name); +void process_fd_init(struct process* proc); +int process_fd_alloc(struct process* proc, unsigned int drive_id, int fs_fd); +int process_fd_get_fsfd(struct process* proc, int fd, unsigned int* drive_id, int* fs_fd); +int process_fd_release(struct process* proc, int fd); + #endif diff --git a/src/arch/x86/syscall.c b/src/arch/x86/syscall.c index 839969f..fed4025 100644 --- a/src/arch/x86/syscall.c +++ b/src/arch/x86/syscall.c @@ -10,26 +10,45 @@ #include #include #include +#include extern struct process* current_process; -void sys_write(unsigned int fd, const char* buf, size_t count) +int64_t sys_read(unsigned int fd, char* buf, size_t count) +{ + return vfs_read((int)fd, buf, count); +} + +int64_t sys_write(unsigned int fd, const char* buf, size_t count) { switch (fd) { case 1: //stdout for (size_t i=0; istatus = DEAD; @@ -61,12 +80,20 @@ struct cpu_status* syscall_handler(struct cpu_status* regs) switch (regs->rax) { case 0: //sys_read + regs->rax = (uint64_t)sys_read((unsigned int)regs->rdi, (char*)regs->rsi, (size_t)regs->rdx); break; case 1: //sys_write - sys_write(regs->rdi, (char*)regs->rsi, regs->rdx); + regs->rax = (uint64_t)sys_write((unsigned int)regs->rdi, (const char*)regs->rsi, (size_t)regs->rdx); + break; + case 2: //sys_open + regs->rax = (uint64_t)sys_open((const char*)regs->rdi, (int)regs->rsi); + break; + case 3: //sys_close + regs->rax = (uint64_t)sys_close((unsigned int)regs->rdi); break; case 60: //sys_exit sys_exit(regs->rdi); + regs->rax = 0; break; default: regs->rax = 0xbad515ca11; diff --git a/src/fs/tar.c b/src/fs/tar.c index 839f551..e0deeb5 100644 --- a/src/fs/tar.c +++ b/src/fs/tar.c @@ -25,6 +25,14 @@ struct tar_header* headers[CALYXFS_FILES_MAX] = {0}; +struct tar_open_file { + bool used; + struct tar_header* header; + size_t offset; +}; + +struct tar_open_file open_files[TAR_OPEN_FILES_MAX] = {0}; + struct tar_file archive; #define TAR_BLOCK_SIZE 512 @@ -101,6 +109,7 @@ int tar_init_fs(struct limine_file* file) DEBUG("calyxfs found at 0x%p (size=%u)", file->address, file->size); archive.address = file->address; archive.size = file->size; + memset(open_files, 0, sizeof(open_files)); tar_parse((uint64_t)archive.address); return 0; } @@ -179,4 +188,108 @@ void tar_file_read(struct tar_header* header, uint8_t* buf) void* file_ptr = (void*)((uint8_t*)header + TAR_BLOCK_SIZE); memcpy(buf, (void*)file_ptr, sz); +} + +int tar_open(const char* path, int flags) +{ + if (!path) { + return -EINVAL; + } + + if (flags != 0) { + return -ENOSYS; + } + + const char* lookup_path = path; + while (*lookup_path == '/') { + lookup_path++; + } + + if (*lookup_path == '\0') { + return -EINVAL; + } + + struct tar_header* header = tar_file_lookup(lookup_path); + if (!header) { + char prefixed[102] = {0}; + prefixed[0] = '.'; + prefixed[1] = '/'; + + size_t i = 0; + while (lookup_path[i] != '\0' && i < sizeof(prefixed) - 3) { + prefixed[2 + i] = lookup_path[i]; + i++; + } + prefixed[2 + i] = '\0'; + + header = tar_file_lookup(prefixed); + } + + if (!header) { + return -ENOENT; + } + + for (int fd = 0; fd < TAR_OPEN_FILES_MAX; fd++) { + if (!open_files[fd].used) { + open_files[fd].used = true; + open_files[fd].header = header; + open_files[fd].offset = 0; + return fd; + } + } + + return -ENOMEM; +} + +int tar_close(int fd) +{ + if (fd < 0 || fd >= TAR_OPEN_FILES_MAX) { + return -EBADF; + } + + if (!open_files[fd].used) { + return -EBADF; + } + + open_files[fd].used = false; + open_files[fd].header = NULL; + open_files[fd].offset = 0; + return 0; +} + +int64_t tar_read(int fd, char* buf, size_t count) +{ + if (!buf || fd < 0 || fd >= TAR_OPEN_FILES_MAX) { + return -EINVAL; + } + + if (!open_files[fd].used || !open_files[fd].header) { + return -EBADF; + } + + uint32_t file_size = tar_getsize(open_files[fd].header->size); + if (open_files[fd].offset >= file_size) { + return 0; + } + + size_t remaining = (size_t)file_size - open_files[fd].offset; + size_t read_count = (count < remaining) ? count : remaining; + + uint8_t* file_ptr = (uint8_t*)open_files[fd].header + TAR_BLOCK_SIZE + open_files[fd].offset; + memcpy(buf, file_ptr, read_count); + open_files[fd].offset += read_count; + return (int64_t)read_count; +} + +/* + * tar_write - Write to file in TAR archive + * + * Not implemented. + */ +int64_t tar_write(int fd, char* buf, size_t count) +{ + (void)fd; + (void)buf; + (void)count; + return -ENOSYS; } \ No newline at end of file diff --git a/src/fs/vfs.c b/src/fs/vfs.c index 68c6ff1..c13ea8d 100644 --- a/src/fs/vfs.c +++ b/src/fs/vfs.c @@ -13,4 +13,311 @@ filesystem-dependent functions. That way we have a nice abstraction layer and can just do calls no matter the underlying fs. -*/ \ No newline at end of file +*/ + +#include +#include +#include +#include +#include +#include + +extern struct process* current_process; + +struct drive* root_drive; + +// Bump-allocated (much like PIDs) +unsigned int next_drive_id = 0; + +static struct drive* vfs_find_drive(unsigned int drive_id) +{ + struct drive* curr = root_drive; + while (curr != NULL) { + if (curr->id == drive_id) { + return curr; + } + curr = curr->next; + } + + return NULL; +} + +static int vfs_resolve_path(const char* path, struct drive** drive, const char** inner_path) +{ + if (!path || !drive || !inner_path) { + return -EINVAL; + } + + if (path[0] >= '0' && path[0] <= '9') { + unsigned int drive_id = 0; + size_t idx = 0; + + while (path[idx] >= '0' && path[idx] <= '9') { + drive_id = (drive_id * 10) + (unsigned int)(path[idx] - '0'); + idx++; + } + + if (path[idx] != ':' || path[idx + 1] != '/') { + return -EINVAL; + } + + *drive = vfs_find_drive(drive_id); + if (*drive == NULL) { + return -ENOENT; + } + + *inner_path = &path[idx + 1]; + return 0; + } + + if (root_drive == NULL) { + return -ENOENT; + } + + *drive = root_drive; + *inner_path = path; + return 0; +} + +/* + * vfs_add_drive - Add a drive to the drive list + * @new: the new drive + * + * This function adds the @new drive at the end + * of the drive linked list. + * + * Return: + * %0 - on success + */ +int vfs_add_drive(struct drive* new) +{ + if (root_drive == NULL) { + root_drive = new; + return 0; + } + struct drive* curr = root_drive; + while (curr->next != NULL) { + curr = curr->next; + } + + curr->next = new; + new->next = NULL; + return 0; +} + +/* + * vfs_mount - Mount a drive + * @file: Limine-loaded module file + * @type: Filesystem type + * + * This function creates a new drive object and then adds + * it to the drive linked list. It also initializes the + * filesystem driver corresponding to the drive type. + * + * Return: + * %0 - on success + * %-ENOMEM - not enough memory + */ +int vfs_mount(struct limine_file* file, DriveType type) +{ + struct fs_operations* ops = NULL; + struct drive* new = kmalloc(sizeof(struct drive)); + if (new == NULL) { + return -ENOMEM; + } + new->id = next_drive_id++; + new->type = type; + new->operations = NULL; + new->next = NULL; + + switch (type) { + case DRIVE_TAR: + if (tar_init_fs(file) < 0) { + kfree(new); + return -EIO; + } + + ops = kmalloc(sizeof(struct fs_operations)); + if (ops == NULL) { + kfree(new); + return -ENOMEM; + } + + ops->open = tar_open; + ops->close = tar_close; + ops->read = tar_read; + ops->write = tar_write; + new->operations = ops; + break; + default: + kfree(new); + return -EINVAL; + } + + return vfs_add_drive(new); +} + +/* + * vfs_remove_drive - Remove a drive from the drive list + * @old: Drive to remove + * + * Return: + * %0 - on success + */ +int vfs_remove_drive(struct drive* old) +{ + if (!old || !root_drive) { + return -ENOENT; + } + + if (root_drive == old) { + root_drive = old->next; + old->next = NULL; + if (old->operations) { + kfree(old->operations); + } + kfree(old); + return 0; + } + + struct drive* tmp = root_drive; + while (tmp->next != NULL && tmp->next != old) { + tmp = tmp->next; + } + + if (tmp->next == NULL) { + return -ENOENT; + } + + // Next is the one to remove + tmp->next = old->next; + old->next = NULL; + if (old->operations) { + kfree(old->operations); + } + kfree(old); + return 0; +} + +/* + * vfs_umount - Unmount a drive + * @drive_id: drive ID to unmount + * + * Return: + * %0 - on success + * %-ENOENT - drive not found + */ +int vfs_umount(unsigned int drive_id) +{ + struct drive* tmp = root_drive; + while (tmp != NULL) { + if (tmp->id == drive_id) { + goto found; + } + tmp = tmp->next; + } + return -ENOENT; + +found: + return vfs_remove_drive(tmp); +} + +int vfs_open(const char* path, int flags) +{ + struct drive* drive = NULL; + const char* inner_path = NULL; + + if (!current_process) { + return -EINVAL; + } + + int rc = vfs_resolve_path(path, &drive, &inner_path); + if (rc < 0) { + return rc; + } + + if (!drive->operations || !drive->operations->open) { + return -ENOSYS; + } + + int fs_fd = drive->operations->open(inner_path, flags); + if (fs_fd < 0) { + return fs_fd; + } + + int proc_fd = process_fd_alloc(current_process, drive->id, fs_fd); + if (proc_fd < 0) { + drive->operations->close(fs_fd); + return proc_fd; + } + + return proc_fd; +} + +int vfs_close(int fd) +{ + if (!current_process) { + return -EINVAL; + } + + unsigned int drive_id = 0; + int fs_fd = -1; + int rc = process_fd_get_fsfd(current_process, fd, &drive_id, &fs_fd); + if (rc < 0) { + return rc; + } + + struct drive* drive = vfs_find_drive(drive_id); + if (!drive || !drive->operations || !drive->operations->close) { + return -ENOENT; + } + + rc = drive->operations->close(fs_fd); + if (rc < 0) { + return rc; + } + + return process_fd_release(current_process, fd); +} + +int64_t vfs_read(int fd, char* buf, size_t count) +{ + if (!current_process || !buf) { + return -EINVAL; + } + + unsigned int drive_id = 0; + int fs_fd = -1; + int rc = process_fd_get_fsfd(current_process, fd, &drive_id, &fs_fd); + if (rc < 0) { + return rc; + } + + struct drive* drive = vfs_find_drive(drive_id); + if (!drive || !drive->operations || !drive->operations->read) { + return -ENOENT; + } + + return (int64_t)drive->operations->read(fs_fd, buf, count); +} + +int64_t vfs_write(int fd, const char* buf, size_t count) +{ + if (!current_process || !buf) { + return -EINVAL; + } + + unsigned int drive_id = 0; + int fs_fd = -1; + int rc = process_fd_get_fsfd(current_process, fd, &drive_id, &fs_fd); + if (rc < 0) { + return rc; + } + + struct drive* drive = vfs_find_drive(drive_id); + if (!drive || !drive->operations || !drive->operations->write) { + return -ENOENT; + } + + return (int64_t)drive->operations->write(fs_fd, (char*)buf, count); +} \ No newline at end of file diff --git a/src/kmain.c b/src/kmain.c index 9470bfe..3a614de 100644 --- a/src/kmain.c +++ b/src/kmain.c @@ -4,6 +4,7 @@ * @license GPL-3.0-only */ +#include "fs/vfs.h" #include #include #include @@ -125,14 +126,8 @@ void kmain() } file = boot_ctx.module->modules[0]; - tar_init_fs(file); /* DANGER ZONE (tests)*/ - struct tar_header* header = tar_file_lookup("./welcome.txt"); - - uint8_t buf[1000] = {0}; - tar_file_read(header, buf); - DEBUG("-BEGIN BUFFER-%s-END BUFFER-", buf); /* END DANGER ZONE */ scheduler_init(); diff --git a/src/sched/process.c b/src/sched/process.c index 0f16df2..01e2503 100644 --- a/src/sched/process.c +++ b/src/sched/process.c @@ -26,6 +26,64 @@ extern uint64_t *kernel_pml4; size_t next_free_pid = 0; +void process_fd_init(struct process* proc) +{ + if (!proc) { + return; + } + + memset(proc->fds, 0, sizeof(proc->fds)); +} + +int process_fd_alloc(struct process* proc, unsigned int drive_id, int fs_fd) +{ + if (!proc || fs_fd < 0) { + return -EINVAL; + } + + for (int fd = 3; fd < PROCESS_FD_MAX; fd++) { + if (!proc->fds[fd].used) { + proc->fds[fd].used = true; + proc->fds[fd].drive_id = drive_id; + proc->fds[fd].fs_fd = fs_fd; + return fd; + } + } + + return -ENOMEM; +} + +int process_fd_get_fsfd(struct process* proc, int fd, unsigned int* drive_id, int* fs_fd) +{ + if (!proc || !drive_id || !fs_fd || fd < 0 || fd >= PROCESS_FD_MAX) { + return -EINVAL; + } + + if (!proc->fds[fd].used) { + return -EBADF; + } + + *drive_id = proc->fds[fd].drive_id; + *fs_fd = proc->fds[fd].fs_fd; + return 0; +} + +int process_fd_release(struct process* proc, int fd) +{ + if (!proc || fd < 0 || fd >= PROCESS_FD_MAX) { + return -EINVAL; + } + + if (!proc->fds[fd].used) { + return -EBADF; + } + + proc->fds[fd].used = false; + proc->fds[fd].drive_id = 0; + proc->fds[fd].fs_fd = -1; + return 0; +} + /* * process_init - Initializes process list */ @@ -100,6 +158,8 @@ struct process* process_create(char* name, void(*function)(void*), void* arg) proc->kernel_stack = kalloc_stack(); + process_fd_init(proc); + proc->next = 0; process_add(&processes_list, proc); @@ -255,6 +315,7 @@ void process_create_user(struct limine_file* file, char* name) proc->context->iret_ss = USER_DATA_SEGMENT | 3; proc->context->iret_cs = USER_CODE_SEGMENT | 3; proc->context->iret_flags = 0x202; // Interrupt Flag set + process_fd_init(proc); void* exec_addr = file->address; uint64_t exec_size = file->size;