aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
authorChris Mason <chris.mason@oracle.com>2008-04-01 13:48:14 -0400
committerChris Mason <chris.mason@oracle.com>2008-09-25 11:04:01 -0400
commit0999df54f850fe1aba29b10d5c869493af107478 (patch)
treec67e0ca38e89d0872f6246093d962ce598b49ec7 /fs/btrfs
parentecbe2402cb4e4e7413544dc392c1a78d0f290292 (diff)
Btrfs: Verify checksums on tree blocks found without read_tree_block
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>
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/ctree.c5
-rw-r--r--fs/btrfs/disk-io.c86
-rw-r--r--fs/btrfs/disk-io.h2
-rw-r--r--fs/btrfs/extent-tree.c2
-rw-r--r--fs/btrfs/tree-defrag.c1
5 files changed, 60 insertions, 36 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 6ba5394834be..df090bf2eec0 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -158,6 +158,8 @@ int __btrfs_cow_block(struct btrfs_trans_handle *trans,
158 } else { 158 } else {
159 root_gen = 0; 159 root_gen = 0;
160 } 160 }
161 if (!(buf->flags & EXTENT_CSUM))
162 WARN_ON(1);
161 163
162 WARN_ON(root->ref_cows && trans->transid != 164 WARN_ON(root->ref_cows && trans->transid !=
163 root->fs_info->running_transaction->transid); 165 root->fs_info->running_transaction->transid);
@@ -245,6 +247,8 @@ int btrfs_cow_block(struct btrfs_trans_handle *trans,
245 root->fs_info->generation); 247 root->fs_info->generation);
246 WARN_ON(1); 248 WARN_ON(1);
247 } 249 }
250 if (!(buf->flags & EXTENT_CSUM))
251 WARN_ON(1);
248 252
249 header_trans = btrfs_header_generation(buf); 253 header_trans = btrfs_header_generation(buf);
250 spin_lock(&root->fs_info->hash_lock); 254 spin_lock(&root->fs_info->hash_lock);
@@ -396,6 +400,7 @@ int btrfs_realloc_node(struct btrfs_trans_handle *trans,
396 if (search_start == 0) 400 if (search_start == 0)
397 search_start = last_block; 401 search_start = last_block;
398 402
403 btrfs_verify_block_csum(root, cur);
399 err = __btrfs_cow_block(trans, root, cur, parent, i, 404 err = __btrfs_cow_block(trans, root, cur, parent, i,
400 &tmp, search_start, 405 &tmp, search_start,
401 min(16 * blocksize, 406 min(16 * blocksize,
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 5547607681f4..e40fb318ad99 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -46,27 +46,6 @@ static int check_tree_block(struct btrfs_root *root, struct extent_buffer *buf)
46 46
47static struct extent_io_ops btree_extent_io_ops; 47static struct extent_io_ops btree_extent_io_ops;
48 48
49struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root,
50 u64 bytenr, u32 blocksize)
51{
52 struct inode *btree_inode = root->fs_info->btree_inode;
53 struct extent_buffer *eb;
54 eb = find_extent_buffer(&BTRFS_I(btree_inode)->io_tree,
55 bytenr, blocksize, GFP_NOFS);
56 return eb;
57}
58
59struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root,
60 u64 bytenr, u32 blocksize)
61{
62 struct inode *btree_inode = root->fs_info->btree_inode;
63 struct extent_buffer *eb;
64
65 eb = alloc_extent_buffer(&BTRFS_I(btree_inode)->io_tree,
66 bytenr, blocksize, NULL, GFP_NOFS);
67 return eb;
68}
69
70struct extent_map *btree_get_extent(struct inode *inode, struct page *page, 49struct extent_map *btree_get_extent(struct inode *inode, struct page *page,
71 size_t page_offset, u64 start, u64 len, 50 size_t page_offset, u64 start, u64 len,
72 int create) 51 int create)
@@ -380,36 +359,29 @@ static int close_all_devices(struct btrfs_fs_info *fs_info)
380 return 0; 359 return 0;
381} 360}
382 361
383struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr, 362int btrfs_verify_block_csum(struct btrfs_root *root,
384 u32 blocksize) 363 struct extent_buffer *buf)
385{ 364{
386 struct extent_buffer *buf = NULL;
387 struct inode *btree_inode = root->fs_info->btree_inode;
388 struct extent_io_tree *io_tree; 365 struct extent_io_tree *io_tree;
389 u64 end; 366 u64 end;
390 int ret; 367 int ret;
391 368
392 io_tree = &BTRFS_I(btree_inode)->io_tree; 369 io_tree = &BTRFS_I(root->fs_info->btree_inode)->io_tree;
393
394 buf = btrfs_find_create_tree_block(root, bytenr, blocksize);
395 if (!buf)
396 return NULL;
397 read_extent_buffer_pages(&BTRFS_I(btree_inode)->io_tree, buf, 0, 1,
398 btree_get_extent);
399
400 if (buf->flags & EXTENT_CSUM) 370 if (buf->flags & EXTENT_CSUM)
401 return buf; 371 return 0;
402 372
403 end = buf->start + PAGE_CACHE_SIZE - 1; 373 end = min_t(u64, buf->len, PAGE_CACHE_SIZE);
374 end = buf->start + end - 1;
404 if (test_range_bit(io_tree, buf->start, end, EXTENT_CSUM, 1)) { 375 if (test_range_bit(io_tree, buf->start, end, EXTENT_CSUM, 1)) {
405 buf->flags |= EXTENT_CSUM; 376 buf->flags |= EXTENT_CSUM;
406 return buf; 377 return 0;
407 } 378 }
408 379
409 lock_extent(io_tree, buf->start, end, GFP_NOFS); 380 lock_extent(io_tree, buf->start, end, GFP_NOFS);
410 381
411 if (test_range_bit(io_tree, buf->start, end, EXTENT_CSUM, 1)) { 382 if (test_range_bit(io_tree, buf->start, end, EXTENT_CSUM, 1)) {
412 buf->flags |= EXTENT_CSUM; 383 buf->flags |= EXTENT_CSUM;
384 ret = 0;
413 goto out_unlock; 385 goto out_unlock;
414 } 386 }
415 387
@@ -419,6 +391,48 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
419 391
420out_unlock: 392out_unlock:
421 unlock_extent(io_tree, buf->start, end, GFP_NOFS); 393 unlock_extent(io_tree, buf->start, end, GFP_NOFS);
394 return ret;
395}
396
397struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root,
398 u64 bytenr, u32 blocksize)
399{
400 struct inode *btree_inode = root->fs_info->btree_inode;
401 struct extent_buffer *eb;
402 eb = find_extent_buffer(&BTRFS_I(btree_inode)->io_tree,
403 bytenr, blocksize, GFP_NOFS);
404 return eb;
405}
406
407struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root,
408 u64 bytenr, u32 blocksize)
409{
410 struct inode *btree_inode = root->fs_info->btree_inode;
411 struct extent_buffer *eb;
412
413 eb = alloc_extent_buffer(&BTRFS_I(btree_inode)->io_tree,
414 bytenr, blocksize, NULL, GFP_NOFS);
415 return eb;
416}
417
418
419struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
420 u32 blocksize)
421{
422 struct extent_buffer *buf = NULL;
423 struct inode *btree_inode = root->fs_info->btree_inode;
424 struct extent_io_tree *io_tree;
425 int ret;
426
427 io_tree = &BTRFS_I(btree_inode)->io_tree;
428
429 buf = btrfs_find_create_tree_block(root, bytenr, blocksize);
430 if (!buf)
431 return NULL;
432 read_extent_buffer_pages(&BTRFS_I(btree_inode)->io_tree, buf, 0, 1,
433 btree_get_extent);
434
435 ret = btrfs_verify_block_csum(root, buf);
422 return buf; 436 return buf;
423} 437}
424 438
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
index b7cbc58a5553..05b88d0e75eb 100644
--- a/fs/btrfs/disk-io.h
+++ b/fs/btrfs/disk-io.h
@@ -69,4 +69,6 @@ u32 btrfs_csum_data(struct btrfs_root *root, char *data, u32 seed, size_t len);
69void btrfs_csum_final(u32 crc, char *result); 69void btrfs_csum_final(u32 crc, char *result);
70void btrfs_throttle(struct btrfs_root *root); 70void btrfs_throttle(struct btrfs_root *root);
71int btrfs_open_device(struct btrfs_device *dev); 71int btrfs_open_device(struct btrfs_device *dev);
72int btrfs_verify_block_csum(struct btrfs_root *root,
73 struct extent_buffer *buf);
72#endif 74#endif
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index cf283b0271ac..a34c289aec21 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -2069,6 +2069,8 @@ static int noinline walk_down_tree(struct btrfs_trans_handle *trans,
2069 BUG_ON(ret); 2069 BUG_ON(ret);
2070 continue; 2070 continue;
2071 } 2071 }
2072 } else if (next) {
2073 btrfs_verify_block_csum(root, next);
2072 } 2074 }
2073 WARN_ON(*level <= 0); 2075 WARN_ON(*level <= 0);
2074 if (path->nodes[*level-1]) 2076 if (path->nodes[*level-1])
diff --git a/fs/btrfs/tree-defrag.c b/fs/btrfs/tree-defrag.c
index 5935cbd8f2b8..256af1870eef 100644
--- a/fs/btrfs/tree-defrag.c
+++ b/fs/btrfs/tree-defrag.c
@@ -101,6 +101,7 @@ static int defrag_walk_down(struct btrfs_trans_handle *trans,
101 path->slots[*level]++; 101 path->slots[*level]++;
102 continue; 102 continue;
103 } 103 }
104 btrfs_verify_block_csum(root, next);
104 } else { 105 } else {
105 next = read_tree_block(root, bytenr, 106 next = read_tree_block(root, bytenr,
106 btrfs_level_size(root, *level - 1)); 107 btrfs_level_size(root, *level - 1));