Merge branch 'work.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[deliverable/linux.git] / lib / iov_iter.c
index 9d96e283520cc7f3ec27714dfa4abfcb3800e319..5fecddc32b1b47e1c2d7ce9706b9a840995f3d4e 100644 (file)
@@ -317,6 +317,32 @@ int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes)
 }
 EXPORT_SYMBOL(iov_iter_fault_in_readable);
 
+/*
+ * 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);
+
 void iov_iter_init(struct iov_iter *i, int direction,
                        const struct iovec *iov, unsigned long nr_segs,
                        size_t count)
@@ -343,7 +369,7 @@ static void memcpy_from_page(char *to, struct page *page, size_t offset, size_t
        kunmap_atomic(from);
 }
 
-static void memcpy_to_page(struct page *page, size_t offset, char *from, size_t len)
+static void memcpy_to_page(struct page *page, size_t offset, const char *from, size_t len)
 {
        char *to = kmap_atomic(page);
        memcpy(to + offset, from, len);
@@ -357,9 +383,9 @@ static void memzero_page(struct page *page, size_t offset, size_t len)
        kunmap_atomic(addr);
 }
 
-size_t copy_to_iter(void *addr, size_t bytes, struct iov_iter *i)
+size_t copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i)
 {
-       char *from = addr;
+       const char *from = addr;
        if (unlikely(bytes > i->count))
                bytes = i->count;
 
@@ -678,10 +704,10 @@ size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum,
 }
 EXPORT_SYMBOL(csum_and_copy_from_iter);
 
-size_t csum_and_copy_to_iter(void *addr, size_t bytes, __wsum *csum,
+size_t csum_and_copy_to_iter(const void *addr, size_t bytes, __wsum *csum,
                             struct iov_iter *i)
 {
-       char *from = addr;
+       const char *from = addr;
        __wsum sum, next;
        size_t off = 0;
        if (unlikely(bytes > i->count))
@@ -766,3 +792,61 @@ const void *dup_iter(struct iov_iter *new, struct iov_iter *old, gfp_t flags)
                                   flags);
 }
 EXPORT_SYMBOL(dup_iter);
+
+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;
+}
+EXPORT_SYMBOL(import_single_range);
This page took 0.034794 seconds and 5 git commands to generate.