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
_vectorio_setup(const struct iovec * iov,int iovcnt,char ** ptr,int op)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
_vectorio_cleanup(const struct iovec * iov,int iovcnt,char * buffer,ssize_t r,int op)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
readv(int fd,const struct iovec * iov,int iovcnt)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
writev(int fd,const struct iovec * iov,int iovcnt)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