diff options
author | AnotherTest <ali.mpfard@gmail.com> | 2021-02-12 05:40:03 +0330 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-02-15 17:32:56 +0100 |
commit | a3a7ab83c472b9cd3a0b119d4b27104525d1d2f1 (patch) | |
tree | 498c5784fec26d859dbbe84b452c385f22d5336a /Kernel | |
parent | 1e79c0461612807eeb0783665fba4e409c56462e (diff) | |
download | serenity-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.h | 3 | ||||
-rw-r--r-- | Kernel/Process.h | 1 | ||||
-rw-r--r-- | Kernel/Syscalls/read.cpp | 58 |
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); |