diff --git a/kernel/api/sys/uio.h b/kernel/api/sys/uio.h new file mode 100644 index 00000000..7269e9ef --- /dev/null +++ b/kernel/api/sys/uio.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +struct iovec { + void* iov_base; // Starting address + size_t iov_len; // Size of the memory pointed to by iov_base. +}; diff --git a/kernel/fs/fs.h b/kernel/fs/fs.h index 18c49d2f..8967d223 100644 --- a/kernel/fs/fs.h +++ b/kernel/fs/fs.h @@ -17,10 +17,10 @@ typedef struct multiboot_mod_list multiboot_module_t; // Open file description struct file { - struct mutex offset_lock; struct inode* inode; atomic_int flags; uint64_t offset; + struct mutex offset_lock; void* private_data; atomic_size_t ref_count; }; diff --git a/kernel/syscall/fs.c b/kernel/syscall/fs.c index 8a01dd16..ecb6547e 100644 --- a/kernel/syscall/fs.c +++ b/kernel/syscall/fs.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -93,6 +94,41 @@ ssize_t sys_ia32_pread64(int fd, void* user_buf, size_t count, uint32_t pos_lo, return rc; } +ssize_t sys_readv(int fd, const struct iovec* user_iov, int iovcnt) { + if (!user_iov || !is_user_range(user_iov, iovcnt * sizeof(struct iovec))) + return -EFAULT; + struct file* file = task_get_file(fd); + if (IS_ERR(file)) + return PTR_ERR(file); + mutex_lock(&file->offset_lock); + ssize_t ret = 0; + for (int i = 0; i < iovcnt; ++i) { + struct iovec iov; + if (copy_from_user(&iov, user_iov + i, sizeof(struct iovec))) { + ret = -EFAULT; + goto fail; + } + if (!is_user_range(iov.iov_base, iov.iov_len)) { + ret = -EFAULT; + goto fail; + } + ssize_t nread = file_read_to_end(file, iov.iov_base, iov.iov_len); + if (nread == -EINTR) { + if (ret == 0) + ret = -ERESTARTSYS; + break; + } + if (IS_ERR(nread)) { + ret = nread; + goto fail; + } + ret += nread; + } +fail: + mutex_unlock(&file->offset_lock); + return ret; +} + ssize_t sys_readlink(const char* user_pathname, char* user_buf, size_t bufsiz) { char pathname[PATH_MAX]; int rc = copy_pathname_from_user(pathname, user_pathname); @@ -153,6 +189,41 @@ ssize_t sys_ia32_pwrite64(int fd, const void* buf, size_t count, return rc; } +ssize_t sys_writev(int fd, const struct iovec* user_iov, int iovcnt) { + if (!user_iov || !is_user_range(user_iov, iovcnt * sizeof(struct iovec))) + return -EFAULT; + struct file* file = task_get_file(fd); + if (IS_ERR(file)) + return PTR_ERR(file); + mutex_lock(&file->offset_lock); + ssize_t ret = 0; + for (int i = 0; i < iovcnt; ++i) { + struct iovec iov; + if (copy_from_user(&iov, user_iov + i, sizeof(struct iovec))) { + ret = -EFAULT; + goto fail; + } + if (!is_user_range(iov.iov_base, iov.iov_len)) { + ret = -EFAULT; + goto fail; + } + ssize_t nwritten = file_write_all(file, iov.iov_base, iov.iov_len); + if (nwritten == -EINTR) { + if (ret == 0) + ret = -ERESTARTSYS; + break; + } + if (IS_ERR(nwritten)) { + ret = nwritten; + goto fail; + } + ret += nwritten; + } +fail: + mutex_unlock(&file->offset_lock); + return ret; +} + static int truncate(const char* user_path, uint64_t length) { char path[PATH_MAX]; int rc = copy_pathname_from_user(path, user_path); diff --git a/kernel/syscall/syscall.h b/kernel/syscall/syscall.h index fab108a4..5c90c225 100644 --- a/kernel/syscall/syscall.h +++ b/kernel/syscall/syscall.h @@ -89,6 +89,8 @@ F(_llseek, sys_llseek, 0) \ F(getdents, sys_getdents, 0) \ F(_newselect, sys_select, 0) \ + F(readv, sys_readv, 0) \ + F(writev, sys_writev, 0) \ F(sched_yield, sys_sched_yield, 0) \ F(nanosleep, sys_nanosleep_time32, 0) \ F(poll, sys_poll, 0) \ @@ -229,6 +231,8 @@ int sys_llseek(unsigned int fd, unsigned long offset_high, ssize_t sys_getdents(int fd, struct linux_dirent* dirp, size_t count); int sys_select(int nfds, unsigned long* readfds, unsigned long* writefds, unsigned long* exceptfds, struct linux_timeval* timeout); +ssize_t sys_readv(int fd, const struct iovec* iov, int iovcnt); +ssize_t sys_writev(int fd, const struct iovec* iov, int iovcnt); int sys_sched_yield(void); int sys_nanosleep_time32(const struct timespec32* duration, struct timespec32* rem); diff --git a/kernel/syscall/unimplemented.h b/kernel/syscall/unimplemented.h index cbe8b69e..1599f31f 100644 --- a/kernel/syscall/unimplemented.h +++ b/kernel/syscall/unimplemented.h @@ -65,8 +65,6 @@ F(setfsgid) \ F(flock) \ F(msync) \ - F(readv) \ - F(writev) \ F(getsid) \ F(fdatasync) \ F(mlock) \ diff --git a/userland/Makefile b/userland/Makefile index cdad249b..fa19e6ea 100644 --- a/userland/Makefile +++ b/userland/Makefile @@ -89,6 +89,7 @@ LIB_OBJS := \ lib/sys/sysinfo.o \ lib/sys/time.o \ lib/sys/times.o \ + lib/sys/uio.o \ lib/sys/utsname.o \ lib/sys/wait.o \ lib/termios.o \ diff --git a/userland/lib/sys/uio.c b/userland/lib/sys/uio.c new file mode 100644 index 00000000..8d9dd333 --- /dev/null +++ b/userland/lib/sys/uio.c @@ -0,0 +1,10 @@ +#include "uio.h" +#include + +ssize_t readv(int fd, const struct iovec* iov, int iovcnt) { + RETURN_WITH_ERRNO(ssize_t, SYSCALL3(readv, fd, iov, iovcnt)); +} + +ssize_t writev(int fd, const struct iovec* iov, int iovcnt) { + RETURN_WITH_ERRNO(ssize_t, SYSCALL3(writev, fd, iov, iovcnt)); +} diff --git a/userland/lib/sys/uio.h b/userland/lib/sys/uio.h new file mode 100644 index 00000000..30b36737 --- /dev/null +++ b/userland/lib/sys/uio.h @@ -0,0 +1,7 @@ +#pragma once + +#include +#include + +ssize_t readv(int fd, const struct iovec* iov, int iovcnt); +ssize_t writev(int fd, const struct iovec* iov, int iovcnt);