diff --git a/Kernel/API/Syscall.h b/Kernel/API/Syscall.h index 984405b84b4..7077deeaae7 100644 --- a/Kernel/API/Syscall.h +++ b/Kernel/API/Syscall.h @@ -191,7 +191,8 @@ namespace Kernel { S(set_coredump_metadata) \ S(abort) \ S(anon_create) \ - S(msyscall) + S(msyscall) \ + S(readv) namespace Syscall { diff --git a/Kernel/Process.h b/Kernel/Process.h index 2b75d4e5ebe..848c0a99769 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -240,6 +240,7 @@ public: int sys$open(Userspace); int sys$close(int fd); ssize_t sys$read(int fd, Userspace, ssize_t); + ssize_t sys$readv(int fd, Userspace iov, int iov_count); ssize_t sys$write(int fd, const u8*, ssize_t); ssize_t sys$writev(int fd, Userspace iov, int iov_count); int sys$fstat(int fd, Userspace); diff --git a/Kernel/Syscalls/read.cpp b/Kernel/Syscalls/read.cpp index 0a1894a0908..b9c12562701 100644 --- a/Kernel/Syscalls/read.cpp +++ b/Kernel/Syscalls/read.cpp @@ -30,6 +30,64 @@ namespace Kernel { +ssize_t Process::sys$readv(int fd, Userspace iov, int iov_count) +{ + REQUIRE_PROMISE(stdio); + if (iov_count < 0) + return -EINVAL; + + { + Checked checked_iov_count = sizeof(iovec); + checked_iov_count *= iov_count; + if (checked_iov_count.has_overflow()) + return -EFAULT; + } + + u64 total_length = 0; + Vector vecs; + vecs.resize(iov_count); + if (!copy_n_from_user(vecs.data(), iov, iov_count)) + return -EFAULT; + for (auto& vec : vecs) { + total_length += vec.iov_len; + if (total_length > NumericLimits::max()) + return -EINVAL; + } + + auto description = file_description(fd); + if (!description) + return -EBADF; + + if (!description->is_readable()) + return -EBADF; + + if (description->is_directory()) + return -EISDIR; + + int nread = 0; + for (auto& vec : vecs) { + if (description->is_blocking()) { + if (!description->can_read()) { + auto unblock_flags = Thread::FileBlocker::BlockFlags::None; + if (Thread::current()->block({}, *description, unblock_flags).was_interrupted()) + return -EINTR; + if (!((u32)unblock_flags & (u32)Thread::FileBlocker::BlockFlags::Read)) + return -EAGAIN; + // TODO: handle exceptions in unblock_flags + } + } + auto buffer = UserOrKernelBuffer::for_user_buffer((u8*)vec.iov_base, vec.iov_len); + if (!buffer.has_value()) + return -EFAULT; + auto result = description->read(buffer.value(), vec.iov_len); + if (result.is_error()) + return result.error(); + nread += result.value(); + } + + return nread; +} + ssize_t Process::sys$read(int fd, Userspace buffer, ssize_t size) { REQUIRE_PROMISE(stdio); diff --git a/Userland/Libraries/LibC/sys/uio.cpp b/Userland/Libraries/LibC/sys/uio.cpp index 2fd6c94fe92..58b03a690f8 100644 --- a/Userland/Libraries/LibC/sys/uio.cpp +++ b/Userland/Libraries/LibC/sys/uio.cpp @@ -35,4 +35,10 @@ ssize_t writev(int fd, const struct iovec* iov, int iov_count) int rc = syscall(SC_writev, fd, iov, iov_count); __RETURN_WITH_ERRNO(rc, rc, -1); } + +ssize_t readv(int fd, const struct iovec* iov, int iov_count) +{ + int rc = syscall(SC_readv, fd, iov, iov_count); + __RETURN_WITH_ERRNO(rc, rc, -1); +} } diff --git a/Userland/Libraries/LibC/sys/uio.h b/Userland/Libraries/LibC/sys/uio.h index be310b38f10..b77492a61be 100644 --- a/Userland/Libraries/LibC/sys/uio.h +++ b/Userland/Libraries/LibC/sys/uio.h @@ -37,5 +37,6 @@ struct iovec { }; ssize_t writev(int fd, const struct iovec*, int iov_count); +ssize_t readv(int fd, const struct iovec*, int iov_count); __END_DECLS