2023-04-11 18:59:56 -07:00
// SPDX-License-Identifier: GPL-2.0-or-later
2017-10-17 21:37:47 -07:00
/*
2023-04-11 18:59:57 -07:00
* Copyright ( C ) 2017 - 2023 Oracle . All Rights Reserved .
2023-04-11 18:59:56 -07:00
* Author : Darrick J . Wong < djwong @ kernel . org >
2017-10-17 21:37:47 -07:00
*/
# include "xfs.h"
# include "xfs_fs.h"
# include "xfs_shared.h"
2023-12-15 10:03:44 -08:00
# include "xfs_bit.h"
2017-10-17 21:37:47 -07:00
# 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_quota.h"
# include "xfs_qm.h"
2022-11-06 17:03:21 -08:00
# include "xfs_bmap.h"
2017-10-17 21:37:47 -07:00
# include "scrub/scrub.h"
# include "scrub/common.h"
2023-12-15 10:03:45 -08:00
# include "scrub/quota.h"
2017-10-17 21:37:47 -07:00
/* Convert a scrub type code to a DQ flag, or return 0 if error. */
2023-12-15 10:03:45 -08:00
xfs_dqtype_t
2018-07-19 12:29:11 -07:00
xchk_quota_to_dqtype (
2018-07-19 12:29:12 -07:00
struct xfs_scrub * sc )
2017-10-17 21:37:47 -07:00
{
switch ( sc - > sm - > sm_type ) {
case XFS_SCRUB_TYPE_UQUOTA :
2020-07-15 17:42:36 -07:00
return XFS_DQTYPE_USER ;
2017-10-17 21:37:47 -07:00
case XFS_SCRUB_TYPE_GQUOTA :
2020-07-15 17:42:36 -07:00
return XFS_DQTYPE_GROUP ;
2017-10-17 21:37:47 -07:00
case XFS_SCRUB_TYPE_PQUOTA :
2020-07-15 17:42:36 -07:00
return XFS_DQTYPE_PROJ ;
2017-10-17 21:37:47 -07:00
default :
return 0 ;
}
}
/* Set us up to scrub a quota. */
int
2018-07-19 12:29:11 -07:00
xchk_setup_quota (
2021-04-07 17:59:39 -07:00
struct xfs_scrub * sc )
2017-10-17 21:37:47 -07:00
{
2020-07-15 17:53:43 -07:00
xfs_dqtype_t dqtype ;
2018-07-19 12:29:12 -07:00
int error ;
2018-05-09 10:02:00 -07:00
2021-08-06 11:05:37 -07:00
if ( ! XFS_IS_QUOTA_ON ( sc - > mp ) )
2018-05-09 10:02:00 -07:00
return - ENOENT ;
2017-10-17 21:37:47 -07:00
2018-07-19 12:29:11 -07:00
dqtype = xchk_quota_to_dqtype ( sc ) ;
2017-10-17 21:37:47 -07:00
if ( dqtype = = 0 )
return - EINVAL ;
2021-12-15 11:53:16 -08:00
2017-10-17 21:37:47 -07:00
if ( ! xfs_this_quota_on ( sc - > mp , dqtype ) )
return - ENOENT ;
2021-12-15 11:53:16 -08:00
2023-04-11 18:59:59 -07:00
if ( xchk_need_intent_drain ( sc ) )
xchk_fsgates_enable ( sc , XCHK_FSGATES_DRAIN ) ;
2021-04-07 17:59:39 -07:00
error = xchk_setup_fs ( sc ) ;
2018-05-09 10:02:00 -07:00
if ( error )
return error ;
2023-08-10 07:48:08 -07:00
error = xchk_install_live_inode ( sc , xfs_quota_inode ( sc - > mp , dqtype ) ) ;
if ( error )
return error ;
2023-08-10 07:48:08 -07:00
xchk_ilock ( sc , XFS_ILOCK_EXCL ) ;
2017-10-17 21:37:47 -07:00
return 0 ;
}
/* Quotas. */
2018-07-19 12:29:11 -07:00
struct xchk_quota_info {
2018-07-19 12:29:12 -07:00
struct xfs_scrub * sc ;
2018-07-19 12:29:12 -07:00
xfs_dqid_t last_id ;
2018-05-04 15:31:21 -07:00
} ;
2023-12-15 10:03:44 -08:00
/* There's a written block backing this dquot, right? */
STATIC int
xchk_quota_item_bmap (
struct xfs_scrub * sc ,
struct xfs_dquot * dq ,
xfs_fileoff_t offset )
{
struct xfs_bmbt_irec irec ;
struct xfs_mount * mp = sc - > mp ;
int nmaps = 1 ;
int error ;
if ( ! xfs_verify_fileoff ( mp , offset ) ) {
xchk_fblock_set_corrupt ( sc , XFS_DATA_FORK , offset ) ;
return 0 ;
}
if ( dq - > q_fileoffset ! = offset ) {
xchk_fblock_set_corrupt ( sc , XFS_DATA_FORK , offset ) ;
return 0 ;
}
error = xfs_bmapi_read ( sc - > ip , offset , 1 , & irec , & nmaps , 0 ) ;
if ( error )
return error ;
if ( nmaps ! = 1 ) {
xchk_fblock_set_corrupt ( sc , XFS_DATA_FORK , offset ) ;
return 0 ;
}
if ( ! xfs_verify_fsbno ( mp , irec . br_startblock ) )
xchk_fblock_set_corrupt ( sc , XFS_DATA_FORK , offset ) ;
if ( XFS_FSB_TO_DADDR ( mp , irec . br_startblock ) ! = dq - > q_blkno )
xchk_fblock_set_corrupt ( sc , XFS_DATA_FORK , offset ) ;
if ( ! xfs_bmap_is_written_extent ( & irec ) )
xchk_fblock_set_corrupt ( sc , XFS_DATA_FORK , offset ) ;
return 0 ;
}
2023-12-15 10:03:44 -08:00
/* Complain if a quota timer is incorrectly set. */
static inline void
xchk_quota_item_timer (
struct xfs_scrub * sc ,
xfs_fileoff_t offset ,
const struct xfs_dquot_res * res )
{
if ( ( res - > softlimit & & res - > count > res - > softlimit ) | |
( res - > hardlimit & & res - > count > res - > hardlimit ) ) {
if ( ! res - > timer )
xchk_fblock_set_corrupt ( sc , XFS_DATA_FORK , offset ) ;
} else {
if ( res - > timer )
xchk_fblock_set_corrupt ( sc , XFS_DATA_FORK , offset ) ;
}
}
2017-10-17 21:37:47 -07:00
/* Scrub the fields in an individual quota item. */
2018-05-04 15:31:21 -07:00
STATIC int
2018-07-19 12:29:11 -07:00
xchk_quota_item (
2023-12-15 10:03:45 -08:00
struct xchk_quota_info * sqi ,
struct xfs_dquot * dq )
2017-10-17 21:37:47 -07:00
{
2018-07-19 12:29:12 -07:00
struct xfs_scrub * sc = sqi - > sc ;
2018-07-19 12:29:12 -07:00
struct xfs_mount * mp = sc - > mp ;
struct xfs_quotainfo * qi = mp - > m_quotainfo ;
xfs_fileoff_t offset ;
xfs_ino_t fs_icount ;
2019-11-05 15:33:56 -08:00
int error = 0 ;
if ( xchk_should_terminate ( sc , & error ) )
2022-11-06 17:03:17 -08:00
return error ;
2017-10-17 21:37:47 -07:00
2023-12-15 10:03:44 -08:00
/*
* We want to validate the bmap record for the storage backing this
* dquot , so we need to lock the dquot and the quota file . For quota
* operations , the locking order is first the ILOCK and then the dquot .
* However , dqiterate gave us a locked dquot , so drop the dquot lock to
* get the ILOCK .
*/
xfs_dqunlock ( dq ) ;
xchk_ilock ( sc , XFS_ILOCK_SHARED ) ;
xfs_dqlock ( dq ) ;
2017-10-17 21:37:47 -07:00
/*
2018-05-04 15:31:21 -07:00
* Except for the root dquot , the actual dquot we got must either have
* the same or higher id as we saw before .
2017-10-17 21:37:47 -07:00
*/
2020-07-14 10:37:30 -07:00
offset = dq - > q_id / qi - > qi_dqperchunk ;
if ( dq - > q_id & & dq - > q_id < = sqi - > last_id )
2018-07-19 12:29:11 -07:00
xchk_fblock_set_corrupt ( sc , XFS_DATA_FORK , offset ) ;
2017-10-17 21:37:47 -07:00
2020-07-14 10:37:30 -07:00
sqi - > last_id = dq - > q_id ;
2018-05-04 15:31:21 -07:00
2023-12-15 10:03:44 -08:00
error = xchk_quota_item_bmap ( sc , dq , offset ) ;
xchk_iunlock ( sc , XFS_ILOCK_SHARED ) ;
if ( ! xchk_fblock_process_error ( sc , XFS_DATA_FORK , offset , & error ) )
return error ;
2017-10-17 21:37:47 -07:00
/*
* Warn if the hard limits are larger than the fs .
* Administrators can do this , though in production this seems
* suspect , which is why we flag it for review .
*
* Complain about corruption if the soft limit is greater than
* the hard limit .
*/
2020-07-14 10:37:31 -07:00
if ( dq - > q_blk . hardlimit > mp - > m_sb . sb_dblocks )
2018-07-19 12:29:11 -07:00
xchk_fblock_set_warning ( sc , XFS_DATA_FORK , offset ) ;
2020-07-14 10:37:31 -07:00
if ( dq - > q_blk . softlimit > dq - > q_blk . hardlimit )
2018-07-19 12:29:11 -07:00
xchk_fblock_set_corrupt ( sc , XFS_DATA_FORK , offset ) ;
2017-10-17 21:37:47 -07:00
2020-07-14 10:37:31 -07:00
if ( dq - > q_ino . hardlimit > M_IGEO ( mp ) - > maxicount )
2018-07-19 12:29:11 -07:00
xchk_fblock_set_warning ( sc , XFS_DATA_FORK , offset ) ;
2020-07-14 10:37:31 -07:00
if ( dq - > q_ino . softlimit > dq - > q_ino . hardlimit )
2018-07-19 12:29:11 -07:00
xchk_fblock_set_corrupt ( sc , XFS_DATA_FORK , offset ) ;
2017-10-17 21:37:47 -07:00
2020-07-14 10:37:31 -07:00
if ( dq - > q_rtb . hardlimit > mp - > m_sb . sb_rblocks )
2018-07-19 12:29:11 -07:00
xchk_fblock_set_warning ( sc , XFS_DATA_FORK , offset ) ;
2020-07-14 10:37:31 -07:00
if ( dq - > q_rtb . softlimit > dq - > q_rtb . hardlimit )
2018-07-19 12:29:11 -07:00
xchk_fblock_set_corrupt ( sc , XFS_DATA_FORK , offset ) ;
2017-10-17 21:37:47 -07:00
/* Check the resource counts. */
fs_icount = percpu_counter_sum ( & mp - > m_icount ) ;
/*
* Check that usage doesn ' t exceed physical limits . However , on
* a reflink filesystem we ' re allowed to exceed physical space
* if there are no quota limits .
*/
2021-08-18 18:46:55 -07:00
if ( xfs_has_reflink ( mp ) ) {
2020-07-14 10:37:31 -07:00
if ( mp - > m_sb . sb_dblocks < dq - > q_blk . count )
2018-07-19 12:29:11 -07:00
xchk_fblock_set_warning ( sc , XFS_DATA_FORK ,
2017-10-17 21:37:47 -07:00
offset ) ;
} else {
2020-07-14 10:37:31 -07:00
if ( mp - > m_sb . sb_dblocks < dq - > q_blk . count )
2018-07-19 12:29:11 -07:00
xchk_fblock_set_corrupt ( sc , XFS_DATA_FORK ,
2017-10-17 21:37:47 -07:00
offset ) ;
}
2020-07-14 10:37:31 -07:00
if ( dq - > q_ino . count > fs_icount | | dq - > q_rtb . count > mp - > m_sb . sb_rblocks )
2018-07-19 12:29:11 -07:00
xchk_fblock_set_corrupt ( sc , XFS_DATA_FORK , offset ) ;
2017-10-17 21:37:47 -07:00
/*
* We can violate the hard limits if the admin suddenly sets a
* lower limit than the actual usage . However , we flag it for
* admin review .
*/
2020-07-14 10:37:30 -07:00
if ( dq - > q_id = = 0 )
goto out ;
2020-07-14 10:37:31 -07:00
if ( dq - > q_blk . hardlimit ! = 0 & &
2020-07-14 10:37:31 -07:00
dq - > q_blk . count > dq - > q_blk . hardlimit )
2018-07-19 12:29:11 -07:00
xchk_fblock_set_warning ( sc , XFS_DATA_FORK , offset ) ;
2020-07-14 10:37:30 -07:00
2020-07-14 10:37:31 -07:00
if ( dq - > q_ino . hardlimit ! = 0 & &
2020-07-14 10:37:31 -07:00
dq - > q_ino . count > dq - > q_ino . hardlimit )
2018-07-19 12:29:11 -07:00
xchk_fblock_set_warning ( sc , XFS_DATA_FORK , offset ) ;
2020-07-14 10:37:30 -07:00
2020-07-14 10:37:31 -07:00
if ( dq - > q_rtb . hardlimit ! = 0 & &
2020-07-14 10:37:31 -07:00
dq - > q_rtb . count > dq - > q_rtb . hardlimit )
2018-07-19 12:29:11 -07:00
xchk_fblock_set_warning ( sc , XFS_DATA_FORK , offset ) ;
2018-05-04 15:31:21 -07:00
2023-12-15 10:03:44 -08:00
xchk_quota_item_timer ( sc , offset , & dq - > q_blk ) ;
xchk_quota_item_timer ( sc , offset , & dq - > q_ino ) ;
xchk_quota_item_timer ( sc , offset , & dq - > q_rtb ) ;
2020-07-14 10:37:30 -07:00
out :
2019-11-05 15:33:56 -08:00
if ( sc - > sm - > sm_flags & XFS_SCRUB_OFLAG_CORRUPT )
2021-03-22 09:51:52 -07:00
return - ECANCELED ;
2019-11-05 15:33:56 -08:00
2018-05-04 15:31:21 -07:00
return 0 ;
2017-10-17 21:37:47 -07:00
}
2018-05-14 06:34:33 -07:00
/* Check the quota's data fork. */
STATIC int
2018-07-19 12:29:11 -07:00
xchk_quota_data_fork (
2018-07-19 12:29:12 -07:00
struct xfs_scrub * sc )
2017-10-17 21:37:47 -07:00
{
2018-07-19 12:29:12 -07:00
struct xfs_bmbt_irec irec = { 0 } ;
struct xfs_iext_cursor icur ;
struct xfs_quotainfo * qi = sc - > mp - > m_quotainfo ;
struct xfs_ifork * ifp ;
xfs_fileoff_t max_dqid_off ;
int error = 0 ;
2017-10-17 21:37:47 -07:00
2018-05-14 06:34:33 -07:00
/* Invoke the fork scrubber. */
2018-07-19 12:29:11 -07:00
error = xchk_metadata_inode_forks ( sc ) ;
2018-05-14 06:34:33 -07:00
if ( error | | ( sc - > sm - > sm_flags & XFS_SCRUB_OFLAG_CORRUPT ) )
return error ;
2017-10-17 21:37:47 -07:00
2018-05-14 06:34:33 -07:00
/* Check for data fork problems that apply only to quota files. */
2023-12-15 10:03:45 -08:00
max_dqid_off = XFS_DQ_ID_MAX / qi - > qi_dqperchunk ;
2022-07-09 10:56:05 -07:00
ifp = xfs_ifork_ptr ( sc - > ip , XFS_DATA_FORK ) ;
2018-05-14 06:34:33 -07:00
for_each_xfs_iext ( ifp , & icur , & irec ) {
2018-07-19 12:29:11 -07:00
if ( xchk_should_terminate ( sc , & error ) )
2017-10-17 21:37:47 -07:00
break ;
2022-11-06 17:03:21 -08:00
2017-10-17 21:37:47 -07:00
/*
2022-11-06 17:03:21 -08:00
* delalloc / unwritten extents or blocks mapped above the highest
2017-10-17 21:37:47 -07:00
* quota id shouldn ' t happen .
*/
2022-11-06 17:03:21 -08:00
if ( ! xfs_bmap_is_written_extent ( & irec ) | |
2017-10-17 21:37:47 -07:00
irec . br_startoff > max_dqid_off | |
2018-05-14 06:34:33 -07:00
irec . br_startoff + irec . br_blockcount - 1 > max_dqid_off ) {
2018-07-19 12:29:11 -07:00
xchk_fblock_set_corrupt ( sc , XFS_DATA_FORK ,
2018-05-14 06:34:33 -07:00
irec . br_startoff ) ;
break ;
}
2017-10-17 21:37:47 -07:00
}
2018-05-14 06:34:33 -07:00
return error ;
}
/* Scrub all of a quota type's items. */
int
2018-07-19 12:29:11 -07:00
xchk_quota (
2018-07-19 12:29:12 -07:00
struct xfs_scrub * sc )
2018-05-14 06:34:33 -07:00
{
2023-12-15 10:03:45 -08:00
struct xchk_dqiter cursor = { } ;
struct xchk_quota_info sqi = { . sc = sc } ;
2018-07-19 12:29:12 -07:00
struct xfs_mount * mp = sc - > mp ;
struct xfs_quotainfo * qi = mp - > m_quotainfo ;
2023-12-15 10:03:45 -08:00
struct xfs_dquot * dq ;
2020-07-15 17:53:43 -07:00
xfs_dqtype_t dqtype ;
2018-07-19 12:29:12 -07:00
int error = 0 ;
2018-05-14 06:34:33 -07:00
2018-07-19 12:29:11 -07:00
dqtype = xchk_quota_to_dqtype ( sc ) ;
2018-05-14 06:34:33 -07:00
/* Look for problem extents. */
2018-07-19 12:29:11 -07:00
error = xchk_quota_data_fork ( sc ) ;
2018-05-14 06:34:33 -07:00
if ( error )
goto out ;
2017-10-17 21:37:47 -07:00
if ( sc - > sm - > sm_flags & XFS_SCRUB_OFLAG_CORRUPT )
goto out ;
2018-05-09 10:02:00 -07:00
/*
* Check all the quota items . Now that we ' ve checked the quota inode
* data fork we have to drop ILOCK_EXCL to use the regular dquot
* functions .
*/
2023-08-10 07:48:08 -07:00
xchk_iunlock ( sc , sc - > ilock_flags ) ;
2023-12-15 10:03:45 -08:00
/* Now look for things that the quota verifiers won't complain about. */
xchk_dqiter_init ( & cursor , sc , dqtype ) ;
while ( ( error = xchk_dquot_iter ( & cursor , & dq ) ) = = 1 ) {
error = xchk_quota_item ( & sqi , dq ) ;
xfs_qm_dqput ( dq ) ;
if ( error )
break ;
}
2021-03-22 09:51:52 -07:00
if ( error = = - ECANCELED )
error = 0 ;
2018-07-19 12:29:11 -07:00
if ( ! xchk_fblock_process_error ( sc , XFS_DATA_FORK ,
2018-05-04 15:31:21 -07:00
sqi . last_id * qi - > qi_dqperchunk , & error ) )
goto out ;
2017-10-17 21:37:47 -07:00
out :
return error ;
}