Compare commits

..

2 Commits

Author SHA1 Message Date
b54a7f3a80
kernel+libc: Add O_* flags and parse them in open()
All checks were successful
continuous-integration/drone/push Build is passing
O_RDONLY, O_WRONLY, O_RDWR, O_TRUNC, O_CREAT and O_EXCL are fully implemented.

O_APPEND is partially implemented.

Other flags are not here yet.
2023-03-12 14:43:58 +01:00
bd572473ad
kernel: Remove FileDescriptorTable and add a helper to resolve fds to FileDescriptors 2023-03-12 13:57:38 +01:00
13 changed files with 121 additions and 33 deletions

View File

@ -14,7 +14,7 @@ int main()
atexit(bye); atexit(bye);
printf("Welcome to %s from userspace (pid %d)!\n", "Luna", getpid()); printf("Welcome to %s from userspace (pid %d)!\n", "Luna", getpid());
int fd = open("/etc/motd", 0); int fd = open("/etc/motd", O_RDONLY);
if (fd < 0) if (fd < 0)
{ {
perror("open"); perror("open");

View File

@ -26,6 +26,8 @@ namespace VFS
virtual Result<usize> write(const u8* buf, usize offset, usize length) = 0; virtual Result<usize> write(const u8* buf, usize offset, usize length) = 0;
virtual Result<void> truncate(usize size) = 0;
// Generic methods // Generic methods
virtual FileSystem& fs() const = 0; virtual FileSystem& fs() const = 0;

View File

@ -118,4 +118,15 @@ namespace TmpFS
return length; return length;
} }
Result<void> FileInode::truncate(usize size)
{
usize old_size = m_data_buffer.size();
TRY(m_data_buffer.try_resize(size));
if (size > old_size) memset(m_data_buffer.data() + old_size, 0, size - old_size);
return {};
}
} }

View File

@ -63,6 +63,8 @@ namespace TmpFS
Result<usize> write(const u8*, usize, usize) override; Result<usize> write(const u8*, usize, usize) override;
Result<void> truncate(usize size) override;
virtual ~FileInode() = default; virtual ~FileInode() = default;
private: private:
@ -103,6 +105,11 @@ namespace TmpFS
return err(EISDIR); return err(EISDIR);
} }
Result<void> truncate(usize) override
{
return err(EISDIR);
}
VFS::FileSystem& fs() const override VFS::FileSystem& fs() const override
{ {
return *m_fs; return *m_fs;

View File

@ -15,17 +15,15 @@ Result<u64> sys_read(Registers*, SyscallArgs args)
if (!MemoryManager::validate_user_write(buf, size)) return err(EFAULT); if (!MemoryManager::validate_user_write(buf, size)) return err(EFAULT);
if (fd < 0 || fd >= FD_MAX) return err(EBADF);
Thread* current = Scheduler::current(); Thread* current = Scheduler::current();
Option<FileDescriptor>& descriptor = current->fd_table->fds[fd]; auto& descriptor = *TRY(current->resolve_fd(fd));
if (!descriptor.has_value()) return err(EBADF); if (!descriptor.is_readable()) return err(EBADF);
usize nread = TRY(descriptor->inode->read(buf, descriptor->offset, size)); usize nread = TRY(descriptor.inode->read(buf, descriptor.offset, size));
descriptor->offset += nread; descriptor.offset += nread;
return nread; return nread;
} }
@ -38,17 +36,17 @@ Result<u64> sys_write(Registers*, SyscallArgs args)
if (!MemoryManager::validate_user_read(buf, size)) return err(EFAULT); if (!MemoryManager::validate_user_read(buf, size)) return err(EFAULT);
if (fd < 0 || fd >= FD_MAX) return err(EBADF);
Thread* current = Scheduler::current(); Thread* current = Scheduler::current();
Option<FileDescriptor>& descriptor = current->fd_table->fds[fd]; auto& descriptor = *TRY(current->resolve_fd(fd));
if (!descriptor.has_value()) return err(EBADF); if (!descriptor.is_writable()) return err(EBADF);
usize nwritten = TRY(descriptor->inode->write(buf, descriptor->offset, size)); if (descriptor.should_append()) todo();
descriptor->offset += nwritten; usize nwritten = TRY(descriptor.inode->write(buf, descriptor.offset, size));
descriptor.offset += nwritten;
return nwritten; return nwritten;
} }
@ -59,27 +57,23 @@ Result<u64> sys_lseek(Registers*, SyscallArgs args)
off_t offset = (long)args[1]; off_t offset = (long)args[1];
int whence = (int)args[2]; int whence = (int)args[2];
if (fd < 0 || fd >= FD_MAX) return err(EBADF);
Thread* current = Scheduler::current(); Thread* current = Scheduler::current();
Option<FileDescriptor>& descriptor = current->fd_table->fds[fd]; auto& descriptor = *TRY(current->resolve_fd(fd));
if (!descriptor.has_value()) return err(EBADF);
off_t new_offset; off_t new_offset;
switch (whence) switch (whence)
{ {
case SEEK_SET: new_offset = offset; break; case SEEK_SET: new_offset = offset; break;
case SEEK_CUR: new_offset = TRY(safe_add((long)descriptor->offset, offset)); break; case SEEK_CUR: new_offset = TRY(safe_add((long)descriptor.offset, offset)); break;
case SEEK_END: todo(); case SEEK_END: todo();
default: return err(EINVAL); default: return err(EINVAL);
} }
if (new_offset < 0) return err(EINVAL); if (new_offset < 0) return err(EINVAL);
descriptor->offset = (usize)new_offset; descriptor.offset = (usize)new_offset;
return (u64)new_offset; return (u64)new_offset;
} }

