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
xchk_quota_to_dqtype(struct xfs_scrub * sc)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
xchk_setup_quota(struct xfs_scrub * sc)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
xchk_quota_item(struct xfs_dquot * dq,xfs_dqtype_t dqtype,void * priv)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
xchk_quota_data_fork(struct xfs_scrub * sc)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
xchk_quota(struct xfs_scrub * sc)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