aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2012-11-26 09:48:41 -0500
committerSteve French <smfrench@gmail.com>2012-11-27 14:46:12 -0500
commit3a98b8614312026d489e56c1d0e294a68e2aad77 (patch)
tree19230b635db898db1ca0661cbc4170fca9bccade /fs
parent5e351cdc998db82935d1248a053a1be37d1160fd (diff)
cifs: fix writeback race with file that is growing
Commit eddb079deb4 created a regression in the writepages codepath. Previously, whenever it needed to check the size of the file, it did so by consulting the inode->i_size field directly. With that patch, the i_size was fetched once on entry into the writepages code and that value was used henceforth. If the file is changing size though (for instance, if someone is writing to it or has truncated it), then that value is likely to be wrong. This can lead to data corruption. Pages past the EOF at the time that the writepages call was issued may be silently dropped and ignored because cifs_writepages wrongly assumes that the file must have been truncated in the interim. Fix cifs_writepages to properly fetch the size from the inode->i_size field instead to properly account for this possibility. Original bug report is here: https://bugzilla.kernel.org/show_bug.cgi?id=50991 Reported-and-Tested-by: Maxim Britov <ungifted01@gmail.com> Reviewed-by: Suresh Jayaraman <sjayaraman@suse.com> Signed-off-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/cifs/file.c6
1 files changed, 3 insertions, 3 deletions
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index edb25b4bbb95..70b6f4c3a0c1 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -1794,7 +1794,6 @@ static int cifs_writepages(struct address_space *mapping,
1794 struct TCP_Server_Info *server; 1794 struct TCP_Server_Info *server;
1795 struct page *page; 1795 struct page *page;
1796 int rc = 0; 1796 int rc = 0;
1797 loff_t isize = i_size_read(mapping->host);
1798 1797
1799 /* 1798 /*
1800 * If wsize is smaller than the page cache size, default to writing 1799 * If wsize is smaller than the page cache size, default to writing
@@ -1899,7 +1898,7 @@ retry:
1899 */ 1898 */
1900 set_page_writeback(page); 1899 set_page_writeback(page);
1901 1900
1902 if (page_offset(page) >= isize) { 1901 if (page_offset(page) >= i_size_read(mapping->host)) {
1903 done = true; 1902 done = true;
1904 unlock_page(page); 1903 unlock_page(page);
1905 end_page_writeback(page); 1904 end_page_writeback(page);
@@ -1932,7 +1931,8 @@ retry:
1932 wdata->offset = page_offset(wdata->pages[0]); 1931 wdata->offset = page_offset(wdata->pages[0]);
1933 wdata->pagesz = PAGE_CACHE_SIZE; 1932 wdata->pagesz = PAGE_CACHE_SIZE;
1934 wdata->tailsz = 1933 wdata->tailsz =
1935 min(isize - page_offset(wdata->pages[nr_pages - 1]), 1934 min(i_size_read(mapping->host) -
1935 page_offset(wdata->pages[nr_pages - 1]),
1936 (loff_t)PAGE_CACHE_SIZE); 1936 (loff_t)PAGE_CACHE_SIZE);
1937 wdata->bytes = ((nr_pages - 1) * PAGE_CACHE_SIZE) + 1937 wdata->bytes = ((nr_pages - 1) * PAGE_CACHE_SIZE) +
1938 wdata->tailsz; 1938 wdata->tailsz;