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_defer.h"
# include "xfs_btree.h"
# include "xfs_bit.h"
# include "xfs_log_format.h"
# include "xfs_trans.h"
# include "xfs_sb.h"
# include "xfs_inode.h"
# include "xfs_inode_fork.h"
# include "xfs_alloc.h"
# include "xfs_rtalloc.h"
# include "xfs_bmap.h"
# include "xfs_bmap_util.h"
# include "xfs_bmap_btree.h"
# include "xfs_rmap.h"
2018-03-23 20:06:53 +03:00
# include "xfs_rmap_btree.h"
2018-01-17 05:53:09 +03:00
# include "xfs_refcount.h"
2017-10-18 07:37:43 +03:00
# include "scrub/xfs_scrub.h"
# include "scrub/scrub.h"
# include "scrub/common.h"
# include "scrub/btree.h"
# include "scrub/trace.h"
/* Set us up with an inode's bmap. */
int
2018-07-19 22:29:11 +03:00
xchk_setup_inode_bmap (
2018-07-19 22:29:12 +03:00
struct xfs_scrub * sc ,
2017-10-18 07:37:43 +03:00
struct xfs_inode * ip )
{
int error ;
2018-07-19 22:29:11 +03:00
error = xchk_get_inode ( sc , ip ) ;
2017-10-18 07:37:43 +03:00
if ( error )
goto out ;
sc - > ilock_flags = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL ;
xfs_ilock ( sc - > ip , sc - > ilock_flags ) ;
/*
* We don ' t want any ephemeral data fork updates sitting around
* while we inspect block mappings , so wait for directio to finish
* and flush dirty data if we have delalloc reservations .
*/
if ( S_ISREG ( VFS_I ( sc - > ip ) - > i_mode ) & &
sc - > sm - > sm_type = = XFS_SCRUB_TYPE_BMBTD ) {
inode_dio_wait ( VFS_I ( sc - > ip ) ) ;
error = filemap_write_and_wait ( VFS_I ( sc - > ip ) - > i_mapping ) ;
if ( error )
goto out ;
}
/* Got the inode, lock it and we're ready to go. */
2018-07-19 22:29:11 +03:00
error = xchk_trans_alloc ( sc , 0 ) ;
2017-10-18 07:37:43 +03:00
if ( error )
goto out ;
sc - > ilock_flags | = XFS_ILOCK_EXCL ;
xfs_ilock ( sc - > ip , XFS_ILOCK_EXCL ) ;
out :
/* scrub teardown will unlock and release the inode */
return error ;
}
/*
* Inode fork block mapping ( BMBT ) scrubber .
* More complex than the others because we have to scrub
* all the extents regardless of whether or not the fork
* is in btree format .
*/
2018-07-19 22:29:11 +03:00
struct xchk_bmap_info {
2018-07-19 22:29:12 +03:00
struct xfs_scrub * sc ;
2017-10-18 07:37:43 +03:00
xfs_fileoff_t lastoff ;
bool is_rt ;
bool is_shared ;
int whichfork ;
} ;
2018-01-17 05:53:08 +03:00
/* Look for a corresponding rmap for this irec. */
static inline bool
2018-07-19 22:29:11 +03:00
xchk_bmap_get_rmap (
struct xchk_bmap_info * info ,
2018-01-17 05:53:08 +03:00
struct xfs_bmbt_irec * irec ,
xfs_agblock_t agbno ,
uint64_t owner ,
struct xfs_rmap_irec * rmap )
{
xfs_fileoff_t offset ;
unsigned int rflags = 0 ;
int has_rmap ;
int error ;
if ( info - > whichfork = = XFS_ATTR_FORK )
rflags | = XFS_RMAP_ATTR_FORK ;
/*
* CoW staging extents are owned ( on disk ) by the refcountbt , so
* their rmaps do not have offsets .
*/
if ( info - > whichfork = = XFS_COW_FORK )
offset = 0 ;
else
offset = irec - > br_startoff ;
/*
* If the caller thinks this could be a shared bmbt extent ( IOWs ,
* any data fork extent of a reflink inode ) then we have to use the
* range rmap lookup to make sure we get the correct owner / offset .
*/
if ( info - > is_shared ) {
error = xfs_rmap_lookup_le_range ( info - > sc - > sa . rmap_cur , agbno ,
owner , offset , rflags , rmap , & has_rmap ) ;
2018-07-19 22:29:11 +03:00
if ( ! xchk_should_check_xref ( info - > sc , & error ,
2018-01-17 05:53:08 +03:00
& info - > sc - > sa . rmap_cur ) )
return false ;
goto out ;
}
/*
* Otherwise , use the ( faster ) regular lookup .
*/
error = xfs_rmap_lookup_le ( info - > sc - > sa . rmap_cur , agbno , 0 , owner ,
offset , rflags , & has_rmap ) ;
2018-07-19 22:29:11 +03:00
if ( ! xchk_should_check_xref ( info - > sc , & error ,
2018-01-17 05:53:08 +03:00
& info - > sc - > sa . rmap_cur ) )
return false ;
if ( ! has_rmap )
goto out ;
error = xfs_rmap_get_rec ( info - > sc - > sa . rmap_cur , rmap , & has_rmap ) ;
2018-07-19 22:29:11 +03:00
if ( ! xchk_should_check_xref ( info - > sc , & error ,
2018-01-17 05:53:08 +03:00
& info - > sc - > sa . rmap_cur ) )
return false ;
out :
if ( ! has_rmap )
2018-07-19 22:29:11 +03:00
xchk_fblock_xref_set_corrupt ( info - > sc , info - > whichfork ,
2018-01-17 05:53:08 +03:00
irec - > br_startoff ) ;
return has_rmap ;
}
/* Make sure that we have rmapbt records for this extent. */
STATIC void
2018-07-19 22:29:11 +03:00
xchk_bmap_xref_rmap (
struct xchk_bmap_info * info ,
2018-01-17 05:53:08 +03:00
struct xfs_bmbt_irec * irec ,
xfs_agblock_t agbno )
{
struct xfs_rmap_irec rmap ;
unsigned long long rmap_end ;
uint64_t owner ;
2018-07-19 22:29:11 +03:00
if ( ! info - > sc - > sa . rmap_cur | | xchk_skip_xref ( info - > sc - > sm ) )
2018-01-17 05:53:08 +03:00
return ;
if ( info - > whichfork = = XFS_COW_FORK )
owner = XFS_RMAP_OWN_COW ;
else
owner = info - > sc - > ip - > i_ino ;
/* Find the rmap record for this irec. */
2018-07-19 22:29:11 +03:00
if ( ! xchk_bmap_get_rmap ( info , irec , agbno , owner , & rmap ) )
2018-01-17 05:53:08 +03:00
return ;
/* Check the rmap. */
rmap_end = ( unsigned long long ) rmap . rm_startblock + rmap . rm_blockcount ;
if ( rmap . rm_startblock > agbno | |
agbno + irec - > br_blockcount > rmap_end )
2018-07-19 22:29:11 +03:00
xchk_fblock_xref_set_corrupt ( info - > sc , info - > whichfork ,
2018-01-17 05:53:08 +03:00
irec - > br_startoff ) ;
/*
* Check the logical offsets if applicable . CoW staging extents
* don ' t track logical offsets since the mappings only exist in
* memory .
*/
if ( info - > whichfork ! = XFS_COW_FORK ) {
rmap_end = ( unsigned long long ) rmap . rm_offset +
rmap . rm_blockcount ;
if ( rmap . rm_offset > irec - > br_startoff | |
irec - > br_startoff + irec - > br_blockcount > rmap_end )
2018-07-19 22:29:11 +03:00
xchk_fblock_xref_set_corrupt ( info - > sc ,
2018-01-17 05:53:08 +03:00
info - > whichfork , irec - > br_startoff ) ;
}
if ( rmap . rm_owner ! = owner )
2018-07-19 22:29:11 +03:00
xchk_fblock_xref_set_corrupt ( info - > sc , info - > whichfork ,
2018-01-17 05:53:08 +03:00
irec - > br_startoff ) ;
/*
* Check for discrepancies between the unwritten flag in the irec and
* the rmap . Note that the ( in - memory ) CoW fork distinguishes between
* unwritten and written extents , but we don ' t track that in the rmap
* records because the blocks are owned ( on - disk ) by the refcountbt ,
* which doesn ' t track unwritten state .
*/
if ( owner ! = XFS_RMAP_OWN_COW & &
irec - > br_state = = XFS_EXT_UNWRITTEN & &
! ( rmap . rm_flags & XFS_RMAP_UNWRITTEN ) )
2018-07-19 22:29:11 +03:00
xchk_fblock_xref_set_corrupt ( info - > sc , info - > whichfork ,
2018-01-17 05:53:08 +03:00
irec - > br_startoff ) ;
if ( info - > whichfork = = XFS_ATTR_FORK & &
! ( rmap . rm_flags & XFS_RMAP_ATTR_FORK ) )
2018-07-19 22:29:11 +03:00
xchk_fblock_xref_set_corrupt ( info - > sc , info - > whichfork ,
2018-01-17 05:53:08 +03:00
irec - > br_startoff ) ;
if ( rmap . rm_flags & XFS_RMAP_BMBT_BLOCK )
2018-07-19 22:29:11 +03:00
xchk_fblock_xref_set_corrupt ( info - > sc , info - > whichfork ,
2018-01-17 05:53:08 +03:00
irec - > br_startoff ) ;
}
2018-01-17 05:53:05 +03:00
/* Cross-reference a single rtdev extent record. */
STATIC void
2018-07-19 22:29:11 +03:00
xchk_bmap_rt_extent_xref (
struct xchk_bmap_info * info ,
2018-01-17 05:53:05 +03:00
struct xfs_inode * ip ,
struct xfs_btree_cur * cur ,
struct xfs_bmbt_irec * irec )
{
if ( info - > sc - > sm - > sm_flags & XFS_SCRUB_OFLAG_CORRUPT )
return ;
2018-01-17 05:53:10 +03:00
2018-07-19 22:29:11 +03:00
xchk_xref_is_used_rt_space ( info - > sc , irec - > br_startblock ,
2018-01-17 05:53:10 +03:00
irec - > br_blockcount ) ;
2018-01-17 05:53:05 +03:00
}
/* Cross-reference a single datadev extent record. */
STATIC void
2018-07-19 22:29:11 +03:00
xchk_bmap_extent_xref (
struct xchk_bmap_info * info ,
2018-01-17 05:53:05 +03:00
struct xfs_inode * ip ,
struct xfs_btree_cur * cur ,
struct xfs_bmbt_irec * irec )
{
2018-01-17 05:53:06 +03:00
struct xfs_mount * mp = info - > sc - > mp ;
xfs_agnumber_t agno ;
xfs_agblock_t agbno ;
xfs_extlen_t len ;
int error ;
2018-01-17 05:53:05 +03:00
if ( info - > sc - > sm - > sm_flags & XFS_SCRUB_OFLAG_CORRUPT )
return ;
2018-01-17 05:53:06 +03:00
agno = XFS_FSB_TO_AGNO ( mp , irec - > br_startblock ) ;
agbno = XFS_FSB_TO_AGBNO ( mp , irec - > br_startblock ) ;
len = irec - > br_blockcount ;
2018-07-19 22:29:11 +03:00
error = xchk_ag_init ( info - > sc , agno , & info - > sc - > sa ) ;
if ( ! xchk_fblock_process_error ( info - > sc , info - > whichfork ,
2018-01-17 05:53:06 +03:00
irec - > br_startoff , & error ) )
return ;
2018-07-19 22:29:11 +03:00
xchk_xref_is_used_space ( info - > sc , agbno , len ) ;
xchk_xref_is_not_inode_chunk ( info - > sc , agbno , len ) ;
xchk_bmap_xref_rmap ( info , irec , agbno ) ;
2018-01-17 05:53:09 +03:00
switch ( info - > whichfork ) {
case XFS_DATA_FORK :
if ( xfs_is_reflink_inode ( info - > sc - > ip ) )
break ;
/* fall through */
case XFS_ATTR_FORK :
2018-07-19 22:29:11 +03:00
xchk_xref_is_not_shared ( info - > sc , agbno ,
2018-01-17 05:53:09 +03:00
irec - > br_blockcount ) ;
break ;
case XFS_COW_FORK :
2018-07-19 22:29:11 +03:00
xchk_xref_is_cow_staging ( info - > sc , agbno ,
2018-01-17 05:53:09 +03:00
irec - > br_blockcount ) ;
break ;
}
2018-01-17 05:53:06 +03:00
2018-07-19 22:29:11 +03:00
xchk_ag_free ( info - > sc , & info - > sc - > sa ) ;
2018-01-17 05:53:05 +03:00
}
2017-10-18 07:37:43 +03:00
/* Scrub a single extent record. */
STATIC int
2018-07-19 22:29:11 +03:00
xchk_bmap_extent (
2017-10-18 07:37:43 +03:00
struct xfs_inode * ip ,
struct xfs_btree_cur * cur ,
2018-07-19 22:29:11 +03:00
struct xchk_bmap_info * info ,
2017-10-18 07:37:43 +03:00
struct xfs_bmbt_irec * irec )
{
struct xfs_mount * mp = info - > sc - > mp ;
struct xfs_buf * bp = NULL ;
2018-01-17 05:54:13 +03:00
xfs_filblks_t end ;
2017-10-18 07:37:43 +03:00
int error = 0 ;
if ( cur )
xfs_btree_get_block ( cur , 0 , & bp ) ;
/*
* Check for out - of - order extents . This record could have come
* from the incore list , for which there is no ordering check .
*/
if ( irec - > br_startoff < info - > lastoff )
2018-07-19 22:29:11 +03:00
xchk_fblock_set_corrupt ( info - > sc , info - > whichfork ,
2017-10-18 07:37:43 +03:00
irec - > br_startoff ) ;
/* There should never be a "hole" extent in either extent list. */
if ( irec - > br_startblock = = HOLESTARTBLOCK )
2018-07-19 22:29:11 +03:00
xchk_fblock_set_corrupt ( info - > sc , info - > whichfork ,
2017-10-18 07:37:43 +03:00
irec - > br_startoff ) ;
/*
* Check for delalloc extents . We never iterate the ones in the
* in - core extent scan , and we should never see these in the bmbt .
*/
if ( isnullstartblock ( irec - > br_startblock ) )
2018-07-19 22:29:11 +03:00
xchk_fblock_set_corrupt ( info - > sc , info - > whichfork ,
2017-10-18 07:37:43 +03:00
irec - > br_startoff ) ;
/* Make sure the extent points to a valid place. */
2018-01-17 05:54:13 +03:00
if ( irec - > br_blockcount > MAXEXTLEN )
2018-07-19 22:29:11 +03:00
xchk_fblock_set_corrupt ( info - > sc , info - > whichfork ,
2018-01-17 05:54:13 +03:00
irec - > br_startoff ) ;
2017-10-18 07:37:43 +03:00
if ( irec - > br_startblock + irec - > br_blockcount < = irec - > br_startblock )
2018-07-19 22:29:11 +03:00
xchk_fblock_set_corrupt ( info - > sc , info - > whichfork ,
2017-10-18 07:37:43 +03:00
irec - > br_startoff ) ;
2018-01-17 05:54:13 +03:00
end = irec - > br_startblock + irec - > br_blockcount - 1 ;
2017-10-18 07:37:43 +03:00
if ( info - > is_rt & &
( ! xfs_verify_rtbno ( mp , irec - > br_startblock ) | |
2018-01-17 05:54:13 +03:00
! xfs_verify_rtbno ( mp , end ) ) )
2018-07-19 22:29:11 +03:00
xchk_fblock_set_corrupt ( info - > sc , info - > whichfork ,
2017-10-18 07:37:43 +03:00
irec - > br_startoff ) ;
if ( ! info - > is_rt & &
( ! xfs_verify_fsbno ( mp , irec - > br_startblock ) | |
2018-01-17 05:54:13 +03:00
! xfs_verify_fsbno ( mp , end ) | |
XFS_FSB_TO_AGNO ( mp , irec - > br_startblock ) ! =
XFS_FSB_TO_AGNO ( mp , end ) ) )
2018-07-19 22:29:11 +03:00
xchk_fblock_set_corrupt ( info - > sc , info - > whichfork ,
2017-10-18 07:37:43 +03:00
irec - > br_startoff ) ;
/* We don't allow unwritten extents on attr forks. */
if ( irec - > br_state = = XFS_EXT_UNWRITTEN & &
info - > whichfork = = XFS_ATTR_FORK )
2018-07-19 22:29:11 +03:00
xchk_fblock_set_corrupt ( info - > sc , info - > whichfork ,
2017-10-18 07:37:43 +03:00
irec - > br_startoff ) ;
2018-01-17 05:53:05 +03:00
if ( info - > is_rt )
2018-07-19 22:29:11 +03:00
xchk_bmap_rt_extent_xref ( info , ip , cur , irec ) ;
2018-01-17 05:53:05 +03:00
else
2018-07-19 22:29:11 +03:00
xchk_bmap_extent_xref ( info , ip , cur , irec ) ;
2018-01-17 05:53:05 +03:00
2017-10-18 07:37:43 +03:00
info - > lastoff = irec - > br_startoff + irec - > br_blockcount ;
return error ;
}
/* Scrub a bmbt record. */
STATIC int
2018-07-19 22:29:11 +03:00
xchk_bmapbt_rec (
struct xchk_btree * bs ,
2017-10-18 07:37:43 +03:00
union xfs_btree_rec * rec )
{
struct xfs_bmbt_irec irec ;
2018-07-19 22:29:11 +03:00
struct xchk_bmap_info * info = bs - > private ;
2017-10-18 07:37:43 +03:00
struct xfs_inode * ip = bs - > cur - > bc_private . b . ip ;
struct xfs_buf * bp = NULL ;
struct xfs_btree_block * block ;
uint64_t owner ;
int i ;
/*
* Check the owners of the btree blocks up to the level below
* the root since the verifiers don ' t do that .
*/
if ( xfs_sb_version_hascrc ( & bs - > cur - > bc_mp - > m_sb ) & &
bs - > cur - > bc_ptrs [ 0 ] = = 1 ) {
for ( i = 0 ; i < bs - > cur - > bc_nlevels - 1 ; i + + ) {
block = xfs_btree_get_block ( bs - > cur , i , & bp ) ;
owner = be64_to_cpu ( block - > bb_u . l . bb_owner ) ;
if ( owner ! = ip - > i_ino )
2018-07-19 22:29:11 +03:00
xchk_fblock_set_corrupt ( bs - > sc ,
2017-10-18 07:37:43 +03:00
info - > whichfork , 0 ) ;
}
}
/* Set up the in-core record and scrub it. */
2017-11-03 20:34:46 +03:00
xfs_bmbt_disk_get_all ( & rec - > bmbt , & irec ) ;
2018-07-19 22:29:11 +03:00
return xchk_bmap_extent ( ip , bs - > cur , info , & irec ) ;
2017-10-18 07:37:43 +03:00
}
/* Scan the btree records. */
STATIC int
2018-07-19 22:29:11 +03:00
xchk_bmap_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:11 +03:00
struct xchk_bmap_info * info )
2017-10-18 07:37:43 +03:00
{
struct xfs_owner_info oinfo ;
struct xfs_mount * mp = sc - > mp ;
struct xfs_inode * ip = sc - > ip ;
struct xfs_btree_cur * cur ;
int error ;
cur = xfs_bmbt_init_cursor ( mp , sc - > tp , ip , whichfork ) ;
xfs_rmap_ino_bmbt_owner ( & oinfo , ip - > i_ino , whichfork ) ;
2018-07-19 22:29:11 +03:00
error = xchk_btree ( sc , cur , xchk_bmapbt_rec , & oinfo , info ) ;
2018-07-19 22:26:31 +03:00
xfs_btree_del_cursor ( cur , error ) ;
2017-10-18 07:37:43 +03:00
return error ;
}
2018-07-19 22:29:11 +03:00
struct xchk_bmap_check_rmap_info {
2018-07-19 22:29:12 +03:00
struct xfs_scrub * sc ;
2018-03-23 20:06:53 +03:00
int whichfork ;
struct xfs_iext_cursor icur ;
} ;
/* Can we find bmaps that fit this rmap? */
STATIC int
2018-07-19 22:29:11 +03:00
xchk_bmap_check_rmap (
2018-03-23 20:06:53 +03:00
struct xfs_btree_cur * cur ,
struct xfs_rmap_irec * rec ,
void * priv )
{
struct xfs_bmbt_irec irec ;
2018-07-19 22:29:11 +03:00
struct xchk_bmap_check_rmap_info * sbcri = priv ;
2018-03-23 20:06:53 +03:00
struct xfs_ifork * ifp ;
2018-07-19 22:29:12 +03:00
struct xfs_scrub * sc = sbcri - > sc ;
2018-03-23 20:06:53 +03:00
bool have_map ;
/* Is this even the right fork? */
if ( rec - > rm_owner ! = sc - > ip - > i_ino )
return 0 ;
if ( ( sbcri - > whichfork = = XFS_ATTR_FORK ) ^
! ! ( rec - > rm_flags & XFS_RMAP_ATTR_FORK ) )
return 0 ;
if ( rec - > rm_flags & XFS_RMAP_BMBT_BLOCK )
return 0 ;
/* Now look up the bmbt record. */
ifp = XFS_IFORK_PTR ( sc - > ip , sbcri - > whichfork ) ;
if ( ! ifp ) {
2018-07-19 22:29:11 +03:00
xchk_fblock_set_corrupt ( sc , sbcri - > whichfork ,
2018-03-23 20:06:53 +03:00
rec - > rm_offset ) ;
goto out ;
}
have_map = xfs_iext_lookup_extent ( sc - > ip , ifp , rec - > rm_offset ,
& sbcri - > icur , & irec ) ;
if ( ! have_map )
2018-07-19 22:29:11 +03:00
xchk_fblock_set_corrupt ( sc , sbcri - > whichfork ,
2018-03-23 20:06:53 +03:00
rec - > rm_offset ) ;
/*
* bmap extent record lengths are constrained to 2 ^ 21 blocks in length
* because of space constraints in the on - disk metadata structure .
* However , rmap extent record lengths are constrained only by AG
* length , so we have to loop through the bmbt to make sure that the
* entire rmap is covered by bmbt records .
*/
while ( have_map ) {
if ( irec . br_startoff ! = rec - > rm_offset )
2018-07-19 22:29:11 +03:00
xchk_fblock_set_corrupt ( sc , sbcri - > whichfork ,
2018-03-23 20:06:53 +03:00
rec - > rm_offset ) ;
if ( irec . br_startblock ! = XFS_AGB_TO_FSB ( sc - > mp ,
cur - > bc_private . a . agno , rec - > rm_startblock ) )
2018-07-19 22:29:11 +03:00
xchk_fblock_set_corrupt ( sc , sbcri - > whichfork ,
2018-03-23 20:06:53 +03:00
rec - > rm_offset ) ;
if ( irec . br_blockcount > rec - > rm_blockcount )
2018-07-19 22:29:11 +03:00
xchk_fblock_set_corrupt ( sc , sbcri - > whichfork ,
2018-03-23 20:06:53 +03:00
rec - > rm_offset ) ;
if ( sc - > sm - > sm_flags & XFS_SCRUB_OFLAG_CORRUPT )
break ;
rec - > rm_startblock + = irec . br_blockcount ;
rec - > rm_offset + = irec . br_blockcount ;
rec - > rm_blockcount - = irec . br_blockcount ;
if ( rec - > rm_blockcount = = 0 )
break ;
have_map = xfs_iext_next_extent ( ifp , & sbcri - > icur , & irec ) ;
if ( ! have_map )
2018-07-19 22:29:11 +03:00
xchk_fblock_set_corrupt ( sc , sbcri - > whichfork ,
2018-03-23 20:06:53 +03:00
rec - > rm_offset ) ;
}
out :
if ( sc - > sm - > sm_flags & XFS_SCRUB_OFLAG_CORRUPT )
return XFS_BTREE_QUERY_RANGE_ABORT ;
return 0 ;
}
/* Make sure each rmap has a corresponding bmbt entry. */
STATIC int
2018-07-19 22:29:11 +03:00
xchk_bmap_check_ag_rmaps (
2018-07-19 22:29:12 +03:00
struct xfs_scrub * sc ,
2018-03-23 20:06:53 +03:00
int whichfork ,
xfs_agnumber_t agno )
{
2018-07-19 22:29:11 +03:00
struct xchk_bmap_check_rmap_info sbcri ;
2018-03-23 20:06:53 +03:00
struct xfs_btree_cur * cur ;
struct xfs_buf * agf ;
int error ;
error = xfs_alloc_read_agf ( sc - > mp , sc - > tp , agno , 0 , & agf ) ;
if ( error )
return error ;
cur = xfs_rmapbt_init_cursor ( sc - > mp , sc - > tp , agf , agno ) ;
if ( ! cur ) {
error = - ENOMEM ;
goto out_agf ;
}
sbcri . sc = sc ;
sbcri . whichfork = whichfork ;
2018-07-19 22:29:11 +03:00
error = xfs_rmap_query_all ( cur , xchk_bmap_check_rmap , & sbcri ) ;
2018-03-23 20:06:53 +03:00
if ( error = = XFS_BTREE_QUERY_RANGE_ABORT )
error = 0 ;
2018-07-19 22:26:31 +03:00
xfs_btree_del_cursor ( cur , error ) ;
2018-03-23 20:06:53 +03:00
out_agf :
xfs_trans_brelse ( sc - > tp , agf ) ;
return error ;
}
/* Make sure each rmap has a corresponding bmbt entry. */
STATIC int
2018-07-19 22:29:11 +03:00
xchk_bmap_check_rmaps (
2018-07-19 22:29:12 +03:00
struct xfs_scrub * sc ,
2018-03-23 20:06:53 +03:00
int whichfork )
{
loff_t size ;
xfs_agnumber_t agno ;
int error ;
if ( ! xfs_sb_version_hasrmapbt ( & sc - > mp - > m_sb ) | |
whichfork = = XFS_COW_FORK | |
( sc - > sm - > sm_flags & XFS_SCRUB_OFLAG_CORRUPT ) )
return 0 ;
/* Don't support realtime rmap checks yet. */
if ( XFS_IS_REALTIME_INODE ( sc - > ip ) & & whichfork = = XFS_DATA_FORK )
return 0 ;
/*
* Only do this for complex maps that are in btree format , or for
* situations where we would seem to have a size but zero extents .
* The inode repair code can zap broken iforks , which means we have
* to flag this bmap as corrupt if there are rmaps that need to be
* reattached .
*/
switch ( whichfork ) {
case XFS_DATA_FORK :
size = i_size_read ( VFS_I ( sc - > ip ) ) ;
break ;
case XFS_ATTR_FORK :
size = XFS_IFORK_Q ( sc - > ip ) ;
break ;
default :
size = 0 ;
break ;
}
if ( XFS_IFORK_FORMAT ( sc - > ip , whichfork ) ! = XFS_DINODE_FMT_BTREE & &
( size = = 0 | | XFS_IFORK_NEXTENTS ( sc - > ip , whichfork ) > 0 ) )
return 0 ;
for ( agno = 0 ; agno < sc - > mp - > m_sb . sb_agcount ; agno + + ) {
2018-07-19 22:29:11 +03:00
error = xchk_bmap_check_ag_rmaps ( sc , whichfork , agno ) ;
2018-03-23 20:06:53 +03:00
if ( error )
return error ;
if ( sc - > sm - > sm_flags & XFS_SCRUB_OFLAG_CORRUPT )
break ;
}
return 0 ;
}
2017-10-18 07:37:43 +03:00
/*
* Scrub an inode fork ' s block mappings .
*
* First we scan every record in every btree block , if applicable .
* Then we unconditionally scan the incore extent cache .
*/
STATIC int
2018-07-19 22:29:11 +03:00
xchk_bmap (
2018-07-19 22:29:12 +03:00
struct xfs_scrub * sc ,
2017-10-18 07:37:43 +03:00
int whichfork )
{
struct xfs_bmbt_irec irec ;
2018-07-19 22:29:11 +03:00
struct xchk_bmap_info info = { NULL } ;
2017-10-18 07:37:43 +03:00
struct xfs_mount * mp = sc - > mp ;
struct xfs_inode * ip = sc - > ip ;
struct xfs_ifork * ifp ;
xfs_fileoff_t endoff ;
2017-11-03 20:34:43 +03:00
struct xfs_iext_cursor icur ;
2017-10-18 07:37:43 +03:00
int error = 0 ;
ifp = XFS_IFORK_PTR ( ip , whichfork ) ;
info . is_rt = whichfork = = XFS_DATA_FORK & & XFS_IS_REALTIME_INODE ( ip ) ;
info . whichfork = whichfork ;
info . is_shared = whichfork = = XFS_DATA_FORK & & xfs_is_reflink_inode ( ip ) ;
info . sc = sc ;
switch ( whichfork ) {
case XFS_COW_FORK :
/* Non-existent CoW forks are ignorable. */
if ( ! ifp )
goto out ;
/* No CoW forks on non-reflink inodes/filesystems. */
if ( ! xfs_is_reflink_inode ( ip ) ) {
2018-07-19 22:29:11 +03:00
xchk_ino_set_corrupt ( sc , sc - > ip - > i_ino ) ;
2017-10-18 07:37:43 +03:00
goto out ;
}
break ;
case XFS_ATTR_FORK :
if ( ! ifp )
2018-03-23 20:06:53 +03:00
goto out_check_rmap ;
2017-10-18 07:37:43 +03:00
if ( ! xfs_sb_version_hasattr ( & mp - > m_sb ) & &
! xfs_sb_version_hasattr2 ( & mp - > m_sb ) )
2018-07-19 22:29:11 +03:00
xchk_ino_set_corrupt ( sc , sc - > ip - > i_ino ) ;
2017-10-18 07:37:43 +03:00
break ;
default :
ASSERT ( whichfork = = XFS_DATA_FORK ) ;
break ;
}
/* Check the fork values */
switch ( XFS_IFORK_FORMAT ( ip , whichfork ) ) {
case XFS_DINODE_FMT_UUID :
case XFS_DINODE_FMT_DEV :
case XFS_DINODE_FMT_LOCAL :
/* No mappings to check. */
goto out ;
case XFS_DINODE_FMT_EXTENTS :
if ( ! ( ifp - > if_flags & XFS_IFEXTENTS ) ) {
2018-07-19 22:29:11 +03:00
xchk_fblock_set_corrupt ( sc , whichfork , 0 ) ;
2017-10-18 07:37:43 +03:00
goto out ;
}
break ;
case XFS_DINODE_FMT_BTREE :
if ( whichfork = = XFS_COW_FORK ) {
2018-07-19 22:29:11 +03:00
xchk_fblock_set_corrupt ( sc , whichfork , 0 ) ;
2017-10-18 07:37:43 +03:00
goto out ;
}
2018-07-19 22:29:11 +03:00
error = xchk_bmap_btree ( sc , whichfork , & info ) ;
2017-10-18 07:37:43 +03:00
if ( error )
goto out ;
break ;
default :
2018-07-19 22:29:11 +03:00
xchk_fblock_set_corrupt ( sc , whichfork , 0 ) ;
2017-10-18 07:37:43 +03:00
goto out ;
}
if ( sc - > sm - > sm_flags & XFS_SCRUB_OFLAG_CORRUPT )
goto out ;
/* Now try to scrub the in-memory extent list. */
if ( ! ( ifp - > if_flags & XFS_IFEXTENTS ) ) {
error = xfs_iread_extents ( sc - > tp , ip , whichfork ) ;
2018-07-19 22:29:11 +03:00
if ( ! xchk_fblock_process_error ( sc , whichfork , 0 , & error ) )
2017-10-18 07:37:43 +03:00
goto out ;
}
/* Find the offset of the last extent in the mapping. */
error = xfs_bmap_last_offset ( ip , & endoff , whichfork ) ;
2018-07-19 22:29:11 +03:00
if ( ! xchk_fblock_process_error ( sc , whichfork , 0 , & error ) )
2017-10-18 07:37:43 +03:00
goto out ;
/* Scrub extent records. */
info . lastoff = 0 ;
ifp = XFS_IFORK_PTR ( ip , whichfork ) ;
2018-01-08 21:49:03 +03:00
for_each_xfs_iext ( ifp , & icur , & irec ) {
2018-07-19 22:29:11 +03:00
if ( xchk_should_terminate ( sc , & error ) | |
2018-05-14 16:34:32 +03:00
( sc - > sm - > sm_flags & XFS_SCRUB_OFLAG_CORRUPT ) )
2017-10-18 07:37:43 +03:00
break ;
if ( isnullstartblock ( irec . br_startblock ) )
continue ;
if ( irec . br_startoff > = endoff ) {
2018-07-19 22:29:11 +03:00
xchk_fblock_set_corrupt ( sc , whichfork ,
2017-10-18 07:37:43 +03:00
irec . br_startoff ) ;
goto out ;
}
2018-07-19 22:29:11 +03:00
error = xchk_bmap_extent ( ip , NULL , & info , & irec ) ;
2017-10-18 07:37:43 +03:00
if ( error )
goto out ;
}
2018-03-23 20:06:53 +03:00
out_check_rmap :
2018-07-19 22:29:11 +03:00
error = xchk_bmap_check_rmaps ( sc , whichfork ) ;
if ( ! xchk_fblock_xref_process_error ( sc , whichfork , 0 , & error ) )
2018-03-23 20:06:53 +03:00
goto out ;
2017-10-18 07:37:43 +03:00
out :
return error ;
}
/* Scrub an inode's data fork. */
int
2018-07-19 22:29:11 +03:00
xchk_bmap_data (
2018-07-19 22:29:12 +03:00
struct xfs_scrub * sc )
2017-10-18 07:37:43 +03:00
{
2018-07-19 22:29:11 +03:00
return xchk_bmap ( sc , XFS_DATA_FORK ) ;
2017-10-18 07:37:43 +03:00
}
/* Scrub an inode's attr fork. */
int
2018-07-19 22:29:11 +03:00
xchk_bmap_attr (
2018-07-19 22:29:12 +03:00
struct xfs_scrub * sc )
2017-10-18 07:37:43 +03:00
{
2018-07-19 22:29:11 +03:00
return xchk_bmap ( sc , XFS_ATTR_FORK ) ;
2017-10-18 07:37:43 +03:00
}
/* Scrub an inode's CoW fork. */
int
2018-07-19 22:29:11 +03:00
xchk_bmap_cow (
2018-07-19 22:29:12 +03:00
struct xfs_scrub * sc )
2017-10-18 07:37:43 +03:00
{
if ( ! xfs_is_reflink_inode ( sc - > ip ) )
return - ENOENT ;
2018-07-19 22:29:11 +03:00
return xchk_bmap ( sc , XFS_COW_FORK ) ;
2017-10-18 07:37:43 +03:00
}