xref: /openbmc/linux/fs/xfs/scrub/quota.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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