iovec.c revision 8acfe468b0384e834a303f08ebc4953d72fb690a
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 38int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr *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 82int 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} 99EXPORT_SYMBOL(memcpy_toiovec); 100 101/* 102 * Copy kernel to iovec. Returns -EFAULT on error. 103 */ 104 105int 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} 125EXPORT_SYMBOL(memcpy_toiovecend); 126 127/* 128 * Copy iovec to kernel. Returns -EFAULT on error. 129 * 130 * Note: this modifies the original iovec. 131 */ 132 133int 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} 150EXPORT_SYMBOL(memcpy_fromiovec); 151 152/* 153 * Copy iovec from kernel. Returns -EFAULT on error. 154 */ 155 156int 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} 179EXPORT_SYMBOL(memcpy_fromiovecend); 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 */ 189int 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; 257out: 258 return err; 259 260out_fault: 261 err = -EFAULT; 262 goto out; 263} 264EXPORT_SYMBOL(csum_partial_copy_fromiovecend); 265