2014-02-06 07:11:33 +07:00
|
|
|
#include <linux/export.h>
|
|
|
|
#include <linux/uio.h>
|
|
|
|
#include <linux/pagemap.h>
|
2014-03-21 15:58:33 +07:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/vmalloc.h>
|
2014-11-24 13:08:00 +07:00
|
|
|
#include <net/checksum.h>
|
2014-02-06 07:11:33 +07:00
|
|
|
|
iov_iter.c: macros for iterating over iov_iter
iterate_all_kinds(iter, size, ident, step_iovec, step_bvec)
iterates through the ranges covered by iter (up to size bytes total),
repeating step_iovec or step_bvec for each of those. ident is
declared in expansion of that thing, either as struct iovec or
struct bvec, and it contains the range we are currently looking
at. step_bvec should be a void expression, step_iovec - a size_t
one, with non-zero meaning "stop here, that many bytes from this
range left". In the end, the amount actually handled is stored
in size.
iov_iter_copy_from_user_atomic() and iov_iter_alignment() converted
to it.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2014-11-28 01:51:41 +07:00
|
|
|
#define iterate_iovec(i, n, __v, __p, skip, STEP) { \
|
|
|
|
size_t left; \
|
|
|
|
size_t wanted = n; \
|
|
|
|
__p = i->iov; \
|
|
|
|
__v.iov_len = min(n, __p->iov_len - skip); \
|
|
|
|
if (likely(__v.iov_len)) { \
|
|
|
|
__v.iov_base = __p->iov_base + skip; \
|
|
|
|
left = (STEP); \
|
|
|
|
__v.iov_len -= left; \
|
|
|
|
skip += __v.iov_len; \
|
|
|
|
n -= __v.iov_len; \
|
|
|
|
} else { \
|
|
|
|
left = 0; \
|
|
|
|
} \
|
|
|
|
while (unlikely(!left && n)) { \
|
|
|
|
__p++; \
|
|
|
|
__v.iov_len = min(n, __p->iov_len); \
|
|
|
|
if (unlikely(!__v.iov_len)) \
|
|
|
|
continue; \
|
|
|
|
__v.iov_base = __p->iov_base; \
|
|
|
|
left = (STEP); \
|
|
|
|
__v.iov_len -= left; \
|
|
|
|
skip = __v.iov_len; \
|
|
|
|
n -= __v.iov_len; \
|
|
|
|
} \
|
|
|
|
n = wanted - n; \
|
|
|
|
}
|
|
|
|
|
2014-11-28 02:48:42 +07:00
|
|
|
#define iterate_kvec(i, n, __v, __p, skip, STEP) { \
|
|
|
|
size_t wanted = n; \
|
|
|
|
__p = i->kvec; \
|
|
|
|
__v.iov_len = min(n, __p->iov_len - skip); \
|
|
|
|
if (likely(__v.iov_len)) { \
|
|
|
|
__v.iov_base = __p->iov_base + skip; \
|
|
|
|
(void)(STEP); \
|
|
|
|
skip += __v.iov_len; \
|
|
|
|
n -= __v.iov_len; \
|
|
|
|
} \
|
|
|
|
while (unlikely(n)) { \
|
|
|
|
__p++; \
|
|
|
|
__v.iov_len = min(n, __p->iov_len); \
|
|
|
|
if (unlikely(!__v.iov_len)) \
|
|
|
|
continue; \
|
|
|
|
__v.iov_base = __p->iov_base; \
|
|
|
|
(void)(STEP); \
|
|
|
|
skip = __v.iov_len; \
|
|
|
|
n -= __v.iov_len; \
|
|
|
|
} \
|
|
|
|
n = wanted; \
|
|
|
|
}
|
|
|
|
|
iov_iter.c: macros for iterating over iov_iter
iterate_all_kinds(iter, size, ident, step_iovec, step_bvec)
iterates through the ranges covered by iter (up to size bytes total),
repeating step_iovec or step_bvec for each of those. ident is
declared in expansion of that thing, either as struct iovec or
struct bvec, and it contains the range we are currently looking
at. step_bvec should be a void expression, step_iovec - a size_t
one, with non-zero meaning "stop here, that many bytes from this
range left". In the end, the amount actually handled is stored
in size.
iov_iter_copy_from_user_atomic() and iov_iter_alignment() converted
to it.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2014-11-28 01:51:41 +07:00
|
|
|
#define iterate_bvec(i, n, __v, __p, skip, STEP) { \
|
|
|
|
size_t wanted = n; \
|
|
|
|
__p = i->bvec; \
|
|
|
|
__v.bv_len = min_t(size_t, n, __p->bv_len - skip); \
|
|
|
|
if (likely(__v.bv_len)) { \
|
|
|
|
__v.bv_page = __p->bv_page; \
|
|
|
|
__v.bv_offset = __p->bv_offset + skip; \
|
|
|
|
(void)(STEP); \
|
|
|
|
skip += __v.bv_len; \
|
|
|
|
n -= __v.bv_len; \
|
|
|
|
} \
|
|
|
|
while (unlikely(n)) { \
|
|
|
|
__p++; \
|
|
|
|
__v.bv_len = min_t(size_t, n, __p->bv_len); \
|
|
|
|
if (unlikely(!__v.bv_len)) \
|
|
|
|
continue; \
|
|
|
|
__v.bv_page = __p->bv_page; \
|
|
|
|
__v.bv_offset = __p->bv_offset; \
|
|
|
|
(void)(STEP); \
|
|
|
|
skip = __v.bv_len; \
|
|
|
|
n -= __v.bv_len; \
|
|
|
|
} \
|
|
|
|
n = wanted; \
|
|
|
|
}
|
|
|
|
|
2014-11-28 02:48:42 +07:00
|
|
|
#define iterate_all_kinds(i, n, v, I, B, K) { \
|
iov_iter.c: macros for iterating over iov_iter
iterate_all_kinds(iter, size, ident, step_iovec, step_bvec)
iterates through the ranges covered by iter (up to size bytes total),
repeating step_iovec or step_bvec for each of those. ident is
declared in expansion of that thing, either as struct iovec or
struct bvec, and it contains the range we are currently looking
at. step_bvec should be a void expression, step_iovec - a size_t
one, with non-zero meaning "stop here, that many bytes from this
range left". In the end, the amount actually handled is stored
in size.
iov_iter_copy_from_user_atomic() and iov_iter_alignment() converted
to it.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2014-11-28 01:51:41 +07:00
|
|
|
size_t skip = i->iov_offset; \
|
|
|
|
if (unlikely(i->type & ITER_BVEC)) { \
|
|
|
|
const struct bio_vec *bvec; \
|
|
|
|
struct bio_vec v; \
|
|
|
|
iterate_bvec(i, n, v, bvec, skip, (B)) \
|
2014-11-28 02:48:42 +07:00
|
|
|
} else if (unlikely(i->type & ITER_KVEC)) { \
|
|
|
|
const struct kvec *kvec; \
|
|
|
|
struct kvec v; \
|
|
|
|
iterate_kvec(i, n, v, kvec, skip, (K)) \
|
iov_iter.c: macros for iterating over iov_iter
iterate_all_kinds(iter, size, ident, step_iovec, step_bvec)
iterates through the ranges covered by iter (up to size bytes total),
repeating step_iovec or step_bvec for each of those. ident is
declared in expansion of that thing, either as struct iovec or
struct bvec, and it contains the range we are currently looking
at. step_bvec should be a void expression, step_iovec - a size_t
one, with non-zero meaning "stop here, that many bytes from this
range left". In the end, the amount actually handled is stored
in size.
iov_iter_copy_from_user_atomic() and iov_iter_alignment() converted
to it.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2014-11-28 01:51:41 +07:00
|
|
|
} else { \
|
|
|
|
const struct iovec *iov; \
|
|
|
|
struct iovec v; \
|
|
|
|
iterate_iovec(i, n, v, iov, skip, (I)) \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
2014-11-28 02:48:42 +07:00
|
|
|
#define iterate_and_advance(i, n, v, I, B, K) { \
|
2016-05-09 22:54:48 +07:00
|
|
|
if (unlikely(i->count < n)) \
|
|
|
|
n = i->count; \
|
2016-05-26 04:36:19 +07:00
|
|
|
if (i->count) { \
|
2016-05-09 22:54:48 +07:00
|
|
|
size_t skip = i->iov_offset; \
|
|
|
|
if (unlikely(i->type & ITER_BVEC)) { \
|
|
|
|
const struct bio_vec *bvec; \
|
|
|
|
struct bio_vec v; \
|
|
|
|
iterate_bvec(i, n, v, bvec, skip, (B)) \
|
|
|
|
if (skip == bvec->bv_len) { \
|
|
|
|
bvec++; \
|
|
|
|
skip = 0; \
|
|
|
|
} \
|
|
|
|
i->nr_segs -= bvec - i->bvec; \
|
|
|
|
i->bvec = bvec; \
|
|
|
|
} else if (unlikely(i->type & ITER_KVEC)) { \
|
|
|
|
const struct kvec *kvec; \
|
|
|
|
struct kvec v; \
|
|
|
|
iterate_kvec(i, n, v, kvec, skip, (K)) \
|
|
|
|
if (skip == kvec->iov_len) { \
|
|
|
|
kvec++; \
|
|
|
|
skip = 0; \
|
|
|
|
} \
|
|
|
|
i->nr_segs -= kvec - i->kvec; \
|
|
|
|
i->kvec = kvec; \
|
|
|
|
} else { \
|
|
|
|
const struct iovec *iov; \
|
|
|
|
struct iovec v; \
|
|
|
|
iterate_iovec(i, n, v, iov, skip, (I)) \
|
|
|
|
if (skip == iov->iov_len) { \
|
|
|
|
iov++; \
|
|
|
|
skip = 0; \
|
|
|
|
} \
|
|
|
|
i->nr_segs -= iov - i->iov; \
|
|
|
|
i->iov = iov; \
|
2014-11-28 01:59:45 +07:00
|
|
|
} \
|
2016-05-09 22:54:48 +07:00
|
|
|
i->count -= n; \
|
|
|
|
i->iov_offset = skip; \
|
2014-11-28 01:59:45 +07:00
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
2014-04-05 10:12:29 +07:00
|
|
|
static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t bytes,
|
2014-02-06 07:11:33 +07:00
|
|
|
struct iov_iter *i)
|
|
|
|
{
|
|
|
|
size_t skip, copy, left, wanted;
|
|
|
|
const struct iovec *iov;
|
|
|
|
char __user *buf;
|
|
|
|
void *kaddr, *from;
|
|
|
|
|
|
|
|
if (unlikely(bytes > i->count))
|
|
|
|
bytes = i->count;
|
|
|
|
|
|
|
|
if (unlikely(!bytes))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
wanted = bytes;
|
|
|
|
iov = i->iov;
|
|
|
|
skip = i->iov_offset;
|
|
|
|
buf = iov->iov_base + skip;
|
|
|
|
copy = min(bytes, iov->iov_len - skip);
|
|
|
|
|
|
|
|
if (!fault_in_pages_writeable(buf, copy)) {
|
|
|
|
kaddr = kmap_atomic(page);
|
|
|
|
from = kaddr + offset;
|
|
|
|
|
|
|
|
/* first chunk, usually the only one */
|
|
|
|
left = __copy_to_user_inatomic(buf, from, copy);
|
|
|
|
copy -= left;
|
|
|
|
skip += copy;
|
|
|
|
from += copy;
|
|
|
|
bytes -= copy;
|
|
|
|
|
|
|
|
while (unlikely(!left && bytes)) {
|
|
|
|
iov++;
|
|
|
|
buf = iov->iov_base;
|
|
|
|
copy = min(bytes, iov->iov_len);
|
|
|
|
left = __copy_to_user_inatomic(buf, from, copy);
|
|
|
|
copy -= left;
|
|
|
|
skip = copy;
|
|
|
|
from += copy;
|
|
|
|
bytes -= copy;
|
|
|
|
}
|
|
|
|
if (likely(!bytes)) {
|
|
|
|
kunmap_atomic(kaddr);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
offset = from - kaddr;
|
|
|
|
buf += copy;
|
|
|
|
kunmap_atomic(kaddr);
|
|
|
|
copy = min(bytes, iov->iov_len - skip);
|
|
|
|
}
|
|
|
|
/* Too bad - revert to non-atomic kmap */
|
|
|
|
kaddr = kmap(page);
|
|
|
|
from = kaddr + offset;
|
|
|
|
left = __copy_to_user(buf, from, copy);
|
|
|
|
copy -= left;
|
|
|
|
skip += copy;
|
|
|
|
from += copy;
|
|
|
|
bytes -= copy;
|
|
|
|
while (unlikely(!left && bytes)) {
|
|
|
|
iov++;
|
|
|
|
buf = iov->iov_base;
|
|
|
|
copy = min(bytes, iov->iov_len);
|
|
|
|
left = __copy_to_user(buf, from, copy);
|
|
|
|
copy -= left;
|
|
|
|
skip = copy;
|
|
|
|
from += copy;
|
|
|
|
bytes -= copy;
|
|
|
|
}
|
|
|
|
kunmap(page);
|
|
|
|
done:
|
2014-04-05 06:23:46 +07:00
|
|
|
if (skip == iov->iov_len) {
|
|
|
|
iov++;
|
|
|
|
skip = 0;
|
|
|
|
}
|
2014-02-06 07:11:33 +07:00
|
|
|
i->count -= wanted - bytes;
|
|
|
|
i->nr_segs -= iov - i->iov;
|
|
|
|
i->iov = iov;
|
|
|
|
i->iov_offset = skip;
|
|
|
|
return wanted - bytes;
|
|
|
|
}
|
|
|
|
|
2014-04-05 10:12:29 +07:00
|
|
|
static size_t copy_page_from_iter_iovec(struct page *page, size_t offset, size_t bytes,
|
2014-04-04 02:05:18 +07:00
|
|
|
struct iov_iter *i)
|
|
|
|
{
|
|
|
|
size_t skip, copy, left, wanted;
|
|
|
|
const struct iovec *iov;
|
|
|
|
char __user *buf;
|
|
|
|
void *kaddr, *to;
|
|
|
|
|
|
|
|
if (unlikely(bytes > i->count))
|
|
|
|
bytes = i->count;
|
|
|
|
|
|
|
|
if (unlikely(!bytes))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
wanted = bytes;
|
|
|
|
iov = i->iov;
|
|
|
|
skip = i->iov_offset;
|
|
|
|
buf = iov->iov_base + skip;
|
|
|
|
copy = min(bytes, iov->iov_len - skip);
|
|
|
|
|
|
|
|
if (!fault_in_pages_readable(buf, copy)) {
|
|
|
|
kaddr = kmap_atomic(page);
|
|
|
|
to = kaddr + offset;
|
|
|
|
|
|
|
|
/* first chunk, usually the only one */
|
|
|
|
left = __copy_from_user_inatomic(to, buf, copy);
|
|
|
|
copy -= left;
|
|
|
|
skip += copy;
|
|
|
|
to += copy;
|
|
|
|
bytes -= copy;
|
|
|
|
|
|
|
|
while (unlikely(!left && bytes)) {
|
|
|
|
iov++;
|
|
|
|
buf = iov->iov_base;
|
|
|
|
copy = min(bytes, iov->iov_len);
|
|
|
|
left = __copy_from_user_inatomic(to, buf, copy);
|
|
|
|
copy -= left;
|
|
|
|
skip = copy;
|
|
|
|
to += copy;
|
|
|
|
bytes -= copy;
|
|
|
|
}
|
|
|
|
if (likely(!bytes)) {
|
|
|
|
kunmap_atomic(kaddr);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
offset = to - kaddr;
|
|
|
|
buf += copy;
|
|
|
|
kunmap_atomic(kaddr);
|
|
|
|
copy = min(bytes, iov->iov_len - skip);
|
|
|
|
}
|
|
|
|
/* Too bad - revert to non-atomic kmap */
|
|
|
|
kaddr = kmap(page);
|
|
|
|
to = kaddr + offset;
|
|
|
|
left = __copy_from_user(to, buf, copy);
|
|
|
|
copy -= left;
|
|
|
|
skip += copy;
|
|
|
|
to += copy;
|
|
|
|
bytes -= copy;
|
|
|
|
while (unlikely(!left && bytes)) {
|
|
|
|
iov++;
|
|
|
|
buf = iov->iov_base;
|
|
|
|
copy = min(bytes, iov->iov_len);
|
|
|
|
left = __copy_from_user(to, buf, copy);
|
|
|
|
copy -= left;
|
|
|
|
skip = copy;
|
|
|
|
to += copy;
|
|
|
|
bytes -= copy;
|
|
|
|
}
|
|
|
|
kunmap(page);
|
|
|
|
done:
|
2014-04-05 06:23:46 +07:00
|
|
|
if (skip == iov->iov_len) {
|
|
|
|
iov++;
|
|
|
|
skip = 0;
|
|
|
|
}
|
2014-04-04 02:05:18 +07:00
|
|
|
i->count -= wanted - bytes;
|
|
|
|
i->nr_segs -= iov - i->iov;
|
|
|
|
i->iov = iov;
|
|
|
|
i->iov_offset = skip;
|
|
|
|
return wanted - bytes;
|
|
|
|
}
|
|
|
|
|
2014-02-06 07:11:33 +07:00
|
|
|
/*
|
|
|
|
* Fault in the first iovec of the given iov_iter, to a maximum length
|
|
|
|
* of bytes. Returns 0 on success, or non-zero if the memory could not be
|
|
|
|
* accessed (ie. because it is an invalid address).
|
|
|
|
*
|
|
|
|
* writev-intensive code may want this to prefault several iovecs -- that
|
|
|
|
* would be possible (callers must not rely on the fact that _only_ the
|
|
|
|
* first iovec will be faulted with the current implementation).
|
|
|
|
*/
|
|
|
|
int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes)
|
|
|
|
{
|
2014-11-28 02:48:42 +07:00
|
|
|
if (!(i->type & (ITER_BVEC|ITER_KVEC))) {
|
2014-04-05 10:12:29 +07:00
|
|
|
char __user *buf = i->iov->iov_base + i->iov_offset;
|
|
|
|
bytes = min(bytes, i->iov->iov_len - i->iov_offset);
|
|
|
|
return fault_in_pages_readable(buf, bytes);
|
|
|
|
}
|
|
|
|
return 0;
|
2014-02-06 07:11:33 +07:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(iov_iter_fault_in_readable);
|
|
|
|
|
2015-03-11 21:43:31 +07:00
|
|
|
/*
|
|
|
|
* Fault in one or more iovecs of the given iov_iter, to a maximum length of
|
|
|
|
* bytes. For each iovec, fault in each page that constitutes the iovec.
|
|
|
|
*
|
|
|
|
* Return 0 on success, or non-zero if the memory could not be accessed (i.e.
|
|
|
|
* because it is an invalid address).
|
|
|
|
*/
|
|
|
|
int iov_iter_fault_in_multipages_readable(struct iov_iter *i, size_t bytes)
|
|
|
|
{
|
|
|
|
size_t skip = i->iov_offset;
|
|
|
|
const struct iovec *iov;
|
|
|
|
int err;
|
|
|
|
struct iovec v;
|
|
|
|
|
|
|
|
if (!(i->type & (ITER_BVEC|ITER_KVEC))) {
|
|
|
|
iterate_iovec(i, bytes, v, iov, skip, ({
|
|
|
|
err = fault_in_multipages_readable(v.iov_base,
|
|
|
|
v.iov_len);
|
|
|
|
if (unlikely(err))
|
|
|
|
return err;
|
|
|
|
0;}))
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(iov_iter_fault_in_multipages_readable);
|
|
|
|
|
2014-03-06 07:28:09 +07:00
|
|
|
void iov_iter_init(struct iov_iter *i, int direction,
|
|
|
|
const struct iovec *iov, unsigned long nr_segs,
|
|
|
|
size_t count)
|
|
|
|
{
|
|
|
|
/* It will get better. Eventually... */
|
2014-11-28 02:48:42 +07:00
|
|
|
if (segment_eq(get_fs(), KERNEL_DS)) {
|
2014-04-05 10:12:29 +07:00
|
|
|
direction |= ITER_KVEC;
|
2014-11-28 02:48:42 +07:00
|
|
|
i->type = direction;
|
|
|
|
i->kvec = (struct kvec *)iov;
|
|
|
|
} else {
|
|
|
|
i->type = direction;
|
|
|
|
i->iov = iov;
|
|
|
|
}
|
2014-03-06 07:28:09 +07:00
|
|
|
i->nr_segs = nr_segs;
|
|
|
|
i->iov_offset = 0;
|
|
|
|
i->count = count;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(iov_iter_init);
|
2014-03-15 15:05:57 +07:00
|
|
|
|
2014-04-05 10:12:29 +07:00
|
|
|
static void memcpy_from_page(char *to, struct page *page, size_t offset, size_t len)
|
|
|
|
{
|
|
|
|
char *from = kmap_atomic(page);
|
|
|
|
memcpy(to, from + offset, len);
|
|
|
|
kunmap_atomic(from);
|
|
|
|
}
|
|
|
|
|
2015-12-07 04:49:22 +07:00
|
|
|
static void memcpy_to_page(struct page *page, size_t offset, const char *from, size_t len)
|
2014-04-05 10:12:29 +07:00
|
|
|
{
|
|
|
|
char *to = kmap_atomic(page);
|
|
|
|
memcpy(to + offset, from, len);
|
|
|
|
kunmap_atomic(to);
|
|
|
|
}
|
|
|
|
|
2014-08-01 20:27:22 +07:00
|
|
|
static void memzero_page(struct page *page, size_t offset, size_t len)
|
|
|
|
{
|
|
|
|
char *addr = kmap_atomic(page);
|
|
|
|
memset(addr + offset, 0, len);
|
|
|
|
kunmap_atomic(addr);
|
|
|
|
}
|
|
|
|
|
2015-12-07 04:49:22 +07:00
|
|
|
size_t copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i)
|
2014-04-05 10:12:29 +07:00
|
|
|
{
|
2015-12-07 04:49:22 +07:00
|
|
|
const char *from = addr;
|
2014-11-28 02:28:06 +07:00
|
|
|
iterate_and_advance(i, bytes, v,
|
|
|
|
__copy_to_user(v.iov_base, (from += v.iov_len) - v.iov_len,
|
|
|
|
v.iov_len),
|
|
|
|
memcpy_to_page(v.bv_page, v.bv_offset,
|
2014-11-28 02:48:42 +07:00
|
|
|
(from += v.bv_len) - v.bv_len, v.bv_len),
|
|
|
|
memcpy(v.iov_base, (from += v.iov_len) - v.iov_len, v.iov_len)
|
2014-11-28 02:28:06 +07:00
|
|
|
)
|
2014-04-05 10:12:29 +07:00
|
|
|
|
2014-11-28 02:28:06 +07:00
|
|
|
return bytes;
|
2014-08-01 20:27:22 +07:00
|
|
|
}
|
2014-11-28 02:22:37 +07:00
|
|
|
EXPORT_SYMBOL(copy_to_iter);
|
2014-08-01 20:27:22 +07:00
|
|
|
|
2014-11-28 02:22:37 +07:00
|
|
|
size_t copy_from_iter(void *addr, size_t bytes, struct iov_iter *i)
|
2014-08-01 20:27:22 +07:00
|
|
|
{
|
2014-11-28 02:26:43 +07:00
|
|
|
char *to = addr;
|
|
|
|
iterate_and_advance(i, bytes, v,
|
|
|
|
__copy_from_user((to += v.iov_len) - v.iov_len, v.iov_base,
|
|
|
|
v.iov_len),
|
|
|
|
memcpy_from_page((to += v.bv_len) - v.bv_len, v.bv_page,
|
2014-11-28 02:48:42 +07:00
|
|
|
v.bv_offset, v.bv_len),
|
|
|
|
memcpy((to += v.iov_len) - v.iov_len, v.iov_base, v.iov_len)
|
2014-11-28 02:26:43 +07:00
|
|
|
)
|
|
|
|
|
|
|
|
return bytes;
|
2014-08-01 20:27:22 +07:00
|
|
|
}
|
2014-11-28 02:22:37 +07:00
|
|
|
EXPORT_SYMBOL(copy_from_iter);
|
2014-08-01 20:27:22 +07:00
|
|
|
|
2014-11-28 08:27:08 +07:00
|
|
|
size_t copy_from_iter_nocache(void *addr, size_t bytes, struct iov_iter *i)
|
|
|
|
{
|
|
|
|
char *to = addr;
|
|
|
|
iterate_and_advance(i, bytes, v,
|
|
|
|
__copy_from_user_nocache((to += v.iov_len) - v.iov_len,
|
|
|
|
v.iov_base, v.iov_len),
|
|
|
|
memcpy_from_page((to += v.bv_len) - v.bv_len, v.bv_page,
|
|
|
|
v.bv_offset, v.bv_len),
|
|
|
|
memcpy((to += v.iov_len) - v.iov_len, v.iov_base, v.iov_len)
|
|
|
|
)
|
|
|
|
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(copy_from_iter_nocache);
|
|
|
|
|
2014-04-05 10:12:29 +07:00
|
|
|
size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes,
|
|
|
|
struct iov_iter *i)
|
|
|
|
{
|
2014-11-28 02:22:37 +07:00
|
|
|
if (i->type & (ITER_BVEC|ITER_KVEC)) {
|
|
|
|
void *kaddr = kmap_atomic(page);
|
|
|
|
size_t wanted = copy_to_iter(kaddr + offset, bytes, i);
|
|
|
|
kunmap_atomic(kaddr);
|
|
|
|
return wanted;
|
|
|
|
} else
|
2014-04-05 10:12:29 +07:00
|
|
|
return copy_page_to_iter_iovec(page, offset, bytes, i);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(copy_page_to_iter);
|
|
|
|
|
|
|
|
size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes,
|
|
|
|
struct iov_iter *i)
|
|
|
|
{
|
2014-11-28 02:48:42 +07:00
|
|
|
if (i->type & (ITER_BVEC|ITER_KVEC)) {
|
2014-11-28 02:22:37 +07:00
|
|
|
void *kaddr = kmap_atomic(page);
|
|
|
|
size_t wanted = copy_from_iter(kaddr + offset, bytes, i);
|
|
|
|
kunmap_atomic(kaddr);
|
|
|
|
return wanted;
|
|
|
|
} else
|
2014-04-05 10:12:29 +07:00
|
|
|
return copy_page_from_iter_iovec(page, offset, bytes, i);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(copy_page_from_iter);
|
|
|
|
|
2014-08-01 20:27:22 +07:00
|
|
|
size_t iov_iter_zero(size_t bytes, struct iov_iter *i)
|
|
|
|
{
|
2014-11-28 02:18:54 +07:00
|
|
|
iterate_and_advance(i, bytes, v,
|
|
|
|
__clear_user(v.iov_base, v.iov_len),
|
2014-11-28 02:48:42 +07:00
|
|
|
memzero_page(v.bv_page, v.bv_offset, v.bv_len),
|
|
|
|
memset(v.iov_base, 0, v.iov_len)
|
2014-11-28 02:18:54 +07:00
|
|
|
)
|
|
|
|
|
|
|
|
return bytes;
|
2014-08-01 20:27:22 +07:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(iov_iter_zero);
|
|
|
|
|
2014-04-05 10:12:29 +07:00
|
|
|
size_t iov_iter_copy_from_user_atomic(struct page *page,
|
|
|
|
struct iov_iter *i, unsigned long offset, size_t bytes)
|
|
|
|
{
|
iov_iter.c: macros for iterating over iov_iter
iterate_all_kinds(iter, size, ident, step_iovec, step_bvec)
iterates through the ranges covered by iter (up to size bytes total),
repeating step_iovec or step_bvec for each of those. ident is
declared in expansion of that thing, either as struct iovec or
struct bvec, and it contains the range we are currently looking
at. step_bvec should be a void expression, step_iovec - a size_t
one, with non-zero meaning "stop here, that many bytes from this
range left". In the end, the amount actually handled is stored
in size.
iov_iter_copy_from_user_atomic() and iov_iter_alignment() converted
to it.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2014-11-28 01:51:41 +07:00
|
|
|
char *kaddr = kmap_atomic(page), *p = kaddr + offset;
|
|
|
|
iterate_all_kinds(i, bytes, v,
|
|
|
|
__copy_from_user_inatomic((p += v.iov_len) - v.iov_len,
|
|
|
|
v.iov_base, v.iov_len),
|
|
|
|
memcpy_from_page((p += v.bv_len) - v.bv_len, v.bv_page,
|
2014-11-28 02:48:42 +07:00
|
|
|
v.bv_offset, v.bv_len),
|
|
|
|
memcpy((p += v.iov_len) - v.iov_len, v.iov_base, v.iov_len)
|
iov_iter.c: macros for iterating over iov_iter
iterate_all_kinds(iter, size, ident, step_iovec, step_bvec)
iterates through the ranges covered by iter (up to size bytes total),
repeating step_iovec or step_bvec for each of those. ident is
declared in expansion of that thing, either as struct iovec or
struct bvec, and it contains the range we are currently looking
at. step_bvec should be a void expression, step_iovec - a size_t
one, with non-zero meaning "stop here, that many bytes from this
range left". In the end, the amount actually handled is stored
in size.
iov_iter_copy_from_user_atomic() and iov_iter_alignment() converted
to it.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2014-11-28 01:51:41 +07:00
|
|
|
)
|
|
|
|
kunmap_atomic(kaddr);
|
|
|
|
return bytes;
|
2014-04-05 10:12:29 +07:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(iov_iter_copy_from_user_atomic);
|
|
|
|
|
|
|
|
void iov_iter_advance(struct iov_iter *i, size_t size)
|
|
|
|
{
|
2014-11-28 02:48:42 +07:00
|
|
|
iterate_and_advance(i, size, v, 0, 0, 0)
|
2014-04-05 10:12:29 +07:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(iov_iter_advance);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return the count of just the current iov_iter segment.
|
|
|
|
*/
|
|
|
|
size_t iov_iter_single_seg_count(const struct iov_iter *i)
|
|
|
|
{
|
|
|
|
if (i->nr_segs == 1)
|
|
|
|
return i->count;
|
|
|
|
else if (i->type & ITER_BVEC)
|
|
|
|
return min(i->count, i->bvec->bv_len - i->iov_offset);
|
2014-11-13 16:15:23 +07:00
|
|
|
else
|
|
|
|
return min(i->count, i->iov->iov_len - i->iov_offset);
|
2014-04-05 10:12:29 +07:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(iov_iter_single_seg_count);
|
|
|
|
|
2014-11-25 02:46:11 +07:00
|
|
|
void iov_iter_kvec(struct iov_iter *i, int direction,
|
2015-01-23 13:08:07 +07:00
|
|
|
const struct kvec *kvec, unsigned long nr_segs,
|
2014-11-25 02:46:11 +07:00
|
|
|
size_t count)
|
|
|
|
{
|
|
|
|
BUG_ON(!(direction & ITER_KVEC));
|
|
|
|
i->type = direction;
|
2015-01-23 13:08:07 +07:00
|
|
|
i->kvec = kvec;
|
2014-11-25 02:46:11 +07:00
|
|
|
i->nr_segs = nr_segs;
|
|
|
|
i->iov_offset = 0;
|
|
|
|
i->count = count;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(iov_iter_kvec);
|
|
|
|
|
2015-01-23 13:08:07 +07:00
|
|
|
void iov_iter_bvec(struct iov_iter *i, int direction,
|
|
|
|
const struct bio_vec *bvec, unsigned long nr_segs,
|
|
|
|
size_t count)
|
|
|
|
{
|
|
|
|
BUG_ON(!(direction & ITER_BVEC));
|
|
|
|
i->type = direction;
|
|
|
|
i->bvec = bvec;
|
|
|
|
i->nr_segs = nr_segs;
|
|
|
|
i->iov_offset = 0;
|
|
|
|
i->count = count;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(iov_iter_bvec);
|
|
|
|
|
2014-04-05 10:12:29 +07:00
|
|
|
unsigned long iov_iter_alignment(const struct iov_iter *i)
|
|
|
|
{
|
iov_iter.c: macros for iterating over iov_iter
iterate_all_kinds(iter, size, ident, step_iovec, step_bvec)
iterates through the ranges covered by iter (up to size bytes total),
repeating step_iovec or step_bvec for each of those. ident is
declared in expansion of that thing, either as struct iovec or
struct bvec, and it contains the range we are currently looking
at. step_bvec should be a void expression, step_iovec - a size_t
one, with non-zero meaning "stop here, that many bytes from this
range left". In the end, the amount actually handled is stored
in size.
iov_iter_copy_from_user_atomic() and iov_iter_alignment() converted
to it.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2014-11-28 01:51:41 +07:00
|
|
|
unsigned long res = 0;
|
|
|
|
size_t size = i->count;
|
|
|
|
|
|
|
|
if (!size)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
iterate_all_kinds(i, size, v,
|
|
|
|
(res |= (unsigned long)v.iov_base | v.iov_len, 0),
|
2014-11-28 02:48:42 +07:00
|
|
|
res |= v.bv_offset | v.bv_len,
|
|
|
|
res |= (unsigned long)v.iov_base | v.iov_len
|
iov_iter.c: macros for iterating over iov_iter
iterate_all_kinds(iter, size, ident, step_iovec, step_bvec)
iterates through the ranges covered by iter (up to size bytes total),
repeating step_iovec or step_bvec for each of those. ident is
declared in expansion of that thing, either as struct iovec or
struct bvec, and it contains the range we are currently looking
at. step_bvec should be a void expression, step_iovec - a size_t
one, with non-zero meaning "stop here, that many bytes from this
range left". In the end, the amount actually handled is stored
in size.
iov_iter_copy_from_user_atomic() and iov_iter_alignment() converted
to it.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2014-11-28 01:51:41 +07:00
|
|
|
)
|
|
|
|
return res;
|
2014-04-05 10:12:29 +07:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(iov_iter_alignment);
|
|
|
|
|
2016-04-09 06:05:19 +07:00
|
|
|
unsigned long iov_iter_gap_alignment(const struct iov_iter *i)
|
|
|
|
{
|
|
|
|
unsigned long res = 0;
|
|
|
|
size_t size = i->count;
|
|
|
|
if (!size)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
iterate_all_kinds(i, size, v,
|
|
|
|
(res |= (!res ? 0 : (unsigned long)v.iov_base) |
|
|
|
|
(size != v.iov_len ? size : 0), 0),
|
|
|
|
(res |= (!res ? 0 : (unsigned long)v.bv_offset) |
|
|
|
|
(size != v.bv_len ? size : 0)),
|
|
|
|
(res |= (!res ? 0 : (unsigned long)v.iov_base) |
|
|
|
|
(size != v.iov_len ? size : 0))
|
|
|
|
);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(iov_iter_gap_alignment);
|
|
|
|
|
2014-04-05 10:12:29 +07:00
|
|
|
ssize_t iov_iter_get_pages(struct iov_iter *i,
|
2014-09-24 22:09:11 +07:00
|
|
|
struct page **pages, size_t maxsize, unsigned maxpages,
|
2014-04-05 10:12:29 +07:00
|
|
|
size_t *start)
|
|
|
|
{
|
2014-11-28 02:12:09 +07:00
|
|
|
if (maxsize > i->count)
|
|
|
|
maxsize = i->count;
|
|
|
|
|
|
|
|
if (!maxsize)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
iterate_all_kinds(i, maxsize, v, ({
|
|
|
|
unsigned long addr = (unsigned long)v.iov_base;
|
|
|
|
size_t len = v.iov_len + (*start = addr & (PAGE_SIZE - 1));
|
|
|
|
int n;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
if (len > maxpages * PAGE_SIZE)
|
|
|
|
len = maxpages * PAGE_SIZE;
|
|
|
|
addr &= ~(PAGE_SIZE - 1);
|
|
|
|
n = DIV_ROUND_UP(len, PAGE_SIZE);
|
|
|
|
res = get_user_pages_fast(addr, n, (i->type & WRITE) != WRITE, pages);
|
|
|
|
if (unlikely(res < 0))
|
|
|
|
return res;
|
|
|
|
return (res == n ? len : res * PAGE_SIZE) - *start;
|
|
|
|
0;}),({
|
|
|
|
/* can't be more than PAGE_SIZE */
|
|
|
|
*start = v.bv_offset;
|
|
|
|
get_page(*pages = v.bv_page);
|
|
|
|
return v.bv_len;
|
2014-11-28 02:48:42 +07:00
|
|
|
}),({
|
|
|
|
return -EFAULT;
|
2014-11-28 02:12:09 +07:00
|
|
|
})
|
|
|
|
)
|
|
|
|
return 0;
|
2014-04-05 10:12:29 +07:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(iov_iter_get_pages);
|
|
|
|
|
2014-11-28 02:14:31 +07:00
|
|
|
static struct page **get_pages_array(size_t n)
|
|
|
|
{
|
|
|
|
struct page **p = kmalloc(n * sizeof(struct page *), GFP_KERNEL);
|
|
|
|
if (!p)
|
|
|
|
p = vmalloc(n * sizeof(struct page *));
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2014-04-05 10:12:29 +07:00
|
|
|
ssize_t iov_iter_get_pages_alloc(struct iov_iter *i,
|
|
|
|
struct page ***pages, size_t maxsize,
|
|
|
|
size_t *start)
|
|
|
|
{
|
2014-11-28 02:14:31 +07:00
|
|
|
struct page **p;
|
|
|
|
|
|
|
|
if (maxsize > i->count)
|
|
|
|
maxsize = i->count;
|
|
|
|
|
|
|
|
if (!maxsize)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
iterate_all_kinds(i, maxsize, v, ({
|
|
|
|
unsigned long addr = (unsigned long)v.iov_base;
|
|
|
|
size_t len = v.iov_len + (*start = addr & (PAGE_SIZE - 1));
|
|
|
|
int n;
|
|
|
|
int res;
|
|
|
|
|
|
|
|
addr &= ~(PAGE_SIZE - 1);
|
|
|
|
n = DIV_ROUND_UP(len, PAGE_SIZE);
|
|
|
|
p = get_pages_array(n);
|
|
|
|
if (!p)
|
|
|
|
return -ENOMEM;
|
|
|
|
res = get_user_pages_fast(addr, n, (i->type & WRITE) != WRITE, p);
|
|
|
|
if (unlikely(res < 0)) {
|
|
|
|
kvfree(p);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
*pages = p;
|
|
|
|
return (res == n ? len : res * PAGE_SIZE) - *start;
|
|
|
|
0;}),({
|
|
|
|
/* can't be more than PAGE_SIZE */
|
|
|
|
*start = v.bv_offset;
|
|
|
|
*pages = p = get_pages_array(1);
|
|
|
|
if (!p)
|
|
|
|
return -ENOMEM;
|
|
|
|
get_page(*p = v.bv_page);
|
|
|
|
return v.bv_len;
|
2014-11-28 02:48:42 +07:00
|
|
|
}),({
|
|
|
|
return -EFAULT;
|
2014-11-28 02:14:31 +07:00
|
|
|
})
|
|
|
|
)
|
|
|
|
return 0;
|
2014-04-05 10:12:29 +07:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(iov_iter_get_pages_alloc);
|
|
|
|
|
2014-11-24 13:08:00 +07:00
|
|
|
size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum,
|
|
|
|
struct iov_iter *i)
|
|
|
|
{
|
|
|
|
char *to = addr;
|
|
|
|
__wsum sum, next;
|
|
|
|
size_t off = 0;
|
|
|
|
sum = *csum;
|
|
|
|
iterate_and_advance(i, bytes, v, ({
|
|
|
|
int err = 0;
|
|
|
|
next = csum_and_copy_from_user(v.iov_base,
|
|
|
|
(to += v.iov_len) - v.iov_len,
|
|
|
|
v.iov_len, 0, &err);
|
|
|
|
if (!err) {
|
|
|
|
sum = csum_block_add(sum, next, off);
|
|
|
|
off += v.iov_len;
|
|
|
|
}
|
|
|
|
err ? v.iov_len : 0;
|
|
|
|
}), ({
|
|
|
|
char *p = kmap_atomic(v.bv_page);
|
|
|
|
next = csum_partial_copy_nocheck(p + v.bv_offset,
|
|
|
|
(to += v.bv_len) - v.bv_len,
|
|
|
|
v.bv_len, 0);
|
|
|
|
kunmap_atomic(p);
|
|
|
|
sum = csum_block_add(sum, next, off);
|
|
|
|
off += v.bv_len;
|
|
|
|
}),({
|
|
|
|
next = csum_partial_copy_nocheck(v.iov_base,
|
|
|
|
(to += v.iov_len) - v.iov_len,
|
|
|
|
v.iov_len, 0);
|
|
|
|
sum = csum_block_add(sum, next, off);
|
|
|
|
off += v.iov_len;
|
|
|
|
})
|
|
|
|
)
|
|
|
|
*csum = sum;
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(csum_and_copy_from_iter);
|
|
|
|
|
2015-12-07 04:49:22 +07:00
|
|
|
size_t csum_and_copy_to_iter(const void *addr, size_t bytes, __wsum *csum,
|
2014-11-24 13:08:00 +07:00
|
|
|
struct iov_iter *i)
|
|
|
|
{
|
2015-12-07 04:49:22 +07:00
|
|
|
const char *from = addr;
|
2014-11-24 13:08:00 +07:00
|
|
|
__wsum sum, next;
|
|
|
|
size_t off = 0;
|
|
|
|
sum = *csum;
|
|
|
|
iterate_and_advance(i, bytes, v, ({
|
|
|
|
int err = 0;
|
|
|
|
next = csum_and_copy_to_user((from += v.iov_len) - v.iov_len,
|
|
|
|
v.iov_base,
|
|
|
|
v.iov_len, 0, &err);
|
|
|
|
if (!err) {
|
|
|
|
sum = csum_block_add(sum, next, off);
|
|
|
|
off += v.iov_len;
|
|
|
|
}
|
|
|
|
err ? v.iov_len : 0;
|
|
|
|
}), ({
|
|
|
|
char *p = kmap_atomic(v.bv_page);
|
|
|
|
next = csum_partial_copy_nocheck((from += v.bv_len) - v.bv_len,
|
|
|
|
p + v.bv_offset,
|
|
|
|
v.bv_len, 0);
|
|
|
|
kunmap_atomic(p);
|
|
|
|
sum = csum_block_add(sum, next, off);
|
|
|
|
off += v.bv_len;
|
|
|
|
}),({
|
|
|
|
next = csum_partial_copy_nocheck((from += v.iov_len) - v.iov_len,
|
|
|
|
v.iov_base,
|
|
|
|
v.iov_len, 0);
|
|
|
|
sum = csum_block_add(sum, next, off);
|
|
|
|
off += v.iov_len;
|
|
|
|
})
|
|
|
|
)
|
|
|
|
*csum = sum;
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(csum_and_copy_to_iter);
|
|
|
|
|
2014-04-05 10:12:29 +07:00
|
|
|
int iov_iter_npages(const struct iov_iter *i, int maxpages)
|
|
|
|
{
|
2014-11-28 02:09:46 +07:00
|
|
|
size_t size = i->count;
|
|
|
|
int npages = 0;
|
|
|
|
|
|
|
|
if (!size)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
iterate_all_kinds(i, size, v, ({
|
|
|
|
unsigned long p = (unsigned long)v.iov_base;
|
|
|
|
npages += DIV_ROUND_UP(p + v.iov_len, PAGE_SIZE)
|
|
|
|
- p / PAGE_SIZE;
|
|
|
|
if (npages >= maxpages)
|
|
|
|
return maxpages;
|
|
|
|
0;}),({
|
|
|
|
npages++;
|
|
|
|
if (npages >= maxpages)
|
|
|
|
return maxpages;
|
2014-11-28 02:48:42 +07:00
|
|
|
}),({
|
|
|
|
unsigned long p = (unsigned long)v.iov_base;
|
|
|
|
npages += DIV_ROUND_UP(p + v.iov_len, PAGE_SIZE)
|
|
|
|
- p / PAGE_SIZE;
|
|
|
|
if (npages >= maxpages)
|
|
|
|
return maxpages;
|
2014-11-28 02:09:46 +07:00
|
|
|
})
|
|
|
|
)
|
|
|
|
return npages;
|
2014-04-05 10:12:29 +07:00
|
|
|
}
|
2014-03-19 12:16:16 +07:00
|
|
|
EXPORT_SYMBOL(iov_iter_npages);
|
2015-02-01 08:08:47 +07:00
|
|
|
|
|
|
|
const void *dup_iter(struct iov_iter *new, struct iov_iter *old, gfp_t flags)
|
|
|
|
{
|
|
|
|
*new = *old;
|
|
|
|
if (new->type & ITER_BVEC)
|
|
|
|
return new->bvec = kmemdup(new->bvec,
|
|
|
|
new->nr_segs * sizeof(struct bio_vec),
|
|
|
|
flags);
|
|
|
|
else
|
|
|
|
/* iovec and kvec have identical layout */
|
|
|
|
return new->iov = kmemdup(new->iov,
|
|
|
|
new->nr_segs * sizeof(struct iovec),
|
|
|
|
flags);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dup_iter);
|
saner iov_iter initialization primitives
iovec-backed iov_iter instances are assumed to satisfy several properties:
* no more than UIO_MAXIOV elements in iovec array
* total size of all ranges is no more than MAX_RW_COUNT
* all ranges pass access_ok().
The problem is, invariants of data structures should be established in the
primitives creating those data structures, not in the code using those
primitives. And iov_iter_init() violates that principle. For a while we
managed to get away with that, but once the use of iov_iter started to
spread, it didn't take long for shit to hit the fan - missed check in
sys_sendto() had introduced a roothole.
We _do_ have primitives for importing and validating iovecs (both native and
compat ones) and those primitives are almost always followed by shoving the
resulting iovec into iov_iter. Life would be considerably simpler (and safer)
if we combined those primitives with initializing iov_iter.
That gives us two new primitives - import_iovec() and compat_import_iovec().
Calling conventions:
iovec = iov_array;
err = import_iovec(direction, uvec, nr_segs,
ARRAY_SIZE(iov_array), &iovec,
&iter);
imports user vector into kernel space (into iov_array if it fits, allocated
if it doesn't fit or if iovec was NULL), validates it and sets iter up to
refer to it. On success 0 is returned and allocated kernel copy (or NULL
if the array had fit into caller-supplied one) is returned via iovec.
On failure all allocations are undone and -E... is returned. If the total
size of ranges exceeds MAX_RW_COUNT, the excess is silently truncated.
compat_import_iovec() expects uvec to be a pointer to user array of compat_iovec;
otherwise it's identical to import_iovec().
Finally, import_single_range() sets iov_iter backed by single-element iovec
covering a user-supplied range -
err = import_single_range(direction, address, size, iovec, &iter);
does validation and sets iter up. Again, size in excess of MAX_RW_COUNT gets
silently truncated.
Next commits will be switching the things up to use of those and reducing
the amount of iov_iter_init() instances.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2015-03-22 04:45:43 +07:00
|
|
|
|
|
|
|
int import_iovec(int type, const struct iovec __user * uvector,
|
|
|
|
unsigned nr_segs, unsigned fast_segs,
|
|
|
|
struct iovec **iov, struct iov_iter *i)
|
|
|
|
{
|
|
|
|
ssize_t n;
|
|
|
|
struct iovec *p;
|
|
|
|
n = rw_copy_check_uvector(type, uvector, nr_segs, fast_segs,
|
|
|
|
*iov, &p);
|
|
|
|
if (n < 0) {
|
|
|
|
if (p != *iov)
|
|
|
|
kfree(p);
|
|
|
|
*iov = NULL;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
iov_iter_init(i, type, p, nr_segs, n);
|
|
|
|
*iov = p == *iov ? NULL : p;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(import_iovec);
|
|
|
|
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
#include <linux/compat.h>
|
|
|
|
|
|
|
|
int compat_import_iovec(int type, const struct compat_iovec __user * uvector,
|
|
|
|
unsigned nr_segs, unsigned fast_segs,
|
|
|
|
struct iovec **iov, struct iov_iter *i)
|
|
|
|
{
|
|
|
|
ssize_t n;
|
|
|
|
struct iovec *p;
|
|
|
|
n = compat_rw_copy_check_uvector(type, uvector, nr_segs, fast_segs,
|
|
|
|
*iov, &p);
|
|
|
|
if (n < 0) {
|
|
|
|
if (p != *iov)
|
|
|
|
kfree(p);
|
|
|
|
*iov = NULL;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
iov_iter_init(i, type, p, nr_segs, n);
|
|
|
|
*iov = p == *iov ? NULL : p;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int import_single_range(int rw, void __user *buf, size_t len,
|
|
|
|
struct iovec *iov, struct iov_iter *i)
|
|
|
|
{
|
|
|
|
if (len > MAX_RW_COUNT)
|
|
|
|
len = MAX_RW_COUNT;
|
|
|
|
if (unlikely(!access_ok(!rw, buf, len)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
iov->iov_base = buf;
|
|
|
|
iov->iov_len = len;
|
|
|
|
iov_iter_init(i, rw, iov, 1, len);
|
|
|
|
return 0;
|
|
|
|
}
|
2015-12-07 08:38:56 +07:00
|
|
|
EXPORT_SYMBOL(import_single_range);
|