2018-06-05 19:42:14 -07:00
// SPDX-License-Identifier: GPL-2.0+
2017-10-17 21:37:42 -07: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_icache.h"
# include "xfs_inode_buf.h"
# include "xfs_inode_fork.h"
# include "xfs_ialloc.h"
# include "xfs_da_format.h"
# include "xfs_reflink.h"
2018-01-16 18:53:08 -08:00
# include "xfs_rmap.h"
2018-01-16 18:53:10 -08:00
# include "xfs_bmap.h"
# include "xfs_bmap_util.h"
2017-10-17 21:37:42 -07:00
# include "scrub/xfs_scrub.h"
# include "scrub/scrub.h"
# include "scrub/common.h"
2018-01-16 18:53:07 -08:00
# include "scrub/btree.h"
2017-10-17 21:37:42 -07:00
# include "scrub/trace.h"
/*
* Grab total control of the inode metadata . It doesn ' t matter here if
* the file data is still changing ; exclusive access to the metadata is
* the goal .
*/
int
2018-07-19 12:29:11 -07:00
xchk_setup_inode (
2018-07-19 12:29:12 -07:00
struct xfs_scrub * sc ,
2018-07-19 12:29:12 -07:00
struct xfs_inode * ip )
2017-10-17 21:37:42 -07:00
{
2018-07-19 12:29:12 -07:00
int error ;
2017-10-17 21:37:42 -07:00
/*
* Try to get the inode . If the verifiers fail , we try again
* in raw mode .
*/
2018-07-19 12:29:11 -07:00
error = xchk_get_inode ( sc , ip ) ;
2017-10-17 21:37:42 -07:00
switch ( error ) {
case 0 :
break ;
case - EFSCORRUPTED :
case - EFSBADCRC :
2018-07-19 12:29:11 -07:00
return xchk_trans_alloc ( sc , 0 ) ;
2017-10-17 21:37:42 -07:00
default :
return error ;
}
/* Got the inode, lock it and we're ready to go. */
sc - > ilock_flags = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL ;
xfs_ilock ( sc - > ip , sc - > ilock_flags ) ;
2018-07-19 12:29:11 -07:00
error = xchk_trans_alloc ( sc , 0 ) ;
2017-10-17 21:37:42 -07: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 for us */
return error ;
}
/* Inode core */
2018-03-23 10:06:55 -07:00
/* Validate di_extsize hint. */
2017-10-17 21:37:42 -07:00
STATIC void
2018-07-19 12:29:11 -07:00
xchk_inode_extsize (
2018-07-19 12:29:12 -07:00
struct xfs_scrub * sc ,
2018-07-19 12:29:12 -07:00
struct xfs_dinode * dip ,
xfs_ino_t ino ,
uint16_t mode ,
uint16_t flags )
2017-10-17 21:37:42 -07:00
{
2018-07-19 12:29:12 -07:00
xfs_failaddr_t fa ;
2017-10-17 21:37:42 -07:00
2018-03-23 10:06:55 -07:00
fa = xfs_inode_validate_extsize ( sc - > mp , be32_to_cpu ( dip - > di_extsize ) ,
mode , flags ) ;
if ( fa )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
}
/*
* Validate di_cowextsize hint .
*
* The rules are documented at xfs_ioctl_setattr_check_cowextsize ( ) .
* These functions must be kept in sync with each other .
*/
STATIC void
2018-07-19 12:29:11 -07:00
xchk_inode_cowextsize (
2018-07-19 12:29:12 -07:00
struct xfs_scrub * sc ,
2018-07-19 12:29:12 -07:00
struct xfs_dinode * dip ,
xfs_ino_t ino ,
uint16_t mode ,
uint16_t flags ,
uint64_t flags2 )
2017-10-17 21:37:42 -07:00
{
2018-07-19 12:29:12 -07:00
xfs_failaddr_t fa ;
2017-10-17 21:37:42 -07:00
2018-03-23 10:06:55 -07:00
fa = xfs_inode_validate_cowextsize ( sc - > mp ,
be32_to_cpu ( dip - > di_cowextsize ) , mode , flags ,
flags2 ) ;
if ( fa )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
}
/* Make sure the di_flags make sense for the inode. */
STATIC void
2018-07-19 12:29:11 -07:00
xchk_inode_flags (
2018-07-19 12:29:12 -07:00
struct xfs_scrub * sc ,
2018-07-19 12:29:12 -07:00
struct xfs_dinode * dip ,
xfs_ino_t ino ,
uint16_t mode ,
uint16_t flags )
2017-10-17 21:37:42 -07:00
{
2018-07-19 12:29:12 -07:00
struct xfs_mount * mp = sc - > mp ;
2017-10-17 21:37:42 -07:00
2018-09-29 13:49:00 +10:00
/* di_flags are all taken, last bit cannot be used */
2017-10-17 21:37:42 -07:00
if ( flags & ~ XFS_DIFLAG_ANY )
goto bad ;
/* rt flags require rt device */
if ( ( flags & ( XFS_DIFLAG_REALTIME | XFS_DIFLAG_RTINHERIT ) ) & &
! mp - > m_rtdev_targp )
goto bad ;
/* new rt bitmap flag only valid for rbmino */
if ( ( flags & XFS_DIFLAG_NEWRTBM ) & & ino ! = mp - > m_sb . sb_rbmino )
goto bad ;
/* directory-only flags */
if ( ( flags & ( XFS_DIFLAG_RTINHERIT |
XFS_DIFLAG_EXTSZINHERIT |
XFS_DIFLAG_PROJINHERIT |
XFS_DIFLAG_NOSYMLINKS ) ) & &
! S_ISDIR ( mode ) )
goto bad ;
/* file-only flags */
if ( ( flags & ( XFS_DIFLAG_REALTIME | FS_XFLAG_EXTSIZE ) ) & &
! S_ISREG ( mode ) )
goto bad ;
/* filestreams and rt make no sense */
if ( ( flags & XFS_DIFLAG_FILESTREAM ) & & ( flags & XFS_DIFLAG_REALTIME ) )
goto bad ;
return ;
bad :
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
}
/* Make sure the di_flags2 make sense for the inode. */
STATIC void
2018-07-19 12:29:11 -07:00
xchk_inode_flags2 (
2018-07-19 12:29:12 -07:00
struct xfs_scrub * sc ,
2018-07-19 12:29:12 -07:00
struct xfs_dinode * dip ,
xfs_ino_t ino ,
uint16_t mode ,
uint16_t flags ,
uint64_t flags2 )
2017-10-17 21:37:42 -07:00
{
2018-07-19 12:29:12 -07:00
struct xfs_mount * mp = sc - > mp ;
2017-10-17 21:37:42 -07:00
2018-09-29 13:49:00 +10:00
/* Unknown di_flags2 could be from a future kernel */
2017-10-17 21:37:42 -07:00
if ( flags2 & ~ XFS_DIFLAG2_ANY )
2018-09-29 13:49:00 +10:00
xchk_ino_set_warning ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
/* reflink flag requires reflink feature */
if ( ( flags2 & XFS_DIFLAG2_REFLINK ) & &
! xfs_sb_version_hasreflink ( & mp - > m_sb ) )
goto bad ;
/* cowextsize flag is checked w.r.t. mode separately */
/* file/dir-only flags */
if ( ( flags2 & XFS_DIFLAG2_DAX ) & & ! ( S_ISREG ( mode ) | | S_ISDIR ( mode ) ) )
goto bad ;
/* file-only flags */
if ( ( flags2 & XFS_DIFLAG2_REFLINK ) & & ! S_ISREG ( mode ) )
goto bad ;
/* realtime and reflink make no sense, currently */
if ( ( flags & XFS_DIFLAG_REALTIME ) & & ( flags2 & XFS_DIFLAG2_REFLINK ) )
goto bad ;
/* dax and reflink make no sense, currently */
if ( ( flags2 & XFS_DIFLAG2_DAX ) & & ( flags2 & XFS_DIFLAG2_REFLINK ) )
goto bad ;
return ;
bad :
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
}
/* Scrub all the ondisk inode fields. */
STATIC void
2018-07-19 12:29:11 -07:00
xchk_dinode (
2018-07-19 12:29:12 -07:00
struct xfs_scrub * sc ,
2018-07-19 12:29:12 -07:00
struct xfs_dinode * dip ,
xfs_ino_t ino )
2017-10-17 21:37:42 -07:00
{
2018-07-19 12:29:12 -07:00
struct xfs_mount * mp = sc - > mp ;
size_t fork_recs ;
unsigned long long isize ;
uint64_t flags2 ;
uint32_t nextents ;
uint16_t flags ;
uint16_t mode ;
2017-10-17 21:37:42 -07:00
flags = be16_to_cpu ( dip - > di_flags ) ;
if ( dip - > di_version > = 3 )
flags2 = be64_to_cpu ( dip - > di_flags2 ) ;
else
flags2 = 0 ;
/* di_mode */
mode = be16_to_cpu ( dip - > di_mode ) ;
2017-11-27 21:40:19 -08:00
switch ( mode & S_IFMT ) {
case S_IFLNK :
case S_IFREG :
case S_IFDIR :
case S_IFCHR :
case S_IFBLK :
case S_IFIFO :
case S_IFSOCK :
/* mode is recognized */
break ;
default :
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-11-27 21:40:19 -08:00
break ;
}
2017-10-17 21:37:42 -07:00
/* v1/v2 fields */
switch ( dip - > di_version ) {
case 1 :
/*
* We autoconvert v1 inodes into v2 inodes on writeout ,
* so just mark this inode for preening .
*/
2018-07-19 12:29:11 -07:00
xchk_ino_set_preen ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
break ;
case 2 :
case 3 :
if ( dip - > di_onlink ! = 0 )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
if ( dip - > di_mode = = 0 & & sc - > ip )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
if ( dip - > di_projid_hi ! = 0 & &
! xfs_sb_version_hasprojid32bit ( & mp - > m_sb ) )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
break ;
default :
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
return ;
}
/*
* di_uid / di_gid - - - 1 isn ' t invalid , but there ' s no way that
* userspace could have created that .
*/
if ( dip - > di_uid = = cpu_to_be32 ( - 1U ) | |
dip - > di_gid = = cpu_to_be32 ( - 1U ) )
2018-07-19 12:29:11 -07:00
xchk_ino_set_warning ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
/* di_format */
switch ( dip - > di_format ) {
case XFS_DINODE_FMT_DEV :
if ( ! S_ISCHR ( mode ) & & ! S_ISBLK ( mode ) & &
! S_ISFIFO ( mode ) & & ! S_ISSOCK ( mode ) )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
break ;
case XFS_DINODE_FMT_LOCAL :
if ( ! S_ISDIR ( mode ) & & ! S_ISLNK ( mode ) )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
break ;
case XFS_DINODE_FMT_EXTENTS :
if ( ! S_ISREG ( mode ) & & ! S_ISDIR ( mode ) & & ! S_ISLNK ( mode ) )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
break ;
case XFS_DINODE_FMT_BTREE :
if ( ! S_ISREG ( mode ) & & ! S_ISDIR ( mode ) )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
break ;
case XFS_DINODE_FMT_UUID :
default :
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
break ;
}
2018-01-08 10:41:35 -08:00
/* di_[amc]time.nsec */
if ( be32_to_cpu ( dip - > di_atime . t_nsec ) > = NSEC_PER_SEC )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2018-01-08 10:41:35 -08:00
if ( be32_to_cpu ( dip - > di_mtime . t_nsec ) > = NSEC_PER_SEC )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2018-01-08 10:41:35 -08:00
if ( be32_to_cpu ( dip - > di_ctime . t_nsec ) > = NSEC_PER_SEC )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2018-01-08 10:41:35 -08:00
2017-10-17 21:37:42 -07:00
/*
* di_size . xfs_dinode_verify checks for things that screw up
* the VFS such as the upper bit being set and zero - length
* symlinks / directories , but we can do more here .
*/
isize = be64_to_cpu ( dip - > di_size ) ;
if ( isize & ( 1ULL < < 63 ) )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
/* Devices, fifos, and sockets must have zero size */
if ( ! S_ISDIR ( mode ) & & ! S_ISREG ( mode ) & & ! S_ISLNK ( mode ) & & isize ! = 0 )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
/* Directories can't be larger than the data section size (32G) */
if ( S_ISDIR ( mode ) & & ( isize = = 0 | | isize > = XFS_DIR2_SPACE_SIZE ) )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
/* Symlinks can't be larger than SYMLINK_MAXLEN */
if ( S_ISLNK ( mode ) & & ( isize = = 0 | | isize > = XFS_SYMLINK_MAXLEN ) )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
/*
* Warn if the running kernel can ' t handle the kinds of offsets
* needed to deal with the file size . In other words , if the
* pagecache can ' t cache all the blocks in this file due to
* overly large offsets , flag the inode for admin review .
*/
if ( isize > = mp - > m_super - > s_maxbytes )
2018-07-19 12:29:11 -07:00
xchk_ino_set_warning ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
/* di_nblocks */
if ( flags2 & XFS_DIFLAG2_REFLINK ) {
; /* nblocks can exceed dblocks */
} else if ( flags & XFS_DIFLAG_REALTIME ) {
/*
* nblocks is the sum of data extents ( in the rtdev ) ,
* attr extents ( in the datadev ) , and both forks ' bmbt
* blocks ( in the datadev ) . This clumsy check is the
* best we can do without cross - referencing with the
* inode forks .
*/
if ( be64_to_cpu ( dip - > di_nblocks ) > =
mp - > m_sb . sb_dblocks + mp - > m_sb . sb_rblocks )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
} else {
if ( be64_to_cpu ( dip - > di_nblocks ) > = mp - > m_sb . sb_dblocks )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
}
2018-07-19 12:29:11 -07:00
xchk_inode_flags ( sc , dip , ino , mode , flags ) ;
2017-10-17 21:37:42 -07:00
2018-07-19 12:29:11 -07:00
xchk_inode_extsize ( sc , dip , ino , mode , flags ) ;
2017-10-17 21:37:42 -07:00
/* di_nextents */
nextents = be32_to_cpu ( dip - > di_nextents ) ;
fork_recs = XFS_DFORK_DSIZE ( dip , mp ) / sizeof ( struct xfs_bmbt_rec ) ;
switch ( dip - > di_format ) {
case XFS_DINODE_FMT_EXTENTS :
if ( nextents > fork_recs )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
break ;
case XFS_DINODE_FMT_BTREE :
if ( nextents < = fork_recs )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
break ;
default :
if ( nextents ! = 0 )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
break ;
}
/* di_forkoff */
if ( XFS_DFORK_APTR ( dip ) > = ( char * ) dip + mp - > m_sb . sb_inodesize )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
if ( dip - > di_anextents ! = 0 & & dip - > di_forkoff = = 0 )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
if ( dip - > di_forkoff = = 0 & & dip - > di_aformat ! = XFS_DINODE_FMT_EXTENTS )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
/* di_aformat */
if ( dip - > di_aformat ! = XFS_DINODE_FMT_LOCAL & &
dip - > di_aformat ! = XFS_DINODE_FMT_EXTENTS & &
dip - > di_aformat ! = XFS_DINODE_FMT_BTREE )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
/* di_anextents */
nextents = be16_to_cpu ( dip - > di_anextents ) ;
fork_recs = XFS_DFORK_ASIZE ( dip , mp ) / sizeof ( struct xfs_bmbt_rec ) ;
switch ( dip - > di_aformat ) {
case XFS_DINODE_FMT_EXTENTS :
if ( nextents > fork_recs )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
break ;
case XFS_DINODE_FMT_BTREE :
if ( nextents < = fork_recs )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
break ;
default :
if ( nextents ! = 0 )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2017-10-17 21:37:42 -07:00
}
if ( dip - > di_version > = 3 ) {
2018-01-08 10:41:35 -08:00
if ( be32_to_cpu ( dip - > di_crtime . t_nsec ) > = NSEC_PER_SEC )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
xchk_inode_flags2 ( sc , dip , ino , mode , flags , flags2 ) ;
xchk_inode_cowextsize ( sc , dip , ino , mode , flags ,
2017-10-17 21:37:42 -07:00
flags2 ) ;
}
}
2018-01-16 18:53:07 -08:00
/*
* Make sure the finobt doesn ' t think this inode is free .
* We don ' t have to check the inobt ourselves because we got the inode via
* IGET_UNTRUSTED , which checks the inobt for us .
*/
static void
2018-07-19 12:29:11 -07:00
xchk_inode_xref_finobt (
2018-07-19 12:29:12 -07:00
struct xfs_scrub * sc ,
2018-01-16 18:53:07 -08:00
xfs_ino_t ino )
{
struct xfs_inobt_rec_incore rec ;
xfs_agino_t agino ;
int has_record ;
int error ;
2018-07-19 12:29:11 -07:00
if ( ! sc - > sa . fino_cur | | xchk_skip_xref ( sc - > sm ) )
2018-01-16 18:53:07 -08:00
return ;
agino = XFS_INO_TO_AGINO ( sc - > mp , ino ) ;
/*
* Try to get the finobt record . If we can ' t get it , then we ' re
* in good shape .
*/
error = xfs_inobt_lookup ( sc - > sa . fino_cur , agino , XFS_LOOKUP_LE ,
& has_record ) ;
2018-07-19 12:29:11 -07:00
if ( ! xchk_should_check_xref ( sc , & error , & sc - > sa . fino_cur ) | |
2018-01-16 18:53:07 -08:00
! has_record )
return ;
error = xfs_inobt_get_rec ( sc - > sa . fino_cur , & rec , & has_record ) ;
2018-07-19 12:29:11 -07:00
if ( ! xchk_should_check_xref ( sc , & error , & sc - > sa . fino_cur ) | |
2018-01-16 18:53:07 -08:00
! has_record )
return ;
/*
* Otherwise , make sure this record either doesn ' t cover this inode ,
* or that it does but it ' s marked present .
*/
if ( rec . ir_startino > agino | |
rec . ir_startino + XFS_INODES_PER_CHUNK < = agino )
return ;
if ( rec . ir_free & XFS_INOBT_MASK ( agino - rec . ir_startino ) )
2018-07-19 12:29:11 -07:00
xchk_btree_xref_set_corrupt ( sc , sc - > sa . fino_cur , 0 ) ;
2018-01-16 18:53:07 -08:00
}
2018-01-16 18:53:10 -08:00
/* Cross reference the inode fields with the forks. */
STATIC void
2018-07-19 12:29:11 -07:00
xchk_inode_xref_bmap (
2018-07-19 12:29:12 -07:00
struct xfs_scrub * sc ,
2018-07-19 12:29:12 -07:00
struct xfs_dinode * dip )
2018-01-16 18:53:10 -08:00
{
2018-07-19 12:29:12 -07:00
xfs_extnum_t nextents ;
xfs_filblks_t count ;
xfs_filblks_t acount ;
int error ;
2018-01-16 18:53:10 -08:00
2018-07-19 12:29:11 -07:00
if ( xchk_skip_xref ( sc - > sm ) )
2018-05-14 06:34:31 -07:00
return ;
2018-01-16 18:53:10 -08:00
/* Walk all the extents to check nextents/naextents/nblocks. */
error = xfs_bmap_count_blocks ( sc - > tp , sc - > ip , XFS_DATA_FORK ,
& nextents , & count ) ;
2018-07-19 12:29:11 -07:00
if ( ! xchk_should_check_xref ( sc , & error , NULL ) )
2018-01-16 18:53:10 -08:00
return ;
if ( nextents < be32_to_cpu ( dip - > di_nextents ) )
2018-07-19 12:29:11 -07:00
xchk_ino_xref_set_corrupt ( sc , sc - > ip - > i_ino ) ;
2018-01-16 18:53:10 -08:00
error = xfs_bmap_count_blocks ( sc - > tp , sc - > ip , XFS_ATTR_FORK ,
& nextents , & acount ) ;
2018-07-19 12:29:11 -07:00
if ( ! xchk_should_check_xref ( sc , & error , NULL ) )
2018-01-16 18:53:10 -08:00
return ;
if ( nextents ! = be16_to_cpu ( dip - > di_anextents ) )
2018-07-19 12:29:11 -07:00
xchk_ino_xref_set_corrupt ( sc , sc - > ip - > i_ino ) ;
2018-01-16 18:53:10 -08:00
/* Check nblocks against the inode. */
if ( count + acount ! = be64_to_cpu ( dip - > di_nblocks ) )
2018-07-19 12:29:11 -07:00
xchk_ino_xref_set_corrupt ( sc , sc - > ip - > i_ino ) ;
2018-01-16 18:53:10 -08:00
}
2018-01-16 18:53:05 -08:00
/* Cross-reference with the other btrees. */
STATIC void
2018-07-19 12:29:11 -07:00
xchk_inode_xref (
2018-07-19 12:29:12 -07:00
struct xfs_scrub * sc ,
2018-07-19 12:29:12 -07:00
xfs_ino_t ino ,
struct xfs_dinode * dip )
2018-01-16 18:53:05 -08:00
{
2018-07-19 12:29:12 -07:00
xfs_agnumber_t agno ;
xfs_agblock_t agbno ;
int error ;
2018-01-16 18:53:06 -08:00
2018-01-16 18:53:05 -08:00
if ( sc - > sm - > sm_flags & XFS_SCRUB_OFLAG_CORRUPT )
return ;
2018-01-16 18:53:06 -08:00
agno = XFS_INO_TO_AGNO ( sc - > mp , ino ) ;
agbno = XFS_INO_TO_AGBNO ( sc - > mp , ino ) ;
2018-07-19 12:29:11 -07:00
error = xchk_ag_init ( sc , agno , & sc - > sa ) ;
if ( ! xchk_xref_process_error ( sc , agno , agbno , & error ) )
2018-01-16 18:53:06 -08:00
return ;
2018-07-19 12:29:11 -07:00
xchk_xref_is_used_space ( sc , agbno , 1 ) ;
xchk_inode_xref_finobt ( sc , ino ) ;
2018-12-12 08:46:23 -08:00
xchk_xref_is_owned_by ( sc , agbno , 1 , & XFS_RMAP_OINFO_INODES ) ;
2018-07-19 12:29:11 -07:00
xchk_xref_is_not_shared ( sc , agbno , 1 ) ;
xchk_inode_xref_bmap ( sc , dip ) ;
2018-01-16 18:53:06 -08:00
2018-07-19 12:29:11 -07:00
xchk_ag_free ( sc , & sc - > sa ) ;
2018-01-16 18:53:05 -08:00
}
2018-01-16 18:53:09 -08:00
/*
* If the reflink iflag disagrees with a scan for shared data fork extents ,
* either flag an error ( shared extents w / no flag ) or a preen ( flag set w / o
* any shared extents ) . We already checked for reflink iflag set on a non
* reflink filesystem .
*/
static void
2018-07-19 12:29:11 -07:00
xchk_inode_check_reflink_iflag (
2018-07-19 12:29:12 -07:00
struct xfs_scrub * sc ,
2018-07-19 12:29:12 -07:00
xfs_ino_t ino )
2018-01-16 18:53:09 -08:00
{
2018-07-19 12:29:12 -07:00
struct xfs_mount * mp = sc - > mp ;
bool has_shared ;
int error ;
2018-01-16 18:53:09 -08:00
if ( ! xfs_sb_version_hasreflink ( & mp - > m_sb ) )
return ;
error = xfs_reflink_inode_has_shared_extents ( sc - > tp , sc - > ip ,
& has_shared ) ;
2018-07-19 12:29:11 -07:00
if ( ! xchk_xref_process_error ( sc , XFS_INO_TO_AGNO ( mp , ino ) ,
2018-01-16 18:53:09 -08:00
XFS_INO_TO_AGBNO ( mp , ino ) , & error ) )
return ;
if ( xfs_is_reflink_inode ( sc - > ip ) & & ! has_shared )
2018-07-19 12:29:11 -07:00
xchk_ino_set_preen ( sc , ino ) ;
2018-01-16 18:53:09 -08:00
else if ( ! xfs_is_reflink_inode ( sc - > ip ) & & has_shared )
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , ino ) ;
2018-01-16 18:53:09 -08:00
}
2017-10-17 21:37:42 -07:00
/* Scrub an inode. */
int
2018-07-19 12:29:11 -07:00
xchk_inode (
2018-07-19 12:29:12 -07:00
struct xfs_scrub * sc )
2017-10-17 21:37:42 -07:00
{
2018-07-19 12:29:12 -07:00
struct xfs_dinode di ;
int error = 0 ;
2017-10-17 21:37:42 -07:00
2018-03-23 10:06:54 -07:00
/*
* If sc - > ip is NULL , that means that the setup function called
* xfs_iget to look up the inode . xfs_iget returned a EFSCORRUPTED
* and a NULL inode , so flag the corruption error and return .
*/
if ( ! sc - > ip ) {
2018-07-19 12:29:11 -07:00
xchk_ino_set_corrupt ( sc , sc - > sm - > sm_ino ) ;
2018-03-23 10:06:54 -07:00
return 0 ;
2017-10-17 21:37:42 -07:00
}
2018-03-23 10:06:54 -07:00
/* Scrub the inode core. */
xfs_inode_to_disk ( sc - > ip , & di , 0 ) ;
2018-07-19 12:29:11 -07:00
xchk_dinode ( sc , & di , sc - > ip - > i_ino ) ;
2017-10-17 21:37:42 -07:00
if ( sc - > sm - > sm_flags & XFS_SCRUB_OFLAG_CORRUPT )
goto out ;
/*
2018-01-16 18:53:09 -08:00
* Look for discrepancies between file ' s data blocks and the reflink
* iflag . We already checked the iflag against the file mode when
* we scrubbed the dinode .
2017-10-17 21:37:42 -07:00
*/
2018-01-16 18:53:09 -08:00
if ( S_ISREG ( VFS_I ( sc - > ip ) - > i_mode ) )
2018-07-19 12:29:11 -07:00
xchk_inode_check_reflink_iflag ( sc , sc - > ip - > i_ino ) ;
2017-10-17 21:37:42 -07:00
2018-07-19 12:29:11 -07:00
xchk_inode_xref ( sc , sc - > ip - > i_ino , & di ) ;
2017-10-17 21:37:42 -07:00
out :
return error ;
}