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