1 #include <sys/cdefs.h> 2 #include <lib.h> 3 #include "namespace.h" 4 5 #include <assert.h> 6 #include <errno.h> 7 #include <limits.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <sys/stat.h> 11 #include <sys/param.h> 12 #include <sys/uio.h> 13 #include <unistd.h> 14 15 /* 16 * Create a single temporary buffer for the entire vector. For writes, also 17 * copy the actual data into the temporary buffer. 18 */ 19 ssize_t 20 _vectorio_setup(const struct iovec * iov, int iovcnt, char ** ptr, int op) 21 { 22 char *buffer; 23 ssize_t totallen, copied; 24 int i; 25 26 /* Parameter sanity checks. */ 27 if (iovcnt < 0 || iovcnt > IOV_MAX) { 28 errno = EINVAL; 29 return -1; 30 } 31 32 totallen = 0; 33 for (i = 0; i < iovcnt; i++) { 34 /* Do not read/write anything in case of possible overflow. */ 35 if ((size_t)SSIZE_MAX - totallen < iov[i].iov_len) { 36 errno = EINVAL; 37 return -1; 38 } 39 totallen += iov[i].iov_len; 40 41 /* Report on NULL pointers. */ 42 if (iov[i].iov_len > 0 && iov[i].iov_base == NULL) { 43 errno = EFAULT; 44 return -1; 45 } 46 } 47 48 /* Anything to do? */ 49 if (totallen == 0) { 50 *ptr = NULL; 51 return 0; 52 } 53 54 /* Allocate a temporary buffer. */ 55 buffer = (char *)malloc(totallen); 56 if (buffer == NULL) 57 return -1; 58 59 /* For writes, copy over the buffer contents before the call. */ 60 if (op == _VECTORIO_WRITE) { 61 copied = 0; 62 for (i = 0; i < iovcnt; i++) { 63 memcpy(buffer + copied, iov[i].iov_base, 64 iov[i].iov_len); 65 copied += iov[i].iov_len; 66 } 67 assert(copied == totallen); 68 } 69 70 /* Return the temporary buffer and its size. */ 71 *ptr = buffer; 72 return totallen; 73 } 74 75 /* 76 * Clean up the temporary buffer created for the vector. For successful reads, 77 * also copy out the retrieved buffer contents. 78 */ 79 void 80 _vectorio_cleanup(const struct iovec * iov, int iovcnt, char * buffer, 81 ssize_t r, int op) 82 { 83 int i, errno_saved; 84 ssize_t copied, len; 85 86 /* Make sure to retain the original errno value in case of failure. */ 87 errno_saved = errno; 88 89 /* 90 * If this was for a read and the read call succeeded, copy out the 91 * resulting data. 92 */ 93 if (op == _VECTORIO_READ && r > 0) { 94 assert(buffer != NULL); 95 copied = 0; 96 i = 0; 97 while (copied < r) { 98 assert(i < iovcnt); 99 len = iov[i].iov_len; 100 if (len > r - copied) 101 len = r - copied; 102 memcpy(iov[i++].iov_base, buffer + copied, len); 103 copied += len; 104 } 105 assert(r < 0 || r == copied); 106 } 107 108 /* Free the temporary buffer. */ 109 if (buffer != NULL) 110 free(buffer); 111 112 errno = errno_saved; 113 } 114 115 /* 116 * Read a vector. 117 */ 118 ssize_t 119 readv(int fd, const struct iovec * iov, int iovcnt) 120 { 121 char *ptr; 122 ssize_t r; 123 124 /* 125 * There ought to be just a readv system call here. Instead, we use an 126 * intermediate buffer. This approach is preferred over multiple read 127 * calls, because the actual I/O operation has to be atomic. 128 */ 129 if ((r = _vectorio_setup(iov, iovcnt, &ptr, _VECTORIO_READ)) <= 0) 130 return r; 131 132 r = read(fd, ptr, r); 133 134 _vectorio_cleanup(iov, iovcnt, ptr, r, _VECTORIO_READ); 135 136 return r; 137 } 138 139 /* 140 * Write a vector. 141 */ 142 ssize_t 143 writev(int fd, const struct iovec * iov, int iovcnt) 144 { 145 char *ptr; 146 ssize_t r; 147 148 /* 149 * There ought to be just a writev system call here. Instead, we use 150 * an intermediate buffer. This approach is preferred over multiple 151 * write calls, because the actual I/O operation has to be atomic. 152 */ 153 if ((r = _vectorio_setup(iov, iovcnt, &ptr, _VECTORIO_WRITE)) <= 0) 154 return r; 155 156 r = write(fd, ptr, r); 157 158 _vectorio_cleanup(iov, iovcnt, ptr, r, _VECTORIO_WRITE); 159 160 return r; 161 } 162