From ef32b3fcbe26dc252536ed8249b8881815d78597 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Fri, 3 May 2013 21:43:01 +0200 Subject: [PATCH] Add {,p}{read,write}v(2). --- libc/Makefile | 4 + libc/include/sys/uio.h | 49 +++++++++ libc/preadv.cpp | 34 ++++++ libc/pwritev.cpp | 34 ++++++ libc/readv.cpp | 33 ++++++ libc/writev.cpp | 33 ++++++ sortix/include/sortix/syscallnum.h | 10 +- sortix/include/sortix/uio.h | 40 +++++++ sortix/io.cpp | 168 +++++++++++++++++++++++++++++ 9 files changed, 402 insertions(+), 3 deletions(-) create mode 100644 libc/include/sys/uio.h create mode 100644 libc/preadv.cpp create mode 100644 libc/pwritev.cpp create mode 100644 libc/readv.cpp create mode 100644 libc/writev.cpp create mode 100644 sortix/include/sortix/uio.h diff --git a/libc/Makefile b/libc/Makefile index 207fe962..84f2e49c 100644 --- a/libc/Makefile +++ b/libc/Makefile @@ -289,16 +289,19 @@ pipe.o \ poll.o \ popen.o \ ppoll.o \ +preadv.o \ print.o \ psignal.o \ putc.o \ pwent.o \ +pwritev.o \ raise.o \ rand.o \ readdirents.o \ readlinkat.o \ readlink.o \ read.o \ +readv.o \ realpath.o \ removeat.o \ remove.o \ @@ -365,6 +368,7 @@ wait.o \ waitpid.o \ winsize.o \ write.o \ +writev.o \ OBJS=\ $(FREEOBJS) \ diff --git a/libc/include/sys/uio.h b/libc/include/sys/uio.h new file mode 100644 index 00000000..44616641 --- /dev/null +++ b/libc/include/sys/uio.h @@ -0,0 +1,49 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + sys/uio.h + Vector IO operations. + +*******************************************************************************/ + +#ifndef INCLUDE_SYS_UIO_H +#define INCLUDE_SYS_UIO_H + +#include + +__BEGIN_DECLS + +@include(size_t.h) +@include(ssize_t.h) +@include(off_t.h) + +__END_DECLS + +#include + +__BEGIN_DECLS + +ssize_t readv(int, const struct iovec*, int); +ssize_t writev(int, const struct iovec*, int); +ssize_t preadv(int, const struct iovec*, int, off_t); +ssize_t pwritev(int, const struct iovec*, int, off_t); + +__END_DECLS + +#endif diff --git a/libc/preadv.cpp b/libc/preadv.cpp new file mode 100644 index 00000000..0ae1c9d7 --- /dev/null +++ b/libc/preadv.cpp @@ -0,0 +1,34 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + preadv.cpp + Read data into multiple buffers. + +*******************************************************************************/ + +#include +#include + +DEFN_SYSCALL4(ssize_t, sys_preadv, SYSCALL_PREADV, int, const struct iovec*, int, off_t); + +extern "C" +ssize_t preadv(int fd, const struct iovec* iov, int iovcnt, off_t offset) +{ + return sys_preadv(fd, iov, iovcnt, offset); +} diff --git a/libc/pwritev.cpp b/libc/pwritev.cpp new file mode 100644 index 00000000..ee6c3331 --- /dev/null +++ b/libc/pwritev.cpp @@ -0,0 +1,34 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + pwritev.cpp + Write data from multiple buffers. + +*******************************************************************************/ + +#include +#include + +DEFN_SYSCALL4(ssize_t, sys_pwritev, SYSCALL_PWRITEV, int, const struct iovec*, int, off_t); + +extern "C" +ssize_t pwritev(int fd, const struct iovec* iov, int iovcnt, off_t offset) +{ + return sys_pwritev(fd, iov, iovcnt, offset); +} diff --git a/libc/readv.cpp b/libc/readv.cpp new file mode 100644 index 00000000..b800b23c --- /dev/null +++ b/libc/readv.cpp @@ -0,0 +1,33 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + readv.cpp + Read data into multiple buffers. + +*******************************************************************************/ + +#include +#include + +DEFN_SYSCALL3(ssize_t, sys_readv, SYSCALL_READV, int, const struct iovec*, int); + +extern "C" ssize_t readv(int fd, const struct iovec* iov, int iovcnt) +{ + return sys_readv(fd, iov, iovcnt); +} diff --git a/libc/writev.cpp b/libc/writev.cpp new file mode 100644 index 00000000..26066cdc --- /dev/null +++ b/libc/writev.cpp @@ -0,0 +1,33 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + writev.cpp + Write data from multiple buffers. + +*******************************************************************************/ + +#include +#include + +DEFN_SYSCALL3(ssize_t, sys_writev, SYSCALL_WRITEV, int, const struct iovec*, int); + +extern "C" ssize_t writev(int fd, const struct iovec* iov, int iovcnt) +{ + return sys_writev(fd, iov, iovcnt); +} diff --git a/sortix/include/sortix/syscallnum.h b/sortix/include/sortix/syscallnum.h index 64a2cf46..4e38b594 100644 --- a/sortix/include/sortix/syscallnum.h +++ b/sortix/include/sortix/syscallnum.h @@ -22,8 +22,8 @@ *******************************************************************************/ -#ifndef SORTIX_SYSCALLNUM_H -#define SORTIX_SYSCALLNUM_H +#ifndef INCLUDE_SORTIX_SYSCALLNUM_H +#define INCLUDE_SORTIX_SYSCALLNUM_H #define SYSCALL_BAD_SYSCALL 0 #define SYSCALL_EXIT 1 @@ -114,6 +114,10 @@ #define SYSCALL_BIND 90 #define SYSCALL_CONNECT 91 #define SYSCALL_LISTEN 92 -#define SYSCALL_MAX_NUM 93 /* index of highest constant + 1 */ +#define SYSCALL_READV 93 +#define SYSCALL_WRITEV 94 +#define SYSCALL_PREADV 95 +#define SYSCALL_PWRITEV 96 +#define SYSCALL_MAX_NUM 97 /* index of highest constant + 1 */ #endif diff --git a/sortix/include/sortix/uio.h b/sortix/include/sortix/uio.h new file mode 100644 index 00000000..78a9ecdb --- /dev/null +++ b/sortix/include/sortix/uio.h @@ -0,0 +1,40 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + sortix/uio.h + Vector IO operations. + +*******************************************************************************/ + +#ifndef INCLUDE_SORTIX_UIO_H +#define INCLUDE_SORTIX_UIO_H + +#include + +__BEGIN_DECLS + +struct iovec +{ + void* iov_base; + size_t iov_len; +}; + +__END_DECLS + +#endif diff --git a/sortix/io.cpp b/sortix/io.cpp index 020ea6b4..ba54a028 100644 --- a/sortix/io.cpp +++ b/sortix/io.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -687,6 +688,169 @@ static ssize_t sys_send(int fd, const void* buffer, size_t count, int flags) return desc->send(&ctx, (const uint8_t*) buffer, count, flags); } +// TODO: We need to move these vector operations into the file descriptors or +// inodes themselves to ensure that they are atomic. Currently these +// operations may overlap and cause nasty bugs/race conditions when +// multiple threads concurrently operates on a file. +// TODO: There is quite a bit of boiler plate code here. Can we do better? + +static struct iovec* FetchIOV(const struct iovec* user_iov, int iovcnt) +{ + if ( iovcnt < 0 ) + return errno = EINVAL, (struct iovec*) NULL; + struct iovec* ret = new struct iovec[iovcnt]; + if ( !ret ) + return NULL; + if ( !CopyFromUser(ret, user_iov, sizeof(struct iovec) * (size_t) iovcnt) ) + { + delete[] ret; + return NULL; + } + return ret; +} + +static ssize_t sys_readv(int fd, const struct iovec* user_iov, int iovcnt) +{ + Ref desc = CurrentProcess()->GetDescriptor(fd); + if ( !desc ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + struct iovec* iov = FetchIOV(user_iov, iovcnt); + if ( !iov ) + return -1; + ssize_t so_far = 0; + for ( int i = 0; i < iovcnt && so_far != SSIZE_MAX; i++ ) + { + uint8_t* buffer = (uint8_t*) iov[i].iov_base; + size_t amount = iov[i].iov_len; + ssize_t max_left = SSIZE_MAX - so_far; + if ( (size_t) max_left < amount ) + amount = (size_t) max_left; + ssize_t num_bytes = desc->read(&ctx, buffer, amount); + if ( num_bytes < 0 ) + { + delete[] iov; + return so_far ? so_far : -1; + } + if ( num_bytes == 0 ) + break; + so_far += num_bytes; + + // TODO: Is this the correct behavior? + if ( (size_t) num_bytes != amount ) + break; + } + delete[] iov; + return so_far; +} + +static ssize_t sys_preadv(int fd, const struct iovec* user_iov, int iovcnt, + off_t offset) +{ + Ref desc = CurrentProcess()->GetDescriptor(fd); + if ( !desc ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + struct iovec* iov = FetchIOV(user_iov, iovcnt); + if ( !iov ) + return -1; + ssize_t so_far = 0; + for ( int i = 0; i < iovcnt && so_far != SSIZE_MAX; i++ ) + { + uint8_t* buffer = (uint8_t*) iov[i].iov_base; + size_t amount = iov[i].iov_len; + ssize_t max_left = SSIZE_MAX - so_far; + if ( (size_t) max_left < amount ) + amount = (size_t) max_left; + ssize_t num_bytes = desc->pread(&ctx, buffer, amount, offset + so_far); + if ( num_bytes < 0 ) + { + delete[] iov; + return so_far ? so_far : -1; + } + if ( num_bytes == 0 ) + break; + so_far += num_bytes; + + // TODO: Is this the correct behavior? + if ( (size_t) num_bytes != amount ) + break; + } + delete[] iov; + return so_far; +} + +static ssize_t sys_writev(int fd, const struct iovec* user_iov, int iovcnt) +{ + Ref desc = CurrentProcess()->GetDescriptor(fd); + if ( !desc ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + struct iovec* iov = FetchIOV(user_iov, iovcnt); + if ( !iov ) + return -1; + ssize_t so_far = 0; + for ( int i = 0; i < iovcnt && so_far != SSIZE_MAX; i++ ) + { + const uint8_t* buffer = (const uint8_t*) iov[i].iov_base; + size_t amount = iov[i].iov_len; + ssize_t max_left = SSIZE_MAX - so_far; + if ( (size_t) max_left < amount ) + amount = (size_t) max_left; + ssize_t num_bytes = desc->write(&ctx, buffer, amount); + if ( num_bytes < 0 ) + { + delete[] iov; + return so_far ? so_far : -1; + } + if ( num_bytes == 0 ) + break; + so_far += num_bytes; + + // TODO: Is this the correct behavior? + if ( (size_t) num_bytes != amount ) + break; + } + delete[] iov; + return so_far; +} + +static ssize_t sys_pwritev(int fd, const struct iovec* user_iov, int iovcnt, + off_t offset) +{ + Ref desc = CurrentProcess()->GetDescriptor(fd); + if ( !desc ) + return -1; + ioctx_t ctx; SetupUserIOCtx(&ctx); + struct iovec* iov = FetchIOV(user_iov, iovcnt); + if ( !iov ) + return -1; + ssize_t so_far = 0; + for ( int i = 0; i < iovcnt && so_far != SSIZE_MAX; i++ ) + { + const uint8_t* buffer = (const uint8_t*) iov[i].iov_base; + size_t amount = iov[i].iov_len; + ssize_t max_left = SSIZE_MAX - so_far; + if ( (size_t) max_left < amount ) + amount = (size_t) max_left; + ssize_t num_bytes = desc->pwrite(&ctx, buffer, amount, offset + so_far); + if ( num_bytes < 0 ) + { + delete[] iov; + return so_far ? so_far : -1; + } + if ( num_bytes == 0 ) + break; + so_far += num_bytes; + + // TODO: Is this the correct behavior? + if ( (size_t) num_bytes != amount ) + break; + } + delete[] iov; + return so_far; +} + void Init() { Syscall::Register(SYSCALL_ACCEPT4, (void*) sys_accept4); @@ -722,10 +886,13 @@ void Init() Syscall::Register(SYSCALL_OPENAT, (void*) sys_openat); Syscall::Register(SYSCALL_OPEN, (void*) sys_open); Syscall::Register(SYSCALL_PREAD, (void*) sys_pread); + Syscall::Register(SYSCALL_PREADV, (void*) sys_preadv); Syscall::Register(SYSCALL_PWRITE, (void*) sys_pwrite); + Syscall::Register(SYSCALL_PWRITEV, (void*) sys_pwritev); Syscall::Register(SYSCALL_READDIRENTS, (void*) sys_readdirents); Syscall::Register(SYSCALL_READLINKAT, (void*) sys_readlinkat); Syscall::Register(SYSCALL_READ, (void*) sys_read); + Syscall::Register(SYSCALL_READV, (void*) sys_readv); Syscall::Register(SYSCALL_RECV, (void*) sys_recv); Syscall::Register(SYSCALL_RENAMEAT, (void*) sys_renameat); Syscall::Register(SYSCALL_RMDIR, (void*) sys_rmdir); @@ -740,6 +907,7 @@ void Init() Syscall::Register(SYSCALL_UNLINK, (void*) sys_unlink); Syscall::Register(SYSCALL_UTIMENSAT, (void*) sys_utimensat); Syscall::Register(SYSCALL_WRITE, (void*) sys_write); + Syscall::Register(SYSCALL_WRITEV, (void*) sys_writev); } } // namespace IO