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