aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNick Piggin <npiggin@suse.de>2007-10-16 04:24:56 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-16 12:42:54 -0400
commit64649a58919e66ec21792dbb6c48cb3da22cbd7f (patch)
tree054605b7da7bad43c299ef66de9d33fda87cd38d
parent5fe172370687e03cc6ba8dca990b75db18ff9bb3 (diff)
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: Nick Piggin <npiggin@suse.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--mm/filemap.c80
1 files changed, 40 insertions, 40 deletions
diff --git a/mm/filemap.c b/mm/filemap.c
index 0c54fc9b8e3d..73b98c6a3389 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -1895,22 +1895,9 @@ generic_file_buffered_write(struct kiocb *iocb, const struct iovec *iov,
1895 } 1895 }
1896 1896
1897 status = a_ops->prepare_write(file, page, offset, offset+bytes); 1897 status = a_ops->prepare_write(file, page, offset, offset+bytes);
1898 if (unlikely(status)) { 1898 if (unlikely(status))
1899 loff_t isize = i_size_read(inode); 1899 goto fs_write_aop_error;
1900 1900
1901 if (status != AOP_TRUNCATED_PAGE)
1902 unlock_page(page);
1903 page_cache_release(page);
1904 if (status == AOP_TRUNCATED_PAGE)
1905 continue;
1906 /*
1907 * prepare_write() may have instantiated a few blocks
1908 * outside i_size. Trim these off again.
1909 */
1910 if (pos + bytes > isize)
1911 vmtruncate(inode, isize);
1912 break;
1913 }
1914 if (likely(nr_segs == 1)) 1901 if (likely(nr_segs == 1))
1915 copied = filemap_copy_from_user(page, offset, 1902 copied = filemap_copy_from_user(page, offset,
1916 buf, bytes); 1903 buf, bytes);
@@ -1919,40 +1906,53 @@ generic_file_buffered_write(struct kiocb *iocb, const struct iovec *iov,
1919 cur_iov, iov_offset, bytes); 1906 cur_iov, iov_offset, bytes);
1920 flush_dcache_page(page); 1907 flush_dcache_page(page);
1921 status = a_ops->commit_write(file, page, offset, offset+bytes); 1908 status = a_ops->commit_write(file, page, offset, offset+bytes);
1922 if (status == AOP_TRUNCATED_PAGE) { 1909 if (unlikely(status < 0 || status == AOP_TRUNCATED_PAGE))
1923 page_cache_release(page); 1910 goto fs_write_aop_error;
1924 continue; 1911 if (unlikely(copied != bytes)) {
1912 status = -EFAULT;
1913 goto fs_write_aop_error;
1925 } 1914 }
1915 if (unlikely(status > 0)) /* filesystem did partial write */
1916 copied = status;
1917
1926 if (likely(copied > 0)) { 1918 if (likely(copied > 0)) {
1927 if (!status) 1919 written += copied;
1928 status = copied; 1920 count -= copied;
1929 1921 pos += copied;
1930 if (status >= 0) { 1922 buf += copied;
1931 written += status; 1923 if (unlikely(nr_segs > 1)) {
1932 count -= status; 1924 filemap_set_next_iovec(&cur_iov,
1933 pos += status; 1925 &iov_offset, copied);
1934 buf += status; 1926 if (count)
1935 if (unlikely(nr_segs > 1)) { 1927 buf = cur_iov->iov_base + iov_offset;
1936 filemap_set_next_iovec(&cur_iov, 1928 } else {
1937 &iov_offset, status); 1929 iov_offset += copied;
1938 if (count)
1939 buf = cur_iov->iov_base +
1940 iov_offset;
1941 } else {
1942 iov_offset += status;
1943 }
1944 } 1930 }
1945 } 1931 }
1946 if (unlikely(copied != bytes))
1947 if (status >= 0)
1948 status = -EFAULT;
1949 unlock_page(page); 1932 unlock_page(page);
1950 mark_page_accessed(page); 1933 mark_page_accessed(page);
1951 page_cache_release(page); 1934 page_cache_release(page);
1952 if (status < 0)
1953 break;
1954 balance_dirty_pages_ratelimited(mapping); 1935 balance_dirty_pages_ratelimited(mapping);
1955 cond_resched(); 1936 cond_resched();
1937 continue;
1938
1939fs_write_aop_error:
1940 if (status != AOP_TRUNCATED_PAGE)
1941 unlock_page(page);
1942 page_cache_release(page);
1943
1944 /*
1945 * prepare_write() may have instantiated a few blocks
1946 * outside i_size. Trim these off again. Don't need
1947 * i_size_read because we hold i_mutex.
1948 */
1949 if (pos + bytes > inode->i_size)
1950 vmtruncate(inode, inode->i_size);
1951 if (status == AOP_TRUNCATED_PAGE)
1952 continue;
1953 else
1954 break;
1955
1956 } while (count); 1956 } while (count);
1957 *ppos = pos; 1957 *ppos = pos;
1958 1958