~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/net/core/iovec.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.