• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 /*
31  *	Verify iovec. The caller must ensure that the iovec is big enough
32  *	to hold the message iovec.
33  *
34  *	Save time not doing access_ok. copy_*_user will make this work
35  *	in any case.
36  */
37 
verify_iovec(struct msghdr * m,struct iovec * iov,struct sockaddr_storage * address,int mode)38 int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *address, int mode)
39 {
40 	int size, ct, err;
41 
42 	if (m->msg_name && m->msg_namelen) {
43 		if (mode == VERIFY_READ) {
44 			void __user *namep;
45 			namep = (void __user __force *) m->msg_name;
46 			err = move_addr_to_kernel(namep, m->msg_namelen,
47 						  address);
48 			if (err < 0)
49 				return err;
50 		}
51 		m->msg_name = address;
52 	} else {
53 		m->msg_name = NULL;
54 		m->msg_namelen = 0;
55 	}
56 
57 	size = m->msg_iovlen * sizeof(struct iovec);
58 	if (copy_from_user(iov, (void __user __force *) m->msg_iov, size))
59 		return -EFAULT;
60 
61 	m->msg_iov = iov;
62 	err = 0;
63 
64 	for (ct = 0; ct < m->msg_iovlen; ct++) {
65 		size_t len = iov[ct].iov_len;
66 
67 		if (len > INT_MAX - err) {
68 			len = INT_MAX - err;
69 			iov[ct].iov_len = len;
70 		}
71 		err += len;
72 	}
73 
74 	return err;
75 }
76 
77 /*
78  *	And now for the all-in-one: copy and checksum from a user iovec
79  *	directly to a datagram
80  *	Calls to csum_partial but the last must be in 32 bit chunks
81  *
82  *	ip_build_xmit must ensure that when fragmenting only the last
83  *	call to this function will be unaligned also.
84  */
csum_partial_copy_fromiovecend(unsigned char * kdata,struct iovec * iov,int offset,unsigned int len,__wsum * csump)85 int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov,
86 				 int offset, unsigned int len, __wsum *csump)
87 {
88 	__wsum csum = *csump;
89 	int partial_cnt = 0, err = 0;
90 
91 	/* Skip over the finished iovecs */
92 	while (offset >= iov->iov_len) {
93 		offset -= iov->iov_len;
94 		iov++;
95 	}
96 
97 	while (len > 0) {
98 		u8 __user *base = iov->iov_base + offset;
99 		int copy = min_t(unsigned int, len, iov->iov_len - offset);
100 
101 		offset = 0;
102 
103 		/* There is a remnant from previous iov. */
104 		if (partial_cnt) {
105 			int par_len = 4 - partial_cnt;
106 
107 			/* iov component is too short ... */
108 			if (par_len > copy) {
109 				if (copy_from_user(kdata, base, copy))
110 					goto out_fault;
111 				kdata += copy;
112 				base += copy;
113 				partial_cnt += copy;
114 				len -= copy;
115 				iov++;
116 				if (len)
117 					continue;
118 				*csump = csum_partial(kdata - partial_cnt,
119 							 partial_cnt, csum);
120 				goto out;
121 			}
122 			if (copy_from_user(kdata, base, par_len))
123 				goto out_fault;
124 			csum = csum_partial(kdata - partial_cnt, 4, csum);
125 			kdata += par_len;
126 			base  += par_len;
127 			copy  -= par_len;
128 			len   -= par_len;
129 			partial_cnt = 0;
130 		}
131 
132 		if (len > copy) {
133 			partial_cnt = copy % 4;
134 			if (partial_cnt) {
135 				copy -= partial_cnt;
136 				if (copy_from_user(kdata + copy, base + copy,
137 						partial_cnt))
138 					goto out_fault;
139 			}
140 		}
141 
142 		if (copy) {
143 			csum = csum_and_copy_from_user(base, kdata, copy,
144 							csum, &err);
145 			if (err)
146 				goto out;
147 		}
148 		len   -= copy + partial_cnt;
149 		kdata += copy + partial_cnt;
150 		iov++;
151 	}
152 	*csump = csum;
153 out:
154 	return err;
155 
156 out_fault:
157 	err = -EFAULT;
158 	goto out;
159 }
160 EXPORT_SYMBOL(csum_partial_copy_fromiovecend);
161 
iov_pages(const struct iovec * iov,int offset,unsigned long nr_segs)162 unsigned long iov_pages(const struct iovec *iov, int offset,
163 			unsigned long nr_segs)
164 {
165 	unsigned long seg, base;
166 	int pages = 0, len, size;
167 
168 	while (nr_segs && (offset >= iov->iov_len)) {
169 		offset -= iov->iov_len;
170 		++iov;
171 		--nr_segs;
172 	}
173 
174 	for (seg = 0; seg < nr_segs; seg++) {
175 		base = (unsigned long)iov[seg].iov_base + offset;
176 		len = iov[seg].iov_len - offset;
177 		size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT;
178 		pages += size;
179 		offset = 0;
180 	}
181 
182 	return pages;
183 }
184 EXPORT_SYMBOL(iov_pages);
185