Commit 64649a58 authored by Nick Piggin's avatar Nick Piggin Committed by Linus Torvalds

mm: trim more holes

If prepare_write fails with AOP_TRUNCATED_PAGE, or if commit_write fails, then
we may have failed the write operation despite prepare_write having
instantiated blocks past i_size.  Fix this, and consolidate the trimming into
one place.
Signed-off-by: default avatarNick Piggin <npiggin@suse.de>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 5fe17237
...@@ -1895,22 +1895,9 @@ generic_file_buffered_write(struct kiocb *iocb, const struct iovec *iov, ...@@ -1895,22 +1895,9 @@ generic_file_buffered_write(struct kiocb *iocb, const struct iovec *iov,
} }
status = a_ops->prepare_write(file, page, offset, offset+bytes); status = a_ops->prepare_write(file, page, offset, offset+bytes);
if (unlikely(status)) { if (unlikely(status))
loff_t isize = i_size_read(inode); goto fs_write_aop_error;
if (status != AOP_TRUNCATED_PAGE)
unlock_page(page);
page_cache_release(page);
if (status == AOP_TRUNCATED_PAGE)
continue;
/*
* prepare_write() may have instantiated a few blocks
* outside i_size. Trim these off again.
*/
if (pos + bytes > isize)
vmtruncate(inode, isize);
break;
}
if (likely(nr_segs == 1)) if (likely(nr_segs == 1))
copied = filemap_copy_from_user(page, offset, copied = filemap_copy_from_user(page, offset,
buf, bytes); buf, bytes);
...@@ -1919,40 +1906,53 @@ generic_file_buffered_write(struct kiocb *iocb, const struct iovec *iov, ...@@ -1919,40 +1906,53 @@ generic_file_buffered_write(struct kiocb *iocb, const struct iovec *iov,
cur_iov, iov_offset, bytes); cur_iov, iov_offset, bytes);
flush_dcache_page(page); flush_dcache_page(page);
status = a_ops->commit_write(file, page, offset, offset+bytes); status = a_ops->commit_write(file, page, offset, offset+bytes);
if (status == AOP_TRUNCATED_PAGE) { if (unlikely(status < 0 || status == AOP_TRUNCATED_PAGE))
page_cache_release(page); goto fs_write_aop_error;
continue; if (unlikely(copied != bytes)) {
status = -EFAULT;
goto fs_write_aop_error;
} }
if (unlikely(status > 0)) /* filesystem did partial write */
copied = status;
if (likely(copied > 0)) { if (likely(copied > 0)) {
if (!status) written += copied;
status = copied; count -= copied;
pos += copied;
if (status >= 0) { buf += copied;
written += status;
count -= status;
pos += status;
buf += status;
if (unlikely(nr_segs > 1)) { if (unlikely(nr_segs > 1)) {
filemap_set_next_iovec(&cur_iov, filemap_set_next_iovec(&cur_iov,
&iov_offset, status); &iov_offset, copied);
if (count) if (count)
buf = cur_iov->iov_base + buf = cur_iov->iov_base + iov_offset;
iov_offset;
} else { } else {
iov_offset += status; iov_offset += copied;
}
} }
} }
if (unlikely(copied != bytes))
if (status >= 0)
status = -EFAULT;
unlock_page(page); unlock_page(page);
mark_page_accessed(page); mark_page_accessed(page);
page_cache_release(page); page_cache_release(page);
if (status < 0)
break;
balance_dirty_pages_ratelimited(mapping); balance_dirty_pages_ratelimited(mapping);
cond_resched(); cond_resched();
continue;
fs_write_aop_error:
if (status != AOP_TRUNCATED_PAGE)
unlock_page(page);
page_cache_release(page);
/*
* prepare_write() may have instantiated a few blocks
* outside i_size. Trim these off again. Don't need
* i_size_read because we hold i_mutex.
*/
if (pos + bytes > inode->i_size)
vmtruncate(inode, inode->i_size);
if (status == AOP_TRUNCATED_PAGE)
continue;
else
break;
} while (count); } while (count);
*ppos = pos; *ppos = pos;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment