diff options
author | Qu Wenruo <wqu@suse.com> | 2019-08-08 21:24:22 -0400 |
---|---|---|
committer | David Sterba <dsterba@suse.com> | 2019-09-09 08:59:12 -0400 |
commit | f82d1c7ca8ae1bf89e8d78c5ecb56b6b228c1a75 (patch) | |
tree | 5ed618e1c776ae7cf773a335475cbf8af44c2bdb /fs/btrfs/tree-checker.c | |
parent | f11369897ed4f8609cdee00d3af47c18fe6bda29 (diff) |
btrfs: tree-checker: Add EXTENT_ITEM and METADATA_ITEM check
This patch introduces the ability to check extent items.
This check involves:
- key->objectid check
Basic alignment check.
- key->type check
Against btrfs_extent_item::type and SKINNY_METADATA feature.
- key->offset alignment check for EXTENT_ITEM
- key->offset check for METADATA_ITEM
- item size check
Both against minimal size and stepping check.
- btrfs_extent_item check
Checks its flags and generation.
- btrfs_extent_inline_ref checks
Against 4 types inline ref.
Checks bytenr alignment and tree level.
- btrfs_extent_item::refs check
Check against total refs found in inline refs.
This check would be the most complex single item check due to its nature
of inlined items.
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Diffstat (limited to 'fs/btrfs/tree-checker.c')
-rw-r--r-- | fs/btrfs/tree-checker.c | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index d83adda6c090..7eee0bbd8c37 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c | |||
@@ -910,6 +910,250 @@ static int check_root_item(struct extent_buffer *leaf, struct btrfs_key *key, | |||
910 | return 0; | 910 | return 0; |
911 | } | 911 | } |
912 | 912 | ||
913 | __printf(3,4) | ||
914 | __cold | ||
915 | static void extent_err(const struct extent_buffer *eb, int slot, | ||
916 | const char *fmt, ...) | ||
917 | { | ||
918 | struct btrfs_key key; | ||
919 | struct va_format vaf; | ||
920 | va_list args; | ||
921 | u64 bytenr; | ||
922 | u64 len; | ||
923 | |||
924 | btrfs_item_key_to_cpu(eb, &key, slot); | ||
925 | bytenr = key.objectid; | ||
926 | if (key.type == BTRFS_METADATA_ITEM_KEY) | ||
927 | len = eb->fs_info->nodesize; | ||
928 | else | ||
929 | len = key.offset; | ||
930 | va_start(args, fmt); | ||
931 | |||
932 | vaf.fmt = fmt; | ||
933 | vaf.va = &args; | ||
934 | |||
935 | btrfs_crit(eb->fs_info, | ||
936 | "corrupt %s: block=%llu slot=%d extent bytenr=%llu len=%llu %pV", | ||
937 | btrfs_header_level(eb) == 0 ? "leaf" : "node", | ||
938 | eb->start, slot, bytenr, len, &vaf); | ||
939 | va_end(args); | ||
940 | } | ||
941 | |||
942 | static int check_extent_item(struct extent_buffer *leaf, | ||
943 | struct btrfs_key *key, int slot) | ||
944 | { | ||
945 | struct btrfs_fs_info *fs_info = leaf->fs_info; | ||
946 | struct btrfs_extent_item *ei; | ||
947 | bool is_tree_block = false; | ||
948 | unsigned long ptr; /* Current pointer inside inline refs */ | ||
949 | unsigned long end; /* Extent item end */ | ||
950 | const u32 item_size = btrfs_item_size_nr(leaf, slot); | ||
951 | u64 flags; | ||
952 | u64 generation; | ||
953 | u64 total_refs; /* Total refs in btrfs_extent_item */ | ||
954 | u64 inline_refs = 0; /* found total inline refs */ | ||
955 | |||
956 | if (key->type == BTRFS_METADATA_ITEM_KEY && | ||
957 | !btrfs_fs_incompat(fs_info, SKINNY_METADATA)) { | ||
958 | generic_err(leaf, slot, | ||
959 | "invalid key type, METADATA_ITEM type invalid when SKINNY_METADATA feature disabled"); | ||
960 | return -EUCLEAN; | ||
961 | } | ||
962 | /* key->objectid is the bytenr for both key types */ | ||
963 | if (!IS_ALIGNED(key->objectid, fs_info->sectorsize)) { | ||
964 | generic_err(leaf, slot, | ||
965 | "invalid key objectid, have %llu expect to be aligned to %u", | ||
966 | key->objectid, fs_info->sectorsize); | ||
967 | return -EUCLEAN; | ||
968 | } | ||
969 | |||
970 | /* key->offset is tree level for METADATA_ITEM_KEY */ | ||
971 | if (key->type == BTRFS_METADATA_ITEM_KEY && | ||
972 | key->offset >= BTRFS_MAX_LEVEL) { | ||
973 | extent_err(leaf, slot, | ||
974 | "invalid tree level, have %llu expect [0, %u]", | ||
975 | key->offset, BTRFS_MAX_LEVEL - 1); | ||
976 | return -EUCLEAN; | ||
977 | } | ||
978 | |||
979 | /* | ||
980 | * EXTENT/METADATA_ITEM consists of: | ||
981 | * 1) One btrfs_extent_item | ||
982 | * Records the total refs, type and generation of the extent. | ||
983 | * | ||
984 | * 2) One btrfs_tree_block_info (for EXTENT_ITEM and tree backref only) | ||
985 | * Records the first key and level of the tree block. | ||
986 | * | ||
987 | * 2) Zero or more btrfs_extent_inline_ref(s) | ||
988 | * Each inline ref has one btrfs_extent_inline_ref shows: | ||
989 | * 2.1) The ref type, one of the 4 | ||
990 | * TREE_BLOCK_REF Tree block only | ||
991 | * SHARED_BLOCK_REF Tree block only | ||
992 | * EXTENT_DATA_REF Data only | ||
993 | * SHARED_DATA_REF Data only | ||
994 | * 2.2) Ref type specific data | ||
995 | * Either using btrfs_extent_inline_ref::offset, or specific | ||
996 | * data structure. | ||
997 | */ | ||
998 | if (item_size < sizeof(*ei)) { | ||
999 | extent_err(leaf, slot, | ||
1000 | "invalid item size, have %u expect [%zu, %u)", | ||
1001 | item_size, sizeof(*ei), | ||
1002 | BTRFS_LEAF_DATA_SIZE(fs_info)); | ||
1003 | return -EUCLEAN; | ||
1004 | } | ||
1005 | end = item_size + btrfs_item_ptr_offset(leaf, slot); | ||
1006 | |||
1007 | /* Checks against extent_item */ | ||
1008 | ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item); | ||
1009 | flags = btrfs_extent_flags(leaf, ei); | ||
1010 | total_refs = btrfs_extent_refs(leaf, ei); | ||
1011 | generation = btrfs_extent_generation(leaf, ei); | ||
1012 | if (generation > btrfs_super_generation(fs_info->super_copy) + 1) { | ||
1013 | extent_err(leaf, slot, | ||
1014 | "invalid generation, have %llu expect (0, %llu]", | ||
1015 | generation, | ||
1016 | btrfs_super_generation(fs_info->super_copy) + 1); | ||
1017 | return -EUCLEAN; | ||
1018 | } | ||
1019 | if (!is_power_of_2(flags & (BTRFS_EXTENT_FLAG_DATA | | ||
1020 | BTRFS_EXTENT_FLAG_TREE_BLOCK))) { | ||
1021 | extent_err(leaf, slot, | ||
1022 | "invalid extent flag, have 0x%llx expect 1 bit set in 0x%llx", | ||
1023 | flags, BTRFS_EXTENT_FLAG_DATA | | ||
1024 | BTRFS_EXTENT_FLAG_TREE_BLOCK); | ||
1025 | return -EUCLEAN; | ||
1026 | } | ||
1027 | is_tree_block = !!(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK); | ||
1028 | if (is_tree_block) { | ||
1029 | if (key->type == BTRFS_EXTENT_ITEM_KEY && | ||
1030 | key->offset != fs_info->nodesize) { | ||
1031 | extent_err(leaf, slot, | ||
1032 | "invalid extent length, have %llu expect %u", | ||
1033 | key->offset, fs_info->nodesize); | ||
1034 | return -EUCLEAN; | ||
1035 | } | ||
1036 | } else { | ||
1037 | if (key->type != BTRFS_EXTENT_ITEM_KEY) { | ||
1038 | extent_err(leaf, slot, | ||
1039 | "invalid key type, have %u expect %u for data backref", | ||
1040 | key->type, BTRFS_EXTENT_ITEM_KEY); | ||
1041 | return -EUCLEAN; | ||
1042 | } | ||
1043 | if (!IS_ALIGNED(key->offset, fs_info->sectorsize)) { | ||
1044 | extent_err(leaf, slot, | ||
1045 | "invalid extent length, have %llu expect aligned to %u", | ||
1046 | key->offset, fs_info->sectorsize); | ||
1047 | return -EUCLEAN; | ||
1048 | } | ||
1049 | } | ||
1050 | ptr = (unsigned long)(struct btrfs_extent_item *)(ei + 1); | ||
1051 | |||
1052 | /* Check the special case of btrfs_tree_block_info */ | ||
1053 | if (is_tree_block && key->type != BTRFS_METADATA_ITEM_KEY) { | ||
1054 | struct btrfs_tree_block_info *info; | ||
1055 | |||
1056 | info = (struct btrfs_tree_block_info *)ptr; | ||
1057 | if (btrfs_tree_block_level(leaf, info) >= BTRFS_MAX_LEVEL) { | ||
1058 | extent_err(leaf, slot, | ||
1059 | "invalid tree block info level, have %u expect [0, %u]", | ||
1060 | btrfs_tree_block_level(leaf, info), | ||
1061 | BTRFS_MAX_LEVEL - 1); | ||
1062 | return -EUCLEAN; | ||
1063 | } | ||
1064 | ptr = (unsigned long)(struct btrfs_tree_block_info *)(info + 1); | ||
1065 | } | ||
1066 | |||
1067 | /* Check inline refs */ | ||
1068 | while (ptr < end) { | ||
1069 | struct btrfs_extent_inline_ref *iref; | ||
1070 | struct btrfs_extent_data_ref *dref; | ||
1071 | struct btrfs_shared_data_ref *sref; | ||
1072 | u64 dref_offset; | ||
1073 | u64 inline_offset; | ||
1074 | u8 inline_type; | ||
1075 | |||
1076 | if (ptr + sizeof(*iref) > end) { | ||
1077 | extent_err(leaf, slot, | ||
1078 | "inline ref item overflows extent item, ptr %lu iref size %zu end %lu", | ||
1079 | ptr, sizeof(*iref), end); | ||
1080 | return -EUCLEAN; | ||
1081 | } | ||
1082 | iref = (struct btrfs_extent_inline_ref *)ptr; | ||
1083 | inline_type = btrfs_extent_inline_ref_type(leaf, iref); | ||
1084 | inline_offset = btrfs_extent_inline_ref_offset(leaf, iref); | ||
1085 | if (ptr + btrfs_extent_inline_ref_size(inline_type) > end) { | ||
1086 | extent_err(leaf, slot, | ||
1087 | "inline ref item overflows extent item, ptr %lu iref size %u end %lu", | ||
1088 | ptr, inline_type, end); | ||
1089 | return -EUCLEAN; | ||
1090 | } | ||
1091 | |||
1092 | switch (inline_type) { | ||
1093 | /* inline_offset is subvolid of the owner, no need to check */ | ||
1094 | case BTRFS_TREE_BLOCK_REF_KEY: | ||
1095 | inline_refs++; | ||
1096 | break; | ||
1097 | /* Contains parent bytenr */ | ||
1098 | case BTRFS_SHARED_BLOCK_REF_KEY: | ||
1099 | if (!IS_ALIGNED(inline_offset, fs_info->sectorsize)) { | ||
1100 | extent_err(leaf, slot, | ||
1101 | "invalid tree parent bytenr, have %llu expect aligned to %u", | ||
1102 | inline_offset, fs_info->sectorsize); | ||
1103 | return -EUCLEAN; | ||
1104 | } | ||
1105 | inline_refs++; | ||
1106 | break; | ||
1107 | /* | ||
1108 | * Contains owner subvolid, owner key objectid, adjusted offset. | ||
1109 | * The only obvious corruption can happen in that offset. | ||
1110 | */ | ||
1111 | case BTRFS_EXTENT_DATA_REF_KEY: | ||
1112 | dref = (struct btrfs_extent_data_ref *)(&iref->offset); | ||
1113 | dref_offset = btrfs_extent_data_ref_offset(leaf, dref); | ||
1114 | if (!IS_ALIGNED(dref_offset, fs_info->sectorsize)) { | ||
1115 | extent_err(leaf, slot, | ||
1116 | "invalid data ref offset, have %llu expect aligned to %u", | ||
1117 | dref_offset, fs_info->sectorsize); | ||
1118 | return -EUCLEAN; | ||
1119 | } | ||
1120 | inline_refs += btrfs_extent_data_ref_count(leaf, dref); | ||
1121 | break; | ||
1122 | /* Contains parent bytenr and ref count */ | ||
1123 | case BTRFS_SHARED_DATA_REF_KEY: | ||
1124 | sref = (struct btrfs_shared_data_ref *)(iref + 1); | ||
1125 | if (!IS_ALIGNED(inline_offset, fs_info->sectorsize)) { | ||
1126 | extent_err(leaf, slot, | ||
1127 | "invalid data parent bytenr, have %llu expect aligned to %u", | ||
1128 | inline_offset, fs_info->sectorsize); | ||
1129 | return -EUCLEAN; | ||
1130 | } | ||
1131 | inline_refs += btrfs_shared_data_ref_count(leaf, sref); | ||
1132 | break; | ||
1133 | default: | ||
1134 | extent_err(leaf, slot, "unknown inline ref type: %u", | ||
1135 | inline_type); | ||
1136 | return -EUCLEAN; | ||
1137 | } | ||
1138 | ptr += btrfs_extent_inline_ref_size(inline_type); | ||
1139 | } | ||
1140 | /* No padding is allowed */ | ||
1141 | if (ptr != end) { | ||
1142 | extent_err(leaf, slot, | ||
1143 | "invalid extent item size, padding bytes found"); | ||
1144 | return -EUCLEAN; | ||
1145 | } | ||
1146 | |||
1147 | /* Finally, check the inline refs against total refs */ | ||
1148 | if (inline_refs > total_refs) { | ||
1149 | extent_err(leaf, slot, | ||
1150 | "invalid extent refs, have %llu expect >= inline %llu", | ||
1151 | total_refs, inline_refs); | ||
1152 | return -EUCLEAN; | ||
1153 | } | ||
1154 | return 0; | ||
1155 | } | ||
1156 | |||
913 | /* | 1157 | /* |
914 | * Common point to switch the item-specific validation. | 1158 | * Common point to switch the item-specific validation. |
915 | */ | 1159 | */ |
@@ -948,6 +1192,10 @@ static int check_leaf_item(struct extent_buffer *leaf, | |||
948 | case BTRFS_ROOT_ITEM_KEY: | 1192 | case BTRFS_ROOT_ITEM_KEY: |
949 | ret = check_root_item(leaf, key, slot); | 1193 | ret = check_root_item(leaf, key, slot); |
950 | break; | 1194 | break; |
1195 | case BTRFS_EXTENT_ITEM_KEY: | ||
1196 | case BTRFS_METADATA_ITEM_KEY: | ||
1197 | ret = check_extent_item(leaf, key, slot); | ||
1198 | break; | ||
951 | } | 1199 | } |
952 | return ret; | 1200 | return ret; |
953 | } | 1201 | } |