18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) Qu Wenruo 2017. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci/* 78c2ecf20Sopenharmony_ci * The module is used to catch unexpected/corrupted tree block data. 88c2ecf20Sopenharmony_ci * Such behavior can be caused either by a fuzzed image or bugs. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * The objective is to do leaf/node validation checks when tree block is read 118c2ecf20Sopenharmony_ci * from disk, and check *every* possible member, so other code won't 128c2ecf20Sopenharmony_ci * need to checking them again. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Due to the potential and unwanted damage, every checker needs to be 158c2ecf20Sopenharmony_ci * carefully reviewed otherwise so it does not prevent mount of valid images. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/types.h> 198c2ecf20Sopenharmony_ci#include <linux/stddef.h> 208c2ecf20Sopenharmony_ci#include <linux/error-injection.h> 218c2ecf20Sopenharmony_ci#include "ctree.h" 228c2ecf20Sopenharmony_ci#include "tree-checker.h" 238c2ecf20Sopenharmony_ci#include "disk-io.h" 248c2ecf20Sopenharmony_ci#include "compression.h" 258c2ecf20Sopenharmony_ci#include "volumes.h" 268c2ecf20Sopenharmony_ci#include "misc.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * Error message should follow the following format: 308c2ecf20Sopenharmony_ci * corrupt <type>: <identifier>, <reason>[, <bad_value>] 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * @type: leaf or node 338c2ecf20Sopenharmony_ci * @identifier: the necessary info to locate the leaf/node. 348c2ecf20Sopenharmony_ci * It's recommended to decode key.objecitd/offset if it's 358c2ecf20Sopenharmony_ci * meaningful. 368c2ecf20Sopenharmony_ci * @reason: describe the error 378c2ecf20Sopenharmony_ci * @bad_value: optional, it's recommended to output bad value and its 388c2ecf20Sopenharmony_ci * expected value (range). 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * Since comma is used to separate the components, only space is allowed 418c2ecf20Sopenharmony_ci * inside each component. 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* 458c2ecf20Sopenharmony_ci * Append generic "corrupt leaf/node root=%llu block=%llu slot=%d: " to @fmt. 468c2ecf20Sopenharmony_ci * Allows callers to customize the output. 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_ci__printf(3, 4) 498c2ecf20Sopenharmony_ci__cold 508c2ecf20Sopenharmony_cistatic void generic_err(const struct extent_buffer *eb, int slot, 518c2ecf20Sopenharmony_ci const char *fmt, ...) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci const struct btrfs_fs_info *fs_info = eb->fs_info; 548c2ecf20Sopenharmony_ci struct va_format vaf; 558c2ecf20Sopenharmony_ci va_list args; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci va_start(args, fmt); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci vaf.fmt = fmt; 608c2ecf20Sopenharmony_ci vaf.va = &args; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci btrfs_crit(fs_info, 638c2ecf20Sopenharmony_ci "corrupt %s: root=%llu block=%llu slot=%d, %pV", 648c2ecf20Sopenharmony_ci btrfs_header_level(eb) == 0 ? "leaf" : "node", 658c2ecf20Sopenharmony_ci btrfs_header_owner(eb), btrfs_header_bytenr(eb), slot, &vaf); 668c2ecf20Sopenharmony_ci va_end(args); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* 708c2ecf20Sopenharmony_ci * Customized reporter for extent data item, since its key objectid and 718c2ecf20Sopenharmony_ci * offset has its own meaning. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci__printf(3, 4) 748c2ecf20Sopenharmony_ci__cold 758c2ecf20Sopenharmony_cistatic void file_extent_err(const struct extent_buffer *eb, int slot, 768c2ecf20Sopenharmony_ci const char *fmt, ...) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci const struct btrfs_fs_info *fs_info = eb->fs_info; 798c2ecf20Sopenharmony_ci struct btrfs_key key; 808c2ecf20Sopenharmony_ci struct va_format vaf; 818c2ecf20Sopenharmony_ci va_list args; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci btrfs_item_key_to_cpu(eb, &key, slot); 848c2ecf20Sopenharmony_ci va_start(args, fmt); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci vaf.fmt = fmt; 878c2ecf20Sopenharmony_ci vaf.va = &args; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci btrfs_crit(fs_info, 908c2ecf20Sopenharmony_ci "corrupt %s: root=%llu block=%llu slot=%d ino=%llu file_offset=%llu, %pV", 918c2ecf20Sopenharmony_ci btrfs_header_level(eb) == 0 ? "leaf" : "node", 928c2ecf20Sopenharmony_ci btrfs_header_owner(eb), btrfs_header_bytenr(eb), slot, 938c2ecf20Sopenharmony_ci key.objectid, key.offset, &vaf); 948c2ecf20Sopenharmony_ci va_end(args); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* 988c2ecf20Sopenharmony_ci * Return 0 if the btrfs_file_extent_##name is aligned to @alignment 998c2ecf20Sopenharmony_ci * Else return 1 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ci#define CHECK_FE_ALIGNED(leaf, slot, fi, name, alignment) \ 1028c2ecf20Sopenharmony_ci({ \ 1038c2ecf20Sopenharmony_ci if (!IS_ALIGNED(btrfs_file_extent_##name((leaf), (fi)), (alignment))) \ 1048c2ecf20Sopenharmony_ci file_extent_err((leaf), (slot), \ 1058c2ecf20Sopenharmony_ci "invalid %s for file extent, have %llu, should be aligned to %u", \ 1068c2ecf20Sopenharmony_ci (#name), btrfs_file_extent_##name((leaf), (fi)), \ 1078c2ecf20Sopenharmony_ci (alignment)); \ 1088c2ecf20Sopenharmony_ci (!IS_ALIGNED(btrfs_file_extent_##name((leaf), (fi)), (alignment))); \ 1098c2ecf20Sopenharmony_ci}) 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic u64 file_extent_end(struct extent_buffer *leaf, 1128c2ecf20Sopenharmony_ci struct btrfs_key *key, 1138c2ecf20Sopenharmony_ci struct btrfs_file_extent_item *extent) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci u64 end; 1168c2ecf20Sopenharmony_ci u64 len; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (btrfs_file_extent_type(leaf, extent) == BTRFS_FILE_EXTENT_INLINE) { 1198c2ecf20Sopenharmony_ci len = btrfs_file_extent_ram_bytes(leaf, extent); 1208c2ecf20Sopenharmony_ci end = ALIGN(key->offset + len, leaf->fs_info->sectorsize); 1218c2ecf20Sopenharmony_ci } else { 1228c2ecf20Sopenharmony_ci len = btrfs_file_extent_num_bytes(leaf, extent); 1238c2ecf20Sopenharmony_ci end = key->offset + len; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci return end; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* 1298c2ecf20Sopenharmony_ci * Customized report for dir_item, the only new important information is 1308c2ecf20Sopenharmony_ci * key->objectid, which represents inode number 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_ci__printf(3, 4) 1338c2ecf20Sopenharmony_ci__cold 1348c2ecf20Sopenharmony_cistatic void dir_item_err(const struct extent_buffer *eb, int slot, 1358c2ecf20Sopenharmony_ci const char *fmt, ...) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci const struct btrfs_fs_info *fs_info = eb->fs_info; 1388c2ecf20Sopenharmony_ci struct btrfs_key key; 1398c2ecf20Sopenharmony_ci struct va_format vaf; 1408c2ecf20Sopenharmony_ci va_list args; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci btrfs_item_key_to_cpu(eb, &key, slot); 1438c2ecf20Sopenharmony_ci va_start(args, fmt); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci vaf.fmt = fmt; 1468c2ecf20Sopenharmony_ci vaf.va = &args; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci btrfs_crit(fs_info, 1498c2ecf20Sopenharmony_ci "corrupt %s: root=%llu block=%llu slot=%d ino=%llu, %pV", 1508c2ecf20Sopenharmony_ci btrfs_header_level(eb) == 0 ? "leaf" : "node", 1518c2ecf20Sopenharmony_ci btrfs_header_owner(eb), btrfs_header_bytenr(eb), slot, 1528c2ecf20Sopenharmony_ci key.objectid, &vaf); 1538c2ecf20Sopenharmony_ci va_end(args); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/* 1578c2ecf20Sopenharmony_ci * This functions checks prev_key->objectid, to ensure current key and prev_key 1588c2ecf20Sopenharmony_ci * share the same objectid as inode number. 1598c2ecf20Sopenharmony_ci * 1608c2ecf20Sopenharmony_ci * This is to detect missing INODE_ITEM in subvolume trees. 1618c2ecf20Sopenharmony_ci * 1628c2ecf20Sopenharmony_ci * Return true if everything is OK or we don't need to check. 1638c2ecf20Sopenharmony_ci * Return false if anything is wrong. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_cistatic bool check_prev_ino(struct extent_buffer *leaf, 1668c2ecf20Sopenharmony_ci struct btrfs_key *key, int slot, 1678c2ecf20Sopenharmony_ci struct btrfs_key *prev_key) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci /* No prev key, skip check */ 1708c2ecf20Sopenharmony_ci if (slot == 0) 1718c2ecf20Sopenharmony_ci return true; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* Only these key->types needs to be checked */ 1748c2ecf20Sopenharmony_ci ASSERT(key->type == BTRFS_XATTR_ITEM_KEY || 1758c2ecf20Sopenharmony_ci key->type == BTRFS_INODE_REF_KEY || 1768c2ecf20Sopenharmony_ci key->type == BTRFS_DIR_INDEX_KEY || 1778c2ecf20Sopenharmony_ci key->type == BTRFS_DIR_ITEM_KEY || 1788c2ecf20Sopenharmony_ci key->type == BTRFS_EXTENT_DATA_KEY); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* 1818c2ecf20Sopenharmony_ci * Only subvolume trees along with their reloc trees need this check. 1828c2ecf20Sopenharmony_ci * Things like log tree doesn't follow this ino requirement. 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ci if (!is_fstree(btrfs_header_owner(leaf))) 1858c2ecf20Sopenharmony_ci return true; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (key->objectid == prev_key->objectid) 1888c2ecf20Sopenharmony_ci return true; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* Error found */ 1918c2ecf20Sopenharmony_ci dir_item_err(leaf, slot, 1928c2ecf20Sopenharmony_ci "invalid previous key objectid, have %llu expect %llu", 1938c2ecf20Sopenharmony_ci prev_key->objectid, key->objectid); 1948c2ecf20Sopenharmony_ci return false; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_cistatic int check_extent_data_item(struct extent_buffer *leaf, 1978c2ecf20Sopenharmony_ci struct btrfs_key *key, int slot, 1988c2ecf20Sopenharmony_ci struct btrfs_key *prev_key) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct btrfs_fs_info *fs_info = leaf->fs_info; 2018c2ecf20Sopenharmony_ci struct btrfs_file_extent_item *fi; 2028c2ecf20Sopenharmony_ci u32 sectorsize = fs_info->sectorsize; 2038c2ecf20Sopenharmony_ci u32 item_size = btrfs_item_size_nr(leaf, slot); 2048c2ecf20Sopenharmony_ci u64 extent_end; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (!IS_ALIGNED(key->offset, sectorsize)) { 2078c2ecf20Sopenharmony_ci file_extent_err(leaf, slot, 2088c2ecf20Sopenharmony_ci"unaligned file_offset for file extent, have %llu should be aligned to %u", 2098c2ecf20Sopenharmony_ci key->offset, sectorsize); 2108c2ecf20Sopenharmony_ci return -EUCLEAN; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* 2148c2ecf20Sopenharmony_ci * Previous key must have the same key->objectid (ino). 2158c2ecf20Sopenharmony_ci * It can be XATTR_ITEM, INODE_ITEM or just another EXTENT_DATA. 2168c2ecf20Sopenharmony_ci * But if objectids mismatch, it means we have a missing 2178c2ecf20Sopenharmony_ci * INODE_ITEM. 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_ci if (!check_prev_ino(leaf, key, slot, prev_key)) 2208c2ecf20Sopenharmony_ci return -EUCLEAN; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* 2258c2ecf20Sopenharmony_ci * Make sure the item contains at least inline header, so the file 2268c2ecf20Sopenharmony_ci * extent type is not some garbage. 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_ci if (item_size < BTRFS_FILE_EXTENT_INLINE_DATA_START) { 2298c2ecf20Sopenharmony_ci file_extent_err(leaf, slot, 2308c2ecf20Sopenharmony_ci "invalid item size, have %u expect [%zu, %u)", 2318c2ecf20Sopenharmony_ci item_size, BTRFS_FILE_EXTENT_INLINE_DATA_START, 2328c2ecf20Sopenharmony_ci SZ_4K); 2338c2ecf20Sopenharmony_ci return -EUCLEAN; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci if (btrfs_file_extent_type(leaf, fi) >= BTRFS_NR_FILE_EXTENT_TYPES) { 2368c2ecf20Sopenharmony_ci file_extent_err(leaf, slot, 2378c2ecf20Sopenharmony_ci "invalid type for file extent, have %u expect range [0, %u]", 2388c2ecf20Sopenharmony_ci btrfs_file_extent_type(leaf, fi), 2398c2ecf20Sopenharmony_ci BTRFS_NR_FILE_EXTENT_TYPES - 1); 2408c2ecf20Sopenharmony_ci return -EUCLEAN; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* 2448c2ecf20Sopenharmony_ci * Support for new compression/encryption must introduce incompat flag, 2458c2ecf20Sopenharmony_ci * and must be caught in open_ctree(). 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_ci if (btrfs_file_extent_compression(leaf, fi) >= BTRFS_NR_COMPRESS_TYPES) { 2488c2ecf20Sopenharmony_ci file_extent_err(leaf, slot, 2498c2ecf20Sopenharmony_ci "invalid compression for file extent, have %u expect range [0, %u]", 2508c2ecf20Sopenharmony_ci btrfs_file_extent_compression(leaf, fi), 2518c2ecf20Sopenharmony_ci BTRFS_NR_COMPRESS_TYPES - 1); 2528c2ecf20Sopenharmony_ci return -EUCLEAN; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci if (btrfs_file_extent_encryption(leaf, fi)) { 2558c2ecf20Sopenharmony_ci file_extent_err(leaf, slot, 2568c2ecf20Sopenharmony_ci "invalid encryption for file extent, have %u expect 0", 2578c2ecf20Sopenharmony_ci btrfs_file_extent_encryption(leaf, fi)); 2588c2ecf20Sopenharmony_ci return -EUCLEAN; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE) { 2618c2ecf20Sopenharmony_ci /* Inline extent must have 0 as key offset */ 2628c2ecf20Sopenharmony_ci if (key->offset) { 2638c2ecf20Sopenharmony_ci file_extent_err(leaf, slot, 2648c2ecf20Sopenharmony_ci "invalid file_offset for inline file extent, have %llu expect 0", 2658c2ecf20Sopenharmony_ci key->offset); 2668c2ecf20Sopenharmony_ci return -EUCLEAN; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* Compressed inline extent has no on-disk size, skip it */ 2708c2ecf20Sopenharmony_ci if (btrfs_file_extent_compression(leaf, fi) != 2718c2ecf20Sopenharmony_ci BTRFS_COMPRESS_NONE) 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* Uncompressed inline extent size must match item size */ 2758c2ecf20Sopenharmony_ci if (item_size != BTRFS_FILE_EXTENT_INLINE_DATA_START + 2768c2ecf20Sopenharmony_ci btrfs_file_extent_ram_bytes(leaf, fi)) { 2778c2ecf20Sopenharmony_ci file_extent_err(leaf, slot, 2788c2ecf20Sopenharmony_ci "invalid ram_bytes for uncompressed inline extent, have %u expect %llu", 2798c2ecf20Sopenharmony_ci item_size, BTRFS_FILE_EXTENT_INLINE_DATA_START + 2808c2ecf20Sopenharmony_ci btrfs_file_extent_ram_bytes(leaf, fi)); 2818c2ecf20Sopenharmony_ci return -EUCLEAN; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* Regular or preallocated extent has fixed item size */ 2878c2ecf20Sopenharmony_ci if (item_size != sizeof(*fi)) { 2888c2ecf20Sopenharmony_ci file_extent_err(leaf, slot, 2898c2ecf20Sopenharmony_ci "invalid item size for reg/prealloc file extent, have %u expect %zu", 2908c2ecf20Sopenharmony_ci item_size, sizeof(*fi)); 2918c2ecf20Sopenharmony_ci return -EUCLEAN; 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci if (CHECK_FE_ALIGNED(leaf, slot, fi, ram_bytes, sectorsize) || 2948c2ecf20Sopenharmony_ci CHECK_FE_ALIGNED(leaf, slot, fi, disk_bytenr, sectorsize) || 2958c2ecf20Sopenharmony_ci CHECK_FE_ALIGNED(leaf, slot, fi, disk_num_bytes, sectorsize) || 2968c2ecf20Sopenharmony_ci CHECK_FE_ALIGNED(leaf, slot, fi, offset, sectorsize) || 2978c2ecf20Sopenharmony_ci CHECK_FE_ALIGNED(leaf, slot, fi, num_bytes, sectorsize)) 2988c2ecf20Sopenharmony_ci return -EUCLEAN; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* Catch extent end overflow */ 3018c2ecf20Sopenharmony_ci if (check_add_overflow(btrfs_file_extent_num_bytes(leaf, fi), 3028c2ecf20Sopenharmony_ci key->offset, &extent_end)) { 3038c2ecf20Sopenharmony_ci file_extent_err(leaf, slot, 3048c2ecf20Sopenharmony_ci "extent end overflow, have file offset %llu extent num bytes %llu", 3058c2ecf20Sopenharmony_ci key->offset, 3068c2ecf20Sopenharmony_ci btrfs_file_extent_num_bytes(leaf, fi)); 3078c2ecf20Sopenharmony_ci return -EUCLEAN; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* 3118c2ecf20Sopenharmony_ci * Check that no two consecutive file extent items, in the same leaf, 3128c2ecf20Sopenharmony_ci * present ranges that overlap each other. 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_ci if (slot > 0 && 3158c2ecf20Sopenharmony_ci prev_key->objectid == key->objectid && 3168c2ecf20Sopenharmony_ci prev_key->type == BTRFS_EXTENT_DATA_KEY) { 3178c2ecf20Sopenharmony_ci struct btrfs_file_extent_item *prev_fi; 3188c2ecf20Sopenharmony_ci u64 prev_end; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci prev_fi = btrfs_item_ptr(leaf, slot - 1, 3218c2ecf20Sopenharmony_ci struct btrfs_file_extent_item); 3228c2ecf20Sopenharmony_ci prev_end = file_extent_end(leaf, prev_key, prev_fi); 3238c2ecf20Sopenharmony_ci if (prev_end > key->offset) { 3248c2ecf20Sopenharmony_ci file_extent_err(leaf, slot - 1, 3258c2ecf20Sopenharmony_ci"file extent end range (%llu) goes beyond start offset (%llu) of the next file extent", 3268c2ecf20Sopenharmony_ci prev_end, key->offset); 3278c2ecf20Sopenharmony_ci return -EUCLEAN; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci return 0; 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic int check_csum_item(struct extent_buffer *leaf, struct btrfs_key *key, 3358c2ecf20Sopenharmony_ci int slot, struct btrfs_key *prev_key) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct btrfs_fs_info *fs_info = leaf->fs_info; 3388c2ecf20Sopenharmony_ci u32 sectorsize = fs_info->sectorsize; 3398c2ecf20Sopenharmony_ci u32 csumsize = btrfs_super_csum_size(fs_info->super_copy); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (key->objectid != BTRFS_EXTENT_CSUM_OBJECTID) { 3428c2ecf20Sopenharmony_ci generic_err(leaf, slot, 3438c2ecf20Sopenharmony_ci "invalid key objectid for csum item, have %llu expect %llu", 3448c2ecf20Sopenharmony_ci key->objectid, BTRFS_EXTENT_CSUM_OBJECTID); 3458c2ecf20Sopenharmony_ci return -EUCLEAN; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci if (!IS_ALIGNED(key->offset, sectorsize)) { 3488c2ecf20Sopenharmony_ci generic_err(leaf, slot, 3498c2ecf20Sopenharmony_ci "unaligned key offset for csum item, have %llu should be aligned to %u", 3508c2ecf20Sopenharmony_ci key->offset, sectorsize); 3518c2ecf20Sopenharmony_ci return -EUCLEAN; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci if (!IS_ALIGNED(btrfs_item_size_nr(leaf, slot), csumsize)) { 3548c2ecf20Sopenharmony_ci generic_err(leaf, slot, 3558c2ecf20Sopenharmony_ci "unaligned item size for csum item, have %u should be aligned to %u", 3568c2ecf20Sopenharmony_ci btrfs_item_size_nr(leaf, slot), csumsize); 3578c2ecf20Sopenharmony_ci return -EUCLEAN; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci if (slot > 0 && prev_key->type == BTRFS_EXTENT_CSUM_KEY) { 3608c2ecf20Sopenharmony_ci u64 prev_csum_end; 3618c2ecf20Sopenharmony_ci u32 prev_item_size; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci prev_item_size = btrfs_item_size_nr(leaf, slot - 1); 3648c2ecf20Sopenharmony_ci prev_csum_end = (prev_item_size / csumsize) * sectorsize; 3658c2ecf20Sopenharmony_ci prev_csum_end += prev_key->offset; 3668c2ecf20Sopenharmony_ci if (prev_csum_end > key->offset) { 3678c2ecf20Sopenharmony_ci generic_err(leaf, slot - 1, 3688c2ecf20Sopenharmony_ci"csum end range (%llu) goes beyond the start range (%llu) of the next csum item", 3698c2ecf20Sopenharmony_ci prev_csum_end, key->offset); 3708c2ecf20Sopenharmony_ci return -EUCLEAN; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci return 0; 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci/* Inode item error output has the same format as dir_item_err() */ 3778c2ecf20Sopenharmony_ci#define inode_item_err(eb, slot, fmt, ...) \ 3788c2ecf20Sopenharmony_ci dir_item_err(eb, slot, fmt, __VA_ARGS__) 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic int check_inode_key(struct extent_buffer *leaf, struct btrfs_key *key, 3818c2ecf20Sopenharmony_ci int slot) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct btrfs_key item_key; 3848c2ecf20Sopenharmony_ci bool is_inode_item; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci btrfs_item_key_to_cpu(leaf, &item_key, slot); 3878c2ecf20Sopenharmony_ci is_inode_item = (item_key.type == BTRFS_INODE_ITEM_KEY); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* For XATTR_ITEM, location key should be all 0 */ 3908c2ecf20Sopenharmony_ci if (item_key.type == BTRFS_XATTR_ITEM_KEY) { 3918c2ecf20Sopenharmony_ci if (key->type != 0 || key->objectid != 0 || key->offset != 0) 3928c2ecf20Sopenharmony_ci return -EUCLEAN; 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if ((key->objectid < BTRFS_FIRST_FREE_OBJECTID || 3978c2ecf20Sopenharmony_ci key->objectid > BTRFS_LAST_FREE_OBJECTID) && 3988c2ecf20Sopenharmony_ci key->objectid != BTRFS_ROOT_TREE_DIR_OBJECTID && 3998c2ecf20Sopenharmony_ci key->objectid != BTRFS_FREE_INO_OBJECTID) { 4008c2ecf20Sopenharmony_ci if (is_inode_item) { 4018c2ecf20Sopenharmony_ci generic_err(leaf, slot, 4028c2ecf20Sopenharmony_ci "invalid key objectid: has %llu expect %llu or [%llu, %llu] or %llu", 4038c2ecf20Sopenharmony_ci key->objectid, BTRFS_ROOT_TREE_DIR_OBJECTID, 4048c2ecf20Sopenharmony_ci BTRFS_FIRST_FREE_OBJECTID, 4058c2ecf20Sopenharmony_ci BTRFS_LAST_FREE_OBJECTID, 4068c2ecf20Sopenharmony_ci BTRFS_FREE_INO_OBJECTID); 4078c2ecf20Sopenharmony_ci } else { 4088c2ecf20Sopenharmony_ci dir_item_err(leaf, slot, 4098c2ecf20Sopenharmony_ci"invalid location key objectid: has %llu expect %llu or [%llu, %llu] or %llu", 4108c2ecf20Sopenharmony_ci key->objectid, BTRFS_ROOT_TREE_DIR_OBJECTID, 4118c2ecf20Sopenharmony_ci BTRFS_FIRST_FREE_OBJECTID, 4128c2ecf20Sopenharmony_ci BTRFS_LAST_FREE_OBJECTID, 4138c2ecf20Sopenharmony_ci BTRFS_FREE_INO_OBJECTID); 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci return -EUCLEAN; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci if (key->offset != 0) { 4188c2ecf20Sopenharmony_ci if (is_inode_item) 4198c2ecf20Sopenharmony_ci inode_item_err(leaf, slot, 4208c2ecf20Sopenharmony_ci "invalid key offset: has %llu expect 0", 4218c2ecf20Sopenharmony_ci key->offset); 4228c2ecf20Sopenharmony_ci else 4238c2ecf20Sopenharmony_ci dir_item_err(leaf, slot, 4248c2ecf20Sopenharmony_ci "invalid location key offset:has %llu expect 0", 4258c2ecf20Sopenharmony_ci key->offset); 4268c2ecf20Sopenharmony_ci return -EUCLEAN; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic int check_root_key(struct extent_buffer *leaf, struct btrfs_key *key, 4328c2ecf20Sopenharmony_ci int slot) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci struct btrfs_key item_key; 4358c2ecf20Sopenharmony_ci bool is_root_item; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci btrfs_item_key_to_cpu(leaf, &item_key, slot); 4388c2ecf20Sopenharmony_ci is_root_item = (item_key.type == BTRFS_ROOT_ITEM_KEY); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* No such tree id */ 4418c2ecf20Sopenharmony_ci if (key->objectid == 0) { 4428c2ecf20Sopenharmony_ci if (is_root_item) 4438c2ecf20Sopenharmony_ci generic_err(leaf, slot, "invalid root id 0"); 4448c2ecf20Sopenharmony_ci else 4458c2ecf20Sopenharmony_ci dir_item_err(leaf, slot, 4468c2ecf20Sopenharmony_ci "invalid location key root id 0"); 4478c2ecf20Sopenharmony_ci return -EUCLEAN; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* DIR_ITEM/INDEX/INODE_REF is not allowed to point to non-fs trees */ 4518c2ecf20Sopenharmony_ci if (!is_fstree(key->objectid) && !is_root_item) { 4528c2ecf20Sopenharmony_ci dir_item_err(leaf, slot, 4538c2ecf20Sopenharmony_ci "invalid location key objectid, have %llu expect [%llu, %llu]", 4548c2ecf20Sopenharmony_ci key->objectid, BTRFS_FIRST_FREE_OBJECTID, 4558c2ecf20Sopenharmony_ci BTRFS_LAST_FREE_OBJECTID); 4568c2ecf20Sopenharmony_ci return -EUCLEAN; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* 4608c2ecf20Sopenharmony_ci * ROOT_ITEM with non-zero offset means this is a snapshot, created at 4618c2ecf20Sopenharmony_ci * @offset transid. 4628c2ecf20Sopenharmony_ci * Furthermore, for location key in DIR_ITEM, its offset is always -1. 4638c2ecf20Sopenharmony_ci * 4648c2ecf20Sopenharmony_ci * So here we only check offset for reloc tree whose key->offset must 4658c2ecf20Sopenharmony_ci * be a valid tree. 4668c2ecf20Sopenharmony_ci */ 4678c2ecf20Sopenharmony_ci if (key->objectid == BTRFS_TREE_RELOC_OBJECTID && key->offset == 0) { 4688c2ecf20Sopenharmony_ci generic_err(leaf, slot, "invalid root id 0 for reloc tree"); 4698c2ecf20Sopenharmony_ci return -EUCLEAN; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic int check_dir_item(struct extent_buffer *leaf, 4758c2ecf20Sopenharmony_ci struct btrfs_key *key, struct btrfs_key *prev_key, 4768c2ecf20Sopenharmony_ci int slot) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci struct btrfs_fs_info *fs_info = leaf->fs_info; 4798c2ecf20Sopenharmony_ci struct btrfs_dir_item *di; 4808c2ecf20Sopenharmony_ci u32 item_size = btrfs_item_size_nr(leaf, slot); 4818c2ecf20Sopenharmony_ci u32 cur = 0; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (!check_prev_ino(leaf, key, slot, prev_key)) 4848c2ecf20Sopenharmony_ci return -EUCLEAN; 4858c2ecf20Sopenharmony_ci di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item); 4868c2ecf20Sopenharmony_ci while (cur < item_size) { 4878c2ecf20Sopenharmony_ci struct btrfs_key location_key; 4888c2ecf20Sopenharmony_ci u32 name_len; 4898c2ecf20Sopenharmony_ci u32 data_len; 4908c2ecf20Sopenharmony_ci u32 max_name_len; 4918c2ecf20Sopenharmony_ci u32 total_size; 4928c2ecf20Sopenharmony_ci u32 name_hash; 4938c2ecf20Sopenharmony_ci u8 dir_type; 4948c2ecf20Sopenharmony_ci int ret; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* header itself should not cross item boundary */ 4978c2ecf20Sopenharmony_ci if (cur + sizeof(*di) > item_size) { 4988c2ecf20Sopenharmony_ci dir_item_err(leaf, slot, 4998c2ecf20Sopenharmony_ci "dir item header crosses item boundary, have %zu boundary %u", 5008c2ecf20Sopenharmony_ci cur + sizeof(*di), item_size); 5018c2ecf20Sopenharmony_ci return -EUCLEAN; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* Location key check */ 5058c2ecf20Sopenharmony_ci btrfs_dir_item_key_to_cpu(leaf, di, &location_key); 5068c2ecf20Sopenharmony_ci if (location_key.type == BTRFS_ROOT_ITEM_KEY) { 5078c2ecf20Sopenharmony_ci ret = check_root_key(leaf, &location_key, slot); 5088c2ecf20Sopenharmony_ci if (ret < 0) 5098c2ecf20Sopenharmony_ci return ret; 5108c2ecf20Sopenharmony_ci } else if (location_key.type == BTRFS_INODE_ITEM_KEY || 5118c2ecf20Sopenharmony_ci location_key.type == 0) { 5128c2ecf20Sopenharmony_ci ret = check_inode_key(leaf, &location_key, slot); 5138c2ecf20Sopenharmony_ci if (ret < 0) 5148c2ecf20Sopenharmony_ci return ret; 5158c2ecf20Sopenharmony_ci } else { 5168c2ecf20Sopenharmony_ci dir_item_err(leaf, slot, 5178c2ecf20Sopenharmony_ci "invalid location key type, have %u, expect %u or %u", 5188c2ecf20Sopenharmony_ci location_key.type, BTRFS_ROOT_ITEM_KEY, 5198c2ecf20Sopenharmony_ci BTRFS_INODE_ITEM_KEY); 5208c2ecf20Sopenharmony_ci return -EUCLEAN; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci /* dir type check */ 5248c2ecf20Sopenharmony_ci dir_type = btrfs_dir_type(leaf, di); 5258c2ecf20Sopenharmony_ci if (dir_type >= BTRFS_FT_MAX) { 5268c2ecf20Sopenharmony_ci dir_item_err(leaf, slot, 5278c2ecf20Sopenharmony_ci "invalid dir item type, have %u expect [0, %u)", 5288c2ecf20Sopenharmony_ci dir_type, BTRFS_FT_MAX); 5298c2ecf20Sopenharmony_ci return -EUCLEAN; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (key->type == BTRFS_XATTR_ITEM_KEY && 5338c2ecf20Sopenharmony_ci dir_type != BTRFS_FT_XATTR) { 5348c2ecf20Sopenharmony_ci dir_item_err(leaf, slot, 5358c2ecf20Sopenharmony_ci "invalid dir item type for XATTR key, have %u expect %u", 5368c2ecf20Sopenharmony_ci dir_type, BTRFS_FT_XATTR); 5378c2ecf20Sopenharmony_ci return -EUCLEAN; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci if (dir_type == BTRFS_FT_XATTR && 5408c2ecf20Sopenharmony_ci key->type != BTRFS_XATTR_ITEM_KEY) { 5418c2ecf20Sopenharmony_ci dir_item_err(leaf, slot, 5428c2ecf20Sopenharmony_ci "xattr dir type found for non-XATTR key"); 5438c2ecf20Sopenharmony_ci return -EUCLEAN; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci if (dir_type == BTRFS_FT_XATTR) 5468c2ecf20Sopenharmony_ci max_name_len = XATTR_NAME_MAX; 5478c2ecf20Sopenharmony_ci else 5488c2ecf20Sopenharmony_ci max_name_len = BTRFS_NAME_LEN; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci /* Name/data length check */ 5518c2ecf20Sopenharmony_ci name_len = btrfs_dir_name_len(leaf, di); 5528c2ecf20Sopenharmony_ci data_len = btrfs_dir_data_len(leaf, di); 5538c2ecf20Sopenharmony_ci if (name_len > max_name_len) { 5548c2ecf20Sopenharmony_ci dir_item_err(leaf, slot, 5558c2ecf20Sopenharmony_ci "dir item name len too long, have %u max %u", 5568c2ecf20Sopenharmony_ci name_len, max_name_len); 5578c2ecf20Sopenharmony_ci return -EUCLEAN; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci if (name_len + data_len > BTRFS_MAX_XATTR_SIZE(fs_info)) { 5608c2ecf20Sopenharmony_ci dir_item_err(leaf, slot, 5618c2ecf20Sopenharmony_ci "dir item name and data len too long, have %u max %u", 5628c2ecf20Sopenharmony_ci name_len + data_len, 5638c2ecf20Sopenharmony_ci BTRFS_MAX_XATTR_SIZE(fs_info)); 5648c2ecf20Sopenharmony_ci return -EUCLEAN; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci if (data_len && dir_type != BTRFS_FT_XATTR) { 5688c2ecf20Sopenharmony_ci dir_item_err(leaf, slot, 5698c2ecf20Sopenharmony_ci "dir item with invalid data len, have %u expect 0", 5708c2ecf20Sopenharmony_ci data_len); 5718c2ecf20Sopenharmony_ci return -EUCLEAN; 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci total_size = sizeof(*di) + name_len + data_len; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci /* header and name/data should not cross item boundary */ 5778c2ecf20Sopenharmony_ci if (cur + total_size > item_size) { 5788c2ecf20Sopenharmony_ci dir_item_err(leaf, slot, 5798c2ecf20Sopenharmony_ci "dir item data crosses item boundary, have %u boundary %u", 5808c2ecf20Sopenharmony_ci cur + total_size, item_size); 5818c2ecf20Sopenharmony_ci return -EUCLEAN; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* 5858c2ecf20Sopenharmony_ci * Special check for XATTR/DIR_ITEM, as key->offset is name 5868c2ecf20Sopenharmony_ci * hash, should match its name 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_ci if (key->type == BTRFS_DIR_ITEM_KEY || 5898c2ecf20Sopenharmony_ci key->type == BTRFS_XATTR_ITEM_KEY) { 5908c2ecf20Sopenharmony_ci char namebuf[max(BTRFS_NAME_LEN, XATTR_NAME_MAX)]; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci read_extent_buffer(leaf, namebuf, 5938c2ecf20Sopenharmony_ci (unsigned long)(di + 1), name_len); 5948c2ecf20Sopenharmony_ci name_hash = btrfs_name_hash(namebuf, name_len); 5958c2ecf20Sopenharmony_ci if (key->offset != name_hash) { 5968c2ecf20Sopenharmony_ci dir_item_err(leaf, slot, 5978c2ecf20Sopenharmony_ci "name hash mismatch with key, have 0x%016x expect 0x%016llx", 5988c2ecf20Sopenharmony_ci name_hash, key->offset); 5998c2ecf20Sopenharmony_ci return -EUCLEAN; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci cur += total_size; 6038c2ecf20Sopenharmony_ci di = (struct btrfs_dir_item *)((void *)di + total_size); 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci return 0; 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci__printf(3, 4) 6098c2ecf20Sopenharmony_ci__cold 6108c2ecf20Sopenharmony_cistatic void block_group_err(const struct extent_buffer *eb, int slot, 6118c2ecf20Sopenharmony_ci const char *fmt, ...) 6128c2ecf20Sopenharmony_ci{ 6138c2ecf20Sopenharmony_ci const struct btrfs_fs_info *fs_info = eb->fs_info; 6148c2ecf20Sopenharmony_ci struct btrfs_key key; 6158c2ecf20Sopenharmony_ci struct va_format vaf; 6168c2ecf20Sopenharmony_ci va_list args; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci btrfs_item_key_to_cpu(eb, &key, slot); 6198c2ecf20Sopenharmony_ci va_start(args, fmt); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci vaf.fmt = fmt; 6228c2ecf20Sopenharmony_ci vaf.va = &args; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci btrfs_crit(fs_info, 6258c2ecf20Sopenharmony_ci "corrupt %s: root=%llu block=%llu slot=%d bg_start=%llu bg_len=%llu, %pV", 6268c2ecf20Sopenharmony_ci btrfs_header_level(eb) == 0 ? "leaf" : "node", 6278c2ecf20Sopenharmony_ci btrfs_header_owner(eb), btrfs_header_bytenr(eb), slot, 6288c2ecf20Sopenharmony_ci key.objectid, key.offset, &vaf); 6298c2ecf20Sopenharmony_ci va_end(args); 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic int check_block_group_item(struct extent_buffer *leaf, 6338c2ecf20Sopenharmony_ci struct btrfs_key *key, int slot) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci struct btrfs_block_group_item bgi; 6368c2ecf20Sopenharmony_ci u32 item_size = btrfs_item_size_nr(leaf, slot); 6378c2ecf20Sopenharmony_ci u64 flags; 6388c2ecf20Sopenharmony_ci u64 type; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci /* 6418c2ecf20Sopenharmony_ci * Here we don't really care about alignment since extent allocator can 6428c2ecf20Sopenharmony_ci * handle it. We care more about the size. 6438c2ecf20Sopenharmony_ci */ 6448c2ecf20Sopenharmony_ci if (key->offset == 0) { 6458c2ecf20Sopenharmony_ci block_group_err(leaf, slot, 6468c2ecf20Sopenharmony_ci "invalid block group size 0"); 6478c2ecf20Sopenharmony_ci return -EUCLEAN; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci if (item_size != sizeof(bgi)) { 6518c2ecf20Sopenharmony_ci block_group_err(leaf, slot, 6528c2ecf20Sopenharmony_ci "invalid item size, have %u expect %zu", 6538c2ecf20Sopenharmony_ci item_size, sizeof(bgi)); 6548c2ecf20Sopenharmony_ci return -EUCLEAN; 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci read_extent_buffer(leaf, &bgi, btrfs_item_ptr_offset(leaf, slot), 6588c2ecf20Sopenharmony_ci sizeof(bgi)); 6598c2ecf20Sopenharmony_ci if (btrfs_stack_block_group_chunk_objectid(&bgi) != 6608c2ecf20Sopenharmony_ci BTRFS_FIRST_CHUNK_TREE_OBJECTID) { 6618c2ecf20Sopenharmony_ci block_group_err(leaf, slot, 6628c2ecf20Sopenharmony_ci "invalid block group chunk objectid, have %llu expect %llu", 6638c2ecf20Sopenharmony_ci btrfs_stack_block_group_chunk_objectid(&bgi), 6648c2ecf20Sopenharmony_ci BTRFS_FIRST_CHUNK_TREE_OBJECTID); 6658c2ecf20Sopenharmony_ci return -EUCLEAN; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci if (btrfs_stack_block_group_used(&bgi) > key->offset) { 6698c2ecf20Sopenharmony_ci block_group_err(leaf, slot, 6708c2ecf20Sopenharmony_ci "invalid block group used, have %llu expect [0, %llu)", 6718c2ecf20Sopenharmony_ci btrfs_stack_block_group_used(&bgi), key->offset); 6728c2ecf20Sopenharmony_ci return -EUCLEAN; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci flags = btrfs_stack_block_group_flags(&bgi); 6768c2ecf20Sopenharmony_ci if (hweight64(flags & BTRFS_BLOCK_GROUP_PROFILE_MASK) > 1) { 6778c2ecf20Sopenharmony_ci block_group_err(leaf, slot, 6788c2ecf20Sopenharmony_ci"invalid profile flags, have 0x%llx (%lu bits set) expect no more than 1 bit set", 6798c2ecf20Sopenharmony_ci flags & BTRFS_BLOCK_GROUP_PROFILE_MASK, 6808c2ecf20Sopenharmony_ci hweight64(flags & BTRFS_BLOCK_GROUP_PROFILE_MASK)); 6818c2ecf20Sopenharmony_ci return -EUCLEAN; 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci type = flags & BTRFS_BLOCK_GROUP_TYPE_MASK; 6858c2ecf20Sopenharmony_ci if (type != BTRFS_BLOCK_GROUP_DATA && 6868c2ecf20Sopenharmony_ci type != BTRFS_BLOCK_GROUP_METADATA && 6878c2ecf20Sopenharmony_ci type != BTRFS_BLOCK_GROUP_SYSTEM && 6888c2ecf20Sopenharmony_ci type != (BTRFS_BLOCK_GROUP_METADATA | 6898c2ecf20Sopenharmony_ci BTRFS_BLOCK_GROUP_DATA)) { 6908c2ecf20Sopenharmony_ci block_group_err(leaf, slot, 6918c2ecf20Sopenharmony_ci"invalid type, have 0x%llx (%lu bits set) expect either 0x%llx, 0x%llx, 0x%llx or 0x%llx", 6928c2ecf20Sopenharmony_ci type, hweight64(type), 6938c2ecf20Sopenharmony_ci BTRFS_BLOCK_GROUP_DATA, BTRFS_BLOCK_GROUP_METADATA, 6948c2ecf20Sopenharmony_ci BTRFS_BLOCK_GROUP_SYSTEM, 6958c2ecf20Sopenharmony_ci BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA); 6968c2ecf20Sopenharmony_ci return -EUCLEAN; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci return 0; 6998c2ecf20Sopenharmony_ci} 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci__printf(4, 5) 7028c2ecf20Sopenharmony_ci__cold 7038c2ecf20Sopenharmony_cistatic void chunk_err(const struct extent_buffer *leaf, 7048c2ecf20Sopenharmony_ci const struct btrfs_chunk *chunk, u64 logical, 7058c2ecf20Sopenharmony_ci const char *fmt, ...) 7068c2ecf20Sopenharmony_ci{ 7078c2ecf20Sopenharmony_ci const struct btrfs_fs_info *fs_info = leaf->fs_info; 7088c2ecf20Sopenharmony_ci bool is_sb; 7098c2ecf20Sopenharmony_ci struct va_format vaf; 7108c2ecf20Sopenharmony_ci va_list args; 7118c2ecf20Sopenharmony_ci int i; 7128c2ecf20Sopenharmony_ci int slot = -1; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci /* Only superblock eb is able to have such small offset */ 7158c2ecf20Sopenharmony_ci is_sb = (leaf->start == BTRFS_SUPER_INFO_OFFSET); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci if (!is_sb) { 7188c2ecf20Sopenharmony_ci /* 7198c2ecf20Sopenharmony_ci * Get the slot number by iterating through all slots, this 7208c2ecf20Sopenharmony_ci * would provide better readability. 7218c2ecf20Sopenharmony_ci */ 7228c2ecf20Sopenharmony_ci for (i = 0; i < btrfs_header_nritems(leaf); i++) { 7238c2ecf20Sopenharmony_ci if (btrfs_item_ptr_offset(leaf, i) == 7248c2ecf20Sopenharmony_ci (unsigned long)chunk) { 7258c2ecf20Sopenharmony_ci slot = i; 7268c2ecf20Sopenharmony_ci break; 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci va_start(args, fmt); 7318c2ecf20Sopenharmony_ci vaf.fmt = fmt; 7328c2ecf20Sopenharmony_ci vaf.va = &args; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci if (is_sb) 7358c2ecf20Sopenharmony_ci btrfs_crit(fs_info, 7368c2ecf20Sopenharmony_ci "corrupt superblock syschunk array: chunk_start=%llu, %pV", 7378c2ecf20Sopenharmony_ci logical, &vaf); 7388c2ecf20Sopenharmony_ci else 7398c2ecf20Sopenharmony_ci btrfs_crit(fs_info, 7408c2ecf20Sopenharmony_ci "corrupt leaf: root=%llu block=%llu slot=%d chunk_start=%llu, %pV", 7418c2ecf20Sopenharmony_ci BTRFS_CHUNK_TREE_OBJECTID, leaf->start, slot, 7428c2ecf20Sopenharmony_ci logical, &vaf); 7438c2ecf20Sopenharmony_ci va_end(args); 7448c2ecf20Sopenharmony_ci} 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci/* 7478c2ecf20Sopenharmony_ci * The common chunk check which could also work on super block sys chunk array. 7488c2ecf20Sopenharmony_ci * 7498c2ecf20Sopenharmony_ci * Return -EUCLEAN if anything is corrupted. 7508c2ecf20Sopenharmony_ci * Return 0 if everything is OK. 7518c2ecf20Sopenharmony_ci */ 7528c2ecf20Sopenharmony_ciint btrfs_check_chunk_valid(struct extent_buffer *leaf, 7538c2ecf20Sopenharmony_ci struct btrfs_chunk *chunk, u64 logical) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci struct btrfs_fs_info *fs_info = leaf->fs_info; 7568c2ecf20Sopenharmony_ci u64 length; 7578c2ecf20Sopenharmony_ci u64 chunk_end; 7588c2ecf20Sopenharmony_ci u64 stripe_len; 7598c2ecf20Sopenharmony_ci u16 num_stripes; 7608c2ecf20Sopenharmony_ci u16 sub_stripes; 7618c2ecf20Sopenharmony_ci u64 type; 7628c2ecf20Sopenharmony_ci u64 features; 7638c2ecf20Sopenharmony_ci bool mixed = false; 7648c2ecf20Sopenharmony_ci int raid_index; 7658c2ecf20Sopenharmony_ci int nparity; 7668c2ecf20Sopenharmony_ci int ncopies; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci length = btrfs_chunk_length(leaf, chunk); 7698c2ecf20Sopenharmony_ci stripe_len = btrfs_chunk_stripe_len(leaf, chunk); 7708c2ecf20Sopenharmony_ci num_stripes = btrfs_chunk_num_stripes(leaf, chunk); 7718c2ecf20Sopenharmony_ci sub_stripes = btrfs_chunk_sub_stripes(leaf, chunk); 7728c2ecf20Sopenharmony_ci type = btrfs_chunk_type(leaf, chunk); 7738c2ecf20Sopenharmony_ci raid_index = btrfs_bg_flags_to_raid_index(type); 7748c2ecf20Sopenharmony_ci ncopies = btrfs_raid_array[raid_index].ncopies; 7758c2ecf20Sopenharmony_ci nparity = btrfs_raid_array[raid_index].nparity; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci if (!num_stripes) { 7788c2ecf20Sopenharmony_ci chunk_err(leaf, chunk, logical, 7798c2ecf20Sopenharmony_ci "invalid chunk num_stripes, have %u", num_stripes); 7808c2ecf20Sopenharmony_ci return -EUCLEAN; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci if (num_stripes < ncopies) { 7838c2ecf20Sopenharmony_ci chunk_err(leaf, chunk, logical, 7848c2ecf20Sopenharmony_ci "invalid chunk num_stripes < ncopies, have %u < %d", 7858c2ecf20Sopenharmony_ci num_stripes, ncopies); 7868c2ecf20Sopenharmony_ci return -EUCLEAN; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci if (nparity && num_stripes == nparity) { 7898c2ecf20Sopenharmony_ci chunk_err(leaf, chunk, logical, 7908c2ecf20Sopenharmony_ci "invalid chunk num_stripes == nparity, have %u == %d", 7918c2ecf20Sopenharmony_ci num_stripes, nparity); 7928c2ecf20Sopenharmony_ci return -EUCLEAN; 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci if (!IS_ALIGNED(logical, fs_info->sectorsize)) { 7958c2ecf20Sopenharmony_ci chunk_err(leaf, chunk, logical, 7968c2ecf20Sopenharmony_ci "invalid chunk logical, have %llu should aligned to %u", 7978c2ecf20Sopenharmony_ci logical, fs_info->sectorsize); 7988c2ecf20Sopenharmony_ci return -EUCLEAN; 7998c2ecf20Sopenharmony_ci } 8008c2ecf20Sopenharmony_ci if (btrfs_chunk_sector_size(leaf, chunk) != fs_info->sectorsize) { 8018c2ecf20Sopenharmony_ci chunk_err(leaf, chunk, logical, 8028c2ecf20Sopenharmony_ci "invalid chunk sectorsize, have %u expect %u", 8038c2ecf20Sopenharmony_ci btrfs_chunk_sector_size(leaf, chunk), 8048c2ecf20Sopenharmony_ci fs_info->sectorsize); 8058c2ecf20Sopenharmony_ci return -EUCLEAN; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci if (!length || !IS_ALIGNED(length, fs_info->sectorsize)) { 8088c2ecf20Sopenharmony_ci chunk_err(leaf, chunk, logical, 8098c2ecf20Sopenharmony_ci "invalid chunk length, have %llu", length); 8108c2ecf20Sopenharmony_ci return -EUCLEAN; 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci if (unlikely(check_add_overflow(logical, length, &chunk_end))) { 8138c2ecf20Sopenharmony_ci chunk_err(leaf, chunk, logical, 8148c2ecf20Sopenharmony_ci"invalid chunk logical start and length, have logical start %llu length %llu", 8158c2ecf20Sopenharmony_ci logical, length); 8168c2ecf20Sopenharmony_ci return -EUCLEAN; 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci if (!is_power_of_2(stripe_len) || stripe_len != BTRFS_STRIPE_LEN) { 8198c2ecf20Sopenharmony_ci chunk_err(leaf, chunk, logical, 8208c2ecf20Sopenharmony_ci "invalid chunk stripe length: %llu", 8218c2ecf20Sopenharmony_ci stripe_len); 8228c2ecf20Sopenharmony_ci return -EUCLEAN; 8238c2ecf20Sopenharmony_ci } 8248c2ecf20Sopenharmony_ci if (~(BTRFS_BLOCK_GROUP_TYPE_MASK | BTRFS_BLOCK_GROUP_PROFILE_MASK) & 8258c2ecf20Sopenharmony_ci type) { 8268c2ecf20Sopenharmony_ci chunk_err(leaf, chunk, logical, 8278c2ecf20Sopenharmony_ci "unrecognized chunk type: 0x%llx", 8288c2ecf20Sopenharmony_ci ~(BTRFS_BLOCK_GROUP_TYPE_MASK | 8298c2ecf20Sopenharmony_ci BTRFS_BLOCK_GROUP_PROFILE_MASK) & 8308c2ecf20Sopenharmony_ci btrfs_chunk_type(leaf, chunk)); 8318c2ecf20Sopenharmony_ci return -EUCLEAN; 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci if (!has_single_bit_set(type & BTRFS_BLOCK_GROUP_PROFILE_MASK) && 8358c2ecf20Sopenharmony_ci (type & BTRFS_BLOCK_GROUP_PROFILE_MASK) != 0) { 8368c2ecf20Sopenharmony_ci chunk_err(leaf, chunk, logical, 8378c2ecf20Sopenharmony_ci "invalid chunk profile flag: 0x%llx, expect 0 or 1 bit set", 8388c2ecf20Sopenharmony_ci type & BTRFS_BLOCK_GROUP_PROFILE_MASK); 8398c2ecf20Sopenharmony_ci return -EUCLEAN; 8408c2ecf20Sopenharmony_ci } 8418c2ecf20Sopenharmony_ci if ((type & BTRFS_BLOCK_GROUP_TYPE_MASK) == 0) { 8428c2ecf20Sopenharmony_ci chunk_err(leaf, chunk, logical, 8438c2ecf20Sopenharmony_ci "missing chunk type flag, have 0x%llx one bit must be set in 0x%llx", 8448c2ecf20Sopenharmony_ci type, BTRFS_BLOCK_GROUP_TYPE_MASK); 8458c2ecf20Sopenharmony_ci return -EUCLEAN; 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci if ((type & BTRFS_BLOCK_GROUP_SYSTEM) && 8498c2ecf20Sopenharmony_ci (type & (BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA))) { 8508c2ecf20Sopenharmony_ci chunk_err(leaf, chunk, logical, 8518c2ecf20Sopenharmony_ci "system chunk with data or metadata type: 0x%llx", 8528c2ecf20Sopenharmony_ci type); 8538c2ecf20Sopenharmony_ci return -EUCLEAN; 8548c2ecf20Sopenharmony_ci } 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci features = btrfs_super_incompat_flags(fs_info->super_copy); 8578c2ecf20Sopenharmony_ci if (features & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS) 8588c2ecf20Sopenharmony_ci mixed = true; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci if (!mixed) { 8618c2ecf20Sopenharmony_ci if ((type & BTRFS_BLOCK_GROUP_METADATA) && 8628c2ecf20Sopenharmony_ci (type & BTRFS_BLOCK_GROUP_DATA)) { 8638c2ecf20Sopenharmony_ci chunk_err(leaf, chunk, logical, 8648c2ecf20Sopenharmony_ci "mixed chunk type in non-mixed mode: 0x%llx", type); 8658c2ecf20Sopenharmony_ci return -EUCLEAN; 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if ((type & BTRFS_BLOCK_GROUP_RAID10 && sub_stripes != 2) || 8708c2ecf20Sopenharmony_ci (type & BTRFS_BLOCK_GROUP_RAID1 && num_stripes != 2) || 8718c2ecf20Sopenharmony_ci (type & BTRFS_BLOCK_GROUP_RAID5 && num_stripes < 2) || 8728c2ecf20Sopenharmony_ci (type & BTRFS_BLOCK_GROUP_RAID6 && num_stripes < 3) || 8738c2ecf20Sopenharmony_ci (type & BTRFS_BLOCK_GROUP_DUP && num_stripes != 2) || 8748c2ecf20Sopenharmony_ci ((type & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0 && num_stripes != 1)) { 8758c2ecf20Sopenharmony_ci chunk_err(leaf, chunk, logical, 8768c2ecf20Sopenharmony_ci "invalid num_stripes:sub_stripes %u:%u for profile %llu", 8778c2ecf20Sopenharmony_ci num_stripes, sub_stripes, 8788c2ecf20Sopenharmony_ci type & BTRFS_BLOCK_GROUP_PROFILE_MASK); 8798c2ecf20Sopenharmony_ci return -EUCLEAN; 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci return 0; 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci/* 8868c2ecf20Sopenharmony_ci * Enhanced version of chunk item checker. 8878c2ecf20Sopenharmony_ci * 8888c2ecf20Sopenharmony_ci * The common btrfs_check_chunk_valid() doesn't check item size since it needs 8898c2ecf20Sopenharmony_ci * to work on super block sys_chunk_array which doesn't have full item ptr. 8908c2ecf20Sopenharmony_ci */ 8918c2ecf20Sopenharmony_cistatic int check_leaf_chunk_item(struct extent_buffer *leaf, 8928c2ecf20Sopenharmony_ci struct btrfs_chunk *chunk, 8938c2ecf20Sopenharmony_ci struct btrfs_key *key, int slot) 8948c2ecf20Sopenharmony_ci{ 8958c2ecf20Sopenharmony_ci int num_stripes; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci if (btrfs_item_size_nr(leaf, slot) < sizeof(struct btrfs_chunk)) { 8988c2ecf20Sopenharmony_ci chunk_err(leaf, chunk, key->offset, 8998c2ecf20Sopenharmony_ci "invalid chunk item size: have %u expect [%zu, %u)", 9008c2ecf20Sopenharmony_ci btrfs_item_size_nr(leaf, slot), 9018c2ecf20Sopenharmony_ci sizeof(struct btrfs_chunk), 9028c2ecf20Sopenharmony_ci BTRFS_LEAF_DATA_SIZE(leaf->fs_info)); 9038c2ecf20Sopenharmony_ci return -EUCLEAN; 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci num_stripes = btrfs_chunk_num_stripes(leaf, chunk); 9078c2ecf20Sopenharmony_ci /* Let btrfs_check_chunk_valid() handle this error type */ 9088c2ecf20Sopenharmony_ci if (num_stripes == 0) 9098c2ecf20Sopenharmony_ci goto out; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci if (btrfs_chunk_item_size(num_stripes) != 9128c2ecf20Sopenharmony_ci btrfs_item_size_nr(leaf, slot)) { 9138c2ecf20Sopenharmony_ci chunk_err(leaf, chunk, key->offset, 9148c2ecf20Sopenharmony_ci "invalid chunk item size: have %u expect %lu", 9158c2ecf20Sopenharmony_ci btrfs_item_size_nr(leaf, slot), 9168c2ecf20Sopenharmony_ci btrfs_chunk_item_size(num_stripes)); 9178c2ecf20Sopenharmony_ci return -EUCLEAN; 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ciout: 9208c2ecf20Sopenharmony_ci return btrfs_check_chunk_valid(leaf, chunk, key->offset); 9218c2ecf20Sopenharmony_ci} 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci__printf(3, 4) 9248c2ecf20Sopenharmony_ci__cold 9258c2ecf20Sopenharmony_cistatic void dev_item_err(const struct extent_buffer *eb, int slot, 9268c2ecf20Sopenharmony_ci const char *fmt, ...) 9278c2ecf20Sopenharmony_ci{ 9288c2ecf20Sopenharmony_ci struct btrfs_key key; 9298c2ecf20Sopenharmony_ci struct va_format vaf; 9308c2ecf20Sopenharmony_ci va_list args; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci btrfs_item_key_to_cpu(eb, &key, slot); 9338c2ecf20Sopenharmony_ci va_start(args, fmt); 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci vaf.fmt = fmt; 9368c2ecf20Sopenharmony_ci vaf.va = &args; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci btrfs_crit(eb->fs_info, 9398c2ecf20Sopenharmony_ci "corrupt %s: root=%llu block=%llu slot=%d devid=%llu %pV", 9408c2ecf20Sopenharmony_ci btrfs_header_level(eb) == 0 ? "leaf" : "node", 9418c2ecf20Sopenharmony_ci btrfs_header_owner(eb), btrfs_header_bytenr(eb), slot, 9428c2ecf20Sopenharmony_ci key.objectid, &vaf); 9438c2ecf20Sopenharmony_ci va_end(args); 9448c2ecf20Sopenharmony_ci} 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_cistatic int check_dev_item(struct extent_buffer *leaf, 9478c2ecf20Sopenharmony_ci struct btrfs_key *key, int slot) 9488c2ecf20Sopenharmony_ci{ 9498c2ecf20Sopenharmony_ci struct btrfs_dev_item *ditem; 9508c2ecf20Sopenharmony_ci const u32 item_size = btrfs_item_size_nr(leaf, slot); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci if (key->objectid != BTRFS_DEV_ITEMS_OBJECTID) { 9538c2ecf20Sopenharmony_ci dev_item_err(leaf, slot, 9548c2ecf20Sopenharmony_ci "invalid objectid: has=%llu expect=%llu", 9558c2ecf20Sopenharmony_ci key->objectid, BTRFS_DEV_ITEMS_OBJECTID); 9568c2ecf20Sopenharmony_ci return -EUCLEAN; 9578c2ecf20Sopenharmony_ci } 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci if (unlikely(item_size != sizeof(*ditem))) { 9608c2ecf20Sopenharmony_ci dev_item_err(leaf, slot, "invalid item size: has %u expect %zu", 9618c2ecf20Sopenharmony_ci item_size, sizeof(*ditem)); 9628c2ecf20Sopenharmony_ci return -EUCLEAN; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci ditem = btrfs_item_ptr(leaf, slot, struct btrfs_dev_item); 9668c2ecf20Sopenharmony_ci if (btrfs_device_id(leaf, ditem) != key->offset) { 9678c2ecf20Sopenharmony_ci dev_item_err(leaf, slot, 9688c2ecf20Sopenharmony_ci "devid mismatch: key has=%llu item has=%llu", 9698c2ecf20Sopenharmony_ci key->offset, btrfs_device_id(leaf, ditem)); 9708c2ecf20Sopenharmony_ci return -EUCLEAN; 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci /* 9748c2ecf20Sopenharmony_ci * For device total_bytes, we don't have reliable way to check it, as 9758c2ecf20Sopenharmony_ci * it can be 0 for device removal. Device size check can only be done 9768c2ecf20Sopenharmony_ci * by dev extents check. 9778c2ecf20Sopenharmony_ci */ 9788c2ecf20Sopenharmony_ci if (btrfs_device_bytes_used(leaf, ditem) > 9798c2ecf20Sopenharmony_ci btrfs_device_total_bytes(leaf, ditem)) { 9808c2ecf20Sopenharmony_ci dev_item_err(leaf, slot, 9818c2ecf20Sopenharmony_ci "invalid bytes used: have %llu expect [0, %llu]", 9828c2ecf20Sopenharmony_ci btrfs_device_bytes_used(leaf, ditem), 9838c2ecf20Sopenharmony_ci btrfs_device_total_bytes(leaf, ditem)); 9848c2ecf20Sopenharmony_ci return -EUCLEAN; 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci /* 9878c2ecf20Sopenharmony_ci * Remaining members like io_align/type/gen/dev_group aren't really 9888c2ecf20Sopenharmony_ci * utilized. Skip them to make later usage of them easier. 9898c2ecf20Sopenharmony_ci */ 9908c2ecf20Sopenharmony_ci return 0; 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_cistatic int check_inode_item(struct extent_buffer *leaf, 9948c2ecf20Sopenharmony_ci struct btrfs_key *key, int slot) 9958c2ecf20Sopenharmony_ci{ 9968c2ecf20Sopenharmony_ci struct btrfs_fs_info *fs_info = leaf->fs_info; 9978c2ecf20Sopenharmony_ci struct btrfs_inode_item *iitem; 9988c2ecf20Sopenharmony_ci u64 super_gen = btrfs_super_generation(fs_info->super_copy); 9998c2ecf20Sopenharmony_ci u32 valid_mask = (S_IFMT | S_ISUID | S_ISGID | S_ISVTX | 0777); 10008c2ecf20Sopenharmony_ci const u32 item_size = btrfs_item_size_nr(leaf, slot); 10018c2ecf20Sopenharmony_ci u32 mode; 10028c2ecf20Sopenharmony_ci int ret; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci ret = check_inode_key(leaf, key, slot); 10058c2ecf20Sopenharmony_ci if (ret < 0) 10068c2ecf20Sopenharmony_ci return ret; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci if (unlikely(item_size != sizeof(*iitem))) { 10098c2ecf20Sopenharmony_ci generic_err(leaf, slot, "invalid item size: has %u expect %zu", 10108c2ecf20Sopenharmony_ci item_size, sizeof(*iitem)); 10118c2ecf20Sopenharmony_ci return -EUCLEAN; 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci iitem = btrfs_item_ptr(leaf, slot, struct btrfs_inode_item); 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci /* Here we use super block generation + 1 to handle log tree */ 10178c2ecf20Sopenharmony_ci if (btrfs_inode_generation(leaf, iitem) > super_gen + 1) { 10188c2ecf20Sopenharmony_ci inode_item_err(leaf, slot, 10198c2ecf20Sopenharmony_ci "invalid inode generation: has %llu expect (0, %llu]", 10208c2ecf20Sopenharmony_ci btrfs_inode_generation(leaf, iitem), 10218c2ecf20Sopenharmony_ci super_gen + 1); 10228c2ecf20Sopenharmony_ci return -EUCLEAN; 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci /* Note for ROOT_TREE_DIR_ITEM, mkfs could set its transid 0 */ 10258c2ecf20Sopenharmony_ci if (btrfs_inode_transid(leaf, iitem) > super_gen + 1) { 10268c2ecf20Sopenharmony_ci inode_item_err(leaf, slot, 10278c2ecf20Sopenharmony_ci "invalid inode transid: has %llu expect [0, %llu]", 10288c2ecf20Sopenharmony_ci btrfs_inode_transid(leaf, iitem), super_gen + 1); 10298c2ecf20Sopenharmony_ci return -EUCLEAN; 10308c2ecf20Sopenharmony_ci } 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci /* 10338c2ecf20Sopenharmony_ci * For size and nbytes it's better not to be too strict, as for dir 10348c2ecf20Sopenharmony_ci * item its size/nbytes can easily get wrong, but doesn't affect 10358c2ecf20Sopenharmony_ci * anything in the fs. So here we skip the check. 10368c2ecf20Sopenharmony_ci */ 10378c2ecf20Sopenharmony_ci mode = btrfs_inode_mode(leaf, iitem); 10388c2ecf20Sopenharmony_ci if (mode & ~valid_mask) { 10398c2ecf20Sopenharmony_ci inode_item_err(leaf, slot, 10408c2ecf20Sopenharmony_ci "unknown mode bit detected: 0x%x", 10418c2ecf20Sopenharmony_ci mode & ~valid_mask); 10428c2ecf20Sopenharmony_ci return -EUCLEAN; 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci /* 10468c2ecf20Sopenharmony_ci * S_IFMT is not bit mapped so we can't completely rely on 10478c2ecf20Sopenharmony_ci * is_power_of_2/has_single_bit_set, but it can save us from checking 10488c2ecf20Sopenharmony_ci * FIFO/CHR/DIR/REG. Only needs to check BLK, LNK and SOCKS 10498c2ecf20Sopenharmony_ci */ 10508c2ecf20Sopenharmony_ci if (!has_single_bit_set(mode & S_IFMT)) { 10518c2ecf20Sopenharmony_ci if (!S_ISLNK(mode) && !S_ISBLK(mode) && !S_ISSOCK(mode)) { 10528c2ecf20Sopenharmony_ci inode_item_err(leaf, slot, 10538c2ecf20Sopenharmony_ci "invalid mode: has 0%o expect valid S_IF* bit(s)", 10548c2ecf20Sopenharmony_ci mode & S_IFMT); 10558c2ecf20Sopenharmony_ci return -EUCLEAN; 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci if (S_ISDIR(mode) && btrfs_inode_nlink(leaf, iitem) > 1) { 10598c2ecf20Sopenharmony_ci inode_item_err(leaf, slot, 10608c2ecf20Sopenharmony_ci "invalid nlink: has %u expect no more than 1 for dir", 10618c2ecf20Sopenharmony_ci btrfs_inode_nlink(leaf, iitem)); 10628c2ecf20Sopenharmony_ci return -EUCLEAN; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci if (btrfs_inode_flags(leaf, iitem) & ~BTRFS_INODE_FLAG_MASK) { 10658c2ecf20Sopenharmony_ci inode_item_err(leaf, slot, 10668c2ecf20Sopenharmony_ci "unknown flags detected: 0x%llx", 10678c2ecf20Sopenharmony_ci btrfs_inode_flags(leaf, iitem) & 10688c2ecf20Sopenharmony_ci ~BTRFS_INODE_FLAG_MASK); 10698c2ecf20Sopenharmony_ci return -EUCLEAN; 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci return 0; 10728c2ecf20Sopenharmony_ci} 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_cistatic int check_root_item(struct extent_buffer *leaf, struct btrfs_key *key, 10758c2ecf20Sopenharmony_ci int slot) 10768c2ecf20Sopenharmony_ci{ 10778c2ecf20Sopenharmony_ci struct btrfs_fs_info *fs_info = leaf->fs_info; 10788c2ecf20Sopenharmony_ci struct btrfs_root_item ri = { 0 }; 10798c2ecf20Sopenharmony_ci const u64 valid_root_flags = BTRFS_ROOT_SUBVOL_RDONLY | 10808c2ecf20Sopenharmony_ci BTRFS_ROOT_SUBVOL_DEAD; 10818c2ecf20Sopenharmony_ci int ret; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci ret = check_root_key(leaf, key, slot); 10848c2ecf20Sopenharmony_ci if (ret < 0) 10858c2ecf20Sopenharmony_ci return ret; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci if (btrfs_item_size_nr(leaf, slot) != sizeof(ri) && 10888c2ecf20Sopenharmony_ci btrfs_item_size_nr(leaf, slot) != btrfs_legacy_root_item_size()) { 10898c2ecf20Sopenharmony_ci generic_err(leaf, slot, 10908c2ecf20Sopenharmony_ci "invalid root item size, have %u expect %zu or %u", 10918c2ecf20Sopenharmony_ci btrfs_item_size_nr(leaf, slot), sizeof(ri), 10928c2ecf20Sopenharmony_ci btrfs_legacy_root_item_size()); 10938c2ecf20Sopenharmony_ci return -EUCLEAN; 10948c2ecf20Sopenharmony_ci } 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci /* 10978c2ecf20Sopenharmony_ci * For legacy root item, the members starting at generation_v2 will be 10988c2ecf20Sopenharmony_ci * all filled with 0. 10998c2ecf20Sopenharmony_ci * And since we allow geneartion_v2 as 0, it will still pass the check. 11008c2ecf20Sopenharmony_ci */ 11018c2ecf20Sopenharmony_ci read_extent_buffer(leaf, &ri, btrfs_item_ptr_offset(leaf, slot), 11028c2ecf20Sopenharmony_ci btrfs_item_size_nr(leaf, slot)); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci /* Generation related */ 11058c2ecf20Sopenharmony_ci if (btrfs_root_generation(&ri) > 11068c2ecf20Sopenharmony_ci btrfs_super_generation(fs_info->super_copy) + 1) { 11078c2ecf20Sopenharmony_ci generic_err(leaf, slot, 11088c2ecf20Sopenharmony_ci "invalid root generation, have %llu expect (0, %llu]", 11098c2ecf20Sopenharmony_ci btrfs_root_generation(&ri), 11108c2ecf20Sopenharmony_ci btrfs_super_generation(fs_info->super_copy) + 1); 11118c2ecf20Sopenharmony_ci return -EUCLEAN; 11128c2ecf20Sopenharmony_ci } 11138c2ecf20Sopenharmony_ci if (btrfs_root_generation_v2(&ri) > 11148c2ecf20Sopenharmony_ci btrfs_super_generation(fs_info->super_copy) + 1) { 11158c2ecf20Sopenharmony_ci generic_err(leaf, slot, 11168c2ecf20Sopenharmony_ci "invalid root v2 generation, have %llu expect (0, %llu]", 11178c2ecf20Sopenharmony_ci btrfs_root_generation_v2(&ri), 11188c2ecf20Sopenharmony_ci btrfs_super_generation(fs_info->super_copy) + 1); 11198c2ecf20Sopenharmony_ci return -EUCLEAN; 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci if (btrfs_root_last_snapshot(&ri) > 11228c2ecf20Sopenharmony_ci btrfs_super_generation(fs_info->super_copy) + 1) { 11238c2ecf20Sopenharmony_ci generic_err(leaf, slot, 11248c2ecf20Sopenharmony_ci "invalid root last_snapshot, have %llu expect (0, %llu]", 11258c2ecf20Sopenharmony_ci btrfs_root_last_snapshot(&ri), 11268c2ecf20Sopenharmony_ci btrfs_super_generation(fs_info->super_copy) + 1); 11278c2ecf20Sopenharmony_ci return -EUCLEAN; 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci /* Alignment and level check */ 11318c2ecf20Sopenharmony_ci if (!IS_ALIGNED(btrfs_root_bytenr(&ri), fs_info->sectorsize)) { 11328c2ecf20Sopenharmony_ci generic_err(leaf, slot, 11338c2ecf20Sopenharmony_ci "invalid root bytenr, have %llu expect to be aligned to %u", 11348c2ecf20Sopenharmony_ci btrfs_root_bytenr(&ri), fs_info->sectorsize); 11358c2ecf20Sopenharmony_ci return -EUCLEAN; 11368c2ecf20Sopenharmony_ci } 11378c2ecf20Sopenharmony_ci if (btrfs_root_level(&ri) >= BTRFS_MAX_LEVEL) { 11388c2ecf20Sopenharmony_ci generic_err(leaf, slot, 11398c2ecf20Sopenharmony_ci "invalid root level, have %u expect [0, %u]", 11408c2ecf20Sopenharmony_ci btrfs_root_level(&ri), BTRFS_MAX_LEVEL - 1); 11418c2ecf20Sopenharmony_ci return -EUCLEAN; 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci if (ri.drop_level >= BTRFS_MAX_LEVEL) { 11448c2ecf20Sopenharmony_ci generic_err(leaf, slot, 11458c2ecf20Sopenharmony_ci "invalid root level, have %u expect [0, %u]", 11468c2ecf20Sopenharmony_ci ri.drop_level, BTRFS_MAX_LEVEL - 1); 11478c2ecf20Sopenharmony_ci return -EUCLEAN; 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci /* Flags check */ 11518c2ecf20Sopenharmony_ci if (btrfs_root_flags(&ri) & ~valid_root_flags) { 11528c2ecf20Sopenharmony_ci generic_err(leaf, slot, 11538c2ecf20Sopenharmony_ci "invalid root flags, have 0x%llx expect mask 0x%llx", 11548c2ecf20Sopenharmony_ci btrfs_root_flags(&ri), valid_root_flags); 11558c2ecf20Sopenharmony_ci return -EUCLEAN; 11568c2ecf20Sopenharmony_ci } 11578c2ecf20Sopenharmony_ci return 0; 11588c2ecf20Sopenharmony_ci} 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci__printf(3,4) 11618c2ecf20Sopenharmony_ci__cold 11628c2ecf20Sopenharmony_cistatic void extent_err(const struct extent_buffer *eb, int slot, 11638c2ecf20Sopenharmony_ci const char *fmt, ...) 11648c2ecf20Sopenharmony_ci{ 11658c2ecf20Sopenharmony_ci struct btrfs_key key; 11668c2ecf20Sopenharmony_ci struct va_format vaf; 11678c2ecf20Sopenharmony_ci va_list args; 11688c2ecf20Sopenharmony_ci u64 bytenr; 11698c2ecf20Sopenharmony_ci u64 len; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci btrfs_item_key_to_cpu(eb, &key, slot); 11728c2ecf20Sopenharmony_ci bytenr = key.objectid; 11738c2ecf20Sopenharmony_ci if (key.type == BTRFS_METADATA_ITEM_KEY || 11748c2ecf20Sopenharmony_ci key.type == BTRFS_TREE_BLOCK_REF_KEY || 11758c2ecf20Sopenharmony_ci key.type == BTRFS_SHARED_BLOCK_REF_KEY) 11768c2ecf20Sopenharmony_ci len = eb->fs_info->nodesize; 11778c2ecf20Sopenharmony_ci else 11788c2ecf20Sopenharmony_ci len = key.offset; 11798c2ecf20Sopenharmony_ci va_start(args, fmt); 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci vaf.fmt = fmt; 11828c2ecf20Sopenharmony_ci vaf.va = &args; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci btrfs_crit(eb->fs_info, 11858c2ecf20Sopenharmony_ci "corrupt %s: block=%llu slot=%d extent bytenr=%llu len=%llu %pV", 11868c2ecf20Sopenharmony_ci btrfs_header_level(eb) == 0 ? "leaf" : "node", 11878c2ecf20Sopenharmony_ci eb->start, slot, bytenr, len, &vaf); 11888c2ecf20Sopenharmony_ci va_end(args); 11898c2ecf20Sopenharmony_ci} 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_cistatic int check_extent_item(struct extent_buffer *leaf, 11928c2ecf20Sopenharmony_ci struct btrfs_key *key, int slot) 11938c2ecf20Sopenharmony_ci{ 11948c2ecf20Sopenharmony_ci struct btrfs_fs_info *fs_info = leaf->fs_info; 11958c2ecf20Sopenharmony_ci struct btrfs_extent_item *ei; 11968c2ecf20Sopenharmony_ci bool is_tree_block = false; 11978c2ecf20Sopenharmony_ci unsigned long ptr; /* Current pointer inside inline refs */ 11988c2ecf20Sopenharmony_ci unsigned long end; /* Extent item end */ 11998c2ecf20Sopenharmony_ci const u32 item_size = btrfs_item_size_nr(leaf, slot); 12008c2ecf20Sopenharmony_ci u64 flags; 12018c2ecf20Sopenharmony_ci u64 generation; 12028c2ecf20Sopenharmony_ci u64 total_refs; /* Total refs in btrfs_extent_item */ 12038c2ecf20Sopenharmony_ci u64 inline_refs = 0; /* found total inline refs */ 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci if (key->type == BTRFS_METADATA_ITEM_KEY && 12068c2ecf20Sopenharmony_ci !btrfs_fs_incompat(fs_info, SKINNY_METADATA)) { 12078c2ecf20Sopenharmony_ci generic_err(leaf, slot, 12088c2ecf20Sopenharmony_ci"invalid key type, METADATA_ITEM type invalid when SKINNY_METADATA feature disabled"); 12098c2ecf20Sopenharmony_ci return -EUCLEAN; 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci /* key->objectid is the bytenr for both key types */ 12128c2ecf20Sopenharmony_ci if (!IS_ALIGNED(key->objectid, fs_info->sectorsize)) { 12138c2ecf20Sopenharmony_ci generic_err(leaf, slot, 12148c2ecf20Sopenharmony_ci "invalid key objectid, have %llu expect to be aligned to %u", 12158c2ecf20Sopenharmony_ci key->objectid, fs_info->sectorsize); 12168c2ecf20Sopenharmony_ci return -EUCLEAN; 12178c2ecf20Sopenharmony_ci } 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci /* key->offset is tree level for METADATA_ITEM_KEY */ 12208c2ecf20Sopenharmony_ci if (key->type == BTRFS_METADATA_ITEM_KEY && 12218c2ecf20Sopenharmony_ci key->offset >= BTRFS_MAX_LEVEL) { 12228c2ecf20Sopenharmony_ci extent_err(leaf, slot, 12238c2ecf20Sopenharmony_ci "invalid tree level, have %llu expect [0, %u]", 12248c2ecf20Sopenharmony_ci key->offset, BTRFS_MAX_LEVEL - 1); 12258c2ecf20Sopenharmony_ci return -EUCLEAN; 12268c2ecf20Sopenharmony_ci } 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci /* 12298c2ecf20Sopenharmony_ci * EXTENT/METADATA_ITEM consists of: 12308c2ecf20Sopenharmony_ci * 1) One btrfs_extent_item 12318c2ecf20Sopenharmony_ci * Records the total refs, type and generation of the extent. 12328c2ecf20Sopenharmony_ci * 12338c2ecf20Sopenharmony_ci * 2) One btrfs_tree_block_info (for EXTENT_ITEM and tree backref only) 12348c2ecf20Sopenharmony_ci * Records the first key and level of the tree block. 12358c2ecf20Sopenharmony_ci * 12368c2ecf20Sopenharmony_ci * 2) Zero or more btrfs_extent_inline_ref(s) 12378c2ecf20Sopenharmony_ci * Each inline ref has one btrfs_extent_inline_ref shows: 12388c2ecf20Sopenharmony_ci * 2.1) The ref type, one of the 4 12398c2ecf20Sopenharmony_ci * TREE_BLOCK_REF Tree block only 12408c2ecf20Sopenharmony_ci * SHARED_BLOCK_REF Tree block only 12418c2ecf20Sopenharmony_ci * EXTENT_DATA_REF Data only 12428c2ecf20Sopenharmony_ci * SHARED_DATA_REF Data only 12438c2ecf20Sopenharmony_ci * 2.2) Ref type specific data 12448c2ecf20Sopenharmony_ci * Either using btrfs_extent_inline_ref::offset, or specific 12458c2ecf20Sopenharmony_ci * data structure. 12468c2ecf20Sopenharmony_ci */ 12478c2ecf20Sopenharmony_ci if (item_size < sizeof(*ei)) { 12488c2ecf20Sopenharmony_ci extent_err(leaf, slot, 12498c2ecf20Sopenharmony_ci "invalid item size, have %u expect [%zu, %u)", 12508c2ecf20Sopenharmony_ci item_size, sizeof(*ei), 12518c2ecf20Sopenharmony_ci BTRFS_LEAF_DATA_SIZE(fs_info)); 12528c2ecf20Sopenharmony_ci return -EUCLEAN; 12538c2ecf20Sopenharmony_ci } 12548c2ecf20Sopenharmony_ci end = item_size + btrfs_item_ptr_offset(leaf, slot); 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci /* Checks against extent_item */ 12578c2ecf20Sopenharmony_ci ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item); 12588c2ecf20Sopenharmony_ci flags = btrfs_extent_flags(leaf, ei); 12598c2ecf20Sopenharmony_ci total_refs = btrfs_extent_refs(leaf, ei); 12608c2ecf20Sopenharmony_ci generation = btrfs_extent_generation(leaf, ei); 12618c2ecf20Sopenharmony_ci if (generation > btrfs_super_generation(fs_info->super_copy) + 1) { 12628c2ecf20Sopenharmony_ci extent_err(leaf, slot, 12638c2ecf20Sopenharmony_ci "invalid generation, have %llu expect (0, %llu]", 12648c2ecf20Sopenharmony_ci generation, 12658c2ecf20Sopenharmony_ci btrfs_super_generation(fs_info->super_copy) + 1); 12668c2ecf20Sopenharmony_ci return -EUCLEAN; 12678c2ecf20Sopenharmony_ci } 12688c2ecf20Sopenharmony_ci if (!has_single_bit_set(flags & (BTRFS_EXTENT_FLAG_DATA | 12698c2ecf20Sopenharmony_ci BTRFS_EXTENT_FLAG_TREE_BLOCK))) { 12708c2ecf20Sopenharmony_ci extent_err(leaf, slot, 12718c2ecf20Sopenharmony_ci "invalid extent flag, have 0x%llx expect 1 bit set in 0x%llx", 12728c2ecf20Sopenharmony_ci flags, BTRFS_EXTENT_FLAG_DATA | 12738c2ecf20Sopenharmony_ci BTRFS_EXTENT_FLAG_TREE_BLOCK); 12748c2ecf20Sopenharmony_ci return -EUCLEAN; 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci is_tree_block = !!(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK); 12778c2ecf20Sopenharmony_ci if (is_tree_block) { 12788c2ecf20Sopenharmony_ci if (key->type == BTRFS_EXTENT_ITEM_KEY && 12798c2ecf20Sopenharmony_ci key->offset != fs_info->nodesize) { 12808c2ecf20Sopenharmony_ci extent_err(leaf, slot, 12818c2ecf20Sopenharmony_ci "invalid extent length, have %llu expect %u", 12828c2ecf20Sopenharmony_ci key->offset, fs_info->nodesize); 12838c2ecf20Sopenharmony_ci return -EUCLEAN; 12848c2ecf20Sopenharmony_ci } 12858c2ecf20Sopenharmony_ci } else { 12868c2ecf20Sopenharmony_ci if (key->type != BTRFS_EXTENT_ITEM_KEY) { 12878c2ecf20Sopenharmony_ci extent_err(leaf, slot, 12888c2ecf20Sopenharmony_ci "invalid key type, have %u expect %u for data backref", 12898c2ecf20Sopenharmony_ci key->type, BTRFS_EXTENT_ITEM_KEY); 12908c2ecf20Sopenharmony_ci return -EUCLEAN; 12918c2ecf20Sopenharmony_ci } 12928c2ecf20Sopenharmony_ci if (!IS_ALIGNED(key->offset, fs_info->sectorsize)) { 12938c2ecf20Sopenharmony_ci extent_err(leaf, slot, 12948c2ecf20Sopenharmony_ci "invalid extent length, have %llu expect aligned to %u", 12958c2ecf20Sopenharmony_ci key->offset, fs_info->sectorsize); 12968c2ecf20Sopenharmony_ci return -EUCLEAN; 12978c2ecf20Sopenharmony_ci } 12988c2ecf20Sopenharmony_ci } 12998c2ecf20Sopenharmony_ci ptr = (unsigned long)(struct btrfs_extent_item *)(ei + 1); 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci /* Check the special case of btrfs_tree_block_info */ 13028c2ecf20Sopenharmony_ci if (is_tree_block && key->type != BTRFS_METADATA_ITEM_KEY) { 13038c2ecf20Sopenharmony_ci struct btrfs_tree_block_info *info; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci info = (struct btrfs_tree_block_info *)ptr; 13068c2ecf20Sopenharmony_ci if (btrfs_tree_block_level(leaf, info) >= BTRFS_MAX_LEVEL) { 13078c2ecf20Sopenharmony_ci extent_err(leaf, slot, 13088c2ecf20Sopenharmony_ci "invalid tree block info level, have %u expect [0, %u]", 13098c2ecf20Sopenharmony_ci btrfs_tree_block_level(leaf, info), 13108c2ecf20Sopenharmony_ci BTRFS_MAX_LEVEL - 1); 13118c2ecf20Sopenharmony_ci return -EUCLEAN; 13128c2ecf20Sopenharmony_ci } 13138c2ecf20Sopenharmony_ci ptr = (unsigned long)(struct btrfs_tree_block_info *)(info + 1); 13148c2ecf20Sopenharmony_ci } 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci /* Check inline refs */ 13178c2ecf20Sopenharmony_ci while (ptr < end) { 13188c2ecf20Sopenharmony_ci struct btrfs_extent_inline_ref *iref; 13198c2ecf20Sopenharmony_ci struct btrfs_extent_data_ref *dref; 13208c2ecf20Sopenharmony_ci struct btrfs_shared_data_ref *sref; 13218c2ecf20Sopenharmony_ci u64 dref_offset; 13228c2ecf20Sopenharmony_ci u64 inline_offset; 13238c2ecf20Sopenharmony_ci u8 inline_type; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci if (ptr + sizeof(*iref) > end) { 13268c2ecf20Sopenharmony_ci extent_err(leaf, slot, 13278c2ecf20Sopenharmony_ci"inline ref item overflows extent item, ptr %lu iref size %zu end %lu", 13288c2ecf20Sopenharmony_ci ptr, sizeof(*iref), end); 13298c2ecf20Sopenharmony_ci return -EUCLEAN; 13308c2ecf20Sopenharmony_ci } 13318c2ecf20Sopenharmony_ci iref = (struct btrfs_extent_inline_ref *)ptr; 13328c2ecf20Sopenharmony_ci inline_type = btrfs_extent_inline_ref_type(leaf, iref); 13338c2ecf20Sopenharmony_ci inline_offset = btrfs_extent_inline_ref_offset(leaf, iref); 13348c2ecf20Sopenharmony_ci if (ptr + btrfs_extent_inline_ref_size(inline_type) > end) { 13358c2ecf20Sopenharmony_ci extent_err(leaf, slot, 13368c2ecf20Sopenharmony_ci"inline ref item overflows extent item, ptr %lu iref size %u end %lu", 13378c2ecf20Sopenharmony_ci ptr, btrfs_extent_inline_ref_size(inline_type), end); 13388c2ecf20Sopenharmony_ci return -EUCLEAN; 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci switch (inline_type) { 13428c2ecf20Sopenharmony_ci /* inline_offset is subvolid of the owner, no need to check */ 13438c2ecf20Sopenharmony_ci case BTRFS_TREE_BLOCK_REF_KEY: 13448c2ecf20Sopenharmony_ci inline_refs++; 13458c2ecf20Sopenharmony_ci break; 13468c2ecf20Sopenharmony_ci /* Contains parent bytenr */ 13478c2ecf20Sopenharmony_ci case BTRFS_SHARED_BLOCK_REF_KEY: 13488c2ecf20Sopenharmony_ci if (!IS_ALIGNED(inline_offset, fs_info->sectorsize)) { 13498c2ecf20Sopenharmony_ci extent_err(leaf, slot, 13508c2ecf20Sopenharmony_ci "invalid tree parent bytenr, have %llu expect aligned to %u", 13518c2ecf20Sopenharmony_ci inline_offset, fs_info->sectorsize); 13528c2ecf20Sopenharmony_ci return -EUCLEAN; 13538c2ecf20Sopenharmony_ci } 13548c2ecf20Sopenharmony_ci inline_refs++; 13558c2ecf20Sopenharmony_ci break; 13568c2ecf20Sopenharmony_ci /* 13578c2ecf20Sopenharmony_ci * Contains owner subvolid, owner key objectid, adjusted offset. 13588c2ecf20Sopenharmony_ci * The only obvious corruption can happen in that offset. 13598c2ecf20Sopenharmony_ci */ 13608c2ecf20Sopenharmony_ci case BTRFS_EXTENT_DATA_REF_KEY: 13618c2ecf20Sopenharmony_ci dref = (struct btrfs_extent_data_ref *)(&iref->offset); 13628c2ecf20Sopenharmony_ci dref_offset = btrfs_extent_data_ref_offset(leaf, dref); 13638c2ecf20Sopenharmony_ci if (!IS_ALIGNED(dref_offset, fs_info->sectorsize)) { 13648c2ecf20Sopenharmony_ci extent_err(leaf, slot, 13658c2ecf20Sopenharmony_ci "invalid data ref offset, have %llu expect aligned to %u", 13668c2ecf20Sopenharmony_ci dref_offset, fs_info->sectorsize); 13678c2ecf20Sopenharmony_ci return -EUCLEAN; 13688c2ecf20Sopenharmony_ci } 13698c2ecf20Sopenharmony_ci inline_refs += btrfs_extent_data_ref_count(leaf, dref); 13708c2ecf20Sopenharmony_ci break; 13718c2ecf20Sopenharmony_ci /* Contains parent bytenr and ref count */ 13728c2ecf20Sopenharmony_ci case BTRFS_SHARED_DATA_REF_KEY: 13738c2ecf20Sopenharmony_ci sref = (struct btrfs_shared_data_ref *)(iref + 1); 13748c2ecf20Sopenharmony_ci if (!IS_ALIGNED(inline_offset, fs_info->sectorsize)) { 13758c2ecf20Sopenharmony_ci extent_err(leaf, slot, 13768c2ecf20Sopenharmony_ci "invalid data parent bytenr, have %llu expect aligned to %u", 13778c2ecf20Sopenharmony_ci inline_offset, fs_info->sectorsize); 13788c2ecf20Sopenharmony_ci return -EUCLEAN; 13798c2ecf20Sopenharmony_ci } 13808c2ecf20Sopenharmony_ci inline_refs += btrfs_shared_data_ref_count(leaf, sref); 13818c2ecf20Sopenharmony_ci break; 13828c2ecf20Sopenharmony_ci default: 13838c2ecf20Sopenharmony_ci extent_err(leaf, slot, "unknown inline ref type: %u", 13848c2ecf20Sopenharmony_ci inline_type); 13858c2ecf20Sopenharmony_ci return -EUCLEAN; 13868c2ecf20Sopenharmony_ci } 13878c2ecf20Sopenharmony_ci ptr += btrfs_extent_inline_ref_size(inline_type); 13888c2ecf20Sopenharmony_ci } 13898c2ecf20Sopenharmony_ci /* No padding is allowed */ 13908c2ecf20Sopenharmony_ci if (ptr != end) { 13918c2ecf20Sopenharmony_ci extent_err(leaf, slot, 13928c2ecf20Sopenharmony_ci "invalid extent item size, padding bytes found"); 13938c2ecf20Sopenharmony_ci return -EUCLEAN; 13948c2ecf20Sopenharmony_ci } 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci /* Finally, check the inline refs against total refs */ 13978c2ecf20Sopenharmony_ci if (inline_refs > total_refs) { 13988c2ecf20Sopenharmony_ci extent_err(leaf, slot, 13998c2ecf20Sopenharmony_ci "invalid extent refs, have %llu expect >= inline %llu", 14008c2ecf20Sopenharmony_ci total_refs, inline_refs); 14018c2ecf20Sopenharmony_ci return -EUCLEAN; 14028c2ecf20Sopenharmony_ci } 14038c2ecf20Sopenharmony_ci return 0; 14048c2ecf20Sopenharmony_ci} 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_cistatic int check_simple_keyed_refs(struct extent_buffer *leaf, 14078c2ecf20Sopenharmony_ci struct btrfs_key *key, int slot) 14088c2ecf20Sopenharmony_ci{ 14098c2ecf20Sopenharmony_ci u32 expect_item_size = 0; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci if (key->type == BTRFS_SHARED_DATA_REF_KEY) 14128c2ecf20Sopenharmony_ci expect_item_size = sizeof(struct btrfs_shared_data_ref); 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci if (btrfs_item_size_nr(leaf, slot) != expect_item_size) { 14158c2ecf20Sopenharmony_ci generic_err(leaf, slot, 14168c2ecf20Sopenharmony_ci "invalid item size, have %u expect %u for key type %u", 14178c2ecf20Sopenharmony_ci btrfs_item_size_nr(leaf, slot), 14188c2ecf20Sopenharmony_ci expect_item_size, key->type); 14198c2ecf20Sopenharmony_ci return -EUCLEAN; 14208c2ecf20Sopenharmony_ci } 14218c2ecf20Sopenharmony_ci if (!IS_ALIGNED(key->objectid, leaf->fs_info->sectorsize)) { 14228c2ecf20Sopenharmony_ci generic_err(leaf, slot, 14238c2ecf20Sopenharmony_ci"invalid key objectid for shared block ref, have %llu expect aligned to %u", 14248c2ecf20Sopenharmony_ci key->objectid, leaf->fs_info->sectorsize); 14258c2ecf20Sopenharmony_ci return -EUCLEAN; 14268c2ecf20Sopenharmony_ci } 14278c2ecf20Sopenharmony_ci if (key->type != BTRFS_TREE_BLOCK_REF_KEY && 14288c2ecf20Sopenharmony_ci !IS_ALIGNED(key->offset, leaf->fs_info->sectorsize)) { 14298c2ecf20Sopenharmony_ci extent_err(leaf, slot, 14308c2ecf20Sopenharmony_ci "invalid tree parent bytenr, have %llu expect aligned to %u", 14318c2ecf20Sopenharmony_ci key->offset, leaf->fs_info->sectorsize); 14328c2ecf20Sopenharmony_ci return -EUCLEAN; 14338c2ecf20Sopenharmony_ci } 14348c2ecf20Sopenharmony_ci return 0; 14358c2ecf20Sopenharmony_ci} 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_cistatic int check_extent_data_ref(struct extent_buffer *leaf, 14388c2ecf20Sopenharmony_ci struct btrfs_key *key, int slot) 14398c2ecf20Sopenharmony_ci{ 14408c2ecf20Sopenharmony_ci struct btrfs_extent_data_ref *dref; 14418c2ecf20Sopenharmony_ci unsigned long ptr = btrfs_item_ptr_offset(leaf, slot); 14428c2ecf20Sopenharmony_ci const unsigned long end = ptr + btrfs_item_size_nr(leaf, slot); 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci if (btrfs_item_size_nr(leaf, slot) % sizeof(*dref) != 0) { 14458c2ecf20Sopenharmony_ci generic_err(leaf, slot, 14468c2ecf20Sopenharmony_ci "invalid item size, have %u expect aligned to %zu for key type %u", 14478c2ecf20Sopenharmony_ci btrfs_item_size_nr(leaf, slot), 14488c2ecf20Sopenharmony_ci sizeof(*dref), key->type); 14498c2ecf20Sopenharmony_ci return -EUCLEAN; 14508c2ecf20Sopenharmony_ci } 14518c2ecf20Sopenharmony_ci if (!IS_ALIGNED(key->objectid, leaf->fs_info->sectorsize)) { 14528c2ecf20Sopenharmony_ci generic_err(leaf, slot, 14538c2ecf20Sopenharmony_ci"invalid key objectid for shared block ref, have %llu expect aligned to %u", 14548c2ecf20Sopenharmony_ci key->objectid, leaf->fs_info->sectorsize); 14558c2ecf20Sopenharmony_ci return -EUCLEAN; 14568c2ecf20Sopenharmony_ci } 14578c2ecf20Sopenharmony_ci for (; ptr < end; ptr += sizeof(*dref)) { 14588c2ecf20Sopenharmony_ci u64 offset; 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci /* 14618c2ecf20Sopenharmony_ci * We cannot check the extent_data_ref hash due to possible 14628c2ecf20Sopenharmony_ci * overflow from the leaf due to hash collisions. 14638c2ecf20Sopenharmony_ci */ 14648c2ecf20Sopenharmony_ci dref = (struct btrfs_extent_data_ref *)ptr; 14658c2ecf20Sopenharmony_ci offset = btrfs_extent_data_ref_offset(leaf, dref); 14668c2ecf20Sopenharmony_ci if (!IS_ALIGNED(offset, leaf->fs_info->sectorsize)) { 14678c2ecf20Sopenharmony_ci extent_err(leaf, slot, 14688c2ecf20Sopenharmony_ci "invalid extent data backref offset, have %llu expect aligned to %u", 14698c2ecf20Sopenharmony_ci offset, leaf->fs_info->sectorsize); 14708c2ecf20Sopenharmony_ci return -EUCLEAN; 14718c2ecf20Sopenharmony_ci } 14728c2ecf20Sopenharmony_ci } 14738c2ecf20Sopenharmony_ci return 0; 14748c2ecf20Sopenharmony_ci} 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci#define inode_ref_err(eb, slot, fmt, args...) \ 14778c2ecf20Sopenharmony_ci inode_item_err(eb, slot, fmt, ##args) 14788c2ecf20Sopenharmony_cistatic int check_inode_ref(struct extent_buffer *leaf, 14798c2ecf20Sopenharmony_ci struct btrfs_key *key, struct btrfs_key *prev_key, 14808c2ecf20Sopenharmony_ci int slot) 14818c2ecf20Sopenharmony_ci{ 14828c2ecf20Sopenharmony_ci struct btrfs_inode_ref *iref; 14838c2ecf20Sopenharmony_ci unsigned long ptr; 14848c2ecf20Sopenharmony_ci unsigned long end; 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci if (!check_prev_ino(leaf, key, slot, prev_key)) 14878c2ecf20Sopenharmony_ci return -EUCLEAN; 14888c2ecf20Sopenharmony_ci /* namelen can't be 0, so item_size == sizeof() is also invalid */ 14898c2ecf20Sopenharmony_ci if (btrfs_item_size_nr(leaf, slot) <= sizeof(*iref)) { 14908c2ecf20Sopenharmony_ci inode_ref_err(leaf, slot, 14918c2ecf20Sopenharmony_ci "invalid item size, have %u expect (%zu, %u)", 14928c2ecf20Sopenharmony_ci btrfs_item_size_nr(leaf, slot), 14938c2ecf20Sopenharmony_ci sizeof(*iref), BTRFS_LEAF_DATA_SIZE(leaf->fs_info)); 14948c2ecf20Sopenharmony_ci return -EUCLEAN; 14958c2ecf20Sopenharmony_ci } 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci ptr = btrfs_item_ptr_offset(leaf, slot); 14988c2ecf20Sopenharmony_ci end = ptr + btrfs_item_size_nr(leaf, slot); 14998c2ecf20Sopenharmony_ci while (ptr < end) { 15008c2ecf20Sopenharmony_ci u16 namelen; 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci if (ptr + sizeof(iref) > end) { 15038c2ecf20Sopenharmony_ci inode_ref_err(leaf, slot, 15048c2ecf20Sopenharmony_ci "inode ref overflow, ptr %lu end %lu inode_ref_size %zu", 15058c2ecf20Sopenharmony_ci ptr, end, sizeof(iref)); 15068c2ecf20Sopenharmony_ci return -EUCLEAN; 15078c2ecf20Sopenharmony_ci } 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci iref = (struct btrfs_inode_ref *)ptr; 15108c2ecf20Sopenharmony_ci namelen = btrfs_inode_ref_name_len(leaf, iref); 15118c2ecf20Sopenharmony_ci if (ptr + sizeof(*iref) + namelen > end) { 15128c2ecf20Sopenharmony_ci inode_ref_err(leaf, slot, 15138c2ecf20Sopenharmony_ci "inode ref overflow, ptr %lu end %lu namelen %u", 15148c2ecf20Sopenharmony_ci ptr, end, namelen); 15158c2ecf20Sopenharmony_ci return -EUCLEAN; 15168c2ecf20Sopenharmony_ci } 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci /* 15198c2ecf20Sopenharmony_ci * NOTE: In theory we should record all found index numbers 15208c2ecf20Sopenharmony_ci * to find any duplicated indexes, but that will be too time 15218c2ecf20Sopenharmony_ci * consuming for inodes with too many hard links. 15228c2ecf20Sopenharmony_ci */ 15238c2ecf20Sopenharmony_ci ptr += sizeof(*iref) + namelen; 15248c2ecf20Sopenharmony_ci } 15258c2ecf20Sopenharmony_ci return 0; 15268c2ecf20Sopenharmony_ci} 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci/* 15298c2ecf20Sopenharmony_ci * Common point to switch the item-specific validation. 15308c2ecf20Sopenharmony_ci */ 15318c2ecf20Sopenharmony_cistatic int check_leaf_item(struct extent_buffer *leaf, 15328c2ecf20Sopenharmony_ci struct btrfs_key *key, int slot, 15338c2ecf20Sopenharmony_ci struct btrfs_key *prev_key) 15348c2ecf20Sopenharmony_ci{ 15358c2ecf20Sopenharmony_ci int ret = 0; 15368c2ecf20Sopenharmony_ci struct btrfs_chunk *chunk; 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci switch (key->type) { 15398c2ecf20Sopenharmony_ci case BTRFS_EXTENT_DATA_KEY: 15408c2ecf20Sopenharmony_ci ret = check_extent_data_item(leaf, key, slot, prev_key); 15418c2ecf20Sopenharmony_ci break; 15428c2ecf20Sopenharmony_ci case BTRFS_EXTENT_CSUM_KEY: 15438c2ecf20Sopenharmony_ci ret = check_csum_item(leaf, key, slot, prev_key); 15448c2ecf20Sopenharmony_ci break; 15458c2ecf20Sopenharmony_ci case BTRFS_DIR_ITEM_KEY: 15468c2ecf20Sopenharmony_ci case BTRFS_DIR_INDEX_KEY: 15478c2ecf20Sopenharmony_ci case BTRFS_XATTR_ITEM_KEY: 15488c2ecf20Sopenharmony_ci ret = check_dir_item(leaf, key, prev_key, slot); 15498c2ecf20Sopenharmony_ci break; 15508c2ecf20Sopenharmony_ci case BTRFS_INODE_REF_KEY: 15518c2ecf20Sopenharmony_ci ret = check_inode_ref(leaf, key, prev_key, slot); 15528c2ecf20Sopenharmony_ci break; 15538c2ecf20Sopenharmony_ci case BTRFS_BLOCK_GROUP_ITEM_KEY: 15548c2ecf20Sopenharmony_ci ret = check_block_group_item(leaf, key, slot); 15558c2ecf20Sopenharmony_ci break; 15568c2ecf20Sopenharmony_ci case BTRFS_CHUNK_ITEM_KEY: 15578c2ecf20Sopenharmony_ci chunk = btrfs_item_ptr(leaf, slot, struct btrfs_chunk); 15588c2ecf20Sopenharmony_ci ret = check_leaf_chunk_item(leaf, chunk, key, slot); 15598c2ecf20Sopenharmony_ci break; 15608c2ecf20Sopenharmony_ci case BTRFS_DEV_ITEM_KEY: 15618c2ecf20Sopenharmony_ci ret = check_dev_item(leaf, key, slot); 15628c2ecf20Sopenharmony_ci break; 15638c2ecf20Sopenharmony_ci case BTRFS_INODE_ITEM_KEY: 15648c2ecf20Sopenharmony_ci ret = check_inode_item(leaf, key, slot); 15658c2ecf20Sopenharmony_ci break; 15668c2ecf20Sopenharmony_ci case BTRFS_ROOT_ITEM_KEY: 15678c2ecf20Sopenharmony_ci ret = check_root_item(leaf, key, slot); 15688c2ecf20Sopenharmony_ci break; 15698c2ecf20Sopenharmony_ci case BTRFS_EXTENT_ITEM_KEY: 15708c2ecf20Sopenharmony_ci case BTRFS_METADATA_ITEM_KEY: 15718c2ecf20Sopenharmony_ci ret = check_extent_item(leaf, key, slot); 15728c2ecf20Sopenharmony_ci break; 15738c2ecf20Sopenharmony_ci case BTRFS_TREE_BLOCK_REF_KEY: 15748c2ecf20Sopenharmony_ci case BTRFS_SHARED_DATA_REF_KEY: 15758c2ecf20Sopenharmony_ci case BTRFS_SHARED_BLOCK_REF_KEY: 15768c2ecf20Sopenharmony_ci ret = check_simple_keyed_refs(leaf, key, slot); 15778c2ecf20Sopenharmony_ci break; 15788c2ecf20Sopenharmony_ci case BTRFS_EXTENT_DATA_REF_KEY: 15798c2ecf20Sopenharmony_ci ret = check_extent_data_ref(leaf, key, slot); 15808c2ecf20Sopenharmony_ci break; 15818c2ecf20Sopenharmony_ci } 15828c2ecf20Sopenharmony_ci return ret; 15838c2ecf20Sopenharmony_ci} 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_cistatic int check_leaf(struct extent_buffer *leaf, bool check_item_data) 15868c2ecf20Sopenharmony_ci{ 15878c2ecf20Sopenharmony_ci struct btrfs_fs_info *fs_info = leaf->fs_info; 15888c2ecf20Sopenharmony_ci /* No valid key type is 0, so all key should be larger than this key */ 15898c2ecf20Sopenharmony_ci struct btrfs_key prev_key = {0, 0, 0}; 15908c2ecf20Sopenharmony_ci struct btrfs_key key; 15918c2ecf20Sopenharmony_ci u32 nritems = btrfs_header_nritems(leaf); 15928c2ecf20Sopenharmony_ci int slot; 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci if (btrfs_header_level(leaf) != 0) { 15958c2ecf20Sopenharmony_ci generic_err(leaf, 0, 15968c2ecf20Sopenharmony_ci "invalid level for leaf, have %d expect 0", 15978c2ecf20Sopenharmony_ci btrfs_header_level(leaf)); 15988c2ecf20Sopenharmony_ci return -EUCLEAN; 15998c2ecf20Sopenharmony_ci } 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci /* 16028c2ecf20Sopenharmony_ci * Extent buffers from a relocation tree have a owner field that 16038c2ecf20Sopenharmony_ci * corresponds to the subvolume tree they are based on. So just from an 16048c2ecf20Sopenharmony_ci * extent buffer alone we can not find out what is the id of the 16058c2ecf20Sopenharmony_ci * corresponding subvolume tree, so we can not figure out if the extent 16068c2ecf20Sopenharmony_ci * buffer corresponds to the root of the relocation tree or not. So 16078c2ecf20Sopenharmony_ci * skip this check for relocation trees. 16088c2ecf20Sopenharmony_ci */ 16098c2ecf20Sopenharmony_ci if (nritems == 0 && !btrfs_header_flag(leaf, BTRFS_HEADER_FLAG_RELOC)) { 16108c2ecf20Sopenharmony_ci u64 owner = btrfs_header_owner(leaf); 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci /* These trees must never be empty */ 16138c2ecf20Sopenharmony_ci if (owner == BTRFS_ROOT_TREE_OBJECTID || 16148c2ecf20Sopenharmony_ci owner == BTRFS_CHUNK_TREE_OBJECTID || 16158c2ecf20Sopenharmony_ci owner == BTRFS_EXTENT_TREE_OBJECTID || 16168c2ecf20Sopenharmony_ci owner == BTRFS_DEV_TREE_OBJECTID || 16178c2ecf20Sopenharmony_ci owner == BTRFS_FS_TREE_OBJECTID || 16188c2ecf20Sopenharmony_ci owner == BTRFS_DATA_RELOC_TREE_OBJECTID) { 16198c2ecf20Sopenharmony_ci generic_err(leaf, 0, 16208c2ecf20Sopenharmony_ci "invalid root, root %llu must never be empty", 16218c2ecf20Sopenharmony_ci owner); 16228c2ecf20Sopenharmony_ci return -EUCLEAN; 16238c2ecf20Sopenharmony_ci } 16248c2ecf20Sopenharmony_ci /* Unknown tree */ 16258c2ecf20Sopenharmony_ci if (owner == 0) { 16268c2ecf20Sopenharmony_ci generic_err(leaf, 0, 16278c2ecf20Sopenharmony_ci "invalid owner, root 0 is not defined"); 16288c2ecf20Sopenharmony_ci return -EUCLEAN; 16298c2ecf20Sopenharmony_ci } 16308c2ecf20Sopenharmony_ci return 0; 16318c2ecf20Sopenharmony_ci } 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci if (nritems == 0) 16348c2ecf20Sopenharmony_ci return 0; 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci /* 16378c2ecf20Sopenharmony_ci * Check the following things to make sure this is a good leaf, and 16388c2ecf20Sopenharmony_ci * leaf users won't need to bother with similar sanity checks: 16398c2ecf20Sopenharmony_ci * 16408c2ecf20Sopenharmony_ci * 1) key ordering 16418c2ecf20Sopenharmony_ci * 2) item offset and size 16428c2ecf20Sopenharmony_ci * No overlap, no hole, all inside the leaf. 16438c2ecf20Sopenharmony_ci * 3) item content 16448c2ecf20Sopenharmony_ci * If possible, do comprehensive sanity check. 16458c2ecf20Sopenharmony_ci * NOTE: All checks must only rely on the item data itself. 16468c2ecf20Sopenharmony_ci */ 16478c2ecf20Sopenharmony_ci for (slot = 0; slot < nritems; slot++) { 16488c2ecf20Sopenharmony_ci u32 item_end_expected; 16498c2ecf20Sopenharmony_ci int ret; 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci btrfs_item_key_to_cpu(leaf, &key, slot); 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci /* Make sure the keys are in the right order */ 16548c2ecf20Sopenharmony_ci if (btrfs_comp_cpu_keys(&prev_key, &key) >= 0) { 16558c2ecf20Sopenharmony_ci generic_err(leaf, slot, 16568c2ecf20Sopenharmony_ci "bad key order, prev (%llu %u %llu) current (%llu %u %llu)", 16578c2ecf20Sopenharmony_ci prev_key.objectid, prev_key.type, 16588c2ecf20Sopenharmony_ci prev_key.offset, key.objectid, key.type, 16598c2ecf20Sopenharmony_ci key.offset); 16608c2ecf20Sopenharmony_ci return -EUCLEAN; 16618c2ecf20Sopenharmony_ci } 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci /* 16648c2ecf20Sopenharmony_ci * Make sure the offset and ends are right, remember that the 16658c2ecf20Sopenharmony_ci * item data starts at the end of the leaf and grows towards the 16668c2ecf20Sopenharmony_ci * front. 16678c2ecf20Sopenharmony_ci */ 16688c2ecf20Sopenharmony_ci if (slot == 0) 16698c2ecf20Sopenharmony_ci item_end_expected = BTRFS_LEAF_DATA_SIZE(fs_info); 16708c2ecf20Sopenharmony_ci else 16718c2ecf20Sopenharmony_ci item_end_expected = btrfs_item_offset_nr(leaf, 16728c2ecf20Sopenharmony_ci slot - 1); 16738c2ecf20Sopenharmony_ci if (btrfs_item_end_nr(leaf, slot) != item_end_expected) { 16748c2ecf20Sopenharmony_ci generic_err(leaf, slot, 16758c2ecf20Sopenharmony_ci "unexpected item end, have %u expect %u", 16768c2ecf20Sopenharmony_ci btrfs_item_end_nr(leaf, slot), 16778c2ecf20Sopenharmony_ci item_end_expected); 16788c2ecf20Sopenharmony_ci return -EUCLEAN; 16798c2ecf20Sopenharmony_ci } 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci /* 16828c2ecf20Sopenharmony_ci * Check to make sure that we don't point outside of the leaf, 16838c2ecf20Sopenharmony_ci * just in case all the items are consistent to each other, but 16848c2ecf20Sopenharmony_ci * all point outside of the leaf. 16858c2ecf20Sopenharmony_ci */ 16868c2ecf20Sopenharmony_ci if (btrfs_item_end_nr(leaf, slot) > 16878c2ecf20Sopenharmony_ci BTRFS_LEAF_DATA_SIZE(fs_info)) { 16888c2ecf20Sopenharmony_ci generic_err(leaf, slot, 16898c2ecf20Sopenharmony_ci "slot end outside of leaf, have %u expect range [0, %u]", 16908c2ecf20Sopenharmony_ci btrfs_item_end_nr(leaf, slot), 16918c2ecf20Sopenharmony_ci BTRFS_LEAF_DATA_SIZE(fs_info)); 16928c2ecf20Sopenharmony_ci return -EUCLEAN; 16938c2ecf20Sopenharmony_ci } 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci /* Also check if the item pointer overlaps with btrfs item. */ 16968c2ecf20Sopenharmony_ci if (btrfs_item_nr_offset(slot) + sizeof(struct btrfs_item) > 16978c2ecf20Sopenharmony_ci btrfs_item_ptr_offset(leaf, slot)) { 16988c2ecf20Sopenharmony_ci generic_err(leaf, slot, 16998c2ecf20Sopenharmony_ci "slot overlaps with its data, item end %lu data start %lu", 17008c2ecf20Sopenharmony_ci btrfs_item_nr_offset(slot) + 17018c2ecf20Sopenharmony_ci sizeof(struct btrfs_item), 17028c2ecf20Sopenharmony_ci btrfs_item_ptr_offset(leaf, slot)); 17038c2ecf20Sopenharmony_ci return -EUCLEAN; 17048c2ecf20Sopenharmony_ci } 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci if (check_item_data) { 17078c2ecf20Sopenharmony_ci /* 17088c2ecf20Sopenharmony_ci * Check if the item size and content meet other 17098c2ecf20Sopenharmony_ci * criteria 17108c2ecf20Sopenharmony_ci */ 17118c2ecf20Sopenharmony_ci ret = check_leaf_item(leaf, &key, slot, &prev_key); 17128c2ecf20Sopenharmony_ci if (ret < 0) 17138c2ecf20Sopenharmony_ci return ret; 17148c2ecf20Sopenharmony_ci } 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci prev_key.objectid = key.objectid; 17178c2ecf20Sopenharmony_ci prev_key.type = key.type; 17188c2ecf20Sopenharmony_ci prev_key.offset = key.offset; 17198c2ecf20Sopenharmony_ci } 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci return 0; 17228c2ecf20Sopenharmony_ci} 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ciint btrfs_check_leaf_full(struct extent_buffer *leaf) 17258c2ecf20Sopenharmony_ci{ 17268c2ecf20Sopenharmony_ci return check_leaf(leaf, true); 17278c2ecf20Sopenharmony_ci} 17288c2ecf20Sopenharmony_ciALLOW_ERROR_INJECTION(btrfs_check_leaf_full, ERRNO); 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ciint btrfs_check_leaf_relaxed(struct extent_buffer *leaf) 17318c2ecf20Sopenharmony_ci{ 17328c2ecf20Sopenharmony_ci return check_leaf(leaf, false); 17338c2ecf20Sopenharmony_ci} 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ciint btrfs_check_node(struct extent_buffer *node) 17368c2ecf20Sopenharmony_ci{ 17378c2ecf20Sopenharmony_ci struct btrfs_fs_info *fs_info = node->fs_info; 17388c2ecf20Sopenharmony_ci unsigned long nr = btrfs_header_nritems(node); 17398c2ecf20Sopenharmony_ci struct btrfs_key key, next_key; 17408c2ecf20Sopenharmony_ci int slot; 17418c2ecf20Sopenharmony_ci int level = btrfs_header_level(node); 17428c2ecf20Sopenharmony_ci u64 bytenr; 17438c2ecf20Sopenharmony_ci int ret = 0; 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci if (level <= 0 || level >= BTRFS_MAX_LEVEL) { 17468c2ecf20Sopenharmony_ci generic_err(node, 0, 17478c2ecf20Sopenharmony_ci "invalid level for node, have %d expect [1, %d]", 17488c2ecf20Sopenharmony_ci level, BTRFS_MAX_LEVEL - 1); 17498c2ecf20Sopenharmony_ci return -EUCLEAN; 17508c2ecf20Sopenharmony_ci } 17518c2ecf20Sopenharmony_ci if (nr == 0 || nr > BTRFS_NODEPTRS_PER_BLOCK(fs_info)) { 17528c2ecf20Sopenharmony_ci btrfs_crit(fs_info, 17538c2ecf20Sopenharmony_ci"corrupt node: root=%llu block=%llu, nritems too %s, have %lu expect range [1,%u]", 17548c2ecf20Sopenharmony_ci btrfs_header_owner(node), node->start, 17558c2ecf20Sopenharmony_ci nr == 0 ? "small" : "large", nr, 17568c2ecf20Sopenharmony_ci BTRFS_NODEPTRS_PER_BLOCK(fs_info)); 17578c2ecf20Sopenharmony_ci return -EUCLEAN; 17588c2ecf20Sopenharmony_ci } 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci for (slot = 0; slot < nr - 1; slot++) { 17618c2ecf20Sopenharmony_ci bytenr = btrfs_node_blockptr(node, slot); 17628c2ecf20Sopenharmony_ci btrfs_node_key_to_cpu(node, &key, slot); 17638c2ecf20Sopenharmony_ci btrfs_node_key_to_cpu(node, &next_key, slot + 1); 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci if (!bytenr) { 17668c2ecf20Sopenharmony_ci generic_err(node, slot, 17678c2ecf20Sopenharmony_ci "invalid NULL node pointer"); 17688c2ecf20Sopenharmony_ci ret = -EUCLEAN; 17698c2ecf20Sopenharmony_ci goto out; 17708c2ecf20Sopenharmony_ci } 17718c2ecf20Sopenharmony_ci if (!IS_ALIGNED(bytenr, fs_info->sectorsize)) { 17728c2ecf20Sopenharmony_ci generic_err(node, slot, 17738c2ecf20Sopenharmony_ci "unaligned pointer, have %llu should be aligned to %u", 17748c2ecf20Sopenharmony_ci bytenr, fs_info->sectorsize); 17758c2ecf20Sopenharmony_ci ret = -EUCLEAN; 17768c2ecf20Sopenharmony_ci goto out; 17778c2ecf20Sopenharmony_ci } 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci if (btrfs_comp_cpu_keys(&key, &next_key) >= 0) { 17808c2ecf20Sopenharmony_ci generic_err(node, slot, 17818c2ecf20Sopenharmony_ci "bad key order, current (%llu %u %llu) next (%llu %u %llu)", 17828c2ecf20Sopenharmony_ci key.objectid, key.type, key.offset, 17838c2ecf20Sopenharmony_ci next_key.objectid, next_key.type, 17848c2ecf20Sopenharmony_ci next_key.offset); 17858c2ecf20Sopenharmony_ci ret = -EUCLEAN; 17868c2ecf20Sopenharmony_ci goto out; 17878c2ecf20Sopenharmony_ci } 17888c2ecf20Sopenharmony_ci } 17898c2ecf20Sopenharmony_ciout: 17908c2ecf20Sopenharmony_ci return ret; 17918c2ecf20Sopenharmony_ci} 17928c2ecf20Sopenharmony_ciALLOW_ERROR_INJECTION(btrfs_check_node, ERRNO); 1793