iovec.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:5k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

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