diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-06-01 13:12:15 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-06-01 13:12:15 -0400 |
| commit | 4edebed86690eb8db9af3ab85baf4a34e73266cc (patch) | |
| tree | 8ab144b08f490f239fa62be52470860c9311664d /fs/ext4/extents.c | |
| parent | 51eab603f5c86dd1eae4c525df3e7f7eeab401d6 (diff) | |
| parent | 5e44f8c374dc4f8eadf61cd18b2c0d46bc87c1b7 (diff) | |
Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4
Pull Ext4 updates from Theodore Ts'o:
"The major new feature added in this update is Darrick J Wong's
metadata checksum feature, which adds crc32 checksums to ext4's
metadata fields.
There is also the usual set of cleanups and bug fixes."
* tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (44 commits)
ext4: hole-punch use truncate_pagecache_range
jbd2: use kmem_cache_zalloc wrapper instead of flag
ext4: remove mb_groups before tearing down the buddy_cache
ext4: add ext4_mb_unload_buddy in the error path
ext4: don't trash state flags in EXT4_IOC_SETFLAGS
ext4: let getattr report the right blocks in delalloc+bigalloc
ext4: add missing save_error_info() to ext4_error()
ext4: add debugging trigger for ext4_error()
ext4: protect group inode free counting with group lock
ext4: use consistent ssize_t type in ext4_file_write()
ext4: fix format flag in ext4_ext_binsearch_idx()
ext4: cleanup in ext4_discard_allocated_blocks()
ext4: return ENOMEM when mounts fail due to lack of memory
ext4: remove redundundant "(char *) bh->b_data" casts
ext4: disallow hard-linked directory in ext4_lookup
ext4: fix potential integer overflow in alloc_flex_gd()
ext4: remove needs_recovery in ext4_mb_init()
ext4: force ro mount if ext4_setup_super() fails
ext4: fix potential NULL dereference in ext4_free_inodes_counts()
ext4/jbd2: add metadata checksumming to the list of supported features
...
Diffstat (limited to 'fs/ext4/extents.c')
| -rw-r--r-- | fs/ext4/extents.c | 91 |
1 files changed, 79 insertions, 12 deletions
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index abcdeab67f52..91341ec6e06a 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c | |||
| @@ -52,6 +52,46 @@ | |||
| 52 | #define EXT4_EXT_MARK_UNINIT1 0x2 /* mark first half uninitialized */ | 52 | #define EXT4_EXT_MARK_UNINIT1 0x2 /* mark first half uninitialized */ |
| 53 | #define EXT4_EXT_MARK_UNINIT2 0x4 /* mark second half uninitialized */ | 53 | #define EXT4_EXT_MARK_UNINIT2 0x4 /* mark second half uninitialized */ |
| 54 | 54 | ||
| 55 | static __le32 ext4_extent_block_csum(struct inode *inode, | ||
| 56 | struct ext4_extent_header *eh) | ||
| 57 | { | ||
| 58 | struct ext4_inode_info *ei = EXT4_I(inode); | ||
| 59 | struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); | ||
| 60 | __u32 csum; | ||
| 61 | |||
| 62 | csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)eh, | ||
| 63 | EXT4_EXTENT_TAIL_OFFSET(eh)); | ||
| 64 | return cpu_to_le32(csum); | ||
| 65 | } | ||
| 66 | |||
| 67 | static int ext4_extent_block_csum_verify(struct inode *inode, | ||
| 68 | struct ext4_extent_header *eh) | ||
| 69 | { | ||
| 70 | struct ext4_extent_tail *et; | ||
| 71 | |||
| 72 | if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb, | ||
| 73 | EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) | ||
| 74 | return 1; | ||
| 75 | |||
| 76 | et = find_ext4_extent_tail(eh); | ||
| 77 | if (et->et_checksum != ext4_extent_block_csum(inode, eh)) | ||
| 78 | return 0; | ||
| 79 | return 1; | ||
| 80 | } | ||
| 81 | |||
| 82 | static void ext4_extent_block_csum_set(struct inode *inode, | ||
| 83 | struct ext4_extent_header *eh) | ||
| 84 | { | ||
| 85 | struct ext4_extent_tail *et; | ||
| 86 | |||
| 87 | if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb, | ||
| 88 | EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) | ||
| 89 | return; | ||
| 90 | |||
| 91 | et = find_ext4_extent_tail(eh); | ||
| 92 | et->et_checksum = ext4_extent_block_csum(inode, eh); | ||
| 93 | } | ||
| 94 | |||
| 55 | static int ext4_split_extent(handle_t *handle, | 95 | static int ext4_split_extent(handle_t *handle, |
| 56 | struct inode *inode, | 96 | struct inode *inode, |
| 57 | struct ext4_ext_path *path, | 97 | struct ext4_ext_path *path, |
| @@ -117,6 +157,7 @@ static int __ext4_ext_dirty(const char *where, unsigned int line, | |||
| 117 | { | 157 | { |
| 118 | int err; | 158 | int err; |
| 119 | if (path->p_bh) { | 159 | if (path->p_bh) { |
| 160 | ext4_extent_block_csum_set(inode, ext_block_hdr(path->p_bh)); | ||
| 120 | /* path points to block */ | 161 | /* path points to block */ |
| 121 | err = __ext4_handle_dirty_metadata(where, line, handle, | 162 | err = __ext4_handle_dirty_metadata(where, line, handle, |
| 122 | inode, path->p_bh); | 163 | inode, path->p_bh); |
| @@ -391,6 +432,12 @@ static int __ext4_ext_check(const char *function, unsigned int line, | |||
| 391 | error_msg = "invalid extent entries"; | 432 | error_msg = "invalid extent entries"; |
| 392 | goto corrupted; | 433 | goto corrupted; |
| 393 | } | 434 | } |
| 435 | /* Verify checksum on non-root extent tree nodes */ | ||
| 436 | if (ext_depth(inode) != depth && | ||
| 437 | !ext4_extent_block_csum_verify(inode, eh)) { | ||
| 438 | error_msg = "extent tree corrupted"; | ||
| 439 | goto corrupted; | ||
| 440 | } | ||
| 394 | return 0; | 441 | return 0; |
| 395 | 442 | ||
| 396 | corrupted: | 443 | corrupted: |
| @@ -412,6 +459,26 @@ int ext4_ext_check_inode(struct inode *inode) | |||
| 412 | return ext4_ext_check(inode, ext_inode_hdr(inode), ext_depth(inode)); | 459 | return ext4_ext_check(inode, ext_inode_hdr(inode), ext_depth(inode)); |
| 413 | } | 460 | } |
| 414 | 461 | ||
| 462 | static int __ext4_ext_check_block(const char *function, unsigned int line, | ||
| 463 | struct inode *inode, | ||
| 464 | struct ext4_extent_header *eh, | ||
| 465 | int depth, | ||
| 466 | struct buffer_head *bh) | ||
| 467 | { | ||
| 468 | int ret; | ||
| 469 | |||
| 470 | if (buffer_verified(bh)) | ||
| 471 | return 0; | ||
| 472 | ret = ext4_ext_check(inode, eh, depth); | ||
| 473 | if (ret) | ||
| 474 | return ret; | ||
| 475 | set_buffer_verified(bh); | ||
| 476 | return ret; | ||
| 477 | } | ||
| 478 | |||
| 479 | #define ext4_ext_check_block(inode, eh, depth, bh) \ | ||
| 480 | __ext4_ext_check_block(__func__, __LINE__, inode, eh, depth, bh) | ||
| 481 | |||
| 415 | #ifdef EXT_DEBUG | 482 | #ifdef EXT_DEBUG |
| 416 | static void ext4_ext_show_path(struct inode *inode, struct ext4_ext_path *path) | 483 | static void ext4_ext_show_path(struct inode *inode, struct ext4_ext_path *path) |
| 417 | { | 484 | { |
| @@ -536,7 +603,7 @@ ext4_ext_binsearch_idx(struct inode *inode, | |||
| 536 | } | 603 | } |
| 537 | 604 | ||
| 538 | path->p_idx = l - 1; | 605 | path->p_idx = l - 1; |
| 539 | ext_debug(" -> %d->%lld ", le32_to_cpu(path->p_idx->ei_block), | 606 | ext_debug(" -> %u->%lld ", le32_to_cpu(path->p_idx->ei_block), |
| 540 | ext4_idx_pblock(path->p_idx)); | 607 | ext4_idx_pblock(path->p_idx)); |
| 541 | 608 | ||
| 542 | #ifdef CHECK_BINSEARCH | 609 | #ifdef CHECK_BINSEARCH |
| @@ -668,8 +735,6 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block, | |||
| 668 | i = depth; | 735 | i = depth; |
| 669 | /* walk through the tree */ | 736 | /* walk through the tree */ |
| 670 | while (i) { | 737 | while (i) { |
| 671 | int need_to_validate = 0; | ||
| 672 | |||
| 673 | ext_debug("depth %d: num %d, max %d\n", | 738 | ext_debug("depth %d: num %d, max %d\n", |
| 674 | ppos, le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max)); | 739 | ppos, le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max)); |
| 675 | 740 | ||
| @@ -688,8 +753,6 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block, | |||
| 688 | put_bh(bh); | 753 | put_bh(bh); |
| 689 | goto err; | 754 | goto err; |
| 690 | } | 755 | } |
| 691 | /* validate the extent entries */ | ||
| 692 | need_to_validate = 1; | ||
| 693 | } | 756 | } |
| 694 | eh = ext_block_hdr(bh); | 757 | eh = ext_block_hdr(bh); |
| 695 | ppos++; | 758 | ppos++; |
| @@ -703,7 +766,7 @@ ext4_ext_find_extent(struct inode *inode, ext4_lblk_t block, | |||
| 703 | path[ppos].p_hdr = eh; | 766 | path[ppos].p_hdr = eh; |
| 704 | i--; | 767 | i--; |
| 705 | 768 | ||
| 706 | if (need_to_validate && ext4_ext_check(inode, eh, i)) | 769 | if (ext4_ext_check_block(inode, eh, i, bh)) |
| 707 | goto err; | 770 | goto err; |
| 708 | } | 771 | } |
| 709 | 772 | ||
| @@ -914,6 +977,7 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode, | |||
| 914 | le16_add_cpu(&neh->eh_entries, m); | 977 | le16_add_cpu(&neh->eh_entries, m); |
| 915 | } | 978 | } |
| 916 | 979 | ||
| 980 | ext4_extent_block_csum_set(inode, neh); | ||
| 917 | set_buffer_uptodate(bh); | 981 | set_buffer_uptodate(bh); |
| 918 | unlock_buffer(bh); | 982 | unlock_buffer(bh); |
| 919 | 983 | ||
| @@ -992,6 +1056,7 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode, | |||
| 992 | sizeof(struct ext4_extent_idx) * m); | 1056 | sizeof(struct ext4_extent_idx) * m); |
| 993 | le16_add_cpu(&neh->eh_entries, m); | 1057 | le16_add_cpu(&neh->eh_entries, m); |
| 994 | } | 1058 | } |
| 1059 | ext4_extent_block_csum_set(inode, neh); | ||
| 995 | set_buffer_uptodate(bh); | 1060 | set_buffer_uptodate(bh); |
| 996 | unlock_buffer(bh); | 1061 | unlock_buffer(bh); |
| 997 | 1062 | ||
| @@ -1089,6 +1154,7 @@ static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode, | |||
| 1089 | else | 1154 | else |
| 1090 | neh->eh_max = cpu_to_le16(ext4_ext_space_block(inode, 0)); | 1155 | neh->eh_max = cpu_to_le16(ext4_ext_space_block(inode, 0)); |
| 1091 | neh->eh_magic = EXT4_EXT_MAGIC; | 1156 | neh->eh_magic = EXT4_EXT_MAGIC; |
| 1157 | ext4_extent_block_csum_set(inode, neh); | ||
| 1092 | set_buffer_uptodate(bh); | 1158 | set_buffer_uptodate(bh); |
| 1093 | unlock_buffer(bh); | 1159 | unlock_buffer(bh); |
| 1094 | 1160 | ||
| @@ -1344,7 +1410,8 @@ got_index: | |||
| 1344 | return -EIO; | 1410 | return -EIO; |
| 1345 | eh = ext_block_hdr(bh); | 1411 | eh = ext_block_hdr(bh); |
| 1346 | /* subtract from p_depth to get proper eh_depth */ | 1412 | /* subtract from p_depth to get proper eh_depth */ |
| 1347 | if (ext4_ext_check(inode, eh, path->p_depth - depth)) { | 1413 | if (ext4_ext_check_block(inode, eh, |
| 1414 | path->p_depth - depth, bh)) { | ||
| 1348 | put_bh(bh); | 1415 | put_bh(bh); |
| 1349 | return -EIO; | 1416 | return -EIO; |
| 1350 | } | 1417 | } |
| @@ -1357,7 +1424,7 @@ got_index: | |||
| 1357 | if (bh == NULL) | 1424 | if (bh == NULL) |
| 1358 | return -EIO; | 1425 | return -EIO; |
| 1359 | eh = ext_block_hdr(bh); | 1426 | eh = ext_block_hdr(bh); |
| 1360 | if (ext4_ext_check(inode, eh, path->p_depth - depth)) { | 1427 | if (ext4_ext_check_block(inode, eh, path->p_depth - depth, bh)) { |
| 1361 | put_bh(bh); | 1428 | put_bh(bh); |
| 1362 | return -EIO; | 1429 | return -EIO; |
| 1363 | } | 1430 | } |
| @@ -2644,8 +2711,8 @@ cont: | |||
| 2644 | err = -EIO; | 2711 | err = -EIO; |
| 2645 | break; | 2712 | break; |
| 2646 | } | 2713 | } |
| 2647 | if (ext4_ext_check(inode, ext_block_hdr(bh), | 2714 | if (ext4_ext_check_block(inode, ext_block_hdr(bh), |
| 2648 | depth - i - 1)) { | 2715 | depth - i - 1, bh)) { |
| 2649 | err = -EIO; | 2716 | err = -EIO; |
| 2650 | break; | 2717 | break; |
| 2651 | } | 2718 | } |
| @@ -4722,8 +4789,8 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length) | |||
| 4722 | 4789 | ||
| 4723 | /* Now release the pages */ | 4790 | /* Now release the pages */ |
| 4724 | if (last_page_offset > first_page_offset) { | 4791 | if (last_page_offset > first_page_offset) { |
| 4725 | truncate_inode_pages_range(mapping, first_page_offset, | 4792 | truncate_pagecache_range(inode, first_page_offset, |
| 4726 | last_page_offset-1); | 4793 | last_page_offset - 1); |
| 4727 | } | 4794 | } |
| 4728 | 4795 | ||
| 4729 | /* finish any pending end_io work */ | 4796 | /* finish any pending end_io work */ |