View File

@ -3,6 +3,10 @@
#include "memory/MemoryManager.h" #include "memory/MemoryManager.h"
#include "sys/Syscall.h" #include "sys/Syscall.h"
#include "thread/Scheduler.h" #include "thread/Scheduler.h"
#include <bits/open-flags.h>
// These flags are needed after open(), the rest only affect open().
constexpr int FLAGS_TO_KEEP = O_RDWR | O_APPEND;
Result<u64> sys_open(Registers*, SyscallArgs args) Result<u64> sys_open(Registers*, SyscallArgs args)
{ {
@ -16,11 +20,28 @@ Result<u64> sys_open(Registers*, SyscallArgs args)
kinfoln("open: trying to open file %s, flags %d", path, flags); kinfoln("open: trying to open file %s, flags %d", path, flags);
auto inode = TRY(VFS::resolve_path(path)); SharedPtr<VFS::Inode> inode;
// Caller did not pass either O_RDONLY, O_WRONLY or O_RDWR
if ((flags & O_RDWR) == 0) { return err(EINVAL); }
auto maybe_inode = VFS::resolve_path(path);
if (maybe_inode.has_error())
{
if (maybe_inode.error() == ENOENT && (flags & O_CREAT)) inode = TRY(VFS::create_file(path));
else
return maybe_inode.release_error();
}
else if (flags & O_EXCL)
return err(EEXIST);
else
inode = maybe_inode.release_value();
if ((flags & O_WRONLY) && (flags & O_TRUNC)) inode->truncate(0);
int fd = TRY(current->allocate_fd(0)); int fd = TRY(current->allocate_fd(0));
current->fd_table->fds[fd] = FileDescriptor { inode, 0, flags }; current->fd_table[fd] = FileDescriptor { inode, 0, flags & FLAGS_TO_KEEP };
kinfoln("open: allocated file descriptor %d for inode %zu", fd, inode->inode_number()); kinfoln("open: allocated file descriptor %d for inode %zu", fd, inode->inode_number());
@ -34,7 +55,7 @@ Result<u64> sys_close(Registers*, SyscallArgs args)
Thread* current = Scheduler::current(); Thread* current = Scheduler::current();
Option<FileDescriptor>& descriptor = current->fd_table->fds[fd]; Option<FileDescriptor>& descriptor = current->fd_table[fd];
if (!descriptor.has_value()) return err(EBADF); if (!descriptor.has_value()) return err(EBADF);

View File

@ -151,8 +151,6 @@ namespace Scheduler
guard.deactivate(); guard.deactivate();
directory_guard.deactivate(); directory_guard.deactivate();
thread->fd_table = FileDescriptorTable {};
kinfoln("Created userspace thread: id %lu with ip %#.16lx and sp %#.16lx (ksp %#lx)", thread->id, thread->ip(), kinfoln("Created userspace thread: id %lu with ip %#.16lx and sp %#.16lx (ksp %#lx)", thread->id, thread->ip(),
thread->sp(), thread->kernel_stack.top()); thread->sp(), thread->kernel_stack.top());

View File

@ -1,4 +1,5 @@
#include "thread/Thread.h" #include "thread/Thread.h"
#include <bits/open-flags.h>
#include <luna/Alloc.h> #include <luna/Alloc.h>
#include <luna/Atomic.h> #include <luna/Atomic.h>
@ -27,8 +28,34 @@ Result<int> Thread::allocate_fd(int min)
{ {
// FIXME: Possible race condition if multiple threads share a FileDescriptorTable? Let's not worry about it for // FIXME: Possible race condition if multiple threads share a FileDescriptorTable? Let's not worry about it for
// now, we're still a long way away from reaching that point. // now, we're still a long way away from reaching that point.
if (!fd_table->fds[i].has_value()) { return i; } if (!fd_table[i].has_value()) { return i; }
} }
return err(EMFILE); return err(EMFILE);
} }
Result<FileDescriptor*> Thread::resolve_fd(int fd)
{
if (fd < 0 || fd >= FD_MAX) return err(EBADF);
Option<FileDescriptor>& maybe_descriptor = fd_table[fd];
if (!maybe_descriptor.has_value()) return err(EBADF);
return maybe_descriptor.value_ptr();
}
bool FileDescriptor::should_append()
{
return flags & O_APPEND;
}
bool FileDescriptor::is_readable()
{
return flags & O_RDONLY;
}
bool FileDescriptor::is_writable()
{
return flags & O_WRONLY;
}

