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;
 | |
| 	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);	
 | |
| }
 | |
| 
 | 
