diff options
author | Anton Altaparmakov <aia21@cam.ac.uk> | 2005-06-23 03:10:21 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-06-23 12:45:34 -0400 |
commit | 152becd26e0563aefdbc4fd1fe491928efe92d1f (patch) | |
tree | 713a5ff0a1610708babf7e57fd3141cd4b9f183e /fs | |
parent | 9a59f452abe11f569e13ec16c51e6d61c54b9838 (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')
-rw-r--r-- | fs/buffer.c | 12 |
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 |