1 /*
2 * iovec manipulation routines.
3 *
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version
8 * 2 of the License, or (at your option) any later version.
9 *
10 * Fixes:
11 * Andrew Lunn : Errors in iovec copying.
12 * Pedro Roque : Added memcpy_fromiovecend and
13 * csum_..._fromiovecend.
14 * Andi Kleen : fixed error handling for 2.1
15 * Alexey Kuznetsov: 2.1 optimisations
16 * Andi Kleen : Fix csum*fromiovecend for IPv6.
17 */
18
19
20 #include <linux/errno.h>
21 #include <linux/sched.h>
22 #include <linux/kernel.h>
23 #include <linux/mm.h>
24 #include <linux/malloc.h>
25 #include <linux/net.h>
26 #include <linux/in6.h>
27 #include <asm/uaccess.h>
28 #include <asm/byteorder.h>
29 #include <net/checksum.h>
30 #include <net/sock.h>
31
32 /*
33 * Verify iovec. The caller must ensure that the iovec is big enough
34 * to hold the message iovec.
35 *
36 * Save time not doing verify_area. copy_*_user will make this work
37 * in any case.
38 */
39
40 int verify_iovec(struct msghdr *m, struct iovec *iov, char *address, int mode)
41 {
42 int size, err, ct;
43
44 if(m->msg_namelen)
45 {
46 if(mode==VERIFY_READ)
47 {
48 err=move_addr_to_kernel(m->msg_name, m->msg_namelen, address);
49 if(err<0)
50 goto out;
51 }
52
53 m->msg_name = address;
54 } else
55 m->msg_name = NULL;
56
57 err = -EFAULT;
58 size = m->msg_iovlen * sizeof(struct iovec);
59 if (copy_from_user(iov, m->msg_iov, size))
60 goto out;
61 m->msg_iov=iov;
62
63 for (err = 0, ct = 0; ct < m->msg_iovlen; ct++) {
64 err += iov[ct].iov_len;
65 /* Goal is not to verify user data, but to prevent returning
66 negative value, which is interpreted as errno.
67 Overflow is still possible, but it is harmless.
68 */
69 if (err < 0)
70 return -EMSGSIZE;
71 }
72 out:
73 return err;
74 }
75
76 /*
77 * Copy kernel to iovec. Returns -EFAULT on error.
78 *
79 * Note: this modifies the original iovec.
80 */
81
82 int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len)
83 {
84 int err = -EFAULT;
85
86 while(len>0)
87 {
88 if(iov->iov_len)
89 {
90 int copy = min(iov->iov_len, len);
91 if (copy_to_user(iov->iov_base, kdata, copy))
92 goto out;
93 kdata+=copy;
94 len-=copy;
95 iov->iov_len-=copy;
96 iov->iov_base+=copy;
97 }
98 iov++;
99 }
100 err = 0;
101 out:
102 return err;
103 }
104
105 /* Copy and checkum skb to user iovec. Caller _must_ check that
106 skb will fit to this iovec.
107
108 Returns: 0 - success.
109 -EINVAL - checksum failure.
110 -EFAULT - fault during copy. Beware, in this case iovec can be
111 modified!
112 */
113
114 int copy_and_csum_toiovec(struct iovec *iov, struct sk_buff *skb, int hlen)
115 {
116 unsigned int csum;
117 int chunk = skb->len - hlen;
118
119 /* Skip filled elements. Pretty silly, look at memcpy_toiovec, though 8) */
120 while (iov->iov_len == 0)
121 iov++;
122
123 if (iov->iov_len < chunk) {
124 if ((unsigned short)csum_fold(csum_partial(skb->h.raw, chunk+hlen, skb->csum)))
125 goto csum_error;
126 if (memcpy_toiovec(iov, skb->h.raw + hlen, chunk))
127 goto fault;
128 } else {
129 int err = 0;
130 csum = csum_partial(skb->h.raw, hlen, skb->csum);
131 csum = csum_and_copy_to_user(skb->h.raw+hlen, iov->iov_base,
132 chunk, csum, &err);
133 if (err || ((unsigned short)csum_fold(csum)))
134 goto csum_error;
135 iov->iov_len -= chunk;
136 iov->iov_base += chunk;
137 }
138 return 0;
139
140 csum_error:
141 return -EINVAL;
142
143 fault:
144 return -EFAULT;
145 }
146
147 /*
148 * In kernel copy to iovec. Returns -EFAULT on error.
149 *
150 * Note: this modifies the original iovec.
151 */
152
153 void memcpy_tokerneliovec(struct iovec *iov, unsigned char *kdata, int len)
154 {
155 while(len>0)
156 {
157 if(iov->iov_len)
158 {
159 int copy = min(iov->iov_len, len);
160 memcpy(iov->iov_base, kdata, copy);
161 kdata+=copy;
162 len-=copy;
163 iov->iov_len-=copy;
164 iov->iov_base+=copy;
165 }
166 iov++;
167 }
168 }
169
170
171 /*
172 * Copy iovec to kernel. Returns -EFAULT on error.
173 *
174 * Note: this modifies the original iovec.
175 */
176
177 int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len)
178 {
179 int err = -EFAULT;
180
181 while(len>0)
182 {
183 if(iov->iov_len)
184 {
185 int copy = min(len, iov->iov_len);
186 if (copy_from_user(kdata, iov->iov_base, copy))
187 goto out;
188 len-=copy;
189 kdata+=copy;
190 iov->iov_base+=copy;
191 iov->iov_len-=copy;
192 }
193 iov++;
194 }
195 err = 0;
196 out:
197 return err;
198 }
199
200
201 /*
202 * For use with ip_build_xmit
203 */
204
205 int memcpy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset,
206 int len)
207 {
208 int err = -EFAULT;
209
210 /* Skip over the finished iovecs */
211 while(offset >= iov->iov_len)
212 {
213 offset -= iov->iov_len;
214 iov++;
215 }
216
217 while (len > 0)
218 {
219 u8 *base = iov->iov_base + offset;
220 int copy = min(len, iov->iov_len - offset);
221
222 offset = 0;
223 if (copy_from_user(kdata, base, copy))
224 goto out;
225 len -= copy;
226 kdata += copy;
227 iov++;
228 }
229 err = 0;
230 out:
231 return err;
232 }
233
234 /*
235 * And now for the all-in-one: copy and checksum from a user iovec
236 * directly to a datagram
237 * Calls to csum_partial but the last must be in 32 bit chunks
238 *
239 * ip_build_xmit must ensure that when fragmenting only the last
240 * call to this function will be unaligned also.
241 */
242
243 int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov,
244 int offset, unsigned int len, int *csump)
245 {
246 int csum = *csump;
247 int partial_cnt = 0, err = 0;
248
249 /* Skip over the finished iovecs */
250 while (offset >= iov->iov_len)
251 {
252 offset -= iov->iov_len;
253 iov++;
254 }
255
256 while (len > 0)
257 {
258 u8 *base = iov->iov_base + offset;
259 unsigned int copy = min(len, iov->iov_len - offset);
260
261 offset = 0;
262 /* There is a remnant from previous iov. */
263 if (partial_cnt)
264 {
265 int par_len = 4 - partial_cnt;
266
267 /* iov component is too short ... */
268 if (par_len > copy) {
269 if (copy_from_user(kdata, base, copy))
270 goto out_fault;
271 kdata += copy;
272 base += copy;
273 partial_cnt += copy;
274 len -= copy;
275 iov++;
276 if (len)
277 continue;
278 *csump = csum_partial(kdata - partial_cnt,
279 partial_cnt, csum);
280 goto out;
281 }
282 if (copy_from_user(kdata, base, par_len))
283 goto out_fault;
284 csum = csum_partial(kdata - partial_cnt, 4, csum);
285 kdata += par_len;
286 base += par_len;
287 copy -= par_len;
288 len -= par_len;
289 partial_cnt = 0;
290 }
291
292 if (len > copy)
293 {
294 partial_cnt = copy % 4;
295 if (partial_cnt)
296 {
297 copy -= partial_cnt;
298 if (copy_from_user(kdata + copy, base + copy,
299 partial_cnt))
300 goto out_fault;
301 }
302 }
303
304 if (copy) {
305 csum = csum_and_copy_from_user(base, kdata, copy,
306 csum, &err);
307 if (err)
308 goto out;
309 }
310 len -= copy + partial_cnt;
311 kdata += copy + partial_cnt;
312 iov++;
313 }
314 *csump = csum;
315 out:
316 return err;
317
318 out_fault:
319 err = -EFAULT;
320 goto out;
321 }
322
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.