Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
iovec.c
Go to the documentation of this file.
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 
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_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  }
55 
56  size = m->msg_iovlen * sizeof(struct iovec);
57  if (copy_from_user(iov, (void __user __force *) m->msg_iov, size))
58  return -EFAULT;
59 
60  m->msg_iov = iov;
61  err = 0;
62 
63  for (ct = 0; ct < m->msg_iovlen; ct++) {
64  size_t len = iov[ct].iov_len;
65 
66  if (len > INT_MAX - err) {
67  len = INT_MAX - err;
68  iov[ct].iov_len = len;
69  }
70  err += len;
71  }
72 
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  while (len > 0) {
85  if (iov->iov_len) {
86  int copy = min_t(unsigned int, iov->iov_len, len);
87  if (copy_to_user(iov->iov_base, kdata, copy))
88  return -EFAULT;
89  kdata += copy;
90  len -= copy;
91  iov->iov_len -= copy;
92  iov->iov_base += copy;
93  }
94  iov++;
95  }
96 
97  return 0;
98 }
100 
101 /*
102  * Copy kernel to iovec. Returns -EFAULT on error.
103  */
104 
105 int memcpy_toiovecend(const struct iovec *iov, unsigned char *kdata,
106  int offset, int len)
107 {
108  int copy;
109  for (; len > 0; ++iov) {
110  /* Skip over the finished iovecs */
111  if (unlikely(offset >= iov->iov_len)) {
112  offset -= iov->iov_len;
113  continue;
114  }
115  copy = min_t(unsigned int, iov->iov_len - offset, len);
116  if (copy_to_user(iov->iov_base + offset, kdata, copy))
117  return -EFAULT;
118  offset = 0;
119  kdata += copy;
120  len -= copy;
121  }
122 
123  return 0;
124 }
126 
127 /*
128  * Copy iovec to kernel. Returns -EFAULT on error.
129  *
130  * Note: this modifies the original iovec.
131  */
132 
133 int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len)
134 {
135  while (len > 0) {
136  if (iov->iov_len) {
137  int copy = min_t(unsigned int, len, iov->iov_len);
138  if (copy_from_user(kdata, iov->iov_base, copy))
139  return -EFAULT;
140  len -= copy;
141  kdata += copy;
142  iov->iov_base += copy;
143  iov->iov_len -= copy;
144  }
145  iov++;
146  }
147 
148  return 0;
149 }
151 
152 /*
153  * Copy iovec from kernel. Returns -EFAULT on error.
154  */
155 
156 int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov,
157  int offset, int len)
158 {
159  /* Skip over the finished iovecs */
160  while (offset >= iov->iov_len) {
161  offset -= iov->iov_len;
162  iov++;
163  }
164 
165  while (len > 0) {
166  u8 __user *base = iov->iov_base + offset;
167  int copy = min_t(unsigned int, len, iov->iov_len - offset);
168 
169  offset = 0;
170  if (copy_from_user(kdata, base, copy))
171  return -EFAULT;
172  len -= copy;
173  kdata += copy;
174  iov++;
175  }
176 
177  return 0;
178 }
180 
181 /*
182  * And now for the all-in-one: copy and checksum from a user iovec
183  * directly to a datagram
184  * Calls to csum_partial but the last must be in 32 bit chunks
185  *
186  * ip_build_xmit must ensure that when fragmenting only the last
187  * call to this function will be unaligned also.
188  */
189 int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov,
190  int offset, unsigned int len, __wsum *csump)
191 {
192  __wsum csum = *csump;
193  int partial_cnt = 0, err = 0;
194 
195  /* Skip over the finished iovecs */
196  while (offset >= iov->iov_len) {
197  offset -= iov->iov_len;
198  iov++;
199  }
200 
201  while (len > 0) {
202  u8 __user *base = iov->iov_base + offset;
203  int copy = min_t(unsigned int, len, iov->iov_len - offset);
204 
205  offset = 0;
206 
207  /* There is a remnant from previous iov. */
208  if (partial_cnt) {
209  int par_len = 4 - partial_cnt;
210 
211  /* iov component is too short ... */
212  if (par_len > copy) {
213  if (copy_from_user(kdata, base, copy))
214  goto out_fault;
215  kdata += copy;
216  base += copy;
217  partial_cnt += copy;
218  len -= copy;
219  iov++;
220  if (len)
221  continue;
222  *csump = csum_partial(kdata - partial_cnt,
223  partial_cnt, csum);
224  goto out;
225  }
226  if (copy_from_user(kdata, base, par_len))
227  goto out_fault;
228  csum = csum_partial(kdata - partial_cnt, 4, csum);
229  kdata += par_len;
230  base += par_len;
231  copy -= par_len;
232  len -= par_len;
233  partial_cnt = 0;
234  }
235 
236  if (len > copy) {
237  partial_cnt = copy % 4;
238  if (partial_cnt) {
239  copy -= partial_cnt;
240  if (copy_from_user(kdata + copy, base + copy,
241  partial_cnt))
242  goto out_fault;
243  }
244  }
245 
246  if (copy) {
247  csum = csum_and_copy_from_user(base, kdata, copy,
248  csum, &err);
249  if (err)
250  goto out;
251  }
252  len -= copy + partial_cnt;
253  kdata += copy + partial_cnt;
254  iov++;
255  }
256  *csump = csum;
257 out:
258  return err;
259 
260 out_fault:
261  err = -EFAULT;
262  goto out;
263 }