]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
feat: sendmsg/recvmsg — advanced socket I/O with scatter-gather iovec support
authorTulio A M Mendes <[email protected]>
Sun, 15 Feb 2026 08:14:34 +0000 (05:14 -0300)
committerTulio A M Mendes <[email protected]>
Sun, 15 Feb 2026 08:14:34 +0000 (05:14 -0300)
include/syscall.h
src/kernel/syscall.c

index 14b985071f5ce6861a069b7465b4c8bcc3ef7a2d..9f6aae023cd729c0c29dce149b78e5ae7598754b 100644 (file)
@@ -143,6 +143,9 @@ enum {
     SYSCALL_INOTIFY_INIT     = 115,
     SYSCALL_INOTIFY_ADD_WATCH = 116,
     SYSCALL_INOTIFY_RM_WATCH  = 117,
+
+    SYSCALL_SENDMSG  = 118,
+    SYSCALL_RECVMSG  = 119,
 };
 
 #endif
index 64dbabf0f1e695f5eafc25cce5ecd06b38ff7625..0e03f025930c189ec23057449b4751484c6beca8 100644 (file)
@@ -4071,6 +4071,73 @@ static inline int sock_fd_get_sid(int fd) {
     return (int)f->node->inode;
 }
 
+__attribute__((noinline))
+static int syscall_sendmsg_impl(int sockfd, void* user_msg, int flags) {
+    int sid = sock_fd_get_sid(sockfd);
+    if (sid < 0) return -EBADF;
+
+    struct { void* name; uint32_t namelen; void* iov; uint32_t iovlen;
+             void* control; uint32_t controllen; int mflags; } kmsg;
+    if (copy_from_user(&kmsg, user_msg, sizeof(kmsg)) < 0) return -EFAULT;
+
+    struct sockaddr_in dest;
+    int has_dest = 0;
+    if (kmsg.name && kmsg.namelen >= sizeof(dest)) {
+        if (copy_from_user(&dest, kmsg.name, sizeof(dest)) < 0) return -EFAULT;
+        has_dest = 1;
+    }
+
+    int total = 0;
+    for (uint32_t i = 0; i < kmsg.iovlen && i < 16; i++) {
+        struct { void* base; uint32_t len; } kiov;
+        uint8_t* iov_arr = (uint8_t*)kmsg.iov;
+        if (copy_from_user(&kiov, &iov_arr[i * sizeof(kiov)], sizeof(kiov)) < 0)
+            return -EFAULT;
+        if (kiov.len == 0) continue;
+        if (!user_range_ok(kiov.base, kiov.len)) return -EFAULT;
+        int ret;
+        if (has_dest)
+            ret = ksocket_sendto(sid, kiov.base, kiov.len, flags, &dest);
+        else
+            ret = ksocket_send(sid, kiov.base, kiov.len, flags);
+        if (ret < 0) return (total > 0) ? total : ret;
+        total += ret;
+    }
+    return total;
+}
+
+__attribute__((noinline))
+static int syscall_recvmsg_impl(int sockfd, void* user_msg, int flags) {
+    int sid = sock_fd_get_sid(sockfd);
+    if (sid < 0) return -EBADF;
+
+    struct { void* name; uint32_t namelen; void* iov; uint32_t iovlen;
+             void* control; uint32_t controllen; int mflags; } kmsg;
+    if (copy_from_user(&kmsg, user_msg, sizeof(kmsg)) < 0) return -EFAULT;
+
+    int total = 0;
+    struct sockaddr_in src;
+    memset(&src, 0, sizeof(src));
+
+    for (uint32_t i = 0; i < kmsg.iovlen && i < 16; i++) {
+        struct { void* base; uint32_t len; } kiov;
+        uint8_t* iov_arr = (uint8_t*)kmsg.iov;
+        if (copy_from_user(&kiov, &iov_arr[i * sizeof(kiov)], sizeof(kiov)) < 0)
+            return -EFAULT;
+        if (kiov.len == 0) continue;
+        if (!user_range_ok(kiov.base, kiov.len)) return -EFAULT;
+        int ret = ksocket_recvfrom(sid, kiov.base, kiov.len, flags, &src);
+        if (ret < 0) return (total > 0) ? total : ret;
+        total += ret;
+        if ((uint32_t)ret < kiov.len) break;
+    }
+
+    if (kmsg.name && kmsg.namelen >= sizeof(src))
+        (void)copy_to_user(kmsg.name, &src, sizeof(src));
+
+    return total;
+}
+
 /* Separate function to keep socket locals off syscall_handler's stack frame */
 __attribute__((noinline))
 static void socket_syscall_dispatch(struct registers* regs, uint32_t syscall_no) {
@@ -4202,6 +4269,18 @@ static void socket_syscall_dispatch(struct registers* regs, uint32_t syscall_no)
         return;
     }
 
+    if (syscall_no == SYSCALL_SENDMSG) {
+        sc_ret(regs) = (uint32_t)syscall_sendmsg_impl(
+            (int)sc_arg0(regs), (void*)sc_arg1(regs), (int)sc_arg2(regs));
+        return;
+    }
+
+    if (syscall_no == SYSCALL_RECVMSG) {
+        sc_ret(regs) = (uint32_t)syscall_recvmsg_impl(
+            (int)sc_arg0(regs), (void*)sc_arg1(regs), (int)sc_arg2(regs));
+        return;
+    }
+
     if (syscall_no == SYSCALL_SETITIMER) {
         /* setitimer(which, user_new_value, user_old_value)
          * struct itimerval { uint32_t it_interval; uint32_t it_value; } (ticks) */