Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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