Btrfs: Verify checksums on tree blocks found without read_tree_block
authorChris Mason <chris.mason@oracle.com>
Tue, 1 Apr 2008 17:48:14 +0000 (13:48 -0400)
committerChris Mason <chris.mason@oracle.com>
Thu, 25 Sep 2008 15:04:01 +0000 (11:04 -0400)
Checksums were only verified by btrfs_read_tree_block, which meant the
functions to probe the page cache for blocks were not validating checksums.
Normally this is fine because the buffers will only be in cache if they
have already been validated.

But, there is a window while the buffer is being read from disk where
it could be up to date in the cache but not yet verified.  This patch
makes sure all buffers go through checksum verification before they
are used.

This is safer, and it prevents modification of buffers before they go
through the csum code.

Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/ctree.c
fs/btrfs/disk-io.c
fs/btrfs/disk-io.h
fs/btrfs/extent-tree.c
fs/btrfs/tree-defrag.c

index 6ba5394834bef19a172ba7e19776ecc4da42fa78..df090bf2eec061c7c29222a2578327c3e92feb12 100644 (file)
@@ -158,6 +158,8 @@ int __btrfs_cow_block(struct btrfs_trans_handle *trans,
        } else {
                root_gen = 0;
        }
+       if (!(buf->flags & EXTENT_CSUM))
+               WARN_ON(1);
 
        WARN_ON(root->ref_cows && trans->transid !=
                root->fs_info->running_transaction->transid);
@@ -245,6 +247,8 @@ int btrfs_cow_block(struct btrfs_trans_handle *trans,
                       root->fs_info->generation);
                WARN_ON(1);
        }
+       if (!(buf->flags & EXTENT_CSUM))
+               WARN_ON(1);
 
        header_trans = btrfs_header_generation(buf);
        spin_lock(&root->fs_info->hash_lock);
@@ -396,6 +400,7 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
                if (search_start == 0)
                        search_start = last_block;
 
+               btrfs_verify_block_csum(root, cur);
                err = __btrfs_cow_block(trans, root, cur, parent, i,
                                        &tmp, search_start,
                                        min(16 * blocksize,
index 5547607681f4147c44e8faf94511a0d8bd012d5b..e40fb318ad99e16d10393e5fb6af601708b836fb 100644 (file)
@@ -46,27 +46,6 @@ static int check_tree_block(struct btrfs_root *root, struct extent_buffer *buf)
 
 static struct extent_io_ops btree_extent_io_ops;
 
-struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root,
-                                           u64 bytenr, u32 blocksize)
-{
-       struct inode *btree_inode = root->fs_info->btree_inode;
-       struct extent_buffer *eb;
-       eb = find_extent_buffer(&BTRFS_I(btree_inode)->io_tree,
-                               bytenr, blocksize, GFP_NOFS);
-       return eb;
-}
-
-struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root,
-                                                u64 bytenr, u32 blocksize)
-{
-       struct inode *btree_inode = root->fs_info->btree_inode;
-       struct extent_buffer *eb;
-
-       eb = alloc_extent_buffer(&BTRFS_I(btree_inode)->io_tree,
-                                bytenr, blocksize, NULL, GFP_NOFS);
-       return eb;
-}
-
 struct extent_map *btree_get_extent(struct inode *inode, struct page *page,
                                    size_t page_offset, u64 start, u64 len,
                                    int create)
@@ -380,36 +359,29 @@ static int close_all_devices(struct btrfs_fs_info *fs_info)
        return 0;
 }
 
-struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
-                                     u32 blocksize)
+int btrfs_verify_block_csum(struct btrfs_root *root,
+                           struct extent_buffer *buf)
 {
-       struct extent_buffer *buf = NULL;
-       struct inode *btree_inode = root->fs_info->btree_inode;
        struct extent_io_tree *io_tree;
        u64 end;
        int ret;
 
-       io_tree = &BTRFS_I(btree_inode)->io_tree;
-
-       buf = btrfs_find_create_tree_block(root, bytenr, blocksize);
-       if (!buf)
-               return NULL;
-       read_extent_buffer_pages(&BTRFS_I(btree_inode)->io_tree, buf, 0, 1,
-                                btree_get_extent);
-
+       io_tree = &BTRFS_I(root->fs_info->btree_inode)->io_tree;
        if (buf->flags & EXTENT_CSUM)
-               return buf;
+               return 0;
 
-       end = buf->start + PAGE_CACHE_SIZE - 1;
+       end = min_t(u64, buf->len, PAGE_CACHE_SIZE);
+       end = buf->start + end - 1;
        if (test_range_bit(io_tree, buf->start, end, EXTENT_CSUM, 1)) {
                buf->flags |= EXTENT_CSUM;
-               return buf;
+               return 0;
        }
 
        lock_extent(io_tree, buf->start, end, GFP_NOFS);
 
        if (test_range_bit(io_tree, buf->start, end, EXTENT_CSUM, 1)) {
                buf->flags |= EXTENT_CSUM;
+               ret = 0;
                goto out_unlock;
        }
 
@@ -419,6 +391,48 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
 
 out_unlock:
        unlock_extent(io_tree, buf->start, end, GFP_NOFS);
+       return ret;
+}
+
+struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root,
+                                           u64 bytenr, u32 blocksize)
+{
+       struct inode *btree_inode = root->fs_info->btree_inode;
+       struct extent_buffer *eb;
+       eb = find_extent_buffer(&BTRFS_I(btree_inode)->io_tree,
+                               bytenr, blocksize, GFP_NOFS);
+       return eb;
+}
+
+struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root,
+                                                u64 bytenr, u32 blocksize)
+{
+       struct inode *btree_inode = root->fs_info->btree_inode;
+       struct extent_buffer *eb;
+
+       eb = alloc_extent_buffer(&BTRFS_I(btree_inode)->io_tree,
+                                bytenr, blocksize, NULL, GFP_NOFS);
+       return eb;
+}
+
+
+struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
+                                     u32 blocksize)
+{
+       struct extent_buffer *buf = NULL;
+       struct inode *btree_inode = root->fs_info->btree_inode;
+       struct extent_io_tree *io_tree;
+       int ret;
+
+       io_tree = &BTRFS_I(btree_inode)->io_tree;
+
+       buf = btrfs_find_create_tree_block(root, bytenr, blocksize);
+       if (!buf)
+               return NULL;
+       read_extent_buffer_pages(&BTRFS_I(btree_inode)->io_tree, buf, 0, 1,
+                                btree_get_extent);
+
+       ret = btrfs_verify_block_csum(root, buf);
        return buf;
 }
 
index b7cbc58a555300badf7600e6be075c3dbd04e828..05b88d0e75eb7c3defc5c3ced95e8cd084023acf 100644 (file)
@@ -69,4 +69,6 @@ u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len);
 void btrfs_csum_final(u32 crc, char *result);
 void btrfs_throttle(struct btrfs_root *root);
 int btrfs_open_device(struct btrfs_device *dev);
+int btrfs_verify_block_csum(struct btrfs_root *root,
+                           struct extent_buffer *buf);
 #endif
index cf283b0271ac417df65062f5f126a544bc380b39..a34c289aec2132faae8296b98232befe737e1ab0 100644 (file)
@@ -2069,6 +2069,8 @@ static int noinline walk_down_tree(struct btrfs_trans_handle *trans,
                                BUG_ON(ret);
                                continue;
                        }
+               } else if (next) {
+                       btrfs_verify_block_csum(root, next);
                }
                WARN_ON(*level <= 0);
                if (path->nodes[*level-1])
index 5935cbd8f2b83c7646c17f2823d36746b3151be1..256af1870eef3696c167b450c7c2cdab42c87fe8 100644 (file)
@@ -101,6 +101,7 @@ static int defrag_walk_down(struct btrfs_trans_handle *trans,
                                path->slots[*level]++;
                                continue;
                        }
+                       btrfs_verify_block_csum(root, next);
                } else {
                        next = read_tree_block(root, bytenr,
                                       btrfs_level_size(root, *level - 1));