128 lines
2.6 KiB
C
128 lines
2.6 KiB
C
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/uio.h>
|
|
#include <unistd.h>
|
|
|
|
#define VECTORIO_READ 1
|
|
#define VECTORIO_WRITE 2
|
|
|
|
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
|
|
|
static ssize_t vectorio_buffer(int fildes, const struct iovec *iov,
|
|
int iovcnt, int readwrite, ssize_t totallen)
|
|
{
|
|
char *buffer;
|
|
int iovidx, errno_saved;
|
|
ssize_t copied, len, r;
|
|
|
|
/* allocate buffer */
|
|
buffer = (char *) malloc(totallen);
|
|
if (!buffer)
|
|
return -1;
|
|
|
|
/* perform the actual read/write for the entire buffer */
|
|
switch (readwrite)
|
|
{
|
|
case VECTORIO_READ:
|
|
/* first read, then copy buffers (only part read) */
|
|
r = read(fildes, buffer, totallen);
|
|
|
|
copied = 0;
|
|
iovidx = 0;
|
|
while (copied < r)
|
|
{
|
|
assert(iovidx < iovcnt);
|
|
len = MIN(r - copied, iov[iovidx].iov_len);
|
|
memcpy(iov[iovidx++].iov_base, buffer + copied, len);
|
|
copied += len;
|
|
}
|
|
assert(r < 0 || r == copied);
|
|
break;
|
|
|
|
case VECTORIO_WRITE:
|
|
/* first copy buffers, then write */
|
|
copied = 0;
|
|
for (iovidx = 0; iovidx < iovcnt; iovidx++)
|
|
{
|
|
memcpy(buffer + copied, iov[iovidx].iov_base,
|
|
iov[iovidx].iov_len);
|
|
copied += iov[iovidx].iov_len;
|
|
}
|
|
assert(copied == totallen);
|
|
|
|
r = write(fildes, buffer, totallen);
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
errno = EINVAL;
|
|
r = -1;
|
|
}
|
|
|
|
/* free the buffer, keeping errno unchanged */
|
|
errno_saved = errno;
|
|
free(buffer);
|
|
errno = errno_saved;
|
|
|
|
return r;
|
|
}
|
|
|
|
static ssize_t vectorio(int fildes, const struct iovec *iov,
|
|
int iovcnt, int readwrite)
|
|
{
|
|
int i;
|
|
struct stat statbuf;
|
|
ssize_t totallen;
|
|
|
|
/* parameter sanity checks */
|
|
if (iovcnt > IOV_MAX)
|
|
{
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
totallen = 0;
|
|
for (i = 0; i < iovcnt; i++)
|
|
{
|
|
/* don't read/write anything in case of possible overflow */
|
|
if ((ssize_t) (totallen + iov[i].iov_len) < totallen)
|
|
{
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
totallen += iov[i].iov_len;
|
|
|
|
/* report on NULL pointers */
|
|
if (iov[i].iov_len && !iov[i].iov_base)
|
|
{
|
|
errno = EFAULT;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* anything to do? */
|
|
if (totallen == 0)
|
|
return 0;
|
|
|
|
/*
|
|
* there aught to be a system call here; instead we use an intermediate
|
|
* buffer; this is preferred over multiple read/write calls because
|
|
* this function has to be atomic
|
|
*/
|
|
return vectorio_buffer(fildes, iov, iovcnt, readwrite, totallen);
|
|
}
|
|
|
|
ssize_t readv(int fildes, const struct iovec *iov, int iovcnt)
|
|
{
|
|
return vectorio(fildes, iov, iovcnt, VECTORIO_READ);
|
|
}
|
|
|
|
ssize_t writev(int fildes, const struct iovec *iov, int iovcnt)
|
|
{
|
|
return vectorio(fildes, iov, iovcnt, VECTORIO_WRITE);
|
|
}
|
|
|