blob: d3d08978f53ad84cdbbb94e71f15ed0336645462 [file] [log] [blame]
Darrick J. Wongc2fc3382017-10-17 21:37:47 -07001/*
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. */
46static inline uint
47xfs_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. */
63int
64xfs_scrub_setup_quota(
65 struct xfs_scrub_context *sc,
66 struct xfs_inode *ip)
67{
68 uint dqtype;
Darrick J. Wongeb41c932018-05-09 10:02:00 -070069 int error;
70
71 if (!XFS_IS_QUOTA_RUNNING(sc->mp) || !XFS_IS_QUOTA_ON(sc->mp))
72 return -ENOENT;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070073
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070074 dqtype = xfs_scrub_quota_to_dqtype(sc);
75 if (dqtype == 0)
76 return -EINVAL;
Darrick J. Wongeb41c932018-05-09 10:02:00 -070077 sc->has_quotaofflock = true;
78 mutex_lock(&sc->mp->m_quotainfo->qi_quotaofflock);
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070079 if (!xfs_this_quota_on(sc->mp, dqtype))
80 return -ENOENT;
Darrick J. Wongeb41c932018-05-09 10:02:00 -070081 error = xfs_scrub_setup_fs(sc, ip);
82 if (error)
83 return error;
84 sc->ip = xfs_quota_inode(sc->mp, dqtype);
85 xfs_ilock(sc->ip, XFS_ILOCK_EXCL);
86 sc->ilock_flags = XFS_ILOCK_EXCL;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070087 return 0;
88}
89
90/* Quotas. */
91
Darrick J. Wong554ba962018-05-04 15:31:21 -070092struct xfs_scrub_quota_info {
93 struct xfs_scrub_context *sc;
94 xfs_dqid_t last_id;
95};
96
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070097/* Scrub the fields in an individual quota item. */
Darrick J. Wong554ba962018-05-04 15:31:21 -070098STATIC int
Darrick J. Wongc2fc3382017-10-17 21:37:47 -070099xfs_scrub_quota_item(
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700100 struct xfs_dquot *dq,
Darrick J. Wong554ba962018-05-04 15:31:21 -0700101 uint dqtype,
102 void *priv)
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700103{
Darrick J. Wong554ba962018-05-04 15:31:21 -0700104 struct xfs_scrub_quota_info *sqi = priv;
105 struct xfs_scrub_context *sc = sqi->sc;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700106 struct xfs_mount *mp = sc->mp;
107 struct xfs_disk_dquot *d = &dq->q_core;
108 struct xfs_quotainfo *qi = mp->m_quotainfo;
109 xfs_fileoff_t offset;
110 unsigned long long bsoft;
111 unsigned long long isoft;
112 unsigned long long rsoft;
113 unsigned long long bhard;
114 unsigned long long ihard;
115 unsigned long long rhard;
116 unsigned long long bcount;
117 unsigned long long icount;
118 unsigned long long rcount;
119 xfs_ino_t fs_icount;
Darrick J. Wong554ba962018-05-04 15:31:21 -0700120 xfs_dqid_t id = be32_to_cpu(d->d_id);
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700121
122 /*
Darrick J. Wong554ba962018-05-04 15:31:21 -0700123 * Except for the root dquot, the actual dquot we got must either have
124 * the same or higher id as we saw before.
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700125 */
Darrick J. Wong554ba962018-05-04 15:31:21 -0700126 offset = id / qi->qi_dqperchunk;
127 if (id && id <= sqi->last_id)
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700128 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
129
Darrick J. Wong554ba962018-05-04 15:31:21 -0700130 sqi->last_id = id;
131
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700132 /* Did we get the dquot type we wanted? */
133 if (dqtype != (d->d_flags & XFS_DQ_ALLTYPES))
134 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
135
136 if (d->d_pad0 != cpu_to_be32(0) || d->d_pad != cpu_to_be16(0))
137 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
138
139 /* Check the limits. */
140 bhard = be64_to_cpu(d->d_blk_hardlimit);
141 ihard = be64_to_cpu(d->d_ino_hardlimit);
142 rhard = be64_to_cpu(d->d_rtb_hardlimit);
143
144 bsoft = be64_to_cpu(d->d_blk_softlimit);
145 isoft = be64_to_cpu(d->d_ino_softlimit);
146 rsoft = be64_to_cpu(d->d_rtb_softlimit);
147
148 /*
149 * Warn if the hard limits are larger than the fs.
150 * Administrators can do this, though in production this seems
151 * suspect, which is why we flag it for review.
152 *
153 * Complain about corruption if the soft limit is greater than
154 * the hard limit.
155 */
156 if (bhard > mp->m_sb.sb_dblocks)
157 xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK, offset);
158 if (bsoft > bhard)
159 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
160
161 if (ihard > mp->m_maxicount)
162 xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK, offset);
163 if (isoft > ihard)
164 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
165
166 if (rhard > mp->m_sb.sb_rblocks)
167 xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK, offset);
168 if (rsoft > rhard)
169 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
170
171 /* Check the resource counts. */
172 bcount = be64_to_cpu(d->d_bcount);
173 icount = be64_to_cpu(d->d_icount);
174 rcount = be64_to_cpu(d->d_rtbcount);
175 fs_icount = percpu_counter_sum(&mp->m_icount);
176
177 /*
178 * Check that usage doesn't exceed physical limits. However, on
179 * a reflink filesystem we're allowed to exceed physical space
180 * if there are no quota limits.
181 */
182 if (xfs_sb_version_hasreflink(&mp->m_sb)) {
183 if (mp->m_sb.sb_dblocks < bcount)
184 xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK,
185 offset);
186 } else {
187 if (mp->m_sb.sb_dblocks < bcount)
188 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK,
189 offset);
190 }
191 if (icount > fs_icount || rcount > mp->m_sb.sb_rblocks)
192 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
193
194 /*
195 * We can violate the hard limits if the admin suddenly sets a
196 * lower limit than the actual usage. However, we flag it for
197 * admin review.
198 */
199 if (id != 0 && bhard != 0 && bcount > bhard)
200 xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK, offset);
201 if (id != 0 && ihard != 0 && icount > ihard)
202 xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK, offset);
203 if (id != 0 && rhard != 0 && rcount > rhard)
204 xfs_scrub_fblock_set_warning(sc, XFS_DATA_FORK, offset);
Darrick J. Wong554ba962018-05-04 15:31:21 -0700205
206 return 0;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700207}
208
209/* Scrub all of a quota type's items. */
210int
211xfs_scrub_quota(
212 struct xfs_scrub_context *sc)
213{
214 struct xfs_bmbt_irec irec = { 0 };
Darrick J. Wong554ba962018-05-04 15:31:21 -0700215 struct xfs_scrub_quota_info sqi;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700216 struct xfs_mount *mp = sc->mp;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700217 struct xfs_quotainfo *qi = mp->m_quotainfo;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700218 xfs_fileoff_t max_dqid_off;
219 xfs_fileoff_t off = 0;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700220 uint dqtype;
221 int nimaps;
Eric Sandeeneda6bc272017-11-27 18:23:32 -0800222 int error = 0;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700223
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700224 dqtype = xfs_scrub_quota_to_dqtype(sc);
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700225
226 /* Look for problem extents. */
Darrick J. Wongeb41c932018-05-09 10:02:00 -0700227 if (sc->ip->i_d.di_flags & XFS_DIFLAG_REALTIME) {
Darrick J. Wong7e56d9e2018-03-23 10:06:54 -0700228 xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino);
Darrick J. Wongeb41c932018-05-09 10:02:00 -0700229 goto out;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700230 }
231 max_dqid_off = ((xfs_dqid_t)-1) / qi->qi_dqperchunk;
232 while (1) {
233 if (xfs_scrub_should_terminate(sc, &error))
234 break;
235
236 off = irec.br_startoff + irec.br_blockcount;
237 nimaps = 1;
Darrick J. Wongeb41c932018-05-09 10:02:00 -0700238 error = xfs_bmapi_read(sc->ip, off, -1, &irec, &nimaps,
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700239 XFS_BMAPI_ENTIRE);
240 if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, off,
241 &error))
Darrick J. Wongeb41c932018-05-09 10:02:00 -0700242 goto out;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700243 if (!nimaps)
244 break;
245 if (irec.br_startblock == HOLESTARTBLOCK)
246 continue;
247
248 /* Check the extent record doesn't point to crap. */
249 if (irec.br_startblock + irec.br_blockcount <=
250 irec.br_startblock)
251 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK,
252 irec.br_startoff);
253 if (!xfs_verify_fsbno(mp, irec.br_startblock) ||
254 !xfs_verify_fsbno(mp, irec.br_startblock +
255 irec.br_blockcount - 1))
256 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK,
257 irec.br_startoff);
258
259 /*
260 * Unwritten extents or blocks mapped above the highest
261 * quota id shouldn't happen.
262 */
263 if (isnullstartblock(irec.br_startblock) ||
264 irec.br_startoff > max_dqid_off ||
265 irec.br_startoff + irec.br_blockcount > max_dqid_off + 1)
266 xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, off);
267 }
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700268 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
269 goto out;
270
Darrick J. Wongeb41c932018-05-09 10:02:00 -0700271 /*
272 * Check all the quota items. Now that we've checked the quota inode
273 * data fork we have to drop ILOCK_EXCL to use the regular dquot
274 * functions.
275 */
276 xfs_iunlock(sc->ip, sc->ilock_flags);
277 sc->ilock_flags = 0;
Darrick J. Wong554ba962018-05-04 15:31:21 -0700278 sqi.sc = sc;
279 sqi.last_id = 0;
280 error = xfs_qm_dqiterate(mp, dqtype, xfs_scrub_quota_item, &sqi);
Darrick J. Wongeb41c932018-05-09 10:02:00 -0700281 sc->ilock_flags = XFS_ILOCK_EXCL;
282 xfs_ilock(sc->ip, sc->ilock_flags);
Darrick J. Wong554ba962018-05-04 15:31:21 -0700283 if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK,
284 sqi.last_id * qi->qi_dqperchunk, &error))
285 goto out;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700286
287out:
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700288 return error;
Darrick J. Wongc2fc3382017-10-17 21:37:47 -0700289}