xref: /minix/minix/lib/libc/sys/vectorio.c (revision 045e0ed3)
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