aboutsummaryrefslogtreecommitdiffstats
path: root/fs/buffer.c
diff options
context:
space:
mode:
authorAnton Altaparmakov <aia21@cam.ac.uk>2005-06-23 03:10:21 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-06-23 12:45:34 -0400
commit152becd26e0563aefdbc4fd1fe491928efe92d1f (patch)
tree713a5ff0a1610708babf7e57fd3141cd4b9f183e /fs/buffer.c
parent9a59f452abe11f569e13ec16c51e6d61c54b9838 (diff)
[PATCH] Bug in error recovery in fs/buffer.c::__block_prepare_write()
fs/buffer.c::__block_prepare_write() has broken error recovery. It calls the get_block() callback with "create = 1" and if that succeeds it immediately clears buffer_new on the just allocated buffer (which has buffer_new set). The bug is that if an error occurs and get_block() returns != 0, we break from this loop and go into recovery code. This code has this comment: /* Error case: */ /* * Zero out any newly allocated blocks to avoid exposing stale * data. If BH_New is set, we know that the block was newly * allocated in the above loop. */ So the intent is obviously good in that it wants to clear just allocated and hence not zeroed buffers. However the code recognises allocated buffers by checking for buffer_new being set. Unfortunately __block_prepare_write() as discussed above already cleared buffer_new on all allocated buffers thus no buffers will be cleared during error recovery and old data will be leaked. The simplest way I can see to fix this is to make the current recovery code work by _not_ clearing buffer_new after calling get_block() in __block_prepare_write(). We cannot safely allow buffer_new buffers to "leak out" of __block_prepare_write(), thus we simply do a quick loop over the buffers clearing buffer_new on each of them if it is set just before returning "success" from __block_prepare_write(). Signed-off-by: Anton Altaparmakov <aia21@cantab.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs/buffer.c')
-rw-r--r--fs/buffer.c12
1 files changed, 8 insertions, 4 deletions
diff --git a/fs/buffer.c b/fs/buffer.c
index 12bdb2791127..13e5938a64f6 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -1926,7 +1926,6 @@ static int __block_prepare_write(struct inode *inode, struct page *page,
1926 if (err) 1926 if (err)
1927 break; 1927 break;
1928 if (buffer_new(bh)) { 1928 if (buffer_new(bh)) {
1929 clear_buffer_new(bh);
1930 unmap_underlying_metadata(bh->b_bdev, 1929 unmap_underlying_metadata(bh->b_bdev,
1931 bh->b_blocknr); 1930 bh->b_blocknr);
1932 if (PageUptodate(page)) { 1931 if (PageUptodate(page)) {
@@ -1968,9 +1967,14 @@ static int __block_prepare_write(struct inode *inode, struct page *page,
1968 if (!buffer_uptodate(*wait_bh)) 1967 if (!buffer_uptodate(*wait_bh))
1969 err = -EIO; 1968 err = -EIO;
1970 } 1969 }
1971 if (!err) 1970 if (!err) {
1972 return err; 1971 bh = head;
1973 1972 do {
1973 if (buffer_new(bh))
1974 clear_buffer_new(bh);
1975 } while ((bh = bh->b_this_page) != head);
1976 return 0;
1977 }
1974 /* Error case: */ 1978 /* Error case: */
1975 /* 1979 /*
1976 * Zero out any newly allocated blocks to avoid exposing stale 1980 * Zero out any newly allocated blocks to avoid exposing stale