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#include <linux/errno.h>
20#include <linux/module.h>
21#include <linux/kernel.h>
22#include <linux/mm.h>
23#include <linux/net.h>
24#include <linux/in6.h>
25#include <asm/uaccess.h>
26#include <asm/byteorder.h>
27#include <net/checksum.h>
28#include <net/sock.h>
29
30#if defined(CONFIG_BCM_RECVFILE)
31#include <typedefs.h>
32#include <bcmdefs.h>
33#endif /* CONFIG_BCM_RECVFILE */
34
35/*
36 *	Verify iovec. The caller must ensure that the iovec is big enough
37 *	to hold the message iovec.
38 *
39 *	Save time not doing access_ok. copy_*_user will make this work
40 *	in any case.
41 */
42
43int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr *address, int mode)
44{
45	int size, ct, err;
46
47	if (m->msg_namelen) {
48		if (mode == VERIFY_READ) {
49			err = move_addr_to_kernel(m->msg_name, m->msg_namelen,
50						  address);
51			if (err < 0)
52				return err;
53		}
54		m->msg_name = address;
55	} else {
56		m->msg_name = NULL;
57	}
58
59	size = m->msg_iovlen * sizeof(struct iovec);
60	if (copy_from_user(iov, m->msg_iov, size))
61		return -EFAULT;
62
63	m->msg_iov = iov;
64	err = 0;
65
66	for (ct = 0; ct < m->msg_iovlen; ct++) {
67		size_t len = iov[ct].iov_len;
68
69		if (len > INT_MAX - err) {
70			len = INT_MAX - err;
71			iov[ct].iov_len = len;
72		}
73		err += len;
74	}
75
76	return err;
77}
78
79/*
80 *	Copy kernel to iovec. Returns -EFAULT on error.
81 *
82 *	Note: this modifies the original iovec.
83 */
84
85int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len)
86{
87	while (len > 0) {
88		if (iov->iov_len) {
89			int copy = min_t(unsigned int, iov->iov_len, len);
90			if (copy_to_user(iov->iov_base, kdata, copy))
91				return -EFAULT;
92			kdata += copy;
93			len -= copy;
94			iov->iov_len -= copy;
95			iov->iov_base += copy;
96		}
97		iov++;
98	}
99
100	return 0;
101}
102EXPORT_SYMBOL(memcpy_toiovec);
103
104/*
105 *	Copy kernel to iovec. Returns -EFAULT on error.
106 */
107
108int memcpy_toiovecend(const struct iovec *iov, unsigned char *kdata,
109		      int offset, int len)
110{
111	int copy;
112	for (; len > 0; ++iov) {
113		/* Skip over the finished iovecs */
114		if (unlikely(offset >= iov->iov_len)) {
115			offset -= iov->iov_len;
116			continue;
117		}
118		copy = min_t(unsigned int, iov->iov_len - offset, len);
119		if (copy_to_user(iov->iov_base + offset, kdata, copy))
120			return -EFAULT;
121		offset = 0;
122		kdata += copy;
123		len -= copy;
124	}
125
126	return 0;
127}
128EXPORT_SYMBOL(memcpy_toiovecend);
129
130#if defined(CONFIG_BCM_RECVFILE)
131/* This was removed in 2.6. Re-add it for splice from socket to file. */
132/*
133 *	In kernel copy to iovec. Returns -EFAULT on error.
134 *
135 *	Note: this modifies the original iovec.
136 */
137
138void BCMFASTPATH_HOST memcpy_tokerneliovec(struct iovec *iov, unsigned char *kdata, int len)
139{
140	while (len > 0) {
141		if (iov->iov_len) {
142			int copy = min_t(unsigned int, iov->iov_len, len);
143
144			memcpy(iov->iov_base, kdata, copy);
145
146			len -= copy;
147			kdata += copy;
148			iov->iov_base += copy;
149			iov->iov_len -= copy;
150		}
151		iov++;
152	}
153}
154EXPORT_SYMBOL(memcpy_tokerneliovec);
155#endif /* CONFIG_BCM_RECVFILE */
156
157/*
158 *	Copy iovec to kernel. Returns -EFAULT on error.
159 *
160 *	Note: this modifies the original iovec.
161 */
162
163int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len)
164{
165	while (len > 0) {
166		if (iov->iov_len) {
167			int copy = min_t(unsigned int, len, iov->iov_len);
168			if (copy_from_user(kdata, iov->iov_base, copy))
169				return -EFAULT;
170			len -= copy;
171			kdata += copy;
172			iov->iov_base += copy;
173			iov->iov_len -= copy;
174		}
175		iov++;
176	}
177
178	return 0;
179}
180EXPORT_SYMBOL(memcpy_fromiovec);
181
182/*
183 *	Copy iovec from kernel. Returns -EFAULT on error.
184 */
185
186int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov,
187			int offset, int len)
188{
189	/* Skip over the finished iovecs */
190	while (offset >= iov->iov_len) {
191		offset -= iov->iov_len;
192		iov++;
193	}
194
195	while (len > 0) {
196		u8 __user *base = iov->iov_base + offset;
197		int copy = min_t(unsigned int, len, iov->iov_len - offset);
198
199		offset = 0;
200		if (copy_from_user(kdata, base, copy))
201			return -EFAULT;
202		len -= copy;
203		kdata += copy;
204		iov++;
205	}
206
207	return 0;
208}
209EXPORT_SYMBOL(memcpy_fromiovecend);
210
211/*
212 *	And now for the all-in-one: copy and checksum from a user iovec
213 *	directly to a datagram
214 *	Calls to csum_partial but the last must be in 32 bit chunks
215 *
216 *	ip_build_xmit must ensure that when fragmenting only the last
217 *	call to this function will be unaligned also.
218 */
219int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov,
220				 int offset, unsigned int len, __wsum *csump)
221{
222	__wsum csum = *csump;
223	int partial_cnt = 0, err = 0;
224
225	/* Skip over the finished iovecs */
226	while (offset >= iov->iov_len) {
227		offset -= iov->iov_len;
228		iov++;
229	}
230
231	while (len > 0) {
232		u8 __user *base = iov->iov_base + offset;
233		int copy = min_t(unsigned int, len, iov->iov_len - offset);
234
235		offset = 0;
236
237		/* There is a remnant from previous iov. */
238		if (partial_cnt) {
239			int par_len = 4 - partial_cnt;
240
241			/* iov component is too short ... */
242			if (par_len > copy) {
243				if (copy_from_user(kdata, base, copy))
244					goto out_fault;
245				kdata += copy;
246				base += copy;
247				partial_cnt += copy;
248				len -= copy;
249				iov++;
250				if (len)
251					continue;
252				*csump = csum_partial(kdata - partial_cnt,
253							 partial_cnt, csum);
254				goto out;
255			}
256			if (copy_from_user(kdata, base, par_len))
257				goto out_fault;
258			csum = csum_partial(kdata - partial_cnt, 4, csum);
259			kdata += par_len;
260			base  += par_len;
261			copy  -= par_len;
262			len   -= par_len;
263			partial_cnt = 0;
264		}
265
266		if (len > copy) {
267			partial_cnt = copy % 4;
268			if (partial_cnt) {
269				copy -= partial_cnt;
270				if (copy_from_user(kdata + copy, base + copy,
271						partial_cnt))
272					goto out_fault;
273			}
274		}
275
276		if (copy) {
277			csum = csum_and_copy_from_user(base, kdata, copy,
278							csum, &err);
279			if (err)
280				goto out;
281		}
282		len   -= copy + partial_cnt;
283		kdata += copy + partial_cnt;
284		iov++;
285	}
286	*csump = csum;
287out:
288	return err;
289
290out_fault:
291	err = -EFAULT;
292	goto out;
293}
294EXPORT_SYMBOL(csum_partial_copy_fromiovecend);
295