2017-10-09 04:51:02 +03:00
/*
* Copyright ( C ) Qu Wenruo 2017. All rights reserved .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public
* License along with this program .
*/
/*
* The module is used to catch unexpected / corrupted tree block data .
* Such behavior can be caused either by a fuzzed image or bugs .
*
* The objective is to do leaf / node validation checks when tree block is read
* from disk , and check * every * possible member , so other code won ' t
* need to checking them again .
*
* Due to the potential and unwanted damage , every checker needs to be
* carefully reviewed otherwise so it does not prevent mount of valid images .
*/
# include "ctree.h"
# include "tree-checker.h"
# include "disk-io.h"
# include "compression.h"
2017-10-09 04:51:03 +03:00
/*
* Error message should follow the following format :
* corrupt < type > : < identifier > , < reason > [ , < bad_value > ]
*
* @ type : leaf or node
* @ identifier : the necessary info to locate the leaf / node .
* It ' s recommened to decode key . objecitd / offset if it ' s
* meaningful .
* @ reason : describe the error
* @ bad_value : optional , it ' s recommened to output bad value and its
* expected value ( range ) .
*
* Since comma is used to separate the components , only space is allowed
* inside each component .
*/
/*
* Append generic " corrupt leaf/node root=%llu block=%llu slot=%d: " to @ fmt .
* Allows callers to customize the output .
*/
__printf ( 4 , 5 )
2018-02-19 19:24:18 +03:00
__cold
2018-01-25 09:56:18 +03:00
static void generic_err ( const struct btrfs_fs_info * fs_info ,
2017-10-09 04:51:03 +03:00
const struct extent_buffer * eb , int slot ,
const char * fmt , . . . )
{
struct va_format vaf ;
va_list args ;
va_start ( args , fmt ) ;
vaf . fmt = fmt ;
vaf . va = & args ;
2018-01-25 09:56:18 +03:00
btrfs_crit ( fs_info ,
2017-10-09 04:51:03 +03:00
" corrupt %s: root=%llu block=%llu slot=%d, %pV " ,
btrfs_header_level ( eb ) = = 0 ? " leaf " : " node " ,
2018-01-25 09:56:18 +03:00
btrfs_header_owner ( eb ) , btrfs_header_bytenr ( eb ) , slot , & vaf ) ;
2017-10-09 04:51:03 +03:00
va_end ( args ) ;
}
2017-10-09 04:51:06 +03:00
/*
* Customized reporter for extent data item , since its key objectid and
* offset has its own meaning .
*/
__printf ( 4 , 5 )
2018-02-19 19:24:18 +03:00
__cold
2018-01-25 09:56:18 +03:00
static void file_extent_err ( const struct btrfs_fs_info * fs_info ,
2017-10-09 04:51:06 +03:00
const struct extent_buffer * eb , int slot ,
const char * fmt , . . . )
{
struct btrfs_key key ;
struct va_format vaf ;
va_list args ;
btrfs_item_key_to_cpu ( eb , & key , slot ) ;
va_start ( args , fmt ) ;
vaf . fmt = fmt ;
vaf . va = & args ;
2018-01-25 09:56:18 +03:00
btrfs_crit ( fs_info ,
2017-10-09 04:51:06 +03:00
" corrupt %s: root=%llu block=%llu slot=%d ino=%llu file_offset=%llu, %pV " ,
2018-01-25 09:56:18 +03:00
btrfs_header_level ( eb ) = = 0 ? " leaf " : " node " ,
btrfs_header_owner ( eb ) , btrfs_header_bytenr ( eb ) , slot ,
key . objectid , key . offset , & vaf ) ;
2017-10-09 04:51:06 +03:00
va_end ( args ) ;
}
/*
* Return 0 if the btrfs_file_extent_ # # name is aligned to @ alignment
* Else return 1
*/
2018-01-25 09:56:18 +03:00
# define CHECK_FE_ALIGNED(fs_info, leaf, slot, fi, name, alignment) \
2017-10-09 04:51:06 +03:00
( { \
if ( ! IS_ALIGNED ( btrfs_file_extent_ # # name ( ( leaf ) , ( fi ) ) , ( alignment ) ) ) \
2018-01-25 09:56:18 +03:00
file_extent_err ( ( fs_info ) , ( leaf ) , ( slot ) , \
2017-10-09 04:51:06 +03:00
" invalid %s for file extent, have %llu, should be aligned to %u " , \
( # name ) , btrfs_file_extent_ # # name ( ( leaf ) , ( fi ) ) , \
( alignment ) ) ; \
( ! IS_ALIGNED ( btrfs_file_extent_ # # name ( ( leaf ) , ( fi ) ) , ( alignment ) ) ) ; \
} )
2018-01-25 09:56:18 +03:00
static int check_extent_data_item ( struct btrfs_fs_info * fs_info ,
2017-10-09 04:51:02 +03:00
struct extent_buffer * leaf ,
struct btrfs_key * key , int slot )
{
struct btrfs_file_extent_item * fi ;
2018-01-25 09:56:18 +03:00
u32 sectorsize = fs_info - > sectorsize ;
2017-10-09 04:51:02 +03:00
u32 item_size = btrfs_item_size_nr ( leaf , slot ) ;
if ( ! IS_ALIGNED ( key - > offset , sectorsize ) ) {
2018-01-25 09:56:18 +03:00
file_extent_err ( fs_info , leaf , slot ,
2017-10-09 04:51:06 +03:00
" unaligned file_offset for file extent, have %llu should be aligned to %u " ,
key - > offset , sectorsize ) ;
2017-10-09 04:51:02 +03:00
return - EUCLEAN ;
}
fi = btrfs_item_ptr ( leaf , slot , struct btrfs_file_extent_item ) ;
if ( btrfs_file_extent_type ( leaf , fi ) > BTRFS_FILE_EXTENT_TYPES ) {
2018-01-25 09:56:18 +03:00
file_extent_err ( fs_info , leaf , slot ,
2017-10-09 04:51:06 +03:00
" invalid type for file extent, have %u expect range [0, %u] " ,
btrfs_file_extent_type ( leaf , fi ) ,
BTRFS_FILE_EXTENT_TYPES ) ;
2017-10-09 04:51:02 +03:00
return - EUCLEAN ;
}
/*
* Support for new compression / encrption must introduce incompat flag ,
* and must be caught in open_ctree ( ) .
*/
if ( btrfs_file_extent_compression ( leaf , fi ) > BTRFS_COMPRESS_TYPES ) {
2018-01-25 09:56:18 +03:00
file_extent_err ( fs_info , leaf , slot ,
2017-10-09 04:51:06 +03:00
" invalid compression for file extent, have %u expect range [0, %u] " ,
btrfs_file_extent_compression ( leaf , fi ) ,
BTRFS_COMPRESS_TYPES ) ;
2017-10-09 04:51:02 +03:00
return - EUCLEAN ;
}
if ( btrfs_file_extent_encryption ( leaf , fi ) ) {
2018-01-25 09:56:18 +03:00
file_extent_err ( fs_info , leaf , slot ,
2017-10-09 04:51:06 +03:00
" invalid encryption for file extent, have %u expect 0 " ,
btrfs_file_extent_encryption ( leaf , fi ) ) ;
2017-10-09 04:51:02 +03:00
return - EUCLEAN ;
}
if ( btrfs_file_extent_type ( leaf , fi ) = = BTRFS_FILE_EXTENT_INLINE ) {
/* Inline extent must have 0 as key offset */
if ( key - > offset ) {
2018-01-25 09:56:18 +03:00
file_extent_err ( fs_info , leaf , slot ,
2017-10-09 04:51:06 +03:00
" invalid file_offset for inline file extent, have %llu expect 0 " ,
key - > offset ) ;
2017-10-09 04:51:02 +03:00
return - EUCLEAN ;
}
/* Compressed inline extent has no on-disk size, skip it */
if ( btrfs_file_extent_compression ( leaf , fi ) ! =
BTRFS_COMPRESS_NONE )
return 0 ;
/* Uncompressed inline extent size must match item size */
if ( item_size ! = BTRFS_FILE_EXTENT_INLINE_DATA_START +
btrfs_file_extent_ram_bytes ( leaf , fi ) ) {
2018-01-25 09:56:18 +03:00
file_extent_err ( fs_info , leaf , slot ,
2017-10-09 04:51:06 +03:00
" invalid ram_bytes for uncompressed inline extent, have %u expect %llu " ,
item_size , BTRFS_FILE_EXTENT_INLINE_DATA_START +
btrfs_file_extent_ram_bytes ( leaf , fi ) ) ;
2017-10-09 04:51:02 +03:00
return - EUCLEAN ;
}
return 0 ;
}
/* Regular or preallocated extent has fixed item size */
if ( item_size ! = sizeof ( * fi ) ) {
2018-01-25 09:56:18 +03:00
file_extent_err ( fs_info , leaf , slot ,
2017-10-13 12:27:35 +03:00
" invalid item size for reg/prealloc file extent, have %u expect %zu " ,
2017-10-09 04:51:06 +03:00
item_size , sizeof ( * fi ) ) ;
2017-10-09 04:51:02 +03:00
return - EUCLEAN ;
}
2018-01-25 09:56:18 +03:00
if ( CHECK_FE_ALIGNED ( fs_info , leaf , slot , fi , ram_bytes , sectorsize ) | |
CHECK_FE_ALIGNED ( fs_info , leaf , slot , fi , disk_bytenr , sectorsize ) | |
CHECK_FE_ALIGNED ( fs_info , leaf , slot , fi , disk_num_bytes , sectorsize ) | |
CHECK_FE_ALIGNED ( fs_info , leaf , slot , fi , offset , sectorsize ) | |
CHECK_FE_ALIGNED ( fs_info , leaf , slot , fi , num_bytes , sectorsize ) )
2017-10-09 04:51:02 +03:00
return - EUCLEAN ;
return 0 ;
}
2018-01-25 09:56:18 +03:00
static int check_csum_item ( struct btrfs_fs_info * fs_info ,
struct extent_buffer * leaf , struct btrfs_key * key ,
int slot )
2017-10-09 04:51:02 +03:00
{
2018-01-25 09:56:18 +03:00
u32 sectorsize = fs_info - > sectorsize ;
u32 csumsize = btrfs_super_csum_size ( fs_info - > super_copy ) ;
2017-10-09 04:51:02 +03:00
if ( key - > objectid ! = BTRFS_EXTENT_CSUM_OBJECTID ) {
2018-01-25 09:56:18 +03:00
generic_err ( fs_info , leaf , slot ,
2017-10-09 04:51:05 +03:00
" invalid key objectid for csum item, have %llu expect %llu " ,
key - > objectid , BTRFS_EXTENT_CSUM_OBJECTID ) ;
2017-10-09 04:51:02 +03:00
return - EUCLEAN ;
}
if ( ! IS_ALIGNED ( key - > offset , sectorsize ) ) {
2018-01-25 09:56:18 +03:00
generic_err ( fs_info , leaf , slot ,
2017-10-09 04:51:05 +03:00
" unaligned key offset for csum item, have %llu should be aligned to %u " ,
key - > offset , sectorsize ) ;
2017-10-09 04:51:02 +03:00
return - EUCLEAN ;
}
if ( ! IS_ALIGNED ( btrfs_item_size_nr ( leaf , slot ) , csumsize ) ) {
2018-01-25 09:56:18 +03:00
generic_err ( fs_info , leaf , slot ,
2017-10-09 04:51:05 +03:00
" unaligned item size for csum item, have %u should be aligned to %u " ,
btrfs_item_size_nr ( leaf , slot ) , csumsize ) ;
2017-10-09 04:51:02 +03:00
return - EUCLEAN ;
}
return 0 ;
}
2017-11-08 03:54:25 +03:00
/*
* Customized reported for dir_item , only important new info is key - > objectid ,
* which represents inode number
*/
__printf ( 4 , 5 )
2018-02-19 19:24:18 +03:00
__cold
2018-01-25 09:56:18 +03:00
static void dir_item_err ( const struct btrfs_fs_info * fs_info ,
2017-11-08 03:54:25 +03:00
const struct extent_buffer * eb , int slot ,
const char * fmt , . . . )
{
struct btrfs_key key ;
struct va_format vaf ;
va_list args ;
btrfs_item_key_to_cpu ( eb , & key , slot ) ;
va_start ( args , fmt ) ;
vaf . fmt = fmt ;
vaf . va = & args ;
2018-01-25 09:56:18 +03:00
btrfs_crit ( fs_info ,
2017-11-08 03:54:25 +03:00
" corrupt %s: root=%llu block=%llu slot=%d ino=%llu, %pV " ,
2018-01-25 09:56:18 +03:00
btrfs_header_level ( eb ) = = 0 ? " leaf " : " node " ,
btrfs_header_owner ( eb ) , btrfs_header_bytenr ( eb ) , slot ,
key . objectid , & vaf ) ;
2017-11-08 03:54:25 +03:00
va_end ( args ) ;
}
2018-01-25 09:56:18 +03:00
static int check_dir_item ( struct btrfs_fs_info * fs_info ,
2017-11-08 03:54:25 +03:00
struct extent_buffer * leaf ,
struct btrfs_key * key , int slot )
{
struct btrfs_dir_item * di ;
u32 item_size = btrfs_item_size_nr ( leaf , slot ) ;
u32 cur = 0 ;
di = btrfs_item_ptr ( leaf , slot , struct btrfs_dir_item ) ;
while ( cur < item_size ) {
u32 name_len ;
u32 data_len ;
u32 max_name_len ;
u32 total_size ;
u32 name_hash ;
u8 dir_type ;
/* header itself should not cross item boundary */
if ( cur + sizeof ( * di ) > item_size ) {
2018-01-25 09:56:18 +03:00
dir_item_err ( fs_info , leaf , slot ,
2017-12-06 17:18:14 +03:00
" dir item header crosses item boundary, have %zu boundary %u " ,
2017-11-08 03:54:25 +03:00
cur + sizeof ( * di ) , item_size ) ;
return - EUCLEAN ;
}
/* dir type check */
dir_type = btrfs_dir_type ( leaf , di ) ;
if ( dir_type > = BTRFS_FT_MAX ) {
2018-01-25 09:56:18 +03:00
dir_item_err ( fs_info , leaf , slot ,
2017-11-08 03:54:25 +03:00
" invalid dir item type, have %u expect [0, %u) " ,
dir_type , BTRFS_FT_MAX ) ;
return - EUCLEAN ;
}
if ( key - > type = = BTRFS_XATTR_ITEM_KEY & &
dir_type ! = BTRFS_FT_XATTR ) {
2018-01-25 09:56:18 +03:00
dir_item_err ( fs_info , leaf , slot ,
2017-11-08 03:54:25 +03:00
" invalid dir item type for XATTR key, have %u expect %u " ,
dir_type , BTRFS_FT_XATTR ) ;
return - EUCLEAN ;
}
if ( dir_type = = BTRFS_FT_XATTR & &
key - > type ! = BTRFS_XATTR_ITEM_KEY ) {
2018-01-25 09:56:18 +03:00
dir_item_err ( fs_info , leaf , slot ,
2017-11-08 03:54:25 +03:00
" xattr dir type found for non-XATTR key " ) ;
return - EUCLEAN ;
}
if ( dir_type = = BTRFS_FT_XATTR )
max_name_len = XATTR_NAME_MAX ;
else
max_name_len = BTRFS_NAME_LEN ;
/* Name/data length check */
name_len = btrfs_dir_name_len ( leaf , di ) ;
data_len = btrfs_dir_data_len ( leaf , di ) ;
if ( name_len > max_name_len ) {
2018-01-25 09:56:18 +03:00
dir_item_err ( fs_info , leaf , slot ,
2017-11-08 03:54:25 +03:00
" dir item name len too long, have %u max %u " ,
name_len , max_name_len ) ;
return - EUCLEAN ;
}
2018-01-25 09:56:18 +03:00
if ( name_len + data_len > BTRFS_MAX_XATTR_SIZE ( fs_info ) ) {
dir_item_err ( fs_info , leaf , slot ,
2017-11-08 03:54:25 +03:00
" dir item name and data len too long, have %u max %u " ,
name_len + data_len ,
2018-01-25 09:56:18 +03:00
BTRFS_MAX_XATTR_SIZE ( fs_info ) ) ;
2017-11-08 03:54:25 +03:00
return - EUCLEAN ;
}
if ( data_len & & dir_type ! = BTRFS_FT_XATTR ) {
2018-01-25 09:56:18 +03:00
dir_item_err ( fs_info , leaf , slot ,
2017-11-08 03:54:25 +03:00
" dir item with invalid data len, have %u expect 0 " ,
data_len ) ;
return - EUCLEAN ;
}
total_size = sizeof ( * di ) + name_len + data_len ;
/* header and name/data should not cross item boundary */
if ( cur + total_size > item_size ) {
2018-01-25 09:56:18 +03:00
dir_item_err ( fs_info , leaf , slot ,
2017-11-08 03:54:25 +03:00
" dir item data crosses item boundary, have %u boundary %u " ,
cur + total_size , item_size ) ;
return - EUCLEAN ;
}
/*
* Special check for XATTR / DIR_ITEM , as key - > offset is name
* hash , should match its name
*/
if ( key - > type = = BTRFS_DIR_ITEM_KEY | |
key - > type = = BTRFS_XATTR_ITEM_KEY ) {
2018-01-10 17:13:07 +03:00
char namebuf [ max ( BTRFS_NAME_LEN , XATTR_NAME_MAX ) ] ;
2017-11-08 03:54:25 +03:00
read_extent_buffer ( leaf , namebuf ,
( unsigned long ) ( di + 1 ) , name_len ) ;
name_hash = btrfs_name_hash ( namebuf , name_len ) ;
if ( key - > offset ! = name_hash ) {
2018-01-25 09:56:18 +03:00
dir_item_err ( fs_info , leaf , slot ,
2017-11-08 03:54:25 +03:00
" name hash mismatch with key, have 0x%016x expect 0x%016llx " ,
name_hash , key - > offset ) ;
return - EUCLEAN ;
}
}
cur + = total_size ;
di = ( struct btrfs_dir_item * ) ( ( void * ) di + total_size ) ;
}
return 0 ;
}
2017-10-09 04:51:02 +03:00
/*
* Common point to switch the item - specific validation .
*/
2018-01-25 09:56:18 +03:00
static int check_leaf_item ( struct btrfs_fs_info * fs_info ,
2017-10-09 04:51:02 +03:00
struct extent_buffer * leaf ,
struct btrfs_key * key , int slot )
{
int ret = 0 ;
switch ( key - > type ) {
case BTRFS_EXTENT_DATA_KEY :
2018-01-25 09:56:18 +03:00
ret = check_extent_data_item ( fs_info , leaf , key , slot ) ;
2017-10-09 04:51:02 +03:00
break ;
case BTRFS_EXTENT_CSUM_KEY :
2018-01-25 09:56:18 +03:00
ret = check_csum_item ( fs_info , leaf , key , slot ) ;
2017-10-09 04:51:02 +03:00
break ;
2017-11-08 03:54:25 +03:00
case BTRFS_DIR_ITEM_KEY :
case BTRFS_DIR_INDEX_KEY :
case BTRFS_XATTR_ITEM_KEY :
2018-01-25 09:56:18 +03:00
ret = check_dir_item ( fs_info , leaf , key , slot ) ;
2017-11-08 03:54:25 +03:00
break ;
2017-10-09 04:51:02 +03:00
}
return ret ;
}
2018-01-25 09:56:18 +03:00
static int check_leaf ( struct btrfs_fs_info * fs_info , struct extent_buffer * leaf ,
2017-11-08 03:54:24 +03:00
bool check_item_data )
2017-10-09 04:51:02 +03:00
{
/* No valid key type is 0, so all key should be larger than this key */
struct btrfs_key prev_key = { 0 , 0 , 0 } ;
struct btrfs_key key ;
u32 nritems = btrfs_header_nritems ( leaf ) ;
int slot ;
/*
* Extent buffers from a relocation tree have a owner field that
* corresponds to the subvolume tree they are based on . So just from an
* extent buffer alone we can not find out what is the id of the
* corresponding subvolume tree , so we can not figure out if the extent
* buffer corresponds to the root of the relocation tree or not . So
* skip this check for relocation trees .
*/
if ( nritems = = 0 & & ! btrfs_header_flag ( leaf , BTRFS_HEADER_FLAG_RELOC ) ) {
struct btrfs_root * check_root ;
key . objectid = btrfs_header_owner ( leaf ) ;
key . type = BTRFS_ROOT_ITEM_KEY ;
key . offset = ( u64 ) - 1 ;
check_root = btrfs_get_fs_root ( fs_info , & key , false ) ;
/*
* The only reason we also check NULL here is that during
* open_ctree ( ) some roots has not yet been set up .
*/
if ( ! IS_ERR_OR_NULL ( check_root ) ) {
struct extent_buffer * eb ;
eb = btrfs_root_node ( check_root ) ;
/* if leaf is the root, then it's fine */
if ( leaf ! = eb ) {
2018-01-25 09:56:18 +03:00
generic_err ( fs_info , leaf , 0 ,
2017-10-09 04:51:04 +03:00
" invalid nritems, have %u should not be 0 for non-root leaf " ,
nritems ) ;
2017-10-09 04:51:02 +03:00
free_extent_buffer ( eb ) ;
return - EUCLEAN ;
}
free_extent_buffer ( eb ) ;
}
return 0 ;
}
if ( nritems = = 0 )
return 0 ;
/*
* Check the following things to make sure this is a good leaf , and
* leaf users won ' t need to bother with similar sanity checks :
*
* 1 ) key ordering
* 2 ) item offset and size
* No overlap , no hole , all inside the leaf .
* 3 ) item content
* If possible , do comprehensive sanity check .
* NOTE : All checks must only rely on the item data itself .
*/
for ( slot = 0 ; slot < nritems ; slot + + ) {
u32 item_end_expected ;
int ret ;
btrfs_item_key_to_cpu ( leaf , & key , slot ) ;
/* Make sure the keys are in the right order */
if ( btrfs_comp_cpu_keys ( & prev_key , & key ) > = 0 ) {
2018-01-25 09:56:18 +03:00
generic_err ( fs_info , leaf , slot ,
2017-10-09 04:51:04 +03:00
" bad key order, prev (%llu %u %llu) current (%llu %u %llu) " ,
prev_key . objectid , prev_key . type ,
prev_key . offset , key . objectid , key . type ,
key . offset ) ;
2017-10-09 04:51:02 +03:00
return - EUCLEAN ;
}
/*
* Make sure the offset and ends are right , remember that the
* item data starts at the end of the leaf and grows towards the
* front .
*/
if ( slot = = 0 )
item_end_expected = BTRFS_LEAF_DATA_SIZE ( fs_info ) ;
else
item_end_expected = btrfs_item_offset_nr ( leaf ,
slot - 1 ) ;
if ( btrfs_item_end_nr ( leaf , slot ) ! = item_end_expected ) {
2018-01-25 09:56:18 +03:00
generic_err ( fs_info , leaf , slot ,
2017-10-09 04:51:04 +03:00
" unexpected item end, have %u expect %u " ,
btrfs_item_end_nr ( leaf , slot ) ,
item_end_expected ) ;
2017-10-09 04:51:02 +03:00
return - EUCLEAN ;
}
/*
* Check to make sure that we don ' t point outside of the leaf ,
* just in case all the items are consistent to each other , but
* all point outside of the leaf .
*/
if ( btrfs_item_end_nr ( leaf , slot ) >
BTRFS_LEAF_DATA_SIZE ( fs_info ) ) {
2018-01-25 09:56:18 +03:00
generic_err ( fs_info , leaf , slot ,
2017-10-09 04:51:04 +03:00
" slot end outside of leaf, have %u expect range [0, %u] " ,
btrfs_item_end_nr ( leaf , slot ) ,
BTRFS_LEAF_DATA_SIZE ( fs_info ) ) ;
2017-10-09 04:51:02 +03:00
return - EUCLEAN ;
}
/* Also check if the item pointer overlaps with btrfs item. */
if ( btrfs_item_nr_offset ( slot ) + sizeof ( struct btrfs_item ) >
btrfs_item_ptr_offset ( leaf , slot ) ) {
2018-01-25 09:56:18 +03:00
generic_err ( fs_info , leaf , slot ,
2017-10-09 04:51:04 +03:00
" slot overlaps with its data, item end %lu data start %lu " ,
btrfs_item_nr_offset ( slot ) +
sizeof ( struct btrfs_item ) ,
btrfs_item_ptr_offset ( leaf , slot ) ) ;
2017-10-09 04:51:02 +03:00
return - EUCLEAN ;
}
2017-11-08 03:54:24 +03:00
if ( check_item_data ) {
/*
* Check if the item size and content meet other
* criteria
*/
2018-01-25 09:56:18 +03:00
ret = check_leaf_item ( fs_info , leaf , & key , slot ) ;
2017-11-08 03:54:24 +03:00
if ( ret < 0 )
return ret ;
}
2017-10-09 04:51:02 +03:00
prev_key . objectid = key . objectid ;
prev_key . type = key . type ;
prev_key . offset = key . offset ;
}
return 0 ;
}
2018-01-25 09:56:18 +03:00
int btrfs_check_leaf_full ( struct btrfs_fs_info * fs_info ,
struct extent_buffer * leaf )
2017-11-08 03:54:24 +03:00
{
2018-01-25 09:56:18 +03:00
return check_leaf ( fs_info , leaf , true ) ;
2017-11-08 03:54:24 +03:00
}
2018-01-25 09:56:18 +03:00
int btrfs_check_leaf_relaxed ( struct btrfs_fs_info * fs_info ,
2017-11-08 03:54:24 +03:00
struct extent_buffer * leaf )
{
2018-01-25 09:56:18 +03:00
return check_leaf ( fs_info , leaf , false ) ;
2017-11-08 03:54:24 +03:00
}
2018-01-25 09:56:18 +03:00
int btrfs_check_node ( struct btrfs_fs_info * fs_info , struct extent_buffer * node )
2017-10-09 04:51:02 +03:00
{
unsigned long nr = btrfs_header_nritems ( node ) ;
struct btrfs_key key , next_key ;
int slot ;
u64 bytenr ;
int ret = 0 ;
2018-01-25 09:56:18 +03:00
if ( nr = = 0 | | nr > BTRFS_NODEPTRS_PER_BLOCK ( fs_info ) ) {
btrfs_crit ( fs_info ,
2017-10-09 04:51:03 +03:00
" corrupt node: root=%llu block=%llu, nritems too %s, have %lu expect range [1,%u] " ,
2018-01-25 09:56:18 +03:00
btrfs_header_owner ( node ) , node - > start ,
2017-10-09 04:51:03 +03:00
nr = = 0 ? " small " : " large " , nr ,
2018-01-25 09:56:18 +03:00
BTRFS_NODEPTRS_PER_BLOCK ( fs_info ) ) ;
2017-10-09 04:51:03 +03:00
return - EUCLEAN ;
2017-10-09 04:51:02 +03:00
}
for ( slot = 0 ; slot < nr - 1 ; slot + + ) {
bytenr = btrfs_node_blockptr ( node , slot ) ;
btrfs_node_key_to_cpu ( node , & key , slot ) ;
btrfs_node_key_to_cpu ( node , & next_key , slot + 1 ) ;
if ( ! bytenr ) {
2018-01-25 09:56:18 +03:00
generic_err ( fs_info , node , slot ,
2017-10-09 04:51:03 +03:00
" invalid NULL node pointer " ) ;
ret = - EUCLEAN ;
goto out ;
}
2018-01-25 09:56:18 +03:00
if ( ! IS_ALIGNED ( bytenr , fs_info - > sectorsize ) ) {
generic_err ( fs_info , node , slot ,
2017-10-09 04:51:03 +03:00
" unaligned pointer, have %llu should be aligned to %u " ,
2018-01-25 09:56:18 +03:00
bytenr , fs_info - > sectorsize ) ;
2017-10-09 04:51:03 +03:00
ret = - EUCLEAN ;
2017-10-09 04:51:02 +03:00
goto out ;
}
if ( btrfs_comp_cpu_keys ( & key , & next_key ) > = 0 ) {
2018-01-25 09:56:18 +03:00
generic_err ( fs_info , node , slot ,
2017-10-09 04:51:03 +03:00
" bad key order, current (%llu %u %llu) next (%llu %u %llu) " ,
key . objectid , key . type , key . offset ,
next_key . objectid , next_key . type ,
next_key . offset ) ;
ret = - EUCLEAN ;
2017-10-09 04:51:02 +03:00
goto out ;
}
}
out :
return ret ;
}