iovec.c revision 5a0e3ad6af8660be21ca98a971cd00f331318c05
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, err, ct; 41 42 if (m->msg_namelen) { 43 if (mode == VERIFY_READ) { 44 err = move_addr_to_kernel(m->msg_name, m->msg_namelen, 45 address); 46 if (err < 0) 47 return err; 48 } 49 m->msg_name = address; 50 } else { 51 m->msg_name = NULL; 52 } 53 54 size = m->msg_iovlen * sizeof(struct iovec); 55 if (copy_from_user(iov, m->msg_iov, size)) 56 return -EFAULT; 57 58 m->msg_iov = iov; 59 err = 0; 60 61 for (ct = 0; ct < m->msg_iovlen; ct++) { 62 err += iov[ct].iov_len; 63 /* 64 * Goal is not to verify user data, but to prevent returning 65 * negative value, which is interpreted as errno. 66 * Overflow is still possible, but it is harmless. 67 */ 68 if (err < 0) 69 return -EMSGSIZE; 70 } 71 72 return err; 73} 74 75/* 76 * Copy kernel to iovec. Returns -EFAULT on error. 77 * 78 * Note: this modifies the original iovec. 79 */ 80 81int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len) 82{ 83 while (len > 0) { 84 if (iov->iov_len) { 85 int copy = min_t(unsigned int, iov->iov_len, len); 86 if (copy_to_user(iov->iov_base, kdata, copy)) 87 return -EFAULT; 88 kdata += copy; 89 len -= copy; 90 iov->iov_len -= copy; 91 iov->iov_base += copy; 92 } 93 iov++; 94 } 95 96 return 0; 97} 98 99/* 100 * Copy kernel to iovec. Returns -EFAULT on error. 101 */ 102 103int memcpy_toiovecend(const struct iovec *iov, unsigned char *kdata, 104 int offset, int len) 105{ 106 int copy; 107 for (; len > 0; ++iov) { 108 /* Skip over the finished iovecs */ 109 if (unlikely(offset >= iov->iov_len)) { 110 offset -= iov->iov_len; 111 continue; 112 } 113 copy = min_t(unsigned int, iov->iov_len - offset, len); 114 if (copy_to_user(iov->iov_base + offset, kdata, copy)) 115 return -EFAULT; 116 offset = 0; 117 kdata += copy; 118 len -= copy; 119 } 120 121 return 0; 122} 123 124/* 125 * Copy iovec to kernel. Returns -EFAULT on error. 126 * 127 * Note: this modifies the original iovec. 128 */ 129 130int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len) 131{ 132 while (len > 0) { 133 if (iov->iov_len) { 134 int copy = min_t(unsigned int, len, iov->iov_len); 135 if (copy_from_user(kdata, iov->iov_base, copy)) 136 return -EFAULT; 137 len -= copy; 138 kdata += copy; 139 iov->iov_base += copy; 140 iov->iov_len -= copy; 141 } 142 iov++; 143 } 144 145 return 0; 146} 147 148/* 149 * Copy iovec from kernel. Returns -EFAULT on error. 150 */ 151 152int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov, 153 int offset, int len) 154{ 155 /* Skip over the finished iovecs */ 156 while (offset >= iov->iov_len) { 157 offset -= iov->iov_len; 158 iov++; 159 } 160 161 while (len > 0) { 162 u8 __user *base = iov->iov_base + offset; 163 int copy = min_t(unsigned int, len, iov->iov_len - offset); 164 165 offset = 0; 166 if (copy_from_user(kdata, base, copy)) 167 return -EFAULT; 168 len -= copy; 169 kdata += copy; 170 iov++; 171 } 172 173 return 0; 174} 175 176/* 177 * And now for the all-in-one: copy and checksum from a user iovec 178 * directly to a datagram 179 * Calls to csum_partial but the last must be in 32 bit chunks 180 * 181 * ip_build_xmit must ensure that when fragmenting only the last 182 * call to this function will be unaligned also. 183 */ 184int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov, 185 int offset, unsigned int len, __wsum *csump) 186{ 187 __wsum csum = *csump; 188 int partial_cnt = 0, err = 0; 189 190 /* Skip over the finished iovecs */ 191 while (offset >= iov->iov_len) { 192 offset -= iov->iov_len; 193 iov++; 194 } 195 196 while (len > 0) { 197 u8 __user *base = iov->iov_base + offset; 198 int copy = min_t(unsigned int, len, iov->iov_len - offset); 199 200 offset = 0; 201 202 /* There is a remnant from previous iov. */ 203 if (partial_cnt) { 204 int par_len = 4 - partial_cnt; 205 206 /* iov component is too short ... */ 207 if (par_len > copy) { 208 if (copy_from_user(kdata, base, copy)) 209 goto out_fault; 210 kdata += copy; 211 base += copy; 212 partial_cnt += copy; 213 len -= copy; 214 iov++; 215 if (len) 216 continue; 217 *csump = csum_partial(kdata - partial_cnt, 218 partial_cnt, csum); 219 goto out; 220 } 221 if (copy_from_user(kdata, base, par_len)) 222 goto out_fault; 223 csum = csum_partial(kdata - partial_cnt, 4, csum); 224 kdata += par_len; 225 base += par_len; 226 copy -= par_len; 227 len -= par_len; 228 partial_cnt = 0; 229 } 230 231 if (len > copy) { 232 partial_cnt = copy % 4; 233 if (partial_cnt) { 234 copy -= partial_cnt; 235 if (copy_from_user(kdata + copy, base + copy, 236 partial_cnt)) 237 goto out_fault; 238 } 239 } 240 241 if (copy) { 242 csum = csum_and_copy_from_user(base, kdata, copy, 243 csum, &err); 244 if (err) 245 goto out; 246 } 247 len -= copy + partial_cnt; 248 kdata += copy + partial_cnt; 249 iov++; 250 } 251 *csump = csum; 252out: 253 return err; 254 255out_fault: 256 err = -EFAULT; 257 goto out; 258} 259 260EXPORT_SYMBOL(csum_partial_copy_fromiovecend); 261EXPORT_SYMBOL(memcpy_fromiovec); 262EXPORT_SYMBOL(memcpy_fromiovecend); 263EXPORT_SYMBOL(memcpy_toiovec); 264EXPORT_SYMBOL(memcpy_toiovecend); 265