]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blob - fs/xfs/scrub/quota.c
Merge remote-tracking branches 'asoc/topic/cs35l32', 'asoc/topic/cs35l34', 'asoc...
[mirror_ubuntu-jammy-kernel.git] / fs / xfs / scrub / quota.c
1 /*
2 * Copyright (C) 2017 Oracle. All Rights Reserved.
3 *
4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it would be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20 #include "xfs.h"
21 #include "xfs_fs.h"
22 #include "xfs_shared.h"
23 #include "xfs_format.h"
24 #include "xfs_trans_resv.h"
25 #include "xfs_mount.h"
26 #include "xfs_defer.h"
27 #include "xfs_btree.h"
28 #include "xfs_bit.h"
29 #include "xfs_log_format.h"
30 #include "xfs_trans.h"
31 #include "xfs_sb.h"
32 #include "xfs_inode.h"
33 #include "xfs_inode_fork.h"
34 #include "xfs_alloc.h"
35 #include "xfs_bmap.h"
36 #include "xfs_quota.h"
37 #include "xfs_qm.h"
38 #include "xfs_dquot.h"
39 #include "xfs_dquot_item.h"
40 #include "scrub/xfs_scrub.h"
41 #include "scrub/scrub.h"
42 #include "scrub/common.h"
43 #include "scrub/trace.h"
44
45 /* Convert a scrub type code to a DQ flag, or return 0 if error. */
46 static inline uint
47 xfs_scrub_quota_to_dqtype(
48 struct xfs_scrub_context *sc)
49 {
50 switch (sc->sm->sm_type) {
51 case XFS_SCRUB_TYPE_UQUOTA:
52 return XFS_DQ_USER;
53 case XFS_SCRUB_TYPE_GQUOTA:
54 return XFS_DQ_GROUP;
55 case XFS_SCRUB_TYPE_PQUOTA:
56 return XFS_DQ_PROJ;
57 default:
58 return 0;
59 }
60 }
61
62 /* Set us up to scrub a quota. */
63 int
64 xfs_scrub_setup_quota(
65 struct xfs_scrub_context *sc,
66 struct xfs_inode *ip)
67 {
68 uint dqtype;
69
70 /*
71 * If userspace gave us an AG number or inode data, they don't
72 * know what they're doing. Get out.
73 */
74 if (sc->sm->sm_agno || sc->sm->sm_ino || sc->sm->sm_gen)
75 return -EINVAL;
76
77 dqtype = xfs_scrub_quota_to_dqtype(sc);
78 if (dqtype == 0)
79 return -EINVAL;
80 if (!xfs_this_quota_on(sc->mp, dqtype))
81 return -ENOENT;
82 return 0;
83 }
84
85 /* Quotas. */
86
87 /* Scrub the fields in an individual quota item. */
88 STATIC void
89 xfs_scrub_quota_item(
90 struct xfs_scrub_context *sc,
91 uint dqtype,
92 struct xfs_dquot *dq,
93 xfs_dqid_t id)
94 {
95 struct xfs_mount *mp = sc->mp;
96 struct xfs_disk_dquot *d = &dq->q_core;
97 struct xfs_quotainfo *qi = mp->m_quotainfo;
98 xfs_fileoff_t offset;
99 unsigned long long bsoft;
100 unsigned long long isoft;
101 unsigned long long rsoft;
102 unsigned long long bhard;
103 unsigned long long ihard;
104 unsigned long long rhard;
105 unsigned long long bcount;
106 unsigned long long icount;
107 unsigned long long rcount;
108 xfs_ino_t fs_icount;
109
110 offset = id / qi->qi_dqperchunk;
111
112 /*
113 * We fed $id and DQNEXT into the xfs_qm_dqget call, which means
114 * that the actual dquot we got must either have the same id or
115 * the next higher id.
116 */
117 if (id > be32_to_cpu(d->d_id))
118 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
119
120 /* Did we get the dquot type we wanted? */
121 if (dqtype != (d->d_flags & XFS_DQ_ALLTYPES))
122 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
123
124 if (d->d_pad0 != cpu_to_be32(0) || d->d_pad != cpu_to_be16(0))
125 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
126
127 /* Check the limits. */
128 bhard = be64_to_cpu(d->d_blk_hardlimit);
129 ihard = be64_to_cpu(d->d_ino_hardlimit);
130 rhard = be64_to_cpu(d->d_rtb_hardlimit);
131
132 bsoft = be64_to_cpu(d->d_blk_softlimit);
133 isoft = be64_to_cpu(d->d_ino_softlimit);
134 rsoft = be64_to_cpu(d->d_rtb_softlimit);
135
136 /*
137 * Warn if the hard limits are larger than the fs.
138 * Administrators can do this, though in production this seems
139 * suspect, which is why we flag it for review.
140 *
141 * Complain about corruption if the soft limit is greater than
142 * the hard limit.
143 */
144 if (bhard > mp->m_sb.sb_dblocks)
145 xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK, offset);
146 if (bsoft > bhard)
147 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
148
149 if (ihard > mp->m_maxicount)
150 xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK, offset);
151 if (isoft > ihard)
152 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
153
154 if (rhard > mp->m_sb.sb_rblocks)
155 xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK, offset);
156 if (rsoft > rhard)
157 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
158
159 /* Check the resource counts. */
160 bcount = be64_to_cpu(d->d_bcount);
161 icount = be64_to_cpu(d->d_icount);
162 rcount = be64_to_cpu(d->d_rtbcount);
163 fs_icount = percpu_counter_sum(&mp->m_icount);
164
165 /*
166 * Check that usage doesn't exceed physical limits. However, on
167 * a reflink filesystem we're allowed to exceed physical space
168 * if there are no quota limits.
169 */
170 if (xfs_sb_version_hasreflink(&mp->m_sb)) {
171 if (mp->m_sb.sb_dblocks < bcount)
172 xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK,
173 offset);
174 } else {
175 if (mp->m_sb.sb_dblocks < bcount)
176 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK,
177 offset);
178 }
179 if (icount > fs_icount || rcount > mp->m_sb.sb_rblocks)
180 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
181
182 /*
183 * We can violate the hard limits if the admin suddenly sets a
184 * lower limit than the actual usage. However, we flag it for
185 * admin review.
186 */
187 if (id != 0 && bhard != 0 && bcount > bhard)
188 xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK, offset);
189 if (id != 0 && ihard != 0 && icount > ihard)
190 xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK, offset);
191 if (id != 0 && rhard != 0 && rcount > rhard)
192 xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK, offset);
193 }
194
195 /* Scrub all of a quota type's items. */
196 int
197 xfs_scrub_quota(
198 struct xfs_scrub_context *sc)
199 {
200 struct xfs_bmbt_irec irec = { 0 };
201 struct xfs_mount *mp = sc->mp;
202 struct xfs_inode *ip;
203 struct xfs_quotainfo *qi = mp->m_quotainfo;
204 struct xfs_dquot *dq;
205 xfs_fileoff_t max_dqid_off;
206 xfs_fileoff_t off = 0;
207 xfs_dqid_t id = 0;
208 uint dqtype;
209 int nimaps;
210 int error = 0;
211
212 if (!XFS_IS_QUOTA_RUNNING(mp) || !XFS_IS_QUOTA_ON(mp))
213 return -ENOENT;
214
215 mutex_lock(&qi->qi_quotaofflock);
216 dqtype = xfs_scrub_quota_to_dqtype(sc);
217 if (!xfs_this_quota_on(sc->mp, dqtype)) {
218 error = -ENOENT;
219 goto out_unlock_quota;
220 }
221
222 /* Attach to the quota inode and set sc->ip so that reporting works. */
223 ip = xfs_quota_inode(sc->mp, dqtype);
224 sc->ip = ip;
225
226 /* Look for problem extents. */
227 xfs_ilock(ip, XFS_ILOCK_EXCL);
228 if (ip->i_d.di_flags & XFS_DIFLAG_REALTIME) {
229 xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL);
230 goto out_unlock_inode;
231 }
232 max_dqid_off = ((xfs_dqid_t)-1) / qi->qi_dqperchunk;
233 while (1) {
234 if (xfs_scrub_should_terminate(sc, &error))
235 break;
236
237 off = irec.br_startoff + irec.br_blockcount;
238 nimaps = 1;
239 error = xfs_bmapi_read(ip, off, -1, &irec, &nimaps,
240 XFS_BMAPI_ENTIRE);
241 if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, off,
242 &error))
243 goto out_unlock_inode;
244 if (!nimaps)
245 break;
246 if (irec.br_startblock == HOLESTARTBLOCK)
247 continue;
248
249 /* Check the extent record doesn't point to crap. */
250 if (irec.br_startblock + irec.br_blockcount <=
251 irec.br_startblock)
252 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK,
253 irec.br_startoff);
254 if (!xfs_verify_fsbno(mp, irec.br_startblock) ||
255 !xfs_verify_fsbno(mp, irec.br_startblock +
256 irec.br_blockcount - 1))
257 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK,
258 irec.br_startoff);
259
260 /*
261 * Unwritten extents or blocks mapped above the highest
262 * quota id shouldn't happen.
263 */
264 if (isnullstartblock(irec.br_startblock) ||
265 irec.br_startoff > max_dqid_off ||
266 irec.br_startoff + irec.br_blockcount > max_dqid_off + 1)
267 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, off);
268 }
269 xfs_iunlock(ip, XFS_ILOCK_EXCL);
270 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
271 goto out;
272
273 /* Check all the quota items. */
274 while (id < ((xfs_dqid_t)-1ULL)) {
275 if (xfs_scrub_should_terminate(sc, &error))
276 break;
277
278 error = xfs_qm_dqget(mp, NULL, id, dqtype, XFS_QMOPT_DQNEXT,
279 &dq);
280 if (error == -ENOENT)
281 break;
282 if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK,
283 id * qi->qi_dqperchunk, &error))
284 break;
285
286 xfs_scrub_quota_item(sc, dqtype, dq, id);
287
288 id = be32_to_cpu(dq->q_core.d_id) + 1;
289 xfs_qm_dqput(dq);
290 if (!id)
291 break;
292 }
293
294 out:
295 /* We set sc->ip earlier, so make sure we clear it now. */
296 sc->ip = NULL;
297 out_unlock_quota:
298 mutex_unlock(&qi->qi_quotaofflock);
299 return error;
300
301 out_unlock_inode:
302 xfs_iunlock(ip, XFS_ILOCK_EXCL);
303 goto out;
304 }