diff options
-rw-r--r-- | fs/cifs/file.c | 34 |
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); |