


---

 25-akpm/mm/memory.c |   18 +++++++++++++++++-
 1 files changed, 17 insertions(+), 1 deletion(-)

diff -puN mm/memory.c~ftruncate-vs-block_write_full_page mm/memory.c
--- 25/mm/memory.c~ftruncate-vs-block_write_full_page	2004-05-18 18:32:05.720865632 -0700
+++ 25-akpm/mm/memory.c	2004-05-18 18:32:05.725864872 -0700
@@ -1221,6 +1221,8 @@ int vmtruncate(struct inode * inode, lof
 {
 	struct address_space *mapping = inode->i_mapping;
 	unsigned long limit;
+	loff_t i_size;
+	struct page *page;
 
 	if (inode->i_size < offset)
 		goto do_expand;
@@ -1235,8 +1237,22 @@ do_expand:
 		goto out_sig;
 	if (offset > inode->i_sb->s_maxbytes)
 		goto out;
-	i_size_write(inode, offset);
 
+	/*
+	 * If there is a pagecache page at the current i_size we need to lock
+	 * it while modifying i_size to synchronise against
+	 * block_write_full_page()'s sampling of i_size.  Otherwise
+	 * block_write_full_page may decide to memset part of this page after
+	 * the application extended the file size.
+	 */
+	i_size = inode->i_size;	/* don't need i_size_read() due to i_sem */
+	page = NULL;
+	if (i_size & (PAGE_CACHE_SIZE - 1))
+		page = find_lock_page(inode->i_mapping,
+				i_size >> PAGE_CACHE_SHIFT);
+	i_size_write(inode, offset);
+	if (page)
+		unlock_page(page);
 out_truncate:
 	if (inode->i_op && inode->i_op->truncate)
 		inode->i_op->truncate(inode);

_
