diff options
-rw-r--r-- | sys/include/arch/aarch64/param.h | 35 | ||||
-rw-r--r-- | sys/include/arch/amd64/param.h | 35 | ||||
-rw-r--r-- | sys/include/sys/limits.h | 1 | ||||
-rw-r--r-- | sys/include/sys/param.h | 10 | ||||
-rw-r--r-- | sys/include/sys/socket.h | 84 | ||||
-rw-r--r-- | sys/include/sys/syscall.h | 2 | ||||
-rw-r--r-- | sys/include/sys/uio.h | 58 | ||||
-rw-r--r-- | sys/kern/kern_socket.c | 211 | ||||
-rw-r--r-- | sys/kern/kern_syscall.c | 2 | ||||
-rw-r--r-- | sys/kern/kern_uio.c | 272 |
10 files changed, 710 insertions, 0 deletions
diff --git a/sys/include/arch/aarch64/param.h b/sys/include/arch/aarch64/param.h new file mode 100644 index 0000000..c074ffb --- /dev/null +++ b/sys/include/arch/aarch64/param.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _AARCH64_PARAM_H_ +#define _AARCH64_PARAM_H_ + +#define M_WORD_SIZE 4 + +#endif /* !_AARCH64_PARAM_H_ */ diff --git a/sys/include/arch/amd64/param.h b/sys/include/arch/amd64/param.h new file mode 100644 index 0000000..6ea3fca --- /dev/null +++ b/sys/include/arch/amd64/param.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _AMD64_PARAM_H_ +#define _AMD64_PARAM_H_ + +#define M_WORD_SIZE 8 + +#endif /* !_AMD64_PARAM_H_ */ diff --git a/sys/include/sys/limits.h b/sys/include/sys/limits.h index 963d113..c0ce5af 100644 --- a/sys/include/sys/limits.h +++ b/sys/include/sys/limits.h @@ -38,4 +38,5 @@ #define CPU_MAX 256 #define VSR_MAX_DOMAIN 16 #define VSR_MAX_CAPSULE 16 +#define IOVEC_MAX 512 #endif /* !_SYS_LIMITS_H_ */ diff --git a/sys/include/sys/param.h b/sys/include/sys/param.h index 7331d5f..2bbbabd 100644 --- a/sys/include/sys/param.h +++ b/sys/include/sys/param.h @@ -30,11 +30,20 @@ #ifndef _SYS_PARAM_H_ #define _SYS_PARAM_H_ +#if defined(_KERNEL) +#include <machine/param.h> +#endif + /* Assumed cache line size */ #ifndef COHERENCY_UNIT #define COHERENCY_UNIT 64 #endif +/* Assumed machine word size */ +#ifndef M_WORD_SIZE +#define M_WORD_SIZE 4 +#endif + /* Bit related macros */ #define ISSET(v, f) ((v) & (f)) #define BIT(n) (1ULL << (n)) @@ -47,6 +56,7 @@ /* Align up/down a value */ #define ALIGN_DOWN(value, align) ((value) & ~((align)-1)) #define ALIGN_UP(value, align) (((value) + (align)-1) & ~((align)-1)) +#define MALIGN(value) ALIGN_UP((value), M_WORD_SIZE) /* Bitmap helper macros */ #define setbit(a, b) ((a)[(b) >> 3] |= BIT(b % 8)) diff --git a/sys/include/sys/socket.h b/sys/include/sys/socket.h index f01e2bf..5ce1ec6 100644 --- a/sys/include/sys/socket.h +++ b/sys/include/sys/socket.h @@ -31,6 +31,9 @@ #define _SYS_SOCKET_H_ #include <sys/socketvar.h> +#include <sys/queue.h> +#include <sys/param.h> +#include <sys/uio.h> #if defined(_KERNEL) #include <sys/types.h> #include <sys/syscall.h> @@ -51,6 +54,11 @@ typedef uint32_t socklen_t; #endif /* !_SOCKLEN_T_DEFINED_ */ /* + * Socket level number + */ +#define SOL_SOCKET 0xFFFF + +/* * Address family defines */ #define AF_UNSPEC 0 @@ -70,22 +78,95 @@ struct sockaddr { char sa_data[14]; }; +/* + * POSIX message header for recvmsg() + * and sendmsg() calls. + */ +struct msghdr { + void *msg_name; /* Optional address */ + socklen_t msg_namelen; /* Size of address */ + struct iovec *msg_iov; /* Scatter/gather array */ + int msg_iovlen; /* Members in msg_iov */ + void *msg_control; /* Ancillary data, see below */ + socklen_t msg_controllen; /* Ancillary data buffer len */ + int msg_flags; /* Message flags */ +}; + +/* + * POSIX control message header for + * ancillary data objects. + */ +struct cmsghdr { + socklen_t cmsg_len; + int cmsg_level; + int cmsg_type; +}; + +#define CMSG_SPACE(len) (MALIGN(sizeof(struct cmsghdr)) + MALIGN(len)) + +/* Return pointer to cmsg data */ +#define CMSG_DATA(cmsg) PTR_OFFSET(cmsg, sizeof(struct cmsghdr)) + +/* Return length of control message */ +#define CMSG_LEN(len) (MALIGN(sizeof(struct cmsghdr)) + MALIGN(len)) + +/* Return pointer to next cmsghdr */ +#define CMSG_NXTHDR(mhdr, cmsg) \ + PTR_OFFSET(cmsg, MALIGN((cmsg)>cmsg_len)) + \ + MALIGN(sizeof(struct cmsghdr)) > \ + PTR_OFFSET((mhdr)->msg_control, (mhdr)->msg_controllen) ? \ + (struct cmsghdr *)NULL : \ + (struct cmsghdr *)PTR_OFFSET(cmsg, MALIGN((cmsg)->cmsg_len)) + +/* Return pointer to first header */ +#define CMSG_FIRSTHDR(mhdr) \ + ((mhdr)->msg_controllen >= sizeof(struct cmsghdr) ? \ + (struct cmsghdr *)(mhdr)->msg_control : \ + (struct cmsghdr *)NULL); + +/* Socket level control messages */ +#define SCM_RIGHTS 0x01 + #if defined(_KERNEL) +struct cmsg { + union { + struct cmsghdr hdr; + uint8_t buf[CMSG_SPACE(sizeof(int))]; + }; + + size_t control_len; + TAILQ_ENTRY(cmsg) link; +}; + +/* + * List of cmsg headers and data, queued up + * during sendmsg() + */ +struct cmsg_list { + TAILQ_HEAD(, cmsg) list; + uint8_t is_init : 1; +}; + struct ksocket { int sockfd; union { struct sockaddr sockaddr; struct sockaddr_un un; }; + struct cmsg_list cmsg_list; struct sockbuf buf; struct mutex *mtx; }; scret_t sys_socket(struct syscall_args *scargs); scret_t sys_bind(struct syscall_args *scargs); + scret_t sys_recv(struct syscall_args *scargs); scret_t sys_send(struct syscall_args *scargs); + +scret_t sys_recvmsg(struct syscall_args *scargs); +scret_t sys_sendmsg(struct syscall_args *scargs); #endif /* _KERNEL */ int socket(int domain, int type, int protocol); @@ -94,4 +175,7 @@ int bind(int sockfd, const struct sockaddr *addr, socklen_t len); ssize_t send(int sockfd, const void *buf, size_t size, int flags); ssize_t recv(int sockfd, void *buf, size_t len, int flags); +ssize_t sendmsg(int socket, const struct msghdr *msg, int flags); +ssize_t recvmsg(int socket, struct msghdr *msg, int flags); + #endif /* !_SYS_SOCKET_H_ */ diff --git a/sys/include/sys/syscall.h b/sys/include/sys/syscall.h index 51a1016..d79a697 100644 --- a/sys/include/sys/syscall.h +++ b/sys/include/sys/syscall.h @@ -63,6 +63,8 @@ #define SYS_bind 22 #define SYS_recv 23 #define SYS_send 24 +#define SYS_sendmsg 25 +#define SYS_recvmsg 26 #if defined(_KERNEL) /* Syscall return value and arg type */ diff --git a/sys/include/sys/uio.h b/sys/include/sys/uio.h new file mode 100644 index 0000000..4318a53 --- /dev/null +++ b/sys/include/sys/uio.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_UIO_H_ +#define _SYS_UIO_H_ + +#if defined(_KERNEL) +#include <sys/types.h> +#else +#include <stdint.h> +#include <stddef.h> +#endif /* _KERNEL */ + +/* + * POSIX I/O vector + */ +struct iovec { + void *iov_base; + size_t iov_len; +}; + +ssize_t readv(int filedes, const struct iovec *iov, int iovcnt); +ssize_t writev(int filedes, const struct iovec *iov, int iovcnt); + +#if defined(_KERNEL) + +int uio_copyin(const struct iovec *u_iov, struct iovec *k_iov, int iovcnt); +int uio_copyout(const struct iovec *k_iov, struct iovec *u_iov, int iovcnt); +void uio_copyin_clean(struct iovec *copy, int iovcnt); + +#endif /* _KERNEL */ +#endif /* !_SYS_UIO_H_ */ diff --git a/sys/kern/kern_socket.c b/sys/kern/kern_socket.c index a6cc8d2..8be5031 100644 --- a/sys/kern/kern_socket.c +++ b/sys/kern/kern_socket.c @@ -323,6 +323,7 @@ int bind(int sockfd, const struct sockaddr *addr, socklen_t len) { struct ksocket *ksock; + struct cmsg_list *clp; int error; if ((error = get_ksock(sockfd, &ksock)) < 0) { @@ -335,6 +336,119 @@ bind(int sockfd, const struct sockaddr *addr, socklen_t len) if (ksock->mtx == NULL) { return -ENOMEM; } + + /* Initialize the cmsg list queue */ + clp = &ksock->cmsg_list; + TAILQ_INIT(&clp->list); + clp->is_init = 1; + return 0; +} + +/* + * Send socket control message - POSIX.1-2008 + * + * @socket: Socket to transmit on + * @msg: Further arguments + * @flags: Optional flags + * + * Returns zero on success, otherwise a less + * than zero errno. + */ +ssize_t +sendmsg(int socket, const struct msghdr *msg, int flags) +{ + struct ksocket *ksock; + struct cmsg *cmsg; + struct sockaddr_un *un; + struct cmsg_list *clp; + size_t control_len = 0; + int error; + + if ((error = get_ksock(socket, &ksock)) < 0) { + return error; + } + + /* We cannot do sendmsg() non domain sockets */ + un = &ksock->un; + if (un->sun_family != AF_UNIX) { + return -EBADF; + } + + control_len = MALIGN(msg->msg_controllen); + + /* Allocate a new cmsg */ + cmsg = dynalloc(control_len + sizeof(struct cmsg)); + if (cmsg == NULL) { + return -EINVAL; + } + + memcpy(cmsg->buf, msg->msg_control, control_len); + clp = &ksock->cmsg_list; + cmsg->control_len = control_len; + TAILQ_INSERT_TAIL(&clp->list, cmsg, link); + return 0; +} + +/* + * Receive socket control message - POSIX.1‐2017 + * + * @socket: Socket to receive on + * @msg: Further arguments + * @flags: Optional flags + * + * Returns zero on success, otherwise a less + * than zero errno. + */ +ssize_t +recvmsg(int socket, struct msghdr *msg, int flags) +{ + struct ksocket *ksock; + struct sockaddr_un *un; + struct cmsg *cmsg, *tmp; + struct cmsghdr *cmsghdr; + struct cmsg_list *clp; + uint8_t *fds; + int error; + + if (socket < 0) { + return -EINVAL; + } + + /* Grab the socket descriptor */ + if ((error = get_ksock(socket, &ksock)) < 0) { + return error; + } + + /* Must be a unix domain socket */ + un = &ksock->un; + if (un->sun_family != AF_UNIX) { + return -EBADF; + } + + /* Grab the control message list */ + clp = &ksock->cmsg_list; + cmsg = TAILQ_FIRST(&clp->list); + + while (cmsg != NULL) { + cmsghdr = &cmsg->hdr; + + /* Check the control message type */ + switch (cmsghdr->cmsg_type) { + case SCM_RIGHTS: + { + fds = (uint8_t *)CMSG_DATA(cmsghdr); + pr_trace("SCM_RIGHTS -> fd %d\n", fds[0]); + break; + } + } + + tmp = cmsg; + cmsg = TAILQ_NEXT(cmsg, link); + + TAILQ_REMOVE(&clp->list, tmp, link); + dynfree(tmp); + } + return 0; } @@ -440,6 +554,103 @@ sys_send(struct syscall_args *scargs) return send(sockfd, buf, len, flags); } +/* + * recvmsg(3) syscall + * + * arg0: socket + * arg1: msg + * arg2: flags + */ +scret_t +sys_recvmsg(struct syscall_args *scargs) +{ + struct msghdr *u_msg = (void *)scargs->arg1; + void *u_control; + size_t controllen; + struct iovec msg_iov; + struct msghdr msg; + ssize_t retval; + int socket = scargs->arg0; + int flags = scargs->arg2; + int error; + + /* Read the message header */ + error = copyin(u_msg, &msg, sizeof(msg)); + if (error < 0) { + pr_error("sys_recvmsg: bad msg\n"); + return error; + } + + /* Grab the message I/O vector */ + error = uio_copyin(msg.msg_iov, &msg_iov, msg.msg_iovlen); + if (error < 0) { + return error; + } + + /* Save control fields */ + u_control = msg.msg_control; + controllen = msg.msg_controllen; + + /* Allocate a new control field to copy in */ + msg.msg_control = dynalloc(controllen); + if (msg.msg_control == NULL) { + uio_copyin_clean(&msg_iov, msg.msg_iovlen); + return -ENOMEM; + } + + error = copyin(u_control, msg.msg_control, controllen); + if (error < 0) { + retval = error; + goto done; + } + + msg.msg_iov = &msg_iov; + retval = recvmsg(socket, &msg, flags); +done: + uio_copyin_clean(&msg_iov, msg.msg_iovlen); + dynfree(msg.msg_control); + return retval; +} + +/* + * sendmsg(3) syscall + * + * arg0: socket + * arg1: msg + * arg2: flags + */ +scret_t +sys_sendmsg(struct syscall_args *scargs) +{ + struct iovec msg_iov; + struct msghdr *u_msg = (void *)scargs->arg1; + struct msghdr msg; + ssize_t retval; + int socket = scargs->arg0; + int flags = scargs->arg2; + int error; + + /* Read the message header */ + error = copyin(u_msg, &msg, sizeof(msg)); + if (error < 0) { + pr_error("sys_sendmsg: bad msg\n"); + return error; + } + + /* Grab the message I/O vector */ + error = uio_copyin(msg.msg_iov, &msg_iov, msg.msg_iovlen); + if (error < 0) { + return error; + } + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &msg_iov; + retval = sendmsg(socket, &msg, flags); + uio_copyin_clean(&msg_iov, msg.msg_iovlen); + return retval; +} + static struct vops socket_vops = { .read = NULL, .write = NULL, diff --git a/sys/kern/kern_syscall.c b/sys/kern/kern_syscall.c index dd3de4f..7b037aa 100644 --- a/sys/kern/kern_syscall.c +++ b/sys/kern/kern_syscall.c @@ -65,6 +65,8 @@ scret_t(*g_sctab[])(struct syscall_args *) = { sys_bind, /* SYS_bind */ sys_recv, /* SYS_recv */ sys_send, /* SYS_send */ + sys_sendmsg, /* SYS_sendmsg */ + sys_recvmsg, /* SYS_recvmsg */ }; const size_t MAX_SYSCALLS = NELEM(g_sctab); diff --git a/sys/kern/kern_uio.c b/sys/kern/kern_uio.c new file mode 100644 index 0000000..2ec1532 --- /dev/null +++ b/sys/kern/kern_uio.c @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/limits.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/filedesc.h> + +/* + * Clean up after a UIO copyin() operation + * + * @iov: iovec copy to clean up + * @iovcnt: Number of iovec entries + */ +void +uio_copyin_clean(struct iovec *iov, int iovcnt) +{ + for (int i = 0; i < iovcnt; ++i) { + if (iov[i].iov_base == NULL) { + continue; + } + + dynfree(iov[i].iov_base); + iov[i].iov_base = NULL; + } +} + +/* + * Read data into POSIX.1‐2017 iovec + * + * @filedes: File descriptor number + * @iov: I/O vector to read file into + * @iovnt: Number of I/O vectors + */ +ssize_t +readv(int filedes, const struct iovec *iov, int iovcnt) +{ + void *base; + size_t len; + ssize_t tmp, bytes_read = 0; + + if (filedes < 0) { + return -EINVAL; + } + + /* + * Make sure that this conforms to our max + * iovec limit. + */ + if (iovcnt > IOVEC_MAX) { + return -EINVAL; + } + + /* + * Go through each I/O vector and read a chunk + * of data into one. + */ + for (int i = 0; i < iovcnt; ++i) { + base = iov[i].iov_base; + len = iov[i].iov_len; + + /* + * If we encounter a base that is NULL, + * or if the length to read is an invalid + * value of zero. We can just assume this + * is some sort of weird list termination? + */ + if (base == NULL || len == 0) { + break; + } + + /* Read the file into this base */ + tmp = fd_read(filedes, base, len); + + /* Did anything go wrong? */ + if (tmp < 0) { + return tmp; + } + + /* No more data */ + if (tmp == 0) { + break; + } + + /* Read more bytes */ + bytes_read += tmp; + } + + return bytes_read; +} + +/* + * Write data from POSIX.1‐2017 iovec + * + * @filedes: File descriptor number + * @iov: I/O vector to write to file + * @iovnt: Number of I/O vectors + */ +ssize_t +writev(int filedes, const struct iovec *iov, int iovcnt) +{ + void *base; + size_t len; + ssize_t bytes_written = 0; + ssize_t tmp; + + if (filedes < 0) { + return -EINVAL; + } + + /* + * Are we within the limits? Return an + * error if not. + */ + if (iovcnt > IOVEC_MAX) { + return -EINVAL; + } + + for (int i = 0; i < iovcnt; ++i) { + base = iov[i].iov_base; + len = iov[i].iov_len; + + /* + * These are invalid, whatever these are, + * terminate our walk through. + */ + if (base == NULL || len == 0) { + break; + } + + /* Write the data from the iovec */ + tmp = fd_write(filedes, base, len); + + /* Was there an error? */ + if (tmp < 0) { + return tmp; + } + + /* No more data to read? */ + if (tmp == 0) { + break; + } + + bytes_written += tmp; + } + + return bytes_written; +} + +/* + * Validate iovecs coming in from userland + * and copy it to a kernel buffer. + * + * XXX: A new buffer is allocated in k_iov[i]->iov_base + * and must be freed with dynfree() after use. + * + * @u_iov: Userspace source iovecs + * @k_iov: Kernel destination iovec + * @iovcnt: Number of iovecs to copy + */ +int +uio_copyin(const struct iovec *u_iov, struct iovec *k_iov, int iovcnt) +{ + struct iovec *iov_dest; + const struct iovec *iov_src; + size_t len; + void *old_base; + int error; + + if (u_iov == NULL || k_iov == NULL) { + return -EINVAL; + } + + for (int i = 0; i < iovcnt; ++i) { + iov_dest = &k_iov[i]; + iov_src = &u_iov[i]; + error = copyin(iov_src, iov_dest, sizeof(*iov_dest)); + + if (error < 0) { + uio_copyin_clean(iov_dest, i + 1); + return error; + } + + /* + * Save the old base so that we may copy the data to + * the new kernel buffer. First we'd need to allocate + * one of course. + */ + old_base = iov_dest->iov_base; + len = iov_dest->iov_len; + iov_dest->iov_base = dynalloc(len); + + /* Did it fail? */ + if (iov_dest->iov_base == NULL) { + uio_copyin_clean(iov_dest, i + 1); + return -ENOMEM; + } + + /* Copy actual data in */ + error = copyin(old_base, iov_dest->iov_base, len); + if (error < 0) { + uio_copyin_clean(iov_dest, i + 1); + return error; + } + } + + return 0; +} + + +/* + * Validate iovecs going out from kernel space (us) + * before actually sending it out. + * + * @k_iov: Kernel iovec to copyout + * @u_iov: Userspace destination + * @iovcnt: Number of iovecs + */ +int +uio_copyout(const struct iovec *k_iov, struct iovec *u_iov, int iovcnt) +{ + struct iovec iov_shadow, *iov_dest; + const struct iovec *iov_src; + int error; + + for (int i = 0; i < iovcnt; ++i) { + iov_dest = &u_iov[i]; + iov_src = &k_iov[i]; + + /* Grab a shadow copy */ + error = copyin(iov_src, &iov_shadow, sizeof(iov_shadow)); + if (error < 0) { + return error; + } + + /* Copy out actual data */ + error = copyout(iov_src->iov_base, iov_dest->iov_base, iov_dest->iov_len); + if (error < 0) { + return error; + } + } + + return 0; +} |