xref: /openbmc/linux/fs/xfs/scrub/quota.c (revision 82e6fdd6)
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 	dqtype = xfs_scrub_quota_to_dqtype(sc);
71 	if (dqtype == 0)
72 		return -EINVAL;
73 	if (!xfs_this_quota_on(sc->mp, dqtype))
74 		return -ENOENT;
75 	return 0;
76 }
77 
78 /* Quotas. */
79 
80 /* Scrub the fields in an individual quota item. */
81 STATIC void
82 xfs_scrub_quota_item(
83 	struct xfs_scrub_context	*sc,
84 	uint				dqtype,
85 	struct xfs_dquot		*dq,
86 	xfs_dqid_t			id)
87 {
88 	struct xfs_mount		*mp = sc->mp;
89 	struct xfs_disk_dquot		*d = &dq->q_core;
90 	struct xfs_quotainfo		*qi = mp->m_quotainfo;
91 	xfs_fileoff_t			offset;
92 	unsigned long long		bsoft;
93 	unsigned long long		isoft;
94 	unsigned long long		rsoft;
95 	unsigned long long		bhard;
96 	unsigned long long		ihard;
97 	unsigned long long		rhard;
98 	unsigned long long		bcount;
99 	unsigned long long		icount;
100 	unsigned long long		rcount;
101 	xfs_ino_t			fs_icount;
102 
103 	offset = id / qi->qi_dqperchunk;
104 
105 	/*
106 	 * We fed $id and DQNEXT into the xfs_qm_dqget call, which means
107 	 * that the actual dquot we got must either have the same id or
108 	 * the next higher id.
109 	 */
110 	if (id > be32_to_cpu(d->d_id))
111 		xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
112 
113 	/* Did we get the dquot type we wanted? */
114 	if (dqtype != (d->d_flags & XFS_DQ_ALLTYPES))
115 		xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
116 
117 	if (d->d_pad0 != cpu_to_be32(0) || d->d_pad != cpu_to_be16(0))
118 		xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
119 
120 	/* Check the limits. */
121 	bhard = be64_to_cpu(d->d_blk_hardlimit);
122 	ihard = be64_to_cpu(d->d_ino_hardlimit);
123 	rhard = be64_to_cpu(d->d_rtb_hardlimit);
124 
125 	bsoft = be64_to_cpu(d->d_blk_softlimit);
126 	isoft = be64_to_cpu(d->d_ino_softlimit);
127 	rsoft = be64_to_cpu(d->d_rtb_softlimit);
128 
129 	/*
130 	 * Warn if the hard limits are larger than the fs.
131 	 * Administrators can do this, though in production this seems
132 	 * suspect, which is why we flag it for review.
133 	 *
134 	 * Complain about corruption if the soft limit is greater than
135 	 * the hard limit.
136 	 */
137 	if (bhard > mp->m_sb.sb_dblocks)
138 		xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK, offset);
139 	if (bsoft > bhard)
140 		xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
141 
142 	if (ihard > mp->m_maxicount)
143 		xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK, offset);
144 	if (isoft > ihard)
145 		xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
146 
147 	if (rhard > mp->m_sb.sb_rblocks)
148 		xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK, offset);
149 	if (rsoft > rhard)
150 		xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
151 
152 	/* Check the resource counts. */
153 	bcount = be64_to_cpu(d->d_bcount);
154 	icount = be64_to_cpu(d->d_icount);
155 	rcount = be64_to_cpu(d->d_rtbcount);
156 	fs_icount = percpu_counter_sum(&mp->m_icount);
157 
158 	/*
159 	 * Check that usage doesn't exceed physical limits.  However, on
160 	 * a reflink filesystem we're allowed to exceed physical space
161 	 * if there are no quota limits.
162 	 */
163 	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
164 		if (mp->m_sb.sb_dblocks < bcount)
165 			xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK,
166 					offset);
167 	} else {
168 		if (mp->m_sb.sb_dblocks < bcount)
169 			xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK,
170 					offset);
171 	}
172 	if (icount > fs_icount || rcount > mp->m_sb.sb_rblocks)
173 		xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
174 
175 	/*
176 	 * We can violate the hard limits if the admin suddenly sets a
177 	 * lower limit than the actual usage.  However, we flag it for
178 	 * admin review.
179 	 */
180 	if (id != 0 && bhard != 0 && bcount > bhard)
181 		xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK, offset);
182 	if (id != 0 && ihard != 0 && icount > ihard)
183 		xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK, offset);
184 	if (id != 0 && rhard != 0 && rcount > rhard)
185 		xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK, offset);
186 }
187 
188 /* Scrub all of a quota type's items. */
189 int
190 xfs_scrub_quota(
191 	struct xfs_scrub_context	*sc)
192 {
193 	struct xfs_bmbt_irec		irec = { 0 };
194 	struct xfs_mount		*mp = sc->mp;
195 	struct xfs_inode		*ip;
196 	struct xfs_quotainfo		*qi = mp->m_quotainfo;
197 	struct xfs_dquot		*dq;
198 	xfs_fileoff_t			max_dqid_off;
199 	xfs_fileoff_t			off = 0;
200 	xfs_dqid_t			id = 0;
201 	uint				dqtype;
202 	int				nimaps;
203 	int				error = 0;
204 
205 	if (!XFS_IS_QUOTA_RUNNING(mp) || !XFS_IS_QUOTA_ON(mp))
206 		return -ENOENT;
207 
208 	mutex_lock(&qi->qi_quotaofflock);
209 	dqtype = xfs_scrub_quota_to_dqtype(sc);
210 	if (!xfs_this_quota_on(sc->mp, dqtype)) {
211 		error = -ENOENT;
212 		goto out_unlock_quota;
213 	}
214 
215 	/* Attach to the quota inode and set sc->ip so that reporting works. */
216 	ip = xfs_quota_inode(sc->mp, dqtype);
217 	sc->ip = ip;
218 
219 	/* Look for problem extents. */
220 	xfs_ilock(ip, XFS_ILOCK_EXCL);
221 	if (ip->i_d.di_flags & XFS_DIFLAG_REALTIME) {
222 		xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL);
223 		goto out_unlock_inode;
224 	}
225 	max_dqid_off = ((xfs_dqid_t)-1) / qi->qi_dqperchunk;
226 	while (1) {
227 		if (xfs_scrub_should_terminate(sc, &error))
228 			break;
229 
230 		off = irec.br_startoff + irec.br_blockcount;
231 		nimaps = 1;
232 		error = xfs_bmapi_read(ip, off, -1, &irec, &nimaps,
233 				XFS_BMAPI_ENTIRE);
234 		if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, off,
235 				&error))
236 			goto out_unlock_inode;
237 		if (!nimaps)
238 			break;
239 		if (irec.br_startblock == HOLESTARTBLOCK)
240 			continue;
241 
242 		/* Check the extent record doesn't point to crap. */
243 		if (irec.br_startblock + irec.br_blockcount <=
244 		    irec.br_startblock)
245 			xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK,
246 					irec.br_startoff);
247 		if (!xfs_verify_fsbno(mp, irec.br_startblock) ||
248 		    !xfs_verify_fsbno(mp, irec.br_startblock +
249 					irec.br_blockcount - 1))
250 			xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK,
251 					irec.br_startoff);
252 
253 		/*
254 		 * Unwritten extents or blocks mapped above the highest
255 		 * quota id shouldn't happen.
256 		 */
257 		if (isnullstartblock(irec.br_startblock) ||
258 		    irec.br_startoff > max_dqid_off ||
259 		    irec.br_startoff + irec.br_blockcount > max_dqid_off + 1)
260 			xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, off);
261 	}
262 	xfs_iunlock(ip, XFS_ILOCK_EXCL);
263 	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
264 		goto out;
265 
266 	/* Check all the quota items. */
267 	while (id < ((xfs_dqid_t)-1ULL)) {
268 		if (xfs_scrub_should_terminate(sc, &error))
269 			break;
270 
271 		error = xfs_qm_dqget(mp, NULL, id, dqtype, XFS_QMOPT_DQNEXT,
272 				&dq);
273 		if (error == -ENOENT)
274 			break;
275 		if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK,
276 				id * qi->qi_dqperchunk, &error))
277 			break;
278 
279 		xfs_scrub_quota_item(sc, dqtype, dq, id);
280 
281 		id = be32_to_cpu(dq->q_core.d_id) + 1;
282 		xfs_qm_dqput(dq);
283 		if (!id)
284 			break;
285 	}
286 
287 out:
288 	/* We set sc->ip earlier, so make sure we clear it now. */
289 	sc->ip = NULL;
290 out_unlock_quota:
291 	mutex_unlock(&qi->qi_quotaofflock);
292 	return error;
293 
294 out_unlock_inode:
295 	xfs_iunlock(ip, XFS_ILOCK_EXCL);
296 	goto out;
297 }
298