Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0-or-later
2 : /*
3 : * Copyright (C) 2017-2023 Oracle. All Rights Reserved.
4 : * Author: Darrick J. Wong <djwong@kernel.org>
5 : */
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"
12 : #include "xfs_log_format.h"
13 : #include "xfs_trans.h"
14 : #include "xfs_inode.h"
15 : #include "xfs_quota.h"
16 : #include "xfs_qm.h"
17 : #include "xfs_bmap.h"
18 : #include "scrub/scrub.h"
19 : #include "scrub/common.h"
20 :
21 : /* Convert a scrub type code to a DQ flag, or return 0 if error. */
22 : static inline xfs_dqtype_t
23 : xchk_quota_to_dqtype(
24 : struct xfs_scrub *sc)
25 : {
26 698659 : switch (sc->sm->sm_type) {
27 : case XFS_SCRUB_TYPE_UQUOTA:
28 : return XFS_DQTYPE_USER;
29 : case XFS_SCRUB_TYPE_GQUOTA:
30 : return XFS_DQTYPE_GROUP;
31 : case XFS_SCRUB_TYPE_PQUOTA:
32 : return XFS_DQTYPE_PROJ;
33 : default:
34 : return 0;
35 : }
36 : }
37 :
38 : /* Set us up to scrub a quota. */
39 : int
40 500268 : xchk_setup_quota(
41 : struct xfs_scrub *sc)
42 : {
43 500268 : xfs_dqtype_t dqtype;
44 500268 : int error;
45 :
46 500268 : if (!XFS_IS_QUOTA_ON(sc->mp))
47 : return -ENOENT;
48 :
49 349503 : dqtype = xchk_quota_to_dqtype(sc);
50 349503 : if (dqtype == 0)
51 : return -EINVAL;
52 :
53 349503 : if (!xfs_this_quota_on(sc->mp, dqtype))
54 : return -ENOENT;
55 :
56 349300 : if (xchk_need_intent_drain(sc))
57 0 : xchk_fsgates_enable(sc, XCHK_FSGATES_DRAIN);
58 :
59 349300 : error = xchk_setup_fs(sc);
60 349300 : if (error)
61 : return error;
62 349156 : sc->ip = xfs_quota_inode(sc->mp, dqtype);
63 349156 : xfs_ilock(sc->ip, XFS_ILOCK_EXCL);
64 349156 : sc->ilock_flags = XFS_ILOCK_EXCL;
65 349156 : return 0;
66 : }
67 :
68 : /* Quotas. */
69 :
70 : struct xchk_quota_info {
71 : struct xfs_scrub *sc;
72 : xfs_dqid_t last_id;
73 : };
74 :
75 : /* Scrub the fields in an individual quota item. */
76 : STATIC int
77 349155 : xchk_quota_item(
78 : struct xfs_dquot *dq,
79 : xfs_dqtype_t dqtype,
80 : void *priv)
81 : {
82 349155 : struct xchk_quota_info *sqi = priv;
83 349155 : struct xfs_scrub *sc = sqi->sc;
84 349155 : struct xfs_mount *mp = sc->mp;
85 349155 : struct xfs_quotainfo *qi = mp->m_quotainfo;
86 349155 : xfs_fileoff_t offset;
87 349155 : xfs_ino_t fs_icount;
88 349155 : int error = 0;
89 :
90 349155 : if (xchk_should_terminate(sc, &error))
91 0 : return error;
92 :
93 : /*
94 : * Except for the root dquot, the actual dquot we got must either have
95 : * the same or higher id as we saw before.
96 : */
97 349155 : offset = dq->q_id / qi->qi_dqperchunk;
98 349155 : if (dq->q_id && dq->q_id <= sqi->last_id)
99 0 : xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
100 :
101 349155 : sqi->last_id = dq->q_id;
102 :
103 : /*
104 : * Warn if the hard limits are larger than the fs.
105 : * Administrators can do this, though in production this seems
106 : * suspect, which is why we flag it for review.
107 : *
108 : * Complain about corruption if the soft limit is greater than
109 : * the hard limit.
110 : */
111 349155 : if (dq->q_blk.hardlimit > mp->m_sb.sb_dblocks)
112 0 : xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
113 349155 : if (dq->q_blk.softlimit > dq->q_blk.hardlimit)
114 0 : xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
115 :
116 349155 : if (dq->q_ino.hardlimit > M_IGEO(mp)->maxicount)
117 0 : xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
118 349155 : if (dq->q_ino.softlimit > dq->q_ino.hardlimit)
119 0 : xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
120 :
121 349155 : if (dq->q_rtb.hardlimit > mp->m_sb.sb_rblocks)
122 0 : xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
123 349155 : if (dq->q_rtb.softlimit > dq->q_rtb.hardlimit)
124 0 : xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
125 :
126 : /* Check the resource counts. */
127 349155 : fs_icount = percpu_counter_sum(&mp->m_icount);
128 :
129 : /*
130 : * Check that usage doesn't exceed physical limits. However, on
131 : * a reflink filesystem we're allowed to exceed physical space
132 : * if there are no quota limits.
133 : */
134 349155 : if (xfs_has_reflink(mp)) {
135 349155 : if (mp->m_sb.sb_dblocks < dq->q_blk.count)
136 147 : xchk_fblock_set_warning(sc, XFS_DATA_FORK,
137 : offset);
138 : } else {
139 0 : if (mp->m_sb.sb_dblocks < dq->q_blk.count)
140 0 : xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
141 : offset);
142 : }
143 349155 : if (dq->q_ino.count > fs_icount || dq->q_rtb.count > mp->m_sb.sb_rblocks)
144 0 : xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
145 :
146 : /*
147 : * We can violate the hard limits if the admin suddenly sets a
148 : * lower limit than the actual usage. However, we flag it for
149 : * admin review.
150 : */
151 349155 : if (dq->q_id == 0)
152 349155 : goto out;
153 :
154 0 : if (dq->q_blk.hardlimit != 0 &&
155 0 : dq->q_blk.count > dq->q_blk.hardlimit)
156 0 : xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
157 :
158 0 : if (dq->q_ino.hardlimit != 0 &&
159 0 : dq->q_ino.count > dq->q_ino.hardlimit)
160 0 : xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
161 :
162 0 : if (dq->q_rtb.hardlimit != 0 &&
163 0 : dq->q_rtb.count > dq->q_rtb.hardlimit)
164 0 : xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset);
165 :
166 0 : out:
167 349155 : if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
168 0 : return -ECANCELED;
169 :
170 : return 0;
171 : }
172 :
173 : /* Check the quota's data fork. */
174 : STATIC int
175 349156 : xchk_quota_data_fork(
176 : struct xfs_scrub *sc)
177 : {
178 349156 : struct xfs_bmbt_irec irec = { 0 };
179 349156 : struct xfs_iext_cursor icur;
180 349156 : struct xfs_quotainfo *qi = sc->mp->m_quotainfo;
181 349156 : struct xfs_ifork *ifp;
182 349156 : xfs_fileoff_t max_dqid_off;
183 349156 : int error = 0;
184 :
185 : /* Invoke the fork scrubber. */
186 349156 : error = xchk_metadata_inode_forks(sc);
187 349156 : if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
188 : return error;
189 :
190 : /* Check for data fork problems that apply only to quota files. */
191 349155 : max_dqid_off = ((xfs_dqid_t)-1) / qi->qi_dqperchunk;
192 349155 : ifp = xfs_ifork_ptr(sc->ip, XFS_DATA_FORK);
193 102053768 : for_each_xfs_iext(ifp, &icur, &irec) {
194 101704613 : if (xchk_should_terminate(sc, &error))
195 : break;
196 :
197 : /*
198 : * delalloc/unwritten extents or blocks mapped above the highest
199 : * quota id shouldn't happen.
200 : */
201 101704613 : if (!xfs_bmap_is_written_extent(&irec) ||
202 101704613 : irec.br_startoff > max_dqid_off ||
203 101704613 : irec.br_startoff + irec.br_blockcount - 1 > max_dqid_off) {
204 0 : xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
205 : irec.br_startoff);
206 0 : break;
207 : }
208 : }
209 :
210 349155 : return error;
211 : }
212 :
213 : /* Scrub all of a quota type's items. */
214 : int
215 349156 : xchk_quota(
216 : struct xfs_scrub *sc)
217 : {
218 349156 : struct xchk_quota_info sqi;
219 349156 : struct xfs_mount *mp = sc->mp;
220 349156 : struct xfs_quotainfo *qi = mp->m_quotainfo;
221 349156 : xfs_dqtype_t dqtype;
222 349156 : int error = 0;
223 :
224 349156 : dqtype = xchk_quota_to_dqtype(sc);
225 :
226 : /* Look for problem extents. */
227 349156 : error = xchk_quota_data_fork(sc);
228 349156 : if (error)
229 1 : goto out;
230 349155 : if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
231 0 : goto out;
232 :
233 : /*
234 : * Check all the quota items. Now that we've checked the quota inode
235 : * data fork we have to drop ILOCK_EXCL to use the regular dquot
236 : * functions.
237 : */
238 349155 : xfs_iunlock(sc->ip, sc->ilock_flags);
239 349155 : sc->ilock_flags = 0;
240 349155 : sqi.sc = sc;
241 349155 : sqi.last_id = 0;
242 349155 : error = xfs_qm_dqiterate(mp, dqtype, xchk_quota_item, &sqi);
243 349155 : sc->ilock_flags = XFS_ILOCK_EXCL;
244 349155 : xfs_ilock(sc->ip, sc->ilock_flags);
245 349155 : if (error == -ECANCELED)
246 0 : error = 0;
247 349155 : if (!xchk_fblock_process_error(sc, XFS_DATA_FORK,
248 349155 : sqi.last_id * qi->qi_dqperchunk, &error))
249 : goto out;
250 :
251 349155 : out:
252 349156 : return error;
253 : }
|