libc: implement 'readv()'

This patch implements the 'readv()' function in the libc.

A lock guard prevents the parallel execution of either or both of the
'readv()' and 'writev()' functions.

Fixes #279.
This commit is contained in:
Christian Prochaska 2012-07-12 17:08:59 +02:00 committed by Norman Feske
parent 9124f303f7
commit dbd1c425bf
4 changed files with 149 additions and 76 deletions

View File

@ -14,7 +14,7 @@ SRC_CC = atexit.cc dummies.cc rlimit.cc sysctl.cc readlink.cc munmap.cc \
issetugid.cc errno.cc gai_strerror.cc clock_gettime.cc \
gettimeofday.cc malloc.cc progname.cc fd_alloc.cc file_operations.cc \
plugin.cc plugin_registry.cc select.cc exit.cc environ.cc nanosleep.cc \
libc_mem_alloc.cc writev.cc pread_pwrite.cc
libc_mem_alloc.cc pread_pwrite.cc readv_writev.cc
#
# Files from string library that are not included in libc-raw_string because

View File

@ -0,0 +1,117 @@
/*
* \brief 'readv()' and 'writev()' implementations
* \author Josef Soentgen
* \author Christian Prochaska
* \date 2012-04-10
*/
/*
* Copyright (C) 2012 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <base/lock.h>
/* libc includes */
#include <sys/uio.h>
#include <limits.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
static Genode::Lock rw_lock;
struct Read
{
ssize_t operator()(int fd, void *buf, size_t count)
{
return read(fd, buf, count);
}
};
struct Write
{
ssize_t operator()(int fd, const void *buf, size_t count)
{
return write(fd, buf, count);
}
};
template <typename Rw_func>
static ssize_t readv_writev_impl(Rw_func rw_func, int fd, const struct iovec *iov, int iovcnt)
{
Genode::Lock_guard<Genode::Lock> rw_lock_guard(rw_lock);
char *v;
ssize_t bytes_transfered_total = 0;
size_t v_len = 0;
int i;
if (iovcnt < 1 || iovcnt > IOV_MAX) {
errno = EINVAL;
return -1;
}
for (i = 0; i < iovcnt; i++)
v_len += iov->iov_len;
if (v_len > SSIZE_MAX) {
errno = EINVAL;
return -1;
}
while (iovcnt > 0) {
v = static_cast<char *>(iov->iov_base);
v_len = iov->iov_len;
while (v_len > 0) {
ssize_t bytes_transfered = rw_func(fd, v, v_len);
if (bytes_transfered == -1)
return -1;
if (bytes_transfered == 0)
return bytes_transfered_total;
v_len -= bytes_transfered;
v += bytes_transfered;
bytes_transfered_total += bytes_transfered;
}
iov++;
iovcnt--;
}
return bytes_transfered_total;
}
extern "C" ssize_t _readv(int fd, const struct iovec *iov, int iovcnt)
{
return readv_writev_impl(Read(), fd, iov, iovcnt);
}
extern "C" ssize_t readv(int fd, const struct iovec *iov, int iovcnt)
{
return _readv(fd, iov, iovcnt);
}
extern "C" ssize_t _writev(int fd, const struct iovec *iov, int iovcnt)
{
return readv_writev_impl(Write(), fd, iov, iovcnt);
}
extern "C" ssize_t writev(int fd, const struct iovec *iov, int iovcnt)
{
return _writev(fd, iov, iovcnt);
}

View File

@ -1,75 +0,0 @@
/*
* \brief Plugin implementation
* \author Josef Soentgen
* \date 2012-04-10
*/
/*
* Copyright (C) 2010-2012 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include <sys/uio.h>
#include <limits.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
enum { MAX_BUFFER_LEN = 2048 };
extern "C" int _writev(int d, const struct iovec *iov, int iovcnt)
{
char buffer[MAX_BUFFER_LEN];
char *v, *b;
ssize_t written;
size_t v_len, _len;
int i;
if (iovcnt < 1 || iovcnt > IOV_MAX) {
return -EINVAL;
}
for (i = 0; i < iovcnt; i++)
v_len += iov->iov_len;
if (v_len > SSIZE_MAX) {
return -EINVAL;
}
b = buffer;
while (iovcnt) {
v = static_cast<char *>(iov->iov_base);
v_len = iov->iov_len;
while (v_len > 0) {
if (v_len < sizeof(buffer))
_len = v_len;
else
_len = sizeof(buffer);
// TODO error check
memmove(b, v, _len);
i = write(d, b, _len);
v += _len;
v_len -= _len;
written += i;
}
iov++;
iovcnt--;
}
return written;
}
extern "C" ssize_t writev(int d, const struct iovec *iov, int iovcnt)
{
return _writev(d, iov, iovcnt);
}

View File

@ -20,6 +20,7 @@
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
@ -44,6 +45,7 @@ int main(int argc, char *argv[])
char const *dir_name = "/testdir";
char const *file_name = "test.tst";
char const *file_name2 = "test2.tst";
char const *file_name3 = "test3.tst";
char const *pattern = "a single line of text";
size_t pattern_size = strlen(pattern) + 1;
@ -109,6 +111,35 @@ int main(int argc, char *argv[])
printf("file content is correct\n");
}
/* test 'readv()' and 'writev()' */
CALL_AND_CHECK(fd, open(file_name3, O_CREAT | O_WRONLY), fd >= 0, "file_name=%s", file_name);
struct iovec iov[2];
/* write "a single line" */
iov[0].iov_base = (void*)pattern;
iov[0].iov_len = 13;
/* write " line of text" */
iov[1].iov_base = (void*)&pattern[8];
iov[1].iov_len = pattern_size - 8;
CALL_AND_CHECK(count, writev(fd, iov, 2), (size_t)count == (pattern_size + 5), "");
CALL_AND_CHECK(ret, close(fd), ret == 0, "");
CALL_AND_CHECK(fd, open(file_name3, O_RDONLY), fd >= 0, "file_name=%s", file_name);
memset(buf, 0, sizeof(buf));
/* read "a single line" */
iov[0].iov_base = buf;
iov[0].iov_len = 13;
/* read " line of text" to offset 8 */
iov[1].iov_base = &buf[8];
iov[1].iov_len = pattern_size;
CALL_AND_CHECK(count, readv(fd, iov, 2), (size_t)count == (pattern_size + 5), "");
CALL_AND_CHECK(ret, close(fd), ret == 0, "");
printf("content of buffer: \"%s\"\n", buf);
if (strcmp(buf, pattern) != 0) {
printf("unexpected content of file\n");
return -1;
} else {
printf("file content is correct\n");
}
/* read directory entries */
DIR *dir;
CALL_AND_CHECK(dir, opendir(dir_name), dir, "dir_name=\"%s\"", dir_name);