Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  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. */
 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;
 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. */
 81STATIC void
 82xfs_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. */
189int
190xfs_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);
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
287out:
288	/* We set sc->ip earlier, so make sure we clear it now. */
289	sc->ip = NULL;
290out_unlock_quota:
291	mutex_unlock(&qi->qi_quotaofflock);
292	return error;
293
294out_unlock_inode:
295	xfs_iunlock(ip, XFS_ILOCK_EXCL);
296	goto out;
297}