]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - repair/scan.c
Update copyright/license notices to match SGI legal prefered boilerplate.
[thirdparty/xfsprogs-dev.git] / repair / scan.c
CommitLineData
2bd0ea18 1/*
da23017d
NS
2 * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
dfc130f3 4 *
da23017d
NS
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
2bd0ea18 7 * published by the Free Software Foundation.
dfc130f3 8 *
da23017d
NS
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
dfc130f3 13 *
da23017d
NS
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2bd0ea18
NS
17 */
18
19#include <libxfs.h>
20#include "avl.h"
21#include "globals.h"
22#include "agheader.h"
23#include "incore.h"
24#include "protos.h"
25#include "err_protos.h"
26#include "dinode.h"
27#include "scan.h"
28#include "versions.h"
29#include "bmap.h"
30
31extern int verify_set_agheader(xfs_mount_t *mp, xfs_buf_t *sbuf, xfs_sb_t *sb,
32 xfs_agf_t *agf, xfs_agi_t *agi, xfs_agnumber_t i);
33
34static xfs_mount_t *mp = NULL;
35static xfs_extlen_t bno_agffreeblks;
36static xfs_extlen_t cnt_agffreeblks;
37static xfs_extlen_t bno_agflongest;
38static xfs_extlen_t cnt_agflongest;
39static xfs_agino_t agicount;
40static xfs_agino_t agifreecount;
41
42void
43set_mp(xfs_mount_t *mpp)
44{
45 mp = mpp;
46}
47
48void
49scan_sbtree(
50 xfs_agblock_t root,
51 int nlevels,
52 xfs_agnumber_t agno,
53 int suspect,
54 void (*func)(xfs_btree_sblock_t *block,
55 int level,
56 xfs_agblock_t bno,
57 xfs_agnumber_t agno,
58 int suspect,
59 int isroot),
60 int isroot)
61{
62 xfs_buf_t *bp;
63
64 bp = libxfs_readbuf(mp->m_dev, XFS_AGB_TO_DADDR(mp, agno, root),
65 XFS_FSB_TO_BB(mp, 1), 0);
66 if (!bp) {
507f4e33 67 do_error(_("can't read btree block %d/%d\n"), agno, root);
2bd0ea18
NS
68 return;
69 }
70 (*func)((xfs_btree_sblock_t *)XFS_BUF_PTR(bp),
71 nlevels - 1, root, agno, suspect, isroot);
72 libxfs_putbuf(bp);
73}
74
75/*
76 * returns 1 on bad news (inode needs to be cleared), 0 on good
77 */
78int
79scan_lbtree(
80 xfs_dfsbno_t root,
81 int nlevels,
82 int (*func)(xfs_btree_lblock_t *block,
83 int level,
84 int type,
85 int whichfork,
86 xfs_dfsbno_t bno,
87 xfs_ino_t ino,
88 xfs_drfsbno_t *tot,
89 __uint64_t *nex,
90 blkmap_t **blkmapp,
91 bmap_cursor_t *bm_cursor,
92 int isroot,
93 int check_dups,
94 int *dirty),
95 int type,
96 int whichfork,
97 xfs_ino_t ino,
98 xfs_drfsbno_t *tot,
99 __uint64_t *nex,
100 blkmap_t **blkmapp,
101 bmap_cursor_t *bm_cursor,
102 int isroot,
103 int check_dups)
104{
105 xfs_buf_t *bp;
106 int err;
107 int dirty = 0;
108
109 bp = libxfs_readbuf(mp->m_dev, XFS_FSB_TO_DADDR(mp, root),
110 XFS_FSB_TO_BB(mp, 1), 0);
111 if (!bp) {
507f4e33 112 do_error(_("can't read btree block %d/%d\n"),
2bd0ea18
NS
113 XFS_FSB_TO_AGNO(mp, root),
114 XFS_FSB_TO_AGBNO(mp, root));
115 return(1);
116 }
117 err = (*func)((xfs_btree_lblock_t *)XFS_BUF_PTR(bp), nlevels - 1,
118 type, whichfork, root, ino, tot, nex, blkmapp,
119 bm_cursor, isroot, check_dups, &dirty);
120
27527004 121 ASSERT(dirty == 0 || (dirty && !no_modify));
2bd0ea18
NS
122
123 if (dirty && !no_modify)
124 libxfs_writebuf(bp, 0);
125 else
126 libxfs_putbuf(bp);
127
128 return(err);
129}
130
131int
132scanfunc_bmap(
133 xfs_btree_lblock_t *ablock,
134 int level,
135 int type,
136 int whichfork,
137 xfs_dfsbno_t bno,
138 xfs_ino_t ino,
139 xfs_drfsbno_t *tot,
140 __uint64_t *nex,
141 blkmap_t **blkmapp,
142 bmap_cursor_t *bm_cursor,
143 int isroot,
144 int check_dups,
145 int *dirty)
146{
147 xfs_bmbt_block_t *block = (xfs_bmbt_block_t *)ablock;
148 int i;
149 int err;
150 xfs_bmbt_ptr_t *pp;
151 xfs_bmbt_key_t *pkey;
152 xfs_bmbt_rec_32_t *rp;
153 xfs_dfiloff_t first_key;
154 xfs_dfiloff_t last_key;
155 char *forkname;
156
157 if (whichfork == XFS_DATA_FORK)
507f4e33 158 forkname = _("data");
2bd0ea18 159 else
507f4e33 160 forkname = _("attr");
2bd0ea18
NS
161
162 /*
dfc130f3 163 * unlike the ag freeblock btrees, if anything looks wrong
2bd0ea18
NS
164 * in an inode bmap tree, just bail. it's possible that
165 * we'll miss a case where the to-be-toasted inode and
166 * another inode are claiming the same block but that's
167 * highly unlikely.
168 */
169 if (INT_GET(block->bb_magic, ARCH_CONVERT) != XFS_BMAP_MAGIC) {
170 do_warn(
507f4e33
NS
171 _("bad magic # %#x in inode %llu (%s fork) bmbt block %llu\n"),
172 INT_GET(block->bb_magic, ARCH_CONVERT),
173 ino, forkname, bno);
2bd0ea18
NS
174 return(1);
175 }
176 if (INT_GET(block->bb_level, ARCH_CONVERT) != level) {
177 do_warn(
507f4e33
NS
178_("expected level %d got %d in inode %llu, (%s fork) bmbt block %llu\n"),
179 level, INT_GET(block->bb_level, ARCH_CONVERT),
180 ino, forkname, bno);
2bd0ea18
NS
181 return(1);
182 }
183
184 if (check_dups == 0) {
185 /*
186 * check sibling pointers. if bad we have a conflict
187 * between the sibling pointers and the child pointers
188 * in the parent block. blow out the inode if that happens
189 */
190 if (bm_cursor->level[level].fsbno != NULLDFSBNO) {
191 /*
192 * this is not the first block on this level
193 * so the cursor for this level has recorded the
194 * values for this's block left-sibling.
195 */
196 if (bno != bm_cursor->level[level].right_fsbno) {
197 do_warn(
507f4e33
NS
198_("bad fwd (right) sibling pointer (saw %llu parent block says %llu)\n"
199 "\tin inode %llu (%s fork) bmap btree block %llu\n"),
2bd0ea18 200 bm_cursor->level[level].right_fsbno,
507f4e33 201 bno, ino, forkname,
2bd0ea18
NS
202 bm_cursor->level[level].fsbno);
203 return(1);
204 }
205 if (INT_GET(block->bb_leftsib, ARCH_CONVERT) !=
206 bm_cursor->level[level].fsbno) {
207 do_warn(
507f4e33
NS
208_("bad back (left) sibling pointer (saw %llu parent block says %llu)\n"
209 "\tin inode %llu (%s fork) bmap btree block %llu\n"),
210 INT_GET(block->bb_leftsib,
211 ARCH_CONVERT),
212 bm_cursor->level[level].fsbno,
2bd0ea18
NS
213 ino, forkname, bno);
214 return(1);
215 }
216 } else {
217 /*
218 * This is the first or only block on this level.
219 * Check that the left sibling pointer is NULL
220 */
221 if (INT_GET(block->bb_leftsib, ARCH_CONVERT) !=
222 NULLDFSBNO) {
223 do_warn(
507f4e33
NS
224_("bad back (left) sibling pointer (saw %llu should be NULL (0))\n"
225 "\tin inode %llu (%s fork) bmap btree block %llu\n"),
226 INT_GET(block->bb_leftsib,
227 ARCH_CONVERT),
2bd0ea18
NS
228 ino, forkname, bno);
229 return(1);
230 }
231 }
232
233 /*
234 * update cursor block pointers to reflect this block
235 */
236 bm_cursor->level[level].fsbno = bno;
507f4e33
NS
237 bm_cursor->level[level].left_fsbno =
238 INT_GET(block->bb_leftsib, ARCH_CONVERT);
239 bm_cursor->level[level].right_fsbno =
240 INT_GET(block->bb_rightsib, ARCH_CONVERT);
2bd0ea18
NS
241
242 switch (get_fsbno_state(mp, bno)) {
243 case XR_E_UNKNOWN:
244 case XR_E_FREE1:
245 case XR_E_FREE:
246 set_fsbno_state(mp, bno, XR_E_INUSE);
247 break;
248 case XR_E_FS_MAP:
249 case XR_E_INUSE:
250 /*
251 * we'll try and continue searching here since
252 * the block looks like it's been claimed by file
253 * to store user data, a directory to store directory
254 * data, or the space allocation btrees but since
255 * we made it here, the block probably
256 * contains btree data.
257 */
258 set_fsbno_state(mp, bno, XR_E_MULT);
259 do_warn(
507f4e33 260 _("inode 0x%llx bmap block 0x%llx claimed, state is %d\n"),
2bd0ea18
NS
261 ino, (__uint64_t) bno,
262 get_fsbno_state(mp, bno));
263 break;
264 case XR_E_MULT:
265 case XR_E_INUSE_FS:
266 set_fsbno_state(mp, bno, XR_E_MULT);
267 do_warn(
507f4e33 268 _("inode 0x%llx bmap block 0x%llx claimed, state is %d\n"),
2bd0ea18
NS
269 ino, (__uint64_t) bno,
270 get_fsbno_state(mp, bno));
271 /*
272 * if we made it to here, this is probably a bmap block
273 * that is being used by *another* file as a bmap block
274 * so the block will be valid. Both files should be
275 * trashed along with any other file that impinges on
276 * any blocks referenced by either file. So we
277 * continue searching down this btree to mark all
278 * blocks duplicate
279 */
280 break;
281 case XR_E_BAD_STATE:
282 default:
283 do_warn(
507f4e33 284 _("bad state %d, inode 0x%llx bmap block 0x%llx\n"),
2bd0ea18
NS
285 get_fsbno_state(mp, bno),
286 ino, (__uint64_t) bno);
287 break;
288 }
289 } else {
290 /*
291 * attribute fork for realtime files is in the regular
292 * filesystem
293 */
294 if (type != XR_INO_RTDATA || whichfork != XFS_DATA_FORK) {
295 if (search_dup_extent(mp, XFS_FSB_TO_AGNO(mp, bno),
296 XFS_FSB_TO_AGBNO(mp, bno)))
297 return(1);
298 } else {
299 if (search_rt_dup_extent(mp, bno))
300 return(1);
301 }
302 }
303 (*tot)++;
304 if (level == 0) {
507f4e33
NS
305 if (INT_GET(block->bb_numrecs, ARCH_CONVERT) >
306 mp->m_bmap_dmxr[0] ||
307 (isroot == 0 && INT_GET(block->bb_numrecs, ARCH_CONVERT) <
308 mp->m_bmap_dmnr[0])) {
309 do_warn(
310 _("inode 0x%llx bad # of bmap records (%u, min - %u, max - %u)\n"),
2bd0ea18
NS
311 ino, INT_GET(block->bb_numrecs, ARCH_CONVERT),
312 mp->m_bmap_dmnr[0], mp->m_bmap_dmxr[0]);
313 return(1);
314 }
315 rp = (xfs_bmbt_rec_32_t *)
316 XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, xfs_bmbt,
317 block, 1, mp->m_bmap_dmxr[0]);
318 *nex += INT_GET(block->bb_numrecs, ARCH_CONVERT);
319 /*
320 * XXX - if we were going to fix up the btree record,
321 * we'd do it right here. For now, if there's a problem,
322 * we'll bail out and presumably clear the inode.
323 */
324 if (check_dups == 0) {
507f4e33
NS
325 err = process_bmbt_reclist(mp, rp,
326 INT_GET(block->bb_numrecs,
327 ARCH_CONVERT),
2bd0ea18
NS
328 type, ino, tot, blkmapp,
329 &first_key, &last_key,
330 whichfork);
331 if (err)
332 return(1);
333 /*
334 * check that key ordering is monotonically increasing.
335 * if the last_key value in the cursor is set to
336 * NULLDFILOFF, then we know this is the first block
337 * on the leaf level and we shouldn't check the
338 * last_key value.
339 */
340 if (first_key <= bm_cursor->level[level].last_key &&
341 bm_cursor->level[level].last_key !=
342 NULLDFILOFF) {
343 do_warn(
507f4e33 344_("out-of-order bmap key (file offset) in inode %llu, %s fork, fsbno %llu\n"),
2bd0ea18
NS
345 ino, forkname, bno);
346 return(1);
347 }
348 /*
349 * update cursor keys to reflect this block.
350 * don't have to check if last_key is > first_key
351 * since that gets checked by process_bmbt_reclist.
352 */
353 bm_cursor->level[level].first_key = first_key;
354 bm_cursor->level[level].last_key = last_key;
355
356 return(0);
357 } else
358 return(scan_bmbt_reclist(mp, rp, INT_GET(block->bb_numrecs, ARCH_CONVERT),
359 type, ino, tot, whichfork));
360 }
361 if (INT_GET(block->bb_numrecs, ARCH_CONVERT) > mp->m_bmap_dmxr[1] ||
507f4e33
NS
362 (isroot == 0 &&
363 INT_GET(block->bb_numrecs, ARCH_CONVERT) < mp->m_bmap_dmnr[1])) {
364 do_warn(
365 _("inode 0x%llx bad # of bmap records (%u, min - %u, max - %u)\n"),
2bd0ea18
NS
366 ino, INT_GET(block->bb_numrecs, ARCH_CONVERT),
367 mp->m_bmap_dmnr[1], mp->m_bmap_dmxr[1]);
368 return(1);
369 }
370 pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, xfs_bmbt, block, 1,
371 mp->m_bmap_dmxr[1]);
372 pkey = XFS_BTREE_KEY_ADDR(mp->m_sb.sb_blocksize, xfs_bmbt, block, 1,
373 mp->m_bmap_dmxr[1]);
374
375 last_key = NULLDFILOFF;
376
377 for (i = 0, err = 0; i < INT_GET(block->bb_numrecs, ARCH_CONVERT); i++) {
378 /*
379 * XXX - if we were going to fix up the interior btree nodes,
380 * we'd do it right here. For now, if there's a problem,
381 * we'll bail out and presumably clear the inode.
382 */
383 if (!verify_dfsbno(mp, INT_GET(pp[i], ARCH_CONVERT))) {
507f4e33 384 do_warn(_("bad bmap btree ptr 0x%llx in ino %llu\n"),
2bd0ea18
NS
385 INT_GET(pp[i], ARCH_CONVERT), ino);
386 return(1);
387 }
388
507f4e33
NS
389 err = scan_lbtree(INT_GET(pp[i], ARCH_CONVERT),
390 level, scanfunc_bmap, type, whichfork,
2bd0ea18
NS
391 ino, tot, nex, blkmapp, bm_cursor, 0,
392 check_dups);
393 if (err)
394 return(1);
395
396 /*
397 * fix key (offset) mismatches between the first key
398 * in the child block (as recorded in the cursor) and the
399 * key in the interior node referencing the child block.
400 *
401 * fixes cases where entries have been shifted between
402 * child blocks but the parent hasn't been updated. We
403 * don't have to worry about the key values in the cursor
404 * not being set since we only look at the key values of
405 * our child and those are guaranteed to be set by the
406 * call to scan_lbtree() above.
407 */
507f4e33
NS
408 if (check_dups == 0 &&
409 INT_GET(pkey[i].br_startoff, ARCH_CONVERT) !=
2bd0ea18
NS
410 bm_cursor->level[level-1].first_key) {
411 if (!no_modify) {
412 do_warn(
507f4e33
NS
413 _("correcting bt key (was %llu, now %llu) in inode %llu\n"
414 "\t\t%s fork, btree block %llu\n"),
415 INT_GET(pkey[i].br_startoff,
416 ARCH_CONVERT),
2bd0ea18 417 bm_cursor->level[level-1].first_key,
507f4e33 418 ino,
2bd0ea18
NS
419 forkname, bno);
420 *dirty = 1;
507f4e33
NS
421 INT_SET(pkey[i].br_startoff, ARCH_CONVERT,
422 bm_cursor->level[level-1].first_key);
2bd0ea18
NS
423 } else {
424 do_warn(
507f4e33
NS
425 _("bad btree key (is %llu, should be %llu) in inode %llu\n"
426 "\t\t%s fork, btree block %llu\n"),
427 INT_GET(pkey[i].br_startoff,
428 ARCH_CONVERT),
2bd0ea18 429 bm_cursor->level[level-1].first_key,
507f4e33 430 ino,
2bd0ea18
NS
431 forkname, bno);
432 }
433 }
434 }
435
436 /*
437 * Check that the last child block's forward sibling pointer
438 * is NULL.
439 */
dfc130f3 440 if (check_dups == 0 &&
2bd0ea18
NS
441 bm_cursor->level[level - 1].right_fsbno != NULLDFSBNO) {
442 do_warn(
507f4e33
NS
443 _("bad fwd (right) sibling pointer (saw %llu should be NULLDFSBNO)\n"
444 "\tin inode %llu (%s fork) bmap btree block %llu\n"),
445 bm_cursor->level[level - 1].right_fsbno,
2bd0ea18
NS
446 ino, forkname,
447 bm_cursor->level[level].fsbno);
448 return(1);
449 }
450
451 /*
452 * update cursor keys to reflect this block
453 */
454 if (check_dups == 0) {
455 bm_cursor->level[level].first_key =
456 INT_GET(pkey[0].br_startoff, ARCH_CONVERT);
457 i = INT_GET(block->bb_numrecs, ARCH_CONVERT) - 1;
458 bm_cursor->level[level].last_key =
459 INT_GET(pkey[i].br_startoff, ARCH_CONVERT);
460 }
461
462 return(0);
463}
464
465void
466scanfunc_bno(
467 xfs_btree_sblock_t *ablock,
468 int level,
469 xfs_agblock_t bno,
470 xfs_agnumber_t agno,
471 int suspect,
472 int isroot
473 )
474{
475 xfs_agblock_t b;
476 xfs_alloc_block_t *block = (xfs_alloc_block_t *)ablock;
477 int i;
478 xfs_alloc_ptr_t *pp;
479 xfs_alloc_rec_t *rp;
480 int hdr_errors = 0;
481 int numrecs;
482 int state;
483
484 if (INT_GET(block->bb_magic, ARCH_CONVERT) != XFS_ABTB_MAGIC) {
507f4e33 485 do_warn(_("bad magic # %#x in btbno block %d/%d\n"),
2bd0ea18
NS
486 INT_GET(block->bb_magic, ARCH_CONVERT), agno, bno);
487 hdr_errors++;
488 if (suspect)
489 return;
490 }
491 if (INT_GET(block->bb_level, ARCH_CONVERT) != level) {
507f4e33
NS
492 do_warn(_("expected level %d got %d in btbno block %d/%d\n"),
493 level, INT_GET(block->bb_level, ARCH_CONVERT),
494 agno, bno);
2bd0ea18
NS
495 hdr_errors++;
496 if (suspect)
497 return;
498 }
499
500 /*
501 * check for btree blocks multiply claimed
502 */
503 state = get_agbno_state(mp, agno, bno);
504
505 switch (state) {
506 case XR_E_UNKNOWN:
507 set_agbno_state(mp, agno, bno, XR_E_FS_MAP);
508 break;
509 default:
510 set_agbno_state(mp, agno, bno, XR_E_MULT);
511 do_warn(
507f4e33 512_("bno freespace btree block claimed (state %d), agno %d, bno %d, suspect %d\n"),
2bd0ea18
NS
513 state, agno, bno, suspect);
514 return;
515 }
516
517 if (level == 0) {
518 numrecs = INT_GET(block->bb_numrecs, ARCH_CONVERT);
519
507f4e33
NS
520 if (INT_GET(block->bb_numrecs, ARCH_CONVERT) >
521 mp->m_alloc_mxr[0]) {
2bd0ea18
NS
522 numrecs = mp->m_alloc_mxr[0];
523 hdr_errors++;
524 }
507f4e33
NS
525 if (isroot == 0 && INT_GET(block->bb_numrecs, ARCH_CONVERT) <
526 mp->m_alloc_mnr[0]) {
2bd0ea18
NS
527 numrecs = mp->m_alloc_mnr[0];
528 hdr_errors++;
529 }
530
531 if (hdr_errors)
532 suspect++;
533
534 rp = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, xfs_alloc, block,
535 1, mp->m_alloc_mxr[0]);
536 for (i = 0; i < numrecs; i++) {
537 if (INT_GET(rp[i].ar_blockcount, ARCH_CONVERT) == 0 ||
507f4e33
NS
538 INT_GET(rp[i].ar_startblock, ARCH_CONVERT) == 0 ||
539 !verify_agbno(mp, agno,
540 INT_GET(rp[i].ar_startblock, ARCH_CONVERT)) ||
541 INT_GET(rp[i].ar_blockcount, ARCH_CONVERT) >
542 MAXEXTLEN)
2bd0ea18
NS
543 continue;
544
507f4e33
NS
545 bno_agffreeblks +=
546 INT_GET(rp[i].ar_blockcount, ARCH_CONVERT);
547 if (INT_GET(rp[i].ar_blockcount, ARCH_CONVERT) >
548 bno_agflongest)
549 bno_agflongest = INT_GET(rp[i].ar_blockcount,
550 ARCH_CONVERT);
2bd0ea18 551 for (b = INT_GET(rp[i].ar_startblock, ARCH_CONVERT);
507f4e33
NS
552 b < INT_GET(rp[i].ar_startblock, ARCH_CONVERT) +
553 INT_GET(rp[i].ar_blockcount, ARCH_CONVERT);
2bd0ea18
NS
554 b++) {
555 if (get_agbno_state(mp, agno, b)
556 == XR_E_UNKNOWN)
557 set_agbno_state(mp, agno, b,
558 XR_E_FREE1);
559 else {
507f4e33
NS
560 do_warn(
561 _("block (%d,%d) multiply claimed by bno space tree, state - %d\n"),
562 agno, b,
563 get_agbno_state(mp, agno, b));
2bd0ea18
NS
564 }
565 }
566 }
567 return;
568 }
569
570 /*
571 * interior record
572 */
573 pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, xfs_alloc, block, 1,
574 mp->m_alloc_mxr[1]);
575
576 numrecs = INT_GET(block->bb_numrecs, ARCH_CONVERT);
577 if (INT_GET(block->bb_numrecs, ARCH_CONVERT) > mp->m_alloc_mxr[1]) {
578 numrecs = mp->m_alloc_mxr[1];
579 hdr_errors++;
580 }
507f4e33
NS
581 if (isroot == 0 &&
582 INT_GET(block->bb_numrecs, ARCH_CONVERT) < mp->m_alloc_mnr[1]) {
2bd0ea18
NS
583 numrecs = mp->m_alloc_mnr[1];
584 hdr_errors++;
585 }
586
587 /*
588 * don't pass bogus tree flag down further if this block
589 * looked ok. bail out if two levels in a row look bad.
590 */
591
592 if (suspect && !hdr_errors)
593 suspect = 0;
594
595 if (hdr_errors) {
596 if (suspect)
597 return;
598 else suspect++;
599 }
600
601 for (i = 0; i < numrecs; i++) {
602 /*
603 * XXX - put sibling detection right here.
604 * we know our sibling chain is good. So as we go,
605 * we check the entry before and after each entry.
606 * If either of the entries references a different block,
607 * check the sibling pointer. If there's a sibling
608 * pointer mismatch, try and extract as much data
dfc130f3 609 * as possible.
2bd0ea18 610 */
507f4e33
NS
611 if (INT_GET(pp[i], ARCH_CONVERT) != 0 &&
612 verify_agbno(mp, agno, INT_GET(pp[i], ARCH_CONVERT)))
613 scan_sbtree(INT_GET(pp[i], ARCH_CONVERT),
614 level, agno, suspect, scanfunc_bno, 0);
2bd0ea18
NS
615 }
616}
617
618void
619scanfunc_cnt(
620 xfs_btree_sblock_t *ablock,
621 int level,
622 xfs_agblock_t bno,
623 xfs_agnumber_t agno,
624 int suspect,
625 int isroot
626 )
627{
628 xfs_alloc_block_t *block;
629 xfs_alloc_ptr_t *pp;
630 xfs_alloc_rec_t *rp;
631 xfs_agblock_t b;
632 int i;
633 int hdr_errors;
634 int numrecs;
635 int state;
636
637 block = (xfs_alloc_block_t *)ablock;
638 hdr_errors = 0;
639
640 if (INT_GET(block->bb_magic, ARCH_CONVERT) != XFS_ABTC_MAGIC) {
507f4e33 641 do_warn(_("bad magic # %#x in btcnt block %d/%d\n"),
2bd0ea18
NS
642 INT_GET(block->bb_magic, ARCH_CONVERT), agno, bno);
643 hdr_errors++;
644 if (suspect)
645 return;
646 }
647 if (INT_GET(block->bb_level, ARCH_CONVERT) != level) {
507f4e33
NS
648 do_warn(_("expected level %d got %d in btcnt block %d/%d\n"),
649 level, INT_GET(block->bb_level, ARCH_CONVERT),
650 agno, bno);
2bd0ea18
NS
651 hdr_errors++;
652 if (suspect)
653 return;
654 }
655
656 /*
657 * check for btree blocks multiply claimed
658 */
659 state = get_agbno_state(mp, agno, bno);
660
661 switch (state) {
662 case XR_E_UNKNOWN:
663 set_agbno_state(mp, agno, bno, XR_E_FS_MAP);
664 break;
665 default:
666 set_agbno_state(mp, agno, bno, XR_E_MULT);
667 do_warn(
507f4e33
NS
668_("bcnt freespace btree block claimed (state %d), agno %d, bno %d, suspect %d\n"),
669 state, agno, bno, suspect);
2bd0ea18
NS
670 return;
671 }
672
673 if (level == 0) {
674 numrecs = INT_GET(block->bb_numrecs, ARCH_CONVERT);
675
507f4e33
NS
676 if (INT_GET(block->bb_numrecs, ARCH_CONVERT) >
677 mp->m_alloc_mxr[0]) {
2bd0ea18
NS
678 numrecs = mp->m_alloc_mxr[0];
679 hdr_errors++;
680 }
507f4e33
NS
681 if (isroot == 0 && INT_GET(block->bb_numrecs, ARCH_CONVERT) <
682 mp->m_alloc_mnr[0]) {
2bd0ea18
NS
683 numrecs = mp->m_alloc_mnr[0];
684 hdr_errors++;
685 }
686
687 if (hdr_errors)
688 suspect++;
689
690 rp = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, xfs_alloc, block,
691 1, mp->m_alloc_mxr[0]);
692 for (i = 0; i < numrecs; i++) {
693 if (INT_GET(rp[i].ar_blockcount, ARCH_CONVERT) == 0 ||
507f4e33
NS
694 INT_GET(rp[i].ar_startblock, ARCH_CONVERT) == 0 ||
695 !verify_agbno(mp, agno,
696 INT_GET(rp[i].ar_startblock, ARCH_CONVERT)) ||
697 INT_GET(rp[i].ar_blockcount, ARCH_CONVERT) >
698 MAXEXTLEN)
2bd0ea18
NS
699 continue;
700
507f4e33
NS
701 cnt_agffreeblks +=
702 INT_GET(rp[i].ar_blockcount, ARCH_CONVERT);
703 if (INT_GET(rp[i].ar_blockcount, ARCH_CONVERT) >
704 cnt_agflongest)
705 cnt_agflongest = INT_GET(rp[i].ar_blockcount,
706 ARCH_CONVERT);
2bd0ea18 707 for (b = INT_GET(rp[i].ar_startblock, ARCH_CONVERT);
507f4e33
NS
708 b < INT_GET(rp[i].ar_startblock, ARCH_CONVERT) +
709 INT_GET(rp[i].ar_blockcount, ARCH_CONVERT);
2bd0ea18
NS
710 b++) {
711 state = get_agbno_state(mp, agno, b);
712 /*
713 * no warning messages -- we'll catch
714 * FREE1 blocks later
715 */
716 switch (state) {
717 case XR_E_FREE1:
718 set_agbno_state(mp, agno, b, XR_E_FREE);
719 break;
720 case XR_E_UNKNOWN:
721 set_agbno_state(mp, agno, b,
722 XR_E_FREE1);
723 break;
724 default:
725 do_warn(
507f4e33 726 _("block (%d,%d) already used, state %d\n"),
2bd0ea18
NS
727 agno, b, state);
728 break;
729 }
730 }
731 }
732 return;
733 }
734
735 /*
736 * interior record
737 */
738 pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, xfs_alloc, block, 1,
739 mp->m_alloc_mxr[1]);
740
741 numrecs = INT_GET(block->bb_numrecs, ARCH_CONVERT);
742 if (INT_GET(block->bb_numrecs, ARCH_CONVERT) > mp->m_alloc_mxr[1]) {
743 numrecs = mp->m_alloc_mxr[1];
744 hdr_errors++;
745 }
507f4e33
NS
746 if (isroot == 0 &&
747 INT_GET(block->bb_numrecs, ARCH_CONVERT) < mp->m_alloc_mnr[1]) {
2bd0ea18
NS
748 numrecs = mp->m_alloc_mnr[1];
749 hdr_errors++;
750 }
751
752 /*
753 * don't pass bogus tree flag down further if this block
754 * looked ok. bail out if two levels in a row look bad.
755 */
756
757 if (suspect && !hdr_errors)
758 suspect = 0;
759
760 if (hdr_errors) {
761 if (suspect)
762 return;
763 else suspect++;
764 }
765
766 for (i = 0; i < numrecs; i++)
507f4e33
NS
767 if (INT_GET(pp[i], ARCH_CONVERT) != 0 &&
768 verify_agbno(mp, agno, INT_GET(pp[i], ARCH_CONVERT)))
2bd0ea18
NS
769 scan_sbtree(INT_GET(pp[i], ARCH_CONVERT), level, agno,
770 suspect, scanfunc_cnt, 0);
771}
772
773/*
774 * this one walks the inode btrees sucking the info there into
775 * the incore avl tree. We try and rescue corrupted btree records
776 * to minimize our chances of losing inodes. Inode info from potentially
777 * corrupt sources could be bogus so rather than put the info straight
778 * into the tree, instead we put it on a list and try and verify the
779 * info in the next phase by examining what's on disk. At that point,
780 * we'll be able to figure out what's what and stick the corrected info
781 * into the tree. We do bail out at some point and give up on a subtree
782 * so as to avoid walking randomly all over the ag.
783 *
784 * Note that it's also ok if the free/inuse info wrong, we can correct
785 * that when we examine the on-disk inode. The important thing is to
786 * get the start and alignment of the inode chunks right. Those chunks
787 * that we aren't sure about go into the uncertain list.
788 */
789void
790scanfunc_ino(
791 xfs_btree_sblock_t *ablock,
792 int level,
793 xfs_agblock_t bno,
794 xfs_agnumber_t agno,
795 int suspect,
796 int isroot
797 )
798{
799 xfs_ino_t lino;
800 xfs_inobt_block_t *block;
801 int i;
802 xfs_agino_t ino;
803 xfs_agblock_t agbno;
804 int j;
805 int nfree;
806 int off;
807 int numrecs;
808 int state;
809 xfs_inobt_ptr_t *pp;
810 xfs_inobt_rec_t *rp;
811 ino_tree_node_t *ino_rec, *first_rec, *last_rec;
812 int hdr_errors;
813
814 block = (xfs_inobt_block_t *)ablock;
815 hdr_errors = 0;
816
817 if (INT_GET(block->bb_magic, ARCH_CONVERT) != XFS_IBT_MAGIC) {
507f4e33 818 do_warn(_("bad magic # %#x in inobt block %d/%d\n"),
2bd0ea18
NS
819 INT_GET(block->bb_magic, ARCH_CONVERT), agno, bno);
820 hdr_errors++;
821 bad_ino_btree = 1;
822 if (suspect)
823 return;
824 }
825 if (INT_GET(block->bb_level, ARCH_CONVERT) != level) {
507f4e33
NS
826 do_warn(_("expected level %d got %d in inobt block %d/%d\n"),
827 level, INT_GET(block->bb_level, ARCH_CONVERT),
828 agno, bno);
2bd0ea18
NS
829 hdr_errors++;
830 bad_ino_btree = 1;
831 if (suspect)
832 return;
833 }
834
835 /*
836 * check for btree blocks multiply claimed, any unknown/free state
837 * is ok in the bitmap block.
838 */
839 state = get_agbno_state(mp, agno, bno);
840
841 switch (state) {
842 case XR_E_UNKNOWN:
843 case XR_E_FREE1:
844 case XR_E_FREE:
845 set_agbno_state(mp, agno, bno, XR_E_FS_MAP);
846 break;
847 default:
848 set_agbno_state(mp, agno, bno, XR_E_MULT);
849 do_warn(
507f4e33
NS
850_("inode btree block claimed (state %d), agno %d, bno %d, suspect %d\n"),
851 state, agno, bno, suspect);
2bd0ea18
NS
852 }
853
854 numrecs = INT_GET(block->bb_numrecs, ARCH_CONVERT);
855
856 /*
857 * leaf record in btree
858 */
859 if (level == 0) {
860 /* check for trashed btree block */
861
507f4e33
NS
862 if (INT_GET(block->bb_numrecs, ARCH_CONVERT) >
863 mp->m_inobt_mxr[0]) {
2bd0ea18
NS
864 numrecs = mp->m_inobt_mxr[0];
865 hdr_errors++;
866 }
507f4e33
NS
867 if (isroot == 0 && INT_GET(block->bb_numrecs, ARCH_CONVERT) <
868 mp->m_inobt_mnr[0]) {
2bd0ea18
NS
869 numrecs = mp->m_inobt_mnr[0];
870 hdr_errors++;
871 }
872
873 if (hdr_errors) {
874 bad_ino_btree = 1;
507f4e33 875 do_warn(_("dubious inode btree block header %d/%d\n"),
2bd0ea18
NS
876 agno, bno);
877 suspect++;
878 }
879
880 rp = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, xfs_inobt, block,
881 1, mp->m_inobt_mxr[0]);
882
883 /*
884 * step through the records, each record points to
885 * a chunk of inodes. The start of inode chunks should
886 * be block-aligned. Each inode btree rec should point
887 * to the start of a block of inodes or the start of a group
888 * of INODES_PER_CHUNK (64) inodes. off is the offset into
889 * the block. skip processing of bogus records.
890 */
891 for (i = 0; i < numrecs; i++) {
892 ino = INT_GET(rp[i].ir_startino, ARCH_CONVERT);
893 off = XFS_AGINO_TO_OFFSET(mp, ino);
894 agbno = XFS_AGINO_TO_AGBNO(mp, ino);
895 lino = XFS_AGINO_TO_INO(mp, agno, ino);
896 /*
897 * on multi-block block chunks, all chunks start
898 * at the beginning of the block. with multi-chunk
899 * blocks, all chunks must start on 64-inode boundaries
900 * since each block can hold N complete chunks. if
901 * fs has aligned inodes, all chunks must start
902 * at a fs_ino_alignment*N'th agbno. skip recs
903 * with badly aligned starting inodes.
904 */
905 if (ino == 0 ||
906 (inodes_per_block <= XFS_INODES_PER_CHUNK &&
907 off != 0) ||
908 (inodes_per_block > XFS_INODES_PER_CHUNK &&
909 off % XFS_INODES_PER_CHUNK != 0) ||
910 (fs_aligned_inodes &&
911 agbno % fs_ino_alignment != 0)) {
912 do_warn(
507f4e33 913 _("badly aligned inode rec (starting inode = %llu)\n"),
2bd0ea18
NS
914 lino);
915 suspect++;
916 }
917
918 /*
919 * verify numeric validity of inode chunk first
920 * before inserting into a tree. don't have to
921 * worry about the overflow case because the
922 * starting ino number of a chunk can only get
923 * within 255 inodes of max (NULLAGINO). if it
924 * gets closer, the agino number will be illegal
925 * as the agbno will be too large.
926 */
927 if (verify_aginum(mp, agno, ino)) {
928 do_warn(
507f4e33 929_("bad starting inode # (%llu (0x%x 0x%x)) in ino rec, skipping rec\n"),
2bd0ea18
NS
930 lino, agno, ino);
931 suspect++;
932 continue;
933 }
934
935 if (verify_aginum(mp, agno,
936 ino + XFS_INODES_PER_CHUNK - 1)) {
937 do_warn(
507f4e33 938_("bad ending inode # (%llu (0x%x 0x%x)) in ino rec, skipping rec\n"),
2bd0ea18
NS
939 lino + XFS_INODES_PER_CHUNK - 1,
940 agno, ino + XFS_INODES_PER_CHUNK - 1);
941 suspect++;
942 continue;
943 }
944
945 /*
946 * set state of each block containing inodes
947 */
948 if (off == 0 && !suspect) {
949 for (j = 0;
950 j < XFS_INODES_PER_CHUNK;
951 j += mp->m_sb.sb_inopblock) {
952 agbno = XFS_AGINO_TO_AGBNO(mp, ino + j);
953 state = get_agbno_state(mp,
954 agno, agbno);
955
956 if (state == XR_E_UNKNOWN) {
957 set_agbno_state(mp, agno,
958 agbno, XR_E_INO);
959 } else if (state == XR_E_INUSE_FS &&
960 agno == 0 &&
961 ino + j >= first_prealloc_ino &&
962 ino + j < last_prealloc_ino) {
963 set_agbno_state(mp, agno,
964 agbno, XR_E_INO);
965 } else {
966 do_warn(
507f4e33 967_("inode chunk claims used block, inobt block - agno %d, bno %d, inopb %d\n"),
2bd0ea18
NS
968 agno, bno,
969 mp->m_sb.sb_inopblock);
970 suspect++;
971 /*
972 * XXX - maybe should mark
973 * block a duplicate
974 */
975 continue;
976 }
977 }
978 }
979 /*
980 * ensure only one avl entry per chunk
981 */
982 find_inode_rec_range(agno, ino,
983 ino + XFS_INODES_PER_CHUNK,
984 &first_rec,
985 &last_rec);
986 if (first_rec != NULL) {
987 /*
988 * this chunk overlaps with one (or more)
989 * already in the tree
990 */
991 do_warn(
507f4e33 992_("inode rec for ino %llu (%d/%d) overlaps existing rec (start %d/%d)\n"),
2bd0ea18
NS
993 lino, agno, ino,
994 agno, first_rec->ino_startnum);
995 suspect++;
996
997 /*
998 * if the 2 chunks start at the same place,
999 * then we don't have to put this one
1000 * in the uncertain list. go to the next one.
1001 */
1002 if (first_rec->ino_startnum == ino)
1003 continue;
1004 }
1005
1006 agicount += XFS_INODES_PER_CHUNK;
507f4e33
NS
1007 agifreecount +=
1008 INT_GET(rp[i].ir_freecount, ARCH_CONVERT);
2bd0ea18
NS
1009 nfree = 0;
1010
1011 /*
1012 * now mark all the inodes as existing and free or used.
1013 * if the tree is suspect, put them into the uncertain
1014 * inode tree.
1015 */
1016 if (!suspect) {
b34acbba 1017 if (XFS_INOBT_IS_FREE_DISK(&rp[i], 0)) {
2bd0ea18
NS
1018 nfree++;
1019 ino_rec = set_inode_free_alloc(agno,
1020 ino);
1021 } else {
1022 ino_rec = set_inode_used_alloc(agno,
1023 ino);
1024 }
1025 for (j = 1; j < XFS_INODES_PER_CHUNK; j++) {
b34acbba 1026 if (XFS_INOBT_IS_FREE_DISK(&rp[i], j)) {
2bd0ea18
NS
1027 nfree++;
1028 set_inode_free(ino_rec, j);
1029 } else {
1030 set_inode_used(ino_rec, j);
1031 }
1032 }
1033 } else {
1034 for (j = 0; j < XFS_INODES_PER_CHUNK; j++) {
b34acbba 1035 if (XFS_INOBT_IS_FREE_DISK(&rp[i], j)) {
2bd0ea18
NS
1036 nfree++;
1037 add_aginode_uncertain(agno,
1038 ino + j, 1);
1039 } else {
1040 add_aginode_uncertain(agno,
1041 ino + j, 0);
1042 }
1043 }
1044 }
1045
1046 if (nfree != INT_GET(rp[i].ir_freecount, ARCH_CONVERT)) {
507f4e33
NS
1047 do_warn(
1048_("ir_freecount/free mismatch, inode chunk %d/%d, freecount %d nfree %d\n"),
1049 agno, ino, INT_GET(rp[i].ir_freecount,
1050 ARCH_CONVERT), nfree);
2bd0ea18
NS
1051 }
1052 }
1053
1054 if (suspect)
1055 bad_ino_btree = 1;
1056
1057 return;
1058 }
1059
1060 /*
1061 * interior record, continue on
1062 */
1063 if (INT_GET(block->bb_numrecs, ARCH_CONVERT) > mp->m_inobt_mxr[1]) {
1064 numrecs = mp->m_inobt_mxr[1];
1065 hdr_errors++;
1066 }
507f4e33
NS
1067 if (isroot == 0 &&
1068 INT_GET(block->bb_numrecs, ARCH_CONVERT) < mp->m_inobt_mnr[1]) {
2bd0ea18
NS
1069 numrecs = mp->m_inobt_mnr[1];
1070 hdr_errors++;
1071 }
1072
1073 pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, xfs_inobt, block, 1,
1074 mp->m_inobt_mxr[1]);
1075
1076 /*
1077 * don't pass bogus tree flag down further if this block
1078 * looked ok. bail out if two levels in a row look bad.
1079 */
1080
1081 if (suspect && !hdr_errors)
1082 suspect = 0;
1083
1084 if (hdr_errors) {
1085 bad_ino_btree = 1;
1086 if (suspect)
1087 return;
1088 else suspect++;
1089 }
1090
1091 for (i = 0; i < numrecs; i++) {
507f4e33
NS
1092 if (INT_GET(pp[i], ARCH_CONVERT) != 0 &&
1093 verify_agbno(mp, agno, INT_GET(pp[i], ARCH_CONVERT)))
1094 scan_sbtree(INT_GET(pp[i], ARCH_CONVERT), level,
1095 agno, suspect, scanfunc_ino, 0);
2bd0ea18
NS
1096 }
1097}
1098
1099void
1100scan_freelist(
1101 xfs_agf_t *agf)
1102{
1103 xfs_agfl_t *agfl;
1104 xfs_buf_t *agflbuf;
1105 xfs_agblock_t bno;
1106 int count;
1107 int i;
1108
1109 if (XFS_SB_BLOCK(mp) != XFS_AGFL_BLOCK(mp) &&
1110 XFS_AGF_BLOCK(mp) != XFS_AGFL_BLOCK(mp) &&
1111 XFS_AGI_BLOCK(mp) != XFS_AGFL_BLOCK(mp))
1112 set_agbno_state(mp, INT_GET(agf->agf_seqno, ARCH_CONVERT),
1113 XFS_AGFL_BLOCK(mp), XR_E_FS_MAP);
1114 if (INT_GET(agf->agf_flcount, ARCH_CONVERT) == 0)
1115 return;
1116 agflbuf = libxfs_readbuf(mp->m_dev,
1117 XFS_AG_DADDR(mp, INT_GET(agf->agf_seqno, ARCH_CONVERT),
9440d84d 1118 XFS_AGFL_DADDR(mp)), XFS_FSS_TO_BB(mp, 1), 0);
2bd0ea18 1119 if (!agflbuf) {
507f4e33 1120 do_abort(_("can't read agfl block for ag %d\n"),
2bd0ea18
NS
1121 INT_GET(agf->agf_seqno, ARCH_CONVERT));
1122 return;
1123 }
1124 agfl = XFS_BUF_TO_AGFL(agflbuf);
1125 i = INT_GET(agf->agf_flfirst, ARCH_CONVERT);
1126 count = 0;
1127 for (;;) {
1128 bno = INT_GET(agfl->agfl_bno[i], ARCH_CONVERT);
1129 if (verify_agbno(mp, INT_GET(agf->agf_seqno,ARCH_CONVERT), bno))
1130 set_agbno_state(mp,
1131 INT_GET(agf->agf_seqno, ARCH_CONVERT),
1132 bno, XR_E_FREE);
1133 else
507f4e33 1134 do_warn(_("bad agbno %u in agfl, agno %d\n"),
2bd0ea18
NS
1135 bno, INT_GET(agf->agf_seqno, ARCH_CONVERT));
1136 count++;
1137 if (i == INT_GET(agf->agf_fllast, ARCH_CONVERT))
1138 break;
9440d84d 1139 if (++i == XFS_AGFL_SIZE(mp))
2bd0ea18
NS
1140 i = 0;
1141 }
1142 if (count != INT_GET(agf->agf_flcount, ARCH_CONVERT)) {
507f4e33 1143 do_warn(_("freeblk count %d != flcount %d in ag %d\n"), count,
2bd0ea18
NS
1144 INT_GET(agf->agf_flcount, ARCH_CONVERT),
1145 INT_GET(agf->agf_seqno, ARCH_CONVERT));
1146 }
1147 libxfs_putbuf(agflbuf);
1148}
1149
1150void
1151scan_ag(
1152 xfs_agnumber_t agno)
1153{
1154 xfs_agf_t *agf;
1155 xfs_buf_t *agfbuf;
1156 int agf_dirty;
1157 xfs_agi_t *agi;
1158 xfs_buf_t *agibuf;
1159 int agi_dirty;
1160 xfs_sb_t *sb;
1161 xfs_buf_t *sbbuf;
1162 int sb_dirty;
1163 int status;
1164
1165 cnt_agffreeblks = cnt_agflongest = 0;
1166 bno_agffreeblks = bno_agflongest = 0;
1167
1168 agi_dirty = agf_dirty = sb_dirty = 0;
1169
1170 agicount = agifreecount = 0;
1171
1172 sbbuf = libxfs_readbuf(mp->m_dev, XFS_AG_DADDR(mp, agno, XFS_SB_DADDR),
1173 1, 0);
1174 if (!sbbuf) {
507f4e33 1175 do_error(_("can't get root superblock for ag %d\n"), agno);
2bd0ea18
NS
1176 return;
1177 }
1178
507f4e33
NS
1179 sb = (xfs_sb_t *)calloc(BBSIZE, 1);
1180 if (!sb) {
1181 do_error(_("can't allocate memory for superblock\n"));
1182 libxfs_putbuf(sbbuf);
1183 return;
dfc130f3 1184 }
46eca962 1185 libxfs_xlate_sb(XFS_BUF_TO_SBP(sbbuf), sb, 1, XFS_SB_ALL_BITS);
2bd0ea18
NS
1186
1187 agfbuf = libxfs_readbuf(mp->m_dev,
9440d84d
NS
1188 XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
1189 XFS_FSS_TO_BB(mp, 1), 0);
2bd0ea18 1190 if (!agfbuf) {
507f4e33 1191 do_error(_("can't read agf block for ag %d\n"), agno);
2bd0ea18 1192 libxfs_putbuf(sbbuf);
dfc130f3 1193 free(sb);
2bd0ea18
NS
1194 return;
1195 }
1196 agf = XFS_BUF_TO_AGF(agfbuf);
1197
1198 agibuf = libxfs_readbuf(mp->m_dev,
9440d84d
NS
1199 XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
1200 XFS_FSS_TO_BB(mp, 1), 0);
2bd0ea18 1201 if (!agibuf) {
507f4e33 1202 do_error(_("can't read agi block for ag %d\n"), agno);
2bd0ea18
NS
1203 libxfs_putbuf(agfbuf);
1204 libxfs_putbuf(sbbuf);
dfc130f3 1205 free(sb);
2bd0ea18
NS
1206 return;
1207 }
1208 agi = XFS_BUF_TO_AGI(agibuf);
1209
1210 /* fix up bad ag headers */
1211
1212 status = verify_set_agheader(mp, sbbuf, sb, agf, agi, agno);
1213
1214 if (status & XR_AG_SB_SEC) {
1215 if (!no_modify)
1216 sb_dirty = 1;
1217 /*
1218 * clear bad sector bit because we don't want
1219 * to skip further processing. we just want to
1220 * ensure that we write out the modified sb buffer.
1221 */
1222 status &= ~XR_AG_SB_SEC;
1223 }
1224 if (status & XR_AG_SB) {
507f4e33
NS
1225 if (!no_modify) {
1226 do_warn(_("reset bad sb for ag %d\n"), agno);
2bd0ea18 1227 sb_dirty = 1;
507f4e33
NS
1228 } else {
1229 do_warn(_("would reset bad sb for ag %d\n"), agno);
1230 }
2bd0ea18
NS
1231 }
1232 if (status & XR_AG_AGF) {
507f4e33
NS
1233 if (!no_modify) {
1234 do_warn(_("reset bad agf for ag %d\n"), agno);
2bd0ea18 1235 agf_dirty = 1;
507f4e33
NS
1236 } else {
1237 do_warn(_("would reset bad agf for ag %d\n"), agno);
1238 }
2bd0ea18
NS
1239 }
1240 if (status & XR_AG_AGI) {
507f4e33
NS
1241 if (!no_modify) {
1242 do_warn(_("reset bad agi for ag %d\n"), agno);
2bd0ea18 1243 agi_dirty = 1;
507f4e33
NS
1244 } else {
1245 do_warn(_("would reset bad agi for ag %d\n"), agno);
1246 }
2bd0ea18
NS
1247 }
1248
1249 if (status && no_modify) {
1250 libxfs_putbuf(agibuf);
1251 libxfs_putbuf(agfbuf);
1252 libxfs_putbuf(sbbuf);
dfc130f3 1253 free(sb);
2bd0ea18 1254
507f4e33
NS
1255 do_warn(_("bad uncorrected agheader %d, skipping ag...\n"),
1256 agno);
2bd0ea18
NS
1257
1258 return;
1259 }
1260
1261 scan_freelist(agf);
1262
1263 if (INT_GET(agf->agf_roots[XFS_BTNUM_BNO], ARCH_CONVERT) != 0 &&
507f4e33
NS
1264 verify_agbno(mp, agno,
1265 INT_GET(agf->agf_roots[XFS_BTNUM_BNO], ARCH_CONVERT)))
1266 scan_sbtree(
1267 INT_GET(agf->agf_roots[XFS_BTNUM_BNO], ARCH_CONVERT),
2bd0ea18
NS
1268 INT_GET(agf->agf_levels[XFS_BTNUM_BNO], ARCH_CONVERT),
1269 agno, 0, scanfunc_bno, 1);
1270 else
507f4e33
NS
1271 do_warn(_("bad agbno %u for btbno root, agno %d\n"),
1272 INT_GET(agf->agf_roots[XFS_BTNUM_BNO], ARCH_CONVERT),
1273 agno);
2bd0ea18
NS
1274
1275 if (INT_GET(agf->agf_roots[XFS_BTNUM_CNT], ARCH_CONVERT) != 0 &&
507f4e33
NS
1276 verify_agbno(mp, agno,
1277 INT_GET(agf->agf_roots[XFS_BTNUM_CNT], ARCH_CONVERT)))
1278 scan_sbtree(
1279 INT_GET(agf->agf_roots[XFS_BTNUM_CNT], ARCH_CONVERT),
2bd0ea18
NS
1280 INT_GET(agf->agf_levels[XFS_BTNUM_CNT], ARCH_CONVERT),
1281 agno, 0, scanfunc_cnt, 1);
1282 else
507f4e33
NS
1283 do_warn(_("bad agbno %u for btbcnt root, agno %d\n"),
1284 INT_GET(agf->agf_roots[XFS_BTNUM_CNT], ARCH_CONVERT),
1285 agno);
1286
1287 if (INT_GET(agi->agi_root, ARCH_CONVERT) != 0 &&
1288 verify_agbno(mp, agno, INT_GET(agi->agi_root, ARCH_CONVERT)))
1289 scan_sbtree(
1290 INT_GET(agi->agi_root, ARCH_CONVERT),
1291 INT_GET(agi->agi_level, ARCH_CONVERT),
1292 agno, 0, scanfunc_ino, 1);
2bd0ea18 1293 else
507f4e33 1294 do_warn(_("bad agbno %u for inobt root, agno %d\n"),
2bd0ea18
NS
1295 INT_GET(agi->agi_root, ARCH_CONVERT), agno);
1296
27527004 1297 ASSERT(agi_dirty == 0 || (agi_dirty && !no_modify));
2bd0ea18
NS
1298
1299 if (agi_dirty && !no_modify)
1300 libxfs_writebuf(agibuf, 0);
1301 else
1302 libxfs_putbuf(agibuf);
1303
27527004 1304 ASSERT(agf_dirty == 0 || (agf_dirty && !no_modify));
2bd0ea18
NS
1305
1306 if (agf_dirty && !no_modify)
1307 libxfs_writebuf(agfbuf, 0);
1308 else
1309 libxfs_putbuf(agfbuf);
1310
27527004 1311 ASSERT(sb_dirty == 0 || (sb_dirty && !no_modify));
2bd0ea18
NS
1312
1313 if (sb_dirty && !no_modify) {
46eca962 1314 libxfs_xlate_sb(XFS_BUF_PTR(sbbuf), sb, -1, XFS_SB_ALL_BITS);
2bd0ea18 1315 libxfs_writebuf(sbbuf, 0);
dfc130f3 1316 } else
2bd0ea18 1317 libxfs_putbuf(sbbuf);
dfc130f3 1318 free(sb);
2bd0ea18 1319}