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_bit.h" |
10 | #include "xfs_format.h" |
11 | #include "xfs_trans_resv.h" |
12 | #include "xfs_mount.h" |
13 | #include "xfs_log_format.h" |
14 | #include "xfs_trans.h" |
15 | #include "xfs_inode.h" |
16 | #include "xfs_quota.h" |
17 | #include "xfs_qm.h" |
18 | #include "xfs_bmap.h" |
19 | #include "scrub/scrub.h" |
20 | #include "scrub/common.h" |
21 | #include "scrub/quota.h" |
22 | |
23 | /* Convert a scrub type code to a DQ flag, or return 0 if error. */ |
24 | xfs_dqtype_t |
25 | xchk_quota_to_dqtype( |
26 | struct xfs_scrub *sc) |
27 | { |
28 | switch (sc->sm->sm_type) { |
29 | case XFS_SCRUB_TYPE_UQUOTA: |
30 | return XFS_DQTYPE_USER; |
31 | case XFS_SCRUB_TYPE_GQUOTA: |
32 | return XFS_DQTYPE_GROUP; |
33 | case XFS_SCRUB_TYPE_PQUOTA: |
34 | return XFS_DQTYPE_PROJ; |
35 | default: |
36 | return 0; |
37 | } |
38 | } |
39 | |
40 | /* Set us up to scrub a quota. */ |
41 | int |
42 | xchk_setup_quota( |
43 | struct xfs_scrub *sc) |
44 | { |
45 | xfs_dqtype_t dqtype; |
46 | int error; |
47 | |
48 | if (!XFS_IS_QUOTA_ON(sc->mp)) |
49 | return -ENOENT; |
50 | |
51 | dqtype = xchk_quota_to_dqtype(sc); |
52 | if (dqtype == 0) |
53 | return -EINVAL; |
54 | |
55 | if (!xfs_this_quota_on(sc->mp, dqtype)) |
56 | return -ENOENT; |
57 | |
58 | if (xchk_need_intent_drain(sc)) |
59 | xchk_fsgates_enable(sc, XCHK_FSGATES_DRAIN); |
60 | |
61 | error = xchk_setup_fs(sc); |
62 | if (error) |
63 | return error; |
64 | |
65 | error = xchk_install_live_inode(sc, xfs_quota_inode(sc->mp, dqtype)); |
66 | if (error) |
67 | return error; |
68 | |
69 | xchk_ilock(sc, XFS_ILOCK_EXCL); |
70 | return 0; |
71 | } |
72 | |
73 | /* Quotas. */ |
74 | |
75 | struct xchk_quota_info { |
76 | struct xfs_scrub *sc; |
77 | xfs_dqid_t last_id; |
78 | }; |
79 | |
80 | /* There's a written block backing this dquot, right? */ |
81 | STATIC int |
82 | xchk_quota_item_bmap( |
83 | struct xfs_scrub *sc, |
84 | struct xfs_dquot *dq, |
85 | xfs_fileoff_t offset) |
86 | { |
87 | struct xfs_bmbt_irec irec; |
88 | struct xfs_mount *mp = sc->mp; |
89 | int nmaps = 1; |
90 | int error; |
91 | |
92 | if (!xfs_verify_fileoff(mp, offset)) { |
93 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
94 | return 0; |
95 | } |
96 | |
97 | if (dq->q_fileoffset != offset) { |
98 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
99 | return 0; |
100 | } |
101 | |
102 | error = xfs_bmapi_read(sc->ip, offset, 1, &irec, &nmaps, 0); |
103 | if (error) |
104 | return error; |
105 | |
106 | if (nmaps != 1) { |
107 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
108 | return 0; |
109 | } |
110 | |
111 | if (!xfs_verify_fsbno(mp, irec.br_startblock)) |
112 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
113 | if (XFS_FSB_TO_DADDR(mp, irec.br_startblock) != dq->q_blkno) |
114 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
115 | if (!xfs_bmap_is_written_extent(&irec)) |
116 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
117 | |
118 | return 0; |
119 | } |
120 | |
121 | /* Complain if a quota timer is incorrectly set. */ |
122 | static inline void |
123 | xchk_quota_item_timer( |
124 | struct xfs_scrub *sc, |
125 | xfs_fileoff_t offset, |
126 | const struct xfs_dquot_res *res) |
127 | { |
128 | if ((res->softlimit && res->count > res->softlimit) || |
129 | (res->hardlimit && res->count > res->hardlimit)) { |
130 | if (!res->timer) |
131 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
132 | } else { |
133 | if (res->timer) |
134 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
135 | } |
136 | } |
137 | |
138 | /* Scrub the fields in an individual quota item. */ |
139 | STATIC int |
140 | xchk_quota_item( |
141 | struct xchk_quota_info *sqi, |
142 | struct xfs_dquot *dq) |
143 | { |
144 | struct xfs_scrub *sc = sqi->sc; |
145 | struct xfs_mount *mp = sc->mp; |
146 | struct xfs_quotainfo *qi = mp->m_quotainfo; |
147 | xfs_fileoff_t offset; |
148 | xfs_ino_t fs_icount; |
149 | int error = 0; |
150 | |
151 | if (xchk_should_terminate(sc, &error)) |
152 | return error; |
153 | |
154 | /* |
155 | * We want to validate the bmap record for the storage backing this |
156 | * dquot, so we need to lock the dquot and the quota file. For quota |
157 | * operations, the locking order is first the ILOCK and then the dquot. |
158 | * However, dqiterate gave us a locked dquot, so drop the dquot lock to |
159 | * get the ILOCK. |
160 | */ |
161 | xfs_dqunlock(dq); |
162 | xchk_ilock(sc, XFS_ILOCK_SHARED); |
163 | xfs_dqlock(dq); |
164 | |
165 | /* |
166 | * Except for the root dquot, the actual dquot we got must either have |
167 | * the same or higher id as we saw before. |
168 | */ |
169 | offset = dq->q_id / qi->qi_dqperchunk; |
170 | if (dq->q_id && dq->q_id <= sqi->last_id) |
171 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
172 | |
173 | sqi->last_id = dq->q_id; |
174 | |
175 | error = xchk_quota_item_bmap(sc, dq, offset); |
176 | xchk_iunlock(sc, XFS_ILOCK_SHARED); |
177 | if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, offset, &error)) |
178 | return error; |
179 | |
180 | /* |
181 | * Warn if the hard limits are larger than the fs. |
182 | * Administrators can do this, though in production this seems |
183 | * suspect, which is why we flag it for review. |
184 | * |
185 | * Complain about corruption if the soft limit is greater than |
186 | * the hard limit. |
187 | */ |
188 | if (dq->q_blk.hardlimit > mp->m_sb.sb_dblocks) |
189 | xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); |
190 | if (dq->q_blk.softlimit > dq->q_blk.hardlimit) |
191 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
192 | |
193 | if (dq->q_ino.hardlimit > M_IGEO(mp)->maxicount) |
194 | xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); |
195 | if (dq->q_ino.softlimit > dq->q_ino.hardlimit) |
196 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
197 | |
198 | if (dq->q_rtb.hardlimit > mp->m_sb.sb_rblocks) |
199 | xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); |
200 | if (dq->q_rtb.softlimit > dq->q_rtb.hardlimit) |
201 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
202 | |
203 | /* Check the resource counts. */ |
204 | fs_icount = percpu_counter_sum(&mp->m_icount); |
205 | |
206 | /* |
207 | * Check that usage doesn't exceed physical limits. However, on |
208 | * a reflink filesystem we're allowed to exceed physical space |
209 | * if there are no quota limits. |
210 | */ |
211 | if (xfs_has_reflink(mp)) { |
212 | if (mp->m_sb.sb_dblocks < dq->q_blk.count) |
213 | xchk_fblock_set_warning(sc, XFS_DATA_FORK, |
214 | offset); |
215 | } else { |
216 | if (mp->m_sb.sb_dblocks < dq->q_blk.count) |
217 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, |
218 | offset); |
219 | } |
220 | if (dq->q_ino.count > fs_icount || dq->q_rtb.count > mp->m_sb.sb_rblocks) |
221 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset); |
222 | |
223 | /* |
224 | * We can violate the hard limits if the admin suddenly sets a |
225 | * lower limit than the actual usage. However, we flag it for |
226 | * admin review. |
227 | */ |
228 | if (dq->q_id == 0) |
229 | goto out; |
230 | |
231 | if (dq->q_blk.hardlimit != 0 && |
232 | dq->q_blk.count > dq->q_blk.hardlimit) |
233 | xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); |
234 | |
235 | if (dq->q_ino.hardlimit != 0 && |
236 | dq->q_ino.count > dq->q_ino.hardlimit) |
237 | xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); |
238 | |
239 | if (dq->q_rtb.hardlimit != 0 && |
240 | dq->q_rtb.count > dq->q_rtb.hardlimit) |
241 | xchk_fblock_set_warning(sc, XFS_DATA_FORK, offset); |
242 | |
243 | xchk_quota_item_timer(sc, offset, &dq->q_blk); |
244 | xchk_quota_item_timer(sc, offset, &dq->q_ino); |
245 | xchk_quota_item_timer(sc, offset, &dq->q_rtb); |
246 | |
247 | out: |
248 | if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) |
249 | return -ECANCELED; |
250 | |
251 | return 0; |
252 | } |
253 | |
254 | /* Check the quota's data fork. */ |
255 | STATIC int |
256 | xchk_quota_data_fork( |
257 | struct xfs_scrub *sc) |
258 | { |
259 | struct xfs_bmbt_irec irec = { 0 }; |
260 | struct xfs_iext_cursor icur; |
261 | struct xfs_quotainfo *qi = sc->mp->m_quotainfo; |
262 | struct xfs_ifork *ifp; |
263 | xfs_fileoff_t max_dqid_off; |
264 | int error = 0; |
265 | |
266 | /* Invoke the fork scrubber. */ |
267 | error = xchk_metadata_inode_forks(sc); |
268 | if (error || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) |
269 | return error; |
270 | |
271 | /* Check for data fork problems that apply only to quota files. */ |
272 | max_dqid_off = XFS_DQ_ID_MAX / qi->qi_dqperchunk; |
273 | ifp = xfs_ifork_ptr(sc->ip, XFS_DATA_FORK); |
274 | for_each_xfs_iext(ifp, &icur, &irec) { |
275 | if (xchk_should_terminate(sc, &error)) |
276 | break; |
277 | |
278 | /* |
279 | * delalloc/unwritten extents or blocks mapped above the highest |
280 | * quota id shouldn't happen. |
281 | */ |
282 | if (!xfs_bmap_is_written_extent(&irec) || |
283 | irec.br_startoff > max_dqid_off || |
284 | irec.br_startoff + irec.br_blockcount - 1 > max_dqid_off) { |
285 | xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, |
286 | irec.br_startoff); |
287 | break; |
288 | } |
289 | } |
290 | |
291 | return error; |
292 | } |
293 | |
294 | /* Scrub all of a quota type's items. */ |
295 | int |
296 | xchk_quota( |
297 | struct xfs_scrub *sc) |
298 | { |
299 | struct xchk_dqiter cursor = { }; |
300 | struct xchk_quota_info sqi = { .sc = sc }; |
301 | struct xfs_mount *mp = sc->mp; |
302 | struct xfs_quotainfo *qi = mp->m_quotainfo; |
303 | struct xfs_dquot *dq; |
304 | xfs_dqtype_t dqtype; |
305 | int error = 0; |
306 | |
307 | dqtype = xchk_quota_to_dqtype(sc); |
308 | |
309 | /* Look for problem extents. */ |
310 | error = xchk_quota_data_fork(sc); |
311 | if (error) |
312 | goto out; |
313 | if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) |
314 | goto out; |
315 | |
316 | /* |
317 | * Check all the quota items. Now that we've checked the quota inode |
318 | * data fork we have to drop ILOCK_EXCL to use the regular dquot |
319 | * functions. |
320 | */ |
321 | xchk_iunlock(sc, sc->ilock_flags); |
322 | |
323 | /* Now look for things that the quota verifiers won't complain about. */ |
324 | xchk_dqiter_init(&cursor, sc, dqtype); |
325 | while ((error = xchk_dquot_iter(&cursor, &dq)) == 1) { |
326 | error = xchk_quota_item(&sqi, dq); |
327 | xfs_qm_dqput(dq); |
328 | if (error) |
329 | break; |
330 | } |
331 | if (error == -ECANCELED) |
332 | error = 0; |
333 | if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, |
334 | sqi.last_id * qi->qi_dqperchunk, &error)) |
335 | goto out; |
336 | |
337 | out: |
338 | return error; |
339 | } |
340 | |