#define MODULE "stdio" #include "interrupts/Context.h" #include "io/Serial.h" #include "log/Log.h" #include "memory/VMM.h" #include "render/TextRenderer.h" #include "std/errno.h" #include "std/stdlib.h" #include "sys/Syscall.h" #include "sys/UserMemory.h" #include "thread/Scheduler.h" #include "thread/Task.h" #include #define OPEN_READ 1 #define OPEN_WRITE 2 #define OPEN_NONBLOCK 4 #define OPEN_CLOEXEC 8 #define OPEN_DIRECTORY 16 #define OPEN_TRUNCATED 32 #define OPEN_CREATE 64 #define OPEN_APPEND 128 #define OPEN_EXCL 256 #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 #define FCNTL_DUPFD 0 #define FCNTL_ISTTY 1 #define FCNTL_GETFD 2 #define FCNTL_SETFD 3 #define FD_CLOEXEC 1 void sys_fcntl(Context* context, int fd, int command, uintptr_t arg) { Task* current_task = Scheduler::current_task(); int err; Descriptor* file = current_task->descriptor_from_fd(fd, err); if (!file) { context->rax = -err; return; } if (command == FCNTL_DUPFD) { int minfd = (int)arg; if (minfd < 0 || minfd >= TASK_MAX_FDS) { context->rax = -EINVAL; return; } int dupfd = current_task->alloc_fd_greater_than_or_equal(minfd); if (dupfd < 0) { context->rax = -EMFILE; return; } current_task->files[dupfd] = *file; context->rax = dupfd; kdbgln("fcntl(F_DUPFD): duplicated fd %d, result is %d", fd, dupfd); return; } else if (command == FCNTL_ISTTY) { VFS::Node* node = file->node(); if (node->tty) { context->rax = 1; } else context->rax = -ENOTTY; return; } else if (command == FCNTL_GETFD) { int flags = 0; if (file->close_on_exec()) context->rax |= FD_CLOEXEC; context->rax = flags; return; } else if (command == FCNTL_SETFD) { int flags = (int)arg; if (flags & FD_CLOEXEC) file->set_close_on_exec(true); else file->set_close_on_exec(false); context->rax = 0; return; } else { context->rax = -EINVAL; return; } } void sys_seek(Context* context, int fd, long offset, int whence) { if (whence < SEEK_SET || whence > SEEK_END) { context->rax = -EINVAL; return; } int err; Descriptor* file = Scheduler::current_task()->descriptor_from_fd(fd, err); if (!file) { context->rax = -err; return; } long new_offset; if (whence == SEEK_SET) new_offset = offset; else if (whence == SEEK_CUR) new_offset = offset + file->offset(); else if (whence == SEEK_END) new_offset = file->length() + offset; else __builtin_unreachable(); if (new_offset < 0) { context->rax = -EINVAL; // FIXME: Is this the right error? return; } if (new_offset == file->offset()) { context->rax = new_offset; return; } int result = file->seek(new_offset); if (result < 0) { context->rax = result; return; } context->rax = new_offset; return; } void sys_write(Context* context, int fd, size_t size, const char* addr) { if (!addr) { context->rax = -EFAULT; return; } int err; Descriptor* file = Scheduler::current_task()->descriptor_from_fd(fd, err); if (!file) { context->rax = -err; return; } if (!file->can_write()) { context->rax = -EBADF; return; } ssize_t result = file->write(size, (const char*)VMM::get_physical((uint64_t)addr)); context->rax = (size_t)result; return; } void sys_open(Context* context, const char* filename, int flags, mode_t) // FIXME: mode is not used. { Task* current_task = Scheduler::current_task(); int fd = current_task->alloc_fd(); if (fd < 0) { context->rax = -EMFILE; return; } char* kfilename = strdup_from_user(filename); if (!kfilename) { context->rax = -EFAULT; return; } VFS::Node* node = VFS::resolve_path(kfilename); if (!node) { bool create = (flags & OPEN_CREATE) > 0; if (create) kwarnln("FIXME: open(O_CREAT) is not implemented"); kfree(kfilename); context->rax = -ENOENT; return; } else { bool excl = (flags & OPEN_EXCL) > 0; if (excl) { kfree(kfilename); context->rax = -EEXIST; return; } } bool can_read = (flags & OPEN_READ) > 0; bool can_write = (flags & OPEN_WRITE) > 0; if (!can_read && !can_write) { kfree(kfilename); context->rax = -EINVAL; return; } if (can_read && !VFS::can_read(node, current_task->euid, current_task->egid)) { kwarnln("open failed because process with uid %d and gid %d couldn't open file %s with mode %d for reading", current_task->euid, current_task->egid, kfilename, node->mode); kfree(kfilename); context->rax = -EACCES; return; } if (can_write && !VFS::can_write(node, current_task->euid, current_task->egid)) { kwarnln("open failed because process with uid %d and gid %d couldn't open file %s with mode %d for writing", current_task->euid, current_task->egid, kfilename, node->mode); kfree(kfilename); context->rax = -EACCES; return; } bool able_to_block = (flags & OPEN_NONBLOCK) == 0; bool close_on_exec = (flags & OPEN_CLOEXEC) > 0; bool only_directory = (flags & OPEN_DIRECTORY) > 0; bool truncate = (flags & OPEN_TRUNCATED) > 0; if (truncate) { kfree(kfilename); kerrorln("FIXME: open(O_TRUNC) is not implemented"); context->rax = -ENOTSUP; return; } bool append = (flags & OPEN_APPEND) > 0; if (only_directory && node->type != VFS_DIRECTORY) { kfree(kfilename); context->rax = -ENOTDIR; return; } kdbgln("open(): opening %s %s, allocated file descriptor %d", kfilename, (can_read && can_write) ? "rw" : can_read ? "r-" : "-w", fd); kfree(kfilename); current_task->files[fd].open(node, can_read, can_write, able_to_block, close_on_exec); if (append && current_task->files[fd].node()->type != VFS_DEVICE) { current_task->files[fd].seek((long)current_task->files[fd].length()); } context->rax = fd; return; } void sys_read(Context* context, int fd, size_t size, char* buffer) { if (!buffer) { context->rax = -EFAULT; return; } int err; Descriptor* file = Scheduler::current_task()->descriptor_from_fd(fd, err); if (!file) { context->rax = -err; return; } if (!file->is_open() || !file->can_read()) { context->rax = -EBADF; return; } if (VFS::would_block(file->node())) { if (!file->able_to_block()) { context->rax = -EAGAIN; return; } Task* current_task = Scheduler::current_task(); current_task->state = current_task->Blocking; current_task->block_reason = BlockReason::Reading; current_task->blocking_read_info.fd = fd; current_task->blocking_read_info.buf = (char*)VMM::get_physical((uint64_t)buffer); // FIXME: Handle errors. current_task->blocking_read_info.size = size; return Scheduler::task_yield(context); } ssize_t result = file->read(size, (char*)VMM::get_physical((uint64_t)buffer)); // FIXME: Handle errors, and big buffers which may // not be across continuous physical pages. context->rax = (size_t)result; return; } void sys_close(Context* context, int fd) { int err; Descriptor* file = Scheduler::current_task()->descriptor_from_fd(fd, err); if (!file) { context->rax = -err; return; } kdbgln("close(): releasing file descriptor %d", fd); file->close(); context->rax = 0; return; } void sys_mkdir(Context* context, const char* filename, mode_t mode) { char* kfilename = strdup_from_user(filename); if (!kfilename) { context->rax = -EFAULT; return; } Task* current_task = Scheduler::current_task(); int rc = VFS::do_mkdir(kfilename, current_task->euid, current_task->egid, mode); kfree(kfilename); context->rax = rc; } void sys_access(Context* context, const char* path, int) // FIXME: Use the amode argument. { char* kpath = strdup_from_user(path); if (!VFS::exists(kpath)) { context->rax = -ENOENT; } else context->rax = 0; kfree(kpath); } void sys_dup2(Context* context, int fd, int fd2) { int err; Descriptor* file1 = Scheduler::current_task()->descriptor_from_fd(fd, err); if (!file1) { context->rax = -err; return; } if (!file1->is_open()) { context->rax = -EBADF; return; } Descriptor* file2 = Scheduler::current_task()->descriptor_from_fd(fd2, err); if (!file2) { context->rax = -err; return; } if (file2->is_open()) file2->close(); *file2 = *file1; kinfoln("dup2(): overwrote fd %d with fd %d", fd2, fd); context->rax = fd2; }