Linux Audio

Check our new training course

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