blob: 7b2cf8fd1ce0fd5f04098eca4c423269ef0512b4 [file] [log] [blame]
Darrick J. Wong99d9d8d2017-10-17 21:37:43 -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_rtalloc.h"
36#include "xfs_bmap.h"
37#include "xfs_bmap_util.h"
38#include "xfs_bmap_btree.h"
39#include "xfs_rmap.h"
Darrick J. Wongf6d5fc22018-01-16 18:53:09 -080040#include "xfs_refcount.h"
Darrick J. Wong99d9d8d2017-10-17 21:37:43 -070041#include "scrub/xfs_scrub.h"
42#include "scrub/scrub.h"
43#include "scrub/common.h"
44#include "scrub/btree.h"
45#include "scrub/trace.h"
46
47/* Set us up with an inode's bmap. */
48int
49xfs_scrub_setup_inode_bmap(
50 struct xfs_scrub_context *sc,
51 struct xfs_inode *ip)
52{
53 struct xfs_mount *mp = sc->mp;
54 int error;
55
56 error = xfs_scrub_get_inode(sc, ip);
57 if (error)
58 goto out;
59
60 sc->ilock_flags = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL;
61 xfs_ilock(sc->ip, sc->ilock_flags);
62
63 /*
64 * We don't want any ephemeral data fork updates sitting around
65 * while we inspect block mappings, so wait for directio to finish
66 * and flush dirty data if we have delalloc reservations.
67 */
68 if (S_ISREG(VFS_I(sc->ip)->i_mode) &&
69 sc->sm->sm_type == XFS_SCRUB_TYPE_BMBTD) {
70 inode_dio_wait(VFS_I(sc->ip));
71 error = filemap_write_and_wait(VFS_I(sc->ip)->i_mapping);
72 if (error)
73 goto out;
74 }
75
76 /* Got the inode, lock it and we're ready to go. */
77 error = xfs_scrub_trans_alloc(sc->sm, mp, &sc->tp);
78 if (error)
79 goto out;
80 sc->ilock_flags |= XFS_ILOCK_EXCL;
81 xfs_ilock(sc->ip, XFS_ILOCK_EXCL);
82
83out:
84 /* scrub teardown will unlock and release the inode */
85 return error;
86}
87
88/*
89 * Inode fork block mapping (BMBT) scrubber.
90 * More complex than the others because we have to scrub
91 * all the extents regardless of whether or not the fork
92 * is in btree format.
93 */
94
95struct xfs_scrub_bmap_info {
96 struct xfs_scrub_context *sc;
97 xfs_fileoff_t lastoff;
98 bool is_rt;
99 bool is_shared;
100 int whichfork;
101};
102
Darrick J. Wongd8526572018-01-16 18:53:08 -0800103/* Look for a corresponding rmap for this irec. */
104static inline bool
105xfs_scrub_bmap_get_rmap(
106 struct xfs_scrub_bmap_info *info,
107 struct xfs_bmbt_irec *irec,
108 xfs_agblock_t agbno,
109 uint64_t owner,
110 struct xfs_rmap_irec *rmap)
111{
112 xfs_fileoff_t offset;
113 unsigned int rflags = 0;
114 int has_rmap;
115 int error;
116
117 if (info->whichfork == XFS_ATTR_FORK)
118 rflags |= XFS_RMAP_ATTR_FORK;
119
120 /*
121 * CoW staging extents are owned (on disk) by the refcountbt, so
122 * their rmaps do not have offsets.
123 */
124 if (info->whichfork == XFS_COW_FORK)
125 offset = 0;
126 else
127 offset = irec->br_startoff;
128
129 /*
130 * If the caller thinks this could be a shared bmbt extent (IOWs,
131 * any data fork extent of a reflink inode) then we have to use the
132 * range rmap lookup to make sure we get the correct owner/offset.
133 */
134 if (info->is_shared) {
135 error = xfs_rmap_lookup_le_range(info->sc->sa.rmap_cur, agbno,
136 owner, offset, rflags, rmap, &has_rmap);
137 if (!xfs_scrub_should_check_xref(info->sc, &error,
138 &info->sc->sa.rmap_cur))
139 return false;
140 goto out;
141 }
142
143 /*
144 * Otherwise, use the (faster) regular lookup.
145 */
146 error = xfs_rmap_lookup_le(info->sc->sa.rmap_cur, agbno, 0, owner,
147 offset, rflags, &has_rmap);
148 if (!xfs_scrub_should_check_xref(info->sc, &error,
149 &info->sc->sa.rmap_cur))
150 return false;
151 if (!has_rmap)
152 goto out;
153
154 error = xfs_rmap_get_rec(info->sc->sa.rmap_cur, rmap, &has_rmap);
155 if (!xfs_scrub_should_check_xref(info->sc, &error,
156 &info->sc->sa.rmap_cur))
157 return false;
158
159out:
160 if (!has_rmap)
161 xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
162 irec->br_startoff);
163 return has_rmap;
164}
165
166/* Make sure that we have rmapbt records for this extent. */
167STATIC void
168xfs_scrub_bmap_xref_rmap(
169 struct xfs_scrub_bmap_info *info,
170 struct xfs_bmbt_irec *irec,
171 xfs_agblock_t agbno)
172{
173 struct xfs_rmap_irec rmap;
174 unsigned long long rmap_end;
175 uint64_t owner;
176
177 if (!info->sc->sa.rmap_cur)
178 return;
179
180 if (info->whichfork == XFS_COW_FORK)
181 owner = XFS_RMAP_OWN_COW;
182 else
183 owner = info->sc->ip->i_ino;
184
185 /* Find the rmap record for this irec. */
186 if (!xfs_scrub_bmap_get_rmap(info, irec, agbno, owner, &rmap))
187 return;
188
189 /* Check the rmap. */
190 rmap_end = (unsigned long long)rmap.rm_startblock + rmap.rm_blockcount;
191 if (rmap.rm_startblock > agbno ||
192 agbno + irec->br_blockcount > rmap_end)
193 xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
194 irec->br_startoff);
195
196 /*
197 * Check the logical offsets if applicable. CoW staging extents
198 * don't track logical offsets since the mappings only exist in
199 * memory.
200 */
201 if (info->whichfork != XFS_COW_FORK) {
202 rmap_end = (unsigned long long)rmap.rm_offset +
203 rmap.rm_blockcount;
204 if (rmap.rm_offset > irec->br_startoff ||
205 irec->br_startoff + irec->br_blockcount > rmap_end)
206 xfs_scrub_fblock_xref_set_corrupt(info->sc,
207 info->whichfork, irec->br_startoff);
208 }
209
210 if (rmap.rm_owner != owner)
211 xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
212 irec->br_startoff);
213
214 /*
215 * Check for discrepancies between the unwritten flag in the irec and
216 * the rmap. Note that the (in-memory) CoW fork distinguishes between
217 * unwritten and written extents, but we don't track that in the rmap
218 * records because the blocks are owned (on-disk) by the refcountbt,
219 * which doesn't track unwritten state.
220 */
221 if (owner != XFS_RMAP_OWN_COW &&
222 irec->br_state == XFS_EXT_UNWRITTEN &&
223 !(rmap.rm_flags & XFS_RMAP_UNWRITTEN))
224 xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
225 irec->br_startoff);
226
227 if (info->whichfork == XFS_ATTR_FORK &&
228 !(rmap.rm_flags & XFS_RMAP_ATTR_FORK))
229 xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
230 irec->br_startoff);
231 if (rmap.rm_flags & XFS_RMAP_BMBT_BLOCK)
232 xfs_scrub_fblock_xref_set_corrupt(info->sc, info->whichfork,
233 irec->br_startoff);
234}
235
Darrick J. Wong166d7642018-01-16 18:53:05 -0800236/* Cross-reference a single rtdev extent record. */
237STATIC void
238xfs_scrub_bmap_rt_extent_xref(
239 struct xfs_scrub_bmap_info *info,
240 struct xfs_inode *ip,
241 struct xfs_btree_cur *cur,
242 struct xfs_bmbt_irec *irec)
243{
244 if (info->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
245 return;
246}
247
248/* Cross-reference a single datadev extent record. */
249STATIC void
250xfs_scrub_bmap_extent_xref(
251 struct xfs_scrub_bmap_info *info,
252 struct xfs_inode *ip,
253 struct xfs_btree_cur *cur,
254 struct xfs_bmbt_irec *irec)
255{
Darrick J. Wong52dc4b42018-01-16 18:53:06 -0800256 struct xfs_mount *mp = info->sc->mp;
257 xfs_agnumber_t agno;
258 xfs_agblock_t agbno;
259 xfs_extlen_t len;
260 int error;
261
Darrick J. Wong166d7642018-01-16 18:53:05 -0800262 if (info->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
263 return;
Darrick J. Wong52dc4b42018-01-16 18:53:06 -0800264
265 agno = XFS_FSB_TO_AGNO(mp, irec->br_startblock);
266 agbno = XFS_FSB_TO_AGBNO(mp, irec->br_startblock);
267 len = irec->br_blockcount;
268
269 error = xfs_scrub_ag_init(info->sc, agno, &info->sc->sa);
270 if (!xfs_scrub_fblock_process_error(info->sc, info->whichfork,
271 irec->br_startoff, &error))
272 return;
273
274 xfs_scrub_xref_is_used_space(info->sc, agbno, len);
Darrick J. Wong2e6f2752018-01-16 18:53:07 -0800275 xfs_scrub_xref_is_not_inode_chunk(info->sc, agbno, len);
Darrick J. Wongd8526572018-01-16 18:53:08 -0800276 xfs_scrub_bmap_xref_rmap(info, irec, agbno);
Darrick J. Wongf6d5fc22018-01-16 18:53:09 -0800277 switch (info->whichfork) {
278 case XFS_DATA_FORK:
279 if (xfs_is_reflink_inode(info->sc->ip))
280 break;
281 /* fall through */
282 case XFS_ATTR_FORK:
283 xfs_scrub_xref_is_not_shared(info->sc, agbno,
284 irec->br_blockcount);
285 break;
286 case XFS_COW_FORK:
287 xfs_scrub_xref_is_cow_staging(info->sc, agbno,
288 irec->br_blockcount);
289 break;
290 }
Darrick J. Wong52dc4b42018-01-16 18:53:06 -0800291
292 xfs_scrub_ag_free(info->sc, &info->sc->sa);
Darrick J. Wong166d7642018-01-16 18:53:05 -0800293}
294
Darrick J. Wong99d9d8d2017-10-17 21:37:43 -0700295/* Scrub a single extent record. */
296STATIC int
297xfs_scrub_bmap_extent(
298 struct xfs_inode *ip,
299 struct xfs_btree_cur *cur,
300 struct xfs_scrub_bmap_info *info,
301 struct xfs_bmbt_irec *irec)
302{
303 struct xfs_mount *mp = info->sc->mp;
304 struct xfs_buf *bp = NULL;
305 int error = 0;
306
307 if (cur)
308 xfs_btree_get_block(cur, 0, &bp);
309
310 /*
311 * Check for out-of-order extents. This record could have come
312 * from the incore list, for which there is no ordering check.
313 */
314 if (irec->br_startoff < info->lastoff)
315 xfs_scrub_fblock_set_corrupt(info->sc, info->whichfork,
316 irec->br_startoff);
317
318 /* There should never be a "hole" extent in either extent list. */
319 if (irec->br_startblock == HOLESTARTBLOCK)
320 xfs_scrub_fblock_set_corrupt(info->sc, info->whichfork,
321 irec->br_startoff);
322
323 /*
324 * Check for delalloc extents. We never iterate the ones in the
325 * in-core extent scan, and we should never see these in the bmbt.
326 */
327 if (isnullstartblock(irec->br_startblock))
328 xfs_scrub_fblock_set_corrupt(info->sc, info->whichfork,
329 irec->br_startoff);
330
331 /* Make sure the extent points to a valid place. */
332 if (irec->br_startblock + irec->br_blockcount <= irec->br_startblock)
333 xfs_scrub_fblock_set_corrupt(info->sc, info->whichfork,
334 irec->br_startoff);
335 if (info->is_rt &&
336 (!xfs_verify_rtbno(mp, irec->br_startblock) ||
337 !xfs_verify_rtbno(mp, irec->br_startblock +
338 irec->br_blockcount - 1)))
339 xfs_scrub_fblock_set_corrupt(info->sc, info->whichfork,
340 irec->br_startoff);
341 if (!info->is_rt &&
342 (!xfs_verify_fsbno(mp, irec->br_startblock) ||
343 !xfs_verify_fsbno(mp, irec->br_startblock +
344 irec->br_blockcount - 1)))
345 xfs_scrub_fblock_set_corrupt(info->sc, info->whichfork,
346 irec->br_startoff);
347
348 /* We don't allow unwritten extents on attr forks. */
349 if (irec->br_state == XFS_EXT_UNWRITTEN &&
350 info->whichfork == XFS_ATTR_FORK)
351 xfs_scrub_fblock_set_corrupt(info->sc, info->whichfork,
352 irec->br_startoff);
353
Darrick J. Wong166d7642018-01-16 18:53:05 -0800354 if (info->is_rt)
355 xfs_scrub_bmap_rt_extent_xref(info, ip, cur, irec);
356 else
357 xfs_scrub_bmap_extent_xref(info, ip, cur, irec);
358
Darrick J. Wong99d9d8d2017-10-17 21:37:43 -0700359 info->lastoff = irec->br_startoff + irec->br_blockcount;
360 return error;
361}
362
363/* Scrub a bmbt record. */
364STATIC int
365xfs_scrub_bmapbt_rec(
366 struct xfs_scrub_btree *bs,
367 union xfs_btree_rec *rec)
368{
Darrick J. Wong99d9d8d2017-10-17 21:37:43 -0700369 struct xfs_bmbt_irec irec;
370 struct xfs_scrub_bmap_info *info = bs->private;
371 struct xfs_inode *ip = bs->cur->bc_private.b.ip;
372 struct xfs_buf *bp = NULL;
373 struct xfs_btree_block *block;
374 uint64_t owner;
375 int i;
376
377 /*
378 * Check the owners of the btree blocks up to the level below
379 * the root since the verifiers don't do that.
380 */
381 if (xfs_sb_version_hascrc(&bs->cur->bc_mp->m_sb) &&
382 bs->cur->bc_ptrs[0] == 1) {
383 for (i = 0; i < bs->cur->bc_nlevels - 1; i++) {
384 block = xfs_btree_get_block(bs->cur, i, &bp);
385 owner = be64_to_cpu(block->bb_u.l.bb_owner);
386 if (owner != ip->i_ino)
387 xfs_scrub_fblock_set_corrupt(bs->sc,
388 info->whichfork, 0);
389 }
390 }
391
392 /* Set up the in-core record and scrub it. */
Christoph Hellwig6bdcf262017-11-03 10:34:46 -0700393 xfs_bmbt_disk_get_all(&rec->bmbt, &irec);
Darrick J. Wong99d9d8d2017-10-17 21:37:43 -0700394 return xfs_scrub_bmap_extent(ip, bs->cur, info, &irec);
395}
396
397/* Scan the btree records. */
398STATIC int
399xfs_scrub_bmap_btree(
400 struct xfs_scrub_context *sc,
401 int whichfork,
402 struct xfs_scrub_bmap_info *info)
403{
404 struct xfs_owner_info oinfo;
405 struct xfs_mount *mp = sc->mp;
406 struct xfs_inode *ip = sc->ip;
407 struct xfs_btree_cur *cur;
408 int error;
409
410 cur = xfs_bmbt_init_cursor(mp, sc->tp, ip, whichfork);
411 xfs_rmap_ino_bmbt_owner(&oinfo, ip->i_ino, whichfork);
412 error = xfs_scrub_btree(sc, cur, xfs_scrub_bmapbt_rec, &oinfo, info);
413 xfs_btree_del_cursor(cur, error ? XFS_BTREE_ERROR :
414 XFS_BTREE_NOERROR);
415 return error;
416}
417
418/*
419 * Scrub an inode fork's block mappings.
420 *
421 * First we scan every record in every btree block, if applicable.
422 * Then we unconditionally scan the incore extent cache.
423 */
424STATIC int
425xfs_scrub_bmap(
426 struct xfs_scrub_context *sc,
427 int whichfork)
428{
429 struct xfs_bmbt_irec irec;
Christoph Hellwig88aa5de2017-11-06 11:53:58 -0800430 struct xfs_scrub_bmap_info info = { NULL };
Darrick J. Wong99d9d8d2017-10-17 21:37:43 -0700431 struct xfs_mount *mp = sc->mp;
432 struct xfs_inode *ip = sc->ip;
433 struct xfs_ifork *ifp;
434 xfs_fileoff_t endoff;
Christoph Hellwigb2b17122017-11-03 10:34:43 -0700435 struct xfs_iext_cursor icur;
Darrick J. Wong99d9d8d2017-10-17 21:37:43 -0700436 int error = 0;
437
438 ifp = XFS_IFORK_PTR(ip, whichfork);
439
440 info.is_rt = whichfork == XFS_DATA_FORK && XFS_IS_REALTIME_INODE(ip);
441 info.whichfork = whichfork;
442 info.is_shared = whichfork == XFS_DATA_FORK && xfs_is_reflink_inode(ip);
443 info.sc = sc;
444
445 switch (whichfork) {
446 case XFS_COW_FORK:
447 /* Non-existent CoW forks are ignorable. */
448 if (!ifp)
449 goto out;
450 /* No CoW forks on non-reflink inodes/filesystems. */
451 if (!xfs_is_reflink_inode(ip)) {
452 xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL);
453 goto out;
454 }
455 break;
456 case XFS_ATTR_FORK:
457 if (!ifp)
458 goto out;
459 if (!xfs_sb_version_hasattr(&mp->m_sb) &&
460 !xfs_sb_version_hasattr2(&mp->m_sb))
461 xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL);
462 break;
463 default:
464 ASSERT(whichfork == XFS_DATA_FORK);
465 break;
466 }
467
468 /* Check the fork values */
469 switch (XFS_IFORK_FORMAT(ip, whichfork)) {
470 case XFS_DINODE_FMT_UUID:
471 case XFS_DINODE_FMT_DEV:
472 case XFS_DINODE_FMT_LOCAL:
473 /* No mappings to check. */
474 goto out;
475 case XFS_DINODE_FMT_EXTENTS:
476 if (!(ifp->if_flags & XFS_IFEXTENTS)) {
477 xfs_scrub_fblock_set_corrupt(sc, whichfork, 0);
478 goto out;
479 }
480 break;
481 case XFS_DINODE_FMT_BTREE:
482 if (whichfork == XFS_COW_FORK) {
483 xfs_scrub_fblock_set_corrupt(sc, whichfork, 0);
484 goto out;
485 }
486
487 error = xfs_scrub_bmap_btree(sc, whichfork, &info);
488 if (error)
489 goto out;
490 break;
491 default:
492 xfs_scrub_fblock_set_corrupt(sc, whichfork, 0);
493 goto out;
494 }
495
496 if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
497 goto out;
498
499 /* Now try to scrub the in-memory extent list. */
500 if (!(ifp->if_flags & XFS_IFEXTENTS)) {
501 error = xfs_iread_extents(sc->tp, ip, whichfork);
502 if (!xfs_scrub_fblock_process_error(sc, whichfork, 0, &error))
503 goto out;
504 }
505
506 /* Find the offset of the last extent in the mapping. */
507 error = xfs_bmap_last_offset(ip, &endoff, whichfork);
508 if (!xfs_scrub_fblock_process_error(sc, whichfork, 0, &error))
509 goto out;
510
511 /* Scrub extent records. */
512 info.lastoff = 0;
513 ifp = XFS_IFORK_PTR(ip, whichfork);
Darrick J. Wong2b9e9b52018-01-08 10:49:03 -0800514 for_each_xfs_iext(ifp, &icur, &irec) {
Darrick J. Wong99d9d8d2017-10-17 21:37:43 -0700515 if (xfs_scrub_should_terminate(sc, &error))
516 break;
517 if (isnullstartblock(irec.br_startblock))
518 continue;
519 if (irec.br_startoff >= endoff) {
520 xfs_scrub_fblock_set_corrupt(sc, whichfork,
521 irec.br_startoff);
522 goto out;
523 }
524 error = xfs_scrub_bmap_extent(ip, NULL, &info, &irec);
525 if (error)
526 goto out;
527 }
528
529out:
530 return error;
531}
532
533/* Scrub an inode's data fork. */
534int
535xfs_scrub_bmap_data(
536 struct xfs_scrub_context *sc)
537{
538 return xfs_scrub_bmap(sc, XFS_DATA_FORK);
539}
540
541/* Scrub an inode's attr fork. */
542int
543xfs_scrub_bmap_attr(
544 struct xfs_scrub_context *sc)
545{
546 return xfs_scrub_bmap(sc, XFS_ATTR_FORK);
547}
548
549/* Scrub an inode's CoW fork. */
550int
551xfs_scrub_bmap_cow(
552 struct xfs_scrub_context *sc)
553{
554 if (!xfs_is_reflink_inode(sc->ip))
555 return -ENOENT;
556
557 return xfs_scrub_bmap(sc, XFS_COW_FORK);
558}