Kernel, libc, userspace: Implement file descriptors
Kernel: Implement a descriptor struct which stores the opened node and read offset, and give each task 8 of those. Implement three syscalls: sys_read, sys_open and sys_close (sys_write still writes to the console instead of using a fd, for now) Implement three new errors: ENOENT, EBADF and EMFILE. libc: Implement the new errors, and the new syscalls in syscall(). Also fix _RETURN_WITH_ERRNO() to set errno correctly, which was making strerror() return null, thus crashing perror(). userspace: make init demonstrate the new file API.
This commit is contained in:
parent
63b2de4e3c
commit
da2ede3450
@ -5,6 +5,8 @@
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
typedef long ssize_t;
|
||||
|
||||
int main()
|
||||
{
|
||||
if (gettid() == 0) // why are we the idle task?
|
||||
@ -25,14 +27,42 @@ int main()
|
||||
|
||||
sleep(2);
|
||||
|
||||
const char* filename = "/sys/config";
|
||||
|
||||
printf("Opening %s for reading...\n", filename);
|
||||
|
||||
int fd = syscall(SYS_open, filename, 1);
|
||||
if (fd < 0)
|
||||
{
|
||||
char* variable = malloc(200);
|
||||
*variable = 3;
|
||||
printf("Allocated variable at address %p\n", variable);
|
||||
free(variable);
|
||||
perror("open");
|
||||
// return 1;
|
||||
}
|
||||
else { printf("Got fd %d\n", fd); }
|
||||
|
||||
char buf[4096];
|
||||
|
||||
ssize_t nread = syscall(SYS_read, fd, sizeof(buf), buf);
|
||||
if (nread < 0)
|
||||
{
|
||||
perror("read");
|
||||
// return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
buf[nread] = 0;
|
||||
|
||||
printf("Read %zd bytes\n\n", nread);
|
||||
|
||||
printf("%s", buf);
|
||||
}
|
||||
|
||||
printf("Press any key to restart.\n");
|
||||
if (syscall(SYS_close, fd) < 0)
|
||||
{
|
||||
perror("close");
|
||||
// return 1;
|
||||
}
|
||||
|
||||
printf("\n\nPress any key to restart.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#define EPERM 1
|
||||
#define ENOENT 2
|
||||
#define EBADF 9
|
||||
#define ENOMEM 12
|
||||
#define EINVAL 22
|
||||
#define EMFILE 24
|
||||
#define ENOSYS 38
|
@ -27,9 +27,8 @@ struct Descriptor
|
||||
Descriptor();
|
||||
|
||||
private:
|
||||
uint64_t m_offset;
|
||||
VFS::Node* m_node;
|
||||
bool m_can_read;
|
||||
|
||||
bool m_is_open;
|
||||
}
|
||||
bool m_can_read;
|
||||
VFS::Node* m_node;
|
||||
uint64_t m_offset;
|
||||
};
|
@ -12,6 +12,9 @@
|
||||
#define SYS_gettid 7
|
||||
#define SYS_mmap 8
|
||||
#define SYS_munmap 9
|
||||
#define SYS_open 10
|
||||
#define SYS_read 11
|
||||
#define SYS_close 12
|
||||
|
||||
namespace Syscall
|
||||
{
|
||||
@ -28,3 +31,6 @@ void sys_getversion(Context* context, char* buffer, size_t max);
|
||||
void sys_gettid(Context* context);
|
||||
void sys_mmap(Context* context, void* address, size_t size, int flags);
|
||||
void sys_munmap(Context* context, void* address, size_t size);
|
||||
void sys_open(Context* context, const char* filename, int flags);
|
||||
void sys_read(Context* context, int fd, size_t size, char* buffer);
|
||||
void sys_close(Context* context, int fd);
|
@ -1,7 +1,10 @@
|
||||
#pragma once
|
||||
#include "fs/FileDescriptor.h"
|
||||
#include "interrupts/Context.h"
|
||||
#include "sys/elf/Image.h"
|
||||
|
||||
#define TASK_MAX_FDS 8
|
||||
|
||||
struct Task
|
||||
{
|
||||
enum TaskState
|
||||
@ -36,6 +39,8 @@ struct Task
|
||||
bool is_user_task();
|
||||
|
||||
ELFImage* image = nullptr;
|
||||
|
||||
Descriptor files[TASK_MAX_FDS];
|
||||
};
|
||||
|
||||
void set_context_from_task(Task& task, Context* ctx);
|
||||
|
@ -5,7 +5,7 @@ Descriptor::Descriptor() : m_is_open(false)
|
||||
}
|
||||
|
||||
Descriptor::Descriptor(const Descriptor& other)
|
||||
: m_is_open(other.is_open()), m_can_read(other.can_read()), m_node(other.m_node), m_offset(other.m_offset)
|
||||
: m_is_open(other.m_is_open), m_can_read(other.m_can_read), m_node(other.m_node), m_offset(other.m_offset)
|
||||
{
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ void Descriptor::open(VFS::Node* node, bool can_read)
|
||||
m_can_read = can_read;
|
||||
m_node = node;
|
||||
m_offset = 0;
|
||||
m_is_open = true;
|
||||
}
|
||||
|
||||
ssize_t Descriptor::read(size_t size, char* buffer)
|
||||
|
@ -26,6 +26,9 @@ void Syscall::entry(Context* context)
|
||||
case SYS_gettid: sys_gettid(context); break;
|
||||
case SYS_mmap: sys_mmap(context, (void*)context->rdi, context->rsi, (int)context->rdx); break;
|
||||
case SYS_munmap: sys_munmap(context, (void*)context->rdi, context->rsi); break;
|
||||
case SYS_open: sys_open(context, (const char*)context->rdi, (int)context->rsi); break;
|
||||
case SYS_read: sys_read(context, (int)context->rdi, context->rsi, (char*)context->rdx); break;
|
||||
case SYS_close: sys_close(context, (int)context->rdi); break;
|
||||
default: context->rax = -ENOSYS; break;
|
||||
}
|
||||
}
|
@ -1,9 +1,108 @@
|
||||
#define MODULE "stdio"
|
||||
|
||||
#include "errno.h"
|
||||
#include "interrupts/Context.h"
|
||||
#include "io/Serial.h"
|
||||
#include "log/Log.h"
|
||||
#include "render/TextRenderer.h"
|
||||
#include "thread/Scheduler.h"
|
||||
#include "thread/Task.h"
|
||||
|
||||
#define STDIO_FAIL(function, error) kwarnln("%s failed with %s", #function, #error)
|
||||
|
||||
void sys_write(Context* context, const char* addr, size_t size)
|
||||
{
|
||||
context->rax = size;
|
||||
TextRenderer::write(addr, size);
|
||||
}
|
||||
|
||||
void sys_open(Context* context, const char* filename, int flags)
|
||||
{
|
||||
Task* current_task = Scheduler::current_task();
|
||||
int fd = -1;
|
||||
for (fd = 0; fd < TASK_MAX_FDS; fd++)
|
||||
{
|
||||
if (!current_task->files[fd].is_open()) break;
|
||||
}
|
||||
if (fd == -1)
|
||||
{
|
||||
STDIO_FAIL(open, EMFILE);
|
||||
context->rax = -EMFILE;
|
||||
return;
|
||||
}
|
||||
VFS::Node* node = VFS::resolve_path(filename);
|
||||
if (!node)
|
||||
{
|
||||
STDIO_FAIL(open, ENOENT);
|
||||
context->rax = -ENOENT;
|
||||
return;
|
||||
}
|
||||
current_task->files[fd].open(node,
|
||||
(bool)flags); // FIXME: Implement more flags. (right now, 1 is can_read, 0 is not)
|
||||
context->rax = fd;
|
||||
return;
|
||||
}
|
||||
|
||||
void sys_read(Context* context, int fd, size_t size, char* buffer)
|
||||
{
|
||||
if (!buffer)
|
||||
{
|
||||
STDIO_FAIL(read, EINVAL);
|
||||
context->rax = -EINVAL;
|
||||
return;
|
||||
}
|
||||
if (fd >= TASK_MAX_FDS)
|
||||
{
|
||||
STDIO_FAIL(read, EBADF);
|
||||
context->rax = -EBADF;
|
||||
return;
|
||||
}
|
||||
if (fd < 0)
|
||||
{
|
||||
STDIO_FAIL(read, EBADF);
|
||||
context->rax = -EBADF;
|
||||
return;
|
||||
}
|
||||
Task* current_task = Scheduler::current_task();
|
||||
if (!current_task->files[fd].is_open())
|
||||
{
|
||||
STDIO_FAIL(read, EBADF);
|
||||
context->rax = -EBADF;
|
||||
return;
|
||||
}
|
||||
if (!current_task->files[fd].can_read())
|
||||
{
|
||||
STDIO_FAIL(read, EBADF);
|
||||
context->rax = -EBADF;
|
||||
return;
|
||||
}
|
||||
ssize_t result = current_task->files[fd].read(size, buffer);
|
||||
context->rax = (size_t)result;
|
||||
return;
|
||||
}
|
||||
|
||||
void sys_close(Context* context, int fd)
|
||||
{
|
||||
if (fd >= TASK_MAX_FDS)
|
||||
{
|
||||
STDIO_FAIL(close, EBADF);
|
||||
context->rax = -EBADF;
|
||||
return;
|
||||
}
|
||||
if (fd < 0)
|
||||
{
|
||||
STDIO_FAIL(close, EBADF);
|
||||
context->rax = -EBADF;
|
||||
return;
|
||||
}
|
||||
Task* current_task = Scheduler::current_task();
|
||||
if (!current_task->files[fd].is_open())
|
||||
{
|
||||
STDIO_FAIL(close, EBADF);
|
||||
context->rax = -EBADF;
|
||||
return;
|
||||
}
|
||||
current_task->files[fd].close();
|
||||
context->rax = 0;
|
||||
return;
|
||||
}
|
@ -21,7 +21,7 @@ typedef struct stackframe
|
||||
void StackTracer::trace()
|
||||
{
|
||||
stackframe* frame = (stackframe*)m_base_pointer;
|
||||
while (Memory::is_kernel_address((uintptr_t)frame))
|
||||
while (frame)
|
||||
{
|
||||
char symbol_name[512];
|
||||
get_symbol_name(frame->instruction, symbol_name);
|
||||
@ -32,11 +32,6 @@ void StackTracer::trace()
|
||||
|
||||
void StackTracer::trace_with_ip(uintptr_t ip)
|
||||
{
|
||||
if (!Memory::is_kernel_address(ip))
|
||||
{
|
||||
printf("(user stack)");
|
||||
return;
|
||||
}
|
||||
char symbol_name[512];
|
||||
get_symbol_name(ip, symbol_name);
|
||||
printf("%lx: %s\n", ip, symbol_name);
|
||||
|
@ -7,7 +7,7 @@
|
||||
do { \
|
||||
if (rc < 0) \
|
||||
{ \
|
||||
errno = (int)rc; \
|
||||
errno = (int)(-rc); \
|
||||
return -1; \
|
||||
} \
|
||||
return (type)rc; \
|
||||
|
@ -4,8 +4,11 @@
|
||||
extern int errno;
|
||||
|
||||
#define EPERM 1
|
||||
#define ENOENT 2
|
||||
#define EBADF 9
|
||||
#define ENOMEM 12
|
||||
#define EINVAL 22
|
||||
#define EMFILE 24
|
||||
#define ENOSYS 38
|
||||
|
||||
#endif
|
@ -11,6 +11,9 @@
|
||||
#define SYS_gettid 7
|
||||
#define SYS_mmap 8
|
||||
#define SYS_munmap 9
|
||||
#define SYS_open 10
|
||||
#define SYS_read 11
|
||||
#define SYS_close 12
|
||||
|
||||
#ifndef __want_syscalls
|
||||
#ifdef __cplusplus
|
||||
|
@ -121,7 +121,11 @@ extern "C"
|
||||
case EINVAL: return "Invalid argument";
|
||||
case ENOMEM: return "Out of memory";
|
||||
case ENOSYS: return "Function not implemented";
|
||||
default: return 0;
|
||||
case ENOENT: return "No such file or directory";
|
||||
case EBADF: return "Bad file descriptor";
|
||||
case EMFILE: return "Too many open files";
|
||||
case 0: return "Success";
|
||||
default: return (char*)(unsigned long int)errnum;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,15 +35,18 @@ extern "C"
|
||||
case SYS_gettid:
|
||||
case SYS_rand: result = __luna_syscall0(number); break;
|
||||
case SYS_exit:
|
||||
case SYS_close:
|
||||
case SYS_sleep: result = __luna_syscall1(number, va_arg(ap, arg)); break;
|
||||
case SYS_write:
|
||||
case SYS_munmap:
|
||||
case SYS_open:
|
||||
case SYS_getversion: {
|
||||
arg arg0 = va_arg(ap, arg);
|
||||
arg arg1 = va_arg(ap, arg);
|
||||
result = __luna_syscall2(number, arg0, arg1);
|
||||
break;
|
||||
}
|
||||
case SYS_read:
|
||||
case SYS_mmap: {
|
||||
arg arg0 = va_arg(ap, arg);
|
||||
arg arg1 = va_arg(ap, arg);
|
||||
|
Loading…
Reference in New Issue
Block a user