blob: 3c7506c7553cc6b04ca5f4fa08d345fe4f4115ef [file] [log] [blame]
Dave Chinner0b61f8a2018-06-05 19:42:14 -07001// SPDX-License-Identifier: GPL-2.0+
Darrick J. Wongc2fc3382017-10-17 21:37:47 -07002/*
3 * Copyright (C) 2017 Oracle. All Rights Reserved.
Darrick J. Wongc2fc3382017-10-17 21:37:47 -07004 * Author: Darrick J. Wong <darrick.wong@oracle.com>
Darrick J. Wongc2fc3382017-10-17 21:37:47 -07005 */
6#include "xfs.h"
7#include "xfs_fs.h"
8#include "xfs_shared.h"
9#include "xfs_format.h"
10#include "xfs_trans_resv.h"
11#include "xfs_mount.h"
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070012#include "xfs_log_format.h"
13#include "xfs_trans.h"
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070014#include "xfs_inode.h"
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070015#include "xfs_quota.h"
16#include "xfs_qm.h"
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070017#include "scrub/scrub.h"
18#include "scrub/common.h"
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070019
20/* Convert a scrub type code to a DQ flag, or return 0 if error. */
Darrick J. Wong1a7ed272020-07-15 17:53:43 -070021static inline xfs_dqtype_t
Darrick J. Wongc517b3a2018-07-19 12:29:11 -070022xchk_quota_to_dqtype(
Darrick J. Wong1d8a7482018-07-19 12:29:12 -070023 struct xfs_scrub *sc)
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070024{
25 switch (sc->sm->sm_type) {
26 case XFS_SCRUB_TYPE_UQUOTA:
Darrick J. Wong8cd49012020-07-15 17:42:36 -070027 return XFS_DQTYPE_USER;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070028 case XFS_SCRUB_TYPE_GQUOTA:
Darrick J. Wong8cd49012020-07-15 17:42:36 -070029 return XFS_DQTYPE_GROUP;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070030 case XFS_SCRUB_TYPE_PQUOTA:
Darrick J. Wong8cd49012020-07-15 17:42:36 -070031 return XFS_DQTYPE_PROJ;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070032 default:
33 return 0;
34 }
35}
36
37/* Set us up to scrub a quota. */
38int
Darrick J. Wongc517b3a2018-07-19 12:29:11 -070039xchk_setup_quota(
Darrick J. Wong026f57e2021-04-07 17:59:39 -070040 struct xfs_scrub *sc)
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070041{
Darrick J. Wong1a7ed272020-07-15 17:53:43 -070042 xfs_dqtype_t dqtype;
Darrick J. Wong032d91f2018-07-19 12:29:12 -070043 int error;
Darrick J. Wongeb41c932018-05-09 10:02:00 -070044
Christoph Hellwig149e53a2021-08-06 11:05:37 -070045 if (!XFS_IS_QUOTA_ON(sc->mp))
Darrick J. Wongeb41c932018-05-09 10:02:00 -070046 return -ENOENT;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070047
Darrick J. Wongc517b3a2018-07-19 12:29:11 -070048 dqtype = xchk_quota_to_dqtype(sc);
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070049 if (dqtype == 0)
50 return -EINVAL;
Darrick J. Wong59d7fab2021-12-15 11:53:16 -080051
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070052 if (!xfs_this_quota_on(sc->mp, dqtype))
53 return -ENOENT;
Darrick J. Wong59d7fab2021-12-15 11:53:16 -080054
Darrick J. Wong026f57e2021-04-07 17:59:39 -070055 error = xchk_setup_fs(sc);
Darrick J. Wongeb41c932018-05-09 10:02:00 -070056 if (error)
57 return error;
58 sc->ip = xfs_quota_inode(sc->mp, dqtype);
59 xfs_ilock(sc->ip, XFS_ILOCK_EXCL);
60 sc->ilock_flags = XFS_ILOCK_EXCL;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070061 return 0;
62}
63
64/* Quotas. */
65
Darrick J. Wongc517b3a2018-07-19 12:29:11 -070066struct xchk_quota_info {
Darrick J. Wong1d8a7482018-07-19 12:29:12 -070067 struct xfs_scrub *sc;
Darrick J. Wong032d91f2018-07-19 12:29:12 -070068 xfs_dqid_t last_id;
Darrick J. Wong554ba962018-05-04 15:31:21 -070069};
70
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070071/* Scrub the fields in an individual quota item. */
Darrick J. Wong554ba962018-05-04 15:31:21 -070072STATIC int
Darrick J. Wongc517b3a2018-07-19 12:29:11 -070073xchk_quota_item(
Darrick J. Wong032d91f2018-07-19 12:29:12 -070074 struct xfs_dquot *dq,
Darrick J. Wong1a7ed272020-07-15 17:53:43 -070075 xfs_dqtype_t dqtype,
Darrick J. Wong032d91f2018-07-19 12:29:12 -070076 void *priv)
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070077{
Darrick J. Wong032d91f2018-07-19 12:29:12 -070078 struct xchk_quota_info *sqi = priv;
Darrick J. Wong1d8a7482018-07-19 12:29:12 -070079 struct xfs_scrub *sc = sqi->sc;
Darrick J. Wong032d91f2018-07-19 12:29:12 -070080 struct xfs_mount *mp = sc->mp;
Darrick J. Wong032d91f2018-07-19 12:29:12 -070081 struct xfs_quotainfo *qi = mp->m_quotainfo;
82 xfs_fileoff_t offset;
Darrick J. Wong032d91f2018-07-19 12:29:12 -070083 xfs_ino_t fs_icount;
Darrick J. Wong8ef347232019-11-05 15:33:56 -080084 int error = 0;
85
86 if (xchk_should_terminate(sc, &error))
Darrick J. Wong05237032021-03-22 09:51:52 -070087 return -ECANCELED;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070088
89 /*
Darrick J. Wong554ba962018-05-04 15:31:21 -070090 * Except for the root dquot, the actual dquot we got must either have
91 * the same or higher id as we saw before.
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070092 */
Darrick J. Wongc51df732020-07-14 10:37:30 -070093 offset = dq->q_id / qi->qi_dqperchunk;
94 if (dq->q_id && dq->q_id <= sqi->last_id)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -070095 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070096
Darrick J. Wongc51df732020-07-14 10:37:30 -070097 sqi->last_id = dq->q_id;
Darrick J. Wong554ba962018-05-04 15:31:21 -070098
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070099 /*
100 * Warn if the hard limits are larger than the fs.
101 * Administrators can do this, though in production this seems
102 * suspect, which is why we flag it for review.
103 *
104 * Complain about corruption if the soft limit is greater than
105 * the hard limit.
106 */
Darrick J. Wongd3537cf2020-07-14 10:37:31 -0700107 if (dq->q_blk.hardlimit > mp->m_sb.sb_dblocks)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700108 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
Darrick J. Wongd3537cf2020-07-14 10:37:31 -0700109 if (dq->q_blk.softlimit > dq->q_blk.hardlimit)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700110 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700111
Darrick J. Wongd3537cf2020-07-14 10:37:31 -0700112 if (dq->q_ino.hardlimit > M_IGEO(mp)->maxicount)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700113 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
Darrick J. Wongd3537cf2020-07-14 10:37:31 -0700114 if (dq->q_ino.softlimit > dq->q_ino.hardlimit)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700115 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700116
Darrick J. Wongd3537cf2020-07-14 10:37:31 -0700117 if (dq->q_rtb.hardlimit > mp->m_sb.sb_rblocks)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700118 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
Darrick J. Wongd3537cf2020-07-14 10:37:31 -0700119 if (dq->q_rtb.softlimit > dq->q_rtb.hardlimit)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700120 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700121
122 /* Check the resource counts. */
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700123 fs_icount = percpu_counter_sum(&mp->m_icount);
124
125 /*
126 * Check that usage doesn't exceed physical limits. However, on
127 * a reflink filesystem we're allowed to exceed physical space
128 * if there are no quota limits.
129 */
Dave Chinnerebd90272021-08-18 18:46:55 -0700130 if (xfs_has_reflink(mp)) {
Darrick J. Wongbe37d402020-07-14 10:37:31 -0700131 if (mp->m_sb.sb_dblocks < dq->q_blk.count)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700132 xchk_fblock_set_warning(sc, XFS_DATA_FORK,
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700133 offset);
134 } else {
Darrick J. Wongbe37d402020-07-14 10:37:31 -0700135 if (mp->m_sb.sb_dblocks < dq->q_blk.count)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700136 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700137 offset);
138 }
Darrick J. Wongbe37d402020-07-14 10:37:31 -0700139 if (dq->q_ino.count > fs_icount || dq->q_rtb.count > mp->m_sb.sb_rblocks)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700140 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700141
142 /*
143 * We can violate the hard limits if the admin suddenly sets a
144 * lower limit than the actual usage. However, we flag it for
145 * admin review.
146 */
Darrick J. Wongc51df732020-07-14 10:37:30 -0700147 if (dq->q_id == 0)
148 goto out;
149
Darrick J. Wongd3537cf2020-07-14 10:37:31 -0700150 if (dq->q_blk.hardlimit != 0 &&
Darrick J. Wongbe37d402020-07-14 10:37:31 -0700151 dq->q_blk.count > dq->q_blk.hardlimit)
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700152 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
Darrick J. Wong554ba962018-05-04 15:31:21 -0700153
Darrick J. Wongd3537cf2020-07-14 10:37:31 -0700154 if (dq->q_ino.hardlimit != 0 &&
Darrick J. Wongbe37d402020-07-14 10:37:31 -0700155 dq->q_ino.count > dq->q_ino.hardlimit)
Darrick J. Wongc51df732020-07-14 10:37:30 -0700156 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
157
Darrick J. Wongd3537cf2020-07-14 10:37:31 -0700158 if (dq->q_rtb.hardlimit != 0 &&
Darrick J. Wongbe37d402020-07-14 10:37:31 -0700159 dq->q_rtb.count > dq->q_rtb.hardlimit)
Darrick J. Wongc51df732020-07-14 10:37:30 -0700160 xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
161
162out:
Darrick J. Wong8ef347232019-11-05 15:33:56 -0800163 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
Darrick J. Wong05237032021-03-22 09:51:52 -0700164 return -ECANCELED;
Darrick J. Wong8ef347232019-11-05 15:33:56 -0800165
Darrick J. Wong554ba962018-05-04 15:31:21 -0700166 return 0;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700167}
168
Darrick J. Wong87d9d602018-05-14 06:34:33 -0700169/* Check the quota's data fork. */
170STATIC int
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700171xchk_quota_data_fork(
Darrick J. Wong1d8a7482018-07-19 12:29:12 -0700172 struct xfs_scrub *sc)
Darrick J. Wong87d9d602018-05-14 06:34:33 -0700173{
Darrick J. Wong032d91f2018-07-19 12:29:12 -0700174 struct xfs_bmbt_irec irec = { 0 };
175 struct xfs_iext_cursor icur;
176 struct xfs_quotainfo *qi = sc->mp->m_quotainfo;
177 struct xfs_ifork *ifp;
178 xfs_fileoff_t max_dqid_off;
179 int error = 0;
Darrick J. Wong87d9d602018-05-14 06:34:33 -0700180
181 /* Invoke the fork scrubber. */
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700182 error = xchk_metadata_inode_forks(sc);
Darrick J. Wong87d9d602018-05-14 06:34:33 -0700183 if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
184 return error;
185
186 /* Check for data fork problems that apply only to quota files. */
187 max_dqid_off = ((xfs_dqid_t)-1) / qi->qi_dqperchunk;
188 ifp = XFS_IFORK_PTR(sc->ip, XFS_DATA_FORK);
189 for_each_xfs_iext(ifp, &icur, &irec) {
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700190 if (xchk_should_terminate(sc, &error))
Darrick J. Wong87d9d602018-05-14 06:34:33 -0700191 break;
192 /*
193 * delalloc extents or blocks mapped above the highest
194 * quota id shouldn't happen.
195 */
196 if (isnullstartblock(irec.br_startblock) ||
197 irec.br_startoff > max_dqid_off ||
198 irec.br_startoff + irec.br_blockcount - 1 > max_dqid_off) {
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700199 xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
Darrick J. Wong87d9d602018-05-14 06:34:33 -0700200 irec.br_startoff);
201 break;
202 }
203 }
204
205 return error;
206}
207
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700208/* Scrub all of a quota type's items. */
209int
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700210xchk_quota(
Darrick J. Wong1d8a7482018-07-19 12:29:12 -0700211 struct xfs_scrub *sc)
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700212{
Darrick J. Wong032d91f2018-07-19 12:29:12 -0700213 struct xchk_quota_info sqi;
214 struct xfs_mount *mp = sc->mp;
215 struct xfs_quotainfo *qi = mp->m_quotainfo;
Darrick J. Wong1a7ed272020-07-15 17:53:43 -0700216 xfs_dqtype_t dqtype;
Darrick J. Wong032d91f2018-07-19 12:29:12 -0700217 int error = 0;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700218
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700219 dqtype = xchk_quota_to_dqtype(sc);
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700220
221 /* Look for problem extents. */
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700222 error = xchk_quota_data_fork(sc);
Darrick J. Wong87d9d602018-05-14 06:34:33 -0700223 if (error)
Darrick J. Wongeb41c932018-05-09 10:02:00 -0700224 goto out;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700225 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
226 goto out;
227
Darrick J. Wongeb41c932018-05-09 10:02:00 -0700228 /*
229 * Check all the quota items. Now that we've checked the quota inode
230 * data fork we have to drop ILOCK_EXCL to use the regular dquot
231 * functions.
232 */
233 xfs_iunlock(sc->ip, sc->ilock_flags);
234 sc->ilock_flags = 0;
Darrick J. Wong554ba962018-05-04 15:31:21 -0700235 sqi.sc = sc;
236 sqi.last_id = 0;
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700237 error = xfs_qm_dqiterate(mp, dqtype, xchk_quota_item, &sqi);
Darrick J. Wongeb41c932018-05-09 10:02:00 -0700238 sc->ilock_flags = XFS_ILOCK_EXCL;
239 xfs_ilock(sc->ip, sc->ilock_flags);
Darrick J. Wong05237032021-03-22 09:51:52 -0700240 if (error == -ECANCELED)
241 error = 0;
Darrick J. Wongc517b3a2018-07-19 12:29:11 -0700242 if (!xchk_fblock_process_error(sc, XFS_DATA_FORK,
Darrick J. Wong554ba962018-05-04 15:31:21 -0700243 sqi.last_id * qi->qi_dqperchunk, &error))
244 goto out;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700245
246out:
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700247 return error;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700248}