1 /*
2 * this code is taken from Michael - the qemu code is GPLv2 so I don't want
3 * to reuse it.
4 * I've adapted it to handle offsets and callback
5 */
6
7 //
8 // iovec.c
9 //
10 // Scatter/gather utility routines
11 //
12 // Copyright (C) 2002 Michael Ringgaard. All rights reserved.
13 //
14 // Redistribution and use in source and binary forms, with or without
15 // modification, are permitted provided that the following conditions
16 // are met:
17 //
18 // 1. Redistributions of source code must retain the above copyright
19 // notice, this list of conditions and the following disclaimer.
20 // 2. Redistributions in binary form must reproduce the above copyright
21 // notice, this list of conditions and the following disclaimer in the
22 // documentation and/or other materials provided with the distribution.
23 // 3. Neither the name of the project nor the names of its contributors
24 // may be used to endorse or promote products derived from this software
25 // without specific prior written permission.
26 //
27 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
28 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
31 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 // SUCH DAMAGE.
38 //
39
40 #include <assert.h>
41 #include <string.h>
42 #include <stdlib.h>
43 #include <stdbool.h>
44 #include "vrend_iov.h"
45
vrend_get_iovec_size(const struct iovec * iov,int iovlen)46 size_t vrend_get_iovec_size(const struct iovec *iov, int iovlen) {
47 size_t size = 0;
48
49 while (iovlen > 0) {
50 size += iov->iov_len;
51 iov++;
52 iovlen--;
53 }
54
55 return size;
56 }
57
vrend_read_from_iovec(const struct iovec * iov,int iovlen,size_t offset,char * buf,size_t count)58 size_t vrend_read_from_iovec(const struct iovec *iov, int iovlen,
59 size_t offset,
60 char *buf, size_t count)
61 {
62 size_t read = 0;
63 size_t len;
64
65 while (count > 0 && iovlen > 0) {
66 if (iov->iov_len > offset) {
67 len = iov->iov_len - offset;
68
69 if (count < len) len = count;
70
71 memcpy(buf, (char*)iov->iov_base + offset, len);
72 read += len;
73
74 buf += len;
75 count -= len;
76 offset = 0;
77 } else {
78 offset -= iov->iov_len;
79 }
80
81 iov++;
82 iovlen--;
83 }
84 assert(offset == 0);
85 return read;
86 }
87
vrend_write_to_iovec(const struct iovec * iov,int iovlen,size_t offset,const char * buf,size_t count)88 size_t vrend_write_to_iovec(const struct iovec *iov, int iovlen,
89 size_t offset, const char *buf, size_t count)
90 {
91 size_t written = 0;
92 size_t len;
93
94 while (count > 0 && iovlen > 0) {
95 if (iov->iov_len > offset) {
96 len = iov->iov_len - offset;
97
98 if (count < len) len = count;
99
100 memcpy((char*)iov->iov_base + offset, buf, len);
101 written += len;
102
103 offset = 0;
104 buf += len;
105 count -= len;
106 } else {
107 offset -= iov->iov_len;
108 }
109 iov++;
110 iovlen--;
111 }
112 assert(offset == 0);
113 return written;
114 }
115
vrend_read_from_iovec_cb(const struct iovec * iov,int iovlen,size_t offset,size_t count,iov_cb iocb,void * cookie)116 size_t vrend_read_from_iovec_cb(const struct iovec *iov, int iovlen,
117 size_t offset, size_t count,
118 iov_cb iocb, void *cookie)
119 {
120 size_t read = 0;
121 size_t len;
122
123 while (count > 0 && iovlen > 0) {
124 if (iov->iov_len > offset) {
125 len = iov->iov_len - offset;
126
127 if (count < len) len = count;
128
129 (*iocb)(cookie, read, (char*)iov->iov_base + offset, len);
130 read += len;
131
132 count -= len;
133 offset = 0;
134 } else {
135 offset -= iov->iov_len;
136 }
137 iov++;
138 iovlen--;
139 }
140 assert(offset == 0);
141 return read;
142
143
144 }
145
146 /**
147 * Copy data from one iovec to another iovec.
148 *
149 * TODO: Implement iovec copy without copy to intermediate buffer.
150 *
151 * \param src_iov The source iov.
152 * \param src_iovlen The number of memory regions in the source iov.
153 * \param src_offset The byte offset in the source iov to start reading from.
154 * \param dst_iov The destination iov.
155 * \param dst_iovlen The number of memory regions in the destination iov.
156 * \param dst_offset The byte offset in the destination iov to start writing to.
157 * \param count The number of bytes to copy
158 * \param buf If not NULL, a pointer to a buffer of at least count size
159 * to use a temporary storage for the copy operation.
160 * \return -1 on failure, 0 on success
161 */
vrend_copy_iovec(const struct iovec * src_iov,int src_iovlen,size_t src_offset,const struct iovec * dst_iov,int dst_iovlen,size_t dst_offset,size_t count,char * buf)162 int vrend_copy_iovec(const struct iovec *src_iov, int src_iovlen, size_t src_offset,
163 const struct iovec *dst_iov, int dst_iovlen, size_t dst_offset,
164 size_t count, char *buf)
165 {
166 int ret = 0;
167 bool needs_free;
168 size_t nread;
169 size_t nwritten;
170
171 if (src_iov == NULL || dst_iov == NULL)
172 return -1;
173
174 if (src_iov == dst_iov && src_offset == dst_offset)
175 return 0;
176
177 if (!buf) {
178 buf = malloc(count);
179 needs_free = true;
180 } else {
181 needs_free = false;
182 }
183
184 if (!buf)
185 return -1;
186
187 nread = vrend_read_from_iovec(src_iov, src_iovlen, src_offset, buf, count);
188 if (nread != count) {
189 ret = -1;
190 goto out;
191 }
192
193 nwritten = vrend_write_to_iovec(dst_iov, dst_iovlen, dst_offset, buf, count);
194 if (nwritten != count) {
195 ret = -1;
196 goto out;
197 }
198
199 out:
200 if (needs_free)
201 free(buf);
202
203 return ret;
204 }
205