2018-06-05 19:42:14 -07:00
// SPDX-License-Identifier: GPL-2.0+
2017-10-17 21:37:47 -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_log_format.h"
# include "xfs_trans.h"
# include "xfs_inode.h"
# include "xfs_quota.h"
# include "xfs_qm.h"
# include "scrub/scrub.h"
# include "scrub/common.h"
/* Convert a scrub type code to a DQ flag, or return 0 if error. */
2020-07-15 17:53:43 -07:00
static inline 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 ;
2019-04-16 08:21:59 -07:00
sc - > flags | = XCHK_HAS_QUOTAOFFLOCK ;
2018-05-09 10:02:00 -07:00
mutex_lock ( & sc - > mp - > m_quotainfo - > qi_quotaofflock ) ;
2017-10-17 21:37:47 -07:00
if ( ! xfs_this_quota_on ( sc - > mp , dqtype ) )
return - ENOENT ;
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 ;
sc - > ip = xfs_quota_inode ( sc - > mp , dqtype ) ;
xfs_ilock ( sc - > ip , XFS_ILOCK_EXCL ) ;
sc - > ilock_flags = 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
} ;
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 (
2018-07-19 12:29:12 -07: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
void * priv )
2017-10-17 21:37:47 -07:00
{
2018-07-19 12:29:12 -07:00
struct xchk_quota_info * sqi = priv ;
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 ) )
2021-03-22 09:51:52 -07:00
return - ECANCELED ;
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
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 .
*/
if ( xfs_sb_version_hasreflink ( & mp - > m_sb ) ) {
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
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. */
2017-10-17 21:37:47 -07:00
max_dqid_off = ( ( xfs_dqid_t ) - 1 ) / qi - > qi_dqperchunk ;
2018-05-14 06:34:33 -07:00
ifp = XFS_IFORK_PTR ( sc - > ip , XFS_DATA_FORK ) ;
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 ;
/*
2018-05-14 06:34:33 -07:00
* delalloc extents or blocks mapped above the highest
2017-10-17 21:37:47 -07:00
* quota id shouldn ' t happen .
*/
if ( isnullstartblock ( irec . br_startblock ) | |
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
{
2018-07-19 12:29:12 -07:00
struct xchk_quota_info sqi ;
struct xfs_mount * mp = sc - > mp ;
struct xfs_quotainfo * qi = mp - > m_quotainfo ;
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 .
*/
xfs_iunlock ( sc - > ip , sc - > ilock_flags ) ;
sc - > ilock_flags = 0 ;
2018-05-04 15:31:21 -07:00
sqi . sc = sc ;
sqi . last_id = 0 ;
2018-07-19 12:29:11 -07:00
error = xfs_qm_dqiterate ( mp , dqtype , xchk_quota_item , & sqi ) ;
2018-05-09 10:02:00 -07:00
sc - > ilock_flags = XFS_ILOCK_EXCL ;
xfs_ilock ( sc - > ip , sc - > ilock_flags ) ;
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 ;
}