2018-06-06 05:42:14 +03:00
// SPDX-License-Identifier: GPL-2.0+
2017-10-18 07:37:43 +03:00
/*
* Copyright ( C ) 2017 Oracle . All Rights Reserved .
* Author : Darrick J . Wong < darrick . wong @ oracle . com >
*/
# include "xfs.h"
# include "xfs_fs.h"
# include "xfs_shared.h"
# include "xfs_format.h"
# include "xfs_trans_resv.h"
# include "xfs_mount.h"
# include "xfs_log_format.h"
# include "xfs_trans.h"
# include "xfs_inode.h"
# include "xfs_dir2.h"
# include "xfs_dir2_priv.h"
# include "xfs_attr_leaf.h"
# include "scrub/scrub.h"
# include "scrub/common.h"
# include "scrub/trace.h"
# include "scrub/dabtree.h"
/* Directory/Attribute Btree */
/*
* Check for da btree operation errors . See the section about handling
* operational errors in common . c .
*/
bool
2018-07-19 22:29:11 +03:00
xchk_da_process_error (
2018-07-19 22:29:12 +03:00
struct xchk_da_btree * ds ,
int level ,
int * error )
2017-10-18 07:37:43 +03:00
{
2018-07-19 22:29:12 +03:00
struct xfs_scrub * sc = ds - > sc ;
2017-10-18 07:37:43 +03:00
if ( * error = = 0 )
return true ;
switch ( * error ) {
case - EDEADLOCK :
/* Used to restart an op with deadlock avoidance. */
2018-07-19 22:29:11 +03:00
trace_xchk_deadlock_retry ( sc - > ip , sc - > sm , * error ) ;
2017-10-18 07:37:43 +03:00
break ;
case - EFSBADCRC :
case - EFSCORRUPTED :
/* Note the badness but don't abort. */
sc - > sm - > sm_flags | = XFS_SCRUB_OFLAG_CORRUPT ;
* error = 0 ;
2021-04-21 01:54:36 +03:00
fallthrough ;
2017-10-18 07:37:43 +03:00
default :
2018-07-19 22:29:11 +03:00
trace_xchk_file_op_error ( sc , ds - > dargs . whichfork ,
2017-10-18 07:37:43 +03:00
xfs_dir2_da_to_db ( ds - > dargs . geo ,
ds - > state - > path . blk [ level ] . blkno ) ,
* error , __return_address ) ;
break ;
}
return false ;
}
/*
* Check for da btree corruption . See the section about handling
* operational errors in common . c .
*/
void
2018-07-19 22:29:11 +03:00
xchk_da_set_corrupt (
2018-07-19 22:29:12 +03:00
struct xchk_da_btree * ds ,
int level )
2017-10-18 07:37:43 +03:00
{
2018-07-19 22:29:12 +03:00
struct xfs_scrub * sc = ds - > sc ;
2017-10-18 07:37:43 +03:00
sc - > sm - > sm_flags | = XFS_SCRUB_OFLAG_CORRUPT ;
2018-07-19 22:29:11 +03:00
trace_xchk_fblock_error ( sc , ds - > dargs . whichfork ,
2017-10-18 07:37:43 +03:00
xfs_dir2_da_to_db ( ds - > dargs . geo ,
ds - > state - > path . blk [ level ] . blkno ) ,
__return_address ) ;
}
2019-11-09 01:52:07 +03:00
static struct xfs_da_node_entry *
xchk_da_btree_node_entry (
struct xchk_da_btree * ds ,
int level )
2017-10-18 07:37:43 +03:00
{
2019-11-09 01:52:07 +03:00
struct xfs_da_state_blk * blk = & ds - > state - > path . blk [ level ] ;
2019-11-09 01:57:48 +03:00
struct xfs_da3_icnode_hdr hdr ;
2017-10-18 07:37:43 +03:00
2019-11-09 01:52:07 +03:00
ASSERT ( blk - > magic = = XFS_DA_NODE_MAGIC ) ;
2017-10-18 07:37:43 +03:00
2019-11-09 01:57:48 +03:00
xfs_da3_node_hdr_from_disk ( ds - > sc - > mp , & hdr , blk - > bp - > b_addr ) ;
return hdr . btree + blk - > index ;
2017-10-18 07:37:43 +03:00
}
/* Scrub a da btree hash (key). */
int
2018-07-19 22:29:11 +03:00
xchk_da_btree_hash (
struct xchk_da_btree * ds ,
2017-10-18 07:37:43 +03:00
int level ,
__be32 * hashp )
{
struct xfs_da_node_entry * entry ;
xfs_dahash_t hash ;
xfs_dahash_t parent_hash ;
/* Is this hash in order? */
hash = be32_to_cpu ( * hashp ) ;
if ( hash < ds - > hashes [ level ] )
2018-07-19 22:29:11 +03:00
xchk_da_set_corrupt ( ds , level ) ;
2017-10-18 07:37:43 +03:00
ds - > hashes [ level ] = hash ;
if ( level = = 0 )
return 0 ;
/* Is this hash no larger than the parent hash? */
2019-11-09 01:52:07 +03:00
entry = xchk_da_btree_node_entry ( ds , level - 1 ) ;
2017-10-18 07:37:43 +03:00
parent_hash = be32_to_cpu ( entry - > hashval ) ;
if ( parent_hash < hash )
2018-07-19 22:29:11 +03:00
xchk_da_set_corrupt ( ds , level ) ;
2017-10-18 07:37:43 +03:00
return 0 ;
}
/*
* Check a da btree pointer . Returns true if it ' s ok to use this
* pointer .
*/
STATIC bool
2018-07-19 22:29:11 +03:00
xchk_da_btree_ptr_ok (
2018-07-19 22:29:12 +03:00
struct xchk_da_btree * ds ,
int level ,
xfs_dablk_t blkno )
2017-10-18 07:37:43 +03:00
{
if ( blkno < ds - > lowest | | ( ds - > highest ! = 0 & & blkno > = ds - > highest ) ) {
2018-07-19 22:29:11 +03:00
xchk_da_set_corrupt ( ds , level ) ;
2017-10-18 07:37:43 +03:00
return false ;
}
return true ;
}
/*
* The da btree scrubber can handle leaf1 blocks as a degenerate
* form of leafn blocks . Since the regular da code doesn ' t handle
* leaf1 , we must multiplex the verifiers .
*/
static void
2018-07-19 22:29:11 +03:00
xchk_da_btree_read_verify (
2017-10-18 07:37:43 +03:00
struct xfs_buf * bp )
{
struct xfs_da_blkinfo * info = bp - > b_addr ;
switch ( be16_to_cpu ( info - > magic ) ) {
case XFS_DIR2_LEAF1_MAGIC :
case XFS_DIR3_LEAF1_MAGIC :
bp - > b_ops = & xfs_dir3_leaf1_buf_ops ;
bp - > b_ops - > verify_read ( bp ) ;
return ;
default :
/*
* xfs_da3_node_buf_ops already know how to handle
* DA * _NODE , ATTR * _LEAF , and DIR * _LEAFN blocks .
*/
bp - > b_ops = & xfs_da3_node_buf_ops ;
bp - > b_ops - > verify_read ( bp ) ;
return ;
}
}
static void
2018-07-19 22:29:11 +03:00
xchk_da_btree_write_verify (
2017-10-18 07:37:43 +03:00
struct xfs_buf * bp )
{
struct xfs_da_blkinfo * info = bp - > b_addr ;
switch ( be16_to_cpu ( info - > magic ) ) {
case XFS_DIR2_LEAF1_MAGIC :
case XFS_DIR3_LEAF1_MAGIC :
bp - > b_ops = & xfs_dir3_leaf1_buf_ops ;
bp - > b_ops - > verify_write ( bp ) ;
return ;
default :
/*
* xfs_da3_node_buf_ops already know how to handle
* DA * _NODE , ATTR * _LEAF , and DIR * _LEAFN blocks .
*/
bp - > b_ops = & xfs_da3_node_buf_ops ;
bp - > b_ops - > verify_write ( bp ) ;
return ;
}
}
2018-01-17 05:53:11 +03:00
static void *
2018-07-19 22:29:11 +03:00
xchk_da_btree_verify (
2018-01-17 05:53:11 +03:00
struct xfs_buf * bp )
{
struct xfs_da_blkinfo * info = bp - > b_addr ;
switch ( be16_to_cpu ( info - > magic ) ) {
case XFS_DIR2_LEAF1_MAGIC :
case XFS_DIR3_LEAF1_MAGIC :
bp - > b_ops = & xfs_dir3_leaf1_buf_ops ;
return bp - > b_ops - > verify_struct ( bp ) ;
default :
bp - > b_ops = & xfs_da3_node_buf_ops ;
return bp - > b_ops - > verify_struct ( bp ) ;
}
}
2017-10-18 07:37:43 +03:00
2018-07-19 22:29:11 +03:00
static const struct xfs_buf_ops xchk_da_btree_buf_ops = {
. name = " xchk_da_btree " ,
. verify_read = xchk_da_btree_read_verify ,
. verify_write = xchk_da_btree_write_verify ,
. verify_struct = xchk_da_btree_verify ,
2017-10-18 07:37:43 +03:00
} ;
/* Check a block's sibling. */
STATIC int
2018-07-19 22:29:11 +03:00
xchk_da_btree_block_check_sibling (
2018-07-19 22:29:12 +03:00
struct xchk_da_btree * ds ,
int level ,
int direction ,
xfs_dablk_t sibling )
2017-10-18 07:37:43 +03:00
{
2020-03-25 06:10:55 +03:00
struct xfs_da_state_path * path = & ds - > state - > path ;
struct xfs_da_state_path * altpath = & ds - > state - > altpath ;
2018-07-19 22:29:12 +03:00
int retval ;
2020-03-25 06:10:55 +03:00
int plevel ;
2018-07-19 22:29:12 +03:00
int error ;
2017-10-18 07:37:43 +03:00
2020-03-25 06:10:55 +03:00
memcpy ( altpath , path , sizeof ( ds - > state - > altpath ) ) ;
2017-10-18 07:37:43 +03:00
/*
* If the pointer is null , we shouldn ' t be able to move the upper
* level pointer anywhere .
*/
if ( sibling = = 0 ) {
2020-03-25 06:10:55 +03:00
error = xfs_da3_path_shift ( ds - > state , altpath , direction ,
false , & retval ) ;
2017-10-18 07:37:43 +03:00
if ( error = = 0 & & retval = = 0 )
2018-07-19 22:29:11 +03:00
xchk_da_set_corrupt ( ds , level ) ;
2017-10-18 07:37:43 +03:00
error = 0 ;
goto out ;
}
/* Move the alternate cursor one block in the direction given. */
2020-03-25 06:10:55 +03:00
error = xfs_da3_path_shift ( ds - > state , altpath , direction , false ,
& retval ) ;
2018-07-19 22:29:11 +03:00
if ( ! xchk_da_process_error ( ds , level , & error ) )
2020-03-25 06:10:55 +03:00
goto out ;
2017-10-18 07:37:43 +03:00
if ( retval ) {
2018-07-19 22:29:11 +03:00
xchk_da_set_corrupt ( ds , level ) ;
2020-03-25 06:10:55 +03:00
goto out ;
2017-10-18 07:37:43 +03:00
}
2020-03-25 06:10:55 +03:00
if ( altpath - > blk [ level ] . bp )
xchk_buffer_recheck ( ds - > sc , altpath - > blk [ level ] . bp ) ;
2017-10-18 07:37:43 +03:00
/* Compare upper level pointer to sibling pointer. */
2020-03-25 06:10:55 +03:00
if ( altpath - > blk [ level ] . blkno ! = sibling )
2018-07-19 22:29:11 +03:00
xchk_da_set_corrupt ( ds , level ) ;
2020-03-25 06:10:55 +03:00
2017-10-18 07:37:43 +03:00
out :
2020-03-25 06:10:55 +03:00
/* Free all buffers in the altpath that aren't referenced from path. */
for ( plevel = 0 ; plevel < altpath - > active ; plevel + + ) {
if ( altpath - > blk [ plevel ] . bp = = NULL | |
( plevel < path - > active & &
altpath - > blk [ plevel ] . bp = = path - > blk [ plevel ] . bp ) )
continue ;
xfs_trans_brelse ( ds - > dargs . trans , altpath - > blk [ plevel ] . bp ) ;
altpath - > blk [ plevel ] . bp = NULL ;
}
2017-10-18 07:37:43 +03:00
return error ;
}
/* Check a block's sibling pointers. */
STATIC int
2018-07-19 22:29:11 +03:00
xchk_da_btree_block_check_siblings (
2018-07-19 22:29:12 +03:00
struct xchk_da_btree * ds ,
int level ,
struct xfs_da_blkinfo * hdr )
2017-10-18 07:37:43 +03:00
{
2018-07-19 22:29:12 +03:00
xfs_dablk_t forw ;
xfs_dablk_t back ;
int error = 0 ;
2017-10-18 07:37:43 +03:00
forw = be32_to_cpu ( hdr - > forw ) ;
back = be32_to_cpu ( hdr - > back ) ;
/* Top level blocks should not have sibling pointers. */
if ( level = = 0 ) {
if ( forw ! = 0 | | back ! = 0 )
2018-07-19 22:29:11 +03:00
xchk_da_set_corrupt ( ds , level ) ;
2017-10-18 07:37:43 +03:00
return 0 ;
}
/*
* Check back ( left ) and forw ( right ) pointers . These functions
* absorb error codes for us .
*/
2018-07-19 22:29:11 +03:00
error = xchk_da_btree_block_check_sibling ( ds , level , 0 , back ) ;
2017-10-18 07:37:43 +03:00
if ( error )
goto out ;
2018-07-19 22:29:11 +03:00
error = xchk_da_btree_block_check_sibling ( ds , level , 1 , forw ) ;
2017-10-18 07:37:43 +03:00
out :
memset ( & ds - > state - > altpath , 0 , sizeof ( ds - > state - > altpath ) ) ;
return error ;
}
/* Load a dir/attribute block from a btree. */
STATIC int
2018-07-19 22:29:11 +03:00
xchk_da_btree_block (
struct xchk_da_btree * ds ,
2017-10-18 07:37:43 +03:00
int level ,
xfs_dablk_t blkno )
{
struct xfs_da_state_blk * blk ;
struct xfs_da_intnode * node ;
struct xfs_da_node_entry * btree ;
struct xfs_da3_blkinfo * hdr3 ;
struct xfs_da_args * dargs = & ds - > dargs ;
struct xfs_inode * ip = ds - > dargs . dp ;
xfs_ino_t owner ;
int * pmaxrecs ;
struct xfs_da3_icnode_hdr nodehdr ;
2017-11-02 22:48:11 +03:00
int error = 0 ;
2017-10-18 07:37:43 +03:00
blk = & ds - > state - > path . blk [ level ] ;
ds - > state - > path . active = level + 1 ;
/* Release old block. */
if ( blk - > bp ) {
xfs_trans_brelse ( dargs - > trans , blk - > bp ) ;
blk - > bp = NULL ;
}
/* Check the pointer. */
blk - > blkno = blkno ;
2018-07-19 22:29:11 +03:00
if ( ! xchk_da_btree_ptr_ok ( ds , level , blkno ) )
2017-10-18 07:37:43 +03:00
goto out_nobuf ;
/* Read the buffer. */
2019-11-20 20:46:04 +03:00
error = xfs_da_read_buf ( dargs - > trans , dargs - > dp , blk - > blkno ,
XFS_DABUF_MAP_HOLE_OK , & blk - > bp , dargs - > whichfork ,
2018-07-19 22:29:11 +03:00
& xchk_da_btree_buf_ops ) ;
if ( ! xchk_da_process_error ( ds , level , & error ) )
2017-10-18 07:37:43 +03:00
goto out_nobuf ;
2018-01-17 05:53:11 +03:00
if ( blk - > bp )
2018-07-19 22:29:11 +03:00
xchk_buffer_recheck ( ds - > sc , blk - > bp ) ;
2017-10-18 07:37:43 +03:00
/*
* We didn ' t find a dir btree root block , which means that
* there ' s no LEAF1 / LEAFN tree ( at least not where it ' s supposed
* to be ) , so jump out now .
*/
if ( ds - > dargs . whichfork = = XFS_DATA_FORK & & level = = 0 & &
blk - > bp = = NULL )
goto out_nobuf ;
/* It's /not/ ok for attr trees not to have a da btree. */
if ( blk - > bp = = NULL ) {
2018-07-19 22:29:11 +03:00
xchk_da_set_corrupt ( ds , level ) ;
2017-10-18 07:37:43 +03:00
goto out_nobuf ;
}
hdr3 = blk - > bp - > b_addr ;
blk - > magic = be16_to_cpu ( hdr3 - > hdr . magic ) ;
pmaxrecs = & ds - > maxrecs [ level ] ;
2017-11-08 23:21:05 +03:00
/* We only started zeroing the header on v5 filesystems. */
2021-08-19 04:46:37 +03:00
if ( xfs_has_crc ( ds - > sc - > mp ) & & hdr3 - > hdr . pad )
2018-07-19 22:29:11 +03:00
xchk_da_set_corrupt ( ds , level ) ;
2017-10-18 07:37:43 +03:00
/* Check the owner. */
2021-08-19 04:46:37 +03:00
if ( xfs_has_crc ( ip - > i_mount ) ) {
2017-10-18 07:37:43 +03:00
owner = be64_to_cpu ( hdr3 - > owner ) ;
if ( owner ! = ip - > i_ino )
2018-07-19 22:29:11 +03:00
xchk_da_set_corrupt ( ds , level ) ;
2017-10-18 07:37:43 +03:00
}
/* Check the siblings. */
2018-07-19 22:29:11 +03:00
error = xchk_da_btree_block_check_siblings ( ds , level , & hdr3 - > hdr ) ;
2017-10-18 07:37:43 +03:00
if ( error )
goto out ;
/* Interpret the buffer. */
switch ( blk - > magic ) {
case XFS_ATTR_LEAF_MAGIC :
case XFS_ATTR3_LEAF_MAGIC :
xfs_trans_buf_set_type ( dargs - > trans , blk - > bp ,
XFS_BLFT_ATTR_LEAF_BUF ) ;
blk - > magic = XFS_ATTR_LEAF_MAGIC ;
blk - > hashval = xfs_attr_leaf_lasthash ( blk - > bp , pmaxrecs ) ;
if ( ds - > tree_level ! = 0 )
2018-07-19 22:29:11 +03:00
xchk_da_set_corrupt ( ds , level ) ;
2017-10-18 07:37:43 +03:00
break ;
case XFS_DIR2_LEAFN_MAGIC :
case XFS_DIR3_LEAFN_MAGIC :
xfs_trans_buf_set_type ( dargs - > trans , blk - > bp ,
XFS_BLFT_DIR_LEAFN_BUF ) ;
blk - > magic = XFS_DIR2_LEAFN_MAGIC ;
blk - > hashval = xfs_dir2_leaf_lasthash ( ip , blk - > bp , pmaxrecs ) ;
if ( ds - > tree_level ! = 0 )
2018-07-19 22:29:11 +03:00
xchk_da_set_corrupt ( ds , level ) ;
2017-10-18 07:37:43 +03:00
break ;
case XFS_DIR2_LEAF1_MAGIC :
case XFS_DIR3_LEAF1_MAGIC :
xfs_trans_buf_set_type ( dargs - > trans , blk - > bp ,
XFS_BLFT_DIR_LEAF1_BUF ) ;
blk - > magic = XFS_DIR2_LEAF1_MAGIC ;
blk - > hashval = xfs_dir2_leaf_lasthash ( ip , blk - > bp , pmaxrecs ) ;
if ( ds - > tree_level ! = 0 )
2018-07-19 22:29:11 +03:00
xchk_da_set_corrupt ( ds , level ) ;
2017-10-18 07:37:43 +03:00
break ;
case XFS_DA_NODE_MAGIC :
case XFS_DA3_NODE_MAGIC :
xfs_trans_buf_set_type ( dargs - > trans , blk - > bp ,
XFS_BLFT_DA_NODE_BUF ) ;
blk - > magic = XFS_DA_NODE_MAGIC ;
node = blk - > bp - > b_addr ;
2019-11-09 01:53:00 +03:00
xfs_da3_node_hdr_from_disk ( ip - > i_mount , & nodehdr , node ) ;
2019-11-09 01:57:48 +03:00
btree = nodehdr . btree ;
2017-10-18 07:37:43 +03:00
* pmaxrecs = nodehdr . count ;
blk - > hashval = be32_to_cpu ( btree [ * pmaxrecs - 1 ] . hashval ) ;
if ( level = = 0 ) {
if ( nodehdr . level > = XFS_DA_NODE_MAXDEPTH ) {
2018-07-19 22:29:11 +03:00
xchk_da_set_corrupt ( ds , level ) ;
2017-10-18 07:37:43 +03:00
goto out_freebp ;
}
ds - > tree_level = nodehdr . level ;
} else {
if ( ds - > tree_level ! = nodehdr . level ) {
2018-07-19 22:29:11 +03:00
xchk_da_set_corrupt ( ds , level ) ;
2017-10-18 07:37:43 +03:00
goto out_freebp ;
}
}
/* XXX: Check hdr3.pad32 once we know how to fix it. */
break ;
default :
2018-07-19 22:29:11 +03:00
xchk_da_set_corrupt ( ds , level ) ;
2017-10-18 07:37:43 +03:00
goto out_freebp ;
}
2020-09-21 19:15:09 +03:00
/*
* If we ' ve been handed a block that is below the dabtree root , does
* its hashval match what the parent block expected to see ?
*/
if ( level > 0 ) {
struct xfs_da_node_entry * key ;
key = xchk_da_btree_node_entry ( ds , level - 1 ) ;
if ( be32_to_cpu ( key - > hashval ) ! = blk - > hashval ) {
xchk_da_set_corrupt ( ds , level ) ;
goto out_freebp ;
}
}
2017-10-18 07:37:43 +03:00
out :
return error ;
out_freebp :
xfs_trans_brelse ( dargs - > trans , blk - > bp ) ;
blk - > bp = NULL ;
out_nobuf :
blk - > blkno = 0 ;
return error ;
}
/* Visit all nodes and leaves of a da btree. */
int
2018-07-19 22:29:11 +03:00
xchk_da_btree (
2018-07-19 22:29:12 +03:00
struct xfs_scrub * sc ,
2017-10-18 07:37:43 +03:00
int whichfork ,
2018-07-19 22:29:12 +03:00
xchk_da_btree_rec_fn scrub_fn ,
2017-10-31 22:10:02 +03:00
void * private )
2017-10-18 07:37:43 +03:00
{
2021-09-16 22:19:00 +03:00
struct xchk_da_btree * ds ;
2017-10-18 07:37:43 +03:00
struct xfs_mount * mp = sc - > mp ;
struct xfs_da_state_blk * blks ;
struct xfs_da_node_entry * key ;
xfs_dablk_t blkno ;
int level ;
int error ;
/* Skip short format data structures; no btree to scan. */
2020-05-18 20:28:05 +03:00
if ( ! xfs_ifork_has_extents ( XFS_IFORK_PTR ( sc - > ip , whichfork ) ) )
2017-10-18 07:37:43 +03:00
return 0 ;
/* Set up initial da state. */
2021-09-16 22:19:00 +03:00
ds = kmem_zalloc ( sizeof ( struct xchk_da_btree ) , KM_NOFS | KM_MAYFAIL ) ;
if ( ! ds )
return - ENOMEM ;
ds - > dargs . dp = sc - > ip ;
ds - > dargs . whichfork = whichfork ;
ds - > dargs . trans = sc - > tp ;
ds - > dargs . op_flags = XFS_DA_OP_OKNOENT ;
ds - > state = xfs_da_state_alloc ( & ds - > dargs ) ;
ds - > sc = sc ;
ds - > private = private ;
2017-10-18 07:37:43 +03:00
if ( whichfork = = XFS_ATTR_FORK ) {
2021-09-16 22:19:00 +03:00
ds - > dargs . geo = mp - > m_attr_geo ;
ds - > lowest = 0 ;
ds - > highest = 0 ;
2017-10-18 07:37:43 +03:00
} else {
2021-09-16 22:19:00 +03:00
ds - > dargs . geo = mp - > m_dir_geo ;
ds - > lowest = ds - > dargs . geo - > leafblk ;
ds - > highest = ds - > dargs . geo - > freeblk ;
2017-10-18 07:37:43 +03:00
}
2021-09-16 22:19:00 +03:00
blkno = ds - > lowest ;
2017-10-18 07:37:43 +03:00
level = 0 ;
/* Find the root of the da tree, if present. */
2021-09-16 22:19:00 +03:00
blks = ds - > state - > path . blk ;
error = xchk_da_btree_block ( ds , level , blkno ) ;
2017-10-18 07:37:43 +03:00
if ( error )
goto out_state ;
/*
2021-09-16 22:19:00 +03:00
* We didn ' t find a block at ds - > lowest , which means that there ' s
2017-10-18 07:37:43 +03:00
* no LEAF1 / LEAFN tree ( at least not where it ' s supposed to be ) ,
* so jump out now .
*/
if ( blks [ level ] . bp = = NULL )
goto out_state ;
blks [ level ] . index = 0 ;
while ( level > = 0 & & level < XFS_DA_NODE_MAXDEPTH ) {
/* Handle leaf block. */
if ( blks [ level ] . magic ! = XFS_DA_NODE_MAGIC ) {
/* End of leaf, pop back towards the root. */
2021-09-16 22:19:00 +03:00
if ( blks [ level ] . index > = ds - > maxrecs [ level ] ) {
2017-10-18 07:37:43 +03:00
if ( level > 0 )
blks [ level - 1 ] . index + + ;
2021-09-16 22:19:00 +03:00
ds - > tree_level + + ;
2017-10-18 07:37:43 +03:00
level - - ;
continue ;
}
/* Dispatch record scrubbing. */
2021-09-16 22:19:00 +03:00
error = scrub_fn ( ds , level ) ;
2017-10-18 07:37:43 +03:00
if ( error )
break ;
2018-07-19 22:29:11 +03:00
if ( xchk_should_terminate ( sc , & error ) | |
2017-10-18 07:37:43 +03:00
( sc - > sm - > sm_flags & XFS_SCRUB_OFLAG_CORRUPT ) )
break ;
blks [ level ] . index + + ;
continue ;
}
/* End of node, pop back towards the root. */
2021-09-16 22:19:00 +03:00
if ( blks [ level ] . index > = ds - > maxrecs [ level ] ) {
2017-10-18 07:37:43 +03:00
if ( level > 0 )
blks [ level - 1 ] . index + + ;
2021-09-16 22:19:00 +03:00
ds - > tree_level + + ;
2017-10-18 07:37:43 +03:00
level - - ;
continue ;
}
/* Hashes in order for scrub? */
2021-09-16 22:19:00 +03:00
key = xchk_da_btree_node_entry ( ds , level ) ;
error = xchk_da_btree_hash ( ds , level , & key - > hashval ) ;
2017-10-18 07:37:43 +03:00
if ( error )
goto out ;
/* Drill another level deeper. */
blkno = be32_to_cpu ( key - > before ) ;
level + + ;
2019-03-19 18:16:21 +03:00
if ( level > = XFS_DA_NODE_MAXDEPTH ) {
/* Too deep! */
2021-09-16 22:19:00 +03:00
xchk_da_set_corrupt ( ds , level - 1 ) ;
2019-03-19 18:16:21 +03:00
break ;
}
2021-09-16 22:19:00 +03:00
ds - > tree_level - - ;
error = xchk_da_btree_block ( ds , level , blkno ) ;
2017-10-18 07:37:43 +03:00
if ( error )
goto out ;
if ( blks [ level ] . bp = = NULL )
goto out ;
blks [ level ] . index = 0 ;
}
out :
/* Release all the buffers we're tracking. */
for ( level = 0 ; level < XFS_DA_NODE_MAXDEPTH ; level + + ) {
if ( blks [ level ] . bp = = NULL )
continue ;
xfs_trans_brelse ( sc - > tp , blks [ level ] . bp ) ;
blks [ level ] . bp = NULL ;
}
out_state :
2021-09-16 22:19:00 +03:00
xfs_da_state_free ( ds - > state ) ;
kmem_free ( ds ) ;
2017-10-18 07:37:43 +03:00
return error ;
}