View File

@ -27,15 +27,14 @@ struct FileDescriptor
SharedPtr<VFS::Inode> inode; SharedPtr<VFS::Inode> inode;
usize offset { 0 }; usize offset { 0 };
int flags { 0 }; int flags { 0 };
bool should_append();
bool is_writable();
bool is_readable();
}; };
static constexpr int FD_MAX = 64; static constexpr int FD_MAX = 64;
struct FileDescriptorTable
{
Option<FileDescriptor> fds[FD_MAX] = {};
};
struct Thread : public LinkedListNode<Thread> struct Thread : public LinkedListNode<Thread>
{ {
Registers regs; Registers regs;
@ -53,9 +52,10 @@ struct Thread : public LinkedListNode<Thread>
Stack kernel_stack; Stack kernel_stack;
OwnedPtr<UserVM> vm_allocator; OwnedPtr<UserVM> vm_allocator;
Option<FileDescriptorTable> fd_table; Option<FileDescriptor> fd_table[FD_MAX] = {};
Result<int> allocate_fd(int min); Result<int> allocate_fd(int min);
Result<FileDescriptor*> resolve_fd(int fd);
FPData fp_data; FPData fp_data;

View File

@ -0,0 +1,14 @@
/* bits/open-flags.h: O_* constants for open(). */
#ifndef _BITS_OPEN_FLAGS_H
#define _BITS_OPEN_FLAGS_H
#define O_RDONLY 1
#define O_WRONLY 2
#define O_RDWR 3
#define O_APPEND 4
#define O_CREAT 8
#define O_EXCL 16
#define O_TRUNC 32
#endif

View File

@ -3,6 +3,8 @@
#ifndef _FCNTL_H #ifndef _FCNTL_H
#define _FCNTL_H #define _FCNTL_H
#include <bits/open-flags.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
{ {

View File

@ -21,7 +21,7 @@ extern "C"
FILE* fopen(const char* path, const char*) FILE* fopen(const char* path, const char*)
{ {
// FIXME: Parse the mode string. // FIXME: Parse the mode string.
int fd = open(path, 0); int fd = open(path, O_RDWR);
if (fd < 0) return nullptr; if (fd < 0) return nullptr;
FILE* f = (FILE*)malloc(sizeof(FILE)); FILE* f = (FILE*)malloc(sizeof(FILE));

View File

@ -116,6 +116,18 @@ template <typename T> class Option
return &m_storage.fetch_reference(); return &m_storage.fetch_reference();
} }
T& operator*()
{
expect(has_value(), "Option::operator* called on an empty Option");
return m_storage.fetch_reference();
}
T* value_ptr(SourceLocation caller = SourceLocation::current())
{
expect_at(has_value(), caller, "Option::value_ptr called on an empty Option");
return &m_storage.fetch_reference();
}
~Option() ~Option()
{ {
if (has_value()) m_storage.destroy(); if (has_value()) m_storage.destroy();