Fix a possible race between expanding ftruncate() and block_write_full_page()'s clearing of the page outside i_size. Signed-off-by: Andrew Morton --- 25-akpm/mm/memory.c | 20 +++++++++++++++++++- 1 files changed, 19 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 Fri Jun 18 15:40:33 2004 +++ 25-akpm/mm/memory.c Fri Jun 18 15:40:33 2004 @@ -1217,6 +1217,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; @@ -1231,8 +1233,24 @@ do_expand: goto out_sig; if (offset > inode->i_sb->s_maxbytes) goto out; - i_size_write(inode, offset); + /* + * Put a pagecache page at the current i_size and 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 = grab_cache_page(inode->i_mapping, + i_size >> PAGE_CACHE_SHIFT); + i_size_write(inode, offset); + if (page) { + unlock_page(page); + page_cache_release(page); + } out_truncate: if (inode->i_op && inode->i_op->truncate) inode->i_op->truncate(inode); _