fix copy_page_from_iter() for compound destinations
authorAl Viro <viro@zeniv.linux.org.uk>
Fri, 29 Jul 2022 16:54:53 +0000 (12:54 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Tue, 9 Aug 2022 02:37:26 +0000 (22:37 -0400)
had been broken for ITER_BVEC et.al. since ever (OK, v3.17 when
ITER_BVEC had first appeared)...

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
lib/iov_iter.c

index 4e3696d349a484acb5e8e4ae4546d8f078dcfdeb..4b7fce72e3e521348851c56f4a6335f8728c07b4 100644 (file)
@@ -738,13 +738,27 @@ 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)
 {
-       if (page_copy_sane(page, offset, bytes)) {
+       size_t res = 0;
+       if (!page_copy_sane(page, offset, bytes))
+               return 0;
+       page += offset / PAGE_SIZE; // first subpage
+       offset %= PAGE_SIZE;
+       while (1) {
                void *kaddr = kmap_local_page(page);
-               size_t wanted = _copy_from_iter(kaddr + offset, bytes, i);
+               size_t n = min(bytes, (size_t)PAGE_SIZE - offset);
+               n = _copy_from_iter(kaddr + offset, n, i);
                kunmap_local(kaddr);
-               return wanted;
+               res += n;
+               bytes -= n;
+               if (!bytes || !n)
+                       break;
+               offset += n;
+               if (offset == PAGE_SIZE) {
+                       page++;
+                       offset = 0;
+               }
        }
-       return 0;
+       return res;
 }
 EXPORT_SYMBOL(copy_page_from_iter);