1739a2fe0SDarrick J. Wong // SPDX-License-Identifier: GPL-2.0-or-later 2c2fc338cSDarrick J. Wong /* 3ecc73f8aSDarrick J. Wong * Copyright (C) 2017-2023 Oracle. All Rights Reserved. 4739a2fe0SDarrick J. Wong * Author: Darrick J. Wong <djwong@kernel.org> 5c2fc338cSDarrick J. Wong */ 6c2fc338cSDarrick J. Wong #include "xfs.h" 7c2fc338cSDarrick J. Wong #include "xfs_fs.h" 8c2fc338cSDarrick J. Wong #include "xfs_shared.h" 9c2fc338cSDarrick J. Wong #include "xfs_format.h" 10c2fc338cSDarrick J. Wong #include "xfs_trans_resv.h" 11c2fc338cSDarrick J. Wong #include "xfs_mount.h" 12c2fc338cSDarrick J. Wong #include "xfs_log_format.h" 13c2fc338cSDarrick J. Wong #include "xfs_trans.h" 14c2fc338cSDarrick J. Wong #include "xfs_inode.h" 15c2fc338cSDarrick J. Wong #include "xfs_quota.h" 16c2fc338cSDarrick J. Wong #include "xfs_qm.h" 17f23c4044SDarrick J. Wong #include "xfs_bmap.h" 18c2fc338cSDarrick J. Wong #include "scrub/scrub.h" 19c2fc338cSDarrick J. Wong #include "scrub/common.h" 20c2fc338cSDarrick J. Wong 21c2fc338cSDarrick J. Wong /* Convert a scrub type code to a DQ flag, or return 0 if error. */ 221a7ed271SDarrick J. Wong static inline xfs_dqtype_t 23c517b3aaSDarrick J. Wong xchk_quota_to_dqtype( 241d8a748aSDarrick J. Wong struct xfs_scrub *sc) 25c2fc338cSDarrick J. Wong { 26c2fc338cSDarrick J. Wong switch (sc->sm->sm_type) { 27c2fc338cSDarrick J. Wong case XFS_SCRUB_TYPE_UQUOTA: 288cd4901dSDarrick J. Wong return XFS_DQTYPE_USER; 29c2fc338cSDarrick J. Wong case XFS_SCRUB_TYPE_GQUOTA: 308cd4901dSDarrick J. Wong return XFS_DQTYPE_GROUP; 31c2fc338cSDarrick J. Wong case XFS_SCRUB_TYPE_PQUOTA: 328cd4901dSDarrick J. Wong return XFS_DQTYPE_PROJ; 33c2fc338cSDarrick J. Wong default: 34c2fc338cSDarrick J. Wong return 0; 35c2fc338cSDarrick J. Wong } 36c2fc338cSDarrick J. Wong } 37c2fc338cSDarrick J. Wong 38c2fc338cSDarrick J. Wong /* Set us up to scrub a quota. */ 39c2fc338cSDarrick J. Wong int 40c517b3aaSDarrick J. Wong xchk_setup_quota( 41026f57ebSDarrick J. Wong struct xfs_scrub *sc) 42c2fc338cSDarrick J. Wong { 431a7ed271SDarrick J. Wong xfs_dqtype_t dqtype; 44eb41c93fSDarrick J. Wong int error; 45eb41c93fSDarrick J. Wong 46149e53afSChristoph Hellwig if (!XFS_IS_QUOTA_ON(sc->mp)) 47eb41c93fSDarrick J. Wong return -ENOENT; 48c2fc338cSDarrick J. Wong 49c517b3aaSDarrick J. Wong dqtype = xchk_quota_to_dqtype(sc); 50c2fc338cSDarrick J. Wong if (dqtype == 0) 51c2fc338cSDarrick J. Wong return -EINVAL; 5259d7fab2SDarrick J. Wong 53c2fc338cSDarrick J. Wong if (!xfs_this_quota_on(sc->mp, dqtype)) 54c2fc338cSDarrick J. Wong return -ENOENT; 5559d7fab2SDarrick J. Wong 56466c525dSDarrick J. Wong if (xchk_need_intent_drain(sc)) 57466c525dSDarrick J. Wong xchk_fsgates_enable(sc, XCHK_FSGATES_DRAIN); 58466c525dSDarrick J. Wong 59026f57ebSDarrick J. Wong error = xchk_setup_fs(sc); 60eb41c93fSDarrick J. Wong if (error) 61eb41c93fSDarrick J. Wong return error; 6217308539SDarrick J. Wong 6317308539SDarrick J. Wong error = xchk_install_live_inode(sc, xfs_quota_inode(sc->mp, dqtype)); 6417308539SDarrick J. Wong if (error) 6517308539SDarrick J. Wong return error; 6617308539SDarrick J. Wong 67*294012fbSDarrick J. Wong xchk_ilock(sc, XFS_ILOCK_EXCL); 68c2fc338cSDarrick J. Wong return 0; 69c2fc338cSDarrick J. Wong } 70c2fc338cSDarrick J. Wong 71c2fc338cSDarrick J. Wong /* Quotas. */ 72c2fc338cSDarrick J. Wong 73c517b3aaSDarrick J. Wong struct xchk_quota_info { 741d8a748aSDarrick J. Wong struct xfs_scrub *sc; 75554ba965SDarrick J. Wong xfs_dqid_t last_id; 76554ba965SDarrick J. Wong }; 77554ba965SDarrick J. Wong 78c2fc338cSDarrick J. Wong /* Scrub the fields in an individual quota item. */ 79554ba965SDarrick J. Wong STATIC int 80c517b3aaSDarrick J. Wong xchk_quota_item( 81c2fc338cSDarrick J. Wong struct xfs_dquot *dq, 821a7ed271SDarrick J. Wong xfs_dqtype_t dqtype, 83554ba965SDarrick J. Wong void *priv) 84c2fc338cSDarrick J. Wong { 85c517b3aaSDarrick J. Wong struct xchk_quota_info *sqi = priv; 861d8a748aSDarrick J. Wong struct xfs_scrub *sc = sqi->sc; 87c2fc338cSDarrick J. Wong struct xfs_mount *mp = sc->mp; 88c2fc338cSDarrick J. Wong struct xfs_quotainfo *qi = mp->m_quotainfo; 89c2fc338cSDarrick J. Wong xfs_fileoff_t offset; 90c2fc338cSDarrick J. Wong xfs_ino_t fs_icount; 918ef34723SDarrick J. Wong int error = 0; 928ef34723SDarrick J. Wong 938ef34723SDarrick J. Wong if (xchk_should_terminate(sc, &error)) 940a713bd4SDarrick J. Wong return error; 95c2fc338cSDarrick J. Wong 96c2fc338cSDarrick J. Wong /* 97554ba965SDarrick J. Wong * Except for the root dquot, the actual dquot we got must either have 98554ba965SDarrick J. Wong * the same or higher id as we saw before. 99c2fc338cSDarrick J. Wong */ 100c51df733SDarrick J. Wong offset = dq->q_id / qi->qi_dqperchunk; 101c51df733SDarrick J. Wong if (dq->q_id && dq->q_id <= sqi->last_id) 102c517b3aaSDarrick J. Wong xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); 103c2fc338cSDarrick J. Wong 104c51df733SDarrick J. Wong sqi->last_id = dq->q_id; 105554ba965SDarrick J. Wong 106c2fc338cSDarrick J. Wong /* 107c2fc338cSDarrick J. Wong * Warn if the hard limits are larger than the fs. 108c2fc338cSDarrick J. Wong * Administrators can do this, though in production this seems 109c2fc338cSDarrick J. Wong * suspect, which is why we flag it for review. 110c2fc338cSDarrick J. Wong * 111c2fc338cSDarrick J. Wong * Complain about corruption if the soft limit is greater than 112c2fc338cSDarrick J. Wong * the hard limit. 113c2fc338cSDarrick J. Wong */ 114d3537cf9SDarrick J. Wong if (dq->q_blk.hardlimit > mp->m_sb.sb_dblocks) 115c517b3aaSDarrick J. Wong xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); 116d3537cf9SDarrick J. Wong if (dq->q_blk.softlimit > dq->q_blk.hardlimit) 117c517b3aaSDarrick J. Wong xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); 118c2fc338cSDarrick J. Wong 119d3537cf9SDarrick J. Wong if (dq->q_ino.hardlimit > M_IGEO(mp)->maxicount) 120c517b3aaSDarrick J. Wong xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); 121d3537cf9SDarrick J. Wong if (dq->q_ino.softlimit > dq->q_ino.hardlimit) 122c517b3aaSDarrick J. Wong xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); 123c2fc338cSDarrick J. Wong 124d3537cf9SDarrick J. Wong if (dq->q_rtb.hardlimit > mp->m_sb.sb_rblocks) 125c517b3aaSDarrick J. Wong xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); 126d3537cf9SDarrick J. Wong if (dq->q_rtb.softlimit > dq->q_rtb.hardlimit) 127c517b3aaSDarrick J. Wong xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); 128c2fc338cSDarrick J. Wong 129c2fc338cSDarrick J. Wong /* Check the resource counts. */ 130c2fc338cSDarrick J. Wong fs_icount = percpu_counter_sum(&mp->m_icount); 131c2fc338cSDarrick J. Wong 132c2fc338cSDarrick J. Wong /* 133c2fc338cSDarrick J. Wong * Check that usage doesn't exceed physical limits. However, on 134c2fc338cSDarrick J. Wong * a reflink filesystem we're allowed to exceed physical space 135c2fc338cSDarrick J. Wong * if there are no quota limits. 136c2fc338cSDarrick J. Wong */ 137ebd9027dSDave Chinner if (xfs_has_reflink(mp)) { 138be37d40cSDarrick J. Wong if (mp->m_sb.sb_dblocks < dq->q_blk.count) 139c517b3aaSDarrick J. Wong xchk_fblock_set_warning(sc, XFS_DATA_FORK, 140c2fc338cSDarrick J. Wong offset); 141c2fc338cSDarrick J. Wong } else { 142be37d40cSDarrick J. Wong if (mp->m_sb.sb_dblocks < dq->q_blk.count) 143c517b3aaSDarrick J. Wong xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 144c2fc338cSDarrick J. Wong offset); 145c2fc338cSDarrick J. Wong } 146be37d40cSDarrick J. Wong if (dq->q_ino.count > fs_icount || dq->q_rtb.count > mp->m_sb.sb_rblocks) 147c517b3aaSDarrick J. Wong xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); 148c2fc338cSDarrick J. Wong 149c2fc338cSDarrick J. Wong /* 150c2fc338cSDarrick J. Wong * We can violate the hard limits if the admin suddenly sets a 151c2fc338cSDarrick J. Wong * lower limit than the actual usage. However, we flag it for 152c2fc338cSDarrick J. Wong * admin review. 153c2fc338cSDarrick J. Wong */ 154c51df733SDarrick J. Wong if (dq->q_id == 0) 155c51df733SDarrick J. Wong goto out; 156c51df733SDarrick J. Wong 157d3537cf9SDarrick J. Wong if (dq->q_blk.hardlimit != 0 && 158be37d40cSDarrick J. Wong dq->q_blk.count > dq->q_blk.hardlimit) 159c517b3aaSDarrick J. Wong xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); 160554ba965SDarrick J. Wong 161d3537cf9SDarrick J. Wong if (dq->q_ino.hardlimit != 0 && 162be37d40cSDarrick J. Wong dq->q_ino.count > dq->q_ino.hardlimit) 163c51df733SDarrick J. Wong xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); 164c51df733SDarrick J. Wong 165d3537cf9SDarrick J. Wong if (dq->q_rtb.hardlimit != 0 && 166be37d40cSDarrick J. Wong dq->q_rtb.count > dq->q_rtb.hardlimit) 167c51df733SDarrick J. Wong xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); 168c51df733SDarrick J. Wong 169c51df733SDarrick J. Wong out: 1708ef34723SDarrick J. Wong if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 17105237032SDarrick J. Wong return -ECANCELED; 1728ef34723SDarrick J. Wong 173554ba965SDarrick J. Wong return 0; 174c2fc338cSDarrick J. Wong } 175c2fc338cSDarrick J. Wong 17687d9d609SDarrick J. Wong /* Check the quota's data fork. */ 17787d9d609SDarrick J. Wong STATIC int 178c517b3aaSDarrick J. Wong xchk_quota_data_fork( 1791d8a748aSDarrick J. Wong struct xfs_scrub *sc) 18087d9d609SDarrick J. Wong { 18187d9d609SDarrick J. Wong struct xfs_bmbt_irec irec = { 0 }; 18287d9d609SDarrick J. Wong struct xfs_iext_cursor icur; 18387d9d609SDarrick J. Wong struct xfs_quotainfo *qi = sc->mp->m_quotainfo; 18487d9d609SDarrick J. Wong struct xfs_ifork *ifp; 18587d9d609SDarrick J. Wong xfs_fileoff_t max_dqid_off; 18687d9d609SDarrick J. Wong int error = 0; 18787d9d609SDarrick J. Wong 18887d9d609SDarrick J. Wong /* Invoke the fork scrubber. */ 189c517b3aaSDarrick J. Wong error = xchk_metadata_inode_forks(sc); 19087d9d609SDarrick J. Wong if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) 19187d9d609SDarrick J. Wong return error; 19287d9d609SDarrick J. Wong 19387d9d609SDarrick J. Wong /* Check for data fork problems that apply only to quota files. */ 19487d9d609SDarrick J. Wong max_dqid_off = ((xfs_dqid_t)-1) / qi->qi_dqperchunk; 195732436efSDarrick J. Wong ifp = xfs_ifork_ptr(sc->ip, XFS_DATA_FORK); 19687d9d609SDarrick J. Wong for_each_xfs_iext(ifp, &icur, &irec) { 197c517b3aaSDarrick J. Wong if (xchk_should_terminate(sc, &error)) 19887d9d609SDarrick J. Wong break; 199f23c4044SDarrick J. Wong 20087d9d609SDarrick J. Wong /* 201f23c4044SDarrick J. Wong * delalloc/unwritten extents or blocks mapped above the highest 20287d9d609SDarrick J. Wong * quota id shouldn't happen. 20387d9d609SDarrick J. Wong */ 204f23c4044SDarrick J. Wong if (!xfs_bmap_is_written_extent(&irec) || 20587d9d609SDarrick J. Wong irec.br_startoff > max_dqid_off || 20687d9d609SDarrick J. Wong irec.br_startoff + irec.br_blockcount - 1 > max_dqid_off) { 207c517b3aaSDarrick J. Wong xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 20887d9d609SDarrick J. Wong irec.br_startoff); 20987d9d609SDarrick J. Wong break; 21087d9d609SDarrick J. Wong } 21187d9d609SDarrick J. Wong } 21287d9d609SDarrick J. Wong 21387d9d609SDarrick J. Wong return error; 21487d9d609SDarrick J. Wong } 21587d9d609SDarrick J. Wong 216c2fc338cSDarrick J. Wong /* Scrub all of a quota type's items. */ 217c2fc338cSDarrick J. Wong int 218c517b3aaSDarrick J. Wong xchk_quota( 2191d8a748aSDarrick J. Wong struct xfs_scrub *sc) 220c2fc338cSDarrick J. Wong { 221c517b3aaSDarrick J. Wong struct xchk_quota_info sqi; 222c2fc338cSDarrick J. Wong struct xfs_mount *mp = sc->mp; 223c2fc338cSDarrick J. Wong struct xfs_quotainfo *qi = mp->m_quotainfo; 2241a7ed271SDarrick J. Wong xfs_dqtype_t dqtype; 225eda6bc27SEric Sandeen int error = 0; 226c2fc338cSDarrick J. Wong 227c517b3aaSDarrick J. Wong dqtype = xchk_quota_to_dqtype(sc); 228c2fc338cSDarrick J. Wong 229c2fc338cSDarrick J. Wong /* Look for problem extents. */ 230c517b3aaSDarrick J. Wong error = xchk_quota_data_fork(sc); 23187d9d609SDarrick J. Wong if (error) 232eb41c93fSDarrick J. Wong goto out; 233c2fc338cSDarrick J. Wong if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 234c2fc338cSDarrick J. Wong goto out; 235c2fc338cSDarrick J. Wong 236eb41c93fSDarrick J. Wong /* 237eb41c93fSDarrick J. Wong * Check all the quota items. Now that we've checked the quota inode 238eb41c93fSDarrick J. Wong * data fork we have to drop ILOCK_EXCL to use the regular dquot 239eb41c93fSDarrick J. Wong * functions. 240eb41c93fSDarrick J. Wong */ 241*294012fbSDarrick J. Wong xchk_iunlock(sc, sc->ilock_flags); 242554ba965SDarrick J. Wong sqi.sc = sc; 243554ba965SDarrick J. Wong sqi.last_id = 0; 244c517b3aaSDarrick J. Wong error = xfs_qm_dqiterate(mp, dqtype, xchk_quota_item, &sqi); 245*294012fbSDarrick J. Wong xchk_ilock(sc, XFS_ILOCK_EXCL); 24605237032SDarrick J. Wong if (error == -ECANCELED) 24705237032SDarrick J. Wong error = 0; 248c517b3aaSDarrick J. Wong if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 249554ba965SDarrick J. Wong sqi.last_id * qi->qi_dqperchunk, &error)) 250554ba965SDarrick J. Wong goto out; 251c2fc338cSDarrick J. Wong 252c2fc338cSDarrick J. Wong out: 253c2fc338cSDarrick J. Wong return error; 254c2fc338cSDarrick J. Wong } 255