diff --git a/kernel/src/sys/file.cpp b/kernel/src/sys/file.cpp index 8785a2fc..8e3b8dee 100644 --- a/kernel/src/sys/file.cpp +++ b/kernel/src/sys/file.cpp @@ -3,6 +3,9 @@ #include "memory/MemoryManager.h" #include "sys/Syscall.h" #include "thread/Scheduler.h" +#include +#include +#include Result sys_read(Registers*, SyscallArgs args) { @@ -49,3 +52,34 @@ Result sys_write(Registers*, SyscallArgs args) return nwritten; } + +Result sys_lseek(Registers*, SyscallArgs args) +{ + int fd = (int)args[0]; + off_t offset = (long)args[1]; + int whence = (int)args[2]; + + if (fd < 0 || fd >= FD_MAX) return err(EBADF); + + Thread* current = Scheduler::current(); + + Option& descriptor = current->fd_table->fds[fd]; + + if (!descriptor.has_value()) return err(EBADF); + + off_t new_offset; + + switch (whence) + { + case SEEK_SET: new_offset = offset; break; + case SEEK_CUR: new_offset = TRY(safe_add((long)descriptor->offset, offset)); break; + case SEEK_END: todo(); + default: return err(EINVAL); + } + + if (new_offset < 0) return err(EINVAL); + + descriptor->offset = (usize)new_offset; + + return (u64)new_offset; +} diff --git a/libc/include/bits/seek.h b/libc/include/bits/seek.h new file mode 100644 index 00000000..a5067c19 --- /dev/null +++ b/libc/include/bits/seek.h @@ -0,0 +1,10 @@ +/* bits/seek.h: SEEK_* constants for lseek() and fseek(). */ + +#ifndef _BITS_SEEK_H +#define _BITS_SEEK_H + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +#endif diff --git a/libc/include/stdio.h b/libc/include/stdio.h index 545faaf5..de22722f 100644 --- a/libc/include/stdio.h +++ b/libc/include/stdio.h @@ -3,6 +3,7 @@ #ifndef _STDIO_H #define _STDIO_H +#include #include #include @@ -13,7 +14,6 @@ typedef struct int _eof; } FILE; -#define SEEK_SET 0 #define EOF -1 extern FILE* stderr; @@ -38,8 +38,20 @@ extern "C" /* Write arbitrarily sized items to a stream. */ size_t fwrite(const void* buf, size_t size, size_t nmemb, FILE* stream); - int fseek(FILE*, long, int); - long ftell(FILE*); + /* Move the file position. Clears the end-of-file indicator on success. */ + int fseek(FILE* stream, long offset, int whence); + + /* Return the current file position. */ + long ftell(FILE* stream); + + /* Rewind the file position and clear the error and end-of-file indicators. */ + void rewind(FILE* stream); + + /* Save the current file position. */ + int fgetpos(FILE* stream, fpos_t* pos); + + /* Restore a file position. */ + int fsetpos(FILE* stream, const fpos_t* pos); /* Return whether the error indicator was set in stream. */ int ferror(FILE* stream); diff --git a/libc/include/sys/types.h b/libc/include/sys/types.h index e6917712..9a9a02f4 100644 --- a/libc/include/sys/types.h +++ b/libc/include/sys/types.h @@ -13,5 +13,8 @@ typedef __i64_t ssize_t; typedef __i64_t time_t; typedef __u16_t mode_t; typedef __u64_t useconds_t; +typedef __i64_t off_t; + +typedef off_t fpos_t; #endif diff --git a/libc/include/unistd.h b/libc/include/unistd.h index bb6a0a3e..8f15cf75 100644 --- a/libc/include/unistd.h +++ b/libc/include/unistd.h @@ -6,6 +6,7 @@ #define __need_NULL #include +#include #include #include @@ -41,6 +42,9 @@ extern "C" /* Write bytes to a file descriptor. */ ssize_t write(int fd, const void* buf, size_t size); + /* Modify a file descriptor's offset. */ + off_t lseek(int fd, off_t offset, int whence); + #ifdef __cplusplus } #endif diff --git a/libc/src/stdio.cpp b/libc/src/stdio.cpp index 07951eea..6ccc932e 100644 --- a/libc/src/stdio.cpp +++ b/libc/src/stdio.cpp @@ -75,6 +75,44 @@ extern "C" return (size_t)nwrite / size; } + int fseek(FILE* stream, long offset, int whence) + { + long result = lseek(stream->_fd, offset, whence); + if (result < 0) return -1; + + // man fseek(3): A successful call to the fseek() function clears the end-of-file indicator for the stream. + stream->_eof = 0; + + return 0; + } + + long ftell(FILE* stream) + { + return lseek(stream->_fd, 0, SEEK_CUR); + } + + void rewind(FILE* stream) + { + lseek(stream->_fd, 0, SEEK_SET); + + clearerr(stream); + } + + int fgetpos(FILE* stream, fpos_t* pos) + { + long offset = ftell(stream); + if (offset < 0) return -1; + + *pos = offset; + + return 0; + } + + int fsetpos(FILE* stream, const fpos_t* pos) + { + return fseek(stream, *pos, SEEK_SET); + } + int ferror(FILE* stream) { return stream->_err; diff --git a/libc/src/unistd.cpp b/libc/src/unistd.cpp index ebbd189e..8b989ce7 100644 --- a/libc/src/unistd.cpp +++ b/libc/src/unistd.cpp @@ -60,4 +60,10 @@ extern "C" long rc = syscall(SYS_write, fd, buf, size); __errno_return(rc, ssize_t); } + + off_t lseek(int fd, off_t offset, int whence) + { + long rc = syscall(SYS_lseek, fd, offset, whence); + __errno_return(rc, off_t); + } } diff --git a/libluna/include/luna/Syscall.h b/libluna/include/luna/Syscall.h index 64928e9a..f4356f28 100644 --- a/libluna/include/luna/Syscall.h +++ b/libluna/include/luna/Syscall.h @@ -2,7 +2,7 @@ #define enumerate_syscalls(_e) \ _e(exit) _e(console_write) _e(clock_gettime) _e(allocate_memory) _e(deallocate_memory) _e(usleep) _e(open) \ - _e(close) _e(read) _e(getpid) _e(write) + _e(close) _e(read) _e(getpid) _e(write) _e(lseek) enum Syscalls {