From b15deb420f773a40bdbbb03949c0377925e7603d Mon Sep 17 00:00:00 2001 From: Bananymous Date: Sun, 23 Apr 2023 14:32:37 +0300 Subject: [PATCH] LibC: Write mostly functioning stdio --- kernel/include/kernel/Process.h | 3 + kernel/include/kernel/Syscall.h | 4 + kernel/kernel/Process.cpp | 17 ++ kernel/kernel/Syscall.cpp | 49 +++- libc/CMakeLists.txt | 1 + libc/fcntl.cpp | 15 + libc/include/errno.h | 3 + libc/include/fcntl.h | 10 + libc/include/stdio.h | 83 +++++- libc/stdio.cpp | 486 +++++++++++++++++++++++++++++--- libc/stdlib.cpp | 2 + libc/string.cpp | 3 + userspace/test.c | 3 +- 13 files changed, 619 insertions(+), 60 deletions(-) create mode 100644 libc/fcntl.cpp diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index f6584669..3a7673bc 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -43,6 +43,7 @@ namespace Kernel BAN::ErrorOr read(int, void*, size_t); BAN::ErrorOr write(int, const void*, size_t); BAN::ErrorOr creat(BAN::StringView, mode_t); + BAN::ErrorOr seek(int, size_t); BAN::ErrorOr fstat(int, stat*); BAN::ErrorOr stat(BAN::StringView, stat*); @@ -54,6 +55,8 @@ namespace Kernel BAN::ErrorOr working_directory() const; BAN::ErrorOr set_working_directory(BAN::StringView); + void termid(char*) const; + static Process& current() { return Thread::current().process(); } MMU& mmu() { return m_mmu ? *m_mmu : MMU::get(); } diff --git a/kernel/include/kernel/Syscall.h b/kernel/include/kernel/Syscall.h index aa64d9dd..6a2ec411 100644 --- a/kernel/include/kernel/Syscall.h +++ b/kernel/include/kernel/Syscall.h @@ -3,6 +3,10 @@ #define SYS_EXIT 1 #define SYS_READ 2 #define SYS_WRITE 3 +#define SYS_TERMID 4 +#define SYS_CLOSE 5 +#define SYS_SEEK 6 +#define SYS_OPEN 7 #include diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index 4ffa19a1..0ef607ee 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -304,6 +304,15 @@ namespace Kernel return ret; } + BAN::ErrorOr Process::seek(int fd, size_t offset) + { + LockGuard _(m_lock); + TRY(validate_fd(fd)); + auto& open_fd = open_file_description(fd); + open_fd.offset = offset; + return {}; + } + BAN::ErrorOr> Process::read_directory_entries(int fd) { OpenFileDescription open_fd_copy; @@ -349,6 +358,14 @@ namespace Kernel return {}; } + void Process::termid(char* buffer) const + { + if (m_tty == nullptr) + buffer[0] = '\0'; + strcpy(buffer, "/dev/"); + strcpy(buffer + 5, m_tty->name().data()); + } + BAN::ErrorOr Process::absolute_path_of(BAN::StringView path) const { if (path.empty()) diff --git a/kernel/kernel/Syscall.cpp b/kernel/kernel/Syscall.cpp index efc8bfaf..4aca3a38 100644 --- a/kernel/kernel/Syscall.cpp +++ b/kernel/kernel/Syscall.cpp @@ -14,7 +14,7 @@ namespace Kernel { auto res = Process::current().read(fd, buffer, size); if (res.is_error()) - return res.error().get_error_code(); + return -res.error().get_error_code(); return res.value(); } @@ -22,7 +22,36 @@ namespace Kernel { auto res = Process::current().write(fd, buffer, size); if (res.is_error()) - return res.error().get_error_code(); + return -res.error().get_error_code(); + return res.value(); + } + + int sys_close(int fd) + { + auto res = Process::current().close(fd); + if (res.is_error()) + return -res.error().get_error_code(); + return 0; + } + + int sys_seek(int fd, long offset) + { + auto res = Process::current().seek(fd, offset); + if (res.is_error()) + return -res.error().get_error_code(); + return 0; + } + + void sys_termid(char* buffer) + { + Process::current().termid(buffer); + } + + int sys_open(const char* path, int oflags) + { + auto res = Process::current().open(path, oflags); + if (res.is_error()) + return -res.error().get_error_code(); return res.value(); } @@ -44,10 +73,20 @@ namespace Kernel case SYS_WRITE: ret = sys_write((int)(uintptr_t)arg1, arg2, (size_t)(uintptr_t)arg3); break; - default: - ret = -1; - dprintln("Unknown syscall"); + case SYS_TERMID: + sys_termid((char*)arg1); break; + case SYS_CLOSE: + ret = sys_close((int)(uintptr_t)arg1); + break; + case SYS_SEEK: + ret = sys_seek((int)(uintptr_t)arg1, (long)arg2); + break; + case SYS_OPEN: + ret = sys_open((const char*)arg1, (int)(uintptr_t)arg2); + break; + default: + Kernel::panic("Unknown syscall {}", syscall); } asm volatile("cli"); diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt index 698e2476..2d23f273 100644 --- a/libc/CMakeLists.txt +++ b/libc/CMakeLists.txt @@ -4,6 +4,7 @@ project(libc CXX) set(LIBC_SOURCES ctype.cpp + fcntl.cpp stdio.cpp stdlib.cpp string.cpp diff --git a/libc/fcntl.cpp b/libc/fcntl.cpp new file mode 100644 index 00000000..4495191f --- /dev/null +++ b/libc/fcntl.cpp @@ -0,0 +1,15 @@ +#include +#include +#include +#include + +int open(const char* path, int oflag, ...) +{ + int ret = syscall(SYS_OPEN, path, oflag); + if (ret < 0) + { + errno = -ret; + return -1; + } + return ret; +} diff --git a/libc/include/errno.h b/libc/include/errno.h index c7a0b7f6..cf66b0c8 100644 --- a/libc/include/errno.h +++ b/libc/include/errno.h @@ -16,6 +16,9 @@ #define ENOBUFS 12 #define ENOTTY 13 #define ENOTBLK 14 +#define EMFILE 15 + +#define errno errno __BEGIN_DECLS diff --git a/libc/include/fcntl.h b/libc/include/fcntl.h index db343f6e..80e27900 100644 --- a/libc/include/fcntl.h +++ b/libc/include/fcntl.h @@ -1,8 +1,18 @@ #pragma once +#include + #define O_RDONLY (1 << 0) #define O_WRONLY (1 << 1) #define O_RDWR (O_RDONLY | O_WRONLY) #define O_ACCMODE (O_RDONLY | O_WRONLY) #define O_EXCL (1 << 2) #define O_CREAT (1 << 3) +#define O_TRUNC (1 << 4) +#define O_APPEND (1 << 5) + +__BEGIN_DECLS + +int open(const char*, int, ...); + +__END_DECLS diff --git a/libc/include/stdio.h b/libc/include/stdio.h index c62de6a9..a6349e79 100644 --- a/libc/include/stdio.h +++ b/libc/include/stdio.h @@ -18,28 +18,83 @@ #define EXIT_FAILURE 1 #define EXIT_SUCCESS 0 +#define L_ctermid 50 + +#define BUFSIZ 128 + +#define FOPEN_MAX 16 + __BEGIN_DECLS struct FILE; typedef struct FILE FILE; +typedef uint64_t fpos_t; + extern FILE* stdin; extern FILE* stdout; extern FILE* stderr; -FILE* fopen(const char* __restrict__, const char* __restrict__); -int fclose(FILE*); -int fflush(FILE*); -int fprintf(FILE* __restrict__, const char* __restrict__, ...); -int fseek(FILE*, long, int); -int printf(const char* __restrict__, ...); -int putchar(int); -int puts(const char*); -int vfprintf(FILE* __restrict__, const char* __restrict__, va_list); -long ftell(FILE*); -size_t fread(void* __restrict__, size_t, size_t, FILE*); -size_t fwrite(void const* __restrict__, size_t, size_t, FILE*); -void setbuf(FILE* __restrict__, char* __restrict__); -int sprintf(char* __restrict__, const char* __restrict__, ...); +void clearerr(FILE*); +char* ctermid(char*); +int fclose(FILE*); +FILE* fdopen(int, const char*); +int feof(FILE*); +int ferror(FILE*); +int fflush(FILE*); +int fgetc(FILE*); +int fgetpos(FILE*, fpos_t*); +char* fgets(char*, int, FILE*); +int fileno(FILE*); +void flockfile(FILE*); +FILE* fopen(const char*, const char*); +int fprintf(FILE*, const char*, ...); +int fputc(int, FILE*); +int fputs(const char*, FILE*); +size_t fread(void*, size_t, size_t, FILE*); +FILE* freopen(const char*, const char*, FILE*); +int fscanf(FILE*, const char*, ...); +int fseek(FILE*, long, int); +int fseeko(FILE*, off_t, int); +int fsetpos(FILE*, const fpos_t*); +long ftell(FILE*); +off_t ftello(FILE*); +int ftrylockfile(FILE*); +void funlockfile(FILE*); +size_t fwrite(const void*, size_t, size_t, FILE*); +int getc(FILE*); +int getchar(void); +int getc_unlocked(FILE*); +int getchar_unlocked(void); +char* gets(char*); +int pclose(FILE*); +void perror(const char*); +FILE* popen(const char*, const char*); +int printf(const char*, ...); +int putc(int, FILE*); +int putchar(int); +int putc_unlocked(int, FILE*); +int putchar_unlocked(int); +int puts(const char*); +int remove(const char*); +int rename(const char*, const char*); +void rewind(FILE*); +int scanf(const char*, ...); +void setbuf(FILE*, char*); +int setvbuf(FILE*, char*, int, size_t); +int snprintf(char*, size_t, const char*, ...); +int sprintf(char*, const char*, ...); +int sscanf(const char*, const char*, ...); +char* tempnam(const char*, const char*); +FILE* tmpfile(void); +char* tmpnam(char*); +int ungetc(int, FILE*); +int vfprintf(FILE*, const char*, va_list); +int vfscanf(FILE*, const char*, va_list); +int vprintf(const char*, va_list); +int vscanf(const char*, va_list); +int vsnprintf(char*, size_t, const char*, va_list); +int vsprintf(char*, const char*, va_list); +int vsscanf(const char*, const char*, va_list); __END_DECLS diff --git a/libc/stdio.cpp b/libc/stdio.cpp index 616b7aca..ac6fb778 100644 --- a/libc/stdio.cpp +++ b/libc/stdio.cpp @@ -1,3 +1,5 @@ +#include +#include #include #include #include @@ -6,92 +8,496 @@ struct FILE { - int fd; + int fd { -1 }; + off_t offset { 0 }; + bool eof { false }; + bool error { false }; + + unsigned char buffer[BUFSIZ]; + uint32_t buffer_index { 0 }; }; -static FILE __stdin { .fd = STDIN_FILENO }; -static FILE __stdout { .fd = STDOUT_FILENO }; -static FILE __stderr { .fd = STDERR_FILENO }; +static FILE s_files[FOPEN_MAX] { + { .fd = STDIN_FILENO }, + { .fd = STDOUT_FILENO }, + { .fd = STDERR_FILENO }, +}; -FILE* stdin = &__stdin; -FILE* stdout = &__stdout; -FILE* stderr = &__stderr; +FILE* stdin = &s_files[0]; +FILE* stdout = &s_files[1]; +FILE* stderr = &s_files[2]; -int fclose(FILE*) +void clearerr(FILE* file) { - return -1; + file->eof = false; + file->error = false; } -int fflush(FILE*) +char* ctermid(char* buffer) { + static char s_buffer[L_ctermid]; + char* target = buffer ? buffer : s_buffer; + syscall(SYS_TERMID, target); + return target; +} + +int fclose(FILE* file) +{ + if (int ret = syscall(SYS_CLOSE, file->fd) < 0) + { + errno = -ret; + return EOF; + } + file->fd = -1; return 0; } -FILE* fopen(const char* __restrict__, const char* __restrict__) +// TODO +FILE* fdopen(int, const char*); + +int feof(FILE* file) { + return file->eof; +} + +int ferror(FILE* file) +{ + return file->error; +} + +int fflush(FILE* file) +{ + if (file == nullptr) + { + for (int i = 0; i < FOPEN_MAX; i++) + if (s_files[i].fd != -1) + if (int ret = fflush(&s_files[i]); ret != 0) + return ret; + return 0; + } + + if (file->buffer_index == 0) + return 0; + + if (long ret = syscall(SYS_WRITE, file->fd, file->buffer, file->buffer_index); ret < 0) + { + errno = -ret; + file->error = true; + return EOF; + } + + file->buffer_index = 0; + return 0; +} + +int fgetc(FILE* file) +{ + if (file->eof) + return EOF; + + unsigned char c; + long ret = syscall(SYS_READ, file->fd, &c, 1); + + if (ret < 0) + { + errno = -ret; + return EOF; + } + else if (ret == 0) + { + file->eof = true; + return EOF; + } + + file->offset++; + return c; +} + +int fgetpos(FILE* file, fpos_t* pos) +{ + *pos = file->offset; + return 0; +} + +char* fgets(char* str, int size, FILE* file) +{ + if (size == 0) + return str; + int c = fgetc(file); + if (c == EOF) + return nullptr; + str[0] = c; + for (int i = 1; i < size - 1; i++) + { + str[i] = fgetc(file); + if (str[i] == EOF) + { + str[i] = '\0'; + return nullptr; + } + if (str[i] == '\n') + { + str[i + 1] = '\0'; + return str; + } + } + str[size - 1] = '\0'; + return str; +} + +// TODO +int fileno(FILE*); + +// TODO +void flockfile(FILE*); + +FILE* fopen(const char* pathname, const char* mode) +{ + uint8_t flags = 0; + if (mode[0] == 'r') + flags |= O_RDONLY; + else if (mode[0] == 'b') + flags |= O_WRONLY | O_CREAT | O_TRUNC; + else if (mode[0] == 'a') + flags |= O_WRONLY | O_CREAT | O_APPEND; + else + { + errno = EINVAL; + return nullptr; + } + + if (mode[1] && mode[2] && mode[1] == mode[2]) + { + errno = EINVAL; + return nullptr; + } + + for (int i = 1; i <= 2; i++) + { + if (mode[i] == 0) + break; + else if (mode[i] == '+') + flags |= O_RDWR; + else if (mode[i] == 'b') + continue; + else + { + errno = EINVAL; + return nullptr; + } + } + + int fd = open(pathname, flags); + if (fd == -1) + return nullptr; + + for (int i = 0; i < FOPEN_MAX; i++) + { + if (s_files[i].fd == -1) + { + s_files[i] = { .fd = fd }; + return &s_files[i]; + } + } + + errno = EMFILE; return nullptr; } -int fseek(FILE*, long, int) +int fprintf(FILE* file, const char* format, ...) { - return -1; + va_list arguments; + va_start(arguments, format); + int ret = vfprintf(file, format, arguments); + va_end(arguments); + return ret; } -long ftell(FILE*) +int fputc(int c, FILE* file) { - return -1; + file->buffer[file->buffer_index++] = c; + if (c == '\n' || file->buffer_index == sizeof(file->buffer)) + if (fflush(file) == EOF) + return EOF; + return (unsigned char)c; } -size_t fread(void* __restrict__, size_t, size_t, FILE*) +int fputs(const char* str, FILE* file) { + while (*str) + { + if (putc(*str, file) == EOF) + return EOF; + str++; + } return 0; } -size_t fwrite(void const* __restrict__, size_t, size_t, FILE*) +size_t fread(void* buffer, size_t size, size_t nitems, FILE* file) { + unsigned char* ubuffer = (unsigned char*)buffer; + for (size_t byte = 0; byte < nitems * size; byte++) + if ((ubuffer[byte] = fgetc(file)) == EOF) + return byte / size; + return nitems; +} + +// TODO +FILE* freopen(const char*, const char*, FILE*); + +// TODO +int fscanf(FILE*, const char*, ...); + +int fseek(FILE* file, long offset, int whence) +{ + return fseeko(file, offset, whence); +} + +int fseeko(FILE* file, off_t offset, int whence) +{ + if (whence == SEEK_CUR) + file->offset += offset; + else if (whence == SEEK_SET) + file->offset = offset; + else if (whence == SEEK_END) + { + errno = ENOTSUP; + return -1; + } + else + { + errno = EINVAL; + return -1; + } + + if (file->offset < 0) + { + file->offset -= offset; + errno = EINVAL; + return -1; + } + + if (long ret = syscall(SYS_SEEK, file->fd, file->offset); ret < 0) + { + errno = -ret; + return -1; + } + + file->eof = false; + return 0; } -int fprintf(FILE* __restrict__ file, const char* __restrict__ format, ...) +int fsetpos(FILE* file, const fpos_t* pos) { - va_list args; - va_start(args, format); - int res = vfprintf(stdout, format, args); - va_end(args); - return res; + return fseek(file, *pos, SEEK_SET); } - -void setbuf(FILE* __restrict__, char* __restrict__) +long ftell(FILE* file) { - + return ftello(file); } -int vfprintf(FILE* __restrict__, const char* __restrict__, va_list) +off_t ftello(FILE* file) { - return -1; + return file->offset; } -int printf(const char* __restrict__ format, ...) +// TODO +int ftrylockfile(FILE*); + +// TODO +void funlockfile(FILE*); + +size_t fwrite(const void* buffer, size_t size, size_t nitems, FILE* file) { - va_list args; - va_start(args, format); - int res = vfprintf(stdout, format, args); - va_end(args); - return res; + unsigned char* ubuffer = (unsigned char*)buffer; + for (size_t byte = 0; byte < nitems * size; byte++) + if (fputc(ubuffer[byte], file) == EOF) + return byte / size; + return nitems; } -int putchar(int ch) +int getc(FILE* file) { - return printf("%c", ch); + return fgetc(file); } -int puts(const char* str) +int getchar(void) { - return syscall(SYS_WRITE, STDOUT_FILENO, str, strlen(str)); + return getc(stdin); } -int sprintf(char* __restrict__ stream, const char* __restrict__ format, ...) +// TODO +int getc_unlocked(FILE*); + +// TODO +int getchar_unlocked(void); + +char* gets(char* buffer) { - return -1; + if (stdin->eof) + return nullptr; + + unsigned char* ubuffer = (unsigned char*)buffer; + + int first = fgetc(stdin); + if (first == EOF) + return nullptr; + *ubuffer++ = first; + + for (;;) + { + int c = fgetc(stdin); + if (c == '\n' || c == EOF) + { + *ubuffer++ = '\0'; + return buffer; + } + *ubuffer++ = c; + } } + +// TODO +int pclose(FILE*); + +void perror(const char* string) +{ + if (string && *string) + { + fputs(string, stderr); + fputs(": ", stderr); + } + fputs(strerror(errno), stderr); + fflush(stderr); + stderr->error = true; +} + +// TODO +FILE* popen(const char*, const char*); + +int printf(const char* format, ...) +{ + va_list arguments; + va_start(arguments, format); + int ret = vfprintf(stdout, format, arguments); + va_end(arguments); + return ret; +} + +int putc(int c, FILE* file) +{ + return fputc(c, file); +} + +int putchar(int c) +{ + return putc(c, stdout); +} + +// TODO +int putc_unlocked(int, FILE*); + +// TODO +int putchar_unlocked(int); + +int puts(const char* string) +{ + if (fputs(string, stdout) == EOF) + return EOF; + if (fputc('\n', stdout) == EOF) + return EOF; + return 0; +} + +// TODO +int remove(const char*); + +// TODO +int rename(const char*, const char*); + +// TODO +void rewind(FILE*); + +// TODO +int scanf(const char*, ...); + +// TODO +void setbuf(FILE*, char*); + +// TODO +int setvbuf(FILE*, char*, int, size_t); + +// TODO +int snprintf(char*, size_t, const char*, ...); + +// TODO +int sprintf(char*, const char*, ...); + +// TODO +int sscanf(const char*, const char*, ...); + +// TODO +char* tempnam(const char*, const char*); + +// TODO +FILE* tmpfile(void); + +// TODO +char* tmpnam(char*); + +// TODO +int ungetc(int, FILE*); + +int vfprintf(FILE* file, const char* format, va_list arguments) +{ + int written = 0; + while (*format) + { + if (*format == '%') + { + format++; + switch (*format) + { + case '%': + break; + case 's': + { + const char* string = va_arg(arguments, const char*); + if (fputs(string, file) == EOF) + return -1; + written += strlen(string); + format++; + break; + } + default: + break; + } + } + if (fputc(*format, file) == EOF) + return -1; + written++; + format++; + } + return written; +} + +// TODO +int vfscanf(FILE*, const char*, va_list); + +int vprintf(const char* format, va_list arguments) +{ + return vfprintf(stdout, format, arguments); +} + +// TODO +int vscanf(const char*, va_list); + +// TODO +int vsnprintf(char*, size_t, const char*, va_list); + +// TODO +int vsprintf(char*, const char*, va_list); + +// TODO +int vsscanf(const char*, const char*, va_list); diff --git a/libc/stdlib.cpp b/libc/stdlib.cpp index 9f30fd09..c8eedc4f 100644 --- a/libc/stdlib.cpp +++ b/libc/stdlib.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -13,6 +14,7 @@ void abort(void) void exit(int status) { + fflush(nullptr); _fini(); _exit(status); ASSERT_NOT_REACHED(); diff --git a/libc/string.cpp b/libc/string.cpp index 8df312fe..d551b59b 100644 --- a/libc/string.cpp +++ b/libc/string.cpp @@ -125,6 +125,9 @@ char* strerror(int error) case ENOTBLK: strcpy(buffer, "Block device required"); break; + case EMFILE: + strcpy(buffer, "File descriptor value too large"); + break; default: { // FIXME: sprintf diff --git a/userspace/test.c b/userspace/test.c index 04e7f20a..722ac1da 100644 --- a/userspace/test.c +++ b/userspace/test.c @@ -2,6 +2,7 @@ int main() { - puts("Hello World!"); + if (printf("Hello %s!", "World") == -1) + perror(NULL); return 0; }