diff options
author | Nick Piggin <npiggin@suse.de> | 2007-10-16 04:24:56 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-16 12:42:54 -0400 |
commit | 64649a58919e66ec21792dbb6c48cb3da22cbd7f (patch) | |
tree | 054605b7da7bad43c299ef66de9d33fda87cd38d | |
parent | 5fe172370687e03cc6ba8dca990b75db18ff9bb3 (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.c | 80 |
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 | |||
1939 | fs_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 | ||