summaryrefslogtreecommitdiff
path: root/Kernel
diff options
context:
space:
mode:
authorAnotherTest <ali.mpfard@gmail.com>2021-02-12 05:40:03 +0330
committerAndreas Kling <kling@serenityos.org>2021-02-15 17:32:56 +0100
commita3a7ab83c472b9cd3a0b119d4b27104525d1d2f1 (patch)
tree498c5784fec26d859dbbe84b452c385f22d5336a /Kernel
parent1e79c0461612807eeb0783665fba4e409c56462e (diff)
downloadserenity-a3a7ab83c472b9cd3a0b119d4b27104525d1d2f1.zip
Kernel+LibC: Implement readv
We already had writev, so let's just add readv too.
Diffstat (limited to 'Kernel')
-rw-r--r--Kernel/API/Syscall.h3
-rw-r--r--Kernel/Process.h1
-rw-r--r--Kernel/Syscalls/read.cpp58
3 files changed, 61 insertions, 1 deletions
diff --git a/Kernel/API/Syscall.h b/Kernel/API/Syscall.h
index 984405b84b..7077deeaae 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 2b75d4e5eb..848c0a9976 100644
--- a/Kernel/Process.h
+++ b/Kernel/Process.h
@@ -240,6 +240,7 @@ public:
int sys$open(Userspace<const Syscall::SC_open_params*>);
int sys$close(int fd);
ssize_t sys$read(int fd, Userspace<u8*>, ssize_t);
+ ssize_t sys$readv(int fd, Userspace<const struct iovec*> iov, int iov_count);
ssize_t sys$write(int fd, const u8*, ssize_t);
ssize_t sys$writev(int fd, Userspace<const struct iovec*> iov, int iov_count);
int sys$fstat(int fd, Userspace<stat*>);
diff --git a/Kernel/Syscalls/read.cpp b/Kernel/Syscalls/read.cpp
index 0a1894a090..b9c1256270 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<const struct iovec*> 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<iovec, 32> 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<i32>::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<Thread::ReadBlocker>({}, *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<u8*> buffer, ssize_t size)
{
REQUIRE_PROMISE(stdio);