File: /usr/src/linux/net/core/iovec.c

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/slab.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_t(unsigned int, 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     /*
106      *	In kernel copy to iovec. Returns -EFAULT on error.
107      *
108      *	Note: this modifies the original iovec.
109      */
110      
111     void memcpy_tokerneliovec(struct iovec *iov, unsigned char *kdata, int len)
112     {
113     	while(len>0)
114     	{
115     		if(iov->iov_len)
116     		{
117     			int copy = min_t(unsigned int, iov->iov_len, len);
118     			memcpy(iov->iov_base, kdata, copy);
119     			kdata+=copy;
120     			len-=copy;
121     			iov->iov_len-=copy;
122     			iov->iov_base+=copy;
123     		}
124     		iov++;
125     	}
126     }
127     
128     
129     /*
130      *	Copy iovec to kernel. Returns -EFAULT on error.
131      *
132      *	Note: this modifies the original iovec.
133      */
134      
135     int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len)
136     {
137     	int err = -EFAULT; 
138     
139     	while(len>0)
140     	{
141     		if(iov->iov_len)
142     		{
143     			int copy = min_t(unsigned int, len, iov->iov_len);
144     			if (copy_from_user(kdata, iov->iov_base, copy))
145     				goto out;
146     			len-=copy;
147     			kdata+=copy;
148     			iov->iov_base+=copy;
149     			iov->iov_len-=copy;
150     		}
151     		iov++;
152     	}
153     	err = 0;
154     out:
155     	return err; 
156     }
157     
158     
159     /*
160      *	For use with ip_build_xmit
161      */
162     
163     int memcpy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset,
164     			int len)
165     {
166     	int err = -EFAULT;
167     
168     	/* Skip over the finished iovecs */
169     	while(offset >= iov->iov_len)
170     	{
171     		offset -= iov->iov_len;
172     		iov++;
173     	}
174     
175     	while (len > 0)
176     	{
177     		u8 *base = iov->iov_base + offset;
178     		int copy = min_t(unsigned int, len, iov->iov_len - offset);
179     
180     		offset = 0;
181     		if (copy_from_user(kdata, base, copy))
182     			goto out;
183     		len   -= copy;
184     		kdata += copy;
185     		iov++;
186     	}
187     	err = 0;
188     out:
189     	return err;
190     }
191     
192     /*
193      *	And now for the all-in-one: copy and checksum from a user iovec
194      *	directly to a datagram
195      *	Calls to csum_partial but the last must be in 32 bit chunks
196      *
197      *	ip_build_xmit must ensure that when fragmenting only the last
198      *	call to this function will be unaligned also.
199      */
200     
201     int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov,
202     				 int offset, unsigned int len, int *csump)
203     {
204     	int csum = *csump;
205     	int partial_cnt = 0, err = 0;
206     
207     	/* Skip over the finished iovecs */
208     	while (offset >= iov->iov_len)
209     	{
210     		offset -= iov->iov_len;
211     		iov++;
212     	}
213     
214     	while (len > 0)
215     	{
216     		u8 *base = iov->iov_base + offset;
217     		int copy = min_t(unsigned int, len, iov->iov_len - offset);
218     
219     		offset = 0;
220     		/* There is a remnant from previous iov. */
221     		if (partial_cnt)
222     		{
223     			int par_len = 4 - partial_cnt;
224     
225     			/* iov component is too short ... */
226     			if (par_len > copy) {
227     				if (copy_from_user(kdata, base, copy))
228     					goto out_fault;
229     				kdata += copy;
230     				base  += copy;
231     				partial_cnt += copy;
232     				len   -= copy;
233     				iov++;
234     				if (len)
235     					continue;
236     				*csump = csum_partial(kdata - partial_cnt,
237     							 partial_cnt, csum);
238     				goto out;
239     			}
240     			if (copy_from_user(kdata, base, par_len))
241     				goto out_fault;
242     			csum = csum_partial(kdata - partial_cnt, 4, csum);
243     			kdata += par_len;
244     			base  += par_len;
245     			copy  -= par_len;
246     			len   -= par_len;
247     			partial_cnt = 0;
248     		}
249     
250     		if (len > copy)
251     		{
252     			partial_cnt = copy % 4;
253     			if (partial_cnt)
254     			{
255     				copy -= partial_cnt;
256     				if (copy_from_user(kdata + copy, base + copy,
257     				 		partial_cnt))
258     					goto out_fault;
259     			}
260     		}
261     
262     		if (copy) {
263     			csum = csum_and_copy_from_user(base, kdata, copy,
264     							csum, &err);
265     			if (err)
266     				goto out;
267     		}
268     		len   -= copy + partial_cnt;
269     		kdata += copy + partial_cnt;
270     		iov++;
271     	}
272             *csump = csum;
273     out:
274     	return err;
275     
276     out_fault:
277     	err = -EFAULT;
278     	goto out;
279     }
280