diff options
author | Qu Wenruo <quwenruo.btrfs@gmx.com> | 2017-10-08 21:51:02 -0400 |
---|---|---|
committer | David Sterba <dsterba@suse.com> | 2017-10-30 07:27:58 -0400 |
commit | 557ea5dd003d371536f6b4e8f7c8209a2b6fd4e3 (patch) | |
tree | 6bcc4d09f5f816a67cbf8ffc75ef83215c6f8adc | |
parent | 1170862d783a3b47e49a1506fe15fa074e35bbb5 (diff) |
btrfs: Move leaf and node validation checker to tree-checker.c
It's no doubt the comprehensive tree block checker will become larger,
so moving them into their own files is quite reasonable.
Signed-off-by: Qu Wenruo <quwenruo.btrfs@gmx.com>
[ wording adjustments ]
Signed-off-by: David Sterba <dsterba@suse.com>
-rw-r--r-- | fs/btrfs/Makefile | 2 | ||||
-rw-r--r-- | fs/btrfs/disk-io.c | 285 | ||||
-rw-r--r-- | fs/btrfs/tree-checker.c | 309 | ||||
-rw-r--r-- | fs/btrfs/tree-checker.h | 26 |
4 files changed, 340 insertions, 282 deletions
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile index 962a95aefb81..88255e133ade 100644 --- a/fs/btrfs/Makefile +++ b/fs/btrfs/Makefile | |||
@@ -9,7 +9,7 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \ | |||
9 | export.o tree-log.o free-space-cache.o zlib.o lzo.o zstd.o \ | 9 | export.o tree-log.o free-space-cache.o zlib.o lzo.o zstd.o \ |
10 | compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o \ | 10 | compression.o delayed-ref.o relocation.o delayed-inode.o scrub.o \ |
11 | reada.o backref.o ulist.o qgroup.o send.o dev-replace.o raid56.o \ | 11 | reada.o backref.o ulist.o qgroup.o send.o dev-replace.o raid56.o \ |
12 | uuid-tree.o props.o hash.o free-space-tree.o | 12 | uuid-tree.o props.o hash.o free-space-tree.o tree-checker.o |
13 | 13 | ||
14 | btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o | 14 | btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o |
15 | btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o | 15 | btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o |
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index c8633f2abdf1..09407384e996 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c | |||
@@ -50,6 +50,7 @@ | |||
50 | #include "sysfs.h" | 50 | #include "sysfs.h" |
51 | #include "qgroup.h" | 51 | #include "qgroup.h" |
52 | #include "compression.h" | 52 | #include "compression.h" |
53 | #include "tree-checker.h" | ||
53 | 54 | ||
54 | #ifdef CONFIG_X86 | 55 | #ifdef CONFIG_X86 |
55 | #include <asm/cpufeature.h> | 56 | #include <asm/cpufeature.h> |
@@ -543,284 +544,6 @@ static int check_tree_block_fsid(struct btrfs_fs_info *fs_info, | |||
543 | return ret; | 544 | return ret; |
544 | } | 545 | } |
545 | 546 | ||
546 | #define CORRUPT(reason, eb, root, slot) \ | ||
547 | btrfs_crit(root->fs_info, \ | ||
548 | "corrupt %s, %s: block=%llu, root=%llu, slot=%d", \ | ||
549 | btrfs_header_level(eb) == 0 ? "leaf" : "node", \ | ||
550 | reason, btrfs_header_bytenr(eb), root->objectid, slot) | ||
551 | |||
552 | static int check_extent_data_item(struct btrfs_root *root, | ||
553 | struct extent_buffer *leaf, | ||
554 | struct btrfs_key *key, int slot) | ||
555 | { | ||
556 | struct btrfs_file_extent_item *fi; | ||
557 | u32 sectorsize = root->fs_info->sectorsize; | ||
558 | u32 item_size = btrfs_item_size_nr(leaf, slot); | ||
559 | |||
560 | if (!IS_ALIGNED(key->offset, sectorsize)) { | ||
561 | CORRUPT("unaligned key offset for file extent", | ||
562 | leaf, root, slot); | ||
563 | return -EUCLEAN; | ||
564 | } | ||
565 | |||
566 | fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item); | ||
567 | |||
568 | if (btrfs_file_extent_type(leaf, fi) > BTRFS_FILE_EXTENT_TYPES) { | ||
569 | CORRUPT("invalid file extent type", leaf, root, slot); | ||
570 | return -EUCLEAN; | ||
571 | } | ||
572 | |||
573 | /* | ||
574 | * Support for new compression/encrption must introduce incompat flag, | ||
575 | * and must be caught in open_ctree(). | ||
576 | */ | ||
577 | if (btrfs_file_extent_compression(leaf, fi) > BTRFS_COMPRESS_TYPES) { | ||
578 | CORRUPT("invalid file extent compression", leaf, root, slot); | ||
579 | return -EUCLEAN; | ||
580 | } | ||
581 | if (btrfs_file_extent_encryption(leaf, fi)) { | ||
582 | CORRUPT("invalid file extent encryption", leaf, root, slot); | ||
583 | return -EUCLEAN; | ||
584 | } | ||
585 | if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE) { | ||
586 | /* Inline extent must have 0 as key offset */ | ||
587 | if (key->offset) { | ||
588 | CORRUPT("inline extent has non-zero key offset", | ||
589 | leaf, root, slot); | ||
590 | return -EUCLEAN; | ||
591 | } | ||
592 | |||
593 | /* Compressed inline extent has no on-disk size, skip it */ | ||
594 | if (btrfs_file_extent_compression(leaf, fi) != | ||
595 | BTRFS_COMPRESS_NONE) | ||
596 | return 0; | ||
597 | |||
598 | /* Uncompressed inline extent size must match item size */ | ||
599 | if (item_size != BTRFS_FILE_EXTENT_INLINE_DATA_START + | ||
600 | btrfs_file_extent_ram_bytes(leaf, fi)) { | ||
601 | CORRUPT("plaintext inline extent has invalid size", | ||
602 | leaf, root, slot); | ||
603 | return -EUCLEAN; | ||
604 | } | ||
605 | return 0; | ||
606 | } | ||
607 | |||
608 | /* Regular or preallocated extent has fixed item size */ | ||
609 | if (item_size != sizeof(*fi)) { | ||
610 | CORRUPT( | ||
611 | "regluar or preallocated extent data item size is invalid", | ||
612 | leaf, root, slot); | ||
613 | return -EUCLEAN; | ||
614 | } | ||
615 | if (!IS_ALIGNED(btrfs_file_extent_ram_bytes(leaf, fi), sectorsize) || | ||
616 | !IS_ALIGNED(btrfs_file_extent_disk_bytenr(leaf, fi), sectorsize) || | ||
617 | !IS_ALIGNED(btrfs_file_extent_disk_num_bytes(leaf, fi), sectorsize) || | ||
618 | !IS_ALIGNED(btrfs_file_extent_offset(leaf, fi), sectorsize) || | ||
619 | !IS_ALIGNED(btrfs_file_extent_num_bytes(leaf, fi), sectorsize)) { | ||
620 | CORRUPT( | ||
621 | "regular or preallocated extent data item has unaligned value", | ||
622 | leaf, root, slot); | ||
623 | return -EUCLEAN; | ||
624 | } | ||
625 | |||
626 | return 0; | ||
627 | } | ||
628 | |||
629 | static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf, | ||
630 | struct btrfs_key *key, int slot) | ||
631 | { | ||
632 | u32 sectorsize = root->fs_info->sectorsize; | ||
633 | u32 csumsize = btrfs_super_csum_size(root->fs_info->super_copy); | ||
634 | |||
635 | if (key->objectid != BTRFS_EXTENT_CSUM_OBJECTID) { | ||
636 | CORRUPT("invalid objectid for csum item", leaf, root, slot); | ||
637 | return -EUCLEAN; | ||
638 | } | ||
639 | if (!IS_ALIGNED(key->offset, sectorsize)) { | ||
640 | CORRUPT("unaligned key offset for csum item", leaf, root, slot); | ||
641 | return -EUCLEAN; | ||
642 | } | ||
643 | if (!IS_ALIGNED(btrfs_item_size_nr(leaf, slot), csumsize)) { | ||
644 | CORRUPT("unaligned csum item size", leaf, root, slot); | ||
645 | return -EUCLEAN; | ||
646 | } | ||
647 | return 0; | ||
648 | } | ||
649 | |||
650 | /* | ||
651 | * Common point to switch the item-specific validation. | ||
652 | */ | ||
653 | static int check_leaf_item(struct btrfs_root *root, | ||
654 | struct extent_buffer *leaf, | ||
655 | struct btrfs_key *key, int slot) | ||
656 | { | ||
657 | int ret = 0; | ||
658 | |||
659 | switch (key->type) { | ||
660 | case BTRFS_EXTENT_DATA_KEY: | ||
661 | ret = check_extent_data_item(root, leaf, key, slot); | ||
662 | break; | ||
663 | case BTRFS_EXTENT_CSUM_KEY: | ||
664 | ret = check_csum_item(root, leaf, key, slot); | ||
665 | break; | ||
666 | } | ||
667 | return ret; | ||
668 | } | ||
669 | |||
670 | static noinline int check_leaf(struct btrfs_root *root, | ||
671 | struct extent_buffer *leaf) | ||
672 | { | ||
673 | struct btrfs_fs_info *fs_info = root->fs_info; | ||
674 | /* No valid key type is 0, so all key should be larger than this key */ | ||
675 | struct btrfs_key prev_key = {0, 0, 0}; | ||
676 | struct btrfs_key key; | ||
677 | u32 nritems = btrfs_header_nritems(leaf); | ||
678 | int slot; | ||
679 | |||
680 | /* | ||
681 | * Extent buffers from a relocation tree have a owner field that | ||
682 | * corresponds to the subvolume tree they are based on. So just from an | ||
683 | * extent buffer alone we can not find out what is the id of the | ||
684 | * corresponding subvolume tree, so we can not figure out if the extent | ||
685 | * buffer corresponds to the root of the relocation tree or not. So skip | ||
686 | * this check for relocation trees. | ||
687 | */ | ||
688 | if (nritems == 0 && !btrfs_header_flag(leaf, BTRFS_HEADER_FLAG_RELOC)) { | ||
689 | struct btrfs_root *check_root; | ||
690 | |||
691 | key.objectid = btrfs_header_owner(leaf); | ||
692 | key.type = BTRFS_ROOT_ITEM_KEY; | ||
693 | key.offset = (u64)-1; | ||
694 | |||
695 | check_root = btrfs_get_fs_root(fs_info, &key, false); | ||
696 | /* | ||
697 | * The only reason we also check NULL here is that during | ||
698 | * open_ctree() some roots has not yet been set up. | ||
699 | */ | ||
700 | if (!IS_ERR_OR_NULL(check_root)) { | ||
701 | struct extent_buffer *eb; | ||
702 | |||
703 | eb = btrfs_root_node(check_root); | ||
704 | /* if leaf is the root, then it's fine */ | ||
705 | if (leaf != eb) { | ||
706 | CORRUPT("non-root leaf's nritems is 0", | ||
707 | leaf, check_root, 0); | ||
708 | free_extent_buffer(eb); | ||
709 | return -EUCLEAN; | ||
710 | } | ||
711 | free_extent_buffer(eb); | ||
712 | } | ||
713 | return 0; | ||
714 | } | ||
715 | |||
716 | if (nritems == 0) | ||
717 | return 0; | ||
718 | |||
719 | /* | ||
720 | * Check the following things to make sure this is a good leaf, and | ||
721 | * leaf users won't need to bother with similar sanity checks: | ||
722 | * | ||
723 | * 1) key order | ||
724 | * 2) item offset and size | ||
725 | * No overlap, no hole, all inside the leaf. | ||
726 | * 3) item content | ||
727 | * If possible, do comprehensive sanity check. | ||
728 | * NOTE: All checks must only rely on the item data itself. | ||
729 | */ | ||
730 | for (slot = 0; slot < nritems; slot++) { | ||
731 | u32 item_end_expected; | ||
732 | int ret; | ||
733 | |||
734 | btrfs_item_key_to_cpu(leaf, &key, slot); | ||
735 | |||
736 | /* Make sure the keys are in the right order */ | ||
737 | if (btrfs_comp_cpu_keys(&prev_key, &key) >= 0) { | ||
738 | CORRUPT("bad key order", leaf, root, slot); | ||
739 | return -EUCLEAN; | ||
740 | } | ||
741 | |||
742 | /* | ||
743 | * Make sure the offset and ends are right, remember that the | ||
744 | * item data starts at the end of the leaf and grows towards the | ||
745 | * front. | ||
746 | */ | ||
747 | if (slot == 0) | ||
748 | item_end_expected = BTRFS_LEAF_DATA_SIZE(fs_info); | ||
749 | else | ||
750 | item_end_expected = btrfs_item_offset_nr(leaf, | ||
751 | slot - 1); | ||
752 | if (btrfs_item_end_nr(leaf, slot) != item_end_expected) { | ||
753 | CORRUPT("slot offset bad", leaf, root, slot); | ||
754 | return -EUCLEAN; | ||
755 | } | ||
756 | |||
757 | /* | ||
758 | * Check to make sure that we don't point outside of the leaf, | ||
759 | * just in case all the items are consistent to each other, but | ||
760 | * all point outside of the leaf. | ||
761 | */ | ||
762 | if (btrfs_item_end_nr(leaf, slot) > | ||
763 | BTRFS_LEAF_DATA_SIZE(fs_info)) { | ||
764 | CORRUPT("slot end outside of leaf", leaf, root, slot); | ||
765 | return -EUCLEAN; | ||
766 | } | ||
767 | |||
768 | /* Also check if the item pointer overlaps with btrfs item. */ | ||
769 | if (btrfs_item_nr_offset(slot) + sizeof(struct btrfs_item) > | ||
770 | btrfs_item_ptr_offset(leaf, slot)) { | ||
771 | CORRUPT("slot overlap with its data", leaf, root, slot); | ||
772 | return -EUCLEAN; | ||
773 | } | ||
774 | |||
775 | /* Check if the item size and content meet other criteria */ | ||
776 | ret = check_leaf_item(root, leaf, &key, slot); | ||
777 | if (ret < 0) | ||
778 | return ret; | ||
779 | |||
780 | prev_key.objectid = key.objectid; | ||
781 | prev_key.type = key.type; | ||
782 | prev_key.offset = key.offset; | ||
783 | } | ||
784 | |||
785 | return 0; | ||
786 | } | ||
787 | |||
788 | static int check_node(struct btrfs_root *root, struct extent_buffer *node) | ||
789 | { | ||
790 | unsigned long nr = btrfs_header_nritems(node); | ||
791 | struct btrfs_key key, next_key; | ||
792 | int slot; | ||
793 | u64 bytenr; | ||
794 | int ret = 0; | ||
795 | |||
796 | if (nr == 0 || nr > BTRFS_NODEPTRS_PER_BLOCK(root->fs_info)) { | ||
797 | btrfs_crit(root->fs_info, | ||
798 | "corrupt node: block %llu root %llu nritems %lu", | ||
799 | node->start, root->objectid, nr); | ||
800 | return -EIO; | ||
801 | } | ||
802 | |||
803 | for (slot = 0; slot < nr - 1; slot++) { | ||
804 | bytenr = btrfs_node_blockptr(node, slot); | ||
805 | btrfs_node_key_to_cpu(node, &key, slot); | ||
806 | btrfs_node_key_to_cpu(node, &next_key, slot + 1); | ||
807 | |||
808 | if (!bytenr) { | ||
809 | CORRUPT("invalid item slot", node, root, slot); | ||
810 | ret = -EIO; | ||
811 | goto out; | ||
812 | } | ||
813 | |||
814 | if (btrfs_comp_cpu_keys(&key, &next_key) >= 0) { | ||
815 | CORRUPT("bad key order", node, root, slot); | ||
816 | ret = -EIO; | ||
817 | goto out; | ||
818 | } | ||
819 | } | ||
820 | out: | ||
821 | return ret; | ||
822 | } | ||
823 | |||
824 | static int btree_readpage_end_io_hook(struct btrfs_io_bio *io_bio, | 547 | static int btree_readpage_end_io_hook(struct btrfs_io_bio *io_bio, |
825 | u64 phy_offset, struct page *page, | 548 | u64 phy_offset, struct page *page, |
826 | u64 start, u64 end, int mirror) | 549 | u64 start, u64 end, int mirror) |
@@ -886,12 +609,12 @@ static int btree_readpage_end_io_hook(struct btrfs_io_bio *io_bio, | |||
886 | * that we don't try and read the other copies of this block, just | 609 | * that we don't try and read the other copies of this block, just |
887 | * return -EIO. | 610 | * return -EIO. |
888 | */ | 611 | */ |
889 | if (found_level == 0 && check_leaf(root, eb)) { | 612 | if (found_level == 0 && btrfs_check_leaf(root, eb)) { |
890 | set_bit(EXTENT_BUFFER_CORRUPT, &eb->bflags); | 613 | set_bit(EXTENT_BUFFER_CORRUPT, &eb->bflags); |
891 | ret = -EIO; | 614 | ret = -EIO; |
892 | } | 615 | } |
893 | 616 | ||
894 | if (found_level > 0 && check_node(root, eb)) | 617 | if (found_level > 0 && btrfs_check_node(root, eb)) |
895 | ret = -EIO; | 618 | ret = -EIO; |
896 | 619 | ||
897 | if (!ret) | 620 | if (!ret) |
@@ -4146,7 +3869,7 @@ void btrfs_mark_buffer_dirty(struct extent_buffer *buf) | |||
4146 | buf->len, | 3869 | buf->len, |
4147 | fs_info->dirty_metadata_batch); | 3870 | fs_info->dirty_metadata_batch); |
4148 | #ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY | 3871 | #ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY |
4149 | if (btrfs_header_level(buf) == 0 && check_leaf(root, buf)) { | 3872 | if (btrfs_header_level(buf) == 0 && btrfs_check_leaf(root, buf)) { |
4150 | btrfs_print_leaf(buf); | 3873 | btrfs_print_leaf(buf); |
4151 | ASSERT(0); | 3874 | ASSERT(0); |
4152 | } | 3875 | } |
diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c new file mode 100644 index 000000000000..56e25a630103 --- /dev/null +++ b/fs/btrfs/tree-checker.c | |||
@@ -0,0 +1,309 @@ | |||
1 | /* | ||
2 | * Copyright (C) Qu Wenruo 2017. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public | ||
6 | * License v2 as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
11 | * General Public License for more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public | ||
14 | * License along with this program. | ||
15 | */ | ||
16 | |||
17 | /* | ||
18 | * The module is used to catch unexpected/corrupted tree block data. | ||
19 | * Such behavior can be caused either by a fuzzed image or bugs. | ||
20 | * | ||
21 | * The objective is to do leaf/node validation checks when tree block is read | ||
22 | * from disk, and check *every* possible member, so other code won't | ||
23 | * need to checking them again. | ||
24 | * | ||
25 | * Due to the potential and unwanted damage, every checker needs to be | ||
26 | * carefully reviewed otherwise so it does not prevent mount of valid images. | ||
27 | */ | ||
28 | |||
29 | #include "ctree.h" | ||
30 | #include "tree-checker.h" | ||
31 | #include "disk-io.h" | ||
32 | #include "compression.h" | ||
33 | |||
34 | #define CORRUPT(reason, eb, root, slot) \ | ||
35 | btrfs_crit(root->fs_info, \ | ||
36 | "corrupt %s, %s: block=%llu, root=%llu, slot=%d", \ | ||
37 | btrfs_header_level(eb) == 0 ? "leaf" : "node", \ | ||
38 | reason, btrfs_header_bytenr(eb), root->objectid, slot) | ||
39 | |||
40 | static int check_extent_data_item(struct btrfs_root *root, | ||
41 | struct extent_buffer *leaf, | ||
42 | struct btrfs_key *key, int slot) | ||
43 | { | ||
44 | struct btrfs_file_extent_item *fi; | ||
45 | u32 sectorsize = root->fs_info->sectorsize; | ||
46 | u32 item_size = btrfs_item_size_nr(leaf, slot); | ||
47 | |||
48 | if (!IS_ALIGNED(key->offset, sectorsize)) { | ||
49 | CORRUPT("unaligned key offset for file extent", | ||
50 | leaf, root, slot); | ||
51 | return -EUCLEAN; | ||
52 | } | ||
53 | |||
54 | fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item); | ||
55 | |||
56 | if (btrfs_file_extent_type(leaf, fi) > BTRFS_FILE_EXTENT_TYPES) { | ||
57 | CORRUPT("invalid file extent type", leaf, root, slot); | ||
58 | return -EUCLEAN; | ||
59 | } | ||
60 | |||
61 | /* | ||
62 | * Support for new compression/encrption must introduce incompat flag, | ||
63 | * and must be caught in open_ctree(). | ||
64 | */ | ||
65 | if (btrfs_file_extent_compression(leaf, fi) > BTRFS_COMPRESS_TYPES) { | ||
66 | CORRUPT("invalid file extent compression", leaf, root, slot); | ||
67 | return -EUCLEAN; | ||
68 | } | ||
69 | if (btrfs_file_extent_encryption(leaf, fi)) { | ||
70 | CORRUPT("invalid file extent encryption", leaf, root, slot); | ||
71 | return -EUCLEAN; | ||
72 | } | ||
73 | if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE) { | ||
74 | /* Inline extent must have 0 as key offset */ | ||
75 | if (key->offset) { | ||
76 | CORRUPT("inline extent has non-zero key offset", | ||
77 | leaf, root, slot); | ||
78 | return -EUCLEAN; | ||
79 | } | ||
80 | |||
81 | /* Compressed inline extent has no on-disk size, skip it */ | ||
82 | if (btrfs_file_extent_compression(leaf, fi) != | ||
83 | BTRFS_COMPRESS_NONE) | ||
84 | return 0; | ||
85 | |||
86 | /* Uncompressed inline extent size must match item size */ | ||
87 | if (item_size != BTRFS_FILE_EXTENT_INLINE_DATA_START + | ||
88 | btrfs_file_extent_ram_bytes(leaf, fi)) { | ||
89 | CORRUPT("plaintext inline extent has invalid size", | ||
90 | leaf, root, slot); | ||
91 | return -EUCLEAN; | ||
92 | } | ||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | /* Regular or preallocated extent has fixed item size */ | ||
97 | if (item_size != sizeof(*fi)) { | ||
98 | CORRUPT( | ||
99 | "regluar or preallocated extent data item size is invalid", | ||
100 | leaf, root, slot); | ||
101 | return -EUCLEAN; | ||
102 | } | ||
103 | if (!IS_ALIGNED(btrfs_file_extent_ram_bytes(leaf, fi), sectorsize) || | ||
104 | !IS_ALIGNED(btrfs_file_extent_disk_bytenr(leaf, fi), sectorsize) || | ||
105 | !IS_ALIGNED(btrfs_file_extent_disk_num_bytes(leaf, fi), sectorsize) || | ||
106 | !IS_ALIGNED(btrfs_file_extent_offset(leaf, fi), sectorsize) || | ||
107 | !IS_ALIGNED(btrfs_file_extent_num_bytes(leaf, fi), sectorsize)) { | ||
108 | CORRUPT( | ||
109 | "regular or preallocated extent data item has unaligned value", | ||
110 | leaf, root, slot); | ||
111 | return -EUCLEAN; | ||
112 | } | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf, | ||
118 | struct btrfs_key *key, int slot) | ||
119 | { | ||
120 | u32 sectorsize = root->fs_info->sectorsize; | ||
121 | u32 csumsize = btrfs_super_csum_size(root->fs_info->super_copy); | ||
122 | |||
123 | if (key->objectid != BTRFS_EXTENT_CSUM_OBJECTID) { | ||
124 | CORRUPT("invalid objectid for csum item", leaf, root, slot); | ||
125 | return -EUCLEAN; | ||
126 | } | ||
127 | if (!IS_ALIGNED(key->offset, sectorsize)) { | ||
128 | CORRUPT("unaligned key offset for csum item", leaf, root, slot); | ||
129 | return -EUCLEAN; | ||
130 | } | ||
131 | if (!IS_ALIGNED(btrfs_item_size_nr(leaf, slot), csumsize)) { | ||
132 | CORRUPT("unaligned csum item size", leaf, root, slot); | ||
133 | return -EUCLEAN; | ||
134 | } | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | /* | ||
139 | * Common point to switch the item-specific validation. | ||
140 | */ | ||
141 | static int check_leaf_item(struct btrfs_root *root, | ||
142 | struct extent_buffer *leaf, | ||
143 | struct btrfs_key *key, int slot) | ||
144 | { | ||
145 | int ret = 0; | ||
146 | |||
147 | switch (key->type) { | ||
148 | case BTRFS_EXTENT_DATA_KEY: | ||
149 | ret = check_extent_data_item(root, leaf, key, slot); | ||
150 | break; | ||
151 | case BTRFS_EXTENT_CSUM_KEY: | ||
152 | ret = check_csum_item(root, leaf, key, slot); | ||
153 | break; | ||
154 | } | ||
155 | return ret; | ||
156 | } | ||
157 | |||
158 | int btrfs_check_leaf(struct btrfs_root *root, struct extent_buffer *leaf) | ||
159 | { | ||
160 | struct btrfs_fs_info *fs_info = root->fs_info; | ||
161 | /* No valid key type is 0, so all key should be larger than this key */ | ||
162 | struct btrfs_key prev_key = {0, 0, 0}; | ||
163 | struct btrfs_key key; | ||
164 | u32 nritems = btrfs_header_nritems(leaf); | ||
165 | int slot; | ||
166 | |||
167 | /* | ||
168 | * Extent buffers from a relocation tree have a owner field that | ||
169 | * corresponds to the subvolume tree they are based on. So just from an | ||
170 | * extent buffer alone we can not find out what is the id of the | ||
171 | * corresponding subvolume tree, so we can not figure out if the extent | ||
172 | * buffer corresponds to the root of the relocation tree or not. So | ||
173 | * skip this check for relocation trees. | ||
174 | */ | ||
175 | if (nritems == 0 && !btrfs_header_flag(leaf, BTRFS_HEADER_FLAG_RELOC)) { | ||
176 | struct btrfs_root *check_root; | ||
177 | |||
178 | key.objectid = btrfs_header_owner(leaf); | ||
179 | key.type = BTRFS_ROOT_ITEM_KEY; | ||
180 | key.offset = (u64)-1; | ||
181 | |||
182 | check_root = btrfs_get_fs_root(fs_info, &key, false); | ||
183 | /* | ||
184 | * The only reason we also check NULL here is that during | ||
185 | * open_ctree() some roots has not yet been set up. | ||
186 | */ | ||
187 | if (!IS_ERR_OR_NULL(check_root)) { | ||
188 | struct extent_buffer *eb; | ||
189 | |||
190 | eb = btrfs_root_node(check_root); | ||
191 | /* if leaf is the root, then it's fine */ | ||
192 | if (leaf != eb) { | ||
193 | CORRUPT("non-root leaf's nritems is 0", | ||
194 | leaf, check_root, 0); | ||
195 | free_extent_buffer(eb); | ||
196 | return -EUCLEAN; | ||
197 | } | ||
198 | free_extent_buffer(eb); | ||
199 | } | ||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | if (nritems == 0) | ||
204 | return 0; | ||
205 | |||
206 | /* | ||
207 | * Check the following things to make sure this is a good leaf, and | ||
208 | * leaf users won't need to bother with similar sanity checks: | ||
209 | * | ||
210 | * 1) key ordering | ||
211 | * 2) item offset and size | ||
212 | * No overlap, no hole, all inside the leaf. | ||
213 | * 3) item content | ||
214 | * If possible, do comprehensive sanity check. | ||
215 | * NOTE: All checks must only rely on the item data itself. | ||
216 | */ | ||
217 | for (slot = 0; slot < nritems; slot++) { | ||
218 | u32 item_end_expected; | ||
219 | int ret; | ||
220 | |||
221 | btrfs_item_key_to_cpu(leaf, &key, slot); | ||
222 | |||
223 | /* Make sure the keys are in the right order */ | ||
224 | if (btrfs_comp_cpu_keys(&prev_key, &key) >= 0) { | ||
225 | CORRUPT("bad key order", leaf, root, slot); | ||
226 | return -EUCLEAN; | ||
227 | } | ||
228 | |||
229 | /* | ||
230 | * Make sure the offset and ends are right, remember that the | ||
231 | * item data starts at the end of the leaf and grows towards the | ||
232 | * front. | ||
233 | */ | ||
234 | if (slot == 0) | ||
235 | item_end_expected = BTRFS_LEAF_DATA_SIZE(fs_info); | ||
236 | else | ||
237 | item_end_expected = btrfs_item_offset_nr(leaf, | ||
238 | slot - 1); | ||
239 | if (btrfs_item_end_nr(leaf, slot) != item_end_expected) { | ||
240 | CORRUPT("slot offset bad", leaf, root, slot); | ||
241 | return -EUCLEAN; | ||
242 | } | ||
243 | |||
244 | /* | ||
245 | * Check to make sure that we don't point outside of the leaf, | ||
246 | * just in case all the items are consistent to each other, but | ||
247 | * all point outside of the leaf. | ||
248 | */ | ||
249 | if (btrfs_item_end_nr(leaf, slot) > | ||
250 | BTRFS_LEAF_DATA_SIZE(fs_info)) { | ||
251 | CORRUPT("slot end outside of leaf", leaf, root, slot); | ||
252 | return -EUCLEAN; | ||
253 | } | ||
254 | |||
255 | /* Also check if the item pointer overlaps with btrfs item. */ | ||
256 | if (btrfs_item_nr_offset(slot) + sizeof(struct btrfs_item) > | ||
257 | btrfs_item_ptr_offset(leaf, slot)) { | ||
258 | CORRUPT("slot overlap with its data", leaf, root, slot); | ||
259 | return -EUCLEAN; | ||
260 | } | ||
261 | |||
262 | /* Check if the item size and content meet other criteria */ | ||
263 | ret = check_leaf_item(root, leaf, &key, slot); | ||
264 | if (ret < 0) | ||
265 | return ret; | ||
266 | |||
267 | prev_key.objectid = key.objectid; | ||
268 | prev_key.type = key.type; | ||
269 | prev_key.offset = key.offset; | ||
270 | } | ||
271 | |||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | int btrfs_check_node(struct btrfs_root *root, struct extent_buffer *node) | ||
276 | { | ||
277 | unsigned long nr = btrfs_header_nritems(node); | ||
278 | struct btrfs_key key, next_key; | ||
279 | int slot; | ||
280 | u64 bytenr; | ||
281 | int ret = 0; | ||
282 | |||
283 | if (nr == 0 || nr > BTRFS_NODEPTRS_PER_BLOCK(root->fs_info)) { | ||
284 | btrfs_crit(root->fs_info, | ||
285 | "corrupt node: block %llu root %llu nritems %lu", | ||
286 | node->start, root->objectid, nr); | ||
287 | return -EIO; | ||
288 | } | ||
289 | |||
290 | for (slot = 0; slot < nr - 1; slot++) { | ||
291 | bytenr = btrfs_node_blockptr(node, slot); | ||
292 | btrfs_node_key_to_cpu(node, &key, slot); | ||
293 | btrfs_node_key_to_cpu(node, &next_key, slot + 1); | ||
294 | |||
295 | if (!bytenr) { | ||
296 | CORRUPT("invalid item slot", node, root, slot); | ||
297 | ret = -EIO; | ||
298 | goto out; | ||
299 | } | ||
300 | |||
301 | if (btrfs_comp_cpu_keys(&key, &next_key) >= 0) { | ||
302 | CORRUPT("bad key order", node, root, slot); | ||
303 | ret = -EIO; | ||
304 | goto out; | ||
305 | } | ||
306 | } | ||
307 | out: | ||
308 | return ret; | ||
309 | } | ||
diff --git a/fs/btrfs/tree-checker.h b/fs/btrfs/tree-checker.h new file mode 100644 index 000000000000..96c486e95d70 --- /dev/null +++ b/fs/btrfs/tree-checker.h | |||
@@ -0,0 +1,26 @@ | |||
1 | /* | ||
2 | * Copyright (C) Qu Wenruo 2017. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public | ||
6 | * License v2 as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
11 | * General Public License for more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public | ||
14 | * License along with this program. | ||
15 | */ | ||
16 | |||
17 | #ifndef __BTRFS_TREE_CHECKER__ | ||
18 | #define __BTRFS_TREE_CHECKER__ | ||
19 | |||
20 | #include "ctree.h" | ||
21 | #include "extent_io.h" | ||
22 | |||
23 | int btrfs_check_leaf(struct btrfs_root *root, struct extent_buffer *leaf); | ||
24 | int btrfs_check_node(struct btrfs_root *root, struct extent_buffer *node); | ||
25 | |||
26 | #endif | ||