summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/cifs/file.c34
1 files changed, 31 insertions, 3 deletions
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index c55808edddf4..aa05d5e6c3ae 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -2107,7 +2107,7 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
{
unsigned int written;
unsigned long num_pages, npages, i;
- size_t copied, len, cur_len;
+ size_t bytes, copied, len, cur_len;
ssize_t total_written = 0;
struct kvec *to_send;
struct page **pages;
@@ -2165,17 +2165,45 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
do {
size_t save_len = cur_len;
for (i = 0; i < npages; i++) {
- copied = min_t(const size_t, cur_len, PAGE_CACHE_SIZE);
+ bytes = min_t(const size_t, cur_len, PAGE_CACHE_SIZE);
copied = iov_iter_copy_from_user(pages[i], &it, 0,
- copied);
+ bytes);
cur_len -= copied;
iov_iter_advance(&it, copied);
to_send[i+1].iov_base = kmap(pages[i]);
to_send[i+1].iov_len = copied;
+ /*
+ * If we didn't copy as much as we expected, then that
+ * may mean we trod into an unmapped area. Stop copying
+ * at that point. On the next pass through the big
+ * loop, we'll likely end up getting a zero-length
+ * write and bailing out of it.
+ */
+ if (copied < bytes)
+ break;
}
cur_len = save_len - cur_len;
+ /*
+ * If we have no data to send, then that probably means that
+ * the copy above failed altogether. That's most likely because
+ * the address in the iovec was bogus. Set the rc to -EFAULT,
+ * free anything we allocated and bail out.
+ */
+ if (!cur_len) {
+ kunmap(pages[0]);
+ if (!total_written)
+ total_written = -EFAULT;
+ break;
+ }
+
+ /*
+ * i + 1 now represents the number of pages we actually used in
+ * the copy phase above.
+ */
+ npages = min(npages, i + 1);
+
do {
if (open_file->invalidHandle) {
rc = cifs_reopen_file(open_file, false);