xref: /kvmtool/util/iovec.c (revision c492534f3ac930107a628c0fadd4c152e8972b35)
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/kernel.h>
21 #include <linux/compiler.h>
22 #include <sys/uio.h>
23 #include <kvm/iovec.h>
24 #include <string.h>
25 
26 /*
27  *	Copy kernel to iovec. Returns -EFAULT on error.
28  *
29  *	Note: this modifies the original iovec.
30  */
31 
memcpy_toiovec(struct iovec * iov,unsigned char * kdata,int len)32 int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len)
33 {
34 	while (len > 0) {
35 		if (iov->iov_len) {
36 			int copy = min_t(unsigned int, iov->iov_len, len);
37 			memcpy(iov->iov_base, kdata, copy);
38 			kdata += copy;
39 			len -= copy;
40 			iov->iov_len -= copy;
41 			iov->iov_base += copy;
42 		}
43 		iov++;
44 	}
45 
46 	return 0;
47 }
48 
49 /*
50  *	Copy kernel to iovec. Returns -EFAULT on error.
51  */
52 
memcpy_toiovecend(const struct iovec * iov,unsigned char * kdata,size_t offset,int len)53 int memcpy_toiovecend(const struct iovec *iov, unsigned char *kdata,
54 		      size_t offset, int len)
55 {
56 	int copy;
57 	for (; len > 0; ++iov) {
58 		/* Skip over the finished iovecs */
59 		if (unlikely(offset >= iov->iov_len)) {
60 			offset -= iov->iov_len;
61 			continue;
62 		}
63 		copy = min_t(unsigned int, iov->iov_len - offset, len);
64 		memcpy(iov->iov_base + offset, kdata, copy);
65 		offset = 0;
66 		kdata += copy;
67 		len -= copy;
68 	}
69 
70 	return 0;
71 }
72 
73 /*
74  *	Copy iovec to kernel. Returns -EFAULT on error.
75  *
76  *	Note: this modifies the original iovec.
77  */
78 
memcpy_fromiovec(unsigned char * kdata,struct iovec * iov,int len)79 int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len)
80 {
81 	while (len > 0) {
82 		if (iov->iov_len) {
83 			int copy = min_t(unsigned int, len, iov->iov_len);
84 			memcpy(kdata, iov->iov_base, copy);
85 			len -= copy;
86 			kdata += copy;
87 			iov->iov_base += copy;
88 			iov->iov_len -= copy;
89 		}
90 		iov++;
91 	}
92 
93 	return 0;
94 }
95 
96 /*
97  *	Copy at most @len bytes from iovec to buffer.
98  *	Returns the remaining len.
99  *
100  *	Note: this modifies the original iovec, the iov pointer, and the
101  *	iovcount to describe the remaining buffer.
102  */
memcpy_fromiovec_safe(void * buf,struct iovec ** iov,size_t len,size_t * iovcount)103 ssize_t memcpy_fromiovec_safe(void *buf, struct iovec **iov, size_t len,
104 			      size_t *iovcount)
105 {
106 	size_t copy;
107 
108 	while (len && *iovcount) {
109 		copy = min(len, (*iov)->iov_len);
110 		memcpy(buf, (*iov)->iov_base, copy);
111 		buf += copy;
112 		len -= copy;
113 
114 		/* Move iov cursor */
115 		(*iov)->iov_base += copy;
116 		(*iov)->iov_len -= copy;
117 
118 		if (!(*iov)->iov_len) {
119 			(*iov)++;
120 			(*iovcount)--;
121 		}
122 	}
123 
124 	return len;
125 }
126 
127 /*
128  *	Copy iovec from kernel. Returns -EFAULT on error.
129  */
130 
memcpy_fromiovecend(unsigned char * kdata,const struct iovec * iov,size_t offset,int len)131 int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov,
132 			size_t offset, int len)
133 {
134 	/* Skip over the finished iovecs */
135 	while (offset >= iov->iov_len) {
136 		offset -= iov->iov_len;
137 		iov++;
138 	}
139 
140 	while (len > 0) {
141 		char *base = iov->iov_base + offset;
142 		int copy = min_t(unsigned int, len, iov->iov_len - offset);
143 
144 		offset = 0;
145 		memcpy(kdata, base, copy);
146 		len -= copy;
147 		kdata += copy;
148 		iov++;
149 	}
150 
151 	return 0;
152 }
153