Browse Source

Kernel+LibC: Implement readv

We already had writev, so let's just add readv too.
AnotherTest 4 years ago
parent
commit
a3a7ab83c4

+ 2 - 1
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 {
 

+ 1 - 0
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*>);

+ 58 - 0
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);

+ 6 - 0
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);
+}
 }

+ 1 - 0
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