iovec.c revision e49332bd12e92da2df6d002f857ec62675ba2648
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/sched.h> 22#include <linux/kernel.h> 23#include <linux/mm.h> 24#include <linux/slab.h> 25#include <linux/net.h> 26#include <linux/in6.h> 27#include <asm/uaccess.h> 28#include <asm/byteorder.h> 29#include <net/checksum.h> 30#include <net/sock.h> 31 32/* 33 * Verify iovec. The caller must ensure that the iovec is big enough 34 * to hold the message iovec. 35 * 36 * Save time not doing access_ok. copy_*_user will make this work 37 * in any case. 38 */ 39 40int verify_iovec(struct msghdr *m, struct iovec *iov, char *address, int mode) 41{ 42 int size, err, ct; 43 44 if (m->msg_namelen) { 45 if (mode == VERIFY_READ) { 46 err = move_addr_to_kernel(m->msg_name, 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, 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 err += iov[ct].iov_len; 65 /* 66 * Goal is not to verify user data, but to prevent returning 67 * negative value, which is interpreted as errno. 68 * Overflow is still possible, but it is harmless. 69 */ 70 if (err < 0) 71 return -EMSGSIZE; 72 } 73 74 return err; 75} 76 77/* 78 * Copy kernel to iovec. Returns -EFAULT on error. 79 * 80 * Note: this modifies the original iovec. 81 */ 82 83int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len) 84{ 85 while (len > 0) { 86 if (iov->iov_len) { 87 int copy = min_t(unsigned int, iov->iov_len, len); 88 if (copy_to_user(iov->iov_base, kdata, copy)) 89 return -EFAULT; 90 kdata += copy; 91 len -= copy; 92 iov->iov_len -= copy; 93 iov->iov_base += copy; 94 } 95 iov++; 96 } 97 98 return 0; 99} 100 101/* 102 * Copy iovec to kernel. Returns -EFAULT on error. 103 * 104 * Note: this modifies the original iovec. 105 */ 106 107int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len) 108{ 109 while (len > 0) { 110 if (iov->iov_len) { 111 int copy = min_t(unsigned int, len, iov->iov_len); 112 if (copy_from_user(kdata, iov->iov_base, copy)) 113 return -EFAULT; 114 len -= copy; 115 kdata += copy; 116 iov->iov_base += copy; 117 iov->iov_len -= copy; 118 } 119 iov++; 120 } 121 122 return 0; 123} 124 125/* 126 * For use with ip_build_xmit 127 */ 128int memcpy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset, 129 int len) 130{ 131 /* Skip over the finished iovecs */ 132 while (offset >= iov->iov_len) { 133 offset -= iov->iov_len; 134 iov++; 135 } 136 137 while (len > 0) { 138 u8 __user *base = iov->iov_base + offset; 139 int copy = min_t(unsigned int, len, iov->iov_len - offset); 140 141 offset = 0; 142 if (copy_from_user(kdata, base, copy)) 143 return -EFAULT; 144 len -= copy; 145 kdata += copy; 146 iov++; 147 } 148 149 return 0; 150} 151 152/* 153 * And now for the all-in-one: copy and checksum from a user iovec 154 * directly to a datagram 155 * Calls to csum_partial but the last must be in 32 bit chunks 156 * 157 * ip_build_xmit must ensure that when fragmenting only the last 158 * call to this function will be unaligned also. 159 */ 160int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov, 161 int offset, unsigned int len, int *csump) 162{ 163 int csum = *csump; 164 int partial_cnt = 0, err = 0; 165 166 /* Skip over the finished iovecs */ 167 while (offset >= iov->iov_len) { 168 offset -= iov->iov_len; 169 iov++; 170 } 171 172 while (len > 0) { 173 u8 __user *base = iov->iov_base + offset; 174 int copy = min_t(unsigned int, len, iov->iov_len - offset); 175 176 offset = 0; 177 178 /* There is a remnant from previous iov. */ 179 if (partial_cnt) { 180 int par_len = 4 - partial_cnt; 181 182 /* iov component is too short ... */ 183 if (par_len > copy) { 184 if (copy_from_user(kdata, base, copy)) 185 goto out_fault; 186 kdata += copy; 187 base += copy; 188 partial_cnt += copy; 189 len -= copy; 190 iov++; 191 if (len) 192 continue; 193 *csump = csum_partial(kdata - partial_cnt, 194 partial_cnt, csum); 195 goto out; 196 } 197 if (copy_from_user(kdata, base, par_len)) 198 goto out_fault; 199 csum = csum_partial(kdata - partial_cnt, 4, csum); 200 kdata += par_len; 201 base += par_len; 202 copy -= par_len; 203 len -= par_len; 204 partial_cnt = 0; 205 } 206 207 if (len > copy) { 208 partial_cnt = copy % 4; 209 if (partial_cnt) { 210 copy -= partial_cnt; 211 if (copy_from_user(kdata + copy, base + copy, 212 partial_cnt)) 213 goto out_fault; 214 } 215 } 216 217 if (copy) { 218 csum = csum_and_copy_from_user(base, kdata, copy, 219 csum, &err); 220 if (err) 221 goto out; 222 } 223 len -= copy + partial_cnt; 224 kdata += copy + partial_cnt; 225 iov++; 226 } 227 *csump = csum; 228out: 229 return err; 230 231out_fault: 232 err = -EFAULT; 233 goto out; 234} 235 236EXPORT_SYMBOL(csum_partial_copy_fromiovecend); 237EXPORT_SYMBOL(memcpy_fromiovec); 238EXPORT_SYMBOL(memcpy_fromiovecend); 239EXPORT_SYMBOL(memcpy_toiovec); 240